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