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