forked from OSchip/llvm-project
Abstract out parts of thunk emission code, add support for simple thunks when using -cxx-abi microsoft
Reviewed at http://llvm-reviews.chandlerc.com/D1787 llvm-svn: 192220
This commit is contained in:
parent
71cd285dc8
commit
1ffb3916ce
|
@ -200,7 +200,6 @@ private:
|
|||
class VTableLayout {
|
||||
public:
|
||||
typedef std::pair<uint64_t, ThunkInfo> VTableThunkTy;
|
||||
typedef SmallVector<ThunkInfo, 1> ThunkInfoVectorTy;
|
||||
|
||||
typedef const VTableComponent *vtable_component_iterator;
|
||||
typedef const VTableThunkTy *vtable_thunk_iterator;
|
||||
|
@ -210,7 +209,7 @@ private:
|
|||
uint64_t NumVTableComponents;
|
||||
llvm::OwningArrayPtr<VTableComponent> VTableComponents;
|
||||
|
||||
/// \brief Contains thunks needed by vtables.
|
||||
/// \brief Contains thunks needed by vtables, sorted by indices.
|
||||
uint64_t NumVTableThunks;
|
||||
llvm::OwningArrayPtr<VTableThunkTy> VTableThunks;
|
||||
|
||||
|
@ -285,9 +284,12 @@ protected:
|
|||
virtual ~VTableContextBase() {}
|
||||
|
||||
public:
|
||||
const ThunkInfoVectorTy *getThunkInfo(const CXXMethodDecl *MD) {
|
||||
virtual const ThunkInfoVectorTy *getThunkInfo(GlobalDecl GD) {
|
||||
const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()->getCanonicalDecl());
|
||||
computeVTableRelatedInformation(MD->getParent());
|
||||
|
||||
// This assumes that all the destructors present in the vtable
|
||||
// use exactly the same set of thunks.
|
||||
ThunksMapTy::const_iterator I = Thunks.find(MD);
|
||||
if (I == Thunks.end()) {
|
||||
// We did not find a thunk for this method.
|
||||
|
@ -483,6 +485,14 @@ public:
|
|||
CharUnits VFPtrOffset);
|
||||
|
||||
const MethodVFTableLocation &getMethodVFTableLocation(GlobalDecl GD);
|
||||
|
||||
const ThunkInfoVectorTy *getThunkInfo(GlobalDecl GD) {
|
||||
// Complete destructors don't have a slot in a vftable, so no thunks needed.
|
||||
if (isa<CXXDestructorDecl>(GD.getDecl()) &&
|
||||
GD.getDtorType() == Dtor_Complete)
|
||||
return 0;
|
||||
return VTableContextBase::getThunkInfo(GD);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,10 @@ struct ReturnAdjustment {
|
|||
LHS.VBaseOffsetOffset == RHS.VBaseOffsetOffset;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -84,6 +88,10 @@ struct ThisAdjustment {
|
|||
LHS.VCallOffsetOffset == RHS.VCallOffsetOffset;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -94,6 +102,8 @@ struct ThisAdjustment {
|
|||
}
|
||||
};
|
||||
|
||||
class CXXMethodDecl;
|
||||
|
||||
/// \brief The \c this pointer adjustment as well as an optional return
|
||||
/// adjustment for a thunk.
|
||||
struct ThunkInfo {
|
||||
|
@ -103,23 +113,25 @@ struct ThunkInfo {
|
|||
/// \brief The return adjustment.
|
||||
ReturnAdjustment Return;
|
||||
|
||||
ThunkInfo() { }
|
||||
/// \brief 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(const ThisAdjustment &This, const ReturnAdjustment &Return)
|
||||
: This(This), Return(Return) { }
|
||||
ThunkInfo() : Method(0) { }
|
||||
|
||||
ThunkInfo(const ThisAdjustment &This, const ReturnAdjustment &Return,
|
||||
const CXXMethodDecl *Method = 0)
|
||||
: This(This), Return(Return), Method(Method) {}
|
||||
|
||||
friend bool operator==(const ThunkInfo &LHS, const ThunkInfo &RHS) {
|
||||
return LHS.This == RHS.This && LHS.Return == RHS.Return;
|
||||
return LHS.This == RHS.This && LHS.Return == RHS.Return &&
|
||||
LHS.Method == RHS.Method;
|
||||
}
|
||||
|
||||
friend bool operator<(const ThunkInfo &LHS, const ThunkInfo &RHS) {
|
||||
if (LHS.This < RHS.This)
|
||||
return true;
|
||||
|
||||
return LHS.This == RHS.This && LHS.Return < RHS.Return;
|
||||
}
|
||||
|
||||
bool isEmpty() const { return This.isEmpty() && Return.isEmpty(); }
|
||||
bool isEmpty() const { return This.isEmpty() && Return.isEmpty() && Method == 0; }
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/CharUnits.h"
|
||||
#include "clang/AST/CXXInheritance.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
|
@ -1405,7 +1406,8 @@ void MicrosoftCXXNameMangler::mangleFunctionClass(const FunctionDecl *FD) {
|
|||
// ::= Z # global far
|
||||
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) {
|
||||
switch (MD->getAccess()) {
|
||||
default:
|
||||
case AS_none:
|
||||
llvm_unreachable("Unsupported access specifier");
|
||||
case AS_private:
|
||||
if (MD->isStatic())
|
||||
Out << 'C';
|
||||
|
@ -1858,36 +1860,70 @@ void MicrosoftMangleContextImpl::mangleName(const NamedDecl *D,
|
|||
return Mangler.mangle(D);
|
||||
}
|
||||
|
||||
static void mangleThunkThisAdjustment(const CXXMethodDecl *MD,
|
||||
const ThisAdjustment &Adjustment,
|
||||
MicrosoftCXXNameMangler &Mangler,
|
||||
raw_ostream &Out) {
|
||||
// FIXME: add support for vtordisp thunks.
|
||||
if (Adjustment.NonVirtual != 0) {
|
||||
switch (MD->getAccess()) {
|
||||
case AS_none:
|
||||
llvm_unreachable("Unsupported access specifier");
|
||||
case AS_private:
|
||||
Out << 'G';
|
||||
break;
|
||||
case AS_protected:
|
||||
Out << 'O';
|
||||
break;
|
||||
case AS_public:
|
||||
Out << 'W';
|
||||
}
|
||||
llvm::APSInt APSNumber(/*BitWidth=*/32, /*isUnsigned=*/true);
|
||||
APSNumber = -Adjustment.NonVirtual;
|
||||
Mangler.mangleNumber(APSNumber);
|
||||
} else {
|
||||
switch (MD->getAccess()) {
|
||||
case AS_none:
|
||||
llvm_unreachable("Unsupported access specifier");
|
||||
case AS_private:
|
||||
Out << 'A';
|
||||
break;
|
||||
case AS_protected:
|
||||
Out << 'I';
|
||||
break;
|
||||
case AS_public:
|
||||
Out << 'Q';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MicrosoftMangleContextImpl::mangleThunk(const CXXMethodDecl *MD,
|
||||
const ThunkInfo &Thunk,
|
||||
raw_ostream &Out) {
|
||||
// FIXME: this is not yet a complete implementation, but merely a
|
||||
// reasonably-working stub to avoid crashing when required to emit a thunk.
|
||||
MicrosoftCXXNameMangler Mangler(*this, Out);
|
||||
Out << "\01?";
|
||||
Mangler.mangleName(MD);
|
||||
if (Thunk.This.NonVirtual != 0) {
|
||||
// FIXME: add support for protected/private or use mangleFunctionClass.
|
||||
Out << "W";
|
||||
llvm::APSInt APSNumber(/*BitWidth=*/32 /*FIXME: check on x64*/,
|
||||
/*isUnsigned=*/true);
|
||||
APSNumber = -Thunk.This.NonVirtual;
|
||||
Mangler.mangleNumber(APSNumber);
|
||||
} else {
|
||||
// FIXME: add support for protected/private or use mangleFunctionClass.
|
||||
Out << "Q";
|
||||
}
|
||||
// FIXME: mangle return adjustment? Most likely includes using an overridee FPT?
|
||||
Mangler.mangleFunctionType(MD->getType()->castAs<FunctionProtoType>(), MD);
|
||||
mangleThunkThisAdjustment(MD, Thunk.This, Mangler, Out);
|
||||
if (!Thunk.Return.isEmpty())
|
||||
assert(Thunk.Method != 0 && "Thunk info should hold the overridee decl");
|
||||
|
||||
const CXXMethodDecl *DeclForFPT = Thunk.Method ? Thunk.Method : MD;
|
||||
Mangler.mangleFunctionType(
|
||||
DeclForFPT->getType()->castAs<FunctionProtoType>(), MD);
|
||||
}
|
||||
|
||||
void MicrosoftMangleContextImpl::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
|
||||
CXXDtorType Type,
|
||||
const ThisAdjustment &,
|
||||
raw_ostream &) {
|
||||
unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error,
|
||||
"cannot mangle thunk for this destructor yet");
|
||||
getDiags().Report(DD->getLocation(), DiagID);
|
||||
void MicrosoftMangleContextImpl::mangleCXXDtorThunk(
|
||||
const CXXDestructorDecl *DD, CXXDtorType Type,
|
||||
const ThisAdjustment &Adjustment, raw_ostream &Out) {
|
||||
// FIXME: Actually, the dtor thunk should be emitted for vector deleting
|
||||
// dtors rather than scalar deleting dtors. Just use the vector deleting dtor
|
||||
// mangling manually until we support both deleting dtor types.
|
||||
assert(Type == Dtor_Deleting);
|
||||
MicrosoftCXXNameMangler Mangler(*this, Out, DD, Type);
|
||||
Out << "\01??_E";
|
||||
Mangler.mangleName(DD->getParent());
|
||||
mangleThunkThisAdjustment(DD, Adjustment, Mangler, Out);
|
||||
Mangler.mangleFunctionType(DD->getType()->castAs<FunctionProtoType>(), DD);
|
||||
}
|
||||
|
||||
void MicrosoftMangleContextImpl::mangleCXXVFTable(
|
||||
|
|
|
@ -992,6 +992,7 @@ public:
|
|||
MostDerivedClassIsVirtual(MostDerivedClassIsVirtual),
|
||||
LayoutClass(LayoutClass), Context(MostDerivedClass->getASTContext()),
|
||||
Overriders(MostDerivedClass, MostDerivedClassOffset, LayoutClass) {
|
||||
assert(!Context.getTargetInfo().getCXXABI().isMicrosoft());
|
||||
|
||||
LayoutVTable();
|
||||
|
||||
|
@ -1904,6 +1905,21 @@ VTableBuilder::LayoutVTablesForVirtualBases(const CXXRecordDecl *RD,
|
|||
}
|
||||
}
|
||||
|
||||
struct ItaniumThunkInfoComparator {
|
||||
bool operator() (const ThunkInfo &LHS, const ThunkInfo &RHS) {
|
||||
assert(LHS.Method == 0);
|
||||
assert(RHS.Method == 0);
|
||||
|
||||
if (LHS.This != RHS.This)
|
||||
return LHS.This < RHS.This;
|
||||
|
||||
if (LHS.Return != RHS.Return)
|
||||
return LHS.Return < RHS.Return;
|
||||
|
||||
llvm_unreachable("Shouldn't observe two equal thunks");
|
||||
}
|
||||
};
|
||||
|
||||
/// dumpLayout - Dump the vtable layout.
|
||||
void VTableBuilder::dumpLayout(raw_ostream& Out) {
|
||||
// FIXME: write more tests that actually use the dumpLayout output to prevent
|
||||
|
@ -2146,7 +2162,8 @@ void VTableBuilder::dumpLayout(raw_ostream& Out) {
|
|||
const CXXMethodDecl *MD = I->second;
|
||||
|
||||
ThunkInfoVectorTy ThunksVector = Thunks[MD];
|
||||
std::sort(ThunksVector.begin(), ThunksVector.end());
|
||||
std::sort(ThunksVector.begin(), ThunksVector.end(),
|
||||
ItaniumThunkInfoComparator());
|
||||
|
||||
Out << "Thunks for '" << MethodName << "' (" << ThunksVector.size();
|
||||
Out << (ThunksVector.size() == 1 ? " entry" : " entries") << ").\n";
|
||||
|
@ -2234,6 +2251,14 @@ void VTableBuilder::dumpLayout(raw_ostream& Out) {
|
|||
Out << '\n';
|
||||
}
|
||||
|
||||
struct VTableThunksComparator {
|
||||
bool operator()(const VTableLayout::VTableThunkTy &LHS,
|
||||
const VTableLayout::VTableThunkTy &RHS) {
|
||||
assert(LHS.first != RHS.first &&
|
||||
"All thunks should have unique indices!");
|
||||
return LHS.first < RHS.first;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
VTableLayout::VTableLayout(uint64_t NumVTableComponents,
|
||||
|
@ -2252,6 +2277,9 @@ VTableLayout::VTableLayout(uint64_t NumVTableComponents,
|
|||
this->VTableComponents.get());
|
||||
std::copy(VTableThunks, VTableThunks+NumVTableThunks,
|
||||
this->VTableThunks.get());
|
||||
std::sort(this->VTableThunks.get(),
|
||||
this->VTableThunks.get() + NumVTableThunks,
|
||||
VTableThunksComparator());
|
||||
}
|
||||
|
||||
VTableLayout::~VTableLayout() { }
|
||||
|
@ -2312,7 +2340,6 @@ VTableContext::getVirtualBaseOffsetOffset(const CXXRecordDecl *RD,
|
|||
static VTableLayout *CreateVTableLayout(const VTableBuilder &Builder) {
|
||||
SmallVector<VTableLayout::VTableThunkTy, 1>
|
||||
VTableThunks(Builder.vtable_thunks_begin(), Builder.vtable_thunks_end());
|
||||
std::sort(VTableThunks.begin(), VTableThunks.end());
|
||||
|
||||
return new VTableLayout(Builder.getNumVTableComponents(),
|
||||
Builder.vtable_component_begin(),
|
||||
|
@ -2520,18 +2547,14 @@ private:
|
|||
|
||||
/// AddMethod - Add a single virtual member function to the vftable
|
||||
/// components vector.
|
||||
void AddMethod(const CXXMethodDecl *MD, ThisAdjustment ThisAdjustment,
|
||||
ReturnAdjustment ReturnAdjustment) {
|
||||
void AddMethod(const CXXMethodDecl *MD, ThunkInfo TI) {
|
||||
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
|
||||
assert(ReturnAdjustment.isEmpty() &&
|
||||
assert(TI.Return.isEmpty() &&
|
||||
"Destructor can't have return adjustment!");
|
||||
Components.push_back(VTableComponent::MakeDeletingDtor(DD));
|
||||
} else {
|
||||
// Add the return adjustment if necessary.
|
||||
if (!ReturnAdjustment.isEmpty() || !ThisAdjustment.isEmpty()) {
|
||||
VTableThunks[Components.size()].Return = ReturnAdjustment;
|
||||
VTableThunks[Components.size()].This = ThisAdjustment;
|
||||
}
|
||||
if (!TI.isEmpty())
|
||||
VTableThunks[Components.size()] = TI;
|
||||
Components.push_back(VTableComponent::MakeFunction(MD));
|
||||
}
|
||||
}
|
||||
|
@ -2816,6 +2839,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
|
|||
FinalOverriders::OverriderInfo Overrider =
|
||||
Overriders.getOverrider(MD, Base.getBaseOffset());
|
||||
ThisAdjustment ThisAdjustmentOffset;
|
||||
bool ForceThunk = false;
|
||||
|
||||
// Check if this virtual member function overrides
|
||||
// a method in one of the visited bases.
|
||||
|
@ -2840,8 +2864,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
|
|||
AddThunk(MD, VTableThunks[OverriddenMethodInfo.VFTableIndex]);
|
||||
}
|
||||
|
||||
if (ComputeReturnAdjustmentBaseOffset(Context, MD, OverriddenMD)
|
||||
.isEmpty()) {
|
||||
if (MD->getResultType() == OverriddenMD->getResultType()) {
|
||||
// No return adjustment needed - just replace the overridden method info
|
||||
// with the current info.
|
||||
MethodInfo MI(OverriddenMethodInfo.VBTableIndex,
|
||||
|
@ -2859,6 +2882,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
|
|||
// method was in the vftable.
|
||||
// For now, just mark the overriden method as shadowed by a new slot.
|
||||
OverriddenMethodInfo.Shadowed = true;
|
||||
ForceThunk = true;
|
||||
|
||||
// Also apply this adjustment to the shadowed slots.
|
||||
if (!ThisAdjustmentOffset.isEmpty()) {
|
||||
|
@ -2907,6 +2931,7 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
|
|||
ComputeReturnAdjustmentBaseOffset(Context, OverriderMD, MD);
|
||||
}
|
||||
if (!ReturnAdjustmentOffset.isEmpty()) {
|
||||
ForceThunk = true;
|
||||
ReturnAdjustment.NonVirtual =
|
||||
ReturnAdjustmentOffset.NonVirtualOffset.getQuantity();
|
||||
if (ReturnAdjustmentOffset.VirtualBase) {
|
||||
|
@ -2918,7 +2943,8 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
|
|||
}
|
||||
}
|
||||
|
||||
AddMethod(Overrider.Method, ThisAdjustmentOffset, ReturnAdjustment);
|
||||
AddMethod(OverriderMD, ThunkInfo(ThisAdjustmentOffset, ReturnAdjustment,
|
||||
ForceThunk ? MD : 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2929,6 +2955,20 @@ void PrintBasePath(const VFPtrInfo::BasePath &Path, raw_ostream &Out) {
|
|||
}
|
||||
}
|
||||
|
||||
struct MicrosoftThunkInfoStableSortComparator {
|
||||
bool operator() (const ThunkInfo &LHS, const ThunkInfo &RHS) {
|
||||
if (LHS.This != RHS.This)
|
||||
return LHS.This < RHS.This;
|
||||
|
||||
if (LHS.Return != RHS.Return)
|
||||
return LHS.Return < RHS.Return;
|
||||
|
||||
// Keep different thunks with the same adjustments in the order they
|
||||
// were put into the vector.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void VFTableBuilder::dumpLayout(raw_ostream &Out) {
|
||||
Out << "VFTable for ";
|
||||
PrintBasePath(WhichVFPtr.PathToBaseWithVFPtr, Out);
|
||||
|
@ -3042,7 +3082,8 @@ void VFTableBuilder::dumpLayout(raw_ostream &Out) {
|
|||
const CXXMethodDecl *MD = I->second;
|
||||
|
||||
ThunkInfoVectorTy ThunksVector = Thunks[MD];
|
||||
std::sort(ThunksVector.begin(), ThunksVector.end());
|
||||
std::stable_sort(ThunksVector.begin(), ThunksVector.end(),
|
||||
MicrosoftThunkInfoStableSortComparator());
|
||||
|
||||
Out << "Thunks for '" << MethodName << "' (" << ThunksVector.size();
|
||||
Out << (ThunksVector.size() == 1 ? " entry" : " entries") << ").\n";
|
||||
|
@ -3218,7 +3259,6 @@ void MicrosoftVFTableContext::computeVTableRelatedInformation(
|
|||
assert(VFTableLayouts.count(id) == 0);
|
||||
SmallVector<VTableLayout::VTableThunkTy, 1> VTableThunks(
|
||||
Builder.vtable_thunks_begin(), Builder.vtable_thunks_end());
|
||||
std::sort(VTableThunks.begin(), VTableThunks.end());
|
||||
VFTableLayouts[id] = new VTableLayout(
|
||||
Builder.getNumVTableComponents(), Builder.vtable_component_begin(),
|
||||
VTableThunks.size(), VTableThunks.data(), EmptyAddressPointsMap, true);
|
||||
|
|
|
@ -339,11 +339,17 @@ public:
|
|||
SourceLocation CallLoc,
|
||||
llvm::Value *This) = 0;
|
||||
|
||||
virtual void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF,
|
||||
GlobalDecl GD,
|
||||
CallArgList &CallArgs) {}
|
||||
|
||||
/// Emit any tables needed to implement virtual inheritance. For Itanium,
|
||||
/// this emits virtual table tables. For the MSVC++ ABI, this emits virtual
|
||||
/// base tables.
|
||||
virtual void emitVirtualInheritanceTables(const CXXRecordDecl *RD) = 0;
|
||||
|
||||
virtual void setThunkLinkage(llvm::Function *Thunk, bool ForVTable) = 0;
|
||||
|
||||
virtual void EmitReturnFromThunk(CodeGenFunction &CGF,
|
||||
RValue RV, QualType ResultType);
|
||||
|
||||
|
|
|
@ -333,6 +333,9 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn,
|
|||
// Add our adjusted 'this' pointer.
|
||||
CallArgs.add(RValue::get(AdjustedThisPtr), ThisType);
|
||||
|
||||
if (isa<CXXDestructorDecl>(MD))
|
||||
CGM.getCXXABI().adjustCallArgsForDestructorThunk(*this, GD, CallArgs);
|
||||
|
||||
// Add the rest of the parameters.
|
||||
for (FunctionDecl::param_const_iterator I = MD->param_begin(),
|
||||
E = MD->param_end(); I != E; ++I) {
|
||||
|
@ -390,14 +393,8 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn,
|
|||
setThunkVisibility(CGM, MD, Thunk, Fn);
|
||||
}
|
||||
|
||||
void CodeGenVTables::EmitThunk(GlobalDecl GD, const ThunkInfo &Thunk,
|
||||
bool UseAvailableExternallyLinkage)
|
||||
{
|
||||
if (CGM.getTarget().getCXXABI().isMicrosoft()) {
|
||||
// Emission of thunks is not supported yet in Microsoft ABI.
|
||||
return;
|
||||
}
|
||||
|
||||
void CodeGenVTables::emitThunk(GlobalDecl GD, const ThunkInfo &Thunk,
|
||||
bool ForVTable) {
|
||||
const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeGlobalDeclaration(GD);
|
||||
|
||||
// FIXME: re-use FnInfo in this computation.
|
||||
|
@ -435,9 +432,11 @@ void CodeGenVTables::EmitThunk(GlobalDecl GD, const ThunkInfo &Thunk,
|
|||
}
|
||||
|
||||
llvm::Function *ThunkFn = cast<llvm::Function>(Entry);
|
||||
bool ABIHasKeyFunctions = CGM.getTarget().getCXXABI().hasKeyFunctions();
|
||||
bool UseAvailableExternallyLinkage = ForVTable && ABIHasKeyFunctions;
|
||||
|
||||
if (!ThunkFn->isDeclaration()) {
|
||||
if (UseAvailableExternallyLinkage) {
|
||||
if (!ABIHasKeyFunctions || UseAvailableExternallyLinkage) {
|
||||
// There is already a thunk emitted for this function, do nothing.
|
||||
return;
|
||||
}
|
||||
|
@ -466,14 +465,17 @@ void CodeGenVTables::EmitThunk(GlobalDecl GD, const ThunkInfo &Thunk,
|
|||
CodeGenFunction(CGM).GenerateThunk(ThunkFn, FnInfo, GD, Thunk);
|
||||
}
|
||||
|
||||
if (UseAvailableExternallyLinkage)
|
||||
ThunkFn->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage);
|
||||
CGM.getCXXABI().setThunkLinkage(ThunkFn, ForVTable);
|
||||
}
|
||||
|
||||
void CodeGenVTables::MaybeEmitThunkAvailableExternally(GlobalDecl GD,
|
||||
const ThunkInfo &Thunk) {
|
||||
// We only want to do this when building with optimizations.
|
||||
if (!CGM.getCodeGenOpts().OptimizationLevel)
|
||||
void CodeGenVTables::maybeEmitThunkForVTable(GlobalDecl GD,
|
||||
const ThunkInfo &Thunk) {
|
||||
// If the ABI has key functions, only the TU with the key function should emit
|
||||
// the thunk. However, we can allow inlining of thunks if we emit them with
|
||||
// available_externally linkage together with vtables when optimizations are
|
||||
// enabled.
|
||||
if (CGM.getTarget().getCXXABI().hasKeyFunctions() &&
|
||||
!CGM.getCodeGenOpts().OptimizationLevel)
|
||||
return;
|
||||
|
||||
// We can't emit thunks for member functions with incomplete types.
|
||||
|
@ -482,7 +484,7 @@ void CodeGenVTables::MaybeEmitThunkAvailableExternally(GlobalDecl GD,
|
|||
cast<FunctionType>(MD->getType().getTypePtr())))
|
||||
return;
|
||||
|
||||
EmitThunk(GD, Thunk, /*UseAvailableExternallyLinkage=*/true);
|
||||
emitThunk(GD, Thunk, /*ForVTable=*/true);
|
||||
}
|
||||
|
||||
void CodeGenVTables::EmitThunks(GlobalDecl GD)
|
||||
|
@ -494,21 +496,18 @@ void CodeGenVTables::EmitThunks(GlobalDecl GD)
|
|||
if (isa<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Base)
|
||||
return;
|
||||
|
||||
const VTableContext::ThunkInfoVectorTy *ThunkInfoVector;
|
||||
if (VFTContext.isValid()) {
|
||||
// FIXME: This is a temporary solution to force generation of vftables in
|
||||
// Microsoft ABI. Remove when we thread VFTableContext through CodeGen.
|
||||
VFTContext->getVFPtrOffsets(MD->getParent());
|
||||
return;
|
||||
ThunkInfoVector = VFTContext->getThunkInfo(GD);
|
||||
} else {
|
||||
ThunkInfoVector = VTContext.getThunkInfo(GD);
|
||||
}
|
||||
|
||||
const VTableContext::ThunkInfoVectorTy *ThunkInfoVector =
|
||||
VTContext.getThunkInfo(MD);
|
||||
if (!ThunkInfoVector)
|
||||
return;
|
||||
|
||||
for (unsigned I = 0, E = ThunkInfoVector->size(); I != E; ++I)
|
||||
EmitThunk(GD, (*ThunkInfoVector)[I],
|
||||
/*UseAvailableExternallyLinkage=*/false);
|
||||
emitThunk(GD, (*ThunkInfoVector)[I], /*ForVTable=*/false);
|
||||
}
|
||||
|
||||
llvm::Constant *
|
||||
|
@ -603,7 +602,7 @@ CodeGenVTables::CreateVTableInitializer(const CXXRecordDecl *RD,
|
|||
VTableThunks[NextVTableThunkIndex].first == I) {
|
||||
const ThunkInfo &Thunk = VTableThunks[NextVTableThunkIndex].second;
|
||||
|
||||
MaybeEmitThunkAvailableExternally(GD, Thunk);
|
||||
maybeEmitThunkForVTable(GD, Thunk);
|
||||
Init = CGM.GetAddrOfThunk(GD, Thunk);
|
||||
|
||||
NextVTableThunkIndex++;
|
||||
|
|
|
@ -52,15 +52,12 @@ class CodeGenVTables {
|
|||
/// indices.
|
||||
SecondaryVirtualPointerIndicesMapTy SecondaryVirtualPointerIndices;
|
||||
|
||||
/// EmitThunk - Emit a single thunk.
|
||||
void EmitThunk(GlobalDecl GD, const ThunkInfo &Thunk,
|
||||
bool UseAvailableExternallyLinkage);
|
||||
/// emitThunk - Emit a single thunk.
|
||||
void emitThunk(GlobalDecl GD, const ThunkInfo &Thunk, bool ForVTable);
|
||||
|
||||
/// MaybeEmitThunkAvailableExternally - Try to emit the given thunk with
|
||||
/// available_externally linkage to allow for inlining of thunks.
|
||||
/// This will be done iff optimizations are enabled and the member function
|
||||
/// doesn't contain any incomplete types.
|
||||
void MaybeEmitThunkAvailableExternally(GlobalDecl GD, const ThunkInfo &Thunk);
|
||||
/// maybeEmitThunkForVTable - Emit the given thunk for the vtable if needed by
|
||||
/// the ABI.
|
||||
void maybeEmitThunkForVTable(GlobalDecl GD, const ThunkInfo &Thunk);
|
||||
|
||||
public:
|
||||
/// CreateVTableInitializer - Create a vtable initializer for the given record
|
||||
|
|
|
@ -173,6 +173,13 @@ public:
|
|||
|
||||
void emitVirtualInheritanceTables(const CXXRecordDecl *RD);
|
||||
|
||||
void setThunkLinkage(llvm::Function *Thunk, bool ForVTable) {
|
||||
// Allow inlining of thunks by emitting them with available_externally
|
||||
// linkage together with vtables when needed.
|
||||
if (ForVTable)
|
||||
Thunk->setLinkage(llvm::GlobalValue::AvailableExternallyLinkage);
|
||||
}
|
||||
|
||||
StringRef GetPureVirtualCallName() { return "__cxa_pure_virtual"; }
|
||||
StringRef GetDeletedVirtualCallName() { return "__cxa_deleted_virtual"; }
|
||||
|
||||
|
|
|
@ -173,8 +173,20 @@ public:
|
|||
CXXDtorType DtorType, SourceLocation CallLoc,
|
||||
llvm::Value *This);
|
||||
|
||||
void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD,
|
||||
CallArgList &CallArgs) {
|
||||
assert(GD.getDtorType() == Dtor_Deleting &&
|
||||
"Only deleting destructor thunks are available in this ABI");
|
||||
CallArgs.add(RValue::get(getStructorImplicitParamValue(CGF)),
|
||||
CGM.getContext().IntTy);
|
||||
}
|
||||
|
||||
void emitVirtualInheritanceTables(const CXXRecordDecl *RD);
|
||||
|
||||
void setThunkLinkage(llvm::Function *Thunk, bool ForVTable) {
|
||||
Thunk->setLinkage(llvm::GlobalValue::WeakAnyLinkage);
|
||||
}
|
||||
|
||||
void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
|
||||
llvm::GlobalVariable *DeclPtr,
|
||||
bool PerformInit);
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
// RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 >%t 2>&1
|
||||
// RUN: FileCheck --check-prefix=MANGLING %s < %t
|
||||
// RUN: FileCheck --check-prefix=XMANGLING %s < %t
|
||||
// RUN: FileCheck --check-prefix=CODEGEN %s < %t
|
||||
// RUN: %clang_cc1 -fno-rtti -emit-llvm %s -o - -cxx-abi microsoft -triple=x86_64-pc-win32 2>&1 | FileCheck --check-prefix=MANGLING-X64 %s
|
||||
|
||||
void foo(void *);
|
||||
|
||||
struct A {
|
||||
virtual ~A();
|
||||
virtual void public_f();
|
||||
// Make sure we don't emit unneeded thunks:
|
||||
// XMANGLING-NOT: @"\01?public_f@A@@QAEXXZ"
|
||||
protected:
|
||||
virtual void protected_f();
|
||||
private:
|
||||
virtual void private_f();
|
||||
};
|
||||
|
||||
struct B {
|
||||
virtual ~B();
|
||||
virtual void public_f();
|
||||
protected:
|
||||
virtual void protected_f();
|
||||
private:
|
||||
virtual void private_f();
|
||||
};
|
||||
|
||||
|
||||
struct C : A, B {
|
||||
C();
|
||||
|
||||
virtual ~C();
|
||||
// MANGLING-DAG: @"\01??1C@@UAE@XZ"
|
||||
// MANGLING-DAG: @"\01??_GC@@UAEPAXI@Z"
|
||||
// MANGLING-DAG: @"\01??_EC@@W3AEPAXI@Z"
|
||||
// MANGLING-X64-DAG: @"\01??1C@@UEAA@XZ"
|
||||
// MANGLING-X64-DAG: @"\01??_GC@@UEAAPEAXI@Z"
|
||||
// MANGLING-X64-DAG: @"\01??_EC@@W7EAAPEAXI@Z"
|
||||
|
||||
// Overrides public_f() of two subobjects with distinct vfptrs, thus needs a thunk.
|
||||
virtual void public_f();
|
||||
// MANGLING-DAG: @"\01?public_f@C@@UAEXXZ"
|
||||
// MANGLING-DAG: @"\01?public_f@C@@W3AEXXZ"
|
||||
// MANGLING-X64-DAG: @"\01?public_f@C@@UEAAXXZ"
|
||||
// MANGLING-X64-DAG: @"\01?public_f@C@@W7EAAXXZ"
|
||||
protected:
|
||||
virtual void protected_f();
|
||||
// MANGLING-DAG: @"\01?protected_f@C@@MAEXXZ"
|
||||
// MANGLING-DAG: @"\01?protected_f@C@@O3AEXXZ"
|
||||
// MANGLING-X64-DAG: @"\01?protected_f@C@@MEAAXXZ"
|
||||
// MANGLING-X64-DAG: @"\01?protected_f@C@@O7EAAXXZ"
|
||||
|
||||
private:
|
||||
virtual void private_f();
|
||||
// MANGLING-DAG: @"\01?private_f@C@@EAEXXZ"
|
||||
// MANGLING-DAG: @"\01?private_f@C@@G3AEXXZ"
|
||||
// MANGLING-X64-DAG: @"\01?private_f@C@@EEAAXXZ"
|
||||
// MANGLING-X64-DAG: @"\01?private_f@C@@G7EAAXXZ"
|
||||
};
|
||||
|
||||
C::C() {} // Emits vftable and forces thunk generation.
|
||||
|
||||
// CODEGEN: define weak x86_thiscallcc void @"\01??_EC@@W3AEPAXI@Z"(%struct.C* %this, i32 %should_call_delete)
|
||||
// CODEGEN: getelementptr inbounds i8* {{.*}}, i64 -4
|
||||
// FIXME: should actually call _EC, not _GC.
|
||||
// CODEGEN: call x86_thiscallcc void @"\01??_GC@@UAEPAXI@Z"
|
||||
// CODEGEN: ret
|
||||
|
||||
// CODEGEN: define weak x86_thiscallcc void @"\01?public_f@C@@W3AEXXZ"(%struct.C*
|
||||
// CODEGEN: getelementptr inbounds i8* {{.*}}, i64 -4
|
||||
// CODEGEN: call x86_thiscallcc void @"\01?public_f@C@@UAEXXZ"(%struct.C*
|
||||
// CODEGEN: ret
|
||||
|
||||
void zoo(C* obj) {
|
||||
delete obj;
|
||||
}
|
||||
|
||||
struct D {
|
||||
virtual B* goo();
|
||||
};
|
||||
|
||||
struct E : D {
|
||||
virtual C* goo();
|
||||
// MANGLING-DAG: @"\01?goo@E@@UAEPAUC@@XZ"
|
||||
// MANGLING-DAG: @"\01?goo@E@@QAEPAUB@@XZ"
|
||||
// MANGLING-X64-DAG: @"\01?goo@E@@UEAAPEAUC@@XZ"
|
||||
// MANGLING-X64-DAG: @"\01?goo@E@@QEAAPEAUB@@XZ"
|
||||
};
|
||||
|
||||
E e; // Emits vftable and forces thunk generation.
|
||||
|
||||
// CODEGEN: define weak x86_thiscallcc %struct.C* @"\01?goo@E@@QAEPAUB@@XZ"
|
||||
// CODEGEN: call x86_thiscallcc %struct.C* @"\01?goo@E@@UAEPAUC@@XZ"
|
||||
// CODEGEN: getelementptr inbounds i8* {{.*}}, i64 4
|
||||
// CODEGEN: ret
|
||||
|
||||
struct F : virtual A, virtual B {
|
||||
virtual ~F();
|
||||
};
|
||||
|
||||
F f; // Just make sure we don't crash, e.g. mangling the complete dtor.
|
||||
|
||||
struct G : C { };
|
||||
|
||||
struct H : E {
|
||||
virtual G* goo();
|
||||
// MANGLING-DAG: @"\01?goo@H@@UAEPAUG@@XZ"
|
||||
// MANGLING-DAG: @"\01?goo@H@@QAEPAUB@@XZ"
|
||||
// MANGLING-DAG: @"\01?goo@H@@QAEPAUC@@XZ"
|
||||
// MANGLING-X64-DAG: @"\01?goo@H@@UEAAPEAUG@@XZ"
|
||||
// MANGLING-X64-DAG: @"\01?goo@H@@QEAAPEAUB@@XZ"
|
||||
// MANGLING-X64-DAG: @"\01?goo@H@@QEAAPEAUC@@XZ"
|
||||
};
|
||||
|
||||
H h;
|
||||
|
||||
// FIXME: Write vtordisp adjusting thunk tests
|
|
@ -93,3 +93,11 @@ void call_vbase_bar(B *obj) {
|
|||
//
|
||||
// CHECK: ret void
|
||||
}
|
||||
|
||||
struct C : B {
|
||||
C();
|
||||
// has an implicit vdtor.
|
||||
};
|
||||
|
||||
// Used to crash on an assertion.
|
||||
C::C() {}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
// RUN: FileCheck --check-prefix=RET-THUNKS-Test3 %s < %t
|
||||
// RUN: FileCheck --check-prefix=RET-THUNKS-Test4 %s < %t
|
||||
// RUN: FileCheck --check-prefix=RET-THUNKS-Test5 %s < %t
|
||||
// RUN: FileCheck --check-prefix=RET-THUNKS-Test6 %s < %t
|
||||
|
||||
// RUN: FileCheck --check-prefix=MANGLING %s < %t
|
||||
|
||||
|
@ -458,4 +459,22 @@ struct Test5 : Ret1, Test1 {
|
|||
};
|
||||
|
||||
Test5 t5;
|
||||
|
||||
struct Ret3 : this_adjustment::Test1 { };
|
||||
|
||||
struct Test6 : Test1 {
|
||||
virtual Ret3* foo();
|
||||
// RET-THUNKS-Test6: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test6' (4 entries).
|
||||
// RET-THUNKS-Test6-NEXT: 0 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
|
||||
// RET-THUNKS-Test6-NEXT: [return adjustment: 4 non-virtual]
|
||||
// RET-THUNKS-Test6-NEXT: 1 | void return_adjustment::Ret1::z()
|
||||
// RET-THUNKS-Test6-NEXT: 2 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
|
||||
// RET-THUNKS-Test6-NEXT: 3 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
|
||||
|
||||
// RET-THUNKS-Test6: VFTable indices for 'return_adjustment::Test6' (1 entries).
|
||||
// RET-THUNKS-Test6-NEXT: 3 | return_adjustment::Ret3 *return_adjustment::Test6::foo()
|
||||
};
|
||||
|
||||
Test6 t6;
|
||||
|
||||
}
|
||||
|
|
|
@ -154,6 +154,9 @@ struct X: virtual C {
|
|||
// TEST4-NOT: VFTable indices for 'Test4::X'
|
||||
|
||||
// MANGLING-DAG: @"\01??_7X@Test4@@6B@"
|
||||
|
||||
// Also check the mangling of the thunk.
|
||||
// MANGLING-DAG: define weak x86_thiscallcc void @"\01?f@C@@WPPPPPPPE@AEXXZ"
|
||||
};
|
||||
|
||||
X x;
|
||||
|
|
Loading…
Reference in New Issue