Index: cfe/trunk/docs/UsersManual.rst =================================================================== --- cfe/trunk/docs/UsersManual.rst +++ cfe/trunk/docs/UsersManual.rst @@ -1053,6 +1053,21 @@ the behavior of sanitizers in the ``cfi`` group to allow checking of cross-DSO virtual and indirect calls. +.. option:: -fwhole-program-vtables + + Enable whole-program vtable optimizations, such as single-implementation + devirtualization and virtual constant propagation. Requires ``-flto``. + + By default, the compiler will assume that all type hierarchies are + closed except those in the ``std`` namespace, the ``stdext`` namespace + and classes with the ``__declspec(uuid())`` attribute. + +.. option:: -fwhole-program-vtables-blacklist=path + + Allows the user to specify the path to a list of additional classes to + blacklist from whole-program vtable optimizations. This list is in the + :ref:`CFI blacklist ` format. + .. option:: -fno-assume-sane-operator-new Don't assume that the C++'s new operator is sane. Index: cfe/trunk/include/clang/Driver/Options.td =================================================================== --- cfe/trunk/include/clang/Driver/Options.td +++ cfe/trunk/include/clang/Driver/Options.td @@ -1124,6 +1124,13 @@ def fvisibility_ms_compat : Flag<["-"], "fvisibility-ms-compat">, Group, HelpText<"Give global types 'default' visibility and global functions and " "variables 'hidden' visibility by default">; +def fwhole_program_vtables : Flag<["-"], "fwhole-program-vtables">, Group, + Flags<[CC1Option]>, + HelpText<"Enables whole-program vtable optimization. Requires -flto">; +def fno_whole_program_vtables : Flag<["-"], "fno-whole-program-vtables">, Group; +def fwhole_program_vtables_blacklist_EQ : Joined<["-"], "fwhole-program-vtables-blacklist=">, + Group, Flags<[CC1Option]>, + HelpText<"Path to a blacklist file for whole-program vtable optimization">; def fwrapv : Flag<["-"], "fwrapv">, Group, Flags<[CC1Option]>, HelpText<"Treat signed integer overflow as two's complement">; def fwritable_strings : Flag<["-"], "fwritable-strings">, Group, Flags<[CC1Option]>, Index: cfe/trunk/include/clang/Frontend/CodeGenOptions.h =================================================================== --- cfe/trunk/include/clang/Frontend/CodeGenOptions.h +++ cfe/trunk/include/clang/Frontend/CodeGenOptions.h @@ -201,6 +201,9 @@ /// \brief A list of all -fno-builtin-* function names (e.g., memset). std::vector NoBuiltinFuncs; + /// List of blacklist files for the whole-program vtable optimization feature. + std::vector WholeProgramVTablesBlacklistFiles; + public: // Define accessors/mutators for code generation options of enumeration type. #define CODEGENOPT(Name, Bits, Default) Index: cfe/trunk/include/clang/Frontend/CodeGenOptions.def =================================================================== --- cfe/trunk/include/clang/Frontend/CodeGenOptions.def +++ cfe/trunk/include/clang/Frontend/CodeGenOptions.def @@ -179,6 +179,9 @@ CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists. +CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program + /// vtable optimization. + /// The user specified number of registers to be used for integral arguments, /// or 0 if unspecified. VALUE_CODEGENOPT(NumRegisterParameters, 32, 0) Index: cfe/trunk/lib/CodeGen/CGClass.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGClass.cpp +++ cfe/trunk/lib/CodeGen/CGClass.cpp @@ -2485,15 +2485,35 @@ RD->bases_begin()->getType()->getAsCXXRecordDecl()); } -void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, +void CodeGenFunction::EmitBitSetCodeForVCall(const CXXRecordDecl *RD, + llvm::Value *VTable, + SourceLocation Loc) { + if (CGM.getCodeGenOpts().WholeProgramVTables && + !CGM.IsBitSetBlacklistedRecord(RD)) { + llvm::Metadata *MD = + CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0)); + llvm::Value *BitSetName = + llvm::MetadataAsValue::get(CGM.getLLVMContext(), MD); + + llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy); + llvm::Value *BitSetTest = + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test), + {CastedVTable, BitSetName}); + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::assume), BitSetTest); + } + + if (SanOpts.has(SanitizerKind::CFIVCall)) + EmitVTablePtrCheckForCall(RD, VTable, CodeGenFunction::CFITCK_VCall, Loc); +} + +void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXRecordDecl *RD, llvm::Value *VTable, CFITypeCheckKind TCK, SourceLocation Loc) { - const CXXRecordDecl *ClassDecl = MD->getParent(); if (!SanOpts.has(SanitizerKind::CFICastStrict)) - ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl); + RD = LeastDerivedClassWithSameLayout(RD); - EmitVTablePtrCheck(ClassDecl, VTable, TCK, Loc); + EmitVTablePtrCheck(RD, VTable, TCK, Loc); } void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T, @@ -2545,7 +2565,7 @@ llvm::Value *VTable, CFITypeCheckKind TCK, SourceLocation Loc) { - if (CGM.IsCFIBlacklistedRecord(RD)) + if (CGM.IsBitSetBlacklistedRecord(RD)) return; SanitizerScope SanScope(this); Index: cfe/trunk/lib/CodeGen/CGExprCXX.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGExprCXX.cpp +++ cfe/trunk/lib/CodeGen/CGExprCXX.cpp @@ -259,7 +259,8 @@ if (SanOpts.has(SanitizerKind::CFINVCall) && MD->getParent()->isDynamicClass()) { llvm::Value *VTable = GetVTablePtr(This, Int8PtrTy, MD->getParent()); - EmitVTablePtrCheckForCall(MD, VTable, CFITCK_NVCall, CE->getLocStart()); + EmitVTablePtrCheckForCall(MD->getParent(), VTable, CFITCK_NVCall, + CE->getLocStart()); } if (getLangOpts().AppleKext && MD->isVirtual() && HasQualifier) Index: cfe/trunk/lib/CodeGen/CGVTables.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGVTables.cpp +++ cfe/trunk/lib/CodeGen/CGVTables.cpp @@ -898,21 +898,34 @@ DeferredVTables.clear(); } -bool CodeGenModule::IsCFIBlacklistedRecord(const CXXRecordDecl *RD) { - if (RD->hasAttr() && - getContext().getSanitizerBlacklist().isBlacklistedType("attr:uuid")) - return true; +bool CodeGenModule::NeedVTableBitSets() { + return getCodeGenOpts().WholeProgramVTables || + getLangOpts().Sanitize.has(SanitizerKind::CFIVCall) || + getLangOpts().Sanitize.has(SanitizerKind::CFINVCall) || + getLangOpts().Sanitize.has(SanitizerKind::CFIDerivedCast) || + getLangOpts().Sanitize.has(SanitizerKind::CFIUnrelatedCast); +} + +bool CodeGenModule::IsBitSetBlacklistedRecord(const CXXRecordDecl *RD) { + std::string TypeName = RD->getQualifiedNameAsString(); + auto isInBlacklist = [&](const SanitizerBlacklist &BL) { + if (RD->hasAttr() && BL.isBlacklistedType("attr:uuid")) + return true; + + return BL.isBlacklistedType(TypeName); + }; - return getContext().getSanitizerBlacklist().isBlacklistedType( - RD->getQualifiedNameAsString()); + return isInBlacklist(WholeProgramVTablesBlacklist) || + ((LangOpts.Sanitize.has(SanitizerKind::CFIVCall) || + LangOpts.Sanitize.has(SanitizerKind::CFINVCall) || + LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) || + LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast)) && + isInBlacklist(getContext().getSanitizerBlacklist())); } void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable, const VTableLayout &VTLayout) { - if (!LangOpts.Sanitize.has(SanitizerKind::CFIVCall) && - !LangOpts.Sanitize.has(SanitizerKind::CFINVCall) && - !LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) && - !LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast)) + if (!NeedVTableBitSets()) return; CharUnits PointerWidth = @@ -922,7 +935,7 @@ std::vector BitsetEntries; // Create a bit set entry for each address point. for (auto &&AP : VTLayout.getAddressPoints()) { - if (IsCFIBlacklistedRecord(AP.first.getBase())) + if (IsBitSetBlacklistedRecord(AP.first.getBase())) continue; BitsetEntries.push_back(std::make_pair(AP.first.getBase(), AP.second)); Index: cfe/trunk/lib/CodeGen/CodeGenFunction.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.h +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h @@ -1401,7 +1401,7 @@ /// EmitVTablePtrCheckForCall - Virtual method MD is being called via VTable. /// If vptr CFI is enabled, emit a check that VTable is valid. - void EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, llvm::Value *VTable, + void EmitVTablePtrCheckForCall(const CXXRecordDecl *RD, llvm::Value *VTable, CFITypeCheckKind TCK, SourceLocation Loc); /// EmitVTablePtrCheck - Emit a check that VTable is a valid virtual table for @@ -1409,6 +1409,12 @@ void EmitVTablePtrCheck(const CXXRecordDecl *RD, llvm::Value *VTable, CFITypeCheckKind TCK, SourceLocation Loc); + /// If whole-program virtual table optimization is enabled, emit an assumption + /// that VTable is a member of the type's bitset. Or, if vptr CFI is enabled, + /// emit a check that VTable is a member of the type's bitset. + void EmitBitSetCodeForVCall(const CXXRecordDecl *RD, llvm::Value *VTable, + SourceLocation Loc); + /// CanDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given /// expr can be devirtualized. bool CanDevirtualizeMemberFunctionCall(const Expr *Base, Index: cfe/trunk/lib/CodeGen/CodeGenModule.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenModule.h +++ cfe/trunk/lib/CodeGen/CodeGenModule.h @@ -489,6 +489,8 @@ /// MDNodes. llvm::DenseMap MetadataIdMap; + SanitizerBlacklist WholeProgramVTablesBlacklist; + public: CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts, const PreprocessorOptions &ppopts, @@ -1108,9 +1110,12 @@ /// \param D Threadprivate declaration. void EmitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D); - /// Returns whether the given record is blacklisted from control flow - /// integrity checks. - bool IsCFIBlacklistedRecord(const CXXRecordDecl *RD); + /// Returns whether we need bit sets attached to vtables. + bool NeedVTableBitSets(); + + /// Returns whether the given record is blacklisted from whole-program + /// transformations (i.e. CFI or whole-program vtable optimization). + bool IsBitSetBlacklistedRecord(const CXXRecordDecl *RD); /// Emit bit set entries for the given vtable using the given layout if /// vptr CFI is enabled. Index: cfe/trunk/lib/CodeGen/CodeGenModule.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenModule.cpp +++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp @@ -97,7 +97,9 @@ NSConcreteStackBlock(nullptr), BlockObjectAssign(nullptr), BlockObjectDispose(nullptr), BlockDescriptorType(nullptr), GenericBlockLiteralType(nullptr), LifetimeStartFn(nullptr), - LifetimeEndFn(nullptr), SanitizerMD(new SanitizerMetadata(*this)) { + LifetimeEndFn(nullptr), SanitizerMD(new SanitizerMetadata(*this)), + WholeProgramVTablesBlacklist(CGO.WholeProgramVTablesBlacklistFiles, + C.getSourceManager()) { // Initialize the type cache. llvm::LLVMContext &LLVMContext = M.getContext(); Index: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp @@ -1602,9 +1602,7 @@ auto *MethodDecl = cast(GD.getDecl()); llvm::Value *VTable = CGF.GetVTablePtr(This, Ty, MethodDecl->getParent()); - if (CGF.SanOpts.has(SanitizerKind::CFIVCall)) - CGF.EmitVTablePtrCheckForCall(MethodDecl, VTable, - CodeGenFunction::CFITCK_VCall, Loc); + CGF.EmitBitSetCodeForVCall(MethodDecl->getParent(), VTable, Loc); uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); llvm::Value *VFuncPtr = Index: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp +++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp @@ -1503,10 +1503,7 @@ void MicrosoftCXXABI::emitVTableBitSetEntries(VPtrInfo *Info, const CXXRecordDecl *RD, llvm::GlobalVariable *VTable) { - if (!getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIVCall) && - !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFINVCall) && - !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIDerivedCast) && - !getContext().getLangOpts().Sanitize.has(SanitizerKind::CFIUnrelatedCast)) + if (!CGM.NeedVTableBitSets()) return; llvm::NamedMDNode *BitsetsMD = @@ -1522,13 +1519,13 @@ : CharUnits::Zero(); if (Info->PathToBaseWithVPtr.empty()) { - if (!CGM.IsCFIBlacklistedRecord(RD)) + if (!CGM.IsBitSetBlacklistedRecord(RD)) CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, RD); return; } // Add a bitset entry for the least derived base belonging to this vftable. - if (!CGM.IsCFIBlacklistedRecord(Info->PathToBaseWithVPtr.back())) + if (!CGM.IsBitSetBlacklistedRecord(Info->PathToBaseWithVPtr.back())) CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, Info->PathToBaseWithVPtr.back()); @@ -1548,12 +1545,12 @@ Offset = VBI->second.VBaseOffset; if (!Offset.isZero()) return; - if (!CGM.IsCFIBlacklistedRecord(DerivedRD)) + if (!CGM.IsBitSetBlacklistedRecord(DerivedRD)) CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, DerivedRD); } // Finally do the same for the most derived class. - if (Info->FullOffsetInMDC.isZero() && !CGM.IsCFIBlacklistedRecord(RD)) + if (Info->FullOffsetInMDC.isZero() && !CGM.IsBitSetBlacklistedRecord(RD)) CGM.CreateVTableBitSetEntry(BitsetsMD, VTable, AddressPoint, RD); } @@ -1822,9 +1819,9 @@ MicrosoftVTableContext::MethodVFTableLocation ML = CGM.getMicrosoftVTableContext().getMethodVFTableLocation(GD); - if (CGF.SanOpts.has(SanitizerKind::CFIVCall)) - CGF.EmitVTablePtrCheck(getClassAtVTableLocation(getContext(), GD, ML), - VTable, CodeGenFunction::CFITCK_VCall, Loc); + if (CGM.NeedVTableBitSets()) + CGF.EmitBitSetCodeForVCall(getClassAtVTableLocation(getContext(), GD, ML), + VTable, Loc); llvm::Value *VFuncPtr = Builder.CreateConstInBoundsGEP1_64(VTable, ML.Index, "vfn"); Index: cfe/trunk/lib/Driver/Tools.cpp =================================================================== --- cfe/trunk/lib/Driver/Tools.cpp +++ cfe/trunk/lib/Driver/Tools.cpp @@ -4270,6 +4270,32 @@ CmdArgs.push_back("-ffunction-sections"); } + if (Args.hasArg(options::OPT_fwhole_program_vtables, + options::OPT_fno_whole_program_vtables, false)) { + if (!D.isUsingLTO()) + D.Diag(diag::err_drv_argument_only_allowed_with) + << "-fwhole-program-vtables" + << "-flto"; + CmdArgs.push_back("-fwhole-program-vtables"); + + clang::SmallString<64> Path(D.ResourceDir); + llvm::sys::path::append(Path, "vtables_blacklist.txt"); + if (llvm::sys::fs::exists(Path)) { + SmallString<64> BlacklistOpt("-fwhole-program-vtables-blacklist="); + BlacklistOpt += Path.str(); + CmdArgs.push_back(Args.MakeArgString(BlacklistOpt)); + } + + for (const Arg *A : + Args.filtered(options::OPT_fwhole_program_vtables_blacklist_EQ)) { + A->claim(); + if (!llvm::sys::fs::exists(A->getValue())) + D.Diag(clang::diag::err_drv_no_such_file) << A->getValue(); + } + + Args.AddAllArgs(CmdArgs, options::OPT_fwhole_program_vtables_blacklist_EQ); + } + if (Args.hasFlag(options::OPT_fdata_sections, options::OPT_fno_data_sections, UseSeparateSections)) { CmdArgs.push_back("-fdata-sections"); Index: cfe/trunk/lib/Frontend/CompilerInvocation.cpp =================================================================== --- cfe/trunk/lib/Frontend/CompilerInvocation.cpp +++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp @@ -441,6 +441,9 @@ Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 0, Diags); Opts.DebugColumnInfo = Args.hasArg(OPT_dwarf_column_info); Opts.EmitCodeView = Args.hasArg(OPT_gcodeview); + Opts.WholeProgramVTables = Args.hasArg(OPT_fwhole_program_vtables); + Opts.WholeProgramVTablesBlacklistFiles = + Args.getAllArgValues(OPT_fwhole_program_vtables_blacklist_EQ); Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); Opts.DebugTypeExtRefs = Args.hasArg(OPT_dwarf_ext_refs); Opts.DebugExplicitImport = Triple.isPS4CPU(); Index: cfe/trunk/runtime/CMakeLists.txt =================================================================== --- cfe/trunk/runtime/CMakeLists.txt +++ cfe/trunk/runtime/CMakeLists.txt @@ -134,3 +134,13 @@ VERBATIM) endif() endif() + +set(src "${CMAKE_CURRENT_SOURCE_DIR}/vtables_blacklist.txt") +set(dst "${LLVM_LIBRARY_OUTPUT_INTDIR}/clang/${CLANG_VERSION}/vtables_blacklist.txt") +add_custom_command(OUTPUT ${dst} + DEPENDS ${src} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst} + COMMENT "Copying vtables blacklist") +add_custom_target(vtables_blacklist DEPENDS ${dst}) +add_dependencies(clang vtables_blacklist) +install(FILES ${src} DESTINATION lib${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION}) Index: cfe/trunk/runtime/vtables_blacklist.txt =================================================================== --- cfe/trunk/runtime/vtables_blacklist.txt +++ cfe/trunk/runtime/vtables_blacklist.txt @@ -0,0 +1,8 @@ +# Standard library types. +type:std::* + +# The stdext namespace contains Microsoft standard library extensions. +type:stdext::* + +# Types with a uuid attribute, i.e. COM types. +type:attr:uuid Index: cfe/trunk/test/CodeGenCXX/bitset-blacklist.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/bitset-blacklist.cpp +++ cfe/trunk/test/CodeGenCXX/bitset-blacklist.cpp @@ -0,0 +1,32 @@ +// RUN: echo "type:attr:uuid" > %t.txt +// RUN: %clang_cc1 -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOUUID %s +// RUN: %clang_cc1 -fms-extensions -fwhole-program-vtables -fwhole-program-vtables-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOUUID %s +// RUN: echo "type:std::*" > %t.txt +// RUN: %clang_cc1 -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s +// RUN: %clang_cc1 -fms-extensions -fwhole-program-vtables -fwhole-program-vtables-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s + +struct __declspec(uuid("00000000-0000-0000-0000-000000000000")) S1 { + virtual void f(); +}; + +namespace std { + +struct S2 { + virtual void f(); +}; + +} + +// CHECK: define{{.*}}s1f +// NOSTD: llvm.bitset.test +// NOUUID-NOT: llvm.bitset.test +void s1f(S1 *s1) { + s1->f(); +} + +// CHECK: define{{.*}}s2f +// NOSTD-NOT: llvm.bitset.test +// NOUUID: llvm.bitset.test +void s2f(std::S2 *s2) { + s2->f(); +} Index: cfe/trunk/test/CodeGenCXX/bitsets.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/bitsets.cpp +++ cfe/trunk/test/CodeGenCXX/bitsets.cpp @@ -0,0 +1,211 @@ +// Tests for the cfi-vcall feature: +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=ITANIUM --check-prefix=ITANIUM-NDIAG --check-prefix=NDIAG %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-ABORT %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s +// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=MS --check-prefix=NDIAG %s + +// Tests for the whole-program-vtables feature: +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM %s +// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS %s + +// MS: @[[VTA:[0-9]*]] {{.*}} comdat($"\01??_7A@@6B@") +// MS: @[[VTB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6B0@@") +// MS: @[[VTAinB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6BA@@@") +// MS: @[[VTAinC:[0-9]*]] {{.*}} comdat($"\01??_7C@@6B@") +// MS: @[[VTBinD:[0-9]*]] {{.*}} comdat($"\01??_7D@?A@@6BB@@@") +// MS: @[[VTAinBinD:[0-9]*]] {{.*}} comdat($"\01??_7D@?A@@6BA@@@") +// MS: @[[VTFA:[0-9]*]] {{.*}} comdat($"\01??_7FA@?1??foo@@YAXXZ@6B@") + +struct A { + A(); + virtual void f(); +}; + +struct B : virtual A { + B(); + virtual void g(); + virtual void h(); +}; + +struct C : virtual A { + C(); +}; + +namespace { + +struct D : B, C { + D(); + virtual void f(); + virtual void h(); +}; + +} + +A::A() {} +B::B() {} +C::C() {} +D::D() {} + +void A::f() { +} + +void B::g() { +} + +void D::f() { +} + +void D::h() { +} + +// DIAG: @[[SRC:.*]] = private unnamed_addr constant [{{.*}} x i8] c"{{.*}}bitsets.cpp\00", align 1 +// 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 [[@LINE+24]], i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]] } + +// ITANIUM: define void @_Z2afP1A +// MS: define void @"\01?af@@YAXPEAUA@@@Z" +void af(A *a) { + // ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A") + // MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@") + // DIAG-NEXT: [[VTVALID0:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT]], metadata !"all-vtables") + // VTABLE-OPT: call void @llvm.assume(i1 [[P]]) + // CFI-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]] + // CFI-NEXT: {{^$}} + + // CFI: [[TRAPBB]] + // NDIAG-NEXT: call void @llvm.trap() + // NDIAG-NEXT: unreachable + // DIAG-NEXT: [[VTINT:%[^ ]*]] = ptrtoint i8* [[VT]] to i64 + // DIAG-NEXT: [[VTVALID:%[^ ]*]] = zext i1 [[VTVALID0]] to i64 + // DIAG-ABORT-NEXT: call void @__ubsan_handle_cfi_check_fail_abort(i8* getelementptr inbounds ({{.*}} @[[BADTYPESTATIC]], i32 0, i32 0), i64 [[VTINT]], i64 [[VTVALID]]) + // DIAG-ABORT-NEXT: unreachable + // DIAG-RECOVER-NEXT: call void @__ubsan_handle_cfi_check_fail(i8* getelementptr inbounds ({{.*}} @[[BADTYPESTATIC]], i32 0, i32 0), i64 [[VTINT]], i64 [[VTVALID]]) + // DIAG-RECOVER-NEXT: br label %[[CONTBB]] + + // CFI: [[CONTBB]] + // CFI: call void % + a->f(); +} + +// ITANIUM: define internal void @_Z3df1PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?df1@@YAXPEAUD@?A@@@Z" +void df1(D *d) { + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]]) + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUA@@") + d->f(); +} + +// ITANIUM: define internal void @_Z3dg1PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?dg1@@YAXPEAUD@?A@@@Z" +void dg1(D *d) { + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUB@@") + d->g(); +} + +// ITANIUM: define internal void @_Z3dh1PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?dh1@@YAXPEAUD@?A@@@Z" +void dh1(D *d) { + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE]]) + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]]) + d->h(); +} + +// ITANIUM: define internal void @_Z3df2PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?df2@@YAXPEAUD@?A@@@Z" +__attribute__((no_sanitize("cfi"))) +void df2(D *d) { + // CFI-NOT: call i1 @llvm.bitset.test + d->f(); +} + +// ITANIUM: define internal void @_Z3df3PN12_GLOBAL__N_11DE +// MS: define internal void @"\01?df3@@YAXPEAUD@?A@@@Z" +__attribute__((no_sanitize("address"))) __attribute__((no_sanitize("cfi-vcall"))) +void df3(D *d) { + // CFI-NOT: call i1 @llvm.bitset.test + d->f(); +} + +D d; + +void foo() { + df1(&d); + dg1(&d); + dh1(&d); + df2(&d); + df3(&d); + + struct FA : A { + void f() {} + } fa; + af(&fa); +} + +namespace test2 { + +struct A { + virtual void m_fn1(); +}; +struct B { + virtual void m_fn2(); +}; +struct C : B, A {}; +struct D : C { + void m_fn1(); +}; + +// ITANIUM: define void @_ZN5test21fEPNS_1DE +// MS: define void @"\01?f@test2@@YAXPEAUD@1@@Z" +void f(D *d) { + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTSN5test21DE") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUA@test2@@") + d->m_fn1(); +} + +} + +// Check for the expected number of elements (15 or 23 respectively). +// MS-NDIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){9}]]} +// MS-DIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){15}]]} +// ITANIUM-NDIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){14}]]} +// ITANIUM-DIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){23}]]} + +// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16} +// ITANIUM-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @_ZTV1A, i64 16} +// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} +// ITANIUM-DAG: !{!"_ZTS1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} +// ITANIUM-DIAG-DAG: !{!"all-vtables", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} +// ITANIUM-DAG: !{!"_ZTS1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} +// ITANIUM-DAG: !{!"_ZTS1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} +// ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} +// ITANIUM-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTV1B, i64 32} +// ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTV1B, i64 32} +// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTV1B, i64 32} +// ITANIUM-DAG: !{!"_ZTS1A", [5 x i8*]* @_ZTV1C, i64 32} +// ITANIUM-DAG: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32} +// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} +// ITANIUM-DAG: !{!{{[0-9]+}}, [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} + +// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTA]], i64 8} +// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTA]], i64 8} +// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8} +// MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTB]], i64 8} +// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8} +// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinB]], i64 8} +// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8} +// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinC]], i64 8} +// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTBinD]], i64 8} +// MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTBinD]], i64 8} +// MS-DAG: !{![[DTYPE]], [3 x i8*]* @[[VTBinD]], i64 8} +// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinBinD]], i64 8} +// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinBinD]], i64 8} +// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTFA]], i64 8} +// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTFA]], i64 8} +// MS-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8} Index: cfe/trunk/test/CodeGenCXX/cfi-blacklist.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/cfi-blacklist.cpp +++ cfe/trunk/test/CodeGenCXX/cfi-blacklist.cpp @@ -1,30 +0,0 @@ -// RUN: echo "type:attr:uuid" > %t.txt -// RUN: %clang_cc1 -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOUUID %s -// RUN: echo "type:std::*" > %t.txt -// RUN: %clang_cc1 -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s - -struct __declspec(uuid("00000000-0000-0000-0000-000000000000")) S1 { - virtual void f(); -}; - -namespace std { - -struct S2 { - virtual void f(); -}; - -} - -// CHECK: define{{.*}}s1f -// NOSTD: llvm.bitset.test -// NOUUID-NOT: llvm.bitset.test -void s1f(S1 *s1) { - s1->f(); -} - -// CHECK: define{{.*}}s2f -// NOSTD-NOT: llvm.bitset.test -// NOUUID: llvm.bitset.test -void s2f(std::S2 *s2) { - s2->f(); -} Index: cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp +++ cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp @@ -1,205 +0,0 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=ITANIUM-NDIAG --check-prefix=NDIAG %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-ABORT %s -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s -// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=MS --check-prefix=NDIAG %s - -// MS: @[[VTA:[0-9]*]] {{.*}} comdat($"\01??_7A@@6B@") -// MS: @[[VTB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6B0@@") -// MS: @[[VTAinB:[0-9]*]] {{.*}} comdat($"\01??_7B@@6BA@@@") -// MS: @[[VTAinC:[0-9]*]] {{.*}} comdat($"\01??_7C@@6B@") -// MS: @[[VTBinD:[0-9]*]] {{.*}} comdat($"\01??_7D@?A@@6BB@@@") -// MS: @[[VTAinBinD:[0-9]*]] {{.*}} comdat($"\01??_7D@?A@@6BA@@@") -// MS: @[[VTFA:[0-9]*]] {{.*}} comdat($"\01??_7FA@?1??foo@@YAXXZ@6B@") - -struct A { - A(); - virtual void f(); -}; - -struct B : virtual A { - B(); - virtual void g(); - virtual void h(); -}; - -struct C : virtual A { - C(); -}; - -namespace { - -struct D : B, C { - D(); - virtual void f(); - virtual void h(); -}; - -} - -A::A() {} -B::B() {} -C::C() {} -D::D() {} - -void A::f() { -} - -void B::g() { -} - -void D::f() { -} - -void D::h() { -} - -// DIAG: @[[SRC:.*]] = private unnamed_addr constant [{{.*}} x i8] c"{{.*}}cfi-vcall.cpp\00", align 1 -// 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 [[@LINE+23]], i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]] } - -// ITANIUM: define void @_Z2afP1A -// MS: define void @"\01?af@@YAXPEAUA@@@Z" -void af(A *a) { - // ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A") - // MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@") - // DIAG-NEXT: [[VTVALID0:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT]], metadata !"all-vtables") - // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]] - // CHECK-NEXT: {{^$}} - - // CHECK: [[TRAPBB]] - // NDIAG-NEXT: call void @llvm.trap() - // NDIAG-NEXT: unreachable - // DIAG-NEXT: [[VTINT:%[^ ]*]] = ptrtoint i8* [[VT]] to i64 - // DIAG-NEXT: [[VTVALID:%[^ ]*]] = zext i1 [[VTVALID0]] to i64 - // DIAG-ABORT-NEXT: call void @__ubsan_handle_cfi_check_fail_abort(i8* getelementptr inbounds ({{.*}} @[[BADTYPESTATIC]], i32 0, i32 0), i64 [[VTINT]], i64 [[VTVALID]]) - // DIAG-ABORT-NEXT: unreachable - // DIAG-RECOVER-NEXT: call void @__ubsan_handle_cfi_check_fail(i8* getelementptr inbounds ({{.*}} @[[BADTYPESTATIC]], i32 0, i32 0), i64 [[VTINT]], i64 [[VTVALID]]) - // DIAG-RECOVER-NEXT: br label %[[CONTBB]] - - // CHECK: [[CONTBB]] - // CHECK: call void % - a->f(); -} - -// ITANIUM: define internal void @_Z3df1PN12_GLOBAL__N_11DE -// MS: define internal void @"\01?df1@@YAXPEAUD@?A@@@Z" -void df1(D *d) { - // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]]) - // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUA@@") - d->f(); -} - -// ITANIUM: define internal void @_Z3dg1PN12_GLOBAL__N_11DE -// MS: define internal void @"\01?dg1@@YAXPEAUD@?A@@@Z" -void dg1(D *d) { - // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") - // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUB@@") - d->g(); -} - -// ITANIUM: define internal void @_Z3dh1PN12_GLOBAL__N_11DE -// MS: define internal void @"\01?dh1@@YAXPEAUD@?A@@@Z" -void dh1(D *d) { - // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE]]) - // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]]) - d->h(); -} - -// ITANIUM: define internal void @_Z3df2PN12_GLOBAL__N_11DE -// MS: define internal void @"\01?df2@@YAXPEAUD@?A@@@Z" -__attribute__((no_sanitize("cfi"))) -void df2(D *d) { - // CHECK-NOT: call i1 @llvm.bitset.test - d->f(); -} - -// ITANIUM: define internal void @_Z3df3PN12_GLOBAL__N_11DE -// MS: define internal void @"\01?df3@@YAXPEAUD@?A@@@Z" -__attribute__((no_sanitize("address"))) __attribute__((no_sanitize("cfi-vcall"))) -void df3(D *d) { - // CHECK-NOT: call i1 @llvm.bitset.test - d->f(); -} - -D d; - -void foo() { - df1(&d); - dg1(&d); - dh1(&d); - df2(&d); - df3(&d); - - struct FA : A { - void f() {} - } fa; - af(&fa); -} - -namespace test2 { - -struct A { - virtual void m_fn1(); -}; -struct B { - virtual void m_fn2(); -}; -struct C : B, A {}; -struct D : C { - void m_fn1(); -}; - -// ITANIUM: define void @_ZN5test21fEPNS_1DE -// MS: define void @"\01?f@test2@@YAXPEAUD@1@@Z" -void f(D *d) { - // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTSN5test21DE") - // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUA@test2@@") - d->m_fn1(); -} - -} - -// Check for the expected number of elements (15 or 23 respectively). -// MS-NDIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){9}]]} -// MS-DIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){15}]]} -// ITANIUM-NDIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){14}]]} -// ITANIUM-DIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){23}]]} - -// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16} -// ITANIUM-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @_ZTV1A, i64 16} -// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} -// ITANIUM-DIAG-DAG: !{!"all-vtables", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} -// ITANIUM-DAG: !{!"_ZTS1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DAG: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} -// ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} -// ITANIUM-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTV1B, i64 32} -// ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTV1B, i64 32} -// ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTV1B, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [5 x i8*]* @_ZTV1C, i64 32} -// ITANIUM-DAG: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32} -// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} -// ITANIUM-DAG: !{!{{[0-9]+}}, [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} - -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTA]], i64 8} -// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTA]], i64 8} -// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8} -// MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTB]], i64 8} -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8} -// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinB]], i64 8} -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8} -// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinC]], i64 8} -// MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTBinD]], i64 8} -// MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTBinD]], i64 8} -// MS-DAG: !{![[DTYPE]], [3 x i8*]* @[[VTBinD]], i64 8} -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinBinD]], i64 8} -// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinBinD]], i64 8} -// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTFA]], i64 8} -// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTFA]], i64 8} -// MS-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8} Index: cfe/trunk/test/Driver/whole-program-vtables.c =================================================================== --- cfe/trunk/test/Driver/whole-program-vtables.c +++ cfe/trunk/test/Driver/whole-program-vtables.c @@ -0,0 +1,11 @@ +// RUN: %clang -target x86_64-unknown-linux -fwhole-program-vtables -### %s 2>&1 | FileCheck --check-prefix=NO-LTO %s +// NO-LTO: invalid argument '-fwhole-program-vtables' only allowed with '-flto' + +// RUN: %clang -target x86_64-unknown-linux -resource-dir=%S/Inputs/resource_dir -flto -fwhole-program-vtables -### -c %s 2>&1 | FileCheck --check-prefix=BLACKLIST %s +// BLACKLIST: "-fwhole-program-vtables-blacklist={{.*}}vtables_blacklist.txt" + +// RUN: %clang -target x86_64-unknown-linux -fwhole-program-vtables-blacklist=nonexistent.txt -flto -fwhole-program-vtables -### -c %s 2>&1 | FileCheck --check-prefix=NON-EXISTENT-BLACKLIST %s +// NON-EXISTENT-BLACKLIST: no such file or directory: 'nonexistent.txt' + +// RUN: %clang -target x86_64-unknown-linux -fwhole-program-vtables-blacklist=%S/Inputs/resource_dir/vtables_blacklist.txt -flto -fwhole-program-vtables -### -c %s 2>&1 | FileCheck --check-prefix=CUSTOM-BLACKLIST %s +// CUSTOM-BLACKLIST: "-fwhole-program-vtables-blacklist={{.*}}Inputs/resource_dir/vtables_blacklist.txt"