[yaml2obj/obj2yaml] - Add support for .stack_sizes sections.
[llvm-complete.git] / tools / llvm-exegesis / lib / BenchmarkResult.cpp
blob685f31a2b7d106a10950423b2c8cc3d6ccf5c5e3
1 //===-- BenchmarkResult.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 "BenchmarkResult.h"
10 #include "BenchmarkRunner.h"
11 #include "llvm/ADT/STLExtras.h"
12 #include "llvm/ADT/ScopeExit.h"
13 #include "llvm/ADT/StringMap.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/ADT/bit.h"
16 #include "llvm/ObjectYAML/YAML.h"
17 #include "llvm/Support/FileOutputBuffer.h"
18 #include "llvm/Support/FileSystem.h"
19 #include "llvm/Support/Format.h"
20 #include "llvm/Support/raw_ostream.h"
22 static constexpr const char kIntegerPrefix[] = "i_0x";
23 static constexpr const char kDoublePrefix[] = "f_";
24 static constexpr const char kInvalidOperand[] = "INVALID";
25 static constexpr llvm::StringLiteral kNoRegister("%noreg");
27 namespace llvm {
29 namespace {
31 // A mutable struct holding an LLVMState that can be passed through the
32 // serialization process to encode/decode registers and instructions.
33 struct YamlContext {
34 YamlContext(const exegesis::LLVMState &State)
35 : State(&State), ErrorStream(LastError),
36 OpcodeNameToOpcodeIdx(
37 generateOpcodeNameToOpcodeIdxMapping(State.getInstrInfo())),
38 RegNameToRegNo(generateRegNameToRegNoMapping(State.getRegInfo())) {}
40 static llvm::StringMap<unsigned>
41 generateOpcodeNameToOpcodeIdxMapping(const llvm::MCInstrInfo &InstrInfo) {
42 llvm::StringMap<unsigned> Map(InstrInfo.getNumOpcodes());
43 for (unsigned I = 0, E = InstrInfo.getNumOpcodes(); I < E; ++I)
44 Map[InstrInfo.getName(I)] = I;
45 assert(Map.size() == InstrInfo.getNumOpcodes() && "Size prediction failed");
46 return Map;
49 llvm::StringMap<unsigned>
50 generateRegNameToRegNoMapping(const llvm::MCRegisterInfo &RegInfo) {
51 llvm::StringMap<unsigned> Map(RegInfo.getNumRegs());
52 // Special-case RegNo 0, which would otherwise be spelled as ''.
53 Map[kNoRegister] = 0;
54 for (unsigned I = 1, E = RegInfo.getNumRegs(); I < E; ++I)
55 Map[RegInfo.getName(I)] = I;
56 assert(Map.size() == RegInfo.getNumRegs() && "Size prediction failed");
57 return Map;
60 void serializeMCInst(const llvm::MCInst &MCInst, llvm::raw_ostream &OS) {
61 OS << getInstrName(MCInst.getOpcode());
62 for (const auto &Op : MCInst) {
63 OS << ' ';
64 serializeMCOperand(Op, OS);
68 void deserializeMCInst(llvm::StringRef String, llvm::MCInst &Value) {
69 llvm::SmallVector<llvm::StringRef, 16> Pieces;
70 String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false);
71 if (Pieces.empty()) {
72 ErrorStream << "Unknown Instruction: '" << String << "'\n";
73 return;
75 bool ProcessOpcode = true;
76 for (llvm::StringRef Piece : Pieces) {
77 if (ProcessOpcode)
78 Value.setOpcode(getInstrOpcode(Piece));
79 else
80 Value.addOperand(deserializeMCOperand(Piece));
81 ProcessOpcode = false;
85 std::string &getLastError() { return ErrorStream.str(); }
87 llvm::raw_string_ostream &getErrorStream() { return ErrorStream; }
89 llvm::StringRef getRegName(unsigned RegNo) {
90 // Special case: RegNo 0 is NoRegister. We have to deal with it explicitly.
91 if (RegNo == 0)
92 return kNoRegister;
93 const llvm::StringRef RegName = State->getRegInfo().getName(RegNo);
94 if (RegName.empty())
95 ErrorStream << "No register with enum value '" << RegNo << "'\n";
96 return RegName;
99 llvm::Optional<unsigned> getRegNo(llvm::StringRef RegName) {
100 auto Iter = RegNameToRegNo.find(RegName);
101 if (Iter != RegNameToRegNo.end())
102 return Iter->second;
103 ErrorStream << "No register with name '" << RegName << "'\n";
104 return llvm::None;
107 private:
108 void serializeIntegerOperand(llvm::raw_ostream &OS, int64_t Value) {
109 OS << kIntegerPrefix;
110 OS.write_hex(llvm::bit_cast<uint64_t>(Value));
113 bool tryDeserializeIntegerOperand(llvm::StringRef String, int64_t &Value) {
114 if (!String.consume_front(kIntegerPrefix))
115 return false;
116 return !String.consumeInteger(16, Value);
119 void serializeFPOperand(llvm::raw_ostream &OS, double Value) {
120 OS << kDoublePrefix << llvm::format("%la", Value);
123 bool tryDeserializeFPOperand(llvm::StringRef String, double &Value) {
124 if (!String.consume_front(kDoublePrefix))
125 return false;
126 char *EndPointer = nullptr;
127 Value = strtod(String.begin(), &EndPointer);
128 return EndPointer == String.end();
131 void serializeMCOperand(const llvm::MCOperand &MCOperand,
132 llvm::raw_ostream &OS) {
133 if (MCOperand.isReg()) {
134 OS << getRegName(MCOperand.getReg());
135 } else if (MCOperand.isImm()) {
136 serializeIntegerOperand(OS, MCOperand.getImm());
137 } else if (MCOperand.isFPImm()) {
138 serializeFPOperand(OS, MCOperand.getFPImm());
139 } else {
140 OS << kInvalidOperand;
144 llvm::MCOperand deserializeMCOperand(llvm::StringRef String) {
145 assert(!String.empty());
146 int64_t IntValue = 0;
147 double DoubleValue = 0;
148 if (tryDeserializeIntegerOperand(String, IntValue))
149 return llvm::MCOperand::createImm(IntValue);
150 if (tryDeserializeFPOperand(String, DoubleValue))
151 return llvm::MCOperand::createFPImm(DoubleValue);
152 if (auto RegNo = getRegNo(String))
153 return llvm::MCOperand::createReg(*RegNo);
154 if (String != kInvalidOperand)
155 ErrorStream << "Unknown Operand: '" << String << "'\n";
156 return {};
159 llvm::StringRef getInstrName(unsigned InstrNo) {
160 const llvm::StringRef InstrName = State->getInstrInfo().getName(InstrNo);
161 if (InstrName.empty())
162 ErrorStream << "No opcode with enum value '" << InstrNo << "'\n";
163 return InstrName;
166 unsigned getInstrOpcode(llvm::StringRef InstrName) {
167 auto Iter = OpcodeNameToOpcodeIdx.find(InstrName);
168 if (Iter != OpcodeNameToOpcodeIdx.end())
169 return Iter->second;
170 ErrorStream << "No opcode with name '" << InstrName << "'\n";
171 return 0;
174 const llvm::exegesis::LLVMState *State;
175 std::string LastError;
176 llvm::raw_string_ostream ErrorStream;
177 const llvm::StringMap<unsigned> OpcodeNameToOpcodeIdx;
178 const llvm::StringMap<unsigned> RegNameToRegNo;
180 } // namespace
182 // Defining YAML traits for IO.
183 namespace yaml {
185 static YamlContext &getTypedContext(void *Ctx) {
186 return *reinterpret_cast<YamlContext *>(Ctx);
189 // std::vector<llvm::MCInst> will be rendered as a list.
190 template <> struct SequenceElementTraits<llvm::MCInst> {
191 static const bool flow = false;
194 template <> struct ScalarTraits<llvm::MCInst> {
196 static void output(const llvm::MCInst &Value, void *Ctx,
197 llvm::raw_ostream &Out) {
198 getTypedContext(Ctx).serializeMCInst(Value, Out);
201 static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
202 YamlContext &Context = getTypedContext(Ctx);
203 Context.deserializeMCInst(Scalar, Value);
204 return Context.getLastError();
207 // By default strings are quoted only when necessary.
208 // We force the use of single quotes for uniformity.
209 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
211 static const bool flow = true;
214 // std::vector<exegesis::Measure> will be rendered as a list.
215 template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> {
216 static const bool flow = false;
219 // exegesis::Measure is rendererd as a flow instead of a list.
220 // e.g. { "key": "the key", "value": 0123 }
221 template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
222 static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
223 Io.mapRequired("key", Obj.Key);
224 if (!Io.outputting()) {
225 // For backward compatibility, interpret debug_string as a key.
226 Io.mapOptional("debug_string", Obj.Key);
228 Io.mapRequired("value", Obj.PerInstructionValue);
229 Io.mapOptional("per_snippet_value", Obj.PerSnippetValue);
231 static const bool flow = true;
234 template <>
235 struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
236 static void enumeration(IO &Io,
237 exegesis::InstructionBenchmark::ModeE &Value) {
238 Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown);
239 Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency);
240 Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops);
241 Io.enumCase(Value, "inverse_throughput",
242 exegesis::InstructionBenchmark::InverseThroughput);
246 // std::vector<exegesis::RegisterValue> will be rendered as a list.
247 template <> struct SequenceElementTraits<exegesis::RegisterValue> {
248 static const bool flow = false;
251 template <> struct ScalarTraits<exegesis::RegisterValue> {
252 static constexpr const unsigned kRadix = 16;
253 static constexpr const bool kSigned = false;
255 static void output(const exegesis::RegisterValue &RV, void *Ctx,
256 llvm::raw_ostream &Out) {
257 YamlContext &Context = getTypedContext(Ctx);
258 Out << Context.getRegName(RV.Register) << "=0x"
259 << RV.Value.toString(kRadix, kSigned);
262 static StringRef input(StringRef String, void *Ctx,
263 exegesis::RegisterValue &RV) {
264 llvm::SmallVector<llvm::StringRef, 2> Pieces;
265 String.split(Pieces, "=0x", /* MaxSplit */ -1,
266 /* KeepEmpty */ false);
267 YamlContext &Context = getTypedContext(Ctx);
268 llvm::Optional<unsigned> RegNo;
269 if (Pieces.size() == 2 && (RegNo = Context.getRegNo(Pieces[0]))) {
270 RV.Register = *RegNo;
271 const unsigned BitsNeeded = llvm::APInt::getBitsNeeded(Pieces[1], kRadix);
272 RV.Value = llvm::APInt(BitsNeeded, Pieces[1], kRadix);
273 } else {
274 Context.getErrorStream()
275 << "Unknown initial register value: '" << String << "'";
277 return Context.getLastError();
280 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
282 static const bool flow = true;
285 template <>
286 struct MappingContextTraits<exegesis::InstructionBenchmarkKey, YamlContext> {
287 static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj,
288 YamlContext &Context) {
289 Io.setContext(&Context);
290 Io.mapRequired("instructions", Obj.Instructions);
291 Io.mapOptional("config", Obj.Config);
292 Io.mapRequired("register_initial_values", Obj.RegisterInitialValues);
296 template <>
297 struct MappingContextTraits<exegesis::InstructionBenchmark, YamlContext> {
298 struct NormalizedBinary {
299 NormalizedBinary(IO &io) {}
300 NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
301 std::vector<uint8_t> denormalize(IO &) {
302 std::vector<uint8_t> Data;
303 std::string Str;
304 raw_string_ostream OSS(Str);
305 Binary.writeAsBinary(OSS);
306 OSS.flush();
307 Data.assign(Str.begin(), Str.end());
308 return Data;
311 BinaryRef Binary;
314 static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj,
315 YamlContext &Context) {
316 Io.mapRequired("mode", Obj.Mode);
317 Io.mapRequired("key", Obj.Key, Context);
318 Io.mapRequired("cpu_name", Obj.CpuName);
319 Io.mapRequired("llvm_triple", Obj.LLVMTriple);
320 Io.mapRequired("num_repetitions", Obj.NumRepetitions);
321 Io.mapRequired("measurements", Obj.Measurements);
322 Io.mapRequired("error", Obj.Error);
323 Io.mapOptional("info", Obj.Info);
324 // AssembledSnippet
325 MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString(
326 Io, Obj.AssembledSnippet);
327 Io.mapOptional("assembled_snippet", BinaryString->Binary);
331 } // namespace yaml
333 namespace exegesis {
335 llvm::Expected<InstructionBenchmark>
336 InstructionBenchmark::readYaml(const LLVMState &State,
337 llvm::StringRef Filename) {
338 if (auto ExpectedMemoryBuffer =
339 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
340 llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
341 YamlContext Context(State);
342 InstructionBenchmark Benchmark;
343 if (Yin.setCurrentDocument())
344 llvm::yaml::yamlize(Yin, Benchmark, /*unused*/ true, Context);
345 if (!Context.getLastError().empty())
346 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
347 return Benchmark;
348 } else {
349 return ExpectedMemoryBuffer.takeError();
353 llvm::Expected<std::vector<InstructionBenchmark>>
354 InstructionBenchmark::readYamls(const LLVMState &State,
355 llvm::StringRef Filename) {
356 if (auto ExpectedMemoryBuffer =
357 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
358 llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
359 YamlContext Context(State);
360 std::vector<InstructionBenchmark> Benchmarks;
361 while (Yin.setCurrentDocument()) {
362 Benchmarks.emplace_back();
363 yamlize(Yin, Benchmarks.back(), /*unused*/ true, Context);
364 if (Yin.error())
365 return llvm::errorCodeToError(Yin.error());
366 if (!Context.getLastError().empty())
367 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
368 Yin.nextDocument();
370 return Benchmarks;
371 } else {
372 return ExpectedMemoryBuffer.takeError();
376 llvm::Error InstructionBenchmark::writeYamlTo(const LLVMState &State,
377 llvm::raw_ostream &OS) {
378 auto Cleanup = make_scope_exit([&] { OS.flush(); });
379 llvm::yaml::Output Yout(OS, nullptr /*Ctx*/, 200 /*WrapColumn*/);
380 YamlContext Context(State);
381 Yout.beginDocuments();
382 llvm::yaml::yamlize(Yout, *this, /*unused*/ true, Context);
383 if (!Context.getLastError().empty())
384 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
385 Yout.endDocuments();
386 return Error::success();
389 llvm::Error InstructionBenchmark::readYamlFrom(const LLVMState &State,
390 llvm::StringRef InputContent) {
391 llvm::yaml::Input Yin(InputContent);
392 YamlContext Context(State);
393 if (Yin.setCurrentDocument())
394 llvm::yaml::yamlize(Yin, *this, /*unused*/ true, Context);
395 if (!Context.getLastError().empty())
396 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
397 return Error::success();
400 llvm::Error InstructionBenchmark::writeYaml(const LLVMState &State,
401 const llvm::StringRef Filename) {
402 if (Filename == "-") {
403 if (auto Err = writeYamlTo(State, llvm::outs()))
404 return Err;
405 } else {
406 int ResultFD = 0;
407 if (auto E = llvm::errorCodeToError(
408 openFileForWrite(Filename, ResultFD, llvm::sys::fs::CD_CreateAlways,
409 llvm::sys::fs::OF_Text))) {
410 return E;
412 llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
413 if (auto Err = writeYamlTo(State, Ostr))
414 return Err;
416 return llvm::Error::success();
419 void PerInstructionStats::push(const BenchmarkMeasure &BM) {
420 if (Key.empty())
421 Key = BM.Key;
422 assert(Key == BM.Key);
423 ++NumValues;
424 SumValues += BM.PerInstructionValue;
425 MaxValue = std::max(MaxValue, BM.PerInstructionValue);
426 MinValue = std::min(MinValue, BM.PerInstructionValue);
429 } // namespace exegesis
430 } // namespace llvm