[ms-cxxabi] Emit fewer trivial return adjusting thunks

Most importantly, this makes our vtable layout match MSVC's.  Previously
we would emit a return adjusting thunk whenever the return types
differed, even if the adjustment would have been trivial.

MSVC does emit some trivial return adjusting thunks, but only if there
was already an overridden method that required a return adjustment.

llvm-svn: 198080
This commit is contained in:
Reid Kleckner
2013-12-27 19:43:59 +00:00
parent ac2002973c
commit 604c8b45e4
3 changed files with 131 additions and 7 deletions

View File

@@ -2541,6 +2541,8 @@ private:
}
}
bool VFTableBuilder::NeedsReturnAdjustingThunk(const CXXMethodDecl *MD);
/// AddMethods - Add the methods of this base subobject and the relevant
/// subbases to the vftable we're currently laying out.
void AddMethods(BaseSubobject Base, unsigned BaseDepth,
@@ -2800,6 +2802,24 @@ static void GroupNewVirtualOverloads(
VirtualMethods.append(Groups[I].rbegin(), Groups[I].rend());
}
/// We need a return adjusting thunk for this method if its return type is
/// not trivially convertible to the return type of any of its overridden
/// methods.
bool VFTableBuilder::NeedsReturnAdjustingThunk(const CXXMethodDecl *MD) {
OverriddenMethodsSetTy OverriddenMethods;
ComputeAllOverriddenMethods(MD, OverriddenMethods);
for (OverriddenMethodsSetTy::iterator I = OverriddenMethods.begin(),
E = OverriddenMethods.end();
I != E; ++I) {
const CXXMethodDecl *OverriddenMD = *I;
BaseOffset Adjustment =
ComputeReturnAdjustmentBaseOffset(Context, MD, OverriddenMD);
if (!Adjustment.isEmpty())
return true;
}
return false;
}
void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
const CXXRecordDecl *LastVBase,
BasesSetVectorTy &VisitedBases) {
@@ -2885,8 +2905,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
AddThunk(MD, VTableThunks[OverriddenMethodInfo.VFTableIndex]);
}
if (Context.hasSameType(MD->getResultType(),
OverriddenMD->getResultType())) {
if (!NeedsReturnAdjustingThunk(MD)) {
// No return adjustment needed - just replace the overridden method info
// with the current info.
MethodInfo MI(OverriddenMethodInfo.VBTableIndex,
@@ -3049,6 +3068,8 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) {
case VTableComponent::CK_FunctionPointer: {
const CXXMethodDecl *MD = Component.getFunctionDecl();
// FIXME: Figure out how to print the real thunk type, since they can
// differ in the return type.
std::string Str = PredefinedExpr::ComputeName(
PredefinedExpr::PrettyFunctionNoVirtual, MD);
Out << Str;

View File

@@ -0,0 +1,104 @@
// RUN: %clang_cc1 -fno-rtti %s -emit-llvm -o %t -cxx-abi microsoft -triple=i386-pc-win32 -fdump-vtable-layouts 2>&1 | FileCheck --check-prefix=VFTABLES %s
// RUN: FileCheck --check-prefix=GLOBALS %s < %t
// RUN: FileCheck --check-prefix=CODEGEN %s < %t
namespace test1 {
// Some covariant types.
struct A { int a; };
struct B { int b; };
struct C : A, B { int c; };
struct D : C { int d; };
struct E : D { int e; };
// One base class and two overrides, all with covariant return types.
struct H { virtual B *foo(); };
struct I : H { virtual C *foo(); };
struct J : I { virtual D *foo(); J(); };
struct K : J { virtual E *foo(); K(); };
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()
// 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.
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()
// Only B to C requires adjustment, but we get 3 thunks in K's vftable, two of
// which are trivial.
// GLOBALS-LABEL: @"\01??_7K@test1@@6B@" = linkonce_odr unnamed_addr constant [4 x i8*]
// 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.
// This thunk has a return adjustment.
// CODEGEN-LABEL: define {{.*}} @"\01?foo@K@test1@@QAEPAUB@2@XZ"
// CODEGEN: call {{.*}} @"\01?foo@K@test1@@UAEPAUE@2@XZ"
// CODEGEN: icmp {{.*}}, null
// CODEGEN: getelementptr
// CODEGEN: ret
// These two don't.
// CODEGEN-LABEL: define {{.*}} @"\01?foo@K@test1@@QAEPAUC@2@XZ"
// CODEGEN: call {{.*}} @"\01?foo@K@test1@@UAEPAUE@2@XZ"
// CODEGEN-NEXT: ret
// CODEGEN-LABEL: define {{.*}} @"\01?foo@K@test1@@QAEPAUD@2@XZ"
// CODEGEN: call {{.*}} @"\01?foo@K@test1@@UAEPAUE@2@XZ"
// CODEGEN-NEXT: ret
}
namespace test2 {
// Covariant types. D* is not trivially convertible to C*.
struct A { int a; };
struct B { int b; };
struct C : B { int c; };
struct D : A, C { int d; };
struct E : D { int e; };
// J's foo will require an adjusting thunk, and K will require a trivial thunk.
struct H { virtual B *foo(); };
struct I : H { virtual C *foo(); };
struct J : I { virtual D *foo(); J(); };
struct K : J { virtual E *foo(); K(); };
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()
// 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()
// GLOBALS-LABEL: @"\01??_7K@test2@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*]
}

View File

@@ -252,12 +252,11 @@ struct N {
N n;
typedef int int_type;
struct O { virtual int f(); };
struct P : O { virtual int_type f(); };
struct O { virtual A *f(); };
struct P : O { virtual B *f(); };
P p;
// CHECK-O: VFTable for 'O' in 'P' (1 entries)
// CHECK-O-NEXT: 0 | int_type P::f()
// CHECK-O-NEXT: 0 | B *P::f()
// CHECK-O: VFTable for 'O' (1 entries)
// CHECK-O-NEXT: 0 | int O::f()
// CHECK-O-NEXT: 0 | A *O::f()