xref: /llvm-project-15.0.7/flang/lib/Lower/IO.cpp (revision df417c37)
18c22cb84SValentin Clement //===-- IO.cpp -- IO statement lowering -----------------------------------===//
28c22cb84SValentin Clement //
38c22cb84SValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48c22cb84SValentin Clement // See https://llvm.org/LICENSE.txt for license information.
58c22cb84SValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68c22cb84SValentin Clement //
78c22cb84SValentin Clement //===----------------------------------------------------------------------===//
88c22cb84SValentin Clement //
98c22cb84SValentin Clement // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
108c22cb84SValentin Clement //
118c22cb84SValentin Clement //===----------------------------------------------------------------------===//
128c22cb84SValentin Clement 
138c22cb84SValentin Clement #include "flang/Lower/IO.h"
148c22cb84SValentin Clement #include "flang/Common/uint128.h"
159aeb7f03SValentin Clement #include "flang/Lower/Allocatable.h"
168c22cb84SValentin Clement #include "flang/Lower/Bridge.h"
179aeb7f03SValentin Clement #include "flang/Lower/ConvertExpr.h"
188c22cb84SValentin Clement #include "flang/Lower/ConvertVariable.h"
198c22cb84SValentin Clement #include "flang/Lower/PFTBuilder.h"
209aeb7f03SValentin Clement #include "flang/Lower/Runtime.h"
218c22cb84SValentin Clement #include "flang/Lower/StatementContext.h"
228c22cb84SValentin Clement #include "flang/Lower/Support/Utils.h"
239aeb7f03SValentin Clement #include "flang/Lower/VectorSubscripts.h"
248c22cb84SValentin Clement #include "flang/Optimizer/Builder/Character.h"
258c22cb84SValentin Clement #include "flang/Optimizer/Builder/Complex.h"
268c22cb84SValentin Clement #include "flang/Optimizer/Builder/FIRBuilder.h"
278c22cb84SValentin Clement #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
285b66cc10SValentin Clement #include "flang/Optimizer/Builder/Todo.h"
299aeb7f03SValentin Clement #include "flang/Optimizer/Support/FIRContext.h"
308c22cb84SValentin Clement #include "flang/Parser/parse-tree.h"
318c22cb84SValentin Clement #include "flang/Runtime/io-api.h"
328c22cb84SValentin Clement #include "flang/Semantics/tools.h"
338c22cb84SValentin Clement #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
341bffc753SEric Schweitz #include "llvm/Support/Debug.h"
358c22cb84SValentin Clement 
368c22cb84SValentin Clement #define DEBUG_TYPE "flang-lower-io"
378c22cb84SValentin Clement 
388c22cb84SValentin Clement // Define additional runtime type models specific to IO.
398c22cb84SValentin Clement namespace fir::runtime {
408c22cb84SValentin Clement template <>
getModel()418c22cb84SValentin Clement constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
428c22cb84SValentin Clement   return getModel<char *>();
438c22cb84SValentin Clement }
448c22cb84SValentin Clement template <>
458c22cb84SValentin Clement constexpr TypeBuilderFunc
getModel()468c22cb84SValentin Clement getModel<const Fortran::runtime::io::NamelistGroup &>() {
478c22cb84SValentin Clement   return [](mlir::MLIRContext *context) -> mlir::Type {
488c22cb84SValentin Clement     return fir::ReferenceType::get(mlir::TupleType::get(context));
498c22cb84SValentin Clement   };
508c22cb84SValentin Clement }
518c22cb84SValentin Clement template <>
getModel()528c22cb84SValentin Clement constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
538c22cb84SValentin Clement   return [](mlir::MLIRContext *context) -> mlir::Type {
548c22cb84SValentin Clement     return mlir::IntegerType::get(context,
558c22cb84SValentin Clement                                   8 * sizeof(Fortran::runtime::io::Iostat));
568c22cb84SValentin Clement   };
578c22cb84SValentin Clement }
588c22cb84SValentin Clement } // namespace fir::runtime
598c22cb84SValentin Clement 
608c22cb84SValentin Clement using namespace Fortran::runtime::io;
618c22cb84SValentin Clement 
628c22cb84SValentin Clement #define mkIOKey(X) FirmkKey(IONAME(X))
638c22cb84SValentin Clement 
648c22cb84SValentin Clement namespace Fortran::lower {
658c22cb84SValentin Clement /// Static table of IO runtime calls
668c22cb84SValentin Clement ///
678c22cb84SValentin Clement /// This logical map contains the name and type builder function for each IO
688c22cb84SValentin Clement /// runtime function listed in the tuple. This table is fully constructed at
698c22cb84SValentin Clement /// compile-time. Use the `mkIOKey` macro to access the table.
708c22cb84SValentin Clement static constexpr std::tuple<
718c22cb84SValentin Clement     mkIOKey(BeginInternalArrayListOutput), mkIOKey(BeginInternalArrayListInput),
728c22cb84SValentin Clement     mkIOKey(BeginInternalArrayFormattedOutput),
738c22cb84SValentin Clement     mkIOKey(BeginInternalArrayFormattedInput), mkIOKey(BeginInternalListOutput),
748c22cb84SValentin Clement     mkIOKey(BeginInternalListInput), mkIOKey(BeginInternalFormattedOutput),
758c22cb84SValentin Clement     mkIOKey(BeginInternalFormattedInput), mkIOKey(BeginExternalListOutput),
768c22cb84SValentin Clement     mkIOKey(BeginExternalListInput), mkIOKey(BeginExternalFormattedOutput),
778c22cb84SValentin Clement     mkIOKey(BeginExternalFormattedInput), mkIOKey(BeginUnformattedOutput),
78deb62f5aSPeter Klausler     mkIOKey(BeginUnformattedInput), mkIOKey(BeginWait), mkIOKey(BeginWaitAll),
798c22cb84SValentin Clement     mkIOKey(BeginClose), mkIOKey(BeginFlush), mkIOKey(BeginBackspace),
808c22cb84SValentin Clement     mkIOKey(BeginEndfile), mkIOKey(BeginRewind), mkIOKey(BeginOpenUnit),
818c22cb84SValentin Clement     mkIOKey(BeginOpenNewUnit), mkIOKey(BeginInquireUnit),
828c22cb84SValentin Clement     mkIOKey(BeginInquireFile), mkIOKey(BeginInquireIoLength),
831bffc753SEric Schweitz     mkIOKey(CheckUnitNumberInRange64), mkIOKey(CheckUnitNumberInRange128),
848c22cb84SValentin Clement     mkIOKey(EnableHandlers), mkIOKey(SetAdvance), mkIOKey(SetBlank),
858c22cb84SValentin Clement     mkIOKey(SetDecimal), mkIOKey(SetDelim), mkIOKey(SetPad), mkIOKey(SetPos),
868c22cb84SValentin Clement     mkIOKey(SetRec), mkIOKey(SetRound), mkIOKey(SetSign),
878c22cb84SValentin Clement     mkIOKey(OutputNamelist), mkIOKey(InputNamelist), mkIOKey(OutputDescriptor),
888c22cb84SValentin Clement     mkIOKey(InputDescriptor), mkIOKey(OutputUnformattedBlock),
898c22cb84SValentin Clement     mkIOKey(InputUnformattedBlock), mkIOKey(OutputInteger8),
908c22cb84SValentin Clement     mkIOKey(OutputInteger16), mkIOKey(OutputInteger32),
911bffc753SEric Schweitz     mkIOKey(OutputInteger64), mkIOKey(OutputInteger128), mkIOKey(InputInteger),
921bffc753SEric Schweitz     mkIOKey(OutputReal32), mkIOKey(InputReal32), mkIOKey(OutputReal64),
931bffc753SEric Schweitz     mkIOKey(InputReal64), mkIOKey(OutputComplex32), mkIOKey(InputComplex32),
941bffc753SEric Schweitz     mkIOKey(OutputComplex64), mkIOKey(InputComplex64), mkIOKey(OutputAscii),
951bffc753SEric Schweitz     mkIOKey(InputAscii), mkIOKey(OutputLogical), mkIOKey(InputLogical),
961bffc753SEric Schweitz     mkIOKey(SetAccess), mkIOKey(SetAction), mkIOKey(SetAsynchronous),
971bffc753SEric Schweitz     mkIOKey(SetCarriagecontrol), mkIOKey(SetEncoding), mkIOKey(SetForm),
981bffc753SEric Schweitz     mkIOKey(SetPosition), mkIOKey(SetRecl), mkIOKey(SetStatus),
991bffc753SEric Schweitz     mkIOKey(SetFile), mkIOKey(GetNewUnit), mkIOKey(GetSize),
1008c22cb84SValentin Clement     mkIOKey(GetIoLength), mkIOKey(GetIoMsg), mkIOKey(InquireCharacter),
1018c22cb84SValentin Clement     mkIOKey(InquireLogical), mkIOKey(InquirePendingId),
1028c22cb84SValentin Clement     mkIOKey(InquireInteger64), mkIOKey(EndIoStatement)>
1038c22cb84SValentin Clement     newIOTable;
1048c22cb84SValentin Clement } // namespace Fortran::lower
1058c22cb84SValentin Clement 
1068c22cb84SValentin Clement namespace {
1078c22cb84SValentin Clement /// IO statements may require exceptional condition handling.  A statement that
1088c22cb84SValentin Clement /// encounters an exceptional condition may branch to a label given on an ERR
1098c22cb84SValentin Clement /// (error), END (end-of-file), or EOR (end-of-record) specifier.  An IOSTAT
1108c22cb84SValentin Clement /// specifier variable may be set to a value that indicates some condition,
1118c22cb84SValentin Clement /// and an IOMSG specifier variable may be set to a description of a condition.
1128c22cb84SValentin Clement struct ConditionSpecInfo {
1138c22cb84SValentin Clement   const Fortran::lower::SomeExpr *ioStatExpr{};
1141bffc753SEric Schweitz   llvm::Optional<fir::ExtendedValue> ioMsg;
1158c22cb84SValentin Clement   bool hasErr{};
1168c22cb84SValentin Clement   bool hasEnd{};
1178c22cb84SValentin Clement   bool hasEor{};
1181bffc753SEric Schweitz   fir::IfOp bigUnitIfOp;
1198c22cb84SValentin Clement 
1208c22cb84SValentin Clement   /// Check for any condition specifier that applies to specifier processing.
hasErrorConditionSpec__anone72d7e3d0311::ConditionSpecInfo1218c22cb84SValentin Clement   bool hasErrorConditionSpec() const { return ioStatExpr != nullptr || hasErr; }
1228c22cb84SValentin Clement 
1238c22cb84SValentin Clement   /// Check for any condition specifier that applies to data transfer items
1248c22cb84SValentin Clement   /// in a PRINT, READ, WRITE, or WAIT statement.  (WAIT may be irrelevant.)
hasTransferConditionSpec__anone72d7e3d0311::ConditionSpecInfo1258c22cb84SValentin Clement   bool hasTransferConditionSpec() const {
1268c22cb84SValentin Clement     return hasErrorConditionSpec() || hasEnd || hasEor;
1278c22cb84SValentin Clement   }
1288c22cb84SValentin Clement 
1298c22cb84SValentin Clement   /// Check for any condition specifier, including IOMSG.
hasAnyConditionSpec__anone72d7e3d0311::ConditionSpecInfo1308c22cb84SValentin Clement   bool hasAnyConditionSpec() const {
1311bffc753SEric Schweitz     return hasTransferConditionSpec() || ioMsg;
1328c22cb84SValentin Clement   }
1338c22cb84SValentin Clement };
1348c22cb84SValentin Clement } // namespace
1358c22cb84SValentin Clement 
1368c22cb84SValentin Clement template <typename D>
1378c22cb84SValentin Clement static void genIoLoop(Fortran::lower::AbstractConverter &converter,
1388c22cb84SValentin Clement                       mlir::Value cookie, const D &ioImpliedDo,
1398c22cb84SValentin Clement                       bool isFormatted, bool checkResult, mlir::Value &ok,
1401bffc753SEric Schweitz                       bool inLoop);
1418c22cb84SValentin Clement 
1428c22cb84SValentin Clement /// Helper function to retrieve the name of the IO function given the key `A`
1438c22cb84SValentin Clement template <typename A>
getName()1448c22cb84SValentin Clement static constexpr const char *getName() {
1458c22cb84SValentin Clement   return std::get<A>(Fortran::lower::newIOTable).name;
1468c22cb84SValentin Clement }
1478c22cb84SValentin Clement 
1488c22cb84SValentin Clement /// Helper function to retrieve the type model signature builder of the IO
1498c22cb84SValentin Clement /// function as defined by the key `A`
1508c22cb84SValentin Clement template <typename A>
getTypeModel()1518c22cb84SValentin Clement static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
1528c22cb84SValentin Clement   return std::get<A>(Fortran::lower::newIOTable).getTypeModel();
1538c22cb84SValentin Clement }
1548c22cb84SValentin Clement 
getLength(mlir::Type argTy)1559aeb7f03SValentin Clement inline int64_t getLength(mlir::Type argTy) {
1569aeb7f03SValentin Clement   return argTy.cast<fir::SequenceType>().getShape()[0];
1579aeb7f03SValentin Clement }
1589aeb7f03SValentin Clement 
1598c22cb84SValentin Clement /// Get (or generate) the MLIR FuncOp for a given IO runtime function.
1608c22cb84SValentin Clement template <typename E>
getIORuntimeFunc(mlir::Location loc,fir::FirOpBuilder & builder)16158ceae95SRiver Riddle static mlir::func::FuncOp getIORuntimeFunc(mlir::Location loc,
1628c22cb84SValentin Clement                                            fir::FirOpBuilder &builder) {
1638c22cb84SValentin Clement   llvm::StringRef name = getName<E>();
1641c7889caSValentin Clement   mlir::func::FuncOp func = builder.getNamedFunction(name);
1658c22cb84SValentin Clement   if (func)
1668c22cb84SValentin Clement     return func;
1678c22cb84SValentin Clement   auto funTy = getTypeModel<E>()(builder.getContext());
1688c22cb84SValentin Clement   func = builder.createFunction(loc, name, funTy);
1698c22cb84SValentin Clement   func->setAttr("fir.runtime", builder.getUnitAttr());
1708c22cb84SValentin Clement   func->setAttr("fir.io", builder.getUnitAttr());
1718c22cb84SValentin Clement   return func;
1728c22cb84SValentin Clement }
1738c22cb84SValentin Clement 
1748c22cb84SValentin Clement /// Generate calls to end an IO statement.  Return the IOSTAT value, if any.
1758c22cb84SValentin Clement /// It is the caller's responsibility to generate branches on that value.
genEndIO(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)1768c22cb84SValentin Clement static mlir::Value genEndIO(Fortran::lower::AbstractConverter &converter,
1778c22cb84SValentin Clement                             mlir::Location loc, mlir::Value cookie,
1781bffc753SEric Schweitz                             ConditionSpecInfo &csi,
1798c22cb84SValentin Clement                             Fortran::lower::StatementContext &stmtCtx) {
1808c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1811bffc753SEric Schweitz   if (csi.ioMsg) {
1821c7889caSValentin Clement     mlir::func::FuncOp getIoMsg =
1831c7889caSValentin Clement         getIORuntimeFunc<mkIOKey(GetIoMsg)>(loc, builder);
1848c22cb84SValentin Clement     builder.create<fir::CallOp>(
1858c22cb84SValentin Clement         loc, getIoMsg,
1868c22cb84SValentin Clement         mlir::ValueRange{
1878c22cb84SValentin Clement             cookie,
1884a3460a7SRiver Riddle             builder.createConvert(loc, getIoMsg.getFunctionType().getInput(1),
1891bffc753SEric Schweitz                                   fir::getBase(*csi.ioMsg)),
1904a3460a7SRiver Riddle             builder.createConvert(loc, getIoMsg.getFunctionType().getInput(2),
1911bffc753SEric Schweitz                                   fir::getLen(*csi.ioMsg))});
1928c22cb84SValentin Clement   }
1931c7889caSValentin Clement   mlir::func::FuncOp endIoStatement =
1941c7889caSValentin Clement       getIORuntimeFunc<mkIOKey(EndIoStatement)>(loc, builder);
1958c22cb84SValentin Clement   auto call = builder.create<fir::CallOp>(loc, endIoStatement,
1968c22cb84SValentin Clement                                           mlir::ValueRange{cookie});
1971bffc753SEric Schweitz   mlir::Value iostat = call.getResult(0);
1981bffc753SEric Schweitz   if (csi.bigUnitIfOp) {
1991e55ec66SValentin Clement     stmtCtx.finalizeAndPop();
2001bffc753SEric Schweitz     builder.create<fir::ResultOp>(loc, iostat);
2011bffc753SEric Schweitz     builder.setInsertionPointAfter(csi.bigUnitIfOp);
2021bffc753SEric Schweitz     iostat = csi.bigUnitIfOp.getResult(0);
2031bffc753SEric Schweitz   }
2048c22cb84SValentin Clement   if (csi.ioStatExpr) {
2058c22cb84SValentin Clement     mlir::Value ioStatVar =
2061bffc753SEric Schweitz         fir::getBase(converter.genExprAddr(loc, csi.ioStatExpr, stmtCtx));
2071bffc753SEric Schweitz     mlir::Value ioStatResult =
2081bffc753SEric Schweitz         builder.createConvert(loc, converter.genType(*csi.ioStatExpr), iostat);
2098c22cb84SValentin Clement     builder.create<fir::StoreOp>(loc, ioStatResult, ioStatVar);
2108c22cb84SValentin Clement   }
2111bffc753SEric Schweitz   return csi.hasTransferConditionSpec() ? iostat : mlir::Value{};
2128c22cb84SValentin Clement }
2138c22cb84SValentin Clement 
2148c22cb84SValentin Clement /// Make the next call in the IO statement conditional on runtime result `ok`.
2158c22cb84SValentin Clement /// If a call returns `ok==false`, further suboperation calls for an IO
2168c22cb84SValentin Clement /// statement will be skipped.  This may generate branch heavy, deeply nested
2178c22cb84SValentin Clement /// conditionals for IO statements with a large number of suboperations.
makeNextConditionalOn(fir::FirOpBuilder & builder,mlir::Location loc,bool checkResult,mlir::Value ok,bool inLoop=false)2188c22cb84SValentin Clement static void makeNextConditionalOn(fir::FirOpBuilder &builder,
2198c22cb84SValentin Clement                                   mlir::Location loc, bool checkResult,
2208c22cb84SValentin Clement                                   mlir::Value ok, bool inLoop = false) {
2218c22cb84SValentin Clement   if (!checkResult || !ok)
2228c22cb84SValentin Clement     // Either no IO calls need to be checked, or this will be the first call.
2238c22cb84SValentin Clement     return;
2248c22cb84SValentin Clement 
2258c22cb84SValentin Clement   // A previous IO call for a statement returned the bool `ok`.  If this call
2268c22cb84SValentin Clement   // is in a fir.iterate_while loop, the result must be propagated up to the
2278c22cb84SValentin Clement   // loop scope as an extra ifOp result. (The propagation is done in genIoLoop.)
2288c22cb84SValentin Clement   mlir::TypeRange resTy;
2298c22cb84SValentin Clement   if (inLoop)
2308c22cb84SValentin Clement     resTy = builder.getI1Type();
2318c22cb84SValentin Clement   auto ifOp = builder.create<fir::IfOp>(loc, resTy, ok,
2328c22cb84SValentin Clement                                         /*withElseRegion=*/inLoop);
2338c22cb84SValentin Clement   builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
2348c22cb84SValentin Clement }
2358c22cb84SValentin Clement 
2368c22cb84SValentin Clement /// Retrieve or generate a runtime description of NAMELIST group `symbol`.
2378c22cb84SValentin Clement /// The form of the description is defined in runtime header file namelist.h.
2388c22cb84SValentin Clement /// Static descriptors are generated for global objects; local descriptors for
2398c22cb84SValentin Clement /// local objects.  If all descriptors are static, the NamelistGroup is static.
2408c22cb84SValentin Clement static mlir::Value
getNamelistGroup(Fortran::lower::AbstractConverter & converter,const Fortran::semantics::Symbol & symbol,Fortran::lower::StatementContext & stmtCtx)2418c22cb84SValentin Clement getNamelistGroup(Fortran::lower::AbstractConverter &converter,
2428c22cb84SValentin Clement                  const Fortran::semantics::Symbol &symbol,
2438c22cb84SValentin Clement                  Fortran::lower::StatementContext &stmtCtx) {
2448c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
2458c22cb84SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
2468c22cb84SValentin Clement   std::string groupMangleName = converter.mangleName(symbol);
2478c22cb84SValentin Clement   if (auto group = builder.getNamedGlobal(groupMangleName))
2488c22cb84SValentin Clement     return builder.create<fir::AddrOfOp>(loc, group.resultType(),
2498c22cb84SValentin Clement                                          group.getSymbol());
2508c22cb84SValentin Clement 
2518c22cb84SValentin Clement   const auto &details =
2528c22cb84SValentin Clement       symbol.GetUltimate().get<Fortran::semantics::NamelistDetails>();
2538c22cb84SValentin Clement   mlir::MLIRContext *context = builder.getContext();
2548c22cb84SValentin Clement   mlir::StringAttr linkOnce = builder.createLinkOnceLinkage();
2558c22cb84SValentin Clement   mlir::IndexType idxTy = builder.getIndexType();
2568c22cb84SValentin Clement   mlir::IntegerType sizeTy = builder.getIntegerType(8 * sizeof(std::size_t));
2578c22cb84SValentin Clement   fir::ReferenceType charRefTy =
2588c22cb84SValentin Clement       fir::ReferenceType::get(builder.getIntegerType(8));
2598c22cb84SValentin Clement   fir::ReferenceType descRefTy =
2608c22cb84SValentin Clement       fir::ReferenceType::get(fir::BoxType::get(mlir::NoneType::get(context)));
2618c22cb84SValentin Clement   fir::SequenceType listTy = fir::SequenceType::get(
2628c22cb84SValentin Clement       details.objects().size(),
2638c22cb84SValentin Clement       mlir::TupleType::get(context, {charRefTy, descRefTy}));
2648c22cb84SValentin Clement   mlir::TupleType groupTy = mlir::TupleType::get(
2658c22cb84SValentin Clement       context, {charRefTy, sizeTy, fir::ReferenceType::get(listTy)});
2668c22cb84SValentin Clement   auto stringAddress = [&](const Fortran::semantics::Symbol &symbol) {
2678c22cb84SValentin Clement     return fir::factory::createStringLiteral(builder, loc,
2688c22cb84SValentin Clement                                              symbol.name().ToString() + '\0');
2698c22cb84SValentin Clement   };
2708c22cb84SValentin Clement 
2718c22cb84SValentin Clement   // Define object names, and static descriptors for global objects.
2728c22cb84SValentin Clement   bool groupIsLocal = false;
2738c22cb84SValentin Clement   stringAddress(symbol);
2748c22cb84SValentin Clement   for (const Fortran::semantics::Symbol &s : details.objects()) {
2758c22cb84SValentin Clement     stringAddress(s);
2768c22cb84SValentin Clement     if (!Fortran::lower::symbolIsGlobal(s)) {
2778c22cb84SValentin Clement       groupIsLocal = true;
2788c22cb84SValentin Clement       continue;
2798c22cb84SValentin Clement     }
2809aeb7f03SValentin Clement     // We know we have a global item.  It it's not a pointer or allocatable,
2819aeb7f03SValentin Clement     // create a static pointer to it.
2829aeb7f03SValentin Clement     if (!IsAllocatableOrPointer(s)) {
2838c22cb84SValentin Clement       std::string mangleName = converter.mangleName(s) + ".desc";
2848c22cb84SValentin Clement       if (builder.getNamedGlobal(mangleName))
2858c22cb84SValentin Clement         continue;
2868c22cb84SValentin Clement       const auto expr = Fortran::evaluate::AsGenericExpr(s);
2878c22cb84SValentin Clement       fir::BoxType boxTy =
2888c22cb84SValentin Clement           fir::BoxType::get(fir::PointerType::get(converter.genType(s)));
2898c22cb84SValentin Clement       auto descFunc = [&](fir::FirOpBuilder &b) {
2908c22cb84SValentin Clement         auto box =
2918c22cb84SValentin Clement             Fortran::lower::genInitialDataTarget(converter, loc, boxTy, *expr);
2928c22cb84SValentin Clement         b.create<fir::HasValueOp>(loc, box);
2938c22cb84SValentin Clement       };
2948c22cb84SValentin Clement       builder.createGlobalConstant(loc, boxTy, mangleName, descFunc, linkOnce);
2958c22cb84SValentin Clement     }
2969aeb7f03SValentin Clement   }
2978c22cb84SValentin Clement 
2988c22cb84SValentin Clement   // Define the list of Items.
2998c22cb84SValentin Clement   mlir::Value listAddr =
3008c22cb84SValentin Clement       groupIsLocal ? builder.create<fir::AllocaOp>(loc, listTy) : mlir::Value{};
3018c22cb84SValentin Clement   std::string listMangleName = groupMangleName + ".list";
3028c22cb84SValentin Clement   auto listFunc = [&](fir::FirOpBuilder &builder) {
3038c22cb84SValentin Clement     mlir::Value list = builder.create<fir::UndefOp>(loc, listTy);
3048c22cb84SValentin Clement     mlir::IntegerAttr zero = builder.getIntegerAttr(idxTy, 0);
3058c22cb84SValentin Clement     mlir::IntegerAttr one = builder.getIntegerAttr(idxTy, 1);
3068c22cb84SValentin Clement     llvm::SmallVector<mlir::Attribute, 2> idx = {mlir::Attribute{},
3078c22cb84SValentin Clement                                                  mlir::Attribute{}};
3088c22cb84SValentin Clement     size_t n = 0;
3098c22cb84SValentin Clement     for (const Fortran::semantics::Symbol &s : details.objects()) {
3108c22cb84SValentin Clement       idx[0] = builder.getIntegerAttr(idxTy, n);
3118c22cb84SValentin Clement       idx[1] = zero;
3128c22cb84SValentin Clement       mlir::Value nameAddr =
3138c22cb84SValentin Clement           builder.createConvert(loc, charRefTy, fir::getBase(stringAddress(s)));
3148c22cb84SValentin Clement       list = builder.create<fir::InsertValueOp>(loc, listTy, list, nameAddr,
3158c22cb84SValentin Clement                                                 builder.getArrayAttr(idx));
3168c22cb84SValentin Clement       idx[1] = one;
3178c22cb84SValentin Clement       mlir::Value descAddr;
3189aeb7f03SValentin Clement       // Items that we created end in ".desc".
3199aeb7f03SValentin Clement       std::string suffix = IsAllocatableOrPointer(s) ? "" : ".desc";
3208c22cb84SValentin Clement       if (auto desc =
3219aeb7f03SValentin Clement               builder.getNamedGlobal(converter.mangleName(s) + suffix)) {
3228c22cb84SValentin Clement         descAddr = builder.create<fir::AddrOfOp>(loc, desc.resultType(),
3238c22cb84SValentin Clement                                                  desc.getSymbol());
3248c22cb84SValentin Clement       } else {
3258c22cb84SValentin Clement         const auto expr = Fortran::evaluate::AsGenericExpr(s);
3268c22cb84SValentin Clement         fir::ExtendedValue exv = converter.genExprAddr(*expr, stmtCtx);
3278c22cb84SValentin Clement         mlir::Type type = fir::getBase(exv).getType();
3288c22cb84SValentin Clement         if (mlir::Type baseTy = fir::dyn_cast_ptrOrBoxEleTy(type))
3298c22cb84SValentin Clement           type = baseTy;
3308c22cb84SValentin Clement         fir::BoxType boxType = fir::BoxType::get(fir::PointerType::get(type));
3318c22cb84SValentin Clement         descAddr = builder.createTemporary(loc, boxType);
3328c22cb84SValentin Clement         fir::MutableBoxValue box = fir::MutableBoxValue(descAddr, {}, {});
3338c22cb84SValentin Clement         fir::factory::associateMutableBox(builder, loc, box, exv,
3348c22cb84SValentin Clement                                           /*lbounds=*/llvm::None);
3358c22cb84SValentin Clement       }
3368c22cb84SValentin Clement       descAddr = builder.createConvert(loc, descRefTy, descAddr);
3378c22cb84SValentin Clement       list = builder.create<fir::InsertValueOp>(loc, listTy, list, descAddr,
3388c22cb84SValentin Clement                                                 builder.getArrayAttr(idx));
3398c22cb84SValentin Clement       ++n;
3408c22cb84SValentin Clement     }
3418c22cb84SValentin Clement     if (groupIsLocal)
3428c22cb84SValentin Clement       builder.create<fir::StoreOp>(loc, list, listAddr);
3438c22cb84SValentin Clement     else
3448c22cb84SValentin Clement       builder.create<fir::HasValueOp>(loc, list);
3458c22cb84SValentin Clement   };
3468c22cb84SValentin Clement   if (groupIsLocal)
3478c22cb84SValentin Clement     listFunc(builder);
3488c22cb84SValentin Clement   else
3498c22cb84SValentin Clement     builder.createGlobalConstant(loc, listTy, listMangleName, listFunc,
3508c22cb84SValentin Clement                                  linkOnce);
3518c22cb84SValentin Clement 
3528c22cb84SValentin Clement   // Define the group.
3538c22cb84SValentin Clement   mlir::Value groupAddr = groupIsLocal
3548c22cb84SValentin Clement                               ? builder.create<fir::AllocaOp>(loc, groupTy)
3558c22cb84SValentin Clement                               : mlir::Value{};
3568c22cb84SValentin Clement   auto groupFunc = [&](fir::FirOpBuilder &builder) {
3578c22cb84SValentin Clement     mlir::IntegerAttr zero = builder.getIntegerAttr(idxTy, 0);
3588c22cb84SValentin Clement     mlir::IntegerAttr one = builder.getIntegerAttr(idxTy, 1);
3598c22cb84SValentin Clement     mlir::IntegerAttr two = builder.getIntegerAttr(idxTy, 2);
3608c22cb84SValentin Clement     mlir::Value group = builder.create<fir::UndefOp>(loc, groupTy);
3618c22cb84SValentin Clement     mlir::Value nameAddr = builder.createConvert(
3628c22cb84SValentin Clement         loc, charRefTy, fir::getBase(stringAddress(symbol)));
3638c22cb84SValentin Clement     group = builder.create<fir::InsertValueOp>(loc, groupTy, group, nameAddr,
3648c22cb84SValentin Clement                                                builder.getArrayAttr(zero));
3658c22cb84SValentin Clement     mlir::Value itemCount =
3668c22cb84SValentin Clement         builder.createIntegerConstant(loc, sizeTy, details.objects().size());
3678c22cb84SValentin Clement     group = builder.create<fir::InsertValueOp>(loc, groupTy, group, itemCount,
3688c22cb84SValentin Clement                                                builder.getArrayAttr(one));
3698c22cb84SValentin Clement     if (fir::GlobalOp list = builder.getNamedGlobal(listMangleName))
3708c22cb84SValentin Clement       listAddr = builder.create<fir::AddrOfOp>(loc, list.resultType(),
3718c22cb84SValentin Clement                                                list.getSymbol());
3728c22cb84SValentin Clement     assert(listAddr && "missing namelist object list");
3738c22cb84SValentin Clement     group = builder.create<fir::InsertValueOp>(loc, groupTy, group, listAddr,
3748c22cb84SValentin Clement                                                builder.getArrayAttr(two));
3758c22cb84SValentin Clement     if (groupIsLocal)
3768c22cb84SValentin Clement       builder.create<fir::StoreOp>(loc, group, groupAddr);
3778c22cb84SValentin Clement     else
3788c22cb84SValentin Clement       builder.create<fir::HasValueOp>(loc, group);
3798c22cb84SValentin Clement   };
3808c22cb84SValentin Clement   if (groupIsLocal) {
3818c22cb84SValentin Clement     groupFunc(builder);
3828c22cb84SValentin Clement   } else {
3838c22cb84SValentin Clement     fir::GlobalOp group =
3848c22cb84SValentin Clement         builder.createGlobal(loc, groupTy, groupMangleName,
3858c22cb84SValentin Clement                              /*isConst=*/true, groupFunc, linkOnce);
3868c22cb84SValentin Clement     groupAddr = builder.create<fir::AddrOfOp>(loc, group.resultType(),
3878c22cb84SValentin Clement                                               group.getSymbol());
3888c22cb84SValentin Clement   }
3898c22cb84SValentin Clement   assert(groupAddr && "missing namelist group result");
3908c22cb84SValentin Clement   return groupAddr;
3918c22cb84SValentin Clement }
3928c22cb84SValentin Clement 
3938c22cb84SValentin Clement /// Generate a namelist IO call.
genNamelistIO(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,mlir::func::FuncOp funcOp,Fortran::semantics::Symbol & symbol,bool checkResult,mlir::Value & ok,Fortran::lower::StatementContext & stmtCtx)3948c22cb84SValentin Clement static void genNamelistIO(Fortran::lower::AbstractConverter &converter,
39558ceae95SRiver Riddle                           mlir::Value cookie, mlir::func::FuncOp funcOp,
3968c22cb84SValentin Clement                           Fortran::semantics::Symbol &symbol, bool checkResult,
3978c22cb84SValentin Clement                           mlir::Value &ok,
3988c22cb84SValentin Clement                           Fortran::lower::StatementContext &stmtCtx) {
3998c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
4008c22cb84SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
4018c22cb84SValentin Clement   makeNextConditionalOn(builder, loc, checkResult, ok);
4024a3460a7SRiver Riddle   mlir::Type argType = funcOp.getFunctionType().getInput(1);
4038c22cb84SValentin Clement   mlir::Value groupAddr = getNamelistGroup(converter, symbol, stmtCtx);
4048c22cb84SValentin Clement   groupAddr = builder.createConvert(loc, argType, groupAddr);
4058c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> args = {cookie, groupAddr};
4068c22cb84SValentin Clement   ok = builder.create<fir::CallOp>(loc, funcOp, args).getResult(0);
4078c22cb84SValentin Clement }
4088c22cb84SValentin Clement 
4098c22cb84SValentin Clement /// Get the output function to call for a value of the given type.
getOutputFunc(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Type type,bool isFormatted)41058ceae95SRiver Riddle static mlir::func::FuncOp getOutputFunc(mlir::Location loc,
41158ceae95SRiver Riddle                                         fir::FirOpBuilder &builder,
41258ceae95SRiver Riddle                                         mlir::Type type, bool isFormatted) {
4138c22cb84SValentin Clement   if (!isFormatted)
4148c22cb84SValentin Clement     return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
4158c22cb84SValentin Clement   if (auto ty = type.dyn_cast<mlir::IntegerType>()) {
4168c22cb84SValentin Clement     switch (ty.getWidth()) {
4178c22cb84SValentin Clement     case 1:
4188c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
4198c22cb84SValentin Clement     case 8:
4208c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputInteger8)>(loc, builder);
4218c22cb84SValentin Clement     case 16:
4228c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputInteger16)>(loc, builder);
4238c22cb84SValentin Clement     case 32:
4248c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputInteger32)>(loc, builder);
4258c22cb84SValentin Clement     case 64:
4268c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputInteger64)>(loc, builder);
4278c22cb84SValentin Clement     case 128:
4288c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputInteger128)>(loc, builder);
4298c22cb84SValentin Clement     }
4308c22cb84SValentin Clement     llvm_unreachable("unknown OutputInteger kind");
4318c22cb84SValentin Clement   }
4328c22cb84SValentin Clement   if (auto ty = type.dyn_cast<mlir::FloatType>()) {
4338c22cb84SValentin Clement     if (auto width = ty.getWidth(); width == 32)
4348c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputReal32)>(loc, builder);
4358c22cb84SValentin Clement     else if (width == 64)
4368c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputReal64)>(loc, builder);
4378c22cb84SValentin Clement   }
4389aeb7f03SValentin Clement   auto kindMap = fir::getKindMapping(builder.getModule());
4398c22cb84SValentin Clement   if (auto ty = type.dyn_cast<fir::ComplexType>()) {
4409aeb7f03SValentin Clement     // COMPLEX(KIND=k) corresponds to a pair of REAL(KIND=k).
4419aeb7f03SValentin Clement     auto width = kindMap.getRealBitsize(ty.getFKind());
4429aeb7f03SValentin Clement     if (width == 32)
4438c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputComplex32)>(loc, builder);
4449aeb7f03SValentin Clement     else if (width == 64)
4458c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputComplex64)>(loc, builder);
4468c22cb84SValentin Clement   }
4478c22cb84SValentin Clement   if (type.isa<fir::LogicalType>())
4488c22cb84SValentin Clement     return getIORuntimeFunc<mkIOKey(OutputLogical)>(loc, builder);
4499aeb7f03SValentin Clement   if (fir::factory::CharacterExprHelper::isCharacterScalar(type)) {
4509aeb7f03SValentin Clement     // TODO: What would it mean if the default CHARACTER KIND is set to a wide
4519aeb7f03SValentin Clement     // character encoding scheme? How do we handle UTF-8? Is it a distinct KIND
4529aeb7f03SValentin Clement     // value? For now, assume that if the default CHARACTER KIND is 8 bit,
4539aeb7f03SValentin Clement     // then it is an ASCII string and UTF-8 is unsupported.
4549aeb7f03SValentin Clement     auto asciiKind = kindMap.defaultCharacterKind();
4559aeb7f03SValentin Clement     if (kindMap.getCharacterBitsize(asciiKind) == 8 &&
4569aeb7f03SValentin Clement         fir::factory::CharacterExprHelper::getCharacterKind(type) == asciiKind)
4578c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(OutputAscii)>(loc, builder);
4589aeb7f03SValentin Clement   }
4598c22cb84SValentin Clement   return getIORuntimeFunc<mkIOKey(OutputDescriptor)>(loc, builder);
4608c22cb84SValentin Clement }
4618c22cb84SValentin Clement 
4628c22cb84SValentin Clement /// Generate a sequence of output data transfer calls.
genOutputItemList(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,const std::list<Fortran::parser::OutputItem> & items,bool isFormatted,bool checkResult,mlir::Value & ok,bool inLoop)4631bffc753SEric Schweitz static void genOutputItemList(
4641bffc753SEric Schweitz     Fortran::lower::AbstractConverter &converter, mlir::Value cookie,
4651bffc753SEric Schweitz     const std::list<Fortran::parser::OutputItem> &items, bool isFormatted,
4661bffc753SEric Schweitz     bool checkResult, mlir::Value &ok, bool inLoop) {
4678c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
4688c22cb84SValentin Clement   for (const Fortran::parser::OutputItem &item : items) {
4698c22cb84SValentin Clement     if (const auto &impliedDo = std::get_if<1>(&item.u)) {
4708c22cb84SValentin Clement       genIoLoop(converter, cookie, impliedDo->value(), isFormatted, checkResult,
4711bffc753SEric Schweitz                 ok, inLoop);
4728c22cb84SValentin Clement       continue;
4738c22cb84SValentin Clement     }
4748c22cb84SValentin Clement     auto &pExpr = std::get<Fortran::parser::Expr>(item.u);
4758c22cb84SValentin Clement     mlir::Location loc = converter.genLocation(pExpr.source);
4768c22cb84SValentin Clement     makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
4771bffc753SEric Schweitz     Fortran::lower::StatementContext stmtCtx;
4788c22cb84SValentin Clement 
4798c22cb84SValentin Clement     const auto *expr = Fortran::semantics::GetExpr(pExpr);
4808c22cb84SValentin Clement     if (!expr)
4818c22cb84SValentin Clement       fir::emitFatalError(loc, "internal error: could not get evaluate::Expr");
4828c22cb84SValentin Clement     mlir::Type itemTy = converter.genType(*expr);
4831c7889caSValentin Clement     mlir::func::FuncOp outputFunc =
4841c7889caSValentin Clement         getOutputFunc(loc, builder, itemTy, isFormatted);
4854a3460a7SRiver Riddle     mlir::Type argType = outputFunc.getFunctionType().getInput(1);
4868c22cb84SValentin Clement     assert((isFormatted || argType.isa<fir::BoxType>()) &&
4878c22cb84SValentin Clement            "expect descriptor for unformatted IO runtime");
4888c22cb84SValentin Clement     llvm::SmallVector<mlir::Value> outputFuncArgs = {cookie};
4898c22cb84SValentin Clement     fir::factory::CharacterExprHelper helper{builder, loc};
4908c22cb84SValentin Clement     if (argType.isa<fir::BoxType>()) {
4911bffc753SEric Schweitz       mlir::Value box = fir::getBase(converter.genExprBox(loc, *expr, stmtCtx));
4928c22cb84SValentin Clement       outputFuncArgs.push_back(builder.createConvert(loc, argType, box));
4938c22cb84SValentin Clement     } else if (helper.isCharacterScalar(itemTy)) {
4941bffc753SEric Schweitz       fir::ExtendedValue exv = converter.genExprAddr(loc, expr, stmtCtx);
4958c22cb84SValentin Clement       // scalar allocatable/pointer may also get here, not clear if
4968c22cb84SValentin Clement       // genExprAddr will lower them as CharBoxValue or BoxValue.
4978c22cb84SValentin Clement       if (!exv.getCharBox())
4988c22cb84SValentin Clement         llvm::report_fatal_error(
4998c22cb84SValentin Clement             "internal error: scalar character not in CharBox");
5008c22cb84SValentin Clement       outputFuncArgs.push_back(builder.createConvert(
5014a3460a7SRiver Riddle           loc, outputFunc.getFunctionType().getInput(1), fir::getBase(exv)));
5028c22cb84SValentin Clement       outputFuncArgs.push_back(builder.createConvert(
5034a3460a7SRiver Riddle           loc, outputFunc.getFunctionType().getInput(2), fir::getLen(exv)));
5048c22cb84SValentin Clement     } else {
5051bffc753SEric Schweitz       fir::ExtendedValue itemBox = converter.genExprValue(loc, expr, stmtCtx);
5068c22cb84SValentin Clement       mlir::Value itemValue = fir::getBase(itemBox);
5078c22cb84SValentin Clement       if (fir::isa_complex(itemTy)) {
5088c22cb84SValentin Clement         auto parts =
5098c22cb84SValentin Clement             fir::factory::Complex{builder, loc}.extractParts(itemValue);
5108c22cb84SValentin Clement         outputFuncArgs.push_back(parts.first);
5118c22cb84SValentin Clement         outputFuncArgs.push_back(parts.second);
5128c22cb84SValentin Clement       } else {
5138c22cb84SValentin Clement         itemValue = builder.createConvert(loc, argType, itemValue);
5148c22cb84SValentin Clement         outputFuncArgs.push_back(itemValue);
5158c22cb84SValentin Clement       }
5168c22cb84SValentin Clement     }
5178c22cb84SValentin Clement     ok = builder.create<fir::CallOp>(loc, outputFunc, outputFuncArgs)
5188c22cb84SValentin Clement              .getResult(0);
5198c22cb84SValentin Clement   }
5208c22cb84SValentin Clement }
5218c22cb84SValentin Clement 
5228c22cb84SValentin Clement /// Get the input function to call for a value of the given type.
getInputFunc(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Type type,bool isFormatted)52358ceae95SRiver Riddle static mlir::func::FuncOp getInputFunc(mlir::Location loc,
52458ceae95SRiver Riddle                                        fir::FirOpBuilder &builder,
5258c22cb84SValentin Clement                                        mlir::Type type, bool isFormatted) {
5268c22cb84SValentin Clement   if (!isFormatted)
5278c22cb84SValentin Clement     return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
5288c22cb84SValentin Clement   if (auto ty = type.dyn_cast<mlir::IntegerType>())
5298c22cb84SValentin Clement     return ty.getWidth() == 1
5308c22cb84SValentin Clement                ? getIORuntimeFunc<mkIOKey(InputLogical)>(loc, builder)
5318c22cb84SValentin Clement                : getIORuntimeFunc<mkIOKey(InputInteger)>(loc, builder);
5328c22cb84SValentin Clement   if (auto ty = type.dyn_cast<mlir::FloatType>()) {
533575eb213SValentin Clement     if (auto width = ty.getWidth(); width == 32)
5348c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(InputReal32)>(loc, builder);
535575eb213SValentin Clement     else if (width == 64)
5368c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(InputReal64)>(loc, builder);
5378c22cb84SValentin Clement   }
5389aeb7f03SValentin Clement   auto kindMap = fir::getKindMapping(builder.getModule());
5398c22cb84SValentin Clement   if (auto ty = type.dyn_cast<fir::ComplexType>()) {
5409aeb7f03SValentin Clement     auto width = kindMap.getRealBitsize(ty.getFKind());
541575eb213SValentin Clement     if (width == 32)
5428c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(InputComplex32)>(loc, builder);
543575eb213SValentin Clement     else if (width == 64)
5448c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(InputComplex64)>(loc, builder);
5458c22cb84SValentin Clement   }
5468c22cb84SValentin Clement   if (type.isa<fir::LogicalType>())
5478c22cb84SValentin Clement     return getIORuntimeFunc<mkIOKey(InputLogical)>(loc, builder);
5489aeb7f03SValentin Clement   if (fir::factory::CharacterExprHelper::isCharacterScalar(type)) {
5499aeb7f03SValentin Clement     auto asciiKind = kindMap.defaultCharacterKind();
5509aeb7f03SValentin Clement     if (kindMap.getCharacterBitsize(asciiKind) == 8 &&
5519aeb7f03SValentin Clement         fir::factory::CharacterExprHelper::getCharacterKind(type) == asciiKind)
5528c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(InputAscii)>(loc, builder);
5539aeb7f03SValentin Clement   }
5548c22cb84SValentin Clement   return getIORuntimeFunc<mkIOKey(InputDescriptor)>(loc, builder);
5558c22cb84SValentin Clement }
5568c22cb84SValentin Clement 
5579aeb7f03SValentin Clement /// Interpret the lowest byte of a LOGICAL and store that value into the full
5589aeb7f03SValentin Clement /// storage of the LOGICAL. The load, convert, and store effectively (sign or
5599aeb7f03SValentin Clement /// zero) extends the lowest byte into the full LOGICAL value storage, as the
5609aeb7f03SValentin Clement /// runtime is unaware of the LOGICAL value's actual bit width (it was passed
5619aeb7f03SValentin Clement /// as a `bool&` to the runtime in order to be set).
boolRefToLogical(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Value addr)5629aeb7f03SValentin Clement static void boolRefToLogical(mlir::Location loc, fir::FirOpBuilder &builder,
5639aeb7f03SValentin Clement                              mlir::Value addr) {
5649aeb7f03SValentin Clement   auto boolType = builder.getRefType(builder.getI1Type());
5659aeb7f03SValentin Clement   auto boolAddr = builder.createConvert(loc, boolType, addr);
5669aeb7f03SValentin Clement   auto boolValue = builder.create<fir::LoadOp>(loc, boolAddr);
5679aeb7f03SValentin Clement   auto logicalType = fir::unwrapPassByRefType(addr.getType());
5689aeb7f03SValentin Clement   // The convert avoid making any assumptions about how LOGICALs are actually
5699aeb7f03SValentin Clement   // represented (it might end-up being either a signed or zero extension).
5709aeb7f03SValentin Clement   auto logicalValue = builder.createConvert(loc, logicalType, boolValue);
5719aeb7f03SValentin Clement   builder.create<fir::StoreOp>(loc, logicalValue, addr);
5729aeb7f03SValentin Clement }
5739aeb7f03SValentin Clement 
createIoRuntimeCallForItem(mlir::Location loc,fir::FirOpBuilder & builder,mlir::func::FuncOp inputFunc,mlir::Value cookie,const fir::ExtendedValue & item)5748c22cb84SValentin Clement static mlir::Value createIoRuntimeCallForItem(mlir::Location loc,
5758c22cb84SValentin Clement                                               fir::FirOpBuilder &builder,
57658ceae95SRiver Riddle                                               mlir::func::FuncOp inputFunc,
5778c22cb84SValentin Clement                                               mlir::Value cookie,
5788c22cb84SValentin Clement                                               const fir::ExtendedValue &item) {
5794a3460a7SRiver Riddle   mlir::Type argType = inputFunc.getFunctionType().getInput(1);
5808c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> inputFuncArgs = {cookie};
5818c22cb84SValentin Clement   if (argType.isa<fir::BoxType>()) {
5828c22cb84SValentin Clement     mlir::Value box = fir::getBase(item);
5838c22cb84SValentin Clement     assert(box.getType().isa<fir::BoxType>() && "must be previously emboxed");
5848c22cb84SValentin Clement     inputFuncArgs.push_back(builder.createConvert(loc, argType, box));
5858c22cb84SValentin Clement   } else {
5868c22cb84SValentin Clement     mlir::Value itemAddr = fir::getBase(item);
5878c22cb84SValentin Clement     mlir::Type itemTy = fir::unwrapPassByRefType(itemAddr.getType());
5888c22cb84SValentin Clement     inputFuncArgs.push_back(builder.createConvert(loc, argType, itemAddr));
5898c22cb84SValentin Clement     fir::factory::CharacterExprHelper charHelper{builder, loc};
5908c22cb84SValentin Clement     if (charHelper.isCharacterScalar(itemTy)) {
5918c22cb84SValentin Clement       mlir::Value len = fir::getLen(item);
5924a3460a7SRiver Riddle       inputFuncArgs.push_back(builder.createConvert(
5934a3460a7SRiver Riddle           loc, inputFunc.getFunctionType().getInput(2), len));
5948c22cb84SValentin Clement     } else if (itemTy.isa<mlir::IntegerType>()) {
5958c22cb84SValentin Clement       inputFuncArgs.push_back(builder.create<mlir::arith::ConstantOp>(
5968c22cb84SValentin Clement           loc, builder.getI32IntegerAttr(
5978c22cb84SValentin Clement                    itemTy.cast<mlir::IntegerType>().getWidth() / 8)));
5988c22cb84SValentin Clement     }
5998c22cb84SValentin Clement   }
6009aeb7f03SValentin Clement   auto call = builder.create<fir::CallOp>(loc, inputFunc, inputFuncArgs);
6019aeb7f03SValentin Clement   auto itemAddr = fir::getBase(item);
6029aeb7f03SValentin Clement   auto itemTy = fir::unwrapRefType(itemAddr.getType());
6039aeb7f03SValentin Clement   if (itemTy.isa<fir::LogicalType>())
6049aeb7f03SValentin Clement     boolRefToLogical(loc, builder, itemAddr);
6059aeb7f03SValentin Clement   return call.getResult(0);
6068c22cb84SValentin Clement }
6078c22cb84SValentin Clement 
6088c22cb84SValentin Clement /// Generate a sequence of input data transfer calls.
genInputItemList(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,const std::list<Fortran::parser::InputItem> & items,bool isFormatted,bool checkResult,mlir::Value & ok,bool inLoop)6098c22cb84SValentin Clement static void genInputItemList(Fortran::lower::AbstractConverter &converter,
6108c22cb84SValentin Clement                              mlir::Value cookie,
6118c22cb84SValentin Clement                              const std::list<Fortran::parser::InputItem> &items,
6128c22cb84SValentin Clement                              bool isFormatted, bool checkResult,
6131bffc753SEric Schweitz                              mlir::Value &ok, bool inLoop) {
6148c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
6158c22cb84SValentin Clement   for (const Fortran::parser::InputItem &item : items) {
6168c22cb84SValentin Clement     if (const auto &impliedDo = std::get_if<1>(&item.u)) {
6178c22cb84SValentin Clement       genIoLoop(converter, cookie, impliedDo->value(), isFormatted, checkResult,
6181bffc753SEric Schweitz                 ok, inLoop);
6198c22cb84SValentin Clement       continue;
6208c22cb84SValentin Clement     }
6218c22cb84SValentin Clement     auto &pVar = std::get<Fortran::parser::Variable>(item.u);
6228c22cb84SValentin Clement     mlir::Location loc = converter.genLocation(pVar.GetSource());
6238c22cb84SValentin Clement     makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
6241bffc753SEric Schweitz     Fortran::lower::StatementContext stmtCtx;
6258c22cb84SValentin Clement     const auto *expr = Fortran::semantics::GetExpr(pVar);
6268c22cb84SValentin Clement     if (!expr)
6278c22cb84SValentin Clement       fir::emitFatalError(loc, "internal error: could not get evaluate::Expr");
6288c22cb84SValentin Clement     if (Fortran::evaluate::HasVectorSubscript(*expr)) {
6299aeb7f03SValentin Clement       auto vectorSubscriptBox =
6309aeb7f03SValentin Clement           Fortran::lower::genVectorSubscriptBox(loc, converter, stmtCtx, *expr);
6311c7889caSValentin Clement       mlir::func::FuncOp inputFunc = getInputFunc(
6329aeb7f03SValentin Clement           loc, builder, vectorSubscriptBox.getElementType(), isFormatted);
6334a3460a7SRiver Riddle       const bool mustBox =
6344a3460a7SRiver Riddle           inputFunc.getFunctionType().getInput(1).isa<fir::BoxType>();
6359aeb7f03SValentin Clement       if (!checkResult) {
6369aeb7f03SValentin Clement         auto elementalGenerator = [&](const fir::ExtendedValue &element) {
6379aeb7f03SValentin Clement           createIoRuntimeCallForItem(loc, builder, inputFunc, cookie,
6389aeb7f03SValentin Clement                                      mustBox ? builder.createBox(loc, element)
6399aeb7f03SValentin Clement                                              : element);
6409aeb7f03SValentin Clement         };
6419aeb7f03SValentin Clement         vectorSubscriptBox.loopOverElements(builder, loc, elementalGenerator);
6429aeb7f03SValentin Clement       } else {
6439aeb7f03SValentin Clement         auto elementalGenerator =
6449aeb7f03SValentin Clement             [&](const fir::ExtendedValue &element) -> mlir::Value {
6459aeb7f03SValentin Clement           return createIoRuntimeCallForItem(
6469aeb7f03SValentin Clement               loc, builder, inputFunc, cookie,
6479aeb7f03SValentin Clement               mustBox ? builder.createBox(loc, element) : element);
6489aeb7f03SValentin Clement         };
6499aeb7f03SValentin Clement         if (!ok)
6509aeb7f03SValentin Clement           ok = builder.createBool(loc, true);
6519aeb7f03SValentin Clement         ok = vectorSubscriptBox.loopOverElementsWhile(builder, loc,
6529aeb7f03SValentin Clement                                                       elementalGenerator, ok);
6539aeb7f03SValentin Clement       }
6549aeb7f03SValentin Clement       continue;
6558c22cb84SValentin Clement     }
6568c22cb84SValentin Clement     mlir::Type itemTy = converter.genType(*expr);
6571c7889caSValentin Clement     mlir::func::FuncOp inputFunc =
6581c7889caSValentin Clement         getInputFunc(loc, builder, itemTy, isFormatted);
6594a3460a7SRiver Riddle     auto itemExv = inputFunc.getFunctionType().getInput(1).isa<fir::BoxType>()
6601bffc753SEric Schweitz                        ? converter.genExprBox(loc, *expr, stmtCtx)
6611bffc753SEric Schweitz                        : converter.genExprAddr(loc, expr, stmtCtx);
6628c22cb84SValentin Clement     ok = createIoRuntimeCallForItem(loc, builder, inputFunc, cookie, itemExv);
6638c22cb84SValentin Clement   }
6648c22cb84SValentin Clement }
6658c22cb84SValentin Clement 
6668c22cb84SValentin Clement /// Generate an io-implied-do loop.
6678c22cb84SValentin Clement template <typename D>
genIoLoop(Fortran::lower::AbstractConverter & converter,mlir::Value cookie,const D & ioImpliedDo,bool isFormatted,bool checkResult,mlir::Value & ok,bool inLoop)6688c22cb84SValentin Clement static void genIoLoop(Fortran::lower::AbstractConverter &converter,
6698c22cb84SValentin Clement                       mlir::Value cookie, const D &ioImpliedDo,
6708c22cb84SValentin Clement                       bool isFormatted, bool checkResult, mlir::Value &ok,
6711bffc753SEric Schweitz                       bool inLoop) {
6721bffc753SEric Schweitz   Fortran::lower::StatementContext stmtCtx;
6738c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
6748c22cb84SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
6758c22cb84SValentin Clement   makeNextConditionalOn(builder, loc, checkResult, ok, inLoop);
6768c22cb84SValentin Clement   const auto &itemList = std::get<0>(ioImpliedDo.t);
6778c22cb84SValentin Clement   const auto &control = std::get<1>(ioImpliedDo.t);
6788c22cb84SValentin Clement   const auto &loopSym = *control.name.thing.thing.symbol;
6791bffc753SEric Schweitz   mlir::Value loopVar = fir::getBase(converter.genExprAddr(
6801bffc753SEric Schweitz       Fortran::evaluate::AsGenericExpr(loopSym).value(), stmtCtx));
6818c22cb84SValentin Clement   auto genControlValue = [&](const Fortran::parser::ScalarIntExpr &expr) {
6828c22cb84SValentin Clement     mlir::Value v = fir::getBase(
6838c22cb84SValentin Clement         converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
6848c22cb84SValentin Clement     return builder.createConvert(loc, builder.getIndexType(), v);
6858c22cb84SValentin Clement   };
6868c22cb84SValentin Clement   mlir::Value lowerValue = genControlValue(control.lower);
6878c22cb84SValentin Clement   mlir::Value upperValue = genControlValue(control.upper);
6888c22cb84SValentin Clement   mlir::Value stepValue =
6898c22cb84SValentin Clement       control.step.has_value()
6908c22cb84SValentin Clement           ? genControlValue(*control.step)
6918c22cb84SValentin Clement           : builder.create<mlir::arith::ConstantIndexOp>(loc, 1);
6928c22cb84SValentin Clement   auto genItemList = [&](const D &ioImpliedDo) {
6938c22cb84SValentin Clement     if constexpr (std::is_same_v<D, Fortran::parser::InputImpliedDo>)
6948c22cb84SValentin Clement       genInputItemList(converter, cookie, itemList, isFormatted, checkResult,
6951bffc753SEric Schweitz                        ok, /*inLoop=*/true);
6968c22cb84SValentin Clement     else
6978c22cb84SValentin Clement       genOutputItemList(converter, cookie, itemList, isFormatted, checkResult,
6981bffc753SEric Schweitz                         ok, /*inLoop=*/true);
6998c22cb84SValentin Clement   };
7008c22cb84SValentin Clement   if (!checkResult) {
7018c22cb84SValentin Clement     // No IO call result checks - the loop is a fir.do_loop op.
7028c22cb84SValentin Clement     auto doLoopOp = builder.create<fir::DoLoopOp>(
7038c22cb84SValentin Clement         loc, lowerValue, upperValue, stepValue, /*unordered=*/false,
7048c22cb84SValentin Clement         /*finalCountValue=*/true);
7058c22cb84SValentin Clement     builder.setInsertionPointToStart(doLoopOp.getBody());
7061bffc753SEric Schweitz     mlir::Value lcv = builder.createConvert(
7071bffc753SEric Schweitz         loc, fir::unwrapRefType(loopVar.getType()), doLoopOp.getInductionVar());
7088c22cb84SValentin Clement     builder.create<fir::StoreOp>(loc, lcv, loopVar);
7098c22cb84SValentin Clement     genItemList(ioImpliedDo);
7108c22cb84SValentin Clement     builder.setInsertionPointToEnd(doLoopOp.getBody());
7118c22cb84SValentin Clement     mlir::Value result = builder.create<mlir::arith::AddIOp>(
7128c22cb84SValentin Clement         loc, doLoopOp.getInductionVar(), doLoopOp.getStep());
7138c22cb84SValentin Clement     builder.create<fir::ResultOp>(loc, result);
7148c22cb84SValentin Clement     builder.setInsertionPointAfter(doLoopOp);
7158c22cb84SValentin Clement     // The loop control variable may be used after the loop.
7161bffc753SEric Schweitz     lcv = builder.createConvert(loc, fir::unwrapRefType(loopVar.getType()),
7178c22cb84SValentin Clement                                 doLoopOp.getResult(0));
7188c22cb84SValentin Clement     builder.create<fir::StoreOp>(loc, lcv, loopVar);
7198c22cb84SValentin Clement     return;
7208c22cb84SValentin Clement   }
7218c22cb84SValentin Clement   // Check IO call results - the loop is a fir.iterate_while op.
7228c22cb84SValentin Clement   if (!ok)
7238c22cb84SValentin Clement     ok = builder.createBool(loc, true);
7248c22cb84SValentin Clement   auto iterWhileOp = builder.create<fir::IterWhileOp>(
7258c22cb84SValentin Clement       loc, lowerValue, upperValue, stepValue, ok, /*finalCountValue*/ true);
7268c22cb84SValentin Clement   builder.setInsertionPointToStart(iterWhileOp.getBody());
7271bffc753SEric Schweitz   mlir::Value lcv =
7281bffc753SEric Schweitz       builder.createConvert(loc, fir::unwrapRefType(loopVar.getType()),
7298c22cb84SValentin Clement                             iterWhileOp.getInductionVar());
7308c22cb84SValentin Clement   builder.create<fir::StoreOp>(loc, lcv, loopVar);
7318c22cb84SValentin Clement   ok = iterWhileOp.getIterateVar();
7328c22cb84SValentin Clement   mlir::Value falseValue =
7338c22cb84SValentin Clement       builder.createIntegerConstant(loc, builder.getI1Type(), 0);
7348c22cb84SValentin Clement   genItemList(ioImpliedDo);
7358c22cb84SValentin Clement   // Unwind nested IO call scopes, filling in true and false ResultOp's.
7368c22cb84SValentin Clement   for (mlir::Operation *op = builder.getBlock()->getParentOp();
7379aeb7f03SValentin Clement        mlir::isa<fir::IfOp>(op); op = op->getBlock()->getParentOp()) {
7389aeb7f03SValentin Clement     auto ifOp = mlir::dyn_cast<fir::IfOp>(op);
7398c22cb84SValentin Clement     mlir::Operation *lastOp = &ifOp.getThenRegion().front().back();
7408c22cb84SValentin Clement     builder.setInsertionPointAfter(lastOp);
7418c22cb84SValentin Clement     // The primary ifOp result is the result of an IO call or loop.
7428c22cb84SValentin Clement     if (mlir::isa<fir::CallOp, fir::IfOp>(*lastOp))
7438c22cb84SValentin Clement       builder.create<fir::ResultOp>(loc, lastOp->getResult(0));
7448c22cb84SValentin Clement     else
7458c22cb84SValentin Clement       builder.create<fir::ResultOp>(loc, ok); // loop result
7468c22cb84SValentin Clement     // The else branch propagates an early exit false result.
7478c22cb84SValentin Clement     builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
7488c22cb84SValentin Clement     builder.create<fir::ResultOp>(loc, falseValue);
7498c22cb84SValentin Clement   }
7508c22cb84SValentin Clement   builder.setInsertionPointToEnd(iterWhileOp.getBody());
7518c22cb84SValentin Clement   mlir::OpResult iterateResult = builder.getBlock()->back().getResult(0);
7528c22cb84SValentin Clement   mlir::Value inductionResult0 = iterWhileOp.getInductionVar();
7538c22cb84SValentin Clement   auto inductionResult1 = builder.create<mlir::arith::AddIOp>(
7548c22cb84SValentin Clement       loc, inductionResult0, iterWhileOp.getStep());
7558c22cb84SValentin Clement   auto inductionResult = builder.create<mlir::arith::SelectOp>(
7568c22cb84SValentin Clement       loc, iterateResult, inductionResult1, inductionResult0);
7578c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> results = {inductionResult, iterateResult};
7588c22cb84SValentin Clement   builder.create<fir::ResultOp>(loc, results);
7598c22cb84SValentin Clement   ok = iterWhileOp.getResult(1);
7608c22cb84SValentin Clement   builder.setInsertionPointAfter(iterWhileOp);
7618c22cb84SValentin Clement   // The loop control variable may be used after the loop.
7621bffc753SEric Schweitz   lcv = builder.createConvert(loc, fir::unwrapRefType(loopVar.getType()),
7638c22cb84SValentin Clement                               iterWhileOp.getResult(0));
7648c22cb84SValentin Clement   builder.create<fir::StoreOp>(loc, lcv, loopVar);
7658c22cb84SValentin Clement }
7668c22cb84SValentin Clement 
7678c22cb84SValentin Clement //===----------------------------------------------------------------------===//
7688c22cb84SValentin Clement // Default argument generation.
7698c22cb84SValentin Clement //===----------------------------------------------------------------------===//
7708c22cb84SValentin Clement 
locToFilename(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Type toType)7718c22cb84SValentin Clement static mlir::Value locToFilename(Fortran::lower::AbstractConverter &converter,
7728c22cb84SValentin Clement                                  mlir::Location loc, mlir::Type toType) {
7738c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
7748c22cb84SValentin Clement   return builder.createConvert(loc, toType,
7758c22cb84SValentin Clement                                fir::factory::locationToFilename(builder, loc));
7768c22cb84SValentin Clement }
7778c22cb84SValentin Clement 
locToLineNo(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Type toType)7788c22cb84SValentin Clement static mlir::Value locToLineNo(Fortran::lower::AbstractConverter &converter,
7798c22cb84SValentin Clement                                mlir::Location loc, mlir::Type toType) {
7808c22cb84SValentin Clement   return fir::factory::locationToLineNo(converter.getFirOpBuilder(), loc,
7818c22cb84SValentin Clement                                         toType);
7828c22cb84SValentin Clement }
7838c22cb84SValentin Clement 
getDefaultScratch(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type toType)7848c22cb84SValentin Clement static mlir::Value getDefaultScratch(fir::FirOpBuilder &builder,
7858c22cb84SValentin Clement                                      mlir::Location loc, mlir::Type toType) {
7868c22cb84SValentin Clement   mlir::Value null = builder.create<mlir::arith::ConstantOp>(
7878c22cb84SValentin Clement       loc, builder.getI64IntegerAttr(0));
7888c22cb84SValentin Clement   return builder.createConvert(loc, toType, null);
7898c22cb84SValentin Clement }
7908c22cb84SValentin Clement 
getDefaultScratchLen(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type toType)7918c22cb84SValentin Clement static mlir::Value getDefaultScratchLen(fir::FirOpBuilder &builder,
7928c22cb84SValentin Clement                                         mlir::Location loc, mlir::Type toType) {
7938c22cb84SValentin Clement   return builder.create<mlir::arith::ConstantOp>(
7948c22cb84SValentin Clement       loc, builder.getIntegerAttr(toType, 0));
7958c22cb84SValentin Clement }
7968c22cb84SValentin Clement 
7978c22cb84SValentin Clement /// Generate a reference to a buffer and the length of buffer given
7988c22cb84SValentin Clement /// a character expression. An array expression will be cast to scalar
7998c22cb84SValentin Clement /// character as long as they are contiguous.
8008c22cb84SValentin Clement static std::tuple<mlir::Value, mlir::Value>
genBuffer(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::lower::SomeExpr & expr,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)8018c22cb84SValentin Clement genBuffer(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
8028c22cb84SValentin Clement           const Fortran::lower::SomeExpr &expr, mlir::Type strTy,
8038c22cb84SValentin Clement           mlir::Type lenTy, Fortran::lower::StatementContext &stmtCtx) {
8048c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
8058c22cb84SValentin Clement   fir::ExtendedValue exprAddr = converter.genExprAddr(expr, stmtCtx);
8068c22cb84SValentin Clement   fir::factory::CharacterExprHelper helper(builder, loc);
8078c22cb84SValentin Clement   using ValuePair = std::pair<mlir::Value, mlir::Value>;
8088c22cb84SValentin Clement   auto [buff, len] = exprAddr.match(
8098c22cb84SValentin Clement       [&](const fir::CharBoxValue &x) -> ValuePair {
8108c22cb84SValentin Clement         return {x.getBuffer(), x.getLen()};
8118c22cb84SValentin Clement       },
8128c22cb84SValentin Clement       [&](const fir::CharArrayBoxValue &x) -> ValuePair {
8138c22cb84SValentin Clement         fir::CharBoxValue scalar = helper.toScalarCharacter(x);
8148c22cb84SValentin Clement         return {scalar.getBuffer(), scalar.getLen()};
8158c22cb84SValentin Clement       },
8168c22cb84SValentin Clement       [&](const fir::BoxValue &) -> ValuePair {
8178c22cb84SValentin Clement         // May need to copy before after IO to handle contiguous
8188c22cb84SValentin Clement         // aspect. Not sure descriptor can get here though.
8198c22cb84SValentin Clement         TODO(loc, "character descriptor to contiguous buffer");
8208c22cb84SValentin Clement       },
8218c22cb84SValentin Clement       [&](const auto &) -> ValuePair {
8228c22cb84SValentin Clement         llvm::report_fatal_error(
8238c22cb84SValentin Clement             "internal error: IO buffer is not a character");
8248c22cb84SValentin Clement       });
8258c22cb84SValentin Clement   buff = builder.createConvert(loc, strTy, buff);
8268c22cb84SValentin Clement   len = builder.createConvert(loc, lenTy, len);
8278c22cb84SValentin Clement   return {buff, len};
8288c22cb84SValentin Clement }
8298c22cb84SValentin Clement 
8308c22cb84SValentin Clement /// Lower a string literal. Many arguments to the runtime are conveyed as
8318c22cb84SValentin Clement /// Fortran CHARACTER literals.
8328c22cb84SValentin Clement template <typename A>
8338c22cb84SValentin Clement static std::tuple<mlir::Value, mlir::Value, mlir::Value>
lowerStringLit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,Fortran::lower::StatementContext & stmtCtx,const A & syntax,mlir::Type strTy,mlir::Type lenTy,mlir::Type ty2={})8348c22cb84SValentin Clement lowerStringLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
8358c22cb84SValentin Clement                Fortran::lower::StatementContext &stmtCtx, const A &syntax,
8368c22cb84SValentin Clement                mlir::Type strTy, mlir::Type lenTy, mlir::Type ty2 = {}) {
8378c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
8388c22cb84SValentin Clement   auto *expr = Fortran::semantics::GetExpr(syntax);
8398c22cb84SValentin Clement   if (!expr)
8408c22cb84SValentin Clement     fir::emitFatalError(loc, "internal error: null semantic expr in IO");
8418c22cb84SValentin Clement   auto [buff, len] = genBuffer(converter, loc, *expr, strTy, lenTy, stmtCtx);
8428c22cb84SValentin Clement   mlir::Value kind;
8438c22cb84SValentin Clement   if (ty2) {
8448c22cb84SValentin Clement     auto kindVal = expr->GetType().value().kind();
8458c22cb84SValentin Clement     kind = builder.create<mlir::arith::ConstantOp>(
8468c22cb84SValentin Clement         loc, builder.getIntegerAttr(ty2, kindVal));
8478c22cb84SValentin Clement   }
8488c22cb84SValentin Clement   return {buff, len, kind};
8498c22cb84SValentin Clement }
8508c22cb84SValentin Clement 
8518c22cb84SValentin Clement /// Pass the body of the FORMAT statement in as if it were a CHARACTER literal
8528c22cb84SValentin Clement /// constant. NB: This is the prescribed manner in which the front-end passes
8538c22cb84SValentin Clement /// this information to lowering.
8548c22cb84SValentin Clement static std::tuple<mlir::Value, mlir::Value, mlir::Value>
lowerSourceTextAsStringLit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,llvm::StringRef text,mlir::Type strTy,mlir::Type lenTy)8558c22cb84SValentin Clement lowerSourceTextAsStringLit(Fortran::lower::AbstractConverter &converter,
8568c22cb84SValentin Clement                            mlir::Location loc, llvm::StringRef text,
8578c22cb84SValentin Clement                            mlir::Type strTy, mlir::Type lenTy) {
8588c22cb84SValentin Clement   text = text.drop_front(text.find('('));
8598c22cb84SValentin Clement   text = text.take_front(text.rfind(')') + 1);
8608c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
8618c22cb84SValentin Clement   mlir::Value addrGlobalStringLit =
8628c22cb84SValentin Clement       fir::getBase(fir::factory::createStringLiteral(builder, loc, text));
8638c22cb84SValentin Clement   mlir::Value buff = builder.createConvert(loc, strTy, addrGlobalStringLit);
8648c22cb84SValentin Clement   mlir::Value len = builder.createIntegerConstant(loc, lenTy, text.size());
8658c22cb84SValentin Clement   return {buff, len, mlir::Value{}};
8668c22cb84SValentin Clement }
8678c22cb84SValentin Clement 
8688c22cb84SValentin Clement //===----------------------------------------------------------------------===//
8698c22cb84SValentin Clement // Handle IO statement specifiers.
8708c22cb84SValentin Clement // These are threaded together for a single statement via the passed cookie.
8718c22cb84SValentin Clement //===----------------------------------------------------------------------===//
8728c22cb84SValentin Clement 
8738c22cb84SValentin Clement /// Generic to build an integral argument to the runtime.
8748c22cb84SValentin Clement template <typename A, typename B>
genIntIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const B & spec)8758c22cb84SValentin Clement mlir::Value genIntIOOption(Fortran::lower::AbstractConverter &converter,
8768c22cb84SValentin Clement                            mlir::Location loc, mlir::Value cookie,
8778c22cb84SValentin Clement                            const B &spec) {
8788c22cb84SValentin Clement   Fortran::lower::StatementContext localStatementCtx;
8798c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
8801c7889caSValentin Clement   mlir::func::FuncOp ioFunc = getIORuntimeFunc<A>(loc, builder);
8814a3460a7SRiver Riddle   mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
8828c22cb84SValentin Clement   mlir::Value expr = fir::getBase(converter.genExprValue(
8831bffc753SEric Schweitz       loc, Fortran::semantics::GetExpr(spec.v), localStatementCtx));
8848c22cb84SValentin Clement   mlir::Value val = builder.createConvert(loc, ioFuncTy.getInput(1), expr);
8858c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> ioArgs = {cookie, val};
8868c22cb84SValentin Clement   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
8878c22cb84SValentin Clement }
8888c22cb84SValentin Clement 
8898c22cb84SValentin Clement /// Generic to build a string argument to the runtime. This passes a CHARACTER
8908c22cb84SValentin Clement /// as a pointer to the buffer and a LEN parameter.
8918c22cb84SValentin Clement template <typename A, typename B>
genCharIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const B & spec)8928c22cb84SValentin Clement mlir::Value genCharIOOption(Fortran::lower::AbstractConverter &converter,
8938c22cb84SValentin Clement                             mlir::Location loc, mlir::Value cookie,
8948c22cb84SValentin Clement                             const B &spec) {
8958c22cb84SValentin Clement   Fortran::lower::StatementContext localStatementCtx;
8968c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
8971c7889caSValentin Clement   mlir::func::FuncOp ioFunc = getIORuntimeFunc<A>(loc, builder);
8984a3460a7SRiver Riddle   mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
8998c22cb84SValentin Clement   std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
9008c22cb84SValentin Clement       lowerStringLit(converter, loc, localStatementCtx, spec,
9018c22cb84SValentin Clement                      ioFuncTy.getInput(1), ioFuncTy.getInput(2));
9028c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> ioArgs = {cookie, std::get<0>(tup),
9038c22cb84SValentin Clement                                            std::get<1>(tup)};
9048c22cb84SValentin Clement   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
9058c22cb84SValentin Clement }
9068c22cb84SValentin Clement 
9078c22cb84SValentin Clement template <typename A>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & spec)9088c22cb84SValentin Clement mlir::Value genIOOption(Fortran::lower::AbstractConverter &converter,
9098c22cb84SValentin Clement                         mlir::Location loc, mlir::Value cookie, const A &spec) {
9108c22cb84SValentin Clement   // These specifiers are processed in advance elsewhere - skip them here.
9118c22cb84SValentin Clement   using PreprocessedSpecs =
9128c22cb84SValentin Clement       std::tuple<Fortran::parser::EndLabel, Fortran::parser::EorLabel,
9138c22cb84SValentin Clement                  Fortran::parser::ErrLabel, Fortran::parser::FileUnitNumber,
9148c22cb84SValentin Clement                  Fortran::parser::Format, Fortran::parser::IoUnit,
9158c22cb84SValentin Clement                  Fortran::parser::MsgVariable, Fortran::parser::Name,
9168c22cb84SValentin Clement                  Fortran::parser::StatVariable>;
9178c22cb84SValentin Clement   static_assert(Fortran::common::HasMember<A, PreprocessedSpecs>,
9188c22cb84SValentin Clement                 "missing genIOOPtion specialization");
9198c22cb84SValentin Clement   return {};
9208c22cb84SValentin Clement }
9218c22cb84SValentin Clement 
9228c22cb84SValentin Clement template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::FileNameExpr & spec)9238c22cb84SValentin Clement mlir::Value genIOOption<Fortran::parser::FileNameExpr>(
9248c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
9258c22cb84SValentin Clement     mlir::Value cookie, const Fortran::parser::FileNameExpr &spec) {
9268c22cb84SValentin Clement   Fortran::lower::StatementContext localStatementCtx;
9278c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
9288c22cb84SValentin Clement   // has an extra KIND argument
9291c7889caSValentin Clement   mlir::func::FuncOp ioFunc = getIORuntimeFunc<mkIOKey(SetFile)>(loc, builder);
9304a3460a7SRiver Riddle   mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
9318c22cb84SValentin Clement   std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
9328c22cb84SValentin Clement       lowerStringLit(converter, loc, localStatementCtx, spec,
9338c22cb84SValentin Clement                      ioFuncTy.getInput(1), ioFuncTy.getInput(2));
9348c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> ioArgs{cookie, std::get<0>(tup),
9358c22cb84SValentin Clement                                         std::get<1>(tup)};
9368c22cb84SValentin Clement   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
9378c22cb84SValentin Clement }
9388c22cb84SValentin Clement 
9398c22cb84SValentin Clement template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::ConnectSpec::CharExpr & spec)9408c22cb84SValentin Clement mlir::Value genIOOption<Fortran::parser::ConnectSpec::CharExpr>(
9418c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
9428c22cb84SValentin Clement     mlir::Value cookie, const Fortran::parser::ConnectSpec::CharExpr &spec) {
9438c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
94458ceae95SRiver Riddle   mlir::func::FuncOp ioFunc;
9458c22cb84SValentin Clement   switch (std::get<Fortran::parser::ConnectSpec::CharExpr::Kind>(spec.t)) {
9468c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Access:
9478c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetAccess)>(loc, builder);
9488c22cb84SValentin Clement     break;
9498c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Action:
9508c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetAction)>(loc, builder);
9518c22cb84SValentin Clement     break;
9528c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Asynchronous:
9538c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetAsynchronous)>(loc, builder);
9548c22cb84SValentin Clement     break;
9558c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Blank:
9568c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetBlank)>(loc, builder);
9578c22cb84SValentin Clement     break;
9588c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Decimal:
9598c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetDecimal)>(loc, builder);
9608c22cb84SValentin Clement     break;
9618c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Delim:
9628c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetDelim)>(loc, builder);
9638c22cb84SValentin Clement     break;
9648c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Encoding:
9658c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetEncoding)>(loc, builder);
9668c22cb84SValentin Clement     break;
9678c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Form:
9688c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetForm)>(loc, builder);
9698c22cb84SValentin Clement     break;
9708c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Pad:
9718c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetPad)>(loc, builder);
9728c22cb84SValentin Clement     break;
9738c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Position:
9748c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetPosition)>(loc, builder);
9758c22cb84SValentin Clement     break;
9768c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Round:
9778c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetRound)>(loc, builder);
9788c22cb84SValentin Clement     break;
9798c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Sign:
9808c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetSign)>(loc, builder);
9818c22cb84SValentin Clement     break;
9828c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Carriagecontrol:
9838c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetCarriagecontrol)>(loc, builder);
9848c22cb84SValentin Clement     break;
9858c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Convert:
9868c22cb84SValentin Clement     TODO(loc, "CONVERT not part of the runtime::io interface");
9878c22cb84SValentin Clement   case Fortran::parser::ConnectSpec::CharExpr::Kind::Dispose:
9888c22cb84SValentin Clement     TODO(loc, "DISPOSE not part of the runtime::io interface");
9898c22cb84SValentin Clement   }
9908c22cb84SValentin Clement   Fortran::lower::StatementContext localStatementCtx;
9914a3460a7SRiver Riddle   mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
9928c22cb84SValentin Clement   std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
9938c22cb84SValentin Clement       lowerStringLit(converter, loc, localStatementCtx,
9948c22cb84SValentin Clement                      std::get<Fortran::parser::ScalarDefaultCharExpr>(spec.t),
9958c22cb84SValentin Clement                      ioFuncTy.getInput(1), ioFuncTy.getInput(2));
9968c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> ioArgs = {cookie, std::get<0>(tup),
9978c22cb84SValentin Clement                                            std::get<1>(tup)};
9988c22cb84SValentin Clement   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
9998c22cb84SValentin Clement }
10008c22cb84SValentin Clement 
10018c22cb84SValentin Clement template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::ConnectSpec::Recl & spec)10028c22cb84SValentin Clement mlir::Value genIOOption<Fortran::parser::ConnectSpec::Recl>(
10038c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
10048c22cb84SValentin Clement     mlir::Value cookie, const Fortran::parser::ConnectSpec::Recl &spec) {
10058c22cb84SValentin Clement   return genIntIOOption<mkIOKey(SetRecl)>(converter, loc, cookie, spec);
10068c22cb84SValentin Clement }
10078c22cb84SValentin Clement 
10088c22cb84SValentin Clement template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::StatusExpr & spec)10098c22cb84SValentin Clement mlir::Value genIOOption<Fortran::parser::StatusExpr>(
10108c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
10118c22cb84SValentin Clement     mlir::Value cookie, const Fortran::parser::StatusExpr &spec) {
10128c22cb84SValentin Clement   return genCharIOOption<mkIOKey(SetStatus)>(converter, loc, cookie, spec.v);
10138c22cb84SValentin Clement }
10148c22cb84SValentin Clement 
10158c22cb84SValentin Clement template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::CharExpr & spec)10168c22cb84SValentin Clement mlir::Value genIOOption<Fortran::parser::IoControlSpec::CharExpr>(
10178c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
10188c22cb84SValentin Clement     mlir::Value cookie, const Fortran::parser::IoControlSpec::CharExpr &spec) {
10198c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
102058ceae95SRiver Riddle   mlir::func::FuncOp ioFunc;
10218c22cb84SValentin Clement   switch (std::get<Fortran::parser::IoControlSpec::CharExpr::Kind>(spec.t)) {
10228c22cb84SValentin Clement   case Fortran::parser::IoControlSpec::CharExpr::Kind::Advance:
10238c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetAdvance)>(loc, builder);
10248c22cb84SValentin Clement     break;
10258c22cb84SValentin Clement   case Fortran::parser::IoControlSpec::CharExpr::Kind::Blank:
10268c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetBlank)>(loc, builder);
10278c22cb84SValentin Clement     break;
10288c22cb84SValentin Clement   case Fortran::parser::IoControlSpec::CharExpr::Kind::Decimal:
10298c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetDecimal)>(loc, builder);
10308c22cb84SValentin Clement     break;
10318c22cb84SValentin Clement   case Fortran::parser::IoControlSpec::CharExpr::Kind::Delim:
10328c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetDelim)>(loc, builder);
10338c22cb84SValentin Clement     break;
10348c22cb84SValentin Clement   case Fortran::parser::IoControlSpec::CharExpr::Kind::Pad:
10358c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetPad)>(loc, builder);
10368c22cb84SValentin Clement     break;
10378c22cb84SValentin Clement   case Fortran::parser::IoControlSpec::CharExpr::Kind::Round:
10388c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetRound)>(loc, builder);
10398c22cb84SValentin Clement     break;
10408c22cb84SValentin Clement   case Fortran::parser::IoControlSpec::CharExpr::Kind::Sign:
10418c22cb84SValentin Clement     ioFunc = getIORuntimeFunc<mkIOKey(SetSign)>(loc, builder);
10428c22cb84SValentin Clement     break;
10438c22cb84SValentin Clement   }
10448c22cb84SValentin Clement   Fortran::lower::StatementContext localStatementCtx;
10454a3460a7SRiver Riddle   mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
10468c22cb84SValentin Clement   std::tuple<mlir::Value, mlir::Value, mlir::Value> tup =
10478c22cb84SValentin Clement       lowerStringLit(converter, loc, localStatementCtx,
10488c22cb84SValentin Clement                      std::get<Fortran::parser::ScalarDefaultCharExpr>(spec.t),
10498c22cb84SValentin Clement                      ioFuncTy.getInput(1), ioFuncTy.getInput(2));
10508c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> ioArgs = {cookie, std::get<0>(tup),
10518c22cb84SValentin Clement                                            std::get<1>(tup)};
10528c22cb84SValentin Clement   return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
10538c22cb84SValentin Clement }
10548c22cb84SValentin Clement 
10558c22cb84SValentin Clement template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::Asynchronous & spec)10568c22cb84SValentin Clement mlir::Value genIOOption<Fortran::parser::IoControlSpec::Asynchronous>(
10578c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
10588c22cb84SValentin Clement     mlir::Value cookie,
10598c22cb84SValentin Clement     const Fortran::parser::IoControlSpec::Asynchronous &spec) {
10608c22cb84SValentin Clement   return genCharIOOption<mkIOKey(SetAsynchronous)>(converter, loc, cookie,
10618c22cb84SValentin Clement                                                    spec.v);
10628c22cb84SValentin Clement }
10638c22cb84SValentin Clement 
10648c22cb84SValentin Clement template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IdVariable & spec)10658c22cb84SValentin Clement mlir::Value genIOOption<Fortran::parser::IdVariable>(
10668c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
10678c22cb84SValentin Clement     mlir::Value cookie, const Fortran::parser::IdVariable &spec) {
10688c22cb84SValentin Clement   TODO(loc, "asynchronous ID not implemented");
10698c22cb84SValentin Clement }
10708c22cb84SValentin Clement 
10718c22cb84SValentin Clement template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::Pos & spec)10728c22cb84SValentin Clement mlir::Value genIOOption<Fortran::parser::IoControlSpec::Pos>(
10738c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
10748c22cb84SValentin Clement     mlir::Value cookie, const Fortran::parser::IoControlSpec::Pos &spec) {
10758c22cb84SValentin Clement   return genIntIOOption<mkIOKey(SetPos)>(converter, loc, cookie, spec);
10768c22cb84SValentin Clement }
10778c22cb84SValentin Clement 
10788c22cb84SValentin Clement template <>
genIOOption(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const Fortran::parser::IoControlSpec::Rec & spec)10798c22cb84SValentin Clement mlir::Value genIOOption<Fortran::parser::IoControlSpec::Rec>(
10808c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
10818c22cb84SValentin Clement     mlir::Value cookie, const Fortran::parser::IoControlSpec::Rec &spec) {
10828c22cb84SValentin Clement   return genIntIOOption<mkIOKey(SetRec)>(converter, loc, cookie, spec);
10838c22cb84SValentin Clement }
10848c22cb84SValentin Clement 
10858c22cb84SValentin Clement /// Generate runtime call to query the read size after an input statement if
10868c22cb84SValentin Clement /// the statement has SIZE control-spec.
10878c22cb84SValentin Clement template <typename A>
genIOReadSize(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & specList,bool checkResult)10888c22cb84SValentin Clement static void genIOReadSize(Fortran::lower::AbstractConverter &converter,
10898c22cb84SValentin Clement                           mlir::Location loc, mlir::Value cookie,
10908c22cb84SValentin Clement                           const A &specList, bool checkResult) {
10918c22cb84SValentin Clement   // This call is not conditional on the current IO status (ok) because the size
10928c22cb84SValentin Clement   // needs to be filled even if some error condition (end-of-file...) was met
10938c22cb84SValentin Clement   // during the input statement (in which case the runtime may return zero for
10948c22cb84SValentin Clement   // the size read).
10958c22cb84SValentin Clement   for (const auto &spec : specList)
10968c22cb84SValentin Clement     if (const auto *size =
10978c22cb84SValentin Clement             std::get_if<Fortran::parser::IoControlSpec::Size>(&spec.u)) {
10988c22cb84SValentin Clement 
10998c22cb84SValentin Clement       fir::FirOpBuilder &builder = converter.getFirOpBuilder();
11001c7889caSValentin Clement       mlir::func::FuncOp ioFunc =
11011c7889caSValentin Clement           getIORuntimeFunc<mkIOKey(GetSize)>(loc, builder);
11028c22cb84SValentin Clement       auto sizeValue =
11038c22cb84SValentin Clement           builder.create<fir::CallOp>(loc, ioFunc, mlir::ValueRange{cookie})
11048c22cb84SValentin Clement               .getResult(0);
11058c22cb84SValentin Clement       Fortran::lower::StatementContext localStatementCtx;
11068c22cb84SValentin Clement       fir::ExtendedValue var = converter.genExprAddr(
11071bffc753SEric Schweitz           loc, Fortran::semantics::GetExpr(size->v), localStatementCtx);
11088c22cb84SValentin Clement       mlir::Value varAddr = fir::getBase(var);
11098c22cb84SValentin Clement       mlir::Type varType = fir::unwrapPassByRefType(varAddr.getType());
11108c22cb84SValentin Clement       mlir::Value sizeCast = builder.createConvert(loc, varType, sizeValue);
11118c22cb84SValentin Clement       builder.create<fir::StoreOp>(loc, sizeCast, varAddr);
11128c22cb84SValentin Clement       break;
11138c22cb84SValentin Clement     }
11148c22cb84SValentin Clement }
11158c22cb84SValentin Clement 
11168c22cb84SValentin Clement //===----------------------------------------------------------------------===//
11178c22cb84SValentin Clement // Gather IO statement condition specifier information (if any).
11188c22cb84SValentin Clement //===----------------------------------------------------------------------===//
11198c22cb84SValentin Clement 
11208c22cb84SValentin Clement template <typename SEEK, typename A>
hasX(const A & list)11218c22cb84SValentin Clement static bool hasX(const A &list) {
11228c22cb84SValentin Clement   for (const auto &spec : list)
11238c22cb84SValentin Clement     if (std::holds_alternative<SEEK>(spec.u))
11248c22cb84SValentin Clement       return true;
11258c22cb84SValentin Clement   return false;
11268c22cb84SValentin Clement }
11278c22cb84SValentin Clement 
1128db48f7b2SValentin Clement template <typename SEEK, typename A>
hasSpec(const A & stmt)11299aeb7f03SValentin Clement static bool hasSpec(const A &stmt) {
1130db48f7b2SValentin Clement   return hasX<SEEK>(stmt.v);
1131db48f7b2SValentin Clement }
1132db48f7b2SValentin Clement 
1133db48f7b2SValentin Clement /// Get the sought expression from the specifier list.
1134db48f7b2SValentin Clement template <typename SEEK, typename A>
getExpr(const A & stmt)1135db48f7b2SValentin Clement static const Fortran::lower::SomeExpr *getExpr(const A &stmt) {
1136db48f7b2SValentin Clement   for (const auto &spec : stmt.v)
1137db48f7b2SValentin Clement     if (auto *f = std::get_if<SEEK>(&spec.u))
1138db48f7b2SValentin Clement       return Fortran::semantics::GetExpr(f->v);
1139db48f7b2SValentin Clement   llvm::report_fatal_error("must have a file unit");
1140db48f7b2SValentin Clement }
1141db48f7b2SValentin Clement 
11428c22cb84SValentin Clement /// For each specifier, build the appropriate call, threading the cookie.
11438c22cb84SValentin Clement template <typename A>
threadSpecs(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & specList,bool checkResult,mlir::Value & ok)11448c22cb84SValentin Clement static void threadSpecs(Fortran::lower::AbstractConverter &converter,
11458c22cb84SValentin Clement                         mlir::Location loc, mlir::Value cookie,
11468c22cb84SValentin Clement                         const A &specList, bool checkResult, mlir::Value &ok) {
11478c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
11488c22cb84SValentin Clement   for (const auto &spec : specList) {
11498c22cb84SValentin Clement     makeNextConditionalOn(builder, loc, checkResult, ok);
11508c22cb84SValentin Clement     ok = std::visit(
11518c22cb84SValentin Clement         Fortran::common::visitors{
11528c22cb84SValentin Clement             [&](const Fortran::parser::IoControlSpec::Size &x) -> mlir::Value {
11538c22cb84SValentin Clement               // Size must be queried after the related READ runtime calls, not
11548c22cb84SValentin Clement               // before.
11558c22cb84SValentin Clement               return ok;
11568c22cb84SValentin Clement             },
11579aeb7f03SValentin Clement             [&](const Fortran::parser::ConnectSpec::Newunit &x) -> mlir::Value {
11589aeb7f03SValentin Clement               // Newunit must be queried after OPEN specifier runtime calls
11599aeb7f03SValentin Clement               // that may fail to avoid modifying the newunit variable if
11609aeb7f03SValentin Clement               // there is an error.
11619aeb7f03SValentin Clement               return ok;
11629aeb7f03SValentin Clement             },
11638c22cb84SValentin Clement             [&](const auto &x) {
11648c22cb84SValentin Clement               return genIOOption(converter, loc, cookie, x);
11658c22cb84SValentin Clement             }},
11668c22cb84SValentin Clement         spec.u);
11678c22cb84SValentin Clement   }
11688c22cb84SValentin Clement }
11698c22cb84SValentin Clement 
11708c22cb84SValentin Clement /// Most IO statements allow one or more of five optional exception condition
11718c22cb84SValentin Clement /// handling specifiers: ERR, EOR, END, IOSTAT, and IOMSG. The first three
11728c22cb84SValentin Clement /// cause control flow to transfer to another statement. The final two return
11738c22cb84SValentin Clement /// information from the runtime, via a variable, about the nature of the
11748c22cb84SValentin Clement /// condition that occurred. These condition specifiers are handled here.
11758c22cb84SValentin Clement template <typename A>
lowerErrorSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & specList)11761bffc753SEric Schweitz ConditionSpecInfo lowerErrorSpec(Fortran::lower::AbstractConverter &converter,
11771bffc753SEric Schweitz                                  mlir::Location loc, const A &specList) {
11781bffc753SEric Schweitz   ConditionSpecInfo csi;
11791bffc753SEric Schweitz   const Fortran::lower::SomeExpr *ioMsgExpr = nullptr;
11808c22cb84SValentin Clement   for (const auto &spec : specList) {
11818c22cb84SValentin Clement     std::visit(
11828c22cb84SValentin Clement         Fortran::common::visitors{
11838c22cb84SValentin Clement             [&](const Fortran::parser::StatVariable &var) {
11848c22cb84SValentin Clement               csi.ioStatExpr = Fortran::semantics::GetExpr(var);
11858c22cb84SValentin Clement             },
11868c22cb84SValentin Clement             [&](const Fortran::parser::InquireSpec::IntVar &var) {
11878c22cb84SValentin Clement               if (std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t) ==
11888c22cb84SValentin Clement                   Fortran::parser::InquireSpec::IntVar::Kind::Iostat)
11898c22cb84SValentin Clement                 csi.ioStatExpr = Fortran::semantics::GetExpr(
11908c22cb84SValentin Clement                     std::get<Fortran::parser::ScalarIntVariable>(var.t));
11918c22cb84SValentin Clement             },
11928c22cb84SValentin Clement             [&](const Fortran::parser::MsgVariable &var) {
11931bffc753SEric Schweitz               ioMsgExpr = Fortran::semantics::GetExpr(var);
11948c22cb84SValentin Clement             },
11958c22cb84SValentin Clement             [&](const Fortran::parser::InquireSpec::CharVar &var) {
11968c22cb84SValentin Clement               if (std::get<Fortran::parser::InquireSpec::CharVar::Kind>(
11978c22cb84SValentin Clement                       var.t) ==
11988c22cb84SValentin Clement                   Fortran::parser::InquireSpec::CharVar::Kind::Iomsg)
11991bffc753SEric Schweitz                 ioMsgExpr = Fortran::semantics::GetExpr(
12008c22cb84SValentin Clement                     std::get<Fortran::parser::ScalarDefaultCharVariable>(
12018c22cb84SValentin Clement                         var.t));
12028c22cb84SValentin Clement             },
12038c22cb84SValentin Clement             [&](const Fortran::parser::EndLabel &) { csi.hasEnd = true; },
12048c22cb84SValentin Clement             [&](const Fortran::parser::EorLabel &) { csi.hasEor = true; },
12058c22cb84SValentin Clement             [&](const Fortran::parser::ErrLabel &) { csi.hasErr = true; },
12068c22cb84SValentin Clement             [](const auto &) {}},
12078c22cb84SValentin Clement         spec.u);
12088c22cb84SValentin Clement   }
12091bffc753SEric Schweitz   if (ioMsgExpr) {
12101bffc753SEric Schweitz     // iomsg is a variable, its evaluation may require temps, but it cannot
12111bffc753SEric Schweitz     // itself be a temp, and it is ok to us a local statement context here.
12121bffc753SEric Schweitz     Fortran::lower::StatementContext stmtCtx;
12131bffc753SEric Schweitz     csi.ioMsg = converter.genExprAddr(loc, ioMsgExpr, stmtCtx);
12141bffc753SEric Schweitz   }
12151bffc753SEric Schweitz 
12161bffc753SEric Schweitz   return csi;
12171bffc753SEric Schweitz }
12181bffc753SEric Schweitz template <typename A>
12191bffc753SEric Schweitz static void
genConditionHandlerCall(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const A & specList,ConditionSpecInfo & csi)12201bffc753SEric Schweitz genConditionHandlerCall(Fortran::lower::AbstractConverter &converter,
12211bffc753SEric Schweitz                         mlir::Location loc, mlir::Value cookie,
12221bffc753SEric Schweitz                         const A &specList, ConditionSpecInfo &csi) {
12238c22cb84SValentin Clement   if (!csi.hasAnyConditionSpec())
12248c22cb84SValentin Clement     return;
12258c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
12261c7889caSValentin Clement   mlir::func::FuncOp enableHandlers =
12271c7889caSValentin Clement       getIORuntimeFunc<mkIOKey(EnableHandlers)>(loc, builder);
12284a3460a7SRiver Riddle   mlir::Type boolType = enableHandlers.getFunctionType().getInput(1);
12298c22cb84SValentin Clement   auto boolValue = [&](bool specifierIsPresent) {
12308c22cb84SValentin Clement     return builder.create<mlir::arith::ConstantOp>(
12318c22cb84SValentin Clement         loc, builder.getIntegerAttr(boolType, specifierIsPresent));
12328c22cb84SValentin Clement   };
12338c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> ioArgs = {cookie,
12348c22cb84SValentin Clement                                            boolValue(csi.ioStatExpr != nullptr),
12358c22cb84SValentin Clement                                            boolValue(csi.hasErr),
12368c22cb84SValentin Clement                                            boolValue(csi.hasEnd),
12378c22cb84SValentin Clement                                            boolValue(csi.hasEor),
12380916d96dSKazu Hirata                                            boolValue(csi.ioMsg.has_value())};
12398c22cb84SValentin Clement   builder.create<fir::CallOp>(loc, enableHandlers, ioArgs);
12408c22cb84SValentin Clement }
12418c22cb84SValentin Clement 
12428c22cb84SValentin Clement //===----------------------------------------------------------------------===//
12438c22cb84SValentin Clement // Data transfer helpers
12448c22cb84SValentin Clement //===----------------------------------------------------------------------===//
12458c22cb84SValentin Clement 
12468c22cb84SValentin Clement template <typename SEEK, typename A>
hasIOControl(const A & stmt)12478c22cb84SValentin Clement static bool hasIOControl(const A &stmt) {
12488c22cb84SValentin Clement   return hasX<SEEK>(stmt.controls);
12498c22cb84SValentin Clement }
12508c22cb84SValentin Clement 
12518c22cb84SValentin Clement template <typename SEEK, typename A>
getIOControl(const A & stmt)12528c22cb84SValentin Clement static const auto *getIOControl(const A &stmt) {
12538c22cb84SValentin Clement   for (const auto &spec : stmt.controls)
12548c22cb84SValentin Clement     if (const auto *result = std::get_if<SEEK>(&spec.u))
12558c22cb84SValentin Clement       return result;
12568c22cb84SValentin Clement   return static_cast<const SEEK *>(nullptr);
12578c22cb84SValentin Clement }
12588c22cb84SValentin Clement 
12598c22cb84SValentin Clement /// Returns true iff the expression in the parse tree is not really a format but
12608c22cb84SValentin Clement /// rather a namelist group.
12618c22cb84SValentin Clement template <typename A>
formatIsActuallyNamelist(const A & format)12628c22cb84SValentin Clement static bool formatIsActuallyNamelist(const A &format) {
12638c22cb84SValentin Clement   if (auto *e = std::get_if<Fortran::parser::Expr>(&format.u)) {
12648c22cb84SValentin Clement     auto *expr = Fortran::semantics::GetExpr(*e);
12658c22cb84SValentin Clement     if (const Fortran::semantics::Symbol *y =
12668c22cb84SValentin Clement             Fortran::evaluate::UnwrapWholeSymbolDataRef(*expr))
12678c22cb84SValentin Clement       return y->has<Fortran::semantics::NamelistDetails>();
12688c22cb84SValentin Clement   }
12698c22cb84SValentin Clement   return false;
12708c22cb84SValentin Clement }
12718c22cb84SValentin Clement 
12728c22cb84SValentin Clement template <typename A>
isDataTransferFormatted(const A & stmt)12738c22cb84SValentin Clement static bool isDataTransferFormatted(const A &stmt) {
12748c22cb84SValentin Clement   if (stmt.format)
12758c22cb84SValentin Clement     return !formatIsActuallyNamelist(*stmt.format);
12768c22cb84SValentin Clement   return hasIOControl<Fortran::parser::Format>(stmt);
12778c22cb84SValentin Clement }
12788c22cb84SValentin Clement template <>
isDataTransferFormatted(const Fortran::parser::PrintStmt &)12798c22cb84SValentin Clement constexpr bool isDataTransferFormatted<Fortran::parser::PrintStmt>(
12808c22cb84SValentin Clement     const Fortran::parser::PrintStmt &) {
12818c22cb84SValentin Clement   return true; // PRINT is always formatted
12828c22cb84SValentin Clement }
12838c22cb84SValentin Clement 
12848c22cb84SValentin Clement template <typename A>
isDataTransferList(const A & stmt)12858c22cb84SValentin Clement static bool isDataTransferList(const A &stmt) {
12868c22cb84SValentin Clement   if (stmt.format)
12878c22cb84SValentin Clement     return std::holds_alternative<Fortran::parser::Star>(stmt.format->u);
12888c22cb84SValentin Clement   if (auto *mem = getIOControl<Fortran::parser::Format>(stmt))
12898c22cb84SValentin Clement     return std::holds_alternative<Fortran::parser::Star>(mem->u);
12908c22cb84SValentin Clement   return false;
12918c22cb84SValentin Clement }
12928c22cb84SValentin Clement template <>
isDataTransferList(const Fortran::parser::PrintStmt & stmt)12938c22cb84SValentin Clement bool isDataTransferList<Fortran::parser::PrintStmt>(
12948c22cb84SValentin Clement     const Fortran::parser::PrintStmt &stmt) {
12958c22cb84SValentin Clement   return std::holds_alternative<Fortran::parser::Star>(
12968c22cb84SValentin Clement       std::get<Fortran::parser::Format>(stmt.t).u);
12978c22cb84SValentin Clement }
12988c22cb84SValentin Clement 
12998c22cb84SValentin Clement template <typename A>
isDataTransferInternal(const A & stmt)13008c22cb84SValentin Clement static bool isDataTransferInternal(const A &stmt) {
13018c22cb84SValentin Clement   if (stmt.iounit.has_value())
13028c22cb84SValentin Clement     return std::holds_alternative<Fortran::parser::Variable>(stmt.iounit->u);
13038c22cb84SValentin Clement   if (auto *unit = getIOControl<Fortran::parser::IoUnit>(stmt))
13048c22cb84SValentin Clement     return std::holds_alternative<Fortran::parser::Variable>(unit->u);
13058c22cb84SValentin Clement   return false;
13068c22cb84SValentin Clement }
13078c22cb84SValentin Clement template <>
isDataTransferInternal(const Fortran::parser::PrintStmt &)13088c22cb84SValentin Clement constexpr bool isDataTransferInternal<Fortran::parser::PrintStmt>(
13098c22cb84SValentin Clement     const Fortran::parser::PrintStmt &) {
13108c22cb84SValentin Clement   return false;
13118c22cb84SValentin Clement }
13128c22cb84SValentin Clement 
13138c22cb84SValentin Clement /// If the variable `var` is an array or of a KIND other than the default
13148c22cb84SValentin Clement /// (normally 1), then a descriptor is required by the runtime IO API. This
13158c22cb84SValentin Clement /// condition holds even in F77 sources.
getVariableBufferRequiredDescriptor(Fortran::lower::AbstractConverter & converter,const Fortran::parser::Variable & var,Fortran::lower::StatementContext & stmtCtx)13168c22cb84SValentin Clement static llvm::Optional<fir::ExtendedValue> getVariableBufferRequiredDescriptor(
13178c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter,
13188c22cb84SValentin Clement     const Fortran::parser::Variable &var,
13198c22cb84SValentin Clement     Fortran::lower::StatementContext &stmtCtx) {
13208c22cb84SValentin Clement   fir::ExtendedValue varBox =
13218c22cb84SValentin Clement       converter.genExprAddr(var.typedExpr->v.value(), stmtCtx);
13228c22cb84SValentin Clement   fir::KindTy defCharKind = converter.getKindMap().defaultCharacterKind();
13238c22cb84SValentin Clement   mlir::Value varAddr = fir::getBase(varBox);
13248c22cb84SValentin Clement   if (fir::factory::CharacterExprHelper::getCharacterOrSequenceKind(
13258c22cb84SValentin Clement           varAddr.getType()) != defCharKind)
13268c22cb84SValentin Clement     return varBox;
13278c22cb84SValentin Clement   if (fir::factory::CharacterExprHelper::isArray(varAddr.getType()))
13288c22cb84SValentin Clement     return varBox;
13298c22cb84SValentin Clement   return llvm::None;
13308c22cb84SValentin Clement }
13318c22cb84SValentin Clement 
13328c22cb84SValentin Clement template <typename A>
13338c22cb84SValentin Clement static llvm::Optional<fir::ExtendedValue>
maybeGetInternalIODescriptor(Fortran::lower::AbstractConverter & converter,const A & stmt,Fortran::lower::StatementContext & stmtCtx)13348c22cb84SValentin Clement maybeGetInternalIODescriptor(Fortran::lower::AbstractConverter &converter,
13358c22cb84SValentin Clement                              const A &stmt,
13368c22cb84SValentin Clement                              Fortran::lower::StatementContext &stmtCtx) {
13378c22cb84SValentin Clement   if (stmt.iounit.has_value())
13388c22cb84SValentin Clement     if (auto *var = std::get_if<Fortran::parser::Variable>(&stmt.iounit->u))
13398c22cb84SValentin Clement       return getVariableBufferRequiredDescriptor(converter, *var, stmtCtx);
13408c22cb84SValentin Clement   if (auto *unit = getIOControl<Fortran::parser::IoUnit>(stmt))
13418c22cb84SValentin Clement     if (auto *var = std::get_if<Fortran::parser::Variable>(&unit->u))
13428c22cb84SValentin Clement       return getVariableBufferRequiredDescriptor(converter, *var, stmtCtx);
13438c22cb84SValentin Clement   return llvm::None;
13448c22cb84SValentin Clement }
13458c22cb84SValentin Clement template <>
13468c22cb84SValentin Clement inline llvm::Optional<fir::ExtendedValue>
maybeGetInternalIODescriptor(Fortran::lower::AbstractConverter &,const Fortran::parser::PrintStmt &,Fortran::lower::StatementContext &)13478c22cb84SValentin Clement maybeGetInternalIODescriptor<Fortran::parser::PrintStmt>(
13488c22cb84SValentin Clement     Fortran::lower::AbstractConverter &, const Fortran::parser::PrintStmt &,
13498c22cb84SValentin Clement     Fortran::lower::StatementContext &) {
13508c22cb84SValentin Clement   return llvm::None;
13518c22cb84SValentin Clement }
13528c22cb84SValentin Clement 
13538c22cb84SValentin Clement template <typename A>
isDataTransferAsynchronous(mlir::Location loc,const A & stmt)13548c22cb84SValentin Clement static bool isDataTransferAsynchronous(mlir::Location loc, const A &stmt) {
13558c22cb84SValentin Clement   if (auto *asynch =
13568c22cb84SValentin Clement           getIOControl<Fortran::parser::IoControlSpec::Asynchronous>(stmt)) {
13578c22cb84SValentin Clement     // FIXME: should contain a string of YES or NO
13588c22cb84SValentin Clement     TODO(loc, "asynchronous transfers not implemented in runtime");
13598c22cb84SValentin Clement   }
13608c22cb84SValentin Clement   return false;
13618c22cb84SValentin Clement }
13628c22cb84SValentin Clement template <>
isDataTransferAsynchronous(mlir::Location,const Fortran::parser::PrintStmt &)13638c22cb84SValentin Clement bool isDataTransferAsynchronous<Fortran::parser::PrintStmt>(
13648c22cb84SValentin Clement     mlir::Location, const Fortran::parser::PrintStmt &) {
13658c22cb84SValentin Clement   return false;
13668c22cb84SValentin Clement }
13678c22cb84SValentin Clement 
13688c22cb84SValentin Clement template <typename A>
isDataTransferNamelist(const A & stmt)13698c22cb84SValentin Clement static bool isDataTransferNamelist(const A &stmt) {
13708c22cb84SValentin Clement   if (stmt.format)
13718c22cb84SValentin Clement     return formatIsActuallyNamelist(*stmt.format);
13728c22cb84SValentin Clement   return hasIOControl<Fortran::parser::Name>(stmt);
13738c22cb84SValentin Clement }
13748c22cb84SValentin Clement template <>
isDataTransferNamelist(const Fortran::parser::PrintStmt &)13758c22cb84SValentin Clement constexpr bool isDataTransferNamelist<Fortran::parser::PrintStmt>(
13768c22cb84SValentin Clement     const Fortran::parser::PrintStmt &) {
13778c22cb84SValentin Clement   return false;
13788c22cb84SValentin Clement }
13798c22cb84SValentin Clement 
13808c22cb84SValentin Clement /// Lowers a format statment that uses an assigned variable label reference as
13818c22cb84SValentin Clement /// a select operation to allow for run-time selection of the format statement.
13828c22cb84SValentin Clement static std::tuple<mlir::Value, mlir::Value, mlir::Value>
lowerReferenceAsStringSelect(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::lower::SomeExpr & expr,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)13838c22cb84SValentin Clement lowerReferenceAsStringSelect(Fortran::lower::AbstractConverter &converter,
13848c22cb84SValentin Clement                              mlir::Location loc,
13858c22cb84SValentin Clement                              const Fortran::lower::SomeExpr &expr,
13868c22cb84SValentin Clement                              mlir::Type strTy, mlir::Type lenTy,
13878c22cb84SValentin Clement                              Fortran::lower::StatementContext &stmtCtx) {
13888c22cb84SValentin Clement   // Possible optimization TODO: Instead of inlining a selectOp every time there
13898c22cb84SValentin Clement   // is a variable reference to a format statement, a function with the selectOp
13908c22cb84SValentin Clement   // could be generated to reduce code size. It is not clear if such an
13918c22cb84SValentin Clement   // optimization would be deployed very often or improve the object code
13928c22cb84SValentin Clement   // beyond, say, what GVN/GCM might produce.
13938c22cb84SValentin Clement 
13948c22cb84SValentin Clement   // Create the requisite blocks to inline a selectOp.
13958c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
13968c22cb84SValentin Clement   mlir::Block *startBlock = builder.getBlock();
13978c22cb84SValentin Clement   mlir::Block *endBlock = startBlock->splitBlock(builder.getInsertionPoint());
13988c22cb84SValentin Clement   mlir::Block *block = startBlock->splitBlock(builder.getInsertionPoint());
13998c22cb84SValentin Clement   builder.setInsertionPointToEnd(block);
14008c22cb84SValentin Clement 
14018c22cb84SValentin Clement   llvm::SmallVector<int64_t> indexList;
14028c22cb84SValentin Clement   llvm::SmallVector<mlir::Block *> blockList;
14038c22cb84SValentin Clement 
14048c22cb84SValentin Clement   auto symbol = GetLastSymbol(&expr);
14058c22cb84SValentin Clement   Fortran::lower::pft::LabelSet labels;
14068c22cb84SValentin Clement   [[maybe_unused]] auto foundLabelSet =
14078c22cb84SValentin Clement       converter.lookupLabelSet(*symbol, labels);
14088c22cb84SValentin Clement   assert(foundLabelSet && "Label not found in map");
14098c22cb84SValentin Clement 
14108c22cb84SValentin Clement   for (auto label : labels) {
14118c22cb84SValentin Clement     indexList.push_back(label);
14128c22cb84SValentin Clement     auto *eval = converter.lookupLabel(label);
14138c22cb84SValentin Clement     assert(eval && "Label is missing from the table");
14148c22cb84SValentin Clement 
14158c22cb84SValentin Clement     llvm::StringRef text = toStringRef(eval->position);
14168c22cb84SValentin Clement     mlir::Value stringRef;
14178c22cb84SValentin Clement     mlir::Value stringLen;
14188c22cb84SValentin Clement     if (eval->isA<Fortran::parser::FormatStmt>()) {
14198c22cb84SValentin Clement       assert(text.find('(') != llvm::StringRef::npos &&
14208c22cb84SValentin Clement              "FORMAT is unexpectedly ill-formed");
14218c22cb84SValentin Clement       // This is a format statement, so extract the spec from the text.
14228c22cb84SValentin Clement       std::tuple<mlir::Value, mlir::Value, mlir::Value> stringLit =
14238c22cb84SValentin Clement           lowerSourceTextAsStringLit(converter, loc, text, strTy, lenTy);
14248c22cb84SValentin Clement       stringRef = std::get<0>(stringLit);
14258c22cb84SValentin Clement       stringLen = std::get<1>(stringLit);
14268c22cb84SValentin Clement     } else {
14278c22cb84SValentin Clement       // This is not a format statement, so use null.
14288c22cb84SValentin Clement       stringRef = builder.createConvert(
14298c22cb84SValentin Clement           loc, strTy,
14308c22cb84SValentin Clement           builder.createIntegerConstant(loc, builder.getIndexType(), 0));
14318c22cb84SValentin Clement       stringLen = builder.createIntegerConstant(loc, lenTy, 0);
14328c22cb84SValentin Clement     }
14338c22cb84SValentin Clement 
14348c22cb84SValentin Clement     // Pass the format string reference and the string length out of the select
14358c22cb84SValentin Clement     // statement.
14368c22cb84SValentin Clement     llvm::SmallVector<mlir::Value> args = {stringRef, stringLen};
14378c22cb84SValentin Clement     builder.create<mlir::cf::BranchOp>(loc, endBlock, args);
14388c22cb84SValentin Clement 
14398c22cb84SValentin Clement     // Add block to the list of cases and make a new one.
14408c22cb84SValentin Clement     blockList.push_back(block);
14418c22cb84SValentin Clement     block = block->splitBlock(builder.getInsertionPoint());
14428c22cb84SValentin Clement     builder.setInsertionPointToEnd(block);
14438c22cb84SValentin Clement   }
14448c22cb84SValentin Clement 
14458c22cb84SValentin Clement   // Create the unit case which should result in an error.
14468c22cb84SValentin Clement   auto *unitBlock = block->splitBlock(builder.getInsertionPoint());
14478c22cb84SValentin Clement   builder.setInsertionPointToEnd(unitBlock);
14488c22cb84SValentin Clement 
14498c22cb84SValentin Clement   // Crash the program.
14508c22cb84SValentin Clement   builder.create<fir::UnreachableOp>(loc);
14518c22cb84SValentin Clement 
14528c22cb84SValentin Clement   // Add unit case to the select statement.
14538c22cb84SValentin Clement   blockList.push_back(unitBlock);
14548c22cb84SValentin Clement 
14558c22cb84SValentin Clement   // Lower the selectOp.
14568c22cb84SValentin Clement   builder.setInsertionPointToEnd(startBlock);
14571bffc753SEric Schweitz   auto label = fir::getBase(converter.genExprValue(loc, &expr, stmtCtx));
14588c22cb84SValentin Clement   builder.create<fir::SelectOp>(loc, label, indexList, blockList);
14598c22cb84SValentin Clement 
14608c22cb84SValentin Clement   builder.setInsertionPointToEnd(endBlock);
14618c22cb84SValentin Clement   endBlock->addArgument(strTy, loc);
14628c22cb84SValentin Clement   endBlock->addArgument(lenTy, loc);
14638c22cb84SValentin Clement 
14648c22cb84SValentin Clement   // Handle and return the string reference and length selected by the selectOp.
14658c22cb84SValentin Clement   auto buff = endBlock->getArgument(0);
14668c22cb84SValentin Clement   auto len = endBlock->getArgument(1);
14678c22cb84SValentin Clement 
14688c22cb84SValentin Clement   return {buff, len, mlir::Value{}};
14698c22cb84SValentin Clement }
14708c22cb84SValentin Clement 
14718c22cb84SValentin Clement /// Generate a reference to a format string.  There are four cases - a format
14728c22cb84SValentin Clement /// statement label, a character format expression, an integer that holds the
14738c22cb84SValentin Clement /// label of a format statement, and the * case.  The first three are done here.
14748c22cb84SValentin Clement /// The * case is done elsewhere.
14758c22cb84SValentin Clement static std::tuple<mlir::Value, mlir::Value, mlir::Value>
genFormat(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::parser::Format & format,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)14768c22cb84SValentin Clement genFormat(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
14778c22cb84SValentin Clement           const Fortran::parser::Format &format, mlir::Type strTy,
14788c22cb84SValentin Clement           mlir::Type lenTy, Fortran::lower::StatementContext &stmtCtx) {
14798c22cb84SValentin Clement   if (const auto *label = std::get_if<Fortran::parser::Label>(&format.u)) {
14808c22cb84SValentin Clement     // format statement label
14818c22cb84SValentin Clement     auto eval = converter.lookupLabel(*label);
14828c22cb84SValentin Clement     assert(eval && "FORMAT not found in PROCEDURE");
14838c22cb84SValentin Clement     return lowerSourceTextAsStringLit(
14848c22cb84SValentin Clement         converter, loc, toStringRef(eval->position), strTy, lenTy);
14858c22cb84SValentin Clement   }
14868c22cb84SValentin Clement   const auto *pExpr = std::get_if<Fortran::parser::Expr>(&format.u);
14878c22cb84SValentin Clement   assert(pExpr && "missing format expression");
14888c22cb84SValentin Clement   auto e = Fortran::semantics::GetExpr(*pExpr);
14898c22cb84SValentin Clement   if (Fortran::semantics::ExprHasTypeCategory(
14908c22cb84SValentin Clement           *e, Fortran::common::TypeCategory::Character))
14918c22cb84SValentin Clement     // character expression
14928c22cb84SValentin Clement     return lowerStringLit(converter, loc, stmtCtx, *pExpr, strTy, lenTy);
14938c22cb84SValentin Clement 
14948c22cb84SValentin Clement   if (Fortran::semantics::ExprHasTypeCategory(
14958c22cb84SValentin Clement           *e, Fortran::common::TypeCategory::Integer) &&
14968c22cb84SValentin Clement       e->Rank() == 0 && Fortran::evaluate::UnwrapWholeSymbolDataRef(*e)) {
14978c22cb84SValentin Clement     // Treat as a scalar integer variable containing an ASSIGN label.
14988c22cb84SValentin Clement     return lowerReferenceAsStringSelect(converter, loc, *e, strTy, lenTy,
14998c22cb84SValentin Clement                                         stmtCtx);
15008c22cb84SValentin Clement   }
15018c22cb84SValentin Clement 
15028c22cb84SValentin Clement   // Legacy extension: it is possible that `*e` is not a scalar INTEGER
15038c22cb84SValentin Clement   // variable containing a label value. The output appears to be the source text
15048c22cb84SValentin Clement   // that initialized the variable? Needs more investigatation.
15058c22cb84SValentin Clement   TODO(loc, "io-control-spec contains a reference to a non-integer, "
15068c22cb84SValentin Clement             "non-scalar, or non-variable");
15078c22cb84SValentin Clement }
15088c22cb84SValentin Clement 
15098c22cb84SValentin Clement template <typename A>
15108c22cb84SValentin Clement std::tuple<mlir::Value, mlir::Value, mlir::Value>
getFormat(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)15118c22cb84SValentin Clement getFormat(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
15128c22cb84SValentin Clement           const A &stmt, mlir::Type strTy, mlir::Type lenTy,
15138c22cb84SValentin Clement           Fortran ::lower::StatementContext &stmtCtx) {
15148c22cb84SValentin Clement   if (stmt.format && !formatIsActuallyNamelist(*stmt.format))
15158c22cb84SValentin Clement     return genFormat(converter, loc, *stmt.format, strTy, lenTy, stmtCtx);
15168c22cb84SValentin Clement   return genFormat(converter, loc, *getIOControl<Fortran::parser::Format>(stmt),
15178c22cb84SValentin Clement                    strTy, lenTy, stmtCtx);
15188c22cb84SValentin Clement }
15198c22cb84SValentin Clement template <>
15208c22cb84SValentin Clement std::tuple<mlir::Value, mlir::Value, mlir::Value>
getFormat(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::parser::PrintStmt & stmt,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)15218c22cb84SValentin Clement getFormat<Fortran::parser::PrintStmt>(
15228c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
15238c22cb84SValentin Clement     const Fortran::parser::PrintStmt &stmt, mlir::Type strTy, mlir::Type lenTy,
15248c22cb84SValentin Clement     Fortran::lower::StatementContext &stmtCtx) {
15258c22cb84SValentin Clement   return genFormat(converter, loc, std::get<Fortran::parser::Format>(stmt.t),
15268c22cb84SValentin Clement                    strTy, lenTy, stmtCtx);
15278c22cb84SValentin Clement }
15288c22cb84SValentin Clement 
15298c22cb84SValentin Clement /// Get a buffer for an internal file data transfer.
15308c22cb84SValentin Clement template <typename A>
15318c22cb84SValentin Clement std::tuple<mlir::Value, mlir::Value>
getBuffer(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::Type strTy,mlir::Type lenTy,Fortran::lower::StatementContext & stmtCtx)15328c22cb84SValentin Clement getBuffer(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
15338c22cb84SValentin Clement           const A &stmt, mlir::Type strTy, mlir::Type lenTy,
15348c22cb84SValentin Clement           Fortran::lower::StatementContext &stmtCtx) {
15358c22cb84SValentin Clement   const Fortran::parser::IoUnit *iounit =
15368c22cb84SValentin Clement       stmt.iounit ? &*stmt.iounit : getIOControl<Fortran::parser::IoUnit>(stmt);
15378c22cb84SValentin Clement   if (iounit)
15388c22cb84SValentin Clement     if (auto *var = std::get_if<Fortran::parser::Variable>(&iounit->u))
15398c22cb84SValentin Clement       if (auto *expr = Fortran::semantics::GetExpr(*var))
15408c22cb84SValentin Clement         return genBuffer(converter, loc, *expr, strTy, lenTy, stmtCtx);
154139377d52SValentin Clement   llvm::report_fatal_error("failed to get IoUnit expr");
15428c22cb84SValentin Clement }
15438c22cb84SValentin Clement 
genIOUnitNumber(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::lower::SomeExpr * iounit,mlir::Type ty,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)15441bffc753SEric Schweitz static mlir::Value genIOUnitNumber(Fortran::lower::AbstractConverter &converter,
15458c22cb84SValentin Clement                                    mlir::Location loc,
15461bffc753SEric Schweitz                                    const Fortran::lower::SomeExpr *iounit,
15471bffc753SEric Schweitz                                    mlir::Type ty, ConditionSpecInfo &csi,
15488c22cb84SValentin Clement                                    Fortran::lower::StatementContext &stmtCtx) {
15498c22cb84SValentin Clement   auto &builder = converter.getFirOpBuilder();
15501bffc753SEric Schweitz   auto rawUnit = fir::getBase(converter.genExprValue(loc, iounit, stmtCtx));
15511bffc753SEric Schweitz   unsigned rawUnitWidth =
15521bffc753SEric Schweitz       rawUnit.getType().cast<mlir::IntegerType>().getWidth();
15531bffc753SEric Schweitz   unsigned runtimeArgWidth = ty.cast<mlir::IntegerType>().getWidth();
15541bffc753SEric Schweitz   // The IO runtime supports `int` unit numbers, if the unit number may
15551bffc753SEric Schweitz   // overflow when passed to the IO runtime, check that the unit number is
15561bffc753SEric Schweitz   // in range before calling the BeginXXX.
15571bffc753SEric Schweitz   if (rawUnitWidth > runtimeArgWidth) {
15581c7889caSValentin Clement     mlir::func::FuncOp check =
15591bffc753SEric Schweitz         rawUnitWidth <= 64
15601bffc753SEric Schweitz             ? getIORuntimeFunc<mkIOKey(CheckUnitNumberInRange64)>(loc, builder)
15611bffc753SEric Schweitz             : getIORuntimeFunc<mkIOKey(CheckUnitNumberInRange128)>(loc,
15621bffc753SEric Schweitz                                                                    builder);
15631bffc753SEric Schweitz     mlir::FunctionType funcTy = check.getFunctionType();
15641bffc753SEric Schweitz     llvm::SmallVector<mlir::Value> args;
15651bffc753SEric Schweitz     args.push_back(builder.createConvert(loc, funcTy.getInput(0), rawUnit));
15661bffc753SEric Schweitz     args.push_back(builder.createBool(loc, csi.hasErrorConditionSpec()));
15671bffc753SEric Schweitz     if (csi.ioMsg) {
15681bffc753SEric Schweitz       args.push_back(builder.createConvert(loc, funcTy.getInput(2),
15691bffc753SEric Schweitz                                            fir::getBase(*csi.ioMsg)));
15701bffc753SEric Schweitz       args.push_back(builder.createConvert(loc, funcTy.getInput(3),
15711bffc753SEric Schweitz                                            fir::getLen(*csi.ioMsg)));
15721bffc753SEric Schweitz     } else {
15731bffc753SEric Schweitz       args.push_back(builder.createNullConstant(loc, funcTy.getInput(2)));
15741bffc753SEric Schweitz       args.push_back(
15751bffc753SEric Schweitz           fir::factory::createZeroValue(builder, loc, funcTy.getInput(3)));
15768c22cb84SValentin Clement     }
15771bffc753SEric Schweitz     mlir::Value file = locToFilename(converter, loc, funcTy.getInput(4));
15781bffc753SEric Schweitz     mlir::Value line = locToLineNo(converter, loc, funcTy.getInput(5));
15791bffc753SEric Schweitz     args.push_back(file);
15801bffc753SEric Schweitz     args.push_back(line);
15811bffc753SEric Schweitz     auto checkCall = builder.create<fir::CallOp>(loc, check, args);
15821bffc753SEric Schweitz     if (csi.hasErrorConditionSpec()) {
15831bffc753SEric Schweitz       mlir::Value iostat = checkCall.getResult(0);
15841bffc753SEric Schweitz       mlir::Type iostatTy = iostat.getType();
15851bffc753SEric Schweitz       mlir::Value zero = fir::factory::createZeroValue(builder, loc, iostatTy);
15861bffc753SEric Schweitz       mlir::Value unitIsOK = builder.create<mlir::arith::CmpIOp>(
15871bffc753SEric Schweitz           loc, mlir::arith::CmpIPredicate::eq, iostat, zero);
15881bffc753SEric Schweitz       auto ifOp = builder.create<fir::IfOp>(loc, iostatTy, unitIsOK,
15891bffc753SEric Schweitz                                             /*withElseRegion=*/true);
15901bffc753SEric Schweitz       builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
15911bffc753SEric Schweitz       builder.create<fir::ResultOp>(loc, iostat);
15921bffc753SEric Schweitz       builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
15931bffc753SEric Schweitz       stmtCtx.pushScope();
15941bffc753SEric Schweitz       csi.bigUnitIfOp = ifOp;
15951bffc753SEric Schweitz     }
15961bffc753SEric Schweitz   }
15971bffc753SEric Schweitz   return builder.createConvert(loc, ty, rawUnit);
15981bffc753SEric Schweitz }
15991bffc753SEric Schweitz 
genIOUnit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::parser::IoUnit * iounit,mlir::Type ty,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)16001bffc753SEric Schweitz static mlir::Value genIOUnit(Fortran::lower::AbstractConverter &converter,
16011bffc753SEric Schweitz                              mlir::Location loc,
16021bffc753SEric Schweitz                              const Fortran::parser::IoUnit *iounit,
16031bffc753SEric Schweitz                              mlir::Type ty, ConditionSpecInfo &csi,
16041bffc753SEric Schweitz                              Fortran::lower::StatementContext &stmtCtx) {
16051bffc753SEric Schweitz   auto &builder = converter.getFirOpBuilder();
16061bffc753SEric Schweitz   if (iounit)
16071bffc753SEric Schweitz     if (auto *e = std::get_if<Fortran::parser::FileUnitNumber>(&iounit->u))
16081bffc753SEric Schweitz       return genIOUnitNumber(converter, loc, Fortran::semantics::GetExpr(*e),
16091bffc753SEric Schweitz                              ty, csi, stmtCtx);
16108c22cb84SValentin Clement   return builder.create<mlir::arith::ConstantOp>(
16118c22cb84SValentin Clement       loc, builder.getIntegerAttr(ty, Fortran::runtime::io::DefaultUnit));
16128c22cb84SValentin Clement }
16138c22cb84SValentin Clement 
16148c22cb84SValentin Clement template <typename A>
getIOUnit(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::Type ty,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)16151bffc753SEric Schweitz static mlir::Value getIOUnit(Fortran::lower::AbstractConverter &converter,
16168c22cb84SValentin Clement                              mlir::Location loc, const A &stmt, mlir::Type ty,
16171bffc753SEric Schweitz                              ConditionSpecInfo &csi,
16188c22cb84SValentin Clement                              Fortran::lower::StatementContext &stmtCtx) {
16191bffc753SEric Schweitz   const Fortran::parser::IoUnit *iounit =
16201bffc753SEric Schweitz       stmt.iounit ? &*stmt.iounit : getIOControl<Fortran::parser::IoUnit>(stmt);
16211bffc753SEric Schweitz   return genIOUnit(converter, loc, iounit, ty, csi, stmtCtx);
16228c22cb84SValentin Clement }
16238c22cb84SValentin Clement //===----------------------------------------------------------------------===//
1624db48f7b2SValentin Clement // Generators for each IO statement type.
1625db48f7b2SValentin Clement //===----------------------------------------------------------------------===//
1626db48f7b2SValentin Clement 
1627db48f7b2SValentin Clement template <typename K, typename S>
genBasicIOStmt(Fortran::lower::AbstractConverter & converter,const S & stmt)1628db48f7b2SValentin Clement static mlir::Value genBasicIOStmt(Fortran::lower::AbstractConverter &converter,
1629db48f7b2SValentin Clement                                   const S &stmt) {
1630db48f7b2SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1631db48f7b2SValentin Clement   Fortran::lower::StatementContext stmtCtx;
1632db48f7b2SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
16331bffc753SEric Schweitz   ConditionSpecInfo csi = lowerErrorSpec(converter, loc, stmt.v);
16341c7889caSValentin Clement   mlir::func::FuncOp beginFunc = getIORuntimeFunc<K>(loc, builder);
16354a3460a7SRiver Riddle   mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
16361bffc753SEric Schweitz   mlir::Value unit = genIOUnitNumber(
16371bffc753SEric Schweitz       converter, loc, getExpr<Fortran::parser::FileUnitNumber>(stmt),
16381bffc753SEric Schweitz       beginFuncTy.getInput(0), csi, stmtCtx);
1639db48f7b2SValentin Clement   mlir::Value un = builder.createConvert(loc, beginFuncTy.getInput(0), unit);
1640db48f7b2SValentin Clement   mlir::Value file = locToFilename(converter, loc, beginFuncTy.getInput(1));
1641db48f7b2SValentin Clement   mlir::Value line = locToLineNo(converter, loc, beginFuncTy.getInput(2));
1642db48f7b2SValentin Clement   auto call = builder.create<fir::CallOp>(loc, beginFunc,
1643db48f7b2SValentin Clement                                           mlir::ValueRange{un, file, line});
1644db48f7b2SValentin Clement   mlir::Value cookie = call.getResult(0);
1645db48f7b2SValentin Clement   genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1646db48f7b2SValentin Clement   mlir::Value ok;
1647db48f7b2SValentin Clement   auto insertPt = builder.saveInsertionPoint();
1648db48f7b2SValentin Clement   threadSpecs(converter, loc, cookie, stmt.v, csi.hasErrorConditionSpec(), ok);
1649db48f7b2SValentin Clement   builder.restoreInsertionPoint(insertPt);
1650db48f7b2SValentin Clement   return genEndIO(converter, converter.getCurrentLocation(), cookie, csi,
1651db48f7b2SValentin Clement                   stmtCtx);
1652db48f7b2SValentin Clement }
1653db48f7b2SValentin Clement 
genBackspaceStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::BackspaceStmt & stmt)165446f46a37SValentin Clement mlir::Value Fortran::lower::genBackspaceStatement(
165546f46a37SValentin Clement     Fortran::lower::AbstractConverter &converter,
165646f46a37SValentin Clement     const Fortran::parser::BackspaceStmt &stmt) {
165746f46a37SValentin Clement   return genBasicIOStmt<mkIOKey(BeginBackspace)>(converter, stmt);
165846f46a37SValentin Clement }
165946f46a37SValentin Clement 
genEndfileStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::EndfileStmt & stmt)166046f46a37SValentin Clement mlir::Value Fortran::lower::genEndfileStatement(
166146f46a37SValentin Clement     Fortran::lower::AbstractConverter &converter,
166246f46a37SValentin Clement     const Fortran::parser::EndfileStmt &stmt) {
166346f46a37SValentin Clement   return genBasicIOStmt<mkIOKey(BeginEndfile)>(converter, stmt);
166446f46a37SValentin Clement }
166546f46a37SValentin Clement 
166646f46a37SValentin Clement mlir::Value
genFlushStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::FlushStmt & stmt)166746f46a37SValentin Clement Fortran::lower::genFlushStatement(Fortran::lower::AbstractConverter &converter,
166846f46a37SValentin Clement                                   const Fortran::parser::FlushStmt &stmt) {
166946f46a37SValentin Clement   return genBasicIOStmt<mkIOKey(BeginFlush)>(converter, stmt);
167046f46a37SValentin Clement }
167146f46a37SValentin Clement 
167246f46a37SValentin Clement mlir::Value
genRewindStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::RewindStmt & stmt)167346f46a37SValentin Clement Fortran::lower::genRewindStatement(Fortran::lower::AbstractConverter &converter,
167446f46a37SValentin Clement                                    const Fortran::parser::RewindStmt &stmt) {
167546f46a37SValentin Clement   return genBasicIOStmt<mkIOKey(BeginRewind)>(converter, stmt);
167646f46a37SValentin Clement }
167746f46a37SValentin Clement 
16789aeb7f03SValentin Clement static mlir::Value
genNewunitSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const std::list<Fortran::parser::ConnectSpec> & specList)16799aeb7f03SValentin Clement genNewunitSpec(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
16809aeb7f03SValentin Clement                mlir::Value cookie,
16819aeb7f03SValentin Clement                const std::list<Fortran::parser::ConnectSpec> &specList) {
16829aeb7f03SValentin Clement   for (const auto &spec : specList)
16839aeb7f03SValentin Clement     if (auto *newunit =
16849aeb7f03SValentin Clement             std::get_if<Fortran::parser::ConnectSpec::Newunit>(&spec.u)) {
16859aeb7f03SValentin Clement       Fortran::lower::StatementContext stmtCtx;
16869aeb7f03SValentin Clement       fir::FirOpBuilder &builder = converter.getFirOpBuilder();
16871c7889caSValentin Clement       mlir::func::FuncOp ioFunc =
16881c7889caSValentin Clement           getIORuntimeFunc<mkIOKey(GetNewUnit)>(loc, builder);
16894a3460a7SRiver Riddle       mlir::FunctionType ioFuncTy = ioFunc.getFunctionType();
16909aeb7f03SValentin Clement       const auto *var = Fortran::semantics::GetExpr(newunit->v);
16919aeb7f03SValentin Clement       mlir::Value addr = builder.createConvert(
16929aeb7f03SValentin Clement           loc, ioFuncTy.getInput(1),
16931bffc753SEric Schweitz           fir::getBase(converter.genExprAddr(loc, var, stmtCtx)));
16949aeb7f03SValentin Clement       auto kind = builder.createIntegerConstant(loc, ioFuncTy.getInput(2),
16959aeb7f03SValentin Clement                                                 var->GetType().value().kind());
16969aeb7f03SValentin Clement       llvm::SmallVector<mlir::Value> ioArgs = {cookie, addr, kind};
16979aeb7f03SValentin Clement       return builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
16989aeb7f03SValentin Clement     }
16999aeb7f03SValentin Clement   llvm_unreachable("missing Newunit spec");
17009aeb7f03SValentin Clement }
17019aeb7f03SValentin Clement 
1702db48f7b2SValentin Clement mlir::Value
genOpenStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::OpenStmt & stmt)1703db48f7b2SValentin Clement Fortran::lower::genOpenStatement(Fortran::lower::AbstractConverter &converter,
1704db48f7b2SValentin Clement                                  const Fortran::parser::OpenStmt &stmt) {
1705db48f7b2SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
1706db48f7b2SValentin Clement   Fortran::lower::StatementContext stmtCtx;
170758ceae95SRiver Riddle   mlir::func::FuncOp beginFunc;
1708db48f7b2SValentin Clement   llvm::SmallVector<mlir::Value> beginArgs;
1709db48f7b2SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
17101bffc753SEric Schweitz   ConditionSpecInfo csi = lowerErrorSpec(converter, loc, stmt.v);
17119aeb7f03SValentin Clement   bool hasNewunitSpec = false;
17129aeb7f03SValentin Clement   if (hasSpec<Fortran::parser::FileUnitNumber>(stmt)) {
1713db48f7b2SValentin Clement     beginFunc = getIORuntimeFunc<mkIOKey(BeginOpenUnit)>(loc, builder);
17144a3460a7SRiver Riddle     mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
17151bffc753SEric Schweitz     mlir::Value unit = genIOUnitNumber(
17161bffc753SEric Schweitz         converter, loc, getExpr<Fortran::parser::FileUnitNumber>(stmt),
17171bffc753SEric Schweitz         beginFuncTy.getInput(0), csi, stmtCtx);
17181bffc753SEric Schweitz     beginArgs.push_back(unit);
1719db48f7b2SValentin Clement     beginArgs.push_back(locToFilename(converter, loc, beginFuncTy.getInput(1)));
1720db48f7b2SValentin Clement     beginArgs.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(2)));
1721db48f7b2SValentin Clement   } else {
17229aeb7f03SValentin Clement     hasNewunitSpec = hasSpec<Fortran::parser::ConnectSpec::Newunit>(stmt);
17239aeb7f03SValentin Clement     assert(hasNewunitSpec && "missing unit specifier");
1724db48f7b2SValentin Clement     beginFunc = getIORuntimeFunc<mkIOKey(BeginOpenNewUnit)>(loc, builder);
17254a3460a7SRiver Riddle     mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
1726db48f7b2SValentin Clement     beginArgs.push_back(locToFilename(converter, loc, beginFuncTy.getInput(0)));
1727db48f7b2SValentin Clement     beginArgs.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(1)));
1728db48f7b2SValentin Clement   }
1729db48f7b2SValentin Clement   auto cookie =
1730db48f7b2SValentin Clement       builder.create<fir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
1731db48f7b2SValentin Clement   genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
1732db48f7b2SValentin Clement   mlir::Value ok;
1733db48f7b2SValentin Clement   auto insertPt = builder.saveInsertionPoint();
1734db48f7b2SValentin Clement   threadSpecs(converter, loc, cookie, stmt.v, csi.hasErrorConditionSpec(), ok);
17359aeb7f03SValentin Clement   if (hasNewunitSpec)
17369aeb7f03SValentin Clement     genNewunitSpec(converter, loc, cookie, stmt.v);
1737db48f7b2SValentin Clement   builder.restoreInsertionPoint(insertPt);
1738db48f7b2SValentin Clement   return genEndIO(converter, loc, cookie, csi, stmtCtx);
1739db48f7b2SValentin Clement }
1740db48f7b2SValentin Clement 
1741db48f7b2SValentin Clement mlir::Value
genCloseStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::CloseStmt & stmt)1742db48f7b2SValentin Clement Fortran::lower::genCloseStatement(Fortran::lower::AbstractConverter &converter,
1743db48f7b2SValentin Clement                                   const Fortran::parser::CloseStmt &stmt) {
1744db48f7b2SValentin Clement   return genBasicIOStmt<mkIOKey(BeginClose)>(converter, stmt);
1745db48f7b2SValentin Clement }
1746db48f7b2SValentin Clement 
174746f46a37SValentin Clement mlir::Value
genWaitStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::WaitStmt & stmt)174846f46a37SValentin Clement Fortran::lower::genWaitStatement(Fortran::lower::AbstractConverter &converter,
174946f46a37SValentin Clement                                  const Fortran::parser::WaitStmt &stmt) {
175046f46a37SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
175146f46a37SValentin Clement   Fortran::lower::StatementContext stmtCtx;
175246f46a37SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
17531bffc753SEric Schweitz   ConditionSpecInfo csi = lowerErrorSpec(converter, loc, stmt.v);
17549aeb7f03SValentin Clement   bool hasId = hasSpec<Fortran::parser::IdExpr>(stmt);
17551c7889caSValentin Clement   mlir::func::FuncOp beginFunc =
17561c7889caSValentin Clement       hasId ? getIORuntimeFunc<mkIOKey(BeginWait)>(loc, builder)
175746f46a37SValentin Clement             : getIORuntimeFunc<mkIOKey(BeginWaitAll)>(loc, builder);
17584a3460a7SRiver Riddle   mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
17591bffc753SEric Schweitz   mlir::Value unit = genIOUnitNumber(
17601bffc753SEric Schweitz       converter, loc, getExpr<Fortran::parser::FileUnitNumber>(stmt),
17611bffc753SEric Schweitz       beginFuncTy.getInput(0), csi, stmtCtx);
17621bffc753SEric Schweitz   llvm::SmallVector<mlir::Value> args{unit};
176346f46a37SValentin Clement   if (hasId) {
176446f46a37SValentin Clement     mlir::Value id = fir::getBase(converter.genExprValue(
17651bffc753SEric Schweitz         loc, getExpr<Fortran::parser::IdExpr>(stmt), stmtCtx));
176646f46a37SValentin Clement     args.push_back(builder.createConvert(loc, beginFuncTy.getInput(1), id));
1767*df417c37SValentin Clement     args.push_back(locToFilename(converter, loc, beginFuncTy.getInput(2)));
1768*df417c37SValentin Clement     args.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(3)));
1769*df417c37SValentin Clement   } else {
1770*df417c37SValentin Clement     args.push_back(locToFilename(converter, loc, beginFuncTy.getInput(1)));
1771*df417c37SValentin Clement     args.push_back(locToLineNo(converter, loc, beginFuncTy.getInput(2)));
177246f46a37SValentin Clement   }
177346f46a37SValentin Clement   auto cookie = builder.create<fir::CallOp>(loc, beginFunc, args).getResult(0);
177446f46a37SValentin Clement   genConditionHandlerCall(converter, loc, cookie, stmt.v, csi);
177546f46a37SValentin Clement   return genEndIO(converter, converter.getCurrentLocation(), cookie, csi,
177646f46a37SValentin Clement                   stmtCtx);
177746f46a37SValentin Clement }
177846f46a37SValentin Clement 
1779db48f7b2SValentin Clement //===----------------------------------------------------------------------===//
17808c22cb84SValentin Clement // Data transfer statements.
17818c22cb84SValentin Clement //
17828c22cb84SValentin Clement // There are several dimensions to the API with regard to data transfer
17838c22cb84SValentin Clement // statements that need to be considered.
17848c22cb84SValentin Clement //
17858c22cb84SValentin Clement //   - input (READ) vs. output (WRITE, PRINT)
17868c22cb84SValentin Clement //   - unformatted vs. formatted vs. list vs. namelist
17878c22cb84SValentin Clement //   - synchronous vs. asynchronous
17888c22cb84SValentin Clement //   - external vs. internal
17898c22cb84SValentin Clement //===----------------------------------------------------------------------===//
17908c22cb84SValentin Clement 
17918c22cb84SValentin Clement // Get the begin data transfer IO function to call for the given values.
17928c22cb84SValentin Clement template <bool isInput>
179358ceae95SRiver Riddle mlir::func::FuncOp
getBeginDataTransferFunc(mlir::Location loc,fir::FirOpBuilder & builder,bool isFormatted,bool isListOrNml,bool isInternal,bool isInternalWithDesc,bool isAsync)17948c22cb84SValentin Clement getBeginDataTransferFunc(mlir::Location loc, fir::FirOpBuilder &builder,
17958c22cb84SValentin Clement                          bool isFormatted, bool isListOrNml, bool isInternal,
17968c22cb84SValentin Clement                          bool isInternalWithDesc, bool isAsync) {
17978c22cb84SValentin Clement   if constexpr (isInput) {
17988c22cb84SValentin Clement     if (isFormatted || isListOrNml) {
17998c22cb84SValentin Clement       if (isInternal) {
18008c22cb84SValentin Clement         if (isInternalWithDesc) {
18018c22cb84SValentin Clement           if (isListOrNml)
18028c22cb84SValentin Clement             return getIORuntimeFunc<mkIOKey(BeginInternalArrayListInput)>(
18038c22cb84SValentin Clement                 loc, builder);
18048c22cb84SValentin Clement           return getIORuntimeFunc<mkIOKey(BeginInternalArrayFormattedInput)>(
18058c22cb84SValentin Clement               loc, builder);
18068c22cb84SValentin Clement         }
18078c22cb84SValentin Clement         if (isListOrNml)
18088c22cb84SValentin Clement           return getIORuntimeFunc<mkIOKey(BeginInternalListInput)>(loc,
18098c22cb84SValentin Clement                                                                    builder);
18108c22cb84SValentin Clement         return getIORuntimeFunc<mkIOKey(BeginInternalFormattedInput)>(loc,
18118c22cb84SValentin Clement                                                                       builder);
18128c22cb84SValentin Clement       }
18138c22cb84SValentin Clement       if (isListOrNml)
18148c22cb84SValentin Clement         return getIORuntimeFunc<mkIOKey(BeginExternalListInput)>(loc, builder);
18158c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(BeginExternalFormattedInput)>(loc,
18168c22cb84SValentin Clement                                                                     builder);
18178c22cb84SValentin Clement     }
18188c22cb84SValentin Clement     return getIORuntimeFunc<mkIOKey(BeginUnformattedInput)>(loc, builder);
18198c22cb84SValentin Clement   } else {
18208c22cb84SValentin Clement     if (isFormatted || isListOrNml) {
18218c22cb84SValentin Clement       if (isInternal) {
18228c22cb84SValentin Clement         if (isInternalWithDesc) {
18238c22cb84SValentin Clement           if (isListOrNml)
18248c22cb84SValentin Clement             return getIORuntimeFunc<mkIOKey(BeginInternalArrayListOutput)>(
18258c22cb84SValentin Clement                 loc, builder);
18268c22cb84SValentin Clement           return getIORuntimeFunc<mkIOKey(BeginInternalArrayFormattedOutput)>(
18278c22cb84SValentin Clement               loc, builder);
18288c22cb84SValentin Clement         }
18298c22cb84SValentin Clement         if (isListOrNml)
18308c22cb84SValentin Clement           return getIORuntimeFunc<mkIOKey(BeginInternalListOutput)>(loc,
18318c22cb84SValentin Clement                                                                     builder);
18328c22cb84SValentin Clement         return getIORuntimeFunc<mkIOKey(BeginInternalFormattedOutput)>(loc,
18338c22cb84SValentin Clement                                                                        builder);
18348c22cb84SValentin Clement       }
18358c22cb84SValentin Clement       if (isListOrNml)
18368c22cb84SValentin Clement         return getIORuntimeFunc<mkIOKey(BeginExternalListOutput)>(loc, builder);
18378c22cb84SValentin Clement       return getIORuntimeFunc<mkIOKey(BeginExternalFormattedOutput)>(loc,
18388c22cb84SValentin Clement                                                                      builder);
18398c22cb84SValentin Clement     }
18408c22cb84SValentin Clement     return getIORuntimeFunc<mkIOKey(BeginUnformattedOutput)>(loc, builder);
18418c22cb84SValentin Clement   }
18428c22cb84SValentin Clement }
18438c22cb84SValentin Clement 
18448c22cb84SValentin Clement /// Generate the arguments of a begin data transfer statement call.
18458c22cb84SValentin Clement template <bool hasIOCtrl, typename A>
genBeginDataTransferCallArgs(llvm::SmallVectorImpl<mlir::Value> & ioArgs,Fortran::lower::AbstractConverter & converter,mlir::Location loc,const A & stmt,mlir::FunctionType ioFuncTy,bool isFormatted,bool isListOrNml,bool isInternal,bool isAsync,const llvm::Optional<fir::ExtendedValue> & descRef,ConditionSpecInfo & csi,Fortran::lower::StatementContext & stmtCtx)18468c22cb84SValentin Clement void genBeginDataTransferCallArgs(
18478c22cb84SValentin Clement     llvm::SmallVectorImpl<mlir::Value> &ioArgs,
18488c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
18498c22cb84SValentin Clement     const A &stmt, mlir::FunctionType ioFuncTy, bool isFormatted,
18508c22cb84SValentin Clement     bool isListOrNml, [[maybe_unused]] bool isInternal,
18518c22cb84SValentin Clement     [[maybe_unused]] bool isAsync,
18521bffc753SEric Schweitz     const llvm::Optional<fir::ExtendedValue> &descRef, ConditionSpecInfo &csi,
18538c22cb84SValentin Clement     Fortran::lower::StatementContext &stmtCtx) {
18548c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
18558c22cb84SValentin Clement   auto maybeGetFormatArgs = [&]() {
18568c22cb84SValentin Clement     if (!isFormatted || isListOrNml)
18578c22cb84SValentin Clement       return;
18588c22cb84SValentin Clement     auto pair =
18598c22cb84SValentin Clement         getFormat(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
18608c22cb84SValentin Clement                   ioFuncTy.getInput(ioArgs.size() + 1), stmtCtx);
18618c22cb84SValentin Clement     ioArgs.push_back(std::get<0>(pair)); // format character string
18628c22cb84SValentin Clement     ioArgs.push_back(std::get<1>(pair)); // format length
18638c22cb84SValentin Clement   };
18648c22cb84SValentin Clement   if constexpr (hasIOCtrl) { // READ or WRITE
18658c22cb84SValentin Clement     if (isInternal) {
18668c22cb84SValentin Clement       // descriptor or scalar variable; maybe explicit format; scratch area
18675413bf1bSKazu Hirata       if (descRef) {
18688c22cb84SValentin Clement         mlir::Value desc = builder.createBox(loc, *descRef);
18698c22cb84SValentin Clement         ioArgs.push_back(
18708c22cb84SValentin Clement             builder.createConvert(loc, ioFuncTy.getInput(ioArgs.size()), desc));
18718c22cb84SValentin Clement       } else {
18728c22cb84SValentin Clement         std::tuple<mlir::Value, mlir::Value> pair =
18738c22cb84SValentin Clement             getBuffer(converter, loc, stmt, ioFuncTy.getInput(ioArgs.size()),
18748c22cb84SValentin Clement                       ioFuncTy.getInput(ioArgs.size() + 1), stmtCtx);
18758c22cb84SValentin Clement         ioArgs.push_back(std::get<0>(pair)); // scalar character variable
18768c22cb84SValentin Clement         ioArgs.push_back(std::get<1>(pair)); // character length
18778c22cb84SValentin Clement       }
18788c22cb84SValentin Clement       maybeGetFormatArgs();
18798c22cb84SValentin Clement       ioArgs.push_back( // internal scratch area buffer
18808c22cb84SValentin Clement           getDefaultScratch(builder, loc, ioFuncTy.getInput(ioArgs.size())));
18818c22cb84SValentin Clement       ioArgs.push_back( // buffer length
18828c22cb84SValentin Clement           getDefaultScratchLen(builder, loc, ioFuncTy.getInput(ioArgs.size())));
18838c22cb84SValentin Clement     } else { // external IO - maybe explicit format; unit
1884deb62f5aSPeter Klausler       if (isAsync)
1885deb62f5aSPeter Klausler         TODO(loc, "asynchronous");
18868c22cb84SValentin Clement       maybeGetFormatArgs();
18878c22cb84SValentin Clement       ioArgs.push_back(getIOUnit(converter, loc, stmt,
18881bffc753SEric Schweitz                                  ioFuncTy.getInput(ioArgs.size()), csi,
18891bffc753SEric Schweitz                                  stmtCtx));
18908c22cb84SValentin Clement     }
18918c22cb84SValentin Clement   } else { // PRINT - maybe explicit format; default unit
18928c22cb84SValentin Clement     maybeGetFormatArgs();
18938c22cb84SValentin Clement     ioArgs.push_back(builder.create<mlir::arith::ConstantOp>(
18948c22cb84SValentin Clement         loc, builder.getIntegerAttr(ioFuncTy.getInput(ioArgs.size()),
18958c22cb84SValentin Clement                                     Fortran::runtime::io::DefaultUnit)));
18968c22cb84SValentin Clement   }
18978c22cb84SValentin Clement   // File name and line number are always the last two arguments.
18988c22cb84SValentin Clement   ioArgs.push_back(
18998c22cb84SValentin Clement       locToFilename(converter, loc, ioFuncTy.getInput(ioArgs.size())));
19008c22cb84SValentin Clement   ioArgs.push_back(
19018c22cb84SValentin Clement       locToLineNo(converter, loc, ioFuncTy.getInput(ioArgs.size())));
19028c22cb84SValentin Clement }
19038c22cb84SValentin Clement 
19048c22cb84SValentin Clement template <bool isInput, bool hasIOCtrl = true, typename A>
19058c22cb84SValentin Clement static mlir::Value
genDataTransferStmt(Fortran::lower::AbstractConverter & converter,const A & stmt)19068c22cb84SValentin Clement genDataTransferStmt(Fortran::lower::AbstractConverter &converter,
19078c22cb84SValentin Clement                     const A &stmt) {
19088c22cb84SValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
19098c22cb84SValentin Clement   Fortran::lower::StatementContext stmtCtx;
19108c22cb84SValentin Clement   mlir::Location loc = converter.getCurrentLocation();
19118c22cb84SValentin Clement   const bool isFormatted = isDataTransferFormatted(stmt);
19128c22cb84SValentin Clement   const bool isList = isFormatted ? isDataTransferList(stmt) : false;
19138c22cb84SValentin Clement   const bool isInternal = isDataTransferInternal(stmt);
19148c22cb84SValentin Clement   llvm::Optional<fir::ExtendedValue> descRef =
19158c22cb84SValentin Clement       isInternal ? maybeGetInternalIODescriptor(converter, stmt, stmtCtx)
19168c22cb84SValentin Clement                  : llvm::None;
19170916d96dSKazu Hirata   const bool isInternalWithDesc = descRef.has_value();
19188c22cb84SValentin Clement   const bool isAsync = isDataTransferAsynchronous(loc, stmt);
19198c22cb84SValentin Clement   const bool isNml = isDataTransferNamelist(stmt);
19208c22cb84SValentin Clement 
19211bffc753SEric Schweitz   // Generate an EnableHandlers call and remaining specifier calls.
19221bffc753SEric Schweitz   ConditionSpecInfo csi;
19231bffc753SEric Schweitz   if constexpr (hasIOCtrl) {
19241bffc753SEric Schweitz     csi = lowerErrorSpec(converter, loc, stmt.controls);
19251bffc753SEric Schweitz   }
19261bffc753SEric Schweitz 
19278c22cb84SValentin Clement   // Generate the begin data transfer function call.
19281c7889caSValentin Clement   mlir::func::FuncOp ioFunc = getBeginDataTransferFunc<isInput>(
19291c7889caSValentin Clement       loc, builder, isFormatted, isList || isNml, isInternal,
19308c22cb84SValentin Clement       isInternalWithDesc, isAsync);
19318c22cb84SValentin Clement   llvm::SmallVector<mlir::Value> ioArgs;
19328c22cb84SValentin Clement   genBeginDataTransferCallArgs<hasIOCtrl>(
19334a3460a7SRiver Riddle       ioArgs, converter, loc, stmt, ioFunc.getFunctionType(), isFormatted,
19341bffc753SEric Schweitz       isList || isNml, isInternal, isAsync, descRef, csi, stmtCtx);
19358c22cb84SValentin Clement   mlir::Value cookie =
19368c22cb84SValentin Clement       builder.create<fir::CallOp>(loc, ioFunc, ioArgs).getResult(0);
19378c22cb84SValentin Clement 
19388c22cb84SValentin Clement   auto insertPt = builder.saveInsertionPoint();
19398c22cb84SValentin Clement   mlir::Value ok;
19408c22cb84SValentin Clement   if constexpr (hasIOCtrl) {
19418c22cb84SValentin Clement     genConditionHandlerCall(converter, loc, cookie, stmt.controls, csi);
19428c22cb84SValentin Clement     threadSpecs(converter, loc, cookie, stmt.controls,
19438c22cb84SValentin Clement                 csi.hasErrorConditionSpec(), ok);
19448c22cb84SValentin Clement   }
19458c22cb84SValentin Clement 
19468c22cb84SValentin Clement   // Generate data transfer list calls.
19478c22cb84SValentin Clement   if constexpr (isInput) { // READ
19488c22cb84SValentin Clement     if (isNml)
19498c22cb84SValentin Clement       genNamelistIO(converter, cookie,
19508c22cb84SValentin Clement                     getIORuntimeFunc<mkIOKey(InputNamelist)>(loc, builder),
19518c22cb84SValentin Clement                     *getIOControl<Fortran::parser::Name>(stmt)->symbol,
19528c22cb84SValentin Clement                     csi.hasTransferConditionSpec(), ok, stmtCtx);
19538c22cb84SValentin Clement     else
19548c22cb84SValentin Clement       genInputItemList(converter, cookie, stmt.items, isFormatted,
19551bffc753SEric Schweitz                        csi.hasTransferConditionSpec(), ok, /*inLoop=*/false);
19568c22cb84SValentin Clement   } else if constexpr (std::is_same_v<A, Fortran::parser::WriteStmt>) {
19578c22cb84SValentin Clement     if (isNml)
19588c22cb84SValentin Clement       genNamelistIO(converter, cookie,
19598c22cb84SValentin Clement                     getIORuntimeFunc<mkIOKey(OutputNamelist)>(loc, builder),
19608c22cb84SValentin Clement                     *getIOControl<Fortran::parser::Name>(stmt)->symbol,
19618c22cb84SValentin Clement                     csi.hasTransferConditionSpec(), ok, stmtCtx);
19628c22cb84SValentin Clement     else
19638c22cb84SValentin Clement       genOutputItemList(converter, cookie, stmt.items, isFormatted,
19648c22cb84SValentin Clement                         csi.hasTransferConditionSpec(), ok,
19651bffc753SEric Schweitz                         /*inLoop=*/false);
19668c22cb84SValentin Clement   } else { // PRINT
19678c22cb84SValentin Clement     genOutputItemList(converter, cookie, std::get<1>(stmt.t), isFormatted,
19688c22cb84SValentin Clement                       csi.hasTransferConditionSpec(), ok,
19691bffc753SEric Schweitz                       /*inLoop=*/false);
19708c22cb84SValentin Clement   }
19718c22cb84SValentin Clement   stmtCtx.finalize();
19728c22cb84SValentin Clement 
19738c22cb84SValentin Clement   builder.restoreInsertionPoint(insertPt);
19748c22cb84SValentin Clement   if constexpr (hasIOCtrl) {
19758c22cb84SValentin Clement     genIOReadSize(converter, loc, cookie, stmt.controls,
19768c22cb84SValentin Clement                   csi.hasErrorConditionSpec());
19778c22cb84SValentin Clement   }
19788c22cb84SValentin Clement   // Generate end statement call/s.
19798c22cb84SValentin Clement   return genEndIO(converter, loc, cookie, csi, stmtCtx);
19808c22cb84SValentin Clement }
19818c22cb84SValentin Clement 
genPrintStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::PrintStmt & stmt)19828c22cb84SValentin Clement void Fortran::lower::genPrintStatement(
19838c22cb84SValentin Clement     Fortran::lower::AbstractConverter &converter,
19848c22cb84SValentin Clement     const Fortran::parser::PrintStmt &stmt) {
19858c22cb84SValentin Clement   // PRINT does not take an io-control-spec. It only has a format specifier, so
19868c22cb84SValentin Clement   // it is a simplified case of WRITE.
19878c22cb84SValentin Clement   genDataTransferStmt</*isInput=*/false, /*ioCtrl=*/false>(converter, stmt);
19888c22cb84SValentin Clement }
19898c22cb84SValentin Clement 
19908c22cb84SValentin Clement mlir::Value
genWriteStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::WriteStmt & stmt)19918c22cb84SValentin Clement Fortran::lower::genWriteStatement(Fortran::lower::AbstractConverter &converter,
19928c22cb84SValentin Clement                                   const Fortran::parser::WriteStmt &stmt) {
19938c22cb84SValentin Clement   return genDataTransferStmt</*isInput=*/false>(converter, stmt);
19948c22cb84SValentin Clement }
19958c22cb84SValentin Clement 
19968c22cb84SValentin Clement mlir::Value
genReadStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::ReadStmt & stmt)19978c22cb84SValentin Clement Fortran::lower::genReadStatement(Fortran::lower::AbstractConverter &converter,
19988c22cb84SValentin Clement                                  const Fortran::parser::ReadStmt &stmt) {
19998c22cb84SValentin Clement   return genDataTransferStmt</*isInput=*/true>(converter, stmt);
20008c22cb84SValentin Clement }
20017e32cadaSValentin Clement 
20027e32cadaSValentin Clement /// Get the file expression from the inquire spec list. Also return if the
20037e32cadaSValentin Clement /// expression is a file name.
20047e32cadaSValentin Clement static std::pair<const Fortran::lower::SomeExpr *, bool>
getInquireFileExpr(const std::list<Fortran::parser::InquireSpec> * stmt)20057e32cadaSValentin Clement getInquireFileExpr(const std::list<Fortran::parser::InquireSpec> *stmt) {
20067e32cadaSValentin Clement   if (!stmt)
20077e32cadaSValentin Clement     return {nullptr, /*filename?=*/false};
20087e32cadaSValentin Clement   for (const Fortran::parser::InquireSpec &spec : *stmt) {
20097e32cadaSValentin Clement     if (auto *f = std::get_if<Fortran::parser::FileUnitNumber>(&spec.u))
20107e32cadaSValentin Clement       return {Fortran::semantics::GetExpr(*f), /*filename?=*/false};
20117e32cadaSValentin Clement     if (auto *f = std::get_if<Fortran::parser::FileNameExpr>(&spec.u))
20127e32cadaSValentin Clement       return {Fortran::semantics::GetExpr(*f), /*filename?=*/true};
20137e32cadaSValentin Clement   }
20147e32cadaSValentin Clement   // semantics should have already caught this condition
20157e32cadaSValentin Clement   llvm::report_fatal_error("inquire spec must have a file");
20167e32cadaSValentin Clement }
20177e32cadaSValentin Clement 
20187e32cadaSValentin Clement /// Generate calls to the four distinct INQUIRE subhandlers. An INQUIRE may
20197e32cadaSValentin Clement /// return values of type CHARACTER, INTEGER, or LOGICAL. There is one
20207e32cadaSValentin Clement /// additional special case for INQUIRE with both PENDING and ID specifiers.
20217e32cadaSValentin Clement template <typename A>
genInquireSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,mlir::Value idExpr,const A & var,Fortran::lower::StatementContext & stmtCtx)20227e32cadaSValentin Clement static mlir::Value genInquireSpec(Fortran::lower::AbstractConverter &converter,
20237e32cadaSValentin Clement                                   mlir::Location loc, mlir::Value cookie,
20247e32cadaSValentin Clement                                   mlir::Value idExpr, const A &var,
20257e32cadaSValentin Clement                                   Fortran::lower::StatementContext &stmtCtx) {
20267e32cadaSValentin Clement   // default case: do nothing
20277e32cadaSValentin Clement   return {};
20287e32cadaSValentin Clement }
20297e32cadaSValentin Clement /// Specialization for CHARACTER.
20307e32cadaSValentin Clement template <>
genInquireSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,mlir::Value idExpr,const Fortran::parser::InquireSpec::CharVar & var,Fortran::lower::StatementContext & stmtCtx)20317e32cadaSValentin Clement mlir::Value genInquireSpec<Fortran::parser::InquireSpec::CharVar>(
20327e32cadaSValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
20337e32cadaSValentin Clement     mlir::Value cookie, mlir::Value idExpr,
20347e32cadaSValentin Clement     const Fortran::parser::InquireSpec::CharVar &var,
20357e32cadaSValentin Clement     Fortran::lower::StatementContext &stmtCtx) {
20367e32cadaSValentin Clement   // IOMSG is handled with exception conditions
20377e32cadaSValentin Clement   if (std::get<Fortran::parser::InquireSpec::CharVar::Kind>(var.t) ==
20387e32cadaSValentin Clement       Fortran::parser::InquireSpec::CharVar::Kind::Iomsg)
20397e32cadaSValentin Clement     return {};
20407e32cadaSValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
20411c7889caSValentin Clement   mlir::func::FuncOp specFunc =
20421c7889caSValentin Clement       getIORuntimeFunc<mkIOKey(InquireCharacter)>(loc, builder);
20434a3460a7SRiver Riddle   mlir::FunctionType specFuncTy = specFunc.getFunctionType();
20447e32cadaSValentin Clement   const auto *varExpr = Fortran::semantics::GetExpr(
20457e32cadaSValentin Clement       std::get<Fortran::parser::ScalarDefaultCharVariable>(var.t));
20461bffc753SEric Schweitz   fir::ExtendedValue str = converter.genExprAddr(loc, varExpr, stmtCtx);
20477e32cadaSValentin Clement   llvm::SmallVector<mlir::Value> args = {
20487e32cadaSValentin Clement       builder.createConvert(loc, specFuncTy.getInput(0), cookie),
20497e32cadaSValentin Clement       builder.createIntegerConstant(
20507e32cadaSValentin Clement           loc, specFuncTy.getInput(1),
20517e32cadaSValentin Clement           Fortran::runtime::io::HashInquiryKeyword(
20527e32cadaSValentin Clement               Fortran::parser::InquireSpec::CharVar::EnumToString(
20537e32cadaSValentin Clement                   std::get<Fortran::parser::InquireSpec::CharVar::Kind>(var.t))
20547e32cadaSValentin Clement                   .c_str())),
20557e32cadaSValentin Clement       builder.createConvert(loc, specFuncTy.getInput(2), fir::getBase(str)),
20567e32cadaSValentin Clement       builder.createConvert(loc, specFuncTy.getInput(3), fir::getLen(str))};
20577e32cadaSValentin Clement   return builder.create<fir::CallOp>(loc, specFunc, args).getResult(0);
20587e32cadaSValentin Clement }
20597e32cadaSValentin Clement /// Specialization for INTEGER.
20607e32cadaSValentin Clement template <>
genInquireSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,mlir::Value idExpr,const Fortran::parser::InquireSpec::IntVar & var,Fortran::lower::StatementContext & stmtCtx)20617e32cadaSValentin Clement mlir::Value genInquireSpec<Fortran::parser::InquireSpec::IntVar>(
20627e32cadaSValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
20637e32cadaSValentin Clement     mlir::Value cookie, mlir::Value idExpr,
20647e32cadaSValentin Clement     const Fortran::parser::InquireSpec::IntVar &var,
20657e32cadaSValentin Clement     Fortran::lower::StatementContext &stmtCtx) {
20667e32cadaSValentin Clement   // IOSTAT is handled with exception conditions
20677e32cadaSValentin Clement   if (std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t) ==
20687e32cadaSValentin Clement       Fortran::parser::InquireSpec::IntVar::Kind::Iostat)
20697e32cadaSValentin Clement     return {};
20707e32cadaSValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
20711c7889caSValentin Clement   mlir::func::FuncOp specFunc =
20721c7889caSValentin Clement       getIORuntimeFunc<mkIOKey(InquireInteger64)>(loc, builder);
20734a3460a7SRiver Riddle   mlir::FunctionType specFuncTy = specFunc.getFunctionType();
20747e32cadaSValentin Clement   const auto *varExpr = Fortran::semantics::GetExpr(
20757e32cadaSValentin Clement       std::get<Fortran::parser::ScalarIntVariable>(var.t));
20761bffc753SEric Schweitz   mlir::Value addr = fir::getBase(converter.genExprAddr(loc, varExpr, stmtCtx));
20777e32cadaSValentin Clement   mlir::Type eleTy = fir::dyn_cast_ptrEleTy(addr.getType());
20787e32cadaSValentin Clement   if (!eleTy)
20797e32cadaSValentin Clement     fir::emitFatalError(loc,
20807e32cadaSValentin Clement                         "internal error: expected a memory reference type");
20819aeb7f03SValentin Clement   auto width = eleTy.cast<mlir::IntegerType>().getWidth();
20827e32cadaSValentin Clement   mlir::IndexType idxTy = builder.getIndexType();
20839aeb7f03SValentin Clement   mlir::Value kind = builder.createIntegerConstant(loc, idxTy, width / 8);
20847e32cadaSValentin Clement   llvm::SmallVector<mlir::Value> args = {
20857e32cadaSValentin Clement       builder.createConvert(loc, specFuncTy.getInput(0), cookie),
20867e32cadaSValentin Clement       builder.createIntegerConstant(
20877e32cadaSValentin Clement           loc, specFuncTy.getInput(1),
20887e32cadaSValentin Clement           Fortran::runtime::io::HashInquiryKeyword(
20897e32cadaSValentin Clement               Fortran::parser::InquireSpec::IntVar::EnumToString(
20907e32cadaSValentin Clement                   std::get<Fortran::parser::InquireSpec::IntVar::Kind>(var.t))
20917e32cadaSValentin Clement                   .c_str())),
20927e32cadaSValentin Clement       builder.createConvert(loc, specFuncTy.getInput(2), addr),
20937e32cadaSValentin Clement       builder.createConvert(loc, specFuncTy.getInput(3), kind)};
20947e32cadaSValentin Clement   return builder.create<fir::CallOp>(loc, specFunc, args).getResult(0);
20957e32cadaSValentin Clement }
20967e32cadaSValentin Clement /// Specialization for LOGICAL and (PENDING + ID).
20977e32cadaSValentin Clement template <>
genInquireSpec(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,mlir::Value idExpr,const Fortran::parser::InquireSpec::LogVar & var,Fortran::lower::StatementContext & stmtCtx)20987e32cadaSValentin Clement mlir::Value genInquireSpec<Fortran::parser::InquireSpec::LogVar>(
20997e32cadaSValentin Clement     Fortran::lower::AbstractConverter &converter, mlir::Location loc,
21007e32cadaSValentin Clement     mlir::Value cookie, mlir::Value idExpr,
21017e32cadaSValentin Clement     const Fortran::parser::InquireSpec::LogVar &var,
21027e32cadaSValentin Clement     Fortran::lower::StatementContext &stmtCtx) {
21037e32cadaSValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
21047e32cadaSValentin Clement   auto logVarKind = std::get<Fortran::parser::InquireSpec::LogVar::Kind>(var.t);
21057e32cadaSValentin Clement   bool pendId =
21067e32cadaSValentin Clement       idExpr &&
21077e32cadaSValentin Clement       logVarKind == Fortran::parser::InquireSpec::LogVar::Kind::Pending;
21081c7889caSValentin Clement   mlir::func::FuncOp specFunc =
21097e32cadaSValentin Clement       pendId ? getIORuntimeFunc<mkIOKey(InquirePendingId)>(loc, builder)
21107e32cadaSValentin Clement              : getIORuntimeFunc<mkIOKey(InquireLogical)>(loc, builder);
21114a3460a7SRiver Riddle   mlir::FunctionType specFuncTy = specFunc.getFunctionType();
21127e32cadaSValentin Clement   mlir::Value addr = fir::getBase(converter.genExprAddr(
21131bffc753SEric Schweitz       loc,
21147e32cadaSValentin Clement       Fortran::semantics::GetExpr(
21157e32cadaSValentin Clement           std::get<Fortran::parser::Scalar<
21167e32cadaSValentin Clement               Fortran::parser::Logical<Fortran::parser::Variable>>>(var.t)),
21171bffc753SEric Schweitz       stmtCtx));
21187e32cadaSValentin Clement   llvm::SmallVector<mlir::Value> args = {
21197e32cadaSValentin Clement       builder.createConvert(loc, specFuncTy.getInput(0), cookie)};
21207e32cadaSValentin Clement   if (pendId)
21217e32cadaSValentin Clement     args.push_back(builder.createConvert(loc, specFuncTy.getInput(1), idExpr));
21227e32cadaSValentin Clement   else
21237e32cadaSValentin Clement     args.push_back(builder.createIntegerConstant(
21247e32cadaSValentin Clement         loc, specFuncTy.getInput(1),
21257e32cadaSValentin Clement         Fortran::runtime::io::HashInquiryKeyword(
21267e32cadaSValentin Clement             Fortran::parser::InquireSpec::LogVar::EnumToString(logVarKind)
21277e32cadaSValentin Clement                 .c_str())));
21287e32cadaSValentin Clement   args.push_back(builder.createConvert(loc, specFuncTy.getInput(2), addr));
21299aeb7f03SValentin Clement   auto call = builder.create<fir::CallOp>(loc, specFunc, args);
21309aeb7f03SValentin Clement   boolRefToLogical(loc, builder, addr);
21319aeb7f03SValentin Clement   return call.getResult(0);
21327e32cadaSValentin Clement }
21337e32cadaSValentin Clement 
21347e32cadaSValentin Clement /// If there is an IdExpr in the list of inquire-specs, then lower it and return
21357e32cadaSValentin Clement /// the resulting Value. Otherwise, return null.
21367e32cadaSValentin Clement static mlir::Value
lowerIdExpr(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const std::list<Fortran::parser::InquireSpec> & ispecs,Fortran::lower::StatementContext & stmtCtx)21377e32cadaSValentin Clement lowerIdExpr(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
21387e32cadaSValentin Clement             const std::list<Fortran::parser::InquireSpec> &ispecs,
21397e32cadaSValentin Clement             Fortran::lower::StatementContext &stmtCtx) {
21407e32cadaSValentin Clement   for (const Fortran::parser::InquireSpec &spec : ispecs)
21417e32cadaSValentin Clement     if (mlir::Value v = std::visit(
21427e32cadaSValentin Clement             Fortran::common::visitors{
21437e32cadaSValentin Clement                 [&](const Fortran::parser::IdExpr &idExpr) {
21447e32cadaSValentin Clement                   return fir::getBase(converter.genExprValue(
21451bffc753SEric Schweitz                       loc, Fortran::semantics::GetExpr(idExpr), stmtCtx));
21467e32cadaSValentin Clement                 },
21477e32cadaSValentin Clement                 [](const auto &) { return mlir::Value{}; }},
21487e32cadaSValentin Clement             spec.u))
21497e32cadaSValentin Clement       return v;
21507e32cadaSValentin Clement   return {};
21517e32cadaSValentin Clement }
21527e32cadaSValentin Clement 
21537e32cadaSValentin Clement /// For each inquire-spec, build the appropriate call, threading the cookie.
threadInquire(Fortran::lower::AbstractConverter & converter,mlir::Location loc,mlir::Value cookie,const std::list<Fortran::parser::InquireSpec> & ispecs,bool checkResult,mlir::Value & ok,Fortran::lower::StatementContext & stmtCtx)21547e32cadaSValentin Clement static void threadInquire(Fortran::lower::AbstractConverter &converter,
21557e32cadaSValentin Clement                           mlir::Location loc, mlir::Value cookie,
21567e32cadaSValentin Clement                           const std::list<Fortran::parser::InquireSpec> &ispecs,
21577e32cadaSValentin Clement                           bool checkResult, mlir::Value &ok,
21587e32cadaSValentin Clement                           Fortran::lower::StatementContext &stmtCtx) {
21597e32cadaSValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
21607e32cadaSValentin Clement   mlir::Value idExpr = lowerIdExpr(converter, loc, ispecs, stmtCtx);
21617e32cadaSValentin Clement   for (const Fortran::parser::InquireSpec &spec : ispecs) {
21627e32cadaSValentin Clement     makeNextConditionalOn(builder, loc, checkResult, ok);
21637e32cadaSValentin Clement     ok = std::visit(Fortran::common::visitors{[&](const auto &x) {
21647e32cadaSValentin Clement                       return genInquireSpec(converter, loc, cookie, idExpr, x,
21657e32cadaSValentin Clement                                             stmtCtx);
21667e32cadaSValentin Clement                     }},
21677e32cadaSValentin Clement                     spec.u);
21687e32cadaSValentin Clement   }
21697e32cadaSValentin Clement }
21707e32cadaSValentin Clement 
genInquireStatement(Fortran::lower::AbstractConverter & converter,const Fortran::parser::InquireStmt & stmt)21717e32cadaSValentin Clement mlir::Value Fortran::lower::genInquireStatement(
21727e32cadaSValentin Clement     Fortran::lower::AbstractConverter &converter,
21737e32cadaSValentin Clement     const Fortran::parser::InquireStmt &stmt) {
21747e32cadaSValentin Clement   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
21757e32cadaSValentin Clement   Fortran::lower::StatementContext stmtCtx;
21767e32cadaSValentin Clement   mlir::Location loc = converter.getCurrentLocation();
217758ceae95SRiver Riddle   mlir::func::FuncOp beginFunc;
21787e32cadaSValentin Clement   llvm::SmallVector<mlir::Value> beginArgs;
21797e32cadaSValentin Clement   const auto *list =
21807e32cadaSValentin Clement       std::get_if<std::list<Fortran::parser::InquireSpec>>(&stmt.u);
21817e32cadaSValentin Clement   auto exprPair = getInquireFileExpr(list);
21827e32cadaSValentin Clement   auto inquireFileUnit = [&]() -> bool {
21837e32cadaSValentin Clement     return exprPair.first && !exprPair.second;
21847e32cadaSValentin Clement   };
21857e32cadaSValentin Clement   auto inquireFileName = [&]() -> bool {
21867e32cadaSValentin Clement     return exprPair.first && exprPair.second;
21877e32cadaSValentin Clement   };
21887e32cadaSValentin Clement 
21891bffc753SEric Schweitz   ConditionSpecInfo csi =
21901bffc753SEric Schweitz       list ? lowerErrorSpec(converter, loc, *list) : ConditionSpecInfo{};
21911bffc753SEric Schweitz 
21927e32cadaSValentin Clement   // Make one of three BeginInquire calls.
21937e32cadaSValentin Clement   if (inquireFileUnit()) {
21947e32cadaSValentin Clement     // Inquire by unit -- [UNIT=]file-unit-number.
21957e32cadaSValentin Clement     beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireUnit)>(loc, builder);
21964a3460a7SRiver Riddle     mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
21971bffc753SEric Schweitz     mlir::Value unit = genIOUnitNumber(converter, loc, exprPair.first,
21981bffc753SEric Schweitz                                        beginFuncTy.getInput(0), csi, stmtCtx);
21991bffc753SEric Schweitz     beginArgs = {unit, locToFilename(converter, loc, beginFuncTy.getInput(1)),
22007e32cadaSValentin Clement                  locToLineNo(converter, loc, beginFuncTy.getInput(2))};
22017e32cadaSValentin Clement   } else if (inquireFileName()) {
22027e32cadaSValentin Clement     // Inquire by file -- FILE=file-name-expr.
22037e32cadaSValentin Clement     beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireFile)>(loc, builder);
22044a3460a7SRiver Riddle     mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
22057e32cadaSValentin Clement     fir::ExtendedValue file =
22061bffc753SEric Schweitz         converter.genExprAddr(loc, exprPair.first, stmtCtx);
22077e32cadaSValentin Clement     beginArgs = {
22087e32cadaSValentin Clement         builder.createConvert(loc, beginFuncTy.getInput(0), fir::getBase(file)),
22097e32cadaSValentin Clement         builder.createConvert(loc, beginFuncTy.getInput(1), fir::getLen(file)),
22107e32cadaSValentin Clement         locToFilename(converter, loc, beginFuncTy.getInput(2)),
22117e32cadaSValentin Clement         locToLineNo(converter, loc, beginFuncTy.getInput(3))};
22127e32cadaSValentin Clement   } else {
22137e32cadaSValentin Clement     // Inquire by output list -- IOLENGTH=scalar-int-variable.
22147e32cadaSValentin Clement     const auto *ioLength =
22157e32cadaSValentin Clement         std::get_if<Fortran::parser::InquireStmt::Iolength>(&stmt.u);
22167e32cadaSValentin Clement     assert(ioLength && "must have an IOLENGTH specifier");
22177e32cadaSValentin Clement     beginFunc = getIORuntimeFunc<mkIOKey(BeginInquireIoLength)>(loc, builder);
22184a3460a7SRiver Riddle     mlir::FunctionType beginFuncTy = beginFunc.getFunctionType();
22197e32cadaSValentin Clement     beginArgs = {locToFilename(converter, loc, beginFuncTy.getInput(0)),
22207e32cadaSValentin Clement                  locToLineNo(converter, loc, beginFuncTy.getInput(1))};
22217e32cadaSValentin Clement     auto cookie =
22227e32cadaSValentin Clement         builder.create<fir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
22237e32cadaSValentin Clement     mlir::Value ok;
22247e32cadaSValentin Clement     genOutputItemList(
22257e32cadaSValentin Clement         converter, cookie,
22267e32cadaSValentin Clement         std::get<std::list<Fortran::parser::OutputItem>>(ioLength->t),
22271bffc753SEric Schweitz         /*isFormatted=*/false, /*checkResult=*/false, ok, /*inLoop=*/false);
22287e32cadaSValentin Clement     auto *ioLengthVar = Fortran::semantics::GetExpr(
22297e32cadaSValentin Clement         std::get<Fortran::parser::ScalarIntVariable>(ioLength->t));
22307e32cadaSValentin Clement     mlir::Value ioLengthVarAddr =
22311bffc753SEric Schweitz         fir::getBase(converter.genExprAddr(loc, ioLengthVar, stmtCtx));
22327e32cadaSValentin Clement     llvm::SmallVector<mlir::Value> args = {cookie};
22337e32cadaSValentin Clement     mlir::Value length =
22347e32cadaSValentin Clement         builder
22357e32cadaSValentin Clement             .create<fir::CallOp>(
22367e32cadaSValentin Clement                 loc, getIORuntimeFunc<mkIOKey(GetIoLength)>(loc, builder), args)
22377e32cadaSValentin Clement             .getResult(0);
22387e32cadaSValentin Clement     mlir::Value length1 =
22397e32cadaSValentin Clement         builder.createConvert(loc, converter.genType(*ioLengthVar), length);
22407e32cadaSValentin Clement     builder.create<fir::StoreOp>(loc, length1, ioLengthVarAddr);
22417e32cadaSValentin Clement     return genEndIO(converter, loc, cookie, csi, stmtCtx);
22427e32cadaSValentin Clement   }
22437e32cadaSValentin Clement 
22447e32cadaSValentin Clement   // Common handling for inquire by unit or file.
22457e32cadaSValentin Clement   assert(list && "inquire-spec list must be present");
22467e32cadaSValentin Clement   auto cookie =
22477e32cadaSValentin Clement       builder.create<fir::CallOp>(loc, beginFunc, beginArgs).getResult(0);
22487e32cadaSValentin Clement   genConditionHandlerCall(converter, loc, cookie, *list, csi);
22497e32cadaSValentin Clement   // Handle remaining arguments in specifier list.
22507e32cadaSValentin Clement   mlir::Value ok;
22517e32cadaSValentin Clement   auto insertPt = builder.saveInsertionPoint();
22527e32cadaSValentin Clement   threadInquire(converter, loc, cookie, *list, csi.hasErrorConditionSpec(), ok,
22537e32cadaSValentin Clement                 stmtCtx);
22547e32cadaSValentin Clement   builder.restoreInsertionPoint(insertPt);
22557e32cadaSValentin Clement   // Generate end statement call.
22567e32cadaSValentin Clement   return genEndIO(converter, loc, cookie, csi, stmtCtx);
22577e32cadaSValentin Clement }
2258