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