[clang] [CIR] Add codegen for atomic fence builtin with non-const memory order (PR #172455)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 16 02:52:45 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Haocong Lu (Luhaocong)
<details>
<summary>Changes</summary>
- Support CIR codegen for follow atomic fence builtin when the memory order is non constant: `__atomic_thread_fence` `__atomic_signal_fence` `__c11_atomic_thread_fence` `__c11_atomic_signal_fence`
- Refactor current implementation when the memory order is constant, the argument expression at AST is evaluated as a constant directly.
- Add test cases that cover all kinds of memory order.
---
Patch is 21.71 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/172455.diff
2 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+82-20)
- (modified) clang/test/CIR/CodeGen/atomic-thread-fence.c (+424)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index b4f02c97f539a..4f732bbb4750c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -63,25 +63,87 @@ static RValue emitBuiltinBitOp(CIRGenFunction &cgf, const CallExpr *e,
static void emitAtomicFenceOp(CIRGenFunction &cgf, const CallExpr *expr,
cir::SyncScopeKind syncScope) {
CIRGenBuilderTy &builder = cgf.getBuilder();
- mlir::Value orderingVal = cgf.emitScalarExpr(expr->getArg(0));
-
- auto constOrdering = orderingVal.getDefiningOp<cir::ConstantOp>();
-
- if (!constOrdering) {
- // TODO(cir): Emit code to switch on `orderingVal`,
- // and creating the fence op for valid values.
- cgf.cgm.errorNYI("Variable atomic fence ordering");
+ mlir::Location loc = cgf.getLoc(expr->getSourceRange());
+
+ // Convert the memory order specified by user to effective one:
+ // Relaxed -> std::nullopt
+ // Consume/Acquire -> Acquire
+ // Release -> Release
+ // AcquireRelease -> AcquireRelease
+ // SequentiallyConsistent -> SequentiallyConsistent
+ auto getEffectiveMemOrder =
+ [](cir::MemOrder oriOrder) -> std::optional<cir::MemOrder> {
+ if (oriOrder == cir::MemOrder::Relaxed)
+ return std::nullopt;
+ else if (oriOrder == cir::MemOrder::Consume ||
+ oriOrder == cir::MemOrder::Acquire)
+ return cir::MemOrder::Acquire;
+ else
+ return oriOrder;
+ };
+
+ // Handle constant memory ordering.
+ Expr::EvalResult eval;
+ if (expr->getArg(0)->EvaluateAsInt(eval, cgf.getContext())) {
+ uint64_t constOrder = eval.Val.getInt().getZExtValue();
+ // Not emit anything if it's an invalid constant.
+ if (!cir::isValidCIRAtomicOrderingCABI(constOrder))
+ return;
+ cir::MemOrder caseOrder = static_cast<cir::MemOrder>(constOrder);
+ if (std::optional<cir::MemOrder> order = getEffectiveMemOrder(caseOrder))
+ cir::AtomicFenceOp::create(
+ builder, loc, order.value(),
+ cir::SyncScopeKindAttr::get(&cgf.getMLIRContext(), syncScope));
return;
}
- auto constOrderingAttr = constOrdering.getValueAttr<cir::IntAttr>();
- assert(constOrderingAttr && "Expected integer constant for ordering");
-
- auto ordering = static_cast<cir::MemOrder>(constOrderingAttr.getUInt());
-
- cir::AtomicFenceOp::create(
- builder, cgf.getLoc(expr->getSourceRange()), ordering,
- cir::SyncScopeKindAttr::get(&cgf.getMLIRContext(), syncScope));
+ // Otherwise, handle variable memory ordering. Emit `SwitchOp` to convert
+ // dynamic value to static value.
+ mlir::Value varOrder = cgf.emitScalarExpr(expr->getArg(0));
+ cir::SwitchOp::create(
+ builder, loc, varOrder,
+ [&](mlir::OpBuilder &, mlir::Location loc, mlir::OperationState &) {
+ mlir::Block *switchBlock = builder.getBlock();
+
+ auto emitMemOrderCase = [&](llvm::ArrayRef<cir::MemOrder> caseOrders) {
+ if (caseOrders.empty()) {
+ // Creating default case operation
+ mlir::OpBuilder::InsertPoint insertPoint;
+ cir::CaseOp::create(builder, loc, builder.getArrayAttr({}),
+ cir::CaseOpKind::Default, insertPoint);
+ builder.restoreInsertionPoint(insertPoint);
+ } else if (auto actualOrder = getEffectiveMemOrder(caseOrders[0])) {
+ // Creating case operation for effective memory order
+ mlir::OpBuilder::InsertPoint insertPoint;
+ llvm::SmallVector<mlir::Attribute, 2> orderAttrs;
+ for (cir::MemOrder caseOrder : caseOrders)
+ orderAttrs.push_back(cir::IntAttr::get(
+ varOrder.getType(), static_cast<int>(caseOrder)));
+ cir::CaseOp::create(builder, loc, builder.getArrayAttr(orderAttrs),
+ cir::CaseOpKind::Anyof, insertPoint);
+ // Creating atomic fence operation
+ builder.restoreInsertionPoint(insertPoint);
+ cir::AtomicFenceOp::create(
+ builder, loc, actualOrder.value(),
+ cir::SyncScopeKindAttr::get(&cgf.getMLIRContext(), syncScope));
+ } else {
+ // Do nothing if unneccssary (!caseOrders.empty() && !actualOrder)
+ return;
+ }
+ builder.createBreak(loc);
+ builder.setInsertionPointToEnd(switchBlock);
+ return;
+ };
+
+ emitMemOrderCase(/*default:*/ {});
+ emitMemOrderCase({cir::MemOrder::Relaxed}); // Not effective
+ emitMemOrderCase({cir::MemOrder::Consume, cir::MemOrder::Acquire});
+ emitMemOrderCase({cir::MemOrder::Release});
+ emitMemOrderCase({cir::MemOrder::AcquireRelease});
+ emitMemOrderCase({cir::MemOrder::SequentiallyConsistent});
+
+ builder.createYield(loc);
+ });
}
namespace {
@@ -1007,16 +1069,16 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__atomic_test_and_set:
case Builtin::BI__atomic_clear:
return errorBuiltinNYI(*this, e, builtinID);
- case Builtin::BI__atomic_thread_fence: {
+ case Builtin::BI__atomic_thread_fence:
+ case Builtin::BI__c11_atomic_thread_fence: {
emitAtomicFenceOp(*this, e, cir::SyncScopeKind::System);
return RValue::get(nullptr);
}
- case Builtin::BI__atomic_signal_fence: {
+ case Builtin::BI__atomic_signal_fence:
+ case Builtin::BI__c11_atomic_signal_fence: {
emitAtomicFenceOp(*this, e, cir::SyncScopeKind::SingleThread);
return RValue::get(nullptr);
}
- case Builtin::BI__c11_atomic_thread_fence:
- case Builtin::BI__c11_atomic_signal_fence:
case Builtin::BI__scoped_atomic_thread_fence:
case Builtin::BI__builtin_signbit:
case Builtin::BI__builtin_signbitf:
diff --git a/clang/test/CIR/CodeGen/atomic-thread-fence.c b/clang/test/CIR/CodeGen/atomic-thread-fence.c
index f28bc6808cbfa..69298fb9cd7b0 100644
--- a/clang/test/CIR/CodeGen/atomic-thread-fence.c
+++ b/clang/test/CIR/CodeGen/atomic-thread-fence.c
@@ -179,3 +179,427 @@ void loadWithSignalFence(DataPtr d) {
// OGCG: %[[DATA_TEMP_LOAD]] = load ptr, ptr %[[DATA_TEMP]], align 8
// OGCG: ret void
}
+
+void const_atomic_thread_fence() {
+ __atomic_thread_fence(__ATOMIC_RELAXED);
+ __atomic_thread_fence(__ATOMIC_CONSUME);
+ __atomic_thread_fence(__ATOMIC_ACQUIRE);
+ __atomic_thread_fence(__ATOMIC_RELEASE);
+ __atomic_thread_fence(__ATOMIC_ACQ_REL);
+ __atomic_thread_fence(__ATOMIC_SEQ_CST);
+ // CIR-LABEL: const_atomic_thread_fence
+ // CIR: cir.atomic.fence syncscope(system) acquire
+ // CIR: cir.atomic.fence syncscope(system) acquire
+ // CIR: cir.atomic.fence syncscope(system) release
+ // CIR: cir.atomic.fence syncscope(system) acq_rel
+ // CIR: cir.atomic.fence syncscope(system) seq_cst
+
+ // LLVM-LABEL: const_atomic_thread_fence
+ // LLVM: fence acquire
+ // LLVM: fence acquire
+ // LLVM: fence release
+ // LLVM: fence acq_rel
+ // LLVM: fence seq_cst
+
+ // OGCG-LABEL: const_atomic_thread_fence
+ // OGCG: fence acquire
+ // OGCG: fence acquire
+ // OGCG: fence release
+ // OGCG: fence acq_rel
+ // OGCG: fence seq_cst
+}
+
+void const_c11_atomic_thread_fence() {
+ __c11_atomic_thread_fence(__ATOMIC_RELAXED);
+ __c11_atomic_thread_fence(__ATOMIC_CONSUME);
+ __c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
+ __c11_atomic_thread_fence(__ATOMIC_RELEASE);
+ __c11_atomic_thread_fence(__ATOMIC_ACQ_REL);
+ __c11_atomic_thread_fence(__ATOMIC_SEQ_CST);
+ // CIR-LABEL: const_c11_atomic_thread_fence
+ // CIR: cir.atomic.fence syncscope(system) acquire
+ // CIR: cir.atomic.fence syncscope(system) acquire
+ // CIR: cir.atomic.fence syncscope(system) release
+ // CIR: cir.atomic.fence syncscope(system) acq_rel
+ // CIR: cir.atomic.fence syncscope(system) seq_cst
+
+ // LLVM-LABEL: const_c11_atomic_thread_fence
+ // LLVM: fence acquire
+ // LLVM: fence acquire
+ // LLVM: fence release
+ // LLVM: fence acq_rel
+ // LLVM: fence seq_cst
+
+ // OGCG-LABEL: const_c11_atomic_thread_fence
+ // OGCG: fence acquire
+ // OGCG: fence acquire
+ // OGCG: fence release
+ // OGCG: fence acq_rel
+ // OGCG: fence seq_cst
+}
+
+void const_atomic_signal_fence() {
+ __atomic_signal_fence(__ATOMIC_RELAXED);
+ __atomic_signal_fence(__ATOMIC_CONSUME);
+ __atomic_signal_fence(__ATOMIC_ACQUIRE);
+ __atomic_signal_fence(__ATOMIC_RELEASE);
+ __atomic_signal_fence(__ATOMIC_ACQ_REL);
+ __atomic_signal_fence(__ATOMIC_SEQ_CST);
+ // CIR-LABEL: const_atomic_signal_fence
+ // CIR: cir.atomic.fence syncscope(single_thread) acquire
+ // CIR: cir.atomic.fence syncscope(single_thread) acquire
+ // CIR: cir.atomic.fence syncscope(single_thread) release
+ // CIR: cir.atomic.fence syncscope(single_thread) acq_rel
+ // CIR: cir.atomic.fence syncscope(single_thread) seq_cst
+
+ // LLVM-LABEL: const_atomic_signal_fence
+ // LLVM: fence syncscope("singlethread") acquire
+ // LLVM: fence syncscope("singlethread") acquire
+ // LLVM: fence syncscope("singlethread") release
+ // LLVM: fence syncscope("singlethread") acq_rel
+ // LLVM: fence syncscope("singlethread") seq_cst
+
+ // OGCG--LABEL: const_atomic_signal_fence
+ // OGCG: fence syncscope("singlethread") acquire
+ // OGCG: fence syncscope("singlethread") acquire
+ // OGCG: fence syncscope("singlethread") release
+ // OGCG: fence syncscope("singlethread") acq_rel
+ // OGCG: fence syncscope("singlethread") seq_cst
+}
+
+void const_c11_atomic_signal_fence() {
+ __c11_atomic_signal_fence(__ATOMIC_RELAXED);
+ __c11_atomic_signal_fence(__ATOMIC_CONSUME);
+ __c11_atomic_signal_fence(__ATOMIC_ACQUIRE);
+ __c11_atomic_signal_fence(__ATOMIC_RELEASE);
+ __c11_atomic_signal_fence(__ATOMIC_ACQ_REL);
+ __c11_atomic_signal_fence(__ATOMIC_SEQ_CST);
+ // CIR-LABEL: const_c11_atomic_signal_fence
+ // CIR: cir.atomic.fence syncscope(single_thread) acquire
+ // CIR: cir.atomic.fence syncscope(single_thread) acquire
+ // CIR: cir.atomic.fence syncscope(single_thread) release
+ // CIR: cir.atomic.fence syncscope(single_thread) acq_rel
+ // CIR: cir.atomic.fence syncscope(single_thread) seq_cst
+
+ // LLVM-LABEL: const_c11_atomic_signal_fence
+ // LLVM: fence syncscope("singlethread") acquire
+ // LLVM: fence syncscope("singlethread") acquire
+ // LLVM: fence syncscope("singlethread") release
+ // LLVM: fence syncscope("singlethread") acq_rel
+ // LLVM: fence syncscope("singlethread") seq_cst
+
+ // OGCG-LABEL: const_c11_atomic_signal_fence
+ // OGCG: fence syncscope("singlethread") acquire
+ // OGCG: fence syncscope("singlethread") acquire
+ // OGCG: fence syncscope("singlethread") release
+ // OGCG: fence syncscope("singlethread") acq_rel
+ // OGCG: fence syncscope("singlethread") seq_cst
+}
+
+void variable_atomic_thread_fences(int memorder) {
+ __atomic_thread_fence(memorder);
+ // CIR-LABEL: variable_atomic_thread_fences
+ // CIR: cir.switch
+ // CIR: cir.case(default, []) {
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<1> : !s32i, #cir.int<2> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(system) acquire
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<3> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(system) release
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<4> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(system) acq_rel
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<5> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(system)
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.yield
+ // CIR: }
+
+ // LLVM-LABEL: variable_atomic_thread_fences
+ // LLVM: %[[ORDER:.+]] = load i32, ptr %[[PTR:.+]], align 4
+ // LLVM: br label %[[SWITCH_BLK:.+]]
+ // LLVM: [[SWITCH_BLK]]:
+ // LLVM: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
+ // LLVM: i32 1, label %[[ACQUIRE_BLK:.+]]
+ // LLVM: i32 2, label %[[ACQUIRE_BLK]]
+ // LLVM: i32 3, label %[[RELEASE_BLK:.+]]
+ // LLVM: i32 4, label %[[ACQ_REL_BLK:.+]]
+ // LLVM: i32 5, label %[[SEQ_CST_BLK:.+]]
+ // LLVM: ]
+ // LLVM: [[DEFAULT_BLK]]:
+ // LLVM: br label %{{.+}}
+ // LLVM: [[ACQUIRE_BLK]]:
+ // LLVM: fence acquire
+ // LLVM: br label %{{.+}}
+ // LLVM: [[RELEASE_BLK]]:
+ // LLVM: fence release
+ // LLVM: br label %{{.+}}
+ // LLVM: [[ACQ_REL_BLK]]:
+ // LLVM: fence acq_rel
+ // LLVM: br label %{{.+}}
+ // LLVM: [[SEQ_CST_BLK]]:
+ // LLVM: fence seq_cst
+ // LLVM: br label %{{.+}}
+
+ // OGCG-LABEL: variable_atomic_thread_fences
+ // OGCG: %[[ORDER:.+]] = load i32, ptr %[[PTR:.+]], align 4
+ // OGCG: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
+ // OGCG: i32 1, label %[[ACQUIRE_BLK:.+]]
+ // OGCG: i32 2, label %[[ACQUIRE_BLK]]
+ // OGCG: i32 3, label %[[RELEASE_BLK:.+]]
+ // OGCG: i32 4, label %[[ACQ_REL_BLK:.+]]
+ // OGCG: i32 5, label %[[SEQ_CST_BLK:.+]]
+ // OGCG: ]
+ // OGCG: [[ACQUIRE_BLK]]:
+ // OGCG: fence acquire
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[RELEASE_BLK]]:
+ // OGCG: fence release
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[ACQ_REL_BLK]]:
+ // OGCG: fence acq_rel
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[SEQ_CST_BLK]]:
+ // OGCG: fence seq_cst
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[DEFAULT_BLK]]:
+ // OGCG: ret void
+}
+
+void variable_c11_atomic_thread_fences(int memorder) {
+ __c11_atomic_thread_fence(memorder);
+ // CIR-LABEL: variable_c11_atomic_thread_fences
+ // CIR: cir.switch
+ // CIR: cir.case(default, []) {
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<1> : !s32i, #cir.int<2> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(system) acquire
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<3> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(system) release
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<4> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(system) acq_rel
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<5> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(system)
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.yield
+ // CIR: }
+
+ // LLVM-LABEL: variable_c11_atomic_thread_fences
+ // LLVM: %[[ORDER:.+]] = load i32, ptr %[[PTR:.+]], align 4
+ // LLVM: br label %[[SWITCH_BLK:.+]]
+ // LLVM: [[SWITCH_BLK]]:
+ // LLVM: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
+ // LLVM: i32 1, label %[[ACQUIRE_BLK:.+]]
+ // LLVM: i32 2, label %[[ACQUIRE_BLK]]
+ // LLVM: i32 3, label %[[RELEASE_BLK:.+]]
+ // LLVM: i32 4, label %[[ACQ_REL_BLK:.+]]
+ // LLVM: i32 5, label %[[SEQ_CST_BLK:.+]]
+ // LLVM: ]
+ // LLVM: [[DEFAULT_BLK]]:
+ // LLVM: br label %{{.+}}
+ // LLVM: [[ACQUIRE_BLK]]:
+ // LLVM: fence acquire
+ // LLVM: br label %{{.+}}
+ // LLVM: [[RELEASE_BLK]]:
+ // LLVM: fence release
+ // LLVM: br label %{{.+}}
+ // LLVM: [[ACQ_REL_BLK]]:
+ // LLVM: fence acq_rel
+ // LLVM: br label %{{.+}}
+ // LLVM: [[SEQ_CST_BLK]]:
+ // LLVM: fence seq_cst
+ // LLVM: br label %{{.+}}
+
+ // OGCG-LABEL: variable_c11_atomic_thread_fences
+ // OGCG: %[[ORDER:.+]] = load i32, ptr %[[PTR:.+]], align 4
+ // OGCG: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
+ // OGCG: i32 1, label %[[ACQUIRE_BLK:.+]]
+ // OGCG: i32 2, label %[[ACQUIRE_BLK]]
+ // OGCG: i32 3, label %[[RELEASE_BLK:.+]]
+ // OGCG: i32 4, label %[[ACQ_REL_BLK:.+]]
+ // OGCG: i32 5, label %[[SEQ_CST_BLK:.+]]
+ // OGCG: ]
+ // OGCG: [[ACQUIRE_BLK]]:
+ // OGCG: fence acquire
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[RELEASE_BLK]]:
+ // OGCG: fence release
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[ACQ_REL_BLK]]:
+ // OGCG: fence acq_rel
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[SEQ_CST_BLK]]:
+ // OGCG: fence seq_cst
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[DEFAULT_BLK]]:
+ // OGCG: ret void
+}
+
+void variable_atomic_signal_fences(int memorder) {
+ __atomic_signal_fence(memorder);
+ // CIR-LABEL: variable_atomic_signal_fences
+ // CIR: cir.switch
+ // CIR: cir.case(default, []) {
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<1> : !s32i, #cir.int<2> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(single_thread) acquire
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<3> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(single_thread) release
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<4> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(single_thread) acq_rel
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<5> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(single_thread)
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.yield
+ // CIR: }
+
+ // LLVM-LABEL: variable_atomic_signal_fences
+ // LLVM: %[[ORDER:.+]] = load i32, ptr %[[PTR:.+]], align 4
+ // LLVM: br label %[[SWITCH_BLK:.+]]
+ // LLVM: [[SWITCH_BLK]]:
+ // LLVM: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
+ // LLVM: i32 1, label %[[ACQUIRE_BLK:.+]]
+ // LLVM: i32 2, label %[[ACQUIRE_BLK]]
+ // LLVM: i32 3, label %[[RELEASE_BLK:.+]]
+ // LLVM: i32 4, label %[[ACQ_REL_BLK:.+]]
+ // LLVM: i32 5, label %[[SEQ_CST_BLK:.+]]
+ // LLVM: ]
+ // LLVM: [[DEFAULT_BLK]]:
+ // LLVM: br label %{{.+}}
+ // LLVM: [[ACQUIRE_BLK]]:
+ // LLVM: fence syncscope("singlethread") acquire
+ // LLVM: br label %{{.+}}
+ // LLVM: [[RELEASE_BLK]]:
+ // LLVM: fence syncscope("singlethread") release
+ // LLVM: br label %{{.+}}
+ // LLVM: [[ACQ_REL_BLK]]:
+ // LLVM: fence syncscope("singlethread") acq_rel
+ // LLVM: br label %{{.+}}
+ // LLVM: [[SEQ_CST_BLK]]:
+ // LLVM: fence syncscope("singlethread") seq_cst
+ // LLVM: br label %{{.+}}
+
+ // OGCG-LABEL: variable_atomic_signal_fences
+ // OGCG: %[[ORDER:.+]] = load i32, ptr %[[PTR:.+]], align 4
+ // OGCG: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
+ // OGCG: i32 1, label %[[ACQUIRE_BLK:.+]]
+ // OGCG: i32 2, label %[[ACQUIRE_BLK]]
+ // OGCG: i32 3, label %[[RELEASE_BLK:.+]]
+ // OGCG: i32 4, label %[[ACQ_REL_BLK:.+]]
+ // OGCG: i32 5, label %[[SEQ_CST_BLK:.+]]
+ // OGCG: ]
+ // OGCG: [[ACQUIRE_BLK]]:
+ // OGCG: fence syncscope("singlethread") acquire
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[RELEASE_BLK]]:
+ // OGCG: fence syncscope("singlethread") release
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[ACQ_REL_BLK]]:
+ // OGCG: fence syncscope("singlethread") acq_rel
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[SEQ_CST_BLK]]:
+ // OGCG: fence syncscope("singlethread") seq_cst
+ // OGCG: br label %[[DEFAULT_BLK]]
+ // OGCG: [[DEFAULT_BLK]]:
+ // OGCG: ret void
+}
+
+void variable_c11_atomic_signal_fences(int memorder) {
+ __c11_atomic_signal_fence(memorder);
+ // CIR-LABEL: variable_c11_atomic_signal_fences
+ // CIR: cir.switch
+ // CIR: cir.case(default, []) {
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<1> : !s32i, #cir.int<2> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(single_thread) acquire
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<3> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(single_thread) release
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<4> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(single_thread) acq_rel
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.case(anyof, [#cir.int<5> : !s32i]) {
+ // CIR: cir.atomic.fence syncscope(single_thread)
+ // CIR: cir.break
+ // CIR: }
+ // CIR: cir.yield
+ // CIR: }
+
+ // LLVM-LABEL: variable_c11_atomic_signal_fences
+ // LLVM: %[[ORDER:.+]] = load i32, ptr %[[PTR:.+]], align 4
+ // LLVM: br label %[[SWITCH_BLK:.+]]
+ // LLVM: [[SWITCH_BLK]]:
+ // LLVM: switch i32 %[[ORDER]], label %[[DEFAULT_BLK:.+]] [
+ // LLVM: i...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/172455
More information about the cfe-commits
mailing list