Analysis.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. //===-- Analysis.cpp --------------------------------------------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "Analysis.h"
  9. #include "BenchmarkResult.h"
  10. #include "llvm/ADT/STLExtras.h"
  11. #include "llvm/MC/MCAsmInfo.h"
  12. #include "llvm/MC/MCTargetOptions.h"
  13. #include "llvm/Support/FormatVariadic.h"
  14. #include <limits>
  15. #include <unordered_set>
  16. #include <vector>
  17. namespace llvm {
  18. namespace exegesis {
  19. static const char kCsvSep = ',';
  20. namespace {
  21. enum EscapeTag { kEscapeCsv, kEscapeHtml, kEscapeHtmlString };
  22. template <EscapeTag Tag> void writeEscaped(raw_ostream &OS, const StringRef S);
  23. template <> void writeEscaped<kEscapeCsv>(raw_ostream &OS, const StringRef S) {
  24. if (!llvm::is_contained(S, kCsvSep)) {
  25. OS << S;
  26. } else {
  27. // Needs escaping.
  28. OS << '"';
  29. for (const char C : S) {
  30. if (C == '"')
  31. OS << "\"\"";
  32. else
  33. OS << C;
  34. }
  35. OS << '"';
  36. }
  37. }
  38. template <> void writeEscaped<kEscapeHtml>(raw_ostream &OS, const StringRef S) {
  39. for (const char C : S) {
  40. if (C == '<')
  41. OS << "&lt;";
  42. else if (C == '>')
  43. OS << "&gt;";
  44. else if (C == '&')
  45. OS << "&amp;";
  46. else
  47. OS << C;
  48. }
  49. }
  50. template <>
  51. void writeEscaped<kEscapeHtmlString>(raw_ostream &OS, const StringRef S) {
  52. for (const char C : S) {
  53. if (C == '"')
  54. OS << "\\\"";
  55. else
  56. OS << C;
  57. }
  58. }
  59. } // namespace
  60. template <EscapeTag Tag>
  61. static void
  62. writeClusterId(raw_ostream &OS,
  63. const InstructionBenchmarkClustering::ClusterId &CID) {
  64. if (CID.isNoise())
  65. writeEscaped<Tag>(OS, "[noise]");
  66. else if (CID.isError())
  67. writeEscaped<Tag>(OS, "[error]");
  68. else
  69. OS << CID.getId();
  70. }
  71. template <EscapeTag Tag>
  72. static void writeMeasurementValue(raw_ostream &OS, const double Value) {
  73. // Given Value, if we wanted to serialize it to a string,
  74. // how many base-10 digits will we need to store, max?
  75. static constexpr auto MaxDigitCount =
  76. std::numeric_limits<decltype(Value)>::max_digits10;
  77. // Also, we will need a decimal separator.
  78. static constexpr auto DecimalSeparatorLen = 1; // '.' e.g.
  79. // So how long of a string will the serialization produce, max?
  80. static constexpr auto SerializationLen = MaxDigitCount + DecimalSeparatorLen;
  81. // WARNING: when changing the format, also adjust the small-size estimate ^.
  82. static constexpr StringLiteral SimpleFloatFormat = StringLiteral("{0:F}");
  83. writeEscaped<Tag>(
  84. OS, formatv(SimpleFloatFormat.data(), Value).sstr<SerializationLen>());
  85. }
  86. template <typename EscapeTag, EscapeTag Tag>
  87. void Analysis::writeSnippet(raw_ostream &OS, ArrayRef<uint8_t> Bytes,
  88. const char *Separator) const {
  89. SmallVector<std::string, 3> Lines;
  90. const auto &SI = State_.getSubtargetInfo();
  91. // Parse the asm snippet and print it.
  92. while (!Bytes.empty()) {
  93. MCInst MI;
  94. uint64_t MISize = 0;
  95. if (!Disasm_->getInstruction(MI, MISize, Bytes, 0, nulls())) {
  96. writeEscaped<Tag>(OS, join(Lines, Separator));
  97. writeEscaped<Tag>(OS, Separator);
  98. writeEscaped<Tag>(OS, "[error decoding asm snippet]");
  99. return;
  100. }
  101. SmallString<128> InstPrinterStr; // FIXME: magic number.
  102. raw_svector_ostream OSS(InstPrinterStr);
  103. InstPrinter_->printInst(&MI, 0, "", SI, OSS);
  104. Bytes = Bytes.drop_front(MISize);
  105. Lines.emplace_back(InstPrinterStr.str().trim());
  106. }
  107. writeEscaped<Tag>(OS, join(Lines, Separator));
  108. }
  109. // Prints a row representing an instruction, along with scheduling info and
  110. // point coordinates (measurements).
  111. void Analysis::printInstructionRowCsv(const size_t PointId,
  112. raw_ostream &OS) const {
  113. const InstructionBenchmark &Point = Clustering_.getPoints()[PointId];
  114. writeClusterId<kEscapeCsv>(OS, Clustering_.getClusterIdForPoint(PointId));
  115. OS << kCsvSep;
  116. writeSnippet<EscapeTag, kEscapeCsv>(OS, Point.AssembledSnippet, "; ");
  117. OS << kCsvSep;
  118. writeEscaped<kEscapeCsv>(OS, Point.Key.Config);
  119. OS << kCsvSep;
  120. assert(!Point.Key.Instructions.empty());
  121. const MCInst &MCI = Point.keyInstruction();
  122. unsigned SchedClassId;
  123. std::tie(SchedClassId, std::ignore) = ResolvedSchedClass::resolveSchedClassId(
  124. State_.getSubtargetInfo(), State_.getInstrInfo(), MCI);
  125. #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  126. const MCSchedClassDesc *const SCDesc =
  127. State_.getSubtargetInfo().getSchedModel().getSchedClassDesc(SchedClassId);
  128. writeEscaped<kEscapeCsv>(OS, SCDesc->Name);
  129. #else
  130. OS << SchedClassId;
  131. #endif
  132. for (const auto &Measurement : Point.Measurements) {
  133. OS << kCsvSep;
  134. writeMeasurementValue<kEscapeCsv>(OS, Measurement.PerInstructionValue);
  135. }
  136. OS << "\n";
  137. }
  138. Analysis::Analysis(const LLVMState &State,
  139. const InstructionBenchmarkClustering &Clustering,
  140. double AnalysisInconsistencyEpsilon,
  141. bool AnalysisDisplayUnstableOpcodes)
  142. : Clustering_(Clustering), State_(State),
  143. AnalysisInconsistencyEpsilonSquared_(AnalysisInconsistencyEpsilon *
  144. AnalysisInconsistencyEpsilon),
  145. AnalysisDisplayUnstableOpcodes_(AnalysisDisplayUnstableOpcodes) {
  146. if (Clustering.getPoints().empty())
  147. return;
  148. MCTargetOptions MCOptions;
  149. const auto &TM = State.getTargetMachine();
  150. const auto &Triple = TM.getTargetTriple();
  151. AsmInfo_.reset(TM.getTarget().createMCAsmInfo(State_.getRegInfo(),
  152. Triple.str(), MCOptions));
  153. InstPrinter_.reset(TM.getTarget().createMCInstPrinter(
  154. Triple, 0 /*default variant*/, *AsmInfo_, State_.getInstrInfo(),
  155. State_.getRegInfo()));
  156. Context_ = std::make_unique<MCContext>(
  157. Triple, AsmInfo_.get(), &State_.getRegInfo(), &State_.getSubtargetInfo());
  158. Disasm_.reset(TM.getTarget().createMCDisassembler(State_.getSubtargetInfo(),
  159. *Context_));
  160. assert(Disasm_ && "cannot create MCDisassembler. missing call to "
  161. "InitializeXXXTargetDisassembler ?");
  162. }
  163. template <>
  164. Error Analysis::run<Analysis::PrintClusters>(raw_ostream &OS) const {
  165. if (Clustering_.getPoints().empty())
  166. return Error::success();
  167. // Write the header.
  168. OS << "cluster_id" << kCsvSep << "opcode_name" << kCsvSep << "config"
  169. << kCsvSep << "sched_class";
  170. for (const auto &Measurement : Clustering_.getPoints().front().Measurements) {
  171. OS << kCsvSep;
  172. writeEscaped<kEscapeCsv>(OS, Measurement.Key);
  173. }
  174. OS << "\n";
  175. // Write the points.
  176. for (const auto &ClusterIt : Clustering_.getValidClusters()) {
  177. for (const size_t PointId : ClusterIt.PointIndices) {
  178. printInstructionRowCsv(PointId, OS);
  179. }
  180. OS << "\n\n";
  181. }
  182. return Error::success();
  183. }
  184. Analysis::ResolvedSchedClassAndPoints::ResolvedSchedClassAndPoints(
  185. ResolvedSchedClass &&RSC)
  186. : RSC(std::move(RSC)) {}
  187. std::vector<Analysis::ResolvedSchedClassAndPoints>
  188. Analysis::makePointsPerSchedClass() const {
  189. std::vector<ResolvedSchedClassAndPoints> Entries;
  190. // Maps SchedClassIds to index in result.
  191. std::unordered_map<unsigned, size_t> SchedClassIdToIndex;
  192. const auto &Points = Clustering_.getPoints();
  193. for (size_t PointId = 0, E = Points.size(); PointId < E; ++PointId) {
  194. const InstructionBenchmark &Point = Points[PointId];
  195. if (!Point.Error.empty())
  196. continue;
  197. assert(!Point.Key.Instructions.empty());
  198. // FIXME: we should be using the tuple of classes for instructions in the
  199. // snippet as key.
  200. const MCInst &MCI = Point.keyInstruction();
  201. unsigned SchedClassId;
  202. bool WasVariant;
  203. std::tie(SchedClassId, WasVariant) =
  204. ResolvedSchedClass::resolveSchedClassId(State_.getSubtargetInfo(),
  205. State_.getInstrInfo(), MCI);
  206. const auto IndexIt = SchedClassIdToIndex.find(SchedClassId);
  207. if (IndexIt == SchedClassIdToIndex.end()) {
  208. // Create a new entry.
  209. SchedClassIdToIndex.emplace(SchedClassId, Entries.size());
  210. ResolvedSchedClassAndPoints Entry(ResolvedSchedClass(
  211. State_.getSubtargetInfo(), SchedClassId, WasVariant));
  212. Entry.PointIds.push_back(PointId);
  213. Entries.push_back(std::move(Entry));
  214. } else {
  215. // Append to the existing entry.
  216. Entries[IndexIt->second].PointIds.push_back(PointId);
  217. }
  218. }
  219. return Entries;
  220. }
  221. // Parallel benchmarks repeat the same opcode multiple times. Just show this
  222. // opcode and show the whole snippet only on hover.
  223. static void writeParallelSnippetHtml(raw_ostream &OS,
  224. const std::vector<MCInst> &Instructions,
  225. const MCInstrInfo &InstrInfo) {
  226. if (Instructions.empty())
  227. return;
  228. writeEscaped<kEscapeHtml>(OS, InstrInfo.getName(Instructions[0].getOpcode()));
  229. if (Instructions.size() > 1)
  230. OS << " (x" << Instructions.size() << ")";
  231. }
  232. // Latency tries to find a serial path. Just show the opcode path and show the
  233. // whole snippet only on hover.
  234. static void writeLatencySnippetHtml(raw_ostream &OS,
  235. const std::vector<MCInst> &Instructions,
  236. const MCInstrInfo &InstrInfo) {
  237. bool First = true;
  238. for (const MCInst &Instr : Instructions) {
  239. if (First)
  240. First = false;
  241. else
  242. OS << " &rarr; ";
  243. writeEscaped<kEscapeHtml>(OS, InstrInfo.getName(Instr.getOpcode()));
  244. }
  245. }
  246. void Analysis::printPointHtml(const InstructionBenchmark &Point,
  247. llvm::raw_ostream &OS) const {
  248. OS << "<li><span class=\"mono\" title=\"";
  249. writeSnippet<EscapeTag, kEscapeHtmlString>(OS, Point.AssembledSnippet, "\n");
  250. OS << "\">";
  251. switch (Point.Mode) {
  252. case InstructionBenchmark::Latency:
  253. writeLatencySnippetHtml(OS, Point.Key.Instructions, State_.getInstrInfo());
  254. break;
  255. case InstructionBenchmark::Uops:
  256. case InstructionBenchmark::InverseThroughput:
  257. writeParallelSnippetHtml(OS, Point.Key.Instructions, State_.getInstrInfo());
  258. break;
  259. default:
  260. llvm_unreachable("invalid mode");
  261. }
  262. OS << "</span> <span class=\"mono\">";
  263. writeEscaped<kEscapeHtml>(OS, Point.Key.Config);
  264. OS << "</span></li>";
  265. }
  266. void Analysis::printSchedClassClustersHtml(
  267. const std::vector<SchedClassCluster> &Clusters,
  268. const ResolvedSchedClass &RSC, raw_ostream &OS) const {
  269. const auto &Points = Clustering_.getPoints();
  270. OS << "<table class=\"sched-class-clusters\">";
  271. OS << "<tr><th>ClusterId</th><th>Opcode/Config</th>";
  272. assert(!Clusters.empty());
  273. for (const auto &Measurement :
  274. Points[Clusters[0].getPointIds()[0]].Measurements) {
  275. OS << "<th>";
  276. writeEscaped<kEscapeHtml>(OS, Measurement.Key);
  277. OS << "</th>";
  278. }
  279. OS << "</tr>";
  280. for (const SchedClassCluster &Cluster : Clusters) {
  281. OS << "<tr class=\""
  282. << (Cluster.measurementsMatch(State_.getSubtargetInfo(), RSC,
  283. Clustering_,
  284. AnalysisInconsistencyEpsilonSquared_)
  285. ? "good-cluster"
  286. : "bad-cluster")
  287. << "\"><td>";
  288. writeClusterId<kEscapeHtml>(OS, Cluster.id());
  289. OS << "</td><td><ul>";
  290. for (const size_t PointId : Cluster.getPointIds()) {
  291. printPointHtml(Points[PointId], OS);
  292. }
  293. OS << "</ul></td>";
  294. for (const auto &Stats : Cluster.getCentroid().getStats()) {
  295. OS << "<td class=\"measurement\">";
  296. writeMeasurementValue<kEscapeHtml>(OS, Stats.avg());
  297. OS << "<br><span class=\"minmax\">[";
  298. writeMeasurementValue<kEscapeHtml>(OS, Stats.min());
  299. OS << ";";
  300. writeMeasurementValue<kEscapeHtml>(OS, Stats.max());
  301. OS << "]</span></td>";
  302. }
  303. OS << "</tr>";
  304. }
  305. OS << "</table>";
  306. }
  307. void Analysis::SchedClassCluster::addPoint(
  308. size_t PointId, const InstructionBenchmarkClustering &Clustering) {
  309. PointIds.push_back(PointId);
  310. const auto &Point = Clustering.getPoints()[PointId];
  311. if (ClusterId.isUndef())
  312. ClusterId = Clustering.getClusterIdForPoint(PointId);
  313. assert(ClusterId == Clustering.getClusterIdForPoint(PointId));
  314. Centroid.addPoint(Point.Measurements);
  315. }
  316. bool Analysis::SchedClassCluster::measurementsMatch(
  317. const MCSubtargetInfo &STI, const ResolvedSchedClass &RSC,
  318. const InstructionBenchmarkClustering &Clustering,
  319. const double AnalysisInconsistencyEpsilonSquared_) const {
  320. assert(!Clustering.getPoints().empty());
  321. const InstructionBenchmark::ModeE Mode = Clustering.getPoints()[0].Mode;
  322. if (!Centroid.validate(Mode))
  323. return false;
  324. const std::vector<BenchmarkMeasure> ClusterCenterPoint =
  325. Centroid.getAsPoint();
  326. const std::vector<BenchmarkMeasure> SchedClassPoint =
  327. RSC.getAsPoint(Mode, STI, Centroid.getStats());
  328. if (SchedClassPoint.empty())
  329. return false; // In Uops mode validate() may not be enough.
  330. assert(ClusterCenterPoint.size() == SchedClassPoint.size() &&
  331. "Expected measured/sched data dimensions to match.");
  332. return Clustering.isNeighbour(ClusterCenterPoint, SchedClassPoint,
  333. AnalysisInconsistencyEpsilonSquared_);
  334. }
  335. void Analysis::printSchedClassDescHtml(const ResolvedSchedClass &RSC,
  336. raw_ostream &OS) const {
  337. OS << "<table class=\"sched-class-desc\">";
  338. OS << "<tr><th>Valid</th><th>Variant</th><th>NumMicroOps</th><th>Latency</"
  339. "th><th>RThroughput</th><th>WriteProcRes</th><th title=\"This is the "
  340. "idealized unit resource (port) pressure assuming ideal "
  341. "distribution\">Idealized Resource Pressure</th></tr>";
  342. if (RSC.SCDesc->isValid()) {
  343. const auto &SI = State_.getSubtargetInfo();
  344. const auto &SM = SI.getSchedModel();
  345. OS << "<tr><td>&#10004;</td>";
  346. OS << "<td>" << (RSC.WasVariant ? "&#10004;" : "&#10005;") << "</td>";
  347. OS << "<td>" << RSC.SCDesc->NumMicroOps << "</td>";
  348. // Latencies.
  349. OS << "<td><ul>";
  350. for (int I = 0, E = RSC.SCDesc->NumWriteLatencyEntries; I < E; ++I) {
  351. const auto *const Entry = SI.getWriteLatencyEntry(RSC.SCDesc, I);
  352. OS << "<li>" << Entry->Cycles;
  353. if (RSC.SCDesc->NumWriteLatencyEntries > 1) {
  354. // Dismabiguate if more than 1 latency.
  355. OS << " (WriteResourceID " << Entry->WriteResourceID << ")";
  356. }
  357. OS << "</li>";
  358. }
  359. OS << "</ul></td>";
  360. // inverse throughput.
  361. OS << "<td>";
  362. writeMeasurementValue<kEscapeHtml>(
  363. OS, MCSchedModel::getReciprocalThroughput(SI, *RSC.SCDesc));
  364. OS << "</td>";
  365. // WriteProcRes.
  366. OS << "<td><ul>";
  367. for (const auto &WPR : RSC.NonRedundantWriteProcRes) {
  368. OS << "<li><span class=\"mono\">";
  369. writeEscaped<kEscapeHtml>(OS,
  370. SM.getProcResource(WPR.ProcResourceIdx)->Name);
  371. OS << "</span>: " << WPR.Cycles << "</li>";
  372. }
  373. OS << "</ul></td>";
  374. // Idealized port pressure.
  375. OS << "<td><ul>";
  376. for (const auto &Pressure : RSC.IdealizedProcResPressure) {
  377. OS << "<li><span class=\"mono\">";
  378. writeEscaped<kEscapeHtml>(
  379. OS, SI.getSchedModel().getProcResource(Pressure.first)->Name);
  380. OS << "</span>: ";
  381. writeMeasurementValue<kEscapeHtml>(OS, Pressure.second);
  382. OS << "</li>";
  383. }
  384. OS << "</ul></td>";
  385. OS << "</tr>";
  386. } else {
  387. OS << "<tr><td>&#10005;</td><td></td><td></td></tr>";
  388. }
  389. OS << "</table>";
  390. }
  391. void Analysis::printClusterRawHtml(
  392. const InstructionBenchmarkClustering::ClusterId &Id, StringRef display_name,
  393. llvm::raw_ostream &OS) const {
  394. const auto &Points = Clustering_.getPoints();
  395. const auto &Cluster = Clustering_.getCluster(Id);
  396. if (Cluster.PointIndices.empty())
  397. return;
  398. OS << "<div class=\"inconsistency\"><p>" << display_name << " Cluster ("
  399. << Cluster.PointIndices.size() << " points)</p>";
  400. OS << "<table class=\"sched-class-clusters\">";
  401. // Table Header.
  402. OS << "<tr><th>ClusterId</th><th>Opcode/Config</th>";
  403. for (const auto &Measurement : Points[Cluster.PointIndices[0]].Measurements) {
  404. OS << "<th>";
  405. writeEscaped<kEscapeHtml>(OS, Measurement.Key);
  406. OS << "</th>";
  407. }
  408. OS << "</tr>";
  409. // Point data.
  410. for (const auto &PointId : Cluster.PointIndices) {
  411. OS << "<tr class=\"bad-cluster\"><td>" << display_name << "</td><td><ul>";
  412. printPointHtml(Points[PointId], OS);
  413. OS << "</ul></td>";
  414. for (const auto &Measurement : Points[PointId].Measurements) {
  415. OS << "<td class=\"measurement\">";
  416. writeMeasurementValue<kEscapeHtml>(OS, Measurement.PerInstructionValue);
  417. }
  418. OS << "</tr>";
  419. }
  420. OS << "</table>";
  421. OS << "</div>";
  422. } // namespace exegesis
  423. static constexpr const char kHtmlHead[] = R"(
  424. <head>
  425. <title>llvm-exegesis Analysis Results</title>
  426. <style>
  427. body {
  428. font-family: sans-serif
  429. }
  430. span.sched-class-name {
  431. font-weight: bold;
  432. font-family: monospace;
  433. }
  434. span.opcode {
  435. font-family: monospace;
  436. }
  437. span.config {
  438. font-family: monospace;
  439. }
  440. div.inconsistency {
  441. margin-top: 50px;
  442. }
  443. table {
  444. margin-left: 50px;
  445. border-collapse: collapse;
  446. }
  447. table, table tr,td,th {
  448. border: 1px solid #444;
  449. }
  450. table ul {
  451. padding-left: 0px;
  452. margin: 0px;
  453. list-style-type: none;
  454. }
  455. table.sched-class-clusters td {
  456. padding-left: 10px;
  457. padding-right: 10px;
  458. padding-top: 10px;
  459. padding-bottom: 10px;
  460. }
  461. table.sched-class-desc td {
  462. padding-left: 10px;
  463. padding-right: 10px;
  464. padding-top: 2px;
  465. padding-bottom: 2px;
  466. }
  467. span.mono {
  468. font-family: monospace;
  469. }
  470. td.measurement {
  471. text-align: center;
  472. }
  473. tr.good-cluster td.measurement {
  474. color: #292
  475. }
  476. tr.bad-cluster td.measurement {
  477. color: #922
  478. }
  479. tr.good-cluster td.measurement span.minmax {
  480. color: #888;
  481. }
  482. tr.bad-cluster td.measurement span.minmax {
  483. color: #888;
  484. }
  485. </style>
  486. </head>
  487. )";
  488. template <>
  489. Error Analysis::run<Analysis::PrintSchedClassInconsistencies>(
  490. raw_ostream &OS) const {
  491. const auto &FirstPoint = Clustering_.getPoints()[0];
  492. // Print the header.
  493. OS << "<!DOCTYPE html><html>" << kHtmlHead << "<body>";
  494. OS << "<h1><span class=\"mono\">llvm-exegesis</span> Analysis Results</h1>";
  495. OS << "<h3>Triple: <span class=\"mono\">";
  496. writeEscaped<kEscapeHtml>(OS, FirstPoint.LLVMTriple);
  497. OS << "</span></h3><h3>Cpu: <span class=\"mono\">";
  498. writeEscaped<kEscapeHtml>(OS, FirstPoint.CpuName);
  499. OS << "</span></h3>";
  500. const auto &SI = State_.getSubtargetInfo();
  501. for (const auto &RSCAndPoints : makePointsPerSchedClass()) {
  502. if (!RSCAndPoints.RSC.SCDesc)
  503. continue;
  504. // Bucket sched class points into sched class clusters.
  505. std::vector<SchedClassCluster> SchedClassClusters;
  506. for (const size_t PointId : RSCAndPoints.PointIds) {
  507. const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId);
  508. if (!ClusterId.isValid())
  509. continue; // Ignore noise and errors. FIXME: take noise into account ?
  510. if (ClusterId.isUnstable() ^ AnalysisDisplayUnstableOpcodes_)
  511. continue; // Either display stable or unstable clusters only.
  512. auto SchedClassClusterIt = llvm::find_if(
  513. SchedClassClusters, [ClusterId](const SchedClassCluster &C) {
  514. return C.id() == ClusterId;
  515. });
  516. if (SchedClassClusterIt == SchedClassClusters.end()) {
  517. SchedClassClusters.emplace_back();
  518. SchedClassClusterIt = std::prev(SchedClassClusters.end());
  519. }
  520. SchedClassClusterIt->addPoint(PointId, Clustering_);
  521. }
  522. // Print any scheduling class that has at least one cluster that does not
  523. // match the checked-in data.
  524. if (all_of(SchedClassClusters, [this, &RSCAndPoints,
  525. &SI](const SchedClassCluster &C) {
  526. return C.measurementsMatch(SI, RSCAndPoints.RSC, Clustering_,
  527. AnalysisInconsistencyEpsilonSquared_);
  528. }))
  529. continue; // Nothing weird.
  530. OS << "<div class=\"inconsistency\"><p>Sched Class <span "
  531. "class=\"sched-class-name\">";
  532. #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  533. writeEscaped<kEscapeHtml>(OS, RSCAndPoints.RSC.SCDesc->Name);
  534. #else
  535. OS << RSCAndPoints.RSC.SchedClassId;
  536. #endif
  537. OS << "</span> contains instructions whose performance characteristics do"
  538. " not match that of LLVM:</p>";
  539. printSchedClassClustersHtml(SchedClassClusters, RSCAndPoints.RSC, OS);
  540. OS << "<p>llvm SchedModel data:</p>";
  541. printSchedClassDescHtml(RSCAndPoints.RSC, OS);
  542. OS << "</div>";
  543. }
  544. printClusterRawHtml(InstructionBenchmarkClustering::ClusterId::noise(),
  545. "[noise]", OS);
  546. OS << "</body></html>";
  547. return Error::success();
  548. }
  549. } // namespace exegesis
  550. } // namespace llvm