Index: src/IceGlobalContext.h |
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h |
index e3e0810cb02824af79d25403db76e50e9aedcc9b..3aa01e2495f4be14fec6ed56709c1500a1552592 100644 |
--- a/src/IceGlobalContext.h |
+++ b/src/IceGlobalContext.h |
@@ -31,6 +31,7 @@ namespace Ice { |
class ClFlags; |
class ConstantPool; |
+class EmitterWorkItem; |
class FuncSigType; |
// LockedPtr is a way to provide automatically locked access to some object. |
@@ -276,6 +277,12 @@ 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 |
@@ -289,6 +296,10 @@ public: |
// Notifies that no more work will be added to the work queue. |
void cfgQueueNotifyEnd() { CfgQ.notifyEnd(); } |
+ void emitQueueBlockingPush(EmitterWorkItem *Item); |
+ EmitterWorkItem *emitQueueBlockingPop(); |
+ void emitQueueNotifyEnd() { EmitQ.notifyEnd(); } |
+ |
void startWorkerThreads() { |
size_t NumWorkers = getFlags().NumTranslationThreads; |
auto Timers = getTimers(); |
@@ -300,18 +311,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. |
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 +356,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, |
@@ -391,6 +422,7 @@ private: |
RandomNumberGenerator RNG; // TODO(stichnot): Move into Cfg. |
std::unique_ptr<ELFObjectWriter> ObjectWriter; |
BoundedProducerConsumerQueue<Cfg> CfgQ; |
JF
2015/02/08 00:29:47
Maybe this should now be the OptimizationQ?
Jim Stichnoth
2015/02/08 17:11:23
Done.
|
+ BoundedProducerConsumerQueue<EmitterWorkItem> EmitQ; |
LockedPtr<ArenaAllocator<>> getAllocator() { |
return LockedPtr<ArenaAllocator<>>(&Allocator, &AllocLock); |
@@ -407,6 +439,7 @@ private: |
std::vector<ThreadContext *> AllThreadContexts; |
std::vector<std::thread> TranslationThreads; |
+ std::vector<std::thread> EmitterThreads; |
JF
2015/02/08 00:29:47
These could just be SmallVector or even std::array
Jim Stichnoth
2015/02/08 17:11:23
16 threads? I've forgotten how to count that low.
|
// Each thread has its own TLS pointer which is also held in |
// AllThreadContexts. |
ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS); |
@@ -471,6 +504,59 @@ private: |
GlobalContext *const Ctx; |
}; |
+class EmitterWorkItem { |
JF
2015/02/08 00:29:47
This is getting to be a pretty big file, it's prob
Jim Stichnoth
2015/02/08 17:11:23
Good idea, moved this into the new IceThreading.h,
|
+ EmitterWorkItem(const EmitterWorkItem &) = delete; |
+ EmitterWorkItem &operator=(const EmitterWorkItem &) = delete; |
JF
2015/02/08 00:29:47
EmitterWorkItem() = delete;
Jim Stichnoth
2015/02/08 17:11:23
Done.
|
+ |
+public: |
+ enum ItemKind { |
+ WI_Nop, // Placeholder to maintain sequence numbers in case there |
+ // is a translation error. |
+ WI_GlobalInits, // A list of global initializers. |
+ WI_Asm, // An already-assembled function that needs to be emitted, |
+ // either as low-level asm text or as an ELF binary. |
+ WI_Cfg // A Cfg that needs to be emitted as "readable" assembly. |
JF
2015/02/08 00:29:47
I'm not sure I get the different between asm and c
Jim Stichnoth
2015/02/08 17:11:23
Added more comments that hopefully clarify.
JF
2015/02/08 21:15:04
Yeah, though I'm wary of having a debugging featur
Jim Stichnoth
2015/02/10 07:51:46
I added a report_fatal_error() call to GlobalConte
|
+ }; |
+ // Constructor for a Nop work item. |
+ explicit EmitterWorkItem(uint32_t Seq) |
+ : Sequence(Seq), Kind(WI_Nop), GlobalInits(nullptr), Function(nullptr), |
+ RawFunc(nullptr) {} |
+ // Constructor for a GlobalInits work item. |
+ EmitterWorkItem(uint32_t Seq, VariableDeclarationList *D) |
+ : Sequence(Seq), Kind(WI_GlobalInits), GlobalInits(D), Function(nullptr), |
+ RawFunc(nullptr) {} |
+ // Constructor for an Asm work item. |
+ EmitterWorkItem(uint32_t Seq, Assembler *A) |
+ : Sequence(Seq), Kind(WI_Asm), GlobalInits(nullptr), Function(A), |
+ RawFunc(nullptr) {} |
+ // Constructor for a Cfg work item. |
+ EmitterWorkItem(uint32_t Seq, Cfg *F) |
+ : Sequence(Seq), Kind(WI_Cfg), GlobalInits(nullptr), Function(nullptr), |
+ RawFunc(F) {} |
+ uint32_t getSequenceNumber() const { return Sequence; } |
+ ItemKind getKind() const { return Kind; } |
+ VariableDeclarationList *getGlobalInits() const { |
+ assert(getKind() == WI_GlobalInits); |
+ return GlobalInits; |
+ } |
+ Assembler *getAsm() const { |
+ assert(getKind() == WI_Asm); |
+ return Function; |
+ } |
+ Cfg *getCfg() const { |
+ assert(getKind() == WI_Cfg); |
+ return RawFunc; |
+ } |
+ ~EmitterWorkItem(); |
JF
2015/02/08 00:29:47
Define inline, since this should do anything.
Jim Stichnoth
2015/02/08 17:11:23
Tried that originally, but it's getting into icky
JF
2015/02/08 21:15:04
Oh yeah, include order would do that, and unique_p
|
+ |
+private: |
+ const uint32_t Sequence; |
+ const ItemKind Kind; |
+ VariableDeclarationList *const GlobalInits; |
+ Assembler *const Function; |
+ Cfg *const RawFunc; |
JF
2015/02/08 00:29:47
3 x unique_ptr?
Jim Stichnoth
2015/02/10 07:51:46
Yeah, I think so, after Karl's CL lands...
|
+}; |
+ |
} // end of namespace Ice |
#endif // SUBZERO_SRC_ICEGLOBALCONTEXT_H |