xref: /llvm-project-15.0.7/flang/lib/Lower/IO.cpp (revision 14324fa4)
1 //===-- IO.cpp -- IO statement lowering -----------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "flang/Lower/IO.h"
14 #include "flang/Common/uint128.h"
15 #include "flang/Lower/Allocatable.h"
16 #include "flang/Lower/Bridge.h"
17 #include "flang/Lower/ConvertExpr.h"
18 #include "flang/Lower/ConvertVariable.h"
19 #include "flang/Lower/PFTBuilder.h"
20 #include "flang/Lower/Runtime.h"
21 #include "flang/Lower/StatementContext.h"
22 #include "flang/Lower/Support/Utils.h"
23 #include "flang/Lower/Todo.h"
24 #include "flang/Lower/VectorSubscripts.h"
25 #include "flang/Optimizer/Builder/Character.h"
26 #include "flang/Optimizer/Builder/Complex.h"
27 #include "flang/Optimizer/Builder/FIRBuilder.h"
28 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
29 #include "flang/Optimizer/Support/FIRContext.h"
30 #include "flang/Parser/parse-tree.h"
31 #include "flang/Runtime/io-api.h"
32 #include "flang/Semantics/tools.h"
33 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
34 
35 #define DEBUG_TYPE "flang-lower-io"
36 
37 // Define additional runtime type models specific to IO.
38 namespace fir::runtime {
39 template <>
40 constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
41   return getModel<char *>();
42 }
43 template <>
44 constexpr TypeBuilderFunc
45 getModel<const Fortran::runtime::io::NamelistGroup &>() {
46   return [](mlir::MLIRContext *context) -> mlir::Type {
47     return fir::ReferenceType::get(mlir::TupleType::get(context));
48   };
49 }
50 template <>
51 constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
52   return [](mlir::MLIRContext *context) -> mlir::Type {
53     return mlir::IntegerType::get(context,
54                                   8 * sizeof(Fortran::runtime::io::Iostat));
55   };
56 }
57 } // namespace fir::runtime
58 
59 using namespace Fortran::runtime::io;
60 
61 #define mkIOKey(X) FirmkKey(IONAME(X))
62 
63 namespace Fortran::lower {
64 /// Static table of IO runtime calls
65 ///
66 /// This logical map contains the name and type builder function for each IO
67 /// runtime function listed in the tuple. This table is fully constructed at
68 /// compile-time. Use the `mkIOKey` macro to access the table.
69 static constexpr std::tuple<
70     mkIOKey(BeginInternalArrayListOutput), mkIOKey(BeginInternalArrayListInput),
71     mkIOKey(BeginInternalArrayFormattedOutput),
72     mkIOKey(BeginInternalArrayFormattedInput), mkIOKey(BeginInternalListOutput),
73     mkIOKey(BeginInternalListInput), mkIOKey(BeginInternalFormattedOutput),
74     mkIOKey(BeginInternalFormattedInput), mkIOKey(BeginExternalListOutput),
75     mkIOKey(BeginExternalListInput), mkIOKey(BeginExternalFormattedOutput),
76     mkIOKey(BeginExternalFormattedInput), mkIOKey(BeginUnformattedOutput),
77     mkIOKey(BeginUnformattedInput), mkIOKey(BeginAsynchronousOutput),
78     mkIOKey(BeginAsynchronousInput), mkIOKey(BeginWait), mkIOKey(BeginWaitAll),
79     mkIOKey(BeginClose), mkIOKey(BeginFlush), mkIOKey(BeginBackspace),
80     mkIOKey(BeginEndfile), mkIOKey(BeginRewind), mkIOKey(BeginOpenUnit),
81     mkIOKey(BeginOpenNewUnit), mkIOKey(BeginInquireUnit),
82     mkIOKey(BeginInquireFile), mkIOKey(BeginInquireIoLength),
83     mkIOKey(EnableHandlers), mkIOKey(SetAdvance), mkIOKey(SetBlank),
84     mkIOKey(SetDecimal), mkIOKey(SetDelim), mkIOKey(SetPad), mkIOKey(SetPos),
85     mkIOKey(SetRec), mkIOKey(SetRound), mkIOKey(SetSign),
86     mkIOKey(OutputNamelist), mkIOKey(InputNamelist), mkIOKey(OutputDescriptor),
87     mkIOKey(InputDescriptor), mkIOKey(OutputUnformattedBlock),
88     mkIOKey(InputUnformattedBlock), mkIOKey(OutputInteger8),
89     mkIOKey(OutputInteger16), mkIOKey(OutputInteger32),
90     mkIOKey(OutputInteger64),
91 #ifdef __SIZEOF_INT128__
92     mkIOKey(OutputInteger128),
93 #endif
94     mkIOKey(InputInteger),
95     mkIOKey(OutputReal32), mkIOKey(InputReal32), mkIOKey(OutputReal64),
96     mkIOKey(InputReal64), mkIOKey(OutputComplex32), mkIOKey(InputComplex32),
97     mkIOKey(OutputComplex64), mkIOKey(InputComplex64), mkIOKey(OutputAscii),
98     mkIOKey(InputAscii), mkIOKey(OutputLogical), mkIOKey(InputLogical),
99     mkIOKey(SetAccess), mkIOKey(SetAction), mkIOKey(SetAsynchronous),
100     mkIOKey(SetCarriagecontrol), mkIOKey(SetEncoding), mkIOKey(SetForm),
101     mkIOKey(SetPosition), mkIOKey(SetRecl), mkIOKey(SetStatus),
102     mkIOKey(SetFile), mkIOKey(GetNewUnit), mkIOKey(GetSize),
103     mkIOKey(GetIoLength), mkIOKey(GetIoMsg), mkIOKey(InquireCharacter),
104     mkIOKey(InquireLogical), mkIOKey(InquirePendingId),
105     mkIOKey(InquireInteger64), mkIOKey(EndIoStatement)>
106     newIOTable;
107 } // namespace Fortran::lower
108 
109 namespace {
110 /// IO statements may require exceptional condition handling.  A statement that
111 /// encounters an exceptional condition may branch to a label given on an ERR
112 /// (error), END (end-of-file), or EOR (end-of-record) specifier.  An IOSTAT
113 /// specifier variable may be set to a value that indicates some condition,
114 /// and an IOMSG specifier variable may be set to a description of a condition.
115 struct ConditionSpecInfo {
116   const Fortran::lower::SomeExpr *ioStatExpr{};
117   const Fortran::lower::SomeExpr *ioMsgExpr{};
118   bool hasErr{};
119   bool hasEnd{};
120   bool hasEor{};
121 
122   /// Check for any condition specifier that applies to specifier processing.
123   bool hasErrorConditionSpec() const { return ioStatExpr != nullptr || hasErr; }
124 
125   /// Check for any condition specifier that applies to data transfer items
126   /// in a PRINT, READ, WRITE, or WAIT statement.  (WAIT may be irrelevant.)
127   bool hasTransferConditionSpec() const {
128     return hasErrorConditionSpec() || hasEnd || hasEor;
129   }
130 
131   /// Check for any condition specifier, including IOMSG.
132   bool hasAnyConditionSpec() const {
133     return hasTransferConditionSpec() || ioMsgExpr != nullptr;
134   }
135 };
136 } // namespace
137 
138 template <typename D>
139 static void genIoLoop(Fortran::lower::AbstractConverter &converter,
140                       mlir::Value cookie, const D &ioImpliedDo,
141                       bool isFormatted, bool checkResult, mlir::Value &ok,
142                       bool inLoop, Fortran::lower::StatementContext &stmtCtx);
143 
144 /// Helper function to retrieve the name of the IO function given the key `A`
145 template <typename A>
146 static constexpr const char *getName() {
147   return std::get<A>(Fortran::lower::newIOTable).name;
148 }
149 
150 /// Helper function to retrieve the type model signature builder of the IO
151 /// function as defined by the key `A`
152 template <typename A>
153 static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
154   return std::get<A>(Fortran::lower::newIOTable).getTypeModel();
155 }
156 
157 inline int64_t getLength(mlir::Type argTy) {
158   return argTy.cast<fir::SequenceType>().getShape()[0];
159 }
160 
161 /// Get (or generate) the MLIR FuncOp for a given IO runtime function.
162 template <typename E>
163 static mlir::FuncOp getIORuntimeFunc(mlir::Location loc,
164                                      fir::FirOpBuilder &builder) {
165   llvm::StringRef name = getName<E>();
166   mlir::FuncOp func = builder.getNamedFunction(name);
167   if (func)
168     return func;
169   auto funTy = getTypeModel<E>()(builder.getContext());
170   func = builder.createFunction(loc, name, funTy);
171   func->setAttr("fir.runtime", builder.getUnitAttr());
172   func->setAttr("fir.io", builder.getUnitAttr());
173   return func;
174 }
175 
176 /// Generate calls to end an IO statement.  Return the IOSTAT value, if any.
177 /// It is the caller's responsibility to generate branches on that value.
178 static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
179                             mlir::Location loc, mlir::Value cookie,
180                             const ConditionSpecInfo &csi,
181                             Fortran::lower::StatementContext &stmtCtx) {
182   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
183   if (csi.ioMsgExpr) {
184     mlir::FuncOp getIoMsg = getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
185     fir::ExtendedValue ioMsgVar =
186         converter.genExprAddr(csi.ioMsgExpr, stmtCtx, loc);
187     builder.create<fir::CallOp>(
188         loc, getIoMsg,
189         mlir::ValueRange{
190             cookie,
191             builder.createConvert(loc, getIoMsg.getType().getInput(1),
192                                   fir::getBase(ioMsgVar)),
193             builder.createConvert(loc, getIoMsg.getType().getInput(2),
194                                   fir::getLen(ioMsgVar))});
195   }
196   mlir::FuncOp endIoStatement =
197       getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
198   auto call = builder.create<fir::CallOp>(loc, endIoStatement,
199                                           mlir::ValueRange{cookie});
200   if (csi.ioStatExpr) {
201     mlir::Value ioStatVar =
202         fir::getBase(converter.genExprAddr(csi.ioStatExpr, stmtCtx, loc));
203     mlir::Value ioStatResult = builder.createConvert(
204         loc, converter.genType(*csi.ioStatExpr), call.getResult(0));
205     builder.create<fir::StoreOp>(loc, ioStatResult, ioStatVar);
206   }
207   return csi.hasTransferConditionSpec() ? call.getResult(0) : mlir::Value{};
208 }
209 
210 /// Make the next call in the IO statement conditional on runtime result `ok`.
211 /// If a call returns `ok==false`, further suboperation calls for an IO
212 /// statement will be skipped.  This may generate branch heavy, deeply nested
213 /// conditionals for IO statements with a large number of suboperations.
214 static void makeNextConditionalOn(fir::FirOpBuilder &builder,
215                                   mlir::Location loc, bool checkResult,
216                                   mlir::Value ok, bool inLoop = false) {
217   if (!checkResult || !ok)
218     // Either no IO calls need to be checked, or this will be the first call.
219     return;
220 
221   // A previous IO call for a statement returned the bool `ok`.  If this call
222   // is in a fir.iterate_while loop, the result must be propagated up to the
223   // loop scope as an extra ifOp result. (The propagation is done in genIoLoop.)
224   mlir::TypeRange resTy;
225   if (inLoop)
226     resTy = builder.getI1Type();
227   auto ifOp = builder.create<fir::IfOp>(loc, resTy, ok,
228                                         /*withElseRegion=*/inLoop);
229   builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
230 }
231 
232 /// Retrieve or generate a runtime description of NAMELIST group `symbol`.
233 /// The form of the description is defined in runtime header file namelist.h.
234 /// Static descriptors are generated for global objects; local descriptors for
235 /// local objects.  If all descriptors are static, the NamelistGroup is static.
236 static mlir::Value
237 getNamelistGroup(Fortran::lower::AbstractConverter &converter,
238                  const Fortran::semantics::Symbol &symbol,
239                  Fortran::lower::StatementContext &stmtCtx) {
240   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
241   mlir::Location loc = converter.getCurrentLocation();
242   std::string groupMangleName = converter.mangleName(symbol);
243   if (auto group = builder.getNamedGlobal(groupMangleName))
244     return builder.create<fir::AddrOfOp>(loc, group.resultType(),
245                                          group.getSymbol());
246 
247   const auto &details =
248       symbol.GetUltimate().get<Fortran::semantics::NamelistDetails>();
249   mlir::MLIRContext *context = builder.getContext();
250   mlir::StringAttr linkOnce = builder.createLinkOnceLinkage();
251   mlir::IndexType idxTy = builder.getIndexType();
252   mlir::IntegerType sizeTy = builder.getIntegerType(8 * sizeof(std::size_t));
253   fir::ReferenceType charRefTy =
254       fir::ReferenceType::get(builder.getIntegerType(8));
255   fir::ReferenceType descRefTy =
256       fir::ReferenceType::get(fir::BoxType::get(mlir::NoneType::get(context)));
257   fir::SequenceType listTy = fir::SequenceType::get(
258       details.objects().size(),
259       mlir::TupleType::get(context, {charRefTy, descRefTy}));
260   mlir::TupleType groupTy = mlir::TupleType::get(
261       context, {charRefTy, sizeTy, fir::ReferenceType::get(listTy)});
262   auto stringAddress = [&](const Fortran::semantics::Symbol &symbol) {
263     return fir::factory::createStringLiteral(builder, loc,
264                                              symbol.name().ToString() + '\0');
265   };
266 
267   // Define object names, and static descriptors for global objects.
268   bool groupIsLocal = false;
269   stringAddress(symbol);
270   for (const Fortran::semantics::Symbol &s : details.objects()) {
271     stringAddress(s);
272     if (!Fortran::lower::symbolIsGlobal(s)) {
273       groupIsLocal = true;
274       continue;
275     }
276     // We know we have a global item.  It it's not a pointer or allocatable,
277     // create a static pointer to it.
278     if (!IsAllocatableOrPointer(s)) {
279       std::string mangleName = converter.mangleName(s) + ".desc";
280       if (builder.getNamedGlobal(mangleName))
281         continue;
282       const auto expr = Fortran::evaluate::AsGenericExpr(s);
283       fir::BoxType boxTy =
284           fir::BoxType::get(fir::PointerType::get(converter.genType(s)));
285       auto descFunc = [&](fir::FirOpBuilder &b) {
286         auto box =
287             Fortran::lower::genInitialDataTarget(converter, loc, boxTy, *expr);
288         b.create<fir::HasValueOp>(loc, box);
289       };
290       builder.createGlobalConstant(loc, boxTy, mangleName, descFunc, linkOnce);
291     }
292   }
293 
294   // Define the list of Items.
295   mlir::Value listAddr =
296       groupIsLocal ? builder.create<fir::AllocaOp>(loc, listTy) : mlir::Value{};
297   std::string listMangleName = groupMangleName + ".list";
298   auto listFunc = [&](fir::FirOpBuilder &builder) {
299     mlir::Value list = builder.create<fir::UndefOp>(loc, listTy);
300     mlir::IntegerAttr zero = builder.getIntegerAttr(idxTy, 0);
301     mlir::IntegerAttr one = builder.getIntegerAttr(idxTy, 1);
302     llvm::SmallVector<mlir::Attribute, 2> idx = {mlir::Attribute{},
303                                                  mlir::Attribute{}};
304     size_t n = 0;
305     for (const Fortran::semantics::Symbol &s : details.objects()) {
306       idx[0] = builder.getIntegerAttr(idxTy, n);
307       idx[1] = zero;
308       mlir::Value nameAddr =
309           builder.createConvert(loc, charRefTy, fir::getBase(stringAddress(s)));
310       list = builder.create<fir::InsertValueOp>(loc, listTy, list, nameAddr,
311                                                 builder.getArrayAttr(idx));
312       idx[1] = one;
313       mlir::Value descAddr;
314       // Items that we created end in ".desc".
315       std::string suffix = IsAllocatableOrPointer(s) ? "" : ".desc";
316       if (auto desc =
317               builder.getNamedGlobal(converter.mangleName(s) + suffix)) {
318         descAddr = builder.create<fir::AddrOfOp>(loc, desc.resultType(),
319                                                  desc.getSymbol());
320       } else {
321         const auto expr = Fortran::evaluate::AsGenericExpr(s);
322         fir::ExtendedValue exv = converter.genExprAddr(*expr, stmtCtx);
323         mlir::Type type = fir::getBase(exv).getType();
324         if (mlir::Type baseTy = fir::dyn_cast_ptrOrBoxEleTy(type))
325           type = baseTy;
326         fir::BoxType boxType = fir::BoxType::get(fir::PointerType::get(type));
327         descAddr = builder.createTemporary(loc, boxType);
328         fir::MutableBoxValue box = fir::MutableBoxValue(descAddr, {}, {});
329         fir::factory::associateMutableBox(builder, loc, box, exv,
330                                           /*lbounds=*/llvm::None);
331       }
332       descAddr = builder.createConvert(loc, descRefTy, descAddr);
333       list = builder.create<fir::InsertValueOp>(loc, listTy, list, descAddr,
334                                                 builder.getArrayAttr(idx));
335       ++n;
336     }
337     if (groupIsLocal)
338       builder.create<fir::StoreOp>(loc, list, listAddr);
339     else
340       builder.create<fir::HasValueOp>(loc, list);
341   };
342   if (groupIsLocal)
343     listFunc(builder);
344   else
345     builder.createGlobalConstant(loc, listTy, listMangleName, listFunc,
346                                  linkOnce);
347 
348   // Define the group.
349   mlir::Value groupAddr = groupIsLocal
350                               ? builder.create<fir::AllocaOp>(loc, groupTy)
351                               : mlir::Value{};
352   auto groupFunc = [&](fir::FirOpBuilder &builder) {
353     mlir::IntegerAttr zero = builder.getIntegerAttr(idxTy, 0);
354     mlir::IntegerAttr one = builder.getIntegerAttr(idxTy, 1);
355     mlir::IntegerAttr two = builder.getIntegerAttr(idxTy, 2);
356     mlir::Value group = builder.create<fir::UndefOp>(loc, groupTy);
357     mlir::Value nameAddr = builder.createConvert(
358         loc, charRefTy, fir::getBase(stringAddress(symbol)));
359     group = builder.create<fir::InsertValueOp>(loc, groupTy, group, nameAddr,
360                                                builder.getArrayAttr(zero));
361     mlir::Value itemCount =
362         builder.createIntegerConstant(loc, sizeTy, details.objects().size());
363     group = builder.create<fir::InsertValueOp>(loc, groupTy, group, itemCount,
364                                                builder.getArrayAttr(one));
365     if (fir::GlobalOp list = builder.getNamedGlobal(listMangleName))
366       listAddr = builder.create<fir::AddrOfOp>(loc, list.resultType(),
367                                                list.getSymbol());
368     assert(listAddr && "missing namelist object list");
369     group = builder.create<fir::InsertValueOp>(loc, groupTy, group, listAddr,
370                                                builder.getArrayAttr(two));
371     if (groupIsLocal)
372       builder.create<fir::StoreOp>(loc, group, groupAddr);
373     else
374       builder.create<fir::HasValueOp>(loc, group);
375   };
376   if (groupIsLocal) {
377     groupFunc(builder);
378   } else {
379     fir::GlobalOp group =
380         builder.createGlobal(loc, groupTy, groupMangleName,
381                              /*isConst=*/true, groupFunc, linkOnce);
382     groupAddr = builder.create<fir::AddrOfOp>(loc, group.resultType(),
383                                               group.getSymbol());
384   }
385   assert(groupAddr && "missing namelist group result");
386   return groupAddr;
387 }
388 
389 /// Generate a namelist IO call.
390 static void genNamelistIO(Fortran::lower::AbstractConverter &converter,
391                           mlir::Value cookie, mlir::FuncOp funcOp,
392                           Fortran::semantics::Symbol &symbol, bool checkResult,
393                           mlir::Value &ok,
394                           Fortran::lower::StatementContext &stmtCtx) {
395   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
396   mlir::Location loc = converter.getCurrentLocation();
397   makeNextConditionalOn(builder, loc, checkResult, ok);
398   mlir::Type argType = funcOp.getType().getInput(1);
399   mlir::Value groupAddr = getNamelistGroup(converter, symbol, stmtCtx);
400   groupAddr = builder.createConvert(loc, argType, groupAddr);
401   llvm::SmallVector<mlir::Value> args = {cookie, groupAddr};
402   ok = builder.create<fir::CallOp>(loc, funcOp, args).getResult(0);
403 }
404 
405 /// Get the output function to call for a value of the given type.
406 static mlir::FuncOp getOutputFunc(mlir::Location loc,
407                                   fir::FirOpBuilder &builder, mlir::Type type,
408                                   bool isFormatted) {
409   if (!isFormatted)
410     return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
411   if (auto ty = type.dyn_cast<mlir::IntegerType>()) {
412     switch (ty.getWidth()) {
413     case 1:
414       return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
415     case 8:
416       return getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc, builder);
417     case 16:
418       return getIORuntimeFunc<mkIOKey(OutputInteger16)>(loc, builder);
419     case 32:
420       return getIORuntimeFunc<mkIOKey(OutputInteger32)>(loc, builder);
421     case 64:
422       return getIORuntimeFunc<mkIOKey(OutputInteger64)>(loc, builder);
423 #ifdef __SIZEOF_INT128__
424     case 128:
425       return getIORuntimeFunc<mkIOKey(OutputInteger128)>(loc, builder);
426 #endif
427     }
428     llvm_unreachable("unknown OutputInteger kind");
429   }
430   if (auto ty = type.dyn_cast<mlir::FloatType>()) {
431     if (auto width = ty.getWidth(); width == 32)
432       return getIORuntimeFunc<mkIOKey(OutputReal32)>(loc, builder);
433     else if (width == 64)
434       return getIORuntimeFunc<mkIOKey(OutputReal64)>(loc, builder);
435   }
436   auto kindMap = fir::getKindMapping(builder.getModule());
437   if (auto ty = type.dyn_cast<fir::ComplexType>()) {
438     // COMPLEX(KIND=k) corresponds to a pair of REAL(KIND=k).
439     auto width = kindMap.getRealBitsize(ty.getFKind());
440     if (width == 32)
441       return getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc, builder);
442     else if (width == 64)
443       return getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc, builder);
444   }
445   if (type.isa<fir::LogicalType>())
446     return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
447   if (fir::factory::CharacterExprHelper::isCharacterScalar(type)) {
448     // TODO: What would it mean if the default CHARACTER KIND is set to a wide
449     // character encoding scheme? How do we handle UTF-8? Is it a distinct KIND
450     // value? For now, assume that if the default CHARACTER KIND is 8 bit,
451     // then it is an ASCII string and UTF-8 is unsupported.
452     auto asciiKind = kindMap.defaultCharacterKind();
453     if (kindMap.getCharacterBitsize(asciiKind) == 8 &&
454         fir::factory::CharacterExprHelper::getCharacterKind(type) == asciiKind)
455       return getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
456   }
457   return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
458 }
459 
460 /// Generate a sequence of output data transfer calls.
461 static void
462 genOutputItemList(Fortran::lower::AbstractConverter &converter,
463                   mlir::Value cookie,
464                   const std::list<Fortran::parser::OutputItem> &items,
465                   bool isFormatted, bool checkResult, mlir::Value &ok,
466                   bool inLoop, Fortran::lower::StatementContext &stmtCtx) {
467   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
468   for (const Fortran::parser::OutputItem &item : items) {
469     if (const auto &impliedDo = std::get_if<1>(&item.u)) {
470       genIoLoop(converter, cookie, impliedDo->value(), isFormatted, checkResult,
471                 ok, inLoop, stmtCtx);
472       continue;
473     }
474     auto &pExpr = std::get<Fortran::parser::Expr>(item.u);
475     mlir::Location loc = converter.genLocation(pExpr.source);
476     makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
477 
478     const auto *expr = Fortran::semantics::GetExpr(pExpr);
479     if (!expr)
480       fir::emitFatalError(loc, "internal error: could not get evaluate::Expr");
481     mlir::Type itemTy = converter.genType(*expr);
482     mlir::FuncOp outputFunc = getOutputFunc(loc, builder, itemTy, isFormatted);
483     mlir::Type argType = outputFunc.getType().getInput(1);
484     assert((isFormatted || argType.isa<fir::BoxType>()) &&
485            "expect descriptor for unformatted IO runtime");
486     llvm::SmallVector<mlir::Value> outputFuncArgs = {cookie};
487     fir::factory::CharacterExprHelper helper{builder, loc};
488     if (argType.isa<fir::BoxType>()) {
489       mlir::Value box = fir::getBase(converter.genExprBox(*expr, stmtCtx, loc));
490       outputFuncArgs.push_back(builder.createConvert(loc, argType, box));
491     } else if (helper.isCharacterScalar(itemTy)) {
492       fir::ExtendedValue exv = converter.genExprAddr(expr, stmtCtx, loc);
493       // scalar allocatable/pointer may also get here, not clear if
494       // genExprAddr will lower them as CharBoxValue or BoxValue.
495       if (!exv.getCharBox())
496         llvm::report_fatal_error(
497             "internal error: scalar character not in CharBox");
498       outputFuncArgs.push_back(builder.createConvert(
499           loc, outputFunc.getType().getInput(1), fir::getBase(exv)));
500       outputFuncArgs.push_back(builder.createConvert(
501           loc, outputFunc.getType().getInput(2), fir::getLen(exv)));
502     } else {
503       fir::ExtendedValue itemBox = converter.genExprValue(expr, stmtCtx, loc);
504       mlir::Value itemValue = fir::getBase(itemBox);
505       if (fir::isa_complex(itemTy)) {
506         auto parts =
507             fir::factory::Complex{builder, loc}.extractParts(itemValue);
508         outputFuncArgs.push_back(parts.first);
509         outputFuncArgs.push_back(parts.second);
510       } else {
511         itemValue = builder.createConvert(loc, argType, itemValue);
512         outputFuncArgs.push_back(itemValue);
513       }
514     }
515     ok = builder.create<fir::CallOp>(loc, outputFunc, outputFuncArgs)
516              .getResult(0);
517   }
518 }
519 
520 /// Get the input function to call for a value of the given type.
521 static mlir::FuncOp getInputFunc(mlir::Location loc, fir::FirOpBuilder &builder,
522                                  mlir::Type type, bool isFormatted) {
523   if (!isFormatted)
524     return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
525   if (auto ty = type.dyn_cast<mlir::IntegerType>())
526     return ty.getWidth() == 1
527                ? getIORuntimeFunc<mkIOKey(InputLogical)>(loc, builder)
528                : getIORuntimeFunc<mkIOKey(InputInteger)>(loc, builder);
529   if (auto ty = type.dyn_cast<mlir::FloatType>()) {
530     if (auto width = ty.getWidth(); width <= 32)
531       return getIORuntimeFunc<mkIOKey(InputReal32)>(loc, builder);
532     else if (width <= 64)
533       return getIORuntimeFunc<mkIOKey(InputReal64)>(loc, builder);
534   }
535   auto kindMap = fir::getKindMapping(builder.getModule());
536   if (auto ty = type.dyn_cast<fir::ComplexType>()) {
537     auto width = kindMap.getRealBitsize(ty.getFKind());
538     if (width <= 32)
539       return getIORuntimeFunc<mkIOKey(InputComplex32)>(loc, builder);
540     else if (width <= 64)
541       return getIORuntimeFunc<mkIOKey(InputComplex64)>(loc, builder);
542   }
543   if (type.isa<fir::LogicalType>())
544     return getIORuntimeFunc<mkIOKey(InputLogical)>(loc, builder);
545   if (fir::factory::CharacterExprHelper::isCharacterScalar(type)) {
546     auto asciiKind = kindMap.defaultCharacterKind();
547     if (kindMap.getCharacterBitsize(asciiKind) == 8 &&
548         fir::factory::CharacterExprHelper::getCharacterKind(type) == asciiKind)
549       return getIORuntimeFunc<mkIOKey(InputAscii)>(loc, builder);
550   }
551   return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
552 }
553 
554 /// Interpret the lowest byte of a LOGICAL and store that value into the full
555 /// storage of the LOGICAL. The load, convert, and store effectively (sign or
556 /// zero) extends the lowest byte into the full LOGICAL value storage, as the
557 /// runtime is unaware of the LOGICAL value's actual bit width (it was passed
558 /// as a `bool&` to the runtime in order to be set).
559 static void boolRefToLogical(mlir::Location loc, fir::FirOpBuilder &builder,
560                              mlir::Value addr) {
561   auto boolType = builder.getRefType(builder.getI1Type());
562   auto boolAddr = builder.createConvert(loc, boolType, addr);
563   auto boolValue = builder.create<fir::LoadOp>(loc, boolAddr);
564   auto logicalType = fir::unwrapPassByRefType(addr.getType());
565   // The convert avoid making any assumptions about how LOGICALs are actually
566   // represented (it might end-up being either a signed or zero extension).
567   auto logicalValue = builder.createConvert(loc, logicalType, boolValue);
568   builder.create<fir::StoreOp>(loc, logicalValue, addr);
569 }
570 
571 static mlir::Value createIoRuntimeCallForItem(mlir::Location loc,
572                                               fir::FirOpBuilder &builder,
573                                               mlir::FuncOp inputFunc,
574                                               mlir::Value cookie,
575                                               const fir::ExtendedValue &item) {
576   mlir::Type argType = inputFunc.getType().getInput(1);
577   llvm::SmallVector<mlir::Value> inputFuncArgs = {cookie};
578   if (argType.isa<fir::BoxType>()) {
579     mlir::Value box = fir::getBase(item);
580     assert(box.getType().isa<fir::BoxType>() && "must be previously emboxed");
581     inputFuncArgs.push_back(builder.createConvert(loc, argType, box));
582   } else {
583     mlir::Value itemAddr = fir::getBase(item);
584     mlir::Type itemTy = fir::unwrapPassByRefType(itemAddr.getType());
585     inputFuncArgs.push_back(builder.createConvert(loc, argType, itemAddr));
586     fir::factory::CharacterExprHelper charHelper{builder, loc};
587     if (charHelper.isCharacterScalar(itemTy)) {
588       mlir::Value len = fir::getLen(item);
589       inputFuncArgs.push_back(
590           builder.createConvert(loc, inputFunc.getType().getInput(2), len));
591     } else if (itemTy.isa<mlir::IntegerType>()) {
592       inputFuncArgs.push_back(builder.create<mlir::arith::ConstantOp>(
593           loc, builder.getI32IntegerAttr(
594                    itemTy.cast<mlir::IntegerType>().getWidth() / 8)));
595     }
596   }
597   auto call = builder.create<fir::CallOp>(loc, inputFunc, inputFuncArgs);
598   auto itemAddr = fir::getBase(item);
599   auto itemTy = fir::unwrapRefType(itemAddr.getType());
600   if (itemTy.isa<fir::LogicalType>())
601     boolRefToLogical(loc, builder, itemAddr);
602   return call.getResult(0);
603 }
604 
605 /// Generate a sequence of input data transfer calls.
606 static void genInputItemList(Fortran::lower::AbstractConverter &converter,
607                              mlir::Value cookie,
608                              const std::list<Fortran::parser::InputItem> &items,
609                              bool isFormatted, bool checkResult,
610                              mlir::Value &ok, bool inLoop,
611                              Fortran::lower::StatementContext &stmtCtx) {
612   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
613   for (const Fortran::parser::InputItem &item : items) {
614     if (const auto &impliedDo = std::get_if<1>(&item.u)) {
615       genIoLoop(converter, cookie, impliedDo->value(), isFormatted, checkResult,
616                 ok, inLoop, stmtCtx);
617       continue;
618     }
619     auto &pVar = std::get<Fortran::parser::Variable>(item.u);
620     mlir::Location loc = converter.genLocation(pVar.GetSource());
621     makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
622     const auto *expr = Fortran::semantics::GetExpr(pVar);
623     if (!expr)
624       fir::emitFatalError(loc, "internal error: could not get evaluate::Expr");
625     if (Fortran::evaluate::HasVectorSubscript(*expr)) {
626       auto vectorSubscriptBox =
627           Fortran::lower::genVectorSubscriptBox(loc, converter, stmtCtx, *expr);
628       mlir::FuncOp inputFunc = getInputFunc(
629           loc, builder, vectorSubscriptBox.getElementType(), isFormatted);
630       const bool mustBox = inputFunc.getType().getInput(1).isa<fir::BoxType>();
631       if (!checkResult) {
632         auto elementalGenerator = [&](const fir::ExtendedValue &element) {
633           createIoRuntimeCallForItem(loc, builder, inputFunc, cookie,
634                                      mustBox ? builder.createBox(loc, element)
635                                              : element);
636         };
637         vectorSubscriptBox.loopOverElements(builder, loc, elementalGenerator);
638       } else {
639         auto elementalGenerator =
640             [&](const fir::ExtendedValue &element) -> mlir::Value {
641           return createIoRuntimeCallForItem(
642               loc, builder, inputFunc, cookie,
643               mustBox ? builder.createBox(loc, element) : element);
644         };
645         if (!ok)
646           ok = builder.createBool(loc, true);
647         ok = vectorSubscriptBox.loopOverElementsWhile(builder, loc,
648                                                       elementalGenerator, ok);
649       }
650       continue;
651     }
652     mlir::Type itemTy = converter.genType(*expr);
653     mlir::FuncOp inputFunc = getInputFunc(loc, builder, itemTy, isFormatted);
654     auto itemExv = inputFunc.getType().getInput(1).isa<fir::BoxType>()
655                        ? converter.genExprBox(*expr, stmtCtx, loc)
656                        : converter.genExprAddr(expr, stmtCtx, loc);
657     ok = createIoRuntimeCallForItem(loc, builder, inputFunc, cookie, itemExv);
658   }
659 }
660 
661 /// Generate an io-implied-do loop.
662 template <typename D>
663 static void genIoLoop(Fortran::lower::AbstractConverter &converter,
664                       mlir::Value cookie, const D &ioImpliedDo,
665                       bool isFormatted, bool checkResult, mlir::Value &ok,
666                       bool inLoop, Fortran::lower::StatementContext &stmtCtx) {
667   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
668   mlir::Location loc = converter.getCurrentLocation();
669   makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
670   const auto &itemList = std::get<0>(ioImpliedDo.t);
671   const auto &control = std::get<1>(ioImpliedDo.t);
672   const auto &loopSym = *control.name.thing.thing.symbol;
673   mlir::Value loopVar = converter.getSymbolAddress(loopSym);
674   auto genControlValue = [&](const Fortran::parser::ScalarIntExpr &expr) {
675     mlir::Value v = fir::getBase(
676         converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
677     return builder.createConvert(loc, builder.getIndexType(), v);
678   };
679   mlir::Value lowerValue = genControlValue(control.lower);
680   mlir::Value upperValue = genControlValue(control.upper);
681   mlir::Value stepValue =
682       control.step.has_value()
683           ? genControlValue(*control.step)
684           : builder.create<mlir::arith::ConstantIndexOp>(loc, 1);
685   auto genItemList = [&](const D &ioImpliedDo) {
686     Fortran::lower::StatementContext loopCtx;
687     if constexpr (std::is_same_v<D, Fortran::parser::InputImpliedDo>)
688       genInputItemList(converter, cookie, itemList, isFormatted, checkResult,
689                        ok, /*inLoop=*/true, loopCtx);
690     else
691       genOutputItemList(converter, cookie, itemList, isFormatted, checkResult,
692                         ok, /*inLoop=*/true, loopCtx);
693   };
694   if (!checkResult) {
695     // No IO call result checks - the loop is a fir.do_loop op.
696     auto doLoopOp = builder.create<fir::DoLoopOp>(
697         loc, lowerValue, upperValue, stepValue, /*unordered=*/false,
698         /*finalCountValue=*/true);
699     builder.setInsertionPointToStart(doLoopOp.getBody());
700     mlir::Value lcv = builder.createConvert(loc, converter.genType(loopSym),
701                                             doLoopOp.getInductionVar());
702     builder.create<fir::StoreOp>(loc, lcv, loopVar);
703     genItemList(ioImpliedDo);
704     builder.setInsertionPointToEnd(doLoopOp.getBody());
705     mlir::Value result = builder.create<mlir::arith::AddIOp>(
706         loc, doLoopOp.getInductionVar(), doLoopOp.getStep());
707     builder.create<fir::ResultOp>(loc, result);
708     builder.setInsertionPointAfter(doLoopOp);
709     // The loop control variable may be used after the loop.
710     lcv = builder.createConvert(loc, converter.genType(loopSym),
711                                 doLoopOp.getResult(0));
712     builder.create<fir::StoreOp>(loc, lcv, loopVar);
713     return;
714   }
715   // Check IO call results - the loop is a fir.iterate_while op.
716   if (!ok)
717     ok = builder.createBool(loc, true);
718   auto iterWhileOp = builder.create<fir::IterWhileOp>(
719       loc, lowerValue, upperValue, stepValue, ok, /*finalCountValue*/ true);
720   builder.setInsertionPointToStart(iterWhileOp.getBody());
721   mlir::Value lcv = builder.createConvert(loc, converter.genType(loopSym),
722                                           iterWhileOp.getInductionVar());
723   builder.create<fir::StoreOp>(loc, lcv, loopVar);
724   ok = iterWhileOp.getIterateVar();
725   mlir::Value falseValue =
726       builder.createIntegerConstant(loc, builder.getI1Type(), 0);
727   genItemList(ioImpliedDo);
728   // Unwind nested IO call scopes, filling in true and false ResultOp's.
729   for (mlir::Operation *op = builder.getBlock()->getParentOp();
730        mlir::isa<fir::IfOp>(op); op = op->getBlock()->getParentOp()) {
731     auto ifOp = mlir::dyn_cast<fir::IfOp>(op);
732     mlir::Operation *lastOp = &ifOp.getThenRegion().front().back();
733     builder.setInsertionPointAfter(lastOp);
734     // The primary ifOp result is the result of an IO call or loop.
735     if (mlir::isa<fir::CallOp, fir::IfOp>(*lastOp))
736       builder.create<fir::ResultOp>(loc, lastOp->getResult(0));
737     else
738       builder.create<fir::ResultOp>(loc, ok); // loop result
739     // The else branch propagates an early exit false result.
740     builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
741     builder.create<fir::ResultOp>(loc, falseValue);
742   }
743   builder.setInsertionPointToEnd(iterWhileOp.getBody());
744   mlir::OpResult iterateResult = builder.getBlock()->back().getResult(0);
745   mlir::Value inductionResult0 = iterWhileOp.getInductionVar();
746   auto inductionResult1 = builder.create<mlir::arith::AddIOp>(
747       loc, inductionResult0, iterWhileOp.getStep());
748   auto inductionResult = builder.create<mlir::arith::SelectOp>(
749       loc, iterateResult, inductionResult1, inductionResult0);
750   llvm::SmallVector<mlir::Value> results = {inductionResult, iterateResult};
751   builder.create<fir::ResultOp>(loc, results);
752   ok = iterWhileOp.getResult(1);
753   builder.setInsertionPointAfter(iterWhileOp);
754   // The loop control variable may be used after the loop.
755   lcv = builder.createConvert(loc, converter.genType(loopSym),
756                               iterWhileOp.getResult(0));
757   builder.create<fir::StoreOp>(loc, lcv, loopVar);
758 }
759 
760 //===----------------------------------------------------------------------===//
761 // Default argument generation.
762 //===----------------------------------------------------------------------===//
763 
764 static mlir::Value locToFilename(Fortran::lower::AbstractConverter &converter,
765                                  mlir::Location loc, mlir::Type toType) {
766   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
767   return builder.createConvert(loc, toType,
768                                fir::factory::locationToFilename(builder, loc));
769 }
770 
771 static mlir::Value locToLineNo(Fortran::lower::AbstractConverter &converter,
772                                mlir::Location loc, mlir::Type toType) {
773   return fir::factory::locationToLineNo(converter.getFirOpBuilder(), loc,
774                                         toType);
775 }
776 
777 static mlir::Value getDefaultScratch(fir::FirOpBuilder &builder,
778                                      mlir::Location loc, mlir::Type toType) {
779   mlir::Value null = builder.create<mlir::arith::ConstantOp>(
780       loc, builder.getI64IntegerAttr(0));
781   return builder.createConvert(loc, toType, null);
782 }
783 
784 static mlir::Value getDefaultScratchLen(fir::FirOpBuilder &builder,
785                                         mlir::Location loc, mlir::Type toType) {
786   return builder.create<mlir::arith::ConstantOp>(
787       loc, builder.getIntegerAttr(toType, 0));
788 }
789 
790 /// Generate a reference to a buffer and the length of buffer given
791 /// a character expression. An array expression will be cast to scalar
792 /// character as long as they are contiguous.
793 static std::tuple<mlir::Value, mlir::Value>
794 genBuffer(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
795           const Fortran::lower::SomeExpr &expr, mlir::Type strTy,
796           mlir::Type lenTy, Fortran::lower::StatementContext &stmtCtx) {
797   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
798   fir::ExtendedValue exprAddr = converter.genExprAddr(expr, stmtCtx);
799   fir::factory::CharacterExprHelper helper(builder, loc);
800   using ValuePair = std::pair<mlir::Value, mlir::Value>;
801   auto [buff, len] = exprAddr.match(
802       [&](const fir::CharBoxValue &x) -> ValuePair {
803         return {x.getBuffer(), x.getLen()};
804       },
805       [&](const fir::CharArrayBoxValue &x) -> ValuePair {
806         fir::CharBoxValue scalar = helper.toScalarCharacter(x);
807         return {scalar.getBuffer(), scalar.getLen()};
808       },
809       [&](const fir::BoxValue &) -> ValuePair {
810         // May need to copy before after IO to handle contiguous
811         // aspect. Not sure descriptor can get here though.
812         TODO(loc, "character descriptor to contiguous buffer");
813       },
814       [&](const auto &) -> ValuePair {
815         llvm::report_fatal_error(
816             "internal error: IO buffer is not a character");
817       });
818   buff = builder.createConvert(loc, strTy, buff);
819   len = builder.createConvert(loc, lenTy, len);
820   return {buff, len};
821 }
822 
823 /// Lower a string literal. Many arguments to the runtime are conveyed as
824 /// Fortran CHARACTER literals.
825 template <typename A>
826 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
827 lowerStringLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
828                Fortran::lower::StatementContext &stmtCtx, const A &syntax,
829                mlir::Type strTy, mlir::Type lenTy, mlir::Type ty2 = {}) {
830   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
831   auto *expr = Fortran::semantics::GetExpr(syntax);
832   if (!expr)
833     fir::emitFatalError(loc, "internal error: null semantic expr in IO");
834   auto [buff, len] = genBuffer(converter, loc, *expr, strTy, lenTy, stmtCtx);
835   mlir::Value kind;
836   if (ty2) {
837     auto kindVal = expr->GetType().value().kind();
838     kind = builder.create<mlir::arith::ConstantOp>(
839         loc, builder.getIntegerAttr(ty2, kindVal));
840   }
841   return {buff, len, kind};
842 }
843 
844 /// Pass the body of the FORMAT statement in as if it were a CHARACTER literal
845 /// constant. NB: This is the prescribed manner in which the front-end passes
846 /// this information to lowering.
847 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
848 lowerSourceTextAsStringLit(Fortran::lower::AbstractConverter &converter,
849                            mlir::Location loc, llvm::StringRef text,
850                            mlir::Type strTy, mlir::Type lenTy) {
851   text = text.drop_front(text.find('('));
852   text = text.take_front(text.rfind(')') + 1);
853   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
854   mlir::Value addrGlobalStringLit =
855       fir::getBase(fir::factory::createStringLiteral(builder, loc, text));
856   mlir::Value buff = builder.createConvert(loc, strTy, addrGlobalStringLit);
857   mlir::Value len = builder.createIntegerConstant(loc, lenTy, text.size());
858   return {buff, len, mlir::Value{}};
859 }
860 
861 //===----------------------------------------------------------------------===//
862 // Handle IO statement specifiers.
863 // These are threaded together for a single statement via the passed cookie.
864 //===----------------------------------------------------------------------===//
865 
866 /// Generic to build an integral argument to the runtime.
867 template <typename A, typename B>
868 mlir::Value genIntIOOption(Fortran::lower::AbstractConverter &converter,
869                            mlir::Location loc, mlir::Value cookie,
870                            const B &spec) {
871   Fortran::lower::StatementContext localStatementCtx;
872   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
873   mlir::FuncOp ioFunc = getIORuntimeFunc<A>(loc, builder);
874   mlir::FunctionType ioFuncTy = ioFunc.getType();
875   mlir::Value expr = fir::getBase(converter.genExprValue(
876       Fortran::semantics::GetExpr(spec.v), localStatementCtx, loc));
877   mlir::Value val = builder.createConvert(loc, ioFuncTy.getInput(1), expr);
878   llvm::SmallVector<mlir::Value> ioArgs = {cookie, val};
879   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
880 }
881 
882 /// Generic to build a string argument to the runtime. This passes a CHARACTER
883 /// as a pointer to the buffer and a LEN parameter.
884 template <typename A, typename B>
885 mlir::Value genCharIOOption(Fortran::lower::AbstractConverter &converter,
886                             mlir::Location loc, mlir::Value cookie,
887                             const B &spec) {
888   Fortran::lower::StatementContext localStatementCtx;
889   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
890   mlir::FuncOp ioFunc = getIORuntimeFunc<A>(loc, builder);
891   mlir::FunctionType ioFuncTy = ioFunc.getType();
892   std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
893       lowerStringLit(converter, loc, localStatementCtx, spec,
894                      ioFuncTy.getInput(1), ioFuncTy.getInput(2));
895   llvm::SmallVector<mlir::Value> ioArgs = {cookie, std::get<0>(tup),
896                                            std::get<1>(tup)};
897   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
898 }
899 
900 template <typename A>
901 mlir::Value genIOOption(Fortran::lower::AbstractConverter &converter,
902                         mlir::Location loc, mlir::Value cookie, const A &spec) {
903   // These specifiers are processed in advance elsewhere - skip them here.
904   using PreprocessedSpecs =
905       std::tuple<Fortran::parser::EndLabel, Fortran::parser::EorLabel,
906                  Fortran::parser::ErrLabel, Fortran::parser::FileUnitNumber,
907                  Fortran::parser::Format, Fortran::parser::IoUnit,
908                  Fortran::parser::MsgVariable, Fortran::parser::Name,
909                  Fortran::parser::StatVariable>;
910   static_assert(Fortran::common::HasMember<A, PreprocessedSpecs>,
911                 "missing genIOOPtion specialization");
912   return {};
913 }
914 
915 template <>
916 mlir::Value genIOOption<Fortran::parser::FileNameExpr>(
917     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
918     mlir::Value cookie, const Fortran::parser::FileNameExpr &spec) {
919   Fortran::lower::StatementContext localStatementCtx;
920   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
921   // has an extra KIND argument
922   mlir::FuncOp ioFunc = getIORuntimeFunc<mkIOKey(SetFile)>(loc, builder);
923   mlir::FunctionType ioFuncTy = ioFunc.getType();
924   std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
925       lowerStringLit(converter, loc, localStatementCtx, spec,
926                      ioFuncTy.getInput(1), ioFuncTy.getInput(2));
927   llvm::SmallVector<mlir::Value> ioArgs{cookie, std::get<0>(tup),
928                                         std::get<1>(tup)};
929   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
930 }
931 
932 template <>
933 mlir::Value genIOOption<Fortran::parser::ConnectSpec::CharExpr>(
934     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
935     mlir::Value cookie, const Fortran::parser::ConnectSpec::CharExpr &spec) {
936   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
937   mlir::FuncOp ioFunc;
938   switch (std::get<Fortran::parser::ConnectSpec::CharExpr::Kind>(spec.t)) {
939   case Fortran::parser::ConnectSpec::CharExpr::Kind::Access:
940     ioFunc = getIORuntimeFunc<mkIOKey(SetAccess)>(loc, builder);
941     break;
942   case Fortran::parser::ConnectSpec::CharExpr::Kind::Action:
943     ioFunc = getIORuntimeFunc<mkIOKey(SetAction)>(loc, builder);
944     break;
945   case Fortran::parser::ConnectSpec::CharExpr::Kind::Asynchronous:
946     ioFunc = getIORuntimeFunc<mkIOKey(SetAsynchronous)>(loc, builder);
947     break;
948   case Fortran::parser::ConnectSpec::CharExpr::Kind::Blank:
949     ioFunc = getIORuntimeFunc<mkIOKey(SetBlank)>(loc, builder);
950     break;
951   case Fortran::parser::ConnectSpec::CharExpr::Kind::Decimal:
952     ioFunc = getIORuntimeFunc<mkIOKey(SetDecimal)>(loc, builder);
953     break;
954   case Fortran::parser::ConnectSpec::CharExpr::Kind::Delim:
955     ioFunc = getIORuntimeFunc<mkIOKey(SetDelim)>(loc, builder);
956     break;
957   case Fortran::parser::ConnectSpec::CharExpr::Kind::Encoding:
958     ioFunc = getIORuntimeFunc<mkIOKey(SetEncoding)>(loc, builder);
959     break;
960   case Fortran::parser::ConnectSpec::CharExpr::Kind::Form:
961     ioFunc = getIORuntimeFunc<mkIOKey(SetForm)>(loc, builder);
962     break;
963   case Fortran::parser::ConnectSpec::CharExpr::Kind::Pad:
964     ioFunc = getIORuntimeFunc<mkIOKey(SetPad)>(loc, builder);
965     break;
966   case Fortran::parser::ConnectSpec::CharExpr::Kind::Position:
967     ioFunc = getIORuntimeFunc<mkIOKey(SetPosition)>(loc, builder);
968     break;
969   case Fortran::parser::ConnectSpec::CharExpr::Kind::Round:
970     ioFunc = getIORuntimeFunc<mkIOKey(SetRound)>(loc, builder);
971     break;
972   case Fortran::parser::ConnectSpec::CharExpr::Kind::Sign:
973     ioFunc = getIORuntimeFunc<mkIOKey(SetSign)>(loc, builder);
974     break;
975   case Fortran::parser::ConnectSpec::CharExpr::Kind::Carriagecontrol:
976     ioFunc = getIORuntimeFunc<mkIOKey(SetCarriagecontrol)>(loc, builder);
977     break;
978   case Fortran::parser::ConnectSpec::CharExpr::Kind::Convert:
979     TODO(loc, "CONVERT not part of the runtime::io interface");
980   case Fortran::parser::ConnectSpec::CharExpr::Kind::Dispose:
981     TODO(loc, "DISPOSE not part of the runtime::io interface");
982   }
983   Fortran::lower::StatementContext localStatementCtx;
984   mlir::FunctionType ioFuncTy = ioFunc.getType();
985   std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
986       lowerStringLit(converter, loc, localStatementCtx,
987                      std::get<Fortran::parser::ScalarDefaultCharExpr>(spec.t),
988                      ioFuncTy.getInput(1), ioFuncTy.getInput(2));
989   llvm::SmallVector<mlir::Value> ioArgs = {cookie, std::get<0>(tup),
990                                            std::get<1>(tup)};
991   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
992 }
993 
994 template <>
995 mlir::Value genIOOption<Fortran::parser::ConnectSpec::Recl>(
996     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
997     mlir::Value cookie, const Fortran::parser::ConnectSpec::Recl &spec) {
998   return genIntIOOption<mkIOKey(SetRecl)>(converter, loc, cookie, spec);
999 }
1000 
1001 template <>
1002 mlir::Value genIOOption<Fortran::parser::StatusExpr>(
1003     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1004     mlir::Value cookie, const Fortran::parser::StatusExpr &spec) {
1005   return genCharIOOption<mkIOKey(SetStatus)>(converter, loc, cookie, spec.v);
1006 }
1007 
1008 template <>
1009 mlir::Value genIOOption<Fortran::parser::IoControlSpec::CharExpr>(
1010     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1011     mlir::Value cookie, const Fortran::parser::IoControlSpec::CharExpr &spec) {
1012   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1013   mlir::FuncOp ioFunc;
1014   switch (std::get<Fortran::parser::IoControlSpec::CharExpr::Kind>(spec.t)) {
1015   case Fortran::parser::IoControlSpec::CharExpr::Kind::Advance:
1016     ioFunc = getIORuntimeFunc<mkIOKey(SetAdvance)>(loc, builder);
1017     break;
1018   case Fortran::parser::IoControlSpec::CharExpr::Kind::Blank:
1019     ioFunc = getIORuntimeFunc<mkIOKey(SetBlank)>(loc, builder);
1020     break;
1021   case Fortran::parser::IoControlSpec::CharExpr::Kind::Decimal:
1022     ioFunc = getIORuntimeFunc<mkIOKey(SetDecimal)>(loc, builder);
1023     break;
1024   case Fortran::parser::IoControlSpec::CharExpr::Kind::Delim:
1025     ioFunc = getIORuntimeFunc<mkIOKey(SetDelim)>(loc, builder);
1026     break;
1027   case Fortran::parser::IoControlSpec::CharExpr::Kind::Pad:
1028     ioFunc = getIORuntimeFunc<mkIOKey(SetPad)>(loc, builder);
1029     break;
1030   case Fortran::parser::IoControlSpec::CharExpr::Kind::Round:
1031     ioFunc = getIORuntimeFunc<mkIOKey(SetRound)>(loc, builder);
1032     break;
1033   case Fortran::parser::IoControlSpec::CharExpr::Kind::Sign:
1034     ioFunc = getIORuntimeFunc<mkIOKey(SetSign)>(loc, builder);
1035     break;
1036   }
1037   Fortran::lower::StatementContext localStatementCtx;
1038   mlir::FunctionType ioFuncTy = ioFunc.getType();
1039   std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
1040       lowerStringLit(converter, loc, localStatementCtx,
1041                      std::get<Fortran::parser::ScalarDefaultCharExpr>(spec.t),
1042                      ioFuncTy.getInput(1), ioFuncTy.getInput(2));
1043   llvm::SmallVector<mlir::Value> ioArgs = {cookie, std::get<0>(tup),
1044                                            std::get<1>(tup)};
1045   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
1046 }
1047 
1048 template <>
1049 mlir::Value genIOOption<Fortran::parser::IoControlSpec::Asynchronous>(
1050     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1051     mlir::Value cookie,
1052     const Fortran::parser::IoControlSpec::Asynchronous &spec) {
1053   return genCharIOOption<mkIOKey(SetAsynchronous)>(converter, loc, cookie,
1054                                                    spec.v);
1055 }
1056 
1057 template <>
1058 mlir::Value genIOOption<Fortran::parser::IdVariable>(
1059     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1060     mlir::Value cookie, const Fortran::parser::IdVariable &spec) {
1061   TODO(loc, "asynchronous ID not implemented");
1062 }
1063 
1064 template <>
1065 mlir::Value genIOOption<Fortran::parser::IoControlSpec::Pos>(
1066     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1067     mlir::Value cookie, const Fortran::parser::IoControlSpec::Pos &spec) {
1068   return genIntIOOption<mkIOKey(SetPos)>(converter, loc, cookie, spec);
1069 }
1070 
1071 template <>
1072 mlir::Value genIOOption<Fortran::parser::IoControlSpec::Rec>(
1073     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1074     mlir::Value cookie, const Fortran::parser::IoControlSpec::Rec &spec) {
1075   return genIntIOOption<mkIOKey(SetRec)>(converter, loc, cookie, spec);
1076 }
1077 
1078 /// Generate runtime call to query the read size after an input statement if
1079 /// the statement has SIZE control-spec.
1080 template <typename A>
1081 static void genIOReadSize(Fortran::lower::AbstractConverter &converter,
1082                           mlir::Location loc, mlir::Value cookie,
1083                           const A &specList, bool checkResult) {
1084   // This call is not conditional on the current IO status (ok) because the size
1085   // needs to be filled even if some error condition (end-of-file...) was met
1086   // during the input statement (in which case the runtime may return zero for
1087   // the size read).
1088   for (const auto &spec : specList)
1089     if (const auto *size =
1090             std::get_if<Fortran::parser::IoControlSpec::Size>(&spec.u)) {
1091 
1092       fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1093       mlir::FuncOp ioFunc = getIORuntimeFunc<mkIOKey(GetSize)>(loc, builder);
1094       auto sizeValue =
1095           builder.create<fir::CallOp>(loc, ioFunc, mlir::ValueRange{cookie})
1096               .getResult(0);
1097       Fortran::lower::StatementContext localStatementCtx;
1098       fir::ExtendedValue var = converter.genExprAddr(
1099           Fortran::semantics::GetExpr(size->v), localStatementCtx, loc);
1100       mlir::Value varAddr = fir::getBase(var);
1101       mlir::Type varType = fir::unwrapPassByRefType(varAddr.getType());
1102       mlir::Value sizeCast = builder.createConvert(loc, varType, sizeValue);
1103       builder.create<fir::StoreOp>(loc, sizeCast, varAddr);
1104       break;
1105     }
1106 }
1107 
1108 //===----------------------------------------------------------------------===//
1109 // Gather IO statement condition specifier information (if any).
1110 //===----------------------------------------------------------------------===//
1111 
1112 template <typename SEEK, typename A>
1113 static bool hasX(const A &list) {
1114   for (const auto &spec : list)
1115     if (std::holds_alternative<SEEK>(spec.u))
1116       return true;
1117   return false;
1118 }
1119 
1120 template <typename SEEK, typename A>
1121 static bool hasSpec(const A &stmt) {
1122   return hasX<SEEK>(stmt.v);
1123 }
1124 
1125 /// Get the sought expression from the specifier list.
1126 template <typename SEEK, typename A>
1127 static const Fortran::lower::SomeExpr *getExpr(const A &stmt) {
1128   for (const auto &spec : stmt.v)
1129     if (auto *f = std::get_if<SEEK>(&spec.u))
1130       return Fortran::semantics::GetExpr(f->v);
1131   llvm::report_fatal_error("must have a file unit");
1132 }
1133 
1134 /// For each specifier, build the appropriate call, threading the cookie.
1135 template <typename A>
1136 static void threadSpecs(Fortran::lower::AbstractConverter &converter,
1137                         mlir::Location loc, mlir::Value cookie,
1138                         const A &specList, bool checkResult, mlir::Value &ok) {
1139   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1140   for (const auto &spec : specList) {
1141     makeNextConditionalOn(builder, loc, checkResult, ok);
1142     ok = std::visit(
1143         Fortran::common::visitors{
1144             [&](const Fortran::parser::IoControlSpec::Size &x) -> mlir::Value {
1145               // Size must be queried after the related READ runtime calls, not
1146               // before.
1147               return ok;
1148             },
1149             [&](const Fortran::parser::ConnectSpec::Newunit &x) -> mlir::Value {
1150               // Newunit must be queried after OPEN specifier runtime calls
1151               // that may fail to avoid modifying the newunit variable if
1152               // there is an error.
1153               return ok;
1154             },
1155             [&](const auto &x) {
1156               return genIOOption(converter, loc, cookie, x);
1157             }},
1158         spec.u);
1159   }
1160 }
1161 
1162 /// Most IO statements allow one or more of five optional exception condition
1163 /// handling specifiers: ERR, EOR, END, IOSTAT, and IOMSG. The first three
1164 /// cause control flow to transfer to another statement. The final two return
1165 /// information from the runtime, via a variable, about the nature of the
1166 /// condition that occurred. These condition specifiers are handled here.
1167 template <typename A>
1168 static void
1169 genConditionHandlerCall(Fortran::lower::AbstractConverter &converter,
1170                         mlir::Location loc, mlir::Value cookie,
1171                         const A &specList, ConditionSpecInfo &csi) {
1172   for (const auto &spec : specList) {
1173     std::visit(
1174         Fortran::common::visitors{
1175             [&](const Fortran::parser::StatVariable &var) {
1176               csi.ioStatExpr = Fortran::semantics::GetExpr(var);
1177             },
1178             [&](const Fortran::parser::InquireSpec::IntVar &var) {
1179               if (std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t) ==
1180                   Fortran::parser::InquireSpec::IntVar::Kind::Iostat)
1181                 csi.ioStatExpr = Fortran::semantics::GetExpr(
1182                     std::get<Fortran::parser::ScalarIntVariable>(var.t));
1183             },
1184             [&](const Fortran::parser::MsgVariable &var) {
1185               csi.ioMsgExpr = Fortran::semantics::GetExpr(var);
1186             },
1187             [&](const Fortran::parser::InquireSpec::CharVar &var) {
1188               if (std::get<Fortran::parser::InquireSpec::CharVar::Kind>(
1189                       var.t) ==
1190                   Fortran::parser::InquireSpec::CharVar::Kind::Iomsg)
1191                 csi.ioMsgExpr = Fortran::semantics::GetExpr(
1192                     std::get<Fortran::parser::ScalarDefaultCharVariable>(
1193                         var.t));
1194             },
1195             [&](const Fortran::parser::EndLabel &) { csi.hasEnd = true; },
1196             [&](const Fortran::parser::EorLabel &) { csi.hasEor = true; },
1197             [&](const Fortran::parser::ErrLabel &) { csi.hasErr = true; },
1198             [](const auto &) {}},
1199         spec.u);
1200   }
1201   if (!csi.hasAnyConditionSpec())
1202     return;
1203   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1204   mlir::FuncOp enableHandlers =
1205       getIORuntimeFunc<mkIOKey(EnableHandlers)>(loc, builder);
1206   mlir::Type boolType = enableHandlers.getType().getInput(1);
1207   auto boolValue = [&](bool specifierIsPresent) {
1208     return builder.create<mlir::arith::ConstantOp>(
1209         loc, builder.getIntegerAttr(boolType, specifierIsPresent));
1210   };
1211   llvm::SmallVector<mlir::Value> ioArgs = {cookie,
1212                                            boolValue(csi.ioStatExpr != nullptr),
1213                                            boolValue(csi.hasErr),
1214                                            boolValue(csi.hasEnd),
1215                                            boolValue(csi.hasEor),
1216                                            boolValue(csi.ioMsgExpr != nullptr)};
1217   builder.create<fir::CallOp>(loc, enableHandlers, ioArgs);
1218 }
1219 
1220 //===----------------------------------------------------------------------===//
1221 // Data transfer helpers
1222 //===----------------------------------------------------------------------===//
1223 
1224 template <typename SEEK, typename A>
1225 static bool hasIOControl(const A &stmt) {
1226   return hasX<SEEK>(stmt.controls);
1227 }
1228 
1229 template <typename SEEK, typename A>
1230 static const auto *getIOControl(const A &stmt) {
1231   for (const auto &spec : stmt.controls)
1232     if (const auto *result = std::get_if<SEEK>(&spec.u))
1233       return result;
1234   return static_cast<const SEEK *>(nullptr);
1235 }
1236 
1237 /// Returns true iff the expression in the parse tree is not really a format but
1238 /// rather a namelist group.
1239 template <typename A>
1240 static bool formatIsActuallyNamelist(const A &format) {
1241   if (auto *e = std::get_if<Fortran::parser::Expr>(&format.u)) {
1242     auto *expr = Fortran::semantics::GetExpr(*e);
1243     if (const Fortran::semantics::Symbol *y =
1244             Fortran::evaluate::UnwrapWholeSymbolDataRef(*expr))
1245       return y->has<Fortran::semantics::NamelistDetails>();
1246   }
1247   return false;
1248 }
1249 
1250 template <typename A>
1251 static bool isDataTransferFormatted(const A &stmt) {
1252   if (stmt.format)
1253     return !formatIsActuallyNamelist(*stmt.format);
1254   return hasIOControl<Fortran::parser::Format>(stmt);
1255 }
1256 template <>
1257 constexpr bool isDataTransferFormatted<Fortran::parser::PrintStmt>(
1258     const Fortran::parser::PrintStmt &) {
1259   return true; // PRINT is always formatted
1260 }
1261 
1262 template <typename A>
1263 static bool isDataTransferList(const A &stmt) {
1264   if (stmt.format)
1265     return std::holds_alternative<Fortran::parser::Star>(stmt.format->u);
1266   if (auto *mem = getIOControl<Fortran::parser::Format>(stmt))
1267     return std::holds_alternative<Fortran::parser::Star>(mem->u);
1268   return false;
1269 }
1270 template <>
1271 bool isDataTransferList<Fortran::parser::PrintStmt>(
1272     const Fortran::parser::PrintStmt &stmt) {
1273   return std::holds_alternative<Fortran::parser::Star>(
1274       std::get<Fortran::parser::Format>(stmt.t).u);
1275 }
1276 
1277 template <typename A>
1278 static bool isDataTransferInternal(const A &stmt) {
1279   if (stmt.iounit.has_value())
1280     return std::holds_alternative<Fortran::parser::Variable>(stmt.iounit->u);
1281   if (auto *unit = getIOControl<Fortran::parser::IoUnit>(stmt))
1282     return std::holds_alternative<Fortran::parser::Variable>(unit->u);
1283   return false;
1284 }
1285 template <>
1286 constexpr bool isDataTransferInternal<Fortran::parser::PrintStmt>(
1287     const Fortran::parser::PrintStmt &) {
1288   return false;
1289 }
1290 
1291 /// If the variable `var` is an array or of a KIND other than the default
1292 /// (normally 1), then a descriptor is required by the runtime IO API. This
1293 /// condition holds even in F77 sources.
1294 static llvm::Optional<fir::ExtendedValue> getVariableBufferRequiredDescriptor(
1295     Fortran::lower::AbstractConverter &converter,
1296     const Fortran::parser::Variable &var,
1297     Fortran::lower::StatementContext &stmtCtx) {
1298   fir::ExtendedValue varBox =
1299       converter.genExprAddr(var.typedExpr->v.value(), stmtCtx);
1300   fir::KindTy defCharKind = converter.getKindMap().defaultCharacterKind();
1301   mlir::Value varAddr = fir::getBase(varBox);
1302   if (fir::factory::CharacterExprHelper::getCharacterOrSequenceKind(
1303           varAddr.getType()) != defCharKind)
1304     return varBox;
1305   if (fir::factory::CharacterExprHelper::isArray(varAddr.getType()))
1306     return varBox;
1307   return llvm::None;
1308 }
1309 
1310 template <typename A>
1311 static llvm::Optional<fir::ExtendedValue>
1312 maybeGetInternalIODescriptor(Fortran::lower::AbstractConverter &converter,
1313                              const A &stmt,
1314                              Fortran::lower::StatementContext &stmtCtx) {
1315   if (stmt.iounit.has_value())
1316     if (auto *var = std::get_if<Fortran::parser::Variable>(&stmt.iounit->u))
1317       return getVariableBufferRequiredDescriptor(converter, *var, stmtCtx);
1318   if (auto *unit = getIOControl<Fortran::parser::IoUnit>(stmt))
1319     if (auto *var = std::get_if<Fortran::parser::Variable>(&unit->u))
1320       return getVariableBufferRequiredDescriptor(converter, *var, stmtCtx);
1321   return llvm::None;
1322 }
1323 template <>
1324 inline llvm::Optional<fir::ExtendedValue>
1325 maybeGetInternalIODescriptor<Fortran::parser::PrintStmt>(
1326     Fortran::lower::AbstractConverter &, const Fortran::parser::PrintStmt &,
1327     Fortran::lower::StatementContext &) {
1328   return llvm::None;
1329 }
1330 
1331 template <typename A>
1332 static bool isDataTransferAsynchronous(mlir::Location loc, const A &stmt) {
1333   if (auto *asynch =
1334           getIOControl<Fortran::parser::IoControlSpec::Asynchronous>(stmt)) {
1335     // FIXME: should contain a string of YES or NO
1336     TODO(loc, "asynchronous transfers not implemented in runtime");
1337   }
1338   return false;
1339 }
1340 template <>
1341 bool isDataTransferAsynchronous<Fortran::parser::PrintStmt>(
1342     mlir::Location, const Fortran::parser::PrintStmt &) {
1343   return false;
1344 }
1345 
1346 template <typename A>
1347 static bool isDataTransferNamelist(const A &stmt) {
1348   if (stmt.format)
1349     return formatIsActuallyNamelist(*stmt.format);
1350   return hasIOControl<Fortran::parser::Name>(stmt);
1351 }
1352 template <>
1353 constexpr bool isDataTransferNamelist<Fortran::parser::PrintStmt>(
1354     const Fortran::parser::PrintStmt &) {
1355   return false;
1356 }
1357 
1358 /// Lowers a format statment that uses an assigned variable label reference as
1359 /// a select operation to allow for run-time selection of the format statement.
1360 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
1361 lowerReferenceAsStringSelect(Fortran::lower::AbstractConverter &converter,
1362                              mlir::Location loc,
1363                              const Fortran::lower::SomeExpr &expr,
1364                              mlir::Type strTy, mlir::Type lenTy,
1365                              Fortran::lower::StatementContext &stmtCtx) {
1366   // Possible optimization TODO: Instead of inlining a selectOp every time there
1367   // is a variable reference to a format statement, a function with the selectOp
1368   // could be generated to reduce code size. It is not clear if such an
1369   // optimization would be deployed very often or improve the object code
1370   // beyond, say, what GVN/GCM might produce.
1371 
1372   // Create the requisite blocks to inline a selectOp.
1373   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1374   mlir::Block *startBlock = builder.getBlock();
1375   mlir::Block *endBlock = startBlock->splitBlock(builder.getInsertionPoint());
1376   mlir::Block *block = startBlock->splitBlock(builder.getInsertionPoint());
1377   builder.setInsertionPointToEnd(block);
1378 
1379   llvm::SmallVector<int64_t> indexList;
1380   llvm::SmallVector<mlir::Block *> blockList;
1381 
1382   auto symbol = GetLastSymbol(&expr);
1383   Fortran::lower::pft::LabelSet labels;
1384   [[maybe_unused]] auto foundLabelSet =
1385       converter.lookupLabelSet(*symbol, labels);
1386   assert(foundLabelSet && "Label not found in map");
1387 
1388   for (auto label : labels) {
1389     indexList.push_back(label);
1390     auto *eval = converter.lookupLabel(label);
1391     assert(eval && "Label is missing from the table");
1392 
1393     llvm::StringRef text = toStringRef(eval->position);
1394     mlir::Value stringRef;
1395     mlir::Value stringLen;
1396     if (eval->isA<Fortran::parser::FormatStmt>()) {
1397       assert(text.find('(') != llvm::StringRef::npos &&
1398              "FORMAT is unexpectedly ill-formed");
1399       // This is a format statement, so extract the spec from the text.
1400       std::tuple<mlir::Value, mlir::Value, mlir::Value> stringLit =
1401           lowerSourceTextAsStringLit(converter, loc, text, strTy, lenTy);
1402       stringRef = std::get<0>(stringLit);
1403       stringLen = std::get<1>(stringLit);
1404     } else {
1405       // This is not a format statement, so use null.
1406       stringRef = builder.createConvert(
1407           loc, strTy,
1408           builder.createIntegerConstant(loc, builder.getIndexType(), 0));
1409       stringLen = builder.createIntegerConstant(loc, lenTy, 0);
1410     }
1411 
1412     // Pass the format string reference and the string length out of the select
1413     // statement.
1414     llvm::SmallVector<mlir::Value> args = {stringRef, stringLen};
1415     builder.create<mlir::cf::BranchOp>(loc, endBlock, args);
1416 
1417     // Add block to the list of cases and make a new one.
1418     blockList.push_back(block);
1419     block = block->splitBlock(builder.getInsertionPoint());
1420     builder.setInsertionPointToEnd(block);
1421   }
1422 
1423   // Create the unit case which should result in an error.
1424   auto *unitBlock = block->splitBlock(builder.getInsertionPoint());
1425   builder.setInsertionPointToEnd(unitBlock);
1426 
1427   // Crash the program.
1428   builder.create<fir::UnreachableOp>(loc);
1429 
1430   // Add unit case to the select statement.
1431   blockList.push_back(unitBlock);
1432 
1433   // Lower the selectOp.
1434   builder.setInsertionPointToEnd(startBlock);
1435   auto label = fir::getBase(converter.genExprValue(&expr, stmtCtx, loc));
1436   builder.create<fir::SelectOp>(loc, label, indexList, blockList);
1437 
1438   builder.setInsertionPointToEnd(endBlock);
1439   endBlock->addArgument(strTy, loc);
1440   endBlock->addArgument(lenTy, loc);
1441 
1442   // Handle and return the string reference and length selected by the selectOp.
1443   auto buff = endBlock->getArgument(0);
1444   auto len = endBlock->getArgument(1);
1445 
1446   return {buff, len, mlir::Value{}};
1447 }
1448 
1449 /// Generate a reference to a format string.  There are four cases - a format
1450 /// statement label, a character format expression, an integer that holds the
1451 /// label of a format statement, and the * case.  The first three are done here.
1452 /// The * case is done elsewhere.
1453 static std::tuple<mlir::Value, mlir::Value, mlir::Value>
1454 genFormat(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1455           const Fortran::parser::Format &format, mlir::Type strTy,
1456           mlir::Type lenTy, Fortran::lower::StatementContext &stmtCtx) {
1457   if (const auto *label = std::get_if<Fortran::parser::Label>(&format.u)) {
1458     // format statement label
1459     auto eval = converter.lookupLabel(*label);
1460     assert(eval && "FORMAT not found in PROCEDURE");
1461     return lowerSourceTextAsStringLit(
1462         converter, loc, toStringRef(eval->position), strTy, lenTy);
1463   }
1464   const auto *pExpr = std::get_if<Fortran::parser::Expr>(&format.u);
1465   assert(pExpr && "missing format expression");
1466   auto e = Fortran::semantics::GetExpr(*pExpr);
1467   if (Fortran::semantics::ExprHasTypeCategory(
1468           *e, Fortran::common::TypeCategory::Character))
1469     // character expression
1470     return lowerStringLit(converter, loc, stmtCtx, *pExpr, strTy, lenTy);
1471 
1472   if (Fortran::semantics::ExprHasTypeCategory(
1473           *e, Fortran::common::TypeCategory::Integer) &&
1474       e->Rank() == 0 && Fortran::evaluate::UnwrapWholeSymbolDataRef(*e)) {
1475     // Treat as a scalar integer variable containing an ASSIGN label.
1476     return lowerReferenceAsStringSelect(converter, loc, *e, strTy, lenTy,
1477                                         stmtCtx);
1478   }
1479 
1480   // Legacy extension: it is possible that `*e` is not a scalar INTEGER
1481   // variable containing a label value. The output appears to be the source text
1482   // that initialized the variable? Needs more investigatation.
1483   TODO(loc, "io-control-spec contains a reference to a non-integer, "
1484             "non-scalar, or non-variable");
1485 }
1486 
1487 template <typename A>
1488 std::tuple<mlir::Value, mlir::Value, mlir::Value>
1489 getFormat(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1490           const A &stmt, mlir::Type strTy, mlir::Type lenTy,
1491           Fortran ::lower::StatementContext &stmtCtx) {
1492   if (stmt.format && !formatIsActuallyNamelist(*stmt.format))
1493     return genFormat(converter, loc, *stmt.format, strTy, lenTy, stmtCtx);
1494   return genFormat(converter, loc, *getIOControl<Fortran::parser::Format>(stmt),
1495                    strTy, lenTy, stmtCtx);
1496 }
1497 template <>
1498 std::tuple<mlir::Value, mlir::Value, mlir::Value>
1499 getFormat<Fortran::parser::PrintStmt>(
1500     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1501     const Fortran::parser::PrintStmt &stmt, mlir::Type strTy, mlir::Type lenTy,
1502     Fortran::lower::StatementContext &stmtCtx) {
1503   return genFormat(converter, loc, std::get<Fortran::parser::Format>(stmt.t),
1504                    strTy, lenTy, stmtCtx);
1505 }
1506 
1507 /// Get a buffer for an internal file data transfer.
1508 template <typename A>
1509 std::tuple<mlir::Value, mlir::Value>
1510 getBuffer(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1511           const A &stmt, mlir::Type strTy, mlir::Type lenTy,
1512           Fortran::lower::StatementContext &stmtCtx) {
1513   const Fortran::parser::IoUnit *iounit =
1514       stmt.iounit ? &*stmt.iounit : getIOControl<Fortran::parser::IoUnit>(stmt);
1515   if (iounit)
1516     if (auto *var = std::get_if<Fortran::parser::Variable>(&iounit->u))
1517       if (auto *expr = Fortran::semantics::GetExpr(*var))
1518         return genBuffer(converter, loc, *expr, strTy, lenTy, stmtCtx);
1519   llvm::report_fatal_error("failed to get IoUnit expr in lowering");
1520 }
1521 
1522 static mlir::Value genIOUnit(Fortran::lower::AbstractConverter &converter,
1523                              mlir::Location loc,
1524                              const Fortran::parser::IoUnit &iounit,
1525                              mlir::Type ty,
1526                              Fortran::lower::StatementContext &stmtCtx) {
1527   auto &builder = converter.getFirOpBuilder();
1528   if (auto *e = std::get_if<Fortran::parser::FileUnitNumber>(&iounit.u)) {
1529     auto ex = fir::getBase(
1530         converter.genExprValue(Fortran::semantics::GetExpr(*e), stmtCtx, loc));
1531     return builder.createConvert(loc, ty, ex);
1532   }
1533   return builder.create<mlir::arith::ConstantOp>(
1534       loc, builder.getIntegerAttr(ty, Fortran::runtime::io::DefaultUnit));
1535 }
1536 
1537 template <typename A>
1538 mlir::Value getIOUnit(Fortran::lower::AbstractConverter &converter,
1539                       mlir::Location loc, const A &stmt, mlir::Type ty,
1540                       Fortran::lower::StatementContext &stmtCtx) {
1541   if (stmt.iounit)
1542     return genIOUnit(converter, loc, *stmt.iounit, ty, stmtCtx);
1543   if (auto *iounit = getIOControl<Fortran::parser::IoUnit>(stmt))
1544     return genIOUnit(converter, loc, *iounit, ty, stmtCtx);
1545   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1546   return builder.create<mlir::arith::ConstantOp>(
1547       loc, builder.getIntegerAttr(ty, Fortran::runtime::io::DefaultUnit));
1548 }
1549 
1550 //===----------------------------------------------------------------------===//
1551 // Generators for each IO statement type.
1552 //===----------------------------------------------------------------------===//
1553 
1554 template <typename K, typename S>
1555 static mlir::Value genBasicIOStmt(Fortran::lower::AbstractConverter &converter,
1556                                   const S &stmt) {
1557   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1558   Fortran::lower::StatementContext stmtCtx;
1559   mlir::Location loc = converter.getCurrentLocation();
1560   mlir::FuncOp beginFunc = getIORuntimeFunc<K>(loc, builder);
1561   mlir::FunctionType beginFuncTy = beginFunc.getType();
1562   mlir::Value unit = fir::getBase(converter.genExprValue(
1563       getExpr<Fortran::parser::FileUnitNumber>(stmt), stmtCtx, loc));
1564   mlir::Value un = builder.createConvert(loc, beginFuncTy.getInput(0), unit);
1565   mlir::Value file = locToFilename(converter, loc, beginFuncTy.getInput(1));
1566   mlir::Value line = locToLineNo(converter, loc, beginFuncTy.getInput(2));
1567   auto call = builder.create<fir::CallOp>(loc, beginFunc,
1568                                           mlir::ValueRange{un, file, line});
1569   mlir::Value cookie = call.getResult(0);
1570   ConditionSpecInfo csi;
1571   genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1572   mlir::Value ok;
1573   auto insertPt = builder.saveInsertionPoint();
1574   threadSpecs(converter, loc, cookie, stmt.v, csi.hasErrorConditionSpec(), ok);
1575   builder.restoreInsertionPoint(insertPt);
1576   return genEndIO(converter, converter.getCurrentLocation(), cookie, csi,
1577                   stmtCtx);
1578 }
1579 
1580 mlir::Value Fortran::lower::genBackspaceStatement(
1581     Fortran::lower::AbstractConverter &converter,
1582     const Fortran::parser::BackspaceStmt &stmt) {
1583   return genBasicIOStmt<mkIOKey(BeginBackspace)>(converter, stmt);
1584 }
1585 
1586 mlir::Value Fortran::lower::genEndfileStatement(
1587     Fortran::lower::AbstractConverter &converter,
1588     const Fortran::parser::EndfileStmt &stmt) {
1589   return genBasicIOStmt<mkIOKey(BeginEndfile)>(converter, stmt);
1590 }
1591 
1592 mlir::Value
1593 Fortran::lower::genFlushStatement(Fortran::lower::AbstractConverter &converter,
1594                                   const Fortran::parser::FlushStmt &stmt) {
1595   return genBasicIOStmt<mkIOKey(BeginFlush)>(converter, stmt);
1596 }
1597 
1598 mlir::Value
1599 Fortran::lower::genRewindStatement(Fortran::lower::AbstractConverter &converter,
1600                                    const Fortran::parser::RewindStmt &stmt) {
1601   return genBasicIOStmt<mkIOKey(BeginRewind)>(converter, stmt);
1602 }
1603 
1604 static mlir::Value
1605 genNewunitSpec(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1606                mlir::Value cookie,
1607                const std::list<Fortran::parser::ConnectSpec> &specList) {
1608   for (const auto &spec : specList)
1609     if (auto *newunit =
1610             std::get_if<Fortran::parser::ConnectSpec::Newunit>(&spec.u)) {
1611       Fortran::lower::StatementContext stmtCtx;
1612       fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1613       mlir::FuncOp ioFunc = getIORuntimeFunc<mkIOKey(GetNewUnit)>(loc, builder);
1614       mlir::FunctionType ioFuncTy = ioFunc.getType();
1615       const auto *var = Fortran::semantics::GetExpr(newunit->v);
1616       mlir::Value addr = builder.createConvert(
1617           loc, ioFuncTy.getInput(1),
1618           fir::getBase(converter.genExprAddr(var, stmtCtx, loc)));
1619       auto kind = builder.createIntegerConstant(loc, ioFuncTy.getInput(2),
1620                                                 var->GetType().value().kind());
1621       llvm::SmallVector<mlir::Value> ioArgs = {cookie, addr, kind};
1622       return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
1623     }
1624   llvm_unreachable("missing Newunit spec");
1625 }
1626 
1627 mlir::Value
1628 Fortran::lower::genOpenStatement(Fortran::lower::AbstractConverter &converter,
1629                                  const Fortran::parser::OpenStmt &stmt) {
1630   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1631   Fortran::lower::StatementContext stmtCtx;
1632   mlir::FuncOp beginFunc;
1633   llvm::SmallVector<mlir::Value> beginArgs;
1634   mlir::Location loc = converter.getCurrentLocation();
1635   bool hasNewunitSpec = false;
1636   if (hasSpec<Fortran::parser::FileUnitNumber>(stmt)) {
1637     beginFunc = getIORuntimeFunc<mkIOKey(BeginOpenUnit)>(loc, builder);
1638     mlir::FunctionType beginFuncTy = beginFunc.getType();
1639     mlir::Value unit = fir::getBase(converter.genExprValue(
1640         getExpr<Fortran::parser::FileUnitNumber>(stmt), stmtCtx, loc));
1641     beginArgs.push_back(
1642         builder.createConvert(loc, beginFuncTy.getInput(0), unit));
1643     beginArgs.push_back(locToFilename(converter, loc, beginFuncTy.getInput(1)));
1644     beginArgs.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(2)));
1645   } else {
1646     hasNewunitSpec = hasSpec<Fortran::parser::ConnectSpec::Newunit>(stmt);
1647     assert(hasNewunitSpec && "missing unit specifier");
1648     beginFunc = getIORuntimeFunc<mkIOKey(BeginOpenNewUnit)>(loc, builder);
1649     mlir::FunctionType beginFuncTy = beginFunc.getType();
1650     beginArgs.push_back(locToFilename(converter, loc, beginFuncTy.getInput(0)));
1651     beginArgs.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(1)));
1652   }
1653   auto cookie =
1654       builder.create<fir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
1655   ConditionSpecInfo csi;
1656   genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1657   mlir::Value ok;
1658   auto insertPt = builder.saveInsertionPoint();
1659   threadSpecs(converter, loc, cookie, stmt.v, csi.hasErrorConditionSpec(), ok);
1660   if (hasNewunitSpec)
1661     genNewunitSpec(converter, loc, cookie, stmt.v);
1662   builder.restoreInsertionPoint(insertPt);
1663   return genEndIO(converter, loc, cookie, csi, stmtCtx);
1664 }
1665 
1666 mlir::Value
1667 Fortran::lower::genCloseStatement(Fortran::lower::AbstractConverter &converter,
1668                                   const Fortran::parser::CloseStmt &stmt) {
1669   return genBasicIOStmt<mkIOKey(BeginClose)>(converter, stmt);
1670 }
1671 
1672 mlir::Value
1673 Fortran::lower::genWaitStatement(Fortran::lower::AbstractConverter &converter,
1674                                  const Fortran::parser::WaitStmt &stmt) {
1675   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1676   Fortran::lower::StatementContext stmtCtx;
1677   mlir::Location loc = converter.getCurrentLocation();
1678   bool hasId = hasSpec<Fortran::parser::IdExpr>(stmt);
1679   mlir::FuncOp beginFunc =
1680       hasId ? getIORuntimeFunc<mkIOKey(BeginWait)>(loc, builder)
1681             : getIORuntimeFunc<mkIOKey(BeginWaitAll)>(loc, builder);
1682   mlir::FunctionType beginFuncTy = beginFunc.getType();
1683   mlir::Value unit = fir::getBase(converter.genExprValue(
1684       getExpr<Fortran::parser::FileUnitNumber>(stmt), stmtCtx, loc));
1685   mlir::Value un = builder.createConvert(loc, beginFuncTy.getInput(0), unit);
1686   llvm::SmallVector<mlir::Value> args{un};
1687   if (hasId) {
1688     mlir::Value id = fir::getBase(converter.genExprValue(
1689         getExpr<Fortran::parser::IdExpr>(stmt), stmtCtx, loc));
1690     args.push_back(builder.createConvert(loc, beginFuncTy.getInput(1), id));
1691   }
1692   auto cookie = builder.create<fir::CallOp>(loc, beginFunc, args).getResult(0);
1693   ConditionSpecInfo csi;
1694   genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1695   return genEndIO(converter, converter.getCurrentLocation(), cookie, csi,
1696                   stmtCtx);
1697 }
1698 
1699 //===----------------------------------------------------------------------===//
1700 // Data transfer statements.
1701 //
1702 // There are several dimensions to the API with regard to data transfer
1703 // statements that need to be considered.
1704 //
1705 //   - input (READ) vs. output (WRITE, PRINT)
1706 //   - unformatted vs. formatted vs. list vs. namelist
1707 //   - synchronous vs. asynchronous
1708 //   - external vs. internal
1709 //===----------------------------------------------------------------------===//
1710 
1711 // Get the begin data transfer IO function to call for the given values.
1712 template <bool isInput>
1713 mlir::FuncOp
1714 getBeginDataTransferFunc(mlir::Location loc, fir::FirOpBuilder &builder,
1715                          bool isFormatted, bool isListOrNml, bool isInternal,
1716                          bool isInternalWithDesc, bool isAsync) {
1717   if constexpr (isInput) {
1718     if (isAsync)
1719       return getIORuntimeFunc<mkIOKey(BeginAsynchronousInput)>(loc, builder);
1720     if (isFormatted || isListOrNml) {
1721       if (isInternal) {
1722         if (isInternalWithDesc) {
1723           if (isListOrNml)
1724             return getIORuntimeFunc<mkIOKey(BeginInternalArrayListInput)>(
1725                 loc, builder);
1726           return getIORuntimeFunc<mkIOKey(BeginInternalArrayFormattedInput)>(
1727               loc, builder);
1728         }
1729         if (isListOrNml)
1730           return getIORuntimeFunc<mkIOKey(BeginInternalListInput)>(loc,
1731                                                                    builder);
1732         return getIORuntimeFunc<mkIOKey(BeginInternalFormattedInput)>(loc,
1733                                                                       builder);
1734       }
1735       if (isListOrNml)
1736         return getIORuntimeFunc<mkIOKey(BeginExternalListInput)>(loc, builder);
1737       return getIORuntimeFunc<mkIOKey(BeginExternalFormattedInput)>(loc,
1738                                                                     builder);
1739     }
1740     return getIORuntimeFunc<mkIOKey(BeginUnformattedInput)>(loc, builder);
1741   } else {
1742     if (isAsync)
1743       return getIORuntimeFunc<mkIOKey(BeginAsynchronousOutput)>(loc, builder);
1744     if (isFormatted || isListOrNml) {
1745       if (isInternal) {
1746         if (isInternalWithDesc) {
1747           if (isListOrNml)
1748             return getIORuntimeFunc<mkIOKey(BeginInternalArrayListOutput)>(
1749                 loc, builder);
1750           return getIORuntimeFunc<mkIOKey(BeginInternalArrayFormattedOutput)>(
1751               loc, builder);
1752         }
1753         if (isListOrNml)
1754           return getIORuntimeFunc<mkIOKey(BeginInternalListOutput)>(loc,
1755                                                                     builder);
1756         return getIORuntimeFunc<mkIOKey(BeginInternalFormattedOutput)>(loc,
1757                                                                        builder);
1758       }
1759       if (isListOrNml)
1760         return getIORuntimeFunc<mkIOKey(BeginExternalListOutput)>(loc, builder);
1761       return getIORuntimeFunc<mkIOKey(BeginExternalFormattedOutput)>(loc,
1762                                                                      builder);
1763     }
1764     return getIORuntimeFunc<mkIOKey(BeginUnformattedOutput)>(loc, builder);
1765   }
1766 }
1767 
1768 /// Generate the arguments of a begin data transfer statement call.
1769 template <bool hasIOCtrl, typename A>
1770 void genBeginDataTransferCallArgs(
1771     llvm::SmallVectorImpl<mlir::Value> &ioArgs,
1772     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1773     const A &stmt, mlir::FunctionType ioFuncTy, bool isFormatted,
1774     bool isListOrNml, [[maybe_unused]] bool isInternal,
1775     [[maybe_unused]] bool isAsync,
1776     const llvm::Optional<fir::ExtendedValue> &descRef,
1777     Fortran::lower::StatementContext &stmtCtx) {
1778   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1779   auto maybeGetFormatArgs = [&]() {
1780     if (!isFormatted || isListOrNml)
1781       return;
1782     auto pair =
1783         getFormat(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
1784                   ioFuncTy.getInput(ioArgs.size() + 1), stmtCtx);
1785     ioArgs.push_back(std::get<0>(pair)); // format character string
1786     ioArgs.push_back(std::get<1>(pair)); // format length
1787   };
1788   if constexpr (hasIOCtrl) { // READ or WRITE
1789     if (isInternal) {
1790       // descriptor or scalar variable; maybe explicit format; scratch area
1791       if (descRef.hasValue()) {
1792         mlir::Value desc = builder.createBox(loc, *descRef);
1793         ioArgs.push_back(
1794             builder.createConvert(loc, ioFuncTy.getInput(ioArgs.size()), desc));
1795       } else {
1796         std::tuple<mlir::Value, mlir::Value> pair =
1797             getBuffer(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
1798                       ioFuncTy.getInput(ioArgs.size() + 1), stmtCtx);
1799         ioArgs.push_back(std::get<0>(pair)); // scalar character variable
1800         ioArgs.push_back(std::get<1>(pair)); // character length
1801       }
1802       maybeGetFormatArgs();
1803       ioArgs.push_back( // internal scratch area buffer
1804           getDefaultScratch(builder, loc, ioFuncTy.getInput(ioArgs.size())));
1805       ioArgs.push_back( // buffer length
1806           getDefaultScratchLen(builder, loc, ioFuncTy.getInput(ioArgs.size())));
1807     } else if (isAsync) { // unit; REC; buffer and length
1808       ioArgs.push_back(getIOUnit(converter, loc, stmt,
1809                                  ioFuncTy.getInput(ioArgs.size()), stmtCtx));
1810       TODO(loc, "asynchronous");
1811     } else { // external IO - maybe explicit format; unit
1812       maybeGetFormatArgs();
1813       ioArgs.push_back(getIOUnit(converter, loc, stmt,
1814                                  ioFuncTy.getInput(ioArgs.size()), stmtCtx));
1815     }
1816   } else { // PRINT - maybe explicit format; default unit
1817     maybeGetFormatArgs();
1818     ioArgs.push_back(builder.create<mlir::arith::ConstantOp>(
1819         loc, builder.getIntegerAttr(ioFuncTy.getInput(ioArgs.size()),
1820                                     Fortran::runtime::io::DefaultUnit)));
1821   }
1822   // File name and line number are always the last two arguments.
1823   ioArgs.push_back(
1824       locToFilename(converter, loc, ioFuncTy.getInput(ioArgs.size())));
1825   ioArgs.push_back(
1826       locToLineNo(converter, loc, ioFuncTy.getInput(ioArgs.size())));
1827 }
1828 
1829 template <bool isInput, bool hasIOCtrl = true, typename A>
1830 static mlir::Value
1831 genDataTransferStmt(Fortran::lower::AbstractConverter &converter,
1832                     const A &stmt) {
1833   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1834   Fortran::lower::StatementContext stmtCtx;
1835   mlir::Location loc = converter.getCurrentLocation();
1836   const bool isFormatted = isDataTransferFormatted(stmt);
1837   const bool isList = isFormatted ? isDataTransferList(stmt) : false;
1838   const bool isInternal = isDataTransferInternal(stmt);
1839   llvm::Optional<fir::ExtendedValue> descRef =
1840       isInternal ? maybeGetInternalIODescriptor(converter, stmt, stmtCtx)
1841                  : llvm::None;
1842   const bool isInternalWithDesc = descRef.hasValue();
1843   const bool isAsync = isDataTransferAsynchronous(loc, stmt);
1844   const bool isNml = isDataTransferNamelist(stmt);
1845 
1846   // Generate the begin data transfer function call.
1847   mlir::FuncOp ioFunc = getBeginDataTransferFunc<isInput>(
1848       loc, builder, isFormatted, isList || isNml, isInternal,
1849       isInternalWithDesc, isAsync);
1850   llvm::SmallVector<mlir::Value> ioArgs;
1851   genBeginDataTransferCallArgs<hasIOCtrl>(
1852       ioArgs, converter, loc, stmt, ioFunc.getType(), isFormatted,
1853       isList || isNml, isInternal, isAsync, descRef, stmtCtx);
1854   mlir::Value cookie =
1855       builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
1856 
1857   // Generate an EnableHandlers call and remaining specifier calls.
1858   ConditionSpecInfo csi;
1859   auto insertPt = builder.saveInsertionPoint();
1860   mlir::Value ok;
1861   if constexpr (hasIOCtrl) {
1862     genConditionHandlerCall(converter, loc, cookie, stmt.controls, csi);
1863     threadSpecs(converter, loc, cookie, stmt.controls,
1864                 csi.hasErrorConditionSpec(), ok);
1865   }
1866 
1867   // Generate data transfer list calls.
1868   if constexpr (isInput) { // READ
1869     if (isNml)
1870       genNamelistIO(converter, cookie,
1871                     getIORuntimeFunc<mkIOKey(InputNamelist)>(loc, builder),
1872                     *getIOControl<Fortran::parser::Name>(stmt)->symbol,
1873                     csi.hasTransferConditionSpec(), ok, stmtCtx);
1874     else
1875       genInputItemList(converter, cookie, stmt.items, isFormatted,
1876                        csi.hasTransferConditionSpec(), ok, /*inLoop=*/false,
1877                        stmtCtx);
1878   } else if constexpr (std::is_same_v<A, Fortran::parser::WriteStmt>) {
1879     if (isNml)
1880       genNamelistIO(converter, cookie,
1881                     getIORuntimeFunc<mkIOKey(OutputNamelist)>(loc, builder),
1882                     *getIOControl<Fortran::parser::Name>(stmt)->symbol,
1883                     csi.hasTransferConditionSpec(), ok, stmtCtx);
1884     else
1885       genOutputItemList(converter, cookie, stmt.items, isFormatted,
1886                         csi.hasTransferConditionSpec(), ok,
1887                         /*inLoop=*/false, stmtCtx);
1888   } else { // PRINT
1889     genOutputItemList(converter, cookie, std::get<1>(stmt.t), isFormatted,
1890                       csi.hasTransferConditionSpec(), ok,
1891                       /*inLoop=*/false, stmtCtx);
1892   }
1893   stmtCtx.finalize();
1894 
1895   builder.restoreInsertionPoint(insertPt);
1896   if constexpr (hasIOCtrl) {
1897     genIOReadSize(converter, loc, cookie, stmt.controls,
1898                   csi.hasErrorConditionSpec());
1899   }
1900   // Generate end statement call/s.
1901   return genEndIO(converter, loc, cookie, csi, stmtCtx);
1902 }
1903 
1904 void Fortran::lower::genPrintStatement(
1905     Fortran::lower::AbstractConverter &converter,
1906     const Fortran::parser::PrintStmt &stmt) {
1907   // PRINT does not take an io-control-spec. It only has a format specifier, so
1908   // it is a simplified case of WRITE.
1909   genDataTransferStmt</*isInput=*/false, /*ioCtrl=*/false>(converter, stmt);
1910 }
1911 
1912 mlir::Value
1913 Fortran::lower::genWriteStatement(Fortran::lower::AbstractConverter &converter,
1914                                   const Fortran::parser::WriteStmt &stmt) {
1915   return genDataTransferStmt</*isInput=*/false>(converter, stmt);
1916 }
1917 
1918 mlir::Value
1919 Fortran::lower::genReadStatement(Fortran::lower::AbstractConverter &converter,
1920                                  const Fortran::parser::ReadStmt &stmt) {
1921   return genDataTransferStmt</*isInput=*/true>(converter, stmt);
1922 }
1923 
1924 /// Get the file expression from the inquire spec list. Also return if the
1925 /// expression is a file name.
1926 static std::pair<const Fortran::lower::SomeExpr *, bool>
1927 getInquireFileExpr(const std::list<Fortran::parser::InquireSpec> *stmt) {
1928   if (!stmt)
1929     return {nullptr, /*filename?=*/false};
1930   for (const Fortran::parser::InquireSpec &spec : *stmt) {
1931     if (auto *f = std::get_if<Fortran::parser::FileUnitNumber>(&spec.u))
1932       return {Fortran::semantics::GetExpr(*f), /*filename?=*/false};
1933     if (auto *f = std::get_if<Fortran::parser::FileNameExpr>(&spec.u))
1934       return {Fortran::semantics::GetExpr(*f), /*filename?=*/true};
1935   }
1936   // semantics should have already caught this condition
1937   llvm::report_fatal_error("inquire spec must have a file");
1938 }
1939 
1940 /// Generate calls to the four distinct INQUIRE subhandlers. An INQUIRE may
1941 /// return values of type CHARACTER, INTEGER, or LOGICAL. There is one
1942 /// additional special case for INQUIRE with both PENDING and ID specifiers.
1943 template <typename A>
1944 static mlir::Value genInquireSpec(Fortran::lower::AbstractConverter &converter,
1945                                   mlir::Location loc, mlir::Value cookie,
1946                                   mlir::Value idExpr, const A &var,
1947                                   Fortran::lower::StatementContext &stmtCtx) {
1948   // default case: do nothing
1949   return {};
1950 }
1951 /// Specialization for CHARACTER.
1952 template <>
1953 mlir::Value genInquireSpec<Fortran::parser::InquireSpec::CharVar>(
1954     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1955     mlir::Value cookie, mlir::Value idExpr,
1956     const Fortran::parser::InquireSpec::CharVar &var,
1957     Fortran::lower::StatementContext &stmtCtx) {
1958   // IOMSG is handled with exception conditions
1959   if (std::get<Fortran::parser::InquireSpec::CharVar::Kind>(var.t) ==
1960       Fortran::parser::InquireSpec::CharVar::Kind::Iomsg)
1961     return {};
1962   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1963   mlir::FuncOp specFunc =
1964       getIORuntimeFunc<mkIOKey(InquireCharacter)>(loc, builder);
1965   mlir::FunctionType specFuncTy = specFunc.getType();
1966   const auto *varExpr = Fortran::semantics::GetExpr(
1967       std::get<Fortran::parser::ScalarDefaultCharVariable>(var.t));
1968   fir::ExtendedValue str = converter.genExprAddr(varExpr, stmtCtx, loc);
1969   llvm::SmallVector<mlir::Value> args = {
1970       builder.createConvert(loc, specFuncTy.getInput(0), cookie),
1971       builder.createIntegerConstant(
1972           loc, specFuncTy.getInput(1),
1973           Fortran::runtime::io::HashInquiryKeyword(
1974               Fortran::parser::InquireSpec::CharVar::EnumToString(
1975                   std::get<Fortran::parser::InquireSpec::CharVar::Kind>(var.t))
1976                   .c_str())),
1977       builder.createConvert(loc, specFuncTy.getInput(2), fir::getBase(str)),
1978       builder.createConvert(loc, specFuncTy.getInput(3), fir::getLen(str))};
1979   return builder.create<fir::CallOp>(loc, specFunc, args).getResult(0);
1980 }
1981 /// Specialization for INTEGER.
1982 template <>
1983 mlir::Value genInquireSpec<Fortran::parser::InquireSpec::IntVar>(
1984     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
1985     mlir::Value cookie, mlir::Value idExpr,
1986     const Fortran::parser::InquireSpec::IntVar &var,
1987     Fortran::lower::StatementContext &stmtCtx) {
1988   // IOSTAT is handled with exception conditions
1989   if (std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t) ==
1990       Fortran::parser::InquireSpec::IntVar::Kind::Iostat)
1991     return {};
1992   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1993   mlir::FuncOp specFunc =
1994       getIORuntimeFunc<mkIOKey(InquireInteger64)>(loc, builder);
1995   mlir::FunctionType specFuncTy = specFunc.getType();
1996   const auto *varExpr = Fortran::semantics::GetExpr(
1997       std::get<Fortran::parser::ScalarIntVariable>(var.t));
1998   mlir::Value addr = fir::getBase(converter.genExprAddr(varExpr, stmtCtx, loc));
1999   mlir::Type eleTy = fir::dyn_cast_ptrEleTy(addr.getType());
2000   if (!eleTy)
2001     fir::emitFatalError(loc,
2002                         "internal error: expected a memory reference type");
2003   auto width = eleTy.cast<mlir::IntegerType>().getWidth();
2004   mlir::IndexType idxTy = builder.getIndexType();
2005   mlir::Value kind = builder.createIntegerConstant(loc, idxTy, width / 8);
2006   llvm::SmallVector<mlir::Value> args = {
2007       builder.createConvert(loc, specFuncTy.getInput(0), cookie),
2008       builder.createIntegerConstant(
2009           loc, specFuncTy.getInput(1),
2010           Fortran::runtime::io::HashInquiryKeyword(
2011               Fortran::parser::InquireSpec::IntVar::EnumToString(
2012                   std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t))
2013                   .c_str())),
2014       builder.createConvert(loc, specFuncTy.getInput(2), addr),
2015       builder.createConvert(loc, specFuncTy.getInput(3), kind)};
2016   return builder.create<fir::CallOp>(loc, specFunc, args).getResult(0);
2017 }
2018 /// Specialization for LOGICAL and (PENDING + ID).
2019 template <>
2020 mlir::Value genInquireSpec<Fortran::parser::InquireSpec::LogVar>(
2021     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
2022     mlir::Value cookie, mlir::Value idExpr,
2023     const Fortran::parser::InquireSpec::LogVar &var,
2024     Fortran::lower::StatementContext &stmtCtx) {
2025   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2026   auto logVarKind = std::get<Fortran::parser::InquireSpec::LogVar::Kind>(var.t);
2027   bool pendId =
2028       idExpr &&
2029       logVarKind == Fortran::parser::InquireSpec::LogVar::Kind::Pending;
2030   mlir::FuncOp specFunc =
2031       pendId ? getIORuntimeFunc<mkIOKey(InquirePendingId)>(loc, builder)
2032              : getIORuntimeFunc<mkIOKey(InquireLogical)>(loc, builder);
2033   mlir::FunctionType specFuncTy = specFunc.getType();
2034   mlir::Value addr = fir::getBase(converter.genExprAddr(
2035       Fortran::semantics::GetExpr(
2036           std::get<Fortran::parser::Scalar<
2037               Fortran::parser::Logical<Fortran::parser::Variable>>>(var.t)),
2038       stmtCtx, loc));
2039   llvm::SmallVector<mlir::Value> args = {
2040       builder.createConvert(loc, specFuncTy.getInput(0), cookie)};
2041   if (pendId)
2042     args.push_back(builder.createConvert(loc, specFuncTy.getInput(1), idExpr));
2043   else
2044     args.push_back(builder.createIntegerConstant(
2045         loc, specFuncTy.getInput(1),
2046         Fortran::runtime::io::HashInquiryKeyword(
2047             Fortran::parser::InquireSpec::LogVar::EnumToString(logVarKind)
2048                 .c_str())));
2049   args.push_back(builder.createConvert(loc, specFuncTy.getInput(2), addr));
2050   auto call = builder.create<fir::CallOp>(loc, specFunc, args);
2051   boolRefToLogical(loc, builder, addr);
2052   return call.getResult(0);
2053 }
2054 
2055 /// If there is an IdExpr in the list of inquire-specs, then lower it and return
2056 /// the resulting Value. Otherwise, return null.
2057 static mlir::Value
2058 lowerIdExpr(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
2059             const std::list<Fortran::parser::InquireSpec> &ispecs,
2060             Fortran::lower::StatementContext &stmtCtx) {
2061   for (const Fortran::parser::InquireSpec &spec : ispecs)
2062     if (mlir::Value v = std::visit(
2063             Fortran::common::visitors{
2064                 [&](const Fortran::parser::IdExpr &idExpr) {
2065                   return fir::getBase(converter.genExprValue(
2066                       Fortran::semantics::GetExpr(idExpr), stmtCtx, loc));
2067                 },
2068                 [](const auto &) { return mlir::Value{}; }},
2069             spec.u))
2070       return v;
2071   return {};
2072 }
2073 
2074 /// For each inquire-spec, build the appropriate call, threading the cookie.
2075 static void threadInquire(Fortran::lower::AbstractConverter &converter,
2076                           mlir::Location loc, mlir::Value cookie,
2077                           const std::list<Fortran::parser::InquireSpec> &ispecs,
2078                           bool checkResult, mlir::Value &ok,
2079                           Fortran::lower::StatementContext &stmtCtx) {
2080   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2081   mlir::Value idExpr = lowerIdExpr(converter, loc, ispecs, stmtCtx);
2082   for (const Fortran::parser::InquireSpec &spec : ispecs) {
2083     makeNextConditionalOn(builder, loc, checkResult, ok);
2084     ok = std::visit(Fortran::common::visitors{[&](const auto &x) {
2085                       return genInquireSpec(converter, loc, cookie, idExpr, x,
2086                                             stmtCtx);
2087                     }},
2088                     spec.u);
2089   }
2090 }
2091 
2092 mlir::Value Fortran::lower::genInquireStatement(
2093     Fortran::lower::AbstractConverter &converter,
2094     const Fortran::parser::InquireStmt &stmt) {
2095   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2096   Fortran::lower::StatementContext stmtCtx;
2097   mlir::Location loc = converter.getCurrentLocation();
2098   mlir::FuncOp beginFunc;
2099   ConditionSpecInfo csi;
2100   llvm::SmallVector<mlir::Value> beginArgs;
2101   const auto *list =
2102       std::get_if<std::list<Fortran::parser::InquireSpec>>(&stmt.u);
2103   auto exprPair = getInquireFileExpr(list);
2104   auto inquireFileUnit = [&]() -> bool {
2105     return exprPair.first && !exprPair.second;
2106   };
2107   auto inquireFileName = [&]() -> bool {
2108     return exprPair.first && exprPair.second;
2109   };
2110 
2111   // Make one of three BeginInquire calls.
2112   if (inquireFileUnit()) {
2113     // Inquire by unit -- [UNIT=]file-unit-number.
2114     beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireUnit)>(loc, builder);
2115     mlir::FunctionType beginFuncTy = beginFunc.getType();
2116     beginArgs = {builder.createConvert(loc, beginFuncTy.getInput(0),
2117                                        fir::getBase(converter.genExprValue(
2118                                            exprPair.first, stmtCtx, loc))),
2119                  locToFilename(converter, loc, beginFuncTy.getInput(1)),
2120                  locToLineNo(converter, loc, beginFuncTy.getInput(2))};
2121   } else if (inquireFileName()) {
2122     // Inquire by file -- FILE=file-name-expr.
2123     beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireFile)>(loc, builder);
2124     mlir::FunctionType beginFuncTy = beginFunc.getType();
2125     fir::ExtendedValue file =
2126         converter.genExprAddr(exprPair.first, stmtCtx, loc);
2127     beginArgs = {
2128         builder.createConvert(loc, beginFuncTy.getInput(0), fir::getBase(file)),
2129         builder.createConvert(loc, beginFuncTy.getInput(1), fir::getLen(file)),
2130         locToFilename(converter, loc, beginFuncTy.getInput(2)),
2131         locToLineNo(converter, loc, beginFuncTy.getInput(3))};
2132   } else {
2133     // Inquire by output list -- IOLENGTH=scalar-int-variable.
2134     const auto *ioLength =
2135         std::get_if<Fortran::parser::InquireStmt::Iolength>(&stmt.u);
2136     assert(ioLength && "must have an IOLENGTH specifier");
2137     beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireIoLength)>(loc, builder);
2138     mlir::FunctionType beginFuncTy = beginFunc.getType();
2139     beginArgs = {locToFilename(converter, loc, beginFuncTy.getInput(0)),
2140                  locToLineNo(converter, loc, beginFuncTy.getInput(1))};
2141     auto cookie =
2142         builder.create<fir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
2143     mlir::Value ok;
2144     genOutputItemList(
2145         converter, cookie,
2146         std::get<std::list<Fortran::parser::OutputItem>>(ioLength->t),
2147         /*isFormatted=*/false, /*checkResult=*/false, ok, /*inLoop=*/false,
2148         stmtCtx);
2149     auto *ioLengthVar = Fortran::semantics::GetExpr(
2150         std::get<Fortran::parser::ScalarIntVariable>(ioLength->t));
2151     mlir::Value ioLengthVarAddr =
2152         fir::getBase(converter.genExprAddr(ioLengthVar, stmtCtx, loc));
2153     llvm::SmallVector<mlir::Value> args = {cookie};
2154     mlir::Value length =
2155         builder
2156             .create<fir::CallOp>(
2157                 loc, getIORuntimeFunc<mkIOKey(GetIoLength)>(loc, builder), args)
2158             .getResult(0);
2159     mlir::Value length1 =
2160         builder.createConvert(loc, converter.genType(*ioLengthVar), length);
2161     builder.create<fir::StoreOp>(loc, length1, ioLengthVarAddr);
2162     return genEndIO(converter, loc, cookie, csi, stmtCtx);
2163   }
2164 
2165   // Common handling for inquire by unit or file.
2166   assert(list && "inquire-spec list must be present");
2167   auto cookie =
2168       builder.create<fir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
2169   genConditionHandlerCall(converter, loc, cookie, *list, csi);
2170   // Handle remaining arguments in specifier list.
2171   mlir::Value ok;
2172   auto insertPt = builder.saveInsertionPoint();
2173   threadInquire(converter, loc, cookie, *list, csi.hasErrorConditionSpec(), ok,
2174                 stmtCtx);
2175   builder.restoreInsertionPoint(insertPt);
2176   // Generate end statement call.
2177   return genEndIO(converter, loc, cookie, csi, stmtCtx);
2178 }
2179