[flang-commits] [flang] 9aa3dca - [flang][openacc] Semantic checks for OpenACC 3.0 clauses validity

via flang-commits flang-commits at lists.llvm.org
Tue Jul 14 17:44:43 PDT 2020


Author: Valentin Clement
Date: 2020-07-14T20:44:35-04:00
New Revision: 9aa3dca80f5cfb67640d53998673ad636ac8b4c9

URL: https://github.com/llvm/llvm-project/commit/9aa3dca80f5cfb67640d53998673ad636ac8b4c9
DIFF: https://github.com/llvm/llvm-project/commit/9aa3dca80f5cfb67640d53998673ad636ac8b4c9.diff

LOG: [flang][openacc] Semantic checks for OpenACC 3.0 clauses validity

Summary: This patch adds semantic checking for the OpenACC 3.0 clauses validity.

Reviewers: sscalpone, tskeith, klausler, ichoyjx, DavidTruby, jdoerfert

Reviewed By: tskeith, klausler

Subscribers: mgorny, llvm-commits

Tags: #llvm, #flang

Differential Revision: https://reviews.llvm.org/D83807

Added: 
    flang/lib/Semantics/canonicalize-acc.cpp
    flang/lib/Semantics/canonicalize-acc.h
    flang/lib/Semantics/check-acc-structure.cpp
    flang/lib/Semantics/check-acc-structure.h
    flang/test/Semantics/acc-branch.f90
    flang/test/Semantics/acc-clause-validity.f90

Modified: 
    flang/lib/Semantics/CMakeLists.txt
    flang/lib/Semantics/semantics.cpp
    llvm/include/llvm/Frontend/OpenACC/ACC.td
    llvm/include/llvm/Frontend/OpenACC/CMakeLists.txt

Removed: 
    flang/test/Semantics/acc-validity.f90


