[llvm] 31486a9 - [mlir][OpenMP] Added translation from `omp.atomic.capture` to LLVM IR

Shraiysh Vaishay via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 21 04:09:47 PDT 2022


Author: Shraiysh Vaishay
Date: 2022-03-21T16:39:36+05:30
New Revision: 31486a9fc27a12e2c504861a1c4c3465cbb55856

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

LOG: [mlir][OpenMP] Added translation from `omp.atomic.capture` to LLVM IR

This patch adds translation from `omp.atomic.capture` to LLVM IR. Also
added tests for the same.

Depends on D121546

Reviewed By: ftynse

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

Added: 
    

Modified: 
    llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
    mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
    mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
    mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
    mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
    mlir/test/Target/LLVMIR/openmp-llvm.mlir

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index f0f02e8138836..4626d20d51e8a 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -3591,6 +3591,7 @@ std::pair<Value *, Value *> OpenMPIRBuilder::emitAtomicUpdate(
   case AtomicRMWInst::Nand:
   case AtomicRMWInst::Or:
   case AtomicRMWInst::Xor:
+  case AtomicRMWInst::Xchg:
     emitRMWOp = XElemTy;
     break;
   case AtomicRMWInst::Sub:
@@ -3606,7 +3607,11 @@ std::pair<Value *, Value *> OpenMPIRBuilder::emitAtomicUpdate(
     Res.first = Builder.CreateAtomicRMW(RMWOp, X, Expr, llvm::MaybeAlign(), AO);
     // not needed except in case of postfix captures. Generate anyway for
     // consistency with the else part. Will be removed with any DCE pass.
-    Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
+    // AtomicRMWInst::Xchg does not have a coressponding instruction.
+    if (RMWOp == AtomicRMWInst::Xchg)
+      Res.second = Res.first;
+    else
+      Res.second = emitRMWOpAsInstruction(Res.first, Expr, RMWOp);
   } else {
     unsigned Addrspace = cast<PointerType>(X->getType())->getAddressSpace();
     IntegerType *IntCastTy =

diff  --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 0cf991855cb79..726152f8a7b3f 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -717,6 +717,11 @@ def AtomicUpdateOp : OpenMP_Op<"atomic.update",
   }];
   let hasVerifier = 1;
   let hasRegionVerifier = 1;
+  let extraClassDeclaration = [{
+    Operation* getFirstOp() {
+      return &getRegion().front().getOperations().front();
+    }
+  }];
 }
 
 def AtomicCaptureOp : OpenMP_Op<"atomic.capture",
@@ -764,6 +769,25 @@ def AtomicCaptureOp : OpenMP_Op<"atomic.capture",
     $region attr-dict
   }];
   let hasRegionVerifier = 1;
