Support for destroying operator delete, per C++2a proposal P0722.
This feature is not (yet) approved by the C++ committee, so this is liable to
be reverted or significantly modified based on committee feedback.
No functionality change intended for existing code (a new type must be defined
in namespace std to take advantage of this feature).
llvm-svn: 315662
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index e86c8dc..60a9936 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1413,10 +1413,11 @@
// possible to delegate the destructor body to the complete
// destructor. Do so.
if (DtorType == Dtor_Deleting) {
+ RunCleanupsScope DtorEpilogue(*this);
EnterDtorCleanups(Dtor, Dtor_Deleting);
- EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
- /*Delegating=*/false, LoadCXXThisAddress());
- PopCleanupBlock();
+ if (HaveInsertPoint())
+ EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
+ /*Delegating=*/false, LoadCXXThisAddress());
return;
}
@@ -1512,6 +1513,13 @@
}
namespace {
+ llvm::Value *LoadThisForDtorDelete(CodeGenFunction &CGF,
+ const CXXDestructorDecl *DD) {
+ if (Expr *ThisArg = DD->getOperatorDeleteThisArg())
+ return CGF.EmitScalarExpr(DD->getOperatorDeleteThisArg());
+ return CGF.LoadCXXThis();
+ }
+
/// Call the operator delete associated with the current destructor.
struct CallDtorDelete final : EHScopeStack::Cleanup {
CallDtorDelete() {}
@@ -1519,11 +1527,38 @@
void Emit(CodeGenFunction &CGF, Flags flags) override {
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
const CXXRecordDecl *ClassDecl = Dtor->getParent();
- CGF.EmitDeleteCall(Dtor->getOperatorDelete(), CGF.LoadCXXThis(),
+ CGF.EmitDeleteCall(Dtor->getOperatorDelete(),
+ LoadThisForDtorDelete(CGF, Dtor),
CGF.getContext().getTagDeclType(ClassDecl));
}
};
+ void EmitConditionalDtorDeleteCall(CodeGenFunction &CGF,
+ llvm::Value *ShouldDeleteCondition,
+ bool ReturnAfterDelete) {
+ llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
+ llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
+ llvm::Value *ShouldCallDelete
+ = CGF.Builder.CreateIsNull(ShouldDeleteCondition);
+ CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
+
+ CGF.EmitBlock(callDeleteBB);
+ const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
+ const CXXRecordDecl *ClassDecl = Dtor->getParent();
+ CGF.EmitDeleteCall(Dtor->getOperatorDelete(),
+ LoadThisForDtorDelete(CGF, Dtor),
+ CGF.getContext().getTagDeclType(ClassDecl));
+ assert(Dtor->getOperatorDelete()->isDestroyingOperatorDelete() ==
+ ReturnAfterDelete &&
+ "unexpected value for ReturnAfterDelete");
+ if (ReturnAfterDelete)
+ CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
+ else
+ CGF.Builder.CreateBr(continueBB);
+
+ CGF.EmitBlock(continueBB);
+ }
+
struct CallDtorDeleteConditional final : EHScopeStack::Cleanup {
llvm::Value *ShouldDeleteCondition;
@@ -1534,20 +1569,8 @@
}
void Emit(CodeGenFunction &CGF, Flags flags) override {
- llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
- llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
- llvm::Value *ShouldCallDelete
- = CGF.Builder.CreateIsNull(ShouldDeleteCondition);
- CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
-
- CGF.EmitBlock(callDeleteBB);
- const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
- const CXXRecordDecl *ClassDecl = Dtor->getParent();
- CGF.EmitDeleteCall(Dtor->getOperatorDelete(), CGF.LoadCXXThis(),
- CGF.getContext().getTagDeclType(ClassDecl));
- CGF.Builder.CreateBr(continueBB);
-
- CGF.EmitBlock(continueBB);
+ EmitConditionalDtorDeleteCall(CGF, ShouldDeleteCondition,
+ /*ReturnAfterDelete*/false);
}
};
@@ -1706,6 +1729,9 @@
/// \brief 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 CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD,
CXXDtorType DtorType) {
assert((!DD->isTrivial() || DD->hasAttr<DLLExportAttr>()) &&
@@ -1718,11 +1744,23 @@
"operator delete missing - EnterDtorCleanups");
if (CXXStructorImplicitParamValue) {
// If there is an implicit param to the deleting dtor, it's a boolean
- // telling whether we should call delete at the end of the dtor.
- EHStack.pushCleanup<CallDtorDeleteConditional>(
- NormalAndEHCleanup, CXXStructorImplicitParamValue);
+ // telling whether this is a deleting destructor.
+ if (DD->getOperatorDelete()->isDestroyingOperatorDelete())
+ EmitConditionalDtorDeleteCall(*this, CXXStructorImplicitParamValue,
+ /*ReturnAfterDelete*/true);
+ else
+ EHStack.pushCleanup<CallDtorDeleteConditional>(
+ NormalAndEHCleanup, CXXStructorImplicitParamValue);
} else {
- EHStack.pushCleanup<CallDtorDelete>(NormalAndEHCleanup);
+ if (DD->getOperatorDelete()->isDestroyingOperatorDelete()) {
+ const CXXRecordDecl *ClassDecl = DD->getParent();
+ EmitDeleteCall(DD->getOperatorDelete(),
+ LoadThisForDtorDelete(*this, DD),
+ getContext().getTagDeclType(ClassDecl));
+ EmitBranchThroughCleanup(ReturnBlock);
+ } else {
+ EHStack.pushCleanup<CallDtorDelete>(NormalAndEHCleanup);
+ }
}
return;
}
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 8ee236d..3eb4bd6 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1311,29 +1311,44 @@
llvm_unreachable("predeclared global operator new/delete is missing");
}
-static std::pair<bool, bool>
-shouldPassSizeAndAlignToUsualDelete(const FunctionProtoType *FPT) {
+namespace {
+/// The parameters to pass to a usual operator delete.
+struct UsualDeleteParams {
+ bool DestroyingDelete = false;
+ bool Size = false;
+ bool Alignment = false;
+};
+}
+
+static UsualDeleteParams getUsualDeleteParams(const FunctionDecl *FD) {
+ UsualDeleteParams Params;
+
+ const FunctionProtoType *FPT = FD->getType()->castAs<FunctionProtoType>();
auto AI = FPT->param_type_begin(), AE = FPT->param_type_end();
// The first argument is always a void*.
++AI;
- // Figure out what other parameters we should be implicitly passing.
- bool PassSize = false;
- bool PassAlignment = false;
+ // The next parameter may be a std::destroying_delete_t.
+ if (FD->isDestroyingOperatorDelete()) {
+ Params.DestroyingDelete = true;
+ assert(AI != AE);
+ ++AI;
+ }
+ // Figure out what other parameters we should be implicitly passing.
if (AI != AE && (*AI)->isIntegerType()) {
- PassSize = true;
+ Params.Size = true;
++AI;
}
if (AI != AE && (*AI)->isAlignValT()) {
- PassAlignment = true;
+ Params.Alignment = true;
++AI;
}
assert(AI == AE && "unexpected usual deallocation function parameter");
- return {PassSize, PassAlignment};
+ return Params;
}
namespace {
@@ -1386,25 +1401,27 @@
OperatorDelete->getType()->getAs<FunctionProtoType>();
CallArgList DeleteArgs;
- // The first argument is always a void*.
+ // The first argument is always a void* (or C* for a destroying operator
+ // delete for class type C).
DeleteArgs.add(Traits::get(CGF, Ptr), FPT->getParamType(0));
// Figure out what other parameters we should be implicitly passing.
- bool PassSize = false;
- bool PassAlignment = false;
+ UsualDeleteParams Params;
if (NumPlacementArgs) {
// A placement deallocation function is implicitly passed an alignment
// if the placement allocation function was, but is never passed a size.
- PassAlignment = PassAlignmentToPlacementDelete;
+ Params.Alignment = PassAlignmentToPlacementDelete;
} else {
// For a non-placement new-expression, 'operator delete' can take a
// size and/or an alignment if it has the right parameters.
- std::tie(PassSize, PassAlignment) =
- shouldPassSizeAndAlignToUsualDelete(FPT);
+ Params = getUsualDeleteParams(OperatorDelete);
}
+ assert(!Params.DestroyingDelete &&
+ "should not call destroying delete in a new-expression");
+
// The second argument can be a std::size_t (for non-placement delete).
- if (PassSize)
+ if (Params.Size)
DeleteArgs.add(Traits::get(CGF, AllocSize),
CGF.getContext().getSizeType());
@@ -1412,7 +1429,7 @@
// is an enum whose underlying type is std::size_t.
// FIXME: Use the right type as the parameter type. Note that in a call
// to operator delete(size_t, ...), we may not have it available.
- if (PassAlignment)
+ if (Params.Alignment)
DeleteArgs.add(RValue::get(llvm::ConstantInt::get(
CGF.SizeTy, AllocAlign.getQuantity())),
CGF.getContext().getSizeType());
@@ -1715,9 +1732,7 @@
CallArgList DeleteArgs;
- std::pair<bool, bool> PassSizeAndAlign =
- shouldPassSizeAndAlignToUsualDelete(DeleteFTy);
-
+ auto Params = getUsualDeleteParams(DeleteFD);
auto ParamTypeIt = DeleteFTy->param_type_begin();
// Pass the pointer itself.
@@ -1725,8 +1740,16 @@
llvm::Value *DeletePtr = Builder.CreateBitCast(Ptr, ConvertType(ArgTy));
DeleteArgs.add(RValue::get(DeletePtr), ArgTy);
+ // Pass the std::destroying_delete tag if present.
+ if (Params.DestroyingDelete) {
+ QualType DDTag = *ParamTypeIt++;
+ // Just pass an 'undef'. We expect the tag type to be an empty struct.
+ auto *V = llvm::UndefValue::get(getTypes().ConvertType(DDTag));
+ DeleteArgs.add(RValue::get(V), DDTag);
+ }
+
// Pass the size if the delete function has a size_t parameter.
- if (PassSizeAndAlign.first) {
+ if (Params.Size) {
QualType SizeType = *ParamTypeIt++;
CharUnits DeleteTypeSize = getContext().getTypeSizeInChars(DeleteTy);
llvm::Value *Size = llvm::ConstantInt::get(ConvertType(SizeType),
@@ -1745,7 +1768,7 @@
}
// Pass the alignment if the delete function has an align_val_t parameter.
- if (PassSizeAndAlign.second) {
+ if (Params.Alignment) {
QualType AlignValType = *ParamTypeIt++;
CharUnits DeleteTypeAlign = getContext().toCharUnitsFromBits(
getContext().getTypeAlignIfKnown(DeleteTy));
@@ -1787,6 +1810,21 @@
OperatorDelete, ElementType);
}
+/// Emit the code for deleting a single object with a destroying operator
+/// delete. If the element type has a non-virtual destructor, Ptr has already
+/// been converted to the type of the parameter of 'operator delete'. Otherwise
+/// Ptr points to an object of the static type.
+static void EmitDestroyingObjectDelete(CodeGenFunction &CGF,
+ const CXXDeleteExpr *DE, Address Ptr,
+ QualType ElementType) {
+ auto *Dtor = ElementType->getAsCXXRecordDecl()->getDestructor();
+ if (Dtor && Dtor->isVirtual())
+ CGF.CGM.getCXXABI().emitVirtualObjectDelete(CGF, DE, Ptr, ElementType,
+ Dtor);
+ else
+ CGF.EmitDeleteCall(DE->getOperatorDelete(), Ptr.getPointer(), ElementType);
+}
+
/// Emit the code for deleting a single object.
static void EmitObjectDelete(CodeGenFunction &CGF,
const CXXDeleteExpr *DE,
@@ -1801,6 +1839,9 @@
DE->getExprLoc(), Ptr.getPointer(),
ElementType);
+ const FunctionDecl *OperatorDelete = DE->getOperatorDelete();
+ assert(!OperatorDelete->isDestroyingOperatorDelete());
+
// Find the destructor for the type, if applicable. If the
// destructor is virtual, we'll just emit the vcall and return.
const CXXDestructorDecl *Dtor = nullptr;
@@ -1820,7 +1861,6 @@
// Make sure that we call delete even if the dtor throws.
// This doesn't have to a conditional cleanup because we're going
// to pop it off in a second.
- const FunctionDecl *OperatorDelete = DE->getOperatorDelete();
CGF.EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup,
Ptr.getPointer(),
OperatorDelete, ElementType);
@@ -1932,10 +1972,19 @@
Builder.CreateCondBr(IsNull, DeleteEnd, DeleteNotNull);
EmitBlock(DeleteNotNull);
+ QualType DeleteTy = E->getDestroyedType();
+
+ // A destroying operator delete overrides the entire operation of the
+ // delete expression.
+ if (E->getOperatorDelete()->isDestroyingOperatorDelete()) {
+ EmitDestroyingObjectDelete(*this, E, Ptr, DeleteTy);
+ EmitBlock(DeleteEnd);
+ return;
+ }
+
// We might be deleting a pointer to array. If so, GEP down to the
// first non-array element.
// (this assumes that A(*)[3][7] is converted to [3 x [7 x %A]]*)
- QualType DeleteTy = Arg->getType()->getAs<PointerType>()->getPointeeType();
if (DeleteTy->isConstantArrayType()) {
llvm::Value *Zero = Builder.getInt32(0);
SmallVector<llvm::Value*,8> GEP;
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 6e55a67..173bc4d 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -3664,6 +3664,18 @@
!CGM.TryEmitBaseDestructorAsAlias(DD))
return;
+ // FIXME: The deleting destructor is equivalent to the selected operator
+ // delete if:
+ // * either the delete is a destroying operator delete or the destructor
+ // would be trivial if it weren't virtual,
+ // * the conversion from the 'this' parameter to the first parameter of the
+ // destructor is equivalent to a bitcast,
+ // * the destructor does not have an implicit "this" return, and
+ // * the operator delete has the same calling convention and IR function type
+ // as the destructor.
+ // In such cases we should try to emit the deleting dtor as an alias to the
+ // selected 'operator delete'.
+
llvm::Function *Fn = CGM.codegenCXXStructor(MD, Type);
if (CGType == StructorCodegen::COMDAT) {