aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
authorMariya Podchishchaeva <mariya.podchishchaeva@intel.com>2025-03-31 10:03:39 +0200
committerGitHub <noreply@github.com>2025-03-31 10:03:39 +0200
commit842b57b77520abf202999946d3bb01b5dcabb179 (patch)
treeda66446e34129389df29c5b77d944eb4f98a72f0 /clang/lib
parent809f857d2c8edffe1dac317982b68a467710f877 (diff)
downloadllvm-842b57b77520abf202999946d3bb01b5dcabb179.zip
llvm-842b57b77520abf202999946d3bb01b5dcabb179.tar.gz
llvm-842b57b77520abf202999946d3bb01b5dcabb179.tar.bz2
Reland [MS][clang] Add support for vector deleting destructors (#133451)
Whereas it is UB in terms of the standard to delete an array of objects via pointer whose static type doesn't match its dynamic type, MSVC supports an extension allowing to do it. Aside from array deletion not working correctly in the mentioned case, currently not having this extension implemented causes clang to generate code that is not compatible with the code generated by MSVC, because clang always puts scalar deleting destructor to the vftable. This PR aims to resolve these problems. It was reverted due to link time errors in chromium with sanitizer coverage enabled, which is fixed by https://github.com/llvm/llvm-project/pull/131929 . The second commit of this PR also contains a fix for a runtime failure in chromium reported in https://github.com/llvm/llvm-project/pull/126240#issuecomment-2730216384 . Fixes https://github.com/llvm/llvm-project/issues/19772
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ItaniumMangle.cpp2
-rw-r--r--clang/lib/AST/MicrosoftMangle.cpp22
-rw-r--r--clang/lib/AST/VTableBuilder.cpp19
-rw-r--r--clang/lib/CodeGen/CGCXX.cpp37
-rw-r--r--clang/lib/CodeGen/CGCXXABI.cpp14
-rw-r--r--clang/lib/CodeGen/CGCXXABI.h7
-rw-r--r--clang/lib/CodeGen/CGClass.cpp77
-rw-r--r--clang/lib/CodeGen/CGDebugInfo.cpp3
-rw-r--r--clang/lib/CodeGen/CGExprCXX.cpp42
-rw-r--r--clang/lib/CodeGen/CGVTables.cpp3
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp48
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h6
-rw-r--r--clang/lib/CodeGen/ItaniumCXXABI.cpp6
-rw-r--r--clang/lib/CodeGen/MicrosoftCXXABI.cpp49
14 files changed, 294 insertions, 41 deletions
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 981cdb3..49a0486 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -6004,6 +6004,8 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
case Dtor_Comdat:
Out << "D5";
break;
+ case Dtor_VectorDeleting:
+ llvm_unreachable("Itanium ABI does not use vector deleting dtors");
}
}
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 15de407..7e96412 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1484,8 +1484,9 @@ void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
// <operator-name> ::= ?_G # scalar deleting destructor
case Dtor_Deleting: Out << "?_G"; return;
// <operator-name> ::= ?_E # vector deleting destructor
- // FIXME: Add a vector deleting dtor type. It goes in the vtable, so we need
- // it.
+ case Dtor_VectorDeleting:
+ Out << "?_E";
+ return;
case Dtor_Comdat:
llvm_unreachable("not expecting a COMDAT");
}
@@ -2886,9 +2887,12 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
// ::= @ # structors (they have no declared return type)
if (IsStructor) {
if (isa<CXXDestructorDecl>(D) && isStructorDecl(D)) {
- // The scalar deleting destructor takes an extra int argument which is not
- // reflected in the AST.
- if (StructorType == Dtor_Deleting) {
+ // The deleting destructors take an extra argument of type int that
+ // indicates whether the storage for the object should be deleted and
+ // whether a single object or an array of objects is being destroyed. This
+ // extra argument is not reflected in the AST.
+ if (StructorType == Dtor_Deleting ||
+ StructorType == Dtor_VectorDeleting) {
Out << (PointersAre64Bit ? "PEAXI@Z" : "PAXI@Z");
return;
}
@@ -3861,10 +3865,10 @@ void MicrosoftMangleContextImpl::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
const ThunkInfo &Thunk,
bool /*ElideOverrideInfo*/,
raw_ostream &Out) {
- // FIXME: Actually, the dtor thunk should be emitted for vector deleting
- // dtors rather than scalar deleting dtors. Just use the vector deleting dtor
- // mangling manually until we support both deleting dtor types.
- assert(Type == Dtor_Deleting);
+ // The dtor thunk should use vector deleting dtor mangling, however as an
+ // optimization we may end up emitting only scalar deleting dtor body, so just
+ // use the vector deleting dtor mangling manually.
+ assert(Type == Dtor_Deleting || Type == Dtor_VectorDeleting);
msvc_hashing_ostream MHO(Out);
MicrosoftCXXNameMangler Mangler(*this, MHO, DD, Type);
Mangler.getStream() << "??_E";
diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp
index 6c97b87..21f9d34 100644
--- a/clang/lib/AST/VTableBuilder.cpp
+++ b/clang/lib/AST/VTableBuilder.cpp
@@ -1735,8 +1735,8 @@ void ItaniumVTableBuilder::LayoutPrimaryAndSecondaryVTables(
const CXXMethodDecl *MD = I.first;
const MethodInfo &MI = I.second;
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
- MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)]
- = MI.VTableIndex - AddressPoint;
+ MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] =
+ MI.VTableIndex - AddressPoint;
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)]
= MI.VTableIndex + 1 - AddressPoint;
} else {
@@ -2657,7 +2657,11 @@ private:
MethodVFTableLocation Loc(MI.VBTableIndex, WhichVFPtr.getVBaseWithVPtr(),
WhichVFPtr.NonVirtualOffset, MI.VFTableIndex);
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
- MethodVFTableLocations[GlobalDecl(DD, Dtor_Deleting)] = Loc;
+ // In Microsoft ABI vftable always references vector deleting dtor.
+ CXXDtorType DtorTy = Context.getTargetInfo().getCXXABI().isMicrosoft()
+ ? Dtor_VectorDeleting
+ : Dtor_Deleting;
+ MethodVFTableLocations[GlobalDecl(DD, DtorTy)] = Loc;
} else {
MethodVFTableLocations[MD] = Loc;
}
@@ -3287,7 +3291,10 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) {
const CXXDestructorDecl *DD = Component.getDestructorDecl();
DD->printQualifiedName(Out);
- Out << "() [scalar deleting]";
+ if (Context.getTargetInfo().getCXXABI().isMicrosoft())
+ Out << "() [vector deleting]";
+ else
+ Out << "() [scalar deleting]";
if (DD->isPureVirtual())
Out << " [pure]";
@@ -3757,7 +3764,7 @@ void MicrosoftVTableContext::dumpMethodLocations(
PredefinedIdentKind::PrettyFunctionNoVirtual, MD);
if (isa<CXXDestructorDecl>(MD)) {
- IndicesMap[I.second] = MethodName + " [scalar deleting]";
+ IndicesMap[I.second] = MethodName + " [vector deleting]";
} else {
IndicesMap[I.second] = MethodName;
}
@@ -3873,7 +3880,7 @@ MicrosoftVTableContext::getMethodVFTableLocation(GlobalDecl GD) {
assert(hasVtableSlot(cast<CXXMethodDecl>(GD.getDecl())) &&
"Only use this method for virtual methods or dtors");
if (isa<CXXDestructorDecl>(GD.getDecl()))
- assert(GD.getDtorType() == Dtor_Deleting);
+ assert(GD.getDtorType() == Dtor_VectorDeleting);
GD = GD.getCanonicalDecl();
diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp
index 78a7b02..6f47e24 100644
--- a/clang/lib/CodeGen/CGCXX.cpp
+++ b/clang/lib/CodeGen/CGCXX.cpp
@@ -175,7 +175,6 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
// requires explicit comdat support in the IL.
if (llvm::GlobalValue::isWeakForLinker(TargetLinkage))
return true;
-
// Create the alias with no name.
auto *Alias = llvm::GlobalAlias::create(AliasValueType, 0, Linkage, "",
Aliasee, &getModule());
@@ -201,6 +200,42 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
return false;
}
+/// Emit a definition as a global alias for another definition, unconditionally.
+void CodeGenModule::EmitDefinitionAsAlias(GlobalDecl AliasDecl,
+ GlobalDecl TargetDecl) {
+
+ llvm::Type *AliasValueType = getTypes().GetFunctionType(AliasDecl);
+
+ StringRef MangledName = getMangledName(AliasDecl);
+ llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
+ if (Entry && !Entry->isDeclaration())
+ return;
+ auto *Aliasee = cast<llvm::GlobalValue>(GetAddrOfGlobal(TargetDecl));
+
+ // Determine the linkage type for the alias.
+ llvm::GlobalValue::LinkageTypes Linkage = getFunctionLinkage(AliasDecl);
+
+ // Create the alias with no name.
+ auto *Alias = llvm::GlobalAlias::create(AliasValueType, 0, Linkage, "",
+ Aliasee, &getModule());
+ // Destructors are always unnamed_addr.
+ Alias->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
+
+ if (Entry) {
+ assert(Entry->getValueType() == AliasValueType &&
+ Entry->getAddressSpace() == Alias->getAddressSpace() &&
+ "declaration exists with different type");
+ Alias->takeName(Entry);
+ Entry->replaceAllUsesWith(Alias);
+ Entry->eraseFromParent();
+ } else {
+ Alias->setName(MangledName);
+ }
+
+ // Set any additional necessary attributes for the alias.
+ SetCommonAttributes(AliasDecl, Alias);
+}
+
llvm::Function *CodeGenModule::codegenCXXStructor(GlobalDecl GD) {
const CGFunctionInfo &FnInfo = getTypes().arrangeCXXStructorDeclaration(GD);
auto *Fn = cast<llvm::Function>(
diff --git a/clang/lib/CodeGen/CGCXXABI.cpp b/clang/lib/CodeGen/CGCXXABI.cpp
index fd35f2a..9f77fbe 100644
--- a/clang/lib/CodeGen/CGCXXABI.cpp
+++ b/clang/lib/CodeGen/CGCXXABI.cpp
@@ -272,6 +272,20 @@ void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, Address ptr,
numElements = readArrayCookieImpl(CGF, allocAddr, cookieSize);
}
+void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, Address ptr,
+ QualType eltTy, llvm::Value *&numElements,
+ llvm::Value *&allocPtr, CharUnits &cookieSize) {
+ assert(eltTy.isDestructedType());
+
+ // Derive a char* in the same address space as the pointer.
+ ptr = ptr.withElementType(CGF.Int8Ty);
+
+ cookieSize = getArrayCookieSizeImpl(eltTy);
+ Address allocAddr = CGF.Builder.CreateConstInBoundsByteGEP(ptr, -cookieSize);
+ allocPtr = allocAddr.emitRawPointer(CGF);
+ numElements = readArrayCookieImpl(CGF, allocAddr, cookieSize);
+}
+
llvm::Value *CGCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
Address ptr,
CharUnits cookieSize) {
diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h
index 687ff7f..148a7ba 100644
--- a/clang/lib/CodeGen/CGCXXABI.h
+++ b/clang/lib/CodeGen/CGCXXABI.h
@@ -275,6 +275,7 @@ public:
virtual CatchTypeInfo getCatchAllTypeInfo();
virtual bool shouldTypeidBeNullChecked(QualType SrcRecordTy) = 0;
+ virtual bool hasVectorDeletingDtors() = 0;
virtual void EmitBadTypeidCall(CodeGenFunction &CGF) = 0;
virtual llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy,
Address ThisPtr,
@@ -575,6 +576,12 @@ public:
QualType ElementType, llvm::Value *&NumElements,
llvm::Value *&AllocPtr, CharUnits &CookieSize);
+ /// Reads the array cookie associated with the given pointer,
+ /// that should have one.
+ void ReadArrayCookie(CodeGenFunction &CGF, Address Ptr, QualType ElementType,
+ llvm::Value *&NumElements, llvm::Value *&AllocPtr,
+ CharUnits &CookieSize);
+
/// Return whether the given global decl needs a VTT parameter.
virtual bool NeedsVTTParameter(GlobalDecl GD);
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 98c93b5..f508930 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1432,6 +1432,70 @@ static bool CanSkipVTablePointerInitialization(CodeGenFunction &CGF,
return true;
}
+static void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
+ CodeGenFunction &CGF,
+ llvm::Value *ShouldDeleteCondition) {
+ Address ThisPtr = CGF.LoadCXXThisAddress();
+ llvm::BasicBlock *ScalarBB = CGF.createBasicBlock("dtor.scalar");
+ llvm::BasicBlock *callDeleteBB =
+ CGF.createBasicBlock("dtor.call_delete_after_array_destroy");
+ llvm::BasicBlock *VectorBB = CGF.createBasicBlock("dtor.vector");
+ auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType());
+ llvm::Value *CheckTheBitForArrayDestroy = CGF.Builder.CreateAnd(
+ ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 2));
+ llvm::Value *ShouldDestroyArray =
+ CGF.Builder.CreateIsNull(CheckTheBitForArrayDestroy);
+ CGF.Builder.CreateCondBr(ShouldDestroyArray, ScalarBB, VectorBB);
+
+ CGF.EmitBlock(VectorBB);
+
+ llvm::Value *numElements = nullptr;
+ llvm::Value *allocatedPtr = nullptr;
+ CharUnits cookieSize;
+ QualType EltTy = DD->getThisType()->getPointeeType();
+ CGF.CGM.getCXXABI().ReadArrayCookie(CGF, ThisPtr, EltTy, numElements,
+ allocatedPtr, cookieSize);
+
+ // Destroy the elements.
+ QualType::DestructionKind dtorKind = EltTy.isDestructedType();
+
+ assert(dtorKind);
+ assert(numElements && "no element count for a type with a destructor!");
+
+ CharUnits elementSize = CGF.getContext().getTypeSizeInChars(EltTy);
+ CharUnits elementAlign =
+ ThisPtr.getAlignment().alignmentOfArrayElement(elementSize);
+
+ llvm::Value *arrayBegin = ThisPtr.emitRawPointer(CGF);
+ llvm::Value *arrayEnd = CGF.Builder.CreateInBoundsGEP(
+ ThisPtr.getElementType(), arrayBegin, numElements, "delete.end");
+
+ // We already checked that the array is not 0-length before entering vector
+ // deleting dtor.
+ CGF.emitArrayDestroy(arrayBegin, arrayEnd, EltTy, elementAlign,
+ CGF.getDestroyer(dtorKind),
+ /*checkZeroLength*/ false, CGF.needsEHCleanup(dtorKind));
+
+ llvm::BasicBlock *VectorBBCont = CGF.createBasicBlock("dtor.vector.cont");
+ CGF.EmitBlock(VectorBBCont);
+
+ llvm::Value *CheckTheBitForDeleteCall = CGF.Builder.CreateAnd(
+ ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 1));
+
+ llvm::Value *ShouldCallDelete =
+ CGF.Builder.CreateIsNull(CheckTheBitForDeleteCall);
+ CGF.Builder.CreateCondBr(ShouldCallDelete, CGF.ReturnBlock.getBlock(),
+ callDeleteBB);
+ CGF.EmitBlock(callDeleteBB);
+ const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
+ const CXXRecordDecl *ClassDecl = Dtor->getParent();
+ CGF.EmitDeleteCall(Dtor->getOperatorDelete(), allocatedPtr,
+ CGF.getContext().getTagDeclType(ClassDecl));
+
+ CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
+ CGF.EmitBlock(ScalarBB);
+}
+
/// EmitDestructorBody - Emits the body of the current destructor.
void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CurGD.getDecl());
@@ -1461,7 +1525,9 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
// 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) {
+ if (DtorType == Dtor_Deleting || DtorType == Dtor_VectorDeleting) {
+ if (CXXStructorImplicitParamValue && DtorType == Dtor_VectorDeleting)
+ EmitConditionalArrayDtorCall(Dtor, *this, CXXStructorImplicitParamValue);
RunCleanupsScope DtorEpilogue(*this);
EnterDtorCleanups(Dtor, Dtor_Deleting);
if (HaveInsertPoint()) {
@@ -1490,6 +1556,8 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
switch (DtorType) {
case Dtor_Comdat: llvm_unreachable("not expecting a COMDAT");
case Dtor_Deleting: llvm_unreachable("already handled deleting case");
+ case Dtor_VectorDeleting:
+ llvm_unreachable("already handled vector deleting case");
case Dtor_Complete:
assert((Body || getTarget().getCXXABI().isMicrosoft()) &&
@@ -1572,7 +1640,6 @@ namespace {
return CGF.EmitScalarExpr(ThisArg);
return CGF.LoadCXXThis();
}
-
/// Call the operator delete associated with the current destructor.
struct CallDtorDelete final : EHScopeStack::Cleanup {
CallDtorDelete() {}
@@ -1591,8 +1658,10 @@ namespace {
bool ReturnAfterDelete) {
llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
- llvm::Value *ShouldCallDelete
- = CGF.Builder.CreateIsNull(ShouldDeleteCondition);
+ auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType());
+ llvm::Value *CheckTheBit = CGF.Builder.CreateAnd(
+ ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 1));
+ llvm::Value *ShouldCallDelete = CGF.Builder.CreateIsNull(CheckTheBit);
CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
CGF.EmitBlock(callDeleteBB);
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index ba0dec9..52aa956 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2119,7 +2119,8 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
// Emit MS ABI vftable information. There is only one entry for the
// deleting dtor.
const auto *DD = dyn_cast<CXXDestructorDecl>(Method);
- GlobalDecl GD = DD ? GlobalDecl(DD, Dtor_Deleting) : GlobalDecl(Method);
+ GlobalDecl GD =
+ DD ? GlobalDecl(DD, Dtor_VectorDeleting) : GlobalDecl(Method);
MethodVFTableLocation ML =
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD);
VIndex = ML.Index;
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 5d96959..5c11c0b 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1209,6 +1209,8 @@ void CodeGenFunction::EmitNewArrayInitializer(
EmitCXXAggrConstructorCall(Ctor, NumElements, CurPtr, CCE,
/*NewPointerIsChecked*/true,
CCE->requiresZeroInitialization());
+ if (CGM.getCXXABI().hasVectorDeletingDtors())
+ CGM.requireVectorDestructorDefinition(Ctor->getParent());
return;
}
@@ -1912,10 +1914,8 @@ static void EmitDestroyingObjectDelete(CodeGenFunction &CGF,
/// Emit the code for deleting a single object.
/// \return \c true if we started emitting UnconditionalDeleteBlock, \c false
/// if not.
-static bool EmitObjectDelete(CodeGenFunction &CGF,
- const CXXDeleteExpr *DE,
- Address Ptr,
- QualType ElementType,
+static bool EmitObjectDelete(CodeGenFunction &CGF, const CXXDeleteExpr *DE,
+ Address Ptr, QualType ElementType,
llvm::BasicBlock *UnconditionalDeleteBlock) {
// C++11 [expr.delete]p3:
// If the static type of the object to be deleted is different from its
@@ -2131,6 +2131,40 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) {
assert(ConvertTypeForMem(DeleteTy) == Ptr.getElementType());
+ if (E->isArrayForm() && CGM.getCXXABI().hasVectorDeletingDtors()) {
+ if (auto *RD = DeleteTy->getAsCXXRecordDecl()) {
+ auto *Dtor = RD->getDestructor();
+ if (Dtor && Dtor->isVirtual()) {
+ llvm::Value *NumElements = nullptr;
+ llvm::Value *AllocatedPtr = nullptr;
+ CharUnits CookieSize;
+ llvm::BasicBlock *bodyBB = createBasicBlock("vdtor.call");
+ llvm::BasicBlock *doneBB = createBasicBlock("vdtor.nocall");
+ // Check array cookie to see if the array has 0 length. Don't call
+ // the destructor in that case.
+ CGM.getCXXABI().ReadArrayCookie(*this, Ptr, E, DeleteTy, NumElements,
+ AllocatedPtr, CookieSize);
+
+ auto *CondTy = cast<llvm::IntegerType>(NumElements->getType());
+ llvm::Value *isEmpty = Builder.CreateICmpEQ(
+ NumElements, llvm::ConstantInt::get(CondTy, 0));
+ Builder.CreateCondBr(isEmpty, doneBB, bodyBB);
+
+ // Delete cookie for empty array.
+ const FunctionDecl *operatorDelete = E->getOperatorDelete();
+ EmitBlock(doneBB);
+ EmitDeleteCall(operatorDelete, AllocatedPtr, DeleteTy, NumElements,
+ CookieSize);
+ EmitBranch(DeleteEnd);
+
+ EmitBlock(bodyBB);
+ if (!EmitObjectDelete(*this, E, Ptr, DeleteTy, DeleteEnd))
+ EmitBlock(DeleteEnd);
+ return;
+ }
+ }
+ }
+
if (E->isArrayForm()) {
EmitArrayDelete(*this, E, Ptr, DeleteTy);
EmitBlock(DeleteEnd);
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index c7b3695..dcd1fa7 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -769,7 +769,8 @@ void CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
case VTableComponent::CK_FunctionPointer:
case VTableComponent::CK_CompleteDtorPointer:
case VTableComponent::CK_DeletingDtorPointer: {
- GlobalDecl GD = component.getGlobalDecl();
+ GlobalDecl GD =
+ component.getGlobalDecl(CGM.getCXXABI().hasVectorDeletingDtors());
const bool IsThunk =
nextVTableThunkIndex < layout.vtable_thunks().size() &&
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 43345da..8f9cf96 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -7937,3 +7937,51 @@ void CodeGenModule::moveLazyEmissionStates(CodeGenModule *NewBuilder) {
NewBuilder->ABI->MangleCtx = std::move(ABI->MangleCtx);
}
+
+bool CodeGenModule::classNeedsVectorDestructor(const CXXRecordDecl *RD) {
+ CXXDestructorDecl *Dtor = RD->getDestructor();
+ // The compiler can't know if new[]/delete[] will be used outside of the DLL,
+ // so just force vector deleting destructor emission if dllexport is present.
+ // This matches MSVC behavior.
+ if (Dtor && Dtor->isVirtual() && Dtor->isDefined() &&
+ Dtor->hasAttr<DLLExportAttr>())
+ return true;
+
+ assert(getCXXABI().hasVectorDeletingDtors());
+ return RequireVectorDeletingDtor.count(RD);
+}
+
+void CodeGenModule::requireVectorDestructorDefinition(const CXXRecordDecl *RD) {
+ assert(getCXXABI().hasVectorDeletingDtors());
+ RequireVectorDeletingDtor.insert(RD);
+
+ // To reduce code size in general case we lazily emit scalar deleting
+ // destructor definition and an alias from vector deleting destructor to
+ // scalar deleting destructor. It may happen that we first emitted the scalar
+ // deleting destructor definition and the alias and then discovered that the
+ // definition of the vector deleting destructor is required. Then we need to
+ // remove the alias and the scalar deleting destructor and queue vector
+ // deleting destructor body for emission. Check if that is the case.
+ CXXDestructorDecl *DtorD = RD->getDestructor();
+ GlobalDecl ScalarDtorGD(DtorD, Dtor_Deleting);
+ StringRef MangledName = getMangledName(ScalarDtorGD);
+ llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
+ if (Entry && !Entry->isDeclaration()) {
+ GlobalDecl VectorDtorGD(DtorD, Dtor_VectorDeleting);
+ StringRef VDName = getMangledName(VectorDtorGD);
+ llvm::GlobalValue *VDEntry = GetGlobalValue(VDName);
+ // It exists and it should be an alias.
+ assert(VDEntry && isa<llvm::GlobalAlias>(VDEntry));
+ auto *NewFn = llvm::Function::Create(
+ cast<llvm::FunctionType>(VDEntry->getValueType()),
+ llvm::Function::ExternalLinkage, VDName, &getModule());
+ SetFunctionAttributes(VectorDtorGD, NewFn, /*IsIncompleteFunction*/ false,
+ /*IsThunk*/ false);
+ NewFn->takeName(VDEntry);
+ VDEntry->replaceAllUsesWith(NewFn);
+ VDEntry->eraseFromParent();
+ Entry->replaceAllUsesWith(NewFn);
+ Entry->eraseFromParent();
+ addDeferredDeclToEmit(VectorDtorGD);
+ }
+}
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 46de3d86..2cf15e2 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -528,6 +528,9 @@ private:
/// that we don't re-emit the initializer.
llvm::DenseMap<const Decl*, unsigned> DelayedCXXInitPosition;
+ /// To remember which types did require a vector deleting dtor.
+ llvm::SmallPtrSet<const CXXRecordDecl *, 16> RequireVectorDeletingDtor;
+
typedef std::pair<OrderGlobalInitsOrStermFinalizers, llvm::Function *>
GlobalInitData;
@@ -1542,6 +1545,7 @@ public:
void EmitGlobal(GlobalDecl D);
bool TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D);
+ void EmitDefinitionAsAlias(GlobalDecl Alias, GlobalDecl Target);
llvm::GlobalValue *GetGlobalValue(StringRef Ref);
@@ -1809,6 +1813,8 @@ public:
// behavior. So projects like the Linux kernel can rely on it.
return !getLangOpts().CPlusPlus;
}
+ void requireVectorDestructorDefinition(const CXXRecordDecl *RD);
+ bool classNeedsVectorDestructor(const CXXRecordDecl *RD);
private:
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 77e995b..38e3a63 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -90,6 +90,8 @@ public:
case Dtor_Comdat:
llvm_unreachable("emitting dtor comdat as function?");
+ case Dtor_VectorDeleting:
+ llvm_unreachable("unexpected dtor kind for this ABI");
}
llvm_unreachable("bad dtor kind");
}
@@ -179,6 +181,7 @@ public:
}
bool shouldTypeidBeNullChecked(QualType SrcRecordTy) override;
+ bool hasVectorDeletingDtors() override { return false; }
void EmitBadTypeidCall(CodeGenFunction &CGF) override;
llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy,
Address ThisPtr,
@@ -448,7 +451,8 @@ public:
if (!IsInlined)
continue;
- StringRef Name = CGM.getMangledName(VtableComponent.getGlobalDecl());
+ StringRef Name = CGM.getMangledName(
+ VtableComponent.getGlobalDecl(/*HasVectorDeletingDtors=*/false));
auto *Entry = CGM.GetGlobalValue(Name);
// This checks if virtual inline function has already been emitted.
// Note that it is possible that this inline function would be emitted
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 40371d9..464d437 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -70,8 +70,8 @@ public:
switch (GD.getDtorType()) {
case Dtor_Complete:
case Dtor_Deleting:
+ case Dtor_VectorDeleting:
return true;
-
case Dtor_Base:
return false;
@@ -145,6 +145,7 @@ public:
}
bool shouldTypeidBeNullChecked(QualType SrcRecordTy) override;
+ bool hasVectorDeletingDtors() override { return true; }
void EmitBadTypeidCall(CodeGenFunction &CGF) override;
llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy,
Address ThisPtr,
@@ -260,7 +261,7 @@ public:
// There's only Dtor_Deleting in vftable but it shares the this
// adjustment with the base one, so look up the deleting one instead.
- LookupGD = GlobalDecl(DD, Dtor_Deleting);
+ LookupGD = GlobalDecl(DD, Dtor_VectorDeleting);
}
MethodVFTableLocation ML =
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
@@ -342,8 +343,8 @@ public:
void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD,
CallArgList &CallArgs) override {
- assert(GD.getDtorType() == Dtor_Deleting &&
- "Only deleting destructor thunks are available in this ABI");
+ assert(GD.getDtorType() == Dtor_VectorDeleting &&
+ "Only vector deleting destructor thunks are available in this ABI");
CallArgs.add(RValue::get(getStructorImplicitParamValue(CGF)),
getContext().IntTy);
}
@@ -1090,7 +1091,8 @@ bool MicrosoftCXXABI::HasThisReturn(GlobalDecl GD) const {
static bool isDeletingDtor(GlobalDecl GD) {
return isa<CXXDestructorDecl>(GD.getDecl()) &&
- GD.getDtorType() == Dtor_Deleting;
+ (GD.getDtorType() == Dtor_Deleting ||
+ GD.getDtorType() == Dtor_VectorDeleting);
}
bool MicrosoftCXXABI::hasMostDerivedReturn(GlobalDecl GD) const {
@@ -1343,7 +1345,8 @@ MicrosoftCXXABI::buildStructorSignature(GlobalDecl GD,
AddedStructorArgCounts Added;
// TODO: 'for base' flag
if (isa<CXXDestructorDecl>(GD.getDecl()) &&
- GD.getDtorType() == Dtor_Deleting) {
+ (GD.getDtorType() == Dtor_Deleting ||
+ GD.getDtorType() == Dtor_VectorDeleting)) {
// The scalar deleting destructor takes an implicit int parameter.
ArgTys.push_back(getContext().IntTy);
++Added.Suffix;
@@ -1375,7 +1378,7 @@ void MicrosoftCXXABI::setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
CXXDtorType DT) const {
// Deleting destructor variants are never imported or exported. Give them the
// default storage class.
- if (DT == Dtor_Deleting) {
+ if (DT == Dtor_Deleting || DT == Dtor_VectorDeleting) {
GV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
} else {
const NamedDecl *ND = Dtor;
@@ -1409,6 +1412,12 @@ llvm::GlobalValue::LinkageTypes MicrosoftCXXABI::getCXXDestructorLinkage(
// and are emitted everywhere they are used. They are internal if the class
// is internal.
return llvm::GlobalValue::LinkOnceODRLinkage;
+ case Dtor_VectorDeleting:
+ // Use the weak, non-ODR linkage for vector deleting destructors to block
+ // inlining. This enables an MS ABI code-size saving optimization that
+ // allows us to avoid emitting array deletion code when arrays of a given
+ // type are not allocated within the final linkage unit.
+ return llvm::GlobalValue::WeakAnyLinkage;
case Dtor_Comdat:
llvm_unreachable("MS C++ ABI does not support comdat dtors");
}
@@ -1440,7 +1449,7 @@ MicrosoftCXXABI::getVirtualFunctionPrologueThisAdjustment(GlobalDecl GD) {
// There's no Dtor_Base in vftable but it shares the this adjustment with
// the deleting one, so look it up instead.
- GD = GlobalDecl(DD, Dtor_Deleting);
+ GD = GlobalDecl(DD, Dtor_VectorDeleting);
}
MethodVFTableLocation ML =
@@ -1489,7 +1498,7 @@ Address MicrosoftCXXABI::adjustThisArgumentForVirtualFunctionCall(
// There's only Dtor_Deleting in vftable but it shares the this adjustment
// with the base one, so look up the deleting one instead.
- LookupGD = GlobalDecl(DD, Dtor_Deleting);
+ LookupGD = GlobalDecl(DD, Dtor_VectorDeleting);
}
MethodVFTableLocation ML =
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
@@ -2002,20 +2011,20 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
auto *D = dyn_cast<const CXXDeleteExpr *>(E);
assert((CE != nullptr) ^ (D != nullptr));
assert(CE == nullptr || CE->arg_begin() == CE->arg_end());
- assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
+ assert(DtorType == Dtor_VectorDeleting || DtorType == Dtor_Complete ||
+ DtorType == Dtor_Deleting);
// We have only one destructor in the vftable but can get both behaviors
// by passing an implicit int parameter.
- GlobalDecl GD(Dtor, Dtor_Deleting);
+ GlobalDecl GD(Dtor, Dtor_VectorDeleting);
const CGFunctionInfo *FInfo =
&CGM.getTypes().arrangeCXXStructorDeclaration(GD);
llvm::FunctionType *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo);
CGCallee Callee = CGCallee::forVirtual(CE, GD, This, Ty);
ASTContext &Context = getContext();
- llvm::Value *ImplicitParam = llvm::ConstantInt::get(
- llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()),
- DtorType == Dtor_Deleting);
+ uint32_t Flags = ((D && D->isArrayForm()) << 1) | (DtorType == Dtor_Deleting);
+ llvm::Value *ImplicitParam = CGF.Builder.getInt32(Flags);
QualType ThisTy;
if (CE) {
@@ -4056,6 +4065,18 @@ void MicrosoftCXXABI::emitCXXStructor(GlobalDecl GD) {
if (GD.getDtorType() == Dtor_Base && !CGM.TryEmitBaseDestructorAsAlias(dtor))
return;
+ if (GD.getDtorType() == Dtor_VectorDeleting &&
+ !CGM.classNeedsVectorDestructor(dtor->getParent())) {
+ // Create GlobalDecl object with the correct type for the scalar
+ // deleting destructor.
+ GlobalDecl ScalarDtorGD(dtor, Dtor_Deleting);
+
+ // Emit an alias from the vector deleting destructor to the scalar deleting
+ // destructor.
+ CGM.EmitDefinitionAsAlias(GD, ScalarDtorGD);
+ return;
+ }
+
llvm::Function *Fn = CGM.codegenCXXStructor(GD);
if (Fn->isWeakForLinker())
Fn->setComdat(CGM.getModule().getOrInsertComdat(Fn->getName()));