Skip to content

Commit 35ffe10

Browse files
authored
[opt] Add --save-stats option (#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`: #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.
1 parent 56eef98 commit 35ffe10

File tree

8 files changed

+133
-72
lines changed

8 files changed

+133
-72
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
@@ -190,6 +190,7 @@ Changes to the LLVM tools
190190
* Some code paths for supporting Python 2.7 in `llvm-lit` have been removed.
191191
* Support for `%T` in lit has been removed.
192192
* Add `--save-stats` option to `llc` to save LLVM statistics to a file. Compatible with the Clang option.
193+
* Add `--save-stats` option to `opt` to save LLVM statistics to a file. Compatible with the Clang option.
193194

194195
* `llvm-config` gained a new flag `--quote-paths` which quotes and escapes paths
195196
emitted on stdout, to account for spaces or other special characters in path.

llvm/include/llvm/CodeGen/CommandFlags.h

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

155155
LLVM_ABI bool getXCOFFReadOnlyPointers();
156156

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

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

165175
LLVM_ABI llvm::BasicBlockSection
@@ -203,6 +213,17 @@ LLVM_ABI Expected<std::unique_ptr<TargetMachine>> createTargetMachineForTriple(
203213
StringRef TargetTriple,
204214
CodeGenOptLevel OptLevel = CodeGenOptLevel::Default);
205215

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

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

@@ -110,13 +120,14 @@ CGOPT(bool, DebugStrictDwarf)
110120
CGOPT(unsigned, AlignLoops)
111121
CGOPT(bool, JMCInstrument)
112122
CGOPT(bool, XCOFFReadOnlyPointers)
123+
CGOPT(codegen::SaveStatsMode, SaveStats)
113124

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

130+
codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
120131
static cl::opt<std::string> MArch(
121132
"march", cl::desc("Architecture to generate code for (see --version)"));
122133
CGBINDOPT(MArch);
@@ -518,11 +529,25 @@ codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() {
518529
cl::init(false));
519530
CGBINDOPT(DisableIntegratedAS);
520531

521-
#undef CGBINDOPT
522-
523532
mc::RegisterMCTargetOptionsFlags();
524533
}
525534

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

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +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
34
; RUN: rm -rf %t.dir && mkdir -p %t.dir && cd %t.dir
5+
46
; RUN: llc --save-stats=obj -o %t.s %s && cat %t.stats | FileCheck %s
57
; RUN: llc --save-stats=cwd -o %t.s %s && cat %{t:stem}.tmp.stats | FileCheck %s
68
; 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 & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
using namespace llvm;
6666

6767
static codegen::RegisterCodeGenFlags CGF;
68+
static codegen::RegisterSaveStatsFlag SSF;
6869

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

214-
enum SaveStatsMode { None, Cwd, Obj };
215-
216-
static cl::opt<SaveStatsMode> SaveStats(
217-
"save-stats",
218-
cl::desc("Save LLVM statistics to a file in the current directory"
219-
"(`-save-stats`/`-save-stats=cwd`) or the directory of the output"
220-
"file (`-save-stats=obj`). (default: cwd)"),
221-
cl::values(clEnumValN(SaveStatsMode::Cwd, "cwd",
222-
"Save to the current working directory"),
223-
clEnumValN(SaveStatsMode::Cwd, "", ""),
224-
clEnumValN(SaveStatsMode::Obj, "obj",
225-
"Save to the output file directory")),
226-
cl::init(SaveStatsMode::None), cl::ValueOptional);
227-
228215
static cl::opt<bool> EnableNewPassManager(
229216
"enable-new-pm", cl::desc("Enable the new pass manager"), cl::init(false));
230217

@@ -377,46 +364,6 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName,
377364

