From 54f9ee334114e9b494fc80978357d58295a48bd1 Mon Sep 17 00:00:00 2001 From: "Paul C. Anagnostopoulos" Date: Thu, 22 Oct 2020 02:58:49 -0400 Subject: [PATCH] [TableGen] Add frontend/backend phase timing capability. Describe in the BackEnd Developer's Guide. Instrument a few backends. Remove an old unused timing facility. Add a null backend for timing the parser. Differential Revision: https://reviews.llvm.org/D91388 --- llvm/docs/CommandGuide/tblgen.rst | 18 +++++++--- llvm/docs/TableGen/BackGuide.rst | 52 +++++++++++++++++++++++++++ llvm/include/llvm/TableGen/Record.h | 32 +++++++++++++++++ llvm/lib/TableGen/Main.cpp | 41 ++++++++++++++------- llvm/lib/TableGen/Record.cpp | 41 +++++++++++++++++++++ llvm/utils/TableGen/CallingConvEmitter.cpp | 2 ++ llvm/utils/TableGen/CodeGenTarget.cpp | 2 -- llvm/utils/TableGen/DAGISelEmitter.cpp | 10 ++++-- llvm/utils/TableGen/GICombinerEmitter.cpp | 17 +++------ llvm/utils/TableGen/PseudoLoweringEmitter.cpp | 2 ++ llvm/utils/TableGen/RegisterBankEmitter.cpp | 3 ++ llvm/utils/TableGen/RegisterInfoEmitter.cpp | 7 ++++ llvm/utils/TableGen/TableGen.cpp | 15 ++++---- 13 files changed, 200 insertions(+), 42 deletions(-) diff --git a/llvm/docs/CommandGuide/tblgen.rst b/llvm/docs/CommandGuide/tblgen.rst index 3eda821..80febad 100644 --- a/llvm/docs/CommandGuide/tblgen.rst +++ b/llvm/docs/CommandGuide/tblgen.rst @@ -43,7 +43,7 @@ General Options Specify the name of the dependency filename. -.. option:: --debug +.. option:: -debug Enable debug output. @@ -58,6 +58,11 @@ General Options ``directory`` value should be a full or partial path to a directory that contains target description files. +.. option:: -null-backend + + Parse the source files and build the records, but do not run any + backend. This is useful for timing the frontend. + .. option:: -o filename Specify the output file name. If ``filename`` is ``-``, then @@ -65,21 +70,24 @@ General Options .. option:: -print-records - Print all classes and records to standard output (default option). + Print all classes and records to standard output (default backend option). .. option:: -print-detailed-records Print a detailed report of all global variables, classes, and records to standard output. -.. option:: --stats +.. option:: -stats Enable statistics output. +.. option:: -time-phases + + Time the parser and backend phases and print a report. + .. option:: -write-if-changed - Write the output file only if it is new or has changed. The default - is true. + Write the output file only if it is new or has changed. llvm-tblgen Options ~~~~~~~~~~~~~~~~~~~ diff --git a/llvm/docs/TableGen/BackGuide.rst b/llvm/docs/TableGen/BackGuide.rst index b9b9cf8..59a1ed1 100644 --- a/llvm/docs/TableGen/BackGuide.rst +++ b/llvm/docs/TableGen/BackGuide.rst @@ -824,3 +824,55 @@ parentheses. Each field is shown with its value and the source location at which it was set. The ``defm`` sequence gives the locations of the ``defm`` statements that were involved in generating the record, in the order they were invoked. + +Timing TableGen Phases +---------------------- + +TableGen provides a phase timing feature that produces a report of the time +used by the various phases of parsing the source files and running the +selected backend. This feature is enabled with the ``--time-phases`` option +of the TableGen command. + +If the backend is *not* instrumented for timing, then a report such as the +following is produced. This is the timing for the +``--print-detailed-records`` backend run on the AMDGPU target. + +.. code-block:: text + + ===-------------------------------------------------------------------------=== + TableGen Phase Timing + ===-------------------------------------------------------------------------=== + Total Execution Time: 101.0106 seconds (102.4819 wall clock) + + ---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name --- + 85.5197 ( 84.9%) 0.1560 ( 50.0%) 85.6757 ( 84.8%) 85.7009 ( 83.6%) Backend overall + 15.1789 ( 15.1%) 0.0000 ( 0.0%) 15.1789 ( 15.0%) 15.1829 ( 14.8%) Parse, build records + 0.0000 ( 0.0%) 0.1560 ( 50.0%) 0.1560 ( 0.2%) 1.5981 ( 1.6%) Write output + 100.6986 (100.0%) 0.3120 (100.0%) 101.0106 (100.0%) 102.4819 (100.0%) Total + +Note that all the time for the backend is lumped under "Backend overall". + +If the backend is instrumented for timing, then its processing is +divided into phases and each one timed separately. This is the timing for +the ``--emit-dag-isel`` backend run on the AMDGPU target. + +.. code-block:: text + + ===-------------------------------------------------------------------------=== + TableGen Phase Timing + ===-------------------------------------------------------------------------=== + Total Execution Time: 746.3868 seconds (747.1447 wall clock) + + ---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name --- + 657.7938 ( 88.1%) 0.1404 ( 90.0%) 657.9342 ( 88.1%) 658.6497 ( 88.2%) Emit matcher table + 70.2317 ( 9.4%) 0.0000 ( 0.0%) 70.2317 ( 9.4%) 70.2700 ( 9.4%) Convert to matchers + 14.8825 ( 2.0%) 0.0156 ( 10.0%) 14.8981 ( 2.0%) 14.9009 ( 2.0%) Parse, build records + 2.1840 ( 0.3%) 0.0000 ( 0.0%) 2.1840 ( 0.3%) 2.1791 ( 0.3%) Sort patterns + 1.1388 ( 0.2%) 0.0000 ( 0.0%) 1.1388 ( 0.2%) 1.1401 ( 0.2%) Optimize matchers + 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0050 ( 0.0%) Write output + 746.2308 (100.0%) 0.1560 (100.0%) 746.3868 (100.0%) 747.1447 (100.0%) Total + +The backend has been divided into four phases and timed separately. + +If you want to instrument a backend, refer to the backend ``DAGISelEmitter.cpp`` +and search for ``Records.startTimer``. \ No newline at end of file diff --git a/llvm/include/llvm/TableGen/Record.h b/llvm/include/llvm/TableGen/Record.h index 920c3e6..2e2bf4b 100644 --- a/llvm/include/llvm/TableGen/Record.h +++ b/llvm/include/llvm/TableGen/Record.h @@ -26,6 +26,7 @@ #include "llvm/Support/SMLoc.h" #include "llvm/Support/TrailingObjects.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/timer.h" #include #include #include @@ -1743,6 +1744,13 @@ class RecordKeeper { std::map> ExtraGlobals; unsigned AnonCounter = 0; + // These members are for the phase timing feature. We need a timer group, + // the last timer started, and a flag to say whether the last timer + // is the special "backend overall timer." + TimerGroup *TimingGroup = nullptr; + Timer *LastTimer = nullptr; + bool BackendTimer = false; + public: /// Get the main TableGen input file's name. const std::string getInputFilename() const { return InputFilename; } @@ -1803,6 +1811,30 @@ public: Init *getNewAnonymousName(); + /// Start phase timing; called if the --time-phases option is specified. + void startPhaseTiming() { + TimingGroup = new TimerGroup("TableGen", "TableGen Phase Timing"); + } + + /// Start timing a phase. Automatically stops any previous phase timer. + void startTimer(StringRef Name); + + /// Stop timing a phase. + void stopTimer(); + + /// Start timing the overall backend. If the backend starts a timer, + /// then this timer is cleared. + void startBackendTimer(StringRef Name); + + /// Stop timing the overall backend. + void stopBackendTimer(); + + /// Stop phase timing and print the report. + void stopPhaseTiming() { + if (TimingGroup) + delete TimingGroup; + } + //===--------------------------------------------------------------------===// // High-level helper methods, useful for tablegen backends. diff --git a/llvm/lib/TableGen/Main.cpp b/llvm/lib/TableGen/Main.cpp index d7c73d2..0ace536 100644 --- a/llvm/lib/TableGen/Main.cpp +++ b/llvm/lib/TableGen/Main.cpp @@ -52,6 +52,9 @@ MacroNames("D", cl::desc("Name of the macro to be defined"), static cl::opt WriteIfChanged("write-if-changed", cl::desc("Only write output if it changed")); +static cl::opt +TimePhases("time-phases", cl::desc("Time phases of parser and backend")); + static int reportError(const char *ProgName, Twine Msg) { errs() << ProgName << ": " << Msg; errs().flush(); @@ -83,7 +86,12 @@ static int createDependencyFile(const TGParser &Parser, const char *argv0) { int llvm::TableGenMain(const char *argv0, TableGenMainFn *MainFn) { RecordKeeper Records; + if (TimePhases) + Records.startPhaseTiming(); + // Parse the input file. + + Records.startTimer("Parse, build records"); ErrorOr> FileOrErr = MemoryBuffer::getFileOrSTDIN(InputFilename); if (std::error_code EC = FileOrErr.getError()) @@ -103,11 +111,15 @@ int llvm::TableGenMain(const char *argv0, TableGenMainFn *MainFn) { if (Parser.ParseFile()) return 1; + Records.stopTimer(); // Write output to memory. + Records.startBackendTimer("Backend overall"); std::string OutString; raw_string_ostream Out(OutString); - if (MainFn(Out, Records)) + unsigned status = MainFn(Out, Records); + Records.stopBackendTimer(); + if (status) return 1; // Always write the depfile, even if the main output hasn't changed. @@ -119,26 +131,31 @@ int llvm::TableGenMain(const char *argv0, TableGenMainFn *MainFn) { return Ret; } + Records.startTimer("Write output"); + bool WriteFile = true; if (WriteIfChanged) { // Only updates the real output file if there are any differences. // This prevents recompilation of all the files depending on it if there // aren't any. if (auto ExistingOrErr = MemoryBuffer::getFile(OutputFilename)) if (std::move(ExistingOrErr.get())->getBuffer() == Out.str()) - return 0; + WriteFile = false; } - - std::error_code EC; - ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None); - if (EC) - return reportError(argv0, "error opening " + OutputFilename + ":" + - EC.message() + "\n"); - OutFile.os() << Out.str(); + if (WriteFile) { + std::error_code EC; + ToolOutputFile OutFile(OutputFilename, EC, sys::fs::OF_None); + if (EC) + return reportError(argv0, "error opening " + OutputFilename + ": " + + EC.message() + "\n"); + OutFile.os() << Out.str(); + if (ErrorsPrinted == 0) + OutFile.keep(); + } + + Records.stopTimer(); + Records.stopPhaseTiming(); if (ErrorsPrinted > 0) return reportError(argv0, Twine(ErrorsPrinted) + " errors.\n"); - - // Declare success. - OutFile.keep(); return 0; } diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp index 81d7002..dd055b8 100644 --- a/llvm/lib/TableGen/Record.cpp +++ b/llvm/lib/TableGen/Record.cpp @@ -27,6 +27,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/timer.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" #include @@ -2574,6 +2575,46 @@ Init *RecordKeeper::getNewAnonymousName() { return StringInit::get("anonymous_" + utostr(AnonCounter++)); } +// These functions implement the phase timing facility. Starting a timer +// when one is already running stops the running one. + +void RecordKeeper::startTimer(StringRef Name) { + if (TimingGroup) { + if (LastTimer && LastTimer->isRunning()) { + LastTimer->stopTimer(); + if (BackendTimer) { + LastTimer->clear(); + BackendTimer = false; + } + } + + LastTimer = new Timer("", Name, *TimingGroup); + LastTimer->startTimer(); + } +} + +void RecordKeeper::stopTimer() { + if (TimingGroup) { + assert(LastTimer && "No phase timer was started"); + LastTimer->stopTimer(); + } +} + +void RecordKeeper::startBackendTimer(StringRef Name) { + if (TimingGroup) { + startTimer(Name); + BackendTimer = true; + } +} + +void RecordKeeper::stopBackendTimer() { + if (TimingGroup) { + if (BackendTimer) + stopTimer(); + BackendTimer = false; + } +} + std::vector RecordKeeper::getAllDerivedDefinitions( const ArrayRef ClassNames) const { SmallVector ClassRecs; diff --git a/llvm/utils/TableGen/CallingConvEmitter.cpp b/llvm/utils/TableGen/CallingConvEmitter.cpp index 9ff9ebd..9e99748 100644 --- a/llvm/utils/TableGen/CallingConvEmitter.cpp +++ b/llvm/utils/TableGen/CallingConvEmitter.cpp @@ -38,6 +38,7 @@ void CallingConvEmitter::run(raw_ostream &O) { // Emit prototypes for all of the non-custom CC's so that they can forward ref // each other. + Records.startTimer("Emit prototypes"); for (Record *CC : CCs) { if (!CC->getValueAsBit("Custom")) { unsigned Pad = CC->getName().size(); @@ -56,6 +57,7 @@ void CallingConvEmitter::run(raw_ostream &O) { } // Emit each non-custom calling convention description in full. + Records.startTimer("Emit full descriptions"); for (Record *CC : CCs) { if (!CC->getValueAsBit("Custom")) EmitCallingConv(CC, O); diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp index 59a9379..794bb62 100644 --- a/llvm/utils/TableGen/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/CodeGenTarget.cpp @@ -433,8 +433,6 @@ CodeGenSchedModels &CodeGenTarget::getSchedModels() const { } void CodeGenTarget::ReadInstructions() const { - NamedRegionTimer T("Read Instructions", "Time spent reading instructions", - "CodeGenTarget", "CodeGenTarget", TimeRegions); std::vector Insts = Records.getAllDerivedDefinitions("Instruction"); if (Insts.size() <= 2) PrintFatalError("No 'Instruction' subclasses defined!"); diff --git a/llvm/utils/TableGen/DAGISelEmitter.cpp b/llvm/utils/TableGen/DAGISelEmitter.cpp index d8e78ce..85a6285 100644 --- a/llvm/utils/TableGen/DAGISelEmitter.cpp +++ b/llvm/utils/TableGen/DAGISelEmitter.cpp @@ -23,9 +23,10 @@ namespace { /// DAGISelEmitter - The top-level class which coordinates construction /// and emission of the instruction selector. class DAGISelEmitter { + RecordKeeper &Records; // Just so we can get at the timing functions. CodeGenDAGPatterns CGP; public: - explicit DAGISelEmitter(RecordKeeper &R) : CGP(R) {} + explicit DAGISelEmitter(RecordKeeper &R) : Records(R), CGP(R) {} void run(raw_ostream &OS); }; } // End anonymous namespace @@ -150,6 +151,7 @@ void DAGISelEmitter::run(raw_ostream &OS) { }); // Add all the patterns to a temporary list so we can sort them. + Records.startTimer("Sort patterns"); std::vector Patterns; for (CodeGenDAGPatterns::ptm_iterator I = CGP.ptm_begin(), E = CGP.ptm_end(); I != E; ++I) @@ -160,8 +162,8 @@ void DAGISelEmitter::run(raw_ostream &OS) { std::stable_sort(Patterns.begin(), Patterns.end(), PatternSortingPredicate(CGP)); - // Convert each variant of each pattern into a Matcher. + Records.startTimer("Convert to matchers"); std::vector PatternMatchers; for (unsigned i = 0, e = Patterns.size(); i != e; ++i) { for (unsigned Variant = 0; ; ++Variant) { @@ -175,8 +177,12 @@ void DAGISelEmitter::run(raw_ostream &OS) { std::unique_ptr TheMatcher = std::make_unique(PatternMatchers); + Records.startTimer("Optimize matchers"); OptimizeMatcher(TheMatcher, CGP); + //Matcher->dump(); + + Records.startTimer("Emit matcher table"); EmitMatcherTable(TheMatcher.get(), CGP, OS); } diff --git a/llvm/utils/TableGen/GICombinerEmitter.cpp b/llvm/utils/TableGen/GICombinerEmitter.cpp index 1d7546d..c0884d5 100644 --- a/llvm/utils/TableGen/GICombinerEmitter.cpp +++ b/llvm/utils/TableGen/GICombinerEmitter.cpp @@ -346,8 +346,6 @@ void CombineRule::declareMatchData(StringRef PatternSymbol, StringRef Type, } bool CombineRule::parseDefs() { - NamedRegionTimer T("parseDefs", "Time spent parsing the defs", "Rule Parsing", - "Time spent on rule parsing", TimeRegions); DagInit *Defs = TheDef.getValueAsDag("Defs"); if (Defs->getOperatorAsDef(TheDef.getLoc())->getName() != "defs") { @@ -488,8 +486,6 @@ bool CombineRule::parseWipMatchOpcodeMatcher(const CodeGenTarget &Target, return false; } bool CombineRule::parseMatcher(const CodeGenTarget &Target) { - NamedRegionTimer T("parseMatcher", "Time spent parsing the matcher", - "Rule Parsing", "Time spent on rule parsing", TimeRegions); StringMap> NamedEdgeDefs; StringMap> NamedEdgeUses; DagInit *Matchers = TheDef.getValueAsDag("Match"); @@ -593,6 +589,7 @@ bool CombineRule::parseMatcher(const CodeGenTarget &Target) { } class GICombinerEmitter { + RecordKeeper &Records; StringRef Name; const CodeGenTarget &Target; Record *Combiner; @@ -626,7 +623,7 @@ public: GICombinerEmitter::GICombinerEmitter(RecordKeeper &RK, const CodeGenTarget &Target, StringRef Name, Record *Combiner) - : Name(Name), Target(Target), Combiner(Combiner) {} + : Records(RK), Name(Name), Target(Target), Combiner(Combiner) {} void GICombinerEmitter::emitNameMatcher(raw_ostream &OS) const { std::vector> Cases; @@ -850,6 +847,7 @@ static void emitAdditionalHelperMethodArguments(raw_ostream &OS, } void GICombinerEmitter::run(raw_ostream &OS) { + Records.startTimer("Gather rules"); gatherRules(Rules, Combiner->getValueAsListOfDefs("Rules")); if (StopAfterParse) { MatchDagCtx.print(errs()); @@ -861,11 +859,8 @@ void GICombinerEmitter::run(raw_ostream &OS) { PrintFatalError(Combiner->getLoc(), "Failed to parse one or more rules"); LLVM_DEBUG(dbgs() << "Optimizing tree for " << Rules.size() << " rules\n"); std::unique_ptr Tree; + Records.startTimer("Optimize combiner"); { - NamedRegionTimer T("Optimize", "Time spent optimizing the combiner", - "Code Generation", "Time spent generating code", - TimeRegions); - GIMatchTreeBuilder TreeBuilder(0); for (const auto &Rule : Rules) { bool HadARoot = false; @@ -887,9 +882,7 @@ void GICombinerEmitter::run(raw_ostream &OS) { return; } - NamedRegionTimer T("Emit", "Time spent emitting the combiner", - "Code Generation", "Time spent generating code", - TimeRegions); + Records.startTimer("Emit combiner"); OS << "#ifdef " << Name.upper() << "_GENCOMBINERHELPER_DEPS\n" << "#include \"llvm/ADT/SparseBitVector.h\"\n" << "namespace llvm {\n" diff --git a/llvm/utils/TableGen/PseudoLoweringEmitter.cpp b/llvm/utils/TableGen/PseudoLoweringEmitter.cpp index 8131c25..e05409d 100644 --- a/llvm/utils/TableGen/PseudoLoweringEmitter.cpp +++ b/llvm/utils/TableGen/PseudoLoweringEmitter.cpp @@ -298,11 +298,13 @@ void PseudoLoweringEmitter::run(raw_ostream &o) { Records.getAllDerivedDefinitions(makeArrayRef(Classes)); // Process the pseudo expansion definitions, validating them as we do so. + Records.startTimer("Process definitions"); for (unsigned i = 0, e = Insts.size(); i != e; ++i) evaluateExpansion(Insts[i]); // Generate expansion code to lower the pseudo to an MCInst of the real // instruction. + Records.startTimer("Emit expansion code"); emitLoweringEmitter(o); } diff --git a/llvm/utils/TableGen/RegisterBankEmitter.cpp b/llvm/utils/TableGen/RegisterBankEmitter.cpp index 44af134..e7583f7 100644 --- a/llvm/utils/TableGen/RegisterBankEmitter.cpp +++ b/llvm/utils/TableGen/RegisterBankEmitter.cpp @@ -282,6 +282,7 @@ void RegisterBankEmitter::run(raw_ostream &OS) { StringRef TargetName = Target.getName(); const CodeGenRegBank &RegisterClassHierarchy = Target.getRegBank(); + Records.startTimer("Analyze records"); std::vector Banks; for (const auto &V : Records.getAllDerivedDefinitions("RegisterBank")) { SmallPtrSet VisitedRCs; @@ -303,6 +304,7 @@ void RegisterBankEmitter::run(raw_ostream &OS) { } // Warn about ambiguous MIR caused by register bank/class name clashes. + Records.startTimer("Warn ambiguous"); for (const auto &Class : RegisterClassHierarchy.getRegClasses()) { for (const auto &Bank : Banks) { if (Bank.getName().lower() == StringRef(Class.getName()).lower()) { @@ -315,6 +317,7 @@ void RegisterBankEmitter::run(raw_ostream &OS) { } } + Records.startTimer("Emit output"); emitSourceFileHeader("Register Bank Source Fragments", OS); OS << "#ifdef GET_REGBANK_DECLARATIONS\n" << "#undef GET_REGBANK_DECLARATIONS\n"; diff --git a/llvm/utils/TableGen/RegisterInfoEmitter.cpp b/llvm/utils/TableGen/RegisterInfoEmitter.cpp index 8f19a28..63710a1 100644 --- a/llvm/utils/TableGen/RegisterInfoEmitter.cpp +++ b/llvm/utils/TableGen/RegisterInfoEmitter.cpp @@ -1616,9 +1616,16 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, void RegisterInfoEmitter::run(raw_ostream &OS) { CodeGenRegBank &RegBank = Target.getRegBank(); + Records.startTimer("Print enums"); runEnums(OS, Target, RegBank); + + Records.startTimer("Print MC registers"); runMCDesc(OS, Target, RegBank); + + Records.startTimer("Print header fragment"); runTargetHeader(OS, Target, RegBank); + + Records.startTimer("Print target registers"); runTargetDesc(OS, Target, RegBank); if (RegisterInfoDebug) diff --git a/llvm/utils/TableGen/TableGen.cpp b/llvm/utils/TableGen/TableGen.cpp index 65e63ff..6d851da 100644 --- a/llvm/utils/TableGen/TableGen.cpp +++ b/llvm/utils/TableGen/TableGen.cpp @@ -22,6 +22,7 @@ using namespace llvm; enum ActionType { PrintRecords, PrintDetailedRecords, + NullBackend, DumpJSON, GenEmitter, GenRegisterInfo, @@ -59,9 +60,6 @@ enum ActionType { }; namespace llvm { -/// Storage for TimeRegionsOpt as a global so that backends aren't required to -/// include CommandLine.h -bool TimeRegions = false; cl::opt EmitLongStrLiterals( "long-string-literals", cl::desc("when emitting large string tables, prefer string literals over " @@ -78,6 +76,8 @@ cl::opt Action( "Print all records to stdout (default)"), clEnumValN(PrintDetailedRecords, "print-detailed-records", "Print full details of all records to stdout"), + clEnumValN(NullBackend, "null-backend", + "Do nothing after parsing (useful for timing)"), clEnumValN(DumpJSON, "dump-json", "Dump all records as machine-readable JSON"), clEnumValN(GenEmitter, "gen-emitter", "Generate machine code emitter"), @@ -145,19 +145,16 @@ cl::opt Class("class", cl::desc("Print Enum list for this class"), cl::value_desc("class name"), cl::cat(PrintEnumsCat)); -cl::opt - TimeRegionsOpt("time-regions", - cl::desc("Time regions of tablegens execution"), - cl::location(TimeRegions)); - bool LLVMTableGenMain(raw_ostream &OS, RecordKeeper &Records) { switch (Action) { case PrintRecords: - OS << Records; // No argument, dump all contents + OS << Records; // No argument, dump all contents break; case PrintDetailedRecords: EmitDetailedRecords(Records, OS); break; + case NullBackend: // No backend at all. + break; case DumpJSON: EmitJSON(Records, OS); break; -- 2.7.4