Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 82 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenAtomic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,31 @@ void AtomicInfo::emitCopyIntoMemory(RValue rvalue) const {
}
}

static void emitMemOrderDefaultCaseLabel(CIRGenBuilderTy &builder,
mlir::Location loc) {
mlir::ArrayAttr ordersAttr = builder.getArrayAttr({});
mlir::OpBuilder::InsertPoint insertPoint;
cir::CaseOp::create(builder, loc, ordersAttr, cir::CaseOpKind::Default,
insertPoint);
builder.restoreInsertionPoint(insertPoint);
}

// Create a "case" operation with the given list of orders as its values. Also
// create the region that will hold the body of the switch-case label.
static void emitMemOrderCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc,
mlir::Type orderType,
llvm::ArrayRef<cir::MemOrder> orders) {
llvm::SmallVector<mlir::Attribute, 2> orderAttrs;
for (cir::MemOrder order : orders)
orderAttrs.push_back(cir::IntAttr::get(orderType, static_cast<int>(order)));
mlir::ArrayAttr ordersAttr = builder.getArrayAttr(orderAttrs);

mlir::OpBuilder::InsertPoint insertPoint;
cir::CaseOp::create(builder, loc, ordersAttr, cir::CaseOpKind::Anyof,
insertPoint);
builder.restoreInsertionPoint(insertPoint);
}

static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak,
Address dest, Address ptr, Address val1,
Address val2, uint64_t size,
Expand Down Expand Up @@ -844,9 +869,63 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) {
emitAtomicOp(*this, e, dest, ptr, val1, val2, isWeakExpr, orderFailExpr,
size, static_cast<cir::MemOrder>(ord));
} else {
assert(!cir::MissingFeatures::atomicExpr());
cgm.errorNYI(e->getSourceRange(), "emitAtomicExpr: dynamic memory order");
return RValue::get(nullptr);
// The memory order is not known at compile-time. The atomic operations
// can't handle runtime memory orders; the memory order must be hard coded.
// Generate a "switch" statement that converts a runtime value into a
// compile-time value.
cir::SwitchOp::create(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the logic for this whole section to a new function

builder, order.getLoc(), order,
[&](mlir::OpBuilder &, mlir::Location loc, mlir::OperationState &) {
mlir::Block *switchBlock = builder.getBlock();

auto emitMemOrderCase = [&](llvm::ArrayRef<cir::MemOrder> caseOrders,
cir::MemOrder actualOrder) {
if (caseOrders.empty())
emitMemOrderDefaultCaseLabel(builder, loc);
else
emitMemOrderCaseLabel(builder, loc, order.getType(), caseOrders);
emitAtomicOp(*this, e, dest, ptr, val1, val2, isWeakExpr,
orderFailExpr, size, actualOrder);
builder.createBreak(loc);
builder.setInsertionPointToEnd(switchBlock);
};

// default:
// Use memory_order_relaxed for relaxed operations and for any memory
// order value that is not supported. There is no good way to report
// an unsupported memory order at runtime, hence the fallback to
// memory_order_relaxed.
emitMemOrderCase(/*caseOrders=*/{}, cir::MemOrder::Relaxed);

if (!isStore) {
// case consume:
// case acquire:
// memory_order_consume is not implemented; it is always treated
// like memory_order_acquire. These memory orders are not valid for
// write-only operations.
emitMemOrderCase({cir::MemOrder::Consume, cir::MemOrder::Acquire},
cir::MemOrder::Acquire);
}

if (!isLoad) {
// case release:
// memory_order_release is not valid for read-only operations.
emitMemOrderCase({cir::MemOrder::Release}, cir::MemOrder::Release);
}

if (!isLoad && !isStore) {
// case acq_rel:
// memory_order_acq_rel is only valid for read-write operations.
emitMemOrderCase({cir::MemOrder::AcquireRelease},
cir::MemOrder::AcquireRelease);
}

// case seq_cst:
emitMemOrderCase({cir::MemOrder::SequentiallyConsistent},
cir::MemOrder::SequentiallyConsistent);

builder.createYield(loc);
});
}

if (resultTy->isVoidType())
Expand Down
266 changes: 266 additions & 0 deletions clang/test/CIR/CodeGen/atomic.c
Original file line number Diff line number Diff line change
Expand Up @@ -1133,3 +1133,269 @@ int c11_atomic_fetch_nand(_Atomic(int) *ptr, int value) {
// OGCG: %[[RES:.+]] = atomicrmw nand ptr %{{.+}}, i32 %{{.+}} seq_cst, align 4
// OGCG-NEXT: store i32 %[[RES]], ptr %{{.+}}, align 4
}