################################################################################
diff  --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt
index 05295f590095..a869d831109b 100644
--- a/flang/lib/Semantics/CMakeLists.txt
+++ b/flang/lib/Semantics/CMakeLists.txt
@@ -2,8 +2,10 @@
 add_flang_library(FortranSemantics
   assignment.cpp
   attr.cpp
+  canonicalize-acc.cpp
   canonicalize-do.cpp
   canonicalize-omp.cpp
+  check-acc-structure.cpp
   check-allocate.cpp
   check-arithmeticif.cpp
   check-call.cpp

diff  --git a/flang/lib/Semantics/canonicalize-acc.cpp b/flang/lib/Semantics/canonicalize-acc.cpp
new file mode 100644
index 000000000000..4c4d716fe7de
--- /dev/null
+++ b/flang/lib/Semantics/canonicalize-acc.cpp
@@ -0,0 +1,84 @@
+//===-- lib/Semantics/canonicalize-acc.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "canonicalize-acc.h"
+#include "flang/Parser/parse-tree-visitor.h"
+#include "flang/Semantics/tools.h"
+
+// After Loop Canonicalization, rewrite OpenACC parse tree to make OpenACC
+// Constructs more structured which provide explicit scopes for later
+// structural checks and semantic analysis.
+//   1. move structured DoConstruct into
+//      OpenACCLoopConstruct. Compilation will not proceed in case of errors
+//      after this pass.
+namespace Fortran::semantics {
+
+using namespace parser::literals;
+
+class CanonicalizationOfAcc {
+public:
+  template <typename T> bool Pre(T &) { return true; }
+  template <typename T> void Post(T &) {}
+  CanonicalizationOfAcc(parser::Messages &messages) : messages_{messages} {}
+
+  void Post(parser::Block &block) {
+    for (auto it{block.begin()}; it != block.end(); ++it) {
+      if (auto *accLoop{parser::Unwrap<parser::OpenACCLoopConstruct>(*it)}) {
+        RewriteOpenACCLoopConstruct(*accLoop, block, it);
+      }
+    } // Block list
+  }
+
+private:
+  void RewriteOpenACCLoopConstruct(parser::OpenACCLoopConstruct &x,
+      parser::Block &block, parser::Block::iterator it) {
+    // Check the sequence of DoConstruct in the same iteration
+    //
+    // Original:
+    //   ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct
+    //     ACCBeginLoopDirective
+    //   ExecutableConstruct -> DoConstruct
+    //
+    // After rewriting:
+    //   ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct
+    //     AccBeginLoopDirective
+    //     DoConstruct
+    parser::Block::iterator nextIt;
+    auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
+    auto &dir{std::get<parser::AccLoopDirective>(beginDir.t)};
+
+    nextIt = it;
+    if (++nextIt != block.end()) {
+      if (auto *doCons{parser::Unwrap<parser::DoConstruct>(*nextIt)}) {
+        if (doCons->GetLoopControl()) {
+          // move DoConstruct
+          std::get<std::optional<parser::DoConstruct>>(x.t) =
+              std::move(*doCons);
+          nextIt = block.erase(nextIt);
+        } else {
+          messages_.Say(dir.source,
+              "DO loop after the %s directive must have loop control"_err_en_US,
+              parser::ToUpperCaseLetters(dir.source.ToString()));
+        }
+        return; // found do-loop
+      }
+    }
+    messages_.Say(dir.source,
+        "A DO loop must follow the %s directive"_err_en_US,
+        parser::ToUpperCaseLetters(dir.source.ToString()));
+  }
+
+  parser::Messages &messages_;
+};
+
+bool CanonicalizeAcc(parser::Messages &messages, parser::Program &program) {
+  CanonicalizationOfAcc acc{messages};
+  Walk(program, acc);
+  return !messages.AnyFatalError();
+}
+} // namespace Fortran::semantics

diff  --git a/flang/lib/Semantics/canonicalize-acc.h b/flang/lib/Semantics/canonicalize-acc.h
new file mode 100644
index 000000000000..f24f9fbc44f3
--- /dev/null
+++ b/flang/lib/Semantics/canonicalize-acc.h
@@ -0,0 +1,21 @@
+//===-- lib/Semantics/canonicalize-acc.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SEMANTICS_CANONICALIZE_ACC_H_
+#define FORTRAN_SEMANTICS_CANONICALIZE_ACC_H_
+
+namespace Fortran::parser {
+struct Program;
+class Messages;
+} // namespace Fortran::parser
+
+namespace Fortran::semantics {
+bool CanonicalizeAcc(parser::Messages &messages, parser::Program &program);
+}
+
+#endif // FORTRAN_SEMANTICS_CANONICALIZE_ACC_H_

diff  --git a/flang/lib/Semantics/check-acc-structure.cpp b/flang/lib/Semantics/check-acc-structure.cpp
new file mode 100644
index 000000000000..974c9dc59abe
--- /dev/null
+++ b/flang/lib/Semantics/check-acc-structure.cpp
@@ -0,0 +1,501 @@
+//===-- lib/Semantics/check-acc-structure.cpp -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "check-acc-structure.h"
+#include "flang/Parser/parse-tree.h"
+#include "flang/Semantics/tools.h"
+
+#define CHECK_SIMPLE_CLAUSE(X, Y) \
+  void AccStructureChecker::Enter(const parser::AccClause::X &) { \
+    CheckAllowed(llvm::acc::Clause::Y); \
+  }
+
+#define CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(X, Y) \
+  void AccStructureChecker::Enter(const parser::AccClause::X &c) { \
+    CheckAllowed(llvm::acc::Clause::Y); \
+    RequiresConstantPositiveParameter(llvm::acc::Clause::Y, c.v); \
+  }
+
+namespace Fortran::semantics {
+
+static constexpr inline AccClauseSet
+    parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses{
+        llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
+        llvm::acc::Clause::ACCC_num_gangs, llvm::acc::Clause::ACCC_num_workers,
+        llvm::acc::Clause::ACCC_vector_length};
+
+static constexpr inline AccClauseSet serialOnlyAllowedAfterDeviceTypeClauses{
+    llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait};
+
+static constexpr inline AccClauseSet loopOnlyAllowedAfterDeviceTypeClauses{
+    llvm::acc::Clause::ACCC_auto, llvm::acc::Clause::ACCC_collapse,
+    llvm::acc::Clause::ACCC_independent, llvm::acc::Clause::ACCC_gang,
+    llvm::acc::Clause::ACCC_seq, llvm::acc::Clause::ACCC_tile,
+    llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker};
+
+static constexpr inline AccClauseSet updateOnlyAllowedAfterDeviceTypeClauses{
+    llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait};
+
+static constexpr inline AccClauseSet routineOnlyAllowedAfterDeviceTypeClauses{
+    llvm::acc::Clause::ACCC_bind, llvm::acc::Clause::ACCC_gang,
+    llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker};
+
+class NoBranchingEnforce {
+public:
+  NoBranchingEnforce(SemanticsContext &context,
+      parser::CharBlock sourcePosition, llvm::acc::Directive directive)
+      : context_{context}, sourcePosition_{sourcePosition}, currentDirective_{
+                                                                directive} {}
+  template <typename T> bool Pre(const T &) { return true; }
+  template <typename T> void Post(const T &) {}
+
+  template <typename T> bool Pre(const parser::Statement<T> &statement) {
+    currentStatementSourcePosition_ = statement.source;
+    return true;
+  }
+
+  void Post(const parser::ReturnStmt &) { emitBranchOutError("RETURN"); }
+  void Post(const parser::ExitStmt &) { emitBranchOutError("EXIT"); }
+  void Post(const parser::StopStmt &) { emitBranchOutError("STOP"); }
+
+private:
+  parser::MessageFixedText GetEnclosingMsg() {
+    return "Enclosing block construct"_en_US;
+  }
+
+  void emitBranchOutError(const char *stmt) {
+    context_
+        .Say(currentStatementSourcePosition_,
+            "%s statement is not allowed in a %s construct"_err_en_US, stmt,
+            parser::ToUpperCaseLetters(
+                llvm::acc::getOpenACCDirectiveName(currentDirective_).str()))
+        .Attach(sourcePosition_, GetEnclosingMsg());
+  }
+
+  SemanticsContext &context_;
+  parser::CharBlock currentStatementSourcePosition_;
+  parser::CharBlock sourcePosition_;
+  llvm::acc::Directive currentDirective_;
+};
+
+void AccStructureChecker::Enter(const parser::AccClause &x) {
+  SetContextClause(x);
+}
+
+void AccStructureChecker::Leave(const parser::AccClauseList &) {}
+
+void AccStructureChecker::Enter(const parser::OpenACCBlockConstruct &x) {
+  const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
+  const auto &endBlockDir{std::get<parser::AccEndBlockDirective>(x.t)};
+  const auto &beginAccBlockDir{
+      std::get<parser::AccBlockDirective>(beginBlockDir.t)};
+
+  CheckMatching(beginAccBlockDir, endBlockDir.v);
+  PushContextAndClauseSets(beginAccBlockDir.source, beginAccBlockDir.v);
+}
+
+void AccStructureChecker::Leave(const parser::OpenACCBlockConstruct &x) {
+  const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
+  const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
+  const parser::Block &block{std::get<parser::Block>(x.t)};
+  switch (blockDir.v) {
+  case llvm::acc::Directive::ACCD_kernels:
+  case llvm::acc::Directive::ACCD_parallel:
+    // Restriction - 880-881 (KERNELS)
+    // Restriction - 843-844 (PARALLEL)
+    CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
+        parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses);
+    // Restriction - 877 (KERNELS)
+    // Restriction - 840 (PARALLEL)
+    CheckNoBranching(block, GetContext().directive, blockDir.source);
+    break;
+  case llvm::acc::Directive::ACCD_serial:
+    // Restriction - 919
+    CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
+        serialOnlyAllowedAfterDeviceTypeClauses);
+    // Restriction - 916
+    CheckNoBranching(block, llvm::acc::Directive::ACCD_serial, blockDir.source);
+    break;
+  case llvm::acc::Directive::ACCD_data:
+    // Restriction - 1117-1118
+    CheckRequireAtLeastOneOf();
+    break;
+  case llvm::acc::Directive::ACCD_host_data:
+    // Restriction - 1578
+    CheckRequireAtLeastOneOf();
+    break;
+  default:
+    break;
+  }
+  accContext_.pop_back();
+}
+
+void AccStructureChecker::CheckNoBranching(const parser::Block &block,
+    const llvm::acc::Directive directive,
+    const parser::CharBlock &directiveSource) const {
+  NoBranchingEnforce noBranchingEnforce{context_, directiveSource, directive};
+  parser::Walk(block, noBranchingEnforce);
+}
+
+void AccStructureChecker::Enter(
+    const parser::OpenACCStandaloneDeclarativeConstruct &x) {
+  const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)};
+  PushContextAndClauseSets(declarativeDir.source, declarativeDir.v);
+}
+
+void AccStructureChecker::Leave(
+    const parser::OpenACCStandaloneDeclarativeConstruct &) {
+  // Restriction - 2075
+  CheckAtLeastOneClause();
+  accContext_.pop_back();
+}
+
+void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) {
+  const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
+  const auto &combinedDir{
+      std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
+  PushContextAndClauseSets(combinedDir.source, combinedDir.v);
+}
+
+void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) {
+  const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
+  const auto &combinedDir{
+      std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
+  switch (combinedDir.v) {
+  case llvm::acc::Directive::ACCD_kernels_loop:
+  case llvm::acc::Directive::ACCD_parallel_loop:
+    // Restriction - 1962 -> (880-881) (KERNELS LOOP)
+    // Restriction - 1962 -> (843-844) (PARALLEL LOOP)
+    CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
+        {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
+            llvm::acc::Clause::ACCC_num_gangs,
+            llvm::acc::Clause::ACCC_num_workers,
+            llvm::acc::Clause::ACCC_vector_length});
+    break;
+  case llvm::acc::Directive::ACCD_serial_loop:
+    // Restriction - 1962 -> (919) (SERIAL LOOP)
+    CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
+        {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait});
+    break;
+  default:
+    break;
+  }
+  accContext_.pop_back();
+}
+
+std::string AccStructureChecker::ContextDirectiveAsFortran() {
+  return parser::ToUpperCaseLetters(
+      llvm::acc::getOpenACCDirectiveName(GetContext().directive).str());
+}
+
+void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) {
+  const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
+  const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
+  PushContextAndClauseSets(loopDir.source, loopDir.v);
+}
+
+void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) {
+  const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
+  const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
+  if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
+    // Restriction - 1615-1616
+    CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
+        loopOnlyAllowedAfterDeviceTypeClauses);
+    // Restriction - 1622
+    CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq,
+        {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector,
+            llvm::acc::Clause::ACCC_worker});
+  }
+  accContext_.pop_back();
+}
+
+void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) {
+  const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
+  PushContextAndClauseSets(standaloneDir.source, standaloneDir.v);
+}
+
+void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) {
+  const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
+  switch (standaloneDir.v) {
+  case llvm::acc::Directive::ACCD_enter_data:
+  case llvm::acc::Directive::ACCD_exit_data:
+  case llvm::acc::Directive::ACCD_set:
+    // Restriction - 1117-1118 (ENTER DATA)
+    // Restriction - 1161-1162 (EXIT DATA)
+    // Restriction - 2254 (SET)
+    CheckRequireAtLeastOneOf();
+    break;
+  case llvm::acc::Directive::ACCD_update:
+    // Restriction - 2301
+    CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
+        updateOnlyAllowedAfterDeviceTypeClauses);
+    break;
+  default:
+    break;
+  }
+  accContext_.pop_back();
+}
+
+void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) {
+  PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine);
+}
+void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) {
+  // Restriction - 2409
+  CheckRequireAtLeastOneOf();
+  // Restriction - 2407-2408
+  CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
+      routineOnlyAllowedAfterDeviceTypeClauses);
+  accContext_.pop_back();
+}
+
+// Clause checkers
+CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse, ACCC_collapse)
+
+CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto)
+CHECK_SIMPLE_CLAUSE(Async, ACCC_async)
+CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach)
+CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind)
+CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture)
+CHECK_SIMPLE_CLAUSE(Copy, ACCC_copy)
+CHECK_SIMPLE_CLAUSE(Default, ACCC_default)
+CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async)
+CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete)
+CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach)
+CHECK_SIMPLE_CLAUSE(Device, ACCC_device)
+CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num)
+CHECK_SIMPLE_CLAUSE(DevicePtr, ACCC_deviceptr)
+CHECK_SIMPLE_CLAUSE(DeviceResident, ACCC_device_resident)
+CHECK_SIMPLE_CLAUSE(DeviceType, ACCC_device_type)
+CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize)
+CHECK_SIMPLE_CLAUSE(FirstPrivate, ACCC_firstprivate)
+CHECK_SIMPLE_CLAUSE(Gang, ACCC_gang)
+CHECK_SIMPLE_CLAUSE(Host, ACCC_host)
+CHECK_SIMPLE_CLAUSE(If, ACCC_if)
+CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present)
+CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent)
+CHECK_SIMPLE_CLAUSE(Link, ACCC_link)
+CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create)
+CHECK_SIMPLE_CLAUSE(NoHost, ACCC_nohost)
+CHECK_SIMPLE_CLAUSE(NumGangs, ACCC_num_gangs)
+CHECK_SIMPLE_CLAUSE(NumWorkers, ACCC_num_workers)
+CHECK_SIMPLE_CLAUSE(Present, ACCC_present)
+CHECK_SIMPLE_CLAUSE(Private, ACCC_private)
+CHECK_SIMPLE_CLAUSE(Read, ACCC_read)
+CHECK_SIMPLE_CLAUSE(Reduction, ACCC_reduction)
+CHECK_SIMPLE_CLAUSE(Self, ACCC_self)
+CHECK_SIMPLE_CLAUSE(Seq, ACCC_seq)
+CHECK_SIMPLE_CLAUSE(Tile, ACCC_tile)
+CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device)
+CHECK_SIMPLE_CLAUSE(Vector, ACCC_vector)
+CHECK_SIMPLE_CLAUSE(VectorLength, ACCC_vector_length)
+CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait)
+CHECK_SIMPLE_CLAUSE(Worker, ACCC_worker)
+CHECK_SIMPLE_CLAUSE(Write, ACCC_write)
+
+void AccStructureChecker::Enter(const parser::AccClause::Create &c) {
+  CheckAllowed(llvm::acc::Clause::ACCC_create);
+  const auto &modifierClause{c.v};
+  if (const auto &modifier{
+          std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
+    if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
+      context_.Say(GetContext().clauseSource,
+          "Only the ZERO modifier is allowed for the %s clause "
+          "on the %s directive"_err_en_US,
+          parser::ToUpperCaseLetters(
+              llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create)
+                  .str()),
+          ContextDirectiveAsFortran());
+    }
+  }
+}
+
+void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) {
+  CheckAllowed(llvm::acc::Clause::ACCC_copyin);
+  const auto &modifierClause{c.v};
+  if (const auto &modifier{
+          std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
+    if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) {
+      context_.Say(GetContext().clauseSource,
+          "Only the READONLY modifier is allowed for the %s clause "
+          "on the %s directive"_err_en_US,
+          parser::ToUpperCaseLetters(
+              llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin)
+                  .str()),
+          ContextDirectiveAsFortran());
+    }
+  }
+}
+
+void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) {
+  CheckAllowed(llvm::acc::Clause::ACCC_copyout);
+  const auto &modifierClause{c.v};
+  if (const auto &modifier{
+          std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
+    if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
+      context_.Say(GetContext().clauseSource,
+          "Only the ZERO modifier is allowed for the %s clause "
+          "on the %s directive"_err_en_US,
+          parser::ToUpperCaseLetters(
+              llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout)
+                  .str()),
+          ContextDirectiveAsFortran());
+    }
+  }
+}
+
+void AccStructureChecker::CheckAllowed(llvm::acc::Clause clause) {
+  if (!GetContext().allowedClauses.test(clause) &&
+      !GetContext().allowedOnceClauses.test(clause) &&
+      !GetContext().allowedExclusiveClauses.test(clause) &&
+      !GetContext().requiredClauses.test(clause)) {
+    context_.Say(GetContext().clauseSource,
+        "%s clause is not allowed on the %s directive"_err_en_US,
+        parser::ToUpperCaseLetters(
+            llvm::acc::getOpenACCClauseName(clause).str()),
+        parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
+    return;
+  }
+  if ((GetContext().allowedOnceClauses.test(clause) ||
+          GetContext().allowedExclusiveClauses.test(clause)) &&
+      FindClause(clause)) {
+    context_.Say(GetContext().clauseSource,
+        "At most one %s clause can appear on the %s directive"_err_en_US,
+        parser::ToUpperCaseLetters(
+            llvm::acc::getOpenACCClauseName(clause).str()),
+        parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
+    return;
+  }
+  if (GetContext().allowedExclusiveClauses.test(clause)) {
+    std::vector<llvm::acc::Clause> others;
+    GetContext().allowedExclusiveClauses.IterateOverMembers(
+        [&](llvm::acc::Clause o) {
+          if (FindClause(o)) {
+            others.emplace_back(o);
+          }
+        });
+    for (const auto &e : others) {
+      context_.Say(GetContext().clauseSource,
+          "%s and %s clauses are mutually exclusive and may not appear on the "
+          "same %s directive"_err_en_US,
+          parser::ToUpperCaseLetters(
+              llvm::acc::getOpenACCClauseName(clause).str()),
+          parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(e).str()),
+          parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
+    }
+    if (!others.empty()) {
+      return;
+    }
+  }
+  SetContextClauseInfo(clause);
+  AddClauseToCrtContext(clause);
+}
+
+void AccStructureChecker::CheckOnlyAllowedAfter(
+    llvm::acc::Clause clause, AccClauseSet set) {
+  bool enforceCheck = false;
+  for (auto cl : GetContext().actualClauses) {
+    if (cl == clause) {
+      enforceCheck = true;
+      continue;
+    } else if (enforceCheck && !set.test(cl)) {
+      auto parserClause = GetContext().clauseInfo.find(cl);
+      context_.Say(parserClause->second->source,
+          "Clause %s is not allowed after clause %s on the %s "
+          "directive"_err_en_US,
+          parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()),
+          parser::ToUpperCaseLetters(
+              llvm::acc::getOpenACCClauseName(clause).str()),
+          ContextDirectiveAsFortran());
+    }
+  }
+}
+
+void AccStructureChecker::CheckRequireAtLeastOneOf() {
+  for (auto cl : GetContext().actualClauses) {
+    if (GetContext().requiredClauses.test(cl))
+      return;
+  }
+  // No clause matched in the actual clauses list
+  context_.Say(GetContext().directiveSource,
+      "At least one of %s clause must appear on the %s directive"_err_en_US,
+      ClauseSetToString(GetContext().requiredClauses),
+      ContextDirectiveAsFortran());
+}
+
+void AccStructureChecker::CheckAtLeastOneClause() {
+  if (GetContext().actualClauses.empty()) {
+    context_.Say(GetContext().directiveSource,
+        "At least one clause is required on the %s directive"_err_en_US,
+        ContextDirectiveAsFortran());
+  }
+}
+
+// Enforce restriction where clauses in the given set are not allowed if the
+// given clause appears.
+void AccStructureChecker::CheckNotAllowedIfClause(
+    llvm::acc::Clause clause, AccClauseSet set) {
+  if (std::find(GetContext().actualClauses.begin(),
+          GetContext().actualClauses.end(),
+          clause) == GetContext().actualClauses.end()) {
+    return; // Clause is not present
+  }
+
+  for (auto cl : GetContext().actualClauses) {
+    if (set.test(cl)) {
+      context_.Say(GetContext().directiveSource,
+          "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,
+          parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()),
+          parser::ToUpperCaseLetters(
+              llvm::acc::getOpenACCClauseName(clause).str()),
+          ContextDirectiveAsFortran());
+    }
+  }
+}
+
+void AccStructureChecker::RequiresConstantPositiveParameter(
+    const llvm::acc::Clause &clause, const parser::ScalarIntConstantExpr &i) {
+  if (const auto v{GetIntValue(i)}) {
+    if (*v <= 0) {
+      context_.Say(GetContext().clauseSource,
+          "The parameter of the %s clause on the %s directive must be "
+          "a constant positive integer expression"_err_en_US,
+          parser::ToUpperCaseLetters(
+              llvm::acc::getOpenACCClauseName(clause).str()),
+          ContextDirectiveAsFortran());
+    }
+  }
+}
+
+void AccStructureChecker::OptionalConstantPositiveParameter(
+    const llvm::acc::Clause &clause,
+    const std::optional<parser::ScalarIntConstantExpr> &o) {
+  if (o != std::nullopt) {
+    RequiresConstantPositiveParameter(clause, o.value());
+  }
+}
+
+std::string AccStructureChecker::ClauseSetToString(const AccClauseSet set) {
+  std::string list;
+  set.IterateOverMembers([&](llvm::acc::Clause o) {
+    if (!list.empty())
+      list.append(", ");
+    list.append(
+        parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(o).str()));
+  });
+  return list;
+}
+
+void AccStructureChecker::SayNotMatching(
+    const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
+  context_
+      .Say(endSource, "Unmatched %s directive"_err_en_US,
+          parser::ToUpperCaseLetters(endSource.ToString()))
+      .Attach(beginSource, "Does not match directive"_en_US);
+}
+
+} // namespace Fortran::semantics

