Skip to content
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ Android Support
Windows Support
^^^^^^^^^^^^^^^

- Clang now supports MSVC vector deleting destructors (GH19772).

LoongArch Support
^^^^^^^^^^^^^^^^^

Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/AST/VTableBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class VTableComponent {

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

GlobalDecl getGlobalDecl() const {
GlobalDecl getGlobalDecl(bool HasVectorDeletingDtors) const {
assert(isUsedFunctionPointerKind() &&
"GlobalDecl can be created only from virtual function");

Expand All @@ -161,7 +161,9 @@ class VTableComponent {
case CK_CompleteDtorPointer:
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Complete);
case CK_DeletingDtorPointer:
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Deleting);
return GlobalDecl(DtorDecl, (HasVectorDeletingDtors)
? CXXDtorType::Dtor_VectorDeleting
: CXXDtorType::Dtor_Deleting);
case CK_VCallOffset:
case CK_VBaseOffset:
case CK_OffsetToTop:
Expand Down
9 changes: 5 additions & 4 deletions clang/include/clang/Basic/ABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ enum CXXCtorType {

/// C++ destructor types.
enum CXXDtorType {
Dtor_Deleting, ///< Deleting dtor
Dtor_Complete, ///< Complete object dtor
Dtor_Base, ///< Base object dtor
Dtor_Comdat ///< The COMDAT used for dtors
Dtor_Deleting, ///< Deleting dtor
Dtor_Complete, ///< Complete object dtor
Dtor_Base, ///< Base object dtor
Dtor_Comdat, ///< The COMDAT used for dtors
Dtor_VectorDeleting ///< Vector deleting dtor
};

} // end namespace clang
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6000,6 +6000,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");
}
}

Expand Down
22 changes: 13 additions & 9 deletions clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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";
Expand Down
19 changes: 13 additions & 6 deletions clang/lib/AST/VTableBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -2657,7 +2657,11 @@ class VFTableBuilder {
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;
}
Expand Down Expand Up @@ -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]";
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();

Expand Down
37 changes: 36 additions & 1 deletion clang/lib/CodeGen/CGCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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>(
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/CodeGen/CGCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CodeGen/CGCXXABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ class CGCXXABI {
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,
Expand Down Expand Up @@ -575,6 +576,12 @@ class CGCXXABI {
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);

Expand Down
77 changes: 73 additions & 4 deletions clang/lib/CodeGen/CGClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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()) &&
Expand Down Expand Up @@ -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() {}
Expand All @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2104,7 +2104,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;
Expand Down
Loading