Author: Andy Kaylor
Date: 2025-10-24T11:04:42-07:00
New Revision: 224f18e549c42233e1cc597873803a183927dfb3

URL: 
https://github.com/llvm/llvm-project/commit/224f18e549c42233e1cc597873803a183927dfb3
DIFF: 
https://github.com/llvm/llvm-project/commit/224f18e549c42233e1cc597873803a183927dfb3.diff

LOG: [CIR] Handle operator delete with virtual destructors (#165010)

This adds support for emitting operator delete when used with classes
that have a virtual destructor.

Added: 
    

Modified: 
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenCXXABI.h
    clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
    clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
    clang/test/CIR/CodeGen/delete.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index d5bc5ce8d8997..369ac3dc37b56 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -241,6 +241,7 @@ struct MissingFeatures {
   static bool dataLayoutPtrHandlingBasedOnLangAS() { return false; }
   static bool deferredCXXGlobalInit() { return false; }
   static bool deleteArray() { return false; }
+  static bool devirtualizeDestructor() { return false; }
   static bool devirtualizeMemberFunction() { return false; }
   static bool ehCleanupFlags() { return false; }
   static bool ehCleanupHasPrebranchedFallthrough() { return false; }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h 
b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index d3c7dac04dc67..13dc9f305945a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -187,6 +187,11 @@ class CIRGenCXXABI {
   virtual void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor,
                                   mlir::Value addr) = 0;
 
+  virtual void emitVirtualObjectDelete(CIRGenFunction &cgf,
+                                       const CXXDeleteExpr *de, Address ptr,
+                                       QualType elementType,
+                                       const CXXDestructorDecl *dtor) = 0;
+
   /// Checks if ABI requires extra virtual offset for vtable field.
   virtual bool
   isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf,

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index a3cdf192bf9b8..7a35382e79a93 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -565,8 +565,10 @@ static void emitObjectDelete(CIRGenFunction &cgf, const 
CXXDeleteExpr *de,
       dtor = rd->getDestructor();
 
       if (dtor->isVirtual()) {
-        cgf.cgm.errorNYI(de->getSourceRange(),
-                         "emitObjectDelete: virtual destructor");
+        assert(!cir::MissingFeatures::devirtualizeDestructor());
+        cgf.cgm.getCXXABI().emitVirtualObjectDelete(cgf, de, ptr, elementType,
+                                                    dtor);
+        return;
       }
     }
   }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp 
b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 2dce0b16e3043..88fedf1acc6a1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -74,6 +74,9 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
                           QualType thisTy) override;
   void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor,
                           mlir::Value addr) override;
+  void emitVirtualObjectDelete(CIRGenFunction &cgf, const CXXDeleteExpr *de,
+                               Address ptr, QualType elementType,
+                               const CXXDestructorDecl *dtor) override;
 
   void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override;
   void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) override;
@@ -2175,6 +2178,21 @@ mlir::Value 
CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf,
                                         isRefCast, castInfo);
 }
 