diff  --git a/flang/lib/Semantics/check-acc-structure.h b/flang/lib/Semantics/check-acc-structure.h
new file mode 100644
index 000000000000..fef12383952d
--- /dev/null
+++ b/flang/lib/Semantics/check-acc-structure.h
@@ -0,0 +1,204 @@
+//===-- lib/Semantics/check-acc-structure.h ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// OpenACC structure validity check list
+//    1. invalid clauses on directive
+//    2. invalid repeated clauses on directive
+//    3. invalid nesting of regions
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SEMANTICS_CHECK_ACC_STRUCTURE_H_
+#define FORTRAN_SEMANTICS_CHECK_ACC_STRUCTURE_H_
+
+#include "flang/Common/enum-set.h"
+#include "flang/Parser/parse-tree.h"
+#include "flang/Semantics/semantics.h"
+#include "llvm/Frontend/OpenACC/ACC.h.inc"
+
+#include <unordered_map>
+
+using AccDirectiveSet = Fortran::common::EnumSet<llvm::acc::Directive,
+    llvm::acc::Directive_enumSize>;
+
+using AccClauseSet =
+    Fortran::common::EnumSet<llvm::acc::Clause, llvm::acc::Clause_enumSize>;
+
+#define GEN_FLANG_DIRECTIVE_CLAUSE_SETS
+#include "llvm/Frontend/OpenACC/ACC.cpp.inc"
+
+namespace Fortran::semantics {
+
+class AccStructureChecker : public virtual BaseChecker {
+public:
+  AccStructureChecker(SemanticsContext &context) : context_{context} {}
+
+  // Construct and directives
+  void Enter(const parser::OpenACCBlockConstruct &);
+  void Leave(const parser::OpenACCBlockConstruct &);
+  void Enter(const parser::OpenACCCombinedConstruct &);
+  void Leave(const parser::OpenACCCombinedConstruct &);
+  void Enter(const parser::OpenACCLoopConstruct &);
+  void Leave(const parser::OpenACCLoopConstruct &);
+  void Enter(const parser::OpenACCRoutineConstruct &);
+  void Leave(const parser::OpenACCRoutineConstruct &);
+  void Enter(const parser::OpenACCStandaloneConstruct &);
+  void Leave(const parser::OpenACCStandaloneConstruct &);
+  void Enter(const parser::OpenACCStandaloneDeclarativeConstruct &);
+  void Leave(const parser::OpenACCStandaloneDeclarativeConstruct &);
+
+  // Clauses
+  void Leave(const parser::AccClauseList &);
+  void Enter(const parser::AccClause &);
+
+  void Enter(const parser::AccClause::Auto &);
+  void Enter(const parser::AccClause::Async &);
+  void Enter(const parser::AccClause::Attach &);
+  void Enter(const parser::AccClause::Bind &);
+  void Enter(const parser::AccClause::Capture &);
+  void Enter(const parser::AccClause::Create &);
+  void Enter(const parser::AccClause::Collapse &);
+  void Enter(const parser::AccClause::Copy &);
+  void Enter(const parser::AccClause::Copyin &);
+  void Enter(const parser::AccClause::Copyout &);
+  void Enter(const parser::AccClause::Default &);
+  void Enter(const parser::AccClause::DefaultAsync &);
+  void Enter(const parser::AccClause::Delete &);
+  void Enter(const parser::AccClause::Detach &);
+  void Enter(const parser::AccClause::Device &);
+  void Enter(const parser::AccClause::DeviceNum &);
+  void Enter(const parser::AccClause::DevicePtr &);
+  void Enter(const parser::AccClause::DeviceResident &);
+  void Enter(const parser::AccClause::DeviceType &);
+  void Enter(const parser::AccClause::Finalize &);
+  void Enter(const parser::AccClause::FirstPrivate &);
+  void Enter(const parser::AccClause::Gang &);
+  void Enter(const parser::AccClause::Host &);
+  void Enter(const parser::AccClause::If &);
+  void Enter(const parser::AccClause::IfPresent &);
+  void Enter(const parser::AccClause::Independent &);
+  void Enter(const parser::AccClause::Link &);
+  void Enter(const parser::AccClause::NoCreate &);
+  void Enter(const parser::AccClause::NoHost &);
+  void Enter(const parser::AccClause::NumGangs &);
+  void Enter(const parser::AccClause::NumWorkers &);
+  void Enter(const parser::AccClause::Present &);
+  void Enter(const parser::AccClause::Private &);
+  void Enter(const parser::AccClause::Read &);
+  void Enter(const parser::AccClause::Reduction &);
+  void Enter(const parser::AccClause::Self &);
+  void Enter(const parser::AccClause::Seq &);
+  void Enter(const parser::AccClause::Tile &);
+  void Enter(const parser::AccClause::UseDevice &);
+  void Enter(const parser::AccClause::Vector &);
+  void Enter(const parser::AccClause::VectorLength &);
+  void Enter(const parser::AccClause::Wait &);
+  void Enter(const parser::AccClause::Worker &);
+  void Enter(const parser::AccClause::Write &);
+
+private:
+#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
+#include "llvm/Frontend/OpenACC/ACC.cpp.inc"
+
+  struct AccContext {
+    AccContext(parser::CharBlock source, llvm::acc::Directive d)
+        : directiveSource{source}, directive{d} {}
+
+    parser::CharBlock directiveSource{nullptr};
+    parser::CharBlock clauseSource{nullptr};
+    llvm::acc::Directive directive;
+    AccClauseSet allowedClauses{};
+    AccClauseSet allowedOnceClauses{};
+    AccClauseSet allowedExclusiveClauses{};
+    AccClauseSet requiredClauses{};
+
+    const parser::AccClause *clause{nullptr};
+    std::multimap<llvm::acc::Clause, const parser::AccClause *> clauseInfo;
+    std::list<llvm::acc::Clause> actualClauses;
+  };
+
+  // back() is the top of the stack
+  AccContext &GetContext() {
+    CHECK(!accContext_.empty());
+    return accContext_.back();
+  }
+
+  void SetContextClause(const parser::AccClause &clause) {
+    GetContext().clauseSource = clause.source;
+    GetContext().clause = &clause;
+  }
+
+  void SetContextClauseInfo(llvm::acc::Clause type) {
+    GetContext().clauseInfo.emplace(type, GetContext().clause);
+  }
+
+  void AddClauseToCrtContext(llvm::acc::Clause type) {
+    GetContext().actualClauses.push_back(type);
+  }
+
+  const parser::AccClause *FindClause(llvm::acc::Clause type) {
+    auto it{GetContext().clauseInfo.find(type)};
+    if (it != GetContext().clauseInfo.end()) {
+      return it->second;
+    }
+    return nullptr;
+  }
+
+  void PushContext(const parser::CharBlock &source, llvm::acc::Directive dir) {
+    accContext_.emplace_back(source, dir);
+  }
+
+  void SetClauseSets(llvm::acc::Directive dir) {
+    accContext_.back().allowedClauses = directiveClausesTable[dir].allowed;
+    accContext_.back().allowedOnceClauses =
+        directiveClausesTable[dir].allowedOnce;
+    accContext_.back().allowedExclusiveClauses =
+        directiveClausesTable[dir].allowedExclusive;
+    accContext_.back().requiredClauses =
+        directiveClausesTable[dir].requiredOneOf;
+  }
+  void PushContextAndClauseSets(
+      const parser::CharBlock &source, llvm::acc::Directive dir) {
+    PushContext(source, dir);
+    SetClauseSets(dir);
+  }
+
+  void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &);
+
+  template <typename B> void CheckMatching(const B &beginDir, const B &endDir) {
+    const auto &begin{beginDir.v};
+    const auto &end{endDir.v};
+    if (begin != end) {
+      SayNotMatching(beginDir.source, endDir.source);
+    }
+  }
+
+  // Check that only clauses in set are after the specific clauses.
+  void CheckOnlyAllowedAfter(llvm::acc::Clause clause, AccClauseSet set);
+  void CheckRequireAtLeastOneOf();
+  void CheckAllowed(llvm::acc::Clause clause);
+  void CheckAtLeastOneClause();
+  void CheckNotAllowedIfClause(llvm::acc::Clause clause, AccClauseSet set);
+  std::string ContextDirectiveAsFortran();
+
+  void CheckNoBranching(const parser::Block &block,
+      const llvm::acc::Directive directive,
+      const parser::CharBlock &directiveSource) const;
+
+  void RequiresConstantPositiveParameter(
+      const llvm::acc::Clause &clause, const parser::ScalarIntConstantExpr &i);
+  void OptionalConstantPositiveParameter(const llvm::acc::Clause &clause,
+      const std::optional<parser::ScalarIntConstantExpr> &o);
+
+  SemanticsContext &context_;
+  std::vector<AccContext> accContext_; // used as a stack
+
+  std::string ClauseSetToString(const AccClauseSet set);
+};
+
+} // namespace Fortran::semantics
+
+#endif // FORTRAN_SEMANTICS_CHECK_ACC_STRUCTURE_H_

