Skip to content

[CIR] Add complete destructor handling #149552

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

Merged
merged 2 commits into from
Jul 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ struct MissingFeatures {
static bool writebacks() { return false; }
static bool appleKext() { return false; }
static bool dtorCleanups() { return false; }
static bool completeDtors() { return false; }
static bool vtableInitialization() { return false; }
static bool msvcBuiltins() { return false; }

Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ class CIRGenCXXABI {
/// Emit dtor variants required by this ABI.
virtual void emitCXXDestructors(const clang::CXXDestructorDecl *d) = 0;

virtual void emitDestructorCall(CIRGenFunction &cgf,
const CXXDestructorDecl *dd, CXXDtorType type,
bool forVirtualBase, bool delegating,
Address thisAddr, QualType thisTy) = 0;

/// Returns true if the given destructor type should be emitted as a linkonce
/// delegating thunk, regardless of whether the dtor is defined in this TU or
/// not.
Expand Down
23 changes: 23 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,29 @@ static void emitNewInitializer(CIRGenFunction &cgf, const CXXNewExpr *e,
}
}

RValue CIRGenFunction::emitCXXDestructorCall(
GlobalDecl dtor, const CIRGenCallee &callee, mlir::Value thisVal,
QualType thisTy, mlir::Value implicitParam, QualType implicitParamTy,
const CallExpr *ce) {
const CXXMethodDecl *dtorDecl = cast<CXXMethodDecl>(dtor.getDecl());

assert(!thisTy.isNull());
assert(thisTy->getAsCXXRecordDecl() == dtorDecl->getParent() &&
"Pointer/Object mixup");

assert(!cir::MissingFeatures::addressSpace());

CallArgList args;
commonBuildCXXMemberOrOperatorCall(*this, dtorDecl, thisVal, implicitParam,
implicitParamTy, ce, args, nullptr);
assert((ce || dtor.getDecl()) && "expected source location provider");
assert(!cir::MissingFeatures::opCallMustTail());
return emitCall(cgm.getTypes().arrangeCXXStructorDeclaration(dtor), callee,
ReturnValueSlot(), args, nullptr,
ce ? getLoc(ce->getExprLoc())
: getLoc(dtor.getDecl()->getSourceRange()));
}

/// Emit a call to an operator new or operator delete function, as implicitly
/// created by new-expressions and delete-expressions.
static RValue emitNewDeleteCall(CIRGenFunction &cgf,
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,14 @@ void CIRGenFunction::emitDelegatingCXXConstructorCall(
}
}

void CIRGenFunction::emitCXXDestructorCall(const CXXDestructorDecl *dd,
CXXDtorType type,
bool forVirtualBase, bool delegating,
Address thisAddr, QualType thisTy) {
cgm.getCXXABI().emitDestructorCall(*this, dd, type, forVirtualBase,
delegating, thisAddr, thisTy);
}

Address CIRGenFunction::getAddressOfBaseClass(
Address value, const CXXRecordDecl *derived,
llvm::iterator_range<CastExpr::path_const_iterator> path,
Expand Down
11 changes: 6 additions & 5 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,11 +593,12 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {

assert(!cir::MissingFeatures::dtorCleanups());

// TODO(cir): A complete destructor is supposed to call the base destructor.
// Since we have to emit both dtor kinds we just fall through for now and.
// As long as we don't support virtual bases this should be functionally
// equivalent.
assert(!cir::MissingFeatures::completeDtors());
if (!isTryBody) {
QualType thisTy = dtor->getFunctionObjectParameterType();
emitCXXDestructorCall(dtor, Dtor_Base, /*forVirtualBase=*/false,
/*delegating=*/false, loadCXXThisAddress(), thisTy);
break;
}

// Fallthrough: act like we're in the base variant.
[[fallthrough]];
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,15 @@ class CIRGenFunction : public CIRGenTypeCache {
bool delegating, Address thisAddr,
CallArgList &args, clang::SourceLocation loc);

void emitCXXDestructorCall(const CXXDestructorDecl *dd, CXXDtorType type,
bool forVirtualBase, bool delegating,
Address thisAddr, QualType thisTy);

RValue emitCXXDestructorCall(GlobalDecl dtor, const CIRGenCallee &callee,
mlir::Value thisVal, QualType thisTy,
mlir::Value implicitParam,
QualType implicitParamTy, const CallExpr *e);

mlir::LogicalResult emitCXXForRangeStmt(const CXXForRangeStmt &s,
llvm::ArrayRef<const Attr *> attrs);

Expand Down
24 changes: 24 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
void emitCXXDestructors(const clang::CXXDestructorDecl *d) override;
void emitCXXStructor(clang::GlobalDecl gd) override;

void emitDestructorCall(CIRGenFunction &cgf, const CXXDestructorDecl *dd,
CXXDtorType type, bool forVirtualBase,
bool delegating, Address thisAddr,
QualType thisTy) override;

bool useThunkForDtorVariant(const CXXDestructorDecl *dtor,
CXXDtorType dt) const override {
// Itanium does not emit any destructor variant as an inline thunk.
Expand Down Expand Up @@ -240,6 +245,25 @@ bool CIRGenItaniumCXXABI::needsVTTParameter(GlobalDecl gd) {
return false;
}

void CIRGenItaniumCXXABI::emitDestructorCall(
CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type,
bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) {
GlobalDecl gd(dd, type);
if (needsVTTParameter(gd)) {
cgm.errorNYI(dd->getSourceRange(), "emitDestructorCall: VTT");
}

mlir::Value vtt = nullptr;
ASTContext &astContext = cgm.getASTContext();
QualType vttTy = astContext.getPointerType(astContext.VoidPtrTy);
assert(!cir::MissingFeatures::appleKext());
CIRGenCallee callee =
CIRGenCallee::forDirect(cgm.getAddrOfCXXStructor(gd), gd);

cgf.emitCXXDestructorCall(gd, callee, thisAddr.getPointer(), thisTy, vtt,
vttTy, nullptr);
}

CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
switch (cgm.getASTContext().getCXXABIKind()) {
case TargetCXXABI::GenericItanium:
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CIR/CodeGen/destructors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ out_of_line_destructor::~out_of_line_destructor() {
// OGCG: ret void

// CIR: cir.func dso_local @_ZN22out_of_line_destructorD1Ev(%{{.+}}: !cir.ptr<!rec_out_of_line_destructor>
// CIR: cir.call @_Z13some_functionv() nothrow : () -> ()
// CIR: cir.call @_ZN22out_of_line_destructorD2Ev(%{{.*}}) nothrow : (!cir.ptr<!rec_out_of_line_destructor>)
// CIR: cir.return

// LLVM: define dso_local void @_ZN22out_of_line_destructorD1Ev(ptr %{{.+}})
// LLVM: call void @_Z13some_functionv()
// LLVM: call void @_ZN22out_of_line_destructorD2Ev
// LLVM: ret void

// OGCG: define dso_local void @_ZN22out_of_line_destructorD1Ev(ptr {{.*}}%{{.+}})
Expand Down