aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
authorMariya Podchishchaeva <mariya.podchishchaeva@intel.com>2025-03-04 09:17:50 +0100
committerGitHub <noreply@github.com>2025-03-04 09:17:50 +0100
commitd6942d54f677000cf713d2b0eba57b641452beb4 (patch)
tree5a1e1e7a9748b14506f2fe03136fa604381b5916 /clang/lib
parenta619a2e53a9ba09ba18a047b8389bf4dd1912b72 (diff)
downloadllvm-d6942d54f677000cf713d2b0eba57b641452beb4.zip
llvm-d6942d54f677000cf713d2b0eba57b641452beb4.tar.gz
llvm-d6942d54f677000cf713d2b0eba57b641452beb4.tar.bz2
[MS][clang] Add support for vector deleting destructors (#126240)
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. 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.cpp46
-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, 292 insertions, 41 deletions
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index b6ba367..b80dd1c 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -6014,6 +6014,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 fe34251..6e2be25 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 18893b9..b7c6ad8 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]";
@@ -3758,7 +3765,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;
}
@@ -3875,7 +3882,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 7c6dfc3..e109fd0 100644
--- a/clang/lib/CodeGen/CGCXXABI.cpp
+++ b/clang/lib/CodeGen/CGCXXABI.cpp
@@ -273,6 +273,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 fa69caa4..e54fd54 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1433,6 +1433,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());
@@ -1462,7 +1526,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()) {
@@ -1491,6 +1557,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()) &&
@@ -1573,7 +1641,6 @@ namespace {
return CGF.EmitScalarExpr(ThisArg);
return CGF.LoadCXXThis();
}
-
/// Call the operator delete associated with the current destructor.
struct CallDtorDelete final : EHScopeStack::Cleanup {
CallDtorDelete() {}
@@ -1592,8 +1659,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 f4d4a36..0e6daa4 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 f71c18a..d4e14f4 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 c910893..66d75ef 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 3caa79b..cec8f12 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -7927,3 +7927,49 @@ 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());
+ 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 5726d79..a72b4f7 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;
@@ -1544,6 +1547,7 @@ public:
void EmitGlobal(GlobalDecl D);
bool TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D);
+ void EmitDefinitionAsAlias(GlobalDecl Alias, GlobalDecl Target);
llvm::GlobalValue *GetGlobalValue(StringRef Ref);
@@ -1807,6 +1811,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 7e26a0d..b145da0 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 5cb742a..4b55fc3 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) {
@@ -4055,6 +4064,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()));