Commit 380d7b96 by Jim Stichnoth

Subzero: Fix timers for multithreaded translation.

Now that multithreaded parsing and translation is in place, timer operations have to be made thread-local. After the non-main threads end, their thread-local timer data needs to be merged into the global timer data, which resides in the GlobalContext object. The merge is a bit tricky because the internal timer stack structure is built up dynamically as items are pushed and popped. Two threads may have radically different timing data: 1. The parser thread profile is completely different from a translator thread. 2. For -timing-funcs, two translator threads hold data for entirely different sets of functions. A bit more tweaking will need to be done to make the timing output fully usable in a multithreaded run. Because of multiple threads, times may add up to >100%. Also, time spent blocked is being "unfairly" attributed to the caller of the blocking operation - we should either count the user time instead of wall-clock time, or add a special timer marker for blocking locking operations. BUG= none R=jvoung@chromium.org Review URL: https://codereview.chromium.org/878383004
parent 51d00936
...@@ -81,13 +81,21 @@ bool Cfg::hasComputedFrame() const { return getTarget()->hasComputedFrame(); } ...@@ -81,13 +81,21 @@ bool Cfg::hasComputedFrame() const { return getTarget()->hasComputedFrame(); }
void Cfg::translate() { void Cfg::translate() {
if (hasError()) if (hasError())
return; return;
// FunctionTimer conditionally pushes/pops a TimerMarker if
// TimeEachFunction is enabled.
std::unique_ptr<TimerMarker> FunctionTimer;
if (ALLOW_DUMP) { if (ALLOW_DUMP) {
const IceString &TimingFocusOn = getContext()->getFlags().TimingFocusOn; const IceString &TimingFocusOn = getContext()->getFlags().TimingFocusOn;
if (TimingFocusOn == "*" || TimingFocusOn == getFunctionName()) { const IceString &Name = getFunctionName();
if (TimingFocusOn == "*" || TimingFocusOn == Name) {
setFocusedTiming(); setFocusedTiming();
getContext()->resetTimer(GlobalContext::TSK_Default); getContext()->resetTimer(GlobalContext::TSK_Default);
getContext()->setTimerName(GlobalContext::TSK_Default, getFunctionName()); getContext()->setTimerName(GlobalContext::TSK_Default, Name);
} }
if (getContext()->getFlags().TimeEachFunction)
FunctionTimer.reset(new TimerMarker(
getContext()->getTimerID(GlobalContext::TSK_Funcs, Name),
getContext(), GlobalContext::TSK_Funcs));
} }
TimerMarker T(TimerStack::TT_translate, this); TimerMarker T(TimerStack::TT_translate, this);
......
...@@ -143,8 +143,9 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, ...@@ -143,8 +143,9 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit,
// Create a new ThreadContext for the current thread. No need to // Create a new ThreadContext for the current thread. No need to
// lock AllThreadContexts at this point since no other threads have // lock AllThreadContexts at this point since no other threads have
// access yet to this GlobalContext object. // access yet to this GlobalContext object.
AllThreadContexts.push_back(new ThreadContext()); ThreadContext *MyTLS = new ThreadContext();
ICE_TLS_SET_FIELD(TLS, AllThreadContexts.back()); AllThreadContexts.push_back(MyTLS);
ICE_TLS_SET_FIELD(TLS, MyTLS);
// Pre-register built-in stack names. // Pre-register built-in stack names.
if (ALLOW_DUMP) { if (ALLOW_DUMP) {
// TODO(stichnot): There needs to be a strong relationship between // TODO(stichnot): There needs to be a strong relationship between
...@@ -152,6 +153,7 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, ...@@ -152,6 +153,7 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit,
newTimerStackID("Total across all functions"); newTimerStackID("Total across all functions");
newTimerStackID("Per-function summary"); newTimerStackID("Per-function summary");
} }
Timers.initInto(MyTLS->Timers);
if (Flags.UseELFWriter) { if (Flags.UseELFWriter) {
ObjectWriter.reset(new ELFObjectWriter(*this, *ELFStr)); ObjectWriter.reset(new ELFObjectWriter(*this, *ELFStr));
} }
...@@ -503,38 +505,32 @@ TimerStackIdT GlobalContext::newTimerStackID(const IceString &Name) { ...@@ -503,38 +505,32 @@ TimerStackIdT GlobalContext::newTimerStackID(const IceString &Name) {
TimerIdT GlobalContext::getTimerID(TimerStackIdT StackID, TimerIdT GlobalContext::getTimerID(TimerStackIdT StackID,
const IceString &Name) { const IceString &Name) {
auto Timers = getTimers(); auto Timers = &ICE_TLS_GET_FIELD(TLS)->Timers;
assert(StackID < Timers->size()); assert(StackID < Timers->size());
return Timers->at(StackID).getTimerID(Name); return Timers->at(StackID).getTimerID(Name);
} }
void GlobalContext::pushTimer(TimerIdT ID, TimerStackIdT StackID) { void GlobalContext::pushTimer(TimerIdT ID, TimerStackIdT StackID) {
// TODO(stichnot): Timers are completely broken for multithreading; fix. auto Timers = &ICE_TLS_GET_FIELD(TLS)->Timers;
if (getFlags().NumTranslationThreads)
llvm::report_fatal_error("Timers and multithreading are currently broken");
auto Timers = getTimers();
assert(StackID < Timers->size()); assert(StackID < Timers->size());
Timers->at(StackID).push(ID); Timers->at(StackID).push(ID);
} }
void GlobalContext::popTimer(TimerIdT ID, TimerStackIdT StackID) { void GlobalContext::popTimer(TimerIdT ID, TimerStackIdT StackID) {
// TODO(stichnot): Timers are completely broken for multithreading; fix. auto Timers = &ICE_TLS_GET_FIELD(TLS)->Timers;
if (getFlags().NumTranslationThreads)
llvm::report_fatal_error("Timers and multithreading are currently broken");
auto Timers = getTimers();
assert(StackID < Timers->size()); assert(StackID < Timers->size());
Timers->at(StackID).pop(ID); Timers->at(StackID).pop(ID);
} }
void GlobalContext::resetTimer(TimerStackIdT StackID) { void GlobalContext::resetTimer(TimerStackIdT StackID) {
auto Timers = getTimers(); auto Timers = &ICE_TLS_GET_FIELD(TLS)->Timers;
assert(StackID < Timers->size()); assert(StackID < Timers->size());
Timers->at(StackID).reset(); Timers->at(StackID).reset();
} }
void GlobalContext::setTimerName(TimerStackIdT StackID, void GlobalContext::setTimerName(TimerStackIdT StackID,
const IceString &NewName) { const IceString &NewName) {
auto Timers = getTimers(); auto Timers = &ICE_TLS_GET_FIELD(TLS)->Timers;
assert(StackID < Timers->size()); assert(StackID < Timers->size());
Timers->at(StackID).setName(NewName); Timers->at(StackID).setName(NewName);
} }
...@@ -560,13 +556,26 @@ void GlobalContext::dumpTimers(TimerStackIdT StackID, bool DumpCumulative) { ...@@ -560,13 +556,26 @@ void GlobalContext::dumpTimers(TimerStackIdT StackID, bool DumpCumulative) {
Timers->at(StackID).dump(getStrDump(), DumpCumulative); Timers->at(StackID).dump(getStrDump(), DumpCumulative);
} }
TimerMarker::TimerMarker(TimerIdT ID, const Cfg *Func) void TimerMarker::push() {
: ID(ID), Ctx(Func->getContext()), Active(false) { switch (StackID) {
if (ALLOW_DUMP) { case GlobalContext::TSK_Default:
Active = Func->getFocusedTiming() || Ctx->getFlags().SubzeroTimingEnabled; Active = Ctx->getFlags().SubzeroTimingEnabled;
if (Active) break;
Ctx->pushTimer(ID); case GlobalContext::TSK_Funcs:
Active = Ctx->getFlags().TimeEachFunction;
break;
default:
break;
} }
if (Active)
Ctx->pushTimer(ID, StackID);
}
void TimerMarker::pushCfg(const Cfg *Func) {
Ctx = Func->getContext();
Active = Func->getFocusedTiming() || Ctx->getFlags().SubzeroTimingEnabled;
if (Active)
Ctx->pushTimer(ID, StackID);
} }
ICE_TLS_DEFINE_FIELD(GlobalContext::ThreadContext *, GlobalContext, TLS); ICE_TLS_DEFINE_FIELD(GlobalContext::ThreadContext *, GlobalContext, TLS);
......
...@@ -83,6 +83,35 @@ class GlobalContext { ...@@ -83,6 +83,35 @@ class GlobalContext {
uint32_t Fills; uint32_t Fills;
}; };
// TimerList is a vector of TimerStack objects, with extra methods
// to initialize and merge these vectors.
class TimerList : public std::vector<TimerStack> {
public:
// initInto() initializes a target list of timers based on the
// current list. In particular, it creates the same number of
// timers, in the same order, with the same names, but initially
// empty of timing data.
void initInto(TimerList &Dest) const {
if (!ALLOW_DUMP)
return;
Dest.clear();
for (const TimerStack &Stack : *this) {
Dest.push_back(TimerStack(Stack.getName()));
}
}
void mergeFrom(TimerList &Src) {
if (!ALLOW_DUMP)
return;
assert(size() == Src.size());
size_type i = 0;
for (TimerStack &Stack : *this) {
assert(Stack.getName() == Src[i].getName());
Stack.mergeFrom(Src[i]);
++i;
}
}
};
// ThreadContext contains thread-local data. This data can be // ThreadContext contains thread-local data. This data can be
// combined/reduced as needed after all threads complete. // combined/reduced as needed after all threads complete.
class ThreadContext { class ThreadContext {
...@@ -92,7 +121,7 @@ class GlobalContext { ...@@ -92,7 +121,7 @@ class GlobalContext {
public: public:
ThreadContext() {} ThreadContext() {}
CodeStats StatsFunction; CodeStats StatsFunction;
std::vector<TimerStack> Timers; TimerList Timers;
}; };
public: public:
...@@ -211,14 +240,20 @@ public: ...@@ -211,14 +240,20 @@ public:
// These are predefined TimerStackIdT values. // These are predefined TimerStackIdT values.
enum TimerStackKind { TSK_Default = 0, TSK_Funcs, TSK_Num }; enum TimerStackKind { TSK_Default = 0, TSK_Funcs, TSK_Num };
// newTimerStackID() creates a new TimerStack in the global space.
// It does not affect any TimerStack objects in TLS.
TimerStackIdT newTimerStackID(const IceString &Name); TimerStackIdT newTimerStackID(const IceString &Name);
// dumpTimers() dumps the global timer data. As such, one probably
// wants to call mergeTimerStacks() as a prerequisite.
void dumpTimers(TimerStackIdT StackID = TSK_Default,
bool DumpCumulative = true);
// The following methods affect only the calling thread's TLS timer
// data.
TimerIdT getTimerID(TimerStackIdT StackID, const IceString &Name); TimerIdT getTimerID(TimerStackIdT StackID, const IceString &Name);
void pushTimer(TimerIdT ID, TimerStackIdT StackID = TSK_Default); void pushTimer(TimerIdT ID, TimerStackIdT StackID);
void popTimer(TimerIdT ID, TimerStackIdT StackID = TSK_Default); void popTimer(TimerIdT ID, TimerStackIdT StackID);
void resetTimer(TimerStackIdT StackID); void resetTimer(TimerStackIdT StackID);
void setTimerName(TimerStackIdT StackID, const IceString &NewName); void setTimerName(TimerStackIdT StackID, const IceString &NewName);
void dumpTimers(TimerStackIdT StackID = TSK_Default,
bool DumpCumulative = true);
// Adds a newly parsed and constructed function to the Cfg work // Adds a newly parsed and constructed function to the Cfg work
// queue. Notifies any idle workers that a new function is // queue. Notifies any idle workers that a new function is
...@@ -235,8 +270,10 @@ public: ...@@ -235,8 +270,10 @@ public:
void startWorkerThreads() { void startWorkerThreads() {
size_t NumWorkers = getFlags().NumTranslationThreads; size_t NumWorkers = getFlags().NumTranslationThreads;
auto Timers = getTimers();
for (size_t i = 0; i < NumWorkers; ++i) { for (size_t i = 0; i < NumWorkers; ++i) {
ThreadContext *WorkerTLS = new ThreadContext(); ThreadContext *WorkerTLS = new ThreadContext();
Timers->initInto(WorkerTLS->Timers);
AllThreadContexts.push_back(WorkerTLS); AllThreadContexts.push_back(WorkerTLS);
TranslationThreads.push_back(std::thread( TranslationThreads.push_back(std::thread(
&GlobalContext::translateFunctionsWrapper, this, WorkerTLS)); &GlobalContext::translateFunctionsWrapper, this, WorkerTLS));
...@@ -254,6 +291,11 @@ public: ...@@ -254,6 +291,11 @@ public:
} }
TranslationThreads.clear(); TranslationThreads.clear();
// TODO(stichnot): join the emitter thread. // TODO(stichnot): join the emitter thread.
if (ALLOW_DUMP) {
auto Timers = getTimers();
for (ThreadContext *TLS : AllThreadContexts)
Timers->mergeFrom(TLS->Timers);
}
} }
// Translation thread startup routine. // Translation thread startup routine.
...@@ -301,7 +343,7 @@ private: ...@@ -301,7 +343,7 @@ private:
ICE_CACHELINE_BOUNDARY; ICE_CACHELINE_BOUNDARY;
// Managed by getTimers() // Managed by getTimers()
GlobalLockType TimerLock; GlobalLockType TimerLock;
std::vector<TimerStack> Timers; TimerList Timers;
ICE_CACHELINE_BOUNDARY; ICE_CACHELINE_BOUNDARY;
// StrLock is a global lock on the dump and emit output streams. // StrLock is a global lock on the dump and emit output streams.
...@@ -331,8 +373,8 @@ private: ...@@ -331,8 +373,8 @@ private:
LockedPtr<CodeStats> getStatsCumulative() { LockedPtr<CodeStats> getStatsCumulative() {
return LockedPtr<CodeStats>(&StatsCumulative, &StatsLock); return LockedPtr<CodeStats>(&StatsCumulative, &StatsLock);
} }
LockedPtr<std::vector<TimerStack>> getTimers() { LockedPtr<TimerList> getTimers() {
return LockedPtr<std::vector<TimerStack>>(&Timers, &TimerLock); return LockedPtr<TimerList>(&Timers, &TimerLock);
} }
std::vector<ThreadContext *> AllThreadContexts; std::vector<ThreadContext *> AllThreadContexts;
...@@ -357,24 +399,31 @@ class TimerMarker { ...@@ -357,24 +399,31 @@ class TimerMarker {
TimerMarker &operator=(const TimerMarker &) = delete; TimerMarker &operator=(const TimerMarker &) = delete;
public: public:
TimerMarker(TimerIdT ID, GlobalContext *Ctx) TimerMarker(TimerIdT ID, GlobalContext *Ctx,
: ID(ID), Ctx(Ctx), Active(false) { TimerStackIdT StackID = GlobalContext::TSK_Default)
if (ALLOW_DUMP) { : ID(ID), Ctx(Ctx), StackID(StackID), Active(false) {
Active = Ctx->getFlags().SubzeroTimingEnabled; if (ALLOW_DUMP)
if (Active) push();
Ctx->pushTimer(ID); }
} TimerMarker(TimerIdT ID, const Cfg *Func,
TimerStackIdT StackID = GlobalContext::TSK_Default)
: ID(ID), Ctx(nullptr), StackID(StackID), Active(false) {
// Ctx gets set at the beginning of pushCfg().
if (ALLOW_DUMP)
pushCfg(Func);
} }
TimerMarker(TimerIdT ID, const Cfg *Func);
~TimerMarker() { ~TimerMarker() {
if (ALLOW_DUMP && Active) if (ALLOW_DUMP && Active)
Ctx->popTimer(ID); Ctx->popTimer(ID, StackID);
} }
private: private:
TimerIdT ID; void push();
GlobalContext *const Ctx; void pushCfg(const Cfg *Func);
const TimerIdT ID;
GlobalContext *Ctx;
const TimerStackIdT StackID;
bool Active; bool Active;
}; };
......
...@@ -24,8 +24,10 @@ TimerStack::TimerStack(const IceString &Name) ...@@ -24,8 +24,10 @@ TimerStack::TimerStack(const IceString &Name)
StateChangeCount(0), StackTop(0) { StateChangeCount(0), StackTop(0) {
if (!ALLOW_DUMP) if (!ALLOW_DUMP)
return; return;
Nodes.resize(1); // Reserve Nodes[0] for the root node. Nodes.resize(1); // Reserve Nodes[0] for the root node (sentinel).
IDs.resize(TT__num); IDs.resize(TT__num);
LeafTimes.resize(TT__num);
LeafCounts.resize(TT__num);
#define STR(s) #s #define STR(s) #s
#define X(tag) \ #define X(tag) \
IDs[TT_##tag] = STR(tag); \ IDs[TT_##tag] = STR(tag); \
...@@ -43,29 +45,108 @@ TimerIdT TimerStack::getTimerID(const IceString &Name) { ...@@ -43,29 +45,108 @@ TimerIdT TimerStack::getTimerID(const IceString &Name) {
if (IDsIndex.find(Name) == IDsIndex.end()) { if (IDsIndex.find(Name) == IDsIndex.end()) {
IDsIndex[Name] = IDs.size(); IDsIndex[Name] = IDs.size();
IDs.push_back(Name); IDs.push_back(Name);
LeafTimes.push_back(decltype(LeafTimes)::value_type());
LeafCounts.push_back(decltype(LeafCounts)::value_type());
} }
return IDsIndex[Name]; return IDsIndex[Name];
} }
// Pushes a new marker onto the timer stack. // Creates a mapping from TimerIdT (leaf) values in the Src timer
void TimerStack::push(TimerIdT ID) { // 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 (!ALLOW_DUMP) if (!ALLOW_DUMP)
return; return;
const bool UpdateCounts = false; TranslationType Mapping = translateIDsFrom(Src);
update(UpdateCounts); TTindex SrcIndex = 0;
if (Nodes[StackTop].Children.size() <= ID) for (const TimerTreeNode &SrcNode : Src.Nodes) {
Nodes[StackTop].Children.resize(ID + 1); // The first node is reserved as a sentinel, so avoid it.
if (Nodes[StackTop].Children[ID] == 0) { 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(); TTindex Size = Nodes.size();
Nodes[StackTop].Children[ID] = Size; Nodes[Parent].Children[ID] = Size;
Nodes.resize(Size + 1); Nodes.resize(Size + 1);
Nodes[Size].Parent = StackTop; Nodes[Size].Parent = Parent;
Nodes[Size].Interior = ID; Nodes[Size].Interior = ID;
} }
StackTop = Nodes[StackTop].Children[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 (!ALLOW_DUMP)
return;
const bool UpdateCounts = false;
update(UpdateCounts);
StackTop = getChildIndex(StackTop, ID);
assert(StackTop);
} }
// Pop the top marker from the timer stack. Validates via assert() // Pops the top marker from the timer stack. Validates via assert()
// that the expected marker is popped. // that the expected marker is popped.
void TimerStack::pop(TimerIdT ID) { void TimerStack::pop(TimerIdT ID) {
if (!ALLOW_DUMP) if (!ALLOW_DUMP)
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICETIMERTREE_DEF #ifndef SUBZERO_SRC_ICETIMERTREE_DEF
#define SUBZERO_SRC_ICETIMERTREE_DEF
#define TIMERTREE_TABLE \ #define TIMERTREE_TABLE \
/* enum value */ \ /* enum value */ \
...@@ -53,5 +54,4 @@ ...@@ -53,5 +54,4 @@
X(vmetadata) X(vmetadata)
//#define X(tag) //#define X(tag)
#define SUBZERO_SRC_ICETIMERTREE_DEF
#endif // SUBZERO_SRC_ICETIMERTREE_DEF #endif // SUBZERO_SRC_ICETIMERTREE_DEF
...@@ -22,8 +22,16 @@ namespace Ice { ...@@ -22,8 +22,16 @@ namespace Ice {
class TimerStack { class TimerStack {
TimerStack &operator=(const TimerStack &) = delete; TimerStack &operator=(const TimerStack &) = delete;
// Timer tree index type // Timer tree index type. A variable of this type is used to access
// an interior, not-necessarily-leaf node of the tree.
typedef std::vector<class TimerTreeNode>::size_type TTindex; typedef std::vector<class TimerTreeNode>::size_type TTindex;
// Representation of a path of leaf values leading to a particular
// node. The representation happens to be in "reverse" order,
// i.e. from leaf/interior to root, for implementation efficiency.
typedef llvm::SmallVector<TTindex, 8> PathType;
// Representation of a mapping of leaf node indexes from one timer
// stack to another.
typedef std::vector<TimerIdT> TranslationType;
// TimerTreeNode represents an interior or leaf node in the call tree. // TimerTreeNode represents an interior or leaf node in the call tree.
// It contains a list of children, a pointer to its parent, and the // It contains a list of children, a pointer to its parent, and the
...@@ -54,7 +62,9 @@ public: ...@@ -54,7 +62,9 @@ public:
TimerStack(const IceString &Name); TimerStack(const IceString &Name);
TimerStack(const TimerStack &) = default; TimerStack(const TimerStack &) = default;
TimerIdT getTimerID(const IceString &Name); TimerIdT getTimerID(const IceString &Name);
void mergeFrom(const TimerStack &Src);
void setName(const IceString &NewName) { Name = NewName; } void setName(const IceString &NewName) { Name = NewName; }
const IceString &getName() const { return Name; }
void push(TimerIdT ID); void push(TimerIdT ID);
void pop(TimerIdT ID); void pop(TimerIdT ID);
void reset(); void reset();
...@@ -63,6 +73,10 @@ public: ...@@ -63,6 +73,10 @@ public:
private: private:
void update(bool UpdateCounts); void update(bool UpdateCounts);
static double timestamp(); static double timestamp();
TranslationType translateIDsFrom(const TimerStack &Src);
PathType getPath(TTindex Index, const TranslationType &Mapping) const;
TTindex getChildIndex(TTindex Parent, TimerIdT ID);
TTindex findPath(const PathType &Path);
IceString Name; IceString Name;
double FirstTimestamp; double FirstTimestamp;
double LastTimestamp; double LastTimestamp;
......
...@@ -1157,7 +1157,7 @@ private: ...@@ -1157,7 +1157,7 @@ private:
if (ALLOW_DUMP && getFlags().TimeEachFunction) { if (ALLOW_DUMP && getFlags().TimeEachFunction) {
getTranslator().getContext()->popTimer( getTranslator().getContext()->popTimer(
getTranslator().getContext()->getTimerID( getTranslator().getContext()->getTimerID(
Ice::GlobalContext::TSK_Funcs, Func->getFunctionName()), Ice::GlobalContext::TSK_Funcs, FuncDecl->getName()),
Ice::GlobalContext::TSK_Funcs); Ice::GlobalContext::TSK_Funcs);
} }
} }
...@@ -1827,6 +1827,7 @@ private: ...@@ -1827,6 +1827,7 @@ private:
void FunctionParser::ExitBlock() { void FunctionParser::ExitBlock() {
if (isIRGenerationDisabled()) { if (isIRGenerationDisabled()) {
popTimerIfTimingEachFunction(); popTimerIfTimingEachFunction();
delete Func;
return; return;
} }
// Before translating, check for blocks without instructions, and // Before translating, check for blocks without instructions, and
...@@ -1844,13 +1845,16 @@ void FunctionParser::ExitBlock() { ...@@ -1844,13 +1845,16 @@ void FunctionParser::ExitBlock() {
++Index; ++Index;
} }
Func->computePredecessors(); Func->computePredecessors();
// Temporarily end per-function timing, which will be resumed by the
// translator function. This is because translation may be done
// asynchronously in a separate thread.
popTimerIfTimingEachFunction();
// Note: Once any errors have been found, we turn off all // Note: Once any errors have been found, we turn off all
// translation of all remaining functions. This allows use to see // translation of all remaining functions. This allows use to see
// multiple errors, without adding extra checks to the translator // multiple errors, without adding extra checks to the translator
// for such parsing errors. // for such parsing errors.
if (Context->getNumErrors() == 0) if (Context->getNumErrors() == 0)
getTranslator().translateFcn(Func); getTranslator().translateFcn(Func);
popTimerIfTimingEachFunction();
} }
void FunctionParser::ReportInvalidBinaryOp(Ice::InstArithmetic::OpKind Op, void FunctionParser::ReportInvalidBinaryOp(Ice::InstArithmetic::OpKind Op,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment