blob: 01748d3c45ba38eb9f174698718550659b31d49b [file] [log] [blame]
Clement Courbetac74acd2018-04-04 11:37:06 +00001//===-- BenchmarkResult.cpp -------------------------------------*- C++ -*-===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Clement Courbetac74acd2018-04-04 11:37:06 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "BenchmarkResult.h"
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000010#include "BenchmarkRunner.h"
Clement Courbet53d35d22018-06-05 10:56:19 +000011#include "llvm/ADT/STLExtras.h"
Guillaume Chatelet9157bc92018-10-04 12:33:46 +000012#include "llvm/ADT/bit.h"
Clement Courbetac74acd2018-04-04 11:37:06 +000013#include "llvm/ADT/StringRef.h"
Clement Courbet4273e1e2018-06-15 07:30:45 +000014#include "llvm/ObjectYAML/YAML.h"
Clement Courbetac74acd2018-04-04 11:37:06 +000015#include "llvm/Support/FileOutputBuffer.h"
16#include "llvm/Support/FileSystem.h"
17#include "llvm/Support/Format.h"
18#include "llvm/Support/raw_ostream.h"
19
Guillaume Chatelet9157bc92018-10-04 12:33:46 +000020static constexpr const char kIntegerPrefix[] = "i_0x";
21static constexpr const char kDoublePrefix[] = "f_";
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000022static constexpr const char kInvalidOperand[] = "INVALID";
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000023
Fangrui Song32401af2018-10-22 17:10:47 +000024namespace llvm {
25
26namespace {
27
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000028// A mutable struct holding an LLVMState that can be passed through the
29// serialization process to encode/decode registers and instructions.
30struct YamlContext {
31 YamlContext(const exegesis::LLVMState &State)
32 : State(&State), ErrorStream(LastError) {}
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000033
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000034 void serializeMCInst(const llvm::MCInst &MCInst, llvm::raw_ostream &OS) {
35 OS << getInstrName(MCInst.getOpcode());
36 for (const auto &Op : MCInst) {
37 OS << ' ';
38 serializeMCOperand(Op, OS);
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000039 }
40 }
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000041
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000042 void deserializeMCInst(llvm::StringRef String, llvm::MCInst &Value) {
43 llvm::SmallVector<llvm::StringRef, 8> Pieces;
44 String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false);
45 if (Pieces.empty()) {
46 ErrorStream << "Unknown Instruction: '" << String << "'";
47 return;
48 }
49 bool ProcessOpcode = true;
50 for (llvm::StringRef Piece : Pieces) {
51 if (ProcessOpcode)
52 Value.setOpcode(getInstrOpcode(Piece));
53 else
54 Value.addOperand(deserializeMCOperand(Piece));
55 ProcessOpcode = false;
56 }
57 }
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000058
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000059 std::string &getLastError() { return ErrorStream.str(); }
60
Guillaume Chatelet345fae52018-09-25 15:15:54 +000061 llvm::raw_string_ostream &getErrorStream() { return ErrorStream; }
62
63 llvm::StringRef getRegName(unsigned RegNo) {
64 const llvm::StringRef RegName = State->getRegInfo().getName(RegNo);
65 if (RegName.empty())
66 ErrorStream << "No register with enum value" << RegNo;
67 return RegName;
68 }
69
70 unsigned getRegNo(llvm::StringRef RegName) {
71 const llvm::MCRegisterInfo &RegInfo = State->getRegInfo();
72 for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I)
73 if (RegInfo.getName(I) == RegName)
74 return I;
75 ErrorStream << "No register with name " << RegName;
76 return 0;
77 }
78
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000079private:
Guillaume Chatelet9157bc92018-10-04 12:33:46 +000080 void serializeIntegerOperand(llvm::raw_ostream &OS, int64_t Value) {
81 OS << kIntegerPrefix;
82 OS.write_hex(llvm::bit_cast<uint64_t>(Value));
83 }
84
85 bool tryDeserializeIntegerOperand(llvm::StringRef String, int64_t &Value) {
86 if (!String.consume_front(kIntegerPrefix))
87 return false;
88 return !String.consumeInteger(16, Value);
89 }
90
91 void serializeFPOperand(llvm::raw_ostream &OS, double Value) {
92 OS << kDoublePrefix << llvm::format("%la", Value);
93 }
94
95 bool tryDeserializeFPOperand(llvm::StringRef String, double &Value) {
96 if (!String.consume_front(kDoublePrefix))
97 return false;
98 char *EndPointer = nullptr;
99 Value = strtod(String.begin(), &EndPointer);
100 return EndPointer == String.end();
101 }
102
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000103 void serializeMCOperand(const llvm::MCOperand &MCOperand,
104 llvm::raw_ostream &OS) {
105 if (MCOperand.isReg()) {
106 OS << getRegName(MCOperand.getReg());
107 } else if (MCOperand.isImm()) {
Guillaume Chatelet9157bc92018-10-04 12:33:46 +0000108 serializeIntegerOperand(OS, MCOperand.getImm());
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000109 } else if (MCOperand.isFPImm()) {
Guillaume Chatelet9157bc92018-10-04 12:33:46 +0000110 serializeFPOperand(OS, MCOperand.getFPImm());
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000111 } else {
112 OS << kInvalidOperand;
113 }
114 }
115
116 llvm::MCOperand deserializeMCOperand(llvm::StringRef String) {
117 assert(!String.empty());
118 int64_t IntValue = 0;
119 double DoubleValue = 0;
Guillaume Chatelet9157bc92018-10-04 12:33:46 +0000120 if (tryDeserializeIntegerOperand(String, IntValue))
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000121 return llvm::MCOperand::createImm(IntValue);
Guillaume Chatelet9157bc92018-10-04 12:33:46 +0000122 if (tryDeserializeFPOperand(String, DoubleValue))
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000123 return llvm::MCOperand::createFPImm(DoubleValue);
124 if (unsigned RegNo = getRegNo(String))
125 return llvm::MCOperand::createReg(RegNo);
126 if (String != kInvalidOperand)
127 ErrorStream << "Unknown Operand: '" << String << "'";
128 return {};
129 }
130
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000131 llvm::StringRef getInstrName(unsigned InstrNo) {
132 const llvm::StringRef InstrName = State->getInstrInfo().getName(InstrNo);
133 if (InstrName.empty())
134 ErrorStream << "No opcode with enum value" << InstrNo;
135 return InstrName;
136 }
137
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000138 unsigned getInstrOpcode(llvm::StringRef InstrName) {
139 const llvm::MCInstrInfo &InstrInfo = State->getInstrInfo();
140 for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I)
141 if (InstrInfo.getName(I) == InstrName)
142 return I;
143 ErrorStream << "No opcode with name " << InstrName;
144 return 0;
145 }
146
Fangrui Song32401af2018-10-22 17:10:47 +0000147 const llvm::exegesis::LLVMState *State;
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000148 std::string LastError;
149 llvm::raw_string_ostream ErrorStream;
150};
Fangrui Song32401af2018-10-22 17:10:47 +0000151} // namespace
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000152
Clement Courbetac74acd2018-04-04 11:37:06 +0000153// Defining YAML traits for IO.
Clement Courbetac74acd2018-04-04 11:37:06 +0000154namespace yaml {
155
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000156static YamlContext &getTypedContext(void *Ctx) {
157 return *reinterpret_cast<YamlContext *>(Ctx);
158}
159
Clement Courbet53d35d22018-06-05 10:56:19 +0000160// std::vector<llvm::MCInst> will be rendered as a list.
161template <> struct SequenceElementTraits<llvm::MCInst> {
162 static const bool flow = false;
163};
164
165template <> struct ScalarTraits<llvm::MCInst> {
166
167 static void output(const llvm::MCInst &Value, void *Ctx,
168 llvm::raw_ostream &Out) {
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000169 getTypedContext(Ctx).serializeMCInst(Value, Out);
Clement Courbet53d35d22018-06-05 10:56:19 +0000170 }
171
172 static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000173 YamlContext &Context = getTypedContext(Ctx);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000174 Context.deserializeMCInst(Scalar, Value);
175 return Context.getLastError();
Clement Courbet53d35d22018-06-05 10:56:19 +0000176 }
177
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000178 // By default strings are quoted only when necessary.
179 // We force the use of single quotes for uniformity.
Clement Courbet53d35d22018-06-05 10:56:19 +0000180 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
181
182 static const bool flow = true;
183};
184
Clement Courbetac74acd2018-04-04 11:37:06 +0000185// std::vector<exegesis::Measure> will be rendered as a list.
186template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> {
187 static const bool flow = false;
188};
189
190// exegesis::Measure is rendererd as a flow instead of a list.
191// e.g. { "key": "the key", "value": 0123 }
192template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
193 static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
Clement Courbet28d4f852018-09-26 13:35:10 +0000194 Io.mapRequired("key", Obj.Key);
195 if (!Io.outputting()) {
196 // For backward compatibility, interpret debug_string as a key.
197 Io.mapOptional("debug_string", Obj.Key);
198 }
Clement Courbet684a5f62018-09-26 08:37:21 +0000199 Io.mapRequired("value", Obj.PerInstructionValue);
200 Io.mapOptional("per_snippet_value", Obj.PerSnippetValue);
Clement Courbetac74acd2018-04-04 11:37:06 +0000201 }
202 static const bool flow = true;
203};
204
Clement Courbet2cb97b92018-06-04 11:43:40 +0000205template <>
Clement Courbet62b34fa2018-06-06 09:42:36 +0000206struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
Clement Courbet2cb97b92018-06-04 11:43:40 +0000207 static void enumeration(IO &Io,
Clement Courbet62b34fa2018-06-06 09:42:36 +0000208 exegesis::InstructionBenchmark::ModeE &Value) {
209 Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown);
210 Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency);
211 Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops);
Clement Courbet2cb97b92018-06-04 11:43:40 +0000212 }
213};
214
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000215// std::vector<exegesis::RegisterValue> will be rendered as a list.
216template <> struct SequenceElementTraits<exegesis::RegisterValue> {
217 static const bool flow = false;
218};
219
220template <> struct ScalarTraits<exegesis::RegisterValue> {
221 static constexpr const unsigned kRadix = 16;
222 static constexpr const bool kSigned = false;
223
224 static void output(const exegesis::RegisterValue &RV, void *Ctx,
225 llvm::raw_ostream &Out) {
226 YamlContext &Context = getTypedContext(Ctx);
227 Out << Context.getRegName(RV.Register) << "=0x"
228 << RV.Value.toString(kRadix, kSigned);
229 }
230
231 static StringRef input(StringRef String, void *Ctx,
232 exegesis::RegisterValue &RV) {
233 llvm::SmallVector<llvm::StringRef, 2> Pieces;
234 String.split(Pieces, "=0x", /* MaxSplit */ -1,
235 /* KeepEmpty */ false);
236 YamlContext &Context = getTypedContext(Ctx);
237 if (Pieces.size() == 2) {
238 RV.Register = Context.getRegNo(Pieces[0]);
239 const unsigned BitsNeeded = llvm::APInt::getBitsNeeded(Pieces[1], kRadix);
240 RV.Value = llvm::APInt(BitsNeeded, Pieces[1], kRadix);
241 } else {
242 Context.getErrorStream()
243 << "Unknown initial register value: '" << String << "'";
244 }
245 return Context.getLastError();
246 }
247
248 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
249
250 static const bool flow = true;
251};
252
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000253template <>
254struct MappingContextTraits<exegesis::InstructionBenchmarkKey, YamlContext> {
255 static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj,
256 YamlContext &Context) {
257 Io.setContext(&Context);
Clement Courbet49fad1c2018-06-14 06:57:52 +0000258 Io.mapRequired("instructions", Obj.Instructions);
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000259 Io.mapOptional("config", Obj.Config);
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000260 Io.mapRequired("register_initial_values", Obj.RegisterInitialValues);
Clement Courbetac74acd2018-04-04 11:37:06 +0000261 }
262};
263
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000264template <>
265struct MappingContextTraits<exegesis::InstructionBenchmark, YamlContext> {
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000266 struct NormalizedBinary {
Clement Courbet4273e1e2018-06-15 07:30:45 +0000267 NormalizedBinary(IO &io) {}
268 NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
269 std::vector<uint8_t> denormalize(IO &) {
270 std::vector<uint8_t> Data;
271 std::string Str;
272 raw_string_ostream OSS(Str);
273 Binary.writeAsBinary(OSS);
274 OSS.flush();
275 Data.assign(Str.begin(), Str.end());
276 return Data;
277 }
278
279 BinaryRef Binary;
280 };
281
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000282 static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj,
283 YamlContext &Context) {
Clement Courbet62b34fa2018-06-06 09:42:36 +0000284 Io.mapRequired("mode", Obj.Mode);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000285 Io.mapRequired("key", Obj.Key, Context);
Clement Courbetac74acd2018-04-04 11:37:06 +0000286 Io.mapRequired("cpu_name", Obj.CpuName);
287 Io.mapRequired("llvm_triple", Obj.LLVMTriple);
288 Io.mapRequired("num_repetitions", Obj.NumRepetitions);
289 Io.mapRequired("measurements", Obj.Measurements);
290 Io.mapRequired("error", Obj.Error);
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000291 Io.mapOptional("info", Obj.Info);
Clement Courbet4273e1e2018-06-15 07:30:45 +0000292 // AssembledSnippet
293 MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString(
294 Io, Obj.AssembledSnippet);
295 Io.mapOptional("assembled_snippet", BinaryString->Binary);
Clement Courbetac74acd2018-04-04 11:37:06 +0000296 }
297};
298
299} // namespace yaml
Clement Courbetac74acd2018-04-04 11:37:06 +0000300
301namespace exegesis {
302
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000303llvm::Expected<InstructionBenchmark>
304InstructionBenchmark::readYaml(const LLVMState &State,
305 llvm::StringRef Filename) {
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000306 if (auto ExpectedMemoryBuffer =
307 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000308 llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
309 YamlContext Context(State);
310 InstructionBenchmark Benchmark;
311 if (Yin.setCurrentDocument())
312 llvm::yaml::yamlize(Yin, Benchmark, /*unused*/ true, Context);
313 if (!Context.getLastError().empty())
314 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000315 return Benchmark;
316 } else {
317 return ExpectedMemoryBuffer.takeError();
318 }
Clement Courbetac74acd2018-04-04 11:37:06 +0000319}
320
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000321llvm::Expected<std::vector<InstructionBenchmark>>
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000322InstructionBenchmark::readYamls(const LLVMState &State,
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000323 llvm::StringRef Filename) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000324 if (auto ExpectedMemoryBuffer =
325 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
326 llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
327 YamlContext Context(State);
328 std::vector<InstructionBenchmark> Benchmarks;
329 while (Yin.setCurrentDocument()) {
330 Benchmarks.emplace_back();
331 yamlize(Yin, Benchmarks.back(), /*unused*/ true, Context);
332 if (Yin.error())
333 return llvm::errorCodeToError(Yin.error());
334 if (!Context.getLastError().empty())
335 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
336 Yin.nextDocument();
337 }
338 return Benchmarks;
339 } else {
340 return ExpectedMemoryBuffer.takeError();
341 }
Clement Courbet7b7c27a2018-05-14 09:01:22 +0000342}
343
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000344void InstructionBenchmark::writeYamlTo(const LLVMState &State,
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000345 llvm::raw_ostream &OS) {
Clement Courbet70667692018-11-07 15:46:45 +0000346 llvm::yaml::Output Yout(OS, nullptr /*Ctx*/, 200 /*WrapColumn*/);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000347 YamlContext Context(State);
Guillaume Chatelet6078f822018-09-25 14:48:24 +0000348 Yout.beginDocuments();
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000349 llvm::yaml::yamlize(Yout, *this, /*unused*/ true, Context);
Guillaume Chatelet6078f822018-09-25 14:48:24 +0000350 Yout.endDocuments();
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000351}
352
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000353void InstructionBenchmark::readYamlFrom(const LLVMState &State,
Clement Courbet53d35d22018-06-05 10:56:19 +0000354 llvm::StringRef InputContent) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000355 llvm::yaml::Input Yin(InputContent);
356 YamlContext Context(State);
357 if (Yin.setCurrentDocument())
358 llvm::yaml::yamlize(Yin, *this, /*unused*/ true, Context);
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000359}
360
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000361llvm::Error InstructionBenchmark::writeYaml(const LLVMState &State,
362 const llvm::StringRef Filename) {
Clement Courbetac74acd2018-04-04 11:37:06 +0000363 if (Filename == "-") {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000364 writeYamlTo(State, llvm::outs());
Clement Courbetac74acd2018-04-04 11:37:06 +0000365 } else {
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000366 int ResultFD = 0;
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000367 if (auto E = llvm::errorCodeToError(
Zachary Turner1f67a3c2018-06-07 19:58:58 +0000368 openFileForWrite(Filename, ResultFD, llvm::sys::fs::CD_CreateAlways,
369 llvm::sys::fs::F_Text))) {
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000370 return E;
371 }
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000372 llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000373 writeYamlTo(State, Ostr);
Clement Courbetac74acd2018-04-04 11:37:06 +0000374 }
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000375 return llvm::Error::success();
Clement Courbetac74acd2018-04-04 11:37:06 +0000376}
377
Clement Courbet684a5f62018-09-26 08:37:21 +0000378void PerInstructionStats::push(const BenchmarkMeasure &BM) {
Clement Courbetae8ae5dc2018-05-24 12:41:02 +0000379 if (Key.empty())
380 Key = BM.Key;
381 assert(Key == BM.Key);
382 ++NumValues;
Clement Courbet684a5f62018-09-26 08:37:21 +0000383 SumValues += BM.PerInstructionValue;
384 MaxValue = std::max(MaxValue, BM.PerInstructionValue);
385 MinValue = std::min(MinValue, BM.PerInstructionValue);
Clement Courbetae8ae5dc2018-05-24 12:41:02 +0000386}
387
Clement Courbetac74acd2018-04-04 11:37:06 +0000388} // namespace exegesis
Fangrui Song32401af2018-10-22 17:10:47 +0000389} // namespace llvm