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