Skip to content

Commit fca6f57

Browse files
committed
[opt] Add --save-stats option (llvm#167304)
This patch adds a Clang-compatible --save-stats option to opt, to provide an easy to use way to save LLVM statistics files when working with opt on the middle end. This is a follow up on the addition to `llc`: llvm#163967 Like on Clang, one can specify --save-stats, --save-stats=cwd, and --save-stats=obj with the same semantics and JSON format. The pre-existing --stats option is not affected. The implementation extracts the flag and its methods into the common `CodeGen/CommandFlags` as `LLVM_ABI`, using a new registration class to conservatively enable opt-in rather than let all tools take it. Its only needed for llc and opt for now. Then it refactors llc and adds support for opt. (cherry-pick 35ffe10)
1 parent bd152d5 commit fca6f57

File tree

8 files changed

+133
-70
lines changed

8 files changed

+133
-70
lines changed

llvm/docs/CommandGuide/opt.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ OPTIONS
7070

7171
Print statistics.
7272

73+
.. option:: --save-stats, --save-stats=cwd, --save-stats=obj
74+
75+
Save LLVM statistics to a file in the current directory
76+
(:option:`--save-stats`/"--save-stats=cwd") or the directory
77+
of the output file ("--save-stats=obj") in JSON format.
78+
7379
.. option:: -time-passes
7480

7581
Record the amount of time needed for each pass and print it to standard

llvm/docs/ReleaseNotes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ Changes to the LLVM tools
298298
* llvm-addr2line now supports a `+` prefix when specifying an address.
299299
* Support for `SHT_LLVM_BB_ADDR_MAP` versions 0 and 1 has been dropped.
300300
* Add `--save-stats` option to `llc` to save LLVM statistics to a file. Compatible with the Clang option.
301+
* Add `--save-stats` option to `opt` to save LLVM statistics to a file. Compatible with the Clang option.
301302

302303
Changes to LLDB
303304
---------------------------------

llvm/include/llvm/CodeGen/CommandFlags.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,22 @@ LLVM_ABI bool getJMCInstrument();
156156

157157
LLVM_ABI bool getXCOFFReadOnlyPointers();
158158

159+
enum SaveStatsMode { None, Cwd, Obj };
160+
161+
LLVM_ABI SaveStatsMode getSaveStats();
162+
159163
/// Create this object with static storage to register codegen-related command
160164
/// line options.
161165
struct RegisterCodeGenFlags {
162166
LLVM_ABI RegisterCodeGenFlags();
163167
};
164168

169+
/// Tools that support stats saving should create this object with static
170+
/// storage to register the --save-stats command line option.
171+
struct RegisterSaveStatsFlag {
172+
LLVM_ABI RegisterSaveStatsFlag();
173+
};
174+
165175
LLVM_ABI bool getEnableBBAddrMap();
166176

167177
LLVM_ABI llvm::BasicBlockSection
@@ -205,6 +215,17 @@ LLVM_ABI Expected<std::unique_ptr<TargetMachine>> createTargetMachineForTriple(
205215
StringRef TargetTriple,
206216
CodeGenOptLevel OptLevel = CodeGenOptLevel::Default);
207217

218+
/// Conditionally enables the collection of LLVM statistics during the tool run,
219+
/// based on the value of the flag. Must be called before the tool run to
220+
/// actually collect data.
221+
LLVM_ABI void MaybeEnableStatistics();
222+
223+
/// Conditionally saves the collected LLVM statistics to the received output
224+
/// file, based on the value of the flag. Should be called after the tool run,
225+
/// and must follow a call to `MaybeEnableStatistics()` to actually have data to
226+
/// write.
227+
LLVM_ABI int MaybeSaveStatistics(StringRef OutputFilename, StringRef ToolName);
228+
208229
} // namespace codegen
209230
} // namespace llvm
210231

llvm/lib/CodeGen/CommandFlags.cpp

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,43 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
#include "llvm/CodeGen/CommandFlags.h"
16+
#include "llvm/ADT/SmallString.h"
17+
#include "llvm/ADT/Statistic.h"
1618
#include "llvm/ADT/StringExtras.h"
19+
#include "llvm/ADT/StringRef.h"
1720
#include "llvm/IR/Instructions.h"
1821
#include "llvm/IR/Intrinsics.h"
1922
#include "llvm/IR/Module.h"
2023
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
2124
#include "llvm/MC/TargetRegistry.h"
2225
#include "llvm/Support/CommandLine.h"
26+
#include "llvm/Support/FileSystem.h"
2327
#include "llvm/Support/MemoryBuffer.h"
28+
#include "llvm/Support/Path.h"
29+
#include "llvm/Support/WithColor.h"
30+
#include "llvm/Support/raw_ostream.h"
2431
#include "llvm/Target/TargetMachine.h"
2532
#include "llvm/TargetParser/Host.h"
2633
#include "llvm/TargetParser/SubtargetFeature.h"
2734
#include "llvm/TargetParser/Triple.h"
35+
#include <cassert>
36+
#include <memory>
2837
#include <optional>
38+
#include <system_error>
2939

