From 58b6db76adab8dee2e64fbc300360f9b46c561ba Mon Sep 17 00:00:00 2001 From: Timur Iskhodzhanov Date: Wed, 6 Nov 2013 06:24:31 +0000 Subject: [PATCH] Fix PR17738 - add support for vtordisp thunks when using -cxx-abi microsoft git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@194132 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/ABI.h | 52 ++- lib/AST/ItaniumMangle.cpp | 7 +- lib/AST/MicrosoftMangle.cpp | 71 +++- lib/AST/VTableBuilder.cpp | 109 ++++-- lib/CodeGen/ItaniumCXXABI.cpp | 3 +- lib/CodeGen/MicrosoftCXXABI.cpp | 25 +- test/CodeGenCXX/microsoft-abi-thunks.cpp | 2 - ...soft-abi-virtual-inheritance-vtordisps.cpp | 83 +++++ ...-vtables-virtual-inheritance-vtordisps.cpp | 324 ++++++++++++++++++ 9 files changed, 621 insertions(+), 55 deletions(-) create mode 100644 test/CodeGenCXX/microsoft-abi-virtual-inheritance-vtordisps.cpp create mode 100644 test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp diff --git a/include/clang/Basic/ABI.h b/include/clang/Basic/ABI.h index 9f214f3d34..3b3d59efc0 100644 --- a/include/clang/Basic/ABI.h +++ b/include/clang/Basic/ABI.h @@ -106,18 +106,53 @@ struct ThisAdjustment { /// nearest virtual base. int64_t NonVirtual; - /// \brief The offset (in bytes), relative to the address point, - /// of the virtual call offset. - int64_t VCallOffsetOffset; + /// \brief Holds the ABI-specific information about the virtual this + /// adjustment, if needed. + union VirtualAdjustment { + // Itanium ABI + struct { + /// \brief The offset (in bytes), relative to the address point, + /// of the virtual call offset. + int64_t VCallOffsetOffset; + } Itanium; + + struct { + /// \brief The offset of the vtordisp (in bytes), relative to the ECX. + int32_t VtordispOffset; + + /// \brief The offset of the vbptr of the derived class (in bytes), + /// relative to the ECX after vtordisp adjustment. + int32_t VBPtrOffset; + + /// \brief The offset (in bytes) of the vbase offset in the vbtable. + int32_t VBOffsetOffset; + } Microsoft; + + VirtualAdjustment() { + memset(this, 0, sizeof(*this)); + } + + bool Equals(const VirtualAdjustment &Other) const { + return memcmp(this, &Other, sizeof(Other)) == 0; + } + + bool isEmpty() const { + VirtualAdjustment Zero; + return Equals(Zero); + } + + bool Less(const VirtualAdjustment &RHS) const { + return memcmp(this, &RHS, sizeof(RHS)) < 0; + } + } Virtual; - ThisAdjustment() : NonVirtual(0), VCallOffsetOffset(0) { } + ThisAdjustment() : NonVirtual(0) { } - bool isEmpty() const { return !NonVirtual && !VCallOffsetOffset; } + bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); } friend bool operator==(const ThisAdjustment &LHS, const ThisAdjustment &RHS) { - return LHS.NonVirtual == RHS.NonVirtual && - LHS.VCallOffsetOffset == RHS.VCallOffsetOffset; + return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Equals(RHS.Virtual); } friend bool operator!=(const ThisAdjustment &LHS, const ThisAdjustment &RHS) { @@ -129,8 +164,7 @@ struct ThisAdjustment { if (LHS.NonVirtual < RHS.NonVirtual) return true; - return LHS.NonVirtual == RHS.NonVirtual && - LHS.VCallOffsetOffset < RHS.VCallOffsetOffset; + return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Less(RHS.Virtual); } }; diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 953bff20ad..3d0e7253d7 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -3657,8 +3657,9 @@ void ItaniumMangleContextImpl::mangleThunk(const CXXMethodDecl *MD, Mangler.getStream() << 'c'; // Mangle the 'this' pointer adjustment. - Mangler.mangleCallOffset(Thunk.This.NonVirtual, Thunk.This.VCallOffsetOffset); - + Mangler.mangleCallOffset(Thunk.This.NonVirtual, + Thunk.This.Virtual.Itanium.VCallOffsetOffset); + // Mangle the return pointer adjustment if there is one. if (!Thunk.Return.isEmpty()) Mangler.mangleCallOffset(Thunk.Return.NonVirtual, @@ -3677,7 +3678,7 @@ void ItaniumMangleContextImpl::mangleCXXDtorThunk( // Mangle the 'this' pointer adjustment. Mangler.mangleCallOffset(ThisAdjustment.NonVirtual, - ThisAdjustment.VCallOffsetOffset); + ThisAdjustment.Virtual.Itanium.VCallOffsetOffset); Mangler.mangleFunctionEncoding(DD); } diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 08d1eb07a3..c36c16745c 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -121,7 +121,7 @@ public: void mangleDeclaration(const NamedDecl *ND); void mangleFunctionEncoding(const FunctionDecl *FD); void mangleVariableEncoding(const VarDecl *VD); - void mangleNumber(int64_t Number); + void mangleNumber(uint32_t Number); void mangleNumber(const llvm::APSInt &Value); void mangleType(QualType T, SourceRange Range, QualifierMangleMode QMM = QMM_Mangle); @@ -387,8 +387,8 @@ void MicrosoftCXXNameMangler::mangleName(const NamedDecl *ND) { Out << '@'; } -void MicrosoftCXXNameMangler::mangleNumber(int64_t Number) { - llvm::APSInt APSNumber(/*BitWidth=*/64, /*isUnsigned=*/false); +void MicrosoftCXXNameMangler::mangleNumber(uint32_t Number) { + llvm::APSInt APSNumber(/*BitWidth=*/32, /*isUnsigned=*/true); APSNumber = Number; mangleNumber(APSNumber); } @@ -836,7 +836,7 @@ void MicrosoftCXXNameMangler::mangleLocalName(const FunctionDecl *FD) { // functions. You could have a method baz of class C inside a function bar // inside a function foo, like so: // ?baz@C@?3??bar@?1??foo@@YAXXZ@YAXXZ@QAEXXZ - int NestLevel = getLocalNestingLevel(FD); + unsigned NestLevel = getLocalNestingLevel(FD); Out << '?'; mangleNumber(NestLevel); Out << '?'; @@ -1367,24 +1367,18 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) { // ::= D # private: static far // ::= E # private: virtual near // ::= F # private: virtual far - // ::= G # private: thunk near - // ::= H # private: thunk far // ::= I # protected: near // ::= J # protected: far // ::= K # protected: static near // ::= L # protected: static far // ::= M # protected: virtual near // ::= N # protected: virtual far - // ::= O # protected: thunk near - // ::= P # protected: thunk far // ::= Q # public: near // ::= R # public: far // ::= S # public: static near // ::= T # public: static far // ::= U # public: virtual near // ::= V # public: virtual far - // ::= W # public: thunk near - // ::= X # public: thunk far // ::= Y # global near // ::= Z # global far if (const CXXMethodDecl *MD = dyn_cast(FD)) { @@ -1843,12 +1837,61 @@ void MicrosoftMangleContextImpl::mangleCXXName(const NamedDecl *D, return Mangler.mangle(D); } +// ::= | | +// +// ::= A # private near +// ::= B # private far +// ::= I # protected near +// ::= J # protected far +// ::= Q # public near +// ::= R # public far +// ::= G # private near +// ::= H # private far +// ::= O # protected near +// ::= P # protected far +// ::= W # public near +// ::= X # public far +// ::= $0 # private near +// ::= $1 # private far +// ::= $2 # protected near +// ::= $3 # protected far +// ::= $4 # public near +// ::= $5 # public far +// ::= | +// ::= +// ::= +// static void mangleThunkThisAdjustment(const CXXMethodDecl *MD, const ThisAdjustment &Adjustment, MicrosoftCXXNameMangler &Mangler, raw_ostream &Out) { - // FIXME: add support for vtordisp thunks. - if (Adjustment.NonVirtual != 0) { + if (!Adjustment.Virtual.isEmpty()) { + Out << '$'; + char AccessSpec; + switch (MD->getAccess()) { + case AS_none: + llvm_unreachable("Unsupported access specifier"); + case AS_private: + AccessSpec = '0'; + break; + case AS_protected: + AccessSpec = '2'; + break; + case AS_public: + AccessSpec = '4'; + } + if (Adjustment.Virtual.Microsoft.VBPtrOffset) { + Out << 'R' << AccessSpec; + Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VBPtrOffset); + Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VBOffsetOffset); + Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VtordispOffset); + Mangler.mangleNumber(Adjustment.NonVirtual); + } else { + Out << AccessSpec; + Mangler.mangleNumber(Adjustment.Virtual.Microsoft.VtordispOffset); + Mangler.mangleNumber(-Adjustment.NonVirtual); + } + } else if (Adjustment.NonVirtual != 0) { switch (MD->getAccess()) { case AS_none: llvm_unreachable("Unsupported access specifier"); @@ -1861,9 +1904,7 @@ static void mangleThunkThisAdjustment(const CXXMethodDecl *MD, case AS_public: Out << 'W'; } - llvm::APSInt APSNumber(/*BitWidth=*/32, /*isUnsigned=*/true); - APSNumber = -Adjustment.NonVirtual; - Mangler.mangleNumber(APSNumber); + Mangler.mangleNumber(-Adjustment.NonVirtual); } else { switch (MD->getAccess()) { case AS_none: diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp index 69a2fb2136..efaa313a67 100644 --- a/lib/AST/VTableBuilder.cpp +++ b/lib/AST/VTableBuilder.cpp @@ -1304,7 +1304,7 @@ ThisAdjustment ItaniumVTableBuilder::ComputeThisAdjustment( VCallOffsets = Builder.getVCallOffsets(); } - Adjustment.VCallOffsetOffset = + Adjustment.Virtual.Itanium.VCallOffsetOffset = VCallOffsets.getVCallOffsetOffset(MD).getQuantity(); } @@ -1552,7 +1552,7 @@ void ItaniumVTableBuilder::AddMethods( ComputeThisAdjustment(OverriddenMD, BaseOffsetInLayoutClass, Overrider); - if (ThisAdjustment.VCallOffsetOffset && + if (ThisAdjustment.Virtual.Itanium.VCallOffsetOffset && Overrider.Method->getParent() == MostDerivedClass) { // There's no return adjustment from OverriddenMD and MD, @@ -2009,8 +2009,8 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { Out << "\n [this adjustment: "; Out << Thunk.This.NonVirtual << " non-virtual"; - if (Thunk.This.VCallOffsetOffset) { - Out << ", " << Thunk.This.VCallOffsetOffset; + if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) { + Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset; Out << " vcall offset offset"; } @@ -2044,8 +2044,8 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { Out << "\n [this adjustment: "; Out << Thunk.This.NonVirtual << " non-virtual"; - if (Thunk.This.VCallOffsetOffset) { - Out << ", " << Thunk.This.VCallOffsetOffset; + if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) { + Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset; Out << " vcall offset offset"; } @@ -2186,8 +2186,8 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { Out << "this adjustment: "; Out << Thunk.This.NonVirtual << " non-virtual"; - if (Thunk.This.VCallOffsetOffset) { - Out << ", " << Thunk.This.VCallOffsetOffset; + if (Thunk.This.Virtual.Itanium.VCallOffsetOffset) { + Out << ", " << Thunk.This.Virtual.Itanium.VCallOffsetOffset; Out << " vcall offset offset"; } } @@ -2527,6 +2527,9 @@ private: BaseSubobject Base, FinalOverriders::OverriderInfo Overrider); + void CalculateVtordispAdjustment(FinalOverriders::OverriderInfo Overrider, + CharUnits ThisOffset, ThisAdjustment &TA); + /// AddMethod - Add a single virtual member function to the vftable /// components vector. void AddMethod(const CXXMethodDecl *MD, ThunkInfo TI) { @@ -2672,7 +2675,7 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD, I != E; ++I) { const CXXBasePath &Path = (*I); CharUnits ThisOffset = Base.getBaseOffset(); - bool SeenVBase = false; + CharUnits LastVBaseOffset; // For each path from the overrider to the parents of the overridden methods, // traverse the path, calculating the this offset in the most derived class. @@ -2684,7 +2687,7 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD, const ASTRecordLayout &Layout = Context.getASTRecordLayout(PrevRD); if (Element.Base->isVirtual()) { - SeenVBase = true; + LastVBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(CurRD); if (Overrider.Method->getParent() == PrevRD) { // This one's interesting. If the final overrider is in a vbase B of the // most derived class and it overrides a method of the B's own vbase A, @@ -2695,23 +2698,25 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD, // differently in the most derived class. ThisOffset += Layout.getVBaseClassOffset(CurRD); } else { - ThisOffset = MostDerivedClassLayout.getVBaseClassOffset(CurRD); + ThisOffset = LastVBaseOffset; } - - // A virtual destructor of a virtual base takes the address of the - // virtual base subobject as the "this" argument. - if (isa(MD)) - break; } else { ThisOffset += Layout.getBaseClassOffset(CurRD); } } - // If a "Base" class has at least one non-virtual base with a virtual - // destructor, the "Base" virtual destructor will take the address of the - // "Base" subobject as the "this" argument. - if (!SeenVBase && isa(MD)) - return Base.getBaseOffset(); + if (isa(MD)) { + if (LastVBaseOffset.isZero()) { + // If a "Base" class has at least one non-virtual base with a virtual + // destructor, the "Base" virtual destructor will take the address + // of the "Base" subobject as the "this" argument. + return Base.getBaseOffset(); + } else { + // A virtual destructor of a virtual base takes the address of the + // virtual base subobject as the "this" argument. + return LastVBaseOffset; + } + } if (Ret > ThisOffset || First) { First = false; @@ -2723,6 +2728,49 @@ VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD, return Ret; } +void VFTableBuilder::CalculateVtordispAdjustment( + FinalOverriders::OverriderInfo Overrider, CharUnits ThisOffset, + ThisAdjustment &TA) { + const ASTRecordLayout::VBaseOffsetsMapTy &VBaseMap = + MostDerivedClassLayout.getVBaseOffsetsMap(); + const ASTRecordLayout::VBaseOffsetsMapTy::const_iterator &VBaseMapEntry = + VBaseMap.find(WhichVFPtr.LastVBase); + assert(VBaseMapEntry != VBaseMap.end()); + + // Check if we need a vtordisp adjustment at all. + if (!VBaseMapEntry->second.hasVtorDisp()) + return; + + CharUnits VFPtrVBaseOffset = VBaseMapEntry->second.VBaseOffset; + // The implicit vtordisp field is located right before the vbase. + TA.Virtual.Microsoft.VtordispOffset = + (VFPtrVBaseOffset - WhichVFPtr.VFPtrFullOffset).getQuantity() - 4; + + // If the final overrider is defined in either: + // - the most derived class or its non-virtual base or + // - the same vbase as the initial declaration, + // a simple vtordisp thunk will suffice. + const CXXRecordDecl *OverriderRD = Overrider.Method->getParent(); + if (OverriderRD == MostDerivedClass) + return; + + const CXXRecordDecl *OverriderVBase = + ComputeBaseOffset(Context, OverriderRD, MostDerivedClass).VirtualBase; + if (!OverriderVBase || OverriderVBase == WhichVFPtr.LastVBase) + return; + + // Otherwise, we need to do use the dynamic offset of the final overrider + // in order to get "this" adjustment right. + TA.Virtual.Microsoft.VBPtrOffset = + (VFPtrVBaseOffset + WhichVFPtr.VFPtrOffset - + MostDerivedClassLayout.getVBPtrOffset()).getQuantity(); + TA.Virtual.Microsoft.VBOffsetOffset = + Context.getTypeSizeInChars(Context.IntTy).getQuantity() * + VTables.getVBTableIndex(MostDerivedClass, OverriderVBase); + + TA.NonVirtual = (ThisOffset - Overrider.Offset).getQuantity(); +} + static void GroupNewVirtualOverloads( const CXXRecordDecl *RD, SmallVector &VirtualMethods) { @@ -2829,6 +2877,12 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, if (TI != WhichVFPtr.VFPtrFullOffset) { ThisAdjustmentOffset.NonVirtual = (TI - WhichVFPtr.VFPtrFullOffset).getQuantity(); + } + + if (WhichVFPtr.LastVBase) + CalculateVtordispAdjustment(Overrider, TI, ThisAdjustmentOffset); + + if (!ThisAdjustmentOffset.isEmpty()) { VTableThunks[OverriddenMethodInfo.VFTableIndex].This = ThisAdjustmentOffset; AddThunk(MD, VTableThunks[OverriddenMethodInfo.VFTableIndex]); @@ -2962,8 +3016,17 @@ static void dumpMicrosoftThunkAdjustment(const ThunkInfo &TI, raw_ostream &Out, if (Multiline || !ContinueFirstLine) Out << LinePrefix; Out << "[this adjustment: "; - assert(TI.This.VCallOffsetOffset == 0 && - "VtorDisp adjustment is not supported yet"); + if (!TI.This.Virtual.isEmpty()) { + assert(T.Virtual.Microsoft.VtordispOffset < 0); + Out << "vtordisp at " << T.Virtual.Microsoft.VtordispOffset << ", "; + if (T.Virtual.Microsoft.VBPtrOffset) { + Out << "vbptr at " << T.Virtual.Microsoft.VBPtrOffset + << " to the left, "; + assert(T.Virtual.Microsoft.VBOffsetOffset > 0); + Out << LinePrefix << " vboffset at " + << T.Virtual.Microsoft.VBOffsetOffset << " in the vbtable, "; + } + } Out << T.NonVirtual << " non-virtual]"; } } diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 9b7cf17030..0e8f31a484 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -1118,7 +1118,8 @@ static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, llvm::Value *ItaniumCXXABI::performThisAdjustment(CodeGenFunction &CGF, llvm::Value *This, const ThisAdjustment &TA) { - return performTypeAdjustment(CGF, This, TA.NonVirtual, TA.VCallOffsetOffset, + return performTypeAdjustment(CGF, This, TA.NonVirtual, + TA.Virtual.Itanium.VCallOffsetOffset, /*IsReturnAdjustment=*/false); } diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 7e3a47d913..6415749e2d 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -988,8 +988,29 @@ llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF, llvm::Value *V = CGF.Builder.CreateBitCast(This, CGF.Int8PtrTy); - assert(TA.VCallOffsetOffset == 0 && - "VtorDisp adjustment is not supported yet"); + if (!TA.Virtual.isEmpty()) { + assert(TA.Virtual.Microsoft.VtordispOffset < 0); + // Adjust the this argument based on the vtordisp value. + llvm::Value *VtorDispPtr = + CGF.Builder.CreateConstGEP1_32(V, TA.Virtual.Microsoft.VtordispOffset); + VtorDispPtr = + CGF.Builder.CreateBitCast(VtorDispPtr, CGF.Int32Ty->getPointerTo()); + llvm::Value *VtorDisp = CGF.Builder.CreateLoad(VtorDispPtr, "vtordisp"); + V = CGF.Builder.CreateGEP(V, CGF.Builder.CreateNeg(VtorDisp)); + + if (TA.Virtual.Microsoft.VBPtrOffset) { + // If the final overrider is defined in a virtual base other than the one + // that holds the vfptr, we have to use a vtordispex thunk which looks up + // the vbtable of the derived class. + assert(TA.Virtual.Microsoft.VBPtrOffset > 0); + assert(TA.Virtual.Microsoft.VBOffsetOffset >= 0); + llvm::Value *VBPtr; + llvm::Value *VBaseOffset = + GetVBaseOffsetFromVBPtr(CGF, V, -TA.Virtual.Microsoft.VBPtrOffset, + TA.Virtual.Microsoft.VBOffsetOffset, &VBPtr); + V = CGF.Builder.CreateInBoundsGEP(VBPtr, VBaseOffset); + } + } if (TA.NonVirtual) { // Non-virtual adjustment might result in a pointer outside the allocated diff --git a/test/CodeGenCXX/microsoft-abi-thunks.cpp b/test/CodeGenCXX/microsoft-abi-thunks.cpp index 6b8270b1c6..f1bc385fdf 100644 --- a/test/CodeGenCXX/microsoft-abi-thunks.cpp +++ b/test/CodeGenCXX/microsoft-abi-thunks.cpp @@ -138,8 +138,6 @@ I::I() {} // Emits vftable and forces thunk generation. // CODEGEN: phi %struct.F* {{.*}} %[[RES]] // CODEGEN: ret %struct.{{[BF]}}* -// FIXME: Write vtordisp adjusting thunk tests - namespace CrashOnThunksForAttributedType { // We used to crash on this because the type of foo is an AttributedType, not // FunctionType, and we had to look through the sugar. diff --git a/test/CodeGenCXX/microsoft-abi-virtual-inheritance-vtordisps.cpp b/test/CodeGenCXX/microsoft-abi-virtual-inheritance-vtordisps.cpp new file mode 100644 index 0000000000..8e23ade658 --- /dev/null +++ b/test/CodeGenCXX/microsoft-abi-virtual-inheritance-vtordisps.cpp @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=i386-pc-win32 -emit-llvm -o - | FileCheck %s + +// For now, just make sure x86_64 doesn't crash. +// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=x86_64-pc-win32 -emit-llvm -o %t + +struct A { + virtual void f(); +}; + +struct B { + virtual void f(); +}; + +struct C : A, B {}; + +struct D : virtual C { + D(); + ~D(); + virtual void f(); + void g(); + int xxx; +}; + +D::D() {} // Forces vftable emission. + +// CHECK-LABEL: define weak x86_thiscallcc void @"\01?f@D@@$4PPPPPPPM@A@AEXXZ" +// CHECK: %[[ECX:.*]] = load %struct.D** %{{.*}} +// CHECK: %[[ECX_i8:.*]] = bitcast %struct.D* %[[ECX]] to i8* +// CHECK: %[[VTORDISP_PTR_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 -4 +// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_PTR_i8]] to i32* +// CHECK: %[[VTORDISP:.*]] = load i32* %[[VTORDISP_PTR]] +// CHECK: %[[VTORDISP_NEG:.*]] = sub i32 0, %[[VTORDISP]] +// CHECK: %[[ADJUSTED_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 %[[VTORDISP_NEG]] +// CHECK: call x86_thiscallcc void @"\01?f@D@@UAEXXZ"(i8* %[[ADJUSTED_i8]]) +// CHECK: ret void + +// CHECK-LABEL: define weak x86_thiscallcc void @"\01?f@D@@$4PPPPPPPI@3AEXXZ" +// CHECK: %[[ECX:.*]] = load %struct.D** %{{.*}} +// CHECK: %[[ECX_i8:.*]] = bitcast %struct.D* %[[ECX]] to i8* +// CHECK: %[[VTORDISP_PTR_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 -8 +// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_PTR_i8]] to i32* +// CHECK: %[[VTORDISP:.*]] = load i32* %[[VTORDISP_PTR]] +// CHECK: %[[VTORDISP_NEG:.*]] = sub i32 0, %[[VTORDISP]] +// CHECK: %[[VTORDISP_ADJUSTED_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 %[[VTORDISP_NEG]] +// CHECK: %[[ADJUSTED_i8:.*]] = getelementptr i8* %[[VTORDISP_ADJUSTED_i8]], i32 -4 +// CHECK: call x86_thiscallcc void @"\01?f@D@@UAEXXZ"(i8* %[[ADJUSTED_i8]]) +// CHECK: ret void + +struct E : virtual A { + virtual void f(); + ~E(); +}; + +struct F { + virtual void z(); +}; + +struct G : virtual F, virtual E { + int ggg; + G(); + ~G(); +}; + +G::G() {} // Forces vftable emission. + +// CHECK-LABEL: define weak x86_thiscallcc void @"\01?f@E@@$R4BA@M@PPPPPPPM@7AEXXZ"(i8*) +// CHECK: %[[ECX:.*]] = load %struct.E** %{{.*}} +// CHECK: %[[ECX_i8:.*]] = bitcast %struct.E* %[[ECX]] to i8* +// CHECK: %[[VTORDISP_PTR_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 -4 +// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_PTR_i8]] to i32* +// CHECK: %[[VTORDISP:.*]] = load i32* %[[VTORDISP_PTR]] +// CHECK: %[[VTORDISP_NEG:.*]] = sub i32 0, %[[VTORDISP]] +// CHECK: %[[VTORDISP_ADJUSTED_i8:.*]] = getelementptr i8* %[[ECX_i8]], i32 %[[VTORDISP_NEG]] +// CHECK: %[[VBPTR_i8:.*]] = getelementptr inbounds i8* %[[VTORDISP_ADJUSTED_i8]], i32 -16 +// CHECK: %[[VBPTR:.*]] = bitcast i8* %[[VBPTR_i8]] to i8** +// CHECK: %[[VBTABLE:.*]] = load i8** %[[VBPTR]] +// CHECK: %[[VBOFFSET_PTR_i8:.*]] = getelementptr inbounds i8* %[[VBTABLE]], i32 12 +// CHECK: %[[VBOFFSET_PTR:.*]] = bitcast i8* %[[VBOFFSET_PTR_i8]] to i32* +// CHECK: %[[VBASE_OFFSET:.*]] = load i32* %[[VBOFFSET_PTR]] +// CHECK: %[[VBASE:.*]] = getelementptr inbounds i8* %[[VBPTR_i8]], i32 %[[VBASE_OFFSET]] +// CHECK: %[[ARG_i8:.*]] = getelementptr i8* %[[VBASE]], i32 8 +// CHECK: call x86_thiscallcc void @"\01?f@E@@UAEXXZ"(i8* %[[ARG_i8]]) +// CHECK: ret void diff --git a/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp new file mode 100644 index 0000000000..67e85053fd --- /dev/null +++ b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp @@ -0,0 +1,324 @@ +// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o - -cxx-abi microsoft -triple=i386-pc-win32 >%t 2>&1 +// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-A %s < %t +// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-B %s < %t +// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-C %s < %t +// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-A %s < %t +// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-B %s < %t +// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-C %s < %t +// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-E %s < %t +// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-F %s < %t +// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-G %s < %t +// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-H %s < %t +// RUN: FileCheck --check-prefix=VTABLE-PR17738-A %s < %t +// RUN: FileCheck --check-prefix=MANGLING %s < %t + +// For now, just make sure x86_64 doesn't crash. +// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 >%t 2>&1 + +struct V1 { + virtual void f(); + virtual ~V1(); +}; + +struct V2 { + virtual void f(); + virtual ~V2(); + int v; +}; + +struct Z { + virtual void g(); + virtual ~Z(); + int x; +}; + +struct V3 : Z, V2 { +}; + +struct V4 : Z, V1, V2 { + int y; +}; + +void use_somewhere_else(void*); + +namespace simple { +// In case of a single-layer virtual inheritance, the "this" adjustment is done +// staically: +// struct A { +// virtual void f(); // Expects "(A*)this" in ECX +// }; +// struct B : virtual A { +// virtual void f(); // Expects "(char*)(B*)this + 12" in ECX +// virtual ~B(); // Might call f() +// }; +// +// If a class overrides a virtual function of its base and has a non-trivial +// ctor/dtor that call(s) the virtual function (or may escape "this" to some +// code that might call it), a virtual adjustment might be needed in case the +// current class layout and the most derived class layout are different. +// This is done using vtordisp thunks. +// +// A simple vtordisp{A,B} thunk for Method@Class is something like: +// sub ecx, [ecx+A] // apply the vtordisp adjustment +// sub ecx, B // apply the subobject adjustment, if needed. +// jmp Method@Class + +struct A : virtual V1 { + // VTABLE-SIMPLE-A: VFTable for 'V1' in 'simple::A' (2 entries). + // VTABLE-SIMPLE-A-NEXT: 0 | void simple::A::f() + // VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + // VTABLE-SIMPLE-A-NEXT: 1 | simple::A::~A() [scalar deleting] + // VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + + virtual void f(); + // MANGLING-DAG: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ" + + virtual ~A(); + // MANGLING-DAG: @"\01??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z" +}; + +A a; + +struct B : virtual V3 { + // VTABLE-SIMPLE-B: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries). + // VTABLE-SIMPLE-B-NEXT: 0 | void Z::g() + // 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: 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] + + // FIXME: The vtordisp thunk should only get emitted for a constructor + // if "this" leaves scope. + B() { use_somewhere_else(this); } + + virtual void f(); + // MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@A@AEXXZ" + + // Has an implicit destructor. + // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z" + // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z" +}; + +B b; + +struct C : virtual V4 { + // VTABLE-SIMPLE-C: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries). + // VTABLE-SIMPLE-C-NEXT: 0 | void Z::g() + // 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: 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: 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] + + int x; + virtual void f(); + // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ" + // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPE@A@AEXXZ" + virtual ~C(); + // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z" + // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z" + // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z" +}; + +C c; +} + +namespace extended { +// If a virtual function requires vtordisp adjustment and the final overrider +// is defined in another vitual base of the most derived class, +// we need to know two vbase offsets. +// In this case, we should use the extended form of vtordisp thunks, called +// vtordispex thunks. +// +// vtordispex{A,B,C,D} thunk for Method@Class is something like: +// sub ecx, [ecx+C] // apply the vtordisp adjustment +// sub ecx, A // jump to the vbtable of the most derived class +// mov eax, [ecx] // load the vbtable address +// add ecx, [eax+B] // lookup the final overrider's vbase offset +// add ecx, D // apphy the subobject offset if needed +// jmp Method@Class + +struct A : virtual simple::A { + // VTABLE-EXTENDED-A: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries). + // VTABLE-EXTENDED-A-NEXT: 0 | void simple::A::f() + // VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, + // VTABLE-EXTENDED-A-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] + // VTABLE-EXTENDED-A-NEXT: 1 | extended::A::~A() [scalar deleting] + // VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + + // `vtordispex{8,8,4294967292,8}' + // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ" + + virtual ~A(); + // vtordisp{4294967292,0} + // MANGLING-DAG: @"\01??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z" +}; + +A a; + +struct B : virtual simple::A { + // This class has an implicit dtor. Vdtors don't require vtordispex thunks + // as the most derived class always has an implicit dtor, + // which is a final overrider. + + // VTABLE-EXTENDED-B: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries). + // ... + // VTABLE-EXTENDED-B: 1 | extended::B::~B() [scalar deleting] + // VTABLE-EXTENDED-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + + // vtordisp{4294967292,0} + // MANGLING-DAG: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z" +}; + +B b; + +struct C : virtual simple::A { + // VTABLE-EXTENDED-C: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries). + // VTABLE-EXTENDED-C-NEXT: 0 | void simple::A::f() + // 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] + + // `vtordispex{12,8,4294967292,8}' + // MANGLING-DAG: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ" + int x; + virtual ~C(); + // MANGLING-DAG: @"\01??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z" +}; + +C c; + +struct D : virtual V2 { + virtual void f(); + virtual ~D(); + int x; +}; + +struct E : virtual D { + // VTABLE-EXTENDED-E: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries). + // VTABLE-EXTENDED-E-NEXT: 0 | void extended::D::f() + // 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] + + // `vtordispex{8,8,4294967292,12}' + // MANGLING-DAG: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ" + + virtual ~E(); + // MANGLING-DAG: @"\01??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z" +}; + +E e; + +struct F : virtual Z, virtual D { + // VTABLE-EXTENDED-F: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries). + // VTABLE-EXTENDED-F-NEXT: 0 | void extended::D::f() + // 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] + + // `vtordispex{20,12,4294967292,12}' + // MANGLING-DAG: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ" + int x; + virtual ~F(); + // MANGLING-DAG: @"\01??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z" +}; + +F f; + +struct G : virtual simple::A { + // VTABLE-EXTENDED-G: VFTable for 'extended::G' (1 entries). + // VTABLE-EXTENDED-G-NEXT: 0 | void extended::G::g() + + // VTABLE-EXTENDED-G: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries). + // VTABLE-EXTENDED-G-NEXT: 0 | void simple::A::f() + // VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left, + // VTABLE-EXTENDED-G-NEXT: vboffset at 8 in the vbtable, 8 non-virtual] + // VTABLE-EXTENDED-G-NEXT: 1 | extended::G::~G() [scalar deleting] + // VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] + + // Emits a G's own vfptr, thus moving the vbptr in the layout. + virtual void g(); + + virtual ~G(); + // vtordisp{4294967292,0} + // MANGLING-DAG: @"\01??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z" +}; + +G g; + +struct H : Z, A { + // VTABLE-EXTENDED-H: VFTable for 'Z' in 'extended::H' (2 entries). + // VTABLE-EXTENDED-H-NEXT: 0 | void Z::g() + // VTABLE-EXTENDED-H-NEXT: 1 | extended::H::~H() [scalar deleting] + + // VTABLE-EXTENDED-H: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries). + // VTABLE-EXTENDED-H-NEXT: 0 | void simple::A::f() + // 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] + + // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ" + // MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z" +}; + +H h; +} + +namespace pr17738 { +// These classes should have vtordispex thunks but MSVS CL miscompiles them. +// Just do the right thing. + +struct A : virtual simple::B { + // VTABLE-PR17738-A: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries). + // VTABLE-PR17738-A-NEXT: 0 | void simple::B::f() + // 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] + + // MANGLING-DAG: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ" + int a; + virtual ~A(); +}; + +A a; +} + +namespace access { +struct A { + virtual ~A(); +protected: + virtual void prot(); +private: + virtual void priv(); +}; + +struct B : virtual A { + virtual ~B(); +protected: + virtual void prot(); + // MANGLING-DAG: @"\01?prot@B@access@@$2PPPPPPPM@A@AEXXZ" +private: + virtual void priv(); + // MANGLING-DAG: @"\01?priv@B@access@@$0PPPPPPPM@A@AEXXZ" +}; + +B b; + +struct C : virtual B { + virtual ~C(); + + // MANGLING-DAG: @"\01?prot@B@access@@$R277PPPPPPPM@7AEXXZ" + // MANGLING-DAG: @"\01?priv@B@access@@$R077PPPPPPPM@7AEXXZ" +}; + +C c; +} -- 2.40.0