[clang] [CIR][CIRGen] Add support for `__sync_val/bool_compare_and_swap` builtins (PR #186529)
Ayokunle Amodu via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 13 16:00:17 PDT 2026
https://github.com/ayokunle321 created https://github.com/llvm/llvm-project/pull/186529
Adds CIRGen support for `__sync_val_compare_and_swap` and `__sync_bool_compare_and_swap` builtins.
>From c8f847bfd78a5502438784f5a147c5d8a0ef5c03 Mon Sep 17 00:00:00 2001
From: Ayokunle Amodu <ayokunle321 at gmail.com>
Date: Fri, 13 Mar 2026 16:54:44 -0600
Subject: [PATCH] add support for atomic xchg
---
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 33 +++++++++
clang/test/CIR/CodeGen/atomic.c | 93 +++++++++++++++++++++++++
2 files changed, 126 insertions(+)
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 5d9ddbe5c3f22..77a20e56a5936 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -172,6 +172,37 @@ static mlir::Value makeBinaryAtomicValue(
return rmwi->getResult(0);
}
+/// Utility to insert an atomic cmpxchg instruction.
+static mlir::Value makeAtomicCmpXchgValue(CIRGenFunction &cgf,
+ const CallExpr *expr,
+ bool returnBool) {
+ QualType typ = returnBool ? expr->getArg(1)->getType() : expr->getType();
+ Address destAddr = checkAtomicAlignment(cgf, expr);
+ clang::CIRGen::CIRGenBuilderTy &builder = cgf.getBuilder();
+
+ cir::IntType intType =
+ expr->getArg(0)->getType()->getPointeeType()->isUnsignedIntegerType()
+ ? builder.getUIntNTy(cgf.getContext().getTypeSize(typ))
+ : builder.getSIntNTy(cgf.getContext().getTypeSize(typ));
+ mlir::Value cmpVal = cgf.emitScalarExpr(expr->getArg(1));
+ cmpVal = emitToInt(cgf, cmpVal, typ, intType);
+ mlir::Value newVal =
+ emitToInt(cgf, cgf.emitScalarExpr(expr->getArg(2)), typ, intType);
+
+ cir::AtomicCmpXchgOp op = cir::AtomicCmpXchgOp::create(
+ builder, cgf.getLoc(expr->getSourceRange()), cmpVal.getType(),
+ builder.getBoolTy(), destAddr.getPointer(), cmpVal, newVal,
+ cir::MemOrderAttr::get(&cgf.getMLIRContext(),
+ cir::MemOrder::SequentiallyConsistent),
+ cir::MemOrderAttr::get(&cgf.getMLIRContext(),
+ cir::MemOrder::SequentiallyConsistent),
+ cir::SyncScopeKindAttr::get(&cgf.getMLIRContext(),
+ cir::SyncScopeKind::System),
+ builder.getI64IntegerAttr(destAddr.getAlignment().getAsAlign().value()));
+
+ return returnBool ? op.getResult(1) : op.getResult(0);
+}
+
static RValue emitBinaryAtomic(CIRGenFunction &cgf,
cir::AtomicFetchKind atomicOpkind,
const CallExpr *e) {
@@ -1818,11 +1849,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
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(makeAtomicCmpXchgValue(*this, e, false));
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 RValue::get(makeAtomicCmpXchgValue(*this, e, true));
case Builtin::BI__sync_swap_1:
case Builtin::BI__sync_swap_2:
case Builtin::BI__sync_swap_4:
diff --git a/clang/test/CIR/CodeGen/atomic.c b/clang/test/CIR/CodeGen/atomic.c
index 200ebc549ef2b..8b0191a49876f 100644
--- a/clang/test/CIR/CodeGen/atomic.c
+++ b/clang/test/CIR/CodeGen/atomic.c
@@ -5,6 +5,8 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+#include <stdbool.h>
+
void f1(void) {
_Atomic(int) x = 42;
}
@@ -2951,3 +2953,94 @@ int atomic_load_and_store_dynamic_order(int *ptr, int order) {
// OGCG: [[CONTINUE_BLK]]:
// OGCG-NEXT: %{{.+}} = load i32, ptr %[[RES_SLOT]], align 4
}
+
+void cmp_bool_int(int* p, int x, int u) {
+ // CIR-LABEL: @cmp_bool_int
+ // CIR: %[[PTR:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
+ // CIR: %[[CMP:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!s32i>, !s32i
+ // CIR: %[[UPD:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!s32i>, !s32i
+ // CIR: %[[OLD:.*]], %[[RES:.*]] = cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) %[[PTR]], %[[CMP]], %[[UPD]] align(4) : (!cir.ptr<!s32i>, !s32i, !s32i) -> (!s32i, !cir.bool)
+ // CIR: cir.store{{.*}} %[[RES]], {{.*}} : !cir.bool, !cir.ptr<!cir.bool>
+
+ // LLVM-LABEL: @cmp_bool_int
+ // LLVM: %[[PTR:.*]] = load ptr
+ // LLVM: %[[CMP:.*]] = load i32
+ // LLVM: %[[UPD:.*]] = load i32
+ // LLVM: %[[RES:.*]] = cmpxchg ptr %[[PTR]], i32 %[[CMP]], i32 %[[UPD]] seq_cst seq_cst, align 4
+ // LLVM: %[[TMP:.*]] = extractvalue { i32, i1 } %[[RES]], 1
+ // LLVM: %[[EXT:.*]] = zext i1 %[[TMP]] to i8
+ // LLVM: store i8 %[[EXT]], ptr {{.*}}
+ bool r = __sync_bool_compare_and_swap(p, x, u);
+}
+
+void cmp_bool_long(long* p, long x, long u) {
+ // CIR-LABEL: @cmp_bool_long
+ // CIR: cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) {{.*}}, {{.*}}, {{.*}} align(8) : (!cir.ptr<!s64i>, !s64i, !s64i) -> (!s64i, !cir.bool)
+
+ // LLVM-LABEL: @cmp_bool_long
+ // LLVM: cmpxchg ptr {{.*}}, i64 {{.*}}, i64 {{.*}} seq_cst seq_cst, align 8
+ bool r = __sync_bool_compare_and_swap(p, x, u);
+}
+
+void cmp_bool_short(short* p, short x, short u) {
+ // CIR-LABEL: @cmp_bool_short
+ // CIR: cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) {{.*}}, {{.*}}, {{.*}} align(2) : (!cir.ptr<!s16i>, !s16i, !s16i) -> (!s16i, !cir.bool)
+
+ // LLVM-LABEL: @cmp_bool_short
+ // LLVM: cmpxchg ptr {{.*}}, i16 {{.*}}, i16 {{.*}} seq_cst seq_cst, align 2
+ bool r = __sync_bool_compare_and_swap(p, x, u);
+}
+
+void cmp_bool_byte(char* p, char x, char u) {
+ // CIR-LABEL: @cmp_bool_byte
+ // CIR: cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) {{.*}}, {{.*}}, {{.*}} align(1) : (!cir.ptr<!s8i>, !s8i, !s8i) -> (!s8i, !cir.bool)
+
+ // LLVM-LABEL: @cmp_bool_byte
+ // LLVM: cmpxchg ptr {{.*}}, i8 {{.*}}, i8 {{.*}} seq_cst seq_cst, align 1
+ bool r = __sync_bool_compare_and_swap(p, x, u);
+}
+
+void cmp_val_int(int* p, int x, int u) {
+ // CIR-LABEL: @cmp_val_int
+ // CIR: %[[PTR:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
+ // CIR: %[[CMP:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!s32i>, !s32i
+ // CIR: %[[UPD:.*]] = cir.load{{.*}} {{.*}} : !cir.ptr<!s32i>, !s32i
+ // CIR: %[[OLD:.*]], %[[RES:.*]] = cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) %[[PTR]], %[[CMP]], %[[UPD]] align(4) : (!cir.ptr<!s32i>, !s32i, !s32i) -> (!s32i, !cir.bool)
+ // CIR: cir.store{{.*}} %[[OLD]], {{.*}} : !s32i, !cir.ptr<!s32i>
+
+ // LLVM-LABEL: @cmp_val_int
+ // LLVM: %[[PTR:.*]] = load ptr
+ // LLVM: %[[CMP:.*]] = load i32
+ // LLVM: %[[UPD:.*]] = load i32
+ // LLVM: %[[RES:.*]] = cmpxchg ptr %[[PTR]], i32 %[[CMP]], i32 %[[UPD]] seq_cst seq_cst, align 4
+ // LLVM: %[[TMP:.*]] = extractvalue { i32, i1 } %[[RES]], 0
+ // LLVM: store i32 %[[TMP]], ptr {{.*}}
+ int r = __sync_val_compare_and_swap(p, x, u);
+}
+
+void cmp_val_long(long* p, long x, long u) {
+ // CIR-LABEL: @cmp_val_long
+ // CIR: cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) {{.*}}, {{.*}}, {{.*}} align(8) : (!cir.ptr<!s64i>, !s64i, !s64i) -> (!s64i, !cir.bool)
+
+ // LLVM-LABEL: @cmp_val_long
+ // LLVM: cmpxchg ptr {{.*}}, i64 {{.*}}, i64 {{.*}} seq_cst seq_cst, align 8
+ long r = __sync_val_compare_and_swap(p, x, u);
+}
+
+void cmp_val_short(short* p, short x, short u) {
+ // CIR-LABEL: @cmp_val_short
+ // CIR: cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) {{.*}}, {{.*}}, {{.*}} align(2) : (!cir.ptr<!s16i>, !s16i, !s16i) -> (!s16i, !cir.bool)
+
+ // LLVM-LABEL: @cmp_val_short
+ // LLVM: cmpxchg ptr {{.*}}, i16 {{.*}}, i16 {{.*}} seq_cst seq_cst, align 2
+ short r = __sync_val_compare_and_swap(p, x, u);
+}
+
+void cmp_val_byte(char* p, char x, char u) {
+ // CIR-LABEL: @cmp_val_byte
+ // CIR: cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) {{.*}}, {{.*}}, {{.*}} align(1) : (!cir.ptr<!s8i>, !s8i, !s8i) -> (!s8i, !cir.bool)
+
+ // LLVM-LABEL: @cmp_val_byte
+ // LLVM: cmpxchg ptr {{.*}}, i8 {{.*}}, i8 {{.*}} seq_cst seq_cst, align 1
+ char r = __sync_val_compare_and_swap(p, x, u);
+}
More information about the cfe-commits
mailing list