378365
return FDOut;
379366
}
380-
381-
static int MaybeEnableStats() {
382-
if (SaveStats == SaveStatsMode::None)
383-
return 0;
384-
385-
llvm::EnableStatistics(false);
386-
return 0;
387-
}
388-
389-
static int MaybeSaveStats(std::string &&OutputFilename) {
390-
if (SaveStats == SaveStatsMode::None)
391-
return 0;
392-
393-
SmallString<128> StatsFilename;
394-
if (SaveStats == SaveStatsMode::Obj) {
395-
StatsFilename = OutputFilename;
396-
llvm::sys::path::remove_filename(StatsFilename);
397-
} else {
398-
assert(SaveStats == SaveStatsMode::Cwd &&
399-
"Should have been a valid --save-stats value");
400-
}
401-
402-
auto BaseName = llvm::sys::path::filename(OutputFilename);
403-
llvm::sys::path::append(StatsFilename, BaseName);
404-
llvm::sys::path::replace_extension(StatsFilename, "stats");
405-
406-
auto FileFlags = llvm::sys::fs::OF_TextWithCRLF;
407-
std::error_code EC;
408-
auto StatsOS =
409-
std::make_unique<llvm::raw_fd_ostream>(StatsFilename, EC, FileFlags);
410-
if (EC) {
411-
WithColor::error(errs(), "llc")
412-
<< "Unable to open statistics file: " << EC.message() << "\n";
413-
return 1;
414-
}
415-
416-
llvm::PrintStatisticsJSON(*StatsOS);
417-
return 0;
418-
}
419-
420367
// main - Entry point for the llc compiler.
421368
//
422369
int main(int argc, char **argv) {
@@ -494,8 +441,7 @@ int main(int argc, char **argv) {
494441
reportError(std::move(E), RemarksFilename);
495442
LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr);
496443

497-
if (int RetVal = MaybeEnableStats())
498-
return RetVal;
444+
codegen::MaybeEnableStatistics();
499445
std::string OutputFilename;
500446

501447
if (InputLanguage != "" && InputLanguage != "ir" && InputLanguage != "mir")
@@ -510,7 +456,7 @@ int main(int argc, char **argv) {
510456
if (RemarksFile)
511457
RemarksFile->keep();
512458

513-
return MaybeSaveStats(std::move(OutputFilename));
459+
return codegen::MaybeSaveStatistics(OutputFilename, "llc");
514460
}
515461

516462
static bool addPass(PassManagerBase &PM, const char *argv0, StringRef PassName,

llvm/tools/opt/optdriver.cpp

Lines changed: 13 additions & 10 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.
@@ -512,6 +513,8 @@ optMain(int argc, char **argv,
512513
}
513514
LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr);
514515

516+
codegen::MaybeEnableStatistics();
517+
515518
// Load the input module...
516519
auto SetDataLayout = [&](StringRef IRTriple,
517520
StringRef IRLayout) -> std::optional<std::string> {
@@ -742,15 +745,15 @@ optMain(int argc, char **argv,
742745
// The user has asked to use the new pass manager and provided a pipeline
743746
// string. Hand off the rest of the functionality to the new code for that
744747
// layer.
745-
return runPassPipeline(
746-
argv[0], *M, TM.get(), &TLII, Out.get(), ThinLinkOut.get(),
747-
RemarksFile.get(), Pipeline, PluginList, PassBuilderCallbacks,
748-
OK, VK, /* ShouldPreserveAssemblyUseListOrder */ false,
749-
/* ShouldPreserveBitcodeUseListOrder */ true, EmitSummaryIndex,
750-
EmitModuleHash, EnableDebugify, VerifyDebugInfoPreserve,
751-
EnableProfileVerification, UnifiedLTO)
752-
? 0
753-
: 1;
748+
if (!runPassPipeline(
749+
argv[0], *M, TM.get(), &TLII, Out.get(), ThinLinkOut.get(),
750+
RemarksFile.get(), Pipeline, PluginList, PassBuilderCallbacks, OK,
751+
VK, /* ShouldPreserveAssemblyUseListOrder */ false,
752+
/* ShouldPreserveBitcodeUseListOrder */ true, EmitSummaryIndex,
753+
EmitModuleHash, EnableDebugify, VerifyDebugInfoPreserve,
754+
EnableProfileVerification, UnifiedLTO))
755+
return 1;
756+
return codegen::MaybeSaveStatistics(OutputFilename, "opt");
754757
}
755758

756759
if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz ||
@@ -928,5 +931,5 @@ optMain(int argc, char **argv,
928931
if (ThinLinkOut)
929932
ThinLinkOut->keep();
930933

931-
return 0;
934+
return codegen::MaybeSaveStatistics(OutputFilename, "opt");
932935
}

0 commit comments

Comments
 (0)