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; } + const VTableComponent &getVTableComponent(size_t i) const { + return VTableComponents[i]; + } + ArrayRef vtable_thunks() const { return VTableThunks; } 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 @@ -325,6 +325,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 @@ -1245,6 +1245,10 @@ "fno-fine-grained-bitfield-accesses">, Group, Flags<[CC1Option]>, HelpText<"Use large-integer access for consecutive bitfield runs.">; +def fexperimental_relative_cxx_abi_vtables : Flag<["-"], "fexperimental-relative-c++-abi-vtables">, + Group, Flags<[CC1Option]>, + HelpText<"Use the experimental 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/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" 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 @@ -2587,6 +2587,23 @@ return VTable; } +llvm::Value *CodeGenFunction::GetVirtualFunctionFromVTable( + const CXXRecordDecl *RD, llvm::Value *VTable, uint64_t VTableIndex, + llvm::Type *Ty) { + if (!getLangOpts().RelativeCXXABIVTables) { + 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 @@ -1542,8 +1542,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) && + !CGM.getLangOpts().RelativeCXXABIVTables) 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,30 @@ const ThunkInfo &ThunkAdjustments, bool ForVTable); - void addVTableComponent(ConstantArrayBuilder &builder, - const VTableLayout &layout, unsigned idx, - llvm::Constant *rtti, - unsigned &nextVTableThunkIndex); + /// Get the constant to be added to a VTable. + llvm::Constant *getVTableComponent(const VTableLayout &layout, + unsigned componentIdx, unsigned vtableIdx, + llvm::Constant *rtti, + unsigned &nextVTableThunkIndex); 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); + + /// 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 lastAddrPoint) const; + + /// Populate the fields of a single vtable element. This accepts either a + /// StructBuilder or ArrayBuilder. + template + void buildVTableElement(BuilderTy &builder, llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, llvm::Constant *RTTI, + unsigned &nextVTableThunkIndex, unsigned vtableIdx); CodeGenVTables(CodeGenModule &CGM); @@ -123,7 +136,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,44 @@ 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 lastAddrPoint) 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, lastAddrPoint)}); + + llvm::Constant *AddrPointInt = + llvm::ConstantExpr::getPtrToInt(gep, PtrDiffTy); + 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) { + auto &component = layout.getVTableComponent(componentIdx); + + llvm::Type *Int8PtrTy = CGM.Int8PtrTy; + llvm::Type *Int32Ty = CGM.Int32Ty; + llvm::Type *FunctionPtrTy = + CGM.getLangOpts().RelativeCXXABIVTables ? 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 +637,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 +671,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 +703,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++; @@ -686,40 +716,103 @@ fnPtr = CGM.GetAddrOfFunction(GD, fnTy, /*ForVTable=*/true); } - fnPtr = llvm::ConstantExpr::getBitCast(fnPtr, CGM.Int8PtrTy); - builder.add(fnPtr); - return; + return llvm::ConstantExpr::getBitCast(fnPtr, CGM.Int8PtrTy); } 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) { - SmallVector tys; +llvm::Type *CodeGenVTables::getVTableType(const CXXRecordDecl *RD, + const VTableLayout &layout) { + SmallVector types; for (unsigned i = 0, e = layout.getNumVTables(); i != e; ++i) { - tys.push_back(llvm::ArrayType::get(CGM.Int8PtrTy, layout.getVTableSize(i))); + if (!CGM.getLangOpts().RelativeCXXABIVTables) { + types.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); + } + types.push_back(llvm::StructType::get(CGM.getLLVMContext(), innerTypes)); + } } - return llvm::StructType::get(CGM.getLLVMContext(), tys); + return llvm::StructType::get(CGM.getLLVMContext(), types); } -void CodeGenVTables::createVTableInitializer(ConstantStructBuilder &builder, - const VTableLayout &layout, - llvm::Constant *rtti) { +template +void CodeGenVTables::buildVTableElement(BuilderTy &builder, + llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, + llvm::Constant *RTTI, + unsigned &nextVTableThunkIndex, + unsigned vtableIdx) { + // We want to point to the first function in the vtable. This would + // normally be the 3rd component under Itanium ABI (2nd for Microsoft), + // but not in the case of virtual inheritance. For both of these ABIs, the + // first virtual function comes after the RTTI component, so we can just + // check if the previous component was RTTI. + // + // FIXME: Need a better way of identifying address points that works with + // the Itanium and MS ABIs. + unsigned lastAddrPoint = 0; + + size_t thisIndex = VTLayout.getVTableOffset(vtableIdx); + size_t nextIndex = thisIndex + VTLayout.getVTableSize(vtableIdx); + for (unsigned componentIdx = thisIndex; componentIdx != nextIndex; + ++componentIdx) { + if (componentIdx > 0 && + VTLayout.getVTableComponent(componentIdx - 1).isRTTIKind()) + lastAddrPoint = componentIdx; + + llvm::Constant *component = getVTableComponent( + VTLayout, componentIdx, vtableIdx, RTTI, nextVTableThunkIndex); + + auto &layoutComponent = VTLayout.getVTableComponent(componentIdx); + if (CGM.getLangOpts().RelativeCXXABIVTables && + layoutComponent.isUsedFunctionPointerKind()) + component = + makeRelative(component, VTable, vtableIdx, lastAddrPoint - thisIndex); + + builder.add(component); + } +} + +void CodeGenVTables::SetVTableInitializer(llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout, + llvm::Constant *RTTI) { + 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) { + if (!CGM.getLangOpts().RelativeCXXABIVTables) { + auto vtableElem = components.beginArray(CGM.Int8PtrTy); + buildVTableElement(vtableElem, VTable, VTLayout, RTTI, + nextVTableThunkIndex, vtableIdx); + vtableElem.finishAndAddTo(components); + } else { + auto vtableElem = components.beginStruct(); + buildVTableElement(vtableElem, VTable, VTLayout, RTTI, + nextVTableThunkIndex, vtableIdx); + vtableElem.finishAndAddTo(components); } - vtableElem.finishAndAddTo(builder); } + + components.finishAndSetAsInitializer(VTable); } llvm::GlobalVariable * @@ -746,7 +839,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 +862,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); // Set properties only after the initializer has been set to ensure that the // GV is treated as definition and not declaration. @@ -1053,8 +1143,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/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -649,9 +649,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 (CGM.getLangOpts().RelativeCXXABIVTables) { + 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 @@ -897,7 +908,11 @@ const ASTContext &Context = getContext(); CharUnits PointerWidth = Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); - uint64_t VTableOffset = (Index * PointerWidth.getQuantity()); + uint64_t VTableOffset = Index; + if (CGM.getLangOpts().RelativeCXXABIVTables) + VTableOffset *= 4; + else + VTableOffset *= PointerWidth.getQuantity(); if (UseARMMethodPtrABI) { // ARM C++ ABI 3.2.1: @@ -1567,10 +1582,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); // Set the correct linkage. VTable->setLinkage(Linkage); @@ -1678,7 +1690,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 @@ -1700,9 +1712,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; @@ -1713,10 +1725,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. @@ -1725,11 +1735,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 @@ -1667,10 +1667,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); emitVTableTypeMetadata(*Info, RD, VTable); } @@ -1796,7 +1793,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. @@ -1829,6 +1826,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, @@ -1856,14 +1854,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); @@ -1889,9 +1885,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); @@ -2012,10 +2007,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 @@ -5333,6 +5333,10 @@ CmdArgs.push_back("-fwhole-program-vtables"); } + // Add unstable C++ ABI flags. + if (Args.hasArg(options::OPT_fexperimental_relative_cxx_abi_vtables)) + CmdArgs.push_back("-fexperimental-relative-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 @@ -3066,6 +3066,9 @@ Opts.CompleteMemberPointers = Args.hasArg(OPT_fcomplete_member_pointers); Opts.BuildingPCHWithObjectFile = Args.hasArg(OPT_building_pch_with_obj); + + Opts.RelativeCXXABIVTables = + Args.hasArg(OPT_fexperimental_relative_cxx_abi_vtables); } static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) { 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 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-STABLE %s +// RUN: %clang_cc1 -fexperimental-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() {} 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,117 @@ +// RUN: %clang_cc1 %s -triple x86_64-unknown-linux-gnu -fexperimental-relative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-ITANIUM %s +// RUN: %clang_cc1 %s -triple x86_64-unknown-windows-msvc -fexperimental-relative-c++-abi-vtables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MS %s + +// CHECK-ITANIUM: @_ZTV1S = 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 2) to i64)) to i32) } }, align 8 +// CHECK-MS: @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 } }* @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 } }* @0, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_7S@@6B@") + +struct S { + S(); + virtual void f1(); + virtual void f2(); +}; + +// CHECK-ITANIUM: @_ZTV1T = 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: @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 } }* @1, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_7T@@6B@") +struct T { + T(); + virtual void g(); +}; + +// CHECK-ITANIUM: @_ZTV1U = 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 2) 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: @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 } }* @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 } }* @2, i32 0, i32 0, i32 1) to i64)) to i32) } }, comdat($"??_7U@@6BS@@@") +// CHECK-MS: @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 } }* @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 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 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 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 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 { 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 { 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 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)(); +} diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -495,6 +495,35 @@ return false; } +/// Returns true if this constant mathes the relative referencing for virtual +/// functions introduced in the relative ABI. This is the difference between a +/// function and its corresponding vtable entry. +/// +/// i64 sub ( +/// i64 ptrtoint (i8* obj to i64), +// i64 ptrtoint ( +// i32* getelementptr inbounds (%object_vtable, %object_vtable* +// @_ZTV12derived_virt, i32 0, i32 0, i32 2) to i64) +static bool MatchesRelativeOffset(const Constant *C) { + const auto *CE = dyn_cast(C); + if (!CE || CE->getOpcode() != Instruction::Sub) + return false; + + const auto *LHS = dyn_cast(CE->getOperand(0)); + const auto *RHS = dyn_cast(CE->getOperand(1)); + if (!LHS || !RHS || LHS->getOpcode() != Instruction::PtrToInt || + RHS->getOpcode() != Instruction::PtrToInt) + return false; + + RHS = dyn_cast(RHS->getOperand(0)); + if (!RHS) + return false; + + return (isa(LHS->getOperand(0)) && + RHS->getOpcode() == Instruction::GetElementPtr && + isa(RHS->getOperand(0))); +} + bool Constant::needsRelocation() const { if (isa(this)) return true; // Global reference. @@ -519,6 +548,10 @@ return false; } + // This results in an R_PLT_PC reloc which can be computed at link time. + if (MatchesRelativeOffset(this)) + return false; + bool Result = false; for (unsigned i = 0, e = getNumOperands(); i != e; ++i) Result |= cast(getOperand(i))->needsRelocation();