[CIR] Handle vtable pure and deleted virtual functions (#183862)

Finding reproducers for these that don't use the deferred vtable (which
    we haven't yet implemented) was a bit of a challenge, but I found
this setup to get these to be emitted. Fortunately it is a quite easy
implementation that doesn't do awfully much.

This patch implements both, plus the name through the itanium ABI.
This commit is contained in:
Erich Keane 2026-03-03 11:11:01 -08:00 committed by GitHub
parent 6893d27757
commit 03bd4ef4ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 65 additions and 4 deletions

View File

@ -290,6 +290,9 @@ public:
CIRGenFunction &cgf, const CXXRecordDecl *vtableClass, BaseSubobject base,
const CXXRecordDecl *nearestVBase) = 0;
virtual llvm::StringRef getPureVirtualCallName() = 0;
virtual llvm::StringRef getDeletedVirtualCallName() = 0;
/// Insert any ABI-specific implicit parameters into the parameter list for a
/// function. This generally involves extra data for constructors and
/// destructors.

View File

@ -139,6 +139,12 @@ public:
mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc,
QualType ty) override;
StringRef getPureVirtualCallName() override { return "__cxa_pure_virtual"; }
StringRef getDeletedVirtualCallName() override {
return "__cxa_deleted_virtual";
}
CatchTypeInfo
getAddrOfCXXCatchHandlerType(mlir::Location loc, QualType ty,
QualType catchHandlerType) override {

View File

@ -176,13 +176,33 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
assert(!cir::MissingFeatures::cudaSupport());
auto getSpecialVirtFn = [&](StringRef name) -> cir::FuncOp {
assert(!cir::MissingFeatures::vtableRelativeLayout());
if (cgm.getLangOpts().OpenMP && cgm.getLangOpts().OpenMPIsTargetDevice &&
cgm.getTriple().isNVPTX())
cgm.errorNYI(gd.getDecl()->getSourceRange(),
"getVTableComponent for OMP Device NVPTX");
cir::FuncType fnTy =
cgm.getBuilder().getFuncType({}, cgm.getBuilder().getVoidTy());
cir::FuncOp fnPtr = cgm.createRuntimeFunction(fnTy, name);
assert(!cir::MissingFeatures::opGlobalUnnamedAddr());
return fnPtr;
};
cir::FuncOp fnPtr;
if (cast<CXXMethodDecl>(gd.getDecl())->isPureVirtual()) {
cgm.errorNYI("getVTableComponent: CK_FunctionPointer: pure virtual");
return mlir::Attribute();
if (!pureVirtualFn)
pureVirtualFn =
getSpecialVirtFn(cgm.getCXXABI().getPureVirtualCallName());
fnPtr = pureVirtualFn;
} else if (cast<CXXMethodDecl>(gd.getDecl())->isDeleted()) {
cgm.errorNYI("getVTableComponent: CK_FunctionPointer: deleted virtual");
return mlir::Attribute();
if (!deletedVirtualFn)
deletedVirtualFn =
getSpecialVirtFn(cgm.getCXXABI().getDeletedVirtualCallName());
fnPtr = deletedVirtualFn;
} else if (nextVTableThunkIndex < layout.vtable_thunks().size() &&
layout.vtable_thunks()[nextVTableThunkIndex].first ==
componentIndex) {

View File

@ -46,6 +46,11 @@ class CIRGenVTables {
/// indices.
SecondaryVirtualPointerIndicesMapTy secondaryVirtualPointerIndices;
/// Cache for the pure virtual member call function.
cir::FuncOp pureVirtualFn = nullptr;
/// Cache for the deleted virtual member call function.
cir::FuncOp deletedVirtualFn = nullptr;
mlir::Attribute
getVTableComponent(const VTableLayout &layout, unsigned componentIndex,
mlir::Attribute rtti, unsigned &nextVTableThunkIndex,

View File

@ -0,0 +1,27 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir
// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR
// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM,LLVM-CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM,LLVM-OGCG
struct Struct {
virtual void f1() = 0;
virtual void f2() = delete;
virtual void f3();
};
void Struct::f3(){}
// CIR: cir.global "private" external @_ZTV6Struct = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI6Struct> : !cir.ptr<!u8i>, #cir.global_view<@__cxa_pure_virtual> : !cir.ptr<!u8i>, #cir.global_view<@__cxa_deleted_virtual> : !cir.ptr<!u8i>, #cir.global_view<@_ZN6Struct2f3Ev> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 5>}>
// LLVM: @_ZTV6Struct = {{.*}}{ [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI6Struct, ptr @__cxa_pure_virtual, ptr @__cxa_deleted_virtual, ptr @_ZN6Struct2f3Ev] }
// CIR: cir.global constant external @_ZTI6Struct = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS6Struct> : !cir.ptr<!u8i>}>
// LLVM-CIR: @_ZTI6Struct = constant { ptr, ptr } { ptr getelementptr (i8, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 16), ptr @_ZTS6Struct }
// LLVM-OGCG: @_ZTI6Struct = constant { ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr @_ZTS6Struct }
//
// CIR: cir.func private dso_local @__cxa_pure_virtual()
// LLVM: declare {{.*}}void @__cxa_pure_virtual()
//
// CIR: cir.func private dso_local @__cxa_deleted_virtual()
// LLVM: declare {{.*}}void @__cxa_deleted_virtual()