[flang-commits] [flang] b49f846 - [Flang][OpenMP][Sema] Add directive rewrite pass to support atomic_default_mem_order REQUIRES clause

Sergio Afonso via flang-commits flang-commits at lists.llvm.org
Thu Oct 19 02:43:55 PDT 2023


Author: Sergio Afonso
Date: 2023-10-19T10:43:37+01:00
New Revision: b49f846fe529131d3762f7498eb5f89f61b5dd2c

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

LOG: [Flang][OpenMP][Sema] Add directive rewrite pass to support atomic_default_mem_order REQUIRES clause

This patch creates the `OmpRewriteMutator` pass that runs at the end of
`RewriteParseTree()`. This pass is intended to make OpenMP-specific mutations
to the PFT after name resolution.

In the case of the `atomic_default_mem_order` clause of the REQUIRES directive,
name resolution results in populating global symbols with information about the
REQUIRES clauses that apply to that scope. The new rewrite pass is then able to
use this information in order to explicitly set the memory order of ATOMIC
constructs for which that is not already specified.

Given that this rewrite happens before semantics checks, the check of the order
in which ATOMIC constructs without explicit memory order and REQUIRES
directives with `atomic_default_mem_order` appear is moved earlier into the
rewrite pass. Otherwise, these problems would not be caught by semantics
checks, since the PFT would be modified by that stage.

This is patch 4/5 of a series splitting D149337 to simplify review.

Depends on D157983.

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

Added: 
    flang/lib/Semantics/rewrite-directives.cpp
    flang/lib/Semantics/rewrite-directives.h
    flang/test/Semantics/OpenMP/requires-atomic01.f90
    flang/test/Semantics/OpenMP/requires-atomic02.f90

Modified: 
    flang/lib/Semantics/CMakeLists.txt
    flang/lib/Semantics/check-omp-structure.cpp
    flang/lib/Semantics/check-omp-structure.h
    flang/lib/Semantics/rewrite-parse-tree.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt
