[llvm] 96a5289 - [tools][llc] Add `--save-stats` option (#163967)

via llvm-commits llvm-commits at lists.llvm.org
Sun Nov 9 04:11:58 PST 2025


Author: Tomer Shafir
Date: 2025-11-09T14:11:53+02:00
New Revision: 96a52893ca401f0ac46a67549f608d526b3f3dc8

URL: https://github.com/llvm/llvm-project/commit/96a52893ca401f0ac46a67549f608d526b3f3dc8
DIFF: https://github.com/llvm/llvm-project/commit/96a52893ca401f0ac46a67549f608d526b3f3dc8.diff

LOG: [tools][llc] Add `--save-stats` option (#163967)

This patch adds a Clang-compatible `--save-stats` option, to provide an
easy to use way to save LLVM statistics files when working with llc on
the backend.

Like on Clang, one can specify `--save-stats`, `--save-stats=cwd`, and
`--save-stats=obj` with the same semantics and JSON format.

The implementation uses 2 methods `MaybeEnableStats` and
`MaybeSaveStats` called before and after `compileModule` respectively
that externally own the statistics related logic, while `compileModule`
is now required to return the resolved output filename via an output
param.

Note: like on Clang, the pre-existing `--stats` option is not affected.

Added: 
    llvm/test/tools/llc/save-stats.ll

Modified: 
    llvm/docs/CommandGuide/llc.rst
    llvm/docs/ReleaseNotes.md
    llvm/tools/llc/llc.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/llc.rst b/llvm/docs/CommandGuide/llc.rst
index cc670f6043656..ffcccfbaefffb 100644
--- a/llvm/docs/CommandGuide/llc.rst
+++ b/llvm/docs/CommandGuide/llc.rst
@@ -129,6 +129,12 @@ End-user Options
 
  Print statistics recorded by code-generation passes.
 
+.. 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 a report to standard

diff  --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 23bba99ec874f..fd78c97c86d24 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -182,6 +182,7 @@ Changes to the LLVM tools
 * `llvm-readelf` now dumps all hex format values in lower-case mode.
 * 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.
 
 * `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/test/tools/llc/save-stats.ll b/llvm/test/tools/llc/save-stats.ll
new file mode 100644
index 0000000000000..94b17e54d9559
--- /dev/null
+++ b/llvm/test/tools/llc/save-stats.ll
@@ -0,0 +1,15 @@
+; REQUIRES: asserts
+
+; RUN: llc -mtriple=arm64-apple-macosx --save-stats=obj -o %t.s %s && cat %t.stats | FileCheck %s
+; RUN: llc -mtriple=arm64-apple-macosx --save-stats=cwd -o %t.s %s && cat %{t:stem}.tmp.stats | FileCheck %s
+; RUN: llc -mtriple=arm64-apple-macosx --save-stats -o %t.s %s && cat %{t:stem}.tmp.stats | FileCheck %s
+; RUN: not llc -mtriple=arm64-apple-macosx --save-stats=invalid -o %t.s %s 2>&1 | FileCheck %s --check-prefix=INVALID_ARG
+
+; CHECK: {
+; CHECK: "asm-printer.EmittedInsts":
+; CHECK: }
+
+; INVALID_ARG: {{.*}}llc{{.*}}: 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 152f7db0719a1..dc2f878830863 100644
--- a/llvm/tools/llc/llc.cpp
+++ b/llvm/tools/llc/llc.cpp
@@ -15,6 +15,7 @@
 #include "NewPMDriver.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
@@ -45,6 +46,7 @@
 #include "llvm/Support/FormattedStream.h"
 #include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/PGOOptions.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/PluginLoader.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/TargetSelect.h"
@@ -57,6 +59,7 @@
 #include "llvm/TargetParser/SubtargetFeature.h"
 #include "llvm/TargetParser/Triple.h"
 #include "llvm/Transforms/Utils/Cloning.h"
+#include <cassert>
 #include <memory>
 #include <optional>
 using namespace llvm;
@@ -208,6 +211,20 @@ static cl::opt<std::string> 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<SaveStatsMode> 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<bool> EnableNewPassManager(
     "enable-new-pm", cl::desc("Enable the new pass manager"), cl::init(false));
 
@@ -281,7 +298,8 @@ static void setPGOOptions(TargetMachine &TM) {
     TM.setPGOOption(PGOOpt);
 }
 
-static int compileModule(char **, LLVMContext &);
+static int compileModule(char **argv, LLVMContext &Context,
+                         std::string &OutputFilename);
 
 [[noreturn]] static void reportError(Twine Msg, StringRef Filename = "") {
   SmallString<256> Prefix;
@@ -360,6 +378,45 @@ static std::unique_ptr<ToolOutputFile> 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<llvm::raw_fd_ostream>(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) {
@@ -437,18 +494,23 @@ int main(int argc, char **argv) {
     reportError(std::move(E), RemarksFilename);
   LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr);
 
+  if (int RetVal = MaybeEnableStats())
+    return RetVal;
+  std::string OutputFilename;
+
   if (InputLanguage != "" && InputLanguage != "ir" && InputLanguage != "mir")
     reportError("input language must be '', 'IR' or 'MIR'");
 
   // Compile the module TimeCompilations times to give better compile time
   // metrics.
   for (unsigned I = TimeCompilations; I; --I)
-    if (int RetVal = compileModule(argv, Context))
+    if (int RetVal = compileModule(argv, Context, OutputFilename))
       return RetVal;
 
   if (RemarksFile)
     RemarksFile->keep();
-  return 0;
+
+  return MaybeSaveStats(std::move(OutputFilename));
 }
 
 static bool addPass(PassManagerBase &PM, const char *argv0, StringRef PassName,
@@ -480,7 +542,8 @@ static bool addPass(PassManagerBase &PM, const char *argv0, StringRef PassName,
   return false;
 }
 
-static int compileModule(char **argv, LLVMContext &Context) {
+static int compileModule(char **argv, LLVMContext &Context,
+                         std::string &OutputFilename) {
   // Load the module to be compiled...
   SMDiagnostic Err;
   std::unique_ptr<Module> M;
@@ -664,6 +727,9 @@ static int compileModule(char **argv, LLVMContext &Context) {
   // Ensure the filename is passed down to CodeViewDebug.
   Target->Options.ObjectFilenameForDebug = Out->outputFilename();
 
+  // Return a copy of the output filename via the output param
+  OutputFilename = Out->outputFilename();
+
   // Tell target that this tool is not necessarily used with argument ABI
   // compliance (i.e. narrow integer argument extensions).
   Target->Options.VerifyArgABICompliance = 0;


        


More information about the llvm-commits mailing list