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