blob: aac93abcbe8eef0822801ee2b5155f4fd309abf8 [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"
Clement Courbet53d35d22018-06-05 10:56:19 +000011#include "llvm/ADT/STLExtras.h"
Clement Courbetac74acd2018-04-04 11:37:06 +000012#include "llvm/ADT/StringRef.h"
13#include "llvm/Support/FileOutputBuffer.h"
14#include "llvm/Support/FileSystem.h"
15#include "llvm/Support/Format.h"
16#include "llvm/Support/raw_ostream.h"
17
Guillaume Chatelet083a0c162018-06-07 07:40:40 +000018static constexpr const char kIntegerFormat[] = "i_0x%" PRId64 "x";
19static constexpr const char kDoubleFormat[] = "f_%la";
20
21static void serialize(const exegesis::BenchmarkResultContext &Context,
22 const llvm::MCOperand &MCOperand, llvm::raw_ostream &OS) {
23 if (MCOperand.isReg()) {
24 OS << Context.getRegName(MCOperand.getReg());
25 } else if (MCOperand.isImm()) {
26 OS << llvm::format(kIntegerFormat, MCOperand.getImm());
27 } else if (MCOperand.isFPImm()) {
28 OS << llvm::format(kDoubleFormat, MCOperand.getFPImm());
29 } else {
30 OS << "INVALID";
31 }
32}
33
34static void serialize(const exegesis::BenchmarkResultContext &Context,
35 const llvm::MCInst &MCInst, llvm::raw_ostream &OS) {
36 OS << Context.getInstrName(MCInst.getOpcode());
37 for (const auto &Op : MCInst) {
38 OS << ' ';
39 serialize(Context, Op, OS);
40 }
41}
42
43static llvm::MCOperand
44deserialize(const exegesis::BenchmarkResultContext &Context,
45 llvm::StringRef String) {
46 assert(!String.empty());
47 int64_t IntValue = 0;
48 double DoubleValue = 0;
49 if (sscanf(String.data(), kIntegerFormat, &IntValue) == 1)
50 return llvm::MCOperand::createImm(IntValue);
51 if (sscanf(String.data(), kDoubleFormat, &DoubleValue) == 1)
52 return llvm::MCOperand::createFPImm(DoubleValue);
53 if (unsigned RegNo = Context.getRegNo(String)) // Returns 0 if invalid.
54 return llvm::MCOperand::createReg(RegNo);
55 return {};
56}
57
58static llvm::StringRef
59deserialize(const exegesis::BenchmarkResultContext &Context,
60 llvm::StringRef String, llvm::MCInst &Value) {
61 llvm::SmallVector<llvm::StringRef, 8> Pieces;
62 String.split(Pieces, " ");
63 if (Pieces.empty())
64 return "Invalid Instruction";
65 bool ProcessOpcode = true;
66 for (llvm::StringRef Piece : Pieces) {
67 if (ProcessOpcode) {
68 ProcessOpcode = false;
69 Value.setOpcode(Context.getInstrOpcode(Piece));
70 if (Value.getOpcode() == 0)
71 return "Unknown Opcode Name";
72 } else {
73 Value.addOperand(deserialize(Context, Piece));
74 }
75 }
76 return {};
77}
78
79// YAML IO requires a mutable pointer to Context but we guarantee to not
80// modify it.
81static void *getUntypedContext(const exegesis::BenchmarkResultContext &Ctx) {
82 return const_cast<exegesis::BenchmarkResultContext *>(&Ctx);
83}
84
85static const exegesis::BenchmarkResultContext &getTypedContext(void *Ctx) {
86 assert(Ctx);
87 return *static_cast<const exegesis::BenchmarkResultContext *>(Ctx);
88}
89
Clement Courbetac74acd2018-04-04 11:37:06 +000090// Defining YAML traits for IO.
91namespace llvm {
92namespace yaml {
93
Clement Courbet53d35d22018-06-05 10:56:19 +000094// std::vector<llvm::MCInst> will be rendered as a list.
95template <> struct SequenceElementTraits<llvm::MCInst> {
96 static const bool flow = false;
97};
98
99template <> struct ScalarTraits<llvm::MCInst> {
100
101 static void output(const llvm::MCInst &Value, void *Ctx,
102 llvm::raw_ostream &Out) {
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000103 serialize(getTypedContext(Ctx), Value, Out);
Clement Courbet53d35d22018-06-05 10:56:19 +0000104 }
105
106 static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000107 return deserialize(getTypedContext(Ctx), Scalar, Value);
Clement Courbet53d35d22018-06-05 10:56:19 +0000108 }
109
110 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
111
112 static const bool flow = true;
113};
114
Clement Courbetac74acd2018-04-04 11:37:06 +0000115// std::vector<exegesis::Measure> will be rendered as a list.
116template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> {
117 static const bool flow = false;
118};
119
120// exegesis::Measure is rendererd as a flow instead of a list.
121// e.g. { "key": "the key", "value": 0123 }
122template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
123 static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
124 Io.mapRequired("key", Obj.Key);
125 Io.mapRequired("value", Obj.Value);
126 Io.mapOptional("debug_string", Obj.DebugString);
127 }
128 static const bool flow = true;
129};
130
Clement Courbet2cb97b92018-06-04 11:43:40 +0000131template <>
Clement Courbet62b34fa2018-06-06 09:42:36 +0000132struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
Clement Courbet2cb97b92018-06-04 11:43:40 +0000133 static void enumeration(IO &Io,
Clement Courbet62b34fa2018-06-06 09:42:36 +0000134 exegesis::InstructionBenchmark::ModeE &Value) {
135 Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown);
136 Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency);
137 Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops);
Clement Courbet2cb97b92018-06-04 11:43:40 +0000138 }
139};
140
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000141template <> struct MappingTraits<exegesis::InstructionBenchmarkKey> {
142 static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj) {
143 Io.mapRequired("opcode_name", Obj.OpcodeName);
Clement Courbet53d35d22018-06-05 10:56:19 +0000144 Io.mapOptional("instructions", Obj.Instructions);
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000145 Io.mapOptional("config", Obj.Config);
Clement Courbetac74acd2018-04-04 11:37:06 +0000146 }
147};
148
149template <> struct MappingTraits<exegesis::InstructionBenchmark> {
150 static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) {
Clement Courbet62b34fa2018-06-06 09:42:36 +0000151 Io.mapRequired("mode", Obj.Mode);
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000152 Io.mapRequired("key", Obj.Key);
Clement Courbetac74acd2018-04-04 11:37:06 +0000153 Io.mapRequired("cpu_name", Obj.CpuName);
154 Io.mapRequired("llvm_triple", Obj.LLVMTriple);
155 Io.mapRequired("num_repetitions", Obj.NumRepetitions);
156 Io.mapRequired("measurements", Obj.Measurements);
157 Io.mapRequired("error", Obj.Error);
Clement Courbeta66bfaa42018-05-15 13:07:05 +0000158 Io.mapOptional("info", Obj.Info);
Clement Courbetac74acd2018-04-04 11:37:06 +0000159 }
160};
161
162} // namespace yaml
163} // namespace llvm
164
Clement Courbet7b7c27a2018-05-14 09:01:22 +0000165LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(exegesis::InstructionBenchmark)
166
Clement Courbetac74acd2018-04-04 11:37:06 +0000167namespace exegesis {
168
Clement Courbet53d35d22018-06-05 10:56:19 +0000169void BenchmarkResultContext::addRegEntry(unsigned RegNo, llvm::StringRef Name) {
170 assert(RegNoToName.find(RegNo) == RegNoToName.end());
171 assert(RegNameToNo.find(Name) == RegNameToNo.end());
172 RegNoToName[RegNo] = Name;
173 RegNameToNo[Name] = RegNo;
174}
175
176llvm::StringRef BenchmarkResultContext::getRegName(unsigned RegNo) const {
177 const auto Itr = RegNoToName.find(RegNo);
178 if (Itr != RegNoToName.end())
179 return Itr->second;
180 return {};
181}
182
183unsigned BenchmarkResultContext::getRegNo(llvm::StringRef Name) const {
184 const auto Itr = RegNameToNo.find(Name);
185 if (Itr != RegNameToNo.end())
186 return Itr->second;
187 return 0;
188}
189
190void BenchmarkResultContext::addInstrEntry(unsigned Opcode,
191 llvm::StringRef Name) {
192 assert(InstrOpcodeToName.find(Opcode) == InstrOpcodeToName.end());
193 assert(InstrNameToOpcode.find(Name) == InstrNameToOpcode.end());
194 InstrOpcodeToName[Opcode] = Name;
195 InstrNameToOpcode[Name] = Opcode;
196}
197
198llvm::StringRef BenchmarkResultContext::getInstrName(unsigned Opcode) const {
199 const auto Itr = InstrOpcodeToName.find(Opcode);
200 if (Itr != InstrOpcodeToName.end())
201 return Itr->second;
202 return {};
203}
204
205unsigned BenchmarkResultContext::getInstrOpcode(llvm::StringRef Name) const {
206 const auto Itr = InstrNameToOpcode.find(Name);
207 if (Itr != InstrNameToOpcode.end())
208 return Itr->second;
209 return 0;
210}
211
Clement Courbet7b7c27a2018-05-14 09:01:22 +0000212template <typename ObjectOrList>
Clement Courbet53d35d22018-06-05 10:56:19 +0000213static ObjectOrList readYamlOrDieCommon(const BenchmarkResultContext &Context,
214 llvm::StringRef Filename) {
Clement Courbetac74acd2018-04-04 11:37:06 +0000215 std::unique_ptr<llvm::MemoryBuffer> MemBuffer = llvm::cantFail(
216 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename)));
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000217 llvm::yaml::Input Yin(*MemBuffer, getUntypedContext(Context));
Clement Courbet7b7c27a2018-05-14 09:01:22 +0000218 ObjectOrList Benchmark;
Clement Courbetac74acd2018-04-04 11:37:06 +0000219 Yin >> Benchmark;
220 return Benchmark;
221}
222
Clement Courbet7b7c27a2018-05-14 09:01:22 +0000223InstructionBenchmark
Clement Courbet53d35d22018-06-05 10:56:19 +0000224InstructionBenchmark::readYamlOrDie(const BenchmarkResultContext &Context,
225 llvm::StringRef Filename) {
226 return readYamlOrDieCommon<InstructionBenchmark>(Context, Filename);
Clement Courbet7b7c27a2018-05-14 09:01:22 +0000227}
228
229std::vector<InstructionBenchmark>
Clement Courbet53d35d22018-06-05 10:56:19 +0000230InstructionBenchmark::readYamlsOrDie(const BenchmarkResultContext &Context,
231 llvm::StringRef Filename) {
232 return readYamlOrDieCommon<std::vector<InstructionBenchmark>>(Context,
233 Filename);
Clement Courbet7b7c27a2018-05-14 09:01:22 +0000234}
235
Clement Courbet53d35d22018-06-05 10:56:19 +0000236void InstructionBenchmark::writeYamlTo(const BenchmarkResultContext &Context,
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000237 llvm::raw_ostream &OS) {
238 llvm::yaml::Output Yout(OS, getUntypedContext(Context));
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000239 Yout << *this;
240}
241
Clement Courbet53d35d22018-06-05 10:56:19 +0000242void InstructionBenchmark::readYamlFrom(const BenchmarkResultContext &Context,
243 llvm::StringRef InputContent) {
Guillaume Chatelet083a0c162018-06-07 07:40:40 +0000244 llvm::yaml::Input Yin(InputContent, getUntypedContext(Context));
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000245 Yin >> *this;
246}
247
248// FIXME: Change the API to let the caller handle errors.
Clement Courbet53d35d22018-06-05 10:56:19 +0000249void InstructionBenchmark::writeYamlOrDie(const BenchmarkResultContext &Context,
250 const llvm::StringRef Filename) {
Clement Courbetac74acd2018-04-04 11:37:06 +0000251 if (Filename == "-") {
Clement Courbet53d35d22018-06-05 10:56:19 +0000252 writeYamlTo(Context, llvm::outs());
Clement Courbetac74acd2018-04-04 11:37:06 +0000253 } else {
Clement Courbet0e69e2d2018-05-17 10:52:18 +0000254 int ResultFD = 0;
255 llvm::cantFail(llvm::errorCodeToError(
256 openFileForWrite(Filename, ResultFD, llvm::sys::fs::F_Text)));
257 llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
Clement Courbet53d35d22018-06-05 10:56:19 +0000258 writeYamlTo(Context, Ostr);
Clement Courbetac74acd2018-04-04 11:37:06 +0000259 }
260}
261
Clement Courbetae8ae5dc2018-05-24 12:41:02 +0000262void BenchmarkMeasureStats::push(const BenchmarkMeasure &BM) {
263 if (Key.empty())
264 Key = BM.Key;
265 assert(Key == BM.Key);
266 ++NumValues;
267 SumValues += BM.Value;
268 MaxValue = std::max(MaxValue, BM.Value);
269 MinValue = std::min(MinValue, BM.Value);
270}
271
Clement Courbetac74acd2018-04-04 11:37:06 +0000272} // namespace exegesis