Skip to content
Open
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
103 changes: 103 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,109 @@ void CIRGenFunction::destroyCXXObject(CIRGenFunction &cgf, Address addr,
/*delegating=*/false, addr, type);
}

namespace {
class DestroyField final : public EHScopeStack::Cleanup {
const FieldDecl *field;
CIRGenFunction::Destroyer *destroyer;

public:
DestroyField(const FieldDecl *field, CIRGenFunction::Destroyer *destroyer)
: field(field), destroyer(destroyer) {}

void emit(CIRGenFunction &cgf) override {
// Find the address of the field.
Address thisValue = cgf.loadCXXThisAddress();
CanQualType recordTy =
cgf.getContext().getCanonicalTagType(field->getParent());
LValue thisLV = cgf.makeAddrLValue(thisValue, recordTy);
LValue lv = cgf.emitLValueForField(thisLV, field);
assert(lv.isSimple());

assert(!cir::MissingFeatures::ehCleanupFlags());
cgf.emitDestroy(lv.getAddress(), field->getType(), destroyer);
}

// This is a placeholder until EHCleanupScope is implemented.
size_t getSize() const override {
assert(!cir::MissingFeatures::ehCleanupScope());
return sizeof(DestroyField);
}
};
} // namespace

/// Emit all code that comes at the end of class's destructor. This is to call
/// destructors on members and base classes in reverse order of their
/// construction.
///
/// For a deleting destructor, this also handles the case where a destroying
/// operator delete completely overrides the definition.
void CIRGenFunction::enterDtorCleanups(const CXXDestructorDecl *dd,
CXXDtorType dtorType) {
assert((!dd->isTrivial() || dd->hasAttr<DLLExportAttr>()) &&
"Should not emit dtor epilogue for non-exported trivial dtor!");

// 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");
return;
}

const CXXRecordDecl *classDecl = dd->getParent();

// Unions have no bases and do not call field destructors.
if (classDecl->isUnion())
return;

// The complete-destructor phase just destructs all the virtual bases.
if (dtorType == Dtor_Complete) {
assert(!cir::MissingFeatures::sanitizers());

if (classDecl->getNumVBases())
cgm.errorNYI(dd->getSourceRange(), "virtual base destructor cleanups");

return;
}

assert(dtorType == Dtor_Base);
assert(!cir::MissingFeatures::sanitizers());

// Destroy non-virtual bases.
for (const CXXBaseSpecifier &base : classDecl->bases()) {
// Ignore virtual bases.
if (base.isVirtual())
continue;

CXXRecordDecl *baseClassDecl = base.getType()->getAsCXXRecordDecl();

if (baseClassDecl->hasTrivialDestructor())
assert(!cir::MissingFeatures::sanitizers());
else
cgm.errorNYI(dd->getSourceRange(),
"non-trivial base destructor cleanups");
}

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

// Destroy direct fields.
for (const FieldDecl *field : classDecl->fields()) {
QualType type = field->getType();
QualType::DestructionKind dtorKind = type.isDestructedType();
if (!dtorKind)
continue;

// Anonymous union members do not have their destructors called.
const RecordType *rt = type->getAsUnionType();
if (rt && rt->getOriginalDecl()->isAnonymousStructOrUnion())
continue;

CleanupKind cleanupKind = getCleanupKind(dtorKind);
assert(!cir::MissingFeatures::ehCleanupFlags());
ehStack.pushCleanup<DestroyField>(cleanupKind, field,
getDestroyer(dtorKind));
}
}

