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