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