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

Reply via email to