| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457 |
- //-------------------------------------------------------------------------------------------------------
- // 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"
- void
- GlobOpt::CaptureCopyPropValue(BasicBlock * block, Sym * sym, Value * val, SListBase<CopyPropSyms>::EditingIterator & bailOutCopySymsIter)
- {
- if (!sym->IsStackSym())
- {
- return;
- }
- StackSym * copyPropSym = block->globOptData.GetCopyPropSym(sym, val);
- if (copyPropSym != nullptr)
- {
- bailOutCopySymsIter.InsertNodeBefore(this->func->m_alloc, sym->AsStackSym(), copyPropSym);
- }
- }
- void
- GlobOpt::CaptureValuesFromScratch(BasicBlock * block,
- SListBase<ConstantStackSymValue>::EditingIterator & bailOutConstValuesIter,
- SListBase<CopyPropSyms>::EditingIterator & bailOutCopySymsIter,
- BVSparse<JitArenaAllocator>* argsToCapture)
- {
- Sym * sym = nullptr;
- Value * value = nullptr;
- ValueInfo * valueInfo = nullptr;
- block->globOptData.changedSyms->ClearAll();
- FOREACH_GLOBHASHTABLE_ENTRY(bucket, block->globOptData.symToValueMap)
- {
- value = bucket.element;
- valueInfo = value->GetValueInfo();
- if (valueInfo->GetSymStore() == nullptr && !valueInfo->HasIntConstantValue())
- {
- continue;
- }
- sym = bucket.value;
- if (sym == nullptr || !sym->IsStackSym() || !(sym->AsStackSym()->HasByteCodeRegSlot()))
- {
- continue;
- }
- block->globOptData.changedSyms->Set(sym->m_id);
- }
- NEXT_GLOBHASHTABLE_ENTRY;
- if (argsToCapture)
- {
- block->globOptData.changedSyms->Or(argsToCapture);
- }
- FOREACH_BITSET_IN_SPARSEBV(symId, block->globOptData.changedSyms)
- {
- HashBucket<Sym*, Value*> * bucket = block->globOptData.symToValueMap->GetBucket(symId);
- StackSym * stackSym = bucket->value->AsStackSym();
- value = bucket->element;
- valueInfo = value->GetValueInfo();
- int intConstantValue;
- if (valueInfo->TryGetIntConstantValue(&intConstantValue))
- {
- BailoutConstantValue constValue;
- constValue.InitIntConstValue(intConstantValue);
- bailOutConstValuesIter.InsertNodeBefore(this->func->m_alloc, stackSym, constValue);
- }
- else if (valueInfo->IsVarConstant())
- {
- BailoutConstantValue constValue;
- constValue.InitVarConstValue(valueInfo->AsVarConstant()->VarValue());
- bailOutConstValuesIter.InsertNodeBefore(this->func->m_alloc, stackSym, constValue);
- }
- else
- {
- CaptureCopyPropValue(block, stackSym, value, bailOutCopySymsIter);
- }
- }
- NEXT_BITSET_IN_SPARSEBV
- }
- void
- GlobOpt::CaptureValuesIncremental(BasicBlock * block,
- SListBase<ConstantStackSymValue>::EditingIterator & bailOutConstValuesIter,
- SListBase<CopyPropSyms>::EditingIterator & bailOutCopySymsIter,
- BVSparse<JitArenaAllocator>* argsToCapture)
- {
- CapturedValues * currCapturedValues = block->globOptData.capturedValues;
- SListBase<ConstantStackSymValue>::Iterator iterConst(currCapturedValues ? &currCapturedValues->constantValues : nullptr);
- SListBase<CopyPropSyms>::Iterator iterCopyPropSym(currCapturedValues ? &currCapturedValues->copyPropSyms : nullptr);
- bool hasConstValue = currCapturedValues ? iterConst.Next() : false;
- bool hasCopyPropSym = currCapturedValues ? iterCopyPropSym.Next() : false;
- block->globOptData.changedSyms->Set(Js::Constants::InvalidSymID);
- if (argsToCapture)
- {
- block->globOptData.changedSyms->Or(argsToCapture);
- }
- FOREACH_BITSET_IN_SPARSEBV(symId, block->globOptData.changedSyms)
- {
- Value * val = nullptr;
- // First process all unchanged syms with m_id < symId. Then, recapture the current changed sym.
- // copy unchanged const sym to new capturedValues
- Sym * constSym = hasConstValue ? iterConst.Data().Key() : nullptr;
- while (constSym && constSym->m_id < symId)
- {
- Assert(constSym->IsStackSym());
- if (!constSym->AsStackSym()->HasArgSlotNum())
- {
- bailOutConstValuesIter.InsertNodeBefore(this->func->m_alloc, constSym->AsStackSym(), iterConst.Data().Value());
- }
- hasConstValue = iterConst.Next();
- constSym = hasConstValue ? iterConst.Data().Key() : nullptr;
- }
- if (constSym && constSym->m_id == symId)
- {
- hasConstValue = iterConst.Next();
- }
- // process unchanged sym; copy-prop sym might have changed
- Sym * capturedSym = hasCopyPropSym ? iterCopyPropSym.Data().Key() : nullptr;
- while (capturedSym && capturedSym->m_id < symId)
- {
- StackSym * capturedCopyPropSym = iterCopyPropSym.Data().Value();
- Assert(capturedSym->IsStackSym());
- if (!block->globOptData.changedSyms->Test(capturedCopyPropSym->m_id))
- {
- if (!capturedSym->AsStackSym()->HasArgSlotNum())
- {
- bailOutCopySymsIter.InsertNodeBefore(this->func->m_alloc, capturedSym->AsStackSym(), capturedCopyPropSym);
- }
- }
- else
- {
- if (!capturedSym->AsStackSym()->HasArgSlotNum())
- {
- val = this->currentBlock->globOptData.FindValue(capturedSym);
- if (val != nullptr)
- {
- CaptureCopyPropValue(block, capturedSym, val, bailOutCopySymsIter);
- }
- }
- }
- hasCopyPropSym = iterCopyPropSym.Next();
- capturedSym = hasCopyPropSym ? iterCopyPropSym.Data().Key() : nullptr;
- }
- if (capturedSym && capturedSym->m_id == symId)
- {
- hasCopyPropSym = iterCopyPropSym.Next();
- }
- // recapture changed sym
- HashBucket<Sym *, Value *> * symIdBucket = nullptr;
- if (symId != Js::Constants::InvalidSymID)
- {
- symIdBucket = block->globOptData.symToValueMap->GetBucket(symId);
- if (symIdBucket != nullptr)
- {
- Sym * symIdSym = symIdBucket->value;
- Assert(symIdSym->IsStackSym() && (symIdSym->AsStackSym()->HasByteCodeRegSlot() || symIdSym->AsStackSym()->HasArgSlotNum()));
- val = symIdBucket->element;
- Assert(val);
- ValueInfo* valueInfo = val->GetValueInfo();
- if (valueInfo->GetSymStore() != nullptr)
- {
- int32 intConstValue;
- BailoutConstantValue constValue;
- if (valueInfo->TryGetIntConstantValue(&intConstValue))
- {
- constValue.InitIntConstValue(intConstValue);
- bailOutConstValuesIter.InsertNodeBefore(this->func->m_alloc, symIdSym->AsStackSym(), constValue);
- }
- else if (valueInfo->IsVarConstant())
- {
- constValue.InitVarConstValue(valueInfo->AsVarConstant()->VarValue());
- bailOutConstValuesIter.InsertNodeBefore(this->func->m_alloc, symIdSym->AsStackSym(), constValue);
- }
- else
- {
- CaptureCopyPropValue(block, symIdSym, val, bailOutCopySymsIter);
- }
- }
- }
- }
- }
- NEXT_BITSET_IN_SPARSEBV
- // If, after going over the set of changed syms since the last time we captured values,
- // there are remaining unprocessed entries in the current captured values set,
- // they can simply be copied over to the new bailout info.
- while (hasConstValue)
- {
- Sym * constSym = iterConst.Data().Key();
- Assert(constSym->IsStackSym());
- Assert(!block->globOptData.changedSyms->Test(constSym->m_id));
- if (!constSym->AsStackSym()->HasArgSlotNum())
- {
- bailOutConstValuesIter.InsertNodeBefore(this->func->m_alloc, constSym->AsStackSym(), iterConst.Data().Value());
- }
- hasConstValue = iterConst.Next();
- }
- while (hasCopyPropSym)
- {
- Sym * capturedSym = iterCopyPropSym.Data().Key();
- StackSym * capturedCopyPropSym = iterCopyPropSym.Data().Value();
- Assert(capturedSym->IsStackSym());
- Assert(!block->globOptData.changedSyms->Test(capturedSym->m_id) &&
- !block->globOptData.changedSyms->Test(capturedCopyPropSym->m_id));
- if (!capturedSym->AsStackSym()->HasArgSlotNum())
- {
- bailOutCopySymsIter.InsertNodeBefore(this->func->m_alloc, capturedSym->AsStackSym(), capturedCopyPropSym);
- }
- hasCopyPropSym = iterCopyPropSym.Next();
- }
- }
- void
- GlobOpt::CaptureValues(BasicBlock *block, BailOutInfo * bailOutInfo, BVSparse<JitArenaAllocator>* argsToCapture)
- {
- if (!this->func->DoGlobOptsForGeneratorFunc())
- {
- // TODO[generators][ianhall]: Enable constprop and copyprop for generator functions; see GlobOpt::CopyProp()
- // Even though CopyProp is disabled for generator functions we must also not put the copy-prop sym into the
- // bailOutInfo so that the bailOutInfo keeps track of the key sym in its byteCodeUpwardExposed list.
- return;
- }
- CapturedValues capturedValues;
- SListBase<ConstantStackSymValue>::EditingIterator bailOutConstValuesIter(&capturedValues.constantValues);
- SListBase<CopyPropSyms>::EditingIterator bailOutCopySymsIter(&capturedValues.copyPropSyms);
- bailOutConstValuesIter.Next();
- bailOutCopySymsIter.Next();
- if (!block->globOptData.capturedValues)
- {
- CaptureValuesFromScratch(block, bailOutConstValuesIter, bailOutCopySymsIter, argsToCapture);
- }
- else
- {
- CaptureValuesIncremental(block, bailOutConstValuesIter, bailOutCopySymsIter, argsToCapture);
- }
- // attach capturedValues to bailOutInfo
- bailOutInfo->capturedValues->constantValues.Clear(this->func->m_alloc);
- bailOutConstValuesIter.SetNext(&bailOutInfo->capturedValues->constantValues);
- bailOutInfo->capturedValues->constantValues = capturedValues.constantValues;
- bailOutInfo->capturedValues->copyPropSyms.Clear(this->func->m_alloc);
- bailOutCopySymsIter.SetNext(&bailOutInfo->capturedValues->copyPropSyms);
- bailOutInfo->capturedValues->copyPropSyms = capturedValues.copyPropSyms;
- // In pre-pass only bailout info created should be for the loop header, and that doesn't take into account the back edge.
- // Don't use the captured values on that bailout for incremental capturing of values.
- if (!PHASE_OFF(Js::IncrementalBailoutPhase, func) && !this->IsLoopPrePass())
- {
- // cache the pointer of current bailout as potential baseline for later bailout in this block
- if (block->globOptData.capturedValuesCandidate)
- {
- block->globOptData.capturedValuesCandidate->DecrementRefCount();
- }
- block->globOptData.capturedValuesCandidate = bailOutInfo->capturedValues;
- block->globOptData.capturedValuesCandidate->IncrementRefCount();
- // reset changed syms to track symbols change after the above captured values candidate
- this->changedSymsAfterIncBailoutCandidate->ClearAll();
- }
- }
- void
- GlobOpt::CaptureArguments(BasicBlock *block, BailOutInfo * bailOutInfo, JitArenaAllocator *allocator)
- {
- FOREACH_BITSET_IN_SPARSEBV(id, this->currentBlock->globOptData.argObjSyms)
- {
- StackSym * stackSym = this->func->m_symTable->FindStackSym(id);
- Assert(stackSym != nullptr);
- if (!stackSym->HasByteCodeRegSlot())
- {
- continue;
- }
- if (!bailOutInfo->capturedValues->argObjSyms)
- {
- bailOutInfo->capturedValues->argObjSyms = JitAnew(allocator, BVSparse<JitArenaAllocator>, allocator);
- }
- bailOutInfo->capturedValues->argObjSyms->Set(id);
- // Add to BailOutInfo
- }
- NEXT_BITSET_IN_SPARSEBV
- }
- void
- GlobOpt::TrackByteCodeSymUsed(IR::Instr * instr, BVSparse<JitArenaAllocator> * instrByteCodeStackSymUsed, PropertySym **pPropertySym)
- {
- if(instr->m_func->GetJITFunctionBody()->IsAsmJsMode())
- {
- return;
- }
- IR::Opnd * src = instr->GetSrc1();
- if (src)
- {
- TrackByteCodeSymUsed(src, instrByteCodeStackSymUsed, pPropertySym);
- src = instr->GetSrc2();
- if (src)
- {
- TrackByteCodeSymUsed(src, instrByteCodeStackSymUsed, pPropertySym);
- }
- }
- #if DBG
- // There should be no more than one property sym used.
- PropertySym *propertySymFromSrc = *pPropertySym;
- #endif
- IR::Opnd * dst = instr->GetDst();
- if (dst)
- {
- StackSym *stackSym = dst->GetStackSym();
- // We want stackSym uses: IndirOpnd and SymOpnds of propertySyms.
- // RegOpnd and SymOPnd of StackSyms are stack sym defs.
- if (stackSym == NULL)
- {
- TrackByteCodeSymUsed(dst, instrByteCodeStackSymUsed, pPropertySym);
- }
- }
- #if DBG
- AssertMsg(propertySymFromSrc == NULL || propertySymFromSrc == *pPropertySym,
- "Lost a property sym use?");
- #endif
- }
- void
- GlobOpt::TrackByteCodeSymUsed(IR::RegOpnd * regOpnd, BVSparse<JitArenaAllocator> * instrByteCodeStackSymUsed)
- {
- // Check JITOptimizedReg to catch case where baseOpnd of indir was optimized.
- if (!regOpnd->GetIsJITOptimizedReg())
- {
- TrackByteCodeSymUsed(regOpnd->m_sym, instrByteCodeStackSymUsed);
- }
- }
- void
- GlobOpt::TrackByteCodeSymUsed(IR::Opnd * opnd, BVSparse<JitArenaAllocator> * instrByteCodeStackSymUsed, PropertySym **pPropertySym)
- {
- if (opnd->GetIsJITOptimizedReg())
- {
- AssertMsg(!opnd->IsIndirOpnd(), "TrackByteCodeSymUsed doesn't expect IndirOpnd with IsJITOptimizedReg turned on");
- return;
- }
- switch(opnd->GetKind())
- {
- case IR::OpndKindReg:
- TrackByteCodeSymUsed(opnd->AsRegOpnd(), instrByteCodeStackSymUsed);
- break;
- case IR::OpndKindSym:
- {
- Sym * sym = opnd->AsSymOpnd()->m_sym;
- if (sym->IsStackSym())
- {
- TrackByteCodeSymUsed(sym->AsStackSym(), instrByteCodeStackSymUsed);
- }
- else
- {
- TrackByteCodeSymUsed(sym->AsPropertySym()->m_stackSym, instrByteCodeStackSymUsed);
- *pPropertySym = sym->AsPropertySym();
- }
- }
- break;
- case IR::OpndKindIndir:
- TrackByteCodeSymUsed(opnd->AsIndirOpnd()->GetBaseOpnd(), instrByteCodeStackSymUsed);
- {
- IR::RegOpnd * indexOpnd = opnd->AsIndirOpnd()->GetIndexOpnd();
- if (indexOpnd)
- {
- TrackByteCodeSymUsed(indexOpnd, instrByteCodeStackSymUsed);
- }
- }
- break;
- }
- }
- void
- GlobOpt::TrackByteCodeSymUsed(StackSym * sym, BVSparse<JitArenaAllocator> * instrByteCodeStackSymUsed)
- {
- // We only care about stack sym that has a corresponding byte code register
- if (sym->HasByteCodeRegSlot())
- {
- if (sym->IsTypeSpec())
- {
- // It has to have a var version for byte code regs
- sym = sym->GetVarEquivSym(nullptr);
- }
- instrByteCodeStackSymUsed->Set(sym->m_id);
- }
- }
- void
- GlobOpt::MarkNonByteCodeUsed(IR::Instr * instr)
- {
- IR::Opnd * dst = instr->GetDst();
- if (dst)
- {
- MarkNonByteCodeUsed(dst);
- }
- IR::Opnd * src1 = instr->GetSrc1();
- if (src1)
- {
- MarkNonByteCodeUsed(src1);
- IR::Opnd * src2 = instr->GetSrc2();
- if (src2)
- {
- MarkNonByteCodeUsed(src2);
- }
- }
- }
- void
- GlobOpt::MarkNonByteCodeUsed(IR::Opnd * opnd)
- {
- switch(opnd->GetKind())
- {
- case IR::OpndKindReg:
- opnd->AsRegOpnd()->SetIsJITOptimizedReg(true);
- break;
- case IR::OpndKindIndir:
- opnd->AsIndirOpnd()->GetBaseOpnd()->SetIsJITOptimizedReg(true);
- {
- IR::RegOpnd * indexOpnd = opnd->AsIndirOpnd()->GetIndexOpnd();
- if (indexOpnd)
- {
- indexOpnd->SetIsJITOptimizedReg(true);
- }
- }
- break;
- }
- }
- void
- GlobOpt::CaptureByteCodeSymUses(IR::Instr * instr)
- {
- if (this->byteCodeUses || this->func->GetJITFunctionBody()->IsAsmJsMode())
- {
- // We already captured it before.
- return;
- }
- Assert(this->propertySymUse == NULL);
- this->byteCodeUses = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
- GlobOpt::TrackByteCodeSymUsed(instr, this->byteCodeUses, &this->propertySymUse);
- AssertMsg(this->byteCodeUses->Equal(this->byteCodeUsesBeforeOpt),
- "Instruction edited before capturing the byte code use");
- }
- void
- GlobOpt::ProcessInlineeEnd(IR::Instr* instr)
- {
- if (instr->m_func->m_hasInlineArgsOpt)
- {
- RecordInlineeFrameInfo(instr);
- }
- EndTrackingOfArgObjSymsForInlinee();
- Assert(this->currentBlock->globOptData.inlinedArgOutSize >= instr->GetArgOutSize(/*getInterpreterArgOutCount*/ false));
- this->currentBlock->globOptData.inlinedArgOutSize -= instr->GetArgOutSize(/*getInterpreterArgOutCount*/ false);
- }
- void
- GlobOpt::TrackCalls(IR::Instr * instr)
- {
- // Keep track of out params for bailout
- switch (instr->m_opcode)
- {
- case Js::OpCode::StartCall:
- Assert(!this->isCallHelper);
- Assert(instr->GetDst()->IsRegOpnd());
- Assert(instr->GetDst()->AsRegOpnd()->m_sym->m_isSingleDef);
- if (this->currentBlock->globOptData.callSequence == nullptr)
- {
- this->currentBlock->globOptData.callSequence = JitAnew(this->alloc, SListBase<IR::Opnd *>);
- this->currentBlock->globOptData.callSequence = this->currentBlock->globOptData.callSequence;
- }
- this->currentBlock->globOptData.callSequence->Prepend(this->alloc, instr->GetDst());
- this->currentBlock->globOptData.totalOutParamCount += instr->GetArgOutCount(/*getInterpreterArgOutCount*/ true);
- this->currentBlock->globOptData.startCallCount++;
- break;
- case Js::OpCode::BytecodeArgOutCapture:
- {
- this->currentBlock->globOptData.callSequence->Prepend(this->alloc, instr->GetDst());
- this->currentBlock->globOptData.argOutCount++;
- break;
- }
- case Js::OpCode::ArgOut_A:
- case Js::OpCode::ArgOut_A_Inline:
- case Js::OpCode::ArgOut_A_FixupForStackArgs:
- case Js::OpCode::ArgOut_A_InlineBuiltIn:
- case Js::OpCode::ArgOut_A_Dynamic:
- case Js::OpCode::ArgOut_A_FromStackArgs:
- case Js::OpCode::ArgOut_A_SpreadArg:
- {
- IR::Opnd * opnd = instr->GetDst();
- if (opnd->IsSymOpnd())
- {
- Assert(!this->isCallHelper);
- Assert(!this->currentBlock->globOptData.callSequence->Empty());
- StackSym* stackSym = opnd->AsSymOpnd()->m_sym->AsStackSym();
- // These scenarios are already tracked using BytecodeArgOutCapture,
- // and we don't want to be tracking ArgOut_A_FixupForStackArgs as these are only visible to the JIT and we should not be restoring them upon bailout.
- if (!stackSym->m_isArgCaptured && instr->m_opcode != Js::OpCode::ArgOut_A_FixupForStackArgs)
- {
- this->currentBlock->globOptData.callSequence->Prepend(this->alloc, instr->GetDst());
- this->currentBlock->globOptData.argOutCount++;
- }
- Assert(stackSym->IsArgSlotSym());
- if (stackSym->m_isInlinedArgSlot)
- {
- uint size = TySize[instr->GetDst()->GetType()];
- this->currentBlock->globOptData.inlinedArgOutSize += size < MachPtr ? MachPtr : size;
- // We want to update the offsets only once: don't do in prepass.
- if (!this->IsLoopPrePass() && stackSym->m_offset >= 0)
- {
- Func * currentFunc = instr->m_func;
- stackSym->FixupStackOffset(currentFunc);
- }
- }
- }
- else
- {
- // It is a reg opnd if it is a helper call
- // It should be all ArgOut until the CallHelper instruction
- Assert(opnd->IsRegOpnd());
- this->isCallHelper = true;
- }
- if (instr->m_opcode == Js::OpCode::ArgOut_A_FixupForStackArgs && !this->IsLoopPrePass())
- {
- instr->m_opcode = Js::OpCode::ArgOut_A_Inline;
- }
- break;
- }
- case Js::OpCode::InlineeStart:
- Assert(instr->m_func->GetParentFunc() == this->currentBlock->globOptData.curFunc);
- Assert(instr->m_func->GetParentFunc());
- this->currentBlock->globOptData.curFunc = instr->m_func;
- this->func->UpdateMaxInlineeArgOutSize(this->currentBlock->globOptData.inlinedArgOutSize);
- this->EndTrackCall(instr);
- if (DoInlineArgsOpt(instr->m_func))
- {
- instr->m_func->m_hasInlineArgsOpt = true;
- InlineeFrameInfo* frameInfo = InlineeFrameInfo::New(func->m_alloc);
- instr->m_func->frameInfo = frameInfo;
- frameInfo->functionSymStartValue = instr->GetSrc1()->GetSym() ?
- CurrentBlockData()->FindValue(instr->GetSrc1()->GetSym()) : nullptr;
- frameInfo->floatSyms = CurrentBlockData()->liveFloat64Syms->CopyNew(this->alloc);
- frameInfo->intSyms = CurrentBlockData()->liveInt32Syms->MinusNew(CurrentBlockData()->liveLossyInt32Syms, this->alloc);
- frameInfo->varSyms = CurrentBlockData()->liveVarSyms->CopyNew(this->alloc);
- }
- break;
- case Js::OpCode::EndCallForPolymorphicInlinee:
- // Have this opcode mimic the functions of both InlineeStart and InlineeEnd in the bailout block of a polymorphic call inlined using fixed methods.
- this->EndTrackCall(instr);
- break;
- case Js::OpCode::CallHelper:
- case Js::OpCode::IsInst:
- Assert(this->isCallHelper);
- this->isCallHelper = false;
- break;
- case Js::OpCode::InlineeEnd:
- ProcessInlineeEnd(instr);
- break;
- case Js::OpCode::InlineeMetaArg:
- {
- Assert(instr->GetDst()->IsSymOpnd());
- StackSym * stackSym = instr->GetDst()->AsSymOpnd()->m_sym->AsStackSym();
- Assert(stackSym->IsArgSlotSym());
- // InlineeMetaArg has the m_func set as the "inlinee" and not the "inliner"
- // TODO: Review this and fix the m_func of InlineeMetaArg to be "inliner" (as for the rest of the ArgOut's)
- // We want to update the offsets only once: don't do in prepass.
- if (!this->IsLoopPrePass())
- {
- Func * currentFunc = instr->m_func->GetParentFunc();
- stackSym->FixupStackOffset(currentFunc);
- }
- this->currentBlock->globOptData.inlinedArgOutSize += MachPtr;
- break;
- }
- case Js::OpCode::InlineBuiltInStart:
- this->inInlinedBuiltIn = true;
- break;
- case Js::OpCode::InlineNonTrackingBuiltInEnd:
- case Js::OpCode::InlineBuiltInEnd:
- {
- // If extra bailouts were added for the InlineMathXXX call itself,
- // move InlineeBuiltInStart just above the InlineMathXXX.
- // This is needed so that the function argument has lifetime after all bailouts for InlineMathXXX,
- // otherwise when we bailout we would get wrong function.
- IR::Instr* inlineBuiltInStartInstr = instr->m_prev;
- while (inlineBuiltInStartInstr->m_opcode != Js::OpCode::InlineBuiltInStart)
- {
- inlineBuiltInStartInstr = inlineBuiltInStartInstr->m_prev;
- }
- IR::Instr *byteCodeUsesInstr = inlineBuiltInStartInstr->m_prev;
- IR::Instr * insertBeforeInstr = instr->m_prev;
- IR::Instr * tmpInstr = insertBeforeInstr;
- while(tmpInstr->m_opcode != Js::OpCode::InlineBuiltInStart )
- {
- if(tmpInstr->m_opcode == Js::OpCode::ByteCodeUses)
- {
- insertBeforeInstr = tmpInstr;
- }
- tmpInstr = tmpInstr->m_prev;
- }
- inlineBuiltInStartInstr->Unlink();
- if(insertBeforeInstr == instr->m_prev)
- {
- insertBeforeInstr->InsertBefore(inlineBuiltInStartInstr);
- }
- else
- {
- insertBeforeInstr->m_prev->InsertBefore(inlineBuiltInStartInstr);
- }
- // Need to move the byte code uses instructions associated with inline built-in start instruction as well. For instance,
- // copy-prop may have replaced the function sym and inserted a byte code uses for the original sym holding the function.
- // That byte code uses instruction needs to appear after bailouts inserted for the InlinMathXXX instruction since the
- // byte code register holding the function object needs to be restored on bailout.
- IR::Instr *const insertByteCodeUsesAfterInstr = inlineBuiltInStartInstr->m_prev;
- if(byteCodeUsesInstr != insertByteCodeUsesAfterInstr)
- {
- // The InlineBuiltInStart instruction was moved, look for its ByteCodeUses instructions that also need to be moved
- while(
- byteCodeUsesInstr->IsByteCodeUsesInstr() &&
- byteCodeUsesInstr->AsByteCodeUsesInstr()->GetByteCodeOffset() == inlineBuiltInStartInstr->GetByteCodeOffset())
- {
- IR::Instr *const instrToMove = byteCodeUsesInstr;
- byteCodeUsesInstr = byteCodeUsesInstr->m_prev;
- instrToMove->Unlink();
- insertByteCodeUsesAfterInstr->InsertAfter(instrToMove);
- }
- }
- // The following code makes more sense to be processed when we hit InlineeBuiltInStart,
- // but when extra bailouts are added for the InlineMathXXX and InlineArrayPop instructions itself, those bailouts
- // need to know about current bailout record, but since they are added after TrackCalls is called
- // for InlineeBuiltInStart, we can't clear current record when got InlineeBuiltInStart
- // Do not track calls for InlineNonTrackingBuiltInEnd, as it is already tracked for InlineArrayPop
- if(instr->m_opcode == Js::OpCode::InlineBuiltInEnd)
- {
- this->EndTrackCall(instr);
- }
- Assert(this->currentBlock->globOptData.inlinedArgOutSize >= instr->GetArgOutSize(/*getInterpreterArgOutCount*/ false));
- this->currentBlock->globOptData.inlinedArgOutSize -= instr->GetArgOutSize(/*getInterpreterArgOutCount*/ false);
- this->inInlinedBuiltIn = false;
- break;
- }
- case Js::OpCode::InlineArrayPop:
- {
- // EndTrackCall should be called here as the Post-op BailOutOnImplicitCalls will bail out to the instruction after the Pop function call instr.
- // This bailout shouldn't be tracking the call sequence as it will then erroneously reserve stack space for arguments when the call would have already happened
- // Can't wait till InlineBuiltinEnd like we do for other InlineMathXXX because by then we would have filled bailout info for the BailOutOnImplicitCalls for InlineArrayPop.
- this->EndTrackCall(instr);
- break;
- }
- default:
- if (OpCodeAttr::CallInstr(instr->m_opcode))
- {
- this->EndTrackCall(instr);
- if (this->inInlinedBuiltIn && instr->m_opcode == Js::OpCode::CallDirect)
- {
- // We can end up in this situation when a built-in apply target is inlined to a CallDirect. We have the following IR:
- //
- // StartCall
- // ArgOut_InlineBuiltIn
- // ArgOut_InlineBuiltIn
- // ArgOut_InlineBuiltIn
- // InlineBuiltInStart
- // ArgOut_A_InlineSpecialized
- // ArgOut_A
- // ArgOut_A
- // CallDirect
- // InlineNonTrackingBuiltInEnd
- //
- // We need to call EndTrackCall twice for CallDirect in this case. The CallDirect may get a BailOutOnImplicitCalls later,
- // but it should not be tracking the call sequence for the apply call as it is a post op bailout and the call would have
- // happened when we bail out.
- // Can't wait till InlineBuiltinEnd like we do for other InlineMathXXX because by then we would have filled bailout info for the BailOutOnImplicitCalls for CallDirect.
- this->EndTrackCall(instr);
- }
- }
- break;
- }
- }
- void GlobOpt::RecordInlineeFrameInfo(IR::Instr* inlineeEnd)
- {
- if (this->IsLoopPrePass())
- {
- return;
- }
- InlineeFrameInfo* frameInfo = inlineeEnd->m_func->frameInfo;
- if (frameInfo->isRecorded)
- {
- Assert(frameInfo->function.type != InlineeFrameInfoValueType_None);
- // Due to Cmp peeps in flow graph - InlineeEnd can be cloned.
- return;
- }
- inlineeEnd->IterateArgInstrs([=] (IR::Instr* argInstr)
- {
- if (argInstr->m_opcode == Js::OpCode::InlineeStart)
- {
- Assert(frameInfo->function.type == InlineeFrameInfoValueType_None);
- IR::RegOpnd* functionObject = argInstr->GetSrc1()->AsRegOpnd();
- if (functionObject->m_sym->IsConst())
- {
- frameInfo->function = InlineFrameInfoValue(functionObject->m_sym->GetConstValueForBailout());
- }
- else
- {
- // If the value of the functionObject symbol has changed between the inlineeStart and the inlineeEnd,
- // we don't record the inlinee frame info (see OS#18318884).
- Assert(frameInfo->functionSymStartValue != nullptr);
- if (!frameInfo->functionSymStartValue->IsEqualTo(CurrentBlockData()->FindValue(functionObject->m_sym)))
- {
- argInstr->m_func->DisableCanDoInlineArgOpt();
- return true;
- }
- frameInfo->function = InlineFrameInfoValue(functionObject->m_sym);
- }
- }
- else if(!GetIsAsmJSFunc()) // don't care about saving arg syms for wasm/asm.js
- {
- Js::ArgSlot argSlot = argInstr->GetDst()->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum();
- IR::Opnd* argOpnd = argInstr->GetSrc1();
- InlineFrameInfoValue frameInfoValue;
- StackSym* argSym = argOpnd->GetStackSym();
- if (!argSym)
- {
- frameInfoValue = InlineFrameInfoValue(argOpnd->GetConstValue());
- }
- else if (argSym->IsConst() && !argSym->IsInt64Const())
- {
- // InlineFrameInfo doesn't currently support Int64Const
- frameInfoValue = InlineFrameInfoValue(argSym->GetConstValueForBailout());
- }
- else
- {
- if (!PHASE_OFF(Js::CopyPropPhase, func))
- {
- Value* value = this->currentBlock->globOptData.FindValue(argSym);
- if (value)
- {
- StackSym * copyPropSym = this->currentBlock->globOptData.GetCopyPropSym(argSym, value);
- if (copyPropSym &&
- frameInfo->varSyms->TestEmpty() && frameInfo->varSyms->Test(copyPropSym->m_id))
- {
- argSym = copyPropSym;
- }
- }
- }
- if (frameInfo->intSyms->TestEmpty() && frameInfo->intSyms->Test(argSym->m_id))
- {
- // Var version of the sym is not live, use the int32 version
- argSym = argSym->GetInt32EquivSym(nullptr);
- Assert(argSym);
- }
- else if (frameInfo->floatSyms->TestEmpty() && frameInfo->floatSyms->Test(argSym->m_id))
- {
- // Var/int32 version of the sym is not live, use the float64 version
- argSym = argSym->GetFloat64EquivSym(nullptr);
- Assert(argSym);
- }
- else
- {
- Assert(frameInfo->varSyms->Test(argSym->m_id));
- }
- if (argSym->IsConst() && !argSym->IsInt64Const())
- {
- frameInfoValue = InlineFrameInfoValue(argSym->GetConstValueForBailout());
- }
- else
- {
- frameInfoValue = InlineFrameInfoValue(argSym);
- }
- }
- Assert(argSlot >= 1);
- frameInfo->arguments->SetItem(argSlot - 1, frameInfoValue);
- }
- return false;
- });
- JitAdelete(this->alloc, frameInfo->intSyms);
- frameInfo->intSyms = nullptr;
- JitAdelete(this->alloc, frameInfo->floatSyms);
- frameInfo->floatSyms = nullptr;
- JitAdelete(this->alloc, frameInfo->varSyms);
- frameInfo->varSyms = nullptr;
- frameInfo->isRecorded = true;
- }
- void GlobOpt::EndTrackingOfArgObjSymsForInlinee()
- {
- Assert(this->currentBlock->globOptData.curFunc->GetParentFunc());
- if (this->currentBlock->globOptData.curFunc->argObjSyms && TrackArgumentsObject())
- {
- BVSparse<JitArenaAllocator> * tempBv = JitAnew(this->tempAlloc, BVSparse<JitArenaAllocator>, this->tempAlloc);
- tempBv->Minus(this->currentBlock->globOptData.curFunc->argObjSyms, this->currentBlock->globOptData.argObjSyms);
- if(!tempBv->IsEmpty())
- {
- // This means there are arguments object symbols in the current function which are not in the current block.
- // This could happen when one of the blocks has a throw and arguments object aliased in it and other blocks don't see it.
- // Rare case, abort stack arguments optimization in this case.
- CannotAllocateArgumentsObjectOnStack();
- }
- else
- {
- Assert(this->currentBlock->globOptData.argObjSyms->OrNew(this->currentBlock->globOptData.curFunc->argObjSyms)->Equal(this->currentBlock->globOptData.argObjSyms));
- this->currentBlock->globOptData.argObjSyms->Minus(this->currentBlock->globOptData.curFunc->argObjSyms);
- }
- JitAdelete(this->tempAlloc, tempBv);
- }
- this->currentBlock->globOptData.curFunc = this->currentBlock->globOptData.curFunc->GetParentFunc();
- }
- void GlobOpt::EndTrackCall(IR::Instr* instr)
- {
- Assert(instr);
- Assert(OpCodeAttr::CallInstr(instr->m_opcode) || instr->m_opcode == Js::OpCode::InlineeStart || instr->m_opcode == Js::OpCode::InlineBuiltInEnd
- || instr->m_opcode == Js::OpCode::InlineArrayPop || instr->m_opcode == Js::OpCode::EndCallForPolymorphicInlinee);
- Assert(!this->isCallHelper);
- Assert(!this->currentBlock->globOptData.callSequence->Empty());
- #if DBG
- uint origArgOutCount = this->currentBlock->globOptData.argOutCount;
- #endif
- while (this->currentBlock->globOptData.callSequence->Head()->GetStackSym()->HasArgSlotNum())
- {
- this->currentBlock->globOptData.argOutCount--;
- this->currentBlock->globOptData.callSequence->RemoveHead(this->alloc);
- }
- StackSym * sym = this->currentBlock->globOptData.callSequence->Head()->AsRegOpnd()->m_sym->AsStackSym();
- this->currentBlock->globOptData.callSequence->RemoveHead(this->alloc);
- #if DBG
- Assert(sym->m_isSingleDef);
- Assert(sym->m_instrDef->m_opcode == Js::OpCode::StartCall);
- // Number of argument set should be the same as indicated at StartCall
- // except NewScObject has an implicit arg1
- Assert((uint)sym->m_instrDef->GetArgOutCount(/*getInterpreterArgOutCount*/ true) ==
- origArgOutCount - this->currentBlock->globOptData.argOutCount +
- (instr->m_opcode == Js::OpCode::NewScObject || instr->m_opcode == Js::OpCode::NewScObjArray
- || instr->m_opcode == Js::OpCode::NewScObjectSpread || instr->m_opcode == Js::OpCode::NewScObjArraySpread));
- #endif
- this->currentBlock->globOptData.totalOutParamCount -= sym->m_instrDef->GetArgOutCount(/*getInterpreterArgOutCount*/ true);
- this->currentBlock->globOptData.startCallCount--;
- }
- void
- GlobOpt::FillBailOutInfo(BasicBlock *block, BailOutInfo * bailOutInfo)
- {
- AssertMsg(!this->isCallHelper, "Bail out can't be inserted the middle of CallHelper sequence");
- BVSparse<JitArenaAllocator>* argsToCapture = nullptr;
- bailOutInfo->liveVarSyms = block->globOptData.liveVarSyms->CopyNew(this->func->m_alloc);
- bailOutInfo->liveFloat64Syms = block->globOptData.liveFloat64Syms->CopyNew(this->func->m_alloc);
- // The live int32 syms in the bailout info are only the syms resulting from lossless conversion to int. If the int32 value
- // was created from a lossy conversion to int, the original var value cannot be re-materialized from the int32 value. So, the
- // int32 version is considered to be not live for the purposes of bailout, which forces the var or float versions to be used
- // directly for restoring the value during bailout. Otherwise, bailout may try to re-materialize the var value by converting
- // the lossily-converted int value back into a var, restoring the wrong value.
- bailOutInfo->liveLosslessInt32Syms =
- block->globOptData.liveInt32Syms->MinusNew(block->globOptData.liveLossyInt32Syms, this->func->m_alloc);
- // Save the stack literal init field count so we can null out the uninitialized fields
- StackLiteralInitFldDataMap * stackLiteralInitFldDataMap = block->globOptData.stackLiteralInitFldDataMap;
- if (stackLiteralInitFldDataMap != nullptr)
- {
- uint stackLiteralInitFldDataCount = stackLiteralInitFldDataMap->Count();
- if (stackLiteralInitFldDataCount != 0)
- {
- auto stackLiteralBailOutInfo = AnewArray(this->func->m_alloc,
- BailOutInfo::StackLiteralBailOutInfo, stackLiteralInitFldDataCount);
- uint i = 0;
- stackLiteralInitFldDataMap->Map(
- [stackLiteralBailOutInfo, stackLiteralInitFldDataCount, &i](StackSym * stackSym, StackLiteralInitFldData const& data)
- {
- Assert(i < stackLiteralInitFldDataCount);
- stackLiteralBailOutInfo[i].stackSym = stackSym;
- stackLiteralBailOutInfo[i].initFldCount = data.currentInitFldCount;
- i++;
- });
- Assert(i == stackLiteralInitFldDataCount);
- bailOutInfo->stackLiteralBailOutInfoCount = stackLiteralInitFldDataCount;
- bailOutInfo->stackLiteralBailOutInfo = stackLiteralBailOutInfo;
- }
- }
- if (TrackArgumentsObject())
- {
- this->CaptureArguments(block, bailOutInfo, this->func->m_alloc);
- }
- if (block->globOptData.callSequence && !block->globOptData.callSequence->Empty())
- {
- uint currentArgOutCount = 0;
- uint startCallNumber = block->globOptData.startCallCount;
- bailOutInfo->startCallInfo = JitAnewArray(this->func->m_alloc, BailOutInfo::StartCallInfo, startCallNumber);
- bailOutInfo->startCallCount = startCallNumber;
- // Save the start call's func to identify the function (inlined) that the call sequence is for
- // We might not have any arg out yet to get the function from
- bailOutInfo->startCallFunc = JitAnewArray(this->func->m_alloc, Func *, startCallNumber);
- #ifdef _M_IX86
- bailOutInfo->inlinedStartCall = BVFixed::New(startCallNumber, this->func->m_alloc, false);
- #endif
- uint totalOutParamCount = block->globOptData.totalOutParamCount;
- bailOutInfo->totalOutParamCount = totalOutParamCount;
- bailOutInfo->argOutSyms = JitAnewArrayZ(this->func->m_alloc, StackSym *, totalOutParamCount);
- FOREACH_SLISTBASE_ENTRY(IR::Opnd *, opnd, block->globOptData.callSequence)
- {
- if(opnd->GetStackSym()->HasArgSlotNum())
- {
- StackSym * sym;
- if(opnd->IsSymOpnd())
- {
- sym = opnd->AsSymOpnd()->m_sym->AsStackSym();
- Assert(sym->IsArgSlotSym());
- Assert(sym->m_isSingleDef);
- Assert(sym->m_instrDef->m_opcode == Js::OpCode::ArgOut_A
- || sym->m_instrDef->m_opcode == Js::OpCode::ArgOut_A_Inline
- || sym->m_instrDef->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn
- || sym->m_instrDef->m_opcode == Js::OpCode::ArgOut_A_SpreadArg
- || sym->m_instrDef->m_opcode == Js::OpCode::ArgOut_A_Dynamic);
- }
- else
- {
- sym = opnd->GetStackSym();
- Assert(this->currentBlock->globOptData.FindValue(sym));
- // StackSym args need to be re-captured
- if (!argsToCapture)
- {
- argsToCapture = JitAnew(this->tempAlloc, BVSparse<JitArenaAllocator>, this->tempAlloc);
- }
- argsToCapture->Set(sym->m_id);
- }
- Assert(totalOutParamCount != 0);
- Assert(totalOutParamCount > currentArgOutCount);
- currentArgOutCount++;
- #pragma prefast(suppress:26000, "currentArgOutCount is never 0");
- bailOutInfo->argOutSyms[totalOutParamCount - currentArgOutCount] = sym;
- // Note that there could be ArgOuts below current bailout instr that belong to current call (currentArgOutCount < argOutCount),
- // in which case we will have nulls in argOutSyms[] in start of section for current call, because we fill from tail.
- // Example: StartCall 3, ArgOut1,.. ArgOut2, Bailout,.. Argout3 -> [NULL, ArgOut1, ArgOut2].
- }
- else
- {
- Assert(opnd->IsRegOpnd());
- StackSym * sym = opnd->AsRegOpnd()->m_sym;
- Assert(!sym->IsArgSlotSym());
- Assert(sym->m_isSingleDef);
- Assert(sym->m_instrDef->m_opcode == Js::OpCode::StartCall);
- Assert(startCallNumber != 0);
- startCallNumber--;
- bailOutInfo->startCallFunc[startCallNumber] = sym->m_instrDef->m_func;
- #ifdef _M_IX86
- if (sym->m_isInlinedArgSlot)
- {
- bailOutInfo->inlinedStartCall->Set(startCallNumber);
- }
- #endif
- uint argOutCount = sym->m_instrDef->GetArgOutCount(/*getInterpreterArgOutCount*/ true);
- Assert(totalOutParamCount >= argOutCount);
- Assert(argOutCount >= currentArgOutCount);
- bailOutInfo->RecordStartCallInfo(startCallNumber, sym->m_instrDef);
- totalOutParamCount -= argOutCount;
- currentArgOutCount = 0;
- }
- }
- NEXT_SLISTBASE_ENTRY;
- Assert(totalOutParamCount == 0);
- Assert(startCallNumber == 0);
- Assert(currentArgOutCount == 0);
- }
- // Save the constant values that we know so we can restore them directly.
- // This allows us to dead store the constant value assign.
- this->CaptureValues(block, bailOutInfo, argsToCapture);
- }
- void
- GlobOpt::FillBailOutInfo(BasicBlock *block, _In_ IR::Instr * instr)
- {
- AssertMsg(!this->isCallHelper, "Bail out can't be inserted the middle of CallHelper sequence");
- Assert(instr->HasBailOutInfo());
- if (this->isRecursiveCallOnLandingPad)
- {
- Assert(block->IsLandingPad());
- Loop * loop = block->next->loop;
- EnsureBailTarget(loop);
- if (instr->GetBailOutInfo() != loop->bailOutInfo)
- {
- instr->ReplaceBailOutInfo(loop->bailOutInfo);
- }
- return;
- }
- FillBailOutInfo(block, instr->GetBailOutInfo());
- }
- IR::ByteCodeUsesInstr *
- GlobOpt::InsertByteCodeUses(IR::Instr * instr, bool includeDef)
- {
- IR::ByteCodeUsesInstr * byteCodeUsesInstr = nullptr;
- if (!this->byteCodeUses)
- {
- Assert(this->isAsmJSFunc);
- return nullptr;
- }
- IR::RegOpnd * dstOpnd = nullptr;
- if (includeDef)
- {
- IR::Opnd * opnd = instr->GetDst();
- if (opnd && opnd->IsRegOpnd())
- {
- dstOpnd = opnd->AsRegOpnd();
- if (dstOpnd->GetIsJITOptimizedReg() || !dstOpnd->m_sym->HasByteCodeRegSlot())
- {
- dstOpnd = nullptr;
- }
- }
- }
- if (!this->byteCodeUses->IsEmpty() || this->propertySymUse || dstOpnd != nullptr)
- {
- if (instr->GetByteCodeOffset() != Js::Constants::NoByteCodeOffset || !instr->HasBailOutInfo())
- {
- byteCodeUsesInstr = IR::ByteCodeUsesInstr::New(instr);
- }
- else
- {
- byteCodeUsesInstr = IR::ByteCodeUsesInstr::New(instr->m_func, instr->GetBailOutInfo()->bailOutOffset);
- }
- if (!this->byteCodeUses->IsEmpty())
- {
- byteCodeUsesInstr->SetBV(byteCodeUses->CopyNew(instr->m_func->m_alloc));
- }
- if (dstOpnd != nullptr)
- {
- byteCodeUsesInstr->SetFakeDst(dstOpnd);
- }
- if (this->propertySymUse)
- {
- byteCodeUsesInstr->propertySymUse = this->propertySymUse;
- }
- instr->InsertBefore(byteCodeUsesInstr);
- }
- JitAdelete(this->alloc, this->byteCodeUses);
- this->byteCodeUses = nullptr;
- this->propertySymUse = nullptr;
- return byteCodeUsesInstr;
- }
- IR::ByteCodeUsesInstr *
- GlobOpt::ConvertToByteCodeUses(IR::Instr * instr)
- {
- #if DBG
- PropertySym *propertySymUseBefore = NULL;
- Assert(this->byteCodeUses == nullptr);
- this->byteCodeUsesBeforeOpt->ClearAll();
- GlobOpt::TrackByteCodeSymUsed(instr, this->byteCodeUsesBeforeOpt, &propertySymUseBefore);
- #endif
- this->CaptureByteCodeSymUses(instr);
- IR::ByteCodeUsesInstr * byteCodeUsesInstr = this->InsertByteCodeUses(instr, true);
- instr->Remove();
- if (byteCodeUsesInstr)
- {
- byteCodeUsesInstr->AggregateFollowingByteCodeUses();
- }
- return byteCodeUsesInstr;
- }
- bool
- GlobOpt::MayNeedBailOut(Loop * loop) const
- {
- Assert(this->IsLoopPrePass());
- return loop->CanHoistInvariants() || this->DoFieldCopyProp(loop) ;
- }
- bool
- GlobOpt::MaySrcNeedBailOnImplicitCall(IR::Opnd const * opnd, Value const * val)
- {
- switch (opnd->GetKind())
- {
- case IR::OpndKindAddr:
- case IR::OpndKindFloatConst:
- case IR::OpndKindIntConst:
- return false;
- case IR::OpndKindReg:
- // Only need implicit call if the operation will call ToPrimitive and we haven't prove
- // that it is already a primitive
- return
- !(val && val->GetValueInfo()->IsPrimitive()) &&
- !opnd->AsRegOpnd()->GetValueType().IsPrimitive() &&
- !opnd->AsRegOpnd()->m_sym->IsInt32() &&
- !opnd->AsRegOpnd()->m_sym->IsFloat64() &&
- !opnd->AsRegOpnd()->m_sym->IsFloatConst() &&
- !opnd->AsRegOpnd()->m_sym->IsIntConst();
- case IR::OpndKindSym:
- if (opnd->AsSymOpnd()->IsPropertySymOpnd())
- {
- IR::PropertySymOpnd const * propertySymOpnd = opnd->AsSymOpnd()->AsPropertySymOpnd();
- if (!propertySymOpnd->MayHaveImplicitCall())
- {
- return false;
- }
- }
- return true;
- default:
- return true;
- };
- }
- bool
- GlobOpt::IsImplicitCallBailOutCurrentlyNeeded(IR::Instr * instr, Value const * src1Val, Value const * src2Val) const
- {
- Assert(!this->IsLoopPrePass());
- return this->IsImplicitCallBailOutCurrentlyNeeded(instr, src1Val, src2Val, this->currentBlock,
- (!this->currentBlock->globOptData.liveFields->IsEmpty()), !this->currentBlock->IsLandingPad(), true);
- }
- bool
- GlobOpt::IsImplicitCallBailOutCurrentlyNeeded(IR::Instr * instr, Value const * src1Val, Value const * src2Val, BasicBlock const * block, bool hasLiveFields, bool mayNeedImplicitCallBailOut, bool isForwardPass) const
- {
- if (mayNeedImplicitCallBailOut &&
- !instr->CallsAccessor() &&
- (
- NeedBailOnImplicitCallForLiveValues(block, isForwardPass) ||
- NeedBailOnImplicitCallForCSE(block, isForwardPass) ||
- NeedBailOnImplicitCallWithFieldOpts(block->loop, hasLiveFields) ||
- NeedBailOnImplicitCallForArrayCheckHoist(block, isForwardPass) ||
- (instr->HasBailOutInfo() && (instr->GetBailOutKind() & IR::BailOutMarkTempObject) != 0)
- ) &&
- (!instr->HasTypeCheckBailOut() && MayNeedBailOnImplicitCall(instr, src1Val, src2Val)))
- {
- return true;
- }
- #if DBG
- if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutAtEveryImplicitCallFlag) &&
- !instr->HasBailOutInfo() && MayNeedBailOnImplicitCall(instr, nullptr, nullptr))
- {
- // always add implicit call bailout even if we don't need it, but only on opcode that supports it
- return true;
- }
- #endif
- return false;
- }
- bool
- GlobOpt::IsTypeCheckProtected(const IR::Instr * instr)
- {
- #if DBG
- IR::Opnd* dst = instr->GetDst();
- IR::Opnd* src1 = instr->GetSrc1();
- IR::Opnd* src2 = instr->GetSrc2();
- AssertMsg(!dst || !dst->IsSymOpnd() || !dst->AsSymOpnd()->IsPropertySymOpnd() ||
- !src1 || !src1->IsSymOpnd() || !src1->AsSymOpnd()->IsPropertySymOpnd(), "No instruction should have a src1 and dst be a PropertySymOpnd.");
- AssertMsg(!src2 || !src2->IsSymOpnd() || !src2->AsSymOpnd()->IsPropertySymOpnd(), "No instruction should have a src2 be a PropertySymOpnd.");
- #endif
- IR::Opnd * opnd = instr->GetDst();
- if (opnd && opnd->IsSymOpnd() && opnd->AsSymOpnd()->IsPropertySymOpnd())
- {
- return opnd->AsPropertySymOpnd()->IsTypeCheckProtected();
- }
- opnd = instr->GetSrc1();
- if (opnd && opnd->IsSymOpnd() && opnd->AsSymOpnd()->IsPropertySymOpnd())
- {
- return opnd->AsPropertySymOpnd()->IsTypeCheckProtected();
- }
- return false;
- }
- bool
- GlobOpt::NeedsTypeCheckBailOut(const IR::Instr *instr, IR::PropertySymOpnd *propertySymOpnd, bool isStore, bool* pIsTypeCheckProtected, IR::BailOutKind *pBailOutKind)
- {
- if (instr->m_opcode == Js::OpCode::CheckPropertyGuardAndLoadType || instr->m_opcode == Js::OpCode::LdMethodFldPolyInlineMiss)
- {
- return false;
- }
- // CheckFixedFld always requires a type check and bailout either at the instruction or upstream.
- Assert(instr->m_opcode != Js::OpCode::CheckFixedFld || (propertySymOpnd->UsesFixedValue() && propertySymOpnd->MayNeedTypeCheckProtection()));
- if (propertySymOpnd->MayNeedTypeCheckProtection())
- {
- bool isCheckFixedFld = instr->m_opcode == Js::OpCode::CheckFixedFld;
- AssertMsg(!isCheckFixedFld || !PHASE_OFF(Js::FixedMethodsPhase, instr->m_func) ||
- !PHASE_OFF(Js::UseFixedDataPropsPhase, instr->m_func), "CheckFixedFld with fixed method/data phase disabled?");
- Assert(!isStore || !isCheckFixedFld);
- // We don't share caches between field loads and stores. We should never have a field store involving a proto cache.
- Assert(!isStore || !propertySymOpnd->IsLoadedFromProto());
- if (propertySymOpnd->NeedsTypeCheckAndBailOut())
- {
- *pBailOutKind = propertySymOpnd->HasEquivalentTypeSet() && !propertySymOpnd->MustDoMonoCheck() ?
- (isCheckFixedFld ? IR::BailOutFailedEquivalentFixedFieldTypeCheck : IR::BailOutFailedEquivalentTypeCheck) :
- (isCheckFixedFld ? IR::BailOutFailedFixedFieldTypeCheck : IR::BailOutFailedTypeCheck);
- return true;
- }
- else
- {
- *pIsTypeCheckProtected = propertySymOpnd->IsTypeCheckProtected();
- *pBailOutKind = IR::BailOutInvalid;
- return false;
- }
- }
- else
- {
- Assert(instr->m_opcode != Js::OpCode::CheckFixedFld);
- *pBailOutKind = IR::BailOutInvalid;
- return false;
- }
- }
- bool
- GlobOpt::MayNeedBailOnImplicitCall(IR::Instr const * instr, Value const * src1Val, Value const * src2Val)
- {
- if (!instr->HasAnyImplicitCalls())
- {
- return false;
- }
- bool isLdElem = false;
- switch (instr->m_opcode)
- {
- case Js::OpCode::LdLen_A:
- {
- const ValueType baseValueType(instr->GetSrc1()->GetValueType());
- return
- !(
- baseValueType.IsString() ||
- baseValueType.IsArray() ||
- (instr->HasBailOutInfo() && instr->GetBailOutKindNoBits() == IR::BailOutOnIrregularLength) // guarantees no implicit calls
- );
- }
- case Js::OpCode::LdElemI_A:
- case Js::OpCode::LdMethodElem:
- case Js::OpCode::InlineArrayPop:
- isLdElem = true;
- // fall-through
- case Js::OpCode::StElemI_A:
- case Js::OpCode::StElemI_A_Strict:
- case Js::OpCode::InlineArrayPush:
- {
- if(!instr->HasBailOutInfo())
- {
- return true;
- }
- // The following bailout kinds already prevent implicit calls from happening. Any conditions that could trigger an
- // implicit call result in a pre-op bailout.
- const IR::BailOutKind bailOutKind = instr->GetBailOutKind();
- return
- !(
- (bailOutKind & ~IR::BailOutKindBits) == IR::BailOutConventionalTypedArrayAccessOnly ||
- bailOutKind & IR::BailOutOnArrayAccessHelperCall ||
- (isLdElem && bailOutKind & IR::BailOutConventionalNativeArrayAccessOnly)
- );
- }
- case Js::OpCode::NewScObjectNoCtor:
- if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & ~IR::BailOutKindBits) == IR::BailOutFailedCtorGuardCheck)
- {
- // No helper call with this bailout.
- return false;
- }
- break;
- default:
- break;
- }
- if (OpCodeAttr::HasImplicitCall(instr->m_opcode))
- {
- // Operation has an implicit call regardless of operand attributes.
- return true;
- }
- IR::Opnd const * opnd = instr->GetDst();
- if (opnd)
- {
- switch (opnd->GetKind())
- {
- case IR::OpndKindReg:
- break;
- case IR::OpndKindSym:
- // No implicit call if we are just storing to a stack sym. Note that stores to non-configurable root
- // object fields may still need implicit call bailout. That's because a non-configurable field may still
- // become read-only and thus the store field will not take place (or throw in strict mode). Hence, we
- // can't optimize (e.g. copy prop) across such field stores.
- if (opnd->AsSymOpnd()->m_sym->IsStackSym())
- {
- return false;
- }
- if (opnd->AsSymOpnd()->IsPropertySymOpnd())
- {
- IR::PropertySymOpnd const * propertySymOpnd = opnd->AsSymOpnd()->AsPropertySymOpnd();
- if (!propertySymOpnd->MayHaveImplicitCall())
- {
- return false;
- }
- }
- return true;
- case IR::OpndKindIndir:
- return true;
- default:
- Assume(UNREACHED);
- }
- }
- opnd = instr->GetSrc1();
- if (opnd != nullptr && MaySrcNeedBailOnImplicitCall(opnd, src1Val))
- {
- return true;
- }
- opnd = instr->GetSrc2();
- if (opnd != nullptr && MaySrcNeedBailOnImplicitCall(opnd, src2Val))
- {
- return true;
- }
- return false;
- }
- void
- GlobOpt::GenerateBailAfterOperation(IR::Instr * *const pInstr, IR::BailOutKind kind)
- {
- Assert(pInstr && *pInstr);
- IR::Instr* instr = *pInstr;
- IR::Instr * nextInstr = instr->GetNextByteCodeInstr();
- IR::Instr * bailOutInstr = instr->ConvertToBailOutInstr(nextInstr, kind);
- if (this->currentBlock->GetLastInstr() == instr)
- {
- this->currentBlock->SetLastInstr(bailOutInstr);
- }
- FillBailOutInfo(this->currentBlock, bailOutInstr);
- *pInstr = bailOutInstr;
- }
- void
- GlobOpt::GenerateBailAtOperation(IR::Instr * *const pInstr, const IR::BailOutKind bailOutKind)
- {
- Assert(pInstr);
- IR::Instr * instr = *pInstr;
- Assert(instr);
- Assert(instr->GetByteCodeOffset() != Js::Constants::NoByteCodeOffset);
- Assert(bailOutKind != IR::BailOutInvalid);
- IR::Instr * bailOutInstr = instr->ConvertToBailOutInstr(instr, bailOutKind);
- if (this->currentBlock->GetLastInstr() == instr)
- {
- this->currentBlock->SetLastInstr(bailOutInstr);
- }
- FillBailOutInfo(currentBlock, bailOutInstr);
- *pInstr = bailOutInstr;
- }
- IR::Instr *
- GlobOpt::EnsureBailTarget(Loop * loop)
- {
- BailOutInfo * bailOutInfo = loop->bailOutInfo;
- IR::Instr * bailOutInstr = bailOutInfo->bailOutInstr;
- if (bailOutInstr == nullptr)
- {
- bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailTarget, IR::BailOutShared, bailOutInfo, bailOutInfo->bailOutFunc);
- loop->landingPad->InsertAfter(bailOutInstr);
- }
- return bailOutInstr;
- }
|