xref: /openbsd-src/gnu/llvm/llvm/tools/llvm-mca/llvm-mca.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
109467b48Spatrick //===-- llvm-mca.cpp - Machine Code Analyzer -------------------*- C++ -* -===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // This utility is a simple driver that allows static performance analysis on
1009467b48Spatrick // machine code similarly to how IACA (Intel Architecture Code Analyzer) works.
1109467b48Spatrick //
1209467b48Spatrick //   llvm-mca [options] <file-name>
1309467b48Spatrick //      -march <type>
1409467b48Spatrick //      -mcpu <cpu>
1509467b48Spatrick //      -o <file>
1609467b48Spatrick //
1709467b48Spatrick // The target defaults to the host target.
1809467b48Spatrick // The cpu defaults to the 'native' host cpu.
1909467b48Spatrick // The output defaults to standard output.
2009467b48Spatrick //
2109467b48Spatrick //===----------------------------------------------------------------------===//
2209467b48Spatrick 
2309467b48Spatrick #include "CodeRegion.h"
2409467b48Spatrick #include "CodeRegionGenerator.h"
2509467b48Spatrick #include "PipelinePrinter.h"
2609467b48Spatrick #include "Views/BottleneckAnalysis.h"
2709467b48Spatrick #include "Views/DispatchStatistics.h"
2809467b48Spatrick #include "Views/InstructionInfoView.h"
2909467b48Spatrick #include "Views/RegisterFileStatistics.h"
3009467b48Spatrick #include "Views/ResourcePressureView.h"
3109467b48Spatrick #include "Views/RetireControlUnitStatistics.h"
3209467b48Spatrick #include "Views/SchedulerStatistics.h"
3309467b48Spatrick #include "Views/SummaryView.h"
3409467b48Spatrick #include "Views/TimelineView.h"
3509467b48Spatrick #include "llvm/MC/MCAsmBackend.h"
3609467b48Spatrick #include "llvm/MC/MCAsmInfo.h"
3709467b48Spatrick #include "llvm/MC/MCCodeEmitter.h"
3809467b48Spatrick #include "llvm/MC/MCContext.h"
3909467b48Spatrick #include "llvm/MC/MCObjectFileInfo.h"
4009467b48Spatrick #include "llvm/MC/MCRegisterInfo.h"
4109467b48Spatrick #include "llvm/MC/MCSubtargetInfo.h"
42097a140dSpatrick #include "llvm/MC/MCTargetOptionsCommandFlags.h"
43*d415bd75Srobert #include "llvm/MC/TargetRegistry.h"
4409467b48Spatrick #include "llvm/MCA/CodeEmitter.h"
4509467b48Spatrick #include "llvm/MCA/Context.h"
4673471bf0Spatrick #include "llvm/MCA/CustomBehaviour.h"
4709467b48Spatrick #include "llvm/MCA/InstrBuilder.h"
4809467b48Spatrick #include "llvm/MCA/Pipeline.h"
4909467b48Spatrick #include "llvm/MCA/Stages/EntryStage.h"
5009467b48Spatrick #include "llvm/MCA/Stages/InstructionTables.h"
5109467b48Spatrick #include "llvm/MCA/Support.h"
5209467b48Spatrick #include "llvm/Support/CommandLine.h"
5309467b48Spatrick #include "llvm/Support/ErrorHandling.h"
5409467b48Spatrick #include "llvm/Support/ErrorOr.h"
5509467b48Spatrick #include "llvm/Support/FileSystem.h"
5609467b48Spatrick #include "llvm/Support/Host.h"
5709467b48Spatrick #include "llvm/Support/InitLLVM.h"
5809467b48Spatrick #include "llvm/Support/MemoryBuffer.h"
5909467b48Spatrick #include "llvm/Support/SourceMgr.h"
6009467b48Spatrick #include "llvm/Support/TargetSelect.h"
6109467b48Spatrick #include "llvm/Support/ToolOutputFile.h"
6209467b48Spatrick #include "llvm/Support/WithColor.h"
6309467b48Spatrick 
6409467b48Spatrick using namespace llvm;
6509467b48Spatrick 
66097a140dSpatrick static mc::RegisterMCTargetOptionsFlags MOF;
67097a140dSpatrick 
6809467b48Spatrick static cl::OptionCategory ToolOptions("Tool Options");
6909467b48Spatrick static cl::OptionCategory ViewOptions("View Options");
7009467b48Spatrick 
7109467b48Spatrick static cl::opt<std::string> InputFilename(cl::Positional,
7209467b48Spatrick                                           cl::desc("<input file>"),
7309467b48Spatrick                                           cl::cat(ToolOptions), cl::init("-"));
7409467b48Spatrick 
7509467b48Spatrick static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
7609467b48Spatrick                                            cl::init("-"), cl::cat(ToolOptions),
7709467b48Spatrick                                            cl::value_desc("filename"));
7809467b48Spatrick 
7909467b48Spatrick static cl::opt<std::string>
8009467b48Spatrick     ArchName("march",
8109467b48Spatrick              cl::desc("Target architecture. "
8209467b48Spatrick                       "See -version for available targets"),
8309467b48Spatrick              cl::cat(ToolOptions));
8409467b48Spatrick 
8509467b48Spatrick static cl::opt<std::string>
8609467b48Spatrick     TripleName("mtriple",
8709467b48Spatrick                cl::desc("Target triple. See -version for available targets"),
8809467b48Spatrick                cl::cat(ToolOptions));
8909467b48Spatrick 
9009467b48Spatrick static cl::opt<std::string>
9109467b48Spatrick     MCPU("mcpu",
9209467b48Spatrick          cl::desc("Target a specific cpu type (-mcpu=help for details)"),
9309467b48Spatrick          cl::value_desc("cpu-name"), cl::cat(ToolOptions), cl::init("native"));
9409467b48Spatrick 
95*d415bd75Srobert static cl::list<std::string>
96*d415bd75Srobert     MATTRS("mattr", cl::CommaSeparated,
97*d415bd75Srobert            cl::desc("Target specific attributes (-mattr=help for details)"),
98*d415bd75Srobert            cl::value_desc("a1,+a2,-a3,..."), cl::cat(ToolOptions));
9909467b48Spatrick 
10073471bf0Spatrick static cl::opt<bool> PrintJson("json",
10173471bf0Spatrick                                cl::desc("Print the output in json format"),
10273471bf0Spatrick                                cl::cat(ToolOptions), cl::init(false));
10373471bf0Spatrick 
10409467b48Spatrick static cl::opt<int>
10509467b48Spatrick     OutputAsmVariant("output-asm-variant",
10609467b48Spatrick                      cl::desc("Syntax variant to use for output printing"),
10709467b48Spatrick                      cl::cat(ToolOptions), cl::init(-1));
10809467b48Spatrick 
10909467b48Spatrick static cl::opt<bool>
11009467b48Spatrick     PrintImmHex("print-imm-hex", cl::cat(ToolOptions), cl::init(false),
11109467b48Spatrick                 cl::desc("Prefer hex format when printing immediate values"));
11209467b48Spatrick 
11309467b48Spatrick static cl::opt<unsigned> Iterations("iterations",
11409467b48Spatrick                                     cl::desc("Number of iterations to run"),
11509467b48Spatrick                                     cl::cat(ToolOptions), cl::init(0));
11609467b48Spatrick 
11709467b48Spatrick static cl::opt<unsigned>
11809467b48Spatrick     DispatchWidth("dispatch", cl::desc("Override the processor dispatch width"),
11909467b48Spatrick                   cl::cat(ToolOptions), cl::init(0));
12009467b48Spatrick 
12109467b48Spatrick static cl::opt<unsigned>
12209467b48Spatrick     RegisterFileSize("register-file-size",
12309467b48Spatrick                      cl::desc("Maximum number of physical registers which can "
12409467b48Spatrick                               "be used for register mappings"),
12509467b48Spatrick                      cl::cat(ToolOptions), cl::init(0));
12609467b48Spatrick 
12709467b48Spatrick static cl::opt<unsigned>
12809467b48Spatrick     MicroOpQueue("micro-op-queue-size", cl::Hidden,
12909467b48Spatrick                  cl::desc("Number of entries in the micro-op queue"),
13009467b48Spatrick                  cl::cat(ToolOptions), cl::init(0));
13109467b48Spatrick 
13209467b48Spatrick static cl::opt<unsigned>
13309467b48Spatrick     DecoderThroughput("decoder-throughput", cl::Hidden,
13409467b48Spatrick                       cl::desc("Maximum throughput from the decoders "
13509467b48Spatrick                                "(instructions per cycle)"),
13609467b48Spatrick                       cl::cat(ToolOptions), cl::init(0));
13709467b48Spatrick 
13809467b48Spatrick static cl::opt<bool>
13909467b48Spatrick     PrintRegisterFileStats("register-file-stats",
14009467b48Spatrick                            cl::desc("Print register file statistics"),
14109467b48Spatrick                            cl::cat(ViewOptions), cl::init(false));
14209467b48Spatrick 
14309467b48Spatrick static cl::opt<bool> PrintDispatchStats("dispatch-stats",
14409467b48Spatrick                                         cl::desc("Print dispatch statistics"),
14509467b48Spatrick                                         cl::cat(ViewOptions), cl::init(false));
14609467b48Spatrick 
14709467b48Spatrick static cl::opt<bool>
14809467b48Spatrick     PrintSummaryView("summary-view", cl::Hidden,
14909467b48Spatrick                      cl::desc("Print summary view (enabled by default)"),
15009467b48Spatrick                      cl::cat(ViewOptions), cl::init(true));
15109467b48Spatrick 
15209467b48Spatrick static cl::opt<bool> PrintSchedulerStats("scheduler-stats",
15309467b48Spatrick                                          cl::desc("Print scheduler statistics"),
15409467b48Spatrick                                          cl::cat(ViewOptions), cl::init(false));
15509467b48Spatrick 
15609467b48Spatrick static cl::opt<bool>
15709467b48Spatrick     PrintRetireStats("retire-stats",
15809467b48Spatrick                      cl::desc("Print retire control unit statistics"),
15909467b48Spatrick                      cl::cat(ViewOptions), cl::init(false));
16009467b48Spatrick 
16109467b48Spatrick static cl::opt<bool> PrintResourcePressureView(
16209467b48Spatrick     "resource-pressure",
16309467b48Spatrick     cl::desc("Print the resource pressure view (enabled by default)"),
16409467b48Spatrick     cl::cat(ViewOptions), cl::init(true));
16509467b48Spatrick 
16609467b48Spatrick static cl::opt<bool> PrintTimelineView("timeline",
16709467b48Spatrick                                        cl::desc("Print the timeline view"),
16809467b48Spatrick                                        cl::cat(ViewOptions), cl::init(false));
16909467b48Spatrick 
17009467b48Spatrick static cl::opt<unsigned> TimelineMaxIterations(
17109467b48Spatrick     "timeline-max-iterations",
17209467b48Spatrick     cl::desc("Maximum number of iterations to print in timeline view"),
17309467b48Spatrick     cl::cat(ViewOptions), cl::init(0));
17409467b48Spatrick 
17573471bf0Spatrick static cl::opt<unsigned>
17673471bf0Spatrick     TimelineMaxCycles("timeline-max-cycles",
17773471bf0Spatrick                       cl::desc("Maximum number of cycles in the timeline view, "
17873471bf0Spatrick                                "or 0 for unlimited. Defaults to 80 cycles"),
17909467b48Spatrick                       cl::cat(ViewOptions), cl::init(80));
18009467b48Spatrick 
18109467b48Spatrick static cl::opt<bool>
18209467b48Spatrick     AssumeNoAlias("noalias",
18309467b48Spatrick                   cl::desc("If set, assume that loads and stores do not alias"),
18409467b48Spatrick                   cl::cat(ToolOptions), cl::init(true));
18509467b48Spatrick 
18609467b48Spatrick static cl::opt<unsigned> LoadQueueSize("lqueue",
18709467b48Spatrick                                        cl::desc("Size of the load queue"),
18809467b48Spatrick                                        cl::cat(ToolOptions), cl::init(0));
18909467b48Spatrick 
19009467b48Spatrick static cl::opt<unsigned> StoreQueueSize("squeue",
19109467b48Spatrick                                         cl::desc("Size of the store queue"),
19209467b48Spatrick                                         cl::cat(ToolOptions), cl::init(0));
19309467b48Spatrick 
19409467b48Spatrick static cl::opt<bool>
19509467b48Spatrick     PrintInstructionTables("instruction-tables",
19609467b48Spatrick                            cl::desc("Print instruction tables"),
19709467b48Spatrick                            cl::cat(ToolOptions), cl::init(false));
19809467b48Spatrick 
19909467b48Spatrick static cl::opt<bool> PrintInstructionInfoView(
20009467b48Spatrick     "instruction-info",
20109467b48Spatrick     cl::desc("Print the instruction info view (enabled by default)"),
20209467b48Spatrick     cl::cat(ViewOptions), cl::init(true));
20309467b48Spatrick 
20409467b48Spatrick static cl::opt<bool> EnableAllStats("all-stats",
20509467b48Spatrick                                     cl::desc("Print all hardware statistics"),
20609467b48Spatrick                                     cl::cat(ViewOptions), cl::init(false));
20709467b48Spatrick 
20809467b48Spatrick static cl::opt<bool>
20909467b48Spatrick     EnableAllViews("all-views",
21009467b48Spatrick                    cl::desc("Print all views including hardware statistics"),
21109467b48Spatrick                    cl::cat(ViewOptions), cl::init(false));
21209467b48Spatrick 
21309467b48Spatrick static cl::opt<bool> EnableBottleneckAnalysis(
21409467b48Spatrick     "bottleneck-analysis",
21509467b48Spatrick     cl::desc("Enable bottleneck analysis (disabled by default)"),
21609467b48Spatrick     cl::cat(ViewOptions), cl::init(false));
21709467b48Spatrick 
21809467b48Spatrick static cl::opt<bool> ShowEncoding(
21909467b48Spatrick     "show-encoding",
22009467b48Spatrick     cl::desc("Print encoding information in the instruction info view"),
22109467b48Spatrick     cl::cat(ViewOptions), cl::init(false));
22209467b48Spatrick 
223*d415bd75Srobert static cl::opt<bool> ShowBarriers(
224*d415bd75Srobert     "show-barriers",
225*d415bd75Srobert     cl::desc("Print memory barrier information in the instruction info view"),
226*d415bd75Srobert     cl::cat(ViewOptions), cl::init(false));
227*d415bd75Srobert 
22873471bf0Spatrick static cl::opt<bool> DisableCustomBehaviour(
22973471bf0Spatrick     "disable-cb",
23073471bf0Spatrick     cl::desc(
23173471bf0Spatrick         "Disable custom behaviour (use the default class which does nothing)."),
23273471bf0Spatrick     cl::cat(ViewOptions), cl::init(false));
23373471bf0Spatrick 
234*d415bd75Srobert static cl::opt<bool> DisableInstrumentManager(
235*d415bd75Srobert     "disable-im",
236*d415bd75Srobert     cl::desc("Disable instrumentation manager (use the default class which "
237*d415bd75Srobert              "ignores instruments.)."),
238*d415bd75Srobert     cl::cat(ViewOptions), cl::init(false));
239*d415bd75Srobert 
24009467b48Spatrick namespace {
24109467b48Spatrick 
getTarget(const char * ProgName)24209467b48Spatrick const Target *getTarget(const char *ProgName) {
24309467b48Spatrick   if (TripleName.empty())
24409467b48Spatrick     TripleName = Triple::normalize(sys::getDefaultTargetTriple());
24509467b48Spatrick   Triple TheTriple(TripleName);
24609467b48Spatrick 
24709467b48Spatrick   // Get the target specific parser.
24809467b48Spatrick   std::string Error;
24909467b48Spatrick   const Target *TheTarget =
25009467b48Spatrick       TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
25109467b48Spatrick   if (!TheTarget) {
25209467b48Spatrick     errs() << ProgName << ": " << Error;
25309467b48Spatrick     return nullptr;
25409467b48Spatrick   }
25509467b48Spatrick 
25673471bf0Spatrick   // Update TripleName with the updated triple from the target lookup.
25773471bf0Spatrick   TripleName = TheTriple.str();
25873471bf0Spatrick 
25909467b48Spatrick   // Return the found target.
26009467b48Spatrick   return TheTarget;
26109467b48Spatrick }
26209467b48Spatrick 
getOutputStream()26309467b48Spatrick ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
26409467b48Spatrick   if (OutputFilename == "")
26509467b48Spatrick     OutputFilename = "-";
26609467b48Spatrick   std::error_code EC;
26773471bf0Spatrick   auto Out = std::make_unique<ToolOutputFile>(OutputFilename, EC,
26873471bf0Spatrick                                               sys::fs::OF_TextWithCRLF);
26909467b48Spatrick   if (!EC)
27009467b48Spatrick     return std::move(Out);
27109467b48Spatrick   return EC;
27209467b48Spatrick }
27309467b48Spatrick } // end of anonymous namespace
27409467b48Spatrick 
processOptionImpl(cl::opt<bool> & O,const cl::opt<bool> & Default)27509467b48Spatrick static void processOptionImpl(cl::opt<bool> &O, const cl::opt<bool> &Default) {
27609467b48Spatrick   if (!O.getNumOccurrences() || O.getPosition() < Default.getPosition())
27709467b48Spatrick     O = Default.getValue();
27809467b48Spatrick }
27909467b48Spatrick 
processViewOptions(bool IsOutOfOrder)28073471bf0Spatrick static void processViewOptions(bool IsOutOfOrder) {
28109467b48Spatrick   if (!EnableAllViews.getNumOccurrences() &&
28209467b48Spatrick       !EnableAllStats.getNumOccurrences())
28309467b48Spatrick     return;
28409467b48Spatrick 
28509467b48Spatrick   if (EnableAllViews.getNumOccurrences()) {
28609467b48Spatrick     processOptionImpl(PrintSummaryView, EnableAllViews);
28773471bf0Spatrick     if (IsOutOfOrder)
28809467b48Spatrick       processOptionImpl(EnableBottleneckAnalysis, EnableAllViews);
28909467b48Spatrick     processOptionImpl(PrintResourcePressureView, EnableAllViews);
29009467b48Spatrick     processOptionImpl(PrintTimelineView, EnableAllViews);
29109467b48Spatrick     processOptionImpl(PrintInstructionInfoView, EnableAllViews);
29209467b48Spatrick   }
29309467b48Spatrick 
29409467b48Spatrick   const cl::opt<bool> &Default =
29509467b48Spatrick       EnableAllViews.getPosition() < EnableAllStats.getPosition()
29609467b48Spatrick           ? EnableAllStats
29709467b48Spatrick           : EnableAllViews;
29809467b48Spatrick   processOptionImpl(PrintRegisterFileStats, Default);
29909467b48Spatrick   processOptionImpl(PrintDispatchStats, Default);
30009467b48Spatrick   processOptionImpl(PrintSchedulerStats, Default);
30173471bf0Spatrick   if (IsOutOfOrder)
30209467b48Spatrick     processOptionImpl(PrintRetireStats, Default);
30309467b48Spatrick }
30409467b48Spatrick 
30509467b48Spatrick // Returns true on success.
runPipeline(mca::Pipeline & P)30609467b48Spatrick static bool runPipeline(mca::Pipeline &P) {
30709467b48Spatrick   // Handle pipeline errors here.
30809467b48Spatrick   Expected<unsigned> Cycles = P.run();
30909467b48Spatrick   if (!Cycles) {
31009467b48Spatrick     WithColor::error() << toString(Cycles.takeError());
31109467b48Spatrick     return false;
31209467b48Spatrick   }
31309467b48Spatrick   return true;
31409467b48Spatrick }
31509467b48Spatrick 
main(int argc,char ** argv)31609467b48Spatrick int main(int argc, char **argv) {
31709467b48Spatrick   InitLLVM X(argc, argv);
31809467b48Spatrick 
31909467b48Spatrick   // Initialize targets and assembly parsers.
32009467b48Spatrick   InitializeAllTargetInfos();
32109467b48Spatrick   InitializeAllTargetMCs();
32209467b48Spatrick   InitializeAllAsmParsers();
323*d415bd75Srobert   InitializeAllTargetMCAs();
324*d415bd75Srobert 
325*d415bd75Srobert   // Register the Target and CPU printer for --version.
326*d415bd75Srobert   cl::AddExtraVersionPrinter(sys::printDefaultTargetAndDetectedCPU);
32709467b48Spatrick 
32809467b48Spatrick   // Enable printing of available targets when flag --version is specified.
32909467b48Spatrick   cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
33009467b48Spatrick 
33109467b48Spatrick   cl::HideUnrelatedOptions({&ToolOptions, &ViewOptions});
33209467b48Spatrick 
33309467b48Spatrick   // Parse flags and initialize target options.
33409467b48Spatrick   cl::ParseCommandLineOptions(argc, argv,
33509467b48Spatrick                               "llvm machine code performance analyzer.\n");
33609467b48Spatrick 
33709467b48Spatrick   // Get the target from the triple. If a triple is not specified, then select
33809467b48Spatrick   // the default triple for the host. If the triple doesn't correspond to any
33909467b48Spatrick   // registered target, then exit with an error message.
34009467b48Spatrick   const char *ProgName = argv[0];
34109467b48Spatrick   const Target *TheTarget = getTarget(ProgName);
34209467b48Spatrick   if (!TheTarget)
34309467b48Spatrick     return 1;
34409467b48Spatrick 
34509467b48Spatrick   // GetTarget() may replaced TripleName with a default triple.
34609467b48Spatrick   // For safety, reconstruct the Triple object.
34709467b48Spatrick   Triple TheTriple(TripleName);
34809467b48Spatrick 
34909467b48Spatrick   ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
35009467b48Spatrick       MemoryBuffer::getFileOrSTDIN(InputFilename);
35109467b48Spatrick   if (std::error_code EC = BufferPtr.getError()) {
35209467b48Spatrick     WithColor::error() << InputFilename << ": " << EC.message() << '\n';
35309467b48Spatrick     return 1;
35409467b48Spatrick   }
35509467b48Spatrick 
35673471bf0Spatrick   if (MCPU == "native")
357097a140dSpatrick     MCPU = std::string(llvm::sys::getHostCPUName());
35809467b48Spatrick 
359*d415bd75Srobert   // Package up features to be passed to target/subtarget
360*d415bd75Srobert   std::string FeaturesStr;
361*d415bd75Srobert   if (MATTRS.size()) {
362*d415bd75Srobert     SubtargetFeatures Features;
363*d415bd75Srobert     for (std::string &MAttr : MATTRS)
364*d415bd75Srobert       Features.AddFeature(MAttr);
365*d415bd75Srobert     FeaturesStr = Features.getString();
366*d415bd75Srobert   }
367*d415bd75Srobert 
36809467b48Spatrick   std::unique_ptr<MCSubtargetInfo> STI(
369*d415bd75Srobert       TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr));
37073471bf0Spatrick   assert(STI && "Unable to create subtarget info!");
37109467b48Spatrick   if (!STI->isCPUStringValid(MCPU))
37209467b48Spatrick     return 1;
37309467b48Spatrick 
37409467b48Spatrick   if (!STI->getSchedModel().hasInstrSchedModel()) {
37509467b48Spatrick     WithColor::error()
37609467b48Spatrick         << "unable to find instruction-level scheduling information for"
37709467b48Spatrick         << " target triple '" << TheTriple.normalize() << "' and cpu '" << MCPU
37809467b48Spatrick         << "'.\n";
37909467b48Spatrick 
38009467b48Spatrick     if (STI->getSchedModel().InstrItineraries)
38109467b48Spatrick       WithColor::note()
38209467b48Spatrick           << "cpu '" << MCPU << "' provides itineraries. However, "
38309467b48Spatrick           << "instruction itineraries are currently unsupported.\n";
38409467b48Spatrick     return 1;
38509467b48Spatrick   }
38609467b48Spatrick 
38773471bf0Spatrick   // Apply overrides to llvm-mca specific options.
388*d415bd75Srobert   bool IsOutOfOrder = STI->getSchedModel().isOutOfOrder();
38973471bf0Spatrick   processViewOptions(IsOutOfOrder);
39073471bf0Spatrick 
39109467b48Spatrick   std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
39209467b48Spatrick   assert(MRI && "Unable to create target register info!");
39309467b48Spatrick 
394097a140dSpatrick   MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags();
39509467b48Spatrick   std::unique_ptr<MCAsmInfo> MAI(
39609467b48Spatrick       TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
39709467b48Spatrick   assert(MAI && "Unable to create target asm info!");
39809467b48Spatrick 
39909467b48Spatrick   SourceMgr SrcMgr;
40009467b48Spatrick 
40109467b48Spatrick   // Tell SrcMgr about this buffer, which is what the parser will pick up.
40209467b48Spatrick   SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc());
40309467b48Spatrick 
40409467b48Spatrick   std::unique_ptr<buffer_ostream> BOS;
40509467b48Spatrick 
40609467b48Spatrick   std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
40773471bf0Spatrick   assert(MCII && "Unable to create instruction info!");
40809467b48Spatrick 
40909467b48Spatrick   std::unique_ptr<MCInstrAnalysis> MCIA(
41009467b48Spatrick       TheTarget->createMCInstrAnalysis(MCII.get()));
41109467b48Spatrick 
41273471bf0Spatrick   // Need to initialize an MCInstPrinter as it is
41373471bf0Spatrick   // required for initializing the MCTargetStreamer
414*d415bd75Srobert   // which needs to happen within the CRG.parseAnalysisRegions() call below.
41573471bf0Spatrick   // Without an MCTargetStreamer, certain assembly directives can trigger a
41673471bf0Spatrick   // segfault. (For example, the .cv_fpo_proc directive on x86 will segfault if
41773471bf0Spatrick   // we don't initialize the MCTargetStreamer.)
41873471bf0Spatrick   unsigned IPtempOutputAsmVariant =
41973471bf0Spatrick       OutputAsmVariant == -1 ? 0 : OutputAsmVariant;
42073471bf0Spatrick   std::unique_ptr<MCInstPrinter> IPtemp(TheTarget->createMCInstPrinter(
42173471bf0Spatrick       Triple(TripleName), IPtempOutputAsmVariant, *MAI, *MCII, *MRI));
42273471bf0Spatrick   if (!IPtemp) {
42373471bf0Spatrick     WithColor::error()
42473471bf0Spatrick         << "unable to create instruction printer for target triple '"
42573471bf0Spatrick         << TheTriple.normalize() << "' with assembly variant "
42673471bf0Spatrick         << IPtempOutputAsmVariant << ".\n";
42773471bf0Spatrick     return 1;
42873471bf0Spatrick   }
42973471bf0Spatrick 
43009467b48Spatrick   // Parse the input and create CodeRegions that llvm-mca can analyze.
431*d415bd75Srobert   MCContext ACtx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr);
432*d415bd75Srobert   std::unique_ptr<MCObjectFileInfo> AMOFI(
433*d415bd75Srobert       TheTarget->createMCObjectFileInfo(ACtx, /*PIC=*/false));
434*d415bd75Srobert   ACtx.setObjectFileInfo(AMOFI.get());
435*d415bd75Srobert   mca::AsmAnalysisRegionGenerator CRG(*TheTarget, SrcMgr, ACtx, *MAI, *STI,
436*d415bd75Srobert                                       *MCII);
437*d415bd75Srobert   Expected<const mca::AnalysisRegions &> RegionsOrErr =
438*d415bd75Srobert       CRG.parseAnalysisRegions(std::move(IPtemp));
43909467b48Spatrick   if (!RegionsOrErr) {
44009467b48Spatrick     if (auto Err =
44109467b48Spatrick             handleErrors(RegionsOrErr.takeError(), [](const StringError &E) {
44209467b48Spatrick               WithColor::error() << E.getMessage() << '\n';
44309467b48Spatrick             })) {
44409467b48Spatrick       // Default case.
44509467b48Spatrick       WithColor::error() << toString(std::move(Err)) << '\n';
44609467b48Spatrick     }
44709467b48Spatrick     return 1;
44809467b48Spatrick   }
449*d415bd75Srobert   const mca::AnalysisRegions &Regions = *RegionsOrErr;
45009467b48Spatrick 
45109467b48Spatrick   // Early exit if errors were found by the code region parsing logic.
45209467b48Spatrick   if (!Regions.isValid())
45309467b48Spatrick     return 1;
45409467b48Spatrick 
45509467b48Spatrick   if (Regions.empty()) {
45609467b48Spatrick     WithColor::error() << "no assembly instructions found.\n";
45709467b48Spatrick     return 1;
45809467b48Spatrick   }
45909467b48Spatrick 
460*d415bd75Srobert   std::unique_ptr<mca::InstrumentManager> IM;
461*d415bd75Srobert   if (!DisableInstrumentManager) {
462*d415bd75Srobert     IM = std::unique_ptr<mca::InstrumentManager>(
463*d415bd75Srobert         TheTarget->createInstrumentManager(*STI, *MCII));
464*d415bd75Srobert   }
465*d415bd75Srobert   if (!IM) {
466*d415bd75Srobert     // If the target doesn't have its own IM implemented (or the -disable-cb
467*d415bd75Srobert     // flag is set) then we use the base class (which does nothing).
468*d415bd75Srobert     IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
469*d415bd75Srobert   }
470*d415bd75Srobert 
471*d415bd75Srobert   // Parse the input and create InstrumentRegion that llvm-mca
472*d415bd75Srobert   // can use to improve analysis.
473*d415bd75Srobert   MCContext ICtx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr);
474*d415bd75Srobert   std::unique_ptr<MCObjectFileInfo> IMOFI(
475*d415bd75Srobert       TheTarget->createMCObjectFileInfo(ICtx, /*PIC=*/false));
476*d415bd75Srobert   ICtx.setObjectFileInfo(IMOFI.get());
477*d415bd75Srobert   mca::AsmInstrumentRegionGenerator IRG(*TheTarget, SrcMgr, ICtx, *MAI, *STI,
478*d415bd75Srobert                                         *MCII, *IM);
479*d415bd75Srobert   Expected<const mca::InstrumentRegions &> InstrumentRegionsOrErr =
480*d415bd75Srobert       IRG.parseInstrumentRegions(std::move(IPtemp));
481*d415bd75Srobert   if (!InstrumentRegionsOrErr) {
482*d415bd75Srobert     if (auto Err = handleErrors(InstrumentRegionsOrErr.takeError(),
483*d415bd75Srobert                                 [](const StringError &E) {
484*d415bd75Srobert                                   WithColor::error() << E.getMessage() << '\n';
485*d415bd75Srobert                                 })) {
486*d415bd75Srobert       // Default case.
487*d415bd75Srobert       WithColor::error() << toString(std::move(Err)) << '\n';
488*d415bd75Srobert     }
489*d415bd75Srobert     return 1;
490*d415bd75Srobert   }
491*d415bd75Srobert   const mca::InstrumentRegions &InstrumentRegions = *InstrumentRegionsOrErr;
492*d415bd75Srobert 
493*d415bd75Srobert   // Early exit if errors were found by the instrumentation parsing logic.
494*d415bd75Srobert   if (!InstrumentRegions.isValid())
495*d415bd75Srobert     return 1;
496*d415bd75Srobert 
49709467b48Spatrick   // Now initialize the output file.
49809467b48Spatrick   auto OF = getOutputStream();
49909467b48Spatrick   if (std::error_code EC = OF.getError()) {
50009467b48Spatrick     WithColor::error() << EC.message() << '\n';
50109467b48Spatrick     return 1;
50209467b48Spatrick   }
50309467b48Spatrick 
50409467b48Spatrick   unsigned AssemblerDialect = CRG.getAssemblerDialect();
50509467b48Spatrick   if (OutputAsmVariant >= 0)
50609467b48Spatrick     AssemblerDialect = static_cast<unsigned>(OutputAsmVariant);
50709467b48Spatrick   std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
50809467b48Spatrick       Triple(TripleName), AssemblerDialect, *MAI, *MCII, *MRI));
50909467b48Spatrick   if (!IP) {
51009467b48Spatrick     WithColor::error()
51109467b48Spatrick         << "unable to create instruction printer for target triple '"
51209467b48Spatrick         << TheTriple.normalize() << "' with assembly variant "
51309467b48Spatrick         << AssemblerDialect << ".\n";
51409467b48Spatrick     return 1;
51509467b48Spatrick   }
51609467b48Spatrick 
51709467b48Spatrick   // Set the display preference for hex vs. decimal immediates.
51809467b48Spatrick   IP->setPrintImmHex(PrintImmHex);
51909467b48Spatrick 
52009467b48Spatrick   std::unique_ptr<ToolOutputFile> TOF = std::move(*OF);
52109467b48Spatrick 
52209467b48Spatrick   const MCSchedModel &SM = STI->getSchedModel();
52309467b48Spatrick 
524*d415bd75Srobert   std::unique_ptr<mca::InstrPostProcess> IPP;
525*d415bd75Srobert   if (!DisableCustomBehaviour) {
526*d415bd75Srobert     // TODO: It may be a good idea to separate CB and IPP so that they can
527*d415bd75Srobert     // be used independently of each other. What I mean by this is to add
528*d415bd75Srobert     // an extra command-line arg --disable-ipp so that CB and IPP can be
529*d415bd75Srobert     // toggled without needing to toggle both of them together.
530*d415bd75Srobert     IPP = std::unique_ptr<mca::InstrPostProcess>(
531*d415bd75Srobert         TheTarget->createInstrPostProcess(*STI, *MCII));
532*d415bd75Srobert   }
533*d415bd75Srobert   if (!IPP) {
534*d415bd75Srobert     // If the target doesn't have its own IPP implemented (or the -disable-cb
535*d415bd75Srobert     // flag is set) then we use the base class (which does nothing).
536*d415bd75Srobert     IPP = std::make_unique<mca::InstrPostProcess>(*STI, *MCII);
537*d415bd75Srobert   }
538*d415bd75Srobert 
53909467b48Spatrick   // Create an instruction builder.
540*d415bd75Srobert   mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM);
54109467b48Spatrick 
54209467b48Spatrick   // Create a context to control ownership of the pipeline hardware.
54309467b48Spatrick   mca::Context MCA(*MRI, *STI);
54409467b48Spatrick 
54509467b48Spatrick   mca::PipelineOptions PO(MicroOpQueue, DecoderThroughput, DispatchWidth,
54609467b48Spatrick                           RegisterFileSize, LoadQueueSize, StoreQueueSize,
54709467b48Spatrick                           AssumeNoAlias, EnableBottleneckAnalysis);
54809467b48Spatrick 
54909467b48Spatrick   // Number each region in the sequence.
55009467b48Spatrick   unsigned RegionIdx = 0;
55109467b48Spatrick 
55209467b48Spatrick   std::unique_ptr<MCCodeEmitter> MCE(
553*d415bd75Srobert       TheTarget->createMCCodeEmitter(*MCII, ACtx));
55473471bf0Spatrick   assert(MCE && "Unable to create code emitter!");
55509467b48Spatrick 
55609467b48Spatrick   std::unique_ptr<MCAsmBackend> MAB(TheTarget->createMCAsmBackend(
557097a140dSpatrick       *STI, *MRI, mc::InitMCTargetOptionsFromFlags()));
55873471bf0Spatrick   assert(MAB && "Unable to create asm backend!");
55909467b48Spatrick 
56073471bf0Spatrick   json::Object JSONOutput;
561*d415bd75Srobert   for (const std::unique_ptr<mca::AnalysisRegion> &Region : Regions) {
56209467b48Spatrick     // Skip empty code regions.
56309467b48Spatrick     if (Region->empty())
56409467b48Spatrick       continue;
56509467b48Spatrick 
56673471bf0Spatrick     IB.clear();
56709467b48Spatrick 
56809467b48Spatrick     // Lower the MCInst sequence into an mca::Instruction sequence.
56909467b48Spatrick     ArrayRef<MCInst> Insts = Region->getInstructions();
57009467b48Spatrick     mca::CodeEmitter CE(*STI, *MAB, *MCE, Insts);
571*d415bd75Srobert 
572*d415bd75Srobert     IPP->resetState();
573*d415bd75Srobert 
574*d415bd75Srobert     SmallVector<std::unique_ptr<mca::Instruction>> LoweredSequence;
57509467b48Spatrick     for (const MCInst &MCI : Insts) {
576*d415bd75Srobert       SMLoc Loc = MCI.getLoc();
577*d415bd75Srobert       const SmallVector<mca::SharedInstrument> Instruments =
578*d415bd75Srobert           InstrumentRegions.getActiveInstruments(Loc);
579*d415bd75Srobert 
58009467b48Spatrick       Expected<std::unique_ptr<mca::Instruction>> Inst =
581*d415bd75Srobert           IB.createInstruction(MCI, Instruments);
58209467b48Spatrick       if (!Inst) {
58309467b48Spatrick         if (auto NewE = handleErrors(
58409467b48Spatrick                 Inst.takeError(),
58509467b48Spatrick                 [&IP, &STI](const mca::InstructionError<MCInst> &IE) {
58609467b48Spatrick                   std::string InstructionStr;
58709467b48Spatrick                   raw_string_ostream SS(InstructionStr);
58809467b48Spatrick                   WithColor::error() << IE.Message << '\n';
58909467b48Spatrick                   IP->printInst(&IE.Inst, 0, "", *STI, SS);
59009467b48Spatrick                   SS.flush();
59109467b48Spatrick                   WithColor::note()
59209467b48Spatrick                       << "instruction: " << InstructionStr << '\n';
59309467b48Spatrick                 })) {
59409467b48Spatrick           // Default case.
59509467b48Spatrick           WithColor::error() << toString(std::move(NewE));
59609467b48Spatrick         }
59709467b48Spatrick         return 1;
59809467b48Spatrick       }
59909467b48Spatrick 
60073471bf0Spatrick       IPP->postProcessInstruction(Inst.get(), MCI);
60173471bf0Spatrick 
60209467b48Spatrick       LoweredSequence.emplace_back(std::move(Inst.get()));
60309467b48Spatrick     }
60409467b48Spatrick 
605*d415bd75Srobert     mca::CircularSourceMgr S(LoweredSequence,
606*d415bd75Srobert                              PrintInstructionTables ? 1 : Iterations);
60709467b48Spatrick 
60809467b48Spatrick     if (PrintInstructionTables) {
60909467b48Spatrick       //  Create a pipeline, stages, and a printer.
61009467b48Spatrick       auto P = std::make_unique<mca::Pipeline>();
61109467b48Spatrick       P->appendStage(std::make_unique<mca::EntryStage>(S));
61209467b48Spatrick       P->appendStage(std::make_unique<mca::InstructionTables>(SM));
61373471bf0Spatrick 
61473471bf0Spatrick       mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO);
61573471bf0Spatrick       if (PrintJson) {
61673471bf0Spatrick         Printer.addView(
61773471bf0Spatrick             std::make_unique<mca::InstructionView>(*STI, *IP, Insts));
61873471bf0Spatrick       }
61909467b48Spatrick 
62009467b48Spatrick       // Create the views for this pipeline, execute, and emit a report.
62109467b48Spatrick       if (PrintInstructionInfoView) {
62209467b48Spatrick         Printer.addView(std::make_unique<mca::InstructionInfoView>(
623*d415bd75Srobert             *STI, *MCII, CE, ShowEncoding, Insts, *IP, LoweredSequence,
624*d415bd75Srobert             ShowBarriers));
62509467b48Spatrick       }
62609467b48Spatrick       Printer.addView(
62709467b48Spatrick           std::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts));
62809467b48Spatrick 
62909467b48Spatrick       if (!runPipeline(*P))
63009467b48Spatrick         return 1;
63109467b48Spatrick 
63273471bf0Spatrick       if (PrintJson) {
63373471bf0Spatrick         Printer.printReport(JSONOutput);
63473471bf0Spatrick       } else {
63509467b48Spatrick         Printer.printReport(TOF->os());
63673471bf0Spatrick       }
63773471bf0Spatrick 
63873471bf0Spatrick       ++RegionIdx;
63909467b48Spatrick       continue;
64009467b48Spatrick     }
64109467b48Spatrick 
64273471bf0Spatrick     // Create the CustomBehaviour object for enforcing Target Specific
64373471bf0Spatrick     // behaviours and dependencies that aren't expressed well enough
64473471bf0Spatrick     // in the tablegen. CB cannot depend on the list of MCInst or
64573471bf0Spatrick     // the source code (but it can depend on the list of
64673471bf0Spatrick     // mca::Instruction or any objects that can be reconstructed
64773471bf0Spatrick     // from the target information).
648*d415bd75Srobert     std::unique_ptr<mca::CustomBehaviour> CB;
649*d415bd75Srobert     if (!DisableCustomBehaviour)
650*d415bd75Srobert       CB = std::unique_ptr<mca::CustomBehaviour>(
651*d415bd75Srobert           TheTarget->createCustomBehaviour(*STI, S, *MCII));
652*d415bd75Srobert     if (!CB)
653*d415bd75Srobert       // If the target doesn't have its own CB implemented (or the -disable-cb
654*d415bd75Srobert       // flag is set) then we use the base class (which does nothing).
655*d415bd75Srobert       CB = std::make_unique<mca::CustomBehaviour>(*STI, S, *MCII);
65673471bf0Spatrick 
65709467b48Spatrick     // Create a basic pipeline simulating an out-of-order backend.
65873471bf0Spatrick     auto P = MCA.createDefaultPipeline(PO, S, *CB);
65973471bf0Spatrick 
66073471bf0Spatrick     mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO);
66173471bf0Spatrick 
662*d415bd75Srobert     // Targets can define their own custom Views that exist within their
663*d415bd75Srobert     // /lib/Target/ directory so that the View can utilize their CustomBehaviour
664*d415bd75Srobert     // or other backend symbols / functionality that are not already exposed
665*d415bd75Srobert     // through one of the MC-layer classes. These Views will be initialized
666*d415bd75Srobert     // using the CustomBehaviour::getViews() variants.
667*d415bd75Srobert     // If a target makes a custom View that does not depend on their target
668*d415bd75Srobert     // CB or their backend, they should put the View within
669*d415bd75Srobert     // /tools/llvm-mca/Views/ instead.
670*d415bd75Srobert     if (!DisableCustomBehaviour) {
671*d415bd75Srobert       std::vector<std::unique_ptr<mca::View>> CBViews =
672*d415bd75Srobert           CB->getStartViews(*IP, Insts);
673*d415bd75Srobert       for (auto &CBView : CBViews)
674*d415bd75Srobert         Printer.addView(std::move(CBView));
675*d415bd75Srobert     }
676*d415bd75Srobert 
67773471bf0Spatrick     // When we output JSON, we add a view that contains the instructions
67873471bf0Spatrick     // and CPU resource information.
67973471bf0Spatrick     if (PrintJson) {
68073471bf0Spatrick       auto IV = std::make_unique<mca::InstructionView>(*STI, *IP, Insts);
68173471bf0Spatrick       Printer.addView(std::move(IV));
68273471bf0Spatrick     }
68309467b48Spatrick 
68409467b48Spatrick     if (PrintSummaryView)
68509467b48Spatrick       Printer.addView(
68609467b48Spatrick           std::make_unique<mca::SummaryView>(SM, Insts, DispatchWidth));
68709467b48Spatrick 
68809467b48Spatrick     if (EnableBottleneckAnalysis) {
68973471bf0Spatrick       if (!IsOutOfOrder) {
69073471bf0Spatrick         WithColor::warning()
69173471bf0Spatrick             << "bottleneck analysis is not supported for in-order CPU '" << MCPU
69273471bf0Spatrick             << "'.\n";
69373471bf0Spatrick       }
69409467b48Spatrick       Printer.addView(std::make_unique<mca::BottleneckAnalysis>(
69509467b48Spatrick           *STI, *IP, Insts, S.getNumIterations()));
69609467b48Spatrick     }
69709467b48Spatrick 
69809467b48Spatrick     if (PrintInstructionInfoView)
69909467b48Spatrick       Printer.addView(std::make_unique<mca::InstructionInfoView>(
700*d415bd75Srobert           *STI, *MCII, CE, ShowEncoding, Insts, *IP, LoweredSequence,
701*d415bd75Srobert           ShowBarriers));
702*d415bd75Srobert 
703*d415bd75Srobert     // Fetch custom Views that are to be placed after the InstructionInfoView.
704*d415bd75Srobert     // Refer to the comment paired with the CB->getStartViews(*IP, Insts); line
705*d415bd75Srobert     // for more info.
706*d415bd75Srobert     if (!DisableCustomBehaviour) {
707*d415bd75Srobert       std::vector<std::unique_ptr<mca::View>> CBViews =
708*d415bd75Srobert           CB->getPostInstrInfoViews(*IP, Insts);
709*d415bd75Srobert       for (auto &CBView : CBViews)
710*d415bd75Srobert         Printer.addView(std::move(CBView));
711*d415bd75Srobert     }
71209467b48Spatrick 
71309467b48Spatrick     if (PrintDispatchStats)
71409467b48Spatrick       Printer.addView(std::make_unique<mca::DispatchStatistics>());
71509467b48Spatrick 
71609467b48Spatrick     if (PrintSchedulerStats)
71709467b48Spatrick       Printer.addView(std::make_unique<mca::SchedulerStatistics>(*STI));
71809467b48Spatrick 
71909467b48Spatrick     if (PrintRetireStats)
72009467b48Spatrick       Printer.addView(std::make_unique<mca::RetireControlUnitStatistics>(SM));
72109467b48Spatrick 
72209467b48Spatrick     if (PrintRegisterFileStats)
72309467b48Spatrick       Printer.addView(std::make_unique<mca::RegisterFileStatistics>(*STI));
72409467b48Spatrick 
72509467b48Spatrick     if (PrintResourcePressureView)
72609467b48Spatrick       Printer.addView(
72709467b48Spatrick           std::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts));
72809467b48Spatrick 
72909467b48Spatrick     if (PrintTimelineView) {
73009467b48Spatrick       unsigned TimelineIterations =
73109467b48Spatrick           TimelineMaxIterations ? TimelineMaxIterations : 10;
73209467b48Spatrick       Printer.addView(std::make_unique<mca::TimelineView>(
73309467b48Spatrick           *STI, *IP, Insts, std::min(TimelineIterations, S.getNumIterations()),
73409467b48Spatrick           TimelineMaxCycles));
73509467b48Spatrick     }
73609467b48Spatrick 
737*d415bd75Srobert     // Fetch custom Views that are to be placed after all other Views.
738*d415bd75Srobert     // Refer to the comment paired with the CB->getStartViews(*IP, Insts); line
739*d415bd75Srobert     // for more info.
740*d415bd75Srobert     if (!DisableCustomBehaviour) {
741*d415bd75Srobert       std::vector<std::unique_ptr<mca::View>> CBViews =
742*d415bd75Srobert           CB->getEndViews(*IP, Insts);
743*d415bd75Srobert       for (auto &CBView : CBViews)
744*d415bd75Srobert         Printer.addView(std::move(CBView));
745*d415bd75Srobert     }
746*d415bd75Srobert 
74709467b48Spatrick     if (!runPipeline(*P))
74809467b48Spatrick       return 1;
74909467b48Spatrick 
75073471bf0Spatrick     if (PrintJson) {
75173471bf0Spatrick       Printer.printReport(JSONOutput);
75273471bf0Spatrick     } else {
75309467b48Spatrick       Printer.printReport(TOF->os());
75409467b48Spatrick     }
75509467b48Spatrick 
75673471bf0Spatrick     ++RegionIdx;
75773471bf0Spatrick   }
75873471bf0Spatrick 
75973471bf0Spatrick   if (PrintJson)
76073471bf0Spatrick     TOF->os() << formatv("{0:2}", json::Value(std::move(JSONOutput))) << "\n";
76173471bf0Spatrick 
76209467b48Spatrick   TOF->keep();
76309467b48Spatrick   return 0;
76409467b48Spatrick }
765