Skip to content

Commit 52a2b5f

Browse files
authored
Merge pull request swiftlang#11901 from swiftlang/cherrypick-save-stats
Cherrypick save stats
2 parents 19eb84f + ec3f364 commit 52a2b5f

File tree

9 files changed

+173
-24
lines changed

9 files changed

+173
-24
lines changed

llvm/docs/CommandGuide/llc.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ End-user Options
136136

137137
Print statistics recorded by code-generation passes.
138138

139+
.. option:: --save-stats, --save-stats=cwd, --save-stats=obj
140+
141+
Save LLVM statistics to a file in the current directory
142+
(:option:`--save-stats`/"--save-stats=cwd") or the directory
143+
of the output file ("--save-stats=obj") in JSON format.
144+
139145
.. option:: --time-passes
140146

141147
Record the amount of time needed for each pass and print a report to standard

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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ Changes to the LLVM tools
297297
([#47468](https://github.com/llvm/llvm-project/issues/47468))
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.
300+
* 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.
300302

301303
Changes to LLDB
302304
---------------------------------

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: 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: llc --save-stats=obj -o %t.s %s && cat %t.stats | FileCheck %s
7+
; RUN: llc --save-stats=cwd -o %t.s %s && cat %{t:stem}.tmp.stats | FileCheck %s
8+
; RUN: llc --save-stats -o %t.s %s && cat %{t:stem}.tmp.stats | FileCheck %s
9+
; RUN: not llc --save-stats=invalid -o %t.s %s 2>&1 | FileCheck %s --check-prefix=INVALID_ARG
10+
11+
; CHECK: {
12+
; CHECK: "asm-printer.EmittedInsts":
13+
; CHECK: }
14+
15+
; INVALID_ARG: {{.*}}llc{{.*}}: for the --save-stats option: Cannot find option named 'invalid'!
16+
define i32 @func() {
17+
ret i32 0
18+
}

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: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "NewPMDriver.h"
1616
#include "llvm/ADT/STLExtras.h"
1717
#include "llvm/ADT/ScopeExit.h"
18+
#include "llvm/ADT/Statistic.h"
1819
#include "llvm/Analysis/TargetLibraryInfo.h"
1920
#include "llvm/CAS/ObjectStore.h"
2021
#include "llvm/CodeGen/CommandFlags.h"
@@ -45,6 +46,7 @@
4546
#include "llvm/Support/FileSystem.h"
4647
#include "llvm/Support/FormattedStream.h"
4748
#include "llvm/Support/InitLLVM.h"
49+
#include "llvm/Support/Path.h"
4850
#include "llvm/Support/PluginLoader.h"
4951
#include "llvm/Support/Process.h"
5052
#include "llvm/Support/SourceMgr.h"
@@ -58,11 +60,13 @@
5860
#include "llvm/TargetParser/SubtargetFeature.h"
5961
#include "llvm/TargetParser/Triple.h"
6062
#include "llvm/Transforms/Utils/Cloning.h"
63+
#include <cassert>
6164
#include <memory>
6265
#include <optional>
6366
using namespace llvm;
6467

6568
static codegen::RegisterCodeGenFlags CGF;
69+
static codegen::RegisterSaveStatsFlag SSF;
6670

6771
// General options for llc. Other pass-specific options are specified
6872
// within the corresponding llc passes, and target-specific options
@@ -262,7 +266,8 @@ static cl::opt<RunPassOption, true, cl::parser<std::string>> RunPass(
262266
cl::desc("Run compiler only for specified passes (comma separated list)"),
263267
cl::value_desc("pass-name"), cl::location(RunPassOpt));
264268

265-
static int compileModule(char **, LLVMContext &);
269+
static int compileModule(char **argv, LLVMContext &Context,
270+
std::string &OutputFilename);
266271

267272
[[noreturn]] static void reportError(Twine Msg, StringRef Filename = "") {
268273
SmallString<256> Prefix;
@@ -282,9 +287,7 @@ static int compileModule(char **, LLVMContext &);
282287
llvm_unreachable("reportError() should not return");
283288
}
284289

285-
static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName,
286-
Triple::OSType OS,
287-
const char *ProgName) {
290+
static std::unique_ptr<ToolOutputFile> GetOutputStream(Triple::OSType OS) {
288291
// If we don't yet have an output filename, make one.
289292
if (OutputFilename.empty()) {
290293
if (InputFilename == "-")
@@ -450,18 +453,22 @@ int main(int argc, char **argv) {
450453
reportError(std::move(E), RemarksFilename);
451454
std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr);
452455

456+
codegen::MaybeEnableStatistics();
457+
std::string OutputFilename;
458+
453459
if (InputLanguage != "" && InputLanguage != "ir" && InputLanguage != "mir")
454460
reportError("input language must be '', 'IR' or 'MIR'");
455461

456462
// Compile the module TimeCompilations times to give better compile time
457463
// metrics.
458464
for (unsigned I = TimeCompilations; I; --I)
459-
if (int RetVal = compileModule(argv, Context))
465+
if (int RetVal = compileModule(argv, Context, OutputFilename))
460466
return RetVal;
461467

462468
if (RemarksFile)
463469
RemarksFile->keep();
464-
return 0;
470+
471+
return codegen::MaybeSaveStatistics(OutputFilename, "llc");
465472
}
466473

467474
static bool addPass(PassManagerBase &PM, const char *argv0,
@@ -493,7 +500,8 @@ static bool addPass(PassManagerBase &PM, const char *argv0,
493500
return false;
494501
}
495502

496-
static int compileModule(char **argv, LLVMContext &Context) {
503+
static int compileModule(char **argv, LLVMContext &Context,
504+
std::string &OutputFilename) {
497505
// Load the module to be compiled...
498506
SMDiagnostic Err;
499507
std::unique_ptr<Module> M;
@@ -671,13 +679,16 @@ static int compileModule(char **argv, LLVMContext &Context) {
671679
Target->Options.FloatABIType = codegen::getFloatABIForCalls();
672680

673681
// Figure out where we are going to send the output.
674-
std::unique_ptr<ToolOutputFile> Out =
675-
GetOutputStream(TheTarget->getName(), TheTriple.getOS(), argv[0]);
676-
if (!Out) return 1;
682+
std::unique_ptr<ToolOutputFile> Out = GetOutputStream(TheTriple.getOS());
683+
if (!Out)
684+
return 1;
677685

678686
// Ensure the filename is passed down to CodeViewDebug.
679687
Target->Options.ObjectFilenameForDebug = Out->outputFilename();
680688

689+
// Return a copy of the output filename via the output param
690+
OutputFilename = Out->outputFilename();
691+
681692
// Tell target that this tool is not necessarily used with argument ABI
682693
// compliance (i.e. narrow integer argument extensions).
683694
Target->Options.VerifyArgABICompliance = 0;

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)