Index: src/IceGlobalContext.cpp |
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp |
index 4324f97f442b08ec1731ad996e28898a277766a4..a03b1e95eeabbf4acb0951b744a15f3742b22f63 100644 |
--- a/src/IceGlobalContext.cpp |
+++ b/src/IceGlobalContext.cpp |
@@ -134,8 +134,10 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, |
: ConstPool(new ConstantPool()), ErrorStatus(), StrDump(OsDump), |
StrEmit(OsEmit), VMask(Mask), Arch(Arch), Opt(Opt), |
TestPrefix(TestPrefix), Flags(Flags), RNG(""), ObjectWriter(), |
- CfgQ(/*MaxSize=*/Flags.NumTranslationThreads, |
- /*Sequential=*/(Flags.NumTranslationThreads == 0)) { |
+ OptQ(/*Sequential=*/(Flags.NumTranslationThreads == 0), |
+ /*MaxSize=*/Flags.NumTranslationThreads), |
+ // EmitQ is allowed unlimited size. |
+ EmitQ(/*Sequential=*/(Flags.NumTranslationThreads == 0)) { |
// Make sure thread_local fields are properly initialized before any |
// accesses are made. Do this here instead of at the start of |
// main() so that all clients (e.g. unit tests) can benefit for |
@@ -162,7 +164,7 @@ GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit, |
} |
void GlobalContext::translateFunctions() { |
- while (std::unique_ptr<Cfg> Func = cfgQueueBlockingPop()) { |
+ while (std::unique_ptr<Cfg> Func = optQueueBlockingPop()) { |
// Install Func in TLS for Cfg-specific container allocators. |
Cfg::setCurrentCfg(Func.get()); |
// Reset per-function stats being accumulated in TLS. |
@@ -178,26 +180,147 @@ void GlobalContext::translateFunctions() { |
if (getFlags().DisableTranslation || |
!matchSymbolName(Func->getFunctionName(), getFlags().TranslateOnly)) { |
Func->dump(); |
+ Cfg::setCurrentCfg(nullptr); |
} else { |
Func->translate(); |
+ EmitterWorkItem *Item = nullptr; |
if (Func->hasError()) { |
getErrorStatus()->assign(EC_Translation); |
OstreamLocker L(this); |
getStrDump() << "ICE translation error: " << Func->getError() << "\n"; |
+ Item = new EmitterWorkItem(Func->getSequenceNumber()); |
} else { |
- if (getFlags().UseIntegratedAssembler) |
+ if (getFlags().UseIntegratedAssembler) { |
Func->emitIAS(); |
- else |
- Func->emit(); |
- // TODO(stichnot): actually add to emit queue |
+ // The Cfg has already emitted into the assembly buffer, so |
+ // stats have been fully collected into this thread's TLS. |
+ // Dump them before TLS is reset for the next Cfg. |
+ dumpStats(Func->getFunctionName()); |
+ Assembler *Asm = Func->releaseAssembler(); |
+ // Copy relevant fields into Asm before Func is deleted. |
+ Asm->setFunctionName(Func->getFunctionName()); |
+ Asm->setInternal(Func->getInternal()); |
+ Item = new EmitterWorkItem(Func->getSequenceNumber(), Asm); |
+ } else { |
+ // The Cfg has not been emitted yet, so stats are not ready |
+ // to be dumped. |
+ Item = new EmitterWorkItem(Func->getSequenceNumber(), Func.release()); |
+ } |
} |
- dumpStats(Func->getFunctionName()); |
+ Cfg::setCurrentCfg(nullptr); |
+ assert(Item); |
+ emitQueueBlockingPush(Item); |
} |
- Cfg::setCurrentCfg(nullptr); |
// The Cfg now gets deleted as Func goes out of scope. |
} |
} |
+namespace { |
+ |
+void lowerGlobals(GlobalContext *Ctx, |
+ VariableDeclarationList *VariableDeclarations, |
+ TargetDataLowering *DataLowering) { |
+ TimerMarker T(TimerStack::TT_emitGlobalInitializers, Ctx); |
+ bool DisableTranslation = Ctx->getFlags().DisableTranslation; |
+ const bool DumpGlobalVariables = |
+ ALLOW_DUMP && Ctx->getVerbose() && Ctx->getFlags().VerboseFocusOn.empty(); |
+ if (Ctx->getFlags().UseELFWriter) { |
+ // Dump all globals if requested, but don't interleave w/ emission. |
+ if (DumpGlobalVariables) { |
+ OstreamLocker L(Ctx); |
+ Ostream &Stream = Ctx->getStrDump(); |
+ for (const Ice::VariableDeclaration *Global : *VariableDeclarations) { |
+ Global->dump(Ctx, Stream); |
+ } |
+ } |
+ DataLowering->lowerGlobalsELF(*VariableDeclarations); |
+ } else { |
+ const IceString &TranslateOnly = Ctx->getFlags().TranslateOnly; |
+ OstreamLocker L(Ctx); |
+ Ostream &Stream = Ctx->getStrDump(); |
+ for (const Ice::VariableDeclaration *Global : *VariableDeclarations) { |
+ // Interleave dump output w/ emit output. |
+ if (DumpGlobalVariables) |
+ Global->dump(Ctx, Stream); |
+ if (!DisableTranslation && |
+ GlobalContext::matchSymbolName(Global->getName(), TranslateOnly)) |
+ DataLowering->lowerGlobal(*Global); |
+ } |
+ } |
+} |
+ |
+// Ensure Pending is large enough that Pending[Index] is valid. |
+void resizePending(std::vector<EmitterWorkItem *> &Pending, uint32_t Index) { |
+ if (Index >= Pending.size()) |
+ Pending.resize(Index + 1); |
+} |
+ |
+} // end of anonymous namespace |
+ |
+void GlobalContext::emitItems() { |
+ const bool Threaded = getFlags().NumTranslationThreads; |
+ // Pending is a vector containing the reassembled, ordered list of |
+ // work items. When we're ready for the next item, we first check |
+ // whether it's in the Pending list. If not, we take an item from |
+ // the work queue, and if it's not the item we're waiting for, we |
+ // insert it into Pending and repeat. The work item is deleted |
+ // after it is processed. |
+ std::vector<EmitterWorkItem *> Pending; |
+ uint32_t DesiredSequenceNumber = getFirstSequenceNumber(); |
+ while (true) { |
+ resizePending(Pending, DesiredSequenceNumber); |
+ // See if Pending contains DesiredSequenceNumber. |
+ EmitterWorkItem *Item = Pending[DesiredSequenceNumber]; |
+ if (Item == nullptr) |
+ Item = emitQueueBlockingPop(); |
+ if (Item == nullptr) |
+ return; |
+ uint32_t ItemSeq = Item->getSequenceNumber(); |
+ if (Threaded && ItemSeq != DesiredSequenceNumber) { |
+ resizePending(Pending, ItemSeq); |
+ Pending[ItemSeq] = Item; |
+ continue; |
+ } |
+ |
+ ++DesiredSequenceNumber; |
+ switch (Item->getKind()) { |
+ case EmitterWorkItem::WI_Nop: |
+ break; |
+ case EmitterWorkItem::WI_GlobalInits: { |
+ lowerGlobals(this, Item->getGlobalInits(), |
+ TargetDataLowering::createLowering(this).get()); |
+ } break; |
+ case EmitterWorkItem::WI_Asm: { |
+ Assembler *Asm = Item->getAsm(); |
+ Asm->alignFunction(); |
+ IceString MangledName = mangleName(Asm->getFunctionName()); |
+ if (getFlags().UseELFWriter) { |
+ getObjectWriter()->writeFunctionCode(MangledName, Asm->getInternal(), |
+ Asm); |
+ } else { |
+ OstreamLocker L(this); |
+ Cfg::emitTextHeader(MangledName, this, Asm); |
+ Asm->emitIASBytes(this); |
+ } |
+ } break; |
+ case EmitterWorkItem::WI_Cfg: { |
+ if (!ALLOW_DUMP) |
+ llvm::report_fatal_error("WI_Cfg work item created inappropriately"); |
+ assert(!getFlags().UseIntegratedAssembler); |
+ Cfg *Func = Item->getCfg(); |
+ // Unfortunately, we have to temporarily install the Cfg in TLS |
+ // because Variable::asType() uses the allocator to create the |
+ // differently-typed copy. |
+ Cfg::setCurrentCfg(Func); |
+ Func->emit(); |
+ Cfg::setCurrentCfg(nullptr); |
+ dumpStats(Func->getFunctionName()); |
+ } break; |
+ } |
+ delete Item; |
+ } |
+} |
+ |
// Scan a string for S[0-9A-Z]*_ patterns and replace them with |
// S<num>_ where <num> is the next base-36 value. If a type name |
// legitimately contains that pattern, then the substitution will be |
@@ -548,17 +671,31 @@ void GlobalContext::setTimerName(TimerStackIdT StackID, |
Timers->at(StackID).setName(NewName); |
} |
-// Note: cfgQueueBlockingPush and cfgQueueBlockingPop use unique_ptr |
+// Note: optQueueBlockingPush and optQueueBlockingPop use unique_ptr |
// at the interface to take and transfer ownership, but they |
// internally store the raw Cfg pointer in the work queue. This |
// allows e.g. future queue optimizations such as the use of atomics |
// to modify queue elements. |
-void GlobalContext::cfgQueueBlockingPush(std::unique_ptr<Cfg> Func) { |
- CfgQ.blockingPush(Func.release()); |
+void GlobalContext::optQueueBlockingPush(std::unique_ptr<Cfg> Func) { |
+ assert(Func); |
+ OptQ.blockingPush(Func.release()); |
+ if (getFlags().NumTranslationThreads == 0) |
+ translateFunctions(); |
} |
-std::unique_ptr<Cfg> GlobalContext::cfgQueueBlockingPop() { |
- return std::unique_ptr<Cfg>(CfgQ.blockingPop()); |
+std::unique_ptr<Cfg> GlobalContext::optQueueBlockingPop() { |
+ return std::unique_ptr<Cfg>(OptQ.blockingPop()); |
+} |
+ |
+void GlobalContext::emitQueueBlockingPush(EmitterWorkItem *Item) { |
+ assert(Item); |
+ EmitQ.blockingPush(Item); |
+ if (getFlags().NumTranslationThreads == 0) |
+ emitItems(); |
+} |
+ |
+EmitterWorkItem *GlobalContext::emitQueueBlockingPop() { |
+ return EmitQ.blockingPop(); |
} |
void GlobalContext::dumpStats(const IceString &Name, bool Final) { |
@@ -603,6 +740,15 @@ void TimerMarker::pushCfg(const Cfg *Func) { |
Ctx->pushTimer(ID, StackID); |
} |
+EmitterWorkItem::~EmitterWorkItem() { |
+ // TODO(kschimpf,stichnot): Delete GlobalInits once it is managed in |
+ // the parser as a unique_ptr. |
+ |
+ // delete GlobalInits; |
+ delete Function; |
+ delete RawFunc; |
+} |
+ |
ICE_TLS_DEFINE_FIELD(GlobalContext::ThreadContext *, GlobalContext, TLS); |
} // end of namespace Ice |