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
