[clang] [CIR][CUDA] Add host-side surface registration support (PR #225146)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 23 04:47:20 PDT 2026
https://github.com/Siya-05 updated https://github.com/llvm/llvm-project/pull/225146
>From 83e8ec141a3e5e2fda507d73ad33fce1b5fed023 Mon Sep 17 00:00:00 2001
From: Sivapriya <sivapriyaofficial2005 at gmail.com>
Date: Mon, 21 Sep 2026 17:06:17 +0000
Subject: [PATCH 1/2] [CIR][CUDA] Add host-side surface registration support
---
.../clang/CIR/Dialect/IR/CIRCUDAAttrs.td | 3 +-
clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp | 34 +++++++----
clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 10 +++-
.../Dialect/Transforms/LoweringPrepare.cpp | 58 +++++++++++++------
clang/test/CIR/CodeGenCUDA/surface.cu | 49 ++++++++++++++++
5 files changed, 121 insertions(+), 33 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRCUDAAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRCUDAAttrs.td
index d993e1b2b11ebb..c6f8afd8d4b306 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRCUDAAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRCUDAAttrs.td
@@ -89,7 +89,8 @@ def CIR_CUDAVarRegistrationInfoAttr : CIR_Attr<"CUDAVarRegistrationInfo", "cu.va
// handleVarRegistration via hasAttr<HIPManagedAttr>().
"bool":$isExtern,
"bool":$isConstant,
- "bool":$isManaged
+ "bool":$isManaged,
+ "int32_t":$surfaceType
);
let hasCustomAssemblyFormat = 1;
diff --git a/clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp b/clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp
index ab4baf336d379a..ac81ac3261a541 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp
@@ -93,7 +93,8 @@ class CIRGenNVCUDARuntime : public CIRGenCUDARuntime {
builder.getContext(),
getDeviceSideName(cast<NamedDecl>(vd)),
cir::CUDADeviceVarKind::Variable, isExtern, isConstant,
- vd->hasAttr<HIPManagedAttr>()));
+ vd->hasAttr<HIPManagedAttr>(),
+ /*surfaceType=*/0));
deviceVars.push_back({
var,
vd,
@@ -101,8 +102,8 @@ class CIRGenNVCUDARuntime : public CIRGenCUDARuntime {
});
}
- void registerDeviceSurf(const VarDecl *vd, cir::GlobalOp &var,
- bool isExtern) {
+ void registerDeviceSurf(const VarDecl *vd, cir::GlobalOp &var, bool isExtern,
+ int32_t surfaceType) {
auto &builder = cgm.getBuilder();
var->setAttr(cir::CUDAVarRegistrationInfoAttr::getMnemonic(),
@@ -111,7 +112,7 @@ class CIRGenNVCUDARuntime : public CIRGenCUDARuntime {
getDeviceSideName(cast<NamedDecl>(vd)),
cir::CUDADeviceVarKind::Surface, isExtern,
/*isConstant=*/false,
- /*isManaged=*/false));
+ /*isManaged=*/false, surfaceType));
deviceVars.push_back({
var,
@@ -129,7 +130,8 @@ class CIRGenNVCUDARuntime : public CIRGenCUDARuntime {
getDeviceSideName(cast<NamedDecl>(vd)),
cir::CUDADeviceVarKind::Texture, isExtern,
/*isConstant=*/false,
- /*isManaged=*/false));
+ /*isManaged=*/false,
+ /*surfaceType=*/0));
deviceVars.push_back({
var,
@@ -430,14 +432,11 @@ void CIRGenNVCUDARuntime::internalizeDeviceSideVar(
// counterparts. It's not clear yet whether it's nvcc's bug or
// a feature, but we've got to do the same for compatibility.
if (d->hasAttr<CUDADeviceAttr>() || d->hasAttr<CUDAConstantAttr>() ||
- d->hasAttr<CUDASharedAttr>()) {
+ d->hasAttr<CUDASharedAttr>() ||
+ d->getType()->isCUDADeviceBuiltinSurfaceType() ||
+ d->getType()->isCUDADeviceBuiltinTextureType()) {
linkage = cir::GlobalLinkageKind::InternalLinkage;
}
-
- if (d->getType()->isCUDADeviceBuiltinSurfaceType() ||
- d->getType()->isCUDADeviceBuiltinTextureType())
- cgm.errorNYI(d->getSourceRange(),
- "internalizeDeviceSideVar: CUDA Surface/Texture support");
}
std::string CIRGenNVCUDARuntime::getDeviceSideName(const NamedDecl *nd) {
@@ -498,8 +497,19 @@ void CIRGenNVCUDARuntime::handleVarRegistration(const VarDecl *vd,
} else if (vd->getType()->isCUDADeviceBuiltinSurfaceType()) {
// Builtin surfaces and their template arguments are also registered
// with CUDA runtime.
+ const auto *td = cast<ClassTemplateSpecializationDecl>(
+ vd->getType()->castAsCXXRecordDecl());
+ const TemplateArgumentList &args = td->getTemplateArgs();
+
+ assert(args.size() == 2 &&
+ "Unexpected number of template arguments of CUDA device "
+ "builtin surface type.");
+
+ auto surfaceType = args[1].getAsIntegral();
+
if (!vd->hasExternalStorage())
- registerDeviceSurf(vd, var, !vd->hasDefinition());
+ registerDeviceSurf(vd, var, !vd->hasDefinition(),
+ surfaceType.getSExtValue());
} else if (vd->getType()->isCUDADeviceBuiltinTextureType()) {
// Builtin textures and their template arguments are also registered
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 05408c8b2f2e99..049797e7ab5c9f 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -519,6 +519,8 @@ void CUDAVarRegistrationInfoAttr::print(AsmPrinter &p) const {
p << ", constant";
if (getIsManaged())
p << ", managed";
+ if (getKind() == CUDADeviceVarKind::Surface)
+ p << ", surface_type = " << getSurfaceType();
p << ">";
}
@@ -553,6 +555,7 @@ Attribute CUDAVarRegistrationInfoAttr::parse(AsmParser &parser, Type odsType) {
bool isExtern = false;
bool isConstant = false;
bool isManaged = false;
+ int32_t surfaceType = 0;
while (parser.parseOptionalGreater().failed()) {
if (parser.parseComma())
@@ -568,14 +571,17 @@ Attribute CUDAVarRegistrationInfoAttr::parse(AsmParser &parser, Type odsType) {
isConstant = true;
else if (flag == "managed")
isManaged = true;
- else {
+ else if (flag == "surface_type") {
+ if (parser.parseEqual() || parser.parseInteger(surfaceType))
+ return {};
+ } else {
parser.emitError(parser.getCurrentLocation(), "unknown flag: ") << flag;
return {};
}
}
return get(parser.getContext(), deviceSideName, *kind, isExtern, isConstant,
- isManaged);
+ isManaged, surfaceType);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index a155bd4661ce3a..b6c22ed0b2f14e 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -2992,6 +2992,16 @@ void LoweringPreparePass::buildCUDARegisterVars(cir::CIRBaseBuilderTy &builder,
FuncType::get({voidPtrPtrTy, voidPtrTy, voidPtrTy, voidPtrTy, intTy,
sizeTy, intTy, intTy},
voidTy));
+ // void __cudaRegisterSurface(void **fatbinHandle,
+ // const struct surfaceReference *hostVar,
+ // const void **deviceAddress,
+ // const char *deviceName,
+ // int type, int ext);
+ FuncOp cudaRegisterSurface = buildRuntimeFunction(
+ globalBuilder, addUnderscoredPrefix(cudaPrefix, "RegisterSurface"), loc,
+ FuncType::get(
+ {voidPtrPtrTy, voidPtrTy, voidPtrTy, voidPtrTy, intTy, intTy},
+ voidTy));
auto makeConstantString = [&](llvm::StringRef str) -> GlobalOp {
auto strType = ArrayType::get(&getContext(), charTy, 1 + str.size());
@@ -3008,15 +3018,6 @@ void LoweringPreparePass::buildCUDARegisterVars(cir::CIRBaseBuilderTy &builder,
mlir::Value fatbinHandle = *regGlobalFunc.args_begin();
for (auto &[global, regAttr] : cudaDeviceVars) {
- switch (regAttr.getKind()) {
- case cir::CUDADeviceVarKind::Variable:
- break;
- case cir::CUDADeviceVarKind::Surface:
- llvm_unreachable("Surface registration NYI");
- case cir::CUDADeviceVarKind::Texture:
- llvm_unreachable("Texture registration NYI");
- }
-
if (regAttr.getIsManaged())
llvm_unreachable("Managed variable registration NYI");
@@ -3028,15 +3029,36 @@ void LoweringPreparePass::buildCUDARegisterVars(cir::CIRBaseBuilderTy &builder,
auto isExtern = ConstantOp::create(
builder, loc, IntAttr::get(intTy, regAttr.getIsExtern() ? 1 : 0));
- llvm::TypeSize size = dataLayout.getTypeAllocSize(global.getSymType());
- auto varSize = ConstantOp::create(
- builder, loc, IntAttr::get(sizeTy, size.getFixedValue()));
- auto isConstant = ConstantOp::create(
- builder, loc, IntAttr::get(intTy, regAttr.getIsConstant() ? 1 : 0));
- auto normalized = ConstantOp::create(builder, loc, IntAttr::get(intTy, 0));
- builder.createCallOp(loc, cudaRegisterVar,
- {fatbinHandle, hostVar, deviceName, deviceName,
- isExtern, varSize, isConstant, normalized});
+
+ switch (regAttr.getKind()) {
+ case cir::CUDADeviceVarKind::Variable: {
+ llvm::TypeSize size = dataLayout.getTypeAllocSize(global.getSymType());
+ auto varSize = ConstantOp::create(
+ builder, loc, IntAttr::get(sizeTy, size.getFixedValue()));
+ auto isConstant = ConstantOp::create(
+ builder, loc, IntAttr::get(intTy, regAttr.getIsConstant() ? 1 : 0));
+ auto normalized =
+ ConstantOp::create(builder, loc, IntAttr::get(intTy, 0));
+
+ builder.createCallOp(loc, cudaRegisterVar,
+ {fatbinHandle, hostVar, deviceName, deviceName,
+ isExtern, varSize, isConstant, normalized});
+ break;
+ }
+
+ case cir::CUDADeviceVarKind::Surface: {
+ auto surfaceType = ConstantOp::create(
+ builder, loc, IntAttr::get(intTy, regAttr.getSurfaceType()));
+
+ builder.createCallOp(loc, cudaRegisterSurface,
+ {fatbinHandle, hostVar, deviceName, deviceName,
+ surfaceType, isExtern});
+ break;
+ }
+
+ case cir::CUDADeviceVarKind::Texture:
+ llvm_unreachable("Texture registration NYI");
+ }
}
}
diff --git a/clang/test/CIR/CodeGenCUDA/surface.cu b/clang/test/CIR/CodeGenCUDA/surface.cu
index 67d7257c07c6e5..619b34ffde48dc 100644
--- a/clang/test/CIR/CodeGenCUDA/surface.cu
+++ b/clang/test/CIR/CodeGenCUDA/surface.cu
@@ -1,9 +1,21 @@
// REQUIRES: x86-registered-target
// REQUIRES: nvptx-registered-target
+
// RUN: %clang_cc1 -fclangir -std=c++11 -fcuda-is-device -triple nvptx64-nvidia-cuda -emit-cir -o - %s | FileCheck --check-prefix=CIR-DEVICE %s
// RUN: %clang_cc1 -fclangir -std=c++11 -fcuda-is-device -triple nvptx64-nvidia-cuda -emit-llvm -o - %s | FileCheck --check-prefix=LLVM-DEVICE %s
// RUN: %clang_cc1 -std=c++11 -fcuda-is-device -triple nvptx64-nvidia-cuda -emit-llvm -o - %s | FileCheck --check-prefix=OGCG-DEVICE %s
+// RUN: echo -n "GPU binary would be here." > %t
+// RUN: %clang_cc1 -fclangir -std=c++11 -triple x86_64-unknown-linux-gnu \
+// RUN: -target-sdk-version=12.3 -fcuda-include-gpubinary %t \
+// RUN: -emit-cir -o - %s | FileCheck --check-prefix=CIR-HOST %s
+// RUN: %clang_cc1 -fclangir -std=c++11 -triple x86_64-unknown-linux-gnu \
+// RUN: -target-sdk-version=12.3 -fcuda-include-gpubinary %t \
+// RUN: -emit-llvm -o - %s | FileCheck --check-prefix=LLVM-HOST %s
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu \
+// RUN: -target-sdk-version=12.3 -fcuda-include-gpubinary %t \
+// RUN: -emit-llvm -o - %s | FileCheck --check-prefix=OGCG-HOST %s
+
struct surfaceReference {
int desc;
};
@@ -18,8 +30,45 @@ struct __attribute__((device_builtin_surface_type)) surface<void, dim>
surface<void, 2> surf;
+//===----------------------------------------------------------------------===//
+// Device-side checks
+//===----------------------------------------------------------------------===//
+
// CIR-DEVICE: cir.global external target_address_space(1) @surf = #cir.undef : !cir.cuda_surface
// CIR now matches OG CodeGen and emits undef for CUDA shadow variables.
// LLVM-DEVICE: @surf ={{.*}} addrspace(1) externally_initialized global i64 undef
// OGCG-DEVICE: @surf ={{.*}} addrspace(1) externally_initialized global i64 undef
+
+//===----------------------------------------------------------------------===//
+// Host-side checks
+//===----------------------------------------------------------------------===//
+
+// Check the CUDA surface registration runtime declaration.
+// CIR-HOST: cir.func private @__cudaRegisterSurface(!cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>, !cir.ptr<!void>, !cir.ptr<!void>, !s32i, !s32i)
+
+// Check that __cuda_register_globals registers the surface using the host
+// shadow, device-side name, surface type, and extern flag.
+// CIR-HOST-LABEL: cir.func internal private @__cuda_register_globals
+// CIR-HOST-SAME: (%[[FATBIN:.*]]: !cir.ptr<!cir.ptr<!void>>
+// CIR-HOST: %[[NAME_RAW:.*]] = cir.get_global @".strsurf"
+// CIR-HOST-NEXT: %[[NAME:.*]] = cir.cast bitcast %[[NAME_RAW]]
+// CIR-HOST-NEXT: %[[HOST_RAW:.*]] = cir.get_global @surf
+// CIR-HOST-NEXT: %[[HOST:.*]] = cir.cast bitcast %[[HOST_RAW]]
+// CIR-HOST-NEXT: %[[EXTERN:.*]] = cir.const #cir.int<0> : !s32i
+// CIR-HOST-NEXT: %[[SURFACE_TYPE:.*]] = cir.const #cir.int<2> : !s32i
+// CIR-HOST-NEXT: cir.call @__cudaRegisterSurface(%[[FATBIN]], %[[HOST]], %[[NAME]], %[[NAME]], %[[SURFACE_TYPE]], %[[EXTERN]])
+
+// Check that the host-side shadow carries the surface registration metadata,
+// including the surface type extracted from surface<void, 2>.
+// CIR-HOST: cir.global{{.*}} @surf = {{.*}}cu.var_registration = #cir.cu.var_registration<surf, Surface, surface_type = 2>
+
+// Check CIR-lowered LLVM registration.
+// LLVM-HOST-LABEL: define internal void @__cuda_register_globals
+// LLVM-HOST-SAME: (ptr %[[FATBIN:.*]])
+// LLVM-HOST: call void @__cudaRegisterSurface(ptr %[[FATBIN]], ptr @surf, ptr @[[NAME:.*]], ptr @[[NAME]], i32 2, i32 0)
+
+// Check parity with original CodeGen.
+// OGCG-HOST-LABEL: define internal void @__cuda_register_globals
+// OGCG-HOST-SAME: (ptr %[[FATBIN:.*]])
+// OGCG-HOST: call void @__cudaRegisterSurface(ptr %[[FATBIN]], ptr @surf, ptr @[[NAME:.*]], ptr @[[NAME]], i32 2, i32 0)
>From 2cae85ca4e93450a5f4bd30c22b8a150780481de Mon Sep 17 00:00:00 2001
From: Sivapriya <sivapriyaofficial2005 at gmail.com>
Date: Wed, 23 Sep 2026 11:46:53 +0000
Subject: [PATCH 2/2] [CIR][CUDA] Address review comments
---
clang/test/CIR/CodeGenCUDA/surface.cu | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
diff --git a/clang/test/CIR/CodeGenCUDA/surface.cu b/clang/test/CIR/CodeGenCUDA/surface.cu
index 619b34ffde48dc..413e19cabbd80c 100644
--- a/clang/test/CIR/CodeGenCUDA/surface.cu
+++ b/clang/test/CIR/CodeGenCUDA/surface.cu
@@ -14,15 +14,19 @@
// RUN: -emit-llvm -o - %s | FileCheck --check-prefix=LLVM-HOST %s
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu \
// RUN: -target-sdk-version=12.3 -fcuda-include-gpubinary %t \
-// RUN: -emit-llvm -o - %s | FileCheck --check-prefix=OGCG-HOST %s
+// RUN: -emit-llvm -o - %s | FileCheck --check-prefix=LLVM-HOST %s
struct surfaceReference {
int desc;
};
-template <typename T, int dim = 1>
-struct __attribute__((device_builtin_surface_type)) surface
- : public surfaceReference {};
+template <class T, int dim = 1>
+struct __attribute__((device_builtin_surface_type)) surface;
+
+template <class T, int dim>
+struct __attribute__((device_builtin_surface_type)) surface {
+ typedef surfaceReference type;
+};
template <int dim>
struct __attribute__((device_builtin_surface_type)) surface<void, dim>
@@ -63,12 +67,7 @@ surface<void, 2> surf;
// including the surface type extracted from surface<void, 2>.
// CIR-HOST: cir.global{{.*}} @surf = {{.*}}cu.var_registration = #cir.cu.var_registration<surf, Surface, surface_type = 2>
-// Check CIR-lowered LLVM registration.
+// Check both CIR-lowered LLVM and original CodeGen registration.
// LLVM-HOST-LABEL: define internal void @__cuda_register_globals
// LLVM-HOST-SAME: (ptr %[[FATBIN:.*]])
-// LLVM-HOST: call void @__cudaRegisterSurface(ptr %[[FATBIN]], ptr @surf, ptr @[[NAME:.*]], ptr @[[NAME]], i32 2, i32 0)
-
-// Check parity with original CodeGen.
-// OGCG-HOST-LABEL: define internal void @__cuda_register_globals
-// OGCG-HOST-SAME: (ptr %[[FATBIN:.*]])
-// OGCG-HOST: call void @__cudaRegisterSurface(ptr %[[FATBIN]], ptr @surf, ptr @[[NAME:.*]], ptr @[[NAME]], i32 2, i32 0)
+// LLVM-HOST: call void @__cudaRegisterSurface(ptr %[[FATBIN]], ptr @surf, ptr @[[NAME:.*]], ptr @[[NAME]], i32 2, i32 0)
\ No newline at end of file
More information about the cfe-commits
mailing list