Skip to content

Commit fdab7da

Browse files
committed
Revert "Revert "[MS][clang] Add support for vector deleting destructors (#126240)""
This reverts commit e11ede5.
1 parent 316bb89 commit fdab7da

34 files changed

+509
-125
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ Windows Support
432432
- Clang now can process the `i128` and `ui128` integeral suffixes when MSVC
433433
extensions are enabled. This allows for properly processing ``intsafe.h`` in
434434
the Windows SDK.
435+
- Clang now supports MSVC vector deleting destructors (GH19772).
435436

436437
LoongArch Support
437438
^^^^^^^^^^^^^^^^^

clang/include/clang/AST/VTableBuilder.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class VTableComponent {
150150

151151
bool isRTTIKind() const { return isRTTIKind(getKind()); }
152152

153-
GlobalDecl getGlobalDecl() const {
153+
GlobalDecl getGlobalDecl(bool HasVectorDeletingDtors) const {
154154
assert(isUsedFunctionPointerKind() &&
155155
"GlobalDecl can be created only from virtual function");
156156

@@ -161,7 +161,9 @@ class VTableComponent {
161161
case CK_CompleteDtorPointer:
162162
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Complete);
163163
case CK_DeletingDtorPointer:
164-
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Deleting);
164+
return GlobalDecl(DtorDecl, (HasVectorDeletingDtors)
165+
? CXXDtorType::Dtor_VectorDeleting
166+
: CXXDtorType::Dtor_Deleting);
165167
case CK_VCallOffset:
166168
case CK_VBaseOffset:
167169
case CK_OffsetToTop:

clang/include/clang/Basic/ABI.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ enum CXXCtorType {
3131

3232
/// C++ destructor types.
3333
enum CXXDtorType {
34-
Dtor_Deleting, ///< Deleting dtor
35-
Dtor_Complete, ///< Complete object dtor
36-
Dtor_Base, ///< Base object dtor
37-
Dtor_Comdat ///< The COMDAT used for dtors
34+
Dtor_Deleting, ///< Deleting dtor
35+
Dtor_Complete, ///< Complete object dtor
36+
Dtor_Base, ///< Base object dtor
37+
Dtor_Comdat, ///< The COMDAT used for dtors
38+
Dtor_VectorDeleting ///< Vector deleting dtor
3839
};
3940

4041
} // end namespace clang

clang/lib/AST/ItaniumMangle.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6004,6 +6004,8 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
60046004
case Dtor_Comdat:
60056005
Out << "D5";
60066006
break;
6007+
case Dtor_VectorDeleting:
6008+
llvm_unreachable("Itanium ABI does not use vector deleting dtors");
60076009
}
60086010
}
60096011

clang/lib/AST/MicrosoftMangle.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,8 +1484,9 @@ void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
14841484
// <operator-name> ::= ?_G # scalar deleting destructor
14851485
case Dtor_Deleting: Out << "?_G"; return;
14861486
// <operator-name> ::= ?_E # vector deleting destructor
1487-
// FIXME: Add a vector deleting dtor type. It goes in the vtable, so we need
1488-
// it.
1487+
case Dtor_VectorDeleting:
1488+
Out << "?_E";
1489+
return;
14891490
case Dtor_Comdat:
14901491
llvm_unreachable("not expecting a COMDAT");
14911492
}
@@ -2886,9 +2887,12 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
28862887
// ::= @ # structors (they have no declared return type)
28872888
if (IsStructor) {
28882889
if (isa<CXXDestructorDecl>(D) && isStructorDecl(D)) {
2889-
// The scalar deleting destructor takes an extra int argument which is not
2890-
// reflected in the AST.
2891-
if (StructorType == Dtor_Deleting) {
2890+
// The deleting destructors take an extra argument of type int that
2891+
// indicates whether the storage for the object should be deleted and
2892+
// whether a single object or an array of objects is being destroyed. This
2893+
// extra argument is not reflected in the AST.
2894+
if (StructorType == Dtor_Deleting ||
2895+
StructorType == Dtor_VectorDeleting) {
28922896
Out << (PointersAre64Bit ? "PEAXI@Z" : "PAXI@Z");
28932897
return;
28942898
}
@@ -3861,10 +3865,10 @@ void MicrosoftMangleContextImpl::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
38613865
const ThunkInfo &Thunk,
38623866
bool /*ElideOverrideInfo*/,
38633867
raw_ostream &Out) {
3864-
// FIXME: Actually, the dtor thunk should be emitted for vector deleting
3865-
// dtors rather than scalar deleting dtors. Just use the vector deleting dtor
3866-
// mangling manually until we support both deleting dtor types.
3867-
assert(Type == Dtor_Deleting);
3868+
// The dtor thunk should use vector deleting dtor mangling, however as an
3869+
// optimization we may end up emitting only scalar deleting dtor body, so just
3870+
// use the vector deleting dtor mangling manually.
3871+
assert(Type == Dtor_Deleting || Type == Dtor_VectorDeleting);
38683872
msvc_hashing_ostream MHO(Out);
38693873
MicrosoftCXXNameMangler Mangler(*this, MHO, DD, Type);
38703874
Mangler.getStream() << "??_E";

clang/lib/AST/VTableBuilder.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,8 +1735,8 @@ void ItaniumVTableBuilder::LayoutPrimaryAndSecondaryVTables(
17351735
const CXXMethodDecl *MD = I.first;
17361736
const MethodInfo &MI = I.second;
17371737
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
1738-
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)]
1739-
= MI.VTableIndex - AddressPoint;
1738+
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] =
1739+
MI.VTableIndex - AddressPoint;
17401740
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)]
17411741
= MI.VTableIndex + 1 - AddressPoint;
17421742
} else {
@@ -2657,7 +2657,11 @@ class VFTableBuilder {
26572657
MethodVFTableLocation Loc(MI.VBTableIndex, WhichVFPtr.getVBaseWithVPtr(),
26582658
WhichVFPtr.NonVirtualOffset, MI.VFTableIndex);
26592659
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
2660-
MethodVFTableLocations[GlobalDecl(DD, Dtor_Deleting)] = Loc;
2660+
// In Microsoft ABI vftable always references vector deleting dtor.
2661+
CXXDtorType DtorTy = Context.getTargetInfo().getCXXABI().isMicrosoft()
2662+
? Dtor_VectorDeleting
2663+
: Dtor_Deleting;
2664+
MethodVFTableLocations[GlobalDecl(DD, DtorTy)] = Loc;
26612665
} else {
26622666
MethodVFTableLocations[MD] = Loc;
26632667
}
@@ -3287,7 +3291,10 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) {
32873291
const CXXDestructorDecl *DD = Component.getDestructorDecl();
32883292

32893293
DD->printQualifiedName(Out);
3290-
Out << "() [scalar deleting]";
3294+
if (Context.getTargetInfo().getCXXABI().isMicrosoft())
3295+
Out << "() [vector deleting]";
3296+
else
3297+
Out << "() [scalar deleting]";
32913298

32923299
if (DD->isPureVirtual())
32933300
Out << " [pure]";
@@ -3758,7 +3765,7 @@ void MicrosoftVTableContext::dumpMethodLocations(
37583765
PredefinedIdentKind::PrettyFunctionNoVirtual, MD);
37593766

37603767
if (isa<CXXDestructorDecl>(MD)) {
3761-
IndicesMap[I.second] = MethodName + " [scalar deleting]";
3768+
IndicesMap[I.second] = MethodName + " [vector deleting]";
37623769
} else {
37633770
IndicesMap[I.second] = MethodName;
37643771
}
@@ -3875,7 +3882,7 @@ MicrosoftVTableContext::getMethodVFTableLocation(GlobalDecl GD) {
38753882
assert(hasVtableSlot(cast<CXXMethodDecl>(GD.getDecl())) &&
38763883
"Only use this method for virtual methods or dtors");
38773884
if (isa<CXXDestructorDecl>(GD.getDecl()))
3878-
assert(GD.getDtorType() == Dtor_Deleting);
3885+
assert(GD.getDtorType() == Dtor_VectorDeleting);
38793886

38803887
GD = GD.getCanonicalDecl();
38813888

clang/lib/CodeGen/CGCXX.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
175175
// requires explicit comdat support in the IL.
176176
if (llvm::GlobalValue::isWeakForLinker(TargetLinkage))
177177
return true;
178-
179178
// Create the alias with no name.
180179
auto *Alias = llvm::GlobalAlias::create(AliasValueType, 0, Linkage, "",
181180
Aliasee, &getModule());
@@ -201,6 +200,42 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
201200
return false;
202201
}
203202

203+
/// Emit a definition as a global alias for another definition, unconditionally.
204+
void CodeGenModule::EmitDefinitionAsAlias(GlobalDecl AliasDecl,
205+
GlobalDecl TargetDecl) {
206+
207+
llvm::Type *AliasValueType = getTypes().GetFunctionType(AliasDecl);
208+
209+
StringRef MangledName = getMangledName(AliasDecl);
210+
llvm::GlobalValue *Entry = GetGlobalValue(MangledName);
211+
if (Entry && !Entry->isDeclaration())
212+
return;
213+
auto *Aliasee = cast<llvm::GlobalValue>(GetAddrOfGlobal(TargetDecl));
214+
215+
// Determine the linkage type for the alias.
216+
llvm::GlobalValue::LinkageTypes Linkage = getFunctionLinkage(AliasDecl);
217+
218+
// Create the alias with no name.
219+
auto *Alias = llvm::GlobalAlias::create(AliasValueType, 0, Linkage, "",
220+
Aliasee, &getModule());
221+
// Destructors are always unnamed_addr.
222+
Alias->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
223+
224+
if (Entry) {
225+
assert(Entry->getValueType() == AliasValueType &&
226+
Entry->getAddressSpace() == Alias->getAddressSpace() &&
227+
"declaration exists with different type");
228+
Alias->takeName(Entry);
229+
Entry->replaceAllUsesWith(Alias);
230+
Entry->eraseFromParent();
231+
} else {
232+
Alias->setName(MangledName);
233+
}
234+
235+
// Set any additional necessary attributes for the alias.
236+
SetCommonAttributes(AliasDecl, Alias);
237+
}
238+
204239
llvm::Function *CodeGenModule::codegenCXXStructor(GlobalDecl GD) {
205240
const CGFunctionInfo &FnInfo = getTypes().arrangeCXXStructorDeclaration(GD);
206241
auto *Fn = cast<llvm::Function>(

clang/lib/CodeGen/CGCXXABI.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,20 @@ void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, Address ptr,
272272
numElements = readArrayCookieImpl(CGF, allocAddr, cookieSize);
273273
}
274274

275+
void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, Address ptr,
276+
QualType eltTy, llvm::Value *&numElements,
277+
llvm::Value *&allocPtr, CharUnits &cookieSize) {
278+
assert(eltTy.isDestructedType());
279+
280+
// Derive a char* in the same address space as the pointer.
281+
ptr = ptr.withElementType(CGF.Int8Ty);
282+
283+
cookieSize = getArrayCookieSizeImpl(eltTy);
284+
Address allocAddr = CGF.Builder.CreateConstInBoundsByteGEP(ptr, -cookieSize);
285+
allocPtr = allocAddr.emitRawPointer(CGF);
286+
numElements = readArrayCookieImpl(CGF, allocAddr, cookieSize);
287+
}
288+
275289
llvm::Value *CGCXXABI::readArrayCookieImpl(CodeGenFunction &CGF,
276290
Address ptr,
277291
CharUnits cookieSize) {

clang/lib/CodeGen/CGCXXABI.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ class CGCXXABI {
275275
virtual CatchTypeInfo getCatchAllTypeInfo();
276276

277277
virtual bool shouldTypeidBeNullChecked(QualType SrcRecordTy) = 0;
278+
virtual bool hasVectorDeletingDtors() = 0;
278279
virtual void EmitBadTypeidCall(CodeGenFunction &CGF) = 0;
279280
virtual llvm::Value *EmitTypeid(CodeGenFunction &CGF, QualType SrcRecordTy,
280281
Address ThisPtr,
@@ -575,6 +576,12 @@ class CGCXXABI {
575576
QualType ElementType, llvm::Value *&NumElements,
576577
llvm::Value *&AllocPtr, CharUnits &CookieSize);
577578

579+
/// Reads the array cookie associated with the given pointer,
580+
/// that should have one.
581+
void ReadArrayCookie(CodeGenFunction &CGF, Address Ptr, QualType ElementType,
582+
llvm::Value *&NumElements, llvm::Value *&AllocPtr,
583+
CharUnits &CookieSize);
584+
578585
/// Return whether the given global decl needs a VTT parameter.
579586
virtual bool NeedsVTTParameter(GlobalDecl GD);
580587

clang/lib/CodeGen/CGClass.cpp

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,70 @@ static bool CanSkipVTablePointerInitialization(CodeGenFunction &CGF,
14321432
return true;
14331433
}
14341434

1435+
static void EmitConditionalArrayDtorCall(const CXXDestructorDecl *DD,
1436+
CodeGenFunction &CGF,
1437+
llvm::Value *ShouldDeleteCondition) {
1438+
Address ThisPtr = CGF.LoadCXXThisAddress();
1439+
llvm::BasicBlock *ScalarBB = CGF.createBasicBlock("dtor.scalar");
1440+
llvm::BasicBlock *callDeleteBB =
1441+
CGF.createBasicBlock("dtor.call_delete_after_array_destroy");
1442+
llvm::BasicBlock *VectorBB = CGF.createBasicBlock("dtor.vector");
1443+
auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType());
1444+
llvm::Value *CheckTheBitForArrayDestroy = CGF.Builder.CreateAnd(
1445+
ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 2));
1446+
llvm::Value *ShouldDestroyArray =
1447+
CGF.Builder.CreateIsNull(CheckTheBitForArrayDestroy);
1448+
CGF.Builder.CreateCondBr(ShouldDestroyArray, ScalarBB, VectorBB);
1449+
1450+
CGF.EmitBlock(VectorBB);
1451+
1452+
llvm::Value *numElements = nullptr;
1453+
llvm::Value *allocatedPtr = nullptr;
1454+
CharUnits cookieSize;
1455+
QualType EltTy = DD->getThisType()->getPointeeType();
1456+
CGF.CGM.getCXXABI().ReadArrayCookie(CGF, ThisPtr, EltTy, numElements,
1457+
allocatedPtr, cookieSize);
1458+
1459+
// Destroy the elements.
1460+
QualType::DestructionKind dtorKind = EltTy.isDestructedType();
1461+
1462+
assert(dtorKind);
1463+
assert(numElements && "no element count for a type with a destructor!");
1464+
1465+
CharUnits elementSize = CGF.getContext().getTypeSizeInChars(EltTy);
1466+
CharUnits elementAlign =
1467+
ThisPtr.getAlignment().alignmentOfArrayElement(elementSize);
1468+
1469+
llvm::Value *arrayBegin = ThisPtr.emitRawPointer(CGF);
1470+
llvm::Value *arrayEnd = CGF.Builder.CreateInBoundsGEP(
1471+
ThisPtr.getElementType(), arrayBegin, numElements, "delete.end");
1472+
1473+
// We already checked that the array is not 0-length before entering vector
1474+
// deleting dtor.
1475+
CGF.emitArrayDestroy(arrayBegin, arrayEnd, EltTy, elementAlign,
1476+
CGF.getDestroyer(dtorKind),
1477+
/*checkZeroLength*/ false, CGF.needsEHCleanup(dtorKind));
1478+
1479+
llvm::BasicBlock *VectorBBCont = CGF.createBasicBlock("dtor.vector.cont");
1480+
CGF.EmitBlock(VectorBBCont);
1481+
1482+
llvm::Value *CheckTheBitForDeleteCall = CGF.Builder.CreateAnd(
1483+
ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 1));
1484+
1485+
llvm::Value *ShouldCallDelete =
1486+
CGF.Builder.CreateIsNull(CheckTheBitForDeleteCall);
1487+
CGF.Builder.CreateCondBr(ShouldCallDelete, CGF.ReturnBlock.getBlock(),
1488+
callDeleteBB);
1489+
CGF.EmitBlock(callDeleteBB);
1490+
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl);
1491+
const CXXRecordDecl *ClassDecl = Dtor->getParent();
1492+
CGF.EmitDeleteCall(Dtor->getOperatorDelete(), allocatedPtr,
1493+
CGF.getContext().getTagDeclType(ClassDecl));
1494+
1495+
CGF.EmitBranchThroughCleanup(CGF.ReturnBlock);
1496+
CGF.EmitBlock(ScalarBB);
1497+
}
1498+
14351499
/// EmitDestructorBody - Emits the body of the current destructor.
14361500
void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
14371501
const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CurGD.getDecl());
@@ -1461,7 +1525,9 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
14611525
// outside of the function-try-block, which means it's always
14621526
// possible to delegate the destructor body to the complete
14631527
// destructor. Do so.
1464-
if (DtorType == Dtor_Deleting) {
1528+
if (DtorType == Dtor_Deleting || DtorType == Dtor_VectorDeleting) {
1529+
if (CXXStructorImplicitParamValue && DtorType == Dtor_VectorDeleting)
1530+
EmitConditionalArrayDtorCall(Dtor, *this, CXXStructorImplicitParamValue);
14651531
RunCleanupsScope DtorEpilogue(*this);
14661532
EnterDtorCleanups(Dtor, Dtor_Deleting);
14671533
if (HaveInsertPoint()) {
@@ -1490,6 +1556,8 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
14901556
switch (DtorType) {
14911557
case Dtor_Comdat: llvm_unreachable("not expecting a COMDAT");
14921558
case Dtor_Deleting: llvm_unreachable("already handled deleting case");
1559+
case Dtor_VectorDeleting:
1560+
llvm_unreachable("already handled vector deleting case");
14931561

14941562
case Dtor_Complete:
14951563
assert((Body || getTarget().getCXXABI().isMicrosoft()) &&
@@ -1572,7 +1640,6 @@ namespace {
15721640
return CGF.EmitScalarExpr(ThisArg);
15731641
return CGF.LoadCXXThis();
15741642
}
1575-
15761643
/// Call the operator delete associated with the current destructor.
15771644
struct CallDtorDelete final : EHScopeStack::Cleanup {
15781645
CallDtorDelete() {}
@@ -1591,8 +1658,10 @@ namespace {
15911658
bool ReturnAfterDelete) {
15921659
llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
15931660
llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
1594-
llvm::Value *ShouldCallDelete
1595-
= CGF.Builder.CreateIsNull(ShouldDeleteCondition);
1661+
auto *CondTy = cast<llvm::IntegerType>(ShouldDeleteCondition->getType());
1662+
llvm::Value *CheckTheBit = CGF.Builder.CreateAnd(
1663+
ShouldDeleteCondition, llvm::ConstantInt::get(CondTy, 1));
1664+
llvm::Value *ShouldCallDelete = CGF.Builder.CreateIsNull(CheckTheBit);
15961665
CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
15971666

15981667
CGF.EmitBlock(callDeleteBB);

clang/lib/CodeGen/CGDebugInfo.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2119,7 +2119,8 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
21192119
// Emit MS ABI vftable information. There is only one entry for the
21202120
// deleting dtor.
21212121
const auto *DD = dyn_cast<CXXDestructorDecl>(Method);
2122-
GlobalDecl GD = DD ? GlobalDecl(DD, Dtor_Deleting) : GlobalDecl(Method);
2122+
GlobalDecl GD =
2123+
DD ? GlobalDecl(DD, Dtor_VectorDeleting) : GlobalDecl(Method);
21232124
MethodVFTableLocation ML =
21242125
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD);
21252126
VIndex = ML.Index;

0 commit comments

Comments
 (0)