mirror of
https://github.com/intel/llvm.git
synced 2026-01-18 07:57:36 +08:00
Abstract out the emission of vtables, add basic support for vtable emission when using -cxx-abi microsoft
Reviewed at http://llvm-reviews.chandlerc.com/D1532 llvm-svn: 191523
This commit is contained in:
@@ -106,8 +106,16 @@ public:
|
||||
raw_ostream &) = 0;
|
||||
virtual void mangleReferenceTemporary(const VarDecl *D,
|
||||
raw_ostream &) = 0;
|
||||
// FIXME: Some of these objects only exist in select ABIs. We should probably
|
||||
// only declare them in ABI-specific manglers?
|
||||
virtual void mangleCXXVTable(const CXXRecordDecl *RD,
|
||||
raw_ostream &) = 0;
|
||||
/// \brief Mangle vftable symbols. Only a subset of the bases along the path
|
||||
/// to the vftable are included in the name. It's up to the caller to pick
|
||||
/// them correctly.
|
||||
virtual void mangleCXXVFTable(const CXXRecordDecl *Derived,
|
||||
ArrayRef<const CXXRecordDecl *> BasePath,
|
||||
raw_ostream &Out) = 0;
|
||||
virtual void mangleCXXVTT(const CXXRecordDecl *RD,
|
||||
raw_ostream &) = 0;
|
||||
/// \brief Mangle vbtable symbols. Only a subset of the bases along the path
|
||||
|
||||
@@ -329,13 +329,6 @@ public:
|
||||
VTableContext(ASTContext &Context);
|
||||
~VTableContext();
|
||||
|
||||
bool isMicrosoftABI() const {
|
||||
// FIXME: Currently, this method is only used in the VTableContext and
|
||||
// VTableBuilder code which is ABI-specific. Probably we can remove it
|
||||
// when we add a layer of abstraction for vtable generation.
|
||||
return IsMicrosoftABI;
|
||||
}
|
||||
|
||||
const VTableLayout &getVTableLayout(const CXXRecordDecl *RD) {
|
||||
computeVTableRelatedInformation(RD);
|
||||
assert(VTableLayouts.count(RD) && "No layout for this record decl!");
|
||||
@@ -377,11 +370,13 @@ unsigned GetVBTableIndex(const CXXRecordDecl *Derived,
|
||||
struct VFPtrInfo {
|
||||
typedef SmallVector<const CXXRecordDecl *, 1> BasePath;
|
||||
|
||||
// Don't pass the PathToMangle as it should be calculated later.
|
||||
VFPtrInfo(CharUnits VFPtrOffset, const BasePath &PathToBaseWithVFPtr)
|
||||
: VBTableIndex(0), LastVBase(0), VFPtrOffset(VFPtrOffset),
|
||||
PathToBaseWithVFPtr(PathToBaseWithVFPtr), VFPtrFullOffset(VFPtrOffset) {
|
||||
}
|
||||
|
||||
// Don't pass the PathToMangle as it should be calculated later.
|
||||
VFPtrInfo(uint64_t VBTableIndex, const CXXRecordDecl *LastVBase,
|
||||
CharUnits VFPtrOffset, const BasePath &PathToBaseWithVFPtr,
|
||||
CharUnits VFPtrFullOffset)
|
||||
@@ -404,9 +399,13 @@ struct VFPtrInfo {
|
||||
CharUnits VFPtrOffset;
|
||||
|
||||
/// This holds the base classes path from the complete type to the first base
|
||||
/// with the given vfptr offset.
|
||||
/// with the given vfptr offset, in the base-to-derived order.
|
||||
BasePath PathToBaseWithVFPtr;
|
||||
|
||||
/// This holds the subset of records that need to be mangled into the vftable
|
||||
/// symbol name in order to get a unique name, in the derived-to-base order.
|
||||
BasePath PathToMangle;
|
||||
|
||||
/// This is the full offset of the vfptr from the start of the complete type.
|
||||
CharUnits VFPtrFullOffset;
|
||||
};
|
||||
|
||||
@@ -137,6 +137,9 @@ public:
|
||||
raw_ostream &);
|
||||
void mangleCXXVTable(const CXXRecordDecl *RD,
|
||||
raw_ostream &);
|
||||
void mangleCXXVFTable(const CXXRecordDecl *Derived,
|
||||
ArrayRef<const CXXRecordDecl *> BasePath,
|
||||
raw_ostream &Out);
|
||||
void mangleCXXVTT(const CXXRecordDecl *RD,
|
||||
raw_ostream &);
|
||||
void mangleCXXVBTable(const CXXRecordDecl *Derived,
|
||||
@@ -3772,6 +3775,14 @@ void ItaniumMangleContext::mangleCXXVTable(const CXXRecordDecl *RD,
|
||||
Mangler.mangleNameOrStandardSubstitution(RD);
|
||||
}
|
||||
|
||||
void
|
||||
ItaniumMangleContext::mangleCXXVFTable(const CXXRecordDecl *Derived,
|
||||
ArrayRef<const CXXRecordDecl *> BasePath,
|
||||
raw_ostream &Out) {
|
||||
llvm_unreachable(
|
||||
"The Itanium C++ ABI does not have vftables (use vtables instead)!");
|
||||
}
|
||||
|
||||
void ItaniumMangleContext::mangleCXXVTT(const CXXRecordDecl *RD,
|
||||
raw_ostream &Out) {
|
||||
// <special-name> ::= TT <type> # VTT structure
|
||||
|
||||
@@ -188,6 +188,9 @@ public:
|
||||
raw_ostream &);
|
||||
virtual void mangleCXXVTable(const CXXRecordDecl *RD,
|
||||
raw_ostream &);
|
||||
virtual void mangleCXXVFTable(const CXXRecordDecl *Derived,
|
||||
ArrayRef<const CXXRecordDecl *> BasePath,
|
||||
raw_ostream &Out);
|
||||
virtual void mangleCXXVTT(const CXXRecordDecl *RD,
|
||||
raw_ostream &);
|
||||
virtual void mangleCXXVBTable(const CXXRecordDecl *Derived,
|
||||
@@ -1900,16 +1903,26 @@ void MicrosoftMangleContext::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
|
||||
|
||||
void MicrosoftMangleContext::mangleCXXVTable(const CXXRecordDecl *RD,
|
||||
raw_ostream &Out) {
|
||||
llvm_unreachable(
|
||||
"The Microsoft C++ ABI does not have vtables (use vftables instead)!");
|
||||
}
|
||||
|
||||
void MicrosoftMangleContext::mangleCXXVFTable(
|
||||
const CXXRecordDecl *Derived, ArrayRef<const CXXRecordDecl *> BasePath,
|
||||
raw_ostream &Out) {
|
||||
// <mangled-name> ::= ?_7 <class-name> <storage-class>
|
||||
// <cvr-qualifiers> [<name>] @
|
||||
// NOTE: <cvr-qualifiers> here is always 'B' (const). <storage-class>
|
||||
// is always '6' for vftables.
|
||||
MicrosoftCXXNameMangler Mangler(*this, Out);
|
||||
Mangler.getStream() << "\01??_7";
|
||||
Mangler.mangleName(RD);
|
||||
Mangler.getStream() << "6B"; // '6' for vftable, 'B' for const.
|
||||
// TODO: If the class has more than one vtable, mangle in the class it came
|
||||
// from.
|
||||
Mangler.mangleName(Derived);
|
||||
Mangler.getStream() << "6B"; // '6' for vftable, 'B' for const.
|
||||
for (ArrayRef<const CXXRecordDecl *>::iterator I = BasePath.begin(),
|
||||
E = BasePath.end();
|
||||
I != E; ++I) {
|
||||
Mangler.mangleName(*I);
|
||||
}
|
||||
Mangler.getStream() << '@';
|
||||
}
|
||||
|
||||
|
||||
@@ -999,10 +999,6 @@ public:
|
||||
dumpLayout(llvm::errs());
|
||||
}
|
||||
|
||||
bool isMicrosoftABI() const {
|
||||
return VTables.isMicrosoftABI();
|
||||
}
|
||||
|
||||
uint64_t getNumThunks() const {
|
||||
return Thunks.size();
|
||||
}
|
||||
@@ -1157,7 +1153,7 @@ void VTableBuilder::ComputeThisAdjustments() {
|
||||
// Add it.
|
||||
VTableThunks[VTableIndex].This = ThisAdjustment;
|
||||
|
||||
if (isa<CXXDestructorDecl>(MD) && !isMicrosoftABI()) {
|
||||
if (isa<CXXDestructorDecl>(MD)) {
|
||||
// Add an adjustment for the deleting destructor as well.
|
||||
VTableThunks[VTableIndex + 1].This = ThisAdjustment;
|
||||
}
|
||||
@@ -1188,8 +1184,6 @@ void VTableBuilder::ComputeThisAdjustments() {
|
||||
break;
|
||||
case VTableComponent::CK_DeletingDtorPointer:
|
||||
// We've already added the thunk when we saw the complete dtor pointer.
|
||||
// FIXME: check how this works in the Microsoft ABI
|
||||
// while working on the multiple inheritance patch.
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1326,15 +1320,9 @@ VTableBuilder::AddMethod(const CXXMethodDecl *MD,
|
||||
assert(ReturnAdjustment.isEmpty() &&
|
||||
"Destructor can't have return adjustment!");
|
||||
|
||||
// FIXME: Should probably add a layer of abstraction for vtable generation.
|
||||
if (!isMicrosoftABI()) {
|
||||
// Add both the complete destructor and the deleting destructor.
|
||||
Components.push_back(VTableComponent::MakeCompleteDtor(DD));
|
||||
Components.push_back(VTableComponent::MakeDeletingDtor(DD));
|
||||
} else {
|
||||
// Add the scalar deleting destructor.
|
||||
Components.push_back(VTableComponent::MakeDeletingDtor(DD));
|
||||
}
|
||||
// Add both the complete destructor and the deleting destructor.
|
||||
Components.push_back(VTableComponent::MakeCompleteDtor(DD));
|
||||
Components.push_back(VTableComponent::MakeDeletingDtor(DD));
|
||||
} else {
|
||||
// Add the return adjustment if necessary.
|
||||
if (!ReturnAdjustment.isEmpty())
|
||||
@@ -1693,18 +1681,12 @@ VTableBuilder::LayoutPrimaryAndSecondaryVTables(BaseSubobject Base,
|
||||
if (Base.getBase() == MostDerivedClass)
|
||||
VBaseOffsetOffsets = Builder.getVBaseOffsetOffsets();
|
||||
|
||||
// FIXME: Should probably add a layer of abstraction for vtable generation.
|
||||
if (!isMicrosoftABI()) {
|
||||
// Add the offset to top.
|
||||
CharUnits OffsetToTop = MostDerivedClassOffset - OffsetInLayoutClass;
|
||||
Components.push_back(VTableComponent::MakeOffsetToTop(OffsetToTop));
|
||||
// Add the offset to top.
|
||||
CharUnits OffsetToTop = MostDerivedClassOffset - OffsetInLayoutClass;
|
||||
Components.push_back(VTableComponent::MakeOffsetToTop(OffsetToTop));
|
||||
|
||||
// Next, add the RTTI.
|
||||
Components.push_back(VTableComponent::MakeRTTI(MostDerivedClass));
|
||||
} else {
|
||||
// FIXME: unclear what to do with RTTI in MS ABI as emitting it anywhere
|
||||
// breaks the vftable layout. Just skip RTTI for now, can't mangle anyway.
|
||||
}
|
||||
// Next, add the RTTI.
|
||||
Components.push_back(VTableComponent::MakeRTTI(MostDerivedClass));
|
||||
|
||||
uint64_t AddressPoint = Components.size();
|
||||
|
||||
@@ -1722,16 +1704,10 @@ VTableBuilder::LayoutPrimaryAndSecondaryVTables(BaseSubobject Base,
|
||||
const CXXMethodDecl *MD = I->first;
|
||||
const MethodInfo &MI = I->second;
|
||||
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
|
||||
// FIXME: Should probably add a layer of abstraction for vtable generation.
|
||||
if (!isMicrosoftABI()) {
|
||||
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)]
|
||||
= MI.VTableIndex - AddressPoint;
|
||||
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)]
|
||||
= MI.VTableIndex + 1 - AddressPoint;
|
||||
} else {
|
||||
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)]
|
||||
= MI.VTableIndex - AddressPoint;
|
||||
}
|
||||
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)]
|
||||
= MI.VTableIndex - AddressPoint;
|
||||
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)]
|
||||
= MI.VTableIndex + 1 - AddressPoint;
|
||||
} else {
|
||||
MethodVTableIndices[MD] = MI.VTableIndex - AddressPoint;
|
||||
}
|
||||
@@ -2044,8 +2020,6 @@ void VTableBuilder::dumpLayout(raw_ostream& Out) {
|
||||
Out << DD->getQualifiedNameAsString();
|
||||
if (IsComplete)
|
||||
Out << "() [complete]";
|
||||
else if (isMicrosoftABI())
|
||||
Out << "() [scalar deleting]";
|
||||
else
|
||||
Out << "() [deleting]";
|
||||
|
||||
@@ -2230,18 +2204,11 @@ void VTableBuilder::dumpLayout(raw_ostream& Out) {
|
||||
MD);
|
||||
|
||||
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
|
||||
// FIXME: Should add a layer of abstraction for vtable generation.
|
||||
if (!isMicrosoftABI()) {
|
||||
GlobalDecl GD(DD, Dtor_Complete);
|
||||
assert(MethodVTableIndices.count(GD));
|
||||
uint64_t VTableIndex = MethodVTableIndices[GD];
|
||||
IndicesMap[VTableIndex] = MethodName + " [complete]";
|
||||
IndicesMap[VTableIndex + 1] = MethodName + " [deleting]";
|
||||
} else {
|
||||
GlobalDecl GD(DD, Dtor_Deleting);
|
||||
assert(MethodVTableIndices.count(GD));
|
||||
IndicesMap[MethodVTableIndices[GD]] = MethodName + " [scalar deleting]";
|
||||
}
|
||||
GlobalDecl GD(DD, Dtor_Complete);
|
||||
assert(MethodVTableIndices.count(GD));
|
||||
uint64_t VTableIndex = MethodVTableIndices[GD];
|
||||
IndicesMap[VTableIndex] = MethodName + " [complete]";
|
||||
IndicesMap[VTableIndex + 1] = MethodName + " [deleting]";
|
||||
} else {
|
||||
assert(MethodVTableIndices.count(MD));
|
||||
IndicesMap[MethodVTableIndices[MD]] = MethodName;
|
||||
@@ -2352,10 +2319,12 @@ static VTableLayout *CreateVTableLayout(const VTableBuilder &Builder) {
|
||||
VTableThunks.size(),
|
||||
VTableThunks.data(),
|
||||
Builder.getAddressPoints(),
|
||||
Builder.isMicrosoftABI());
|
||||
/*IsMicrosoftABI=*/false);
|
||||
}
|
||||
|
||||
void VTableContext::computeVTableRelatedInformation(const CXXRecordDecl *RD) {
|
||||
assert(!IsMicrosoftABI && "Shouldn't be called in this ABI!");
|
||||
|
||||
const VTableLayout *&Entry = VTableLayouts[RD];
|
||||
|
||||
// Check if we've computed this information before.
|
||||
@@ -3132,6 +3101,55 @@ static void EnumerateVFPtrs(
|
||||
}
|
||||
}
|
||||
|
||||
/// CalculatePathToMangle - Calculate the subset of records that should be used
|
||||
/// to mangle the vftable for the given vfptr.
|
||||
/// Should only be called if a class has multiple vftables.
|
||||
static void
|
||||
CalculatePathToMangle(const CXXRecordDecl *RD, VFPtrInfo &VFPtr) {
|
||||
// FIXME: In some rare cases this code produces a slightly incorrect mangling.
|
||||
// It's very likely that the vbtable mangling code can be adjusted to mangle
|
||||
// both vftables and vbtables correctly.
|
||||
|
||||
VFPtrInfo::BasePath &FullPath = VFPtr.PathToBaseWithVFPtr;
|
||||
if (FullPath.empty()) {
|
||||
// Mangle the class's own vftable.
|
||||
assert(RD->getNumVBases() &&
|
||||
"Something's wrong: if the most derived "
|
||||
"class has more than one vftable, it can only have its own "
|
||||
"vftable if it has vbases");
|
||||
VFPtr.PathToMangle.push_back(RD);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned Begin = 0;
|
||||
|
||||
// First, skip all the bases before the vbase.
|
||||
if (VFPtr.LastVBase) {
|
||||
while (FullPath[Begin] != VFPtr.LastVBase) {
|
||||
Begin++;
|
||||
assert(Begin < FullPath.size());
|
||||
}
|
||||
}
|
||||
|
||||
// Then, put the rest of the base path in the reverse order.
|
||||
for (unsigned I = FullPath.size(); I != Begin; --I) {
|
||||
const CXXRecordDecl *CurBase = FullPath[I - 1],
|
||||
*ItsBase = (I == 1) ? RD : FullPath[I - 2];
|
||||
bool BaseIsVirtual = false;
|
||||
for (CXXRecordDecl::base_class_const_iterator J = ItsBase->bases_begin(),
|
||||
F = ItsBase->bases_end(); J != F; ++J) {
|
||||
if (J->getType()->getAsCXXRecordDecl() == CurBase) {
|
||||
BaseIsVirtual = J->isVirtual();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Should skip the current base if it is a non-virtual base with no siblings.
|
||||
if (BaseIsVirtual || ItsBase->getNumBases() != 1)
|
||||
VFPtr.PathToMangle.push_back(CurBase);
|
||||
}
|
||||
}
|
||||
|
||||
static void EnumerateVFPtrs(ASTContext &Context, const CXXRecordDecl *ForClass,
|
||||
MicrosoftVFTableContext::VFPtrListTy &Result) {
|
||||
Result.clear();
|
||||
@@ -3140,6 +3158,10 @@ static void EnumerateVFPtrs(ASTContext &Context, const CXXRecordDecl *ForClass,
|
||||
EnumerateVFPtrs(Context, ForClass, ClassLayout,
|
||||
BaseSubobject(ForClass, CharUnits::Zero()), 0,
|
||||
VFPtrInfo::BasePath(), VisitedVBases, Result);
|
||||
if (Result.size() > 1) {
|
||||
for (unsigned I = 0, E = Result.size(); I != E; ++I)
|
||||
CalculatePathToMangle(ForClass, Result[I]);
|
||||
}
|
||||
}
|
||||
|
||||
void MicrosoftVFTableContext::computeVTableRelatedInformation(
|
||||
|
||||
@@ -280,7 +280,7 @@ static llvm::Value *BuildAppleKextVirtualCall(CodeGenFunction &CGF,
|
||||
"No kext in Microsoft ABI");
|
||||
GD = GD.getCanonicalDecl();
|
||||
CodeGenModule &CGM = CGF.CGM;
|
||||
llvm::Value *VTable = CGM.getVTables().GetAddrOfVTable(RD);
|
||||
llvm::Value *VTable = CGM.getCXXABI().getAddrOfVTable(RD, CharUnits());
|
||||
Ty = Ty->getPointerTo()->getPointerTo();
|
||||
VTable = CGF.Builder.CreateBitCast(VTable, Ty);
|
||||
assert(VTable && "BuildVirtualCall = kext vtbl pointer is null");
|
||||
|
||||
@@ -303,6 +303,29 @@ public:
|
||||
CallExpr::const_arg_iterator ArgBeg,
|
||||
CallExpr::const_arg_iterator ArgEnd) = 0;
|
||||
|
||||
/// Emits the VTable definitions required for the given record type.
|
||||
virtual void emitVTableDefinitions(CodeGenVTables &CGVT,
|
||||
const CXXRecordDecl *RD) = 0;
|
||||
|
||||
/// Get the address point of the vtable for the given base subobject while
|
||||
/// building a constructor or a destructor. On return, NeedsVirtualOffset
|
||||
/// tells if a virtual base adjustment is needed in order to get the offset
|
||||
/// of the base subobject.
|
||||
virtual llvm::Value *getVTableAddressPointInStructor(
|
||||
CodeGenFunction &CGF, const CXXRecordDecl *RD, BaseSubobject Base,
|
||||
const CXXRecordDecl *NearestVBase, bool &NeedsVirtualOffset) = 0;
|
||||
|
||||
/// Get the address point of the vtable for the given base subobject while
|
||||
/// building a constexpr.
|
||||
virtual llvm::Constant *
|
||||
getVTableAddressPointForConstExpr(BaseSubobject Base,
|
||||
const CXXRecordDecl *VTableClass) = 0;
|
||||
|
||||
/// Get the address of the vtable for the given record decl which should be
|
||||
/// used for the vptr at the given offset in RD.
|
||||
virtual llvm::GlobalVariable *getAddrOfVTable(const CXXRecordDecl *RD,
|
||||
CharUnits VPtrOffset) = 0;
|
||||
|
||||
/// Build a virtual function pointer in the ABI-specific way.
|
||||
virtual llvm::Value *getVirtualFunctionPointer(CodeGenFunction &CGF,
|
||||
GlobalDecl GD,
|
||||
@@ -319,9 +342,7 @@ public:
|
||||
/// Emit any tables needed to implement virtual inheritance. For Itanium,
|
||||
/// this emits virtual table tables. For the MSVC++ ABI, this emits virtual
|
||||
/// base tables.
|
||||
virtual void
|
||||
EmitVirtualInheritanceTables(llvm::GlobalVariable::LinkageTypes Linkage,
|
||||
const CXXRecordDecl *RD) = 0;
|
||||
virtual void emitVirtualInheritanceTables(const CXXRecordDecl *RD) = 0;
|
||||
|
||||
virtual void EmitReturnFromThunk(CodeGenFunction &CGF,
|
||||
RValue RV, QualType ResultType);
|
||||
|
||||
@@ -1878,39 +1878,19 @@ CodeGenFunction::InitializeVTablePointer(BaseSubobject Base,
|
||||
const CXXRecordDecl *NearestVBase,
|
||||
CharUnits OffsetFromNearestVBase,
|
||||
const CXXRecordDecl *VTableClass) {
|
||||
const CXXRecordDecl *RD = Base.getBase();
|
||||
|
||||
// Compute the address point.
|
||||
llvm::Value *VTableAddressPoint;
|
||||
|
||||
bool NeedsVTTParam = CGM.getCXXABI().NeedsVTTParameter(CurGD);
|
||||
|
||||
// Check if we need to use a vtable from the VTT.
|
||||
if (NeedsVTTParam && (RD->getNumVBases() || NearestVBase)) {
|
||||
// Get the secondary vpointer index.
|
||||
uint64_t VirtualPointerIndex =
|
||||
CGM.getVTables().getSecondaryVirtualPointerIndex(VTableClass, Base);
|
||||
|
||||
/// Load the VTT.
|
||||
llvm::Value *VTT = LoadCXXVTT();
|
||||
if (VirtualPointerIndex)
|
||||
VTT = Builder.CreateConstInBoundsGEP1_64(VTT, VirtualPointerIndex);
|
||||
|
||||
// And load the address point from the VTT.
|
||||
VTableAddressPoint = Builder.CreateLoad(VTT);
|
||||
} else {
|
||||
llvm::Constant *VTable = CGM.getVTables().GetAddrOfVTable(VTableClass);
|
||||
uint64_t AddressPoint =
|
||||
CGM.getVTableContext().getVTableLayout(VTableClass).getAddressPoint(Base);
|
||||
VTableAddressPoint =
|
||||
Builder.CreateConstInBoundsGEP2_64(VTable, 0, AddressPoint);
|
||||
}
|
||||
bool NeedsVirtualOffset;
|
||||
llvm::Value *VTableAddressPoint =
|
||||
CGM.getCXXABI().getVTableAddressPointInStructor(
|
||||
*this, VTableClass, Base, NearestVBase, NeedsVirtualOffset);
|
||||
if (!VTableAddressPoint)
|
||||
return;
|
||||
|
||||
// Compute where to store the address point.
|
||||
llvm::Value *VirtualOffset = 0;
|
||||
CharUnits NonVirtualOffset = CharUnits::Zero();
|
||||
|
||||
if (NeedsVTTParam && NearestVBase) {
|
||||
if (NeedsVirtualOffset) {
|
||||
// We need to use the virtual base offset offset because the virtual base
|
||||
// might have a different offset in the most derived class.
|
||||
VirtualOffset = CGM.getCXXABI().GetVirtualBaseClassOffset(*this,
|
||||
|
||||
@@ -1053,7 +1053,11 @@ CGDebugInfo::CreateCXXMemberFunction(const CXXMethodDecl *Method,
|
||||
|
||||
// It doesn't make sense to give a virtual destructor a vtable index,
|
||||
// since a single destructor has two entries in the vtable.
|
||||
if (!isa<CXXDestructorDecl>(Method))
|
||||
// FIXME: Add proper support for debug info for virtual calls in
|
||||
// the Microsoft ABI, where we may use multiple vptrs to make a vftable
|
||||
// lookup if we have multiple or virtual inheritance.
|
||||
if (!isa<CXXDestructorDecl>(Method) &&
|
||||
!CGM.getTarget().getCXXABI().isMicrosoft())
|
||||
VIndex = CGM.getVTableContext().getMethodVTableIndex(Method);
|
||||
ContainingType = RecordTy;
|
||||
}
|
||||
|
||||
@@ -53,9 +53,6 @@ private:
|
||||
NextFieldOffsetInChars(CharUnits::Zero()),
|
||||
LLVMStructAlignment(CharUnits::One()) { }
|
||||
|
||||
void AppendVTablePointer(BaseSubobject Base, llvm::Constant *VTable,
|
||||
const CXXRecordDecl *VTableClass);
|
||||
|
||||
void AppendField(const FieldDecl *Field, uint64_t FieldOffset,
|
||||
llvm::Constant *InitExpr);
|
||||
|
||||
@@ -72,8 +69,7 @@ private:
|
||||
|
||||
bool Build(InitListExpr *ILE);
|
||||
void Build(const APValue &Val, const RecordDecl *RD, bool IsPrimaryBase,
|
||||
llvm::Constant *VTable, const CXXRecordDecl *VTableClass,
|
||||
CharUnits BaseOffset);
|
||||
const CXXRecordDecl *VTableClass, CharUnits BaseOffset);
|
||||
llvm::Constant *Finalize(QualType Ty);
|
||||
|
||||
CharUnits getAlignment(const llvm::Constant *C) const {
|
||||
@@ -88,23 +84,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
void ConstStructBuilder::AppendVTablePointer(BaseSubobject Base,
|
||||
llvm::Constant *VTable,
|
||||
const CXXRecordDecl *VTableClass) {
|
||||
// Find the appropriate vtable within the vtable group.
|
||||
uint64_t AddressPoint =
|
||||
CGM.getVTableContext().getVTableLayout(VTableClass).getAddressPoint(Base);
|
||||
llvm::Value *Indices[] = {
|
||||
llvm::ConstantInt::get(CGM.Int64Ty, 0),
|
||||
llvm::ConstantInt::get(CGM.Int64Ty, AddressPoint)
|
||||
};
|
||||
llvm::Constant *VTableAddressPoint =
|
||||
llvm::ConstantExpr::getInBoundsGetElementPtr(VTable, Indices);
|
||||
|
||||
// Add the vtable at the start of the object.
|
||||
AppendBytes(Base.getBaseOffset(), VTableAddressPoint);
|
||||
}
|
||||
|
||||
void ConstStructBuilder::
|
||||
AppendField(const FieldDecl *Field, uint64_t FieldOffset,
|
||||
llvm::Constant *InitCst) {
|
||||
@@ -424,15 +403,19 @@ struct BaseInfo {
|
||||
}
|
||||
|
||||
void ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
|
||||
bool IsPrimaryBase, llvm::Constant *VTable,
|
||||
bool IsPrimaryBase,
|
||||
const CXXRecordDecl *VTableClass,
|
||||
CharUnits Offset) {
|
||||
const ASTRecordLayout &Layout = CGM.getContext().getASTRecordLayout(RD);
|
||||
|
||||
if (const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD)) {
|
||||
// Add a vtable pointer, if we need one and it hasn't already been added.
|
||||
if (CD->isDynamicClass() && !IsPrimaryBase)
|
||||
AppendVTablePointer(BaseSubobject(CD, Offset), VTable, VTableClass);
|
||||
if (CD->isDynamicClass() && !IsPrimaryBase) {
|
||||
llvm::Constant *VTableAddressPoint =
|
||||
CGM.getCXXABI().getVTableAddressPointForConstExpr(
|
||||
BaseSubobject(CD, Offset), VTableClass);
|
||||
AppendBytes(Offset, VTableAddressPoint);
|
||||
}
|
||||
|
||||
// Accumulate and sort bases, in order to visit them in address order, which
|
||||
// may not be the same as declaration order.
|
||||
@@ -453,7 +436,7 @@ void ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
|
||||
|
||||
bool IsPrimaryBase = Layout.getPrimaryBase() == Base.Decl;
|
||||
Build(Val.getStructBase(Base.Index), Base.Decl, IsPrimaryBase,
|
||||
VTable, VTableClass, Offset + Base.Offset);
|
||||
VTableClass, Offset + Base.Offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,11 +543,7 @@ llvm::Constant *ConstStructBuilder::BuildStruct(CodeGenModule &CGM,
|
||||
|
||||
const RecordDecl *RD = ValTy->castAs<RecordType>()->getDecl();
|
||||
const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD);
|
||||
llvm::Constant *VTable = 0;
|
||||
if (CD && CD->isDynamicClass())
|
||||
VTable = CGM.getVTables().GetAddrOfVTable(CD);
|
||||
|
||||
Builder.Build(Val, RD, false, VTable, CD, CharUnits::Zero());
|
||||
Builder.Build(Val, RD, false, CD, CharUnits::Zero());
|
||||
|
||||
return Builder.Finalize(ValTy);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@ using namespace clang;
|
||||
using namespace CodeGen;
|
||||
|
||||
static llvm::Constant *
|
||||
GetAddrOfVTTVTable(CodeGenVTables &CGVT, const CXXRecordDecl *MostDerivedClass,
|
||||
GetAddrOfVTTVTable(CodeGenVTables &CGVT, CodeGenModule &CGM,
|
||||
const CXXRecordDecl *MostDerivedClass,
|
||||
const VTTVTable &VTable,
|
||||
llvm::GlobalVariable::LinkageTypes Linkage,
|
||||
llvm::DenseMap<BaseSubobject, uint64_t> &AddressPoints) {
|
||||
@@ -27,7 +28,7 @@ GetAddrOfVTTVTable(CodeGenVTables &CGVT, const CXXRecordDecl *MostDerivedClass,
|
||||
assert(VTable.getBaseOffset().isZero() &&
|
||||
"Most derived class vtable must have a zero offset!");
|
||||
// This is a regular vtable.
|
||||
return CGVT.GetAddrOfVTable(MostDerivedClass);
|
||||
return CGM.getCXXABI().getAddrOfVTable(MostDerivedClass, CharUnits());
|
||||
}
|
||||
|
||||
return CGVT.GenerateConstructionVTable(MostDerivedClass,
|
||||
@@ -52,7 +53,7 @@ CodeGenVTables::EmitVTTDefinition(llvm::GlobalVariable *VTT,
|
||||
for (const VTTVTable *i = Builder.getVTTVTables().begin(),
|
||||
*e = Builder.getVTTVTables().end(); i != e; ++i) {
|
||||
VTableAddressPoints.push_back(VTableAddressPointsMapTy());
|
||||
VTables.push_back(GetAddrOfVTTVTable(*this, RD, *i, Linkage,
|
||||
VTables.push_back(GetAddrOfVTTVTable(*this, CGM, RD, *i, Linkage,
|
||||
VTableAddressPoints.back()));
|
||||
}
|
||||
|
||||
@@ -106,7 +107,7 @@ llvm::GlobalVariable *CodeGenVTables::GetAddrOfVTT(const CXXRecordDecl *RD) {
|
||||
StringRef Name = OutName.str();
|
||||
|
||||
// This will also defer the definition of the VTT.
|
||||
(void) GetAddrOfVTable(RD);
|
||||
(void) CGM.getCXXABI().getAddrOfVTable(RD, CharUnits());
|
||||
|
||||
VTTBuilder Builder(CGM.getContext(), RD, /*GenerateDefinition=*/false);
|
||||
|
||||
|
||||
@@ -498,6 +498,7 @@ void CodeGenVTables::EmitThunks(GlobalDecl GD)
|
||||
// FIXME: This is a temporary solution to force generation of vftables in
|
||||
// Microsoft ABI. Remove when we thread VFTableContext through CodeGen.
|
||||
VFTContext->getVFPtrOffsets(MD->getParent());
|
||||
return;
|
||||
}
|
||||
|
||||
const VTableContext::ThunkInfoVectorTy *ThunkInfoVector =
|
||||
@@ -629,53 +630,6 @@ CodeGenVTables::CreateVTableInitializer(const CXXRecordDecl *RD,
|
||||
return llvm::ConstantArray::get(ArrayType, Inits);
|
||||
}
|
||||
|
||||
llvm::GlobalVariable *CodeGenVTables::GetAddrOfVTable(const CXXRecordDecl *RD) {
|
||||
llvm::GlobalVariable *&VTable = VTables[RD];
|
||||
if (VTable)
|
||||
return VTable;
|
||||
|
||||
// Queue up this v-table for possible deferred emission.
|
||||
CGM.addDeferredVTable(RD);
|
||||
|
||||
SmallString<256> OutName;
|
||||
llvm::raw_svector_ostream Out(OutName);
|
||||
CGM.getCXXABI().getMangleContext().mangleCXXVTable(RD, Out);
|
||||
Out.flush();
|
||||
StringRef Name = OutName.str();
|
||||
|
||||
llvm::ArrayType *ArrayType =
|
||||
llvm::ArrayType::get(CGM.Int8PtrTy,
|
||||
VTContext.getVTableLayout(RD).getNumVTableComponents());
|
||||
|
||||
VTable =
|
||||
CGM.CreateOrReplaceCXXRuntimeVariable(Name, ArrayType,
|
||||
llvm::GlobalValue::ExternalLinkage);
|
||||
VTable->setUnnamedAddr(true);
|
||||
return VTable;
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenVTables::EmitVTableDefinition(llvm::GlobalVariable *VTable,
|
||||
llvm::GlobalVariable::LinkageTypes Linkage,
|
||||
const CXXRecordDecl *RD) {
|
||||
const VTableLayout &VTLayout = VTContext.getVTableLayout(RD);
|
||||
|
||||
// Create and set the initializer.
|
||||
llvm::Constant *Init =
|
||||
CreateVTableInitializer(RD,
|
||||
VTLayout.vtable_component_begin(),
|
||||
VTLayout.getNumVTableComponents(),
|
||||
VTLayout.vtable_thunk_begin(),
|
||||
VTLayout.getNumVTableThunks());
|
||||
VTable->setInitializer(Init);
|
||||
|
||||
// Set the correct linkage.
|
||||
VTable->setLinkage(Linkage);
|
||||
|
||||
// Set the right visibility.
|
||||
CGM.setTypeVisibility(VTable, RD, CodeGenModule::TVK_ForVTable);
|
||||
}
|
||||
|
||||
llvm::GlobalVariable *
|
||||
CodeGenVTables::GenerateConstructionVTable(const CXXRecordDecl *RD,
|
||||
const BaseSubobject &Base,
|
||||
@@ -818,35 +772,10 @@ CodeGenVTables::GenerateClassData(const CXXRecordDecl *RD) {
|
||||
if (CGDebugInfo *DI = CGM.getModuleDebugInfo())
|
||||
DI->completeClassData(RD);
|
||||
|
||||
if (VFTContext.isValid()) {
|
||||
// FIXME: This is a temporary solution to force generation of vftables in
|
||||
// Microsoft ABI. Remove when we thread VFTableContext through CodeGen.
|
||||
VFTContext->getVFPtrOffsets(RD);
|
||||
}
|
||||
|
||||
// First off, check whether we've already emitted the v-table and
|
||||
// associated stuff.
|
||||
llvm::GlobalVariable *VTable = GetAddrOfVTable(RD);
|
||||
if (VTable->hasInitializer())
|
||||
return;
|
||||
|
||||
llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD);
|
||||
EmitVTableDefinition(VTable, Linkage, RD);
|
||||
|
||||
if (RD->getNumVBases())
|
||||
CGM.getCXXABI().EmitVirtualInheritanceTables(Linkage, RD);
|
||||
CGM.getCXXABI().emitVirtualInheritanceTables(RD);
|
||||
|
||||
// If this is the magic class __cxxabiv1::__fundamental_type_info,
|
||||
// we will emit the typeinfo for the fundamental types. This is the
|
||||
// same behaviour as GCC.
|
||||
const DeclContext *DC = RD->getDeclContext();
|
||||
if (RD->getIdentifier() &&
|
||||
RD->getIdentifier()->isStr("__fundamental_type_info") &&
|
||||
isa<NamespaceDecl>(DC) &&
|
||||
cast<NamespaceDecl>(DC)->getIdentifier() &&
|
||||
cast<NamespaceDecl>(DC)->getIdentifier()->isStr("__cxxabiv1") &&
|
||||
DC->getParent()->isTranslationUnit())
|
||||
CGM.EmitFundamentalRTTIDescriptors();
|
||||
CGM.getCXXABI().emitVTableDefinitions(*this, RD);
|
||||
}
|
||||
|
||||
/// At this point in the translation unit, does it appear that can we
|
||||
|
||||
@@ -35,9 +35,6 @@ class CodeGenVTables {
|
||||
// classes?
|
||||
VTableContext VTContext;
|
||||
OwningPtr<MicrosoftVFTableContext> VFTContext;
|
||||
|
||||
/// VTables - All the vtables which have been defined.
|
||||
llvm::DenseMap<const CXXRecordDecl *, llvm::GlobalVariable *> VTables;
|
||||
|
||||
/// VTableAddressPointsMapTy - Address points for a single vtable.
|
||||
typedef llvm::DenseMap<BaseSubobject, uint64_t> VTableAddressPointsMapTy;
|
||||
@@ -65,6 +62,7 @@ class CodeGenVTables {
|
||||
/// doesn't contain any incomplete types.
|
||||
void MaybeEmitThunkAvailableExternally(GlobalDecl GD, const ThunkInfo &Thunk);
|
||||
|
||||
public:
|
||||
/// CreateVTableInitializer - Create a vtable initializer for the given record
|
||||
/// decl.
|
||||
/// \param Components - The vtable components; this is really an array of
|
||||
@@ -75,7 +73,6 @@ class CodeGenVTables {
|
||||
const VTableLayout::VTableThunkTy *VTableThunks,
|
||||
unsigned NumVTableThunks);
|
||||
|
||||
public:
|
||||
CodeGenVTables(CodeGenModule &CGM);
|
||||
|
||||
VTableContext &getVTableContext() { return VTContext; }
|
||||
@@ -95,14 +92,6 @@ public:
|
||||
/// class decl.
|
||||
uint64_t getAddressPoint(BaseSubobject Base, const CXXRecordDecl *RD);
|
||||
|
||||
/// GetAddrOfVTable - Get the address of the vtable for the given record decl.
|
||||
llvm::GlobalVariable *GetAddrOfVTable(const CXXRecordDecl *RD);
|
||||
|
||||
/// EmitVTableDefinition - Emit the definition of the given vtable.
|
||||
void EmitVTableDefinition(llvm::GlobalVariable *VTable,
|
||||
llvm::GlobalVariable::LinkageTypes Linkage,
|
||||
const CXXRecordDecl *RD);
|
||||
|
||||
/// GenerateConstructionVTable - Generate a construction vtable for the given
|
||||
/// base subobject.
|
||||
llvm::GlobalVariable *
|
||||
|
||||
@@ -250,7 +250,6 @@ class CodeGenModule : public CodeGenTypeCache {
|
||||
|
||||
/// VTables - Holds information about C++ vtables.
|
||||
CodeGenVTables VTables;
|
||||
friend class CodeGenVTables;
|
||||
|
||||
CGObjCRuntime* ObjCRuntime;
|
||||
CGOpenCLRuntime* OpenCLRuntime;
|
||||
@@ -915,6 +914,10 @@ public:
|
||||
|
||||
void EmitVTable(CXXRecordDecl *Class, bool DefinitionRequired);
|
||||
|
||||
/// EmitFundamentalRTTIDescriptors - Emit the RTTI descriptors for the
|
||||
/// builtin types.
|
||||
void EmitFundamentalRTTIDescriptors();
|
||||
|
||||
/// \brief Appends Opts to the "Linker Options" metadata value.
|
||||
void AppendLinkerOptions(StringRef Opts);
|
||||
|
||||
@@ -1068,10 +1071,6 @@ private:
|
||||
/// given type.
|
||||
void EmitFundamentalRTTIDescriptor(QualType Type);
|
||||
|
||||
/// EmitFundamentalRTTIDescriptors - Emit the RTTI descriptors for the
|
||||
/// builtin types.
|
||||
void EmitFundamentalRTTIDescriptors();
|
||||
|
||||
/// EmitDeferred - Emit any needed decls for which code generation
|
||||
/// was deferred.
|
||||
void EmitDeferred();
|
||||
|
||||
@@ -34,6 +34,9 @@ using namespace CodeGen;
|
||||
|
||||
namespace {
|
||||
class ItaniumCXXABI : public CodeGen::CGCXXABI {
|
||||
/// VTables - All the vtables which have been defined.
|
||||
llvm::DenseMap<const CXXRecordDecl *, llvm::GlobalVariable *> VTables;
|
||||
|
||||
protected:
|
||||
bool UseARMMethodPtrABI;
|
||||
bool UseARMGuardVarABI;
|
||||
@@ -142,6 +145,20 @@ public:
|
||||
CallExpr::const_arg_iterator ArgBeg,
|
||||
CallExpr::const_arg_iterator ArgEnd);
|
||||
|
||||
void emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD);
|
||||
|
||||
llvm::Value *getVTableAddressPointInStructor(
|
||||
CodeGenFunction &CGF, const CXXRecordDecl *VTableClass,
|
||||
BaseSubobject Base, const CXXRecordDecl *NearestVBase,
|
||||
bool &NeedsVirtualOffset);
|
||||
|
||||
llvm::Constant *
|
||||
getVTableAddressPointForConstExpr(BaseSubobject Base,
|
||||
const CXXRecordDecl *VTableClass);
|
||||
|
||||
llvm::GlobalVariable *getAddrOfVTable(const CXXRecordDecl *RD,
|
||||
CharUnits VPtrOffset);
|
||||
|
||||
llvm::Value *getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD,
|
||||
llvm::Value *This, llvm::Type *Ty);
|
||||
|
||||
@@ -150,8 +167,7 @@ public:
|
||||
CXXDtorType DtorType, SourceLocation CallLoc,
|
||||
llvm::Value *This);
|
||||
|
||||
void EmitVirtualInheritanceTables(llvm::GlobalVariable::LinkageTypes Linkage,
|
||||
const CXXRecordDecl *RD);
|
||||
void emitVirtualInheritanceTables(const CXXRecordDecl *RD);
|
||||
|
||||
StringRef GetPureVirtualCallName() { return "__cxa_pure_virtual"; }
|
||||
StringRef GetDeletedVirtualCallName() { return "__cxa_deleted_virtual"; }
|
||||
@@ -888,6 +904,113 @@ void ItaniumCXXABI::EmitConstructorCall(CodeGenFunction &CGF,
|
||||
This, VTT, VTTTy, ArgBeg, ArgEnd);
|
||||
}
|
||||
|
||||
void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
|
||||
const CXXRecordDecl *RD) {
|
||||
llvm::GlobalVariable *VTable = getAddrOfVTable(RD, CharUnits());
|
||||
if (VTable->hasInitializer())
|
||||
return;
|
||||
|
||||
VTableContext &VTContext = CGM.getVTableContext();
|
||||
const VTableLayout &VTLayout = VTContext.getVTableLayout(RD);
|
||||
llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD);
|
||||
|
||||
// Create and set the initializer.
|
||||
llvm::Constant *Init = CGVT.CreateVTableInitializer(
|
||||
RD, VTLayout.vtable_component_begin(), VTLayout.getNumVTableComponents(),
|
||||
VTLayout.vtable_thunk_begin(), VTLayout.getNumVTableThunks());
|
||||
VTable->setInitializer(Init);
|
||||
|
||||
// Set the correct linkage.
|
||||
VTable->setLinkage(Linkage);
|
||||
|
||||
// Set the right visibility.
|
||||
CGM.setTypeVisibility(VTable, RD, CodeGenModule::TVK_ForVTable);
|
||||
|
||||
// If this is the magic class __cxxabiv1::__fundamental_type_info,
|
||||
// we will emit the typeinfo for the fundamental types. This is the
|
||||
// same behaviour as GCC.
|
||||
const DeclContext *DC = RD->getDeclContext();
|
||||
if (RD->getIdentifier() &&
|
||||
RD->getIdentifier()->isStr("__fundamental_type_info") &&
|
||||
isa<NamespaceDecl>(DC) && cast<NamespaceDecl>(DC)->getIdentifier() &&
|
||||
cast<NamespaceDecl>(DC)->getIdentifier()->isStr("__cxxabiv1") &&
|
||||
DC->getParent()->isTranslationUnit())
|
||||
CGM.EmitFundamentalRTTIDescriptors();
|
||||
}
|
||||
|
||||
llvm::Value *ItaniumCXXABI::getVTableAddressPointInStructor(
|
||||
CodeGenFunction &CGF, const CXXRecordDecl *VTableClass, BaseSubobject Base,
|
||||
const CXXRecordDecl *NearestVBase, bool &NeedsVirtualOffset) {
|
||||
bool NeedsVTTParam = CGM.getCXXABI().NeedsVTTParameter(CGF.CurGD);
|
||||
NeedsVirtualOffset = (NeedsVTTParam && NearestVBase);
|
||||
|
||||
llvm::Value *VTableAddressPoint;
|
||||
if (NeedsVTTParam && (Base.getBase()->getNumVBases() || NearestVBase)) {
|
||||
// Get the secondary vpointer index.
|
||||
uint64_t VirtualPointerIndex =
|
||||
CGM.getVTables().getSecondaryVirtualPointerIndex(VTableClass, Base);
|
||||
|
||||
/// Load the VTT.
|
||||
llvm::Value *VTT = CGF.LoadCXXVTT();
|
||||
if (VirtualPointerIndex)
|
||||
VTT = CGF.Builder.CreateConstInBoundsGEP1_64(VTT, VirtualPointerIndex);
|
||||
|
||||
// And load the address point from the VTT.
|
||||
VTableAddressPoint = CGF.Builder.CreateLoad(VTT);
|
||||
} else {
|
||||
llvm::Constant *VTable =
|
||||
CGM.getCXXABI().getAddrOfVTable(VTableClass, CharUnits());
|
||||
uint64_t AddressPoint = CGM.getVTableContext().getVTableLayout(VTableClass)
|
||||
.getAddressPoint(Base);
|
||||
VTableAddressPoint =
|
||||
CGF.Builder.CreateConstInBoundsGEP2_64(VTable, 0, AddressPoint);
|
||||
}
|
||||
|
||||
return VTableAddressPoint;
|
||||
}
|
||||
|
||||
llvm::Constant *ItaniumCXXABI::getVTableAddressPointForConstExpr(
|
||||
BaseSubobject Base, const CXXRecordDecl *VTableClass) {
|
||||
llvm::Constant *VTable = getAddrOfVTable(VTableClass, CharUnits());
|
||||
|
||||
// Find the appropriate vtable within the vtable group.
|
||||
uint64_t AddressPoint =
|
||||
CGM.getVTableContext().getVTableLayout(VTableClass).getAddressPoint(Base);
|
||||
llvm::Value *Indices[] = {
|
||||
llvm::ConstantInt::get(CGM.Int64Ty, 0),
|
||||
llvm::ConstantInt::get(CGM.Int64Ty, AddressPoint)
|
||||
};
|
||||
|
||||
return llvm::ConstantExpr::getInBoundsGetElementPtr(VTable, Indices);
|
||||
}
|
||||
|
||||
llvm::GlobalVariable *ItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *RD,
|
||||
CharUnits VPtrOffset) {
|
||||
assert(VPtrOffset.isZero() && "Itanium ABI only supports zero vptr offsets");
|
||||
|
||||
llvm::GlobalVariable *&VTable = VTables[RD];
|
||||
if (VTable)
|
||||
return VTable;
|
||||
|
||||
// Queue up this v-table for possible deferred emission.
|
||||
CGM.addDeferredVTable(RD);
|
||||
|
||||
SmallString<256> OutName;
|
||||
llvm::raw_svector_ostream Out(OutName);
|
||||
CGM.getCXXABI().getMangleContext().mangleCXXVTable(RD, Out);
|
||||
Out.flush();
|
||||
StringRef Name = OutName.str();
|
||||
|
||||
VTableContext &VTContext = CGM.getVTableContext();
|
||||
llvm::ArrayType *ArrayType = llvm::ArrayType::get(
|
||||
CGM.Int8PtrTy, VTContext.getVTableLayout(RD).getNumVTableComponents());
|
||||
|
||||
VTable = CGM.CreateOrReplaceCXXRuntimeVariable(
|
||||
Name, ArrayType, llvm::GlobalValue::ExternalLinkage);
|
||||
VTable->setUnnamedAddr(true);
|
||||
return VTable;
|
||||
}
|
||||
|
||||
llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
|
||||
GlobalDecl GD,
|
||||
llvm::Value *This,
|
||||
@@ -919,11 +1042,10 @@ void ItaniumCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
|
||||
/*ImplicitParam=*/0, QualType(), 0, 0);
|
||||
}
|
||||
|
||||
void ItaniumCXXABI::EmitVirtualInheritanceTables(
|
||||
llvm::GlobalVariable::LinkageTypes Linkage, const CXXRecordDecl *RD) {
|
||||
void ItaniumCXXABI::emitVirtualInheritanceTables(const CXXRecordDecl *RD) {
|
||||
CodeGenVTables &VTables = CGM.getVTables();
|
||||
llvm::GlobalVariable *VTT = VTables.GetAddrOfVTT(RD);
|
||||
VTables.EmitVTTDefinition(VTT, Linkage, RD);
|
||||
VTables.EmitVTTDefinition(VTT, CGM.getVTableLinkage(RD), RD);
|
||||
}
|
||||
|
||||
void ARMCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF,
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/VTableBuilder.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace CodeGen;
|
||||
@@ -150,6 +151,20 @@ public:
|
||||
CallExpr::const_arg_iterator ArgBeg,
|
||||
CallExpr::const_arg_iterator ArgEnd);
|
||||
|
||||
void emitVTableDefinitions(CodeGenVTables &CGVT, const CXXRecordDecl *RD);
|
||||
|
||||
llvm::Value *getVTableAddressPointInStructor(
|
||||
CodeGenFunction &CGF, const CXXRecordDecl *VTableClass,
|
||||
BaseSubobject Base, const CXXRecordDecl *NearestVBase,
|
||||
bool &NeedsVirtualOffset);
|
||||
|
||||
llvm::Constant *
|
||||
getVTableAddressPointForConstExpr(BaseSubobject Base,
|
||||
const CXXRecordDecl *VTableClass);
|
||||
|
||||
llvm::GlobalVariable *getAddrOfVTable(const CXXRecordDecl *RD,
|
||||
CharUnits VPtrOffset);
|
||||
|
||||
llvm::Value *getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD,
|
||||
llvm::Value *This, llvm::Type *Ty);
|
||||
|
||||
@@ -158,8 +173,7 @@ public:
|
||||
CXXDtorType DtorType, SourceLocation CallLoc,
|
||||
llvm::Value *This);
|
||||
|
||||
void EmitVirtualInheritanceTables(llvm::GlobalVariable::LinkageTypes Linkage,
|
||||
const CXXRecordDecl *RD);
|
||||
void emitVirtualInheritanceTables(const CXXRecordDecl *RD);
|
||||
|
||||
void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
|
||||
llvm::GlobalVariable *DeclPtr,
|
||||
@@ -303,7 +317,16 @@ public:
|
||||
const MemberPointerType *MPT);
|
||||
|
||||
private:
|
||||
/// VBTables - All the vbtables which have been referenced.
|
||||
typedef std::pair<const CXXRecordDecl *, CharUnits> VFTableIdTy;
|
||||
typedef llvm::DenseMap<VFTableIdTy, llvm::GlobalVariable *> VFTablesMapTy;
|
||||
/// \brief All the vftables that have been referenced.
|
||||
VFTablesMapTy VFTablesMap;
|
||||
|
||||
/// \brief This set holds the record decls we've deferred vtable emission for.
|
||||
llvm::SmallPtrSet<const CXXRecordDecl *, 4> DeferredVFTables;
|
||||
|
||||
|
||||
/// \brief All the vbtables which have been referenced.
|
||||
llvm::DenseMap<const CXXRecordDecl *, VBTableVector> VBTablesMap;
|
||||
|
||||
/// Info on the global variable used to guard initialization of static locals.
|
||||
@@ -616,6 +639,116 @@ void MicrosoftCXXABI::EmitConstructorCall(CodeGenFunction &CGF,
|
||||
ImplicitParam, ImplicitParamTy, ArgBeg, ArgEnd);
|
||||
}
|
||||
|
||||
void MicrosoftCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
|
||||
const CXXRecordDecl *RD) {
|
||||
MicrosoftVFTableContext &VFTContext = CGM.getVFTableContext();
|
||||
MicrosoftVFTableContext::VFPtrListTy VFPtrs = VFTContext.getVFPtrOffsets(RD);
|
||||
llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD);
|
||||
|
||||
for (MicrosoftVFTableContext::VFPtrListTy::iterator I = VFPtrs.begin(),
|
||||
E = VFPtrs.end(); I != E; ++I) {
|
||||
llvm::GlobalVariable *VTable = getAddrOfVTable(RD, I->VFPtrFullOffset);
|
||||
if (VTable->hasInitializer())
|
||||
continue;
|
||||
|
||||
const VTableLayout &VTLayout =
|
||||
VFTContext.getVFTableLayout(RD, I->VFPtrFullOffset);
|
||||
llvm::Constant *Init = CGVT.CreateVTableInitializer(
|
||||
RD, VTLayout.vtable_component_begin(),
|
||||
VTLayout.getNumVTableComponents(), VTLayout.vtable_thunk_begin(),
|
||||
VTLayout.getNumVTableThunks());
|
||||
VTable->setInitializer(Init);
|
||||
|
||||
VTable->setLinkage(Linkage);
|
||||
CGM.setTypeVisibility(VTable, RD, CodeGenModule::TVK_ForVTable);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Value *MicrosoftCXXABI::getVTableAddressPointInStructor(
|
||||
CodeGenFunction &CGF, const CXXRecordDecl *VTableClass, BaseSubobject Base,
|
||||
const CXXRecordDecl *NearestVBase, bool &NeedsVirtualOffset) {
|
||||
NeedsVirtualOffset = (NearestVBase != 0);
|
||||
|
||||
llvm::Value *VTableAddressPoint =
|
||||
getAddrOfVTable(VTableClass, Base.getBaseOffset());
|
||||
if (!VTableAddressPoint) {
|
||||
assert(Base.getBase()->getNumVBases() &&
|
||||
!CGM.getContext().getASTRecordLayout(Base.getBase()).hasOwnVFPtr());
|
||||
}
|
||||
return VTableAddressPoint;
|
||||
}
|
||||
|
||||
static void mangleVFTableName(CodeGenModule &CGM, const CXXRecordDecl *RD,
|
||||
const VFPtrInfo &VFPtr, SmallString<256> &Name) {
|
||||
llvm::raw_svector_ostream Out(Name);
|
||||
CGM.getCXXABI().getMangleContext().mangleCXXVFTable(
|
||||
RD, VFPtr.PathToMangle, Out);
|
||||
}
|
||||
|
||||
llvm::Constant *MicrosoftCXXABI::getVTableAddressPointForConstExpr(
|
||||
BaseSubobject Base, const CXXRecordDecl *VTableClass) {
|
||||
llvm::Constant *VTable = getAddrOfVTable(VTableClass, Base.getBaseOffset());
|
||||
assert(VTable && "Couldn't find a vftable for the given base?");
|
||||
return VTable;
|
||||
}
|
||||
|
||||
llvm::GlobalVariable *MicrosoftCXXABI::getAddrOfVTable(const CXXRecordDecl *RD,
|
||||
CharUnits VPtrOffset) {
|
||||
// getAddrOfVTable may return 0 if asked to get an address of a vtable which
|
||||
// shouldn't be used in the given record type. We want to cache this result in
|
||||
// VFTablesMap, thus a simple zero check is not sufficient.
|
||||
VFTableIdTy ID(RD, VPtrOffset);
|
||||
VFTablesMapTy::iterator I;
|
||||
bool Inserted;
|
||||
llvm::tie(I, Inserted) = VFTablesMap.insert(
|
||||
std::make_pair(ID, static_cast<llvm::GlobalVariable *>(0)));
|
||||
if (!Inserted)
|
||||
return I->second;
|
||||
|
||||
llvm::GlobalVariable *&VTable = I->second;
|
||||
|
||||
MicrosoftVFTableContext &VFTContext = CGM.getVFTableContext();
|
||||
const MicrosoftVFTableContext::VFPtrListTy &VFPtrs =
|
||||
VFTContext.getVFPtrOffsets(RD);
|
||||
|
||||
if (DeferredVFTables.insert(RD)) {
|
||||
// We haven't processed this record type before.
|
||||
// Queue up this v-table for possible deferred emission.
|
||||
CGM.addDeferredVTable(RD);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Create all the vftables at once in order to make sure each vftable has
|
||||
// a unique mangled name.
|
||||
llvm::StringSet<> ObservedMangledNames;
|
||||
for (size_t J = 0, F = VFPtrs.size(); J != F; ++J) {
|
||||
SmallString<256> Name;
|
||||
mangleVFTableName(CGM, RD, VFPtrs[J], Name);
|
||||
if (!ObservedMangledNames.insert(Name.str()))
|
||||
llvm_unreachable("Already saw this mangling before?");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (size_t J = 0, F = VFPtrs.size(); J != F; ++J) {
|
||||
if (VFPtrs[J].VFPtrFullOffset != VPtrOffset)
|
||||
continue;
|
||||
|
||||
llvm::ArrayType *ArrayType = llvm::ArrayType::get(
|
||||
CGM.Int8PtrTy,
|
||||
VFTContext.getVFTableLayout(RD, VFPtrs[J].VFPtrFullOffset)
|
||||
.getNumVTableComponents());
|
||||
|
||||
SmallString<256> Name;
|
||||
mangleVFTableName(CGM, RD, VFPtrs[J], Name);
|
||||
VTable = CGM.CreateOrReplaceCXXRuntimeVariable(
|
||||
Name.str(), ArrayType, llvm::GlobalValue::ExternalLinkage);
|
||||
VTable->setUnnamedAddr(true);
|
||||
break;
|
||||
}
|
||||
|
||||
return VTable;
|
||||
}
|
||||
|
||||
llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
|
||||
GlobalDecl GD,
|
||||
llvm::Value *This,
|
||||
@@ -674,9 +807,10 @@ MicrosoftCXXABI::EnumerateVBTables(const CXXRecordDecl *RD) {
|
||||
return VBTables;
|
||||
}
|
||||
|
||||
void MicrosoftCXXABI::EmitVirtualInheritanceTables(
|
||||
llvm::GlobalVariable::LinkageTypes Linkage, const CXXRecordDecl *RD) {
|
||||
void MicrosoftCXXABI::emitVirtualInheritanceTables(const CXXRecordDecl *RD) {
|
||||
const VBTableVector &VBTables = EnumerateVBTables(RD);
|
||||
llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD);
|
||||
|
||||
for (VBTableVector::const_iterator I = VBTables.begin(), E = VBTables.end();
|
||||
I != E; ++I) {
|
||||
I->EmitVBTableDefinition(CGM, RD, Linkage);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// RUN: %clang_cc1 -std=c++11 -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
|
||||
|
||||
struct A {
|
||||
constexpr A(int x) : x(x) {}
|
||||
virtual void f();
|
||||
int x;
|
||||
};
|
||||
|
||||
A a(42);
|
||||
// CHECK: @"\01?a@@3UA@@A" = global { [1 x i8*]*, i32 } { [1 x i8*]* @"\01??_7A@@6B@", i32 42 }, align 4
|
||||
|
||||
struct B {
|
||||
constexpr B(int y) : y(y) {}
|
||||
virtual void g();
|
||||
int y;
|
||||
};
|
||||
|
||||
struct C : A, B {
|
||||
constexpr C() : A(777), B(13) {}
|
||||
};
|
||||
|
||||
C c;
|
||||
// CHECK: @"\01?c@@3UC@@A" = global { [1 x i8*]*, i32, [1 x i8*]*, i32 } { [1 x i8*]* @"\01??_7C@@6BA@@@", i32 777, [1 x i8*]* @"\01??_7C@@6BB@@@", i32 13 }
|
||||
@@ -145,3 +145,39 @@ void call_grandchild_right(GrandchildOverride *obj) {
|
||||
// Just make sure we don't crash.
|
||||
obj->right();
|
||||
}
|
||||
|
||||
void emit_ctors() {
|
||||
Left l;
|
||||
// CHECK: define {{.*}} @"\01??0Left@@QAE@XZ"
|
||||
// CHECK-NOT: getelementptr
|
||||
// CHECK: store [1 x i8*]* @"\01??_7Left@@6B@"
|
||||
// CHECK: ret
|
||||
|
||||
Right r;
|
||||
// CHECK: define {{.*}} @"\01??0Right@@QAE@XZ"
|
||||
// CHECK-NOT: getelementptr
|
||||
// CHECK: store [1 x i8*]* @"\01??_7Right@@6B@"
|
||||
// CHECK: ret
|
||||
|
||||
ChildOverride co;
|
||||
// CHECK: define {{.*}} @"\01??0ChildOverride@@QAE@XZ"
|
||||
// CHECK: %[[THIS:.*]] = load %struct.ChildOverride**
|
||||
// CHECK: %[[VFPTR:.*]] = bitcast %struct.ChildOverride* %this1 to [1 x i8*]**
|
||||
// CHECK: store [1 x i8*]* @"\01??_7ChildOverride@@6BLeft@@@", [1 x i8*]** %[[VFPTR]]
|
||||
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.ChildOverride* %[[THIS]] to i8*
|
||||
// CHECK: %[[VFPTR_i8:.*]] = getelementptr inbounds i8* %[[THIS_i8]], i32 4
|
||||
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VFPTR_i8]] to [1 x i8*]**
|
||||
// CHECK: store [1 x i8*]* @"\01??_7ChildOverride@@6BRight@@@", [1 x i8*]** %[[VFPTR]]
|
||||
// CHECK: ret
|
||||
|
||||
GrandchildOverride gc;
|
||||
// CHECK: define {{.*}} @"\01??0GrandchildOverride@@QAE@XZ"
|
||||
// CHECK: %[[THIS:.*]] = load %struct.GrandchildOverride**
|
||||
// CHECK: %[[VFPTR:.*]] = bitcast %struct.GrandchildOverride* %this1 to [1 x i8*]**
|
||||
// CHECK: store [1 x i8*]* @"\01??_7GrandchildOverride@@6BLeft@@@", [1 x i8*]** %[[VFPTR]]
|
||||
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.GrandchildOverride* %[[THIS]] to i8*
|
||||
// CHECK: %[[VFPTR_i8:.*]] = getelementptr inbounds i8* %[[THIS_i8]], i32 4
|
||||
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VFPTR_i8]] to [1 x i8*]**
|
||||
// CHECK: store [1 x i8*]* @"\01??_7GrandchildOverride@@6BRight@@@", [1 x i8*]** %[[VFPTR]]
|
||||
// CHECK: ret
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ void C::foo() {}
|
||||
void check_vftable_offset() {
|
||||
C c;
|
||||
// The vftable pointer should point at the beginning of the vftable.
|
||||
// CHECK: [[THIS_PTR:%[0-9]+]] = bitcast %"struct.basic::C"* {{.*}} to i8***
|
||||
// CHECK: store i8** getelementptr inbounds ([2 x i8*]* @"\01??_7C@basic@@6B@", i64 0, i64 0), i8*** [[THIS_PTR]]
|
||||
// CHECK: [[THIS_PTR:%[0-9]+]] = bitcast %"struct.basic::C"* {{.*}} to [2 x i8*]**
|
||||
// CHECK: store [2 x i8*]* @"\01??_7C@basic@@6B@", [2 x i8*]** [[THIS_PTR]]
|
||||
}
|
||||
|
||||
void call_complete_dtor(C *obj_ptr) {
|
||||
@@ -161,7 +161,8 @@ C::C() {
|
||||
// CHECK-NEXT: br label %[[SKIP_VBASES]]
|
||||
//
|
||||
// CHECK: [[SKIP_VBASES]]
|
||||
// CHECK: @"\01??_7C@constructors@@6B@"
|
||||
// Class C does not define or override methods, so shouldn't change the vfptr.
|
||||
// CHECK-NOT: @"\01??_7C@constructors@@6B@"
|
||||
// CHECK: ret
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,26 @@ struct VBase {
|
||||
};
|
||||
|
||||
struct B : virtual VBase {
|
||||
B();
|
||||
virtual void foo();
|
||||
virtual void bar();
|
||||
};
|
||||
|
||||
B::B() {
|
||||
// CHECK: @"\01??0B@@QAE@XZ"
|
||||
// CHECK: %[[THIS:.*]] = load %struct.B**
|
||||
// CHECK: br i1 %{{.*}}, label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]]
|
||||
// CHECK: %[[SKIP_VBASES]]
|
||||
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
||||
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8* %[[THIS_i8]], i32 0
|
||||
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
||||
// CHECK: %[[VFPTR_i8:.*]] = getelementptr inbounds i8* %[[THIS_i8]], i32 %{{.*}}
|
||||
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VFPTR_i8]] to [2 x i8*]**
|
||||
// CHECK: store [2 x i8*]* @"\01??_7B@@6B@", [2 x i8*]** %[[VFPTR]]
|
||||
// FIXME: Should initialize the vtorDisp here.
|
||||
// CHECK: ret
|
||||
}
|
||||
|
||||
void B::foo() {
|
||||
// CHECK: define x86_thiscallcc void @"\01?foo@B@@UAEXXZ"(i8*
|
||||
//
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
// RUN: FileCheck --check-prefix=RET-THUNKS-Test4 %s < %t
|
||||
// RUN: FileCheck --check-prefix=RET-THUNKS-Test5 %s < %t
|
||||
|
||||
// RUN: FileCheck --check-prefix=MANGLING %s < %t
|
||||
|
||||
struct Empty {
|
||||
// Doesn't have a vftable!
|
||||
};
|
||||
@@ -51,6 +53,9 @@ struct Test1: A, B {
|
||||
// NO-THUNKS-Test1: VFTable indices for 'no_thunks::Test1' (1 entries)
|
||||
// NO-THUNKS-Test1-NEXT: 0 | void no_thunks::Test1::f()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Test1@no_thunks@@6BA@@@"
|
||||
// MANGLING-DAG: @"\01??_7Test1@no_thunks@@6BB@@@"
|
||||
|
||||
// Overrides only the left child's method (A::f), needs no thunks.
|
||||
virtual void f();
|
||||
};
|
||||
@@ -102,6 +107,8 @@ struct Test4 : Empty, A {
|
||||
// NO-THUNKS-Test4: VFTable indices for 'no_thunks::Test4' (1 entries).
|
||||
// NO-THUNKS-Test4-NEXT: 0 | void no_thunks::Test4::f()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Test4@no_thunks@@6B@"
|
||||
|
||||
virtual void f();
|
||||
};
|
||||
|
||||
@@ -127,6 +134,11 @@ struct Test5: Test1, Test2 {
|
||||
// NO-THUNKS-Test5: VFTable indices for 'no_thunks::Test5' (1 entries).
|
||||
// NO-THUNKS-Test5-NEXT: 1 | void no_thunks::Test5::z()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Test5@no_thunks@@6BA@@Test1@1@@"
|
||||
// MANGLING-DAG: @"\01??_7Test5@no_thunks@@6BA@@Test2@1@@"
|
||||
// MANGLING-DAG: @"\01??_7Test5@no_thunks@@6BB@@Test1@1@@"
|
||||
// MANGLING-DAG: @"\01??_7Test5@no_thunks@@6BB@@Test2@1@@"
|
||||
|
||||
virtual void z();
|
||||
};
|
||||
|
||||
@@ -143,6 +155,9 @@ struct Test6: Test1 {
|
||||
// NO-THUNKS-Test6: VFTable indices for 'no_thunks::Test6' (1 entries).
|
||||
// NO-THUNKS-Test6-NEXT: 0 | void no_thunks::Test6::f()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Test6@no_thunks@@6BA@@@"
|
||||
// MANGLING-DAG: @"\01??_7Test6@no_thunks@@6BB@@@"
|
||||
|
||||
// Overrides both no_thunks::Test1::f and A::f.
|
||||
virtual void f();
|
||||
};
|
||||
@@ -203,6 +218,9 @@ struct Test9: A, D {
|
||||
// NO-THUNKS-Test9: VFTable indices for 'no_thunks::Test9' (1 entries).
|
||||
// NO-THUNKS-Test9-NEXT: 1 | void no_thunks::Test9::h()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Test9@no_thunks@@6BA@@@"
|
||||
// MANGLING-DAG: @"\01??_7Test9@no_thunks@@6BD@1@@"
|
||||
|
||||
virtual void h();
|
||||
};
|
||||
|
||||
@@ -228,6 +246,9 @@ struct Test1: A, D {
|
||||
// PURE-VIRTUAL-Test1-NEXT: via vfptr at offset 4
|
||||
// PURE-VIRTUAL-Test1-NEXT: 0 | void pure_virtual::Test1::g()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Test1@pure_virtual@@6BA@@@"
|
||||
// MANGLING-DAG: @"\01??_7Test1@pure_virtual@@6BD@1@@"
|
||||
|
||||
// Overrides only the right child's method (pure_virtual::D::g), needs this adjustment but
|
||||
// not thunks.
|
||||
virtual void g();
|
||||
@@ -254,6 +275,9 @@ struct Test1 : B, C {
|
||||
// THIS-THUNKS-Test1: VFTable indices for 'this_adjustment::Test1' (1 entries).
|
||||
// THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Test1@this_adjustment@@6BB@@@"
|
||||
// MANGLING-DAG: @"\01??_7Test1@this_adjustment@@6BC@@@"
|
||||
|
||||
virtual void g();
|
||||
};
|
||||
|
||||
@@ -278,6 +302,10 @@ struct Test2 : A, B, C {
|
||||
// THIS-THUNKS-Test2-NEXT: via vfptr at offset 4
|
||||
// THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Test2@this_adjustment@@6BA@@@"
|
||||
// MANGLING-DAG: @"\01??_7Test2@this_adjustment@@6BB@@@"
|
||||
// MANGLING-DAG: @"\01??_7Test2@this_adjustment@@6BC@@@"
|
||||
|
||||
virtual void g();
|
||||
};
|
||||
|
||||
@@ -337,6 +365,8 @@ struct Test1 : Ret1 {
|
||||
// RET-THUNKS-Test1: VFTable indices for 'return_adjustment::Test1' (1 entries).
|
||||
// RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Test1@return_adjustment@@6B@"
|
||||
|
||||
virtual this_adjustment::Test1* foo();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=i386-pc-win32 -emit-llvm -fdump-vtable-layouts -o - > %t 2>&1
|
||||
// RUN: FileCheck --check-prefix=EMITS-VTABLE %s < %t
|
||||
// RUN: FileCheck --check-prefix=NO-VTABLE %s < %t
|
||||
// RUN: FileCheck --check-prefix=EMITS-VFTABLE %s < %t
|
||||
// RUN: FileCheck --check-prefix=NO-VFTABLE %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-A %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-B %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-C %s < %t
|
||||
@@ -10,19 +10,12 @@
|
||||
// RUN: FileCheck --check-prefix=CHECK-G %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-I %s < %t
|
||||
|
||||
// FIXME: Currently, we only test VFTableContext in the AST, but still use
|
||||
// VTableContext for CodeGen. We should remove the "Vtable" checks below when we
|
||||
// completely switch from VTableContext to VFTableContext.
|
||||
// Currently, the order of Vtable vs VFTable output depends on whether the
|
||||
// v*table info was required by a constructor or a method definition.
|
||||
|
||||
struct A {
|
||||
// CHECK-A: VFTable for 'A' (3 entries)
|
||||
// CHECK-A-NEXT: 0 | void A::f()
|
||||
// CHECK-A-NEXT: 1 | void A::g()
|
||||
// CHECK-A-NEXT: 2 | void A::h()
|
||||
|
||||
// CHECK-A: Vtable for 'A' (3 entries)
|
||||
// CHECK-A: VFTable indices for 'A' (3 entries)
|
||||
// CHECK-A-NEXT: 0 | void A::f()
|
||||
// CHECK-A-NEXT: 1 | void A::g()
|
||||
// CHECK-A-NEXT: 2 | void A::h()
|
||||
@@ -33,20 +26,17 @@ struct A {
|
||||
int ia;
|
||||
};
|
||||
A a;
|
||||
// EMITS-VTABLE-DAG: @"\01??_7A@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*]
|
||||
// EMITS-VFTABLE-DAG: @"\01??_7A@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*]
|
||||
|
||||
struct B : A {
|
||||
// CHECK-B: Vtable for 'B' (5 entries)
|
||||
// CHECK-B: VFTable for 'A' in 'B' (5 entries)
|
||||
// CHECK-B-NEXT: 0 | void B::f()
|
||||
// CHECK-B-NEXT: 1 | void A::g()
|
||||
// CHECK-B-NEXT: 2 | void A::h()
|
||||
// CHECK-B-NEXT: 3 | void B::i()
|
||||
// CHECK-B-NEXT: 4 | void B::j()
|
||||
|
||||
// CHECK-B: VFTable for 'A' in 'B' (5 entries)
|
||||
// CHECK-B: VFTable indices for 'B' (3 entries)
|
||||
// CHECK-B-NEXT: 0 | void B::f()
|
||||
// CHECK-B-NEXT: 1 | void A::g()
|
||||
// CHECK-B-NEXT: 2 | void A::h()
|
||||
// CHECK-B-NEXT: 3 | void B::i()
|
||||
// CHECK-B-NEXT: 4 | void B::j()
|
||||
|
||||
@@ -55,7 +45,7 @@ struct B : A {
|
||||
virtual void j();
|
||||
};
|
||||
B b;
|
||||
// EMITS-VTABLE-DAG: @"\01??_7B@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
|
||||
// EMITS-VFTABLE-DAG: @"\01??_7B@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
|
||||
|
||||
struct C {
|
||||
// CHECK-C: VFTable for 'C' (2 entries)
|
||||
@@ -65,25 +55,17 @@ struct C {
|
||||
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
|
||||
// CHECK-C-NEXT: 1 | void C::f()
|
||||
|
||||
// CHECK-C: Vtable for 'C' (2 entries)
|
||||
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
|
||||
// CHECK-C-NEXT: 1 | void C::f()
|
||||
// CHECK-C: VTable indices for 'C' (2 entries).
|
||||
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
|
||||
// CHECK-C-NEXT: 1 | void C::f()
|
||||
|
||||
virtual ~C();
|
||||
virtual void f();
|
||||
};
|
||||
void C::f() {}
|
||||
// NO-VTABLE-NOT: @"\01??_7C@@6B@"
|
||||
// NO-VFTABLE-NOT: @"\01??_7C@@6B@"
|
||||
|
||||
struct D {
|
||||
// CHECK-D: Vtable for 'D' (2 entries)
|
||||
// CHECK-D: VFTable for 'D' (2 entries)
|
||||
// CHECK-D-NEXT: 0 | void D::f()
|
||||
// CHECK-D-NEXT: 1 | D::~D() [scalar deleting]
|
||||
|
||||
// CHECK-D: VFTable for 'D' (2 entries)
|
||||
// CHECK-D: VFTable indices for 'D' (2 entries)
|
||||
// CHECK-D-NEXT: 0 | void D::f()
|
||||
// CHECK-D-NEXT: 1 | D::~D() [scalar deleting]
|
||||
|
||||
@@ -91,7 +73,7 @@ struct D {
|
||||
virtual ~D();
|
||||
};
|
||||
D d;
|
||||
// EMITS-VTABLE-DAG: @"\01??_7D@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*]
|
||||
// EMITS-VFTABLE-DAG: @"\01??_7D@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*]
|
||||
|
||||
struct E : A {
|
||||
// CHECK-E: VFTable for 'A' in 'E' (5 entries)
|
||||
@@ -104,35 +86,15 @@ struct E : A {
|
||||
// CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
|
||||
// CHECK-E-NEXT: 4 | void E::i()
|
||||
|
||||
// CHECK-E: Vtable for 'E' (5 entries)
|
||||
// CHECK-E-NEXT: 0 | void A::f()
|
||||
// CHECK-E-NEXT: 1 | void A::g()
|
||||
// CHECK-E-NEXT: 2 | void A::h()
|
||||
// CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
|
||||
// CHECK-E-NEXT: 4 | void E::i()
|
||||
// CHECK-E: VTable indices for 'E' (2 entries).
|
||||
// CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
|
||||
// CHECK-E-NEXT: 4 | void E::i()
|
||||
|
||||
// ~E would be the key method, but it isn't used, and MS ABI has no key
|
||||
// methods.
|
||||
virtual ~E();
|
||||
virtual void i();
|
||||
};
|
||||
void E::i() {}
|
||||
// NO-VTABLE-NOT: @"\01??_7E@@6B@"
|
||||
// NO-VFTABLE-NOT: @"\01??_7E@@6B@"
|
||||
|
||||
struct F : A {
|
||||
// CHECK-F: Vtable for 'F' (5 entries)
|
||||
// CHECK-F-NEXT: 0 | void A::f()
|
||||
// CHECK-F-NEXT: 1 | void A::g()
|
||||
// CHECK-F-NEXT: 2 | void A::h()
|
||||
// CHECK-F-NEXT: 3 | void F::i()
|
||||
// CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
|
||||
// CHECK-F: VTable indices for 'F' (2 entries).
|
||||
// CHECK-F-NEXT: 3 | void F::i()
|
||||
// CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
|
||||
|
||||
// CHECK-F: VFTable for 'A' in 'F' (5 entries)
|
||||
// CHECK-F-NEXT: 0 | void A::f()
|
||||
// CHECK-F-NEXT: 1 | void A::g()
|
||||
@@ -147,7 +109,7 @@ struct F : A {
|
||||
virtual ~F();
|
||||
};
|
||||
F f;
|
||||
// EMITS-VTABLE-DAG: @"\01??_7F@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
|
||||
// EMITS-VFTABLE-DAG: @"\01??_7F@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
|
||||
|
||||
struct G : E {
|
||||
// CHECK-G: VFTable for 'A' in 'E' in 'G' (6 entries)
|
||||
@@ -162,31 +124,19 @@ struct G : E {
|
||||
// CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
|
||||
// CHECK-G-NEXT: 5 | void G::j()
|
||||
|
||||
// CHECK-G: Vtable for 'G' (6 entries)
|
||||
// CHECK-G-NEXT: 0 | void G::f()
|
||||
// CHECK-G-NEXT: 1 | void A::g()
|
||||
// CHECK-G-NEXT: 2 | void A::h()
|
||||
// CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
|
||||
// CHECK-G-NEXT: 4 | void E::i()
|
||||
// CHECK-G-NEXT: 5 | void G::j()
|
||||
// CHECK-G: VTable indices for 'G' (3 entries).
|
||||
// CHECK-G-NEXT: 0 | void G::f()
|
||||
// CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
|
||||
// CHECK-G-NEXT: 5 | void G::j()
|
||||
|
||||
virtual void f(); // overrides A::f()
|
||||
virtual ~G();
|
||||
virtual void j();
|
||||
};
|
||||
void G::j() {}
|
||||
// NO-VTABLE-NOT: @"\01??_7G@@6B@"
|
||||
// NO-VFTABLE-NOT: @"\01??_7G@@6B@"
|
||||
|
||||
// Test that the usual Itanium-style key method does not emit a vtable.
|
||||
struct H {
|
||||
virtual void f();
|
||||
};
|
||||
void H::f() {}
|
||||
// NO-VTABLE-NOT: @"\01??_7H@@6B@"
|
||||
// NO-VFTABLE-NOT: @"\01??_7H@@6B@"
|
||||
|
||||
struct Empty { };
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
// RUN: FileCheck --check-prefix=RET-W %s < %t
|
||||
// RUN: FileCheck --check-prefix=RET-T %s < %t
|
||||
|
||||
// RUN: FileCheck --check-prefix=MANGLING %s < %t
|
||||
|
||||
struct Empty { };
|
||||
|
||||
struct A {
|
||||
@@ -38,6 +40,8 @@ struct C: virtual A {
|
||||
// VTABLE-C-NEXT: vbtable index 1, vfptr at offset 0
|
||||
// VTABLE-C-NEXT: 0 | void C::f()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7C@@6B@"
|
||||
|
||||
~C(); // Currently required to have correct record layout, see PR16406
|
||||
virtual void f();
|
||||
};
|
||||
@@ -58,6 +62,9 @@ struct D: virtual A {
|
||||
// VTABLE-D-NEXT: via vbtable index 1, vfptr at offset 0
|
||||
// VTABLE-D-NEXT: 0 | void D::f()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7D@@6B0@@"
|
||||
// MANGLING-DAG: @"\01??_7D@@6BA@@@"
|
||||
|
||||
virtual void f();
|
||||
virtual void h();
|
||||
};
|
||||
@@ -71,6 +78,7 @@ struct X { int x; };
|
||||
|
||||
// X and A get reordered in the layout since X doesn't have a vfptr while A has.
|
||||
struct Y : X, A { };
|
||||
// MANGLING-DAG: @"\01??_7Y@Test1@@6B@"
|
||||
|
||||
struct Z : virtual Y {
|
||||
// TEST1: VFTable for 'A' in 'Test1::Y' in 'Test1::Z' (2 entries).
|
||||
@@ -78,6 +86,8 @@ struct Z : virtual Y {
|
||||
// TEST1-NEXT: 1 | void A::z()
|
||||
|
||||
// TEST1-NOT: VFTable indices for 'Test1::Z'
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Z@Test1@@6B@"
|
||||
};
|
||||
|
||||
Z z;
|
||||
@@ -99,6 +109,10 @@ struct X: virtual A, virtual B {
|
||||
// TEST2: VFTable indices for 'Test2::X' (1 entries).
|
||||
// TEST2-NEXT: 0 | void Test2::X::h()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7X@Test2@@6B01@@"
|
||||
// MANGLING-DAG: @"\01??_7X@Test2@@6BA@@@"
|
||||
// MANGLING-DAG: @"\01??_7X@Test2@@6BB@@@"
|
||||
|
||||
virtual void h();
|
||||
};
|
||||
|
||||
@@ -107,7 +121,9 @@ X x;
|
||||
|
||||
namespace Test3 {
|
||||
|
||||
struct X : virtual A { };
|
||||
struct X : virtual A {
|
||||
// MANGLING-DAG: @"\01??_7X@Test3@@6B@"
|
||||
};
|
||||
|
||||
struct Y: virtual X {
|
||||
// TEST3: VFTable for 'A' in 'Test3::X' in 'Test3::Y' (2 entries).
|
||||
@@ -115,6 +131,8 @@ struct Y: virtual X {
|
||||
// TEST3-NEXT: 1 | void A::z()
|
||||
|
||||
// TEST3-NOT: VFTable indices for 'Test3::Y'
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Y@Test3@@6B@"
|
||||
};
|
||||
|
||||
Y y;
|
||||
@@ -134,6 +152,8 @@ struct X: virtual C {
|
||||
// TEST4-NEXT: 1 | void A::z()
|
||||
|
||||
// TEST4-NOT: VFTable indices for 'Test4::X'
|
||||
|
||||
// MANGLING-DAG: @"\01??_7X@Test4@@6B@"
|
||||
};
|
||||
|
||||
X x;
|
||||
@@ -143,6 +163,7 @@ namespace Test5 {
|
||||
|
||||
// New methods are added to the base's vftable.
|
||||
struct X : A {
|
||||
// MANGLING-DAG: @"\01??_7X@Test5@@6B@"
|
||||
virtual void g();
|
||||
};
|
||||
|
||||
@@ -158,6 +179,9 @@ struct Y : virtual X {
|
||||
// TEST5: VFTable indices for 'Test5::Y' (1 entries).
|
||||
// TEST5-NEXT: 0 | void Test5::Y::h()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Y@Test5@@6B01@@"
|
||||
// MANGLING-DAG: @"\01??_7Y@Test5@@6BX@1@@"
|
||||
|
||||
virtual void h();
|
||||
};
|
||||
|
||||
@@ -172,6 +196,8 @@ struct X : A, virtual Empty {
|
||||
// TEST6-NEXT: 1 | void A::z()
|
||||
|
||||
// TEST6-NOT: VFTable indices for 'Test6::X'
|
||||
|
||||
// MANGLING-DAG: @"\01??_7X@Test6@@6B@"
|
||||
};
|
||||
|
||||
X x;
|
||||
@@ -179,7 +205,9 @@ X x;
|
||||
|
||||
namespace Test7 {
|
||||
|
||||
struct X : C { };
|
||||
struct X : C {
|
||||
// MANGLING-DAG: @"\01??_7X@Test7@@6B@"
|
||||
};
|
||||
|
||||
struct Y : virtual X {
|
||||
// TEST7: VFTable for 'A' in 'C' in 'Test7::X' in 'Test7::Y' (2 entries).
|
||||
@@ -191,6 +219,8 @@ struct Y : virtual X {
|
||||
// TEST7-NEXT: 0 | this adjustment: 12 non-virtual
|
||||
|
||||
// TEST7-NOT: VFTable indices for 'Test7::Y'
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Y@Test7@@6B@"
|
||||
};
|
||||
|
||||
Y y;
|
||||
@@ -210,6 +240,9 @@ struct X : D, C {
|
||||
// TEST8: VFTable indices for 'Test8::X' (1 entries).
|
||||
// TEST8-NEXT: via vbtable index 1, vfptr at offset 0
|
||||
|
||||
// MANGLING-DAG: @"\01??_7X@Test8@@6BA@@@"
|
||||
// MANGLING-DAG: @"\01??_7X@Test8@@6BD@@@"
|
||||
|
||||
virtual void f();
|
||||
};
|
||||
|
||||
@@ -231,6 +264,9 @@ struct Y : virtual X {
|
||||
// TEST9-Y: VFTable indices for 'Test9::Y' (1 entries).
|
||||
// TEST9-Y-NEXT: 0 | void Test9::Y::h()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Y@Test9@@6B01@@"
|
||||
// MANGLING-DAG: @"\01??_7Y@Test9@@6BX@1@@"
|
||||
|
||||
virtual void h();
|
||||
};
|
||||
|
||||
@@ -248,6 +284,13 @@ struct Z : Y, virtual B {
|
||||
// TEST9-Z-NEXT: 0 | void B::g()
|
||||
|
||||
// TEST9-Z-NOT: VFTable indices for 'Test9::Z'
|
||||
|
||||
// MANGLING-DAG: @"\01??_7Z@Test9@@6BX@1@@"
|
||||
// MANGLING-DAG: @"\01??_7Z@Test9@@6BY@1@@"
|
||||
|
||||
// FIXME this one is wrong:
|
||||
// INCORRECT MANGLING-DAG: @"\01??_7Z@Test9@@6BB@@@"
|
||||
// MANGLING-DAG-SHOULD-BE: @"\01??_7Z@Test9@@6B@"
|
||||
};
|
||||
|
||||
Z z;
|
||||
@@ -275,6 +318,16 @@ struct W : Z, D, virtual A, virtual B {
|
||||
// TEST9-W-NEXT: 0 | this adjustment: -8 non-virtual
|
||||
|
||||
// TEST9-W-NOT: VFTable indices for 'Test9::W'
|
||||
|
||||
// MANGLING-DAG: @"\01??_7W@Test9@@6BA@@@"
|
||||
// MANGLING-DAG: @"\01??_7W@Test9@@6BD@@@"
|
||||
// MANGLING-DAG: @"\01??_7W@Test9@@6BX@1@@"
|
||||
|
||||
// FIXME: these two are wrong:
|
||||
// INCORRECT MANGLING-DAG: @"\01??_7W@Test9@@6BB@@@"
|
||||
// MANGLING-DAG-SHOULD-BE: @"\01??_7W@Test9@@6B@"
|
||||
// INCORRECT MANGLING-DAG: @"\01??_7W@Test9@@6BY@1@Z@1@@"
|
||||
// MANGLING-DAG-SHOULD-BE: @"\01??_7W@Test9@@6BY@1@@"
|
||||
};
|
||||
|
||||
W w;
|
||||
@@ -320,6 +373,16 @@ struct T : Z, D, virtual A, virtual B {
|
||||
// TEST9-T-NEXT: via vbtable index 2, vfptr at offset 0
|
||||
// TEST9-T-NEXT: 0 | void Test9::T::g()
|
||||
|
||||
// MANGLING-DAG: @"\01??_7T@Test9@@6BA@@@"
|
||||
// MANGLING-DAG: @"\01??_7T@Test9@@6BD@@@"
|
||||
// MANGLING-DAG: @"\01??_7T@Test9@@6BX@1@@"
|
||||
|
||||
// FIXME: these two are wrong:
|
||||
// INCORRECT MANGLING-DAG: @"\01??_7T@Test9@@6BB@@@"
|
||||
// MANGLING-DAG-SHOULD-BE: @"\01??_7T@Test9@@6B@"
|
||||
// INCORRECT MANGLING-DAG: @"\01??_7T@Test9@@6BY@1@Z@1@@"
|
||||
// MANGLING-DAG-SHOULD-BE: @"\01??_7T@Test9@@6BY@1@@"
|
||||
|
||||
virtual void f();
|
||||
virtual void g();
|
||||
virtual void h();
|
||||
|
||||
@@ -27,12 +27,6 @@ int main() {
|
||||
// CHECK: call {{.*}} @_ZN13basic_istreamIcED2Ev
|
||||
// CHECK: }
|
||||
|
||||
// basic_iostream's deleting dtor calls its complete dtor, then
|
||||
// operator delete().
|
||||
// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED0Ev(%struct.basic_iostream* {{.*}}%this) unnamed_addr
|
||||
// CHECK: call {{.*}} @_ZN14basic_iostreamIcED1Ev
|
||||
// CHECK: call {{.*}} @_ZdlPv
|
||||
|
||||
// basic_istream's complete dtor calls the base dtor,
|
||||
// then its virtual base's base dtor.
|
||||
// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED1Ev(%struct.basic_istream* {{.*}}%this) unnamed_addr
|
||||
@@ -45,6 +39,12 @@ int main() {
|
||||
// CHECK: call {{.*}} @_ZN13basic_istreamIcED1Ev
|
||||
// CHECK: call {{.*}} @_ZdlPv
|
||||
|
||||
// basic_iostream's deleting dtor calls its complete dtor, then
|
||||
// operator delete().
|
||||
// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED0Ev(%struct.basic_iostream* {{.*}}%this) unnamed_addr
|
||||
// CHECK: call {{.*}} @_ZN14basic_iostreamIcED1Ev
|
||||
// CHECK: call {{.*}} @_ZdlPv
|
||||
|
||||
// basic_istream's base dtor is a no-op.
|
||||
// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED2Ev(%struct.basic_istream* {{.*}}%this, i8** %vtt) unnamed_addr
|
||||
// CHECK-NOT: call
|
||||
|
||||
Reference in New Issue
Block a user