namespace {
-enum EscapeTag { kEscapeCsv, kEscapeHtml };
+enum EscapeTag { kEscapeCsv, kEscapeHtml, kEscapeHtmlString };
template <EscapeTag Tag>
void writeEscaped(llvm::raw_ostream &OS, const llvm::StringRef S);
}
}
+template <>
+void writeEscaped<kEscapeHtmlString>(llvm::raw_ostream &OS, const llvm::StringRef S) {
+ for (const char C : S) {
+ if (C == '"')
+ OS << "\\\"";
+ else
+ OS << C;
+ }
+}
+
} // namespace
template <EscapeTag Tag>
writeEscaped<Tag>(OS, llvm::formatv("{0:F}", Value).str());
}
+template <EscapeTag Tag>
+static void writeSnippet(llvm::raw_ostream &OS,
+ const std::vector<llvm::MCInst> &Instructions,
+ const llvm::MCInstrInfo &InstrInfo,
+ const char* Separator) {
+ // FIXME: Print operands.
+ llvm::SmallVector<llvm::StringRef, 3> Opcodes;
+ for (const llvm::MCInst &Instr : Instructions) {
+ Opcodes.push_back(InstrInfo.getName(Instr.getOpcode()));
+ }
+ writeEscaped<Tag>(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,
const InstructionBenchmark &Point = Clustering_.getPoints()[PointId];
writeClusterId<kEscapeCsv>(OS, Clustering_.getClusterIdForPoint(PointId));
OS << kCsvSep;
- writeEscaped<kEscapeCsv>(OS, Point.Key.OpcodeName);
+ writeSnippet<kEscapeCsv>(OS, Point.Key.Instructions, *InstrInfo_, "; ");
OS << kCsvSep;
writeEscaped<kEscapeCsv>(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<kEscapeCsv>(OS, SCDesc->Name);
+ const auto &SchedModel = SubtargetInfo_->getSchedModel();
+ const llvm::MCSchedClassDesc *const SCDesc =
+ SchedModel.getSchedClassDesc(SchedClassId);
+ writeEscaped<kEscapeCsv>(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<kEscapeCsv>(OS, Measurement.Value);
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 <>
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<llvm::MCInst> &Instructions,
+ const llvm::MCInstrInfo &InstrInfo) {
+ if (Instructions.empty())
+ return;
+ writeEscaped<kEscapeHtml>(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<llvm::MCInst> &Instructions,
+ const llvm::MCInstrInfo &InstrInfo) {
+ bool First = true;
+ for (const llvm::MCInst &Instr : Instructions) {
+ if (First)
+ First = false;
+ else
+ OS << " → ";
+ writeEscaped<kEscapeHtml>(OS, InstrInfo.getName(Instr.getOpcode()));
+ }
+}
+
void Analysis::printSchedClassClustersHtml(
const std::vector<SchedClassCluster> &Clusters, const SchedClass &SC,
llvm::raw_ostream &OS) const {
OS << "</td><td><ul>";
for (const size_t PointId : Cluster.getPointIds()) {
const auto &Point = Points[PointId];
- OS << "<li><span class=\"mono\">";
- writeEscaped<kEscapeHtml>(OS, Point.Key.OpcodeName);
+ OS << "<li><span class=\"mono\" title=\"";
+ writeSnippet<kEscapeHtmlString>(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 << "</span> <span class=\"mono\">";
writeEscaped<kEscapeHtml>(OS, Point.Key.Config);
OS << "</span></li>";