r198380 - [ms-cxxabi] Move VBTableBuilder from CodeGen over to AST/VTableBuilder.cpp
Reid Kleckner
reid at kleckner.net
Thu Jan 2 16:14:35 PST 2014
Author: rnk
Date: Thu Jan 2 18:14:35 2014
New Revision: 198380
URL: http://llvm.org/viewvc/llvm-project?rev=198380&view=rev
Log:
[ms-cxxabi] Move VBTableBuilder from CodeGen over to AST/VTableBuilder.cpp
Summary:
No functionality change.
This code should live here long-term because we should be able to use it
to compute correct vftable names.
It turns out that the most natural way to implement the naming algorithm
is to use a caching layer similar to what we already have for virtual
table info in VTableContext. Subsequent changes will take advantage of
this to fix PR17748, where we have a vbtable name collision.
Reviewers: majnemer
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D2499
Removed:
cfe/trunk/lib/CodeGen/MicrosoftVBTables.cpp
cfe/trunk/lib/CodeGen/MicrosoftVBTables.h
Modified:
cfe/trunk/include/clang/AST/VTableBuilder.h
cfe/trunk/lib/AST/VTableBuilder.cpp
cfe/trunk/lib/CodeGen/CMakeLists.txt
cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
Modified: cfe/trunk/include/clang/AST/VTableBuilder.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/VTableBuilder.h?rev=198380&r1=198379&r2=198380&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/VTableBuilder.h (original)
+++ cfe/trunk/include/clang/AST/VTableBuilder.h Thu Jan 2 18:14:35 2014
@@ -20,7 +20,7 @@
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/ABI.h"
#include "llvm/ADT/SetVector.h"
-#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/DenseMap.h"
#include <utility>
namespace clang {
@@ -409,6 +409,43 @@ struct VFPtrInfo {
CharUnits VFPtrFullOffset;
};
+/// Holds information for a virtual base table for a single subobject. A record
+/// may contain as many vbptrs as there are base subobjects.
+struct VBTableInfo {
+ VBTableInfo(const CXXRecordDecl *ReusingBase, BaseSubobject VBPtrSubobject)
+ : ReusingBase(ReusingBase), VBPtrSubobject(VBPtrSubobject) { }
+
+ /// The vbtable will hold all of the virtual bases of ReusingBase. This may
+ /// or may not be the same class as VBPtrSubobject.Base. A derived class will
+ /// reuse the vbptr of the first non-virtual base subobject that has one.
+ const CXXRecordDecl *ReusingBase;
+
+ /// The vbptr is stored inside this subobject.
+ BaseSubobject VBPtrSubobject;
+
+ /// The bases from the inheritance path that got used to mangle the vbtable
+ /// name. This 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> MangledPath;
+};
+
+// FIXME: Don't store these by value, they contain vectors.
+typedef SmallVector<VBTableInfo, 2> VBTableVector;
+
+/// All virtual base related information about a given record decl. Includes
+/// information on all virtual base tables and the path components that are used
+/// to mangle them.
+struct VirtualBaseInfo {
+ /// A map from virtual base to vbtable index for doing a conversion from the
+ /// the derived class to the a base.
+ llvm::DenseMap<const CXXRecordDecl *, unsigned> VBTableIndices;
+
+ /// Information on all virtual base tables used when this record is the most
+ /// derived class.
+ VBTableVector VBTables;
+};
+
class MicrosoftVTableContext : public VTableContextBase {
public:
struct MethodVFTableLocation {
@@ -465,6 +502,8 @@ private:
typedef llvm::DenseMap<VFTableIdTy, const VTableLayout *> VFTableLayoutMapTy;
VFTableLayoutMapTy VFTableLayouts;
+ llvm::DenseMap<const CXXRecordDecl *, VirtualBaseInfo *> VBaseInfo;
+
typedef llvm::SmallSetVector<const CXXRecordDecl *, 8> BasesSetVectorTy;
void enumerateVFPtrs(const CXXRecordDecl *MostDerivedClass,
const ASTRecordLayout &MostDerivedClassLayout,
@@ -482,18 +521,14 @@ private:
const MethodVFTableLocationsTy &NewMethods,
raw_ostream &);
- typedef std::pair<const CXXRecordDecl *, const CXXRecordDecl *> ClassPairTy;
- typedef llvm::DenseMap<ClassPairTy, unsigned> VBTableIndicesTy;
- VBTableIndicesTy VBTableIndices;
- llvm::DenseSet<const CXXRecordDecl *> ComputedVBTableIndices;
-
- void computeVBTableRelatedInformation(const CXXRecordDecl *RD);
+ const VirtualBaseInfo *
+ computeVBTableRelatedInformation(const CXXRecordDecl *RD);
public:
MicrosoftVTableContext(ASTContext &Context)
: VTableContextBase(/*MS=*/true), Context(Context) {}
- ~MicrosoftVTableContext() { llvm::DeleteContainerSeconds(VFTableLayouts); }
+ ~MicrosoftVTableContext();
const VFPtrListTy &getVFPtrOffsets(const CXXRecordDecl *RD);
@@ -515,16 +550,13 @@ public:
/// 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.
unsigned getVBTableIndex(const CXXRecordDecl *Derived,
- const CXXRecordDecl *VBase) {
- computeVBTableRelatedInformation(Derived);
- ClassPairTy Pair(Derived, VBase);
- assert(VBTableIndices.count(Pair) == 1 &&
- "VBase must be a vbase of Derived");
- return VBTableIndices[Pair];
- }
+ const CXXRecordDecl *VBase);
+
+ const VBTableVector &enumerateVBTables(const CXXRecordDecl *RD);
static bool classof(const VTableContextBase *VT) { return VT->isMicrosoft(); }
};
-}
+
+} // namespace clang
#endif
Modified: cfe/trunk/lib/AST/VTableBuilder.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/VTableBuilder.cpp?rev=198380&r1=198379&r2=198380&view=diff
==============================================================================
--- cfe/trunk/lib/AST/VTableBuilder.cpp (original)
+++ cfe/trunk/lib/AST/VTableBuilder.cpp Thu Jan 2 18:14:35 2014
@@ -2633,6 +2633,8 @@ public:
void dumpLayout(raw_ostream &);
};
+} // end namespace
+
/// InitialOverriddenDefinitionCollector - Finds the set of least derived bases
/// that define the given method.
struct InitialOverriddenDefinitionCollector {
@@ -2997,6 +2999,7 @@ void PrintBasePath(const VFPtrInfo::Base
}
}
+namespace {
struct MicrosoftThunkInfoStableSortComparator {
bool operator() (const ThunkInfo &LHS, const ThunkInfo &RHS) {
if (LHS.This != RHS.This)
@@ -3010,6 +3013,7 @@ struct MicrosoftThunkInfoStableSortCompa
return false;
}
};
+}
static void dumpMicrosoftThunkAdjustment(const ThunkInfo &TI, raw_ostream &Out,
bool ContinueFirstLine) {
@@ -3160,6 +3164,220 @@ void VFTableBuilder::dumpLayout(raw_ostr
}
}
}
+
+namespace {
+
+struct VBTablePath;
+typedef llvm::SmallVector<VBTablePath *, 6> VBTablePathVector;
+
+/// Produces MSVC-compatible vbtable data. The symbols produced by this builder
+/// match those produced by MSVC 2012, which is different from MSVC 2010.
+///
+/// Unlike Itanium, which uses only one vtable per class, MSVC uses a different
+/// symbol for every "address point" installed in base subobjects. As a result,
+/// we have to compute unique symbols for every table. Since there can be
+/// multiple non-virtual base subobjects of the same class, combining the most
+/// derived class with the base containing the vtable is insufficient. The most
+/// trivial algorithm would be to mangle in the entire path from base to most
+/// derived, but that would be too easy and would create unnecessarily large
+/// symbols. ;)
+///
+/// MSVC 2012 appears to minimize the vbtable names using the following
+/// algorithm. First, walk the class hierarchy in the usual order, depth first,
+/// left to right, to find all of the subobjects which contain a vbptr field.
+/// Visiting each class node yields a list of inheritance paths to vbptrs. Each
+/// record with a vbptr creates an initially empty path.
+///
+/// To combine paths from child nodes, the paths are compared to check for
+/// ambiguity. Paths are "ambiguous" if multiple paths have the same set of
+/// components in the same order. Each group of ambiguous paths is extended by
+/// appending the class of the base from which it came. If the current class
+/// node produced an ambiguous path, its path is extended with the current class.
+/// After extending paths, MSVC again checks for ambiguity, and extends any
+/// ambiguous path which wasn't already extended. Because each node yields an
+/// unambiguous set of paths, MSVC doesn't need to extend any path more than once
+/// to produce an unambiguous set of paths.
+///
+/// The VBTableBuilder class attempts to implement this algorithm by repeatedly
+/// bucketing paths together by sorting them.
+///
+/// TODO: Presumably vftables use the same algorithm.
+///
+/// TODO: Implement the MSVC 2010 name mangling scheme to avoid emitting
+/// duplicate vbtables with different symbols.
+
+class VBTableBuilder {
+public:
+ VBTableBuilder(ASTContext &Context, const CXXRecordDecl *MostDerived);
+
+ void enumerateVBTables(VBTableVector &VBTables);
+
+private:
+ bool hasVBPtr(const CXXRecordDecl *RD);
+
+ /// Enumerates paths to bases with vbptrs. The paths elements are compressed
+ /// to contain only the classes necessary to form an unambiguous path.
+ void findUnambiguousPaths(const CXXRecordDecl *ReusingBase,
+ BaseSubobject CurSubobject,
+ VBTablePathVector &Paths);
+
+ void extendPath(VBTablePath *Info, bool SecondPass);
+
+ bool rebucketPaths(VBTablePathVector &Paths, size_t PathsStart,
+ bool SecondPass = false);
+
+ ASTContext &Context;
+
+ const CXXRecordDecl *MostDerived;
+
+ /// Caches the layout of the most derived class.
+ const ASTRecordLayout &DerivedLayout;
+
+ /// Set of vbases to avoid re-visiting the same vbases.
+ llvm::SmallPtrSet<const CXXRecordDecl*, 4> VBasesSeen;
+};
+
+/// 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;
+};
+
+} // end namespace
+
+VBTableBuilder::VBTableBuilder(ASTContext &Context,
+ const CXXRecordDecl *MostDerived)
+ : Context(Context), MostDerived(MostDerived),
+ DerivedLayout(Context.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;
+ VBTables.push_back(P->VBInfo);
+ }
+}
+
+
+void VBTableBuilder::findUnambiguousPaths(const CXXRecordDecl *ReusingBase,
+ BaseSubobject CurSubobject,
+ VBTablePathVector &Paths) {
+ size_t PathsStart = Paths.size();
+ bool ReuseVBPtrFromBase = true;
+ const CXXRecordDecl *CurBase = CurSubobject.getBase();
+ const ASTRecordLayout &Layout = Context.getASTRecordLayout(CurBase);
+
+ // If this base has a vbptr, then we've found a path. These are not full
+ // paths, so we don't use CXXBasePath.
+ if (Layout.hasOwnVBPtr()) {
+ ReuseVBPtrFromBase = false;
+ VBTablePath *Info = new VBTablePath(VBTableInfo(ReusingBase, CurSubobject));
+ Paths.push_back(Info);
+ }
+
+ // Recurse onto any bases which themselves have virtual bases.
+ 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]->VBInfo.MangledPath != Paths[I - 1]->VBInfo.MangledPath &&
+ "vbtable paths are not unique");
+ }
+#endif
+}
+
+static bool pathCompare(VBTablePath *LHS, VBTablePath *RHS) {
+ return LHS->VBInfo.MangledPath < RHS->VBInfo.MangledPath;
+}
+
+void VBTableBuilder::extendPath(VBTablePath *P, bool SecondPass) {
+ assert(P->NextBase || SecondPass);
+ if (P->NextBase) {
+ P->VBInfo.MangledPath.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]->VBInfo.MangledPath ==
+ PathsSorted[I]->VBInfo.MangledPath);
+
+ // 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;
+}
+
+MicrosoftVTableContext::~MicrosoftVTableContext() {
+ llvm::DeleteContainerSeconds(VFTableLayouts);
+ llvm::DeleteContainerSeconds(VBaseInfo);
}
void MicrosoftVTableContext::enumerateVFPtrs(
@@ -3373,39 +3591,51 @@ void MicrosoftVTableContext::dumpMethodL
}
}
-void MicrosoftVTableContext::computeVBTableRelatedInformation(
+const VirtualBaseInfo *MicrosoftVTableContext::computeVBTableRelatedInformation(
const CXXRecordDecl *RD) {
- if (ComputedVBTableIndices.count(RD))
- return;
- ComputedVBTableIndices.insert(RD);
+ VirtualBaseInfo *&Entry = VBaseInfo[RD];
+ if (Entry)
+ return Entry;
- const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
- BasesSetVectorTy VisitedBases;
+ Entry = new VirtualBaseInfo();
+
+ VBTableBuilder(Context, RD).enumerateVBTables(Entry->VBTables);
// First, see if the Derived class shared the vbptr with a non-virtual base.
+ const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
if (const CXXRecordDecl *VBPtrBase = Layout.getBaseSharingVBPtr()) {
- // If the Derived class shares the vbptr with a non-virtual base,
- // it inherits its vbase indices.
- computeVBTableRelatedInformation(VBPtrBase);
- for (CXXRecordDecl::base_class_const_iterator I = VBPtrBase->vbases_begin(),
- E = VBPtrBase->vbases_end(); I != E; ++I) {
- const CXXRecordDecl *SubVBase = I->getType()->getAsCXXRecordDecl();
- assert(VBTableIndices.count(ClassPairTy(VBPtrBase, SubVBase)));
- VBTableIndices[ClassPairTy(RD, SubVBase)] =
- VBTableIndices[ClassPairTy(VBPtrBase, SubVBase)];
- VisitedBases.insert(SubVBase);
- }
+ // If the Derived class shares the vbptr with a non-virtual base, the shared
+ // virtual bases come first so that the layout is the same.
+ const VirtualBaseInfo *BaseInfo =
+ computeVBTableRelatedInformation(VBPtrBase);
+ Entry->VBTableIndices.insert(BaseInfo->VBTableIndices.begin(),
+ BaseInfo->VBTableIndices.end());
}
// New vbases are added to the end of the vbtable.
// Skip the self entry and vbases visited in the non-virtual base, if any.
- unsigned VBTableIndex = 1 + VisitedBases.size();
+ unsigned VBTableIndex = 1 + Entry->VBTableIndices.size();
for (CXXRecordDecl::base_class_const_iterator I = RD->vbases_begin(),
- E = RD->vbases_end(); I != E; ++I) {
+ E = RD->vbases_end();
+ I != E; ++I) {
const CXXRecordDecl *CurVBase = I->getType()->getAsCXXRecordDecl();
- if (VisitedBases.insert(CurVBase))
- VBTableIndices[ClassPairTy(RD, CurVBase)] = VBTableIndex++;
+ if (!Entry->VBTableIndices.count(CurVBase))
+ Entry->VBTableIndices[CurVBase] = VBTableIndex++;
}
+
+ return Entry;
+}
+
+unsigned MicrosoftVTableContext::getVBTableIndex(const CXXRecordDecl *Derived,
+ const CXXRecordDecl *VBase) {
+ const VirtualBaseInfo *VBInfo = computeVBTableRelatedInformation(Derived);
+ assert(VBInfo->VBTableIndices.count(VBase));
+ return VBInfo->VBTableIndices.find(VBase)->second;
+}
+
+const VBTableVector &
+MicrosoftVTableContext::enumerateVBTables(const CXXRecordDecl *RD) {
+ return computeVBTableRelatedInformation(RD)->VBTables;
}
const MicrosoftVTableContext::VFPtrListTy &
Modified: cfe/trunk/lib/CodeGen/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CMakeLists.txt?rev=198380&r1=198379&r2=198380&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CMakeLists.txt (original)
+++ cfe/trunk/lib/CodeGen/CMakeLists.txt Thu Jan 2 18:14:35 2014
@@ -55,7 +55,6 @@ add_clang_library(clangCodeGen
CodeGenTypes.cpp
ItaniumCXXABI.cpp
MicrosoftCXXABI.cpp
- MicrosoftVBTables.cpp
ModuleBuilder.cpp
TargetInfo.cpp
)
Modified: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=198380&r1=198379&r2=198380&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp Thu Jan 2 18:14:35 2014
@@ -17,7 +17,6 @@
#include "CGCXXABI.h"
#include "CodeGenModule.h"
#include "CGVTables.h"
-#include "MicrosoftVBTables.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/VTableBuilder.h"
@@ -28,6 +27,12 @@ using namespace CodeGen;
namespace {
+/// Holds all the vbtable globals for a given class.
+struct VBTableGlobals {
+ const VBTableVector *VBTables;
+ SmallVector<llvm::GlobalVariable *, 2> Globals;
+};
+
class MicrosoftCXXABI : public CGCXXABI {
public:
MicrosoftCXXABI(CodeGenModule &CGM) : CGCXXABI(CGM) {}
@@ -189,6 +194,13 @@ public:
void emitVirtualInheritanceTables(const CXXRecordDecl *RD);
+ llvm::GlobalVariable *
+ getAddrOfVBTable(const VBTableInfo &VBT, const CXXRecordDecl *RD,
+ llvm::GlobalVariable::LinkageTypes Linkage);
+
+ void emitVBTableDefinition(const VBTableInfo &VBT, const CXXRecordDecl *RD,
+ llvm::GlobalVariable *GV) const;
+
void setThunkLinkage(llvm::Function *Thunk, bool ForVTable) {
Thunk->setLinkage(llvm::GlobalValue::WeakAnyLinkage);
}
@@ -312,7 +324,7 @@ private:
void EmitVBPtrStores(CodeGenFunction &CGF, const CXXRecordDecl *RD);
/// \brief Caching wrapper around VBTableBuilder::enumerateVBTables().
- const VBTableVector &EnumerateVBTables(const CXXRecordDecl *RD);
+ const VBTableGlobals &enumerateVBTables(const CXXRecordDecl *RD);
/// \brief Generate a thunk for calling a virtual member function MD.
llvm::Function *EmitVirtualMemPtrThunk(const CXXMethodDecl *MD,
@@ -369,7 +381,7 @@ private:
/// \brief All the vbtables which have been referenced.
- llvm::DenseMap<const CXXRecordDecl *, VBTableVector> VBTablesMap;
+ llvm::DenseMap<const CXXRecordDecl *, VBTableGlobals> VBTablesMap;
/// Info on the global variable used to guard initialization of static locals.
/// The BitIndex field is only used for externally invisible declarations.
@@ -550,21 +562,23 @@ void MicrosoftCXXABI::EmitCXXConstructor
void MicrosoftCXXABI::EmitVBPtrStores(CodeGenFunction &CGF,
const CXXRecordDecl *RD) {
+ llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD);
llvm::Value *ThisInt8Ptr =
CGF.Builder.CreateBitCast(getThisValue(CGF), CGM.Int8PtrTy, "this.int8");
- const VBTableVector &VBTables = EnumerateVBTables(RD);
- for (VBTableVector::const_iterator I = VBTables.begin(), E = VBTables.end();
- I != E; ++I) {
+ const VBTableGlobals &VBGlobals = enumerateVBTables(RD);
+ for (unsigned I = 0, E = VBGlobals.VBTables->size(); I != E; ++I) {
+ const VBTableInfo &VBT = (*VBGlobals.VBTables)[I];
+ llvm::GlobalVariable *GV = VBGlobals.Globals[I];
const ASTRecordLayout &SubobjectLayout =
- CGM.getContext().getASTRecordLayout(I->VBPtrSubobject.getBase());
- uint64_t Offs = (I->VBPtrSubobject.getBaseOffset() +
+ CGM.getContext().getASTRecordLayout(VBT.VBPtrSubobject.getBase());
+ uint64_t Offs = (VBT.VBPtrSubobject.getBaseOffset() +
SubobjectLayout.getVBPtrOffset()).getQuantity();
llvm::Value *VBPtr =
CGF.Builder.CreateConstInBoundsGEP1_64(ThisInt8Ptr, Offs);
- VBPtr = CGF.Builder.CreateBitCast(VBPtr, I->GV->getType()->getPointerTo(0),
- "vbptr." + I->ReusingBase->getName());
- CGF.Builder.CreateStore(I->GV, VBPtr);
+ VBPtr = CGF.Builder.CreateBitCast(VBPtr, GV->getType()->getPointerTo(0),
+ "vbptr." + VBT.ReusingBase->getName());
+ CGF.Builder.CreateStore(GV, VBPtr);
}
}
@@ -986,20 +1000,30 @@ void MicrosoftCXXABI::EmitVirtualDestruc
ImplicitParam, Context.IntTy, 0, 0);
}
-const VBTableVector &
-MicrosoftCXXABI::EnumerateVBTables(const CXXRecordDecl *RD) {
+const VBTableGlobals &
+MicrosoftCXXABI::enumerateVBTables(const CXXRecordDecl *RD) {
// At this layer, we can key the cache off of a single class, which is much
- // easier than caching at the GlobalVariable layer.
- llvm::DenseMap<const CXXRecordDecl*, VBTableVector>::iterator I;
- bool added;
- llvm::tie(I, added) = VBTablesMap.insert(std::make_pair(RD, VBTableVector()));
- VBTableVector &VBTables = I->second;
- if (!added)
- return VBTables;
+ // easier than caching each vbtable individually.
+ llvm::DenseMap<const CXXRecordDecl*, VBTableGlobals>::iterator Entry;
+ bool Added;
+ llvm::tie(Entry, Added) = VBTablesMap.insert(std::make_pair(RD, VBTableGlobals()));
+ VBTableGlobals &VBGlobals = Entry->second;
+ if (!Added)
+ return VBGlobals;
- VBTableBuilder(CGM, RD).enumerateVBTables(VBTables);
+ MicrosoftVTableContext &Context = CGM.getMicrosoftVTableContext();
+ VBGlobals.VBTables = &Context.enumerateVBTables(RD);
- return VBTables;
+ // Cache the globals for all vbtables so we don't have to recompute the
+ // mangled names.
+ llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD);
+ for (VBTableVector::const_iterator I = VBGlobals.VBTables->begin(),
+ E = VBGlobals.VBTables->end();
+ I != E; ++I) {
+ VBGlobals.Globals.push_back(getAddrOfVBTable(*I, RD, Linkage));
+ }
+
+ return VBGlobals;
}
llvm::Function *
@@ -1039,13 +1063,80 @@ MicrosoftCXXABI::EmitVirtualMemPtrThunk(
}
void MicrosoftCXXABI::emitVirtualInheritanceTables(const CXXRecordDecl *RD) {
- const VBTableVector &VBTables = EnumerateVBTables(RD);
- llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD);
+ const VBTableGlobals &VBGlobals = enumerateVBTables(RD);
+ for (unsigned I = 0, E = VBGlobals.VBTables->size(); I != E; ++I) {
+ const VBTableInfo &VBT = (*VBGlobals.VBTables)[I];
+ llvm::GlobalVariable *GV = VBGlobals.Globals[I];
+ emitVBTableDefinition(VBT, RD, GV);
+ }
+}
- for (VBTableVector::const_iterator I = VBTables.begin(), E = VBTables.end();
+llvm::GlobalVariable *
+MicrosoftCXXABI::getAddrOfVBTable(const VBTableInfo &VBT,
+ const CXXRecordDecl *RD,
+ llvm::GlobalVariable::LinkageTypes Linkage) {
+ SmallString<256> OutName;
+ llvm::raw_svector_ostream Out(OutName);
+ MicrosoftMangleContext &Mangler =
+ cast<MicrosoftMangleContext>(CGM.getCXXABI().getMangleContext());
+ Mangler.mangleCXXVBTable(RD, VBT.MangledPath, Out);
+ Out.flush();
+ StringRef Name = OutName.str();
+
+ llvm::ArrayType *VBTableType =
+ llvm::ArrayType::get(CGM.IntTy, 1 + VBT.ReusingBase->getNumVBases());
+
+ assert(!CGM.getModule().getNamedGlobal(Name) &&
+ "vbtable with this name already exists: mangling bug?");
+ llvm::GlobalVariable *GV =
+ CGM.CreateOrReplaceCXXRuntimeVariable(Name, VBTableType, Linkage);
+ GV->setUnnamedAddr(true);
+ return GV;
+}
+
+void MicrosoftCXXABI::emitVBTableDefinition(const VBTableInfo &VBT,
+ const CXXRecordDecl *RD,
+ llvm::GlobalVariable *GV) const {
+ const CXXRecordDecl *ReusingBase = VBT.ReusingBase;
+
+ assert(RD->getNumVBases() && ReusingBase->getNumVBases() &&
+ "should only emit vbtables for classes with vbtables");
+
+ const ASTRecordLayout &BaseLayout =
+ CGM.getContext().getASTRecordLayout(VBT.VBPtrSubobject.getBase());
+ const ASTRecordLayout &DerivedLayout =
+ CGM.getContext().getASTRecordLayout(RD);
+
+ SmallVector<llvm::Constant *, 4> Offsets(1 + ReusingBase->getNumVBases(), 0);
+
+ // The offset from ReusingBase's vbptr to itself always leads.
+ CharUnits VBPtrOffset = BaseLayout.getVBPtrOffset();
+ Offsets[0] = llvm::ConstantInt::get(CGM.IntTy, -VBPtrOffset.getQuantity());
+
+ MicrosoftVTableContext &Context = CGM.getMicrosoftVTableContext();
+ for (CXXRecordDecl::base_class_const_iterator I = ReusingBase->vbases_begin(),
+ E = ReusingBase->vbases_end();
I != E; ++I) {
- I->EmitVBTableDefinition(CGM, RD, Linkage);
- }
+ const CXXRecordDecl *VBase = I->getType()->getAsCXXRecordDecl();
+ CharUnits Offset = DerivedLayout.getVBaseClassOffset(VBase);
+ assert(!Offset.isNegative());
+ // Make it relative to the subobject vbptr.
+ Offset -= VBT.VBPtrSubobject.getBaseOffset() + VBPtrOffset;
+ unsigned VBIndex = Context.getVBTableIndex(ReusingBase, VBase);
+ assert(Offsets[VBIndex] == 0 && "The same vbindex seen twice?");
+ Offsets[VBIndex] = 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 right visibility.
+ CGM.setTypeVisibility(GV, RD, CodeGenModule::TVK_ForVTable);
}
llvm::Value *MicrosoftCXXABI::performThisAdjustment(CodeGenFunction &CGF,
Removed: cfe/trunk/lib/CodeGen/MicrosoftVBTables.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftVBTables.cpp?rev=198379&view=auto
==============================================================================
--- cfe/trunk/lib/CodeGen/MicrosoftVBTables.cpp (original)
+++ cfe/trunk/lib/CodeGen/MicrosoftVBTables.cpp (removed)
@@ -1,233 +0,0 @@
-//===--- 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);
- }
-}
-
-
-void VBTableBuilder::findUnambiguousPaths(const CXXRecordDecl *ReusingBase,
- BaseSubobject CurSubobject,
- VBTablePathVector &Paths) {
- size_t PathsStart = Paths.size();
- bool ReuseVBPtrFromBase = true;
- const CXXRecordDecl *CurBase = CurSubobject.getBase();
- const ASTRecordLayout &Layout = CGM.getContext().getASTRecordLayout(CurBase);
-
- // If this base has a vbptr, then we've found a path. These are not full
- // paths, so we don't use CXXBasePath.
- if (Layout.hasOwnVBPtr()) {
- ReuseVBPtrFromBase = false;
- VBTablePath *Info = new VBTablePath(
- VBTableInfo(ReusingBase, CurSubobject, /*GV=*/0));
- Paths.push_back(Info);
- }
-
- // Recurse onto any bases which themselves have virtual bases.
- 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);
- MicrosoftMangleContext &Mangler =
- cast<MicrosoftMangleContext>(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(1 + ReusingBase->getNumVBases(), 0);
-
- // The offset from ReusingBase's vbptr to itself always leads.
- CharUnits VBPtrOffset = BaseLayout.getVBPtrOffset();
- Offsets[0] = llvm::ConstantInt::get(CGM.IntTy, -VBPtrOffset.getQuantity());
-
- MicrosoftVTableContext &Context = CGM.getMicrosoftVTableContext();
- 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;
- unsigned VBIndex = Context.getVBTableIndex(ReusingBase, VBase);
- assert(Offsets[VBIndex] == 0 && "The same vbindex seen twice?");
- Offsets[VBIndex] = 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
Removed: cfe/trunk/lib/CodeGen/MicrosoftVBTables.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftVBTables.h?rev=198379&view=auto
==============================================================================
--- cfe/trunk/lib/CodeGen/MicrosoftVBTables.h (original)
+++ cfe/trunk/lib/CodeGen/MicrosoftVBTables.h (removed)
@@ -1,129 +0,0 @@
-//===--- MicrosoftVBTables.h - 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 "clang/AST/BaseSubobject.h"
-#include "clang/Basic/LLVM.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/IR/GlobalVariable.h"
-#include <vector>
-
-namespace clang {
-
-class ASTRecordLayout;
-
-namespace CodeGen {
-
-class CodeGenModule;
-
-struct VBTableInfo {
- VBTableInfo(const CXXRecordDecl *ReusingBase, BaseSubobject VBPtrSubobject,
- llvm::GlobalVariable *GV)
- : ReusingBase(ReusingBase), VBPtrSubobject(VBPtrSubobject), GV(GV) { }
-
- /// The vbtable will hold all of the virtual bases of ReusingBase. This may
- /// or may not be the same class as VBPtrSubobject.Base. A derived class will
- /// reuse the vbptr of the first non-virtual base subobject that has one.
- const CXXRecordDecl *ReusingBase;
-
- /// The vbptr is stored inside this subobject.
- BaseSubobject VBPtrSubobject;
-
- /// The GlobalVariable for this vbtable.
- llvm::GlobalVariable *GV;
-
- /// \brief Emits a definition for GV by setting it's initializer.
- void EmitVBTableDefinition(CodeGenModule &CGM, const CXXRecordDecl *RD,
- llvm::GlobalVariable::LinkageTypes Linkage) const;
-};
-
-// These are embedded in a DenseMap and the elements are large, so we don't want
-// SmallVector.
-typedef std::vector<VBTableInfo> VBTableVector;
-
-struct VBTablePath;
-
-typedef llvm::SmallVector<VBTablePath *, 6> VBTablePathVector;
-
-/// Produces MSVC-compatible vbtable data. The symbols produced by this builder
-/// match those produced by MSVC 2012, which is different from MSVC 2010.
-///
-/// Unlike Itanium, which uses only one vtable per class, MSVC uses a different
-/// symbol for every "address point" installed in base subobjects. As a result,
-/// we have to compute unique symbols for every table. Since there can be
-/// multiple non-virtual base subobjects of the same class, combining the most
-/// derived class with the base containing the vtable is insufficient. The most
-/// trivial algorithm would be to mangle in the entire path from base to most
-/// derived, but that would be too easy and would create unnecessarily large
-/// symbols. ;)
-///
-/// MSVC 2012 appears to minimize the vbtable names using the following
-/// algorithm. First, walk the class hierarchy in the usual order, depth first,
-/// left to right, to find all of the subobjects which contain a vbptr field.
-/// Visiting each class node yields a list of inheritance paths to vbptrs. Each
-/// record with a vbptr creates an initially empty path.
-///
-/// To combine paths from child nodes, the paths are compared to check for
-/// ambiguity. Paths are "ambiguous" if multiple paths have the same set of
-/// components in the same order. Each group of ambiguous paths is extended by
-/// appending the class of the base from which it came. If the current class
-/// node produced an ambiguous path, its path is extended with the current class.
-/// After extending paths, MSVC again checks for ambiguity, and extends any
-/// ambiguous path which wasn't already extended. Because each node yields an
-/// unambiguous set of paths, MSVC doesn't need to extend any path more than once
-/// to produce an unambiguous set of paths.
-///
-/// The VBTableBuilder class attempts to implement this algorithm by repeatedly
-/// bucketing paths together by sorting them.
-///
-/// TODO: Presumably vftables use the same algorithm.
-///
-/// TODO: Implement the MSVC 2010 name mangling scheme to avoid emitting
-/// duplicate vbtables with different symbols.
-class VBTableBuilder {
-public:
- VBTableBuilder(CodeGenModule &CGM, const CXXRecordDecl *MostDerived);
-
- void enumerateVBTables(VBTableVector &VBTables);
-
-private:
- bool hasVBPtr(const CXXRecordDecl *RD);
-
- llvm::GlobalVariable *getAddrOfVBTable(const CXXRecordDecl *ReusingBase,
- ArrayRef<const CXXRecordDecl *> BasePath);
-
- /// Enumerates paths to bases with vbptrs. The paths elements are compressed
- /// to contain only the classes necessary to form an unambiguous path.
- void findUnambiguousPaths(const CXXRecordDecl *ReusingBase,
- BaseSubobject CurSubobject,
- VBTablePathVector &Paths);
-
- void extendPath(VBTablePath *Info, bool SecondPass);
-
- bool rebucketPaths(VBTablePathVector &Paths, size_t PathsStart,
- bool SecondPass = false);
-
- CodeGenModule &CGM;
-
- const CXXRecordDecl *MostDerived;
-
- /// Caches the layout of the most derived class.
- const ASTRecordLayout &DerivedLayout;
-
- /// Set of vbases to avoid re-visiting the same vbases.
- llvm::SmallPtrSet<const CXXRecordDecl*, 4> VBasesSeen;
-};
-
-} // namespace CodeGen
-
-} // namespace clang
More information about the cfe-commits
mailing list