https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/229423
>From 8096cd56ac7c828de75a146c5b22aecca2958796 Mon Sep 17 00:00:00 2001 From: Sirui Mu <[email protected]> Date: Tue, 6 Oct 2026 21:34:12 +0800 Subject: [PATCH] [CIR] CIRGen via libcall for OpenCL atomic builtins This patch adds CIRGen support for OpenCL atomic builtins that would go through libcalls. Assisted-by: codex / gpt-6.1 sol light --- clang/lib/CIR/CodeGen/CIRGenAtomic.cpp | 46 ++++++------ .../test/CIR/CodeGen/atomic-libcall-opencl.c | 70 +++++++++++++++++++ 2 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 clang/test/CIR/CodeGen/atomic-libcall-opencl.c diff --git a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp index d974f0792d481..a0bf0dab88627 100644 --- a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp @@ -1183,8 +1183,8 @@ static RValue emitAtomicLibCall(CIRGenFunction &cgf, llvm::StringRef funcName, static RValue emitLibCallForAtomicExpr(CIRGenFunction &cgf, AtomicExpr *e, Address atomicPtr, Address dest, Address val1, Address val2, - uint64_t atomicTySize, - QualType resultTy) { + uint64_t atomicTySize, QualType resultTy, + mlir::Value scope) { mlir::Location loc = cgf.getLoc(e->getSourceRange()); CallArgList args; @@ -1197,12 +1197,20 @@ static RValue emitLibCallForAtomicExpr(CIRGenFunction &cgf, AtomicExpr *e, // The OpenCL atomic library functions only accept pointer arguments to // generic address space. auto castToGenericAddrSpace = [&](mlir::Value v, QualType pt) { - if (!e->isOpenCL()) - return cgf.getBuilder().createPtrBitcast(v, cgf.voidTy); - - assert(!cir::MissingFeatures::openCL()); - cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: openCL"); - return cgf.getBuilder().createPtrBitcast(v, cgf.voidTy); + v = cgf.getBuilder().createPtrBitcast(v, cgf.voidTy); + if (!e->isOpenCL() || !pt->isPointerType()) + return v; + + LangAS addrSpace = + pt->castAs<clang::PointerType>()->getPointeeType().getAddressSpace(); + if (addrSpace == LangAS::opencl_generic) + return v; + + auto destAddrSpace = cir::TargetAddressSpaceAttr::get( + &cgf.getMLIRContext(), + cgf.getContext().getTargetAddressSpace(LangAS::opencl_generic)); + mlir::Type destType = cir::PointerType::get(cgf.voidTy, destAddrSpace); + return cgf.performAddrSpaceCast(v, destType); }; args.add(RValue::get(castToGenericAddrSpace(atomicPtr.emitRawPointer(), e->getPtr()->getType())), @@ -1211,7 +1219,7 @@ static RValue emitLibCallForAtomicExpr(CIRGenFunction &cgf, AtomicExpr *e, mlir::Value order = cgf.emitScalarExpr(e->getOrder()); // The next 1-3 parameters are op-dependent. - llvm::StringRef calleeName; + std::string calleeName; QualType retTy; bool hasRetTy = false; switch (e->getOp()) { @@ -1362,27 +1370,23 @@ static RValue emitLibCallForAtomicExpr(CIRGenFunction &cgf, AtomicExpr *e, llvm_unreachable("Integral atomic operations always become atomicrmw!"); } - if (e->isOpenCL()) { - assert(!cir::MissingFeatures::openCL()); - cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: openCL"); - return RValue::get(nullptr); - } + if (e->isOpenCL()) + calleeName = + std::string("__opencl") + StringRef(calleeName).drop_front(1).str(); // By default, assume we return a value of the atomic type. if (!hasRetTy) { // Value is returned through parameter before the order. retTy = cgf.getContext().VoidTy; - args.add(RValue::get(castToGenericAddrSpace(dest.emitRawPointer(), retTy)), + args.add(RValue::get(castToGenericAddrSpace(dest.emitRawPointer(), + cgf.getContext().VoidPtrTy)), cgf.getContext().VoidPtrTy); } // Order is always the last parameter. args.add(RValue::get(order), cgf.getContext().IntTy); - if (e->isOpenCL()) { - assert(!cir::MissingFeatures::openCL()); - cgf.cgm.errorNYI(loc, "emitLibCallForAtomicExpr: openCL"); - return RValue::get(nullptr); - } + if (e->isOpenCL()) + args.add(RValue::get(scope), cgf.getContext().IntTy); RValue res = emitAtomicLibCall(cgf, calleeName, retTy, args); @@ -1646,7 +1650,7 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) { // See: https://llvm.org/docs/Atomics.html#libcalls-atomic if (useLibCall) return emitLibCallForAtomicExpr(*this, e, ptr, dest, val1, val2, size, - resultTy); + resultTy, scope); bool isStore = e->getOp() == AtomicExpr::AO__c11_atomic_store || e->getOp() == AtomicExpr::AO__opencl_atomic_store || diff --git a/clang/test/CIR/CodeGen/atomic-libcall-opencl.c b/clang/test/CIR/CodeGen/atomic-libcall-opencl.c new file mode 100644 index 0000000000000..71791f92ab75c --- /dev/null +++ b/clang/test/CIR/CodeGen/atomic-libcall-opencl.c @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-atomic-alignment -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-atomic-alignment -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM + +// A 24-byte atomic forces frontend libcalls. OpenCL libcalls use the +// __opencl_atomic_ prefix and take the memory scope after the memory orders. +struct Big { + int x[6]; +}; + +// CIR-LABEL: @load( +// CIR-DAG: %[[ORDER:.+]] = cir.const #cir.int<5> : !s32i +// CIR-DAG: %[[SCOPE:.+]] = cir.const #cir.int<2> : !s32i +// CIR: cir.call @__opencl_atomic_load(%{{.+}}, %{{.+}}, %{{.+}}, %[[ORDER]], %[[SCOPE]]) +// LLVM-LABEL: @load( +// LLVM: call void @__opencl_atomic_load(i64 noundef 24, ptr noundef %{{.+}}, ptr noundef %{{.+}}, i32 noundef 5, i32 noundef 2) +struct Big load(_Atomic(struct Big) *p) { + return __opencl_atomic_load(p, __ATOMIC_SEQ_CST, __OPENCL_MEMORY_SCOPE_DEVICE); +} + +// CIR-LABEL: @store( +// CIR-DAG: %[[ORDER:.+]] = cir.const #cir.int<3> : !s32i +// CIR-DAG: %[[SCOPE:.+]] = cir.const #cir.int<1> : !s32i +// CIR: cir.call @__opencl_atomic_store(%{{.+}}, %{{.+}}, %{{.+}}, %[[ORDER]], %[[SCOPE]]) +// LLVM-LABEL: @store( +// LLVM: call void @__opencl_atomic_store(i64 noundef 24, ptr noundef %{{.+}}, ptr noundef %{{.+}}, i32 noundef 3, i32 noundef 1) +void store(_Atomic(struct Big) *p, struct Big v) { + __opencl_atomic_store(p, v, __ATOMIC_RELEASE, __OPENCL_MEMORY_SCOPE_WORK_GROUP); +} + +// CIR-LABEL: @exchange( +// CIR-DAG: %[[SCOPE:.+]] = cir.load {{.*}} : !cir.ptr<!s32i>, !s32i +// CIR-DAG: %[[ORDER:.+]] = cir.const #cir.int<4> : !s32i +// CIR: cir.call @__opencl_atomic_exchange(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %[[ORDER]], %[[SCOPE]]) +// LLVM-LABEL: @exchange( +// LLVM: call void @__opencl_atomic_exchange(i64 noundef 24, ptr noundef %{{.+}}, ptr noundef %{{.+}}, ptr noundef %{{.+}}, i32 noundef 4, i32 noundef %{{.+}}) +struct Big exchange(_Atomic(struct Big) *p, struct Big v, int scope) { + return __opencl_atomic_exchange(p, v, __ATOMIC_ACQ_REL, scope); +} + +// CIR-LABEL: @cmpxchg_strong( +// CIR-DAG: %[[SUCCESS:.+]] = cir.const #cir.int<4> : !s32i +// CIR-DAG: %[[FAILURE:.+]] = cir.const #cir.int<2> : !s32i +// CIR-DAG: %[[SCOPE:.+]] = cir.const #cir.int<3> : !s32i +// CIR: cir.call @__opencl_atomic_compare_exchange(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %[[SUCCESS]], %[[FAILURE]], %[[SCOPE]]) +// LLVM-LABEL: @cmpxchg_strong( +// LLVM: call zeroext i1 @__opencl_atomic_compare_exchange(i64 noundef 24, ptr noundef %{{.+}}, ptr noundef %{{.+}}, ptr noundef %{{.+}}, i32 noundef 4, i32 noundef 2, i32 noundef 3) +_Bool cmpxchg_strong(_Atomic(struct Big) *p, struct Big *expected, + struct Big desired) { + return __opencl_atomic_compare_exchange_strong( + p, expected, desired, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE, + __OPENCL_MEMORY_SCOPE_ALL_SVM_DEVICES); +} + +// CIR-LABEL: @cmpxchg_weak( +// CIR-DAG: %[[SUCCESS_SLOT:.+]] = cir.alloca "success" {{.*}} : !cir.ptr<!s32i> +// CIR-DAG: %[[FAILURE_SLOT:.+]] = cir.alloca "failure" {{.*}} : !cir.ptr<!s32i> +// CIR-DAG: %[[SCOPE_SLOT:.+]] = cir.alloca "scope" {{.*}} : !cir.ptr<!s32i> +// CIR-DAG: %[[SUCCESS:.+]] = cir.load {{.*}} %[[SUCCESS_SLOT]] : !cir.ptr<!s32i>, !s32i +// CIR-DAG: %[[FAILURE:.+]] = cir.load {{.*}} %[[FAILURE_SLOT]] : !cir.ptr<!s32i>, !s32i +// CIR-DAG: %[[SCOPE:.+]] = cir.load {{.*}} %[[SCOPE_SLOT]] : !cir.ptr<!s32i>, !s32i +// CIR: cir.call @__opencl_atomic_compare_exchange(%{{.+}}, %{{.+}}, %{{.+}}, %{{.+}}, %[[SUCCESS]], %[[FAILURE]], %[[SCOPE]]) +// LLVM-LABEL: @cmpxchg_weak( +// LLVM: call zeroext i1 @__opencl_atomic_compare_exchange(i64 noundef 24, ptr noundef %{{.+}}, ptr noundef %{{.+}}, ptr noundef %{{.+}}, i32 noundef %{{.+}}, i32 noundef %{{.+}}, i32 noundef %{{.+}}) +_Bool cmpxchg_weak(_Atomic(struct Big) *p, struct Big *expected, + struct Big desired, int success, int failure, int scope) { + return __opencl_atomic_compare_exchange_weak(p, expected, desired, success, + failure, scope); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
