[clang] bc3eb52 - [CIR] Lower __atomic_is_lock_free / __c11_atomic_is_lock_free (#205862)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 25 10:50:49 PDT 2026
Author: adams381
Date: 2026-06-25T12:50:43-05:00
New Revision: bc3eb524b2d1dcc3e48161cd2eb80a2364fdc097
URL: https://github.com/llvm/llvm-project/commit/bc3eb524b2d1dcc3e48161cd2eb80a2364fdc097
DIFF: https://github.com/llvm/llvm-project/commit/bc3eb524b2d1dcc3e48161cd2eb80a2364fdc097.diff
LOG: [CIR] Lower __atomic_is_lock_free / __c11_atomic_is_lock_free (#205862)
`__atomic_is_lock_free` and `__c11_atomic_is_lock_free` were routed to
`errorNYI` in CIRGen, so `std::atomic<T>::is_lock_free()` failed to
compile under `-fclangir` whenever the query wasn't constant-folded --
which is what the libcxx atomics lock-free tests hit.
This mirrors classic CodeGen (`CGBuiltin.cpp`): emit a call to the
runtime entry `bool __atomic_is_lock_free(size_t size, void *ptr)`.
`__atomic_is_lock_free` forwards its pointer argument;
`__c11_atomic_is_lock_free` passes a null pointer, since an `_Atomic`
object is always suitably aligned. `__atomic_test_and_set` /
`__atomic_clear` stay NYI.
The lowered call omits the `noundef`/`zeroext` argument and return
attributes classic emits, and the declaration picks up `dso_local`.
That's the existing CIR libcall attribute gap, so the test uses split
`LLVMCIR`/`OGCG` prefixes where the two diverge and a shared skeleton
where they match.
Added:
clang/test/CIR/CodeGen/builtin-atomic-is-lock-free.c
Modified:
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Removed:
################################################################################
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 50529a61068d5..ba2dd2dccc63b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -261,6 +261,30 @@ static void emitAtomicFenceOp(CIRGenFunction &cgf, const CallExpr *expr,
emitAtomicOpCallBackFn);
}
+// Emit a runtime call to bool __atomic_is_lock_free(size_t size, void *ptr).
+// For the __c11 builtin the pointer is null, since an _Atomic object is always
+// suitably aligned.
+static RValue emitAtomicIsLockFree(CIRGenFunction &cgf, const CallExpr *e,
+ unsigned builtinID) {
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ mlir::Location loc = cgf.getLoc(e->getExprLoc());
+
+ mlir::Type sizeTy = cgf.convertType(cgf.getContext().getSizeType());
+ mlir::Value size = cgf.emitScalarExpr(e->getArg(0));
+ mlir::Value ptr;
+ if (builtinID == Builtin::BI__atomic_is_lock_free)
+ ptr = builder.createBitcast(cgf.emitScalarExpr(e->getArg(1)),
+ builder.getVoidPtrTy());
+ else
+ ptr = builder.getNullPtr(builder.getVoidPtrTy(), loc);
+
+ cir::FuncOp func = cgf.cgm.createRuntimeFunction(
+ cir::FuncType::get({sizeTy, builder.getVoidPtrTy()}, builder.getBoolTy()),
+ "__atomic_is_lock_free");
+ return RValue::get(
+ builder.createCallOp(loc, func, mlir::ValueRange{size, ptr}).getResult());
+}
+
namespace {
struct WidthAndSignedness {
unsigned width;
@@ -2182,6 +2206,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
}
case Builtin::BI__c11_atomic_is_lock_free:
case Builtin::BI__atomic_is_lock_free:
+ return emitAtomicIsLockFree(*this, e, builtinID);
case Builtin::BI__atomic_test_and_set:
case Builtin::BI__atomic_clear:
return errorBuiltinNYI(*this, e, builtinID);
diff --git a/clang/test/CIR/CodeGen/builtin-atomic-is-lock-free.c b/clang/test/CIR/CodeGen/builtin-atomic-is-lock-free.c
new file mode 100644
index 0000000000000..2792f595275a6
--- /dev/null
+++ b/clang/test/CIR/CodeGen/builtin-atomic-is-lock-free.c
@@ -0,0 +1,46 @@
+// 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-prefixes=LLVM,LLVMCIR --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,OGCG --input-file=%t.ll %s
+
+typedef struct { char buf[24]; } Big;
+
+int test_atomic(Big *p) { return __atomic_is_lock_free(sizeof(Big), p); }
+
+int test_c11(void) { return __c11_atomic_is_lock_free(sizeof(Big)); }
+
+int test_atomic_var(unsigned long n, void *p) {
+ return __atomic_is_lock_free(n, p);
+}
+
+// CIR-DAG: cir.func private dso_local @__atomic_is_lock_free(!u64i, !cir.ptr<!void>) -> !cir.bool
+
+// CIR-LABEL: cir.func{{.*}} @test_atomic(
+// CIR: %[[SZ:.*]] = cir.const #cir.int<24> : !u64i
+// CIR: %[[P:.*]] = cir.load{{.*}} : !cir.ptr<!cir.ptr<!rec_Big>>, !cir.ptr<!rec_Big>
+// CIR: %[[VP:.*]] = cir.cast bitcast %[[P]] : !cir.ptr<!rec_Big> -> !cir.ptr<!void>
+// CIR: cir.call @__atomic_is_lock_free(%[[SZ]], %[[VP]]) : (!u64i, !cir.ptr<!void>) -> !cir.bool
+
+// CIR-LABEL: cir.func{{.*}} @test_c11(
+// CIR: %[[SZ2:.*]] = cir.const #cir.int<24> : !u64i
+// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
+// CIR: cir.call @__atomic_is_lock_free(%[[SZ2]], %[[NULL]]) : (!u64i, !cir.ptr<!void>) -> !cir.bool
+
+// CIR-LABEL: cir.func{{.*}} @test_atomic_var(
+// CIR: cir.call @__atomic_is_lock_free(%{{.*}}, %{{.*}}) : (!u64i, !cir.ptr<!void>) -> !cir.bool
+
+// LLVM-LABEL: define dso_local i32 @test_atomic(
+// LLVMCIR: call i1 @__atomic_is_lock_free(i64 24, ptr %{{.*}})
+// OGCG: call zeroext i1 @__atomic_is_lock_free(i64 noundef 24, ptr noundef %{{.*}})
+// LLVM: zext i1 %{{.*}} to i32
+
+// LLVM-LABEL: define dso_local i32 @test_c11(
+// LLVMCIR: call i1 @__atomic_is_lock_free(i64 24, ptr null)
+// OGCG: call zeroext i1 @__atomic_is_lock_free(i64 noundef 24, ptr noundef null)
+// LLVM: zext i1 %{{.*}} to i32
+
+// LLVM-LABEL: define dso_local i32 @test_atomic_var(
+// LLVMCIR: call i1 @__atomic_is_lock_free(i64 %{{.*}}, ptr %{{.*}})
+// OGCG: call zeroext i1 @__atomic_is_lock_free(i64 noundef %{{.*}}, ptr noundef %{{.*}})
More information about the cfe-commits
mailing list