[llvm-exegesis] Print the whole snippet in analysis.
Summary:
On hover, the whole asm snippet is displayed, including operands.
This requires the actual assembly output instead of just the MCInsts:
This is because some pseudo-instructions get lowered to actual target
instructions during codegen (e.g. ABS_Fp32 -> SSE or X87).
Reviewers: gchatelet
Subscribers: mgorny, tschuett, llvm-commits
Differential Revision: https://reviews.llvm.org/D48164
llvm-svn: 334805
diff --git a/llvm/tools/llvm-exegesis/lib/Analysis.cpp b/llvm/tools/llvm-exegesis/lib/Analysis.cpp
index 842790d..bb51180 100644
--- a/llvm/tools/llvm-exegesis/lib/Analysis.cpp
+++ b/llvm/tools/llvm-exegesis/lib/Analysis.cpp
@@ -10,6 +10,7 @@
#include "Analysis.h"
#include "BenchmarkResult.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/MC/MCAsmInfo.h"
#include "llvm/Support/FormatVariadic.h"
#include <unordered_set>
#include <vector>
@@ -57,7 +58,8 @@
}
template <>
-void writeEscaped<kEscapeHtmlString>(llvm::raw_ostream &OS, const llvm::StringRef S) {
+void writeEscaped<kEscapeHtmlString>(llvm::raw_ostream &OS,
+ const llvm::StringRef S) {
for (const char C : S) {
if (C == '"')
OS << "\\\"";
@@ -85,17 +87,31 @@
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()));
+template <typename EscapeTag, EscapeTag Tag>
+void Analysis::writeSnippet(llvm::raw_ostream &OS,
+ llvm::ArrayRef<uint8_t> Bytes,
+ const char *Separator) const {
+ llvm::SmallVector<std::string, 3> Lines;
+ // Parse the asm snippet and print it.
+ while (!Bytes.empty()) {
+ llvm::MCInst MI;
+ uint64_t MISize = 0;
+ if (!Disasm_->getInstruction(MI, MISize, Bytes, 0, llvm::nulls(),
+ llvm::nulls())) {
+ writeEscaped<Tag>(OS, llvm::join(Lines, Separator));
+ writeEscaped<Tag>(OS, Separator);
+ writeEscaped<Tag>(OS, "[error decoding asm snippet]");
+ return;
+ }
+ Lines.emplace_back();
+ std::string &Line = Lines.back();
+ llvm::raw_string_ostream OSS(Line);
+ InstPrinter_->printInst(&MI, OSS, "", *SubtargetInfo_);
+ Bytes = Bytes.drop_front(MISize);
+ OSS.flush();
+ Line = llvm::StringRef(Line).trim().str();
}
- writeEscaped<Tag>(OS, llvm::join(Opcodes, Separator));
+ writeEscaped<Tag>(OS, llvm::join(Lines, Separator));
}
// Prints a row representing an instruction, along with scheduling info and
@@ -105,7 +121,7 @@
const InstructionBenchmark &Point = Clustering_.getPoints()[PointId];
writeClusterId<kEscapeCsv>(OS, Clustering_.getClusterIdForPoint(PointId));
OS << kCsvSep;
- writeSnippet<kEscapeCsv>(OS, Point.Key.Instructions, *InstrInfo_, "; ");
+ writeSnippet<EscapeTag, kEscapeCsv>(OS, Point.AssembledSnippet, "; ");
OS << kCsvSep;
writeEscaped<kEscapeCsv>(OS, Point.Key.Config);
OS << kCsvSep;
@@ -134,10 +150,21 @@
if (Clustering.getPoints().empty())
return;
- InstrInfo_.reset(Target.createMCInstrInfo());
const InstructionBenchmark &FirstPoint = Clustering.getPoints().front();
+ InstrInfo_.reset(Target.createMCInstrInfo());
+ RegInfo_.reset(Target.createMCRegInfo(FirstPoint.LLVMTriple));
+ AsmInfo_.reset(Target.createMCAsmInfo(*RegInfo_, FirstPoint.LLVMTriple));
SubtargetInfo_.reset(Target.createMCSubtargetInfo(FirstPoint.LLVMTriple,
FirstPoint.CpuName, ""));
+ InstPrinter_.reset(Target.createMCInstPrinter(
+ llvm::Triple(FirstPoint.LLVMTriple), 0 /*default variant*/, *AsmInfo_,
+ *InstrInfo_, *RegInfo_));
+
+ Context_ = llvm::make_unique<llvm::MCContext>(AsmInfo_.get(), RegInfo_.get(),
+ &ObjectFileInfo_);
+ Disasm_.reset(Target.createMCDisassembler(*SubtargetInfo_, *Context_));
+ assert(Disasm_ && "cannot create MCDisassembler. missing call to "
+ "InitializeXXXTargetDisassembler ?");
}
template <>
@@ -197,9 +224,10 @@
// 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) {
+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)
@@ -238,17 +266,18 @@
for (const size_t PointId : Cluster.getPointIds()) {
const auto &Point = Points[PointId];
OS << "<li><span class=\"mono\" title=\"";
- writeSnippet<kEscapeHtmlString>(OS, Point.Key.Instructions, *InstrInfo_, "\n");
+ writeSnippet<EscapeTag, kEscapeHtmlString>(OS, Point.AssembledSnippet,
+ "\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");
+ 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);
@@ -345,7 +374,7 @@
Analysis::SchedClass::SchedClass(const llvm::MCSchedClassDesc &SD,
const llvm::MCSubtargetInfo &STI)
- : SCDesc(SD),
+ : SCDesc(&SD),
NonRedundantWriteProcRes(getNonRedundantWriteProcRes(SD, STI)),
IdealizedProcResPressure(computeIdealizedProcResPressure(
STI.getSchedModel(), NonRedundantWriteProcRes)) {}
@@ -382,9 +411,9 @@
}
// Find the latency.
SchedClassPoint[0].Value = 0.0;
- for (unsigned I = 0; I < SC.SCDesc.NumWriteLatencyEntries; ++I) {
+ for (unsigned I = 0; I < SC.SCDesc->NumWriteLatencyEntries; ++I) {
const llvm::MCWriteLatencyEntry *const WLE =
- STI.getWriteLatencyEntry(&SC.SCDesc, I);
+ STI.getWriteLatencyEntry(SC.SCDesc, I);
SchedClassPoint[0].Value =
std::max<double>(SchedClassPoint[0].Value, WLE->Cycles);
}
@@ -425,19 +454,19 @@
"th><th>WriteProcRes</th><th title=\"This is the idealized unit "
"resource (port) pressure assuming ideal distribution\">Idealized "
"Resource Pressure</th></tr>";
- if (SC.SCDesc.isValid()) {
+ if (SC.SCDesc->isValid()) {
const auto &SM = SubtargetInfo_->getSchedModel();
OS << "<tr><td>✔</td>";
- OS << "<td>" << (SC.SCDesc.isVariant() ? "✔" : "✕")
+ OS << "<td>" << (SC.SCDesc->isVariant() ? "✔" : "✕")
<< "</td>";
- OS << "<td>" << SC.SCDesc.NumMicroOps << "</td>";
+ OS << "<td>" << SC.SCDesc->NumMicroOps << "</td>";
// Latencies.
OS << "<td><ul>";
- for (int I = 0, E = SC.SCDesc.NumWriteLatencyEntries; I < E; ++I) {
+ for (int I = 0, E = SC.SCDesc->NumWriteLatencyEntries; I < E; ++I) {
const auto *const Entry =
- SubtargetInfo_->getWriteLatencyEntry(&SC.SCDesc, I);
+ SubtargetInfo_->getWriteLatencyEntry(SC.SCDesc, I);
OS << "<li>" << Entry->Cycles;
- if (SC.SCDesc.NumWriteLatencyEntries > 1) {
+ if (SC.SCDesc->NumWriteLatencyEntries > 1) {
// Dismabiguate if more than 1 latency.
OS << " (WriteResourceID " << Entry->WriteResourceID << ")";
}