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