Index: tools/llvm-exegesis/lib/Analysis.cpp =================================================================== --- tools/llvm-exegesis/lib/Analysis.cpp +++ tools/llvm-exegesis/lib/Analysis.cpp @@ -20,7 +20,7 @@ namespace { -enum EscapeTag { kEscapeCsv, kEscapeHtml }; +enum EscapeTag { kEscapeCsv, kEscapeHtml, kEscapeHtmlString }; template void writeEscaped(llvm::raw_ostream &OS, const llvm::StringRef S); @@ -56,6 +56,16 @@ } } +template <> +void writeEscaped(llvm::raw_ostream &OS, const llvm::StringRef S) { + for (const char C : S) { + if (C == '"') + OS << "\\\""; + else + OS << C; + } +} + } // namespace template @@ -75,6 +85,19 @@ writeEscaped(OS, llvm::formatv("{0:F}", Value).str()); } +template +static void writeSnippet(llvm::raw_ostream &OS, + const std::vector &Instructions, + const llvm::MCInstrInfo &InstrInfo, + const char* Separator) { + // FIXME: Print operands. + llvm::SmallVector Opcodes; + for (const llvm::MCInst &Instr : Instructions) { + Opcodes.push_back(InstrInfo.getName(Instr.getOpcode())); + } + writeEscaped(OS, llvm::join(Opcodes, Separator)); +} + // Prints a row representing an instruction, along with scheduling info and // point coordinates (measurements). void Analysis::printInstructionRowCsv(const size_t PointId, @@ -82,25 +105,22 @@ const InstructionBenchmark &Point = Clustering_.getPoints()[PointId]; writeClusterId(OS, Clustering_.getClusterIdForPoint(PointId)); OS << kCsvSep; - writeEscaped(OS, Point.Key.OpcodeName); + writeSnippet(OS, Point.Key.Instructions, *InstrInfo_, "; "); OS << kCsvSep; writeEscaped(OS, Point.Key.Config); OS << kCsvSep; - const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName); - if (OpcodeIt != MnemonicToOpcode_.end()) { - const unsigned SchedClassId = - InstrInfo_->get(OpcodeIt->second).getSchedClass(); + assert(!Point.Key.Instructions.empty()); + // FIXME: Resolve variant classes. + const unsigned SchedClassId = + InstrInfo_->get(Point.Key.Instructions[0].getOpcode()).getSchedClass(); #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) - const auto &SchedModel = SubtargetInfo_->getSchedModel(); - const llvm::MCSchedClassDesc *const SCDesc = - SchedModel.getSchedClassDesc(SchedClassId); - writeEscaped(OS, SCDesc->Name); + const auto &SchedModel = SubtargetInfo_->getSchedModel(); + const llvm::MCSchedClassDesc *const SCDesc = + SchedModel.getSchedClassDesc(SchedClassId); + writeEscaped(OS, SCDesc->Name); #else - OS << SchedClassId; + OS << SchedClassId; #endif - } - // FIXME: Print the sched class once InstructionBenchmark separates key into - // (mnemonic, mode, opaque). for (const auto &Measurement : Point.Measurements) { OS << kCsvSep; writeMeasurementValue(OS, Measurement.Value); @@ -118,10 +138,6 @@ const InstructionBenchmark &FirstPoint = Clustering.getPoints().front(); SubtargetInfo_.reset(Target.createMCSubtargetInfo(FirstPoint.LLVMTriple, FirstPoint.CpuName, "")); - - // Build an index of mnemonic->opcode. - for (int I = 0, E = InstrInfo_->getNumOpcodes(); I < E; ++I) - MnemonicToOpcode_.emplace(InstrInfo_->getName(I), I); } template <> @@ -158,16 +174,43 @@ const InstructionBenchmark &Point = Points[PointId]; if (!Point.Error.empty()) continue; - const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName); - if (OpcodeIt == MnemonicToOpcode_.end()) - continue; - const unsigned SchedClassId = - InstrInfo_->get(OpcodeIt->second).getSchedClass(); - PointsPerSchedClass[SchedClassId].push_back(PointId); + assert(!Point.Key.Instructions.empty()); + const auto Opcode = Point.Key.Instructions[0].getOpcode(); + // FIXME: Resolve variant classes. + PointsPerSchedClass[InstrInfo_->get(Opcode).getSchedClass()].push_back( + PointId); } return PointsPerSchedClass; } +// Uops repeat the same opcode over again. Just show this opcode and show the +// whole snippet only on hover. +static void writeUopsSnippetHtml(llvm::raw_ostream &OS, + const std::vector &Instructions, + const llvm::MCInstrInfo &InstrInfo) { + if (Instructions.empty()) + return; + writeEscaped(OS, InstrInfo.getName(Instructions[0].getOpcode())); + if (Instructions.size() > 1) { + OS << " (x" << Instructions.size() << ")"; + } +} + +// Latency tries to find a serial path. Just show the opcode path and show the +// whole snippet only on hover. +static void writeLatencySnippetHtml(llvm::raw_ostream &OS, + const std::vector &Instructions, + const llvm::MCInstrInfo &InstrInfo) { + bool First = true; + for (const llvm::MCInst &Instr : Instructions) { + if (First) + First = false; + else + OS << " → "; + writeEscaped(OS, InstrInfo.getName(Instr.getOpcode())); + } +} + void Analysis::printSchedClassClustersHtml( const std::vector &Clusters, const SchedClass &SC, llvm::raw_ostream &OS) const { @@ -195,8 +238,19 @@ OS << "
    "; for (const size_t PointId : Cluster.getPointIds()) { const auto &Point = Points[PointId]; - OS << "
  • "; - writeEscaped(OS, Point.Key.OpcodeName); + OS << "
  • (OS, Point.Key.Instructions, *InstrInfo_, "\n"); + OS << "\">"; + switch (Point.Mode) { + case InstructionBenchmark::Latency: + writeLatencySnippetHtml(OS, Point.Key.Instructions, *InstrInfo_); + break; + case InstructionBenchmark::Uops: + writeUopsSnippetHtml(OS, Point.Key.Instructions, *InstrInfo_); + break; + default: + llvm_unreachable("invalid mode"); + } OS << " "; writeEscaped(OS, Point.Key.Config); OS << "
  • "; Index: tools/llvm-exegesis/lib/BenchmarkResult.h =================================================================== --- tools/llvm-exegesis/lib/BenchmarkResult.h +++ tools/llvm-exegesis/lib/BenchmarkResult.h @@ -32,7 +32,6 @@ struct InstructionBenchmarkKey { // The LLVM opcode name. - std::string OpcodeName; // FIXME: Deprecated, use Instructions below. std::vector Instructions; // An opaque configuration, that can be used to separate several benchmarks of // the same instruction under different configurations. Index: tools/llvm-exegesis/lib/BenchmarkResult.cpp =================================================================== --- tools/llvm-exegesis/lib/BenchmarkResult.cpp +++ tools/llvm-exegesis/lib/BenchmarkResult.cpp @@ -140,8 +140,7 @@ template <> struct MappingTraits { static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj) { - Io.mapRequired("opcode_name", Obj.OpcodeName); - Io.mapOptional("instructions", Obj.Instructions); + Io.mapRequired("instructions", Obj.Instructions); Io.mapOptional("config", Obj.Config); } }; Index: tools/llvm-exegesis/lib/BenchmarkRunner.cpp =================================================================== --- tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -60,7 +60,6 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration, unsigned Opcode, unsigned NumRepetitions) const { InstructionBenchmark InstrBenchmark; - InstrBenchmark.Key.OpcodeName = State.getInstrInfo().getName(Opcode); InstrBenchmark.Mode = getMode(); InstrBenchmark.CpuName = State.getCpuName(); InstrBenchmark.LLVMTriple = State.getTriple(); Index: unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp +++ unittests/tools/llvm-exegesis/BenchmarkResultTest.cpp @@ -63,7 +63,6 @@ InstructionBenchmark ToDisk; - ToDisk.Key.OpcodeName = "name"; ToDisk.Key.Instructions.push_back(llvm::MCInstBuilder(kInstrId) .addReg(kReg1Id) .addReg(kReg2Id) @@ -91,7 +90,6 @@ const auto FromDisk = ExitOnErr(InstructionBenchmark::readYaml(Ctx, Filename)); - EXPECT_EQ(FromDisk.Key.OpcodeName, ToDisk.Key.OpcodeName); EXPECT_THAT(FromDisk.Key.Instructions, Pointwise(EqMCInst(), ToDisk.Key.Instructions)); EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config); @@ -109,7 +107,6 @@ ExitOnErr(InstructionBenchmark::readYamls(Ctx, Filename)); ASSERT_EQ(FromDiskVector.size(), size_t{1}); const auto FromDisk = FromDiskVector[0]; - EXPECT_EQ(FromDisk.Key.OpcodeName, ToDisk.Key.OpcodeName); EXPECT_THAT(FromDisk.Key.Instructions, Pointwise(EqMCInst(), ToDisk.Key.Instructions)); EXPECT_EQ(FromDisk.Key.Config, ToDisk.Key.Config);