1 //===-- BenchmarkRunner.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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include <array> 10 #include <string> 11 12 #include "Assembler.h" 13 #include "BenchmarkRunner.h" 14 #include "Error.h" 15 #include "MCInstrDescView.h" 16 #include "PerfHelper.h" 17 #include "llvm/ADT/StringExtras.h" 18 #include "llvm/ADT/StringRef.h" 19 #include "llvm/ADT/Twine.h" 20 #include "llvm/Support/CrashRecoveryContext.h" 21 #include "llvm/Support/FileSystem.h" 22 #include "llvm/Support/MemoryBuffer.h" 23 #include "llvm/Support/Program.h" 24 25 namespace llvm { 26 namespace exegesis { 27 28 BenchmarkRunner::BenchmarkRunner(const LLVMState &State, 29 InstructionBenchmark::ModeE Mode) 30 : State(State), Mode(Mode), Scratch(std::make_unique<ScratchSpace>()) {} 31 32 BenchmarkRunner::~BenchmarkRunner() = default; 33 34 namespace { 35 class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor { 36 public: 37 FunctionExecutorImpl(const LLVMState &State, 38 object::OwningBinary<object::ObjectFile> Obj, 39 BenchmarkRunner::ScratchSpace *Scratch) 40 : Function(State.createTargetMachine(), std::move(Obj)), 41 Scratch(Scratch) {} 42 43 private: 44 Expected<int64_t> runAndMeasure(const char *Counters) const override { 45 // We sum counts when there are several counters for a single ProcRes 46 // (e.g. P23 on SandyBridge). 47 int64_t CounterValue = 0; 48 SmallVector<StringRef, 2> CounterNames; 49 StringRef(Counters).split(CounterNames, '+'); 50 char *const ScratchPtr = Scratch->ptr(); 51 for (auto &CounterName : CounterNames) { 52 CounterName = CounterName.trim(); 53 pfm::PerfEvent PerfEvent(CounterName); 54 if (!PerfEvent.valid()) 55 report_fatal_error( 56 Twine("invalid perf event '").concat(CounterName).concat("'")); 57 pfm::Counter Counter(PerfEvent); 58 Scratch->clear(); 59 { 60 CrashRecoveryContext CRC; 61 CrashRecoveryContext::Enable(); 62 const bool Crashed = !CRC.RunSafely([this, &Counter, ScratchPtr]() { 63 Counter.start(); 64 this->Function(ScratchPtr); 65 Counter.stop(); 66 }); 67 CrashRecoveryContext::Disable(); 68 // FIXME: Better diagnosis. 69 if (Crashed) 70 return make_error<Failure>("snippet crashed while running"); 71 } 72 CounterValue += Counter.read(); 73 } 74 return CounterValue; 75 } 76 77 const ExecutableFunction Function; 78 BenchmarkRunner::ScratchSpace *const Scratch; 79 }; 80 } // namespace 81 82 InstructionBenchmark BenchmarkRunner::runConfiguration( 83 const BenchmarkCode &BC, unsigned NumRepetitions, 84 const SnippetRepetitor &Repetitor, bool DumpObjectToDisk) const { 85 InstructionBenchmark InstrBenchmark; 86 InstrBenchmark.Mode = Mode; 87 InstrBenchmark.CpuName = State.getTargetMachine().getTargetCPU(); 88 InstrBenchmark.LLVMTriple = 89 State.getTargetMachine().getTargetTriple().normalize(); 90 InstrBenchmark.NumRepetitions = NumRepetitions; 91 InstrBenchmark.Info = BC.Info; 92 93 const std::vector<MCInst> &Instructions = BC.Key.Instructions; 94 95 InstrBenchmark.Key = BC.Key; 96 97 // Assemble at least kMinInstructionsForSnippet instructions by repeating the 98 // snippet for debug/analysis. This is so that the user clearly understands 99 // that the inside instructions are repeated. 100 constexpr const int kMinInstructionsForSnippet = 16; 101 { 102 SmallString<0> Buffer; 103 raw_svector_ostream OS(Buffer); 104 assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), 105 BC.LiveIns, BC.Key.RegisterInitialValues, 106 Repetitor.Repeat(Instructions, kMinInstructionsForSnippet), 107 OS); 108 const ExecutableFunction EF(State.createTargetMachine(), 109 getObjectFromBuffer(OS.str())); 110 const auto FnBytes = EF.getFunctionBytes(); 111 InstrBenchmark.AssembledSnippet.assign(FnBytes.begin(), FnBytes.end()); 112 } 113 114 // Assemble NumRepetitions instructions repetitions of the snippet for 115 // measurements. 116 const auto Filler = 117 Repetitor.Repeat(Instructions, InstrBenchmark.NumRepetitions); 118 119 object::OwningBinary<object::ObjectFile> ObjectFile; 120 if (DumpObjectToDisk) { 121 auto ObjectFilePath = writeObjectFile(BC, Filler); 122 if (Error E = ObjectFilePath.takeError()) { 123 InstrBenchmark.Error = toString(std::move(E)); 124 return InstrBenchmark; 125 } 126 outs() << "Check generated assembly with: /usr/bin/objdump -d " 127 << *ObjectFilePath << "\n"; 128 ObjectFile = getObjectFromFile(*ObjectFilePath); 129 } else { 130 SmallString<0> Buffer; 131 raw_svector_ostream OS(Buffer); 132 assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), 133 BC.LiveIns, BC.Key.RegisterInitialValues, Filler, OS); 134 ObjectFile = getObjectFromBuffer(OS.str()); 135 } 136 137 const FunctionExecutorImpl Executor(State, std::move(ObjectFile), 138 Scratch.get()); 139 auto Measurements = runMeasurements(Executor); 140 if (Error E = Measurements.takeError()) { 141 InstrBenchmark.Error = toString(std::move(E)); 142 return InstrBenchmark; 143 } 144 InstrBenchmark.Measurements = std::move(*Measurements); 145 assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions"); 146 for (BenchmarkMeasure &BM : InstrBenchmark.Measurements) { 147 // Scale the measurements by instruction. 148 BM.PerInstructionValue /= InstrBenchmark.NumRepetitions; 149 // Scale the measurements by snippet. 150 BM.PerSnippetValue *= static_cast<double>(Instructions.size()) / 151 InstrBenchmark.NumRepetitions; 152 } 153 154 return InstrBenchmark; 155 } 156 157 Expected<std::string> 158 BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC, 159 const FillFunction &FillFunction) const { 160 int ResultFD = 0; 161 SmallString<256> ResultPath; 162 if (Error E = errorCodeToError( 163 sys::fs::createTemporaryFile("snippet", "o", ResultFD, ResultPath))) 164 return std::move(E); 165 raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); 166 assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), 167 BC.LiveIns, BC.Key.RegisterInitialValues, FillFunction, OFS); 168 return ResultPath.str(); 169 } 170 171 BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {} 172 173 } // namespace exegesis 174 } // namespace llvm 175