Index: docs/UsersManual.rst =================================================================== --- docs/UsersManual.rst +++ docs/UsersManual.rst @@ -1171,6 +1171,23 @@ // value of -fmax-type-align. } +**-flto-relative-c++-abi-vtables** + + This flag controls whether the compiler uses a more space-efficient virtual + table ABI, known as the relative ABI. It enables the relative ABI for all + classes with :doc:`hidden LTO visibility `. Requires `-flto`. + + This ABI is not guaranteed to be stable between versions of Clang. This + means that it is an ODR violation to define a class with the relative + ABI in two translation units compiled with different versions of + Clang. Specifically, mixing different head revisions or major releases + is not allowed, but mixing different point releases is fine. + + It is not guaranteed that all classes with hidden LTO visibility will + use the relative ABI. For example, if a base class uses the platform ABI + and declares virtual functions, that will force any derived classes to + use the platform ABI. The compiler will diagnose any cases where a class + derives from multiple incompatible base classes. Profile Guided Optimization --------------------------- Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -457,6 +457,9 @@ /// \brief Whether we are currently parsing base specifiers. unsigned IsParsingBaseSpecifiers : 1; + /// \brief Whether the class uses the relative C++ vtable ABI. + unsigned IsRelativeCXXABI : 1; + /// \brief The number of base class specifiers in Bases. unsigned NumBases; @@ -703,6 +706,11 @@ return data().IsParsingBaseSpecifiers; } + void setIsRelativeCXXABI() { data().IsRelativeCXXABI = true; } + bool isRelativeCXXABI() const { + return data().IsRelativeCXXABI; + } + /// \brief Sets the base classes of this struct or class. void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases); @@ -1715,6 +1723,12 @@ return getLambdaData().MethodTyInfo; } + /// Returns whether this class would have hidden LTO visibility if it were built + /// in a translation unit with LTO, and therefore may participate in + /// (single-module) CFI, whole-program vtable optimization and the relative + /// C++ ABI. + bool hasHiddenLTOVisibility() const; + static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K >= firstCXXRecord && K <= lastCXXRecord; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -8525,4 +8525,9 @@ "'%0' qualifier on omitted return type %1 has no effect">, InGroup; +def err_abi_mismatch : Error< + "inconsistent ABI for class %0">; +def note_abi_relative_base : Note< + "base %0 uses the %select{platform|relative}1 ABI">; + } // end of sema component. Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -254,6 +254,14 @@ "field padding (0: none, 1:least " "aggressive, 2: more aggressive)") +LANGOPT(LTOVisibilityPublicStd, 1, 0, "Whether to use public LTO visibility " + "for entities in std and stdext " + "namespaces. This is enabled by " + "clang-cl's /MT and /MTd flags.") +LANGOPT(LTORelativeCXXABIVTables, 1, 0, + "Whether to use clang's relative C++ vtable ABI " + "for classes with hidden LTO visibility") + #undef LANGOPT #undef COMPATIBLE_LANGOPT #undef BENIGN_LANGOPT Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -756,6 +756,9 @@ HelpText<"Enable LTO in 'full' mode">; def fno_lto : Flag<["-"], "fno-lto">, Group, HelpText<"Disable LTO mode (default)">; +def flto_relative_cxx_abi_vtables : Flag<["-"], "flto-relative-c++-abi-vtables">, + Group, Flags<[CC1Option]>, + HelpText<"Use the unstable C++ class ABI for classes with hidden LTO visibility">; def fthinlto_index_EQ : Joined<["-"], "fthinlto-index=">, Flags<[CC1Option]>, Group, HelpText<"Perform ThinLTO importing using provided function summary index">; Index: include/clang/Frontend/CodeGenOptions.def =================================================================== --- include/clang/Frontend/CodeGenOptions.def +++ include/clang/Frontend/CodeGenOptions.def @@ -190,10 +190,6 @@ CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program /// vtable optimization. -/// Whether to use public LTO visibility for entities in std and stdext -/// namespaces. This is enabled by clang-cl's /MT and /MTd flags. -CODEGENOPT(LTOVisibilityPublicStd, 1, 0) - /// The user specified number of registers to be used for integral arguments, /// or 0 if unspecified. VALUE_CODEGENOPT(NumRegisterParameters, 32, 0) Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -9482,6 +9482,12 @@ // Emitting members of dllexported classes is delayed until the class // (including field initializers) is fully parsed. SmallVector DelayedDllExportClasses; + + /// Determine the ABI for this class using its attributes, bases and implicit + /// contexts. Check for conflicts between bases or between a base and an + /// attribute. Set the class's isRelativeCXXABI() flag according to the + /// result. + void checkClassABI(CXXRecordDecl *RD); }; /// \brief RAII object that enters a new expression evaluation context. Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -20,6 +20,7 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" using namespace clang; @@ -71,8 +72,8 @@ ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false), - IsParsingBaseSpecifiers(false), NumBases(0), NumVBases(0), Bases(), - VBases(), Definition(D), FirstFriend() {} + IsParsingBaseSpecifiers(false), IsRelativeCXXABI(false), NumBases(0), + NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend() {} CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { return Bases.get(Definition->getASTContext().getExternalSource()); @@ -1443,6 +1444,41 @@ return false; } +bool CXXRecordDecl::hasHiddenLTOVisibility() const { + LinkageInfo LV = getLinkageAndVisibility(); + if (!clang::isExternallyVisible(LV.getLinkage())) + return true; + + if (hasAttr() || hasAttr()) + return false; + + ASTContext &Context = getASTContext(); + if (Context.getTargetInfo().getTriple().isOSBinFormatCOFF()) { + if (hasAttr() || hasAttr()) + return false; + } else { + if (LV.getVisibility() != HiddenVisibility) + return false; + } + + if (Context.getLangOpts().LTOVisibilityPublicStd) { + const DeclContext *DC = this; + while (1) { + auto *D = cast(DC); + DC = DC->getParent(); + if (isa(DC->getRedeclContext())) { + if (auto *ND = dyn_cast(D)) + if (const IdentifierInfo *II = ND->getIdentifier()) + if (II->isStr("std") || II->isStr("stdext")) + return false; + break; + } + } + } + + return true; +} + void CXXMethodDecl::anchor() { } bool CXXMethodDecl::isStatic() const { Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -2450,6 +2450,24 @@ return VTable; } +llvm::Value *CodeGenFunction::GetVirtualFunctionFromVTable(const CXXRecordDecl *RD, + llvm::Value *VTable, + uint64_t VTableIndex, + llvm::Type *Ty) { + if (!RD->isRelativeCXXABI()) { + VTable = Builder.CreateBitCast(VTable, Ty->getPointerTo()->getPointerTo()); + llvm::Value *VTableSlotPtr = + Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn"); + return Builder.CreateAlignedLoad(VTableSlotPtr, getPointerAlign()); + } + + VTable = Builder.CreateBitCast(VTable, Int8PtrTy); + llvm::Value *Load = Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::load_relative, {Int32Ty}), + {VTable, llvm::ConstantInt::get(Int32Ty, 4 * VTableIndex)}); + return Builder.CreateBitCast(Load, Ty->getPointerTo()); +} + // If a class has a single non-virtual base and does not introduce or override // virtual member functions or fields, it will have the same layout as its base. // This function returns the least derived such class. @@ -2489,7 +2507,7 @@ llvm::Value *VTable, SourceLocation Loc) { if (CGM.getCodeGenOpts().WholeProgramVTables && - CGM.HasHiddenLTOVisibility(RD)) { + RD->hasHiddenLTOVisibility()) { llvm::Metadata *MD = CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0)); llvm::Value *BitSetName = @@ -2566,7 +2584,7 @@ CFITypeCheckKind TCK, SourceLocation Loc) { if (!CGM.getCodeGenOpts().SanitizeCfiCrossDso && - !CGM.HasHiddenLTOVisibility(RD)) + !RD->hasHiddenLTOVisibility()) return; std::string TypeName = RD->getQualifiedNameAsString(); Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -1159,9 +1159,16 @@ // FIXME: Add proper support for debug info for virtual calls in // the Microsoft ABI, where we may use multiple vptrs to make a vftable // lookup if we have multiple or virtual inheritance. + // FIXME: Add support for debug info for virtual calls in the relative + // ABI, where the virtual function address needs to be calculated from + // the virtual table address. if (!isa(Method) && - !CGM.getTarget().getCXXABI().isMicrosoft()) + !CGM.getTarget().getCXXABI().isMicrosoft() && + !Method->getParent()->isRelativeCXXABI()) VIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(Method); + else + // -1u indicates that the vtable index is non-representable. + VIndex = -1u; ContainingType = RecordTy; } Index: lib/CodeGen/CGVTables.h =================================================================== --- lib/CodeGen/CGVTables.h +++ lib/CodeGen/CGVTables.h @@ -57,14 +57,16 @@ void maybeEmitThunkForVTable(GlobalDecl GD, const ThunkInfo &Thunk); public: - /// CreateVTableInitializer - Create a vtable initializer for the given record - /// decl. + /// SetVTableInitializer - Set VTable's initializer to the vtable initializer + /// for the given record decl. /// \param Components - The vtable components; this is really an array of /// VTableComponents. - llvm::Constant *CreateVTableInitializer( - const CXXRecordDecl *RD, const VTableComponent *Components, - unsigned NumComponents, const VTableLayout::VTableThunkTy *VTableThunks, - unsigned NumVTableThunks, llvm::Constant *RTTI); + void SetVTableInitializer(llvm::GlobalVariable *VTable, + const CXXRecordDecl *RD, + const VTableComponent *Components, + unsigned NumComponents, + const VTableLayout::VTableThunkTy *VTableThunks, + unsigned NumVTableThunks, llvm::Constant *RTTI); CodeGenVTables(CodeGenModule &CGM); @@ -112,6 +114,11 @@ void GenerateClassData(const CXXRecordDecl *RD); bool isVTableExternal(const CXXRecordDecl *RD); + + /// Returns the type of a vtable with the given layout. Normally an array of + /// pointers, but may be a struct under the relative vtable ABI. + llvm::Type *GetVTableType(const CXXRecordDecl *RD, + const VTableLayout &VTLayout); }; } // end namespace CodeGen Index: lib/CodeGen/CGVTables.cpp =================================================================== --- lib/CodeGen/CGVTables.cpp +++ lib/CodeGen/CGVTables.cpp @@ -521,13 +521,31 @@ emitThunk(GD, Thunk, /*ForVTable=*/false); } -llvm::Constant *CodeGenVTables::CreateVTableInitializer( - const CXXRecordDecl *RD, const VTableComponent *Components, - unsigned NumComponents, const VTableLayout::VTableThunkTy *VTableThunks, - unsigned NumVTableThunks, llvm::Constant *RTTI) { +llvm::Type *CodeGenVTables::GetVTableType(const CXXRecordDecl *RD, + const VTableLayout &VTLayout) { + if (!RD->isRelativeCXXABI()) + return llvm::ArrayType::get(CGM.Int8PtrTy, + VTLayout.getNumVTableComponents()); + + std::vector Types; + for (auto &Comp : VTLayout.vtable_components()) { + if (Comp.isFunctionPointerKind()) + Types.push_back(CGM.Int32Ty); + else + Types.push_back(CGM.Int8PtrTy); + } + return llvm::StructType::get(CGM.getLLVMContext(), Types); +} + +void CodeGenVTables::SetVTableInitializer( + llvm::GlobalVariable *VTable, const CXXRecordDecl *RD, + const VTableComponent *Components, unsigned NumComponents, + const VTableLayout::VTableThunkTy *VTableThunks, unsigned NumVTableThunks, + llvm::Constant *RTTI) { SmallVector Inits; llvm::Type *Int8PtrTy = CGM.Int8PtrTy; + llvm::Type *Int32Ty = CGM.Int32Ty; llvm::Type *PtrDiffTy = CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType()); @@ -536,9 +554,36 @@ llvm::Constant *PureVirtualFn = nullptr, *DeletedVirtualFn = nullptr; + bool RelativeABI = RD->isRelativeCXXABI(); + llvm::Type *VTableTy = VTable->getValueType(); + llvm::Type *FunctionPtrTy = RelativeABI ? Int32Ty : Int8PtrTy; + llvm::Constant *AddrPointInt; + + auto maybeMakeRelative = [&](llvm::Constant *C) { + if (!RelativeABI) + return C; + return llvm::ConstantExpr::getIntegerCast( + llvm::ConstantExpr::getSub( + llvm::ConstantExpr::getPtrToInt(C, PtrDiffTy), AddrPointInt), + Int32Ty, /*isSigned=*/true); + }; + for (unsigned I = 0; I != NumComponents; ++I) { VTableComponent Component = Components[I]; + if (RelativeABI && Component.isFunctionPointerKind() && + (I == 0 || !Components[I - 1].isFunctionPointerKind())) { + // FIXME: Need a better way of identifying address points that works with + // the Itanium and MS ABIs. + AddrPointInt = llvm::ConstantExpr::getPtrToInt( + llvm::ConstantExpr::getGetElementPtr( + VTableTy, VTable, + llvm::ArrayRef{ + llvm::ConstantInt::get(Int32Ty, 0), + llvm::ConstantInt::get(Int32Ty, I)}), + PtrDiffTy); + } + llvm::Constant *Init = nullptr; switch (Component.getKind()) { @@ -592,7 +637,7 @@ : (MD->hasAttr() || !MD->hasAttr()); if (!CanEmitMethod) { - Init = llvm::ConstantExpr::getNullValue(Int8PtrTy); + Init = llvm::ConstantExpr::getNullValue(FunctionPtrTy); break; } // Method is acceptable, continue processing as usual. @@ -610,7 +655,7 @@ PureVirtualFn = llvm::ConstantExpr::getBitCast(PureVirtualFn, CGM.Int8PtrTy); } - Init = PureVirtualFn; + Init = maybeMakeRelative(PureVirtualFn); } else if (cast(GD.getDecl())->isDeleted()) { if (!DeletedVirtualFn) { llvm::FunctionType *Ty = @@ -623,7 +668,7 @@ DeletedVirtualFn = llvm::ConstantExpr::getBitCast(DeletedVirtualFn, CGM.Int8PtrTy); } - Init = DeletedVirtualFn; + Init = maybeMakeRelative(DeletedVirtualFn); } else { // Check if we should use a thunk. if (NextVTableThunkIndex < NumVTableThunks && @@ -641,20 +686,26 @@ } Init = llvm::ConstantExpr::getBitCast(Init, Int8PtrTy); + Init = maybeMakeRelative(Init); } break; } case VTableComponent::CK_UnusedFunctionPointer: - Init = llvm::ConstantExpr::getNullValue(Int8PtrTy); + Init = llvm::ConstantExpr::getNullValue(FunctionPtrTy); break; }; Inits.push_back(Init); } - - llvm::ArrayType *ArrayType = llvm::ArrayType::get(Int8PtrTy, NumComponents); - return llvm::ConstantArray::get(ArrayType, Inits); + + if (RelativeABI) { + VTable->setInitializer( + llvm::ConstantStruct::get(cast(VTableTy), Inits)); + } else { + VTable->setInitializer( + llvm::ConstantArray::get(cast(VTableTy), Inits)); + } } llvm::GlobalVariable * @@ -681,8 +732,7 @@ Base.getBase(), Out); StringRef Name = OutName.str(); - llvm::ArrayType *ArrayType = - llvm::ArrayType::get(CGM.Int8PtrTy, VTLayout->getNumVTableComponents()); + llvm::Type *VTType = GetVTableType(RD, *VTLayout); // Construction vtable symbols are not part of the Itanium ABI, so we cannot // guarantee that they actually will be available externally. Instead, when @@ -694,7 +744,7 @@ // Create the variable that will hold the construction vtable. llvm::GlobalVariable *VTable = - CGM.CreateOrReplaceCXXRuntimeVariable(Name, ArrayType, Linkage); + CGM.CreateOrReplaceCXXRuntimeVariable(Name, VTType, Linkage); CGM.setGlobalVisibility(VTable, RD); // V-tables are always unnamed_addr. @@ -704,12 +754,11 @@ CGM.getContext().getTagDeclType(Base.getBase())); // Create and set the initializer. - llvm::Constant *Init = CreateVTableInitializer( - Base.getBase(), VTLayout->vtable_component_begin(), + SetVTableInitializer( + VTable, Base.getBase(), VTLayout->vtable_component_begin(), VTLayout->getNumVTableComponents(), VTLayout->vtable_thunk_begin(), VTLayout->getNumVTableThunks(), RTTI); - VTable->setInitializer(Init); - + CGM.EmitVTableBitSetEntries(VTable, *VTLayout.get()); return VTable; @@ -900,40 +949,6 @@ DeferredVTables.clear(); } -bool CodeGenModule::HasHiddenLTOVisibility(const CXXRecordDecl *RD) { - LinkageInfo LV = RD->getLinkageAndVisibility(); - if (!isExternallyVisible(LV.getLinkage())) - return true; - - if (RD->hasAttr() || RD->hasAttr()) - return false; - - if (getTriple().isOSBinFormatCOFF()) { - if (RD->hasAttr() || RD->hasAttr()) - return false; - } else { - if (LV.getVisibility() != HiddenVisibility) - return false; - } - - if (getCodeGenOpts().LTOVisibilityPublicStd) { - const DeclContext *DC = RD; - while (1) { - auto *D = cast(DC); - DC = DC->getParent(); - if (isa(DC->getRedeclContext())) { - if (auto *ND = dyn_cast(D)) - if (const IdentifierInfo *II = ND->getIdentifier()) - if (II->isStr("std") || II->isStr("stdext")) - return false; - break; - } - } - } - - return true; -} - void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable, const VTableLayout &VTLayout) { if (!getCodeGenOpts().PrepareForLTO) @@ -976,8 +991,18 @@ llvm::NamedMDNode *BitsetsMD = getModule().getOrInsertNamedMetadata("llvm.bitsets"); - for (auto BitsetEntry : BitsetEntries) - CreateVTableBitSetEntry(BitsetsMD, VTable, - PointerWidth * BitsetEntry.second, - BitsetEntry.first); + + const llvm::StructLayout *SL = nullptr; + if (auto *ST = dyn_cast(VTable->getValueType())) + SL = getDataLayout().getStructLayout(ST); + + for (auto BitsetEntry : BitsetEntries) { + CharUnits ByteOffset; + if (SL) + ByteOffset = + CharUnits::fromQuantity(SL->getElementOffset(BitsetEntry.second)); + else + ByteOffset = PointerWidth * BitsetEntry.second; + CreateVTableBitSetEntry(BitsetsMD, VTable, ByteOffset, BitsetEntry.first); + } } Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1392,6 +1392,11 @@ llvm::Value *GetVTablePtr(Address This, llvm::Type *VTableTy, const CXXRecordDecl *VTableClass); + llvm::Value *GetVirtualFunctionFromVTable(const CXXRecordDecl *RD, + llvm::Value *VTable, + uint64_t VTableIndex, + llvm::Type *Ty); + enum CFITypeCheckKind { CFITCK_VCall, CFITCK_NVCall, Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -1113,11 +1113,6 @@ void EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D, CodeGenFunction *CGF = nullptr); - /// Returns whether the given record has hidden LTO visibility and therefore - /// may participate in (single-module) CFI and whole-program vtable - /// optimization. - bool HasHiddenLTOVisibility(const CXXRecordDecl *RD); - /// Emit bit set entries for the given vtable using the given layout if /// vptr CFI is enabled. void EmitVTableBitSetEntries(llvm::GlobalVariable *VTable, Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -478,6 +478,12 @@ LangOpts.CUDADeviceFlushDenormalsToZero ? 1 : 0); } + if (CodeGenOpts.PrepareForLTO) { + getModule().addModuleFlag(llvm::Module::Error, + "lto-relative-c++-abi-vtables", + getLangOpts().LTORelativeCXXABIVTables); + } + if (uint32_t PLevel = Context.getLangOpts().PICLevel) { assert(PLevel < 3 && "Invalid PIC Level"); getModule().setPICLevel(static_cast(PLevel)); Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -578,17 +578,26 @@ llvm::Value *VTable = CGF.GetVTablePtr(Address(This, VTablePtrAlign), VTableTy, RD); - // Apply the offset. llvm::Value *VTableOffset = FnAsInt; if (!UseARMMethodPtrABI) VTableOffset = Builder.CreateSub(VTableOffset, ptrdiff_1); - VTable = Builder.CreateGEP(VTable, VTableOffset); // Load the virtual function to call. - VTable = Builder.CreateBitCast(VTable, FTy->getPointerTo()->getPointerTo()); - llvm::Value *VirtualFn = - Builder.CreateAlignedLoad(VTable, CGF.getPointerAlign(), - "memptr.virtualfn"); + llvm::Value *VirtualFn; + if (RD->isRelativeCXXABI()) { + VirtualFn = + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::load_relative, + {VTableOffset->getType()}), + {VTable, VTableOffset}); + VirtualFn = Builder.CreateBitCast(VirtualFn, FTy->getPointerTo()); + } else { + // Apply the offset. + llvm::Value *VTableSlotPtr = Builder.CreateGEP(VTable, VTableOffset); + VTableSlotPtr = Builder.CreateBitCast(VTableSlotPtr, + FTy->getPointerTo()->getPointerTo()); + VirtualFn = Builder.CreateAlignedLoad(VTableSlotPtr, CGF.getPointerAlign(), + "memptr.virtualfn"); + } CGF.EmitBranch(FnEnd); // In the non-virtual path, the function pointer is actually a @@ -796,7 +805,11 @@ const ASTContext &Context = getContext(); CharUnits PointerWidth = Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); - uint64_t VTableOffset = (Index * PointerWidth.getQuantity()); + uint64_t VTableOffset = Index; + if (MD->getParent()->isRelativeCXXABI()) + VTableOffset *= 4; + else + VTableOffset *= PointerWidth.getQuantity(); if (UseARMMethodPtrABI) { // ARM C++ ABI 3.2.1: @@ -1458,10 +1471,10 @@ CGM.GetAddrOfRTTIDescriptor(CGM.getContext().getTagDeclType(RD)); // Create and set the initializer. - llvm::Constant *Init = CGVT.CreateVTableInitializer( - RD, VTLayout.vtable_component_begin(), VTLayout.getNumVTableComponents(), - VTLayout.vtable_thunk_begin(), VTLayout.getNumVTableThunks(), RTTI); - VTable->setInitializer(Init); + CGVT.SetVTableInitializer(VTable, RD, VTLayout.vtable_component_begin(), + VTLayout.getNumVTableComponents(), + VTLayout.vtable_thunk_begin(), + VTLayout.getNumVTableThunks(), RTTI); // Set the correct linkage. VTable->setLinkage(Linkage); @@ -1569,12 +1582,12 @@ llvm::raw_svector_ostream Out(Name); getMangleContext().mangleCXXVTable(RD, Out); - ItaniumVTableContext &VTContext = CGM.getItaniumVTableContext(); - llvm::ArrayType *ArrayType = llvm::ArrayType::get( - CGM.Int8PtrTy, VTContext.getVTableLayout(RD).getNumVTableComponents()); + const VTableLayout &VTLayout = + CGM.getItaniumVTableContext().getVTableLayout(RD); + llvm::Type *VTableType = CGM.getVTables().GetVTableType(RD, VTLayout); VTable = CGM.CreateOrReplaceCXXRuntimeVariable( - Name, ArrayType, llvm::GlobalValue::ExternalLinkage); + Name, VTableType, llvm::GlobalValue::ExternalLinkage); VTable->setUnnamedAddr(true); if (RD->hasAttr()) @@ -1591,16 +1604,15 @@ llvm::Type *Ty, SourceLocation Loc) { GD = GD.getCanonicalDecl(); - Ty = Ty->getPointerTo()->getPointerTo(); auto *MethodDecl = cast(GD.getDecl()); - llvm::Value *VTable = CGF.GetVTablePtr(This, Ty, MethodDecl->getParent()); + llvm::Value *VTable = CGF.GetVTablePtr( + This, Ty->getPointerTo()->getPointerTo(), MethodDecl->getParent()); CGF.EmitBitSetCodeForVCall(MethodDecl->getParent(), VTable, Loc); uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); - llvm::Value *VFuncPtr = - CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn"); - return CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); + return CGF.GetVirtualFunctionFromVTable(MethodDecl->getParent(), VTable, + VTableIndex, Ty); } llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall( Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -1571,12 +1571,10 @@ [](const VTableComponent &VTC) { return VTC.isRTTIKind(); })) RTTI = getMSCompleteObjectLocator(RD, Info); - llvm::Constant *Init = CGVT.CreateVTableInitializer( - RD, VTLayout.vtable_component_begin(), - VTLayout.getNumVTableComponents(), VTLayout.vtable_thunk_begin(), - VTLayout.getNumVTableThunks(), RTTI); - - VTable->setInitializer(Init); + CGVT.SetVTableInitializer(VTable, RD, VTLayout.vtable_component_begin(), + VTLayout.getNumVTableComponents(), + VTLayout.vtable_thunk_begin(), + VTLayout.getNumVTableThunks(), RTTI); emitVTableBitSetEntries(Info, RD, VTable); } @@ -1695,16 +1693,14 @@ return VTable; } - uint64_t NumVTableSlots = - VTContext.getVFTableLayout(RD, VFPtr->FullOffsetInMDC) - .getNumVTableComponents(); + const VTableLayout &VTLayout = + VTContext.getVFTableLayout(RD, VFPtr->FullOffsetInMDC); llvm::GlobalValue::LinkageTypes VTableLinkage = VTableAliasIsRequred ? llvm::GlobalValue::PrivateLinkage : VFTableLinkage; StringRef VTableName = VTableAliasIsRequred ? StringRef() : VFTableName.str(); - llvm::ArrayType *VTableType = - llvm::ArrayType::get(CGM.Int8PtrTy, NumVTableSlots); + llvm::Type *VTableType = CGM.getVTables().GetVTableType(RD, VTLayout); // Create a backing variable for the contents of VTable. The VTable may // or may not include space for a pointer to RTTI data. @@ -1725,8 +1721,8 @@ // importing it. We never reference the RTTI data directly so there is no // need to make room for it. if (VTableAliasIsRequred) { - llvm::Value *GEPIndices[] = {llvm::ConstantInt::get(CGM.IntTy, 0), - llvm::ConstantInt::get(CGM.IntTy, 1)}; + llvm::Value *GEPIndices[] = {llvm::ConstantInt::get(CGM.Int32Ty, 0), + llvm::ConstantInt::get(CGM.Int32Ty, 1)}; // Create a GEP which points just after the first entry in the VFTable, // this should be the location of the first virtual method. llvm::Constant *VTableGEP = llvm::ConstantExpr::getInBoundsGetElementPtr( @@ -1736,6 +1732,7 @@ if (C) C->setSelectionKind(llvm::Comdat::Largest); } + VTableGEP = llvm::ConstantExpr::getBitCast(VTableGEP, CGM.Int8PtrPtrTy); VFTable = llvm::GlobalAlias::create(CGM.Int8PtrTy, /*AddressSpace=*/0, VFTableLinkage, VFTableName.str(), VTableGEP, @@ -1807,14 +1804,13 @@ llvm::Type *Ty, SourceLocation Loc) { GD = GD.getCanonicalDecl(); - CGBuilderTy &Builder = CGF.Builder; - Ty = Ty->getPointerTo()->getPointerTo(); Address VPtr = adjustThisArgumentForVirtualFunctionCall(CGF, GD, This, true); auto *MethodDecl = cast(GD.getDecl()); - llvm::Value *VTable = CGF.GetVTablePtr(VPtr, Ty, MethodDecl->getParent()); + llvm::Value *VTable = CGF.GetVTablePtr( + VPtr, Ty->getPointerTo()->getPointerTo(), MethodDecl->getParent()); MicrosoftVTableContext::MethodVFTableLocation ML = CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD); @@ -1822,9 +1818,8 @@ CGF.EmitBitSetCodeForVCall(getClassAtVTableLocation(getContext(), GD, ML), VTable, Loc); - llvm::Value *VFuncPtr = - Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn"); - return Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); + return CGF.GetVirtualFunctionFromVTable(MethodDecl->getParent(), VTable, + ML.Index, Ty); } llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall( @@ -1940,12 +1935,11 @@ // Load the vfptr and then callee from the vftable. The callee should have // adjusted 'this' so that the vfptr is at offset zero. llvm::Value *VTable = CGF.GetVTablePtr( - getThisAddress(CGF), ThunkTy->getPointerTo()->getPointerTo(), MD->getParent()); + getThisAddress(CGF), ThunkTy->getPointerTo()->getPointerTo(), + MD->getParent()); - llvm::Value *VFuncPtr = - CGF.Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn"); - llvm::Value *Callee = - CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); + llvm::Value *Callee = CGF.GetVirtualFunctionFromVTable( + MD->getParent(), VTable, ML.Index, ThunkTy); CGF.EmitMustTailThunk(MD, getThisValue(CGF), Callee); Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -5851,6 +5851,15 @@ CmdArgs.push_back("-fwhole-program-vtables"); } + // Add unstable C++ ABI flags. + if (Args.hasArg(options::OPT_flto_relative_cxx_abi_vtables)) { + if (!D.isUsingLTO()) + D.Diag(diag::err_drv_argument_only_allowed_with) + << "-fwhole-program-vtables" + << "-flto"; + CmdArgs.push_back("-flto-relative-c++-abi-vtables"); + } + // Finally add the compile command to the compilation. if (Args.hasArg(options::OPT__SLASH_fallback) && Output.getType() == types::TY_Object && Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -487,7 +487,6 @@ Opts.DebugColumnInfo = Args.hasArg(OPT_dwarf_column_info); Opts.EmitCodeView = Args.hasArg(OPT_gcodeview); Opts.WholeProgramVTables = Args.hasArg(OPT_fwhole_program_vtables); - Opts.LTOVisibilityPublicStd = Args.hasArg(OPT_flto_visibility_public_std); Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); Opts.DebugTypeExtRefs = Args.hasArg(OPT_dwarf_ext_refs); Opts.DebugExplicitImport = Triple.isPS4CPU(); @@ -2049,6 +2048,9 @@ Opts.SanitizeAddressFieldPadding = getLastArgIntValue(Args, OPT_fsanitize_address_field_padding, 0, Diags); Opts.SanitizerBlacklistFiles = Args.getAllArgValues(OPT_fsanitize_blacklist); + + Opts.LTOVisibilityPublicStd = Args.hasArg(OPT_flto_visibility_public_std); + Opts.LTORelativeCXXABIVTables = Args.hasArg(OPT_flto_relative_cxx_abi_vtables); } static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -4922,6 +4922,60 @@ } } +void Sema::checkClassABI(CXXRecordDecl *Record) { + if (!getLangOpts().LTORelativeCXXABIVTables) + return; + + // This can only be done accurately for non-dependent types, as the + // determination uses the class's bases, which may be dependent. + if (Record->isDependentType()) + return; + + // No need to do this for non-dynamic classes. + if (!Record->isDynamicClass()) + return; + + // First, see if this class inherits an ABI from a dynamic base class. If the + // bases disagree on which ABI to use, diagnose. + bool InheritsABI = false; + bool InheritedABIIsRelative; + CXXRecordDecl *InheritedABIFrom; + for (CXXBaseSpecifier &B : Record->bases()) { + auto Base = B.getType()->getAsCXXRecordDecl(); + // Base can be null in invalid programs (see PR16677). + if (!Base || !Base->isDynamicClass()) + continue; + if (InheritsABI) { + if (Base->isRelativeCXXABI() != InheritedABIIsRelative) { + Diag(Record->getLocation(), diag::err_abi_mismatch) << Record; + CXXRecordDecl *Platform = + InheritedABIIsRelative ? Base : InheritedABIFrom; + CXXRecordDecl *Relative = + InheritedABIIsRelative ? InheritedABIFrom : Base; + Diag(Platform->getLocation(), diag::note_abi_relative_base) + << Platform << /*Relative=*/false; + Diag(Relative->getLocation(), diag::note_abi_relative_base) + << Relative << /*Relative=*/true; + } + } else { + InheritsABI = true; + InheritedABIIsRelative = Base->isRelativeCXXABI(); + InheritedABIFrom = Base; + } + } + + // If the class's ABI is inherited, apply it. + if (InheritsABI) { + if (InheritedABIIsRelative) + Record->setIsRelativeCXXABI(); + return; + } + + // This class's ABI is not inherited. Infer it from LTO visibility. + if (Record->hasHiddenLTOVisibility()) + Record->setIsRelativeCXXABI(); +} + /// \brief Perform semantic checks on a class definition that has been /// completing, introducing implicitly-declared members, checking for /// abstract types, etc. @@ -5075,6 +5129,8 @@ DeclareInheritingConstructors(Record); checkClassLevelDLLAttribute(Record); + + checkClassABI(Record); } /// Look up the special member function that would be called by a special Index: test/CodeGenCXX/bitsets.cpp =================================================================== --- test/CodeGenCXX/bitsets.cpp +++ test/CodeGenCXX/bitsets.cpp @@ -5,8 +5,10 @@ // RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=MS --check-prefix=NDIAG %s // Tests for the whole-program-vtables feature: -// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM %s -// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS %s +// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM --check-prefix=ITANIUM-STABLE %s +// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS --check-prefix=MS-STABLE %s +// RUN: %clang_cc1 -flto -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -flto-relative-c++-abi-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM --check-prefix=ITANIUM-UNSTABLE %s +// RUN: %clang_cc1 -flto -triple x86_64-pc-windows-msvc -fwhole-program-vtables -flto-relative-c++-abi-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS --check-prefix=MS-UNSTABLE %s // MS: @[[VTA:[0-9]*]] {{.*}} comdat($"\01??_7A@@6B@") // MS: @[[VTB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6B0@@") @@ -171,41 +173,67 @@ // ITANIUM-NDIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){14}]]} // ITANIUM-DIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){23}]]} -// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16} +// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16} // ITANIUM-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @_ZTV1A, i64 16} -// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} // ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} +// ITANIUM-STABLE-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} // ITANIUM-DIAG-DAG: !{!"all-vtables", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} -// ITANIUM-DAG: !{!"_ZTS1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-STABLE-DAG: !{!"_ZTS1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} +// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} // ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DAG: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} +// ITANIUM-STABLE-DAG: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-STABLE-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} // ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} -// ITANIUM-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTV1B, i64 32} +// ITANIUM-STABLE-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTV1B, i64 32} // ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTV1B, i64 32} -// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTV1B, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [5 x i8*]* @_ZTV1C, i64 32} -// ITANIUM-DAG: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} -// ITANIUM-DAG: !{!{{[0-9]+}}, [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} - -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTA]], i64 8} +// ITANIUM-STABLE-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTV1B, i64 32} +// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [5 x i8*]* @_ZTV1C, i64 32} +// ITANIUM-STABLE-DAG: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32} +// ITANIUM-STABLE-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} +// ITANIUM-STABLE-DAG: !{!{{[0-9]+}}, [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} + +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i32 }* @_ZTV1A, i64 16} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32, i32, i32 }* @_ZTV1B, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1B", { i8*, i8*, i8*, i8*, i32, i32, i32 }* @_ZTV1B, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32 }* @_ZTV1C, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1C", { i8*, i8*, i8*, i8*, i32 }* @_ZTV1C, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32, i32, i32 }* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1B", { i8*, i8*, i8*, i8*, i32, i32, i32 }* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32, i8*, i8*, i8*, i32 }* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1C", { i8*, i8*, i8*, i8*, i32, i8*, i8*, i8*, i32 }* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i8*, i8*, i32, i32, i32, i8*, i8*, i8*, i8*, i32 }* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1B", { i8*, i8*, i8*, i8*, i32, i32, i32, i8*, i8*, i8*, i8*, i32 }* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1C", { i8*, i8*, i8*, i8*, i32, i32, i32, i8*, i8*, i8*, i8*, i32 }* @_ZTVN12_GLOBAL__N_11DE, i64 80} +// ITANIUM-UNSTABLE-DAG: !{![[DTYPE]], { i8*, i8*, i8*, i8*, i32, i32, i32, i8*, i8*, i8*, i8*, i32 }* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-UNSTABLE-DAG: !{!"_ZTS1A", { i8*, i8*, i32 }* @_ZTVZ3foovE2FA, i64 16} +// ITANIUM-UNSTABLE-DAG: !{!{{[0-9]+}}, { i8*, i8*, i32 }* @_ZTVZ3foovE2FA, i64 16} + +// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTA]], i64 8} // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTA]], i64 8} -// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8} +// MS-STABLE-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8} // MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTB]], i64 8} -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8} +// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8} // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinB]], i64 8} -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8} +// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8} // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinC]], i64 8} -// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTBinD]], i64 8} +// MS-STABLE-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTBinD]], i64 8} // MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTBinD]], i64 8} -// MS-DAG: !{![[DTYPE]], [3 x i8*]* @[[VTBinD]], i64 8} -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinBinD]], i64 8} +// MS-STABLE-DAG: !{![[DTYPE]], [3 x i8*]* @[[VTBinD]], i64 8} +// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinBinD]], i64 8} // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinBinD]], i64 8} -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTFA]], i64 8} +// MS-STABLE-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTFA]], i64 8} // MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTFA]], i64 8} -// MS-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8} +// MS-STABLE-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8} + +// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTA]], i64 8} +// MS-UNSTABLE-DAG: !{!"?AUB@@", { i8*, i32, i32 }* @[[VTB]], i64 8} +// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTAinB]], i64 8} +// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTAinC]], i64 8} +// MS-UNSTABLE-DAG: !{!"?AUB@@", { i8*, i32, i32 }* @[[VTBinD]], i64 8} +// MS-UNSTABLE-DAG: !{![[DTYPE]], { i8*, i32, i32 }* @[[VTBinD]], i64 8} +// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTAinBinD]], i64 8} +// MS-UNSTABLE-DAG: !{!"?AUA@@", { i8*, i32 }* @[[VTFA]], i64 8} +// MS-UNSTABLE-DAG: !{!{{[0-9]+}}, { i8*, i32 }* @[[VTFA]], i64 8} Index: test/CodeGenCXX/debug-info-virtual-fn-relative.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/debug-info-virtual-fn-relative.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fvisibility hidden -emit-llvm -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-STABLE %s +// RUN: %clang_cc1 -fvisibility hidden -flto-relative-c++-abi-vtables -emit-llvm -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-UNSTABLE %s + +struct S { + S(); + virtual void f(); +}; + +// CHECK-STABLE: virtualIndex: 0, +// CHECK-UNSTABLE: virtualIndex: 4294967295, +S::S() {} +void S::f() {} Index: test/CodeGenCXX/vtable-relative-abi.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/vtable-relative-abi.cpp @@ -0,0 +1,123 @@ +// RUN: %clang_cc1 %s -flto -triple x86_64-unknown-linux-gnu -fvisibility hidden -flto-relative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-ITANIUM %s +// RUN: %clang_cc1 %s -flto -triple x86_64-unknown-windows-msvc -flto-relative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MS %s +// RUN: %clang_cc1 %s -flto -triple x86_64-unknown-linux-gnu -fvisibility hidden -emit-llvm -o - | FileCheck --check-prefix=CHECK-NOABI %s + +// CHECK-ITANIUM: @_ZTV1S = hidden unnamed_addr constant { i8*, i8*, i32, i32 } { i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI1S to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @_ZN1S2f1Ev to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 }* @_ZTV1S, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @_ZN1S2f2Ev to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i8*, i32, i32 }, { i8*, i8*, i32, i32 }* @_ZTV1S, i32 0, i32 2) to i64)) to i32) }, align 8 +// CHECK-MS: @0 = private unnamed_addr constant { i8*, i32, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"\01??_R4S@@6B@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"\01?f1@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @0, i32 0, i32 1) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"\01?f2@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @0, i32 0, i32 1) to i64)) to i32) }, comdat($"\01??_7S@@6B@") +struct S { + S(); + virtual void f1(); + virtual void f2(); +}; + +// CHECK-ITANIUM: @_ZTV1T = hidden unnamed_addr constant { i8*, i8*, i32 } { i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI1T to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @_ZN1T1gEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i8*, i32 }, { i8*, i8*, i32 }* @_ZTV1T, i32 0, i32 2) to i64)) to i32) } +// CHECK-MS: @1 = private unnamed_addr constant { i8*, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"\01??_R4T@@6B@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @"\01?g@T@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @1, i32 0, i32 1) to i64)) to i32) }, comdat($"\01??_7T@@6B@") +struct T { + T(); + virtual void g(); +}; + +// CHECK-ITANIUM: @_ZTV1U = hidden unnamed_addr constant { i8*, i8*, i32, i32, i8*, i8*, i32 } { i8* null, i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }* @_ZTI1U to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.U*)* @_ZN1U2f1Ev to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i8*, i32, i32, i8*, i8*, i32 }, { i8*, i8*, i32, i32, i8*, i8*, i32 }* @_ZTV1U, i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @_ZN1S2f2Ev to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i8*, i32, i32, i8*, i8*, i32 }, { i8*, i8*, i32, i32, i8*, i8*, i32 }* @_ZTV1U, i32 0, i32 2) to i64)) to i32), i8* inttoptr (i64 -8 to i8*), i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64, i8*, i64 }* @_ZTI1U to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @_ZN1T1gEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i8*, i32, i32, i8*, i8*, i32 }, { i8*, i8*, i32, i32, i8*, i8*, i32 }* @_ZTV1U, i32 0, i32 6) to i64)) to i32) }, align 8 +// CHECK-MS: @2 = private unnamed_addr constant { i8*, i32, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"\01??_R4U@@6BS@@@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.U*)* @"\01?f1@U@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @2, i32 0, i32 1) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"\01?f2@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @2, i32 0, i32 1) to i64)) to i32) }, comdat($"\01??_7U@@6BS@@@") +// CHECK-MS: @3 = private unnamed_addr constant { i8*, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"\01??_R4U@@6BT@@@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @"\01?g@T@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i32 }, { i8*, i32 }* @3, i32 0, i32 1) to i64)) to i32) }, comdat($"\01??_7U@@6BT@@@") +struct U : S, T { + U(); + virtual void f1(); +}; + +S::S() {} +void S::f1() {} + +T::T() {} +void T::g() {} + +U::U() {} +void U::f1() {} + +struct V { + virtual void f(); +}; + +struct V1 : virtual V { +}; + +struct V2 : virtual V { +}; + +// CHECK-ITANIUM: @_ZTC2V30_2V1 = linkonce_odr hidden unnamed_addr constant { i8*, i8*, i8*, i8*, i32 } { i8* null, i8* null, i8* null, i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64 }* @_ZTI2V1 to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.V*)* @_ZN1V1fEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i8*, i8*, i8*, i32 }, { i8*, i8*, i8*, i8*, i32 }* @_ZTC2V30_2V1, i32 0, i32 4) to i64)) to i32) }, comdat +// CHECK-ITANIUM: @_ZTC2V38_2V2 = linkonce_odr hidden unnamed_addr constant { i8*, i8*, i8*, i8*, i32, i8*, i8*, i8*, i32 } { i8* inttoptr (i64 -8 to i8*), i8* inttoptr (i64 -8 to i8*), i8* null, i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64 }* @_ZTI2V2 to i8*), i32 0, i8* null, i8* inttoptr (i64 8 to i8*), i8* bitcast ({ i8*, i8*, i32, i32, i8*, i64 }* @_ZTI2V2 to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.V*)* @_ZN1V1fEv to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i8*, i8*, i8*, i8*, i32, i8*, i8*, i8*, i32 }, { i8*, i8*, i8*, i8*, i32, i8*, i8*, i8*, i32 }* @_ZTC2V38_2V2, i32 0, i32 8) to i64)) to i32) }, comdat +struct V3 : V1, V2 { + V3(); +}; + +V3::V3() {} + +// CHECK-ITANIUM: define hidden void @_Z5call1P1S +// CHECK-MS: define void @"\01?call1@@YAXPEAUS@@@Z" +void call1(S *s) { + // CHECK: [[VTABLE:%.*]] = load void + // CHECK: [[VTABLEI8:%.*]] = bitcast {{.*}} [[VTABLE]] to i8* + // CHECK: [[VFN:%.*]] = call i8* @llvm.load.relative.i32(i8* [[VTABLEI8]], i32 0) + // CHECK: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void ( + // CHECK: call void [[VFNCASTED]]( + s->f1(); +} + +// CHECK-ITANIUM: define hidden void @_Z5call2P1S +// CHECK-MS: define void @"\01?call2@@YAXPEAUS@@@Z" +void call2(S *s) { + // CHECK: [[VTABLE:%.*]] = load void + // CHECK: [[VTABLEI8:%.*]] = bitcast {{.*}} [[VTABLE]] to i8* + // CHECK: [[VFN:%.*]] = call i8* @llvm.load.relative.i32(i8* [[VTABLEI8]], i32 4) + // CHECK: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void ( + // CHECK: call void [[VFNCASTED]]( + s->f2(); +} + +typedef void (S::*mfp)(); + +// CHECK-ITANIUM: define hidden { i64, i64 } @_Z7getmfp1v() +// CHECK-ITANIUM: ret { i64, i64 } { i64 1, i64 0 } +// CHECK-MS: define i8* @"\01?getmfp1@@YAP8S@@EAAXXZXZ"() +// CHECK-MS: ret i8* bitcast {{.*}} @"\01??_9S@@$BA@AA" +// CHECK-MS: define linkonce_odr void @"\01??_9S@@$BA@AA" +// CHECK-MS: [[VTABLE:%.*]] = load void +// CHECK-MS: [[VTABLEI8:%.*]] = bitcast {{.*}} [[VTABLE]] to i8* +// CHECK-MS: [[VFN:%.*]] = call i8* @llvm.load.relative.i32(i8* [[VTABLEI8]], i32 0) +// CHECK-MS: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void ( +// CHECK-MS: musttail call {{.*}} [[VFNCASTED]]( +mfp getmfp1() { + return &S::f1; +} + +// CHECK-ITANIUM: define hidden { i64, i64 } @_Z7getmfp2v() +// CHECK-ITANIUM: ret { i64, i64 } { i64 5, i64 0 } +// CHECK-MS: define i8* @"\01?getmfp2@@YAP8S@@EAAXXZXZ"() +// CHECK-MS: ret i8* bitcast {{.*}} @"\01??_9S@@$B7AA" +// CHECK-MS: define linkonce_odr void @"\01??_9S@@$B7AA" +// CHECK-MS: [[VTABLE:%.*]] = load void +// CHECK-MS: [[VTABLEI8:%.*]] = bitcast {{.*}} [[VTABLE]] to i8* +// CHECK-MS: [[VFN:%.*]] = call i8* @llvm.load.relative.i32(i8* [[VTABLEI8]], i32 4) +// CHECK-MS: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void ( +// CHECK-MS: musttail call {{.*}} [[VFNCASTED]]( +mfp getmfp2() { + return &S::f2; +} + +// In the MS ABI virtual member function calls use thunks (which we already +// tested above), so there's nothing to test that's specific to the relative +// ABI. + +// CHECK-ITANIUM: define hidden void @_Z7callmfpP1SMS_FvvE +void callmfp(S *s, mfp p) { + // CHECK-ITANIUM: [[VTOFFSET:%.*]] = sub i64 {{.*}}, 1 + // CHECK-ITANIUM: [[VFN:%.*]] = call i8* @llvm.load.relative.i64(i8* {{.*}}, i64 [[VTOFFSET]]) + // CHECK-ITANIUM: bitcast i8* [[VFN]] to void ( + (s->*p)(); +} + +// CHECK-NOABI: !llvm.module.flags = !{[[MF:![0-9]+]]} +// CHECK-NOABI: [[MF]] = !{i32 1, !"lto-relative-c++-abi-vtables", i32 0} + +// CHECK: !llvm.module.flags = !{[[MF:![0-9]+]]} +// CHECK: [[MF]] = !{i32 1, !"lto-relative-c++-abi-vtables", i32 1} Index: test/Driver/unstable-cxx-abi-vtables.cpp =================================================================== --- /dev/null +++ test/Driver/unstable-cxx-abi-vtables.cpp @@ -0,0 +1,5 @@ +// RUN: %clang -flto-relative-c++-abi-vtables -### %s 2>&1 | FileCheck -check-prefix=CLASSES %s +// CLASSES: '-fwhole-program-vtables' only allowed with '-flto' +// +// RUN: %clang -flto -flto-relative-c++-abi-vtables -### %s 2>&1 | FileCheck -check-prefix=LTO-CLASSES %s +// LTO-CLASSES: "-flto-relative-c++-abi-vtables" Index: test/SemaCXX/unstable-cxx-abi.cpp =================================================================== --- /dev/null +++ test/SemaCXX/unstable-cxx-abi.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 %s -w -std=c++11 -fsyntax-only -verify -flto-relative-c++-abi-vtables -fvisibility hidden + +struct __attribute__((visibility("default"))) platform { // expected-note 2{{base 'platform' uses the platform ABI}} + virtual void f(); +}; +struct relative { // expected-note 2{{base 'relative' uses the relative ABI}} + virtual void f(); +}; + +struct mixed_bases : platform, relative {}; // expected-error {{inconsistent ABI for class 'mixed_bases'}} +struct mixed_base_vbase : platform, virtual relative {}; // expected-error {{inconsistent ABI for class 'mixed_base_vbase'}} + +void f() { + struct relative2 { + virtual void f(); + }; + struct relative_derived : relative, relative2 {}; +} + +struct __attribute__((visibility("default"))) platform2 { + void mf() { + struct platform3 { + virtual void f(); + }; + struct platform_derived : platform, platform3 {}; + } +}; + +struct __attribute__((visibility("default"))) non_dynamic {}; + +struct relative_derived : non_dynamic, relative {}; +struct platform_derived : non_dynamic, platform {}; + +struct relative_derived2 : relative_derived, relative {}; +struct platform_derived2 : platform_derived, platform {};