| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "Backend.h"
- #include "SccLiveness.h"
- // Build SCC liveness. SCC stands for Strongly Connected Components. It's a simple
- // conservative algorithm which has the advantage of being O(N). A simple forward walk
- // of the IR looks at the first and last use of each symbols and creates the lifetimes.
- // The code assumes the blocks are in R-DFO order to start with. For loops, the lifetimes
- // are simply extended to cover the whole loop.
- //
- // The disadvantages are:
- // o Separate lifetimes of a given symbol are not separated
- // o Very conservative in loops, which is where we'd like precise info...
- //
- // Single-def symbols do not have the first issue. We also try to make up for number 2
- // by not extending the lifetime of symbols if the first def and the last use are in
- // same loop.
- //
- // The code builds a list of lifetimes sorted in start order.
- // We actually build the list in reverse start order, and then reverse it.
- void
- SCCLiveness::Build()
- {
- // First, lets number each instruction to get an ordering.
- // Note that we assume the blocks are in RDFO.
- // NOTE: Currently the DoInterruptProbe pass will number the instructions. If it has,
- // then the numbering here is not necessary. But there should be no phase between the two
- // that can invalidate the numbering.
- if (!this->func->HasInstrNumber())
- {
- this->func->NumberInstrs();
- }
- IR::LabelInstr *lastLabelInstr = nullptr;
- FOREACH_INSTR_IN_FUNC_EDITING(instr, instrNext, this->func)
- {
- IR::Opnd *dst, *src1, *src2;
- uint32 instrNum = instr->GetNumber();
- // End of loop?
- if (this->curLoop && instrNum >= this->curLoop->regAlloc.loopEnd)
- {
- AssertMsg(this->loopNest > 0, "Loop nest is messed up");
- AssertMsg(instr->IsBranchInstr(), "Loop tail should be a branchInstr");
- AssertMsg(instr->AsBranchInstr()->IsLoopTail(this->func), "Loop tail not marked correctly");
- FOREACH_SLIST_ENTRY(Lifetime *, lifetime, this->curLoop->regAlloc.extendedLifetime)
- {
- if (this->curLoop->regAlloc.hasNonOpHelperCall)
- {
- lifetime->isLiveAcrossUserCalls = true;
- }
- if (this->curLoop->regAlloc.hasCall)
- {
- lifetime->isLiveAcrossCalls = true;
- }
- if(lifetime->end == this->curLoop->regAlloc.loopEnd)
- {
- lifetime->totalOpHelperLengthByEnd = this->totalOpHelperFullVisitedLength + CurrentOpHelperVisitedLength(instr);
- }
- }
- NEXT_SLIST_ENTRY;
- this->curLoop->regAlloc.helperLength = this->totalOpHelperFullVisitedLength + CurrentOpHelperVisitedLength(instr);
- while (this->curLoop && instrNum >= this->curLoop->regAlloc.loopEnd)
- {
- this->curLoop = this->curLoop->parent;
- this->loopNest--;
- }
- }
- if (instr->HasBailOutInfo())
- {
- // At this point, the bailout should be lowered to a CALL to BailOut
- #if DBG
- Assert(LowererMD::IsCall(instr));
- IR::Opnd * helperOpnd = nullptr;
- if (instr->GetSrc1()->IsHelperCallOpnd())
- {
- helperOpnd = instr->GetSrc1();
- }
- else if (instr->GetSrc1()->AsRegOpnd()->m_sym)
- {
- Assert(instr->GetSrc1()->AsRegOpnd()->m_sym->m_instrDef);
- helperOpnd = instr->GetSrc1()->AsRegOpnd()->m_sym->m_instrDef->GetSrc1();
- }
- Assert(!helperOpnd || BailOutInfo::IsBailOutHelper(helperOpnd->AsHelperCallOpnd()->m_fnHelper));
- #endif
- ProcessBailOutUses(instr);
- }
- if (instr->m_opcode == Js::OpCode::InlineeEnd && instr->m_func->m_hasInlineArgsOpt)
- {
- instr->m_func->frameInfo->IterateSyms([=](StackSym* argSym)
- {
- this->ProcessStackSymUse(argSym, instr);
- });
- }
- // Process srcs
- src1 = instr->GetSrc1();
- if (src1)
- {
- this->ProcessSrc(src1, instr);
- src2 = instr->GetSrc2();
- if (src2)
- {
- this->ProcessSrc(src2, instr);
- }
- }
- // Keep track of the last call instruction number to find out whether a lifetime crosses a call
- // Do not count call to bailout which exits anyways
- if (LowererMD::IsCall(instr) && !instr->HasBailOutInfo())
- {
- if (this->lastOpHelperLabel == nullptr)
- {
- // Catch only user calls (non op helper calls)
- this->lastNonOpHelperCall = instr->GetNumber();
- if (this->curLoop)
- {
- this->curLoop->regAlloc.hasNonOpHelperCall = true;
- }
- }
- // Catch all calls
- this->lastCall = instr->GetNumber();
- if (this->curLoop)
- {
- this->curLoop->regAlloc.hasCall = true;
- }
- }
- // Process dst
- dst = instr->GetDst();
- if (dst)
- {
- this->ProcessDst(dst, instr);
- }
- if (instr->IsLabelInstr())
- {
- IR::LabelInstr * labelInstr = instr->AsLabelInstr();
- if (labelInstr->IsUnreferenced())
- {
- // Unreferenced labels can potentially be removed. See if the label tells
- // us we're transitioning between a helper and non-helper block.
- if (labelInstr->isOpHelper == (this->lastOpHelperLabel != nullptr)
- && lastLabelInstr && labelInstr->isOpHelper == lastLabelInstr->isOpHelper)
- {
- // No such transition. Remove the label.
- Assert(!labelInstr->GetRegion() || labelInstr->GetRegion() == this->curRegion);
- labelInstr->Remove();
- continue;
- }
- }
- lastLabelInstr = labelInstr;
- Region * region = labelInstr->GetRegion();
- if (region != nullptr)
- {
- if (this->curRegion && this->curRegion != region)
- {
- this->curRegion->SetEnd(labelInstr->m_prev);
- }
- if (region->GetStart() == nullptr)
- {
- region->SetStart(labelInstr);
- }
- region->SetEnd(nullptr);
- this->curRegion = region;
- }
- else
- {
- labelInstr->SetRegion(this->curRegion);
- }
- // Look for start of loop
- if (labelInstr->m_isLoopTop)
- {
- this->loopNest++; // used in spill cost calculation.
- IR::LabelInstr * labelInstr = instr->AsLabelInstr();
- uint32 lastBranchNum = 0;
- IR::BranchInstr *lastBranchInstr = nullptr;
- FOREACH_SLISTCOUNTED_ENTRY(IR::BranchInstr *, ref, &labelInstr->labelRefs)
- {
- if (ref->GetNumber() > lastBranchNum)
- {
- lastBranchInstr = ref;
- lastBranchNum = lastBranchInstr->GetNumber();
- }
- }
- NEXT_SLISTCOUNTED_ENTRY;
- AssertMsg(instrNum < lastBranchNum, "Didn't find back edge...");
- AssertMsg(lastBranchInstr->IsLoopTail(this->func), "Loop tail not marked properly");
- Loop * loop = labelInstr->GetLoop();
- loop->parent = this->curLoop;
- this->curLoop = loop;
- loop->regAlloc.loopStart = instrNum;
- loop->regAlloc.loopEnd = lastBranchNum;
- // Tail duplication can result in cases in which an outer loop lexically ends before the inner loop.
- // The register allocator could then thrash in the inner loop registers used for a live-on-back-edge
- // sym on the outer loop. To prevent this, we need to mark the end of the outer loop as the end of the
- // inner loop and update the lifetimes already extended in the outer loop in keeping with this change.
- for (Loop* parentLoop = loop->parent; parentLoop != nullptr; parentLoop = parentLoop->parent)
- {
- if (parentLoop->regAlloc.loopEnd < loop->regAlloc.loopEnd)
- {
- // We need to go over extended lifetimes in outer loops to update the lifetimes of symbols that might
- // have had their lifetime extended to the outer loop end (which is before the current loop end) and
- // may not have any uses in the current loop to extend their lifetimes to the current loop end.
- FOREACH_SLIST_ENTRY(Lifetime *, lifetime, parentLoop->regAlloc.extendedLifetime)
- {
- if (lifetime->end == parentLoop->regAlloc.loopEnd)
- {
- lifetime->end = loop->regAlloc.loopEnd;
- }
- }
- NEXT_SLIST_ENTRY;
- parentLoop->regAlloc.loopEnd = loop->regAlloc.loopEnd;
- }
- }
- loop->regAlloc.extendedLifetime = JitAnew(this->tempAlloc, SList<Lifetime *>, this->tempAlloc);
- loop->regAlloc.hasNonOpHelperCall = false;
- loop->regAlloc.hasCall = false;
- loop->regAlloc.hasAirLock = false;
- }
- // track whether we are in a helper block or not
- if (this->lastOpHelperLabel != nullptr)
- {
- this->EndOpHelper(labelInstr);
- }
- if (labelInstr->isOpHelper && !PHASE_OFF(Js::OpHelperRegOptPhase, this->func))
- {
- this->lastOpHelperLabel = labelInstr;
- }
- }
- else if (instr->IsBranchInstr() && !instr->AsBranchInstr()->IsMultiBranch())
- {
- IR::LabelInstr * branchTarget = instr->AsBranchInstr()->GetTarget();
- Js::OpCode brOpcode = instr->m_opcode;
- if (branchTarget->GetRegion() == nullptr && this->func->HasTry())
- {
- Assert(brOpcode != Js::OpCode::Leave && brOpcode != Js::OpCode::TryCatch && brOpcode != Js::OpCode::TryFinally);
- branchTarget->SetRegion(this->curRegion);
- }
- }
- if (this->lastOpHelperLabel != nullptr && instr->IsBranchInstr())
- {
- IR::LabelInstr *targetLabel = instr->AsBranchInstr()->GetTarget();
- if (targetLabel->isOpHelper && instr->AsBranchInstr()->IsConditional())
- {
- // If we have:
- // L1: [helper]
- // CMP
- // JCC helperLabel
- // code
- // Insert a helper label before 'code' to mark this is also helper code.
- IR::Instr *branchInstrNext = instr->GetNextRealInstrOrLabel();
- if (!branchInstrNext->IsLabelInstr())
- {
- instrNext = IR::LabelInstr::New(Js::OpCode::Label, instr->m_func, true);
- instr->InsertAfter(instrNext);
- instrNext->CopyNumber(instrNext->m_next);
- }
- }
- this->EndOpHelper(instr);
- }
- }NEXT_INSTR_IN_FUNC_EDITING;
- if (this->func->HasTry())
- {
- #if DBG
- FOREACH_INSTR_IN_FUNC(instr, this->func)
- {
- if (instr->IsLabelInstr())
- {
- Assert(instr->AsLabelInstr()->GetRegion() != nullptr);
- }
- }
- NEXT_INSTR_IN_FUNC
- #endif
- AssertMsg(this->curRegion, "Function with try but no regions?");
- AssertMsg(this->curRegion->GetStart() && !this->curRegion->GetEnd(), "Current region not active?");
- // Check for lifetimes that have been extended such that they now span multiple regions.
- this->curRegion->SetEnd(this->func->m_exitInstr);
- if (this->func->HasTry() && !this->func->DoOptimizeTryCatch())
- {
- FOREACH_SLIST_ENTRY(Lifetime *, lifetime, &this->lifetimeList)
- {
- if (lifetime->dontAllocate)
- {
- continue;
- }
- if (lifetime->start < lifetime->region->GetStart()->GetNumber() ||
- lifetime->end > lifetime->region->GetEnd()->GetNumber())
- {
- lifetime->dontAllocate = true;
- }
- }
- NEXT_SLIST_ENTRY;
- }
- }
- AssertMsg(this->loopNest == 0, "LoopNest is messed up");
- // The list is built in reverse order. Let's flip it in increasing start order.
- this->lifetimeList.Reverse();
- this->opHelperBlockList.Reverse();
- #if DBG_DUMP
- if (PHASE_DUMP(Js::LivenessPhase, this->func))
- {
- this->Dump();
- }
- #endif
- }
- void
- SCCLiveness::EndOpHelper(IR::Instr * instr)
- {
- Assert(this->lastOpHelperLabel != nullptr);
- OpHelperBlock * opHelperBlock = this->opHelperBlockList.PrependNode(this->tempAlloc);
- Assert(opHelperBlock != nullptr);
- opHelperBlock->opHelperLabel = this->lastOpHelperLabel;
- opHelperBlock->opHelperEndInstr = instr;
- this->totalOpHelperFullVisitedLength += opHelperBlock->Length();
- this->lastOpHelperLabel = nullptr;
- }
- // SCCLiveness::ProcessSrc
- void
- SCCLiveness::ProcessSrc(IR::Opnd *src, IR::Instr *instr)
- {
- if (src->IsRegOpnd())
- {
- this->ProcessRegUse(src->AsRegOpnd(), instr);
- }
- else if (src->IsIndirOpnd())
- {
- IR::IndirOpnd *indirOpnd = src->AsIndirOpnd();
- AssertMsg(indirOpnd->GetBaseOpnd(), "Indir should have a base...");
- if (!this->FoldIndir(instr, indirOpnd))
- {
- this->ProcessRegUse(indirOpnd->GetBaseOpnd(), instr);
- if (indirOpnd->GetIndexOpnd())
- {
- this->ProcessRegUse(indirOpnd->GetIndexOpnd(), instr);
- }
- }
- }
- }
- // SCCLiveness::ProcessDst
- void
- SCCLiveness::ProcessDst(IR::Opnd *dst, IR::Instr *instr)
- {
- if (dst->IsIndirOpnd())
- {
- // Indir regs are really uses
- IR::IndirOpnd *indirOpnd = dst->AsIndirOpnd();
- AssertMsg(indirOpnd->GetBaseOpnd(), "Indir should have a base...");
- if (!this->FoldIndir(instr, indirOpnd))
- {
- this->ProcessRegUse(indirOpnd->GetBaseOpnd(), instr);
- if (indirOpnd->GetIndexOpnd())
- {
- this->ProcessRegUse(indirOpnd->GetIndexOpnd(), instr);
- }
- }
- }
- else if (dst->IsRegOpnd())
- {
- this->ProcessRegDef(dst->AsRegOpnd(), instr);
- }
- }
- void
- SCCLiveness::ProcessBailOutUses(IR::Instr * instr)
- {
- BailOutInfo * bailOutInfo = instr->GetBailOutInfo();
- FOREACH_BITSET_IN_SPARSEBV(id, bailOutInfo->byteCodeUpwardExposedUsed)
- {
- StackSym * stackSym = this->func->m_symTable->FindStackSym(id);
- Assert(stackSym != nullptr);
- ProcessStackSymUse(stackSym, instr);
- }
- NEXT_BITSET_IN_SPARSEBV;
- FOREACH_SLISTBASE_ENTRY(CopyPropSyms, copyPropSyms, &bailOutInfo->usedCapturedValues.copyPropSyms)
- {
- ProcessStackSymUse(copyPropSyms.Value(), instr);
- }
- NEXT_SLISTBASE_ENTRY;
- bailOutInfo->IterateArgOutSyms([=] (uint, uint, StackSym* sym) {
- if(!sym->IsArgSlotSym() && sym->m_isBailOutReferenced)
- {
- ProcessStackSymUse(sym, instr);
- }
- });
- if(bailOutInfo->branchConditionOpnd)
- {
- ProcessSrc(bailOutInfo->branchConditionOpnd, instr);
- }
- // BailOnNoProfile might have caused the deletion of a cloned InlineeEnd. As a result, argument
- // lifetimes wouldn't have been extended beyond the bailout point (InlineeEnd extends the lifetimes)
- // Extend argument lifetimes up to the bail out point to allow LinearScan::SpillInlineeArgs to spill
- // inlinee args.
- if ((instr->GetBailOutKind() == IR::BailOutOnNoProfile) && !instr->m_func->IsTopFunc())
- {
- Func * inlinee = instr->m_func;
- while (!inlinee->IsTopFunc())
- {
- if (inlinee->m_hasInlineArgsOpt && inlinee->frameInfo->isRecorded)
- {
- inlinee->frameInfo->IterateSyms([=](StackSym* argSym)
- {
- this->ProcessStackSymUse(argSym, instr);
- });
- inlinee = inlinee->GetParentFunc();
- }
- else
- {
- // if an inlinee's arguments haven't been optimized away, it's ancestors' shouldn't have been too.
- break;
- }
- }
- }
- }
- void
- SCCLiveness::ProcessStackSymUse(StackSym * stackSym, IR::Instr * instr, int usageSize)
- {
- Lifetime * lifetime = stackSym->scratch.linearScan.lifetime;
- if (lifetime == nullptr)
- {
- #if DBG
- wchar_t debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(L"Function: %s (%s) ", this->func->GetJnFunction()->GetDisplayName(), this->func->GetJnFunction()->GetDebugNumberSet(debugStringBuffer));
- Output::Print(L"Reg: ");
- stackSym->Dump();
- Output::Print(L"\n");
- Output::Flush();
- #endif
- AnalysisAssertMsg(UNREACHED, "Uninitialized reg?");
- }
- else
- {
- if (lifetime->region != this->curRegion && !this->func->DoOptimizeTryCatch())
- {
- lifetime->dontAllocate = true;
- }
- ExtendLifetime(lifetime, instr);
- }
- lifetime->AddToUseCount(LinearScan::GetUseSpillCost(this->loopNest, (this->lastOpHelperLabel != nullptr)), this->curLoop, this->func);
- if (lifetime->start < this->lastCall)
- {
- lifetime->isLiveAcrossCalls = true;
- }
- if (lifetime->start < this->lastNonOpHelperCall)
- {
- lifetime->isLiveAcrossUserCalls = true;
- }
- lifetime->isDeadStore = false;
- lifetime->intUsageBv.Set(usageSize);
- }
- // SCCLiveness::ProcessRegUse
- void
- SCCLiveness::ProcessRegUse(IR::RegOpnd *regUse, IR::Instr *instr)
- {
- StackSym * stackSym = regUse->m_sym;
- if (stackSym == nullptr)
- {
- return;
- }
- ProcessStackSymUse(stackSym, instr, TySize[regUse->GetType()]);
- }
- // SCCLiveness::ProcessRegDef
- void
- SCCLiveness::ProcessRegDef(IR::RegOpnd *regDef, IR::Instr *instr)
- {
- StackSym * stackSym = regDef->m_sym;
- // PhysReg
- if (stackSym == nullptr || regDef->GetReg() != RegNOREG)
- {
- IR::Opnd *src = instr->GetSrc1();
- // If this symbol is assigned to a physical register, let's tell the register
- // allocator to prefer assigning that register to the lifetime.
- //
- // Note: this only pays off if this is the last-use of the symbol, but
- // unfortunately we don't have a way to tell that currently...
- if (LowererMD::IsAssign(instr) && src->IsRegOpnd() && src->AsRegOpnd()->m_sym)
- {
- StackSym *srcSym = src->AsRegOpnd()->m_sym;
- srcSym->scratch.linearScan.lifetime->regPreference.Set(regDef->GetReg());
- }
- // This physreg doesn't have a lifetime, just return.
- if (stackSym == nullptr)
- {
- return;
- }
- }
- // Arg slot sym can be in a RegOpnd for param passed via registers
- // Skip creating a lifetime for those.
- if (stackSym->IsArgSlotSym())
- {
- return;
- }
- // We'll extend the lifetime only if there are uses in a different loop region
- // from one of the defs.
- Lifetime * lifetime = stackSym->scratch.linearScan.lifetime;
- if (lifetime == nullptr)
- {
- lifetime = this->InsertLifetime(stackSym, regDef->GetReg(), instr);
- lifetime->region = this->curRegion;
- lifetime->isFloat = regDef->IsFloat();
- lifetime->isSimd128F4 = regDef->IsSimd128F4();
- lifetime->isSimd128I4 = regDef->IsSimd128I4();
- lifetime->isSimd128D2 = regDef->IsSimd128D2();
- }
- else
- {
- AssertMsg(lifetime->start <= instr->GetNumber(), "Lifetime start not set correctly");
- ExtendLifetime(lifetime, instr);
- if (lifetime->region != this->curRegion && !this->func->DoOptimizeTryCatch())
- {
- lifetime->dontAllocate = true;
- }
- }
- lifetime->AddToUseCount(LinearScan::GetUseSpillCost(this->loopNest, (this->lastOpHelperLabel != nullptr)), this->curLoop, this->func);
- lifetime->intUsageBv.Set(TySize[regDef->GetType()]);
- }
- // SCCLiveness::ExtendLifetime
- // Manages extend lifetimes to the end of loops if the corresponding symbol
- // is live on the back edge of the loop
- void
- SCCLiveness::ExtendLifetime(Lifetime *lifetime, IR::Instr *instr)
- {
- AssertMsg(lifetime != nullptr, "Lifetime not provided");
- AssertMsg(lifetime->sym != nullptr, "Lifetime has no symbol");
- Assert(this->extendedLifetimesLoopList->Empty());
- // Find the loop that we need to extend the lifetime to
- StackSym * sym = lifetime->sym;
- Loop * loop = this->curLoop;
- uint32 extendedLifetimeStart = lifetime->start;
- uint32 extendedLifetimeEnd = lifetime->end;
- bool isLiveOnBackEdge = false;
- bool loopAddedToList = false;
- while (loop)
- {
- if (loop->regAlloc.liveOnBackEdgeSyms->Test(sym->m_id))
- {
- isLiveOnBackEdge = true;
- if (loop->regAlloc.loopStart < extendedLifetimeStart)
- {
- extendedLifetimeStart = loop->regAlloc.loopStart;
- this->extendedLifetimesLoopList->Prepend(this->tempAlloc, loop);
- loopAddedToList = true;
- }
- if (loop->regAlloc.loopEnd > extendedLifetimeEnd)
- {
- extendedLifetimeEnd = loop->regAlloc.loopEnd;
- if (!loopAddedToList)
- {
- this->extendedLifetimesLoopList->Prepend(this->tempAlloc, loop);
- }
- }
- }
- loop = loop->parent;
- loopAddedToList = false;
- }
- if (!isLiveOnBackEdge)
- {
- // Don't extend lifetime to loop boundary if the use are not live on back edge
- // Note: the above loop doesn't detect a reg that is live on an outer back edge
- // but not an inner one, so we can't assume here that the lifetime hasn't been extended
- // past the current instruction.
- if (lifetime->end < instr->GetNumber())
- {
- lifetime->end = instr->GetNumber();
- lifetime->totalOpHelperLengthByEnd = this->totalOpHelperFullVisitedLength + CurrentOpHelperVisitedLength(instr);
- }
- }
- else
- {
- // extend lifetime to the outer most loop boundary that have the symbol live on back edge.
- bool isLifetimeExtended = false;
- if (lifetime->start > extendedLifetimeStart)
- {
- isLifetimeExtended = true;
- lifetime->start = extendedLifetimeStart;
- }
- if (lifetime->end < extendedLifetimeEnd)
- {
- isLifetimeExtended = true;
- lifetime->end = extendedLifetimeEnd;
- // The total op helper length by the end of this lifetime will be updated once we reach the loop tail
- }
- if (isLifetimeExtended)
- {
- // Keep track of the lifetime extended for this loop so we can update the call bits
- FOREACH_SLISTBASE_ENTRY(Loop *, loop, this->extendedLifetimesLoopList)
- {
- loop->regAlloc.extendedLifetime->Prepend(lifetime);
- }
- NEXT_SLISTBASE_ENTRY
- }
- AssertMsg(lifetime->end > instr->GetNumber(), "Lifetime end not set correctly");
- }
- this->extendedLifetimesLoopList->Clear(this->tempAlloc);
- }
- // SCCLiveness::InsertLifetime
- // Insert a new lifetime in the list of lifetime. The lifetime are inserted
- // in the reverse order of the lifetime starts.
- Lifetime *
- SCCLiveness::InsertLifetime(StackSym *stackSym, RegNum reg, IR::Instr *const currentInstr)
- {
- const uint start = currentInstr->GetNumber(), end = start;
- Lifetime * lifetime = JitAnew(tempAlloc, Lifetime, tempAlloc, stackSym, reg, start, end, this->func);
- lifetime->totalOpHelperLengthByEnd = this->totalOpHelperFullVisitedLength + CurrentOpHelperVisitedLength(currentInstr);
- // Find insertion point
- // This looks like a search, but we should almost exit on the first iteration, except
- // when we have loops and some lifetimes where extended.
- FOREACH_SLIST_ENTRY_EDITING(Lifetime *, lifetime, &this->lifetimeList, iter)
- {
- if (lifetime->start <= start)
- {
- break;
- }
- }
- NEXT_SLIST_ENTRY_EDITING;
- iter.InsertBefore(lifetime);
- // let's say 'var a = 10;'. if a is not used in the function, we still want to have the instr, otherwise the write-through will not happen and upon debug bailout
- // we would not be able to restore the values to see in locals window.
- if (this->func->IsJitInDebugMode() && stackSym->HasByteCodeRegSlot() && this->func->IsNonTempLocalVar(stackSym->GetByteCodeRegSlot()))
- {
- lifetime->isDeadStore = false;
- }
- stackSym->scratch.linearScan.lifetime = lifetime;
- return lifetime;
- }
- bool
- SCCLiveness::FoldIndir(IR::Instr *instr, IR::Opnd *opnd)
- {
- #ifdef _M_ARM
- // Can't be folded on ARM
- return false;
- #else
- IR::IndirOpnd *indir = opnd->AsIndirOpnd();
- if(indir->GetIndexOpnd())
- {
- IR::RegOpnd *index = indir->GetIndexOpnd();
- if (!index->m_sym || !index->m_sym->IsIntConst())
- {
- return false;
- }
- // offset = indir.offset + (index << scale)
- int32 offset = index->m_sym->GetIntConstValue();
- if(indir->GetScale() != 0 && Int32Math::Shl(offset, indir->GetScale(), &offset) ||
- indir->GetOffset() != 0 && Int32Math::Add(indir->GetOffset(), offset, &offset))
- {
- return false;
- }
- indir->SetOffset(offset);
- indir->SetIndexOpnd(nullptr);
- }
- IR::RegOpnd *base = indir->GetBaseOpnd();
- if (!base->m_sym || !base->m_sym->IsConst() || base->m_sym->IsIntConst() || base->m_sym->IsFloatConst())
- {
- return false;
- }
- uint8 *constValue = static_cast<uint8 *>(base->m_sym->GetConstAddress());
- if(indir->GetOffset() != 0)
- {
- if(indir->GetOffset() < 0 ? constValue + indir->GetOffset() > constValue : constValue + indir->GetOffset() < constValue)
- {
- return false;
- }
- constValue += indir->GetOffset();
- }
- #ifdef _M_X64
- // Encoding only allows 32bits worth
- if(!Math::FitsInDWord((size_t)constValue))
- {
- return false;
- }
- #endif
- IR::MemRefOpnd *memref = IR::MemRefOpnd::New(constValue, indir->GetType(), instr->m_func);
- if (indir == instr->GetDst())
- {
- instr->ReplaceDst(memref);
- }
- else
- {
- instr->ReplaceSrc(indir, memref);
- }
- return true;
- #endif
- }
- uint SCCLiveness::CurrentOpHelperVisitedLength(IR::Instr *const currentInstr) const
- {
- Assert(currentInstr);
- if(!lastOpHelperLabel)
- {
- return 0;
- }
- Assert(currentInstr->GetNumber() >= lastOpHelperLabel->GetNumber());
- uint visitedLength = currentInstr->GetNumber() - lastOpHelperLabel->GetNumber();
- if(!currentInstr->IsLabelInstr())
- {
- // Consider the current instruction to have been visited
- ++visitedLength;
- }
- return visitedLength;
- }
- #if DBG_DUMP
- // SCCLiveness::Dump
- void
- SCCLiveness::Dump()
- {
- this->func->DumpHeader();
- Output::Print(L"************ Liveness ************\n");
- FOREACH_SLIST_ENTRY(Lifetime *, lifetime, &this->lifetimeList)
- {
- lifetime->sym->Dump();
- Output::Print(L": live range %3d - %3d (XUserCall: %d, XCall: %d)\n", lifetime->start, lifetime->end,
- lifetime->isLiveAcrossUserCalls,
- lifetime->isLiveAcrossCalls);
- }
- NEXT_SLIST_ENTRY;
- FOREACH_INSTR_IN_FUNC(instr, func)
- {
- Output::Print(L"%3d > ", instr->GetNumber());
- instr->Dump();
- } NEXT_INSTR_IN_FUNC;
- }
- #endif
- uint OpHelperBlock::Length() const
- {
- Assert(opHelperLabel);
- Assert(opHelperEndInstr);
- uint length = opHelperEndInstr->GetNumber() - opHelperLabel->GetNumber();
- if(!opHelperEndInstr->IsLabelInstr())
- {
- ++length;
- }
- return length;
- }
|