https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/220946
>From 1e059870591230ae8141fb91498a4d45ecba940f Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Thu, 3 Sep 2026 06:12:56 -0700 Subject: [PATCH 1/2] [CIR] Implement 'vtable initialization' lowering This showed up in a test suite, and is basically just ensuring that our vtable pointers are properly cleaned up during destruction. The entirety of the static functions (and the implementation) are near word-for-word copies of what classic codegen does. However, there ARE a few parts that are potentially untested(including strict-vtable-pointers which aren't implemented yet), but the implementation is put in place, as it is a mechanical copy/paste implementation. Note: Claude came up with additional test cases. --- clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 92 ++++++++- .../destructor-vtable-reinit-strict.cpp | 46 +++++ .../CIR/CodeGen/destructor-vtable-reinit.cpp | 179 ++++++++++++++++++ 4 files changed, 316 insertions(+), 2 deletions(-) create mode 100644 clang/test/CIR/CodeGen/destructor-vtable-reinit-strict.cpp create mode 100644 clang/test/CIR/CodeGen/destructor-vtable-reinit.cpp diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 184cb833b3ffb..23e903f197cfa 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -303,7 +303,6 @@ struct MissingFeatures { static bool vaArgABILowering() { return false; } static bool vectorConstants() { return false; } static bool vlas() { return false; } - static bool vtableInitialization() { return false; } static bool vtableEmitMetadata() { return false; } static bool vtableRelativeLayout() { return false; } static bool weakRefReference() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 8301627ad8123..ba92375c7231f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -888,6 +888,86 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { "emitConstructorBody: emit body statement failed."); } +static bool fieldHasTrivialDestructorBody(ASTContext &context, + const FieldDecl *field); + +static bool +hasTrivialDestructorBody(ASTContext &context, + const CXXRecordDecl *baseClassDecl, + const CXXRecordDecl *mostDerivedClassDecl) { + // If the destructor is trivial we don't have to check anything else. + if (baseClassDecl->hasTrivialDestructor()) + return true; + + if (!baseClassDecl->getDestructor()->hasTrivialBody()) + return false; + + // Check fields. + for (const auto *field : baseClassDecl->fields()) + if (!fieldHasTrivialDestructorBody(context, field)) + return false; + + // Check non-virtual bases. + for (const auto &base : baseClassDecl->bases()) { + if (base.isVirtual()) + continue; + + const auto *nonVirtualBase = base.getType()->castAsCXXRecordDecl(); + if (!hasTrivialDestructorBody(context, nonVirtualBase, + mostDerivedClassDecl)) + return false; + } + + if (baseClassDecl == mostDerivedClassDecl) { + // Check virtual bases. + for (const auto &vbase : baseClassDecl->vbases()) { + const auto *virtualBase = vbase.getType()->castAsCXXRecordDecl(); + if (!hasTrivialDestructorBody(context, virtualBase, mostDerivedClassDecl)) + return false; + } + } + return true; +} + +static bool fieldHasTrivialDestructorBody(ASTContext &context, + const FieldDecl *field) { + QualType fieldBaseElementType = context.getBaseElementType(field->getType()); + + auto *fieldClassDecl = fieldBaseElementType->getAsCXXRecordDecl(); + if (!fieldClassDecl) + return true; + + // The destructor for an implicit anonymous union member is never invoked. + if (fieldClassDecl->isUnion() && fieldClassDecl->isAnonymousStructOrUnion()) + return true; + + return hasTrivialDestructorBody(context, fieldClassDecl, fieldClassDecl); +} + +/// Check whether we need to initialize any vtable pointers before calling +/// this destructor. +static bool canSkipVTablePointerInitialization(CIRGenFunction &cgf, + const CXXDestructorDecl *dtor) { + const CXXRecordDecl *classDecl = dtor->getParent(); + if (!classDecl->isDynamicClass()) + return true; + + // For a final class, the vtable pointer is known to already point to the + // class's vtable. + if (classDecl->isEffectivelyFinal()) + return true; + + if (!dtor->hasTrivialBody()) + return false; + + // Check the fields. + for (const auto *field : classDecl->fields()) + if (!fieldHasTrivialDestructorBody(cgf.getContext(), field)) + return false; + + return true; +} + /// Emits the body of the current destructor. void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl()); @@ -972,10 +1052,20 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { case Dtor_Base: assert(body); + bool needsVTableInit = !canSkipVTablePointerInitialization(*this, dtor); + // Launder 'this' if necessary. + if (needsVTableInit && cgm.getCodeGenOpts().StrictVTablePointers && + cgm.getCodeGenOpts().OptimizationLevel > 0) { + cxxThisValue = cir::LaunderOp::create( + builder, getLoc(dtor->getBeginLoc()), loadCXXThis()); + } + // Enter the cleanup scopes for fields and non-virtual bases. enterDtorCleanups(dtor, Dtor_Base); - assert(!cir::MissingFeatures::vtableInitialization()); + // Initialize the vtable pointers before entering the body. + if (needsVTableInit) + initializeVTablePointers(getLoc(dtor->getBeginLoc()), dtor->getParent()); if (isTryBody) { cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor"); diff --git a/clang/test/CIR/CodeGen/destructor-vtable-reinit-strict.cpp b/clang/test/CIR/CodeGen/destructor-vtable-reinit-strict.cpp new file mode 100644 index 0000000000000..a3636a9217f83 --- /dev/null +++ b/clang/test/CIR/CodeGen/destructor-vtable-reinit-strict.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fstrict-vtable-pointers -O1 \ +// RUN: -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fstrict-vtable-pointers -O1 \ +// RUN: -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM,LLVMCIR --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fstrict-vtable-pointers -O1 \ +// RUN: -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s + +void some_function(); + +struct Base { + virtual ~Base(); +}; + +struct Derived : Base { + virtual ~Derived(); +}; + +Derived::~Derived() { some_function(); } + +// CIR-LABEL: cir.func {{.*}} @_ZN7DerivedD2Ev( +// CIR-NEXT: %[[THIS_ADDR:.*]] = cir.alloca "this" {{.*}} init +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CIR-NEXT: %[[LAUNDERED:.*]] = cir.launder %[[THIS]] +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: %[[DERIVED_VPTR:.*]] = cir.vtable.address_point(@_ZTV7Derived, address_point = <index = 0, offset = 2>) : !cir.vptr +// CIR-NEXT: %[[DERIVED_VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[LAUNDERED]] : !cir.ptr<!rec_Derived> -> !cir.ptr<!cir.vptr> +// CIR-NEXT: cir.store{{.*}} %[[DERIVED_VPTR]], %[[DERIVED_VPTR_ADDR]] : !cir.vptr, !cir.ptr<!cir.vptr> +// CIR-NEXT: cir.call @_Z13some_functionv() +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: %[[BASE_ADDR:.*]] = cir.base_class_addr %[[LAUNDERED]] : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base> +// CIR-NEXT: cir.call @_ZN4BaseD2Ev(%[[BASE_ADDR]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } +// CIR-NEXT: cir.return + +// LLVM-LABEL: define{{.*}} void @_ZN7DerivedD2Ev( +// LLVM: %[[LAUNDERED:.*]] = {{.*}}call ptr @llvm.launder.invariant.group.p0(ptr {{.*}}) +// LLVMCIR-NEXT:store ptr getelementptr inbounds nuw (i8, ptr @_ZTV7Derived, i64 16), ptr %[[LAUNDERED]] +// OGCG-NEXT: store ptr getelementptr inbounds nuw inrange(-16, 16) (i8, ptr @_ZTV7Derived, i64 16), ptr %[[LAUNDERED]] +// LLVM: call void @_Z13some_functionv() +// LLVM: call void @_ZN4BaseD2Ev(ptr {{.*}}%[[LAUNDERED]]) +// LLVM: ret void diff --git a/clang/test/CIR/CodeGen/destructor-vtable-reinit.cpp b/clang/test/CIR/CodeGen/destructor-vtable-reinit.cpp new file mode 100644 index 0000000000000..887c22588d263 --- /dev/null +++ b/clang/test/CIR/CodeGen/destructor-vtable-reinit.cpp @@ -0,0 +1,179 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM,LLVMCIR --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s + +void some_function(); + +struct Base { + virtual ~Base(); +}; + +struct Derived : Base { + virtual ~Derived(); +}; + +Base::~Base() { some_function(); } + +// CIR-LABEL: cir.func {{.*}} @_ZN4BaseD2Ev( +// CIR-NEXT: %[[THIS_ADDR:.*]] = cir.alloca "this" {{.*}} init +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CIR-NEXT: %[[BASE_VPTR:.*]] = cir.vtable.address_point(@_ZTV4Base, address_point = <index = 0, offset = 2>) : !cir.vptr +// CIR-NEXT: %[[BASE_VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr<!rec_Base> -> !cir.ptr<!cir.vptr> +// CIR-NEXT: cir.store{{.*}} %[[BASE_VPTR]], %[[BASE_VPTR_ADDR]] : !cir.vptr, !cir.ptr<!cir.vptr> +// CIR-NEXT: cir.call @_Z13some_functionv() +// CIR-NEXT: cir.return + +// LLVM-LABEL: define{{.*}} void @_ZN4BaseD2Ev( +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVMCIR-NEXT:store ptr getelementptr inbounds nuw (i8, ptr @_ZTV4Base, i64 16), ptr %[[THIS]] +// OGCG-NEXT: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTV4Base, i32 0, i32 0, i32 2), ptr %[[THIS]] +// LLVM-NEXT: call void @_Z13some_functionv() +// LLVM-NEXT: ret void + + +Derived::~Derived() { some_function(); } + +// CIR-LABEL: cir.func {{.*}} @_ZN7DerivedD2Ev( +// CIR-NEXT: %[[THIS_ADDR:.*]] = cir.alloca "this" {{.*}} init +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: %[[DERIVED_VPTR:.*]] = cir.vtable.address_point(@_ZTV7Derived, address_point = <index = 0, offset = 2>) : !cir.vptr +// CIR-NEXT: %[[DERIVED_VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr<!rec_Derived> -> !cir.ptr<!cir.vptr> +// CIR-NEXT: cir.store{{.*}} %[[DERIVED_VPTR]], %[[DERIVED_VPTR_ADDR]] : !cir.vptr, !cir.ptr<!cir.vptr> +// CIR-NEXT: cir.call @_Z13some_functionv() +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: %[[BASE_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_Base> +// CIR-NEXT: cir.call @_ZN4BaseD2Ev(%[[BASE_ADDR]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } +// CIR-NEXT: cir.return + +// Cleanup scopes insert a bunch of empty blocks, so we can't use LLVM-NEXT as +// aggressively as I'd like. +// LLVM-LABEL: define{{.*}} void @_ZN7DerivedD2Ev( +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVMCIR: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV7Derived, i64 16), ptr %[[THIS]] +// OGCG: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTV7Derived, i32 0, i32 0, i32 2), ptr %[[THIS]] +// LLVM-NEXT: call void @_Z13some_functionv() +// LLVM: call void @_ZN4BaseD2Ev(ptr {{.*}}%[[THIS]]) +// LLVM: ret void + + +// A destructor of an effectively-final class never needs to reinitialize its +// vtable pointer, since it's already known to point at the class's own +// vtable. +struct FinalDerived final : Base { + virtual ~FinalDerived(); +}; + +FinalDerived::~FinalDerived() { some_function(); } + +// CIR-LABEL: cir.func {{.*}} @_ZN12FinalDerivedD2Ev( +// CIR-NEXT: %[[THIS_ADDR:.*]] = cir.alloca "this" {{.*}} init +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: cir.call @_Z13some_functionv() +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: %[[BASE_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_FinalDerived> nonnull [0] -> !cir.ptr<!rec_Base> +// CIR-NEXT: cir.call @_ZN4BaseD2Ev(%[[BASE_ADDR]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } +// CIR-NEXT: cir.return + +// LLVM-LABEL: define{{.*}} void @_ZN12FinalDerivedD2Ev( +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVM-NOT: store ptr {{.*}}@_ZTV12FinalDerived +// LLVM: call void @_Z13some_functionv() +// LLVM: call void @_ZN4BaseD2Ev(ptr {{.*}}%[[THIS]]) +// LLVM: ret void + + +// A destructor with a trivial body (and no non-trivial field destructors) +// also never needs to reinitialize the vtable pointer. +struct TrivialDtor : Base { + virtual ~TrivialDtor(); +}; + +TrivialDtor::~TrivialDtor() {} + +// CIR-LABEL: cir.func {{.*}} @_ZN11TrivialDtorD2Ev( +// CIR-NEXT: %[[THIS_ADDR:.*]] = cir.alloca "this" {{.*}} init +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: %[[BASE_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_TrivialDtor> nonnull [0] -> !cir.ptr<!rec_Base> +// CIR-NEXT: cir.call @_ZN4BaseD2Ev(%[[BASE_ADDR]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } +// CIR-NEXT: cir.return + +// LLVM-LABEL: define{{.*}} void @_ZN11TrivialDtorD2Ev( +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVM-NOT: store ptr {{.*}}@_ZTV11TrivialDtor +// LLVM: call void @_ZN4BaseD2Ev(ptr {{.*}}%[[THIS]]) +// LLVM: ret void + + +// A class with more than one non-virtual polymorphic base has more than one +// vtable pointer of its own to reinitialize. +struct Mother { + virtual ~Mother(); +}; +struct Father { + virtual ~Father(); +}; +struct MultiBase : Mother, Father { + virtual ~MultiBase(); +}; + +MultiBase::~MultiBase() { some_function(); } + +// CIR-LABEL: cir.func {{.*}} @_ZN9MultiBaseD2Ev( +// CIR-NEXT: %[[THIS_ADDR:.*]] = cir.alloca "this" {{.*}} init +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: cir.cleanup.scope { +// CIR-NEXT: %[[MOTHER_VPTR:.*]] = cir.vtable.address_point(@_ZTV9MultiBase, address_point = <index = 0, offset = 2>) : !cir.vptr +// CIR-NEXT: %[[MOTHER_VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr<!rec_MultiBase> -> !cir.ptr<!cir.vptr> +// CIR-NEXT: cir.store{{.*}} %[[MOTHER_VPTR]], %[[MOTHER_VPTR_ADDR]] : !cir.vptr, !cir.ptr<!cir.vptr> +// CIR-NEXT: %[[FATHER_VPTR:.*]] = cir.vtable.address_point(@_ZTV9MultiBase, address_point = <index = 1, offset = 2>) : !cir.vptr +// CIR-NEXT: %[[FATHER_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_MultiBase> nonnull [8] -> !cir.ptr<!rec_Father> +// CIR-NEXT: %[[FATHER_VPTR_ADDR:.*]] = cir.vtable.get_vptr %[[FATHER_ADDR]] : !cir.ptr<!rec_Father> -> !cir.ptr<!cir.vptr> +// CIR-NEXT: cir.store{{.*}} %[[FATHER_VPTR]], %[[FATHER_VPTR_ADDR]] : !cir.vptr, !cir.ptr<!cir.vptr> +// CIR-NEXT: cir.call @_Z13some_functionv() +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: %[[FATHER_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_MultiBase> nonnull [8] -> !cir.ptr<!rec_Father> +// CIR-NEXT: cir.call @_ZN6FatherD2Ev(%[[FATHER_ADDR]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } +// CIR-NEXT: cir.yield +// CIR-NEXT: } cleanup normal { +// CIR-NEXT: %[[MOTHER_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_MultiBase> nonnull [0] -> !cir.ptr<!rec_Mother> +// CIR-NEXT: cir.call @_ZN6MotherD2Ev(%[[MOTHER_ADDR]]) +// CIR-NEXT: cir.yield +// CIR-NEXT: } +// CIR-NEXT: cir.return + +// LLVM-LABEL: define{{.*}} void @_ZN9MultiBaseD2Ev( +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVMCIR: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV9MultiBase, i64 16), ptr %[[THIS]] +// OGCG: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr], [4 x ptr] }, ptr @_ZTV9MultiBase, i32 0, i32 0, i32 2), ptr %[[THIS]] +// LLVM: %[[FATHER_ADDR:.*]] = getelementptr {{.*}}i8, ptr %[[THIS]], i{{32|64}} 8 +// LLVMCIR: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV9MultiBase, i64 48), ptr %[[FATHER_ADDR]] +// OGCG: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr], [4 x ptr] }, ptr @_ZTV9MultiBase, i32 0, i32 1, i32 2), ptr %[[FATHER_ADDR]] +// LLVM: call void @_Z13some_functionv() +// LLVM: call void @_ZN6FatherD2Ev(ptr {{.*}}) +// LLVM: call void @_ZN6MotherD2Ev(ptr {{.*}}%[[THIS]]) +// LLVM: ret void >From 926e819520a5f94a9004201b0f7d9dcbc28d60a5 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Fri, 4 Sep 2026 08:14:30 -0700 Subject: [PATCH 2/2] Re-base onto the 'main' extraction of the helper functions in CodeGenUtils --- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 84 +----------------------- 1 file changed, 3 insertions(+), 81 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index ba92375c7231f..6ec51939e02dc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -21,6 +21,7 @@ #include "clang/AST/GlobalDecl.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" +#include "clang/CodeGenUtils/CodeGenUtils.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/IR/FPEnv.h" @@ -888,86 +889,6 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { "emitConstructorBody: emit body statement failed."); } -static bool fieldHasTrivialDestructorBody(ASTContext &context, - const FieldDecl *field); - -static bool -hasTrivialDestructorBody(ASTContext &context, - const CXXRecordDecl *baseClassDecl, - const CXXRecordDecl *mostDerivedClassDecl) { - // If the destructor is trivial we don't have to check anything else. - if (baseClassDecl->hasTrivialDestructor()) - return true; - - if (!baseClassDecl->getDestructor()->hasTrivialBody()) - return false; - - // Check fields. - for (const auto *field : baseClassDecl->fields()) - if (!fieldHasTrivialDestructorBody(context, field)) - return false; - - // Check non-virtual bases. - for (const auto &base : baseClassDecl->bases()) { - if (base.isVirtual()) - continue; - - const auto *nonVirtualBase = base.getType()->castAsCXXRecordDecl(); - if (!hasTrivialDestructorBody(context, nonVirtualBase, - mostDerivedClassDecl)) - return false; - } - - if (baseClassDecl == mostDerivedClassDecl) { - // Check virtual bases. - for (const auto &vbase : baseClassDecl->vbases()) { - const auto *virtualBase = vbase.getType()->castAsCXXRecordDecl(); - if (!hasTrivialDestructorBody(context, virtualBase, mostDerivedClassDecl)) - return false; - } - } - return true; -} - -static bool fieldHasTrivialDestructorBody(ASTContext &context, - const FieldDecl *field) { - QualType fieldBaseElementType = context.getBaseElementType(field->getType()); - - auto *fieldClassDecl = fieldBaseElementType->getAsCXXRecordDecl(); - if (!fieldClassDecl) - return true; - - // The destructor for an implicit anonymous union member is never invoked. - if (fieldClassDecl->isUnion() && fieldClassDecl->isAnonymousStructOrUnion()) - return true; - - return hasTrivialDestructorBody(context, fieldClassDecl, fieldClassDecl); -} - -/// Check whether we need to initialize any vtable pointers before calling -/// this destructor. -static bool canSkipVTablePointerInitialization(CIRGenFunction &cgf, - const CXXDestructorDecl *dtor) { - const CXXRecordDecl *classDecl = dtor->getParent(); - if (!classDecl->isDynamicClass()) - return true; - - // For a final class, the vtable pointer is known to already point to the - // class's vtable. - if (classDecl->isEffectivelyFinal()) - return true; - - if (!dtor->hasTrivialBody()) - return false; - - // Check the fields. - for (const auto *field : classDecl->fields()) - if (!fieldHasTrivialDestructorBody(cgf.getContext(), field)) - return false; - - return true; -} - /// Emits the body of the current destructor. void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl()); @@ -1052,7 +973,8 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { case Dtor_Base: assert(body); - bool needsVTableInit = !canSkipVTablePointerInitialization(*this, dtor); + bool needsVTableInit = + !CodeGenUtils::canSkipVTablePointerInitialization(getContext(), dtor); // Launder 'this' if necessary. if (needsVTableInit && cgm.getCodeGenOpts().StrictVTablePointers && cgm.getCodeGenOpts().OptimizationLevel > 0) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