int atomic_load_dynamic_order(int *ptr, int order) {
// CIR-LABEL: atomic_load_dynamic_order
// LLVM-LABEL: atomic_load_dynamic_order
// OGCG-LABEL: atomic_load_dynamic_order

return __atomic_load_n(ptr, order);

// CIR: %[[PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
// CIR-NEXT: %[[ORDER:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: cir.switch (%[[ORDER]] : !s32i) {
// CIR-NEXT: cir.case(default, []) {
// CIR-NEXT: %[[RES:.+]] = cir.load align(4) atomic(relaxed) %[[PTR]] : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: cir.store align(4) %[[RES]], %[[RES_SLOT:.+]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.case(anyof, [#cir.int<1> : !s32i, #cir.int<2> : !s32i]) {
// CIR-NEXT: %[[RES:.+]] = cir.load align(4) atomic(acquire) %[[PTR]] : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: cir.store align(4) %[[RES]], %[[RES_SLOT]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.case(anyof, [#cir.int<5> : !s32i]) {
// CIR-NEXT: %[[RES:.+]] = cir.load align(4) atomic(seq_cst) %[[PTR]] : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: cir.store align(4) %[[RES]], %[[RES_SLOT]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.yield
// CIR-NEXT: }
// CIR-NEXT: %{{.+}} = cir.load align(4) %[[RES_SLOT]] : !cir.ptr<!s32i>, !s32i

// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
// LLVM-NEXT: %[[ORDER:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM-NEXT: br label %[[SWITCH_BLK:.+]]
// LLVM: [[SWITCH_BLK]]:
// LLVM-NEXT: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
// LLVM-NEXT: i32 1, label %[[ACQUIRE_BLK:.+]]
// LLVM-NEXT: i32 2, label %[[ACQUIRE_BLK]]
// LLVM-NEXT: i32 5, label %[[SEQ_CST_BLK:.+]]
// LLVM-NEXT: ]
// LLVM: [[DEFAULT_BLK]]:
// LLVM-NEXT: %[[RES:.+]] = load atomic i32, ptr %[[PTR]] monotonic, align 4
// LLVM-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT:.+]], align 4
// LLVM-NEXT: br label %[[CONTINUE_BLK:.+]]
// LLVM: [[ACQUIRE_BLK]]:
// LLVM-NEXT: %[[RES:.+]] = load atomic i32, ptr %[[PTR]] acquire, align 4
// LLVM-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// LLVM-NEXT: br label %[[CONTINUE_BLK]]
// LLVM: [[SEQ_CST_BLK]]:
// LLVM-NEXT: %[[RES:.+]] = load atomic i32, ptr %[[PTR]] seq_cst, align 4
// LLVM-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// LLVM-NEXT: br label %[[CONTINUE_BLK]]
// LLVM: [[CONTINUE_BLK]]:
// LLVM-NEXT: %{{.+}} = load i32, ptr %[[RES_SLOT]], align 4

// OGCG: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
// OGCG-NEXT: %[[ORDER:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG-NEXT: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
// OGCG-NEXT: i32 1, label %[[ACQUIRE_BLK:.+]]
// OGCG-NEXT: i32 2, label %[[ACQUIRE_BLK]]
// OGCG-NEXT: i32 5, label %[[SEQ_CST_BLK:.+]]
// OGCG-NEXT: ]
// OGCG: [[DEFAULT_BLK]]:
// OGCG-NEXT: %[[RES:.+]] = load atomic i32, ptr %[[PTR]] monotonic, align 4
// OGCG-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT:.+]], align 4
// OGCG-NEXT: br label %[[CONTINUE_BLK:.+]]
// OGCG: [[ACQUIRE_BLK]]:
// OGCG-NEXT: %[[RES:.+]] = load atomic i32, ptr %[[PTR]] acquire, align 4
// OGCG-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// OGCG-NEXT: br label %[[CONTINUE_BLK]]
// OGCG: [[SEQ_CST_BLK]]:
// OGCG-NEXT: %[[RES:.+]] = load atomic i32, ptr %[[PTR]] seq_cst, align 4
// OGCG-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// OGCG-NEXT: br label %[[CONTINUE_BLK]]
// OGCG: [[CONTINUE_BLK]]:
// OGCG-NEXT: %{{.+}} = load i32, ptr %[[RES_SLOT]], align 4
}

