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