forked from OSchip/llvm-project
First step towards vftable generation with -cxx-abi microsoft PR13231
llvm-svn: 173035
This commit is contained in:
parent
d11e9a8300
commit
52b8a05234
|
@ -215,12 +215,15 @@ private:
|
||||||
/// Address points - Address points for all vtables.
|
/// Address points - Address points for all vtables.
|
||||||
AddressPointsMapTy AddressPoints;
|
AddressPointsMapTy AddressPoints;
|
||||||
|
|
||||||
|
bool IsMicrosoftABI;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VTableLayout(uint64_t NumVTableComponents,
|
VTableLayout(uint64_t NumVTableComponents,
|
||||||
const VTableComponent *VTableComponents,
|
const VTableComponent *VTableComponents,
|
||||||
uint64_t NumVTableThunks,
|
uint64_t NumVTableThunks,
|
||||||
const VTableThunkTy *VTableThunks,
|
const VTableThunkTy *VTableThunks,
|
||||||
const AddressPointsMapTy &AddressPoints);
|
const AddressPointsMapTy &AddressPoints,
|
||||||
|
bool IsMicrosoftABI);
|
||||||
~VTableLayout();
|
~VTableLayout();
|
||||||
|
|
||||||
uint64_t getNumVTableComponents() const {
|
uint64_t getNumVTableComponents() const {
|
||||||
|
@ -252,7 +255,7 @@ public:
|
||||||
"Did not find address point!");
|
"Did not find address point!");
|
||||||
|
|
||||||
uint64_t AddressPoint = AddressPoints.lookup(Base);
|
uint64_t AddressPoint = AddressPoints.lookup(Base);
|
||||||
assert(AddressPoint && "Address point must not be zero!");
|
assert(AddressPoint != 0 || IsMicrosoftABI);
|
||||||
|
|
||||||
return AddressPoint;
|
return AddressPoint;
|
||||||
}
|
}
|
||||||
|
@ -271,6 +274,8 @@ public:
|
||||||
typedef SmallVector<ThunkInfo, 1> ThunkInfoVectorTy;
|
typedef SmallVector<ThunkInfo, 1> ThunkInfoVectorTy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool IsMicrosoftABI;
|
||||||
|
|
||||||
/// MethodVTableIndices - Contains the index (relative to the vtable address
|
/// MethodVTableIndices - Contains the index (relative to the vtable address
|
||||||
/// point) where the function pointer for a virtual function is stored.
|
/// point) where the function pointer for a virtual function is stored.
|
||||||
typedef llvm::DenseMap<GlobalDecl, int64_t> MethodVTableIndicesTy;
|
typedef llvm::DenseMap<GlobalDecl, int64_t> MethodVTableIndicesTy;
|
||||||
|
@ -306,10 +311,21 @@ private:
|
||||||
/// given record decl.
|
/// given record decl.
|
||||||
void ComputeVTableRelatedInformation(const CXXRecordDecl *RD);
|
void ComputeVTableRelatedInformation(const CXXRecordDecl *RD);
|
||||||
|
|
||||||
|
/// ErrorUnsupported - Print out an error that the v-table layout code
|
||||||
|
/// doesn't support the particular C++ feature yet.
|
||||||
|
void ErrorUnsupported(StringRef Feature, SourceLocation Location);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VTableContext(ASTContext &Context) : Context(Context) {}
|
VTableContext(ASTContext &Context);
|
||||||
~VTableContext();
|
~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) {
|
const VTableLayout &getVTableLayout(const CXXRecordDecl *RD) {
|
||||||
ComputeVTableRelatedInformation(RD);
|
ComputeVTableRelatedInformation(RD);
|
||||||
assert(VTableLayouts.count(RD) && "No layout for this record decl!");
|
assert(VTableLayouts.count(RD) && "No layout for this record decl!");
|
||||||
|
|
|
@ -1002,6 +1002,10 @@ public:
|
||||||
dumpLayout(llvm::errs());
|
dumpLayout(llvm::errs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isMicrosoftABI() const {
|
||||||
|
return VTables.isMicrosoftABI();
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t getNumThunks() const {
|
uint64_t getNumThunks() const {
|
||||||
return Thunks.size();
|
return Thunks.size();
|
||||||
}
|
}
|
||||||
|
@ -1296,9 +1300,18 @@ VTableBuilder::AddMethod(const CXXMethodDecl *MD,
|
||||||
assert(ReturnAdjustment.isEmpty() &&
|
assert(ReturnAdjustment.isEmpty() &&
|
||||||
"Destructor can't have return adjustment!");
|
"Destructor can't have return adjustment!");
|
||||||
|
|
||||||
// Add both the complete destructor and the deleting destructor.
|
// FIXME: Should probably add a layer of abstraction for vtable generation.
|
||||||
Components.push_back(VTableComponent::MakeCompleteDtor(DD));
|
if (!isMicrosoftABI()) {
|
||||||
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 only one destructor in MS mode.
|
||||||
|
// FIXME: The virtual destructors are handled differently in MS ABI,
|
||||||
|
// we should add such a support later. For now, put the complete
|
||||||
|
// destructor into the vftable just to make its layout right.
|
||||||
|
Components.push_back(VTableComponent::MakeCompleteDtor(DD));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add the return adjustment if necessary.
|
// Add the return adjustment if necessary.
|
||||||
if (!ReturnAdjustment.isEmpty())
|
if (!ReturnAdjustment.isEmpty())
|
||||||
|
@ -1613,14 +1626,19 @@ VTableBuilder::LayoutPrimaryAndSecondaryVTables(BaseSubobject Base,
|
||||||
if (Base.getBase() == MostDerivedClass)
|
if (Base.getBase() == MostDerivedClass)
|
||||||
VBaseOffsetOffsets = Builder.getVBaseOffsetOffsets();
|
VBaseOffsetOffsets = Builder.getVBaseOffsetOffsets();
|
||||||
|
|
||||||
// Add the offset to top.
|
// FIXME: Should probably add a layer of abstraction for vtable generation.
|
||||||
CharUnits OffsetToTop = MostDerivedClassOffset - OffsetInLayoutClass;
|
if (!isMicrosoftABI()) {
|
||||||
Components.push_back(
|
// Add the offset to top.
|
||||||
VTableComponent::MakeOffsetToTop(OffsetToTop));
|
CharUnits OffsetToTop = MostDerivedClassOffset - OffsetInLayoutClass;
|
||||||
|
Components.push_back(VTableComponent::MakeOffsetToTop(OffsetToTop));
|
||||||
// Next, add the RTTI.
|
|
||||||
Components.push_back(VTableComponent::MakeRTTI(MostDerivedClass));
|
// 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.
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t AddressPoint = Components.size();
|
uint64_t AddressPoint = Components.size();
|
||||||
|
|
||||||
// Now go through all virtual member functions and add them.
|
// Now go through all virtual member functions and add them.
|
||||||
|
@ -2121,10 +2139,16 @@ void VTableBuilder::dumpLayout(raw_ostream& Out) {
|
||||||
MD);
|
MD);
|
||||||
|
|
||||||
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
|
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
|
||||||
IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Complete))] =
|
// FIXME: Should add a layer of abstraction for vtable generation.
|
||||||
MethodName + " [complete]";
|
if (!isMicrosoftABI()) {
|
||||||
IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Deleting))] =
|
IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Complete))]
|
||||||
MethodName + " [deleting]";
|
= MethodName + " [complete]";
|
||||||
|
IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Deleting))]
|
||||||
|
= MethodName + " [deleting]";
|
||||||
|
} else {
|
||||||
|
IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Complete))]
|
||||||
|
= MethodName;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
IndicesMap[VTables.getMethodVTableIndex(MD)] = MethodName;
|
IndicesMap[VTables.getMethodVTableIndex(MD)] = MethodName;
|
||||||
}
|
}
|
||||||
|
@ -2155,12 +2179,14 @@ VTableLayout::VTableLayout(uint64_t NumVTableComponents,
|
||||||
const VTableComponent *VTableComponents,
|
const VTableComponent *VTableComponents,
|
||||||
uint64_t NumVTableThunks,
|
uint64_t NumVTableThunks,
|
||||||
const VTableThunkTy *VTableThunks,
|
const VTableThunkTy *VTableThunks,
|
||||||
const AddressPointsMapTy &AddressPoints)
|
const AddressPointsMapTy &AddressPoints,
|
||||||
|
bool IsMicrosoftABI)
|
||||||
: NumVTableComponents(NumVTableComponents),
|
: NumVTableComponents(NumVTableComponents),
|
||||||
VTableComponents(new VTableComponent[NumVTableComponents]),
|
VTableComponents(new VTableComponent[NumVTableComponents]),
|
||||||
NumVTableThunks(NumVTableThunks),
|
NumVTableThunks(NumVTableThunks),
|
||||||
VTableThunks(new VTableThunkTy[NumVTableThunks]),
|
VTableThunks(new VTableThunkTy[NumVTableThunks]),
|
||||||
AddressPoints(AddressPoints) {
|
AddressPoints(AddressPoints),
|
||||||
|
IsMicrosoftABI(IsMicrosoftABI) {
|
||||||
std::copy(VTableComponents, VTableComponents+NumVTableComponents,
|
std::copy(VTableComponents, VTableComponents+NumVTableComponents,
|
||||||
this->VTableComponents.get());
|
this->VTableComponents.get());
|
||||||
std::copy(VTableThunks, VTableThunks+NumVTableThunks,
|
std::copy(VTableThunks, VTableThunks+NumVTableThunks,
|
||||||
|
@ -2169,6 +2195,10 @@ VTableLayout::VTableLayout(uint64_t NumVTableComponents,
|
||||||
|
|
||||||
VTableLayout::~VTableLayout() { }
|
VTableLayout::~VTableLayout() { }
|
||||||
|
|
||||||
|
VTableContext::VTableContext(ASTContext &Context)
|
||||||
|
: Context(Context),
|
||||||
|
IsMicrosoftABI(Context.getTargetInfo().getCXXABI() == CXXABI_Microsoft) { }
|
||||||
|
|
||||||
VTableContext::~VTableContext() {
|
VTableContext::~VTableContext() {
|
||||||
llvm::DeleteContainerSeconds(VTableLayouts);
|
llvm::DeleteContainerSeconds(VTableLayouts);
|
||||||
}
|
}
|
||||||
|
@ -2240,12 +2270,17 @@ void VTableContext::ComputeMethodVTableIndices(const CXXRecordDecl *RD) {
|
||||||
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
|
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
|
||||||
const CXXDestructorDecl *OverriddenDD =
|
const CXXDestructorDecl *OverriddenDD =
|
||||||
cast<CXXDestructorDecl>(OverriddenMD);
|
cast<CXXDestructorDecl>(OverriddenMD);
|
||||||
|
|
||||||
// Add both the complete and deleting entries.
|
if (!isMicrosoftABI()) {
|
||||||
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] =
|
// Add both the complete and deleting entries.
|
||||||
getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Complete));
|
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] =
|
||||||
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] =
|
getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Complete));
|
||||||
getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting));
|
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] =
|
||||||
|
getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting));
|
||||||
|
} else {
|
||||||
|
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] =
|
||||||
|
getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Complete));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
MethodVTableIndices[MD] = getMethodVTableIndex(OverriddenMD);
|
MethodVTableIndices[MD] = getMethodVTableIndex(OverriddenMD);
|
||||||
}
|
}
|
||||||
|
@ -2263,11 +2298,19 @@ void VTableContext::ComputeMethodVTableIndices(const CXXRecordDecl *RD) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the complete dtor.
|
if (!isMicrosoftABI()) {
|
||||||
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] = CurrentIndex++;
|
// Add the complete dtor.
|
||||||
|
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] = CurrentIndex++;
|
||||||
// Add the deleting dtor.
|
|
||||||
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = CurrentIndex++;
|
// Add the deleting dtor.
|
||||||
|
MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = CurrentIndex++;
|
||||||
|
} else {
|
||||||
|
// Add only the deleting dtor.
|
||||||
|
// FIXME: The virtual destructors are handled differently in MS ABI,
|
||||||
|
// we should add such a support later. For now, put the complete
|
||||||
|
// destructor into the vftable indices.
|
||||||
|
MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] = CurrentIndex++;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add the entry.
|
// Add the entry.
|
||||||
MethodVTableIndices[MD] = CurrentIndex++;
|
MethodVTableIndices[MD] = CurrentIndex++;
|
||||||
|
@ -2279,6 +2322,11 @@ void VTableContext::ComputeMethodVTableIndices(const CXXRecordDecl *RD) {
|
||||||
// If a class has an implicitly-defined virtual destructor,
|
// If a class has an implicitly-defined virtual destructor,
|
||||||
// its entries come after the declared virtual function pointers.
|
// its entries come after the declared virtual function pointers.
|
||||||
|
|
||||||
|
if (isMicrosoftABI()) {
|
||||||
|
ErrorUnsupported("implicit virtual destructor in the Microsoft ABI",
|
||||||
|
ImplicitVirtualDtor->getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
// Add the complete dtor.
|
// Add the complete dtor.
|
||||||
MethodVTableIndices[GlobalDecl(ImplicitVirtualDtor, Dtor_Complete)] =
|
MethodVTableIndices[GlobalDecl(ImplicitVirtualDtor, Dtor_Complete)] =
|
||||||
CurrentIndex++;
|
CurrentIndex++;
|
||||||
|
@ -2358,7 +2406,8 @@ static VTableLayout *CreateVTableLayout(const VTableBuilder &Builder) {
|
||||||
Builder.vtable_component_begin(),
|
Builder.vtable_component_begin(),
|
||||||
VTableThunks.size(),
|
VTableThunks.size(),
|
||||||
VTableThunks.data(),
|
VTableThunks.data(),
|
||||||
Builder.getAddressPoints());
|
Builder.getAddressPoints(),
|
||||||
|
Builder.isMicrosoftABI());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VTableContext::ComputeVTableRelatedInformation(const CXXRecordDecl *RD) {
|
void VTableContext::ComputeVTableRelatedInformation(const CXXRecordDecl *RD) {
|
||||||
|
@ -2398,6 +2447,14 @@ void VTableContext::ComputeVTableRelatedInformation(const CXXRecordDecl *RD) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VTableContext::ErrorUnsupported(StringRef Feature,
|
||||||
|
SourceLocation Location) {
|
||||||
|
clang::DiagnosticsEngine &Diags = Context.getDiagnostics();
|
||||||
|
unsigned DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
|
||||||
|
"v-table layout for %0 is not supported yet");
|
||||||
|
Diags.Report(Context.getFullLoc(Location), DiagID) << Feature;
|
||||||
|
}
|
||||||
|
|
||||||
VTableLayout *VTableContext::createConstructionVTableLayout(
|
VTableLayout *VTableContext::createConstructionVTableLayout(
|
||||||
const CXXRecordDecl *MostDerivedClass,
|
const CXXRecordDecl *MostDerivedClass,
|
||||||
CharUnits MostDerivedClassOffset,
|
CharUnits MostDerivedClassOffset,
|
||||||
|
|
|
@ -207,7 +207,7 @@ void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
|
||||||
}
|
}
|
||||||
|
|
||||||
void MicrosoftCXXABI::EmitVTables(const CXXRecordDecl *Class) {
|
void MicrosoftCXXABI::EmitVTables(const CXXRecordDecl *Class) {
|
||||||
// FIXME: implement
|
CGM.getVTables().GenerateClassData(CGM.getVTableLinkage(Class), Class);
|
||||||
}
|
}
|
||||||
|
|
||||||
CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
|
CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
|
||||||
|
|
|
@ -22,3 +22,14 @@ void no_contstructor_destructor_infinite_recursion() {
|
||||||
// CHECK: ret
|
// CHECK: ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct B {
|
||||||
|
virtual ~B();
|
||||||
|
virtual void foo();
|
||||||
|
};
|
||||||
|
|
||||||
|
void check_vftable_offset() {
|
||||||
|
B b;
|
||||||
|
// The vftable pointer should point at the beginning of the vftable.
|
||||||
|
// CHECK: [[THIS_PTR:%[0-9]+]] = bitcast %struct.B* {{.*}} to i8***
|
||||||
|
// CHECK: store i8** getelementptr inbounds ([2 x i8*]* @"\01??_7B@@6B@", i64 0, i64 0), i8*** [[THIS_PTR]]
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
// 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=CHECK-A %s < %t
|
||||||
|
// RUN: FileCheck --check-prefix=CHECK-B %s < %t
|
||||||
|
// RUN: FileCheck --check-prefix=CHECK-C %s < %t
|
||||||
|
// RUN: FileCheck --check-prefix=CHECK-D %s < %t
|
||||||
|
// RUN: FileCheck --check-prefix=CHECK-E %s < %t
|
||||||
|
// RUN: FileCheck --check-prefix=CHECK-F %s < %t
|
||||||
|
// RUN: FileCheck --check-prefix=CHECK-G %s < %t
|
||||||
|
|
||||||
|
struct A {
|
||||||
|
// CHECK-A: Vtable 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()
|
||||||
|
// EMITS-VTABLE: @"\01??_7A@@6B@" = unnamed_addr constant [3 x i8*]
|
||||||
|
virtual void f();
|
||||||
|
virtual void g();
|
||||||
|
virtual void h();
|
||||||
|
int ia;
|
||||||
|
};
|
||||||
|
void A::f() {}
|
||||||
|
|
||||||
|
struct B : A {
|
||||||
|
// CHECK-B: Vtable for '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()
|
||||||
|
// EMITS-VTABLE: @"\01??_7B@@6B@" = unnamed_addr constant [5 x i8*]
|
||||||
|
virtual void f(); // overrides A::f()
|
||||||
|
virtual void i();
|
||||||
|
virtual void j();
|
||||||
|
};
|
||||||
|
void B::f() {}
|
||||||
|
|
||||||
|
struct C {
|
||||||
|
// CHECK-C: Vtable for 'C' (2 entries)
|
||||||
|
// CHECK-C-NEXT: 0 | C::~C()
|
||||||
|
// CHECK-C-NEXT: 1 | void C::f()
|
||||||
|
// CHECK-C: VTable indices for 'C' (2 entries).
|
||||||
|
// CHECK-C-NEXT: 0 | C::~C()
|
||||||
|
// CHECK-C-NEXT: 1 | void C::f()
|
||||||
|
// Never used, so doesn't emit a vtable.
|
||||||
|
virtual ~C();
|
||||||
|
|
||||||
|
virtual void f();
|
||||||
|
};
|
||||||
|
void C::f() {}
|
||||||
|
|
||||||
|
struct D {
|
||||||
|
// CHECK-D: Vtable for 'D' (2 entries)
|
||||||
|
// CHECK-D-NEXT: 0 | void D::f()
|
||||||
|
// CHECK-D-NEXT: 1 | D::~D()
|
||||||
|
// EMITS-VTABLE: @"\01??_7D@@6B@" = unnamed_addr constant [2 x i8*]
|
||||||
|
virtual void f();
|
||||||
|
|
||||||
|
virtual ~D();
|
||||||
|
};
|
||||||
|
void D::f() {}
|
||||||
|
|
||||||
|
struct E : A {
|
||||||
|
// 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()
|
||||||
|
// CHECK-E-NEXT: 4 | void E::i()
|
||||||
|
// CHECK-E: VTable indices for 'E' (2 entries).
|
||||||
|
// CHECK-E-NEXT: 3 | E::~E()
|
||||||
|
// CHECK-E-NEXT: 4 | void E::i()
|
||||||
|
|
||||||
|
// Never used, so doesn't emit a vtable.
|
||||||
|
virtual ~E();
|
||||||
|
virtual void i();
|
||||||
|
};
|
||||||
|
void E::i() {}
|
||||||
|
|
||||||
|
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()
|
||||||
|
// CHECK-F: VTable indices for 'F' (2 entries).
|
||||||
|
// CHECK-F-NEXT: 3 | void F::i()
|
||||||
|
// CHECK-F-NEXT: 4 | F::~F()
|
||||||
|
// EMITS-VTABLE: @"\01??_7F@@6B@" = unnamed_addr constant [5 x i8*]
|
||||||
|
virtual void i();
|
||||||
|
virtual ~F();
|
||||||
|
};
|
||||||
|
void F::i() {}
|
||||||
|
|
||||||
|
struct G : E {
|
||||||
|
// 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()
|
||||||
|
// 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()
|
||||||
|
// CHECK-G-NEXT: 5 | void G::j()
|
||||||
|
// Never used, so doesn't emit a vtable.
|
||||||
|
virtual void f(); // overrides A::f()
|
||||||
|
virtual ~G();
|
||||||
|
virtual void j();
|
||||||
|
};
|
||||||
|
void G::j() {}
|
Loading…
Reference in New Issue