llvm-project/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp
Clement Courbet 53d35d2dc4 [llvm-exegesis] Add instructions to BenchmarkResult Key.
We want llvm-exegesis to explore instructions (effect of initial register values, effect of operand selection). To enable this a BenchmarkResult muststore all the relevant data in its key. This patch starts adding such data. Here we simply allow to store the generated instructions, following patches will add operands and initial values for registers.

https://reviews.llvm.org/D47764

Authored by: Guilluame Chatelet

llvm-svn: 334008
2018-06-05 10:56:19 +00:00

220 lines
7.4 KiB
C++

//===-- 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/Support/FileOutputBuffer.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
// 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) {
assert(Ctx);
auto *Context = static_cast<const exegesis::BenchmarkResultContext *>(Ctx);
const StringRef Name = Context->getInstrName(Value.getOpcode());
assert(!Name.empty());
Out << Name;
}
static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) {
assert(Ctx);
auto *Context = static_cast<const exegesis::BenchmarkResultContext *>(Ctx);
const unsigned Opcode = Context->getInstrOpcode(Scalar);
if (Opcode == 0) {
return "Unable to parse instruction";
}
Value.setOpcode(Opcode);
return StringRef();
}
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::InstructionBenchmarkKey::ModeE> {
static void enumeration(IO &Io,
exegesis::InstructionBenchmarkKey::ModeE &Value) {
Io.enumCase(Value, "", exegesis::InstructionBenchmarkKey::Unknown);
Io.enumCase(Value, "latency", exegesis::InstructionBenchmarkKey::Latency);
Io.enumCase(Value, "uops", exegesis::InstructionBenchmarkKey::Uops);
}
};
template <> struct MappingTraits<exegesis::InstructionBenchmarkKey> {
static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj) {
Io.mapRequired("opcode_name", Obj.OpcodeName);
Io.mapOptional("instructions", Obj.Instructions);
Io.mapRequired("mode", Obj.Mode);
Io.mapOptional("config", Obj.Config);
}
};
template <> struct MappingTraits<exegesis::InstructionBenchmark> {
static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) {
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);
}
};
} // 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 ObjectOrList readYamlOrDieCommon(const BenchmarkResultContext &Context,
llvm::StringRef Filename) {
std::unique_ptr<llvm::MemoryBuffer> MemBuffer = llvm::cantFail(
llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename)));
// YAML IO requires a mutable pointer to Context but we guarantee to not
// modify it.
llvm::yaml::Input Yin(*MemBuffer,
const_cast<BenchmarkResultContext *>(&Context));
ObjectOrList Benchmark;
Yin >> Benchmark;
return Benchmark;
}
InstructionBenchmark
InstructionBenchmark::readYamlOrDie(const BenchmarkResultContext &Context,
llvm::StringRef Filename) {
return readYamlOrDieCommon<InstructionBenchmark>(Context, Filename);
}
std::vector<InstructionBenchmark>
InstructionBenchmark::readYamlsOrDie(const BenchmarkResultContext &Context,
llvm::StringRef Filename) {
return readYamlOrDieCommon<std::vector<InstructionBenchmark>>(Context,
Filename);
}
void InstructionBenchmark::writeYamlTo(const BenchmarkResultContext &Context,
llvm::raw_ostream &S) {
// YAML IO requires a mutable pointer to Context but we guarantee to not
// modify it.
llvm::yaml::Output Yout(S, const_cast<BenchmarkResultContext *>(&Context));
Yout << *this;
}
void InstructionBenchmark::readYamlFrom(const BenchmarkResultContext &Context,
llvm::StringRef InputContent) {
// YAML IO requires a mutable pointer to Context but we guarantee to not
// modify it.
llvm::yaml::Input Yin(InputContent,
const_cast<BenchmarkResultContext *>(&Context));
Yin >> *this;
}
// FIXME: Change the API to let the caller handle errors.
void InstructionBenchmark::writeYamlOrDie(const BenchmarkResultContext &Context,
const llvm::StringRef Filename) {
if (Filename == "-") {
writeYamlTo(Context, llvm::outs());
} else {
int ResultFD = 0;
llvm::cantFail(llvm::errorCodeToError(
openFileForWrite(Filename, ResultFD, llvm::sys::fs::F_Text)));
llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/);
writeYamlTo(Context, Ostr);
}
}
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