[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 30 12:06:01 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/7] 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 &region = 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() == &region) {
+          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/7] 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/7] 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/7] 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/7] 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 {

>From e8462a267b6d010ebc1e3ed881c1f38a9faca6be Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Wed, 24 Jun 2026 14:44:43 -0500
Subject: [PATCH 6/7] Simplifying the IR generator for one of the cases. This
 also fixes the failure in clang for "atomic compare capture".

---
 .../Integration/OpenMP/atomic-compare.f90     |  8 +---
 llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp     |  7 +--
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      | 43 +++++++++++++------
 mlir/test/Target/LLVMIR/openmp-llvm.mlir      | 12 ++----
 4 files changed, 38 insertions(+), 32 deletions(-)

diff --git a/flang/test/Integration/OpenMP/atomic-compare.f90 b/flang/test/Integration/OpenMP/atomic-compare.f90
index da86f3ff6ef16..4ae1b9a1f1e52 100644
--- a/flang/test/Integration/OpenMP/atomic-compare.f90
+++ b/flang/test/Integration/OpenMP/atomic-compare.f90
@@ -268,9 +268,7 @@ subroutine atomic_compare_weak(x, e, d)
 !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: %[[CAPTURED:.*]] = select i1 %[[SUCCESS]], i32 %[[EVAL]], i32 %[[OLD]]
-!CHECK: store i32 %[[CAPTURED]], ptr %[[V]]
+!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
@@ -286,9 +284,7 @@ subroutine atomic_compare_capture_int_eq(x, e, d, v)
 !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: %[[CAPTURED:.*]] = select i1 %[[SUCCESS]], i32 %[[EVAL]], i32 %[[OLD]]
-!CHECK: store i32 %[[CAPTURED]], ptr %[[V]]
+!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
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index 51f210e189e22..f8c1999fe1b89 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -11400,9 +11400,7 @@ OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createAtomicCompare(
         assert(OldValue->getType() == V.ElemTy &&
                "OldValue and V must be of same type");
         if (IsPostfixUpdate) {
-          SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
-          Value *NewValue = Builder.CreateSelect(SuccessOrFail, D, OldValue);
-          Builder.CreateStore(NewValue, V.Var, V.IsVolatile);
+          Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
         } else {
           SuccessOrFail = Builder.CreateExtractValue(Result, /*Idxs=*/1);
           if (IsFailOnly) {
@@ -11458,8 +11456,7 @@ OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createAtomicCompare(
         assert(OldValue->getType() == V.ElemTy &&
                "OldValue and V must be of same type");
         if (IsPostfixUpdate) {
-          Value *NewValue = Builder.CreateSelect(SuccessOrFail, D, OldValue);
-          Builder.CreateStore(NewValue, V.Var, V.IsVolatile);
+          Builder.CreateStore(OldValue, V.Var, V.IsVolatile);
         } else {
           if (IsFailOnly) {
             BasicBlock *CurBB = Builder.GetInsertBlock();
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 85389caa9ac42..e1fb08d780753 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -5499,25 +5499,23 @@ 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 isReadFirst = isa<omp::AtomicReadOp>(atomicCaptureOp.getFirstOp());
+    bool isPostfixCapture = !isReadFirst;
     bool isFailOnly = atomicCaptureOp.getFailOnly();
 
-    // For fail-only, the OMPIRBuilder's conditional store logic is under
-    // the !IsPostfixUpdate path, so force it to false.
-    if (isFailOnly)
-      isPostfixUpdate = false;
+    llvm::OpenMPIRBuilder::AtomicOpValue llvmAtomicVForCall = llvmAtomicV;
+    // Postfix: bypass V in OMPIRBuilder, handled manually below.
+    if (isPostfixCapture && !isFailOnly)
+      llvmAtomicVForCall = {nullptr, nullptr, false, false};
+
+    // Prefix: IsPostfixUpdate=true → direct store of old value.
+    // Fail-only: IsPostfixUpdate=false → conditional store on failure.
+    bool isPostfixUpdate = !isFailOnly;
 
     bool isWeak = atomicCompareOp.getWeak();
     bool savedHandleFPNegZero = ompBuilder->setHandleFPNegZero(true);
     llvm::OpenMPIRBuilder::InsertPointOrErrorTy afterIP =
-        ompBuilder->createAtomicCompare(ompLoc, llvmAtomicX, llvmAtomicV,
+        ompBuilder->createAtomicCompare(ompLoc, llvmAtomicX, llvmAtomicVForCall,
                                         llvmAtomicR, eVal, dVal, atomicOrdering,
                                         compareOp, isXBinopExpr,
                                         isPostfixUpdate, isFailOnly, isWeak);
@@ -5527,6 +5525,25 @@ convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
       return failure();
 
     builder.restoreIP(*afterIP);
+
+    // Postfix: v = select(success, D, old) — captures new value of x.
+    if (isPostfixCapture && !isFailOnly) {
+      llvm::BasicBlock *curBB = builder.GetInsertBlock();
+      llvm::Instruction *cmpxchgInst = nullptr;
+      for (auto &inst : llvm::reverse(*curBB)) {
+        if (isa<llvm::AtomicCmpXchgInst>(&inst)) {
+          cmpxchgInst = &inst;
+          break;
+        }
+      }
+      assert(cmpxchgInst && "expected cmpxchg instruction");
+      llvm::Value *oldVal = builder.CreateExtractValue(cmpxchgInst, /*Idxs=*/0);
+      llvm::Value *successVal =
+          builder.CreateExtractValue(cmpxchgInst, /*Idxs=*/1);
+      llvm::Value *newVal = builder.CreateSelect(successVal, dVal, oldVal);
+      builder.CreateStore(newVal, llvmAtomicV.Var, llvmAtomicV.IsVolatile);
+    }
+
     return success();
   }
 
diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index 978450d598b9b..377bd88894854 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -2798,12 +2798,10 @@ 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 prefix compare+capture → cmpxchg + select(success, E, old) + store
+  // Integer equality prefix compare+capture → cmpxchg + store old value
   // 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: %[[CAPTURED:.*]] = select i1 %[[SUCCESS]], i32 %[[E]], i32 %[[OLD]]
-  // CHECK: store i32 %[[CAPTURED]], ptr %[[V]]
+  // 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 {
@@ -2821,12 +2819,10 @@ 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 weak prefix compare+capture → cmpxchg + select(success, E, old) + store
+  // Integer equality weak prefix compare+capture → cmpxchg + store old value
   // CHECK: %[[RES:.*]] = cmpxchg weak ptr %[[X]], i32 %[[E]], i32 %[[D]] monotonic monotonic
   // CHECK: %[[OLD:.*]] = extractvalue { i32, i1 } %[[RES]], 0
-  // CHECK: %[[SUCCESS:.*]] = extractvalue { i32, i1 } %[[RES]], 1
-  // CHECK: %[[CAPTURED:.*]] = select i1 %[[SUCCESS]], i32 %[[E]], i32 %[[OLD]]
-  // CHECK: store i32 %[[CAPTURED]], ptr %[[V]]
+  // 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 {

>From 101b9fa02882686a21d346ef12e541c717ca605b Mon Sep 17 00:00:00 2001
From: Sunil Kuravinakop <kuravina at pe31.hpc.amslabs.hpecorp.net>
Date: Tue, 30 Jun 2026 14:00:46 -0500
Subject: [PATCH 7/7] Handling real and logical types.

---
 flang/lib/Lower/OpenMP/Atomic.cpp             | 117 ++++++--
 .../Integration/OpenMP/atomic-compare.f90     |  58 ++++
 flang/test/Lower/OpenMP/atomic-compare.f90    |  83 +++++-
 flang/test/Parser/OpenMP/atomic-unparse.f90   |  12 +
 .../Interfaces/AtomicInterfaces.td            |  13 +
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      | 252 +++++++++++-------
 mlir/test/Dialect/OpenMP/ops.mlir             |  24 ++
 mlir/test/Target/LLVMIR/openmp-llvm.mlir      |  48 ++++
 8 files changed, 470 insertions(+), 137 deletions(-)

diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp
index 209871abd52b6..3811ea3d40bbd 100644
--- a/flang/lib/Lower/OpenMP/Atomic.cpp
+++ b/flang/lib/Lower/OpenMP/Atomic.cpp
@@ -375,6 +375,8 @@ genAtomicRead(lower::AbstractConverter &converter,
         fir::getBase(converter.genExprValue(assign.rhs, stmtCtx, &loc));
     converter.resetExprOverrides();
 
+    if (value.getType() != storeType)
+      value = builder.createConvert(loc, storeType, value);
     fir::StoreOp::create(builder, loc, value, storeAddr);
   }
   return op;
@@ -634,6 +636,45 @@ void Fortran::lower::omp::lowerAtomic(
           },
           rel->u);
     }
+
+    // Fortran uses .eqv./.neqv. for logical equality/inequality, which are
+    // LogicalOperation expressions rather than Relational expressions.
+    if (!expectedExprStorage) {
+      if (const auto *someLogical =
+              evaluate::UnwrapExpr<evaluate::Expr<evaluate::SomeLogical>>(
+                  *cond)) {
+        common::visit(
+            [&](const auto &kindLogical) {
+              using LogicalExpr = std::decay_t<decltype(kindLogical)>;
+              constexpr int K = LogicalExpr::Result::kind;
+              if (const auto *logOp =
+                      std::get_if<evaluate::LogicalOperation<K>>(
+                          &kindLogical.u)) {
+                if (logOp->logicalOperator == common::LogicalOperator::Eqv ||
+                    logOp->logicalOperator == common::LogicalOperator::Neqv) {
+                  relOpr =
+                      (logOp->logicalOperator == common::LogicalOperator::Eqv)
+                          ? common::RelationalOperator::EQ
+                          : common::RelationalOperator::NE;
+                  using Operand =
+                      typename evaluate::LogicalOperation<K>::Operand;
+                  auto leftExpr = evaluate::AsGenericExpr(
+                      evaluate::Expr<Operand>{logOp->left()});
+                  auto rightExpr = evaluate::AsGenericExpr(
+                      evaluate::Expr<Operand>{logOp->right()});
+                  if (evaluate::IsSameOrConvertOf(rightExpr, atom)) {
+                    expectedExprStorage = std::move(leftExpr);
+                    relOpr = reverseRelOp(relOpr);
+                  } else {
+                    expectedExprStorage = std::move(rightExpr);
+                  }
+                }
+              }
+            },
+            someLogical->u);
+      }
+    }
+
     if (!expectedExprStorage) {
       mlir::emitError(loc, "internal error: atomic compare condition is not a "
                            "recognized relational expression");
@@ -647,6 +688,20 @@ void Fortran::lower::omp::lowerAtomic(
       expectedVal = builder.createConvert(loc, elemTypeOfX, expectedVal);
     }
 
+    // For logical types, convert address and expected value to integer
+    // type here (above the atomic compare region) so that the region
+    // only contains arith.cmpi eq on integers. If done inside the region,
+    // fir.convert logical<4> -> i32 would lower to `icmp ne %val, 0`
+    // which violates the atomic compare verifier's expectations.
+    if (mlir::isa<fir::LogicalType>(elemTypeOfX)) {
+      unsigned kind = mlir::cast<fir::LogicalType>(elemTypeOfX).getFKind();
+      mlir::Type intTy = builder.getIntegerType(kind * 8);
+      mlir::Type intRefTy = builder.getRefType(intTy);
+      atomAddr = builder.createConvert(loc, intRefTy, atomAddr);
+      expectedVal = builder.createConvert(loc, intTy, expectedVal);
+      elemTypeOfX = intTy;
+    }
+
     // 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
@@ -694,6 +749,39 @@ void Fortran::lower::omp::lowerAtomic(
         })) {
       weakAttr = builder.getUnitAttr();
     }
+
+    // Extract write assignment (x = d) and generate desired value (d)
+    // before creating the compare region, so that d is defined outside
+    // the region and any intermediate conversions (e.g., logical-to-integer
+    // truthiness normalization) don't appear inside the atomic compare block.
+    [[maybe_unused]] int writeActionCond = 0;
+    const evaluate::Assignment *writeAssign = nullptr;
+    if (analysis.op0.what & analysis.Write) {
+      writeAssign = get(analysis.op0.assign);
+      writeActionCond = analysis.op0.what;
+    }
+    if (!writeAssign && (analysis.op1.what & analysis.Write)) {
+      writeAssign = get(analysis.op1.assign);
+      writeActionCond = analysis.op1.what;
+    }
+    if (!writeAssign) {
+      mlir::emitError(loc,
+                      "internal error: atomic compare has no write assignment");
+      return;
+    }
+    assert((writeActionCond & analysis.IfTrue) &&
+           "atomic compare write should be conditioned on IfTrue");
+
+    // Generate desiredVal before the capture/compare region so any
+    // intermediate ops (loads, conversions) don't pollute the atomic blocks.
+    fir::FirOpBuilder::InsertPoint savedIP = builder.saveInsertionPoint();
+    builder.restoreInsertionPoint(preAt);
+    mlir::Value desiredVal =
+        fir::getBase(converter.genExprValue(writeAssign->rhs, stmtCtx, &loc));
+    if (desiredVal.getType() != elemTypeOfX)
+      desiredVal = builder.createConvert(loc, elemTypeOfX, desiredVal);
+    builder.restoreInsertionPoint(savedIP);
+
     mlir::Operation *atomicOp = mlir::omp::AtomicCompareOp::create(
         builder, loc, atomAddr, weakAttr, hint,
         makeMemOrderAttr(converter, memOrder));
@@ -721,35 +809,6 @@ void Fortran::lower::omp::lowerAtomic(
       return;
     }
 
-    // Check for presence of Assignment (x = d) and wether it is being invoked
-    // only for IfTrue condition.
-
-    // writeActionCond is a bitmask combining the following flags:
-    //  1) the action type (Read/Write/Update)
-    //  2) condition (IfTrue/IfFalse)
-    [[maybe_unused]] int writeActionCond = 0;
-    const evaluate::Assignment *writeAssign = nullptr;
-    if (analysis.op0.what & analysis.Write) {
-      writeAssign = get(analysis.op0.assign);
-      writeActionCond = analysis.op0.what;
-    }
-    if (!writeAssign && (analysis.op1.what & analysis.Write)) {
-      writeAssign = get(analysis.op1.assign);
-      writeActionCond = analysis.op1.what;
-    }
-    if (!writeAssign) {
-      mlir::emitError(loc,
-                      "internal error: atomic compare has no write assignment");
-      return;
-    }
-    assert((writeActionCond & analysis.IfTrue) &&
-           "atomic compare write should be conditioned on IfTrue");
-
-    // Generate new/desired value of x e.g. x = d
-    mlir::Value desiredVal =
-        fir::getBase(converter.genExprValue(writeAssign->rhs, stmtCtx, &loc));
-    if (desiredVal.getType() != elemTypeOfX)
-      desiredVal = builder.createConvert(loc, elemTypeOfX, desiredVal);
     mlir::Value newVal = mlir::arith::SelectOp::create(builder, loc, cmpResult,
                                                        desiredVal, blockArg);
 
diff --git a/flang/test/Integration/OpenMP/atomic-compare.f90 b/flang/test/Integration/OpenMP/atomic-compare.f90
index 4ae1b9a1f1e52..f30349c42e33e 100644
--- a/flang/test/Integration/OpenMP/atomic-compare.f90
+++ b/flang/test/Integration/OpenMP/atomic-compare.f90
@@ -336,3 +336,61 @@ subroutine atomic_compare_capture_fail_only(x, e, d, v)
   !$omp end atomic
 end
 
+! Real equality compare+capture (postfix): if(x==e) x=d; v=x
+! v captures new value of x (d if success, old if fail)
+!CHECK-LABEL: define void @atomic_compare_capture_real_(
+!CHECK-SAME: ptr noalias %[[X:.*]], ptr noalias %[[E:.*]], ptr noalias %[[D:.*]], ptr noalias %[[V:.*]])
+!CHECK: %[[EVAL:.*]] = load float, ptr %[[E]]{{.*}}
+!CHECK: %[[DVAL:.*]] = load float, ptr %[[D]]{{.*}}
+!CHECK: %[[EBITS:.*]] = bitcast float %[[EVAL]] to i32
+!CHECK: %[[DBITS:.*]] = bitcast float %[[DVAL]] to i32
+!CHECK: %[[XLOAD:.*]] = load atomic i32, ptr %[[X]] monotonic{{.*}}
+!CHECK: %[[XFP:.*]] = bitcast i32 %[[XLOAD]] to float
+! Part 1: NaN check - if either x or e is NaN, comparison fails
+!CHECK: %[[E_ISNAN:.*]] = fcmp uno float %[[EVAL]], %[[EVAL]]
+!CHECK: %[[X_ISNAN:.*]] = fcmp uno float %[[XFP]], %[[XFP]]
+!CHECK: %[[EITHER_NAN:.*]] = or i1 %[[E_ISNAN]], %[[X_ISNAN]]
+!CHECK: br i1 %[[EITHER_NAN]], label %[[NAN_BB:.*]], label %[[NOTNAN_BB:.*]]
+!CHECK: [[NAN_BB]]:
+!CHECK: br label %[[EXIT_BB:.*]]
+! Part 2: Both-zero check - handles +0.0 vs -0.0 (same value, different bits)
+!CHECK: [[NOTNAN_BB]]:
+!CHECK: %[[XISZERO:.*]] = fcmp oeq float %[[XFP]], 0.000000e+00
+!CHECK: %[[EISZERO:.*]] = fcmp oeq float %[[EVAL]], 0.000000e+00
+!CHECK: %[[BOTHZERO:.*]] = and i1 %[[XISZERO]], %[[EISZERO]]
+!CHECK: br i1 %[[BOTHZERO]], label %[[ZERO_BB:.*]], label %[[NORMAL_BB:.*]]
+!CHECK: [[ZERO_BB]]:
+!CHECK: %[[ZERORES:.*]] = cmpxchg ptr %[[X]], i32 %[[XLOAD]], i32 %[[DBITS]] monotonic monotonic{{.*}}
+!CHECK: br label %[[EXIT_BB]]
+! Part 3: Normal compare - standard cmpxchg with bitcasted expected value
+!CHECK: [[NORMAL_BB]]:
+!CHECK: %[[NORMRES:.*]] = cmpxchg ptr %[[X]], i32 %[[EBITS]], i32 %[[DBITS]] monotonic monotonic{{.*}}
+!CHECK: br label %[[EXIT_BB]]
+! Exit: select v = (success ? d : old_x)
+!CHECK: [[EXIT_BB]]:
+!CHECK: %[[OLD:.*]] = phi i32 {{.*}}
+!CHECK: %[[OK:.*]] = phi i1 {{.*}}
+!CHECK: %[[OLDFP:.*]] = bitcast i32 %[[OLD]] to float
+!CHECK: %[[VVAL:.*]] = select i1 %[[OK]], float %[[DVAL]], float %[[OLDFP]]
+!CHECK: store float %[[VVAL]], ptr %[[V]]{{.*}}
+subroutine atomic_compare_capture_real(x, e, d, v)
+  real :: x, e, d, v
+  !$omp atomic compare capture
+    if (x == e) x = d
+    v = x
+  !$omp end atomic
+end
+
+! Logical .eqv. compare+capture (postfix): if(x.eqv.e) x=d; v=x
+! Logicals are compared as integers after truthiness normalization
+!CHECK-LABEL: define void @atomic_compare_capture_logical_(
+!CHECK-SAME: ptr noalias %[[X:.*]], ptr noalias %[[E:.*]], ptr noalias %[[D:.*]], ptr noalias %[[V:.*]])
+!CHECK: cmpxchg ptr %[[X]], i32 %{{.*}}, i32 %{{.*}} monotonic monotonic{{.*}}
+subroutine atomic_compare_capture_logical(x, e, d, v)
+  logical :: x, e, d, v
+  !$omp atomic compare capture
+    if (x .eqv. e) x = d
+    v = x
+  !$omp end atomic
+end
+
diff --git a/flang/test/Lower/OpenMP/atomic-compare.f90 b/flang/test/Lower/OpenMP/atomic-compare.f90
index c770605954539..1219d05296e4d 100644
--- a/flang/test/Lower/OpenMP/atomic-compare.f90
+++ b/flang/test/Lower/OpenMP/atomic-compare.f90
@@ -10,10 +10,10 @@
 ! CHECK:         %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
 ! CHECK:         %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
 ! CHECK:         %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
+! CHECK:         %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<i32>
 ! CHECK:         omp.atomic.compare memory_order(relaxed) %[[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:         }
@@ -31,10 +31,10 @@ subroutine atomic_compare_int_eq(x, e, d)
 ! CHECK:         %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
 ! CHECK:         %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
 ! CHECK:         %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<f32>
+! CHECK:         %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<f32>
 ! CHECK:         omp.atomic.compare memory_order(relaxed) %[[X_DECL]]#0 : !fir.ref<f32> {
 ! CHECK:         ^bb0(%[[XVAL:.*]]: f32):
 ! CHECK:           %[[CMP:.*]] = arith.cmpf oeq, %[[XVAL]], %[[EVAL]] fastmath<contract> : f32
-! CHECK:           %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<f32>
 ! CHECK:           %[[SEL:.*]] = arith.select %[[CMP]], %[[DVAL]], %[[XVAL]] : f32
 ! CHECK:           omp.yield(%[[SEL]] : f32)
 ! CHECK:         }
@@ -52,10 +52,10 @@ subroutine atomic_compare_float_eq(x, e, d)
 ! CHECK:         %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
 ! CHECK:         %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
 ! CHECK:         %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<complex<f32>>
+! CHECK:         %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<complex<f32>>
 ! CHECK:         omp.atomic.compare memory_order(relaxed) %[[X_DECL]]#0 : !fir.ref<complex<f32>> {
 ! CHECK:         ^bb0(%[[XVAL:.*]]: complex<f32>):
 ! CHECK:           %[[CMP:.*]] = fir.cmpc "oeq", %[[XVAL]], %[[EVAL]] {fastmath = #arith.fastmath<contract>} : complex<f32>
-! CHECK:           %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<complex<f32>>
 ! CHECK:           %[[SEL:.*]] = arith.select %[[CMP]], %[[DVAL]], %[[XVAL]] : complex<f32>
 ! CHECK:           omp.yield(%[[SEL]] : complex<f32>)
 ! CHECK:         }
@@ -71,10 +71,10 @@ subroutine atomic_compare_complex_eq(x, e, d)
 ! CHECK:         %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
 ! CHECK:         %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
 ! CHECK:         %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
+! CHECK:         %[[EVAL2:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
 ! CHECK:         omp.atomic.compare memory_order(relaxed) %[[X_DECL]]#0 : !fir.ref<i32> {
 ! CHECK:         ^bb0(%[[XVAL:.*]]: i32):
 ! CHECK:           %[[CMP:.*]] = arith.cmpi slt, %[[XVAL]], %[[EVAL]] : i32
-! CHECK:           %[[EVAL2:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
 ! CHECK:           %[[SEL:.*]] = arith.select %[[CMP]], %[[EVAL2]], %[[XVAL]] : i32
 ! CHECK:           omp.yield(%[[SEL]] : i32)
 ! CHECK:         }
@@ -90,10 +90,10 @@ subroutine atomic_compare_int_lt(x, e)
 ! CHECK:         %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
 ! CHECK:         %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
 ! CHECK:         %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
+! CHECK:         %[[EVAL2:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
 ! CHECK:         omp.atomic.compare memory_order(relaxed) %[[X_DECL]]#0 : !fir.ref<i32> {
 ! CHECK:         ^bb0(%[[XVAL:.*]]: i32):
 ! CHECK:           %[[CMP:.*]] = arith.cmpi sgt, %[[XVAL]], %[[EVAL]] : i32
-! CHECK:           %[[EVAL2:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
 ! CHECK:           %[[SEL:.*]] = arith.select %[[CMP]], %[[EVAL2]], %[[XVAL]] : i32
 ! CHECK:           omp.yield(%[[SEL]] : i32)
 ! CHECK:         }
@@ -109,10 +109,10 @@ subroutine atomic_compare_int_gt(x, e)
 ! CHECK:         %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
 ! CHECK:         %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
 ! CHECK:         %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<f32>
+! CHECK:         %[[EVAL2:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<f32>
 ! CHECK:         omp.atomic.compare memory_order(relaxed) %[[X_DECL]]#0 : !fir.ref<f32> {
 ! CHECK:         ^bb0(%[[XVAL:.*]]: f32):
 ! CHECK:           %[[CMP:.*]] = arith.cmpf olt, %[[XVAL]], %[[EVAL]] fastmath<contract> : f32
-! CHECK:           %[[EVAL2:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<f32>
 ! CHECK:           %[[SEL:.*]] = arith.select %[[CMP]], %[[EVAL2]], %[[XVAL]] : f32
 ! CHECK:           omp.yield(%[[SEL]] : f32)
 ! CHECK:         }
@@ -128,10 +128,10 @@ subroutine atomic_compare_float_lt(x, e)
 ! CHECK:         %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
 ! CHECK:         %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
 ! CHECK:         %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<f32>
+! CHECK:         %[[EVAL2:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<f32>
 ! CHECK:         omp.atomic.compare memory_order(relaxed) %[[X_DECL]]#0 : !fir.ref<f32> {
 ! CHECK:         ^bb0(%[[XVAL:.*]]: f32):
 ! CHECK:           %[[CMP:.*]] = arith.cmpf ogt, %[[XVAL]], %[[EVAL]] fastmath<contract> : f32
-! CHECK:           %[[EVAL2:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<f32>
 ! CHECK:           %[[SEL:.*]] = arith.select %[[CMP]], %[[EVAL2]], %[[XVAL]] : f32
 ! CHECK:           omp.yield(%[[SEL]] : f32)
 ! CHECK:         }
@@ -149,10 +149,10 @@ subroutine atomic_compare_float_gt(x, e)
 ! CHECK:         %[[E_DECL:.*]]:2 = hlfir.declare %[[E]] {{.*}}
 ! CHECK:         %[[X_DECL:.*]]:2 = hlfir.declare %[[X]] {{.*}}
 ! CHECK:         %[[EVAL:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<i32>
+! CHECK:         %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<i32>
 ! CHECK:         omp.atomic.compare memory_order(relaxed) %[[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:         } {{.*}}weak{{.*}}
@@ -172,12 +172,12 @@ subroutine atomic_compare_int_eq_weak(x, e, d)
 ! 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:         %[[DVAL:.*]] = fir.load %[[D_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:           }
@@ -200,12 +200,12 @@ subroutine atomic_compare_capture_int_eq(x, e, d, v)
 ! 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:         %[[DVAL:.*]] = fir.load %[[D_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{{.*}}
@@ -228,11 +228,11 @@ subroutine atomic_compare_capture_int_gt(x, e, d, v)
 ! 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:         %[[DVAL:.*]] = fir.load %[[D_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:           }
@@ -256,11 +256,11 @@ subroutine atomic_compare_capture_postfix(x, e, d, v)
 ! 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:         %[[DVAL:.*]] = fir.load %[[D_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:           }
@@ -276,3 +276,62 @@ subroutine atomic_compare_capture_fail_only(x, e, d, v)
     end if
   !$omp end atomic
 end
+
+! CHECK-LABEL: func.func @_QPatomic_compare_capture_real(
+! CHECK-SAME:    %[[X:.*]]: !fir.ref<f32> {fir.bindc_name = "x"},
+! CHECK-SAME:    %[[E:.*]]: !fir.ref<f32> {fir.bindc_name = "e"},
+! CHECK-SAME:    %[[D:.*]]: !fir.ref<f32> {fir.bindc_name = "d"},
+! CHECK-SAME:    %[[V:.*]]: !fir.ref<f32> {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<f32>
+! CHECK:         %[[DVAL:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<f32>
+! CHECK:         omp.atomic.capture memory_order(relaxed) {
+! CHECK:           omp.atomic.compare %[[X_DECL]]#0 : !fir.ref<f32> {
+! CHECK:           ^bb0(%[[XVAL:.*]]: f32):
+! CHECK:             %[[CMP:.*]] = arith.cmpf oeq, %[[XVAL]], %[[EVAL]] fastmath<contract> : f32
+! CHECK:             %[[SEL:.*]] = arith.select %[[CMP]], %[[DVAL]], %[[XVAL]] : f32
+! CHECK:             omp.yield(%[[SEL]] : f32)
+! CHECK:           }
+! CHECK:           omp.atomic.read %[[V_DECL]]#0 = %[[X_DECL]]#0 : !fir.ref<f32>, !fir.ref<f32>, f32
+! CHECK:         }
+subroutine atomic_compare_capture_real(x, e, d, v)
+  real :: x, e, d, v
+  !$omp atomic compare capture
+    if (x == e) x = d
+    v = x
+  !$omp end atomic
+end
+
+! CHECK-LABEL: func.func @_QPatomic_compare_capture_logical(
+! CHECK-SAME:    %[[X:.*]]: !fir.ref<!fir.logical<4>> {fir.bindc_name = "x"},
+! CHECK-SAME:    %[[E:.*]]: !fir.ref<!fir.logical<4>> {fir.bindc_name = "e"},
+! CHECK-SAME:    %[[D:.*]]: !fir.ref<!fir.logical<4>> {fir.bindc_name = "d"},
+! CHECK-SAME:    %[[V:.*]]: !fir.ref<!fir.logical<4>> {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:         %[[ELOAD:.*]] = fir.load %[[E_DECL]]#0 : !fir.ref<!fir.logical<4>>
+! CHECK:         %[[XCONV:.*]] = fir.convert %[[X_DECL]]#0 : (!fir.ref<!fir.logical<4>>) -> !fir.ref<i32>
+! CHECK:         %[[ECONV:.*]] = fir.convert %[[ELOAD]] : (!fir.logical<4>) -> i32
+! CHECK:         %[[DLOAD:.*]] = fir.load %[[D_DECL]]#0 : !fir.ref<!fir.logical<4>>
+! CHECK:         %[[DCONV:.*]] = fir.convert %[[DLOAD]] : (!fir.logical<4>) -> i32
+! CHECK:         omp.atomic.capture memory_order(relaxed) {
+! CHECK:           omp.atomic.compare %[[XCONV]] : !fir.ref<i32> {
+! CHECK:           ^bb0(%[[XVAL:.*]]: i32):
+! CHECK:             %[[CMP:.*]] = arith.cmpi eq, %[[XVAL]], %[[ECONV]] : i32
+! CHECK:             %[[SEL:.*]] = arith.select %[[CMP]], %[[DCONV]], %[[XVAL]] : i32
+! CHECK:             omp.yield(%[[SEL]] : i32)
+! CHECK:           }
+! CHECK:           omp.atomic.read %{{.*}} = %[[XCONV]] : !fir.ref<i32>, !fir.ref<i32>, i32
+! CHECK:         }
+subroutine atomic_compare_capture_logical(x, e, d, v)
+  logical :: x, e, d, v
+  !$omp atomic compare capture
+    if (x .eqv. e) x = d
+    v = x
+  !$omp end atomic
+end
diff --git a/flang/test/Parser/OpenMP/atomic-unparse.f90 b/flang/test/Parser/OpenMP/atomic-unparse.f90
index 521c1bb2811e2..0d30cd48035ea 100644
--- a/flang/test/Parser/OpenMP/atomic-unparse.f90
+++ b/flang/test/Parser/OpenMP/atomic-unparse.f90
@@ -4,6 +4,7 @@ program main
    implicit none
    integer :: i, j = 10
    integer :: k
+   logical :: l1, l2, l3, l4
 !READ
 !$omp atomic read
    i = j
@@ -225,6 +226,12 @@ program main
    end if
 !$omp end atomic
 
+!COMPARE CAPTURE LOGICAL
+!$omp atomic compare capture
+   if (l1 .eqv. l2) l1 = l3
+   l4 = l1
+!$omp end atomic
+
 !ATOMIC
 !$omp atomic
    i = j
@@ -342,6 +349,11 @@ end program main
 !CHECK: !$OMP ATOMIC COMPARE CAPTURE
 !CHECK: !$OMP END ATOMIC
 
+!COMPARE CAPTURE LOGICAL
+
+!CHECK: !$OMP ATOMIC COMPARE CAPTURE
+!CHECK: !$OMP END ATOMIC
+
 !ATOMIC
 !CHECK: !$OMP ATOMIC
 !CHECK: !$OMP ATOMIC SEQ_CST
diff --git a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
index 5b1527bcb84ec..a61c43e2fd9cd 100644
--- a/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
+++ b/mlir/include/mlir/Dialect/OpenACCMPCommon/Interfaces/AtomicInterfaces.td
@@ -488,6 +488,19 @@ def AtomicCompareOpInterface : OpInterface<"AtomicCompareOpInterface"> {
               }
             }
             break;
+          } else if (llvm::is_contained(
+                         {"arith.maxsi", "arith.minsi", "arith.maxui",
+                          "arith.minui", "arith.maximumf", "arith.minimumf",
+                          "llvm.intr.smax", "llvm.intr.smin",
+                          "llvm.intr.umax", "llvm.intr.umin",
+                          "llvm.intr.maxnum", "llvm.intr.minnum"},
+                         opName)) {
+            // The canonicalizer may fold select(cmpi slt, e, x) into
+            // maxsi/minsi (or the float equivalents). After fir-to-llvm-ir
+            // these become llvm.intr.smax/smin etc. All are valid
+            // representations of atomic compare min/max operations.
+            foundComparison = true;
+            break;
           }
         }
 
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index e1fb08d780753..8e49571cd2461 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 #include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
 #include "mlir/Analysis/TopologicalSortUtils.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
 #include "mlir/Dialect/LLVMIR/LLVMTypes.h"
 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
@@ -5369,6 +5370,102 @@ convertFCmpPredicateToAtomicCompareOp(LLVM::FCmpPredicate predicate) {
   }
 }
 
+/// Holds the extracted comparison pattern information from an atomic compare
+/// region.
+struct AtomicComparePatternInfo {
+  llvm::omp::OMPAtomicCompareOp compareOp = llvm::omp::OMPAtomicCompareOp::EQ;
+  llvm::Value *eVal = nullptr;
+  llvm::Value *dVal = nullptr;
+  bool isXBinopExpr = false;
+  bool isSigned = false;
+};
+
+/// Extract comparison predicate, expected value (e), desired value (d), and
+/// related flags from an atomic compare region block by scanning for
+/// icmp/fcmp/select/min/max operations.
+static LogicalResult extractAtomicComparePattern(
+    Block &block,
+    llvm::function_ref<llvm::Value *(mlir::Value)> materializeValue,
+    omp::AtomicCompareOp atomicCompareOp, AtomicComparePatternInfo &info) {
+  for (Operation &op : block.getOperations()) {
+    // Pre-filter: skip icmps that don't involve the block argument
+    // (e.g., truthiness extractions from logical-to-integer conversion).
+    if (auto icmpOp = dyn_cast<LLVM::ICmpOp>(op);
+        icmpOp && icmpOp.getOperand(0) != block.getArgument(0) &&
+        icmpOp.getOperand(1) != block.getArgument(0))
+      continue;
+
+    LogicalResult result =
+        llvm::TypeSwitch<Operation *, LogicalResult>(&op)
+            .Case<LLVM::ICmpOp>([&](LLVM::ICmpOp icmpOp) -> LogicalResult {
+              auto maybeOp =
+                  convertICmpPredicateToAtomicCompareOp(icmpOp.getPredicate());
+              if (!maybeOp)
+                return atomicCompareOp.emitError(
+                    "unsupported comparison predicate in atomic compare");
+              info.compareOp = *maybeOp;
+              LLVM::ICmpPredicate pred = icmpOp.getPredicate();
+              info.isSigned = (pred == LLVM::ICmpPredicate::slt ||
+                               pred == LLVM::ICmpPredicate::sgt ||
+                               pred == LLVM::ICmpPredicate::sle ||
+                               pred == LLVM::ICmpPredicate::sge);
+              info.isXBinopExpr =
+                  (icmpOp.getOperand(0) == block.getArgument(0));
+              mlir::Value eOperand = info.isXBinopExpr ? icmpOp.getOperand(1)
+                                                       : icmpOp.getOperand(0);
+              info.eVal = materializeValue(eOperand);
+              return success();
+            })
+            .Case<LLVM::FCmpOp>([&](LLVM::FCmpOp fcmpOp) -> LogicalResult {
+              auto maybeOp =
+                  convertFCmpPredicateToAtomicCompareOp(fcmpOp.getPredicate());
+              if (!maybeOp)
+                return atomicCompareOp.emitError(
+                    "unsupported comparison predicate in atomic compare");
+              info.compareOp = *maybeOp;
+              info.isXBinopExpr =
+                  (fcmpOp.getOperand(0) == block.getArgument(0));
+              mlir::Value eOperand = info.isXBinopExpr ? fcmpOp.getOperand(1)
+                                                       : fcmpOp.getOperand(0);
+              info.eVal = materializeValue(eOperand);
+              return success();
+            })
+            .Case<LLVM::SelectOp>([&](LLVM::SelectOp selectOp) {
+              if (!info.dVal)
+                info.dVal = materializeValue(selectOp.getTrueValue());
+              return success();
+            })
+            .Case<mlir::arith::MaxSIOp, mlir::arith::MinSIOp,
+                  mlir::arith::MaxUIOp, mlir::arith::MinUIOp,
+                  mlir::arith::MaximumFOp, mlir::arith::MinimumFOp,
+                  LLVM::SMaxOp, LLVM::SMinOp, LLVM::UMaxOp, LLVM::UMinOp,
+                  LLVM::MaxNumOp, LLVM::MinNumOp>([&](Operation *) {
+              // Canonicalized min/max ops (arith or LLVM intrinsic form).
+              // max(x,e) came from slt/ult/olt -> OMPAtomicCompareOp::MIN
+              // min(x,e) came from sgt/ugt/ogt -> OMPAtomicCompareOp::MAX
+              // (OMPIRBuilder inverts: MIN->atomicrmw max, MAX->atomicrmw min)
+              bool isMax = isa<mlir::arith::MaxSIOp, mlir::arith::MaxUIOp,
+                               mlir::arith::MaximumFOp, LLVM::SMaxOp,
+                               LLVM::UMaxOp, LLVM::MaxNumOp>(op);
+              info.compareOp = isMax ? llvm::omp::OMPAtomicCompareOp::MIN
+                                     : llvm::omp::OMPAtomicCompareOp::MAX;
+              info.isSigned = isa<mlir::arith::MaxSIOp, mlir::arith::MinSIOp,
+                                  LLVM::SMaxOp, LLVM::SMinOp>(op);
+              info.isXBinopExpr = (op.getOperand(0) == block.getArgument(0));
+              mlir::Value eOperand =
+                  info.isXBinopExpr ? op.getOperand(1) : op.getOperand(0);
+              info.eVal = materializeValue(eOperand);
+              info.dVal = info.eVal;
+              return success();
+            })
+            .Default([](Operation *) { return success(); });
+
+    if (failed(result))
+      return result;
+  }
+  return success();
+}
+
 static LogicalResult
 convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
                         llvm::IRBuilderBase &builder,
@@ -5409,7 +5506,11 @@ convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
     // 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);
+                       LLVM::OrOp, LLVM::SMaxOp, LLVM::SMinOp, LLVM::UMaxOp,
+                       LLVM::UMinOp, LLVM::MaxNumOp, LLVM::MinNumOp,
+                       mlir::arith::MaxSIOp, mlir::arith::MinSIOp,
+                       mlir::arith::MaxUIOp, mlir::arith::MinUIOp,
+                       mlir::arith::MaximumFOp, mlir::arith::MinimumFOp>(op);
     };
     for (Operation &op : block.without_terminator()) {
       if (isAtomicComparePatternOp(op))
@@ -5443,47 +5544,16 @@ convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
     };
 
     // 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;
+    AtomicComparePatternInfo patternInfo;
+    if (failed(extractAtomicComparePattern(block, materializeValue,
+                                           atomicCompareOp, patternInfo)))
+      return failure();
 
-    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());
-      }
-    }
+    llvm::omp::OMPAtomicCompareOp compareOp = patternInfo.compareOp;
+    llvm::Value *eVal = patternInfo.eVal;
+    llvm::Value *dVal = patternInfo.dVal;
+    bool isXBinopExpr = patternInfo.isXBinopExpr;
+    isSigned = patternInfo.isSigned;
 
     if (!eVal)
       return atomicCompareOp.emitError(
@@ -5529,17 +5599,42 @@ convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
     // Postfix: v = select(success, D, old) — captures new value of x.
     if (isPostfixCapture && !isFailOnly) {
       llvm::BasicBlock *curBB = builder.GetInsertBlock();
-      llvm::Instruction *cmpxchgInst = nullptr;
+      llvm::Value *oldVal = nullptr;
+      llvm::Value *successVal = nullptr;
+
+      // Integer path (and non-HandleFPNegZero FP path): a single cmpxchg
+      // lives in the current block.
       for (auto &inst : llvm::reverse(*curBB)) {
         if (isa<llvm::AtomicCmpXchgInst>(&inst)) {
-          cmpxchgInst = &inst;
+          oldVal = builder.CreateExtractValue(&inst, /*Idxs=*/0);
+          successVal = builder.CreateExtractValue(&inst, /*Idxs=*/1);
           break;
         }
       }
-      assert(cmpxchgInst && "expected cmpxchg instruction");
-      llvm::Value *oldVal = builder.CreateExtractValue(cmpxchgInst, /*Idxs=*/0);
-      llvm::Value *successVal =
-          builder.CreateExtractValue(cmpxchgInst, /*Idxs=*/1);
+
+      // FP HandleFPNegZero path: the OMPIRBuilder emits a multi-block
+      // structure (NaN / ±0.0 handling) with cmpxchg in predecessor
+      // blocks. Results are merged via PHI nodes in the current (exit)
+      // block: an i1 PHI for success and a bitcast of an integer PHI
+      // for the old FP value.
+      if (!oldVal) {
+        for (auto &inst : *curBB) {
+          auto *phi = dyn_cast<llvm::PHINode>(&inst);
+          if (!phi)
+            break;
+          if (phi->getType()->isIntegerTy(1))
+            successVal = phi;
+        }
+        for (auto &inst : *curBB) {
+          if (auto *bc = dyn_cast<llvm::BitCastInst>(&inst)) {
+            oldVal = bc;
+            break;
+          }
+        }
+      }
+
+      assert(oldVal && "expected cmpxchg or PHI+bitcast for compare capture");
+      assert(successVal && "expected success flag for compare capture");
       llvm::Value *newVal = builder.CreateSelect(successVal, dVal, oldVal);
       builder.CreateStore(newVal, llvmAtomicV.Var, llvmAtomicV.IsVolatile);
     }
@@ -5686,7 +5781,11 @@ convertOmpAtomicCompare(omp::AtomicCompareOp atomicCompareOp,
 
   auto isAtomicComparePatternOp = [](Operation &op) {
     return llvm::isa<LLVM::ICmpOp, LLVM::FCmpOp, LLVM::SelectOp, LLVM::AndOp,
-                     LLVM::OrOp>(op);
+                     LLVM::OrOp, LLVM::SMaxOp, LLVM::SMinOp, LLVM::UMaxOp,
+                     LLVM::UMinOp, LLVM::MaxNumOp, LLVM::MinNumOp,
+                     mlir::arith::MaxSIOp, mlir::arith::MinSIOp,
+                     mlir::arith::MaxUIOp, mlir::arith::MinUIOp,
+                     mlir::arith::MaximumFOp, mlir::arith::MinimumFOp>(op);
   };
 
   // Pre-translate operations inside the region that compute e and d (e.g.,
@@ -5859,54 +5958,15 @@ convertOmpAtomicCompare(omp::AtomicCompareOp atomicCompareOp,
     }
     return success();
   } else {
-
-    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);
-
-        // Identify which operand is the block argument (x) and which is e.
-        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());
-      }
-    }
-  }
-
-  // For non-complex patterns, also extract dVal from SelectOp.
-  if (!dVal) {
-    for (Operation &op : block.getOperations()) {
-      if (auto selectOp = dyn_cast<LLVM::SelectOp>(op)) {
-        dVal = materializeValue(selectOp.getTrueValue());
-        break;
-      }
-    }
+    AtomicComparePatternInfo patternInfo;
+    if (failed(extractAtomicComparePattern(block, materializeValue,
+                                           atomicCompareOp, patternInfo)))
+      return failure();
+    compareOp = patternInfo.compareOp;
+    eVal = patternInfo.eVal;
+    dVal = patternInfo.dVal;
+    isXBinopExpr = patternInfo.isXBinopExpr;
+    isSigned = patternInfo.isSigned;
   }
 
   if (!eVal)
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 6b13be481e0e0..cc40506b909b1 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -2161,6 +2161,30 @@ func.func @omp_atomic_compare_capture_fail_only(%v: memref<i32>, %x: memref<i32>
   return
 }
 
+// CHECK-LABEL: omp_atomic_compare_capture_real
+// CHECK-SAME: (%[[V:.*]]: memref<f32>, %[[X:.*]]: memref<f32>, %[[E:.*]]: f32, %[[D:.*]]: f32)
+func.func @omp_atomic_compare_capture_real(%v: memref<f32>, %x: memref<f32>, %e: f32, %d: f32) {
+  // CHECK: omp.atomic.capture {
+  // CHECK-NEXT: omp.atomic.compare %[[X]] : memref<f32> {
+  // CHECK-NEXT: ^bb0(%[[XVAL:.*]]: f32):
+  // CHECK-NEXT:   %[[CMP:.*]] = arith.cmpf oeq, %[[XVAL]], %[[E]] : f32
+  // CHECK-NEXT:   %[[SEL:.*]] = arith.select %[[CMP]], %[[D]], %[[XVAL]] : f32
+  // CHECK-NEXT:   omp.yield(%[[SEL]] : f32)
+  // CHECK-NEXT: }
+  // CHECK-NEXT: omp.atomic.read %[[V]] = %[[X]] : memref<f32>, memref<f32>, f32
+  // CHECK-NEXT: }
+  omp.atomic.capture {
+    omp.atomic.compare %x : memref<f32> {
+    ^bb0(%xval: f32):
+      %cmp = arith.cmpf oeq, %xval, %e : f32
+      %sel = arith.select %cmp, %d, %xval : f32
+      omp.yield(%sel : f32)
+    }
+    omp.atomic.read %v = %x : memref<f32>, memref<f32>, f32
+  }
+  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 377bd88894854..b779d20f42d0c 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -2882,6 +2882,54 @@ llvm.func @omp_atomic_compare_capture_fail_only(%x : !llvm.ptr, %v : !llvm.ptr,
 }
 // -----
 
+// CHECK-LABEL: @omp_atomic_compare_capture_real
+// CHECK-SAME: (ptr %[[X:.*]], ptr %[[V:.*]], float %[[E:.*]], float %[[D:.*]])
+llvm.func @omp_atomic_compare_capture_real(%x : !llvm.ptr, %v : !llvm.ptr, %e : f32, %d : f32) {
+  // Real compare+capture: uses HandleFPNegZero multi-block path
+  // CHECK: %[[EBITS:.*]] = bitcast float %[[E]] to i32
+  // CHECK: %[[DBITS:.*]] = bitcast float %[[D]] to i32
+  // CHECK: %[[XI:.*]] = load atomic i32, ptr %[[X]] monotonic{{.*}}
+  // CHECK: %[[XFP:.*]] = bitcast i32 %[[XI]] to float
+  // Part 1: NaN check
+  // CHECK: %[[E_NAN:.*]] = fcmp uno float %[[E]], %[[E]]
+  // CHECK: %[[X_NAN:.*]] = fcmp uno float %[[XFP]], %[[XFP]]
+  // CHECK: %[[EITHER_NAN:.*]] = or i1 %[[E_NAN]], %[[X_NAN]]
+  // CHECK: br i1 %[[EITHER_NAN]], label %[[NAN:.*]], label %[[NOTNAN:.*]]
+  // CHECK: [[NAN]]:
+  // CHECK: br label %[[EXIT:.*]]
+  // Part 2: Both-zero check
+  // CHECK: [[NOTNAN]]:
+  // CHECK: %[[XISZERO:.*]] = fcmp oeq float %[[XFP]], 0.000000e+00
+  // CHECK: %[[EISZERO:.*]] = fcmp oeq float %[[E]], 0.000000e+00
+  // CHECK: %[[BOTHZERO:.*]] = and i1 %[[XISZERO]], %[[EISZERO]]
+  // CHECK: br i1 %[[BOTHZERO]], label %[[ZERO:.*]], label %[[NORMAL:.*]]
+  // CHECK: [[ZERO]]:
+  // CHECK: cmpxchg ptr %[[X]], i32 %[[XI]], i32 %[[DBITS]] monotonic monotonic{{.*}}
+  // CHECK: br label %[[EXIT]]
+  // Part 3: Normal cmpxchg
+  // CHECK: [[NORMAL]]:
+  // CHECK: cmpxchg ptr %[[X]], i32 %[[EBITS]], i32 %[[DBITS]] monotonic monotonic{{.*}}
+  // CHECK: br label %[[EXIT]]
+  // Exit: select v = (success ? d : old_x)
+  // CHECK: [[EXIT]]:
+  // CHECK: %[[OLD:.*]] = phi i32 {{.*}}
+  // CHECK: %[[OK:.*]] = phi i1 {{.*}}
+  // CHECK: %[[OLDFP:.*]] = bitcast i32 %[[OLD]] to float
+  // CHECK: %[[VVAL:.*]] = select i1 %[[OK]], float %[[D]], float %[[OLDFP]]
+  // CHECK: store float %[[VVAL]], ptr %[[V]]{{.*}}
+  omp.atomic.capture {
+    omp.atomic.compare %x : !llvm.ptr {
+    ^bb0(%xval : f32):
+      %cmp = llvm.fcmp "oeq" %xval, %e : f32
+      %sel = llvm.select %cmp, %d, %xval : i1, f32
+      omp.yield(%sel : f32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr, !llvm.ptr, f32
+  }
+  llvm.return
+}
+// -----
+
 // CHECK-LABEL: @omp_sections_empty
 llvm.func @omp_sections_empty() -> () {
   omp.sections {



More information about the flang-commits mailing list