Index: llvm/trunk/tools/llvm-exegesis/lib/BenchmarkResult.h =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/BenchmarkResult.h +++ llvm/trunk/tools/llvm-exegesis/lib/BenchmarkResult.h @@ -16,6 +16,7 @@ #ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H #define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H +#include "LlvmState.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCInst.h" @@ -28,8 +29,6 @@ namespace exegesis { -struct BenchmarkResultContext; // Forward declaration. - struct InstructionBenchmarkKey { // The LLVM opcode name. std::vector Instructions; @@ -62,19 +61,17 @@ // Read functions. static llvm::Expected - readYaml(const BenchmarkResultContext &Context, llvm::StringRef Filename); + readYaml(const LLVMState &State, llvm::StringRef Filename); static llvm::Expected> - readYamls(const BenchmarkResultContext &Context, llvm::StringRef Filename); + readYamls(const LLVMState &State, llvm::StringRef Filename); - void readYamlFrom(const BenchmarkResultContext &Context, - llvm::StringRef InputContent); + void readYamlFrom(const LLVMState &State, llvm::StringRef InputContent); // Write functions, non-const because of YAML traits. - void writeYamlTo(const BenchmarkResultContext &Context, llvm::raw_ostream &S); + void writeYamlTo(const LLVMState &State, llvm::raw_ostream &S); - llvm::Error writeYaml(const BenchmarkResultContext &Context, - const llvm::StringRef Filename); + llvm::Error writeYaml(const LLVMState &State, const llvm::StringRef Filename); }; //------------------------------------------------------------------------------ @@ -102,38 +99,6 @@ double MinValue = std::numeric_limits::max(); }; -// This context is used when de/serializing InstructionBenchmark to guarantee -// that Registers and Instructions are human readable and preserved accross -// different versions of LLVM. -struct BenchmarkResultContext { - BenchmarkResultContext() = default; - BenchmarkResultContext(BenchmarkResultContext &&) = default; - BenchmarkResultContext &operator=(BenchmarkResultContext &&) = default; - BenchmarkResultContext(const BenchmarkResultContext &) = delete; - BenchmarkResultContext &operator=(const BenchmarkResultContext &) = delete; - - // Populate Registers and Instruction mapping. - void addRegEntry(unsigned RegNo, llvm::StringRef Name); - void addInstrEntry(unsigned Opcode, llvm::StringRef Name); - - // Register accessors. - llvm::StringRef getRegName(unsigned RegNo) const; - unsigned getRegNo(llvm::StringRef Name) const; // 0 is not found. - - // Instruction accessors. - llvm::StringRef getInstrName(unsigned Opcode) const; - unsigned getInstrOpcode(llvm::StringRef Name) const; // 0 is not found. - -private: - // Ideally we would like to use MCRegisterInfo and MCInstrInfo but doing so - // would make testing harder, instead we create a mapping that we can easily - // populate. - std::unordered_map InstrOpcodeToName; - std::unordered_map RegNoToName; - llvm::StringMap InstrNameToOpcode; - llvm::StringMap RegNameToNo; -}; - } // namespace exegesis #endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H Index: llvm/trunk/tools/llvm-exegesis/lib/BenchmarkResult.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/BenchmarkResult.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/BenchmarkResult.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "BenchmarkResult.h" +#include "BenchmarkRunner.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ObjectYAML/YAML.h" @@ -18,75 +19,106 @@ static constexpr const char kIntegerFormat[] = "i_0x%" PRId64 "x"; static constexpr const char kDoubleFormat[] = "f_%la"; +static constexpr const char kInvalidOperand[] = "INVALID"; -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"; +// A mutable struct holding an LLVMState that can be passed through the +// serialization process to encode/decode registers and instructions. +struct YamlContext { + YamlContext(const exegesis::LLVMState &State) + : State(&State), ErrorStream(LastError) {} + + void serializeMCInst(const llvm::MCInst &MCInst, llvm::raw_ostream &OS) { + OS << getInstrName(MCInst.getOpcode()); + for (const auto &Op : MCInst) { + OS << ' '; + serializeMCOperand(Op, OS); + } } -} -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); + void deserializeMCInst(llvm::StringRef String, llvm::MCInst &Value) { + llvm::SmallVector Pieces; + String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false); + if (Pieces.empty()) { + ErrorStream << "Unknown Instruction: '" << String << "'"; + return; + } + bool ProcessOpcode = true; + for (llvm::StringRef Piece : Pieces) { + if (ProcessOpcode) + Value.setOpcode(getInstrOpcode(Piece)); + else + Value.addOperand(deserializeMCOperand(Piece)); + ProcessOpcode = false; + } } -} -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 {}; -} + std::string &getLastError() { return ErrorStream.str(); } -static llvm::StringRef -deserialize(const exegesis::BenchmarkResultContext &Context, - llvm::StringRef String, llvm::MCInst &Value) { - llvm::SmallVector Pieces; - String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false); - 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"; +private: + void serializeMCOperand(const llvm::MCOperand &MCOperand, + llvm::raw_ostream &OS) { + if (MCOperand.isReg()) { + OS << getRegName(MCOperand.getReg()); + } else if (MCOperand.isImm()) { + OS << llvm::format(kIntegerFormat, MCOperand.getImm()); + } else if (MCOperand.isFPImm()) { + OS << llvm::format(kDoubleFormat, MCOperand.getFPImm()); } else { - Value.addOperand(deserialize(Context, Piece)); + OS << kInvalidOperand; } } - 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(&Ctx); -} - -static const exegesis::BenchmarkResultContext &getTypedContext(void *Ctx) { - assert(Ctx); - return *static_cast(Ctx); -} + llvm::MCOperand deserializeMCOperand(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 = getRegNo(String)) + return llvm::MCOperand::createReg(RegNo); + if (String != kInvalidOperand) + ErrorStream << "Unknown Operand: '" << String << "'"; + return {}; + } + + llvm::StringRef getRegName(unsigned RegNo) { + const llvm::StringRef RegName = State->getRegInfo().getName(RegNo); + if (RegName.empty()) + ErrorStream << "No register with enum value" << RegNo; + return RegName; + } + + llvm::StringRef getInstrName(unsigned InstrNo) { + const llvm::StringRef InstrName = State->getInstrInfo().getName(InstrNo); + if (InstrName.empty()) + ErrorStream << "No opcode with enum value" << InstrNo; + return InstrName; + } + + unsigned getRegNo(llvm::StringRef RegName) { + const llvm::MCRegisterInfo &RegInfo = State->getRegInfo(); + for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I) + if (RegInfo.getName(I) == RegName) + return I; + ErrorStream << "No register with name " << RegName; + return 0; + } + + unsigned getInstrOpcode(llvm::StringRef InstrName) { + const llvm::MCInstrInfo &InstrInfo = State->getInstrInfo(); + for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I) + if (InstrInfo.getName(I) == InstrName) + return I; + ErrorStream << "No opcode with name " << InstrName; + return 0; + } + + const exegesis::LLVMState *State; + std::string LastError; + llvm::raw_string_ostream ErrorStream; +}; // Defining YAML traits for IO. namespace llvm { @@ -101,11 +133,13 @@ static void output(const llvm::MCInst &Value, void *Ctx, llvm::raw_ostream &Out) { - serialize(getTypedContext(Ctx), Value, Out); + reinterpret_cast(Ctx)->serializeMCInst(Value, Out); } static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) { - return deserialize(getTypedContext(Ctx), Scalar, Value); + YamlContext &Context = *reinterpret_cast(Ctx); + Context.deserializeMCInst(Scalar, Value); + return Context.getLastError(); } static QuotingType mustQuote(StringRef) { return QuotingType::Single; } @@ -139,14 +173,18 @@ } }; -template <> struct MappingTraits { - static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj) { +template <> +struct MappingContextTraits { + static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj, + YamlContext &Context) { + Io.setContext(&Context); Io.mapRequired("instructions", Obj.Instructions); Io.mapOptional("config", Obj.Config); } }; -template <> struct MappingTraits { +template <> +struct MappingContextTraits { class NormalizedBinary { public: NormalizedBinary(IO &io) {} @@ -164,9 +202,10 @@ BinaryRef Binary; }; - static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) { + static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj, + YamlContext &Context) { Io.mapRequired("mode", Obj.Mode); - Io.mapRequired("key", Obj.Key); + Io.mapRequired("key", Obj.Key, Context); Io.mapRequired("cpu_name", Obj.CpuName); Io.mapRequired("llvm_triple", Obj.LLVMTriple); Io.mapRequired("num_repetitions", Obj.NumRepetitions); @@ -183,99 +222,68 @@ } // 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 -static llvm::Expected -readYamlCommon(const BenchmarkResultContext &Context, - llvm::StringRef Filename) { +llvm::Expected +InstructionBenchmark::readYaml(const LLVMState &State, + llvm::StringRef Filename) { if (auto ExpectedMemoryBuffer = llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) { - std::unique_ptr MemoryBuffer = - std::move(ExpectedMemoryBuffer.get()); - llvm::yaml::Input Yin(*MemoryBuffer, getUntypedContext(Context)); - ObjectOrList Benchmark; - Yin >> Benchmark; + llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get()); + YamlContext Context(State); + InstructionBenchmark Benchmark; + if (Yin.setCurrentDocument()) + llvm::yaml::yamlize(Yin, Benchmark, /*unused*/ true, Context); + if (!Context.getLastError().empty()) + return llvm::make_error(Context.getLastError()); return Benchmark; } else { return ExpectedMemoryBuffer.takeError(); } } -llvm::Expected -InstructionBenchmark::readYaml(const BenchmarkResultContext &Context, - llvm::StringRef Filename) { - return readYamlCommon(Context, Filename); -} - llvm::Expected> -InstructionBenchmark::readYamls(const BenchmarkResultContext &Context, +InstructionBenchmark::readYamls(const LLVMState &State, llvm::StringRef Filename) { - return readYamlCommon>(Context, Filename); + if (auto ExpectedMemoryBuffer = + llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) { + llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get()); + YamlContext Context(State); + std::vector Benchmarks; + while (Yin.setCurrentDocument()) { + Benchmarks.emplace_back(); + yamlize(Yin, Benchmarks.back(), /*unused*/ true, Context); + if (Yin.error()) + return llvm::errorCodeToError(Yin.error()); + if (!Context.getLastError().empty()) + return llvm::make_error(Context.getLastError()); + Yin.nextDocument(); + } + return Benchmarks; + } else { + return ExpectedMemoryBuffer.takeError(); + } } -void InstructionBenchmark::writeYamlTo(const BenchmarkResultContext &Context, +void InstructionBenchmark::writeYamlTo(const LLVMState &State, llvm::raw_ostream &OS) { - llvm::yaml::Output Yout(OS, getUntypedContext(Context)); - Yout << *this; + llvm::yaml::Output Yout(OS); + YamlContext Context(State); + llvm::yaml::yamlize(Yout, *this, /*unused*/ true, Context); } -void InstructionBenchmark::readYamlFrom(const BenchmarkResultContext &Context, +void InstructionBenchmark::readYamlFrom(const LLVMState &State, llvm::StringRef InputContent) { - llvm::yaml::Input Yin(InputContent, getUntypedContext(Context)); - Yin >> *this; + llvm::yaml::Input Yin(InputContent); + YamlContext Context(State); + if (Yin.setCurrentDocument()) + llvm::yaml::yamlize(Yin, *this, /*unused*/ true, Context); } -llvm::Error -InstructionBenchmark::writeYaml(const BenchmarkResultContext &Context, - const llvm::StringRef Filename) { +llvm::Error InstructionBenchmark::writeYaml(const LLVMState &State, + const llvm::StringRef Filename) { if (Filename == "-") { - writeYamlTo(Context, llvm::outs()); + writeYamlTo(State, llvm::outs()); } else { int ResultFD = 0; if (auto E = llvm::errorCodeToError( @@ -284,7 +292,7 @@ return E; } llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/); - writeYamlTo(Context, Ostr); + writeYamlTo(State, Ostr); } return llvm::Error::success(); } Index: llvm/trunk/tools/llvm-exegesis/llvm-exegesis.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/llvm-exegesis.cpp +++ llvm/trunk/tools/llvm-exegesis/llvm-exegesis.cpp @@ -123,21 +123,6 @@ llvm::report_fatal_error(llvm::Twine("unknown opcode ").concat(OpcodeName)); } -static BenchmarkResultContext -getBenchmarkResultContext(const LLVMState &State) { - BenchmarkResultContext Ctx; - - const llvm::MCInstrInfo &InstrInfo = State.getInstrInfo(); - for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I) - Ctx.addInstrEntry(I, InstrInfo.getName(I).data()); - - const llvm::MCRegisterInfo &RegInfo = State.getRegInfo(); - for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I) - Ctx.addRegEntry(I, RegInfo.getName(I)); - - return Ctx; -} - // Generates code snippets for opcode `Opcode`. static llvm::Expected> generateSnippets(const LLVMState &State, unsigned Opcode) { @@ -345,12 +330,10 @@ if (BenchmarkFile.empty()) BenchmarkFile = "-"; - const BenchmarkResultContext Context = getBenchmarkResultContext(State); - for (const BenchmarkCode &Conf : Configurations) { InstructionBenchmark Result = Runner->runConfiguration(Conf, NumRepetitions); - ExitOnErr(Result.writeYaml(Context, BenchmarkFile)); + ExitOnErr(Result.writeYaml(State, BenchmarkFile)); } exegesis::pfm::pfmTerminate(); } @@ -386,8 +369,7 @@ // Read benchmarks. const LLVMState State; const std::vector Points = - ExitOnErr(InstructionBenchmark::readYamls( - getBenchmarkResultContext(State), BenchmarkFile)); + ExitOnErr(InstructionBenchmark::readYamls(State, BenchmarkFile)); llvm::outs() << "Parsed " << Points.size() << " benchmark points\n"; if (Points.empty()) { llvm::errs() << "no benchmarks to analyze\n"; Index: llvm/trunk/unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp =================================================================== --- llvm/trunk/unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp +++ llvm/trunk/unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp @@ -1,134 +0,0 @@ -//===-- BenchmarkResultTest.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/SmallString.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/YAMLTraits.h" -#include "llvm/Support/raw_ostream.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using ::testing::AllOf; -using ::testing::Eq; -using ::testing::get; -using ::testing::Pointwise; -using ::testing::Property; - -namespace exegesis { - -bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) { - return std::tie(A.Key, A.Value) == std::tie(B.Key, B.Value); -} - -static std::string Dump(const llvm::MCInst &McInst) { - std::string Buffer; - llvm::raw_string_ostream OS(Buffer); - McInst.print(OS); - return Buffer; -} - -MATCHER(EqMCInst, "") { - const std::string Lhs = Dump(get<0>(arg)); - const std::string Rhs = Dump(get<1>(arg)); - if (Lhs != Rhs) { - *result_listener << Lhs << " <=> " << Rhs; - return false; - } - return true; -} - -namespace { - -static constexpr const unsigned kInstrId = 5; -static constexpr const char kInstrName[] = "Instruction5"; -static constexpr const unsigned kReg1Id = 1; -static constexpr const char kReg1Name[] = "Reg1"; -static constexpr const unsigned kReg2Id = 2; -static constexpr const char kReg2Name[] = "Reg2"; - -TEST(BenchmarkResultTest, WriteToAndReadFromDisk) { - llvm::ExitOnError ExitOnErr; - BenchmarkResultContext Ctx; - Ctx.addInstrEntry(kInstrId, kInstrName); - Ctx.addRegEntry(kReg1Id, kReg1Name); - Ctx.addRegEntry(kReg2Id, kReg2Name); - - InstructionBenchmark ToDisk; - - ToDisk.Key.Instructions.push_back(llvm::MCInstBuilder(kInstrId) - .addReg(kReg1Id) - .addReg(kReg2Id) - .addImm(123) - .addFPImm(0.5)); - ToDisk.Key.Config = "config"; - ToDisk.Mode = InstructionBenchmark::Latency; - ToDisk.CpuName = "cpu_name"; - ToDisk.LLVMTriple = "llvm_triple"; - ToDisk.NumRepetitions = 1; - ToDisk.Measurements.push_back(BenchmarkMeasure{"a", 1, "debug a"}); - ToDisk.Measurements.push_back(BenchmarkMeasure{"b", 2, ""}); - ToDisk.Error = "error"; - ToDisk.Info = "info"; - - llvm::SmallString<64> Filename; - std::error_code EC; - EC = llvm::sys::fs::createUniqueDirectory("BenchmarkResultTestDir", Filename); - ASSERT_FALSE(EC); - llvm::sys::path::append(Filename, "data.yaml"); - ExitOnErr(ToDisk.writeYaml(Ctx, Filename)); - - { - // One-element version. - const auto FromDisk = - ExitOnErr(InstructionBenchmark::readYaml(Ctx, Filename)); - - EXPECT_THAT(FromDisk.Key.Instructions, - Pointwise(EqMCInst(), ToDisk.Key.Instructions)); - EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config); - EXPECT_EQ(FromDisk.Mode, ToDisk.Mode); - EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName); - EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple); - EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions); - EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements); - EXPECT_THAT(FromDisk.Error, ToDisk.Error); - EXPECT_EQ(FromDisk.Info, ToDisk.Info); - } - { - // Vector version. - const auto FromDiskVector = - ExitOnErr(InstructionBenchmark::readYamls(Ctx, Filename)); - ASSERT_EQ(FromDiskVector.size(), size_t{1}); - const auto FromDisk = FromDiskVector[0]; - EXPECT_THAT(FromDisk.Key.Instructions, - Pointwise(EqMCInst(), ToDisk.Key.Instructions)); - EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config); - EXPECT_EQ(FromDisk.Mode, ToDisk.Mode); - EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName); - EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple); - EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions); - EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements); - EXPECT_THAT(FromDisk.Error, ToDisk.Error); - EXPECT_EQ(FromDisk.Info, ToDisk.Info); - } -} - -TEST(BenchmarkResultTest, BenchmarkMeasureStats) { - BenchmarkMeasureStats Stats; - Stats.push(BenchmarkMeasure{"a", 0.5, "debug a"}); - Stats.push(BenchmarkMeasure{"a", 1.5, "debug a"}); - Stats.push(BenchmarkMeasure{"a", -1.0, "debug a"}); - Stats.push(BenchmarkMeasure{"a", 0.0, "debug a"}); - EXPECT_EQ(Stats.min(), -1.0); - EXPECT_EQ(Stats.max(), 1.5); - EXPECT_EQ(Stats.avg(), 0.25); // (0.5+1.5-1.0+0.0) / 4 -} -} // namespace -} // namespace exegesis Index: llvm/trunk/unittests/tools/llvm-exegesis/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/tools/llvm-exegesis/CMakeLists.txt +++ llvm/trunk/unittests/tools/llvm-exegesis/CMakeLists.txt @@ -11,7 +11,6 @@ ) add_llvm_unittest(LLVMExegesisTests - BenchmarkResultTest.cpp BenchmarkRunnerTest.cpp ClusteringTest.cpp PerfHelperTest.cpp Index: llvm/trunk/unittests/tools/llvm-exegesis/X86/BenchmarkResultTest.cpp =================================================================== --- llvm/trunk/unittests/tools/llvm-exegesis/X86/BenchmarkResultTest.cpp +++ llvm/trunk/unittests/tools/llvm-exegesis/X86/BenchmarkResultTest.cpp @@ -0,0 +1,134 @@ +//===-- BenchmarkResultTest.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 "X86InstrInfo.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using ::testing::AllOf; +using ::testing::Eq; +using ::testing::get; +using ::testing::Pointwise; +using ::testing::Property; + +namespace exegesis { + +bool operator==(const BenchmarkMeasure &A, const BenchmarkMeasure &B) { + return std::tie(A.Key, A.Value) == std::tie(B.Key, B.Value); +} + +static std::string Dump(const llvm::MCInst &McInst) { + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + McInst.print(OS); + return Buffer; +} + +MATCHER(EqMCInst, "") { + const std::string Lhs = Dump(get<0>(arg)); + const std::string Rhs = Dump(get<1>(arg)); + if (Lhs != Rhs) { + *result_listener << Lhs << " <=> " << Rhs; + return false; + } + return true; +} + +namespace { + +TEST(BenchmarkResultTest, WriteToAndReadFromDisk) { + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86Target(); + LLVMInitializeX86TargetMC(); + + // Read benchmarks. + const LLVMState State; + + llvm::ExitOnError ExitOnErr; + + InstructionBenchmark ToDisk; + + ToDisk.Key.Instructions.push_back(llvm::MCInstBuilder(llvm::X86::XOR32rr) + .addReg(llvm::X86::AL) + .addReg(llvm::X86::AH) + .addImm(123) + .addFPImm(0.5)); + ToDisk.Key.Config = "config"; + ToDisk.Mode = InstructionBenchmark::Latency; + ToDisk.CpuName = "cpu_name"; + ToDisk.LLVMTriple = "llvm_triple"; + ToDisk.NumRepetitions = 1; + ToDisk.Measurements.push_back(BenchmarkMeasure{"a", 1, "debug a"}); + ToDisk.Measurements.push_back(BenchmarkMeasure{"b", 2, ""}); + ToDisk.Error = "error"; + ToDisk.Info = "info"; + + llvm::SmallString<64> Filename; + std::error_code EC; + EC = llvm::sys::fs::createUniqueDirectory("BenchmarkResultTestDir", Filename); + ASSERT_FALSE(EC); + llvm::sys::path::append(Filename, "data.yaml"); + llvm::errs() << Filename << "-------\n"; + ExitOnErr(ToDisk.writeYaml(State, Filename)); + + { + // One-element version. + const auto FromDisk = + ExitOnErr(InstructionBenchmark::readYaml(State, Filename)); + + EXPECT_THAT(FromDisk.Key.Instructions, + Pointwise(EqMCInst(), ToDisk.Key.Instructions)); + EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config); + EXPECT_EQ(FromDisk.Mode, ToDisk.Mode); + EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName); + EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple); + EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions); + EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements); + EXPECT_THAT(FromDisk.Error, ToDisk.Error); + EXPECT_EQ(FromDisk.Info, ToDisk.Info); + } + { + // Vector version. + const auto FromDiskVector = + ExitOnErr(InstructionBenchmark::readYamls(State, Filename)); + ASSERT_EQ(FromDiskVector.size(), size_t{1}); + const auto FromDisk = FromDiskVector[0]; + EXPECT_THAT(FromDisk.Key.Instructions, + Pointwise(EqMCInst(), ToDisk.Key.Instructions)); + EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config); + EXPECT_EQ(FromDisk.Mode, ToDisk.Mode); + EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName); + EXPECT_EQ(FromDisk.LLVMTriple, ToDisk.LLVMTriple); + EXPECT_EQ(FromDisk.NumRepetitions, ToDisk.NumRepetitions); + EXPECT_THAT(FromDisk.Measurements, ToDisk.Measurements); + EXPECT_THAT(FromDisk.Error, ToDisk.Error); + EXPECT_EQ(FromDisk.Info, ToDisk.Info); + } +} + +TEST(BenchmarkResultTest, BenchmarkMeasureStats) { + BenchmarkMeasureStats Stats; + Stats.push(BenchmarkMeasure{"a", 0.5, "debug a"}); + Stats.push(BenchmarkMeasure{"a", 1.5, "debug a"}); + Stats.push(BenchmarkMeasure{"a", -1.0, "debug a"}); + Stats.push(BenchmarkMeasure{"a", 0.0, "debug a"}); + EXPECT_EQ(Stats.min(), -1.0); + EXPECT_EQ(Stats.max(), 1.5); + EXPECT_EQ(Stats.avg(), 0.25); // (0.5+1.5-1.0+0.0) / 4 +} +} // namespace +} // namespace exegesis Index: llvm/trunk/unittests/tools/llvm-exegesis/X86/CMakeLists.txt =================================================================== --- llvm/trunk/unittests/tools/llvm-exegesis/X86/CMakeLists.txt +++ llvm/trunk/unittests/tools/llvm-exegesis/X86/CMakeLists.txt @@ -16,6 +16,7 @@ add_llvm_unittest(LLVMExegesisX86Tests AssemblerTest.cpp AnalysisTest.cpp + BenchmarkResultTest.cpp SnippetGeneratorTest.cpp RegisterAliasingTest.cpp TargetTest.cpp