blob: 4b91c6c3b3c04340035c145ace70c6177cf78ff9 [file] [log] [blame]
Clement Courbetac74acd2018-04-04 11:37:06 +00001//===-- BenchmarkResult.cpp -------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "BenchmarkResult.h"
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000011#include "BenchmarkRunner.h"
Clement Courbet53d35d22018-06-05 10:56:19 +000012#include "llvm/ADT/STLExtras.h"
Guillaume Chatelet9157bc92018-10-04 12:33:46 +000013#include "llvm/ADT/bit.h"
Clement Courbetac74acd2018-04-04 11:37:06 +000014#include "llvm/ADT/StringRef.h"
Clement Courbet4273e1e2018-06-15 07:30:45 +000015#include "llvm/ObjectYAML/YAML.h"
Clement Courbetac74acd2018-04-04 11:37:06 +000016#include "llvm/Support/FileOutputBuffer.h"
17#include "llvm/Support/FileSystem.h"
18#include "llvm/Support/Format.h"
19#include "llvm/Support/raw_ostream.h"
20
Guillaume Chatelet9157bc92018-10-04 12:33:46 +000021static constexpr const char kIntegerPrefix[] = "i_0x";
22static constexpr const char kDoublePrefix[] = "f_";
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000023static constexpr const char kInvalidOperand[] = "INVALID";
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000024
Fangrui Song32401af2018-10-22 17:10:47 +000025namespace llvm {
26
27namespace {
28
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000029// A mutable struct holding an LLVMState that can be passed through the
30// serialization process to encode/decode registers and instructions.
31struct YamlContext {
32 YamlContext(const exegesis::LLVMState &State)
33 : State(&State), ErrorStream(LastError) {}
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000034
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000035 void serializeMCInst(const llvm::MCInst &MCInst, llvm::raw_ostream &OS) {
36 OS << getInstrName(MCInst.getOpcode());
37 for (const auto &Op : MCInst) {
38 OS << ' ';
39 serializeMCOperand(Op, OS);
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000040 }
41 }
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000042
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000043 void deserializeMCInst(llvm::StringRef String, llvm::MCInst &Value) {
44 llvm::SmallVector<llvm::StringRef, 8> Pieces;
45 String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false);
46 if (Pieces.empty()) {
47 ErrorStream << "Unknown Instruction: '" << String << "'";
48 return;
49 }
50 bool ProcessOpcode = true;
51 for (llvm::StringRef Piece : Pieces) {
52 if (ProcessOpcode)
53 Value.setOpcode(getInstrOpcode(Piece));
54 else
55 Value.addOperand(deserializeMCOperand(Piece));
56 ProcessOpcode = false;
57 }
58 }
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000059
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000060 std::string &getLastError() { return ErrorStream.str(); }
61
Guillaume Chatelet345fae52018-09-25 15:15:54 +000062 llvm::raw_string_ostream &getErrorStream() { return ErrorStream; }
63
64 llvm::StringRef getRegName(unsigned RegNo) {
65 const llvm::StringRef RegName = State->getRegInfo().getName(RegNo);
66 if (RegName.empty())
67 ErrorStream << "No register with enum value" << RegNo;
68 return RegName;
69 }
70
71 unsigned getRegNo(llvm::StringRef RegName) {
72 const llvm::MCRegisterInfo &RegInfo = State->getRegInfo();
73 for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I)
74 if (RegInfo.getName(I) == RegName)
75 return I;
76 ErrorStream << "No register with name " << RegName;
77 return 0;
78 }
79
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000080private:
Guillaume Chatelet9157bc92018-10-04 12:33:46 +000081 void serializeIntegerOperand(llvm::raw_ostream &OS, int64_t Value) {
82 OS << kIntegerPrefix;
83 OS.write_hex(llvm::bit_cast<uint64_t>(Value));
84 }
85
86 bool tryDeserializeIntegerOperand(llvm::StringRef String, int64_t &Value) {
87 if (!String.consume_front(kIntegerPrefix))
88 return false;
89 return !String.consumeInteger(16, Value);
90 }
91
92 void serializeFPOperand(llvm::raw_ostream &OS, double Value) {
93 OS << kDoublePrefix << llvm::format("%la", Value);
94 }
95
96 bool tryDeserializeFPOperand(llvm::StringRef String, double &Value) {
97 if (!String.consume_front(kDoublePrefix))
98 return false;
99 char *EndPointer = nullptr;
100 Value = strtod(String.begin(), &EndPointer);
101 return EndPointer == String.end();
102 }
103
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000104 void serializeMCOperand(const llvm::MCOperand &MCOperand,
105 llvm::raw_ostream &OS) {
106 if (MCOperand.isReg()) {
107 OS << getRegName(MCOperand.getReg());
108 } else if (MCOperand.isImm()) {
Guillaume Chatelet9157bc92018-10-04 12:33:46 +0000109 serializeIntegerOperand(OS, MCOperand.getImm());
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000110 } else if (MCOperand.isFPImm()) {
Guillaume Chatelet9157bc92018-10-04 12:33:46 +0000111 serializeFPOperand(OS, MCOperand.getFPImm());
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000112 } else {
113 OS << kInvalidOperand;
114 }
115 }
116
117 llvm::MCOperand deserializeMCOperand(llvm::StringRef String) {
118 assert(!String.empty());
119 int64_t IntValue = 0;
120 double DoubleValue = 0;
Guillaume Chatelet9157bc92018-10-04 12:33:46 +0000121 if (tryDeserializeIntegerOperand(String, IntValue))
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000122 return llvm::MCOperand::createImm(IntValue);
Guillaume Chatelet9157bc92018-10-04 12:33:46 +0000123 if (tryDeserializeFPOperand(String, DoubleValue))
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000124 return llvm::MCOperand::createFPImm(DoubleValue);
125 if (unsigned RegNo = getRegNo(String))
126 return llvm::MCOperand::createReg(RegNo);
127 if (String != kInvalidOperand)
128 ErrorStream << "Unknown Operand: '" << String << "'";
129 return {};
130 }
131
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000132 llvm::StringRef getInstrName(unsigned InstrNo) {
133 const llvm::StringRef InstrName = State->getInstrInfo().getName(InstrNo);
134 if (InstrName.empty())
135 ErrorStream << "No opcode with enum value" << InstrNo;
136 return InstrName;
137 }
138
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000139 unsigned getInstrOpcode(llvm::StringRef InstrName) {
140 const llvm::MCInstrInfo &InstrInfo = State->getInstrInfo();
141 for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I)
142 if (InstrInfo.getName(I) == InstrName)
143 return I;
144 ErrorStream << "No opcode with name " << InstrName;
145 return 0;
146 }
147
Fangrui Song32401af2018-10-22 17:10:47 +0000148 const llvm::exegesis::LLVMState *State;
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000149 std::string LastError;
150 llvm::raw_string_ostream ErrorStream;
151};
Fangrui Song32401af2018-10-22 17:10:47 +0000152} // namespace
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000153
Clement Courbetac74acd2018-04-04 11:37:06 +0000154// Defining YAML traits for IO.
Clement Courbetac74acd2018-04-04 11:37:06 +0000155namespace yaml {
156
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000157static YamlContext &getTypedContext(void *Ctx) {
158 return *reinterpret_cast<YamlContext *>(Ctx);
159}
160
Clement Courbet53d35d22018-06-05 10:56:19 +0000161// std::vector<llvm::MCInst> will be rendered as a list.
162template <> struct SequenceElementTraits<llvm::MCInst> {
163 static const bool flow = false;
164};
165
166template <> struct ScalarTraits<llvm::MCInst> {
167
168 static void output(const llvm::MCInst &Value, void *Ctx,
169 llvm::raw_ostream &Out) {
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000170 getTypedContext(Ctx).serializeMCInst(Value, Out);
Clement Courbet53d35d22018-06-05 10:56:19 +0000171 }
172
173 static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000174 YamlContext &Context = getTypedContext(Ctx);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000175 Context.deserializeMCInst(Scalar, Value);
176 return Context.getLastError();
Clement Courbet53d35d22018-06-05 10:56:19 +0000177 }
178
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000179 // By default strings are quoted only when necessary.
180 // We force the use of single quotes for uniformity.
Clement Courbet53d35d22018-06-05 10:56:19 +0000181 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
182
183 static const bool flow = true;
184};
185
Clement Courbetac74acd2018-04-04 11:37:06 +0000186// std::vector<exegesis::Measure> will be rendered as a list.
187template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> {
188 static const bool flow = false;
189};
190
191// exegesis::Measure is rendererd as a flow instead of a list.
192// e.g. { "key": "the key", "value": 0123 }
193template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
194 static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
Clement Courbet28d4f852018-09-26 13:35:10 +0000195 Io.mapRequired("key", Obj.Key);
196 if (!Io.outputting()) {
197 // For backward compatibility, interpret debug_string as a key.
198 Io.mapOptional("debug_string", Obj.Key);
199 }
Clement Courbet684a5f62018-09-26 08:37:21 +0000200 Io.mapRequired("value", Obj.PerInstructionValue);
201 Io.mapOptional("per_snippet_value", Obj.PerSnippetValue);
Clement Courbetac74acd2018-04-04 11:37:06 +0000202 }
203 static const bool flow = true;
204};
205
Clement Courbet2cb97b92018-06-04 11:43:40 +0000206template <>
Clement Courbet62b34fa2018-06-06 09:42:36 +0000207struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
Clement Courbet2cb97b92018-06-04 11:43:40 +0000208 static void enumeration(IO &Io,
Clement Courbet62b34fa2018-06-06 09:42:36 +0000209 exegesis::InstructionBenchmark::ModeE &Value) {
210 Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown);
211 Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency);
212 Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops);
Clement Courbet2cb97b92018-06-04 11:43:40 +0000213 }
214};
215
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000216// std::vector<exegesis::RegisterValue> will be rendered as a list.
217template <> struct SequenceElementTraits<exegesis::RegisterValue> {
218 static const bool flow = false;
219};
220
221template <> struct ScalarTraits<exegesis::RegisterValue> {
222 static constexpr const unsigned kRadix = 16;
223 static constexpr const bool kSigned = false;
224
225 static void output(const exegesis::RegisterValue &RV, void *Ctx,
226 llvm::raw_ostream &Out) {
227 YamlContext &Context = getTypedContext(Ctx);
228 Out << Context.getRegName(RV.Register) << "=0x"
229 << RV.Value.toString(kRadix, kSigned);
230 }
231
232 static StringRef input(StringRef String, void *Ctx,
233 exegesis::RegisterValue &RV) {
234 llvm::SmallVector<llvm::StringRef, 2> Pieces;
235 String.split(Pieces, "=0x", /* MaxSplit */ -1,
236 /* KeepEmpty */ false);
237 YamlContext &Context = getTypedContext(Ctx);
238 if (Pieces.size() == 2) {
239 RV.Register = Context.getRegNo(Pieces[0]);
240 const unsigned BitsNeeded = llvm::APInt::getBitsNeeded(Pieces[1], kRadix);
241 RV.Value = llvm::APInt(BitsNeeded, Pieces[1], kRadix);
242 } else {
243 Context.getErrorStream()
244 << "Unknown initial register value: '" << String << "'";
245 }
246 return Context.getLastError();
247 }
248
249 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
250
251 static const bool flow = true;
252};
253
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000254template <>
255struct MappingContextTraits<exegesis::InstructionBenchmarkKey, YamlContext> {
256 static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj,
257 YamlContext &Context) {
258 Io.setContext(&Context);
Clement Courbet49fad1c2018-06-14 06:57:52 +0000259 Io.mapRequired("instructions", Obj.Instructions);
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000260 Io.mapOptional("config", Obj.Config);
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000261 Io.mapRequired("register_initial_values", Obj.RegisterInitialValues);
Clement Courbetac74acd2018-04-04 11:37:06 +0000262 }
263};
264
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000265template <>
266struct MappingContextTraits<exegesis::InstructionBenchmark, YamlContext> {
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000267 struct NormalizedBinary {
Clement Courbet4273e1e2018-06-15 07:30:45 +0000268 NormalizedBinary(IO &io) {}
269 NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
270 std::vector<uint8_t> denormalize(IO &) {
271 std::vector<uint8_t> Data;
272 std::string Str;
273 raw_string_ostream OSS(Str);
274 Binary.writeAsBinary(OSS);
275 OSS.flush();
276 Data.assign(Str.begin(), Str.end());
277 return Data;
278 }
279
280 BinaryRef Binary;
281 };
282
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000283 static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj,
284 YamlContext &Context) {
Clement Courbet62b34fa2018-06-06 09:42:36 +0000285 Io.mapRequired("mode", Obj.Mode);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000286 Io.mapRequired("key", Obj.Key, Context);
Clement Courbetac74acd2018-04-04 11:37:06 +0000287 Io.mapRequired("cpu_name", Obj.CpuName);
288 Io.mapRequired("llvm_triple", Obj.LLVMTriple);
289 Io.mapRequired("num_repetitions", Obj.NumRepetitions);
290 Io.mapRequired("measurements", Obj.Measurements);
291 Io.mapRequired("error", Obj.Error);
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000292 Io.mapOptional("info", Obj.Info);
Clement Courbet4273e1e2018-06-15 07:30:45 +0000293 // AssembledSnippet
294 MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString(
295 Io, Obj.AssembledSnippet);
296 Io.mapOptional("assembled_snippet", BinaryString->Binary);
Clement Courbetac74acd2018-04-04 11:37:06 +0000297 }
298};
299
300} // namespace yaml
Clement Courbetac74acd2018-04-04 11:37:06 +0000301
302namespace exegesis {
303
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000304llvm::Expected<InstructionBenchmark>
305InstructionBenchmark::readYaml(const LLVMState &State,
306 llvm::StringRef Filename) {
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000307 if (auto ExpectedMemoryBuffer =
308 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000309 llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
310 YamlContext Context(State);
311 InstructionBenchmark Benchmark;
312 if (Yin.setCurrentDocument())
313 llvm::yaml::yamlize(Yin, Benchmark, /*unused*/ true, Context);
314 if (!Context.getLastError().empty())
315 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000316 return Benchmark;
317 } else {
318 return ExpectedMemoryBuffer.takeError();
319 }
Clement Courbetac74acd2018-04-04 11:37:06 +0000320}
321
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000322llvm::Expected<std::vector<InstructionBenchmark>>
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000323InstructionBenchmark::readYamls(const LLVMState &State,
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000324 llvm::StringRef Filename) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000325 if (auto ExpectedMemoryBuffer =
326 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
327 llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
328 YamlContext Context(State);
329 std::vector<InstructionBenchmark> Benchmarks;
330 while (Yin.setCurrentDocument()) {
331 Benchmarks.emplace_back();
332 yamlize(Yin, Benchmarks.back(), /*unused*/ true, Context);
333 if (Yin.error())
334 return llvm::errorCodeToError(Yin.error());
335 if (!Context.getLastError().empty())
336 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
337 Yin.nextDocument();
338 }
339 return Benchmarks;
340 } else {
341 return ExpectedMemoryBuffer.takeError();
342 }
Clement Courbet7b7c27a2018-05-14 09:01:22 +0000343}
344
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000345void InstructionBenchmark::writeYamlTo(const LLVMState &State,
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000346 llvm::raw_ostream &OS) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000347 llvm::yaml::Output Yout(OS);
348 YamlContext Context(State);
Guillaume Chatelet6078f822018-09-25 14:48:24 +0000349 Yout.beginDocuments();
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000350 llvm::yaml::yamlize(Yout, *this, /*unused*/ true, Context);
Guillaume Chatelet6078f822018-09-25 14:48:24 +0000351 Yout.endDocuments();
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000352}
353
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000354void InstructionBenchmark::readYamlFrom(const LLVMState &State,
Clement Courbet53d35d22018-06-05 10:56:19 +0000355 llvm::StringRef InputContent) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000356 llvm::yaml::Input Yin(InputContent);
357 YamlContext Context(State);
358 if (Yin.setCurrentDocument())
359 llvm::yaml::yamlize(Yin, *this, /*unused*/ true, Context);
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000360}
361
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000362llvm::Error InstructionBenchmark::writeYaml(const LLVMState &State,
363 const llvm::StringRef Filename) {
Clement Courbetac74acd2018-04-04 11:37:06 +0000364 if (Filename == "-") {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000365 writeYamlTo(State, llvm::outs());
Clement Courbetac74acd2018-04-04 11:37:06 +0000366 } else {
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000367 int ResultFD = 0;
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000368 if (auto E = llvm::errorCodeToError(
Zachary Turner1f67a3c2018-06-07 19:58:58 +0000369 openFileForWrite(Filename, ResultFD, llvm::sys::fs::CD_CreateAlways,
370 llvm::sys::fs::F_Text))) {
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000371 return E;
372 }
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000373 llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000374 writeYamlTo(State, Ostr);
Clement Courbetac74acd2018-04-04 11:37:06 +0000375 }
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000376 return llvm::Error::success();
Clement Courbetac74acd2018-04-04 11:37:06 +0000377}
378
Clement Courbet684a5f62018-09-26 08:37:21 +0000379void PerInstructionStats::push(const BenchmarkMeasure &BM) {
Clement Courbetae8ae5dc2018-05-24 12:41:02 +0000380 if (Key.empty())
381 Key = BM.Key;
382 assert(Key == BM.Key);
383 ++NumValues;
Clement Courbet684a5f62018-09-26 08:37:21 +0000384 SumValues += BM.PerInstructionValue;
385 MaxValue = std::max(MaxValue, BM.PerInstructionValue);
386 MinValue = std::min(MinValue, BM.PerInstructionValue);
Clement Courbetae8ae5dc2018-05-24 12:41:02 +0000387}
388
Clement Courbetac74acd2018-04-04 11:37:06 +0000389} // namespace exegesis
Fangrui Song32401af2018-10-22 17:10:47 +0000390} // namespace llvm