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