Skip to content

Commit 16055e7

Browse files
committed
Fix PR20444 -- wrong number of vftable slots created when return adjustment thunks are needed
Reviewed at http://reviews.llvm.org/D4822 llvm-svn: 215312
1 parent b696d46 commit 16055e7

File tree

2 files changed

+71
-30
lines changed

2 files changed

+71
-30
lines changed

clang/lib/AST/VTableBuilder.cpp

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2464,11 +2464,18 @@ class VFTableBuilder {
24642464
/// or used for vcalls in the most derived class.
24652465
bool Shadowed;
24662466

2467-
MethodInfo(uint64_t VBTableIndex, uint64_t VFTableIndex)
2467+
/// UsesExtraSlot - Indicates if this vftable slot was created because
2468+
/// any of the overridden slots required a return adjusting thunk.
2469+
bool UsesExtraSlot;
2470+
2471+
MethodInfo(uint64_t VBTableIndex, uint64_t VFTableIndex,
2472+
bool UsesExtraSlot = false)
24682473
: VBTableIndex(VBTableIndex), VFTableIndex(VFTableIndex),
2469-
Shadowed(false) {}
2474+
Shadowed(false), UsesExtraSlot(UsesExtraSlot) {}
24702475

2471-
MethodInfo() : VBTableIndex(0), VFTableIndex(0), Shadowed(false) {}
2476+
MethodInfo()
2477+
: VBTableIndex(0), VFTableIndex(0), Shadowed(false),
2478+
UsesExtraSlot(false) {}
24722479
};
24732480

24742481
typedef llvm::DenseMap<const CXXMethodDecl *, MethodInfo> MethodInfoMapTy;
@@ -2525,8 +2532,6 @@ class VFTableBuilder {
25252532
}
25262533
}
25272534

2528-
bool NeedsReturnAdjustingThunk(const CXXMethodDecl *MD);
2529-
25302535
/// AddMethods - Add the methods of this base subobject and the relevant
25312536
/// subbases to the vftable we're currently laying out.
25322537
void AddMethods(BaseSubobject Base, unsigned BaseDepth,
@@ -2789,24 +2794,6 @@ static void GroupNewVirtualOverloads(
27892794
VirtualMethods.append(Groups[I].rbegin(), Groups[I].rend());
27902795
}
27912796

2792-
/// We need a return adjusting thunk for this method if its return type is
2793-
/// not trivially convertible to the return type of any of its overridden
2794-
/// methods.
2795-
bool VFTableBuilder::NeedsReturnAdjustingThunk(const CXXMethodDecl *MD) {
2796-
OverriddenMethodsSetTy OverriddenMethods;
2797-
ComputeAllOverriddenMethods(MD, OverriddenMethods);
2798-
for (OverriddenMethodsSetTy::iterator I = OverriddenMethods.begin(),
2799-
E = OverriddenMethods.end();
2800-
I != E; ++I) {
2801-
const CXXMethodDecl *OverriddenMD = *I;
2802-
BaseOffset Adjustment =
2803-
ComputeReturnAdjustmentBaseOffset(Context, MD, OverriddenMD);
2804-
if (!Adjustment.isEmpty())
2805-
return true;
2806-
}
2807-
return false;
2808-
}
2809-
28102797
static bool isDirectVBase(const CXXRecordDecl *Base, const CXXRecordDecl *RD) {
28112798
for (const auto &B : RD->bases()) {
28122799
if (B.isVirtual() && B.getType()->getAsCXXRecordDecl() == Base)
@@ -2873,7 +2860,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
28732860
FindNearestOverriddenMethod(MD, VisitedBases);
28742861

28752862
ThisAdjustment ThisAdjustmentOffset;
2876-
bool ReturnAdjustingThunk = false;
2863+
bool ReturnAdjustingThunk = false, ForceReturnAdjustmentMangling = false;
28772864
CharUnits ThisOffset = ComputeThisOffset(Overrider);
28782865
ThisAdjustmentOffset.NonVirtual =
28792866
(ThisOffset - WhichVFPtr.FullOffsetInMDC).getQuantity();
@@ -2892,7 +2879,16 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
28922879

28932880
MethodInfo &OverriddenMethodInfo = OverriddenMDIterator->second;
28942881

2895-
if (!NeedsReturnAdjustingThunk(MD)) {
2882+
// Let's check if the overrider requires any return adjustments.
2883+
// We must create a new slot if the MD's return type is not trivially
2884+
// convertible to the OverriddenMD's one.
2885+
// Once a chain of method overrides adds a return adjusting vftable slot,
2886+
// all subsequent overrides will also use an extra method slot.
2887+
ReturnAdjustingThunk = !ComputeReturnAdjustmentBaseOffset(
2888+
Context, MD, OverriddenMD).isEmpty() ||
2889+
OverriddenMethodInfo.UsesExtraSlot;
2890+
2891+
if (!ReturnAdjustingThunk) {
28962892
// No return adjustment needed - just replace the overridden method info
28972893
// with the current info.
28982894
MethodInfo MI(OverriddenMethodInfo.VBTableIndex,
@@ -2911,7 +2907,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
29112907

29122908
// Force a special name mangling for a return-adjusting thunk
29132909
// unless the method is the final overrider without this adjustment.
2914-
ReturnAdjustingThunk =
2910+
ForceReturnAdjustmentMangling =
29152911
!(MD == OverriderMD && ThisAdjustmentOffset.isEmpty());
29162912
} else if (Base.getBaseOffset() != WhichVFPtr.FullOffsetInMDC ||
29172913
MD->size_overridden_methods()) {
@@ -2926,7 +2922,8 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
29262922
unsigned VBIndex =
29272923
LastVBase ? VTables.getVBTableIndex(MostDerivedClass, LastVBase) : 0;
29282924
MethodInfo MI(VBIndex,
2929-
HasRTTIComponent ? Components.size() - 1 : Components.size());
2925+
HasRTTIComponent ? Components.size() - 1 : Components.size(),
2926+
ReturnAdjustingThunk);
29302927

29312928
assert(!MethodInfoMap.count(MD) &&
29322929
"Should not have method info for this method yet!");
@@ -2941,7 +2938,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
29412938
ComputeReturnAdjustmentBaseOffset(Context, OverriderMD, MD);
29422939
}
29432940
if (!ReturnAdjustmentOffset.isEmpty()) {
2944-
ReturnAdjustingThunk = true;
2941+
ForceReturnAdjustmentMangling = true;
29452942
ReturnAdjustment.NonVirtual =
29462943
ReturnAdjustmentOffset.NonVirtualOffset.getQuantity();
29472944
if (ReturnAdjustmentOffset.VirtualBase) {
@@ -2955,8 +2952,9 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
29552952
}
29562953
}
29572954

2958-
AddMethod(OverriderMD, ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment,
2959-
ReturnAdjustingThunk ? MD : nullptr));
2955+
AddMethod(OverriderMD,
2956+
ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment,
2957+
ForceReturnAdjustmentMangling ? MD : nullptr));
29602958
}
29612959
}
29622960

