Add MicrosoftVFTableContext to AST

llvm-svn: 187409
This commit is contained in:
Timur Iskhodzhanov 2013-07-30 09:46:19 +00:00
parent f3e218a021
commit df7e7fb642
9 changed files with 2001 additions and 67 deletions

View File

@ -268,12 +268,38 @@ public:
}
};
class VTableContext {
class VTableContextBase {
public:
typedef SmallVector<std::pair<uint64_t, ThunkInfo>, 1>
VTableThunksTy;
typedef SmallVector<ThunkInfo, 1> ThunkInfoVectorTy;
protected:
typedef llvm::DenseMap<const CXXMethodDecl *, ThunkInfoVectorTy> ThunksMapTy;
/// \brief Contains all thunks that a given method decl will need.
ThunksMapTy Thunks;
/// Compute and store all vtable related information (vtable layout, vbase
/// offset offsets, thunks etc) for the given record decl.
virtual void computeVTableRelatedInformation(const CXXRecordDecl *RD) = 0;
virtual ~VTableContextBase() {}
public:
const ThunkInfoVectorTy *getThunkInfo(const CXXMethodDecl *MD) {
computeVTableRelatedInformation(MD->getParent());
ThunksMapTy::const_iterator I = Thunks.find(MD);
if (I == Thunks.end()) {
// We did not find a thunk for this method.
return 0;
}
return &I->second;
}
};
// FIXME: rename to ItaniumVTableContext.
class VTableContext : public VTableContextBase {
private:
bool IsMicrosoftABI;
@ -297,14 +323,7 @@ private:
VirtualBaseClassOffsetOffsetsMapTy;
VirtualBaseClassOffsetOffsetsMapTy VirtualBaseClassOffsetOffsets;
typedef llvm::DenseMap<const CXXMethodDecl *, ThunkInfoVectorTy> ThunksMapTy;
/// \brief Contains all thunks that a given method decl will need.
ThunksMapTy Thunks;
/// Compute and store all vtable related information (vtable layout, vbase
/// offset offsets, thunks etc) for the given record decl.
void ComputeVTableRelatedInformation(const CXXRecordDecl *RD);
void computeVTableRelatedInformation(const CXXRecordDecl *RD);
public:
VTableContext(ASTContext &Context);
@ -318,7 +337,7 @@ public:
}
const VTableLayout &getVTableLayout(const CXXRecordDecl *RD) {
ComputeVTableRelatedInformation(RD);
computeVTableRelatedInformation(RD);
assert(VTableLayouts.count(RD) && "No layout for this record decl!");
return *VTableLayouts[RD];
@ -330,18 +349,6 @@ public:
bool MostDerivedClassIsVirtual,
const CXXRecordDecl *LayoutClass);
const ThunkInfoVectorTy *getThunkInfo(const CXXMethodDecl *MD) {
ComputeVTableRelatedInformation(MD->getParent());
ThunksMapTy::const_iterator I = Thunks.find(MD);
if (I == Thunks.end()) {
// We did not find a thunk for this method.
return 0;
}
return &I->second;
}
/// \brief Locate a virtual function in the vtable.
///
/// Return the index (relative to the vtable address point) where the
@ -357,6 +364,118 @@ public:
const CXXRecordDecl *VBase);
};
/// \brief Computes the index of VBase in the vbtable of Derived.
/// VBase must be a morally virtual base of Derived. The vbtable is
/// an array of i32 offsets. The first entry is a self entry, and the rest are
/// offsets from the vbptr to virtual bases. The bases are ordered the same way
/// our vbases are ordered: as they appear in a left-to-right depth-first search
/// of the hierarchy.
// FIXME: make this a static method of VBTableBuilder when we move it to AST.
unsigned GetVBTableIndex(const CXXRecordDecl *Derived,
const CXXRecordDecl *VBase);
struct VFPtrInfo {
typedef SmallVector<const CXXRecordDecl *, 1> BasePath;
VFPtrInfo(CharUnits VFPtrOffset, const BasePath &PathToBaseWithVFPtr)
: VBTableIndex(0), LastVBase(0), VFPtrOffset(VFPtrOffset),
PathToBaseWithVFPtr(PathToBaseWithVFPtr), VFPtrFullOffset(VFPtrOffset) {
}
VFPtrInfo(uint64_t VBTableIndex, const CXXRecordDecl *LastVBase,
CharUnits VFPtrOffset, const BasePath &PathToBaseWithVFPtr,
CharUnits VFPtrFullOffset)
: VBTableIndex(VBTableIndex), LastVBase(LastVBase),
VFPtrOffset(VFPtrOffset), PathToBaseWithVFPtr(PathToBaseWithVFPtr),
VFPtrFullOffset(VFPtrFullOffset) {
assert(VBTableIndex && "The full constructor should only be used "
"for vfptrs in virtual bases");
assert(LastVBase);
}
/// If nonzero, holds the vbtable index of the virtual base with the vfptr.
uint64_t VBTableIndex;
/// Stores the last vbase on the path from the complete type to the vfptr.
const CXXRecordDecl *LastVBase;
/// This is the offset of the vfptr from the start of the last vbase,
/// or the complete type if there are no virtual bases.
CharUnits VFPtrOffset;
/// This holds the base classes path from the complete type to the first base
/// with the given vfptr offset.
BasePath PathToBaseWithVFPtr;
/// This is the full offset of the vfptr from the start of the complete type.
CharUnits VFPtrFullOffset;
};
class MicrosoftVFTableContext : public VTableContextBase {
public:
struct MethodVFTableLocation {
/// If nonzero, holds the vbtable index of the virtual base with the vfptr.
uint64_t VBTableIndex;
/// This is the offset of the vfptr from the start of the last vbase, or the
/// complete type if there are no virtual bases.
CharUnits VFTableOffset;
/// Method's index in the vftable.
uint64_t Index;
MethodVFTableLocation()
: VBTableIndex(0), VFTableOffset(CharUnits::Zero()), Index(0) {}
MethodVFTableLocation(uint64_t VBTableIndex, CharUnits VFTableOffset,
uint64_t Index)
: VBTableIndex(VBTableIndex), VFTableOffset(VFTableOffset),
Index(Index) {}
bool operator<(const MethodVFTableLocation &other) const {
if (VBTableIndex != other.VBTableIndex)
return VBTableIndex < other.VBTableIndex;
if (VFTableOffset != other.VFTableOffset)
return VFTableOffset < other.VFTableOffset;
return Index < other.Index;
}
};
typedef SmallVector<VFPtrInfo, 1> VFPtrListTy;
private:
ASTContext &Context;
typedef llvm::DenseMap<GlobalDecl, MethodVFTableLocation>
MethodVFTableLocationsTy;
MethodVFTableLocationsTy MethodVFTableLocations;
typedef llvm::DenseMap<const CXXRecordDecl *, VFPtrListTy>
VFPtrLocationsMapTy;
VFPtrLocationsMapTy VFPtrLocations;
typedef std::pair<const CXXRecordDecl *, CharUnits> VFTableIdTy;
typedef llvm::DenseMap<VFTableIdTy, const VTableLayout *> VFTableLayoutMapTy;
VFTableLayoutMapTy VFTableLayouts;
void computeVTableRelatedInformation(const CXXRecordDecl *RD);
void dumpMethodLocations(const CXXRecordDecl *RD,
const MethodVFTableLocationsTy &NewMethods,
raw_ostream &);
public:
MicrosoftVFTableContext(ASTContext &Context) : Context(Context) {}
~MicrosoftVFTableContext() { llvm::DeleteContainerSeconds(VFTableLayouts); }
const VFPtrListTy &getVFPtrOffsets(const CXXRecordDecl *RD);
const VTableLayout &getVFTableLayout(const CXXRecordDecl *RD,
CharUnits VFPtrOffset);
const MethodVFTableLocation &getMethodVFTableLocation(GlobalDecl GD);
};
}
#endif

View File

