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 @@ -337,6 +337,21 @@ const char *Argv0 = nullptr; ArrayRef CommandLineArgs; + /// The minimum hotness value a diagnostic needs in order to be included in + /// optimization diagnostics. + /// + /// The threshold is an Optional value, which maps to one of the 3 states: + /// 1. 0 => threshold disabled. All emarks will be printed. + /// 2. positive int => manual threshold by user. Remarks with hotness exceed + /// threshold will be printed. + /// 3. None => 'auto' threshold by user. The actual value is not + /// available at command line, but will be synced with + /// hotness threhold from profile summary during + /// compilation. + /// + /// If threshold option is not specified, it is disabled by default. + Optional DiagnosticsHotnessThreshold = 0; + public: // Define accessors/mutators for code generation options of enumeration type. #define CODEGENOPT(Name, Bits, Default) 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 @@ -352,10 +352,6 @@ /// Whether to report the hotness of the code region for optimization remarks. CODEGENOPT(DiagnosticsWithHotness, 1, 0) -/// The minimum hotness value a diagnostic needs in order to be included in -/// optimization diagnostics. -VALUE_CODEGENOPT(DiagnosticsHotnessThreshold, 32, 0) - /// Whether copy relocations support is available when building as PIE. CODEGENOPT(PIECopyRelocations, 1, 0) diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -108,6 +108,8 @@ "unable to execute command: %0">; def err_drv_invalid_darwin_version : Error< "invalid Darwin version number: %0">; +def err_drv_invalid_diagnotics_hotness_threshold : Error< + "invalid argument in '%0', only integer or 'auto' is supported">; def err_drv_missing_argument : Error< "argument to '%0' is missing (expected %1 value%s1)">; def err_drv_invalid_Xarch_argument_with_args : Error< diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -632,6 +632,11 @@ /// \return True if the argument combination will end up generating remarks. bool willEmitRemarks(const llvm::opt::ArgList &Args); +/// \return True if the argument combination will end up outputing remarks +/// onto screen. +/// This checks for clang specific R-value ('-Rpass-*') group. +bool hasRpassOptions(const llvm::opt::ArgList &Args); + } // end namespace driver } // end namespace clang 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 @@ -908,8 +908,9 @@ def fdiagnostics_show_hotness : Flag<["-"], "fdiagnostics-show-hotness">, Group, Flags<[CC1Option]>, HelpText<"Enable profile hotness information in diagnostic line">; def fdiagnostics_hotness_threshold_EQ : Joined<["-"], "fdiagnostics-hotness-threshold=">, - Group, Flags<[CC1Option]>, MetaVarName<"">, - HelpText<"Prevent optimization remarks from being output if they do not have at least this profile count">; + Group, Flags<[CC1Option]>, MetaVarName<"">, + HelpText<"Prevent optimization remarks from being output if they do not have at least this profile count. " + "Use 'auto' to apply the threshold from profile summary">; def fdiagnostics_show_option : Flag<["-"], "fdiagnostics-show-option">, Group, HelpText<"Print option name with mappable diagnostics">; def fdiagnostics_show_note_include_stack : Flag<["-"], "fdiagnostics-show-note-include-stack">, diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -5204,3 +5204,19 @@ return true; return false; } + +bool clang::driver::hasRpassOptions(const ArgList &Args) { + // -Rpass= enables it. + if (Args.getLastArg(options::OPT_Rpass_EQ)) + return true; + + // -Rpass-missed= alone enables it too. + if (Args.getLastArg(options::OPT_Rpass_missed_EQ)) + return true; + + // -Rpass-analysis= alone enables it too. + if (Args.getLastArg(options::OPT_Rpass_analysis_EQ)) + return true; + + return false; +} diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -60,6 +60,95 @@ using namespace clang; using namespace llvm::opt; +// Remarks option pass-through only happens when +// 1). single arch target +// 2). linker is lld +static bool checkRemarksOptions(StringRef LinkerPath, const ArgList &Args, + const llvm::Triple &Triple) { + bool hasMultipleArchs = + Triple.isOSDarwin() && Args.getAllArgValues(options::OPT_arch).size() > 1; + + bool isLLD = llvm::sys::path::filename(LinkerPath) == "ld.lld" || + llvm::sys::path::stem(LinkerPath) != "ld.lld"; + if (hasMultipleArchs || !isLLD) + return false; + return true; +} + +static void renderRpassOptions(const ArgList &Args, ArgStringList &CmdArgs) { + if (const Arg *A = Args.getLastArg(options::OPT_Rpass_EQ)) { + CmdArgs.push_back("-mllvm"); + std::string Passes = std::string("-pass-remarks=") + A->getValue(); + CmdArgs.push_back(Args.MakeArgString(Passes)); + } + + if (const Arg *A = Args.getLastArg(options::OPT_Rpass_missed_EQ)) { + CmdArgs.push_back("-mllvm"); + std::string Passes = std::string("-pass-remarks-missed=") + A->getValue(); + CmdArgs.push_back(Args.MakeArgString(Passes)); + } + + if (const Arg *A = Args.getLastArg(options::OPT_Rpass_analysis_EQ)) { + CmdArgs.push_back("-mllvm"); + std::string Passes = std::string("-pass-remarks-analysis=") + A->getValue(); + CmdArgs.push_back(Args.MakeArgString(Passes)); + } +} + +static void renderRemarksOptions(const ArgList &Args, ArgStringList &CmdArgs, + const llvm::Triple &Triple, + const InputInfo &Input, + const InputInfo &Output) { + StringRef Format = "yaml"; + if (const Arg *A = Args.getLastArg(options::OPT_fsave_optimization_record_EQ)) + Format = A->getValue(); + + CmdArgs.push_back("--opt-remarks-filename"); + + SmallString<128> F; + const Arg *A = Args.getLastArg(options::OPT_foptimization_record_file_EQ); + if (A) { + F = A->getValue(); + } else { + if (Output.isFilename()) + F = Output.getFilename(); + + if (F.empty()) { + // Use the input filename. + F = llvm::sys::path::stem(Input.getBaseInput()); + } + } + // Append "opt.ld." to the end of the file name. + SmallString<32> Extension; + Extension += ".opt.ld."; + Extension += Format; + + CmdArgs.push_back(Args.MakeArgString(F + Extension)); + + if (const Arg *A = + Args.getLastArg(options::OPT_foptimization_record_passes_EQ)) { + CmdArgs.push_back("--opt-remarks-passes"); + CmdArgs.push_back(A->getValue()); + } + + CmdArgs.push_back("--opt-remarks-format"); + CmdArgs.push_back(Format.data()); +} + +static void renderRemarksHotnessOptions(const ArgList &Args, + ArgStringList &CmdArgs) { + if (Args.hasFlag(options::OPT_fdiagnostics_show_hotness, + options::OPT_fno_diagnostics_show_hotness, false)) + CmdArgs.push_back("--opt-remarks-with-hotness"); + + if (const Arg *A = + Args.getLastArg(options::OPT_fdiagnostics_hotness_threshold_EQ)) { + std::string Opt = + std::string("--opt-remarks-hotness-threshold=") + A->getValue(); + CmdArgs.push_back(Args.MakeArgString(Opt)); + } +} + void tools::addPathIfExists(const Driver &D, const Twine &Path, ToolChain::path_list &Paths) { if (D.getVFS().exists(Path)) @@ -531,6 +620,23 @@ Args.MakeArgString(Twine("-plugin-opt=stats-file=") + StatsFile)); addX86AlignBranchArgs(D, Args, CmdArgs, /*IsLTO=*/true); + + if (checkRemarksOptions(Linker, Args, ToolChain.getEffectiveTriple())) { + // handle remark diagnostics on screen options: '-Rpass-*' + if (hasRpassOptions(Args)) { + renderRpassOptions(Args, CmdArgs); + } + + // handle serialized remarks options: '-fsave-optimization-record' + // and '-foptimization-record-*' + if (willEmitRemarks(Args)) { + renderRemarksOptions(Args, CmdArgs, ToolChain.getEffectiveTriple(), Input, + Output); + } + + // handle remarks hotness/threshold related options + renderRemarksHotnessOptions(Args, CmdArgs); + } } void tools::addArchSpecificRPath(const ToolChain &TC, const ArgList &Args, 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 @@ -14,6 +14,7 @@ #include "clang/Basic/CommentOptions.h" #include "clang/Basic/DebugInfoOptions.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticDriver.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/LLVM.h" @@ -66,6 +67,7 @@ #include "llvm/Option/OptTable.h" #include "llvm/Option/Option.h" #include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" @@ -1390,11 +1392,24 @@ Diags.Report(diag::warn_drv_diagnostics_hotness_requires_pgo) << "-fdiagnostics-show-hotness"; - Opts.DiagnosticsHotnessThreshold = getLastArgUInt64Value( - Args, options::OPT_fdiagnostics_hotness_threshold_EQ, 0); - if (Opts.DiagnosticsHotnessThreshold > 0 && !UsingProfile) - Diags.Report(diag::warn_drv_diagnostics_hotness_requires_pgo) - << "-fdiagnostics-hotness-threshold="; + // Parse remarks hotness threshold. Valid value is either integer or 'auto'. + if (auto *arg = + Args.getLastArg(options::OPT_fdiagnostics_hotness_threshold_EQ)) { + auto ResultOrErr = + llvm::remarks::parseHotnessThresholdOption(arg->getValue()); + + if (!ResultOrErr) { + Diags.Report(diag::err_drv_invalid_diagnotics_hotness_threshold) + << "-fdiagnostics-hotness-threshold="; + } else { + Opts.DiagnosticsHotnessThreshold = *ResultOrErr; + if ((!Opts.DiagnosticsHotnessThreshold.hasValue() || + Opts.DiagnosticsHotnessThreshold.getValue() > 0) && + !UsingProfile) + Diags.Report(diag::warn_drv_diagnostics_hotness_requires_pgo) + << "-fdiagnostics-hotness-threshold="; + } + } // If the user requested to use a sample profile for PGO, then the // backend will need to track source location information so the profile diff --git a/clang/test/Driver/remarks-pass-through.c b/clang/test/Driver/remarks-pass-through.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/remarks-pass-through.c @@ -0,0 +1,38 @@ +// This test verifies remarks options pass-through into linker(lld) + +// no pass-through if lto is disabled +// RUN: %clang -### -o FOO -fdiagnostics-hotness-threshold=auto -fsave-optimization-record %s 2>&1 | not FileCheck %s + +// no pass-through if linker is not lld +// RUN: %clang -### -o FOO -fuse-ld=gold -fdiagnostics-hotness-threshold=auto -fsave-optimization-record %s 2>&1 | not FileCheck %s + +// pass-through cases +// RUN: %clang -### -o FOO -fuse-ld=lld -flto -fdiagnostics-hotness-threshold=100 -fsave-optimization-record -foptimization-record-passes=inline %s 2>&1 | FileCheck %s +// RUN: %clang -### -o FOO -fuse-ld=lld -flto -fdiagnostics-hotness-threshold=100 -fsave-optimization-record -foptimization-record-passes=inline %s 2>&1 | FileCheck %s -check-prefix=CHECK-MANUAL + +// RUN: %clang -### -o FOO -fuse-ld=lld -flto -fdiagnostics-hotness-threshold=auto -fsave-optimization-record -foptimization-record-passes=inline %s 2>&1 | FileCheck %s +// RUN: %clang -### -o FOO -fuse-ld=lld -flto -fdiagnostics-hotness-threshold=auto -fsave-optimization-record -foptimization-record-passes=inline %s 2>&1 | FileCheck %s -check-prefix=CHECK-AUTO +// +// RUN: %clang -### -o FOO -fuse-ld=lld -flto=thin -fdiagnostics-hotness-threshold=auto -fsave-optimization-record -foptimization-record-passes=inline %s 2>&1 | FileCheck %s +// RUN: %clang -### -o FOO -fuse-ld=lld -flto=thin -fdiagnostics-hotness-threshold=auto -fsave-optimization-record -foptimization-record-passes=inline %s 2>&1 | FileCheck %s -check-prefix=CHECK-AUTO +// +// RUN: %clang -### -o FOO -fuse-ld=lld -flto=thin -fdiagnostics-hotness-threshold=auto -fsave-optimization-record=some-format -foptimization-record-file=FOO.txt %s 2>&1 | FileCheck %s -check-prefix=CHECK-CUSTOM +// +// RUN: %clang -### -o FOO -fuse-ld=lld -flto=thin -fdiagnostics-hotness-threshold=auto -Rpass=inline -Rpass-missed=inline -Rpass-analysis=inline %s 2>&1 | FileCheck %s -check-prefix=CHECK-RPASS +// +// CHECK: "--opt-remarks-filename" "FOO.opt.ld.yaml" +// CHECK: "--opt-remarks-passes" "inline" +// CHECK: "--opt-remarks-format" "yaml" +// +// CHECK-MANUAL: "--opt-remarks-hotness-threshold=100" +// +// CHECK-AUTO: "--opt-remarks-hotness-threshold=auto" +// +// CHECK-CUSTOM: "--opt-remarks-filename" "FOO.txt.opt.ld.some-format" +// CHECK-CUSTOM: "--opt-remarks-format" "some-format" +// CHECK-CUSTOM: "--opt-remarks-hotness-threshold=auto" +// +// CHECK-RPASS: "-mllvm" "-pass-remarks=inline" +// CHECK-RPASS: "-mllvm" "-pass-remarks-missed=inline" +// CHECK-RPASS: "-mllvm" "-pass-remarks-analysis=inline" +// CHECK-RPASS: "--opt-remarks-hotness-threshold=auto" diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -260,6 +260,21 @@ unsigned timeTraceGranularity; int32_t splitStackAdjustSize; + // The minimum hotness value a diagnostic needs in order to be included in + // optimization diagnostics. + // + // The threshold is an Optional value, which maps to one of the 3 states: + // 1. 0 => threshold disabled. All emarks will be printed. + // 2. positive int => manual threshold by user. Remarks with hotness exceed + // threshold will be printed. + // 3. None => 'auto' threshold by user. The actual value is not + // available at command line, but will be synced with + // hotness threhold from profile summary during + // compilation. + // + // If threshold option is not specified, it is disabled by default. + llvm::Optional optRemarksHotnessThreshold = 0; + // The following config options do not directly correspond to any // particular command line options. diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -48,6 +48,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/LTO/LTO.h" +#include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" #include "llvm/Support/GlobPattern.h" @@ -978,6 +979,16 @@ config->oFormatBinary = isOutputFormatBinary(args); config->omagic = args.hasFlag(OPT_omagic, OPT_no_omagic, false); config->optRemarksFilename = args.getLastArgValue(OPT_opt_remarks_filename); + // Parse remarks hotness threshold. Valid value is either integer or 'auto'. + if (auto *arg = args.getLastArg(OPT_opt_remarks_hotness_threshold)) { + auto resultOrErr = remarks::parseHotnessThresholdOption(arg->getValue()); + if (!resultOrErr) { + error(arg->getSpelling() + ": invalid argument '" + arg->getValue() + + "', only integer or 'auto' is supported."); + } else { + config->optRemarksHotnessThreshold = *resultOrErr; + } + } config->optRemarksPasses = args.getLastArgValue(OPT_opt_remarks_passes); config->optRemarksWithHotness = args.hasArg(OPT_opt_remarks_with_hotness); config->optRemarksFormat = args.getLastArgValue(OPT_opt_remarks_format); diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp --- a/lld/ELF/LTO.cpp +++ b/lld/ELF/LTO.cpp @@ -130,6 +130,7 @@ c.RemarksFilename = std::string(config->optRemarksFilename); c.RemarksPasses = std::string(config->optRemarksPasses); c.RemarksWithHotness = config->optRemarksWithHotness; + c.RemarksHotnessThreshold = config->optRemarksHotnessThreshold; c.RemarksFormat = std::string(config->optRemarksFormat); c.SampleProfile = std::string(config->ltoSampleProfile); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -538,6 +538,10 @@ defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">; def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">, HelpText<"YAML output file for optimization remarks">; +defm opt_remarks_hotness_threshold: EEq<"opt-remarks-hotness-threshold", + "Minimum profile count required for an optimization remark to be output." + " Use 'auto' to apply the threshold from profile summary.">, + MetaVarName<"">; def opt_remarks_passes: Separate<["--"], "opt-remarks-passes">, HelpText<"Regex for the passes that need to be serialized to the output file">; def opt_remarks_with_hotness: FF<"opt-remarks-with-hotness">, diff --git a/lld/test/ELF/lto/opt-remarks.ll b/lld/test/ELF/lto/opt-remarks.ll --- a/lld/test/ELF/lto/opt-remarks.ll +++ b/lld/test/ELF/lto/opt-remarks.ll @@ -1,19 +1,25 @@ ; REQUIRES: x86 ; RUN: llvm-as %s -o %t.o -; RUN: rm -f %t.yaml +; RUN: rm -f %t.yaml %t1.yaml %t.hot.yaml %t.t300.yaml %t.t301.yaml ; RUN: ld.lld --opt-remarks-filename %t.yaml %t.o -o %t -shared -save-temps ; RUN: llvm-dis %t.0.4.opt.bc -o - | FileCheck %s ; RUN: ld.lld --opt-remarks-with-hotness --opt-remarks-filename %t.hot.yaml \ ; RUN: %t.o -o %t -shared +; RUN: ld.lld --opt-remarks-with-hotness --opt-remarks-hotness-threshold=300 \ +; RUN: --opt-remarks-filename %t.t300.yaml %t.o -o %t -shared +; RUN: ld.lld --opt-remarks-with-hotness --opt-remarks-hotness-threshold=301 \ +; RUN: --opt-remarks-filename %t.t301.yaml %t.o -o %t -shared ; RUN: cat %t.yaml | FileCheck %s -check-prefix=YAML ; RUN: cat %t.hot.yaml | FileCheck %s -check-prefix=YAML-HOT +; RUN: cat %t.t300.yaml | FileCheck %s -check-prefix=YAML-HOT +; RUN: count 0 < %t.t301.yaml ; RUN: ld.lld --opt-remarks-filename %t1.yaml --opt-remarks-passes inline %t.o \ ; RUN: -o /dev/null -shared ; RUN: cat %t1.yaml | FileCheck %s -check-prefix=YAML-PASSES ; RUN: ld.lld --opt-remarks-filename %t1.yaml --opt-remarks-format yaml %t.o \ ; RUN: -o /dev/null -shared -; RUN: cat %t.yaml | FileCheck %s -check-prefix=YAML +; RUN: cat %t1.yaml | FileCheck %s -check-prefix=YAML ; Check that @tinkywinky is inlined after optimizations. ; CHECK-LABEL: define i32 @main diff --git a/llvm/include/llvm/Analysis/ProfileSummaryInfo.h b/llvm/include/llvm/Analysis/ProfileSummaryInfo.h --- a/llvm/include/llvm/Analysis/ProfileSummaryInfo.h +++ b/llvm/include/llvm/Analysis/ProfileSummaryInfo.h @@ -38,7 +38,7 @@ // units. This would require making this depend on BFI. class ProfileSummaryInfo { private: - Module &M; + const Module &M; std::unique_ptr Summary; void computeThresholds(); // Count thresholds to answer isHotCount and isColdCount queries. @@ -58,7 +58,8 @@ mutable DenseMap ThresholdCache; public: - ProfileSummaryInfo(Module &M) : M(M) { refresh(); } + ProfileSummaryInfo(const Module &M) : M(M) { refresh(); } + ProfileSummaryInfo(ProfileSummaryInfo &&Arg) = default; /// If no summary is present, attempt to refresh. diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h --- a/llvm/include/llvm/IR/LLVMContext.h +++ b/llvm/include/llvm/IR/LLVMContext.h @@ -222,13 +222,23 @@ void setDiagnosticsHotnessRequested(bool Requested); /// Return the minimum hotness value a diagnostic would need in order - /// to be included in optimization diagnostics. If there is no minimum, this - /// returns None. + /// to be included in optimization diagnostics. + /// + /// Three possible return values: + /// 0 - threshold is disabled. Everything will be printed out. + /// positive int - threshold is set. + /// UINT64_MAX - threshold is not yet set, and needs to be synced from + /// profile summary. Note that in case of missing profile + /// summary, threshold will be kept at "MAX", effectively + /// suppresses all remarks output. uint64_t getDiagnosticsHotnessThreshold() const; /// Set the minimum hotness value a diagnostic needs in order to be /// included in optimization diagnostics. - void setDiagnosticsHotnessThreshold(uint64_t Threshold); + void setDiagnosticsHotnessThreshold(Optional Threshold); + + /// Return if hotness threshold is requested from PSI. + bool isDiagnosticsHotnessThresholdSetFromPSI() const; /// The "main remark streamer" used by all the specialized remark streamers. /// This streamer keeps generic remark metadata in memory throughout the life diff --git a/llvm/include/llvm/IR/LLVMRemarkStreamer.h b/llvm/include/llvm/IR/LLVMRemarkStreamer.h --- a/llvm/include/llvm/IR/LLVMRemarkStreamer.h +++ b/llvm/include/llvm/IR/LLVMRemarkStreamer.h @@ -79,16 +79,15 @@ setupLLVMOptimizationRemarks(LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses, StringRef RemarksFormat, bool RemarksWithHotness, - unsigned RemarksHotnessThreshold = 0); + Optional RemarksHotnessThreshold = 0); /// Setup optimization remarks that output directly to a raw_ostream. /// \p OS is managed by the caller and should be open for writing as long as \p /// Context is streaming remarks to it. -Error setupLLVMOptimizationRemarks(LLVMContext &Context, raw_ostream &OS, - StringRef RemarksPasses, - StringRef RemarksFormat, - bool RemarksWithHotness, - unsigned RemarksHotnessThreshold = 0); +Error setupLLVMOptimizationRemarks( + LLVMContext &Context, raw_ostream &OS, StringRef RemarksPasses, + StringRef RemarksFormat, bool RemarksWithHotness, + Optional RemarksHotnessThreshold = 0); } // end namespace llvm diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h --- a/llvm/include/llvm/IR/Module.h +++ b/llvm/include/llvm/IR/Module.h @@ -854,7 +854,7 @@ /// Returns profile summary metadata. When IsCS is true, use the context /// sensitive profile summary. - Metadata *getProfileSummary(bool IsCS); + Metadata *getProfileSummary(bool IsCS) const; /// @} /// Returns whether semantic interposition is to be respected. diff --git a/llvm/include/llvm/LTO/Config.h b/llvm/include/llvm/LTO/Config.h --- a/llvm/include/llvm/LTO/Config.h +++ b/llvm/include/llvm/LTO/Config.h @@ -121,6 +121,21 @@ /// Whether to emit optimization remarks with hotness informations. bool RemarksWithHotness = false; + /// The minimum hotness value a diagnostic needs in order to be included in + /// optimization diagnostics. + /// + /// The threshold is an Optional value, which maps to one of the 3 states: + /// 1. 0 => threshold disabled. All emarks will be printed. + /// 2. positive int => manual threshold by user. Remarks with hotness exceed + /// threshold will be printed. + /// 3. None => 'auto' threshold by user. The actual value is not + /// available at command line, but will be synced with + /// hotness threhold from profile summary during + /// compilation. + /// + /// If threshold option is not specified, it is disabled by default. + llvm::Optional RemarksHotnessThreshold = 0; + /// The format used for serializing remarks (default: YAML). std::string RemarksFormat = ""; diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h --- a/llvm/include/llvm/LTO/LTO.h +++ b/llvm/include/llvm/LTO/LTO.h @@ -82,10 +82,10 @@ const std::string &NewPrefix); /// Setup optimization remarks. -Expected> -setupLLVMOptimizationRemarks(LLVMContext &Context, StringRef RemarksFilename, - StringRef RemarksPasses, StringRef RemarksFormat, - bool RemarksWithHotness, int Count = -1); +Expected> setupLLVMOptimizationRemarks( + LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses, + StringRef RemarksFormat, bool RemarksWithHotness, + Optional RemarksHotnessThreshold = 0, int Count = -1); /// Setups the output file for saving statistics. Expected> diff --git a/llvm/include/llvm/Remarks/HotnessThresholdParser.h b/llvm/include/llvm/Remarks/HotnessThresholdParser.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Remarks/HotnessThresholdParser.h @@ -0,0 +1,65 @@ +//===- HotnessThresholdParser.h - Parser for hotness threshold --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// @file +/// This file implements a simple parser to decode commandline option for +/// remarks hotness threshold. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_REMARKS_HOTNESSTHRESHOLDPARSER_H +#define LLVM_REMARKS_HOTNESSTHRESHOLDPARSER_H + +#include "llvm/ADT/Optional.h" +#include "llvm/Support/CommandLine.h" +namespace llvm { +namespace remarks { + +// Parse remarks hotness threshold argument value. +// Valid option values is +// 1. integer: manually specified threshold; or +// 2. string 'auto': automatically get threshold from profile summary +// +// Return None Optional if 'auto' is specified, indicating the value will +// be filled later during PSI. +inline Expected> parseHotnessThresholdOption(StringRef Arg) { + if (Arg == "auto") { + return None; + } + + int64_t Val; + if (Arg.getAsInteger(10, Val)) { + return createStringError(llvm::inconvertibleErrorCode(), + "Not an integer: %s", Arg.data()); + } + + // Negative integer effectively means no threshold + return Val < 0 ? 0 : Val; +} + +// A simple CL parser for '-pass-remarks-hotness-threshold=' +class HotnessThresholdParser : public cl::parser> { +public: + HotnessThresholdParser(cl::Option &O) : cl::parser>(O) {} + + bool parse(cl::Option &O, StringRef ArgName, StringRef Arg, + Optional &V) { + auto ResultOrErr = parseHotnessThresholdOption(Arg); + if (!ResultOrErr) { + return O.error("Invalid argument '" + Arg + + "', only integer or 'auto' is supported."); + } + + V = *ResultOrErr; + return false; + } +}; + +} // namespace remarks +} // namespace llvm +#endif // LLVM_REMARKS_HOTNESSTHRESHOLDPARSER_H diff --git a/llvm/lib/Analysis/OptimizationRemarkEmitter.cpp b/llvm/lib/Analysis/OptimizationRemarkEmitter.cpp --- a/llvm/lib/Analysis/OptimizationRemarkEmitter.cpp +++ b/llvm/lib/Analysis/OptimizationRemarkEmitter.cpp @@ -15,6 +15,7 @@ #include "llvm/Analysis/BranchProbabilityInfo.h" #include "llvm/Analysis/LazyBlockFrequencyInfo.h" #include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/LLVMContext.h" @@ -96,9 +97,17 @@ bool OptimizationRemarkEmitterWrapperPass::runOnFunction(Function &Fn) { BlockFrequencyInfo *BFI; - if (Fn.getContext().getDiagnosticsHotnessRequested()) + auto &Context = Fn.getContext(); + if (Context.getDiagnosticsHotnessRequested()) { BFI = &getAnalysis().getBFI(); - else + // Get hotness threshold from PSI. This should only happen once. + if (Context.isDiagnosticsHotnessThresholdSetFromPSI()) { + if (ProfileSummaryInfo *PSI = + &getAnalysis().getPSI()) + Context.setDiagnosticsHotnessThreshold( + PSI->getOrCompHotCountThreshold()); + } + } else BFI = nullptr; ORE = std::make_unique(&Fn, BFI); @@ -117,10 +126,19 @@ OptimizationRemarkEmitterAnalysis::run(Function &F, FunctionAnalysisManager &AM) { BlockFrequencyInfo *BFI; + auto &Context = F.getContext(); - if (F.getContext().getDiagnosticsHotnessRequested()) + if (Context.getDiagnosticsHotnessRequested()) { BFI = &AM.getResult(F); - else + // Get hotness threshold from PSI. This should only happen once. + if (Context.isDiagnosticsHotnessThresholdSetFromPSI()) { + auto &MAMProxy = AM.getResult(F); + if (ProfileSummaryInfo *PSI = + MAMProxy.getCachedResult(*F.getParent())) + Context.setDiagnosticsHotnessThreshold( + PSI->getOrCompHotCountThreshold()); + } + } else BFI = nullptr; return OptimizationRemarkEmitter(&F, BFI); @@ -133,5 +151,6 @@ INITIALIZE_PASS_BEGIN(OptimizationRemarkEmitterWrapperPass, ORE_NAME, ore_name, false, true) INITIALIZE_PASS_DEPENDENCY(LazyBFIPass) +INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass) INITIALIZE_PASS_END(OptimizationRemarkEmitterWrapperPass, ORE_NAME, ore_name, false, true) diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp --- a/llvm/lib/IR/LLVMContext.cpp +++ b/llvm/lib/IR/LLVMContext.cpp @@ -146,11 +146,16 @@ return pImpl->DiagnosticsHotnessRequested; } -void LLVMContext::setDiagnosticsHotnessThreshold(uint64_t Threshold) { +void LLVMContext::setDiagnosticsHotnessThreshold(Optional Threshold) { pImpl->DiagnosticsHotnessThreshold = Threshold; } + uint64_t LLVMContext::getDiagnosticsHotnessThreshold() const { - return pImpl->DiagnosticsHotnessThreshold; + return pImpl->DiagnosticsHotnessThreshold.getValueOr(UINT64_MAX); +} + +bool LLVMContext::isDiagnosticsHotnessThresholdSetFromPSI() const { + return !pImpl->DiagnosticsHotnessThreshold.hasValue(); } remarks::RemarkStreamer *LLVMContext::getMainRemarkStreamer() { diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1296,7 +1296,26 @@ std::unique_ptr DiagHandler; bool RespectDiagnosticFilters = false; bool DiagnosticsHotnessRequested = false; - uint64_t DiagnosticsHotnessThreshold = 0; + /// The minimum hotness value a diagnostic needs in order to be included in + /// optimization diagnostics. + /// + /// The threshold is an Optional value, which maps to one of the 3 states: + /// 1). 0 => threshold disabled. All emarks will be printed. + /// 2). positive int => manual threshold by user. Remarks with hotness exceed + /// threshold will be printed. + /// 3). None => 'auto' threshold by user. The actual value is not + /// available at command line, but will be synced with + /// hotness threhold from profile summary during + /// compilation. + /// + /// State 1 and 2 are considered as terminal states. State transition is + /// only allowed from 3 to 2, when the threshold is first synced with profile + /// summary. This ensures that the threshold is set only once and stays + /// constant. + /// + /// If threshold option is not specified, it is disabled (0) by default. + Optional DiagnosticsHotnessThreshold = 0; + /// The specialized remark streamer used by LLVM's OptimizationRemarkEmitter. std::unique_ptr LLVMRS; diff --git a/llvm/lib/IR/LLVMRemarkStreamer.cpp b/llvm/lib/IR/LLVMRemarkStreamer.cpp --- a/llvm/lib/IR/LLVMRemarkStreamer.cpp +++ b/llvm/lib/IR/LLVMRemarkStreamer.cpp @@ -92,12 +92,11 @@ Expected> llvm::setupLLVMOptimizationRemarks( LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses, StringRef RemarksFormat, bool RemarksWithHotness, - unsigned RemarksHotnessThreshold) { + Optional RemarksHotnessThreshold) { if (RemarksWithHotness) Context.setDiagnosticsHotnessRequested(true); - if (RemarksHotnessThreshold) - Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold); + Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold); if (RemarksFilename.empty()) return nullptr; @@ -137,16 +136,14 @@ return std::move(RemarksFile); } -Error llvm::setupLLVMOptimizationRemarks(LLVMContext &Context, raw_ostream &OS, - StringRef RemarksPasses, - StringRef RemarksFormat, - bool RemarksWithHotness, - unsigned RemarksHotnessThreshold) { +Error llvm::setupLLVMOptimizationRemarks( + LLVMContext &Context, raw_ostream &OS, StringRef RemarksPasses, + StringRef RemarksFormat, bool RemarksWithHotness, + Optional RemarksHotnessThreshold) { if (RemarksWithHotness) Context.setDiagnosticsHotnessRequested(true); - if (RemarksHotnessThreshold) - Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold); + Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold); Expected Format = remarks::parseFormat(RemarksFormat); if (Error E = Format.takeError()) diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp --- a/llvm/lib/IR/Module.cpp +++ b/llvm/lib/IR/Module.cpp @@ -582,7 +582,7 @@ setModuleFlag(ModFlagBehavior::Error, "ProfileSummary", M); } -Metadata *Module::getProfileSummary(bool IsCS) { +Metadata *Module::getProfileSummary(bool IsCS) const { return (IsCS ? getModuleFlag("CSProfileSummary") : getModuleFlag("ProfileSummary")); } diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -983,7 +983,8 @@ // Setup optimization remarks. auto DiagFileOrErr = lto::setupLLVMOptimizationRemarks( RegularLTO.CombinedModule->getContext(), Conf.RemarksFilename, - Conf.RemarksPasses, Conf.RemarksFormat, Conf.RemarksWithHotness); + Conf.RemarksPasses, Conf.RemarksFormat, Conf.RemarksWithHotness, + Conf.RemarksHotnessThreshold); if (!DiagFileOrErr) return DiagFileOrErr.takeError(); @@ -1459,7 +1460,8 @@ Expected> lto::setupLLVMOptimizationRemarks( LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses, - StringRef RemarksFormat, bool RemarksWithHotness, int Count) { + StringRef RemarksFormat, bool RemarksWithHotness, + Optional RemarksHotnessThreshold, int Count) { std::string Filename = std::string(RemarksFilename); // For ThinLTO, file.opt. becomes // file.opt..thin... @@ -1469,7 +1471,8 @@ .str(); auto ResultOrErr = llvm::setupLLVMOptimizationRemarks( - Context, Filename, RemarksPasses, RemarksFormat, RemarksWithHotness); + Context, Filename, RemarksPasses, RemarksFormat, RemarksWithHotness, + RemarksHotnessThreshold); if (Error E = ResultOrErr.takeError()) return std::move(E); diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp --- a/llvm/lib/LTO/LTOBackend.cpp +++ b/llvm/lib/LTO/LTOBackend.cpp @@ -542,7 +542,8 @@ // Setup optimization remarks. auto DiagFileOrErr = lto::setupLLVMOptimizationRemarks( Mod.getContext(), Conf.RemarksFilename, Conf.RemarksPasses, - Conf.RemarksFormat, Conf.RemarksWithHotness, Task); + Conf.RemarksFormat, Conf.RemarksWithHotness, Conf.RemarksHotnessThreshold, + Task); if (!DiagFileOrErr) return DiagFileOrErr.takeError(); auto DiagnosticOutputFile = std::move(*DiagFileOrErr); diff --git a/llvm/lib/LTO/LTOCodeGenerator.cpp b/llvm/lib/LTO/LTOCodeGenerator.cpp --- a/llvm/lib/LTO/LTOCodeGenerator.cpp +++ b/llvm/lib/LTO/LTOCodeGenerator.cpp @@ -43,6 +43,7 @@ #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/SubtargetFeature.h" +#include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -87,6 +88,15 @@ cl::desc("With PGO, include profile count in optimization remarks"), cl::Hidden); +cl::opt, false, remarks::HotnessThresholdParser> + RemarksHotnessThreshold( + "lto-pass-remarks-hotness-threshold", + cl::desc("Minimum profile count required for an " + "optimization remark to be output." + " Use 'auto' to apply the threshold from profile summary."), + cl::value_desc("uint or 'auto'"), cl::init(Optional(0)), + cl::Hidden); + cl::opt RemarksFilename("lto-pass-remarks-output", cl::desc("Output filename for pass remarks"), @@ -528,9 +538,9 @@ if (!this->determineTarget()) return false; - auto DiagFileOrErr = - lto::setupLLVMOptimizationRemarks(Context, RemarksFilename, RemarksPasses, - RemarksFormat, RemarksWithHotness); + auto DiagFileOrErr = lto::setupLLVMOptimizationRemarks( + Context, RemarksFilename, RemarksPasses, RemarksFormat, + RemarksWithHotness, RemarksHotnessThreshold); if (!DiagFileOrErr) { errs() << "Error: " << toString(DiagFileOrErr.takeError()) << "\n"; report_fatal_error("Can't get an output file for the remarks"); diff --git a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp --- a/llvm/lib/LTO/ThinLTOCodeGenerator.cpp +++ b/llvm/lib/LTO/ThinLTOCodeGenerator.cpp @@ -75,6 +75,7 @@ extern cl::opt RemarksFilename; extern cl::opt RemarksPasses; extern cl::opt RemarksWithHotness; +extern cl::opt> RemarksHotnessThreshold; extern cl::opt RemarksFormat; } @@ -1097,7 +1098,7 @@ Context.enableDebugTypeODRUniquing(); auto DiagFileOrErr = lto::setupLLVMOptimizationRemarks( Context, RemarksFilename, RemarksPasses, RemarksFormat, - RemarksWithHotness, count); + RemarksWithHotness, RemarksHotnessThreshold, count); if (!DiagFileOrErr) { errs() << "Error: " << toString(DiagFileOrErr.takeError()) << "\n"; report_fatal_error("ThinLTO: Can't get an output file for the " diff --git a/llvm/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll b/llvm/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll --- a/llvm/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll +++ b/llvm/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll @@ -2,12 +2,24 @@ ; with -lto-pass-remarks-with-hotness. ; RUN: llvm-as < %s >%t.bc -; RUN: rm -f %t.yaml +; RUN: rm -f %t.yaml %t.t300.yaml %t.t301.yaml ; RUN: llvm-lto -lto-pass-remarks-output=%t.yaml \ ; RUN: -lto-pass-remarks-with-hotness \ ; RUN: -exported-symbol _main -o %t.o %t.bc ; RUN: cat %t.yaml | FileCheck -check-prefix=YAML %s +; RUN: llvm-lto -lto-pass-remarks-output=%t.t300.yaml \ +; RUN: -lto-pass-remarks-with-hotness \ +; RUN: -lto-pass-remarks-hotness-threshold=300 \ +; RUN: -exported-symbol _main -o %t.o %t.bc +; RUN: cat %t.t300.yaml | FileCheck -check-prefix=YAML %s + +; RUN: llvm-lto -lto-pass-remarks-output=%t.t301.yaml \ +; RUN: -lto-pass-remarks-with-hotness \ +; RUN: -lto-pass-remarks-hotness-threshold=301 \ +; RUN: -exported-symbol _main -o %t.o %t.bc +; RUN: cat %t.t301.yaml | not FileCheck -check-prefix=YAML %s + ; YAML: --- !Passed ; YAML-NEXT: Pass: inline ; YAML-NEXT: Name: Inlined diff --git a/llvm/test/Other/optimization-remarks-auto.ll b/llvm/test/Other/optimization-remarks-auto.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/optimization-remarks-auto.ll @@ -0,0 +1,85 @@ +;; This test verifies 'auto' hotness threshold when profile summary is available. +;; +;; new PM +; RUN: rm -f %t.yaml %t.hot.yaml +; RUN: opt < %s --disable-output --enable-new-pm \ +; RUN: --passes='inline' \ +; RUN: --pass-remarks-output=%t.yaml --pass-remarks-filter='inline' \ +; RUN: --pass-remarks-with-hotness +; RUN: FileCheck %s -check-prefix=YAML-PASS < %t.yaml +; RUN: FileCheck %s -check-prefix=YAML-MISS < %t.yaml + +;; test 'auto' threshold +; RUN: opt < %s --disable-output --enable-new-pm \ +; RUN: --passes='module(print-profile-summary,cgscc(inline))' \ +; RUN: --pass-remarks-output=%t.hot.yaml --pass-remarks-filter='inline' \ +; RUN: --pass-remarks-with-hotness --pass-remarks-hotness-threshold=auto 2>&1 | FileCheck %s +; RUN: FileCheck %s -check-prefix=YAML-PASS < %t.hot.yaml +; RUN: not FileCheck %s -check-prefix=YAML-MISS < %t.hot.yaml + +; RUN: opt < %s --disable-output --enable-new-pm \ +; RUN: --passes='module(print-profile-summary,cgscc(inline))' \ +; RUN: --pass-remarks=inline --pass-remarks-missed=inline --pass-remarks-analysis=inline \ +; RUN: --pass-remarks-with-hotness --pass-remarks-hotness-threshold=auto 2>&1 | FileCheck %s -check-prefix=CHECK-RPASS + +; YAML-PASS: --- !Passed +; YAML-PASS-NEXT: Pass: inline +; YAML-PASS-NEXT: Name: Inlined +; YAML-PASS-NEXT: Function: caller1 +; YAML-PASS-NEXT: Hotness: 400 + +; YAML-MISS: --- !Missed +; YAML-MISS-NEXT: Pass: inline +; YAML-MISS-NEXT: Name: NeverInline +; YAML-MISS-NEXT: Function: caller2 +; YAML-MISS-NEXT: Hotness: 1 + +; CHECK-RPASS: callee1 inlined into caller1 with (cost=-30, threshold=4500) (hotness: 400) +; CHECK-RPASS-NOT: callee2 not inlined into caller2 because it should never be inlined (cost=never): noinline function attribute (hotness: 1) + +define void @callee1() !prof !20 { +; CHECK: callee1 :hot +entry: + ret void +} + +; Function Attrs: noinline +define void @callee2() noinline !prof !21 { +; CHECK: callee2 :cold +entry: + ret void +} + +define void @caller1() !prof !20 { +; CHECK: caller1 :hot +entry: + call void @callee1() + ret void +} + +define void @caller2() !prof !21 { +; CHECK: caller2 :cold +entry: + call void @callee2() + ret void +} + +!llvm.module.flags = !{!1} +!20 = !{!"function_entry_count", i64 400} +!21 = !{!"function_entry_count", i64 1} + +!1 = !{i32 1, !"ProfileSummary", !2} +!2 = !{!3, !4, !5, !6, !7, !8, !9, !10} +!3 = !{!"ProfileFormat", !"InstrProf"} +!4 = !{!"TotalCount", i64 10000} +!5 = !{!"MaxCount", i64 10} +!6 = !{!"MaxInternalCount", i64 1} +!7 = !{!"MaxFunctionCount", i64 1000} +!8 = !{!"NumCounts", i64 3} +!9 = !{!"NumFunctions", i64 3} +!10 = !{!"DetailedSummary", !11} +!11 = !{!12, !13, !14} +!12 = !{i32 10000, i64 100, i32 1} +!13 = !{i32 999000, i64 100, i32 1} +!14 = !{i32 999999, i64 1, i32 2} + diff --git a/llvm/test/Transforms/SampleProfile/Inputs/remarks-hotness.prof b/llvm/test/Transforms/SampleProfile/Inputs/remarks-hotness.prof new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/SampleProfile/Inputs/remarks-hotness.prof @@ -0,0 +1,8 @@ +_Z7callee1v:600:600 + 1: 600 +_Z7callee2v:1:1 + 1: 1 +_Z7caller1v:400:400 + 1: 400 +_Z7caller2v:1:1 + 1: 1 diff --git a/llvm/test/Transforms/SampleProfile/remarks-hotness.ll b/llvm/test/Transforms/SampleProfile/remarks-hotness.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/SampleProfile/remarks-hotness.ll @@ -0,0 +1,96 @@ +;; This test verifies 'auto' hotness threshold when profile file is provided. +;; +;; new PM +; RUN: rm -f %t.yaml %t.hot.yaml +; RUN: opt %s --enable-new-pm --passes='sample-profile,cgscc(inline)' \ +; RUN: --sample-profile-file=%S/Inputs/remarks-hotness.prof \ +; RUN: -S --pass-remarks-filter=inline --pass-remarks-output=%t.yaml \ +; RUN: -pass-remarks-with-hotness --disable-output +; RUN: FileCheck %s -check-prefix=YAML-PASS < %t.yaml +; RUN: FileCheck %s -check-prefix=YAML-MISS < %t.yaml + +;; test 'auto' threshold +; RUN: opt %s --enable-new-pm --passes='sample-profile,cgscc(inline)' \ +; RUN: --sample-profile-file=%S/Inputs/remarks-hotness.prof \ +; RUN: -S --pass-remarks-filter=inline --pass-remarks-output=%t.hot.yaml \ +; RUN: --pass-remarks-with-hotness --pass-remarks-hotness-threshold=auto --disable-output +; RUN: FileCheck %s -check-prefix=YAML-PASS < %t.hot.yaml +; RUN: not FileCheck %s -check-prefix=YAML-MISS < %t.hot.yaml + +; RUN: opt %s --enable-new-pm --passes='sample-profile,cgscc(inline)' \ +; RUN: --sample-profile-file=%S/Inputs/remarks-hotness.prof \ +; RUN: -S --pass-remarks=inline --pass-remarks-missed=inline --pass-remarks-analysis=inline \ +; RUN: --pass-remarks-with-hotness --pass-remarks-hotness-threshold=auto --disable-output 2>&1 | FileCheck %s -check-prefix=CHECK-RPASS + +; YAML-PASS: --- !Passed +; YAML-PASS-NEXT: Pass: inline +; YAML-PASS-NEXT: Name: Inlined +; YAML-PASS-NEXT: DebugLoc: { File: remarks-hotness.cpp, Line: 10, Column: 10 } +; YAML-PASS-NEXT: Function: _Z7caller1v +; YAML-PASS-NEXT: Hotness: 401 + +; YAML-MISS: --- !Missed +; YAML-MISS-NEXT: Pass: inline +; YAML-MISS-NEXT: Name: NeverInline +; YAML-MISS-NEXT: DebugLoc: { File: remarks-hotness.cpp, Line: 14, Column: 10 } +; YAML-MISS-NEXT: Function: _Z7caller2v +; YAML-MISS-NEXT: Hotness: 2 + +; CHECK-RPASS: _Z7callee1v inlined into _Z7caller1v with (cost=-30, threshold=4500) at callsite _Z7caller1v:1 (hotness: 401) +; CHECK-RPASS-NOT: _Z7callee2v not inlined into _Z7caller2v because it should never be inlined (cost=never): noinline function attribute (hotness: 2) + +; ModuleID = 'remarks-hotness.cpp' +source_filename = "remarks-hotness.cpp" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: use-sample-profile +define dso_local i32 @_Z7callee1v() #0 !dbg !7 { + ret i32 1, !dbg !11 +} + +; Function Attrs: noinline nounwind uwtable use-sample-profile +define dso_local i32 @_Z7callee2v() #1 !dbg !12 { + ret i32 2, !dbg !13 +} + +; Function Attrs: use-sample-profile +define dso_local i32 @_Z7caller1v() #0 !dbg !14 { + %1 = call i32 @_Z7callee1v(), !dbg !15 + ret i32 %1, !dbg !16 +} + +; Function Attrs: use-sample-profile +define dso_local i32 @_Z7caller2v() #0 !dbg !17 { + %1 = call i32 @_Z7callee2v(), !dbg !18 + ret i32 %1, !dbg !19 +} + +attributes #0 = { "use-sample-profile" } +attributes #1 = { noinline nounwind uwtable "use-sample-profile" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, debugInfoForProfiling: true, nameTableKind: None) +!1 = !DIFile(filename: "remarks-hotness.cpp", directory: ".") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 11.0.0"} +!7 = distinct !DISubprogram(name: "callee1", linkageName: "_Z7callee1v", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !DILocation(line: 2, column: 3, scope: !7) +!12 = distinct !DISubprogram(name: "callee2", linkageName: "_Z7callee2v", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!13 = !DILocation(line: 6, column: 3, scope: !12) +!14 = distinct !DISubprogram(name: "caller1", linkageName: "_Z7caller1v", scope: !1, file: !1, line: 9, type: !8, scopeLine: 9, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!15 = !DILocation(line: 10, column: 10, scope: !14) +!16 = !DILocation(line: 10, column: 3, scope: !14) +!17 = distinct !DISubprogram(name: "caller2", linkageName: "_Z7caller2v", scope: !1, file: !1, line: 13, type: !8, scopeLine: 13, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!18 = !DILocation(line: 14, column: 10, scope: !17) +!19 = !DILocation(line: 14, column: 3, scope: !17) + diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp --- a/llvm/tools/llc/llc.cpp +++ b/llvm/tools/llc/llc.cpp @@ -37,6 +37,7 @@ #include "llvm/InitializePasses.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Pass.h" +#include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" @@ -142,11 +143,13 @@ cl::desc("With PGO, include profile count in optimization remarks"), cl::Hidden); -static cl::opt - RemarksHotnessThreshold("pass-remarks-hotness-threshold", - cl::desc("Minimum profile count required for " - "an optimization remark to be output"), - cl::Hidden); +static cl::opt, false, remarks::HotnessThresholdParser> + RemarksHotnessThreshold( + "pass-remarks-hotness-threshold", + cl::desc("Minimum profile count required for " + "an optimization remark to be output. " + "Use 'auto' to apply the threshold from profile summary."), + cl::value_desc("N or 'auto'"), cl::init(0), cl::Hidden); static cl::opt RemarksFilename("pass-remarks-output", diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp --- a/llvm/tools/opt/opt.cpp +++ b/llvm/tools/opt/opt.cpp @@ -38,6 +38,7 @@ #include "llvm/LinkAllIR.h" #include "llvm/LinkAllPasses.h" #include "llvm/MC/SubtargetFeature.h" +#include "llvm/Remarks/HotnessThresholdParser.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -272,11 +273,13 @@ cl::desc("With PGO, include profile count in optimization remarks"), cl::Hidden); -static cl::opt - RemarksHotnessThreshold("pass-remarks-hotness-threshold", - cl::desc("Minimum profile count required for " - "an optimization remark to be output"), - cl::Hidden); +static cl::opt, false, remarks::HotnessThresholdParser> + RemarksHotnessThreshold( + "pass-remarks-hotness-threshold", + cl::desc("Minimum profile count required for " + "an optimization remark to be output. " + "Use 'auto' to apply the threshold from profile summary."), + cl::value_desc("N or 'auto'"), cl::init(0), cl::Hidden); static cl::opt RemarksFilename("pass-remarks-output",