void atomic_store_dynamic_order(int *ptr, int order) {
// CIR-LABEL: atomic_store_dynamic_order
// LLVM-LABEL: atomic_store_dynamic_order
// OGCG-LABEL: atomic_store_dynamic_order

__atomic_store_n(ptr, 10, order);

// CIR: %[[PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
// CIR-NEXT: %[[ORDER:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR: cir.switch (%[[ORDER]] : !s32i) {
// CIR-NEXT: cir.case(default, []) {
// CIR-NEXT: %[[VALUE:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: cir.store align(4) atomic(relaxed) %[[VALUE]], %[[PTR]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.case(anyof, [#cir.int<3> : !s32i]) {
// CIR-NEXT: %[[VALUE:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: cir.store align(4) atomic(release) %[[VALUE]], %[[PTR]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.case(anyof, [#cir.int<5> : !s32i]) {
// CIR-NEXT: %[[VALUE:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: cir.store align(4) atomic(seq_cst) %[[VALUE]], %[[PTR]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.yield
// CIR-NEXT: }

// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
// LLVM-NEXT: %[[ORDER:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM: br label %[[SWITCH_BLK:.+]]
// LLVM: [[SWITCH_BLK]]:
// LLVM-NEXT: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
// LLVM-NEXT: i32 3, label %[[RELEASE_BLK:.+]]
// LLVM-NEXT: i32 5, label %[[SEQ_CST_BLK:.+]]
// LLVM-NEXT: ]
// LLVM: [[DEFAULT_BLK]]:
// LLVM-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM-NEXT: store atomic i32 %[[VALUE]], ptr %[[PTR]] monotonic, align 4
// LLVM-NEXT: br label %{{.+}}
// LLVM: [[RELEASE_BLK]]:
// LLVM-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM-NEXT: store atomic i32 %[[VALUE]], ptr %[[PTR]] release, align 4
// LLVM-NEXT: br label %{{.+}}
// LLVM: [[SEQ_CST_BLK]]:
// LLVM-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM-NEXT: store atomic i32 %[[VALUE]], ptr %[[PTR]] seq_cst, align 4
// LLVM-NEXT: br label %{{.+}}

// OGCG: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
// OGCG-NEXT: %[[ORDER:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
// OGCG-NEXT: i32 3, label %[[RELEASE_BLK:.+]]
// OGCG-NEXT: i32 5, label %[[SEQ_CST_BLK:.+]]
// OGCG-NEXT: ]
// OGCG: [[DEFAULT_BLK]]:
// OGCG-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG-NEXT: store atomic i32 %[[VALUE]], ptr %[[PTR]] monotonic, align 4
// OGCG-NEXT: br label %{{.+}}
// OGCG: [[RELEASE_BLK]]:
// OGCG-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG-NEXT: store atomic i32 %[[VALUE]], ptr %[[PTR]] release, align 4
// OGCG-NEXT: br label %{{.+}}
// OGCG: [[SEQ_CST_BLK]]:
// OGCG-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG-NEXT: store atomic i32 %[[VALUE]], ptr %[[PTR]] seq_cst, align 4
// OGCG-NEXT: br label %{{.+}}
}

int atomic_load_and_store_dynamic_order(int *ptr, int order) {
// CIR-LABEL: atomic_load_and_store_dynamic_order
// LLVM-LABEL: atomic_load_and_store_dynamic_order
// OGCG-LABEL: atomic_load_and_store_dynamic_order

return __atomic_exchange_n(ptr, 20, order);

// CIR: %[[PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
// CIR-NEXT: %[[ORDER:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR: cir.switch (%[[ORDER]] : !s32i) {
// CIR-NEXT: cir.case(default, []) {
// CIR-NEXT: %[[LIT:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: %[[RES:.+]] = cir.atomic.xchg relaxed %[[PTR]], %[[LIT]] : (!cir.ptr<!s32i>, !s32i) -> !s32i
// CIR-NEXT: cir.store align(4) %[[RES]], %[[RES_SLOT:.+]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.case(anyof, [#cir.int<1> : !s32i, #cir.int<2> : !s32i]) {
// CIR-NEXT: %[[LIT:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: %[[RES:.+]] = cir.atomic.xchg acquire %[[PTR]], %[[LIT]] : (!cir.ptr<!s32i>, !s32i) -> !s32i
// CIR-NEXT: cir.store align(4) %[[RES]], %[[RES_SLOT]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.case(anyof, [#cir.int<3> : !s32i]) {
// CIR-NEXT: %[[LIT:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: %[[RES:.+]] = cir.atomic.xchg release %[[PTR]], %[[LIT]] : (!cir.ptr<!s32i>, !s32i) -> !s32i
// CIR-NEXT: cir.store align(4) %[[RES]], %[[RES_SLOT]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.case(anyof, [#cir.int<4> : !s32i]) {
// CIR-NEXT: %[[LIT:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: %[[RES:.+]] = cir.atomic.xchg acq_rel %[[PTR]], %[[LIT]] : (!cir.ptr<!s32i>, !s32i) -> !s32i
// CIR-NEXT: cir.store align(4) %[[RES]], %[[RES_SLOT]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.case(anyof, [#cir.int<5> : !s32i]) {
// CIR-NEXT: %[[LIT:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: %[[RES:.+]] = cir.atomic.xchg seq_cst %[[PTR]], %[[LIT]] : (!cir.ptr<!s32i>, !s32i) -> !s32i
// CIR-NEXT: cir.store align(4) %[[RES]], %[[RES_SLOT]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT: cir.break
// CIR-NEXT: }
// CIR-NEXT: cir.yield
// CIR-NEXT: }
// CIR-NEXT: %{{.+}} = cir.load align(4) %[[RES_SLOT]] : !cir.ptr<!s32i>, !s32i

// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
// LLVM-NEXT: %[[ORDER:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM: br label %[[SWITCH_BLK:.+]]
// LLVM: [[SWITCH_BLK]]:
// LLVM-NEXT: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
// LLVM-NEXT: i32 1, label %[[ACQUIRE_BLK:.+]]
// LLVM-NEXT: i32 2, label %[[ACQUIRE_BLK]]
// LLVM-NEXT: i32 3, label %[[RELEASE_BLK:.+]]
// LLVM-NEXT: i32 4, label %[[ACQ_REL_BLK:.+]]
// LLVM-NEXT: i32 5, label %[[SEQ_CST_BLK:.+]]
// LLVM-NEXT: ]
// LLVM: [[DEFAULT_BLK]]:
// LLVM-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] monotonic, align 4
// LLVM-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT:.+]], align 4
// LLVM-NEXT: br label %[[CONTINUE_BLK:.+]]
// LLVM: [[ACQUIRE_BLK]]:
// LLVM-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] acquire, align 4
// LLVM-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// LLVM-NEXT: br label %[[CONTINUE_BLK]]
// LLVM: [[RELEASE_BLK]]:
// LLVM-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] release, align 4
// LLVM-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// LLVM-NEXT: br label %[[CONTINUE_BLK]]
// LLVM: [[ACQ_REL_BLK]]:
// LLVM-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] acq_rel, align 4
// LLVM-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// LLVM-NEXT: br label %[[CONTINUE_BLK]]
// LLVM: [[SEQ_CST_BLK]]:
// LLVM-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// LLVM-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] seq_cst, align 4
// LLVM-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// LLVM-NEXT: br label %[[CONTINUE_BLK]]
// LLVM: [[CONTINUE_BLK]]:
// LLVM-NEXT: %{{.+}} = load i32, ptr %[[RES_SLOT]], align 4

// OGCG: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8
// OGCG-NEXT: %[[ORDER:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
// OGCG-NEXT: i32 1, label %[[ACQUIRE_BLK:.+]]
// OGCG-NEXT: i32 2, label %[[ACQUIRE_BLK]]
// OGCG-NEXT: i32 3, label %[[RELEASE_BLK:.+]]
// OGCG-NEXT: i32 4, label %[[ACQ_REL_BLK:.+]]
// OGCG-NEXT: i32 5, label %[[SEQ_CST_BLK:.+]]
// OGCG-NEXT: ]
// OGCG: [[DEFAULT_BLK]]:
// OGCG-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] monotonic, align 4
// OGCG-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT:.+]], align 4
// OGCG-NEXT: br label %[[CONTINUE_BLK:.+]]
// OGCG: [[ACQUIRE_BLK]]:
// OGCG-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] acquire, align 4
// OGCG-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// OGCG-NEXT: br label %[[CONTINUE_BLK]]
// OGCG: [[RELEASE_BLK]]:
// OGCG-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] release, align 4
// OGCG-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// OGCG-NEXT: br label %[[CONTINUE_BLK]]
// OGCG: [[ACQ_REL_BLK]]:
// OGCG-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] acq_rel, align 4
// OGCG-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// OGCG-NEXT: br label %[[CONTINUE_BLK]]
// OGCG: [[SEQ_CST_BLK]]:
// OGCG-NEXT: %[[VALUE:.+]] = load i32, ptr %{{.+}}, align 4
// OGCG-NEXT: %[[RES:.+]] = atomicrmw xchg ptr %[[PTR]], i32 %[[VALUE]] seq_cst, align 4
// OGCG-NEXT: store i32 %[[RES]], ptr %[[RES_SLOT]], align 4
// OGCG-NEXT: br label %[[CONTINUE_BLK]]
// OGCG: [[CONTINUE_BLK]]:
// OGCG-NEXT: %{{.+}} = load i32, ptr %[[RES_SLOT]], align 4
}