|  | //===--------------------- TimelineView.cpp ---------------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | /// \brief | 
|  | /// | 
|  | /// This file implements the TimelineView interface. | 
|  | /// | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "TimelineView.h" | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace mca { | 
|  |  | 
|  | void TimelineView::initialize(unsigned MaxIterations) { | 
|  | unsigned NumInstructions = | 
|  | AsmSequence.getNumIterations() * AsmSequence.size(); | 
|  | if (!MaxIterations) | 
|  | MaxIterations = DEFAULT_ITERATIONS; | 
|  | unsigned NumEntries = | 
|  | std::min(NumInstructions, MaxIterations * AsmSequence.size()); | 
|  | Timeline.resize(NumEntries); | 
|  | TimelineViewEntry NullTVEntry = {0, 0, 0, 0, 0}; | 
|  | std::fill(Timeline.begin(), Timeline.end(), NullTVEntry); | 
|  |  | 
|  | WaitTime.resize(AsmSequence.size()); | 
|  | WaitTimeEntry NullWTEntry = {0, 0, 0, 0}; | 
|  | std::fill(WaitTime.begin(), WaitTime.end(), NullWTEntry); | 
|  | } | 
|  |  | 
|  | void TimelineView::onEvent(const HWInstructionEvent &Event) { | 
|  | const unsigned Index = Event.IR.getSourceIndex(); | 
|  | if (CurrentCycle >= MaxCycle || Index >= Timeline.size()) | 
|  | return; | 
|  | switch (Event.Type) { | 
|  | case HWInstructionEvent::Retired: { | 
|  | TimelineViewEntry &TVEntry = Timeline[Index]; | 
|  | TVEntry.CycleRetired = CurrentCycle; | 
|  |  | 
|  | // Update the WaitTime entry which corresponds to this Index. | 
|  | WaitTimeEntry &WTEntry = WaitTime[Index % AsmSequence.size()]; | 
|  | WTEntry.Executions++; | 
|  | WTEntry.CyclesSpentInSchedulerQueue += | 
|  | TVEntry.CycleIssued - TVEntry.CycleDispatched; | 
|  | assert(TVEntry.CycleDispatched <= TVEntry.CycleReady); | 
|  | WTEntry.CyclesSpentInSQWhileReady += | 
|  | TVEntry.CycleIssued - TVEntry.CycleReady; | 
|  | WTEntry.CyclesSpentAfterWBAndBeforeRetire += | 
|  | (TVEntry.CycleRetired - 1) - TVEntry.CycleExecuted; | 
|  | break; | 
|  | } | 
|  | case HWInstructionEvent::Ready: | 
|  | Timeline[Index].CycleReady = CurrentCycle; | 
|  | break; | 
|  | case HWInstructionEvent::Issued: | 
|  | Timeline[Index].CycleIssued = CurrentCycle; | 
|  | break; | 
|  | case HWInstructionEvent::Executed: | 
|  | Timeline[Index].CycleExecuted = CurrentCycle; | 
|  | break; | 
|  | case HWInstructionEvent::Dispatched: | 
|  | Timeline[Index].CycleDispatched = CurrentCycle; | 
|  | break; | 
|  | default: | 
|  | return; | 
|  | } | 
|  | LastCycle = std::max(LastCycle, CurrentCycle); | 
|  | } | 
|  |  | 
|  | void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS, | 
|  | const WaitTimeEntry &Entry, | 
|  | unsigned SourceIndex) const { | 
|  | OS << SourceIndex << '.'; | 
|  | OS.PadToColumn(7); | 
|  |  | 
|  | if (Entry.Executions == 0) { | 
|  | OS << "-      -      -      -     "; | 
|  | } else { | 
|  | double AverageTime1, AverageTime2, AverageTime3; | 
|  | unsigned Executions = Entry.Executions; | 
|  | AverageTime1 = (double)Entry.CyclesSpentInSchedulerQueue / Executions; | 
|  | AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / Executions; | 
|  | AverageTime3 = (double)Entry.CyclesSpentAfterWBAndBeforeRetire / Executions; | 
|  |  | 
|  | OS << Executions; | 
|  | OS.PadToColumn(13); | 
|  |  | 
|  | OS << format("%.1f", floor((AverageTime1 * 10) + 0.5) / 10); | 
|  | OS.PadToColumn(20); | 
|  | OS << format("%.1f", floor((AverageTime2 * 10) + 0.5) / 10); | 
|  | OS.PadToColumn(27); | 
|  | OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10); | 
|  | OS.PadToColumn(34); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TimelineView::printAverageWaitTimes(raw_ostream &OS) const { | 
|  | if (WaitTime.empty()) | 
|  | return; | 
|  |  | 
|  | std::string Buffer; | 
|  | raw_string_ostream TempStream(Buffer); | 
|  | formatted_raw_ostream FOS(TempStream); | 
|  |  | 
|  | FOS << "\n\nAverage Wait times (based on the timeline view):\n" | 
|  | << "[0]: Executions\n" | 
|  | << "[1]: Average time spent waiting in a scheduler's queue\n" | 
|  | << "[2]: Average time spent waiting in a scheduler's queue while ready\n" | 
|  | << "[3]: Average time elapsed from WB until retire stage\n\n"; | 
|  | FOS << "      [0]    [1]    [2]    [3]\n"; | 
|  |  | 
|  | // Use a different string stream for the instruction. | 
|  | std::string Instruction; | 
|  | raw_string_ostream InstrStream(Instruction); | 
|  |  | 
|  | for (unsigned I = 0, E = WaitTime.size(); I < E; ++I) { | 
|  | printWaitTimeEntry(FOS, WaitTime[I], I); | 
|  | // Append the instruction info at the end of the line. | 
|  | const MCInst &Inst = AsmSequence.getMCInstFromIndex(I); | 
|  |  | 
|  | MCIP.printInst(&Inst, InstrStream, "", STI); | 
|  | InstrStream.flush(); | 
|  |  | 
|  | // Consume any tabs or spaces at the beginning of the string. | 
|  | StringRef Str(Instruction); | 
|  | Str = Str.ltrim(); | 
|  | FOS << "   " << Str << '\n'; | 
|  | FOS.flush(); | 
|  | Instruction = ""; | 
|  |  | 
|  | OS << Buffer; | 
|  | Buffer = ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TimelineView::printTimelineViewEntry(formatted_raw_ostream &OS, | 
|  | const TimelineViewEntry &Entry, | 
|  | unsigned Iteration, | 
|  | unsigned SourceIndex) const { | 
|  | if (Iteration == 0 && SourceIndex == 0) | 
|  | OS << '\n'; | 
|  | OS << '[' << Iteration << ',' << SourceIndex << ']'; | 
|  | OS.PadToColumn(10); | 
|  | for (unsigned I = 0, E = Entry.CycleDispatched; I < E; ++I) | 
|  | OS << ((I % 5 == 0) ? '.' : ' '); | 
|  | OS << TimelineView::DisplayChar::Dispatched; | 
|  | if (Entry.CycleDispatched != Entry.CycleExecuted) { | 
|  | // Zero latency instructions have the same value for CycleDispatched, | 
|  | // CycleIssued and CycleExecuted. | 
|  | for (unsigned I = Entry.CycleDispatched + 1, E = Entry.CycleIssued; I < E; | 
|  | ++I) | 
|  | OS << TimelineView::DisplayChar::Waiting; | 
|  | if (Entry.CycleIssued == Entry.CycleExecuted) | 
|  | OS << TimelineView::DisplayChar::DisplayChar::Executed; | 
|  | else { | 
|  | if (Entry.CycleDispatched != Entry.CycleIssued) | 
|  | OS << TimelineView::DisplayChar::Executing; | 
|  | for (unsigned I = Entry.CycleIssued + 1, E = Entry.CycleExecuted; I < E; | 
|  | ++I) | 
|  | OS << TimelineView::DisplayChar::Executing; | 
|  | OS << TimelineView::DisplayChar::Executed; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (unsigned I = Entry.CycleExecuted + 1, E = Entry.CycleRetired; I < E; ++I) | 
|  | OS << TimelineView::DisplayChar::RetireLag; | 
|  | OS << TimelineView::DisplayChar::Retired; | 
|  |  | 
|  | // Skip other columns. | 
|  | for (unsigned I = Entry.CycleRetired + 1, E = LastCycle; I <= E; ++I) | 
|  | OS << ((I % 5 == 0 || I == LastCycle) ? '.' : ' '); | 
|  | } | 
|  |  | 
|  | static void printTimelineHeader(formatted_raw_ostream &OS, unsigned Cycles) { | 
|  | OS << "\n\nTimeline view:\n"; | 
|  | if (Cycles >= 10) { | 
|  | OS.PadToColumn(10); | 
|  | for (unsigned I = 0; I <= Cycles; ++I) { | 
|  | if (((I / 10) & 1) == 0) | 
|  | OS << ' '; | 
|  | else | 
|  | OS << I % 10; | 
|  | } | 
|  | OS << '\n'; | 
|  | } | 
|  |  | 
|  | OS << "Index"; | 
|  | OS.PadToColumn(10); | 
|  | for (unsigned I = 0; I <= Cycles; ++I) { | 
|  | if (((I / 10) & 1) == 0) | 
|  | OS << I % 10; | 
|  | else | 
|  | OS << ' '; | 
|  | } | 
|  | OS << '\n'; | 
|  | } | 
|  |  | 
|  | void TimelineView::printTimeline(raw_ostream &OS) const { | 
|  | std::string Buffer; | 
|  | raw_string_ostream StringStream(Buffer); | 
|  | formatted_raw_ostream FOS(StringStream); | 
|  |  | 
|  | printTimelineHeader(FOS, LastCycle); | 
|  | FOS.flush(); | 
|  | OS << Buffer; | 
|  |  | 
|  | // Use a different string stream for the instruction. | 
|  | std::string Instruction; | 
|  | raw_string_ostream InstrStream(Instruction); | 
|  |  | 
|  | for (unsigned I = 0, E = Timeline.size(); I < E; ++I) { | 
|  | Buffer = ""; | 
|  | const TimelineViewEntry &Entry = Timeline[I]; | 
|  | if (Entry.CycleRetired == 0) | 
|  | return; | 
|  |  | 
|  | unsigned Iteration = I / AsmSequence.size(); | 
|  | unsigned SourceIndex = I % AsmSequence.size(); | 
|  | printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex); | 
|  | // Append the instruction info at the end of the line. | 
|  | const MCInst &Inst = AsmSequence.getMCInstFromIndex(I); | 
|  | MCIP.printInst(&Inst, InstrStream, "", STI); | 
|  | InstrStream.flush(); | 
|  |  | 
|  | // Consume any tabs or spaces at the beginning of the string. | 
|  | StringRef Str(Instruction); | 
|  | Str = Str.ltrim(); | 
|  | FOS << "   " << Str << '\n'; | 
|  | FOS.flush(); | 
|  | Instruction = ""; | 
|  | OS << Buffer; | 
|  | } | 
|  | } | 
|  | } // namespace mca |