1 //===-- 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"
11 #include "llvm/ADT/STLExtras.h"
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/ObjectYAML/YAML.h"
14 #include "llvm/Support/FileOutputBuffer.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/Format.h"
17 #include "llvm/Support/raw_ostream.h"
18
19 static constexpr const char kIntegerFormat[] = "i_0x%" PRId64 "x";
20 static constexpr const char kDoubleFormat[] = "f_%la";
21
serialize(const exegesis::BenchmarkResultContext & Context,const llvm::MCOperand & MCOperand,llvm::raw_ostream & OS)22 static void serialize(const exegesis::BenchmarkResultContext &Context,
23 const llvm::MCOperand &MCOperand, llvm::raw_ostream &OS) {
24 if (MCOperand.isReg()) {
25 OS << Context.getRegName(MCOperand.getReg());
26 } else if (MCOperand.isImm()) {
27 OS << llvm::format(kIntegerFormat, MCOperand.getImm());
28 } else if (MCOperand.isFPImm()) {
29 OS << llvm::format(kDoubleFormat, MCOperand.getFPImm());
30 } else {
31 OS << "INVALID";
32 }
33 }
34
serialize(const exegesis::BenchmarkResultContext & Context,const llvm::MCInst & MCInst,llvm::raw_ostream & OS)35 static void serialize(const exegesis::BenchmarkResultContext &Context,
36 const llvm::MCInst &MCInst, llvm::raw_ostream &OS) {
37 OS << Context.getInstrName(MCInst.getOpcode());
38 for (const auto &Op : MCInst) {
39 OS << ' ';
40 serialize(Context, Op, OS);
41 }
42 }
43
44 static llvm::MCOperand
deserialize(const exegesis::BenchmarkResultContext & Context,llvm::StringRef String)45 deserialize(const exegesis::BenchmarkResultContext &Context,
46 llvm::StringRef String) {
47 assert(!String.empty());
48 int64_t IntValue = 0;
49 double DoubleValue = 0;
50 if (sscanf(String.data(), kIntegerFormat, &IntValue) == 1)
51 return llvm::MCOperand::createImm(IntValue);
52 if (sscanf(String.data(), kDoubleFormat, &DoubleValue) == 1)
53 return llvm::MCOperand::createFPImm(DoubleValue);
54 if (unsigned RegNo = Context.getRegNo(String)) // Returns 0 if invalid.
55 return llvm::MCOperand::createReg(RegNo);
56 return {};
57 }
58
59 static llvm::StringRef
deserialize(const exegesis::BenchmarkResultContext & Context,llvm::StringRef String,llvm::MCInst & Value)60 deserialize(const exegesis::BenchmarkResultContext &Context,
61 llvm::StringRef String, llvm::MCInst &Value) {
62 llvm::SmallVector<llvm::StringRef, 8> Pieces;
63 String.split(Pieces, " ");
64 if (Pieces.empty())
65 return "Invalid Instruction";
66 bool ProcessOpcode = true;
67 for (llvm::StringRef Piece : Pieces) {
68 if (ProcessOpcode) {
69 ProcessOpcode = false;
70 Value.setOpcode(Context.getInstrOpcode(Piece));
71 if (Value.getOpcode() == 0)
72 return "Unknown Opcode Name";
73 } else {
74 Value.addOperand(deserialize(Context, Piece));
75 }
76 }
77 return {};
78 }
79
80 // YAML IO requires a mutable pointer to Context but we guarantee to not
81 // modify it.
getUntypedContext(const exegesis::BenchmarkResultContext & Ctx)82 static void *getUntypedContext(const exegesis::BenchmarkResultContext &Ctx) {
83 return const_cast<exegesis::BenchmarkResultContext *>(&Ctx);
84 }
85
getTypedContext(void * Ctx)86 static const exegesis::BenchmarkResultContext &getTypedContext(void *Ctx) {
87 assert(Ctx);
88 return *static_cast<const exegesis::BenchmarkResultContext *>(Ctx);
89 }
90
91 // Defining YAML traits for IO.
92 namespace llvm {
93 namespace yaml {
94
95 // std::vector<llvm::MCInst> will be rendered as a list.
96 template <> struct SequenceElementTraits<llvm::MCInst> {
97 static const bool flow = false;
98 };
99
100 template <> struct ScalarTraits<llvm::MCInst> {
101
outputllvm::yaml::ScalarTraits102 static void output(const llvm::MCInst &Value, void *Ctx,
103 llvm::raw_ostream &Out) {
104 serialize(getTypedContext(Ctx), Value, Out);
105 }
106
inputllvm::yaml::ScalarTraits107 static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
108 return deserialize(getTypedContext(Ctx), Scalar, Value);
109 }
110
mustQuotellvm::yaml::ScalarTraits111 static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
112
113 static const bool flow = true;
114 };
115
116 // std::vector<exegesis::Measure> will be rendered as a list.
117 template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> {
118 static const bool flow = false;
119 };
120
121 // exegesis::Measure is rendererd as a flow instead of a list.
122 // e.g. { "key": "the key", "value": 0123 }
123 template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
mappingllvm::yaml::MappingTraits124 static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
125 Io.mapRequired("key", Obj.Key);
126 Io.mapRequired("value", Obj.Value);
127 Io.mapOptional("debug_string", Obj.DebugString);
128 }
129 static const bool flow = true;
130 };
131
132 template <>
133 struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
enumerationllvm::yaml::ScalarEnumerationTraits134 static void enumeration(IO &Io,
135 exegesis::InstructionBenchmark::ModeE &Value) {
136 Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown);
137 Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency);
138 Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops);
139 }
140 };
141
142 template <> struct MappingTraits<exegesis::InstructionBenchmarkKey> {
mappingllvm::yaml::MappingTraits143 static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj) {
144 Io.mapRequired("instructions", Obj.Instructions);
145 Io.mapOptional("config", Obj.Config);
146 }
147 };
148
149 template <> struct MappingTraits<exegesis::InstructionBenchmark> {
150 class NormalizedBinary {
151 public:
NormalizedBinary(IO & io)152 NormalizedBinary(IO &io) {}
NormalizedBinary(IO &,std::vector<uint8_t> & Data)153 NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
denormalize(IO &)154 std::vector<uint8_t> denormalize(IO &) {
155 std::vector<uint8_t> Data;
156 std::string Str;
157 raw_string_ostream OSS(Str);
158 Binary.writeAsBinary(OSS);
159 OSS.flush();
160 Data.assign(Str.begin(), Str.end());
161 return Data;
162 }
163
164 BinaryRef Binary;
165 };
166
mappingllvm::yaml::MappingTraits167 static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) {
168 Io.mapRequired("mode", Obj.Mode);
169 Io.mapRequired("key", Obj.Key);
170 Io.mapRequired("cpu_name", Obj.CpuName);
171 Io.mapRequired("llvm_triple", Obj.LLVMTriple);
172 Io.mapRequired("num_repetitions", Obj.NumRepetitions);
173 Io.mapRequired("measurements", Obj.Measurements);
174 Io.mapRequired("error", Obj.Error);
175 Io.mapOptional("info", Obj.Info);
176 // AssembledSnippet
177 MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString(
178 Io, Obj.AssembledSnippet);
179 Io.mapOptional("assembled_snippet", BinaryString->Binary);
180 }
181 };
182
183 } // namespace yaml
184 } // namespace llvm
185
186 LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(exegesis::InstructionBenchmark)
187
188 namespace exegesis {
189
addRegEntry(unsigned RegNo,llvm::StringRef Name)190 void BenchmarkResultContext::addRegEntry(unsigned RegNo, llvm::StringRef Name) {
191 assert(RegNoToName.find(RegNo) == RegNoToName.end());
192 assert(RegNameToNo.find(Name) == RegNameToNo.end());
193 RegNoToName[RegNo] = Name;
194 RegNameToNo[Name] = RegNo;
195 }
196
getRegName(unsigned RegNo) const197 llvm::StringRef BenchmarkResultContext::getRegName(unsigned RegNo) const {
198 const auto Itr = RegNoToName.find(RegNo);
199 if (Itr != RegNoToName.end())
200 return Itr->second;
201 return {};
202 }
203
getRegNo(llvm::StringRef Name) const204 unsigned BenchmarkResultContext::getRegNo(llvm::StringRef Name) const {
205 const auto Itr = RegNameToNo.find(Name);
206 if (Itr != RegNameToNo.end())
207 return Itr->second;
208 return 0;
209 }
210
addInstrEntry(unsigned Opcode,llvm::StringRef Name)211 void BenchmarkResultContext::addInstrEntry(unsigned Opcode,
212 llvm::StringRef Name) {
213 assert(InstrOpcodeToName.find(Opcode) == InstrOpcodeToName.end());
214 assert(InstrNameToOpcode.find(Name) == InstrNameToOpcode.end());
215 InstrOpcodeToName[Opcode] = Name;
216 InstrNameToOpcode[Name] = Opcode;
217 }
218
getInstrName(unsigned Opcode) const219 llvm::StringRef BenchmarkResultContext::getInstrName(unsigned Opcode) const {
220 const auto Itr = InstrOpcodeToName.find(Opcode);
221 if (Itr != InstrOpcodeToName.end())
222 return Itr->second;
223 return {};
224 }
225
getInstrOpcode(llvm::StringRef Name) const226 unsigned BenchmarkResultContext::getInstrOpcode(llvm::StringRef Name) const {
227 const auto Itr = InstrNameToOpcode.find(Name);
228 if (Itr != InstrNameToOpcode.end())
229 return Itr->second;
230 return 0;
231 }
232
233 template <typename ObjectOrList>
234 static llvm::Expected<ObjectOrList>
readYamlCommon(const BenchmarkResultContext & Context,llvm::StringRef Filename)235 readYamlCommon(const BenchmarkResultContext &Context,
236 llvm::StringRef Filename) {
237 if (auto ExpectedMemoryBuffer =
238 llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
239 std::unique_ptr<llvm::MemoryBuffer> MemoryBuffer =
240 std::move(ExpectedMemoryBuffer.get());
241 llvm::yaml::Input Yin(*MemoryBuffer, getUntypedContext(Context));
242 ObjectOrList Benchmark;
243 Yin >> Benchmark;
244 return Benchmark;
245 } else {
246 return ExpectedMemoryBuffer.takeError();
247 }
248 }
249
250 llvm::Expected<InstructionBenchmark>
readYaml(const BenchmarkResultContext & Context,llvm::StringRef Filename)251 InstructionBenchmark::readYaml(const BenchmarkResultContext &Context,
252 llvm::StringRef Filename) {
253 return readYamlCommon<InstructionBenchmark>(Context, Filename);
254 }
255
256 llvm::Expected<std::vector<InstructionBenchmark>>
readYamls(const BenchmarkResultContext & Context,llvm::StringRef Filename)257 InstructionBenchmark::readYamls(const BenchmarkResultContext &Context,
258 llvm::StringRef Filename) {
259 return readYamlCommon<std::vector<InstructionBenchmark>>(Context, Filename);
260 }
261
writeYamlTo(const BenchmarkResultContext & Context,llvm::raw_ostream & OS)262 void InstructionBenchmark::writeYamlTo(const BenchmarkResultContext &Context,
263 llvm::raw_ostream &OS) {
264 llvm::yaml::Output Yout(OS, getUntypedContext(Context));
265 Yout << *this;
266 }
267
readYamlFrom(const BenchmarkResultContext & Context,llvm::StringRef InputContent)268 void InstructionBenchmark::readYamlFrom(const BenchmarkResultContext &Context,
269 llvm::StringRef InputContent) {
270 llvm::yaml::Input Yin(InputContent, getUntypedContext(Context));
271 Yin >> *this;
272 }
273
274 llvm::Error
writeYaml(const BenchmarkResultContext & Context,const llvm::StringRef Filename)275 InstructionBenchmark::writeYaml(const BenchmarkResultContext &Context,
276 const llvm::StringRef Filename) {
277 if (Filename == "-") {
278 writeYamlTo(Context, llvm::outs());
279 } else {
280 int ResultFD = 0;
281 if (auto E = llvm::errorCodeToError(
282 openFileForWrite(Filename, ResultFD, llvm::sys::fs::CD_CreateAlways,
283 llvm::sys::fs::F_Text))) {
284 return E;
285 }
286 llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
287 writeYamlTo(Context, Ostr);
288 }
289 return llvm::Error::success();
290 }
291
push(const BenchmarkMeasure & BM)292 void BenchmarkMeasureStats::push(const BenchmarkMeasure &BM) {
293 if (Key.empty())
294 Key = BM.Key;
295 assert(Key == BM.Key);
296 ++NumValues;
297 SumValues += BM.Value;
298 MaxValue = std::max(MaxValue, BM.Value);
299 MinValue = std::min(MinValue, BM.Value);
300 }
301
302 } // namespace exegesis
303