diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 4fc21fe708f8..a074466ec530 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -53,9 +53,14 @@ private: NextFieldOffsetInChars(CharUnits::Zero()), LLVMStructAlignment(CharUnits::One()) { } + void AppendVTablePointer(BaseSubobject Base, llvm::Constant *VTable, + const CXXRecordDecl *VTableClass); + void AppendField(const FieldDecl *Field, uint64_t FieldOffset, llvm::Constant *InitExpr); + void AppendBytes(CharUnits FieldOffsetInChars, llvm::Constant *InitCst); + void AppendBitField(const FieldDecl *Field, uint64_t FieldOffset, llvm::ConstantInt *InitExpr); @@ -66,7 +71,9 @@ private: void ConvertStructToPacked(); bool Build(InitListExpr *ILE); - void Build(const APValue &Val, QualType ValTy); + void Build(const APValue &Val, const RecordDecl *RD, bool IsPrimaryBase, + llvm::Constant *VTable, const CXXRecordDecl *VTableClass, + CharUnits BaseOffset); llvm::Constant *Finalize(QualType Ty); CharUnits getAlignment(const llvm::Constant *C) const { @@ -81,14 +88,36 @@ private: } }; +void ConstStructBuilder::AppendVTablePointer(BaseSubobject Base, + llvm::Constant *VTable, + const CXXRecordDecl *VTableClass) { + // Find the appropriate vtable within the vtable group. + uint64_t AddressPoint = + CGM.getVTableContext().getVTableLayout(VTableClass).getAddressPoint(Base); + llvm::Value *Indices[] = { + llvm::ConstantInt::get(CGM.Int64Ty, 0), + llvm::ConstantInt::get(CGM.Int64Ty, AddressPoint) + }; + llvm::Constant *VTableAddressPoint = + llvm::ConstantExpr::getInBoundsGetElementPtr(VTable, Indices); + + // Add the vtable at the start of the object. + AppendBytes(CharUnits::Zero(), VTableAddressPoint); +} + void ConstStructBuilder:: AppendField(const FieldDecl *Field, uint64_t FieldOffset, llvm::Constant *InitCst) { - const ASTContext &Context = CGM.getContext(); CharUnits FieldOffsetInChars = Context.toCharUnitsFromBits(FieldOffset); + AppendBytes(FieldOffsetInChars, InitCst); +} + +void ConstStructBuilder:: +AppendBytes(CharUnits FieldOffsetInChars, llvm::Constant *InitCst) { + assert(NextFieldOffsetInChars <= FieldOffsetInChars && "Field offset mismatch!"); @@ -399,23 +428,56 @@ bool ConstStructBuilder::Build(InitListExpr *ILE) { return true; } -void ConstStructBuilder::Build(const APValue &Val, QualType ValTy) { - RecordDecl *RD = ValTy->getAs()->getDecl(); +namespace { +struct BaseInfo { + BaseInfo(const CXXRecordDecl *Decl, CharUnits Offset, unsigned Index) + : Decl(Decl), Offset(Offset), Index(Index) { + } + + const CXXRecordDecl *Decl; + CharUnits Offset; + unsigned Index; + + bool operator<(const BaseInfo &O) const { return Offset < O.Offset; } +}; +} + +void ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD, + bool IsPrimaryBase, llvm::Constant *VTable, + const CXXRecordDecl *VTableClass, + CharUnits Offset) { const ASTRecordLayout &Layout = CGM.getContext().getASTRecordLayout(RD); - if (CXXRecordDecl *CD = dyn_cast(RD)) { + if (const CXXRecordDecl *CD = dyn_cast(RD)) { + // Add a vtable pointer, if we need one and it hasn't already been added. + if (CD->isDynamicClass() && !IsPrimaryBase) + AppendVTablePointer(BaseSubobject(CD, Offset), VTable, VTableClass); + + // Accumulate and sort bases, in order to visit them in address order, which + // may not be the same as declaration order. + llvm::SmallVector Bases; + Bases.reserve(CD->getNumBases()); unsigned BaseNo = 0; - for (CXXRecordDecl::base_class_iterator Base = CD->bases_begin(), + for (CXXRecordDecl::base_class_const_iterator Base = CD->bases_begin(), BaseEnd = CD->bases_end(); Base != BaseEnd; ++Base, ++BaseNo) { - // Build the base class subobject at the appropriately-offset location - // within this object. + assert(!Base->isVirtual() && "should not have virtual bases here"); const CXXRecordDecl *BD = Base->getType()->getAsCXXRecordDecl(); CharUnits BaseOffset = Layout.getBaseClassOffset(BD); - NextFieldOffsetInChars -= BaseOffset; + Bases.push_back(BaseInfo(BD, BaseOffset, BaseNo)); + } + std::stable_sort(Bases.begin(), Bases.end()); - Build(Val.getStructBase(BaseNo), Base->getType()); + for (unsigned I = 0, N = Bases.size(); I != N; ++I) { + BaseInfo &Base = Bases[I]; + // Build the base class subobject at the appropriately-offset location + // within this object. + NextFieldOffsetInChars -= Base.Offset; - NextFieldOffsetInChars += BaseOffset; + bool IsPrimaryBase = Layout.getPrimaryBase() == Base.Decl; + Build(Val.getStructBase(Base.Index), Base.Decl, IsPrimaryBase, + VTable, VTableClass, Offset + Base.Offset); + + NextFieldOffsetInChars += Base.Offset; } } @@ -532,7 +594,15 @@ llvm::Constant *ConstStructBuilder::BuildStruct(CodeGenModule &CGM, const APValue &Val, QualType ValTy) { ConstStructBuilder Builder(CGM, CGF); - Builder.Build(Val, ValTy); + + const RecordDecl *RD = ValTy->castAs()->getDecl(); + const CXXRecordDecl *CD = dyn_cast(RD); + llvm::Constant *VTable = 0; + if (CD && CD->isDynamicClass()) + VTable = CGM.getVTables().GetAddrOfVTable(CD); + + Builder.Build(Val, RD, false, VTable, CD, CharUnits::Zero()); + return Builder.Finalize(ValTy); } diff --git a/clang/test/CodeGenCXX/const-init-cxx11.cpp b/clang/test/CodeGenCXX/const-init-cxx11.cpp index 0ff8d10f6737..5366d6d48558 100644 --- a/clang/test/CodeGenCXX/const-init-cxx11.cpp +++ b/clang/test/CodeGenCXX/const-init-cxx11.cpp @@ -254,6 +254,42 @@ namespace NonLiteralConstexpr { } } +// PR12067 +namespace VirtualMembers { + struct A { + constexpr A(double d) : d(d) {} + virtual void f(); + double d; + }; + struct B : A { + constexpr B() : A(2.0), c{'h', 'e', 'l', 'l', 'o'} {} + constexpr B(int n) : A(n), c{'w', 'o', 'r', 'l', 'd'} {} + virtual void g(); + char c[5]; + }; + struct C { + constexpr C() : n(64) {} + int n; + }; + struct D : C, A, B { + constexpr D() : A(1.0), B(), s(5) {} + short s; + }; + struct E : D, B { + constexpr E() : B(3), c{'b','y','e'} {} + char c[3]; + }; + + // CHECK: @_ZN14VirtualMembers1eE = global { i8**, double, i32, i8**, double, [5 x i8], i16, i8**, double, [5 x i8], [3 x i8] } { i8** getelementptr inbounds ([11 x i8*]* @_ZTVN14VirtualMembers1EE, i64 0, i64 2), double 1.000000e+00, i32 64, i8** getelementptr inbounds ([11 x i8*]* @_ZTVN14VirtualMembers1EE, i64 0, i64 5), double 2.000000e+00, [5 x i8] c"hello", i16 5, i8** getelementptr inbounds ([11 x i8*]* @_ZTVN14VirtualMembers1EE, i64 0, i64 9), double 3.000000e+00, [5 x i8] c"world", [3 x i8] c"bye" } + E e; + + struct nsMemoryImpl { + virtual void f(); + }; + // CHECK: @_ZN14VirtualMembersL13sGlobalMemoryE = internal global { i8** } { i8** getelementptr inbounds ([3 x i8*]* @_ZTVN14VirtualMembers12nsMemoryImplE, i64 0, i64 2) } + static nsMemoryImpl sGlobalMemory; +} + // Constant initialization tests go before this point, // dynamic initialization tests go after.