Index: llvm/trunk/tools/llvm-exegesis/lib/Analysis.h =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Analysis.h +++ llvm/trunk/tools/llvm-exegesis/lib/Analysis.h @@ -18,8 +18,8 @@ #include "Clustering.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/Support/TargetRegistry.h" #include "llvm/Support/Error.h" +#include "llvm/Support/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -29,19 +29,28 @@ // A helper class to analyze benchmark results for a target. class Analysis { public: - Analysis(const llvm::Target& Target, const InstructionBenchmarkClustering &Clustering); + Analysis(const llvm::Target &Target, + const InstructionBenchmarkClustering &Clustering); // Prints a csv of instructions for each cluster. llvm::Error printClusters(llvm::raw_ostream &OS) const; - private: - void printInstructionRow(size_t ClusterId, size_t PointId, - llvm::raw_ostream &OS) const; - - const InstructionBenchmarkClustering & Clustering_; - std::unique_ptr SubtargetInfo_; - std::unique_ptr InstrInfo_; - std::unordered_map MnemonicToOpcode_; + // Find potential errors in the scheduling information given measurements. + llvm::Error printSchedClassInconsistencies(llvm::raw_ostream &OS) const; + +private: + void printInstructionRow(bool PrintSchedClass, size_t PointId, + llvm::raw_ostream &OS) const; + + // Builds a map of Sched Class -> indices of points that belong to the sched + // class. + std::unordered_map> + makePointsPerSchedClass() const; + + const InstructionBenchmarkClustering &Clustering_; + std::unique_ptr SubtargetInfo_; + std::unique_ptr InstrInfo_; + std::unordered_map MnemonicToOpcode_; }; } // namespace exegesis Index: llvm/trunk/tools/llvm-exegesis/lib/Analysis.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Analysis.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/Analysis.cpp @@ -10,6 +10,7 @@ #include "Analysis.h" #include "BenchmarkResult.h" #include "llvm/Support/FormatVariadic.h" +#include #include namespace exegesis { @@ -34,26 +35,36 @@ // Prints a row representing an instruction, along with scheduling info and // point coordinates (measurements). -void Analysis::printInstructionRow(const size_t ClusterId, const size_t PointId, +void Analysis::printInstructionRow(const bool PrintSchedClass, + const size_t PointId, llvm::raw_ostream &OS) const { const InstructionBenchmark &Point = Clustering_.getPoints()[PointId]; - - OS << ClusterId << kCsvSep; + const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId); + if (ClusterId.isNoise()) + OS << "[noise]"; + else if (ClusterId.isError()) + OS << "[error]"; + else + OS << ClusterId.getId(); + OS << kCsvSep; writeCsvEscaped(OS, Point.Key.OpcodeName); OS << kCsvSep; writeCsvEscaped(OS, Point.Key.Config); - OS << kCsvSep; - const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName); - if (OpcodeIt != MnemonicToOpcode_.end()) { - const unsigned SchedClassId = InstrInfo_->get(OpcodeIt->second).getSchedClass(); + if (PrintSchedClass) { + OS << kCsvSep; + const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName); + if (OpcodeIt != MnemonicToOpcode_.end()) { + const unsigned SchedClassId = + InstrInfo_->get(OpcodeIt->second).getSchedClass(); #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) - const auto &SchedModel = SubtargetInfo_->getSchedModel(); - const llvm::MCSchedClassDesc *const SCDesc = - SchedModel.getSchedClassDesc(SchedClassId); - writeCsvEscaped(OS, SCDesc->Name); + const auto &SchedModel = SubtargetInfo_->getSchedModel(); + const llvm::MCSchedClassDesc *const SCDesc = + SchedModel.getSchedClassDesc(SchedClassId); + writeCsvEscaped(OS, SCDesc->Name); #else - OS << SchedClassId; + OS << SchedClassId; #endif + } } // FIXME: Print the sched class once InstructionBenchmark separates key into // (mnemonic, mode, opaque). @@ -72,8 +83,8 @@ InstrInfo_.reset(Target.createMCInstrInfo()); const InstructionBenchmark &FirstPoint = Clustering.getPoints().front(); - SubtargetInfo_.reset(Target.createMCSubtargetInfo( - FirstPoint.LLVMTriple, FirstPoint.CpuName, "")); + SubtargetInfo_.reset(Target.createMCSubtargetInfo(FirstPoint.LLVMTriple, + FirstPoint.CpuName, "")); // Build an index of mnemonic->opcode. for (int I = 0, E = InstrInfo_->getNumOpcodes(); I < E; ++I) @@ -94,14 +105,66 @@ OS << "\n"; // Write the points. - const auto& Clusters = Clustering_.getValidClusters(); + const auto &Clusters = Clustering_.getValidClusters(); for (size_t I = 0, E = Clusters.size(); I < E; ++I) { for (const size_t PointId : Clusters[I].PointIndices) { - printInstructionRow(I, PointId, OS); + printInstructionRow(/*PrintSchedClass*/ true, PointId, OS); } OS << "\n\n"; } return llvm::Error::success(); } +std::unordered_map> +Analysis::makePointsPerSchedClass() const { + std::unordered_map> PointsPerSchedClass; + const auto &Points = Clustering_.getPoints(); + for (size_t PointId = 0, E = Points.size(); PointId < E; ++PointId) { + const InstructionBenchmark &Point = Points[PointId]; + if (!Point.Error.empty()) + continue; + const auto OpcodeIt = MnemonicToOpcode_.find(Point.Key.OpcodeName); + if (OpcodeIt == MnemonicToOpcode_.end()) + continue; + const unsigned SchedClassId = + InstrInfo_->get(OpcodeIt->second).getSchedClass(); + PointsPerSchedClass[SchedClassId].push_back(PointId); + } + return PointsPerSchedClass; +} + +llvm::Error +Analysis::printSchedClassInconsistencies(llvm::raw_ostream &OS) const { + // All the points in a scheduling class should be in the same cluster. + // Print any scheduling class for which this is not the case. + for (const auto &SchedClassAndPoints : makePointsPerSchedClass()) { + std::unordered_set ClustersForSchedClass; + for (const size_t PointId : SchedClassAndPoints.second) { + const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId); + if (!ClusterId.isValid()) + continue; // Ignore noise and errors. + ClustersForSchedClass.insert(ClusterId.getId()); + } + if (ClustersForSchedClass.size() <= 1) + continue; // Nothing weird. + + OS << "\nSched Class "; +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + const auto &SchedModel = SubtargetInfo_->getSchedModel(); + const llvm::MCSchedClassDesc *const SCDesc = + SchedModel.getSchedClassDesc(SchedClassAndPoints.first); + OS << SCDesc->Name; +#else + OS << SchedClassAndPoints.first; +#endif + OS << " contains instructions with distinct performance " + "characteristics, falling into " + << ClustersForSchedClass.size() << " clusters:\n"; + for (const size_t PointId : SchedClassAndPoints.second) { + printInstructionRow(/*PrintSchedClass*/ false, PointId, OS); + } + } + return llvm::Error::success(); +} + } // namespace exegesis Index: llvm/trunk/tools/llvm-exegesis/llvm-exegesis.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/llvm-exegesis.cpp +++ llvm/trunk/tools/llvm-exegesis/llvm-exegesis.cpp @@ -160,6 +160,9 @@ if (auto Err = Analyzer.printClusters(ClustersOS)) llvm::report_fatal_error(std::move(Err)); + + if (auto Err = Analyzer.printSchedClassInconsistencies(llvm::outs())) + llvm::report_fatal_error(std::move(Err)); } } // namespace exegesis