1 //===-- lib/Semantics/check-omp-structure.cpp -----------------------------===// 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 "check-omp-structure.h" 10 #include "flang/Parser/parse-tree.h" 11 #include "flang/Semantics/tools.h" 12 #include <algorithm> 13 14 namespace Fortran::semantics { 15 16 // Use when clause falls under 'struct OmpClause' in 'parse-tree.h'. 17 #define CHECK_SIMPLE_CLAUSE(X, Y) \ 18 void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \ 19 CheckAllowed(llvm::omp::Clause::Y); \ 20 } 21 22 #define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \ 23 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \ 24 CheckAllowed(llvm::omp::Clause::Y); \ 25 RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \ 26 } 27 28 #define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \ 29 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \ 30 CheckAllowed(llvm::omp::Clause::Y); \ 31 RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \ 32 } 33 34 // Use when clause don't falls under 'struct OmpClause' in 'parse-tree.h'. 35 #define CHECK_SIMPLE_PARSER_CLAUSE(X, Y) \ 36 void OmpStructureChecker::Enter(const parser::X &) { \ 37 CheckAllowed(llvm::omp::Y); \ 38 } 39 40 // 'OmpWorkshareBlockChecker' is used to check the validity of the assignment 41 // statements and the expressions enclosed in an OpenMP Workshare construct 42 class OmpWorkshareBlockChecker { 43 public: 44 OmpWorkshareBlockChecker(SemanticsContext &context, parser::CharBlock source) 45 : context_{context}, source_{source} {} 46 47 template <typename T> bool Pre(const T &) { return true; } 48 template <typename T> void Post(const T &) {} 49 50 bool Pre(const parser::AssignmentStmt &assignment) { 51 const auto &var{std::get<parser::Variable>(assignment.t)}; 52 const auto &expr{std::get<parser::Expr>(assignment.t)}; 53 const auto *lhs{GetExpr(context_, var)}; 54 const auto *rhs{GetExpr(context_, expr)}; 55 if (lhs && rhs) { 56 Tristate isDefined{semantics::IsDefinedAssignment( 57 lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())}; 58 if (isDefined == Tristate::Yes) { 59 context_.Say(expr.source, 60 "Defined assignment statement is not " 61 "allowed in a WORKSHARE construct"_err_en_US); 62 } 63 } 64 return true; 65 } 66 67 bool Pre(const parser::Expr &expr) { 68 if (const auto *e{GetExpr(context_, expr)}) { 69 for (const Symbol &symbol : evaluate::CollectSymbols(*e)) { 70 const Symbol &root{GetAssociationRoot(symbol)}; 71 if (IsFunction(root) && 72 !(root.attrs().test(Attr::ELEMENTAL) || 73 root.attrs().test(Attr::INTRINSIC))) { 74 context_.Say(expr.source, 75 "User defined non-ELEMENTAL function " 76 "'%s' is not allowed in a WORKSHARE construct"_err_en_US, 77 root.name()); 78 } 79 } 80 } 81 return false; 82 } 83 84 private: 85 SemanticsContext &context_; 86 parser::CharBlock source_; 87 }; 88 89 class OmpCycleChecker { 90 public: 91 OmpCycleChecker(SemanticsContext &context, std::int64_t cycleLevel) 92 : context_{context}, cycleLevel_{cycleLevel} {} 93 94 template <typename T> bool Pre(const T &) { return true; } 95 template <typename T> void Post(const T &) {} 96 97 bool Pre(const parser::DoConstruct &dc) { 98 cycleLevel_--; 99 const auto &labelName{std::get<0>(std::get<0>(dc.t).statement.t)}; 100 if (labelName) { 101 labelNamesandLevels_.emplace(labelName.value().ToString(), cycleLevel_); 102 } 103 return true; 104 } 105 106 bool Pre(const parser::CycleStmt &cyclestmt) { 107 std::map<std::string, std::int64_t>::iterator it; 108 bool err{false}; 109 if (cyclestmt.v) { 110 it = labelNamesandLevels_.find(cyclestmt.v->source.ToString()); 111 err = (it != labelNamesandLevels_.end() && it->second > 0); 112 } 113 if (cycleLevel_ > 0 || err) { 114 context_.Say(*cycleSource_, 115 "CYCLE statement to non-innermost associated loop of an OpenMP DO construct"_err_en_US); 116 } 117 return true; 118 } 119 120 bool Pre(const parser::Statement<parser::ActionStmt> &actionstmt) { 121 cycleSource_ = &actionstmt.source; 122 return true; 123 } 124 125 private: 126 SemanticsContext &context_; 127 const parser::CharBlock *cycleSource_; 128 std::int64_t cycleLevel_; 129 std::map<std::string, std::int64_t> labelNamesandLevels_; 130 }; 131 132 bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet &set) { 133 // Definition of close nesting: 134 // 135 // `A region nested inside another region with no parallel region nested 136 // between them` 137 // 138 // Examples: 139 // non-parallel construct 1 140 // non-parallel construct 2 141 // parallel construct 142 // construct 3 143 // In the above example, construct 3 is NOT closely nested inside construct 1 144 // or 2 145 // 146 // non-parallel construct 1 147 // non-parallel construct 2 148 // construct 3 149 // In the above example, construct 3 is closely nested inside BOTH construct 1 150 // and 2 151 // 152 // Algorithm: 153 // Starting from the parent context, Check in a bottom-up fashion, each level 154 // of the context stack. If we have a match for one of the (supplied) 155 // violating directives, `close nesting` is satisfied. If no match is there in 156 // the entire stack, `close nesting` is not satisfied. If at any level, a 157 // `parallel` region is found, `close nesting` is not satisfied. 158 159 if (CurrentDirectiveIsNested()) { 160 int index = dirContext_.size() - 2; 161 while (index != -1) { 162 if (set.test(dirContext_[index].directive)) { 163 return true; 164 } else if (llvm::omp::parallelSet.test(dirContext_[index].directive)) { 165 return false; 166 } 167 index--; 168 } 169 } 170 return false; 171 } 172 173 bool OmpStructureChecker::HasInvalidWorksharingNesting( 174 const parser::CharBlock &source, const OmpDirectiveSet &set) { 175 // set contains all the invalid closely nested directives 176 // for the given directive (`source` here) 177 if (IsCloselyNestedRegion(set)) { 178 context_.Say(source, 179 "A worksharing region may not be closely nested inside a " 180 "worksharing, explicit task, taskloop, critical, ordered, atomic, or " 181 "master region"_err_en_US); 182 return true; 183 } 184 return false; 185 } 186 187 void OmpStructureChecker::HasInvalidDistributeNesting( 188 const parser::OpenMPLoopConstruct &x) { 189 bool violation{false}; 190 191 OmpDirectiveSet distributeSet{llvm::omp::Directive::OMPD_distribute, 192 llvm::omp::Directive::OMPD_distribute_parallel_do, 193 llvm::omp::Directive::OMPD_distribute_parallel_do_simd, 194 llvm::omp::Directive::OMPD_distribute_parallel_for, 195 llvm::omp::Directive::OMPD_distribute_parallel_for_simd, 196 llvm::omp::Directive::OMPD_distribute_simd}; 197 198 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 199 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 200 if (distributeSet.test(beginDir.v)) { 201 // `distribute` region has to be nested 202 if (!CurrentDirectiveIsNested()) { 203 violation = true; 204 } else { 205 // `distribute` region has to be strictly nested inside `teams` 206 if (!llvm::omp::teamSet.test(GetContextParent().directive)) { 207 violation = true; 208 } 209 } 210 } 211 if (violation) { 212 context_.Say(beginDir.source, 213 "`DISTRIBUTE` region has to be strictly nested inside `TEAMS` region."_err_en_US); 214 } 215 } 216 217 void OmpStructureChecker::HasInvalidTeamsNesting( 218 const llvm::omp::Directive &dir, const parser::CharBlock &source) { 219 OmpDirectiveSet allowedSet{llvm::omp::Directive::OMPD_parallel, 220 llvm::omp::Directive::OMPD_parallel_do, 221 llvm::omp::Directive::OMPD_parallel_do_simd, 222 llvm::omp::Directive::OMPD_parallel_for, 223 llvm::omp::Directive::OMPD_parallel_for_simd, 224 llvm::omp::Directive::OMPD_parallel_master, 225 llvm::omp::Directive::OMPD_parallel_master_taskloop, 226 llvm::omp::Directive::OMPD_parallel_master_taskloop_simd, 227 llvm::omp::Directive::OMPD_parallel_sections, 228 llvm::omp::Directive::OMPD_parallel_workshare, 229 llvm::omp::Directive::OMPD_distribute, 230 llvm::omp::Directive::OMPD_distribute_parallel_do, 231 llvm::omp::Directive::OMPD_distribute_parallel_do_simd, 232 llvm::omp::Directive::OMPD_distribute_parallel_for, 233 llvm::omp::Directive::OMPD_distribute_parallel_for_simd, 234 llvm::omp::Directive::OMPD_distribute_simd}; 235 236 if (!allowedSet.test(dir)) { 237 context_.Say(source, 238 "Only `DISTRIBUTE` or `PARALLEL` regions are allowed to be strictly nested inside `TEAMS` region."_err_en_US); 239 } 240 } 241 242 void OmpStructureChecker::CheckPredefinedAllocatorRestriction( 243 const parser::CharBlock &source, const parser::Name &name) { 244 if (const auto *symbol{name.symbol}) { 245 const auto *commonBlock{FindCommonBlockContaining(*symbol)}; 246 const auto &scope{context_.FindScope(symbol->name())}; 247 const Scope &containingScope{GetProgramUnitContaining(scope)}; 248 if (!isPredefinedAllocator && 249 (IsSave(*symbol) || commonBlock || 250 containingScope.kind() == Scope::Kind::Module)) { 251 context_.Say(source, 252 "If list items within the ALLOCATE directive have the " 253 "SAVE attribute, are a common block name, or are " 254 "declared in the scope of a module, then only " 255 "predefined memory allocator parameters can be used " 256 "in the allocator clause"_err_en_US); 257 } 258 } 259 } 260 261 void OmpStructureChecker::CheckPredefinedAllocatorRestriction( 262 const parser::CharBlock &source, 263 const parser::OmpObjectList &ompObjectList) { 264 for (const auto &ompObject : ompObjectList.v) { 265 common::visit( 266 common::visitors{ 267 [&](const parser::Designator &designator) { 268 if (const auto *dataRef{ 269 std::get_if<parser::DataRef>(&designator.u)}) { 270 if (const auto *name{std::get_if<parser::Name>(&dataRef->u)}) { 271 CheckPredefinedAllocatorRestriction(source, *name); 272 } 273 } 274 }, 275 [&](const parser::Name &name) { 276 CheckPredefinedAllocatorRestriction(source, name); 277 }, 278 }, 279 ompObject.u); 280 } 281 } 282 283 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) { 284 // Simd Construct with Ordered Construct Nesting check 285 // We cannot use CurrentDirectiveIsNested() here because 286 // PushContextAndClauseSets() has not been called yet, it is 287 // called individually for each construct. Therefore a 288 // dirContext_ size `1` means the current construct is nested 289 if (dirContext_.size() >= 1) { 290 if (GetDirectiveNest(SIMDNest) > 0) { 291 CheckSIMDNest(x); 292 } 293 if (GetDirectiveNest(TargetNest) > 0) { 294 CheckTargetNest(x); 295 } 296 } 297 } 298 299 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { 300 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 301 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 302 303 // check matching, End directive is optional 304 if (const auto &endLoopDir{ 305 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) { 306 const auto &endDir{ 307 std::get<parser::OmpLoopDirective>(endLoopDir.value().t)}; 308 309 CheckMatching<parser::OmpLoopDirective>(beginDir, endDir); 310 } 311 312 PushContextAndClauseSets(beginDir.source, beginDir.v); 313 if (llvm::omp::simdSet.test(GetContext().directive)) { 314 EnterDirectiveNest(SIMDNest); 315 } 316 317 if (beginDir.v == llvm::omp::Directive::OMPD_do) { 318 // 2.7.1 do-clause -> private-clause | 319 // firstprivate-clause | 320 // lastprivate-clause | 321 // linear-clause | 322 // reduction-clause | 323 // schedule-clause | 324 // collapse-clause | 325 // ordered-clause 326 327 // nesting check 328 HasInvalidWorksharingNesting( 329 beginDir.source, llvm::omp::nestedWorkshareErrSet); 330 } 331 SetLoopInfo(x); 332 333 if (const auto &doConstruct{ 334 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 335 const auto &doBlock{std::get<parser::Block>(doConstruct->t)}; 336 CheckNoBranching(doBlock, beginDir.v, beginDir.source); 337 } 338 CheckDoWhile(x); 339 CheckLoopItrVariableIsInt(x); 340 CheckCycleConstraints(x); 341 HasInvalidDistributeNesting(x); 342 if (CurrentDirectiveIsNested() && 343 llvm::omp::teamSet.test(GetContextParent().directive)) { 344 HasInvalidTeamsNesting(beginDir.v, beginDir.source); 345 } 346 if ((beginDir.v == llvm::omp::Directive::OMPD_distribute_parallel_do_simd) || 347 (beginDir.v == llvm::omp::Directive::OMPD_distribute_simd)) { 348 CheckDistLinear(x); 349 } 350 } 351 const parser::Name OmpStructureChecker::GetLoopIndex( 352 const parser::DoConstruct *x) { 353 using Bounds = parser::LoopControl::Bounds; 354 return std::get<Bounds>(x->GetLoopControl()->u).name.thing; 355 } 356 void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) { 357 if (const auto &loopConstruct{ 358 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 359 const parser::DoConstruct *loop{&*loopConstruct}; 360 if (loop && loop->IsDoNormal()) { 361 const parser::Name &itrVal{GetLoopIndex(loop)}; 362 SetLoopIv(itrVal.symbol); 363 } 364 } 365 } 366 void OmpStructureChecker::CheckDoWhile(const parser::OpenMPLoopConstruct &x) { 367 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 368 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 369 if (beginDir.v == llvm::omp::Directive::OMPD_do) { 370 if (const auto &doConstruct{ 371 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 372 if (doConstruct.value().IsDoWhile()) { 373 const auto &doStmt{std::get<parser::Statement<parser::NonLabelDoStmt>>( 374 doConstruct.value().t)}; 375 context_.Say(doStmt.source, 376 "The DO loop cannot be a DO WHILE with DO directive."_err_en_US); 377 } 378 } 379 } 380 } 381 382 void OmpStructureChecker::CheckLoopItrVariableIsInt( 383 const parser::OpenMPLoopConstruct &x) { 384 if (const auto &loopConstruct{ 385 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 386 387 for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { 388 if (loop->IsDoNormal()) { 389 const parser::Name &itrVal{GetLoopIndex(loop)}; 390 if (itrVal.symbol) { 391 const auto *type{itrVal.symbol->GetType()}; 392 if (!type->IsNumeric(TypeCategory::Integer)) { 393 context_.Say(itrVal.source, 394 "The DO loop iteration" 395 " variable must be of the type integer."_err_en_US, 396 itrVal.ToString()); 397 } 398 } 399 } 400 // Get the next DoConstruct if block is not empty. 401 const auto &block{std::get<parser::Block>(loop->t)}; 402 const auto it{block.begin()}; 403 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) 404 : nullptr; 405 } 406 } 407 } 408 409 void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) { 410 // Check the following: 411 // The only OpenMP constructs that can be encountered during execution of 412 // a simd region are the `atomic` construct, the `loop` construct, the `simd` 413 // construct and the `ordered` construct with the `simd` clause. 414 // TODO: Expand the check to include `LOOP` construct as well when it is 415 // supported. 416 417 // Check if the parent context has the SIMD clause 418 // Please note that we use GetContext() instead of GetContextParent() 419 // because PushContextAndClauseSets() has not been called on the 420 // current context yet. 421 // TODO: Check for declare simd regions. 422 bool eligibleSIMD{false}; 423 common::visit(Fortran::common::visitors{ 424 // Allow `!$OMP ORDERED SIMD` 425 [&](const parser::OpenMPBlockConstruct &c) { 426 const auto &beginBlockDir{ 427 std::get<parser::OmpBeginBlockDirective>(c.t)}; 428 const auto &beginDir{ 429 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 430 if (beginDir.v == llvm::omp::Directive::OMPD_ordered) { 431 const auto &clauses{ 432 std::get<parser::OmpClauseList>(beginBlockDir.t)}; 433 for (const auto &clause : clauses.v) { 434 if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { 435 eligibleSIMD = true; 436 break; 437 } 438 } 439 } 440 }, 441 [&](const parser::OpenMPSimpleStandaloneConstruct &c) { 442 const auto &dir{ 443 std::get<parser::OmpSimpleStandaloneDirective>(c.t)}; 444 if (dir.v == llvm::omp::Directive::OMPD_ordered) { 445 const auto &clauses{ 446 std::get<parser::OmpClauseList>(c.t)}; 447 for (const auto &clause : clauses.v) { 448 if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { 449 eligibleSIMD = true; 450 break; 451 } 452 } 453 } 454 }, 455 // Allowing SIMD construct 456 [&](const parser::OpenMPLoopConstruct &c) { 457 const auto &beginLoopDir{ 458 std::get<parser::OmpBeginLoopDirective>(c.t)}; 459 const auto &beginDir{ 460 std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 461 if ((beginDir.v == llvm::omp::Directive::OMPD_simd) || 462 (beginDir.v == llvm::omp::Directive::OMPD_do_simd)) { 463 eligibleSIMD = true; 464 } 465 }, 466 [&](const parser::OpenMPAtomicConstruct &c) { 467 // Allow `!$OMP ATOMIC` 468 eligibleSIMD = true; 469 }, 470 [&](const auto &c) {}, 471 }, 472 c.u); 473 if (!eligibleSIMD) { 474 context_.Say(parser::FindSourceLocation(c), 475 "The only OpenMP constructs that can be encountered during execution " 476 "of a 'SIMD'" 477 " region are the `ATOMIC` construct, the `LOOP` construct, the `SIMD`" 478 " construct and the `ORDERED` construct with the `SIMD` clause."_err_en_US); 479 } 480 } 481 482 void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) { 483 // 2.12.5 Target Construct Restriction 484 bool eligibleTarget{true}; 485 llvm::omp::Directive ineligibleTargetDir; 486 common::visit( 487 common::visitors{ 488 [&](const parser::OpenMPBlockConstruct &c) { 489 const auto &beginBlockDir{ 490 std::get<parser::OmpBeginBlockDirective>(c.t)}; 491 const auto &beginDir{ 492 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 493 if (beginDir.v == llvm::omp::Directive::OMPD_target_data) { 494 eligibleTarget = false; 495 ineligibleTargetDir = beginDir.v; 496 } 497 }, 498 [&](const parser::OpenMPStandaloneConstruct &c) { 499 common::visit( 500 common::visitors{ 501 [&](const parser::OpenMPSimpleStandaloneConstruct &c) { 502 const auto &dir{ 503 std::get<parser::OmpSimpleStandaloneDirective>(c.t)}; 504 if (dir.v == llvm::omp::Directive::OMPD_target_update || 505 dir.v == 506 llvm::omp::Directive::OMPD_target_enter_data || 507 dir.v == 508 llvm::omp::Directive::OMPD_target_exit_data) { 509 eligibleTarget = false; 510 ineligibleTargetDir = dir.v; 511 } 512 }, 513 [&](const auto &c) {}, 514 }, 515 c.u); 516 }, 517 [&](const auto &c) {}, 518 }, 519 c.u); 520 if (!eligibleTarget) { 521 context_.Say(parser::FindSourceLocation(c), 522 "If %s directive is nested inside TARGET region, the behaviour " 523 "is unspecified"_port_en_US, 524 parser::ToUpperCaseLetters( 525 getDirectiveName(ineligibleTargetDir).str())); 526 } 527 } 528 529 std::int64_t OmpStructureChecker::GetOrdCollapseLevel( 530 const parser::OpenMPLoopConstruct &x) { 531 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 532 const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; 533 std::int64_t orderedCollapseLevel{1}; 534 std::int64_t orderedLevel{0}; 535 std::int64_t collapseLevel{0}; 536 537 for (const auto &clause : clauseList.v) { 538 if (const auto *collapseClause{ 539 std::get_if<parser::OmpClause::Collapse>(&clause.u)}) { 540 if (const auto v{GetIntValue(collapseClause->v)}) { 541 collapseLevel = *v; 542 } 543 } 544 if (const auto *orderedClause{ 545 std::get_if<parser::OmpClause::Ordered>(&clause.u)}) { 546 if (const auto v{GetIntValue(orderedClause->v)}) { 547 orderedLevel = *v; 548 } 549 } 550 } 551 if (orderedLevel >= collapseLevel) { 552 orderedCollapseLevel = orderedLevel; 553 } else { 554 orderedCollapseLevel = collapseLevel; 555 } 556 return orderedCollapseLevel; 557 } 558 559 void OmpStructureChecker::CheckCycleConstraints( 560 const parser::OpenMPLoopConstruct &x) { 561 std::int64_t ordCollapseLevel{GetOrdCollapseLevel(x)}; 562 OmpCycleChecker ompCycleChecker{context_, ordCollapseLevel}; 563 parser::Walk(x, ompCycleChecker); 564 } 565 566 void OmpStructureChecker::CheckDistLinear( 567 const parser::OpenMPLoopConstruct &x) { 568 569 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 570 const auto &clauses{std::get<parser::OmpClauseList>(beginLoopDir.t)}; 571 572 semantics::UnorderedSymbolSet indexVars; 573 574 // Collect symbols of all the variables from linear clauses 575 for (const auto &clause : clauses.v) { 576 if (const auto *linearClause{ 577 std::get_if<parser::OmpClause::Linear>(&clause.u)}) { 578 579 std::list<parser::Name> values; 580 // Get the variant type 581 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>( 582 linearClause->v.u)) { 583 const auto &withM{ 584 std::get<parser::OmpLinearClause::WithModifier>(linearClause->v.u)}; 585 values = withM.names; 586 } else { 587 const auto &withOutM{std::get<parser::OmpLinearClause::WithoutModifier>( 588 linearClause->v.u)}; 589 values = withOutM.names; 590 } 591 for (auto const &v : values) { 592 indexVars.insert(*(v.symbol)); 593 } 594 } 595 } 596 597 if (!indexVars.empty()) { 598 // Get collapse level, if given, to find which loops are "associated." 599 std::int64_t collapseVal{GetOrdCollapseLevel(x)}; 600 // Include the top loop if no collapse is specified 601 if (collapseVal == 0) { 602 collapseVal = 1; 603 } 604 605 // Match the loop index variables with the collected symbols from linear 606 // clauses. 607 if (const auto &loopConstruct{ 608 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 609 for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { 610 if (loop->IsDoNormal()) { 611 const parser::Name &itrVal{GetLoopIndex(loop)}; 612 if (itrVal.symbol) { 613 // Remove the symbol from the collcted set 614 indexVars.erase(*(itrVal.symbol)); 615 } 616 collapseVal--; 617 if (collapseVal == 0) { 618 break; 619 } 620 } 621 // Get the next DoConstruct if block is not empty. 622 const auto &block{std::get<parser::Block>(loop->t)}; 623 const auto it{block.begin()}; 624 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) 625 : nullptr; 626 } 627 } 628 629 // Show error for the remaining variables 630 for (auto var : indexVars) { 631 const Symbol &root{GetAssociationRoot(var)}; 632 context_.Say(parser::FindSourceLocation(x), 633 "Variable '%s' not allowed in `LINEAR` clause, only loop iterator can be specified in `LINEAR` clause of a construct combined with `DISTRIBUTE`"_err_en_US, 634 root.name()); 635 } 636 } 637 } 638 639 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) { 640 if (llvm::omp::simdSet.test(GetContext().directive)) { 641 ExitDirectiveNest(SIMDNest); 642 } 643 dirContext_.pop_back(); 644 } 645 646 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) { 647 const auto &dir{std::get<parser::OmpLoopDirective>(x.t)}; 648 ResetPartialContext(dir.source); 649 switch (dir.v) { 650 // 2.7.1 end-do -> END DO [nowait-clause] 651 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause] 652 case llvm::omp::Directive::OMPD_do: 653 case llvm::omp::Directive::OMPD_do_simd: 654 SetClauseSets(dir.v); 655 break; 656 default: 657 // no clauses are allowed 658 break; 659 } 660 } 661 662 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { 663 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; 664 const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)}; 665 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 666 const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)}; 667 const parser::Block &block{std::get<parser::Block>(x.t)}; 668 669 CheckMatching<parser::OmpBlockDirective>(beginDir, endDir); 670 671 PushContextAndClauseSets(beginDir.source, beginDir.v); 672 if (GetContext().directive == llvm::omp::Directive::OMPD_target) { 673 EnterDirectiveNest(TargetNest); 674 } 675 676 if (CurrentDirectiveIsNested()) { 677 if (llvm::omp::teamSet.test(GetContextParent().directive)) { 678 HasInvalidTeamsNesting(beginDir.v, beginDir.source); 679 } 680 if (GetContext().directive == llvm::omp::Directive::OMPD_master) { 681 CheckMasterNesting(x); 682 } 683 // A teams region can only be strictly nested within the implicit parallel 684 // region or a target region. 685 if (GetContext().directive == llvm::omp::Directive::OMPD_teams && 686 GetContextParent().directive != llvm::omp::Directive::OMPD_target) { 687 context_.Say(parser::FindSourceLocation(x), 688 "%s region can only be strictly nested within the implicit parallel " 689 "region or TARGET region"_err_en_US, 690 ContextDirectiveAsFortran()); 691 } 692 // If a teams construct is nested within a target construct, that target 693 // construct must contain no statements, declarations or directives outside 694 // of the teams construct. 695 if (GetContext().directive == llvm::omp::Directive::OMPD_teams && 696 GetContextParent().directive == llvm::omp::Directive::OMPD_target && 697 !GetDirectiveNest(TargetBlockOnlyTeams)) { 698 context_.Say(GetContextParent().directiveSource, 699 "TARGET construct with nested TEAMS region contains statements or " 700 "directives outside of the TEAMS construct"_err_en_US); 701 } 702 } 703 704 CheckNoBranching(block, beginDir.v, beginDir.source); 705 706 switch (beginDir.v) { 707 case llvm::omp::Directive::OMPD_target: 708 if (CheckTargetBlockOnlyTeams(block)) { 709 EnterDirectiveNest(TargetBlockOnlyTeams); 710 } 711 break; 712 case llvm::omp::OMPD_workshare: 713 case llvm::omp::OMPD_parallel_workshare: 714 CheckWorkshareBlockStmts(block, beginDir.source); 715 HasInvalidWorksharingNesting( 716 beginDir.source, llvm::omp::nestedWorkshareErrSet); 717 break; 718 case llvm::omp::Directive::OMPD_single: 719 // TODO: This check needs to be extended while implementing nesting of 720 // regions checks. 721 HasInvalidWorksharingNesting( 722 beginDir.source, llvm::omp::nestedWorkshareErrSet); 723 break; 724 default: 725 break; 726 } 727 } 728 729 void OmpStructureChecker::CheckMasterNesting( 730 const parser::OpenMPBlockConstruct &x) { 731 // A MASTER region may not be `closely nested` inside a worksharing, loop, 732 // task, taskloop, or atomic region. 733 // TODO: Expand the check to include `LOOP` construct as well when it is 734 // supported. 735 if (IsCloselyNestedRegion(llvm::omp::nestedMasterErrSet)) { 736 context_.Say(parser::FindSourceLocation(x), 737 "`MASTER` region may not be closely nested inside of `WORKSHARING`, " 738 "`LOOP`, `TASK`, `TASKLOOP`," 739 " or `ATOMIC` region."_err_en_US); 740 } 741 } 742 743 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) { 744 if (GetDirectiveNest(TargetBlockOnlyTeams)) { 745 ExitDirectiveNest(TargetBlockOnlyTeams); 746 } 747 if (GetContext().directive == llvm::omp::Directive::OMPD_target) { 748 ExitDirectiveNest(TargetNest); 749 } 750 dirContext_.pop_back(); 751 } 752 753 void OmpStructureChecker::ChecksOnOrderedAsBlock() { 754 if (FindClause(llvm::omp::Clause::OMPC_depend)) { 755 context_.Say(GetContext().clauseSource, 756 "DEPEND(*) clauses are not allowed when ORDERED construct is a block" 757 " construct with an ORDERED region"_err_en_US); 758 return; 759 } 760 761 OmpDirectiveSet notAllowedParallelSet{llvm::omp::Directive::OMPD_parallel, 762 llvm::omp::Directive::OMPD_target_parallel, 763 llvm::omp::Directive::OMPD_parallel_sections, 764 llvm::omp::Directive::OMPD_parallel_workshare}; 765 bool isNestedInDo{false}; 766 bool isNestedInDoSIMD{false}; 767 bool isNestedInSIMD{false}; 768 bool noOrderedClause{false}; 769 bool isOrderedClauseWithPara{false}; 770 bool isCloselyNestedRegion{true}; 771 if (CurrentDirectiveIsNested()) { 772 for (int i = (int)dirContext_.size() - 2; i >= 0; i--) { 773 if (llvm::omp::nestedOrderedErrSet.test(dirContext_[i].directive)) { 774 context_.Say(GetContext().directiveSource, 775 "`ORDERED` region may not be closely nested inside of `CRITICAL`, " 776 "`ORDERED`, explicit `TASK` or `TASKLOOP` region."_err_en_US); 777 break; 778 } else if (llvm::omp::doSet.test(dirContext_[i].directive)) { 779 isNestedInDo = true; 780 isNestedInDoSIMD = llvm::omp::doSimdSet.test(dirContext_[i].directive); 781 if (const auto *clause{ 782 FindClause(dirContext_[i], llvm::omp::Clause::OMPC_ordered)}) { 783 const auto &orderedClause{ 784 std::get<parser::OmpClause::Ordered>(clause->u)}; 785 const auto orderedValue{GetIntValue(orderedClause.v)}; 786 isOrderedClauseWithPara = orderedValue > 0; 787 } else { 788 noOrderedClause = true; 789 } 790 break; 791 } else if (llvm::omp::simdSet.test(dirContext_[i].directive)) { 792 isNestedInSIMD = true; 793 break; 794 } else if (notAllowedParallelSet.test(dirContext_[i].directive)) { 795 isCloselyNestedRegion = false; 796 break; 797 } 798 } 799 } 800 801 if (!isCloselyNestedRegion) { 802 context_.Say(GetContext().directiveSource, 803 "An ORDERED directive without the DEPEND clause must be closely nested " 804 "in a SIMD, worksharing-loop, or worksharing-loop SIMD " 805 "region"_err_en_US); 806 } else { 807 if (CurrentDirectiveIsNested() && 808 FindClause(llvm::omp::Clause::OMPC_simd) && 809 (!isNestedInDoSIMD && !isNestedInSIMD)) { 810 context_.Say(GetContext().directiveSource, 811 "An ORDERED directive with SIMD clause must be closely nested in a " 812 "SIMD or worksharing-loop SIMD region"_err_en_US); 813 } 814 if (isNestedInDo && (noOrderedClause || isOrderedClauseWithPara)) { 815 context_.Say(GetContext().directiveSource, 816 "An ORDERED directive without the DEPEND clause must be closely " 817 "nested in a worksharing-loop (or worksharing-loop SIMD) region with " 818 "ORDERED clause without the parameter"_err_en_US); 819 } 820 } 821 } 822 823 void OmpStructureChecker::Leave(const parser::OmpBeginBlockDirective &) { 824 switch (GetContext().directive) { 825 case llvm::omp::Directive::OMPD_ordered: 826 // [5.1] 2.19.9 Ordered Construct Restriction 827 ChecksOnOrderedAsBlock(); 828 break; 829 default: 830 break; 831 } 832 } 833 834 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) { 835 const auto &beginSectionsDir{ 836 std::get<parser::OmpBeginSectionsDirective>(x.t)}; 837 const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)}; 838 const auto &beginDir{ 839 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 840 const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)}; 841 CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir); 842 843 PushContextAndClauseSets(beginDir.source, beginDir.v); 844 const auto §ionBlocks{std::get<parser::OmpSectionBlocks>(x.t)}; 845 for (const parser::OpenMPConstruct &block : sectionBlocks.v) { 846 CheckNoBranching(std::get<parser::OpenMPSectionConstruct>(block.u).v, 847 beginDir.v, beginDir.source); 848 } 849 HasInvalidWorksharingNesting( 850 beginDir.source, llvm::omp::nestedWorkshareErrSet); 851 } 852 853 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) { 854 dirContext_.pop_back(); 855 } 856 857 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) { 858 const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)}; 859 ResetPartialContext(dir.source); 860 switch (dir.v) { 861 // 2.7.2 end-sections -> END SECTIONS [nowait-clause] 862 case llvm::omp::Directive::OMPD_sections: 863 PushContextAndClauseSets( 864 dir.source, llvm::omp::Directive::OMPD_end_sections); 865 break; 866 default: 867 // no clauses are allowed 868 break; 869 } 870 } 871 872 // TODO: Verify the popping of dirContext requirement after nowait 873 // implementation, as there is an implicit barrier at the end of the worksharing 874 // constructs unless a nowait clause is specified. Only OMPD_end_sections is 875 // popped becuase it is pushed while entering the EndSectionsDirective. 876 void OmpStructureChecker::Leave(const parser::OmpEndSectionsDirective &x) { 877 if (GetContext().directive == llvm::omp::Directive::OMPD_end_sections) { 878 dirContext_.pop_back(); 879 } 880 } 881 882 void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar( 883 const parser::OmpObjectList &objList) { 884 for (const auto &ompObject : objList.v) { 885 common::visit( 886 common::visitors{ 887 [&](const parser::Designator &) { 888 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 889 const auto &declScope{ 890 GetProgramUnitContaining(name->symbol->GetUltimate())}; 891 const auto *sym = 892 declScope.parent().FindSymbol(name->symbol->name()); 893 if (sym && 894 (sym->has<MainProgramDetails>() || 895 sym->has<ModuleDetails>())) { 896 context_.Say(name->source, 897 "The module name or main program name cannot be in a %s " 898 "directive"_err_en_US, 899 ContextDirectiveAsFortran()); 900 } else if (name->symbol->GetUltimate().IsSubprogram()) { 901 if (GetContext().directive == 902 llvm::omp::Directive::OMPD_threadprivate) 903 context_.Say(name->source, 904 "The procedure name cannot be in a %s " 905 "directive"_err_en_US, 906 ContextDirectiveAsFortran()); 907 // TODO: Check for procedure name in declare target directive. 908 } else if (name->symbol->attrs().test(Attr::PARAMETER)) { 909 if (GetContext().directive == 910 llvm::omp::Directive::OMPD_threadprivate) 911 context_.Say(name->source, 912 "The entity with PARAMETER attribute cannot be in a %s " 913 "directive"_err_en_US, 914 ContextDirectiveAsFortran()); 915 else if (GetContext().directive == 916 llvm::omp::Directive::OMPD_declare_target) 917 context_.Say(name->source, 918 "The entity with PARAMETER attribute is used in a %s " 919 "directive"_warn_en_US, 920 ContextDirectiveAsFortran()); 921 } else if (FindCommonBlockContaining(*name->symbol)) { 922 context_.Say(name->source, 923 "A variable in a %s directive cannot be an element of a " 924 "common block"_err_en_US, 925 ContextDirectiveAsFortran()); 926 } else if (!IsSave(*name->symbol) && 927 declScope.kind() != Scope::Kind::MainProgram && 928 declScope.kind() != Scope::Kind::Module) { 929 context_.Say(name->source, 930 "A variable that appears in a %s directive must be " 931 "declared in the scope of a module or have the SAVE " 932 "attribute, either explicitly or implicitly"_err_en_US, 933 ContextDirectiveAsFortran()); 934 } else if (FindEquivalenceSet(*name->symbol)) { 935 context_.Say(name->source, 936 "A variable in a %s directive cannot appear in an " 937 "EQUIVALENCE statement"_err_en_US, 938 ContextDirectiveAsFortran()); 939 } else if (name->symbol->test(Symbol::Flag::OmpThreadprivate) && 940 GetContext().directive == 941 llvm::omp::Directive::OMPD_declare_target) { 942 context_.Say(name->source, 943 "A THREADPRIVATE variable cannot appear in a %s " 944 "directive"_err_en_US, 945 ContextDirectiveAsFortran()); 946 } 947 } 948 }, 949 [&](const parser::Name &) {}, // common block 950 }, 951 ompObject.u); 952 } 953 } 954 955 void OmpStructureChecker::Enter(const parser::OpenMPThreadprivate &c) { 956 const auto &dir{std::get<parser::Verbatim>(c.t)}; 957 PushContextAndClauseSets( 958 dir.source, llvm::omp::Directive::OMPD_threadprivate); 959 } 960 961 void OmpStructureChecker::Leave(const parser::OpenMPThreadprivate &c) { 962 const auto &dir{std::get<parser::Verbatim>(c.t)}; 963 const auto &objectList{std::get<parser::OmpObjectList>(c.t)}; 964 CheckIsVarPartOfAnotherVar(dir.source, objectList); 965 CheckThreadprivateOrDeclareTargetVar(objectList); 966 dirContext_.pop_back(); 967 } 968 969 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) { 970 const auto &dir{std::get<parser::Verbatim>(x.t)}; 971 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd); 972 } 973 974 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) { 975 dirContext_.pop_back(); 976 } 977 978 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) { 979 isPredefinedAllocator = true; 980 const auto &dir{std::get<parser::Verbatim>(x.t)}; 981 const auto &objectList{std::get<parser::OmpObjectList>(x.t)}; 982 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); 983 CheckIsVarPartOfAnotherVar(dir.source, objectList); 984 } 985 986 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) { 987 const auto &dir{std::get<parser::Verbatim>(x.t)}; 988 const auto &objectList{std::get<parser::OmpObjectList>(x.t)}; 989 CheckPredefinedAllocatorRestriction(dir.source, objectList); 990 dirContext_.pop_back(); 991 } 992 993 void OmpStructureChecker::Enter(const parser::OmpClause::Allocator &x) { 994 CheckAllowed(llvm::omp::Clause::OMPC_allocator); 995 // Note: Predefined allocators are stored in ScalarExpr as numbers 996 // whereas custom allocators are stored as strings, so if the ScalarExpr 997 // actually has an int value, then it must be a predefined allocator 998 isPredefinedAllocator = GetIntValue(x.v).has_value(); 999 RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator, x.v); 1000 } 1001 1002 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) { 1003 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1004 PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target); 1005 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)}; 1006 if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) { 1007 SetClauseSets(llvm::omp::Directive::OMPD_declare_target); 1008 } 1009 } 1010 1011 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) { 1012 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1013 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)}; 1014 if (const auto *objectList{parser::Unwrap<parser::OmpObjectList>(spec.u)}) { 1015 CheckIsVarPartOfAnotherVar(dir.source, *objectList); 1016 CheckThreadprivateOrDeclareTargetVar(*objectList); 1017 } else if (const auto *clauseList{ 1018 parser::Unwrap<parser::OmpClauseList>(spec.u)}) { 1019 for (const auto &clause : clauseList->v) { 1020 if (const auto *toClause{std::get_if<parser::OmpClause::To>(&clause.u)}) { 1021 CheckIsVarPartOfAnotherVar(dir.source, toClause->v); 1022 CheckThreadprivateOrDeclareTargetVar(toClause->v); 1023 } else if (const auto *linkClause{ 1024 std::get_if<parser::OmpClause::Link>(&clause.u)}) { 1025 CheckIsVarPartOfAnotherVar(dir.source, linkClause->v); 1026 CheckThreadprivateOrDeclareTargetVar(linkClause->v); 1027 } 1028 } 1029 } 1030 dirContext_.pop_back(); 1031 } 1032 1033 void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) { 1034 isPredefinedAllocator = true; 1035 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1036 const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)}; 1037 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); 1038 if (objectList) { 1039 CheckIsVarPartOfAnotherVar(dir.source, *objectList); 1040 } 1041 } 1042 1043 void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) { 1044 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1045 const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)}; 1046 if (objectList) 1047 CheckPredefinedAllocatorRestriction(dir.source, *objectList); 1048 dirContext_.pop_back(); 1049 } 1050 1051 void OmpStructureChecker::CheckBarrierNesting( 1052 const parser::OpenMPSimpleStandaloneConstruct &x) { 1053 // A barrier region may not be `closely nested` inside a worksharing, loop, 1054 // task, taskloop, critical, ordered, atomic, or master region. 1055 // TODO: Expand the check to include `LOOP` construct as well when it is 1056 // supported. 1057 if (GetContext().directive == llvm::omp::Directive::OMPD_barrier) { 1058 if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet)) { 1059 context_.Say(parser::FindSourceLocation(x), 1060 "`BARRIER` region may not be closely nested inside of `WORKSHARING`, " 1061 "`LOOP`, `TASK`, `TASKLOOP`," 1062 "`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US); 1063 } 1064 } 1065 } 1066 1067 void OmpStructureChecker::ChecksOnOrderedAsStandalone() { 1068 if (FindClause(llvm::omp::Clause::OMPC_threads) || 1069 FindClause(llvm::omp::Clause::OMPC_simd)) { 1070 context_.Say(GetContext().clauseSource, 1071 "THREADS, SIMD clauses are not allowed when ORDERED construct is a " 1072 "standalone construct with no ORDERED region"_err_en_US); 1073 } 1074 1075 bool isSinkPresent{false}; 1076 int dependSourceCount{0}; 1077 auto clauseAll = FindClauses(llvm::omp::Clause::OMPC_depend); 1078 for (auto itr = clauseAll.first; itr != clauseAll.second; ++itr) { 1079 const auto &dependClause{ 1080 std::get<parser::OmpClause::Depend>(itr->second->u)}; 1081 if (std::get_if<parser::OmpDependClause::Source>(&dependClause.v.u)) { 1082 dependSourceCount++; 1083 if (isSinkPresent) { 1084 context_.Say(itr->second->source, 1085 "DEPEND(SOURCE) is not allowed when DEPEND(SINK: vec) is present " 1086 "on ORDERED directive"_err_en_US); 1087 } 1088 if (dependSourceCount > 1) { 1089 context_.Say(itr->second->source, 1090 "At most one DEPEND(SOURCE) clause can appear on the ORDERED " 1091 "directive"_err_en_US); 1092 } 1093 } else if (std::get_if<parser::OmpDependClause::Sink>(&dependClause.v.u)) { 1094 isSinkPresent = true; 1095 if (dependSourceCount > 0) { 1096 context_.Say(itr->second->source, 1097 "DEPEND(SINK: vec) is not allowed when DEPEND(SOURCE) is present " 1098 "on ORDERED directive"_err_en_US); 1099 } 1100 } else { 1101 context_.Say(itr->second->source, 1102 "Only DEPEND(SOURCE) or DEPEND(SINK: vec) are allowed when ORDERED " 1103 "construct is a standalone construct with no ORDERED " 1104 "region"_err_en_US); 1105 } 1106 } 1107 1108 OmpDirectiveSet allowedDoSet{llvm::omp::Directive::OMPD_do, 1109 llvm::omp::Directive::OMPD_parallel_do, 1110 llvm::omp::Directive::OMPD_target_parallel_do}; 1111 bool isNestedInDoOrderedWithPara{false}; 1112 if (CurrentDirectiveIsNested() && 1113 allowedDoSet.test(GetContextParent().directive)) { 1114 if (const auto *clause{ 1115 FindClause(GetContextParent(), llvm::omp::Clause::OMPC_ordered)}) { 1116 const auto &orderedClause{ 1117 std::get<parser::OmpClause::Ordered>(clause->u)}; 1118 const auto orderedValue{GetIntValue(orderedClause.v)}; 1119 if (orderedValue > 0) { 1120 isNestedInDoOrderedWithPara = true; 1121 CheckOrderedDependClause(orderedValue); 1122 } 1123 } 1124 } 1125 1126 if (FindClause(llvm::omp::Clause::OMPC_depend) && 1127 !isNestedInDoOrderedWithPara) { 1128 context_.Say(GetContext().clauseSource, 1129 "An ORDERED construct with the DEPEND clause must be closely nested " 1130 "in a worksharing-loop (or parallel worksharing-loop) construct with " 1131 "ORDERED clause with a parameter"_err_en_US); 1132 } 1133 } 1134 1135 void OmpStructureChecker::CheckOrderedDependClause( 1136 std::optional<std::int64_t> orderedValue) { 1137 auto clauseAll{FindClauses(llvm::omp::Clause::OMPC_depend)}; 1138 for (auto itr = clauseAll.first; itr != clauseAll.second; ++itr) { 1139 const auto &dependClause{ 1140 std::get<parser::OmpClause::Depend>(itr->second->u)}; 1141 if (const auto *sinkVectors{ 1142 std::get_if<parser::OmpDependClause::Sink>(&dependClause.v.u)}) { 1143 std::int64_t numVar = sinkVectors->v.size(); 1144 if (orderedValue != numVar) { 1145 context_.Say(itr->second->source, 1146 "The number of variables in DEPEND(SINK: vec) clause does not " 1147 "match the parameter specified in ORDERED clause"_err_en_US); 1148 } 1149 } 1150 } 1151 } 1152 1153 void OmpStructureChecker::Enter( 1154 const parser::OpenMPSimpleStandaloneConstruct &x) { 1155 const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)}; 1156 PushContextAndClauseSets(dir.source, dir.v); 1157 CheckBarrierNesting(x); 1158 } 1159 1160 void OmpStructureChecker::Leave( 1161 const parser::OpenMPSimpleStandaloneConstruct &) { 1162 switch (GetContext().directive) { 1163 case llvm::omp::Directive::OMPD_ordered: 1164 // [5.1] 2.19.9 Ordered Construct Restriction 1165 ChecksOnOrderedAsStandalone(); 1166 break; 1167 default: 1168 break; 1169 } 1170 dirContext_.pop_back(); 1171 } 1172 1173 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) { 1174 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1175 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_flush); 1176 } 1177 1178 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) { 1179 if (FindClause(llvm::omp::Clause::OMPC_acquire) || 1180 FindClause(llvm::omp::Clause::OMPC_release) || 1181 FindClause(llvm::omp::Clause::OMPC_acq_rel)) { 1182 if (const auto &flushList{ 1183 std::get<std::optional<parser::OmpObjectList>>(x.t)}) { 1184 context_.Say(parser::FindSourceLocation(flushList), 1185 "If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items " 1186 "must not be specified on the FLUSH directive"_err_en_US); 1187 } 1188 } 1189 dirContext_.pop_back(); 1190 } 1191 1192 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) { 1193 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1194 const auto &type{std::get<parser::OmpCancelType>(x.t)}; 1195 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel); 1196 CheckCancellationNest(dir.source, type.v); 1197 } 1198 1199 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) { 1200 dirContext_.pop_back(); 1201 } 1202 1203 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) { 1204 const auto &dir{std::get<parser::OmpCriticalDirective>(x.t)}; 1205 const auto &endDir{std::get<parser::OmpEndCriticalDirective>(x.t)}; 1206 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_critical); 1207 const auto &block{std::get<parser::Block>(x.t)}; 1208 CheckNoBranching(block, llvm::omp::Directive::OMPD_critical, dir.source); 1209 const auto &dirName{std::get<std::optional<parser::Name>>(dir.t)}; 1210 const auto &endDirName{std::get<std::optional<parser::Name>>(endDir.t)}; 1211 const auto &ompClause{std::get<parser::OmpClauseList>(dir.t)}; 1212 if (dirName && endDirName && 1213 dirName->ToString().compare(endDirName->ToString())) { 1214 context_ 1215 .Say(endDirName->source, 1216 parser::MessageFormattedText{ 1217 "CRITICAL directive names do not match"_err_en_US}) 1218 .Attach(dirName->source, "should be "_en_US); 1219 } else if (dirName && !endDirName) { 1220 context_ 1221 .Say(dirName->source, 1222 parser::MessageFormattedText{ 1223 "CRITICAL directive names do not match"_err_en_US}) 1224 .Attach(dirName->source, "should be NULL"_en_US); 1225 } else if (!dirName && endDirName) { 1226 context_ 1227 .Say(endDirName->source, 1228 parser::MessageFormattedText{ 1229 "CRITICAL directive names do not match"_err_en_US}) 1230 .Attach(endDirName->source, "should be NULL"_en_US); 1231 } 1232 if (!dirName && !ompClause.source.empty() && 1233 ompClause.source.NULTerminatedToString() != "hint(omp_sync_hint_none)") { 1234 context_.Say(dir.source, 1235 parser::MessageFormattedText{ 1236 "Hint clause other than omp_sync_hint_none cannot be specified for an unnamed CRITICAL directive"_err_en_US}); 1237 } 1238 } 1239 1240 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) { 1241 dirContext_.pop_back(); 1242 } 1243 1244 void OmpStructureChecker::Enter( 1245 const parser::OpenMPCancellationPointConstruct &x) { 1246 const auto &dir{std::get<parser::Verbatim>(x.t)}; 1247 const auto &type{std::get<parser::OmpCancelType>(x.t)}; 1248 PushContextAndClauseSets( 1249 dir.source, llvm::omp::Directive::OMPD_cancellation_point); 1250 CheckCancellationNest(dir.source, type.v); 1251 } 1252 1253 void OmpStructureChecker::Leave( 1254 const parser::OpenMPCancellationPointConstruct &) { 1255 dirContext_.pop_back(); 1256 } 1257 1258 void OmpStructureChecker::CheckCancellationNest( 1259 const parser::CharBlock &source, const parser::OmpCancelType::Type &type) { 1260 if (CurrentDirectiveIsNested()) { 1261 // If construct-type-clause is taskgroup, the cancellation construct must be 1262 // closely nested inside a task or a taskloop construct and the cancellation 1263 // region must be closely nested inside a taskgroup region. If 1264 // construct-type-clause is sections, the cancellation construct must be 1265 // closely nested inside a sections or section construct. Otherwise, the 1266 // cancellation construct must be closely nested inside an OpenMP construct 1267 // that matches the type specified in construct-type-clause of the 1268 // cancellation construct. 1269 1270 OmpDirectiveSet allowedTaskgroupSet{ 1271 llvm::omp::Directive::OMPD_task, llvm::omp::Directive::OMPD_taskloop}; 1272 OmpDirectiveSet allowedSectionsSet{llvm::omp::Directive::OMPD_sections, 1273 llvm::omp::Directive::OMPD_parallel_sections}; 1274 OmpDirectiveSet allowedDoSet{llvm::omp::Directive::OMPD_do, 1275 llvm::omp::Directive::OMPD_distribute_parallel_do, 1276 llvm::omp::Directive::OMPD_parallel_do, 1277 llvm::omp::Directive::OMPD_target_parallel_do, 1278 llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do, 1279 llvm::omp::Directive::OMPD_teams_distribute_parallel_do}; 1280 OmpDirectiveSet allowedParallelSet{llvm::omp::Directive::OMPD_parallel, 1281 llvm::omp::Directive::OMPD_target_parallel}; 1282 1283 bool eligibleCancellation{false}; 1284 switch (type) { 1285 case parser::OmpCancelType::Type::Taskgroup: 1286 if (allowedTaskgroupSet.test(GetContextParent().directive)) { 1287 eligibleCancellation = true; 1288 if (dirContext_.size() >= 3) { 1289 // Check if the cancellation region is closely nested inside a 1290 // taskgroup region when there are more than two levels of directives 1291 // in the directive context stack. 1292 if (GetContextParent().directive == llvm::omp::Directive::OMPD_task || 1293 FindClauseParent(llvm::omp::Clause::OMPC_nogroup)) { 1294 for (int i = dirContext_.size() - 3; i >= 0; i--) { 1295 if (dirContext_[i].directive == 1296 llvm::omp::Directive::OMPD_taskgroup) { 1297 break; 1298 } 1299 if (allowedParallelSet.test(dirContext_[i].directive)) { 1300 eligibleCancellation = false; 1301 break; 1302 } 1303 } 1304 } 1305 } 1306 } 1307 if (!eligibleCancellation) { 1308 context_.Say(source, 1309 "With %s clause, %s construct must be closely nested inside TASK " 1310 "or TASKLOOP construct and %s region must be closely nested inside " 1311 "TASKGROUP region"_err_en_US, 1312 parser::ToUpperCaseLetters( 1313 parser::OmpCancelType::EnumToString(type)), 1314 ContextDirectiveAsFortran(), ContextDirectiveAsFortran()); 1315 } 1316 return; 1317 case parser::OmpCancelType::Type::Sections: 1318 if (allowedSectionsSet.test(GetContextParent().directive)) { 1319 eligibleCancellation = true; 1320 } 1321 break; 1322 case Fortran::parser::OmpCancelType::Type::Do: 1323 if (allowedDoSet.test(GetContextParent().directive)) { 1324 eligibleCancellation = true; 1325 } 1326 break; 1327 case parser::OmpCancelType::Type::Parallel: 1328 if (allowedParallelSet.test(GetContextParent().directive)) { 1329 eligibleCancellation = true; 1330 } 1331 break; 1332 } 1333 if (!eligibleCancellation) { 1334 context_.Say(source, 1335 "With %s clause, %s construct cannot be closely nested inside %s " 1336 "construct"_err_en_US, 1337 parser::ToUpperCaseLetters(parser::OmpCancelType::EnumToString(type)), 1338 ContextDirectiveAsFortran(), 1339 parser::ToUpperCaseLetters( 1340 getDirectiveName(GetContextParent().directive).str())); 1341 } 1342 } else { 1343 // The cancellation directive cannot be orphaned. 1344 switch (type) { 1345 case parser::OmpCancelType::Type::Taskgroup: 1346 context_.Say(source, 1347 "%s %s directive is not closely nested inside " 1348 "TASK or TASKLOOP"_err_en_US, 1349 ContextDirectiveAsFortran(), 1350 parser::ToUpperCaseLetters( 1351 parser::OmpCancelType::EnumToString(type))); 1352 break; 1353 case parser::OmpCancelType::Type::Sections: 1354 context_.Say(source, 1355 "%s %s directive is not closely nested inside " 1356 "SECTION or SECTIONS"_err_en_US, 1357 ContextDirectiveAsFortran(), 1358 parser::ToUpperCaseLetters( 1359 parser::OmpCancelType::EnumToString(type))); 1360 break; 1361 case Fortran::parser::OmpCancelType::Type::Do: 1362 context_.Say(source, 1363 "%s %s directive is not closely nested inside " 1364 "the construct that matches the DO clause type"_err_en_US, 1365 ContextDirectiveAsFortran(), 1366 parser::ToUpperCaseLetters( 1367 parser::OmpCancelType::EnumToString(type))); 1368 break; 1369 case parser::OmpCancelType::Type::Parallel: 1370 context_.Say(source, 1371 "%s %s directive is not closely nested inside " 1372 "the construct that matches the PARALLEL clause type"_err_en_US, 1373 ContextDirectiveAsFortran(), 1374 parser::ToUpperCaseLetters( 1375 parser::OmpCancelType::EnumToString(type))); 1376 break; 1377 } 1378 } 1379 } 1380 1381 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) { 1382 const auto &dir{std::get<parser::OmpBlockDirective>(x.t)}; 1383 ResetPartialContext(dir.source); 1384 switch (dir.v) { 1385 // 2.7.3 end-single-clause -> copyprivate-clause | 1386 // nowait-clause 1387 case llvm::omp::Directive::OMPD_single: 1388 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_single); 1389 break; 1390 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause] 1391 case llvm::omp::Directive::OMPD_workshare: 1392 PushContextAndClauseSets( 1393 dir.source, llvm::omp::Directive::OMPD_end_workshare); 1394 break; 1395 default: 1396 // no clauses are allowed 1397 break; 1398 } 1399 } 1400 1401 // TODO: Verify the popping of dirContext requirement after nowait 1402 // implementation, as there is an implicit barrier at the end of the worksharing 1403 // constructs unless a nowait clause is specified. Only OMPD_end_single and 1404 // end_workshareare popped as they are pushed while entering the 1405 // EndBlockDirective. 1406 void OmpStructureChecker::Leave(const parser::OmpEndBlockDirective &x) { 1407 if ((GetContext().directive == llvm::omp::Directive::OMPD_end_single) || 1408 (GetContext().directive == llvm::omp::Directive::OMPD_end_workshare)) { 1409 dirContext_.pop_back(); 1410 } 1411 } 1412 1413 template <typename T, typename D> 1414 bool OmpStructureChecker::IsOperatorValid(const T &node, const D &variable) { 1415 using AllowedBinaryOperators = 1416 std::variant<parser::Expr::Add, parser::Expr::Multiply, 1417 parser::Expr::Subtract, parser::Expr::Divide, parser::Expr::AND, 1418 parser::Expr::OR, parser::Expr::EQV, parser::Expr::NEQV>; 1419 using BinaryOperators = std::variant<parser::Expr::Add, 1420 parser::Expr::Multiply, parser::Expr::Subtract, parser::Expr::Divide, 1421 parser::Expr::AND, parser::Expr::OR, parser::Expr::EQV, 1422 parser::Expr::NEQV, parser::Expr::Power, parser::Expr::Concat, 1423 parser::Expr::LT, parser::Expr::LE, parser::Expr::EQ, parser::Expr::NE, 1424 parser::Expr::GE, parser::Expr::GT>; 1425 1426 if constexpr (common::HasMember<T, BinaryOperators>) { 1427 const auto &variableName{variable.GetSource().ToString()}; 1428 const auto &exprLeft{std::get<0>(node.t)}; 1429 const auto &exprRight{std::get<1>(node.t)}; 1430 if ((exprLeft.value().source.ToString() != variableName) && 1431 (exprRight.value().source.ToString() != variableName)) { 1432 context_.Say(variable.GetSource(), 1433 "Atomic update variable '%s' not found in the RHS of the " 1434 "assignment statement in an ATOMIC (UPDATE) construct"_err_en_US, 1435 variableName); 1436 } 1437 return common::HasMember<T, AllowedBinaryOperators>; 1438 } 1439 return true; 1440 } 1441 1442 void OmpStructureChecker::CheckAtomicUpdateAssignmentStmt( 1443 const parser::AssignmentStmt &assignment) { 1444 const auto &expr{std::get<parser::Expr>(assignment.t)}; 1445 const auto &var{std::get<parser::Variable>(assignment.t)}; 1446 common::visit( 1447 common::visitors{ 1448 [&](const common::Indirection<parser::FunctionReference> &x) { 1449 const auto &procedureDesignator{ 1450 std::get<parser::ProcedureDesignator>(x.value().v.t)}; 1451 const parser::Name *name{ 1452 std::get_if<parser::Name>(&procedureDesignator.u)}; 1453 if (name && 1454 !(name->source == "max" || name->source == "min" || 1455 name->source == "iand" || name->source == "ior" || 1456 name->source == "ieor")) { 1457 context_.Say(expr.source, 1458 "Invalid intrinsic procedure name in " 1459 "OpenMP ATOMIC (UPDATE) statement"_err_en_US); 1460 } else if (name) { 1461 bool foundMatch{false}; 1462 if (auto varDesignatorIndirection = 1463 std::get_if<Fortran::common::Indirection< 1464 Fortran::parser::Designator>>(&var.u)) { 1465 const auto &varDesignator = varDesignatorIndirection->value(); 1466 if (const auto *dataRef = std::get_if<Fortran::parser::DataRef>( 1467 &varDesignator.u)) { 1468 if (const auto *name = 1469 std::get_if<Fortran::parser::Name>(&dataRef->u)) { 1470 const auto &varSymbol = *name->symbol; 1471 if (const auto *e{GetExpr(context_, expr)}) { 1472 for (const Symbol &symbol : 1473 evaluate::CollectSymbols(*e)) { 1474 if (symbol == varSymbol) { 1475 foundMatch = true; 1476 break; 1477 } 1478 } 1479 } 1480 } 1481 } 1482 } 1483 if (!foundMatch) { 1484 context_.Say(expr.source, 1485 "Atomic update variable '%s' not found in the " 1486 "argument list of intrinsic procedure"_err_en_US, 1487 var.GetSource().ToString()); 1488 } 1489 } 1490 }, 1491 [&](const auto &x) { 1492 if (!IsOperatorValid(x, var)) { 1493 context_.Say(expr.source, 1494 "Invalid operator in OpenMP ATOMIC (UPDATE) statement"_err_en_US); 1495 } 1496 }, 1497 }, 1498 expr.u); 1499 } 1500 1501 void OmpStructureChecker::CheckAtomicMemoryOrderClause( 1502 const parser::OmpAtomicClauseList &clauseList) { 1503 int numMemoryOrderClause = 0; 1504 for (const auto &clause : clauseList.v) { 1505 if (std::get_if<Fortran::parser::OmpMemoryOrderClause>(&clause.u)) { 1506 numMemoryOrderClause++; 1507 if (numMemoryOrderClause > 1) { 1508 context_.Say(clause.source, 1509 "More than one memory order clause not allowed on OpenMP " 1510 "Atomic construct"_err_en_US); 1511 return; 1512 } 1513 } 1514 } 1515 } 1516 1517 void OmpStructureChecker::CheckAtomicMemoryOrderClause( 1518 const parser::OmpAtomicClauseList &leftHandClauseList, 1519 const parser::OmpAtomicClauseList &rightHandClauseList) { 1520 int numMemoryOrderClause = 0; 1521 for (const auto &clause : leftHandClauseList.v) { 1522 if (std::get_if<Fortran::parser::OmpMemoryOrderClause>(&clause.u)) { 1523 numMemoryOrderClause++; 1524 if (numMemoryOrderClause > 1) { 1525 context_.Say(clause.source, 1526 "More than one memory order clause not allowed on " 1527 "OpenMP Atomic construct"_err_en_US); 1528 return; 1529 } 1530 } 1531 } 1532 for (const auto &clause : rightHandClauseList.v) { 1533 if (std::get_if<Fortran::parser::OmpMemoryOrderClause>(&clause.u)) { 1534 numMemoryOrderClause++; 1535 if (numMemoryOrderClause > 1) { 1536 context_.Say(clause.source, 1537 "More than one memory order clause not " 1538 "allowed on OpenMP Atomic construct"_err_en_US); 1539 return; 1540 } 1541 } 1542 } 1543 } 1544 1545 void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) { 1546 common::visit( 1547 common::visitors{ 1548 [&](const parser::OmpAtomic &atomicConstruct) { 1549 const auto &dir{std::get<parser::Verbatim>(atomicConstruct.t)}; 1550 PushContextAndClauseSets( 1551 dir.source, llvm::omp::Directive::OMPD_atomic); 1552 CheckAtomicUpdateAssignmentStmt( 1553 std::get<parser::Statement<parser::AssignmentStmt>>( 1554 atomicConstruct.t) 1555 .statement); 1556 CheckAtomicMemoryOrderClause( 1557 std::get<parser::OmpAtomicClauseList>(atomicConstruct.t)); 1558 }, 1559 [&](const parser::OmpAtomicUpdate &atomicConstruct) { 1560 const auto &dir{std::get<parser::Verbatim>(atomicConstruct.t)}; 1561 PushContextAndClauseSets( 1562 dir.source, llvm::omp::Directive::OMPD_atomic); 1563 CheckAtomicUpdateAssignmentStmt( 1564 std::get<parser::Statement<parser::AssignmentStmt>>( 1565 atomicConstruct.t) 1566 .statement); 1567 CheckAtomicMemoryOrderClause( 1568 std::get<0>(atomicConstruct.t), std::get<2>(atomicConstruct.t)); 1569 }, 1570 [&](const auto &atomicConstruct) { 1571 const auto &dir{std::get<parser::Verbatim>(atomicConstruct.t)}; 1572 PushContextAndClauseSets( 1573 dir.source, llvm::omp::Directive::OMPD_atomic); 1574 CheckAtomicMemoryOrderClause( 1575 std::get<0>(atomicConstruct.t), std::get<2>(atomicConstruct.t)); 1576 }, 1577 }, 1578 x.u); 1579 } 1580 1581 void OmpStructureChecker::Leave(const parser::OpenMPAtomicConstruct &) { 1582 dirContext_.pop_back(); 1583 } 1584 1585 // Clauses 1586 // Mainly categorized as 1587 // 1. Checks on 'OmpClauseList' from 'parse-tree.h'. 1588 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h. 1589 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h. 1590 1591 void OmpStructureChecker::Leave(const parser::OmpClauseList &) { 1592 // 2.7.1 Loop Construct Restriction 1593 if (llvm::omp::doSet.test(GetContext().directive)) { 1594 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) { 1595 // only one schedule clause is allowed 1596 const auto &schedClause{std::get<parser::OmpClause::Schedule>(clause->u)}; 1597 if (ScheduleModifierHasType(schedClause.v, 1598 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 1599 if (FindClause(llvm::omp::Clause::OMPC_ordered)) { 1600 context_.Say(clause->source, 1601 "The NONMONOTONIC modifier cannot be specified " 1602 "if an ORDERED clause is specified"_err_en_US); 1603 } 1604 if (ScheduleModifierHasType(schedClause.v, 1605 parser::OmpScheduleModifierType::ModType::Monotonic)) { 1606 context_.Say(clause->source, 1607 "The MONOTONIC and NONMONOTONIC modifiers " 1608 "cannot be both specified"_err_en_US); 1609 } 1610 } 1611 } 1612 1613 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) { 1614 // only one ordered clause is allowed 1615 const auto &orderedClause{ 1616 std::get<parser::OmpClause::Ordered>(clause->u)}; 1617 1618 if (orderedClause.v) { 1619 CheckNotAllowedIfClause( 1620 llvm::omp::Clause::OMPC_ordered, {llvm::omp::Clause::OMPC_linear}); 1621 1622 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) { 1623 const auto &collapseClause{ 1624 std::get<parser::OmpClause::Collapse>(clause2->u)}; 1625 // ordered and collapse both have parameters 1626 if (const auto orderedValue{GetIntValue(orderedClause.v)}) { 1627 if (const auto collapseValue{GetIntValue(collapseClause.v)}) { 1628 if (*orderedValue > 0 && *orderedValue < *collapseValue) { 1629 context_.Say(clause->source, 1630 "The parameter of the ORDERED clause must be " 1631 "greater than or equal to " 1632 "the parameter of the COLLAPSE clause"_err_en_US); 1633 } 1634 } 1635 } 1636 } 1637 } 1638 1639 // TODO: ordered region binding check (requires nesting implementation) 1640 } 1641 } // doSet 1642 1643 // 2.8.1 Simd Construct Restriction 1644 if (llvm::omp::simdSet.test(GetContext().directive)) { 1645 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) { 1646 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) { 1647 const auto &simdlenClause{ 1648 std::get<parser::OmpClause::Simdlen>(clause->u)}; 1649 const auto &safelenClause{ 1650 std::get<parser::OmpClause::Safelen>(clause2->u)}; 1651 // simdlen and safelen both have parameters 1652 if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) { 1653 if (const auto safelenValue{GetIntValue(safelenClause.v)}) { 1654 if (*safelenValue > 0 && *simdlenValue > *safelenValue) { 1655 context_.Say(clause->source, 1656 "The parameter of the SIMDLEN clause must be less than or " 1657 "equal to the parameter of the SAFELEN clause"_err_en_US); 1658 } 1659 } 1660 } 1661 } 1662 } 1663 // A list-item cannot appear in more than one aligned clause 1664 semantics::UnorderedSymbolSet alignedVars; 1665 auto clauseAll = FindClauses(llvm::omp::Clause::OMPC_aligned); 1666 for (auto itr = clauseAll.first; itr != clauseAll.second; ++itr) { 1667 const auto &alignedClause{ 1668 std::get<parser::OmpClause::Aligned>(itr->second->u)}; 1669 const auto &alignedNameList{ 1670 std::get<std::list<parser::Name>>(alignedClause.v.t)}; 1671 for (auto const &var : alignedNameList) { 1672 if (alignedVars.count(*(var.symbol)) == 1) { 1673 context_.Say(itr->second->source, 1674 "List item '%s' present at multiple ALIGNED clauses"_err_en_US, 1675 var.ToString()); 1676 break; 1677 } 1678 alignedVars.insert(*(var.symbol)); 1679 } 1680 } 1681 } // SIMD 1682 1683 // 2.7.3 Single Construct Restriction 1684 if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) { 1685 CheckNotAllowedIfClause( 1686 llvm::omp::Clause::OMPC_copyprivate, {llvm::omp::Clause::OMPC_nowait}); 1687 } 1688 1689 auto testThreadprivateVarErr = [&](Symbol sym, parser::Name name, 1690 llvmOmpClause clauseTy) { 1691 if (sym.test(Symbol::Flag::OmpThreadprivate)) 1692 context_.Say(name.source, 1693 "A THREADPRIVATE variable cannot be in %s clause"_err_en_US, 1694 parser::ToUpperCaseLetters(getClauseName(clauseTy).str())); 1695 }; 1696 1697 // [5.1] 2.21.2 Threadprivate Directive Restriction 1698 OmpClauseSet threadprivateAllowedSet{llvm::omp::Clause::OMPC_copyin, 1699 llvm::omp::Clause::OMPC_copyprivate, llvm::omp::Clause::OMPC_schedule, 1700 llvm::omp::Clause::OMPC_num_threads, llvm::omp::Clause::OMPC_thread_limit, 1701 llvm::omp::Clause::OMPC_if}; 1702 for (auto it : GetContext().clauseInfo) { 1703 llvmOmpClause type = it.first; 1704 const auto *clause = it.second; 1705 if (!threadprivateAllowedSet.test(type)) { 1706 if (const auto *objList{GetOmpObjectList(*clause)}) { 1707 for (const auto &ompObject : objList->v) { 1708 common::visit( 1709 common::visitors{ 1710 [&](const parser::Designator &) { 1711 if (const auto *name{ 1712 parser::Unwrap<parser::Name>(ompObject)}) 1713 testThreadprivateVarErr( 1714 name->symbol->GetUltimate(), *name, type); 1715 }, 1716 [&](const parser::Name &name) { 1717 if (name.symbol) { 1718 for (const auto &mem : 1719 name.symbol->get<CommonBlockDetails>().objects()) { 1720 testThreadprivateVarErr(mem->GetUltimate(), name, type); 1721 break; 1722 } 1723 } 1724 }, 1725 }, 1726 ompObject.u); 1727 } 1728 } 1729 } 1730 } 1731 1732 CheckRequireAtLeastOneOf(); 1733 } 1734 1735 void OmpStructureChecker::Enter(const parser::OmpClause &x) { 1736 SetContextClause(x); 1737 } 1738 1739 // Following clauses do not have a separate node in parse-tree.h. 1740 CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel) 1741 CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire) 1742 CHECK_SIMPLE_CLAUSE(AtomicDefaultMemOrder, OMPC_atomic_default_mem_order) 1743 CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity) 1744 CHECK_SIMPLE_CLAUSE(Allocate, OMPC_allocate) 1745 CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture) 1746 CHECK_SIMPLE_CLAUSE(Copyin, OMPC_copyin) 1747 CHECK_SIMPLE_CLAUSE(Default, OMPC_default) 1748 CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj) 1749 CHECK_SIMPLE_CLAUSE(Destroy, OMPC_destroy) 1750 CHECK_SIMPLE_CLAUSE(Detach, OMPC_detach) 1751 CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type) 1752 CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule) 1753 CHECK_SIMPLE_CLAUSE(DynamicAllocators, OMPC_dynamic_allocators) 1754 CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive) 1755 CHECK_SIMPLE_CLAUSE(Final, OMPC_final) 1756 CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush) 1757 CHECK_SIMPLE_CLAUSE(From, OMPC_from) 1758 CHECK_SIMPLE_CLAUSE(Full, OMPC_full) 1759 CHECK_SIMPLE_CLAUSE(Hint, OMPC_hint) 1760 CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction) 1761 CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive) 1762 CHECK_SIMPLE_CLAUSE(Match, OMPC_match) 1763 CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal) 1764 CHECK_SIMPLE_CLAUSE(Order, OMPC_order) 1765 CHECK_SIMPLE_CLAUSE(Read, OMPC_read) 1766 CHECK_SIMPLE_CLAUSE(ReverseOffload, OMPC_reverse_offload) 1767 CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate) 1768 CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads) 1769 CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) 1770 CHECK_SIMPLE_CLAUSE(IsDevicePtr, OMPC_is_device_ptr) 1771 CHECK_SIMPLE_CLAUSE(HasDeviceAddr, OMPC_has_device_addr) 1772 CHECK_SIMPLE_CLAUSE(Link, OMPC_link) 1773 CHECK_SIMPLE_CLAUSE(Indirect, OMPC_indirect) 1774 CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable) 1775 CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup) 1776 CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch) 1777 CHECK_SIMPLE_CLAUSE(Nowait, OMPC_nowait) 1778 CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial) 1779 CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind) 1780 CHECK_SIMPLE_CLAUSE(Release, OMPC_release) 1781 CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed) 1782 CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst) 1783 CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd) 1784 CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes) 1785 CHECK_SIMPLE_CLAUSE(TaskReduction, OMPC_task_reduction) 1786 CHECK_SIMPLE_CLAUSE(To, OMPC_to) 1787 CHECK_SIMPLE_CLAUSE(UnifiedAddress, OMPC_unified_address) 1788 CHECK_SIMPLE_CLAUSE(UnifiedSharedMemory, OMPC_unified_shared_memory) 1789 CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform) 1790 CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown) 1791 CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied) 1792 CHECK_SIMPLE_CLAUSE(UseDevicePtr, OMPC_use_device_ptr) 1793 CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators) 1794 CHECK_SIMPLE_CLAUSE(Update, OMPC_update) 1795 CHECK_SIMPLE_CLAUSE(UseDeviceAddr, OMPC_use_device_addr) 1796 CHECK_SIMPLE_CLAUSE(Write, OMPC_write) 1797 CHECK_SIMPLE_CLAUSE(Init, OMPC_init) 1798 CHECK_SIMPLE_CLAUSE(Use, OMPC_use) 1799 CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants) 1800 CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext) 1801 CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter) 1802 CHECK_SIMPLE_CLAUSE(When, OMPC_when) 1803 CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args) 1804 CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args) 1805 CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order) 1806 CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind) 1807 CHECK_SIMPLE_CLAUSE(Align, OMPC_align) 1808 CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare) 1809 CHECK_SIMPLE_CLAUSE(CancellationConstructType, OMPC_cancellation_construct_type) 1810 1811 CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize) 1812 CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks) 1813 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams) 1814 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads) 1815 CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority) 1816 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit) 1817 CHECK_REQ_SCALAR_INT_CLAUSE(Device, OMPC_device) 1818 1819 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse) 1820 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen) 1821 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen) 1822 1823 // Restrictions specific to each clause are implemented apart from the 1824 // generalized restrictions. 1825 void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) { 1826 CheckAllowed(llvm::omp::Clause::OMPC_reduction); 1827 if (CheckReductionOperators(x)) { 1828 CheckReductionTypeList(x); 1829 } 1830 } 1831 bool OmpStructureChecker::CheckReductionOperators( 1832 const parser::OmpClause::Reduction &x) { 1833 1834 const auto &definedOp{std::get<0>(x.v.t)}; 1835 bool ok = false; 1836 common::visit( 1837 common::visitors{ 1838 [&](const parser::DefinedOperator &dOpr) { 1839 const auto &intrinsicOp{ 1840 std::get<parser::DefinedOperator::IntrinsicOperator>(dOpr.u)}; 1841 ok = CheckIntrinsicOperator(intrinsicOp); 1842 }, 1843 [&](const parser::ProcedureDesignator &procD) { 1844 const parser::Name *name{std::get_if<parser::Name>(&procD.u)}; 1845 if (name) { 1846 if (name->source == "max" || name->source == "min" || 1847 name->source == "iand" || name->source == "ior" || 1848 name->source == "ieor") { 1849 ok = true; 1850 } else { 1851 context_.Say(GetContext().clauseSource, 1852 "Invalid reduction identifier in REDUCTION clause."_err_en_US, 1853 ContextDirectiveAsFortran()); 1854 } 1855 } 1856 }, 1857 }, 1858 definedOp.u); 1859 1860 return ok; 1861 } 1862 bool OmpStructureChecker::CheckIntrinsicOperator( 1863 const parser::DefinedOperator::IntrinsicOperator &op) { 1864 1865 switch (op) { 1866 case parser::DefinedOperator::IntrinsicOperator::Add: 1867 case parser::DefinedOperator::IntrinsicOperator::Subtract: 1868 case parser::DefinedOperator::IntrinsicOperator::Multiply: 1869 case parser::DefinedOperator::IntrinsicOperator::AND: 1870 case parser::DefinedOperator::IntrinsicOperator::OR: 1871 case parser::DefinedOperator::IntrinsicOperator::EQV: 1872 case parser::DefinedOperator::IntrinsicOperator::NEQV: 1873 return true; 1874 default: 1875 context_.Say(GetContext().clauseSource, 1876 "Invalid reduction operator in REDUCTION clause."_err_en_US, 1877 ContextDirectiveAsFortran()); 1878 } 1879 return false; 1880 } 1881 1882 void OmpStructureChecker::CheckReductionTypeList( 1883 const parser::OmpClause::Reduction &x) { 1884 const auto &ompObjectList{std::get<parser::OmpObjectList>(x.v.t)}; 1885 CheckIntentInPointerAndDefinable( 1886 ompObjectList, llvm::omp::Clause::OMPC_reduction); 1887 CheckReductionArraySection(ompObjectList); 1888 CheckMultipleAppearanceAcrossContext(ompObjectList); 1889 } 1890 1891 void OmpStructureChecker::CheckIntentInPointerAndDefinable( 1892 const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) { 1893 for (const auto &ompObject : objectList.v) { 1894 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1895 if (const auto *symbol{name->symbol}) { 1896 if (IsPointer(symbol->GetUltimate()) && 1897 IsIntentIn(symbol->GetUltimate())) { 1898 context_.Say(GetContext().clauseSource, 1899 "Pointer '%s' with the INTENT(IN) attribute may not appear " 1900 "in a %s clause"_err_en_US, 1901 symbol->name(), 1902 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1903 } 1904 if (auto msg{ 1905 WhyNotModifiable(*symbol, context_.FindScope(name->source))}) { 1906 context_ 1907 .Say(GetContext().clauseSource, 1908 "Variable '%s' on the %s clause is not definable"_err_en_US, 1909 symbol->name(), 1910 parser::ToUpperCaseLetters(getClauseName(clause).str())) 1911 .Attach(std::move(*msg)); 1912 } 1913 } 1914 } 1915 } 1916 } 1917 1918 void OmpStructureChecker::CheckReductionArraySection( 1919 const parser::OmpObjectList &ompObjectList) { 1920 for (const auto &ompObject : ompObjectList.v) { 1921 if (const auto *dataRef{parser::Unwrap<parser::DataRef>(ompObject)}) { 1922 if (const auto *arrayElement{ 1923 parser::Unwrap<parser::ArrayElement>(ompObject)}) { 1924 if (arrayElement) { 1925 CheckArraySection(*arrayElement, GetLastName(*dataRef), 1926 llvm::omp::Clause::OMPC_reduction); 1927 } 1928 } 1929 } 1930 } 1931 } 1932 1933 void OmpStructureChecker::CheckMultipleAppearanceAcrossContext( 1934 const parser::OmpObjectList &redObjectList) { 1935 // TODO: Verify the assumption here that the immediately enclosing region is 1936 // the parallel region to which the worksharing construct having reduction 1937 // binds to. 1938 if (auto *enclosingContext{GetEnclosingDirContext()}) { 1939 for (auto it : enclosingContext->clauseInfo) { 1940 llvmOmpClause type = it.first; 1941 const auto *clause = it.second; 1942 if (llvm::omp::privateReductionSet.test(type)) { 1943 if (const auto *objList{GetOmpObjectList(*clause)}) { 1944 for (const auto &ompObject : objList->v) { 1945 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1946 if (const auto *symbol{name->symbol}) { 1947 for (const auto &redOmpObject : redObjectList.v) { 1948 if (const auto *rname{ 1949 parser::Unwrap<parser::Name>(redOmpObject)}) { 1950 if (const auto *rsymbol{rname->symbol}) { 1951 if (rsymbol->name() == symbol->name()) { 1952 context_.Say(GetContext().clauseSource, 1953 "%s variable '%s' is %s in outer context must" 1954 " be shared in the parallel regions to which any" 1955 " of the worksharing regions arising from the " 1956 "worksharing" 1957 " construct bind."_err_en_US, 1958 parser::ToUpperCaseLetters( 1959 getClauseName(llvm::omp::Clause::OMPC_reduction) 1960 .str()), 1961 symbol->name(), 1962 parser::ToUpperCaseLetters( 1963 getClauseName(type).str())); 1964 } 1965 } 1966 } 1967 } 1968 } 1969 } 1970 } 1971 } 1972 } 1973 } 1974 } 1975 } 1976 1977 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) { 1978 CheckAllowed(llvm::omp::Clause::OMPC_ordered); 1979 // the parameter of ordered clause is optional 1980 if (const auto &expr{x.v}) { 1981 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr); 1982 // 2.8.3 Loop SIMD Construct Restriction 1983 if (llvm::omp::doSimdSet.test(GetContext().directive)) { 1984 context_.Say(GetContext().clauseSource, 1985 "No ORDERED clause with a parameter can be specified " 1986 "on the %s directive"_err_en_US, 1987 ContextDirectiveAsFortran()); 1988 } 1989 } 1990 } 1991 1992 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) { 1993 CheckAllowed(llvm::omp::Clause::OMPC_shared); 1994 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v); 1995 } 1996 void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) { 1997 CheckAllowed(llvm::omp::Clause::OMPC_private); 1998 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v); 1999 CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_private); 2000 } 2001 2002 bool OmpStructureChecker::IsDataRefTypeParamInquiry( 2003 const parser::DataRef *dataRef) { 2004 bool dataRefIsTypeParamInquiry{false}; 2005 if (const auto *structComp{ 2006 parser::Unwrap<parser::StructureComponent>(dataRef)}) { 2007 if (const auto *compSymbol{structComp->component.symbol}) { 2008 if (const auto *compSymbolMiscDetails{ 2009 std::get_if<MiscDetails>(&compSymbol->details())}) { 2010 const auto detailsKind = compSymbolMiscDetails->kind(); 2011 dataRefIsTypeParamInquiry = 2012 (detailsKind == MiscDetails::Kind::KindParamInquiry || 2013 detailsKind == MiscDetails::Kind::LenParamInquiry); 2014 } else if (compSymbol->has<TypeParamDetails>()) { 2015 dataRefIsTypeParamInquiry = true; 2016 } 2017 } 2018 } 2019 return dataRefIsTypeParamInquiry; 2020 } 2021 2022 void OmpStructureChecker::CheckIsVarPartOfAnotherVar( 2023 const parser::CharBlock &source, const parser::OmpObjectList &objList) { 2024 OmpDirectiveSet nonPartialVarSet{llvm::omp::Directive::OMPD_allocate, 2025 llvm::omp::Directive::OMPD_threadprivate, 2026 llvm::omp::Directive::OMPD_declare_target}; 2027 for (const auto &ompObject : objList.v) { 2028 common::visit( 2029 common::visitors{ 2030 [&](const parser::Designator &designator) { 2031 if (const auto *dataRef{ 2032 std::get_if<parser::DataRef>(&designator.u)}) { 2033 if (IsDataRefTypeParamInquiry(dataRef)) { 2034 context_.Say(source, 2035 "A type parameter inquiry cannot appear on the %s " 2036 "directive"_err_en_US, 2037 ContextDirectiveAsFortran()); 2038 } else if (parser::Unwrap<parser::StructureComponent>( 2039 ompObject) || 2040 parser::Unwrap<parser::ArrayElement>(ompObject)) { 2041 if (nonPartialVarSet.test(GetContext().directive)) { 2042 context_.Say(source, 2043 "A variable that is part of another variable (as an " 2044 "array or structure element) cannot appear on the %s " 2045 "directive"_err_en_US, 2046 ContextDirectiveAsFortran()); 2047 } else { 2048 context_.Say(source, 2049 "A variable that is part of another variable (as an " 2050 "array or structure element) cannot appear in a " 2051 "PRIVATE or SHARED clause"_err_en_US); 2052 } 2053 } 2054 } 2055 }, 2056 [&](const parser::Name &name) {}, 2057 }, 2058 ompObject.u); 2059 } 2060 } 2061 2062 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) { 2063 CheckAllowed(llvm::omp::Clause::OMPC_firstprivate); 2064 CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate, x.v); 2065 2066 SymbolSourceMap currSymbols; 2067 GetSymbolsInObjectList(x.v, currSymbols); 2068 2069 DirectivesClauseTriple dirClauseTriple; 2070 // Check firstprivate variables in worksharing constructs 2071 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do, 2072 std::make_pair( 2073 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 2074 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections, 2075 std::make_pair( 2076 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 2077 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_single, 2078 std::make_pair( 2079 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 2080 // Check firstprivate variables in distribute construct 2081 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute, 2082 std::make_pair( 2083 llvm::omp::Directive::OMPD_teams, llvm::omp::privateReductionSet)); 2084 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute, 2085 std::make_pair(llvm::omp::Directive::OMPD_target_teams, 2086 llvm::omp::privateReductionSet)); 2087 // Check firstprivate variables in task and taskloop constructs 2088 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_task, 2089 std::make_pair(llvm::omp::Directive::OMPD_parallel, 2090 OmpClauseSet{llvm::omp::Clause::OMPC_reduction})); 2091 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_taskloop, 2092 std::make_pair(llvm::omp::Directive::OMPD_parallel, 2093 OmpClauseSet{llvm::omp::Clause::OMPC_reduction})); 2094 2095 CheckPrivateSymbolsInOuterCxt( 2096 currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_firstprivate); 2097 } 2098 2099 void OmpStructureChecker::CheckIsLoopIvPartOfClause( 2100 llvmOmpClause clause, const parser::OmpObjectList &ompObjectList) { 2101 for (const auto &ompObject : ompObjectList.v) { 2102 if (const parser::Name * name{parser::Unwrap<parser::Name>(ompObject)}) { 2103 if (name->symbol == GetContext().loopIV) { 2104 context_.Say(name->source, 2105 "DO iteration variable %s is not allowed in %s clause."_err_en_US, 2106 name->ToString(), 2107 parser::ToUpperCaseLetters(getClauseName(clause).str())); 2108 } 2109 } 2110 } 2111 } 2112 // Following clauses have a seperate node in parse-tree.h. 2113 // Atomic-clause 2114 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead, OMPC_read) 2115 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicWrite, OMPC_write) 2116 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicUpdate, OMPC_update) 2117 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicCapture, OMPC_capture) 2118 2119 void OmpStructureChecker::Leave(const parser::OmpAtomicRead &) { 2120 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_read, 2121 {llvm::omp::Clause::OMPC_release, llvm::omp::Clause::OMPC_acq_rel}); 2122 } 2123 void OmpStructureChecker::Leave(const parser::OmpAtomicWrite &) { 2124 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_write, 2125 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel}); 2126 } 2127 void OmpStructureChecker::Leave(const parser::OmpAtomicUpdate &) { 2128 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_update, 2129 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel}); 2130 } 2131 // OmpAtomic node represents atomic directive without atomic-clause. 2132 // atomic-clause - READ,WRITE,UPDATE,CAPTURE. 2133 void OmpStructureChecker::Leave(const parser::OmpAtomic &) { 2134 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acquire)}) { 2135 context_.Say(clause->source, 2136 "Clause ACQUIRE is not allowed on the ATOMIC directive"_err_en_US); 2137 } 2138 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acq_rel)}) { 2139 context_.Say(clause->source, 2140 "Clause ACQ_REL is not allowed on the ATOMIC directive"_err_en_US); 2141 } 2142 } 2143 // Restrictions specific to each clause are implemented apart from the 2144 // generalized restrictions. 2145 void OmpStructureChecker::Enter(const parser::OmpClause::Aligned &x) { 2146 CheckAllowed(llvm::omp::Clause::OMPC_aligned); 2147 2148 if (const auto &expr{ 2149 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.v.t)}) { 2150 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_aligned, *expr); 2151 } 2152 // 2.8.1 TODO: list-item attribute check 2153 } 2154 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) { 2155 CheckAllowed(llvm::omp::Clause::OMPC_defaultmap); 2156 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory; 2157 if (!std::get<std::optional<VariableCategory>>(x.v.t)) { 2158 context_.Say(GetContext().clauseSource, 2159 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP " 2160 "clause"_err_en_US); 2161 } 2162 } 2163 void OmpStructureChecker::Enter(const parser::OmpClause::If &x) { 2164 CheckAllowed(llvm::omp::Clause::OMPC_if); 2165 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier; 2166 static std::unordered_map<dirNameModifier, OmpDirectiveSet> 2167 dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet}, 2168 {dirNameModifier::Target, llvm::omp::targetSet}, 2169 {dirNameModifier::TargetEnterData, 2170 {llvm::omp::Directive::OMPD_target_enter_data}}, 2171 {dirNameModifier::TargetExitData, 2172 {llvm::omp::Directive::OMPD_target_exit_data}}, 2173 {dirNameModifier::TargetData, 2174 {llvm::omp::Directive::OMPD_target_data}}, 2175 {dirNameModifier::TargetUpdate, 2176 {llvm::omp::Directive::OMPD_target_update}}, 2177 {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}}, 2178 {dirNameModifier::Taskloop, llvm::omp::taskloopSet}}; 2179 if (const auto &directiveName{ 2180 std::get<std::optional<dirNameModifier>>(x.v.t)}) { 2181 auto search{dirNameModifierMap.find(*directiveName)}; 2182 if (search == dirNameModifierMap.end() || 2183 !search->second.test(GetContext().directive)) { 2184 context_ 2185 .Say(GetContext().clauseSource, 2186 "Unmatched directive name modifier %s on the IF clause"_err_en_US, 2187 parser::ToUpperCaseLetters( 2188 parser::OmpIfClause::EnumToString(*directiveName))) 2189 .Attach( 2190 GetContext().directiveSource, "Cannot apply to directive"_en_US); 2191 } 2192 } 2193 } 2194 2195 void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) { 2196 CheckAllowed(llvm::omp::Clause::OMPC_linear); 2197 2198 // 2.7 Loop Construct Restriction 2199 if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) { 2200 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.v.u)) { 2201 context_.Say(GetContext().clauseSource, 2202 "A modifier may not be specified in a LINEAR clause " 2203 "on the %s directive"_err_en_US, 2204 ContextDirectiveAsFortran()); 2205 } 2206 } 2207 } 2208 2209 void OmpStructureChecker::CheckAllowedMapTypes( 2210 const parser::OmpMapType::Type &type, 2211 const std::list<parser::OmpMapType::Type> &allowedMapTypeList) { 2212 const auto found{std::find( 2213 std::begin(allowedMapTypeList), std::end(allowedMapTypeList), type)}; 2214 if (found == std::end(allowedMapTypeList)) { 2215 std::string commaSeperatedMapTypes; 2216 llvm::interleave( 2217 allowedMapTypeList.begin(), allowedMapTypeList.end(), 2218 [&](const parser::OmpMapType::Type &mapType) { 2219 commaSeperatedMapTypes.append(parser::ToUpperCaseLetters( 2220 parser::OmpMapType::EnumToString(mapType))); 2221 }, 2222 [&] { commaSeperatedMapTypes.append(", "); }); 2223 context_.Say(GetContext().clauseSource, 2224 "Only the %s map types are permitted " 2225 "for MAP clauses on the %s directive"_err_en_US, 2226 commaSeperatedMapTypes, ContextDirectiveAsFortran()); 2227 } 2228 } 2229 2230 void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) { 2231 CheckAllowed(llvm::omp::Clause::OMPC_map); 2232 2233 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.v.t)}) { 2234 using Type = parser::OmpMapType::Type; 2235 const Type &type{std::get<Type>(maptype->t)}; 2236 switch (GetContext().directive) { 2237 case llvm::omp::Directive::OMPD_target: 2238 case llvm::omp::Directive::OMPD_target_teams: 2239 case llvm::omp::Directive::OMPD_target_teams_distribute: 2240 case llvm::omp::Directive::OMPD_target_teams_distribute_simd: 2241 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: 2242 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: 2243 case llvm::omp::Directive::OMPD_target_data: 2244 CheckAllowedMapTypes( 2245 type, {Type::To, Type::From, Type::Tofrom, Type::Alloc}); 2246 break; 2247 case llvm::omp::Directive::OMPD_target_enter_data: 2248 CheckAllowedMapTypes(type, {Type::To, Type::Alloc}); 2249 break; 2250 case llvm::omp::Directive::OMPD_target_exit_data: 2251 CheckAllowedMapTypes(type, {Type::From, Type::Release, Type::Delete}); 2252 break; 2253 default: 2254 break; 2255 } 2256 } 2257 } 2258 2259 bool OmpStructureChecker::ScheduleModifierHasType( 2260 const parser::OmpScheduleClause &x, 2261 const parser::OmpScheduleModifierType::ModType &type) { 2262 const auto &modifier{ 2263 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)}; 2264 if (modifier) { 2265 const auto &modType1{ 2266 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)}; 2267 const auto &modType2{ 2268 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>( 2269 modifier->t)}; 2270 if (modType1.v.v == type || (modType2 && modType2->v.v == type)) { 2271 return true; 2272 } 2273 } 2274 return false; 2275 } 2276 void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) { 2277 CheckAllowed(llvm::omp::Clause::OMPC_schedule); 2278 const parser::OmpScheduleClause &scheduleClause = x.v; 2279 2280 // 2.7 Loop Construct Restriction 2281 if (llvm::omp::doSet.test(GetContext().directive)) { 2282 const auto &kind{std::get<1>(scheduleClause.t)}; 2283 const auto &chunk{std::get<2>(scheduleClause.t)}; 2284 if (chunk) { 2285 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime || 2286 kind == parser::OmpScheduleClause::ScheduleType::Auto) { 2287 context_.Say(GetContext().clauseSource, 2288 "When SCHEDULE clause has %s specified, " 2289 "it must not have chunk size specified"_err_en_US, 2290 parser::ToUpperCaseLetters( 2291 parser::OmpScheduleClause::EnumToString(kind))); 2292 } 2293 if (const auto &chunkExpr{std::get<std::optional<parser::ScalarIntExpr>>( 2294 scheduleClause.t)}) { 2295 RequiresPositiveParameter( 2296 llvm::omp::Clause::OMPC_schedule, *chunkExpr, "chunk size"); 2297 } 2298 } 2299 2300 if (ScheduleModifierHasType(scheduleClause, 2301 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 2302 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic && 2303 kind != parser::OmpScheduleClause::ScheduleType::Guided) { 2304 context_.Say(GetContext().clauseSource, 2305 "The NONMONOTONIC modifier can only be specified with " 2306 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US); 2307 } 2308 } 2309 } 2310 } 2311 2312 void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) { 2313 CheckAllowed(llvm::omp::Clause::OMPC_depend); 2314 if (const auto *inOut{std::get_if<parser::OmpDependClause::InOut>(&x.v.u)}) { 2315 const auto &designators{std::get<std::list<parser::Designator>>(inOut->t)}; 2316 for (const auto &ele : designators) { 2317 if (const auto *dataRef{std::get_if<parser::DataRef>(&ele.u)}) { 2318 CheckDependList(*dataRef); 2319 if (const auto *arr{ 2320 std::get_if<common::Indirection<parser::ArrayElement>>( 2321 &dataRef->u)}) { 2322 CheckArraySection(arr->value(), GetLastName(*dataRef), 2323 llvm::omp::Clause::OMPC_depend); 2324 } 2325 } 2326 } 2327 } 2328 } 2329 2330 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &x) { 2331 CheckAllowed(llvm::omp::Clause::OMPC_copyprivate); 2332 CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_copyprivate); 2333 } 2334 2335 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) { 2336 CheckAllowed(llvm::omp::Clause::OMPC_lastprivate); 2337 2338 DirectivesClauseTriple dirClauseTriple; 2339 SymbolSourceMap currSymbols; 2340 GetSymbolsInObjectList(x.v, currSymbols); 2341 CheckDefinableObjects(currSymbols, GetClauseKindForParserClass(x)); 2342 2343 // Check lastprivate variables in worksharing constructs 2344 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do, 2345 std::make_pair( 2346 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 2347 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections, 2348 std::make_pair( 2349 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 2350 2351 CheckPrivateSymbolsInOuterCxt( 2352 currSymbols, dirClauseTriple, GetClauseKindForParserClass(x)); 2353 } 2354 2355 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) { 2356 return llvm::omp::getOpenMPClauseName(clause); 2357 } 2358 2359 llvm::StringRef OmpStructureChecker::getDirectiveName( 2360 llvm::omp::Directive directive) { 2361 return llvm::omp::getOpenMPDirectiveName(directive); 2362 } 2363 2364 void OmpStructureChecker::CheckDependList(const parser::DataRef &d) { 2365 common::visit( 2366 common::visitors{ 2367 [&](const common::Indirection<parser::ArrayElement> &elem) { 2368 // Check if the base element is valid on Depend Clause 2369 CheckDependList(elem.value().base); 2370 }, 2371 [&](const common::Indirection<parser::StructureComponent> &) { 2372 context_.Say(GetContext().clauseSource, 2373 "A variable that is part of another variable " 2374 "(such as an element of a structure) but is not an array " 2375 "element or an array section cannot appear in a DEPEND " 2376 "clause"_err_en_US); 2377 }, 2378 [&](const common::Indirection<parser::CoindexedNamedObject> &) { 2379 context_.Say(GetContext().clauseSource, 2380 "Coarrays are not supported in DEPEND clause"_err_en_US); 2381 }, 2382 [&](const parser::Name &) { return; }, 2383 }, 2384 d.u); 2385 } 2386 2387 // Called from both Reduction and Depend clause. 2388 void OmpStructureChecker::CheckArraySection( 2389 const parser::ArrayElement &arrayElement, const parser::Name &name, 2390 const llvm::omp::Clause clause) { 2391 if (!arrayElement.subscripts.empty()) { 2392 for (const auto &subscript : arrayElement.subscripts) { 2393 if (const auto *triplet{ 2394 std::get_if<parser::SubscriptTriplet>(&subscript.u)}) { 2395 if (std::get<0>(triplet->t) && std::get<1>(triplet->t)) { 2396 const auto &lower{std::get<0>(triplet->t)}; 2397 const auto &upper{std::get<1>(triplet->t)}; 2398 if (lower && upper) { 2399 const auto lval{GetIntValue(lower)}; 2400 const auto uval{GetIntValue(upper)}; 2401 if (lval && uval && *uval < *lval) { 2402 context_.Say(GetContext().clauseSource, 2403 "'%s' in %s clause" 2404 " is a zero size array section"_err_en_US, 2405 name.ToString(), 2406 parser::ToUpperCaseLetters(getClauseName(clause).str())); 2407 break; 2408 } else if (std::get<2>(triplet->t)) { 2409 const auto &strideExpr{std::get<2>(triplet->t)}; 2410 if (strideExpr) { 2411 if (clause == llvm::omp::Clause::OMPC_depend) { 2412 context_.Say(GetContext().clauseSource, 2413 "Stride should not be specified for array section in " 2414 "DEPEND " 2415 "clause"_err_en_US); 2416 } 2417 const auto stride{GetIntValue(strideExpr)}; 2418 if ((stride && stride != 1)) { 2419 context_.Say(GetContext().clauseSource, 2420 "A list item that appears in a REDUCTION clause" 2421 " should have a contiguous storage array section."_err_en_US, 2422 ContextDirectiveAsFortran()); 2423 break; 2424 } 2425 } 2426 } 2427 } 2428 } 2429 } 2430 } 2431 } 2432 } 2433 2434 void OmpStructureChecker::CheckIntentInPointer( 2435 const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) { 2436 SymbolSourceMap symbols; 2437 GetSymbolsInObjectList(objectList, symbols); 2438 for (auto it{symbols.begin()}; it != symbols.end(); ++it) { 2439 const auto *symbol{it->first}; 2440 const auto source{it->second}; 2441 if (IsPointer(*symbol) && IsIntentIn(*symbol)) { 2442 context_.Say(source, 2443 "Pointer '%s' with the INTENT(IN) attribute may not appear " 2444 "in a %s clause"_err_en_US, 2445 symbol->name(), 2446 parser::ToUpperCaseLetters(getClauseName(clause).str())); 2447 } 2448 } 2449 } 2450 2451 void OmpStructureChecker::GetSymbolsInObjectList( 2452 const parser::OmpObjectList &objectList, SymbolSourceMap &symbols) { 2453 for (const auto &ompObject : objectList.v) { 2454 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 2455 if (const auto *symbol{name->symbol}) { 2456 if (const auto *commonBlockDetails{ 2457 symbol->detailsIf<CommonBlockDetails>()}) { 2458 for (const auto &object : commonBlockDetails->objects()) { 2459 symbols.emplace(&object->GetUltimate(), name->source); 2460 } 2461 } else { 2462 symbols.emplace(&symbol->GetUltimate(), name->source); 2463 } 2464 } 2465 } 2466 } 2467 } 2468 2469 void OmpStructureChecker::CheckDefinableObjects( 2470 SymbolSourceMap &symbols, const llvm::omp::Clause clause) { 2471 for (auto it{symbols.begin()}; it != symbols.end(); ++it) { 2472 const auto *symbol{it->first}; 2473 const auto source{it->second}; 2474 if (auto msg{WhyNotModifiable(*symbol, context_.FindScope(source))}) { 2475 context_ 2476 .Say(source, 2477 "Variable '%s' on the %s clause is not definable"_err_en_US, 2478 symbol->name(), 2479 parser::ToUpperCaseLetters(getClauseName(clause).str())) 2480 .Attach(std::move(*msg)); 2481 } 2482 } 2483 } 2484 2485 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt( 2486 SymbolSourceMap &currSymbols, DirectivesClauseTriple &dirClauseTriple, 2487 const llvm::omp::Clause currClause) { 2488 SymbolSourceMap enclosingSymbols; 2489 auto range{dirClauseTriple.equal_range(GetContext().directive)}; 2490 for (auto dirIter{range.first}; dirIter != range.second; ++dirIter) { 2491 auto enclosingDir{dirIter->second.first}; 2492 auto enclosingClauseSet{dirIter->second.second}; 2493 if (auto *enclosingContext{GetEnclosingContextWithDir(enclosingDir)}) { 2494 for (auto it{enclosingContext->clauseInfo.begin()}; 2495 it != enclosingContext->clauseInfo.end(); ++it) { 2496 if (enclosingClauseSet.test(it->first)) { 2497 if (const auto *ompObjectList{GetOmpObjectList(*it->second)}) { 2498 GetSymbolsInObjectList(*ompObjectList, enclosingSymbols); 2499 } 2500 } 2501 } 2502 2503 // Check if the symbols in current context are private in outer context 2504 for (auto iter{currSymbols.begin()}; iter != currSymbols.end(); ++iter) { 2505 const auto *symbol{iter->first}; 2506 const auto source{iter->second}; 2507 if (enclosingSymbols.find(symbol) != enclosingSymbols.end()) { 2508 context_.Say(source, 2509 "%s variable '%s' is PRIVATE in outer context"_err_en_US, 2510 parser::ToUpperCaseLetters(getClauseName(currClause).str()), 2511 symbol->name()); 2512 } 2513 } 2514 } 2515 } 2516 } 2517 2518 bool OmpStructureChecker::CheckTargetBlockOnlyTeams( 2519 const parser::Block &block) { 2520 bool nestedTeams{false}; 2521 auto it{block.begin()}; 2522 2523 if (const auto *ompConstruct{parser::Unwrap<parser::OpenMPConstruct>(*it)}) { 2524 if (const auto *ompBlockConstruct{ 2525 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) { 2526 const auto &beginBlockDir{ 2527 std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)}; 2528 const auto &beginDir{ 2529 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 2530 if (beginDir.v == llvm::omp::Directive::OMPD_teams) { 2531 nestedTeams = true; 2532 } 2533 } 2534 } 2535 2536 if (nestedTeams && ++it == block.end()) { 2537 return true; 2538 } 2539 return false; 2540 } 2541 2542 void OmpStructureChecker::CheckWorkshareBlockStmts( 2543 const parser::Block &block, parser::CharBlock source) { 2544 OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source}; 2545 2546 for (auto it{block.begin()}; it != block.end(); ++it) { 2547 if (parser::Unwrap<parser::AssignmentStmt>(*it) || 2548 parser::Unwrap<parser::ForallStmt>(*it) || 2549 parser::Unwrap<parser::ForallConstruct>(*it) || 2550 parser::Unwrap<parser::WhereStmt>(*it) || 2551 parser::Unwrap<parser::WhereConstruct>(*it)) { 2552 parser::Walk(*it, ompWorkshareBlockChecker); 2553 } else if (const auto *ompConstruct{ 2554 parser::Unwrap<parser::OpenMPConstruct>(*it)}) { 2555 if (const auto *ompAtomicConstruct{ 2556 std::get_if<parser::OpenMPAtomicConstruct>(&ompConstruct->u)}) { 2557 // Check if assignment statements in the enclosing OpenMP Atomic 2558 // construct are allowed in the Workshare construct 2559 parser::Walk(*ompAtomicConstruct, ompWorkshareBlockChecker); 2560 } else if (const auto *ompCriticalConstruct{ 2561 std::get_if<parser::OpenMPCriticalConstruct>( 2562 &ompConstruct->u)}) { 2563 // All the restrictions on the Workshare construct apply to the 2564 // statements in the enclosing critical constructs 2565 const auto &criticalBlock{ 2566 std::get<parser::Block>(ompCriticalConstruct->t)}; 2567 CheckWorkshareBlockStmts(criticalBlock, source); 2568 } else { 2569 // Check if OpenMP constructs enclosed in the Workshare construct are 2570 // 'Parallel' constructs 2571 auto currentDir{llvm::omp::Directive::OMPD_unknown}; 2572 const OmpDirectiveSet parallelDirSet{ 2573 llvm::omp::Directive::OMPD_parallel, 2574 llvm::omp::Directive::OMPD_parallel_do, 2575 llvm::omp::Directive::OMPD_parallel_sections, 2576 llvm::omp::Directive::OMPD_parallel_workshare, 2577 llvm::omp::Directive::OMPD_parallel_do_simd}; 2578 2579 if (const auto *ompBlockConstruct{ 2580 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) { 2581 const auto &beginBlockDir{ 2582 std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)}; 2583 const auto &beginDir{ 2584 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 2585 currentDir = beginDir.v; 2586 } else if (const auto *ompLoopConstruct{ 2587 std::get_if<parser::OpenMPLoopConstruct>( 2588 &ompConstruct->u)}) { 2589 const auto &beginLoopDir{ 2590 std::get<parser::OmpBeginLoopDirective>(ompLoopConstruct->t)}; 2591 const auto &beginDir{ 2592 std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 2593 currentDir = beginDir.v; 2594 } else if (const auto *ompSectionsConstruct{ 2595 std::get_if<parser::OpenMPSectionsConstruct>( 2596 &ompConstruct->u)}) { 2597 const auto &beginSectionsDir{ 2598 std::get<parser::OmpBeginSectionsDirective>( 2599 ompSectionsConstruct->t)}; 2600 const auto &beginDir{ 2601 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 2602 currentDir = beginDir.v; 2603 } 2604 2605 if (!parallelDirSet.test(currentDir)) { 2606 context_.Say(source, 2607 "OpenMP constructs enclosed in WORKSHARE construct may consist " 2608 "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US); 2609 } 2610 } 2611 } else { 2612 context_.Say(source, 2613 "The structured block in a WORKSHARE construct may consist of only " 2614 "SCALAR or ARRAY assignments, FORALL or WHERE statements, " 2615 "FORALL, WHERE, ATOMIC, CRITICAL or PARALLEL constructs"_err_en_US); 2616 } 2617 } 2618 } 2619 2620 const parser::OmpObjectList *OmpStructureChecker::GetOmpObjectList( 2621 const parser::OmpClause &clause) { 2622 2623 // Clauses with OmpObjectList as its data member 2624 using MemberObjectListClauses = std::tuple<parser::OmpClause::Copyprivate, 2625 parser::OmpClause::Copyin, parser::OmpClause::Firstprivate, 2626 parser::OmpClause::From, parser::OmpClause::Lastprivate, 2627 parser::OmpClause::Link, parser::OmpClause::Private, 2628 parser::OmpClause::Shared, parser::OmpClause::To>; 2629 2630 // Clauses with OmpObjectList in the tuple 2631 using TupleObjectListClauses = std::tuple<parser::OmpClause::Allocate, 2632 parser::OmpClause::Map, parser::OmpClause::Reduction>; 2633 2634 // TODO:: Generate the tuples using TableGen. 2635 // Handle other constructs with OmpObjectList such as OpenMPThreadprivate. 2636 return common::visit( 2637 common::visitors{ 2638 [&](const auto &x) -> const parser::OmpObjectList * { 2639 using Ty = std::decay_t<decltype(x)>; 2640 if constexpr (common::HasMember<Ty, MemberObjectListClauses>) { 2641 return &x.v; 2642 } else if constexpr (common::HasMember<Ty, 2643 TupleObjectListClauses>) { 2644 return &(std::get<parser::OmpObjectList>(x.v.t)); 2645 } else { 2646 return nullptr; 2647 } 2648 }, 2649 }, 2650 clause.u); 2651 } 2652 2653 } // namespace Fortran::semantics 2654