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