+/// The Itanium ABI always places an offset to the complete object
+/// at entry -2 in the vtable.
+void CIRGenItaniumCXXABI::emitVirtualObjectDelete(
+    CIRGenFunction &cgf, const CXXDeleteExpr *delExpr, Address ptr,
+    QualType elementType, const CXXDestructorDecl *dtor) {
+  bool useGlobalDelete = delExpr->isGlobalDelete();
+  if (useGlobalDelete) {
+    cgf.cgm.errorNYI(delExpr->getSourceRange(),
+                     "emitVirtualObjectDelete: global delete");
+  }
+
+  CXXDtorType dtorType = useGlobalDelete ? Dtor_Complete : Dtor_Deleting;
+  emitVirtualDestructorCall(cgf, dtor, dtorType, ptr, delExpr);
+}
+
 /************************** Array allocation cookies 
**************************/
 
 CharUnits CIRGenItaniumCXXABI::getArrayCookieSizeImpl(QualType elementType) {

diff  --git a/clang/test/CIR/CodeGen/delete.cpp 
b/clang/test/CIR/CodeGen/delete.cpp
index 69640aa04531f..d8ac4361bb538 100644
--- a/clang/test/CIR/CodeGen/delete.cpp
+++ b/clang/test/CIR/CodeGen/delete.cpp
@@ -86,3 +86,42 @@ Container::~Container() { delete contents; }
 // These functions are declared/defined below the calls in OGCG.
 // OGCG: define linkonce_odr void @_ZN8ContentsD2Ev
 // OGCG: declare void @_ZdlPvm(ptr noundef, i64 noundef)
+
+struct StructWithVirtualDestructor {
+  virtual ~StructWithVirtualDestructor();
+};
+
+void destroy(StructWithVirtualDestructor *x) {
+  delete x;
+}
+
+// CIR: cir.func {{.*}} 
@_Z7destroyP27StructWithVirtualDestructor(%[[X_ARG:.*]]: 
!cir.ptr<!rec_StructWithVirtualDestructor> {{.*}})
+// CIR:   %[[X_ADDR:.*]] = cir.alloca 
!cir.ptr<!rec_StructWithVirtualDestructor>
+// CIR:   cir.store %[[X_ARG]], %[[X_ADDR]]
+// CIR:   %[[X:.*]] = cir.load{{.*}} %[[X_ADDR]]
+// CIR:   %[[VTABLE_PTR:.*]] = cir.vtable.get_vptr %[[X]] : 
!cir.ptr<!rec_StructWithVirtualDestructor> -> !cir.ptr<!cir.vptr>
+// CIR:   %[[VTABLE:.*]] = cir.load{{.*}} %[[VTABLE_PTR]] : 
!cir.ptr<!cir.vptr>, !cir.vptr
+// CIR:   %[[DTOR_FN_ADDR_PTR:.*]] = cir.vtable.get_virtual_fn_addr 
%[[VTABLE]][1]
+// CIR:   %[[DTOR_FN_ADDR:.*]] = cir.load{{.*}} %[[DTOR_FN_ADDR_PTR]]
+// CIR:   cir.call %[[DTOR_FN_ADDR]](%[[X]])
+
+// LLVM: define {{.*}} void @_Z7destroyP27StructWithVirtualDestructor(ptr 
%[[X_ARG:.*]])
+// LLVM:   %[[X_ADDR:.*]] = alloca ptr
+// LLVM:   store ptr %[[X_ARG]], ptr %[[X_ADDR]]
+// LLVM:   %[[X:.*]] = load ptr, ptr %[[X_ADDR]]
+// LLVM:   %[[VTABLE:.*]] = load ptr, ptr %[[X]]
+// LLVM:   %[[DTOR_FN_ADDR_PTR:.*]] = getelementptr inbounds ptr, ptr 
%[[VTABLE]], i32 1
+// LLVM:   %[[DTOR_FN_ADDR:.*]] = load ptr, ptr %[[DTOR_FN_ADDR_PTR]]
+// LLVM:   call void %[[DTOR_FN_ADDR]](ptr %[[X]])
+
+// OGCG: define {{.*}} void @_Z7destroyP27StructWithVirtualDestructor(ptr 
{{.*}} %[[X_ARG:.*]])
+// OGCG:   %[[X_ADDR:.*]] = alloca ptr
+// OGCG:   store ptr %[[X_ARG]], ptr %[[X_ADDR]]
+// OGCG:   %[[X:.*]] = load ptr, ptr %[[X_ADDR]]
+// OGCG:   %[[ISNULL:.*]] = icmp eq ptr %[[X]], null
+// OGCG:   br i1 %[[ISNULL]], label %{{.*}}, label %[[DELETE_NOTNULL:.*]]
+// OGCG: [[DELETE_NOTNULL]]:
+// OGCG:   %[[VTABLE:.*]] = load ptr, ptr %[[X]]
+// OGCG:   %[[DTOR_FN_ADDR_PTR:.*]] = getelementptr inbounds ptr, ptr 
%[[VTABLE]], i64 1
+// OGCG:   %[[DTOR_FN_ADDR:.*]] = load ptr, ptr %[[DTOR_FN_ADDR_PTR]]
+// OGCG:   call void %[[DTOR_FN_ADDR]](ptr {{.*}} %[[X]])


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

Reply via email to