/// AddMethod - Add a single virtual member function to the vftable
/// components vector.
void AddMethod(const CXXMethodDecl *MD, ThunkInfo TI) {
+ if (!TI.isEmpty()) {
+ VTableThunks[Components.size()] = TI;
+ AddThunk(MD, TI);
+ }
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
assert(TI.Return.isEmpty() &&
"Destructor can't have return adjustment!");
Components.push_back(VTableComponent::MakeDeletingDtor(DD));
} else {
- if (!TI.isEmpty())
- VTableThunks[Components.size()] = TI;
Components.push_back(VTableComponent::MakeFunction(MD));
}
}
const CXXRecordDecl *LastVBase,
BasesSetVectorTy &VisitedBases);
- void CheckBadVirtualInheritanceHierarchy() {
- // We fail at this-adjustment for virtual methods inherited from
- // non-virtual bases that overrides a method in a virtual base.
- if (Context.getLangOpts().DumpVTableLayouts)
- return;
- for (CXXRecordDecl::base_class_const_iterator BI =
- MostDerivedClass->bases_begin(), BE = MostDerivedClass->bases_end();
- BI != BE; ++BI) {
- const CXXRecordDecl *Base = BI->getType()->getAsCXXRecordDecl();
- if (BI->isVirtual())
- continue;
- for (CXXRecordDecl::method_iterator I = Base->method_begin(),
- E = Base->method_end(); I != E; ++I) {
- const CXXMethodDecl *Method = *I;
- if (!Method->isVirtual())
- continue;
- if (isa<CXXDestructorDecl>(Method))
- continue;
- OverriddenMethodsSetTy OverriddenMethods;
- ComputeAllOverriddenMethods(Method, OverriddenMethods);
- for (OverriddenMethodsSetTy::const_iterator I =
- OverriddenMethods.begin(),
- E = OverriddenMethods.end(); I != E; ++I) {
- const CXXMethodDecl *Overridden = *I;
- if (Base->isVirtuallyDerivedFrom(Overridden->getParent())) {
- ErrorUnsupported("classes with non-virtual base "
- "classes that override methods in virtual bases",
- BI->getLocStart());
- return;
- }
- }
- }
- }
- }
-
void LayoutVFTable() {
// FIXME: add support for RTTI when we have proper LLVM support for symbols
// pointing to the middle of a section.
- CheckBadVirtualInheritanceHierarchy();
-
BasesSetVectorTy VisitedBases;
AddMethods(BaseSubobject(MostDerivedClass, CharUnits::Zero()), 0, 0,
VisitedBases);
InitialOverriddenDefinitionCollector Collector;
visitAllOverriddenMethods(Overrider.Method, Collector);
+ // If there are no overrides then 'this' is located
+ // in the base that defines the method.
+ if (Collector.Bases.size() == 0)
+ return Overrider.Offset;
+
CXXBasePaths Paths;
Overrider.Method->getParent()->lookupInBases(BaseInSet, &Collector.Bases,
Paths);
FinalOverriders::OverriderInfo Overrider =
Overriders.getOverrider(MD, Base.getBaseOffset());
- ThisAdjustment ThisAdjustmentOffset;
- bool ForceThunk = false;
+ const CXXMethodDecl *OverriderMD = Overrider.Method;
+ const CXXMethodDecl *OverriddenMD =
+ FindNearestOverriddenMethod(MD, VisitedBases);
- // Check if this virtual member function overrides
- // a method in one of the visited bases.
- if (const CXXMethodDecl *OverriddenMD =
- FindNearestOverriddenMethod(MD, VisitedBases)) {
+ ThisAdjustment ThisAdjustmentOffset;
+ bool ReturnAdjustingThunk = false;
+ CharUnits ThisOffset = ComputeThisOffset(Overrider);
+ ThisAdjustmentOffset.NonVirtual =
+ (ThisOffset - WhichVFPtr.FullOffsetInMDC).getQuantity();
+ if ((OverriddenMD || OverriderMD != MD) &&
+ WhichVFPtr.getVBaseWithVPtr())
+ CalculateVtordispAdjustment(Overrider, ThisOffset, ThisAdjustmentOffset);
+
+ if (OverriddenMD) {
+ // If MD overrides anything in this vftable, we need to update the entries.
MethodInfoMapTy::iterator OverriddenMDIterator =
MethodInfoMap.find(OverriddenMD);
MethodInfo &OverriddenMethodInfo = OverriddenMDIterator->second;
- // Create a this-adjusting thunk if needed.
- CharUnits TI = ComputeThisOffset(Overrider);
- if (TI != WhichVFPtr.FullOffsetInMDC) {
- ThisAdjustmentOffset.NonVirtual =
- (TI - WhichVFPtr.FullOffsetInMDC).getQuantity();
- }
-
- if (WhichVFPtr.getVBaseWithVPtr())
- CalculateVtordispAdjustment(Overrider, TI, ThisAdjustmentOffset);
-
- if (!ThisAdjustmentOffset.isEmpty()) {
- VTableThunks[OverriddenMethodInfo.VFTableIndex].This =
- ThisAdjustmentOffset;
- AddThunk(MD, VTableThunks[OverriddenMethodInfo.VFTableIndex]);
- }
-
if (!NeedsReturnAdjustingThunk(MD)) {
// No return adjustment needed - just replace the overridden method info
// with the current info.
}
// In case we need a return adjustment, we'll add a new slot for
- // the overrider and put a return-adjusting thunk where the overridden
- // method was in the vftable.
- // For now, just mark the overriden method as shadowed by a new slot.
+ // the overrider. Mark the overriden method as shadowed by the new slot.
OverriddenMethodInfo.Shadowed = true;
- ForceThunk = true;
-
- // Also apply this adjustment to the shadowed slots.
- if (!ThisAdjustmentOffset.isEmpty()) {
- // FIXME: this is O(N^2), can be O(N).
- const CXXMethodDecl *SubOverride = OverriddenMD;
- while ((SubOverride =
- FindNearestOverriddenMethod(SubOverride, VisitedBases))) {
- MethodInfoMapTy::iterator SubOverrideIterator =
- MethodInfoMap.find(SubOverride);
- if (SubOverrideIterator == MethodInfoMap.end())
- break;
- MethodInfo &SubOverrideMI = SubOverrideIterator->second;
- assert(SubOverrideMI.Shadowed);
- VTableThunks[SubOverrideMI.VFTableIndex].This =
- ThisAdjustmentOffset;
- AddThunk(MD, VTableThunks[SubOverrideMI.VFTableIndex]);
- }
- }
+
+ // Force a special name mangling for a return-adjusting thunk
+ // unless the method is the final overrider without this adjustment.
+ ReturnAdjustingThunk =
+ !(MD == OverriderMD && ThisAdjustmentOffset.isEmpty());
} else if (Base.getBaseOffset() != WhichVFPtr.FullOffsetInMDC ||
MD->size_overridden_methods()) {
// Skip methods that don't belong to the vftable of the current class,
"Should not have method info for this method yet!");
MethodInfoMap.insert(std::make_pair(MD, MI));
- const CXXMethodDecl *OverriderMD = Overrider.Method;
-
// Check if this overrider needs a return adjustment.
// We don't want to do this for pure virtual member functions.
BaseOffset ReturnAdjustmentOffset;
ComputeReturnAdjustmentBaseOffset(Context, OverriderMD, MD);
}
if (!ReturnAdjustmentOffset.isEmpty()) {
- ForceThunk = true;
+ ReturnAdjustingThunk = true;
ReturnAdjustment.NonVirtual =
ReturnAdjustmentOffset.NonVirtualOffset.getQuantity();
if (ReturnAdjustmentOffset.VirtualBase) {
}
AddMethod(OverriderMD, ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment,
- ForceThunk ? MD : 0));
+ ReturnAdjustingThunk ? MD : 0));
}
}
bool ContinueFirstLine) {
const ReturnAdjustment &R = TI.Return;
bool Multiline = false;
- const char *LinePrefix = "\n ";
- if (!R.isEmpty()) {
+ const char *LinePrefix = "\n ";
+ if (!R.isEmpty() || TI.Method) {
if (!ContinueFirstLine)
Out << LinePrefix;
- Out << "[return adjustment: ";
+ Out << "[return adjustment (to type '"
+ << TI.Method->getReturnType().getCanonicalType().getAsString()
+ << "'): ";
if (R.Virtual.Microsoft.VBPtrOffset)
Out << "vbptr at offset " << R.Virtual.Microsoft.VBPtrOffset << ", ";
if (R.Virtual.Microsoft.VBIndex)
Out << "vtordisp at " << T.Virtual.Microsoft.VtordispOffset << ", ";
if (T.Virtual.Microsoft.VBPtrOffset) {
Out << "vbptr at " << T.Virtual.Microsoft.VBPtrOffset
- << " to the left, ";
+ << " to the left,";
assert(T.Virtual.Microsoft.VBOffsetOffset > 0);
Out << LinePrefix << " vboffset at "
<< T.Virtual.Microsoft.VBOffsetOffset << " in the vbtable, ";
// CHECK: ret
}
-struct X : virtual VBase {
- int x;
-};
-
-
-struct C : X {
+struct C : B {
C();
// has an implicit vdtor.
};
struct Test1 : Ret1 {
// RET-THUNKS-Test1: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' (3 entries).
// RET-THUNKS-Test1-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test1::foo()
- // RET-THUNKS-Test1-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test1-NEXT: [return adjustment (to type 'struct C *'): 4 non-virtual]
// RET-THUNKS-Test1-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo()
+ // RET-THUNKS-Test1: Thunks for 'this_adjustment::Test1 *return_adjustment::Test1::foo()' (1 entry).
+ // RET-THUNKS-Test1-NEXT: 0 | [return adjustment (to type 'struct C *'): 4 non-virtual]
+
// RET-THUNKS-Test1: VFTable indices for 'return_adjustment::Test1' (1 entry).
// RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo()
struct Test2 : Test1 {
// RET-THUNKS-Test2: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test2' (4 entries).
// RET-THUNKS-Test2-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
- // RET-THUNKS-Test2-NEXT: [return adjustment: 8 non-virtual]
+ // RET-THUNKS-Test2-NEXT: [return adjustment (to type 'struct C *'): 8 non-virtual]
// RET-THUNKS-Test2-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test2-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
- // RET-THUNKS-Test2-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test2-NEXT: [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
// RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
+ // RET-THUNKS-Test2: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test2::foo()' (2 entries).
+ // RET-THUNKS-Test2-NEXT: 0 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
+ // RET-THUNKS-Test2-NEXT: 1 | [return adjustment (to type 'struct C *'): 8 non-virtual]
+
// RET-THUNKS-Test2: VFTable indices for 'return_adjustment::Test2' (1 entry).
// RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
// RET-THUNKS-Test3: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' (3 entries).
// RET-THUNKS-Test3-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test3::foo()
- // RET-THUNKS-Test3-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test3-NEXT: [return adjustment (to type 'struct C *'): 4 non-virtual]
// RET-THUNKS-Test3-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo()
+ // RET-THUNKS-Test3: Thunks for 'this_adjustment::Test1 *return_adjustment::Test3::foo()' (1 entry).
+ // RET-THUNKS-Test3-NEXT: 0 | [return adjustment (to type 'struct C *'): 4 non-virtual]
+
// RET-THUNKS-Test3: VFTable indices for 'return_adjustment::Test3' (1 entry).
// RET-THUNKS-Test3-NEXT: via vfptr at offset 4
// RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo()
// RET-THUNKS-Test4: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' in 'return_adjustment::Test4' (4 entries).
// RET-THUNKS-Test4-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
- // RET-THUNKS-Test4-NEXT: [return adjustment: 8 non-virtual]
+ // RET-THUNKS-Test4-NEXT: [return adjustment (to type 'struct C *'): 8 non-virtual]
// RET-THUNKS-Test4-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test4-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
- // RET-THUNKS-Test4-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test4-NEXT: [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
// RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
+ // RET-THUNKS-Test4: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test4::foo()' (2 entries).
+ // RET-THUNKS-Test4-NEXT: 0 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
+ // RET-THUNKS-Test4-NEXT: 1 | [return adjustment (to type 'struct C *'): 8 non-virtual]
+
// RET-THUNKS-Test4: VFTable indices for 'return_adjustment::Test4' (1 entry).
// RET-THUNKS-Test4-NEXT: -- accessible via vfptr at offset 4 --
- // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
+ // RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
virtual Ret2* foo();
};
struct Test5 : Ret1, Test1 {
// RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test5' (3 entries).
// RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
- // RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual]
+ // RET-THUNKS-Test5-NEXT: [return adjustment (to type 'struct C *'): 8 non-virtual]
// RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
+ // RET-THUNKS-Test5: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test5::foo()' (1 entry).
+ // RET-THUNKS-Test5-NEXT: 0 | [return adjustment (to type 'struct C *'): 8 non-virtual]
+
// RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test5' (4 entries).
// RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
- // RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual]
+ // RET-THUNKS-Test5-NEXT: [return adjustment (to type 'struct C *'): 8 non-virtual]
// RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
// RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
- // RET-THUNKS-Test5-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test5-NEXT: [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
// RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
// RET-THUNKS-Test5-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
+ // RET-THUNKS-Test5-NEXT: [return adjustment (to type 'struct return_adjustment::Ret2 *'): 0 non-virtual]
+ // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
+
+ // RET-THUNKS-Test5: Thunks for 'return_adjustment::Ret2 *return_adjustment::Test5::foo()' (3 entries).
+ // RET-THUNKS-Test5-NEXT: 0 | [return adjustment (to type 'struct return_adjustment::Ret2 *'): 0 non-virtual]
+ // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
+ // RET-THUNKS-Test5-NEXT: 1 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 4 non-virtual]
+ // RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
+ // RET-THUNKS-Test5-NEXT: 2 | [return adjustment (to type 'struct C *'): 8 non-virtual]
// RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
// RET-THUNKS-Test5: VFTable indices for 'return_adjustment::Test5' (1 entry).
virtual Ret3* foo();
// RET-THUNKS-Test6: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test6' (4 entries).
// RET-THUNKS-Test6-NEXT: 0 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
- // RET-THUNKS-Test6-NEXT: [return adjustment: 4 non-virtual]
+ // RET-THUNKS-Test6-NEXT: [return adjustment (to type 'struct C *'): 4 non-virtual]
// RET-THUNKS-Test6-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test6-NEXT: 2 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
+ // RET-THUNKS-Test6-NEXT: [return adjustment (to type 'struct this_adjustment::Test1 *'): 0 non-virtual]
// RET-THUNKS-Test6-NEXT: 3 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
+ // RET-THUNKS-Test6: Thunks for 'return_adjustment::Ret3 *return_adjustment::Test6::foo()' (2 entries).
+ // RET-THUNKS-Test6-NEXT: 0 | [return adjustment (to type 'struct this_adjustment::Test1 *'): 0 non-virtual]
+ // RET-THUNKS-Test6-NEXT: 1 | [return adjustment (to type 'struct C *'): 4 non-virtual]
+
// RET-THUNKS-Test6: VFTable indices for 'return_adjustment::Test6' (1 entry).
// RET-THUNKS-Test6-NEXT: 3 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
};
J::J() {}
// VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' (3 entries).
-// VFTABLES: 0 | test1::D *test1::J::foo()
-// VFTABLES: [return adjustment: 4 non-virtual]
-// VFTABLES: 1 | test1::D *test1::J::foo()
-// VFTABLES: 2 | test1::D *test1::J::foo()
+// VFTABLES-NEXT: 0 | test1::D *test1::J::foo()
+// VFTABLES-NEXT: [return adjustment (to type 'struct test1::B *'): 4 non-virtual]
+// VFTABLES-NEXT: 1 | test1::D *test1::J::foo()
+// VFTABLES-NEXT: [return adjustment (to type 'struct test1::C *'): 0 non-virtual]
+// VFTABLES-NEXT: 2 | test1::D *test1::J::foo()
// GLOBALS-LABEL: @"\01??_7J@test1@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*]
// GLOBALS: @"\01?foo@J@test1@@QAEPAUB@2@XZ"
// GLOBALS: @"\01?foo@J@test1@@QAEPAUC@2@XZ"
-// GLOBALS: @"\01?foo@J@test1@@QAEPAUD@2@XZ"
-// FIXME: Should be UAEPAUD.
+// GLOBALS: @"\01?foo@J@test1@@UAEPAUD@2@XZ"
K::K() {}
// VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' in 'test1::K' (4 entries).
-// VFTABLES: 0 | test1::E *test1::K::foo()
-// VFTABLES: [return adjustment: 4 non-virtual]
-// VFTABLES: 1 | test1::E *test1::K::foo()
-// VFTABLES: 2 | test1::E *test1::K::foo()
-// VFTABLES: 3 | test1::E *test1::K::foo()
+// VFTABLES-NEXT: 0 | test1::E *test1::K::foo()
+// VFTABLES-NEXT: [return adjustment (to type 'struct test1::B *'): 4 non-virtual]
+// VFTABLES-NEXT: 1 | test1::E *test1::K::foo()
+// VFTABLES-NEXT: [return adjustment (to type 'struct test1::C *'): 0 non-virtual]
+// VFTABLES-NEXT: 2 | test1::E *test1::K::foo()
+// VFTABLES-NEXT: [return adjustment (to type 'struct test1::D *'): 0 non-virtual]
+// VFTABLES-NEXT: 3 | test1::E *test1::K::foo()
// Only B to C requires adjustment, but we get 3 thunks in K's vftable, two of
// which are trivial.
// GLOBALS: @"\01?foo@K@test1@@QAEPAUB@2@XZ"
// GLOBALS: @"\01?foo@K@test1@@QAEPAUC@2@XZ"
// GLOBALS: @"\01?foo@K@test1@@QAEPAUD@2@XZ"
-// GLOBALS: @"\01?foo@K@test1@@QAEPAUE@2@XZ"
-// FIXME: Should be UAEPAUE.
+// GLOBALS: @"\01?foo@K@test1@@UAEPAUE@2@XZ"
// This thunk has a return adjustment.
// CODEGEN-LABEL: define {{.*}} @"\01?foo@K@test1@@QAEPAUB@2@XZ"
J::J() {}
// VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' (2 entries).
-// VFTABLES: 0 | test2::D *test2::J::foo()
-// VFTABLES: [return adjustment: 4 non-virtual]
-// VFTABLES: 1 | test2::D *test2::J::foo()
+// VFTABLES-NEXT: 0 | test2::D *test2::J::foo()
+// VFTABLES-NEXT: [return adjustment (to type 'struct test2::B *'): 4 non-virtual]
+// VFTABLES-NEXT: 1 | test2::D *test2::J::foo()
// GLOBALS-LABEL: @"\01??_7J@test2@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*]
K::K() {}
// VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' in 'test2::K' (3 entries).
-// VFTABLES: 0 | test2::E *test2::K::foo()
-// VFTABLES: [return adjustment: 4 non-virtual]
-// VFTABLES: 1 | test2::E *test2::K::foo()
-// VFTABLES: 2 | test2::E *test2::K::foo()
+// VFTABLES-NEXT: 0 | test2::E *test2::K::foo()
+// VFTABLES-NEXT: [return adjustment (to type 'struct test2::B *'): 4 non-virtual]
+// VFTABLES-NEXT: 1 | test2::E *test2::K::foo()
+// VFTABLES-NEXT: [return adjustment (to type 'struct test2::D *'): 0 non-virtual]
+// VFTABLES-NEXT: 2 | test2::E *test2::K::foo()
// GLOBALS-LABEL: @"\01??_7K@test2@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*]
// VTABLE-SIMPLE-A-NEXT: 1 | simple::A::~A() [scalar deleting]
// VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
+ // VTABLE-SIMPLE-A: Thunks for 'simple::A::~A()' (1 entry).
+ // VTABLE-SIMPLE-A-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
+
+ // VTABLE-SIMPLE-A: Thunks for 'void simple::A::f()' (1 entry).
+ // VTABLE-SIMPLE-A-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
+
virtual void f();
// MANGLING-DAG: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ"
// VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
// VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
+ // VTABLE-SIMPLE-B: Thunks for 'simple::B::~B()' (1 entry).
+ // VTABLE-SIMPLE-B-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
+
// VTABLE-SIMPLE-B: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
// VTABLE-SIMPLE-B-NEXT: 0 | void simple::B::f()
// VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
// VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
// VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
+ // VTABLE-SIMPLE-B: Thunks for 'simple::B::~B()' (1 entry).
+ // VTABLE-SIMPLE-B-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
+
+ // VTABLE-SIMPLE-B: Thunks for 'void simple::B::f()' (1 entry).
+ // VTABLE-SIMPLE-B-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
+
// FIXME: The vtordisp thunk should only get emitted for a constructor
// if "this" leaves scope.
B() { use_somewhere_else(this); }
// VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
+ // VTABLE-SIMPLE-C: Thunks for 'simple::C::~C()' (1 entry).
+ // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
+
// VTABLE-SIMPLE-C: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
// VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
// VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
+ // VTABLE-SIMPLE-C: Thunks for 'simple::C::~C()' (1 entry).
+ // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
+
+ // VTABLE-SIMPLE-C: Thunks for 'void simple::C::f()' (1 entry).
+ // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
+
// VTABLE-SIMPLE-C: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
// VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual]
// VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual]
+ // VTABLE-SIMPLE-C: Thunks for 'simple::C::~C()' (1 entry).
+ // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual]
+
+ // VTABLE-SIMPLE-C: Thunks for 'void simple::C::f()' (1 entry).
+ // VTABLE-SIMPLE-C-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual]
+
int x;
virtual void f();
// MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ"
// VTABLE-EXTENDED-A-NEXT: 1 | extended::A::~A() [scalar deleting]
// VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
+ // VTABLE-EXTENDED-A: Thunks for 'void simple::A::f()' (1 entry).
+ // VTABLE-EXTENDED-A-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+ // VTABLE-EXTENDED-A-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
+
// `vtordispex{8,8,4294967292,8}'
// MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
// VTABLE-EXTENDED-B: 1 | extended::B::~B() [scalar deleting]
// VTABLE-EXTENDED-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
+ // VTABLE-EXTENDED-B: Thunks for 'void simple::A::f()' (1 entry).
+ // VTABLE-EXTENDED-B-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+ // VTABLE-EXTENDED-B-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
+
// vtordisp{4294967292,0}
// MANGLING-DAG: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"
};
// VTABLE-EXTENDED-C-NEXT: [this adjustment: vtordisp at -4, vbptr at 12 to the left,
// VTABLE-EXTENDED-C-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
+ // VTABLE-EXTENDED-C: Thunks for 'void simple::A::f()' (1 entry).
+ // VTABLE-EXTENDED-C-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 12 to the left,
+ // VTABLE-EXTENDED-C-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
+
// `vtordispex{12,8,4294967292,8}'
// MANGLING-DAG: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ"
int x;
// VTABLE-EXTENDED-E-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
// VTABLE-EXTENDED-E-NEXT: vboffset at 8 in the vbtable, 12 non-virtual]
+ // VTABLE-EXTENDED-E: Thunks for 'void extended::D::f()' (1 entry).
+ // VTABLE-EXTENDED-E-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+ // VTABLE-EXTENDED-E-NEXT: vboffset at 8 in the vbtable, 12 non-virtual]
+
// `vtordispex{8,8,4294967292,12}'
// MANGLING-DAG: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"
// VTABLE-EXTENDED-F-NEXT: [this adjustment: vtordisp at -4, vbptr at 20 to the left,
// VTABLE-EXTENDED-F-NEXT: vboffset at 12 in the vbtable, 12 non-virtual]
+ // VTABLE-EXTENDED-F: Thunks for 'void extended::D::f()' (1 entry).
+ // VTABLE-EXTENDED-F-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 20 to the left,
+ // VTABLE-EXTENDED-F-NEXT: vboffset at 12 in the vbtable, 12 non-virtual]
+
// `vtordispex{20,12,4294967292,12}'
// MANGLING-DAG: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ"
int x;
// VTABLE-EXTENDED-G-NEXT: 1 | extended::G::~G() [scalar deleting]
// VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
+ // VTABLE-EXTENDED-G: Thunks for 'void simple::A::f()' (1 entry).
+ // VTABLE-EXTENDED-G-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+ // VTABLE-EXTENDED-G-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
+
// Emits a G's own vfptr, thus moving the vbptr in the layout.
virtual void g();
// VTABLE-EXTENDED-H-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
// VTABLE-EXTENDED-H-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
+ // VTABLE-EXTENDED-H: Thunks for 'void simple::A::f()' (1 entry).
+ // VTABLE-EXTENDED-H-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
+ // VTABLE-EXTENDED-H-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
+
// MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
// MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"
};
// VTABLE-PR17738-A-NEXT: [this adjustment: vtordisp at -12, vbptr at 20 to the left,
// VTABLE-PR17738-A-NEXT: vboffset at 8 in the vbtable, 16 non-virtual]
+ // VTABLE-PR17738-A: Thunks for 'void simple::B::f()' (1 entry).
+ // VTABLE-PR17738-A-NEXT: 0 | [this adjustment: vtordisp at -12, vbptr at 20 to the left,
+ // VTABLE-PR17738-A-NEXT: vboffset at 8 in the vbtable, 16 non-virtual]
+
// MANGLING-DAG: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"
int a;
virtual ~A();
// RUN: FileCheck --check-prefix=TEST7 %s < %t
// RUN: FileCheck --check-prefix=TEST8-X %s < %t
// RUN: FileCheck --check-prefix=TEST8-Z %s < %t
+// RUN: FileCheck --check-prefix=TEST8-T %s < %t
// RUN: FileCheck --check-prefix=TEST9-Y %s < %t
// RUN: FileCheck --check-prefix=TEST9-Z %s < %t
// RUN: FileCheck --check-prefix=TEST9-W %s < %t
// MANGLING-DAG: @"\01??_7C@@6B@"
- virtual void f();
+ virtual void f() {}
};
C c;
// TEST4-NEXT: [this adjustment: 8 non-virtual]
// TEST4-NEXT: 1 | void A::z()
+ // TEST4: Thunks for 'void C::f()' (1 entry).
+ // TEST4-NEXT: 0 | [this adjustment: 8 non-virtual]
+
// TEST4-NOT: VFTable indices for 'Test4::X'
// MANGLING-DAG: @"\01??_7X@Test4@@6B@"
// Another diamond inheritance which led to AST crashes.
struct Y : virtual A {};
-class Z : Y, C {
+struct Z : Y, C {
// TEST8-Z: VFTable for 'A' in 'Test8::Y' in 'Test8::Z' (2 entries).
// TEST8-Z-NEXT: 0 | void Test8::Z::f()
// TEST8-Z-NEXT: 1 | void A::z()
virtual void f();
};
Z z;
+
+// Another diamond inheritance which we miscompiled (PR18967).
+struct W : virtual A {
+ virtual void bar();
+};
+
+struct T : W, C {
+ // TEST8-T: VFTable for 'Test8::W' in 'Test8::T' (1 entry)
+ // TEST8-T-NEXT: 0 | void Test8::T::bar()
+
+ // TEST8-T: VFTable for 'A' in 'Test8::W' in 'Test8::T' (2 entries)
+ // TEST8-T-NEXT: 0 | void C::f()
+ // TEST8-T-NEXT: [this adjustment: -4 non-virtual]
+ // TEST8-T-NEXT: 1 | void A::z()
+
+ // TEST8-T: Thunks for 'void C::f()' (1 entry).
+ // TEST8-T-NEXT: 0 | [this adjustment: -4 non-virtual]
+ virtual void bar();
+ int field;
+};
+T t;
}
namespace Test9 {
// VDTORS-U-NEXT: [this adjustment: -4 non-virtual]
// VDTORS-U-NEXT: 1 | void vdtors::X::zzz()
- // VDTORS-U: Thunks for 'vdtors::W::~W()' (1 entry).
+ // VDTORS-U: Thunks for 'vdtors::U::~U()' (1 entry).
// VDTORS-U-NEXT: 0 | [this adjustment: -4 non-virtual]
// VDTORS-U: VFTable indices for 'vdtors::U' (1 entry).
// VDTORS-V-NEXT: [this adjustment: -4 non-virtual]
// VDTORS-V-NEXT: 1 | void vdtors::X::zzz()
- // VDTORS-V: Thunks for 'vdtors::W::~W()' (1 entry).
+ // VDTORS-V: Thunks for 'vdtors::V::~V()' (1 entry).
// VDTORS-V-NEXT: 0 | [this adjustment: -4 non-virtual]
// VDTORS-V: VFTable indices for 'vdtors::V' (1 entry).
struct W : Z {
// RET-W: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries).
// RET-W-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo()
- // RET-W-NEXT: [return adjustment: vbase #1, 0 non-virtual]
+ // RET-W-NEXT: [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
// RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
+ // RET-W: Thunks for 'return_adjustment::X *return_adjustment::W::foo()' (1 entry).
+ // RET-W-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
+
// RET-W: VFTable indices for 'return_adjustment::W' (1 entry).
// RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
struct T : W {
// RET-T: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries).
// RET-T-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo()
- // RET-T-NEXT: [return adjustment: vbase #1, 0 non-virtual]
+ // RET-T-NEXT: [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
// RET-T-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo()
- // RET-T-NEXT: [return adjustment: vbase #2, 0 non-virtual]
+ // RET-T-NEXT: [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual]
// RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
+ // RET-T: Thunks for 'return_adjustment::Y *return_adjustment::T::foo()' (2 entries).
+ // RET-T-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
+ // RET-T-NEXT: 1 | [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual]
+
// RET-T: VFTable indices for 'return_adjustment::T' (1 entry).
// RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
struct V : Z {
// RET-V: VFTable for 'return_adjustment::Z' in 'return_adjustment::V' (2 entries).
// RET-V-NEXT: 0 | return_adjustment::U *return_adjustment::V::foo()
- // RET-V-NEXT: [return adjustment: vbptr at offset 4, vbase #1, 0 non-virtual]
+ // RET-V-NEXT: [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual]
// RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo()
+ // RET-V: Thunks for 'return_adjustment::U *return_adjustment::V::foo()' (1 entry).
+ // RET-V-NEXT: 0 | [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual]
+
// RET-V: VFTable indices for 'return_adjustment::V' (1 entry).
// RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo()