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