[yaml2obj/obj2yaml] - Add support for .stack_sizes sections.
[llvm-complete.git] / tools / llvm-exegesis / lib / BenchmarkRunner.cpp
blob38abf3eefa3e18e37e2fd2ac23ffc12d05511513
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 //===----------------------------------------------------------------------===//
9 #include <array>
10 #include <string>
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"
24 namespace llvm {
25 namespace exegesis {
27 BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S)
28 : llvm::StringError(S, llvm::inconvertibleErrorCode()) {}
30 BenchmarkRunner::BenchmarkRunner(const LLVMState &State,
31 InstructionBenchmark::ModeE Mode)
32 : State(State), Mode(Mode), Scratch(std::make_unique<ScratchSpace>()) {}
34 BenchmarkRunner::~BenchmarkRunner() = default;
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;
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) {}
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();
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");
88 CounterValue += Counter.read();
90 return CounterValue;
93 const ExecutableFunction Function;
94 BenchmarkRunner::ScratchSpace *const Scratch;
96 } // namespace
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;
110 const std::vector<llvm::MCInst> &Instructions = BC.Instructions;
112 InstrBenchmark.Key.Instructions = Instructions;
113 InstrBenchmark.Key.RegisterInitialValues = BC.RegisterInitialValues;
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;
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());
131 // Assemble NumRepetitions instructions repetitions of the snippet for
132 // measurements.
133 const auto Code = GenerateInstructions(BC, InstrBenchmark.NumRepetitions);
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;
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());
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;
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;
170 return InstrBenchmark;
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();
187 BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {}
189 } // namespace exegesis
190 } // namespace llvm