Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions llvm/docs/CommandGuide/opt.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions llvm/docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions llvm/include/llvm/CodeGen/CommandFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -203,6 +213,17 @@ LLVM_ABI Expected<std::unique_ptr<TargetMachine>> 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

Expand Down
74 changes: 69 additions & 5 deletions llvm/lib/CodeGen/CommandFlags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <cassert>
#include <memory>
#include <optional>
#include <system_error>

using namespace llvm;

#define CGOPT(TY, NAME) \
static cl::opt<TY> *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<TY> *NAME##View; \
std::vector<TY> codegen::get##NAME() { \
assert(NAME##View && "RegisterCodeGenFlags not created."); \
assert(NAME##View && "Flag not registered."); \
return *NAME##View; \
}

Expand Down Expand Up @@ -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<std::string> MArch(
"march", cl::desc("Architecture to generate code for (see --version)"));
CGBINDOPT(MArch);
Expand Down Expand Up @@ -515,11 +526,25 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
cl::init(false));
CGBINDOPT(DisableIntegratedAS);

#undef CGBINDOPT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be moved after RegisterSaveStatsFlag

Copy link
Contributor Author

@tomershafir tomershafir Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I intentionally removed it because I dont think its needed anymore moving CGBINDOPT to a wider scope, becoming a redundant compile time overhead


mc::RegisterMCTargetOptionsFlags();
}

codegen::RegisterSaveStatsFlag::RegisterSaveStatsFlag() {
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);
CGBINDOPT(SaveStats);
}

llvm::BasicBlockSection
codegen::getBBSectionsMode(llvm::TargetOptions &Options) {
if (getBBSections() == "all")
Expand Down Expand Up @@ -763,3 +788,42 @@ codegen::createTargetMachineForTriple(StringRef TargetTriple,
TargetTriple);
return std::unique_ptr<TargetMachine>(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<llvm::raw_fd_ostream>(StatsFilename, EC, FileFlags);
if (EC) {
WithColor::error(errs(), ToolName)
<< "Unable to open statistics file: " << EC.message() << "\n";
return 1;
}

llvm::PrintStatisticsJSON(*StatsOS);
return 0;
}
2 changes: 2 additions & 0 deletions llvm/test/tools/llc/save-stats.ll
Original file line number Diff line number Diff line change
@@ -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
Expand Down
18 changes: 18 additions & 0 deletions llvm/test/tools/opt/save-stats.ll
Original file line number Diff line number Diff line change
@@ -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
}
60 changes: 3 additions & 57 deletions llvm/tools/llc/llc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -211,20 +212,6 @@ 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));

Expand Down Expand Up @@ -377,46 +364,6 @@ 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) {
Expand Down Expand Up @@ -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")
Expand All @@ -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,
Expand Down
23 changes: 13 additions & 10 deletions llvm/tools/opt/optdriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<std::string> {
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -928,5 +931,5 @@ optMain(int argc, char **argv,
if (ThinLinkOut)
ThinLinkOut->keep();

return 0;
return codegen::MaybeSaveStatistics(OutputFilename, "opt");
}
Loading