[flang-commits] [flang] [mlir] [flang][OpenMP] Support for compare fail (PR #214179)

via flang-commits flang-commits at lists.llvm.org
Wed Aug 5 03:09:00 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-openmp

@llvm/pr-subscribers-flang-fir-hlfir

Author: SunilKuravinakop

<details>
<summary>Changes</summary>

Support for fail clause in "!omp atomic compare fail".

```
  subroutine compare_exchange(x, expected, desired)
    implicit none
    integer, intent(inout) :: x
    integer, intent(in)    :: expected
    integer, intent(in)    :: desired

    !$omp atomic compare fail(relaxed)
    if (x == expected) then
      x = desired
    end if
  end subroutine compare_exchange
```
  
This also Fixes [#<!-- -->214176](https://github.com/llvm/llvm-project/issues/214176)

---
Full diff: https://github.com/llvm/llvm-project/pull/214179.diff


9 Files Affected:

- (modified) flang/lib/Lower/OpenMP/Atomic.cpp (+32-5) 
- (modified) flang/lib/Semantics/check-omp-atomic.cpp (+15) 
- (added) flang/test/Lower/OpenMP/atomic-compare-fail.f90 (+43) 
- (added) flang/test/Semantics/OpenMP/atomic-compare-fail.f90 (+27) 
- (modified) flang/test/Semantics/OpenMP/atomic-compare.f90 (+3-3) 
- (modified) mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td (+8-1) 
- (modified) mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp (+13-4) 
- (modified) mlir/test/Dialect/OpenMP/ops.mlir (+18) 
- (modified) mlir/test/Target/LLVMIR/openmp-llvm.mlir (+27) 


``````````diff
diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp
index 80a07e37d951a..faf170c49149e 100644
--- a/flang/lib/Lower/OpenMP/Atomic.cpp
+++ b/flang/lib/Lower/OpenMP/Atomic.cpp
@@ -159,6 +159,24 @@ getMemoryOrderKind(common::OmpMemoryOrderType kind) {
   llvm_unreachable("Unexpected kind");
 }
 
+static mlir::omp::ClauseMemoryOrderKind
+getMemoryOrderKind(omp::clause::Fail::MemoryOrder kind) {
+  using MemoryOrder = omp::clause::Fail::MemoryOrder;
+  switch (kind) {
+  case MemoryOrder::AcqRel:
+    return mlir::omp::ClauseMemoryOrderKind::Acq_rel;
+  case MemoryOrder::Acquire:
+    return mlir::omp::ClauseMemoryOrderKind::Acquire;
+  case MemoryOrder::Relaxed:
+    return mlir::omp::ClauseMemoryOrderKind::Relaxed;
+  case MemoryOrder::Release:
+    return mlir::omp::ClauseMemoryOrderKind::Release;
+  case MemoryOrder::SeqCst:
+    return mlir::omp::ClauseMemoryOrderKind::Seq_cst;
+  }
+  llvm_unreachable("Unexpected memory order");
+}
+
 static std::optional<mlir::omp::ClauseMemoryOrderKind>
 getMemoryOrderKind(llvm::omp::Clause clauseId) {
   switch (clauseId) {
@@ -564,15 +582,23 @@ void Fortran::lower::omp::lowerAtomic(
     // e : expecteVal
     // d : desiredVal
 
-    // Check for compound clauses (fail, capture) that are not yet
-    // supported with atomic compare.
+    // Atomic compare with capture is not yet supported.
     if (llvm::any_of(clauses, [](const omp::Clause &clause) {
-          return clause.id == llvm::omp::Clause::OMPC_fail ||
-                 clause.id == llvm::omp::Clause::OMPC_capture;
+          return clause.id == llvm::omp::Clause::OMPC_capture;
         })) {
       TODO(loc, "Compound clauses of OpenMP ATOMIC COMPARE");
     }
 
+    // The `fail` clause sets the memory ordering for a failed compare;
+    // extract its argument to attach to the omp.atomic.compare op below.
+    std::optional<mlir::omp::ClauseMemoryOrderKind> failMemOrder;
+    for (const omp::Clause &clause : clauses) {
+      if (const auto *fail = std::get_if<omp::clause::Fail>(&clause.u)) {
+        failMemOrder = getMemoryOrderKind(fail->v);
+        break;
+      }
+    }
+
     common::RelationalOperator relOpr = common::RelationalOperator::EQ;
     std::optional<semantics::SomeExpr> expectedExprStorage;
     bool isUnsigned = false;
@@ -624,7 +650,8 @@ void Fortran::lower::omp::lowerAtomic(
     }
     mlir::Operation *atomicOp = mlir::omp::AtomicCompareOp::create(
         builder, loc, atomAddr, weakAttr, hint,
-        makeMemOrderAttr(converter, memOrder));
+        makeMemOrderAttr(converter, memOrder),
+        makeMemOrderAttr(converter, failMemOrder));
     mlir::Block *block = builder.createBlock(&atomicOp->getRegion(0));
     mlir::Value blockArg = block->addArgument(elemTypeOfX, loc);
     builder.setInsertionPointToEnd(block);
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index ec307be469982..810d91a54d710 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -1648,6 +1648,21 @@ void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
 
   checkIncompatibleMemoryOrderClause(context_, x, atomic, memoryOrder);
 
+  // OpenMP 5.2 [15.8.3] extended-atomic Clauses: acq_rel and release cannot
+  // be specified as arguments to the fail clause, so its memory order argument
+  // must be SEQ_CST, ACQUIRE, or RELAXED.
+  for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
+    if (const auto *fail{std::get_if<parser::OmpClause::Fail>(&clause.u)}) {
+      common::OmpMemoryOrderType ord{fail->v.v};
+      if (ord != common::OmpMemoryOrderType::Seq_Cst &&
+          ord != common::OmpMemoryOrderType::Acquire &&
+          ord != common::OmpMemoryOrderType::Relaxed) {
+        context_.Say(clause.source,
+            "The argument of the FAIL clause must be SEQ_CST, ACQUIRE, or RELAXED"_err_en_US);
+      }
+    }
+  }
+
   switch (kind) {
   case llvm::omp::Clause::OMPC_read:
     CheckAtomicRead(x);
diff --git a/flang/test/Lower/OpenMP/atomic-compare-fail.f90 b/flang/test/Lower/OpenMP/atomic-compare-fail.f90
new file mode 100644
index 0000000000000..5f63ffa4890fd
--- /dev/null
+++ b/flang/test/Lower/OpenMP/atomic-compare-fail.f90
@@ -0,0 +1,43 @@
+! This test checks lowering of the OpenMP `fail` clause on a (non-capturing)
+! atomic compare construct. The fail memory order is attached as the
+! `fail_memory_order` attribute on omp.atomic.compare.
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s
+! RUN: bbc -fopenmp -fopenmp-version=52 -emit-hlfir %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPfail_acquire
+subroutine fail_acquire(x, e, d)
+  integer :: x, e, d
+  !$omp atomic compare fail(acquire)
+  if (x == e) x = d
+end
+! CHECK: omp.atomic.compare memory_order(relaxed) %{{.*}} : !fir.ref<i32> {
+! CHECK: ^bb0(%[[XVAL:.*]]: i32):
+! CHECK:   arith.cmpi eq, %[[XVAL]], %{{.*}} : i32
+! CHECK:   omp.yield
+! CHECK: } {fail_memory_order = #omp<memoryorderkind acquire>}
+
+! CHECK-LABEL: func.func @_QPfail_relaxed
+subroutine fail_relaxed(x, e, d)
+  integer :: x, e, d
+  !$omp atomic compare fail(relaxed)
+  if (x == e) x = d
+end
+! CHECK: } {fail_memory_order = #omp<memoryorderkind relaxed>}
+
+! CHECK-LABEL: func.func @_QPfail_seqcst
+subroutine fail_seqcst(x, e, d)
+  integer :: x, e, d
+  !$omp atomic compare fail(seq_cst)
+  if (x == e) x = d
+end
+! CHECK: } {fail_memory_order = #omp<memoryorderkind seq_cst>}
+
+! CHECK-LABEL: func.func @_QPseqcst_fail_relaxed
+subroutine seqcst_fail_relaxed(x, e, d)
+  integer :: x, e, d
+  !$omp atomic seq_cst compare fail(relaxed)
+  if (x == e) x = d
+end
+! CHECK: omp.atomic.compare memory_order(seq_cst) %{{.*}} : !fir.ref<i32> {
+! CHECK: } {fail_memory_order = #omp<memoryorderkind relaxed>}
diff --git a/flang/test/Semantics/OpenMP/atomic-compare-fail.f90 b/flang/test/Semantics/OpenMP/atomic-compare-fail.f90
new file mode 100644
index 0000000000000..10522dd3d950e
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/atomic-compare-fail.f90
@@ -0,0 +1,27 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=52
+
+! OpenMP 5.2 [15.8.3] extended-atomic Clauses: acq_rel and release cannot be
+! specified as arguments to the fail clause, so the argument must be SEQ_CST,
+! ACQUIRE, or RELAXED.
+
+subroutine valid(x, e, d)
+  integer :: x, e, d
+  !$omp atomic compare fail(seq_cst)
+  if (x == e) x = d
+  !$omp atomic compare fail(acquire)
+  if (x == e) x = d
+  !$omp atomic compare fail(relaxed)
+  if (x == e) x = d
+  !$omp atomic seq_cst compare fail(relaxed)
+  if (x == e) x = d
+end
+
+subroutine invalid(x, e, d)
+  integer :: x, e, d
+  !ERROR: The argument of the FAIL clause must be SEQ_CST, ACQUIRE, or RELAXED
+  !$omp atomic compare fail(release)
+  if (x == e) x = d
+  !ERROR: The argument of the FAIL clause must be SEQ_CST, ACQUIRE, or RELAXED
+  !$omp atomic compare fail(acq_rel)
+  if (x == e) x = d
+end
diff --git a/flang/test/Semantics/OpenMP/atomic-compare.f90 b/flang/test/Semantics/OpenMP/atomic-compare.f90
index 10da3e13d0992..64b2c2b3a9225 100644
--- a/flang/test/Semantics/OpenMP/atomic-compare.f90
+++ b/flang/test/Semantics/OpenMP/atomic-compare.f90
@@ -36,11 +36,11 @@
   if (b .eq. a) b = c
   !$omp end atomic
 
-  !$omp atomic hint(1) acq_rel compare fail(release)
+  !$omp atomic hint(1) acq_rel compare fail(acquire)
   if (c .eq. a) a = b
   !$omp end atomic
 
-  !$omp atomic compare fail(release)
+  !$omp atomic compare fail(acquire)
   if (c .eq. a) a = b
   !$omp end atomic
 
@@ -90,7 +90,7 @@
   if (b .eq. c) b = a
 
   !ERROR: At most one FAIL clause can appear on ATOMIC directive
-  !$omp atomic fail(release) compare fail(release)
+  !$omp atomic fail(acquire) compare fail(acquire)
   if (c .eq. a) a = b
   !$omp end atomic
 
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 70597b85902c5..23f4711c9c355 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2047,13 +2047,20 @@ def AtomicCompareOp : OpenMP_Op<"atomic.compare", traits = [
         omp.yield
       }
     ```
+
+    The optional `fail_memory_order` attribute specifies the memory ordering
+    to use for the comparison when the atomic conditional update fails (i.e.
+    the value of `x` is not updated). It corresponds to the OpenMP `fail`
+    clause and overrides the failure ordering that would otherwise be derived
+    from the construct's memory ordering.
   }] # clausesDescription;
 
   let arguments = !con(
       (ins Arg<OpenMP_PointerLikeType,
                "Address of variable to be compared/updated", [MemRead, MemWrite]>:$x,
            UnitAttr:$weak),
-      clausesArgs);
+      clausesArgs,
+      (ins OptionalAttr<MemoryOrderKindAttr>:$fail_memory_order));
 
   // Override region definition.
   let regions = (region SizedRegion<1>:$region);
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index e09bb720ced2d..94183e8d3c691 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -6211,10 +6211,19 @@ convertOmpAtomicCompare(omp::AtomicCompareOp atomicCompareOp,
   bool isWeak = atomicCompareOp.getWeak();
 
   bool savedHandleFPNegZero = ompBuilder->setHandleFPNegZero(true);
-  llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =
-      ompBuilder->createAtomicCompare(ompLoc, llvmAtomicX, vOpVal, rOpVal, eVal,
-                                      dVal, atomicOrdering, compareOp,
-                                      isXBinopExpr, false, false, isWeak);
+  llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP = [&]() {
+    if (auto failOrder = atomicCompareOp.getFailMemoryOrder()) {
+      llvm::AtomicOrdering failureOrdering = convertAtomicOrdering(*failOrder);
+      return ompBuilder->createAtomicCompare(
+          ompLoc, llvmAtomicX, vOpVal, rOpVal, eVal, dVal, atomicOrdering,
+          compareOp, isXBinopExpr, /*IsPostfixUpdate=*/false,
+          /*IsFailOnly=*/false, failureOrdering, isWeak);
+    }
+    return ompBuilder->createAtomicCompare(
+        ompLoc, llvmAtomicX, vOpVal, rOpVal, eVal, dVal, atomicOrdering,
+        compareOp, isXBinopExpr, /*IsPostfixUpdate=*/false,
+        /*IsFailOnly=*/false, isWeak);
+  }();
   ompBuilder->setHandleFPNegZero(savedHandleFPNegZero);
 
   if (failed(handleError(afterIP, *atomicCompareOp)))
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 00b975c371d1b..a035205a4d2b4 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -2134,6 +2134,24 @@ func.func @omp_atomic_compare(%x: memref<i32>, %e: i32, %d: i32) {
   return
 }
 
+// CHECK-LABEL: omp_atomic_compare_fail
+// CHECK-SAME: (%[[X:.*]]: memref<i32>, %[[E:.*]]: i32, %[[D:.*]]: i32)
+func.func @omp_atomic_compare_fail(%x: memref<i32>, %e: i32, %d: i32) {
+  // CHECK: omp.atomic.compare memory_order(seq_cst) %[[X]] : memref<i32> {
+  // CHECK-NEXT: ^bb0(%[[XVAL:.*]]: i32):
+  // CHECK-NEXT:   %[[CMP:.*]] = arith.cmpi eq, %[[XVAL]], %[[E]] : i32
+  // CHECK-NEXT:   %[[SEL:.*]] = arith.select %[[CMP]], %[[D]], %[[XVAL]] : i32
+  // CHECK-NEXT:   omp.yield(%[[SEL]] : i32)
+  // CHECK-NEXT: } {fail_memory_order = #omp<memoryorderkind acquire>}
+  omp.atomic.compare memory_order(seq_cst) %x : memref<i32> {
+  ^bb0(%xval: i32):
+    %cmp = arith.cmpi eq, %xval, %e : i32
+    %sel = arith.select %cmp, %d, %xval : i32
+    omp.yield(%sel : i32)
+  } {fail_memory_order = #omp<memoryorderkind acquire>}
+  return
+}
+
 // CHECK-LABEL: omp_sectionsop
 func.func @omp_sectionsop(%data_var1 : memref<i32>, %data_var2 : memref<i32>,
                      %data_var3 : memref<i32>, %redn_var : !llvm.ptr) {
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index e867dd8afcb9b..fe3412c0b3f18 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -2811,6 +2811,33 @@ llvm.func @omp_atomic_compare_weak(%x : !llvm.ptr, %e : i32, %d : i32) {
 
 // -----
 
+// CHECK-LABEL: @omp_atomic_compare_fail
+// CHECK-SAME: (ptr %[[X:.*]], i32 %[[E:.*]], i32 %[[D:.*]])
+llvm.func @omp_atomic_compare_fail(%x : !llvm.ptr, %e : i32, %d : i32) {
+  // The fail clause sets the cmpxchg failure ordering independently of the
+  // success ordering. Relaxed success + acquire failure.
+  // CHECK: cmpxchg ptr %[[X]], i32 %[[E]], i32 %[[D]] monotonic acquire
+  omp.atomic.compare %x : !llvm.ptr {
+  ^bb0(%xval : i32):
+    %cmp = llvm.icmp "eq" %xval, %e : i32
+    %sel = llvm.select %cmp, %d, %xval : i1, i32
+    omp.yield(%sel : i32)
+  } {fail_memory_order = #omp<memoryorderkind acquire>}
+
+  // Seq_cst success + relaxed failure.
+  // CHECK: cmpxchg ptr %[[X]], i32 %[[E]], i32 %[[D]] seq_cst monotonic
+  omp.atomic.compare memory_order(seq_cst) %x : !llvm.ptr {
+  ^bb0(%xval : i32):
+    %cmp = llvm.icmp "eq" %xval, %e : i32
+    %sel = llvm.select %cmp, %d, %xval : i1, i32
+    omp.yield(%sel : i32)
+  } {fail_memory_order = #omp<memoryorderkind relaxed>}
+
+  llvm.return
+}
+
+// -----
+
 // CHECK-LABEL: @omp_atomic_compare_float_neg_zero
 // CHECK-SAME: (ptr %[[XF:.*]], float %[[EF:.*]], float %[[DF:.*]])
 // Verify NaN guard + ±0.0 handling.

``````````

</details>


https://github.com/llvm/llvm-project/pull/214179


More information about the flang-commits mailing list