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 mlir::FuncOp with 199 // 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::FuncOp getFunc(llvm::StringRef name, mlir::FunctionType ty) { 746 if (mlir::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 &) { 1190 TODO(toLocation(), "OpenACCDeclarativeConstruct lowering"); 1191 } 1192 1193 void genFIR(const Fortran::parser::OpenMPConstruct &omp) { 1194 mlir::OpBuilder::InsertPoint insertPt = builder->saveInsertionPoint(); 1195 localSymbols.pushScope(); 1196 Fortran::lower::genOpenMPConstruct(*this, getEval(), omp); 1197 1198 for (Fortran::lower::pft::Evaluation &e : getEval().getNestedEvaluations()) 1199 genFIR(e); 1200 localSymbols.popScope(); 1201 builder->restoreInsertionPoint(insertPt); 1202 } 1203 1204 void genFIR(const Fortran::parser::OpenMPDeclarativeConstruct &ompDecl) { 1205 TODO(toLocation(), "OpenMPDeclarativeConstruct lowering"); 1206 } 1207 1208 /// Generate FIR for a SELECT CASE statement. 1209 /// The type may be CHARACTER, INTEGER, or LOGICAL. 1210 void genFIR(const Fortran::parser::SelectCaseStmt &stmt) { 1211 Fortran::lower::pft::Evaluation &eval = getEval(); 1212 mlir::MLIRContext *context = builder->getContext(); 1213 mlir::Location loc = toLocation(); 1214 Fortran::lower::StatementContext stmtCtx; 1215 const Fortran::lower::SomeExpr *expr = Fortran::semantics::GetExpr( 1216 std::get<Fortran::parser::Scalar<Fortran::parser::Expr>>(stmt.t)); 1217 bool isCharSelector = isCharacterCategory(expr->GetType()->category()); 1218 bool isLogicalSelector = isLogicalCategory(expr->GetType()->category()); 1219 auto charValue = [&](const Fortran::lower::SomeExpr *expr) { 1220 fir::ExtendedValue exv = genExprAddr(*expr, stmtCtx, &loc); 1221 return exv.match( 1222 [&](const fir::CharBoxValue &cbv) { 1223 return fir::factory::CharacterExprHelper{*builder, loc} 1224 .createEmboxChar(cbv.getAddr(), cbv.getLen()); 1225 }, 1226 [&](auto) { 1227 fir::emitFatalError(loc, "not a character"); 1228 return mlir::Value{}; 1229 }); 1230 }; 1231 mlir::Value selector; 1232 if (isCharSelector) { 1233 selector = charValue(expr); 1234 } else { 1235 selector = createFIRExpr(loc, expr, stmtCtx); 1236 if (isLogicalSelector) 1237 selector = builder->createConvert(loc, builder->getI1Type(), selector); 1238 } 1239 mlir::Type selectType = selector.getType(); 1240 llvm::SmallVector<mlir::Attribute> attrList; 1241 llvm::SmallVector<mlir::Value> valueList; 1242 llvm::SmallVector<mlir::Block *> blockList; 1243 mlir::Block *defaultBlock = eval.parentConstruct->constructExit->block; 1244 using CaseValue = Fortran::parser::Scalar<Fortran::parser::ConstantExpr>; 1245 auto addValue = [&](const CaseValue &caseValue) { 1246 const Fortran::lower::SomeExpr *expr = 1247 Fortran::semantics::GetExpr(caseValue.thing); 1248 if (isCharSelector) 1249 valueList.push_back(charValue(expr)); 1250 else if (isLogicalSelector) 1251 valueList.push_back(builder->createConvert( 1252 loc, selectType, createFIRExpr(toLocation(), expr, stmtCtx))); 1253 else 1254 valueList.push_back(builder->createIntegerConstant( 1255 loc, selectType, *Fortran::evaluate::ToInt64(*expr))); 1256 }; 1257 for (Fortran::lower::pft::Evaluation *e = eval.controlSuccessor; e; 1258 e = e->controlSuccessor) { 1259 const auto &caseStmt = e->getIf<Fortran::parser::CaseStmt>(); 1260 assert(e->block && "missing CaseStmt block"); 1261 const auto &caseSelector = 1262 std::get<Fortran::parser::CaseSelector>(caseStmt->t); 1263 const auto *caseValueRangeList = 1264 std::get_if<std::list<Fortran::parser::CaseValueRange>>( 1265 &caseSelector.u); 1266 if (!caseValueRangeList) { 1267 defaultBlock = e->block; 1268 continue; 1269 } 1270 for (const Fortran::parser::CaseValueRange &caseValueRange : 1271 *caseValueRangeList) { 1272 blockList.push_back(e->block); 1273 if (const auto *caseValue = std::get_if<CaseValue>(&caseValueRange.u)) { 1274 attrList.push_back(fir::PointIntervalAttr::get(context)); 1275 addValue(*caseValue); 1276 continue; 1277 } 1278 const auto &caseRange = 1279 std::get<Fortran::parser::CaseValueRange::Range>(caseValueRange.u); 1280 if (caseRange.lower && caseRange.upper) { 1281 attrList.push_back(fir::ClosedIntervalAttr::get(context)); 1282 addValue(*caseRange.lower); 1283 addValue(*caseRange.upper); 1284 } else if (caseRange.lower) { 1285 attrList.push_back(fir::LowerBoundAttr::get(context)); 1286 addValue(*caseRange.lower); 1287 } else { 1288 attrList.push_back(fir::UpperBoundAttr::get(context)); 1289 addValue(*caseRange.upper); 1290 } 1291 } 1292 } 1293 // Skip a logical default block that can never be referenced. 1294 if (isLogicalSelector && attrList.size() == 2) 1295 defaultBlock = eval.parentConstruct->constructExit->block; 1296 attrList.push_back(mlir::UnitAttr::get(context)); 1297 blockList.push_back(defaultBlock); 1298 1299 // Generate a fir::SelectCaseOp. 1300 // Explicit branch code is better for the LOGICAL type. The CHARACTER type 1301 // does not yet have downstream support, and also uses explicit branch code. 1302 // The -no-structured-fir option can be used to force generation of INTEGER 1303 // type branch code. 1304 if (!isLogicalSelector && !isCharSelector && eval.lowerAsStructured()) { 1305 // Numeric selector is a ssa register, all temps that may have 1306 // been generated while evaluating it can be cleaned-up before the 1307 // fir.select_case. 1308 stmtCtx.finalize(); 1309 builder->create<fir::SelectCaseOp>(loc, selector, attrList, valueList, 1310 blockList); 1311 return; 1312 } 1313 1314 // Generate a sequence of case value comparisons and branches. 1315 auto caseValue = valueList.begin(); 1316 auto caseBlock = blockList.begin(); 1317 for (mlir::Attribute attr : attrList) { 1318 if (attr.isa<mlir::UnitAttr>()) { 1319 genFIRBranch(*caseBlock++); 1320 break; 1321 } 1322 auto genCond = [&](mlir::Value rhs, 1323 mlir::arith::CmpIPredicate pred) -> mlir::Value { 1324 if (!isCharSelector) 1325 return builder->create<mlir::arith::CmpIOp>(loc, pred, selector, rhs); 1326 fir::factory::CharacterExprHelper charHelper{*builder, loc}; 1327 std::pair<mlir::Value, mlir::Value> lhsVal = 1328 charHelper.createUnboxChar(selector); 1329 mlir::Value &lhsAddr = lhsVal.first; 1330 mlir::Value &lhsLen = lhsVal.second; 1331 std::pair<mlir::Value, mlir::Value> rhsVal = 1332 charHelper.createUnboxChar(rhs); 1333 mlir::Value &rhsAddr = rhsVal.first; 1334 mlir::Value &rhsLen = rhsVal.second; 1335 return fir::runtime::genCharCompare(*builder, loc, pred, lhsAddr, 1336 lhsLen, rhsAddr, rhsLen); 1337 }; 1338 mlir::Block *newBlock = insertBlock(*caseBlock); 1339 if (attr.isa<fir::ClosedIntervalAttr>()) { 1340 mlir::Block *newBlock2 = insertBlock(*caseBlock); 1341 mlir::Value cond = 1342 genCond(*caseValue++, mlir::arith::CmpIPredicate::sge); 1343 genFIRConditionalBranch(cond, newBlock, newBlock2); 1344 builder->setInsertionPointToEnd(newBlock); 1345 mlir::Value cond2 = 1346 genCond(*caseValue++, mlir::arith::CmpIPredicate::sle); 1347 genFIRConditionalBranch(cond2, *caseBlock++, newBlock2); 1348 builder->setInsertionPointToEnd(newBlock2); 1349 continue; 1350 } 1351 mlir::arith::CmpIPredicate pred; 1352 if (attr.isa<fir::PointIntervalAttr>()) { 1353 pred = mlir::arith::CmpIPredicate::eq; 1354 } else if (attr.isa<fir::LowerBoundAttr>()) { 1355 pred = mlir::arith::CmpIPredicate::sge; 1356 } else { 1357 assert(attr.isa<fir::UpperBoundAttr>() && "unexpected predicate"); 1358 pred = mlir::arith::CmpIPredicate::sle; 1359 } 1360 mlir::Value cond = genCond(*caseValue++, pred); 1361 genFIRConditionalBranch(cond, *caseBlock++, newBlock); 1362 builder->setInsertionPointToEnd(newBlock); 1363 } 1364 assert(caseValue == valueList.end() && caseBlock == blockList.end() && 1365 "select case list mismatch"); 1366 // Clean-up the selector at the end of the construct if it is a temporary 1367 // (which is possible with characters). 1368 mlir::OpBuilder::InsertPoint insertPt = builder->saveInsertionPoint(); 1369 builder->setInsertionPointToEnd(eval.parentConstruct->constructExit->block); 1370 stmtCtx.finalize(); 1371 builder->restoreInsertionPoint(insertPt); 1372 } 1373 1374 fir::ExtendedValue 1375 genAssociateSelector(const Fortran::lower::SomeExpr &selector, 1376 Fortran::lower::StatementContext &stmtCtx) { 1377 return isArraySectionWithoutVectorSubscript(selector) 1378 ? Fortran::lower::createSomeArrayBox(*this, selector, 1379 localSymbols, stmtCtx) 1380 : genExprAddr(selector, stmtCtx); 1381 } 1382 1383 void genFIR(const Fortran::parser::AssociateConstruct &) { 1384 Fortran::lower::StatementContext stmtCtx; 1385 Fortran::lower::pft::Evaluation &eval = getEval(); 1386 for (Fortran::lower::pft::Evaluation &e : eval.getNestedEvaluations()) { 1387 if (auto *stmt = e.getIf<Fortran::parser::AssociateStmt>()) { 1388 if (eval.lowerAsUnstructured()) 1389 maybeStartBlock(e.block); 1390 localSymbols.pushScope(); 1391 for (const Fortran::parser::Association &assoc : 1392 std::get<std::list<Fortran::parser::Association>>(stmt->t)) { 1393 Fortran::semantics::Symbol &sym = 1394 *std::get<Fortran::parser::Name>(assoc.t).symbol; 1395 const Fortran::lower::SomeExpr &selector = 1396 *sym.get<Fortran::semantics::AssocEntityDetails>().expr(); 1397 localSymbols.addSymbol(sym, genAssociateSelector(selector, stmtCtx)); 1398 } 1399 } else if (e.getIf<Fortran::parser::EndAssociateStmt>()) { 1400 if (eval.lowerAsUnstructured()) 1401 maybeStartBlock(e.block); 1402 stmtCtx.finalize(); 1403 localSymbols.popScope(); 1404 } else { 1405 genFIR(e); 1406 } 1407 } 1408 } 1409 1410 void genFIR(const Fortran::parser::BlockConstruct &blockConstruct) { 1411 setCurrentPositionAt(blockConstruct); 1412 TODO(toLocation(), "BlockConstruct lowering"); 1413 } 1414 void genFIR(const Fortran::parser::BlockStmt &) { 1415 TODO(toLocation(), "BlockStmt lowering"); 1416 } 1417 void genFIR(const Fortran::parser::EndBlockStmt &) { 1418 TODO(toLocation(), "EndBlockStmt lowering"); 1419 } 1420 1421 void genFIR(const Fortran::parser::ChangeTeamConstruct &construct) { 1422 TODO(toLocation(), "ChangeTeamConstruct lowering"); 1423 } 1424 void genFIR(const Fortran::parser::ChangeTeamStmt &stmt) { 1425 TODO(toLocation(), "ChangeTeamStmt lowering"); 1426 } 1427 void genFIR(const Fortran::parser::EndChangeTeamStmt &stmt) { 1428 TODO(toLocation(), "EndChangeTeamStmt lowering"); 1429 } 1430 1431 void genFIR(const Fortran::parser::CriticalConstruct &criticalConstruct) { 1432 setCurrentPositionAt(criticalConstruct); 1433 TODO(toLocation(), "CriticalConstruct lowering"); 1434 } 1435 void genFIR(const Fortran::parser::CriticalStmt &) { 1436 TODO(toLocation(), "CriticalStmt lowering"); 1437 } 1438 void genFIR(const Fortran::parser::EndCriticalStmt &) { 1439 TODO(toLocation(), "EndCriticalStmt lowering"); 1440 } 1441 1442 void genFIR(const Fortran::parser::SelectRankConstruct &selectRankConstruct) { 1443 setCurrentPositionAt(selectRankConstruct); 1444 TODO(toLocation(), "SelectRankConstruct lowering"); 1445 } 1446 void genFIR(const Fortran::parser::SelectRankStmt &) { 1447 TODO(toLocation(), "SelectRankStmt lowering"); 1448 } 1449 void genFIR(const Fortran::parser::SelectRankCaseStmt &) { 1450 TODO(toLocation(), "SelectRankCaseStmt lowering"); 1451 } 1452 1453 void genFIR(const Fortran::parser::SelectTypeConstruct &selectTypeConstruct) { 1454 setCurrentPositionAt(selectTypeConstruct); 1455 TODO(toLocation(), "SelectTypeConstruct lowering"); 1456 } 1457 void genFIR(const Fortran::parser::SelectTypeStmt &) { 1458 TODO(toLocation(), "SelectTypeStmt lowering"); 1459 } 1460 void genFIR(const Fortran::parser::TypeGuardStmt &) { 1461 TODO(toLocation(), "TypeGuardStmt lowering"); 1462 } 1463 1464 //===--------------------------------------------------------------------===// 1465 // IO statements (see io.h) 1466 //===--------------------------------------------------------------------===// 1467 1468 void genFIR(const Fortran::parser::BackspaceStmt &stmt) { 1469 mlir::Value iostat = genBackspaceStatement(*this, stmt); 1470 genIoConditionBranches(getEval(), stmt.v, iostat); 1471 } 1472 void genFIR(const Fortran::parser::CloseStmt &stmt) { 1473 mlir::Value iostat = genCloseStatement(*this, stmt); 1474 genIoConditionBranches(getEval(), stmt.v, iostat); 1475 } 1476 void genFIR(const Fortran::parser::EndfileStmt &stmt) { 1477 mlir::Value iostat = genEndfileStatement(*this, stmt); 1478 genIoConditionBranches(getEval(), stmt.v, iostat); 1479 } 1480 void genFIR(const Fortran::parser::FlushStmt &stmt) { 1481 mlir::Value iostat = genFlushStatement(*this, stmt); 1482 genIoConditionBranches(getEval(), stmt.v, iostat); 1483 } 1484 void genFIR(const Fortran::parser::InquireStmt &stmt) { 1485 mlir::Value iostat = genInquireStatement(*this, stmt); 1486 if (const auto *specs = 1487 std::get_if<std::list<Fortran::parser::InquireSpec>>(&stmt.u)) 1488 genIoConditionBranches(getEval(), *specs, iostat); 1489 } 1490 void genFIR(const Fortran::parser::OpenStmt &stmt) { 1491 mlir::Value iostat = genOpenStatement(*this, stmt); 1492 genIoConditionBranches(getEval(), stmt.v, iostat); 1493 } 1494 void genFIR(const Fortran::parser::PrintStmt &stmt) { 1495 genPrintStatement(*this, stmt); 1496 } 1497 void genFIR(const Fortran::parser::ReadStmt &stmt) { 1498 mlir::Value iostat = genReadStatement(*this, stmt); 1499 genIoConditionBranches(getEval(), stmt.controls, iostat); 1500 } 1501 void genFIR(const Fortran::parser::RewindStmt &stmt) { 1502 mlir::Value iostat = genRewindStatement(*this, stmt); 1503 genIoConditionBranches(getEval(), stmt.v, iostat); 1504 } 1505 void genFIR(const Fortran::parser::WaitStmt &stmt) { 1506 mlir::Value iostat = genWaitStatement(*this, stmt); 1507 genIoConditionBranches(getEval(), stmt.v, iostat); 1508 } 1509 void genFIR(const Fortran::parser::WriteStmt &stmt) { 1510 mlir::Value iostat = genWriteStatement(*this, stmt); 1511 genIoConditionBranches(getEval(), stmt.controls, iostat); 1512 } 1513 1514 template <typename A> 1515 void genIoConditionBranches(Fortran::lower::pft::Evaluation &eval, 1516 const A &specList, mlir::Value iostat) { 1517 if (!iostat) 1518 return; 1519 1520 mlir::Block *endBlock = nullptr; 1521 mlir::Block *eorBlock = nullptr; 1522 mlir::Block *errBlock = nullptr; 1523 for (const auto &spec : specList) { 1524 std::visit(Fortran::common::visitors{ 1525 [&](const Fortran::parser::EndLabel &label) { 1526 endBlock = blockOfLabel(eval, label.v); 1527 }, 1528 [&](const Fortran::parser::EorLabel &label) { 1529 eorBlock = blockOfLabel(eval, label.v); 1530 }, 1531 [&](const Fortran::parser::ErrLabel &label) { 1532 errBlock = blockOfLabel(eval, label.v); 1533 }, 1534 [](const auto &) {}}, 1535 spec.u); 1536 } 1537 if (!endBlock && !eorBlock && !errBlock) 1538 return; 1539 1540 mlir::Location loc = toLocation(); 1541 mlir::Type indexType = builder->getIndexType(); 1542 mlir::Value selector = builder->createConvert(loc, indexType, iostat); 1543 llvm::SmallVector<int64_t> indexList; 1544 llvm::SmallVector<mlir::Block *> blockList; 1545 if (eorBlock) { 1546 indexList.push_back(Fortran::runtime::io::IostatEor); 1547 blockList.push_back(eorBlock); 1548 } 1549 if (endBlock) { 1550 indexList.push_back(Fortran::runtime::io::IostatEnd); 1551 blockList.push_back(endBlock); 1552 } 1553 if (errBlock) { 1554 indexList.push_back(0); 1555 blockList.push_back(eval.nonNopSuccessor().block); 1556 // ERR label statement is the default successor. 1557 blockList.push_back(errBlock); 1558 } else { 1559 // Fallthrough successor statement is the default successor. 1560 blockList.push_back(eval.nonNopSuccessor().block); 1561 } 1562 builder->create<fir::SelectOp>(loc, selector, indexList, blockList); 1563 } 1564 1565 //===--------------------------------------------------------------------===// 1566 // Memory allocation and deallocation 1567 //===--------------------------------------------------------------------===// 1568 1569 void genFIR(const Fortran::parser::AllocateStmt &stmt) { 1570 Fortran::lower::genAllocateStmt(*this, stmt, toLocation()); 1571 } 1572 1573 void genFIR(const Fortran::parser::DeallocateStmt &stmt) { 1574 Fortran::lower::genDeallocateStmt(*this, stmt, toLocation()); 1575 } 1576 1577 /// Nullify pointer object list 1578 /// 1579 /// For each pointer object, reset the pointer to a disassociated status. 1580 /// We do this by setting each pointer to null. 1581 void genFIR(const Fortran::parser::NullifyStmt &stmt) { 1582 mlir::Location loc = toLocation(); 1583 for (auto &pointerObject : stmt.v) { 1584 const Fortran::lower::SomeExpr *expr = 1585 Fortran::semantics::GetExpr(pointerObject); 1586 assert(expr); 1587 fir::MutableBoxValue box = genExprMutableBox(loc, *expr); 1588 fir::factory::disassociateMutableBox(*builder, loc, box); 1589 } 1590 } 1591 1592 //===--------------------------------------------------------------------===// 1593 1594 void genFIR(const Fortran::parser::EventPostStmt &stmt) { 1595 genEventPostStatement(*this, stmt); 1596 } 1597 1598 void genFIR(const Fortran::parser::EventWaitStmt &stmt) { 1599 genEventWaitStatement(*this, stmt); 1600 } 1601 1602 void genFIR(const Fortran::parser::FormTeamStmt &stmt) { 1603 genFormTeamStatement(*this, getEval(), stmt); 1604 } 1605 1606 void genFIR(const Fortran::parser::LockStmt &stmt) { 1607 genLockStatement(*this, stmt); 1608 } 1609 1610 fir::ExtendedValue 1611 genInitializerExprValue(const Fortran::lower::SomeExpr &expr, 1612 Fortran::lower::StatementContext &stmtCtx) { 1613 return Fortran::lower::createSomeInitializerExpression( 1614 toLocation(), *this, expr, localSymbols, stmtCtx); 1615 } 1616 1617 /// Return true if the current context is a conditionalized and implied 1618 /// iteration space. 1619 bool implicitIterationSpace() { return !implicitIterSpace.empty(); } 1620 1621 /// Return true if context is currently an explicit iteration space. A scalar 1622 /// assignment expression may be contextually within a user-defined iteration 1623 /// space, transforming it into an array expression. 1624 bool explicitIterationSpace() { return explicitIterSpace.isActive(); } 1625 1626 /// Generate an array assignment. 1627 /// This is an assignment expression with rank > 0. The assignment may or may 1628 /// not be in a WHERE and/or FORALL context. 1629 void genArrayAssignment(const Fortran::evaluate::Assignment &assign, 1630 Fortran::lower::StatementContext &stmtCtx) { 1631 if (isWholeAllocatable(assign.lhs)) { 1632 // Assignment to allocatables may require the lhs to be 1633 // deallocated/reallocated. See Fortran 2018 10.2.1.3 p3 1634 Fortran::lower::createAllocatableArrayAssignment( 1635 *this, assign.lhs, assign.rhs, explicitIterSpace, implicitIterSpace, 1636 localSymbols, stmtCtx); 1637 return; 1638 } 1639 1640 if (!implicitIterationSpace() && !explicitIterationSpace()) { 1641 // No masks and the iteration space is implied by the array, so create a 1642 // simple array assignment. 1643 Fortran::lower::createSomeArrayAssignment(*this, assign.lhs, assign.rhs, 1644 localSymbols, stmtCtx); 1645 return; 1646 } 1647 1648 // If there is an explicit iteration space, generate an array assignment 1649 // with a user-specified iteration space and possibly with masks. These 1650 // assignments may *appear* to be scalar expressions, but the scalar 1651 // expression is evaluated at all points in the user-defined space much like 1652 // an ordinary array assignment. More specifically, the semantics inside the 1653 // FORALL much more closely resembles that of WHERE than a scalar 1654 // assignment. 1655 // Otherwise, generate a masked array assignment. The iteration space is 1656 // implied by the lhs array expression. 1657 Fortran::lower::createAnyMaskedArrayAssignment( 1658 *this, assign.lhs, assign.rhs, explicitIterSpace, implicitIterSpace, 1659 localSymbols, 1660 explicitIterationSpace() ? explicitIterSpace.stmtContext() 1661 : implicitIterSpace.stmtContext()); 1662 } 1663 1664 static bool 1665 isArraySectionWithoutVectorSubscript(const Fortran::lower::SomeExpr &expr) { 1666 return expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) && 1667 !Fortran::evaluate::UnwrapWholeSymbolDataRef(expr) && 1668 !Fortran::evaluate::HasVectorSubscript(expr); 1669 } 1670 1671 #if !defined(NDEBUG) 1672 static bool isFuncResultDesignator(const Fortran::lower::SomeExpr &expr) { 1673 const Fortran::semantics::Symbol *sym = 1674 Fortran::evaluate::GetFirstSymbol(expr); 1675 return sym && sym->IsFuncResult(); 1676 } 1677 #endif 1678 1679 static bool isWholeAllocatable(const Fortran::lower::SomeExpr &expr) { 1680 const Fortran::semantics::Symbol *sym = 1681 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr); 1682 return sym && Fortran::semantics::IsAllocatable(*sym); 1683 } 1684 1685 /// Shared for both assignments and pointer assignments. 1686 void genAssignment(const Fortran::evaluate::Assignment &assign) { 1687 Fortran::lower::StatementContext stmtCtx; 1688 mlir::Location loc = toLocation(); 1689 if (explicitIterationSpace()) { 1690 Fortran::lower::createArrayLoads(*this, explicitIterSpace, localSymbols); 1691 explicitIterSpace.genLoopNest(); 1692 } 1693 std::visit( 1694 Fortran::common::visitors{ 1695 // [1] Plain old assignment. 1696 [&](const Fortran::evaluate::Assignment::Intrinsic &) { 1697 const Fortran::semantics::Symbol *sym = 1698 Fortran::evaluate::GetLastSymbol(assign.lhs); 1699 1700 if (!sym) 1701 TODO(loc, "assignment to pointer result of function reference"); 1702 1703 std::optional<Fortran::evaluate::DynamicType> lhsType = 1704 assign.lhs.GetType(); 1705 assert(lhsType && "lhs cannot be typeless"); 1706 // Assignment to polymorphic allocatables may require changing the 1707 // variable dynamic type (See Fortran 2018 10.2.1.3 p3). 1708 if (lhsType->IsPolymorphic() && isWholeAllocatable(assign.lhs)) 1709 TODO(loc, "assignment to polymorphic allocatable"); 1710 1711 // Note: No ad-hoc handling for pointers is required here. The 1712 // target will be assigned as per 2018 10.2.1.3 p2. genExprAddr 1713 // on a pointer returns the target address and not the address of 1714 // the pointer variable. 1715 1716 if (assign.lhs.Rank() > 0 || explicitIterationSpace()) { 1717 // Array assignment 1718 // See Fortran 2018 10.2.1.3 p5, p6, and p7 1719 genArrayAssignment(assign, stmtCtx); 1720 return; 1721 } 1722 1723 // Scalar assignment 1724 const bool isNumericScalar = 1725 isNumericScalarCategory(lhsType->category()); 1726 fir::ExtendedValue rhs = isNumericScalar 1727 ? genExprValue(assign.rhs, stmtCtx) 1728 : genExprAddr(assign.rhs, stmtCtx); 1729 bool lhsIsWholeAllocatable = isWholeAllocatable(assign.lhs); 1730 llvm::Optional<fir::factory::MutableBoxReallocation> lhsRealloc; 1731 llvm::Optional<fir::MutableBoxValue> lhsMutableBox; 1732 auto lhs = [&]() -> fir::ExtendedValue { 1733 if (lhsIsWholeAllocatable) { 1734 lhsMutableBox = genExprMutableBox(loc, assign.lhs); 1735 llvm::SmallVector<mlir::Value> lengthParams; 1736 if (const fir::CharBoxValue *charBox = rhs.getCharBox()) 1737 lengthParams.push_back(charBox->getLen()); 1738 else if (fir::isDerivedWithLengthParameters(rhs)) 1739 TODO(loc, "assignment to derived type allocatable with " 1740 "length parameters"); 1741 lhsRealloc = fir::factory::genReallocIfNeeded( 1742 *builder, loc, *lhsMutableBox, 1743 /*shape=*/llvm::None, lengthParams); 1744 return lhsRealloc->newValue; 1745 } 1746 return genExprAddr(assign.lhs, stmtCtx); 1747 }(); 1748 1749 if (isNumericScalar) { 1750 // Fortran 2018 10.2.1.3 p8 and p9 1751 // Conversions should have been inserted by semantic analysis, 1752 // but they can be incorrect between the rhs and lhs. Correct 1753 // that here. 1754 mlir::Value addr = fir::getBase(lhs); 1755 mlir::Value val = fir::getBase(rhs); 1756 // A function with multiple entry points returning different 1757 // types tags all result variables with one of the largest 1758 // types to allow them to share the same storage. Assignment 1759 // to a result variable of one of the other types requires 1760 // conversion to the actual type. 1761 mlir::Type toTy = genType(assign.lhs); 1762 mlir::Value cast = 1763 builder->convertWithSemantics(loc, toTy, val); 1764 if (fir::dyn_cast_ptrEleTy(addr.getType()) != toTy) { 1765 assert(isFuncResultDesignator(assign.lhs) && "type mismatch"); 1766 addr = builder->createConvert( 1767 toLocation(), builder->getRefType(toTy), addr); 1768 } 1769 builder->create<fir::StoreOp>(loc, cast, addr); 1770 } else if (isCharacterCategory(lhsType->category())) { 1771 // Fortran 2018 10.2.1.3 p10 and p11 1772 fir::factory::CharacterExprHelper{*builder, loc}.createAssign( 1773 lhs, rhs); 1774 } else if (isDerivedCategory(lhsType->category())) { 1775 // Fortran 2018 10.2.1.3 p13 and p14 1776 // Recursively gen an assignment on each element pair. 1777 fir::factory::genRecordAssignment(*builder, loc, lhs, rhs); 1778 } else { 1779 llvm_unreachable("unknown category"); 1780 } 1781 if (lhsIsWholeAllocatable) 1782 fir::factory::finalizeRealloc( 1783 *builder, loc, lhsMutableBox.getValue(), 1784 /*lbounds=*/llvm::None, /*takeLboundsIfRealloc=*/false, 1785 lhsRealloc.getValue()); 1786 }, 1787 1788 // [2] User defined assignment. If the context is a scalar 1789 // expression then call the procedure. 1790 [&](const Fortran::evaluate::ProcedureRef &procRef) { 1791 Fortran::lower::StatementContext &ctx = 1792 explicitIterationSpace() ? explicitIterSpace.stmtContext() 1793 : stmtCtx; 1794 Fortran::lower::createSubroutineCall( 1795 *this, procRef, explicitIterSpace, implicitIterSpace, 1796 localSymbols, ctx, /*isUserDefAssignment=*/true); 1797 }, 1798 1799 // [3] Pointer assignment with possibly empty bounds-spec. R1035: a 1800 // bounds-spec is a lower bound value. 1801 [&](const Fortran::evaluate::Assignment::BoundsSpec &lbExprs) { 1802 if (IsProcedure(assign.rhs)) 1803 TODO(loc, "procedure pointer assignment"); 1804 std::optional<Fortran::evaluate::DynamicType> lhsType = 1805 assign.lhs.GetType(); 1806 std::optional<Fortran::evaluate::DynamicType> rhsType = 1807 assign.rhs.GetType(); 1808 // Polymorphic lhs/rhs may need more care. See F2018 10.2.2.3. 1809 if ((lhsType && lhsType->IsPolymorphic()) || 1810 (rhsType && rhsType->IsPolymorphic())) 1811 TODO(loc, "pointer assignment involving polymorphic entity"); 1812 1813 // FIXME: in the explicit space context, we want to use 1814 // ScalarArrayExprLowering here. 1815 fir::MutableBoxValue lhs = genExprMutableBox(loc, assign.lhs); 1816 llvm::SmallVector<mlir::Value> lbounds; 1817 for (const Fortran::evaluate::ExtentExpr &lbExpr : lbExprs) 1818 lbounds.push_back( 1819 fir::getBase(genExprValue(toEvExpr(lbExpr), stmtCtx))); 1820 Fortran::lower::associateMutableBox(*this, loc, lhs, assign.rhs, 1821 lbounds, stmtCtx); 1822 if (explicitIterationSpace()) { 1823 mlir::ValueRange inners = explicitIterSpace.getInnerArgs(); 1824 if (!inners.empty()) { 1825 // TODO: should force a copy-in/copy-out here. 1826 // e.g., obj%ptr(i+1) => obj%ptr(i) 1827 builder->create<fir::ResultOp>(loc, inners); 1828 } 1829 } 1830 }, 1831 1832 // [4] Pointer assignment with bounds-remapping. R1036: a 1833 // bounds-remapping is a pair, lower bound and upper bound. 1834 [&](const Fortran::evaluate::Assignment::BoundsRemapping 1835 &boundExprs) { 1836 std::optional<Fortran::evaluate::DynamicType> lhsType = 1837 assign.lhs.GetType(); 1838 std::optional<Fortran::evaluate::DynamicType> rhsType = 1839 assign.rhs.GetType(); 1840 // Polymorphic lhs/rhs may need more care. See F2018 10.2.2.3. 1841 if ((lhsType && lhsType->IsPolymorphic()) || 1842 (rhsType && rhsType->IsPolymorphic())) 1843 TODO(loc, "pointer assignment involving polymorphic entity"); 1844 1845 // FIXME: in the explicit space context, we want to use 1846 // ScalarArrayExprLowering here. 1847 fir::MutableBoxValue lhs = genExprMutableBox(loc, assign.lhs); 1848 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 1849 assign.rhs)) { 1850 fir::factory::disassociateMutableBox(*builder, loc, lhs); 1851 return; 1852 } 1853 llvm::SmallVector<mlir::Value> lbounds; 1854 llvm::SmallVector<mlir::Value> ubounds; 1855 for (const std::pair<Fortran::evaluate::ExtentExpr, 1856 Fortran::evaluate::ExtentExpr> &pair : 1857 boundExprs) { 1858 const Fortran::evaluate::ExtentExpr &lbExpr = pair.first; 1859 const Fortran::evaluate::ExtentExpr &ubExpr = pair.second; 1860 lbounds.push_back( 1861 fir::getBase(genExprValue(toEvExpr(lbExpr), stmtCtx))); 1862 ubounds.push_back( 1863 fir::getBase(genExprValue(toEvExpr(ubExpr), stmtCtx))); 1864 } 1865 // Do not generate a temp in case rhs is an array section. 1866 fir::ExtendedValue rhs = 1867 isArraySectionWithoutVectorSubscript(assign.rhs) 1868 ? Fortran::lower::createSomeArrayBox( 1869 *this, assign.rhs, localSymbols, stmtCtx) 1870 : genExprAddr(assign.rhs, stmtCtx); 1871 fir::factory::associateMutableBoxWithRemap(*builder, loc, lhs, 1872 rhs, lbounds, ubounds); 1873 if (explicitIterationSpace()) { 1874 mlir::ValueRange inners = explicitIterSpace.getInnerArgs(); 1875 if (!inners.empty()) { 1876 // TODO: should force a copy-in/copy-out here. 1877 // e.g., obj%ptr(i+1) => obj%ptr(i) 1878 builder->create<fir::ResultOp>(loc, inners); 1879 } 1880 } 1881 }, 1882 }, 1883 assign.u); 1884 if (explicitIterationSpace()) 1885 Fortran::lower::createArrayMergeStores(*this, explicitIterSpace); 1886 } 1887 1888 void genFIR(const Fortran::parser::WhereConstruct &c) { 1889 implicitIterSpace.growStack(); 1890 genNestedStatement( 1891 std::get< 1892 Fortran::parser::Statement<Fortran::parser::WhereConstructStmt>>( 1893 c.t)); 1894 for (const auto &body : 1895 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(c.t)) 1896 genFIR(body); 1897 for (const auto &e : 1898 std::get<std::list<Fortran::parser::WhereConstruct::MaskedElsewhere>>( 1899 c.t)) 1900 genFIR(e); 1901 if (const auto &e = 1902 std::get<std::optional<Fortran::parser::WhereConstruct::Elsewhere>>( 1903 c.t); 1904 e.has_value()) 1905 genFIR(*e); 1906 genNestedStatement( 1907 std::get<Fortran::parser::Statement<Fortran::parser::EndWhereStmt>>( 1908 c.t)); 1909 } 1910 void genFIR(const Fortran::parser::WhereBodyConstruct &body) { 1911 std::visit( 1912 Fortran::common::visitors{ 1913 [&](const Fortran::parser::Statement< 1914 Fortran::parser::AssignmentStmt> &stmt) { 1915 genNestedStatement(stmt); 1916 }, 1917 [&](const Fortran::parser::Statement<Fortran::parser::WhereStmt> 1918 &stmt) { genNestedStatement(stmt); }, 1919 [&](const Fortran::common::Indirection< 1920 Fortran::parser::WhereConstruct> &c) { genFIR(c.value()); }, 1921 }, 1922 body.u); 1923 } 1924 void genFIR(const Fortran::parser::WhereConstructStmt &stmt) { 1925 implicitIterSpace.append(Fortran::semantics::GetExpr( 1926 std::get<Fortran::parser::LogicalExpr>(stmt.t))); 1927 } 1928 void genFIR(const Fortran::parser::WhereConstruct::MaskedElsewhere &ew) { 1929 genNestedStatement( 1930 std::get< 1931 Fortran::parser::Statement<Fortran::parser::MaskedElsewhereStmt>>( 1932 ew.t)); 1933 for (const auto &body : 1934 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew.t)) 1935 genFIR(body); 1936 } 1937 void genFIR(const Fortran::parser::MaskedElsewhereStmt &stmt) { 1938 implicitIterSpace.append(Fortran::semantics::GetExpr( 1939 std::get<Fortran::parser::LogicalExpr>(stmt.t))); 1940 } 1941 void genFIR(const Fortran::parser::WhereConstruct::Elsewhere &ew) { 1942 genNestedStatement( 1943 std::get<Fortran::parser::Statement<Fortran::parser::ElsewhereStmt>>( 1944 ew.t)); 1945 for (const auto &body : 1946 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew.t)) 1947 genFIR(body); 1948 } 1949 void genFIR(const Fortran::parser::ElsewhereStmt &stmt) { 1950 implicitIterSpace.append(nullptr); 1951 } 1952 void genFIR(const Fortran::parser::EndWhereStmt &) { 1953 implicitIterSpace.shrinkStack(); 1954 } 1955 1956 void genFIR(const Fortran::parser::WhereStmt &stmt) { 1957 Fortran::lower::StatementContext stmtCtx; 1958 const auto &assign = std::get<Fortran::parser::AssignmentStmt>(stmt.t); 1959 implicitIterSpace.growStack(); 1960 implicitIterSpace.append(Fortran::semantics::GetExpr( 1961 std::get<Fortran::parser::LogicalExpr>(stmt.t))); 1962 genAssignment(*assign.typedAssignment->v); 1963 implicitIterSpace.shrinkStack(); 1964 } 1965 1966 void genFIR(const Fortran::parser::PointerAssignmentStmt &stmt) { 1967 genAssignment(*stmt.typedAssignment->v); 1968 } 1969 1970 void genFIR(const Fortran::parser::AssignmentStmt &stmt) { 1971 genAssignment(*stmt.typedAssignment->v); 1972 } 1973 1974 void genFIR(const Fortran::parser::SyncAllStmt &stmt) { 1975 genSyncAllStatement(*this, stmt); 1976 } 1977 1978 void genFIR(const Fortran::parser::SyncImagesStmt &stmt) { 1979 genSyncImagesStatement(*this, stmt); 1980 } 1981 1982 void genFIR(const Fortran::parser::SyncMemoryStmt &stmt) { 1983 genSyncMemoryStatement(*this, stmt); 1984 } 1985 1986 void genFIR(const Fortran::parser::SyncTeamStmt &stmt) { 1987 genSyncTeamStatement(*this, stmt); 1988 } 1989 1990 void genFIR(const Fortran::parser::UnlockStmt &stmt) { 1991 genUnlockStatement(*this, stmt); 1992 } 1993 1994 void genFIR(const Fortran::parser::AssignStmt &stmt) { 1995 const Fortran::semantics::Symbol &symbol = 1996 *std::get<Fortran::parser::Name>(stmt.t).symbol; 1997 mlir::Location loc = toLocation(); 1998 mlir::Value labelValue = builder->createIntegerConstant( 1999 loc, genType(symbol), std::get<Fortran::parser::Label>(stmt.t)); 2000 builder->create<fir::StoreOp>(loc, labelValue, getSymbolAddress(symbol)); 2001 } 2002 2003 void genFIR(const Fortran::parser::FormatStmt &) { 2004 // do nothing. 2005 2006 // FORMAT statements have no semantics. They may be lowered if used by a 2007 // data transfer statement. 2008 } 2009 2010 void genFIR(const Fortran::parser::PauseStmt &stmt) { 2011 genPauseStatement(*this, stmt); 2012 } 2013 2014 // call FAIL IMAGE in runtime 2015 void genFIR(const Fortran::parser::FailImageStmt &stmt) { 2016 TODO(toLocation(), "FailImageStmt lowering"); 2017 } 2018 2019 // call STOP, ERROR STOP in runtime 2020 void genFIR(const Fortran::parser::StopStmt &stmt) { 2021 genStopStatement(*this, stmt); 2022 } 2023 2024 void genFIR(const Fortran::parser::ReturnStmt &stmt) { 2025 Fortran::lower::pft::FunctionLikeUnit *funit = 2026 getEval().getOwningProcedure(); 2027 assert(funit && "not inside main program, function or subroutine"); 2028 if (funit->isMainProgram()) { 2029 genExitRoutine(); 2030 return; 2031 } 2032 mlir::Location loc = toLocation(); 2033 if (stmt.v) { 2034 // Alternate return statement - If this is a subroutine where some 2035 // alternate entries have alternate returns, but the active entry point 2036 // does not, ignore the alternate return value. Otherwise, assign it 2037 // to the compiler-generated result variable. 2038 const Fortran::semantics::Symbol &symbol = funit->getSubprogramSymbol(); 2039 if (Fortran::semantics::HasAlternateReturns(symbol)) { 2040 Fortran::lower::StatementContext stmtCtx; 2041 const Fortran::lower::SomeExpr *expr = 2042 Fortran::semantics::GetExpr(*stmt.v); 2043 assert(expr && "missing alternate return expression"); 2044 mlir::Value altReturnIndex = builder->createConvert( 2045 loc, builder->getIndexType(), createFIRExpr(loc, expr, stmtCtx)); 2046 builder->create<fir::StoreOp>(loc, altReturnIndex, 2047 getAltReturnResult(symbol)); 2048 } 2049 } 2050 // Branch to the last block of the SUBROUTINE, which has the actual return. 2051 if (!funit->finalBlock) { 2052 mlir::OpBuilder::InsertPoint insPt = builder->saveInsertionPoint(); 2053 funit->finalBlock = builder->createBlock(&builder->getRegion()); 2054 builder->restoreInsertionPoint(insPt); 2055 } 2056 builder->create<mlir::cf::BranchOp>(loc, funit->finalBlock); 2057 } 2058 2059 void genFIR(const Fortran::parser::CycleStmt &) { 2060 genFIRBranch(getEval().controlSuccessor->block); 2061 } 2062 void genFIR(const Fortran::parser::ExitStmt &) { 2063 genFIRBranch(getEval().controlSuccessor->block); 2064 } 2065 void genFIR(const Fortran::parser::GotoStmt &) { 2066 genFIRBranch(getEval().controlSuccessor->block); 2067 } 2068 2069 void genFIR(const Fortran::parser::EndDoStmt &) { 2070 TODO(toLocation(), "EndDoStmt lowering"); 2071 } 2072 2073 // Nop statements - No code, or code is generated at the construct level. 2074 void genFIR(const Fortran::parser::AssociateStmt &) {} // nop 2075 void genFIR(const Fortran::parser::CaseStmt &) {} // nop 2076 void genFIR(const Fortran::parser::ContinueStmt &) {} // nop 2077 void genFIR(const Fortran::parser::ElseIfStmt &) {} // nop 2078 void genFIR(const Fortran::parser::ElseStmt &) {} // nop 2079 void genFIR(const Fortran::parser::EndAssociateStmt &) {} // nop 2080 void genFIR(const Fortran::parser::EndFunctionStmt &) {} // nop 2081 void genFIR(const Fortran::parser::EndIfStmt &) {} // nop 2082 void genFIR(const Fortran::parser::EndMpSubprogramStmt &) {} // nop 2083 void genFIR(const Fortran::parser::EndSelectStmt &) {} // nop 2084 void genFIR(const Fortran::parser::EndSubroutineStmt &) {} // nop 2085 void genFIR(const Fortran::parser::EntryStmt &) {} // nop 2086 void genFIR(const Fortran::parser::IfStmt &) {} // nop 2087 void genFIR(const Fortran::parser::IfThenStmt &) {} // nop 2088 2089 void genFIR(const Fortran::parser::NonLabelDoStmt &) { 2090 TODO(toLocation(), "NonLabelDoStmt lowering"); 2091 } 2092 2093 void genFIR(const Fortran::parser::OmpEndLoopDirective &) { 2094 TODO(toLocation(), "OmpEndLoopDirective lowering"); 2095 } 2096 2097 void genFIR(const Fortran::parser::NamelistStmt &) { 2098 TODO(toLocation(), "NamelistStmt lowering"); 2099 } 2100 2101 /// Generate FIR for the Evaluation `eval`. 2102 void genFIR(Fortran::lower::pft::Evaluation &eval, 2103 bool unstructuredContext = true) { 2104 if (unstructuredContext) { 2105 // When transitioning from unstructured to structured code, 2106 // the structured code could be a target that starts a new block. 2107 maybeStartBlock(eval.isConstruct() && eval.lowerAsStructured() 2108 ? eval.getFirstNestedEvaluation().block 2109 : eval.block); 2110 } 2111 2112 setCurrentEval(eval); 2113 setCurrentPosition(eval.position); 2114 eval.visit([&](const auto &stmt) { genFIR(stmt); }); 2115 2116 if (unstructuredContext && blockIsUnterminated()) { 2117 // Exit from an unstructured IF or SELECT construct block. 2118 Fortran::lower::pft::Evaluation *successor{}; 2119 if (eval.isActionStmt()) 2120 successor = eval.controlSuccessor; 2121 else if (eval.isConstruct() && 2122 eval.getLastNestedEvaluation() 2123 .lexicalSuccessor->isIntermediateConstructStmt()) 2124 successor = eval.constructExit; 2125 if (successor && successor->block) 2126 genFIRBranch(successor->block); 2127 } 2128 } 2129 2130 /// Map mlir function block arguments to the corresponding Fortran dummy 2131 /// variables. When the result is passed as a hidden argument, the Fortran 2132 /// result is also mapped. The symbol map is used to hold this mapping. 2133 void mapDummiesAndResults(Fortran::lower::pft::FunctionLikeUnit &funit, 2134 const Fortran::lower::CalleeInterface &callee) { 2135 assert(builder && "require a builder object at this point"); 2136 using PassBy = Fortran::lower::CalleeInterface::PassEntityBy; 2137 auto mapPassedEntity = [&](const auto arg) -> void { 2138 if (arg.passBy == PassBy::AddressAndLength) { 2139 // TODO: now that fir call has some attributes regarding character 2140 // return, PassBy::AddressAndLength should be retired. 2141 mlir::Location loc = toLocation(); 2142 fir::factory::CharacterExprHelper charHelp{*builder, loc}; 2143 mlir::Value box = 2144 charHelp.createEmboxChar(arg.firArgument, arg.firLength); 2145 addSymbol(arg.entity->get(), box); 2146 } else { 2147 if (arg.entity.has_value()) { 2148 addSymbol(arg.entity->get(), arg.firArgument); 2149 } else { 2150 assert(funit.parentHasHostAssoc()); 2151 funit.parentHostAssoc().internalProcedureBindings(*this, 2152 localSymbols); 2153 } 2154 } 2155 }; 2156 for (const Fortran::lower::CalleeInterface::PassedEntity &arg : 2157 callee.getPassedArguments()) 2158 mapPassedEntity(arg); 2159 2160 // Allocate local skeleton instances of dummies from other entry points. 2161 // Most of these locals will not survive into final generated code, but 2162 // some will. It is illegal to reference them at run time if they do. 2163 for (const Fortran::semantics::Symbol *arg : 2164 funit.nonUniversalDummyArguments) { 2165 if (lookupSymbol(*arg)) 2166 continue; 2167 mlir::Type type = genType(*arg); 2168 // TODO: Account for VALUE arguments (and possibly other variants). 2169 type = builder->getRefType(type); 2170 addSymbol(*arg, builder->create<fir::UndefOp>(toLocation(), type)); 2171 } 2172 if (std::optional<Fortran::lower::CalleeInterface::PassedEntity> 2173 passedResult = callee.getPassedResult()) { 2174 mapPassedEntity(*passedResult); 2175 // FIXME: need to make sure things are OK here. addSymbol may not be OK 2176 if (funit.primaryResult && 2177 passedResult->entity->get() != *funit.primaryResult) 2178 addSymbol(*funit.primaryResult, 2179 getSymbolAddress(passedResult->entity->get())); 2180 } 2181 } 2182 2183 /// Instantiate variable \p var and add it to the symbol map. 2184 /// See ConvertVariable.cpp. 2185 void instantiateVar(const Fortran::lower::pft::Variable &var, 2186 Fortran::lower::AggregateStoreMap &storeMap) { 2187 Fortran::lower::instantiateVariable(*this, var, localSymbols, storeMap); 2188 } 2189 2190 /// Prepare to translate a new function 2191 void startNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) { 2192 assert(!builder && "expected nullptr"); 2193 Fortran::lower::CalleeInterface callee(funit, *this); 2194 mlir::FuncOp func = callee.addEntryBlockAndMapArguments(); 2195 builder = new fir::FirOpBuilder(func, bridge.getKindMap()); 2196 assert(builder && "FirOpBuilder did not instantiate"); 2197 builder->setInsertionPointToStart(&func.front()); 2198 func.setVisibility(mlir::SymbolTable::Visibility::Public); 2199 2200 mapDummiesAndResults(funit, callee); 2201 2202 // Note: not storing Variable references because getOrderedSymbolTable 2203 // below returns a temporary. 2204 llvm::SmallVector<Fortran::lower::pft::Variable> deferredFuncResultList; 2205 2206 // Backup actual argument for entry character results 2207 // with different lengths. It needs to be added to the non 2208 // primary results symbol before mapSymbolAttributes is called. 2209 Fortran::lower::SymbolBox resultArg; 2210 if (std::optional<Fortran::lower::CalleeInterface::PassedEntity> 2211 passedResult = callee.getPassedResult()) 2212 resultArg = lookupSymbol(passedResult->entity->get()); 2213 2214 Fortran::lower::AggregateStoreMap storeMap; 2215 // The front-end is currently not adding module variables referenced 2216 // in a module procedure as host associated. As a result we need to 2217 // instantiate all module variables here if this is a module procedure. 2218 // It is likely that the front-end behavior should change here. 2219 // This also applies to internal procedures inside module procedures. 2220 if (auto *module = Fortran::lower::pft::getAncestor< 2221 Fortran::lower::pft::ModuleLikeUnit>(funit)) 2222 for (const Fortran::lower::pft::Variable &var : 2223 module->getOrderedSymbolTable()) 2224 instantiateVar(var, storeMap); 2225 2226 mlir::Value primaryFuncResultStorage; 2227 for (const Fortran::lower::pft::Variable &var : 2228 funit.getOrderedSymbolTable()) { 2229 // Always instantiate aggregate storage blocks. 2230 if (var.isAggregateStore()) { 2231 instantiateVar(var, storeMap); 2232 continue; 2233 } 2234 const Fortran::semantics::Symbol &sym = var.getSymbol(); 2235 if (funit.parentHasHostAssoc()) { 2236 // Never instantitate host associated variables, as they are already 2237 // instantiated from an argument tuple. Instead, just bind the symbol to 2238 // the reference to the host variable, which must be in the map. 2239 const Fortran::semantics::Symbol &ultimate = sym.GetUltimate(); 2240 if (funit.parentHostAssoc().isAssociated(ultimate)) { 2241 Fortran::lower::SymbolBox hostBox = 2242 localSymbols.lookupSymbol(ultimate); 2243 assert(hostBox && "host association is not in map"); 2244 localSymbols.addSymbol(sym, hostBox.toExtendedValue()); 2245 continue; 2246 } 2247 } 2248 if (!sym.IsFuncResult() || !funit.primaryResult) { 2249 instantiateVar(var, storeMap); 2250 } else if (&sym == funit.primaryResult) { 2251 instantiateVar(var, storeMap); 2252 primaryFuncResultStorage = getSymbolAddress(sym); 2253 } else { 2254 deferredFuncResultList.push_back(var); 2255 } 2256 } 2257 2258 // If this is a host procedure with host associations, then create the tuple 2259 // of pointers for passing to the internal procedures. 2260 if (!funit.getHostAssoc().empty()) 2261 funit.getHostAssoc().hostProcedureBindings(*this, localSymbols); 2262 2263 /// TODO: should use same mechanism as equivalence? 2264 /// One blocking point is character entry returns that need special handling 2265 /// since they are not locally allocated but come as argument. CHARACTER(*) 2266 /// is not something that fit wells with equivalence lowering. 2267 for (const Fortran::lower::pft::Variable &altResult : 2268 deferredFuncResultList) { 2269 if (std::optional<Fortran::lower::CalleeInterface::PassedEntity> 2270 passedResult = callee.getPassedResult()) 2271 addSymbol(altResult.getSymbol(), resultArg.getAddr()); 2272 Fortran::lower::StatementContext stmtCtx; 2273 Fortran::lower::mapSymbolAttributes(*this, altResult, localSymbols, 2274 stmtCtx, primaryFuncResultStorage); 2275 } 2276 2277 // Create most function blocks in advance. 2278 createEmptyBlocks(funit.evaluationList); 2279 2280 // Reinstate entry block as the current insertion point. 2281 builder->setInsertionPointToEnd(&func.front()); 2282 2283 if (callee.hasAlternateReturns()) { 2284 // Create a local temp to hold the alternate return index. 2285 // Give it an integer index type and the subroutine name (for dumps). 2286 // Attach it to the subroutine symbol in the localSymbols map. 2287 // Initialize it to zero, the "fallthrough" alternate return value. 2288 const Fortran::semantics::Symbol &symbol = funit.getSubprogramSymbol(); 2289 mlir::Location loc = toLocation(); 2290 mlir::Type idxTy = builder->getIndexType(); 2291 mlir::Value altResult = 2292 builder->createTemporary(loc, idxTy, toStringRef(symbol.name())); 2293 addSymbol(symbol, altResult); 2294 mlir::Value zero = builder->createIntegerConstant(loc, idxTy, 0); 2295 builder->create<fir::StoreOp>(loc, zero, altResult); 2296 } 2297 2298 if (Fortran::lower::pft::Evaluation *alternateEntryEval = 2299 funit.getEntryEval()) 2300 genFIRBranch(alternateEntryEval->lexicalSuccessor->block); 2301 } 2302 2303 /// Create global blocks for the current function. This eliminates the 2304 /// distinction between forward and backward targets when generating 2305 /// branches. A block is "global" if it can be the target of a GOTO or 2306 /// other source code branch. A block that can only be targeted by a 2307 /// compiler generated branch is "local". For example, a DO loop preheader 2308 /// block containing loop initialization code is global. A loop header 2309 /// block, which is the target of the loop back edge, is local. Blocks 2310 /// belong to a region. Any block within a nested region must be replaced 2311 /// with a block belonging to that region. Branches may not cross region 2312 /// boundaries. 2313 void createEmptyBlocks( 2314 std::list<Fortran::lower::pft::Evaluation> &evaluationList) { 2315 mlir::Region *region = &builder->getRegion(); 2316 for (Fortran::lower::pft::Evaluation &eval : evaluationList) { 2317 if (eval.isNewBlock) 2318 eval.block = builder->createBlock(region); 2319 if (eval.isConstruct() || eval.isDirective()) { 2320 if (eval.lowerAsUnstructured()) { 2321 createEmptyBlocks(eval.getNestedEvaluations()); 2322 } else if (eval.hasNestedEvaluations()) { 2323 // A structured construct that is a target starts a new block. 2324 Fortran::lower::pft::Evaluation &constructStmt = 2325 eval.getFirstNestedEvaluation(); 2326 if (constructStmt.isNewBlock) 2327 constructStmt.block = builder->createBlock(region); 2328 } 2329 } 2330 } 2331 } 2332 2333 /// Return the predicate: "current block does not have a terminator branch". 2334 bool blockIsUnterminated() { 2335 mlir::Block *currentBlock = builder->getBlock(); 2336 return currentBlock->empty() || 2337 !currentBlock->back().hasTrait<mlir::OpTrait::IsTerminator>(); 2338 } 2339 2340 /// Unconditionally switch code insertion to a new block. 2341 void startBlock(mlir::Block *newBlock) { 2342 assert(newBlock && "missing block"); 2343 // Default termination for the current block is a fallthrough branch to 2344 // the new block. 2345 if (blockIsUnterminated()) 2346 genFIRBranch(newBlock); 2347 // Some blocks may be re/started more than once, and might not be empty. 2348 // If the new block already has (only) a terminator, set the insertion 2349 // point to the start of the block. Otherwise set it to the end. 2350 builder->setInsertionPointToStart(newBlock); 2351 if (blockIsUnterminated()) 2352 builder->setInsertionPointToEnd(newBlock); 2353 } 2354 2355 /// Conditionally switch code insertion to a new block. 2356 void maybeStartBlock(mlir::Block *newBlock) { 2357 if (newBlock) 2358 startBlock(newBlock); 2359 } 2360 2361 /// Emit return and cleanup after the function has been translated. 2362 void endNewFunction(Fortran::lower::pft::FunctionLikeUnit &funit) { 2363 setCurrentPosition(Fortran::lower::pft::stmtSourceLoc(funit.endStmt)); 2364 if (funit.isMainProgram()) 2365 genExitRoutine(); 2366 else 2367 genFIRProcedureExit(funit, funit.getSubprogramSymbol()); 2368 funit.finalBlock = nullptr; 2369 LLVM_DEBUG(llvm::dbgs() << "*** Lowering result:\n\n" 2370 << *builder->getFunction() << '\n'); 2371 // FIXME: Simplification should happen in a normal pass, not here. 2372 mlir::IRRewriter rewriter(*builder); 2373 (void)mlir::simplifyRegions(rewriter, 2374 {builder->getRegion()}); // remove dead code 2375 delete builder; 2376 builder = nullptr; 2377 hostAssocTuple = mlir::Value{}; 2378 localSymbols.clear(); 2379 } 2380 2381 /// Helper to generate GlobalOps when the builder is not positioned in any 2382 /// region block. This is required because the FirOpBuilder assumes it is 2383 /// always positioned inside a region block when creating globals, the easiest 2384 /// way comply is to create a dummy function and to throw it afterwards. 2385 void createGlobalOutsideOfFunctionLowering( 2386 const std::function<void()> &createGlobals) { 2387 // FIXME: get rid of the bogus function context and instantiate the 2388 // globals directly into the module. 2389 mlir::MLIRContext *context = &getMLIRContext(); 2390 mlir::FuncOp func = fir::FirOpBuilder::createFunction( 2391 mlir::UnknownLoc::get(context), getModuleOp(), 2392 fir::NameUniquer::doGenerated("Sham"), 2393 mlir::FunctionType::get(context, llvm::None, llvm::None)); 2394 func.addEntryBlock(); 2395 builder = new fir::FirOpBuilder(func, bridge.getKindMap()); 2396 createGlobals(); 2397 if (mlir::Region *region = func.getCallableRegion()) 2398 region->dropAllReferences(); 2399 func.erase(); 2400 delete builder; 2401 builder = nullptr; 2402 localSymbols.clear(); 2403 } 2404 /// Instantiate the data from a BLOCK DATA unit. 2405 void lowerBlockData(Fortran::lower::pft::BlockDataUnit &bdunit) { 2406 createGlobalOutsideOfFunctionLowering([&]() { 2407 Fortran::lower::AggregateStoreMap fakeMap; 2408 for (const auto &[_, sym] : bdunit.symTab) { 2409 if (sym->has<Fortran::semantics::ObjectEntityDetails>()) { 2410 Fortran::lower::pft::Variable var(*sym, true); 2411 instantiateVar(var, fakeMap); 2412 } 2413 } 2414 }); 2415 } 2416 2417 /// Lower a procedure (nest). 2418 void lowerFunc(Fortran::lower::pft::FunctionLikeUnit &funit) { 2419 if (!funit.isMainProgram()) { 2420 const Fortran::semantics::Symbol &procSymbol = 2421 funit.getSubprogramSymbol(); 2422 if (procSymbol.owner().IsSubmodule()) { 2423 TODO(toLocation(), "support submodules"); 2424 return; 2425 } 2426 } 2427 setCurrentPosition(funit.getStartingSourceLoc()); 2428 for (int entryIndex = 0, last = funit.entryPointList.size(); 2429 entryIndex < last; ++entryIndex) { 2430 funit.setActiveEntry(entryIndex); 2431 startNewFunction(funit); // the entry point for lowering this procedure 2432 for (Fortran::lower::pft::Evaluation &eval : funit.evaluationList) 2433 genFIR(eval); 2434 endNewFunction(funit); 2435 } 2436 funit.setActiveEntry(0); 2437 for (Fortran::lower::pft::FunctionLikeUnit &f : funit.nestedFunctions) 2438 lowerFunc(f); // internal procedure 2439 } 2440 2441 /// Lower module variable definitions to fir::globalOp and OpenMP/OpenACC 2442 /// declarative construct. 2443 void lowerModuleDeclScope(Fortran::lower::pft::ModuleLikeUnit &mod) { 2444 setCurrentPosition(mod.getStartingSourceLoc()); 2445 createGlobalOutsideOfFunctionLowering([&]() { 2446 for (const Fortran::lower::pft::Variable &var : 2447 mod.getOrderedSymbolTable()) { 2448 // Only define the variables owned by this module. 2449 const Fortran::semantics::Scope *owningScope = var.getOwningScope(); 2450 if (!owningScope || mod.getScope() == *owningScope) 2451 Fortran::lower::defineModuleVariable(*this, var); 2452 } 2453 for (auto &eval : mod.evaluationList) 2454 genFIR(eval); 2455 }); 2456 } 2457 2458 /// Lower functions contained in a module. 2459 void lowerMod(Fortran::lower::pft::ModuleLikeUnit &mod) { 2460 for (Fortran::lower::pft::FunctionLikeUnit &f : mod.nestedFunctions) 2461 lowerFunc(f); 2462 } 2463 2464 void setCurrentPosition(const Fortran::parser::CharBlock &position) { 2465 if (position != Fortran::parser::CharBlock{}) 2466 currentPosition = position; 2467 } 2468 2469 /// Set current position at the location of \p parseTreeNode. Note that the 2470 /// position is updated automatically when visiting statements, but not when 2471 /// entering higher level nodes like constructs or procedures. This helper is 2472 /// intended to cover the latter cases. 2473 template <typename A> 2474 void setCurrentPositionAt(const A &parseTreeNode) { 2475 setCurrentPosition(Fortran::parser::FindSourceLocation(parseTreeNode)); 2476 } 2477 2478 //===--------------------------------------------------------------------===// 2479 // Utility methods 2480 //===--------------------------------------------------------------------===// 2481 2482 /// Convert a parser CharBlock to a Location 2483 mlir::Location toLocation(const Fortran::parser::CharBlock &cb) { 2484 return genLocation(cb); 2485 } 2486 2487 mlir::Location toLocation() { return toLocation(currentPosition); } 2488 void setCurrentEval(Fortran::lower::pft::Evaluation &eval) { 2489 evalPtr = &eval; 2490 } 2491 Fortran::lower::pft::Evaluation &getEval() { 2492 assert(evalPtr); 2493 return *evalPtr; 2494 } 2495 2496 std::optional<Fortran::evaluate::Shape> 2497 getShape(const Fortran::lower::SomeExpr &expr) { 2498 return Fortran::evaluate::GetShape(foldingContext, expr); 2499 } 2500 2501 //===--------------------------------------------------------------------===// 2502 // Analysis on a nested explicit iteration space. 2503 //===--------------------------------------------------------------------===// 2504 2505 void analyzeExplicitSpace(const Fortran::parser::ConcurrentHeader &header) { 2506 explicitIterSpace.pushLevel(); 2507 for (const Fortran::parser::ConcurrentControl &ctrl : 2508 std::get<std::list<Fortran::parser::ConcurrentControl>>(header.t)) { 2509 const Fortran::semantics::Symbol *ctrlVar = 2510 std::get<Fortran::parser::Name>(ctrl.t).symbol; 2511 explicitIterSpace.addSymbol(ctrlVar); 2512 } 2513 if (const auto &mask = 2514 std::get<std::optional<Fortran::parser::ScalarLogicalExpr>>( 2515 header.t); 2516 mask.has_value()) 2517 analyzeExplicitSpace(*Fortran::semantics::GetExpr(*mask)); 2518 } 2519 template <bool LHS = false, typename A> 2520 void analyzeExplicitSpace(const Fortran::evaluate::Expr<A> &e) { 2521 explicitIterSpace.exprBase(&e, LHS); 2522 } 2523 void analyzeExplicitSpace(const Fortran::evaluate::Assignment *assign) { 2524 auto analyzeAssign = [&](const Fortran::lower::SomeExpr &lhs, 2525 const Fortran::lower::SomeExpr &rhs) { 2526 analyzeExplicitSpace</*LHS=*/true>(lhs); 2527 analyzeExplicitSpace(rhs); 2528 }; 2529 std::visit( 2530 Fortran::common::visitors{ 2531 [&](const Fortran::evaluate::ProcedureRef &procRef) { 2532 // Ensure the procRef expressions are the one being visited. 2533 assert(procRef.arguments().size() == 2); 2534 const Fortran::lower::SomeExpr *lhs = 2535 procRef.arguments()[0].value().UnwrapExpr(); 2536 const Fortran::lower::SomeExpr *rhs = 2537 procRef.arguments()[1].value().UnwrapExpr(); 2538 assert(lhs && rhs && 2539 "user defined assignment arguments must be expressions"); 2540 analyzeAssign(*lhs, *rhs); 2541 }, 2542 [&](const auto &) { analyzeAssign(assign->lhs, assign->rhs); }}, 2543 assign->u); 2544 explicitIterSpace.endAssign(); 2545 } 2546 void analyzeExplicitSpace(const Fortran::parser::ForallAssignmentStmt &stmt) { 2547 std::visit([&](const auto &s) { analyzeExplicitSpace(s); }, stmt.u); 2548 } 2549 void analyzeExplicitSpace(const Fortran::parser::AssignmentStmt &s) { 2550 analyzeExplicitSpace(s.typedAssignment->v.operator->()); 2551 } 2552 void analyzeExplicitSpace(const Fortran::parser::PointerAssignmentStmt &s) { 2553 analyzeExplicitSpace(s.typedAssignment->v.operator->()); 2554 } 2555 void analyzeExplicitSpace(const Fortran::parser::WhereConstruct &c) { 2556 analyzeExplicitSpace( 2557 std::get< 2558 Fortran::parser::Statement<Fortran::parser::WhereConstructStmt>>( 2559 c.t) 2560 .statement); 2561 for (const Fortran::parser::WhereBodyConstruct &body : 2562 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(c.t)) 2563 analyzeExplicitSpace(body); 2564 for (const Fortran::parser::WhereConstruct::MaskedElsewhere &e : 2565 std::get<std::list<Fortran::parser::WhereConstruct::MaskedElsewhere>>( 2566 c.t)) 2567 analyzeExplicitSpace(e); 2568 if (const auto &e = 2569 std::get<std::optional<Fortran::parser::WhereConstruct::Elsewhere>>( 2570 c.t); 2571 e.has_value()) 2572 analyzeExplicitSpace(e.operator->()); 2573 } 2574 void analyzeExplicitSpace(const Fortran::parser::WhereConstructStmt &ws) { 2575 const Fortran::lower::SomeExpr *exp = Fortran::semantics::GetExpr( 2576 std::get<Fortran::parser::LogicalExpr>(ws.t)); 2577 addMaskVariable(exp); 2578 analyzeExplicitSpace(*exp); 2579 } 2580 void analyzeExplicitSpace( 2581 const Fortran::parser::WhereConstruct::MaskedElsewhere &ew) { 2582 analyzeExplicitSpace( 2583 std::get< 2584 Fortran::parser::Statement<Fortran::parser::MaskedElsewhereStmt>>( 2585 ew.t) 2586 .statement); 2587 for (const Fortran::parser::WhereBodyConstruct &e : 2588 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew.t)) 2589 analyzeExplicitSpace(e); 2590 } 2591 void analyzeExplicitSpace(const Fortran::parser::WhereBodyConstruct &body) { 2592 std::visit(Fortran::common::visitors{ 2593 [&](const Fortran::common::Indirection< 2594 Fortran::parser::WhereConstruct> &wc) { 2595 analyzeExplicitSpace(wc.value()); 2596 }, 2597 [&](const auto &s) { analyzeExplicitSpace(s.statement); }}, 2598 body.u); 2599 } 2600 void analyzeExplicitSpace(const Fortran::parser::MaskedElsewhereStmt &stmt) { 2601 const Fortran::lower::SomeExpr *exp = Fortran::semantics::GetExpr( 2602 std::get<Fortran::parser::LogicalExpr>(stmt.t)); 2603 addMaskVariable(exp); 2604 analyzeExplicitSpace(*exp); 2605 } 2606 void 2607 analyzeExplicitSpace(const Fortran::parser::WhereConstruct::Elsewhere *ew) { 2608 for (const Fortran::parser::WhereBodyConstruct &e : 2609 std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew->t)) 2610 analyzeExplicitSpace(e); 2611 } 2612 void analyzeExplicitSpace(const Fortran::parser::WhereStmt &stmt) { 2613 const Fortran::lower::SomeExpr *exp = Fortran::semantics::GetExpr( 2614 std::get<Fortran::parser::LogicalExpr>(stmt.t)); 2615 addMaskVariable(exp); 2616 analyzeExplicitSpace(*exp); 2617 const std::optional<Fortran::evaluate::Assignment> &assign = 2618 std::get<Fortran::parser::AssignmentStmt>(stmt.t).typedAssignment->v; 2619 assert(assign.has_value() && "WHERE has no statement"); 2620 analyzeExplicitSpace(assign.operator->()); 2621 } 2622 void analyzeExplicitSpace(const Fortran::parser::ForallStmt &forall) { 2623 analyzeExplicitSpace( 2624 std::get< 2625 Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>( 2626 forall.t) 2627 .value()); 2628 analyzeExplicitSpace(std::get<Fortran::parser::UnlabeledStatement< 2629 Fortran::parser::ForallAssignmentStmt>>(forall.t) 2630 .statement); 2631 analyzeExplicitSpacePop(); 2632 } 2633 void 2634 analyzeExplicitSpace(const Fortran::parser::ForallConstructStmt &forall) { 2635 analyzeExplicitSpace( 2636 std::get< 2637 Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>( 2638 forall.t) 2639 .value()); 2640 } 2641 void analyzeExplicitSpace(const Fortran::parser::ForallConstruct &forall) { 2642 analyzeExplicitSpace( 2643 std::get< 2644 Fortran::parser::Statement<Fortran::parser::ForallConstructStmt>>( 2645 forall.t) 2646 .statement); 2647 for (const Fortran::parser::ForallBodyConstruct &s : 2648 std::get<std::list<Fortran::parser::ForallBodyConstruct>>(forall.t)) { 2649 std::visit(Fortran::common::visitors{ 2650 [&](const Fortran::common::Indirection< 2651 Fortran::parser::ForallConstruct> &b) { 2652 analyzeExplicitSpace(b.value()); 2653 }, 2654 [&](const Fortran::parser::WhereConstruct &w) { 2655 analyzeExplicitSpace(w); 2656 }, 2657 [&](const auto &b) { analyzeExplicitSpace(b.statement); }}, 2658 s.u); 2659 } 2660 analyzeExplicitSpacePop(); 2661 } 2662 2663 void analyzeExplicitSpacePop() { explicitIterSpace.popLevel(); } 2664 2665 void addMaskVariable(Fortran::lower::FrontEndExpr exp) { 2666 // Note: use i8 to store bool values. This avoids round-down behavior found 2667 // with sequences of i1. That is, an array of i1 will be truncated in size 2668 // and be too small. For example, a buffer of type fir.array<7xi1> will have 2669 // 0 size. 2670 mlir::Type i64Ty = builder->getIntegerType(64); 2671 mlir::TupleType ty = fir::factory::getRaggedArrayHeaderType(*builder); 2672 mlir::Type buffTy = ty.getType(1); 2673 mlir::Type shTy = ty.getType(2); 2674 mlir::Location loc = toLocation(); 2675 mlir::Value hdr = builder->createTemporary(loc, ty); 2676 // FIXME: Is there a way to create a `zeroinitializer` in LLVM-IR dialect? 2677 // For now, explicitly set lazy ragged header to all zeros. 2678 // auto nilTup = builder->createNullConstant(loc, ty); 2679 // builder->create<fir::StoreOp>(loc, nilTup, hdr); 2680 mlir::Type i32Ty = builder->getIntegerType(32); 2681 mlir::Value zero = builder->createIntegerConstant(loc, i32Ty, 0); 2682 mlir::Value zero64 = builder->createIntegerConstant(loc, i64Ty, 0); 2683 mlir::Value flags = builder->create<fir::CoordinateOp>( 2684 loc, builder->getRefType(i64Ty), hdr, zero); 2685 builder->create<fir::StoreOp>(loc, zero64, flags); 2686 mlir::Value one = builder->createIntegerConstant(loc, i32Ty, 1); 2687 mlir::Value nullPtr1 = builder->createNullConstant(loc, buffTy); 2688 mlir::Value var = builder->create<fir::CoordinateOp>( 2689 loc, builder->getRefType(buffTy), hdr, one); 2690 builder->create<fir::StoreOp>(loc, nullPtr1, var); 2691 mlir::Value two = builder->createIntegerConstant(loc, i32Ty, 2); 2692 mlir::Value nullPtr2 = builder->createNullConstant(loc, shTy); 2693 mlir::Value shape = builder->create<fir::CoordinateOp>( 2694 loc, builder->getRefType(shTy), hdr, two); 2695 builder->create<fir::StoreOp>(loc, nullPtr2, shape); 2696 implicitIterSpace.addMaskVariable(exp, var, shape, hdr); 2697 explicitIterSpace.outermostContext().attachCleanup( 2698 [builder = this->builder, hdr, loc]() { 2699 fir::runtime::genRaggedArrayDeallocate(loc, *builder, hdr); 2700 }); 2701 } 2702 2703 void createRuntimeTypeInfoGlobals() {} 2704 2705 //===--------------------------------------------------------------------===// 2706 2707 Fortran::lower::LoweringBridge &bridge; 2708 Fortran::evaluate::FoldingContext foldingContext; 2709 fir::FirOpBuilder *builder = nullptr; 2710 Fortran::lower::pft::Evaluation *evalPtr = nullptr; 2711 Fortran::lower::SymMap localSymbols; 2712 Fortran::parser::CharBlock currentPosition; 2713 RuntimeTypeInfoConverter runtimeTypeInfoConverter; 2714 2715 /// WHERE statement/construct mask expression stack. 2716 Fortran::lower::ImplicitIterSpace implicitIterSpace; 2717 2718 /// FORALL context 2719 Fortran::lower::ExplicitIterSpace explicitIterSpace; 2720 2721 /// Tuple of host assoicated variables. 2722 mlir::Value hostAssocTuple; 2723 }; 2724 2725 } // namespace 2726 2727 Fortran::evaluate::FoldingContext 2728 Fortran::lower::LoweringBridge::createFoldingContext() const { 2729 return {getDefaultKinds(), getIntrinsicTable()}; 2730 } 2731 2732 void Fortran::lower::LoweringBridge::lower( 2733 const Fortran::parser::Program &prg, 2734 const Fortran::semantics::SemanticsContext &semanticsContext) { 2735 std::unique_ptr<Fortran::lower::pft::Program> pft = 2736 Fortran::lower::createPFT(prg, semanticsContext); 2737 if (dumpBeforeFir) 2738 Fortran::lower::dumpPFT(llvm::errs(), *pft); 2739 FirConverter converter{*this}; 2740 converter.run(*pft); 2741 } 2742 2743 void Fortran::lower::LoweringBridge::parseSourceFile(llvm::SourceMgr &srcMgr) { 2744 mlir::OwningOpRef<mlir::ModuleOp> owningRef = 2745 mlir::parseSourceFile<mlir::ModuleOp>(srcMgr, &context); 2746 module.reset(new mlir::ModuleOp(owningRef.get().getOperation())); 2747 owningRef.release(); 2748 } 2749 2750 Fortran::lower::LoweringBridge::LoweringBridge( 2751 mlir::MLIRContext &context, 2752 const Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds, 2753 const Fortran::evaluate::IntrinsicProcTable &intrinsics, 2754 const Fortran::parser::AllCookedSources &cooked, llvm::StringRef triple, 2755 fir::KindMapping &kindMap) 2756 : defaultKinds{defaultKinds}, intrinsics{intrinsics}, cooked{&cooked}, 2757 context{context}, kindMap{kindMap} { 2758 // Register the diagnostic handler. 2759 context.getDiagEngine().registerHandler([](mlir::Diagnostic &diag) { 2760 llvm::raw_ostream &os = llvm::errs(); 2761 switch (diag.getSeverity()) { 2762 case mlir::DiagnosticSeverity::Error: 2763 os << "error: "; 2764 break; 2765 case mlir::DiagnosticSeverity::Remark: 2766 os << "info: "; 2767 break; 2768 case mlir::DiagnosticSeverity::Warning: 2769 os << "warning: "; 2770 break; 2771 default: 2772 break; 2773 } 2774 if (!diag.getLocation().isa<mlir::UnknownLoc>()) 2775 os << diag.getLocation() << ": "; 2776 os << diag << '\n'; 2777 os.flush(); 2778 return mlir::success(); 2779 }); 2780 2781 // Create the module and attach the attributes. 2782 module = std::make_unique<mlir::ModuleOp>( 2783 mlir::ModuleOp::create(mlir::UnknownLoc::get(&context))); 2784 assert(module.get() && "module was not created"); 2785 fir::setTargetTriple(*module.get(), triple); 2786 fir::setKindMapping(*module.get(), kindMap); 2787 } 2788