-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[CIR] Add Minimal Destructor Definition Support #144719
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -463,7 +463,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, | |
startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin()); | ||
|
||
if (isa<CXXDestructorDecl>(funcDecl)) | ||
getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition"); | ||
emitDestructorBody(args); | ||
else if (isa<CXXConstructorDecl>(funcDecl)) | ||
emitConstructorBody(args); | ||
else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && | ||
|
@@ -538,6 +538,104 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { | |
} | ||
} | ||
|
||
/// Emits the body of the current destructor. | ||
void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { | ||
const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl()); | ||
CXXDtorType dtorType = curGD.getDtorType(); | ||
|
||
// For an abstract class, non-base destructors are never used (and can't | ||
// be emitted in general, because vbase dtors may not have been validated | ||
// by Sema), but the Itanium ABI doesn't make them optional and Clang may | ||
// in fact emit references to them from other compilations, so emit them | ||
// as functions containing a trap instruction. | ||
if (dtorType != Dtor_Base && dtor->getParent()->isAbstract()) { | ||
SourceLocation loc = | ||
dtor->hasBody() ? dtor->getBody()->getBeginLoc() : dtor->getLocation(); | ||
builder.create<cir::TrapOp>(getLoc(loc)); | ||
// The corresponding clang/CodeGen logic clears the insertion point here, | ||
// but MLIR's builder requires a valid insertion point, so we create a dummy | ||
// block (since the trap is a block terminator). | ||
builder.createBlock(builder.getBlock()->getParent()); | ||
return; | ||
} | ||
|
||
Stmt *body = dtor->getBody(); | ||
if (body) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's useful to put this assert inside an |
||
assert(!cir::MissingFeatures::incrementProfileCounter()); | ||
|
||
// The call to operator delete in a deleting destructor happens | ||
// outside of the function-try-block, which means it's always | ||
// possible to delegate the destructor body to the complete | ||
// destructor. Do so. | ||
if (dtorType == Dtor_Deleting) { | ||
cgm.errorNYI(dtor->getSourceRange(), "deleting destructor"); | ||
return; | ||
} | ||
|
||
// If the body is a function-try-block, enter the try before | ||
// anything else. | ||
const bool isTryBody = isa_and_nonnull<CXXTryStmt>(body); | ||
if (isTryBody) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. curly braces not needed here |
||
cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor"); | ||
} | ||
|
||
assert(!cir::MissingFeatures::sanitizers()); | ||
assert(!cir::MissingFeatures::dtorCleanups()); | ||
|
||
// If this is the complete variant, just invoke the base variant; | ||
// the epilogue will destruct the virtual bases. But we can't do | ||
// this optimization if the body is a function-try-block, because | ||
// we'd introduce *two* handler blocks. In the Microsoft ABI, we | ||
// always delegate because we might not have a definition in this TU. | ||
switch (dtorType) { | ||
case Dtor_Comdat: | ||
llvm_unreachable("not expecting a COMDAT"); | ||
case Dtor_Deleting: | ||
llvm_unreachable("already handled deleting case"); | ||
|
||
case Dtor_Complete: | ||
assert((body || getTarget().getCXXABI().isMicrosoft()) && | ||
"can't emit a dtor without a body for non-Microsoft ABIs"); | ||
|
||
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()); | ||
|
||
// Fallthrough: act like we're in the base variant. | ||
[[fallthrough]]; | ||
|
||
case Dtor_Base: | ||
assert(body); | ||
|
||
assert(!cir::MissingFeatures::dtorCleanups()); | ||
assert(!cir::MissingFeatures::vtableInitialization()); | ||
|
||
if (isTryBody) { | ||
cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor"); | ||
} else if (body) { | ||
(void)emitStmt(body, /*useCurrentScope=*/true); | ||
} else { | ||
assert(dtor->isImplicit() && "bodyless dtor not implicit"); | ||
// nothing to do besides what's in the epilogue | ||
} | ||
// -fapple-kext must inline any call to this dtor into | ||
// the caller's body. | ||
assert(!cir::MissingFeatures::appleKext()); | ||
|
||
break; | ||
} | ||
|
||
assert(!cir::MissingFeatures::dtorCleanups()); | ||
|
||
// Exit the try if applicable. | ||
if (isTryBody) | ||
cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor"); | ||
} | ||
|
||
/// Given a value of type T* that may not be to a complete object, construct | ||
/// an l-vlaue withi the natural pointee alignment of T. | ||
LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir -mno-constructor-aliases %s -o %t.cir | ||
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR | ||
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -mno-constructor-aliases -o %t-cir.ll | ||
// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM | ||
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -mno-constructor-aliases -o %t.ll | ||
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG | ||
|
||
void some_function() noexcept; | ||
|
||
struct out_of_line_destructor { | ||
int prevent_tail_padding_reuse; | ||
~out_of_line_destructor(); | ||
}; | ||
|
||
out_of_line_destructor::~out_of_line_destructor() { | ||
some_function(); | ||
} | ||
|
||
// CIR: !rec_out_of_line_destructor = !cir.record<struct "out_of_line_destructor" {!s32i}> | ||
|
||
// CIR: cir.func @_ZN22out_of_line_destructorD2Ev(%{{.+}}: !cir.ptr<!rec_out_of_line_destructor> | ||
// CIR: cir.call @_Z13some_functionv() : () -> () | ||
// CIR: cir.return | ||
|
||
// LLVM: define void @_ZN22out_of_line_destructorD2Ev(ptr %{{.+}}) | ||
// LLVM: call void @_Z13some_functionv() | ||
// LLVM: ret void | ||
|
||
// OGCG: define dso_local void @_ZN22out_of_line_destructorD2Ev(ptr {{.*}}%{{.+}}) | ||
// OGCG: call void @_Z13some_functionv() | ||
// OGCG: ret void | ||
|
||
// CIR: cir.func @_ZN22out_of_line_destructorD1Ev(%{{.+}}: !cir.ptr<!rec_out_of_line_destructor> | ||
// CIR: cir.call @_Z13some_functionv() : () -> () | ||
// CIR: cir.return | ||
|
||
// LLVM: define void @_ZN22out_of_line_destructorD1Ev(ptr %{{.+}}) | ||
// LLVM: call void @_Z13some_functionv() | ||
// LLVM: ret void | ||
|
||
// OGCG: define dso_local void @_ZN22out_of_line_destructorD1Ev(ptr {{.*}}%{{.+}}) | ||
// OGCG: call void @_ZN22out_of_line_destructorD2Ev | ||
// OGCG: ret void | ||
|
||
struct inline_destructor { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're not emitting code for this case, right? You should probably delete it until we do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a CHECK-NOT or couple CHECK-NEXT with a comment? |
||
int prevent_tail_padding_reuse; | ||
~inline_destructor() noexcept(false) { | ||
some_function(); | ||
} | ||
}; |
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.
It's probably not possible to write a test that hits this code yet. If that's so, can you make it NYI until we can?