https://github.com/aobolensk created 
https://github.com/llvm/llvm-project/pull/230910

None

>From a11022813ac64ae705ed90781a0a84cba3e4da57 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <[email protected]>
Date: Sun, 11 Oct 2026 09:31:06 +0200
Subject: [PATCH] [CIR][HIP] Support managed variables

---
 clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp        | 70 +++++++++++++
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        |  3 +-
 .../Dialect/Transforms/LoweringPrepare.cpp    | 35 ++++++-
 .../template-class-static-member.cu           |  6 +-
 clang/test/CIR/CodeGenHIP/managed-var.hip     | 97 +++++++++++++++++++
 5 files changed, 204 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/CIR/CodeGenHIP/managed-var.hip

diff --git a/clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp
index 79153ca788756..8f7d65b827507 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCUDANV.cpp
@@ -62,6 +62,7 @@ class CIRGenNVCUDARuntime : public CIRGenCUDARuntime {
   void emitDeviceStubBodyNew(CIRGenFunction &cgf, cir::FuncOp fn,
                              FunctionArgList &args);
   void recordDeviceBinary();
+  void transformManagedVars();
   mlir::Value prepareKernelArgs(CIRGenFunction &cgf, mlir::Location loc,
                                 FunctionArgList &args);
   mlir::Operation *getKernelHandle(cir::FuncOp fn, GlobalDecl gd) override;
@@ -581,7 +582,76 @@ void CIRGenNVCUDARuntime::recordDeviceBinary() {
                            mlir::StringAttr::get(bytes, fatbinTy));
 }
 
