mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-28 14:06:08 +00:00

In Itanium, dynamic classes have one vtable with several different address points for dynamic base classes that can't share vtables. In the MS C++ ABI, each vbtable that can't be shared gets its own symbol, similar to how ctor vtables work in Itanium. However, instead of mangling the subobject offset into the symbol, the unique portions of the inheritance path are mangled into the symbol to make it unique. This patch implements the MSVC 2012 scheme for forming unique vbtable symbol names. MSVC 2010 use the same mangling with a different subset of the path. Implementing that mangling and possibly others is TODO. Each vbtable is an array of i32 offsets from the vbptr that points to it to another virtual base subobject. The first entry of a vbtable always points to the base of the current subobject, implying that it is the same no matter which parent class contains it. Reviewers: rjmccall Differential Revision: http://llvm-reviews.chandlerc.com/D636 llvm-svn: 184309
237 lines
8.9 KiB
C++
237 lines
8.9 KiB
C++
//===--- MicrosoftVBTables.cpp - Virtual Base Table Emission --------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This class generates data about MSVC virtual base tables.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MicrosoftVBTables.h"
|
|
#include "CodeGenModule.h"
|
|
#include "CGCXXABI.h"
|
|
|
|
namespace clang {
|
|
namespace CodeGen {
|
|
|
|
/// Holds intermediate data about a path to a vbptr inside a base subobject.
|
|
struct VBTablePath {
|
|
VBTablePath(const VBTableInfo &VBInfo)
|
|
: VBInfo(VBInfo), NextBase(VBInfo.VBPtrSubobject.getBase()) { }
|
|
|
|
/// All the data needed to build a vbtable, minus the GlobalVariable whose
|
|
/// name we haven't computed yet.
|
|
VBTableInfo VBInfo;
|
|
|
|
/// Next base to use for disambiguation. Can be null if we've already
|
|
/// disambiguated this path once.
|
|
const CXXRecordDecl *NextBase;
|
|
|
|
/// Path is not really a full path like a CXXBasePath. It holds the subset of
|
|
/// records that need to be mangled into the vbtable symbol name in order to get
|
|
/// a unique name.
|
|
llvm::SmallVector<const CXXRecordDecl *, 1> Path;
|
|
};
|
|
|
|
VBTableBuilder::VBTableBuilder(CodeGenModule &CGM,
|
|
const CXXRecordDecl *MostDerived)
|
|
: CGM(CGM), MostDerived(MostDerived),
|
|
DerivedLayout(CGM.getContext().getASTRecordLayout(MostDerived)) {}
|
|
|
|
void VBTableBuilder::enumerateVBTables(VBTableVector &VBTables) {
|
|
VBTablePathVector Paths;
|
|
findUnambiguousPaths(MostDerived, BaseSubobject(MostDerived,
|
|
CharUnits::Zero()), Paths);
|
|
for (VBTablePathVector::iterator I = Paths.begin(), E = Paths.end();
|
|
I != E; ++I) {
|
|
VBTablePath *P = *I;
|
|
P->VBInfo.GV = getAddrOfVBTable(P->VBInfo.ReusingBase, P->Path);
|
|
VBTables.push_back(P->VBInfo);
|
|
}
|
|
}
|
|
|
|
bool VBTableBuilder::hasVBPtr(const CXXRecordDecl *RD) {
|
|
const ASTRecordLayout &Layout = CGM.getContext().getASTRecordLayout(RD);
|
|
return Layout.getVBPtrOffset().getQuantity() != -1;
|
|
}
|
|
|
|
void VBTableBuilder::findUnambiguousPaths(const CXXRecordDecl *ReusingBase,
|
|
BaseSubobject CurSubobject,
|
|
VBTablePathVector &Paths) {
|
|
size_t PathsStart = Paths.size();
|
|
bool ReuseVBPtrFromBase = true;
|
|
const CXXRecordDecl *CurBase = CurSubobject.getBase();
|
|
|
|
// If this base has a vbptr, then we've found a path. These are not full
|
|
// paths, so we don't use CXXBasePath.
|
|
if (hasVBPtr(CurBase)) {
|
|
ReuseVBPtrFromBase = false;
|
|
VBTablePath *Info = new VBTablePath(
|
|
VBTableInfo(ReusingBase, CurSubobject, /*GV=*/0));
|
|
Paths.push_back(Info);
|
|
}
|
|
|
|
// Recurse onto any bases which themselves have virtual bases.
|
|
const ASTRecordLayout &Layout = CGM.getContext().getASTRecordLayout(CurBase);
|
|
for (CXXRecordDecl::base_class_const_iterator I = CurBase->bases_begin(),
|
|
E = CurBase->bases_end(); I != E; ++I) {
|
|
const CXXRecordDecl *Base = I->getType()->getAsCXXRecordDecl();
|
|
if (!Base->getNumVBases())
|
|
continue; // Bases without virtual bases have no vbptrs.
|
|
CharUnits NextOffset;
|
|
const CXXRecordDecl *NextReusingBase = Base;
|
|
if (I->isVirtual()) {
|
|
if (!VBasesSeen.insert(Base))
|
|
continue; // Don't visit virtual bases twice.
|
|
NextOffset = DerivedLayout.getVBaseClassOffset(Base);
|
|
} else {
|
|
NextOffset = (CurSubobject.getBaseOffset() +
|
|
Layout.getBaseClassOffset(Base));
|
|
|
|
// If CurBase didn't have a vbptr, then ReusingBase will reuse the vbptr
|
|
// from the first non-virtual base with vbases for its vbptr.
|
|
if (ReuseVBPtrFromBase) {
|
|
NextReusingBase = ReusingBase;
|
|
ReuseVBPtrFromBase = false;
|
|
}
|
|
}
|
|
|
|
size_t NumPaths = Paths.size();
|
|
findUnambiguousPaths(NextReusingBase, BaseSubobject(Base, NextOffset),
|
|
Paths);
|
|
|
|
// Tag paths through this base with the base itself. We might use it to
|
|
// disambiguate.
|
|
for (size_t I = NumPaths, E = Paths.size(); I != E; ++I)
|
|
Paths[I]->NextBase = Base;
|
|
}
|
|
|
|
bool AmbiguousPaths = rebucketPaths(Paths, PathsStart);
|
|
if (AmbiguousPaths)
|
|
rebucketPaths(Paths, PathsStart, /*SecondPass=*/true);
|
|
|
|
#ifndef NDEBUG
|
|
// Check that the paths are in fact unique.
|
|
for (size_t I = PathsStart + 1, E = Paths.size(); I != E; ++I) {
|
|
assert(Paths[I]->Path != Paths[I - 1]->Path && "vbtable paths are not unique");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static bool pathCompare(VBTablePath *LHS, VBTablePath *RHS) {
|
|
return LHS->Path < RHS->Path;
|
|
}
|
|
|
|
void VBTableBuilder::extendPath(VBTablePath *P, bool SecondPass) {
|
|
assert(P->NextBase || SecondPass);
|
|
if (P->NextBase) {
|
|
P->Path.push_back(P->NextBase);
|
|
P->NextBase = 0; // Prevent the path from being extended twice.
|
|
}
|
|
}
|
|
|
|
bool VBTableBuilder::rebucketPaths(VBTablePathVector &Paths, size_t PathsStart,
|
|
bool SecondPass) {
|
|
// What we're essentially doing here is bucketing together ambiguous paths.
|
|
// Any bucket with more than one path in it gets extended by NextBase, which
|
|
// is usually the direct base of the inherited the vbptr. This code uses a
|
|
// sorted vector to implement a multiset to form the buckets. Note that the
|
|
// ordering is based on pointers, but it doesn't change our output order. The
|
|
// current algorithm is designed to match MSVC 2012's names.
|
|
// TODO: Implement MSVC 2010 or earlier names to avoid extra vbtable cruft.
|
|
VBTablePathVector PathsSorted(&Paths[PathsStart], &Paths.back() + 1);
|
|
std::sort(PathsSorted.begin(), PathsSorted.end(), pathCompare);
|
|
bool AmbiguousPaths = false;
|
|
for (size_t I = 0, E = PathsSorted.size(); I != E;) {
|
|
// Scan forward to find the end of the bucket.
|
|
size_t BucketStart = I;
|
|
do {
|
|
++I;
|
|
} while (I != E && PathsSorted[BucketStart]->Path == PathsSorted[I]->Path);
|
|
|
|
// If this bucket has multiple paths, extend them all.
|
|
if (I - BucketStart > 1) {
|
|
AmbiguousPaths = true;
|
|
for (size_t II = BucketStart; II != I; ++II)
|
|
extendPath(PathsSorted[II], SecondPass);
|
|
}
|
|
}
|
|
return AmbiguousPaths;
|
|
}
|
|
|
|
llvm::GlobalVariable *
|
|
VBTableBuilder::getAddrOfVBTable(const CXXRecordDecl *ReusingBase,
|
|
ArrayRef<const CXXRecordDecl *> BasePath) {
|
|
// Caching at this layer is redundant with the caching in EnumerateVBTables().
|
|
|
|
SmallString<256> OutName;
|
|
llvm::raw_svector_ostream Out(OutName);
|
|
MangleContext &Mangler = CGM.getCXXABI().getMangleContext();
|
|
Mangler.mangleCXXVBTable(MostDerived, BasePath, Out);
|
|
Out.flush();
|
|
StringRef Name = OutName.str();
|
|
|
|
llvm::ArrayType *VBTableType =
|
|
llvm::ArrayType::get(CGM.IntTy, 1 + ReusingBase->getNumVBases());
|
|
|
|
assert(!CGM.getModule().getNamedGlobal(Name) &&
|
|
"vbtable with this name already exists: mangling bug?");
|
|
llvm::GlobalVariable *VBTable =
|
|
CGM.CreateOrReplaceCXXRuntimeVariable(Name, VBTableType,
|
|
llvm::GlobalValue::ExternalLinkage);
|
|
VBTable->setUnnamedAddr(true);
|
|
return VBTable;
|
|
}
|
|
|
|
void VBTableInfo::EmitVBTableDefinition(
|
|
CodeGenModule &CGM, const CXXRecordDecl *RD,
|
|
llvm::GlobalVariable::LinkageTypes Linkage) const {
|
|
assert(RD->getNumVBases() && ReusingBase->getNumVBases() &&
|
|
"should only emit vbtables for classes with vbtables");
|
|
|
|
const ASTRecordLayout &BaseLayout =
|
|
CGM.getContext().getASTRecordLayout(VBPtrSubobject.getBase());
|
|
const ASTRecordLayout &DerivedLayout =
|
|
CGM.getContext().getASTRecordLayout(RD);
|
|
|
|
SmallVector<llvm::Constant *, 4> Offsets;
|
|
|
|
// The offset from ReusingBase's vbptr to itself always leads.
|
|
CharUnits VBPtrOffset = BaseLayout.getVBPtrOffset();
|
|
Offsets.push_back(
|
|
llvm::ConstantInt::get(CGM.IntTy, -VBPtrOffset.getQuantity()));
|
|
|
|
// These are laid out in the same order as in Itanium, which is the same as
|
|
// the order of the vbase iterator.
|
|
for (CXXRecordDecl::base_class_const_iterator I = ReusingBase->vbases_begin(),
|
|
E = ReusingBase->vbases_end(); I != E; ++I) {
|
|
const CXXRecordDecl *VBase = I->getType()->getAsCXXRecordDecl();
|
|
CharUnits Offset = DerivedLayout.getVBaseClassOffset(VBase);
|
|
assert(!Offset.isNegative());
|
|
// Make it relative to the subobject vbptr.
|
|
Offset -= VBPtrSubobject.getBaseOffset() + VBPtrOffset;
|
|
Offsets.push_back(llvm::ConstantInt::get(CGM.IntTy, Offset.getQuantity()));
|
|
}
|
|
|
|
assert(Offsets.size() ==
|
|
cast<llvm::ArrayType>(cast<llvm::PointerType>(GV->getType())
|
|
->getElementType())->getNumElements());
|
|
llvm::ArrayType *VBTableType =
|
|
llvm::ArrayType::get(CGM.IntTy, Offsets.size());
|
|
llvm::Constant *Init = llvm::ConstantArray::get(VBTableType, Offsets);
|
|
GV->setInitializer(Init);
|
|
|
|
// Set the correct linkage.
|
|
GV->setLinkage(Linkage);
|
|
|
|
// Set the right visibility.
|
|
CGM.setTypeVisibility(GV, RD, CodeGenModule::TVK_ForVTable);
|
|
}
|
|
|
|
} // namespace CodeGen
|
|
} // namespace clang
|