diff options
author | Clement Courbet <courbet@google.com> | 2018-06-15 07:30:45 +0000 |
---|---|---|
committer | Clement Courbet <courbet@google.com> | 2018-06-15 07:30:45 +0000 |
commit | 4273e1e828f33692bfc45a09cd68c6ebed2aec15 (patch) | |
tree | 6397bb9100f33a4e62f8969308c2cb90252282c8 /llvm | |
parent | e4192a86dc564b5d3e19e477179275f1b2a71f4e (diff) | |
download | bcm5719-llvm-4273e1e828f33692bfc45a09cd68c6ebed2aec15.tar.gz bcm5719-llvm-4273e1e828f33692bfc45a09cd68c6ebed2aec15.zip |
[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
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/Analysis.cpp | 97 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/Analysis.h | 17 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp | 22 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/BenchmarkResult.h | 1 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp | 58 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h | 2 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/lib/LLVMBuild.txt | 2 | ||||
-rw-r--r-- | llvm/tools/llvm-exegesis/llvm-exegesis.cpp | 1 |
9 files changed, 152 insertions, 50 deletions
diff --git a/llvm/tools/llvm-exegesis/lib/Analysis.cpp b/llvm/tools/llvm-exegesis/lib/Analysis.cpp index 842790d74c7..bb511808096 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 @@ 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 << ")"; } diff --git a/llvm/tools/llvm-exegesis/lib/Analysis.h b/llvm/tools/llvm-exegesis/lib/Analysis.h index c6218052fae..ae0e04fda9d 100644 --- a/llvm/tools/llvm-exegesis/lib/Analysis.h +++ b/llvm/tools/llvm-exegesis/lib/Analysis.h @@ -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_; }; diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp index 841219cb7f2..33ad65075d9 100644 --- a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.cpp @@ -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); } }; diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h index c226aa0020a..561cd87f7ed 100644 --- a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h @@ -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> diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp index bed802d0b1e..188ee2d3731 100644 --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -74,24 +74,41 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration, return InstrBenchmark; } - for (const auto &MCInst : Snippet) - InstrBenchmark.Key.Instructions.push_back(MCInst); - - std::vector<llvm::MCInst> Code; - for (int I = 0; I < InstrBenchmark.NumRepetitions; ++I) - Code.push_back(Snippet[I % Snippet.size()]); + InstrBenchmark.Key.Instructions = Snippet; + + // 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; + }; + + // 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()); + } - auto ExpectedObjectPath = writeObjectFile(Code); - if (llvm::Error E = ExpectedObjectPath.takeError()) { + // 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 diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h index 5e8c061018b..4705e713a92 100644 --- a/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ b/llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -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 diff --git a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt index 3f82fd2bbd9..10cf16af95e 100644 --- a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt +++ b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt @@ -22,8 +22,10 @@ llvm_map_components_to_libnames(libs ExecutionEngine GlobalISel MC + MCDisassembler MCJIT Object + ObjectYAML Support ) diff --git a/llvm/tools/llvm-exegesis/lib/LLVMBuild.txt b/llvm/tools/llvm-exegesis/lib/LLVMBuild.txt index 0e4c6d12db2..e02ea7151df 100644 --- a/llvm/tools/llvm-exegesis/lib/LLVMBuild.txt +++ b/llvm/tools/llvm-exegesis/lib/LLVMBuild.txt @@ -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 diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp index 687469df817..09e9693a96a 100644 --- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -180,6 +180,7 @@ static void analysisMain() { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetDisassembler(); // Read benchmarks. const LLVMState State; const std::vector<InstructionBenchmark> Points = |