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,10 @@ 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(cast(Thunk.BaseMethod.getDecl()) + ->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,33 @@ 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 { + GlobalDecl BaseMethod; + if (auto *DD = dyn_cast(MD)) + BaseMethod = GlobalDecl(DD, GD.getDtorType()); + else + BaseMethod = MD; + + TI = {BaseMethod, 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 +1325,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 +1337,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 +1544,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!"); @@ -1540,9 +1575,15 @@ ReturnAdjustment ReturnAdjustment = ComputeReturnAdjustment(ReturnAdjustmentOffset); + GlobalDecl BaseMethod; + if (auto *DD = dyn_cast(MD)) + BaseMethod = GlobalDecl(DD, GD.getDtorType()); + else + BaseMethod = MD; + // This is a virtual thunk for the most derived class, add it. AddThunk(Overrider.Method, - ThunkInfo(ThisAdjustment, ReturnAdjustment)); + ThunkInfo(BaseMethod, ThisAdjustment, ReturnAdjustment)); } } @@ -1608,7 +1649,7 @@ ReturnAdjustment ReturnAdjustment = ComputeReturnAdjustment(ReturnAdjustmentOffset); - AddMethod(Overrider.Method, ReturnAdjustment); + AddMethod(MD, Overrider.Method, ReturnAdjustment); } } @@ -1958,8 +1999,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 +2048,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 +2163,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 +2555,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 +3144,7 @@ } } - AddMethod(FinalOverriderMD, - ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment, - ForceReturnAdjustmentMangling ? MD : nullptr)); + AddMethod(MD, FinalOverriderMD, ThisAdjustmentOffset, ReturnAdjustment); } } @@ -3111,11 +3162,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 +3233,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 +3251,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 @@ -163,8 +163,12 @@ // Get the original function assert(FnInfo.isVariadic()); - llvm::Type *Ty = CGM.getTypes().GetFunctionType(FnInfo); - llvm::Value *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true); + + const CGFunctionInfo &CalleeFnInfo = + CGM.getTypes().arrangeGlobalDeclaration(GD); + llvm::FunctionType *CalleeFnTy = CGM.getTypes().GetFunctionType(CalleeFnInfo); + llvm::Constant *Callee = + CGM.GetAddrOfFunction(GD, CalleeFnTy, /*ForVTable=*/true); llvm::Function *BaseFn = cast(Callee); // Cloning can't work if we don't have a definition. The Microsoft ABI may @@ -183,7 +187,7 @@ // Ensure that the value mapper does not encounter any of them. resolveTopLevelMetadata(BaseFn, VMap); llvm::Function *NewFn = llvm::CloneFunction(BaseFn, VMap); - Fn->replaceAllUsesWith(NewFn); + Fn->replaceAllUsesWith(llvm::ConstantExpr::getBitCast(NewFn, Fn->getType())); NewFn->takeName(Fn); Fn->eraseFromParent(); Fn = NewFn; @@ -289,9 +293,9 @@ FinishFunction(); } -void CodeGenFunction::EmitCallAndReturnForThunk(llvm::FunctionCallee Callee, - const ThunkInfo *Thunk, - bool IsUnprototyped) { +void CodeGenFunction::EmitCallAndReturnForThunk( + llvm::FunctionCallee Callee, const CGFunctionInfo &CalleeFnInfo, + GlobalDecl CalleeGD, const ThunkInfo *Thunk, bool IsUnprototyped) { assert(isa(CurGD.getDecl()) && "Please use a new CGF for this thunk"); const CXXMethodDecl *MD = cast(CurGD.getDecl()); @@ -317,14 +321,15 @@ CGM.ErrorUnsupported( MD, "non-trivial argument copy for return-adjusting thunk"); } - EmitMustTailThunk(CurGD, AdjustedThisPtr, Callee); + EmitMustTailThunk(CalleeGD, AdjustedThisPtr, CalleeFnInfo, Callee); return; } // Start building CallArgs. CallArgList CallArgs; - QualType ThisType = MD->getThisType(); - CallArgs.add(RValue::get(AdjustedThisPtr), ThisType); + QualType CalleeThisType = + cast(CalleeGD.getDecl())->getThisType(); + CallArgs.add(RValue::get(AdjustedThisPtr), CalleeThisType); if (isa(MD)) CGM.getCXXABI().adjustCallArgsForDestructorThunk(*this, CurGD, CallArgs); @@ -356,11 +361,10 @@ #endif // Determine whether we have a return value slot to use. - QualType ResultType = CGM.getCXXABI().HasThisReturn(CurGD) - ? ThisType - : CGM.getCXXABI().hasMostDerivedReturn(CurGD) - ? CGM.getContext().VoidPtrTy - : FPT->getReturnType(); + QualType ResultType = CGM.getCXXABI().HasThisReturn(CurGD) ? CalleeThisType + : CGM.getCXXABI().hasMostDerivedReturn(CurGD) + ? CGM.getContext().VoidPtrTy + : FPT->getReturnType(); ReturnValueSlot Slot; if (!ResultType->isVoidType() && (CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect || @@ -370,8 +374,8 @@ // Now emit our call. llvm::CallBase *CallOrInvoke; - RValue RV = EmitCall(*CurFnInfo, CGCallee::forDirect(Callee, CurGD), Slot, - CallArgs, &CallOrInvoke); + RValue RV = EmitCall(CalleeFnInfo, CGCallee::forDirect(Callee, CalleeGD), + Slot, CallArgs, &CallOrInvoke); // Consider return adjustment if we have ThunkInfo. if (Thunk && !Thunk->Return.isEmpty()) @@ -380,8 +384,12 @@ Call->setTailCallKind(llvm::CallInst::TCK_Tail); // Emit return. - if (!ResultType->isVoidType() && Slot.isNull()) + if (!ResultType->isVoidType() && Slot.isNull()) { + if (RV.getScalarVal()) + RV = RValue::get( + Builder.CreateBitCast(RV.getScalarVal(), CurFn->getReturnType())); CGM.getCXXABI().EmitReturnFromThunk(*this, RV, ResultType); + } // Disable the final ARC autorelease. AutoreleaseResult = false; @@ -389,8 +397,9 @@ FinishThunk(); } -void CodeGenFunction::EmitMustTailThunk(GlobalDecl GD, +void CodeGenFunction::EmitMustTailThunk(GlobalDecl CalleeGD, llvm::Value *AdjustedThisPtr, + const CGFunctionInfo &CalleeFnInfo, llvm::FunctionCallee Callee) { // Emitting a musttail call thunk doesn't use any of the CGCall.cpp machinery // to translate AST arguments into LLVM IR arguments. For thunks, we know @@ -401,11 +410,11 @@ Args.push_back(&A); // Set the adjusted 'this' pointer. - const ABIArgInfo &ThisAI = CurFnInfo->arg_begin()->info; + const ABIArgInfo &ThisAI = CalleeFnInfo.arg_begin()->info; if (ThisAI.isDirect()) { - const ABIArgInfo &RetAI = CurFnInfo->getReturnInfo(); + const ABIArgInfo &RetAI = CalleeFnInfo.getReturnInfo(); int ThisArgNo = RetAI.isIndirect() && !RetAI.isSRetAfterThis() ? 1 : 0; - llvm::Type *ThisType = Args[ThisArgNo]->getType(); + llvm::Type *ThisType = Callee.getFunctionType()->getParamType(ThisArgNo); if (ThisType != AdjustedThisPtr->getType()) AdjustedThisPtr = Builder.CreateBitCast(AdjustedThisPtr, ThisType); Args[ThisArgNo] = AdjustedThisPtr; @@ -426,15 +435,16 @@ // Apply the standard set of call attributes. unsigned CallingConv; llvm::AttributeList Attrs; - CGM.ConstructAttributeList(Callee.getCallee()->getName(), *CurFnInfo, GD, - Attrs, CallingConv, /*AttrOnCallSite=*/true); + CGM.ConstructAttributeList(Callee.getCallee()->getName(), CalleeFnInfo, + CalleeGD, Attrs, CallingConv, + /*AttrOnCallSite=*/true); Call->setAttributes(Attrs); Call->setCallingConv(static_cast(CallingConv)); if (Call->getType()->isVoidTy()) Builder.CreateRetVoid(); else - Builder.CreateRet(Call); + Builder.CreateRet(Builder.CreateBitCast(Call, CurFn->getReturnType())); // Finish the function to maintain CodeGenFunction invariants. // FIXME: Don't emit unreachable code. @@ -443,31 +453,34 @@ FinishThunk(); } -void CodeGenFunction::generateThunk(llvm::Function *Fn, - const CGFunctionInfo &FnInfo, GlobalDecl GD, +void CodeGenFunction::generateThunk(llvm::Function *ThunkFn, + const CGFunctionInfo &ThunkFnInfo, + GlobalDecl ThunkGD, GlobalDecl CalleeGD, const ThunkInfo &Thunk, bool IsUnprototyped) { - StartThunk(Fn, GD, FnInfo, IsUnprototyped); + StartThunk(ThunkFn, ThunkGD, ThunkFnInfo, 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); + auto *CalleeMD = cast(CalleeGD.getDecl()); + if (!CalleeMD->isDefined()) { + CGM.ErrorUnsupported(ThunkGD.getDecl(), "thunk for forward declaration"); + return; + } - llvm::Constant *Callee = CGM.GetAddrOfFunction(GD, Ty, /*ForVTable=*/true); + const CGFunctionInfo &CalleeFnInfo = + CGM.getTypes().arrangeGlobalDeclaration(CalleeGD); + llvm::FunctionType *CalleeFnTy = CGM.getTypes().GetFunctionType(CalleeFnInfo); + llvm::Constant *Callee = + CGM.GetAddrOfFunction(CalleeGD, CalleeFnTy, /*ForVTable=*/true); // Fix up the function type for an unprototyped musttail call. if (IsUnprototyped) - Callee = llvm::ConstantExpr::getBitCast(Callee, Fn->getType()); + Callee = llvm::ConstantExpr::getBitCast(Callee, CalleeFnTy); // Make the call and return the result. - EmitCallAndReturnForThunk(llvm::FunctionCallee(Fn->getFunctionType(), Callee), - &Thunk, IsUnprototyped); + EmitCallAndReturnForThunk(llvm::FunctionCallee(CalleeFnTy, Callee), + CalleeFnInfo, CalleeGD, &Thunk, IsUnprototyped); } static bool shouldEmitVTableThunk(CodeGenModule &CGM, const CXXMethodDecl *MD, @@ -503,21 +516,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 +542,15 @@ if (ThunkFn->getFunctionType() != ThunkFnTy) { llvm::GlobalValue *OldThunkFn = ThunkFn; - assert(OldThunkFn->isDeclaration() && "Shouldn't replace non-declaration"); + // FIXME: ??? + // 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 +583,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 +610,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, TI.BaseMethod, GD, + TI, IsUnprototyped); } setThunkProperties(CGM, TI, ThunkFn, ForVTable, GD); @@ -727,22 +747,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 @@ -2170,18 +2170,21 @@ const CGFunctionInfo &FnInfo, bool IsUnprototyped); void EmitCallAndReturnForThunk(llvm::FunctionCallee Callee, - const ThunkInfo *Thunk, bool IsUnprototyped); + const CGFunctionInfo &CalleeFnInfo, + GlobalDecl CalleeGD, const ThunkInfo *Thunk, + bool IsUnprototyped); void FinishThunk(); /// Emit a musttail call for a thunk with a potentially adjusted this pointer. - void EmitMustTailThunk(GlobalDecl GD, llvm::Value *AdjustedThisPtr, + void EmitMustTailThunk(GlobalDecl CalleeGD, llvm::Value *AdjustedThisPtr, + const CGFunctionInfo &CalleeFnInfo, 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 ThunkGD, GlobalDecl CalleeGD, + const ThunkInfo &Thunk, bool IsUnprototyped); llvm::Function *GenerateVarArgsThunk(llvm::Function *Fn, const CGFunctionInfo &FnInfo, diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -2084,7 +2084,7 @@ CGF.Builder.CreateAlignedLoad(ThunkTy->getPointerTo(), VFuncPtr, CGF.getPointerAlign()); - CGF.EmitMustTailThunk(MD, getThisValue(CGF), {ThunkTy, Callee}); + CGF.EmitMustTailThunk(MD, getThisValue(CGF), FnInfo, {ThunkTy, Callee}); return ThunkFn; } diff --git a/clang/test/CodeGen/available-externally-hidden.cpp b/clang/test/CodeGen/available-externally-hidden.cpp --- a/clang/test/CodeGen/available-externally-hidden.cpp +++ b/clang/test/CodeGen/available-externally-hidden.cpp @@ -21,6 +21,7 @@ class SyncMessageFilter : public Filter, public Sender { public: bool Send(Message* message) override; + // expected-error@-1 {{cannot compile this thunk for forward declaration yet}} }; class TestSyncMessageFilter : public SyncMessageFilter { diff --git a/clang/test/CodeGenCXX/RelativeVTablesABI/diamond-inheritance.cpp b/clang/test/CodeGenCXX/RelativeVTablesABI/diamond-inheritance.cpp --- a/clang/test/CodeGenCXX/RelativeVTablesABI/diamond-inheritance.cpp +++ b/clang/test/CodeGenCXX/RelativeVTablesABI/diamond-inheritance.cpp @@ -18,7 +18,7 @@ // vtable contains 2 inner vtables: // - 1st table containing D::foo(), B::barB(), and D::baz(). // - 2nd table containing a thunk to D::foo() and C::barC(). -// CHECK: @_ZTV1D.local = private unnamed_addr constant { [5 x i32], [4 x i32] } { [5 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1D.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.D*)* dso_local_equivalent @_ZN1D3fooEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* dso_local_equivalent @_ZN1B4barBEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.D*)* dso_local_equivalent @_ZN1D3bazEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 0, i32 2) to i64)) to i32)], [4 x i32] [i32 -8, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1D.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.D*)* dso_local_equivalent @_ZThn8_N1D3fooEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* dso_local_equivalent @_ZN1C4barCEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 1, i32 2) to i64)) to i32)] }, align 4 +// CHECK: @_ZTV1D.local = private unnamed_addr constant { [5 x i32], [4 x i32] } { [5 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1D.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.D*)* dso_local_equivalent @_ZN1D3fooEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* dso_local_equivalent @_ZN1B4barBEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.D*)* dso_local_equivalent @_ZN1D3bazEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 0, i32 2) to i64)) to i32)], [4 x i32] [i32 -8, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1D.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.A*)* dso_local_equivalent @_ZThn8_N1D3fooEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* dso_local_equivalent @_ZN1C4barCEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [5 x i32], [4 x i32] }, { [5 x i32], [4 x i32] }* @_ZTV1D.local, i32 0, i32 1, i32 2) to i64)) to i32)] }, align 4 // @_ZTV1B ={{.*}} unnamed_addr alias { [4 x i32] }, { [4 x i32] }* @_ZTV1B.local // @_ZTV1C ={{.*}} unnamed_addr alias { [4 x i32] }, { [4 x i32] }* @_ZTV1C.local diff --git a/clang/test/CodeGenCXX/RelativeVTablesABI/multiple-inheritance.cpp b/clang/test/CodeGenCXX/RelativeVTablesABI/multiple-inheritance.cpp --- a/clang/test/CodeGenCXX/RelativeVTablesABI/multiple-inheritance.cpp +++ b/clang/test/CodeGenCXX/RelativeVTablesABI/multiple-inheritance.cpp @@ -9,7 +9,7 @@ // VTable for C contains 2 sub-vtables (represented as 2 structs). The first contains the components for B and the second contains the components for C. The RTTI ptr in both arrays still point to the RTTI struct for C. // The component for bar() instead points to a thunk which redirects to C::bar() which overrides B::bar(). // Now that we have a class with 2 parents, the offset to top in the second array is non-zero. -// CHECK: @_ZTV1C.local = private unnamed_addr constant { [4 x i32], [3 x i32] } { [4 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* dso_local_equivalent @_ZN1C3fooEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* dso_local_equivalent @_ZN1C3barEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32)], [3 x i32] [i32 -8, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* dso_local_equivalent @_ZThn8_N1C3barEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 1, i32 2) to i64)) to i32)] }, align 4 +// CHECK: @_ZTV1C.local = private unnamed_addr constant { [4 x i32], [3 x i32] } { [4 x i32] [i32 0, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* dso_local_equivalent @_ZN1C3fooEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.C*)* dso_local_equivalent @_ZN1C3barEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 0, i32 2) to i64)) to i32)], [3 x i32] [i32 -8, i32 trunc (i64 sub (i64 ptrtoint ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }** @_ZTI1C.rtti_proxy to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 1, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%class.B*)* dso_local_equivalent @_ZThn8_N1C3barEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local, i32 0, i32 1, i32 2) to i64)) to i32)] }, align 4 // CHECK: @_ZTV1C ={{.*}} unnamed_addr alias { [4 x i32], [3 x i32] }, { [4 x i32], [3 x i32] }* @_ZTV1C.local 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) #1 align 2 { +// CHECK: tail call void @_ZN3Obj4Foo2Ev(%class.Obj.0* nonnull dereferenceable(16) %3) diff --git a/clang/test/CodeGenCXX/thunks.cpp b/clang/test/CodeGenCXX/thunks.cpp --- a/clang/test/CodeGenCXX/thunks.cpp +++ b/clang/test/CodeGenCXX/thunks.cpp @@ -410,7 +410,7 @@ // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 -32 // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 -24 // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 8 - // CHECK: ret %"struct.Test13::D"* + // CHECK: ret %"struct.Test13::B1"* // WIN64-LABEL: define weak_odr dso_local nonnull align 8 dereferenceable(8) %"struct.Test13::D"* @"?foo1@D@Test13@@$4PPPPPPPE@A@EAAAEAUB1@2@XZ"( // This adjustment.