3040
using namespace llvm;
3141

3242
#define CGOPT(TY, NAME) \
3343
static cl::opt<TY> *NAME##View; \
3444
TY codegen::get##NAME() { \
35-
assert(NAME##View && "RegisterCodeGenFlags not created."); \
45+
assert(NAME##View && "Flag not registered."); \
3646
return *NAME##View; \
3747
}
3848

3949
#define CGLIST(TY, NAME) \
4050
static cl::list<TY> *NAME##View; \
4151
std::vector<TY> codegen::get##NAME() { \
42-
assert(NAME##View && "RegisterCodeGenFlags not created."); \
52+
assert(NAME##View && "Flag not registered."); \
4353
return *NAME##View; \
4454
}
4555

@@ -111,13 +121,14 @@ CGOPT(bool, DebugStrictDwarf)
111121
CGOPT(unsigned, AlignLoops)
112122
CGOPT(bool, JMCInstrument)
113123
CGOPT(bool, XCOFFReadOnlyPointers)
124+
CGOPT(codegen::SaveStatsMode, SaveStats)
114125

115-
codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
116126
#define CGBINDOPT(NAME) \
117127
do { \
118128
NAME##View = std::addressof(NAME); \
119129
} while (0)
120130

131+
codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
121132
static cl::opt<std::string> MArch(
122133
"march", cl::desc("Architecture to generate code for (see --version)"));
123134
CGBINDOPT(MArch);
@@ -523,11 +534,25 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
523534
cl::init(false));
524535
CGBINDOPT(DisableIntegratedAS);
525536

526-
#undef CGBINDOPT
527-
528537
mc::RegisterMCTargetOptionsFlags();
529538
}
530539

