1 //===-- PFTBuilder.cc -----------------------------------------------------===// 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 #include "flang/Lower/PFTBuilder.h" 10 #include "IntervalSet.h" 11 #include "flang/Lower/Support/Utils.h" 12 #include "flang/Parser/dump-parse-tree.h" 13 #include "flang/Parser/parse-tree-visitor.h" 14 #include "flang/Semantics/semantics.h" 15 #include "flang/Semantics/tools.h" 16 #include "llvm/ADT/DenseSet.h" 17 #include "llvm/ADT/IntervalMap.h" 18 #include "llvm/Support/CommandLine.h" 19 #include "llvm/Support/Debug.h" 20 21 #define DEBUG_TYPE "flang-pft" 22 23 static llvm::cl::opt<bool> clDisableStructuredFir( 24 "no-structured-fir", llvm::cl::desc("disable generation of structured FIR"), 25 llvm::cl::init(false), llvm::cl::Hidden); 26 27 static llvm::cl::opt<bool> nonRecursiveProcedures( 28 "non-recursive-procedures", 29 llvm::cl::desc("Make procedures non-recursive by default. This was the " 30 "default for all Fortran standards prior to 2018."), 31 llvm::cl::init(/*2018 standard=*/false)); 32 33 using namespace Fortran; 34 35 namespace { 36 /// Helpers to unveil parser node inside Fortran::parser::Statement<>, 37 /// Fortran::parser::UnlabeledStatement, and Fortran::common::Indirection<> 38 template <typename A> 39 struct RemoveIndirectionHelper { 40 using Type = A; 41 }; 42 template <typename A> 43 struct RemoveIndirectionHelper<common::Indirection<A>> { 44 using Type = A; 45 }; 46 47 template <typename A> 48 struct UnwrapStmt { 49 static constexpr bool isStmt{false}; 50 }; 51 template <typename A> 52 struct UnwrapStmt<parser::Statement<A>> { 53 static constexpr bool isStmt{true}; 54 using Type = typename RemoveIndirectionHelper<A>::Type; 55 constexpr UnwrapStmt(const parser::Statement<A> &a) 56 : unwrapped{removeIndirection(a.statement)}, position{a.source}, 57 label{a.label} {} 58 const Type &unwrapped; 59 parser::CharBlock position; 60 std::optional<parser::Label> label; 61 }; 62 template <typename A> 63 struct UnwrapStmt<parser::UnlabeledStatement<A>> { 64 static constexpr bool isStmt{true}; 65 using Type = typename RemoveIndirectionHelper<A>::Type; 66 constexpr UnwrapStmt(const parser::UnlabeledStatement<A> &a) 67 : unwrapped{removeIndirection(a.statement)}, position{a.source} {} 68 const Type &unwrapped; 69 parser::CharBlock position; 70 std::optional<parser::Label> label; 71 }; 72 73 /// The instantiation of a parse tree visitor (Pre and Post) is extremely 74 /// expensive in terms of compile and link time. So one goal here is to 75 /// limit the bridge to one such instantiation. 76 class PFTBuilder { 77 public: 78 PFTBuilder(const semantics::SemanticsContext &semanticsContext) 79 : pgm{std::make_unique<lower::pft::Program>()}, semanticsContext{ 80 semanticsContext} { 81 lower::pft::PftNode pftRoot{*pgm.get()}; 82 pftParentStack.push_back(pftRoot); 83 } 84 85 /// Get the result 86 std::unique_ptr<lower::pft::Program> result() { return std::move(pgm); } 87 88 template <typename A> 89 constexpr bool Pre(const A &a) { 90 if constexpr (lower::pft::isFunctionLike<A>) { 91 return enterFunction(a, semanticsContext); 92 } else if constexpr (lower::pft::isConstruct<A> || 93 lower::pft::isDirective<A>) { 94 return enterConstructOrDirective(a); 95 } else if constexpr (UnwrapStmt<A>::isStmt) { 96 using T = typename UnwrapStmt<A>::Type; 97 // Node "a" being visited has one of the following types: 98 // Statement<T>, Statement<Indirection<T>>, UnlabeledStatement<T>, 99 // or UnlabeledStatement<Indirection<T>> 100 auto stmt{UnwrapStmt<A>(a)}; 101 if constexpr (lower::pft::isConstructStmt<T> || 102 lower::pft::isOtherStmt<T>) { 103 addEvaluation(lower::pft::Evaluation{ 104 stmt.unwrapped, pftParentStack.back(), stmt.position, stmt.label}); 105 return false; 106 } else if constexpr (std::is_same_v<T, parser::ActionStmt>) { 107 return std::visit( 108 common::visitors{ 109 [&](const common::Indirection<parser::IfStmt> &x) { 110 convertIfStmt(x.value(), stmt.position, stmt.label); 111 return false; 112 }, 113 [&](const auto &x) { 114 addEvaluation(lower::pft::Evaluation{ 115 removeIndirection(x), pftParentStack.back(), 116 stmt.position, stmt.label}); 117 return true; 118 }, 119 }, 120 stmt.unwrapped.u); 121 } 122 } 123 return true; 124 } 125 126 /// Convert an IfStmt into an IfConstruct, retaining the IfStmt as the 127 /// first statement of the construct. 128 void convertIfStmt(const parser::IfStmt &ifStmt, parser::CharBlock position, 129 std::optional<parser::Label> label) { 130 // Generate a skeleton IfConstruct parse node. Its components are never 131 // referenced. The actual components are available via the IfConstruct 132 // evaluation's nested evaluationList, with the ifStmt in the position of 133 // the otherwise normal IfThenStmt. Caution: All other PFT nodes reference 134 // front end generated parse nodes; this is an exceptional case. 135 static const auto ifConstruct = parser::IfConstruct{ 136 parser::Statement<parser::IfThenStmt>{ 137 std::nullopt, 138 parser::IfThenStmt{ 139 std::optional<parser::Name>{}, 140 parser::ScalarLogicalExpr{parser::LogicalExpr{parser::Expr{ 141 parser::LiteralConstant{parser::LogicalLiteralConstant{ 142 false, std::optional<parser::KindParam>{}}}}}}}}, 143 parser::Block{}, std::list<parser::IfConstruct::ElseIfBlock>{}, 144 std::optional<parser::IfConstruct::ElseBlock>{}, 145 parser::Statement<parser::EndIfStmt>{std::nullopt, 146 parser::EndIfStmt{std::nullopt}}}; 147 enterConstructOrDirective(ifConstruct); 148 addEvaluation( 149 lower::pft::Evaluation{ifStmt, pftParentStack.back(), position, label}); 150 Pre(std::get<parser::UnlabeledStatement<parser::ActionStmt>>(ifStmt.t)); 151 static const auto endIfStmt = parser::EndIfStmt{std::nullopt}; 152 addEvaluation( 153 lower::pft::Evaluation{endIfStmt, pftParentStack.back(), {}, {}}); 154 exitConstructOrDirective(); 155 } 156 157 template <typename A> 158 constexpr void Post(const A &) { 159 if constexpr (lower::pft::isFunctionLike<A>) { 160 exitFunction(); 161 } else if constexpr (lower::pft::isConstruct<A> || 162 lower::pft::isDirective<A>) { 163 exitConstructOrDirective(); 164 } 165 } 166 167 // Module like 168 bool Pre(const parser::Module &node) { return enterModule(node); } 169 bool Pre(const parser::Submodule &node) { return enterModule(node); } 170 171 void Post(const parser::Module &) { exitModule(); } 172 void Post(const parser::Submodule &) { exitModule(); } 173 174 // Block data 175 bool Pre(const parser::BlockData &node) { 176 addUnit(lower::pft::BlockDataUnit{node, pftParentStack.back(), 177 semanticsContext}); 178 return false; 179 } 180 181 // Get rid of production wrapper 182 bool Pre(const parser::Statement<parser::ForallAssignmentStmt> &statement) { 183 addEvaluation(std::visit( 184 [&](const auto &x) { 185 return lower::pft::Evaluation{x, pftParentStack.back(), 186 statement.source, statement.label}; 187 }, 188 statement.statement.u)); 189 return false; 190 } 191 bool Pre(const parser::WhereBodyConstruct &whereBody) { 192 return std::visit( 193 common::visitors{ 194 [&](const parser::Statement<parser::AssignmentStmt> &stmt) { 195 // Not caught as other AssignmentStmt because it is not 196 // wrapped in a parser::ActionStmt. 197 addEvaluation(lower::pft::Evaluation{stmt.statement, 198 pftParentStack.back(), 199 stmt.source, stmt.label}); 200 return false; 201 }, 202 [&](const auto &) { return true; }, 203 }, 204 whereBody.u); 205 } 206 207 // CompilerDirective have special handling in case they are top level 208 // directives (i.e. they do not belong to a ProgramUnit). 209 bool Pre(const parser::CompilerDirective &directive) { 210 assert(pftParentStack.size() > 0 && 211 "At least the Program must be a parent"); 212 if (pftParentStack.back().isA<lower::pft::Program>()) { 213 addUnit( 214 lower::pft::CompilerDirectiveUnit(directive, pftParentStack.back())); 215 return false; 216 } 217 return enterConstructOrDirective(directive); 218 } 219 220 private: 221 /// Initialize a new module-like unit and make it the builder's focus. 222 template <typename A> 223 bool enterModule(const A &func) { 224 auto &unit = 225 addUnit(lower::pft::ModuleLikeUnit{func, pftParentStack.back()}); 226 functionList = &unit.nestedFunctions; 227 pftParentStack.emplace_back(unit); 228 return true; 229 } 230 231 void exitModule() { 232 pftParentStack.pop_back(); 233 resetFunctionState(); 234 } 235 236 /// Add the end statement Evaluation of a sub/program to the PFT. 237 /// There may be intervening internal subprogram definitions between 238 /// prior statements and this end statement. 239 void endFunctionBody() { 240 if (evaluationListStack.empty()) 241 return; 242 auto evaluationList = evaluationListStack.back(); 243 if (evaluationList->empty() || !evaluationList->back().isEndStmt()) { 244 const auto &endStmt = 245 pftParentStack.back().get<lower::pft::FunctionLikeUnit>().endStmt; 246 endStmt.visit(common::visitors{ 247 [&](const parser::Statement<parser::EndProgramStmt> &s) { 248 addEvaluation(lower::pft::Evaluation{ 249 s.statement, pftParentStack.back(), s.source, s.label}); 250 }, 251 [&](const parser::Statement<parser::EndFunctionStmt> &s) { 252 addEvaluation(lower::pft::Evaluation{ 253 s.statement, pftParentStack.back(), s.source, s.label}); 254 }, 255 [&](const parser::Statement<parser::EndSubroutineStmt> &s) { 256 addEvaluation(lower::pft::Evaluation{ 257 s.statement, pftParentStack.back(), s.source, s.label}); 258 }, 259 [&](const parser::Statement<parser::EndMpSubprogramStmt> &s) { 260 addEvaluation(lower::pft::Evaluation{ 261 s.statement, pftParentStack.back(), s.source, s.label}); 262 }, 263 [&](const auto &s) { 264 llvm::report_fatal_error("missing end statement or unexpected " 265 "begin statement reference"); 266 }, 267 }); 268 } 269 lastLexicalEvaluation = nullptr; 270 } 271 272 /// Initialize a new function-like unit and make it the builder's focus. 273 template <typename A> 274 bool enterFunction(const A &func, 275 const semantics::SemanticsContext &semanticsContext) { 276 endFunctionBody(); // enclosing host subprogram body, if any 277 auto &unit = addFunction(lower::pft::FunctionLikeUnit{ 278 func, pftParentStack.back(), semanticsContext}); 279 labelEvaluationMap = &unit.labelEvaluationMap; 280 assignSymbolLabelMap = &unit.assignSymbolLabelMap; 281 functionList = &unit.nestedFunctions; 282 pushEvaluationList(&unit.evaluationList); 283 pftParentStack.emplace_back(unit); 284 return true; 285 } 286 287 void exitFunction() { 288 rewriteIfGotos(); 289 endFunctionBody(); 290 analyzeBranches(nullptr, *evaluationListStack.back()); // add branch links 291 processEntryPoints(); 292 popEvaluationList(); 293 labelEvaluationMap = nullptr; 294 assignSymbolLabelMap = nullptr; 295 pftParentStack.pop_back(); 296 resetFunctionState(); 297 } 298 299 /// Initialize a new construct and make it the builder's focus. 300 template <typename A> 301 bool enterConstructOrDirective(const A &construct) { 302 auto &eval = 303 addEvaluation(lower::pft::Evaluation{construct, pftParentStack.back()}); 304 eval.evaluationList.reset(new lower::pft::EvaluationList); 305 pushEvaluationList(eval.evaluationList.get()); 306 pftParentStack.emplace_back(eval); 307 constructAndDirectiveStack.emplace_back(&eval); 308 return true; 309 } 310 311 void exitConstructOrDirective() { 312 rewriteIfGotos(); 313 popEvaluationList(); 314 pftParentStack.pop_back(); 315 constructAndDirectiveStack.pop_back(); 316 } 317 318 /// Reset function state to that of an enclosing host function. 319 void resetFunctionState() { 320 if (!pftParentStack.empty()) { 321 pftParentStack.back().visit(common::visitors{ 322 [&](lower::pft::FunctionLikeUnit &p) { 323 functionList = &p.nestedFunctions; 324 labelEvaluationMap = &p.labelEvaluationMap; 325 assignSymbolLabelMap = &p.assignSymbolLabelMap; 326 }, 327 [&](lower::pft::ModuleLikeUnit &p) { 328 functionList = &p.nestedFunctions; 329 }, 330 [&](auto &) { functionList = nullptr; }, 331 }); 332 } 333 } 334 335 template <typename A> 336 A &addUnit(A &&unit) { 337 pgm->getUnits().emplace_back(std::move(unit)); 338 return std::get<A>(pgm->getUnits().back()); 339 } 340 341 template <typename A> 342 A &addFunction(A &&func) { 343 if (functionList) { 344 functionList->emplace_back(std::move(func)); 345 return functionList->back(); 346 } 347 return addUnit(std::move(func)); 348 } 349 350 // ActionStmt has a couple of non-conforming cases, explicitly handled here. 351 // The other cases use an Indirection, which are discarded in the PFT. 352 lower::pft::Evaluation 353 makeEvaluationAction(const parser::ActionStmt &statement, 354 parser::CharBlock position, 355 std::optional<parser::Label> label) { 356 return std::visit( 357 common::visitors{ 358 [&](const auto &x) { 359 return lower::pft::Evaluation{ 360 removeIndirection(x), pftParentStack.back(), position, label}; 361 }, 362 }, 363 statement.u); 364 } 365 366 /// Append an Evaluation to the end of the current list. 367 lower::pft::Evaluation &addEvaluation(lower::pft::Evaluation &&eval) { 368 assert(functionList && "not in a function"); 369 assert(!evaluationListStack.empty() && "empty evaluation list stack"); 370 if (!constructAndDirectiveStack.empty()) 371 eval.parentConstruct = constructAndDirectiveStack.back(); 372 auto &entryPointList = eval.getOwningProcedure()->entryPointList; 373 evaluationListStack.back()->emplace_back(std::move(eval)); 374 lower::pft::Evaluation *p = &evaluationListStack.back()->back(); 375 if (p->isActionStmt() || p->isConstructStmt() || p->isEndStmt()) { 376 if (lastLexicalEvaluation) { 377 lastLexicalEvaluation->lexicalSuccessor = p; 378 p->printIndex = lastLexicalEvaluation->printIndex + 1; 379 } else { 380 p->printIndex = 1; 381 } 382 lastLexicalEvaluation = p; 383 for (auto entryIndex = entryPointList.size() - 1; 384 entryIndex && !entryPointList[entryIndex].second->lexicalSuccessor; 385 --entryIndex) 386 // Link to the entry's first executable statement. 387 entryPointList[entryIndex].second->lexicalSuccessor = p; 388 } else if (const auto *entryStmt = p->getIf<parser::EntryStmt>()) { 389 const auto *sym = std::get<parser::Name>(entryStmt->t).symbol; 390 assert(sym->has<semantics::SubprogramDetails>() && 391 "entry must be a subprogram"); 392 entryPointList.push_back(std::pair{sym, p}); 393 } 394 if (p->label.has_value()) 395 labelEvaluationMap->try_emplace(*p->label, p); 396 return evaluationListStack.back()->back(); 397 } 398 399 /// push a new list on the stack of Evaluation lists 400 void pushEvaluationList(lower::pft::EvaluationList *evaluationList) { 401 assert(functionList && "not in a function"); 402 assert(evaluationList && evaluationList->empty() && 403 "evaluation list isn't correct"); 404 evaluationListStack.emplace_back(evaluationList); 405 } 406 407 /// pop the current list and return to the last Evaluation list 408 void popEvaluationList() { 409 assert(functionList && "not in a function"); 410 evaluationListStack.pop_back(); 411 } 412 413 /// Rewrite IfConstructs containing a GotoStmt to eliminate an unstructured 414 /// branch and a trivial basic block. The pre-branch-analysis code: 415 /// 416 /// <<IfConstruct>> 417 /// 1 If[Then]Stmt: if(cond) goto L 418 /// 2 GotoStmt: goto L 419 /// 3 EndIfStmt 420 /// <<End IfConstruct>> 421 /// 4 Statement: ... 422 /// 5 Statement: ... 423 /// 6 Statement: L ... 424 /// 425 /// becomes: 426 /// 427 /// <<IfConstruct>> 428 /// 1 If[Then]Stmt [negate]: if(cond) goto L 429 /// 4 Statement: ... 430 /// 5 Statement: ... 431 /// 3 EndIfStmt 432 /// <<End IfConstruct>> 433 /// 6 Statement: L ... 434 /// 435 /// The If[Then]Stmt condition is implicitly negated. It is not modified 436 /// in the PFT. It must be negated when generating FIR. The GotoStmt is 437 /// deleted. 438 /// 439 /// The transformation is only valid for forward branch targets at the same 440 /// construct nesting level as the IfConstruct. The result must not violate 441 /// construct nesting requirements or contain an EntryStmt. The result 442 /// is subject to normal un/structured code classification analysis. The 443 /// result is allowed to violate the F18 Clause 11.1.2.1 prohibition on 444 /// transfer of control into the interior of a construct block, as that does 445 /// not compromise correct code generation. When two transformation 446 /// candidates overlap, at least one must be disallowed. In such cases, 447 /// the current heuristic favors simple code generation, which happens to 448 /// favor later candidates over earlier candidates. That choice is probably 449 /// not significant, but could be changed. 450 /// 451 void rewriteIfGotos() { 452 using T = struct { 453 lower::pft::EvaluationList::iterator ifConstructIt; 454 parser::Label ifTargetLabel; 455 }; 456 llvm::SmallVector<T, 8> ifExpansionStack; 457 auto &evaluationList = *evaluationListStack.back(); 458 for (auto it = evaluationList.begin(), end = evaluationList.end(); 459 it != end; ++it) { 460 auto &eval = *it; 461 if (eval.isA<parser::EntryStmt>()) { 462 ifExpansionStack.clear(); 463 continue; 464 } 465 auto firstStmt = [](lower::pft::Evaluation *e) { 466 return e->isConstruct() ? &*e->evaluationList->begin() : e; 467 }; 468 auto &targetEval = *firstStmt(&eval); 469 if (targetEval.label) { 470 while (!ifExpansionStack.empty() && 471 ifExpansionStack.back().ifTargetLabel == *targetEval.label) { 472 auto ifConstructIt = ifExpansionStack.back().ifConstructIt; 473 auto successorIt = std::next(ifConstructIt); 474 if (successorIt != it) { 475 auto &ifBodyList = *ifConstructIt->evaluationList; 476 auto gotoStmtIt = std::next(ifBodyList.begin()); 477 assert(gotoStmtIt->isA<parser::GotoStmt>() && "expected GotoStmt"); 478 ifBodyList.erase(gotoStmtIt); 479 auto &ifStmt = *ifBodyList.begin(); 480 ifStmt.negateCondition = true; 481 ifStmt.lexicalSuccessor = firstStmt(&*successorIt); 482 auto endIfStmtIt = std::prev(ifBodyList.end()); 483 std::prev(it)->lexicalSuccessor = &*endIfStmtIt; 484 endIfStmtIt->lexicalSuccessor = firstStmt(&*it); 485 ifBodyList.splice(endIfStmtIt, evaluationList, successorIt, it); 486 for (; successorIt != endIfStmtIt; ++successorIt) 487 successorIt->parentConstruct = &*ifConstructIt; 488 } 489 ifExpansionStack.pop_back(); 490 } 491 } 492 if (eval.isA<parser::IfConstruct>() && eval.evaluationList->size() == 3) { 493 if (auto *gotoStmt = std::next(eval.evaluationList->begin()) 494 ->getIf<parser::GotoStmt>()) 495 ifExpansionStack.push_back({it, gotoStmt->v}); 496 } 497 } 498 } 499 500 /// Mark I/O statement ERR, EOR, and END specifier branch targets. 501 /// Mark an I/O statement with an assigned format as unstructured. 502 template <typename A> 503 void analyzeIoBranches(lower::pft::Evaluation &eval, const A &stmt) { 504 auto analyzeFormatSpec = [&](const parser::Format &format) { 505 if (const auto *expr = std::get_if<parser::Expr>(&format.u)) { 506 if (semantics::ExprHasTypeCategory(*semantics::GetExpr(*expr), 507 common::TypeCategory::Integer)) 508 eval.isUnstructured = true; 509 } 510 }; 511 auto analyzeSpecs{[&](const auto &specList) { 512 for (const auto &spec : specList) { 513 std::visit( 514 Fortran::common::visitors{ 515 [&](const Fortran::parser::Format &format) { 516 analyzeFormatSpec(format); 517 }, 518 [&](const auto &label) { 519 using LabelNodes = 520 std::tuple<parser::ErrLabel, parser::EorLabel, 521 parser::EndLabel>; 522 if constexpr (common::HasMember<decltype(label), LabelNodes>) 523 markBranchTarget(eval, label.v); 524 }}, 525 spec.u); 526 } 527 }}; 528 529 using OtherIOStmts = 530 std::tuple<parser::BackspaceStmt, parser::CloseStmt, 531 parser::EndfileStmt, parser::FlushStmt, parser::OpenStmt, 532 parser::RewindStmt, parser::WaitStmt>; 533 534 if constexpr (std::is_same_v<A, parser::ReadStmt> || 535 std::is_same_v<A, parser::WriteStmt>) { 536 if (stmt.format) 537 analyzeFormatSpec(*stmt.format); 538 analyzeSpecs(stmt.controls); 539 } else if constexpr (std::is_same_v<A, parser::PrintStmt>) { 540 analyzeFormatSpec(std::get<parser::Format>(stmt.t)); 541 } else if constexpr (std::is_same_v<A, parser::InquireStmt>) { 542 if (const auto *specList = 543 std::get_if<std::list<parser::InquireSpec>>(&stmt.u)) 544 analyzeSpecs(*specList); 545 } else if constexpr (common::HasMember<A, OtherIOStmts>) { 546 analyzeSpecs(stmt.v); 547 } else { 548 // Always crash if this is instantiated 549 static_assert(!std::is_same_v<A, parser::ReadStmt>, 550 "Unexpected IO statement"); 551 } 552 } 553 554 /// Set the exit of a construct, possibly from multiple enclosing constructs. 555 void setConstructExit(lower::pft::Evaluation &eval) { 556 eval.constructExit = &eval.evaluationList->back().nonNopSuccessor(); 557 } 558 559 /// Mark the target of a branch as a new block. 560 void markBranchTarget(lower::pft::Evaluation &sourceEvaluation, 561 lower::pft::Evaluation &targetEvaluation) { 562 sourceEvaluation.isUnstructured = true; 563 if (!sourceEvaluation.controlSuccessor) 564 sourceEvaluation.controlSuccessor = &targetEvaluation; 565 targetEvaluation.isNewBlock = true; 566 // If this is a branch into the body of a construct (usually illegal, 567 // but allowed in some legacy cases), then the targetEvaluation and its 568 // ancestors must be marked as unstructured. 569 auto *sourceConstruct = sourceEvaluation.parentConstruct; 570 auto *targetConstruct = targetEvaluation.parentConstruct; 571 if (targetConstruct && 572 &targetConstruct->getFirstNestedEvaluation() == &targetEvaluation) 573 // A branch to an initial constructStmt is a branch to the construct. 574 targetConstruct = targetConstruct->parentConstruct; 575 if (targetConstruct) { 576 while (sourceConstruct && sourceConstruct != targetConstruct) 577 sourceConstruct = sourceConstruct->parentConstruct; 578 if (sourceConstruct != targetConstruct) 579 for (auto *eval = &targetEvaluation; eval; eval = eval->parentConstruct) 580 eval->isUnstructured = true; 581 } 582 } 583 void markBranchTarget(lower::pft::Evaluation &sourceEvaluation, 584 parser::Label label) { 585 assert(label && "missing branch target label"); 586 lower::pft::Evaluation *targetEvaluation{ 587 labelEvaluationMap->find(label)->second}; 588 assert(targetEvaluation && "missing branch target evaluation"); 589 markBranchTarget(sourceEvaluation, *targetEvaluation); 590 } 591 592 /// Mark the successor of an Evaluation as a new block. 593 void markSuccessorAsNewBlock(lower::pft::Evaluation &eval) { 594 eval.nonNopSuccessor().isNewBlock = true; 595 } 596 597 template <typename A> 598 inline std::string getConstructName(const A &stmt) { 599 using MaybeConstructNameWrapper = 600 std::tuple<parser::BlockStmt, parser::CycleStmt, parser::ElseStmt, 601 parser::ElsewhereStmt, parser::EndAssociateStmt, 602 parser::EndBlockStmt, parser::EndCriticalStmt, 603 parser::EndDoStmt, parser::EndForallStmt, parser::EndIfStmt, 604 parser::EndSelectStmt, parser::EndWhereStmt, 605 parser::ExitStmt>; 606 if constexpr (common::HasMember<A, MaybeConstructNameWrapper>) { 607 if (stmt.v) 608 return stmt.v->ToString(); 609 } 610 611 using MaybeConstructNameInTuple = std::tuple< 612 parser::AssociateStmt, parser::CaseStmt, parser::ChangeTeamStmt, 613 parser::CriticalStmt, parser::ElseIfStmt, parser::EndChangeTeamStmt, 614 parser::ForallConstructStmt, parser::IfThenStmt, parser::LabelDoStmt, 615 parser::MaskedElsewhereStmt, parser::NonLabelDoStmt, 616 parser::SelectCaseStmt, parser::SelectRankCaseStmt, 617 parser::TypeGuardStmt, parser::WhereConstructStmt>; 618 619 if constexpr (common::HasMember<A, MaybeConstructNameInTuple>) { 620 if (auto name = std::get<std::optional<parser::Name>>(stmt.t)) 621 return name->ToString(); 622 } 623 624 // These statements have several std::optional<parser::Name> 625 if constexpr (std::is_same_v<A, parser::SelectRankStmt> || 626 std::is_same_v<A, parser::SelectTypeStmt>) { 627 if (auto name = std::get<0>(stmt.t)) 628 return name->ToString(); 629 } 630 return {}; 631 } 632 633 /// \p parentConstruct can be null if this statement is at the highest 634 /// level of a program. 635 template <typename A> 636 void insertConstructName(const A &stmt, 637 lower::pft::Evaluation *parentConstruct) { 638 std::string name = getConstructName(stmt); 639 if (!name.empty()) 640 constructNameMap[name] = parentConstruct; 641 } 642 643 /// Insert branch links for a list of Evaluations. 644 /// \p parentConstruct can be null if the evaluationList contains the 645 /// top-level statements of a program. 646 void analyzeBranches(lower::pft::Evaluation *parentConstruct, 647 std::list<lower::pft::Evaluation> &evaluationList) { 648 lower::pft::Evaluation *lastConstructStmtEvaluation{}; 649 for (auto &eval : evaluationList) { 650 eval.visit(common::visitors{ 651 // Action statements (except I/O statements) 652 [&](const parser::CallStmt &s) { 653 // Look for alternate return specifiers. 654 const auto &args = 655 std::get<std::list<parser::ActualArgSpec>>(s.v.t); 656 for (const auto &arg : args) { 657 const auto &actual = std::get<parser::ActualArg>(arg.t); 658 if (const auto *altReturn = 659 std::get_if<parser::AltReturnSpec>(&actual.u)) 660 markBranchTarget(eval, altReturn->v); 661 } 662 }, 663 [&](const parser::CycleStmt &s) { 664 std::string name = getConstructName(s); 665 lower::pft::Evaluation *construct{name.empty() 666 ? doConstructStack.back() 667 : constructNameMap[name]}; 668 assert(construct && "missing CYCLE construct"); 669 markBranchTarget(eval, construct->evaluationList->back()); 670 }, 671 [&](const parser::ExitStmt &s) { 672 std::string name = getConstructName(s); 673 lower::pft::Evaluation *construct{name.empty() 674 ? doConstructStack.back() 675 : constructNameMap[name]}; 676 assert(construct && "missing EXIT construct"); 677 markBranchTarget(eval, *construct->constructExit); 678 }, 679 [&](const parser::GotoStmt &s) { markBranchTarget(eval, s.v); }, 680 [&](const parser::IfStmt &) { 681 eval.lexicalSuccessor->isNewBlock = true; 682 lastConstructStmtEvaluation = &eval; 683 }, 684 [&](const parser::ReturnStmt &) { 685 eval.isUnstructured = true; 686 if (eval.lexicalSuccessor->lexicalSuccessor) 687 markSuccessorAsNewBlock(eval); 688 }, 689 [&](const parser::StopStmt &) { 690 eval.isUnstructured = true; 691 if (eval.lexicalSuccessor->lexicalSuccessor) 692 markSuccessorAsNewBlock(eval); 693 }, 694 [&](const parser::ComputedGotoStmt &s) { 695 for (auto &label : std::get<std::list<parser::Label>>(s.t)) 696 markBranchTarget(eval, label); 697 }, 698 [&](const parser::ArithmeticIfStmt &s) { 699 markBranchTarget(eval, std::get<1>(s.t)); 700 markBranchTarget(eval, std::get<2>(s.t)); 701 markBranchTarget(eval, std::get<3>(s.t)); 702 }, 703 [&](const parser::AssignStmt &s) { // legacy label assignment 704 auto &label = std::get<parser::Label>(s.t); 705 const auto *sym = std::get<parser::Name>(s.t).symbol; 706 assert(sym && "missing AssignStmt symbol"); 707 lower::pft::Evaluation *target{ 708 labelEvaluationMap->find(label)->second}; 709 assert(target && "missing branch target evaluation"); 710 if (!target->isA<parser::FormatStmt>()) 711 target->isNewBlock = true; 712 auto iter = assignSymbolLabelMap->find(*sym); 713 if (iter == assignSymbolLabelMap->end()) { 714 lower::pft::LabelSet labelSet{}; 715 labelSet.insert(label); 716 assignSymbolLabelMap->try_emplace(*sym, labelSet); 717 } else { 718 iter->second.insert(label); 719 } 720 }, 721 [&](const parser::AssignedGotoStmt &) { 722 // Although this statement is a branch, it doesn't have any 723 // explicit control successors. So the code at the end of the 724 // loop won't mark the successor. Do that here. 725 eval.isUnstructured = true; 726 markSuccessorAsNewBlock(eval); 727 }, 728 729 // Construct statements 730 [&](const parser::AssociateStmt &s) { 731 insertConstructName(s, parentConstruct); 732 }, 733 [&](const parser::BlockStmt &s) { 734 insertConstructName(s, parentConstruct); 735 }, 736 [&](const parser::SelectCaseStmt &s) { 737 insertConstructName(s, parentConstruct); 738 lastConstructStmtEvaluation = &eval; 739 }, 740 [&](const parser::CaseStmt &) { 741 eval.isNewBlock = true; 742 lastConstructStmtEvaluation->controlSuccessor = &eval; 743 lastConstructStmtEvaluation = &eval; 744 }, 745 [&](const parser::EndSelectStmt &) { 746 eval.nonNopSuccessor().isNewBlock = true; 747 lastConstructStmtEvaluation = nullptr; 748 }, 749 [&](const parser::ChangeTeamStmt &s) { 750 insertConstructName(s, parentConstruct); 751 }, 752 [&](const parser::CriticalStmt &s) { 753 insertConstructName(s, parentConstruct); 754 }, 755 [&](const parser::NonLabelDoStmt &s) { 756 insertConstructName(s, parentConstruct); 757 doConstructStack.push_back(parentConstruct); 758 const auto &loopControl = 759 std::get<std::optional<parser::LoopControl>>(s.t); 760 if (!loopControl.has_value()) { 761 eval.isUnstructured = true; // infinite loop 762 return; 763 } 764 eval.nonNopSuccessor().isNewBlock = true; 765 eval.controlSuccessor = &evaluationList.back(); 766 if (const auto *bounds = 767 std::get_if<parser::LoopControl::Bounds>(&loopControl->u)) { 768 if (bounds->name.thing.symbol->GetType()->IsNumeric( 769 common::TypeCategory::Real)) 770 eval.isUnstructured = true; // real-valued loop control 771 } else if (std::get_if<parser::ScalarLogicalExpr>( 772 &loopControl->u)) { 773 eval.isUnstructured = true; // while loop 774 } 775 }, 776 [&](const parser::EndDoStmt &) { 777 lower::pft::Evaluation &doEval = evaluationList.front(); 778 eval.controlSuccessor = &doEval; 779 doConstructStack.pop_back(); 780 if (parentConstruct->lowerAsStructured()) 781 return; 782 // The loop is unstructured, which wasn't known for all cases when 783 // visiting the NonLabelDoStmt. 784 parentConstruct->constructExit->isNewBlock = true; 785 const auto &doStmt = *doEval.getIf<parser::NonLabelDoStmt>(); 786 const auto &loopControl = 787 std::get<std::optional<parser::LoopControl>>(doStmt.t); 788 if (!loopControl.has_value()) 789 return; // infinite loop 790 if (const auto *concurrent = 791 std::get_if<parser::LoopControl::Concurrent>( 792 &loopControl->u)) { 793 // If there is a mask, the EndDoStmt starts a new block. 794 const auto &header = 795 std::get<parser::ConcurrentHeader>(concurrent->t); 796 eval.isNewBlock |= 797 std::get<std::optional<parser::ScalarLogicalExpr>>(header.t) 798 .has_value(); 799 } 800 }, 801 [&](const parser::IfThenStmt &s) { 802 insertConstructName(s, parentConstruct); 803 eval.lexicalSuccessor->isNewBlock = true; 804 lastConstructStmtEvaluation = &eval; 805 }, 806 [&](const parser::ElseIfStmt &) { 807 eval.isNewBlock = true; 808 eval.lexicalSuccessor->isNewBlock = true; 809 lastConstructStmtEvaluation->controlSuccessor = &eval; 810 lastConstructStmtEvaluation = &eval; 811 }, 812 [&](const parser::ElseStmt &) { 813 eval.isNewBlock = true; 814 lastConstructStmtEvaluation->controlSuccessor = &eval; 815 lastConstructStmtEvaluation = nullptr; 816 }, 817 [&](const parser::EndIfStmt &) { 818 if (parentConstruct->lowerAsUnstructured()) 819 parentConstruct->constructExit->isNewBlock = true; 820 if (lastConstructStmtEvaluation) { 821 lastConstructStmtEvaluation->controlSuccessor = 822 parentConstruct->constructExit; 823 lastConstructStmtEvaluation = nullptr; 824 } 825 }, 826 [&](const parser::SelectRankStmt &s) { 827 insertConstructName(s, parentConstruct); 828 }, 829 [&](const parser::SelectRankCaseStmt &) { eval.isNewBlock = true; }, 830 [&](const parser::SelectTypeStmt &s) { 831 insertConstructName(s, parentConstruct); 832 }, 833 [&](const parser::TypeGuardStmt &) { eval.isNewBlock = true; }, 834 835 // Constructs - set (unstructured) construct exit targets 836 [&](const parser::AssociateConstruct &) { setConstructExit(eval); }, 837 [&](const parser::BlockConstruct &) { 838 // EndBlockStmt may have code. 839 eval.constructExit = &eval.evaluationList->back(); 840 }, 841 [&](const parser::CaseConstruct &) { 842 setConstructExit(eval); 843 eval.isUnstructured = true; 844 }, 845 [&](const parser::ChangeTeamConstruct &) { 846 // EndChangeTeamStmt may have code. 847 eval.constructExit = &eval.evaluationList->back(); 848 }, 849 [&](const parser::CriticalConstruct &) { 850 // EndCriticalStmt may have code. 851 eval.constructExit = &eval.evaluationList->back(); 852 }, 853 [&](const parser::DoConstruct &) { setConstructExit(eval); }, 854 [&](const parser::IfConstruct &) { setConstructExit(eval); }, 855 [&](const parser::SelectRankConstruct &) { 856 setConstructExit(eval); 857 eval.isUnstructured = true; 858 }, 859 [&](const parser::SelectTypeConstruct &) { 860 setConstructExit(eval); 861 eval.isUnstructured = true; 862 }, 863 864 // Default - Common analysis for I/O statements; otherwise nop. 865 [&](const auto &stmt) { 866 using A = std::decay_t<decltype(stmt)>; 867 using IoStmts = std::tuple< 868 parser::BackspaceStmt, parser::CloseStmt, parser::EndfileStmt, 869 parser::FlushStmt, parser::InquireStmt, parser::OpenStmt, 870 parser::PrintStmt, parser::ReadStmt, parser::RewindStmt, 871 parser::WaitStmt, parser::WriteStmt>; 872 if constexpr (common::HasMember<A, IoStmts>) 873 analyzeIoBranches(eval, stmt); 874 }, 875 }); 876 877 // Analyze construct evaluations. 878 if (eval.evaluationList) 879 analyzeBranches(&eval, *eval.evaluationList); 880 881 // Set the successor of the last statement in an IF or SELECT block. 882 if (!eval.controlSuccessor && eval.lexicalSuccessor && 883 eval.lexicalSuccessor->isIntermediateConstructStmt()) { 884 eval.controlSuccessor = parentConstruct->constructExit; 885 eval.lexicalSuccessor->isNewBlock = true; 886 } 887 888 // Propagate isUnstructured flag to enclosing construct. 889 if (parentConstruct && eval.isUnstructured) 890 parentConstruct->isUnstructured = true; 891 892 // The successor of a branch starts a new block. 893 if (eval.controlSuccessor && eval.isActionStmt() && 894 eval.lowerAsUnstructured()) 895 markSuccessorAsNewBlock(eval); 896 } 897 } 898 899 /// For multiple entry subprograms, build a list of the dummy arguments that 900 /// appear in some, but not all entry points. For those that are functions, 901 /// also find one of the largest function results, since a single result 902 /// container holds the result for all entries. 903 void processEntryPoints() { 904 auto *unit = evaluationListStack.back()->front().getOwningProcedure(); 905 int entryCount = unit->entryPointList.size(); 906 if (entryCount == 1) 907 return; 908 llvm::DenseMap<semantics::Symbol *, int> dummyCountMap; 909 for (int entryIndex = 0; entryIndex < entryCount; ++entryIndex) { 910 unit->setActiveEntry(entryIndex); 911 const auto &details = 912 unit->getSubprogramSymbol().get<semantics::SubprogramDetails>(); 913 for (auto *arg : details.dummyArgs()) { 914 if (!arg) 915 continue; // alternate return specifier (no actual argument) 916 const auto iter = dummyCountMap.find(arg); 917 if (iter == dummyCountMap.end()) 918 dummyCountMap.try_emplace(arg, 1); 919 else 920 ++iter->second; 921 } 922 if (details.isFunction()) { 923 const auto *resultSym = &details.result(); 924 assert(resultSym && "missing result symbol"); 925 if (!unit->primaryResult || 926 unit->primaryResult->size() < resultSym->size()) 927 unit->primaryResult = resultSym; 928 } 929 } 930 unit->setActiveEntry(0); 931 for (auto arg : dummyCountMap) 932 if (arg.second < entryCount) 933 unit->nonUniversalDummyArguments.push_back(arg.first); 934 } 935 936 std::unique_ptr<lower::pft::Program> pgm; 937 std::vector<lower::pft::PftNode> pftParentStack; 938 const semantics::SemanticsContext &semanticsContext; 939 940 /// functionList points to the internal or module procedure function list 941 /// of a FunctionLikeUnit or a ModuleLikeUnit. It may be null. 942 std::list<lower::pft::FunctionLikeUnit> *functionList{}; 943 std::vector<lower::pft::Evaluation *> constructAndDirectiveStack{}; 944 std::vector<lower::pft::Evaluation *> doConstructStack{}; 945 /// evaluationListStack is the current nested construct evaluationList state. 946 std::vector<lower::pft::EvaluationList *> evaluationListStack{}; 947 llvm::DenseMap<parser::Label, lower::pft::Evaluation *> *labelEvaluationMap{}; 948 lower::pft::SymbolLabelMap *assignSymbolLabelMap{}; 949 std::map<std::string, lower::pft::Evaluation *> constructNameMap{}; 950 lower::pft::Evaluation *lastLexicalEvaluation{}; 951 }; 952 953 class PFTDumper { 954 public: 955 void dumpPFT(llvm::raw_ostream &outputStream, 956 const lower::pft::Program &pft) { 957 for (auto &unit : pft.getUnits()) { 958 std::visit(common::visitors{ 959 [&](const lower::pft::BlockDataUnit &unit) { 960 outputStream << getNodeIndex(unit) << " "; 961 outputStream << "BlockData: "; 962 outputStream << "\nEnd BlockData\n\n"; 963 }, 964 [&](const lower::pft::FunctionLikeUnit &func) { 965 dumpFunctionLikeUnit(outputStream, func); 966 }, 967 [&](const lower::pft::ModuleLikeUnit &unit) { 968 dumpModuleLikeUnit(outputStream, unit); 969 }, 970 [&](const lower::pft::CompilerDirectiveUnit &unit) { 971 dumpCompilerDirectiveUnit(outputStream, unit); 972 }, 973 }, 974 unit); 975 } 976 } 977 978 llvm::StringRef evaluationName(const lower::pft::Evaluation &eval) { 979 return eval.visit([](const auto &parseTreeNode) { 980 return parser::ParseTreeDumper::GetNodeName(parseTreeNode); 981 }); 982 } 983 984 void dumpEvaluation(llvm::raw_ostream &outputStream, 985 const lower::pft::Evaluation &eval, 986 const std::string &indentString, int indent = 1) { 987 llvm::StringRef name = evaluationName(eval); 988 std::string bang = eval.isUnstructured ? "!" : ""; 989 if (eval.isConstruct() || eval.isDirective()) { 990 outputStream << indentString << "<<" << name << bang << ">>"; 991 if (eval.constructExit) 992 outputStream << " -> " << eval.constructExit->printIndex; 993 outputStream << '\n'; 994 dumpEvaluationList(outputStream, *eval.evaluationList, indent + 1); 995 outputStream << indentString << "<<End " << name << bang << ">>\n"; 996 return; 997 } 998 outputStream << indentString; 999 if (eval.printIndex) 1000 outputStream << eval.printIndex << ' '; 1001 if (eval.isNewBlock) 1002 outputStream << '^'; 1003 outputStream << name << bang; 1004 if (eval.isActionStmt() || eval.isConstructStmt()) { 1005 if (eval.negateCondition) 1006 outputStream << " [negate]"; 1007 if (eval.controlSuccessor) 1008 outputStream << " -> " << eval.controlSuccessor->printIndex; 1009 } else if (eval.isA<parser::EntryStmt>() && eval.lexicalSuccessor) { 1010 outputStream << " -> " << eval.lexicalSuccessor->printIndex; 1011 } 1012 if (!eval.position.empty()) 1013 outputStream << ": " << eval.position.ToString(); 1014 outputStream << '\n'; 1015 } 1016 1017 void dumpEvaluation(llvm::raw_ostream &ostream, 1018 const lower::pft::Evaluation &eval) { 1019 dumpEvaluation(ostream, eval, ""); 1020 } 1021 1022 void dumpEvaluationList(llvm::raw_ostream &outputStream, 1023 const lower::pft::EvaluationList &evaluationList, 1024 int indent = 1) { 1025 static const auto white = " ++"s; 1026 auto indentString = white.substr(0, indent * 2); 1027 for (const auto &eval : evaluationList) 1028 dumpEvaluation(outputStream, eval, indentString, indent); 1029 } 1030 1031 void 1032 dumpFunctionLikeUnit(llvm::raw_ostream &outputStream, 1033 const lower::pft::FunctionLikeUnit &functionLikeUnit) { 1034 outputStream << getNodeIndex(functionLikeUnit) << " "; 1035 llvm::StringRef unitKind; 1036 llvm::StringRef name; 1037 llvm::StringRef header; 1038 if (functionLikeUnit.beginStmt) { 1039 functionLikeUnit.beginStmt->visit(common::visitors{ 1040 [&](const parser::Statement<parser::ProgramStmt> &stmt) { 1041 unitKind = "Program"; 1042 name = toStringRef(stmt.statement.v.source); 1043 }, 1044 [&](const parser::Statement<parser::FunctionStmt> &stmt) { 1045 unitKind = "Function"; 1046 name = toStringRef(std::get<parser::Name>(stmt.statement.t).source); 1047 header = toStringRef(stmt.source); 1048 }, 1049 [&](const parser::Statement<parser::SubroutineStmt> &stmt) { 1050 unitKind = "Subroutine"; 1051 name = toStringRef(std::get<parser::Name>(stmt.statement.t).source); 1052 header = toStringRef(stmt.source); 1053 }, 1054 [&](const parser::Statement<parser::MpSubprogramStmt> &stmt) { 1055 unitKind = "MpSubprogram"; 1056 name = toStringRef(stmt.statement.v.source); 1057 header = toStringRef(stmt.source); 1058 }, 1059 [&](const auto &) { llvm_unreachable("not a valid begin stmt"); }, 1060 }); 1061 } else { 1062 unitKind = "Program"; 1063 name = "<anonymous>"; 1064 } 1065 outputStream << unitKind << ' ' << name; 1066 if (!header.empty()) 1067 outputStream << ": " << header; 1068 outputStream << '\n'; 1069 dumpEvaluationList(outputStream, functionLikeUnit.evaluationList); 1070 if (!functionLikeUnit.nestedFunctions.empty()) { 1071 outputStream << "\nContains\n"; 1072 for (auto &func : functionLikeUnit.nestedFunctions) 1073 dumpFunctionLikeUnit(outputStream, func); 1074 outputStream << "End Contains\n"; 1075 } 1076 outputStream << "End " << unitKind << ' ' << name << "\n\n"; 1077 } 1078 1079 void dumpModuleLikeUnit(llvm::raw_ostream &outputStream, 1080 const lower::pft::ModuleLikeUnit &moduleLikeUnit) { 1081 outputStream << getNodeIndex(moduleLikeUnit) << " "; 1082 outputStream << "ModuleLike: "; 1083 outputStream << "\nContains\n"; 1084 for (auto &func : moduleLikeUnit.nestedFunctions) 1085 dumpFunctionLikeUnit(outputStream, func); 1086 outputStream << "End Contains\nEnd ModuleLike\n\n"; 1087 } 1088 1089 // Top level directives 1090 void dumpCompilerDirectiveUnit( 1091 llvm::raw_ostream &outputStream, 1092 const lower::pft::CompilerDirectiveUnit &directive) { 1093 outputStream << getNodeIndex(directive) << " "; 1094 outputStream << "CompilerDirective: !"; 1095 outputStream << directive.get<Fortran::parser::CompilerDirective>() 1096 .source.ToString(); 1097 outputStream << "\nEnd CompilerDirective\n\n"; 1098 } 1099 1100 template <typename T> 1101 std::size_t getNodeIndex(const T &node) { 1102 auto addr = static_cast<const void *>(&node); 1103 auto it = nodeIndexes.find(addr); 1104 if (it != nodeIndexes.end()) 1105 return it->second; 1106 nodeIndexes.try_emplace(addr, nextIndex); 1107 return nextIndex++; 1108 } 1109 std::size_t getNodeIndex(const lower::pft::Program &) { return 0; } 1110 1111 private: 1112 llvm::DenseMap<const void *, std::size_t> nodeIndexes; 1113 std::size_t nextIndex{1}; // 0 is the root 1114 }; 1115 1116 } // namespace 1117 1118 template <typename A, typename T> 1119 static lower::pft::FunctionLikeUnit::FunctionStatement 1120 getFunctionStmt(const T &func) { 1121 lower::pft::FunctionLikeUnit::FunctionStatement result{ 1122 std::get<parser::Statement<A>>(func.t)}; 1123 return result; 1124 } 1125 template <typename A, typename T> 1126 static lower::pft::ModuleLikeUnit::ModuleStatement getModuleStmt(const T &mod) { 1127 lower::pft::ModuleLikeUnit::ModuleStatement result{ 1128 std::get<parser::Statement<A>>(mod.t)}; 1129 return result; 1130 } 1131 1132 template <typename A> 1133 static const semantics::Symbol *getSymbol(A &beginStmt) { 1134 const auto *symbol = beginStmt.visit(common::visitors{ 1135 [](const parser::Statement<parser::ProgramStmt> &stmt) 1136 -> const semantics::Symbol * { return stmt.statement.v.symbol; }, 1137 [](const parser::Statement<parser::FunctionStmt> &stmt) 1138 -> const semantics::Symbol * { 1139 return std::get<parser::Name>(stmt.statement.t).symbol; 1140 }, 1141 [](const parser::Statement<parser::SubroutineStmt> &stmt) 1142 -> const semantics::Symbol * { 1143 return std::get<parser::Name>(stmt.statement.t).symbol; 1144 }, 1145 [](const parser::Statement<parser::MpSubprogramStmt> &stmt) 1146 -> const semantics::Symbol * { return stmt.statement.v.symbol; }, 1147 [](const parser::Statement<parser::ModuleStmt> &stmt) 1148 -> const semantics::Symbol * { return stmt.statement.v.symbol; }, 1149 [](const parser::Statement<parser::SubmoduleStmt> &stmt) 1150 -> const semantics::Symbol * { 1151 return std::get<parser::Name>(stmt.statement.t).symbol; 1152 }, 1153 [](const auto &) -> const semantics::Symbol * { 1154 llvm_unreachable("unknown FunctionLike or ModuleLike beginStmt"); 1155 return nullptr; 1156 }}); 1157 assert(symbol && "parser::Name must have resolved symbol"); 1158 return symbol; 1159 } 1160 1161 bool Fortran::lower::pft::Evaluation::lowerAsStructured() const { 1162 return !lowerAsUnstructured(); 1163 } 1164 1165 bool Fortran::lower::pft::Evaluation::lowerAsUnstructured() const { 1166 return isUnstructured || clDisableStructuredFir; 1167 } 1168 1169 lower::pft::FunctionLikeUnit * 1170 Fortran::lower::pft::Evaluation::getOwningProcedure() const { 1171 return parent.visit(common::visitors{ 1172 [](lower::pft::FunctionLikeUnit &c) { return &c; }, 1173 [&](lower::pft::Evaluation &c) { return c.getOwningProcedure(); }, 1174 [](auto &) -> lower::pft::FunctionLikeUnit * { return nullptr; }, 1175 }); 1176 } 1177 1178 bool Fortran::lower::definedInCommonBlock(const semantics::Symbol &sym) { 1179 return semantics::FindCommonBlockContaining(sym); 1180 } 1181 1182 /// Is the symbol `sym` a global? 1183 static bool symbolIsGlobal(const semantics::Symbol &sym) { 1184 if (const auto *details = sym.detailsIf<semantics::ObjectEntityDetails>()) 1185 if (details->init()) 1186 return true; 1187 return semantics::IsSaved(sym) || lower::definedInCommonBlock(sym); 1188 } 1189 1190 namespace { 1191 /// This helper class is for sorting the symbols in the symbol table. We want 1192 /// the symbols in an order such that a symbol will be visited after those it 1193 /// depends upon. Otherwise this sort is stable and preserves the order of the 1194 /// symbol table, which is sorted by name. 1195 struct SymbolDependenceDepth { 1196 explicit SymbolDependenceDepth( 1197 std::vector<std::vector<lower::pft::Variable>> &vars, bool reentrant) 1198 : vars{vars}, reentrant{reentrant} {} 1199 1200 void analyzeAliasesInCurrentScope(const semantics::Scope &scope) { 1201 for (const auto &iter : scope) { 1202 const auto &ultimate = iter.second.get().GetUltimate(); 1203 if (skipSymbol(ultimate)) 1204 continue; 1205 bool isDeclaration = scope != ultimate.owner(); 1206 analyzeAliases(ultimate.owner(), isDeclaration); 1207 } 1208 // add all aggregate stores to the front of the work list 1209 adjustSize(1); 1210 // The copy in the loop matters, 'stores' will still be used. 1211 for (auto st : stores) { 1212 vars[0].emplace_back(std::move(st)); 1213 } 1214 } 1215 // Analyze the equivalence sets. This analysis need not be performed when the 1216 // scope has no equivalence sets. 1217 void analyzeAliases(const semantics::Scope &scope, bool isDeclaration) { 1218 if (scope.equivalenceSets().empty()) 1219 return; 1220 if (scopeAnlyzedForAliases.find(&scope) != scopeAnlyzedForAliases.end()) 1221 return; 1222 scopeAnlyzedForAliases.insert(&scope); 1223 Fortran::lower::IntervalSet intervals; 1224 llvm::DenseMap<std::size_t, llvm::SmallVector<const semantics::Symbol *, 8>> 1225 aliasSets; 1226 llvm::DenseMap<std::size_t, const semantics::Symbol *> setIsGlobal; 1227 1228 // 1. Construct the intervals. Determine each entity's interval, merging 1229 // overlapping intervals into aggregates. 1230 for (const auto &pair : scope) { 1231 const auto &sym = pair.second.get(); 1232 if (skipSymbol(sym)) 1233 continue; 1234 LLVM_DEBUG(llvm::dbgs() << "symbol: " << sym << '\n'); 1235 intervals.merge(sym.offset(), sym.offset() + sym.size() - 1); 1236 } 1237 1238 // 2. Compute alias sets. Adds each entity to a set for the interval it 1239 // appears to be mapped into. 1240 for (const auto &pair : scope) { 1241 const auto &sym = pair.second.get(); 1242 if (skipSymbol(sym)) 1243 continue; 1244 auto iter = intervals.find(sym.offset()); 1245 if (iter != intervals.end()) { 1246 LLVM_DEBUG(llvm::dbgs() 1247 << "symbol: " << toStringRef(sym.name()) << " on [" 1248 << iter->first << ".." << iter->second << "]\n"); 1249 aliasSets[iter->first].push_back(&sym); 1250 if (symbolIsGlobal(sym)) 1251 setIsGlobal.insert({iter->first, &sym}); 1252 } 1253 } 1254 1255 // 3. For each alias set with more than 1 member, add an Interval to the 1256 // stores. The Interval will be lowered into a single memory allocation, 1257 // with the co-located, overlapping variables mapped into that memory range. 1258 for (const auto &pair : aliasSets) { 1259 if (pair.second.size() > 1) { 1260 // Set contains more than 1 aliasing variable. 1261 // 1. Mark the symbols as aliasing for lowering. 1262 for (auto *sym : pair.second) 1263 aliasSyms.insert(sym); 1264 auto gvarIter = setIsGlobal.find(pair.first); 1265 auto iter = intervals.find(pair.first); 1266 auto ibgn = iter->first; 1267 auto ilen = iter->second - ibgn + 1; 1268 // 2. Add an Interval to the list of stores allocated for this unit. 1269 lower::pft::Variable::Interval interval(ibgn, ilen); 1270 if (gvarIter != setIsGlobal.end()) { 1271 LLVM_DEBUG(llvm::dbgs() 1272 << "interval [" << ibgn << ".." << ibgn + ilen 1273 << ") added as global " << *gvarIter->second << '\n'); 1274 stores.emplace_back(std::move(interval), scope, pair.second, 1275 isDeclaration); 1276 } else { 1277 LLVM_DEBUG(llvm::dbgs() << "interval [" << ibgn << ".." << ibgn + ilen 1278 << ") added\n"); 1279 stores.emplace_back(std::move(interval), scope, isDeclaration); 1280 } 1281 } 1282 } 1283 } 1284 1285 // Recursively visit each symbol to determine the height of its dependence on 1286 // other symbols. 1287 int analyze(const semantics::Symbol &sym) { 1288 auto done = seen.insert(&sym); 1289 LLVM_DEBUG(llvm::dbgs() << "analyze symbol: " << sym << '\n'); 1290 if (!done.second) 1291 return 0; 1292 if (semantics::IsProcedure(sym)) { 1293 // TODO: add declaration? 1294 return 0; 1295 } 1296 auto ultimate = sym.GetUltimate(); 1297 if (!ultimate.has<semantics::ObjectEntityDetails>() && 1298 !ultimate.has<semantics::ProcEntityDetails>()) 1299 return 0; 1300 1301 if (sym.has<semantics::DerivedTypeDetails>()) 1302 llvm_unreachable("not yet implemented - derived type analysis"); 1303 1304 // Symbol must be something lowering will have to allocate. 1305 bool global = semantics::IsSaved(sym); 1306 int depth = 0; 1307 const auto *symTy = sym.GetType(); 1308 assert(symTy && "symbol must have a type"); 1309 1310 // check CHARACTER's length 1311 if (symTy->category() == semantics::DeclTypeSpec::Character) 1312 if (auto e = symTy->characterTypeSpec().length().GetExplicit()) { 1313 // turn variable into a global if this unit is not reentrant 1314 global = global || !reentrant; 1315 for (const auto &s : evaluate::CollectSymbols(*e)) 1316 depth = std::max(analyze(s) + 1, depth); 1317 } 1318 1319 if (const auto *details = sym.detailsIf<semantics::ObjectEntityDetails>()) { 1320 auto doExplicit = [&](const auto &bound) { 1321 if (bound.isExplicit()) { 1322 semantics::SomeExpr e{*bound.GetExplicit()}; 1323 for (const auto &s : evaluate::CollectSymbols(e)) 1324 depth = std::max(analyze(s) + 1, depth); 1325 } 1326 }; 1327 // handle any symbols in array bound declarations 1328 if (!details->shape().empty()) 1329 global = global || !reentrant; 1330 for (const auto &subs : details->shape()) { 1331 doExplicit(subs.lbound()); 1332 doExplicit(subs.ubound()); 1333 } 1334 // handle any symbols in coarray bound declarations 1335 if (!details->coshape().empty()) 1336 global = global || !reentrant; 1337 for (const auto &subs : details->coshape()) { 1338 doExplicit(subs.lbound()); 1339 doExplicit(subs.ubound()); 1340 } 1341 // handle any symbols in initialization expressions 1342 if (auto e = details->init()) { 1343 // A PARAMETER may not be marked as implicitly SAVE, so set the flag. 1344 global = true; 1345 for (const auto &s : evaluate::CollectSymbols(*e)) 1346 depth = std::max(analyze(s) + 1, depth); 1347 } 1348 } 1349 adjustSize(depth + 1); 1350 vars[depth].emplace_back(sym, global, depth); 1351 if (semantics::IsAllocatable(sym)) 1352 vars[depth].back().setHeapAlloc(); 1353 if (semantics::IsPointer(sym)) 1354 vars[depth].back().setPointer(); 1355 if (ultimate.attrs().test(semantics::Attr::TARGET)) 1356 vars[depth].back().setTarget(); 1357 1358 // If there are alias sets, then link the participating variables to their 1359 // aggregate stores when constructing the new variable on the list. 1360 if (auto *store = findStoreIfAlias(sym)) { 1361 vars[depth].back().setAlias(store->getOffset()); 1362 } 1363 return depth; 1364 } 1365 1366 /// Save the final list of variable allocations as a single vector and free 1367 /// the rest. 1368 void finalize() { 1369 for (int i = 1, end = vars.size(); i < end; ++i) 1370 vars[0].insert(vars[0].end(), vars[i].begin(), vars[i].end()); 1371 vars.resize(1); 1372 } 1373 1374 Fortran::lower::pft::Variable::AggregateStore * 1375 findStoreIfAlias(const Fortran::evaluate::Symbol &sym) { 1376 const auto &ultimate = sym.GetUltimate(); 1377 const auto &scope = ultimate.owner(); 1378 // Expect the total number of EQUIVALENCE sets to be small for a typical 1379 // Fortran program. 1380 if (aliasSyms.find(&ultimate) != aliasSyms.end()) { 1381 LLVM_DEBUG(llvm::dbgs() << "symbol: " << ultimate << '\n'); 1382 LLVM_DEBUG(llvm::dbgs() << "scope: " << scope << '\n'); 1383 auto off = ultimate.offset(); 1384 for (auto &v : stores) { 1385 if (v.scope == &scope) { 1386 auto bot = std::get<0>(v.interval); 1387 if (off >= bot && off < bot + std::get<1>(v.interval)) 1388 return &v; 1389 } 1390 } 1391 // clang-format off 1392 LLVM_DEBUG( 1393 llvm::dbgs() << "looking for " << off << "\n{\n"; 1394 for (auto v : stores) { 1395 llvm::dbgs() << " in scope: " << v.scope << "\n"; 1396 llvm::dbgs() << " i = [" << std::get<0>(v.interval) << ".." 1397 << std::get<0>(v.interval) + std::get<1>(v.interval) 1398 << "]\n"; 1399 } 1400 llvm::dbgs() << "}\n"); 1401 // clang-format on 1402 llvm_unreachable("the store must be present"); 1403 } 1404 return nullptr; 1405 } 1406 1407 private: 1408 /// Skip symbol in alias analysis. 1409 bool skipSymbol(const semantics::Symbol &sym) { 1410 return !sym.has<semantics::ObjectEntityDetails>() || 1411 lower::definedInCommonBlock(sym); 1412 } 1413 1414 // Make sure the table is of appropriate size. 1415 void adjustSize(std::size_t size) { 1416 if (vars.size() < size) 1417 vars.resize(size); 1418 } 1419 1420 llvm::SmallSet<const semantics::Symbol *, 32> seen; 1421 std::vector<std::vector<lower::pft::Variable>> &vars; 1422 llvm::SmallSet<const semantics::Symbol *, 32> aliasSyms; 1423 llvm::SmallSet<const semantics::Scope *, 4> scopeAnlyzedForAliases; 1424 std::vector<Fortran::lower::pft::Variable::AggregateStore> stores; 1425 bool reentrant; 1426 }; 1427 } // namespace 1428 1429 static void processSymbolTable( 1430 const semantics::Scope &scope, 1431 std::vector<std::vector<Fortran::lower::pft::Variable>> &varList, 1432 bool reentrant) { 1433 SymbolDependenceDepth sdd{varList, reentrant}; 1434 sdd.analyzeAliasesInCurrentScope(scope); 1435 for (const auto &iter : scope) 1436 sdd.analyze(iter.second.get()); 1437 sdd.finalize(); 1438 } 1439 1440 Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit( 1441 const parser::MainProgram &func, const lower::pft::PftNode &parent, 1442 const semantics::SemanticsContext &semanticsContext) 1443 : ProgramUnit{func, parent}, endStmt{ 1444 getFunctionStmt<parser::EndProgramStmt>( 1445 func)} { 1446 const auto &programStmt = 1447 std::get<std::optional<parser::Statement<parser::ProgramStmt>>>(func.t); 1448 if (programStmt.has_value()) { 1449 beginStmt = FunctionStatement(programStmt.value()); 1450 const auto *symbol = getSymbol(*beginStmt); 1451 entryPointList[0].first = symbol; 1452 processSymbolTable(*symbol->scope(), varList, isRecursive()); 1453 } else { 1454 processSymbolTable( 1455 semanticsContext.FindScope( 1456 std::get<parser::Statement<parser::EndProgramStmt>>(func.t).source), 1457 varList, isRecursive()); 1458 } 1459 } 1460 1461 Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit( 1462 const parser::FunctionSubprogram &func, const lower::pft::PftNode &parent, 1463 const semantics::SemanticsContext &) 1464 : ProgramUnit{func, parent}, 1465 beginStmt{getFunctionStmt<parser::FunctionStmt>(func)}, 1466 endStmt{getFunctionStmt<parser::EndFunctionStmt>(func)} { 1467 const auto *symbol = getSymbol(*beginStmt); 1468 entryPointList[0].first = symbol; 1469 processSymbolTable(*symbol->scope(), varList, isRecursive()); 1470 } 1471 1472 Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit( 1473 const parser::SubroutineSubprogram &func, const lower::pft::PftNode &parent, 1474 const semantics::SemanticsContext &) 1475 : ProgramUnit{func, parent}, 1476 beginStmt{getFunctionStmt<parser::SubroutineStmt>(func)}, 1477 endStmt{getFunctionStmt<parser::EndSubroutineStmt>(func)} { 1478 const auto *symbol = getSymbol(*beginStmt); 1479 entryPointList[0].first = symbol; 1480 processSymbolTable(*symbol->scope(), varList, isRecursive()); 1481 } 1482 1483 Fortran::lower::pft::FunctionLikeUnit::FunctionLikeUnit( 1484 const parser::SeparateModuleSubprogram &func, 1485 const lower::pft::PftNode &parent, const semantics::SemanticsContext &) 1486 : ProgramUnit{func, parent}, 1487 beginStmt{getFunctionStmt<parser::MpSubprogramStmt>(func)}, 1488 endStmt{getFunctionStmt<parser::EndMpSubprogramStmt>(func)} { 1489 const auto *symbol = getSymbol(*beginStmt); 1490 entryPointList[0].first = symbol; 1491 processSymbolTable(*symbol->scope(), varList, isRecursive()); 1492 } 1493 1494 Fortran::lower::pft::ModuleLikeUnit::ModuleLikeUnit( 1495 const parser::Module &m, const lower::pft::PftNode &parent) 1496 : ProgramUnit{m, parent}, beginStmt{getModuleStmt<parser::ModuleStmt>(m)}, 1497 endStmt{getModuleStmt<parser::EndModuleStmt>(m)} { 1498 const auto *symbol = getSymbol(beginStmt); 1499 processSymbolTable(*symbol->scope(), varList, /*reentrant=*/false); 1500 } 1501 1502 Fortran::lower::pft::ModuleLikeUnit::ModuleLikeUnit( 1503 const parser::Submodule &m, const lower::pft::PftNode &parent) 1504 : ProgramUnit{m, parent}, beginStmt{getModuleStmt<parser::SubmoduleStmt>( 1505 m)}, 1506 endStmt{getModuleStmt<parser::EndSubmoduleStmt>(m)} { 1507 const auto *symbol = getSymbol(beginStmt); 1508 processSymbolTable(*symbol->scope(), varList, /*reentrant=*/false); 1509 } 1510 1511 Fortran::lower::pft::BlockDataUnit::BlockDataUnit( 1512 const parser::BlockData &bd, const lower::pft::PftNode &parent, 1513 const semantics::SemanticsContext &semanticsContext) 1514 : ProgramUnit{bd, parent}, 1515 symTab{semanticsContext.FindScope( 1516 std::get<parser::Statement<parser::EndBlockDataStmt>>(bd.t).source)} { 1517 } 1518 1519 std::unique_ptr<lower::pft::Program> 1520 Fortran::lower::createPFT(const parser::Program &root, 1521 const semantics::SemanticsContext &semanticsContext) { 1522 PFTBuilder walker(semanticsContext); 1523 Walk(root, walker); 1524 return walker.result(); 1525 } 1526 1527 // FIXME: FlangDriver 1528 // This option should be integrated with the real driver as the default of 1529 // RECURSIVE vs. NON_RECURSIVE may be changed by other command line options, 1530 // etc., etc. 1531 bool Fortran::lower::defaultRecursiveFunctionSetting() { 1532 return !nonRecursiveProcedures; 1533 } 1534 1535 void Fortran::lower::dumpPFT(llvm::raw_ostream &outputStream, 1536 const lower::pft::Program &pft) { 1537 PFTDumper{}.dumpPFT(outputStream, pft); 1538 } 1539 1540 void Fortran::lower::pft::Program::dump() const { 1541 dumpPFT(llvm::errs(), *this); 1542 } 1543 1544 void Fortran::lower::pft::Evaluation::dump() const { 1545 PFTDumper{}.dumpEvaluation(llvm::errs(), *this); 1546 } 1547 1548 void Fortran::lower::pft::Variable::dump() const { 1549 if (auto *s = std::get_if<Nominal>(&var)) { 1550 llvm::errs() << "symbol: " << s->symbol->name(); 1551 llvm::errs() << " (depth: " << s->depth << ')'; 1552 if (s->global) 1553 llvm::errs() << ", global"; 1554 if (s->heapAlloc) 1555 llvm::errs() << ", allocatable"; 1556 if (s->pointer) 1557 llvm::errs() << ", pointer"; 1558 if (s->target) 1559 llvm::errs() << ", target"; 1560 if (s->aliaser) 1561 llvm::errs() << ", equivalence(" << s->aliasOffset << ')'; 1562 } else if (auto *s = std::get_if<AggregateStore>(&var)) { 1563 llvm::errs() << "interval[" << std::get<0>(s->interval) << ", " 1564 << std::get<1>(s->interval) << "]:"; 1565 if (s->isGlobal()) 1566 llvm::errs() << ", global"; 1567 if (s->vars.size()) { 1568 llvm::errs() << ", vars: {"; 1569 llvm::interleaveComma(s->vars, llvm::errs(), 1570 [](auto *y) { llvm::errs() << *y; }); 1571 llvm::errs() << '}'; 1572 } 1573 } else { 1574 llvm_unreachable("not a Variable"); 1575 } 1576 llvm::errs() << '\n'; 1577 } 1578 1579 void Fortran::lower::pft::FunctionLikeUnit::dump() const { 1580 PFTDumper{}.dumpFunctionLikeUnit(llvm::errs(), *this); 1581 } 1582 1583 void Fortran::lower::pft::ModuleLikeUnit::dump() const { 1584 PFTDumper{}.dumpModuleLikeUnit(llvm::errs(), *this); 1585 } 1586 1587 /// The BlockDataUnit dump is just the associated symbol table. 1588 void Fortran::lower::pft::BlockDataUnit::dump() const { 1589 llvm::errs() << "block data {\n" << symTab << "\n}\n"; 1590 } 1591