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 "BenchmarkRunner.h"
12 #include "llvm/ADT/STLExtras.h"
13 #include "llvm/ADT/bit.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/ObjectYAML/YAML.h"
16 #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 
21 static constexpr const char kIntegerPrefix[] = "i_0x";
22 static constexpr const char kDoublePrefix[] = "f_";
23 static constexpr const char kInvalidOperand[] = "INVALID";
24 
25 namespace llvm {
26 
27 namespace {
28 
29 // A mutable struct holding an LLVMState that can be passed through the
30 // serialization process to encode/decode registers and instructions.
31 struct YamlContext {
YamlContextllvm::__anonb37135520111::YamlContext32   YamlContext(const exegesis::LLVMState &State)
33       : State(&State), ErrorStream(LastError) {}
34 
serializeMCInstllvm::__anonb37135520111::YamlContext35   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);
40     }
41   }
42 
deserializeMCInstllvm::__anonb37135520111::YamlContext43   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   }
59 
getLastErrorllvm::__anonb37135520111::YamlContext60   std::string &getLastError() { return ErrorStream.str(); }
61 
getErrorStreamllvm::__anonb37135520111::YamlContext62   llvm::raw_string_ostream &getErrorStream() { return ErrorStream; }
63 
getRegNamellvm::__anonb37135520111::YamlContext64   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 
getRegNollvm::__anonb37135520111::YamlContext71   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 
80 private:
serializeIntegerOperandllvm::__anonb37135520111::YamlContext81   void serializeIntegerOperand(llvm::raw_ostream &OS, int64_t Value) {
82     OS << kIntegerPrefix;
83     OS.write_hex(llvm::bit_cast<uint64_t>(Value));
84   }
85 
tryDeserializeIntegerOperandllvm::__anonb37135520111::YamlContext86   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 
serializeFPOperandllvm::__anonb37135520111::YamlContext92   void serializeFPOperand(llvm::raw_ostream &OS, double Value) {
93     OS << kDoublePrefix << llvm::format("%la", Value);
94   }
95 
tryDeserializeFPOperandllvm::__anonb37135520111::YamlContext96   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 
serializeMCOperandllvm::__anonb37135520111::YamlContext104   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()) {
109       serializeIntegerOperand(OS, MCOperand.getImm());
110     } else if (MCOperand.isFPImm()) {
111       serializeFPOperand(OS, MCOperand.getFPImm());
112     } else {
113       OS << kInvalidOperand;
114     }
115   }
116 
deserializeMCOperandllvm::__anonb37135520111::YamlContext117   llvm::MCOperand deserializeMCOperand(llvm::StringRef String) {
118     assert(!String.empty());
119     int64_t IntValue = 0;
120     double DoubleValue = 0;
121     if (tryDeserializeIntegerOperand(String, IntValue))
122       return llvm::MCOperand::createImm(IntValue);
123     if (tryDeserializeFPOperand(String, DoubleValue))
124       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 
getInstrNamellvm::__anonb37135520111::YamlContext132   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 
getInstrOpcodellvm::__anonb37135520111::YamlContext139   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 
148   const llvm::exegesis::LLVMState *State;
149   std::string LastError;
150   llvm::raw_string_ostream ErrorStream;
151 };
152 } // namespace
153 
154 // Defining YAML traits for IO.
155 namespace yaml {
156 
getTypedContext(void * Ctx)157 static YamlContext &getTypedContext(void *Ctx) {
158   return *reinterpret_cast<YamlContext *>(Ctx);
159 }
160 
161 // std::vector<llvm::MCInst> will be rendered as a list.
162 template <> struct SequenceElementTraits<llvm::MCInst> {
163   static const bool flow = false;
164 };
165 
166 template <> struct ScalarTraits<llvm::MCInst> {
167 
outputllvm::yaml::ScalarTraits168   static void output(const llvm::MCInst &Value, void *Ctx,
169                      llvm::raw_ostream &Out) {
170     getTypedContext(Ctx).serializeMCInst(Value, Out);
171   }
172 
inputllvm::yaml::ScalarTraits173   static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
174     YamlContext &Context = getTypedContext(Ctx);
175     Context.deserializeMCInst(Scalar, Value);
176     return Context.getLastError();
177   }
178 
179   // By default strings are quoted only when necessary.
180   // We force the use of single quotes for uniformity.
mustQuotellvm::yaml::ScalarTraits181   static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
182 
183   static const bool flow = true;
184 };
185 
186 // std::vector<exegesis::Measure> will be rendered as a list.
187 template <> 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 }
193 template <> struct MappingTraits<exegesis::BenchmarkMeasure> {
mappingllvm::yaml::MappingTraits194   static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) {
195     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     }
200     Io.mapRequired("value", Obj.PerInstructionValue);
201     Io.mapOptional("per_snippet_value", Obj.PerSnippetValue);
202   }
203   static const bool flow = true;
204 };
205 
206 template <>
207 struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> {
enumerationllvm::yaml::ScalarEnumerationTraits208   static void enumeration(IO &Io,
209                           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);
213   }
214 };
215 
216 // std::vector<exegesis::RegisterValue> will be rendered as a list.
217 template <> struct SequenceElementTraits<exegesis::RegisterValue> {
218   static const bool flow = false;
219 };
220 
221 template <> struct ScalarTraits<exegesis::RegisterValue> {
222   static constexpr const unsigned kRadix = 16;
223   static constexpr const bool kSigned = false;
224 
outputllvm::yaml::ScalarTraits225   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 
inputllvm::yaml::ScalarTraits232   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 
mustQuotellvm::yaml::ScalarTraits249   static QuotingType mustQuote(StringRef) { return QuotingType::Single; }
250 
251   static const bool flow = true;
252 };
253 
254 template <>
255 struct MappingContextTraits<exegesis::InstructionBenchmarkKey, YamlContext> {
mappingllvm::yaml::MappingContextTraits256   static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj,
257                       YamlContext &Context) {
258     Io.setContext(&Context);
259     Io.mapRequired("instructions", Obj.Instructions);
260     Io.mapOptional("config", Obj.Config);
261     Io.mapRequired("register_initial_values", Obj.RegisterInitialValues);
262   }
263 };
264 
265 template <>
266 struct MappingContextTraits<exegesis::InstructionBenchmark, YamlContext> {
267   struct NormalizedBinary {
NormalizedBinaryllvm::yaml::MappingContextTraits::NormalizedBinary268     NormalizedBinary(IO &io) {}
NormalizedBinaryllvm::yaml::MappingContextTraits::NormalizedBinary269     NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
denormalizellvm::yaml::MappingContextTraits::NormalizedBinary270     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 
mappingllvm::yaml::MappingContextTraits283   static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj,
284                       YamlContext &Context) {
285     Io.mapRequired("mode", Obj.Mode);
286     Io.mapRequired("key", Obj.Key, Context);
287     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);
292     Io.mapOptional("info", Obj.Info);
293     // AssembledSnippet
294     MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString(
295         Io, Obj.AssembledSnippet);
296     Io.mapOptional("assembled_snippet", BinaryString->Binary);
297   }
298 };
299 
300 } // namespace yaml
301 
302 namespace exegesis {
303 
304 llvm::Expected<InstructionBenchmark>
readYaml(const LLVMState & State,llvm::StringRef Filename)305 InstructionBenchmark::readYaml(const LLVMState &State,
306                                llvm::StringRef Filename) {
307   if (auto ExpectedMemoryBuffer =
308           llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) {
309     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());
316     return Benchmark;
317   } else {
318     return ExpectedMemoryBuffer.takeError();
319   }
320 }
321 
322 llvm::Expected<std::vector<InstructionBenchmark>>
readYamls(const LLVMState & State,llvm::StringRef Filename)323 InstructionBenchmark::readYamls(const LLVMState &State,
324                                 llvm::StringRef Filename) {
325   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   }
343 }
344 
writeYamlTo(const LLVMState & State,llvm::raw_ostream & OS)345 void InstructionBenchmark::writeYamlTo(const LLVMState &State,
346                                        llvm::raw_ostream &OS) {
347   llvm::yaml::Output Yout(OS, nullptr /*Ctx*/, 200 /*WrapColumn*/);
348   YamlContext Context(State);
349   Yout.beginDocuments();
350   llvm::yaml::yamlize(Yout, *this, /*unused*/ true, Context);
351   Yout.endDocuments();
352 }
353 
readYamlFrom(const LLVMState & State,llvm::StringRef InputContent)354 void InstructionBenchmark::readYamlFrom(const LLVMState &State,
355                                         llvm::StringRef InputContent) {
356   llvm::yaml::Input Yin(InputContent);
357   YamlContext Context(State);
358   if (Yin.setCurrentDocument())
359     llvm::yaml::yamlize(Yin, *this, /*unused*/ true, Context);
360 }
361 
writeYaml(const LLVMState & State,const llvm::StringRef Filename)362 llvm::Error InstructionBenchmark::writeYaml(const LLVMState &State,
363                                             const llvm::StringRef Filename) {
364   if (Filename == "-") {
365     writeYamlTo(State, llvm::outs());
366   } else {
367     int ResultFD = 0;
368     if (auto E = llvm::errorCodeToError(
369             openFileForWrite(Filename, ResultFD, llvm::sys::fs::CD_CreateAlways,
370                              llvm::sys::fs::F_Text))) {
371       return E;
372     }
373     llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
374     writeYamlTo(State, Ostr);
375   }
376   return llvm::Error::success();
377 }
378 
push(const BenchmarkMeasure & BM)379 void PerInstructionStats::push(const BenchmarkMeasure &BM) {
380   if (Key.empty())
381     Key = BM.Key;
382   assert(Key == BM.Key);
383   ++NumValues;
384   SumValues += BM.PerInstructionValue;
385   MaxValue = std::max(MaxValue, BM.PerInstructionValue);
386   MinValue = std::min(MinValue, BM.PerInstructionValue);
387 }
388 
389 } // namespace exegesis
390 } // namespace llvm
391