| 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
|
|
|