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