clang/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance-return-adjustment.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,46 @@ struct X : E {
295295

296296
void build_vftable(X *obj) { obj->foo(); }
297297
}
298+
299+
namespace pr20444 {
300+
struct A {
301+
virtual A* f();
302+
};
303+
struct B {
304+
virtual B* f();
305+
};
306+
struct C : A, B {
307+
virtual C* f();
308+
// CHECK-LABEL: VFTable for 'pr20444::A' in 'pr20444::C' (1 entry).
309+
// CHECK-NEXT: 0 | pr20444::C *pr20444::C::f()
310+
311+
// CHECK-LABEL: VFTable for 'pr20444::B' in 'pr20444::C' (2 entries).
312+
// CHECK-NEXT: 0 | pr20444::C *pr20444::C::f()
313+
// CHECK-NEXT: [return adjustment (to type 'struct pr20444::B *'): 4 non-virtual]
314+
// CHECK-NEXT: [this adjustment: -4 non-virtual]
315+
// CHECK-NEXT: 1 | pr20444::C *pr20444::C::f()
316+
// CHECK-NEXT: [return adjustment (to type 'struct pr20444::C *'): 0 non-virtual]
317+
// CHECK-NEXT: [this adjustment: -4 non-virtual]
318+
};
319+
320+
void build_vftable(C *obj) { obj->f(); }
321+
322+
struct D : C {
323+
virtual D* f();
324+
// CHECK-LABEL: VFTable for 'pr20444::A' in 'pr20444::C' in 'pr20444::D' (1 entry).
325+
// CHECK-NEXT: 0 | pr20444::D *pr20444::D::f()
326+
327+
// CHECK-LABEL: VFTable for 'pr20444::B' in 'pr20444::C' in 'pr20444::D' (3 entries).
328+
// CHECK-NEXT: 0 | pr20444::D *pr20444::D::f()
329+
// CHECK-NEXT: [return adjustment (to type 'struct pr20444::B *'): 4 non-virtual]
330+
// CHECK-NEXT: [this adjustment: -4 non-virtual]
331+
// CHECK-NEXT: 1 | pr20444::D *pr20444::D::f()
332+
// CHECK-NEXT: [return adjustment (to type 'struct pr20444::C *'): 0 non-virtual]
333+
// CHECK-NEXT: [this adjustment: -4 non-virtual]
334+
// CHECK-NEXT: 2 | pr20444::D *pr20444::D::f()
335+
// CHECK-NEXT: [return adjustment (to type 'struct pr20444::D *'): 0 non-virtual]
336+
// CHECK-NEXT: [this adjustment: -4 non-virtual]
337+
};
338+
339+
void build_vftable(D *obj) { obj->f(); }
340+
}

0 commit comments

Comments
 (0)