Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

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?

Copy link
Contributor Author

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.

return getBasePointer();
}

mlir::Type getType() const {
assert(mlir::cast<cir::PointerType>(
pointerAndKnownNonNull.getPointer().getType())
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
33 changes: 32 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
43 changes: 33 additions & 10 deletions clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
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
/*ImplicitParam=*/nullptr,
/*ImplicitParamTy=*/QualType(), ce);
/*implicitParam=*/nullptr,
/*implicitParamTy=*/QualType(), ce);

}
return RValue::get(nullptr);
}

Expand Down
8 changes: 7 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
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
emitCXXDestructorCall(dtor, Dtor_Complete, /*ForVirtualBase=*/false,
/*Delegating=*/false, loadCXXThisAddress(), thisTy);
emitCXXDestructorCall(dtor, Dtor_Complete, /*forVirtualBase=*/false,
/*delegating=*/false, loadCXXThisAddress(), thisTy);

}
return;
}

Expand Down
31 changes: 30 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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();
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
QualType thisTy;
if (callExpr)
thisTy = callExpr->getObjectType();
else
thisTy = delExpr->getDestroyedType();
QualType thisTy = callExpr ? callExpr->getObjectType() : delExpr->getDestroyedType();


cgf.emitCXXDestructorCall(globalDecl, callee, thisAddr.emitRawPointer(),
thisTy, nullptr, QualType(), nullptr);
return nullptr;
}

void CIRGenItaniumCXXABI::emitVirtualInheritanceTables(
const CXXRecordDecl *rd) {
CIRGenVTables &vtables = cgm.getVTables();
Expand Down
6 changes: 2 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
10 changes: 3 additions & 7 deletions clang/lib/CIR/CodeGen/CIRGenVTables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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());
Expand Down
129 changes: 129 additions & 0 deletions clang/test/CIR/CodeGen/virtual-destructor-calls.cpp
Original file line number Diff line number Diff line change
@@ -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*);
}