diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -527,6 +527,9 @@ /// Whether we are currently parsing base specifiers. unsigned IsParsingBaseSpecifiers : 1; + /// \brief Whether the class uses the relative C++ vtable ABI. + unsigned IsRelativeCXXABI : 1; + unsigned HasODRHash : 1; /// A hash of parts of the class to help in ODR checking. @@ -807,6 +810,10 @@ return data().IsParsingBaseSpecifiers; } + void setIsRelativeCXXABI() { data().IsRelativeCXXABI = true; } + + bool isRelativeCXXABI() const { return data().IsRelativeCXXABI; } + unsigned getODRHash() const; /// Sets the base classes of this struct or class. diff --git a/clang/include/clang/AST/VTableBuilder.h b/clang/include/clang/AST/VTableBuilder.h --- a/clang/include/clang/AST/VTableBuilder.h +++ b/clang/include/clang/AST/VTableBuilder.h @@ -264,6 +264,10 @@ return VTableComponents; } + VTableComponent &getVTableComponent(size_t i) const { + return VTableComponents[i]; + } + ArrayRef vtable_thunks() const { return VTableThunks; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9534,4 +9534,9 @@ "%select{non-pointer|function pointer|void pointer}0 argument to " "'__builtin_launder' is not allowed">; +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. diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -322,6 +322,10 @@ LANGOPT(RegisterStaticDestructors, 1, 1, "Register C++ static destructors") +LANGOPT(RelativeCXXABIVTables, 1, 0, + "Whether to use clang's relative C++ ABI " + "for classes with vtables") + #undef LANGOPT #undef COMPATIBLE_LANGOPT #undef BENIGN_LANGOPT diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1236,6 +1236,10 @@ "fno-fine-grained-bitfield-accesses">, Group, Flags<[CC1Option]>, HelpText<"Use large-integer access for consecutive bitfield runs.">; +def frelative_cxx_abi_vtables : Flag<["-"], "frelative-c++-abi-vtables">, + Group, Flags<[CC1Option]>, + HelpText<"Use the unstable C++ class ABI for classes with virtual tables">; + def flat__namespace : Flag<["-"], "flat_namespace">; def flax_vector_conversions : Flag<["-"], "flax-vector-conversions">, Group; def flimited_precision_EQ : Joined<["-"], "flimited-precision=">, Group; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10926,6 +10926,12 @@ // (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); + private: class SavePendingParsedClassStateRAII { public: diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -35,6 +35,7 @@ #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/None.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" @@ -102,7 +103,8 @@ ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false), - IsParsingBaseSpecifiers(false), HasODRHash(false), Definition(D) {} + IsParsingBaseSpecifiers(false), IsRelativeCXXABI(false), + HasODRHash(false), Definition(D) {} CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { return Bases.get(Definition->getASTContext().getExternalSource()); diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2573,6 +2573,23 @@ 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. diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1537,8 +1537,11 @@ if (CGM.getTarget().getCXXABI().isItaniumFamily()) { // It doesn't make sense to give a virtual destructor a vtable index, // since a single destructor has two entries in the vtable. - if (!isa(Method)) + if (!isa(Method) && + !Method->getParent()->isRelativeCXXABI()) VIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(Method); + else + VIndex = -1u; } else { // Emit MS ABI vftable information. There is only one entry for the // deleting dtor. diff --git a/clang/lib/CodeGen/CGVTables.h b/clang/lib/CodeGen/CGVTables.h --- a/clang/lib/CodeGen/CGVTables.h +++ b/clang/lib/CodeGen/CGVTables.h @@ -61,17 +61,32 @@ const ThunkInfo &ThunkAdjustments, bool ForVTable); + /// Get the constant to be added to a VTable. + llvm::Constant *getVTableComponent(const VTableLayout &layout, + unsigned componentIdx, unsigned vtableIdx, + llvm::Constant *rtti, + unsigned &nextVTableThunkIndex, + bool RelativeABI, + llvm::GlobalVariable *VTable); + + /// Add a constant to a VTable through a builder. void addVTableComponent(ConstantArrayBuilder &builder, - const VTableLayout &layout, unsigned idx, - llvm::Constant *rtti, - unsigned &nextVTableThunkIndex); + const VTableLayout &layout, unsigned componentIdx, + unsigned vtableIdx, llvm::Constant *rtti, + unsigned &nextVTableThunkIndex, bool RelativeABI, + llvm::GlobalVariable *VTable); + + /// Make a function pointer into a relative integer when using the relative + /// vtables ABI. + llvm::Constant *makeRelative(llvm::Constant *C, llvm::GlobalVariable *VTable, + unsigned vtableIdx, unsigned relCompIdx) const; public: /// Add vtable components for the given vtable layout to the given /// global initializer. - void createVTableInitializer(ConstantStructBuilder &builder, - const VTableLayout &layout, - llvm::Constant *rtti); + void SetVTableInitializer(llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, llvm::Constant *RTTI, + bool RelativeABI); CodeGenVTables(CodeGenModule &CGM); @@ -123,7 +138,8 @@ /// Returns the type of a vtable with the given layout. Normally a struct of /// arrays of pointers, with one struct element for each vtable in the vtable /// group. - llvm::Type *getVTableType(const VTableLayout &layout); + llvm::Type *getVTableType(const CXXRecordDecl *RD, + const VTableLayout &layout); }; } // end namespace CodeGen diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -586,15 +586,45 @@ maybeEmitThunk(GD, Thunk, /*ForVTable=*/false); } -void CodeGenVTables::addVTableComponent( - ConstantArrayBuilder &builder, const VTableLayout &layout, - unsigned idx, llvm::Constant *rtti, unsigned &nextVTableThunkIndex) { - auto &component = layout.vtable_components()[idx]; +llvm::Constant *CodeGenVTables::makeRelative(llvm::Constant *C, + llvm::GlobalVariable *VTable, + unsigned vtableIdx, + unsigned relCompIdx) const { + llvm::Type *Int32Ty = CGM.Int32Ty; + llvm::Type *PtrDiffTy = + CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType()); + llvm::Type *VTableTy = VTable->getValueType(); + llvm::Constant *gep = llvm::ConstantExpr::getGetElementPtr( + VTableTy, VTable, + llvm::ArrayRef{ + llvm::ConstantInt::get(Int32Ty, 0), + llvm::ConstantInt::get(Int32Ty, vtableIdx), + llvm::ConstantInt::get(Int32Ty, relCompIdx)}); + llvm::Constant *AddrPointInt = + llvm::ConstantExpr::getPtrToInt(gep, PtrDiffTy); + + // FIXME: Need a better way of identifying address points that works with + // the Itanium and MS ABIs. + llvm::Constant *ptrToInt = llvm::ConstantExpr::getPtrToInt(C, PtrDiffTy); + return llvm::ConstantExpr::getIntegerCast( + llvm::ConstantExpr::getSub(ptrToInt, AddrPointInt), Int32Ty, + /*isSigned=*/true); +} + +llvm::Constant *CodeGenVTables::getVTableComponent( + const VTableLayout &layout, unsigned componentIdx, unsigned vtableIdx, + llvm::Constant *rtti, unsigned &nextVTableThunkIndex, bool RelativeABI, + llvm::GlobalVariable *VTable) { + auto &component = layout.getVTableComponent(componentIdx); + + llvm::Type *Int8PtrTy = CGM.Int8PtrTy; + llvm::Type *Int32Ty = CGM.Int32Ty; + llvm::Type *FunctionPtrTy = RelativeABI ? Int32Ty : Int8PtrTy; auto addOffsetConstant = [&](CharUnits offset) { - builder.add(llvm::ConstantExpr::getIntToPtr( + return llvm::ConstantExpr::getIntToPtr( llvm::ConstantInt::get(CGM.PtrDiffTy, offset.getQuantity()), - CGM.Int8PtrTy)); + CGM.Int8PtrTy); }; switch (component.getKind()) { @@ -608,7 +638,7 @@ return addOffsetConstant(component.getOffsetToTop()); case VTableComponent::CK_RTTI: - return builder.add(llvm::ConstantExpr::getBitCast(rtti, CGM.Int8PtrTy)); + return llvm::ConstantExpr::getBitCast(rtti, CGM.Int8PtrTy); case VTableComponent::CK_FunctionPointer: case VTableComponent::CK_CompleteDtorPointer: @@ -642,7 +672,7 @@ ? MD->hasAttr() : (MD->hasAttr() || !MD->hasAttr()); if (!CanEmitMethod) - return builder.addNullPointer(CGM.Int8PtrTy); + return llvm::ConstantExpr::getNullValue(FunctionPtrTy); // Method is acceptable, continue processing as usual. } @@ -674,7 +704,8 @@ // Thunks. } else if (nextVTableThunkIndex < layout.vtable_thunks().size() && - layout.vtable_thunks()[nextVTableThunkIndex].first == idx) { + layout.vtable_thunks()[nextVTableThunkIndex].first == + componentIdx) { auto &thunkInfo = layout.vtable_thunks()[nextVTableThunkIndex].second; nextVTableThunkIndex++; @@ -687,39 +718,94 @@ } fnPtr = llvm::ConstantExpr::getBitCast(fnPtr, CGM.Int8PtrTy); - builder.add(fnPtr); - return; + if (RelativeABI && component.isFunctionPointerKind()) { + size_t thisIndex = layout.getVTableOffset(vtableIdx); + return makeRelative(fnPtr, VTable, vtableIdx, componentIdx - thisIndex); + } + return fnPtr; } case VTableComponent::CK_UnusedFunctionPointer: - return builder.addNullPointer(CGM.Int8PtrTy); + return llvm::ConstantExpr::getNullValue(FunctionPtrTy); } llvm_unreachable("Unexpected vtable component kind"); } -llvm::Type *CodeGenVTables::getVTableType(const VTableLayout &layout) { +void CodeGenVTables::addVTableComponent( + ConstantArrayBuilder &builder, const VTableLayout &layout, + unsigned componentIdx, unsigned vtableIdx, llvm::Constant *rtti, + unsigned &nextVTableThunkIndex, bool RelativeABI, + llvm::GlobalVariable *VTable) { + builder.add(getVTableComponent(layout, componentIdx, vtableIdx, rtti, + nextVTableThunkIndex, RelativeABI, VTable)); +} + +llvm::Type *CodeGenVTables::getVTableType(const CXXRecordDecl *RD, + const VTableLayout &layout) { SmallVector tys; for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i) { - tys.push_back(llvm::ArrayType::get(CGM.Int8PtrTy, layout.getVTableSize(i))); + if (!RD->isRelativeCXXABI()) { + tys.push_back( + llvm::ArrayType::get(CGM.Int8PtrTy, layout.getVTableSize(i))); + } else { + SmallVector innerTypes; + size_t thisIndex = layout.getVTableOffset(i); + size_t nextIndex = thisIndex + layout.getVTableSize(i); + for (unsigned componentIdx = thisIndex; componentIdx != nextIndex; + ++componentIdx) { + auto &component = layout.getVTableComponent(componentIdx); + if (component.isFunctionPointerKind()) + innerTypes.push_back(CGM.Int32Ty); + else + innerTypes.push_back(CGM.Int8PtrTy); + } + tys.push_back(llvm::StructType::get(CGM.getLLVMContext(), innerTypes)); + } } return llvm::StructType::get(CGM.getLLVMContext(), tys); } -void CodeGenVTables::createVTableInitializer(ConstantStructBuilder &builder, - const VTableLayout &layout, - llvm::Constant *rtti) { +void CodeGenVTables::SetVTableInitializer(llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, + llvm::Constant *RTTI, + bool RelativeABI) { + ConstantInitBuilder builder(CGM); + auto components = builder.beginStruct(); + unsigned nextVTableThunkIndex = 0; - for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i) { - auto vtableElem = builder.beginArray(CGM.Int8PtrTy); - size_t thisIndex = layout.getVTableOffset(i); - size_t nextIndex = thisIndex + layout.getVTableSize(i); - for (unsigned i = thisIndex; i != nextIndex; ++i) { - addVTableComponent(vtableElem, layout, i, rtti, nextVTableThunkIndex); + for (unsigned vtableIdx = 0; vtableIdx != VTLayout.getNumVTables(); + ++vtableIdx) { + size_t thisIndex = VTLayout.getVTableOffset(vtableIdx); + size_t nextIndex = thisIndex + VTLayout.getVTableSize(vtableIdx); + + if (!RelativeABI) { + // Construct a normal array of pointers. + auto vtableElem = components.beginArray(CGM.Int8PtrTy); + for (unsigned componentIdx = thisIndex; componentIdx != nextIndex; + ++componentIdx) { + addVTableComponent(vtableElem, VTLayout, componentIdx, vtableIdx, RTTI, + nextVTableThunkIndex, RelativeABI, VTable); + } + vtableElem.finishAndAddTo(components); + } else { + // Instead use an i32 to indicate an offset. + SmallVector Inits; + for (unsigned componentIdx = thisIndex; componentIdx != nextIndex; + ++componentIdx) { + Inits.push_back(getVTableComponent(VTLayout, componentIdx, vtableIdx, + RTTI, nextVTableThunkIndex, + RelativeABI, VTable)); + } + auto *VTableTy = cast(VTable->getValueType()); + auto *StructTy = + cast(VTableTy->getElementType(vtableIdx)); + components.add(llvm::ConstantStruct::get(StructTy, Inits)); } - vtableElem.finishAndAddTo(builder); } + + components.finishAndSetAsInitializer(VTable); } llvm::GlobalVariable * @@ -746,7 +832,7 @@ Base.getBase(), Out); StringRef Name = OutName.str(); - llvm::Type *VTType = getVTableType(*VTLayout); + 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 @@ -769,10 +855,7 @@ CGM.getContext().getTagDeclType(Base.getBase())); // Create and set the initializer. - ConstantInitBuilder builder(CGM); - auto components = builder.beginStruct(); - createVTableInitializer(components, *VTLayout, RTTI); - components.finishAndSetAsInitializer(VTable); + SetVTableInitializer(VTable, *VTLayout, RTTI, RD->isRelativeCXXABI()); // Set properties only after the initializer has been set to ensure that the // GV is treated as definition and not declaration. @@ -1053,8 +1136,10 @@ ArrayRef Comps = VTLayout.vtable_components(); for (auto AP : AddressPoints) { + CharUnits ByteOffset = PointerWidth * AP.second; + // Create type metadata for the address point. - AddVTableTypeMetadata(VTable, PointerWidth * AP.second, AP.first); + AddVTableTypeMetadata(VTable, ByteOffset, AP.first); // The class associated with each address point could also potentially be // used for indirect calls via a member function pointer, so we need to diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1904,6 +1904,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, diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -531,6 +531,12 @@ CodeGenOpts.FlushDenorm ? 1 : 0); } + if (CodeGenOpts.PrepareForLTO) { + getModule().addModuleFlag(llvm::Module::Error, + "relative-c++-abi-vtables", + getLangOpts().RelativeCXXABIVTables); + } + // Emit OpenCL specific module metadata: OpenCL/SPIR version. if (LangOpts.OpenCL) { EmitOpenCLMetadata(); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -666,9 +666,20 @@ } // Load the virtual function to call. - VFPAddr = Builder.CreateBitCast(VFPAddr, FTy->getPointerTo()->getPointerTo()); - llvm::Value *VirtualFn = Builder.CreateAlignedLoad( - VFPAddr, CGF.getPointerAlign(), "memptr.virtualfn"); + llvm::Value *VirtualFn; + if (RD->hasDefinition() && 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.CreateBitCast(VFPAddr, 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 @@ -914,7 +925,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: @@ -1584,10 +1599,7 @@ CGM.GetAddrOfRTTIDescriptor(CGM.getContext().getTagDeclType(RD)); // Create and set the initializer. - ConstantInitBuilder Builder(CGM); - auto Components = Builder.beginStruct(); - CGVT.createVTableInitializer(Components, VTLayout, RTTI); - Components.finishAndSetAsInitializer(VTable); + CGVT.SetVTableInitializer(VTable, VTLayout, RTTI, RD->isRelativeCXXABI()); // Set the correct linkage. VTable->setLinkage(Linkage); @@ -1695,7 +1707,7 @@ const VTableLayout &VTLayout = CGM.getItaniumVTableContext().getVTableLayout(RD); - llvm::Type *VTableType = CGM.getVTables().getVTableType(VTLayout); + llvm::Type *VTableType = CGM.getVTables().getVTableType(RD, VTLayout); // Use pointer alignment for the vtable. Otherwise we would align them based // on the size of the initializer which doesn't make sense as only single @@ -1717,9 +1729,9 @@ Address This, llvm::Type *Ty, SourceLocation Loc) { - 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()); uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); llvm::Value *VFunc; @@ -1730,10 +1742,8 @@ } else { CGF.EmitTypeMetadataCodeForVCall(MethodDecl->getParent(), VTable, Loc); - llvm::Value *VFuncPtr = - CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn"); - auto *VFuncLoad = - CGF.Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); + auto *VFuncLoad = CGF.GetVirtualFunctionFromVTable(MethodDecl->getParent(), + VTable, VTableIndex, Ty); // Add !invariant.load md to virtual function load to indicate that // function didn't change inside vtable. @@ -1742,11 +1752,14 @@ // the same virtual function loads from the same vtable load, which won't // happen without enabled devirtualization with -fstrict-vtable-pointers. if (CGM.getCodeGenOpts().OptimizationLevel > 0 && - CGM.getCodeGenOpts().StrictVTablePointers) - VFuncLoad->setMetadata( - llvm::LLVMContext::MD_invariant_load, - llvm::MDNode::get(CGM.getLLVMContext(), - llvm::ArrayRef())); + CGM.getCodeGenOpts().StrictVTablePointers) { + if (auto *VFuncLoadInstr = dyn_cast(VFuncLoad)) { + VFuncLoadInstr->setMetadata( + llvm::LLVMContext::MD_invariant_load, + llvm::MDNode::get(CGM.getLLVMContext(), + llvm::ArrayRef())); + } + } VFunc = VFuncLoad; } diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -1646,10 +1646,7 @@ [](const VTableComponent &VTC) { return VTC.isRTTIKind(); })) RTTI = getMSCompleteObjectLocator(RD, *Info); - ConstantInitBuilder Builder(CGM); - auto Components = Builder.beginStruct(); - CGVT.createVTableInitializer(Components, VTLayout, RTTI); - Components.finishAndSetAsInitializer(VTable); + CGVT.SetVTableInitializer(VTable, VTLayout, RTTI, RD->isRelativeCXXABI()); emitVTableTypeMetadata(*Info, RD, VTable); } @@ -1775,7 +1772,7 @@ StringRef VTableName = VTableAliasIsRequred ? StringRef() : VFTableName.str(); - llvm::Type *VTableType = CGM.getVTables().getVTableType(VTLayout); + 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. @@ -1808,6 +1805,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, @@ -1835,14 +1833,12 @@ Address This, llvm::Type *Ty, SourceLocation Loc) { - 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 &VFTContext = CGM.getMicrosoftVTableContext(); MethodVFTableLocation ML = VFTContext.getMethodVFTableLocation(GD); @@ -1868,9 +1864,8 @@ if (CGM.getCodeGenOpts().PrepareForLTO) CGF.EmitTypeMetadataCodeForVCall(getObjectWithVPtr(), VTable, Loc); - llvm::Value *VFuncPtr = - Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn"); - VFunc = Builder.CreateAlignedLoad(VFuncPtr, CGF.getPointerAlign()); + VFunc = CGF.GetVirtualFunctionFromVTable(MethodDecl->getParent(), VTable, + ML.Index, Ty); } CGCallee Callee(GD, VFunc); @@ -1992,10 +1987,8 @@ llvm::Value *VTable = CGF.GetVTablePtr( 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), {ThunkTy, Callee}); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5265,6 +5265,10 @@ CmdArgs.push_back("-fwhole-program-vtables"); } + // Add unstable C++ ABI flags. + if (Args.hasArg(options::OPT_frelative_cxx_abi_vtables)) + CmdArgs.push_back("-frelative-c++-abi-vtables"); + bool RequiresSplitLTOUnit = WholeProgramVTables || Sanitize.needsLTO(); bool SplitLTOUnit = Args.hasFlag(options::OPT_fsplit_lto_unit, diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3030,6 +3030,8 @@ Opts.CompleteMemberPointers = Args.hasArg(OPT_fcomplete_member_pointers); Opts.BuildingPCHWithObjectFile = Args.hasArg(OPT_building_pch_with_obj); + + Opts.RelativeCXXABIVTables = Args.hasArg(OPT_frelative_cxx_abi_vtables); } static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -5885,6 +5885,18 @@ } } +void Sema::checkClassABI(CXXRecordDecl *Record) { + if (!getLangOpts().RelativeCXXABIVTables) + 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; + + Record->setIsRelativeCXXABI(); +} + /// Determine whether a type is permitted to be passed or returned in /// registers, per C++ [class.temporary]p3. static bool canPassInRegisters(Sema &S, CXXRecordDecl *D, @@ -6164,6 +6176,7 @@ checkClassLevelDLLAttribute(Record); checkClassLevelCodeSegAttribute(Record); + checkClassABI(Record); bool ClangABICompat4 = Context.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver4; diff --git a/clang/test/CodeGenCXX/debug-info-virtual-fn-relative.cpp b/clang/test/CodeGenCXX/debug-info-virtual-fn-relative.cpp new file mode 100644 --- /dev/null +++ b/clang/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 -frelative-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() {} diff --git a/clang/test/CodeGenCXX/type-metadata.cpp b/clang/test/CodeGenCXX/type-metadata.cpp --- a/clang/test/CodeGenCXX/type-metadata.cpp +++ b/clang/test/CodeGenCXX/type-metadata.cpp @@ -5,18 +5,22 @@ // RUN: %clang_cc1 -flto -flto-unit -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-NVT --check-prefix=MS --check-prefix=TT-MS --check-prefix=NDIAG %s // Tests for the whole-program-vtables feature: -// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM %s -// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS --check-prefix=TT-MS %s +// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM --check-prefix=ITANIUM-STABLE %s +// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -frelative-c++-abi-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=TT-ITANIUM --check-prefix=ITANIUM-UNSTABLE %s +// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS --check-prefix=TT-MS --check-prefix=MS-STABLE %s +// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-pc-windows-msvc -fwhole-program-vtables -frelative-c++-abi-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=TT-MS --check-prefix=MS-UNSTABLE %s // Tests for cfi + whole-program-vtables: // RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-VT --check-prefix=ITANIUM --check-prefix=TC-ITANIUM %s // RUN: %clang_cc1 -flto -flto-unit -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-VT --check-prefix=MS --check-prefix=TC-MS %s -// ITANIUM: @_ZTV1A = {{[^!]*}}, !type [[A16:![0-9]+]] +// ITANIUM: @_ZTV1A = {{.*}} { [3 x i8*] } {{[^!]*}}, !type [[A16:![0-9]+]] +// ITANIUM-UNSTABLE: @_ZTV1A = {{.*}} { { i8*, i8*, i32 } } {{[^!]*}}, !type [[A16:![0-9]+]] // ITANIUM-DIAG-SAME: !type [[ALL16:![0-9]+]] // ITANIUM-SAME: !type [[AF16:![0-9]+]] -// ITANIUM: @_ZTV1B = {{[^!]*}}, !type [[A32:![0-9]+]] +// ITANIUM: @_ZTV1B = {{.*}} { [7 x i8*] } {{[^!]*}}, !type [[A32:![0-9]+]] +// ITANIUM-UNSTABLE: @_ZTV1B = {{.*}} { { i8*, i8*, i8*, i8*, i32, i32, i32 } } {{[^!]*}}, !type [[A32:![0-9]+]] // ITANIUM-DIAG-SAME: !type [[ALL32:![0-9]+]] // ITANIUM-SAME: !type [[AF32:![0-9]+]] // ITANIUM-SAME: !type [[AF40:![0-9]+]] @@ -27,7 +31,8 @@ // ITANIUM-SAME: !type [[BF40:![0-9]+]] // ITANIUM-SAME: !type [[BF48:![0-9]+]] -// ITANIUM: @_ZTV1C = {{[^!]*}}, !type [[A32]] +// ITANIUM: @_ZTV1C = {{.*}} { [5 x i8*] } {{[^!]*}}, !type [[A32]] +// ITANIUM-UNSTABLE: @_ZTV1C = {{.*}} { { i8*, i8*, i8*, i8*, i32 } } {{[^!]*}}, !type [[A32]] // ITANIUM-DIAG-SAME: !type [[ALL32]] // ITANIUM-SAME: !type [[AF32]] // ITANIUM-SAME: !type [[C32:![0-9]+]] @@ -38,7 +43,8 @@ // DIAG: @[[TYPE:.*]] = private unnamed_addr constant { i16, i16, [4 x i8] } { i16 -1, i16 0, [4 x i8] c"'A'\00" } // DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { i8, { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }* } { i8 0, { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 123, i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]] } -// ITANIUM: @_ZTVN12_GLOBAL__N_11DE = {{[^!]*}}, !type [[A32]] +// ITANIUM: @_ZTVN12_GLOBAL__N_11DE = {{.*}} { [7 x i8*], [5 x i8*] } {{[^!]*}}, !type [[A32]] +// ITANIUM-UNSTABLE: @_ZTVN12_GLOBAL__N_11DE = {{.*}} { { i8*, i8*, i8*, i8*, i32, i32, i32 }, { i8*, i8*, i8*, i8*, i32 } } {{[^!]*}}, !type [[A32]] // ITANIUM-DIAG-SAME: !type [[ALL32]] // ITANIUM-SAME: !type [[AF32]] // ITANIUM-SAME: !type [[AF40]] @@ -59,32 +65,43 @@ // ITANIUM-SAME: !type [[DF40:![0-9]+]] // ITANIUM-SAME: !type [[DF48:![0-9]+]] -// ITANIUM: @_ZTCN12_GLOBAL__N_11DE0_1B = {{[^!]*}}, !type [[A32]] +// ITANIUM: @_ZTCN12_GLOBAL__N_11DE0_1B = {{.*}} { [7 x i8*] } {{[^!]*}}, !type [[A32]] +// ITANIUM-UNSTABLE: @_ZTCN12_GLOBAL__N_11DE0_1B = {{.*}} { { i8*, i8*, i8*, i8*, i32, i32, i32 } } {{[^!]*}}, !type [[A32]] // ITANIUM-DIAG-SAME: !type [[ALL32]] // ITANIUM-SAME: !type [[B32]] // ITANIUM-DIAG-SAME: !type [[ALL32]] -// ITANIUM: @_ZTCN12_GLOBAL__N_11DE8_1C = {{[^!]*}}, !type [[A64:![0-9]+]] +// ITANIUM: @_ZTCN12_GLOBAL__N_11DE8_1C = {{.*}} { [5 x i8*], [4 x i8*] } {{[^!]*}}, !type [[A64:![0-9]+]] +// ITANIUM-UNSTABLE: @_ZTCN12_GLOBAL__N_11DE8_1C = {{.*}} { { i8*, i8*, i8*, i8*, i32 }, { i8*, i8*, i8*, i32 } } {{[^!]*}}, !type [[A64:![0-9]+]] // ITANIUM-DIAG-SAME: !type [[ALL64:![0-9]+]] // ITANIUM-SAME: !type [[AF64:![0-9]+]] // ITANIUM-SAME: !type [[C32]] // ITANIUM-DIAG-SAME: !type [[ALL32]] // ITANIUM-SAME: !type [[CF64:![0-9]+]] -// ITANIUM: @_ZTVZ3foovE2FA = {{[^!]*}}, !type [[A16]] +// ITANIUM: @_ZTVZ3foovE2FA = {{.*}} { [3 x i8*] } {{[^!]*}}, !type [[A16]] +// ITANIUM-UNSTABLE: @_ZTVZ3foovE2FA = {{.*}} { { i8*, i8*, i32 } } {{[^!]*}}, !type [[A16]] // ITANIUM-DIAG-SAME: !type [[ALL16]] // ITANIUM-SAME: !type [[AF16]] // ITANIUM-SAME: !type [[FA16:![0-9]+]] // ITANIUM-DIAG-SAME: !type [[ALL16]] // ITANIUM-SAME: !type [[FAF16:![0-9]+]] -// MS: comdat($"??_7A@@6B@"), !type [[A8:![0-9]+]] -// MS: comdat($"??_7B@@6B0@@"), !type [[B8:![0-9]+]] -// MS: comdat($"??_7B@@6BA@@@"), !type [[A8]] -// MS: comdat($"??_7C@@6B@"), !type [[A8]] -// MS: comdat($"??_7D@?A0x{{[^@]*}}@@6BB@@@"), !type [[B8]], !type [[D8:![0-9]+]] -// MS: comdat($"??_7D@?A0x{{[^@]*}}@@6BA@@@"), !type [[A8]] -// MS: comdat($"??_7FA@?1??foo@@YAXXZ@6B@"), !type [[A8]], !type [[FA8:![0-9]+]] +// MS-STABLE: @anon.[[NUM:[a-z0-9]+]].0 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7A@@6B@"), !type [[A8:![0-9]+]] +// MS-STABLE: @anon.[[NUM]].1 = {{.*}} { [3 x i8*] } {{.*}} comdat($"??_7B@@6B0@@"), !type [[B8:![0-9]+]] +// MS-STABLE: @anon.[[NUM]].2 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7B@@6BA@@@"), !type [[A8]] +// MS-STABLE: @anon.[[NUM]].3 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7C@@6B@"), !type [[A8]] +// MS-STABLE: @anon.[[NUM]].4 = {{.*}} { [3 x i8*] } {{.*}} comdat($"??_7D@?A0x{{[^@]*}}@@6BB@@@"), !type [[B8]], !type [[D8:![0-9]+]] +// MS-STABLE: @anon.[[NUM]].5 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7D@?A0x{{[^@]*}}@@6BA@@@"), !type [[A8]] +// MS-STABLE: @anon.[[NUM]].6 = {{.*}} { [2 x i8*] } {{.*}} comdat($"??_7FA@?1??foo@@YAXXZ@6B@"), !type [[A8]], !type [[FA8:![0-9]+]] + +// MS-UNSTABLE: @anon.[[NUM:[a-z0-9]+]].0 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7A@@6B@"), !type [[A8:![0-9]+]] +// MS-UNSTABLE: @anon.[[NUM]].1 = {{.*}} { { i8*, i32, i32 } } {{.*}} comdat($"??_7B@@6B0@@"), !type [[B8:![0-9]+]] +// MS-UNSTABLE: @anon.[[NUM]].2 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7B@@6BA@@@"), !type [[A8]] +// MS-UNSTABLE: @anon.[[NUM]].3 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7C@@6B@"), !type [[A8]] +// MS-UNSTABLE: @anon.[[NUM]].4 = {{.*}} { { i8*, i32, i32 } } {{.*}} comdat($"??_7D@?A0x{{[^@]*}}@@6BB@@@"), !type [[B8]], !type [[D8:![0-9]+]] +// MS-UNSTABLE: @anon.[[NUM]].5 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7D@?A0x{{[^@]*}}@@6BA@@@"), !type [[A8]] +// MS-UNSTABLE: @anon.[[NUM]].6 = {{.*}} { { i8*, i32 } } {{.*}} comdat($"??_7FA@?1??foo@@YAXXZ@6B@"), !type [[A8]], !type [[FA8:![0-9]+]] struct A { A(); @@ -283,9 +300,9 @@ // ITANIUM: [[FAF16]] = !{i64 16, [[FAF_ID:![0-9]+]]} // ITANIUM: [[FAF_ID]] = distinct !{} -// MS: [[A8]] = !{i64 8, !"?AUA@@"} -// MS: [[B8]] = !{i64 8, !"?AUB@@"} -// MS: [[D8]] = !{i64 8, [[D_ID:![0-9]+]]} -// MS: [[D_ID]] = distinct !{} -// MS: [[FA8]] = !{i64 8, [[FA_ID:![0-9]+]]} -// MS: [[FA_ID]] = distinct !{} +// MS-STABLE: [[A8]] = !{i64 8, !"?AUA@@"} +// MS-STABLE: [[B8]] = !{i64 8, !"?AUB@@"} +// MS-STABLE: [[D8]] = !{i64 8, [[D_ID:![0-9]+]]} +// MS-STABLE: [[D_ID]] = distinct !{} +// MS-STABLE: [[FA8]] = !{i64 8, [[FA_ID:![0-9]+]]} +// MS-STABLE: [[FA_ID]] = distinct !{} diff --git a/clang/test/CodeGenCXX/vtable-relative-abi.cpp b/clang/test/CodeGenCXX/vtable-relative-abi.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/vtable-relative-abi.cpp @@ -0,0 +1,123 @@ +// RUN: %clang_cc1 %s -flto -flto-unit -triple x86_64-unknown-linux-gnu -fvisibility hidden -frelative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-ITANIUM %s +// RUN: %clang_cc1 %s -flto -flto-unit -triple x86_64-unknown-windows-msvc -frelative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MS %s +// RUN: %clang_cc1 %s -flto -flto-unit -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*, 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 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 0, i32 3) to i64)) to i32) } }, align 8 +// CHECK-MS: @anon.[[NUM:[a-z0-9]+]].0 = private unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"??_R4S@@6B@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"?f1@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @anon.[[NUM]].0, i32 0, i32 0, i32 1) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"?f2@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @anon.[[NUM]].0, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat($"??_7S@@6B@") +struct S { + S(); + virtual void f1(); + virtual void f2(); +}; + +// CHECK-ITANIUM: @_ZTV1T = hidden unnamed_addr constant { { i8*, i8*, i32 } } { { 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 0, i32 2) to i64)) to i32) } }, align 8 +// CHECK-MS: @anon.[[NUM]].1 = private unnamed_addr constant { { i8*, i32 } } { { i8*, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"??_R4T@@6B@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @"?g@T@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32 } }, { { i8*, i32 } }* @anon.[[NUM]].1, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_7T@@6B@") +struct T { + T(); + virtual void g(); +}; + +// CHECK-ITANIUM: @_ZTV1U = hidden unnamed_addr constant { { i8*, i8*, i32, i32 }, { i8*, i8*, i32 } } { { i8*, i8*, i32, 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 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 0, i32 3) to i64)) to i32) }, { i8*, i8*, 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 1, i32 2) to i64)) to i32) } }, align 8 +// CHECK-MS: @anon.[[NUM]].2 = private unnamed_addr constant { { i8*, i32, i32 } } { { i8*, i32, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"??_R4U@@6BS@@@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.U*)* @"?f1@U@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @anon.[[NUM]].2, i32 0, i32 0, i32 1) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.S*)* @"?f2@S@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32, i32 } }, { { i8*, i32, i32 } }* @anon.[[NUM]].2, i32 0, i32 0, i32 2) to i64)) to i32) } }, comdat($"??_7U@@6BS@@@") +// CHECK-MS: @anon.[[NUM]].3 = private unnamed_addr constant { { i8*, i32 } } { { i8*, i32 } { i8* bitcast (%rtti.CompleteObjectLocator* @"??_R4U@@6BT@@@" to i8*), i32 trunc (i64 sub (i64 ptrtoint (void (%struct.T*)* @"?g@T@@UEAAXXZ" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ { i8*, i32 } }, { { i8*, i32 } }* @anon.[[NUM]].3, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_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*, 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 0, i32 4) to i64)) to i32) } }, comdat, align 8 +// CHECK-ITANIUM: @_ZTC2V38_2V2 = linkonce_odr hidden unnamed_addr constant { { i8*, i8*, i8*, i8*, i32 }, { i8*, i8*, i8*, i32 } } { { i8*, 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*, i8*, i8*, i32 } { 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 1, i32 3) to i64)) to i32) } }, comdat, align 8 +struct V3 : V1, V2 { + V3(); +}; + +V3::V3() {} + +// CHECK-ITANIUM: define hidden void @_Z5call1P1S +// CHECK-MS: define {{.*}} void @"?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 @"?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* @"?getmfp1@@YAP8S@@EAAXXZXZ"() +// CHECK-MS: ret i8* bitcast {{.*}} @"??_9S@@$BA@AA" +// CHECK-MS: define linkonce_odr void @"??_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* @"?getmfp2@@YAP8S@@EAAXXZXZ"() +// CHECK-MS: ret i8* bitcast {{.*}} @"??_9S@@$B7AA" +// CHECK-MS: define linkonce_odr void @"??_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 = !{{{![0-9]+}}, [[MF:![0-9]+]]} +// CHECK-NOABI: [[MF]] = !{i32 1, !"relative-c++-abi-vtables", i32 0} + +// CHECK: !llvm.module.flags = !{{{![0-9]+}}, [[MF:![0-9]+]]} +// CHECK: [[MF]] = !{i32 1, !"relative-c++-abi-vtables", i32 1} diff --git a/clang/test/Driver/unstable-cxx-abi-vtables.cpp b/clang/test/Driver/unstable-cxx-abi-vtables.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Driver/unstable-cxx-abi-vtables.cpp @@ -0,0 +1,2 @@ +// RUN: %clang -flto -frelative-c++-abi-vtables -### %s 2>&1 | FileCheck -check-prefix=LTO-CLASSES %s +// LTO-CLASSES: "-frelative-c++-abi-vtables" diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -908,7 +908,7 @@ } // Copy relocations are only possible if we are creating an executable. - if (Config->Shared) { + if (Config->Shared && Type == Target->CopyRel) { errorOrWarn("relocation " + toString(Type) + " cannot be used against symbol " + toString(Sym) + "; recompile with -fPIC" + getLocation(Sec, Sym, Offset));