diff --git a/clang/include/clang/AST/VTableBuilder.h b/clang/include/clang/AST/VTableBuilder.h --- a/clang/include/clang/AST/VTableBuilder.h +++ b/clang/include/clang/AST/VTableBuilder.h @@ -18,6 +18,7 @@ #include "clang/AST/GlobalDecl.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/ABI.h" +#include "clang/Basic/Thunk.h" #include "llvm/ADT/DenseMap.h" #include #include @@ -253,7 +254,7 @@ OwningArrayRef VTableComponents; /// Contains thunks needed by vtables, sorted by indices. - OwningArrayRef VTableThunks; + std::vector VTableThunks; /// Address points for all vtables. AddressPointsMapTy AddressPoints; diff --git a/clang/include/clang/Basic/ABI.h b/clang/include/clang/Basic/ABI.h --- a/clang/include/clang/Basic/ABI.h +++ b/clang/include/clang/Basic/ABI.h @@ -37,174 +37,6 @@ Dtor_Comdat ///< The COMDAT used for dtors }; -/// A return adjustment. -struct ReturnAdjustment { - /// The non-virtual adjustment from the derived object to its - /// nearest virtual base. - int64_t NonVirtual; - - /// Holds the ABI-specific information about the virtual return - /// adjustment, if needed. - union VirtualAdjustment { - // Itanium ABI - struct { - /// The offset (in bytes), relative to the address point - /// of the virtual base class offset. - int64_t VBaseOffsetOffset; - } Itanium; - - // Microsoft ABI - struct { - /// The offset (in bytes) of the vbptr, relative to the beginning - /// of the derived class. - uint32_t VBPtrOffset; - - /// 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; - - ReturnAdjustment() : NonVirtual(0) {} - - bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); } - - friend bool operator==(const ReturnAdjustment &LHS, - const ReturnAdjustment &RHS) { - return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Equals(RHS.Virtual); - } - - friend bool operator!=(const ReturnAdjustment &LHS, const ReturnAdjustment &RHS) { - return !(LHS == RHS); - } - - friend bool operator<(const ReturnAdjustment &LHS, - const ReturnAdjustment &RHS) { - if (LHS.NonVirtual < RHS.NonVirtual) - return true; - - return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Less(RHS.Virtual); - } -}; - -/// A \c this pointer adjustment. -struct ThisAdjustment { - /// The non-virtual adjustment from the derived object to its - /// nearest virtual base. - int64_t NonVirtual; - - /// Holds the ABI-specific information about the virtual this - /// adjustment, if needed. - union VirtualAdjustment { - // Itanium ABI - struct { - /// The offset (in bytes), relative to the address point, - /// of the virtual call offset. - int64_t VCallOffsetOffset; - } Itanium; - - struct { - /// The offset of the vtordisp (in bytes), relative to the ECX. - int32_t VtordispOffset; - - /// The offset of the vbptr of the derived class (in bytes), - /// relative to the ECX after vtordisp adjustment. - int32_t VBPtrOffset; - - /// 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) { } - - bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); } - - friend bool operator==(const ThisAdjustment &LHS, - const ThisAdjustment &RHS) { - return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Equals(RHS.Virtual); - } - - friend bool operator!=(const ThisAdjustment &LHS, const ThisAdjustment &RHS) { - return !(LHS == RHS); - } - - friend bool operator<(const ThisAdjustment &LHS, - const ThisAdjustment &RHS) { - if (LHS.NonVirtual < RHS.NonVirtual) - return true; - - return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Less(RHS.Virtual); - } -}; - -class CXXMethodDecl; - -/// The \c this pointer adjustment as well as an optional return -/// adjustment for a thunk. -struct ThunkInfo { - /// The \c this pointer adjustment. - ThisAdjustment This; - - /// The return adjustment. - ReturnAdjustment Return; - - /// Holds a pointer to the overridden method this thunk is for, - /// if needed by the ABI to distinguish different thunks with equal - /// adjustments. Otherwise, null. - /// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using - /// an ABI-specific comparator. - const CXXMethodDecl *Method; - - ThunkInfo() : Method(nullptr) { } - - ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return, - const CXXMethodDecl *Method = nullptr) - : This(This), Return(Return), Method(Method) {} - - friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) { - return LHS.This == RHS.This && LHS.Return == RHS.Return && - LHS.Method == RHS.Method; - } - - bool isEmpty() const { - return This.isEmpty() && Return.isEmpty() && Method == nullptr; - } -}; - } // end namespace clang #endif diff --git a/clang/include/clang/Basic/Thunk.h b/clang/include/clang/Basic/Thunk.h new file mode 100644 --- /dev/null +++ b/clang/include/clang/Basic/Thunk.h @@ -0,0 +1,181 @@ +//===----- Thunk.h - Declarations related to VTable Thunks ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Enums/classes describing THUNK related information about constructors, +/// destructors and thunks. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_THUNK_H +#define LLVM_CLANG_BASIC_THUNK_H + +#include "clang/AST/GlobalDecl.h" + +namespace clang { + +/// A return adjustment. +struct ReturnAdjustment { + /// The non-virtual adjustment from the derived object to its + /// nearest virtual base. + int64_t NonVirtual; + + /// Holds the ABI-specific information about the virtual return + /// adjustment, if needed. + union VirtualAdjustment { + // Itanium ABI + struct { + /// The offset (in bytes), relative to the address point + /// of the virtual base class offset. + int64_t VBaseOffsetOffset; + } Itanium; + + // Microsoft ABI + struct { + /// The offset (in bytes) of the vbptr, relative to the beginning + /// of the derived class. + uint32_t VBPtrOffset; + + /// 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; + + ReturnAdjustment() : NonVirtual(0) {} + + bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); } + + friend bool operator==(const ReturnAdjustment &LHS, + const ReturnAdjustment &RHS) { + return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Equals(RHS.Virtual); + } + + friend bool operator!=(const ReturnAdjustment &LHS, + const ReturnAdjustment &RHS) { + return !(LHS == RHS); + } + + friend bool operator<(const ReturnAdjustment &LHS, + const ReturnAdjustment &RHS) { + if (LHS.NonVirtual < RHS.NonVirtual) + return true; + + return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Less(RHS.Virtual); + } +}; + +/// A \c this pointer adjustment. +struct ThisAdjustment { + /// The non-virtual adjustment from the derived object to its + /// nearest virtual base. + int64_t NonVirtual; + + /// Holds the ABI-specific information about the virtual this + /// adjustment, if needed. + union VirtualAdjustment { + // Itanium ABI + struct { + /// The offset (in bytes), relative to the address point, + /// of the virtual call offset. + int64_t VCallOffsetOffset; + } Itanium; + + struct { + /// The offset of the vtordisp (in bytes), relative to the ECX. + int32_t VtordispOffset; + + /// The offset of the vbptr of the derived class (in bytes), + /// relative to the ECX after vtordisp adjustment. + int32_t VBPtrOffset; + + /// 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) {} + + bool isEmpty() const { return !NonVirtual && Virtual.isEmpty(); } + + friend bool operator==(const ThisAdjustment &LHS, const ThisAdjustment &RHS) { + return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Equals(RHS.Virtual); + } + + friend bool operator!=(const ThisAdjustment &LHS, const ThisAdjustment &RHS) { + return !(LHS == RHS); + } + + friend bool operator<(const ThisAdjustment &LHS, const ThisAdjustment &RHS) { + if (LHS.NonVirtual < RHS.NonVirtual) + return true; + + return LHS.NonVirtual == RHS.NonVirtual && LHS.Virtual.Less(RHS.Virtual); + } +}; + +/// The \c this pointer adjustment as well as an optional return +/// adjustment for a thunk. +struct ThunkInfo { + /// Holds a pointer to the overridden (i.e. base) method this thunk is for. + /// CAUTION: In the unlikely event you need to sort ThunkInfos, consider using + /// an ABI-specific comparator. + GlobalDecl BaseMethod; + + /// The \c this pointer adjustment. + ThisAdjustment This; + + /// The return adjustment. + ReturnAdjustment Return; + + ThunkInfo() = delete; + + ThunkInfo(GlobalDecl BaseMethod, const ThisAdjustment &This, + const ReturnAdjustment &Return) + : BaseMethod(BaseMethod), This(This), Return(Return) {} + + friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) { + return LHS.BaseMethod == RHS.BaseMethod && LHS.This == RHS.This && + LHS.Return == RHS.Return; + } + + bool isEmpty() const { return This.isEmpty() && Return.isEmpty(); } +}; + +} // end namespace clang + +#endif diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -14,7 +14,6 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/Mangle.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" @@ -23,14 +22,16 @@ #include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" -#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/Mangle.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/ABI.h" #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Thunk.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -3466,13 +3466,7 @@ AccessSpecifier AS = Thunk.Return.isEmpty() ? MD->getAccess() : AS_public; mangleThunkThisAdjustment(AS, Thunk.This, Mangler, MHO); - if (!Thunk.Return.isEmpty()) - assert(Thunk.Method != nullptr && - "Thunk info should hold the overridee decl"); - - const CXXMethodDecl *DeclForFPT = Thunk.Method ? Thunk.Method : MD; - Mangler.mangleFunctionType( - DeclForFPT->getType()->castAs(), MD); + Mangler.mangleFunctionType(MD->getType()->castAs(), MD); } void MicrosoftMangleContextImpl::mangleCXXDtorThunk( diff --git a/clang/lib/AST/VTableBuilder.cpp b/clang/lib/AST/VTableBuilder.cpp --- a/clang/lib/AST/VTableBuilder.cpp +++ b/clang/lib/AST/VTableBuilder.cpp @@ -913,7 +913,8 @@ /// AddMethod - Add a single virtual member function to the vtable /// components vector. - void AddMethod(const CXXMethodDecl *MD, ReturnAdjustment ReturnAdjustment); + void AddMethod(const CXXMethodDecl *BaseMethod, const CXXMethodDecl *MD, + ReturnAdjustment ReturnAdjustment); /// IsOverriderUsed - Returns whether the overrider will ever be used in this /// part of the vtable. @@ -1115,10 +1116,12 @@ // Ignore adjustments for unused function pointers. uint64_t VTableIndex = MethodInfo.VTableIndex; - if (Components[VTableIndex].getKind() == - VTableComponent::CK_UnusedFunctionPointer) + const VTableComponent &component = Components[VTableIndex]; + if (component.getKind() == VTableComponent::CK_UnusedFunctionPointer) continue; + GlobalDecl GD = component.getGlobalDecl(); + // Get the final overrider for this method. FinalOverriders::OverriderInfo Overrider = Overriders.getOverrider(MD, MethodInfo.BaseOffset); @@ -1130,7 +1133,8 @@ // While the thunk itself might be needed by vtables in subclasses or // in construction vtables, there doesn't seem to be a reason for using // the thunk in this vtable. Still, we do so to match gcc. - if (VTableThunks.lookup(VTableIndex).Return.isEmpty()) + auto it = VTableThunks.find(VTableIndex); + if (it == VTableThunks.end() || it->second.Return.isEmpty()) continue; } @@ -1140,12 +1144,26 @@ if (ThisAdjustment.isEmpty()) continue; - // Add it. - VTableThunks[VTableIndex].This = ThisAdjustment; + auto ApplyThisAdjustment = [&](unsigned Idx) { + Optional TI; + + auto it = VTableThunks.find(Idx); + if (it != VTableThunks.end()) { + TI = it->second; + TI->This = ThisAdjustment; + } else + TI = {GD, ThisAdjustment, ReturnAdjustment()}; + + // Override it. Note that there may or may not be a thunk already. + VTableThunks.erase(Idx); + VTableThunks.insert({Idx, *TI}); + }; + + ApplyThisAdjustment(VTableIndex); if (isa(MD)) { // Add an adjustment for the deleting destructor as well. - VTableThunks[VTableIndex + 1].This = ThisAdjustment; + ApplyThisAdjustment(VTableIndex + 1); } } @@ -1300,7 +1318,8 @@ return Adjustment; } -void ItaniumVTableBuilder::AddMethod(const CXXMethodDecl *MD, +void ItaniumVTableBuilder::AddMethod(const CXXMethodDecl *BaseMethod, + const CXXMethodDecl *MD, ReturnAdjustment ReturnAdjustment) { if (const CXXDestructorDecl *DD = dyn_cast(MD)) { assert(ReturnAdjustment.isEmpty() && @@ -1311,8 +1330,13 @@ Components.push_back(VTableComponent::MakeDeletingDtor(DD)); } else { // Add the return adjustment if necessary. - if (!ReturnAdjustment.isEmpty()) - VTableThunks[Components.size()].Return = ReturnAdjustment; + if (!ReturnAdjustment.isEmpty()) { + assert(VTableThunks.find(Components.size()) == VTableThunks.end() && + "ZZZ"); + VTableThunks.insert( + {Components.size(), + ThunkInfo(BaseMethod, ThisAdjustment(), ReturnAdjustment)}); + } // Add the function. Components.push_back(VTableComponent::MakeFunction(MD)); @@ -1513,6 +1537,10 @@ MethodInfo MethodInfo(Base.getBaseOffset(), BaseOffsetInLayoutClass, OverriddenMethodInfo.VTableIndex); + uint64_t VTableIndex = MethodInfo.VTableIndex; + const VTableComponent &component = Components[VTableIndex]; + GlobalDecl GD = component.getGlobalDecl(); + assert(!MethodInfoMap.count(MD) && "Should not have method info for this method yet!"); @@ -1542,7 +1570,7 @@ // This is a virtual thunk for the most derived class, add it. AddThunk(Overrider.Method, - ThunkInfo(ThisAdjustment, ReturnAdjustment)); + ThunkInfo(GD, ThisAdjustment, ReturnAdjustment)); } } @@ -1608,7 +1636,7 @@ ReturnAdjustment ReturnAdjustment = ComputeReturnAdjustment(ReturnAdjustmentOffset); - AddMethod(Overrider.Method, ReturnAdjustment); + AddMethod(MD, Overrider.Method, ReturnAdjustment); } } @@ -1958,8 +1986,9 @@ if (MD->isDeleted()) Out << " [deleted]"; - ThunkInfo Thunk = VTableThunks.lookup(I); - if (!Thunk.isEmpty()) { + auto it = VTableThunks.find(I); + if (it != VTableThunks.end()) { + ThunkInfo Thunk = it->second; // If this function pointer has a return adjustment, dump it. if (!Thunk.Return.isEmpty()) { Out << "\n [return adjustment: "; @@ -2006,8 +2035,9 @@ if (DD->isPure()) Out << " [pure]"; - ThunkInfo Thunk = VTableThunks.lookup(I); - if (!Thunk.isEmpty()) { + auto it = VTableThunks.find(I); + if (it != VTableThunks.end()) { + ThunkInfo Thunk = it->second; // If this destructor has a 'this' pointer adjustment, dump it. if (!Thunk.This.isEmpty()) { Out << "\n [this adjustment: "; @@ -2120,7 +2150,6 @@ ThunkInfoVectorTy ThunksVector = Thunks[MD]; llvm::sort(ThunksVector, [](const ThunkInfo &LHS, const ThunkInfo &RHS) { - assert(LHS.Method == nullptr && RHS.Method == nullptr); return std::tie(LHS.This, LHS.Return) < std::tie(RHS.This, RHS.Return); }); @@ -2513,18 +2542,29 @@ /// 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); - } + void AddMethod(const CXXMethodDecl *BaseMD, const CXXMethodDecl *MD, + ThisAdjustment This, ReturnAdjustment Return) { if (const CXXDestructorDecl *DD = dyn_cast(MD)) { - assert(TI.Return.isEmpty() && - "Destructor can't have return adjustment!"); + assert(Return.isEmpty() && "Destructor can't have return adjustment!"); Components.push_back(VTableComponent::MakeDeletingDtor(DD)); } else { Components.push_back(VTableComponent::MakeFunction(MD)); } + + GlobalDecl GD; + if (isa(BaseMD)) + GD = GlobalDecl(cast(BaseMD), + CXXDtorType::Dtor_Deleting); + else + GD = BaseMD; + + ThunkInfo TI(GD, This, Return); + if (!TI.isEmpty()) { + assert(VTableThunks.find(Components.size() - 1) == VTableThunks.end() && + "ZZZ"); + VTableThunks.insert({Components.size() - 1, TI}); + AddThunk(MD, TI); + } } /// AddMethods - Add the methods of this base subobject and the relevant @@ -3091,9 +3131,7 @@ } } - AddMethod(FinalOverriderMD, - ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment, - ForceReturnAdjustmentMangling ? MD : nullptr)); + AddMethod(MD, FinalOverriderMD, ThisAdjustmentOffset, ReturnAdjustment); } } @@ -3111,11 +3149,14 @@ const ReturnAdjustment &R = TI.Return; bool Multiline = false; const char *LinePrefix = "\n "; - if (!R.isEmpty() || TI.Method) { + if (!R.isEmpty()) { if (!ContinueFirstLine) Out << LinePrefix; Out << "[return adjustment (to type '" - << TI.Method->getReturnType().getCanonicalType().getAsString() + << cast(TI.BaseMethod.getDecl()) + ->getReturnType() + .getCanonicalType() + .getAsString() << "'): "; if (R.Virtual.Microsoft.VBPtrOffset) Out << "vbptr at offset " << R.Virtual.Microsoft.VBPtrOffset << ", "; @@ -3179,9 +3220,11 @@ if (MD->isDeleted()) Out << " [deleted]"; - ThunkInfo Thunk = VTableThunks.lookup(I); - if (!Thunk.isEmpty()) + auto it = VTableThunks.find(I); + if (it != VTableThunks.end()) { + ThunkInfo Thunk = it->second; dumpMicrosoftThunkAdjustment(Thunk, Out, /*ContinueFirstLine=*/false); + } break; } @@ -3195,8 +3238,9 @@ if (DD->isPure()) Out << " [pure]"; - ThunkInfo Thunk = VTableThunks.lookup(I); - if (!Thunk.isEmpty()) { + auto it = VTableThunks.find(I); + if (it != VTableThunks.end()) { + ThunkInfo Thunk = it->second; assert(Thunk.Return.isEmpty() && "No return adjustment needed for destructors!"); dumpMicrosoftThunkAdjustment(Thunk, Out, /*ContinueFirstLine=*/false); diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -289,7 +289,9 @@ FinishFunction(); } -void CodeGenFunction::EmitCallAndReturnForThunk(llvm::FunctionCallee Callee, +void CodeGenFunction::EmitCallAndReturnForThunk(const CGFunctionInfo &CallInfo, + llvm::FunctionCallee Callee, + GlobalDecl CalleeGD, const ThunkInfo *Thunk, bool IsUnprototyped) { assert(isa(CurGD.getDecl()) && @@ -370,7 +372,7 @@ // Now emit our call. llvm::CallBase *CallOrInvoke; - RValue RV = EmitCall(*CurFnInfo, CGCallee::forDirect(Callee, CurGD), Slot, + RValue RV = EmitCall(CallInfo, CGCallee::forDirect(Callee, CalleeGD), Slot, CallArgs, &CallOrInvoke); // Consider return adjustment if we have ThunkInfo. @@ -443,14 +445,17 @@ FinishThunk(); } -void CodeGenFunction::generateThunk(llvm::Function *Fn, - const CGFunctionInfo &FnInfo, GlobalDecl GD, - const ThunkInfo &Thunk, +void CodeGenFunction::generateThunk(llvm::Function *ThunkFn, + const CGFunctionInfo &ThunkFnInfo, + GlobalDecl GD, const ThunkInfo &Thunk, bool IsUnprototyped) { - StartThunk(Fn, GD, FnInfo, IsUnprototyped); + StartThunk(ThunkFn, Thunk.BaseMethod, ThunkFnInfo, IsUnprototyped); // Create a scope with an artificial location for the body of this function. auto AL = ApplyDebugLocation::CreateArtificial(*this); + const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeGlobalDeclaration(GD); + llvm::FunctionType *FnTy = CGM.getTypes().GetFunctionType(FnInfo); + // Get our callee. Use a placeholder type if this method is unprototyped so // that CodeGenModule doesn't try to set attributes. llvm::Type *Ty; @@ -463,10 +468,10 @@ // Fix up the function type for an unprototyped musttail call. if (IsUnprototyped) - Callee = llvm::ConstantExpr::getBitCast(Callee, Fn->getType()); + Callee = llvm::ConstantExpr::getBitCast(Callee, FnTy); // Make the call and return the result. - EmitCallAndReturnForThunk(llvm::FunctionCallee(Fn->getFunctionType(), Callee), + EmitCallAndReturnForThunk(FnInfo, llvm::FunctionCallee(FnTy, Callee), GD, &Thunk, IsUnprototyped); } @@ -503,21 +508,25 @@ MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), TI.This, Out); else MCtx.mangleThunk(MD, TI, Out); + llvm::Type *ThunkVTableTy = CGM.getTypes().GetFunctionTypeForVTable(GD); llvm::Constant *Thunk = CGM.GetAddrOfThunk(Name, ThunkVTableTy, GD); // If we don't need to emit a definition, return this declaration as is. bool IsUnprototyped = !CGM.getTypes().isFuncTypeConvertible( - MD->getType()->castAs()); + cast(TI.BaseMethod.getDecl()) + ->getType() + ->castAs()); if (!shouldEmitVTableThunk(CGM, MD, IsUnprototyped, ForVTable)) return Thunk; - // Arrange a function prototype appropriate for a function definition. In some + // Arrange a thunk function prototype appropriate for a function def. In some // cases in the MS ABI, we may need to build an unprototyped musttail thunk. - const CGFunctionInfo &FnInfo = - IsUnprototyped ? CGM.getTypes().arrangeUnprototypedMustTailThunk(MD) - : CGM.getTypes().arrangeGlobalDeclaration(GD); - llvm::FunctionType *ThunkFnTy = CGM.getTypes().GetFunctionType(FnInfo); + const CGFunctionInfo &ThunkFnInfo = + IsUnprototyped ? CGM.getTypes().arrangeUnprototypedMustTailThunk( + cast(TI.BaseMethod.getDecl())) + : CGM.getTypes().arrangeGlobalDeclaration(TI.BaseMethod); + llvm::FunctionType *ThunkFnTy = CGM.getTypes().GetFunctionType(ThunkFnInfo); // If the type of the underlying GlobalValue is wrong, we'll have to replace // it. It should be a declaration. @@ -525,13 +534,14 @@ if (ThunkFn->getFunctionType() != ThunkFnTy) { llvm::GlobalValue *OldThunkFn = ThunkFn; - assert(OldThunkFn->isDeclaration() && "Shouldn't replace non-declaration"); + // assert(OldThunkFn->isDeclaration() && "Shouldn't replace + // non-declaration"); // Remove the name from the old thunk function and get a new thunk. OldThunkFn->setName(StringRef()); ThunkFn = llvm::Function::Create(ThunkFnTy, llvm::Function::ExternalLinkage, Name.str(), &CGM.getModule()); - CGM.SetLLVMFunctionAttributes(MD, FnInfo, ThunkFn); + CGM.SetLLVMFunctionAttributes(GD, ThunkFnInfo, ThunkFn); // If needed, replace the old thunk with a bitcast. if (!OldThunkFn->use_empty()) { @@ -564,7 +574,7 @@ if (IsUnprototyped) ThunkFn->addFnAttr("thunk"); - CGM.SetLLVMFunctionAttributesForDefinition(GD.getDecl(), ThunkFn); + CGM.SetLLVMFunctionAttributesForDefinition(MD, ThunkFn); // Thunks for variadic methods are special because in general variadic // arguments cannot be perfectly forwarded. In the general case, clang @@ -591,10 +601,11 @@ if (UseAvailableExternallyLinkage) return ThunkFn; ThunkFn = - CodeGenFunction(CGM).GenerateVarArgsThunk(ThunkFn, FnInfo, GD, TI); + CodeGenFunction(CGM).GenerateVarArgsThunk(ThunkFn, ThunkFnInfo, GD, TI); } else { // Normal thunk body generation. - CodeGenFunction(CGM).generateThunk(ThunkFn, FnInfo, GD, TI, IsUnprototyped); + CodeGenFunction(CGM).generateThunk(ThunkFn, ThunkFnInfo, GD, TI, + IsUnprototyped); } setThunkProperties(CGM, TI, ThunkFn, ForVTable, GD); @@ -727,22 +738,7 @@ case VTableComponent::CK_FunctionPointer: case VTableComponent::CK_CompleteDtorPointer: case VTableComponent::CK_DeletingDtorPointer: { - GlobalDecl GD; - - // Get the right global decl. - switch (component.getKind()) { - default: - llvm_unreachable("Unexpected vtable component kind"); - case VTableComponent::CK_FunctionPointer: - GD = component.getFunctionDecl(); - break; - case VTableComponent::CK_CompleteDtorPointer: - GD = GlobalDecl(component.getDestructorDecl(), Dtor_Complete); - break; - case VTableComponent::CK_DeletingDtorPointer: - GD = GlobalDecl(component.getDestructorDecl(), Dtor_Deleting); - break; - } + GlobalDecl GD = component.getGlobalDecl(); if (CGM.getLangOpts().CUDA) { // Emit NULL for methods we can't codegen on this diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2163,8 +2163,10 @@ void StartThunk(llvm::Function *Fn, GlobalDecl GD, const CGFunctionInfo &FnInfo, bool IsUnprototyped); - void EmitCallAndReturnForThunk(llvm::FunctionCallee Callee, - const ThunkInfo *Thunk, bool IsUnprototyped); + void EmitCallAndReturnForThunk(const CGFunctionInfo &CallInfo, + llvm::FunctionCallee Callee, + GlobalDecl CalleeGD, const ThunkInfo *Thunk, + bool IsUnprototyped); void FinishThunk(); @@ -2173,7 +2175,7 @@ llvm::FunctionCallee Callee); /// Generate a thunk for the given method. - void generateThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo, + void generateThunk(llvm::Function *ThunkFn, const CGFunctionInfo &ThunkFnInfo, GlobalDecl GD, const ThunkInfo &Thunk, bool IsUnprototyped); diff --git a/clang/test/CodeGenCXX/thunk-linkonce-odr.cpp b/clang/test/CodeGenCXX/thunk-linkonce-odr.cpp --- a/clang/test/CodeGenCXX/thunk-linkonce-odr.cpp +++ b/clang/test/CodeGenCXX/thunk-linkonce-odr.cpp @@ -29,5 +29,5 @@ // Thunks should be marked as "linkonce ODR" not "weak". // -// CHECK: define linkonce_odr i32 @_ZThn{{[48]}}_N1D1fEv // CHECK: define linkonce_odr i32 @_ZThn{{[48]}}_N1C1fEv +// CHECK: define linkonce_odr i32 @_ZThn{{[48]}}_N1D1fEv diff --git a/clang/test/CodeGenCXX/thunk-returning-memptr.cpp b/clang/test/CodeGenCXX/thunk-returning-memptr.cpp --- a/clang/test/CodeGenCXX/thunk-returning-memptr.cpp +++ b/clang/test/CodeGenCXX/thunk-returning-memptr.cpp @@ -23,5 +23,5 @@ // Because of the tail call, the return value cannot be copied into a local // alloca. (PR39901) -// CHECK-LABEL: define linkonce_odr void @_ZThn4_N1C1fEv({ i32, i32 }* noalias sret({ i32, i32 }) align 4 %agg.result, %struct.C* {{[^,]*}} %this) +// CHECK-LABEL: define linkonce_odr void @_ZThn4_N1C1fEv({ i32, i32 }* noalias sret({ i32, i32 }) align 4 %agg.result, %struct.B* {{[^,]*}} %this.coerce) // CHECK: tail call void @_ZN1C1fEv({ i32, i32 }* sret({ i32, i32 }) align 4 %agg.result diff --git a/clang/test/CodeGenCXX/thunk-wrong-return-type.cpp b/clang/test/CodeGenCXX/thunk-wrong-return-type.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/thunk-wrong-return-type.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple %itanium_abi_triple %s -emit-llvm -o - %s | FileCheck %s + +struct A {}; +struct alignas(32) B : virtual A { + char c[32]; +}; +struct Pad { + char c[7]; +}; +struct C : B, Pad, virtual A {}; + +struct X { + virtual A &f(); +}; +struct U { + virtual ~U(); +}; +C c; +struct Y : U, X { + virtual B &f() override { return c; } +}; + +Y y; + +// CHECK: define linkonce_odr nonnull align 1 dereferenceable(1) %struct.A.8* @_ZTchn8_v0_n24_N1Y1fEv(%struct.X.7* nonnull dereferenceable(8) %this.coerce) #1 comdat align 2 { diff --git a/clang/test/CodeGenCXX/thunk-wrong-this.cpp b/clang/test/CodeGenCXX/thunk-wrong-this.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/thunk-wrong-this.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -emit-llvm-only -triple %itanium_abi_triple %s -emit-llvm -o - %s | FileCheck %s + +class Base1 { + virtual void Foo1(); +}; + +class Base2 { + virtual void Foo2(); +}; + +class alignas(16) Obj : public Base1, public Base2 { + void Foo1() override; + void Foo2() override; + ~Obj(); +}; + +void Obj::Foo1() {} +void Obj::Foo2() {} + +// CHECK: define dso_local void @_ZThn8_N3Obj4Foo2Ev(%class.Base2.2* nonnull dereferenceable(8) %this.coerce) #1 align 2 { +// CHECK: tail call void @_ZN3Obj4Foo2Ev(%class.Obj.0* nonnull dereferenceable(16) %2)