Skip to content
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
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ class CIRGenCXXABI {
/// Loads the incoming C++ this pointer as it was passed by the caller.
mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf);

/// Get the implicit (second) parameter that comes after the "this" pointer,
/// or nullptr if there is isn't one.
virtual mlir::Value getCXXDestructorImplicitParam(CIRGenFunction &cgf,
const CXXDestructorDecl *dd,
CXXDtorType type,
bool forVirtualBase,
bool delegating) = 0;

/// Emit constructor variants required by this ABI.
virtual void emitCXXConstructors(const clang::CXXConstructorDecl *d) = 0;

Expand Down
45 changes: 41 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,30 @@ static bool isInitializerOfDynamicClass(const CXXCtorInitializer *baseInit) {
}

namespace {
/// Call the destructor for a direct base class.
struct CallBaseDtor final : EHScopeStack::Cleanup {
const CXXRecordDecl *baseClass;
bool baseIsVirtual;
CallBaseDtor(const CXXRecordDecl *base, bool baseIsVirtual)
: baseClass(base), baseIsVirtual(baseIsVirtual) {}

void emit(CIRGenFunction &cgf) override {
const CXXRecordDecl *derivedClass =
cast<CXXMethodDecl>(cgf.curFuncDecl)->getParent();

const CXXDestructorDecl *d = baseClass->getDestructor();
// We are already inside a destructor, so presumably the object being
// destroyed should have the expected type.
QualType thisTy = d->getFunctionObjectParameterType();
assert(cgf.currSrcLoc && "expected source location");
Address addr = cgf.getAddressOfDirectBaseInCompleteClass(
*cgf.currSrcLoc, cgf.loadCXXThisAddress(), derivedClass, baseClass,
baseIsVirtual);
cgf.emitCXXDestructorCall(d, Dtor_Base, baseIsVirtual,
/*delegating=*/false, addr, thisTy);
}
};

/// A visitor which checks whether an initializer uses 'this' in a
/// way which requires the vtable to be properly set.
struct DynamicThisUseChecker
Expand Down Expand Up @@ -922,8 +946,21 @@ void CIRGenFunction::enterDtorCleanups(const CXXDestructorDecl *dd,
if (dtorType == Dtor_Complete) {
assert(!cir::MissingFeatures::sanitizers());

if (classDecl->getNumVBases())
cgm.errorNYI(dd->getSourceRange(), "virtual base destructor cleanups");
// We push them in the forward order so that they'll be popped in
// the reverse order.
for (const CXXBaseSpecifier &base : classDecl->vbases()) {
auto *baseClassDecl = base.getType()->castAsCXXRecordDecl();

if (baseClassDecl->hasTrivialDestructor()) {
// Under SanitizeMemoryUseAfterDtor, poison the trivial base class
// memory. For non-trival base classes the same is done in the class
// destructor.
assert(!cir::MissingFeatures::sanitizers());
} else {
ehStack.pushCleanup<CallBaseDtor>(NormalAndEHCleanup, baseClassDecl,
/*baseIsVirtual=*/true);
}
}

return;
}
Expand All @@ -942,8 +979,8 @@ void CIRGenFunction::enterDtorCleanups(const CXXDestructorDecl *dd,
if (baseClassDecl->hasTrivialDestructor())
assert(!cir::MissingFeatures::sanitizers());
else
cgm.errorNYI(dd->getSourceRange(),
"non-trivial base destructor cleanups");
ehStack.pushCleanup<CallBaseDtor>(NormalAndEHCleanup, baseClassDecl,
/*baseIsVirtual=*/false);
}

assert(!cir::MissingFeatures::sanitizers());
Expand Down
20 changes: 14 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {

void addImplicitStructorParams(CIRGenFunction &cgf, QualType &resTy,
FunctionArgList &params) override;

mlir::Value getCXXDestructorImplicitParam(CIRGenFunction &cgf,
const CXXDestructorDecl *dd,
CXXDtorType type,
bool forVirtualBase,
bool delegating) override;
void emitCXXConstructors(const clang::CXXConstructorDecl *d) override;
void emitCXXDestructors(const clang::CXXDestructorDecl *d) override;
void emitCXXStructor(clang::GlobalDecl gd) override;
Expand Down Expand Up @@ -1504,11 +1508,8 @@ 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;
mlir::Value vtt =
getCXXDestructorImplicitParam(cgf, dd, type, forVirtualBase, delegating);
ASTContext &astContext = cgm.getASTContext();
QualType vttTy = astContext.getPointerType(astContext.VoidPtrTy);
assert(!cir::MissingFeatures::appleKext());
Expand Down Expand Up @@ -1540,6 +1541,13 @@ void CIRGenItaniumCXXABI::registerGlobalDtor(const VarDecl *vd,
// prepare. Nothing to be done for CIR here.
}

mlir::Value CIRGenItaniumCXXABI::getCXXDestructorImplicitParam(
CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type,
bool forVirtualBase, bool delegating) {
GlobalDecl gd(dd, type);
return cgf.getVTTParameter(gd, forVirtualBase, delegating);
}

// The idea here is creating a separate block for the throw with an
// `UnreachableOp` as the terminator. So, we branch from the current block
// to the throw block and create a block for the remaining operations.
Expand Down
81 changes: 81 additions & 0 deletions clang/test/CIR/CodeGen/dtors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,84 @@ void test_nested_dtor() {
// OGCG: define {{.*}} void @_ZN1DD2Ev
// OGCG: %[[C:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i64 4
// OGCG: call void @_ZN1CD1Ev(ptr {{.*}} %[[C]])

struct E {
~E();
};

struct F : public E {
int n;
~F() {}
};

// CIR: cir.func {{.*}} @_ZN1FD2Ev
// CIR: %[[BASE_E:.*]] = cir.base_class_addr %{{.*}} : !cir.ptr<!rec_F> nonnull [0] -> !cir.ptr<!rec_E>
// CIR: cir.call @_ZN1ED2Ev(%[[BASE_E]]) nothrow : (!cir.ptr<!rec_E>) -> ()

// Because E is at offset 0 in F, there is no getelementptr needed.

// LLVM: define {{.*}} void @_ZN1FD2Ev
// LLVM: call void @_ZN1ED2Ev(ptr %{{.*}})

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

void test_base_dtor_call() {
F f;
}

// CIR: cir.func {{.*}} @_Z19test_base_dtor_callv()
// cir.call @_ZN1FD2Ev(%{{.*}}) nothrow : (!cir.ptr<!rec_F>) -> ()

// LLVM: define {{.*}} void @_Z19test_base_dtor_callv()
// LLVM: call void @_ZN1FD2Ev(ptr %{{.*}})

// OGCG: define {{.*}} void @_Z19test_base_dtor_callv()
// OGCG: call void @_ZN1FD2Ev(ptr {{.*}} %{{.*}})

// OGCG: define {{.*}} void @_ZN1FD2Ev
// OGCG: call void @_ZN1ED2Ev(ptr {{.*}} %{{.*}})

struct VirtualBase {
~VirtualBase();
};

struct Derived : virtual VirtualBase {
~Derived() {}
};

void test_base_dtor_call_virtual_base() {
Derived d;
}

// Derived D2 (base) destructor -- does not call VirtualBase destructor

// CIR: cir.func {{.*}} @_ZN7DerivedD2Ev
// CIR-NOT: cir.call{{.*}} @_ZN11VirtualBaseD2Ev
// CIR: cir.return

// LLVM: define {{.*}} void @_ZN7DerivedD2Ev
// LLVM-NOT: call{{.*}} @_ZN11VirtualBaseD2Ev
// LLVM: ret

// Derived D1 (complete) destructor -- does call VirtualBase destructor

// CIR: cir.func {{.*}} @_ZN7DerivedD1Ev
// CIR: %[[THIS:.*]] = cir.load %{{.*}}
// CIR: %[[VTT:.*]] = cir.vtt.address_point @_ZTT7Derived, offset = 0 -> !cir.ptr<!cir.ptr<!void>>
// CIR: cir.call @_ZN7DerivedD2Ev(%[[THIS]], %[[VTT]])
// CIR: %[[VIRTUAL_BASE:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_Derived> nonnull [0] -> !cir.ptr<!rec_VirtualBase>
// CIR: cir.call @_ZN11VirtualBaseD2Ev(%[[VIRTUAL_BASE]])

// LLVM: define {{.*}} void @_ZN7DerivedD1Ev
// LLVM: call void @_ZN7DerivedD2Ev(ptr %{{.*}}, ptr @_ZTT7Derived)
// LLVM: call void @_ZN11VirtualBaseD2Ev(ptr %{{.*}})

// OGCG emits these destructors in reverse order

// OGCG: define {{.*}} void @_ZN7DerivedD1Ev
// OGCG: call void @_ZN7DerivedD2Ev(ptr {{.*}} %{{.*}}, ptr {{.*}} @_ZTT7Derived)
// OGCG: call void @_ZN11VirtualBaseD2Ev(ptr {{.*}} %{{.*}})

// OGCG: define {{.*}} void @_ZN7DerivedD2Ev
// OGCG-NOT: call{{.*}} @_ZN11VirtualBaseD2Ev
// OGCG: ret