[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