xref: /llvm-project/llvm/tools/llvm-exegesis/lib/Analysis.cpp (revision a1bee62308f1a321d859c78bf11f4aa6bc85d442)
1 //===-- Analysis.cpp --------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "Analysis.h"
11 #include "BenchmarkResult.h"
12 #include "llvm/Support/FormatVariadic.h"
13 #include <unordered_set>
14 #include <vector>
15 
16 namespace exegesis {
17 
18 static const char kCsvSep = ',';
19 
20 static void writeCsvEscaped(llvm::raw_ostream &OS, const std::string &S) {
21   if (std::find(S.begin(), S.end(), kCsvSep) == S.end()) {
22     OS << S;
23   } else {
24     // Needs escaping.
25     OS << '"';
26     for (const char C : S) {
27       if (C == '"')
28         OS << "\"\"";
29       else
30         OS << C;
31     }
32     OS << '"';
33   }
34 }
35 
36 // Prints a row representing an instruction, along with scheduling info and
37 // point coordinates (measurements).
38 void Analysis::printInstructionRow(const bool PrintSchedClass,
39                                    const size_t PointId,
40                                    llvm::raw_ostream &OS) const {
41   const InstructionBenchmark &Point = Clustering_.getPoints()[PointId];
42   const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId);
43   if (ClusterId.isNoise())
44     OS << "[noise]";
45   else if (ClusterId.isError())
46     OS << "[error]";
47   else
48     OS << ClusterId.getId();
49   OS << kCsvSep;
50   writeCsvEscaped(OS, Point.Key.OpcodeName);
51   OS << kCsvSep;
52   writeCsvEscaped(OS, Point.Key.Config);
53   if (PrintSchedClass) {
54     OS << kCsvSep;
55     const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName);
56     if (OpcodeIt != MnemonicToOpcode_.end()) {
57       const unsigned SchedClassId =
58           InstrInfo_->get(OpcodeIt->second).getSchedClass();
59 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
60       const auto &SchedModel = SubtargetInfo_->getSchedModel();
61       const llvm::MCSchedClassDesc *const SCDesc =
62           SchedModel.getSchedClassDesc(SchedClassId);
63       writeCsvEscaped(OS, SCDesc->Name);
64 #else
65       OS << SchedClassId;
66 #endif
67     }
68   }
69   // FIXME: Print the sched class once InstructionBenchmark separates key into
70   // (mnemonic, mode, opaque).
71   for (const auto &Measurement : Point.Measurements) {
72     OS << kCsvSep;
73     writeCsvEscaped(OS, llvm::formatv("{0:F}", Measurement.Value));
74   }
75   OS << "\n";
76 }
77 
78 Analysis::Analysis(const llvm::Target &Target,
79                    const InstructionBenchmarkClustering &Clustering)
80     : Clustering_(Clustering) {
81   if (Clustering.getPoints().empty())
82     return;
83 
84   InstrInfo_.reset(Target.createMCInstrInfo());
85   const InstructionBenchmark &FirstPoint = Clustering.getPoints().front();
86   SubtargetInfo_.reset(Target.createMCSubtargetInfo(FirstPoint.LLVMTriple,
87                                                     FirstPoint.CpuName, ""));
88 
89   // Build an index of mnemonic->opcode.
90   for (int I = 0, E = InstrInfo_->getNumOpcodes(); I < E; ++I)
91     MnemonicToOpcode_.emplace(InstrInfo_->getName(I), I);
92 }
93 
94 template <>
95 llvm::Error
96 Analysis::run<Analysis::PrintClusters>(llvm::raw_ostream &OS) const {
97   if (Clustering_.getPoints().empty())
98     return llvm::Error::success();
99 
100   // Write the header.
101   OS << "cluster_id" << kCsvSep << "opcode_name" << kCsvSep << "config"
102      << kCsvSep << "sched_class";
103   for (const auto &Measurement : Clustering_.getPoints().front().Measurements) {
104     OS << kCsvSep;
105     writeCsvEscaped(OS, Measurement.Key);
106   }
107   OS << "\n";
108 
109   // Write the points.
110   const auto &Clusters = Clustering_.getValidClusters();
111   for (size_t I = 0, E = Clusters.size(); I < E; ++I) {
112     for (const size_t PointId : Clusters[I].PointIndices) {
113       printInstructionRow(/*PrintSchedClass*/ true, PointId, OS);
114     }
115     OS << "\n\n";
116   }
117   return llvm::Error::success();
118 }
119 
120 std::unordered_map<unsigned, std::vector<size_t>>
121 Analysis::makePointsPerSchedClass() const {
122   std::unordered_map<unsigned, std::vector<size_t>> PointsPerSchedClass;
123   const auto &Points = Clustering_.getPoints();
124   for (size_t PointId = 0, E = Points.size(); PointId < E; ++PointId) {
125     const InstructionBenchmark &Point = Points[PointId];
126     if (!Point.Error.empty())
127       continue;
128     const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName);
129     if (OpcodeIt == MnemonicToOpcode_.end())
130       continue;
131     const unsigned SchedClassId =
132         InstrInfo_->get(OpcodeIt->second).getSchedClass();
133     PointsPerSchedClass[SchedClassId].push_back(PointId);
134   }
135   return PointsPerSchedClass;
136 }
137 
138 template <>
139 llvm::Error Analysis::run<Analysis::PrintSchedClassInconsistencies>(
140     llvm::raw_ostream &OS) const {
141   // All the points in a scheduling class should be in the same cluster.
142   // Print any scheduling class for which this is not the case.
143   for (const auto &SchedClassAndPoints : makePointsPerSchedClass()) {
144     std::unordered_set<size_t> ClustersForSchedClass;
145     for (const size_t PointId : SchedClassAndPoints.second) {
146       const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId);
147       if (!ClusterId.isValid())
148         continue; // Ignore noise and errors.
149       ClustersForSchedClass.insert(ClusterId.getId());
150     }
151     if (ClustersForSchedClass.size() <= 1)
152       continue; // Nothing weird.
153 
154     OS << "\nSched Class ";
155 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
156     const auto &SchedModel = SubtargetInfo_->getSchedModel();
157     const llvm::MCSchedClassDesc *const SCDesc =
158         SchedModel.getSchedClassDesc(SchedClassAndPoints.first);
159     OS << SCDesc->Name;
160 #else
161     OS << SchedClassAndPoints.first;
162 #endif
163     OS << " contains instructions with distinct performance "
164           "characteristics, falling into "
165        << ClustersForSchedClass.size() << " clusters:\n";
166     for (const size_t PointId : SchedClassAndPoints.second) {
167       printInstructionRow(/*PrintSchedClass*/ false, PointId, OS);
168     }
169   }
170   return llvm::Error::success();
171 }
172 
173 } // namespace exegesis
174