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