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