//===- subzero/src/IceTimerTree.cpp - Pass timer defs ---------------------===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Defines the TimerTree class, which tracks flat and cumulative
/// execution time collection of call chains.
///
//===----------------------------------------------------------------------===//

#include "IceTimerTree.h"

#include "IceDefs.h"

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif // __clang__

#include "llvm/Support/Format.h"
#include "llvm/Support/Timer.h"

#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__

namespace Ice {

TimerStack::TimerStack(const std::string &Name)
    : Name(Name), FirstTimestamp(timestamp()), LastTimestamp(FirstTimestamp) {
  if (!BuildDefs::timers())
    return;
  Nodes.resize(1); // Reserve Nodes[0] for the root node (sentinel).
  IDs.resize(TT__num);
  LeafTimes.resize(TT__num);
  LeafCounts.resize(TT__num);
#define STR(s) #s
#define X(tag)                                                                 \
  IDs[TT_##tag] = STR(tag);                                                    \
  IDsIndex[STR(tag)] = TT_##tag;
  TIMERTREE_TABLE;
#undef X
#undef STR
}

// Returns the unique timer ID for the given Name, creating a new ID if needed.
TimerIdT TimerStack::getTimerID(const std::string &Name) {
  if (!BuildDefs::timers())
    return 0;
  if (IDsIndex.find(Name) == IDsIndex.end()) {
    IDsIndex[Name] = IDs.size();
    IDs.push_back(Name);
    LeafTimes.push_back(decltype(LeafTimes)::value_type());
    LeafCounts.push_back(decltype(LeafCounts)::value_type());
  }
  return IDsIndex[Name];
}

// Creates a mapping from TimerIdT (leaf) values in the Src timer stack into
// TimerIdT values in this timer stack. Creates new entries in this timer stack
// as needed.
TimerStack::TranslationType
TimerStack::translateIDsFrom(const TimerStack &Src) {
  size_t Size = Src.IDs.size();
  TranslationType Mapping(Size);
  for (TimerIdT i = 0; i < Size; ++i) {
    Mapping[i] = getTimerID(Src.IDs[i]);
  }
  return Mapping;
}

// Merges two timer stacks, by combining and summing corresponding entries.
// This timer stack is updated from Src.
void TimerStack::mergeFrom(const TimerStack &Src) {
  if (!BuildDefs::timers())
    return;
  TranslationType Mapping = translateIDsFrom(Src);
  TTindex SrcIndex = 0;
  for (const TimerTreeNode &SrcNode : Src.Nodes) {
    // The first node is reserved as a sentinel, so avoid it.
    if (SrcIndex > 0) {
      // Find the full path to the Src node, translated to path components
      // corresponding to this timer stack.
      PathType MyPath = Src.getPath(SrcIndex, Mapping);
      // Find a node in this timer stack corresponding to the given path,
      // creating new interior nodes as necessary.
      TTindex MyIndex = findPath(MyPath);
      Nodes[MyIndex].Time += SrcNode.Time;
      Nodes[MyIndex].UpdateCount += SrcNode.UpdateCount;
    }
    ++SrcIndex;
  }
  for (TimerIdT i = 0; i < Src.LeafTimes.size(); ++i) {
    LeafTimes[Mapping[i]] += Src.LeafTimes[i];
    LeafCounts[Mapping[i]] += Src.LeafCounts[i];
  }
  StateChangeCount += Src.StateChangeCount;
}

// Constructs a path consisting of the sequence of leaf values leading to a
// given node, with the Mapping translation applied to the leaf values. The
// path ends up being in "reverse" order, i.e. from leaf to root.
TimerStack::PathType TimerStack::getPath(TTindex Index,
                                         const TranslationType &Mapping) const {
  PathType Path;
  while (Index) {
    Path.push_back(Mapping[Nodes[Index].Interior]);
    assert(Nodes[Index].Parent < Index);
    Index = Nodes[Index].Parent;
  }
  return Path;
}

// Given a parent node and a leaf ID, returns the index of the parent's child
// ID, creating a new node for the child as necessary.
TimerStack::TTindex TimerStack::getChildIndex(TimerStack::TTindex Parent,
                                              TimerIdT ID) {
  if (Nodes[Parent].Children.size() <= ID)
    Nodes[Parent].Children.resize(ID + 1);
  if (Nodes[Parent].Children[ID] == 0) {
    TTindex Size = Nodes.size();
    Nodes[Parent].Children[ID] = Size;
    Nodes.resize(Size + 1);
    Nodes[Size].Parent = Parent;
    Nodes[Size].Interior = ID;
  }
  return Nodes[Parent].Children[ID];
}

// Finds a node in the timer stack corresponding to the given path, creating
// new interior nodes as necessary.
TimerStack::TTindex TimerStack::findPath(const PathType &Path) {
  TTindex CurIndex = 0;
  // The path is in reverse order (leaf to root), so it needs to be followed in
  // reverse.
  for (TTindex Index : reverse_range(Path)) {
    CurIndex = getChildIndex(CurIndex, Index);
  }
  assert(CurIndex); // shouldn't be the sentinel node
  return CurIndex;
}

// Pushes a new marker onto the timer stack.
void TimerStack::push(TimerIdT ID) {
  if (!BuildDefs::timers())
    return;
  constexpr bool UpdateCounts = false;
  update(UpdateCounts);
  StackTop = getChildIndex(StackTop, ID);
  assert(StackTop);
}

// Pops the top marker from the timer stack. Validates via assert() that the
// expected marker is popped.
void TimerStack::pop(TimerIdT ID) {
  if (!BuildDefs::timers())
    return;
  constexpr bool UpdateCounts = true;
  update(UpdateCounts);
  assert(StackTop);
  assert(Nodes[StackTop].Parent < StackTop);
  // Verify that the expected ID is being popped.
  assert(Nodes[StackTop].Interior == ID);
  (void)ID;
  // Verify that the parent's child points to the current stack top.
  assert(Nodes[Nodes[StackTop].Parent].Children[ID] == StackTop);
  StackTop = Nodes[StackTop].Parent;
}

// At a state change (e.g. push or pop), updates the flat and cumulative
// timings for everything on the timer stack.
void TimerStack::update(bool UpdateCounts) {
  if (!BuildDefs::timers())
    return;
  ++StateChangeCount;
  // Whenever the stack is about to change, we grab the time delta since the
  // last change and add it to all active cumulative elements and to the flat
  // element for the top of the stack.
  double Current = timestamp();
  double Delta = Current - LastTimestamp;
  if (StackTop) {
    TimerIdT Leaf = Nodes[StackTop].Interior;
    if (Leaf >= LeafTimes.size()) {
      LeafTimes.resize(Leaf + 1);
      LeafCounts.resize(Leaf + 1);
    }
    LeafTimes[Leaf] += Delta;
    if (UpdateCounts)
      ++LeafCounts[Leaf];
  }
  TTindex Prefix = StackTop;
  while (Prefix) {
    Nodes[Prefix].Time += Delta;
    // Only update a leaf node count, not the internal node counts.
    if (UpdateCounts && Prefix == StackTop)
      ++Nodes[Prefix].UpdateCount;
    TTindex Next = Nodes[Prefix].Parent;
    assert(Next < Prefix);
    Prefix = Next;
  }
  // Capture the next timestamp *after* the updates are finished. This
  // minimizes how much the timer can perturb the reported timing. The numbers
  // may not sum to 100%, and the missing amount is indicative of the overhead
  // of timing.
  LastTimestamp = timestamp();
}

void TimerStack::reset() {
  if (!BuildDefs::timers())
    return;
  StateChangeCount = 0;
  FirstTimestamp = LastTimestamp = timestamp();
  LeafTimes.assign(LeafTimes.size(), 0);
  LeafCounts.assign(LeafCounts.size(), 0);
  for (TimerTreeNode &Node : Nodes) {
    Node.Time = 0;
    Node.UpdateCount = 0;
  }
}

namespace {

using DumpMapType = std::multimap<double, std::string>;

// Dump the Map items in reverse order of their time contribution.  If
// AddPercents is true (i.e. for printing "flat times"), it also prints a
// cumulative percentage column, and recalculates TotalTime as the sum of all
// the individual times so that cumulative percentage adds up to 100%.
void dumpHelper(Ostream &Str, const DumpMapType &Map, double TotalTime,
                bool AddPercents) {
  if (!BuildDefs::timers())
    return;
  if (AddPercents) {
    // Recalculate TotalTime as the sum of the individual times.  This is
    // because the individual times generally add up to less than 100% because
    // of timer overhead.
    TotalTime = 0;
    for (const auto &I : Map) {
      TotalTime += I.first;
    }
  }
  double Sum = 0;
  for (const auto &I : reverse_range(Map)) {
    Sum += I.first;
    if (AddPercents) {
      Str << llvm::format("  %10.6f %4.1f%% %5.1f%% ", I.first,
                          I.first * 100 / TotalTime, Sum * 100 / TotalTime)
          << I.second << "\n";
    } else {
      Str << llvm::format("  %10.6f %4.1f%% ", I.first,
                          I.first * 100 / TotalTime) << I.second << "\n";
    }
  }
}

} // end of anonymous namespace

void TimerStack::dump(Ostream &Str, bool DumpCumulative) {
  if (!BuildDefs::timers())
    return;
  constexpr bool UpdateCounts = true;
  update(UpdateCounts);
  double TotalTime = LastTimestamp - FirstTimestamp;
  assert(TotalTime);
  char PrefixStr[30];
  if (DumpCumulative) {
    Str << Name << " - Cumulative times:\n"
                   "     Seconds   Pct  EventCnt TimerPath\n";
    DumpMapType CumulativeMap;
    for (TTindex i = 1; i < Nodes.size(); ++i) {
      TTindex Prefix = i;
      std::string Suffix = "";
      while (Prefix) {
        if (Suffix.empty())
          Suffix = IDs[Nodes[Prefix].Interior];
        else
          Suffix = IDs[Nodes[Prefix].Interior] + "." + Suffix;
        assert(Nodes[Prefix].Parent < Prefix);
        Prefix = Nodes[Prefix].Parent;
      }
      snprintf(PrefixStr, llvm::array_lengthof(PrefixStr), "%9zu ",
               Nodes[i].UpdateCount);
      CumulativeMap.insert(std::make_pair(Nodes[i].Time, PrefixStr + Suffix));
    }
    constexpr bool NoAddPercents = false;
    dumpHelper(Str, CumulativeMap, TotalTime, NoAddPercents);
  }
  Str << Name << " - Flat times:\n"
                 "     Seconds   Pct CumPct  EventCnt TimerName\n";
  DumpMapType FlatMap;
  for (TimerIdT i = 0; i < LeafTimes.size(); ++i) {
    if (LeafCounts[i]) {
      snprintf(PrefixStr, llvm::array_lengthof(PrefixStr), "%9zu ",
               LeafCounts[i]);
      FlatMap.insert(std::make_pair(LeafTimes[i], PrefixStr + IDs[i]));
    }
  }
  constexpr bool AddPercents = true;
  dumpHelper(Str, FlatMap, TotalTime, AddPercents);
  Str << "Number of timer updates: " << StateChangeCount << "\n";
}

double TimerStack::timestamp() {
  // TODO: Implement in terms of std::chrono for C++11.
  return llvm::TimeRecord::getCurrentTime(false).getWallTime();
}

} // end of namespace Ice
