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