1 //===----------------------------------------------------------------------===// 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 "resolve-directives.h" 10 11 #include "check-acc-structure.h" 12 #include "check-omp-structure.h" 13 #include "resolve-names-utils.h" 14 #include "flang/Common/idioms.h" 15 #include "flang/Evaluate/fold.h" 16 #include "flang/Evaluate/type.h" 17 #include "flang/Parser/parse-tree-visitor.h" 18 #include "flang/Parser/parse-tree.h" 19 #include "flang/Parser/tools.h" 20 #include "flang/Semantics/expression.h" 21 #include <list> 22 #include <map> 23 24 namespace Fortran::semantics { 25 26 template <typename T> class DirectiveAttributeVisitor { 27 public: 28 explicit DirectiveAttributeVisitor(SemanticsContext &context) 29 : context_{context} {} 30 31 template <typename A> bool Pre(const A &) { return true; } 32 template <typename A> void Post(const A &) {} 33 34 protected: 35 struct DirContext { 36 DirContext(const parser::CharBlock &source, T d, Scope &s) 37 : directiveSource{source}, directive{d}, scope{s} {} 38 parser::CharBlock directiveSource; 39 T directive; 40 Scope &scope; 41 Symbol::Flag defaultDSA{Symbol::Flag::AccShared}; // TODOACC 42 std::map<const Symbol *, Symbol::Flag> objectWithDSA; 43 bool withinConstruct{false}; 44 std::int64_t associatedLoopLevel{0}; 45 }; 46 47 DirContext &GetContext() { 48 CHECK(!dirContext_.empty()); 49 return dirContext_.back(); 50 } 51 std::optional<DirContext> GetContextIf() { 52 return dirContext_.empty() 53 ? std::nullopt 54 : std::make_optional<DirContext>(dirContext_.back()); 55 } 56 void PushContext(const parser::CharBlock &source, T dir) { 57 dirContext_.emplace_back(source, dir, context_.FindScope(source)); 58 } 59 void PopContext() { dirContext_.pop_back(); } 60 void SetContextDirectiveSource(parser::CharBlock &dir) { 61 GetContext().directiveSource = dir; 62 } 63 Scope &currScope() { return GetContext().scope; } 64 void SetContextDefaultDSA(Symbol::Flag flag) { 65 GetContext().defaultDSA = flag; 66 } 67 void AddToContextObjectWithDSA( 68 const Symbol &symbol, Symbol::Flag flag, DirContext &context) { 69 context.objectWithDSA.emplace(&symbol, flag); 70 } 71 void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) { 72 AddToContextObjectWithDSA(symbol, flag, GetContext()); 73 } 74 bool IsObjectWithDSA(const Symbol &symbol) { 75 auto it{GetContext().objectWithDSA.find(&symbol)}; 76 return it != GetContext().objectWithDSA.end(); 77 } 78 void SetContextAssociatedLoopLevel(std::int64_t level) { 79 GetContext().associatedLoopLevel = level; 80 } 81 Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) { 82 const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})}; 83 return *pair.first->second; 84 } 85 Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) { 86 return MakeAssocSymbol(name, prev, currScope()); 87 } 88 static const parser::Name *GetDesignatorNameIfDataRef( 89 const parser::Designator &designator) { 90 const auto *dataRef{std::get_if<parser::DataRef>(&designator.u)}; 91 return dataRef ? std::get_if<parser::Name>(&dataRef->u) : nullptr; 92 } 93 void AddDataSharingAttributeObject(SymbolRef object) { 94 dataSharingAttributeObjects_.insert(object); 95 } 96 void ClearDataSharingAttributeObjects() { 97 dataSharingAttributeObjects_.clear(); 98 } 99 bool HasDataSharingAttributeObject(const Symbol &); 100 const parser::Name &GetLoopIndex(const parser::DoConstruct &); 101 const parser::DoConstruct *GetDoConstructIf( 102 const parser::ExecutionPartConstruct &); 103 Symbol *DeclarePrivateAccessEntity( 104 const parser::Name &, Symbol::Flag, Scope &); 105 Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag, Scope &); 106 Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); 107 108 UnorderedSymbolSet dataSharingAttributeObjects_; // on one directive 109 SemanticsContext &context_; 110 std::vector<DirContext> dirContext_; // used as a stack 111 }; 112 113 class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> { 114 public: 115 explicit AccAttributeVisitor(SemanticsContext &context) 116 : DirectiveAttributeVisitor(context) {} 117 118 template <typename A> void Walk(const A &x) { parser::Walk(x, *this); } 119 template <typename A> bool Pre(const A &) { return true; } 120 template <typename A> void Post(const A &) {} 121 122 bool Pre(const parser::OpenACCBlockConstruct &); 123 void Post(const parser::OpenACCBlockConstruct &) { PopContext(); } 124 bool Pre(const parser::OpenACCCombinedConstruct &); 125 void Post(const parser::OpenACCCombinedConstruct &) { PopContext(); } 126 127 bool Pre(const parser::OpenACCDeclarativeConstruct &); 128 void Post(const parser::OpenACCDeclarativeConstruct &) { PopContext(); } 129 130 bool Pre(const parser::OpenACCRoutineConstruct &); 131 bool Pre(const parser::AccBindClause &); 132 void Post(const parser::OpenACCStandaloneDeclarativeConstruct &); 133 134 void Post(const parser::AccBeginBlockDirective &) { 135 GetContext().withinConstruct = true; 136 } 137 138 bool Pre(const parser::OpenACCLoopConstruct &); 139 void Post(const parser::OpenACCLoopConstruct &) { PopContext(); } 140 void Post(const parser::AccLoopDirective &) { 141 GetContext().withinConstruct = true; 142 } 143 144 bool Pre(const parser::OpenACCStandaloneConstruct &); 145 void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); } 146 void Post(const parser::AccStandaloneDirective &) { 147 GetContext().withinConstruct = true; 148 } 149 150 bool Pre(const parser::OpenACCCacheConstruct &); 151 void Post(const parser::OpenACCCacheConstruct &) { PopContext(); } 152 153 void Post(const parser::AccDefaultClause &); 154 155 bool Pre(const parser::AccClause::Copy &x) { 156 ResolveAccObjectList(x.v, Symbol::Flag::AccCopyIn); 157 ResolveAccObjectList(x.v, Symbol::Flag::AccCopyOut); 158 return false; 159 } 160 161 bool Pre(const parser::AccClause::Create &x) { 162 const auto &objectList{std::get<parser::AccObjectList>(x.v.t)}; 163 ResolveAccObjectList(objectList, Symbol::Flag::AccCreate); 164 return false; 165 } 166 167 bool Pre(const parser::AccClause::Copyin &x) { 168 const auto &objectList{std::get<parser::AccObjectList>(x.v.t)}; 169 ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn); 170 return false; 171 } 172 173 bool Pre(const parser::AccClause::Copyout &x) { 174 const auto &objectList{std::get<parser::AccObjectList>(x.v.t)}; 175 ResolveAccObjectList(objectList, Symbol::Flag::AccCopyOut); 176 return false; 177 } 178 179 bool Pre(const parser::AccClause::Present &x) { 180 ResolveAccObjectList(x.v, Symbol::Flag::AccPresent); 181 return false; 182 } 183 bool Pre(const parser::AccClause::Private &x) { 184 ResolveAccObjectList(x.v, Symbol::Flag::AccPrivate); 185 return false; 186 } 187 bool Pre(const parser::AccClause::Firstprivate &x) { 188 ResolveAccObjectList(x.v, Symbol::Flag::AccFirstPrivate); 189 return false; 190 } 191 192 void Post(const parser::Name &); 193 194 private: 195 std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &); 196 197 static constexpr Symbol::Flags dataSharingAttributeFlags{ 198 Symbol::Flag::AccShared, Symbol::Flag::AccPrivate, 199 Symbol::Flag::AccPresent, Symbol::Flag::AccFirstPrivate, 200 Symbol::Flag::AccReduction}; 201 202 static constexpr Symbol::Flags dataMappingAttributeFlags{ 203 Symbol::Flag::AccCreate, Symbol::Flag::AccCopyIn, 204 Symbol::Flag::AccCopyOut, Symbol::Flag::AccDelete}; 205 206 static constexpr Symbol::Flags accFlagsRequireNewSymbol{ 207 Symbol::Flag::AccPrivate, Symbol::Flag::AccFirstPrivate, 208 Symbol::Flag::AccReduction}; 209 210 static constexpr Symbol::Flags accFlagsRequireMark{}; 211 212 void PrivatizeAssociatedLoopIndex(const parser::OpenACCLoopConstruct &); 213 void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag); 214 void ResolveAccObject(const parser::AccObject &, Symbol::Flag); 215 Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &); 216 Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &); 217 Symbol *ResolveName(const parser::Name &); 218 Symbol *ResolveAccCommonBlockName(const parser::Name *); 219 Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); 220 Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); 221 void CheckMultipleAppearances( 222 const parser::Name &, const Symbol &, Symbol::Flag); 223 void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList); 224 void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList); 225 }; 226 227 // Data-sharing and Data-mapping attributes for data-refs in OpenMP construct 228 class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> { 229 public: 230 explicit OmpAttributeVisitor(SemanticsContext &context) 231 : DirectiveAttributeVisitor(context) {} 232 233 template <typename A> void Walk(const A &x) { parser::Walk(x, *this); } 234 template <typename A> bool Pre(const A &) { return true; } 235 template <typename A> void Post(const A &) {} 236 237 template <typename A> bool Pre(const parser::Statement<A> &statement) { 238 currentStatementSource_ = statement.source; 239 // Keep track of the labels in all the labelled statements 240 if (statement.label) { 241 auto label{statement.label.value()}; 242 // Get the context to check if the labelled statement is in an 243 // enclosing OpenMP construct 244 std::optional<DirContext> thisContext{GetContextIf()}; 245 targetLabels_.emplace( 246 label, std::make_pair(currentStatementSource_, thisContext)); 247 // Check if a statement that causes a jump to the 'label' 248 // has already been encountered 249 auto range{sourceLabels_.equal_range(label)}; 250 for (auto it{range.first}; it != range.second; ++it) { 251 // Check if both the statement with 'label' and the statement that 252 // causes a jump to the 'label' are in the same scope 253 CheckLabelContext(it->second.first, currentStatementSource_, 254 it->second.second, thisContext); 255 } 256 } 257 return true; 258 } 259 260 bool Pre(const parser::InternalSubprogram &) { 261 // Clear the labels being tracked in the previous scope 262 ClearLabels(); 263 return true; 264 } 265 266 bool Pre(const parser::ModuleSubprogram &) { 267 // Clear the labels being tracked in the previous scope 268 ClearLabels(); 269 return true; 270 } 271 272 bool Pre(const parser::SpecificationPart &x) { 273 Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t)); 274 return true; 275 } 276 277 bool Pre(const parser::StmtFunctionStmt &x) { 278 const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)}; 279 if (const auto *expr{GetExpr(parsedExpr)}) { 280 for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) { 281 if (!IsStmtFunctionDummy(symbol)) { 282 stmtFunctionExprSymbols_.insert(symbol.GetUltimate()); 283 } 284 } 285 } 286 return true; 287 } 288 289 bool Pre(const parser::OpenMPBlockConstruct &); 290 void Post(const parser::OpenMPBlockConstruct &); 291 292 void Post(const parser::OmpBeginBlockDirective &) { 293 GetContext().withinConstruct = true; 294 } 295 296 bool Pre(const parser::OpenMPLoopConstruct &); 297 void Post(const parser::OpenMPLoopConstruct &) { PopContext(); } 298 void Post(const parser::OmpBeginLoopDirective &) { 299 GetContext().withinConstruct = true; 300 } 301 bool Pre(const parser::DoConstruct &); 302 303 bool Pre(const parser::OpenMPSectionsConstruct &); 304 void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); } 305 306 bool Pre(const parser::OpenMPCriticalConstruct &); 307 void Post(const parser::OpenMPCriticalConstruct &) { PopContext(); } 308 309 bool Pre(const parser::OpenMPDeclareSimdConstruct &x) { 310 PushContext(x.source, llvm::omp::Directive::OMPD_declare_simd); 311 const auto &name{std::get<std::optional<parser::Name>>(x.t)}; 312 if (name) { 313 ResolveOmpName(*name, Symbol::Flag::OmpDeclareSimd); 314 } 315 return true; 316 } 317 void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); } 318 bool Pre(const parser::OpenMPThreadprivate &); 319 void Post(const parser::OpenMPThreadprivate &) { PopContext(); } 320 321 // 2.15.3 Data-Sharing Attribute Clauses 322 void Post(const parser::OmpDefaultClause &); 323 bool Pre(const parser::OmpClause::Shared &x) { 324 ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared); 325 return false; 326 } 327 bool Pre(const parser::OmpClause::Private &x) { 328 ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate); 329 return false; 330 } 331 bool Pre(const parser::OmpAllocateClause &x) { 332 const auto &objectList{std::get<parser::OmpObjectList>(x.t)}; 333 ResolveOmpObjectList(objectList, Symbol::Flag::OmpAllocate); 334 return false; 335 } 336 bool Pre(const parser::OmpClause::Firstprivate &x) { 337 ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate); 338 return false; 339 } 340 bool Pre(const parser::OmpClause::Lastprivate &x) { 341 ResolveOmpObjectList(x.v, Symbol::Flag::OmpLastPrivate); 342 return false; 343 } 344 bool Pre(const parser::OmpClause::Copyin &x) { 345 ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyIn); 346 return false; 347 } 348 bool Pre(const parser::OmpClause::Copyprivate &x) { 349 ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyPrivate); 350 return false; 351 } 352 bool Pre(const parser::OmpLinearClause &x) { 353 std::visit(common::visitors{ 354 [&](const parser::OmpLinearClause::WithoutModifier 355 &linearWithoutModifier) { 356 ResolveOmpNameList( 357 linearWithoutModifier.names, Symbol::Flag::OmpLinear); 358 }, 359 [&](const parser::OmpLinearClause::WithModifier 360 &linearWithModifier) { 361 ResolveOmpNameList( 362 linearWithModifier.names, Symbol::Flag::OmpLinear); 363 }, 364 }, 365 x.u); 366 return false; 367 } 368 369 bool Pre(const parser::OmpClause::Reduction &x) { 370 const parser::OmpReductionOperator &opr{ 371 std::get<parser::OmpReductionOperator>(x.v.t)}; 372 if (const auto *procD{parser::Unwrap<parser::ProcedureDesignator>(opr.u)}) { 373 if (const auto *name{parser::Unwrap<parser::Name>(procD->u)}) { 374 if (!name->symbol) { 375 const auto namePair{currScope().try_emplace( 376 name->source, Attrs{}, ProcEntityDetails{})}; 377 auto &symbol{*namePair.first->second}; 378 name->symbol = &symbol; 379 name->symbol->set(Symbol::Flag::OmpReduction); 380 AddToContextObjectWithDSA(*name->symbol, Symbol::Flag::OmpReduction); 381 } 382 } 383 if (const auto *procRef{ 384 parser::Unwrap<parser::ProcComponentRef>(procD->u)}) { 385 ResolveOmp(*procRef->v.thing.component.symbol, 386 Symbol::Flag::OmpReduction, currScope()); 387 } 388 } 389 const auto &objList{std::get<parser::OmpObjectList>(x.v.t)}; 390 ResolveOmpObjectList(objList, Symbol::Flag::OmpReduction); 391 return false; 392 } 393 394 bool Pre(const parser::OmpAlignedClause &x) { 395 const auto &alignedNameList{std::get<std::list<parser::Name>>(x.t)}; 396 ResolveOmpNameList(alignedNameList, Symbol::Flag::OmpAligned); 397 return false; 398 } 399 void Post(const parser::Name &); 400 401 // Keep track of labels in the statements that causes jumps to target labels 402 void Post(const parser::GotoStmt &gotoStmt) { CheckSourceLabel(gotoStmt.v); } 403 void Post(const parser::ComputedGotoStmt &computedGotoStmt) { 404 for (auto &label : std::get<std::list<parser::Label>>(computedGotoStmt.t)) { 405 CheckSourceLabel(label); 406 } 407 } 408 void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) { 409 CheckSourceLabel(std::get<1>(arithmeticIfStmt.t)); 410 CheckSourceLabel(std::get<2>(arithmeticIfStmt.t)); 411 CheckSourceLabel(std::get<3>(arithmeticIfStmt.t)); 412 } 413 void Post(const parser::AssignedGotoStmt &assignedGotoStmt) { 414 for (auto &label : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) { 415 CheckSourceLabel(label); 416 } 417 } 418 void Post(const parser::AltReturnSpec &altReturnSpec) { 419 CheckSourceLabel(altReturnSpec.v); 420 } 421 void Post(const parser::ErrLabel &errLabel) { CheckSourceLabel(errLabel.v); } 422 void Post(const parser::EndLabel &endLabel) { CheckSourceLabel(endLabel.v); } 423 void Post(const parser::EorLabel &eorLabel) { CheckSourceLabel(eorLabel.v); } 424 425 const parser::OmpClause *associatedClause{nullptr}; 426 void SetAssociatedClause(const parser::OmpClause &c) { 427 associatedClause = &c; 428 } 429 const parser::OmpClause *GetAssociatedClause() { return associatedClause; } 430 431 private: 432 std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &); 433 434 static constexpr Symbol::Flags dataSharingAttributeFlags{ 435 Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate, 436 Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate, 437 Symbol::Flag::OmpReduction, Symbol::Flag::OmpLinear}; 438 439 static constexpr Symbol::Flags privateDataSharingAttributeFlags{ 440 Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate, 441 Symbol::Flag::OmpLastPrivate}; 442 443 static constexpr Symbol::Flags ompFlagsRequireNewSymbol{ 444 Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear, 445 Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate, 446 Symbol::Flag::OmpReduction}; 447 448 static constexpr Symbol::Flags ompFlagsRequireMark{ 449 Symbol::Flag::OmpThreadprivate}; 450 451 static constexpr Symbol::Flags dataCopyingAttributeFlags{ 452 Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate}; 453 454 std::vector<const parser::Name *> allocateNames_; // on one directive 455 UnorderedSymbolSet privateDataSharingAttributeObjects_; // on one directive 456 UnorderedSymbolSet stmtFunctionExprSymbols_; 457 std::multimap<const parser::Label, 458 std::pair<parser::CharBlock, std::optional<DirContext>>> 459 sourceLabels_; 460 std::map<const parser::Label, 461 std::pair<parser::CharBlock, std::optional<DirContext>>> 462 targetLabels_; 463 parser::CharBlock currentStatementSource_; 464 465 void AddAllocateName(const parser::Name *&object) { 466 allocateNames_.push_back(object); 467 } 468 void ClearAllocateNames() { allocateNames_.clear(); } 469 470 void AddPrivateDataSharingAttributeObjects(SymbolRef object) { 471 privateDataSharingAttributeObjects_.insert(object); 472 } 473 void ClearPrivateDataSharingAttributeObjects() { 474 privateDataSharingAttributeObjects_.clear(); 475 } 476 477 // Predetermined DSA rules 478 void PrivatizeAssociatedLoopIndexAndCheckLoopLevel( 479 const parser::OpenMPLoopConstruct &); 480 void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &); 481 482 void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag); 483 void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag); 484 Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &); 485 Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &); 486 Symbol *ResolveOmpCommonBlockName(const parser::Name *); 487 void ResolveOmpNameList(const std::list<parser::Name> &, Symbol::Flag); 488 void ResolveOmpName(const parser::Name &, Symbol::Flag); 489 Symbol *ResolveName(const parser::Name *); 490 Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); 491 Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); 492 void CheckMultipleAppearances( 493 const parser::Name &, const Symbol &, Symbol::Flag); 494 495 void CheckDataCopyingClause( 496 const parser::Name &, const Symbol &, Symbol::Flag); 497 void CheckAssocLoopLevel(std::int64_t level, const parser::OmpClause *clause); 498 void CheckPrivateDSAObject( 499 const parser::Name &, const Symbol &, Symbol::Flag); 500 void CheckSourceLabel(const parser::Label &); 501 void CheckLabelContext(const parser::CharBlock, const parser::CharBlock, 502 std::optional<DirContext>, std::optional<DirContext>); 503 void ClearLabels() { 504 sourceLabels_.clear(); 505 targetLabels_.clear(); 506 }; 507 508 bool HasSymbolInEnclosingScope(const Symbol &, Scope &); 509 std::int64_t ordCollapseLevel{0}; 510 }; 511 512 template <typename T> 513 bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject( 514 const Symbol &object) { 515 auto it{dataSharingAttributeObjects_.find(object)}; 516 return it != dataSharingAttributeObjects_.end(); 517 } 518 519 template <typename T> 520 const parser::Name &DirectiveAttributeVisitor<T>::GetLoopIndex( 521 const parser::DoConstruct &x) { 522 using Bounds = parser::LoopControl::Bounds; 523 return std::get<Bounds>(x.GetLoopControl()->u).name.thing; 524 } 525 526 template <typename T> 527 const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf( 528 const parser::ExecutionPartConstruct &x) { 529 return parser::Unwrap<parser::DoConstruct>(x); 530 } 531 532 template <typename T> 533 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity( 534 const parser::Name &name, Symbol::Flag flag, Scope &scope) { 535 if (!name.symbol) { 536 return nullptr; // not resolved by Name Resolution step, do nothing 537 } 538 name.symbol = DeclarePrivateAccessEntity(*name.symbol, flag, scope); 539 return name.symbol; 540 } 541 542 template <typename T> 543 Symbol *DirectiveAttributeVisitor<T>::DeclarePrivateAccessEntity( 544 Symbol &object, Symbol::Flag flag, Scope &scope) { 545 if (object.owner() != currScope()) { 546 auto &symbol{MakeAssocSymbol(object.name(), object, scope)}; 547 symbol.set(flag); 548 return &symbol; 549 } else { 550 object.set(flag); 551 return &object; 552 } 553 } 554 555 bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) { 556 const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)}; 557 const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)}; 558 switch (blockDir.v) { 559 case llvm::acc::Directive::ACCD_data: 560 case llvm::acc::Directive::ACCD_host_data: 561 case llvm::acc::Directive::ACCD_kernels: 562 case llvm::acc::Directive::ACCD_parallel: 563 case llvm::acc::Directive::ACCD_serial: 564 PushContext(blockDir.source, blockDir.v); 565 break; 566 default: 567 break; 568 } 569 ClearDataSharingAttributeObjects(); 570 return true; 571 } 572 573 bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct &x) { 574 if (const auto *declConstruct{ 575 std::get_if<parser::OpenACCStandaloneDeclarativeConstruct>(&x.u)}) { 576 const auto &declDir{ 577 std::get<parser::AccDeclarativeDirective>(declConstruct->t)}; 578 PushContext(declDir.source, llvm::acc::Directive::ACCD_declare); 579 } else if (const auto *routineConstruct{ 580 std::get_if<parser::OpenACCRoutineConstruct>(&x.u)}) { 581 const auto &verbatim{std::get<parser::Verbatim>(routineConstruct->t)}; 582 PushContext(verbatim.source, llvm::acc::Directive::ACCD_routine); 583 } 584 ClearDataSharingAttributeObjects(); 585 return true; 586 } 587 588 static const parser::AccObjectList &GetAccObjectList( 589 const parser::AccClause &clause) { 590 if (const auto *copyClause = 591 std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) { 592 return copyClause->v; 593 } else if (const auto *createClause = 594 std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) { 595 const Fortran::parser::AccObjectListWithModifier &listWithModifier = 596 createClause->v; 597 const Fortran::parser::AccObjectList &accObjectList = 598 std::get<Fortran::parser::AccObjectList>(listWithModifier.t); 599 return accObjectList; 600 } else if (const auto *copyinClause = 601 std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) { 602 const Fortran::parser::AccObjectListWithModifier &listWithModifier = 603 copyinClause->v; 604 const Fortran::parser::AccObjectList &accObjectList = 605 std::get<Fortran::parser::AccObjectList>(listWithModifier.t); 606 return accObjectList; 607 } else if (const auto *copyoutClause = 608 std::get_if<Fortran::parser::AccClause::Copyout>(&clause.u)) { 609 const Fortran::parser::AccObjectListWithModifier &listWithModifier = 610 copyoutClause->v; 611 const Fortran::parser::AccObjectList &accObjectList = 612 std::get<Fortran::parser::AccObjectList>(listWithModifier.t); 613 return accObjectList; 614 } else if (const auto *presentClause = 615 std::get_if<Fortran::parser::AccClause::Present>(&clause.u)) { 616 return presentClause->v; 617 } else if (const auto *deviceptrClause = 618 std::get_if<Fortran::parser::AccClause::Deviceptr>( 619 &clause.u)) { 620 return deviceptrClause->v; 621 } else if (const auto *deviceResidentClause = 622 std::get_if<Fortran::parser::AccClause::DeviceResident>( 623 &clause.u)) { 624 return deviceResidentClause->v; 625 } else if (const auto *linkClause = 626 std::get_if<Fortran::parser::AccClause::Link>(&clause.u)) { 627 return linkClause->v; 628 } else { 629 llvm_unreachable("Clause without object list!"); 630 } 631 } 632 633 void AccAttributeVisitor::Post( 634 const parser::OpenACCStandaloneDeclarativeConstruct &x) { 635 const auto &clauseList = std::get<parser::AccClauseList>(x.t); 636 for (const auto &clause : clauseList.v) { 637 // Restriction - line 2414 638 DoNotAllowAssumedSizedArray(GetAccObjectList(clause)); 639 } 640 } 641 642 bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) { 643 const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; 644 const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)}; 645 const auto &clauseList{std::get<parser::AccClauseList>(beginDir.t)}; 646 if (loopDir.v == llvm::acc::Directive::ACCD_loop) { 647 PushContext(loopDir.source, loopDir.v); 648 } 649 ClearDataSharingAttributeObjects(); 650 SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); 651 PrivatizeAssociatedLoopIndex(x); 652 return true; 653 } 654 655 bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) { 656 const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)}; 657 switch (standaloneDir.v) { 658 case llvm::acc::Directive::ACCD_enter_data: 659 case llvm::acc::Directive::ACCD_exit_data: 660 case llvm::acc::Directive::ACCD_init: 661 case llvm::acc::Directive::ACCD_set: 662 case llvm::acc::Directive::ACCD_shutdown: 663 case llvm::acc::Directive::ACCD_update: 664 PushContext(standaloneDir.source, standaloneDir.v); 665 break; 666 default: 667 break; 668 } 669 ClearDataSharingAttributeObjects(); 670 return true; 671 } 672 673 Symbol *AccAttributeVisitor::ResolveName(const parser::Name &name) { 674 Symbol *prev{currScope().FindSymbol(name.source)}; 675 if (prev != name.symbol) { 676 name.symbol = prev; 677 } 678 return prev; 679 } 680 681 bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct &x) { 682 const auto &optName{std::get<std::optional<parser::Name>>(x.t)}; 683 if (optName) { 684 if (!ResolveName(*optName)) 685 context_.Say((*optName).source, 686 "No function or subroutine declared for '%s'"_err_en_US, 687 (*optName).source); 688 } 689 return true; 690 } 691 692 bool AccAttributeVisitor::Pre(const parser::AccBindClause &x) { 693 if (const auto *name{std::get_if<parser::Name>(&x.u)}) { 694 if (!ResolveName(*name)) 695 context_.Say(name->source, 696 "No function or subroutine declared for '%s'"_err_en_US, 697 name->source); 698 } 699 return true; 700 } 701 702 bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) { 703 const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; 704 const auto &combinedDir{ 705 std::get<parser::AccCombinedDirective>(beginBlockDir.t)}; 706 switch (combinedDir.v) { 707 case llvm::acc::Directive::ACCD_kernels_loop: 708 case llvm::acc::Directive::ACCD_parallel_loop: 709 case llvm::acc::Directive::ACCD_serial_loop: 710 PushContext(combinedDir.source, combinedDir.v); 711 break; 712 default: 713 break; 714 } 715 ClearDataSharingAttributeObjects(); 716 return true; 717 } 718 719 static bool IsLastNameArray(const parser::Designator &designator) { 720 const auto &name{GetLastName(designator)}; 721 const evaluate::DataRef dataRef{*(name.symbol)}; 722 return std::visit( 723 common::visitors{ 724 [](const evaluate::SymbolRef &ref) { return ref->Rank() > 0; }, 725 [](const evaluate::ArrayRef &aref) { 726 return aref.base().IsSymbol() || 727 aref.base().GetComponent().base().Rank() == 0; 728 }, 729 [](const auto &) { return false; }, 730 }, 731 dataRef.u); 732 } 733 734 void AccAttributeVisitor::AllowOnlyArrayAndSubArray( 735 const parser::AccObjectList &objectList) { 736 for (const auto &accObject : objectList.v) { 737 std::visit( 738 common::visitors{ 739 [&](const parser::Designator &designator) { 740 if (!IsLastNameArray(designator)) 741 context_.Say(designator.source, 742 "Only array element or subarray are allowed in %s directive"_err_en_US, 743 parser::ToUpperCaseLetters( 744 llvm::acc::getOpenACCDirectiveName( 745 GetContext().directive) 746 .str())); 747 }, 748 [&](const auto &name) { 749 context_.Say(name.source, 750 "Only array element or subarray are allowed in %s directive"_err_en_US, 751 parser::ToUpperCaseLetters( 752 llvm::acc::getOpenACCDirectiveName(GetContext().directive) 753 .str())); 754 }, 755 }, 756 accObject.u); 757 } 758 } 759 760 void AccAttributeVisitor::DoNotAllowAssumedSizedArray( 761 const parser::AccObjectList &objectList) { 762 for (const auto &accObject : objectList.v) { 763 std::visit( 764 common::visitors{ 765 [&](const parser::Designator &designator) { 766 const auto &name{GetLastName(designator)}; 767 if (name.symbol && semantics::IsAssumedSizeArray(*name.symbol)) 768 context_.Say(designator.source, 769 "Assumed-size dummy arrays may not appear on the %s " 770 "directive"_err_en_US, 771 parser::ToUpperCaseLetters( 772 llvm::acc::getOpenACCDirectiveName( 773 GetContext().directive) 774 .str())); 775 }, 776 [&](const auto &name) { 777 778 }, 779 }, 780 accObject.u); 781 } 782 } 783 784 bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) { 785 const auto &verbatim{std::get<parser::Verbatim>(x.t)}; 786 PushContext(verbatim.source, llvm::acc::Directive::ACCD_cache); 787 ClearDataSharingAttributeObjects(); 788 789 const auto &objectListWithModifier = 790 std::get<parser::AccObjectListWithModifier>(x.t); 791 const auto &objectList = 792 std::get<Fortran::parser::AccObjectList>(objectListWithModifier.t); 793 794 // 2.10 Cache directive restriction: A var in a cache directive must be a 795 // single array element or a simple subarray. 796 AllowOnlyArrayAndSubArray(objectList); 797 798 return true; 799 } 800 801 std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses( 802 const parser::AccClauseList &x) { 803 std::int64_t collapseLevel{0}; 804 for (const auto &clause : x.v) { 805 if (const auto *collapseClause{ 806 std::get_if<parser::AccClause::Collapse>(&clause.u)}) { 807 if (const auto v{EvaluateInt64(context_, collapseClause->v)}) { 808 collapseLevel = *v; 809 } 810 } 811 } 812 813 if (collapseLevel) { 814 return collapseLevel; 815 } 816 return 1; // default is outermost loop 817 } 818 819 void AccAttributeVisitor::PrivatizeAssociatedLoopIndex( 820 const parser::OpenACCLoopConstruct &x) { 821 std::int64_t level{GetContext().associatedLoopLevel}; 822 if (level <= 0) { // collpase value was negative or 0 823 return; 824 } 825 Symbol::Flag ivDSA{Symbol::Flag::AccPrivate}; 826 827 const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)}; 828 for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { 829 // go through all the nested do-loops and resolve index variables 830 const parser::Name &iv{GetLoopIndex(*loop)}; 831 if (auto *symbol{ResolveAcc(iv, ivDSA, currScope())}) { 832 symbol->set(Symbol::Flag::AccPreDetermined); 833 iv.symbol = symbol; // adjust the symbol within region 834 AddToContextObjectWithDSA(*symbol, ivDSA); 835 } 836 837 const auto &block{std::get<parser::Block>(loop->t)}; 838 const auto it{block.begin()}; 839 loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; 840 } 841 CHECK(level == 0); 842 } 843 844 void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) { 845 if (!dirContext_.empty()) { 846 switch (x.v) { 847 case llvm::acc::DefaultValue::ACC_Default_present: 848 SetContextDefaultDSA(Symbol::Flag::AccPresent); 849 break; 850 case llvm::acc::DefaultValue::ACC_Default_none: 851 SetContextDefaultDSA(Symbol::Flag::AccNone); 852 break; 853 } 854 } 855 } 856 857 // For OpenACC constructs, check all the data-refs within the constructs 858 // and adjust the symbol for each Name if necessary 859 void AccAttributeVisitor::Post(const parser::Name &name) { 860 auto *symbol{name.symbol}; 861 if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { 862 if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() && 863 !IsObjectWithDSA(*symbol)) { 864 if (Symbol * found{currScope().FindSymbol(name.source)}) { 865 if (symbol != found) { 866 name.symbol = found; // adjust the symbol within region 867 } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) { 868 // 2.5.14. 869 context_.Say(name.source, 870 "The DEFAULT(NONE) clause requires that '%s' must be listed in " 871 "a data-mapping clause"_err_en_US, 872 symbol->name()); 873 } 874 } 875 } 876 } // within OpenACC construct 877 } 878 879 Symbol *AccAttributeVisitor::ResolveAccCommonBlockName( 880 const parser::Name *name) { 881 if (!name) { 882 return nullptr; 883 } else if (auto *prev{ 884 GetContext().scope.parent().FindCommonBlock(name->source)}) { 885 name->symbol = prev; 886 return prev; 887 } else { 888 return nullptr; 889 } 890 } 891 892 void AccAttributeVisitor::ResolveAccObjectList( 893 const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) { 894 for (const auto &accObject : accObjectList.v) { 895 ResolveAccObject(accObject, accFlag); 896 } 897 } 898 899 void AccAttributeVisitor::ResolveAccObject( 900 const parser::AccObject &accObject, Symbol::Flag accFlag) { 901 std::visit( 902 common::visitors{ 903 [&](const parser::Designator &designator) { 904 if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { 905 if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) { 906 AddToContextObjectWithDSA(*symbol, accFlag); 907 if (dataSharingAttributeFlags.test(accFlag)) { 908 CheckMultipleAppearances(*name, *symbol, accFlag); 909 } 910 } 911 } else { 912 // Array sections to be changed to substrings as needed 913 if (AnalyzeExpr(context_, designator)) { 914 if (std::holds_alternative<parser::Substring>(designator.u)) { 915 context_.Say(designator.source, 916 "Substrings are not allowed on OpenACC " 917 "directives or clauses"_err_en_US); 918 } 919 } 920 // other checks, more TBD 921 } 922 }, 923 [&](const parser::Name &name) { // common block 924 if (auto *symbol{ResolveAccCommonBlockName(&name)}) { 925 CheckMultipleAppearances( 926 name, *symbol, Symbol::Flag::AccCommonBlock); 927 for (auto &object : symbol->get<CommonBlockDetails>().objects()) { 928 if (auto *resolvedObject{ 929 ResolveAcc(*object, accFlag, currScope())}) { 930 AddToContextObjectWithDSA(*resolvedObject, accFlag); 931 } 932 } 933 } else { 934 context_.Say(name.source, 935 "COMMON block must be declared in the same scoping unit " 936 "in which the OpenACC directive or clause appears"_err_en_US); 937 } 938 }, 939 }, 940 accObject.u); 941 } 942 943 Symbol *AccAttributeVisitor::ResolveAcc( 944 const parser::Name &name, Symbol::Flag accFlag, Scope &scope) { 945 if (accFlagsRequireNewSymbol.test(accFlag)) { 946 return DeclarePrivateAccessEntity(name, accFlag, scope); 947 } else { 948 return DeclareOrMarkOtherAccessEntity(name, accFlag); 949 } 950 } 951 952 Symbol *AccAttributeVisitor::ResolveAcc( 953 Symbol &symbol, Symbol::Flag accFlag, Scope &scope) { 954 if (accFlagsRequireNewSymbol.test(accFlag)) { 955 return DeclarePrivateAccessEntity(symbol, accFlag, scope); 956 } else { 957 return DeclareOrMarkOtherAccessEntity(symbol, accFlag); 958 } 959 } 960 961 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( 962 const parser::Name &name, Symbol::Flag accFlag) { 963 Symbol *prev{currScope().FindSymbol(name.source)}; 964 if (!name.symbol || !prev) { 965 return nullptr; 966 } else if (prev != name.symbol) { 967 name.symbol = prev; 968 } 969 return DeclareOrMarkOtherAccessEntity(*prev, accFlag); 970 } 971 972 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( 973 Symbol &object, Symbol::Flag accFlag) { 974 if (accFlagsRequireMark.test(accFlag)) { 975 object.set(accFlag); 976 } 977 return &object; 978 } 979 980 static bool WithMultipleAppearancesAccException( 981 const Symbol &symbol, Symbol::Flag flag) { 982 return false; // Place holder 983 } 984 985 void AccAttributeVisitor::CheckMultipleAppearances( 986 const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) { 987 const auto *target{&symbol}; 988 if (accFlagsRequireNewSymbol.test(accFlag)) { 989 if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) { 990 target = &details->symbol(); 991 } 992 } 993 if (HasDataSharingAttributeObject(*target) && 994 !WithMultipleAppearancesAccException(symbol, accFlag)) { 995 context_.Say(name.source, 996 "'%s' appears in more than one data-sharing clause " 997 "on the same OpenACC directive"_err_en_US, 998 name.ToString()); 999 } else { 1000 AddDataSharingAttributeObject(*target); 1001 } 1002 } 1003 1004 bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { 1005 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; 1006 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 1007 switch (beginDir.v) { 1008 case llvm::omp::Directive::OMPD_master: 1009 case llvm::omp::Directive::OMPD_ordered: 1010 case llvm::omp::Directive::OMPD_parallel: 1011 case llvm::omp::Directive::OMPD_single: 1012 case llvm::omp::Directive::OMPD_target: 1013 case llvm::omp::Directive::OMPD_target_data: 1014 case llvm::omp::Directive::OMPD_task: 1015 case llvm::omp::Directive::OMPD_teams: 1016 case llvm::omp::Directive::OMPD_workshare: 1017 case llvm::omp::Directive::OMPD_parallel_workshare: 1018 case llvm::omp::Directive::OMPD_target_teams: 1019 case llvm::omp::Directive::OMPD_target_parallel: 1020 case llvm::omp::Directive::OMPD_taskgroup: 1021 PushContext(beginDir.source, beginDir.v); 1022 break; 1023 default: 1024 // TODO others 1025 break; 1026 } 1027 ClearDataSharingAttributeObjects(); 1028 ClearPrivateDataSharingAttributeObjects(); 1029 ClearAllocateNames(); 1030 return true; 1031 } 1032 1033 void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) { 1034 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; 1035 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 1036 switch (beginDir.v) { 1037 case llvm::omp::Directive::OMPD_parallel: 1038 case llvm::omp::Directive::OMPD_single: 1039 case llvm::omp::Directive::OMPD_target: 1040 case llvm::omp::Directive::OMPD_task: 1041 case llvm::omp::Directive::OMPD_teams: 1042 case llvm::omp::Directive::OMPD_parallel_workshare: 1043 case llvm::omp::Directive::OMPD_target_teams: 1044 case llvm::omp::Directive::OMPD_target_parallel: { 1045 bool hasPrivate; 1046 for (const auto *allocName : allocateNames_) { 1047 hasPrivate = false; 1048 for (auto privateObj : privateDataSharingAttributeObjects_) { 1049 const Symbol &symbolPrivate{*privateObj}; 1050 if (allocName->source == symbolPrivate.name()) { 1051 hasPrivate = true; 1052 break; 1053 } 1054 } 1055 if (!hasPrivate) { 1056 context_.Say(allocName->source, 1057 "The ALLOCATE clause requires that '%s' must be listed in a " 1058 "private " 1059 "data-sharing attribute clause on the same directive"_err_en_US, 1060 allocName->ToString()); 1061 } 1062 } 1063 break; 1064 } 1065 default: 1066 break; 1067 } 1068 PopContext(); 1069 } 1070 1071 bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { 1072 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 1073 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 1074 const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; 1075 switch (beginDir.v) { 1076 case llvm::omp::Directive::OMPD_distribute: 1077 case llvm::omp::Directive::OMPD_distribute_parallel_do: 1078 case llvm::omp::Directive::OMPD_distribute_parallel_do_simd: 1079 case llvm::omp::Directive::OMPD_distribute_simd: 1080 case llvm::omp::Directive::OMPD_do: 1081 case llvm::omp::Directive::OMPD_do_simd: 1082 case llvm::omp::Directive::OMPD_parallel_do: 1083 case llvm::omp::Directive::OMPD_parallel_do_simd: 1084 case llvm::omp::Directive::OMPD_simd: 1085 case llvm::omp::Directive::OMPD_target_parallel_do: 1086 case llvm::omp::Directive::OMPD_target_parallel_do_simd: 1087 case llvm::omp::Directive::OMPD_target_teams_distribute: 1088 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: 1089 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: 1090 case llvm::omp::Directive::OMPD_target_teams_distribute_simd: 1091 case llvm::omp::Directive::OMPD_target_simd: 1092 case llvm::omp::Directive::OMPD_taskloop: 1093 case llvm::omp::Directive::OMPD_taskloop_simd: 1094 case llvm::omp::Directive::OMPD_teams_distribute: 1095 case llvm::omp::Directive::OMPD_teams_distribute_parallel_do: 1096 case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd: 1097 case llvm::omp::Directive::OMPD_teams_distribute_simd: 1098 PushContext(beginDir.source, beginDir.v); 1099 break; 1100 default: 1101 break; 1102 } 1103 ClearDataSharingAttributeObjects(); 1104 SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); 1105 1106 if (beginDir.v == llvm::omp::Directive::OMPD_do) { 1107 if (const auto &doConstruct{ 1108 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 1109 if (doConstruct.value().IsDoWhile()) { 1110 return true; 1111 } 1112 } 1113 } 1114 PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x); 1115 ordCollapseLevel = GetAssociatedLoopLevelFromClauses(clauseList) + 1; 1116 return true; 1117 } 1118 1119 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct( 1120 const parser::Name &iv) { 1121 auto targetIt{dirContext_.rbegin()}; 1122 for (;; ++targetIt) { 1123 if (targetIt == dirContext_.rend()) { 1124 return; 1125 } 1126 if (llvm::omp::parallelSet.test(targetIt->directive) || 1127 llvm::omp::taskGeneratingSet.test(targetIt->directive)) { 1128 break; 1129 } 1130 } 1131 if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) { 1132 targetIt++; 1133 symbol->set(Symbol::Flag::OmpPreDetermined); 1134 iv.symbol = symbol; // adjust the symbol within region 1135 for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) { 1136 AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it); 1137 } 1138 } 1139 } 1140 1141 // [OMP-4.5]2.15.1.1 Data-sharing Attribute Rules - Predetermined 1142 // - A loop iteration variable for a sequential loop in a parallel 1143 // or task generating construct is private in the innermost such 1144 // construct that encloses the loop 1145 // Loop iteration variables are not well defined for DO WHILE loop. 1146 // Use of DO CONCURRENT inside OpenMP construct is unspecified behavior 1147 // till OpenMP-5.0 standard. 1148 // In above both cases we skip the privatization of iteration variables. 1149 bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) { 1150 // TODO:[OpenMP 5.1] DO CONCURRENT indices are private 1151 if (x.IsDoNormal()) { 1152 if (!dirContext_.empty() && GetContext().withinConstruct) { 1153 if (const auto &iv{GetLoopIndex(x)}; iv.symbol) { 1154 if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) { 1155 ResolveSeqLoopIndexInParallelOrTaskConstruct(iv); 1156 } else { 1157 // TODO: conflict checks with explicitly determined DSA 1158 } 1159 ordCollapseLevel--; 1160 if (ordCollapseLevel) { 1161 if (const auto *details{iv.symbol->detailsIf<HostAssocDetails>()}) { 1162 const Symbol *tpSymbol = &details->symbol(); 1163 if (tpSymbol->test(Symbol::Flag::OmpThreadprivate)) { 1164 context_.Say(iv.source, 1165 "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US, 1166 iv.ToString()); 1167 } 1168 } 1169 } 1170 } 1171 } 1172 } 1173 return true; 1174 } 1175 1176 std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses( 1177 const parser::OmpClauseList &x) { 1178 std::int64_t orderedLevel{0}; 1179 std::int64_t collapseLevel{0}; 1180 1181 const parser::OmpClause *ordClause{nullptr}; 1182 const parser::OmpClause *collClause{nullptr}; 1183 1184 for (const auto &clause : x.v) { 1185 if (const auto *orderedClause{ 1186 std::get_if<parser::OmpClause::Ordered>(&clause.u)}) { 1187 if (const auto v{EvaluateInt64(context_, orderedClause->v)}) { 1188 orderedLevel = *v; 1189 } 1190 ordClause = &clause; 1191 } 1192 if (const auto *collapseClause{ 1193 std::get_if<parser::OmpClause::Collapse>(&clause.u)}) { 1194 if (const auto v{EvaluateInt64(context_, collapseClause->v)}) { 1195 collapseLevel = *v; 1196 } 1197 collClause = &clause; 1198 } 1199 } 1200 1201 if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) { 1202 SetAssociatedClause(*ordClause); 1203 return orderedLevel; 1204 } else if (!orderedLevel && collapseLevel) { 1205 SetAssociatedClause(*collClause); 1206 return collapseLevel; 1207 } // orderedLevel < collapseLevel is an error handled in structural checks 1208 return 1; // default is outermost loop 1209 } 1210 1211 // 2.15.1.1 Data-sharing Attribute Rules - Predetermined 1212 // - The loop iteration variable(s) in the associated do-loop(s) of a do, 1213 // parallel do, taskloop, or distribute construct is (are) private. 1214 // - The loop iteration variable in the associated do-loop of a simd construct 1215 // with just one associated do-loop is linear with a linear-step that is the 1216 // increment of the associated do-loop. 1217 // - The loop iteration variables in the associated do-loops of a simd 1218 // construct with multiple associated do-loops are lastprivate. 1219 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( 1220 const parser::OpenMPLoopConstruct &x) { 1221 std::int64_t level{GetContext().associatedLoopLevel}; 1222 if (level <= 0) { 1223 return; 1224 } 1225 Symbol::Flag ivDSA; 1226 if (!llvm::omp::simdSet.test(GetContext().directive)) { 1227 ivDSA = Symbol::Flag::OmpPrivate; 1228 } else if (level == 1) { 1229 ivDSA = Symbol::Flag::OmpLinear; 1230 } else { 1231 ivDSA = Symbol::Flag::OmpLastPrivate; 1232 } 1233 1234 const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)}; 1235 for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { 1236 // go through all the nested do-loops and resolve index variables 1237 const parser::Name &iv{GetLoopIndex(*loop)}; 1238 if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) { 1239 symbol->set(Symbol::Flag::OmpPreDetermined); 1240 iv.symbol = symbol; // adjust the symbol within region 1241 AddToContextObjectWithDSA(*symbol, ivDSA); 1242 } 1243 1244 const auto &block{std::get<parser::Block>(loop->t)}; 1245 const auto it{block.begin()}; 1246 loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; 1247 } 1248 CheckAssocLoopLevel(level, GetAssociatedClause()); 1249 } 1250 void OmpAttributeVisitor::CheckAssocLoopLevel( 1251 std::int64_t level, const parser::OmpClause *clause) { 1252 if (clause && level != 0) { 1253 context_.Say(clause->source, 1254 "The value of the parameter in the COLLAPSE or ORDERED clause must" 1255 " not be larger than the number of nested loops" 1256 " following the construct."_err_en_US); 1257 } 1258 } 1259 1260 bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) { 1261 const auto &beginSectionsDir{ 1262 std::get<parser::OmpBeginSectionsDirective>(x.t)}; 1263 const auto &beginDir{ 1264 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 1265 switch (beginDir.v) { 1266 case llvm::omp::Directive::OMPD_parallel_sections: 1267 case llvm::omp::Directive::OMPD_sections: 1268 PushContext(beginDir.source, beginDir.v); 1269 break; 1270 default: 1271 break; 1272 } 1273 ClearDataSharingAttributeObjects(); 1274 return true; 1275 } 1276 1277 bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) { 1278 const auto &criticalDir{std::get<parser::OmpCriticalDirective>(x.t)}; 1279 PushContext(criticalDir.source, llvm::omp::Directive::OMPD_critical); 1280 return true; 1281 } 1282 1283 bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) { 1284 PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate); 1285 const auto &list{std::get<parser::OmpObjectList>(x.t)}; 1286 ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate); 1287 return true; 1288 } 1289 1290 void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) { 1291 if (!dirContext_.empty()) { 1292 switch (x.v) { 1293 case parser::OmpDefaultClause::Type::Private: 1294 SetContextDefaultDSA(Symbol::Flag::OmpPrivate); 1295 break; 1296 case parser::OmpDefaultClause::Type::Firstprivate: 1297 SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate); 1298 break; 1299 case parser::OmpDefaultClause::Type::Shared: 1300 SetContextDefaultDSA(Symbol::Flag::OmpShared); 1301 break; 1302 case parser::OmpDefaultClause::Type::None: 1303 SetContextDefaultDSA(Symbol::Flag::OmpNone); 1304 break; 1305 } 1306 } 1307 } 1308 1309 // For OpenMP constructs, check all the data-refs within the constructs 1310 // and adjust the symbol for each Name if necessary 1311 void OmpAttributeVisitor::Post(const parser::Name &name) { 1312 auto *symbol{name.symbol}; 1313 if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { 1314 if (!symbol->owner().IsDerivedType() && !symbol->has<ProcEntityDetails>() && 1315 !IsObjectWithDSA(*symbol)) { 1316 // TODO: create a separate function to go through the rules for 1317 // predetermined, explicitly determined, and implicitly 1318 // determined data-sharing attributes (2.15.1.1). 1319 if (Symbol * found{currScope().FindSymbol(name.source)}) { 1320 if (symbol != found) { 1321 name.symbol = found; // adjust the symbol within region 1322 } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) { 1323 context_.Say(name.source, 1324 "The DEFAULT(NONE) clause requires that '%s' must be listed in " 1325 "a data-sharing attribute clause"_err_en_US, 1326 symbol->name()); 1327 } 1328 } 1329 } 1330 } // within OpenMP construct 1331 } 1332 1333 Symbol *OmpAttributeVisitor::ResolveName(const parser::Name *name) { 1334 if (auto *resolvedSymbol{ 1335 name ? GetContext().scope.FindSymbol(name->source) : nullptr}) { 1336 name->symbol = resolvedSymbol; 1337 return resolvedSymbol; 1338 } else { 1339 return nullptr; 1340 } 1341 } 1342 1343 void OmpAttributeVisitor::ResolveOmpName( 1344 const parser::Name &name, Symbol::Flag ompFlag) { 1345 if (ResolveName(&name)) { 1346 if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) { 1347 if (dataSharingAttributeFlags.test(ompFlag)) { 1348 AddToContextObjectWithDSA(*resolvedSymbol, ompFlag); 1349 } 1350 } 1351 } 1352 } 1353 1354 void OmpAttributeVisitor::ResolveOmpNameList( 1355 const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) { 1356 for (const auto &name : nameList) { 1357 ResolveOmpName(name, ompFlag); 1358 } 1359 } 1360 1361 Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName( 1362 const parser::Name *name) { 1363 if (auto *prev{name 1364 ? GetContext().scope.parent().FindCommonBlock(name->source) 1365 : nullptr}) { 1366 name->symbol = prev; 1367 return prev; 1368 } 1369 // Check if the Common Block is declared in the current scope 1370 if (auto *commonBlockSymbol{ 1371 name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) { 1372 name->symbol = commonBlockSymbol; 1373 return commonBlockSymbol; 1374 } 1375 return nullptr; 1376 } 1377 1378 void OmpAttributeVisitor::ResolveOmpObjectList( 1379 const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) { 1380 for (const auto &ompObject : ompObjectList.v) { 1381 ResolveOmpObject(ompObject, ompFlag); 1382 } 1383 } 1384 1385 void OmpAttributeVisitor::ResolveOmpObject( 1386 const parser::OmpObject &ompObject, Symbol::Flag ompFlag) { 1387 std::visit( 1388 common::visitors{ 1389 [&](const parser::Designator &designator) { 1390 if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { 1391 if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) { 1392 if (dataCopyingAttributeFlags.test(ompFlag)) { 1393 CheckDataCopyingClause(*name, *symbol, ompFlag); 1394 } else { 1395 AddToContextObjectWithDSA(*symbol, ompFlag); 1396 if (dataSharingAttributeFlags.test(ompFlag)) { 1397 CheckMultipleAppearances(*name, *symbol, ompFlag); 1398 } 1399 if (privateDataSharingAttributeFlags.test(ompFlag)) { 1400 CheckPrivateDSAObject(*name, *symbol, ompFlag); 1401 } 1402 1403 if (ompFlag == Symbol::Flag::OmpAllocate) { 1404 AddAllocateName(name); 1405 } 1406 } 1407 } 1408 } else { 1409 // Array sections to be changed to substrings as needed 1410 if (AnalyzeExpr(context_, designator)) { 1411 if (std::holds_alternative<parser::Substring>(designator.u)) { 1412 context_.Say(designator.source, 1413 "Substrings are not allowed on OpenMP " 1414 "directives or clauses"_err_en_US); 1415 } 1416 } 1417 // other checks, more TBD 1418 } 1419 }, 1420 [&](const parser::Name &name) { // common block 1421 if (auto *symbol{ResolveOmpCommonBlockName(&name)}) { 1422 if (!dataCopyingAttributeFlags.test(ompFlag)) { 1423 CheckMultipleAppearances( 1424 name, *symbol, Symbol::Flag::OmpCommonBlock); 1425 } 1426 // 2.15.3 When a named common block appears in a list, it has the 1427 // same meaning as if every explicit member of the common block 1428 // appeared in the list 1429 for (auto &object : symbol->get<CommonBlockDetails>().objects()) { 1430 if (auto *resolvedObject{ 1431 ResolveOmp(*object, ompFlag, currScope())}) { 1432 if (dataCopyingAttributeFlags.test(ompFlag)) { 1433 CheckDataCopyingClause(name, *resolvedObject, ompFlag); 1434 } else { 1435 AddToContextObjectWithDSA(*resolvedObject, ompFlag); 1436 } 1437 } 1438 } 1439 } else { 1440 context_.Say(name.source, // 2.15.3 1441 "COMMON block must be declared in the same scoping unit " 1442 "in which the OpenMP directive or clause appears"_err_en_US); 1443 } 1444 }, 1445 }, 1446 ompObject.u); 1447 } 1448 1449 Symbol *OmpAttributeVisitor::ResolveOmp( 1450 const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) { 1451 if (ompFlagsRequireNewSymbol.test(ompFlag)) { 1452 return DeclarePrivateAccessEntity(name, ompFlag, scope); 1453 } else { 1454 return DeclareOrMarkOtherAccessEntity(name, ompFlag); 1455 } 1456 } 1457 1458 Symbol *OmpAttributeVisitor::ResolveOmp( 1459 Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) { 1460 if (ompFlagsRequireNewSymbol.test(ompFlag)) { 1461 return DeclarePrivateAccessEntity(symbol, ompFlag, scope); 1462 } else { 1463 return DeclareOrMarkOtherAccessEntity(symbol, ompFlag); 1464 } 1465 } 1466 1467 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( 1468 const parser::Name &name, Symbol::Flag ompFlag) { 1469 Symbol *prev{currScope().FindSymbol(name.source)}; 1470 if (!name.symbol || !prev) { 1471 return nullptr; 1472 } else if (prev != name.symbol) { 1473 name.symbol = prev; 1474 } 1475 return DeclareOrMarkOtherAccessEntity(*prev, ompFlag); 1476 } 1477 1478 Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( 1479 Symbol &object, Symbol::Flag ompFlag) { 1480 if (ompFlagsRequireMark.test(ompFlag)) { 1481 object.set(ompFlag); 1482 } 1483 return &object; 1484 } 1485 1486 static bool WithMultipleAppearancesOmpException( 1487 const Symbol &symbol, Symbol::Flag flag) { 1488 return (flag == Symbol::Flag::OmpFirstPrivate && 1489 symbol.test(Symbol::Flag::OmpLastPrivate)) || 1490 (flag == Symbol::Flag::OmpLastPrivate && 1491 symbol.test(Symbol::Flag::OmpFirstPrivate)); 1492 } 1493 1494 void OmpAttributeVisitor::CheckMultipleAppearances( 1495 const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { 1496 const auto *target{&symbol}; 1497 if (ompFlagsRequireNewSymbol.test(ompFlag)) { 1498 if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) { 1499 target = &details->symbol(); 1500 } 1501 } 1502 if (HasDataSharingAttributeObject(*target) && 1503 !WithMultipleAppearancesOmpException(symbol, ompFlag)) { 1504 context_.Say(name.source, 1505 "'%s' appears in more than one data-sharing clause " 1506 "on the same OpenMP directive"_err_en_US, 1507 name.ToString()); 1508 } else { 1509 AddDataSharingAttributeObject(*target); 1510 if (privateDataSharingAttributeFlags.test(ompFlag)) { 1511 AddPrivateDataSharingAttributeObjects(*target); 1512 } 1513 } 1514 } 1515 1516 void ResolveAccParts( 1517 SemanticsContext &context, const parser::ProgramUnit &node) { 1518 if (context.IsEnabled(common::LanguageFeature::OpenACC)) { 1519 AccAttributeVisitor{context}.Walk(node); 1520 } 1521 } 1522 1523 void ResolveOmpParts( 1524 SemanticsContext &context, const parser::ProgramUnit &node) { 1525 if (context.IsEnabled(common::LanguageFeature::OpenMP)) { 1526 OmpAttributeVisitor{context}.Walk(node); 1527 if (!context.AnyFatalError()) { 1528 // The data-sharing attribute of the loop iteration variable for a 1529 // sequential loop (2.15.1.1) can only be determined when visiting 1530 // the corresponding DoConstruct, a second walk is to adjust the 1531 // symbols for all the data-refs of that loop iteration variable 1532 // prior to the DoConstruct. 1533 OmpAttributeVisitor{context}.Walk(node); 1534 } 1535 } 1536 } 1537 1538 void OmpAttributeVisitor::CheckDataCopyingClause( 1539 const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { 1540 const auto *checkSymbol{&symbol}; 1541 if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) 1542 checkSymbol = &details->symbol(); 1543 1544 if (ompFlag == Symbol::Flag::OmpCopyIn) { 1545 // List of items/objects that can appear in a 'copyin' clause must be 1546 // 'threadprivate' 1547 if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate)) 1548 context_.Say(name.source, 1549 "Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US, 1550 checkSymbol->name()); 1551 } else if (ompFlag == Symbol::Flag::OmpCopyPrivate && 1552 GetContext().directive == llvm::omp::Directive::OMPD_single) { 1553 // A list item that appears in a 'copyprivate' clause may not appear on a 1554 // 'private' or 'firstprivate' clause on a single construct 1555 if (IsObjectWithDSA(symbol) && 1556 (symbol.test(Symbol::Flag::OmpPrivate) || 1557 symbol.test(Symbol::Flag::OmpFirstPrivate))) { 1558 context_.Say(name.source, 1559 "COPYPRIVATE variable '%s' may not appear on a PRIVATE or " 1560 "FIRSTPRIVATE clause on a SINGLE construct"_err_en_US, 1561 symbol.name()); 1562 } else { 1563 // List of items/objects that can appear in a 'copyprivate' clause must be 1564 // either 'private' or 'threadprivate' in enclosing context. 1565 if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate) && 1566 !(HasSymbolInEnclosingScope(symbol, currScope()) && 1567 symbol.test(Symbol::Flag::OmpPrivate))) { 1568 context_.Say(name.source, 1569 "COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in " 1570 "outer context"_err_en_US, 1571 symbol.name()); 1572 } 1573 } 1574 } 1575 } 1576 1577 void OmpAttributeVisitor::CheckPrivateDSAObject( 1578 const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { 1579 const auto &ultimateSymbol{symbol.GetUltimate()}; 1580 llvm::StringRef clauseName{"PRIVATE"}; 1581 if (ompFlag == Symbol::Flag::OmpFirstPrivate) 1582 clauseName = "FIRSTPRIVATE"; 1583 else if (ompFlag == Symbol::Flag::OmpLastPrivate) 1584 clauseName = "LASTPRIVATE"; 1585 1586 if (ultimateSymbol.test(Symbol::Flag::InNamelist)) { 1587 context_.Say(name.source, 1588 "Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US, 1589 name.ToString(), clauseName.str()); 1590 } 1591 1592 if (stmtFunctionExprSymbols_.find(ultimateSymbol) != 1593 stmtFunctionExprSymbols_.end()) { 1594 context_.Say(name.source, 1595 "Variable '%s' in STATEMENT FUNCTION expression cannot be in a " 1596 "%s clause"_err_en_US, 1597 name.ToString(), clauseName.str()); 1598 } 1599 } 1600 1601 void OmpAttributeVisitor::CheckSourceLabel(const parser::Label &label) { 1602 // Get the context to check if the statement causing a jump to the 'label' is 1603 // in an enclosing OpenMP construct 1604 std::optional<DirContext> thisContext{GetContextIf()}; 1605 sourceLabels_.emplace( 1606 label, std::make_pair(currentStatementSource_, thisContext)); 1607 // Check if the statement with 'label' to which a jump is being introduced 1608 // has already been encountered 1609 auto it{targetLabels_.find(label)}; 1610 if (it != targetLabels_.end()) { 1611 // Check if both the statement with 'label' and the statement that causes a 1612 // jump to the 'label' are in the same scope 1613 CheckLabelContext(currentStatementSource_, it->second.first, thisContext, 1614 it->second.second); 1615 } 1616 } 1617 1618 // Check for invalid branch into or out of OpenMP structured blocks 1619 void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source, 1620 const parser::CharBlock target, std::optional<DirContext> sourceContext, 1621 std::optional<DirContext> targetContext) { 1622 if (targetContext && 1623 (!sourceContext || 1624 (sourceContext->scope != targetContext->scope && 1625 !DoesScopeContain( 1626 &targetContext->scope, sourceContext->scope)))) { 1627 context_ 1628 .Say(source, "invalid branch into an OpenMP structured block"_err_en_US) 1629 .Attach(target, "In the enclosing %s directive branched into"_en_US, 1630 parser::ToUpperCaseLetters( 1631 llvm::omp::getOpenMPDirectiveName(targetContext->directive) 1632 .str())); 1633 } 1634 if (sourceContext && 1635 (!targetContext || 1636 (sourceContext->scope != targetContext->scope && 1637 !DoesScopeContain( 1638 &sourceContext->scope, targetContext->scope)))) { 1639 context_ 1640 .Say(source, 1641 "invalid branch leaving an OpenMP structured block"_err_en_US) 1642 .Attach(target, "Outside the enclosing %s directive"_en_US, 1643 parser::ToUpperCaseLetters( 1644 llvm::omp::getOpenMPDirectiveName(sourceContext->directive) 1645 .str())); 1646 } 1647 } 1648 1649 bool OmpAttributeVisitor::HasSymbolInEnclosingScope( 1650 const Symbol &symbol, Scope &scope) { 1651 const auto symbols{scope.parent().GetSymbols()}; 1652 auto it{std::find(symbols.begin(), symbols.end(), symbol)}; 1653 return it != symbols.end(); 1654 } 1655 1656 } // namespace Fortran::semantics 1657