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