Skip to content

Commit 56a0157

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 5ac616f commit 56a0157

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
@@ -175,6 +175,7 @@ Changes to the LLVM tools
175175
* `llvm-readelf` now dumps all hex format values in lower-case mode.
176176
* Some code paths for supporting Python 2.7 in `llvm-lit` have been removed.
177177
* Support for `%T` in lit has been removed.
178+
* Add `--save-stats` option to `llc` to save LLVM statistics to a file. Compatible with the Clang option.
178179

179180
Changes to LLDB
180181
---------------------------------

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

@@ -276,7 +286,8 @@ static void setPGOOptions(TargetMachine &TM) {
276286
TM.setPGOOption(PGOOpt);
277287
}
278288

279-
static int compileModule(char **, LLVMContext &);
289+
static int compileModule(char **argv, LLVMContext &Context,
290+
std::string &OutputFilename);
280291

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

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

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

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

444503
if (RemarksFile)
445504
RemarksFile->keep();
446-
return 0;
505+
506+
return MaybeSaveStats(std::move(OutputFilename));
447507
}
448508

449509
static bool addPass(PassManagerBase &PM, const char *argv0, StringRef PassName,
@@ -475,7 +535,8 @@ static bool addPass(PassManagerBase &PM, const char *argv0, StringRef PassName,
475535
return false;
476536
}
477537

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

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

0 commit comments

Comments
 (0)