blob: c61d869a2a6766cdc63b6b9b8804c2d0d18b69a9 [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"
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 Chatelet083a0c162018-06-07 07:40:40 +000020static constexpr const char kIntegerFormat[] = "i_0x%" PRId64 "x";
21static constexpr const char kDoubleFormat[] = "f_%la";
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000022static constexpr const char kInvalidOperand[] = "INVALID";
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000023
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000024// A mutable struct holding an LLVMState that can be passed through the
25// serialization process to encode/decode registers and instructions.
26struct YamlContext {
27 YamlContext(const exegesis::LLVMState &State)
28 : State(&State), ErrorStream(LastError) {}
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000029
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000030 void serializeMCInst(const llvm::MCInst &MCInst, llvm::raw_ostream &OS) {
31 OS << getInstrName(MCInst.getOpcode());
32 for (const auto &Op : MCInst) {
33 OS << ' ';
34 serializeMCOperand(Op, OS);
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000035 }
36 }
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000037
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000038 void deserializeMCInst(llvm::StringRef String, llvm::MCInst &Value) {
39 llvm::SmallVector<llvm::StringRef, 8> Pieces;
40 String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false);
41 if (Pieces.empty()) {
42 ErrorStream << "Unknown Instruction: '" << String << "'";
43 return;
44 }
45 bool ProcessOpcode = true;
46 for (llvm::StringRef Piece : Pieces) {
47 if (ProcessOpcode)
48 Value.setOpcode(getInstrOpcode(Piece));
49 else
50 Value.addOperand(deserializeMCOperand(Piece));
51 ProcessOpcode = false;
52 }
53 }
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000054
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000055 std::string &getLastError() { return ErrorStream.str(); }
56
Guillaume Chatelet345fae52018-09-25 15:15:54 +000057 llvm::raw_string_ostream &getErrorStream() { return ErrorStream; }
58
59 llvm::StringRef getRegName(unsigned RegNo) {
60 const llvm::StringRef RegName = State->getRegInfo().getName(RegNo);
61 if (RegName.empty())
62 ErrorStream << "No register with enum value" << RegNo;
63 return RegName;
64 }
65
66 unsigned getRegNo(llvm::StringRef RegName) {
67 const llvm::MCRegisterInfo &RegInfo = State->getRegInfo();
68 for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I)
69 if (RegInfo.getName(I) == RegName)
70 return I;
71 ErrorStream << "No register with name " << RegName;
72 return 0;
73 }
74
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000075private:
76 void serializeMCOperand(const llvm::MCOperand &MCOperand,
77 llvm::raw_ostream &OS) {
78 if (MCOperand.isReg()) {
79 OS << getRegName(MCOperand.getReg());
80 } else if (MCOperand.isImm()) {
81 OS << llvm::format(kIntegerFormat, MCOperand.getImm());
82 } else if (MCOperand.isFPImm()) {
83 OS << llvm::format(kDoubleFormat, MCOperand.getFPImm());
84 } else {
85 OS << kInvalidOperand;
86 }
87 }
88
89 llvm::MCOperand deserializeMCOperand(llvm::StringRef String) {
90 assert(!String.empty());
91 int64_t IntValue = 0;
92 double DoubleValue = 0;
Simon Pilgrim92d02022018-10-03 14:51:09 +000093 if (String[0] == kIntegerFormat[0] &&
94 sscanf(String.data(), kIntegerFormat, &IntValue) == 1)
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000095 return llvm::MCOperand::createImm(IntValue);
Simon Pilgrim92d02022018-10-03 14:51:09 +000096 if (String[0] == kDoubleFormat[0] &&
97 sscanf(String.data(), kDoubleFormat, &DoubleValue) == 1)
Guillaume Chatelet55ad0872018-09-25 12:18:08 +000098 return llvm::MCOperand::createFPImm(DoubleValue);
99 if (unsigned RegNo = getRegNo(String))
100 return llvm::MCOperand::createReg(RegNo);
101 if (String != kInvalidOperand)
102 ErrorStream << "Unknown Operand: '" << String << "'";
103 return {};
104 }
105
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000106 llvm::StringRef getInstrName(unsigned InstrNo) {
107 const llvm::StringRef InstrName = State->getInstrInfo().getName(InstrNo);
108 if (InstrName.empty())
109 ErrorStream << "No opcode with enum value" << InstrNo;
110 return InstrName;
111 }
112
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000113 unsigned getInstrOpcode(llvm::StringRef InstrName) {
114 const llvm::MCInstrInfo &InstrInfo = State->getInstrInfo();
115 for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I)
116 if (InstrInfo.getName(I) == InstrName)
117 return I;
118 ErrorStream << "No opcode with name " << InstrName;
119 return 0;
120 }
121
122 const exegesis::LLVMState *State;
123 std::string LastError;
124 llvm::raw_string_ostream ErrorStream;
125};
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000126
Clement Courbetac74acd2018-04-04 11:37:06 +0000127// Defining YAML traits for IO.
128namespace llvm {
129namespace yaml {
130
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000131static YamlContext &getTypedContext(void *Ctx) {
132 return *reinterpret_cast<YamlContext *>(Ctx);
133}
134
Clement Courbet53d35d22018-06-05 10:56:19 +0000135// std::vector<llvm::MCInst> will be rendered as a list.
136template <> struct SequenceElementTraits<llvm::MCInst> {
137 static const bool flow = false;
138};
139
140template <> struct ScalarTraits<llvm::MCInst> {
141
142 static void output(const llvm::MCInst &Value, void *Ctx,
143 llvm::raw_ostream &Out) {
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000144 getTypedContext(Ctx).serializeMCInst(Value, Out);
Clement Courbet53d35d22018-06-05 10:56:19 +0000145 }
146
147 static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000148 YamlContext &Context = getTypedContext(Ctx);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000149 Context.deserializeMCInst(Scalar, Value);
150 return Context.getLastError();
Clement Courbet53d35d22018-06-05 10:56:19 +0000151 }
152
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000153 // By default strings are quoted only when necessary.
154 // We force the use of single quotes for uniformity.
Clement Courbet53d35d22018-06-05 10:56:19 +0000155 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
156
157 static const bool flow = true;
158};
159
Clement Courbetac74acd2018-04-04 11:37:06 +0000160// std::vector<exegesis::Measure> will be rendered as a list.
161template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> {
162 static const bool flow = false;
163};
164
165// exegesis::Measure is rendererd as a flow instead of a list.
166// e.g. { "key": "the key", "value": 0123 }
167template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
168 static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
Clement Courbet28d4f852018-09-26 13:35:10 +0000169 Io.mapRequired("key", Obj.Key);
170 if (!Io.outputting()) {
171 // For backward compatibility, interpret debug_string as a key.
172 Io.mapOptional("debug_string", Obj.Key);
173 }
Clement Courbet684a5f62018-09-26 08:37:21 +0000174 Io.mapRequired("value", Obj.PerInstructionValue);
175 Io.mapOptional("per_snippet_value", Obj.PerSnippetValue);
Clement Courbetac74acd2018-04-04 11:37:06 +0000176 }
177 static const bool flow = true;
178};
179
Clement Courbet2cb97b92018-06-04 11:43:40 +0000180template <>
Clement Courbet62b34fa2018-06-06 09:42:36 +0000181struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
Clement Courbet2cb97b92018-06-04 11:43:40 +0000182 static void enumeration(IO &Io,
Clement Courbet62b34fa2018-06-06 09:42:36 +0000183 exegesis::InstructionBenchmark::ModeE &Value) {
184 Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown);
185 Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency);
186 Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops);
Clement Courbet2cb97b92018-06-04 11:43:40 +0000187 }
188};
189
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000190// std::vector<exegesis::RegisterValue> will be rendered as a list.
191template <> struct SequenceElementTraits<exegesis::RegisterValue> {
192 static const bool flow = false;
193};
194
195template <> struct ScalarTraits<exegesis::RegisterValue> {
196 static constexpr const unsigned kRadix = 16;
197 static constexpr const bool kSigned = false;
198
199 static void output(const exegesis::RegisterValue &RV, void *Ctx,
200 llvm::raw_ostream &Out) {
201 YamlContext &Context = getTypedContext(Ctx);
202 Out << Context.getRegName(RV.Register) << "=0x"
203 << RV.Value.toString(kRadix, kSigned);
204 }
205
206 static StringRef input(StringRef String, void *Ctx,
207 exegesis::RegisterValue &RV) {
208 llvm::SmallVector<llvm::StringRef, 2> Pieces;
209 String.split(Pieces, "=0x", /* MaxSplit */ -1,
210 /* KeepEmpty */ false);
211 YamlContext &Context = getTypedContext(Ctx);
212 if (Pieces.size() == 2) {
213 RV.Register = Context.getRegNo(Pieces[0]);
214 const unsigned BitsNeeded = llvm::APInt::getBitsNeeded(Pieces[1], kRadix);
215 RV.Value = llvm::APInt(BitsNeeded, Pieces[1], kRadix);
216 } else {
217 Context.getErrorStream()
218 << "Unknown initial register value: '" << String << "'";
219 }
220 return Context.getLastError();
221 }
222
223 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
224
225 static const bool flow = true;
226};
227
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000228template <>
229struct MappingContextTraits<exegesis::InstructionBenchmarkKey, YamlContext> {
230 static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj,
231 YamlContext &Context) {
232 Io.setContext(&Context);
Clement Courbet49fad1c2018-06-14 06:57:52 +0000233 Io.mapRequired("instructions", Obj.Instructions);
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000234 Io.mapOptional("config", Obj.Config);
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000235 Io.mapRequired("register_initial_values", Obj.RegisterInitialValues);
Clement Courbetac74acd2018-04-04 11:37:06 +0000236 }
237};
238
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000239template <>
240struct MappingContextTraits<exegesis::InstructionBenchmark, YamlContext> {
Guillaume Chatelet345fae52018-09-25 15:15:54 +0000241 struct NormalizedBinary {
Clement Courbet4273e1e2018-06-15 07:30:45 +0000242 NormalizedBinary(IO &io) {}
243 NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
244 std::vector<uint8_t> denormalize(IO &) {
245 std::vector<uint8_t> Data;
246 std::string Str;
247 raw_string_ostream OSS(Str);
248 Binary.writeAsBinary(OSS);
249 OSS.flush();
250 Data.assign(Str.begin(), Str.end());
251 return Data;
252 }
253
254 BinaryRef Binary;
255 };
256
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000257 static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj,
258 YamlContext &Context) {
Clement Courbet62b34fa2018-06-06 09:42:36 +0000259 Io.mapRequired("mode", Obj.Mode);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000260 Io.mapRequired("key", Obj.Key, Context);
Clement Courbetac74acd2018-04-04 11:37:06 +0000261 Io.mapRequired("cpu_name", Obj.CpuName);
262 Io.mapRequired("llvm_triple", Obj.LLVMTriple);
263 Io.mapRequired("num_repetitions", Obj.NumRepetitions);
264 Io.mapRequired("measurements", Obj.Measurements);
265 Io.mapRequired("error", Obj.Error);
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000266 Io.mapOptional("info", Obj.Info);
Clement Courbet4273e1e2018-06-15 07:30:45 +0000267 // AssembledSnippet
268 MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString(
269 Io, Obj.AssembledSnippet);
270 Io.mapOptional("assembled_snippet", BinaryString->Binary);
Clement Courbetac74acd2018-04-04 11:37:06 +0000271 }
272};
273
274} // namespace yaml
275} // namespace llvm
276
277namespace exegesis {
278
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000279llvm::Expected<InstructionBenchmark>
280InstructionBenchmark::readYaml(const LLVMState &State,
281 llvm::StringRef Filename) {
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000282 if (auto ExpectedMemoryBuffer =
283 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000284 llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
285 YamlContext Context(State);
286 InstructionBenchmark Benchmark;
287 if (Yin.setCurrentDocument())
288 llvm::yaml::yamlize(Yin, Benchmark, /*unused*/ true, Context);
289 if (!Context.getLastError().empty())
290 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000291 return Benchmark;
292 } else {
293 return ExpectedMemoryBuffer.takeError();
294 }
Clement Courbetac74acd2018-04-04 11:37:06 +0000295}
296
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000297llvm::Expected<std::vector<InstructionBenchmark>>
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000298InstructionBenchmark::readYamls(const LLVMState &State,
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000299 llvm::StringRef Filename) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000300 if (auto ExpectedMemoryBuffer =
301 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
302 llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get());
303 YamlContext Context(State);
304 std::vector<InstructionBenchmark> Benchmarks;
305 while (Yin.setCurrentDocument()) {
306 Benchmarks.emplace_back();
307 yamlize(Yin, Benchmarks.back(), /*unused*/ true, Context);
308 if (Yin.error())
309 return llvm::errorCodeToError(Yin.error());
310 if (!Context.getLastError().empty())
311 return llvm::make_error<BenchmarkFailure>(Context.getLastError());
312 Yin.nextDocument();
313 }
314 return Benchmarks;
315 } else {
316 return ExpectedMemoryBuffer.takeError();
317 }
Clement Courbet7b7c27a2018-05-14 09:01:22 +0000318}
319
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000320void InstructionBenchmark::writeYamlTo(const LLVMState &State,
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000321 llvm::raw_ostream &OS) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000322 llvm::yaml::Output Yout(OS);
323 YamlContext Context(State);
Guillaume Chatelet6078f822018-09-25 14:48:24 +0000324 Yout.beginDocuments();
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000325 llvm::yaml::yamlize(Yout, *this, /*unused*/ true, Context);
Guillaume Chatelet6078f822018-09-25 14:48:24 +0000326 Yout.endDocuments();
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000327}
328
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000329void InstructionBenchmark::readYamlFrom(const LLVMState &State,
Clement Courbet53d35d22018-06-05 10:56:19 +0000330 llvm::StringRef InputContent) {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000331 llvm::yaml::Input Yin(InputContent);
332 YamlContext Context(State);
333 if (Yin.setCurrentDocument())
334 llvm::yaml::yamlize(Yin, *this, /*unused*/ true, Context);
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000335}
336
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000337llvm::Error InstructionBenchmark::writeYaml(const LLVMState &State,
338 const llvm::StringRef Filename) {
Clement Courbetac74acd2018-04-04 11:37:06 +0000339 if (Filename == "-") {
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000340 writeYamlTo(State, llvm::outs());
Clement Courbetac74acd2018-04-04 11:37:06 +0000341 } else {
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000342 int ResultFD = 0;
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000343 if (auto E = llvm::errorCodeToError(
Zachary Turner1f67a3c2018-06-07 19:58:58 +0000344 openFileForWrite(Filename, ResultFD, llvm::sys::fs::CD_CreateAlways,
345 llvm::sys::fs::F_Text))) {
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000346 return E;
347 }
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000348 llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
Guillaume Chatelet55ad0872018-09-25 12:18:08 +0000349 writeYamlTo(State, Ostr);
Clement Courbetac74acd2018-04-04 11:37:06 +0000350 }
Guillaume Chatelet8c91d4c2018-06-07 07:51:16 +0000351 return llvm::Error::success();
Clement Courbetac74acd2018-04-04 11:37:06 +0000352}
353
Clement Courbet684a5f62018-09-26 08:37:21 +0000354void PerInstructionStats::push(const BenchmarkMeasure &BM) {
Clement Courbetae8ae5dc2018-05-24 12:41:02 +0000355 if (Key.empty())
356 Key = BM.Key;
357 assert(Key == BM.Key);
358 ++NumValues;
Clement Courbet684a5f62018-09-26 08:37:21 +0000359 SumValues += BM.PerInstructionValue;
360 MaxValue = std::max(MaxValue, BM.PerInstructionValue);
361 MinValue = std::min(MinValue, BM.PerInstructionValue);
Clement Courbetae8ae5dc2018-05-24 12:41:02 +0000362}
363
Clement Courbetac74acd2018-04-04 11:37:06 +0000364} // namespace exegesis