1 //===--------------------- TimelineView.cpp ---------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
10 /// This file implements the TimelineView interface.
12 //===----------------------------------------------------------------------===//
14 #include "Views/TimelineView.h"
19 TimelineView::TimelineView(const MCSubtargetInfo
&sti
, MCInstPrinter
&Printer
,
20 llvm::ArrayRef
<llvm::MCInst
> S
, unsigned Iterations
,
22 : STI(sti
), MCIP(Printer
), Source(S
), CurrentCycle(0),
23 MaxCycle(Cycles
== 0 ? 80 : Cycles
), LastCycle(0), WaitTime(S
.size()),
24 UsedBuffer(S
.size()) {
25 unsigned NumInstructions
= Source
.size();
26 assert(Iterations
&& "Invalid number of iterations specified!");
27 NumInstructions
*= Iterations
;
28 Timeline
.resize(NumInstructions
);
29 TimelineViewEntry InvalidTVEntry
= {-1, 0, 0, 0, 0};
30 std::fill(Timeline
.begin(), Timeline
.end(), InvalidTVEntry
);
32 WaitTimeEntry NullWTEntry
= {0, 0, 0};
33 std::fill(WaitTime
.begin(), WaitTime
.end(), NullWTEntry
);
35 std::pair
<unsigned, int> NullUsedBufferEntry
= {/* Invalid resource ID*/ 0,
36 /* unknown buffer size */ -1};
37 std::fill(UsedBuffer
.begin(), UsedBuffer
.end(), NullUsedBufferEntry
);
40 void TimelineView::onReservedBuffers(const InstRef
&IR
,
41 ArrayRef
<unsigned> Buffers
) {
42 if (IR
.getSourceIndex() >= Source
.size())
45 const MCSchedModel
&SM
= STI
.getSchedModel();
46 std::pair
<unsigned, int> BufferInfo
= {0, -1};
47 for (const unsigned Buffer
: Buffers
) {
48 const MCProcResourceDesc
&MCDesc
= *SM
.getProcResource(Buffer
);
49 if (!BufferInfo
.first
|| BufferInfo
.second
> MCDesc
.BufferSize
) {
50 BufferInfo
.first
= Buffer
;
51 BufferInfo
.second
= MCDesc
.BufferSize
;
55 UsedBuffer
[IR
.getSourceIndex()] = BufferInfo
;
58 void TimelineView::onEvent(const HWInstructionEvent
&Event
) {
59 const unsigned Index
= Event
.IR
.getSourceIndex();
60 if (Index
>= Timeline
.size())
64 case HWInstructionEvent::Retired
: {
65 TimelineViewEntry
&TVEntry
= Timeline
[Index
];
66 if (CurrentCycle
< MaxCycle
)
67 TVEntry
.CycleRetired
= CurrentCycle
;
69 // Update the WaitTime entry which corresponds to this Index.
70 assert(TVEntry
.CycleDispatched
>= 0 && "Invalid TVEntry found!");
71 unsigned CycleDispatched
= static_cast<unsigned>(TVEntry
.CycleDispatched
);
72 WaitTimeEntry
&WTEntry
= WaitTime
[Index
% Source
.size()];
73 WTEntry
.CyclesSpentInSchedulerQueue
+=
74 TVEntry
.CycleIssued
- CycleDispatched
;
75 assert(CycleDispatched
<= TVEntry
.CycleReady
&&
76 "Instruction cannot be ready if it hasn't been dispatched yet!");
77 WTEntry
.CyclesSpentInSQWhileReady
+=
78 TVEntry
.CycleIssued
- TVEntry
.CycleReady
;
79 WTEntry
.CyclesSpentAfterWBAndBeforeRetire
+=
80 (CurrentCycle
- 1) - TVEntry
.CycleExecuted
;
83 case HWInstructionEvent::Ready
:
84 Timeline
[Index
].CycleReady
= CurrentCycle
;
86 case HWInstructionEvent::Issued
:
87 Timeline
[Index
].CycleIssued
= CurrentCycle
;
89 case HWInstructionEvent::Executed
:
90 Timeline
[Index
].CycleExecuted
= CurrentCycle
;
92 case HWInstructionEvent::Dispatched
:
93 // There may be multiple dispatch events. Microcoded instructions that are
94 // expanded into multiple uOps may require multiple dispatch cycles. Here,
95 // we want to capture the first dispatch cycle.
96 if (Timeline
[Index
].CycleDispatched
== -1)
97 Timeline
[Index
].CycleDispatched
= static_cast<int>(CurrentCycle
);
102 if (CurrentCycle
< MaxCycle
)
103 LastCycle
= std::max(LastCycle
, CurrentCycle
);
106 static raw_ostream::Colors
chooseColor(unsigned CumulativeCycles
,
107 unsigned Executions
, int BufferSize
) {
108 if (CumulativeCycles
&& BufferSize
< 0)
109 return raw_ostream::MAGENTA
;
110 unsigned Size
= static_cast<unsigned>(BufferSize
);
111 if (CumulativeCycles
>= Size
* Executions
)
112 return raw_ostream::RED
;
113 if ((CumulativeCycles
* 2) >= Size
* Executions
)
114 return raw_ostream::YELLOW
;
115 return raw_ostream::SAVEDCOLOR
;
118 static void tryChangeColor(raw_ostream
&OS
, unsigned Cycles
,
119 unsigned Executions
, int BufferSize
) {
120 if (!OS
.has_colors())
123 raw_ostream::Colors Color
= chooseColor(Cycles
, Executions
, BufferSize
);
124 if (Color
== raw_ostream::SAVEDCOLOR
) {
128 OS
.changeColor(Color
, /* bold */ true, /* BG */ false);
131 void TimelineView::printWaitTimeEntry(formatted_raw_ostream
&OS
,
132 const WaitTimeEntry
&Entry
,
133 unsigned SourceIndex
,
134 unsigned Executions
) const {
135 OS
<< SourceIndex
<< '.';
138 double AverageTime1
, AverageTime2
, AverageTime3
;
139 AverageTime1
= (double)Entry
.CyclesSpentInSchedulerQueue
/ Executions
;
140 AverageTime2
= (double)Entry
.CyclesSpentInSQWhileReady
/ Executions
;
141 AverageTime3
= (double)Entry
.CyclesSpentAfterWBAndBeforeRetire
/ Executions
;
145 int BufferSize
= UsedBuffer
[SourceIndex
].second
;
146 tryChangeColor(OS
, Entry
.CyclesSpentInSchedulerQueue
, Executions
, BufferSize
);
147 OS
<< format("%.1f", floor((AverageTime1
* 10) + 0.5) / 10);
149 tryChangeColor(OS
, Entry
.CyclesSpentInSQWhileReady
, Executions
, BufferSize
);
150 OS
<< format("%.1f", floor((AverageTime2
* 10) + 0.5) / 10);
152 tryChangeColor(OS
, Entry
.CyclesSpentAfterWBAndBeforeRetire
, Executions
,
153 STI
.getSchedModel().MicroOpBufferSize
);
154 OS
<< format("%.1f", floor((AverageTime3
* 10) + 0.5) / 10);
161 void TimelineView::printAverageWaitTimes(raw_ostream
&OS
) const {
163 "\n\nAverage Wait times (based on the timeline view):\n"
165 "[1]: Average time spent waiting in a scheduler's queue\n"
166 "[2]: Average time spent waiting in a scheduler's queue while ready\n"
167 "[3]: Average time elapsed from WB until retire stage\n\n"
168 " [0] [1] [2] [3]\n";
171 // Use a different string stream for printing instructions.
172 std::string Instruction
;
173 raw_string_ostream
InstrStream(Instruction
);
175 formatted_raw_ostream
FOS(OS
);
176 unsigned Executions
= Timeline
.size() / Source
.size();
178 for (const MCInst
&Inst
: Source
) {
179 printWaitTimeEntry(FOS
, WaitTime
[IID
], IID
, Executions
);
180 // Append the instruction info at the end of the line.
181 MCIP
.printInst(&Inst
, InstrStream
, "", STI
);
184 // Consume any tabs or spaces at the beginning of the string.
185 StringRef
Str(Instruction
);
187 FOS
<< " " << Str
<< '\n';
195 void TimelineView::printTimelineViewEntry(formatted_raw_ostream
&OS
,
196 const TimelineViewEntry
&Entry
,
198 unsigned SourceIndex
) const {
199 if (Iteration
== 0 && SourceIndex
== 0)
201 OS
<< '[' << Iteration
<< ',' << SourceIndex
<< ']';
203 assert(Entry
.CycleDispatched
>= 0 && "Invalid TimelineViewEntry!");
204 unsigned CycleDispatched
= static_cast<unsigned>(Entry
.CycleDispatched
);
205 for (unsigned I
= 0, E
= CycleDispatched
; I
< E
; ++I
)
206 OS
<< ((I
% 5 == 0) ? '.' : ' ');
207 OS
<< TimelineView::DisplayChar::Dispatched
;
208 if (CycleDispatched
!= Entry
.CycleExecuted
) {
209 // Zero latency instructions have the same value for CycleDispatched,
210 // CycleIssued and CycleExecuted.
211 for (unsigned I
= CycleDispatched
+ 1, E
= Entry
.CycleIssued
; I
< E
; ++I
)
212 OS
<< TimelineView::DisplayChar::Waiting
;
213 if (Entry
.CycleIssued
== Entry
.CycleExecuted
)
214 OS
<< TimelineView::DisplayChar::DisplayChar::Executed
;
216 if (CycleDispatched
!= Entry
.CycleIssued
)
217 OS
<< TimelineView::DisplayChar::Executing
;
218 for (unsigned I
= Entry
.CycleIssued
+ 1, E
= Entry
.CycleExecuted
; I
< E
;
220 OS
<< TimelineView::DisplayChar::Executing
;
221 OS
<< TimelineView::DisplayChar::Executed
;
225 for (unsigned I
= Entry
.CycleExecuted
+ 1, E
= Entry
.CycleRetired
; I
< E
; ++I
)
226 OS
<< TimelineView::DisplayChar::RetireLag
;
227 OS
<< TimelineView::DisplayChar::Retired
;
229 // Skip other columns.
230 for (unsigned I
= Entry
.CycleRetired
+ 1, E
= LastCycle
; I
<= E
; ++I
)
231 OS
<< ((I
% 5 == 0 || I
== LastCycle
) ? '.' : ' ');
234 static void printTimelineHeader(formatted_raw_ostream
&OS
, unsigned Cycles
) {
235 OS
<< "\n\nTimeline view:\n";
238 for (unsigned I
= 0; I
<= Cycles
; ++I
) {
239 if (((I
/ 10) & 1) == 0)
249 for (unsigned I
= 0; I
<= Cycles
; ++I
) {
250 if (((I
/ 10) & 1) == 0)
258 void TimelineView::printTimeline(raw_ostream
&OS
) const {
259 formatted_raw_ostream
FOS(OS
);
260 printTimelineHeader(FOS
, LastCycle
);
263 // Use a different string stream for the instruction.
264 std::string Instruction
;
265 raw_string_ostream
InstrStream(Instruction
);
268 const unsigned Iterations
= Timeline
.size() / Source
.size();
269 for (unsigned Iteration
= 0; Iteration
< Iterations
; ++Iteration
) {
270 for (const MCInst
&Inst
: Source
) {
271 const TimelineViewEntry
&Entry
= Timeline
[IID
];
272 if (Entry
.CycleRetired
== 0)
275 unsigned SourceIndex
= IID
% Source
.size();
276 printTimelineViewEntry(FOS
, Entry
, Iteration
, SourceIndex
);
277 // Append the instruction info at the end of the line.
278 MCIP
.printInst(&Inst
, InstrStream
, "", STI
);
281 // Consume any tabs or spaces at the beginning of the string.
282 StringRef
Str(Instruction
);
284 FOS
<< " " << Str
<< '\n';