Skip to content

Commit 6d342b5

Browse files
committed
[tools][llc] Add --save-stats option
This patch adds a Clang-compatible `--save-stats` option, to provide an easy to use way to save LLVM statistics files when working with llc on the backend. Like on Clang, one can specify `--save-stats`, `--save-stats=cwd`, and `--save-stats=obj` with the same semantics and JSON format. The implementation uses 2 methods `MaybeEnableStats` and `MaybeSaveStats` called before and after `compileModule` respectively that externally own the statistics related logic, while `compileModule` is now required to return the resolved output filename via an output param. Note: like on Clang, the pre-existing `--stats` option is not affected.
1 parent 6345222 commit 6d342b5

File tree

4 files changed

+85
-4
lines changed

4 files changed

+85
-4
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/ReleaseNotes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ Changes to the LLVM tools
163163
* `llvm-readelf` now dumps all hex format values in lower-case mode.
164164
* Some code paths for supporting Python 2.7 in `llvm-lit` have been removed.
165165
* Support for `%T` in lit has been removed.
166+
* Add `--save-stats` option to `llc` to save LLVM statistics to a file. Compatible with the Clang option.
166167

167168
Changes to LLDB
168169
---------------------------------

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
; RUN: rm -f %t.stats && llc -mtriple=arm64-apple-macosx --save-stats=obj -o %t.s %s && test -f %t.stats
3+
; RUN: rm -f %{t:stem}.tmp.stats && llc -mtriple=arm64-apple-macosx --save-stats=cwd -o %t.s %s && test -f %{t:stem}.tmp.stats
4+
; RUN: rm -f %{t:stem}.tmp.stats && llc -mtriple=arm64-apple-macosx --save-stats -o %t.s %s && test -f %{t:stem}.tmp.stats
5+
; RUN: not llc -mtriple=arm64-apple-macosx --save-stats=invalid -o %t.s %s 2>&1 | FileCheck %s --check-prefix=INVALID_ARG
6+
7+
; INVALID_ARG: llc: error: Invalid --save-stats value: invalid, must be empty, 'cwd' or 'obj'
8+
define i32 @func() {
9+
ret i32 0
10+
}

llvm/tools/llc/llc.cpp

Lines changed: 68 additions & 4 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/CodeGen/CommandFlags.h"
2021
#include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
@@ -45,6 +46,7 @@
4546
#include "llvm/Support/FormattedStream.h"
4647
#include "llvm/Support/InitLLVM.h"
4748
#include "llvm/Support/PGOOptions.h"
49+
#include "llvm/Support/Path.h"
4850
#include "llvm/Support/PluginLoader.h"
4951
#include "llvm/Support/SourceMgr.h"
5052
#include "llvm/Support/TargetSelect.h"
@@ -57,6 +59,7 @@
5759
#include "llvm/TargetParser/SubtargetFeature.h"
5860
#include "llvm/TargetParser/Triple.h"
5961
#include "llvm/Transforms/Utils/Cloning.h"
62+
#include <cassert>
6063
#include <memory>
6164
#include <optional>
6265
using namespace llvm;
@@ -203,6 +206,13 @@ static cl::opt<std::string> RemarksFormat(
203206
cl::desc("The format used for serializing remarks (default: YAML)"),
204207
cl::value_desc("format"), cl::init("yaml"));
205208

209+
static cl::opt<std::string> SaveStats(
210+
"save-stats",
211+
cl::desc("Save LLVM statistics to a file in the current directory"
212+
"(`-save-stats`/`-save-stats=cwd`) or the directory of the output"
213+
"file (`-save-stats=obj`). (default: cwd)"),
214+
cl::init(""), cl::ValueOptional);
215+
206216
static cl::opt<bool> EnableNewPassManager(
207217
"enable-new-pm", cl::desc("Enable the new pass manager"), cl::init(false));
208218

@@ -277,7 +287,8 @@ static void setPGOOptions(TargetMachine &TM) {
277287
TM.setPGOOption(PGOOpt);
278288
}
279289

280-
static int compileModule(char **, LLVMContext &);
290+
static int compileModule(char **argv, LLVMContext &Context,
291+
std::string &OutputFilename);
281292

