[flang-commits] [flang] [llvm] [mlir] [flang][OpenMP] Support for "atomic compare capture" (PR #202315)
via flang-commits
flang-commits at lists.llvm.org
Tue Jun 23 16:07:42 PDT 2026
https://github.com/SunilKuravinakop updated https://github.com/llvm/llvm-project/pull/202315
>From 216cbd2e710ce3c725bb51c5606bea4ba84a33a3 Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Sat, 6 Jun 2026 14:38:54 -0500
Subject: [PATCH 1/5] Support for "atomic compare capture".
---
flang/lib/Lower/OpenMP/Atomic.cpp | 88 ++++---
.../Interfaces/AtomicInterfaces.td | 9 +-
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 10 +
mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp | 6 +
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 216 ++++++++++++++----
5 files changed, 250 insertions(+), 79 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp
index 9d711b92bb520..5e02d3fb10dbb 100644
--- a/flang/lib/Lower/OpenMP/Atomic.cpp
+++ b/flang/lib/Lower/OpenMP/Atomic.cpp
@@ -558,16 +558,45 @@ void Fortran::lower::omp::lowerAtomic(
int action1 = analysis.op1.what & analysis.Action;
memOrder = makeValidForAction(memOrder, action0, action1, version);
+ // --- Shared capture scaffolding ---
+ mlir::Operation *captureOp = nullptr;
+ fir::FirOpBuilder::InsertPoint preAt = builder.saveInsertionPoint();
+ fir::FirOpBuilder::InsertPoint atomicAt, postAt;
+
+ if (construct.IsCapture()) {
+ assert(action0 != analysis.None && action1 != analysis.None &&
+ "Expexcing two actions");
+ (void)action0;
+ (void)action1;
+ captureOp = mlir::omp::AtomicCaptureOp::create(
+ builder, loc, hint, makeMemOrderAttr(converter, memOrder));
+ // Set the non-atomic insertion point to before the atomic.capture.
+ preAt = getInsertionPointBefore(captureOp);
+
+ mlir::Block *block = builder.createBlock(&captureOp->getRegion(0));
+ builder.setInsertionPointToEnd(block);
+ // Set the atomic insertion point to before the terminator inside
+ // atomic.capture.
+ mlir::Operation *term = mlir::omp::TerminatorOp::create(builder, loc);
+ atomicAt = getInsertionPointBefore(term);
+ postAt = getInsertionPointAfter(captureOp);
+ hint = nullptr;
+ memOrder = std::nullopt;
+ }
+
if (auto *cond = get(analysis.cond)) {
// atomic compare: if (x == e) x = d
// e : expecteVal
// d : desiredVal
- // Check for compound clauses (fail, capture, weak) that are not yet
+ // Restore insertion point so pre-processing code (e.g. computing
+ // expectedVal) is emitted before the capture op, not after the terminator.
+ builder.restoreInsertionPoint(preAt);
+
+ // Check for compound clauses (fail, weak) that are not yet
// supported with atomic compare.
if (llvm::any_of(clauses, [](const omp::Clause &clause) {
return clause.id == llvm::omp::Clause::OMPC_fail ||
- clause.id == llvm::omp::Clause::OMPC_capture ||
clause.id == llvm::omp::Clause::OMPC_weak;
})) {
TODO(loc, "Compound clauses of OpenMP ATOMIC COMPARE");
@@ -616,6 +645,17 @@ void Fortran::lower::omp::lowerAtomic(
expectedVal = builder.createConvert(loc, elemTypeOfX, expectedVal);
}
+ // If this is a compare+capture, generate the read op first.
+ if (construct.IsCapture()) {
+ assert(get(analysis.op0.assign) && (analysis.op0.what & analysis.Read) &&
+ "Expected a read operation for compare capture");
+ mlir::Operation *readOp = genAtomicRead(
+ converter, semaCtx, loc, stmtCtx, atomAddr, atom,
+ *get(analysis.op0.assign), hint, memOrder, preAt, atomicAt, postAt);
+ assert(readOp && "Should have created an atomic read operation");
+ builder.setInsertionPointAfter(readOp);
+ }
+
mlir::UnitAttr weakAttr = nullptr;
mlir::Operation *atomicOp = mlir::omp::AtomicCompareOp::create(
builder, loc, atomAddr, weakAttr, hint,
@@ -679,34 +719,9 @@ void Fortran::lower::omp::lowerAtomic(
// Generate omp.yield
mlir::omp::YieldOp::create(builder, loc, newVal);
builder.setInsertionPointAfter(atomicOp);
-
// END omp atomic compare
} else {
- mlir::Operation *captureOp = nullptr;
- fir::FirOpBuilder::InsertPoint preAt = builder.saveInsertionPoint();
- fir::FirOpBuilder::InsertPoint atomicAt, postAt;
-
- if (construct.IsCapture()) {
- // Capturing operation.
- assert(action0 != analysis.None && action1 != analysis.None &&
- "Expexcing two actions");
- (void)action0;
- (void)action1;
- captureOp = mlir::omp::AtomicCaptureOp::create(
- builder, loc, hint, makeMemOrderAttr(converter, memOrder));
- // Set the non-atomic insertion point to before the atomic.capture.
- preAt = getInsertionPointBefore(captureOp);
-
- mlir::Block *block = builder.createBlock(&captureOp->getRegion(0));
- builder.setInsertionPointToEnd(block);
- // Set the atomic insertion point to before the terminator inside
- // atomic.capture.
- mlir::Operation *term = mlir::omp::TerminatorOp::create(builder, loc);
- atomicAt = getInsertionPointBefore(term);
- postAt = getInsertionPointAfter(captureOp);
- hint = nullptr;
- memOrder = std::nullopt;
- } else {
+ if (!construct.IsCapture()) {
// Non-capturing operation.
assert(action0 != analysis.None && action1 == analysis.None &&
"Expexcing single action");
@@ -729,16 +744,13 @@ void Fortran::lower::omp::lowerAtomic(
*get(analysis.op1.assign), hint, memOrder, preAt, atomicAt, postAt);
}
- if (construct.IsCapture()) {
- // If this is a capture operation, the first/second ops will be inside
- // of it. Set the insertion point to past the capture op itself.
- builder.restoreInsertionPoint(postAt);
- } else {
- if (secondOp) {
- builder.setInsertionPointAfter(secondOp);
- } else {
- builder.setInsertionPointAfter(firstOp);
- }
+ if (!construct.IsCapture()) {
+ builder.setInsertionPointAfter(secondOp ? secondOp : firstOp);
}
}
+
+ // Shared capture cleanup.
+ if (construct.IsCapture()) {
+ builder.restoreInsertionPoint(postAt);
+ }
}
diff --git a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
index abb21705b3c1c..8c9015f05bb72 100644
--- a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
@@ -289,10 +289,12 @@ def AtomicCaptureOpInterface : OpInterface<"AtomicCaptureOpInterface"> {
auto secondReadStmt = dyn_cast<AtomicReadOpInterface>(secondOp);
auto secondUpdateStmt = dyn_cast<AtomicUpdateOpInterface>(secondOp);
auto secondWriteStmt = dyn_cast<AtomicWriteOpInterface>(secondOp);
+ auto secondCompareStmt = dyn_cast<AtomicCompareOpInterface>(secondOp);
if (!((firstUpdateStmt && secondReadStmt) ||
(firstReadStmt && secondUpdateStmt) ||
- (firstReadStmt && secondWriteStmt)))
+ (firstReadStmt && secondWriteStmt) ||
+ (firstReadStmt && secondCompareStmt)))
return ops.front().emitError()
<< "invalid sequence of operations in the capture region";
if (firstUpdateStmt && secondReadStmt &&
@@ -310,6 +312,11 @@ def AtomicCaptureOpInterface : OpInterface<"AtomicCaptureOpInterface"> {
return firstReadStmt.emitError()
<< "captured variable in atomic.read must be updated in "
"second operation";
+ if (firstReadStmt && secondCompareStmt &&
+ firstReadStmt.getX() != secondCompareStmt.getX())
+ return firstReadStmt.emitError()
+ << "captured variable in atomic.read must be updated in "
+ "second operation";
return mlir::success();
}]
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 0962b330e2f23..1241abc10298f 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -1928,6 +1928,12 @@ def AtomicCaptureOp : OpenMP_Op<"atomic.capture", traits = [
omp.atomic.write ...
omp.terminator
}
+
+ omp.atomic.capture {
+ omp.atomic.read ...
+ omp.atomic.compare ...
+ omp.terminator
+ }
```
}] # clausesDescription;
@@ -1946,6 +1952,10 @@ def AtomicCaptureOp : OpenMP_Op<"atomic.capture", traits = [
/// Returns the `atomic.update` operation inside the region, if any.
/// Otherwise, it returns nullptr.
AtomicUpdateOp getAtomicUpdateOp();
+
+ /// Returns the `atomic.compare` operation inside the region, if any.
+ /// Otherwise, it returns nullptr.
+ AtomicCompareOp getAtomicCompareOp();
}] # clausesExtraClassDeclaration;
let hasRegionVerifier = 1;
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index db5fd8f2e3230..0eafd0a267b97 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -4615,6 +4615,12 @@ AtomicUpdateOp AtomicCaptureOp::getAtomicUpdateOp() {
return dyn_cast<AtomicUpdateOp>(getSecondOp());
}
+AtomicCompareOp AtomicCaptureOp::getAtomicCompareOp() {
+ if (auto op = dyn_cast<AtomicCompareOp>(getFirstOp()))
+ return op;
+ return dyn_cast<AtomicCompareOp>(getSecondOp());
+}
+
LogicalResult AtomicCaptureOp::verify() {
return verifySynchronizationHint(*this, getHint());
}
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 07fa92bdabe50..4928b288adb65 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -4795,6 +4795,43 @@ convertOmpAtomicUpdate(omp::AtomicUpdateOp &opInst,
return success();
}
+/// Helper to extract the OMPAtomicCompareOp from an integer comparison
+/// predicate. Returns std::nullopt for unsupported predicates.
+static std::optional<llvm::omp::OMPAtomicCompareOp>
+convertICmpPredicateToAtomicCompareOp(LLVM::ICmpPredicate predicate) {
+ switch (predicate) {
+ case LLVM::ICmpPredicate::eq:
+ return llvm::omp::OMPAtomicCompareOp::EQ;
+ case LLVM::ICmpPredicate::slt:
+ case LLVM::ICmpPredicate::ult:
+ return llvm::omp::OMPAtomicCompareOp::MIN;
+ case LLVM::ICmpPredicate::sgt:
+ case LLVM::ICmpPredicate::ugt:
+ return llvm::omp::OMPAtomicCompareOp::MAX;
+ default:
+ return std::nullopt;
+ }
+}
+
+/// Helper to extract the OMPAtomicCompareOp from a floating-point comparison
+/// predicate. Returns std::nullopt for unsupported predicates.
+static std::optional<llvm::omp::OMPAtomicCompareOp>
+convertFCmpPredicateToAtomicCompareOp(LLVM::FCmpPredicate predicate) {
+ switch (predicate) {
+ case LLVM::FCmpPredicate::oeq:
+ case LLVM::FCmpPredicate::ueq:
+ return llvm::omp::OMPAtomicCompareOp::EQ;
+ case LLVM::FCmpPredicate::olt:
+ case LLVM::FCmpPredicate::ult:
+ return llvm::omp::OMPAtomicCompareOp::MIN;
+ case LLVM::FCmpPredicate::ogt:
+ case LLVM::FCmpPredicate::ugt:
+ return llvm::omp::OMPAtomicCompareOp::MAX;
+ default:
+ return std::nullopt;
+ }
+}
+
static LogicalResult
convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
llvm::IRBuilderBase &builder,
@@ -4803,13 +4840,149 @@ convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
if (failed(checkImplementationStatus(*atomicCaptureOp)))
return failure();
+ omp::AtomicUpdateOp atomicUpdateOp = atomicCaptureOp.getAtomicUpdateOp();
+ omp::AtomicWriteOp atomicWriteOp = atomicCaptureOp.getAtomicWriteOp();
+ omp::AtomicCompareOp atomicCompareOp = atomicCaptureOp.getAtomicCompareOp();
+
+ // If the capture contains an atomic.compare, delegate to
+ // createAtomicCompare with the capture variable (V) set.
+ if (atomicCompareOp) {
+ omp::AtomicReadOp atomicReadOp = atomicCaptureOp.getAtomicReadOp();
+ assert(atomicReadOp && "expected atomic.read in capture+compare");
+
+ Region ®ion = atomicCompareOp.getRegion();
+ Block &block = region.front();
+
+ llvm::Type *llvmXElementType =
+ moduleTranslation.convertType(block.getArgument(0).getType());
+ llvm::Value *llvmX = moduleTranslation.lookupValue(atomicCompareOp.getX());
+ llvm::Value *llvmV = moduleTranslation.lookupValue(atomicReadOp.getV());
+
+ bool isSigned = false;
+ llvm::OpenMPIRBuilder::AtomicOpValue llvmAtomicX = {
+ llvmX, llvmXElementType, isSigned, /*IsVolatile=*/false};
+ llvm::OpenMPIRBuilder::AtomicOpValue llvmAtomicV = {
+ llvmV, llvmXElementType, /*isSigned=*/false, /*IsVolatile=*/false};
+ llvm::OpenMPIRBuilder::AtomicOpValue llvmAtomicR = {nullptr, nullptr, false,
+ false};
+
+ llvm::AtomicOrdering atomicOrdering =
+ convertAtomicOrdering(atomicCaptureOp.getMemoryOrder());
+
+ // Pre-translate non-pattern operations inside the compare region.
+ auto isAtomicComparePatternOp = [](Operation &op) {
+ return llvm::isa<LLVM::ICmpOp, LLVM::FCmpOp, LLVM::SelectOp, LLVM::AndOp,
+ LLVM::OrOp>(op);
+ };
+ for (Operation &op : block.without_terminator()) {
+ if (isAtomicComparePatternOp(op))
+ continue;
+ bool allOperandsMapped =
+ llvm::all_of(op.getOperands(), [&](mlir::Value v) {
+ return moduleTranslation.lookupValue(v) != nullptr;
+ });
+ if (!allOperandsMapped)
+ continue;
+ if (failed(moduleTranslation.convertOperation(op, builder)))
+ return atomicCompareOp.emitError(
+ "failed to translate operation inside atomic compare region");
+ }
+
+ auto materializeValue = [&](mlir::Value val) -> llvm::Value * {
+ if (llvm::Value *existing = moduleTranslation.lookupValue(val))
+ return existing;
+ if (auto loadOp = val.getDefiningOp<LLVM::LoadOp>()) {
+ if (loadOp->getParentRegion() == ®ion) {
+ llvm::Value *loadAddr =
+ moduleTranslation.lookupValue(loadOp.getAddr());
+ if (!loadAddr)
+ return nullptr;
+ llvm::Type *loadType =
+ moduleTranslation.convertType(loadOp.getResult().getType());
+ return builder.CreateLoad(loadType, loadAddr);
+ }
+ }
+ return nullptr;
+ };
+
+ // Extract comparison predicate, eVal, and dVal from the region.
+ llvm::omp::OMPAtomicCompareOp compareOp = llvm::omp::OMPAtomicCompareOp::EQ;
+ llvm::Value *eVal = nullptr;
+ llvm::Value *dVal = nullptr;
+ bool isXBinopExpr = false;
+
+ for (Operation &op : block.getOperations()) {
+ if (auto icmpOp = dyn_cast<LLVM::ICmpOp>(op)) {
+ auto maybeOp =
+ convertICmpPredicateToAtomicCompareOp(icmpOp.getPredicate());
+ if (!maybeOp)
+ return atomicCompareOp.emitError(
+ "unsupported comparison predicate in atomic compare");
+ compareOp = *maybeOp;
+
+ LLVM::ICmpPredicate pred = icmpOp.getPredicate();
+ isSigned = (pred == LLVM::ICmpPredicate::slt ||
+ pred == LLVM::ICmpPredicate::sgt ||
+ pred == LLVM::ICmpPredicate::sle ||
+ pred == LLVM::ICmpPredicate::sge);
+
+ isXBinopExpr = (icmpOp.getOperand(0) == block.getArgument(0));
+ mlir::Value eOperand =
+ isXBinopExpr ? icmpOp.getOperand(1) : icmpOp.getOperand(0);
+ eVal = materializeValue(eOperand);
+ } else if (auto fcmpOp = dyn_cast<LLVM::FCmpOp>(op)) {
+ auto maybeOp =
+ convertFCmpPredicateToAtomicCompareOp(fcmpOp.getPredicate());
+ if (!maybeOp)
+ return atomicCompareOp.emitError(
+ "unsupported comparison predicate in atomic compare");
+ compareOp = *maybeOp;
+
+ isXBinopExpr = (fcmpOp.getOperand(0) == block.getArgument(0));
+ mlir::Value eOperand =
+ isXBinopExpr ? fcmpOp.getOperand(1) : fcmpOp.getOperand(0);
+ eVal = materializeValue(eOperand);
+ } else if (auto selectOp = dyn_cast<LLVM::SelectOp>(op)) {
+ if (!dVal)
+ dVal = materializeValue(selectOp.getTrueValue());
+ }
+ }
+
+ if (!eVal)
+ return atomicCompareOp.emitError(
+ "failed to extract expected value (e) from atomic compare region");
+ if (!dVal) {
+ auto yieldOp = cast<omp::YieldOp>(block.getTerminator());
+ if (yieldOp.getResults().empty())
+ return atomicCompareOp.emitError(
+ "failed to extract desired value (d) from atomic compare region");
+ dVal = materializeValue(yieldOp.getResults()[0]);
+ }
+
+ llvmAtomicX.IsSigned = isSigned;
+
+ llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
+ bool isPostfixUpdate = true;
+
+ bool savedHandleFPNegZero = ompBuilder->setHandleFPNegZero(true);
+ llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =
+ ompBuilder->createAtomicCompare(ompLoc, llvmAtomicX, llvmAtomicV,
+ llvmAtomicR, eVal, dVal, atomicOrdering,
+ compareOp, isXBinopExpr,
+ isPostfixUpdate, /*IsFailOnly=*/false);
+ ompBuilder->setHandleFPNegZero(savedHandleFPNegZero);
+
+ if (failed(handleError(afterIP, *atomicCaptureOp)))
+ return failure();
+
+ builder.restoreIP(*afterIP);
+ return success();
+ }
+
mlir::Value mlirExpr;
bool isXBinopExpr = false, isPostfixUpdate = false;
llvm::AtomicRMWInst::BinOp binop = llvm::AtomicRMWInst::BinOp::BAD_BINOP;
- omp::AtomicUpdateOp atomicUpdateOp = atomicCaptureOp.getAtomicUpdateOp();
- omp::AtomicWriteOp atomicWriteOp = atomicCaptureOp.getAtomicWriteOp();
-
assert((atomicUpdateOp || atomicWriteOp) &&
"internal op must be an atomic.update or atomic.write op");
@@ -4896,43 +5069,6 @@ convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
return success();
}
-/// Helper to extract the OMPAtomicCompareOp from an integer comparison
-/// predicate. Returns std::nullopt for unsupported predicates.
-static std::optional<llvm::omp::OMPAtomicCompareOp>
-convertICmpPredicateToAtomicCompareOp(LLVM::ICmpPredicate predicate) {
- switch (predicate) {
- case LLVM::ICmpPredicate::eq:
- return llvm::omp::OMPAtomicCompareOp::EQ;
- case LLVM::ICmpPredicate::slt:
- case LLVM::ICmpPredicate::ult:
- return llvm::omp::OMPAtomicCompareOp::MIN;
- case LLVM::ICmpPredicate::sgt:
- case LLVM::ICmpPredicate::ugt:
- return llvm::omp::OMPAtomicCompareOp::MAX;
- default:
- return std::nullopt;
- }
-}
-
-/// Helper to extract the OMPAtomicCompareOp from a floating-point comparison
-/// predicate. Returns std::nullopt for unsupported predicates.
-static std::optional<llvm::omp::OMPAtomicCompareOp>
-convertFCmpPredicateToAtomicCompareOp(LLVM::FCmpPredicate predicate) {
- switch (predicate) {
- case LLVM::FCmpPredicate::oeq:
- case LLVM::FCmpPredicate::ueq:
- return llvm::omp::OMPAtomicCompareOp::EQ;
- case LLVM::FCmpPredicate::olt:
- case LLVM::FCmpPredicate::ult:
- return llvm::omp::OMPAtomicCompareOp::MIN;
- case LLVM::FCmpPredicate::ogt:
- case LLVM::FCmpPredicate::ugt:
- return llvm::omp::OMPAtomicCompareOp::MAX;
- default:
- return std::nullopt;
- }
-}
-
/// Converts an omp.atomic.compare operation to LLVM IR.
///
/// if (x == e) x = d
>From 7d3293944598e8090a64cdec2ac3c791a64b73f1 Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Mon, 8 Jun 2026 05:12:22 -0500
Subject: [PATCH 2/5] Adding tests for "atomic compare capture".
---
.../Integration/OpenMP/atomic-compare.f90 | 32 +++++++++++
flang/test/Lower/OpenMP/atomic-compare.f90 | 56 +++++++++++++++++++
flang/test/Parser/OpenMP/atomic-unparse.f90 | 29 ++++++++++
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 9 +--
mlir/test/Dialect/OpenMP/ops.mlir | 48 ++++++++++++++++
mlir/test/Target/LLVMIR/openmp-llvm.mlir | 41 ++++++++++++++
6 files changed, 211 insertions(+), 4 deletions(-)
diff --git a/flang/test/Integration/OpenMP/atomic-compare.f90 b/flang/test/Integration/OpenMP/atomic-compare.f90
index 249fb0dd8fa64..650f64b80af12 100644
--- a/flang/test/Integration/OpenMP/atomic-compare.f90
+++ b/flang/test/Integration/OpenMP/atomic-compare.f90
@@ -260,3 +260,35 @@ subroutine atomic_compare_weak(x, e, d)
if (x == e) x = d
end
+! Integer equality compare+capture: cmpxchg + store old value
+!CHECK-LABEL: define void @atomic_compare_capture_int_eq_(
+!CHECK-SAME: ptr noalias %[[X:.*]], ptr noalias %[[E:.*]], ptr noalias %[[D:.*]], ptr noalias %[[V:.*]])
+!CHECK: %[[EVAL:.*]] = load i32, ptr %[[E]]
+!CHECK: %[[DVAL:.*]] = load i32, ptr %[[D]]
+!CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[EVAL]], i32 %[[DVAL]] monotonic monotonic
+!CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
+!CHECK: store i32 %[[OLD]], ptr %[[V]]
+subroutine atomic_compare_capture_int_eq(x, e, d, v)
+ integer :: x, e, d, v
+ !$omp atomic compare capture
+ v = x
+ if (x == e) x = d
+ !$omp end atomic
+end
+
+! Compare+capture with clause order reversed: capture compare
+!CHECK-LABEL: define void @atomic_capture_compare_int_eq_(
+!CHECK-SAME: ptr noalias %[[X:.*]], ptr noalias %[[E:.*]], ptr noalias %[[D:.*]], ptr noalias %[[V:.*]])
+!CHECK: %[[EVAL:.*]] = load i32, ptr %[[E]]
+!CHECK: %[[DVAL:.*]] = load i32, ptr %[[D]]
+!CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[EVAL]], i32 %[[DVAL]] monotonic monotonic
+!CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
+!CHECK: store i32 %[[OLD]], ptr %[[V]]
+subroutine atomic_capture_compare_int_eq(x, e, d, v)
+ integer :: x, e, d, v
+ !$omp atomic capture compare
+ v = x
+ if (x == e) x = d
+ !$omp end atomic
+end
+
diff --git a/flang/test/Lower/OpenMP/atomic-compare.f90 b/flang/test/Lower/OpenMP/atomic-compare.f90
index ac70edbed4e60..752a221aa538d 100644
--- a/flang/test/Lower/OpenMP/atomic-compare.f90
+++ b/flang/test/Lower/OpenMP/atomic-compare.f90
@@ -161,3 +161,59 @@ subroutine atomic_compare_int_eq_weak(x, e, d)
!$omp atomic compare weak
if (x .eq. e) x = d
end
+
+! CHECK-LABEL: func.func @_QPatomic_compare_capture_int_eq(
+! CHECK-SAME: %[[X:.*]]: !fir.ref<i32> {fir.bindc_name = "x"},
+! CHECK-SAME: %[[E:.*]]: !fir.ref<i32> {fir.bindc_name = "e"},
+! CHECK-SAME: %[[D:.*]]: !fir.ref<i32> {fir.bindc_name = "d"},
+! CHECK-SAME: %[[V:.*]]: !fir.ref<i32> {fir.bindc_name = "v"})
+! CHECK: %[[D_DECL:.*]]:2 = hlfir.declare %[[D]] {{.*}}
+! CHECK: %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
+! CHECK: %[[V_DECL:.*]]:2 = hlfir.declare %[[V]] {{.*}}
+! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
+! CHECK: %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
+! CHECK: omp.atomic.capture memory_order(relaxed) {
+! CHECK: omp.atomic.read %[[V_DECL]]#0 = %[[X_DECL]]#0 : !fir.ref<i32>, !fir.ref<i32>, i32
+! CHECK: omp.atomic.compare %[[X_DECL]]#0 : !fir.ref<i32> {
+! CHECK: ^bb0(%[[XVAL:.*]]: i32):
+! CHECK: %[[CMP:.*]] = arith.cmpi eq, %[[XVAL]], %[[EVAL]] : i32
+! CHECK: %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<i32>
+! CHECK: %[[SEL:.*]] = arith.select %[[CMP]], %[[DVAL]], %[[XVAL]] : i32
+! CHECK: omp.yield(%[[SEL]] : i32)
+! CHECK: }
+! CHECK: }
+subroutine atomic_compare_capture_int_eq(x, e, d, v)
+ integer :: x, e, d, v
+ !$omp atomic compare capture
+ v = x
+ if (x .eq. e) x = d
+ !$omp end atomic
+end
+
+! CHECK-LABEL: func.func @_QPatomic_compare_capture_int_gt(
+! CHECK-SAME: %[[X:.*]]: !fir.ref<i32> {fir.bindc_name = "x"},
+! CHECK-SAME: %[[E:.*]]: !fir.ref<i32> {fir.bindc_name = "e"},
+! CHECK-SAME: %[[D:.*]]: !fir.ref<i32> {fir.bindc_name = "d"},
+! CHECK-SAME: %[[V:.*]]: !fir.ref<i32> {fir.bindc_name = "v"})
+! CHECK: %[[D_DECL:.*]]:2 = hlfir.declare %[[D]] {{.*}}
+! CHECK: %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
+! CHECK: %[[V_DECL:.*]]:2 = hlfir.declare %[[V]] {{.*}}
+! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
+! CHECK: %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
+! CHECK: omp.atomic.capture memory_order(relaxed) {
+! CHECK: omp.atomic.read %[[V_DECL]]#0 = %[[X_DECL]]#0 : !fir.ref<i32>, !fir.ref<i32>, i32
+! CHECK: omp.atomic.compare %[[X_DECL]]#0 : !fir.ref<i32> {
+! CHECK: ^bb0(%[[XVAL:.*]]: i32):
+! CHECK: %[[CMP:.*]] = arith.cmpi sgt, %[[XVAL]], %[[EVAL]] : i32
+! CHECK: %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<i32>
+! CHECK: %[[SEL:.*]] = arith.select %[[CMP]], %[[DVAL]], %[[XVAL]] : i32
+! CHECK: omp.yield(%[[SEL]] : i32)
+! CHECK: } {{.*}}weak{{.*}}
+! CHECK: }
+subroutine atomic_compare_capture_int_gt(x, e, d, v)
+ integer :: x, e, d, v
+ !$omp atomic compare capture weak
+ v = x
+ if (x > e) x = d
+ !$omp end atomic
+end
diff --git a/flang/test/Parser/OpenMP/atomic-unparse.f90 b/flang/test/Parser/OpenMP/atomic-unparse.f90
index 4f3cf0eac0338..dc0cc1a62f6c2 100644
--- a/flang/test/Parser/OpenMP/atomic-unparse.f90
+++ b/flang/test/Parser/OpenMP/atomic-unparse.f90
@@ -192,6 +192,26 @@ program main
i = j
end if
+!COMPARE CAPTURE
+!$omp atomic compare capture
+ k = i
+ if (i .eq. j) then
+ i = k
+ end if
+!$omp end atomic
+!$omp atomic capture compare
+ k = i
+ if (i .eq. j) then
+ i = k
+ end if
+!$omp end atomic
+!$omp atomic capture compare weak
+ k = i
+ if (i < j) then
+ i = k
+ end if
+!$omp end atomic
+
!ATOMIC
!$omp atomic
i = j
@@ -296,6 +316,15 @@ end program main
!CHECK: !$OMP ATOMIC WEAK COMPARE
!CHECK: !$OMP ATOMIC COMPARE SEQ_CST WEAK
+!COMPARE CAPTURE
+
+!CHECK: !$OMP ATOMIC COMPARE CAPTURE
+!CHECK: !$OMP END ATOMIC
+!CHECK: !$OMP ATOMIC CAPTURE COMPARE
+!CHECK: !$OMP END ATOMIC
+!CHECK: !$OMP ATOMIC CAPTURE COMPARE WEAK
+!CHECK: !$OMP END ATOMIC
+
!ATOMIC
!CHECK: !$OMP ATOMIC
!CHECK: !$OMP ATOMIC SEQ_CST
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 17f99a8c86d52..c5a07a7dc6cb2 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -4963,13 +4963,14 @@ convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
bool isPostfixUpdate = true;
+ bool isWeak = atomicCompareOp.getWeak();
bool savedHandleFPNegZero = ompBuilder->setHandleFPNegZero(true);
llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =
- ompBuilder->createAtomicCompare(ompLoc, llvmAtomicX, llvmAtomicV,
- llvmAtomicR, eVal, dVal, atomicOrdering,
- compareOp, isXBinopExpr,
- isPostfixUpdate, /*IsFailOnly=*/false);
+ ompBuilder->createAtomicCompare(
+ ompLoc, llvmAtomicX, llvmAtomicV, llvmAtomicR, eVal, dVal,
+ atomicOrdering, compareOp, isXBinopExpr, isPostfixUpdate,
+ /*IsFailOnly=*/false, isWeak);
ompBuilder->setHandleFPNegZero(savedHandleFPNegZero);
if (failed(handleError(afterIP, *atomicCaptureOp)))
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index acf6879906e74..289ec60551cb7 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -2047,6 +2047,54 @@ func.func @omp_atomic_compare(%x: memref<i32>, %e: i32, %d: i32) {
return
}
+// CHECK-LABEL: omp_atomic_compare_capture
+// CHECK-SAME: (%[[V:.*]]: memref<i32>, %[[X:.*]]: memref<i32>, %[[E:.*]]: i32, %[[D:.*]]: i32)
+func.func @omp_atomic_compare_capture(%v: memref<i32>, %x: memref<i32>, %e: i32, %d: i32) {
+ // CHECK: omp.atomic.capture {
+ // CHECK-NEXT: omp.atomic.read %[[V]] = %[[X]] : memref<i32>, memref<i32>, i32
+ // CHECK-NEXT: omp.atomic.compare %[[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: }
+ // CHECK-NEXT: }
+ omp.atomic.capture {
+ omp.atomic.read %v = %x : memref<i32>, memref<i32>, i32
+ omp.atomic.compare %x : memref<i32> {
+ ^bb0(%xval: i32):
+ %cmp = arith.cmpi eq, %xval, %e : i32
+ %sel = arith.select %cmp, %d, %xval : i32
+ omp.yield(%sel : i32)
+ }
+ }
+ return
+}
+
+// CHECK-LABEL: omp_atomic_compare_capture_weak
+// CHECK-SAME: (%[[V:.*]]: memref<i32>, %[[X:.*]]: memref<i32>, %[[E:.*]]: i32, %[[D:.*]]: i32)
+func.func @omp_atomic_compare_capture_weak(%v: memref<i32>, %x: memref<i32>, %e: i32, %d: i32) {
+ // CHECK: omp.atomic.capture {
+ // CHECK-NEXT: omp.atomic.read %[[V]] = %[[X]] : memref<i32>, memref<i32>, i32
+ // CHECK-NEXT: omp.atomic.compare %[[X]] : memref<i32> {
+ // CHECK-NEXT: ^bb0(%[[XVAL:.*]]: i32):
+ // CHECK-NEXT: %[[CMP:.*]] = arith.cmpi sgt, %[[XVAL]], %[[E]] : i32
+ // CHECK-NEXT: %[[SEL:.*]] = arith.select %[[CMP]], %[[D]], %[[XVAL]] : i32
+ // CHECK-NEXT: omp.yield(%[[SEL]] : i32)
+ // CHECK-NEXT: } {weak}
+ // CHECK-NEXT: }
+ omp.atomic.capture {
+ omp.atomic.read %v = %x : memref<i32>, memref<i32>, i32
+ omp.atomic.compare %x : memref<i32> {
+ ^bb0(%xval: i32):
+ %cmp = arith.cmpi sgt, %xval, %e : i32
+ %sel = arith.select %cmp, %d, %xval : i32
+ omp.yield(%sel : i32)
+ } {weak}
+ }
+ 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 1890eaa6a3f0b..6ce57b304bfd9 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -2795,6 +2795,47 @@ llvm.func @omp_atomic_compare_float_neg_zero(%xf : !llvm.ptr, %ef : f32, %df : f
// -----
+// CHECK-LABEL: @omp_atomic_compare_capture_int_eq
+// CHECK-SAME: (ptr %[[X:.*]], ptr %[[V:.*]], i32 %[[E:.*]], i32 %[[D:.*]])
+llvm.func @omp_atomic_compare_capture_int_eq(%x : !llvm.ptr, %v : !llvm.ptr, %e : i32, %d : i32) {
+ // Integer equality compare+capture → cmpxchg + extractvalue + store
+ // CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[E]], i32 %[[D]] monotonic monotonic
+ // CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
+ // CHECK: store i32 %[[OLD]], ptr %[[V]]
+ omp.atomic.capture {
+ omp.atomic.read %v = %x : !llvm.ptr, !llvm.ptr, i32
+ 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)
+ }
+ }
+ llvm.return
+}
+
+// -----
+
+// CHECK-LABEL: @omp_atomic_compare_capture_weak_int_eq
+// CHECK-SAME: (ptr %[[X:.*]], ptr %[[V:.*]], i32 %[[E:.*]], i32 %[[D:.*]])
+llvm.func @omp_atomic_compare_capture_weak_int_eq(%x : !llvm.ptr, %v : !llvm.ptr, %e : i32, %d : i32) {
+ // Integer equality compare+capture → cmpxchg + extractvalue + store
+ // CHECK: %[[RES:.*]] = cmpxchg weak ptr %[[X]], i32 %[[E]], i32 %[[D]] monotonic monotonic
+ // CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
+ // CHECK: store i32 %[[OLD]], ptr %[[V]]
+ omp.atomic.capture {
+ omp.atomic.read %v = %x : !llvm.ptr, !llvm.ptr, i32
+ 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)
+ } {weak}
+ }
+ llvm.return
+}
+// -----
+
// CHECK-LABEL: @omp_sections_empty
llvm.func @omp_sections_empty() -> () {
omp.sections {
>From f84987dea68a1dedc6b61bf0c9f5a2e24a0a1b40 Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Tue, 9 Jun 2026 04:42:48 -0500
Subject: [PATCH 3/5] Minor changes incorporating feedback.
---
flang/lib/Lower/OpenMP/Atomic.cpp | 4 ++--
.../Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp
index c0f97aada637d..aa497b838e5b6 100644
--- a/flang/lib/Lower/OpenMP/Atomic.cpp
+++ b/flang/lib/Lower/OpenMP/Atomic.cpp
@@ -567,7 +567,7 @@ void Fortran::lower::omp::lowerAtomic(
if (construct.IsCapture()) {
assert(action0 != analysis.None && action1 != analysis.None &&
- "Expexcing two actions");
+ "Expecting two actions");
(void)action0;
(void)action1;
captureOp = mlir::omp::AtomicCaptureOp::create(
@@ -730,7 +730,7 @@ void Fortran::lower::omp::lowerAtomic(
if (!construct.IsCapture()) {
// Non-capturing operation.
assert(action0 != analysis.None && action1 == analysis.None &&
- "Expexcing single action");
+ "Expecting single action");
assert(!(analysis.op0.what & analysis.Condition));
postAt = atomicAt = preAt;
}
diff --git a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
index 8c9015f05bb72..dc5b04126b757 100644
--- a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
@@ -315,7 +315,7 @@ def AtomicCaptureOpInterface : OpInterface<"AtomicCaptureOpInterface"> {
if (firstReadStmt && secondCompareStmt &&
firstReadStmt.getX() != secondCompareStmt.getX())
return firstReadStmt.emitError()
- << "captured variable in atomic.read must be updated in "
+ << "captured variable in atomic.read must be compared/updated in "
"second operation";
return mlir::success();
>From ca24a147920d24c188127dbbe37ca62bcaeab792 Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Tue, 23 Jun 2026 14:18:17 -0500
Subject: [PATCH 4/5] Handling test cases mentionend in OpenMP spec.
---
flang/lib/Lower/OpenMP/Atomic.cpp | 61 ++++++++++++++++---
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp | 7 ++-
.../Interfaces/AtomicInterfaces.td | 11 +++-
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 2 +
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 12 ++--
5 files changed, 74 insertions(+), 19 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp
index aa497b838e5b6..209871abd52b6 100644
--- a/flang/lib/Lower/OpenMP/Atomic.cpp
+++ b/flang/lib/Lower/OpenMP/Atomic.cpp
@@ -571,7 +571,8 @@ void Fortran::lower::omp::lowerAtomic(
(void)action0;
(void)action1;
captureOp = mlir::omp::AtomicCaptureOp::create(
- builder, loc, hint, makeMemOrderAttr(converter, memOrder));
+ builder, loc, hint, makeMemOrderAttr(converter, memOrder),
+ /*fail_only=*/nullptr);
// Set the non-atomic insertion point to before the atomic.capture.
preAt = getInsertionPointBefore(captureOp);
@@ -646,15 +647,45 @@ void Fortran::lower::omp::lowerAtomic(
expectedVal = builder.createConvert(loc, elemTypeOfX, expectedVal);
}
- // If this is a compare+capture, generate the read op first.
+ // If this is a compare+capture, determine the ordering of ops.
+ // Pattern 1 (prefix): v = x; if (x == e) x = d → read first
+ // Pattern 2 (postfix): if (x == e) x = d; v = x → compare first
+ // Pattern 3 (fail-only): if (x == e) x = d; else v = x → read on failure
+ bool isPostfixCapture = false;
+ bool isFailOnly = false;
+ const evaluate::Assignment *readAssign = nullptr;
if (construct.IsCapture()) {
- assert(get(analysis.op0.assign) && (analysis.op0.what & analysis.Read) &&
- "Expected a read operation for compare capture");
- mlir::Operation *readOp = genAtomicRead(
- converter, semaCtx, loc, stmtCtx, atomAddr, atom,
- *get(analysis.op0.assign), hint, memOrder, preAt, atomicAt, postAt);
- assert(readOp && "Should have created an atomic read operation");
- builder.setInsertionPointAfter(readOp);
+ // Determine which op is the read and check for fail-only (IfFalse).
+ int readWhat = 0;
+ if (analysis.op0.what & analysis.Read) {
+ readAssign = get(analysis.op0.assign);
+ readWhat = analysis.op0.what;
+ } else if (analysis.op1.what & analysis.Read) {
+ readAssign = get(analysis.op1.assign);
+ readWhat = analysis.op1.what;
+ isPostfixCapture = true;
+ }
+ assert(readAssign && "Expected a read assignment for compare capture");
+
+ // Check if the read is conditioned on comparison failure (else branch).
+ if (readWhat & analysis.IfFalse)
+ isFailOnly = true;
+
+ if (!isPostfixCapture && !isFailOnly) {
+ // Pattern 1 (prefix): read is first, generate it before compare.
+ mlir::Operation *readOp =
+ genAtomicRead(converter, semaCtx, loc, stmtCtx, atomAddr, atom,
+ *readAssign, hint, memOrder, preAt, atomicAt, postAt);
+ assert(readOp && "Should have created an atomic read operation");
+ builder.setInsertionPointAfter(readOp);
+ } else {
+ // Pattern 2 (postfix) or 3 (fail-only): compare first, read after.
+ builder.restoreInsertionPoint(atomicAt);
+ }
+
+ // Set the fail_only attribute on the capture op.
+ if (isFailOnly && captureOp)
+ mlir::cast<mlir::omp::AtomicCaptureOp>(captureOp).setFailOnly(true);
}
mlir::UnitAttr weakAttr = nullptr;
@@ -725,6 +756,18 @@ void Fortran::lower::omp::lowerAtomic(
// Generate omp.yield
mlir::omp::YieldOp::create(builder, loc, newVal);
builder.setInsertionPointAfter(atomicOp);
+
+ // Pattern 2 (postfix) or 3 (fail-only): compare first, read second.
+ // Generate read after compare for postfix or fail-only patterns.
+ if (construct.IsCapture() && (isPostfixCapture || isFailOnly)) {
+ fir::FirOpBuilder::InsertPoint afterCompareAt =
+ builder.saveInsertionPoint();
+ mlir::Operation *readOp = genAtomicRead(
+ converter, semaCtx, loc, stmtCtx, atomAddr, atom, *readAssign, hint,
+ memOrder, preAt, afterCompareAt, postAt);
+ assert(readOp && "Should have created an atomic read operation");
+ builder.setInsertionPointAfter(readOp);
+ }
// END omp atomic compare
} else {
if (!construct.IsCapture()) {
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index 92eb7de0d882f..b2ccc05ad6770 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -11321,7 +11321,9 @@ OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createAtomicCompare(
assert(OldValue->getType() == V.ElemTy &&
"OldValue and V must be of same type");
if (IsPostfixUpdate) {
- Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
+ SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
+ Value *NewValue = Builder.CreateSelect(SuccessOrFail, D, OldValue);
+ Builder.CreateStore(NewValue, V.Var, V.IsVolatile);
} else {
SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
if (IsFailOnly) {
@@ -11377,7 +11379,8 @@ OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createAtomicCompare(
assert(OldValue->getType() == V.ElemTy &&
"OldValue and V must be of same type");
if (IsPostfixUpdate) {
- Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
+ Value *NewValue = Builder.CreateSelect(SuccessOrFail, D, OldValue);
+ Builder.CreateStore(NewValue, V.Var, V.IsVolatile);
} else {
if (IsFailOnly) {
BasicBlock *CurBB = Builder.GetInsertBlock();
diff --git a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
index dc5b04126b757..5b1527bcb84ec 100644
--- a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
@@ -286,6 +286,7 @@ def AtomicCaptureOpInterface : OpInterface<"AtomicCaptureOpInterface"> {
auto &secondOp = *ops.getNextNode(firstOp);
auto firstReadStmt = dyn_cast<AtomicReadOpInterface>(firstOp);
auto firstUpdateStmt = dyn_cast<AtomicUpdateOpInterface>(firstOp);
+ auto firstCompareStmt = dyn_cast<AtomicCompareOpInterface>(firstOp);
auto secondReadStmt = dyn_cast<AtomicReadOpInterface>(secondOp);
auto secondUpdateStmt = dyn_cast<AtomicUpdateOpInterface>(secondOp);
auto secondWriteStmt = dyn_cast<AtomicWriteOpInterface>(secondOp);
@@ -294,7 +295,8 @@ def AtomicCaptureOpInterface : OpInterface<"AtomicCaptureOpInterface"> {
if (!((firstUpdateStmt && secondReadStmt) ||
(firstReadStmt && secondUpdateStmt) ||
(firstReadStmt && secondWriteStmt) ||
- (firstReadStmt && secondCompareStmt)))
+ (firstReadStmt && secondCompareStmt) ||
+ (firstCompareStmt && secondReadStmt)))
return ops.front().emitError()
<< "invalid sequence of operations in the capture region";
if (firstUpdateStmt && secondReadStmt &&
@@ -315,8 +317,13 @@ def AtomicCaptureOpInterface : OpInterface<"AtomicCaptureOpInterface"> {
if (firstReadStmt && secondCompareStmt &&
firstReadStmt.getX() != secondCompareStmt.getX())
return firstReadStmt.emitError()
- << "captured variable in atomic.read must be compared/updated in "
+ << "captured variable in atomic.read must be updated in "
"second operation";
+ if (firstCompareStmt && secondReadStmt &&
+ secondReadStmt.getX() != firstCompareStmt.getX())
+ return secondReadStmt.emitError()
+ << "captured variable in atomic.read must be updated in "
+ "first operation";
return mlir::success();
}]
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index ebee887f2afd1..69c22641df6a2 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -1938,6 +1938,8 @@ def AtomicCaptureOp : OpenMP_Op<"atomic.capture", traits = [
```
}] # clausesDescription;
+ let arguments = !con(clausesArgs, (ins UnitAttr:$fail_only));
+
// 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 dff1ae30e20aa..76616c7fba27a 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -4974,15 +4974,15 @@ convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
llvmAtomicX.IsSigned = isSigned;
llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
- bool isPostfixUpdate = true;
- bool isWeak = atomicCompareOp.getWeak();
+ bool isFailOnly = atomicCaptureOp.getFailOnly();
+ bool isPostfixUpdate = !isFailOnly;
bool savedHandleFPNegZero = ompBuilder->setHandleFPNegZero(true);
llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =
- ompBuilder->createAtomicCompare(
- ompLoc, llvmAtomicX, llvmAtomicV, llvmAtomicR, eVal, dVal,
- atomicOrdering, compareOp, isXBinopExpr, isPostfixUpdate,
- /*IsFailOnly=*/false, isWeak);
+ ompBuilder->createAtomicCompare(ompLoc, llvmAtomicX, llvmAtomicV,
+ llvmAtomicR, eVal, dVal, atomicOrdering,
+ compareOp, isXBinopExpr,
+ isPostfixUpdate, isFailOnly);
ompBuilder->setHandleFPNegZero(savedHandleFPNegZero);
if (failed(handleError(afterIP, *atomicCaptureOp)))
>From afaf2025463e9e8cf15974747fa9e2d8b8e5fa95 Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Tue, 23 Jun 2026 18:02:52 -0500
Subject: [PATCH 5/5] Adding tests into flang and mlir for the grammar
mentionend in the spec.
---
.../Integration/OpenMP/atomic-compare.f90 | 56 ++++++++++++++++--
flang/test/Lower/OpenMP/atomic-compare.f90 | 59 +++++++++++++++++++
flang/test/Parser/OpenMP/atomic-unparse.f90 | 17 ++++++
.../OpenMP/OpenMPToLLVMIRTranslation.cpp | 17 +++++-
mlir/test/Dialect/OpenMP/ops.mlir | 48 +++++++++++++++
mlir/test/Target/LLVMIR/openmp-llvm.mlir | 58 ++++++++++++++++--
6 files changed, 245 insertions(+), 10 deletions(-)
diff --git a/flang/test/Integration/OpenMP/atomic-compare.f90 b/flang/test/Integration/OpenMP/atomic-compare.f90
index 650f64b80af12..da86f3ff6ef16 100644
--- a/flang/test/Integration/OpenMP/atomic-compare.f90
+++ b/flang/test/Integration/OpenMP/atomic-compare.f90
@@ -260,14 +260,17 @@ subroutine atomic_compare_weak(x, e, d)
if (x == e) x = d
end
-! Integer equality compare+capture: cmpxchg + store old value
+! Integer equality compare+capture (prefix): v=x; if(x==e) x=d
+! v captures old value of x
!CHECK-LABEL: define void @atomic_compare_capture_int_eq_(
!CHECK-SAME: ptr noalias %[[X:.*]], ptr noalias %[[E:.*]], ptr noalias %[[D:.*]], ptr noalias %[[V:.*]])
!CHECK: %[[EVAL:.*]] = load i32, ptr %[[E]]
!CHECK: %[[DVAL:.*]] = load i32, ptr %[[D]]
!CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[EVAL]], i32 %[[DVAL]] monotonic monotonic
!CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
-!CHECK: store i32 %[[OLD]], ptr %[[V]]
+!CHECK: %[[SUCCESS:.*]] = extractvalue { i32, i1 } %[[RES]], 1
+!CHECK: %[[CAPTURED:.*]] = select i1 %[[SUCCESS]], i32 %[[EVAL]], i32 %[[OLD]]
+!CHECK: store i32 %[[CAPTURED]], ptr %[[V]]
subroutine atomic_compare_capture_int_eq(x, e, d, v)
integer :: x, e, d, v
!$omp atomic compare capture
@@ -276,14 +279,16 @@ subroutine atomic_compare_capture_int_eq(x, e, d, v)
!$omp end atomic
end
-! Compare+capture with clause order reversed: capture compare
+! Compare+capture with clause order reversed: capture compare (still prefix read)
!CHECK-LABEL: define void @atomic_capture_compare_int_eq_(
!CHECK-SAME: ptr noalias %[[X:.*]], ptr noalias %[[E:.*]], ptr noalias %[[D:.*]], ptr noalias %[[V:.*]])
!CHECK: %[[EVAL:.*]] = load i32, ptr %[[E]]
!CHECK: %[[DVAL:.*]] = load i32, ptr %[[D]]
!CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[EVAL]], i32 %[[DVAL]] monotonic monotonic
!CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
-!CHECK: store i32 %[[OLD]], ptr %[[V]]
+!CHECK: %[[SUCCESS:.*]] = extractvalue { i32, i1 } %[[RES]], 1
+!CHECK: %[[CAPTURED:.*]] = select i1 %[[SUCCESS]], i32 %[[EVAL]], i32 %[[OLD]]
+!CHECK: store i32 %[[CAPTURED]], ptr %[[V]]
subroutine atomic_capture_compare_int_eq(x, e, d, v)
integer :: x, e, d, v
!$omp atomic capture compare
@@ -292,3 +297,46 @@ subroutine atomic_capture_compare_int_eq(x, e, d, v)
!$omp end atomic
end
+! Postfix compare+capture: if (x == e) x = d; v = x
+! v captures new value (d if swapped, old x if not)
+!CHECK-LABEL: define void @atomic_compare_capture_postfix_(
+!CHECK-SAME: ptr noalias %[[X:.*]], ptr noalias %[[E:.*]], ptr noalias %[[D:.*]], ptr noalias %[[V:.*]])
+!CHECK: %[[EVAL:.*]] = load i32, ptr %[[E]]
+!CHECK: %[[DVAL:.*]] = load i32, ptr %[[D]]
+!CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[EVAL]], i32 %[[DVAL]] monotonic monotonic
+!CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
+!CHECK: %[[SUCCESS:.*]] = extractvalue { i32, i1 } %[[RES]], 1
+!CHECK: %[[NEWVAL:.*]] = select i1 %[[SUCCESS]], i32 %[[DVAL]], i32 %[[OLD]]
+!CHECK: store i32 %[[NEWVAL]], ptr %[[V]]
+subroutine atomic_compare_capture_postfix(x, e, d, v)
+ integer :: x, e, d, v
+ !$omp atomic compare capture
+ if (x == e) x = d
+ v = x
+ !$omp end atomic
+end
+
+! Fail-only compare+capture: if (x == e) x = d; else v = x
+! v is only written when the comparison fails
+!CHECK-LABEL: define void @atomic_compare_capture_fail_only_(
+!CHECK-SAME: ptr noalias %[[X:.*]], ptr noalias %[[E:.*]], ptr noalias %[[D:.*]], ptr noalias %[[V:.*]])
+!CHECK: %[[EVAL:.*]] = load i32, ptr %[[E]]
+!CHECK: %[[DVAL:.*]] = load i32, ptr %[[D]]
+!CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[EVAL]], i32 %[[DVAL]] monotonic monotonic
+!CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
+!CHECK: %[[SUCCESS:.*]] = extractvalue { i32, i1 } %[[RES]], 1
+!CHECK: br i1 %[[SUCCESS]], label %[[EXIT:.*]], label %[[CONT:.*]]
+!CHECK: {{.*}}:
+!CHECK: store i32 %[[OLD]], ptr %[[V]]
+!CHECK: br label %[[EXIT2:.*]]
+subroutine atomic_compare_capture_fail_only(x, e, d, v)
+ integer :: x, e, d, v
+ !$omp atomic compare capture
+ if (x == e) then
+ x = d
+ else
+ v = x
+ end if
+ !$omp end atomic
+end
+
diff --git a/flang/test/Lower/OpenMP/atomic-compare.f90 b/flang/test/Lower/OpenMP/atomic-compare.f90
index 752a221aa538d..c770605954539 100644
--- a/flang/test/Lower/OpenMP/atomic-compare.f90
+++ b/flang/test/Lower/OpenMP/atomic-compare.f90
@@ -217,3 +217,62 @@ subroutine atomic_compare_capture_int_gt(x, e, d, v)
if (x > e) x = d
!$omp end atomic
end
+
+! CHECK-LABEL: func.func @_QPatomic_compare_capture_postfix(
+! CHECK-SAME: %[[X:.*]]: !fir.ref<i32> {fir.bindc_name = "x"},
+! CHECK-SAME: %[[E:.*]]: !fir.ref<i32> {fir.bindc_name = "e"},
+! CHECK-SAME: %[[D:.*]]: !fir.ref<i32> {fir.bindc_name = "d"},
+! CHECK-SAME: %[[V:.*]]: !fir.ref<i32> {fir.bindc_name = "v"})
+! CHECK: %[[D_DECL:.*]]:2 = hlfir.declare %[[D]] {{.*}}
+! CHECK: %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
+! CHECK: %[[V_DECL:.*]]:2 = hlfir.declare %[[V]] {{.*}}
+! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
+! CHECK: %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
+! CHECK: omp.atomic.capture memory_order(relaxed) {
+! CHECK: omp.atomic.compare %[[X_DECL]]#0 : !fir.ref<i32> {
+! CHECK: ^bb0(%[[XVAL:.*]]: i32):
+! CHECK: %[[CMP:.*]] = arith.cmpi eq, %[[XVAL]], %[[EVAL]] : i32
+! CHECK: %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<i32>
+! CHECK: %[[SEL:.*]] = arith.select %[[CMP]], %[[DVAL]], %[[XVAL]] : i32
+! CHECK: omp.yield(%[[SEL]] : i32)
+! CHECK: }
+! CHECK: omp.atomic.read %[[V_DECL]]#0 = %[[X_DECL]]#0 : !fir.ref<i32>, !fir.ref<i32>, i32
+! CHECK: }
+subroutine atomic_compare_capture_postfix(x, e, d, v)
+ integer :: x, e, d, v
+ !$omp atomic compare capture
+ if (x .eq. e) x = d
+ v = x
+ !$omp end atomic
+end
+
+! CHECK-LABEL: func.func @_QPatomic_compare_capture_fail_only(
+! CHECK-SAME: %[[X:.*]]: !fir.ref<i32> {fir.bindc_name = "x"},
+! CHECK-SAME: %[[E:.*]]: !fir.ref<i32> {fir.bindc_name = "e"},
+! CHECK-SAME: %[[D:.*]]: !fir.ref<i32> {fir.bindc_name = "d"},
+! CHECK-SAME: %[[V:.*]]: !fir.ref<i32> {fir.bindc_name = "v"})
+! CHECK: %[[D_DECL:.*]]:2 = hlfir.declare %[[D]] {{.*}}
+! CHECK: %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
+! CHECK: %[[V_DECL:.*]]:2 = hlfir.declare %[[V]] {{.*}}
+! CHECK: %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
+! CHECK: %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
+! CHECK: omp.atomic.capture memory_order(relaxed) {
+! CHECK: omp.atomic.compare %[[X_DECL]]#0 : !fir.ref<i32> {
+! CHECK: ^bb0(%[[XVAL:.*]]: i32):
+! CHECK: %[[CMP:.*]] = arith.cmpi eq, %[[XVAL]], %[[EVAL]] : i32
+! CHECK: %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<i32>
+! CHECK: %[[SEL:.*]] = arith.select %[[CMP]], %[[DVAL]], %[[XVAL]] : i32
+! CHECK: omp.yield(%[[SEL]] : i32)
+! CHECK: }
+! CHECK: omp.atomic.read %[[V_DECL]]#0 = %[[X_DECL]]#0 : !fir.ref<i32>, !fir.ref<i32>, i32
+! CHECK: } {fail_only}
+subroutine atomic_compare_capture_fail_only(x, e, d, v)
+ integer :: x, e, d, v
+ !$omp atomic compare capture
+ if (x .eq. e) then
+ x = d
+ else
+ v = x
+ end if
+ !$omp end atomic
+end
diff --git a/flang/test/Parser/OpenMP/atomic-unparse.f90 b/flang/test/Parser/OpenMP/atomic-unparse.f90
index dc0cc1a62f6c2..521c1bb2811e2 100644
--- a/flang/test/Parser/OpenMP/atomic-unparse.f90
+++ b/flang/test/Parser/OpenMP/atomic-unparse.f90
@@ -211,6 +211,19 @@ program main
i = k
end if
!$omp end atomic
+!$omp atomic compare capture
+ if (i .eq. j) then
+ i = k
+ end if
+ k = i
+!$omp end atomic
+!$omp atomic compare capture
+ if (i .eq. j) then
+ i = k
+ else
+ k = i
+ end if
+!$omp end atomic
!ATOMIC
!$omp atomic
@@ -324,6 +337,10 @@ end program main
!CHECK: !$OMP END ATOMIC
!CHECK: !$OMP ATOMIC CAPTURE COMPARE WEAK
!CHECK: !$OMP END ATOMIC
+!CHECK: !$OMP ATOMIC COMPARE CAPTURE
+!CHECK: !$OMP END ATOMIC
+!CHECK: !$OMP ATOMIC COMPARE CAPTURE
+!CHECK: !$OMP END ATOMIC
!ATOMIC
!CHECK: !$OMP ATOMIC
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 76616c7fba27a..b6fc6d6dc3038 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -4974,15 +4974,28 @@ convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
llvmAtomicX.IsSigned = isSigned;
llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
+ // In OMPIRBuilder::createAtomicCompare:
+ // IsPostfixUpdate=true → select(success, D, OldValue) → new value
+ // IsPostfixUpdate=false → select(success, E, OldValue) → old value
+ // Prefix read (v=x; if(x==e) x=d): v captures old → IsPostfixUpdate=false
+ // Postfix read (if(x==e) x=d; v=x): v captures new → IsPostfixUpdate=true
+ // Fail-only: conditional store only on failure → IsPostfixUpdate=false
+ bool isPostfixUpdate =
+ !isa<omp::AtomicReadOp>(atomicCaptureOp.getFirstOp());
bool isFailOnly = atomicCaptureOp.getFailOnly();
- bool isPostfixUpdate = !isFailOnly;
+ // For fail-only, the OMPIRBuilder's conditional store logic is under
+ // the !IsPostfixUpdate path, so force it to false.
+ if (isFailOnly)
+ isPostfixUpdate = false;
+
+ bool isWeak = atomicCompareOp.getWeak();
bool savedHandleFPNegZero = ompBuilder->setHandleFPNegZero(true);
llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =
ompBuilder->createAtomicCompare(ompLoc, llvmAtomicX, llvmAtomicV,
llvmAtomicR, eVal, dVal, atomicOrdering,
compareOp, isXBinopExpr,
- isPostfixUpdate, isFailOnly);
+ isPostfixUpdate, isFailOnly, isWeak);
ompBuilder->setHandleFPNegZero(savedHandleFPNegZero);
if (failed(handleError(afterIP, *atomicCaptureOp)))
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 87f9a6f8e4119..6b13be481e0e0 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -2113,6 +2113,54 @@ func.func @omp_atomic_compare_capture_weak(%v: memref<i32>, %x: memref<i32>, %e:
return
}
+// CHECK-LABEL: omp_atomic_compare_capture_postfix
+// CHECK-SAME: (%[[V:.*]]: memref<i32>, %[[X:.*]]: memref<i32>, %[[E:.*]]: i32, %[[D:.*]]: i32)
+func.func @omp_atomic_compare_capture_postfix(%v: memref<i32>, %x: memref<i32>, %e: i32, %d: i32) {
+ // CHECK: omp.atomic.capture {
+ // CHECK-NEXT: omp.atomic.compare %[[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: }
+ // CHECK-NEXT: omp.atomic.read %[[V]] = %[[X]] : memref<i32>, memref<i32>, i32
+ // CHECK-NEXT: }
+ omp.atomic.capture {
+ omp.atomic.compare %x : memref<i32> {
+ ^bb0(%xval: i32):
+ %cmp = arith.cmpi eq, %xval, %e : i32
+ %sel = arith.select %cmp, %d, %xval : i32
+ omp.yield(%sel : i32)
+ }
+ omp.atomic.read %v = %x : memref<i32>, memref<i32>, i32
+ }
+ return
+}
+
+// CHECK-LABEL: omp_atomic_compare_capture_fail_only
+// CHECK-SAME: (%[[V:.*]]: memref<i32>, %[[X:.*]]: memref<i32>, %[[E:.*]]: i32, %[[D:.*]]: i32)
+func.func @omp_atomic_compare_capture_fail_only(%v: memref<i32>, %x: memref<i32>, %e: i32, %d: i32) {
+ // CHECK: omp.atomic.capture {
+ // CHECK-NEXT: omp.atomic.compare %[[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: }
+ // CHECK-NEXT: omp.atomic.read %[[V]] = %[[X]] : memref<i32>, memref<i32>, i32
+ // CHECK-NEXT: } {fail_only}
+ omp.atomic.capture {
+ omp.atomic.compare %x : memref<i32> {
+ ^bb0(%xval: i32):
+ %cmp = arith.cmpi eq, %xval, %e : i32
+ %sel = arith.select %cmp, %d, %xval : i32
+ omp.yield(%sel : i32)
+ }
+ omp.atomic.read %v = %x : memref<i32>, memref<i32>, i32
+ } {fail_only}
+ 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 6ce57b304bfd9..978450d598b9b 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -2798,10 +2798,12 @@ llvm.func @omp_atomic_compare_float_neg_zero(%xf : !llvm.ptr, %ef : f32, %df : f
// CHECK-LABEL: @omp_atomic_compare_capture_int_eq
// CHECK-SAME: (ptr %[[X:.*]], ptr %[[V:.*]], i32 %[[E:.*]], i32 %[[D:.*]])
llvm.func @omp_atomic_compare_capture_int_eq(%x : !llvm.ptr, %v : !llvm.ptr, %e : i32, %d : i32) {
- // Integer equality compare+capture → cmpxchg + extractvalue + store
+ // Integer equality prefix compare+capture → cmpxchg + select(success, E, old) + store
// CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[E]], i32 %[[D]] monotonic monotonic
// CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
- // CHECK: store i32 %[[OLD]], ptr %[[V]]
+ // CHECK: %[[SUCCESS:.*]] = extractvalue { i32, i1 } %[[RES]], 1
+ // CHECK: %[[CAPTURED:.*]] = select i1 %[[SUCCESS]], i32 %[[E]], i32 %[[OLD]]
+ // CHECK: store i32 %[[CAPTURED]], ptr %[[V]]
omp.atomic.capture {
omp.atomic.read %v = %x : !llvm.ptr, !llvm.ptr, i32
omp.atomic.compare %x : !llvm.ptr {
@@ -2819,10 +2821,12 @@ llvm.func @omp_atomic_compare_capture_int_eq(%x : !llvm.ptr, %v : !llvm.ptr, %e
// CHECK-LABEL: @omp_atomic_compare_capture_weak_int_eq
// CHECK-SAME: (ptr %[[X:.*]], ptr %[[V:.*]], i32 %[[E:.*]], i32 %[[D:.*]])
llvm.func @omp_atomic_compare_capture_weak_int_eq(%x : !llvm.ptr, %v : !llvm.ptr, %e : i32, %d : i32) {
- // Integer equality compare+capture → cmpxchg + extractvalue + store
+ // Integer equality weak prefix compare+capture → cmpxchg + select(success, E, old) + store
// CHECK: %[[RES:.*]] = cmpxchg weak ptr %[[X]], i32 %[[E]], i32 %[[D]] monotonic monotonic
// CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
- // CHECK: store i32 %[[OLD]], ptr %[[V]]
+ // CHECK: %[[SUCCESS:.*]] = extractvalue { i32, i1 } %[[RES]], 1
+ // CHECK: %[[CAPTURED:.*]] = select i1 %[[SUCCESS]], i32 %[[E]], i32 %[[OLD]]
+ // CHECK: store i32 %[[CAPTURED]], ptr %[[V]]
omp.atomic.capture {
omp.atomic.read %v = %x : !llvm.ptr, !llvm.ptr, i32
omp.atomic.compare %x : !llvm.ptr {
@@ -2836,6 +2840,52 @@ llvm.func @omp_atomic_compare_capture_weak_int_eq(%x : !llvm.ptr, %v : !llvm.ptr
}
// -----
+// CHECK-LABEL: @omp_atomic_compare_capture_postfix
+// CHECK-SAME: (ptr %[[X:.*]], ptr %[[V:.*]], i32 %[[E:.*]], i32 %[[D:.*]])
+llvm.func @omp_atomic_compare_capture_postfix(%x : !llvm.ptr, %v : !llvm.ptr, %e : i32, %d : i32) {
+ // Postfix compare+capture: v captures new value (d if swapped, old x if not)
+ // CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[E]], i32 %[[D]] monotonic monotonic
+ // CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
+ // CHECK: %[[SUCCESS:.*]] = extractvalue { i32, i1 } %[[RES]], 1
+ // CHECK: %[[NEWVAL:.*]] = select i1 %[[SUCCESS]], i32 %[[D]], i32 %[[OLD]]
+ // CHECK: store i32 %[[NEWVAL]], ptr %[[V]]
+ omp.atomic.capture {
+ 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)
+ }
+ omp.atomic.read %v = %x : !llvm.ptr, !llvm.ptr, i32
+ }
+ llvm.return
+}
+// -----
+
+// CHECK-LABEL: @omp_atomic_compare_capture_fail_only
+// CHECK-SAME: (ptr %[[X:.*]], ptr %[[V:.*]], i32 %[[E:.*]], i32 %[[D:.*]])
+llvm.func @omp_atomic_compare_capture_fail_only(%x : !llvm.ptr, %v : !llvm.ptr, %e : i32, %d : i32) {
+ // Fail-only compare+capture: v is only written when comparison fails
+ // CHECK: %[[RES:.*]] = cmpxchg ptr %[[X]], i32 %[[E]], i32 %[[D]] monotonic monotonic
+ // CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
+ // CHECK: %[[SUCCESS:.*]] = extractvalue { i32, i1 } %[[RES]], 1
+ // CHECK: br i1 %[[SUCCESS]], label %[[EXIT:.*]], label %[[CONT:.*]]
+ // CHECK: [[CONT]]:
+ // CHECK: store i32 %[[OLD]], ptr %[[V]]
+ // CHECK: br label %[[EXIT2:.*]]
+ omp.atomic.capture {
+ 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)
+ }
+ omp.atomic.read %v = %x : !llvm.ptr, !llvm.ptr, i32
+ } {fail_only}
+ llvm.return
+}
+// -----
+
// CHECK-LABEL: @omp_sections_empty
llvm.func @omp_sections_empty() -> () {
omp.sections {
More information about the flang-commits
mailing list