brintos

brintos / llvm-project-archived public Read only

0
0
Text · 29.4 KiB · b5820f8 Raw
739 lines · cpp
1//===-- llvm-exegesis.cpp ---------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// Measures execution properties (latencies/uops) of an instruction.11///12//===----------------------------------------------------------------------===//13 14#include "lib/Analysis.h"15#include "lib/BenchmarkResult.h"16#include "lib/BenchmarkRunner.h"17#include "lib/Clustering.h"18#include "lib/CodeTemplate.h"19#include "lib/Error.h"20#include "lib/LlvmState.h"21#include "lib/PerfHelper.h"22#include "lib/ProgressMeter.h"23#include "lib/ResultAggregator.h"24#include "lib/SnippetFile.h"25#include "lib/SnippetRepetitor.h"26#include "lib/Target.h"27#include "lib/TargetSelect.h"28#include "lib/ValidationEvent.h"29#include "llvm/ADT/StringExtras.h"30#include "llvm/ADT/Twine.h"31#include "llvm/MC/MCInstBuilder.h"32#include "llvm/MC/MCObjectFileInfo.h"33#include "llvm/MC/MCParser/MCAsmParser.h"34#include "llvm/MC/MCParser/MCTargetAsmParser.h"35#include "llvm/MC/MCRegisterInfo.h"36#include "llvm/MC/MCSubtargetInfo.h"37#include "llvm/MC/TargetRegistry.h"38#include "llvm/Object/ObjectFile.h"39#include "llvm/Support/CommandLine.h"40#include "llvm/Support/FileSystem.h"41#include "llvm/Support/Format.h"42#include "llvm/Support/InitLLVM.h"43#include "llvm/Support/Path.h"44#include "llvm/Support/SourceMgr.h"45#include "llvm/Support/TargetSelect.h"46#include "llvm/TargetParser/Host.h"47#include <algorithm>48#include <string>49 50namespace llvm {51namespace exegesis {52 53static cl::opt<int> OpcodeIndex(54    "opcode-index",55    cl::desc("opcode to measure, by index, or -1 to measure all opcodes"),56    cl::cat(BenchmarkOptions), cl::init(0));57 58static cl::opt<std::string>59    OpcodeNames("opcode-name",60                cl::desc("comma-separated list of opcodes to measure, by name"),61                cl::cat(BenchmarkOptions), cl::init(""));62 63static cl::opt<std::string> SnippetsFile("snippets-file",64                                         cl::desc("code snippets to measure"),65                                         cl::cat(BenchmarkOptions),66                                         cl::init(""));67 68static cl::opt<std::string>69    BenchmarkFile("benchmarks-file",70                  cl::desc("File to read (analysis mode) or write "71                           "(latency/uops/inverse_throughput modes) benchmark "72                           "results. “-” uses stdin/stdout."),73                  cl::cat(Options), cl::init(""));74 75static cl::opt<Benchmark::ModeE> BenchmarkMode(76    "mode", cl::desc("the mode to run"), cl::cat(Options),77    cl::values(clEnumValN(Benchmark::Latency, "latency", "Instruction Latency"),78               clEnumValN(Benchmark::InverseThroughput, "inverse_throughput",79                          "Instruction Inverse Throughput"),80               clEnumValN(Benchmark::Uops, "uops", "Uop Decomposition"),81               // When not asking for a specific benchmark mode,82               // we'll analyse the results.83               clEnumValN(Benchmark::Unknown, "analysis", "Analysis")));84 85static cl::opt<Benchmark::ResultAggregationModeE> ResultAggMode(86    "result-aggregation-mode", cl::desc("How to aggregate multi-values result"),87    cl::cat(BenchmarkOptions),88    cl::values(clEnumValN(Benchmark::Min, "min", "Keep min reading"),89               clEnumValN(Benchmark::Max, "max", "Keep max reading"),90               clEnumValN(Benchmark::Mean, "mean",91                          "Compute mean of all readings"),92               clEnumValN(Benchmark::MinVariance, "min-variance",93                          "Keep readings set with min-variance")),94    cl::init(Benchmark::Min));95 96static cl::opt<Benchmark::RepetitionModeE> RepetitionMode(97    "repetition-mode", cl::desc("how to repeat the instruction snippet"),98    cl::cat(BenchmarkOptions),99    cl::values(100        clEnumValN(Benchmark::Duplicate, "duplicate", "Duplicate the snippet"),101        clEnumValN(Benchmark::Loop, "loop", "Loop over the snippet"),102        clEnumValN(Benchmark::AggregateMin, "min",103                   "All of the above and take the minimum of measurements"),104        clEnumValN(Benchmark::MiddleHalfDuplicate, "middle-half-duplicate",105                   "Middle half duplicate mode"),106        clEnumValN(Benchmark::MiddleHalfLoop, "middle-half-loop",107                   "Middle half loop mode")),108    cl::init(Benchmark::Duplicate));109 110static cl::opt<bool> BenchmarkMeasurementsPrintProgress(111    "measurements-print-progress",112    cl::desc("Produce progress indicator when performing measurements"),113    cl::cat(BenchmarkOptions), cl::init(false));114 115static cl::opt<BenchmarkPhaseSelectorE> BenchmarkPhaseSelector(116    "benchmark-phase",117    cl::desc(118        "it is possible to stop the benchmarking process after some phase"),119    cl::cat(BenchmarkOptions),120    cl::values(121        clEnumValN(BenchmarkPhaseSelectorE::PrepareSnippet, "prepare-snippet",122                   "Only generate the minimal instruction sequence"),123        clEnumValN(BenchmarkPhaseSelectorE::PrepareAndAssembleSnippet,124                   "prepare-and-assemble-snippet",125                   "Same as prepare-snippet, but also dumps an excerpt of the "126                   "sequence (hex encoded)"),127        clEnumValN(BenchmarkPhaseSelectorE::AssembleMeasuredCode,128                   "assemble-measured-code",129                   "Same as prepare-and-assemble-snippet, but also creates the "130                   "full sequence "131                   "that can be dumped to a file using --dump-object-to-disk"),132        clEnumValN(133            BenchmarkPhaseSelectorE::Measure, "measure",134            "Same as prepare-measured-code, but also runs the measurement "135            "(default)")),136    cl::init(BenchmarkPhaseSelectorE::Measure));137 138static cl::opt<bool>139    UseDummyPerfCounters("use-dummy-perf-counters",140                         cl::desc("Do not read real performance counters, use "141                                  "dummy values (for testing)"),142                         cl::cat(BenchmarkOptions), cl::init(false));143 144static cl::opt<unsigned>145    MinInstructions("min-instructions",146                    cl::desc("The minimum number of instructions that should "147                             "be included in the snippet"),148                    cl::cat(BenchmarkOptions), cl::init(10000));149 150static cl::opt<unsigned>151    LoopBodySize("loop-body-size",152                 cl::desc("when repeating the instruction snippet by looping "153                          "over it, duplicate the snippet until the loop body "154                          "contains at least this many instruction"),155                 cl::cat(BenchmarkOptions), cl::init(0));156 157static cl::opt<unsigned> MaxConfigsPerOpcode(158    "max-configs-per-opcode",159    cl::desc(160        "allow to snippet generator to generate at most that many configs"),161    cl::cat(BenchmarkOptions), cl::init(1));162 163static cl::opt<bool> IgnoreInvalidSchedClass(164    "ignore-invalid-sched-class",165    cl::desc("ignore instructions that do not define a sched class"),166    cl::cat(BenchmarkOptions), cl::init(false));167 168static cl::opt<BenchmarkFilter> AnalysisSnippetFilter(169    "analysis-filter", cl::desc("Filter the benchmarks before analysing them"),170    cl::cat(BenchmarkOptions),171    cl::values(172        clEnumValN(BenchmarkFilter::All, "all",173                   "Keep all benchmarks (default)"),174        clEnumValN(BenchmarkFilter::RegOnly, "reg-only",175                   "Keep only those benchmarks that do *NOT* involve memory"),176        clEnumValN(BenchmarkFilter::WithMem, "mem-only",177                   "Keep only the benchmarks that *DO* involve memory")),178    cl::init(BenchmarkFilter::All));179 180static cl::opt<BenchmarkClustering::ModeE> AnalysisClusteringAlgorithm(181    "analysis-clustering", cl::desc("the clustering algorithm to use"),182    cl::cat(AnalysisOptions),183    cl::values(clEnumValN(BenchmarkClustering::Dbscan, "dbscan",184                          "use DBSCAN/OPTICS algorithm"),185               clEnumValN(BenchmarkClustering::Naive, "naive",186                          "one cluster per opcode")),187    cl::init(BenchmarkClustering::Dbscan));188 189static cl::opt<unsigned> AnalysisDbscanNumPoints(190    "analysis-numpoints",191    cl::desc("minimum number of points in an analysis cluster (dbscan only)"),192    cl::cat(AnalysisOptions), cl::init(3));193 194static cl::opt<float> AnalysisClusteringEpsilon(195    "analysis-clustering-epsilon",196    cl::desc("epsilon for benchmark point clustering"),197    cl::cat(AnalysisOptions), cl::init(0.1));198 199static cl::opt<float> AnalysisInconsistencyEpsilon(200    "analysis-inconsistency-epsilon",201    cl::desc("epsilon for detection of when the cluster is different from the "202             "LLVM schedule profile values"),203    cl::cat(AnalysisOptions), cl::init(0.1));204 205static cl::opt<std::string>206    AnalysisClustersOutputFile("analysis-clusters-output-file", cl::desc(""),207                               cl::cat(AnalysisOptions), cl::init(""));208static cl::opt<std::string>209    AnalysisInconsistenciesOutputFile("analysis-inconsistencies-output-file",210                                      cl::desc(""), cl::cat(AnalysisOptions),211                                      cl::init(""));212 213static cl::opt<bool> AnalysisDisplayUnstableOpcodes(214    "analysis-display-unstable-clusters",215    cl::desc("if there is more than one benchmark for an opcode, said "216             "benchmarks may end up not being clustered into the same cluster "217             "if the measured performance characteristics are different. by "218             "default all such opcodes are filtered out. this flag will "219             "instead show only such unstable opcodes"),220    cl::cat(AnalysisOptions), cl::init(false));221 222static cl::opt<bool> AnalysisOverrideBenchmarksTripleAndCpu(223    "analysis-override-benchmark-triple-and-cpu",224    cl::desc("By default, we analyze the benchmarks for the triple/CPU they "225             "were measured for, but if you want to analyze them for some "226             "other combination (specified via -mtriple/-mcpu), you can "227             "pass this flag."),228    cl::cat(AnalysisOptions), cl::init(false));229 230static cl::opt<std::string>231    TripleName("mtriple",232               cl::desc("Target triple. See -version for available targets"),233               cl::cat(Options));234 235static cl::opt<std::string>236    MCPU("mcpu",237         cl::desc("Target a specific cpu type (-mcpu=help for details)"),238         cl::value_desc("cpu-name"), cl::cat(Options), cl::init("native"));239 240static cl::opt<std::string>241    DumpObjectToDisk("dump-object-to-disk",242                     cl::desc("dumps the generated benchmark object to disk "243                              "and prints a message to access it"),244                     cl::ValueOptional, cl::cat(BenchmarkOptions));245 246static cl::opt<BenchmarkRunner::ExecutionModeE> ExecutionMode(247    "execution-mode",248    cl::desc("Selects the execution mode to use for running snippets"),249    cl::cat(BenchmarkOptions),250    cl::values(clEnumValN(BenchmarkRunner::ExecutionModeE::InProcess,251                          "inprocess",252                          "Executes the snippets within the same process"),253               clEnumValN(BenchmarkRunner::ExecutionModeE::SubProcess,254                          "subprocess",255                          "Spawns a subprocess for each snippet execution, "256                          "allows for the use of memory annotations")),257    cl::init(BenchmarkRunner::ExecutionModeE::InProcess));258 259static cl::opt<unsigned> BenchmarkRepeatCount(260    "benchmark-repeat-count",261    cl::desc("The number of times to repeat measurements on the benchmark k "262             "before aggregating the results"),263    cl::cat(BenchmarkOptions), cl::init(30));264 265static cl::list<ValidationEvent> ValidationCounters(266    "validation-counter",267    cl::desc(268        "The name of a validation counter to run concurrently with the main "269        "counter to validate benchmarking assumptions"),270    cl::CommaSeparated, cl::cat(BenchmarkOptions), ValidationEventOptions());271 272static cl::opt<int> BenchmarkProcessCPU(273    "benchmark-process-cpu",274    cl::desc("The CPU number that the benchmarking process should executon on"),275    cl::cat(BenchmarkOptions), cl::init(-1));276 277static cl::opt<std::string> MAttr(278    "mattr", cl::desc("comma-separated list of target architecture features"),279    cl::value_desc("+feature1,-feature2,..."), cl::cat(Options), cl::init(""));280 281static ExitOnError ExitOnErr("llvm-exegesis error: ");282 283// Helper function that logs the error(s) and exits.284template <typename... ArgTs> static void ExitWithError(ArgTs &&... Args) {285  ExitOnErr(make_error<Failure>(std::forward<ArgTs>(Args)...));286}287 288// Check Err. If it's in a failure state log the file error(s) and exit.289static void ExitOnFileError(const Twine &FileName, Error Err) {290  if (Err) {291    ExitOnErr(createFileError(FileName, std::move(Err)));292  }293}294 295// Check E. If it's in a success state then return the contained value.296// If it's in a failure state log the file error(s) and exit.297template <typename T>298T ExitOnFileError(const Twine &FileName, Expected<T> &&E) {299  ExitOnFileError(FileName, E.takeError());300  return std::move(*E);301}302 303// Checks that only one of OpcodeNames, OpcodeIndex or SnippetsFile is provided,304// and returns the opcode indices or {} if snippets should be read from305// `SnippetsFile`.306static std::vector<unsigned> getOpcodesOrDie(const LLVMState &State) {307  const size_t NumSetFlags = (OpcodeNames.empty() ? 0 : 1) +308                             (OpcodeIndex == 0 ? 0 : 1) +309                             (SnippetsFile.empty() ? 0 : 1);310  const auto &ET = State.getExegesisTarget();311  const auto AvailableFeatures = State.getSubtargetInfo().getFeatureBits();312 313  if (NumSetFlags != 1) {314    ExitOnErr.setBanner("llvm-exegesis: ");315    ExitWithError("please provide one and only one of 'opcode-index', "316                  "'opcode-name' or 'snippets-file'");317  }318  if (!SnippetsFile.empty())319    return {};320  if (OpcodeIndex > 0)321    return {static_cast<unsigned>(OpcodeIndex)};322  if (OpcodeIndex < 0) {323    std::vector<unsigned> Result;324    unsigned NumOpcodes = State.getInstrInfo().getNumOpcodes();325    Result.reserve(NumOpcodes);326    for (unsigned I = 0, E = NumOpcodes; I < E; ++I) {327      if (!ET.isOpcodeAvailable(I, AvailableFeatures))328        continue;329      Result.push_back(I);330    }331    return Result;332  }333  // Resolve opcode name -> opcode.334  const auto ResolveName = [&State](StringRef OpcodeName) -> unsigned {335    const auto &Map = State.getOpcodeNameToOpcodeIdxMapping();336    auto I = Map.find(OpcodeName);337    if (I != Map.end())338      return I->getSecond();339    return 0u;340  };341 342  SmallVector<StringRef, 2> Pieces;343  StringRef(OpcodeNames.getValue())344      .split(Pieces, ",", /* MaxSplit */ -1, /* KeepEmpty */ false);345  std::vector<unsigned> Result;346  Result.reserve(Pieces.size());347  for (const StringRef &OpcodeName : Pieces) {348    if (unsigned Opcode = ResolveName(OpcodeName))349      Result.push_back(Opcode);350    else351      ExitWithError(Twine("unknown opcode ").concat(OpcodeName));352  }353  return Result;354}355 356// Generates code snippets for opcode `Opcode`.357static Expected<std::vector<BenchmarkCode>>358generateSnippets(const LLVMState &State, unsigned Opcode,359                 const BitVector &ForbiddenRegs) {360  // Ignore instructions that we cannot run.361  if (const char *Reason =362          State.getExegesisTarget().getIgnoredOpcodeReasonOrNull(State, Opcode))363    return make_error<Failure>(Reason);364 365  const Instruction &Instr = State.getIC().getInstr(Opcode);366  const std::vector<InstructionTemplate> InstructionVariants =367      State.getExegesisTarget().generateInstructionVariants(368          Instr, MaxConfigsPerOpcode);369 370  SnippetGenerator::Options SnippetOptions;371  SnippetOptions.MaxConfigsPerOpcode = MaxConfigsPerOpcode;372  const std::unique_ptr<SnippetGenerator> Generator =373      State.getExegesisTarget().createSnippetGenerator(BenchmarkMode, State,374                                                       SnippetOptions);375  if (!Generator)376    ExitWithError("cannot create snippet generator");377 378  std::vector<BenchmarkCode> Benchmarks;379  for (const InstructionTemplate &Variant : InstructionVariants) {380    if (Benchmarks.size() >= MaxConfigsPerOpcode)381      break;382    if (auto Err = Generator->generateConfigurations(Variant, Benchmarks,383                                                     ForbiddenRegs))384      return std::move(Err);385  }386  return Benchmarks;387}388 389static void runBenchmarkConfigurations(390    const LLVMState &State, ArrayRef<BenchmarkCode> Configurations,391    ArrayRef<std::unique_ptr<const SnippetRepetitor>> Repetitors,392    const BenchmarkRunner &Runner) {393  assert(!Configurations.empty() && "Don't have any configurations to run.");394  std::optional<raw_fd_ostream> FileOstr;395  if (BenchmarkFile != "-") {396    int ResultFD = 0;397    // Create output file or open existing file and truncate it, once.398    ExitOnErr(errorCodeToError(openFileForWrite(BenchmarkFile, ResultFD,399                                                sys::fs::CD_CreateAlways,400                                                sys::fs::OF_TextWithCRLF)));401    FileOstr.emplace(ResultFD, true /*shouldClose*/);402  }403  raw_ostream &Ostr = FileOstr ? *FileOstr : outs();404 405  std::optional<ProgressMeter<>> Meter;406  if (BenchmarkMeasurementsPrintProgress)407    Meter.emplace(Configurations.size());408 409  SmallVector<unsigned, 2> MinInstructionCounts = {MinInstructions};410  if (RepetitionMode == Benchmark::MiddleHalfDuplicate ||411      RepetitionMode == Benchmark::MiddleHalfLoop)412    MinInstructionCounts.push_back(MinInstructions * 2);413 414  for (const BenchmarkCode &Conf : Configurations) {415    ProgressMeter<>::ProgressMeterStep MeterStep(Meter ? &*Meter : nullptr);416    SmallVector<Benchmark, 2> AllResults;417 418    for (const std::unique_ptr<const SnippetRepetitor> &Repetitor :419         Repetitors) {420      for (unsigned IterationRepetitions : MinInstructionCounts) {421        auto RC = ExitOnErr(Runner.getRunnableConfiguration(422            Conf, IterationRepetitions, LoopBodySize, *Repetitor));423        std::optional<StringRef> DumpFile;424        if (DumpObjectToDisk.getNumOccurrences())425          DumpFile = DumpObjectToDisk;426        const std::optional<int> BenchmarkCPU =427            BenchmarkProcessCPU == -1428                ? std::nullopt429                : std::optional(BenchmarkProcessCPU.getValue());430        auto [Err, BenchmarkResult] =431            Runner.runConfiguration(std::move(RC), DumpFile, BenchmarkCPU);432        if (Err) {433          // Errors from executing the snippets are fine.434          // All other errors are a framework issue and should fail.435          if (!Err.isA<SnippetExecutionFailure>())436            ExitOnErr(std::move(Err));437 438          BenchmarkResult.Error = toString(std::move(Err));439        }440        AllResults.push_back(std::move(BenchmarkResult));441      }442    }443 444    Benchmark &Result = AllResults.front();445 446    // If any of our measurements failed, pretend they all have failed.447    if (AllResults.size() > 1 &&448        any_of(AllResults, [](const Benchmark &R) {449          return R.Measurements.empty();450        }))451      Result.Measurements.clear();452 453    std::unique_ptr<ResultAggregator> ResultAgg =454        ResultAggregator::CreateAggregator(RepetitionMode);455    ResultAgg->AggregateResults(Result,456                                ArrayRef<Benchmark>(AllResults).drop_front());457 458    // With dummy counters, measurements are rather meaningless,459    // so drop them altogether.460    if (UseDummyPerfCounters)461      Result.Measurements.clear();462 463    ExitOnFileError(BenchmarkFile, Result.writeYamlTo(State, Ostr));464  }465}466 467void benchmarkMain() {468  if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure &&469      !UseDummyPerfCounters) {470#ifndef HAVE_LIBPFM471    ExitWithError(472        "benchmarking unavailable, LLVM was built without libpfm. You can "473        "pass --benchmark-phase=... to skip the actual benchmarking or "474        "--use-dummy-perf-counters to not query the kernel for real event "475        "counts.");476#else477    if (pfm::pfmInitialize())478      ExitWithError("cannot initialize libpfm");479#endif480  }481 482  InitializeAllExegesisTargets();483#define LLVM_EXEGESIS(TargetName)                                              \484  LLVMInitialize##TargetName##AsmPrinter();                                    \485  LLVMInitialize##TargetName##AsmParser();                                     \486  LLVMInitialize##TargetName##Disassembler();487#include "llvm/Config/TargetExegesis.def"488 489  const LLVMState State = ExitOnErr(490      LLVMState::Create(TripleName, MCPU, MAttr, UseDummyPerfCounters));491 492  // Preliminary check to ensure features needed for requested493  // benchmark mode are present on target CPU and/or OS.494  if (BenchmarkPhaseSelector == BenchmarkPhaseSelectorE::Measure)495    ExitOnErr(State.getExegesisTarget().checkFeatureSupport());496 497  if (ExecutionMode == BenchmarkRunner::ExecutionModeE::SubProcess &&498      UseDummyPerfCounters)499    ExitWithError("Dummy perf counters are not supported in the subprocess "500                  "execution mode.");501 502  const std::unique_ptr<BenchmarkRunner> Runner =503      ExitOnErr(State.getExegesisTarget().createBenchmarkRunner(504          BenchmarkMode, State, BenchmarkPhaseSelector, ExecutionMode,505          BenchmarkRepeatCount, ValidationCounters, ResultAggMode));506  if (!Runner) {507    ExitWithError("cannot create benchmark runner");508  }509 510  const auto Opcodes = getOpcodesOrDie(State);511  std::vector<BenchmarkCode> Configurations;512 513  MCRegister LoopRegister =514      State.getExegesisTarget().getDefaultLoopCounterRegister(515          State.getTargetMachine().getTargetTriple());516 517  if (Opcodes.empty()) {518    Configurations = ExitOnErr(readSnippets(State, SnippetsFile));519    for (const auto &Configuration : Configurations) {520      if (ExecutionMode != BenchmarkRunner::ExecutionModeE::SubProcess &&521          (Configuration.Key.MemoryMappings.size() != 0 ||522           Configuration.Key.MemoryValues.size() != 0 ||523           Configuration.Key.SnippetAddress != 0))524        ExitWithError("Memory and snippet address annotations are only "525                      "supported in subprocess "526                      "execution mode");527    }528    LoopRegister = Configurations[0].Key.LoopRegister;529  }530 531  SmallVector<std::unique_ptr<const SnippetRepetitor>, 2> Repetitors;532  if (RepetitionMode != Benchmark::RepetitionModeE::AggregateMin)533    Repetitors.emplace_back(534        SnippetRepetitor::Create(RepetitionMode, State, LoopRegister));535  else {536    for (Benchmark::RepetitionModeE RepMode :537         {Benchmark::RepetitionModeE::Duplicate,538          Benchmark::RepetitionModeE::Loop})539      Repetitors.emplace_back(540          SnippetRepetitor::Create(RepMode, State, LoopRegister));541  }542 543  BitVector AllReservedRegs;544  for (const std::unique_ptr<const SnippetRepetitor> &Repetitor : Repetitors)545    AllReservedRegs |= Repetitor->getReservedRegs();546 547  if (!Opcodes.empty()) {548    for (const unsigned Opcode : Opcodes) {549      // Ignore instructions without a sched class if550      // -ignore-invalid-sched-class is passed.551      if (IgnoreInvalidSchedClass &&552          State.getInstrInfo().get(Opcode).getSchedClass() == 0) {553        errs() << State.getInstrInfo().getName(Opcode)554               << ": ignoring instruction without sched class\n";555        continue;556      }557 558      auto ConfigsForInstr = generateSnippets(State, Opcode, AllReservedRegs);559      if (!ConfigsForInstr) {560        logAllUnhandledErrors(561            ConfigsForInstr.takeError(), errs(),562            Twine(State.getInstrInfo().getName(Opcode)).concat(": "));563        continue;564      }565      std::move(ConfigsForInstr->begin(), ConfigsForInstr->end(),566                std::back_inserter(Configurations));567    }568  }569 570  if (MinInstructions == 0) {571    ExitOnErr.setBanner("llvm-exegesis: ");572    ExitWithError("--min-instructions must be greater than zero");573  }574 575  // Write to standard output if file is not set.576  if (BenchmarkFile.empty())577    BenchmarkFile = "-";578 579  if (!Configurations.empty())580    runBenchmarkConfigurations(State, Configurations, Repetitors, *Runner);581 582  pfm::pfmTerminate();583}584 585// Prints the results of running analysis pass `Pass` to file `OutputFilename`586// if OutputFilename is non-empty.587template <typename Pass>588static void maybeRunAnalysis(const Analysis &Analyzer, const std::string &Name,589                             const std::string &OutputFilename) {590  if (OutputFilename.empty())591    return;592  if (OutputFilename != "-") {593    errs() << "Printing " << Name << " results to file '" << OutputFilename594           << "'\n";595  }596  std::error_code ErrorCode;597  raw_fd_ostream ClustersOS(OutputFilename, ErrorCode,598                            sys::fs::FA_Read | sys::fs::FA_Write);599  if (ErrorCode)600    ExitOnFileError(OutputFilename, errorCodeToError(ErrorCode));601  if (auto Err = Analyzer.run<Pass>(ClustersOS))602    ExitOnFileError(OutputFilename, std::move(Err));603}604 605static void filterPoints(MutableArrayRef<Benchmark> Points,606                         const MCInstrInfo &MCII) {607  if (AnalysisSnippetFilter == BenchmarkFilter::All)608    return;609 610  bool WantPointsWithMemOps = AnalysisSnippetFilter == BenchmarkFilter::WithMem;611  for (Benchmark &Point : Points) {612    if (!Point.Error.empty())613      continue;614    if (WantPointsWithMemOps ==615        any_of(Point.Key.Instructions, [&MCII](const MCInst &Inst) {616          const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode());617          return MCDesc.mayLoad() || MCDesc.mayStore();618        }))619      continue;620    Point.Error = "filtered out by user";621  }622}623 624static void analysisMain() {625  ExitOnErr.setBanner("llvm-exegesis: ");626  if (BenchmarkFile.empty())627    ExitWithError("--benchmarks-file must be set");628 629  if (AnalysisClustersOutputFile.empty() &&630      AnalysisInconsistenciesOutputFile.empty()) {631    ExitWithError(632        "for --mode=analysis: At least one of --analysis-clusters-output-file "633        "and --analysis-inconsistencies-output-file must be specified");634  }635 636  InitializeAllExegesisTargets();637#define LLVM_EXEGESIS(TargetName)                                              \638  LLVMInitialize##TargetName##AsmPrinter();                                    \639  LLVMInitialize##TargetName##Disassembler();640#include "llvm/Config/TargetExegesis.def"641 642  auto MemoryBuffer = ExitOnFileError(643      BenchmarkFile,644      errorOrToExpected(MemoryBuffer::getFile(BenchmarkFile, /*IsText=*/true)));645 646  const auto TriplesAndCpus = ExitOnFileError(647      BenchmarkFile,648      Benchmark::readTriplesAndCpusFromYamls(*MemoryBuffer));649  if (TriplesAndCpus.empty()) {650    errs() << "no benchmarks to analyze\n";651    return;652  }653  if (TriplesAndCpus.size() > 1) {654    ExitWithError("analysis file contains benchmarks from several CPUs. This "655                  "is unsupported.");656  }657  auto TripleAndCpu = *TriplesAndCpus.begin();658  if (AnalysisOverrideBenchmarksTripleAndCpu) {659    errs() << "overridding file CPU name (" << TripleAndCpu.CpuName660           << ") with provided tripled (" << TripleName << ") and CPU name ("661           << MCPU << ")\n";662    TripleAndCpu.LLVMTriple = TripleName;663    TripleAndCpu.CpuName = MCPU;664  }665  errs() << "using Triple '" << TripleAndCpu.LLVMTriple << "' and CPU '"666         << TripleAndCpu.CpuName << "'\n";667 668  // Read benchmarks.669  const LLVMState State = ExitOnErr(670      LLVMState::Create(TripleAndCpu.LLVMTriple, TripleAndCpu.CpuName));671  std::vector<Benchmark> Points = ExitOnFileError(672      BenchmarkFile, Benchmark::readYamls(State, *MemoryBuffer));673 674  outs() << "Parsed " << Points.size() << " benchmark points\n";675  if (Points.empty()) {676    errs() << "no benchmarks to analyze\n";677    return;678  }679  // FIXME: Merge points from several runs (latency and uops).680 681  filterPoints(Points, State.getInstrInfo());682 683  const auto Clustering = ExitOnErr(BenchmarkClustering::create(684      Points, AnalysisClusteringAlgorithm, AnalysisDbscanNumPoints,685      AnalysisClusteringEpsilon, &State.getSubtargetInfo(),686      &State.getInstrInfo()));687 688  const Analysis Analyzer(State, Clustering, AnalysisInconsistencyEpsilon,689                          AnalysisDisplayUnstableOpcodes);690 691  maybeRunAnalysis<Analysis::PrintClusters>(Analyzer, "analysis clusters",692                                            AnalysisClustersOutputFile);693  maybeRunAnalysis<Analysis::PrintSchedClassInconsistencies>(694      Analyzer, "sched class consistency analysis",695      AnalysisInconsistenciesOutputFile);696}697 698} // namespace exegesis699} // namespace llvm700 701int main(int Argc, char **Argv) {702  using namespace llvm;703 704  InitLLVM X(Argc, Argv);705 706  // Initialize targets so we can print them when flag --version is specified.707#define LLVM_EXEGESIS(TargetName)                                              \708  LLVMInitialize##TargetName##Target();                                        \709  LLVMInitialize##TargetName##TargetInfo();                                    \710  LLVMInitialize##TargetName##TargetMC();711#include "llvm/Config/TargetExegesis.def"712 713  // Register the Target and CPU printer for --version.714  cl::AddExtraVersionPrinter(sys::printDefaultTargetAndDetectedCPU);715 716  // Enable printing of available targets when flag --version is specified.717  cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);718 719  cl::HideUnrelatedOptions({&exegesis::Options, &exegesis::BenchmarkOptions,720                            &exegesis::AnalysisOptions});721 722  cl::ParseCommandLineOptions(Argc, Argv,723                              "llvm host machine instruction characteristics "724                              "measurment and analysis.\n");725 726  exegesis::ExitOnErr.setExitCodeMapper([](const Error &Err) {727    if (Err.isA<exegesis::ClusteringError>())728      return EXIT_SUCCESS;729    return EXIT_FAILURE;730  });731 732  if (exegesis::BenchmarkMode == exegesis::Benchmark::Unknown) {733    exegesis::analysisMain();734  } else {735    exegesis::benchmarkMain();736  }737  return EXIT_SUCCESS;738}739