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