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(var)}; 54 const auto *rhs{GetExpr(expr)}; 55 Tristate isDefined{semantics::IsDefinedAssignment( 56 lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())}; 57 if (isDefined == Tristate::Yes) { 58 context_.Say(expr.source, 59 "Defined assignment statement is not " 60 "allowed in a WORKSHARE construct"_err_en_US); 61 } 62 return true; 63 } 64 65 bool Pre(const parser::Expr &expr) { 66 if (const auto *e{GetExpr(expr)}) { 67 for (const Symbol &symbol : evaluate::CollectSymbols(*e)) { 68 const Symbol &root{GetAssociationRoot(symbol)}; 69 if (IsFunction(root) && 70 !(root.attrs().test(Attr::ELEMENTAL) || 71 root.attrs().test(Attr::INTRINSIC))) { 72 context_.Say(expr.source, 73 "User defined non-ELEMENTAL function " 74 "'%s' is not allowed in a WORKSHARE construct"_err_en_US, 75 root.name()); 76 } 77 } 78 } 79 return false; 80 } 81 82 private: 83 SemanticsContext &context_; 84 parser::CharBlock source_; 85 }; 86 87 class OmpCycleChecker { 88 public: 89 OmpCycleChecker(SemanticsContext &context, std::int64_t cycleLevel) 90 : context_{context}, cycleLevel_{cycleLevel} {} 91 92 template <typename T> bool Pre(const T &) { return true; } 93 template <typename T> void Post(const T &) {} 94 95 bool Pre(const parser::DoConstruct &dc) { 96 cycleLevel_--; 97 const auto &labelName{std::get<0>(std::get<0>(dc.t).statement.t)}; 98 if (labelName) { 99 labelNamesandLevels_.emplace(labelName.value().ToString(), cycleLevel_); 100 } 101 return true; 102 } 103 104 bool Pre(const parser::CycleStmt &cyclestmt) { 105 std::map<std::string, std::int64_t>::iterator it; 106 bool err{false}; 107 if (cyclestmt.v) { 108 it = labelNamesandLevels_.find(cyclestmt.v->source.ToString()); 109 err = (it != labelNamesandLevels_.end() && it->second > 0); 110 } 111 if (cycleLevel_ > 0 || err) { 112 context_.Say(*cycleSource_, 113 "CYCLE statement to non-innermost associated loop of an OpenMP DO construct"_err_en_US); 114 } 115 return true; 116 } 117 118 bool Pre(const parser::Statement<parser::ActionStmt> &actionstmt) { 119 cycleSource_ = &actionstmt.source; 120 return true; 121 } 122 123 private: 124 SemanticsContext &context_; 125 const parser::CharBlock *cycleSource_; 126 std::int64_t cycleLevel_; 127 std::map<std::string, std::int64_t> labelNamesandLevels_; 128 }; 129 130 bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet &set) { 131 // Definition of close nesting: 132 // 133 // `A region nested inside another region with no parallel region nested 134 // between them` 135 // 136 // Examples: 137 // non-parallel construct 1 138 // non-parallel construct 2 139 // parallel construct 140 // construct 3 141 // In the above example, construct 3 is NOT closely nested inside construct 1 142 // or 2 143 // 144 // non-parallel construct 1 145 // non-parallel construct 2 146 // construct 3 147 // In the above example, construct 3 is closely nested inside BOTH construct 1 148 // and 2 149 // 150 // Algorithm: 151 // Starting from the parent context, Check in a bottom-up fashion, each level 152 // of the context stack. If we have a match for one of the (supplied) 153 // violating directives, `close nesting` is satisfied. If no match is there in 154 // the entire stack, `close nesting` is not satisfied. If at any level, a 155 // `parallel` region is found, `close nesting` is not satisfied. 156 157 if (CurrentDirectiveIsNested()) { 158 int index = dirContext_.size() - 2; 159 while (index != -1) { 160 if (set.test(dirContext_[index].directive)) { 161 return true; 162 } else if (llvm::omp::parallelSet.test(dirContext_[index].directive)) { 163 return false; 164 } 165 index--; 166 } 167 } 168 return false; 169 } 170 171 bool OmpStructureChecker::HasInvalidWorksharingNesting( 172 const parser::CharBlock &source, const OmpDirectiveSet &set) { 173 // set contains all the invalid closely nested directives 174 // for the given directive (`source` here) 175 if (IsCloselyNestedRegion(set)) { 176 context_.Say(source, 177 "A worksharing region may not be closely nested inside a " 178 "worksharing, explicit task, taskloop, critical, ordered, atomic, or " 179 "master region"_err_en_US); 180 return true; 181 } 182 return false; 183 } 184 185 void OmpStructureChecker::HasInvalidDistributeNesting( 186 const parser::OpenMPLoopConstruct &x) { 187 bool violation{false}; 188 189 OmpDirectiveSet distributeSet{llvm::omp::Directive::OMPD_distribute, 190 llvm::omp::Directive::OMPD_distribute_parallel_do, 191 llvm::omp::Directive::OMPD_distribute_parallel_do_simd, 192 llvm::omp::Directive::OMPD_distribute_parallel_for, 193 llvm::omp::Directive::OMPD_distribute_parallel_for_simd, 194 llvm::omp::Directive::OMPD_distribute_simd}; 195 196 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 197 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 198 if (distributeSet.test(beginDir.v)) { 199 // `distribute` region has to be nested 200 if (!CurrentDirectiveIsNested()) { 201 violation = true; 202 } else { 203 // `distribute` region has to be strictly nested inside `teams` 204 if (!llvm::omp::teamSet.test(GetContextParent().directive)) { 205 violation = true; 206 } 207 } 208 } 209 if (violation) { 210 context_.Say(beginDir.source, 211 "`DISTRIBUTE` region has to be strictly nested inside `TEAMS` region."_err_en_US); 212 } 213 } 214 215 void OmpStructureChecker::HasInvalidTeamsNesting( 216 const llvm::omp::Directive &dir, const parser::CharBlock &source) { 217 OmpDirectiveSet allowedSet{llvm::omp::Directive::OMPD_parallel, 218 llvm::omp::Directive::OMPD_parallel_do, 219 llvm::omp::Directive::OMPD_parallel_do_simd, 220 llvm::omp::Directive::OMPD_parallel_for, 221 llvm::omp::Directive::OMPD_parallel_for_simd, 222 llvm::omp::Directive::OMPD_parallel_master, 223 llvm::omp::Directive::OMPD_parallel_master_taskloop, 224 llvm::omp::Directive::OMPD_parallel_master_taskloop_simd, 225 llvm::omp::Directive::OMPD_parallel_sections, 226 llvm::omp::Directive::OMPD_parallel_workshare, 227 llvm::omp::Directive::OMPD_distribute, 228 llvm::omp::Directive::OMPD_distribute_parallel_do, 229 llvm::omp::Directive::OMPD_distribute_parallel_do_simd, 230 llvm::omp::Directive::OMPD_distribute_parallel_for, 231 llvm::omp::Directive::OMPD_distribute_parallel_for_simd, 232 llvm::omp::Directive::OMPD_distribute_simd}; 233 234 if (!allowedSet.test(dir)) { 235 context_.Say(source, 236 "Only `DISTRIBUTE` or `PARALLEL` regions are allowed to be strictly nested inside `TEAMS` region."_err_en_US); 237 } 238 } 239 240 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) { 241 // Simd Construct with Ordered Construct Nesting check 242 // We cannot use CurrentDirectiveIsNested() here because 243 // PushContextAndClauseSets() has not been called yet, it is 244 // called individually for each construct. Therefore a 245 // dirContext_ size `1` means the current construct is nested 246 if (dirContext_.size() >= 1) { 247 if (GetSIMDNest() > 0) { 248 CheckSIMDNest(x); 249 } 250 } 251 } 252 253 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { 254 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 255 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 256 257 // check matching, End directive is optional 258 if (const auto &endLoopDir{ 259 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) { 260 const auto &endDir{ 261 std::get<parser::OmpLoopDirective>(endLoopDir.value().t)}; 262 263 CheckMatching<parser::OmpLoopDirective>(beginDir, endDir); 264 } 265 266 PushContextAndClauseSets(beginDir.source, beginDir.v); 267 if (llvm::omp::simdSet.test(GetContext().directive)) { 268 EnterSIMDNest(); 269 } 270 271 if (beginDir.v == llvm::omp::Directive::OMPD_do) { 272 // 2.7.1 do-clause -> private-clause | 273 // firstprivate-clause | 274 // lastprivate-clause | 275 // linear-clause | 276 // reduction-clause | 277 // schedule-clause | 278 // collapse-clause | 279 // ordered-clause 280 281 // nesting check 282 HasInvalidWorksharingNesting( 283 beginDir.source, llvm::omp::nestedWorkshareErrSet); 284 } 285 SetLoopInfo(x); 286 287 if (const auto &doConstruct{ 288 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 289 const auto &doBlock{std::get<parser::Block>(doConstruct->t)}; 290 CheckNoBranching(doBlock, beginDir.v, beginDir.source); 291 } 292 CheckDoWhile(x); 293 CheckLoopItrVariableIsInt(x); 294 CheckCycleConstraints(x); 295 HasInvalidDistributeNesting(x); 296 if (CurrentDirectiveIsNested() && 297 llvm::omp::teamSet.test(GetContextParent().directive)) { 298 HasInvalidTeamsNesting(beginDir.v, beginDir.source); 299 } 300 if ((beginDir.v == llvm::omp::Directive::OMPD_distribute_parallel_do_simd) || 301 (beginDir.v == llvm::omp::Directive::OMPD_distribute_simd)) { 302 CheckDistLinear(x); 303 } 304 } 305 const parser::Name OmpStructureChecker::GetLoopIndex( 306 const parser::DoConstruct *x) { 307 using Bounds = parser::LoopControl::Bounds; 308 return std::get<Bounds>(x->GetLoopControl()->u).name.thing; 309 } 310 void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) { 311 if (const auto &loopConstruct{ 312 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 313 const parser::DoConstruct *loop{&*loopConstruct}; 314 if (loop && loop->IsDoNormal()) { 315 const parser::Name &itrVal{GetLoopIndex(loop)}; 316 SetLoopIv(itrVal.symbol); 317 } 318 } 319 } 320 void OmpStructureChecker::CheckDoWhile(const parser::OpenMPLoopConstruct &x) { 321 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 322 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 323 if (beginDir.v == llvm::omp::Directive::OMPD_do) { 324 if (const auto &doConstruct{ 325 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 326 if (doConstruct.value().IsDoWhile()) { 327 const auto &doStmt{std::get<parser::Statement<parser::NonLabelDoStmt>>( 328 doConstruct.value().t)}; 329 context_.Say(doStmt.source, 330 "The DO loop cannot be a DO WHILE with DO directive."_err_en_US); 331 } 332 } 333 } 334 } 335 336 void OmpStructureChecker::CheckLoopItrVariableIsInt( 337 const parser::OpenMPLoopConstruct &x) { 338 if (const auto &loopConstruct{ 339 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 340 341 for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { 342 if (loop->IsDoNormal()) { 343 const parser::Name &itrVal{GetLoopIndex(loop)}; 344 if (itrVal.symbol) { 345 const auto *type{itrVal.symbol->GetType()}; 346 if (!type->IsNumeric(TypeCategory::Integer)) { 347 context_.Say(itrVal.source, 348 "The DO loop iteration" 349 " variable must be of the type integer."_err_en_US, 350 itrVal.ToString()); 351 } 352 } 353 } 354 // Get the next DoConstruct if block is not empty. 355 const auto &block{std::get<parser::Block>(loop->t)}; 356 const auto it{block.begin()}; 357 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) 358 : nullptr; 359 } 360 } 361 } 362 363 void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) { 364 // Check the following: 365 // The only OpenMP constructs that can be encountered during execution of 366 // a simd region are the `atomic` construct, the `loop` construct, the `simd` 367 // construct and the `ordered` construct with the `simd` clause. 368 // TODO: Expand the check to include `LOOP` construct as well when it is 369 // supported. 370 371 // Check if the parent context has the SIMD clause 372 // Please note that we use GetContext() instead of GetContextParent() 373 // because PushContextAndClauseSets() has not been called on the 374 // current context yet. 375 // TODO: Check for declare simd regions. 376 bool eligibleSIMD{false}; 377 std::visit(Fortran::common::visitors{ 378 // Allow `!$OMP ORDERED SIMD` 379 [&](const parser::OpenMPBlockConstruct &c) { 380 const auto &beginBlockDir{ 381 std::get<parser::OmpBeginBlockDirective>(c.t)}; 382 const auto &beginDir{ 383 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 384 if (beginDir.v == llvm::omp::Directive::OMPD_ordered) { 385 const auto &clauses{ 386 std::get<parser::OmpClauseList>(beginBlockDir.t)}; 387 for (const auto &clause : clauses.v) { 388 if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { 389 eligibleSIMD = true; 390 break; 391 } 392 } 393 } 394 }, 395 [&](const parser::OpenMPSimpleStandaloneConstruct &c) { 396 const auto &dir{ 397 std::get<parser::OmpSimpleStandaloneDirective>(c.t)}; 398 if (dir.v == llvm::omp::Directive::OMPD_ordered) { 399 const auto &clauses{std::get<parser::OmpClauseList>(c.t)}; 400 for (const auto &clause : clauses.v) { 401 if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { 402 eligibleSIMD = true; 403 break; 404 } 405 } 406 } 407 }, 408 // Allowing SIMD construct 409 [&](const parser::OpenMPLoopConstruct &c) { 410 const auto &beginLoopDir{ 411 std::get<parser::OmpBeginLoopDirective>(c.t)}; 412 const auto &beginDir{ 413 std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 414 if ((beginDir.v == llvm::omp::Directive::OMPD_simd) || 415 (beginDir.v == llvm::omp::Directive::OMPD_do_simd)) { 416 eligibleSIMD = true; 417 } 418 }, 419 [&](const parser::OpenMPAtomicConstruct &c) { 420 // Allow `!$OMP ATOMIC` 421 eligibleSIMD = true; 422 }, 423 [&](const auto &c) {}, 424 }, 425 c.u); 426 if (!eligibleSIMD) { 427 context_.Say(parser::FindSourceLocation(c), 428 "The only OpenMP constructs that can be encountered during execution " 429 "of a 'SIMD'" 430 " region are the `ATOMIC` construct, the `LOOP` construct, the `SIMD`" 431 " construct and the `ORDERED` construct with the `SIMD` clause."_err_en_US); 432 } 433 } 434 435 std::int64_t OmpStructureChecker::GetOrdCollapseLevel( 436 const parser::OpenMPLoopConstruct &x) { 437 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 438 const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; 439 std::int64_t orderedCollapseLevel{1}; 440 std::int64_t orderedLevel{0}; 441 std::int64_t collapseLevel{0}; 442 443 for (const auto &clause : clauseList.v) { 444 if (const auto *collapseClause{ 445 std::get_if<parser::OmpClause::Collapse>(&clause.u)}) { 446 if (const auto v{GetIntValue(collapseClause->v)}) { 447 collapseLevel = *v; 448 } 449 } 450 if (const auto *orderedClause{ 451 std::get_if<parser::OmpClause::Ordered>(&clause.u)}) { 452 if (const auto v{GetIntValue(orderedClause->v)}) { 453 orderedLevel = *v; 454 } 455 } 456 } 457 if (orderedLevel >= collapseLevel) { 458 orderedCollapseLevel = orderedLevel; 459 } else { 460 orderedCollapseLevel = collapseLevel; 461 } 462 return orderedCollapseLevel; 463 } 464 465 void OmpStructureChecker::CheckCycleConstraints( 466 const parser::OpenMPLoopConstruct &x) { 467 std::int64_t ordCollapseLevel{GetOrdCollapseLevel(x)}; 468 OmpCycleChecker ompCycleChecker{context_, ordCollapseLevel}; 469 parser::Walk(x, ompCycleChecker); 470 } 471 472 void OmpStructureChecker::CheckDistLinear( 473 const parser::OpenMPLoopConstruct &x) { 474 475 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 476 const auto &clauses{std::get<parser::OmpClauseList>(beginLoopDir.t)}; 477 478 semantics::UnorderedSymbolSet indexVars; 479 480 // Collect symbols of all the variables from linear clauses 481 for (const auto &clause : clauses.v) { 482 if (const auto *linearClause{ 483 std::get_if<parser::OmpClause::Linear>(&clause.u)}) { 484 485 std::list<parser::Name> values; 486 // Get the variant type 487 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>( 488 linearClause->v.u)) { 489 const auto &withM{ 490 std::get<parser::OmpLinearClause::WithModifier>(linearClause->v.u)}; 491 values = withM.names; 492 } else { 493 const auto &withOutM{std::get<parser::OmpLinearClause::WithoutModifier>( 494 linearClause->v.u)}; 495 values = withOutM.names; 496 } 497 for (auto const &v : values) { 498 indexVars.insert(*(v.symbol)); 499 } 500 } 501 } 502 503 if (!indexVars.empty()) { 504 // Get collapse level, if given, to find which loops are "associated." 505 std::int64_t collapseVal{GetOrdCollapseLevel(x)}; 506 // Include the top loop if no collapse is specified 507 if (collapseVal == 0) 508 collapseVal = 1; 509 510 // Match the loop index variables with the collected symbols from linear 511 // clauses. 512 if (const auto &loopConstruct{ 513 std::get<std::optional<parser::DoConstruct>>(x.t)}) { 514 for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) { 515 if (loop->IsDoNormal()) { 516 const parser::Name &itrVal{GetLoopIndex(loop)}; 517 if (itrVal.symbol) { 518 // Remove the symbol from the collcted set 519 indexVars.erase(*(itrVal.symbol)); 520 } 521 collapseVal--; 522 if (collapseVal == 0) 523 break; 524 } 525 // Get the next DoConstruct if block is not empty. 526 const auto &block{std::get<parser::Block>(loop->t)}; 527 const auto it{block.begin()}; 528 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it) 529 : nullptr; 530 } 531 } 532 533 // Show error for the remaining variables 534 for (auto var : indexVars) { 535 const Symbol &root{GetAssociationRoot(var)}; 536 context_.Say(parser::FindSourceLocation(x), 537 "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, 538 root.name()); 539 } 540 } 541 } 542 543 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) { 544 if (llvm::omp::simdSet.test(GetContext().directive)) { 545 ExitSIMDNest(); 546 } 547 dirContext_.pop_back(); 548 } 549 550 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) { 551 const auto &dir{std::get<parser::OmpLoopDirective>(x.t)}; 552 ResetPartialContext(dir.source); 553 switch (dir.v) { 554 // 2.7.1 end-do -> END DO [nowait-clause] 555 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause] 556 case llvm::omp::Directive::OMPD_do: 557 case llvm::omp::Directive::OMPD_do_simd: 558 SetClauseSets(dir.v); 559 break; 560 default: 561 // no clauses are allowed 562 break; 563 } 564 } 565 566 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { 567 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; 568 const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)}; 569 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 570 const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)}; 571 const parser::Block &block{std::get<parser::Block>(x.t)}; 572 573 CheckMatching<parser::OmpBlockDirective>(beginDir, endDir); 574 575 PushContextAndClauseSets(beginDir.source, beginDir.v); 576 577 if (CurrentDirectiveIsNested()) { 578 CheckIfDoOrderedClause(beginDir); 579 if (llvm::omp::teamSet.test(GetContextParent().directive)) { 580 HasInvalidTeamsNesting(beginDir.v, beginDir.source); 581 } 582 if (GetContext().directive == llvm::omp::Directive::OMPD_master) { 583 CheckMasterNesting(x); 584 } 585 } 586 587 CheckNoBranching(block, beginDir.v, beginDir.source); 588 589 switch (beginDir.v) { 590 case llvm::omp::OMPD_workshare: 591 case llvm::omp::OMPD_parallel_workshare: 592 CheckWorkshareBlockStmts(block, beginDir.source); 593 HasInvalidWorksharingNesting( 594 beginDir.source, llvm::omp::nestedWorkshareErrSet); 595 break; 596 case llvm::omp::Directive::OMPD_single: 597 // TODO: This check needs to be extended while implementing nesting of 598 // regions checks. 599 HasInvalidWorksharingNesting( 600 beginDir.source, llvm::omp::nestedWorkshareErrSet); 601 break; 602 default: 603 break; 604 } 605 } 606 607 void OmpStructureChecker::CheckMasterNesting( 608 const parser::OpenMPBlockConstruct &x) { 609 // A MASTER region may not be `closely nested` inside a worksharing, loop, 610 // task, taskloop, or atomic region. 611 // TODO: Expand the check to include `LOOP` construct as well when it is 612 // supported. 613 if (IsCloselyNestedRegion(llvm::omp::nestedMasterErrSet)) { 614 context_.Say(parser::FindSourceLocation(x), 615 "`MASTER` region may not be closely nested inside of `WORKSHARING`, " 616 "`LOOP`, `TASK`, `TASKLOOP`," 617 " or `ATOMIC` region."_err_en_US); 618 } 619 } 620 621 void OmpStructureChecker::CheckIfDoOrderedClause( 622 const parser::OmpBlockDirective &blkDirective) { 623 if (blkDirective.v == llvm::omp::OMPD_ordered) { 624 // Loops 625 if (llvm::omp::doSet.test(GetContextParent().directive) && 626 !FindClauseParent(llvm::omp::Clause::OMPC_ordered)) { 627 context_.Say(blkDirective.source, 628 "The ORDERED clause must be present on the loop" 629 " construct if any ORDERED region ever binds" 630 " to a loop region arising from the loop construct."_err_en_US); 631 } 632 // Other disallowed nestings, these directives do not support 633 // ordered clause in them, so no need to check 634 else if (IsCloselyNestedRegion(llvm::omp::nestedOrderedErrSet)) { 635 context_.Say(blkDirective.source, 636 "`ORDERED` region may not be closely nested inside of " 637 "`CRITICAL`, `ORDERED`, explicit `TASK` or `TASKLOOP` region."_err_en_US); 638 } 639 } 640 } 641 642 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) { 643 dirContext_.pop_back(); 644 } 645 646 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) { 647 const auto &beginSectionsDir{ 648 std::get<parser::OmpBeginSectionsDirective>(x.t)}; 649 const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)}; 650 const auto &beginDir{ 651 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 652 const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)}; 653 CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir); 654 655 PushContextAndClauseSets(beginDir.source, beginDir.v); 656 const auto §ionBlocks{std::get<parser::OmpSectionBlocks>(x.t)}; 657 for (const auto &block : sectionBlocks.v) { 658 CheckNoBranching(block, beginDir.v, beginDir.source); 659 } 660 HasInvalidWorksharingNesting( 661 beginDir.source, llvm::omp::nestedWorkshareErrSet); 662 } 663 664 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) { 665 dirContext_.pop_back(); 666 } 667 668 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) { 669 const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)}; 670 ResetPartialContext(dir.source); 671 switch (dir.v) { 672 // 2.7.2 end-sections -> END SECTIONS [nowait-clause] 673 case llvm::omp::Directive::OMPD_sections: 674 PushContextAndClauseSets( 675 dir.source, llvm::omp::Directive::OMPD_end_sections); 676 break; 677 default: 678 // no clauses are allowed 679 break; 680 } 681 } 682 683 // TODO: Verify the popping of dirContext requirement after nowait 684 // implementation, as there is an implicit barrier at the end of the worksharing 685 // constructs unless a nowait clause is specified. Only OMPD_end_sections is 686 // popped becuase it is pushed while entering the EndSectionsDirective. 687 void OmpStructureChecker::Leave(const parser::OmpEndSectionsDirective &x) { 688 if (GetContext().directive == llvm::omp::Directive::OMPD_end_sections) { 689 dirContext_.pop_back(); 690 } 691 } 692 693 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) { 694 const auto &dir{std::get<parser::Verbatim>(x.t)}; 695 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd); 696 } 697 698 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) { 699 dirContext_.pop_back(); 700 } 701 702 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) { 703 const auto &dir{std::get<parser::Verbatim>(x.t)}; 704 const auto &objectList{std::get<parser::OmpObjectList>(x.t)}; 705 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); 706 CheckIsVarPartOfAnotherVar(dir.source, objectList); 707 } 708 709 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) { 710 dirContext_.pop_back(); 711 } 712 713 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) { 714 const auto &dir{std::get<parser::Verbatim>(x.t)}; 715 PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target); 716 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)}; 717 if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) { 718 SetClauseSets(llvm::omp::Directive::OMPD_declare_target); 719 } 720 } 721 722 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) { 723 dirContext_.pop_back(); 724 } 725 726 void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) { 727 const auto &dir{std::get<parser::Verbatim>(x.t)}; 728 const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)}; 729 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate); 730 if (objectList) 731 CheckIsVarPartOfAnotherVar(dir.source, *objectList); 732 } 733 734 void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &) { 735 dirContext_.pop_back(); 736 } 737 738 void OmpStructureChecker::Enter( 739 const parser::OpenMPSimpleStandaloneConstruct &x) { 740 const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)}; 741 PushContextAndClauseSets(dir.source, dir.v); 742 } 743 744 void OmpStructureChecker::Leave( 745 const parser::OpenMPSimpleStandaloneConstruct &) { 746 dirContext_.pop_back(); 747 } 748 749 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) { 750 const auto &dir{std::get<parser::Verbatim>(x.t)}; 751 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_flush); 752 } 753 754 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) { 755 if (FindClause(llvm::omp::Clause::OMPC_acquire) || 756 FindClause(llvm::omp::Clause::OMPC_release) || 757 FindClause(llvm::omp::Clause::OMPC_acq_rel)) { 758 if (const auto &flushList{ 759 std::get<std::optional<parser::OmpObjectList>>(x.t)}) { 760 context_.Say(parser::FindSourceLocation(flushList), 761 "If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items " 762 "must not be specified on the FLUSH directive"_err_en_US); 763 } 764 } 765 dirContext_.pop_back(); 766 } 767 768 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) { 769 const auto &dir{std::get<parser::Verbatim>(x.t)}; 770 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel); 771 } 772 773 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) { 774 dirContext_.pop_back(); 775 } 776 777 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) { 778 const auto &dir{std::get<parser::OmpCriticalDirective>(x.t)}; 779 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_critical); 780 const auto &block{std::get<parser::Block>(x.t)}; 781 CheckNoBranching(block, llvm::omp::Directive::OMPD_critical, dir.source); 782 } 783 784 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) { 785 dirContext_.pop_back(); 786 } 787 788 void OmpStructureChecker::Enter( 789 const parser::OpenMPCancellationPointConstruct &x) { 790 const auto &dir{std::get<parser::Verbatim>(x.t)}; 791 PushContextAndClauseSets( 792 dir.source, llvm::omp::Directive::OMPD_cancellation_point); 793 } 794 795 void OmpStructureChecker::Leave( 796 const parser::OpenMPCancellationPointConstruct &) { 797 dirContext_.pop_back(); 798 } 799 800 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) { 801 const auto &dir{std::get<parser::OmpBlockDirective>(x.t)}; 802 ResetPartialContext(dir.source); 803 switch (dir.v) { 804 // 2.7.3 end-single-clause -> copyprivate-clause | 805 // nowait-clause 806 case llvm::omp::Directive::OMPD_single: 807 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_single); 808 break; 809 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause] 810 case llvm::omp::Directive::OMPD_workshare: 811 PushContextAndClauseSets( 812 dir.source, llvm::omp::Directive::OMPD_end_workshare); 813 break; 814 default: 815 // no clauses are allowed 816 break; 817 } 818 } 819 820 // TODO: Verify the popping of dirContext requirement after nowait 821 // implementation, as there is an implicit barrier at the end of the worksharing 822 // constructs unless a nowait clause is specified. Only OMPD_end_single and 823 // end_workshareare popped as they are pushed while entering the 824 // EndBlockDirective. 825 void OmpStructureChecker::Leave(const parser::OmpEndBlockDirective &x) { 826 if ((GetContext().directive == llvm::omp::Directive::OMPD_end_single) || 827 (GetContext().directive == llvm::omp::Directive::OMPD_end_workshare)) { 828 dirContext_.pop_back(); 829 } 830 } 831 832 void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) { 833 std::visit( 834 common::visitors{ 835 [&](const auto &someAtomicConstruct) { 836 const auto &dir{std::get<parser::Verbatim>(someAtomicConstruct.t)}; 837 PushContextAndClauseSets( 838 dir.source, llvm::omp::Directive::OMPD_atomic); 839 }, 840 }, 841 x.u); 842 } 843 844 void OmpStructureChecker::Leave(const parser::OpenMPAtomicConstruct &) { 845 dirContext_.pop_back(); 846 } 847 848 // Clauses 849 // Mainly categorized as 850 // 1. Checks on 'OmpClauseList' from 'parse-tree.h'. 851 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h. 852 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h. 853 854 void OmpStructureChecker::Leave(const parser::OmpClauseList &) { 855 // 2.7 Loop Construct Restriction 856 if (llvm::omp::doSet.test(GetContext().directive)) { 857 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) { 858 // only one schedule clause is allowed 859 const auto &schedClause{std::get<parser::OmpClause::Schedule>(clause->u)}; 860 if (ScheduleModifierHasType(schedClause.v, 861 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 862 if (FindClause(llvm::omp::Clause::OMPC_ordered)) { 863 context_.Say(clause->source, 864 "The NONMONOTONIC modifier cannot be specified " 865 "if an ORDERED clause is specified"_err_en_US); 866 } 867 if (ScheduleModifierHasType(schedClause.v, 868 parser::OmpScheduleModifierType::ModType::Monotonic)) { 869 context_.Say(clause->source, 870 "The MONOTONIC and NONMONOTONIC modifiers " 871 "cannot be both specified"_err_en_US); 872 } 873 } 874 } 875 876 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) { 877 // only one ordered clause is allowed 878 const auto &orderedClause{ 879 std::get<parser::OmpClause::Ordered>(clause->u)}; 880 881 if (orderedClause.v) { 882 CheckNotAllowedIfClause( 883 llvm::omp::Clause::OMPC_ordered, {llvm::omp::Clause::OMPC_linear}); 884 885 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) { 886 const auto &collapseClause{ 887 std::get<parser::OmpClause::Collapse>(clause2->u)}; 888 // ordered and collapse both have parameters 889 if (const auto orderedValue{GetIntValue(orderedClause.v)}) { 890 if (const auto collapseValue{GetIntValue(collapseClause.v)}) { 891 if (*orderedValue > 0 && *orderedValue < *collapseValue) { 892 context_.Say(clause->source, 893 "The parameter of the ORDERED clause must be " 894 "greater than or equal to " 895 "the parameter of the COLLAPSE clause"_err_en_US); 896 } 897 } 898 } 899 } 900 } 901 902 // TODO: ordered region binding check (requires nesting implementation) 903 } 904 } // doSet 905 906 // 2.8.1 Simd Construct Restriction 907 if (llvm::omp::simdSet.test(GetContext().directive)) { 908 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) { 909 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) { 910 const auto &simdlenClause{ 911 std::get<parser::OmpClause::Simdlen>(clause->u)}; 912 const auto &safelenClause{ 913 std::get<parser::OmpClause::Safelen>(clause2->u)}; 914 // simdlen and safelen both have parameters 915 if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) { 916 if (const auto safelenValue{GetIntValue(safelenClause.v)}) { 917 if (*safelenValue > 0 && *simdlenValue > *safelenValue) { 918 context_.Say(clause->source, 919 "The parameter of the SIMDLEN clause must be less than or " 920 "equal to the parameter of the SAFELEN clause"_err_en_US); 921 } 922 } 923 } 924 } 925 } 926 // A list-item cannot appear in more than one aligned clause 927 semantics::UnorderedSymbolSet alignedVars; 928 auto clauseAll = FindClauses(llvm::omp::Clause::OMPC_aligned); 929 for (auto itr = clauseAll.first; itr != clauseAll.second; ++itr) { 930 const auto &alignedClause{ 931 std::get<parser::OmpClause::Aligned>(itr->second->u)}; 932 const auto &alignedNameList{ 933 std::get<std::list<parser::Name>>(alignedClause.v.t)}; 934 for (auto const &var : alignedNameList) { 935 if (alignedVars.count(*(var.symbol)) == 1) { 936 context_.Say(itr->second->source, 937 "List item '%s' present at multiple ALIGNED clauses"_err_en_US, 938 var.ToString()); 939 break; 940 } 941 alignedVars.insert(*(var.symbol)); 942 } 943 } 944 } // SIMD 945 946 // 2.7.3 Single Construct Restriction 947 if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) { 948 CheckNotAllowedIfClause( 949 llvm::omp::Clause::OMPC_copyprivate, {llvm::omp::Clause::OMPC_nowait}); 950 } 951 952 CheckRequireAtLeastOneOf(); 953 } 954 955 void OmpStructureChecker::Enter(const parser::OmpClause &x) { 956 SetContextClause(x); 957 } 958 959 // Following clauses do not have a separate node in parse-tree.h. 960 CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel) 961 CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire) 962 CHECK_SIMPLE_CLAUSE(AtomicDefaultMemOrder, OMPC_atomic_default_mem_order) 963 CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity) 964 CHECK_SIMPLE_CLAUSE(Allocate, OMPC_allocate) 965 CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture) 966 CHECK_SIMPLE_CLAUSE(Copyin, OMPC_copyin) 967 CHECK_SIMPLE_CLAUSE(Default, OMPC_default) 968 CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj) 969 CHECK_SIMPLE_CLAUSE(Destroy, OMPC_destroy) 970 CHECK_SIMPLE_CLAUSE(Detach, OMPC_detach) 971 CHECK_SIMPLE_CLAUSE(Device, OMPC_device) 972 CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type) 973 CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule) 974 CHECK_SIMPLE_CLAUSE(DynamicAllocators, OMPC_dynamic_allocators) 975 CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive) 976 CHECK_SIMPLE_CLAUSE(Final, OMPC_final) 977 CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush) 978 CHECK_SIMPLE_CLAUSE(From, OMPC_from) 979 CHECK_SIMPLE_CLAUSE(Full, OMPC_full) 980 CHECK_SIMPLE_CLAUSE(Hint, OMPC_hint) 981 CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction) 982 CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive) 983 CHECK_SIMPLE_CLAUSE(Match, OMPC_match) 984 CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal) 985 CHECK_SIMPLE_CLAUSE(Order, OMPC_order) 986 CHECK_SIMPLE_CLAUSE(Read, OMPC_read) 987 CHECK_SIMPLE_CLAUSE(ReverseOffload, OMPC_reverse_offload) 988 CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate) 989 CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads) 990 CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) 991 CHECK_SIMPLE_CLAUSE(IsDevicePtr, OMPC_is_device_ptr) 992 CHECK_SIMPLE_CLAUSE(Link, OMPC_link) 993 CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable) 994 CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup) 995 CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch) 996 CHECK_SIMPLE_CLAUSE(Nowait, OMPC_nowait) 997 CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial) 998 CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind) 999 CHECK_SIMPLE_CLAUSE(Release, OMPC_release) 1000 CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed) 1001 CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst) 1002 CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd) 1003 CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes) 1004 CHECK_SIMPLE_CLAUSE(TaskReduction, OMPC_task_reduction) 1005 CHECK_SIMPLE_CLAUSE(To, OMPC_to) 1006 CHECK_SIMPLE_CLAUSE(UnifiedAddress, OMPC_unified_address) 1007 CHECK_SIMPLE_CLAUSE(UnifiedSharedMemory, OMPC_unified_shared_memory) 1008 CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform) 1009 CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown) 1010 CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied) 1011 CHECK_SIMPLE_CLAUSE(UseDevicePtr, OMPC_use_device_ptr) 1012 CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators) 1013 CHECK_SIMPLE_CLAUSE(Update, OMPC_update) 1014 CHECK_SIMPLE_CLAUSE(UseDeviceAddr, OMPC_use_device_addr) 1015 CHECK_SIMPLE_CLAUSE(Write, OMPC_write) 1016 CHECK_SIMPLE_CLAUSE(Init, OMPC_init) 1017 CHECK_SIMPLE_CLAUSE(Use, OMPC_use) 1018 CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants) 1019 CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext) 1020 CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter) 1021 1022 CHECK_REQ_SCALAR_INT_CLAUSE(Allocator, OMPC_allocator) 1023 CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize) 1024 CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks) 1025 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams) 1026 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads) 1027 CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority) 1028 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit) 1029 1030 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse) 1031 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen) 1032 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen) 1033 1034 // Restrictions specific to each clause are implemented apart from the 1035 // generalized restrictions. 1036 void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) { 1037 CheckAllowed(llvm::omp::Clause::OMPC_reduction); 1038 if (CheckReductionOperators(x)) { 1039 CheckReductionTypeList(x); 1040 } 1041 } 1042 bool OmpStructureChecker::CheckReductionOperators( 1043 const parser::OmpClause::Reduction &x) { 1044 1045 const auto &definedOp{std::get<0>(x.v.t)}; 1046 bool ok = false; 1047 std::visit( 1048 common::visitors{ 1049 [&](const parser::DefinedOperator &dOpr) { 1050 const auto &intrinsicOp{ 1051 std::get<parser::DefinedOperator::IntrinsicOperator>(dOpr.u)}; 1052 ok = CheckIntrinsicOperator(intrinsicOp); 1053 }, 1054 [&](const parser::ProcedureDesignator &procD) { 1055 const parser::Name *name{std::get_if<parser::Name>(&procD.u)}; 1056 if (name) { 1057 if (name->source == "max" || name->source == "min" || 1058 name->source == "iand" || name->source == "ior" || 1059 name->source == "ieor") { 1060 ok = true; 1061 } else { 1062 context_.Say(GetContext().clauseSource, 1063 "Invalid reduction identifier in REDUCTION clause."_err_en_US, 1064 ContextDirectiveAsFortran()); 1065 } 1066 } 1067 }, 1068 }, 1069 definedOp.u); 1070 1071 return ok; 1072 } 1073 bool OmpStructureChecker::CheckIntrinsicOperator( 1074 const parser::DefinedOperator::IntrinsicOperator &op) { 1075 1076 switch (op) { 1077 case parser::DefinedOperator::IntrinsicOperator::Add: 1078 case parser::DefinedOperator::IntrinsicOperator::Subtract: 1079 case parser::DefinedOperator::IntrinsicOperator::Multiply: 1080 case parser::DefinedOperator::IntrinsicOperator::AND: 1081 case parser::DefinedOperator::IntrinsicOperator::OR: 1082 case parser::DefinedOperator::IntrinsicOperator::EQV: 1083 case parser::DefinedOperator::IntrinsicOperator::NEQV: 1084 return true; 1085 default: 1086 context_.Say(GetContext().clauseSource, 1087 "Invalid reduction operator in REDUCTION clause."_err_en_US, 1088 ContextDirectiveAsFortran()); 1089 } 1090 return false; 1091 } 1092 1093 void OmpStructureChecker::CheckReductionTypeList( 1094 const parser::OmpClause::Reduction &x) { 1095 const auto &ompObjectList{std::get<parser::OmpObjectList>(x.v.t)}; 1096 CheckIntentInPointerAndDefinable( 1097 ompObjectList, llvm::omp::Clause::OMPC_reduction); 1098 CheckReductionArraySection(ompObjectList); 1099 CheckMultipleAppearanceAcrossContext(ompObjectList); 1100 } 1101 1102 void OmpStructureChecker::CheckIntentInPointerAndDefinable( 1103 const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) { 1104 for (const auto &ompObject : objectList.v) { 1105 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1106 if (const auto *symbol{name->symbol}) { 1107 if (IsPointer(symbol->GetUltimate()) && 1108 IsIntentIn(symbol->GetUltimate())) { 1109 context_.Say(GetContext().clauseSource, 1110 "Pointer '%s' with the INTENT(IN) attribute may not appear " 1111 "in a %s clause"_err_en_US, 1112 symbol->name(), 1113 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1114 } 1115 if (auto msg{ 1116 WhyNotModifiable(*symbol, context_.FindScope(name->source))}) { 1117 context_.Say(GetContext().clauseSource, 1118 "Variable '%s' on the %s clause is not definable"_err_en_US, 1119 symbol->name(), 1120 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1121 } 1122 } 1123 } 1124 } 1125 } 1126 1127 void OmpStructureChecker::CheckReductionArraySection( 1128 const parser::OmpObjectList &ompObjectList) { 1129 for (const auto &ompObject : ompObjectList.v) { 1130 if (const auto *dataRef{parser::Unwrap<parser::DataRef>(ompObject)}) { 1131 if (const auto *arrayElement{ 1132 parser::Unwrap<parser::ArrayElement>(ompObject)}) { 1133 if (arrayElement) { 1134 CheckArraySection(*arrayElement, GetLastName(*dataRef), 1135 llvm::omp::Clause::OMPC_reduction); 1136 } 1137 } 1138 } 1139 } 1140 } 1141 1142 void OmpStructureChecker::CheckMultipleAppearanceAcrossContext( 1143 const parser::OmpObjectList &redObjectList) { 1144 // TODO: Verify the assumption here that the immediately enclosing region is 1145 // the parallel region to which the worksharing construct having reduction 1146 // binds to. 1147 if (auto *enclosingContext{GetEnclosingDirContext()}) { 1148 for (auto it : enclosingContext->clauseInfo) { 1149 llvmOmpClause type = it.first; 1150 const auto *clause = it.second; 1151 if (llvm::omp::privateReductionSet.test(type)) { 1152 if (const auto *objList{GetOmpObjectList(*clause)}) { 1153 for (const auto &ompObject : objList->v) { 1154 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1155 if (const auto *symbol{name->symbol}) { 1156 for (const auto &redOmpObject : redObjectList.v) { 1157 if (const auto *rname{ 1158 parser::Unwrap<parser::Name>(redOmpObject)}) { 1159 if (const auto *rsymbol{rname->symbol}) { 1160 if (rsymbol->name() == symbol->name()) { 1161 context_.Say(GetContext().clauseSource, 1162 "%s variable '%s' is %s in outer context must" 1163 " be shared in the parallel regions to which any" 1164 " of the worksharing regions arising from the " 1165 "worksharing" 1166 " construct bind."_err_en_US, 1167 parser::ToUpperCaseLetters( 1168 getClauseName(llvm::omp::Clause::OMPC_reduction) 1169 .str()), 1170 symbol->name(), 1171 parser::ToUpperCaseLetters( 1172 getClauseName(type).str())); 1173 } 1174 } 1175 } 1176 } 1177 } 1178 } 1179 } 1180 } 1181 } 1182 } 1183 } 1184 } 1185 1186 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) { 1187 CheckAllowed(llvm::omp::Clause::OMPC_ordered); 1188 // the parameter of ordered clause is optional 1189 if (const auto &expr{x.v}) { 1190 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr); 1191 // 2.8.3 Loop SIMD Construct Restriction 1192 if (llvm::omp::doSimdSet.test(GetContext().directive)) { 1193 context_.Say(GetContext().clauseSource, 1194 "No ORDERED clause with a parameter can be specified " 1195 "on the %s directive"_err_en_US, 1196 ContextDirectiveAsFortran()); 1197 } 1198 } 1199 } 1200 1201 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) { 1202 CheckAllowed(llvm::omp::Clause::OMPC_shared); 1203 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v); 1204 } 1205 void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) { 1206 CheckAllowed(llvm::omp::Clause::OMPC_private); 1207 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v); 1208 CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_private); 1209 } 1210 1211 bool OmpStructureChecker::IsDataRefTypeParamInquiry( 1212 const parser::DataRef *dataRef) { 1213 bool dataRefIsTypeParamInquiry{false}; 1214 if (const auto *structComp{ 1215 parser::Unwrap<parser::StructureComponent>(dataRef)}) { 1216 if (const auto *compSymbol{structComp->component.symbol}) { 1217 if (const auto *compSymbolMiscDetails{ 1218 std::get_if<MiscDetails>(&compSymbol->details())}) { 1219 const auto detailsKind = compSymbolMiscDetails->kind(); 1220 dataRefIsTypeParamInquiry = 1221 (detailsKind == MiscDetails::Kind::KindParamInquiry || 1222 detailsKind == MiscDetails::Kind::LenParamInquiry); 1223 } else if (compSymbol->has<TypeParamDetails>()) { 1224 dataRefIsTypeParamInquiry = true; 1225 } 1226 } 1227 } 1228 return dataRefIsTypeParamInquiry; 1229 } 1230 1231 void OmpStructureChecker::CheckIsVarPartOfAnotherVar( 1232 const parser::CharBlock &source, const parser::OmpObjectList &objList) { 1233 1234 for (const auto &ompObject : objList.v) { 1235 std::visit( 1236 common::visitors{ 1237 [&](const parser::Designator &designator) { 1238 if (const auto *dataRef{ 1239 std::get_if<parser::DataRef>(&designator.u)}) { 1240 if (IsDataRefTypeParamInquiry(dataRef)) { 1241 context_.Say(source, 1242 "A type parameter inquiry cannot appear in an ALLOCATE directive"_err_en_US); 1243 } else if (parser::Unwrap<parser::StructureComponent>( 1244 ompObject) || 1245 parser::Unwrap<parser::ArrayElement>(ompObject)) { 1246 context_.Say(source, 1247 "A variable that is part of another variable (as an " 1248 "array or structure element)" 1249 " cannot appear in a PRIVATE or SHARED clause or on the ALLOCATE directive."_err_en_US); 1250 } 1251 } 1252 }, 1253 [&](const parser::Name &name) {}, 1254 }, 1255 ompObject.u); 1256 } 1257 } 1258 1259 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) { 1260 CheckAllowed(llvm::omp::Clause::OMPC_firstprivate); 1261 CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate, x.v); 1262 1263 SymbolSourceMap currSymbols; 1264 GetSymbolsInObjectList(x.v, currSymbols); 1265 1266 DirectivesClauseTriple dirClauseTriple; 1267 // Check firstprivate variables in worksharing constructs 1268 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do, 1269 std::make_pair( 1270 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1271 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections, 1272 std::make_pair( 1273 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1274 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_single, 1275 std::make_pair( 1276 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1277 // Check firstprivate variables in distribute construct 1278 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute, 1279 std::make_pair( 1280 llvm::omp::Directive::OMPD_teams, llvm::omp::privateReductionSet)); 1281 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute, 1282 std::make_pair(llvm::omp::Directive::OMPD_target_teams, 1283 llvm::omp::privateReductionSet)); 1284 // Check firstprivate variables in task and taskloop constructs 1285 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_task, 1286 std::make_pair(llvm::omp::Directive::OMPD_parallel, 1287 OmpClauseSet{llvm::omp::Clause::OMPC_reduction})); 1288 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_taskloop, 1289 std::make_pair(llvm::omp::Directive::OMPD_parallel, 1290 OmpClauseSet{llvm::omp::Clause::OMPC_reduction})); 1291 1292 CheckPrivateSymbolsInOuterCxt( 1293 currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_firstprivate); 1294 } 1295 1296 void OmpStructureChecker::CheckIsLoopIvPartOfClause( 1297 llvmOmpClause clause, const parser::OmpObjectList &ompObjectList) { 1298 for (const auto &ompObject : ompObjectList.v) { 1299 if (const parser::Name * name{parser::Unwrap<parser::Name>(ompObject)}) { 1300 if (name->symbol == GetContext().loopIV) { 1301 context_.Say(name->source, 1302 "DO iteration variable %s is not allowed in %s clause."_err_en_US, 1303 name->ToString(), 1304 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1305 } 1306 } 1307 } 1308 } 1309 // Following clauses have a seperate node in parse-tree.h. 1310 // Atomic-clause 1311 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead, OMPC_read) 1312 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicWrite, OMPC_write) 1313 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicUpdate, OMPC_update) 1314 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicCapture, OMPC_capture) 1315 1316 void OmpStructureChecker::Leave(const parser::OmpAtomicRead &) { 1317 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_read, 1318 {llvm::omp::Clause::OMPC_release, llvm::omp::Clause::OMPC_acq_rel}); 1319 } 1320 void OmpStructureChecker::Leave(const parser::OmpAtomicWrite &) { 1321 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_write, 1322 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel}); 1323 } 1324 void OmpStructureChecker::Leave(const parser::OmpAtomicUpdate &) { 1325 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_update, 1326 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel}); 1327 } 1328 // OmpAtomic node represents atomic directive without atomic-clause. 1329 // atomic-clause - READ,WRITE,UPDATE,CAPTURE. 1330 void OmpStructureChecker::Leave(const parser::OmpAtomic &) { 1331 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acquire)}) { 1332 context_.Say(clause->source, 1333 "Clause ACQUIRE is not allowed on the ATOMIC directive"_err_en_US); 1334 } 1335 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acq_rel)}) { 1336 context_.Say(clause->source, 1337 "Clause ACQ_REL is not allowed on the ATOMIC directive"_err_en_US); 1338 } 1339 } 1340 // Restrictions specific to each clause are implemented apart from the 1341 // generalized restrictions. 1342 void OmpStructureChecker::Enter(const parser::OmpClause::Aligned &x) { 1343 CheckAllowed(llvm::omp::Clause::OMPC_aligned); 1344 1345 if (const auto &expr{ 1346 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.v.t)}) { 1347 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_aligned, *expr); 1348 } 1349 // 2.8.1 TODO: list-item attribute check 1350 } 1351 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) { 1352 CheckAllowed(llvm::omp::Clause::OMPC_defaultmap); 1353 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory; 1354 if (!std::get<std::optional<VariableCategory>>(x.v.t)) { 1355 context_.Say(GetContext().clauseSource, 1356 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP " 1357 "clause"_err_en_US); 1358 } 1359 } 1360 void OmpStructureChecker::Enter(const parser::OmpClause::If &x) { 1361 CheckAllowed(llvm::omp::Clause::OMPC_if); 1362 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier; 1363 static std::unordered_map<dirNameModifier, OmpDirectiveSet> 1364 dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet}, 1365 {dirNameModifier::Target, llvm::omp::targetSet}, 1366 {dirNameModifier::TargetEnterData, 1367 {llvm::omp::Directive::OMPD_target_enter_data}}, 1368 {dirNameModifier::TargetExitData, 1369 {llvm::omp::Directive::OMPD_target_exit_data}}, 1370 {dirNameModifier::TargetData, 1371 {llvm::omp::Directive::OMPD_target_data}}, 1372 {dirNameModifier::TargetUpdate, 1373 {llvm::omp::Directive::OMPD_target_update}}, 1374 {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}}, 1375 {dirNameModifier::Taskloop, llvm::omp::taskloopSet}}; 1376 if (const auto &directiveName{ 1377 std::get<std::optional<dirNameModifier>>(x.v.t)}) { 1378 auto search{dirNameModifierMap.find(*directiveName)}; 1379 if (search == dirNameModifierMap.end() || 1380 !search->second.test(GetContext().directive)) { 1381 context_ 1382 .Say(GetContext().clauseSource, 1383 "Unmatched directive name modifier %s on the IF clause"_err_en_US, 1384 parser::ToUpperCaseLetters( 1385 parser::OmpIfClause::EnumToString(*directiveName))) 1386 .Attach( 1387 GetContext().directiveSource, "Cannot apply to directive"_en_US); 1388 } 1389 } 1390 } 1391 1392 void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) { 1393 CheckAllowed(llvm::omp::Clause::OMPC_linear); 1394 1395 // 2.7 Loop Construct Restriction 1396 if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) { 1397 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.v.u)) { 1398 context_.Say(GetContext().clauseSource, 1399 "A modifier may not be specified in a LINEAR clause " 1400 "on the %s directive"_err_en_US, 1401 ContextDirectiveAsFortran()); 1402 } 1403 } 1404 } 1405 1406 void OmpStructureChecker::CheckAllowedMapTypes( 1407 const parser::OmpMapType::Type &type, 1408 const std::list<parser::OmpMapType::Type> &allowedMapTypeList) { 1409 const auto found{std::find( 1410 std::begin(allowedMapTypeList), std::end(allowedMapTypeList), type)}; 1411 if (found == std::end(allowedMapTypeList)) { 1412 std::string commaSeperatedMapTypes; 1413 llvm::interleave( 1414 allowedMapTypeList.begin(), allowedMapTypeList.end(), 1415 [&](const parser::OmpMapType::Type &mapType) { 1416 commaSeperatedMapTypes.append(parser::ToUpperCaseLetters( 1417 parser::OmpMapType::EnumToString(mapType))); 1418 }, 1419 [&] { commaSeperatedMapTypes.append(", "); }); 1420 context_.Say(GetContext().clauseSource, 1421 "Only the %s map types are permitted " 1422 "for MAP clauses on the %s directive"_err_en_US, 1423 commaSeperatedMapTypes, ContextDirectiveAsFortran()); 1424 } 1425 } 1426 1427 void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) { 1428 CheckAllowed(llvm::omp::Clause::OMPC_map); 1429 1430 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.v.t)}) { 1431 using Type = parser::OmpMapType::Type; 1432 const Type &type{std::get<Type>(maptype->t)}; 1433 switch (GetContext().directive) { 1434 case llvm::omp::Directive::OMPD_target: 1435 case llvm::omp::Directive::OMPD_target_teams: 1436 case llvm::omp::Directive::OMPD_target_teams_distribute: 1437 case llvm::omp::Directive::OMPD_target_teams_distribute_simd: 1438 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: 1439 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: 1440 case llvm::omp::Directive::OMPD_target_data: 1441 CheckAllowedMapTypes( 1442 type, {Type::To, Type::From, Type::Tofrom, Type::Alloc}); 1443 break; 1444 case llvm::omp::Directive::OMPD_target_enter_data: 1445 CheckAllowedMapTypes(type, {Type::To, Type::Alloc}); 1446 break; 1447 case llvm::omp::Directive::OMPD_target_exit_data: 1448 CheckAllowedMapTypes(type, {Type::From, Type::Release, Type::Delete}); 1449 break; 1450 default: 1451 break; 1452 } 1453 } 1454 } 1455 1456 bool OmpStructureChecker::ScheduleModifierHasType( 1457 const parser::OmpScheduleClause &x, 1458 const parser::OmpScheduleModifierType::ModType &type) { 1459 const auto &modifier{ 1460 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)}; 1461 if (modifier) { 1462 const auto &modType1{ 1463 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)}; 1464 const auto &modType2{ 1465 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>( 1466 modifier->t)}; 1467 if (modType1.v.v == type || (modType2 && modType2->v.v == type)) { 1468 return true; 1469 } 1470 } 1471 return false; 1472 } 1473 void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) { 1474 CheckAllowed(llvm::omp::Clause::OMPC_schedule); 1475 const parser::OmpScheduleClause &scheduleClause = x.v; 1476 1477 // 2.7 Loop Construct Restriction 1478 if (llvm::omp::doSet.test(GetContext().directive)) { 1479 const auto &kind{std::get<1>(scheduleClause.t)}; 1480 const auto &chunk{std::get<2>(scheduleClause.t)}; 1481 if (chunk) { 1482 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime || 1483 kind == parser::OmpScheduleClause::ScheduleType::Auto) { 1484 context_.Say(GetContext().clauseSource, 1485 "When SCHEDULE clause has %s specified, " 1486 "it must not have chunk size specified"_err_en_US, 1487 parser::ToUpperCaseLetters( 1488 parser::OmpScheduleClause::EnumToString(kind))); 1489 } 1490 if (const auto &chunkExpr{std::get<std::optional<parser::ScalarIntExpr>>( 1491 scheduleClause.t)}) { 1492 RequiresPositiveParameter( 1493 llvm::omp::Clause::OMPC_schedule, *chunkExpr, "chunk size"); 1494 } 1495 } 1496 1497 if (ScheduleModifierHasType(scheduleClause, 1498 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 1499 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic && 1500 kind != parser::OmpScheduleClause::ScheduleType::Guided) { 1501 context_.Say(GetContext().clauseSource, 1502 "The NONMONOTONIC modifier can only be specified with " 1503 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US); 1504 } 1505 } 1506 } 1507 } 1508 1509 void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) { 1510 CheckAllowed(llvm::omp::Clause::OMPC_depend); 1511 if (const auto *inOut{std::get_if<parser::OmpDependClause::InOut>(&x.v.u)}) { 1512 const auto &designators{std::get<std::list<parser::Designator>>(inOut->t)}; 1513 for (const auto &ele : designators) { 1514 if (const auto *dataRef{std::get_if<parser::DataRef>(&ele.u)}) { 1515 CheckDependList(*dataRef); 1516 if (const auto *arr{ 1517 std::get_if<common::Indirection<parser::ArrayElement>>( 1518 &dataRef->u)}) { 1519 CheckArraySection(arr->value(), GetLastName(*dataRef), 1520 llvm::omp::Clause::OMPC_depend); 1521 } 1522 } 1523 } 1524 } 1525 } 1526 1527 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &x) { 1528 CheckAllowed(llvm::omp::Clause::OMPC_copyprivate); 1529 CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_copyprivate); 1530 } 1531 1532 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) { 1533 CheckAllowed(llvm::omp::Clause::OMPC_lastprivate); 1534 1535 DirectivesClauseTriple dirClauseTriple; 1536 SymbolSourceMap currSymbols; 1537 GetSymbolsInObjectList(x.v, currSymbols); 1538 CheckDefinableObjects(currSymbols, GetClauseKindForParserClass(x)); 1539 1540 // Check lastprivate variables in worksharing constructs 1541 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do, 1542 std::make_pair( 1543 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1544 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections, 1545 std::make_pair( 1546 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1547 1548 CheckPrivateSymbolsInOuterCxt( 1549 currSymbols, dirClauseTriple, GetClauseKindForParserClass(x)); 1550 } 1551 1552 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) { 1553 return llvm::omp::getOpenMPClauseName(clause); 1554 } 1555 1556 llvm::StringRef OmpStructureChecker::getDirectiveName( 1557 llvm::omp::Directive directive) { 1558 return llvm::omp::getOpenMPDirectiveName(directive); 1559 } 1560 1561 void OmpStructureChecker::CheckDependList(const parser::DataRef &d) { 1562 std::visit( 1563 common::visitors{ 1564 [&](const common::Indirection<parser::ArrayElement> &elem) { 1565 // Check if the base element is valid on Depend Clause 1566 CheckDependList(elem.value().base); 1567 }, 1568 [&](const common::Indirection<parser::StructureComponent> &) { 1569 context_.Say(GetContext().clauseSource, 1570 "A variable that is part of another variable " 1571 "(such as an element of a structure) but is not an array " 1572 "element or an array section cannot appear in a DEPEND " 1573 "clause"_err_en_US); 1574 }, 1575 [&](const common::Indirection<parser::CoindexedNamedObject> &) { 1576 context_.Say(GetContext().clauseSource, 1577 "Coarrays are not supported in DEPEND clause"_err_en_US); 1578 }, 1579 [&](const parser::Name &) { return; }, 1580 }, 1581 d.u); 1582 } 1583 1584 // Called from both Reduction and Depend clause. 1585 void OmpStructureChecker::CheckArraySection( 1586 const parser::ArrayElement &arrayElement, const parser::Name &name, 1587 const llvm::omp::Clause clause) { 1588 if (!arrayElement.subscripts.empty()) { 1589 for (const auto &subscript : arrayElement.subscripts) { 1590 if (const auto *triplet{ 1591 std::get_if<parser::SubscriptTriplet>(&subscript.u)}) { 1592 if (std::get<0>(triplet->t) && std::get<1>(triplet->t)) { 1593 const auto &lower{std::get<0>(triplet->t)}; 1594 const auto &upper{std::get<1>(triplet->t)}; 1595 if (lower && upper) { 1596 const auto lval{GetIntValue(lower)}; 1597 const auto uval{GetIntValue(upper)}; 1598 if (lval && uval && *uval < *lval) { 1599 context_.Say(GetContext().clauseSource, 1600 "'%s' in %s clause" 1601 " is a zero size array section"_err_en_US, 1602 name.ToString(), 1603 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1604 break; 1605 } else if (std::get<2>(triplet->t)) { 1606 const auto &strideExpr{std::get<2>(triplet->t)}; 1607 if (strideExpr) { 1608 if (clause == llvm::omp::Clause::OMPC_depend) { 1609 context_.Say(GetContext().clauseSource, 1610 "Stride should not be specified for array section in " 1611 "DEPEND " 1612 "clause"_err_en_US); 1613 } 1614 const auto stride{GetIntValue(strideExpr)}; 1615 if ((stride && stride != 1)) { 1616 context_.Say(GetContext().clauseSource, 1617 "A list item that appears in a REDUCTION clause" 1618 " should have a contiguous storage array section."_err_en_US, 1619 ContextDirectiveAsFortran()); 1620 break; 1621 } 1622 } 1623 } 1624 } 1625 } 1626 } 1627 } 1628 } 1629 } 1630 1631 void OmpStructureChecker::CheckIntentInPointer( 1632 const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) { 1633 SymbolSourceMap symbols; 1634 GetSymbolsInObjectList(objectList, symbols); 1635 for (auto it{symbols.begin()}; it != symbols.end(); ++it) { 1636 const auto *symbol{it->first}; 1637 const auto source{it->second}; 1638 if (IsPointer(*symbol) && IsIntentIn(*symbol)) { 1639 context_.Say(source, 1640 "Pointer '%s' with the INTENT(IN) attribute may not appear " 1641 "in a %s clause"_err_en_US, 1642 symbol->name(), 1643 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1644 } 1645 } 1646 } 1647 1648 void OmpStructureChecker::GetSymbolsInObjectList( 1649 const parser::OmpObjectList &objectList, SymbolSourceMap &symbols) { 1650 for (const auto &ompObject : objectList.v) { 1651 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1652 if (const auto *symbol{name->symbol}) { 1653 if (const auto *commonBlockDetails{ 1654 symbol->detailsIf<CommonBlockDetails>()}) { 1655 for (const auto &object : commonBlockDetails->objects()) { 1656 symbols.emplace(&object->GetUltimate(), name->source); 1657 } 1658 } else { 1659 symbols.emplace(&symbol->GetUltimate(), name->source); 1660 } 1661 } 1662 } 1663 } 1664 } 1665 1666 void OmpStructureChecker::CheckDefinableObjects( 1667 SymbolSourceMap &symbols, const llvm::omp::Clause clause) { 1668 for (auto it{symbols.begin()}; it != symbols.end(); ++it) { 1669 const auto *symbol{it->first}; 1670 const auto source{it->second}; 1671 if (auto msg{WhyNotModifiable(*symbol, context_.FindScope(source))}) { 1672 context_ 1673 .Say(source, 1674 "Variable '%s' on the %s clause is not definable"_err_en_US, 1675 symbol->name(), 1676 parser::ToUpperCaseLetters(getClauseName(clause).str())) 1677 .Attach(source, std::move(*msg), symbol->name()); 1678 } 1679 } 1680 } 1681 1682 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt( 1683 SymbolSourceMap &currSymbols, DirectivesClauseTriple &dirClauseTriple, 1684 const llvm::omp::Clause currClause) { 1685 SymbolSourceMap enclosingSymbols; 1686 auto range{dirClauseTriple.equal_range(GetContext().directive)}; 1687 for (auto dirIter{range.first}; dirIter != range.second; ++dirIter) { 1688 auto enclosingDir{dirIter->second.first}; 1689 auto enclosingClauseSet{dirIter->second.second}; 1690 if (auto *enclosingContext{GetEnclosingContextWithDir(enclosingDir)}) { 1691 for (auto it{enclosingContext->clauseInfo.begin()}; 1692 it != enclosingContext->clauseInfo.end(); ++it) { 1693 if (enclosingClauseSet.test(it->first)) { 1694 if (const auto *ompObjectList{GetOmpObjectList(*it->second)}) { 1695 GetSymbolsInObjectList(*ompObjectList, enclosingSymbols); 1696 } 1697 } 1698 } 1699 1700 // Check if the symbols in current context are private in outer context 1701 for (auto iter{currSymbols.begin()}; iter != currSymbols.end(); ++iter) { 1702 const auto *symbol{iter->first}; 1703 const auto source{iter->second}; 1704 if (enclosingSymbols.find(symbol) != enclosingSymbols.end()) { 1705 context_.Say(source, 1706 "%s variable '%s' is PRIVATE in outer context"_err_en_US, 1707 parser::ToUpperCaseLetters(getClauseName(currClause).str()), 1708 symbol->name()); 1709 } 1710 } 1711 } 1712 } 1713 } 1714 1715 void OmpStructureChecker::CheckWorkshareBlockStmts( 1716 const parser::Block &block, parser::CharBlock source) { 1717 OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source}; 1718 1719 for (auto it{block.begin()}; it != block.end(); ++it) { 1720 if (parser::Unwrap<parser::AssignmentStmt>(*it) || 1721 parser::Unwrap<parser::ForallStmt>(*it) || 1722 parser::Unwrap<parser::ForallConstruct>(*it) || 1723 parser::Unwrap<parser::WhereStmt>(*it) || 1724 parser::Unwrap<parser::WhereConstruct>(*it)) { 1725 parser::Walk(*it, ompWorkshareBlockChecker); 1726 } else if (const auto *ompConstruct{ 1727 parser::Unwrap<parser::OpenMPConstruct>(*it)}) { 1728 if (const auto *ompAtomicConstruct{ 1729 std::get_if<parser::OpenMPAtomicConstruct>(&ompConstruct->u)}) { 1730 // Check if assignment statements in the enclosing OpenMP Atomic 1731 // construct are allowed in the Workshare construct 1732 parser::Walk(*ompAtomicConstruct, ompWorkshareBlockChecker); 1733 } else if (const auto *ompCriticalConstruct{ 1734 std::get_if<parser::OpenMPCriticalConstruct>( 1735 &ompConstruct->u)}) { 1736 // All the restrictions on the Workshare construct apply to the 1737 // statements in the enclosing critical constructs 1738 const auto &criticalBlock{ 1739 std::get<parser::Block>(ompCriticalConstruct->t)}; 1740 CheckWorkshareBlockStmts(criticalBlock, source); 1741 } else { 1742 // Check if OpenMP constructs enclosed in the Workshare construct are 1743 // 'Parallel' constructs 1744 auto currentDir{llvm::omp::Directive::OMPD_unknown}; 1745 const OmpDirectiveSet parallelDirSet{ 1746 llvm::omp::Directive::OMPD_parallel, 1747 llvm::omp::Directive::OMPD_parallel_do, 1748 llvm::omp::Directive::OMPD_parallel_sections, 1749 llvm::omp::Directive::OMPD_parallel_workshare, 1750 llvm::omp::Directive::OMPD_parallel_do_simd}; 1751 1752 if (const auto *ompBlockConstruct{ 1753 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) { 1754 const auto &beginBlockDir{ 1755 std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)}; 1756 const auto &beginDir{ 1757 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 1758 currentDir = beginDir.v; 1759 } else if (const auto *ompLoopConstruct{ 1760 std::get_if<parser::OpenMPLoopConstruct>( 1761 &ompConstruct->u)}) { 1762 const auto &beginLoopDir{ 1763 std::get<parser::OmpBeginLoopDirective>(ompLoopConstruct->t)}; 1764 const auto &beginDir{ 1765 std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 1766 currentDir = beginDir.v; 1767 } else if (const auto *ompSectionsConstruct{ 1768 std::get_if<parser::OpenMPSectionsConstruct>( 1769 &ompConstruct->u)}) { 1770 const auto &beginSectionsDir{ 1771 std::get<parser::OmpBeginSectionsDirective>( 1772 ompSectionsConstruct->t)}; 1773 const auto &beginDir{ 1774 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 1775 currentDir = beginDir.v; 1776 } 1777 1778 if (!parallelDirSet.test(currentDir)) { 1779 context_.Say(source, 1780 "OpenMP constructs enclosed in WORKSHARE construct may consist " 1781 "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US); 1782 } 1783 } 1784 } else { 1785 context_.Say(source, 1786 "The structured block in a WORKSHARE construct may consist of only " 1787 "SCALAR or ARRAY assignments, FORALL or WHERE statements, " 1788 "FORALL, WHERE, ATOMIC, CRITICAL or PARALLEL constructs"_err_en_US); 1789 } 1790 } 1791 } 1792 1793 const parser::OmpObjectList *OmpStructureChecker::GetOmpObjectList( 1794 const parser::OmpClause &clause) { 1795 1796 // Clauses with OmpObjectList as its data member 1797 using MemberObjectListClauses = std::tuple<parser::OmpClause::Copyprivate, 1798 parser::OmpClause::Copyin, parser::OmpClause::Firstprivate, 1799 parser::OmpClause::From, parser::OmpClause::Lastprivate, 1800 parser::OmpClause::Link, parser::OmpClause::Private, 1801 parser::OmpClause::Shared, parser::OmpClause::To>; 1802 1803 // Clauses with OmpObjectList in the tuple 1804 using TupleObjectListClauses = std::tuple<parser::OmpClause::Allocate, 1805 parser::OmpClause::Map, parser::OmpClause::Reduction>; 1806 1807 // TODO:: Generate the tuples using TableGen. 1808 // Handle other constructs with OmpObjectList such as OpenMPThreadprivate. 1809 return std::visit( 1810 common::visitors{ 1811 [&](const auto &x) -> const parser::OmpObjectList * { 1812 using Ty = std::decay_t<decltype(x)>; 1813 if constexpr (common::HasMember<Ty, MemberObjectListClauses>) { 1814 return &x.v; 1815 } else if constexpr (common::HasMember<Ty, 1816 TupleObjectListClauses>) { 1817 return &(std::get<parser::OmpObjectList>(x.v.t)); 1818 } else { 1819 return nullptr; 1820 } 1821 }, 1822 }, 1823 clause.u); 1824 } 1825 1826 } // namespace Fortran::semantics 1827