1 //===-- lib/Semantics/check-acc-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 #include "check-acc-structure.h" 9 #include "flang/Parser/parse-tree.h" 10 #include "flang/Semantics/tools.h" 11 12 #define CHECK_SIMPLE_CLAUSE(X, Y) \ 13 void AccStructureChecker::Enter(const parser::AccClause::X &) { \ 14 CheckAllowed(llvm::acc::Clause::Y); \ 15 } 16 17 #define CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(X, Y) \ 18 void AccStructureChecker::Enter(const parser::AccClause::X &c) { \ 19 CheckAllowed(llvm::acc::Clause::Y); \ 20 RequiresConstantPositiveParameter(llvm::acc::Clause::Y, c.v); \ 21 } 22 23 namespace Fortran::semantics { 24 25 static constexpr inline AccClauseSet 26 parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses{ 27 llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait, 28 llvm::acc::Clause::ACCC_num_gangs, llvm::acc::Clause::ACCC_num_workers, 29 llvm::acc::Clause::ACCC_vector_length}; 30 31 static constexpr inline AccClauseSet serialOnlyAllowedAfterDeviceTypeClauses{ 32 llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait}; 33 34 static constexpr inline AccClauseSet loopOnlyAllowedAfterDeviceTypeClauses{ 35 llvm::acc::Clause::ACCC_auto, llvm::acc::Clause::ACCC_collapse, 36 llvm::acc::Clause::ACCC_independent, llvm::acc::Clause::ACCC_gang, 37 llvm::acc::Clause::ACCC_seq, llvm::acc::Clause::ACCC_tile, 38 llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker}; 39 40 static constexpr inline AccClauseSet updateOnlyAllowedAfterDeviceTypeClauses{ 41 llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait}; 42 43 static constexpr inline AccClauseSet routineOnlyAllowedAfterDeviceTypeClauses{ 44 llvm::acc::Clause::ACCC_bind, llvm::acc::Clause::ACCC_gang, 45 llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker}; 46 47 class NoBranchingEnforce { 48 public: 49 NoBranchingEnforce(SemanticsContext &context, 50 parser::CharBlock sourcePosition, llvm::acc::Directive directive) 51 : context_{context}, sourcePosition_{sourcePosition}, currentDirective_{ 52 directive} {} 53 template <typename T> bool Pre(const T &) { return true; } 54 template <typename T> void Post(const T &) {} 55 56 template <typename T> bool Pre(const parser::Statement<T> &statement) { 57 currentStatementSourcePosition_ = statement.source; 58 return true; 59 } 60 61 void Post(const parser::ReturnStmt &) { EmitBranchOutError("RETURN"); } 62 void Post(const parser::ExitStmt &exitStmt) { 63 if (const auto &exitName{exitStmt.v}) { 64 CheckConstructNameBranching("EXIT", exitName.value()); 65 } 66 } 67 void Post(const parser::StopStmt &) { EmitBranchOutError("STOP"); } 68 69 private: 70 parser::MessageFormattedText GetEnclosingMsg() const { 71 return {"Enclosing %s construct"_en_US, 72 parser::ToUpperCaseLetters( 73 llvm::acc::getOpenACCDirectiveName(currentDirective_).str())}; 74 } 75 76 void EmitBranchOutError(const char *stmt) const { 77 context_ 78 .Say(currentStatementSourcePosition_, 79 "%s statement is not allowed in a %s construct"_err_en_US, stmt, 80 parser::ToUpperCaseLetters( 81 llvm::acc::getOpenACCDirectiveName(currentDirective_).str())) 82 .Attach(sourcePosition_, GetEnclosingMsg()); 83 } 84 85 void EmitBranchOutErrorWithName( 86 const char *stmt, const parser::Name &toName) const { 87 const std::string branchingToName{toName.ToString()}; 88 const auto upperCaseConstructName{parser::ToUpperCaseLetters( 89 llvm::acc::getOpenACCDirectiveName(currentDirective_).str())}; 90 context_ 91 .Say(currentStatementSourcePosition_, 92 "%s to construct '%s' outside of %s construct is not allowed"_err_en_US, 93 stmt, branchingToName, upperCaseConstructName) 94 .Attach(sourcePosition_, GetEnclosingMsg()); 95 } 96 97 // Current semantic checker is not following OpenACC constructs as they are 98 // not Fortran constructs. Hence the ConstructStack doesn't capture OpenACC 99 // constructs. Apply an inverse way to figure out if a construct-name is 100 // branching out of an OpenACC construct. The control flow goes out of an 101 // OpenACC construct, if a construct-name from statement is found in 102 // ConstructStack. 103 void CheckConstructNameBranching( 104 const char *stmt, const parser::Name &stmtName) { 105 const ConstructStack &stack{context_.constructStack()}; 106 for (auto iter{stack.cend()}; iter-- != stack.cbegin();) { 107 const ConstructNode &construct{*iter}; 108 const auto &constructName{MaybeGetNodeName(construct)}; 109 if (constructName) { 110 if (stmtName.source == constructName->source) { 111 EmitBranchOutErrorWithName(stmt, stmtName); 112 return; 113 } 114 } 115 } 116 } 117 118 SemanticsContext &context_; 119 parser::CharBlock currentStatementSourcePosition_; 120 parser::CharBlock sourcePosition_; 121 llvm::acc::Directive currentDirective_; 122 }; 123 124 void AccStructureChecker::Enter(const parser::AccClause &x) { 125 SetContextClause(x); 126 } 127 128 void AccStructureChecker::Leave(const parser::AccClauseList &) {} 129 130 void AccStructureChecker::Enter(const parser::OpenACCBlockConstruct &x) { 131 const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)}; 132 const auto &endBlockDir{std::get<parser::AccEndBlockDirective>(x.t)}; 133 const auto &beginAccBlockDir{ 134 std::get<parser::AccBlockDirective>(beginBlockDir.t)}; 135 136 CheckMatching(beginAccBlockDir, endBlockDir.v); 137 PushContextAndClauseSets(beginAccBlockDir.source, beginAccBlockDir.v); 138 } 139 140 void AccStructureChecker::Leave(const parser::OpenACCBlockConstruct &x) { 141 const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)}; 142 const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)}; 143 const parser::Block &block{std::get<parser::Block>(x.t)}; 144 switch (blockDir.v) { 145 case llvm::acc::Directive::ACCD_kernels: 146 case llvm::acc::Directive::ACCD_parallel: 147 // Restriction - 880-881 (KERNELS) 148 // Restriction - 843-844 (PARALLEL) 149 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 150 parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses); 151 // Restriction - 877 (KERNELS) 152 // Restriction - 840 (PARALLEL) 153 CheckNoBranching(block, GetContext().directive, blockDir.source); 154 break; 155 case llvm::acc::Directive::ACCD_serial: 156 // Restriction - 919 157 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 158 serialOnlyAllowedAfterDeviceTypeClauses); 159 // Restriction - 916 160 CheckNoBranching(block, llvm::acc::Directive::ACCD_serial, blockDir.source); 161 break; 162 case llvm::acc::Directive::ACCD_data: 163 // Restriction - 1117-1118 164 CheckRequireAtLeastOneOf(); 165 break; 166 case llvm::acc::Directive::ACCD_host_data: 167 // Restriction - 1578 168 CheckRequireAtLeastOneOf(); 169 break; 170 default: 171 break; 172 } 173 dirContext_.pop_back(); 174 } 175 176 void AccStructureChecker::CheckNoBranching(const parser::Block &block, 177 const llvm::acc::Directive directive, 178 const parser::CharBlock &directiveSource) const { 179 NoBranchingEnforce noBranchingEnforce{context_, directiveSource, directive}; 180 parser::Walk(block, noBranchingEnforce); 181 } 182 183 void AccStructureChecker::Enter( 184 const parser::OpenACCStandaloneDeclarativeConstruct &x) { 185 const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)}; 186 PushContextAndClauseSets(declarativeDir.source, declarativeDir.v); 187 } 188 189 void AccStructureChecker::Leave( 190 const parser::OpenACCStandaloneDeclarativeConstruct &) { 191 // Restriction - 2075 192 CheckAtLeastOneClause(); 193 dirContext_.pop_back(); 194 } 195 196 void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) { 197 const auto &beginCombinedDir{ 198 std::get<parser::AccBeginCombinedDirective>(x.t)}; 199 const auto &combinedDir{ 200 std::get<parser::AccCombinedDirective>(beginCombinedDir.t)}; 201 202 // check matching, End directive is optional 203 if (const auto &endCombinedDir{ 204 std::get<std::optional<parser::AccEndCombinedDirective>>(x.t)}) { 205 CheckMatching<parser::AccCombinedDirective>(combinedDir, endCombinedDir->v); 206 } 207 208 PushContextAndClauseSets(combinedDir.source, combinedDir.v); 209 } 210 211 void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) { 212 const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; 213 const auto &combinedDir{ 214 std::get<parser::AccCombinedDirective>(beginBlockDir.t)}; 215 switch (combinedDir.v) { 216 case llvm::acc::Directive::ACCD_kernels_loop: 217 case llvm::acc::Directive::ACCD_parallel_loop: 218 // Restriction - 1962 -> (880-881) (KERNELS LOOP) 219 // Restriction - 1962 -> (843-844) (PARALLEL LOOP) 220 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 221 {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait, 222 llvm::acc::Clause::ACCC_num_gangs, 223 llvm::acc::Clause::ACCC_num_workers, 224 llvm::acc::Clause::ACCC_vector_length}); 225 break; 226 case llvm::acc::Directive::ACCD_serial_loop: 227 // Restriction - 1962 -> (919) (SERIAL LOOP) 228 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 229 {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait}); 230 break; 231 default: 232 break; 233 } 234 dirContext_.pop_back(); 235 } 236 237 void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) { 238 const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; 239 const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)}; 240 PushContextAndClauseSets(loopDir.source, loopDir.v); 241 } 242 243 void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) { 244 const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; 245 const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)}; 246 if (loopDir.v == llvm::acc::Directive::ACCD_loop) { 247 // Restriction - 1615-1616 248 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 249 loopOnlyAllowedAfterDeviceTypeClauses); 250 // Restriction - 1622 251 CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq, 252 {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector, 253 llvm::acc::Clause::ACCC_worker}); 254 } 255 dirContext_.pop_back(); 256 } 257 258 void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) { 259 const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)}; 260 PushContextAndClauseSets(standaloneDir.source, standaloneDir.v); 261 } 262 263 void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) { 264 const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)}; 265 switch (standaloneDir.v) { 266 case llvm::acc::Directive::ACCD_enter_data: 267 case llvm::acc::Directive::ACCD_exit_data: 268 case llvm::acc::Directive::ACCD_set: 269 // Restriction - 1117-1118 (ENTER DATA) 270 // Restriction - 1161-1162 (EXIT DATA) 271 // Restriction - 2254 (SET) 272 CheckRequireAtLeastOneOf(); 273 break; 274 case llvm::acc::Directive::ACCD_update: 275 // Restriction - 2301 276 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 277 updateOnlyAllowedAfterDeviceTypeClauses); 278 break; 279 default: 280 break; 281 } 282 dirContext_.pop_back(); 283 } 284 285 void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) { 286 PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine); 287 } 288 void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) { 289 // Restriction - 2409 290 CheckRequireAtLeastOneOf(); 291 // Restriction - 2407-2408 292 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 293 routineOnlyAllowedAfterDeviceTypeClauses); 294 dirContext_.pop_back(); 295 } 296 297 void AccStructureChecker::Enter(const parser::OpenACCWaitConstruct &x) { 298 const auto &verbatim{std::get<parser::Verbatim>(x.t)}; 299 PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_wait); 300 } 301 void AccStructureChecker::Leave(const parser::OpenACCWaitConstruct &x) { 302 dirContext_.pop_back(); 303 } 304 305 void AccStructureChecker::Enter(const parser::OpenACCAtomicConstruct &x) { 306 PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_atomic); 307 } 308 void AccStructureChecker::Leave(const parser::OpenACCAtomicConstruct &x) { 309 dirContext_.pop_back(); 310 } 311 312 // Clause checkers 313 CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse, ACCC_collapse) 314 315 CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto) 316 CHECK_SIMPLE_CLAUSE(Async, ACCC_async) 317 CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach) 318 CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind) 319 CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture) 320 CHECK_SIMPLE_CLAUSE(Copy, ACCC_copy) 321 CHECK_SIMPLE_CLAUSE(Default, ACCC_default) 322 CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async) 323 CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete) 324 CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach) 325 CHECK_SIMPLE_CLAUSE(Device, ACCC_device) 326 CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num) 327 CHECK_SIMPLE_CLAUSE(Deviceptr, ACCC_deviceptr) 328 CHECK_SIMPLE_CLAUSE(DeviceResident, ACCC_device_resident) 329 CHECK_SIMPLE_CLAUSE(DeviceType, ACCC_device_type) 330 CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize) 331 CHECK_SIMPLE_CLAUSE(Firstprivate, ACCC_firstprivate) 332 CHECK_SIMPLE_CLAUSE(Gang, ACCC_gang) 333 CHECK_SIMPLE_CLAUSE(Host, ACCC_host) 334 CHECK_SIMPLE_CLAUSE(If, ACCC_if) 335 CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present) 336 CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent) 337 CHECK_SIMPLE_CLAUSE(Link, ACCC_link) 338 CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create) 339 CHECK_SIMPLE_CLAUSE(Nohost, ACCC_nohost) 340 CHECK_SIMPLE_CLAUSE(NumGangs, ACCC_num_gangs) 341 CHECK_SIMPLE_CLAUSE(NumWorkers, ACCC_num_workers) 342 CHECK_SIMPLE_CLAUSE(Present, ACCC_present) 343 CHECK_SIMPLE_CLAUSE(Private, ACCC_private) 344 CHECK_SIMPLE_CLAUSE(Read, ACCC_read) 345 CHECK_SIMPLE_CLAUSE(Reduction, ACCC_reduction) 346 CHECK_SIMPLE_CLAUSE(Self, ACCC_self) 347 CHECK_SIMPLE_CLAUSE(Seq, ACCC_seq) 348 CHECK_SIMPLE_CLAUSE(Tile, ACCC_tile) 349 CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device) 350 CHECK_SIMPLE_CLAUSE(Vector, ACCC_vector) 351 CHECK_SIMPLE_CLAUSE(VectorLength, ACCC_vector_length) 352 CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait) 353 CHECK_SIMPLE_CLAUSE(Worker, ACCC_worker) 354 CHECK_SIMPLE_CLAUSE(Write, ACCC_write) 355 356 void AccStructureChecker::Enter(const parser::AccClause::Create &c) { 357 CheckAllowed(llvm::acc::Clause::ACCC_create); 358 const auto &modifierClause{c.v}; 359 if (const auto &modifier{ 360 std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) { 361 if (modifier->v != parser::AccDataModifier::Modifier::Zero) { 362 context_.Say(GetContext().clauseSource, 363 "Only the ZERO modifier is allowed for the %s clause " 364 "on the %s directive"_err_en_US, 365 parser::ToUpperCaseLetters( 366 llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create) 367 .str()), 368 ContextDirectiveAsFortran()); 369 } 370 } 371 } 372 373 void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) { 374 CheckAllowed(llvm::acc::Clause::ACCC_copyin); 375 const auto &modifierClause{c.v}; 376 if (const auto &modifier{ 377 std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) { 378 if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) { 379 context_.Say(GetContext().clauseSource, 380 "Only the READONLY modifier is allowed for the %s clause " 381 "on the %s directive"_err_en_US, 382 parser::ToUpperCaseLetters( 383 llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin) 384 .str()), 385 ContextDirectiveAsFortran()); 386 } 387 } 388 } 389 390 void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) { 391 CheckAllowed(llvm::acc::Clause::ACCC_copyout); 392 const auto &modifierClause{c.v}; 393 if (const auto &modifier{ 394 std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) { 395 if (modifier->v != parser::AccDataModifier::Modifier::Zero) { 396 context_.Say(GetContext().clauseSource, 397 "Only the ZERO modifier is allowed for the %s clause " 398 "on the %s directive"_err_en_US, 399 parser::ToUpperCaseLetters( 400 llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout) 401 .str()), 402 ContextDirectiveAsFortran()); 403 } 404 } 405 } 406 407 llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) { 408 return llvm::acc::getOpenACCClauseName(clause); 409 } 410 411 llvm::StringRef AccStructureChecker::getDirectiveName( 412 llvm::acc::Directive directive) { 413 return llvm::acc::getOpenACCDirectiveName(directive); 414 } 415 416 } // namespace Fortran::semantics 417