forked from OSchip/llvm-project
[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
This commit is contained in:
parent
e4192a86dc
commit
4273e1e828
|
@ -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 @@ void writeEscaped<kEscapeHtml>(llvm::raw_ostream &OS, const llvm::StringRef S) {
|
|||
}
|
||||
|
||||
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 @@ static void writeMeasurementValue(llvm::raw_ostream &OS, const double Value) {
|
|||
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 @@ void Analysis::printInstructionRowCsv(const size_t PointId,
|
|||
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 @@ Analysis::Analysis(const llvm::Target &Target,
|
|||
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 @@ static void writeUopsSnippetHtml(llvm::raw_ostream &OS,
|
|||
|
||||
// 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 @@ void Analysis::printSchedClassClustersHtml(
|
|||
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 @@ getNonRedundantWriteProcRes(const llvm::MCSchedClassDesc &SCDesc,
|
|||
|
||||
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 @@ bool Analysis::SchedClassCluster::measurementsMatch(
|
|||
}
|
||||
// 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 @@ void Analysis::printSchedClassDescHtml(const SchedClass &SC,
|
|||
"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 << ")";
|
||||
}
|
||||
|
|
|
@ -16,11 +16,16 @@
|
|||
#define LLVM_TOOLS_LLVM_EXEGESIS_ANALYSIS_H
|
||||
|
||||
#include "Clustering.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
|
||||
#include "llvm/MC/MCInstPrinter.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/MC/MCObjectFileInfo.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/TargetRegistry.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
@ -48,7 +53,7 @@ private:
|
|||
SchedClass(const llvm::MCSchedClassDesc &SD,
|
||||
const llvm::MCSubtargetInfo &STI);
|
||||
|
||||
const llvm::MCSchedClassDesc &SCDesc;
|
||||
const llvm::MCSchedClassDesc *const SCDesc;
|
||||
const llvm::SmallVector<llvm::MCWriteProcResEntry, 8>
|
||||
NonRedundantWriteProcRes;
|
||||
const std::vector<std::pair<uint16_t, float>> IdealizedProcResPressure;
|
||||
|
@ -97,9 +102,19 @@ private:
|
|||
std::unordered_map<unsigned, std::vector<size_t>>
|
||||
makePointsPerSchedClass() const;
|
||||
|
||||
template <typename EscapeTag, EscapeTag Tag>
|
||||
void writeSnippet(llvm::raw_ostream &OS, llvm::ArrayRef<uint8_t> Bytes,
|
||||
const char *Separator) const;
|
||||
|
||||
const InstructionBenchmarkClustering &Clustering_;
|
||||
llvm::MCObjectFileInfo ObjectFileInfo_;
|
||||
std::unique_ptr<llvm::MCContext> Context_;
|
||||
std::unique_ptr<llvm::MCSubtargetInfo> SubtargetInfo_;
|
||||
std::unique_ptr<llvm::MCInstrInfo> InstrInfo_;
|
||||
std::unique_ptr<llvm::MCRegisterInfo> RegInfo_;
|
||||
std::unique_ptr<llvm::MCAsmInfo> AsmInfo_;
|
||||
std::unique_ptr<llvm::MCInstPrinter> InstPrinter_;
|
||||
std::unique_ptr<llvm::MCDisassembler> Disasm_;
|
||||
std::unordered_map<std::string, unsigned> MnemonicToOpcode_;
|
||||
};
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "BenchmarkResult.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ObjectYAML/YAML.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
|
@ -146,6 +147,23 @@ template <> struct MappingTraits<exegesis::InstructionBenchmarkKey> {
|
|||
};
|
||||
|
||||
template <> struct MappingTraits<exegesis::InstructionBenchmark> {
|
||||
class NormalizedBinary {
|
||||
public:
|
||||
NormalizedBinary(IO &io) {}
|
||||
NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {}
|
||||
std::vector<uint8_t> denormalize(IO &) {
|
||||
std::vector<uint8_t> Data;
|
||||
std::string Str;
|
||||
raw_string_ostream OSS(Str);
|
||||
Binary.writeAsBinary(OSS);
|
||||
OSS.flush();
|
||||
Data.assign(Str.begin(), Str.end());
|
||||
return Data;
|
||||
}
|
||||
|
||||
BinaryRef Binary;
|
||||
};
|
||||
|
||||
static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) {
|
||||
Io.mapRequired("mode", Obj.Mode);
|
||||
Io.mapRequired("key", Obj.Key);
|
||||
|
@ -155,6 +173,10 @@ template <> struct MappingTraits<exegesis::InstructionBenchmark> {
|
|||
Io.mapRequired("measurements", Obj.Measurements);
|
||||
Io.mapRequired("error", Obj.Error);
|
||||
Io.mapOptional("info", Obj.Info);
|
||||
// AssembledSnippet
|
||||
MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString(
|
||||
Io, Obj.AssembledSnippet);
|
||||
Io.mapOptional("assembled_snippet", BinaryString->Binary);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ struct InstructionBenchmark {
|
|||
std::vector<BenchmarkMeasure> Measurements;
|
||||
std::string Error;
|
||||
std::string Info;
|
||||
std::vector<uint8_t> AssembledSnippet;
|
||||
|
||||
// Read functions.
|
||||
static llvm::Expected<InstructionBenchmark>
|
||||
|
|
|
@ -74,24 +74,41 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration,
|
|||
return InstrBenchmark;
|
||||
}
|
||||
|
||||
for (const auto &MCInst : Snippet)
|
||||
InstrBenchmark.Key.Instructions.push_back(MCInst);
|
||||
InstrBenchmark.Key.Instructions = Snippet;
|
||||
|
||||
std::vector<llvm::MCInst> Code;
|
||||
for (int I = 0; I < InstrBenchmark.NumRepetitions; ++I)
|
||||
Code.push_back(Snippet[I % Snippet.size()]);
|
||||
// Repeat the snippet until there are at least NumInstructions in the
|
||||
// resulting code. The snippet is always repeated at least once.
|
||||
const auto GenerateInstructions = [&Snippet](const int MinInstructions) {
|
||||
std::vector<llvm::MCInst> Code = Snippet;
|
||||
for (int I = 0; I < MinInstructions; ++I)
|
||||
Code.push_back(Snippet[I % Snippet.size()]);
|
||||
return Code;
|
||||
};
|
||||
|
||||
auto ExpectedObjectPath = writeObjectFile(Code);
|
||||
if (llvm::Error E = ExpectedObjectPath.takeError()) {
|
||||
// Assemble at least kMinInstructionsForSnippet instructions by repeating the
|
||||
// snippet for debug/analysis. This is so that the user clearly understands
|
||||
// that the inside instructions are repeated.
|
||||
constexpr const int kMinInstructionsForSnippet = 16;
|
||||
{
|
||||
auto EF = createExecutableFunction(
|
||||
GenerateInstructions(kMinInstructionsForSnippet));
|
||||
if (llvm::Error E = EF.takeError()) {
|
||||
InstrBenchmark.Error = llvm::toString(std::move(E));
|
||||
return InstrBenchmark;
|
||||
}
|
||||
const auto FnBytes = EF->getFunctionBytes();
|
||||
InstrBenchmark.AssembledSnippet.assign(FnBytes.begin(), FnBytes.end());
|
||||
}
|
||||
|
||||
// Assemble NumRepetitions instructions repetitions of the snippet for
|
||||
// measurements.
|
||||
auto EF = createExecutableFunction(
|
||||
GenerateInstructions(InstrBenchmark.NumRepetitions));
|
||||
if (llvm::Error E = EF.takeError()) {
|
||||
InstrBenchmark.Error = llvm::toString(std::move(E));
|
||||
return InstrBenchmark;
|
||||
}
|
||||
|
||||
// FIXME: Check if TargetMachine or ExecutionEngine can be reused instead of
|
||||
// creating one everytime.
|
||||
const ExecutableFunction EF(State.createTargetMachine(),
|
||||
getObjectFromFile(*ExpectedObjectPath));
|
||||
InstrBenchmark.Measurements = runMeasurements(EF, NumRepetitions);
|
||||
InstrBenchmark.Measurements = runMeasurements(*EF, NumRepetitions);
|
||||
|
||||
return InstrBenchmark;
|
||||
}
|
||||
|
@ -110,4 +127,17 @@ BenchmarkRunner::writeObjectFile(llvm::ArrayRef<llvm::MCInst> Code) const {
|
|||
return ResultPath.str();
|
||||
}
|
||||
|
||||
llvm::Expected<ExecutableFunction> BenchmarkRunner::createExecutableFunction(
|
||||
llvm::ArrayRef<llvm::MCInst> Code) const {
|
||||
auto ExpectedObjectPath = writeObjectFile(Code);
|
||||
if (llvm::Error E = ExpectedObjectPath.takeError()) {
|
||||
return std::move(E);
|
||||
}
|
||||
|
||||
// FIXME: Check if TargetMachine or ExecutionEngine can be reused instead of
|
||||
// creating one everytime.
|
||||
return ExecutableFunction(State.createTargetMachine(),
|
||||
getObjectFromFile(*ExpectedObjectPath));
|
||||
}
|
||||
|
||||
} // namespace exegesis
|
||||
|
|
|
@ -91,6 +91,8 @@ private:
|
|||
|
||||
llvm::Expected<std::string>
|
||||
writeObjectFile(llvm::ArrayRef<llvm::MCInst> Code) const;
|
||||
llvm::Expected<ExecutableFunction>
|
||||
createExecutableFunction(llvm::ArrayRef<llvm::MCInst> Code) const;
|
||||
};
|
||||
|
||||
} // namespace exegesis
|
||||
|
|
|
@ -22,8 +22,10 @@ llvm_map_components_to_libnames(libs
|
|||
ExecutionEngine
|
||||
GlobalISel
|
||||
MC
|
||||
MCDisassembler
|
||||
MCJIT
|
||||
Object
|
||||
ObjectYAML
|
||||
Support
|
||||
)
|
||||
|
||||
|
|
|
@ -19,4 +19,4 @@
|
|||
type = Library
|
||||
name = Exegesis
|
||||
parent = Libraries
|
||||
required_libraries = CodeGen ExecutionEngine MC MCJIT Object Support
|
||||
required_libraries = CodeGen ExecutionEngine MC MCDisassembler MCJIT Object ObjectYAML Support
|
||||
|
|
|
@ -180,6 +180,7 @@ static void analysisMain() {
|
|||
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
llvm::InitializeNativeTargetDisassembler();
|
||||
// Read benchmarks.
|
||||
const LLVMState State;
|
||||
const std::vector<InstructionBenchmark> Points =
|
||||
|
|
Loading…
Reference in New Issue