diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 598e826a473a6..89efe39650aa9 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -240,6 +240,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 {{.*}}) +// CIR: %[[X_ADDR:.*]] = cir.alloca !cir.ptr +// CIR: cir.store %[[X_ARG]], %[[X_ADDR]] +// CIR: %[[X:.*]] = cir.load{{.*}} %[[X_ADDR]] +// CIR: %[[VTABLE_PTR:.*]] = cir.vtable.get_vptr %[[X]] : !cir.ptr -> !cir.ptr +// CIR: %[[VTABLE:.*]] = cir.load{{.*}} %[[VTABLE_PTR]] : !cir.ptr, !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]])