Index: src/IceGlobalContext.h |
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h |
index 751147ee48502811e0f37eaf21c92d0dae0ad17c..3583fbf26ecc7b9f4430f0dc47800244c2d68d63 100644 |
--- a/src/IceGlobalContext.h |
+++ b/src/IceGlobalContext.h |
@@ -15,8 +15,9 @@ |
#ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H |
#define SUBZERO_SRC_ICEGLOBALCONTEXT_H |
-#include <memory> |
#include <mutex> |
+#include <queue> |
+#include <thread> |
#include "IceDefs.h" |
#include "IceClFlags.h" |
@@ -96,20 +97,69 @@ class GlobalContext { |
std::vector<TimerStack> Timers; |
}; |
+ class CfgQueue { |
+ public: |
+ explicit CfgQueue(uint32_t NumWorkers) |
+ : NumWorkers(NumWorkers), IsEnded(false) {} |
+ void add(Cfg *Func) { |
+ std::unique_lock<GlobalLockType> L(Lock); |
JF
2015/01/22 20:50:56
Can you add a comment on CfgQueue explaining the l
jvoung (off chromium)
2015/01/22 23:06:06
Yeah, for pnacl-llc, it dequeues N functions at a
Jim Stichnoth
2015/01/23 07:55:54
Done.
Jim Stichnoth
2015/01/23 07:55:55
Even for small functions, my sense is that it take
jvoung (off chromium)
2015/01/23 17:49:09
Sorry I don't have the numbers anymore, but it can
JF
2015/01/23 17:51:02
Code review was:
https://codereview.chromium.org
|
+ // If the work queue is already "full", wait for a consumer to |
+ // grab an element and shrink the queue. |
+ while (WorkQueue.size() > NumWorkers) { |
+ Shrunk.wait(L); |
+ } |
+ WorkQueue.push(Func); |
+ L.unlock(); |
+ GrewOrEnded.notify_one(); |
+ } |
+ Cfg *get() { |
+ std::unique_lock<GlobalLockType> L(Lock); |
+ while (!IsEnded || !WorkQueue.empty()) { |
+ if (!WorkQueue.empty()) { |
+ Cfg *Func = WorkQueue.front(); |
+ WorkQueue.pop(); |
+ L.unlock(); |
+ Shrunk.notify_one(); |
+ return Func; |
+ } |
+ // If the work queue is empty, and this is pure sequential |
+ // execution, then return nullptr. |
+ if (NumWorkers == 0) |
+ return nullptr; |
+ GrewOrEnded.wait(L); |
+ } |
+ return nullptr; |
+ } |
+ void end() { |
+ std::unique_lock<GlobalLockType> L(Lock); |
+ IsEnded = true; |
+ L.unlock(); |
+ GrewOrEnded.notify_all(); |
+ } |
+ |
+ private: |
+ std::queue<Cfg *> WorkQueue; |
JF
2015/01/22 20:50:56
This should probably be an std::array if the size
Jim Stichnoth
2015/01/23 07:55:55
There is just one add() and one get() per function
|
+ // Lock guards access to WorkQueue and IsEnded. |
+ GlobalLockType Lock; |
+ // GrewOrEnded is notified (by the producer) when something is |
+ // added to the queue, in case consumers are waiting for a |
+ // non-empty queue. |
+ std::condition_variable GrewOrEnded; |
+ // Shrunk is notified (by the consumer) when something is removed |
+ // from the queue, in case the producer is waiting for the queue |
+ // to drop below maximum capacity. |
+ std::condition_variable Shrunk; |
+ const uint32_t NumWorkers; |
JF
2015/01/22 20:50:56
I'd make this a size_t.
Jim Stichnoth
2015/01/23 07:55:55
Done, here and in IceClFlags.h and llvm2ice.cpp.
|
+ bool IsEnded; |
+ }; |
+ |
public: |
GlobalContext(Ostream *OsDump, Ostream *OsEmit, ELFStreamer *ELFStreamer, |
VerboseMask Mask, TargetArch Arch, OptLevel Opt, |
IceString TestPrefix, const ClFlags &Flags); |
~GlobalContext(); |
- // Returns true if any of the specified options in the verbose mask |
- // are set. If the argument is omitted, it checks if any verbose |
- // options at all are set. |
VerboseMask getVerbose() const { return VMask; } |
- bool isVerbose(VerboseMask Mask = IceV_All) const { return VMask & Mask; } |
- void setVerbose(VerboseMask Mask) { VMask = Mask; } |
- void addVerbose(VerboseMask Mask) { VMask |= Mask; } |
- void subVerbose(VerboseMask Mask) { VMask &= ~Mask; } |
// The dump and emit streams need to be used by only one thread at a |
// time. This is done by exclusively reserving the streams via |
@@ -129,6 +179,7 @@ public: |
TargetArch getTargetArch() const { return Arch; } |
OptLevel getOptLevel() const { return Opt; } |
+ bool getErrorStatus() const { return ErrorStatus; } |
// When emitting assembly, we allow a string to be prepended to |
// names of translated functions. This makes it easier to create an |
@@ -229,6 +280,58 @@ public: |
void dumpTimers(TimerStackIdT StackID = TSK_Default, |
bool DumpCumulative = true); |
+ // Adds a newly parsed and constructed function to the Cfg work |
+ // queue. Notifies any idle workers that a new function is |
+ // available for translating. May block if the work queue is too |
+ // large, in order to control memory footprint. |
+ void cfgQueueAdd(Cfg *Func) { CfgQ.add(Func); } |
+ // Takes a Cfg from the work queue for translating. May block if |
+ // the work queue is currently empty. Returns nullptr if there is |
+ // no more work (in which case the translation thread will probably |
+ // just exit). |
JF
2015/01/22 20:50:56
"probably"? That seems mostly accurate.
Jim Stichnoth
2015/01/23 07:55:54
Done.
|
+ Cfg *cfgQueueGet() { return CfgQ.get(); } |
+ // Notifies that no more work will be added to the work queue. |
+ void cfgQueueEnd() { CfgQ.end(); } |
+ |
+ void startWorkerThreads() { |
+ uint32_t NumWorkers = getFlags().NumTranslationThreads; |
+ for (uint32_t i = 0; i < NumWorkers; ++i) { |
+ ThreadContext *WorkerTLS = new ThreadContext(); |
+ AllThreadContexts.push_back(WorkerTLS); |
+ TranslationThreads.push_back(std::thread( |
+ &GlobalContext::translateFunctionsWrapper, this, WorkerTLS)); |
+ } |
+ if (NumWorkers) { |
+ // TODO(stichnot): start emitter thread |
JF
2015/01/22 20:50:56
?
Jim Stichnoth
2015/01/23 07:55:54
Done.
|
+ } |
+ } |
+ |
+ void waitForWorkerThreads() { |
+ cfgQueueEnd(); |
+ // TODO(stichnot): end the emitter queue |
+ for (std::thread &Worker : TranslationThreads) { |
+ Worker.join(); |
+ } |
+ TranslationThreads.clear(); |
+ // TODO(stichnot): join the emitter queue |
+ } |
+ |
+ // Translation thread startup routine. |
+ void translateFunctionsWrapper(ThreadContext *MyTLS) { |
+ TLS = MyTLS; |
+ translateFunctions(); |
+ } |
+ // Translate functions from the Cfg queue until the queue is empty. |
+ void translateFunctions(); |
+ |
+ // Utility function to match a symbol name against a match string. |
+ // An empty match string means match everything. Returns true if |
+ // there is a match. |
+ static bool matchSymbolName(const IceString &SymbolName, |
+ const IceString &Match) { |
+ return Match.empty() || Match == SymbolName; |
+} |
JF
2015/01/22 20:50:56
I don't understand what this is for.
Jim Stichnoth
2015/01/23 07:55:54
Hopefully documented better.
|
+ |
private: |
// Try to make sure the mutexes are allocated on separate cache |
// lines, assuming the maximum cache line size is 64. |
@@ -257,6 +360,8 @@ private: |
std::unique_ptr<ELFObjectWriter> ObjectWriter; |
CodeStats StatsCumulative; |
std::vector<TimerStack> Timers; |
+ CfgQueue CfgQ; |
+ bool ErrorStatus; |
JF
2015/01/22 20:50:56
Use std::error_code?
Jim Stichnoth
2015/01/23 07:55:54
Done.
|
LockedPtr<ArenaAllocator<>> getAllocator() { |
return LockedPtr<ArenaAllocator<>>(&Allocator, &AllocLock); |
@@ -272,6 +377,7 @@ private: |
} |
std::vector<ThreadContext *> AllThreadContexts; |
+ std::vector<std::thread> TranslationThreads; |
// Each thread has its own TLS pointer which is also held in |
// AllThreadContexts. |
thread_local static ThreadContext *TLS; |