Index: docs/UsersManual.rst =================================================================== --- docs/UsersManual.rst +++ docs/UsersManual.rst @@ -1165,6 +1165,52 @@ // value of -fmax-type-align. } +**-funstable-c++-abi-classes** + +**-funstable-c++-abi-classes=PATH** + + These flags control whether the compiler uses a more space-efficient + virtual table ABI, known as the relative ABI. The first flag enables the + ABI for all classes in the program, while the second flag enables the + ABI for classes in namespaces named in the file at ``PATH``. + + 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. + + The format of the namespace list file is a series of lines ending in + either ``*`` or ``**``. Preceding that is a namespace specifier delimited + by double-colons followed by ``::``, or the empty string to denote the + global namespace. ``#`` serves as a comment character. Each entry in the + list indicates that classes in that namespace, including nested classes, + classes defined in enclosed anonymous namespaces, and classes defined + within member functions of those classes, may use the relative ABI. + + If the line ends in ``*`` this applies to the given namespace only, + while if the line ends in ``**`` it applies to the given namespace and + any enclosed namespaces. + + It is not guaranteed that all such classes 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. + + Example: + + .. code-block:: console + + # Enables the unstable ABI for classes in the global namespace. + * + + # Enables the unstable ABI for classes in the base namespace, and + # any enclosed namespaces. + base::** + + Note that it is recommended to use the ``[[clang::unstable_abi]]`` + attribute instead of these flags wherever possible. Profile Guided Optimization --------------------------- Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -1163,9 +1163,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() && + !CGM.getVTables().shouldUseRelativeABI(Method->getParent())) 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,14 @@ void GenerateClassData(const CXXRecordDecl *RD); bool isVTableExternal(const CXXRecordDecl *RD); + + /// Whether to use the relative vtable ABI for this record. + bool shouldUseRelativeABI(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 @@ -523,13 +523,35 @@ 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) { +bool CodeGenVTables::shouldUseRelativeABI(const CXXRecordDecl *RD) { + return RD->isUnstableCXXABI(); +} + +llvm::Type *CodeGenVTables::GetVTableType(const CXXRecordDecl *RD, + const VTableLayout &VTLayout) { + if (!shouldUseRelativeABI(RD)) + 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()); @@ -538,9 +560,36 @@ llvm::Constant *PureVirtualFn = nullptr, *DeletedVirtualFn = nullptr; + bool RelativeABI = shouldUseRelativeABI(RD); + 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()) { @@ -594,7 +643,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. @@ -612,7 +661,7 @@ PureVirtualFn = llvm::ConstantExpr::getBitCast(PureVirtualFn, CGM.Int8PtrTy); } - Init = PureVirtualFn; + Init = maybeMakeRelative(PureVirtualFn); } else if (cast(GD.getDecl())->isDeleted()) { if (!DeletedVirtualFn) { llvm::FunctionType *Ty = @@ -625,7 +674,7 @@ DeletedVirtualFn = llvm::ConstantExpr::getBitCast(DeletedVirtualFn, CGM.Int8PtrTy); } - Init = DeletedVirtualFn; + Init = maybeMakeRelative(DeletedVirtualFn); } else { // Check if we should use a thunk. if (NextVTableThunkIndex < NumVTableThunks && @@ -643,20 +692,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 * @@ -683,8 +738,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 @@ -696,7 +750,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. @@ -706,12 +760,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; Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1383,6 +1383,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/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -589,13 +589,24 @@ llvm::Value *VTableOffset = FnAsInt; if (!UseARMMethodPtrABI) VTableOffset = Builder.CreateSub(VTableOffset, ptrdiff_1); - VTable = Builder.CreateGEP(VTable, VTableOffset); + llvm::Value *VTableSlotPtr = 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 (CGM.getVTables().shouldUseRelativeABI(RD)) { + VTableSlotPtr = + Builder.CreateBitCast(VTableSlotPtr, CGF.Int32Ty->getPointerTo()); + llvm::Value *VirtualFnOffset = + Builder.CreateAlignedLoad(VTableSlotPtr, 4, "memptr.vfnoffset"); + llvm::Value *VTableInt8Ptr = Builder.CreateBitCast(VTable, CGF.Int8PtrTy); + VirtualFn = Builder.CreateGEP(VTableInt8Ptr, VirtualFnOffset); + VirtualFn = Builder.CreateBitCast(VirtualFn, FTy->getPointerTo()); + } else { + 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 @@ -803,7 +814,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.getVTables().shouldUseRelativeABI(MD->getParent())) + VTableOffset *= 4; + else + VTableOffset *= PointerWidth.getQuantity(); if (UseARMMethodPtrABI) { // ARM C++ ABI 3.2.1: @@ -1465,10 +1480,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); @@ -1576,12 +1591,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()) @@ -1598,16 +1613,35 @@ 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 *CodeGenFunction::GetVirtualFunctionFromVTable(const CXXRecordDecl *RD, + llvm::Value *VTable, + uint64_t VTableIndex, + llvm::Type *Ty) { + if (!CGM.getVTables().shouldUseRelativeABI(RD)) { + 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, Int32Ty->getPointerTo()); + llvm::Value *VTableSlotPtr = + Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn"); + llvm::Value *VTableSlot = Builder.CreateAlignedLoad(VTableSlotPtr, 4); + llvm::Value *VTableInt8Ptr = Builder.CreateBitCast(VTable, Int8PtrTy); + llvm::Value *FnPtr = Builder.CreateGEP(VTableInt8Ptr, VTableSlot); + return Builder.CreateBitCast(FnPtr, Ty->getPointerTo()); } llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall( Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -1572,12 +1572,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); } @@ -1696,16 +1694,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. @@ -1726,8 +1722,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( @@ -1737,6 +1733,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, @@ -1808,14 +1805,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); @@ -1823,9 +1819,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( @@ -1941,12 +1936,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: 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 -emit-llvm -debug-info-kind=limited %s -o - | FileCheck --check-prefix=CHECK-STABLE %s +// RUN: %clang_cc1 -funstable-c++-abi-classes -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 -triple x86_64-unknown-linux-gnu -funstable-c++-abi-classes -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-ITANIUM %s +// RUN: %clang_cc1 %s -triple x86_64-unknown-windows-msvc -funstable-c++-abi-classes -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-MS %s + +// CHECK-ITANIUM: @_ZTV1S = 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 = 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 = 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 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 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 void @_Z5call1P1S +// CHECK-MS: define void @"\01?call1@@YAXPEAUS@@@Z" +void call1(S *s) { + // CHECK: [[VOFFSETPTR:%.*]] = getelementptr inbounds i32, i32* [[VTABLE:%.*]], i64 0 + // CHECK: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETPTR]], align 4 + // CHECK: [[VTABLEI8:%.*]] = bitcast i32* [[VTABLE]] to i8* + // CHECK: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLEI8]], i32 [[VOFFSET]] + // CHECK: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void ( + // CHECK: call void [[VFNCASTED]]( + s->f1(); +} + +// CHECK-ITANIUM: define void @_Z5call2P1S +// CHECK-MS: define void @"\01?call2@@YAXPEAUS@@@Z" +void call2(S *s) { + // CHECK: [[VOFFSETPTR:%.*]] = getelementptr inbounds i32, i32* [[VTABLE:%.*]], i64 1 + // CHECK: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETPTR]], align 4 + // CHECK: [[VTABLEI8:%.*]] = bitcast i32* [[VTABLE]] to i8* + // CHECK: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLEI8]], i32 [[VOFFSET]] + // 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* @"\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: [[VOFFSETPTR:%.*]] = getelementptr inbounds i32, i32* [[VTABLE:%.*]], i64 0 +// CHECK-MS: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETPTR]], align 4 +// CHECK-MS: [[VTABLEI8:%.*]] = bitcast i32* [[VTABLE]] to i8* +// CHECK-MS: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLEI8]], i32 [[VOFFSET]] +// CHECK-MS: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void ( +// CHECK-MS: musttail call void {{.*}} [[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* @"\01?getmfp2@@YAP8S@@EAAXXZXZ"() +// CHECK-MS: ret i8* bitcast {{.*}} @"\01??_9S@@$B7AA" +// CHECK-MS: define linkonce_odr void @"\01??_9S@@$B7AA" +// CHECK-MS: [[VOFFSETPTR:%.*]] = getelementptr inbounds i32, i32* [[VTABLE:%.*]], i64 1 +// CHECK-MS: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETPTR]], align 4 +// CHECK-MS: [[VTABLEI8:%.*]] = bitcast i32* [[VTABLE]] to i8* +// CHECK-MS: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLEI8]], i32 [[VOFFSET]] +// CHECK-MS: [[VFNCASTED:%.*]] = bitcast i8* [[VFN]] to void ( +// CHECK-MS: musttail call void {{.*}} [[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: [[VOFFSETPTR:%.*]] = getelementptr i8, i8* [[VTABLE:%.*]], i64 [[VTOFFSET]] + // CHECK-ITANIUM: [[VOFFSETI32PTR:%.*]] = bitcast i8* [[VOFFSETPTR]] to i32* + // CHECK-ITANIUM: [[VOFFSET:%.*]] = load i32, i32* [[VOFFSETI32PTR]], align 4 + // CHECK-ITANIUM: [[VFN:%.*]] = getelementptr i8, i8* [[VTABLE]], i32 [[VOFFSET]] + // CHECK-ITANIUM: bitcast i8* [[VFN]] to void ( + (s->*p)(); +}