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