From c70cc5d90403f99ccce5cab3a6c022ad9cdcb66c Mon Sep 17 00:00:00 2001 From: Timur Iskhodzhanov Date: Wed, 30 Oct 2013 11:55:43 +0000 Subject: [PATCH] Make thunk this/return adjustment ABI-specific. Also, fix the return adjustment when using -cxx-abi microsoft Reviewed at http://llvm-reviews.chandlerc.com/D2026 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@193679 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/ABI.h | 54 +++++++--- lib/AST/ItaniumMangle.cpp | 4 +- lib/AST/VTableBuilder.cpp | 98 +++++++++---------- lib/CodeGen/CGCXXABI.h | 8 ++ lib/CodeGen/CGVTables.cpp | 73 ++------------ lib/CodeGen/ItaniumCXXABI.cpp | 68 +++++++++++++ lib/CodeGen/MicrosoftCXXABI.cpp | 64 ++++++++++++ test/CodeGenCXX/microsoft-abi-thunks.cpp | 37 +++++-- ...tables-multiple-nonvirtual-inheritance.cpp | 20 ++-- ...rosoft-abi-vtables-virtual-inheritance.cpp | 35 +++++-- 10 files changed, 305 insertions(+), 156 deletions(-) diff --git a/include/clang/Basic/ABI.h b/include/clang/Basic/ABI.h index 7c76ec130e..9f214f3d34 100644 --- a/include/clang/Basic/ABI.h +++ b/include/clang/Basic/ABI.h @@ -39,19 +39,52 @@ struct ReturnAdjustment { /// \brief The non-virtual adjustment from the derived object to its /// nearest virtual base. int64_t NonVirtual; + + /// \brief Holds the ABI-specific information about the virtual return + /// adjustment, if needed. + union VirtualAdjustment { + // Itanium ABI + struct { + /// \brief The offset (in bytes), relative to the address point + /// of the virtual base class offset. + int64_t VBaseOffsetOffset; + } Itanium; + + // Microsoft ABI + struct { + /// \brief The offset (in bytes) of the vbptr, relative to the beginning + /// of the derived class. + uint32_t VBPtrOffset; + + /// \brief Index of the virtual base in the vbtable. + uint32_t VBIndex; + } 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; - /// \brief The offset (in bytes), relative to the address point - /// of the virtual base class offset. - int64_t VBaseOffsetOffset; - - ReturnAdjustment() : NonVirtual(0), VBaseOffsetOffset(0) { } + ReturnAdjustment() : NonVirtual(0) {} - bool isEmpty() const { return !NonVirtual && !VBaseOffsetOffset; } + bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); } friend bool operator==(const ReturnAdjustment &LHS, const ReturnAdjustment &RHS) { - return LHS.NonVirtual == RHS.NonVirtual && - LHS.VBaseOffsetOffset == RHS.VBaseOffsetOffset; + return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Equals(RHS.Virtual); } friend bool operator!=(const ReturnAdjustment &LHS, const ReturnAdjustment &RHS) { @@ -62,9 +95,8 @@ struct ReturnAdjustment { const ReturnAdjustment &RHS) { if (LHS.NonVirtual < RHS.NonVirtual) return true; - - return LHS.NonVirtual == RHS.NonVirtual && - LHS.VBaseOffsetOffset < RHS.VBaseOffsetOffset; + + return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Less(RHS.Virtual); } }; diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 59fa7212de..21a6c107bb 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -3657,8 +3657,8 @@ void ItaniumMangleContextImpl::mangleThunk(const CXXMethodDecl *MD, // Mangle the return pointer adjustment if there is one. if (!Thunk.Return.isEmpty()) Mangler.mangleCallOffset(Thunk.Return.NonVirtual, - Thunk.Return.VBaseOffsetOffset); - + Thunk.Return.Virtual.Itanium.VBaseOffsetOffset); + Mangler.mangleFunctionEncoding(MD); } diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp index 3031955452..c2b33e6558 100644 --- a/lib/AST/VTableBuilder.cpp +++ b/lib/AST/VTableBuilder.cpp @@ -1203,10 +1203,10 @@ ItaniumVTableBuilder::ComputeReturnAdjustment(BaseOffset Offset) { // Get the virtual base offset offset. if (Offset.DerivedClass == MostDerivedClass) { // We can get the offset offset directly from our map. - Adjustment.VBaseOffsetOffset = + Adjustment.Virtual.Itanium.VBaseOffsetOffset = VBaseOffsetOffsets.lookup(Offset.VirtualBase).getQuantity(); } else { - Adjustment.VBaseOffsetOffset = + Adjustment.Virtual.Itanium.VBaseOffsetOffset = VTables.getVirtualBaseOffsetOffset(Offset.DerivedClass, Offset.VirtualBase).getQuantity(); } @@ -1304,7 +1304,7 @@ ThisAdjustment ItaniumVTableBuilder::ComputeThisAdjustment( VCallOffsets = Builder.getVCallOffsets(); } - Adjustment.VCallOffsetOffset = + Adjustment.VCallOffsetOffset = VCallOffsets.getVCallOffsetOffset(MD).getQuantity(); } @@ -1996,8 +1996,8 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { Out << "\n [return adjustment: "; Out << Thunk.Return.NonVirtual << " non-virtual"; - if (Thunk.Return.VBaseOffsetOffset) { - Out << ", " << Thunk.Return.VBaseOffsetOffset; + if (Thunk.Return.Virtual.Itanium.VBaseOffsetOffset) { + Out << ", " << Thunk.Return.Virtual.Itanium.VBaseOffsetOffset; Out << " vbase offset offset"; } @@ -2172,8 +2172,8 @@ void ItaniumVTableBuilder::dumpLayout(raw_ostream &Out) { if (!Thunk.Return.isEmpty()) { Out << "return adjustment: " << Thunk.Return.NonVirtual; Out << " non-virtual"; - if (Thunk.Return.VBaseOffsetOffset) { - Out << ", " << Thunk.Return.VBaseOffsetOffset; + if (Thunk.Return.Virtual.Itanium.VBaseOffsetOffset) { + Out << ", " << Thunk.Return.Virtual.Itanium.VBaseOffsetOffset; Out << " vbase offset offset"; } @@ -2911,9 +2911,11 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth, ReturnAdjustment.NonVirtual = ReturnAdjustmentOffset.NonVirtualOffset.getQuantity(); if (ReturnAdjustmentOffset.VirtualBase) { - // FIXME: We might want to create a VBIndex alias for VBaseOffsetOffset - // in the ReturnAdjustment struct. - ReturnAdjustment.VBaseOffsetOffset = + const ASTRecordLayout &DerivedLayout = + Context.getASTRecordLayout(ReturnAdjustmentOffset.DerivedClass); + ReturnAdjustment.Virtual.Microsoft.VBPtrOffset = + DerivedLayout.getVBPtrOffset().getQuantity(); + ReturnAdjustment.Virtual.Microsoft.VBIndex = GetVBTableIndex(ReturnAdjustmentOffset.DerivedClass, ReturnAdjustmentOffset.VirtualBase); } @@ -2945,6 +2947,34 @@ struct MicrosoftThunkInfoStableSortComparator { } }; +static void dumpMicrosoftThunkAdjustment(const ThunkInfo &TI, raw_ostream &Out, + bool ContinueFirstLine) { + const ReturnAdjustment &R = TI.Return; + bool Multiline = false; + const char *LinePrefix = "\n "; + if (!R.isEmpty()) { + if (!ContinueFirstLine) + Out << LinePrefix; + Out << "[return adjustment: "; + if (R.Virtual.Microsoft.VBPtrOffset) + Out << "vbptr at offset " << R.Virtual.Microsoft.VBPtrOffset << ", "; + if (R.Virtual.Microsoft.VBIndex) + Out << "vbase #" << R.Virtual.Microsoft.VBIndex << ", "; + Out << R.NonVirtual << " non-virtual]"; + Multiline = true; + } + + const ThisAdjustment &T = TI.This; + if (!T.isEmpty()) { + if (Multiline || !ContinueFirstLine) + Out << LinePrefix; + Out << "[this adjustment: "; + assert(TI.This.VCallOffsetOffset == 0 && + "VtorDisp adjustment is not supported yet"); + Out << T.NonVirtual << " non-virtual]"; + } +} + void VFTableBuilder::dumpLayout(raw_ostream &Out) { Out << "VFTable for "; PrintBasePath(WhichVFPtr.PathToBaseWithVFPtr, Out); @@ -2977,23 +3007,8 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) { } ThunkInfo Thunk = VTableThunks.lookup(I); - if (!Thunk.isEmpty()) { - // If this function pointer has a return adjustment, dump it. - if (!Thunk.Return.isEmpty()) { - Out << "\n [return adjustment: "; - if (Thunk.Return.VBaseOffsetOffset) - Out << "vbase #" << Thunk.Return.VBaseOffsetOffset << ", "; - Out << Thunk.Return.NonVirtual << " non-virtual]"; - } - - // If this function pointer has a 'this' pointer adjustment, dump it. - if (!Thunk.This.isEmpty()) { - assert(!Thunk.This.VCallOffsetOffset && - "No virtual this adjustment in this ABI"); - Out << "\n [this adjustment: " << Thunk.This.NonVirtual - << " non-virtual]"; - } - } + if (!Thunk.isEmpty()) + dumpMicrosoftThunkAdjustment(Thunk, Out, /*ContinueFirstLine=*/false); break; } @@ -3011,13 +3026,7 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) { if (!Thunk.isEmpty()) { assert(Thunk.Return.isEmpty() && "No return adjustment needed for destructors!"); - // If this destructor has a 'this' pointer adjustment, dump it. - if (!Thunk.This.isEmpty()) { - assert(!Thunk.This.VCallOffsetOffset && - "No virtual this adjustment in this ABI"); - Out << "\n [this adjustment: " << Thunk.This.NonVirtual - << " non-virtual]"; - } + dumpMicrosoftThunkAdjustment(Thunk, Out, /*ContinueFirstLine=*/false); } break; @@ -3068,26 +3077,7 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) { const ThunkInfo &Thunk = ThunksVector[I]; Out << llvm::format("%4d | ", I); - - // If this function pointer has a return pointer adjustment, dump it. - if (!Thunk.Return.isEmpty()) { - Out << "return adjustment: "; - if (Thunk.Return.VBaseOffsetOffset) - Out << "vbase #" << Thunk.Return.VBaseOffsetOffset << ", "; - Out << Thunk.Return.NonVirtual << " non-virtual"; - - if (!Thunk.This.isEmpty()) - Out << "\n "; - } - - // If this function pointer has a 'this' pointer adjustment, dump it. - if (!Thunk.This.isEmpty()) { - assert(!Thunk.This.VCallOffsetOffset && - "No virtual this adjustment in this ABI"); - Out << "this adjustment: "; - Out << Thunk.This.NonVirtual << " non-virtual"; - } - + dumpMicrosoftThunkAdjustment(Thunk, Out, /*ContinueFirstLine=*/true); Out << '\n'; } diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index c76769f425..44bdf66f60 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -356,6 +356,14 @@ public: virtual void setThunkLinkage(llvm::Function *Thunk, bool ForVTable) = 0; + virtual llvm::Value *performThisAdjustment(CodeGenFunction &CGF, + llvm::Value *This, + const ThisAdjustment &TA) = 0; + + virtual llvm::Value *performReturnAdjustment(CodeGenFunction &CGF, + llvm::Value *Ret, + const ReturnAdjustment &RA) = 0; + virtual void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResultType); diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index 425c12301f..5c639a3b3d 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -56,53 +56,6 @@ llvm::Constant *CodeGenModule::GetAddrOfThunk(GlobalDecl GD, return GetOrCreateLLVMFunction(Name, Ty, GD, /*ForVTable=*/true); } -static llvm::Value *PerformTypeAdjustment(CodeGenFunction &CGF, - llvm::Value *Ptr, - int64_t NonVirtualAdjustment, - int64_t VirtualAdjustment, - bool IsReturnAdjustment) { - if (!NonVirtualAdjustment && !VirtualAdjustment) - return Ptr; - - llvm::Type *Int8PtrTy = CGF.Int8PtrTy; - llvm::Value *V = CGF.Builder.CreateBitCast(Ptr, Int8PtrTy); - - if (NonVirtualAdjustment && !IsReturnAdjustment) { - // Perform the non-virtual adjustment for a base-to-derived cast. - V = CGF.Builder.CreateConstInBoundsGEP1_64(V, NonVirtualAdjustment); - } - - if (VirtualAdjustment) { - llvm::Type *PtrDiffTy = - CGF.ConvertType(CGF.getContext().getPointerDiffType()); - - // Perform the virtual adjustment. - llvm::Value *VTablePtrPtr = - CGF.Builder.CreateBitCast(V, Int8PtrTy->getPointerTo()); - - llvm::Value *VTablePtr = CGF.Builder.CreateLoad(VTablePtrPtr); - - llvm::Value *OffsetPtr = - CGF.Builder.CreateConstInBoundsGEP1_64(VTablePtr, VirtualAdjustment); - - OffsetPtr = CGF.Builder.CreateBitCast(OffsetPtr, PtrDiffTy->getPointerTo()); - - // Load the adjustment offset from the vtable. - llvm::Value *Offset = CGF.Builder.CreateLoad(OffsetPtr); - - // Adjust our pointer. - V = CGF.Builder.CreateInBoundsGEP(V, Offset); - } - - if (NonVirtualAdjustment && IsReturnAdjustment) { - // Perform the non-virtual adjustment for a derived-to-base cast. - V = CGF.Builder.CreateConstInBoundsGEP1_64(V, NonVirtualAdjustment); - } - - // Cast back to the original type. - return CGF.Builder.CreateBitCast(V, Ptr->getType()); -} - static void setThunkVisibility(CodeGenModule &CGM, const CXXMethodDecl *MD, const ThunkInfo &Thunk, llvm::Function *Fn) { CGM.setGlobalVisibility(Fn, MD); @@ -181,12 +134,10 @@ static RValue PerformReturnAdjustment(CodeGenFunction &CGF, CGF.Builder.CreateCondBr(IsNull, AdjustNull, AdjustNotNull); CGF.EmitBlock(AdjustNotNull); } - - ReturnValue = PerformTypeAdjustment(CGF, ReturnValue, - Thunk.Return.NonVirtual, - Thunk.Return.VBaseOffsetOffset, - /*IsReturnAdjustment*/true); - + + ReturnValue = CGF.CGM.getCXXABI().performReturnAdjustment(CGF, ReturnValue, + Thunk.Return); + if (NullCheckValue) { CGF.Builder.CreateBr(AdjustEnd); CGF.EmitBlock(AdjustNull); @@ -266,11 +217,8 @@ void CodeGenFunction::GenerateVarArgsThunk( assert(ThisStore && "Store of this should be in entry block?"); // Adjust "this", if necessary. Builder.SetInsertPoint(ThisStore); - llvm::Value *AdjustedThisPtr = - PerformTypeAdjustment(*this, ThisPtr, - Thunk.This.NonVirtual, - Thunk.This.VCallOffsetOffset, - /*IsReturnAdjustment*/false); + llvm::Value *AdjustedThisPtr = + CGM.getCXXABI().performThisAdjustment(*this, ThisPtr, Thunk.This); ThisStore->setOperand(0, AdjustedThisPtr); if (!Thunk.Return.isEmpty()) { @@ -322,12 +270,9 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn, CXXThisValue = CXXABIThisValue; // Adjust the 'this' pointer if necessary. - llvm::Value *AdjustedThisPtr = - PerformTypeAdjustment(*this, LoadCXXThis(), - Thunk.This.NonVirtual, - Thunk.This.VCallOffsetOffset, - /*IsReturnAdjustment*/false); - + llvm::Value *AdjustedThisPtr = + CGM.getCXXABI().performThisAdjustment(*this, LoadCXXThis(), Thunk.This); + CallArgList CallArgs; // Add our adjusted 'this' pointer. diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 6ffc39e264..03fea46736 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -180,6 +180,12 @@ public: Thunk->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage); } + llvm::Value *performThisAdjustment(CodeGenFunction &CGF, llvm::Value *This, + const ThisAdjustment &TA); + + llvm::Value *performReturnAdjustment(CodeGenFunction &CGF, llvm::Value *Ret, + const ReturnAdjustment &RA); + StringRef GetPureVirtualCallName() { return "__cxa_pure_virtual"; } StringRef GetDeletedVirtualCallName() { return "__cxa_deleted_virtual"; } @@ -1059,6 +1065,68 @@ void ItaniumCXXABI::emitVirtualInheritanceTables(const CXXRecordDecl *RD) { VTables.EmitVTTDefinition(VTT, CGM.getVTableLinkage(RD), RD); } +static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, + llvm::Value *Ptr, + int64_t NonVirtualAdjustment, + int64_t VirtualAdjustment, + bool IsReturnAdjustment) { + if (!NonVirtualAdjustment && !VirtualAdjustment) + return Ptr; + + llvm::Type *Int8PtrTy = CGF.Int8PtrTy; + llvm::Value *V = CGF.Builder.CreateBitCast(Ptr, Int8PtrTy); + + if (NonVirtualAdjustment && !IsReturnAdjustment) { + // Perform the non-virtual adjustment for a base-to-derived cast. + V = CGF.Builder.CreateConstInBoundsGEP1_64(V, NonVirtualAdjustment); + } + + if (VirtualAdjustment) { + llvm::Type *PtrDiffTy = + CGF.ConvertType(CGF.getContext().getPointerDiffType()); + + // Perform the virtual adjustment. + llvm::Value *VTablePtrPtr = + CGF.Builder.CreateBitCast(V, Int8PtrTy->getPointerTo()); + + llvm::Value *VTablePtr = CGF.Builder.CreateLoad(VTablePtrPtr); + + llvm::Value *OffsetPtr = + CGF.Builder.CreateConstInBoundsGEP1_64(VTablePtr, VirtualAdjustment); + + OffsetPtr = CGF.Builder.CreateBitCast(OffsetPtr, PtrDiffTy->getPointerTo()); + + // Load the adjustment offset from the vtable. + llvm::Value *Offset = CGF.Builder.CreateLoad(OffsetPtr); + + // Adjust our pointer. + V = CGF.Builder.CreateInBoundsGEP(V, Offset); + } + + if (NonVirtualAdjustment && IsReturnAdjustment) { + // Perform the non-virtual adjustment for a derived-to-base cast. + V = CGF.Builder.CreateConstInBoundsGEP1_64(V, NonVirtualAdjustment); + } + + // Cast back to the original type. + return CGF.Builder.CreateBitCast(V, Ptr->getType()); +} + +llvm::Value *ItaniumCXXABI::performThisAdjustment(CodeGenFunction &CGF, + llvm::Value *This, + const ThisAdjustment &TA) { + return performTypeAdjustment(CGF, This, TA.NonVirtual, TA.VCallOffsetOffset, + /*IsReturnAdjustment=*/false); +} + +llvm::Value * +ItaniumCXXABI::performReturnAdjustment(CodeGenFunction &CGF, llvm::Value *Ret, + const ReturnAdjustment &RA) { + return performTypeAdjustment(CGF, Ret, RA.NonVirtual, + RA.Virtual.Itanium.VBaseOffsetOffset, + /*IsReturnAdjustment=*/true); +} + void ARMCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResultType) { if (!isa(CGF.CurGD.getDecl())) diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 02f080996c..de13116b87 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -190,6 +190,12 @@ public: Thunk->setLinkage(llvm::GlobalValue::WeakAnyLinkage); } + llvm::Value *performThisAdjustment(CodeGenFunction &CGF, llvm::Value *This, + const ThisAdjustment &TA); + + llvm::Value *performReturnAdjustment(CodeGenFunction &CGF, llvm::Value *Ret, + const ReturnAdjustment &RA); + void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, llvm::GlobalVariable *DeclPtr, bool PerformInit); @@ -268,6 +274,16 @@ private: llvm::Value *VBTableOffset, llvm::Value **VBPtr = 0); + llvm::Value *GetVBaseOffsetFromVBPtr(CodeGenFunction &CGF, + llvm::Value *Base, + int32_t VBPtrOffset, + int32_t VBTableOffset, + llvm::Value **VBPtr = 0) { + llvm::Value *VBPOffset = llvm::ConstantInt::get(CGM.IntTy, VBPtrOffset), + *VBTOffset = llvm::ConstantInt::get(CGM.IntTy, VBTableOffset); + return GetVBaseOffsetFromVBPtr(CGF, Base, VBPOffset, VBTOffset, VBPtr); + } + /// \brief Performs a full virtual base adjustment. Used to dereference /// pointers to members of virtual bases. llvm::Value *AdjustVirtualBase(CodeGenFunction &CGF, const CXXRecordDecl *RD, @@ -962,6 +978,54 @@ void MicrosoftCXXABI::emitVirtualInheritanceTables(const CXXRecordDecl *RD) { } } +llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF, + llvm::Value *This, + const ThisAdjustment &TA) { + if (TA.isEmpty()) + return This; + + llvm::Value *V = CGF.Builder.CreateBitCast(This, CGF.Int8PtrTy); + + assert(TA.VCallOffsetOffset == 0 && + "VtorDisp adjustment is not supported yet"); + + if (TA.NonVirtual) { + // Non-virtual adjustment might result in a pointer outside the allocated + // object, e.g. if the final overrider class is laid out after the virtual + // base that declares a method in the most derived class. + V = CGF.Builder.CreateConstGEP1_32(V, TA.NonVirtual); + } + + // Don't need to bitcast back, the call CodeGen will handle this. + return V; +} + +llvm::Value * +MicrosoftCXXABI::performReturnAdjustment(CodeGenFunction &CGF, llvm::Value *Ret, + const ReturnAdjustment &RA) { + if (RA.isEmpty()) + return Ret; + + llvm::Value *V = CGF.Builder.CreateBitCast(Ret, CGF.Int8PtrTy); + + if (RA.Virtual.Microsoft.VBIndex) { + assert(RA.Virtual.Microsoft.VBIndex > 0); + int32_t IntSize = + getContext().getTypeSizeInChars(getContext().IntTy).getQuantity(); + llvm::Value *VBPtr; + llvm::Value *VBaseOffset = + GetVBaseOffsetFromVBPtr(CGF, V, RA.Virtual.Microsoft.VBPtrOffset, + IntSize * RA.Virtual.Microsoft.VBIndex, &VBPtr); + V = CGF.Builder.CreateInBoundsGEP(VBPtr, VBaseOffset); + } + + if (RA.NonVirtual) + V = CGF.Builder.CreateConstInBoundsGEP1_32(V, RA.NonVirtual); + + // Cast back to the original type. + return CGF.Builder.CreateBitCast(V, Ret->getType()); +} + bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr, QualType elementType) { // Microsoft seems to completely ignore the possibility of a diff --git a/test/CodeGenCXX/microsoft-abi-thunks.cpp b/test/CodeGenCXX/microsoft-abi-thunks.cpp index 3b4281f76d..6b8270b1c6 100644 --- a/test/CodeGenCXX/microsoft-abi-thunks.cpp +++ b/test/CodeGenCXX/microsoft-abi-thunks.cpp @@ -61,14 +61,14 @@ struct C : A, B { C::C() {} // Emits vftable and forces thunk generation. -// CODEGEN: define weak x86_thiscallcc void @"\01??_EC@@W3AEPAXI@Z"(%struct.C* %this, i32 %should_call_delete) -// CODEGEN: getelementptr inbounds i8* {{.*}}, i64 -4 +// CODEGEN-LABEL: define weak x86_thiscallcc void @"\01??_EC@@W3AEPAXI@Z"(%struct.C* %this, i32 %should_call_delete) +// CODEGEN: getelementptr i8* {{.*}}, i32 -4 // FIXME: should actually call _EC, not _GC. // CODEGEN: call x86_thiscallcc void @"\01??_GC@@UAEPAXI@Z" // CODEGEN: ret -// CODEGEN: define weak x86_thiscallcc void @"\01?public_f@C@@W3AEXXZ"(%struct.C* -// CODEGEN: getelementptr inbounds i8* {{.*}}, i64 -4 +// CODEGEN-LABEL: define weak x86_thiscallcc void @"\01?public_f@C@@W3AEXXZ"(%struct.C* +// CODEGEN: getelementptr i8* {{.*}}, i32 -4 // CODEGEN: call x86_thiscallcc void @"\01?public_f@C@@UAEXXZ"(%struct.C* // CODEGEN: ret @@ -81,6 +81,7 @@ struct D { }; struct E : D { + E(); virtual C* goo(); // MANGLING-DAG: @"\01?goo@E@@UAEPAUC@@XZ" // MANGLING-DAG: @"\01?goo@E@@QAEPAUB@@XZ" @@ -88,14 +89,15 @@ struct E : D { // MANGLING-X64-DAG: @"\01?goo@E@@QEAAPEAUB@@XZ" }; -E e; // Emits vftable and forces thunk generation. +E::E() {} // Emits vftable and forces thunk generation. -// CODEGEN: define weak x86_thiscallcc %struct.C* @"\01?goo@E@@QAEPAUB@@XZ" +// CODEGEN-LABEL: define weak x86_thiscallcc %struct.C* @"\01?goo@E@@QAEPAUB@@XZ" // CODEGEN: call x86_thiscallcc %struct.C* @"\01?goo@E@@UAEPAUC@@XZ" -// CODEGEN: getelementptr inbounds i8* {{.*}}, i64 4 +// CODEGEN: getelementptr inbounds i8* {{.*}}, i32 4 // CODEGEN: ret struct F : virtual A, virtual B { + virtual void own_method(); virtual ~F(); }; @@ -115,6 +117,27 @@ struct H : E { H h; +struct I : D { + I(); + virtual F* goo(); +}; + +I::I() {} // Emits vftable and forces thunk generation. + +// CODEGEN-LABEL: define weak x86_thiscallcc %struct.{{[BF]}}* @"\01?goo@I@@QAEPAUB@@XZ" +// CODEGEN: %[[ORIG_RET:.*]] = call x86_thiscallcc %struct.F* @"\01?goo@I@@UAEPAUF@@XZ" +// CODEGEN: %[[ORIG_RET_i8:.*]] = bitcast %struct.F* %[[ORIG_RET]] to i8* +// CODEGEN: %[[VBPTR_i8:.*]] = getelementptr inbounds i8* %[[ORIG_RET_i8]], i32 4 +// CODEGEN: %[[VBPTR:.*]] = bitcast i8* %[[VBPTR_i8]] to i8** +// CODEGEN: %[[VBTABLE:.*]] = load i8** %[[VBPTR]] +// CODEGEN: %[[VBASE_OFFSET_PTR_i8:.*]] = getelementptr inbounds i8* %[[VBTABLE]], i32 8 +// CODEGEN: %[[VBASE_OFFSET_PTR:.*]] = bitcast i8* %[[VBASE_OFFSET_PTR_i8]] to i32* +// CODEGEN: %[[VBASE_OFFSET:.*]] = load i32* %[[VBASE_OFFSET_PTR]] +// CODEGEN: %[[RES_i8:.*]] = getelementptr inbounds i8* %[[VBPTR_i8]], i32 %[[VBASE_OFFSET]] +// CODEGEN: %[[RES:.*]] = bitcast i8* %[[RES_i8]] to %struct.F* +// CODEGEN: phi %struct.F* {{.*}} %[[RES]] +// CODEGEN: ret %struct.{{[BF]}}* + // FIXME: Write vtordisp adjusting thunk tests namespace CrashOnThunksForAttributedType { diff --git a/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp index 11f5654e02..c3f1ab08b3 100644 --- a/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp +++ b/test/CodeGenCXX/microsoft-abi-vtables-multiple-nonvirtual-inheritance.cpp @@ -275,7 +275,7 @@ struct Test1 : B, C { // THIS-THUNKS-Test1-NEXT: [this adjustment: -4 non-virtual] // THIS-THUNKS-Test1: Thunks for 'void this_adjustment::Test1::g()' (1 entry). - // THIS-THUNKS-Test1-NEXT: 0 | this adjustment: -4 non-virtual + // THIS-THUNKS-Test1-NEXT: 0 | [this adjustment: -4 non-virtual] // THIS-THUNKS-Test1: VFTable indices for 'this_adjustment::Test1' (1 entries). // THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g() @@ -301,7 +301,7 @@ struct Test2 : A, B, C { // THIS-THUNKS-Test2-NEXT: [this adjustment: -4 non-virtual] // THIS-THUNKS-Test2: Thunks for 'void this_adjustment::Test2::g()' (1 entry). - // THIS-THUNKS-Test2-NEXT: 0 | this adjustment: -4 non-virtual + // THIS-THUNKS-Test2-NEXT: 0 | [this adjustment: -4 non-virtual] // THIS-THUNKS-Test2: VFTable indices for 'this_adjustment::Test2' (1 entries). // THIS-THUNKS-Test2-NEXT: via vfptr at offset 4 @@ -327,18 +327,18 @@ struct Test3: no_thunks::Test1, no_thunks::Test2 { // THIS-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test2' in 'this_adjustment::Test3' (1 entries). // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f() - // THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual] + // THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual] // THIS-THUNKS-Test3: Thunks for 'void this_adjustment::Test3::f()' (1 entry). - // THIS-THUNKS-Test3-NEXT: 0 | this adjustment: -8 non-virtual + // THIS-THUNKS-Test3-NEXT: 0 | [this adjustment: -8 non-virtual] // THIS-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test2' in 'this_adjustment::Test3' (2 entries). // THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g() - // THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual] + // THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual] // THIS-THUNKS-Test3-NEXT: 1 | void B::h() // THIS-THUNKS-Test3: Thunks for 'void this_adjustment::Test3::g()' (1 entry). - // THIS-THUNKS-Test3-NEXT: 0 | this adjustment: -8 non-virtual + // THIS-THUNKS-Test3-NEXT: 0 | [this adjustment: -8 non-virtual] // THIS-THUNKS-Test3: VFTable indices for 'this_adjustment::Test3' (2 entries). // THIS-THUNKS-Test3-NEXT: via vfptr at offset 0 @@ -373,7 +373,7 @@ struct Test3 : Test1, Test2 { // VDTOR-THUNKS-Test3-NEXT: [this adjustment: -4 non-virtual] // VDTOR-THUNKS-Test3: Thunks for 'vdtor::Test3::~Test3()' (1 entry). - // VDTOR-THUNKS-Test3-NEXT: 0 | this adjustment: -4 non-virtual + // VDTOR-THUNKS-Test3-NEXT: 0 | [this adjustment: -4 non-virtual] // VDTOR-THUNKS-Test3: VFTable indices for 'vdtor::Test3' (1 entries). // VDTOR-THUNKS-Test3-NEXT: 0 | vdtor::Test3::~Test3() [scalar deleting] @@ -398,7 +398,7 @@ struct Test5 : Test4, Test2 { // VDTOR-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual] // VDTOR-THUNKS-Test5: Thunks for 'vdtor::Test5::~Test5()' (1 entry). - // VDTOR-THUNKS-Test5-NEXT: 0 | this adjustment: -4 non-virtual + // VDTOR-THUNKS-Test5-NEXT: 0 | [this adjustment: -4 non-virtual] // VDTOR-THUNKS-Test5: VFTable indices for 'vdtor::Test5' (1 entries). // VDTOR-THUNKS-Test5-NEXT: -- accessible via vfptr at offset 4 -- @@ -418,7 +418,7 @@ struct Test6 : Test4, Test2 { // VDTOR-THUNKS-Test6-NEXT: [this adjustment: -4 non-virtual] // VDTOR-THUNKS-Test6: Thunks for 'vdtor::Test6::~Test6()' (1 entry). - // VDTOR-THUNKS-Test6-NEXT: 0 | this adjustment: -4 non-virtual + // VDTOR-THUNKS-Test6-NEXT: 0 | [this adjustment: -4 non-virtual] // VDTOR-THUNKS-Test6: VFTable indices for 'vdtor::Test6' (1 entries). // VDTOR-THUNKS-Test6-NEXT: -- accessible via vfptr at offset 4 -- @@ -436,7 +436,7 @@ struct Test7 : Test5 { // VDTOR-THUNKS-Test7-NEXT: [this adjustment: -4 non-virtual] // VDTOR-THUNKS-Test7: Thunks for 'vdtor::Test7::~Test7()' (1 entry). - // VDTOR-THUNKS-Test7-NEXT: 0 | this adjustment: -4 non-virtual + // VDTOR-THUNKS-Test7-NEXT: 0 | [this adjustment: -4 non-virtual] // VDTOR-THUNKS-Test7: VFTable indices for 'vdtor::Test7' (1 entries). // VDTOR-THUNKS-Test7-NEXT: -- accessible via vfptr at offset 4 -- diff --git a/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp index cfbdad941a..781e4adbc5 100644 --- a/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp +++ b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance.cpp @@ -22,6 +22,7 @@ // RUN: FileCheck --check-prefix=VDTORS-P %s < %t // RUN: FileCheck --check-prefix=RET-W %s < %t // RUN: FileCheck --check-prefix=RET-T %s < %t +// RUN: FileCheck --check-prefix=RET-V %s < %t // RUN: FileCheck --check-prefix=MANGLING %s < %t @@ -152,7 +153,7 @@ struct X: virtual C { // TEST4: VFTable for 'A' in 'C' in 'Test4::X' (2 entries). // TEST4-NEXT: 0 | void C::f() - // TEST4-NEXT: [this adjustment: 8 non-virtual] + // TEST4-NEXT: [this adjustment: 8 non-virtual] // TEST4-NEXT: 1 | void A::z() // TEST4-NOT: VFTable indices for 'Test4::X' @@ -223,7 +224,7 @@ struct Y : virtual X { // TEST7-NEXT: 1 | void A::z() // TEST7: Thunks for 'void C::f()' (1 entry). - // TEST7-NEXT: 0 | this adjustment: 8 non-virtual + // TEST7-NEXT: 0 | [this adjustment: 8 non-virtual] // TEST7-NOT: VFTable indices for 'Test7::Y' @@ -338,7 +339,7 @@ struct W : Z, D, virtual A, virtual B { // TEST9-W-NEXT: 1 | void A::z() // TEST9-W: Thunks for 'void D::f()' (1 entry). - // TEST9-W-NEXT: 0 | this adjustment: -8 non-virtual + // TEST9-W-NEXT: 0 | [this adjustment: -8 non-virtual] // TEST9-W-NOT: VFTable indices for 'Test9::W' @@ -371,7 +372,7 @@ struct T : Z, D, virtual A, virtual B { // TEST9-T-NEXT: [this adjustment: -8 non-virtual] // TEST9-T: Thunks for 'void Test9::T::h()' (1 entry). - // TEST9-T-NEXT: 0 | this adjustment: -8 non-virtual + // TEST9-T-NEXT: 0 | [this adjustment: -8 non-virtual] // TEST9-T: VFTable for 'A' in 'D' in 'Test9::T' (2 entries). // TEST9-T-NEXT: 0 | void Test9::T::f() @@ -380,10 +381,10 @@ struct T : Z, D, virtual A, virtual B { // TEST9-T-NEXT: [this adjustment: -8 non-virtual] // TEST9-T: Thunks for 'void Test9::T::f()' (1 entry). - // TEST9-T-NEXT: 0 | this adjustment: -8 non-virtual + // TEST9-T-NEXT: 0 | [this adjustment: -8 non-virtual] // TEST9-T: Thunks for 'void Test9::T::z()' (1 entry). - // TEST9-T-NEXT: 0 | this adjustment: -8 non-virtual + // TEST9-T-NEXT: 0 | [this adjustment: -8 non-virtual] // TEST9-T: VFTable indices for 'Test9::T' (4 entries). // TEST9-T-NEXT: via vfptr at offset 0 @@ -464,7 +465,7 @@ struct U : virtual W { // VDTORS-U-NEXT: 1 | void vdtors::X::zzz() // VDTORS-U: Thunks for 'vdtors::W::~W()' (1 entry). - // VDTORS-U-NEXT: 0 | this adjustment: -4 non-virtual + // VDTORS-U-NEXT: 0 | [this adjustment: -4 non-virtual] // VDTORS-U: VFTable indices for 'vdtors::U' (1 entries). // VDTORS-U-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 -- @@ -484,7 +485,7 @@ struct V : virtual W { // VDTORS-V-NEXT: 1 | void vdtors::X::zzz() // VDTORS-V: Thunks for 'vdtors::W::~W()' (1 entry). - // VDTORS-V-NEXT: 0 | this adjustment: -4 non-virtual + // VDTORS-V-NEXT: 0 | [this adjustment: -4 non-virtual] // VDTORS-V: VFTable indices for 'vdtors::V' (1 entries). // VDTORS-V-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 -- @@ -553,4 +554,22 @@ struct T : W { }; T t; + +struct U : virtual A { + virtual void g(); // adds a vfptr +}; + +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: 1 | return_adjustment::U *return_adjustment::V::foo() + + // RET-V: VFTable indices for 'return_adjustment::V' (1 entries). + // RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo() + + virtual U* foo(); +}; + +V v; } -- 2.40.0