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

Reply via email to