//===-- 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