Index: tools/llvm-exegesis/lib/BenchmarkResult.cpp =================================================================== --- tools/llvm-exegesis/lib/BenchmarkResult.cpp +++ tools/llvm-exegesis/lib/BenchmarkResult.cpp @@ -15,6 +15,81 @@ #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" +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 << MCOperand.getImm(); + } else if (MCOperand.isFPImm()) { + OS << 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()); + // Starts with an uppercase, this is a Register. + if (std::isupper(String.front())) + return llvm::MCOperand::createReg(Context.getRegNo(String)); + // There's a dot, this is a floating point. + if (String.count('.')) { + double Value = 0; + if (String.getAsDouble(Value)) + return {}; + return llvm::MCOperand::createFPImm(Value); + } + // Try as Integer. + int64_t Value = 0; + if (String.getAsInteger(10, Value)) + return {}; + return llvm::MCOperand::createImm(Value); +} + +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 +103,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 +217,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 +237,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: 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); } -MATCHER(EqMCInst, "") { - return get<0>(arg).getOpcode() == get<1>(arg).getOpcode(); +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) { 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.Mode = InstructionBenchmarkKey::Latency; ToDisk.Key.Config = "config"; ToDisk.CpuName = "cpu_name"; @@ -68,7 +91,7 @@ EXPECT_EQ(FromDisk.Key.OpcodeName, ToDisk.Key.OpcodeName); EXPECT_THAT(FromDisk.Key.Instructions, - Pointwise(EqMCInst(), ToDisk.Key.Instructions)); + Pointwise(EqMcinst(), ToDisk.Key.Instructions)); EXPECT_EQ(FromDisk.Key.Mode, ToDisk.Key.Mode); EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config); EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName); @@ -86,7 +109,7 @@ const auto FromDisk = FromDiskVector[0]; EXPECT_EQ(FromDisk.Key.OpcodeName, ToDisk.Key.OpcodeName); EXPECT_THAT(FromDisk.Key.Instructions, - Pointwise(EqMCInst(), ToDisk.Key.Instructions)); + Pointwise(EqMcinst(), ToDisk.Key.Instructions)); EXPECT_EQ(FromDisk.Key.Mode, ToDisk.Key.Mode); EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config); EXPECT_EQ(FromDisk.CpuName, ToDisk.CpuName);