+// Uses of a managed variable load its address from a pointer the runtime 
fills.
+void CIRGenNVCUDARuntime::transformManagedVars() {
+  CIRGenBuilderTy &builder = cgm.getBuilder();
+  mlir::ModuleOp module = cgm.getModule();
+  for (auto &&info : deviceVars) {
+    cir::GlobalOp var = info.var;
+    if (info.flags != cir::CUDADeviceVarKind::Variable ||
+        !info.d->hasAttr<HIPManagedAttr>())
+      continue;
+
+    cir::PointerType varPtrTy =
+        builder.getPointerTo(var.getSymType(), var.getAddrSpaceAttr());
+    mlir::ptr::MemorySpaceAttrInterface managedAS = cir::toCIRAddressSpaceAttr(
+        cgm.getMLIRContext(),
+        cgm.getLangOpts().CUDAIsDevice ? LangAS::cuda_device : 
LangAS::Default);
+    std::optional<mlir::SymbolTable::UseRange> uses = 
var.getSymbolUses(module);
+
+    std::string name = var.getSymName().str();
+    // An empty comdat would follow the rename.
+    std::optional<llvm::StringRef> comdat = var.getComdat();
+    if (comdat && comdat->empty())
+      var.setComdat(llvm::StringRef(name));
+    cgm.eraseGlobalSymbol(var);
+    var.setSymName(name + ".managed");
+    cgm.insertGlobalSymbol(var);
+
+    cir::GlobalOp managedVar =
+        cgm.createGlobalOp(var.getLoc(), name, varPtrTy,
+                           /*isConstant=*/false, managedAS);
+    managedVar.setLinkage(var.getLinkage());
+    if (!var.isDeclaration())
+      managedVar.setInitialValueAttr(builder.getConstNullPtrAttr(varPtrTy));
+    managedVar.setDsoLocal(var.getDsoLocal());
+    managedVar.setGlobalVisibility(var.getGlobalVisibility());
+    mlir::SymbolTable::setSymbolVisibility(
+        managedVar, mlir::SymbolTable::getSymbolVisibility(var));
+    managedVar->setAttr(
+        cir::CUDAExternallyInitializedAttr::getMnemonic(),
+        cir::CUDAExternallyInitializedAttr::get(&cgm.getMLIRContext()));
+
+    // The uses now name the pointer global.
+    cir::PointerType managedPtrTy =
+        builder.getPointerTo(varPtrTy, managedVar.getAddrSpaceAttr());
+    for (mlir::SymbolTable::SymbolUse use : *uses) {
+      auto getGlobalOp = dyn_cast<cir::GetGlobalOp>(use.getUser());
+      if (!getGlobalOp) {
+        cgm.errorNYI(info.d->getSourceRange(),
+                     "transformManagedVars: non-instruction use");
+        continue;
+      }
+      mlir::Value addr = getGlobalOp.getAddr();
+      addr.setType(managedPtrTy);
+      mlir::OpBuilder::InsertionGuard guard(builder);
+      builder.setInsertionPointAfter(getGlobalOp);
+      mlir::Value ld = builder.createAlignedLoad(
+          getGlobalOp.getLoc(), varPtrTy, addr,
+          CharUnits::fromQuantity(var.getAlignment().value_or(1)));
+      addr.replaceAllUsesExcept(ld, ld.getDefiningOp());
+    }
+
+    // The runtime allocates them even if device code never uses them.
+    if (cgm.getLangOpts().CUDAIsDevice && !var.isDeclaration()) {
+      cgm.addCompilerUsedGlobal(var);
+      cgm.addCompilerUsedGlobal(managedVar);
+    }
+  }
+}
+
 void CIRGenNVCUDARuntime::finalizeModule() {
+  transformManagedVars();
   if (!cgm.getLangOpts().CUDAIsDevice) {
     recordDeviceBinary();
     return;
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 1f80f4e0ebf08..d38ed3a54e7eb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1414,8 +1414,7 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, 
mlir::Type ty,
     // in both device and host compilations.
     if (getLangOpts().CUDA && d && d->hasAttr<HIPManagedAttr>() &&
         d->hasExternalStorage())
-      errorNYI(d->getSourceRange(),
-               "getOrCreateCIRGlobal: HIP managed attribute");
+      getCUDARuntime().handleVarRegistration(d, gv);
   }
 
   assert(!cir::MissingFeatures::addressSpace());
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 9334bf65210c4..fb5eaf6fe1cbb 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -3262,6 +3262,15 @@ void 
LoweringPreparePass::buildCUDARegisterVars(cir::CIRBaseBuilderTy &builder,
       FuncType::get({voidPtrPtrTy, voidPtrTy, voidPtrTy, voidPtrTy, intTy,
                      sizeTy, intTy, intTy},
                     voidTy));
+  // void __hipRegisterManagedVar(void **fatbinHandle, char *managedVar,
+  //                              char *var, const char *deviceName,
+  //                              size_t size, unsigned align);
+  FuncOp cudaRegisterManagedVar = buildRuntimeFunction(
+      globalBuilder, addUnderscoredPrefix(cudaPrefix, "RegisterManagedVar"),
+      loc,
+      FuncType::get(
+          {voidPtrPtrTy, voidPtrTy, voidPtrTy, voidPtrTy, sizeTy, intTy},
+          voidTy));
 
   auto makeConstantString = [&](llvm::StringRef str) -> GlobalOp {
     auto strType = ArrayType::get(&getContext(), charTy, 1 + str.size());
@@ -3287,8 +3296,9 @@ void 
LoweringPreparePass::buildCUDARegisterVars(cir::CIRBaseBuilderTy &builder,
       llvm_unreachable("Texture registration NYI");
     }
 
-    if (regAttr.getIsManaged())
-      llvm_unreachable("Managed variable registration NYI");
+    // External managed variables are registered by their defining TU.
+    if (regAttr.getIsManaged() && global.isDeclaration())
+      continue;
 
     GlobalOp deviceNameStr = makeConstantString(regAttr.getDeviceSideName());
     mlir::Value deviceName = builder.createBitcast(
@@ -3296,9 +3306,28 @@ void 
LoweringPreparePass::buildCUDARegisterVars(cir::CIRBaseBuilderTy &builder,
     mlir::Value hostVar =
         builder.createBitcast(builder.createGetGlobal(global), voidPtrTy);
 
+    llvm::TypeSize size = dataLayout.getTypeAllocSize(global.getSymType());
+
+    if (regAttr.getIsManaged()) {
+      llvm::StringRef name = global.getSymName();
+      assert(name.ends_with(".managed") &&
+             "HIP managed variables not transformed");
+      auto managedVar = mlirModule.lookupSymbol<GlobalOp>(
+          name.drop_back(llvm::StringRef(".managed").size()));
+      mlir::Value managedPtr =
+          builder.createBitcast(builder.createGetGlobal(managedVar), 
voidPtrTy);
+      auto varSize = ConstantOp::create(
+          builder, loc, IntAttr::get(sizeTy, size.getFixedValue()));
+      auto align = ConstantOp::create(
+          builder, loc, IntAttr::get(intTy, 
global.getAlignment().value_or(1)));
+      builder.createCallOp(
+          loc, cudaRegisterManagedVar,
+          {fatbinHandle, managedPtr, hostVar, deviceName, varSize, align});
+      continue;
+    }
+
     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(
diff --git a/clang/test/CIR/CodeGenCUDA/template-class-static-member.cu 
b/clang/test/CIR/CodeGenCUDA/template-class-static-member.cu
index fccfddc2b8018..32a8f62a8de30 100644
--- a/clang/test/CIR/CodeGenCUDA/template-class-static-member.cu
+++ b/clang/test/CIR/CodeGenCUDA/template-class-static-member.cu
@@ -56,7 +56,8 @@ template class A<int>;
 
 // CIR-DEV-DAG: cir.global "private" internal comdat dso_local  
target_address_space(1) @_ZN1AIiE8d_memberE = #cir.int<0>
 // CIR-DEV-DAG: cir.global "private" constant internal comdat dso_local  
target_address_space(4) @_ZN1AIiE8c_memberE = #cir.int<0>
-// CIR-DEV-DAG: cir.global "private" internal comdat dso_local  
target_address_space(1) @_ZN1AIiE8m_memberE = #cir.int<0>
+// CIR-DEV-DAG: cir.global "private" internal comdat("_ZN1AIiE8m_memberE") 
dso_local  target_address_space(1) @_ZN1AIiE8m_memberE.managed = #cir.int<0>
+// CIR-DEV-DAG: cir.global "private" internal  dso_local  
target_address_space(1) @_ZN1AIiE8m_memberE = #cir.ptr<null> : !cir.ptr<!s32i, 
target_address_space(1)> {cu.externally_initialized = 
#cir.cu.externally_initialized}
 // CIR-DEV-DAG: cir.global "private" constant internal comdat dso_local  
target_address_space(4) @_ZN1AIiE12const_memberE = #cir.int<0>
 // CIR-DEV-NEG-NOT: @_ZN1AIiE8h_memberE
 
@@ -69,7 +70,8 @@ template class A<int>;
 // CIR-HOST-DAG:  cir.global weak_odr comdat @_ZN1AIiE8h_memberE = #cir.int<0>
 // CIR-HOST-DAG:  cir.global "private" internal comdat dso_local 
@_ZN1AIiE8d_memberE = #cir.undef
 // CIR-HOST-DAG:  cir.global "private" internal comdat dso_local 
@_ZN1AIiE8c_memberE = #cir.undef
-// CIR-HOST-DAG:  cir.global "private" internal comdat dso_local 
@_ZN1AIiE8m_memberE = #cir.int<0>
+// CIR-HOST-DAG:  cir.global "private" internal comdat("_ZN1AIiE8m_memberE") 
dso_local @_ZN1AIiE8m_memberE.managed = #cir.int<0>
+// CIR-HOST-DAG:  cir.global "private" internal dso_local @_ZN1AIiE8m_memberE 
= #cir.ptr<null> : !cir.ptr<!s32i> {cu.externally_initialized = 
#cir.cu.externally_initialized}
 // CIR-HOST-DAG:  cir.global constant weak_odr comdat @_ZN1AIiE12const_memberE 
= #cir.int<0>
 
 // HOST-DAG: @_ZN1AIiE8h_memberE = weak_odr global i32 0, comdat, align 4
diff --git a/clang/test/CIR/CodeGenHIP/managed-var.hip 
b/clang/test/CIR/CodeGenHIP/managed-var.hip
new file mode 100644
index 0000000000000..313a73e5935d9
--- /dev/null
+++ b/clang/test/CIR/CodeGenHIP/managed-var.hip
@@ -0,0 +1,97 @@
+// RUN: echo -n "GPU binary would be here." > %t
+
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fcuda-is-device -fclangir \
+// RUN:            -x hip -emit-cir %s -o %t.dev.cir
+// RUN: FileCheck --check-prefix=CIR-DEV --input-file=%t.dev.cir %s
+
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fcuda-is-device -fclangir \
+// RUN:            -x hip -emit-llvm %s -o %t.dev.cir.ll
+// RUN: FileCheck --check-prefix=DEV --input-file=%t.dev.cir.ll %s
+
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fcuda-is-device \
+// RUN:            -x hip -emit-llvm %s -o %t.dev.ll
+// RUN: FileCheck --check-prefix=DEV --input-file=%t.dev.ll %s
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \
+// RUN:            -x hip -fhip-new-launch-api -fcuda-include-gpubinary %t \
+// RUN:            -emit-cir %s -o %t.host.cir
+// RUN: FileCheck --check-prefix=CIR-HOST --input-file=%t.host.cir %s
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir \
+// RUN:            -x hip -fhip-new-launch-api -fcuda-include-gpubinary %t \
+// RUN:            -emit-llvm %s -o %t.host.cir.ll
+// RUN: FileCheck --check-prefix=HOST --input-file=%t.host.cir.ll %s
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu \
+// RUN:            -x hip -fhip-new-launch-api -fcuda-include-gpubinary %t \
+// RUN:            -emit-llvm %s -o %t.host.ll
+// RUN: FileCheck --check-prefix=HOST --input-file=%t.host.ll %s
+
+#include "../CodeGenCUDA/Inputs/cuda.h"
+
+__managed__ int x = 1;
+__managed__ float arr[8];
+extern __managed__ int ex;
+static __managed__ int sx = 2;
+
+template <typename T> __managed__ T tv = 3;
+
+// CIR-DEV-DAG: cir.global external target_address_space(1) @x.managed = 
#cir.int<1> : !s32i align(4) {cu.externally_initialized = 
#cir.cu.externally_initialized, cu.var_registration = 
#cir.cu.var_registration<x, Variable, managed>}
+// CIR-DEV-DAG: cir.global external target_address_space(1) @x = 
#cir.ptr<null> : !cir.ptr<!s32i, target_address_space(1)> 
{cu.externally_initialized = #cir.cu.externally_initialized}
+// CIR-DEV-DAG: cir.global linkonce_odr comdat("_Z2tvIiE") 
target_address_space(1) @_Z2tvIiE.managed = #cir.int<3>
+
+// DEV-DAG: @x.managed = addrspace(1) externally_initialized global i32 1, 
align 4
+// DEV-DAG: @x = addrspace(1) externally_initialized global ptr addrspace(1) 
null
+// DEV-DAG: @arr.managed = addrspace(1) externally_initialized global [8 x 
float] zeroinitializer, align 4
+// DEV-DAG: @arr = addrspace(1) externally_initialized global ptr addrspace(1) 
null
+// DEV-DAG: @ex.managed = external addrspace(1) global i32, align 4
+// DEV-DAG: @ex = external addrspace(1) externally_initialized global ptr 
addrspace(1){{$}}
+// DEV-DAG: @_ZL2sx.managed = addrspace(1) externally_initialized global i32 
2, align 4
+// DEV-DAG: @_ZL2sx = addrspace(1) externally_initialized global ptr 
addrspace(1) null
+// DEV-DAG: @_Z2tvIiE.managed = linkonce_odr addrspace(1) 
externally_initialized global i32 3, comdat($_Z2tvIiE), align 4
+// DEV-DAG: @_Z2tvIiE = linkonce_odr addrspace(1) externally_initialized 
global ptr addrspace(1) null{{$}}
+// DEV-DAG: @llvm.compiler.used = {{.*}}@x.managed{{.*}}@x 
to{{.*}}@arr.managed{{.*}}@arr to{{.*}}@_ZL2sx.managed{{.*}}@_ZL2sx 
to{{.*}}@_Z2tvIiE.managed{{.*}}@_Z2tvIiE to
+
+// HOST-DAG: @x.managed = internal global i32 1, align 4
+// HOST-DAG: @x = internal externally_initialized global ptr null
+// HOST-DAG: @arr.managed = internal global [8 x float] zeroinitializer, align 
16
+// HOST-DAG: @arr = internal externally_initialized global ptr null
+// HOST-DAG: @ex.managed = external global i32, align 4
+// HOST-DAG: @ex = external externally_initialized global ptr{{$}}
+// HOST-DAG: @_ZL2sx.managed = internal global i32 2, align 4
+// HOST-DAG: @_ZL2sx = internal externally_initialized global ptr null
+// HOST-DAG: @_Z2tvIiE.managed = internal global i32 3, comdat($_Z2tvIiE), 
align 4
+// HOST-DAG: @_Z2tvIiE = internal externally_initialized global ptr null
+
+// CIR-DEV-LABEL: cir.func{{.*}} @_Z4kernPi
+// CIR-DEV: %[[XADDR:.*]] = cir.get_global @x : !cir.ptr<!cir.ptr<!s32i, 
target_address_space(1)>, target_address_space(1)>
+// CIR-DEV: %{{.*}} = cir.load align(4) %[[XADDR]] : !cir.ptr<!cir.ptr<!s32i, 
target_address_space(1)>, target_address_space(1)>, !cir.ptr<!s32i, 
target_address_space(1)>
+
+// DEV-LABEL: define {{.*}}@_Z4kernPi
+// DEV: %[[LD_X:.*]] = load ptr addrspace(1), ptr addrspace(1) @x, align 4
+// DEV: store i32 2, ptr %{{.*}}, align 4
+// DEV: load ptr addrspace(1), ptr addrspace(1) @arr, align 4
+// DEV: load ptr addrspace(1), ptr addrspace(1) @ex, align 4
+// DEV: load ptr addrspace(1), ptr addrspace(1) @_ZL2sx, align 4
+// DEV: load ptr addrspace(1), ptr addrspace(1) @_Z2tvIiE, align 4
+__attribute__((global)) void kern(int *out) {
+  x = 2;
+  arr[1] = 3.0f;
+  *out = ex + sx + tv<int>;
+}
+
+// HOST-DAG: load ptr, ptr @x, align 4
+// HOST-DAG: load ptr, ptr @ex, align 4
+// HOST-DAG: load ptr, ptr @_ZL2sx, align 4
+// HOST-DAG: load ptr, ptr @_Z2tvIiE, align 4
+int host_use() { return x + ex + sx + tv<int>; }
+
+// External managed variables are registered by their defining TU.
+// CIR-HOST-LABEL: cir.func internal private @__hip_register_globals
+// CIR-HOST-COUNT-4: cir.call @__hipRegisterManagedVar
+// CIR-HOST-NEXT: cir.return
+
+// HOST-DAG: call void @__hipRegisterManagedVar(ptr %{{.*}}, ptr @x, ptr 
@x.managed, ptr @{{.*}}, i64 4, i32 4)
+// HOST-DAG: call void @__hipRegisterManagedVar(ptr %{{.*}}, ptr @arr, ptr 
@arr.managed, ptr @{{.*}}, i64 32, i32 16)
+// HOST-DAG: call void @__hipRegisterManagedVar(ptr %{{.*}}, ptr @_ZL2sx, ptr 
@_ZL2sx.managed, ptr @{{.*}}, i64 4, i32 4)
+// HOST-DAG: call void @__hipRegisterManagedVar(ptr %{{.*}}, ptr @_Z2tvIiE, 
ptr @_Z2tvIiE.managed, ptr @{{.*}}, i64 4, i32 4)

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to