diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -186,6 +186,29 @@ /// Regexes separated by a semi-colon to filter the files to not instrument. std::string ProfileExcludeFiles; + /// Regular expression with it's original pattern string + struct RegexWithPattern { + std::string Pattern; + std::shared_ptr Regex; + + RegexWithPattern() = default; + RegexWithPattern(std::string Pattern, std::shared_ptr Regex) + : Pattern(Pattern), Regex(Regex) {} + + /// Returns true iff the optimization remark holds a valid regular + /// expression. + bool hasValidPattern() const { return Regex != nullptr; } + + /// Matches the given string against the regex, if there is some. + bool patternMatches(StringRef String) const { + return hasValidPattern() && Regex->match(String); + } + }; + + /// Skip types defined in these paths when performing whole-program vtable + /// optimization. + RegexWithPattern SkipVtableFilepaths; + /// The version string to put into coverage files. char CoverageVersion[4]; diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -374,6 +374,8 @@ CODEGENOPT(VirtualFunctionElimination, 1, 0) ///< Whether to apply the dead /// virtual function elimination /// optimization. +CODEGENOPT(EmitVtableTypePaths, 1, 0) ///< Whether to emit filepath of class declarations + /// in vtable type metadata. /// Whether to use public LTO visibility for entities in std and stdext /// namespaces. This is enabled by clang-cl's /MT and /MTd flags. 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 @@ -3157,6 +3157,8 @@ CodeGenOpts<"WholeProgramVTables">, DefaultFalse, PosFlag, NegFlag, BothFlags<[CoreOption]>>; +def fskip_vtable_file_paths_EQ : Joined<["-"], "fskip-vtable-filepaths=">, Group, + HelpText<"Skip types from participating in whole-program vtable optimization defined under these filepaths">, Flags<[CC1Option]>; defm split_lto_unit : BoolFOption<"split-lto-unit", CodeGenOpts<"EnableSplitLTOUnit">, DefaultFalse, PosFlag, diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7067,7 +7067,20 @@ const CXXRecordDecl *RD) { llvm::Metadata *MD = CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0)); - VTable->addTypeMetadata(Offset.getQuantity(), MD); + + bool EmitForType = true; + if (CodeGenOpts.SkipVtableFilepaths.hasValidPattern()) { + const PresumedLoc &PLoc = + Context.getFullLoc(RD->getBeginLoc()).getPresumedLoc(true); + assert(PLoc.isValid() && "Source location is expected to be always valid."); + std::string FilePath = PLoc.getFilename(); + + if (CodeGenOpts.SkipVtableFilepaths.patternMatches(FilePath)) + EmitForType = false; + } + + if (EmitForType) + VTable->addTypeMetadata(Offset.getQuantity(), MD); if (CodeGenOpts.SanitizeCfiCrossDso) if (auto CrossDsoTypeId = CreateCrossDsoCfiTypeId(MD)) 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 @@ -7245,6 +7245,16 @@ if (SplitLTOUnit) CmdArgs.push_back("-fsplit-lto-unit"); + for (const Arg *A : Args.filtered(options::OPT_fskip_vtable_file_paths_EQ)) { + if (!WholeProgramVTables) + D.Diag(diag::err_drv_argument_only_allowed_with) + << "-fskip-vtable-filepaths" + << "-fwhole-program-vtables"; + StringRef Path = A->getValue(); + CmdArgs.push_back(Args.MakeArgString("-fskip-vtable-filepaths=" + Path)); + A->claim(); + } + if (Arg *A = Args.getLastArg(options::OPT_fglobal_isel, options::OPT_fno_global_isel)) { CmdArgs.push_back("-mllvm"); 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 @@ -1578,6 +1578,10 @@ GenerateOptimizationRemark(Args, SA, OPT_Rpass_analysis_EQ, "pass-analysis", Opts.OptimizationRemarkAnalysis); + if (Opts.SkipVtableFilepaths.hasValidPattern()) + GenerateArg(Args, OPT_fskip_vtable_file_paths_EQ, + Opts.SkipVtableFilepaths.Pattern, SA); + GenerateArg(Args, OPT_fdiagnostics_hotness_threshold_EQ, Opts.DiagnosticsHotnessThreshold ? Twine(*Opts.DiagnosticsHotnessThreshold) @@ -1989,6 +1993,19 @@ Opts.OptimizationRemarkMissed.hasValidPattern() || Opts.OptimizationRemarkAnalysis.hasValidPattern(); + if (Arg *A = Args.getLastArg(OPT_fskip_vtable_file_paths_EQ)) { + StringRef Val = A->getValue(); + std::string RegexError; + std::shared_ptr Pattern = std::make_shared(Val); + if (!Pattern->isValid(RegexError)) { + Diags.Report(diag::err_drv_optimization_remark_pattern) + << RegexError << A->getAsString(Args); + Pattern.reset(); + } + Opts.SkipVtableFilepaths = + CodeGenOptions::RegexWithPattern(std::string(Val), Pattern); + } + bool UsingSampleProfile = !Opts.SampleProfileFile.empty(); bool UsingProfile = UsingSampleProfile || !Opts.ProfileInstrumentUsePath.empty(); diff --git a/clang/test/CodeGenCXX/type-metadata-skip-vtable-filepaths.cpp b/clang/test/CodeGenCXX/type-metadata-skip-vtable-filepaths.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/type-metadata-skip-vtable-filepaths.cpp @@ -0,0 +1,20 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t + +// RUN: %clang_cc1 -flto=thin -flto-unit -fwhole-program-vtables -triple x86_64-unknown-linux -fvisibility=hidden -emit-llvm -o - a.cpp | FileCheck %s +// RUN: %clang_cc1 -flto=thin -flto-unit -fwhole-program-vtables -triple x86_64-unknown-linux -fskip-vtable-filepaths=[^p]$ -fvisibility=hidden -emit-llvm -o - a.cpp | FileCheck %s +// RUN: %clang_cc1 -flto=thin -flto-unit -fwhole-program-vtables -triple x86_64-unknown-linux -fskip-vtable-filepaths=p$ -fvisibility=hidden -emit-llvm -o - a.cpp | FileCheck -check-prefix=SKIP-PATH %s + +// CHECK: !{i64 16, !"_ZTS1A"} +// SKIP-PATH-NOT: !{i64 16, !"_ZTS1A"} +//--- a.cpp + +struct A { + virtual int f() { return 1; } +}; + +int f() { + auto a = new A(); + return a->f(); +} diff --git a/clang/test/Driver/fskip-vtable-filepaths.c b/clang/test/Driver/fskip-vtable-filepaths.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/fskip-vtable-filepaths.c @@ -0,0 +1,7 @@ +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin 2>&1 | FileCheck --check-prefix=NOSKIP %s +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fwhole-program-vtables -fskip-vtable-filepaths=abc 2>&1 | FileCheck --check-prefix=SKIP %s +// RUN: %clang -target x86_64-unknown-linux -### %s -flto=thin -fskip-vtable-filepaths=abc 2>&1 | FileCheck --check-prefix=ERROR1 %s + +// SKIP: "-fskip-vtable-filepaths=abc" +// NOSKIP-NOT: "-fskip-vtable-filepaths=abc" +// ERROR1: error: invalid argument '-fskip-vtable-filepaths' only allowed with '-fwhole-program-vtables'