void CIRGenFunction::emitDelegatingCXXConstructorCall(
const CXXConstructorDecl *ctor, const FunctionArgList &args) {
assert(ctor->isDelegatingConstructor());
Expand Down
14 changes: 10 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,9 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor");

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

// Enter the epilogue cleanups.
RunCleanupsScope dtorEpilogue(*this);

// If this is the complete variant, just invoke the base variant;
// the epilogue will destruct the virtual bases. But we can't do
Expand All @@ -708,7 +710,8 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
assert((body || getTarget().getCXXABI().isMicrosoft()) &&
"can't emit a dtor without a body for non-Microsoft ABIs");

assert(!cir::MissingFeatures::dtorCleanups());
// Enter the cleanup scopes for virtual bases.
enterDtorCleanups(dtor, Dtor_Complete);

if (!isTryBody) {
QualType thisTy = dtor->getFunctionObjectParameterType();
Expand All @@ -723,7 +726,9 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
case Dtor_Base:
assert(body);

assert(!cir::MissingFeatures::dtorCleanups());
// Enter the cleanup scopes for fields and non-virtual bases.
enterDtorCleanups(dtor, Dtor_Base);

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

if (isTryBody) {
Expand All @@ -741,7 +746,8 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) {
break;
}

assert(!cir::MissingFeatures::dtorCleanups());
// Jump out through the epilogue cleanups.
dtorEpilogue.forceCleanup();

// Exit the try if applicable.
if (isTryBody)
Expand Down
27 changes: 27 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,33 @@ class CIRGenFunction : public CIRGenTypeCache {
cir::GlobalOp gv,
cir::GetGlobalOp gvAddr);

/// Enter the cleanups necessary to complete the given phase of destruction
/// for a destructor. The end result should call destructors on members and
/// base classes in reverse order of their construction.
void enterDtorCleanups(const CXXDestructorDecl *dtor, CXXDtorType type);

/// Determines whether an EH cleanup is required to destroy a type
/// with the given destruction kind.
/// TODO(cir): could be shared with Clang LLVM codegen
bool needsEHCleanup(QualType::DestructionKind kind) {
switch (kind) {
case QualType::DK_none:
return false;
case QualType::DK_cxx_destructor:
case QualType::DK_objc_weak_lifetime:
case QualType::DK_nontrivial_c_struct:
return getLangOpts().Exceptions;
case QualType::DK_objc_strong_lifetime:
return getLangOpts().Exceptions &&
cgm.getCodeGenOpts().ObjCAutoRefCountExceptions;
}
llvm_unreachable("bad destruction kind");
}

CleanupKind getCleanupKind(QualType::DestructionKind kind) {
return (needsEHCleanup(kind) ? NormalAndEHCleanup : NormalCleanup);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (needsEHCleanup(kind) ? NormalAndEHCleanup : NormalCleanup);
return needsEHCleanup(kind) ? NormalAndEHCleanup : NormalCleanup;

}

/// Set the address of a local variable.
void setAddrOfLocalVar(const clang::VarDecl *vd, Address addr) {
assert(!localDeclMap.count(vd) && "Decl already exists in LocalDeclMap!");
Expand Down
37 changes: 37 additions & 0 deletions clang/test/CIR/CodeGen/dtors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,40 @@ bool test_temp_and() { return make_temp(1) && make_temp(2); }
// OGCG: br label %[[CLEANUP_DONE]]
// OGCG: [[CLEANUP_DONE]]:
// OGCG: call void @_ZN1BD2Ev(ptr {{.*}} %[[REF_TMP0]])

struct C {
~C();
};

struct D {
int n;
C c;
~D() {}
};

// CIR: cir.func {{.*}} @_ZN1DD2Ev
// CIR: %[[C:.*]] = cir.get_member %{{.*}}[1] {name = "c"}
// CIR: cir.call @_ZN1CD1Ev(%[[C]])

// LLVM: define {{.*}} void @_ZN1DD2Ev
// LLVM: %[[C:.*]] = getelementptr %struct.D, ptr %{{.*}}, i32 0, i32 1
// LLVM: call void @_ZN1CD1Ev(ptr %[[C]])

// This destructor is defined after the calling function in OGCG.

void test_nested_dtor() {
D d;
}

// CIR: cir.func{{.*}} @_Z16test_nested_dtorv()
// CIR: cir.call @_ZN1DD2Ev(%{{.*}})

// LLVM: define {{.*}} void @_Z16test_nested_dtorv()
// LLVM: call void @_ZN1DD2Ev(ptr %{{.*}})

// OGCG: define {{.*}} void @_Z16test_nested_dtorv()
// OGCG: call void @_ZN1DD2Ev(ptr {{.*}} %{{.*}})

// OGCG: define {{.*}} void @_ZN1DD2Ev
// OGCG: %[[C:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i64 4
// OGCG: call void @_ZN1CD1Ev(ptr {{.*}} %[[C]])