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