Index: src/IceGlobalContext.h |
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h |
index 280bbd0e399e7ea15965ed4074fbd98e38bac500..f7f5a5afe6168c469f788091feb094d4a5de01c1 100644 |
--- a/src/IceGlobalContext.h |
+++ b/src/IceGlobalContext.h |
@@ -23,6 +23,7 @@ |
#include "IceClFlags.h" |
#include "IceIntrinsics.h" |
#include "IceRNG.h" |
+#include "IceThreading.h" |
#include "IceTimerTree.h" |
#include "IceTypes.h" |
#include "IceUtils.h" |
@@ -31,6 +32,7 @@ namespace Ice { |
class ClFlags; |
class ConstantPool; |
+class EmitterWorkItem; |
class FuncSigType; |
// LockedPtr is a way to provide automatically locked access to some object. |
@@ -276,18 +278,28 @@ public: |
void resetTimer(TimerStackIdT StackID); |
void setTimerName(TimerStackIdT StackID, const IceString &NewName); |
+ // This is the first work item sequence number that the parser |
+ // produces, and correspondingly the first sequence number that the |
+ // emitter thread will wait for. Start numbering at 1 to leave room |
+ // for a sentinel, in case e.g. we wish to inject items with a |
+ // special sequence number that may be executed out of order. |
+ static uint32_t getFirstSequenceNumber() { return 1; } |
// 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 cfgQueueBlockingPush(std::unique_ptr<Cfg> Func); |
+ void optQueueBlockingPush(std::unique_ptr<Cfg> 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 - the queue is empty and either end() has been |
// called or the Sequential flag was set. |
- std::unique_ptr<Cfg> cfgQueueBlockingPop(); |
+ std::unique_ptr<Cfg> optQueueBlockingPop(); |
// Notifies that no more work will be added to the work queue. |
- void cfgQueueNotifyEnd() { CfgQ.notifyEnd(); } |
+ void optQueueNotifyEnd() { OptQ.notifyEnd(); } |
+ |
+ void emitQueueBlockingPush(EmitterWorkItem *Item); |
+ EmitterWorkItem *emitQueueBlockingPop(); |
+ void emitQueueNotifyEnd() { EmitQ.notifyEnd(); } |
void startWorkerThreads() { |
size_t NumWorkers = getFlags().getNumTranslationThreads(); |
@@ -300,18 +312,29 @@ public: |
&GlobalContext::translateFunctionsWrapper, this, WorkerTLS)); |
} |
if (NumWorkers) { |
- // TODO(stichnot): start a new thread for the emitter queue worker. |
+ ThreadContext *WorkerTLS = new ThreadContext(); |
+ Timers->initInto(WorkerTLS->Timers); |
+ AllThreadContexts.push_back(WorkerTLS); |
+ EmitterThreads.push_back( |
+ std::thread(&GlobalContext::emitterWrapper, this, WorkerTLS)); |
} |
} |
void waitForWorkerThreads() { |
- cfgQueueNotifyEnd(); |
- // TODO(stichnot): call end() on the emitter work queue. |
+ optQueueNotifyEnd(); |
for (std::thread &Worker : TranslationThreads) { |
Worker.join(); |
} |
TranslationThreads.clear(); |
- // TODO(stichnot): join the emitter thread. |
+ |
+ // Only notify the emit queue to end after all the translation |
+ // threads have ended. |
+ emitQueueNotifyEnd(); |
+ for (std::thread &Worker : EmitterThreads) { |
+ Worker.join(); |
+ } |
+ EmitterThreads.clear(); |
+ |
if (ALLOW_DUMP) { |
auto Timers = getTimers(); |
for (ThreadContext *TLS : AllThreadContexts) |
@@ -334,6 +357,15 @@ public: |
// Translate functions from the Cfg queue until the queue is empty. |
void translateFunctions(); |
+ // Emitter thread startup routine. |
+ void emitterWrapper(ThreadContext *MyTLS) { |
+ ICE_TLS_SET_FIELD(TLS, MyTLS); |
+ emitItems(); |
+ } |
+ // Emit functions and global initializers from the emitter queue |
+ // until the queue is empty. |
+ void emitItems(); |
+ |
// Utility function to match a symbol name against a match string. |
// This is used in a few cases where we want to take some action on |
// a particular function or symbol based on a command-line argument, |
@@ -390,7 +422,8 @@ private: |
const ClFlags &Flags; |
RandomNumberGenerator RNG; // TODO(stichnot): Move into Cfg. |
std::unique_ptr<ELFObjectWriter> ObjectWriter; |
- BoundedProducerConsumerQueue<Cfg> CfgQ; |
+ BoundedProducerConsumerQueue<Cfg> OptQ; |
+ BoundedProducerConsumerQueue<EmitterWorkItem> EmitQ; |
LockedPtr<ArenaAllocator<>> getAllocator() { |
return LockedPtr<ArenaAllocator<>>(&Allocator, &AllocLock); |
@@ -405,8 +438,9 @@ private: |
return LockedPtr<TimerList>(&Timers, &TimerLock); |
} |
- std::vector<ThreadContext *> AllThreadContexts; |
- std::vector<std::thread> TranslationThreads; |
+ llvm::SmallVector<ThreadContext *, 128> AllThreadContexts; |
+ llvm::SmallVector<std::thread, 128> TranslationThreads; |
+ llvm::SmallVector<std::thread, 128> EmitterThreads; |
// Each thread has its own TLS pointer which is also held in |
// AllThreadContexts. |
ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS); |