+  let extraClassDeclaration = [{
+    /// Returns the first operation in atomic capture region
+    Operation* getFirstOp();
+
+    /// Returns the second operation in atomic capture region
+    Operation* getSecondOp();
+
+    /// Returns the `atomic.read` operation inside the region, if any.
+    /// Otherwise, it returns nullptr.
+    AtomicReadOp getAtomicReadOp();
+
+    /// Returns the `atomic.write` operation inside the region, if any.
+    /// Otherwise, it returns nullptr.
+    AtomicWriteOp getAtomicWriteOp();
+
+    /// Returns the `atomic.update` operation inside the region, if any.
+    /// Otherwise, it returns nullptr.
+    AtomicUpdateOp getAtomicUpdateOp();
+  }];
 }
 
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 774b6b30b9456..602b26cc32bdc 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -1149,6 +1149,33 @@ LogicalResult AtomicUpdateOp::verifyRegions() {
 // Verifier for AtomicCaptureOp
 //===----------------------------------------------------------------------===//
 
+Operation *AtomicCaptureOp::getFirstOp() {
+  return &getRegion().front().getOperations().front();
+}
+
+Operation *AtomicCaptureOp::getSecondOp() {
+  auto &ops = getRegion().front().getOperations();
+  return ops.getNextNode(ops.front());
+}
+
+AtomicReadOp AtomicCaptureOp::getAtomicReadOp() {
+  if (auto op = dyn_cast<AtomicReadOp>(getFirstOp()))
+    return op;
+  return dyn_cast<AtomicReadOp>(getSecondOp());
+}
+
+AtomicWriteOp AtomicCaptureOp::getAtomicWriteOp() {
+  if (auto op = dyn_cast<AtomicWriteOp>(getFirstOp()))
+    return op;
+  return dyn_cast<AtomicWriteOp>(getSecondOp());
+}
+
+AtomicUpdateOp AtomicCaptureOp::getAtomicUpdateOp() {
+  if (auto op = dyn_cast<AtomicUpdateOp>(getFirstOp()))
+    return op;
+  return dyn_cast<AtomicUpdateOp>(getSecondOp());
+}
+
 LogicalResult AtomicCaptureOp::verifyRegions() {
   Block::OpListType &ops = region().front().getOperations();
   if (ops.size() != 3)

diff  --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index a835b72913989..b71f5d9173d64 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -1114,6 +1114,108 @@ convertOmpAtomicUpdate(omp::AtomicUpdateOp &opInst,
   return updateGenStatus;
 }
 
+static LogicalResult
+convertOmpAtomicCapture(omp::AtomicCaptureOp atomicCaptureOp,
+                        llvm::IRBuilderBase &builder,
+                        LLVM::ModuleTranslation &moduleTranslation) {
+  llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
+  llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
+  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");
+
+  if (atomicWriteOp) {
+    isPostfixUpdate = true;
+    mlirExpr = atomicWriteOp.value();
+  } else {
+    isPostfixUpdate = atomicCaptureOp.getSecondOp() ==
+                      atomicCaptureOp.getAtomicUpdateOp().getOperation();
+    auto &innerOpList = atomicUpdateOp.region().front().getOperations();
+    if (innerOpList.size() != 2)
+      return atomicUpdateOp.emitError(
+          "exactly two operations are allowed inside an "
+          "atomic update region while lowering to LLVM IR");
+    Operation *innerUpdateOp = atomicUpdateOp.getFirstOp();
+    if (innerUpdateOp->getNumOperands() != 2 ||
+        !llvm::is_contained(innerUpdateOp->getOperands(),
+                            atomicUpdateOp.getRegion().getArgument(0)))
+      return atomicUpdateOp.emitError(
+          "the update operation inside the region must be a binary operation "
+          "and that update operation must have the region argument as an "
+          "operand");
+    binop = convertBinOpToAtomic(*innerUpdateOp);
+
+    isXBinopExpr = innerUpdateOp->getOperand(0) ==
+                   atomicUpdateOp.getRegion().getArgument(0);
+
+    mlirExpr = (isXBinopExpr ? innerUpdateOp->getOperand(1)
+                             : innerUpdateOp->getOperand(0));
+  }
+
+  llvm::Value *llvmExpr = moduleTranslation.lookupValue(mlirExpr);
+  llvm::Value *llvmX =
+      moduleTranslation.lookupValue(atomicCaptureOp.getAtomicReadOp().x());
+  llvm::Value *llvmV =
+      moduleTranslation.lookupValue(atomicCaptureOp.getAtomicReadOp().v());
+  auto mlirXType = atomicCaptureOp.getAtomicReadOp()
+                       .x()
+                       .getType()
+                       .cast<LLVM::LLVMPointerType>();
+  llvm::Type *llvmXElementType =
+      moduleTranslation.convertType(mlirXType.getElementType());
+  llvm::OpenMPIRBuilder::AtomicOpValue llvmAtomicX = {llvmX, llvmXElementType,
+                                                      /*isSigned=*/false,
+                                                      /*isVolatile=*/false};
+  llvm::OpenMPIRBuilder::AtomicOpValue llvmAtomicV = {llvmV, llvmXElementType,
+                                                      /*isSigned=*/false,
+                                                      /*isVolatile=*/false};
+
+  llvm::AtomicOrdering atomicOrdering =
+      convertAtomicOrdering(atomicCaptureOp.memory_order_val());
+
+  LogicalResult updateGenStatus = success();
+  auto updateFn = [&](llvm::Value *atomicx,
+                      llvm::IRBuilder<> &builder) -> llvm::Value * {
+    if (atomicWriteOp)
+      return moduleTranslation.lookupValue(atomicWriteOp.value());
+    Block &bb = *atomicUpdateOp.region().begin();
+    moduleTranslation.mapValue(*atomicUpdateOp.region().args_begin(), atomicx);
+    moduleTranslation.mapBlock(&bb, builder.GetInsertBlock());
+    if (failed(moduleTranslation.convertBlock(bb, true, builder))) {
+      updateGenStatus = (atomicUpdateOp.emitError()
+                         << "unable to convert update operation to llvm IR");
+      return nullptr;
+    }
+    omp::YieldOp yieldop = dyn_cast<omp::YieldOp>(bb.getTerminator());
+    assert(yieldop && yieldop.results().size() == 1 &&
+           "terminator must be omp.yield op and it must have exactly one "
+           "argument");
+    return moduleTranslation.lookupValue(yieldop.results()[0]);
+  };
+  // Handle ambiguous alloca, if any.
+  auto allocaIP = findAllocaInsertPoint(builder, moduleTranslation);
+  llvm::UnreachableInst *unreachableInst;
+  if (allocaIP.getPoint() == ompLoc.IP.getPoint()) {
+    // Same point => split basic block and make them unambigous.
+    unreachableInst = builder.CreateUnreachable();
+    builder.SetInsertPoint(builder.GetInsertBlock()->splitBasicBlock(
+        unreachableInst, "alloca_split"));
+    ompLoc.IP = builder.saveIP();
+    unreachableInst->removeFromParent();
+  }
+  builder.restoreIP(ompBuilder->createAtomicCapture(
+      ompLoc, findAllocaInsertPoint(builder, moduleTranslation), llvmAtomicX,
+      llvmAtomicV, llvmExpr, atomicOrdering, binop, updateFn, atomicUpdateOp,
+      isPostfixUpdate, isXBinopExpr));
+  return updateGenStatus;
+}
+
 /// Converts an OpenMP reduction operation using OpenMPIRBuilder. Expects the
 /// mapping between reduction variables and their private equivalents to have
 /// been stored on the ModuleTranslation stack. Currently only supports
@@ -1247,6 +1349,9 @@ LogicalResult OpenMPDialectLLVMIRTranslationInterface::convertOperation(
       .Case([&](omp::AtomicUpdateOp op) {
         return convertOmpAtomicUpdate(op, builder, moduleTranslation);
       })
+      .Case([&](omp::AtomicCaptureOp op) {
+        return convertOmpAtomicCapture(op, builder, moduleTranslation);
+      })
       .Case([&](omp::SectionsOp) {
         return convertOmpSections(*op, builder, moduleTranslation);
       })

diff  --git a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
index 232df2e699c68..171db04d2e8d8 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm-invalid.mlir
@@ -29,3 +29,41 @@ llvm.func @omp_atomic_update_multiple_step_update(%x: !llvm.ptr<i32>, %expr: i32
   }
   llvm.return
 }
+
+// -----
+
+// Checking translation when the update is carried out by using more than one
+// operations in the atomic capture region.
+llvm.func @omp_atomic_update_multiple_step_update(%x: !llvm.ptr<i32>, %v: !llvm.ptr<i32>, %expr: i32) {
+  // expected-error @+1 {{LLVM Translation failed for operation: omp.atomic.capture}}
+  omp.atomic.capture memory_order(seq_cst) {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    // expected-error @+1 {{the update operation inside the region must be a binary operation and that update operation must have the region argument as an operand}}
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.mul %expr, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+  llvm.return
+}
+
+// -----
+
+// Checking translation when the captured variable is not used in the inner
+// update operation
+llvm.func @omp_atomic_update_multiple_step_update(%x: !llvm.ptr<i32>, %v: !llvm.ptr<i32>, %expr: i32) {
+  // expected-error @+1 {{LLVM Translation failed for operation: omp.atomic.capture}}
+  omp.atomic.capture memory_order(seq_cst) {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    // expected-error @+1 {{exactly two operations are allowed inside an atomic update region while lowering to LLVM IR}}
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %t1 = llvm.mul %xval, %expr : i32
+      %t2 = llvm.sdiv %t1, %expr : i32
+      %newval = llvm.add %xval, %t2 : i32
+      omp.yield(%newval : i32)
+    }
+  }
+  llvm.return
+}

diff  --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
index fe3a34dbb2961..26cdaf8ddf9f0 100644
--- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir
@@ -1063,6 +1063,590 @@ llvm.func @omp_atomic_update_intrinsic(%x:!llvm.ptr<i32>, %expr: i32) {
 
 // -----
 
+// CHECK-LABEL: @omp_atomic_capture_prefix_update
+// CHECK-SAME: (i32* %[[x:.*]], i32* %[[v:.*]], i32 %[[expr:.*]], float* %[[xf:.*]], float* %[[vf:.*]], float %[[exprf:.*]])
+llvm.func @omp_atomic_capture_prefix_update(
+  %x: !llvm.ptr<i32>, %v: !llvm.ptr<i32>, %expr: i32,
+  %xf: !llvm.ptr<f32>, %vf: !llvm.ptr<f32>, %exprf: f32) -> () {
+  // CHECK: %[[res:.*]] = atomicrmw add i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK-NEXT: %[[newval:.*]] = add i32 %[[res]], %[[expr]]
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw sub i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK-NEXT: %[[newval:.*]] = sub i32 %[[res]], %[[expr]]
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.sub %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw and i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK-NEXT: %[[newval:.*]] = and i32 %[[res]], %[[expr]]
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.and %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw or i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK-NEXT: %[[newval:.*]] = or i32 %[[res]], %[[expr]]
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.or %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw xor i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK-NEXT: %[[newval:.*]] = xor i32 %[[res]], %[[expr]]
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.xor %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = mul i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.mul %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = sdiv i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.sdiv %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = udiv i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.udiv %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = shl i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.shl %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = lshr i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.lshr %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = ashr i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.ashr %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = call i32 @llvm.smax.i32(i32 %[[xval]], i32 %[[expr]])
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = "llvm.intr.smax"(%xval, %expr) : (i32, i32) -> i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = call i32 @llvm.smin.i32(i32 %[[xval]], i32 %[[expr]])
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = "llvm.intr.smin"(%xval, %expr) : (i32, i32) -> i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = call i32 @llvm.umax.i32(i32 %[[xval]], i32 %[[expr]])
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = "llvm.intr.umax"(%xval, %expr) : (i32, i32) -> i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = call i32 @llvm.umin.i32(i32 %[[xval]], i32 %[[expr]])
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[newval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = "llvm.intr.umin"(%xval, %expr) : (i32, i32) -> i32
+      omp.yield(%newval : i32)
+    }
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK: %[[newval:.*]] = fadd float %{{.*}}, %[[exprf]]
+  // CHECK: store float %[[newval]], float* %{{.*}}
+  // CHECK: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK: %[[xf_bitcast:.*]] = bitcast float* %[[xf]] to i32*
+  // CHECK: %{{.*}} = cmpxchg i32* %[[xf_bitcast]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store float %[[newval]], float* %[[vf]]
+  omp.atomic.capture {
+    omp.atomic.update %xf : !llvm.ptr<f32> {
+    ^bb0(%xval: f32):
+      %newval = llvm.fadd %xval, %exprf : f32
+      omp.yield(%newval : f32)
+    }
+    omp.atomic.read %vf = %xf : !llvm.ptr<f32>
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK: %[[newval:.*]] = fsub float %{{.*}}, %[[exprf]]
+  // CHECK: store float %[[newval]], float* %{{.*}}
+  // CHECK: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK: %[[xf_bitcast:.*]] = bitcast float* %[[xf]] to i32*
+  // CHECK: %{{.*}} = cmpxchg i32* %[[xf_bitcast]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store float %[[newval]], float* %[[vf]]
+  omp.atomic.capture {
+    omp.atomic.update %xf : !llvm.ptr<f32> {
+    ^bb0(%xval: f32):
+      %newval = llvm.fsub %xval, %exprf : f32
+      omp.yield(%newval : f32)
+    }
+    omp.atomic.read %vf = %xf : !llvm.ptr<f32>
+  }
+
+  llvm.return
+}
+
+// -----
+
+// CHECK-LABEL: @omp_atomic_capture_postfix_update
+// CHECK-SAME: (i32* %[[x:.*]], i32* %[[v:.*]], i32 %[[expr:.*]], float* %[[xf:.*]], float* %[[vf:.*]], float %[[exprf:.*]])
+llvm.func @omp_atomic_capture_postfix_update(
+  %x: !llvm.ptr<i32>, %v: !llvm.ptr<i32>, %expr: i32,
+  %xf: !llvm.ptr<f32>, %vf: !llvm.ptr<f32>, %exprf: f32) -> () {
+  // CHECK: %[[res:.*]] = atomicrmw add i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw sub i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.sub %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw and i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.and %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw or i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.or %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw xor i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.xor %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = mul i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.mul %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = sdiv i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.sdiv %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = udiv i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.udiv %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = shl i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.shl %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = lshr i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.lshr %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = ashr i32 %[[xval]], %[[expr]]
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.ashr %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = call i32 @llvm.smax.i32(i32 %[[xval]], i32 %[[expr]])
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = "llvm.intr.smax"(%xval, %expr) : (i32, i32) -> i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = call i32 @llvm.smin.i32(i32 %[[xval]], i32 %[[expr]])
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = "llvm.intr.smin"(%xval, %expr) : (i32, i32) -> i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = call i32 @llvm.umax.i32(i32 %[[xval]], i32 %[[expr]])
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = "llvm.intr.umax"(%xval, %expr) : (i32, i32) -> i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK-NEXT: %[[newval:.*]] = call i32 @llvm.umin.i32(i32 %[[xval]], i32 %[[expr]])
+  // CHECK-NEXT: store i32 %[[newval]], i32* %{{.*}}
+  // CHECK-NEXT: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK-NEXT: %{{.*}} = cmpxchg i32* %[[x]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = "llvm.intr.umin"(%xval, %expr) : (i32, i32) -> i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK: %[[xvalf:.*]] = bitcast i32 %[[xval]] to float
+  // CHECK: %[[newval:.*]] = fadd float %{{.*}}, %[[exprf]]
+  // CHECK: store float %[[newval]], float* %{{.*}}
+  // CHECK: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK: %[[xf_bitcast:.*]] = bitcast float* %[[xf]] to i32*
+  // CHECK: %{{.*}} = cmpxchg i32* %[[xf_bitcast]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store float %[[xvalf]], float* %[[vf]]
+  omp.atomic.capture {
+    omp.atomic.read %vf = %xf : !llvm.ptr<f32>
+    omp.atomic.update %xf : !llvm.ptr<f32> {
+    ^bb0(%xval: f32):
+      %newval = llvm.fadd %xval, %exprf : f32
+      omp.yield(%newval : f32)
+    }
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK: %[[xvalf:.*]] = bitcast i32 %[[xval]] to float
+  // CHECK: %[[newval:.*]] = fsub float %{{.*}}, %[[exprf]]
+  // CHECK: store float %[[newval]], float* %{{.*}}
+  // CHECK: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK: %[[xf_bitcast:.*]] = bitcast float* %[[xf]] to i32*
+  // CHECK: %{{.*}} = cmpxchg i32* %[[xf_bitcast]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store float %[[xvalf]], float* %[[vf]]
+  omp.atomic.capture {
+    omp.atomic.read %vf = %xf : !llvm.ptr<f32>
+    omp.atomic.update %xf : !llvm.ptr<f32> {
+    ^bb0(%xval: f32):
+      %newval = llvm.fsub %xval, %exprf : f32
+      omp.yield(%newval : f32)
+    }
+  }
+
+  llvm.return
+}
+
+// -----
+// CHECK-LABEL: @omp_atomic_capture_misc
+// CHECK-SAME: (i32* %[[x:.*]], i32* %[[v:.*]], i32 %[[expr:.*]], float* %[[xf:.*]], float* %[[vf:.*]], float %[[exprf:.*]])
+llvm.func @omp_atomic_capture_misc(
+  %x: !llvm.ptr<i32>, %v: !llvm.ptr<i32>, %expr: i32,
+  %xf: !llvm.ptr<f32>, %vf: !llvm.ptr<f32>, %exprf: f32) -> () {
+  // CHECK: %[[xval:.*]] = atomicrmw xchg i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK: store i32 %[[xval]], i32* %[[v]]
+  omp.atomic.capture{
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.write %x = %expr : !llvm.ptr<i32>, i32
+  }
+
+  // CHECK: %[[xval:.*]] = phi i32
+  // CHECK: %[[xvalf:.*]] = bitcast i32 %[[xval]] to float
+  // CHECK: store float %[[exprf]], float* %{{.*}}
+  // CHECK: %[[newval_:.*]] = load i32, i32* %{{.*}}
+  // CHECK: %[[xf_bitcast:.*]] = bitcast float* %[[xf]] to i32*
+  // CHECK: %{{.*}} = cmpxchg i32* %[[xf_bitcast]], i32 %[[xval]], i32 %[[newval_]] monotonic monotonic
+  // CHECK: store float %[[xvalf]], float* %[[vf]]
+  omp.atomic.capture{
+    omp.atomic.read %vf = %xf : !llvm.ptr<f32>
+    omp.atomic.write %xf = %exprf : !llvm.ptr<f32>, f32
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw add i32* %[[x]], i32 %[[expr]] seq_cst
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture memory_order(seq_cst) {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw add i32* %[[x]], i32 %[[expr]] acquire
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture memory_order(acquire) {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw add i32* %[[x]], i32 %[[expr]] release
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture memory_order(release) {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw add i32* %[[x]], i32 %[[expr]] monotonic
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture memory_order(relaxed) {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  // CHECK: %[[res:.*]] = atomicrmw add i32* %[[x]], i32 %[[expr]] acq_rel
+  // CHECK: store i32 %[[res]], i32* %[[v]]
+  omp.atomic.capture memory_order(acq_rel) {
+    omp.atomic.read %v = %x : !llvm.ptr<i32>
+    omp.atomic.update %x : !llvm.ptr<i32> {
+    ^bb0(%xval: i32):
+      %newval = llvm.add %xval, %expr : i32
+      omp.yield(%newval : i32)
+    }
+  }
+
+  llvm.return
+}
+
+// -----
+
 // CHECK-LABEL: @omp_sections_empty
 llvm.func @omp_sections_empty() -> () {
   omp.sections {


        


More information about the llvm-commits mailing list