diff options
-rw-r--r-- | clang/include/clang/AST/Mangle.h | 6 | ||||
-rw-r--r-- | clang/lib/AST/ItaniumMangle.cpp | 10 | ||||
-rw-r--r-- | clang/lib/AST/MicrosoftMangle.cpp | 36 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGCXXABI.cpp | 5 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGCXXABI.h | 10 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGClass.cpp | 3 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGVTables.cpp | 10 | ||||
-rw-r--r-- | clang/lib/CodeGen/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/lib/CodeGen/ItaniumCXXABI.cpp | 10 | ||||
-rw-r--r-- | clang/lib/CodeGen/MicrosoftCXXABI.cpp | 75 | ||||
-rw-r--r-- | clang/lib/CodeGen/MicrosoftVBTables.cpp | 236 | ||||
-rw-r--r-- | clang/lib/CodeGen/MicrosoftVBTables.h | 129 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/microsoft-abi-structors.cpp | 15 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/microsoft-abi-vbtables.cpp | 436 |
14 files changed, 956 insertions, 26 deletions
diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h index b6d22cfb5fd..98e03f5dece 100644 --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -110,6 +110,12 @@ public: raw_ostream &) = 0; virtual void mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) = 0; + /// \brief Mangle vbtable symbols. Only a subset of the bases along the path + /// to the vbtable are included in the name. It's up to the caller to pick + /// them correctly. + virtual void mangleCXXVBTable(const CXXRecordDecl *Derived, + ArrayRef<const CXXRecordDecl *> BasePath, + raw_ostream &Out) = 0; virtual void mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset, const CXXRecordDecl *Type, raw_ostream &) = 0; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 1d58e8df041..065465aa59e 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -130,6 +130,9 @@ public: raw_ostream &); void mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &); + void mangleCXXVBTable(const CXXRecordDecl *Derived, + ArrayRef<const CXXRecordDecl *> BasePath, + raw_ostream &Out); void mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset, const CXXRecordDecl *Type, raw_ostream &); @@ -3597,6 +3600,13 @@ void ItaniumMangleContext::mangleCXXVTT(const CXXRecordDecl *RD, Mangler.mangleNameOrStandardSubstitution(RD); } +void +ItaniumMangleContext::mangleCXXVBTable(const CXXRecordDecl *Derived, + ArrayRef<const CXXRecordDecl *> BasePath, + raw_ostream &Out) { + llvm_unreachable("The Itanium C++ ABI does not have virtual base tables!"); +} + void ItaniumMangleContext::mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset, const CXXRecordDecl *Type, diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 976fcf8ad69..5e488e042e7 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -155,6 +155,9 @@ public: raw_ostream &); virtual void mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &); + virtual void mangleCXXVBTable(const CXXRecordDecl *Derived, + ArrayRef<const CXXRecordDecl *> BasePath, + raw_ostream &Out); virtual void mangleCXXCtorVTable(const CXXRecordDecl *RD, int64_t Offset, const CXXRecordDecl *Type, raw_ostream &); @@ -1757,24 +1760,41 @@ void MicrosoftMangleContext::mangleCXXDtorThunk(const CXXDestructorDecl *DD, "cannot mangle thunk for this destructor yet"); getDiags().Report(DD->getLocation(), DiagID); } + void MicrosoftMangleContext::mangleCXXVTable(const CXXRecordDecl *RD, raw_ostream &Out) { - // <mangled-name> ::= ? <operator-name> <class-name> <storage-class> - // <cvr-qualifiers> [<name>] @ - // <operator-name> ::= _7 # vftable - // ::= _8 # vbtable + // <mangled-name> ::= ?_7 <class-name> <storage-class> + // <cvr-qualifiers> [<name>] @ // NOTE: <cvr-qualifiers> here is always 'B' (const). <storage-class> - // is always '6' for vftables and '7' for vbtables. (The difference is - // beyond me.) - // TODO: vbtables. + // is always '6' for vftables. MicrosoftCXXNameMangler Mangler(*this, Out); Mangler.getStream() << "\01??_7"; Mangler.mangleName(RD); - Mangler.getStream() << "6B"; + Mangler.getStream() << "6B"; // '6' for vftable, 'B' for const. // TODO: If the class has more than one vtable, mangle in the class it came // from. Mangler.getStream() << '@'; } + +void MicrosoftMangleContext::mangleCXXVBTable( + const CXXRecordDecl *Derived, ArrayRef<const CXXRecordDecl *> BasePath, + raw_ostream &Out) { + // <mangled-name> ::= ?_8 <class-name> <storage-class> + // <cvr-qualifiers> [<name>] @ + // NOTE: <cvr-qualifiers> here is always 'B' (const). <storage-class> + // is always '7' for vbtables. + MicrosoftCXXNameMangler Mangler(*this, Out); + Mangler.getStream() << "\01??_8"; + Mangler.mangleName(Derived); + Mangler.getStream() << "7B"; // '7' for vbtable, 'B' for const. + for (ArrayRef<const CXXRecordDecl *>::iterator I = BasePath.begin(), + E = BasePath.end(); + I != E; ++I) { + Mangler.mangleName(*I); + } + Mangler.getStream() << '@'; +} + void MicrosoftMangleContext::mangleCXXVTT(const CXXRecordDecl *RD, raw_ostream &) { llvm_unreachable("The MS C++ ABI does not have virtual table tables!"); diff --git a/clang/lib/CodeGen/CGCXXABI.cpp b/clang/lib/CodeGen/CGCXXABI.cpp index 4a125e6d7fa..7f07344adf0 100644 --- a/clang/lib/CodeGen/CGCXXABI.cpp +++ b/clang/lib/CodeGen/CGCXXABI.cpp @@ -273,8 +273,9 @@ CharUnits CGCXXABI::getMemberPointerPathAdjustment(const APValue &MP) { return ThisAdjustment; } -llvm::BasicBlock *CGCXXABI::EmitCtorCompleteObjectHandler( - CodeGenFunction &CGF) { +llvm::BasicBlock * +CGCXXABI::EmitCtorCompleteObjectHandler(CodeGenFunction &CGF, + const CXXRecordDecl *RD) { if (CGM.getTarget().getCXXABI().hasConstructorVariants()) llvm_unreachable("shouldn't be called in this ABI"); diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h index 899e5e7b258..2e505d224fc 100644 --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -231,7 +231,8 @@ public: CanQualType &ResTy, SmallVectorImpl<CanQualType> &ArgTys) = 0; - virtual llvm::BasicBlock *EmitCtorCompleteObjectHandler(CodeGenFunction &CGF); + virtual llvm::BasicBlock *EmitCtorCompleteObjectHandler(CodeGenFunction &CGF, + const CXXRecordDecl *RD); /// Build the signature of the given destructor variant by adding /// any required parameters. For convenience, ArgTys has been initialized @@ -275,6 +276,13 @@ public: ReturnValueSlot ReturnValue, llvm::Value *This) = 0; + /// Emit any tables needed to implement virtual inheritance. For Itanium, + /// this emits virtual table tables. For the MSVC++ ABI, this emits virtual + /// base tables. + virtual void + EmitVirtualInheritanceTables(llvm::GlobalVariable::LinkageTypes Linkage, + const CXXRecordDecl *RD) = 0; + virtual void EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResultType); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 88f5cf9e6b5..ce4ead24194 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -1109,7 +1109,8 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD, !CGM.getTarget().getCXXABI().hasConstructorVariants()) { // The ABIs that don't have constructor variants need to put a branch // before the virtual base initialization code. - BaseCtorContinueBB = CGM.getCXXABI().EmitCtorCompleteObjectHandler(*this); + BaseCtorContinueBB = + CGM.getCXXABI().EmitCtorCompleteObjectHandler(*this, ClassDecl); assert(BaseCtorContinueBB); } diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index d1168770ddb..9c4a5e0cdad 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -813,14 +813,8 @@ CodeGenVTables::GenerateClassData(const CXXRecordDecl *RD) { llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD); EmitVTableDefinition(VTable, Linkage, RD); - if (RD->getNumVBases()) { - if (!CGM.getTarget().getCXXABI().isMicrosoft()) { - llvm::GlobalVariable *VTT = GetAddrOfVTT(RD); - EmitVTTDefinition(VTT, Linkage, RD); - } else { - // FIXME: Emit vbtables here. - } - } + if (RD->getNumVBases()) + CGM.getCXXABI().EmitVirtualInheritanceTables(Linkage, RD); // If this is the magic class __cxxabiv1::__fundamental_type_info, // we will emit the typeinfo for the fundamental types. This is the diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt index 9ca2295a922..e65c699a1f4 100644 --- a/clang/lib/CodeGen/CMakeLists.txt +++ b/clang/lib/CodeGen/CMakeLists.txt @@ -48,6 +48,7 @@ add_clang_library(clangCodeGen CodeGenTypes.cpp ItaniumCXXABI.cpp MicrosoftCXXABI.cpp + MicrosoftVBTables.cpp ModuleBuilder.cpp TargetInfo.cpp ) diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 72284007381..1952ee6012f 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -135,6 +135,9 @@ public: ReturnValueSlot ReturnValue, llvm::Value *This); + void EmitVirtualInheritanceTables(llvm::GlobalVariable::LinkageTypes Linkage, + const CXXRecordDecl *RD); + StringRef GetPureVirtualCallName() { return "__cxa_pure_virtual"; } StringRef GetDeletedVirtualCallName() { return "__cxa_deleted_virtual"; } @@ -841,6 +844,13 @@ RValue ItaniumCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF, /*ImplicitParam=*/0, QualType(), 0, 0); } +void ItaniumCXXABI::EmitVirtualInheritanceTables( + llvm::GlobalVariable::LinkageTypes Linkage, const CXXRecordDecl *RD) { + CodeGenVTables &VTables = CGM.getVTables(); + llvm::GlobalVariable *VTT = VTables.GetAddrOfVTT(RD); + VTables.EmitVTTDefinition(VTT, Linkage, RD); +} + void ARMCXXABI::EmitReturnFromThunk(CodeGenFunction &CGF, RValue RV, QualType ResultType) { if (!isa<CXXDestructorDecl>(CGF.CurGD.getDecl())) diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index e6eca28b29f..663011570d5 100644 --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -16,6 +16,8 @@ #include "CGCXXABI.h" #include "CodeGenModule.h" +#include "CGVTables.h" +#include "MicrosoftVBTables.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -60,7 +62,8 @@ public: CanQualType &ResTy, SmallVectorImpl<CanQualType> &ArgTys); - llvm::BasicBlock *EmitCtorCompleteObjectHandler(CodeGenFunction &CGF); + llvm::BasicBlock *EmitCtorCompleteObjectHandler(CodeGenFunction &CGF, + const CXXRecordDecl *RD); void BuildDestructorSignature(const CXXDestructorDecl *Ctor, CXXDtorType Type, @@ -89,6 +92,9 @@ public: ReturnValueSlot ReturnValue, llvm::Value *This); + void EmitVirtualInheritanceTables(llvm::GlobalVariable::LinkageTypes Linkage, + const CXXRecordDecl *RD); + void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, llvm::GlobalVariable *DeclPtr, bool PerformInit); @@ -184,6 +190,12 @@ private: bool MemberPointerConstantIsNull(const MemberPointerType *MPT, llvm::Constant *MP); + /// \brief - Initialize all vbptrs of 'this' with RD as the complete type. + void EmitVBPtrStores(CodeGenFunction &CGF, const CXXRecordDecl *RD); + + /// \brief Caching wrapper around VBTableBuilder::enumerateVBTables(). + const VBTableVector &EnumerateVBTables(const CXXRecordDecl *RD); + public: virtual llvm::Type *ConvertMemberPointerType(const MemberPointerType *MPT); @@ -224,6 +236,9 @@ public: llvm::Value *MemPtr, const MemberPointerType *MPT); +private: + /// VBTables - All the vbtables which have been referenced. + llvm::DenseMap<const CXXRecordDecl *, VBTableVector> VBTablesMap; }; } @@ -318,13 +333,14 @@ void MicrosoftCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor, } } -llvm::BasicBlock *MicrosoftCXXABI::EmitCtorCompleteObjectHandler( - CodeGenFunction &CGF) { +llvm::BasicBlock * +MicrosoftCXXABI::EmitCtorCompleteObjectHandler(CodeGenFunction &CGF, + const CXXRecordDecl *RD) { llvm::Value *IsMostDerivedClass = getStructorImplicitParamValue(CGF); assert(IsMostDerivedClass && "ctor for a class with virtual bases must have an implicit parameter"); - llvm::Value *IsCompleteObject - = CGF.Builder.CreateIsNotNull(IsMostDerivedClass, "is_complete_object"); + llvm::Value *IsCompleteObject = + CGF.Builder.CreateIsNotNull(IsMostDerivedClass, "is_complete_object"); llvm::BasicBlock *CallVbaseCtorsBB = CGF.createBasicBlock("ctor.init_vbases"); llvm::BasicBlock *SkipVbaseCtorsBB = CGF.createBasicBlock("ctor.skip_vbases"); @@ -332,13 +348,35 @@ llvm::BasicBlock *MicrosoftCXXABI::EmitCtorCompleteObjectHandler( CallVbaseCtorsBB, SkipVbaseCtorsBB); CGF.EmitBlock(CallVbaseCtorsBB); - // FIXME: emit vbtables somewhere around here. + + // Fill in the vbtable pointers here. + EmitVBPtrStores(CGF, RD); // CGF will put the base ctor calls in this basic block for us later. return SkipVbaseCtorsBB; } +void MicrosoftCXXABI::EmitVBPtrStores(CodeGenFunction &CGF, + const CXXRecordDecl *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 ASTRecordLayout &SubobjectLayout = + CGM.getContext().getASTRecordLayout(I->VBPtrSubobject.getBase()); + uint64_t Offs = (I->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); + } +} + void MicrosoftCXXABI::BuildDestructorSignature(const CXXDestructorDecl *Dtor, CXXDtorType Type, CanQualType &ResTy, @@ -470,6 +508,31 @@ RValue MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF, ImplicitParam, Context.BoolTy, 0, 0); } +const VBTableVector & +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; + + VBTableBuilder(CGM, RD).enumerateVBTables(VBTables); + + return VBTables; +} + +void MicrosoftCXXABI::EmitVirtualInheritanceTables( + llvm::GlobalVariable::LinkageTypes Linkage, const CXXRecordDecl *RD) { + const VBTableVector &VBTables = EnumerateVBTables(RD); + for (VBTableVector::const_iterator I = VBTables.begin(), E = VBTables.end(); + I != E; ++I) { + I->EmitVBTableDefinition(CGM, RD, Linkage); + } +} + bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr, QualType elementType) { // Microsoft seems to completely ignore the possibility of a diff --git a/clang/lib/CodeGen/MicrosoftVBTables.cpp b/clang/lib/CodeGen/MicrosoftVBTables.cpp new file mode 100644 index 00000000000..4523aa82b73 --- /dev/null +++ b/clang/lib/CodeGen/MicrosoftVBTables.cpp @@ -0,0 +1,236 @@ +//===--- 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 diff --git a/clang/lib/CodeGen/MicrosoftVBTables.h b/clang/lib/CodeGen/MicrosoftVBTables.h new file mode 100644 index 00000000000..4ad8e07582e --- /dev/null +++ b/clang/lib/CodeGen/MicrosoftVBTables.h @@ -0,0 +1,129 @@ +//===--- 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 diff --git a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp index 2c2f162540f..27c00063395 100644 --- a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp +++ b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp @@ -154,6 +154,10 @@ C::C() { // CHECK: br i1 %[[SHOULD_CALL_VBASE_CTORS]], label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]] // // CHECK: [[INIT_VBASES]] + // CHECK-NEXT: %[[this_i8:.*]] = bitcast %"struct.constructors::C"* %{{.*}} to i8* + // CHECK-NEXT: %[[vbptr_off:.*]] = getelementptr inbounds i8* %[[this_i8]], i64 0 + // CHECK-NEXT: %[[vbptr:.*]] = bitcast i8* %[[vbptr_off]] to [2 x i32]** + // CHECK-NEXT: store [2 x i32]* @"\01??_8C@constructors@@7B@", [2 x i32]** %[[vbptr]] // CHECK-NEXT: bitcast %"struct.constructors::C"* %{{.*}} to %"struct.constructors::A"* // CHECK-NEXT: call x86_thiscallcc %"struct.constructors::A"* @"\01??0A@constructors@@QAE@XZ"(%"struct.constructors::A"* %{{.*}}) // CHECK-NEXT: br label %[[SKIP_VBASES]] @@ -182,6 +186,10 @@ D::D() { // CHECK: br i1 %[[SHOULD_CALL_VBASE_CTORS]], label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]] // // CHECK: [[INIT_VBASES]] + // CHECK-NEXT: %[[this_i8:.*]] = bitcast %"struct.constructors::D"* %{{.*}} to i8* + // CHECK-NEXT: %[[vbptr_off:.*]] = getelementptr inbounds i8* %[[this_i8]], i64 0 + // CHECK-NEXT: %[[vbptr:.*]] = bitcast i8* %[[vbptr_off]] to [2 x i32]** + // CHECK-NEXT: store [2 x i32]* @"\01??_8D@constructors@@7B@", [2 x i32]** %[[vbptr]] // CHECK-NEXT: bitcast %"struct.constructors::D"* %{{.*}} to %"struct.constructors::A"* // CHECK-NEXT: call x86_thiscallcc %"struct.constructors::A"* @"\01??0A@constructors@@QAE@XZ"(%"struct.constructors::A"* %{{.*}}) // CHECK-NEXT: br label %[[SKIP_VBASES]] @@ -203,6 +211,13 @@ E::E() { // CHECK: br i1 %[[SHOULD_CALL_VBASE_CTORS]], label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]] // // CHECK: [[INIT_VBASES]] + // CHECK-NEXT: %[[this_i8:.*]] = bitcast %"struct.constructors::E"* %{{.*}} to i8* + // CHECK-NEXT: %[[offs:.*]] = getelementptr inbounds i8* %[[this_i8]], i64 0 + // CHECK-NEXT: %[[vbptr_E:.*]] = bitcast i8* %[[offs]] to [3 x i32]** + // CHECK-NEXT: store [3 x i32]* @"\01??_8E@constructors@@7B01@@", [3 x i32]** %[[vbptr_E]] + // CHECK-NEXT: %[[offs:.*]] = getelementptr inbounds i8* %[[this_i8]], i64 4 + // CHECK-NEXT: %[[vbptr_C:.*]] = bitcast i8* %[[offs]] to [2 x i32]** + // CHECK-NEXT: store [2 x i32]* @"\01??_8E@constructors@@7BC@1@@", [2 x i32]** %[[vbptr_C]] // CHECK-NEXT: bitcast %"struct.constructors::E"* %{{.*}} to %"struct.constructors::A"* // CHECK-NEXT: call x86_thiscallcc %"struct.constructors::A"* @"\01??0A@constructors@@QAE@XZ"(%"struct.constructors::A"* %{{.*}}) // CHECK: call x86_thiscallcc %"struct.constructors::C"* @"\01??0C@constructors@@QAE@XZ"(%"struct.constructors::C"* %{{.*}}, i32 0) diff --git a/clang/test/CodeGenCXX/microsoft-abi-vbtables.cpp b/clang/test/CodeGenCXX/microsoft-abi-vbtables.cpp new file mode 100644 index 00000000000..1a6bdebd620 --- /dev/null +++ b/clang/test/CodeGenCXX/microsoft-abi-vbtables.cpp @@ -0,0 +1,436 @@ +// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=i386-pc-win32 -emit-llvm -o %t +// +// FIXME: These repeated FileCheck invocations are ugly, but I can't get the +// output in source file order. Can CHECK-DAG help here? +// 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 %s < %t +// RUN: FileCheck --check-prefix=TEST10 %s < %t +// RUN: FileCheck --check-prefix=TEST11 %s < %t +// RUN: FileCheck --check-prefix=TEST12 %s < %t +// RUN: FileCheck --check-prefix=TEST13 %s < %t +// RUN: FileCheck --check-prefix=TEST14 %s < %t +// RUN: FileCheck --check-prefix=TEST15 %s < %t +// RUN: FileCheck --check-prefix=TEST16 %s < %t +// RUN: FileCheck --check-prefix=TEST17 %s < %t +// RUN: FileCheck --check-prefix=TEST18 %s < %t +// RUN: FileCheck --check-prefix=TEST19 %s < %t +// RUN: FileCheck --check-prefix=TEST20 %s < %t +// RUN: FileCheck --check-prefix=TEST21 %s < %t + +// See microsoft-abi-structors.cpp for constructor codegen tests. + +namespace Test1 { +// Classic diamond, fully virtual. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual A { int c; }; +struct D : virtual B, virtual C { int d; }; +D d; // Force vbtable emission. + +// Layout should be: +// D: vbptr D +// int d +// A: int a +// B: vbptr B +// int b +// C: vbptr C +// int c + +// TEST1: @"\01??_8D@Test1@@7B01@@" = linkonce_odr unnamed_addr constant [4 x i32] [i32 0, i32 8, i32 12, i32 20] +// TEST1: @"\01??_8D@Test1@@7BB@1@@" = {{.*}} [2 x i32] [i32 0, i32 -4] +// TEST1: @"\01??_8D@Test1@@7BC@1@@" = {{.*}} [2 x i32] [i32 0, i32 -12] +// TEST1: @"\01??_8C@Test1@@7B@" = {{.*}} [2 x i32] [i32 0, i32 8] +// TEST1: @"\01??_8B@Test1@@7B@" = {{.*}} [2 x i32] [i32 0, i32 8] +} + +namespace Test2 { +// Classic diamond, only A is virtual. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual A { int c; }; +struct D : B, C { int d; }; +D d; // Force vbtable emission. + +// Layout should be: +// B: vbptr B +// int b +// C: vbptr C +// int c +// D: int d +// A: int a + +// TEST2: @"\01??_8D@Test2@@7BB@1@@" = {{.*}} [2 x i32] [i32 0, i32 20] +// TEST2: @"\01??_8D@Test2@@7BC@1@@" = {{.*}} [2 x i32] [i32 0, i32 12] +// TEST2: @"\01??_8C@Test2@@7B@" = {{.*}} [2 x i32] [i32 0, i32 8] +// TEST2: @"\01??_8B@Test2@@7B@" = {{.*}} [2 x i32] [i32 0, i32 8] +} + +namespace Test3 { +struct A { int a; }; +struct B { int b; }; +struct C : virtual A, virtual B { int c; }; +C c; + +// TEST3: @"\01??_8C@Test3@@7B@" = {{.*}} [3 x i32] [i32 0, i32 8, i32 12] +} + +namespace Test4 { +// Test reusing a vbptr from a non-virtual base. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : B, virtual A { int c; }; +C c; // Force vbtable emission. + +// TEST4: @"\01??_8C@Test4@@7B@" = {{.*}} [2 x i32] [i32 0, i32 12] +// TEST4: @"\01??_8B@Test4@@7B@" = {{.*}} [2 x i32] [i32 0, i32 8] +} + +namespace Test5 { +// Test multiple base subobjects of the same type when that type has a virtual +// base. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : B { int c; }; +struct D : B, C { int d; }; +D d; // Force vbtable emission. + +// TEST5: @"\01??_8D@Test5@@7BB@1@@" +// TEST5: @"\01??_8D@Test5@@7BC@1@@" +// TEST5: @"\01??_8C@Test5@@7B@" +// TEST5: @"\01??_8B@Test5@@7B@" +} + +namespace Test6 { +// Test that we skip unneeded base path component names. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : B { int c; }; +struct D : B, C { int d; }; +struct E : D { int e; }; +struct F : E, B, C { int f; }; +struct G : F, virtual E { int g; }; +G g; + +// TEST6: @"\01??_8G@Test6@@7BB@1@E@1@F@1@@" = +// TEST6: @"\01??_8G@Test6@@7BC@1@E@1@F@1@@" = +// TEST6: @"\01??_8G@Test6@@7BB@1@F@1@@" = +// TEST6: @"\01??_8G@Test6@@7BC@1@F@1@@" = +// TEST6: @"\01??_8G@Test6@@7BB@1@E@1@@" = +// TEST6: @"\01??_8G@Test6@@7BC@1@E@1@@" = +// TEST6: @"\01??_8F@Test6@@7BB@1@E@1@@" = {{.*}} [2 x i32] [i32 0, i32 52] +// TEST6: @"\01??_8F@Test6@@7BC@1@E@1@@" = {{.*}} [2 x i32] [i32 0, i32 44] +// TEST6: @"\01??_8F@Test6@@7BB@1@@" = {{.*}} [2 x i32] [i32 0, i32 24] +// TEST6: @"\01??_8F@Test6@@7BC@1@@" = {{.*}} [2 x i32] [i32 0, i32 16] +// TEST6: @"\01??_8C@Test6@@7B@" = {{.*}} [2 x i32] [i32 0, i32 12] +// TEST6: @"\01??_8B@Test6@@7B@" = {{.*}} [2 x i32] [i32 0, i32 8] +// TEST6: @"\01??_8E@Test6@@7BB@1@@" = {{.*}} [2 x i32] [i32 0, i32 28] +// TEST6: @"\01??_8E@Test6@@7BC@1@@" = {{.*}} [2 x i32] [i32 0, i32 20] +// TEST6: @"\01??_8D@Test6@@7BB@1@@" = {{.*}} [2 x i32] [i32 0, i32 24] +// TEST6: @"\01??_8D@Test6@@7BC@1@@" = {{.*}} [2 x i32] [i32 0, i32 16] +} + +namespace Test7 { +// Test a non-virtual base which reuses the vbptr of another base. +struct A { int a; }; +struct B { int b; }; +struct C { int c; }; +struct D : virtual A { int d; }; +struct E : B, D, virtual A, virtual C { int e; }; +E o; + +// TEST7: @"\01??_8E@Test7@@7B@" = {{.*}} [3 x i32] [i32 0, i32 12, i32 16] +// TEST7: @"\01??_8D@Test7@@7B@" = {{.*}} [2 x i32] [i32 0, i32 8] +} + +namespace Test8 { +// Test a virtual base which reuses the vbptr of another base. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : B { int c; }; +struct D : virtual C { int d; }; +D o; + +// TEST8: @"\01??_8D@Test8@@7B01@@" = {{.*}} [3 x i32] [i32 0, i32 8, i32 12] +// TEST8: @"\01??_8D@Test8@@7BC@1@@" = {{.*}} [2 x i32] [i32 0, i32 -4] +// TEST8: @"\01??_8C@Test8@@7B@" = {{.*}} [2 x i32] [i32 0, i32 12] +// TEST8: @"\01??_8B@Test8@@7B@" = {{.*}} [2 x i32] [i32 0, i32 8] +} + +namespace Test9 { +// D has to add to B's vbtable because D has more morally virtual bases than B. +// D then takes B's vbptr and the vbtable is named for D, not B. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual B { int c; }; +struct BB : B { int bb; }; // Indirection =/ +struct D : BB, C { int d; }; +struct E : virtual D { }; +E e; + +// TEST9: @"\01??_8E@Test9@@7B01@@" = +// TEST9: @"\01??_8E@Test9@@7BD@1@@" = +// TEST9: @"\01??_8E@Test9@@7BC@1@@" = +// TEST9: @"\01??_8E@Test9@@7BB@1@@" = +// TEST9: @"\01??_8D@Test9@@7B@" = +// TEST9: @"\01??_8D@Test9@@7BC@1@@" = +// TEST9: @"\01??_8D@Test9@@7BB@1@@" = +// TEST9: @"\01??_8C@Test9@@7B01@@" = +// TEST9: @"\01??_8C@Test9@@7BB@1@@" = +// TEST9: @"\01??_8BB@Test9@@7B@" = +// TEST9: @"\01??_8B@Test9@@7B@" = +} + +namespace Test10 { +struct A { int a; }; +struct B { int b; }; +struct C : virtual A { int c; }; +struct D : B, C { int d; }; +D d; + +// TEST10: @"\01??_8D@Test10@@7B@" = +// TEST10: @"\01??_8C@Test10@@7B@" = + +} + +namespace Test11 { +// Typical diamond with an extra single inheritance indirection for B and C. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual A { int c; }; +struct D : B { int d; }; +struct E : C { int e; }; +struct F : D, E { int f; }; +F f; + +// TEST11: @"\01??_8F@Test11@@7BD@1@@" = linkonce_odr unnamed_addr constant [2 x i32] [i32 0, i32 28] +// TEST11: @"\01??_8F@Test11@@7BE@1@@" = linkonce_odr unnamed_addr constant [2 x i32] [i32 0, i32 16] +// TEST11: @"\01??_8E@Test11@@7B@" = linkonce_odr unnamed_addr constant [2 x i32] [i32 0, i32 12] +// TEST11: @"\01??_8C@Test11@@7B@" = linkonce_odr unnamed_addr constant [2 x i32] [i32 0, i32 8] +// TEST11: @"\01??_8D@Test11@@7B@" = linkonce_odr unnamed_addr constant [2 x i32] [i32 0, i32 12] +// TEST11: @"\01??_8B@Test11@@7B@" = linkonce_odr unnamed_addr constant [2 x i32] [i32 0, i32 8] + +} + +namespace Test12 { +// Another vbptr inside a virtual base. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual B { int c; }; +struct D : C, B { int d; }; +struct E : D, C, B { int e; }; +E e; + +// TEST12: @"\01??_8E@Test12@@7BC@1@D@1@@" = +// TEST12: @"\01??_8E@Test12@@7BB@1@D@1@@" = +// TEST12: @"\01??_8E@Test12@@7BD@1@@" = +// TEST12: @"\01??_8E@Test12@@7BC@1@@" = +// TEST12: @"\01??_8E@Test12@@7BB@1@@" = +// TEST12: @"\01??_8C@Test12@@7B01@@" = +// TEST12: @"\01??_8C@Test12@@7BB@1@@" = +// TEST12: @"\01??_8D@Test12@@7BC@1@@" = +// TEST12: @"\01??_8D@Test12@@7BB@1@@" = +// TEST12: @"\01??_8D@Test12@@7B@" = +// TEST12: @"\01??_8B@Test12@@7B@" = +} + +namespace Test13 { +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual B { int c; }; +struct D : virtual C { int d; }; +struct E : D, C, B { int e; }; +E e; + +// TEST13: @"\01??_8E@Test13@@7BD@1@@" = +// TEST13: @"\01??_8E@Test13@@7BC@1@D@1@@" = +// TEST13: @"\01??_8E@Test13@@7BB@1@D@1@@" = +// TEST13: @"\01??_8E@Test13@@7BC@1@@" = +// TEST13: @"\01??_8E@Test13@@7BB@1@@" = +// TEST13: @"\01??_8D@Test13@@7B@" = +// TEST13: @"\01??_8D@Test13@@7BC@1@@" = +// TEST13: @"\01??_8D@Test13@@7BB@1@@" = +// TEST13: @"\01??_8C@Test13@@7B01@@" = +// TEST13: @"\01??_8C@Test13@@7BB@1@@" = +// TEST13: @"\01??_8B@Test13@@7B@" = +} + +namespace Test14 { +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual B { int c; }; +struct D : virtual C { int d; }; +struct E : D, virtual C, virtual B { int e; }; +E e; + +// TEST14: @"\01??_8E@Test14@@7B@" = +// TEST14: @"\01??_8E@Test14@@7BC@1@@" = +// TEST14: @"\01??_8E@Test14@@7BB@1@@" = +// TEST14: @"\01??_8D@Test14@@7B@" = +// TEST14: @"\01??_8D@Test14@@7BC@1@@" = +// TEST14: @"\01??_8D@Test14@@7BB@1@@" = +// TEST14: @"\01??_8C@Test14@@7B01@@" = +// TEST14: @"\01??_8C@Test14@@7BB@1@@" = +// TEST14: @"\01??_8B@Test14@@7B@" = +} + +namespace Test15 { +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual A { int c; }; +struct D : virtual B { int d; }; +struct E : D, C, B { int e; }; +E e; + +// TEST15: @"\01??_8E@Test15@@7BD@1@@" = +// TEST15: @"\01??_8E@Test15@@7BB@1@D@1@@" = +// TEST15: @"\01??_8E@Test15@@7BC@1@@" = +// TEST15: @"\01??_8E@Test15@@7BB@1@@" = +// TEST15: @"\01??_8C@Test15@@7B@" = +// TEST15: @"\01??_8D@Test15@@7B01@@" = +// TEST15: @"\01??_8D@Test15@@7BB@1@@" = +// TEST15: @"\01??_8B@Test15@@7B@" = +} + +namespace Test16 { +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual B { int c; }; // ambig +struct D : virtual C { int d; }; +struct E : virtual D { int e; }; // ambig +struct F : E, D, C, B { int f; }; // ambig +F f; + +// TEST16: @"\01??_8F@Test16@@7BE@1@@" = +// TEST16: @"\01??_8F@Test16@@7BD@1@E@1@@" = +// TEST16: @"\01??_8F@Test16@@7BC@1@E@1@@" = +// TEST16: @"\01??_8F@Test16@@7BB@1@E@1@@" = +// TEST16: @"\01??_8F@Test16@@7BD@1@@" = +// TEST16: @"\01??_8F@Test16@@7BC@1@@" = +// TEST16: @"\01??_8F@Test16@@7BB@1@@" = +// TEST16: @"\01??_8E@Test16@@7B01@@" = +// TEST16: @"\01??_8E@Test16@@7BD@1@@" = +// TEST16: @"\01??_8E@Test16@@7BC@1@@" = +// TEST16: @"\01??_8E@Test16@@7BB@1@@" = +// TEST16: @"\01??_8D@Test16@@7B@" = +// TEST16: @"\01??_8D@Test16@@7BC@1@@" = +// TEST16: @"\01??_8D@Test16@@7BB@1@@" = +// TEST16: @"\01??_8C@Test16@@7B01@@" = +// TEST16: @"\01??_8C@Test16@@7BB@1@@" = +// TEST16: @"\01??_8B@Test16@@7B@" = +} + +namespace Test17 { +// This test case has an interesting alternating pattern of using "vbtable of B" +// and "vbtable of C for C". This may be the key to the underlying algorithm. +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual B { int c; }; // ambig +struct D : virtual C { int d; }; +struct E : virtual D { int e; }; // ambig +struct F : virtual E { int f; }; +struct G : virtual F { int g; }; // ambig +struct H : virtual G { int h; }; +struct I : virtual H { int i; }; // ambig +struct J : virtual I { int j; }; +struct K : virtual J { int k; }; // ambig +K k; + +// TEST17: @"\01??_8K@Test17@@7B01@@" = +// TEST17: @"\01??_8J@Test17@@7B@" = +// TEST17: @"\01??_8I@Test17@@7B01@@" = +// TEST17: @"\01??_8H@Test17@@7B@" = +// TEST17: @"\01??_8G@Test17@@7B01@@" = +// TEST17: @"\01??_8F@Test17@@7B@" = +// TEST17: @"\01??_8E@Test17@@7B01@@" = +// TEST17: @"\01??_8D@Test17@@7B@" = +// TEST17: @"\01??_8C@Test17@@7B01@@" = +// TEST17: @"\01??_8B@Test17@@7B@" = +} + +namespace Test18 { +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : B { int c; }; +struct D : C, B { int d; }; +struct E : D, C, B { int e; }; +E e; + +// TEST18: @"\01??_8E@Test18@@7BC@1@D@1@@" = +// TEST18: @"\01??_8E@Test18@@7BB@1@D@1@@" = +// TEST18: @"\01??_8E@Test18@@7BC@1@@" = +// TEST18: @"\01??_8E@Test18@@7BB@1@@" = +// TEST18: @"\01??_8B@Test18@@7B@" = +// TEST18: @"\01??_8C@Test18@@7B@" = +// TEST18: @"\01??_8D@Test18@@7BC@1@@" = +// TEST18: @"\01??_8D@Test18@@7BB@1@@" = +} + +namespace Test19 { +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : virtual B { int c; }; +struct D : virtual C, virtual B { int d; }; +struct E : virtual D, virtual C, virtual B { int e; }; +E e; + +// TEST19: @"\01??_8E@Test19@@7B01@@" = +// TEST19: @"\01??_8E@Test19@@7BD@1@@" = +// TEST19: @"\01??_8E@Test19@@7BC@1@@" = +// TEST19: @"\01??_8E@Test19@@7BB@1@@" = +// TEST19: @"\01??_8D@Test19@@7B@" = +// TEST19: @"\01??_8D@Test19@@7BC@1@@" = +// TEST19: @"\01??_8D@Test19@@7BB@1@@" = +// TEST19: @"\01??_8C@Test19@@7B01@@" = +// TEST19: @"\01??_8C@Test19@@7BB@1@@" = +// TEST19: @"\01??_8B@Test19@@7B@" = +} + +namespace Test20 { +// E has no direct vbases, but it adds to C's vbtable anyway. +struct A { int a; }; +struct B { int b; }; +struct C : virtual A { int c; }; +struct D : virtual B { int d; }; +struct E : C, D { int e; }; +E f; + +// TEST20: @"\01??_8E@Test20@@7BC@1@@" = linkonce_odr unnamed_addr constant [3 x i32] [i32 0, i32 20, i32 24] +// TEST20: @"\01??_8E@Test20@@7BD@1@@" = linkonce_odr unnamed_addr constant [2 x i32] [i32 0, i32 16] +// TEST20: @"\01??_8D@Test20@@7B@" = linkonce_odr unnamed_addr constant [2 x i32] [i32 0, i32 8] +// TEST20: @"\01??_8C@Test20@@7B@" = linkonce_odr unnamed_addr constant [2 x i32] [i32 0, i32 8] +} + +namespace Test21 { +struct A { int a; }; +struct B : virtual A { int b; }; +struct C : B { int c; }; +struct D : B { int d; }; +struct E : C, D { int e; }; +struct F : virtual E { int f; }; +struct G : E { int g; }; +struct H : F, G { int h; }; +H h; + +// TEST21: @"\01??_8H@Test21@@7B@" = +// TEST21: @"\01??_8H@Test21@@7BC@1@F@1@@" = +// TEST21: @"\01??_8H@Test21@@7BD@1@F@1@@" = +// TEST21: @"\01??_8H@Test21@@7BC@1@G@1@@" = +// TEST21: @"\01??_8H@Test21@@7BD@1@G@1@@" = +// TEST21: @"\01??_8G@Test21@@7BC@1@@" = +// TEST21: @"\01??_8G@Test21@@7BD@1@@" = +// TEST21: @"\01??_8F@Test21@@7B@" = +// TEST21: @"\01??_8F@Test21@@7BC@1@@" = +// TEST21: @"\01??_8F@Test21@@7BD@1@@" = +// TEST21: @"\01??_8E@Test21@@7BC@1@@" = +// TEST21: @"\01??_8E@Test21@@7BD@1@@" = +// TEST21: @"\01??_8D@Test21@@7B@" = +// TEST21: @"\01??_8B@Test21@@7B@" = +// TEST21: @"\01??_8C@Test21@@7B@" = +} |