index bfd2f2b9790441a..809206565fc1cfc 100644
--- a/flang/lib/Semantics/CMakeLists.txt
+++ b/flang/lib/Semantics/CMakeLists.txt
@@ -36,6 +36,7 @@ add_flang_library(FortranSemantics
   resolve-directives.cpp
   resolve-names-utils.cpp
   resolve-names.cpp
+  rewrite-directives.cpp
   rewrite-parse-tree.cpp
   runtime-type-info.cpp
   scope.cpp

diff  --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index b642cb7678c6797..12f54fbd51e1c2f 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -1879,9 +1879,6 @@ void OmpStructureChecker::CheckAtomicMemoryOrderClause(
   if (rightHandClauseList) {
     checkForValidMemoryOrderClause(rightHandClauseList);
   }
-  if (numMemoryOrderClause == 0) {
-    atomicDirectiveDefaultOrderFound_ = true;
-  }
 }
 
 void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
@@ -3219,16 +3216,7 @@ void OmpStructureChecker::Enter(
 void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) {
   CheckAllowed(clause);
 
-  if (clause == llvm::omp::Clause::OMPC_atomic_default_mem_order) {
-    // Check that it does not appear after an atomic operation without memory
-    // order
-    if (atomicDirectiveDefaultOrderFound_) {
-      context_.Say(GetContext().clauseSource,
-          "REQUIRES directive with '%s' clause found lexically after atomic "
-          "operation without a memory order clause"_err_en_US,
-          parser::ToUpperCaseLetters(getClauseName(clause).str()));
-    }
-  } else {
+  if (clause != llvm::omp::Clause::OMPC_atomic_default_mem_order) {
     // Check that it does not appear after a device construct
     if (deviceConstructFound_) {
       context_.Say(GetContext().clauseSource,

diff  --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index cdff890ddd417f4..78892d7bd282e65 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -210,7 +210,6 @@ class OmpStructureChecker
 
   void CheckAllowedRequiresClause(llvmOmpClause clause);
   bool deviceConstructFound_{false};
-  bool atomicDirectiveDefaultOrderFound_{false};
 
   void EnterDirectiveNest(const int index) { directiveNest_[index]++; }
   void ExitDirectiveNest(const int index) { directiveNest_[index]--; }

diff  --git a/flang/lib/Semantics/rewrite-directives.cpp b/flang/lib/Semantics/rewrite-directives.cpp
new file mode 100644
index 000000000000000..bab91d25308225b
--- /dev/null
+++ b/flang/lib/Semantics/rewrite-directives.cpp
@@ -0,0 +1,177 @@
+//===-- lib/Semantics/rewrite-directives.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 "rewrite-directives.h"
+#include "flang/Parser/parse-tree-visitor.h"
+#include "flang/Parser/parse-tree.h"
+#include "flang/Semantics/semantics.h"
+#include "flang/Semantics/symbol.h"
+#include "llvm/Frontend/OpenMP/OMP.h.inc"
+#include <list>
+
+namespace Fortran::semantics {
+
+using namespace parser::literals;
+
+class DirectiveRewriteMutator {
+public:
+  explicit DirectiveRewriteMutator(SemanticsContext &context)
+      : context_{context} {}
+
+  // Default action for a parse tree node is to visit children.
+  template <typename T> bool Pre(T &) { return true; }
+  template <typename T> void Post(T &) {}
+
+protected:
+  SemanticsContext &context_;
+};
+
+// Rewrite atomic constructs to add an explicit memory ordering to all that do
+// not specify it, honoring in this way the `atomic_default_mem_order` clause of
+// the REQUIRES directive.
+class OmpRewriteMutator : public DirectiveRewriteMutator {
+public:
+  explicit OmpRewriteMutator(SemanticsContext &context)
+      : DirectiveRewriteMutator(context) {}
+
+  template <typename T> bool Pre(T &) { return true; }
+  template <typename T> void Post(T &) {}
+
+  bool Pre(parser::OpenMPAtomicConstruct &);
+  bool Pre(parser::OpenMPRequiresConstruct &);
+
+private:
+  bool atomicDirectiveDefaultOrderFound_{false};
+};
+
+bool OmpRewriteMutator::Pre(parser::OpenMPAtomicConstruct &x) {
+  // Find top-level parent of the operation.
+  Symbol *topLevelParent{common::visit(
+      [&](auto &atomic) {
+        Symbol *symbol{nullptr};
+        Scope *scope{
+            &context_.FindScope(std::get<parser::Verbatim>(atomic.t).source)};
+        do {
+          if (Symbol * parent{scope->symbol()}) {
+            symbol = parent;
+          }
+          scope = &scope->parent();
+        } while (!scope->IsGlobal());
+
+        assert(symbol &&
+            "Atomic construct must be within a scope associated with a symbol");
+        return symbol;
+      },
+      x.u)};
+
+  // Get the `atomic_default_mem_order` clause from the top-level parent.
+  std::optional<common::OmpAtomicDefaultMemOrderType> defaultMemOrder;
+  common::visit(
+      [&](auto &details) {
+        if constexpr (std::is_convertible_v<decltype(&details),
+                          WithOmpDeclarative *>) {
+          if (details.has_ompAtomicDefaultMemOrder()) {
+            defaultMemOrder = *details.ompAtomicDefaultMemOrder();
+          }
+        }
+      },
+      topLevelParent->details());
+
+  if (!defaultMemOrder) {
+    return false;
+  }
+
+  auto findMemOrderClause =
+      [](const std::list<parser::OmpAtomicClause> &clauses) {
+        return std::find_if(
+                   clauses.begin(), clauses.end(), [](const auto &clause) {
+                     return std::get_if<parser::OmpMemoryOrderClause>(
+                         &clause.u);
+                   }) != clauses.end();
+      };
+
+  // Get the clause list to which the new memory order clause must be added,
+  // only if there are no other memory order clauses present for this atomic
+  // directive.
+  std::list<parser::OmpAtomicClause> *clauseList = common::visit(
+      common::visitors{[&](parser::OmpAtomic &atomicConstruct) {
+                         // OmpAtomic only has a single list of clauses.
+                         auto &clauses{std::get<parser::OmpAtomicClauseList>(
+                             atomicConstruct.t)};
+                         return !findMemOrderClause(clauses.v) ? &clauses.v
+                                                               : nullptr;
+                       },
+          [&](auto &atomicConstruct) {
+            // All other atomic constructs have two lists of clauses.
+            auto &clausesLhs{std::get<0>(atomicConstruct.t)};
+            auto &clausesRhs{std::get<2>(atomicConstruct.t)};
+            return !findMemOrderClause(clausesLhs.v) &&
+                    !findMemOrderClause(clausesRhs.v)
+                ? &clausesRhs.v
+                : nullptr;
+          }},
+      x.u);
+
+  // Add a memory order clause to the atomic directive.
+  if (clauseList) {
+    atomicDirectiveDefaultOrderFound_ = true;
+    switch (*defaultMemOrder) {
+    case common::OmpAtomicDefaultMemOrderType::AcqRel:
+      clauseList->emplace_back<parser::OmpMemoryOrderClause>(common::visit(
+          common::visitors{[](parser::OmpAtomicRead &) -> parser::OmpClause {
+                             return parser::OmpClause::Acquire{};
+                           },
+              [](parser::OmpAtomicCapture &) -> parser::OmpClause {
+                return parser::OmpClause::AcqRel{};
+              },
+              [](auto &) -> parser::OmpClause {
+                // parser::{OmpAtomic, OmpAtomicUpdate, OmpAtomicWrite}
+                return parser::OmpClause::Release{};
+              }},
+          x.u));
+      break;
+    case common::OmpAtomicDefaultMemOrderType::Relaxed:
+      clauseList->emplace_back<parser::OmpMemoryOrderClause>(
+          parser::OmpClause{parser::OmpClause::Relaxed{}});
+      break;
+    case common::OmpAtomicDefaultMemOrderType::SeqCst:
+      clauseList->emplace_back<parser::OmpMemoryOrderClause>(
+          parser::OmpClause{parser::OmpClause::SeqCst{}});
+      break;
+    }
+  }
+
+  return false;
+}
+
+bool OmpRewriteMutator::Pre(parser::OpenMPRequiresConstruct &x) {
+  for (parser::OmpClause &clause : std::get<parser::OmpClauseList>(x.t).v) {
+    if (std::holds_alternative<parser::OmpClause::AtomicDefaultMemOrder>(
+            clause.u) &&
+        atomicDirectiveDefaultOrderFound_) {
+      context_.Say(clause.source,
+          "REQUIRES directive with '%s' clause found lexically after atomic "
+          "operation without a memory order clause"_err_en_US,
+          parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(
+              llvm::omp::OMPC_atomic_default_mem_order)
+                                         .str()));
+    }
+  }
+  return false;
+}
+
+bool RewriteOmpParts(SemanticsContext &context, parser::Program &program) {
+  if (!context.IsEnabled(common::LanguageFeature::OpenMP)) {
+    return true;
+  }
+  OmpRewriteMutator ompMutator{context};
+  parser::Walk(program, ompMutator);
+  return !context.AnyFatalError();
+}
+
+} // namespace Fortran::semantics

diff  --git a/flang/lib/Semantics/rewrite-directives.h b/flang/lib/Semantics/rewrite-directives.h
new file mode 100644
index 000000000000000..675962192842710
--- /dev/null
+++ b/flang/lib/Semantics/rewrite-directives.h
@@ -0,0 +1,24 @@
+//===-- lib/Semantics/rewrite-directives.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_REWRITE_DIRECTIVES_H_
+#define FORTRAN_SEMANTICS_REWRITE_DIRECTIVES_H_
+
+namespace Fortran::parser {
+struct Program;
+} // namespace Fortran::parser
+
+namespace Fortran::semantics {
+class SemanticsContext;
+} // namespace Fortran::semantics
+
+namespace Fortran::semantics {
+bool RewriteOmpParts(SemanticsContext &, parser::Program &);
+} // namespace Fortran::semantics
+
+#endif // FORTRAN_SEMANTICS_REWRITE_DIRECTIVES_H_

diff  --git a/flang/lib/Semantics/rewrite-parse-tree.cpp b/flang/lib/Semantics/rewrite-parse-tree.cpp
index 05df1f408082104..b4fb72ce213017c 100644
--- a/flang/lib/Semantics/rewrite-parse-tree.cpp
+++ b/flang/lib/Semantics/rewrite-parse-tree.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "rewrite-parse-tree.h"
+#include "rewrite-directives.h"
 #include "flang/Common/indirection.h"
 #include "flang/Parser/parse-tree-visitor.h"
 #include "flang/Parser/parse-tree.h"
@@ -175,7 +176,7 @@ void RewriteMutator::Post(parser::WriteStmt &x) {
 bool RewriteParseTree(SemanticsContext &context, parser::Program &program) {
   RewriteMutator mutator{context};
   parser::Walk(program, mutator);
-  return !context.AnyFatalError();
+  return !context.AnyFatalError() && RewriteOmpParts(context, program);
 }
 
 } // namespace Fortran::semantics

diff  --git a/flang/test/Semantics/OpenMP/requires-atomic01.f90 b/flang/test/Semantics/OpenMP/requires-atomic01.f90
new file mode 100644
index 000000000000000..b39c9cdcc0bb339
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/requires-atomic01.f90
@@ -0,0 +1,109 @@
+! RUN: %flang_fc1 -fopenmp -fdebug-dump-parse-tree %s 2>&1 | FileCheck %s
+! Ensure that requires atomic_default_mem_order is used to update atomic
+! operations with no explicit memory order set.
+program requires
+  implicit none
+  !$omp requires atomic_default_mem_order(seq_cst)
+  integer :: i, j
+
+  ! ----------------------------------------------------------------------------
+  ! READ
+  ! ----------------------------------------------------------------------------
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicRead
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  !$omp atomic read
+  i = j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicRead
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed read
+  i = j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicRead
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic read relaxed
+  i = j
+  
+  ! ----------------------------------------------------------------------------
+  ! WRITE
+  ! ----------------------------------------------------------------------------
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicWrite
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  !$omp atomic write
+  i = j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicWrite
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed write
+  i = j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicWrite
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic write relaxed
+  i = j
+
+  ! ----------------------------------------------------------------------------
+  ! UPDATE
+  ! ----------------------------------------------------------------------------
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicUpdate
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  !$omp atomic update
+  i = i + j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicUpdate
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed update
+  i = i + j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicUpdate
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic update relaxed
+  i = i + j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomic
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  !$omp atomic
+  i = i + j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomic
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed
+  i = i + j
+
+  ! ----------------------------------------------------------------------------
+  ! CAPTURE
+  ! ----------------------------------------------------------------------------
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  !$omp atomic capture
+  i = j
+  i = j
+  !$omp end atomic
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed capture
+  i = j
+  i = j
+  !$omp end atomic
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> SeqCst
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic capture relaxed
+  i = j
+  i = j
+  !$omp end atomic
+end program requires

diff  --git a/flang/test/Semantics/OpenMP/requires-atomic02.f90 b/flang/test/Semantics/OpenMP/requires-atomic02.f90
new file mode 100644
index 000000000000000..3af83970e7927a9
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/requires-atomic02.f90
@@ -0,0 +1,109 @@
+! RUN: %flang_fc1 -fopenmp -fdebug-dump-parse-tree %s 2>&1 | FileCheck %s
+! Ensure that requires atomic_default_mem_order is used to update atomic
+! operations with no explicit memory order set. ACQ_REL clause tested here.
+program requires
+  implicit none
+  !$omp requires atomic_default_mem_order(acq_rel)
+  integer :: i, j
+
+  ! ----------------------------------------------------------------------------
+  ! READ
+  ! ----------------------------------------------------------------------------
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicRead
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Acquire
+  !$omp atomic read
+  i = j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicRead
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> Acquire
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed read
+  i = j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicRead
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> Acquire
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic read relaxed
+  i = j
+  
+  ! ----------------------------------------------------------------------------
+  ! WRITE
+  ! ----------------------------------------------------------------------------
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicWrite
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Release
+  !$omp atomic write
+  i = j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicWrite
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> Release
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed write
+  i = j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicWrite
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> Release
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic write relaxed
+  i = j
+
+  ! ----------------------------------------------------------------------------
+  ! UPDATE
+  ! ----------------------------------------------------------------------------
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicUpdate
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Release
+  !$omp atomic update
+  i = i + j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicUpdate
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> Release
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed update
+  i = i + j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicUpdate
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> Release
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic update relaxed
+  i = i + j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomic
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Release
+  !$omp atomic
+  i = i + j
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomic
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> Release
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed
+  i = i + j
+
+  ! ----------------------------------------------------------------------------
+  ! CAPTURE
+  ! ----------------------------------------------------------------------------
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> AcqRel
+  !$omp atomic capture
+  i = j
+  i = j
+  !$omp end atomic
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> AcqRel
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic relaxed capture
+  i = j
+  i = j
+  !$omp end atomic
+
+  ! CHECK-LABEL: OpenMPAtomicConstruct -> OmpAtomicCapture
+  ! CHECK-NOT: OmpMemoryOrderClause -> OmpClause -> AcqRel
+  ! CHECK: OmpMemoryOrderClause -> OmpClause -> Relaxed
+  !$omp atomic capture relaxed
+  i = j
+  i = j
+  !$omp end atomic
+end program requires


        


More information about the flang-commits mailing list