//===-- BenchmarkResult.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "BenchmarkResult.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ObjectYAML/YAML.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" static constexpr const char kIntegerFormat[] = "i_0x%" PRId64 "x"; static constexpr const char kDoubleFormat[] = "f_%la"; static void serialize(const exegesis::BenchmarkResultContext &Context, const llvm::MCOperand &MCOperand, llvm::raw_ostream &OS) { if (MCOperand.isReg()) { OS << Context.getRegName(MCOperand.getReg()); } else if (MCOperand.isImm()) { OS << llvm::format(kIntegerFormat, MCOperand.getImm()); } else if (MCOperand.isFPImm()) { OS << llvm::format(kDoubleFormat, MCOperand.getFPImm()); } else { OS << "INVALID"; } } static void serialize(const exegesis::BenchmarkResultContext &Context, const llvm::MCInst &MCInst, llvm::raw_ostream &OS) { OS << Context.getInstrName(MCInst.getOpcode()); for (const auto &Op : MCInst) { OS << ' '; serialize(Context, Op, OS); } } static llvm::MCOperand deserialize(const exegesis::BenchmarkResultContext &Context, llvm::StringRef String) { assert(!String.empty()); int64_t IntValue = 0; double DoubleValue = 0; if (sscanf(String.data(), kIntegerFormat, &IntValue) == 1) return llvm::MCOperand::createImm(IntValue); if (sscanf(String.data(), kDoubleFormat, &DoubleValue) == 1) return llvm::MCOperand::createFPImm(DoubleValue); if (unsigned RegNo = Context.getRegNo(String)) // Returns 0 if invalid. return llvm::MCOperand::createReg(RegNo); return {}; } static llvm::StringRef deserialize(const exegesis::BenchmarkResultContext &Context, llvm::StringRef String, llvm::MCInst &Value) { llvm::SmallVector<llvm::StringRef, 8> Pieces; String.split(Pieces, " "); if (Pieces.empty()) return "Invalid Instruction"; bool ProcessOpcode = true; for (llvm::StringRef Piece : Pieces) { if (ProcessOpcode) { ProcessOpcode = false; Value.setOpcode(Context.getInstrOpcode(Piece)); if (Value.getOpcode() == 0) return "Unknown Opcode Name"; } else { Value.addOperand(deserialize(Context, Piece)); } } return {}; } // YAML IO requires a mutable pointer to Context but we guarantee to not // modify it. static void *getUntypedContext(const exegesis::BenchmarkResultContext &Ctx) { return const_cast<exegesis::BenchmarkResultContext *>(&Ctx); } static const exegesis::BenchmarkResultContext &getTypedContext(void *Ctx) { assert(Ctx); return *static_cast<const exegesis::BenchmarkResultContext *>(Ctx); } // Defining YAML traits for IO. namespace llvm { namespace yaml { // std::vector<llvm::MCInst> will be rendered as a list. template <> struct SequenceElementTraits<llvm::MCInst> { static const bool flow = false; }; template <> struct ScalarTraits<llvm::MCInst> { static void output(const llvm::MCInst &Value, void *Ctx, llvm::raw_ostream &Out) { serialize(getTypedContext(Ctx), Value, Out); } static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) { return deserialize(getTypedContext(Ctx), Scalar, Value); } static QuotingType mustQuote(StringRef) { return QuotingType::Single; } static const bool flow = true; }; // std::vector<exegesis::Measure> will be rendered as a list. template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> { static const bool flow = false; }; // exegesis::Measure is rendererd as a flow instead of a list. // e.g. { "key": "the key", "value": 0123 } template <> struct MappingTraits<exegesis::BenchmarkMeasure> { static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) { Io.mapRequired("key", Obj.Key); Io.mapRequired("value", Obj.Value); Io.mapOptional("debug_string", Obj.DebugString); } static const bool flow = true; }; template <> struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> { static void enumeration(IO &Io, exegesis::InstructionBenchmark::ModeE &Value) { Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown); Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency); Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops); } }; template <> struct MappingTraits<exegesis::InstructionBenchmarkKey> { static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj) { Io.mapRequired("instructions", Obj.Instructions); Io.mapOptional("config", Obj.Config); } }; template <> struct MappingTraits<exegesis::InstructionBenchmark> { class NormalizedBinary { public: NormalizedBinary(IO &io) {} NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {} std::vector<uint8_t> denormalize(IO &) { std::vector<uint8_t> Data; std::string Str; raw_string_ostream OSS(Str); Binary.writeAsBinary(OSS); OSS.flush(); Data.assign(Str.begin(), Str.end()); return Data; } BinaryRef Binary; }; static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) { Io.mapRequired("mode", Obj.Mode); Io.mapRequired("key", Obj.Key); Io.mapRequired("cpu_name", Obj.CpuName); Io.mapRequired("llvm_triple", Obj.LLVMTriple); Io.mapRequired("num_repetitions", Obj.NumRepetitions); Io.mapRequired("measurements", Obj.Measurements); Io.mapRequired("error", Obj.Error); Io.mapOptional("info", Obj.Info); // AssembledSnippet MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString( Io, Obj.AssembledSnippet); Io.mapOptional("assembled_snippet", BinaryString->Binary); } }; } // namespace yaml } // namespace llvm LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(exegesis::InstructionBenchmark) namespace exegesis { void BenchmarkResultContext::addRegEntry(unsigned RegNo, llvm::StringRef Name) { assert(RegNoToName.find(RegNo) == RegNoToName.end()); assert(RegNameToNo.find(Name) == RegNameToNo.end()); RegNoToName[RegNo] = Name; RegNameToNo[Name] = RegNo; } llvm::StringRef BenchmarkResultContext::getRegName(unsigned RegNo) const { const auto Itr = RegNoToName.find(RegNo); if (Itr != RegNoToName.end()) return Itr->second; return {}; } unsigned BenchmarkResultContext::getRegNo(llvm::StringRef Name) const { const auto Itr = RegNameToNo.find(Name); if (Itr != RegNameToNo.end()) return Itr->second; return 0; } void BenchmarkResultContext::addInstrEntry(unsigned Opcode, llvm::StringRef Name) { assert(InstrOpcodeToName.find(Opcode) == InstrOpcodeToName.end()); assert(InstrNameToOpcode.find(Name) == InstrNameToOpcode.end()); InstrOpcodeToName[Opcode] = Name; InstrNameToOpcode[Name] = Opcode; } llvm::StringRef BenchmarkResultContext::getInstrName(unsigned Opcode) const { const auto Itr = InstrOpcodeToName.find(Opcode); if (Itr != InstrOpcodeToName.end()) return Itr->second; return {}; } unsigned BenchmarkResultContext::getInstrOpcode(llvm::StringRef Name) const { const auto Itr = InstrNameToOpcode.find(Name); if (Itr != InstrNameToOpcode.end()) return Itr->second; return 0; } template <typename ObjectOrList> static llvm::Expected<ObjectOrList> readYamlCommon(const BenchmarkResultContext &Context, llvm::StringRef Filename) { if (auto ExpectedMemoryBuffer = llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) { std::unique_ptr<llvm::MemoryBuffer> MemoryBuffer = std::move(ExpectedMemoryBuffer.get()); llvm::yaml::Input Yin(*MemoryBuffer, getUntypedContext(Context)); ObjectOrList Benchmark; Yin >> Benchmark; return Benchmark; } else { return ExpectedMemoryBuffer.takeError(); } } llvm::Expected<InstructionBenchmark> InstructionBenchmark::readYaml(const BenchmarkResultContext &Context, llvm::StringRef Filename) { return readYamlCommon<InstructionBenchmark>(Context, Filename); } llvm::Expected<std::vector<InstructionBenchmark>> InstructionBenchmark::readYamls(const BenchmarkResultContext &Context, llvm::StringRef Filename) { return readYamlCommon<std::vector<InstructionBenchmark>>(Context, Filename); } void InstructionBenchmark::writeYamlTo(const BenchmarkResultContext &Context, llvm::raw_ostream &OS) { llvm::yaml::Output Yout(OS, getUntypedContext(Context)); Yout << *this; } void InstructionBenchmark::readYamlFrom(const BenchmarkResultContext &Context, llvm::StringRef InputContent) { llvm::yaml::Input Yin(InputContent, getUntypedContext(Context)); Yin >> *this; } llvm::Error InstructionBenchmark::writeYaml(const BenchmarkResultContext &Context, const llvm::StringRef Filename) { if (Filename == "-") { writeYamlTo(Context, llvm::outs()); } else { int ResultFD = 0; if (auto E = llvm::errorCodeToError( openFileForWrite(Filename, ResultFD, llvm::sys::fs::CD_CreateAlways, llvm::sys::fs::F_Text))) { return E; } llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/); writeYamlTo(Context, Ostr); } return llvm::Error::success(); } void BenchmarkMeasureStats::push(const BenchmarkMeasure &BM) { if (Key.empty()) Key = BM.Key; assert(Key == BM.Key); ++NumValues; SumValues += BM.Value; MaxValue = std::max(MaxValue, BM.Value); MinValue = std::min(MinValue, BM.Value); } } // namespace exegesis