Index: tools/llvm-exegesis/lib/BenchmarkResult.cpp =================================================================== --- tools/llvm-exegesis/lib/BenchmarkResult.cpp +++ tools/llvm-exegesis/lib/BenchmarkResult.cpp @@ -15,6 +15,78 @@ #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 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(&Ctx); +} + +static const exegesis::BenchmarkResultContext &getTypedContext(void *Ctx) { + assert(Ctx); + return *static_cast(Ctx); +} + // Defining YAML traits for IO. namespace llvm { namespace yaml { @@ -28,22 +100,11 @@ static void output(const llvm::MCInst &Value, void *Ctx, llvm::raw_ostream &Out) { - assert(Ctx); - auto *Context = static_cast(Ctx); - const StringRef Name = Context->getInstrName(Value.getOpcode()); - assert(!Name.empty()); - Out << Name; + serialize(getTypedContext(Ctx), Value, Out); } static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) { - assert(Ctx); - auto *Context = static_cast(Ctx); - const unsigned Opcode = Context->getInstrOpcode(Scalar); - if (Opcode == 0) { - return "Unable to parse instruction"; - } - Value.setOpcode(Opcode); - return StringRef(); + return deserialize(getTypedContext(Ctx), Scalar, Value); } static QuotingType mustQuote(StringRef) { return QuotingType::Single; } @@ -153,10 +214,7 @@ llvm::StringRef Filename) { std::unique_ptr 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(&Context)); + llvm::yaml::Input Yin(*MemBuffer, getUntypedContext(Context)); ObjectOrList Benchmark; Yin >> Benchmark; return Benchmark; @@ -176,19 +234,14 @@ } 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(&Context)); + llvm::raw_ostream &OS) { + llvm::yaml::Output Yout(OS, getUntypedContext(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(&Context)); + llvm::yaml::Input Yin(InputContent, getUntypedContext(Context)); Yin >> *this; } Index: tools/llvm-exegesis/lib/BenchmarkRunner.cpp =================================================================== --- tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -63,7 +63,9 @@ InstrBenchmark.Error = "Empty snippet"; return InstrBenchmark; } - + for (const auto &MCInst : Snippet) { + InstrBenchmark.Key.Instructions.push_back(MCInst); + } InfoStream << "Snippet:\n"; for (const auto &MCInst : Snippet) { DumpMCInst(MCRegisterInfo, MCInstrInfo, MCInst, InfoStream); Index: unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp +++ unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp @@ -28,23 +28,46 @@ 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, "") { - return get<0>(arg).getOpcode() == get<1>(arg).getOpcode(); + 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) { BenchmarkResultContext Ctx; Ctx.addInstrEntry(kInstrId, kInstrName); + Ctx.addRegEntry(kReg1Id, kReg1Name); + Ctx.addRegEntry(kReg2Id, kReg2Name); InstructionBenchmark ToDisk; ToDisk.Key.OpcodeName = "name"; - ToDisk.Key.Instructions.push_back(llvm::MCInstBuilder(kInstrId)); + 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";