xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-mca/Views/BottleneckAnalysis.cpp (revision 4b50c451720d8b427757a6da1dd2bb4c52cd9e35)
1 //===--------------------- BottleneckAnalysis.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 /// \file
9 ///
10 /// This file implements the functionalities used by the BottleneckAnalysis
11 /// to report bottleneck info.
12 ///
13 //===----------------------------------------------------------------------===//
14 
15 #include "Views/BottleneckAnalysis.h"
16 #include "llvm/MC/MCInst.h"
17 #include "llvm/MCA/Support.h"
18 #include "llvm/Support/Format.h"
19 #include "llvm/Support/FormattedStream.h"
20 
21 namespace llvm {
22 namespace mca {
23 
24 #define DEBUG_TYPE "llvm-mca"
25 
26 PressureTracker::PressureTracker(const MCSchedModel &Model)
27     : SM(Model),
28       ResourcePressureDistribution(Model.getNumProcResourceKinds(), 0),
29       ProcResID2Mask(Model.getNumProcResourceKinds(), 0),
30       ResIdx2ProcResID(Model.getNumProcResourceKinds(), 0),
31       ProcResID2ResourceUsersIndex(Model.getNumProcResourceKinds(), 0) {
32   computeProcResourceMasks(SM, ProcResID2Mask);
33 
34   // Ignore the invalid resource at index zero.
35   unsigned NextResourceUsersIdx = 0;
36   for (unsigned I = 1, E = Model.getNumProcResourceKinds(); I < E; ++I) {
37     const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
38     ProcResID2ResourceUsersIndex[I] = NextResourceUsersIdx;
39     NextResourceUsersIdx += ProcResource.NumUnits;
40     uint64_t ResourceMask = ProcResID2Mask[I];
41     ResIdx2ProcResID[getResourceStateIndex(ResourceMask)] = I;
42   }
43 
44   ResourceUsers.resize(NextResourceUsersIdx);
45   std::fill(ResourceUsers.begin(), ResourceUsers.end(),
46             std::make_pair<unsigned, unsigned>(~0U, 0U));
47 }
48 
49 void PressureTracker::getResourceUsers(uint64_t ResourceMask,
50                                        SmallVectorImpl<User> &Users) const {
51   unsigned Index = getResourceStateIndex(ResourceMask);
52   unsigned ProcResID = ResIdx2ProcResID[Index];
53   const MCProcResourceDesc &PRDesc = *SM.getProcResource(ProcResID);
54   for (unsigned I = 0, E = PRDesc.NumUnits; I < E; ++I) {
55     const User U = getResourceUser(ProcResID, I);
56     if (U.second && IPI.find(U.first) != IPI.end())
57       Users.emplace_back(U);
58   }
59 }
60 
61 void PressureTracker::onInstructionDispatched(unsigned IID) {
62   IPI.insert(std::make_pair(IID, InstructionPressureInfo()));
63 }
64 
65 void PressureTracker::onInstructionExecuted(unsigned IID) { IPI.erase(IID); }
66 
67 void PressureTracker::handleInstructionIssuedEvent(
68     const HWInstructionIssuedEvent &Event) {
69   unsigned IID = Event.IR.getSourceIndex();
70   using ResourceRef = HWInstructionIssuedEvent::ResourceRef;
71   using ResourceUse = std::pair<ResourceRef, ResourceCycles>;
72   for (const ResourceUse &Use : Event.UsedResources) {
73     const ResourceRef &RR = Use.first;
74     unsigned Index = ProcResID2ResourceUsersIndex[RR.first];
75     Index += countTrailingZeros(RR.second);
76     ResourceUsers[Index] = std::make_pair(IID, Use.second.getNumerator());
77   }
78 }
79 
80 void PressureTracker::updateResourcePressureDistribution(
81     uint64_t CumulativeMask) {
82   while (CumulativeMask) {
83     uint64_t Current = CumulativeMask & (-CumulativeMask);
84     unsigned ResIdx = getResourceStateIndex(Current);
85     unsigned ProcResID = ResIdx2ProcResID[ResIdx];
86     uint64_t Mask = ProcResID2Mask[ProcResID];
87 
88     if (Mask == Current) {
89       ResourcePressureDistribution[ProcResID]++;
90       CumulativeMask ^= Current;
91       continue;
92     }
93 
94     Mask ^= Current;
95     while (Mask) {
96       uint64_t SubUnit = Mask & (-Mask);
97       ResIdx = getResourceStateIndex(SubUnit);
98       ProcResID = ResIdx2ProcResID[ResIdx];
99       ResourcePressureDistribution[ProcResID]++;
100       Mask ^= SubUnit;
101     }
102 
103     CumulativeMask ^= Current;
104   }
105 }
106 
107 void PressureTracker::handlePressureEvent(const HWPressureEvent &Event) {
108   assert(Event.Reason != HWPressureEvent::INVALID &&
109          "Unexpected invalid event!");
110 
111   switch (Event.Reason) {
112   default:
113     break;
114 
115   case HWPressureEvent::RESOURCES: {
116     const uint64_t ResourceMask = Event.ResourceMask;
117     updateResourcePressureDistribution(Event.ResourceMask);
118 
119     for (const InstRef &IR : Event.AffectedInstructions) {
120       const Instruction &IS = *IR.getInstruction();
121       unsigned BusyResources = IS.getCriticalResourceMask() & ResourceMask;
122       if (!BusyResources)
123         continue;
124 
125       unsigned IID = IR.getSourceIndex();
126       IPI[IID].ResourcePressureCycles++;
127     }
128     break;
129   }
130 
131   case HWPressureEvent::REGISTER_DEPS:
132     for (const InstRef &IR : Event.AffectedInstructions) {
133       unsigned IID = IR.getSourceIndex();
134       IPI[IID].RegisterPressureCycles++;
135     }
136     break;
137 
138   case HWPressureEvent::MEMORY_DEPS:
139     for (const InstRef &IR : Event.AffectedInstructions) {
140       unsigned IID = IR.getSourceIndex();
141       IPI[IID].MemoryPressureCycles++;
142     }
143   }
144 }
145 
146 #ifndef NDEBUG
147 void DependencyGraph::dumpDependencyEdge(raw_ostream &OS,
148                                          const DependencyEdge &DepEdge,
149                                          MCInstPrinter &MCIP) const {
150   unsigned FromIID = DepEdge.FromIID;
151   unsigned ToIID = DepEdge.ToIID;
152   assert(FromIID < ToIID && "Graph should be acyclic!");
153 
154   const DependencyEdge::Dependency &DE = DepEdge.Dep;
155   assert(DE.Type != DependencyEdge::DT_INVALID && "Unexpected invalid edge!");
156 
157   OS << " FROM: " << FromIID << " TO: " << ToIID << "             ";
158   if (DE.Type == DependencyEdge::DT_REGISTER) {
159     OS << " - REGISTER: ";
160     MCIP.printRegName(OS, DE.ResourceOrRegID);
161   } else if (DE.Type == DependencyEdge::DT_MEMORY) {
162     OS << " - MEMORY";
163   } else {
164     assert(DE.Type == DependencyEdge::DT_RESOURCE &&
165            "Unsupported dependency type!");
166     OS << " - RESOURCE MASK: " << DE.ResourceOrRegID;
167   }
168   OS << " - CYCLES: " << DE.Cost << '\n';
169 }
170 #endif // NDEBUG
171 
172 void DependencyGraph::initializeRootSet(
173     SmallVectorImpl<unsigned> &RootSet) const {
174   for (unsigned I = 0, E = Nodes.size(); I < E; ++I) {
175     const DGNode &N = Nodes[I];
176     if (N.NumPredecessors == 0 && !N.OutgoingEdges.empty())
177       RootSet.emplace_back(I);
178   }
179 }
180 
181 void DependencyGraph::propagateThroughEdges(
182     SmallVectorImpl<unsigned> &RootSet) {
183   SmallVector<unsigned, 8> ToVisit;
184 
185   // A critical sequence is computed as the longest path from a node of the
186   // RootSet to a leaf node (i.e. a node with no successors).  The RootSet is
187   // composed of nodes with at least one successor, and no predecessors.
188   //
189   // Each node of the graph starts with an initial default cost of zero.  The
190   // cost of a node is a measure of criticality: the higher the cost, the bigger
191   // is the performance impact.
192   //
193   // This algorithm is very similar to a (reverse) Dijkstra.  Every iteration of
194   // the inner loop selects (i.e. visits) a node N from a set of `unvisited
195   // nodes`, and then propagates the cost of N to all its neighbors.
196   //
197   // The `unvisited nodes` set initially contains all the nodes from the
198   // RootSet.  A node N is added to the `unvisited nodes` if all its
199   // predecessors have been visited already.
200   //
201   // For simplicity, every node tracks the number of unvisited incoming edges in
202   // field `NumVisitedPredecessors`.  When the value of that field drops to
203   // zero, then the corresponding node is added to a `ToVisit` set.
204   //
205   // At the end of every iteration of the outer loop, set `ToVisit` becomes our
206   // new `unvisited nodes` set.
207   //
208   // The algorithm terminates when the set of unvisited nodes (i.e. our RootSet)
209   // is empty. This algorithm works under the assumption that the graph is
210   // acyclic.
211   do {
212     for (unsigned IID : RootSet) {
213       const DGNode &N = Nodes[IID];
214       for (const DependencyEdge &DepEdge : N.OutgoingEdges) {
215         unsigned ToIID = DepEdge.ToIID;
216         DGNode &To = Nodes[ToIID];
217         uint64_t Cost = N.Cost + DepEdge.Dep.Cost;
218         // Check if this is the most expensive incoming edge seen so far.  In
219         // case, update the total cost of the destination node (ToIID), as well
220         // its field `CriticalPredecessor`.
221         if (Cost > To.Cost) {
222           To.CriticalPredecessor = DepEdge;
223           To.Cost = Cost;
224           To.Depth = N.Depth + 1;
225         }
226         To.NumVisitedPredecessors++;
227         if (To.NumVisitedPredecessors == To.NumPredecessors)
228           ToVisit.emplace_back(ToIID);
229       }
230     }
231 
232     std::swap(RootSet, ToVisit);
233     ToVisit.clear();
234   } while (!RootSet.empty());
235 }
236 
237 void DependencyGraph::getCriticalSequence(
238     SmallVectorImpl<const DependencyEdge *> &Seq) const {
239   // At this stage, nodes of the graph have been already visited, and costs have
240   // been propagated through the edges (see method `propagateThroughEdges()`).
241 
242   // Identify the node N with the highest cost in the graph. By construction,
243   // that node is the last instruction of our critical sequence.
244   // Field N.Depth would tell us the total length of the sequence.
245   //
246   // To obtain the sequence of critical edges, we simply follow the chain of critical
247   // predecessors starting from node N (field DGNode::CriticalPredecessor).
248   const auto It = std::max_element(
249       Nodes.begin(), Nodes.end(),
250       [](const DGNode &Lhs, const DGNode &Rhs) { return Lhs.Cost < Rhs.Cost; });
251   unsigned IID = std::distance(Nodes.begin(), It);
252   Seq.resize(Nodes[IID].Depth);
253   for (unsigned I = Seq.size(), E = 0; I > E; --I) {
254     const DGNode &N = Nodes[IID];
255     Seq[I - 1] = &N.CriticalPredecessor;
256     IID = N.CriticalPredecessor.FromIID;
257   }
258 }
259 
260 static void printInstruction(formatted_raw_ostream &FOS,
261                              const MCSubtargetInfo &STI, MCInstPrinter &MCIP,
262                              const MCInst &MCI,
263                              bool UseDifferentColor = false) {
264   std::string Instruction;
265   raw_string_ostream InstrStream(Instruction);
266 
267   FOS.PadToColumn(14);
268 
269   MCIP.printInst(&MCI, InstrStream, "", STI);
270   InstrStream.flush();
271 
272   if (UseDifferentColor)
273     FOS.changeColor(raw_ostream::CYAN, true, false);
274   FOS << StringRef(Instruction).ltrim();
275   if (UseDifferentColor)
276     FOS.resetColor();
277 }
278 
279 void BottleneckAnalysis::printCriticalSequence(raw_ostream &OS) const {
280   SmallVector<const DependencyEdge *, 16> Seq;
281   DG.getCriticalSequence(Seq);
282   if (Seq.empty())
283     return;
284 
285   OS << "\nCritical sequence based on the simulation:\n\n";
286 
287   const DependencyEdge &FirstEdge = *Seq[0];
288   unsigned FromIID = FirstEdge.FromIID % Source.size();
289   unsigned ToIID = FirstEdge.ToIID % Source.size();
290   bool IsLoopCarried = FromIID >= ToIID;
291 
292   formatted_raw_ostream FOS(OS);
293   FOS.PadToColumn(14);
294   FOS << "Instruction";
295   FOS.PadToColumn(58);
296   FOS << "Dependency Information";
297 
298   bool HasColors = FOS.has_colors();
299 
300   unsigned CurrentIID = 0;
301   if (IsLoopCarried) {
302     FOS << "\n +----< " << FromIID << ".";
303     printInstruction(FOS, STI, MCIP, Source[FromIID], HasColors);
304     FOS << "\n |\n |    < loop carried > \n |";
305   } else {
306     while (CurrentIID < FromIID) {
307       FOS << "\n        " << CurrentIID << ".";
308       printInstruction(FOS, STI, MCIP, Source[CurrentIID]);
309       CurrentIID++;
310     }
311 
312     FOS << "\n +----< " << CurrentIID << ".";
313     printInstruction(FOS, STI, MCIP, Source[CurrentIID], HasColors);
314     CurrentIID++;
315   }
316 
317   for (const DependencyEdge *&DE : Seq) {
318     ToIID = DE->ToIID % Source.size();
319     unsigned LastIID = CurrentIID > ToIID ? Source.size() : ToIID;
320 
321     while (CurrentIID < LastIID) {
322       FOS << "\n |      " << CurrentIID << ".";
323       printInstruction(FOS, STI, MCIP, Source[CurrentIID]);
324       CurrentIID++;
325     }
326 
327     if (CurrentIID == ToIID) {
328       FOS << "\n +----> " << ToIID << ".";
329       printInstruction(FOS, STI, MCIP, Source[CurrentIID], HasColors);
330     } else {
331       FOS << "\n |\n |    < loop carried > \n |"
332           << "\n +----> " << ToIID << ".";
333       printInstruction(FOS, STI, MCIP, Source[ToIID], HasColors);
334     }
335     FOS.PadToColumn(58);
336 
337     const DependencyEdge::Dependency &Dep = DE->Dep;
338     if (HasColors)
339       FOS.changeColor(raw_ostream::SAVEDCOLOR, true, false);
340 
341     if (Dep.Type == DependencyEdge::DT_REGISTER) {
342       FOS << "## REGISTER dependency:  ";
343       if (HasColors)
344         FOS.changeColor(raw_ostream::MAGENTA, true, false);
345       MCIP.printRegName(FOS, Dep.ResourceOrRegID);
346     } else if (Dep.Type == DependencyEdge::DT_MEMORY) {
347       FOS << "## MEMORY dependency.";
348     } else {
349       assert(Dep.Type == DependencyEdge::DT_RESOURCE &&
350              "Unsupported dependency type!");
351       FOS << "## RESOURCE interference:  ";
352       if (HasColors)
353         FOS.changeColor(raw_ostream::MAGENTA, true, false);
354       FOS << Tracker.resolveResourceName(Dep.ResourceOrRegID);
355       if (HasColors) {
356         FOS.resetColor();
357         FOS.changeColor(raw_ostream::SAVEDCOLOR, true, false);
358       }
359       FOS << " [ probability: " << ((DE->Frequency * 100) / Iterations)
360           << "% ]";
361     }
362     if (HasColors)
363       FOS.resetColor();
364     ++CurrentIID;
365   }
366 
367   while (CurrentIID < Source.size()) {
368     FOS << "\n        " << CurrentIID << ".";
369     printInstruction(FOS, STI, MCIP, Source[CurrentIID]);
370     CurrentIID++;
371   }
372 
373   FOS << '\n';
374   FOS.flush();
375 }
376 
377 #ifndef NDEBUG
378 void DependencyGraph::dump(raw_ostream &OS, MCInstPrinter &MCIP) const {
379   OS << "\nREG DEPS\n";
380   for (const DGNode &Node : Nodes)
381     for (const DependencyEdge &DE : Node.OutgoingEdges)
382       if (DE.Dep.Type == DependencyEdge::DT_REGISTER)
383         dumpDependencyEdge(OS, DE, MCIP);
384 
385   OS << "\nMEM DEPS\n";
386   for (const DGNode &Node : Nodes)
387     for (const DependencyEdge &DE : Node.OutgoingEdges)
388       if (DE.Dep.Type == DependencyEdge::DT_MEMORY)
389         dumpDependencyEdge(OS, DE, MCIP);
390 
391   OS << "\nRESOURCE DEPS\n";
392   for (const DGNode &Node : Nodes)
393     for (const DependencyEdge &DE : Node.OutgoingEdges)
394       if (DE.Dep.Type == DependencyEdge::DT_RESOURCE)
395         dumpDependencyEdge(OS, DE, MCIP);
396 }
397 #endif // NDEBUG
398 
399 void DependencyGraph::addDependency(unsigned From, unsigned To,
400                                     DependencyEdge::Dependency &&Dep) {
401   DGNode &NodeFrom = Nodes[From];
402   DGNode &NodeTo = Nodes[To];
403   SmallVectorImpl<DependencyEdge> &Vec = NodeFrom.OutgoingEdges;
404 
405   auto It = find_if(Vec, [To, Dep](DependencyEdge &DE) {
406     return DE.ToIID == To && DE.Dep.ResourceOrRegID == Dep.ResourceOrRegID;
407   });
408 
409   if (It != Vec.end()) {
410     It->Dep.Cost += Dep.Cost;
411     It->Frequency++;
412     return;
413   }
414 
415   DependencyEdge DE = {Dep, From, To, 1};
416   Vec.emplace_back(DE);
417   NodeTo.NumPredecessors++;
418 }
419 
420 BottleneckAnalysis::BottleneckAnalysis(const MCSubtargetInfo &sti,
421                                        MCInstPrinter &Printer,
422                                        ArrayRef<MCInst> S, unsigned NumIter)
423     : STI(sti), MCIP(Printer), Tracker(STI.getSchedModel()), DG(S.size() * 3),
424       Source(S), Iterations(NumIter), TotalCycles(0),
425       PressureIncreasedBecauseOfResources(false),
426       PressureIncreasedBecauseOfRegisterDependencies(false),
427       PressureIncreasedBecauseOfMemoryDependencies(false),
428       SeenStallCycles(false), BPI() {}
429 
430 void BottleneckAnalysis::addRegisterDep(unsigned From, unsigned To,
431                                         unsigned RegID, unsigned Cost) {
432   bool IsLoopCarried = From >= To;
433   unsigned SourceSize = Source.size();
434   if (IsLoopCarried) {
435     Cost *= Iterations / 2;
436     DG.addRegisterDep(From, To + SourceSize, RegID, Cost);
437     DG.addRegisterDep(From + SourceSize, To + (SourceSize * 2), RegID, Cost);
438     return;
439   }
440   DG.addRegisterDep(From + SourceSize, To + SourceSize, RegID, Cost);
441 }
442 
443 void BottleneckAnalysis::addMemoryDep(unsigned From, unsigned To,
444                                       unsigned Cost) {
445   bool IsLoopCarried = From >= To;
446   unsigned SourceSize = Source.size();
447   if (IsLoopCarried) {
448     Cost *= Iterations / 2;
449     DG.addMemoryDep(From, To + SourceSize, Cost);
450     DG.addMemoryDep(From + SourceSize, To + (SourceSize * 2), Cost);
451     return;
452   }
453   DG.addMemoryDep(From + SourceSize, To + SourceSize, Cost);
454 }
455 
456 void BottleneckAnalysis::addResourceDep(unsigned From, unsigned To,
457                                         uint64_t Mask, unsigned Cost) {
458   bool IsLoopCarried = From >= To;
459   unsigned SourceSize = Source.size();
460   if (IsLoopCarried) {
461     Cost *= Iterations / 2;
462     DG.addResourceDep(From, To + SourceSize, Mask, Cost);
463     DG.addResourceDep(From + SourceSize, To + (SourceSize * 2), Mask, Cost);
464     return;
465   }
466   DG.addResourceDep(From + SourceSize, To + SourceSize, Mask, Cost);
467 }
468 
469 void BottleneckAnalysis::onEvent(const HWInstructionEvent &Event) {
470   const unsigned IID = Event.IR.getSourceIndex();
471   if (Event.Type == HWInstructionEvent::Dispatched) {
472     Tracker.onInstructionDispatched(IID);
473     return;
474   }
475   if (Event.Type == HWInstructionEvent::Executed) {
476     Tracker.onInstructionExecuted(IID);
477     return;
478   }
479 
480   if (Event.Type != HWInstructionEvent::Issued)
481     return;
482 
483   const Instruction &IS = *Event.IR.getInstruction();
484   unsigned To = IID % Source.size();
485 
486   unsigned Cycles = 2 * Tracker.getResourcePressureCycles(IID);
487   uint64_t ResourceMask = IS.getCriticalResourceMask();
488   SmallVector<std::pair<unsigned, unsigned>, 4> Users;
489   while (ResourceMask) {
490     uint64_t Current = ResourceMask & (-ResourceMask);
491     Tracker.getResourceUsers(Current, Users);
492     for (const std::pair<unsigned, unsigned> &U : Users)
493       addResourceDep(U.first % Source.size(), To, Current, U.second + Cycles);
494     Users.clear();
495     ResourceMask ^= Current;
496   }
497 
498   const CriticalDependency &RegDep = IS.getCriticalRegDep();
499   if (RegDep.Cycles) {
500     Cycles = RegDep.Cycles + 2 * Tracker.getRegisterPressureCycles(IID);
501     unsigned From = RegDep.IID % Source.size();
502     addRegisterDep(From, To, RegDep.RegID, Cycles);
503   }
504 
505   const CriticalDependency &MemDep = IS.getCriticalMemDep();
506   if (MemDep.Cycles) {
507     Cycles = MemDep.Cycles + 2 * Tracker.getMemoryPressureCycles(IID);
508     unsigned From = MemDep.IID % Source.size();
509     addMemoryDep(From, To, Cycles);
510   }
511 
512   Tracker.handleInstructionIssuedEvent(
513       static_cast<const HWInstructionIssuedEvent &>(Event));
514 
515   // Check if this is the last simulated instruction.
516   if (IID == ((Iterations * Source.size()) - 1))
517     DG.finalizeGraph();
518 }
519 
520 void BottleneckAnalysis::onEvent(const HWPressureEvent &Event) {
521   assert(Event.Reason != HWPressureEvent::INVALID &&
522          "Unexpected invalid event!");
523 
524   Tracker.handlePressureEvent(Event);
525 
526   switch (Event.Reason) {
527   default:
528     break;
529 
530   case HWPressureEvent::RESOURCES:
531     PressureIncreasedBecauseOfResources = true;
532     break;
533   case HWPressureEvent::REGISTER_DEPS:
534     PressureIncreasedBecauseOfRegisterDependencies = true;
535     break;
536   case HWPressureEvent::MEMORY_DEPS:
537     PressureIncreasedBecauseOfMemoryDependencies = true;
538     break;
539   }
540 }
541 
542 void BottleneckAnalysis::onCycleEnd() {
543   ++TotalCycles;
544 
545   bool PressureIncreasedBecauseOfDataDependencies =
546       PressureIncreasedBecauseOfRegisterDependencies ||
547       PressureIncreasedBecauseOfMemoryDependencies;
548   if (!PressureIncreasedBecauseOfResources &&
549       !PressureIncreasedBecauseOfDataDependencies)
550     return;
551 
552   ++BPI.PressureIncreaseCycles;
553   if (PressureIncreasedBecauseOfRegisterDependencies)
554     ++BPI.RegisterDependencyCycles;
555   if (PressureIncreasedBecauseOfMemoryDependencies)
556     ++BPI.MemoryDependencyCycles;
557   if (PressureIncreasedBecauseOfDataDependencies)
558     ++BPI.DataDependencyCycles;
559   if (PressureIncreasedBecauseOfResources)
560     ++BPI.ResourcePressureCycles;
561   PressureIncreasedBecauseOfResources = false;
562   PressureIncreasedBecauseOfRegisterDependencies = false;
563   PressureIncreasedBecauseOfMemoryDependencies = false;
564 }
565 
566 void BottleneckAnalysis::printBottleneckHints(raw_ostream &OS) const {
567   if (!SeenStallCycles || !BPI.PressureIncreaseCycles) {
568     OS << "\n\nNo resource or data dependency bottlenecks discovered.\n";
569     return;
570   }
571 
572   double PressurePerCycle =
573       (double)BPI.PressureIncreaseCycles * 100 / TotalCycles;
574   double ResourcePressurePerCycle =
575       (double)BPI.ResourcePressureCycles * 100 / TotalCycles;
576   double DDPerCycle = (double)BPI.DataDependencyCycles * 100 / TotalCycles;
577   double RegDepPressurePerCycle =
578       (double)BPI.RegisterDependencyCycles * 100 / TotalCycles;
579   double MemDepPressurePerCycle =
580       (double)BPI.MemoryDependencyCycles * 100 / TotalCycles;
581 
582   OS << "\n\nCycles with backend pressure increase [ "
583      << format("%.2f", floor((PressurePerCycle * 100) + 0.5) / 100) << "% ]";
584 
585   OS << "\nThroughput Bottlenecks: "
586      << "\n  Resource Pressure       [ "
587      << format("%.2f", floor((ResourcePressurePerCycle * 100) + 0.5) / 100)
588      << "% ]";
589 
590   if (BPI.PressureIncreaseCycles) {
591     ArrayRef<unsigned> Distribution = Tracker.getResourcePressureDistribution();
592     const MCSchedModel &SM = STI.getSchedModel();
593     for (unsigned I = 0, E = Distribution.size(); I < E; ++I) {
594       unsigned ResourceCycles = Distribution[I];
595       if (ResourceCycles) {
596         double Frequency = (double)ResourceCycles * 100 / TotalCycles;
597         const MCProcResourceDesc &PRDesc = *SM.getProcResource(I);
598         OS << "\n  - " << PRDesc.Name << "  [ "
599            << format("%.2f", floor((Frequency * 100) + 0.5) / 100) << "% ]";
600       }
601     }
602   }
603 
604   OS << "\n  Data Dependencies:      [ "
605      << format("%.2f", floor((DDPerCycle * 100) + 0.5) / 100) << "% ]";
606   OS << "\n  - Register Dependencies [ "
607      << format("%.2f", floor((RegDepPressurePerCycle * 100) + 0.5) / 100)
608      << "% ]";
609   OS << "\n  - Memory Dependencies   [ "
610      << format("%.2f", floor((MemDepPressurePerCycle * 100) + 0.5) / 100)
611      << "% ]\n";
612 }
613 
614 void BottleneckAnalysis::printView(raw_ostream &OS) const {
615   std::string Buffer;
616   raw_string_ostream TempStream(Buffer);
617   printBottleneckHints(TempStream);
618   TempStream.flush();
619   OS << Buffer;
620   printCriticalSequence(OS);
621 }
622 
623 } // namespace mca.
624 } // namespace llvm
625