282293
[[noreturn]] static void reportError(Twine Msg, StringRef Filename = "") {
283294
SmallString<256> Prefix;
@@ -356,6 +367,50 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName,
356367
return FDOut;
357368
}
358369

370+
static int MaybeEnableStats() {
371+
if (SaveStats.getNumOccurrences() > 0) {
372+
if (SaveStats.empty() || SaveStats == "cwd" || SaveStats == "obj") {
373+
llvm::EnableStatistics(false);
374+
} else {
375+
WithColor::error(errs(), "llc")
376+
<< "Invalid --save-stats value: " << SaveStats
377+
<< ", must be empty, 'cwd' or 'obj'\n";
378+
return 1;
379+
}
380+
}
381+
return 0;
382+
}
383+
384+
static int MaybeSaveStats(std::string &&OutputFilename) {
385+
if (SaveStats.getNumOccurrences() > 0) {
386+
SmallString<128> StatsFilename;
387+
if (SaveStats == "obj") {
388+
StatsFilename = OutputFilename;
389+
llvm::sys::path::remove_filename(StatsFilename);
390+
} else {
391+
assert((SaveStats.empty() || SaveStats == "cwd") &&
392+
"Should have been a valid --save-stats value");
393+
}
394+
395+
auto BaseName = llvm::sys::path::filename(OutputFilename);
396+
llvm::sys::path::append(StatsFilename, BaseName);
397+
llvm::sys::path::replace_extension(StatsFilename, "stats");
398+
399+
auto FileFlags = llvm::sys::fs::OF_TextWithCRLF;
400+
std::error_code EC;
401+
auto StatsOS =
402+
std::make_unique<llvm::raw_fd_ostream>(StatsFilename, EC, FileFlags);
403+
if (EC) {
404+
WithColor::error(errs(), "llc")
405+
<< "Unable to open statistics file: " << EC.message() << "\n";
406+
return 1;
407+
} else {
408+
llvm::PrintStatisticsJSON(*StatsOS);
409+
}
410+
}
411+
return 0;
412+
}
413+
359414
// main - Entry point for the llc compiler.
360415
//
361416
int main(int argc, char **argv) {
@@ -433,18 +488,23 @@ int main(int argc, char **argv) {
433488
reportError(std::move(E), RemarksFilename);
434489
LLVMRemarkFileHandle RemarksFile = std::move(*RemarksFileOrErr);
435490

491+
if (int RetVal = MaybeEnableStats())
492+
return RetVal;
493+
std::string OutputFilename;
494+
436495
if (InputLanguage != "" && InputLanguage != "ir" && InputLanguage != "mir")
437496
reportError("input language must be '', 'IR' or 'MIR'");
438497

439498
// Compile the module TimeCompilations times to give better compile time
440499
// metrics.
441500
for (unsigned I = TimeCompilations; I; --I)
442-
if (int RetVal = compileModule(argv, Context))
501+
if (int RetVal = compileModule(argv, Context, OutputFilename))
443502
return RetVal;
444503

445504
if (RemarksFile)
446505
RemarksFile->keep();
447-
return 0;
506+
507+
return MaybeSaveStats(std::move(OutputFilename));
448508
}
449509

450510
static bool addPass(PassManagerBase &PM, const char *argv0, StringRef PassName,
@@ -476,7 +536,8 @@ static bool addPass(PassManagerBase &PM, const char *argv0, StringRef PassName,
476536
return false;
477537
}
478538

479-
static int compileModule(char **argv, LLVMContext &Context) {
539+
static int compileModule(char **argv, LLVMContext &Context,
540+
std::string &OutputFilename) {
480541
// Load the module to be compiled...
481542
SMDiagnostic Err;
482543
std::unique_ptr<Module> M;
@@ -660,6 +721,9 @@ static int compileModule(char **argv, LLVMContext &Context) {
660721
// Ensure the filename is passed down to CodeViewDebug.
661722
Target->Options.ObjectFilenameForDebug = Out->outputFilename();
662723

724+
// Return a copy of the output filename via the output param
725+
OutputFilename = Out->outputFilename();
726+
663727
// Tell target that this tool is not necessarily used with argument ABI
664728
// compliance (i.e. narrow integer argument extensions).
665729
Target->Options.VerifyArgABICompliance = 0;

0 commit comments

Comments
 (0)