[clang] [CIR] Implement missing __sync_* atomic builtins (PR #214606)
Cyrus Ding via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 9 20:13:30 PDT 2026
https://github.com/dingcyrus updated https://github.com/llvm/llvm-project/pull/214606
>From 64090b58328373f788fd10d62b80a8c6c97b102b Mon Sep 17 00:00:00 2001
From: Cyrus Ding <785101675 at qq.com>
Date: Fri, 7 Aug 2026 09:53:16 +0800
Subject: [PATCH] [CIR] Implement missing __sync_* atomic builtins
Add CIR codegen support for five legacy __sync_* builtins that
previously emitted "Not Yet Implemented" errors:
__sync_val_compare_and_swap -> cir.atomic.cmpxchg (seq_cst)
__sync_bool_compare_and_swap -> cir.atomic.cmpxchg + cmp (seq_cst)
__sync_swap -> cir.atomic.xchg (seq_cst)
__sync_lock_test_and_set -> cir.atomic.xchg (acquire)
__sync_lock_release -> cir.store 0 (release)
Both unsuffixed and size-suffixed forms (_1, _2, _4, _8, _16) are
handled, covering 30 builtin variants in total.
The existing emitBinaryAtomic / emitBinaryAtomicPost infrastructure
already handled the arithmetic __sync_* family (fetch_and_add, etc.).
This patch adds the remaining compare-and-swap, exchange, and
lock-release helpers: emitAtomicCmpXchgValue, emitAtomicCmpXchgBool,
emitAtomicXchg, and emitAtomicLockRelease.
Also fix a cosmetic issue in errorBuiltinNYI: the diagnostic
hardcoded "X86" in the message even for target-independent builtins
such as __sync_* compiled with --target=aarch64. Replace
"unimplemented X86 builtin call" with "unimplemented builtin call".
Fixes #214445.
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 104 ++++++++++++++++--
.../test/CIR/CodeGenBuiltins/builtin-sync.cpp | 58 ++++++++++
.../CodeGenBuiltins/builtin-undef-rvalue.cpp | 2 +-
3 files changed, 156 insertions(+), 8 deletions(-)
create mode 100644 clang/test/CIR/CodeGenBuiltins/builtin-sync.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index afb13572df5bf..c677dc521ede6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -247,6 +247,79 @@ static RValue emitBinaryAtomicPost(CIRGenFunction &cgf,
return RValue::get(result);
}
+/// Emit a `cir.atomic.cmpxchg` for __sync_val_compare_and_swap_N.
+/// Returns the old value (type matches the pointee of ptr).
+static mlir::Value emitAtomicCmpXchgValue(CIRGenFunction &cgf,
+ const CallExpr *e,
+ cir::MemOrder succOrder,
+ cir::MemOrder failOrder) {
+ Address destAddr = checkAtomicAlignment(cgf, e);
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ mlir::Value destValue = destAddr.emitRawPointer();
+ mlir::Value expected = cgf.emitScalarExpr(e->getArg(1));
+ mlir::Value desired = cgf.emitScalarExpr(e->getArg(2));
+
+ auto cmpxchg = cir::AtomicCmpXchgOp::create(
+ builder, cgf.getLoc(e->getSourceRange()), destValue, expected, desired,
+ succOrder, failOrder, cir::SyncScopeKind::System,
+ /*alignment=*/nullptr, /*weak=*/false, /*is_volatile=*/false);
+ return cmpxchg.getOld();
+}
+
+/// Emit a `cir.atomic.cmpxchg` for __sync_bool_compare_and_swap_N.
+/// Returns a boolean: true if the exchange succeeded.
+static RValue emitAtomicCmpXchgBool(CIRGenFunction &cgf, const CallExpr *e) {
+ Address destAddr = checkAtomicAlignment(cgf, e);
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ mlir::Value destValue = destAddr.emitRawPointer();
+ mlir::Value expected = cgf.emitScalarExpr(e->getArg(1));
+ mlir::Value desired = cgf.emitScalarExpr(e->getArg(2));
+
+ auto cmpxchg = cir::AtomicCmpXchgOp::create(
+ builder, cgf.getLoc(e->getSourceRange()), destValue, expected, desired,
+ cir::MemOrder::SequentiallyConsistent,
+ cir::MemOrder::SequentiallyConsistent, cir::SyncScopeKind::System,
+ /*alignment=*/nullptr, /*weak=*/false, /*is_volatile=*/false);
+
+ // cir.atomic.cmpxchg already returns (old, success). Use the success
+ // flag directly instead of re-emitting getArg(1) and comparing.
+ mlir::Value success = cmpxchg.getSuccess();
+ mlir::Value result =
+ builder.createIntCast(success, cgf.convertType(e->getType()));
+ return RValue::get(result);
+}
+
+/// Emit a `cir.atomic.xchg` for __sync_swap_N and __sync_lock_test_and_set_N.
+static mlir::Value emitAtomicXchg(CIRGenFunction &cgf, const CallExpr *e,
+ cir::MemOrder ordering) {
+ Address destAddr = checkAtomicAlignment(cgf, e);
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ mlir::Value destValue = destAddr.emitRawPointer();
+ mlir::Value val = cgf.emitScalarExpr(e->getArg(1));
+
+ auto xchg = cir::AtomicXchgOp::create(
+ builder, cgf.getLoc(e->getSourceRange()), destValue, val, ordering,
+ cir::SyncScopeKind::System, /*is_volatile=*/false);
+ return xchg.getResult();
+}
+
+/// Emit a release store of 0 for __sync_lock_release_N.
+static void emitAtomicLockRelease(CIRGenFunction &cgf, const CallExpr *e) {
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ Address destAddr = checkAtomicAlignment(cgf, e);
+ mlir::Location loc = cgf.getLoc(e->getSourceRange());
+ mlir::Type pointeeTy = destAddr.getType();
+ mlir::Value zero =
+ builder.getConstant(loc, builder.getZeroInitAttr(pointeeTy));
+ auto orderAttr =
+ cir::MemOrderAttr::get(&cgf.getMLIRContext(), cir::MemOrder::Release);
+ auto scopeAttr = cir::SyncScopeKindAttr::get(&cgf.getMLIRContext(),
+ cir::SyncScopeKind::System);
+ builder.createStore(loc, zero, destAddr, /*isVolatile=*/false,
+ /*isNontemporal=*/false,
+ /*align=*/mlir::IntegerAttr{}, scopeAttr, orderAttr);
+}
+
static void emitAtomicFenceOp(CIRGenFunction &cgf, const CallExpr *expr,
cir::SyncScopeKind syncScope) {
CIRGenBuilderTy &builder = cgf.getBuilder();
@@ -432,11 +505,11 @@ static RValue errorBuiltinNYI(CIRGenFunction &cgf, const CallExpr *e,
if (cgf.getContext().BuiltinInfo.isLibFunction(builtinID)) {
cgf.cgm.errorNYI(
e->getSourceRange(),
- std::string("unimplemented X86 library function builtin call: ") +
+ std::string("unimplemented library function builtin call: ") +
cgf.getContext().BuiltinInfo.getName(builtinID));
} else {
cgf.cgm.errorNYI(e->getSourceRange(),
- std::string("unimplemented X86 builtin call: ") +
+ std::string("unimplemented builtin call: ") +
cgf.getContext().BuiltinInfo.getName(builtinID));
}
@@ -2048,11 +2121,19 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__sync_xor_and_fetch:
case Builtin::BI__sync_nand_and_fetch:
case Builtin::BI__sync_val_compare_and_swap:
+ return RValue::get(
+ emitAtomicCmpXchgValue(*this, e, cir::MemOrder::SequentiallyConsistent,
+ cir::MemOrder::SequentiallyConsistent));
case Builtin::BI__sync_bool_compare_and_swap:
+ return emitAtomicCmpXchgBool(*this, e);
case Builtin::BI__sync_lock_test_and_set:
+ return RValue::get(emitAtomicXchg(*this, e, cir::MemOrder::Acquire));
case Builtin::BI__sync_lock_release:
+ emitAtomicLockRelease(*this, e);
+ return RValue::get(nullptr);
case Builtin::BI__sync_swap:
- return errorBuiltinNYI(*this, e, builtinID);
+ return RValue::get(
+ emitAtomicXchg(*this, e, cir::MemOrder::SequentiallyConsistent));
case Builtin::BI__sync_fetch_and_add_1:
case Builtin::BI__sync_fetch_and_add_2:
case Builtin::BI__sync_fetch_and_add_4:
@@ -2140,26 +2221,35 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
case Builtin::BI__sync_val_compare_and_swap_2:
case Builtin::BI__sync_val_compare_and_swap_4:
case Builtin::BI__sync_val_compare_and_swap_8:
- case Builtin::BI__sync_val_compare_and_swap_16:
+ return RValue::get(
+ emitAtomicCmpXchgValue(*this, e, cir::MemOrder::SequentiallyConsistent,
+ cir::MemOrder::SequentiallyConsistent));
case Builtin::BI__sync_bool_compare_and_swap_1:
case Builtin::BI__sync_bool_compare_and_swap_2:
case Builtin::BI__sync_bool_compare_and_swap_4:
case Builtin::BI__sync_bool_compare_and_swap_8:
- case Builtin::BI__sync_bool_compare_and_swap_16:
+ return emitAtomicCmpXchgBool(*this, e);
case Builtin::BI__sync_swap_1:
case Builtin::BI__sync_swap_2:
case Builtin::BI__sync_swap_4:
case Builtin::BI__sync_swap_8:
- case Builtin::BI__sync_swap_16:
+ return RValue::get(
+ emitAtomicXchg(*this, e, cir::MemOrder::SequentiallyConsistent));
case Builtin::BI__sync_lock_test_and_set_1:
case Builtin::BI__sync_lock_test_and_set_2:
case Builtin::BI__sync_lock_test_and_set_4:
case Builtin::BI__sync_lock_test_and_set_8:
- case Builtin::BI__sync_lock_test_and_set_16:
+ return RValue::get(emitAtomicXchg(*this, e, cir::MemOrder::Acquire));
case Builtin::BI__sync_lock_release_1:
case Builtin::BI__sync_lock_release_2:
case Builtin::BI__sync_lock_release_4:
case Builtin::BI__sync_lock_release_8:
+ emitAtomicLockRelease(*this, e);
+ return RValue::get(nullptr);
+ case Builtin::BI__sync_val_compare_and_swap_16:
+ case Builtin::BI__sync_bool_compare_and_swap_16:
+ case Builtin::BI__sync_swap_16:
+ case Builtin::BI__sync_lock_test_and_set_16:
case Builtin::BI__sync_lock_release_16:
return errorBuiltinNYI(*this, e, builtinID);
case Builtin::BI__sync_synchronize: {
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-sync.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-sync.cpp
new file mode 100644
index 0000000000000..2c59322f3bc86
--- /dev/null
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-sync.cpp
@@ -0,0 +1,58 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+
+extern "C" {
+
+// CIR-LABEL: @test_sync_val_compare_and_swap(
+// CIR: cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) %{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}} : (!cir.ptr<!s32i>, !s32i, !s32i) -> (!s32i, !cir.bool)
+// CIR: cir.return
+int test_sync_val_compare_and_swap(int *p, int old, int newval) {
+ return __sync_val_compare_and_swap(p, old, newval);
+}
+
+// CIR-LABEL: @test_sync_bool_compare_and_swap(
+// CIR: cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) %{{[0-9]+}}, %{{[0-9]+}}, %{{[0-9]+}} : (!cir.ptr<!s32i>, !s32i, !s32i) -> (!s32i, !cir.bool)
+// CIR: cir.return
+int test_sync_bool_compare_and_swap(int *p, int old, int newval) {
+ return __sync_bool_compare_and_swap(p, old, newval);
+}
+
+// CIR-LABEL: @test_sync_swap(
+// CIR: cir.atomic.xchg seq_cst syncscope(system) %{{[0-9]+}}, %{{[0-9]+}} : (!cir.ptr<!s32i>, !s32i) -> !s32i
+// CIR: cir.return
+int test_sync_swap(int *p, int val) {
+ return __sync_swap(p, val);
+}
+
+// CIR-LABEL: @test_sync_lock_test_and_set(
+// CIR: cir.atomic.xchg acquire syncscope(system) %{{[0-9]+}}, %{{[0-9]+}} : (!cir.ptr<!s32i>, !s32i) -> !s32i
+// CIR: cir.return
+int test_sync_lock_test_and_set(int *p, int val) {
+ return __sync_lock_test_and_set(p, val);
+}
+
+// CIR-LABEL: @test_sync_lock_release(
+// CIR: cir.store atomic(release) syncscope(system) %{{[0-9]+}}, %{{[0-9]+}} : !cir.ptr<!s32i>
+// CIR: cir.return
+void test_sync_lock_release(int *p) {
+ __sync_lock_release(p);
+}
+
+}
+
+// LLVM-LABEL: @test_sync_val_compare_and_swap(
+// LLVM: cmpxchg ptr %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}} seq_cst seq_cst, align 4
+
+// LLVM-LABEL: @test_sync_bool_compare_and_swap(
+// LLVM: cmpxchg ptr %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}} seq_cst seq_cst, align 4
+
+// LLVM-LABEL: @test_sync_swap(
+// LLVM: atomicrmw xchg ptr %{{[0-9]+}}, i32 %{{[0-9]+}} seq_cst, align 4
+
+// LLVM-LABEL: @test_sync_lock_test_and_set(
+// LLVM: atomicrmw xchg ptr %{{[0-9]+}}, i32 %{{[0-9]+}} acquire, align 4
+
+// LLVM-LABEL: @test_sync_lock_release(
+// LLVM: store atomic i32 0, ptr %{{[0-9]+}} release, align 4
diff --git a/clang/test/CIR/CodeGenBuiltins/builtin-undef-rvalue.cpp b/clang/test/CIR/CodeGenBuiltins/builtin-undef-rvalue.cpp
index 335383c075dac..dac0ddf366334 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-undef-rvalue.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-undef-rvalue.cpp
@@ -6,7 +6,7 @@
typedef int v4si __attribute__((vector_size(16)));
int test_builtin_reduce_add_undef_rvalue(v4si x) {
- // expected-error at +1 {{unimplemented X86 builtin call: __builtin_reduce_add}}
+ // expected-error at +1 {{unimplemented builtin call: __builtin_reduce_add}}
return __builtin_reduce_add(x);
}
More information about the cfe-commits
mailing list