Home | History | Annotate | Download | only in lib
      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 
     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 
     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
     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
     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.
     82 static void *getUntypedContext(const exegesis::BenchmarkResultContext &Ctx) {
     83   return const_cast<exegesis::BenchmarkResultContext *>(&Ctx);
     84 }
     85 
     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 
    102   static void output(const llvm::MCInst &Value, void *Ctx,
    103                      llvm::raw_ostream &Out) {
    104     serialize(getTypedContext(Ctx), Value, Out);
    105   }
    106 
    107   static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
    108     return deserialize(getTypedContext(Ctx), Scalar, Value);
    109   }
    110 
    111   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> {
    124   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> {
    134   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> {
    143   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:
    152     NormalizedBinary(IO &io) {}
    153     NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
    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 
    167   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 
    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 
    197 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 
    204 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 
    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 
    219 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 
    226 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>
    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>
    251 InstructionBenchmark::readYaml(const BenchmarkResultContext &Context,
    252                                llvm::StringRef Filename) {
    253   return readYamlCommon<InstructionBenchmark>(Context, Filename);
    254 }
    255 
    256 llvm::Expected<std::vector<InstructionBenchmark>>
    257 InstructionBenchmark::readYamls(const BenchmarkResultContext &Context,
    258                                 llvm::StringRef Filename) {
    259   return readYamlCommon<std::vector<InstructionBenchmark>>(Context, Filename);
    260 }
    261 
    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 
    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
    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 
    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