540+
codegen::RegisterSaveStatsFlag::RegisterSaveStatsFlag() {
541+
static cl::opt<SaveStatsMode> SaveStats(
542+
"save-stats",
543+
cl::desc(
544+
"Save LLVM statistics to a file in the current directory"
545+
"(`-save-stats`/`-save-stats=cwd`) or the directory of the output"
546+
"file (`-save-stats=obj`). (default: cwd)"),
547+
cl::values(clEnumValN(SaveStatsMode::Cwd, "cwd",
548+
"Save to the current working directory"),
549+
clEnumValN(SaveStatsMode::Cwd, "", ""),
550+
clEnumValN(SaveStatsMode::Obj, "obj",
551+
"Save to the output file directory")),
552+
cl::init(SaveStatsMode::None), cl::ValueOptional);
553+
CGBINDOPT(SaveStats);
554+
}
555+
531556
llvm::BasicBlockSection
532557
codegen::getBBSectionsMode(llvm::TargetOptions &Options) {
533558
if (getBBSections() == "all")
@@ -774,3 +799,42 @@ codegen::createTargetMachineForTriple(StringRef TargetTriple,
774799
TargetTriple);
775800
return std::unique_ptr<TargetMachine>(Target);
776801
}
802+
803+
void codegen::MaybeEnableStatistics() {
804+
if (getSaveStats() == SaveStatsMode::None)
805+
return;
806+
807+
llvm::EnableStatistics(false);
808+
}
809+
810+
int codegen::MaybeSaveStatistics(StringRef OutputFilename, StringRef ToolName) {
811+
auto SaveStatsValue = getSaveStats();
812+
if (SaveStatsValue == codegen::SaveStatsMode::None)
813+
return 0;
814+
815+
SmallString<128> StatsFilename;
816+
if (SaveStatsValue == codegen::SaveStatsMode::Obj) {
817+
StatsFilename = OutputFilename;
818+
llvm::sys::path::remove_filename(StatsFilename);
819+
} else {
820+
assert(SaveStatsValue == codegen::SaveStatsMode::Cwd &&
821+
"Should have been a valid --save-stats value");
822+
}
823+
824+
auto BaseName = llvm::sys::path::filename(OutputFilename);
825+
llvm::sys::path::append(StatsFilename, BaseName);
826+
llvm::sys::path::replace_extension(StatsFilename, "stats");
827+
828+
auto FileFlags = llvm::sys::fs::OF_TextWithCRLF;
829+
std::error_code EC;
830+
auto StatsOS =
831+
std::make_unique<llvm::raw_fd_ostream>(StatsFilename, EC, FileFlags);
832+
if (EC) {
833+
WithColor::error(errs(), ToolName)
834+
<< "Unable to open statistics file: " << EC.message() << "\n";
835+
return 1;
836+
}
837+
838+
llvm::PrintStatisticsJSON(*StatsOS);
839+
return 0;
840+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
; REQUIRES: asserts
22

3+
; Ensure the test runs in a temp directory. See https://github.com/llvm/llvm-project/pull/167403#event-20848739526
4+
; RUN: rm -rf %t.dir && mkdir -p %t.dir && cd %t.dir
5+
36
; RUN: llc --save-stats=obj -o %t.s %s && cat %t.stats | FileCheck %s
47
; RUN: llc --save-stats=cwd -o %t.s %s && cat %{t:stem}.tmp.stats | FileCheck %s
58
; RUN: llc --save-stats -o %t.s %s && cat %{t:stem}.tmp.stats | FileCheck %s

llvm/test/tools/opt/save-stats.ll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
; REQUIRES: asserts
2+
3+
; Ensure the test runs in a temp directory. See https://github.com/llvm/llvm-project/pull/167403#event-20848739526
4+
; RUN: rm -rf %t.dir && mkdir -p %t.dir && cd %t.dir
5+
6+
; RUN: opt -S -passes=instcombine --save-stats=obj -o %t.ll %s && cat %t.stats | FileCheck %s
7+
; RUN: opt -S -passes=instcombine --save-stats=cwd -o %t.ll %s && cat %{t:stem}.tmp.stats | FileCheck %s
8+
; RUN: opt -S -passes=instcombine --save-stats -o %t.ll %s && cat %{t:stem}.tmp.stats | FileCheck %s
9+
; RUN: not opt -S --save-stats=invalid -o %t.ll %s 2>&1 | FileCheck %s --check-prefix=INVALID_ARG
10+
11+
; CHECK: {
12+
; CHECK: "instcombine.NumWorklistIterations":
13+
; CHECK: }
14+
15+
; INVALID_ARG: {{.*}}opt{{.*}}: for the --save-stats option: Cannot find option named 'invalid'!
16+
define i32 @func() {
17+
ret i32 0
18+
}

llvm/tools/llc/llc.cpp

Lines changed: 3 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
using namespace llvm;
6767

6868
static codegen::RegisterCodeGenFlags CGF;
69+
static codegen::RegisterSaveStatsFlag SSF;
6970

7071
// General options for llc. Other pass-specific options are specified
7172
// within the corresponding llc passes, and target-specific options
@@ -203,20 +204,6 @@ static cl::opt<std::string> RemarksFormat(
203204
cl::desc("The format used for serializing remarks (default: YAML)"),
204205
cl::value_desc("format"), cl::init("yaml"));
205206

206-
enum SaveStatsMode { None, Cwd, Obj };
207-
208-
static cl::opt<SaveStatsMode> SaveStats(
209-
"save-stats",
210-
cl::desc("Save LLVM statistics to a file in the current directory"
211-
"(`-save-stats`/`-save-stats=cwd`) or the directory of the output"
212-
"file (`-save-stats=obj`). (default: cwd)"),
213-
cl::values(clEnumValN(SaveStatsMode::Cwd, "cwd",
214-
"Save to the current working directory"),
215-
clEnumValN(SaveStatsMode::Cwd, "", ""),
216-
clEnumValN(SaveStatsMode::Obj, "obj",
217-
"Save to the output file directory")),
218-
cl::init(SaveStatsMode::None), cl::ValueOptional);
219-
220207
static cl::opt<bool> EnableNewPassManager(
221208
"enable-new-pm", cl::desc("Enable the new pass manager"), cl::init(false));
222209

@@ -391,45 +378,6 @@ static void verifyCASOptions(const Triple &TheTriple) {
391378
}
392379
// END MCCAS
393380

394-
static int MaybeEnableStats() {
395-
if (SaveStats == SaveStatsMode::None)
396-
return 0;
397-
398-
llvm::EnableStatistics(false);
399-
return 0;
400-
}
401-
402-
static int MaybeSaveStats(std::string &&OutputFilename) {
403-
if (SaveStats == SaveStatsMode::None)
404-
return 0;
405-
406-
SmallString<128> StatsFilename;
407-
if (SaveStats == SaveStatsMode::Obj) {
408-
StatsFilename = OutputFilename;
409-
llvm::sys::path::remove_filename(StatsFilename);
410-
} else {
411-
assert(SaveStats == SaveStatsMode::Cwd &&
412-
"Should have been a valid --save-stats value");
413-
}
414-
415-
auto BaseName = llvm::sys::path::filename(OutputFilename);
416-
llvm::sys::path::append(StatsFilename, BaseName);
417-
llvm::sys::path::replace_extension(StatsFilename, "stats");
418-
419-
auto FileFlags = llvm::sys::fs::OF_TextWithCRLF;
420-
std::error_code EC;
421-
auto StatsOS =
422-
std::make_unique<llvm::raw_fd_ostream>(StatsFilename, EC, FileFlags);
423-
if (EC) {
424-
WithColor::error(errs(), "llc")
425-
<< "Unable to open statistics file: " << EC.message() << "\n";
426-
return 1;
427-
}
428-
429-
llvm::PrintStatisticsJSON(*StatsOS);
430-
return 0;
431-
}
432-
433381
// main - Entry point for the llc compiler.
434382
//
435383
int main(int argc, char **argv) {
@@ -507,8 +455,7 @@ int main(int argc, char **argv) {
507455
reportError(std::move(E), RemarksFilename);
508456
std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr);
509457

510-
if (int RetVal = MaybeEnableStats())
511-
return RetVal;
458+
codegen::MaybeEnableStatistics();
512459
std::string OutputFilename;
513460

514461
if (InputLanguage != "" && InputLanguage != "ir" && InputLanguage != "mir")
@@ -523,7 +470,7 @@ int main(int argc, char **argv) {
523470
if (RemarksFile)
524471
RemarksFile->keep();
525472

526-
return MaybeSaveStats(std::move(OutputFilename));
473+
return codegen::MaybeSaveStatistics(OutputFilename, "llc");
527474
}
528475

529476
static bool addPass(PassManagerBase &PM, const char *argv0,

llvm/tools/opt/optdriver.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ using namespace llvm;
6464
using namespace opt_tool;
6565

6666
static codegen::RegisterCodeGenFlags CFG;
67+
static codegen::RegisterSaveStatsFlag SSF;
6768

6869
// The OptimizationList is automatically populated with registered Passes by the
6970
// PassNameParser.
@@ -511,6 +512,8 @@ extern "C" int optMain(
511512
}
512513
std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr);
513514

515+
codegen::MaybeEnableStatistics();
516+
514517
// Load the input module...
515518
auto SetDataLayout = [&](StringRef IRTriple,
516519
StringRef IRLayout) -> std::optional<std::string> {
@@ -741,14 +744,14 @@ extern "C" int optMain(
741744
// The user has asked to use the new pass manager and provided a pipeline
742745
// string. Hand off the rest of the functionality to the new code for that
743746
// layer.
744-
return runPassPipeline(
745-
argv[0], *M, TM.get(), &TLII, Out.get(), ThinLinkOut.get(),
746-
RemarksFile.get(), Pipeline, PluginList, PassBuilderCallbacks,
747-
OK, VK, PreserveAssemblyUseListOrder,
748-
PreserveBitcodeUseListOrder, EmitSummaryIndex, EmitModuleHash,
749-
EnableDebugify, VerifyDebugInfoPreserve, UnifiedLTO)
750-
? 0
751-
: 1;
747+
if (!runPassPipeline(
748+
argv[0], *M, TM.get(), &TLII, Out.get(), ThinLinkOut.get(),
749+
RemarksFile.get(), Pipeline, PluginList, PassBuilderCallbacks, OK,
750+
VK, PreserveAssemblyUseListOrder, PreserveBitcodeUseListOrder,
751+
EmitSummaryIndex, EmitModuleHash, EnableDebugify,
752+
VerifyDebugInfoPreserve, UnifiedLTO))
753+
return 1;
754+
return codegen::MaybeSaveStatistics(OutputFilename, "opt");
752755
}
753756

754757
if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz ||
@@ -924,5 +927,5 @@ extern "C" int optMain(
924927
if (ThinLinkOut)
925928
ThinLinkOut->keep();
926929

927-
return 0;
930+
return codegen::MaybeSaveStatistics(OutputFilename, "opt");
928931
}

0 commit comments

Comments
 (0)