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(std::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 llvm::SmallString<0> Buffer; 121 llvm::raw_svector_ostream OS(Buffer); 122 assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), 123 BC.LiveIns, BC.RegisterInitialValues, 124 GenerateInstructions(BC, kMinInstructionsForSnippet), OS); 125 const ExecutableFunction EF(State.createTargetMachine(), 126 getObjectFromBuffer(OS.str())); 127 const auto FnBytes = EF.getFunctionBytes(); 128 InstrBenchmark.AssembledSnippet.assign(FnBytes.begin(), FnBytes.end()); 129 } 130 131 // Assemble NumRepetitions instructions repetitions of the snippet for 132 // measurements. 133 const auto Code = GenerateInstructions(BC, InstrBenchmark.NumRepetitions); 134 135 llvm::object::OwningBinary<llvm::object::ObjectFile> ObjectFile; 136 if (DumpObjectToDisk) { 137 auto ObjectFilePath = writeObjectFile(BC, Code); 138 if (llvm::Error E = ObjectFilePath.takeError()) { 139 InstrBenchmark.Error = llvm::toString(std::move(E)); 140 return InstrBenchmark; 141 } 142 llvm::outs() << "Check generated assembly with: /usr/bin/objdump -d " 143 << *ObjectFilePath << "\n"; 144 ObjectFile = getObjectFromFile(*ObjectFilePath); 145 } else { 146 llvm::SmallString<0> Buffer; 147 llvm::raw_svector_ostream OS(Buffer); 148 assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), 149 BC.LiveIns, BC.RegisterInitialValues, Code, OS); 150 ObjectFile = getObjectFromBuffer(OS.str()); 151 } 152 153 const FunctionExecutorImpl Executor(State, std::move(ObjectFile), 154 Scratch.get()); 155 auto Measurements = runMeasurements(Executor); 156 if (llvm::Error E = Measurements.takeError()) { 157 InstrBenchmark.Error = llvm::toString(std::move(E)); 158 return InstrBenchmark; 159 } 160 InstrBenchmark.Measurements = std::move(*Measurements); 161 assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions"); 162 for (BenchmarkMeasure &BM : InstrBenchmark.Measurements) { 163 // Scale the measurements by instruction. 164 BM.PerInstructionValue /= InstrBenchmark.NumRepetitions; 165 // Scale the measurements by snippet. 166 BM.PerSnippetValue *= static_cast<double>(BC.Instructions.size()) / 167 InstrBenchmark.NumRepetitions; 168 } 169 170 return InstrBenchmark; 171 } 172 173 llvm::Expected<std::string> 174 BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC, 175 llvm::ArrayRef<llvm::MCInst> Code) const { 176 int ResultFD = 0; 177 llvm::SmallString<256> ResultPath; 178 if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile( 179 "snippet", "o", ResultFD, ResultPath))) 180 return std::move(E); 181 llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); 182 assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), 183 BC.LiveIns, BC.RegisterInitialValues, Code, OFS); 184 return ResultPath.str(); 185 } 186 187 BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {} 188 189 } // namespace exegesis 190 } // namespace llvm 191