-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[CIR] Add support for virtual destructor calls #162725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This adds support for calling virtual destructors.
@llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis adds support for calling virtual destructors. Full diff: https://github.com/llvm/llvm-project/pull/162725.diff 9 Files Affected:
diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h
index fb74aa0f3bb00..683ff396eb5cb 100644
--- a/clang/lib/CIR/CodeGen/Address.h
+++ b/clang/lib/CIR/CodeGen/Address.h
@@ -90,6 +90,13 @@ class Address {
return getPointer();
}
+ /// Return the pointer contained in this class after authenticating it and
+ /// adding offset to it if necessary.
+ mlir::Value emitRawPointer() const {
+ // TODO(cir): update this class with latest traditional LLVM codegen bits
+ return getBasePointer();
+ }
+
mlir::Type getType() const {
assert(mlir::cast<cir::PointerType>(
pointerAndKnownNonNull.getPointer().getType())
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 06f41cd8fcfdb..6d3741c417351 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -191,6 +191,15 @@ class CIRGenCXXABI {
virtual void emitVTableDefinitions(CIRGenVTables &cgvt,
const CXXRecordDecl *rd) = 0;
+ using DeleteOrMemberCallExpr =
+ llvm::PointerUnion<const CXXDeleteExpr *, const CXXMemberCallExpr *>;
+
+ virtual mlir::Value emitVirtualDestructorCall(CIRGenFunction &cgf,
+ const CXXDestructorDecl *dtor,
+ CXXDtorType dtorType,
+ Address thisAddr,
+ DeleteOrMemberCallExpr e) = 0;
+
/// Emit any tables needed to implement virtual inheritance. For Itanium,
/// this emits virtual table tables.
virtual void emitVirtualInheritanceTables(const CXXRecordDecl *rd) = 0;
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 485b2c86cbc58..dd357ce69f1b3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -895,6 +895,26 @@ void CIRGenFunction::destroyCXXObject(CIRGenFunction &cgf, Address addr,
}
namespace {
+mlir::Value loadThisForDtorDelete(CIRGenFunction &cgf,
+ const CXXDestructorDecl *dd) {
+ if (Expr *thisArg = dd->getOperatorDeleteThisArg())
+ return cgf.emitScalarExpr(thisArg);
+ return cgf.loadCXXThis();
+}
+
+/// Call the operator delete associated with the current destructor.
+struct CallDtorDelete final : EHScopeStack::Cleanup {
+ CallDtorDelete() {}
+
+ void emit(CIRGenFunction &cgf) override {
+ const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(cgf.curFuncDecl);
+ const CXXRecordDecl *classDecl = dtor->getParent();
+ cgf.emitDeleteCall(dtor->getOperatorDelete(),
+ loadThisForDtorDelete(cgf, dtor),
+ cgf.getContext().getCanonicalTagType(classDecl));
+ }
+};
+
class DestroyField final : public EHScopeStack::Cleanup {
const FieldDecl *field;
CIRGenFunction::Destroyer *destroyer;
@@ -932,7 +952,18 @@ void CIRGenFunction::enterDtorCleanups(const CXXDestructorDecl *dd,
// The deleting-destructor phase just needs to call the appropriate
// operator delete that Sema picked up.
if (dtorType == Dtor_Deleting) {
- cgm.errorNYI(dd->getSourceRange(), "deleting destructor cleanups");
+ assert(dd->getOperatorDelete() &&
+ "operator delete missing - EnterDtorCleanups");
+ if (cxxStructorImplicitParamValue) {
+ cgm.errorNYI(dd->getSourceRange(), "deleting destructor with vtt");
+ } else {
+ if (dd->getOperatorDelete()->isDestroyingOperatorDelete()) {
+ cgm.errorNYI(dd->getSourceRange(),
+ "deleting destructor with destroying operator delete");
+ } else {
+ ehStack.pushCleanup<CallDtorDelete>(NormalAndEHCleanup);
+ }
+ }
return;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 97c0944fca336..2d11cee38b4c6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -130,13 +130,11 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
const CXXMethodDecl *calleeDecl =
devirtualizedMethod ? devirtualizedMethod : md;
const CIRGenFunctionInfo *fInfo = nullptr;
- if (isa<CXXDestructorDecl>(calleeDecl)) {
- cgm.errorNYI(ce->getSourceRange(),
- "emitCXXMemberOrOperatorMemberCallExpr: destructor call");
- return RValue::get(nullptr);
- }
-
- fInfo = &cgm.getTypes().arrangeCXXMethodDeclaration(calleeDecl);
+ if (const auto *dtor = dyn_cast<CXXDestructorDecl>(calleeDecl))
+ fInfo = &cgm.getTypes().arrangeCXXStructorDeclaration(
+ GlobalDecl(dtor, Dtor_Complete));
+ else
+ fInfo = &cgm.getTypes().arrangeCXXMethodDeclaration(calleeDecl);
cir::FuncType ty = cgm.getTypes().getFunctionType(*fInfo);
@@ -151,9 +149,34 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
// because then we know what the type is.
bool useVirtualCall = canUseVirtualCall && !devirtualizedMethod;
- if (isa<CXXDestructorDecl>(calleeDecl)) {
- cgm.errorNYI(ce->getSourceRange(),
- "emitCXXMemberOrOperatorMemberCallExpr: destructor call");
+ if (const auto *dtor = dyn_cast<CXXDestructorDecl>(calleeDecl)) {
+ assert(ce->arg_begin() == ce->arg_end() &&
+ "Destructor shouldn't have explicit parameters");
+ assert(returnValue.isNull() && "Destructor shouldn't have return value");
+ if (useVirtualCall) {
+ cgm.getCXXABI().emitVirtualDestructorCall(*this, dtor, Dtor_Complete,
+ thisPtr.getAddress(),
+ cast<CXXMemberCallExpr>(ce));
+ } else {
+ GlobalDecl globalDecl(dtor, Dtor_Complete);
+ CIRGenCallee callee;
+ assert(!cir::MissingFeatures::appleKext());
+ if (!devirtualizedMethod) {
+ callee = CIRGenCallee::forDirect(
+ cgm.getAddrOfCXXStructor(globalDecl, fInfo, ty), globalDecl);
+ } else {
+ cgm.errorNYI(ce->getSourceRange(), "devirtualized destructor call");
+ return RValue::get(nullptr);
+ }
+
+ QualType thisTy =
+ isArrow ? base->getType()->getPointeeType() : base->getType();
+ // CIRGen does not pass CallOrInvoke here (different from OG LLVM codegen)
+ // because in practice it always null even in OG.
+ emitCXXDestructorCall(globalDecl, callee, thisPtr.getPointer(), thisTy,
+ /*ImplicitParam=*/nullptr,
+ /*ImplicitParamTy=*/QualType(), ce);
+ }
return RValue::get(nullptr);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 7a774e0441bbb..f7c2fb3a68bbb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -678,7 +678,13 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
// possible to delegate the destructor body to the complete
// destructor. Do so.
if (dtorType == Dtor_Deleting) {
- cgm.errorNYI(dtor->getSourceRange(), "deleting destructor");
+ RunCleanupsScope dtorEpilogue(*this);
+ enterDtorCleanups(dtor, Dtor_Deleting);
+ if (haveInsertPoint()) {
+ QualType thisTy = dtor->getFunctionObjectParameterType();
+ emitCXXDestructorCall(dtor, Dtor_Complete, /*ForVirtualBase=*/false,
+ /*Delegating=*/false, loadCXXThisAddress(), thisTy);
+ }
return;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 9e490c6d054a4..4be03ca5f4dff 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -95,7 +95,10 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
clang::GlobalDecl gd, Address thisAddr,
mlir::Type ty,
SourceLocation loc) override;
-
+ mlir::Value emitVirtualDestructorCall(CIRGenFunction &cgf,
+ const CXXDestructorDecl *dtor,
+ CXXDtorType dtorType, Address thisAddr,
+ DeleteOrMemberCallExpr e) override;
mlir::Value getVTableAddressPoint(BaseSubobject base,
const CXXRecordDecl *vtableClass) override;
mlir::Value getVTableAddressPointInStructorWithVTT(
@@ -465,6 +468,32 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
}
}
+mlir::Value CIRGenItaniumCXXABI::emitVirtualDestructorCall(
+ CIRGenFunction &cgf, const CXXDestructorDecl *dtor, CXXDtorType dtorType,
+ Address thisAddr, DeleteOrMemberCallExpr expr) {
+ auto *callExpr = dyn_cast<const CXXMemberCallExpr *>(expr);
+ auto *delExpr = dyn_cast<const CXXDeleteExpr *>(expr);
+ assert((callExpr != nullptr) ^ (delExpr != nullptr));
+ assert(callExpr == nullptr || callExpr->arg_begin() == callExpr->arg_end());
+ assert(dtorType == Dtor_Deleting || dtorType == Dtor_Complete);
+
+ GlobalDecl globalDecl(dtor, dtorType);
+ const CIRGenFunctionInfo *fnInfo =
+ &cgm.getTypes().arrangeCXXStructorDeclaration(globalDecl);
+ const cir::FuncType &fnTy = cgm.getTypes().getFunctionType(*fnInfo);
+ auto callee = CIRGenCallee::forVirtual(callExpr, globalDecl, thisAddr, fnTy);
+
+ QualType thisTy;
+ if (callExpr)
+ thisTy = callExpr->getObjectType();
+ else
+ thisTy = delExpr->getDestroyedType();
+
+ cgf.emitCXXDestructorCall(globalDecl, callee, thisAddr.emitRawPointer(),
+ thisTy, nullptr, QualType(), nullptr);
+ return nullptr;
+}
+
void CIRGenItaniumCXXABI::emitVirtualInheritanceTables(
const CXXRecordDecl *rd) {
CIRGenVTables &vtables = cgm.getVTables();
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index e65896a9ff109..2ab1ea0c8ff8e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -619,10 +619,8 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeGlobalDeclaration(GlobalDecl gd) {
const auto *fd = cast<FunctionDecl>(gd.getDecl());
if (isa<CXXConstructorDecl>(gd.getDecl()) ||
- isa<CXXDestructorDecl>(gd.getDecl())) {
- cgm.errorNYI(SourceLocation(),
- "arrangeGlobalDeclaration for C++ constructor or destructor");
- }
+ isa<CXXDestructorDecl>(gd.getDecl()))
+ return arrangeCXXStructorDeclaration(gd);
return arrangeFunctionDeclaration(fd);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 84f59773757b5..36bab625c4dd2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -120,12 +120,6 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
assert(!cir::MissingFeatures::vtableRelativeLayout());
switch (component.getKind()) {
- case VTableComponent::CK_CompleteDtorPointer:
- cgm.errorNYI("getVTableComponent: CompleteDtorPointer");
- return mlir::Attribute();
- case VTableComponent::CK_DeletingDtorPointer:
- cgm.errorNYI("getVTableComponent: DeletingDtorPointer");
- return mlir::Attribute();
case VTableComponent::CK_UnusedFunctionPointer:
cgm.errorNYI("getVTableComponent: UnusedFunctionPointer");
return mlir::Attribute();
@@ -148,7 +142,9 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
"expected GlobalViewAttr or ConstPtrAttr");
return rtti;
- case VTableComponent::CK_FunctionPointer: {
+ case VTableComponent::CK_FunctionPointer:
+ case VTableComponent::CK_CompleteDtorPointer:
+ case VTableComponent::CK_DeletingDtorPointer: {
GlobalDecl gd = component.getGlobalDecl();
assert(!cir::MissingFeatures::cudaSupport());
diff --git a/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp b/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp
new file mode 100644
index 0000000000000..08a6b21ca91d3
--- /dev/null
+++ b/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp
@@ -0,0 +1,129 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// TODO(cir): Try to emit base destructor as an alias at O1 or higher.
+
+// FIXME: LLVM IR dialect does not yet support function ptr globals, which precludes
+// a lot of the proper semantics for properly representing alias functions in LLVM
+// (see the note on LLVM_O1 below).
+
+struct Member {
+ ~Member();
+};
+
+struct A {
+ virtual ~A();
+};
+
+struct B : A {
+ Member m;
+ virtual ~B();
+};
+
+B::~B() { }
+
+// Aliases are inserted before the function definitions in LLVM IR
+// FIXME: These should have unnamed_addr set.
+// LLVM: @_ZN1BD1Ev = alias void (ptr), ptr @_ZN1BD2Ev
+// LLVM: @_ZN1CD1Ev = alias void (ptr), ptr @_ZN1CD2Ev
+
+// OGCG: @_ZN1BD1Ev = unnamed_addr alias void (ptr), ptr @_ZN1BD2Ev
+// OGCG: @_ZN1CD1Ev = unnamed_addr alias void (ptr), ptr @_ZN1CD2Ev
+
+
+// Base (D2) dtor for B: calls A's base dtor.
+
+// CIR: cir.func{{.*}} @_ZN1BD2Ev
+// CIR: cir.call @_ZN6MemberD1Ev
+// CIR: cir.call @_ZN1AD2Ev
+
+// LLVM: define{{.*}} void @_ZN1BD2Ev
+// LLVM: call void @_ZN6MemberD1Ev
+// LLVM: call void @_ZN1AD2Ev
+
+// OGCG: define{{.*}} @_ZN1BD2Ev
+// OGCG: call void @_ZN6MemberD1Ev
+// OGCG: call void @_ZN1AD2Ev
+
+// Complete (D1) dtor for B: just an alias because there are no virtual bases.
+
+// CIR: cir.func{{.*}} @_ZN1BD1Ev(!cir.ptr<!rec_B>) alias(@_ZN1BD2Ev)
+// This is defined above for LLVM and OGCG.
+
+// Deleting (D0) dtor for B: defers to the complete dtor but also calls operator delete.
+
+// CIR: cir.func{{.*}} @_ZN1BD0Ev
+// CIR: cir.call @_ZN1BD1Ev(%[[THIS:.*]]) nothrow : (!cir.ptr<!rec_B>) -> ()
+// CIR: %[[THIS_VOID:.*]] = cir.cast bitcast %[[THIS]] : !cir.ptr<!rec_B> -> !cir.ptr<!void>
+// CIR: %[[SIZE:.*]] = cir.const #cir.int<16>
+// CIR: cir.call @_ZdlPvm(%[[THIS_VOID]], %[[SIZE]])
+
+// LLVM: define{{.*}} void @_ZN1BD0Ev
+// LLVM: call void @_ZN1BD1Ev(ptr %[[THIS:.*]])
+// LLVM: call void @_ZdlPvm(ptr %[[THIS]], i64 16)
+
+// OGCG: define{{.*}} @_ZN1BD0Ev
+// OGCG: call void @_ZN1BD1Ev(ptr{{.*}} %[[THIS:.*]])
+// OGCG: call void @_ZdlPvm(ptr{{.*}} %[[THIS]], i64{{.*}} 16)
+
+struct C : B {
+ ~C();
+};
+
+C::~C() { }
+
+// Base (D2) dtor for C: calls B's base dtor.
+
+// CIR: cir.func{{.*}} @_ZN1CD2Ev
+// CIR: %[[B:.*]] = cir.base_class_addr %[[THIS:.*]] : !cir.ptr<!rec_C> nonnull [0] -> !cir.ptr<!rec_B>
+// CIR: cir.call @_ZN1BD2Ev(%[[B]])
+
+// LLVM: define{{.*}} void @_ZN1CD2Ev
+// LLVM: call void @_ZN1BD2Ev
+
+// OGCG: define{{.*}} @_ZN1CD2Ev
+// OGCG: call void @_ZN1BD2Ev
+
+// Complete (D1) dtor for C: just an alias because there are no virtual bases.
+
+// CIR: cir.func{{.*}} @_ZN1CD1Ev(!cir.ptr<!rec_C>) alias(@_ZN1CD2Ev)
+// This is defined above for LLVM and OGCG.
+
+
+// Deleting (D0) dtor for C: defers to the complete dtor but also calls operator delete.
+
+// CIR: cir.func{{.*}} @_ZN1CD0Ev
+// CIR: cir.call @_ZN1CD1Ev(%[[THIS:.*]]) nothrow : (!cir.ptr<!rec_C>) -> ()
+// CIR: %[[THIS_VOID:.*]] = cir.cast bitcast %[[THIS]] : !cir.ptr<!rec_C> -> !cir.ptr<!void>
+// CIR: %[[SIZE:.*]] = cir.const #cir.int<16>
+// CIR: cir.call @_ZdlPvm(%[[THIS_VOID]], %[[SIZE]])
+
+// LLVM: define{{.*}} void @_ZN1CD0Ev
+// LLVM: call void @_ZN1CD1Ev(ptr %[[THIS:.*]])
+// LLVM: call void @_ZdlPvm(ptr %[[THIS]], i64 16)
+
+// OGCG: define{{.*}} @_ZN1CD0Ev
+// OGCG: call void @_ZN1CD1Ev(ptr{{.*}} %[[THIS:.*]])
+// OGCG: call void @_ZdlPvm(ptr{{.*}} %[[THIS]], i64{{.*}} 16)
+
+namespace PR12798 {
+ // A qualified call to a base class destructor should not undergo virtual
+ // dispatch. Template instantiation used to lose the qualifier.
+ struct A { virtual ~A(); };
+ template<typename T> void f(T *p) { p->A::~A(); }
+
+ // CIR: cir.func{{.*}} @_ZN7PR127981fINS_1AEEEvPT_
+ // CIR: cir.call @_ZN7PR127981AD1Ev
+
+ // LLVM: define{{.*}} @_ZN7PR127981fINS_1AEEEvPT_
+ // LLVM: call void @_ZN7PR127981AD1Ev
+
+ // OGCG: define{{.*}} @_ZN7PR127981fINS_1AEEEvPT_
+ // OGCG: call void @_ZN7PR127981AD1Ev
+
+ template void f(A*);
+}
|
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis adds support for calling virtual destructors. Full diff: https://github.com/llvm/llvm-project/pull/162725.diff 9 Files Affected:
diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h
index fb74aa0f3bb00..683ff396eb5cb 100644
--- a/clang/lib/CIR/CodeGen/Address.h
+++ b/clang/lib/CIR/CodeGen/Address.h
@@ -90,6 +90,13 @@ class Address {
return getPointer();
}
+ /// Return the pointer contained in this class after authenticating it and
+ /// adding offset to it if necessary.
+ mlir::Value emitRawPointer() const {
+ // TODO(cir): update this class with latest traditional LLVM codegen bits
+ return getBasePointer();
+ }
+
mlir::Type getType() const {
assert(mlir::cast<cir::PointerType>(
pointerAndKnownNonNull.getPointer().getType())
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index 06f41cd8fcfdb..6d3741c417351 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -191,6 +191,15 @@ class CIRGenCXXABI {
virtual void emitVTableDefinitions(CIRGenVTables &cgvt,
const CXXRecordDecl *rd) = 0;
+ using DeleteOrMemberCallExpr =
+ llvm::PointerUnion<const CXXDeleteExpr *, const CXXMemberCallExpr *>;
+
+ virtual mlir::Value emitVirtualDestructorCall(CIRGenFunction &cgf,
+ const CXXDestructorDecl *dtor,
+ CXXDtorType dtorType,
+ Address thisAddr,
+ DeleteOrMemberCallExpr e) = 0;
+
/// Emit any tables needed to implement virtual inheritance. For Itanium,
/// this emits virtual table tables.
virtual void emitVirtualInheritanceTables(const CXXRecordDecl *rd) = 0;
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 485b2c86cbc58..dd357ce69f1b3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -895,6 +895,26 @@ void CIRGenFunction::destroyCXXObject(CIRGenFunction &cgf, Address addr,
}
namespace {
+mlir::Value loadThisForDtorDelete(CIRGenFunction &cgf,
+ const CXXDestructorDecl *dd) {
+ if (Expr *thisArg = dd->getOperatorDeleteThisArg())
+ return cgf.emitScalarExpr(thisArg);
+ return cgf.loadCXXThis();
+}
+
+/// Call the operator delete associated with the current destructor.
+struct CallDtorDelete final : EHScopeStack::Cleanup {
+ CallDtorDelete() {}
+
+ void emit(CIRGenFunction &cgf) override {
+ const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(cgf.curFuncDecl);
+ const CXXRecordDecl *classDecl = dtor->getParent();
+ cgf.emitDeleteCall(dtor->getOperatorDelete(),
+ loadThisForDtorDelete(cgf, dtor),
+ cgf.getContext().getCanonicalTagType(classDecl));
+ }
+};
+
class DestroyField final : public EHScopeStack::Cleanup {
const FieldDecl *field;
CIRGenFunction::Destroyer *destroyer;
@@ -932,7 +952,18 @@ void CIRGenFunction::enterDtorCleanups(const CXXDestructorDecl *dd,
// The deleting-destructor phase just needs to call the appropriate
// operator delete that Sema picked up.
if (dtorType == Dtor_Deleting) {
- cgm.errorNYI(dd->getSourceRange(), "deleting destructor cleanups");
+ assert(dd->getOperatorDelete() &&
+ "operator delete missing - EnterDtorCleanups");
+ if (cxxStructorImplicitParamValue) {
+ cgm.errorNYI(dd->getSourceRange(), "deleting destructor with vtt");
+ } else {
+ if (dd->getOperatorDelete()->isDestroyingOperatorDelete()) {
+ cgm.errorNYI(dd->getSourceRange(),
+ "deleting destructor with destroying operator delete");
+ } else {
+ ehStack.pushCleanup<CallDtorDelete>(NormalAndEHCleanup);
+ }
+ }
return;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 97c0944fca336..2d11cee38b4c6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -130,13 +130,11 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
const CXXMethodDecl *calleeDecl =
devirtualizedMethod ? devirtualizedMethod : md;
const CIRGenFunctionInfo *fInfo = nullptr;
- if (isa<CXXDestructorDecl>(calleeDecl)) {
- cgm.errorNYI(ce->getSourceRange(),
- "emitCXXMemberOrOperatorMemberCallExpr: destructor call");
- return RValue::get(nullptr);
- }
-
- fInfo = &cgm.getTypes().arrangeCXXMethodDeclaration(calleeDecl);
+ if (const auto *dtor = dyn_cast<CXXDestructorDecl>(calleeDecl))
+ fInfo = &cgm.getTypes().arrangeCXXStructorDeclaration(
+ GlobalDecl(dtor, Dtor_Complete));
+ else
+ fInfo = &cgm.getTypes().arrangeCXXMethodDeclaration(calleeDecl);
cir::FuncType ty = cgm.getTypes().getFunctionType(*fInfo);
@@ -151,9 +149,34 @@ RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr(
// because then we know what the type is.
bool useVirtualCall = canUseVirtualCall && !devirtualizedMethod;
- if (isa<CXXDestructorDecl>(calleeDecl)) {
- cgm.errorNYI(ce->getSourceRange(),
- "emitCXXMemberOrOperatorMemberCallExpr: destructor call");
+ if (const auto *dtor = dyn_cast<CXXDestructorDecl>(calleeDecl)) {
+ assert(ce->arg_begin() == ce->arg_end() &&
+ "Destructor shouldn't have explicit parameters");
+ assert(returnValue.isNull() && "Destructor shouldn't have return value");
+ if (useVirtualCall) {
+ cgm.getCXXABI().emitVirtualDestructorCall(*this, dtor, Dtor_Complete,
+ thisPtr.getAddress(),
+ cast<CXXMemberCallExpr>(ce));
+ } else {
+ GlobalDecl globalDecl(dtor, Dtor_Complete);
+ CIRGenCallee callee;
+ assert(!cir::MissingFeatures::appleKext());
+ if (!devirtualizedMethod) {
+ callee = CIRGenCallee::forDirect(
+ cgm.getAddrOfCXXStructor(globalDecl, fInfo, ty), globalDecl);
+ } else {
+ cgm.errorNYI(ce->getSourceRange(), "devirtualized destructor call");
+ return RValue::get(nullptr);
+ }
+
+ QualType thisTy =
+ isArrow ? base->getType()->getPointeeType() : base->getType();
+ // CIRGen does not pass CallOrInvoke here (different from OG LLVM codegen)
+ // because in practice it always null even in OG.
+ emitCXXDestructorCall(globalDecl, callee, thisPtr.getPointer(), thisTy,
+ /*ImplicitParam=*/nullptr,
+ /*ImplicitParamTy=*/QualType(), ce);
+ }
return RValue::get(nullptr);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 7a774e0441bbb..f7c2fb3a68bbb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -678,7 +678,13 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
// possible to delegate the destructor body to the complete
// destructor. Do so.
if (dtorType == Dtor_Deleting) {
- cgm.errorNYI(dtor->getSourceRange(), "deleting destructor");
+ RunCleanupsScope dtorEpilogue(*this);
+ enterDtorCleanups(dtor, Dtor_Deleting);
+ if (haveInsertPoint()) {
+ QualType thisTy = dtor->getFunctionObjectParameterType();
+ emitCXXDestructorCall(dtor, Dtor_Complete, /*ForVirtualBase=*/false,
+ /*Delegating=*/false, loadCXXThisAddress(), thisTy);
+ }
return;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index 9e490c6d054a4..4be03ca5f4dff 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -95,7 +95,10 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
clang::GlobalDecl gd, Address thisAddr,
mlir::Type ty,
SourceLocation loc) override;
-
+ mlir::Value emitVirtualDestructorCall(CIRGenFunction &cgf,
+ const CXXDestructorDecl *dtor,
+ CXXDtorType dtorType, Address thisAddr,
+ DeleteOrMemberCallExpr e) override;
mlir::Value getVTableAddressPoint(BaseSubobject base,
const CXXRecordDecl *vtableClass) override;
mlir::Value getVTableAddressPointInStructorWithVTT(
@@ -465,6 +468,32 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
}
}
+mlir::Value CIRGenItaniumCXXABI::emitVirtualDestructorCall(
+ CIRGenFunction &cgf, const CXXDestructorDecl *dtor, CXXDtorType dtorType,
+ Address thisAddr, DeleteOrMemberCallExpr expr) {
+ auto *callExpr = dyn_cast<const CXXMemberCallExpr *>(expr);
+ auto *delExpr = dyn_cast<const CXXDeleteExpr *>(expr);
+ assert((callExpr != nullptr) ^ (delExpr != nullptr));
+ assert(callExpr == nullptr || callExpr->arg_begin() == callExpr->arg_end());
+ assert(dtorType == Dtor_Deleting || dtorType == Dtor_Complete);
+
+ GlobalDecl globalDecl(dtor, dtorType);
+ const CIRGenFunctionInfo *fnInfo =
+ &cgm.getTypes().arrangeCXXStructorDeclaration(globalDecl);
+ const cir::FuncType &fnTy = cgm.getTypes().getFunctionType(*fnInfo);
+ auto callee = CIRGenCallee::forVirtual(callExpr, globalDecl, thisAddr, fnTy);
+
+ QualType thisTy;
+ if (callExpr)
+ thisTy = callExpr->getObjectType();
+ else
+ thisTy = delExpr->getDestroyedType();
+
+ cgf.emitCXXDestructorCall(globalDecl, callee, thisAddr.emitRawPointer(),
+ thisTy, nullptr, QualType(), nullptr);
+ return nullptr;
+}
+
void CIRGenItaniumCXXABI::emitVirtualInheritanceTables(
const CXXRecordDecl *rd) {
CIRGenVTables &vtables = cgm.getVTables();
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index e65896a9ff109..2ab1ea0c8ff8e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -619,10 +619,8 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeGlobalDeclaration(GlobalDecl gd) {
const auto *fd = cast<FunctionDecl>(gd.getDecl());
if (isa<CXXConstructorDecl>(gd.getDecl()) ||
- isa<CXXDestructorDecl>(gd.getDecl())) {
- cgm.errorNYI(SourceLocation(),
- "arrangeGlobalDeclaration for C++ constructor or destructor");
- }
+ isa<CXXDestructorDecl>(gd.getDecl()))
+ return arrangeCXXStructorDeclaration(gd);
return arrangeFunctionDeclaration(fd);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 84f59773757b5..36bab625c4dd2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -120,12 +120,6 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
assert(!cir::MissingFeatures::vtableRelativeLayout());
switch (component.getKind()) {
- case VTableComponent::CK_CompleteDtorPointer:
- cgm.errorNYI("getVTableComponent: CompleteDtorPointer");
- return mlir::Attribute();
- case VTableComponent::CK_DeletingDtorPointer:
- cgm.errorNYI("getVTableComponent: DeletingDtorPointer");
- return mlir::Attribute();
case VTableComponent::CK_UnusedFunctionPointer:
cgm.errorNYI("getVTableComponent: UnusedFunctionPointer");
return mlir::Attribute();
@@ -148,7 +142,9 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
"expected GlobalViewAttr or ConstPtrAttr");
return rtti;
- case VTableComponent::CK_FunctionPointer: {
+ case VTableComponent::CK_FunctionPointer:
+ case VTableComponent::CK_CompleteDtorPointer:
+ case VTableComponent::CK_DeletingDtorPointer: {
GlobalDecl gd = component.getGlobalDecl();
assert(!cir::MissingFeatures::cudaSupport());
diff --git a/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp b/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp
new file mode 100644
index 0000000000000..08a6b21ca91d3
--- /dev/null
+++ b/clang/test/CIR/CodeGen/virtual-destructor-calls.cpp
@@ -0,0 +1,129 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -std=c++20 -mconstructor-aliases -O0 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+// TODO(cir): Try to emit base destructor as an alias at O1 or higher.
+
+// FIXME: LLVM IR dialect does not yet support function ptr globals, which precludes
+// a lot of the proper semantics for properly representing alias functions in LLVM
+// (see the note on LLVM_O1 below).
+
+struct Member {
+ ~Member();
+};
+
+struct A {
+ virtual ~A();
+};
+
+struct B : A {
+ Member m;
+ virtual ~B();
+};
+
+B::~B() { }
+
+// Aliases are inserted before the function definitions in LLVM IR
+// FIXME: These should have unnamed_addr set.
+// LLVM: @_ZN1BD1Ev = alias void (ptr), ptr @_ZN1BD2Ev
+// LLVM: @_ZN1CD1Ev = alias void (ptr), ptr @_ZN1CD2Ev
+
+// OGCG: @_ZN1BD1Ev = unnamed_addr alias void (ptr), ptr @_ZN1BD2Ev
+// OGCG: @_ZN1CD1Ev = unnamed_addr alias void (ptr), ptr @_ZN1CD2Ev
+
+
+// Base (D2) dtor for B: calls A's base dtor.
+
+// CIR: cir.func{{.*}} @_ZN1BD2Ev
+// CIR: cir.call @_ZN6MemberD1Ev
+// CIR: cir.call @_ZN1AD2Ev
+
+// LLVM: define{{.*}} void @_ZN1BD2Ev
+// LLVM: call void @_ZN6MemberD1Ev
+// LLVM: call void @_ZN1AD2Ev
+
+// OGCG: define{{.*}} @_ZN1BD2Ev
+// OGCG: call void @_ZN6MemberD1Ev
+// OGCG: call void @_ZN1AD2Ev
+
+// Complete (D1) dtor for B: just an alias because there are no virtual bases.
+
+// CIR: cir.func{{.*}} @_ZN1BD1Ev(!cir.ptr<!rec_B>) alias(@_ZN1BD2Ev)
+// This is defined above for LLVM and OGCG.
+
+// Deleting (D0) dtor for B: defers to the complete dtor but also calls operator delete.
+
+// CIR: cir.func{{.*}} @_ZN1BD0Ev
+// CIR: cir.call @_ZN1BD1Ev(%[[THIS:.*]]) nothrow : (!cir.ptr<!rec_B>) -> ()
+// CIR: %[[THIS_VOID:.*]] = cir.cast bitcast %[[THIS]] : !cir.ptr<!rec_B> -> !cir.ptr<!void>
+// CIR: %[[SIZE:.*]] = cir.const #cir.int<16>
+// CIR: cir.call @_ZdlPvm(%[[THIS_VOID]], %[[SIZE]])
+
+// LLVM: define{{.*}} void @_ZN1BD0Ev
+// LLVM: call void @_ZN1BD1Ev(ptr %[[THIS:.*]])
+// LLVM: call void @_ZdlPvm(ptr %[[THIS]], i64 16)
+
+// OGCG: define{{.*}} @_ZN1BD0Ev
+// OGCG: call void @_ZN1BD1Ev(ptr{{.*}} %[[THIS:.*]])
+// OGCG: call void @_ZdlPvm(ptr{{.*}} %[[THIS]], i64{{.*}} 16)
+
+struct C : B {
+ ~C();
+};
+
+C::~C() { }
+
+// Base (D2) dtor for C: calls B's base dtor.
+
+// CIR: cir.func{{.*}} @_ZN1CD2Ev
+// CIR: %[[B:.*]] = cir.base_class_addr %[[THIS:.*]] : !cir.ptr<!rec_C> nonnull [0] -> !cir.ptr<!rec_B>
+// CIR: cir.call @_ZN1BD2Ev(%[[B]])
+
+// LLVM: define{{.*}} void @_ZN1CD2Ev
+// LLVM: call void @_ZN1BD2Ev
+
+// OGCG: define{{.*}} @_ZN1CD2Ev
+// OGCG: call void @_ZN1BD2Ev
+
+// Complete (D1) dtor for C: just an alias because there are no virtual bases.
+
+// CIR: cir.func{{.*}} @_ZN1CD1Ev(!cir.ptr<!rec_C>) alias(@_ZN1CD2Ev)
+// This is defined above for LLVM and OGCG.
+
+
+// Deleting (D0) dtor for C: defers to the complete dtor but also calls operator delete.
+
+// CIR: cir.func{{.*}} @_ZN1CD0Ev
+// CIR: cir.call @_ZN1CD1Ev(%[[THIS:.*]]) nothrow : (!cir.ptr<!rec_C>) -> ()
+// CIR: %[[THIS_VOID:.*]] = cir.cast bitcast %[[THIS]] : !cir.ptr<!rec_C> -> !cir.ptr<!void>
+// CIR: %[[SIZE:.*]] = cir.const #cir.int<16>
+// CIR: cir.call @_ZdlPvm(%[[THIS_VOID]], %[[SIZE]])
+
+// LLVM: define{{.*}} void @_ZN1CD0Ev
+// LLVM: call void @_ZN1CD1Ev(ptr %[[THIS:.*]])
+// LLVM: call void @_ZdlPvm(ptr %[[THIS]], i64 16)
+
+// OGCG: define{{.*}} @_ZN1CD0Ev
+// OGCG: call void @_ZN1CD1Ev(ptr{{.*}} %[[THIS:.*]])
+// OGCG: call void @_ZdlPvm(ptr{{.*}} %[[THIS]], i64{{.*}} 16)
+
+namespace PR12798 {
+ // A qualified call to a base class destructor should not undergo virtual
+ // dispatch. Template instantiation used to lose the qualifier.
+ struct A { virtual ~A(); };
+ template<typename T> void f(T *p) { p->A::~A(); }
+
+ // CIR: cir.func{{.*}} @_ZN7PR127981fINS_1AEEEvPT_
+ // CIR: cir.call @_ZN7PR127981AD1Ev
+
+ // LLVM: define{{.*}} @_ZN7PR127981fINS_1AEEEvPT_
+ // LLVM: call void @_ZN7PR127981AD1Ev
+
+ // OGCG: define{{.*}} @_ZN7PR127981fINS_1AEEEvPT_
+ // OGCG: call void @_ZN7PR127981AD1Ev
+
+ template void f(A*);
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, with some nits
/*ImplicitParam=*/nullptr, | ||
/*ImplicitParamTy=*/QualType(), ce); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/*ImplicitParam=*/nullptr, | |
/*ImplicitParamTy=*/QualType(), ce); | |
/*implicitParam=*/nullptr, | |
/*implicitParamTy=*/QualType(), ce); |
emitCXXDestructorCall(dtor, Dtor_Complete, /*ForVirtualBase=*/false, | ||
/*Delegating=*/false, loadCXXThisAddress(), thisTy); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
emitCXXDestructorCall(dtor, Dtor_Complete, /*ForVirtualBase=*/false, | |
/*Delegating=*/false, loadCXXThisAddress(), thisTy); | |
emitCXXDestructorCall(dtor, Dtor_Complete, /*forVirtualBase=*/false, | |
/*delegating=*/false, loadCXXThisAddress(), thisTy); |
QualType thisTy; | ||
if (callExpr) | ||
thisTy = callExpr->getObjectType(); | ||
else | ||
thisTy = delExpr->getDestroyedType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QualType thisTy; | |
if (callExpr) | |
thisTy = callExpr->getObjectType(); | |
else | |
thisTy = delExpr->getDestroyedType(); | |
QualType thisTy = callExpr ? callExpr->getObjectType() : delExpr->getDestroyedType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM after nit
clang/lib/CIR/CodeGen/Address.h
Outdated
/// Return the pointer contained in this class after authenticating it and | ||
/// adding offset to it if necessary. | ||
mlir::Value emitRawPointer() const { | ||
// TODO(cir): update this class with latest traditional LLVM codegen bits |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth changing the comment to a more accurate assert on missing feature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sure. We even already have the MissingFeature I needed.
This adds support for calling virtual destructors.
This adds support for calling virtual destructors.