Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 call before the tool run to actually
/// collect data.
LLVM_ABI int MaybeEnableStatistics();

/// Conditionally saves the collected LLVM statistics to the recevied output
/// file, based on the value of the flag. Shuould be called after the tool run,
/// and must follow a call to `MaybeEnableStats()` to actually have data to
/// write.
LLVM_ABI int MaybeSaveStatistics(StringRef OutputFilename, StringRef ToolName);

} // namespace codegen
} // namespace llvm

Expand Down
75 changes: 70 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,43 @@ codegen::createTargetMachineForTriple(StringRef TargetTriple,
TargetTriple);
return std::unique_ptr<TargetMachine>(Target);
}

int codegen::MaybeEnableStatistics() {
if (getSaveStats() == SaveStatsMode::None)
return 0;

llvm::EnableStatistics(false);
return 0;
}

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
}
59 changes: 3 additions & 56 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,7 +441,7 @@ int main(int argc, char **argv) {
reportError(std::move(E), RemarksFilename);
LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr);

if (int RetVal = MaybeEnableStats())
if (int RetVal = codegen::MaybeEnableStatistics())
return RetVal;
std::string OutputFilename;

Expand All @@ -510,7 +457,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
24 changes: 14 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,9 @@ optMain(int argc, char **argv,
}
LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr);

if (int RetVal = codegen::MaybeEnableStatistics())
return RetVal;

// Load the input module...
auto SetDataLayout = [&](StringRef IRTriple,
StringRef IRLayout) -> std::optional<std::string> {
Expand Down Expand Up @@ -742,15 +746,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 +932,5 @@ optMain(int argc, char **argv,
if (ThinLinkOut)
ThinLinkOut->keep();

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