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