@ -89,6 +89,8 @@ public:
void mangleNumber(const llvm::APSInt &Value);
void mangleType(QualType T, SourceRange Range,
QualifierMangleMode QMM = QMM_Mangle);
void mangleFunctionType(const FunctionType *T, const FunctionDecl *D,
bool IsStructor, bool IsInstMethod);
private:
void disableBackReferences() { UseNameBackReferences = false; }
@ -122,8 +124,6 @@ private:
#undef TYPE
void mangleType(const TagType*);
void mangleFunctionType(const FunctionType *T, const FunctionDecl *D,
bool IsStructor, bool IsInstMethod);
void mangleDecayedArrayType(const ArrayType *T, bool IsGlobal);
void mangleArrayType(const ArrayType *T);
void mangleFunctionClass(const FunctionDecl *FD);
@ -1781,13 +1781,30 @@ void MicrosoftMangleContext::mangleName(const NamedDecl *D,
MicrosoftCXXNameMangler Mangler(*this, Out);
return Mangler.mangle(D);
}
void MicrosoftMangleContext::mangleThunk(const CXXMethodDecl *MD,
const ThunkInfo &Thunk,
raw_ostream &) {
unsigned DiagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error,
"cannot mangle thunk for this method yet");
getDiags().Report(MD->getLocation(), DiagID);
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, false, true);
}
void MicrosoftMangleContext::mangleCXXDtorThunk(const CXXDestructorDecl *DD,
CXXDtorType Type,
const ThisAdjustment &,

View File

@ -63,7 +63,7 @@ public:
/// Method - The method decl of the overrider.
const CXXMethodDecl *Method;
/// Offset - the base offset of the overrider in the layout class.
/// Offset - the base offset of the overrider's parent in the layout class.
CharUnits Offset;
OverriderInfo() : Method(0), Offset(CharUnits::Zero()) { }
@ -768,6 +768,7 @@ VCallAndVBaseOffsetBuilder::AddVBaseOffsets(const CXXRecordDecl *RD,
}
/// VTableBuilder - Class for building vtable layout information.
// FIXME: rename to ItaniumVTableBuilder.
class VTableBuilder {
public:
/// PrimaryBasesSetVectorTy - A set vector of direct and indirect
@ -1080,23 +1081,44 @@ void VTableBuilder::AddThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk) {
typedef llvm::SmallPtrSet<const CXXMethodDecl *, 8> OverriddenMethodsSetTy;
/// ComputeAllOverriddenMethods - Given a method decl, will return a set of all
/// the overridden methods that the function decl overrides.
static void
ComputeAllOverriddenMethods(const CXXMethodDecl *MD,
OverriddenMethodsSetTy& OverriddenMethods) {
/// Visit all the methods overridden by the given method recursively,
/// in a depth-first pre-order. The Visitor's visitor method returns a bool
/// indicating whether to continue the recursion for the given overridden
/// method (i.e. returning false stops the iteration).
template <class VisitorTy>
static void
visitAllOverriddenMethods(const CXXMethodDecl *MD, VisitorTy &Visitor) {
assert(MD->isVirtual() && "Method is not virtual!");
for (CXXMethodDecl::method_iterator I = MD->begin_overridden_methods(),
E = MD->end_overridden_methods(); I != E; ++I) {
const CXXMethodDecl *OverriddenMD = *I;
OverriddenMethods.insert(OverriddenMD);
ComputeAllOverriddenMethods(OverriddenMD, OverriddenMethods);
if (!Visitor.visit(OverriddenMD))
continue;
visitAllOverriddenMethods(OverriddenMD, Visitor);
}
}
namespace {
struct OverriddenMethodsCollector {
OverriddenMethodsSetTy *Methods;
bool visit(const CXXMethodDecl *MD) {
// Don't recurse on this method if we've already collected it.
return Methods->insert(MD);
}
};
}
/// ComputeAllOverriddenMethods - Given a method decl, will return a set of all
/// the overridden methods that the function decl overrides.
static void
ComputeAllOverriddenMethods(const CXXMethodDecl *MD,
OverriddenMethodsSetTy& OverriddenMethods) {
OverriddenMethodsCollector Collector = { &OverriddenMethods };
visitAllOverriddenMethods(MD, Collector);
}
void VTableBuilder::ComputeThisAdjustments() {
// Now go through the method info map and see if any of the methods need
// 'this' pointer adjustments.
@ -1135,7 +1157,7 @@ void VTableBuilder::ComputeThisAdjustments() {
// Add it.
VTableThunks[VTableIndex].This = ThisAdjustment;
if (isa<CXXDestructorDecl>(MD)) {
if (isa<CXXDestructorDecl>(MD) && !isMicrosoftABI()) {
// Add an adjustment for the deleting destructor as well.
VTableThunks[VTableIndex + 1].This = ThisAdjustment;
}
@ -1415,18 +1437,21 @@ VTableBuilder::IsOverriderUsed(const CXXMethodDecl *Overrider,
return OverridesIndirectMethodInBases(Overrider, PrimaryBases);
}
typedef llvm::SmallSetVector<const CXXRecordDecl *, 8> BasesSetVectorTy;
/// FindNearestOverriddenMethod - Given a method, returns the overridden method
/// from the nearest base. Returns null if no method was found.
static const CXXMethodDecl *
/// The Bases are expected to be sorted in a base-to-derived order.
static const CXXMethodDecl *
FindNearestOverriddenMethod(const CXXMethodDecl *MD,
VTableBuilder::PrimaryBasesSetVectorTy &Bases) {
BasesSetVectorTy &Bases) {
OverriddenMethodsSetTy OverriddenMethods;
ComputeAllOverriddenMethods(MD, OverriddenMethods);
for (int I = Bases.size(), E = 0; I != E; --I) {
const CXXRecordDecl *PrimaryBase = Bases[I - 1];
// Now check the overriden methods.
// Now check the overridden methods.
for (OverriddenMethodsSetTy::const_iterator I = OverriddenMethods.begin(),
E = OverriddenMethods.end(); I != E; ++I) {
const CXXMethodDecl *OverriddenMD = *I;
@ -2279,7 +2304,7 @@ uint64_t VTableContext::getMethodVTableIndex(GlobalDecl GD) {
const CXXRecordDecl *RD = cast<CXXMethodDecl>(GD.getDecl())->getParent();
ComputeVTableRelatedInformation(RD);
computeVTableRelatedInformation(RD);
I = MethodVTableIndices.find(GD);
assert(I != MethodVTableIndices.end() && "Did not find index!");
@ -2330,7 +2355,7 @@ static VTableLayout *CreateVTableLayout(const VTableBuilder &Builder) {
Builder.isMicrosoftABI());
}
void VTableContext::ComputeVTableRelatedInformation(const CXXRecordDecl *RD) {
void VTableContext::computeVTableRelatedInformation(const CXXRecordDecl *RD) {
const VTableLayout *&Entry = VTableLayouts[RD];
// Check if we've computed this information before.
@ -2378,3 +2403,868 @@ VTableLayout *VTableContext::createConstructionVTableLayout(
MostDerivedClassIsVirtual, LayoutClass);
return CreateVTableLayout(Builder);
}
unsigned clang::GetVBTableIndex(const CXXRecordDecl *Derived,
const CXXRecordDecl *VBase) {
unsigned VBTableIndex = 1; // Start with one to skip the self entry.
for (CXXRecordDecl::base_class_const_iterator I = Derived->vbases_begin(),
E = Derived->vbases_end(); I != E; ++I) {
if (I->getType()->getAsCXXRecordDecl() == VBase)
return VBTableIndex;
++VBTableIndex;
}
llvm_unreachable("VBase must be a vbase of Derived");
}
namespace {
// Vtables in the Microsoft ABI are different from the Itanium ABI.
//
// The main differences are:
// 1. Separate vftable and vbtable.
//
// 2. Each subobject with a vfptr gets its own vftable rather than an address
// point in a single vtable shared between all the subobjects.
// Each vftable is represented by a separate section and virtual calls
// must be done using the vftable which has a slot for the function to be
// called.
//
// 3. Virtual method definitions expect their 'this' parameter to point to the
// first vfptr whose table provides a compatible overridden method. In many
// cases, this permits the original vf-table entry to directly call
// the method instead of passing through a thunk.
//
// A compatible overridden method is one which does not have a non-trivial
// covariant-return adjustment.
//
// The first vfptr is the one with the lowest offset in the complete-object
// layout of the defining class, and the method definition will subtract
// that constant offset from the parameter value to get the real 'this'
// value. Therefore, if the offset isn't really constant (e.g. if a virtual
// function defined in a virtual base is overridden in a more derived
// virtual base and these bases have a reverse order in the complete
// object), the vf-table may require a this-adjustment thunk.
//
// 4. vftables do not contain new entries for overrides that merely require
// this-adjustment. Together with #3, this keeps vf-tables smaller and
// eliminates the need for this-adjustment thunks in many cases, at the cost
// of often requiring redundant work to adjust the "this" pointer.
//
// 5. Instead of VTT and constructor vtables, vbtables and vtordisps are used.
// Vtordisps are emitted into the class layout if a class has
// a) a user-defined ctor/dtor
// and
// b) a method overriding a method in a virtual base.
class VFTableBuilder {
public:
typedef MicrosoftVFTableContext::MethodVFTableLocation MethodVFTableLocation;
typedef llvm::DenseMap<GlobalDecl, MethodVFTableLocation>
MethodVFTableLocationsTy;
private:
/// Context - The ASTContext which we will use for layout information.
ASTContext &Context;
/// MostDerivedClass - The most derived class for which we're building this
/// vtable.
const CXXRecordDecl *MostDerivedClass;
const ASTRecordLayout &MostDerivedClassLayout;
VFPtrInfo WhichVFPtr;
/// FinalOverriders - The final overriders of the most derived class.
const FinalOverriders Overriders;
/// Components - The components of the vftable being built.
SmallVector<VTableComponent, 64> Components;
MethodVFTableLocationsTy MethodVFTableLocations;
/// MethodInfo - Contains information about a method in a vtable.
/// (Used for computing 'this' pointer adjustment thunks.
struct MethodInfo {
/// VBTableIndex - The nonzero index in the vbtable that
/// this method's base has, or zero.
const uint64_t VBTableIndex;
/// VFTableIndex - The index in the vftable that this method has.
const uint64_t VFTableIndex;
/// Shadowed - Indicates if this vftable slot is shadowed by
/// a slot for a covariant-return override. If so, it shouldn't be printed
/// or used for vcalls in the most derived class.
bool Shadowed;
MethodInfo(uint64_t VBTableIndex, uint64_t VFTableIndex)
: VBTableIndex(VBTableIndex), VFTableIndex(VFTableIndex),
Shadowed(false) {}
MethodInfo() : VBTableIndex(0), VFTableIndex(0), Shadowed(false) {}
};
typedef llvm::DenseMap<const CXXMethodDecl *, MethodInfo> MethodInfoMapTy;
/// MethodInfoMap - The information for all methods in the vftable we're
/// currently building.
MethodInfoMapTy MethodInfoMap;
typedef llvm::DenseMap<uint64_t, ThunkInfo> VTableThunksMapTy;
/// VTableThunks - The thunks by vftable index in the vftable currently being
/// built.
VTableThunksMapTy VTableThunks;
typedef SmallVector<ThunkInfo, 1> ThunkInfoVectorTy;
typedef llvm::DenseMap<const CXXMethodDecl *, ThunkInfoVectorTy> ThunksMapTy;
/// Thunks - A map that contains all the thunks needed for all methods in the
/// most derived class for which the vftable is currently being built.
ThunksMapTy Thunks;
/// AddThunk - Add a thunk for the given method.
void AddThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk) {
SmallVector<ThunkInfo, 1> &ThunksVector = Thunks[MD];
// Check if we have this thunk already.
if (std::find(ThunksVector.begin(), ThunksVector.end(), Thunk) !=
ThunksVector.end())
return;
ThunksVector.push_back(Thunk);
}
/// ComputeThisOffset - Returns the 'this' argument offset for the given
/// method in the given subobject, relative to the beginning of the
/// MostDerivedClass.
CharUnits ComputeThisOffset(const CXXMethodDecl *MD,
BaseSubobject Base,
FinalOverriders::OverriderInfo Overrider);
/// AddMethod - Add a single virtual member function to the vftable
/// components vector.
void AddMethod(const CXXMethodDecl *MD, ThisAdjustment ThisAdjustment,
ReturnAdjustment ReturnAdjustment) {
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
assert(ReturnAdjustment.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;
}
Components.push_back(VTableComponent::MakeFunction(MD));
}
}
/// AddMethods - Add the methods of this base subobject and the relevant
/// subbases to the vftable we're currently laying out.
void AddMethods(BaseSubobject Base, unsigned BaseDepth,
const CXXRecordDecl *LastVBase,
BasesSetVectorTy &VisitedBases);
void LayoutVFTable() {
// FIXME: add support for RTTI when we have proper LLVM support for symbols
// pointing to the middle of a section.
BasesSetVectorTy VisitedBases;
AddMethods(BaseSubobject(MostDerivedClass, CharUnits::Zero()), 0, 0,
VisitedBases);
assert(MethodVFTableLocations.empty());
for (MethodInfoMapTy::const_iterator I = MethodInfoMap.begin(),
E = MethodInfoMap.end(); I != E; ++I) {
const CXXMethodDecl *MD = I->first;
const MethodInfo &MI = I->second;
// Skip the methods that the MostDerivedClass didn't override
// and the entries shadowed by return adjusting thunks.
if (MD->getParent() != MostDerivedClass || MI.Shadowed)
continue;
MethodVFTableLocation Loc(MI.VBTableIndex, WhichVFPtr.VFPtrOffset,
MI.VFTableIndex);
if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(MD)) {
MethodVFTableLocations[GlobalDecl(DD, Dtor_Deleting)] = Loc;
} else {
MethodVFTableLocations[MD] = Loc;
}
}
}
void ErrorUnsupported(StringRef Feature, SourceLocation Location) {
clang::DiagnosticsEngine &Diags = Context.getDiagnostics();
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error, "v-table layout for %0 is not supported yet");
Diags.Report(Context.getFullLoc(Location), DiagID) << Feature;
}
public:
VFTableBuilder(const CXXRecordDecl *MostDerivedClass, VFPtrInfo Which)
: Context(MostDerivedClass->getASTContext()),
MostDerivedClass(MostDerivedClass),
MostDerivedClassLayout(Context.getASTRecordLayout(MostDerivedClass)),
WhichVFPtr(Which),
Overriders(MostDerivedClass, CharUnits(), MostDerivedClass) {
LayoutVFTable();
if (Context.getLangOpts().DumpVTableLayouts)
dumpLayout(llvm::errs());
}
uint64_t getNumThunks() const { return Thunks.size(); }
ThunksMapTy::const_iterator thunks_begin() const { return Thunks.begin(); }
ThunksMapTy::const_iterator thunks_end() const { return Thunks.end(); }
MethodVFTableLocationsTy::const_iterator vtable_indices_begin() const {
return MethodVFTableLocations.begin();
}
MethodVFTableLocationsTy::const_iterator vtable_indices_end() const {
return MethodVFTableLocations.end();
}
uint64_t getNumVTableComponents() const { return Components.size(); }
const VTableComponent *vtable_component_begin() const {
return Components.begin();
}
const VTableComponent *vtable_component_end() const {
return Components.end();
}
VTableThunksMapTy::const_iterator vtable_thunks_begin() const {
return VTableThunks.begin();
}
VTableThunksMapTy::const_iterator vtable_thunks_end() const {
return VTableThunks.end();
}
void dumpLayout(raw_ostream &);
};
/// InitialOverriddenDefinitionCollector - Finds the set of least derived bases
/// that define the given method.
struct InitialOverriddenDefinitionCollector {
BasesSetVectorTy Bases;
OverriddenMethodsSetTy VisitedOverriddenMethods;
bool visit(const CXXMethodDecl *OverriddenMD) {
if (OverriddenMD->size_overridden_methods() == 0)
Bases.insert(OverriddenMD->getParent());
// Don't recurse on this method if we've already collected it.
return VisitedOverriddenMethods.insert(OverriddenMD);
}
};
static bool BaseInSet(const CXXBaseSpecifier *Specifier,
CXXBasePath &Path, void *BasesSet) {
BasesSetVectorTy *Bases = (BasesSetVectorTy *)BasesSet;
return Bases->count(Specifier->getType()->getAsCXXRecordDecl());
}
CharUnits
VFTableBuilder::ComputeThisOffset(const CXXMethodDecl *MD,
BaseSubobject Base,
FinalOverriders::OverriderInfo Overrider) {
// Complete object virtual destructors are always emitted in the most derived
// class, thus don't have this offset.
if (isa<CXXDestructorDecl>(MD))
return CharUnits();
InitialOverriddenDefinitionCollector Collector;
visitAllOverriddenMethods(MD, Collector);
CXXBasePaths Paths;
Base.getBase()->lookupInBases(BaseInSet, &Collector.Bases, Paths);
// This will hold the smallest this offset among overridees of MD.
// This implies that an offset of a non-virtual base will dominate an offset
// of a virtual base to potentially reduce the number of thunks required
// in the derived classes that inherit this method.
CharUnits Ret;
bool First = true;
for (CXXBasePaths::paths_iterator I = Paths.begin(), E = Paths.end();
I != E; ++I) {
const CXXBasePath &Path = (*I);
CharUnits ThisOffset = Base.getBaseOffset();
// For each path from the overrider to the parents of the overridden methods,
// traverse the path, calculating the this offset in the most derived class.
for (int J = 0, F = Path.size(); J != F; ++J) {
const CXXBasePathElement &Element = Path[J];
QualType CurTy = Element.Base->getType();
const CXXRecordDecl *PrevRD = Element.Class,
*CurRD = CurTy->getAsCXXRecordDecl();
const ASTRecordLayout &Layout = Context.getASTRecordLayout(PrevRD);
if (Element.Base->isVirtual()) {
if (Overrider.Method->getParent() == PrevRD) {
// This one's interesting. If the final overrider is in a vbase B of the
// most derived class and it overrides a method of the B's own vbase A,
// it uses A* as "this". In its prologue, it can cast A* to B* with
// a static offset. This offset is used regardless of the actual
// offset of A from B in the most derived class, requiring an
// this-adjusting thunk in the vftable if A and B are laid out
// differently in the most derived class.
ThisOffset += Layout.getVBaseClassOffset(CurRD);
} else {
ThisOffset = MostDerivedClassLayout.getVBaseClassOffset(CurRD);
}
} else {
ThisOffset += Layout.getBaseClassOffset(CurRD);
}
}
if (Ret > ThisOffset || First) {
First = false;
Ret = ThisOffset;
}
}
assert(!First && "Method not found in the given subobject?");
return Ret;
}
static const CXXMethodDecl*
FindDirectlyOverriddenMethodInBases(const CXXMethodDecl *MD,
BasesSetVectorTy &Bases) {
// We can't just iterate over the overridden methods and return the first one
// which has its parent in Bases, e.g. this doesn't work when we have
// multiple subobjects of the same type that have its virtual function
// overridden.
for (int I = Bases.size(), E = 0; I != E; --I) {
const CXXRecordDecl *CurrentBase = Bases[I - 1];
for (CXXMethodDecl::method_iterator I = MD->begin_overridden_methods(),
E = MD->end_overridden_methods(); I != E; ++I) {
const CXXMethodDecl *OverriddenMD = *I;
if (OverriddenMD->getParent() == CurrentBase)
return OverriddenMD;
}
}
return 0;
}
void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
const CXXRecordDecl *LastVBase,
BasesSetVectorTy &VisitedBases) {
const CXXRecordDecl *RD = Base.getBase();
if (!RD->isPolymorphic())
return;
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
// See if this class expands a vftable of the base we look at, which is either
// the one defined by the vfptr base path or the primary base of the current class.
const CXXRecordDecl *NextBase = 0, *NextLastVBase = LastVBase;
CharUnits NextBaseOffset;
if (BaseDepth < WhichVFPtr.PathToBaseWithVFPtr.size()) {
NextBase = WhichVFPtr.PathToBaseWithVFPtr[BaseDepth];
if (Layout.getVBaseOffsetsMap().count(NextBase)) {
NextLastVBase = NextBase;
NextBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(NextBase);
} else {
NextBaseOffset =
Base.getBaseOffset() + Layout.getBaseClassOffset(NextBase);
}
} else if (const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase()) {
assert(!Layout.isPrimaryBaseVirtual() &&
"No primary virtual bases in this ABI");
NextBase = PrimaryBase;
NextBaseOffset = Base.getBaseOffset();
}
if (NextBase) {
AddMethods(BaseSubobject(NextBase, NextBaseOffset), BaseDepth + 1,
NextLastVBase, VisitedBases);
if (!VisitedBases.insert(NextBase))
llvm_unreachable("Found a duplicate primary base!");
}
// Now go through all virtual member functions and add them to the current
// vftable. This is done by
// - replacing overridden methods in their existing slots, as long as they
// don't require return adjustment; calculating This adjustment if needed.
// - adding new slots for methods of the current base not present in any
// sub-bases;
// - adding new slots for methods that require Return adjustment.
// We keep track of the methods visited in the sub-bases in MethodInfoMap.
for (CXXRecordDecl::method_iterator I = RD->method_begin(),
E = RD->method_end(); I != E; ++I) {
const CXXMethodDecl *MD = *I;
if (!MD->isVirtual())
continue;
FinalOverriders::OverriderInfo Overrider =
Overriders.getOverrider(MD, Base.getBaseOffset());
ThisAdjustment ThisAdjustmentOffset;
// Check if this virtual member function overrides
// a method in one of the visited bases.
if (const CXXMethodDecl *OverriddenMD =
FindDirectlyOverriddenMethodInBases(MD, VisitedBases)) {
MethodInfoMapTy::iterator OverriddenMDIterator =
MethodInfoMap.find(OverriddenMD);
// If the overridden method went to a different vftable, skip it.
if (OverriddenMDIterator == MethodInfoMap.end())
continue;
MethodInfo &OverriddenMethodInfo = OverriddenMDIterator->second;
// Create a this-adjusting thunk if needed.
CharUnits TI = ComputeThisOffset(MD, Base, Overrider);
if (TI != WhichVFPtr.VFPtrFullOffset) {
ThisAdjustmentOffset.NonVirtual =
(TI - WhichVFPtr.VFPtrFullOffset).getQuantity();
VTableThunks[OverriddenMethodInfo.VFTableIndex].This =
ThisAdjustmentOffset;
AddThunk(MD, VTableThunks[OverriddenMethodInfo.VFTableIndex]);
}
if (ComputeReturnAdjustmentBaseOffset(Context, MD, OverriddenMD)
.isEmpty()) {
// No return adjustment needed - just replace the overridden method info
// with the current info.
MethodInfo MI(OverriddenMethodInfo.VBTableIndex,
OverriddenMethodInfo.VFTableIndex);
MethodInfoMap.erase(OverriddenMDIterator);
assert(!MethodInfoMap.count(MD) &&
"Should not have method info for this method yet!");
MethodInfoMap.insert(std::make_pair(MD, MI));
continue;
} else {
// In case we need a return adjustment, we'll add a new slot for
// the overrider and put a return-adjusting thunk where the overridden
// method was in the vftable.
// For now, just mark the overriden method as shadowed by a new slot.
OverriddenMethodInfo.Shadowed = true;
// Also apply this adjustment to the shadowed slots.
if (!ThisAdjustmentOffset.isEmpty()) {
// FIXME: this is O(N^2), can be O(N).
const CXXMethodDecl *SubOverride = OverriddenMD;
while ((SubOverride =
FindDirectlyOverriddenMethodInBases(SubOverride, VisitedBases))) {
MethodInfoMapTy::iterator SubOverrideIterator =
MethodInfoMap.find(SubOverride);
if (SubOverrideIterator == MethodInfoMap.end())
break;
MethodInfo &SubOverrideMI = SubOverrideIterator->second;
assert(SubOverrideMI.Shadowed);
VTableThunks[SubOverrideMI.VFTableIndex].This =
ThisAdjustmentOffset;
AddThunk(MD, VTableThunks[SubOverrideMI.VFTableIndex]);
}
}
}
} else if (Base.getBaseOffset() != WhichVFPtr.VFPtrFullOffset ||
MD->size_overridden_methods()) {
// Skip methods that don't belong to the vftable of the current class,
// e.g. each method that wasn't seen in any of the visited sub-bases
// but overrides multiple methods of other sub-bases.
continue;
}
// If we got here, MD is a method not seen in any of the sub-bases or
// it requires return adjustment. Insert the method info for this method.
unsigned VBIndex =
LastVBase ? GetVBTableIndex(MostDerivedClass, LastVBase) : 0;
MethodInfo MI(VBIndex, Components.size());
assert(!MethodInfoMap.count(MD) &&
"Should not have method info for this method yet!");
MethodInfoMap.insert(std::make_pair(MD, MI));
const CXXMethodDecl *OverriderMD = Overrider.Method;
// Check if this overrider needs a return adjustment.
// We don't want to do this for pure virtual member functions.
BaseOffset ReturnAdjustmentOffset;
ReturnAdjustment ReturnAdjustment;
if (!OverriderMD->isPure()) {
ReturnAdjustmentOffset =
ComputeReturnAdjustmentBaseOffset(Context, OverriderMD, MD);
}
if (!ReturnAdjustmentOffset.isEmpty()) {
ReturnAdjustment.NonVirtual =
ReturnAdjustmentOffset.NonVirtualOffset.getQuantity();
if (ReturnAdjustmentOffset.VirtualBase) {
// FIXME: We might want to create a VBIndex alias for VBaseOffsetOffset
// in the ReturnAdjustment struct.
ReturnAdjustment.VBaseOffsetOffset =
GetVBTableIndex(ReturnAdjustmentOffset.DerivedClass,
ReturnAdjustmentOffset.VirtualBase);
}
}
AddMethod(Overrider.Method, ThisAdjustmentOffset, ReturnAdjustment);
}
}
void PrintBasePath(const VFPtrInfo::BasePath &Path, raw_ostream &Out) {
for (VFPtrInfo::BasePath::const_reverse_iterator I = Path.rbegin(),
E = Path.rend(); I != E; ++I) {
Out << "'" << (*I)->getQualifiedNameAsString() << "' in ";
}
}
void VFTableBuilder::dumpLayout(raw_ostream &Out) {
Out << "VFTable for ";
PrintBasePath(WhichVFPtr.PathToBaseWithVFPtr, Out);
Out << "'" << MostDerivedClass->getQualifiedNameAsString();
Out << "' (" << Components.size() << " entries).\n";
for (unsigned I = 0, E = Components.size(); I != E; ++I) {
Out << llvm::format("%4d | ", I);
const VTableComponent &Component = Components[I];
// Dump the component.
switch (Component.getKind()) {
case VTableComponent::CK_RTTI:
Out << Component.getRTTIDecl()->getQualifiedNameAsString() << " RTTI";
break;
case VTableComponent::CK_FunctionPointer: {
const CXXMethodDecl *MD = Component.getFunctionDecl();
std::string Str = PredefinedExpr::ComputeName(
PredefinedExpr::PrettyFunctionNoVirtual, MD);
Out << Str;
if (MD->isPure())
Out << " [pure]";
if (MD->isDeleted()) {
ErrorUnsupported("deleted methods", MD->getLocation());
Out << " [deleted]";
}
ThunkInfo Thunk = VTableThunks.lookup(I);
if (!Thunk.isEmpty()) {
// If this function pointer has a return adjustment, dump it.
if (!Thunk.Return.isEmpty()) {
Out << "\n [return adjustment: ";
if (Thunk.Return.VBaseOffsetOffset)
Out << "vbase #" << Thunk.Return.VBaseOffsetOffset << ", ";
Out << Thunk.Return.NonVirtual << " non-virtual]";
}
// If this function pointer has a 'this' pointer adjustment, dump it.
if (!Thunk.This.isEmpty()) {
assert(!Thunk.This.VCallOffsetOffset &&
"No virtual this adjustment in this ABI");
Out << "\n [this adjustment: " << Thunk.This.NonVirtual
<< " non-virtual]";
}
}
break;
}
case VTableComponent::CK_DeletingDtorPointer: {
const CXXDestructorDecl *DD = Component.getDestructorDecl();
Out << DD->getQualifiedNameAsString();
Out << "() [scalar deleting]";
if (DD->isPure())
Out << " [pure]";
ThunkInfo Thunk = VTableThunks.lookup(I);
if (!Thunk.isEmpty()) {
assert(Thunk.Return.isEmpty() &&
"No return adjustment needed for destructors!");
// If this destructor has a 'this' pointer adjustment, dump it.
if (!Thunk.This.isEmpty()) {
assert(!Thunk.This.VCallOffsetOffset &&
"No virtual this adjustment in this ABI");
Out << "\n [this adjustment: " << Thunk.This.NonVirtual
<< " non-virtual]";
}
}
break;
}
default:
DiagnosticsEngine &Diags = Context.getDiagnostics();
unsigned DiagID = Diags.getCustomDiagID(
DiagnosticsEngine::Error,
"Unexpected vftable component type %0 for component number %1");
Diags.Report(MostDerivedClass->getLocation(), DiagID)
<< I << Component.getKind();
}
Out << '\n';
}
Out << '\n';
if (!Thunks.empty()) {
// We store the method names in a map to get a stable order.
std::map<std::string, const CXXMethodDecl *> MethodNamesAndDecls;
for (ThunksMapTy::const_iterator I = Thunks.begin(), E = Thunks.end();
I != E; ++I) {
const CXXMethodDecl *MD = I->first;
std::string MethodName = PredefinedExpr::ComputeName(
PredefinedExpr::PrettyFunctionNoVirtual, MD);
MethodNamesAndDecls.insert(std::make_pair(MethodName, MD));
}
for (std::map<std::string, const CXXMethodDecl *>::const_iterator
I = MethodNamesAndDecls.begin(),
E = MethodNamesAndDecls.end();
I != E; ++I) {
const std::string &MethodName = I->first;
const CXXMethodDecl *MD = I->second;
ThunkInfoVectorTy ThunksVector = Thunks[MD];
std::sort(ThunksVector.begin(), ThunksVector.end());
Out << "Thunks for '" << MethodName << "' (" << ThunksVector.size();
Out << (ThunksVector.size() == 1 ? " entry" : " entries") << ").\n";
for (unsigned I = 0, E = ThunksVector.size(); I != E; ++I) {
const ThunkInfo &Thunk = ThunksVector[I];
Out << llvm::format("%4d | ", I);
// If this function pointer has a return pointer adjustment, dump it.
if (!Thunk.Return.isEmpty()) {
Out << "return adjustment: ";
if (Thunk.Return.VBaseOffsetOffset)
Out << "vbase #" << Thunk.Return.VBaseOffsetOffset << ", ";
Out << Thunk.Return.NonVirtual << " non-virtual";
if (!Thunk.This.isEmpty())
Out << "\n ";
}
// If this function pointer has a 'this' pointer adjustment, dump it.
if (!Thunk.This.isEmpty()) {
assert(!Thunk.This.VCallOffsetOffset &&
"No virtual this adjustment in this ABI");
Out << "this adjustment: ";
Out << Thunk.This.NonVirtual << " non-virtual";
}
Out << '\n';
}
Out << '\n';
}
}
}
}
static void EnumerateVFPtrs(
ASTContext &Context, const CXXRecordDecl *MostDerivedClass,
const ASTRecordLayout &MostDerivedClassLayout,
BaseSubobject Base, const CXXRecordDecl *LastVBase,
const VFPtrInfo::BasePath &PathFromCompleteClass,
BasesSetVectorTy &VisitedVBases,
MicrosoftVFTableContext::VFPtrListTy &Result) {
const CXXRecordDecl *CurrentClass = Base.getBase();
CharUnits OffsetInCompleteClass = Base.getBaseOffset();
const ASTRecordLayout &CurrentClassLayout =
Context.getASTRecordLayout(CurrentClass);
if (CurrentClassLayout.hasOwnVFPtr()) {
if (LastVBase) {
uint64_t VBIndex = GetVBTableIndex(MostDerivedClass, LastVBase);
assert(VBIndex > 0 && "vbases must have vbindex!");
CharUnits VFPtrOffset =
OffsetInCompleteClass -
MostDerivedClassLayout.getVBaseClassOffset(LastVBase);
Result.push_back(VFPtrInfo(VBIndex, LastVBase, VFPtrOffset,
PathFromCompleteClass, OffsetInCompleteClass));
} else {
Result.push_back(VFPtrInfo(OffsetInCompleteClass, PathFromCompleteClass));
}
}
for (CXXRecordDecl::base_class_const_iterator I = CurrentClass->bases_begin(),
E = CurrentClass->bases_end(); I != E; ++I) {
const CXXRecordDecl *BaseDecl = I->getType()->getAsCXXRecordDecl();
CharUnits NextBaseOffset;
const CXXRecordDecl *NextLastVBase;
if (I->isVirtual()) {
if (VisitedVBases.count(BaseDecl))
continue;
VisitedVBases.insert(BaseDecl);
NextBaseOffset = MostDerivedClassLayout.getVBaseClassOffset(BaseDecl);
NextLastVBase = BaseDecl;
} else {
NextBaseOffset = OffsetInCompleteClass +
CurrentClassLayout.getBaseClassOffset(BaseDecl);
NextLastVBase = LastVBase;
}
VFPtrInfo::BasePath NewPath = PathFromCompleteClass;
NewPath.push_back(BaseDecl);
BaseSubobject NextBase(BaseDecl, NextBaseOffset);
EnumerateVFPtrs(Context, MostDerivedClass, MostDerivedClassLayout, NextBase,
NextLastVBase, NewPath, VisitedVBases, Result);
}
}
void EnumerateVFPtrs(ASTContext &Context, const CXXRecordDecl *ForClass,
MicrosoftVFTableContext::VFPtrListTy &Result) {
Result.clear();
const ASTRecordLayout &ClassLayout = Context.getASTRecordLayout(ForClass);
BasesSetVectorTy VisitedVBases;
EnumerateVFPtrs(Context, ForClass, ClassLayout,
BaseSubobject(ForClass, CharUnits::Zero()), 0,
VFPtrInfo::BasePath(), VisitedVBases, Result);
}
void MicrosoftVFTableContext::computeVTableRelatedInformation(
const CXXRecordDecl *RD) {
assert(RD->isDynamicClass());
// Check if we've computed this information before.
if (VFPtrLocations.count(RD))
return;
const VTableLayout::AddressPointsMapTy EmptyAddressPointsMap;
VFPtrListTy &VFPtrs = VFPtrLocations[RD];
EnumerateVFPtrs(Context, RD, VFPtrs);
MethodVFTableLocationsTy NewMethodLocations;
for (VFPtrListTy::iterator I = VFPtrs.begin(), E = VFPtrs.end();
I != E; ++I) {
VFTableBuilder Builder(RD, *I);
VFTableIdTy id(RD, I->VFPtrFullOffset);
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);
NewMethodLocations.insert(Builder.vtable_indices_begin(),
Builder.vtable_indices_end());
Thunks.insert(Builder.thunks_begin(), Builder.thunks_end());
}
MethodVFTableLocations.insert(NewMethodLocations.begin(),
NewMethodLocations.end());
if (Context.getLangOpts().DumpVTableLayouts)
dumpMethodLocations(RD, NewMethodLocations, llvm::errs());
}
void MicrosoftVFTableContext::dumpMethodLocations(
const CXXRecordDecl *RD, const MethodVFTableLocationsTy &NewMethods,
raw_ostream &Out) {
// Compute the vtable indices for all the member functions.
// Store them in a map keyed by the location so we'll get a sorted table.
std::map<MethodVFTableLocation, std::string> IndicesMap;
bool HasNonzeroOffset = false;
for (MethodVFTableLocationsTy::const_iterator I = NewMethods.begin(),
E = NewMethods.end(); I != E; ++I) {
const CXXMethodDecl *MD = cast<const CXXMethodDecl>(I->first.getDecl());
assert(MD->isVirtual());
std::string MethodName = PredefinedExpr::ComputeName(
PredefinedExpr::PrettyFunctionNoVirtual, MD);
if (isa<CXXDestructorDecl>(MD)) {
IndicesMap[I->second] = MethodName + " [scalar deleting]";
} else {
IndicesMap[I->second] = MethodName;
}
if (!I->second.VFTableOffset.isZero() || I->second.VBTableIndex != 0)
HasNonzeroOffset = true;
}
// Print the vtable indices for all the member functions.
if (!IndicesMap.empty()) {
Out << "VFTable indices for ";
Out << "'" << RD->getQualifiedNameAsString();
Out << "' (" << IndicesMap.size() << " entries).\n";
CharUnits LastVFPtrOffset = CharUnits::fromQuantity(-1);
uint64_t LastVBIndex = 0;
for (std::map<MethodVFTableLocation, std::string>::const_iterator
I = IndicesMap.begin(),
E = IndicesMap.end();
I != E; ++I) {
CharUnits VFPtrOffset = I->first.VFTableOffset;
uint64_t VBIndex = I->first.VBTableIndex;
if (HasNonzeroOffset &&
(VFPtrOffset != LastVFPtrOffset || VBIndex != LastVBIndex)) {
assert(VBIndex > LastVBIndex || VFPtrOffset > LastVFPtrOffset);
Out << " -- accessible via ";
if (VBIndex)
Out << "vbtable index " << VBIndex << ", ";
Out << "vfptr at offset " << VFPtrOffset.getQuantity() << " --\n";
LastVFPtrOffset = VFPtrOffset;
LastVBIndex = VBIndex;
}
uint64_t VTableIndex = I->first.Index;
const std::string &MethodName = I->second;
Out << llvm::format("%4" PRIu64 " | ", VTableIndex) << MethodName << '\n';
}
Out << '\n';
}
}
const MicrosoftVFTableContext::VFPtrListTy &
MicrosoftVFTableContext::getVFPtrOffsets(const CXXRecordDecl *RD) {
computeVTableRelatedInformation(RD);
assert(VFPtrLocations.count(RD) && "Couldn't find vfptr locations");
return VFPtrLocations[RD];
}
const VTableLayout &
MicrosoftVFTableContext::getVFTableLayout(const CXXRecordDecl *RD,
CharUnits VFPtrOffset) {
computeVTableRelatedInformation(RD);
VFTableIdTy id(RD, VFPtrOffset);
assert(VFTableLayouts.count(id) && "Couldn't find a VFTable at this offset");
return *VFTableLayouts[id];
}
const MicrosoftVFTableContext::MethodVFTableLocation &
MicrosoftVFTableContext::getMethodVFTableLocation(GlobalDecl GD) {
assert(cast<CXXMethodDecl>(GD.getDecl())->isVirtual() &&
"Only use this method for virtual methods or dtors");
if (isa<CXXDestructorDecl>(GD.getDecl()))
assert(GD.getDtorType() == Dtor_Deleting);
MethodVFTableLocationsTy::iterator I = MethodVFTableLocations.find(GD);
if (I != MethodVFTableLocations.end())
return I->second;
const CXXRecordDecl *RD = cast<CXXMethodDecl>(GD.getDecl())->getParent();
computeVTableRelatedInformation(RD);
I = MethodVFTableLocations.find(GD);
assert(I != MethodVFTableLocations.end() && "Did not find index!");
return I->second;
}

View File

@ -29,7 +29,14 @@ using namespace clang;
using namespace CodeGen;
CodeGenVTables::CodeGenVTables(CodeGenModule &CGM)
: CGM(CGM), VTContext(CGM.getContext()) { }
: CGM(CGM), VTContext(CGM.getContext()) {
if (CGM.getTarget().getCXXABI().isMicrosoft()) {
// FIXME: Eventually, we should only have one of V*TContexts available.
// Today we use both in the Microsoft ABI as MicrosoftVFTableContext
// is not completely supported in CodeGen yet.
VFTContext.reset(new MicrosoftVFTableContext(CGM.getContext()));
}
}
llvm::Constant *CodeGenModule::GetAddrOfThunk(GlobalDecl GD,
const ThunkInfo &Thunk) {
@ -389,6 +396,11 @@ void CodeGenFunction::GenerateThunk(llvm::Function *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;
}
const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeGlobalDeclaration(GD);
// FIXME: re-use FnInfo in this computation.
@ -485,6 +497,12 @@ void CodeGenVTables::EmitThunks(GlobalDecl GD)
if (isa<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Base)
return;
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());
}
const VTableContext::ThunkInfoVectorTy *ThunkInfoVector =
VTContext.getThunkInfo(MD);
if (!ThunkInfoVector)
@ -804,6 +822,12 @@ void CodeGenModule::EmitVTable(CXXRecordDecl *theClass, bool isRequired) {
void
CodeGenVTables::GenerateClassData(const CXXRecordDecl *RD) {
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(RD);
}
// First off, check whether we've already emitted the v-table and
// associated stuff.
llvm::GlobalVariable *VTable = GetAddrOfVTable(RD);

View File

@ -32,6 +32,7 @@ class CodeGenVTables {
CodeGenModule &CGM;
VTableContext VTContext;
OwningPtr<MicrosoftVFTableContext> VFTContext;
/// VTables - All the vtables which have been defined.
llvm::DenseMap<const CXXRecordDecl *, llvm::GlobalVariable *> VTables;

View File

@ -20,6 +20,7 @@
#include "MicrosoftVBTables.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/VTableBuilder.h"
using namespace clang;
using namespace CodeGen;
@ -315,24 +316,6 @@ CharUnits MicrosoftCXXABI::GetVBPtrOffsetFromBases(const CXXRecordDecl *RD) {
return Total;
}
/// \brief Computes the index of BaseClassDecl in the vbtable of ClassDecl.
/// BaseClassDecl must be a morally virtual base of ClassDecl. The vbtable is
/// an array of i32 offsets. The first entry is a self entry, and the rest are
/// offsets from the vbptr to virtual bases. The bases are ordered the same way
/// our vbases are ordered: as they appear in a left-to-right depth-first search
/// of the hierarchy.
static unsigned GetVBTableIndex(const CXXRecordDecl *ClassDecl,
const CXXRecordDecl *BaseClassDecl) {
unsigned VBTableIndex = 1; // Start with one to skip the self entry.
for (CXXRecordDecl::base_class_const_iterator I = ClassDecl->vbases_begin(),
E = ClassDecl->vbases_end(); I != E; ++I) {
if (I->getType()->getAsCXXRecordDecl() == BaseClassDecl)
return VBTableIndex;
VBTableIndex++;
}
llvm_unreachable("BaseClassDecl must be a vbase of ClassDecl");
}
llvm::Value *
MicrosoftCXXABI::GetVirtualBaseClassOffset(CodeGenFunction &CGF,
llvm::Value *This,

View File

@ -0,0 +1,431 @@
// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=i386-pc-win32 -emit-llvm -fdump-vtable-layouts -o - >%t 2>&1
// RUN: FileCheck --check-prefix=NO-THUNKS-Test1 %s < %t
// RUN: FileCheck --check-prefix=NO-THUNKS-Test2 %s < %t
// RUN: FileCheck --check-prefix=NO-THUNKS-Test3 %s < %t
// RUN: FileCheck --check-prefix=NO-THUNKS-Test4 %s < %t
// RUN: FileCheck --check-prefix=NO-THUNKS-Test5 %s < %t
// RUN: FileCheck --check-prefix=NO-THUNKS-Test6 %s < %t
// RUN: FileCheck --check-prefix=NO-THUNKS-Test7 %s < %t
// RUN: FileCheck --check-prefix=NO-THUNKS-Test8 %s < %t
// RUN: FileCheck --check-prefix=NO-THUNKS-Test9 %s < %t
// RUN: FileCheck --check-prefix=PURE-VIRTUAL-Test1 %s < %t
// RUN: FileCheck --check-prefix=THIS-THUNKS-Test1 %s < %t
// RUN: FileCheck --check-prefix=THIS-THUNKS-Test2 %s < %t
// RUN: FileCheck --check-prefix=THIS-THUNKS-Test3 %s < %t
// RUN: FileCheck --check-prefix=RET-THUNKS-Test1 %s < %t
// RUN: FileCheck --check-prefix=RET-THUNKS-Test2 %s < %t
// 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
struct Empty {
// Doesn't have a vftable!
};
struct A {
virtual void f();
};
struct B {
virtual void g();
// Add an extra virtual method so it's easier to check for the absence of thunks.
virtual void h();
};
struct C {
virtual void g(); // Might "collide" with B::g if both are bases of some class.
};
namespace no_thunks {
struct Test1: A, B {
// NO-THUNKS-Test1: VFTable for 'A' in 'no_thunks::Test1' (1 entries)
// NO-THUNKS-Test1-NEXT: 0 | void no_thunks::Test1::f()
// NO-THUNKS-Test1: VFTable for 'B' in 'no_thunks::Test1' (2 entries)
// NO-THUNKS-Test1-NEXT: 0 | void B::g()
// NO-THUNKS-Test1-NEXT: 1 | void B::h()
// NO-THUNKS-Test1: VFTable indices for 'no_thunks::Test1' (1 entries)
// NO-THUNKS-Test1-NEXT: 0 | void no_thunks::Test1::f()
// Overrides only the left child's method (A::f), needs no thunks.
virtual void f();
};
Test1 t1;
struct Test2: A, B {
// NO-THUNKS-Test2: VFTable for 'A' in 'no_thunks::Test2' (1 entries)
// NO-THUNKS-Test2-NEXT: 0 | void A::f()
// NO-THUNKS-Test2: VFTable for 'B' in 'no_thunks::Test2' (2 entries)
// NO-THUNKS-Test2-NEXT: 0 | void no_thunks::Test2::g()
// NO-THUNKS-Test2-NEXT: 1 | void B::h()
// NO-THUNKS-Test2: VFTable indices for 'no_thunks::Test2' (1 entries).
// NO-THUNKS-Test2-NEXT: via vfptr at offset 4
// NO-THUNKS-Test2-NEXT: 0 | void no_thunks::Test2::g()
// Overrides only the right child's method (B::g), needs this adjustment but
// not thunks.
virtual void g();
};
Test2 t2;
struct Test3: A, B {
// NO-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test3' (2 entries)
// NO-THUNKS-Test3-NEXT: 0 | void A::f()
// NO-THUNKS-Test3-NEXT: 1 | void no_thunks::Test3::i()
// NO-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test3' (2 entries)
// NO-THUNKS-Test3-NEXT: 0 | void B::g()
// NO-THUNKS-Test3-NEXT: 1 | void B::h()
// NO-THUNKS-Test3: VFTable indices for 'no_thunks::Test3' (1 entries).
// NO-THUNKS-Test3-NEXT: 1 | void no_thunks::Test3::i()
// Only adds a new method.
virtual void i();
};
Test3 t3;
// Only the right base has a vftable, so it's laid out before the left one!
struct Test4 : Empty, A {
// NO-THUNKS-Test4: VFTable for 'A' in 'no_thunks::Test4' (1 entries)
// NO-THUNKS-Test4-NEXT: 0 | void no_thunks::Test4::f()
// NO-THUNKS-Test4: VFTable indices for 'no_thunks::Test4' (1 entries).
// NO-THUNKS-Test4-NEXT: 0 | void no_thunks::Test4::f()
virtual void f();
};
Test4 t4;
// 2-level structure with repeating subobject types, but no thunks needed.
struct Test5: Test1, Test2 {
// NO-THUNKS-Test5: VFTable for 'A' in 'no_thunks::Test1' in 'no_thunks::Test5' (2 entries)
// NO-THUNKS-Test5-NEXT: 0 | void no_thunks::Test1::f()
// NO-THUNKS-Test5-NEXT: 1 | void no_thunks::Test5::z()
// NO-THUNKS-Test5: VFTable for 'B' in 'no_thunks::Test1' in 'no_thunks::Test5' (2 entries)
// NO-THUNKS-Test5-NEXT: 0 | void B::g()
// NO-THUNKS-Test5-NEXT: 1 | void B::h()
// NO-THUNKS-Test5: VFTable for 'A' in 'no_thunks::Test2' in 'no_thunks::Test5' (1 entries)
// NO-THUNKS-Test5-NEXT: 0 | void A::f()
// NO-THUNKS-Test5: VFTable for 'B' in 'no_thunks::Test2' in 'no_thunks::Test5' (2 entries)
// NO-THUNKS-Test5-NEXT: 0 | void no_thunks::Test2::g()
// NO-THUNKS-Test5-NEXT: 1 | void B::h()
// NO-THUNKS-Test5: VFTable indices for 'no_thunks::Test5' (1 entries).
// NO-THUNKS-Test5-NEXT: 1 | void no_thunks::Test5::z()
virtual void z();
};
Test5 t5;
struct Test6: Test1 {
// NO-THUNKS-Test6: VFTable for 'A' in 'no_thunks::Test1' in 'no_thunks::Test6' (1 entries).
// NO-THUNKS-Test6-NEXT: 0 | void no_thunks::Test6::f()
// NO-THUNKS-Test6: VFTable for 'B' in 'no_thunks::Test1' in 'no_thunks::Test6' (2 entries).
// NO-THUNKS-Test6-NEXT: 0 | void B::g()
// NO-THUNKS-Test6-NEXT: 1 | void B::h()
// NO-THUNKS-Test6: VFTable indices for 'no_thunks::Test6' (1 entries).
// NO-THUNKS-Test6-NEXT: 0 | void no_thunks::Test6::f()
// Overrides both no_thunks::Test1::f and A::f.
virtual void f();
};
Test6 t6;
struct Test7: Test2 {
// NO-THUNKS-Test7: VFTable for 'A' in 'no_thunks::Test2' in 'no_thunks::Test7' (1 entries).
// NO-THUNKS-Test7-NEXT: 0 | void A::f()
// NO-THUNKS-Test7: VFTable for 'B' in 'no_thunks::Test2' in 'no_thunks::Test7' (2 entries).
// NO-THUNKS-Test7-NEXT: 0 | void no_thunks::Test7::g()
// NO-THUNKS-Test7-NEXT: 1 | void B::h()
// NO-THUNKS-Test7: VFTable indices for 'no_thunks::Test7' (1 entries).
// NO-THUNKS-Test7-NEXT: via vfptr at offset 4
// NO-THUNKS-Test7-NEXT: 0 | void no_thunks::Test7::g()
// Overrides both no_thunks::Test2::g and B::g.
virtual void g();
};
Test7 t7;
struct Test8: Test3 {
// NO-THUNKS-Test8: VFTable for 'A' in 'no_thunks::Test3' in 'no_thunks::Test8' (2 entries).
// NO-THUNKS-Test8-NEXT: 0 | void A::f()
// NO-THUNKS-Test8-NEXT: 1 | void no_thunks::Test3::i()
// NO-THUNKS-Test8: VFTable for 'B' in 'no_thunks::Test3' in 'no_thunks::Test8' (2 entries).
// NO-THUNKS-Test8-NEXT: 0 | void no_thunks::Test8::g()
// NO-THUNKS-Test8-NEXT: 1 | void B::h()
// NO-THUNKS-Test8: VFTable indices for 'no_thunks::Test8' (1 entries).
// NO-THUNKS-Test8-NEXT: via vfptr at offset 4
// NO-THUNKS-Test8-NEXT: 0 | void no_thunks::Test8::g()
// Overrides grandparent's B::g.
virtual void g();
};
Test8 t8;
struct D : A {
virtual void g();
};
// Repeating subobject.
struct Test9: A, D {
// NO-THUNKS-Test9: VFTable for 'A' in 'no_thunks::Test9' (2 entries).
// NO-THUNKS-Test9-NEXT: 0 | void A::f()
// NO-THUNKS-Test9-NEXT: 1 | void no_thunks::Test9::h()
// NO-THUNKS-Test9: VFTable for 'A' in 'no_thunks::D' in 'no_thunks::Test9' (2 entries).
// NO-THUNKS-Test9-NEXT: 0 | void A::f()
// NO-THUNKS-Test9-NEXT: 1 | void no_thunks::D::g()
// NO-THUNKS-Test9: VFTable indices for 'no_thunks::Test9' (1 entries).
// NO-THUNKS-Test9-NEXT: 1 | void no_thunks::Test9::h()
virtual void h();
};
Test9 t9;
}
namespace pure_virtual {
struct D {
virtual void g() = 0;
virtual void h();
};
struct Test1: A, D {
// PURE-VIRTUAL-Test1: VFTable for 'A' in 'pure_virtual::Test1' (1 entries)
// PURE-VIRTUAL-Test1-NEXT: 0 | void A::f()
// PURE-VIRTUAL-Test1: VFTable for 'pure_virtual::D' in 'pure_virtual::Test1' (2 entries)
// PURE-VIRTUAL-Test1-NEXT: 0 | void pure_virtual::Test1::g()
// PURE-VIRTUAL-Test1-NEXT: 1 | void pure_virtual::D::h()
// PURE-VIRTUAL-Test1: VFTable indices for 'pure_virtual::Test1' (1 entries).
// PURE-VIRTUAL-Test1-NEXT: via vfptr at offset 4
// PURE-VIRTUAL-Test1-NEXT: 0 | void pure_virtual::Test1::g()
// Overrides only the right child's method (pure_virtual::D::g), needs this adjustment but
// not thunks.
virtual void g();
};
Test1 t1;
}
namespace this_adjustment {
// Overrides methods of two bases at the same time, thus needing thunks.
struct Test1 : B, C {
// THIS-THUNKS-Test1: VFTable for 'B' in 'this_adjustment::Test1' (2 entries).
// THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g()
// THIS-THUNKS-Test1-NEXT: 1 | void B::h()
// THIS-THUNKS-Test1: VFTable for 'C' in 'this_adjustment::Test1' (1 entries).
// THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g()
// THIS-THUNKS-Test1-NEXT: [this adjustment: -4 non-virtual]
// THIS-THUNKS-Test1: Thunks for 'void this_adjustment::Test1::g()' (1 entry).
// THIS-THUNKS-Test1-NEXT: 0 | this adjustment: -4 non-virtual
// THIS-THUNKS-Test1: VFTable indices for 'this_adjustment::Test1' (1 entries).
// THIS-THUNKS-Test1-NEXT: 0 | void this_adjustment::Test1::g()
virtual void g();
};
Test1 t1;
struct Test2 : A, B, C {
// THIS-THUNKS-Test2: VFTable for 'A' in 'this_adjustment::Test2' (1 entries).
// THIS-THUNKS-Test2-NEXT: 0 | void A::f()
// THIS-THUNKS-Test2: VFTable for 'B' in 'this_adjustment::Test2' (2 entries).
// THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g()
// THIS-THUNKS-Test2-NEXT: 1 | void B::h()
// THIS-THUNKS-Test2: VFTable for 'C' in 'this_adjustment::Test2' (1 entries).
// THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g()
// THIS-THUNKS-Test2-NEXT: [this adjustment: -4 non-virtual]
// THIS-THUNKS-Test2: Thunks for 'void this_adjustment::Test2::g()' (1 entry).
// THIS-THUNKS-Test2-NEXT: 0 | this adjustment: -4 non-virtual
// THIS-THUNKS-Test2: VFTable indices for 'this_adjustment::Test2' (1 entries).
// THIS-THUNKS-Test2-NEXT: via vfptr at offset 4
// THIS-THUNKS-Test2-NEXT: 0 | void this_adjustment::Test2::g()
virtual void g();
};
Test2 t2;
// Overrides methods of two bases at the same time, thus needing thunks.
struct Test3: no_thunks::Test1, no_thunks::Test2 {
// THIS-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test1' in 'this_adjustment::Test3' (1 entries).
// THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f()
// THIS-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test1' in 'this_adjustment::Test3' (2 entries).
// THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g()
// THIS-THUNKS-Test3-NEXT: 1 | void B::h()
// THIS-THUNKS-Test3: VFTable for 'A' in 'no_thunks::Test2' in 'this_adjustment::Test3' (1 entries).
// THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f()
// THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual]
// THIS-THUNKS-Test3: Thunks for 'void this_adjustment::Test3::f()' (1 entry).
// THIS-THUNKS-Test3-NEXT: 0 | this adjustment: -8 non-virtual
// THIS-THUNKS-Test3: VFTable for 'B' in 'no_thunks::Test2' in 'this_adjustment::Test3' (2 entries).
// THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g()
// THIS-THUNKS-Test3-NEXT: [this adjustment: -8 non-virtual]
// THIS-THUNKS-Test3-NEXT: 1 | void B::h()
// THIS-THUNKS-Test3: Thunks for 'void this_adjustment::Test3::g()' (1 entry).
// THIS-THUNKS-Test3-NEXT: 0 | this adjustment: -8 non-virtual
// THIS-THUNKS-Test3: VFTable indices for 'this_adjustment::Test3' (2 entries).
// THIS-THUNKS-Test3-NEXT: via vfptr at offset 0
// THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::f()
// THIS-THUNKS-Test3-NEXT: via vfptr at offset 4
// THIS-THUNKS-Test3-NEXT: 0 | void this_adjustment::Test3::g()
virtual void f();
virtual void g();
};
Test3 t3;
}
namespace return_adjustment {
struct Ret1 {
virtual C* foo();
virtual void z();
};
struct Test1 : Ret1 {
// RET-THUNKS-Test1: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' (3 entries).
// RET-THUNKS-Test1-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test1::foo()
// RET-THUNKS-Test1-NEXT: [return adjustment: 4 non-virtual]
// RET-THUNKS-Test1-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo()
// RET-THUNKS-Test1: VFTable indices for 'return_adjustment::Test1' (1 entries).
// RET-THUNKS-Test1-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test1::foo()
virtual this_adjustment::Test1* foo();
};
Test1 t1;
struct Ret2 : B, this_adjustment::Test1 { };
struct Test2 : Test1 {
// RET-THUNKS-Test2: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test2' (4 entries).
// RET-THUNKS-Test2-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
// RET-THUNKS-Test2-NEXT: [return adjustment: 8 non-virtual]
// RET-THUNKS-Test2-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test2-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
// RET-THUNKS-Test2-NEXT: [return adjustment: 4 non-virtual]
// RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
// RET-THUNKS-Test2: VFTable indices for 'return_adjustment::Test2' (1 entries).
// RET-THUNKS-Test2-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test2::foo()
virtual Ret2* foo();
};
Test2 t2;
struct Test3: B, Ret1 {
// RET-THUNKS-Test3: VFTable for 'B' in 'return_adjustment::Test3' (2 entries).
// RET-THUNKS-Test3-NEXT: 0 | void B::g()
// RET-THUNKS-Test3-NEXT: 1 | void B::h()
// RET-THUNKS-Test3: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' (3 entries).
// RET-THUNKS-Test3-NEXT: 0 | this_adjustment::Test1 *return_adjustment::Test3::foo()
// RET-THUNKS-Test3-NEXT: [return adjustment: 4 non-virtual]
// RET-THUNKS-Test3-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo()
// RET-THUNKS-Test3: VFTable indices for 'return_adjustment::Test3' (1 entries).
// RET-THUNKS-Test3-NEXT: via vfptr at offset 4
// RET-THUNKS-Test3-NEXT: 2 | this_adjustment::Test1 *return_adjustment::Test3::foo()
virtual this_adjustment::Test1* foo();
};
Test3 t3;
struct Test4 : Test3 {
// RET-THUNKS-Test4: VFTable for 'B' in 'return_adjustment::Test3' in 'return_adjustment::Test4' (2 entries).
// RET-THUNKS-Test4-NEXT: 0 | void B::g()
// RET-THUNKS-Test4-NEXT: 1 | void B::h()
// RET-THUNKS-Test4: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test3' in 'return_adjustment::Test4' (4 entries).
// RET-THUNKS-Test4-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
// RET-THUNKS-Test4-NEXT: [return adjustment: 8 non-virtual]
// RET-THUNKS-Test4-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test4-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
// RET-THUNKS-Test4-NEXT: [return adjustment: 4 non-virtual]
// RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
// RET-THUNKS-Test4: VFTable indices for 'return_adjustment::Test4' (1 entries).
// RET-THUNKS-Test4-NEXT: -- accessible via vfptr at offset 4 --
// RET-THUNKS-Test4-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test4::foo()
virtual Ret2* foo();
};
Test4 t4;
struct Test5 : Ret1, Test1 {
// RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test5' (3 entries).
// RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
// RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual]
// RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
// RET-THUNKS-Test5: VFTable for 'return_adjustment::Ret1' in 'return_adjustment::Test1' in 'return_adjustment::Test5' (4 entries).
// RET-THUNKS-Test5-NEXT: 0 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
// RET-THUNKS-Test5-NEXT: [return adjustment: 8 non-virtual]
// RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
// RET-THUNKS-Test5-NEXT: 1 | void return_adjustment::Ret1::z()
// RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
// RET-THUNKS-Test5-NEXT: [return adjustment: 4 non-virtual]
// RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
// RET-THUNKS-Test5-NEXT: 3 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
// RET-THUNKS-Test5-NEXT: [this adjustment: -4 non-virtual]
// RET-THUNKS-Test5: VFTable indices for 'return_adjustment::Test5' (1 entries).
// RET-THUNKS-Test5-NEXT: 2 | return_adjustment::Ret2 *return_adjustment::Test5::foo()
virtual Ret2* foo();
};
Test5 t5;
}

View File

@ -8,12 +8,25 @@
// RUN: FileCheck --check-prefix=CHECK-E %s < %t
// RUN: FileCheck --check-prefix=CHECK-F %s < %t
// RUN: FileCheck --check-prefix=CHECK-G %s < %t
// RUN: FileCheck --check-prefix=CHECK-I %s < %t
// FIXME: Currently, we only test VFTableContext in the AST, but still use
// VTableContext for CodeGen. We should remove the "Vtable" checks below when we
// completely switch from VTableContext to VFTableContext.
// Currently, the order of Vtable vs VFTable output depends on whether the
// v*table info was required by a constructor or a method definition.
struct A {
// CHECK-A: Vtable for 'A' (3 entries)
// CHECK-A-NEXT: 0 | void A::f()
// CHECK-A-NEXT: 1 | void A::g()
// CHECK-A-NEXT: 2 | void A::h()
// CHECK-A: VFTable for 'A' (3 entries)
// CHECK-A-NEXT: 0 | void A::f()
// CHECK-A-NEXT: 1 | void A::g()
// CHECK-A-NEXT: 2 | void A::h()
virtual void f();
virtual void g();
virtual void h();
@ -29,6 +42,14 @@ struct B : A {
// CHECK-B-NEXT: 2 | void A::h()
// CHECK-B-NEXT: 3 | void B::i()
// CHECK-B-NEXT: 4 | void B::j()
// CHECK-B: VFTable for 'A' in 'B' (5 entries)
// CHECK-B-NEXT: 0 | void B::f()
// CHECK-B-NEXT: 1 | void A::g()
// CHECK-B-NEXT: 2 | void A::h()
// CHECK-B-NEXT: 3 | void B::i()
// CHECK-B-NEXT: 4 | void B::j()
virtual void f(); // overrides A::f()
virtual void i();
virtual void j();
@ -37,14 +58,21 @@ B b;
// EMITS-VTABLE-DAG: @"\01??_7B@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
struct C {
// CHECK-C: VFTable for 'C' (2 entries)
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
// CHECK-C: VFTable indices for 'C' (2 entries).
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
// CHECK-C: Vtable for 'C' (2 entries)
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
// CHECK-C: VTable indices for 'C' (2 entries).
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
virtual ~C();
virtual ~C();
virtual void f();
};
void C::f() {}
@ -54,14 +82,28 @@ struct D {
// CHECK-D: Vtable for 'D' (2 entries)
// CHECK-D-NEXT: 0 | void D::f()
// CHECK-D-NEXT: 1 | D::~D() [scalar deleting]
virtual void f();
// CHECK-D: VFTable for 'D' (2 entries)
// CHECK-D-NEXT: 0 | void D::f()
// CHECK-D-NEXT: 1 | D::~D() [scalar deleting]
virtual void f();
virtual ~D();
};
D d;
// EMITS-VTABLE-DAG: @"\01??_7D@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*]
struct E : A {
// CHECK-E: VFTable for 'A' in 'E' (5 entries)
// CHECK-E-NEXT: 0 | void A::f()
// CHECK-E-NEXT: 1 | void A::g()
// CHECK-E-NEXT: 2 | void A::h()
// CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
// CHECK-E-NEXT: 4 | void E::i()
// CHECK-E: VFTable indices for 'E' (2 entries).
// CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
// CHECK-E-NEXT: 4 | void E::i()
// CHECK-E: Vtable for 'E' (5 entries)
// CHECK-E-NEXT: 0 | void A::f()
// CHECK-E-NEXT: 1 | void A::g()
@ -90,6 +132,17 @@ struct F : A {
// CHECK-F: VTable indices for 'F' (2 entries).
// CHECK-F-NEXT: 3 | void F::i()
// CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
// CHECK-F: VFTable for 'A' in 'F' (5 entries)
// CHECK-F-NEXT: 0 | void A::f()
// CHECK-F-NEXT: 1 | void A::g()
// CHECK-F-NEXT: 2 | void A::h()
// CHECK-F-NEXT: 3 | void F::i()
// CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
// CHECK-F: VFTable indices for 'F' (2 entries).
// CHECK-F-NEXT: 3 | void F::i()
// CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
virtual void i();
virtual ~F();
};
@ -97,6 +150,18 @@ F f;
// EMITS-VTABLE-DAG: @"\01??_7F@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
struct G : E {
// CHECK-G: VFTable for 'A' in 'E' in 'G' (6 entries)
// CHECK-G-NEXT: 0 | void G::f()
// CHECK-G-NEXT: 1 | void A::g()
// CHECK-G-NEXT: 2 | void A::h()
// CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-G-NEXT: 4 | void E::i()
// CHECK-G-NEXT: 5 | void G::j()
// CHECK-G: VFTable indices for 'G' (3 entries).
// CHECK-G-NEXT: 0 | void G::f()
// CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-G-NEXT: 5 | void G::j()
// CHECK-G: Vtable for 'G' (6 entries)
// CHECK-G-NEXT: 0 | void G::f()
// CHECK-G-NEXT: 1 | void A::g()
@ -108,6 +173,7 @@ struct G : E {
// CHECK-G-NEXT: 0 | void G::f()
// CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-G-NEXT: 5 | void G::j()
virtual void f(); // overrides A::f()
virtual ~G();
virtual void j();
@ -121,3 +187,15 @@ struct H {
};
void H::f() {}
// NO-VTABLE-NOT: @"\01??_7H@@6B@"
struct Empty { };
struct I : Empty {
// CHECK-I: VFTable for 'I' (2 entries)
// CHECK-I-NEXT: 0 | void I::f()
// CHECK-I-NEXT: 1 | void I::g()
virtual void f();
virtual void g();
};
I i;

View File

@ -0,0 +1,391 @@
// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o - -cxx-abi microsoft -triple=i386-pc-win32 >%t 2>&1
// RUN: FileCheck --check-prefix=VTABLE-C %s < %t
// RUN: FileCheck --check-prefix=VTABLE-D %s < %t
// RUN: FileCheck --check-prefix=TEST1 %s < %t
// RUN: FileCheck --check-prefix=TEST2 %s < %t
// RUN: FileCheck --check-prefix=TEST3 %s < %t
// RUN: FileCheck --check-prefix=TEST4 %s < %t
// RUN: FileCheck --check-prefix=TEST5 %s < %t
// RUN: FileCheck --check-prefix=TEST6 %s < %t
// RUN: FileCheck --check-prefix=TEST7 %s < %t
// RUN: FileCheck --check-prefix=TEST8 %s < %t
// RUN: FileCheck --check-prefix=TEST9-Y %s < %t
// RUN: FileCheck --check-prefix=TEST9-Z %s < %t
// RUN: FileCheck --check-prefix=TEST9-W %s < %t
// RUN: FileCheck --check-prefix=TEST9-T %s < %t
// RUN: FileCheck --check-prefix=TEST10 %s < %t
// RUN: FileCheck --check-prefix=RET-W %s < %t
// RUN: FileCheck --check-prefix=RET-T %s < %t
struct Empty { };
struct A {
virtual void f();
virtual void z(); // Useful to check there are no thunks for f() when appropriate.
};
struct B {
virtual void g();
};
struct C: virtual A {
// VTABLE-C: VFTable for 'A' in 'C' (2 entries)
// VTABLE-C-NEXT: 0 | void C::f()
// VTABLE-C-NEXT: 1 | void A::z()
// VTABLE-C: VFTable indices for 'C' (1 entries)
// VTABLE-C-NEXT: vbtable index 1, vfptr at offset 0
// VTABLE-C-NEXT: 0 | void C::f()
~C(); // Currently required to have correct record layout, see PR16406
virtual void f();
};
C c;
struct D: virtual A {
// VTABLE-D: VFTable for 'D' (1 entries).
// VTABLE-D-NEXT: 0 | void D::h()
// VTABLE-D: VFTable for 'A' in 'D' (2 entries).
// VTABLE-D-NEXT: 0 | void D::f()
// VTABLE-D-NEXT: 1 | void A::z()
// VTABLE-D: VFTable indices for 'D' (2 entries).
// VTABLE-D-NEXT: via vfptr at offset 0
// VTABLE-D-NEXT: 0 | void D::h()
// VTABLE-D-NEXT: via vbtable index 1, vfptr at offset 0
// VTABLE-D-NEXT: 0 | void D::f()
virtual void f();
virtual void h();
};
void D::h() {}
D d;
namespace Test1 {
struct X { int x; };
// X and A get reordered in the layout since X doesn't have a vfptr while A has.
struct Y : X, A { };
struct Z : virtual Y {
// TEST1: VFTable for 'A' in 'Test1::Y' in 'Test1::Z' (2 entries).
// TEST1-NEXT: 0 | void A::f()
// TEST1-NEXT: 1 | void A::z()
// TEST1-NOT: VFTable indices for 'Test1::Z'
};
Z z;
}
namespace Test2 {
struct X: virtual A, virtual B {
// TEST2: VFTable for 'Test2::X' (1 entries).
// TEST2-NEXT: 0 | void Test2::X::h()
// TEST2: VFTable for 'A' in 'Test2::X' (2 entries).
// TEST2-NEXT: 0 | void A::f()
// TEST2-NEXT: 1 | void A::z()
// TEST2: VFTable for 'B' in 'Test2::X' (1 entries).
// TEST2-NEXT: 0 | void B::g()
// TEST2: VFTable indices for 'Test2::X' (1 entries).
// TEST2-NEXT: 0 | void Test2::X::h()
virtual void h();
};
X x;
}
namespace Test3 {
struct X : virtual A { };
struct Y: virtual X {
// TEST3: VFTable for 'A' in 'Test3::X' in 'Test3::Y' (2 entries).
// TEST3-NEXT: 0 | void A::f()
// TEST3-NEXT: 1 | void A::z()
// TEST3-NOT: VFTable indices for 'Test3::Y'
};
Y y;
}
namespace Test4 {
struct X: virtual C {
// This one's interesting. C::f expects (A*) to be passed as 'this' and does
// ECX-=4 to cast to (C*). In X, C and A vbases are reordered, so the thunk
// should pass a pointer to the end of X in order
// for ECX-=4 to point at the C part.
// TEST4: VFTable for 'A' in 'C' in 'Test4::X' (2 entries).
// TEST4-NEXT: 0 | void C::f()
// TEST4-NEXT: [this adjustment: 12 non-virtual]
// TEST4-NEXT: 1 | void A::z()
// TEST4-NOT: VFTable indices for 'Test4::X'
};
X x;
}
namespace Test5 {
// New methods are added to the base's vftable.
struct X : A {
virtual void g();
};
struct Y : virtual X {
// TEST5: VFTable for 'Test5::Y' (1 entries).
// TEST5-NEXT: 0 | void Test5::Y::h()
// TEST5: VFTable for 'A' in 'Test5::X' in 'Test5::Y' (3 entries).
// TEST5-NEXT: 0 | void A::f()
// TEST5-NEXT: 1 | void A::z()
// TEST5-NEXT: 2 | void Test5::X::g()
// TEST5: VFTable indices for 'Test5::Y' (1 entries).
// TEST5-NEXT: 0 | void Test5::Y::h()
virtual void h();
};
Y y;
}
namespace Test6 {
struct X : A, virtual Empty {
// TEST6: VFTable for 'A' in 'Test6::X' (2 entries).
// TEST6-NEXT: 0 | void A::f()
// TEST6-NEXT: 1 | void A::z()
// TEST6-NOT: VFTable indices for 'Test6::X'
};
X x;
}
namespace Test7 {
struct X : C { };
struct Y : virtual X {
// TEST7: VFTable for 'A' in 'C' in 'Test7::X' in 'Test7::Y' (2 entries).
// TEST7-NEXT: 0 | void C::f()
// TEST7-NEXT: [this adjustment: 12 non-virtual]
// TEST7-NEXT: 1 | void A::z()
// TEST7: Thunks for 'void C::f()' (1 entry).
// TEST7-NEXT: 0 | this adjustment: 12 non-virtual
// TEST7-NOT: VFTable indices for 'Test7::Y'
};
Y y;
}
namespace Test8 {
// This is a typical diamond inheritance with a shared 'A' vbase.
struct X : D, C {
// TEST8: VFTable for 'D' in 'Test8::X' (1 entries).
// TEST8-NEXT: 0 | void D::h()
// TEST8: VFTable for 'A' in 'D' in 'Test8::X' (2 entries).
// TEST8-NEXT: 0 | void Test8::X::f()
// TEST8-NEXT: 1 | void A::z()
// TEST8: VFTable indices for 'Test8::X' (1 entries).
// TEST8-NEXT: via vbtable index 1, vfptr at offset 0
virtual void f();
};
X x;
}
namespace Test9 {
struct X : A { };
struct Y : virtual X {
// TEST9-Y: VFTable for 'Test9::Y' (1 entries).
// TEST9-Y-NEXT: 0 | void Test9::Y::h()
// TEST9-Y: VFTable for 'A' in 'Test9::X' in 'Test9::Y' (2 entries).
// TEST9-Y-NEXT: 0 | void A::f()
// TEST9-Y-NEXT: 1 | void A::z()
// TEST9-Y: VFTable indices for 'Test9::Y' (1 entries).
// TEST9-Y-NEXT: 0 | void Test9::Y::h()
virtual void h();
};
Y y;
struct Z : Y, virtual B {
// TEST9-Z: VFTable for 'Test9::Y' in 'Test9::Z' (1 entries).
// TEST9-Z-NEXT: 0 | void Test9::Y::h()
// TEST9-Z: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' (2 entries).
// TEST9-Z-NEXT: 0 | void A::f()
// TEST9-Z-NEXT: 1 | void A::z()
// TEST9-Z: VFTable for 'B' in 'Test9::Z' (1 entries).
// TEST9-Z-NEXT: 0 | void B::g()
// TEST9-Z-NOT: VFTable indices for 'Test9::Z'
};
Z z;
struct W : Z, D, virtual A, virtual B {
// TEST9-W: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::W' (1 entries).
// TEST9-W-NEXT: 0 | void Test9::Y::h()
// TEST9-W: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::W' (2 entries).
// TEST9-W-NEXT: 0 | void A::f()
// TEST9-W-NEXT: 1 | void A::z()
// TEST9-W: VFTable for 'B' in 'Test9::Z' in 'Test9::W' (1 entries).
// TEST9-W-NEXT: 0 | void B::g()
// TEST9-W: VFTable for 'D' in 'Test9::W' (1 entries).
// TEST9-W-NEXT: 0 | void D::h()
// TEST9-W: VFTable for 'A' in 'D' in 'Test9::W' (2 entries).
// TEST9-W-NEXT: 0 | void D::f()
// TEST9-W-NEXT: [this adjustment: -8 non-virtual]
// TEST9-W-NEXT: 1 | void A::z()
// TEST9-W: Thunks for 'void D::f()' (1 entry).
// TEST9-W-NEXT: 0 | this adjustment: -8 non-virtual
// TEST9-W-NOT: VFTable indices for 'Test9::W'
};
W w;
struct T : Z, D, virtual A, virtual B {
~T(); // Currently required to have correct record layout, see PR16406
// TEST9-T: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::T' (1 entries).
// TEST9-T-NEXT: 0 | void Test9::T::h()
// TEST9-T: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::T' (2 entries).
// TEST9-T-NEXT: 0 | void Test9::T::f()
// TEST9-T-NEXT: 1 | void Test9::T::z()
// TEST9-T: VFTable for 'B' in 'Test9::Z' in 'Test9::T' (1 entries).
// TEST9-T-NEXT: 0 | void Test9::T::g()
// TEST9-T: VFTable for 'D' in 'Test9::T' (1 entries).
// TEST9-T-NEXT: 0 | void Test9::T::h()
// TEST9-T-NEXT: [this adjustment: -8 non-virtual]
// TEST9-T: Thunks for 'void Test9::T::h()' (1 entry).
// TEST9-T-NEXT: 0 | this adjustment: -8 non-virtual
// TEST9-T: VFTable for 'A' in 'D' in 'Test9::T' (2 entries).
// TEST9-T-NEXT: 0 | void Test9::T::f()
// TEST9-T-NEXT: [this adjustment: -16 non-virtual]
// TEST9-T-NEXT: 1 | void Test9::T::z()
// TEST9-T-NEXT: [this adjustment: -16 non-virtual]
// TEST9-T: Thunks for 'void Test9::T::f()' (1 entry).
// TEST9-T-NEXT: 0 | this adjustment: -16 non-virtual
// TEST9-T: Thunks for 'void Test9::T::z()' (1 entry).
// TEST9-T-NEXT: 0 | this adjustment: -16 non-virtual
// TEST9-T: VFTable indices for 'Test9::T' (4 entries).
// TEST9-T-NEXT: via vfptr at offset 0
// TEST9-T-NEXT: 0 | void Test9::T::h()
// TEST9-T-NEXT: via vbtable index 1, vfptr at offset 0
// TEST9-T-NEXT: 0 | void Test9::T::f()
// TEST9-T-NEXT: 1 | void Test9::T::z()
// TEST9-T-NEXT: via vbtable index 2, vfptr at offset 0
// TEST9-T-NEXT: 0 | void Test9::T::g()
virtual void f();
virtual void g();
virtual void h();
virtual void z();
};
T t;
}
namespace Test10 {
struct X : virtual C, virtual A {
// TEST10: VFTable for 'A' in 'C' in 'Test10::X' (2 entries).
// TEST10-NEXT: 0 | void Test10::X::f()
// TEST10-NEXT: 1 | void A::z()
// TEST10: VFTable indices for 'Test10::X' (1 entries).
// TEST10-NEXT: via vbtable index 1, vfptr at offset 0
// TEST10-NEXT: 0 | void Test10::X::f()
virtual void f();
};
void X::f() {}
X x;
}
namespace return_adjustment {
struct X : virtual A {
virtual void f();
};
struct Y : virtual A, virtual X {
virtual void f();
};
struct Z {
virtual A* foo();
};
struct W : Z {
// RET-W: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries).
// RET-W-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo()
// RET-W-NEXT: [return adjustment: vbase #1, 0 non-virtual]
// RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
// RET-W: VFTable indices for 'return_adjustment::W' (1 entries).
// RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
virtual X* foo();
};
W y;
struct T : W {
// RET-T: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries).
// RET-T-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo()
// RET-T-NEXT: [return adjustment: vbase #1, 0 non-virtual]
// RET-T-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo()
// RET-T-NEXT: [return adjustment: vbase #2, 0 non-virtual]
// RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
// RET-T: VFTable indices for 'return_adjustment::T' (1 entries).
// RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
virtual Y* foo();
};
T t;
}