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