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(Hint, OMPC_hint) 980 CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction) 981 CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive) 982 CHECK_SIMPLE_CLAUSE(Match, OMPC_match) 983 CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal) 984 CHECK_SIMPLE_CLAUSE(Order, OMPC_order) 985 CHECK_SIMPLE_CLAUSE(Read, OMPC_read) 986 CHECK_SIMPLE_CLAUSE(ReverseOffload, OMPC_reverse_offload) 987 CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate) 988 CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads) 989 CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) 990 CHECK_SIMPLE_CLAUSE(IsDevicePtr, OMPC_is_device_ptr) 991 CHECK_SIMPLE_CLAUSE(Link, OMPC_link) 992 CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable) 993 CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup) 994 CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch) 995 CHECK_SIMPLE_CLAUSE(Nowait, OMPC_nowait) 996 CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind) 997 CHECK_SIMPLE_CLAUSE(Release, OMPC_release) 998 CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed) 999 CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst) 1000 CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd) 1001 CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes) 1002 CHECK_SIMPLE_CLAUSE(TaskReduction, OMPC_task_reduction) 1003 CHECK_SIMPLE_CLAUSE(To, OMPC_to) 1004 CHECK_SIMPLE_CLAUSE(UnifiedAddress, OMPC_unified_address) 1005 CHECK_SIMPLE_CLAUSE(UnifiedSharedMemory, OMPC_unified_shared_memory) 1006 CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform) 1007 CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown) 1008 CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied) 1009 CHECK_SIMPLE_CLAUSE(UseDevicePtr, OMPC_use_device_ptr) 1010 CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators) 1011 CHECK_SIMPLE_CLAUSE(Update, OMPC_update) 1012 CHECK_SIMPLE_CLAUSE(UseDeviceAddr, OMPC_use_device_addr) 1013 CHECK_SIMPLE_CLAUSE(Write, OMPC_write) 1014 CHECK_SIMPLE_CLAUSE(Init, OMPC_init) 1015 CHECK_SIMPLE_CLAUSE(Use, OMPC_use) 1016 CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants) 1017 CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext) 1018 CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter) 1019 1020 CHECK_REQ_SCALAR_INT_CLAUSE(Allocator, OMPC_allocator) 1021 CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize) 1022 CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks) 1023 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams) 1024 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads) 1025 CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority) 1026 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit) 1027 1028 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse) 1029 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen) 1030 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen) 1031 1032 // Restrictions specific to each clause are implemented apart from the 1033 // generalized restrictions. 1034 void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) { 1035 CheckAllowed(llvm::omp::Clause::OMPC_reduction); 1036 if (CheckReductionOperators(x)) { 1037 CheckReductionTypeList(x); 1038 } 1039 } 1040 bool OmpStructureChecker::CheckReductionOperators( 1041 const parser::OmpClause::Reduction &x) { 1042 1043 const auto &definedOp{std::get<0>(x.v.t)}; 1044 bool ok = false; 1045 std::visit( 1046 common::visitors{ 1047 [&](const parser::DefinedOperator &dOpr) { 1048 const auto &intrinsicOp{ 1049 std::get<parser::DefinedOperator::IntrinsicOperator>(dOpr.u)}; 1050 ok = CheckIntrinsicOperator(intrinsicOp); 1051 }, 1052 [&](const parser::ProcedureDesignator &procD) { 1053 const parser::Name *name{std::get_if<parser::Name>(&procD.u)}; 1054 if (name) { 1055 if (name->source == "max" || name->source == "min" || 1056 name->source == "iand" || name->source == "ior" || 1057 name->source == "ieor") { 1058 ok = true; 1059 } else { 1060 context_.Say(GetContext().clauseSource, 1061 "Invalid reduction identifier in REDUCTION clause."_err_en_US, 1062 ContextDirectiveAsFortran()); 1063 } 1064 } 1065 }, 1066 }, 1067 definedOp.u); 1068 1069 return ok; 1070 } 1071 bool OmpStructureChecker::CheckIntrinsicOperator( 1072 const parser::DefinedOperator::IntrinsicOperator &op) { 1073 1074 switch (op) { 1075 case parser::DefinedOperator::IntrinsicOperator::Add: 1076 case parser::DefinedOperator::IntrinsicOperator::Subtract: 1077 case parser::DefinedOperator::IntrinsicOperator::Multiply: 1078 case parser::DefinedOperator::IntrinsicOperator::AND: 1079 case parser::DefinedOperator::IntrinsicOperator::OR: 1080 case parser::DefinedOperator::IntrinsicOperator::EQV: 1081 case parser::DefinedOperator::IntrinsicOperator::NEQV: 1082 return true; 1083 default: 1084 context_.Say(GetContext().clauseSource, 1085 "Invalid reduction operator in REDUCTION clause."_err_en_US, 1086 ContextDirectiveAsFortran()); 1087 } 1088 return false; 1089 } 1090 1091 void OmpStructureChecker::CheckReductionTypeList( 1092 const parser::OmpClause::Reduction &x) { 1093 const auto &ompObjectList{std::get<parser::OmpObjectList>(x.v.t)}; 1094 CheckIntentInPointerAndDefinable( 1095 ompObjectList, llvm::omp::Clause::OMPC_reduction); 1096 CheckReductionArraySection(ompObjectList); 1097 CheckMultipleAppearanceAcrossContext(ompObjectList); 1098 } 1099 1100 void OmpStructureChecker::CheckIntentInPointerAndDefinable( 1101 const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) { 1102 for (const auto &ompObject : objectList.v) { 1103 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1104 if (const auto *symbol{name->symbol}) { 1105 if (IsPointer(symbol->GetUltimate()) && 1106 IsIntentIn(symbol->GetUltimate())) { 1107 context_.Say(GetContext().clauseSource, 1108 "Pointer '%s' with the INTENT(IN) attribute may not appear " 1109 "in a %s clause"_err_en_US, 1110 symbol->name(), 1111 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1112 } 1113 if (auto msg{ 1114 WhyNotModifiable(*symbol, context_.FindScope(name->source))}) { 1115 context_.Say(GetContext().clauseSource, 1116 "Variable '%s' on the %s clause is not definable"_err_en_US, 1117 symbol->name(), 1118 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1119 } 1120 } 1121 } 1122 } 1123 } 1124 1125 void OmpStructureChecker::CheckReductionArraySection( 1126 const parser::OmpObjectList &ompObjectList) { 1127 for (const auto &ompObject : ompObjectList.v) { 1128 if (const auto *dataRef{parser::Unwrap<parser::DataRef>(ompObject)}) { 1129 if (const auto *arrayElement{ 1130 parser::Unwrap<parser::ArrayElement>(ompObject)}) { 1131 if (arrayElement) { 1132 CheckArraySection(*arrayElement, GetLastName(*dataRef), 1133 llvm::omp::Clause::OMPC_reduction); 1134 } 1135 } 1136 } 1137 } 1138 } 1139 1140 void OmpStructureChecker::CheckMultipleAppearanceAcrossContext( 1141 const parser::OmpObjectList &redObjectList) { 1142 // TODO: Verify the assumption here that the immediately enclosing region is 1143 // the parallel region to which the worksharing construct having reduction 1144 // binds to. 1145 if (auto *enclosingContext{GetEnclosingDirContext()}) { 1146 for (auto it : enclosingContext->clauseInfo) { 1147 llvmOmpClause type = it.first; 1148 const auto *clause = it.second; 1149 if (llvm::omp::privateReductionSet.test(type)) { 1150 if (const auto *objList{GetOmpObjectList(*clause)}) { 1151 for (const auto &ompObject : objList->v) { 1152 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1153 if (const auto *symbol{name->symbol}) { 1154 for (const auto &redOmpObject : redObjectList.v) { 1155 if (const auto *rname{ 1156 parser::Unwrap<parser::Name>(redOmpObject)}) { 1157 if (const auto *rsymbol{rname->symbol}) { 1158 if (rsymbol->name() == symbol->name()) { 1159 context_.Say(GetContext().clauseSource, 1160 "%s variable '%s' is %s in outer context must" 1161 " be shared in the parallel regions to which any" 1162 " of the worksharing regions arising from the " 1163 "worksharing" 1164 " construct bind."_err_en_US, 1165 parser::ToUpperCaseLetters( 1166 getClauseName(llvm::omp::Clause::OMPC_reduction) 1167 .str()), 1168 symbol->name(), 1169 parser::ToUpperCaseLetters( 1170 getClauseName(type).str())); 1171 } 1172 } 1173 } 1174 } 1175 } 1176 } 1177 } 1178 } 1179 } 1180 } 1181 } 1182 } 1183 1184 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) { 1185 CheckAllowed(llvm::omp::Clause::OMPC_ordered); 1186 // the parameter of ordered clause is optional 1187 if (const auto &expr{x.v}) { 1188 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr); 1189 // 2.8.3 Loop SIMD Construct Restriction 1190 if (llvm::omp::doSimdSet.test(GetContext().directive)) { 1191 context_.Say(GetContext().clauseSource, 1192 "No ORDERED clause with a parameter can be specified " 1193 "on the %s directive"_err_en_US, 1194 ContextDirectiveAsFortran()); 1195 } 1196 } 1197 } 1198 1199 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) { 1200 CheckAllowed(llvm::omp::Clause::OMPC_shared); 1201 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v); 1202 } 1203 void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) { 1204 CheckAllowed(llvm::omp::Clause::OMPC_private); 1205 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v); 1206 CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_private); 1207 } 1208 1209 bool OmpStructureChecker::IsDataRefTypeParamInquiry( 1210 const parser::DataRef *dataRef) { 1211 bool dataRefIsTypeParamInquiry{false}; 1212 if (const auto *structComp{ 1213 parser::Unwrap<parser::StructureComponent>(dataRef)}) { 1214 if (const auto *compSymbol{structComp->component.symbol}) { 1215 if (const auto *compSymbolMiscDetails{ 1216 std::get_if<MiscDetails>(&compSymbol->details())}) { 1217 const auto detailsKind = compSymbolMiscDetails->kind(); 1218 dataRefIsTypeParamInquiry = 1219 (detailsKind == MiscDetails::Kind::KindParamInquiry || 1220 detailsKind == MiscDetails::Kind::LenParamInquiry); 1221 } else if (compSymbol->has<TypeParamDetails>()) { 1222 dataRefIsTypeParamInquiry = true; 1223 } 1224 } 1225 } 1226 return dataRefIsTypeParamInquiry; 1227 } 1228 1229 void OmpStructureChecker::CheckIsVarPartOfAnotherVar( 1230 const parser::CharBlock &source, const parser::OmpObjectList &objList) { 1231 1232 for (const auto &ompObject : objList.v) { 1233 std::visit( 1234 common::visitors{ 1235 [&](const parser::Designator &designator) { 1236 if (const auto *dataRef{ 1237 std::get_if<parser::DataRef>(&designator.u)}) { 1238 if (IsDataRefTypeParamInquiry(dataRef)) { 1239 context_.Say(source, 1240 "A type parameter inquiry cannot appear in an ALLOCATE directive"_err_en_US); 1241 } else if (parser::Unwrap<parser::StructureComponent>( 1242 ompObject) || 1243 parser::Unwrap<parser::ArrayElement>(ompObject)) { 1244 context_.Say(source, 1245 "A variable that is part of another variable (as an " 1246 "array or structure element)" 1247 " cannot appear in a PRIVATE or SHARED clause or on the ALLOCATE directive."_err_en_US); 1248 } 1249 } 1250 }, 1251 [&](const parser::Name &name) {}, 1252 }, 1253 ompObject.u); 1254 } 1255 } 1256 1257 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) { 1258 CheckAllowed(llvm::omp::Clause::OMPC_firstprivate); 1259 CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate, x.v); 1260 1261 SymbolSourceMap currSymbols; 1262 GetSymbolsInObjectList(x.v, currSymbols); 1263 1264 DirectivesClauseTriple dirClauseTriple; 1265 // Check firstprivate variables in worksharing constructs 1266 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do, 1267 std::make_pair( 1268 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1269 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections, 1270 std::make_pair( 1271 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1272 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_single, 1273 std::make_pair( 1274 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1275 // Check firstprivate variables in distribute construct 1276 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute, 1277 std::make_pair( 1278 llvm::omp::Directive::OMPD_teams, llvm::omp::privateReductionSet)); 1279 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute, 1280 std::make_pair(llvm::omp::Directive::OMPD_target_teams, 1281 llvm::omp::privateReductionSet)); 1282 // Check firstprivate variables in task and taskloop constructs 1283 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_task, 1284 std::make_pair(llvm::omp::Directive::OMPD_parallel, 1285 OmpClauseSet{llvm::omp::Clause::OMPC_reduction})); 1286 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_taskloop, 1287 std::make_pair(llvm::omp::Directive::OMPD_parallel, 1288 OmpClauseSet{llvm::omp::Clause::OMPC_reduction})); 1289 1290 CheckPrivateSymbolsInOuterCxt( 1291 currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_firstprivate); 1292 } 1293 1294 void OmpStructureChecker::CheckIsLoopIvPartOfClause( 1295 llvmOmpClause clause, const parser::OmpObjectList &ompObjectList) { 1296 for (const auto &ompObject : ompObjectList.v) { 1297 if (const parser::Name * name{parser::Unwrap<parser::Name>(ompObject)}) { 1298 if (name->symbol == GetContext().loopIV) { 1299 context_.Say(name->source, 1300 "DO iteration variable %s is not allowed in %s clause."_err_en_US, 1301 name->ToString(), 1302 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1303 } 1304 } 1305 } 1306 } 1307 // Following clauses have a seperate node in parse-tree.h. 1308 // Atomic-clause 1309 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead, OMPC_read) 1310 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicWrite, OMPC_write) 1311 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicUpdate, OMPC_update) 1312 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicCapture, OMPC_capture) 1313 1314 void OmpStructureChecker::Leave(const parser::OmpAtomicRead &) { 1315 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_read, 1316 {llvm::omp::Clause::OMPC_release, llvm::omp::Clause::OMPC_acq_rel}); 1317 } 1318 void OmpStructureChecker::Leave(const parser::OmpAtomicWrite &) { 1319 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_write, 1320 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel}); 1321 } 1322 void OmpStructureChecker::Leave(const parser::OmpAtomicUpdate &) { 1323 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_update, 1324 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel}); 1325 } 1326 // OmpAtomic node represents atomic directive without atomic-clause. 1327 // atomic-clause - READ,WRITE,UPDATE,CAPTURE. 1328 void OmpStructureChecker::Leave(const parser::OmpAtomic &) { 1329 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acquire)}) { 1330 context_.Say(clause->source, 1331 "Clause ACQUIRE is not allowed on the ATOMIC directive"_err_en_US); 1332 } 1333 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acq_rel)}) { 1334 context_.Say(clause->source, 1335 "Clause ACQ_REL is not allowed on the ATOMIC directive"_err_en_US); 1336 } 1337 } 1338 // Restrictions specific to each clause are implemented apart from the 1339 // generalized restrictions. 1340 void OmpStructureChecker::Enter(const parser::OmpClause::Aligned &x) { 1341 CheckAllowed(llvm::omp::Clause::OMPC_aligned); 1342 1343 if (const auto &expr{ 1344 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.v.t)}) { 1345 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_aligned, *expr); 1346 } 1347 // 2.8.1 TODO: list-item attribute check 1348 } 1349 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) { 1350 CheckAllowed(llvm::omp::Clause::OMPC_defaultmap); 1351 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory; 1352 if (!std::get<std::optional<VariableCategory>>(x.v.t)) { 1353 context_.Say(GetContext().clauseSource, 1354 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP " 1355 "clause"_err_en_US); 1356 } 1357 } 1358 void OmpStructureChecker::Enter(const parser::OmpClause::If &x) { 1359 CheckAllowed(llvm::omp::Clause::OMPC_if); 1360 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier; 1361 static std::unordered_map<dirNameModifier, OmpDirectiveSet> 1362 dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet}, 1363 {dirNameModifier::Target, llvm::omp::targetSet}, 1364 {dirNameModifier::TargetEnterData, 1365 {llvm::omp::Directive::OMPD_target_enter_data}}, 1366 {dirNameModifier::TargetExitData, 1367 {llvm::omp::Directive::OMPD_target_exit_data}}, 1368 {dirNameModifier::TargetData, 1369 {llvm::omp::Directive::OMPD_target_data}}, 1370 {dirNameModifier::TargetUpdate, 1371 {llvm::omp::Directive::OMPD_target_update}}, 1372 {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}}, 1373 {dirNameModifier::Taskloop, llvm::omp::taskloopSet}}; 1374 if (const auto &directiveName{ 1375 std::get<std::optional<dirNameModifier>>(x.v.t)}) { 1376 auto search{dirNameModifierMap.find(*directiveName)}; 1377 if (search == dirNameModifierMap.end() || 1378 !search->second.test(GetContext().directive)) { 1379 context_ 1380 .Say(GetContext().clauseSource, 1381 "Unmatched directive name modifier %s on the IF clause"_err_en_US, 1382 parser::ToUpperCaseLetters( 1383 parser::OmpIfClause::EnumToString(*directiveName))) 1384 .Attach( 1385 GetContext().directiveSource, "Cannot apply to directive"_en_US); 1386 } 1387 } 1388 } 1389 1390 void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) { 1391 CheckAllowed(llvm::omp::Clause::OMPC_linear); 1392 1393 // 2.7 Loop Construct Restriction 1394 if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) { 1395 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.v.u)) { 1396 context_.Say(GetContext().clauseSource, 1397 "A modifier may not be specified in a LINEAR clause " 1398 "on the %s directive"_err_en_US, 1399 ContextDirectiveAsFortran()); 1400 } 1401 } 1402 } 1403 1404 void OmpStructureChecker::CheckAllowedMapTypes( 1405 const parser::OmpMapType::Type &type, 1406 const std::list<parser::OmpMapType::Type> &allowedMapTypeList) { 1407 const auto found{std::find( 1408 std::begin(allowedMapTypeList), std::end(allowedMapTypeList), type)}; 1409 if (found == std::end(allowedMapTypeList)) { 1410 std::string commaSeperatedMapTypes; 1411 llvm::interleave( 1412 allowedMapTypeList.begin(), allowedMapTypeList.end(), 1413 [&](const parser::OmpMapType::Type &mapType) { 1414 commaSeperatedMapTypes.append(parser::ToUpperCaseLetters( 1415 parser::OmpMapType::EnumToString(mapType))); 1416 }, 1417 [&] { commaSeperatedMapTypes.append(", "); }); 1418 context_.Say(GetContext().clauseSource, 1419 "Only the %s map types are permitted " 1420 "for MAP clauses on the %s directive"_err_en_US, 1421 commaSeperatedMapTypes, ContextDirectiveAsFortran()); 1422 } 1423 } 1424 1425 void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) { 1426 CheckAllowed(llvm::omp::Clause::OMPC_map); 1427 1428 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.v.t)}) { 1429 using Type = parser::OmpMapType::Type; 1430 const Type &type{std::get<Type>(maptype->t)}; 1431 switch (GetContext().directive) { 1432 case llvm::omp::Directive::OMPD_target: 1433 case llvm::omp::Directive::OMPD_target_teams: 1434 case llvm::omp::Directive::OMPD_target_teams_distribute: 1435 case llvm::omp::Directive::OMPD_target_teams_distribute_simd: 1436 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: 1437 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: 1438 case llvm::omp::Directive::OMPD_target_data: 1439 CheckAllowedMapTypes( 1440 type, {Type::To, Type::From, Type::Tofrom, Type::Alloc}); 1441 break; 1442 case llvm::omp::Directive::OMPD_target_enter_data: 1443 CheckAllowedMapTypes(type, {Type::To, Type::Alloc}); 1444 break; 1445 case llvm::omp::Directive::OMPD_target_exit_data: 1446 CheckAllowedMapTypes(type, {Type::From, Type::Release, Type::Delete}); 1447 break; 1448 default: 1449 break; 1450 } 1451 } 1452 } 1453 1454 bool OmpStructureChecker::ScheduleModifierHasType( 1455 const parser::OmpScheduleClause &x, 1456 const parser::OmpScheduleModifierType::ModType &type) { 1457 const auto &modifier{ 1458 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)}; 1459 if (modifier) { 1460 const auto &modType1{ 1461 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)}; 1462 const auto &modType2{ 1463 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>( 1464 modifier->t)}; 1465 if (modType1.v.v == type || (modType2 && modType2->v.v == type)) { 1466 return true; 1467 } 1468 } 1469 return false; 1470 } 1471 void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) { 1472 CheckAllowed(llvm::omp::Clause::OMPC_schedule); 1473 const parser::OmpScheduleClause &scheduleClause = x.v; 1474 1475 // 2.7 Loop Construct Restriction 1476 if (llvm::omp::doSet.test(GetContext().directive)) { 1477 const auto &kind{std::get<1>(scheduleClause.t)}; 1478 const auto &chunk{std::get<2>(scheduleClause.t)}; 1479 if (chunk) { 1480 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime || 1481 kind == parser::OmpScheduleClause::ScheduleType::Auto) { 1482 context_.Say(GetContext().clauseSource, 1483 "When SCHEDULE clause has %s specified, " 1484 "it must not have chunk size specified"_err_en_US, 1485 parser::ToUpperCaseLetters( 1486 parser::OmpScheduleClause::EnumToString(kind))); 1487 } 1488 if (const auto &chunkExpr{std::get<std::optional<parser::ScalarIntExpr>>( 1489 scheduleClause.t)}) { 1490 RequiresPositiveParameter( 1491 llvm::omp::Clause::OMPC_schedule, *chunkExpr, "chunk size"); 1492 } 1493 } 1494 1495 if (ScheduleModifierHasType(scheduleClause, 1496 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 1497 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic && 1498 kind != parser::OmpScheduleClause::ScheduleType::Guided) { 1499 context_.Say(GetContext().clauseSource, 1500 "The NONMONOTONIC modifier can only be specified with " 1501 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US); 1502 } 1503 } 1504 } 1505 } 1506 1507 void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) { 1508 CheckAllowed(llvm::omp::Clause::OMPC_depend); 1509 if (const auto *inOut{std::get_if<parser::OmpDependClause::InOut>(&x.v.u)}) { 1510 const auto &designators{std::get<std::list<parser::Designator>>(inOut->t)}; 1511 for (const auto &ele : designators) { 1512 if (const auto *dataRef{std::get_if<parser::DataRef>(&ele.u)}) { 1513 CheckDependList(*dataRef); 1514 if (const auto *arr{ 1515 std::get_if<common::Indirection<parser::ArrayElement>>( 1516 &dataRef->u)}) { 1517 CheckArraySection(arr->value(), GetLastName(*dataRef), 1518 llvm::omp::Clause::OMPC_depend); 1519 } 1520 } 1521 } 1522 } 1523 } 1524 1525 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &x) { 1526 CheckAllowed(llvm::omp::Clause::OMPC_copyprivate); 1527 CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_copyprivate); 1528 } 1529 1530 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) { 1531 CheckAllowed(llvm::omp::Clause::OMPC_lastprivate); 1532 1533 DirectivesClauseTriple dirClauseTriple; 1534 SymbolSourceMap currSymbols; 1535 GetSymbolsInObjectList(x.v, currSymbols); 1536 CheckDefinableObjects(currSymbols, GetClauseKindForParserClass(x)); 1537 1538 // Check lastprivate variables in worksharing constructs 1539 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do, 1540 std::make_pair( 1541 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1542 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections, 1543 std::make_pair( 1544 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet)); 1545 1546 CheckPrivateSymbolsInOuterCxt( 1547 currSymbols, dirClauseTriple, GetClauseKindForParserClass(x)); 1548 } 1549 1550 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) { 1551 return llvm::omp::getOpenMPClauseName(clause); 1552 } 1553 1554 llvm::StringRef OmpStructureChecker::getDirectiveName( 1555 llvm::omp::Directive directive) { 1556 return llvm::omp::getOpenMPDirectiveName(directive); 1557 } 1558 1559 void OmpStructureChecker::CheckDependList(const parser::DataRef &d) { 1560 std::visit( 1561 common::visitors{ 1562 [&](const common::Indirection<parser::ArrayElement> &elem) { 1563 // Check if the base element is valid on Depend Clause 1564 CheckDependList(elem.value().base); 1565 }, 1566 [&](const common::Indirection<parser::StructureComponent> &) { 1567 context_.Say(GetContext().clauseSource, 1568 "A variable that is part of another variable " 1569 "(such as an element of a structure) but is not an array " 1570 "element or an array section cannot appear in a DEPEND " 1571 "clause"_err_en_US); 1572 }, 1573 [&](const common::Indirection<parser::CoindexedNamedObject> &) { 1574 context_.Say(GetContext().clauseSource, 1575 "Coarrays are not supported in DEPEND clause"_err_en_US); 1576 }, 1577 [&](const parser::Name &) { return; }, 1578 }, 1579 d.u); 1580 } 1581 1582 // Called from both Reduction and Depend clause. 1583 void OmpStructureChecker::CheckArraySection( 1584 const parser::ArrayElement &arrayElement, const parser::Name &name, 1585 const llvm::omp::Clause clause) { 1586 if (!arrayElement.subscripts.empty()) { 1587 for (const auto &subscript : arrayElement.subscripts) { 1588 if (const auto *triplet{ 1589 std::get_if<parser::SubscriptTriplet>(&subscript.u)}) { 1590 if (std::get<0>(triplet->t) && std::get<1>(triplet->t)) { 1591 const auto &lower{std::get<0>(triplet->t)}; 1592 const auto &upper{std::get<1>(triplet->t)}; 1593 if (lower && upper) { 1594 const auto lval{GetIntValue(lower)}; 1595 const auto uval{GetIntValue(upper)}; 1596 if (lval && uval && *uval < *lval) { 1597 context_.Say(GetContext().clauseSource, 1598 "'%s' in %s clause" 1599 " is a zero size array section"_err_en_US, 1600 name.ToString(), 1601 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1602 break; 1603 } else if (std::get<2>(triplet->t)) { 1604 const auto &strideExpr{std::get<2>(triplet->t)}; 1605 if (strideExpr) { 1606 if (clause == llvm::omp::Clause::OMPC_depend) { 1607 context_.Say(GetContext().clauseSource, 1608 "Stride should not be specified for array section in " 1609 "DEPEND " 1610 "clause"_err_en_US); 1611 } 1612 const auto stride{GetIntValue(strideExpr)}; 1613 if ((stride && stride != 1)) { 1614 context_.Say(GetContext().clauseSource, 1615 "A list item that appears in a REDUCTION clause" 1616 " should have a contiguous storage array section."_err_en_US, 1617 ContextDirectiveAsFortran()); 1618 break; 1619 } 1620 } 1621 } 1622 } 1623 } 1624 } 1625 } 1626 } 1627 } 1628 1629 void OmpStructureChecker::CheckIntentInPointer( 1630 const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) { 1631 SymbolSourceMap symbols; 1632 GetSymbolsInObjectList(objectList, symbols); 1633 for (auto it{symbols.begin()}; it != symbols.end(); ++it) { 1634 const auto *symbol{it->first}; 1635 const auto source{it->second}; 1636 if (IsPointer(*symbol) && IsIntentIn(*symbol)) { 1637 context_.Say(source, 1638 "Pointer '%s' with the INTENT(IN) attribute may not appear " 1639 "in a %s clause"_err_en_US, 1640 symbol->name(), 1641 parser::ToUpperCaseLetters(getClauseName(clause).str())); 1642 } 1643 } 1644 } 1645 1646 void OmpStructureChecker::GetSymbolsInObjectList( 1647 const parser::OmpObjectList &objectList, SymbolSourceMap &symbols) { 1648 for (const auto &ompObject : objectList.v) { 1649 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) { 1650 if (const auto *symbol{name->symbol}) { 1651 if (const auto *commonBlockDetails{ 1652 symbol->detailsIf<CommonBlockDetails>()}) { 1653 for (const auto &object : commonBlockDetails->objects()) { 1654 symbols.emplace(&object->GetUltimate(), name->source); 1655 } 1656 } else { 1657 symbols.emplace(&symbol->GetUltimate(), name->source); 1658 } 1659 } 1660 } 1661 } 1662 } 1663 1664 void OmpStructureChecker::CheckDefinableObjects( 1665 SymbolSourceMap &symbols, const llvm::omp::Clause clause) { 1666 for (auto it{symbols.begin()}; it != symbols.end(); ++it) { 1667 const auto *symbol{it->first}; 1668 const auto source{it->second}; 1669 if (auto msg{WhyNotModifiable(*symbol, context_.FindScope(source))}) { 1670 context_ 1671 .Say(source, 1672 "Variable '%s' on the %s clause is not definable"_err_en_US, 1673 symbol->name(), 1674 parser::ToUpperCaseLetters(getClauseName(clause).str())) 1675 .Attach(source, std::move(*msg), symbol->name()); 1676 } 1677 } 1678 } 1679 1680 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt( 1681 SymbolSourceMap &currSymbols, DirectivesClauseTriple &dirClauseTriple, 1682 const llvm::omp::Clause currClause) { 1683 SymbolSourceMap enclosingSymbols; 1684 auto range{dirClauseTriple.equal_range(GetContext().directive)}; 1685 for (auto dirIter{range.first}; dirIter != range.second; ++dirIter) { 1686 auto enclosingDir{dirIter->second.first}; 1687 auto enclosingClauseSet{dirIter->second.second}; 1688 if (auto *enclosingContext{GetEnclosingContextWithDir(enclosingDir)}) { 1689 for (auto it{enclosingContext->clauseInfo.begin()}; 1690 it != enclosingContext->clauseInfo.end(); ++it) { 1691 if (enclosingClauseSet.test(it->first)) { 1692 if (const auto *ompObjectList{GetOmpObjectList(*it->second)}) { 1693 GetSymbolsInObjectList(*ompObjectList, enclosingSymbols); 1694 } 1695 } 1696 } 1697 1698 // Check if the symbols in current context are private in outer context 1699 for (auto iter{currSymbols.begin()}; iter != currSymbols.end(); ++iter) { 1700 const auto *symbol{iter->first}; 1701 const auto source{iter->second}; 1702 if (enclosingSymbols.find(symbol) != enclosingSymbols.end()) { 1703 context_.Say(source, 1704 "%s variable '%s' is PRIVATE in outer context"_err_en_US, 1705 parser::ToUpperCaseLetters(getClauseName(currClause).str()), 1706 symbol->name()); 1707 } 1708 } 1709 } 1710 } 1711 } 1712 1713 void OmpStructureChecker::CheckWorkshareBlockStmts( 1714 const parser::Block &block, parser::CharBlock source) { 1715 OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source}; 1716 1717 for (auto it{block.begin()}; it != block.end(); ++it) { 1718 if (parser::Unwrap<parser::AssignmentStmt>(*it) || 1719 parser::Unwrap<parser::ForallStmt>(*it) || 1720 parser::Unwrap<parser::ForallConstruct>(*it) || 1721 parser::Unwrap<parser::WhereStmt>(*it) || 1722 parser::Unwrap<parser::WhereConstruct>(*it)) { 1723 parser::Walk(*it, ompWorkshareBlockChecker); 1724 } else if (const auto *ompConstruct{ 1725 parser::Unwrap<parser::OpenMPConstruct>(*it)}) { 1726 if (const auto *ompAtomicConstruct{ 1727 std::get_if<parser::OpenMPAtomicConstruct>(&ompConstruct->u)}) { 1728 // Check if assignment statements in the enclosing OpenMP Atomic 1729 // construct are allowed in the Workshare construct 1730 parser::Walk(*ompAtomicConstruct, ompWorkshareBlockChecker); 1731 } else if (const auto *ompCriticalConstruct{ 1732 std::get_if<parser::OpenMPCriticalConstruct>( 1733 &ompConstruct->u)}) { 1734 // All the restrictions on the Workshare construct apply to the 1735 // statements in the enclosing critical constructs 1736 const auto &criticalBlock{ 1737 std::get<parser::Block>(ompCriticalConstruct->t)}; 1738 CheckWorkshareBlockStmts(criticalBlock, source); 1739 } else { 1740 // Check if OpenMP constructs enclosed in the Workshare construct are 1741 // 'Parallel' constructs 1742 auto currentDir{llvm::omp::Directive::OMPD_unknown}; 1743 const OmpDirectiveSet parallelDirSet{ 1744 llvm::omp::Directive::OMPD_parallel, 1745 llvm::omp::Directive::OMPD_parallel_do, 1746 llvm::omp::Directive::OMPD_parallel_sections, 1747 llvm::omp::Directive::OMPD_parallel_workshare, 1748 llvm::omp::Directive::OMPD_parallel_do_simd}; 1749 1750 if (const auto *ompBlockConstruct{ 1751 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) { 1752 const auto &beginBlockDir{ 1753 std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)}; 1754 const auto &beginDir{ 1755 std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 1756 currentDir = beginDir.v; 1757 } else if (const auto *ompLoopConstruct{ 1758 std::get_if<parser::OpenMPLoopConstruct>( 1759 &ompConstruct->u)}) { 1760 const auto &beginLoopDir{ 1761 std::get<parser::OmpBeginLoopDirective>(ompLoopConstruct->t)}; 1762 const auto &beginDir{ 1763 std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 1764 currentDir = beginDir.v; 1765 } else if (const auto *ompSectionsConstruct{ 1766 std::get_if<parser::OpenMPSectionsConstruct>( 1767 &ompConstruct->u)}) { 1768 const auto &beginSectionsDir{ 1769 std::get<parser::OmpBeginSectionsDirective>( 1770 ompSectionsConstruct->t)}; 1771 const auto &beginDir{ 1772 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 1773 currentDir = beginDir.v; 1774 } 1775 1776 if (!parallelDirSet.test(currentDir)) { 1777 context_.Say(source, 1778 "OpenMP constructs enclosed in WORKSHARE construct may consist " 1779 "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US); 1780 } 1781 } 1782 } else { 1783 context_.Say(source, 1784 "The structured block in a WORKSHARE construct may consist of only " 1785 "SCALAR or ARRAY assignments, FORALL or WHERE statements, " 1786 "FORALL, WHERE, ATOMIC, CRITICAL or PARALLEL constructs"_err_en_US); 1787 } 1788 } 1789 } 1790 1791 const parser::OmpObjectList *OmpStructureChecker::GetOmpObjectList( 1792 const parser::OmpClause &clause) { 1793 1794 // Clauses with OmpObjectList as its data member 1795 using MemberObjectListClauses = std::tuple<parser::OmpClause::Copyprivate, 1796 parser::OmpClause::Copyin, parser::OmpClause::Firstprivate, 1797 parser::OmpClause::From, parser::OmpClause::Lastprivate, 1798 parser::OmpClause::Link, parser::OmpClause::Private, 1799 parser::OmpClause::Shared, parser::OmpClause::To>; 1800 1801 // Clauses with OmpObjectList in the tuple 1802 using TupleObjectListClauses = std::tuple<parser::OmpClause::Allocate, 1803 parser::OmpClause::Map, parser::OmpClause::Reduction>; 1804 1805 // TODO:: Generate the tuples using TableGen. 1806 // Handle other constructs with OmpObjectList such as OpenMPThreadprivate. 1807 return std::visit( 1808 common::visitors{ 1809 [&](const auto &x) -> const parser::OmpObjectList * { 1810 using Ty = std::decay_t<decltype(x)>; 1811 if constexpr (common::HasMember<Ty, MemberObjectListClauses>) { 1812 return &x.v; 1813 } else if constexpr (common::HasMember<Ty, 1814 TupleObjectListClauses>) { 1815 return &(std::get<parser::OmpObjectList>(x.v.t)); 1816 } else { 1817 return nullptr; 1818 } 1819 }, 1820 }, 1821 clause.u); 1822 } 1823 1824 } // namespace Fortran::semantics 1825