From 7810af0a43fc6b46eaed5c5c366adc0d3dda98c5 Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Wed, 19 Jun 2013 15:20:38 +0000 Subject: [PATCH] [ms-cxxabi] Emit and install appropriately mangled vbtables 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 --- clang/include/clang/AST/Mangle.h | 6 + clang/lib/AST/ItaniumMangle.cpp | 10 + clang/lib/AST/MicrosoftMangle.cpp | 36 +- clang/lib/CodeGen/CGCXXABI.cpp | 5 +- clang/lib/CodeGen/CGCXXABI.h | 10 +- clang/lib/CodeGen/CGClass.cpp | 3 +- clang/lib/CodeGen/CGVTables.cpp | 10 +- clang/lib/CodeGen/CMakeLists.txt | 1 + clang/lib/CodeGen/ItaniumCXXABI.cpp | 10 + clang/lib/CodeGen/MicrosoftCXXABI.cpp | 75 ++- clang/lib/CodeGen/MicrosoftVBTables.cpp | 236 ++++++++++ clang/lib/CodeGen/MicrosoftVBTables.h | 129 ++++++ .../CodeGenCXX/microsoft-abi-structors.cpp | 15 + .../CodeGenCXX/microsoft-abi-vbtables.cpp | 436 ++++++++++++++++++ 14 files changed, 956 insertions(+), 26 deletions(-) create mode 100644 clang/lib/CodeGen/MicrosoftVBTables.cpp create mode 100644 clang/lib/CodeGen/MicrosoftVBTables.h create mode 100644 clang/test/CodeGenCXX/microsoft-abi-vbtables.cpp diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h index b6d22cfb5fd7..98e03f5decea 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 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 1d58e8df0419..065465aa59e3 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 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 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 976fcf8ad69e..5e488e042e71 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 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) { - // ::= ? - // [] @ - // ::= _7 # vftable - // ::= _8 # vbtable + // ::= ?_7 + // [] @ // NOTE: here is always 'B' (const). - // 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 BasePath, + raw_ostream &Out) { + // ::= ?_8 + // [] @ + // NOTE: here is always 'B' (const). + // 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::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 4a125e6d7fa7..7f07344adf06 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 899e5e7b258a..2e505d224fcc 100644 --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -231,7 +231,8 @@ public: CanQualType &ResTy, SmallVectorImpl &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 88f5cf9e6b54..ce4ead241941 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 d1168770ddb2..9c4a5e0cdadb 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 9ca2295a9229..e65c699a1f48 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 722840073812..1952ee6012f6 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(CGF.CurGD.getDecl())) diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp index e6eca28b29f5..663011570d52 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 &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 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::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 000000000000..4523aa82b737 --- /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 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 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 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(cast(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 000000000000..4ad8e07582ef --- /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 + +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 VBTableVector; + +struct VBTablePath; + +typedef llvm::SmallVector 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 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 VBasesSeen; +}; + +} // namespace CodeGen + +} // namespace clang diff --git a/clang/test/CodeGenCXX/microsoft-abi-structors.cpp b/clang/test/CodeGenCXX/microsoft-abi-structors.cpp index 2c2f162540fa..27c00063395f 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 000000000000..1a6bdebd620f --- /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@" = +}