diff --git a/llvm/docs/CommandGuide/opt.rst b/llvm/docs/CommandGuide/opt.rst index da93b8e4e9c54..eb15f0ea43634 100644 --- a/llvm/docs/CommandGuide/opt.rst +++ b/llvm/docs/CommandGuide/opt.rst @@ -70,6 +70,12 @@ OPTIONS Print statistics. +.. option:: --save-stats, --save-stats=cwd, --save-stats=obj + + Save LLVM statistics to a file in the current directory + (:option:`--save-stats`/"--save-stats=cwd") or the directory + of the output file ("--save-stats=obj") in JSON format. + .. option:: -time-passes Record the amount of time needed for each pass and print it to standard diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index fd78c97c86d24..1ae539fec0fef 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -183,6 +183,7 @@ Changes to the LLVM tools * Some code paths for supporting Python 2.7 in `llvm-lit` have been removed. * Support for `%T` in lit has been removed. * Add `--save-stats` option to `llc` to save LLVM statistics to a file. Compatible with the Clang option. +* Add `--save-stats` option to `opt` to save LLVM statistics to a file. Compatible with the Clang option. * `llvm-config` gained a new flag `--quote-paths` which quotes and escapes paths emitted on stdout, to account for spaces or other special characters in path. diff --git a/llvm/include/llvm/CodeGen/CommandFlags.h b/llvm/include/llvm/CodeGen/CommandFlags.h index af66f2d5776ba..59aacc75e055d 100644 --- a/llvm/include/llvm/CodeGen/CommandFlags.h +++ b/llvm/include/llvm/CodeGen/CommandFlags.h @@ -154,12 +154,22 @@ LLVM_ABI bool getJMCInstrument(); LLVM_ABI bool getXCOFFReadOnlyPointers(); +enum SaveStatsMode { None, Cwd, Obj }; + +LLVM_ABI SaveStatsMode getSaveStats(); + /// Create this object with static storage to register codegen-related command /// line options. struct RegisterCodeGenFlags { LLVM_ABI RegisterCodeGenFlags(); }; +/// Tools that support stats saving should create this object with static +/// storage to register the --save-stats command line option. +struct RegisterSaveStatsFlag { + LLVM_ABI RegisterSaveStatsFlag(); +}; + LLVM_ABI bool getEnableBBAddrMap(); LLVM_ABI llvm::BasicBlockSection @@ -203,6 +213,17 @@ LLVM_ABI Expected> createTargetMachineForTriple( StringRef TargetTriple, CodeGenOptLevel OptLevel = CodeGenOptLevel::Default); +/// Conditionally enables the collection of LLVM statistics during the tool run, +/// based on the value of the flag. Must be called before the tool run to +/// actually collect data. +LLVM_ABI void MaybeEnableStatistics(); + +/// Conditionally saves the collected LLVM statistics to the received output +/// file, based on the value of the flag. Should be called after the tool run, +/// and must follow a call to `MaybeEnableStatistics()` to actually have data to +/// write. +LLVM_ABI int MaybeSaveStatistics(StringRef OutputFilename, StringRef ToolName); + } // namespace codegen } // namespace llvm diff --git a/llvm/lib/CodeGen/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp index c1365f499dcf5..e7d76e138bac7 100644 --- a/llvm/lib/CodeGen/CommandFlags.cpp +++ b/llvm/lib/CodeGen/CommandFlags.cpp @@ -13,33 +13,43 @@ //===----------------------------------------------------------------------===// #include "llvm/CodeGen/CommandFlags.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Module.h" #include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include "llvm/TargetParser/Host.h" #include "llvm/TargetParser/SubtargetFeature.h" #include "llvm/TargetParser/Triple.h" +#include +#include #include +#include using namespace llvm; #define CGOPT(TY, NAME) \ static cl::opt *NAME##View; \ TY codegen::get##NAME() { \ - assert(NAME##View && "RegisterCodeGenFlags not created."); \ + assert(NAME##View && "Flag not registered."); \ return *NAME##View; \ } #define CGLIST(TY, NAME) \ static cl::list *NAME##View; \ std::vector codegen::get##NAME() { \ - assert(NAME##View && "RegisterCodeGenFlags not created."); \ + assert(NAME##View && "Flag not registered."); \ return *NAME##View; \ } @@ -110,13 +120,14 @@ CGOPT(bool, DebugStrictDwarf) CGOPT(unsigned, AlignLoops) CGOPT(bool, JMCInstrument) CGOPT(bool, XCOFFReadOnlyPointers) +CGOPT(codegen::SaveStatsMode, SaveStats) -codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() { #define CGBINDOPT(NAME) \ do { \ NAME##View = std::addressof(NAME); \ } while (0) +codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() { static cl::opt MArch( "march", cl::desc("Architecture to generate code for (see --version)")); CGBINDOPT(MArch); @@ -515,11 +526,25 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() { cl::init(false)); CGBINDOPT(DisableIntegratedAS); -#undef CGBINDOPT - mc::RegisterMCTargetOptionsFlags(); } +codegen::RegisterSaveStatsFlag::RegisterSaveStatsFlag() { + static cl::opt SaveStats( + "save-stats", + cl::desc( + "Save LLVM statistics to a file in the current directory" + "(`-save-stats`/`-save-stats=cwd`) or the directory of the output" + "file (`-save-stats=obj`). (default: cwd)"), + cl::values(clEnumValN(SaveStatsMode::Cwd, "cwd", + "Save to the current working directory"), + clEnumValN(SaveStatsMode::Cwd, "", ""), + clEnumValN(SaveStatsMode::Obj, "obj", + "Save to the output file directory")), + cl::init(SaveStatsMode::None), cl::ValueOptional); + CGBINDOPT(SaveStats); +} + llvm::BasicBlockSection codegen::getBBSectionsMode(llvm::TargetOptions &Options) { if (getBBSections() == "all") @@ -763,3 +788,42 @@ codegen::createTargetMachineForTriple(StringRef TargetTriple, TargetTriple); return std::unique_ptr(Target); } + +void codegen::MaybeEnableStatistics() { + if (getSaveStats() == SaveStatsMode::None) + return; + + llvm::EnableStatistics(false); +} + +int codegen::MaybeSaveStatistics(StringRef OutputFilename, StringRef ToolName) { + auto SaveStatsValue = getSaveStats(); + if (SaveStatsValue == codegen::SaveStatsMode::None) + return 0; + + SmallString<128> StatsFilename; + if (SaveStatsValue == codegen::SaveStatsMode::Obj) { + StatsFilename = OutputFilename; + llvm::sys::path::remove_filename(StatsFilename); + } else { + assert(SaveStatsValue == codegen::SaveStatsMode::Cwd && + "Should have been a valid --save-stats value"); + } + + auto BaseName = llvm::sys::path::filename(OutputFilename); + llvm::sys::path::append(StatsFilename, BaseName); + llvm::sys::path::replace_extension(StatsFilename, "stats"); + + auto FileFlags = llvm::sys::fs::OF_TextWithCRLF; + std::error_code EC; + auto StatsOS = + std::make_unique(StatsFilename, EC, FileFlags); + if (EC) { + WithColor::error(errs(), ToolName) + << "Unable to open statistics file: " << EC.message() << "\n"; + return 1; + } + + llvm::PrintStatisticsJSON(*StatsOS); + return 0; +} diff --git a/llvm/test/tools/llc/save-stats.ll b/llvm/test/tools/llc/save-stats.ll index a5769f86648dc..8c0c30677015a 100644 --- a/llvm/test/tools/llc/save-stats.ll +++ b/llvm/test/tools/llc/save-stats.ll @@ -1,6 +1,8 @@ ; REQUIRES: asserts +; Ensure the test runs in a temp directory. See https://github.com/llvm/llvm-project/pull/167403#event-20848739526 ; RUN: rm -rf %t.dir && mkdir -p %t.dir && cd %t.dir + ; RUN: llc --save-stats=obj -o %t.s %s && cat %t.stats | FileCheck %s ; RUN: llc --save-stats=cwd -o %t.s %s && cat %{t:stem}.tmp.stats | FileCheck %s ; RUN: llc --save-stats -o %t.s %s && cat %{t:stem}.tmp.stats | FileCheck %s diff --git a/llvm/test/tools/opt/save-stats.ll b/llvm/test/tools/opt/save-stats.ll new file mode 100644 index 0000000000000..eaaa717b261c2 --- /dev/null +++ b/llvm/test/tools/opt/save-stats.ll @@ -0,0 +1,18 @@ +; REQUIRES: asserts + +; Ensure the test runs in a temp directory. See https://github.com/llvm/llvm-project/pull/167403#event-20848739526 +; RUN: rm -rf %t.dir && mkdir -p %t.dir && cd %t.dir + +; RUN: opt -S -passes=instcombine --save-stats=obj -o %t.ll %s && cat %t.stats | FileCheck %s +; RUN: opt -S -passes=instcombine --save-stats=cwd -o %t.ll %s && cat %{t:stem}.tmp.stats | FileCheck %s +; RUN: opt -S -passes=instcombine --save-stats -o %t.ll %s && cat %{t:stem}.tmp.stats | FileCheck %s +; RUN: not opt -S --save-stats=invalid -o %t.ll %s 2>&1 | FileCheck %s --check-prefix=INVALID_ARG + +; CHECK: { +; CHECK: "instcombine.NumWorklistIterations": +; CHECK: } + +; INVALID_ARG: {{.*}}opt{{.*}}: for the --save-stats option: Cannot find option named 'invalid'! +define i32 @func() { + ret i32 0 +} diff --git a/llvm/tools/llc/llc.cpp b/llvm/tools/llc/llc.cpp index dc2f878830863..a257b351ec846 100644 --- a/llvm/tools/llc/llc.cpp +++ b/llvm/tools/llc/llc.cpp @@ -65,6 +65,7 @@ using namespace llvm; static codegen::RegisterCodeGenFlags CGF; +static codegen::RegisterSaveStatsFlag SSF; // General options for llc. Other pass-specific options are specified // within the corresponding llc passes, and target-specific options @@ -211,20 +212,6 @@ static cl::opt RemarksFormat( cl::desc("The format used for serializing remarks (default: YAML)"), cl::value_desc("format"), cl::init("yaml")); -enum SaveStatsMode { None, Cwd, Obj }; - -static cl::opt SaveStats( - "save-stats", - cl::desc("Save LLVM statistics to a file in the current directory" - "(`-save-stats`/`-save-stats=cwd`) or the directory of the output" - "file (`-save-stats=obj`). (default: cwd)"), - cl::values(clEnumValN(SaveStatsMode::Cwd, "cwd", - "Save to the current working directory"), - clEnumValN(SaveStatsMode::Cwd, "", ""), - clEnumValN(SaveStatsMode::Obj, "obj", - "Save to the output file directory")), - cl::init(SaveStatsMode::None), cl::ValueOptional); - static cl::opt EnableNewPassManager( "enable-new-pm", cl::desc("Enable the new pass manager"), cl::init(false)); @@ -377,46 +364,6 @@ static std::unique_ptr GetOutputStream(const char *TargetName, return FDOut; } - -static int MaybeEnableStats() { - if (SaveStats == SaveStatsMode::None) - return 0; - - llvm::EnableStatistics(false); - return 0; -} - -static int MaybeSaveStats(std::string &&OutputFilename) { - if (SaveStats == SaveStatsMode::None) - return 0; - - SmallString<128> StatsFilename; - if (SaveStats == SaveStatsMode::Obj) { - StatsFilename = OutputFilename; - llvm::sys::path::remove_filename(StatsFilename); - } else { - assert(SaveStats == SaveStatsMode::Cwd && - "Should have been a valid --save-stats value"); - } - - auto BaseName = llvm::sys::path::filename(OutputFilename); - llvm::sys::path::append(StatsFilename, BaseName); - llvm::sys::path::replace_extension(StatsFilename, "stats"); - - auto FileFlags = llvm::sys::fs::OF_TextWithCRLF; - std::error_code EC; - auto StatsOS = - std::make_unique(StatsFilename, EC, FileFlags); - if (EC) { - WithColor::error(errs(), "llc") - << "Unable to open statistics file: " << EC.message() << "\n"; - return 1; - } - - llvm::PrintStatisticsJSON(*StatsOS); - return 0; -} - // main - Entry point for the llc compiler. // int main(int argc, char **argv) { @@ -494,8 +441,7 @@ int main(int argc, char **argv) { reportError(std::move(E), RemarksFilename); LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr); - if (int RetVal = MaybeEnableStats()) - return RetVal; + codegen::MaybeEnableStatistics(); std::string OutputFilename; if (InputLanguage != "" && InputLanguage != "ir" && InputLanguage != "mir") @@ -510,7 +456,7 @@ int main(int argc, char **argv) { if (RemarksFile) RemarksFile->keep(); - return MaybeSaveStats(std::move(OutputFilename)); + return codegen::MaybeSaveStatistics(OutputFilename, "llc"); } static bool addPass(PassManagerBase &PM, const char *argv0, StringRef PassName, diff --git a/llvm/tools/opt/optdriver.cpp b/llvm/tools/opt/optdriver.cpp index f70db3133f69d..ef6e5412bda48 100644 --- a/llvm/tools/opt/optdriver.cpp +++ b/llvm/tools/opt/optdriver.cpp @@ -64,6 +64,7 @@ using namespace llvm; using namespace opt_tool; static codegen::RegisterCodeGenFlags CFG; +static codegen::RegisterSaveStatsFlag SSF; // The OptimizationList is automatically populated with registered Passes by the // PassNameParser. @@ -512,6 +513,8 @@ optMain(int argc, char **argv, } LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr); + codegen::MaybeEnableStatistics(); + // Load the input module... auto SetDataLayout = [&](StringRef IRTriple, StringRef IRLayout) -> std::optional { @@ -742,15 +745,15 @@ optMain(int argc, char **argv, // The user has asked to use the new pass manager and provided a pipeline // string. Hand off the rest of the functionality to the new code for that // layer. - return runPassPipeline( - argv[0], *M, TM.get(), &TLII, Out.get(), ThinLinkOut.get(), - RemarksFile.get(), Pipeline, PluginList, PassBuilderCallbacks, - OK, VK, /* ShouldPreserveAssemblyUseListOrder */ false, - /* ShouldPreserveBitcodeUseListOrder */ true, EmitSummaryIndex, - EmitModuleHash, EnableDebugify, VerifyDebugInfoPreserve, - EnableProfileVerification, UnifiedLTO) - ? 0 - : 1; + if (!runPassPipeline( + argv[0], *M, TM.get(), &TLII, Out.get(), ThinLinkOut.get(), + RemarksFile.get(), Pipeline, PluginList, PassBuilderCallbacks, OK, + VK, /* ShouldPreserveAssemblyUseListOrder */ false, + /* ShouldPreserveBitcodeUseListOrder */ true, EmitSummaryIndex, + EmitModuleHash, EnableDebugify, VerifyDebugInfoPreserve, + EnableProfileVerification, UnifiedLTO)) + return 1; + return codegen::MaybeSaveStatistics(OutputFilename, "opt"); } if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || @@ -928,5 +931,5 @@ optMain(int argc, char **argv, if (ThinLinkOut) ThinLinkOut->keep(); - return 0; + return codegen::MaybeSaveStatistics(OutputFilename, "opt"); }