https://github.com/pvelesko updated 
https://github.com/llvm/llvm-project/pull/212452

>From 26aaa87a3136b666bf5ca4352af8db76527c07b3 Mon Sep 17 00:00:00 2001
From: Paulius Velesko <[email protected]>
Date: Tue, 28 Jul 2026 13:13:35 +0300
Subject: [PATCH] [clang][SPIR-V] Emit C++ vtable components in the generic
 address space

A vtable slot holds a function address. Functions live in the program
address space and the slots in the default globals address space, so on
spirv64 clang emits each slot as

  addrspacecast (ptr @fn to ptr addrspace(1))

SPIR-V has no cast between two non-generic storage classes:
OpPtrCastToGeneric converts a Workgroup, CrossWorkgroup or Function
pointer to Generic, and OpGenericCastToPtr converts back. The
SPIRV-LLVM-Translator rejects the module ("Casts from private/local/global
address space are allowed only to generic") and the SPIR-V backend emits a
vtable initializer that spirv-val rejects.

Emit the pointer components as generic pointers on non-logical SPIR and
SPIR-V targets whose program address space is not already the generic
one: spir, spir64, spirv32, spirv64 and spirv64-intel (functions in
CodeSectionINTEL, which SPV_INTEL_function_pointers makes part of
Generic). The RTTI component, a global, is cast to generic, and
getVirtualFunctionPointer loads the slot with the component type.

spirv64-amd-amdhsa is unchanged because its functions are already generic,
and so is logical SPIR-V, which has no generic pointers. Other targets are
unaffected because the component type stays GlobalsInt8PtrTy.
---
 clang/lib/CodeGen/CGVTables.cpp               | 17 +++++-----
 clang/lib/CodeGen/CodeGenModule.cpp           |  7 ++++
 clang/lib/CodeGen/CodeGenTypeCache.h          |  3 ++
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |  4 +--
 .../CodeGenCXX/vtable-address-space-spirv.cpp | 32 +++++++++++++++++++
 5 files changed, 53 insertions(+), 10 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/vtable-address-space-spirv.cpp

diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 1aa3fb1928279..c63e1e4e5d7f0 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -730,7 +730,7 @@ void 
CodeGenVTables::addRelativeComponent(ConstantArrayBuilder &builder,
 llvm::Type *CodeGenModule::getVTableComponentType() const {
   if (getLangOpts().RelativeCXXABIVTables)
     return Int32Ty;
-  return GlobalsInt8PtrTy;
+  return VTableComponentPtrTy;
 }
 
 llvm::Type *CodeGenVTables::getVTableComponentType() const {
@@ -742,7 +742,7 @@ static void AddPointerLayoutOffset(const CodeGenModule &CGM,
                                    CharUnits offset) {
   builder.add(llvm::ConstantExpr::getIntToPtr(
       llvm::ConstantInt::getSigned(CGM.PtrDiffTy, offset.getQuantity()),
-      CGM.GlobalsInt8PtrTy));
+      CGM.VTableComponentPtrTy));
 }
 
 static void AddRelativeLayoutOffset(const CodeGenModule &CGM,
@@ -780,7 +780,8 @@ void 
CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
                                   vtableHasLocalLinkage,
                                   /*isCompleteDtor=*/false);
     else
-      return builder.add(rtti);
+      return builder.add(llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(
+          rtti, CGM.VTableComponentPtrTy));
 
   case VTableComponent::CK_FunctionPointer:
   case VTableComponent::CK_CompleteDtorPointer:
@@ -808,7 +809,7 @@ void 
CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
         if (IsThunk)
           nextVTableThunkIndex++;
         return builder.add(
-            llvm::ConstantExpr::getNullValue(CGM.GlobalsInt8PtrTy));
+            llvm::ConstantExpr::getNullValue(CGM.VTableComponentPtrTy));
       }
       // Method is acceptable, continue processing as usual.
     }
@@ -885,11 +886,11 @@ void 
CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
       //       globals;  fixing said issue might be intrusive, and will be done
       //       later.
       unsigned FnAS = fnPtr->getType()->getPointerAddressSpace();
-      unsigned GVAS = CGM.GlobalsInt8PtrTy->getPointerAddressSpace();
+      unsigned GVAS = CGM.VTableComponentPtrTy->getPointerAddressSpace();
 
       if (FnAS != GVAS)
-        fnPtr =
-            llvm::ConstantExpr::getAddrSpaceCast(fnPtr, CGM.GlobalsInt8PtrTy);
+        fnPtr = llvm::ConstantExpr::getAddrSpaceCast(fnPtr,
+                                                     CGM.VTableComponentPtrTy);
       if (const auto &Schema =
           CGM.getCodeGenOpts().PointerAuth.CXXVirtualFunctionPointers)
         return builder.addSignedPointer(fnPtr, Schema, GD, QualType());
@@ -901,7 +902,7 @@ void 
CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
     if (RelativeCXXABIVTables)
       return builder.add(llvm::ConstantExpr::getNullValue(CGM.Int32Ty));
     else
-      return builder.addNullPointer(CGM.GlobalsInt8PtrTy);
+      return builder.addNullPointer(CGM.VTableComponentPtrTy);
   }
 
   llvm_unreachable("Unexpected vtable component kind");
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index bf03e10f9fb42..524d81b843730 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -591,11 +591,18 @@ CodeGenModule::CodeGenModule(ASTContext &C,
       llvm::PointerType::get(LLVMContext, DL.getAllocaAddrSpace());
   GlobalsInt8PtrTy =
       llvm::PointerType::get(LLVMContext, DL.getDefaultGlobalsAddressSpace());
+  VTableComponentPtrTy = GlobalsInt8PtrTy;
   ProgramPtrTy =
       llvm::PointerType::get(LLVMContext, DL.getProgramAddressSpace());
   ConstGlobalsPtrTy = llvm::PointerType::get(
       LLVMContext, C.getTargetAddressSpace(GetGlobalConstantAddressSpace()));
 
+  // SPIR-V can only cast a function pointer to the generic address space.
+  unsigned GenericAS = C.getTargetAddressSpace(LangAS::opencl_generic);
+  if (getTriple().isSPIROrSPIRV() && !getTriple().isSPIRVLogical() &&
+      DL.getProgramAddressSpace() != GenericAS)
+    VTableComponentPtrTy = llvm::PointerType::get(LLVMContext, GenericAS);
+
   // Build C++20 Module initializers.
   // TODO: Add Microsoft here once we know the mangling required for the
   // initializers.
diff --git a/clang/lib/CodeGen/CodeGenTypeCache.h 
b/clang/lib/CodeGen/CodeGenTypeCache.h
index 17eca207d7c80..b15ade84c5b92 100644
--- a/clang/lib/CodeGen/CodeGenTypeCache.h
+++ b/clang/lib/CodeGen/CodeGenTypeCache.h
@@ -72,6 +72,9 @@ struct CodeGenTypeCache {
     llvm::PointerType *GlobalsInt8PtrTy;
   };
 
+  /// Type of a pointer component of a C++ vtable.
+  llvm::PointerType *VTableComponentPtrTy;
+
   /// Pointer in program address space
   llvm::PointerType *ProgramPtrTy;
 
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp 
b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 5bbb742a7b1ef..feb0f0e9acbc0 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -2275,8 +2275,8 @@ CGCallee 
ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
           {VTable, llvm::ConstantInt::get(CGM.Int32Ty, ByteOffset)});
     } else {
       VTableSlotPtr = CGF.Builder.CreateConstInBoundsGEP1_64(
-          PtrTy, VTable, VTableIndex, "vfn");
-      VFuncLoad = CGF.Builder.CreateAlignedLoad(PtrTy, VTableSlotPtr,
+          ComponentTy, VTable, VTableIndex, "vfn");
+      VFuncLoad = CGF.Builder.CreateAlignedLoad(ComponentTy, VTableSlotPtr,
                                                 CGF.getPointerAlign());
     }
 
diff --git a/clang/test/CodeGenCXX/vtable-address-space-spirv.cpp 
b/clang/test/CodeGenCXX/vtable-address-space-spirv.cpp
new file mode 100644
index 0000000000000..9687e34c3dedb
--- /dev/null
+++ b/clang/test/CodeGenCXX/vtable-address-space-spirv.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 %s -triple=spirv64 -std=c++11 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple=spirv32 -std=c++11 -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -triple=spir64-unknown-unknown -std=c++11 -emit-llvm -o 
- | FileCheck %s
+// RUN: %clang_cc1 %s -triple=spir-unknown-unknown -std=c++11 -emit-llvm -o - 
| FileCheck %s
+
+// Functions are already generic here, so the components stay global.
+// RUN: %clang_cc1 %s -triple=spirv64-amd-amdhsa -std=c++11 -emit-llvm -o - | 
FileCheck %s --check-prefix=AMDGCNSPIRV
+
+struct A {
+  virtual void f();
+  virtual void g();
+};
+
+void A::f() {}
+
+// CHECK: @_ZTV1A ={{.*}}addrspace(1) constant { [4 x ptr addrspace(4)] } { [4 
x ptr addrspace(4)] [ptr addrspace(4) null, ptr addrspace(4) addrspacecast (ptr 
addrspace(1) @_ZTI1A to ptr addrspace(4)), ptr addrspace(4) addrspacecast (ptr 
@_ZN1A1fEv to ptr addrspace(4)), ptr addrspace(4) addrspacecast (ptr @_ZN1A1gEv 
to ptr addrspace(4))]
+// CHECK: @_ZTI1A ={{.*}}addrspace(1) constant { ptr addrspace(1), ptr 
addrspace(1) }
+
+// AMDGCNSPIRV: @_ZTV1A ={{.*}}addrspace(1) constant { [4 x ptr addrspace(1)] 
} { [4 x ptr addrspace(1)] [ptr addrspace(1) null, ptr addrspace(1) @_ZTI1A, 
ptr addrspace(1) addrspacecast (ptr addrspace(4) @_ZN1A1fEv to ptr 
addrspace(1)), ptr addrspace(1) addrspacecast (ptr addrspace(4) @_ZN1A1gEv to 
ptr addrspace(1))]
+
+void call(A *a) { a->g(); }
+
+// CHECK-LABEL: define {{.*}}@_Z4callP1A
+// CHECK: %[[VT:.*]] = load ptr addrspace(1),
+// CHECK: %[[SLOT:.*]] = getelementptr inbounds ptr addrspace(4), ptr 
addrspace(1) %[[VT]], i64 1
+// CHECK: %[[FN:.*]] = load ptr addrspace(4), ptr addrspace(1) %[[SLOT]]
+// CHECK: call {{.*}}addrspace(4) void %[[FN]]
+
+// AMDGCNSPIRV-LABEL: define {{.*}}@_Z4callP1A
+// AMDGCNSPIRV: %[[VT:.*]] = load ptr addrspace(1),
+// AMDGCNSPIRV: %[[SLOT:.*]] = getelementptr inbounds ptr addrspace(1), ptr 
addrspace(1) %[[VT]], i64 1
+// AMDGCNSPIRV: %[[FN:.*]] = load ptr addrspace(1), ptr addrspace(1) %[[SLOT]]

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

Reply via email to