weiwang created this revision. Herald added subscribers: llvm-commits, cfe-commits, dang, dexonsmith, steven_wu, MaskRay, hiraditya, eraman, arichardson, inglorion, emaste. Herald added a reviewer: espindola. Herald added a reviewer: MaskRay. Herald added projects: clang, LLVM.
Expand remarks hotness threshold option -fdiagnostics-hotness-threshold in clang driver command-line to both filtering remarks by hotness threshold from profile summary and automatic remarks options pass-through to linker. Remarks hotness filtering relies on several driver options. Table below lists how different options are correlated and affect final remarks outputs: | profile | hotness | threshold | remarks printed | | ------- | ------- | --------- | --------------- | | No | No | No | All | | No | No | Yes | None | | No | Yes | No | All | | No | Yes | Yes | None | | Yes | No | No | All | | Yes | No | Yes | None | | Yes | Yes | No | All | | Yes | Yes | Yes | >=threshold | | The new argument value -fdiagnostics-hotness-threshold=auto indicates threshold will be synced with hotness threshold from profile summary during compilation. In addition, when the following conditions are met, remarks related options are passed into linker as well: 1. LTO is enabled; 2. Single arch target is specified; 3. The linker is lld; The "auto" threshold relies on the availability of profile summary. In case of missing such information, no remarks will be generated. This gives novice user a convenient way to collect and filter remarks throughout a typical toolchain invocation with sample profile and LTO using single switch from the clang driver. A typical use of this option from clang command-line: - Using -Rpass* options to print remarks to screen: clang -fuse-ld=lld -flto=thin -fprofile-sample-use=foo_sample.txt -Rpass=inline -Rpass-missed=inline -Rpass-analysis=inline -fdiagnostics-show-hotness -fdiagnostics-hotness-threshold=auto -o foo foo.cpp Remarks will be dumped to screen from both pre-lto and lto compilation. - Using serialized remarks options: clang -fuse-ld=lld -flto=thin -fprofile-sample-use=foo_sample.txt -fsave-optimization-record -fdiagnostics-show-hotness -fdiagnostics-hotness-threshold=auto -o foo foo.cpp This will produce multiple yaml files containing optimization remarks: - foo.opt.yaml : remarks from pre-lto - foo.opt.ld.yaml.thin.1.yaml: remark during lto Both types of options can be used together. Given its restricted use cases, this shouldn't be viewed as a complete replacement of current remarks option handling for the linker. In order to make the option consistent across various tools, the support for the new 'auto' argument is also available in the following tools: - lld: -opt-remarks-with-hotness=auto - llvm-lto: -lto-pass-remarks-hotness-threshold=auto - opt/llc: --pass-remarks-hotness-threshold=auto Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D84246 Files: clang/include/clang/Basic/CodeGenOptions.def clang/include/clang/Basic/CodeGenOptions.h clang/include/clang/Basic/DiagnosticDriverKinds.td clang/include/clang/Driver/Driver.h clang/include/clang/Driver/Options.td clang/lib/Driver/Driver.cpp clang/lib/Driver/ToolChains/CommonArgs.cpp clang/lib/Frontend/CompilerInvocation.cpp clang/test/Driver/remarks-pass-through.c lld/ELF/Config.h lld/ELF/Driver.cpp lld/ELF/LTO.cpp lld/ELF/Options.td lld/test/ELF/lto/opt-remarks.ll llvm/include/llvm/Analysis/ProfileSummaryInfo.h llvm/include/llvm/IR/LLVMContext.h llvm/include/llvm/IR/LLVMRemarkStreamer.h llvm/include/llvm/IR/Module.h llvm/include/llvm/LTO/Config.h llvm/include/llvm/LTO/LTO.h llvm/include/llvm/Remarks/HotnessThresholdParser.h llvm/lib/Analysis/OptimizationRemarkEmitter.cpp llvm/lib/IR/LLVMContext.cpp llvm/lib/IR/LLVMContextImpl.h llvm/lib/IR/LLVMRemarkStreamer.cpp llvm/lib/IR/Module.cpp llvm/lib/LTO/LTO.cpp llvm/lib/LTO/LTOBackend.cpp llvm/lib/LTO/LTOCodeGenerator.cpp llvm/lib/LTO/ThinLTOCodeGenerator.cpp llvm/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll llvm/test/Other/optimization-remarks-auto.ll llvm/test/Transforms/SampleProfile/Inputs/remarks-hotness.prof llvm/test/Transforms/SampleProfile/remarks-hotness.ll llvm/tools/llc/llc.cpp llvm/tools/opt/opt.cpp
Index: llvm/tools/opt/opt.cpp =================================================================== --- llvm/tools/opt/opt.cpp +++ llvm/tools/opt/opt.cpp @@ -40,6 +40,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" @@ -274,11 +275,13 @@ cl::desc("With PGO, include profile count in optimization remarks"), cl::Hidden); -static cl::opt<unsigned> - RemarksHotnessThreshold("pass-remarks-hotness-threshold", - cl::desc("Minimum profile count required for " - "an optimization remark to be output"), - cl::Hidden); +static cl::opt<Optional<uint64_t>, 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<std::string> RemarksFilename("pass-remarks-output", Index: llvm/tools/llc/llc.cpp =================================================================== --- llvm/tools/llc/llc.cpp +++ 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<unsigned> - RemarksHotnessThreshold("pass-remarks-hotness-threshold", - cl::desc("Minimum profile count required for " - "an optimization remark to be output"), - cl::Hidden); +static cl::opt<Optional<uint64_t>, 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<std::string> RemarksFilename("pass-remarks-output", Index: llvm/test/Transforms/SampleProfile/remarks-hotness.ll =================================================================== --- /dev/null +++ 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) + Index: llvm/test/Transforms/SampleProfile/Inputs/remarks-hotness.prof =================================================================== --- /dev/null +++ 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 Index: llvm/test/Other/optimization-remarks-auto.ll =================================================================== --- /dev/null +++ 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} + Index: llvm/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll =================================================================== --- llvm/test/LTO/X86/diagnostic-handler-remarks-with-hotness.ll +++ 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 Index: llvm/lib/LTO/ThinLTOCodeGenerator.cpp =================================================================== --- llvm/lib/LTO/ThinLTOCodeGenerator.cpp +++ llvm/lib/LTO/ThinLTOCodeGenerator.cpp @@ -75,6 +75,7 @@ extern cl::opt<std::string> RemarksFilename; extern cl::opt<std::string> RemarksPasses; extern cl::opt<bool> RemarksWithHotness; +extern cl::opt<Optional<uint64_t>> RemarksHotnessThreshold; extern cl::opt<std::string> 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 " Index: llvm/lib/LTO/LTOCodeGenerator.cpp =================================================================== --- llvm/lib/LTO/LTOCodeGenerator.cpp +++ 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,14 @@ cl::desc("With PGO, include profile count in optimization remarks"), cl::Hidden); +cl::opt<Optional<uint64_t>, 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(0), cl::Hidden); + cl::opt<std::string> RemarksFilename("lto-pass-remarks-output", cl::desc("Output filename for pass remarks"), @@ -528,9 +537,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"); Index: llvm/lib/LTO/LTOBackend.cpp =================================================================== --- llvm/lib/LTO/LTOBackend.cpp +++ 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); Index: llvm/lib/LTO/LTO.cpp =================================================================== --- llvm/lib/LTO/LTO.cpp +++ 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<std::unique_ptr<ToolOutputFile>> lto::setupLLVMOptimizationRemarks( LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses, - StringRef RemarksFormat, bool RemarksWithHotness, int Count) { + StringRef RemarksFormat, bool RemarksWithHotness, + Optional<uint64_t> RemarksHotnessThreshold, int Count) { std::string Filename = std::string(RemarksFilename); // For ThinLTO, file.opt.<format> becomes // file.opt.<format>.thin.<num>.<format>. @@ -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); Index: llvm/lib/IR/Module.cpp =================================================================== --- llvm/lib/IR/Module.cpp +++ 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")); } Index: llvm/lib/IR/LLVMRemarkStreamer.cpp =================================================================== --- llvm/lib/IR/LLVMRemarkStreamer.cpp +++ llvm/lib/IR/LLVMRemarkStreamer.cpp @@ -92,12 +92,11 @@ Expected<std::unique_ptr<ToolOutputFile>> llvm::setupLLVMOptimizationRemarks( LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses, StringRef RemarksFormat, bool RemarksWithHotness, - unsigned RemarksHotnessThreshold) { + Optional<uint64_t> 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<uint64_t> RemarksHotnessThreshold) { if (RemarksWithHotness) Context.setDiagnosticsHotnessRequested(true); - if (RemarksHotnessThreshold) - Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold); + Context.setDiagnosticsHotnessThreshold(RemarksHotnessThreshold); Expected<remarks::Format> Format = remarks::parseFormat(RemarksFormat); if (Error E = Format.takeError()) Index: llvm/lib/IR/LLVMContextImpl.h =================================================================== --- llvm/lib/IR/LLVMContextImpl.h +++ llvm/lib/IR/LLVMContextImpl.h @@ -1296,7 +1296,26 @@ std::unique_ptr<DiagnosticHandler> 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<uint64_t> DiagnosticsHotnessThreshold = 0; + /// The specialized remark streamer used by LLVM's OptimizationRemarkEmitter. std::unique_ptr<LLVMRemarkStreamer> LLVMRS; Index: llvm/lib/IR/LLVMContext.cpp =================================================================== --- llvm/lib/IR/LLVMContext.cpp +++ llvm/lib/IR/LLVMContext.cpp @@ -146,11 +146,16 @@ return pImpl->DiagnosticsHotnessRequested; } -void LLVMContext::setDiagnosticsHotnessThreshold(uint64_t Threshold) { +void LLVMContext::setDiagnosticsHotnessThreshold(Optional<uint64_t> 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() { Index: llvm/lib/Analysis/OptimizationRemarkEmitter.cpp =================================================================== --- llvm/lib/Analysis/OptimizationRemarkEmitter.cpp +++ 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<LazyBlockFrequencyInfoPass>().getBFI(); - else + // Get hotness threshold from PSI. This should only happen once. + if (Context.isDiagnosticsHotnessThresholdSetFromPSI()) { + if (ProfileSummaryInfo *PSI = + &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI()) + Context.setDiagnosticsHotnessThreshold( + PSI->getOrCompHotCountThreshold()); + } + } else BFI = nullptr; ORE = std::make_unique<OptimizationRemarkEmitter>(&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<BlockFrequencyAnalysis>(F); - else + // Get hotness threshold from PSI. This should only happen once. + if (Context.isDiagnosticsHotnessThresholdSetFromPSI()) { + auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F); + if (ProfileSummaryInfo *PSI = + MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*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) Index: llvm/include/llvm/Remarks/HotnessThresholdParser.h =================================================================== --- /dev/null +++ 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<Optional<uint64_t>> 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<Optional<uint64_t>> { +public: + HotnessThresholdParser(cl::Option &O) : cl::parser<Optional<uint64_t>>(O) {} + + bool parse(cl::Option &O, StringRef ArgName, StringRef Arg, + Optional<uint64_t> &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 Index: llvm/include/llvm/LTO/LTO.h =================================================================== --- llvm/include/llvm/LTO/LTO.h +++ llvm/include/llvm/LTO/LTO.h @@ -82,10 +82,10 @@ const std::string &NewPrefix); /// Setup optimization remarks. -Expected<std::unique_ptr<ToolOutputFile>> -setupLLVMOptimizationRemarks(LLVMContext &Context, StringRef RemarksFilename, - StringRef RemarksPasses, StringRef RemarksFormat, - bool RemarksWithHotness, int Count = -1); +Expected<std::unique_ptr<ToolOutputFile>> setupLLVMOptimizationRemarks( + LLVMContext &Context, StringRef RemarksFilename, StringRef RemarksPasses, + StringRef RemarksFormat, bool RemarksWithHotness, + Optional<uint64_t> RemarksHotnessThreshold = 0, int Count = -1); /// Setups the output file for saving statistics. Expected<std::unique_ptr<ToolOutputFile>> Index: llvm/include/llvm/LTO/Config.h =================================================================== --- llvm/include/llvm/LTO/Config.h +++ 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<uint64_t> RemarksHotnessThreshold = 0; + /// The format used for serializing remarks (default: YAML). std::string RemarksFormat = ""; Index: llvm/include/llvm/IR/Module.h =================================================================== --- llvm/include/llvm/IR/Module.h +++ 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. Index: llvm/include/llvm/IR/LLVMRemarkStreamer.h =================================================================== --- llvm/include/llvm/IR/LLVMRemarkStreamer.h +++ 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<uint64_t> 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<uint64_t> RemarksHotnessThreshold = 0); } // end namespace llvm Index: llvm/include/llvm/IR/LLVMContext.h =================================================================== --- llvm/include/llvm/IR/LLVMContext.h +++ 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<uint64_t> 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 Index: llvm/include/llvm/Analysis/ProfileSummaryInfo.h =================================================================== --- llvm/include/llvm/Analysis/ProfileSummaryInfo.h +++ llvm/include/llvm/Analysis/ProfileSummaryInfo.h @@ -39,7 +39,7 @@ // units. This would require making this depend on BFI. class ProfileSummaryInfo { private: - Module &M; + const Module &M; std::unique_ptr<ProfileSummary> Summary; void computeThresholds(); // Count thresholds to answer isHotCount and isColdCount queries. @@ -59,7 +59,8 @@ mutable DenseMap<int, uint64_t> 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. Index: lld/test/ELF/lto/opt-remarks.ll =================================================================== --- lld/test/ELF/lto/opt-remarks.ll +++ 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 Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ 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<"<value>">; 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">, Index: lld/ELF/LTO.cpp =================================================================== --- lld/ELF/LTO.cpp +++ 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); Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ 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" @@ -980,6 +981,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); Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ 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<uint64_t> optRemarksHotnessThreshold = 0; + // The following config options do not directly correspond to any // particular command line options. Index: clang/test/Driver/remarks-pass-through.c =================================================================== --- /dev/null +++ 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" Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ 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 Index: clang/lib/Driver/ToolChains/CommonArgs.cpp =================================================================== --- clang/lib/Driver/ToolChains/CommonArgs.cpp +++ 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.<format>" 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, Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -5204,3 +5204,19 @@ return true; return false; } + +bool clang::driver::hasRpassOptions(const ArgList &Args) { + // -Rpass=<format> enables it. + if (Args.getLastArg(options::OPT_Rpass_EQ)) + return true; + + // -Rpass-missed=<format> alone enables it too. + if (Args.getLastArg(options::OPT_Rpass_missed_EQ)) + return true; + + // -Rpass-analysis=<format> alone enables it too. + if (Args.getLastArg(options::OPT_Rpass_analysis_EQ)) + return true; + + return false; +} Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -904,8 +904,9 @@ def fdiagnostics_show_hotness : Flag<["-"], "fdiagnostics-show-hotness">, Group<f_Group>, Flags<[CC1Option]>, HelpText<"Enable profile hotness information in diagnostic line">; def fdiagnostics_hotness_threshold_EQ : Joined<["-"], "fdiagnostics-hotness-threshold=">, - Group<f_Group>, Flags<[CC1Option]>, MetaVarName<"<number>">, - HelpText<"Prevent optimization remarks from being output if they do not have at least this profile count">; + Group<f_Group>, Flags<[CC1Option]>, MetaVarName<"<value>">, + 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<f_Group>, HelpText<"Print option name with mappable diagnostics">; def fdiagnostics_show_note_include_stack : Flag<["-"], "fdiagnostics-show-note-include-stack">, Index: clang/include/clang/Driver/Driver.h =================================================================== --- clang/include/clang/Driver/Driver.h +++ 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 Index: clang/include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticDriverKinds.td +++ 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< Index: clang/include/clang/Basic/CodeGenOptions.h =================================================================== --- clang/include/clang/Basic/CodeGenOptions.h +++ clang/include/clang/Basic/CodeGenOptions.h @@ -337,6 +337,21 @@ const char *Argv0 = nullptr; ArrayRef<const char *> 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<uint64_t> DiagnosticsHotnessThreshold = 0; + public: // Define accessors/mutators for code generation options of enumeration type. #define CODEGENOPT(Name, Bits, Default) Index: clang/include/clang/Basic/CodeGenOptions.def =================================================================== --- clang/include/clang/Basic/CodeGenOptions.def +++ 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)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits