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 llvm::Error Analysis::printClusters(llvm::raw_ostream &OS) const { 95 if (Clustering_.getPoints().empty()) 96 return llvm::Error::success(); 97 98 // Write the header. 99 OS << "cluster_id" << kCsvSep << "opcode_name" << kCsvSep << "config" 100 << kCsvSep << "sched_class"; 101 for (const auto &Measurement : Clustering_.getPoints().front().Measurements) { 102 OS << kCsvSep; 103 writeCsvEscaped(OS, Measurement.Key); 104 } 105 OS << "\n"; 106 107 // Write the points. 108 const auto &Clusters = Clustering_.getValidClusters(); 109 for (size_t I = 0, E = Clusters.size(); I < E; ++I) { 110 for (const size_t PointId : Clusters[I].PointIndices) { 111 printInstructionRow(/*PrintSchedClass*/ true, PointId, OS); 112 } 113 OS << "\n\n"; 114 } 115 return llvm::Error::success(); 116 } 117 118 std::unordered_map<unsigned, std::vector<size_t>> 119 Analysis::makePointsPerSchedClass() const { 120 std::unordered_map<unsigned, std::vector<size_t>> PointsPerSchedClass; 121 const auto &Points = Clustering_.getPoints(); 122 for (size_t PointId = 0, E = Points.size(); PointId < E; ++PointId) { 123 const InstructionBenchmark &Point = Points[PointId]; 124 if (!Point.Error.empty()) 125 continue; 126 const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName); 127 if (OpcodeIt == MnemonicToOpcode_.end()) 128 continue; 129 const unsigned SchedClassId = 130 InstrInfo_->get(OpcodeIt->second).getSchedClass(); 131 PointsPerSchedClass[SchedClassId].push_back(PointId); 132 } 133 return PointsPerSchedClass; 134 } 135 136 llvm::Error 137 Analysis::printSchedClassInconsistencies(llvm::raw_ostream &OS) const { 138 // All the points in a scheduling class should be in the same cluster. 139 // Print any scheduling class for which this is not the case. 140 for (const auto &SchedClassAndPoints : makePointsPerSchedClass()) { 141 std::unordered_set<size_t> ClustersForSchedClass; 142 for (const size_t PointId : SchedClassAndPoints.second) { 143 const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId); 144 if (!ClusterId.isValid()) 145 continue; // Ignore noise and errors. 146 ClustersForSchedClass.insert(ClusterId.getId()); 147 } 148 if (ClustersForSchedClass.size() <= 1) 149 continue; // Nothing weird. 150 151 OS << "\nSched Class "; 152 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) 153 const auto &SchedModel = SubtargetInfo_->getSchedModel(); 154 const llvm::MCSchedClassDesc *const SCDesc = 155 SchedModel.getSchedClassDesc(SchedClassAndPoints.first); 156 OS << SCDesc->Name; 157 #else 158 OS << SchedClassAndPoints.first; 159 #endif 160 OS << " contains instructions with distinct performance " 161 "characteristics, falling into " 162 << ClustersForSchedClass.size() << " clusters:\n"; 163 for (const size_t PointId : SchedClassAndPoints.second) { 164 printInstructionRow(/*PrintSchedClass*/ false, PointId, OS); 165 } 166 } 167 return llvm::Error::success(); 168 } 169 170 } // namespace exegesis 171