diff  --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index 681e1dc5ca27..e949c92ff6dd 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -8,8 +8,10 @@
 
 #include "flang/Semantics/semantics.h"
 #include "assignment.h"
+#include "canonicalize-acc.h"
 #include "canonicalize-do.h"
 #include "canonicalize-omp.h"
+#include "check-acc-structure.h"
 #include "check-allocate.h"
 #include "check-arithmeticif.h"
 #include "check-case.h"
@@ -154,12 +156,12 @@ class MiscChecker : public virtual BaseChecker {
 };
 
 using StatementSemanticsPass1 = ExprChecker;
-using StatementSemanticsPass2 = SemanticsVisitor<AllocateChecker,
-    ArithmeticIfStmtChecker, AssignmentChecker, CaseChecker, CoarrayChecker,
-    DataChecker, DeallocateChecker, DoForallChecker, IfStmtChecker, IoChecker,
-    MiscChecker, NamelistChecker, NullifyChecker, OmpStructureChecker,
-    PurityChecker, ReturnStmtChecker, SelectRankConstructChecker,
-    SelectTypeChecker, StopChecker>;
+using StatementSemanticsPass2 = SemanticsVisitor<AccStructureChecker,
+    AllocateChecker, ArithmeticIfStmtChecker, AssignmentChecker, CaseChecker,
+    CoarrayChecker, DataChecker, DeallocateChecker, DoForallChecker,
+    IfStmtChecker, IoChecker, MiscChecker, NamelistChecker, NullifyChecker,
+    OmpStructureChecker, PurityChecker, ReturnStmtChecker,
+    SelectRankConstructChecker, SelectTypeChecker, StopChecker>;
 
 static bool PerformStatementSemantics(
     SemanticsContext &context, parser::Program &program) {
@@ -325,6 +327,7 @@ SymbolVector SemanticsContext::GetIndexVars(IndexVarKind kind) {
 bool Semantics::Perform() {
   return ValidateLabels(context_, program_) &&
       parser::CanonicalizeDo(program_) && // force line break
+      CanonicalizeAcc(context_.messages(), program_) &&
       CanonicalizeOmp(context_.messages(), program_) &&
       PerformStatementSemantics(context_, program_) &&
       ModFileWriter{context_}.WriteAll();

diff  --git a/flang/test/Semantics/acc-branch.f90 b/flang/test/Semantics/acc-branch.f90
new file mode 100644
index 000000000000..b1c2a6b860e4
--- /dev/null
+++ b/flang/test/Semantics/acc-branch.f90
@@ -0,0 +1,101 @@
+! RUN: %S/test_errors.sh %s %t %f18 -fopenacc
+
+! Check OpenACC restruction in branch in and out of some construct
+!
+
+program openacc_clause_validity
+
+  implicit none
+
+  integer :: i
+  integer :: N = 256
+  real(8) :: a(256)
+
+  !$acc parallel
+  !$acc loop
+  do i = 1, N
+    a(i) = 3.14
+    !ERROR: RETURN statement is not allowed in a PARALLEL construct
+    return
+  end do
+  !$acc end parallel
+
+  !$acc parallel
+  !$acc loop
+  do i = 1, N
+    a(i) = 3.14
+    if(i == N-1) THEN
+      !ERROR: EXIT statement is not allowed in a PARALLEL construct
+      exit
+    end if
+  end do
+  !$acc end parallel
+
+  !$acc parallel
+  !$acc loop
+  do i = 1, N
+    a(i) = 3.14
+    if(i == N-1) THEN
+      !ERROR: STOP statement is not allowed in a PARALLEL construct
+      stop 999
+    end if
+  end do
+  !$acc end parallel
+
+  !$acc kernels
+  do i = 1, N
+    a(i) = 3.14
+    !ERROR: RETURN statement is not allowed in a KERNELS construct
+    return
+  end do
+  !$acc end kernels
+
+  !$acc kernels
+  do i = 1, N
+    a(i) = 3.14
+    if(i == N-1) THEN
+      !ERROR: EXIT statement is not allowed in a KERNELS construct
+      exit
+    end if
+  end do
+  !$acc end kernels
+
+  !$acc kernels
+  do i = 1, N
+    a(i) = 3.14
+    if(i == N-1) THEN
+      !ERROR: STOP statement is not allowed in a KERNELS construct
+      stop 999
+    end if
+  end do
+  !$acc end kernels
+
+  !$acc serial
+  do i = 1, N
+    a(i) = 3.14
+    !ERROR: RETURN statement is not allowed in a SERIAL construct
+    return
+  end do
+  !$acc end serial
+
+  !$acc serial
+  do i = 1, N
+    a(i) = 3.14
+    if(i == N-1) THEN
+      !ERROR: EXIT statement is not allowed in a SERIAL construct
+      exit
+    end if
+  end do
+  !$acc end serial
+
+  !$acc serial
+  do i = 1, N
+    a(i) = 3.14
+    if(i == N-1) THEN
+      !ERROR: STOP statement is not allowed in a SERIAL construct
+      stop 999
+    end if
+  end do
+  !$acc end serial
+
+end program openacc_clause_validity

diff  --git a/flang/test/Semantics/acc-validity.f90 b/flang/test/Semantics/acc-clause-validity.f90
similarity index 53%
rename from flang/test/Semantics/acc-validity.f90
rename to flang/test/Semantics/acc-clause-validity.f90
index 88f62a84d161..a8aefad384b1 100644
--- a/flang/test/Semantics/acc-validity.f90
+++ b/flang/test/Semantics/acc-clause-validity.f90
@@ -16,31 +16,41 @@ program openacc_clause_validity
 
   integer :: i, j
   integer :: N = 256
-
+  !ERROR: At least one clause is required on the DECLARE directive
   !$acc declare
   real(8) :: a(256)
 
+  !ERROR: At least one of ATTACH, COPYIN, CREATE clause must appear on the ENTER DATA directive
   !$acc enter data
 
+  !ERROR: Only the READONLY modifier is allowed for the COPYIN clause on the ENTER DATA directive
   !$acc enter data copyin(zero: i)
 
+  !ERROR: Only the ZERO modifier is allowed for the CREATE clause on the ENTER DATA directive
   !$acc enter data create(readonly: i)
 
+  !ERROR: Only the ZERO modifier is allowed for the COPYOUT clause on the DATA directive
   !$acc data copyout(readonly: i)
   !$acc end data
 
+  !ERROR: COPYOUT clause is not allowed on the ENTER DATA directive
   !$acc enter data copyin(i) copyout(i)
 
+  !ERROR: At most one IF clause can appear on the DATA directive
   !$acc data copy(i) if(.true.) if(.true.)
   !$acc end data
 
+  !ERROR: At least one of COPYOUT, DELETE, DETACH clause must appear on the EXIT DATA directive
   !$acc exit data
 
+  !ERROR: At least one of USE_DEVICE clause must appear on the HOST_DATA directive
   !$acc host_data
   !$acc end host_data
 
+  !ERROR: At least one of DEFAULT_ASYNC, DEVICE_NUM, DEVICE_TYPE clause must appear on the SET directive
   !$acc set
 
+  !ERROR: At least one of ATTACH, COPY, COPYIN, COPYOUT, CREATE, DEFAULT, DEVICEPTR, NO_CREATE, PRESENT clause must appear on the DATA directive
   !$acc data
   !$acc end data
 
@@ -48,16 +58,16 @@ program openacc_clause_validity
   !$acc end data
 
   !$acc data copyin(i)
-
+  !ERROR: Unmatched PARALLEL directive
   !$acc end parallel
 
   !$acc update device(i) device_type(*) async
 
-
+  !ERROR: Clause IF is not allowed after clause DEVICE_TYPE on the UPDATE directive
   !$acc update device(i) device_type(*) if(.TRUE.)
 
   !$acc parallel
-
+  !ERROR: INDEPENDENT and SEQ clauses are mutually exclusive and may not appear on the same LOOP directive
   !$acc loop seq independent
   do i = 1, N
     a(i) = 3.14
@@ -72,7 +82,7 @@ program openacc_clause_validity
   !$acc end parallel
 
   !$acc parallel
-
+  !ERROR: The parameter of the COLLAPSE clause on the LOOP directive must be a constant positive integer expression
   !$acc loop collapse(-1)
   do i = 1, N
     do j = 1, N
@@ -82,7 +92,7 @@ program openacc_clause_validity
   !$acc end parallel
 
   !$acc parallel
-
+  !ERROR: Clause PRIVATE is not allowed after clause DEVICE_TYPE on the LOOP directive
   !$acc loop device_type(*) private(i)
   do i = 1, N
     a(i) = 3.14
@@ -90,14 +100,14 @@ program openacc_clause_validity
   !$acc end parallel
 
   !$acc parallel
-
+  !ERROR: Clause GANG is not allowed if clause SEQ appears on the LOOP directive
   !$acc loop gang seq
   do i = 1, N
     a(i) = 3.14
   end do
   !$acc end parallel
 
-
+  !ERROR: Clause IF is not allowed after clause DEVICE_TYPE on the PARALLEL directive
   !$acc parallel device_type(*) if(.TRUE.)
   !$acc loop
   do i = 1, N
@@ -105,7 +115,7 @@ program openacc_clause_validity
   end do
   !$acc end parallel
 
-
+  !ERROR: Clause IF is not allowed after clause DEVICE_TYPE on the PARALLEL LOOP directive
   !$acc parallel loop device_type(*) if(.TRUE.)
   do i = 1, N
     a(i) = 3.14
@@ -118,14 +128,14 @@ program openacc_clause_validity
   end do
   !$acc end kernels
 
-
+  !ERROR: Clause IF is not allowed after clause DEVICE_TYPE on the KERNELS directive
   !$acc kernels device_type(*) if(.TRUE.)
   do i = 1, N
     a(i) = 3.14
   end do
   !$acc end kernels
 
-
+  !ERROR: Clause IF is not allowed after clause DEVICE_TYPE on the KERNELS LOOP directive
   !$acc kernels loop device_type(*) if(.TRUE.)
   do i = 1, N
     a(i) = 3.14
@@ -138,14 +148,14 @@ program openacc_clause_validity
   end do
   !$acc end serial
 
-
+  !ERROR: Clause IF is not allowed after clause DEVICE_TYPE on the SERIAL directive
   !$acc serial device_type(*) if(.TRUE.)
   do i = 1, N
     a(i) = 3.14
   end do
   !$acc end serial
 
-
+  !ERROR: Clause IF is not allowed after clause DEVICE_TYPE on the SERIAL LOOP directive
   !$acc serial loop device_type(*) if(.TRUE.)
   do i = 1, N
     a(i) = 3.14
@@ -156,14 +166,14 @@ program openacc_clause_validity
 
    subroutine sub1(a)
      real :: a(:)
-
+     !ERROR: At least one of GANG, SEQ, VECTOR, WORKER clause must appear on the ROUTINE directive
      !$acc routine
    end subroutine sub1
 
    subroutine sub2(a)
      real :: a(:)
-
+     !ERROR: Clause NOHOST is not allowed after clause DEVICE_TYPE on the ROUTINE directive
      !$acc routine seq device_type(*) nohost
    end subroutine sub2
 
-end program openacc_clause_validity
\ No newline at end of file
+end program openacc_clause_validity

diff  --git a/llvm/include/llvm/Frontend/OpenACC/ACC.td b/llvm/include/llvm/Frontend/OpenACC/ACC.td
index 0bc0f2481db5..e96b7e846662 100644
--- a/llvm/include/llvm/Frontend/OpenACC/ACC.td
+++ b/llvm/include/llvm/Frontend/OpenACC/ACC.td
@@ -103,7 +103,7 @@ def ACCC_Device : Clause<"device"> {
 }
 
 // 2.14.1
-def ACCC_DeviceNum : Clause<"devicenum">  {
+def ACCC_DeviceNum : Clause<"device_num">  {
   let flangClass = "ScalarIntConstantExpr";
 }
 

diff  --git a/llvm/include/llvm/Frontend/OpenACC/CMakeLists.txt b/llvm/include/llvm/Frontend/OpenACC/CMakeLists.txt
index 82cc7cfaccc9..31086ec9a47b 100644
--- a/llvm/include/llvm/Frontend/OpenACC/CMakeLists.txt
+++ b/llvm/include/llvm/Frontend/OpenACC/CMakeLists.txt
@@ -1,4 +1,4 @@
 set(LLVM_TARGET_DEFINITIONS ACC.td)
 tablegen(LLVM ACC.h.inc --gen-directive-decl)
-tablegen(LLVM ACC.cpp.inc --gen-directive-impl)
+tablegen(LLVM ACC.cpp.inc --gen-directive-gen)
 add_public_tablegen_target(acc_gen)


        


More information about the flang-commits mailing list