[MS][clang] Revert vector deleting destructors support (#135611)
Finding operator delete[] is still problematic, without it the extension is a security hazard, so reverting until the problem with operator delete[] is figured out. This reverts the following PRs: Reland [MS][clang] Add support for vector deleting destructors (llvm#133451) [MS][clang] Make sure vector deleting dtor calls correct operator delete (llvm#133950) [MS][clang] Fix crash on deletion of array of pointers (llvm#134088) [clang] Do not diagnose unused deleted operator delete[] (llvm#134357) [MS][clang] Error about ambiguous operator delete[] only when required (llvm#135041)
This commit is contained in:
parent
fe54d1afcc
commit
88d0b0835d
@ -530,7 +530,6 @@ Windows Support
|
||||
- Clang now can process the `i128` and `ui128` integeral suffixes when MSVC
|
||||
extensions are enabled. This allows for properly processing ``intsafe.h`` in
|
||||
the Windows SDK.
|
||||
- Clang now supports MSVC vector deleting destructors (GH19772).
|
||||
|
||||
LoongArch Support
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
@ -2852,7 +2852,6 @@ class CXXDestructorDecl : public CXXMethodDecl {
|
||||
// FIXME: Don't allocate storage for these except in the first declaration
|
||||
// of a virtual destructor.
|
||||
FunctionDecl *OperatorDelete = nullptr;
|
||||
FunctionDecl *OperatorArrayDelete = nullptr;
|
||||
Expr *OperatorDeleteThisArg = nullptr;
|
||||
|
||||
CXXDestructorDecl(ASTContext &C, CXXRecordDecl *RD, SourceLocation StartLoc,
|
||||
@ -2878,16 +2877,11 @@ public:
|
||||
static CXXDestructorDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
|
||||
|
||||
void setOperatorDelete(FunctionDecl *OD, Expr *ThisArg);
|
||||
void setOperatorArrayDelete(FunctionDecl *OD);
|
||||
|
||||
const FunctionDecl *getOperatorDelete() const {
|
||||
return getCanonicalDecl()->OperatorDelete;
|
||||
}
|
||||
|
||||
const FunctionDecl *getArrayOperatorDelete() const {
|
||||
return getCanonicalDecl()->OperatorArrayDelete;
|
||||
}
|
||||
|
||||
Expr *getOperatorDeleteThisArg() const {
|
||||
return getCanonicalDecl()->OperatorDeleteThisArg;
|
||||
}
|
||||
|
@ -150,7 +150,7 @@ public:
|
||||
|
||||
bool isRTTIKind() const { return isRTTIKind(getKind()); }
|
||||
|
||||
GlobalDecl getGlobalDecl(bool HasVectorDeletingDtors) const {
|
||||
GlobalDecl getGlobalDecl() const {
|
||||
assert(isUsedFunctionPointerKind() &&
|
||||
"GlobalDecl can be created only from virtual function");
|
||||
|
||||
@ -161,9 +161,7 @@ public:
|
||||
case CK_CompleteDtorPointer:
|
||||
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Complete);
|
||||
case CK_DeletingDtorPointer:
|
||||
return GlobalDecl(DtorDecl, (HasVectorDeletingDtors)
|
||||
? CXXDtorType::Dtor_VectorDeleting
|
||||
: CXXDtorType::Dtor_Deleting);
|
||||
return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Deleting);
|
||||
case CK_VCallOffset:
|
||||
case CK_VBaseOffset:
|
||||
case CK_OffsetToTop:
|
||||
|
@ -31,11 +31,10 @@ 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_VectorDeleting ///< Vector deleting dtor
|
||||
Dtor_Deleting, ///< Deleting dtor
|
||||
Dtor_Complete, ///< Complete object dtor
|
||||
Dtor_Base, ///< Base object dtor
|
||||
Dtor_Comdat ///< The COMDAT used for dtors
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
@ -8366,7 +8366,6 @@ public:
|
||||
DeclarationName Name);
|
||||
FunctionDecl *FindDeallocationFunctionForDestructor(SourceLocation StartLoc,
|
||||
CXXRecordDecl *RD,
|
||||
DeclarationName Name,
|
||||
bool Diagnose = true);
|
||||
|
||||
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
|
||||
|
@ -3064,12 +3064,6 @@ void CXXDestructorDecl::setOperatorDelete(FunctionDecl *OD, Expr *ThisArg) {
|
||||
}
|
||||
}
|
||||
|
||||
void CXXDestructorDecl::setOperatorArrayDelete(FunctionDecl *OD) {
|
||||
auto *First = cast<CXXDestructorDecl>(getFirstDecl());
|
||||
if (OD && !First->OperatorArrayDelete)
|
||||
First->OperatorArrayDelete = OD;
|
||||
}
|
||||
|
||||
bool CXXDestructorDecl::isCalledByDelete(const FunctionDecl *OpDel) const {
|
||||
// C++20 [expr.delete]p6: If the value of the operand of the delete-
|
||||
// expression is not a null pointer value and the selected deallocation
|
||||
|
@ -71,9 +71,6 @@ const CXXRecordDecl *Expr::getBestDynamicClassType() const {
|
||||
if (const PointerType *PTy = DerivedType->getAs<PointerType>())
|
||||
DerivedType = PTy->getPointeeType();
|
||||
|
||||
while (const ArrayType *ATy = DerivedType->getAsArrayTypeUnsafe())
|
||||
DerivedType = ATy->getElementType();
|
||||
|
||||
if (DerivedType->isDependentType())
|
||||
return nullptr;
|
||||
|
||||
|
@ -6047,8 +6047,6 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
|
||||
case Dtor_Comdat:
|
||||
Out << "D5";
|
||||
break;
|
||||
case Dtor_VectorDeleting:
|
||||
llvm_unreachable("Itanium ABI does not use vector deleting dtors");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1490,9 +1490,8 @@ void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
|
||||
// <operator-name> ::= ?_G # scalar deleting destructor
|
||||
case Dtor_Deleting: Out << "?_G"; return;
|
||||
// <operator-name> ::= ?_E # vector deleting destructor
|
||||
case Dtor_VectorDeleting:
|
||||
Out << "?_E";
|
||||
return;
|
||||
// FIXME: Add a vector deleting dtor type. It goes in the vtable, so we need
|
||||
// it.
|
||||
case Dtor_Comdat:
|
||||
llvm_unreachable("not expecting a COMDAT");
|
||||
}
|
||||
@ -2893,12 +2892,9 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
|
||||
// ::= @ # structors (they have no declared return type)
|
||||
if (IsStructor) {
|
||||
if (isa<CXXDestructorDecl>(D) && isStructorDecl(D)) {
|
||||
// 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) {
|
||||
// The scalar deleting destructor takes an extra int argument which is not
|
||||
// reflected in the AST.
|
||||
if (StructorType == Dtor_Deleting) {
|
||||
Out << (PointersAre64Bit ? "PEAXI@Z" : "PAXI@Z");
|
||||
return;
|
||||
}
|
||||
@ -3871,10 +3867,10 @@ void MicrosoftMangleContextImpl::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
|
||||
const ThunkInfo &Thunk,
|
||||
bool /*ElideOverrideInfo*/,
|
||||
raw_ostream &Out) {
|
||||
// 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);
|
||||
// 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);
|
||||
msvc_hashing_ostream MHO(Out);
|
||||
MicrosoftCXXNameMangler Mangler(*this, MHO, DD, Type);
|
||||
Mangler.getStream() << "??_E";
|
||||
|
@ -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,11 +2657,7 @@ private:
|
||||
MethodVFTableLocation Loc(MI.VBTableIndex, WhichVFPtr.getVBaseWithVPtr(),
|
||||
WhichVFPtr.NonVirtualOffset, MI.VFTableIndex);
|
||||
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
|
||||
// In Microsoft ABI vftable always references vector deleting dtor.
|
||||
CXXDtorType DtorTy = Context.getTargetInfo().getCXXABI().isMicrosoft()
|
||||
? Dtor_VectorDeleting
|
||||
: Dtor_Deleting;
|
||||
MethodVFTableLocations[GlobalDecl(DD, DtorTy)] = Loc;
|
||||
MethodVFTableLocations[GlobalDecl(DD, Dtor_Deleting)] = Loc;
|
||||
} else {
|
||||
MethodVFTableLocations[MD] = Loc;
|
||||
}
|
||||
@ -3291,10 +3287,7 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) {
|
||||
const CXXDestructorDecl *DD = Component.getDestructorDecl();
|
||||
|
||||
DD->printQualifiedName(Out);
|
||||
if (Context.getTargetInfo().getCXXABI().isMicrosoft())
|
||||
Out << "() [vector deleting]";
|
||||
else
|
||||
Out << "() [scalar deleting]";
|
||||
Out << "() [scalar deleting]";
|
||||
|
||||
if (DD->isPureVirtual())
|
||||
Out << " [pure]";
|
||||
@ -3764,7 +3757,7 @@ void MicrosoftVTableContext::dumpMethodLocations(
|
||||
PredefinedIdentKind::PrettyFunctionNoVirtual, MD);
|
||||
|
||||
if (isa<CXXDestructorDecl>(MD)) {
|
||||
IndicesMap[I.second] = MethodName + " [vector deleting]";
|
||||
IndicesMap[I.second] = MethodName + " [scalar deleting]";
|
||||
} else {
|
||||
IndicesMap[I.second] = MethodName;
|
||||
}
|
||||
@ -3880,7 +3873,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_VectorDeleting);
|
||||
assert(GD.getDtorType() == Dtor_Deleting);
|
||||
|
||||
GD = GD.getCanonicalDecl();
|
||||
|
||||
|
@ -175,6 +175,7 @@ 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());
|
||||
@ -200,42 +201,6 @@ 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>(
|
||||
|
@ -271,20 +271,6 @@ 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) {
|
||||
|
@ -275,7 +275,6 @@ 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,
|
||||
@ -576,12 +575,6 @@ 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);
|
||||
|
||||
|
@ -1429,70 +1429,6 @@ 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->getArrayOperatorDelete(), 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());
|
||||
@ -1522,9 +1458,7 @@ 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 || DtorType == Dtor_VectorDeleting) {
|
||||
if (CXXStructorImplicitParamValue && DtorType == Dtor_VectorDeleting)
|
||||
EmitConditionalArrayDtorCall(Dtor, *this, CXXStructorImplicitParamValue);
|
||||
if (DtorType == Dtor_Deleting) {
|
||||
RunCleanupsScope DtorEpilogue(*this);
|
||||
EnterDtorCleanups(Dtor, Dtor_Deleting);
|
||||
if (HaveInsertPoint()) {
|
||||
@ -1553,8 +1487,6 @@ 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()) &&
|
||||
@ -1637,6 +1569,7 @@ namespace {
|
||||
return CGF.EmitScalarExpr(ThisArg);
|
||||
return CGF.LoadCXXThis();
|
||||
}
|
||||
|
||||
/// Call the operator delete associated with the current destructor.
|
||||
struct CallDtorDelete final : EHScopeStack::Cleanup {
|
||||
CallDtorDelete() {}
|
||||
@ -1655,10 +1588,8 @@ namespace {
|
||||
bool ReturnAfterDelete) {
|
||||
llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete");
|
||||
llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue");
|
||||
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);
|
||||
llvm::Value *ShouldCallDelete
|
||||
= CGF.Builder.CreateIsNull(ShouldDeleteCondition);
|
||||
CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB);
|
||||
|
||||
CGF.EmitBlock(callDeleteBB);
|
||||
|
@ -2119,8 +2119,7 @@ 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_VectorDeleting) : GlobalDecl(Method);
|
||||
GlobalDecl GD = DD ? GlobalDecl(DD, Dtor_Deleting) : GlobalDecl(Method);
|
||||
MethodVFTableLocation ML =
|
||||
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD);
|
||||
VIndex = ML.Index;
|
||||
|
@ -1209,8 +1209,6 @@ void CodeGenFunction::EmitNewArrayInitializer(
|
||||
EmitCXXAggrConstructorCall(Ctor, NumElements, CurPtr, CCE,
|
||||
/*NewPointerIsChecked*/true,
|
||||
CCE->requiresZeroInitialization());
|
||||
if (CGM.getCXXABI().hasVectorDeletingDtors())
|
||||
CGM.requireVectorDestructorDefinition(Ctor->getParent());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1957,8 +1955,10 @@ 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
|
||||
@ -2174,40 +2174,6 @@ 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);
|
||||
|
@ -770,8 +770,7 @@ void CodeGenVTables::addVTableComponent(ConstantArrayBuilder &builder,
|
||||
case VTableComponent::CK_FunctionPointer:
|
||||
case VTableComponent::CK_CompleteDtorPointer:
|
||||
case VTableComponent::CK_DeletingDtorPointer: {
|
||||
GlobalDecl GD =
|
||||
component.getGlobalDecl(CGM.getCXXABI().hasVectorDeletingDtors());
|
||||
GlobalDecl GD = component.getGlobalDecl();
|
||||
|
||||
const bool IsThunk =
|
||||
nextVTableThunkIndex < layout.vtable_thunks().size() &&
|
||||
|
@ -7948,51 +7948,3 @@ 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);
|
||||
}
|
||||
}
|
||||
|
@ -528,9 +528,6 @@ 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;
|
||||
|
||||
@ -1545,7 +1542,6 @@ public:
|
||||
void EmitGlobal(GlobalDecl D);
|
||||
|
||||
bool TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D);
|
||||
void EmitDefinitionAsAlias(GlobalDecl Alias, GlobalDecl Target);
|
||||
|
||||
llvm::GlobalValue *GetGlobalValue(StringRef Ref);
|
||||
|
||||
@ -1813,8 +1809,6 @@ 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;
|
||||
|
@ -91,8 +91,6 @@ 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");
|
||||
}
|
||||
@ -181,7 +179,6 @@ 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,
|
||||
@ -451,8 +448,7 @@ public:
|
||||
if (!IsInlined)
|
||||
continue;
|
||||
|
||||
StringRef Name = CGM.getMangledName(
|
||||
VtableComponent.getGlobalDecl(/*HasVectorDeletingDtors=*/false));
|
||||
StringRef Name = CGM.getMangledName(VtableComponent.getGlobalDecl());
|
||||
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
|
||||
|
@ -71,8 +71,8 @@ public:
|
||||
switch (GD.getDtorType()) {
|
||||
case Dtor_Complete:
|
||||
case Dtor_Deleting:
|
||||
case Dtor_VectorDeleting:
|
||||
return true;
|
||||
|
||||
case Dtor_Base:
|
||||
return false;
|
||||
|
||||
@ -146,7 +146,6 @@ 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,
|
||||
@ -262,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_VectorDeleting);
|
||||
LookupGD = GlobalDecl(DD, Dtor_Deleting);
|
||||
}
|
||||
MethodVFTableLocation ML =
|
||||
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
|
||||
@ -344,8 +343,8 @@ public:
|
||||
|
||||
void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD,
|
||||
CallArgList &CallArgs) override {
|
||||
assert(GD.getDtorType() == Dtor_VectorDeleting &&
|
||||
"Only vector deleting destructor thunks are available in this ABI");
|
||||
assert(GD.getDtorType() == Dtor_Deleting &&
|
||||
"Only deleting destructor thunks are available in this ABI");
|
||||
CallArgs.add(RValue::get(getStructorImplicitParamValue(CGF)),
|
||||
getContext().IntTy);
|
||||
}
|
||||
@ -1092,8 +1091,7 @@ bool MicrosoftCXXABI::HasThisReturn(GlobalDecl GD) const {
|
||||
|
||||
static bool isDeletingDtor(GlobalDecl GD) {
|
||||
return isa<CXXDestructorDecl>(GD.getDecl()) &&
|
||||
(GD.getDtorType() == Dtor_Deleting ||
|
||||
GD.getDtorType() == Dtor_VectorDeleting);
|
||||
GD.getDtorType() == Dtor_Deleting;
|
||||
}
|
||||
|
||||
bool MicrosoftCXXABI::hasMostDerivedReturn(GlobalDecl GD) const {
|
||||
@ -1346,8 +1344,7 @@ MicrosoftCXXABI::buildStructorSignature(GlobalDecl GD,
|
||||
AddedStructorArgCounts Added;
|
||||
// TODO: 'for base' flag
|
||||
if (isa<CXXDestructorDecl>(GD.getDecl()) &&
|
||||
(GD.getDtorType() == Dtor_Deleting ||
|
||||
GD.getDtorType() == Dtor_VectorDeleting)) {
|
||||
GD.getDtorType() == Dtor_Deleting) {
|
||||
// The scalar deleting destructor takes an implicit int parameter.
|
||||
ArgTys.push_back(getContext().IntTy);
|
||||
++Added.Suffix;
|
||||
@ -1379,7 +1376,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 || DT == Dtor_VectorDeleting) {
|
||||
if (DT == Dtor_Deleting) {
|
||||
GV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
|
||||
} else {
|
||||
const NamedDecl *ND = Dtor;
|
||||
@ -1413,12 +1410,6 @@ 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");
|
||||
}
|
||||
@ -1450,7 +1441,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_VectorDeleting);
|
||||
GD = GlobalDecl(DD, Dtor_Deleting);
|
||||
}
|
||||
|
||||
MethodVFTableLocation ML =
|
||||
@ -1499,7 +1490,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_VectorDeleting);
|
||||
LookupGD = GlobalDecl(DD, Dtor_Deleting);
|
||||
}
|
||||
MethodVFTableLocation ML =
|
||||
CGM.getMicrosoftVTableContext().getMethodVFTableLocation(LookupGD);
|
||||
@ -2012,20 +2003,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_VectorDeleting || DtorType == Dtor_Complete ||
|
||||
DtorType == Dtor_Deleting);
|
||||
assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
|
||||
|
||||
// We have only one destructor in the vftable but can get both behaviors
|
||||
// by passing an implicit int parameter.
|
||||
GlobalDecl GD(Dtor, Dtor_VectorDeleting);
|
||||
GlobalDecl GD(Dtor, Dtor_Deleting);
|
||||
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();
|
||||
uint32_t Flags = ((D && D->isArrayForm()) << 1) | (DtorType == Dtor_Deleting);
|
||||
llvm::Value *ImplicitParam = CGF.Builder.getInt32(Flags);
|
||||
llvm::Value *ImplicitParam = llvm::ConstantInt::get(
|
||||
llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()),
|
||||
DtorType == Dtor_Deleting);
|
||||
|
||||
QualType ThisTy;
|
||||
if (CE) {
|
||||
@ -2034,9 +2025,6 @@ llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
|
||||
ThisTy = D->getDestroyedType();
|
||||
}
|
||||
|
||||
while (const ArrayType *ATy = Context.getAsArrayType(ThisTy))
|
||||
ThisTy = ATy->getElementType();
|
||||
|
||||
This = adjustThisArgumentForVirtualFunctionCall(CGF, GD, This, true);
|
||||
RValue RV =
|
||||
CGF.EmitCXXDestructorCall(GD, Callee, This.emitRawPointer(CGF), ThisTy,
|
||||
@ -4070,18 +4058,6 @@ 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()));
|
||||
|
@ -11058,11 +11058,9 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
|
||||
else
|
||||
Loc = RD->getLocation();
|
||||
|
||||
DeclarationName Name =
|
||||
Context.DeclarationNames.getCXXOperatorName(OO_Delete);
|
||||
// If we have a virtual destructor, look up the deallocation function
|
||||
if (FunctionDecl *OperatorDelete =
|
||||
FindDeallocationFunctionForDestructor(Loc, RD, Name)) {
|
||||
FindDeallocationFunctionForDestructor(Loc, RD)) {
|
||||
Expr *ThisArg = nullptr;
|
||||
|
||||
// If the notional 'delete this' expression requires a non-trivial
|
||||
@ -11097,23 +11095,6 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) {
|
||||
DiagnoseUseOfDecl(OperatorDelete, Loc);
|
||||
MarkFunctionReferenced(Loc, OperatorDelete);
|
||||
Destructor->setOperatorDelete(OperatorDelete, ThisArg);
|
||||
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
|
||||
// Lookup delete[] too in case we have to emit a vector deleting dtor;
|
||||
DeclarationName VDeleteName =
|
||||
Context.DeclarationNames.getCXXOperatorName(OO_Array_Delete);
|
||||
// Diagnose if there is no available operator delete[] found and the
|
||||
// destructor is exported. Vector deleting dtor body emission requires
|
||||
// operator delete[] to be present. Whenever the destructor is exported,
|
||||
// we just always emit vector deleting dtor body, because we don't know
|
||||
// if new[] will be used with the type outside of the library. Otherwise
|
||||
// when the dtor is not exported then new[]/delete[] in the TU will make
|
||||
// sure the operator is referenced and its uses diagnosed.
|
||||
bool Diagnose =
|
||||
Destructor->hasAttr<DLLExportAttr>() && Destructor->isDefined();
|
||||
FunctionDecl *ArrOperatorDelete = FindDeallocationFunctionForDestructor(
|
||||
Loc, RD, VDeleteName, Diagnose);
|
||||
Destructor->setOperatorArrayDelete(ArrOperatorDelete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3016,19 +3016,8 @@ bool Sema::FindAllocationFunctions(
|
||||
return true;
|
||||
}
|
||||
|
||||
// new[] will force emission of vector deleting dtor which needs delete[].
|
||||
bool MaybeVectorDeletingDtor = false;
|
||||
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
|
||||
if (AllocElemType->isRecordType() && IsArray) {
|
||||
auto *RD =
|
||||
cast<CXXRecordDecl>(AllocElemType->castAs<RecordType>()->getDecl());
|
||||
CXXDestructorDecl *DD = RD->getDestructor();
|
||||
MaybeVectorDeletingDtor = DD && DD->isVirtual() && !DD->isDeleted();
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need an operator delete if we're running under -fno-exceptions.
|
||||
if (!getLangOpts().Exceptions && !MaybeVectorDeletingDtor) {
|
||||
if (!getLangOpts().Exceptions) {
|
||||
OperatorDelete = nullptr;
|
||||
return false;
|
||||
}
|
||||
@ -3587,8 +3576,8 @@ Sema::FindUsualDeallocationFunction(SourceLocation StartLoc,
|
||||
|
||||
FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
|
||||
CXXRecordDecl *RD,
|
||||
DeclarationName Name,
|
||||
bool Diagnose) {
|
||||
DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete);
|
||||
|
||||
FunctionDecl *OperatorDelete = nullptr;
|
||||
QualType DeallocType = Context.getRecordType(RD);
|
||||
@ -3602,7 +3591,8 @@ FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc,
|
||||
if (OperatorDelete)
|
||||
return OperatorDelete;
|
||||
|
||||
// If there's no class-specific operator delete, look up the global delete.
|
||||
// If there's no class-specific operator delete, look up the global
|
||||
// non-array delete.
|
||||
QualType RecordType = Context.getRecordType(RD);
|
||||
IDP.PassAlignment =
|
||||
alignedAllocationModeFromBool(hasNewExtendedAlignment(*this, RecordType));
|
||||
@ -3619,11 +3609,8 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD,
|
||||
// Try to find operator delete/operator delete[] in class scope.
|
||||
LookupQualifiedName(Found, RD);
|
||||
|
||||
if (Found.isAmbiguous()) {
|
||||
if (!Diagnose)
|
||||
Found.suppressDiagnostics();
|
||||
if (Found.isAmbiguous())
|
||||
return true;
|
||||
}
|
||||
|
||||
Found.suppressDiagnostics();
|
||||
|
||||
|
@ -16,7 +16,7 @@ struct AB: A, B {
|
||||
template struct AB<int>;
|
||||
|
||||
// CHECK: define {{.*}}@"??_E?$AB@H@@W3AEPAXI@Z"({{.*}} !dbg [[THUNK_VEC_DEL_DTOR:![0-9]*]]
|
||||
// CHECK: call {{.*}}@"??_E?$AB@H@@UAEPAXI@Z"({{.*}}) #{{[0-9]*}}, !dbg [[THUNK_LOC:![0-9]*]]
|
||||
// CHECK: call {{.*}}@"??_G?$AB@H@@UAEPAXI@Z"({{.*}}) #{{[0-9]*}}, !dbg [[THUNK_LOC:![0-9]*]]
|
||||
// CHECK: define
|
||||
|
||||
// CHECK: [[THUNK_VEC_DEL_DTOR]] = distinct !DISubprogram
|
||||
|
@ -631,7 +631,7 @@ struct __declspec(dllexport) Y {
|
||||
|
||||
struct __declspec(dllexport) Z { virtual ~Z() {} };
|
||||
// The scalar deleting dtor does not get exported:
|
||||
// M32-DAG: define weak dso_local x86_thiscallcc ptr @"??_EZ@@UAEPAXI@Z"
|
||||
// M32-DAG: define linkonce_odr dso_local x86_thiscallcc ptr @"??_GZ@@UAEPAXI@Z"
|
||||
|
||||
|
||||
// The user-defined dtor does get exported:
|
||||
|
@ -4,7 +4,7 @@
|
||||
// own copy the vftable when emitting the available externally constructor.
|
||||
|
||||
// CHECK: @"??_7?$Foo@H@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [
|
||||
// CHECK-SAME: ptr @"??_E?$Foo@H@@UEAAPEAXI@Z"
|
||||
// CHECK-SAME: ptr @"??_G?$Foo@H@@UEAAPEAXI@Z"
|
||||
// CHECK-SAME: ] }, comdat
|
||||
|
||||
// CHECK-LABEL: define dso_local noundef ptr @"?f@@YAPEAU?$Foo@H@@XZ"()
|
||||
|
@ -52,8 +52,7 @@ struct C {
|
||||
// DTORS: store ptr %{{.*}}, ptr %[[RETVAL:retval]]
|
||||
// DTORS: %[[SHOULD_DELETE_VALUE:[0-9a-z._]+]] = load i32, ptr %[[SHOULD_DELETE_VAR]]
|
||||
// DTORS: call x86_thiscallcc void @"??1C@basic@@UAE@XZ"(ptr {{[^,]*}} %[[THIS:[0-9a-z]+]])
|
||||
// DTORS-NEXT: %[[AND:[0-9]+]] = and i32 %[[SHOULD_DELETE_VALUE]], 1
|
||||
// DTORS-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[AND]], 0
|
||||
// DTORS-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[SHOULD_DELETE_VALUE]], 0
|
||||
// DTORS-NEXT: br i1 %[[CONDITION]], label %[[CONTINUE_LABEL:[0-9a-z._]+]], label %[[CALL_DELETE_LABEL:[0-9a-z._]+]]
|
||||
//
|
||||
// DTORS: [[CALL_DELETE_LABEL]]
|
||||
@ -167,7 +166,7 @@ void foo() {
|
||||
// DTORS2-LABEL: define linkonce_odr dso_local x86_thiscallcc ptr @"??_EC@dtor_in_second_nvbase@@W3AEPAXI@Z"(ptr %this, i32 %should_call_delete)
|
||||
// Do an adjustment from B* to C*.
|
||||
// DTORS2: getelementptr i8, ptr %{{.*}}, i32 -4
|
||||
// DTORS2: %[[CALL:.*]] = tail call x86_thiscallcc ptr @"??_EC@dtor_in_second_nvbase@@UAEPAXI@Z"
|
||||
// DTORS2: %[[CALL:.*]] = tail call x86_thiscallcc ptr @"??_GC@dtor_in_second_nvbase@@UAEPAXI@Z"
|
||||
// DTORS2: ret ptr %[[CALL]]
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,8 @@ C::C() {} // Emits vftable and forces thunk generation.
|
||||
|
||||
// CODEGEN-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_EC@@W3AEPAXI@Z"(ptr noundef %this, i32 noundef %should_call_delete) {{.*}} comdat
|
||||
// CODEGEN: getelementptr i8, ptr {{.*}}, i32 -4
|
||||
// CODEGEN: call x86_thiscallcc noundef ptr @"??_EC@@UAEPAXI@Z"
|
||||
// FIXME: should actually call _EC, not _GC.
|
||||
// CODEGEN: call x86_thiscallcc noundef ptr @"??_GC@@UAEPAXI@Z"
|
||||
// CODEGEN: ret
|
||||
|
||||
// CODEGEN-LABEL: define linkonce_odr dso_local x86_thiscallcc void @"?public_f@C@@W3AEXXZ"(ptr
|
||||
|
@ -8,38 +8,38 @@ struct S {
|
||||
virtual ~S();
|
||||
} s;
|
||||
|
||||
// RTTI-DAG: [[VTABLE_S:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4S@@6B@", ptr @"??_ES@@UAEPAXI@Z"] }, comdat($"??_7S@@6B@")
|
||||
// RTTI-DAG: [[VTABLE_S:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4S@@6B@", ptr @"??_GS@@UAEPAXI@Z"] }, comdat($"??_7S@@6B@")
|
||||
// RTTI-DAG: @"??_7S@@6B@" = unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_S]], i32 0, i32 0, i32 1)
|
||||
|
||||
// NO-RTTI-DAG: @"??_7S@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_ES@@UAEPAXI@Z"] }
|
||||
// NO-RTTI-DAG: @"??_7S@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GS@@UAEPAXI@Z"] }
|
||||
|
||||
struct __declspec(dllimport) U {
|
||||
virtual ~U();
|
||||
} u;
|
||||
|
||||
// RTTI-DAG: [[VTABLE_U:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4U@@6B@", ptr @"??_EU@@UAEPAXI@Z"] }
|
||||
// RTTI-DAG: [[VTABLE_U:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4U@@6B@", ptr @"??_GU@@UAEPAXI@Z"] }
|
||||
// RTTI-DAG: @"??_SU@@6B@" = unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_U]], i32 0, i32 0, i32 1)
|
||||
|
||||
// NO-RTTI-DAG: @"??_SU@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_EU@@UAEPAXI@Z"] }
|
||||
// NO-RTTI-DAG: @"??_SU@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GU@@UAEPAXI@Z"] }
|
||||
|
||||
struct __declspec(dllexport) V {
|
||||
virtual ~V();
|
||||
} v;
|
||||
|
||||
// RTTI-DAG: [[VTABLE_V:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4V@@6B@", ptr @"??_EV@@UAEPAXI@Z"] }, comdat($"??_7V@@6B@")
|
||||
// RTTI-DAG: [[VTABLE_V:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4V@@6B@", ptr @"??_GV@@UAEPAXI@Z"] }, comdat($"??_7V@@6B@")
|
||||
// RTTI-DAG: @"??_7V@@6B@" = dllexport unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_V]], i32 0, i32 0, i32 1)
|
||||
|
||||
// NO-RTTI-DAG: @"??_7V@@6B@" = weak_odr dllexport unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_EV@@UAEPAXI@Z"] }
|
||||
// NO-RTTI-DAG: @"??_7V@@6B@" = weak_odr dllexport unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GV@@UAEPAXI@Z"] }
|
||||
|
||||
namespace {
|
||||
struct W {
|
||||
virtual ~W() {}
|
||||
} w;
|
||||
}
|
||||
// RTTI-DAG: [[VTABLE_W:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4W@?A0x{{[^@]*}}@@6B@", ptr @"??_EW@?A0x{{[^@]*}}@@UAEPAXI@Z"] }
|
||||
// RTTI-DAG: [[VTABLE_W:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4W@?A0x{{[^@]*}}@@6B@", ptr @"??_GW@?A0x{{[^@]*}}@@UAEPAXI@Z"] }
|
||||
// RTTI-DAG: @"??_7W@?A0x{{[^@]*}}@@6B@" = internal unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_W]], i32 0, i32 0, i32 1)
|
||||
|
||||
// NO-RTTI-DAG: @"??_7W@?A0x{{[^@]*}}@@6B@" = internal unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_EW@?A0x{{[^@]*}}@@UAEPAXI@Z"] }
|
||||
// NO-RTTI-DAG: @"??_7W@?A0x{{[^@]*}}@@6B@" = internal unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GW@?A0x{{[^@]*}}@@UAEPAXI@Z"] }
|
||||
|
||||
struct X {};
|
||||
template <class> struct Y : virtual X {
|
||||
@ -49,7 +49,7 @@ template <class> struct Y : virtual X {
|
||||
|
||||
extern template class Y<int>;
|
||||
template Y<int>::Y();
|
||||
// RTTI-DAG: [[VTABLE_Y:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4?$Y@H@@6B@", ptr @"??_E?$Y@H@@UAEPAXI@Z"] }, comdat($"??_7?$Y@H@@6B@")
|
||||
// RTTI-DAG: [[VTABLE_Y:@.*]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4?$Y@H@@6B@", ptr @"??_G?$Y@H@@UAEPAXI@Z"] }, comdat($"??_7?$Y@H@@6B@")
|
||||
// RTTI-DAG: @"??_7?$Y@H@@6B@" = unnamed_addr alias ptr, getelementptr inbounds ({ [2 x ptr] }, ptr [[VTABLE_Y]], i32 0, i32 0, i32 1)
|
||||
|
||||
// NO-RTTI-DAG: @"??_7?$Y@H@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_E?$Y@H@@UAEPAXI@Z"] }, comdat
|
||||
// NO-RTTI-DAG: @"??_7?$Y@H@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_G?$Y@H@@UAEPAXI@Z"] }, comdat
|
||||
|
@ -80,15 +80,6 @@ B::~B() {
|
||||
// CHECK2: call x86_thiscallcc void @"??1VBase@@UAE@XZ"(ptr {{[^,]*}} %[[VBASE_i8]])
|
||||
// CHECK2: ret
|
||||
|
||||
// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??0B@test2@@QAE@XZ"
|
||||
// CHECK2: (ptr {{[^,]*}} returned align 4 dereferenceable(4) %this, i32 noundef %is_most_derived)
|
||||
// CHECK2: call x86_thiscallcc noundef ptr @"??0A@test2@@QAE@XZ"(ptr {{[^,]*}} %{{.*}})
|
||||
// CHECK2: ret
|
||||
|
||||
// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_GD@pr36921@@UAEPAXI@Z"(
|
||||
// CHECK2: %[[THIS_RELOAD:.*]] = load ptr, ptr
|
||||
// CHECK2: %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_RELOAD]], i32 -4
|
||||
|
||||
// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_GB@@UAEPAXI@Z"
|
||||
// CHECK2: store ptr %{{.*}}, ptr %[[THIS_ADDR:.*]], align 4
|
||||
// CHECK2: %[[THIS_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_PARAM_i8:.*]], i32 -8
|
||||
@ -302,6 +293,11 @@ void callC() { C x; }
|
||||
// CHECK: call x86_thiscallcc noundef ptr @"??0A@test2@@QAE@XZ"(ptr {{[^,]*}} %{{.*}})
|
||||
// CHECK: ret
|
||||
|
||||
// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??0B@test2@@QAE@XZ"
|
||||
// CHECK2: (ptr {{[^,]*}} returned align 4 dereferenceable(4) %this, i32 noundef %is_most_derived)
|
||||
// CHECK2: call x86_thiscallcc noundef ptr @"??0A@test2@@QAE@XZ"(ptr {{[^,]*}} %{{.*}})
|
||||
// CHECK2: ret
|
||||
|
||||
}
|
||||
|
||||
namespace test3 {
|
||||
@ -484,6 +480,9 @@ struct B {
|
||||
struct C : virtual B {};
|
||||
struct D : virtual A, C {};
|
||||
D d;
|
||||
// CHECK2-LABEL: define linkonce_odr dso_local x86_thiscallcc noundef ptr @"??_GD@pr36921@@UAEPAXI@Z"(
|
||||
// CHECK2: %[[THIS_RELOAD:.*]] = load ptr, ptr
|
||||
// CHECK2: %[[THIS_ADJ_i8:.*]] = getelementptr inbounds i8, ptr %[[THIS_RELOAD]], i32 -4
|
||||
}
|
||||
|
||||
namespace issue_60465 {
|
||||
|
@ -12,18 +12,18 @@ struct B {
|
||||
|
||||
struct C : A, B {
|
||||
// CHECK-LABEL: VFTable for 'A' in 'C' (2 entries).
|
||||
// CHECK-NEXT: 0 | C::~C() [vector deleting]
|
||||
// CHECK-NEXT: 0 | C::~C() [scalar deleting]
|
||||
// CHECK-NEXT: 1 | void A::z1()
|
||||
|
||||
// CHECK-LABEL: VFTable for 'B' in 'C' (1 entry).
|
||||
// CHECK-NEXT: 0 | C::~C() [vector deleting]
|
||||
// CHECK-NEXT: 0 | C::~C() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: -4 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'C::~C()' (1 entry).
|
||||
// CHECK-NEXT: 0 | [this adjustment: -4 non-virtual]
|
||||
|
||||
// CHECK-LABEL: VFTable indices for 'C' (1 entry).
|
||||
// CHECK-NEXT: 0 | C::~C() [vector deleting]
|
||||
// CHECK-NEXT: 0 | C::~C() [scalar deleting]
|
||||
virtual ~C();
|
||||
};
|
||||
|
||||
@ -41,7 +41,7 @@ struct E : D, B {
|
||||
// CHECK-NEXT: 0 | void D::z4()
|
||||
|
||||
// CHECK-LABEL: VFTable for 'B' in 'E' (1 entry).
|
||||
// CHECK-NEXT: 0 | E::~E() [vector deleting]
|
||||
// CHECK-NEXT: 0 | E::~E() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: -4 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'E::~E()' (1 entry).
|
||||
@ -49,7 +49,7 @@ struct E : D, B {
|
||||
|
||||
// CHECK-LABEL: VFTable indices for 'E' (1 entry).
|
||||
// CHECK-NEXT: -- accessible via vfptr at offset 4 --
|
||||
// CHECK-NEXT: 0 | E::~E() [vector deleting]
|
||||
// CHECK-NEXT: 0 | E::~E() [scalar deleting]
|
||||
};
|
||||
|
||||
void build_vftable(E *obj) { delete obj; }
|
||||
@ -61,7 +61,7 @@ struct F : D, B {
|
||||
// CHECK-NEXT: 0 | void D::z4()
|
||||
|
||||
// CHECK-LABEL: VFTable for 'B' in 'F' (1 entry).
|
||||
// CHECK-NEXT: 0 | F::~F() [vector deleting]
|
||||
// CHECK-NEXT: 0 | F::~F() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: -4 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'F::~F()' (1 entry).
|
||||
@ -69,7 +69,7 @@ struct F : D, B {
|
||||
|
||||
// CHECK-LABEL: VFTable indices for 'F' (1 entry).
|
||||
// CHECK-NEXT: -- accessible via vfptr at offset 4 --
|
||||
// CHECK-NEXT: 0 | F::~F() [vector deleting]
|
||||
// CHECK-NEXT: 0 | F::~F() [scalar deleting]
|
||||
};
|
||||
|
||||
void build_vftable(F *obj) { delete obj; }
|
||||
@ -79,7 +79,7 @@ struct G : F {
|
||||
// CHECK-NEXT: 0 | void D::z4()
|
||||
|
||||
// CHECK-LABEL: VFTable for 'B' in 'F' in 'G' (1 entry).
|
||||
// CHECK-NEXT: 0 | G::~G() [vector deleting]
|
||||
// CHECK-NEXT: 0 | G::~G() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: -4 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'G::~G()' (1 entry).
|
||||
@ -87,7 +87,7 @@ struct G : F {
|
||||
|
||||
// CHECK-LABEL: VFTable indices for 'G' (1 entry).
|
||||
// CHECK-NEXT: -- accessible via vfptr at offset 4 --
|
||||
// CHECK-NEXT: 0 | G::~G() [vector deleting]
|
||||
// CHECK-NEXT: 0 | G::~G() [scalar deleting]
|
||||
virtual ~G();
|
||||
};
|
||||
|
||||
|
@ -213,6 +213,6 @@ struct C : virtual B { C *f(); };
|
||||
C c;
|
||||
// VFTABLES-LABEL: VFTable indices for 'pr34302::C' (2 entries).
|
||||
// VFTABLES-NEXT: -- accessible via vbtable index 1, vfptr at offset 0 --
|
||||
// VFTABLES-NEXT: 0 | pr34302::C::~C() [vector deleting]
|
||||
// VFTABLES-NEXT: 0 | pr34302::C::~C() [scalar deleting]
|
||||
// VFTABLES-NEXT: 2 | C *pr34302::C::f()
|
||||
}
|
||||
|
@ -44,10 +44,10 @@ void use(B *obj) { obj->f(); }
|
||||
|
||||
struct C {
|
||||
// CHECK-LABEL: VFTable for 'C' (2 entries)
|
||||
// CHECK-NEXT: 0 | C::~C() [vector deleting]
|
||||
// CHECK-NEXT: 0 | C::~C() [scalar deleting]
|
||||
// CHECK-NEXT: 1 | void C::f()
|
||||
// CHECK-LABEL: VFTable indices for 'C' (2 entries).
|
||||
// CHECK-NEXT: 0 | C::~C() [vector deleting]
|
||||
// CHECK-NEXT: 0 | C::~C() [scalar deleting]
|
||||
// CHECK-NEXT: 1 | void C::f()
|
||||
|
||||
virtual ~C();
|
||||
@ -60,10 +60,10 @@ void use(C *obj) { obj->f(); }
|
||||
struct D {
|
||||
// CHECK-LABEL: VFTable for 'D' (2 entries)
|
||||
// CHECK-NEXT: 0 | void D::f()
|
||||
// CHECK-NEXT: 1 | D::~D() [vector deleting]
|
||||
// CHECK-NEXT: 1 | D::~D() [scalar deleting]
|
||||
// CHECK-LABEL: VFTable indices for 'D' (2 entries)
|
||||
// CHECK-NEXT: 0 | void D::f()
|
||||
// CHECK-NEXT: 1 | D::~D() [vector deleting]
|
||||
// CHECK-NEXT: 1 | D::~D() [scalar deleting]
|
||||
|
||||
virtual void f();
|
||||
virtual ~D();
|
||||
@ -77,10 +77,10 @@ struct E : A {
|
||||
// CHECK-NEXT: 0 | void A::f()
|
||||
// CHECK-NEXT: 1 | void A::g()
|
||||
// CHECK-NEXT: 2 | void A::h()
|
||||
// CHECK-NEXT: 3 | E::~E() [vector deleting]
|
||||
// CHECK-NEXT: 3 | E::~E() [scalar deleting]
|
||||
// CHECK-NEXT: 4 | void E::i()
|
||||
// CHECK-LABEL: VFTable indices for 'E' (2 entries).
|
||||
// CHECK-NEXT: 3 | E::~E() [vector deleting]
|
||||
// CHECK-NEXT: 3 | E::~E() [scalar deleting]
|
||||
// CHECK-NEXT: 4 | void E::i()
|
||||
|
||||
// ~E would be the key method, but it isn't used, and MS ABI has no key
|
||||
@ -98,10 +98,10 @@ struct F : A {
|
||||
// CHECK-NEXT: 1 | void A::g()
|
||||
// CHECK-NEXT: 2 | void A::h()
|
||||
// CHECK-NEXT: 3 | void F::i()
|
||||
// CHECK-NEXT: 4 | F::~F() [vector deleting]
|
||||
// CHECK-NEXT: 4 | F::~F() [scalar deleting]
|
||||
// CHECK-LABEL: VFTable indices for 'F' (2 entries).
|
||||
// CHECK-NEXT: 3 | void F::i()
|
||||
// CHECK-NEXT: 4 | F::~F() [vector deleting]
|
||||
// CHECK-NEXT: 4 | F::~F() [scalar deleting]
|
||||
|
||||
virtual void i();
|
||||
virtual ~F();
|
||||
@ -115,12 +115,12 @@ struct G : E {
|
||||
// CHECK-NEXT: 0 | void G::f()
|
||||
// CHECK-NEXT: 1 | void A::g()
|
||||
// CHECK-NEXT: 2 | void A::h()
|
||||
// CHECK-NEXT: 3 | G::~G() [vector deleting]
|
||||
// CHECK-NEXT: 3 | G::~G() [scalar deleting]
|
||||
// CHECK-NEXT: 4 | void E::i()
|
||||
// CHECK-NEXT: 5 | void G::j()
|
||||
// CHECK-LABEL: VFTable indices for 'G' (3 entries).
|
||||
// CHECK-NEXT: 0 | void G::f()
|
||||
// CHECK-NEXT: 3 | G::~G() [vector deleting]
|
||||
// CHECK-NEXT: 3 | G::~G() [scalar deleting]
|
||||
// CHECK-NEXT: 5 | void G::j()
|
||||
|
||||
virtual void f(); // overrides A::f()
|
||||
|
@ -57,7 +57,7 @@ struct A : virtual V1 {
|
||||
// CHECK-LABEL: VFTable for 'V1' in 'simple::A' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::A::f()
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
// CHECK-NEXT: 1 | simple::A::~A() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::A::~A() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'simple::A::~A()' (1 entry).
|
||||
@ -79,7 +79,7 @@ void use(A *obj) { obj->f(); }
|
||||
struct B : virtual V3 {
|
||||
// CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries).
|
||||
// CHECK-NEXT: 0 | void Z::g()
|
||||
// CHECK-NEXT: 1 | simple::B::~B() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
|
||||
@ -88,7 +88,7 @@ struct B : virtual V3 {
|
||||
// CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::B::f()
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
|
||||
// CHECK-NEXT: 1 | simple::B::~B() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
|
||||
@ -115,7 +115,7 @@ void use(B *obj) { obj->f(); }
|
||||
struct C : virtual V4 {
|
||||
// CHECK-LABEL: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries).
|
||||
// CHECK-NEXT: 0 | void Z::g()
|
||||
// CHECK-NEXT: 1 | simple::C::~C() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
|
||||
@ -124,7 +124,7 @@ struct C : virtual V4 {
|
||||
// CHECK-LABEL: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::C::f()
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
|
||||
// CHECK-NEXT: 1 | simple::C::~C() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
|
||||
@ -136,7 +136,7 @@ struct C : virtual V4 {
|
||||
// CHECK-LABEL: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::C::f()
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual]
|
||||
// CHECK-NEXT: 1 | simple::C::~C() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
|
||||
@ -162,7 +162,7 @@ class D : B {
|
||||
// CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'simple::D' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::B::f()
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -12, -4 non-virtual]
|
||||
// CHECK-NEXT: 1 | simple::D::~D() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::D::~D() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
|
||||
D();
|
||||
int z;
|
||||
@ -180,12 +180,12 @@ struct F : virtual E {
|
||||
// CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::F::g()
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
// CHECK-NEXT: 1 | simple::F::~F() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::F::~F() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::E::f()
|
||||
// CHECK-NEXT: 1 | simple::F::~F() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::F::~F() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
|
||||
|
||||
F();
|
||||
@ -202,12 +202,12 @@ struct G : F {
|
||||
// CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::F::g()
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]
|
||||
// CHECK-NEXT: 1 | simple::G::~G() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::G::~G() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::E::f()
|
||||
// CHECK-NEXT: 1 | simple::G::~G() [vector deleting]
|
||||
// CHECK-NEXT: 1 | simple::G::~G() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
|
||||
|
||||
G();
|
||||
@ -240,7 +240,7 @@ struct A : virtual simple::A {
|
||||
// CHECK-NEXT: 0 | void simple::A::f()
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
||||
// CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
||||
// CHECK-NEXT: 1 | extended::A::~A() [vector deleting]
|
||||
// CHECK-NEXT: 1 | extended::A::~A() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
|
||||
@ -265,7 +265,7 @@ struct B : virtual simple::A {
|
||||
|
||||
// CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).
|
||||
// ...
|
||||
// CHECK: 1 | extended::B::~B() [vector deleting]
|
||||
// CHECK: 1 | extended::B::~B() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
|
||||
@ -353,7 +353,7 @@ struct G : virtual simple::A {
|
||||
// CHECK-NEXT: 0 | void simple::A::f()
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
||||
// CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
||||
// CHECK-NEXT: 1 | extended::G::~G() [vector deleting]
|
||||
// CHECK-NEXT: 1 | extended::G::~G() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
|
||||
@ -374,7 +374,7 @@ void use(G *obj) { obj->g(); }
|
||||
struct H : Z, A {
|
||||
// CHECK-LABEL: VFTable for 'Z' in 'extended::H' (2 entries).
|
||||
// CHECK-NEXT: 0 | void Z::g()
|
||||
// CHECK-NEXT: 1 | extended::H::~H() [vector deleting]
|
||||
// CHECK-NEXT: 1 | extended::H::~H() [scalar deleting]
|
||||
|
||||
// CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries).
|
||||
// CHECK-NEXT: 0 | void simple::A::f()
|
||||
|
@ -492,7 +492,7 @@ struct X {
|
||||
|
||||
struct Y : virtual X {
|
||||
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::Y' (2 entries).
|
||||
// CHECK-NEXT: 0 | vdtors::Y::~Y() [vector deleting]
|
||||
// CHECK-NEXT: 0 | vdtors::Y::~Y() [scalar deleting]
|
||||
// CHECK-NEXT: 1 | void vdtors::X::zzz()
|
||||
|
||||
// CHECK-NOT: Thunks for 'vdtors::Y::~Y()'
|
||||
@ -515,7 +515,7 @@ struct U : virtual W {
|
||||
// CHECK-NEXT: 0 | void vdtors::Z::z()
|
||||
|
||||
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::U' (2 entries).
|
||||
// CHECK-NEXT: 0 | vdtors::U::~U() [vector deleting]
|
||||
// CHECK-NEXT: 0 | vdtors::U::~U() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: -4 non-virtual]
|
||||
// CHECK-NEXT: 1 | void vdtors::X::zzz()
|
||||
|
||||
@ -524,7 +524,7 @@ struct U : virtual W {
|
||||
|
||||
// CHECK-LABEL: VFTable indices for 'vdtors::U' (1 entry).
|
||||
// CHECK-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 --
|
||||
// CHECK-NEXT: 0 | vdtors::U::~U() [vector deleting]
|
||||
// CHECK-NEXT: 0 | vdtors::U::~U() [scalar deleting]
|
||||
virtual ~U();
|
||||
};
|
||||
|
||||
@ -536,7 +536,7 @@ struct V : virtual W {
|
||||
// CHECK-NEXT: 0 | void vdtors::Z::z()
|
||||
|
||||
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::V' (2 entries).
|
||||
// CHECK-NEXT: 0 | vdtors::V::~V() [vector deleting]
|
||||
// CHECK-NEXT: 0 | vdtors::V::~V() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: -4 non-virtual]
|
||||
// CHECK-NEXT: 1 | void vdtors::X::zzz()
|
||||
|
||||
@ -545,7 +545,7 @@ struct V : virtual W {
|
||||
|
||||
// CHECK-LABEL: VFTable indices for 'vdtors::V' (1 entry).
|
||||
// CHECK-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 --
|
||||
// CHECK-NEXT: 0 | vdtors::V::~V() [vector deleting]
|
||||
// CHECK-NEXT: 0 | vdtors::V::~V() [scalar deleting]
|
||||
};
|
||||
|
||||
V v;
|
||||
@ -557,7 +557,7 @@ struct T : virtual X {
|
||||
|
||||
struct P : T, Y {
|
||||
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::T' in 'vdtors::P' (2 entries).
|
||||
// CHECK-NEXT: 0 | vdtors::P::~P() [vector deleting]
|
||||
// CHECK-NEXT: 0 | vdtors::P::~P() [scalar deleting]
|
||||
// CHECK-NEXT: 1 | void vdtors::X::zzz()
|
||||
|
||||
// CHECK-NOT: Thunks for 'vdtors::P::~P()'
|
||||
@ -574,18 +574,18 @@ struct Q {
|
||||
// PR19172: Yet another diamond we miscompiled.
|
||||
struct R : virtual Q, X {
|
||||
// CHECK-LABEL: VFTable for 'vdtors::Q' in 'vdtors::R' (1 entry).
|
||||
// CHECK-NEXT: 0 | vdtors::R::~R() [vector deleting]
|
||||
// CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting]
|
||||
// CHECK-NEXT: [this adjustment: -8 non-virtual]
|
||||
|
||||
// CHECK-LABEL: Thunks for 'vdtors::R::~R()' (1 entry).
|
||||
// CHECK-NEXT: 0 | [this adjustment: -8 non-virtual]
|
||||
|
||||
// CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::R' (2 entries).
|
||||
// CHECK-NEXT: 0 | vdtors::R::~R() [vector deleting]
|
||||
// CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting]
|
||||
// CHECK-NEXT: 1 | void vdtors::X::zzz()
|
||||
|
||||
// CHECK-LABEL: VFTable indices for 'vdtors::R' (1 entry).
|
||||
// CHECK-NEXT: 0 | vdtors::R::~R() [vector deleting]
|
||||
// CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting]
|
||||
virtual ~R();
|
||||
};
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
// vftable shouldn't have RTTI data in it.
|
||||
// CHECK-NOT: @"??_R4S@@6B@"
|
||||
// CHECK: @"??_7S@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_ES@@UAEPAXI@Z"] }, comdat
|
||||
// CHECK: @"??_7S@@6B@" = linkonce_odr unnamed_addr constant { [1 x ptr] } { [1 x ptr] [ptr @"??_GS@@UAEPAXI@Z"] }, comdat
|
||||
|
||||
struct type_info;
|
||||
namespace std { using ::type_info; }
|
||||
|
@ -1,218 +0,0 @@
|
||||
// RUN: %clang_cc1 -emit-llvm %s -triple=x86_64-pc-windows-msvc -o - | FileCheck --check-prefixes=X64,CHECK %s
|
||||
// RUN: %clang_cc1 -emit-llvm %s -triple=i386-pc-windows-msvc -o - | FileCheck --check-prefixes=X86,CHECK %s
|
||||
|
||||
struct Bird {
|
||||
virtual ~Bird();
|
||||
};
|
||||
|
||||
struct Parrot : public Bird {
|
||||
// X64: @[[ParrotVtable:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Parrot@@6B@", ptr @"??_EParrot@@UEAAPEAXI@Z"] }, comdat($"??_7Parrot@@6B@")
|
||||
// X86: @[[ParrotVtable:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Parrot@@6B@", ptr @"??_EParrot@@UAEPAXI@Z"] }, comdat($"??_7Parrot@@6B@")
|
||||
// X64: @[[Bird:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Bird@@6B@", ptr @"??_EBird@@UEAAPEAXI@Z"] }, comdat($"??_7Bird@@6B@")
|
||||
// X86: @[[Bird:[0-9]+]] = private unnamed_addr constant { [2 x ptr] } { [2 x ptr] [ptr @"??_R4Bird@@6B@", ptr @"??_EBird@@UAEPAXI@Z"] }, comdat($"??_7Bird@@6B@")
|
||||
virtual ~Parrot() {}
|
||||
};
|
||||
|
||||
Bird::~Bird() {}
|
||||
|
||||
// For the weird bird we first emit scalar deleting destructor, then find out
|
||||
// that we need vector deleting destructor and remove the alias.
|
||||
struct JustAWeirdBird {
|
||||
virtual ~JustAWeirdBird() {}
|
||||
|
||||
bool doSmth(int n) {
|
||||
JustAWeirdBird *c = new JustAWeirdBird[n];
|
||||
|
||||
delete[] c;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
struct HasOperatorDelete : public Bird{
|
||||
~HasOperatorDelete() { }
|
||||
void operator delete(void *p) { i-=2; }
|
||||
void operator delete[](void *p) { i--; }
|
||||
};
|
||||
|
||||
struct AllocatedAsArray : public Bird {
|
||||
|
||||
};
|
||||
|
||||
// Vector deleting dtor for Bird is an alias because no new Bird[] expressions
|
||||
// in the TU.
|
||||
// X64: @"??_EBird@@UEAAPEAXI@Z" = weak dso_local unnamed_addr alias ptr (ptr, i32), ptr @"??_GBird@@UEAAPEAXI@Z"
|
||||
// X86: @"??_EBird@@UAEPAXI@Z" = weak dso_local unnamed_addr alias ptr (ptr, i32), ptr @"??_GBird@@UAEPAXI@Z"
|
||||
// No scalar destructor for Parrot.
|
||||
// CHECK-NOT: @"??_GParrot"
|
||||
// No vector destructor definition for Bird.
|
||||
// CHECK-NOT: define{{.*}}@"??_EBird"
|
||||
// No scalar deleting dtor for JustAWeirdBird.
|
||||
// CHECK-NOT: @"??_GJustAWeirdBird"
|
||||
|
||||
void dealloc(Bird *p) {
|
||||
delete[] p;
|
||||
}
|
||||
|
||||
Bird* alloc() {
|
||||
Parrot* P = new Parrot[38];
|
||||
return P;
|
||||
}
|
||||
|
||||
|
||||
template<class C>
|
||||
struct S {
|
||||
void foo() { void *p = new C(); delete (C *)p; }
|
||||
};
|
||||
|
||||
S<AllocatedAsArray[1][3]> sp;
|
||||
|
||||
void bar() {
|
||||
dealloc(alloc());
|
||||
|
||||
JustAWeirdBird B;
|
||||
B.doSmth(38);
|
||||
|
||||
Bird *p = new HasOperatorDelete[2];
|
||||
dealloc(p);
|
||||
|
||||
sp.foo();
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define dso_local void @{{.*}}dealloc{{.*}}(
|
||||
// CHECK-SAME: ptr noundef %[[PTR:.*]])
|
||||
// CHECK: entry:
|
||||
// CHECK-NEXT: %[[PTRADDR:.*]] = alloca ptr
|
||||
// CHECK-NEXT: store ptr %[[PTR]], ptr %[[PTRADDR]]
|
||||
// CHECK-NEXT: %[[LPTR:.*]] = load ptr, ptr %[[PTRADDR]]
|
||||
// CHECK-NEXT: %[[ISNULL:.*]] = icmp eq ptr %[[LPTR]], null
|
||||
// CHECK-NEXT: br i1 %[[ISNULL]], label %delete.end, label %delete.notnull
|
||||
// CHECK: delete.notnull:
|
||||
// X64-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LPTR]], i64 -8
|
||||
// X86-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LPTR]], i32 -4
|
||||
// X64-NEXT: %[[HOWMANY:.*]] = load i64, ptr %[[COOKIEGEP]]
|
||||
// X86-NEXT: %[[HOWMANY:.*]] = load i32, ptr %[[COOKIEGEP]]
|
||||
// X64-NEXT: %[[ISNOELEM:.*]] = icmp eq i64 %2, 0
|
||||
// X86-NEXT: %[[ISNOELEM:.*]] = icmp eq i32 %2, 0
|
||||
// CHECK-NEXT: br i1 %[[ISNOELEM]], label %vdtor.nocall, label %vdtor.call
|
||||
// CHECK: vdtor.nocall:
|
||||
// X64-NEXT: %[[HOWMANYBYTES:.*]] = mul i64 8, %[[HOWMANY]]
|
||||
// X86-NEXT: %[[HOWMANYBYTES:.*]] = mul i32 4, %[[HOWMANY]]
|
||||
// X64-NEXT: %[[ADDCOOKIESIZE:.*]] = add i64 %[[HOWMANYBYTES]], 8
|
||||
// X86-NEXT: %[[ADDCOOKIESIZE:.*]] = add i32 %[[HOWMANYBYTES]], 4
|
||||
// X64-NEXT: call void @"??_V@YAXPEAX_K@Z"(ptr noundef %[[COOKIEGEP]], i64 noundef %[[ADDCOOKIESIZE]])
|
||||
// X86-NEXT: call void @"??_V@YAXPAXI@Z"(ptr noundef %[[COOKIEGEP]], i32 noundef %[[ADDCOOKIESIZE]])
|
||||
// CHECK-NEXT: br label %delete.end
|
||||
// CHECK: vdtor.call:
|
||||
// CHECK-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[LPTR]]
|
||||
// CHECK-NEXT: %[[FPGEP:.*]] = getelementptr inbounds ptr, ptr %[[VTABLE]], i64 0
|
||||
// CHECK-NEXT: %[[FPLOAD:.*]] = load ptr, ptr %[[FPGEP]]
|
||||
// X64-NEXT: %[[CALL:.*]] = call noundef ptr %[[FPLOAD]](ptr noundef nonnull align 8 dereferenceable(8) %[[LPTR]], i32 noundef 3)
|
||||
// X86-NEXT: %[[CALL:.*]] = call x86_thiscallcc noundef ptr %[[FPLOAD]](ptr noundef nonnull align 4 dereferenceable(4) %[[LPTR]], i32 noundef 3)
|
||||
// CHECK-NEXT: br label %delete.end
|
||||
// CHECK: delete.end:
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
// Definition of S::foo, check that it has vector deleting destructor call
|
||||
// X64-LABEL: define linkonce_odr dso_local void @"?foo@?$S@$$BY102UAllocatedAsArray@@@@QEAAXXZ"
|
||||
// X86-LABEL: define linkonce_odr dso_local x86_thiscallcc void @"?foo@?$S@$$BY102UAllocatedAsArray@@@@QAEXXZ"
|
||||
// CHECK: delete.notnull: ; preds = %arrayctor.cont
|
||||
// CHECK-NEXT: %[[DEL_PTR:.*]] = getelementptr inbounds [1 x [3 x %struct.AllocatedAsArray]], ptr %[[THE_ARRAY:.*]], i32 0, i32 0
|
||||
// X64-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[DEL_PTR]], i64 -8
|
||||
// X86-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[DEL_PTR]], i32 -4
|
||||
// X64-NEXT: %[[HOWMANY:.*]] = load i64, ptr %[[COOKIEGEP]]
|
||||
// X86-NEXT: %[[HOWMANY:.*]] = load i32, ptr %[[COOKIEGEP]]
|
||||
// X64-NEXT: %[[ISNOELEM:.*]] = icmp eq i64 %[[HOWMANY]], 0
|
||||
// X86-NEXT: %[[ISNOELEM:.*]] = icmp eq i32 %[[HOWMANY]], 0
|
||||
// CHECK-NEXT: br i1 %[[ISNOELEM]], label %vdtor.nocall, label %vdtor.call
|
||||
// CHECK: vdtor.nocall: ; preds = %delete.notnull
|
||||
// X64-NEXT: %[[HOWMANYBYTES:.*]] = mul i64 8, %[[HOWMANY]]
|
||||
// X86-NEXT: %[[HOWMANYBYTES:.*]] = mul i32 4, %[[HOWMANY]]
|
||||
// X64-NEXT: %[[ADDCOOKIESIZE:.*]] = add i64 %[[HOWMANYBYTES]], 8
|
||||
// X86-NEXT: %[[ADDCOOKIESIZE:.*]] = add i32 %[[HOWMANYBYTES]], 4
|
||||
// X64-NEXT: call void @"??_V@YAXPEAX_K@Z"(ptr noundef %[[COOKIEGEP]], i64 noundef %[[ADDCOOKIESIZE]])
|
||||
// X86-NEXT: call void @"??_V@YAXPAXI@Z"(ptr noundef %[[COOKIEGEP]], i32 noundef %[[ADDCOOKIESIZE]])
|
||||
// CHECK-NEXT: br label %delete.end
|
||||
// CHECK: vdtor.call: ; preds = %delete.notnull
|
||||
// CHECK-NEXT: %[[VTABLE:.*]] = load ptr, ptr %[[DEL_PTR]]
|
||||
// CHECK-NEXT: %[[FPGEP:.*]] = getelementptr inbounds ptr, ptr %[[VTABLE]], i64 0
|
||||
// CHECK-NEXT: %[[FPLOAD:.*]] = load ptr, ptr %[[FPGEP]]
|
||||
// X64-NEXT: %[[CALL:.*]] = call noundef ptr %[[FPLOAD]](ptr noundef nonnull align 8 dereferenceable(8) %[[DEL_PTR]], i32 noundef 3)
|
||||
// X86-NEXT: %[[CALL:.*]] = call x86_thiscallcc noundef ptr %[[FPLOAD]](ptr noundef nonnull align 4 dereferenceable(4) %[[DEL_PTR]], i32 noundef 3)
|
||||
// CHECK-NEXT: br label %delete.end
|
||||
// CHECK: delete.end:
|
||||
// CHECK-NEXT: ret void
|
||||
|
||||
// Vector dtor definition for Parrot.
|
||||
// X64-LABEL: define weak dso_local noundef ptr @"??_EParrot@@UEAAPEAXI@Z"(
|
||||
// X64-SAME: ptr {{.*}} %[[THIS:.*]], i32 {{.*}} %[[IMPLICIT_PARAM:.*]]) unnamed_addr
|
||||
// X86-LABEL: define weak dso_local x86_thiscallcc noundef ptr @"??_EParrot@@UAEPAXI@Z"(
|
||||
// X86-SAME: ptr noundef nonnull align 4 dereferenceable(4) %[[THIS:.*]], i32 noundef %[[IMPLICIT_PARAM:.*]]) unnamed_addr
|
||||
// CHECK: entry:
|
||||
// CHECK-NEXT: %[[RET:.*]] = alloca ptr
|
||||
// CHECK-NEXT: %[[IPADDR:.*]] = alloca i32
|
||||
// CHECK-NEXT: %[[THISADDR:.*]] = alloca ptr
|
||||
// CHECK-NEXT: store i32 %[[IMPLICIT_PARAM]], ptr %[[IPADDR]]
|
||||
// CHECK-NEXT: store ptr %[[THIS]], ptr %[[THISADDR]]
|
||||
// CHECK-NEXT: %[[LTHIS:.*]] = load ptr, ptr %[[THISADDR]]
|
||||
// CHECK-NEXT: store ptr %[[LTHIS]], ptr %[[RET]]
|
||||
// CHECK-NEXT: %[[LIP:.*]] = load i32, ptr %[[IPADDR]]
|
||||
// CHECK-NEXT: %[[SECONDBIT:.*]] = and i32 %[[LIP]], 2
|
||||
// CHECK-NEXT: %[[ISSECONDBITZERO:.*]] = icmp eq i32 %[[SECONDBIT]], 0
|
||||
// CHECK-NEXT: br i1 %[[ISSECONDBITZERO:.*]], label %dtor.scalar, label %dtor.vector
|
||||
// CHECK: dtor.vector:
|
||||
// X64-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LTHIS]], i64 -8
|
||||
// X86-NEXT: %[[COOKIEGEP:.*]] = getelementptr inbounds i8, ptr %[[LTHIS]], i32 -4
|
||||
// X64-NEXT: %[[HOWMANY:.*]] = load i64, ptr %[[COOKIEGEP]]
|
||||
// X86-NEXT: %[[HOWMANY:.*]] = load i32, ptr %[[COOKIEGEP]]
|
||||
// X64-NEXT: %[[END:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[LTHIS]], i64 %[[HOWMANY]]
|
||||
// X86-NEXT: %[[END:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[LTHIS]], i32 %[[HOWMANY]]
|
||||
// CHECK-NEXT: br label %arraydestroy.body
|
||||
// CHECK: arraydestroy.body:
|
||||
// CHECK-NEXT: %[[PASTELEM:.*]] = phi ptr [ %delete.end, %dtor.vector ], [ %arraydestroy.element, %arraydestroy.body ]
|
||||
// X64-NEXT: %[[CURELEM:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[PASTELEM]], i64 -1
|
||||
// X86-NEXT: %[[CURELEM:.*]] = getelementptr inbounds %struct.Parrot, ptr %[[PASTELEM]], i32 -1
|
||||
// X64-NEXT: call void @"??1Parrot@@UEAA@XZ"(ptr noundef nonnull align 8 dereferenceable(8) %[[CURELEM]])
|
||||
// X86-NEXT: call x86_thiscallcc void @"??1Parrot@@UAE@XZ"(ptr noundef nonnull align 4 dereferenceable(4) %[[CURELEM]])
|
||||
// CHECK-NEXT: %[[DONE:.*]] = icmp eq ptr %[[CURELEM]], %[[LTHIS]]
|
||||
// CHECK-NEXT: br i1 %[[DONE]], label %arraydestroy.done3, label %arraydestroy.body
|
||||
// CHECK: arraydestroy.done3:
|
||||
// CHECK-NEXT: br label %dtor.vector.cont
|
||||
// CHECK: dtor.vector.cont:
|
||||
// CHECK-NEXT: %[[FIRSTBIT:.*]] = and i32 %[[LIP]], 1
|
||||
// CHECK-NEXT: %[[ISFIRSTBITZERO:.*]] = icmp eq i32 %[[FIRSTBIT]], 0
|
||||
// CHECK-NEXT: br i1 %[[ISFIRSTBITZERO]], label %dtor.continue, label %dtor.call_delete_after_array_destroy
|
||||
// CHECK: dtor.call_delete_after_array_destroy:
|
||||
// X64-NEXT: call void @"??_V@YAXPEAX_K@Z"(ptr noundef %[[COOKIEGEP]], i64 noundef 8)
|
||||
// X86-NEXT: call void @"??_V@YAXPAXI@Z"(ptr noundef %[[COOKIEGEP]], i32 noundef 4)
|
||||
// CHECK-NEXT: br label %dtor.continue
|
||||
// CHECK: dtor.scalar:
|
||||
// X64-NEXT: call void @"??1Parrot@@UEAA@XZ"(ptr noundef nonnull align 8 dereferenceable(8) %[[LTHIS]])
|
||||
// X86-NEXT: call x86_thiscallcc void @"??1Parrot@@UAE@XZ"(ptr noundef nonnull align 4 dereferenceable(4) %[[LTHIS]])
|
||||
// CHECK-NEXT: %[[FIRSTBIT:.*]] = and i32 %[[LIP]], 1
|
||||
// CHECK-NEXT: %[[ISFIRSTBITZERO:.*]] = icmp eq i32 %[[FIRSTBIT]], 0
|
||||
// CHECK-NEXT: br i1 %[[ISFIRSTBITZERO]], label %dtor.continue, label %dtor.call_delete
|
||||
// CHECK: dtor.call_delete:
|
||||
// X64-NEXT: call void @"??3@YAXPEAX_K@Z"(ptr noundef %[[LTHIS]], i64 noundef 8)
|
||||
// X86-NEXT: call void @"??3@YAXPAXI@Z"(ptr noundef %[[LTHIS]], i32 noundef 4)
|
||||
// CHECK-NEXT: br label %dtor.continue
|
||||
// CHECK: dtor.continue:
|
||||
// CHECK-NEXT: %[[LOADRET:.*]] = load ptr, ptr %[[RET]]
|
||||
// CHECK-NEXT: ret ptr %[[LOADRET]]
|
||||
|
||||
// X64: define weak dso_local noundef ptr @"??_EJustAWeirdBird@@UEAAPEAXI@Z"(
|
||||
// X64-SAME: ptr noundef nonnull align 8 dereferenceable(8) %this, i32 noundef %should_call_delete)
|
||||
// X86: define weak dso_local x86_thiscallcc noundef ptr @"??_EJustAWeirdBird@@UAEPAXI@Z"(
|
||||
// X86-SAME: ptr noundef nonnull align 4 dereferenceable(4) %this, i32 noundef %should_call_delete) unnamed_addr
|
||||
|
||||
// X64-LABEL: define weak dso_local noundef ptr @"??_EHasOperatorDelete@@UEAAPEAXI@Z"
|
||||
// X86-LABEL: define weak dso_local x86_thiscallcc noundef ptr @"??_EHasOperatorDelete@@UAEPAXI@Z"
|
||||
// CHECK: dtor.call_delete_after_array_destroy:
|
||||
// X64-NEXT: call void @"??_VHasOperatorDelete@@SAXPEAX@Z"
|
||||
// X86-NEXT: call void @"??_VHasOperatorDelete@@SAXPAX@Z"
|
||||
// CHECK: dtor.call_delete:
|
||||
// X64-NEXT: call void @"??3HasOperatorDelete@@SAXPEAX@Z"
|
||||
// X86-NEXT: call void @"??3HasOperatorDelete@@SAXPAX@Z"
|
||||
|
||||
// X64: define weak dso_local noundef ptr @"??_EAllocatedAsArray@@UEAAPEAXI@Z"
|
||||
// X86: define weak dso_local x86_thiscallcc noundef ptr @"??_EAllocatedAsArray@@UAEPAXI@Z"
|
@ -26,7 +26,7 @@ struct B {
|
||||
B b;
|
||||
|
||||
// ITANIUM-DAG: @_ZTV1C = {{.*}} constant { [4 x ptr] } {{.*}} null, ptr @_ZTI1C, ptr @_ZN1CD1Ev, ptr @_ZN1CD0Ev
|
||||
// MSABI-DAG: @[[C_VFTABLE:.*]] = {{.*}} constant { [2 x ptr] } {{.*}} @"??_R4C@@6B@", ptr @"??_EC@@UEAAPEAXI@Z"
|
||||
// MSABI-DAG: @[[C_VFTABLE:.*]] = {{.*}} constant { [2 x ptr] } {{.*}} @"??_R4C@@6B@", ptr @"??_GC@@UEAAPEAXI@Z"
|
||||
struct C {
|
||||
virtual ~C() = default;
|
||||
virtual consteval C &operator=(const C&) = default;
|
||||
@ -36,7 +36,7 @@ struct C {
|
||||
C c;
|
||||
|
||||
// ITANIUM-DAG: @_ZTV1D = {{.*}} constant { [4 x ptr] } {{.*}} null, ptr @_ZTI1D, ptr @_ZN1DD1Ev, ptr @_ZN1DD0Ev
|
||||
// MSABI-DAG: @[[D_VFTABLE:.*]] = {{.*}} constant { [2 x ptr] } {{.*}} @"??_R4D@@6B@", ptr @"??_ED@@UEAAPEAXI@Z"
|
||||
// MSABI-DAG: @[[D_VFTABLE:.*]] = {{.*}} constant { [2 x ptr] } {{.*}} @"??_R4D@@6B@", ptr @"??_GD@@UEAAPEAXI@Z"
|
||||
struct D : C {};
|
||||
// ITANIUM-DAG: @d = {{.*}}global { ptr } { {{.*}} @_ZTV1D,
|
||||
// MSABI-DAG: @"?d@@3UD@@A" = {{.*}}global { ptr } { ptr @"??_7D@@6B@" }
|
||||
|
@ -23,4 +23,4 @@ void test() {
|
||||
|
||||
// Check that the virtual table is an unnamed_addr constant in comdat that can
|
||||
// be merged with the virtual table with other TUs.
|
||||
// CHECK: unnamed_addr constant {{.*}}[ptr @"??_R4Fruit@@6B@", ptr @"??_EFruit@@UAEPAXI@Z", ptr @"?eval@Fruit@@UAEXXZ"{{.*}}comdat($"??_7Fruit@@6B@")
|
||||
// CHECK: unnamed_addr constant {{.*}}[ptr @"??_R4Fruit@@6B@", ptr @"??_GFruit@@UAEPAXI@Z", ptr @"?eval@Fruit@@UAEXXZ"{{.*}}comdat($"??_7Fruit@@6B@")
|
||||
|
@ -24,15 +24,16 @@ DerivedABC *useABCVTable() { return new DerivedABC(); }
|
||||
// MSVC: @"__profn_??1ABC@@{{.*}}" =
|
||||
// MSVC-NOT: @"__profn_??_G{{.*}}" =
|
||||
|
||||
// MSVC-LABEL: define linkonce_odr dso_local noundef ptr @"??_GDerivedABC@@UEAAPEAXI@Z"(ptr {{[^,]*}} %this, {{.*}})
|
||||
// MSVC-NOT: call void @llvm.instrprof.increment({{.*}})
|
||||
// MSVC: call void @"??1DerivedABC@@UEAA@XZ"({{.*}})
|
||||
// MSVC: ret void
|
||||
|
||||
// MSVC-LABEL: define linkonce_odr dso_local noundef ptr @"??_GABC@@UEAAPEAXI@Z"(ptr {{[^,]*}} %this, {{.*}})
|
||||
// MSVC-NOT: call void @llvm.instrprof.increment({{.*}})
|
||||
// MSVC: call void @llvm.trap()
|
||||
// MSVC-NEXT: unreachable
|
||||
|
||||
// MSVC-LABEL: define linkonce_odr dso_local noundef ptr @"??_GDerivedABC@@UEAAPEAXI@Z"(ptr {{[^,]*}} %this, {{.*}})
|
||||
// MSVC-NOT: call void @llvm.instrprof.increment({{.*}})
|
||||
// MSVC: call void @"??1DerivedABC@@UEAA@XZ"({{.*}})
|
||||
|
||||
// MSVC-LABEL: define linkonce_odr dso_local void @"??1DerivedABC@@UEAA@XZ"({{.*}})
|
||||
// MSVC: call void @llvm.instrprof.increment({{.*}})
|
||||
// MSVC: call void @"??1ABC@@UEAA@XZ"({{.*}})
|
||||
|
@ -1,60 +0,0 @@
|
||||
// RUN: %clang_cc1 %s -verify=expected -fsyntax-only -triple=x86_64-unknown-linux-gnu
|
||||
// RUN: %clang_cc1 %s -verify=expected -fsyntax-only -triple=x86_64-unknown-linux-gnu -std=c++20
|
||||
// RUN: %clang_cc1 %s -verify=expected,ms -fms-extensions -fms-compatibility -triple=x86_64-pc-windows-msvc -DMS
|
||||
|
||||
struct Foo {
|
||||
virtual ~Foo() {} // expected-error {{attempt to use a deleted function}}
|
||||
static void operator delete(void* ptr) = delete; // expected-note {{explicitly marked deleted here}}
|
||||
};
|
||||
|
||||
|
||||
struct Bar {
|
||||
virtual ~Bar() {}
|
||||
static void operator delete[](void* ptr) = delete;
|
||||
};
|
||||
|
||||
struct Baz {
|
||||
virtual ~Baz() {}
|
||||
static void operator delete[](void* ptr) = delete; // expected-note {{explicitly marked deleted here}}\
|
||||
ms-note{{explicitly marked deleted here}}}
|
||||
};
|
||||
|
||||
struct BarBaz {
|
||||
~BarBaz() {}
|
||||
static void operator delete[](void* ptr) = delete;
|
||||
};
|
||||
|
||||
void foobar() {
|
||||
Baz *B = new Baz[10](); // ms-error {{attempt to use a deleted function}}
|
||||
delete [] B; // expected-error {{attempt to use a deleted function}}
|
||||
BarBaz *BB = new BarBaz[10]();
|
||||
}
|
||||
|
||||
struct BaseDelete1 {
|
||||
void operator delete[](void *); //ms-note 3{{member found by ambiguous name lookup}}
|
||||
};
|
||||
struct BaseDelete2 {
|
||||
void operator delete[](void *); //ms-note 3{{member found by ambiguous name lookup}}
|
||||
};
|
||||
struct BaseDestructor {
|
||||
BaseDestructor() {}
|
||||
virtual ~BaseDestructor() = default;
|
||||
};
|
||||
struct Final : BaseDelete1, BaseDelete2, BaseDestructor {
|
||||
Final() {}
|
||||
};
|
||||
struct FinalExplicit : BaseDelete1, BaseDelete2, BaseDestructor {
|
||||
FinalExplicit() {}
|
||||
inline ~FinalExplicit() {}
|
||||
};
|
||||
|
||||
#ifdef MS
|
||||
struct Final1 : BaseDelete1, BaseDelete2, BaseDestructor {
|
||||
__declspec(dllexport) ~Final1() {} // ms-error {{member 'operator delete[]' found in multiple base classes of different types}}
|
||||
};
|
||||
#endif // MS
|
||||
|
||||
void foo() {
|
||||
Final* a = new Final[10](); // ms-error {{member 'operator delete[]' found in multiple base classes of different types}}
|
||||
FinalExplicit* b = new FinalExplicit[10](); // ms-error {{member 'operator delete[]' found in multiple base classes of different types}}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user