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