1 //===-- Bridge.cpp -- bridge to lower to MLIR -----------------------------===// 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/Bridge.h" 14 #include "flang/Lower/Allocatable.h" 15 #include "flang/Lower/CallInterface.h" 16 #include "flang/Lower/Coarray.h" 17 #include "flang/Lower/ConvertExpr.h" 18 #include "flang/Lower/ConvertType.h" 19 #include "flang/Lower/ConvertVariable.h" 20 #include "flang/Lower/HostAssociations.h" 21 #include "flang/Lower/IO.h" 22 #include "flang/Lower/IterationSpace.h" 23 #include "flang/Lower/Mangler.h" 24 #include "flang/Lower/OpenACC.h" 25 #include "flang/Lower/OpenMP.h" 26 #include "flang/Lower/PFTBuilder.h" 27 #include "flang/Lower/Runtime.h" 28 #include "flang/Lower/StatementContext.h" 29 #include "flang/Lower/Support/Utils.h" 30 #include "flang/Lower/Todo.h" 31 #include "flang/Optimizer/Builder/BoxValue.h" 32 #include "flang/Optimizer/Builder/Character.h" 33 #include "flang/Optimizer/Builder/FIRBuilder.h" 34 #include "flang/Optimizer/Builder/Runtime/Character.h" 35 #include "flang/Optimizer/Builder/Runtime/Ragged.h" 36 #include "flang/Optimizer/Dialect/FIRAttr.h" 37 #include "flang/Optimizer/Dialect/FIRDialect.h" 38 #include "flang/Optimizer/Dialect/FIROps.h" 39 #include "flang/Optimizer/Support/FIRContext.h" 40 #include "flang/Optimizer/Support/FatalError.h" 41 #include "flang/Optimizer/Support/InternalNames.h" 42 #include "flang/Optimizer/Transforms/Passes.h" 43 #include "flang/Parser/parse-tree.h" 44 #include "flang/Runtime/iostat.h" 45 #include "flang/Semantics/tools.h" 46 #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" 47 #include "mlir/IR/PatternMatch.h" 48 #include "mlir/Parser/Parser.h" 49 #include "mlir/Transforms/RegionUtils.h" 50 #include "llvm/Support/CommandLine.h" 51 #include "llvm/Support/Debug.h" 52 #include "llvm/Support/ErrorHandling.h" 53 54 #define DEBUG_TYPE "flang-lower-bridge" 55 56 static llvm::cl::opt<bool> dumpBeforeFir( 57 "fdebug-dump-pre-fir", llvm::cl::init(false), 58 llvm::cl::desc("dump the Pre-FIR tree prior to FIR generation")); 59 60 static llvm::cl::opt<bool> forceLoopToExecuteOnce( 61 "always-execute-loop-body", llvm::cl::init(false), 62 llvm::cl::desc("force the body of a loop to execute at least once")); 63 64 namespace { 65 /// Helper class to generate the runtime type info global data. This data 66 /// is required to describe the derived type to the runtime so that it can 67 /// operate over it. It must be ensured this data will be generated for every 68 /// derived type lowered in the current translated unit. However, this data 69 /// cannot be generated before FuncOp have been created for functions since the 70 /// initializers may take their address (e.g for type bound procedures). This 71 /// class allows registering all the required runtime type info while it is not 72 /// possible to create globals, and to generate this data after function 73 /// lowering. 74 class RuntimeTypeInfoConverter { 75 /// Store the location and symbols of derived type info to be generated. 76 /// The location of the derived type instantiation is also stored because 77 /// runtime type descriptor symbol are compiler generated and cannot be mapped 78 /// to user code on their own. 79 struct TypeInfoSymbol { 80 Fortran::semantics::SymbolRef symbol; 81 mlir::Location loc; 82 }; 83 84 public: 85 void registerTypeInfoSymbol(Fortran::lower::AbstractConverter &converter, 86 mlir::Location loc, 87 Fortran::semantics::SymbolRef typeInfoSym) { 88 if (seen.contains(typeInfoSym)) 89 return; 90 seen.insert(typeInfoSym); 91 if (!skipRegistration) { 92 registeredTypeInfoSymbols.emplace_back(TypeInfoSymbol{typeInfoSym, loc}); 93 return; 94 } 95 // Once the registration is closed, symbols cannot be added to the 96 // registeredTypeInfoSymbols list because it may be iterated over. 97 // However, after registration is closed, it is safe to directly generate 98 // the globals because all FuncOps whose addresses may be required by the 99 // initializers have been generated. 100 Fortran::lower::createRuntimeTypeInfoGlobal(converter, loc, 101 typeInfoSym.get()); 102 } 103 104 void createTypeInfoGlobals(Fortran::lower::AbstractConverter &converter) { 105 skipRegistration = true; 106 for (const TypeInfoSymbol &info : registeredTypeInfoSymbols) 107 Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.loc, 108 info.symbol.get()); 109 registeredTypeInfoSymbols.clear(); 110 } 111 112 private: 113 /// Store the runtime type descriptors that will be required for the 114 /// derived type that have been converted to FIR derived types. 115 llvm::SmallVector<TypeInfoSymbol> registeredTypeInfoSymbols; 116 /// Create derived type runtime info global immediately without storing the 117 /// symbol in registeredTypeInfoSymbols. 118 bool skipRegistration = false; 119 /// Track symbols symbols processed during and after the registration 120 /// to avoid infinite loops between type conversions and global variable 121 /// creation. 122 llvm::SmallSetVector<Fortran::semantics::SymbolRef, 64> seen; 123 }; 124 125 } // namespace 126 127 //===----------------------------------------------------------------------===// 128 // FirConverter 129 //===----------------------------------------------------------------------===// 130 131 namespace { 132 133 /// Traverse the pre-FIR tree (PFT) to generate the FIR dialect of MLIR. 134 class FirConverter : public Fortran::lower::AbstractConverter { 135 public: 136 explicit FirConverter(Fortran::lower::LoweringBridge &bridge) 137 : bridge{bridge}, foldingContext{bridge.createFoldingContext()} {} 138 virtual ~FirConverter() = default; 139 140 /// Convert the PFT to FIR. 141 void run(Fortran::lower::pft::Program &pft) { 142 // Preliminary translation pass. 143 // - Declare all functions that have definitions so that definition 144 // signatures prevail over call site signatures. 145 // - Define module variables and OpenMP/OpenACC declarative construct so 146 // that they are available before lowering any function that may use 147 // them. 148 // - Translate block data programs so that common block definitions with 149 // data initializations take precedence over other definitions. 150 for (Fortran::lower::pft::Program::Units &u : pft.getUnits()) { 151 std::visit( 152 Fortran::common::visitors{ 153 [&](Fortran::lower::pft::FunctionLikeUnit &f) { 154 declareFunction(f); 155 }, 156 [&](Fortran::lower::pft::ModuleLikeUnit &m) { 157 lowerModuleDeclScope(m); 158 for (Fortran::lower::pft::FunctionLikeUnit &f : 159 m.nestedFunctions) 160 declareFunction(f); 161 }, 162 [&](Fortran::lower::pft::BlockDataUnit &b) { lowerBlockData(b); }, 163 [&](Fortran::lower::pft::CompilerDirectiveUnit &d) {}, 164 }, 165 u); 166 } 167 168 // Primary translation pass. 169 for (Fortran::lower::pft::Program::Units &u : pft.getUnits()) { 170 std::visit( 171 Fortran::common::visitors{ 172 [&](Fortran::lower::pft::FunctionLikeUnit &f) { lowerFunc(f); }, 173 [&](Fortran::lower::pft::ModuleLikeUnit &m) { lowerMod(m); }, 174 [&](Fortran::lower::pft::BlockDataUnit &b) {}, 175 [&](Fortran::lower::pft::CompilerDirectiveUnit &d) { 176 setCurrentPosition( 177 d.get<Fortran::parser::CompilerDirective>().source); 178 mlir::emitWarning(toLocation(), 179 "ignoring all compiler directives"); 180 }, 181 }, 182 u); 183 } 184 185 /// Once all the code has been translated, create runtime type info 186 /// global data structure for the derived types that have been 187 /// processed. 188 createGlobalOutsideOfFunctionLowering( 189 [&]() { runtimeTypeInfoConverter.createTypeInfoGlobals(*this); }); 190 } 191 192 /// Declare a function. 193 void declareFunction(Fortran::lower::pft::FunctionLikeUnit &funit) { 194 setCurrentPosition(funit.getStartingSourceLoc()); 195 for (int entryIndex = 0, last = funit.entryPointList.size(); 196 entryIndex < last; ++entryIndex) { 197 funit.setActiveEntry(entryIndex); 198 // Calling CalleeInterface ctor will build a declaration 199 // mlir::func::FuncOp with no other side effects. 200 // TODO: when doing some compiler profiling on real apps, it may be worth 201 // to check it's better to save the CalleeInterface instead of recomputing 202 // it later when lowering the body. CalleeInterface ctor should be linear 203 // with the number of arguments, so it is not awful to do it that way for 204 // now, but the linear coefficient might be non negligible. Until 205 // measured, stick to the solution that impacts the code less. 206 Fortran::lower::CalleeInterface{funit, *this}; 207 } 208 funit.setActiveEntry(0); 209 210 // Compute the set of host associated entities from the nested functions. 211 llvm::SetVector<const Fortran::semantics::Symbol *> escapeHost; 212 for (Fortran::lower::pft::FunctionLikeUnit &f : funit.nestedFunctions) 213 collectHostAssociatedVariables(f, escapeHost); 214 funit.setHostAssociatedSymbols(escapeHost); 215 216 // Declare internal procedures 217 for (Fortran::lower::pft::FunctionLikeUnit &f : funit.nestedFunctions) 218 declareFunction(f); 219 } 220 221 /// Collects the canonical list of all host associated symbols. These bindings 222 /// must be aggregated into a tuple which can then be added to each of the 223 /// internal procedure declarations and passed at each call site. 224 void collectHostAssociatedVariables( 225 Fortran::lower::pft::FunctionLikeUnit &funit, 226 llvm::SetVector<const Fortran::semantics::Symbol *> &escapees) { 227 const Fortran::semantics::Scope *internalScope = 228 funit.getSubprogramSymbol().scope(); 229 assert(internalScope && "internal procedures symbol must create a scope"); 230 auto addToListIfEscapee = [&](const Fortran::semantics::Symbol &sym) { 231 const Fortran::semantics::Symbol &ultimate = sym.GetUltimate(); 232 const auto *namelistDetails = 233 ultimate.detailsIf<Fortran::semantics::NamelistDetails>(); 234 if (ultimate.has<Fortran::semantics::ObjectEntityDetails>() || 235 Fortran::semantics::IsProcedurePointer(ultimate) || 236 Fortran::semantics::IsDummy(sym) || namelistDetails) { 237 const Fortran::semantics::Scope &ultimateScope = ultimate.owner(); 238 if (ultimateScope.kind() == 239 Fortran::semantics::Scope::Kind::MainProgram || 240 ultimateScope.kind() == Fortran::semantics::Scope::Kind::Subprogram) 241 if (ultimateScope != *internalScope && 242 ultimateScope.Contains(*internalScope)) { 243 if (namelistDetails) { 244 // So far, namelist symbols are processed on the fly in IO and 245 // the related namelist data structure is not added to the symbol 246 // map, so it cannot be passed to the internal procedures. 247 // Instead, all the symbols of the host namelist used in the 248 // internal procedure must be considered as host associated so 249 // that IO lowering can find them when needed. 250 for (const auto &namelistObject : namelistDetails->objects()) 251 escapees.insert(&*namelistObject); 252 } else { 253 escapees.insert(&ultimate); 254 } 255 } 256 } 257 }; 258 Fortran::lower::pft::visitAllSymbols(funit, addToListIfEscapee); 259 } 260 261 //===--------------------------------------------------------------------===// 262 // AbstractConverter overrides 263 //===--------------------------------------------------------------------===// 264 265 mlir::Value getSymbolAddress(Fortran::lower::SymbolRef sym) override final { 266 return lookupSymbol(sym).getAddr(); 267 } 268 269 mlir::Value impliedDoBinding(llvm::StringRef name) override final { 270 mlir::Value val = localSymbols.lookupImpliedDo(name); 271 if (!val) 272 fir::emitFatalError(toLocation(), "ac-do-variable has no binding"); 273 return val; 274 } 275 276 void copySymbolBinding(Fortran::lower::SymbolRef src, 277 Fortran::lower::SymbolRef target) override final { 278 localSymbols.addSymbol(target, lookupSymbol(src).toExtendedValue()); 279 } 280 281 /// Add the symbol binding to the inner-most level of the symbol map and 282 /// return true if it is not already present. Otherwise, return false. 283 bool bindIfNewSymbol(Fortran::lower::SymbolRef sym, 284 const fir::ExtendedValue &exval) { 285 if (shallowLookupSymbol(sym)) 286 return false; 287 bindSymbol(sym, exval); 288 return true; 289 } 290 291 void bindSymbol(Fortran::lower::SymbolRef sym, 292 const fir::ExtendedValue &exval) override final { 293 localSymbols.addSymbol(sym, exval, /*forced=*/true); 294 } 295 296 bool lookupLabelSet(Fortran::lower::SymbolRef sym, 297 Fortran::lower::pft::LabelSet &labelSet) override final { 298 Fortran::lower::pft::FunctionLikeUnit &owningProc = 299 *getEval().getOwningProcedure(); 300 auto iter = owningProc.assignSymbolLabelMap.find(sym); 301 if (iter == owningProc.assignSymbolLabelMap.end()) 302 return false; 303 labelSet = iter->second; 304 return true; 305 } 306 307 Fortran::lower::pft::Evaluation * 308 lookupLabel(Fortran::lower::pft::Label label) override final { 309 Fortran::lower::pft::FunctionLikeUnit &owningProc = 310 *getEval().getOwningProcedure(); 311 auto iter = owningProc.labelEvaluationMap.find(label); 312 if (iter == owningProc.labelEvaluationMap.end()) 313 return nullptr; 314 return iter->second; 315 } 316 317 fir::ExtendedValue genExprAddr(const Fortran::lower::SomeExpr &expr, 318 Fortran::lower::StatementContext &context, 319 mlir::Location *loc = nullptr) override final { 320 return Fortran::lower::createSomeExtendedAddress( 321 loc ? *loc : toLocation(), *this, expr, localSymbols, context); 322 } 323 fir::ExtendedValue 324 genExprValue(const Fortran::lower::SomeExpr &expr, 325 Fortran::lower::StatementContext &context, 326 mlir::Location *loc = nullptr) override final { 327 return Fortran::lower::createSomeExtendedExpression( 328 loc ? *loc : toLocation(), *this, expr, localSymbols, context); 329 } 330 fir::MutableBoxValue 331 genExprMutableBox(mlir::Location loc, 332 const Fortran::lower::SomeExpr &expr) override final { 333 return Fortran::lower::createMutableBox(loc, *this, expr, localSymbols); 334 } 335 fir::ExtendedValue genExprBox(const Fortran::lower::SomeExpr &expr, 336 Fortran::lower::StatementContext &context, 337 mlir::Location loc) override final { 338 return Fortran::lower::createBoxValue(loc, *this, expr, localSymbols, 339 context); 340 } 341 342 Fortran::evaluate::FoldingContext &getFoldingContext() override final { 343 return foldingContext; 344 } 345 346 mlir::Type genType(const Fortran::lower::SomeExpr &expr) override final { 347 return Fortran::lower::translateSomeExprToFIRType(*this, expr); 348 } 349 mlir::Type genType(const Fortran::lower::pft::Variable &var) override final { 350 return Fortran::lower::translateVariableToFIRType(*this, var); 351 } 352 mlir::Type genType(Fortran::lower::SymbolRef sym) override final { 353 return Fortran::lower::translateSymbolToFIRType(*this, sym); 354 } 355 mlir::Type 356 genType(Fortran::common::TypeCategory tc, int kind, 357 llvm::ArrayRef<std::int64_t> lenParameters) override final { 358 return Fortran::lower::getFIRType(&getMLIRContext(), tc, kind, 359 lenParameters); 360 } 361 mlir::Type 362 genType(const Fortran::semantics::DerivedTypeSpec &tySpec) override final { 363 return Fortran::lower::translateDerivedTypeToFIRType(*this, tySpec); 364 } 365 mlir::Type genType(Fortran::common::TypeCategory tc) override final { 366 return Fortran::lower::getFIRType( 367 &getMLIRContext(), tc, bridge.getDefaultKinds().GetDefaultKind(tc), 368 llvm::None); 369 } 370 371 bool createHostAssociateVarClone( 372 const Fortran::semantics::Symbol &sym) override final { 373 mlir::Location loc = genLocation(sym.name()); 374 mlir::Type symType = genType(sym); 375 const auto *details = sym.detailsIf<Fortran::semantics::HostAssocDetails>(); 376 assert(details && "No host-association found"); 377 const Fortran::semantics::Symbol &hsym = details->symbol(); 378 Fortran::lower::SymbolBox hsb = lookupSymbol(hsym); 379 380 auto allocate = [&](llvm::ArrayRef<mlir::Value> shape, 381 llvm::ArrayRef<mlir::Value> typeParams) -> mlir::Value { 382 mlir::Value allocVal = builder->allocateLocal( 383 loc, symType, mangleName(sym), toStringRef(sym.GetUltimate().name()), 384 /*pinned=*/true, shape, typeParams, 385 sym.GetUltimate().attrs().test(Fortran::semantics::Attr::TARGET)); 386 return allocVal; 387 }; 388 389 fir::ExtendedValue hexv = getExtendedValue(hsb); 390 fir::ExtendedValue exv = hexv.match( 391 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 392 const Fortran::semantics::DeclTypeSpec *type = sym.GetType(); 393 if (type && type->IsPolymorphic()) 394 TODO(loc, "create polymorphic host associated copy"); 395 // Create a contiguous temp with the same shape and length as 396 // the original variable described by a fir.box. 397 llvm::SmallVector<mlir::Value> extents = 398 fir::factory::getExtents(*builder, loc, hexv); 399 if (box.isDerivedWithLengthParameters()) 400 TODO(loc, "get length parameters from derived type BoxValue"); 401 if (box.isCharacter()) { 402 mlir::Value len = fir::factory::readCharLen(*builder, loc, box); 403 mlir::Value temp = allocate(extents, {len}); 404 return fir::CharArrayBoxValue{temp, len, extents}; 405 } 406 return fir::ArrayBoxValue{allocate(extents, {}), extents}; 407 }, 408 [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue { 409 // Allocate storage for a pointer/allocatble descriptor. 410 // No shape/lengths to be passed to the alloca. 411 return fir::MutableBoxValue(allocate({}, {}), 412 box.nonDeferredLenParams(), {}); 413 }, 414 [&](const auto &) -> fir::ExtendedValue { 415 mlir::Value temp = 416 allocate(fir::factory::getExtents(*builder, loc, hexv), 417 fir::getTypeParams(hexv)); 418 return fir::substBase(hexv, temp); 419 }); 420 421 return bindIfNewSymbol(sym, exv); 422 } 423 424 void 425 copyHostAssociateVar(const Fortran::semantics::Symbol &sym) override final { 426 // 1) Fetch the original copy of the variable. 427 assert(sym.has<Fortran::semantics::HostAssocDetails>() && 428 "No host-association found"); 429 const Fortran::semantics::Symbol &hsym = sym.GetUltimate(); 430 Fortran::lower::SymbolBox hsb = lookupSymbol(hsym); 431 fir::ExtendedValue hexv = getExtendedValue(hsb); 432 433 // 2) Create a copy that will mask the original. 434 createHostAssociateVarClone(sym); 435 Fortran::lower::SymbolBox sb = lookupSymbol(sym); 436 fir::ExtendedValue exv = getExtendedValue(sb); 437 438 // 3) Perform the assignment. 439 mlir::Location loc = genLocation(sym.name()); 440 mlir::Type symType = genType(sym); 441 if (auto seqTy = symType.dyn_cast<fir::SequenceType>()) { 442 Fortran::lower::StatementContext stmtCtx; 443 Fortran::lower::createSomeArrayAssignment(*this, exv, hexv, localSymbols, 444 stmtCtx); 445 stmtCtx.finalize(); 446 } else if (hexv.getBoxOf<fir::CharBoxValue>()) { 447 fir::factory::CharacterExprHelper{*builder, loc}.createAssign(exv, hexv); 448 } else if (hexv.getBoxOf<fir::MutableBoxValue>()) { 449 TODO(loc, "firstprivatisation of allocatable variables"); 450 } else { 451 auto loadVal = builder->create<fir::LoadOp>(loc, fir::getBase(hexv)); 452 builder->create<fir::StoreOp>(loc, loadVal, fir::getBase(exv)); 453 } 454 } 455 456 //===--------------------------------------------------------------------===// 457 // Utility methods 458 //===--------------------------------------------------------------------===// 459 460 mlir::Location getCurrentLocation() override final { return toLocation(); } 461 462 /// Generate a dummy location. 463 mlir::Location genUnknownLocation() override final { 464 // Note: builder may not be instantiated yet 465 return mlir::UnknownLoc::get(&getMLIRContext()); 466 } 467 468 /// Generate a `Location` from the `CharBlock`. 469 mlir::Location 470 genLocation(const Fortran::parser::CharBlock &block) override final { 471 if (const Fortran::parser::AllCookedSources *cooked = 472 bridge.getCookedSource()) { 473 if (std::optional<std::pair<Fortran::parser::SourcePosition, 474 Fortran::parser::SourcePosition>> 475 loc = cooked->GetSourcePositionRange(block)) { 476 // loc is a pair (begin, end); use the beginning position 477 Fortran::parser::SourcePosition &filePos = loc->first; 478 return mlir::FileLineColLoc::get(&getMLIRContext(), filePos.file.path(), 479 filePos.line, filePos.column); 480 } 481 } 482 return genUnknownLocation(); 483 } 484 485 fir::FirOpBuilder &getFirOpBuilder() override final { return *builder; } 486 487 mlir::ModuleOp &getModuleOp() override final { return bridge.getModule(); } 488 489 mlir::MLIRContext &getMLIRContext() override final { 490 return bridge.getMLIRContext(); 491 } 492 std::string 493 mangleName(const Fortran::semantics::Symbol &symbol) override final { 494 return Fortran::lower::mangle::mangleName(symbol); 495 } 496 497 const fir::KindMapping &getKindMap() override final { 498 return bridge.getKindMap(); 499 } 500 501 mlir::Value hostAssocTupleValue() override final { return hostAssocTuple; } 502 503 /// Record a binding for the ssa-value of the tuple for this function. 504 void bindHostAssocTuple(mlir::Value val) override final { 505 assert(!hostAssocTuple && val); 506 hostAssocTuple = val; 507 } 508 509 void registerRuntimeTypeInfo( 510 mlir::Location loc, 511 Fortran::lower::SymbolRef typeInfoSym) override final { 512 runtimeTypeInfoConverter.registerTypeInfoSymbol(*this, loc, typeInfoSym); 513 } 514 515 private: 516 FirConverter() = delete; 517 FirConverter(const FirConverter &) = delete; 518 FirConverter &operator=(const FirConverter &) = delete; 519 520 //===--------------------------------------------------------------------===// 521 // Helper member functions 522 //===--------------------------------------------------------------------===// 523 524 mlir::Value createFIRExpr(mlir::Location loc, 525 const Fortran::lower::SomeExpr *expr, 526 Fortran::lower::StatementContext &stmtCtx) { 527 return fir::getBase(genExprValue(*expr, stmtCtx, &loc)); 528 } 529 530 /// Find the symbol in the local map or return null. 531 Fortran::lower::SymbolBox 532 lookupSymbol(const Fortran::semantics::Symbol &sym) { 533 if (Fortran::lower::SymbolBox v = localSymbols.lookupSymbol(sym)) 534 return v; 535 return {}; 536 } 537 538 /// Find the symbol in the inner-most level of the local map or return null. 539 Fortran::lower::SymbolBox 540 shallowLookupSymbol(const Fortran::semantics::Symbol &sym) { 541 if (Fortran::lower::SymbolBox v = localSymbols.shallowLookupSymbol(sym)) 542 return v; 543 return {}; 544 } 545 546 /// Add the symbol to the local map and return `true`. If the symbol is 547 /// already in the map and \p forced is `false`, the map is not updated. 548 /// Instead the value `false` is returned. 549 bool addSymbol(const Fortran::semantics::SymbolRef sym, mlir::Value val, 550 bool forced = false) { 551 if (!forced && lookupSymbol(sym)) 552 return false; 553 localSymbols.addSymbol(sym, val, forced); 554 return true; 555 } 556 557 bool addCharSymbol(const Fortran::semantics::SymbolRef sym, mlir::Value val, 558 mlir::Value len, bool forced = false) { 559 if (!forced && lookupSymbol(sym)) 560 return false; 561 // TODO: ensure val type is fir.array<len x fir.char<kind>> like. Insert 562 // cast if needed. 563 localSymbols.addCharSymbol(sym, val, len, forced); 564 return true; 565 } 566 567 fir::ExtendedValue getExtendedValue(Fortran::lower::SymbolBox sb) { 568 return sb.match( 569 [&](const Fortran::lower::SymbolBox::PointerOrAllocatable &box) { 570 return fir::factory::genMutableBoxRead(*builder, getCurrentLocation(), 571 box); 572 }, 573 [&sb](auto &) { return sb.toExtendedValue(); }); 574 } 575 576 static bool isNumericScalarCategory(Fortran::common::TypeCategory cat) { 577 return cat == Fortran::common::TypeCategory::Integer || 578 cat == Fortran::common::TypeCategory::Real || 579 cat == Fortran::common::TypeCategory::Complex || 580 cat == Fortran::common::TypeCategory::Logical; 581 } 582 static bool isLogicalCategory(Fortran::common::TypeCategory cat) { 583 return cat == Fortran::common::TypeCategory::Logical; 584 } 585 static bool isCharacterCategory(Fortran::common::TypeCategory cat) { 586 return cat == Fortran::common::TypeCategory::Character; 587 } 588 static bool isDerivedCategory(Fortran::common::TypeCategory cat) { 589 return cat == Fortran::common::TypeCategory::Derived; 590 } 591 592 /// Insert a new block before \p block. Leave the insertion point unchanged. 593 mlir::Block *insertBlock(mlir::Block *block) { 594 mlir::OpBuilder::InsertPoint insertPt = builder->saveInsertionPoint(); 595 mlir::Block *newBlock = builder->createBlock(block); 596 builder->restoreInsertionPoint(insertPt); 597 return newBlock; 598 } 599 600 mlir::Block *blockOfLabel(Fortran::lower::pft::Evaluation &eval, 601 Fortran::parser::Label label) { 602 const Fortran::lower::pft::LabelEvalMap &labelEvaluationMap = 603 eval.getOwningProcedure()->labelEvaluationMap; 604 const auto iter = labelEvaluationMap.find(label); 605 assert(iter != labelEvaluationMap.end() && "label missing from map"); 606 mlir::Block *block = iter->second->block; 607 assert(block && "missing labeled evaluation block"); 608 return block; 609 } 610 611 void genFIRBranch(mlir::Block *targetBlock) { 612 assert(targetBlock && "missing unconditional target block"); 613 builder->create<mlir::cf::BranchOp>(toLocation(), targetBlock); 614 } 615 616 void genFIRConditionalBranch(mlir::Value cond, mlir::Block *trueTarget, 617 mlir::Block *falseTarget) { 618 assert(trueTarget && "missing conditional branch true block"); 619 assert(falseTarget && "missing conditional branch false block"); 620 mlir::Location loc = toLocation(); 621 mlir::Value bcc = builder->createConvert(loc, builder->getI1Type(), cond); 622 builder->create<mlir::cf::CondBranchOp>(loc, bcc, trueTarget, llvm::None, 623 falseTarget, llvm::None); 624 } 625 void genFIRConditionalBranch(mlir::Value cond, 626 Fortran::lower::pft::Evaluation *trueTarget, 627 Fortran::lower::pft::Evaluation *falseTarget) { 628 genFIRConditionalBranch(cond, trueTarget->block, falseTarget->block); 629 } 630 void genFIRConditionalBranch(const Fortran::parser::ScalarLogicalExpr &expr, 631 mlir::Block *trueTarget, 632 mlir::Block *falseTarget) { 633 Fortran::lower::StatementContext stmtCtx; 634 mlir::Value cond = 635 createFIRExpr(toLocation(), Fortran::semantics::GetExpr(expr), stmtCtx); 636 stmtCtx.finalize(); 637 genFIRConditionalBranch(cond, trueTarget, falseTarget); 638 } 639 void genFIRConditionalBranch(const Fortran::parser::ScalarLogicalExpr &expr, 640 Fortran::lower::pft::Evaluation *trueTarget, 641 Fortran::lower::pft::Evaluation *falseTarget) { 642 Fortran::lower::StatementContext stmtCtx; 643 mlir::Value cond = 644 createFIRExpr(toLocation(), Fortran::semantics::GetExpr(expr), stmtCtx); 645 stmtCtx.finalize(); 646 genFIRConditionalBranch(cond, trueTarget->block, falseTarget->block); 647 } 648 649 //===--------------------------------------------------------------------===// 650 // Termination of symbolically referenced execution units 651 //===--------------------------------------------------------------------===// 652 653 /// END of program 654 /// 655 /// Generate the cleanup block before the program exits 656 void genExitRoutine() { 657 if (blockIsUnterminated()) 658 builder->create<mlir::func::ReturnOp>(toLocation()); 659 } 660 void genFIR(const Fortran::parser::EndProgramStmt &) { genExitRoutine(); } 661 662 /// END of procedure-like constructs 663 /// 664 /// Generate the cleanup block before the procedure exits 665 void genReturnSymbol(const Fortran::semantics::Symbol &functionSymbol) { 666 const Fortran::semantics::Symbol &resultSym = 667 functionSymbol.get<Fortran::semantics::SubprogramDetails>().result(); 668 Fortran::lower::SymbolBox resultSymBox = lookupSymbol(resultSym); 669 mlir::Location loc = toLocation(); 670 if (!resultSymBox) { 671 mlir::emitError(loc, "failed lowering function return"); 672 return; 673 } 674 mlir::Value resultVal = resultSymBox.match( 675 [&](const fir::CharBoxValue &x) -> mlir::Value { 676 return fir::factory::CharacterExprHelper{*builder, loc} 677 .createEmboxChar(x.getBuffer(), x.getLen()); 678 }, 679 [&](const auto &) -> mlir::Value { 680 mlir::Value resultRef = resultSymBox.getAddr(); 681 mlir::Type resultType = genType(resultSym); 682 mlir::Type resultRefType = builder->getRefType(resultType); 683 // A function with multiple entry points returning different types 684 // tags all result variables with one of the largest types to allow 685 // them to share the same storage. Convert this to the actual type. 686 if (resultRef.getType() != resultRefType) 687 resultRef = builder->createConvert(loc, resultRefType, resultRef); 688 return builder->create<fir::LoadOp>(loc, resultRef); 689 }); 690 builder->create<mlir::func::ReturnOp>(loc, resultVal); 691 } 692 693 /// Get the return value of a call to \p symbol, which is a subroutine entry 694 /// point that has alternative return specifiers. 695 const mlir::Value 696 getAltReturnResult(const Fortran::semantics::Symbol &symbol) { 697 assert(Fortran::semantics::HasAlternateReturns(symbol) && 698 "subroutine does not have alternate returns"); 699 return getSymbolAddress(symbol); 700 } 701 702 void genFIRProcedureExit(Fortran::lower::pft::FunctionLikeUnit &funit, 703 const Fortran::semantics::Symbol &symbol) { 704 if (mlir::Block *finalBlock = funit.finalBlock) { 705 // The current block must end with a terminator. 706 if (blockIsUnterminated()) 707 builder->create<mlir::cf::BranchOp>(toLocation(), finalBlock); 708 // Set insertion point to final block. 709 builder->setInsertionPoint(finalBlock, finalBlock->end()); 710 } 711 if (Fortran::semantics::IsFunction(symbol)) { 712 genReturnSymbol(symbol); 713 } else if (Fortran::semantics::HasAlternateReturns(symbol)) { 714 mlir::Value retval = builder->create<fir::LoadOp>( 715 toLocation(), getAltReturnResult(symbol)); 716 builder->create<mlir::func::ReturnOp>(toLocation(), retval); 717 } else { 718 genExitRoutine(); 719 } 720 } 721 722 // 723 // Statements that have control-flow semantics 724 // 725 726 /// Generate an If[Then]Stmt condition or its negation. 727 template <typename A> 728 mlir::Value genIfCondition(const A *stmt, bool negate = false) { 729 mlir::Location loc = toLocation(); 730 Fortran::lower::StatementContext stmtCtx; 731 mlir::Value condExpr = createFIRExpr( 732 loc, 733 Fortran::semantics::GetExpr( 734 std::get<Fortran::parser::ScalarLogicalExpr>(stmt->t)), 735 stmtCtx); 736 stmtCtx.finalize(); 737 mlir::Value cond = 738 builder->createConvert(loc, builder->getI1Type(), condExpr); 739 if (negate) 740 cond = builder->create<mlir::arith::XOrIOp>( 741 loc, cond, builder->createIntegerConstant(loc, cond.getType(), 1)); 742 return cond; 743 } 744 745 mlir::func::FuncOp getFunc(llvm::StringRef name, mlir::FunctionType ty) { 746 if (mlir::func::FuncOp func = builder->getNamedFunction(name)) { 747 assert(func.getFunctionType() == ty); 748 return func; 749 } 750 return builder->createFunction(toLocation(), name, ty); 751 } 752 753 /// Lowering of CALL statement 754 void genFIR(const Fortran::parser::CallStmt &stmt) { 755 Fortran::lower::StatementContext stmtCtx; 756 Fortran::lower::pft::Evaluation &eval = getEval(); 757 setCurrentPosition(stmt.v.source); 758 assert(stmt.typedCall && "Call was not analyzed"); 759 // Call statement lowering shares code with function call lowering. 760 mlir::Value res = Fortran::lower::createSubroutineCall( 761 *this, *stmt.typedCall, explicitIterSpace, implicitIterSpace, 762 localSymbols, stmtCtx, /*isUserDefAssignment=*/false); 763 if (!res) 764 return; // "Normal" subroutine call. 765 // Call with alternate return specifiers. 766 // The call returns an index that selects an alternate return branch target. 767 llvm::SmallVector<int64_t> indexList; 768 llvm::SmallVector<mlir::Block *> blockList; 769 int64_t index = 0; 770 for (const Fortran::parser::ActualArgSpec &arg : 771 std::get<std::list<Fortran::parser::ActualArgSpec>>(stmt.v.t)) { 772 const auto &actual = std::get<Fortran::parser::ActualArg>(arg.t); 773 if (const auto *altReturn = 774 std::get_if<Fortran::parser::AltReturnSpec>(&actual.u)) { 775 indexList.push_back(++index); 776 blockList.push_back(blockOfLabel(eval, altReturn->v)); 777 } 778 } 779 blockList.push_back(eval.nonNopSuccessor().block); // default = fallthrough 780 stmtCtx.finalize(); 781 builder->create<fir::SelectOp>(toLocation(), res, indexList, blockList); 782 } 783 784 void genFIR(const Fortran::parser::ComputedGotoStmt &stmt) { 785 Fortran::lower::StatementContext stmtCtx; 786 Fortran::lower::pft::Evaluation &eval = getEval(); 787 mlir::Value selectExpr = 788 createFIRExpr(toLocation(), 789 Fortran::semantics::GetExpr( 790 std::get<Fortran::parser::ScalarIntExpr>(stmt.t)), 791 stmtCtx); 792 stmtCtx.finalize(); 793 llvm::SmallVector<int64_t> indexList; 794 llvm::SmallVector<mlir::Block *> blockList; 795 int64_t index = 0; 796 for (Fortran::parser::Label label : 797 std::get<std::list<Fortran::parser::Label>>(stmt.t)) { 798 indexList.push_back(++index); 799 blockList.push_back(blockOfLabel(eval, label)); 800 } 801 blockList.push_back(eval.nonNopSuccessor().block); // default 802 builder->create<fir::SelectOp>(toLocation(), selectExpr, indexList, 803 blockList); 804 } 805 806 void genFIR(const Fortran::parser::ArithmeticIfStmt &stmt) { 807 Fortran::lower::StatementContext stmtCtx; 808 Fortran::lower::pft::Evaluation &eval = getEval(); 809 mlir::Value expr = createFIRExpr( 810 toLocation(), 811 Fortran::semantics::GetExpr(std::get<Fortran::parser::Expr>(stmt.t)), 812 stmtCtx); 813 stmtCtx.finalize(); 814 mlir::Type exprType = expr.getType(); 815 mlir::Location loc = toLocation(); 816 if (exprType.isSignlessInteger()) { 817 // Arithmetic expression has Integer type. Generate a SelectCaseOp 818 // with ranges {(-inf:-1], 0=default, [1:inf)}. 819 mlir::MLIRContext *context = builder->getContext(); 820 llvm::SmallVector<mlir::Attribute> attrList; 821 llvm::SmallVector<mlir::Value> valueList; 822 llvm::SmallVector<mlir::Block *> blockList; 823 attrList.push_back(fir::UpperBoundAttr::get(context)); 824 valueList.push_back(builder->createIntegerConstant(loc, exprType, -1)); 825 blockList.push_back(blockOfLabel(eval, std::get<1>(stmt.t))); 826 attrList.push_back(fir::LowerBoundAttr::get(context)); 827 valueList.push_back(builder->createIntegerConstant(loc, exprType, 1)); 828 blockList.push_back(blockOfLabel(eval, std::get<3>(stmt.t))); 829 attrList.push_back(mlir::UnitAttr::get(context)); // 0 is the "default" 830 blockList.push_back(blockOfLabel(eval, std::get<2>(stmt.t))); 831 builder->create<fir::SelectCaseOp>(loc, expr, attrList, valueList, 832 blockList); 833 return; 834 } 835 // Arithmetic expression has Real type. Generate 836 // sum = expr + expr [ raise an exception if expr is a NaN ] 837 // if (sum < 0.0) goto L1 else if (sum > 0.0) goto L3 else goto L2 838 auto sum = builder->create<mlir::arith::AddFOp>(loc, expr, expr); 839 auto zero = builder->create<mlir::arith::ConstantOp>( 840 loc, exprType, builder->getFloatAttr(exprType, 0.0)); 841 auto cond1 = builder->create<mlir::arith::CmpFOp>( 842 loc, mlir::arith::CmpFPredicate::OLT, sum, zero); 843 mlir::Block *elseIfBlock = 844 builder->getBlock()->splitBlock(builder->getInsertionPoint()); 845 genFIRConditionalBranch(cond1, blockOfLabel(eval, std::get<1>(stmt.t)), 846 elseIfBlock); 847 startBlock(elseIfBlock); 848 auto cond2 = builder->create<mlir::arith::CmpFOp>( 849 loc, mlir::arith::CmpFPredicate::OGT, sum, zero); 850 genFIRConditionalBranch(cond2, blockOfLabel(eval, std::get<3>(stmt.t)), 851 blockOfLabel(eval, std::get<2>(stmt.t))); 852 } 853 854 void genFIR(const Fortran::parser::AssignedGotoStmt &stmt) { 855 // Program requirement 1990 8.2.4 - 856 // 857 // At the time of execution of an assigned GOTO statement, the integer 858 // variable must be defined with the value of a statement label of a 859 // branch target statement that appears in the same scoping unit. 860 // Note that the variable may be defined with a statement label value 861 // only by an ASSIGN statement in the same scoping unit as the assigned 862 // GOTO statement. 863 864 mlir::Location loc = toLocation(); 865 Fortran::lower::pft::Evaluation &eval = getEval(); 866 const Fortran::lower::pft::SymbolLabelMap &symbolLabelMap = 867 eval.getOwningProcedure()->assignSymbolLabelMap; 868 const Fortran::semantics::Symbol &symbol = 869 *std::get<Fortran::parser::Name>(stmt.t).symbol; 870 auto selectExpr = 871 builder->create<fir::LoadOp>(loc, getSymbolAddress(symbol)); 872 auto iter = symbolLabelMap.find(symbol); 873 if (iter == symbolLabelMap.end()) { 874 // Fail for a nonconforming program unit that does not have any ASSIGN 875 // statements. The front end should check for this. 876 mlir::emitError(loc, "(semantics issue) no assigned goto targets"); 877 exit(1); 878 } 879 auto labelSet = iter->second; 880 llvm::SmallVector<int64_t> indexList; 881 llvm::SmallVector<mlir::Block *> blockList; 882 auto addLabel = [&](Fortran::parser::Label label) { 883 indexList.push_back(label); 884 blockList.push_back(blockOfLabel(eval, label)); 885 }; 886 // Add labels from an explicit list. The list may have duplicates. 887 for (Fortran::parser::Label label : 888 std::get<std::list<Fortran::parser::Label>>(stmt.t)) { 889 if (labelSet.count(label) && 890 std::find(indexList.begin(), indexList.end(), label) == 891 indexList.end()) { // ignore duplicates 892 addLabel(label); 893 } 894 } 895 // Absent an explicit list, add all possible label targets. 896 if (indexList.empty()) 897 for (auto &label : labelSet) 898 addLabel(label); 899 // Add a nop/fallthrough branch to the switch for a nonconforming program 900 // unit that violates the program requirement above. 901 blockList.push_back(eval.nonNopSuccessor().block); // default 902 builder->create<fir::SelectOp>(loc, selectExpr, indexList, blockList); 903 } 904 905 /// Generate FIR for a DO construct. There are six variants: 906 /// - unstructured infinite and while loops 907 /// - structured and unstructured increment loops 908 /// - structured and unstructured concurrent loops 909 void genFIR(const Fortran::parser::DoConstruct &doConstruct) { 910 TODO(toLocation(), "DoConstruct lowering"); 911 } 912 913 /// Generate structured or unstructured FIR for an IF construct. 914 /// The initial statement may be either an IfStmt or an IfThenStmt. 915 void genFIR(const Fortran::parser::IfConstruct &) { 916 mlir::Location loc = toLocation(); 917 Fortran::lower::pft::Evaluation &eval = getEval(); 918 if (eval.lowerAsStructured()) { 919 // Structured fir.if nest. 920 fir::IfOp topIfOp, currentIfOp; 921 for (Fortran::lower::pft::Evaluation &e : eval.getNestedEvaluations()) { 922 auto genIfOp = [&](mlir::Value cond) { 923 auto ifOp = builder->create<fir::IfOp>(loc, cond, /*withElse=*/true); 924 builder->setInsertionPointToStart(&ifOp.getThenRegion().front()); 925 return ifOp; 926 }; 927 if (auto *s = e.getIf<Fortran::parser::IfThenStmt>()) { 928 topIfOp = currentIfOp = genIfOp(genIfCondition(s, e.negateCondition)); 929 } else if (auto *s = e.getIf<Fortran::parser::IfStmt>()) { 930 topIfOp = currentIfOp = genIfOp(genIfCondition(s, e.negateCondition)); 931 } else if (auto *s = e.getIf<Fortran::parser::ElseIfStmt>()) { 932 builder->setInsertionPointToStart( 933 ¤tIfOp.getElseRegion().front()); 934 currentIfOp = genIfOp(genIfCondition(s)); 935 } else if (e.isA<Fortran::parser::ElseStmt>()) { 936 builder->setInsertionPointToStart( 937 ¤tIfOp.getElseRegion().front()); 938 } else if (e.isA<Fortran::parser::EndIfStmt>()) { 939 builder->setInsertionPointAfter(topIfOp); 940 } else { 941 genFIR(e, /*unstructuredContext=*/false); 942 } 943 } 944 return; 945 } 946 947 // Unstructured branch sequence. 948 for (Fortran::lower::pft::Evaluation &e : eval.getNestedEvaluations()) { 949 auto genIfBranch = [&](mlir::Value cond) { 950 if (e.lexicalSuccessor == e.controlSuccessor) // empty block -> exit 951 genFIRConditionalBranch(cond, e.parentConstruct->constructExit, 952 e.controlSuccessor); 953 else // non-empty block 954 genFIRConditionalBranch(cond, e.lexicalSuccessor, e.controlSuccessor); 955 }; 956 if (auto *s = e.getIf<Fortran::parser::IfThenStmt>()) { 957 maybeStartBlock(e.block); 958 genIfBranch(genIfCondition(s, e.negateCondition)); 959 } else if (auto *s = e.getIf<Fortran::parser::IfStmt>()) { 960 maybeStartBlock(e.block); 961 genIfBranch(genIfCondition(s, e.negateCondition)); 962 } else if (auto *s = e.getIf<Fortran::parser::ElseIfStmt>()) { 963 startBlock(e.block); 964 genIfBranch(genIfCondition(s)); 965 } else { 966 genFIR(e); 967 } 968 } 969 } 970 971 void genFIR(const Fortran::parser::CaseConstruct &) { 972 for (Fortran::lower::pft::Evaluation &e : getEval().getNestedEvaluations()) 973 genFIR(e); 974 } 975 976 template <typename A> 977 void genNestedStatement(const Fortran::parser::Statement<A> &stmt) { 978 setCurrentPosition(stmt.source); 979 genFIR(stmt.statement); 980 } 981 982 /// Force the binding of an explicit symbol. This is used to bind and re-bind 983 /// a concurrent control symbol to its value. 984 void forceControlVariableBinding(const Fortran::semantics::Symbol *sym, 985 mlir::Value inducVar) { 986 mlir::Location loc = toLocation(); 987 assert(sym && "There must be a symbol to bind"); 988 mlir::Type toTy = genType(*sym); 989 // FIXME: this should be a "per iteration" temporary. 990 mlir::Value tmp = builder->createTemporary( 991 loc, toTy, toStringRef(sym->name()), 992 llvm::ArrayRef<mlir::NamedAttribute>{ 993 Fortran::lower::getAdaptToByRefAttr(*builder)}); 994 mlir::Value cast = builder->createConvert(loc, toTy, inducVar); 995 builder->create<fir::StoreOp>(loc, cast, tmp); 996 localSymbols.addSymbol(*sym, tmp, /*force=*/true); 997 } 998 999 /// Process a concurrent header for a FORALL. (Concurrent headers for DO 1000 /// CONCURRENT loops are lowered elsewhere.) 1001 void genFIR(const Fortran::parser::ConcurrentHeader &header) { 1002 llvm::SmallVector<mlir::Value> lows; 1003 llvm::SmallVector<mlir::Value> highs; 1004 llvm::SmallVector<mlir::Value> steps; 1005 if (explicitIterSpace.isOutermostForall()) { 1006 // For the outermost forall, we evaluate the bounds expressions once. 1007 // Contrastingly, if this forall is nested, the bounds expressions are 1008 // assumed to be pure, possibly dependent on outer concurrent control 1009 // variables, possibly variant with respect to arguments, and will be 1010 // re-evaluated. 1011 mlir::Location loc = toLocation(); 1012 mlir::Type idxTy = builder->getIndexType(); 1013 Fortran::lower::StatementContext &stmtCtx = 1014 explicitIterSpace.stmtContext(); 1015 auto lowerExpr = [&](auto &e) { 1016 return fir::getBase(genExprValue(e, stmtCtx)); 1017 }; 1018 for (const Fortran::parser::ConcurrentControl &ctrl : 1019 std::get<std::list<Fortran::parser::ConcurrentControl>>(header.t)) { 1020 const Fortran::lower::SomeExpr *lo = 1021 Fortran::semantics::GetExpr(std::get<1>(ctrl.t)); 1022 const Fortran::lower::SomeExpr *hi = 1023 Fortran::semantics::GetExpr(std::get<2>(ctrl.t)); 1024 auto &optStep = 1025 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(ctrl.t); 1026 lows.push_back(builder->createConvert(loc, idxTy, lowerExpr(*lo))); 1027 highs.push_back(builder->createConvert(loc, idxTy, lowerExpr(*hi))); 1028 steps.push_back( 1029 optStep.has_value() 1030 ? builder->createConvert( 1031 loc, idxTy, 1032 lowerExpr(*Fortran::semantics::GetExpr(*optStep))) 1033 : builder->createIntegerConstant(loc, idxTy, 1)); 1034 } 1035 } 1036 auto lambda = [&, lows, highs, steps]() { 1037 // Create our iteration space from the header spec. 1038 mlir::Location loc = toLocation(); 1039 mlir::Type idxTy = builder->getIndexType(); 1040 llvm::SmallVector<fir::DoLoopOp> loops; 1041 Fortran::lower::StatementContext &stmtCtx = 1042 explicitIterSpace.stmtContext(); 1043 auto lowerExpr = [&](auto &e) { 1044 return fir::getBase(genExprValue(e, stmtCtx)); 1045 }; 1046 const bool outermost = !lows.empty(); 1047 std::size_t headerIndex = 0; 1048 for (const Fortran::parser::ConcurrentControl &ctrl : 1049 std::get<std::list<Fortran::parser::ConcurrentControl>>(header.t)) { 1050 const Fortran::semantics::Symbol *ctrlVar = 1051 std::get<Fortran::parser::Name>(ctrl.t).symbol; 1052 mlir::Value lb; 1053 mlir::Value ub; 1054 mlir::Value by; 1055 if (outermost) { 1056 assert(headerIndex < lows.size()); 1057 if (headerIndex == 0) 1058 explicitIterSpace.resetInnerArgs(); 1059 lb = lows[headerIndex]; 1060 ub = highs[headerIndex]; 1061 by = steps[headerIndex++]; 1062 } else { 1063 const Fortran::lower::SomeExpr *lo = 1064 Fortran::semantics::GetExpr(std::get<1>(ctrl.t)); 1065 const Fortran::lower::SomeExpr *hi = 1066 Fortran::semantics::GetExpr(std::get<2>(ctrl.t)); 1067 auto &optStep = 1068 std::get<std::optional<Fortran::parser::ScalarIntExpr>>(ctrl.t); 1069 lb = builder->createConvert(loc, idxTy, lowerExpr(*lo)); 1070 ub = builder->createConvert(loc, idxTy, lowerExpr(*hi)); 1071 by = optStep.has_value() 1072 ? builder->createConvert( 1073 loc, idxTy, 1074 lowerExpr(*Fortran::semantics::GetExpr(*optStep))) 1075 : builder->createIntegerConstant(loc, idxTy, 1); 1076 } 1077 auto lp = builder->create<fir::DoLoopOp>( 1078 loc, lb, ub, by, /*unordered=*/true, 1079 /*finalCount=*/false, explicitIterSpace.getInnerArgs()); 1080 if (!loops.empty() || !outermost) 1081 builder->create<fir::ResultOp>(loc, lp.getResults()); 1082 explicitIterSpace.setInnerArgs(lp.getRegionIterArgs()); 1083 builder->setInsertionPointToStart(lp.getBody()); 1084 forceControlVariableBinding(ctrlVar, lp.getInductionVar()); 1085 loops.push_back(lp); 1086 } 1087 if (outermost) 1088 explicitIterSpace.setOuterLoop(loops[0]); 1089 explicitIterSpace.appendLoops(loops); 1090 if (const auto &mask = 1091 std::get<std::optional<Fortran::parser::ScalarLogicalExpr>>( 1092 header.t); 1093 mask.has_value()) { 1094 mlir::Type i1Ty = builder->getI1Type(); 1095 fir::ExtendedValue maskExv = 1096 genExprValue(*Fortran::semantics::GetExpr(mask.value()), stmtCtx); 1097 mlir::Value cond = 1098 builder->createConvert(loc, i1Ty, fir::getBase(maskExv)); 1099 auto ifOp = builder->create<fir::IfOp>( 1100 loc, explicitIterSpace.innerArgTypes(), cond, 1101 /*withElseRegion=*/true); 1102 builder->create<fir::ResultOp>(loc, ifOp.getResults()); 1103 builder->setInsertionPointToStart(&ifOp.getElseRegion().front()); 1104 builder->create<fir::ResultOp>(loc, explicitIterSpace.getInnerArgs()); 1105 builder->setInsertionPointToStart(&ifOp.getThenRegion().front()); 1106 } 1107 }; 1108 // Push the lambda to gen the loop nest context. 1109 explicitIterSpace.pushLoopNest(lambda); 1110 } 1111 1112 void genFIR(const Fortran::parser::ForallAssignmentStmt &stmt) { 1113 std::visit([&](const auto &x) { genFIR(x); }, stmt.u); 1114 } 1115 1116 void genFIR(const Fortran::parser::EndForallStmt &) { 1117 cleanupExplicitSpace(); 1118 } 1119 1120 template <typename A> 1121 void prepareExplicitSpace(const A &forall) { 1122 if (!explicitIterSpace.isActive()) 1123 analyzeExplicitSpace(forall); 1124 localSymbols.pushScope(); 1125 explicitIterSpace.enter(); 1126 } 1127 1128 /// Cleanup all the FORALL context information when we exit. 1129 void cleanupExplicitSpace() { 1130 explicitIterSpace.leave(); 1131 localSymbols.popScope(); 1132 } 1133 1134 /// Generate FIR for a FORALL statement. 1135 void genFIR(const Fortran::parser::ForallStmt &stmt) { 1136 prepareExplicitSpace(stmt); 1137 genFIR(std::get< 1138 Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>( 1139 stmt.t) 1140 .value()); 1141 genFIR(std::get<Fortran::parser::UnlabeledStatement< 1142 Fortran::parser::ForallAssignmentStmt>>(stmt.t) 1143 .statement); 1144 cleanupExplicitSpace(); 1145 } 1146 1147 /// Generate FIR for a FORALL construct. 1148 void genFIR(const Fortran::parser::ForallConstruct &forall) { 1149 prepareExplicitSpace(forall); 1150 genNestedStatement( 1151 std::get< 1152 Fortran::parser::Statement<Fortran::parser::ForallConstructStmt>>( 1153 forall.t)); 1154 for (const Fortran::parser::ForallBodyConstruct &s : 1155 std::get<std::list<Fortran::parser::ForallBodyConstruct>>(forall.t)) { 1156 std::visit( 1157 Fortran::common::visitors{ 1158 [&](const Fortran::parser::WhereConstruct &b) { genFIR(b); }, 1159 [&](const Fortran::common::Indirection< 1160 Fortran::parser::ForallConstruct> &b) { genFIR(b.value()); }, 1161 [&](const auto &b) { genNestedStatement(b); }}, 1162 s.u); 1163 } 1164 genNestedStatement( 1165 std::get<Fortran::parser::Statement<Fortran::parser::EndForallStmt>>( 1166 forall.t)); 1167 } 1168 1169 /// Lower the concurrent header specification. 1170 void genFIR(const Fortran::parser::ForallConstructStmt &stmt) { 1171 genFIR(std::get< 1172 Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>( 1173 stmt.t) 1174 .value()); 1175 } 1176 1177 void genFIR(const Fortran::parser::CompilerDirective &) { 1178 TODO(toLocation(), "CompilerDirective lowering"); 1179 } 1180 1181 void genFIR(const Fortran::parser::OpenACCConstruct &acc) { 1182 mlir::OpBuilder::InsertPoint insertPt = builder->saveInsertionPoint(); 1183 genOpenACCConstruct(*this, getEval(), acc); 1184 for (Fortran::lower::pft::Evaluation &e : getEval().getNestedEvaluations()) 1185 genFIR(e); 1186 builder->restoreInsertionPoint(insertPt); 1187 } 1188 1189 void genFIR(const Fortran::parser::OpenACCDeclarativeConstruct &accDecl) { 1190 mlir::OpBuilder::InsertPoint insertPt = builder->saveInsertionPoint(); 1191 genOpenACCDeclarativeConstruct(*this, getEval(), accDecl); 1192 for (Fortran::lower::pft::Evaluation &e : getEval().getNestedEvaluations()) 1193 genFIR(e); 1194 builder->restoreInsertionPoint(insertPt); 1195 } 1196 1197 void genFIR(const Fortran::parser::OpenMPConstruct &omp) { 1198 mlir::OpBuilder::InsertPoint insertPt = builder->saveInsertionPoint(); 1199 localSymbols.pushScope(); 1200 Fortran::lower::genOpenMPConstruct(*this, getEval(), omp); 1201 1202 for (Fortran::lower::pft::Evaluation &e : getEval().getNestedEvaluations()) 1203 genFIR(e); 1204 localSymbols.popScope(); 1205 builder->restoreInsertionPoint(insertPt); 1206 } 1207 1208 void genFIR(const Fortran::parser::OpenMPDeclarativeConstruct &ompDecl) { 1209 mlir::OpBuilder::InsertPoint insertPt = builder->saveInsertionPoint(); 1210 genOpenMPDeclarativeConstruct(*this, getEval(), ompDecl); 1211 for (Fortran::lower::pft::Evaluation &e : getEval().getNestedEvaluations()) 1212 genFIR(e); 1213 builder->restoreInsertionPoint(insertPt); 1214 } 1215 1216 /// Generate FIR for a SELECT CASE statement. 1217 /// The type may be CHARACTER, INTEGER, or LOGICAL. 1218 void genFIR(const Fortran::parser::SelectCaseStmt &stmt) { 1219 Fortran::lower::pft::Evaluation &eval = getEval(); 1220 mlir::MLIRContext *context = builder->getContext(); 1221 mlir::Location loc = toLocation(); 1222 Fortran::lower::StatementContext stmtCtx; 1223 const Fortran::lower::SomeExpr *expr = Fortran::semantics::GetExpr( 1224 std::get<Fortran::parser::Scalar<Fortran::parser::Expr>>(stmt.t)); 1225 bool isCharSelector = isCharacterCategory(expr->GetType()->category()); 1226 bool isLogicalSelector = isLogicalCategory(expr->GetType()->category()); 1227 auto charValue = [&](const Fortran::lower::SomeExpr *expr) { 1228 fir::ExtendedValue exv = genExprAddr(*expr, stmtCtx, &loc); 1229 return exv.match( 1230 [&](const fir::CharBoxValue &cbv) { 1231 return fir::factory::CharacterExprHelper{*builder, loc} 1232 .createEmboxChar(cbv.getAddr(), cbv.getLen()); 1233 }, 1234 [&](auto) { 1235 fir::emitFatalError(loc, "not a character"); 1236 return mlir::Value{}; 1237 }); 1238 }; 1239 mlir::Value selector; 1240 if (isCharSelector) { 1241 selector = charValue(expr); 1242 } else { 1243 selector = createFIRExpr(loc, expr, stmtCtx); 1244 if (isLogicalSelector) 1245 selector = builder->createConvert(loc, builder->getI1Type(), selector); 1246 } 1247 mlir::Type selectType = selector.getType(); 1248 llvm::SmallVector<mlir::Attribute> attrList; 1249 llvm::SmallVector<mlir::Value> valueList; 1250 llvm::SmallVector<mlir::Block *> blockList; 1251 mlir::Block *defaultBlock = eval.parentConstruct->constructExit->block; 1252 using CaseValue = Fortran::parser::Scalar<Fortran::parser::ConstantExpr>; 1253 auto addValue = [&](const CaseValue &caseValue) { 1254 const Fortran::lower::SomeExpr *expr = 1255 Fortran::semantics::GetExpr(caseValue.thing); 1256 if (isCharSelector) 1257 valueList.push_back(charValue(expr)); 1258 else if (isLogicalSelector) 1259 valueList.push_back(builder->createConvert( 1260 loc, selectType, createFIRExpr(toLocation(), expr, stmtCtx))); 1261 else 1262 valueList.push_back(builder->createIntegerConstant( 1263 loc, selectType, *Fortran::evaluate::ToInt64(*expr))); 1264 }; 1265 for (Fortran::lower::pft::Evaluation *e = eval.controlSuccessor; e; 1266 e = e->controlSuccessor) { 1267 const auto &caseStmt = e->getIf<Fortran::parser::CaseStmt>(); 1268 assert(e->block && "missing CaseStmt block"); 1269 const auto &caseSelector = 1270 std::get<Fortran::parser::CaseSelector>(caseStmt->t); 1271 const auto *caseValueRangeList = 1272 std::get_if<std::list<Fortran::parser::CaseValueRange>>( 1273 &caseSelector.u); 1274 if (!caseValueRangeList) { 1275 defaultBlock = e->block; 1276 continue; 1277 } 1278 for (const Fortran::parser::CaseValueRange &caseValueRange : 1279 *caseValueRangeList) { 1280 blockList.push_back(e->block); 1281 if (const auto *caseValue = std::get_if<CaseValue>(&caseValueRange.u)) { 1282 attrList.push_back(fir::PointIntervalAttr::get(context)); 1283 addValue(*caseValue); 1284 continue; 1285 } 1286 const auto &caseRange = 1287 std::get<Fortran::parser::CaseValueRange::Range>(caseValueRange.u); 1288 if (caseRange.lower && caseRange.upper) { 1289 attrList.push_back(fir::ClosedIntervalAttr::get(context)); 1290 addValue(*caseRange.lower); 1291 addValue(*caseRange.upper); 1292 } else if (caseRange.lower) { 1293 attrList.push_back(fir::LowerBoundAttr::get(context)); 1294 addValue(*caseRange.lower); 1295 } else { 1296 attrList.push_back(fir::UpperBoundAttr::get(context)); 1297 addValue(*caseRange.upper); 1298 } 1299 } 1300 } 1301 // Skip a logical default block that can never be referenced. 1302 if (isLogicalSelector && attrList.size() == 2) 1303 defaultBlock = eval.parentConstruct->constructExit->block; 1304 attrList.push_back(mlir::UnitAttr::get(context)); 1305 blockList.push_back(defaultBlock); 1306 1307 // Generate a fir::SelectCaseOp. 1308 // Explicit branch code is better for the LOGICAL type. The CHARACTER type 1309 // does not yet have downstream support, and also uses explicit branch code. 1310 // The -no-structured-fir option can be used to force generation of INTEGER 1311 // type branch code. 1312 if (!isLogicalSelector && !isCharSelector && eval.lowerAsStructured()) { 1313 // Numeric selector is a ssa register, all temps that may have 1314 // been generated while evaluating it can be cleaned-up before the 1315 // fir.select_case. 1316 stmtCtx.finalize(); 1317 builder->create<fir::SelectCaseOp>(loc, selector, attrList, valueList, 1318 blockList); 1319 return; 1320 } 1321 1322 // Generate a sequence of case value comparisons and branches. 1323 auto caseValue = valueList.begin(); 1324 auto caseBlock = blockList.begin(); 1325 for (mlir::Attribute attr : attrList) { 1326 if (attr.isa<mlir::UnitAttr>()) { 1327 genFIRBranch(*caseBlock++); 1328 break; 1329 } 1330 auto genCond = [&](mlir::Value rhs, 1331 mlir::arith::CmpIPredicate pred) -> mlir::Value { 1332 if (!isCharSelector) 1333 return builder->create<mlir::arith::CmpIOp>(loc, pred, selector, rhs); 1334 fir::factory::CharacterExprHelper charHelper{*builder, loc}; 1335 std::pair<mlir::Value, mlir::Value> lhsVal = 1336 charHelper.createUnboxChar(selector); 1337 mlir::Value &lhsAddr = lhsVal.first; 1338 mlir::Value &lhsLen = lhsVal.second; 1339 std::pair<mlir::Value, mlir::Value> rhsVal = 1340 charHelper.createUnboxChar(rhs); 1341 mlir::Value &rhsAddr = rhsVal.first; 1342 mlir::Value &rhsLen = rhsVal.second; 1343 return fir::runtime::genCharCompare(*builder, loc, pred, lhsAddr, 1344 lhsLen, rhsAddr, rhsLen); 1345 }; 1346 mlir::Block *newBlock = insertBlock(*caseBlock); 1347 if (attr.isa<fir::ClosedIntervalAttr>()) { 1348 mlir::Block *newBlock2 = insertBlock(*caseBlock); 1349 mlir::Value cond = 1350 genCond(*caseValue++, mlir::arith::CmpIPredicate::sge); 1351 genFIRConditionalBranch(cond, newBlock, newBlock2); 1352 builder->setInsertionPointToEnd(newBlock); 1353 mlir::Value cond2 = 1354 genCond(*caseValue++, mlir::arith::CmpIPredicate::sle); 1355 genFIRConditionalBranch(cond2, *caseBlock++, newBlock2); 1356 builder->setInsertionPointToEnd(newBlock2); 1357 continue; 1358 } 1359 mlir::arith::CmpIPredicate pred; 1360 if (attr.isa<fir::PointIntervalAttr>()) { 1361 pred = mlir::arith::CmpIPredicate::eq; 1362 } else if (attr.isa<fir::LowerBoundAttr>()) { 1363 pred = mlir::arith::CmpIPredicate::sge; 1364 } else { 1365 assert(attr.isa<fir::UpperBoundAttr>() && "unexpected predicate"); 1366 pred = mlir::arith::CmpIPredicate::sle; 1367 } 1368 mlir::Value cond = genCond(*caseValue++, pred); 1369 genFIRConditionalBranch(cond, *caseBlock++, newBlock); 1370 builder->setInsertionPointToEnd(newBlock); 1371 } 1372 assert(caseValue == valueList.end() && caseBlock == blockList.end() && 1373 "select case list mismatch"); 1374 // Clean-up the selector at the end of the construct if it is a temporary 1375 // (which is possible with characters). 1376 mlir::OpBuilder::InsertPoint insertPt = builder->saveInsertionPoint(); 1377 builder->setInsertionPointToEnd(eval.parentConstruct->constructExit->block); 1378 stmtCtx.finalize(); 1379 builder->restoreInsertionPoint(insertPt); 1380 } 1381 1382 fir::ExtendedValue 1383 genAssociateSelector(const Fortran::lower::SomeExpr &selector, 1384 Fortran::lower::StatementContext &stmtCtx) { 1385 return isArraySectionWithoutVectorSubscript(selector) 1386 ? Fortran::lower::createSomeArrayBox(*this, selector, 1387 localSymbols, stmtCtx) 1388 : genExprAddr(selector, stmtCtx); 1389 } 1390 1391 void genFIR(const Fortran::parser::AssociateConstruct &) { 1392 Fortran::lower::StatementContext stmtCtx; 1393 Fortran::lower::pft::Evaluation &eval = getEval(); 1394 for (Fortran::lower::pft::Evaluation &e : eval.getNestedEvaluations()) { 1395 if (auto *stmt = e.getIf<Fortran::parser::AssociateStmt>()) { 1396 if (eval.lowerAsUnstructured()) 1397 maybeStartBlock(e.block); 1398 localSymbols.pushScope(); 1399 for (const Fortran::parser::Association &assoc : 1400 std::get<std::list<Fortran::parser::Association>>(stmt->t)) { 1401 Fortran::semantics::Symbol &sym = 1402 *std::get<Fortran::parser::Name>(assoc.t).symbol; 1403 const Fortran::lower::SomeExpr &selector = 1404 *sym.get<Fortran::semantics::AssocEntityDetails>().expr(); 1405 localSymbols.addSymbol(sym, genAssociateSelector(selector, stmtCtx)); 1406 } 1407 } else if (e.getIf<Fortran::parser::EndAssociateStmt>()) { 1408 if (eval.lowerAsUnstructured()) 1409 maybeStartBlock(e.block); 1410 stmtCtx.finalize(); 1411 localSymbols.popScope(); 1412 } else { 1413 genFIR(e); 1414 } 1415 } 1416 } 1417 1418 void genFIR(const Fortran::parser::BlockConstruct &blockConstruct) { 1419 setCurrentPositionAt(blockConstruct); 1420 TODO(toLocation(), "BlockConstruct lowering"); 1421 } 1422 void genFIR(const Fortran::parser::BlockStmt &) { 1423 TODO(toLocation(), "BlockStmt lowering"); 1424 } 1425 void genFIR(const Fortran::parser::EndBlockStmt &) { 1426 TODO(toLocation(), "EndBlockStmt lowering"); 1427 } 1428 1429 void genFIR(const Fortran::parser::ChangeTeamConstruct &construct) { 1430 TODO(toLocation(), "ChangeTeamConstruct lowering"); 1431 } 1432 void genFIR(const Fortran::parser::ChangeTeamStmt &stmt) { 1433 TODO(toLocation(), "ChangeTeamStmt lowering"); 1434 } 1435 void genFIR(const Fortran::parser::EndChangeTeamStmt &stmt) { 1436 TODO(toLocation(), "EndChangeTeamStmt lowering"); 1437 } 1438 1439 void genFIR(const Fortran::parser::CriticalConstruct &criticalConstruct) { 1440 setCurrentPositionAt(criticalConstruct); 1441 TODO(toLocation(), "CriticalConstruct lowering"); 1442 } 1443 void genFIR(const Fortran::parser::CriticalStmt &) { 1444 TODO(toLocation(), "CriticalStmt lowering"); 1445 } 1446 void genFIR(const Fortran::parser::EndCriticalStmt &) { 1447 TODO(toLocation(), "EndCriticalStmt lowering"); 1448 } 1449 1450 void genFIR(const Fortran::parser::SelectRankConstruct &selectRankConstruct) { 1451 setCurrentPositionAt(selectRankConstruct); 1452 TODO(toLocation(), "SelectRankConstruct lowering"); 1453 } 1454 void genFIR(const Fortran::parser::SelectRankStmt &) { 1455 TODO(toLocation(), "SelectRankStmt lowering"); 1456 } 1457 void genFIR(const Fortran::parser::SelectRankCaseStmt &) { 1458 TODO(toLocation(), "SelectRankCaseStmt lowering"); 1459 } 1460 1461 void genFIR(const Fortran::parser::SelectTypeConstruct &selectTypeConstruct) { 1462 setCurrentPositionAt(selectTypeConstruct); 1463 TODO(toLocation(), "SelectTypeConstruct lowering"); 1464 } 1465 void genFIR(const Fortran::parser::SelectTypeStmt &) { 1466 TODO(toLocation(), "SelectTypeStmt lowering"); 1467 } 1468 void genFIR(const Fortran::parser::TypeGuardStmt &) { 1469 TODO(toLocation(), "TypeGuardStmt lowering"); 1470 } 1471 1472 //===--------------------------------------------------------------------===// 1473 // IO statements (see io.h) 1474 //===--------------------------------------------------------------------===// 1475 1476 void genFIR(const Fortran::parser::BackspaceStmt &stmt) { 1477 mlir::Value iostat = genBackspaceStatement(*this, stmt); 1478 genIoConditionBranches(getEval(), stmt.v, iostat); 1479 } 1480 void genFIR(const Fortran::parser::CloseStmt &stmt) { 1481 mlir::Value iostat = genCloseStatement(*this, stmt); 1482 genIoConditionBranches(getEval(), stmt.v, iostat); 1483 } 1484 void genFIR(const Fortran::parser::EndfileStmt &stmt) { 1485 mlir::Value iostat = genEndfileStatement(*this, stmt); 1486 genIoConditionBranches(getEval(), stmt.v, iostat); 1487 } 1488 void genFIR(const Fortran::parser::FlushStmt &stmt) { 1489 mlir::Value iostat = genFlushStatement(*this, stmt); 1490 genIoConditionBranches(getEval(), stmt.v, iostat); 1491 } 1492 void genFIR(const Fortran::parser::InquireStmt &stmt) { 1493 mlir::Value iostat = genInquireStatement(*this, stmt); 1494 if (const auto *specs = 1495 std::get_if<std::list<Fortran::parser::InquireSpec>>(&stmt.u)) 1496 genIoConditionBranches(getEval(), *specs, iostat); 1497 } 1498 void genFIR(const Fortran::parser::OpenStmt &stmt) { 1499 mlir::Value iostat = genOpenStatement(*this, stmt); 1500 genIoConditionBranches(getEval(), stmt.v, iostat); 1501 } 1502 void genFIR(const Fortran::parser::PrintStmt &stmt) { 1503 genPrintStatement(*this, stmt); 1504 } 1505 void genFIR(const Fortran::parser::ReadStmt &stmt) { 1506 mlir::Value iostat = genReadStatement(*this, stmt); 1507 genIoConditionBranches(getEval(), stmt.controls, iostat); 1508 } 1509 void genFIR(const Fortran::parser::RewindStmt &stmt) { 1510 mlir::Value iostat = genRewindStatement(*this, stmt); 1511 genIoConditionBranches(getEval(), stmt.v, iostat); 1512 } 1513 void genFIR(const Fortran::parser::WaitStmt &stmt) { 1514 mlir::Value iostat = genWaitStatement(*this, stmt); 1515 genIoConditionBranches(getEval(), stmt.v, iostat); 1516 } 1517 void genFIR(const Fortran::parser::WriteStmt &stmt) { 1518 mlir::Value iostat = genWriteStatement(*this, stmt); 1519 genIoConditionBranches(getEval(), stmt.controls, iostat); 1520 } 1521 1522 template <typename A> 1523 void genIoConditionBranches(Fortran::lower::pft::Evaluation &eval, 1524 const A &specList, mlir::Value iostat) { 1525 if (!iostat) 1526 return; 1527 1528 mlir::Block *endBlock = nullptr; 1529 mlir::Block *eorBlock = nullptr; 1530 mlir::Block *errBlock = nullptr; 1531 for (const auto &spec : specList) { 1532 std::visit(Fortran::common::visitors{ 1533 [&](const Fortran::parser::EndLabel &label) { 1534 endBlock = blockOfLabel(eval, label.v); 1535 }, 1536 [&](const Fortran::parser::EorLabel &label) { 1537 eorBlock = blockOfLabel(eval, label.v); 1538 }, 1539 [&](const Fortran::parser::ErrLabel &label) { 1540 errBlock = blockOfLabel(eval, label.v); 1541 }, 1542 [](const auto &) {}}, 1543 spec.u); 1544 } 1545 if (!endBlock && !eorBlock && !errBlock) 1546 return; 1547 1548 mlir::Location loc = toLocation(); 1549 mlir::Type indexType = builder->getIndexType(); 1550 mlir::Value selector = builder->createConvert(loc, indexType, iostat); 1551 llvm::SmallVector<int64_t> indexList; 1552 llvm::SmallVector<mlir::Block *> blockList; 1553 if (eorBlock) { 1554 indexList.push_back(Fortran::runtime::io::IostatEor); 1555 blockList.push_back(eorBlock); 1556 } 1557 if (endBlock) { 1558 indexList.push_back(Fortran::runtime::io::IostatEnd); 1559 blockList.push_back(endBlock); 1560 } 1561 if (errBlock) { 1562 indexList.push_back(0); 1563 blockList.push_back(eval.nonNopSuccessor().block); 1564 // ERR label statement is the default successor. 1565 blockList.push_back(errBlock); 1566 } else { 1567 // Fallthrough successor statement is the default successor. 1568 blockList.push_back(eval.nonNopSuccessor().block); 1569 } 1570 builder->create<fir::SelectOp>(loc, selector, indexList, blockList); 1571 } 1572 1573 //===--------------------------------------------------------------------===// 1574 // Memory allocation and deallocation 1575 //===--------------------------------------------------------------------===// 1576 1577 void genFIR(const Fortran::parser::AllocateStmt &stmt) { 1578 Fortran::lower::genAllocateStmt(*this, stmt, toLocation()); 1579 } 1580 1581 void genFIR(const Fortran::parser::DeallocateStmt &stmt) { 1582 Fortran::lower::genDeallocateStmt(*this, stmt, toLocation()); 1583 } 1584 1585 /// Nullify pointer object list 1586 /// 1587 /// For each pointer object, reset the pointer to a disassociated status. 1588 /// We do this by setting each pointer to null. 1589 void genFIR(const Fortran::parser::NullifyStmt &stmt) { 1590 mlir::Location loc = toLocation(); 1591 for (auto &pointerObject : stmt.v) { 1592 const Fortran::lower::SomeExpr *expr = 1593 Fortran::semantics::GetExpr(pointerObject); 1594 assert(expr); 1595 fir::MutableBoxValue box = genExprMutableBox(loc, *expr); 1596 fir::factory::disassociateMutableBox(*builder, loc, box); 1597 } 1598 } 1599 1600 //===--------------------------------------------------------------------===// 1601 1602 void genFIR(const Fortran::parser::EventPostStmt &stmt) { 1603 genEventPostStatement(*this, stmt); 1604 } 1605 1606 void genFIR(const Fortran::parser::EventWaitStmt &stmt) { 1607 genEventWaitStatement(*this, stmt); 1608 } 1609 1610 void genFIR(const Fortran::parser::FormTeamStmt &stmt) { 1611 genFormTeamStatement(*this, getEval(), stmt); 1612 } 1613 1614 void genFIR(const Fortran::parser::LockStmt &stmt) { 1615 genLockStatement(*this, stmt); 1616 } 1617 1618 fir::ExtendedValue 1619 genInitializerExprValue(const Fortran::lower::SomeExpr &expr, 1620 Fortran::lower::StatementContext &stmtCtx) { 1621 return Fortran::lower::createSomeInitializerExpression( 1622 toLocation(), *this, expr, localSymbols, stmtCtx); 1623 } 1624 1625 /// Return true if the current context is a conditionalized and implied 1626 /// iteration space. 1627 bool implicitIterationSpace() { return !implicitIterSpace.empty(); } 1628 1629 /// Return true if context is currently an explicit iteration space. A scalar 1630 /// assignment expression may be contextually within a user-defined iteration 1631 /// space, transforming it into an array expression. 1632 bool explicitIterationSpace() { return explicitIterSpace.isActive(); } 1633 1634 /// Generate an array assignment. 1635 /// This is an assignment expression with rank > 0. The assignment may or may 1636 /// not be in a WHERE and/or FORALL context. 1637 void genArrayAssignment(const Fortran::evaluate::Assignment &assign, 1638 Fortran::lower::StatementContext &stmtCtx) { 1639 if (isWholeAllocatable(assign.lhs)) { 1640 // Assignment to allocatables may require the lhs to be 1641 // deallocated/reallocated. See Fortran 2018 10.2.1.3 p3 1642 Fortran::lower::createAllocatableArrayAssignment( 1643 *this, assign.lhs, assign.rhs, explicitIterSpace, implicitIterSpace, 1644 localSymbols, stmtCtx); 1645 return; 1646 } 1647 1648 if (!implicitIterationSpace() && !explicitIterationSpace()) { 1649 // No masks and the iteration space is implied by the array, so create a 1650 // simple array assignment. 1651 Fortran::lower::createSomeArrayAssignment(*this, assign.lhs, assign.rhs, 1652 localSymbols, stmtCtx); 1653 return; 1654 } 1655 1656 // If there is an explicit iteration space, generate an array assignment 1657 // with a user-specified iteration space and possibly with masks. These 1658 // assignments may *appear* to be scalar expressions, but the scalar 1659 // expression is evaluated at all points in the user-defined space much like 1660 // an ordinary array assignment. More specifically, the semantics inside the 1661 // FORALL much more closely resembles that of WHERE than a scalar 1662 // assignment. 1663 // Otherwise, generate a masked array assignment. The iteration space is 1664 // implied by the lhs array expression. 1665 Fortran::lower::createAnyMaskedArrayAssignment( 1666 *this, assign.lhs, assign.rhs, explicitIterSpace, implicitIterSpace, 1667 localSymbols, 1668 explicitIterationSpace() ? explicitIterSpace.stmtContext() 1669 : implicitIterSpace.stmtContext()); 1670 } 1671 1672 static bool 1673 isArraySectionWithoutVectorSubscript(const Fortran::lower::SomeExpr &expr) { 1674 return expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) && 1675 !Fortran::evaluate::UnwrapWholeSymbolDataRef(expr) && 1676 !Fortran::evaluate::HasVectorSubscript(expr); 1677 } 1678 1679 #if !defined(NDEBUG) 1680 static bool isFuncResultDesignator(const Fortran::lower::SomeExpr &expr) { 1681 const Fortran::semantics::Symbol *sym = 1682 Fortran::evaluate::GetFirstSymbol(expr); 1683 return sym && sym->IsFuncResult(); 1684 } 1685 #endif 1686 1687 static bool isWholeAllocatable(const Fortran::lower::SomeExpr &expr) { 1688 const Fortran::semantics::Symbol *sym = 1689 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr); 1690 return sym && Fortran::semantics::IsAllocatable(*sym); 1691 } 1692 1693 /// Shared for both assignments and pointer assignments. 1694 void genAssignment(const Fortran::evaluate::Assignment &assign) { 1695 Fortran::lower::StatementContext stmtCtx; 1696 mlir::Location loc = toLocation(); 1697 if (explicitIterationSpace()) { 1698 Fortran::lower::createArrayLoads(*this, explicitIterSpace, localSymbols); 1699 explicitIterSpace.genLoopNest(); 1700 } 1701 std::visit( 1702 Fortran::common::visitors{ 1703 // [1] Plain old assignment. 1704 [&](const Fortran::evaluate::Assignment::Intrinsic &) { 1705 const Fortran::semantics::Symbol *sym = 1706 Fortran::evaluate::GetLastSymbol(assign.lhs); 1707 1708 if (!sym) 1709 TODO(loc, "assignment to pointer result of function reference"); 1710 1711 std::optional<Fortran::evaluate::DynamicType> lhsType = 1712 assign.lhs.GetType(); 1713 assert(lhsType && "lhs cannot be typeless"); 1714 // Assignment to polymorphic allocatables may require changing the 1715 // variable dynamic type (See Fortran 2018 10.2.1.3 p3). 1716 if (lhsType->IsPolymorphic() && isWholeAllocatable(assign.lhs)) 1717 TODO(loc, "assignment to polymorphic allocatable"); 1718 1719 // Note: No ad-hoc handling for pointers is required here. The 1720 // target will be assigned as per 2018 10.2.1.3 p2. genExprAddr 1721 // on a pointer returns the target address and not the address of 1722 // the pointer variable. 1723 1724 if (assign.lhs.Rank() > 0 || explicitIterationSpace()) { 1725 // Array assignment 1726 // See Fortran 2018 10.2.1.3 p5, p6, and p7 1727 genArrayAssignment(assign, stmtCtx); 1728 return; 1729 } 1730 1731 // Scalar assignment 1732 const bool isNumericScalar = 1733 isNumericScalarCategory(lhsType->category()); 1734 fir::ExtendedValue rhs = isNumericScalar 1735 ? genExprValue(assign.rhs, stmtCtx) 1736 : genExprAddr(assign.rhs, stmtCtx); 1737 bool lhsIsWholeAllocatable = isWholeAllocatable(assign.lhs); 1738 llvm::Optional<fir::factory::MutableBoxReallocation> lhsRealloc; 1739 llvm::Optional<fir::MutableBoxValue> lhsMutableBox; 1740 auto lhs = [&]() -> fir::ExtendedValue { 1741 if (lhsIsWholeAllocatable) { 1742 lhsMutableBox = genExprMutableBox(loc, assign.lhs); 1743 llvm::SmallVector<mlir::Value> lengthParams; 1744 if (const fir::CharBoxValue *charBox = rhs.getCharBox()) 1745 lengthParams.push_back(charBox->getLen()); 1746 else if (fir::isDerivedWithLengthParameters(rhs)) 1747 TODO(loc, "assignment to derived type allocatable with " 1748 "length parameters"); 1749 lhsRealloc = fir::factory::genReallocIfNeeded( 1750 *builder, loc, *lhsMutableBox, 1751 /*shape=*/llvm::None, lengthParams); 1752 return lhsRealloc->newValue; 1753 } 1754 return genExprAddr(assign.lhs, stmtCtx); 1755 }(); 1756 1757 if (isNumericScalar) { 1758 // Fortran 2018 10.2.1.3 p8 and p9 1759 // Conversions should have been inserted by semantic analysis, 1760 // but they can be incorrect between the rhs and lhs. Correct 1761 // that here. 1762 mlir::Value addr = fir::getBase(lhs); 1763 mlir::Value val = fir::getBase(rhs); 1764 // A function with multiple entry points returning different 1765 // types tags all result variables with one of the largest 1766 // types to allow them to share the same storage. Assignment 1767 // to a result variable of one of the other types requires 1768 // conversion to the actual type. 1769 mlir::Type toTy = genType(assign.lhs); 1770 mlir::Value cast = 1771 builder->convertWithSemantics(loc, toTy, val); 1772 if (fir::dyn_cast_ptrEleTy(addr.getType()) != toTy) { 1773 assert(isFuncResultDesignator(assign.lhs) && "type mismatch"); 1774 addr = builder->createConvert( 1775 toLocation(), builder->getRefType(toTy), addr); 1776 } 1777 builder->create<fir::StoreOp>(loc, cast, addr); 1778 } else if (isCharacterCategory(lhsType->category())) { 1779 // Fortran 2018 10.2.1.3 p10 and p11 1780 fir::factory::CharacterExprHelper{*builder, loc}.createAssign( 1781 lhs, rhs); 1782 } else if (isDerivedCategory(lhsType->category())) { 1783 // Fortran 2018 10.2.1.3 p13 and p14 1784 // Recursively gen an assignment on each element pair. 1785 fir::factory::genRecordAssignment(*builder, loc, lhs, rhs); 1786 } else { 1787 llvm_unreachable("unknown category"); 1788 } 1789 if (lhsIsWholeAllocatable) 1790 fir::factory::finalizeRealloc( 1791 *builder, loc, lhsMutableBox.getValue(), 1792 /*lbounds=*/llvm::None, /*takeLboundsIfRealloc=*/false, 1793 lhsRealloc.getValue()); 1794 }, 1795 1796 // [2] User defined assignment. If the context is a scalar 1797 // expression then call the procedure. 1798 [&](const Fortran::evaluate::ProcedureRef &procRef) { 1799 Fortran::lower::StatementContext &ctx = 1800 explicitIterationSpace() ? explicitIterSpace.stmtContext() 1801 : stmtCtx; 1802 Fortran::lower::createSubroutineCall( 1803 *this, procRef, explicitIterSpace, implicitIterSpace, 1804 localSymbols, ctx, /*isUserDefAssignment=*/true); 1805 }, 1806 1807 // [3] Pointer assignment with possibly empty bounds-spec. R1035: a 1808 // bounds-spec is a lower bound value. 1809 [&](const Fortran::evaluate::Assignment::BoundsSpec &lbExprs) { 1810 if (IsProcedure(assign.rhs)) 1811 TODO(loc, "procedure pointer assignment"); 1812 std::optional<Fortran::evaluate::DynamicType> lhsType = 1813 assign.lhs.GetType(); 1814 std::optional<Fortran::evaluate::DynamicType> rhsType = 1815 assign.rhs.GetType(); 1816 // Polymorphic lhs/rhs may need more care. See F2018 10.2.2.3. 1817 if ((lhsType && lhsType->IsPolymorphic()) || 1818 (rhsType && rhsType->IsPolymorphic())) 1819 TODO(loc, "pointer assignment involving polymorphic entity"); 1820 1821 // FIXME: in the explicit space context, we want to use 1822 // ScalarArrayExprLowering here. 1823 fir::MutableBoxValue lhs = genExprMutableBox(loc, assign.lhs); 1824 llvm::SmallVector<mlir::Value> lbounds; 1825 for (const Fortran::evaluate::ExtentExpr &lbExpr : lbExprs) 1826 lbounds.push_back( 1827 fir::getBase(genExprValue(toEvExpr(lbExpr), stmtCtx))); 1828 Fortran::lower::associateMutableBox(*this, loc, lhs, assign.rhs, 1829 lbounds, stmtCtx); 1830 if (explicitIterationSpace()) { 1831 mlir::ValueRange inners = explicitIterSpace.getInnerArgs(); 1832 if (!inners.empty()) { 1833 // TODO: should force a copy-in/copy-out here. 1834 // e.g., obj%ptr(i+1) => obj%ptr(i) 1835 builder->create<fir::ResultOp>(loc, inners); 1836 } 1837 } 1838 }, 1839 1840 // [4] Pointer assignment with bounds-remapping. R1036: a 1841 // bounds-remapping is a pair, lower bound and upper bound. 1842 [&](const Fortran::evaluate::Assignment::BoundsRemapping 1843 &boundExprs) { 1844 std::optional<Fortran::evaluate::DynamicType> lhsType = 1845 assign.lhs.GetType(); 1846 std::optional<Fortran::evaluate::DynamicType> rhsType = 1847 assign.rhs.GetType(); 1848 // Polymorphic lhs/rhs may need more care. See F2018 10.2.2.3. 1849 if ((lhsType && lhsType->IsPolymorphic()) || 1850 (rhsType && rhsType->IsPolymorphic())) 1851 TODO(loc, "pointer assignment involving polymorphic entity"); 1852 1853 // FIXME: in the explicit space context, we want to use 1854 // ScalarArrayExprLowering here. 1855 fir::MutableBoxValue lhs = genExprMutableBox(loc, assign.lhs); 1856 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 1857 assign.rhs)) { 1858 fir::factory::disassociateMutableBox(*builder, loc, lhs); 1859 return; 1860 } 1861 llvm::SmallVector<mlir::Value> lbounds; 1862 llvm::SmallVector<mlir::Value> ubounds; 1863 for (const std::pair<Fortran::evaluate::ExtentExpr, 1864 Fortran::evaluate::ExtentExpr> &pair : 1865 boundExprs) { 1866 const Fortran::evaluate::ExtentExpr &lbExpr = pair.first; 1867 const Fortran::evaluate::ExtentExpr &ubExpr = pair.second; 1868 lbounds.push_back( 1869 fir::getBase(genExprValue(toEvExpr(lbExpr), stmtCtx))); 1870 ubounds.push_back( 1871 fir::getBase(genExprValue(toEvExpr(ubExpr), stmtCtx))); 1872 } 1873 // Do not generate a temp in case rhs is an array section. 1874 fir::ExtendedValue rhs = 1875 isArraySectionWithoutVectorSubscript(assign.rhs) 1876 ? Fortran::lower::createSomeArrayBox( 1877 *this, assign.rhs, localSymbols, stmtCtx) 1878 : genExprAddr(assign.rhs, stmtCtx); 1879 fir::factory::associateMutableBoxWithRemap(*builder, loc, lhs, 1880 rhs, lbounds, ubounds); 1881 if (explicitIterationSpace()) { 1882 mlir::ValueRange inners = explicitIterSpace.getInnerArgs(); 1883 if (!inners.empty()) { 1884 // TODO: should force a copy-in/copy-out here. 1885 // e.g., obj%ptr(i+1) => obj%ptr(i) 1886 builder->create<fir::ResultOp>(loc, inners); 1887 } 1888 } 1889 }, 1890 }, 1891 assign.u); 1892 if (explicitIterationSpace()) 1893 Fortran::lower::createArrayMergeStores(*this, explicitIterSpace); 1894 } 1895 1896 void genFIR(const Fortran::parser::WhereConstruct &c) { 1897 implicitIterSpace.growStack(); 1898 genNestedStatement( 1899 std::get< 1900 Fortran::parser::Statement<Fortran::parser::WhereConstructStmt>>( 1901 c.t)); 1902 for (const auto &body : 1903 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(c.t)) 1904 genFIR(body); 1905 for (const auto &e : 1906 std::get<std::list<Fortran::parser::WhereConstruct::MaskedElsewhere>>( 1907 c.t)) 1908 genFIR(e); 1909 if (const auto &e = 1910 std::get<std::optional<Fortran::parser::WhereConstruct::Elsewhere>>( 1911 c.t); 1912 e.has_value()) 1913 genFIR(*e); 1914 genNestedStatement( 1915 std::get<Fortran::parser::Statement<Fortran::parser::EndWhereStmt>>( 1916 c.t)); 1917 } 1918 void genFIR(const Fortran::parser::WhereBodyConstruct &body) { 1919 std::visit( 1920 Fortran::common::visitors{ 1921 [&](const Fortran::parser::Statement< 1922 Fortran::parser::AssignmentStmt> &stmt) { 1923 genNestedStatement(stmt); 1924 }, 1925 [&](const Fortran::parser::Statement<Fortran::parser::WhereStmt> 1926 &stmt) { genNestedStatement(stmt); }, 1927 [&](const Fortran::common::Indirection< 1928 Fortran::parser::WhereConstruct> &c) { genFIR(c.value()); }, 1929 }, 1930 body.u); 1931 } 1932 void genFIR(const Fortran::parser::WhereConstructStmt &stmt) { 1933 implicitIterSpace.append(Fortran::semantics::GetExpr( 1934 std::get<Fortran::parser::LogicalExpr>(stmt.t))); 1935 } 1936 void genFIR(const Fortran::parser::WhereConstruct::MaskedElsewhere &ew) { 1937 genNestedStatement( 1938 std::get< 1939 Fortran::parser::Statement<Fortran::parser::MaskedElsewhereStmt>>( 1940 ew.t)); 1941 for (const auto &body : 1942 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew.t)) 1943 genFIR(body); 1944 } 1945 void genFIR(const Fortran::parser::MaskedElsewhereStmt &stmt) { 1946 implicitIterSpace.append(Fortran::semantics::GetExpr( 1947 std::get<Fortran::parser::LogicalExpr>(stmt.t))); 1948 } 1949 void genFIR(const Fortran::parser::WhereConstruct::Elsewhere &ew) { 1950 genNestedStatement( 1951 std::get<Fortran::parser::Statement<Fortran::parser::ElsewhereStmt>>( 1952 ew.t)); 1953 for (const auto &body : 1954 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew.t)) 1955 genFIR(body); 1956 } 1957 void genFIR(const Fortran::parser::ElsewhereStmt &stmt) { 1958 implicitIterSpace.append(nullptr); 1959 } 1960 void genFIR(const Fortran::parser::EndWhereStmt &) { 1961 implicitIterSpace.shrinkStack(); 1962 } 1963 1964 void genFIR(const Fortran::parser::WhereStmt &stmt) { 1965 Fortran::lower::StatementContext stmtCtx; 1966 const auto &assign = std::get<Fortran::parser::AssignmentStmt>(stmt.t); 1967 implicitIterSpace.growStack(); 1968 implicitIterSpace.append(Fortran::semantics::GetExpr( 1969 std::get<Fortran::parser::LogicalExpr>(stmt.t))); 1970 genAssignment(*assign.typedAssignment->v); 1971 implicitIterSpace.shrinkStack(); 1972 } 1973 1974 void genFIR(const Fortran::parser::PointerAssignmentStmt &stmt) { 1975 genAssignment(*stmt.typedAssignment->v); 1976 } 1977 1978 void genFIR(const Fortran::parser::AssignmentStmt &stmt) { 1979 genAssignment(*stmt.typedAssignment->v); 1980 } 1981 1982 void genFIR(const Fortran::parser::SyncAllStmt &stmt) { 1983 genSyncAllStatement(*this, stmt); 1984 } 1985 1986 void genFIR(const Fortran::parser::SyncImagesStmt &stmt) { 1987 genSyncImagesStatement(*this, stmt); 1988 } 1989 1990 void genFIR(const Fortran::parser::SyncMemoryStmt &stmt) { 1991 genSyncMemoryStatement(*this, stmt); 1992 } 1993 1994 void genFIR(const Fortran::parser::SyncTeamStmt &stmt) { 1995 genSyncTeamStatement(*this, stmt); 1996 } 1997 1998 void genFIR(const Fortran::parser::UnlockStmt &stmt) { 1999 genUnlockStatement(*this, stmt); 2000 } 2001 2002 void genFIR(const Fortran::parser::AssignStmt &stmt) { 2003 const Fortran::semantics::Symbol &symbol = 2004 *std::get<Fortran::parser::Name>(stmt.t).symbol; 2005 mlir::Location loc = toLocation(); 2006 mlir::Value labelValue = builder->createIntegerConstant( 2007 loc, genType(symbol), std::get<Fortran::parser::Label>(stmt.t)); 2008 builder->create<fir::StoreOp>(loc, labelValue, getSymbolAddress(symbol)); 2009 } 2010 2011 void genFIR(const Fortran::parser::FormatStmt &) { 2012 // do nothing. 2013 2014 // FORMAT statements have no semantics. They may be lowered if used by a 2015 // data transfer statement. 2016 } 2017 2018 void genFIR(const Fortran::parser::PauseStmt &stmt) { 2019 genPauseStatement(*this, stmt); 2020 } 2021 2022 // call FAIL IMAGE in runtime 2023 void genFIR(const Fortran::parser::FailImageStmt &stmt) { 2024 genFailImageStatement(*this); 2025 } 2026 2027 // call STOP, ERROR STOP in runtime 2028 void genFIR(const Fortran::parser::StopStmt &stmt) { 2029 genStopStatement(*this, stmt); 2030 } 2031 2032 void genFIR(const Fortran::parser::ReturnStmt &stmt) { 2033 Fortran::lower::pft::FunctionLikeUnit *funit = 2034 getEval().getOwningProcedure(); 2035 assert(funit && "not inside main program, function or subroutine"); 2036 if (funit->isMainProgram()) { 2037 genExitRoutine(); 2038 return; 2039 } 2040 mlir::Location loc = toLocation(); 2041 if (stmt.v) { 2042 // Alternate return statement - If this is a subroutine where some 2043 // alternate entries have alternate returns, but the active entry point 2044 // does not, ignore the alternate return value. Otherwise, assign it 2045 // to the compiler-generated result variable. 2046 const Fortran::semantics::Symbol &symbol = funit->getSubprogramSymbol(); 2047 if (Fortran::semantics::HasAlternateReturns(symbol)) { 2048 Fortran::lower::StatementContext stmtCtx; 2049 const Fortran::lower::SomeExpr *expr = 2050 Fortran::semantics::GetExpr(*stmt.v); 2051 assert(expr && "missing alternate return expression"); 2052 mlir::Value altReturnIndex = builder->createConvert( 2053 loc, builder->getIndexType(), createFIRExpr(loc, expr, stmtCtx)); 2054 builder->create<fir::StoreOp>(loc, altReturnIndex, 2055 getAltReturnResult(symbol)); 2056 } 2057 } 2058 // Branch to the last block of the SUBROUTINE, which has the actual return. 2059 if (!funit->finalBlock) { 2060 mlir::OpBuilder::InsertPoint insPt = builder->saveInsertionPoint(); 2061 funit->finalBlock = builder->createBlock(&builder->getRegion()); 2062 builder->restoreInsertionPoint(insPt); 2063 } 2064 builder->create<mlir::cf::BranchOp>(loc, funit->finalBlock); 2065 } 2066 2067 void genFIR(const Fortran::parser::CycleStmt &) { 2068 genFIRBranch(getEval().controlSuccessor->block); 2069 } 2070 void genFIR(const Fortran::parser::ExitStmt &) { 2071 genFIRBranch(getEval().controlSuccessor->block); 2072 } 2073 void genFIR(const Fortran::parser::GotoStmt &) { 2074 genFIRBranch(getEval().controlSuccessor->block); 2075 } 2076 2077 void genFIR(const Fortran::parser::EndDoStmt &) { 2078 TODO(toLocation(), "EndDoStmt lowering"); 2079 } 2080 2081 // Nop statements - No code, or code is generated at the construct level. 2082 void genFIR(const Fortran::parser::AssociateStmt &) {} // nop 2083 void genFIR(const Fortran::parser::CaseStmt &) {} // nop 2084 void genFIR(const Fortran::parser::ContinueStmt &) {} // nop 2085 void genFIR(const Fortran::parser::ElseIfStmt &) {} // nop 2086 void genFIR(const Fortran::parser::ElseStmt &) {} // nop 2087 void genFIR(const Fortran::parser::EndAssociateStmt &) {} // nop 2088 void genFIR(const Fortran::parser::EndFunctionStmt &) {} // nop 2089 void genFIR(const Fortran::parser::EndIfStmt &) {} // nop 2090 void genFIR(const Fortran::parser::EndMpSubprogramStmt &) {} // nop 2091 void genFIR(const Fortran::parser::EndSelectStmt &) {} // nop 2092 void genFIR(const Fortran::parser::EndSubroutineStmt &) {} // nop 2093 void genFIR(const Fortran::parser::EntryStmt &) {} // nop 2094 void genFIR(const Fortran::parser::IfStmt &) {} // nop 2095 void genFIR(const Fortran::parser::IfThenStmt &) {} // nop 2096 2097 void genFIR(const Fortran::parser::NonLabelDoStmt &) { 2098 TODO(toLocation(), "NonLabelDoStmt lowering"); 2099 } 2100 2101 void genFIR(const Fortran::parser::OmpEndLoopDirective &) { 2102 TODO(toLocation(), "OmpEndLoopDirective lowering"); 2103 } 2104 2105 void genFIR(const Fortran::parser::NamelistStmt &) { 2106 TODO(toLocation(), "NamelistStmt lowering"); 2107 } 2108 2109 /// Generate FIR for the Evaluation `eval`. 2110 void genFIR(Fortran::lower::pft::Evaluation &eval, 2111 bool unstructuredContext = true) { 2112 if (unstructuredContext) { 2113 // When transitioning from unstructured to structured code, 2114 // the structured code could be a target that starts a new block. 2115 maybeStartBlock(eval.isConstruct() && eval.lowerAsStructured() 2116 ? eval.getFirstNestedEvaluation().block 2117 : eval.block); 2118 } 2119 2120 setCurrentEval(eval); 2121 setCurrentPosition(eval.position); 2122 eval.visit([&](const auto &stmt) { genFIR(stmt); }); 2123 2124 if (unstructuredContext && blockIsUnterminated()) { 2125 // Exit from an unstructured IF or SELECT construct block. 2126 Fortran::lower::pft::Evaluation *successor{}; 2127 if (eval.isActionStmt()) 2128 successor = eval.controlSuccessor; 2129 else if (eval.isConstruct() && 2130 eval.getLastNestedEvaluation() 2131 .lexicalSuccessor->isIntermediateConstructStmt()) 2132 successor = eval.constructExit; 2133 if (successor && successor->block) 2134 genFIRBranch(successor->block); 2135 } 2136 } 2137 2138 /// Map mlir function block arguments to the corresponding Fortran dummy 2139 /// variables. When the result is passed as a hidden argument, the Fortran 2140 /// result is also mapped. The symbol map is used to hold this mapping. 2141 void mapDummiesAndResults(Fortran::lower::pft::FunctionLikeUnit &funit, 2142 const Fortran::lower::CalleeInterface &callee) { 2143 assert(builder && "require a builder object at this point"); 2144 using PassBy = Fortran::lower::CalleeInterface::PassEntityBy; 2145 auto mapPassedEntity = [&](const auto arg) -> void { 2146 if (arg.passBy == PassBy::AddressAndLength) { 2147 // TODO: now that fir call has some attributes regarding character 2148 // return, PassBy::AddressAndLength should be retired. 2149 mlir::Location loc = toLocation(); 2150 fir::factory::CharacterExprHelper charHelp{*builder, loc}; 2151 mlir::Value box = 2152 charHelp.createEmboxChar(arg.firArgument, arg.firLength); 2153 addSymbol(arg.entity->get(), box); 2154 } else { 2155 if (arg.entity.has_value()) { 2156 addSymbol(arg.entity->get(), arg.firArgument); 2157 } else { 2158 assert(funit.parentHasHostAssoc()); 2159 funit.parentHostAssoc().internalProcedureBindings(*this, 2160 localSymbols); 2161 } 2162 } 2163 }; 2164 for (const Fortran::lower::CalleeInterface::PassedEntity &arg : 2165 callee.getPassedArguments()) 2166 mapPassedEntity(arg); 2167 2168 // Allocate local skeleton instances of dummies from other entry points. 2169 // Most of these locals will not survive into final generated code, but 2170 // some will. It is illegal to reference them at run time if they do. 2171 for (const Fortran::semantics::Symbol *arg : 2172 funit.nonUniversalDummyArguments) { 2173 if (lookupSymbol(*arg)) 2174 continue; 2175 mlir::Type type = genType(*arg); 2176 // TODO: Account for VALUE arguments (and possibly other variants). 2177 type = builder->getRefType(type); 2178 addSymbol(*arg, builder->create<fir::UndefOp>(toLocation(), type)); 2179 } 2180 if (std::optional<Fortran::lower::CalleeInterface::PassedEntity> 2181 passedResult = callee.getPassedResult()) { 2182 mapPassedEntity(*passedResult); 2183 // FIXME: need to make sure things are OK here. addSymbol may not be OK 2184 if (funit.primaryResult && 2185 passedResult->entity->get() != *funit.primaryResult) 2186 addSymbol(*funit.primaryResult, 2187 getSymbolAddress(passedResult->entity->get())); 2188 } 2189 } 2190 2191 /// Instantiate variable \p var and add it to the symbol map. 2192 /// See ConvertVariable.cpp. 2193 void instantiateVar(const Fortran::lower::pft::Variable &var, 2194 Fortran::lower::AggregateStoreMap &storeMap) { 2195 Fortran::lower::instantiateVariable(*this, var, localSymbols, storeMap); 2196 } 2197 2198 /// Prepare to translate a new function 2199 void startNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) { 2200 assert(!builder && "expected nullptr"); 2201 Fortran::lower::CalleeInterface callee(funit, *this); 2202 mlir::func::FuncOp func = callee.addEntryBlockAndMapArguments(); 2203 builder = new fir::FirOpBuilder(func, bridge.getKindMap()); 2204 assert(builder && "FirOpBuilder did not instantiate"); 2205 builder->setInsertionPointToStart(&func.front()); 2206 func.setVisibility(mlir::SymbolTable::Visibility::Public); 2207 2208 mapDummiesAndResults(funit, callee); 2209 2210 // Note: not storing Variable references because getOrderedSymbolTable 2211 // below returns a temporary. 2212 llvm::SmallVector<Fortran::lower::pft::Variable> deferredFuncResultList; 2213 2214 // Backup actual argument for entry character results 2215 // with different lengths. It needs to be added to the non 2216 // primary results symbol before mapSymbolAttributes is called. 2217 Fortran::lower::SymbolBox resultArg; 2218 if (std::optional<Fortran::lower::CalleeInterface::PassedEntity> 2219 passedResult = callee.getPassedResult()) 2220 resultArg = lookupSymbol(passedResult->entity->get()); 2221 2222 Fortran::lower::AggregateStoreMap storeMap; 2223 // The front-end is currently not adding module variables referenced 2224 // in a module procedure as host associated. As a result we need to 2225 // instantiate all module variables here if this is a module procedure. 2226 // It is likely that the front-end behavior should change here. 2227 // This also applies to internal procedures inside module procedures. 2228 if (auto *module = Fortran::lower::pft::getAncestor< 2229 Fortran::lower::pft::ModuleLikeUnit>(funit)) 2230 for (const Fortran::lower::pft::Variable &var : 2231 module->getOrderedSymbolTable()) 2232 instantiateVar(var, storeMap); 2233 2234 mlir::Value primaryFuncResultStorage; 2235 for (const Fortran::lower::pft::Variable &var : 2236 funit.getOrderedSymbolTable()) { 2237 // Always instantiate aggregate storage blocks. 2238 if (var.isAggregateStore()) { 2239 instantiateVar(var, storeMap); 2240 continue; 2241 } 2242 const Fortran::semantics::Symbol &sym = var.getSymbol(); 2243 if (funit.parentHasHostAssoc()) { 2244 // Never instantitate host associated variables, as they are already 2245 // instantiated from an argument tuple. Instead, just bind the symbol to 2246 // the reference to the host variable, which must be in the map. 2247 const Fortran::semantics::Symbol &ultimate = sym.GetUltimate(); 2248 if (funit.parentHostAssoc().isAssociated(ultimate)) { 2249 Fortran::lower::SymbolBox hostBox = 2250 localSymbols.lookupSymbol(ultimate); 2251 assert(hostBox && "host association is not in map"); 2252 localSymbols.addSymbol(sym, hostBox.toExtendedValue()); 2253 continue; 2254 } 2255 } 2256 if (!sym.IsFuncResult() || !funit.primaryResult) { 2257 instantiateVar(var, storeMap); 2258 } else if (&sym == funit.primaryResult) { 2259 instantiateVar(var, storeMap); 2260 primaryFuncResultStorage = getSymbolAddress(sym); 2261 } else { 2262 deferredFuncResultList.push_back(var); 2263 } 2264 } 2265 2266 // If this is a host procedure with host associations, then create the tuple 2267 // of pointers for passing to the internal procedures. 2268 if (!funit.getHostAssoc().empty()) 2269 funit.getHostAssoc().hostProcedureBindings(*this, localSymbols); 2270 2271 /// TODO: should use same mechanism as equivalence? 2272 /// One blocking point is character entry returns that need special handling 2273 /// since they are not locally allocated but come as argument. CHARACTER(*) 2274 /// is not something that fit wells with equivalence lowering. 2275 for (const Fortran::lower::pft::Variable &altResult : 2276 deferredFuncResultList) { 2277 if (std::optional<Fortran::lower::CalleeInterface::PassedEntity> 2278 passedResult = callee.getPassedResult()) 2279 addSymbol(altResult.getSymbol(), resultArg.getAddr()); 2280 Fortran::lower::StatementContext stmtCtx; 2281 Fortran::lower::mapSymbolAttributes(*this, altResult, localSymbols, 2282 stmtCtx, primaryFuncResultStorage); 2283 } 2284 2285 // Create most function blocks in advance. 2286 createEmptyBlocks(funit.evaluationList); 2287 2288 // Reinstate entry block as the current insertion point. 2289 builder->setInsertionPointToEnd(&func.front()); 2290 2291 if (callee.hasAlternateReturns()) { 2292 // Create a local temp to hold the alternate return index. 2293 // Give it an integer index type and the subroutine name (for dumps). 2294 // Attach it to the subroutine symbol in the localSymbols map. 2295 // Initialize it to zero, the "fallthrough" alternate return value. 2296 const Fortran::semantics::Symbol &symbol = funit.getSubprogramSymbol(); 2297 mlir::Location loc = toLocation(); 2298 mlir::Type idxTy = builder->getIndexType(); 2299 mlir::Value altResult = 2300 builder->createTemporary(loc, idxTy, toStringRef(symbol.name())); 2301 addSymbol(symbol, altResult); 2302 mlir::Value zero = builder->createIntegerConstant(loc, idxTy, 0); 2303 builder->create<fir::StoreOp>(loc, zero, altResult); 2304 } 2305 2306 if (Fortran::lower::pft::Evaluation *alternateEntryEval = 2307 funit.getEntryEval()) 2308 genFIRBranch(alternateEntryEval->lexicalSuccessor->block); 2309 } 2310 2311 /// Create global blocks for the current function. This eliminates the 2312 /// distinction between forward and backward targets when generating 2313 /// branches. A block is "global" if it can be the target of a GOTO or 2314 /// other source code branch. A block that can only be targeted by a 2315 /// compiler generated branch is "local". For example, a DO loop preheader 2316 /// block containing loop initialization code is global. A loop header 2317 /// block, which is the target of the loop back edge, is local. Blocks 2318 /// belong to a region. Any block within a nested region must be replaced 2319 /// with a block belonging to that region. Branches may not cross region 2320 /// boundaries. 2321 void createEmptyBlocks( 2322 std::list<Fortran::lower::pft::Evaluation> &evaluationList) { 2323 mlir::Region *region = &builder->getRegion(); 2324 for (Fortran::lower::pft::Evaluation &eval : evaluationList) { 2325 if (eval.isNewBlock) 2326 eval.block = builder->createBlock(region); 2327 if (eval.isConstruct() || eval.isDirective()) { 2328 if (eval.lowerAsUnstructured()) { 2329 createEmptyBlocks(eval.getNestedEvaluations()); 2330 } else if (eval.hasNestedEvaluations()) { 2331 // A structured construct that is a target starts a new block. 2332 Fortran::lower::pft::Evaluation &constructStmt = 2333 eval.getFirstNestedEvaluation(); 2334 if (constructStmt.isNewBlock) 2335 constructStmt.block = builder->createBlock(region); 2336 } 2337 } 2338 } 2339 } 2340 2341 /// Return the predicate: "current block does not have a terminator branch". 2342 bool blockIsUnterminated() { 2343 mlir::Block *currentBlock = builder->getBlock(); 2344 return currentBlock->empty() || 2345 !currentBlock->back().hasTrait<mlir::OpTrait::IsTerminator>(); 2346 } 2347 2348 /// Unconditionally switch code insertion to a new block. 2349 void startBlock(mlir::Block *newBlock) { 2350 assert(newBlock && "missing block"); 2351 // Default termination for the current block is a fallthrough branch to 2352 // the new block. 2353 if (blockIsUnterminated()) 2354 genFIRBranch(newBlock); 2355 // Some blocks may be re/started more than once, and might not be empty. 2356 // If the new block already has (only) a terminator, set the insertion 2357 // point to the start of the block. Otherwise set it to the end. 2358 builder->setInsertionPointToStart(newBlock); 2359 if (blockIsUnterminated()) 2360 builder->setInsertionPointToEnd(newBlock); 2361 } 2362 2363 /// Conditionally switch code insertion to a new block. 2364 void maybeStartBlock(mlir::Block *newBlock) { 2365 if (newBlock) 2366 startBlock(newBlock); 2367 } 2368 2369 /// Emit return and cleanup after the function has been translated. 2370 void endNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) { 2371 setCurrentPosition(Fortran::lower::pft::stmtSourceLoc(funit.endStmt)); 2372 if (funit.isMainProgram()) 2373 genExitRoutine(); 2374 else 2375 genFIRProcedureExit(funit, funit.getSubprogramSymbol()); 2376 funit.finalBlock = nullptr; 2377 LLVM_DEBUG(llvm::dbgs() << "*** Lowering result:\n\n" 2378 << *builder->getFunction() << '\n'); 2379 // FIXME: Simplification should happen in a normal pass, not here. 2380 mlir::IRRewriter rewriter(*builder); 2381 (void)mlir::simplifyRegions(rewriter, 2382 {builder->getRegion()}); // remove dead code 2383 delete builder; 2384 builder = nullptr; 2385 hostAssocTuple = mlir::Value{}; 2386 localSymbols.clear(); 2387 } 2388 2389 /// Helper to generate GlobalOps when the builder is not positioned in any 2390 /// region block. This is required because the FirOpBuilder assumes it is 2391 /// always positioned inside a region block when creating globals, the easiest 2392 /// way comply is to create a dummy function and to throw it afterwards. 2393 void createGlobalOutsideOfFunctionLowering( 2394 const std::function<void()> &createGlobals) { 2395 // FIXME: get rid of the bogus function context and instantiate the 2396 // globals directly into the module. 2397 mlir::MLIRContext *context = &getMLIRContext(); 2398 mlir::func::FuncOp func = fir::FirOpBuilder::createFunction( 2399 mlir::UnknownLoc::get(context), getModuleOp(), 2400 fir::NameUniquer::doGenerated("Sham"), 2401 mlir::FunctionType::get(context, llvm::None, llvm::None)); 2402 func.addEntryBlock(); 2403 builder = new fir::FirOpBuilder(func, bridge.getKindMap()); 2404 createGlobals(); 2405 if (mlir::Region *region = func.getCallableRegion()) 2406 region->dropAllReferences(); 2407 func.erase(); 2408 delete builder; 2409 builder = nullptr; 2410 localSymbols.clear(); 2411 } 2412 /// Instantiate the data from a BLOCK DATA unit. 2413 void lowerBlockData(Fortran::lower::pft::BlockDataUnit &bdunit) { 2414 createGlobalOutsideOfFunctionLowering([&]() { 2415 Fortran::lower::AggregateStoreMap fakeMap; 2416 for (const auto &[_, sym] : bdunit.symTab) { 2417 if (sym->has<Fortran::semantics::ObjectEntityDetails>()) { 2418 Fortran::lower::pft::Variable var(*sym, true); 2419 instantiateVar(var, fakeMap); 2420 } 2421 } 2422 }); 2423 } 2424 2425 /// Lower a procedure (nest). 2426 void lowerFunc(Fortran::lower::pft::FunctionLikeUnit &funit) { 2427 if (!funit.isMainProgram()) { 2428 const Fortran::semantics::Symbol &procSymbol = 2429 funit.getSubprogramSymbol(); 2430 if (procSymbol.owner().IsSubmodule()) { 2431 TODO(toLocation(), "support submodules"); 2432 return; 2433 } 2434 } 2435 setCurrentPosition(funit.getStartingSourceLoc()); 2436 for (int entryIndex = 0, last = funit.entryPointList.size(); 2437 entryIndex < last; ++entryIndex) { 2438 funit.setActiveEntry(entryIndex); 2439 startNewFunction(funit); // the entry point for lowering this procedure 2440 for (Fortran::lower::pft::Evaluation &eval : funit.evaluationList) 2441 genFIR(eval); 2442 endNewFunction(funit); 2443 } 2444 funit.setActiveEntry(0); 2445 for (Fortran::lower::pft::FunctionLikeUnit &f : funit.nestedFunctions) 2446 lowerFunc(f); // internal procedure 2447 } 2448 2449 /// Lower module variable definitions to fir::globalOp and OpenMP/OpenACC 2450 /// declarative construct. 2451 void lowerModuleDeclScope(Fortran::lower::pft::ModuleLikeUnit &mod) { 2452 setCurrentPosition(mod.getStartingSourceLoc()); 2453 createGlobalOutsideOfFunctionLowering([&]() { 2454 for (const Fortran::lower::pft::Variable &var : 2455 mod.getOrderedSymbolTable()) { 2456 // Only define the variables owned by this module. 2457 const Fortran::semantics::Scope *owningScope = var.getOwningScope(); 2458 if (!owningScope || mod.getScope() == *owningScope) 2459 Fortran::lower::defineModuleVariable(*this, var); 2460 } 2461 for (auto &eval : mod.evaluationList) 2462 genFIR(eval); 2463 }); 2464 } 2465 2466 /// Lower functions contained in a module. 2467 void lowerMod(Fortran::lower::pft::ModuleLikeUnit &mod) { 2468 for (Fortran::lower::pft::FunctionLikeUnit &f : mod.nestedFunctions) 2469 lowerFunc(f); 2470 } 2471 2472 void setCurrentPosition(const Fortran::parser::CharBlock &position) { 2473 if (position != Fortran::parser::CharBlock{}) 2474 currentPosition = position; 2475 } 2476 2477 /// Set current position at the location of \p parseTreeNode. Note that the 2478 /// position is updated automatically when visiting statements, but not when 2479 /// entering higher level nodes like constructs or procedures. This helper is 2480 /// intended to cover the latter cases. 2481 template <typename A> 2482 void setCurrentPositionAt(const A &parseTreeNode) { 2483 setCurrentPosition(Fortran::parser::FindSourceLocation(parseTreeNode)); 2484 } 2485 2486 //===--------------------------------------------------------------------===// 2487 // Utility methods 2488 //===--------------------------------------------------------------------===// 2489 2490 /// Convert a parser CharBlock to a Location 2491 mlir::Location toLocation(const Fortran::parser::CharBlock &cb) { 2492 return genLocation(cb); 2493 } 2494 2495 mlir::Location toLocation() { return toLocation(currentPosition); } 2496 void setCurrentEval(Fortran::lower::pft::Evaluation &eval) { 2497 evalPtr = &eval; 2498 } 2499 Fortran::lower::pft::Evaluation &getEval() { 2500 assert(evalPtr); 2501 return *evalPtr; 2502 } 2503 2504 std::optional<Fortran::evaluate::Shape> 2505 getShape(const Fortran::lower::SomeExpr &expr) { 2506 return Fortran::evaluate::GetShape(foldingContext, expr); 2507 } 2508 2509 //===--------------------------------------------------------------------===// 2510 // Analysis on a nested explicit iteration space. 2511 //===--------------------------------------------------------------------===// 2512 2513 void analyzeExplicitSpace(const Fortran::parser::ConcurrentHeader &header) { 2514 explicitIterSpace.pushLevel(); 2515 for (const Fortran::parser::ConcurrentControl &ctrl : 2516 std::get<std::list<Fortran::parser::ConcurrentControl>>(header.t)) { 2517 const Fortran::semantics::Symbol *ctrlVar = 2518 std::get<Fortran::parser::Name>(ctrl.t).symbol; 2519 explicitIterSpace.addSymbol(ctrlVar); 2520 } 2521 if (const auto &mask = 2522 std::get<std::optional<Fortran::parser::ScalarLogicalExpr>>( 2523 header.t); 2524 mask.has_value()) 2525 analyzeExplicitSpace(*Fortran::semantics::GetExpr(*mask)); 2526 } 2527 template <bool LHS = false, typename A> 2528 void analyzeExplicitSpace(const Fortran::evaluate::Expr<A> &e) { 2529 explicitIterSpace.exprBase(&e, LHS); 2530 } 2531 void analyzeExplicitSpace(const Fortran::evaluate::Assignment *assign) { 2532 auto analyzeAssign = [&](const Fortran::lower::SomeExpr &lhs, 2533 const Fortran::lower::SomeExpr &rhs) { 2534 analyzeExplicitSpace</*LHS=*/true>(lhs); 2535 analyzeExplicitSpace(rhs); 2536 }; 2537 std::visit( 2538 Fortran::common::visitors{ 2539 [&](const Fortran::evaluate::ProcedureRef &procRef) { 2540 // Ensure the procRef expressions are the one being visited. 2541 assert(procRef.arguments().size() == 2); 2542 const Fortran::lower::SomeExpr *lhs = 2543 procRef.arguments()[0].value().UnwrapExpr(); 2544 const Fortran::lower::SomeExpr *rhs = 2545 procRef.arguments()[1].value().UnwrapExpr(); 2546 assert(lhs && rhs && 2547 "user defined assignment arguments must be expressions"); 2548 analyzeAssign(*lhs, *rhs); 2549 }, 2550 [&](const auto &) { analyzeAssign(assign->lhs, assign->rhs); }}, 2551 assign->u); 2552 explicitIterSpace.endAssign(); 2553 } 2554 void analyzeExplicitSpace(const Fortran::parser::ForallAssignmentStmt &stmt) { 2555 std::visit([&](const auto &s) { analyzeExplicitSpace(s); }, stmt.u); 2556 } 2557 void analyzeExplicitSpace(const Fortran::parser::AssignmentStmt &s) { 2558 analyzeExplicitSpace(s.typedAssignment->v.operator->()); 2559 } 2560 void analyzeExplicitSpace(const Fortran::parser::PointerAssignmentStmt &s) { 2561 analyzeExplicitSpace(s.typedAssignment->v.operator->()); 2562 } 2563 void analyzeExplicitSpace(const Fortran::parser::WhereConstruct &c) { 2564 analyzeExplicitSpace( 2565 std::get< 2566 Fortran::parser::Statement<Fortran::parser::WhereConstructStmt>>( 2567 c.t) 2568 .statement); 2569 for (const Fortran::parser::WhereBodyConstruct &body : 2570 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(c.t)) 2571 analyzeExplicitSpace(body); 2572 for (const Fortran::parser::WhereConstruct::MaskedElsewhere &e : 2573 std::get<std::list<Fortran::parser::WhereConstruct::MaskedElsewhere>>( 2574 c.t)) 2575 analyzeExplicitSpace(e); 2576 if (const auto &e = 2577 std::get<std::optional<Fortran::parser::WhereConstruct::Elsewhere>>( 2578 c.t); 2579 e.has_value()) 2580 analyzeExplicitSpace(e.operator->()); 2581 } 2582 void analyzeExplicitSpace(const Fortran::parser::WhereConstructStmt &ws) { 2583 const Fortran::lower::SomeExpr *exp = Fortran::semantics::GetExpr( 2584 std::get<Fortran::parser::LogicalExpr>(ws.t)); 2585 addMaskVariable(exp); 2586 analyzeExplicitSpace(*exp); 2587 } 2588 void analyzeExplicitSpace( 2589 const Fortran::parser::WhereConstruct::MaskedElsewhere &ew) { 2590 analyzeExplicitSpace( 2591 std::get< 2592 Fortran::parser::Statement<Fortran::parser::MaskedElsewhereStmt>>( 2593 ew.t) 2594 .statement); 2595 for (const Fortran::parser::WhereBodyConstruct &e : 2596 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew.t)) 2597 analyzeExplicitSpace(e); 2598 } 2599 void analyzeExplicitSpace(const Fortran::parser::WhereBodyConstruct &body) { 2600 std::visit(Fortran::common::visitors{ 2601 [&](const Fortran::common::Indirection< 2602 Fortran::parser::WhereConstruct> &wc) { 2603 analyzeExplicitSpace(wc.value()); 2604 }, 2605 [&](const auto &s) { analyzeExplicitSpace(s.statement); }}, 2606 body.u); 2607 } 2608 void analyzeExplicitSpace(const Fortran::parser::MaskedElsewhereStmt &stmt) { 2609 const Fortran::lower::SomeExpr *exp = Fortran::semantics::GetExpr( 2610 std::get<Fortran::parser::LogicalExpr>(stmt.t)); 2611 addMaskVariable(exp); 2612 analyzeExplicitSpace(*exp); 2613 } 2614 void 2615 analyzeExplicitSpace(const Fortran::parser::WhereConstruct::Elsewhere *ew) { 2616 for (const Fortran::parser::WhereBodyConstruct &e : 2617 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew->t)) 2618 analyzeExplicitSpace(e); 2619 } 2620 void analyzeExplicitSpace(const Fortran::parser::WhereStmt &stmt) { 2621 const Fortran::lower::SomeExpr *exp = Fortran::semantics::GetExpr( 2622 std::get<Fortran::parser::LogicalExpr>(stmt.t)); 2623 addMaskVariable(exp); 2624 analyzeExplicitSpace(*exp); 2625 const std::optional<Fortran::evaluate::Assignment> &assign = 2626 std::get<Fortran::parser::AssignmentStmt>(stmt.t).typedAssignment->v; 2627 assert(assign.has_value() && "WHERE has no statement"); 2628 analyzeExplicitSpace(assign.operator->()); 2629 } 2630 void analyzeExplicitSpace(const Fortran::parser::ForallStmt &forall) { 2631 analyzeExplicitSpace( 2632 std::get< 2633 Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>( 2634 forall.t) 2635 .value()); 2636 analyzeExplicitSpace(std::get<Fortran::parser::UnlabeledStatement< 2637 Fortran::parser::ForallAssignmentStmt>>(forall.t) 2638 .statement); 2639 analyzeExplicitSpacePop(); 2640 } 2641 void 2642 analyzeExplicitSpace(const Fortran::parser::ForallConstructStmt &forall) { 2643 analyzeExplicitSpace( 2644 std::get< 2645 Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>( 2646 forall.t) 2647 .value()); 2648 } 2649 void analyzeExplicitSpace(const Fortran::parser::ForallConstruct &forall) { 2650 analyzeExplicitSpace( 2651 std::get< 2652 Fortran::parser::Statement<Fortran::parser::ForallConstructStmt>>( 2653 forall.t) 2654 .statement); 2655 for (const Fortran::parser::ForallBodyConstruct &s : 2656 std::get<std::list<Fortran::parser::ForallBodyConstruct>>(forall.t)) { 2657 std::visit(Fortran::common::visitors{ 2658 [&](const Fortran::common::Indirection< 2659 Fortran::parser::ForallConstruct> &b) { 2660 analyzeExplicitSpace(b.value()); 2661 }, 2662 [&](const Fortran::parser::WhereConstruct &w) { 2663 analyzeExplicitSpace(w); 2664 }, 2665 [&](const auto &b) { analyzeExplicitSpace(b.statement); }}, 2666 s.u); 2667 } 2668 analyzeExplicitSpacePop(); 2669 } 2670 2671 void analyzeExplicitSpacePop() { explicitIterSpace.popLevel(); } 2672 2673 void addMaskVariable(Fortran::lower::FrontEndExpr exp) { 2674 // Note: use i8 to store bool values. This avoids round-down behavior found 2675 // with sequences of i1. That is, an array of i1 will be truncated in size 2676 // and be too small. For example, a buffer of type fir.array<7xi1> will have 2677 // 0 size. 2678 mlir::Type i64Ty = builder->getIntegerType(64); 2679 mlir::TupleType ty = fir::factory::getRaggedArrayHeaderType(*builder); 2680 mlir::Type buffTy = ty.getType(1); 2681 mlir::Type shTy = ty.getType(2); 2682 mlir::Location loc = toLocation(); 2683 mlir::Value hdr = builder->createTemporary(loc, ty); 2684 // FIXME: Is there a way to create a `zeroinitializer` in LLVM-IR dialect? 2685 // For now, explicitly set lazy ragged header to all zeros. 2686 // auto nilTup = builder->createNullConstant(loc, ty); 2687 // builder->create<fir::StoreOp>(loc, nilTup, hdr); 2688 mlir::Type i32Ty = builder->getIntegerType(32); 2689 mlir::Value zero = builder->createIntegerConstant(loc, i32Ty, 0); 2690 mlir::Value zero64 = builder->createIntegerConstant(loc, i64Ty, 0); 2691 mlir::Value flags = builder->create<fir::CoordinateOp>( 2692 loc, builder->getRefType(i64Ty), hdr, zero); 2693 builder->create<fir::StoreOp>(loc, zero64, flags); 2694 mlir::Value one = builder->createIntegerConstant(loc, i32Ty, 1); 2695 mlir::Value nullPtr1 = builder->createNullConstant(loc, buffTy); 2696 mlir::Value var = builder->create<fir::CoordinateOp>( 2697 loc, builder->getRefType(buffTy), hdr, one); 2698 builder->create<fir::StoreOp>(loc, nullPtr1, var); 2699 mlir::Value two = builder->createIntegerConstant(loc, i32Ty, 2); 2700 mlir::Value nullPtr2 = builder->createNullConstant(loc, shTy); 2701 mlir::Value shape = builder->create<fir::CoordinateOp>( 2702 loc, builder->getRefType(shTy), hdr, two); 2703 builder->create<fir::StoreOp>(loc, nullPtr2, shape); 2704 implicitIterSpace.addMaskVariable(exp, var, shape, hdr); 2705 explicitIterSpace.outermostContext().attachCleanup( 2706 [builder = this->builder, hdr, loc]() { 2707 fir::runtime::genRaggedArrayDeallocate(loc, *builder, hdr); 2708 }); 2709 } 2710 2711 void createRuntimeTypeInfoGlobals() {} 2712 2713 //===--------------------------------------------------------------------===// 2714 2715 Fortran::lower::LoweringBridge &bridge; 2716 Fortran::evaluate::FoldingContext foldingContext; 2717 fir::FirOpBuilder *builder = nullptr; 2718 Fortran::lower::pft::Evaluation *evalPtr = nullptr; 2719 Fortran::lower::SymMap localSymbols; 2720 Fortran::parser::CharBlock currentPosition; 2721 RuntimeTypeInfoConverter runtimeTypeInfoConverter; 2722 2723 /// WHERE statement/construct mask expression stack. 2724 Fortran::lower::ImplicitIterSpace implicitIterSpace; 2725 2726 /// FORALL context 2727 Fortran::lower::ExplicitIterSpace explicitIterSpace; 2728 2729 /// Tuple of host assoicated variables. 2730 mlir::Value hostAssocTuple; 2731 }; 2732 2733 } // namespace 2734 2735 Fortran::evaluate::FoldingContext 2736 Fortran::lower::LoweringBridge::createFoldingContext() const { 2737 return {getDefaultKinds(), getIntrinsicTable()}; 2738 } 2739 2740 void Fortran::lower::LoweringBridge::lower( 2741 const Fortran::parser::Program &prg, 2742 const Fortran::semantics::SemanticsContext &semanticsContext) { 2743 std::unique_ptr<Fortran::lower::pft::Program> pft = 2744 Fortran::lower::createPFT(prg, semanticsContext); 2745 if (dumpBeforeFir) 2746 Fortran::lower::dumpPFT(llvm::errs(), *pft); 2747 FirConverter converter{*this}; 2748 converter.run(*pft); 2749 } 2750 2751 void Fortran::lower::LoweringBridge::parseSourceFile(llvm::SourceMgr &srcMgr) { 2752 mlir::OwningOpRef<mlir::ModuleOp> owningRef = 2753 mlir::parseSourceFile<mlir::ModuleOp>(srcMgr, &context); 2754 module.reset(new mlir::ModuleOp(owningRef.get().getOperation())); 2755 owningRef.release(); 2756 } 2757 2758 Fortran::lower::LoweringBridge::LoweringBridge( 2759 mlir::MLIRContext &context, 2760 const Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds, 2761 const Fortran::evaluate::IntrinsicProcTable &intrinsics, 2762 const Fortran::parser::AllCookedSources &cooked, llvm::StringRef triple, 2763 fir::KindMapping &kindMap) 2764 : defaultKinds{defaultKinds}, intrinsics{intrinsics}, cooked{&cooked}, 2765 context{context}, kindMap{kindMap} { 2766 // Register the diagnostic handler. 2767 context.getDiagEngine().registerHandler([](mlir::Diagnostic &diag) { 2768 llvm::raw_ostream &os = llvm::errs(); 2769 switch (diag.getSeverity()) { 2770 case mlir::DiagnosticSeverity::Error: 2771 os << "error: "; 2772 break; 2773 case mlir::DiagnosticSeverity::Remark: 2774 os << "info: "; 2775 break; 2776 case mlir::DiagnosticSeverity::Warning: 2777 os << "warning: "; 2778 break; 2779 default: 2780 break; 2781 } 2782 if (!diag.getLocation().isa<mlir::UnknownLoc>()) 2783 os << diag.getLocation() << ": "; 2784 os << diag << '\n'; 2785 os.flush(); 2786 return mlir::success(); 2787 }); 2788 2789 // Create the module and attach the attributes. 2790 module = std::make_unique<mlir::ModuleOp>( 2791 mlir::ModuleOp::create(mlir::UnknownLoc::get(&context))); 2792 assert(module.get() && "module was not created"); 2793 fir::setTargetTriple(*module.get(), triple); 2794 fir::setKindMapping(*module.get(), kindMap); 2795 } 2796