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,186 @@ +//===----- 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. + GlobalDecl BaseMethod; + + /// Holds a pointer to the overridee(!) 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; + + /// The \c this pointer adjustment. + ThisAdjustment This; + + /// The return adjustment. + ReturnAdjustment Return; + + ThunkInfo() = delete; + + ThunkInfo(GlobalDecl BaseMethod, const CXXMethodDecl *Method, + const ThisAdjustment &This, const ReturnAdjustment &Return) + : BaseMethod(BaseMethod), Method(Method), 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,8 @@ 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); + Thunk.Method->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(GlobalDecl GD, 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, Overrider.Method, 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,7 @@ return Adjustment; } -void ItaniumVTableBuilder::AddMethod(const CXXMethodDecl *MD, +void ItaniumVTableBuilder::AddMethod(GlobalDecl GD, const CXXMethodDecl *MD, ReturnAdjustment ReturnAdjustment) { if (const CXXDestructorDecl *DD = dyn_cast(MD)) { assert(ReturnAdjustment.isEmpty() && @@ -1311,8 +1329,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(GD, MD, ThisAdjustment(), ReturnAdjustment)}); + } // Add the function. Components.push_back(VTableComponent::MakeFunction(MD)); @@ -1513,6 +1536,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 +1569,8 @@ // This is a virtual thunk for the most derived class, add it. AddThunk(Overrider.Method, - ThunkInfo(ThisAdjustment, ReturnAdjustment)); + ThunkInfo(GD, Overrider.Method, ThisAdjustment, + ReturnAdjustment)); } } @@ -1605,10 +1633,17 @@ ComputeReturnAdjustmentBaseOffset(Context, OverriderMD, MD); } + GlobalDecl BaseGD; + if (isa(MD)) + BaseGD = + GlobalDecl(cast(MD), CXXDtorType::Dtor_Deleting); + else + BaseGD = MD; + ReturnAdjustment ReturnAdjustment = ComputeReturnAdjustment(ReturnAdjustmentOffset); - AddMethod(Overrider.Method, ReturnAdjustment); + AddMethod(BaseGD, Overrider.Method, ReturnAdjustment); } } @@ -1958,8 +1993,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 +2042,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 +2157,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); }); @@ -2515,12 +2551,13 @@ /// components vector. void AddMethod(const CXXMethodDecl *MD, ThunkInfo TI) { if (!TI.isEmpty()) { - VTableThunks[Components.size()] = TI; + assert(VTableThunks.find(Components.size()) == VTableThunks.end() && + "ZZZ"); + VTableThunks.insert({Components.size(), TI}); AddThunk(MD, TI); } if (const CXXDestructorDecl *DD = dyn_cast(MD)) { - assert(TI.Return.isEmpty() && - "Destructor can't have return adjustment!"); + assert(TI.Return.isEmpty() && "Destructor can't have return adjustment!"); Components.push_back(VTableComponent::MakeDeletingDtor(DD)); } else { Components.push_back(VTableComponent::MakeFunction(MD)); @@ -3091,9 +3128,16 @@ } } + GlobalDecl BaseGD; + if (isa(MD)) + BaseGD = + GlobalDecl(cast(MD), CXXDtorType::Dtor_Deleting); + else + BaseGD = MD; + AddMethod(FinalOverriderMD, - ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment, - ForceReturnAdjustmentMangling ? MD : nullptr)); + ThunkInfo(BaseGD, FinalOverriderMD, ThisAdjustmentOffset, + ReturnAdjustment)); } } @@ -3111,7 +3155,7 @@ 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 '" @@ -3179,9 +3223,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 +3241,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 @@ -153,19 +153,16 @@ // no-op thunk for the regular definition) call va_start/va_end. // There's a bit of per-call overhead for this solution, but it's // better for codesize if the definition is long. -llvm::Function * -CodeGenFunction::GenerateVarArgsThunk(llvm::Function *Fn, - const CGFunctionInfo &FnInfo, - GlobalDecl GD, const ThunkInfo &Thunk) { +llvm::Function *CodeGenFunction::GenerateVarArgsThunk( + llvm::Function *Fn, const CGFunctionInfo &FnInfo, GlobalDecl GD, + llvm::FunctionCallee Callee, const ThunkInfo &Thunk) { const CXXMethodDecl *MD = cast(GD.getDecl()); const FunctionProtoType *FPT = MD->getType()->castAs(); QualType ResultType = FPT->getReturnType(); // Get the original function assert(FnInfo.isVariadic()); - llvm::Type *Ty = CGM.getTypes().GetFunctionType(FnInfo); - llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true); - llvm::Function *BaseFn = cast(Callee); + llvm::Function *BaseFn = cast(Callee.getCallee()); // Cloning can't work if we don't have a definition. The Microsoft ABI may // require thunks when a definition is not available. Emit an error in these @@ -289,9 +286,9 @@ FinishFunction(); } -void CodeGenFunction::EmitCallAndReturnForThunk(llvm::FunctionCallee Callee, - const ThunkInfo *Thunk, - bool IsUnprototyped) { +void CodeGenFunction::EmitCallAndReturnForThunk( + llvm::FunctionCallee Callee, const CGFunctionInfo &CalleeFnInfo, + const ThunkInfo *Thunk, bool IsUnprototyped) { assert(isa(CurGD.getDecl()) && "Please use a new CGF for this thunk"); const CXXMethodDecl *MD = cast(CurGD.getDecl()); @@ -370,7 +367,7 @@ // Now emit our call. llvm::CallBase *CallOrInvoke; - RValue RV = EmitCall(*CurFnInfo, CGCallee::forDirect(Callee, CurGD), Slot, + RValue RV = EmitCall(CalleeFnInfo, CGCallee::forDirect(Callee, CurGD), Slot, CallArgs, &CallOrInvoke); // Consider return adjustment if we have ThunkInfo. @@ -443,31 +440,16 @@ FinishThunk(); } -void CodeGenFunction::generateThunk(llvm::Function *Fn, - const CGFunctionInfo &FnInfo, GlobalDecl GD, +void CodeGenFunction::generateThunk(llvm::Function *ThunkFn, + const CGFunctionInfo &ThunkFnInfo, + GlobalDecl CD, llvm::FunctionCallee Callee, + const CGFunctionInfo &CalleeFnInfo, const ThunkInfo &Thunk, bool IsUnprototyped) { - StartThunk(Fn, GD, FnInfo, IsUnprototyped); - // Create a scope with an artificial location for the body of this function. - auto AL = ApplyDebugLocation::CreateArtificial(*this); - - // Get our callee. Use a placeholder type if this method is unprototyped so - // that CodeGenModule doesn't try to set attributes. - llvm::Type *Ty; - if (IsUnprototyped) - Ty = llvm::StructType::get(getLLVMContext()); - else - Ty = CGM.getTypes().GetFunctionType(FnInfo); - - llvm::Constant *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true); - - // Fix up the function type for an unprototyped musttail call. - if (IsUnprototyped) - Callee = llvm::ConstantExpr::getBitCast(Callee, Fn->getType()); + StartThunk(ThunkFn, CD, ThunkFnInfo, IsUnprototyped); // Make the call and return the result. - EmitCallAndReturnForThunk(llvm::FunctionCallee(Fn->getFunctionType(), Callee), - &Thunk, IsUnprototyped); + EmitCallAndReturnForThunk(Callee, CalleeFnInfo, &Thunk, IsUnprototyped); } static bool shouldEmitVTableThunk(CodeGenModule &CGM, const CXXMethodDecl *MD, @@ -500,11 +482,13 @@ MangleContext &MCtx = CGM.getCXXABI().getMangleContext(); llvm::raw_svector_ostream Out(Name); if (const CXXDestructorDecl *DD = dyn_cast(MD)) - MCtx.mangleCXXDtorThunk(DD, GD.getDtorType(), TI.This, Out); + MCtx.mangleCXXDtorThunk(DD, TI.BaseMethod.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); + llvm::Type *ThunkVTableTy = + CGM.getTypes().GetFunctionTypeForVTable(TI.BaseMethod); + llvm::Constant *Thunk = + CGM.GetAddrOfThunk(Name, ThunkVTableTy, TI.BaseMethod); // If we don't need to emit a definition, return this declaration as is. bool IsUnprototyped = !CGM.getTypes().isFuncTypeConvertible( @@ -512,12 +496,18 @@ 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 = + const CGFunctionInfo &ThunkFnInfo = + IsUnprototyped ? CGM.getTypes().arrangeUnprototypedMustTailThunk( + cast(TI.BaseMethod.getDecl())) + : CGM.getTypes().arrangeGlobalDeclaration(TI.BaseMethod); + llvm::FunctionType *ThunkFnTy = CGM.getTypes().GetFunctionType(ThunkFnInfo); + + const CGFunctionInfo &BaseFnInfo = IsUnprototyped ? CGM.getTypes().arrangeUnprototypedMustTailThunk(MD) : CGM.getTypes().arrangeGlobalDeclaration(GD); - llvm::FunctionType *ThunkFnTy = CGM.getTypes().GetFunctionType(FnInfo); + llvm::FunctionType *BaseFnTy = CGM.getTypes().GetFunctionType(BaseFnInfo); // If the type of the underlying GlobalValue is wrong, we'll have to replace // it. It should be a declaration. @@ -531,7 +521,7 @@ OldThunkFn->setName(StringRef()); ThunkFn = llvm::Function::Create(ThunkFnTy, llvm::Function::ExternalLinkage, Name.str(), &CGM.getModule()); - CGM.SetLLVMFunctionAttributes(MD, FnInfo, ThunkFn); + CGM.SetLLVMFunctionAttributes(TI.BaseMethod, ThunkFnInfo, ThunkFn); // If needed, replace the old thunk with a bitcast. if (!OldThunkFn->use_empty()) { @@ -553,7 +543,7 @@ return ThunkFn; } - setThunkProperties(CGM, TI, ThunkFn, ForVTable, GD); + setThunkProperties(CGM, TI, ThunkFn, ForVTable, TI.BaseMethod); return ThunkFn; } @@ -564,7 +554,7 @@ if (IsUnprototyped) ThunkFn->addFnAttr("thunk"); - CGM.SetLLVMFunctionAttributesForDefinition(GD.getDecl(), ThunkFn); + CGM.SetLLVMFunctionAttributesForDefinition(TI.BaseMethod.getDecl(), ThunkFn); // Thunks for variadic methods are special because in general variadic // arguments cannot be perfectly forwarded. In the general case, clang @@ -587,17 +577,19 @@ } } + llvm::FunctionCallee Callee(BaseFnTy, CGM.GetAddrOfFunction(GD, BaseFnTy)); if (ShouldCloneVarArgs) { if (UseAvailableExternallyLinkage) return ThunkFn; - ThunkFn = - CodeGenFunction(CGM).GenerateVarArgsThunk(ThunkFn, FnInfo, GD, TI); + ThunkFn = CodeGenFunction(CGM).GenerateVarArgsThunk(ThunkFn, ThunkFnInfo, + GD, Callee, TI); } else { // Normal thunk body generation. - CodeGenFunction(CGM).generateThunk(ThunkFn, FnInfo, GD, TI, IsUnprototyped); + CodeGenFunction(CGM).generateThunk(ThunkFn, ThunkFnInfo, GD, Callee, + BaseFnInfo, TI, IsUnprototyped); } - setThunkProperties(CGM, TI, ThunkFn, ForVTable, GD); + setThunkProperties(CGM, TI, ThunkFn, ForVTable, TI.BaseMethod); return ThunkFn; } @@ -727,22 +719,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 @@ -2164,6 +2164,7 @@ const CGFunctionInfo &FnInfo, bool IsUnprototyped); void EmitCallAndReturnForThunk(llvm::FunctionCallee Callee, + const CGFunctionInfo &CalleeFnInfo, const ThunkInfo *Thunk, bool IsUnprototyped); void FinishThunk(); @@ -2173,13 +2174,16 @@ llvm::FunctionCallee Callee); /// Generate a thunk for the given method. - void generateThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo, - GlobalDecl GD, const ThunkInfo &Thunk, - bool IsUnprototyped); + void generateThunk(llvm::Function *ThunkFn, const CGFunctionInfo &ThunkFnInfo, + GlobalDecl CD, llvm::FunctionCallee Callee, + const CGFunctionInfo &CalleeFnInfo, const ThunkInfo &Thunk, + bool IsUnprototype); llvm::Function *GenerateVarArgsThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo, - GlobalDecl GD, const ThunkInfo &Thunk); + GlobalDecl GD, + llvm::FunctionCallee Callee, + const ThunkInfo &Thunk); void EmitCtorPrologue(const CXXConstructorDecl *CD, CXXCtorType Type, FunctionArgList &Args); 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)