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