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