| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "Backend.h"
- void
- GlobOptBlockData::NullOutBlockData(GlobOpt* globOpt, Func* func)
- {
- this->globOpt = globOpt;
- this->symToValueMap = nullptr;
- this->exprToValueMap = nullptr;
- this->liveFields = nullptr;
- this->maybeWrittenTypeSyms = nullptr;
- this->isTempSrc = nullptr;
- this->liveVarSyms = nullptr;
- this->liveInt32Syms = nullptr;
- this->liveLossyInt32Syms = nullptr;
- this->liveFloat64Syms = nullptr;
- this->argObjSyms = nullptr;
- this->maybeTempObjectSyms = nullptr;
- this->canStoreTempObjectSyms = nullptr;
- this->valuesToKillOnCalls = nullptr;
- this->inductionVariables = nullptr;
- this->availableIntBoundChecks = nullptr;
- this->callSequence = nullptr;
- this->startCallCount = 0;
- this->argOutCount = 0;
- this->totalOutParamCount = 0;
- this->inlinedArgOutSize = 0;
- this->hasCSECandidates = false;
- this->curFunc = func;
- this->stackLiteralInitFldDataMap = nullptr;
- if (this->capturedValues)
- {
- this->capturedValues->DecrementRefCount();
- }
- this->capturedValues = nullptr;
- this->changedSyms = nullptr;
- this->OnDataUnreferenced();
- }
- void
- GlobOptBlockData::InitBlockData(GlobOpt* globOpt, Func* func)
- {
- this->globOpt = globOpt;
- JitArenaAllocator *const alloc = this->globOpt->alloc;
- this->symToValueMap = GlobHashTable::New(alloc, 64);
- this->exprToValueMap = ExprHashTable::New(alloc, 64);
- this->liveFields = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveArrayValues = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->isTempSrc = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveVarSyms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveInt32Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveLossyInt32Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveFloat64Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->argObjSyms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->maybeTempObjectSyms = nullptr;
- this->canStoreTempObjectSyms = nullptr;
- this->valuesToKillOnCalls = JitAnew(alloc, ValueSet, alloc);
- if(this->globOpt->DoBoundCheckHoist())
- {
- this->inductionVariables = this->globOpt->IsLoopPrePass() ? JitAnew(alloc, InductionVariableSet, alloc) : nullptr;
- this->availableIntBoundChecks = JitAnew(alloc, IntBoundCheckSet, alloc);
- }
- this->maybeWrittenTypeSyms = nullptr;
- this->callSequence = nullptr;
- this->startCallCount = 0;
- this->argOutCount = 0;
- this->totalOutParamCount = 0;
- this->inlinedArgOutSize = 0;
- this->hasCSECandidates = false;
- this->curFunc = func;
- this->stackLiteralInitFldDataMap = nullptr;
- this->changedSyms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->OnDataInitialized(alloc);
- }
- void
- GlobOptBlockData::ReuseBlockData(GlobOptBlockData *fromData)
- {
- this->globOpt = fromData->globOpt;
- // Reuse dead map
- this->symToValueMap = fromData->symToValueMap;
- this->exprToValueMap = fromData->exprToValueMap;
- this->liveFields = fromData->liveFields;
- this->liveArrayValues = fromData->liveArrayValues;
- this->maybeWrittenTypeSyms = fromData->maybeWrittenTypeSyms;
- this->isTempSrc = fromData->isTempSrc;
- this->liveVarSyms = fromData->liveVarSyms;
- this->liveInt32Syms = fromData->liveInt32Syms;
- this->liveLossyInt32Syms = fromData->liveLossyInt32Syms;
- this->liveFloat64Syms = fromData->liveFloat64Syms;
- if (this->globOpt->TrackArgumentsObject())
- {
- this->argObjSyms = fromData->argObjSyms;
- }
- this->maybeTempObjectSyms = fromData->maybeTempObjectSyms;
- this->canStoreTempObjectSyms = fromData->canStoreTempObjectSyms;
- this->curFunc = fromData->curFunc;
- this->valuesToKillOnCalls = fromData->valuesToKillOnCalls;
- this->inductionVariables = fromData->inductionVariables;
- this->availableIntBoundChecks = fromData->availableIntBoundChecks;
- this->callSequence = fromData->callSequence;
- this->startCallCount = fromData->startCallCount;
- this->argOutCount = fromData->argOutCount;
- this->totalOutParamCount = fromData->totalOutParamCount;
- this->inlinedArgOutSize = fromData->inlinedArgOutSize;
- this->hasCSECandidates = fromData->hasCSECandidates;
- this->stackLiteralInitFldDataMap = fromData->stackLiteralInitFldDataMap;
- this->changedSyms = fromData->changedSyms;
- this->capturedValues = fromData->capturedValues;
- if (this->capturedValues)
- {
- this->capturedValues->IncrementRefCount();
- }
- this->OnDataReused(fromData);
- }
- void
- GlobOptBlockData::CopyBlockData(GlobOptBlockData *fromData)
- {
- this->globOpt = fromData->globOpt;
- this->symToValueMap = fromData->symToValueMap;
- this->exprToValueMap = fromData->exprToValueMap;
- this->liveFields = fromData->liveFields;
- this->liveArrayValues = fromData->liveArrayValues;
- this->maybeWrittenTypeSyms = fromData->maybeWrittenTypeSyms;
- this->isTempSrc = fromData->isTempSrc;
- this->liveVarSyms = fromData->liveVarSyms;
- this->liveInt32Syms = fromData->liveInt32Syms;
- this->liveLossyInt32Syms = fromData->liveLossyInt32Syms;
- this->liveFloat64Syms = fromData->liveFloat64Syms;
- this->argObjSyms = fromData->argObjSyms;
- this->maybeTempObjectSyms = fromData->maybeTempObjectSyms;
- this->canStoreTempObjectSyms = fromData->canStoreTempObjectSyms;
- this->curFunc = fromData->curFunc;
- this->valuesToKillOnCalls = fromData->valuesToKillOnCalls;
- this->inductionVariables = fromData->inductionVariables;
- this->availableIntBoundChecks = fromData->availableIntBoundChecks;
- this->callSequence = fromData->callSequence;
- this->startCallCount = fromData->startCallCount;
- this->argOutCount = fromData->argOutCount;
- this->totalOutParamCount = fromData->totalOutParamCount;
- this->inlinedArgOutSize = fromData->inlinedArgOutSize;
- this->hasCSECandidates = fromData->hasCSECandidates;
- this->changedSyms = fromData->changedSyms;
- this->capturedValues = fromData->capturedValues;
- this->stackLiteralInitFldDataMap = fromData->stackLiteralInitFldDataMap;
- this->OnDataReused(fromData);
- }
- void
- GlobOptBlockData::DeleteBlockData()
- {
- JitArenaAllocator *const alloc = this->globOpt->alloc;
- this->symToValueMap->Delete();
- this->exprToValueMap->Delete();
- JitAdelete(alloc, this->liveFields);
- JitAdelete(alloc, this->liveArrayValues);
- if (this->maybeWrittenTypeSyms)
- {
- JitAdelete(alloc, this->maybeWrittenTypeSyms);
- }
- JitAdelete(alloc, this->isTempSrc);
- JitAdelete(alloc, this->liveVarSyms);
- JitAdelete(alloc, this->liveInt32Syms);
- JitAdelete(alloc, this->liveLossyInt32Syms);
- JitAdelete(alloc, this->liveFloat64Syms);
- if (this->argObjSyms)
- {
- JitAdelete(alloc, this->argObjSyms);
- }
- if (this->maybeTempObjectSyms)
- {
- JitAdelete(alloc, this->maybeTempObjectSyms);
- if (this->canStoreTempObjectSyms)
- {
- JitAdelete(alloc, this->canStoreTempObjectSyms);
- }
- }
- else
- {
- Assert(!this->canStoreTempObjectSyms);
- }
- JitAdelete(alloc, this->valuesToKillOnCalls);
- if(this->inductionVariables)
- {
- JitAdelete(alloc, this->inductionVariables);
- }
- if(this->availableIntBoundChecks)
- {
- JitAdelete(alloc, this->availableIntBoundChecks);
- }
- if (this->stackLiteralInitFldDataMap)
- {
- JitAdelete(alloc, this->stackLiteralInitFldDataMap);
- }
- JitAdelete(alloc, this->changedSyms);
- this->changedSyms = nullptr;
- if (this->capturedValues && this->capturedValues->DecrementRefCount() == 0)
- {
- JitAdelete(this->curFunc->m_alloc, this->capturedValues);
- this->capturedValues = nullptr;
- }
- this->OnDataDeleted();
- }
- void GlobOptBlockData::CloneBlockData(BasicBlock *const toBlockContext, BasicBlock *const fromBlock)
- {
- GlobOptBlockData *const fromData = &fromBlock->globOptData;
- this->globOpt = fromData->globOpt;
- JitArenaAllocator *const alloc = this->globOpt->alloc;
- this->symToValueMap = fromData->symToValueMap->Copy();
- this->exprToValueMap = fromData->exprToValueMap->Copy();
- // Clone the values as well to allow for flow-sensitive ValueInfo
- this->globOpt->CloneValues(toBlockContext, this, fromData);
- if(this->globOpt->DoBoundCheckHoist())
- {
- this->globOpt->CloneBoundCheckHoistBlockData(toBlockContext, this, fromBlock, fromData);
- }
- this->liveFields = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveFields->Copy(fromData->liveFields);
- this->liveArrayValues = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveArrayValues->Copy(fromData->liveArrayValues);
- if (fromData->maybeWrittenTypeSyms)
- {
- this->maybeWrittenTypeSyms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->maybeWrittenTypeSyms->Copy(fromData->maybeWrittenTypeSyms);
- }
- this->isTempSrc = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->isTempSrc->Copy(fromData->isTempSrc);
- this->liveVarSyms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveVarSyms->Copy(fromData->liveVarSyms);
- this->liveInt32Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveInt32Syms->Copy(fromData->liveInt32Syms);
- this->liveLossyInt32Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveLossyInt32Syms->Copy(fromData->liveLossyInt32Syms);
- this->liveFloat64Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->liveFloat64Syms->Copy(fromData->liveFloat64Syms);
- if (this->globOpt->TrackArgumentsObject() && fromData->argObjSyms)
- {
- this->argObjSyms = fromData->argObjSyms->CopyNew(alloc);
- }
- if (fromData->maybeTempObjectSyms && !fromData->maybeTempObjectSyms->IsEmpty())
- {
- this->maybeTempObjectSyms = fromData->maybeTempObjectSyms->CopyNew(alloc);
- if (fromData->canStoreTempObjectSyms && !fromData->canStoreTempObjectSyms->IsEmpty())
- {
- this->canStoreTempObjectSyms = fromData->canStoreTempObjectSyms->CopyNew(alloc);
- }
- }
- else
- {
- Assert(fromData->canStoreTempObjectSyms == nullptr || fromData->canStoreTempObjectSyms->IsEmpty());
- }
- this->curFunc = fromData->curFunc;
- if (fromData->callSequence != nullptr)
- {
- this->callSequence = JitAnew(alloc, SListBase<IR::Opnd *>);
- fromData->callSequence->CopyTo(alloc, *(this->callSequence));
- }
- else
- {
- this->callSequence = nullptr;
- }
- this->startCallCount = fromData->startCallCount;
- this->argOutCount = fromData->argOutCount;
- this->totalOutParamCount = fromData->totalOutParamCount;
- this->inlinedArgOutSize = fromData->inlinedArgOutSize;
- this->hasCSECandidates = fromData->hasCSECandidates;
- // Although we don't need the data on loop pre pass, we need to do it for the loop header
- // because we capture the loop header bailout on loop prepass
- if (fromData->stackLiteralInitFldDataMap != nullptr &&
- (!this->globOpt->IsLoopPrePass() || (toBlockContext->isLoopHeader && toBlockContext->loop == this->globOpt->rootLoopPrePass)))
- {
- this->stackLiteralInitFldDataMap = fromData->stackLiteralInitFldDataMap->Clone();
- }
- else
- {
- this->stackLiteralInitFldDataMap = nullptr;
- }
- this->changedSyms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
- this->changedSyms->Copy(fromData->changedSyms);
- this->capturedValues = fromData->capturedValues;
- if (this->capturedValues)
- {
- this->capturedValues->IncrementRefCount();
- }
- Assert(fromData->HasData());
- this->OnDataInitialized(alloc);
- }
- void GlobOptBlockData::RemoveUnavailableCandidates(PRECandidates * candidates)
- {
- // In case of multiple back-edges to the loop, make sure the candidates are still valid.
- FOREACH_SLIST_ENTRY_EDITING(GlobHashBucket*, candidate, candidates->candidatesList, iter)
- {
- Value *candidateValue = candidate->element;
- PropertySym *candidatePropertySym = candidate->value->AsPropertySym();
- ValueNumber valueNumber = candidateValue->GetValueNumber();
- Sym *symStore = candidateValue->GetValueInfo()->GetSymStore();
- Value *blockValue = this->FindValue(candidatePropertySym);
- if (blockValue && blockValue->GetValueNumber() == valueNumber
- && blockValue->GetValueInfo()->GetSymStore() == symStore)
- {
- Value *symStoreValue = this->FindValue(symStore);
- if (symStoreValue && symStoreValue->GetValueNumber() == valueNumber)
- {
- continue;
- }
- }
- iter.RemoveCurrent();
- Assert(candidates->candidatesToProcess->Test(candidatePropertySym->m_id));
- candidates->candidatesToProcess->Clear(candidatePropertySym->m_id);
- } NEXT_SLIST_ENTRY_EDITING;
- }
- template <typename CapturedList, typename CapturedItemsAreEqual>
- void
- GlobOptBlockData::MergeCapturedValues(
- SListBase<CapturedList> * toList,
- SListBase<CapturedList> * fromList,
- CapturedItemsAreEqual itemsAreEqual)
- {
- typename SListBase<CapturedList>::Iterator iterTo(toList);
- typename SListBase<CapturedList>::Iterator iterFrom(fromList);
- bool hasTo = iterTo.Next();
- bool hasFrom = fromList == nullptr ? false : iterFrom.Next();
- // to be conservative, only copy the captured value for common sym Ids
- // in from and to CapturedList, mark all non-common sym Ids for re-capture
- while (hasFrom && hasTo)
- {
- Sym * symFrom = iterFrom.Data().Key();
- Sym * symTo = iterTo.Data().Key();
- if (symFrom->m_id < symTo->m_id)
- {
- this->changedSyms->Set(symFrom->m_id);
- hasFrom = iterFrom.Next();
- }
- else if(symFrom->m_id > symTo->m_id)
- {
- this->changedSyms->Set(symTo->m_id);
- hasTo = iterTo.Next();
- }
- else
- {
- if (!itemsAreEqual(&iterFrom.Data(), &iterTo.Data()))
- {
- this->changedSyms->Set(symTo->m_id);
- }
- hasFrom = iterFrom.Next();
- hasTo = iterTo.Next();
- }
- }
- bool hasRemain = hasFrom || hasTo;
- if (hasRemain)
- {
- typename SListBase<CapturedList>::Iterator iterRemain(hasFrom ? iterFrom : iterTo);
- do
- {
- Sym * symRemain = iterRemain.Data().Key();
- this->changedSyms->Set(symRemain->m_id);
- hasRemain = iterRemain.Next();
- } while (hasRemain);
- }
- }
- void
- GlobOptBlockData::MergeBlockData(
- BasicBlock *toBlock,
- BasicBlock *fromBlock,
- BVSparse<JitArenaAllocator> *const symsRequiringCompensation,
- BVSparse<JitArenaAllocator> *const symsCreatedForMerge,
- bool forceTypeSpecOnLoopHeader)
- {
- GlobOptBlockData *fromData = &(fromBlock->globOptData);
- if(this->globOpt->DoBoundCheckHoist())
- {
- // Do this before merging values so that it can see whether a sym's value was changed on one side or the other
- this->globOpt->MergeBoundCheckHoistBlockData(toBlock, this, fromBlock, fromData);
- }
- bool isLoopBackEdge = toBlock->isLoopHeader;
- this->MergeValueMaps(toBlock, fromBlock, symsRequiringCompensation, symsCreatedForMerge);
- this->globOpt->InsertCloneStrs(toBlock, this, fromData);
- this->liveFields->And(fromData->liveFields);
- this->liveArrayValues->And(fromData->liveArrayValues);
- this->isTempSrc->And(fromData->isTempSrc);
- this->hasCSECandidates &= fromData->hasCSECandidates;
- this->changedSyms->Or(fromData->changedSyms);
- if (this->capturedValues == nullptr)
- {
- this->capturedValues = fromData->capturedValues;
- if (this->capturedValues)
- {
- this->capturedValues->IncrementRefCount();
- }
- }
- else
- {
- this->MergeCapturedValues(
- &this->capturedValues->constantValues,
- fromData->capturedValues == nullptr ? nullptr : &fromData->capturedValues->constantValues,
- [&](ConstantStackSymValue * symValueFrom, ConstantStackSymValue * symValueTo)
- {
- Assert(symValueFrom->Key()->m_id == symValueTo->Key()->m_id);
- return symValueFrom->Value().IsEqual(symValueTo->Value());
- });
- this->MergeCapturedValues(
- &this->capturedValues->copyPropSyms,
- fromData->capturedValues == nullptr ? nullptr : &fromData->capturedValues->copyPropSyms,
- [&](CopyPropSyms * copyPropSymFrom, CopyPropSyms * copyPropSymTo)
- {
- Assert(copyPropSymFrom->Key()->m_id == copyPropSymTo->Key()->m_id);
- if (copyPropSymFrom->Value()->m_id == copyPropSymTo->Value()->m_id)
- {
- Value * fromVal = fromData->FindValue(copyPropSymFrom->Key());
- Value * toVal = this->FindValue(copyPropSymFrom->Key());
- return fromVal && toVal && fromVal->IsEqualTo(toVal);
- }
- return false;
- });
- }
- if (fromData->maybeWrittenTypeSyms)
- {
- if (this->maybeWrittenTypeSyms == nullptr)
- {
- this->maybeWrittenTypeSyms = JitAnew(this->globOpt->alloc, BVSparse<JitArenaAllocator>, this->globOpt->alloc);
- this->maybeWrittenTypeSyms->Copy(fromData->maybeWrittenTypeSyms);
- }
- else
- {
- this->maybeWrittenTypeSyms->Or(fromData->maybeWrittenTypeSyms);
- }
- }
- {
- // - Keep the var sym live if any of the following is true:
- // - The var sym is live on both sides
- // - The var sym is the only live sym that contains the lossless value of the sym on a side (that is, the lossless
- // int32 sym is not live, and the float64 sym is not live on that side), and the sym of any type is live on the
- // other side
- // - On a side, the var and float64 syms are live, the lossless int32 sym is not live, the sym's merged value is
- // likely int, and the sym of any type is live on the other side. Since the value is likely int, it may be
- // int-specialized (with lossless conversion) later. Keeping only the float64 sym live requires doing a lossless
- // conversion from float64 to int32, with bailout if the value of the float is not a true 32-bit integer. Checking
- // that is costly, and if the float64 sym is converted back to var, it does not become a tagged int, causing a
- // guaranteed bailout if a lossless conversion to int happens later. Keep the var sym live to preserve its
- // tagged-ness so that it can be int-specialized while avoiding unnecessary bailouts.
- // - Keep the int32 sym live if it's live on both sides
- // - Mark the sym as lossy if it's lossy on any side
- // - Keep the float64 sym live if it's live on a side and the sym of a specialized lossless type is live on the other
- // side
- //
- // fromData.temp =
- // (fromData.var - (fromData.int32 - fromData.lossyInt32)) &
- // (this.var | this.int32 | this.float64)
- // this.temp =
- // (this.var - (this.int32 - this.lossyInt32)) &
- // (fromData.var | fromData.int32 | fromData.float64)
- // this.var =
- // (fromData.var & this.var) |
- // (fromData.temp - fromData.float64) |
- // (this.temp - this.float64) |
- // (fromData.temp & fromData.float64 | this.temp & this.float64) & (value ~ int)
- //
- // this.float64 =
- // fromData.float64 & ((this.int32 - this.lossyInt32) | this.float64) |
- // this.float64 & ((fromData.int32 - fromData.lossyInt32) | fromData.float64)
- // this.int32 &= fromData.int32
- // this.lossyInt32 = (fromData.lossyInt32 | this.lossyInt32) & this.int32
- BVSparse<JitArenaAllocator> tempBv1(this->globOpt->tempAlloc);
- BVSparse<JitArenaAllocator> tempBv2(this->globOpt->tempAlloc);
- if (isLoopBackEdge && forceTypeSpecOnLoopHeader)
- {
- Loop *const loop = toBlock->loop;
- // Force to lossless int32:
- // forceLosslessInt32 =
- // ((fromData.int32 - fromData.lossyInt32) - (this.int32 - this.lossyInt32)) &
- // loop.likelyIntSymsUsedBeforeDefined &
- // this.var
- tempBv1.Minus(fromData->liveInt32Syms, fromData->liveLossyInt32Syms);
- tempBv2.Minus(this->liveInt32Syms, this->liveLossyInt32Syms);
- tempBv1.Minus(&tempBv2);
- tempBv1.And(loop->likelyIntSymsUsedBeforeDefined);
- tempBv1.And(this->liveVarSyms);
- this->liveInt32Syms->Or(&tempBv1);
- this->liveLossyInt32Syms->Minus(&tempBv1);
- if(this->globOpt->DoLossyIntTypeSpec())
- {
- // Force to lossy int32:
- // forceLossyInt32 = (fromData.int32 - this.int32) & loop.symsUsedBeforeDefined & this.var
- tempBv1.Minus(fromData->liveInt32Syms, this->liveInt32Syms);
- tempBv1.And(loop->symsUsedBeforeDefined);
- tempBv1.And(this->liveVarSyms);
- this->liveInt32Syms->Or(&tempBv1);
- this->liveLossyInt32Syms->Or(&tempBv1);
- }
- // Force to float64:
- // forceFloat64 =
- // fromData.float64 & loop.forceFloat64 |
- // (fromData.float64 - this.float64) & loop.likelyNumberSymsUsedBeforeDefined
- tempBv1.And(fromData->liveFloat64Syms, loop->forceFloat64SymsOnEntry);
- this->liveFloat64Syms->Or(&tempBv1);
- tempBv1.Minus(fromData->liveFloat64Syms, this->liveFloat64Syms);
- tempBv1.And(loop->likelyNumberSymsUsedBeforeDefined);
- this->liveFloat64Syms->Or(&tempBv1);
- }
- {
- BVSparse<JitArenaAllocator> tempBv3(this->globOpt->tempAlloc);
- // fromData.temp =
- // (fromData.var - (fromData.int32 - fromData.lossyInt32)) &
- // (this.var | this.int32 | this.float64)
- tempBv2.Minus(fromData->liveInt32Syms, fromData->liveLossyInt32Syms);
- tempBv1.Minus(fromData->liveVarSyms, &tempBv2);
- tempBv2.Or(this->liveVarSyms, this->liveInt32Syms);
- tempBv2.Or(this->liveFloat64Syms);
- tempBv1.And(&tempBv2);
- // this.temp =
- // (this.var - (this.int32 - this.lossyInt32)) &
- // (fromData.var | fromData.int32 | fromData.float64)
- tempBv3.Minus(this->liveInt32Syms, this->liveLossyInt32Syms);
- tempBv2.Minus(this->liveVarSyms, &tempBv3);
- tempBv3.Or(fromData->liveVarSyms, fromData->liveInt32Syms);
- tempBv3.Or(fromData->liveFloat64Syms);
- tempBv2.And(&tempBv3);
- {
- BVSparse<JitArenaAllocator> tempBv4(this->globOpt->tempAlloc);
- // fromData.temp & fromData.float64 | this.temp & this.float64
- tempBv3.And(&tempBv1, fromData->liveFloat64Syms);
- tempBv4.And(&tempBv2, this->liveFloat64Syms);
- tempBv3.Or(&tempBv4);
- }
- // (fromData.temp - fromData.float64) |
- // (this.temp - this.float64)
- tempBv1.Minus(fromData->liveFloat64Syms);
- tempBv2.Minus(this->liveFloat64Syms);
- tempBv1.Or(&tempBv2);
- // this.var =
- // (fromData.var & this.var) |
- // (fromData.temp - fromData.float64) |
- // (this.temp - this.float64)
- this->liveVarSyms->And(fromData->liveVarSyms);
- this->liveVarSyms->Or(&tempBv1);
- // this.var |=
- // (fromData.temp & fromData.float64 | this.temp & this.float64) & (value ~ int)
- FOREACH_BITSET_IN_SPARSEBV(id, &tempBv3)
- {
- StackSym *const stackSym = this->globOpt->func->m_symTable->FindStackSym(id);
- Assert(stackSym);
- Value *const value = this->FindValue(stackSym);
- if(value)
- {
- ValueInfo *const valueInfo = value->GetValueInfo();
- if(valueInfo->IsInt() || (valueInfo->IsLikelyInt() && this->globOpt->DoAggressiveIntTypeSpec()))
- {
- this->liveVarSyms->Set(id);
- }
- }
- } NEXT_BITSET_IN_SPARSEBV;
- }
- // fromData.float64 & ((this.int32 - this.lossyInt32) | this.float64)
- tempBv1.Minus(this->liveInt32Syms, this->liveLossyInt32Syms);
- tempBv1.Or(this->liveFloat64Syms);
- tempBv1.And(fromData->liveFloat64Syms);
- // this.float64 & ((fromData.int32 - fromData.lossyInt32) | fromData.float64)
- tempBv2.Minus(fromData->liveInt32Syms, fromData->liveLossyInt32Syms);
- tempBv2.Or(fromData->liveFloat64Syms);
- tempBv2.And(this->liveFloat64Syms);
- // this.float64 =
- // fromData.float64 & ((this.int32 - this.lossyInt32) | this.float64) |
- // this.float64 & ((fromData.int32 - fromData.lossyInt32) | fromData.float64)
- this->liveFloat64Syms->Or(&tempBv1, &tempBv2);
- // this.int32 &= fromData.int32
- // this.lossyInt32 = (fromData.lossyInt32 | this.lossyInt32) & this.int32
- this->liveInt32Syms->And(fromData->liveInt32Syms);
- this->liveLossyInt32Syms->Or(fromData->liveLossyInt32Syms);
- this->liveLossyInt32Syms->And(this->liveInt32Syms);
- }
- if (this->globOpt->TrackArgumentsObject())
- {
- if (!this->argObjSyms->Equal(fromData->argObjSyms))
- {
- this->globOpt->CannotAllocateArgumentsObjectOnStack(nullptr);
- }
- }
- if (fromData->maybeTempObjectSyms && !fromData->maybeTempObjectSyms->IsEmpty())
- {
- if (this->maybeTempObjectSyms)
- {
- this->maybeTempObjectSyms->Or(fromData->maybeTempObjectSyms);
- }
- else
- {
- this->maybeTempObjectSyms = fromData->maybeTempObjectSyms->CopyNew(this->globOpt->alloc);
- }
- if (fromData->canStoreTempObjectSyms && !fromData->canStoreTempObjectSyms->IsEmpty())
- {
- if (this->canStoreTempObjectSyms)
- {
- // Both need to be temp object
- this->canStoreTempObjectSyms->And(fromData->canStoreTempObjectSyms);
- }
- }
- else if (this->canStoreTempObjectSyms)
- {
- this->canStoreTempObjectSyms->ClearAll();
- }
- }
- else
- {
- Assert(!fromData->canStoreTempObjectSyms || fromData->canStoreTempObjectSyms->IsEmpty());
- if (this->canStoreTempObjectSyms)
- {
- this->canStoreTempObjectSyms->ClearAll();
- }
- }
- Assert(this->curFunc == fromData->curFunc);
- Assert((this->callSequence == nullptr && fromData->callSequence == nullptr) || this->callSequence->Equals(*(fromData->callSequence)));
- Assert(this->startCallCount == fromData->startCallCount);
- Assert(this->argOutCount == fromData->argOutCount);
- Assert(this->totalOutParamCount == fromData->totalOutParamCount);
- Assert(this->inlinedArgOutSize == fromData->inlinedArgOutSize);
- // stackLiteralInitFldDataMap is a union of the stack literal from two path.
- // Although we don't need the data on loop prepass, we need to do it for the loop header
- // because we capture the loop header bailout on loop prepass.
- if (fromData->stackLiteralInitFldDataMap != nullptr &&
- (!this->globOpt->IsLoopPrePass() || (toBlock->isLoopHeader && toBlock->loop == this->globOpt->rootLoopPrePass)))
- {
- if (this->stackLiteralInitFldDataMap == nullptr)
- {
- this->stackLiteralInitFldDataMap = fromData->stackLiteralInitFldDataMap->Clone();
- }
- else
- {
- StackLiteralInitFldDataMap * toMap = this->stackLiteralInitFldDataMap;
- fromData->stackLiteralInitFldDataMap->Map([toMap](StackSym * stackSym, StackLiteralInitFldData const& data)
- {
- if (toMap->AddNew(stackSym, data) == -1)
- {
- // If there is an existing data for the stackSym, both path should match
- DebugOnly(StackLiteralInitFldData const * currentData);
- Assert(toMap->TryGetReference(stackSym, ¤tData));
- Assert(currentData->currentInitFldCount == data.currentInitFldCount);
- Assert(currentData->propIds == data.propIds);
- }
- });
- }
- }
- }
- void
- GlobOptBlockData::MergeValueMaps(
- BasicBlock *toBlock,
- BasicBlock *fromBlock,
- BVSparse<JitArenaAllocator> *const symsRequiringCompensation,
- BVSparse<JitArenaAllocator> *const symsCreatedForMerge)
- {
- GlobOptBlockData *fromData = &(fromBlock->globOptData);
- bool isLoopBackEdge = toBlock->isLoopHeader;
- Loop *loop = toBlock->loop;
- bool isLoopPrepass = (loop && this->globOpt->prePassLoop == loop);
- Assert(this->globOpt->valuesCreatedForMerge->Count() == 0);
- DebugOnly(ValueSetByValueNumber mergedValues(this->globOpt->tempAlloc, 64));
- BVSparse<JitArenaAllocator> *const mergedValueTypesTrackedForKills = this->globOpt->tempBv;
- Assert(mergedValueTypesTrackedForKills->IsEmpty());
- this->valuesToKillOnCalls->Clear(); // the tracking will be reevaluated based on merged value types
- GlobHashTable *thisTable = this->symToValueMap;
- GlobHashTable *otherTable = fromData->symToValueMap;
- for (uint i = 0; i < thisTable->tableSize; i++)
- {
- SListBase<GlobHashBucket>::Iterator iter2(&otherTable->table[i]);
- iter2.Next();
- FOREACH_SLISTBASE_ENTRY_EDITING(GlobHashBucket, bucket, &thisTable->table[i], iter)
- {
- while (iter2.IsValid() && bucket.value->m_id < iter2.Data().value->m_id)
- {
- iter2.Next();
- }
- Value *newValue = nullptr;
- if (iter2.IsValid() && bucket.value->m_id == iter2.Data().value->m_id)
- {
- // Syms that are assigned to within the loop must have unique
- // value numbers in the loop header after merging; a single
- // prepass is not adequate to determine that sym values are
- // equivalent through all possible loop paths.
- bool forceUniqueValue =
- isLoopBackEdge &&
- !this->globOpt->IsLoopPrePass() &&
- loop &&
- loop->symsAssignedToInLoop->Test(bucket.value->m_id);
- newValue =
- this->MergeValues(
- bucket.element,
- iter2.Data().element,
- iter2.Data().value,
- isLoopBackEdge,
- forceUniqueValue,
- symsRequiringCompensation,
- symsCreatedForMerge);
- }
- if (newValue == nullptr)
- {
- iter.RemoveCurrent(thisTable->alloc);
- continue;
- }
- else
- {
- #if DBG
- // Ensure that only one value per value number is produced by merge. Byte-code constant values are reused in
- // multiple blocks without cloning, so exclude those value numbers.
- {
- Value *const previouslyMergedValue = mergedValues.Lookup(newValue->GetValueNumber());
- if (previouslyMergedValue)
- {
- if (!this->globOpt->byteCodeConstantValueNumbersBv->Test(newValue->GetValueNumber()))
- {
- Assert(newValue == previouslyMergedValue);
- }
- }
- else
- {
- mergedValues.Add(newValue);
- }
- }
- #endif
- this->globOpt->TrackMergedValueForKills(newValue, this, mergedValueTypesTrackedForKills);
- bucket.element = newValue;
- }
- iter2.Next();
- } NEXT_SLISTBASE_ENTRY_EDITING;
- if (isLoopPrepass && !this->globOpt->rootLoopPrePass->allFieldsKilled)
- {
- while (iter2.IsValid())
- {
- iter2.Next();
- }
- }
- }
- this->globOpt->valuesCreatedForMerge->Clear();
- DebugOnly(mergedValues.Reset());
- mergedValueTypesTrackedForKills->ClearAll();
- this->exprToValueMap->And(fromData->exprToValueMap);
- this->globOpt->ProcessValueKills(toBlock, this);
- bool isLastLoopBackEdge = false;
- if (isLoopBackEdge)
- {
- this->globOpt->ProcessValueKillsForLoopHeaderAfterBackEdgeMerge(toBlock, this);
- BasicBlock *lastBlock = nullptr;
- FOREACH_PREDECESSOR_BLOCK(pred, toBlock)
- {
- Assert(!lastBlock || pred->GetBlockNum() > lastBlock->GetBlockNum());
- lastBlock = pred;
- }NEXT_PREDECESSOR_BLOCK;
- isLastLoopBackEdge = (lastBlock == fromBlock);
- }
- }
- Value *
- GlobOptBlockData::MergeValues(
- Value *toDataValue,
- Value *fromDataValue,
- Sym *fromDataSym,
- bool isLoopBackEdge,
- bool forceUniqueValue,
- BVSparse<JitArenaAllocator> *const symsRequiringCompensation,
- BVSparse<JitArenaAllocator> *const symsCreatedForMerge)
- {
- // Same map
- if (toDataValue == fromDataValue)
- {
- return toDataValue;
- }
- const ValueNumberPair sourceValueNumberPair(toDataValue->GetValueNumber(), fromDataValue->GetValueNumber());
- const bool sameValueNumber = sourceValueNumberPair.First() == sourceValueNumberPair.Second();
- ValueInfo *newValueInfo =
- this->MergeValueInfo(
- toDataValue,
- fromDataValue,
- fromDataSym,
- isLoopBackEdge,
- sameValueNumber,
- symsRequiringCompensation,
- symsCreatedForMerge);
- if (newValueInfo == nullptr)
- {
- return nullptr;
- }
- if (sameValueNumber && newValueInfo == toDataValue->GetValueInfo())
- {
- return toDataValue;
- }
- Value *newValue = nullptr;
- if (forceUniqueValue)
- {
- newValue = this->globOpt->NewValue(newValueInfo);
- }
- else
- {
- // There may be other syms in toData that haven't been merged yet, referring to the current toData value for this sym. If
- // the merge produced a new value info, don't corrupt the value info for the other sym by changing the same value. Instead,
- // create one value per source value number pair per merge and reuse that for new value infos.
- newValue = this->globOpt->valuesCreatedForMerge->Lookup(sourceValueNumberPair, nullptr);
- if (newValue)
- {
- Assert(sameValueNumber == (newValue->GetValueNumber() == toDataValue->GetValueNumber()));
- // This is an exception where Value::SetValueInfo is called directly instead of GlobOpt::ChangeValueInfo, because we're
- // actually generating new value info through merges.
- newValue->SetValueInfo(newValueInfo);
- }
- else
- {
- newValue = this->globOpt->NewValue(sameValueNumber ? sourceValueNumberPair.First() : this->globOpt->NewValueNumber(), newValueInfo);
- this->globOpt->valuesCreatedForMerge->Add(sourceValueNumberPair, newValue);
- }
- }
- // Set symStore if same on both paths.
- if (toDataValue->GetValueInfo()->GetSymStore() == fromDataValue->GetValueInfo()->GetSymStore())
- {
- this->globOpt->SetSymStoreDirect(newValueInfo, toDataValue->GetValueInfo()->GetSymStore());
- }
- return newValue;
- }
- ValueInfo *
- GlobOptBlockData::MergeValueInfo(
- Value *toDataVal,
- Value *fromDataVal,
- Sym *fromDataSym,
- bool isLoopBackEdge,
- bool sameValueNumber,
- BVSparse<JitArenaAllocator> *const symsRequiringCompensation,
- BVSparse<JitArenaAllocator> *const symsCreatedForMerge)
- {
- ValueInfo *const toDataValueInfo = toDataVal->GetValueInfo();
- ValueInfo *const fromDataValueInfo = fromDataVal->GetValueInfo();
- if (toDataValueInfo->IsJsType() || fromDataValueInfo->IsJsType())
- {
- Assert(toDataValueInfo->IsJsType() && fromDataValueInfo->IsJsType());
- return this->MergeJsTypeValueInfo(toDataValueInfo->AsJsType(), fromDataValueInfo->AsJsType(), isLoopBackEdge, sameValueNumber);
- }
- // Same value
- if (toDataValueInfo == fromDataValueInfo)
- {
- return toDataValueInfo;
- }
- ValueType newValueType(toDataValueInfo->Type().Merge(fromDataValueInfo->Type()));
- if (newValueType.IsLikelyInt())
- {
- return ValueInfo::MergeLikelyIntValueInfo(this->globOpt->alloc, toDataVal, fromDataVal, newValueType);
- }
- if(newValueType.IsLikelyAnyOptimizedArray())
- {
- if(newValueType.IsLikelyArrayOrObjectWithArray() &&
- toDataValueInfo->IsLikelyArrayOrObjectWithArray() &&
- fromDataValueInfo->IsLikelyArrayOrObjectWithArray())
- {
- // Value type merge for missing values is aggressive by default (for profile data) - if either side likely has no
- // missing values, then the merged value type also likely has no missing values. This is because arrays often start
- // off having missing values but are eventually filled up. In GlobOpt however, we need to be conservative because
- // the existence of a value type that likely has missing values indicates that it is more likely for it to have
- // missing values than not. Also, StElems that are likely to create missing values are tracked in profile data and
- // will update value types to say they are now likely to have missing values, and that needs to be propagated
- // conservatively.
- newValueType =
- newValueType.SetHasNoMissingValues(
- toDataValueInfo->HasNoMissingValues() && fromDataValueInfo->HasNoMissingValues());
- if(toDataValueInfo->HasIntElements() != fromDataValueInfo->HasIntElements() ||
- toDataValueInfo->HasFloatElements() != fromDataValueInfo->HasFloatElements())
- {
- // When merging arrays with different native storage types, make the merged value type a likely version to force
- // array checks to be done again and cause a conversion and/or bailout as necessary
- newValueType = newValueType.ToLikely();
- }
- }
- if(!(newValueType.IsObject() && toDataValueInfo->IsArrayValueInfo() && fromDataValueInfo->IsArrayValueInfo()))
- {
- return ValueInfo::New(this->globOpt->alloc, newValueType);
- }
- return
- this->MergeArrayValueInfo(
- newValueType,
- toDataValueInfo->AsArrayValueInfo(),
- fromDataValueInfo->AsArrayValueInfo(),
- fromDataSym,
- symsRequiringCompensation,
- symsCreatedForMerge,
- isLoopBackEdge);
- }
- // Consider: If both values are VarConstantValueInfo with the same value, we could
- // merge them preserving the value.
- return ValueInfo::New(this->globOpt->alloc, newValueType);
- }
- JsTypeValueInfo*
- GlobOptBlockData::MergeJsTypeValueInfo(JsTypeValueInfo * toValueInfo, JsTypeValueInfo * fromValueInfo, bool isLoopBackEdge, bool sameValueNumber)
- {
- // On loop back edges we must be conservative and only consider type values which are invariant throughout the loop.
- // That's because in dead store pass we can't correctly track object pointer assignments (o = p), and we may not
- // be able to register correct type checks for the right properties upstream. If we ever figure out how to enhance
- // the dead store pass to track this info we could go more aggressively, as below.
- if (isLoopBackEdge && !sameValueNumber)
- {
- return nullptr;
- }
- if (toValueInfo == fromValueInfo)
- {
- return toValueInfo;
- }
- const JITTypeHolder toType = toValueInfo->GetJsType();
- const JITTypeHolder fromType = fromValueInfo->GetJsType();
- const JITTypeHolder mergedType = toType == fromType ? toType : JITTypeHolder(nullptr);
- Js::EquivalentTypeSet* toTypeSet = toValueInfo->GetJsTypeSet();
- Js::EquivalentTypeSet* fromTypeSet = fromValueInfo->GetJsTypeSet();
- Js::EquivalentTypeSet* mergedTypeSet = (toTypeSet != nullptr && fromTypeSet != nullptr && Js::EquivalentTypeSet::AreIdentical(toTypeSet, fromTypeSet)) ? toTypeSet : nullptr;
- #if DBG_DUMP
- if (PHASE_TRACE(Js::ObjTypeSpecPhase, this->globOpt->func) || PHASE_TRACE(Js::EquivObjTypeSpecPhase, this->globOpt->func))
- {
- Output::Print(_u("ObjTypeSpec: Merging type value info:\n"));
- Output::Print(_u(" from (shared %d): "), fromValueInfo->GetIsShared());
- fromValueInfo->Dump();
- Output::Print(_u("\n to (shared %d): "), toValueInfo->GetIsShared());
- toValueInfo->Dump();
- }
- #endif
- if (mergedType == toType && mergedTypeSet == toTypeSet)
- {
- #if DBG_DUMP
- if (PHASE_TRACE(Js::ObjTypeSpecPhase, this->globOpt->func) || PHASE_TRACE(Js::EquivObjTypeSpecPhase, this->globOpt->func))
- {
- Output::Print(_u("\n result (shared %d): "), toValueInfo->GetIsShared());
- toValueInfo->Dump();
- Output::Print(_u("\n"));
- }
- #endif
- return toValueInfo;
- }
- if (mergedType == nullptr && mergedTypeSet == nullptr)
- {
- // No info, so don't bother making a value.
- return nullptr;
- }
- if (toValueInfo->GetIsShared())
- {
- JsTypeValueInfo* mergedValueInfo = JsTypeValueInfo::New(this->globOpt->alloc, mergedType, mergedTypeSet);
- #if DBG_DUMP
- if (PHASE_TRACE(Js::ObjTypeSpecPhase, this->globOpt->func) || PHASE_TRACE(Js::EquivObjTypeSpecPhase, this->globOpt->func))
- {
- Output::Print(_u("\n result (shared %d): "), mergedValueInfo->GetIsShared());
- mergedValueInfo->Dump();
- Output::Print(_u("\n"));
- }
- #endif
- return mergedValueInfo;
- }
- else
- {
- toValueInfo->SetJsType(mergedType);
- toValueInfo->SetJsTypeSet(mergedTypeSet);
- #if DBG_DUMP
- if (PHASE_TRACE(Js::ObjTypeSpecPhase, this->globOpt->func) || PHASE_TRACE(Js::EquivObjTypeSpecPhase, this->globOpt->func))
- {
- Output::Print(_u("\n result (shared %d): "), toValueInfo->GetIsShared());
- toValueInfo->Dump();
- Output::Print(_u("\n"));
- }
- #endif
- return toValueInfo;
- }
- }
- ValueInfo *GlobOptBlockData::MergeArrayValueInfo(
- const ValueType mergedValueType,
- const ArrayValueInfo *const toDataValueInfo,
- const ArrayValueInfo *const fromDataValueInfo,
- Sym *const arraySym,
- BVSparse<JitArenaAllocator> *const symsRequiringCompensation,
- BVSparse<JitArenaAllocator> *const symsCreatedForMerge,
- bool isLoopBackEdge)
- {
- Assert(mergedValueType.IsAnyOptimizedArray());
- Assert(toDataValueInfo);
- Assert(fromDataValueInfo);
- Assert(toDataValueInfo != fromDataValueInfo);
- Assert(arraySym);
- Assert(!symsRequiringCompensation == this->globOpt->IsLoopPrePass());
- Assert(!symsCreatedForMerge == this->globOpt->IsLoopPrePass());
- // Merge the segment and segment length syms. If we have the segment and/or the segment length syms available on both sides
- // but in different syms, create a new sym and record that the array sym requires compensation. Compensation will be
- // inserted later to initialize this new sym from all predecessors of the merged block.
- StackSym *newHeadSegmentSym = nullptr;
- if(toDataValueInfo->HeadSegmentSym() && fromDataValueInfo->HeadSegmentSym())
- {
- if(toDataValueInfo->HeadSegmentSym() == fromDataValueInfo->HeadSegmentSym())
- {
- newHeadSegmentSym = toDataValueInfo->HeadSegmentSym();
- }
- else
- {
- if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge)
- {
- // Adding compensation code in the prepass won't help, as the symstores would again be different in the main pass.
- Assert(symsRequiringCompensation);
- symsRequiringCompensation->Set(arraySym->m_id);
- Assert(symsCreatedForMerge);
- if (symsCreatedForMerge->Test(toDataValueInfo->HeadSegmentSym()->m_id))
- {
- newHeadSegmentSym = toDataValueInfo->HeadSegmentSym();
- }
- else
- {
- newHeadSegmentSym = StackSym::New(TyMachPtr, this->globOpt->func);
- symsCreatedForMerge->Set(newHeadSegmentSym->m_id);
- }
- }
- }
- }
- StackSym *newHeadSegmentLengthSym = nullptr;
- if(toDataValueInfo->HeadSegmentLengthSym() && fromDataValueInfo->HeadSegmentLengthSym())
- {
- if(toDataValueInfo->HeadSegmentLengthSym() == fromDataValueInfo->HeadSegmentLengthSym())
- {
- newHeadSegmentLengthSym = toDataValueInfo->HeadSegmentLengthSym();
- }
- else
- {
- if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge)
- {
- Assert(symsRequiringCompensation);
- symsRequiringCompensation->Set(arraySym->m_id);
- Assert(symsCreatedForMerge);
- if (symsCreatedForMerge->Test(toDataValueInfo->HeadSegmentLengthSym()->m_id))
- {
- newHeadSegmentLengthSym = toDataValueInfo->HeadSegmentLengthSym();
- }
- else
- {
- newHeadSegmentLengthSym = StackSym::New(TyUint32, this->globOpt->func);
- symsCreatedForMerge->Set(newHeadSegmentLengthSym->m_id);
- }
- }
- }
- }
- StackSym *newLengthSym = nullptr;
- if(toDataValueInfo->LengthSym() && fromDataValueInfo->LengthSym())
- {
- if(toDataValueInfo->LengthSym() == fromDataValueInfo->LengthSym())
- {
- newLengthSym = toDataValueInfo->LengthSym();
- }
- else
- {
- if (!this->globOpt->IsLoopPrePass() && !isLoopBackEdge)
- {
- Assert(symsRequiringCompensation);
- symsRequiringCompensation->Set(arraySym->m_id);
- Assert(symsCreatedForMerge);
- if (symsCreatedForMerge->Test(toDataValueInfo->LengthSym()->m_id))
- {
- newLengthSym = toDataValueInfo->LengthSym();
- }
- else
- {
- newLengthSym = StackSym::New(TyUint32, this->globOpt->func);
- symsCreatedForMerge->Set(newLengthSym->m_id);
- }
- }
- }
- }
- if(newHeadSegmentSym || newHeadSegmentLengthSym || newLengthSym)
- {
- return ArrayValueInfo::New(this->globOpt->alloc, mergedValueType, newHeadSegmentSym, newHeadSegmentLengthSym, newLengthSym);
- }
- if(symsRequiringCompensation)
- {
- symsRequiringCompensation->Clear(arraySym->m_id);
- }
- return ValueInfo::New(this->globOpt->alloc, mergedValueType);
- }
- void
- GlobOptBlockData::TrackArgumentsSym(IR::RegOpnd const* opnd)
- {
- if(!this->curFunc->argObjSyms)
- {
- this->curFunc->argObjSyms = JitAnew(this->globOpt->alloc, BVSparse<JitArenaAllocator>, this->globOpt->alloc);
- }
- this->curFunc->argObjSyms->Set(opnd->m_sym->m_id);
- this->argObjSyms->Set(opnd->m_sym->m_id);
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (PHASE_TESTTRACE(Js::StackArgOptPhase, this->globOpt->func))
- {
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- char16 debugStringBuffer2[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("Created a new alias s%d for arguments object in function %s(%s) topFunc %s(%s)\n"),
- opnd->m_sym->m_id,
- this->curFunc->GetJITFunctionBody()->GetDisplayName(),
- this->curFunc->GetDebugNumberSet(debugStringBuffer),
- this->globOpt->func->GetJITFunctionBody()->GetDisplayName(),
- this->globOpt->func->GetDebugNumberSet(debugStringBuffer2)
- );
- Output::Flush();
- }
- #endif
- }
- void
- GlobOptBlockData::ClearArgumentsSym(IR::RegOpnd const* opnd)
- {
- // We blindly clear so need to check func has argObjSyms
- if (this->curFunc->argObjSyms)
- {
- this->curFunc->argObjSyms->Clear(opnd->m_sym->m_id);
- }
- this->argObjSyms->Clear(opnd->m_sym->m_id);
- }
- BOOL
- GlobOptBlockData::TestAnyArgumentsSym() const
- {
- return this->argObjSyms->TestEmpty();
- }
- BOOL
- GlobOptBlockData::IsArgumentsSymID(SymID id) const
- {
- return this->argObjSyms->Test(id);
- }
- BOOL
- GlobOptBlockData::IsArgumentsOpnd(IR::Opnd const* opnd) const
- {
- SymID id = 0;
- if (opnd->IsRegOpnd())
- {
- id = opnd->AsRegOpnd()->m_sym->m_id;
- return this->IsArgumentsSymID(id);
- }
- else if (opnd->IsSymOpnd())
- {
- Sym const *sym = opnd->AsSymOpnd()->m_sym;
- if (sym && sym->IsPropertySym())
- {
- PropertySym const *propertySym = sym->AsPropertySym();
- id = propertySym->m_stackSym->m_id;
- return this->IsArgumentsSymID(id);
- }
- return false;
- }
- else if (opnd->IsIndirOpnd())
- {
- IR::RegOpnd const *indexOpnd = opnd->AsIndirOpnd()->GetIndexOpnd();
- IR::RegOpnd const *baseOpnd = opnd->AsIndirOpnd()->GetBaseOpnd();
- return this->IsArgumentsSymID(baseOpnd->m_sym->m_id) || (indexOpnd && this->IsArgumentsSymID(indexOpnd->m_sym->m_id));
- }
- AssertMsg(false, "Unknown type");
- return false;
- }
- Value*
- GlobOptBlockData::FindValue(Sym *sym)
- {
- Assert(this->symToValueMap);
- if (sym->IsStackSym() && sym->AsStackSym()->IsTypeSpec())
- {
- sym = sym->AsStackSym()->GetVarEquivSym(this->globOpt->func);
- }
- else if (sym->IsPropertySym())
- {
- return this->FindPropertyValue(sym->m_id);
- }
- if (sym->IsStackSym() && sym->AsStackSym()->IsFromByteCodeConstantTable())
- {
- return this->globOpt->byteCodeConstantValueArray->Get(sym->m_id);
- }
- else
- {
- return FindValueFromMapDirect(sym->m_id);
- }
- }
- Value *
- GlobOptBlockData::FindPropertyValue(SymID symId)
- {
- Assert(this->globOpt->func->m_symTable->Find(symId)->IsPropertySym());
- if (!this->liveFields->Test(symId))
- {
- return nullptr;
- }
- return FindValueFromMapDirect(symId);
- }
- Value *
- GlobOptBlockData::FindObjectTypeValue(SymID typeSymId)
- {
- Assert(this->globOpt->func->m_symTable->Find(typeSymId)->IsStackSym());
- if (!this->liveFields->Test(typeSymId))
- {
- return nullptr;
- }
- return FindObjectTypeValueNoLivenessCheck(typeSymId);
- }
- Value *
- GlobOptBlockData::FindObjectTypeValueNoLivenessCheck(StackSym* typeSym)
- {
- return FindObjectTypeValueNoLivenessCheck(typeSym->m_id);
- }
- Value *
- GlobOptBlockData::FindObjectTypeValueNoLivenessCheck(SymID typeSymId)
- {
- Value* value = this->FindValueFromMapDirect(typeSymId);
- Assert(value == nullptr || value->GetValueInfo()->IsJsType());
- return value;
- }
- Value *
- GlobOptBlockData::FindObjectTypeValue(StackSym* typeSym)
- {
- return FindObjectTypeValue(typeSym->m_id);
- }
- Value *
- GlobOptBlockData::FindFuturePropertyValue(PropertySym *const propertySym)
- {
- Assert(propertySym);
- // Try a direct lookup based on this sym
- Value *const value = this->FindValue(propertySym);
- if(value)
- {
- return value;
- }
- if(PHASE_OFF(Js::CopyPropPhase, this->globOpt->func))
- {
- // Need to use copy-prop info to backtrack
- return nullptr;
- }
- // Try to get the property object's value
- StackSym *const objectSym = propertySym->m_stackSym;
- Value *objectValue = this->FindValue(objectSym);
- if(!objectValue)
- {
- if(!objectSym->IsSingleDef())
- {
- return nullptr;
- }
- switch(objectSym->m_instrDef->m_opcode)
- {
- case Js::OpCode::Ld_A:
- case Js::OpCode::LdSlotArr:
- case Js::OpCode::LdSlot:
- // Allow only these op-codes for tracking the object sym's value transfer backwards. Other transfer op-codes
- // could be included here if this function is used in scenarios that need them.
- break;
- default:
- return nullptr;
- }
- // Try to get the property object's value from the src of the definition
- IR::Opnd *const objectTransferSrc = objectSym->m_instrDef->GetSrc1();
- if(!objectTransferSrc)
- {
- return nullptr;
- }
- if(objectTransferSrc->IsRegOpnd())
- {
- objectValue = this->FindValue(objectTransferSrc->AsRegOpnd()->m_sym);
- }
- else if(objectTransferSrc->IsSymOpnd())
- {
- Sym *const objectTransferSrcSym = objectTransferSrc->AsSymOpnd()->m_sym;
- if(objectTransferSrcSym->IsStackSym())
- {
- objectValue = this->FindValue(objectTransferSrcSym);
- }
- else
- {
- // About to make a recursive call, so when jitting in the foreground, probe the stack
- if(!this->globOpt->func->IsBackgroundJIT())
- {
- PROBE_STACK_NO_DISPOSE(this->globOpt->func->GetScriptContext(), Js::Constants::MinStackDefault);
- }
- objectValue = FindFuturePropertyValue(objectTransferSrcSym->AsPropertySym());
- }
- }
- else
- {
- return nullptr;
- }
- if(!objectValue)
- {
- return nullptr;
- }
- }
- // Try to use the property object's copy-prop sym and the property ID to find a mapped property sym, and get its value
- StackSym *const objectCopyPropSym = this->GetCopyPropSym(nullptr, objectValue);
- if(!objectCopyPropSym)
- {
- return nullptr;
- }
- PropertySym *const propertyCopyPropSym = PropertySym::Find(objectCopyPropSym->m_id, propertySym->m_propertyId, this->globOpt->func);
- if(!propertyCopyPropSym)
- {
- return nullptr;
- }
- return this->FindValue(propertyCopyPropSym);
- }
- Value *
- GlobOptBlockData::FindValueFromMapDirect(SymID symId)
- {
- Value ** valuePtr = this->symToValueMap->Get(symId);
- if (valuePtr == nullptr)
- {
- return 0;
- }
- return (*valuePtr);
- }
- StackSym *
- GlobOptBlockData::GetCopyPropSym(Sym * sym, Value * value)
- {
- ValueInfo *valueInfo = value->GetValueInfo();
- Sym * copySym = valueInfo->GetSymStore();
- if (!copySym)
- {
- return nullptr;
- }
- // Only copy prop stackSym, as a propertySym wouldn't improve anything.
- // SingleDef info isn't flow sensitive, so make sure the symbol is actually live.
- if (copySym->IsStackSym() && copySym != sym)
- {
- Assert(!copySym->AsStackSym()->IsTypeSpec());
- Value *copySymVal = this->FindValue(valueInfo->GetSymStore());
- if (copySymVal && copySymVal->GetValueNumber() == value->GetValueNumber())
- {
- if (valueInfo->IsVarConstant() && !this->IsLive(copySym))
- {
- // Because the addrConstantToValueMap isn't flow-based, the symStore of
- // varConstants may not be live.
- return nullptr;
- }
- return copySym->AsStackSym();
- }
- }
- return nullptr;
- }
- void
- GlobOptBlockData::ClearSymValue(Sym* sym)
- {
- this->symToValueMap->Clear(sym->m_id);
- }
- void
- GlobOptBlockData::MarkTempLastUse(IR::Instr *instr, IR::RegOpnd *regOpnd)
- {
- if (OpCodeAttr::NonTempNumberSources(instr->m_opcode))
- {
- // Turn off bit if opcode could cause the src to be aliased.
- this->isTempSrc->Clear(regOpnd->m_sym->m_id);
- }
- else if (this->isTempSrc->Test(regOpnd->m_sym->m_id))
- {
- // We just mark things that are temp in the globopt phase.
- // The backwards phase will turn this off if it is not the last use.
- // The isTempSrc is freed at the end of each block, which is why the backwards phase can't
- // just use it.
- if (!PHASE_OFF(Js::BackwardPhase, this->globOpt->func) && !this->globOpt->IsLoopPrePass())
- {
- regOpnd->m_isTempLastUse = true;
- }
- }
- }
- Value *
- GlobOptBlockData::InsertNewValue(Value *val, IR::Opnd *opnd)
- {
- return this->SetValue(val, opnd);
- }
- void
- GlobOptBlockData::SetChangedSym(SymID symId)
- {
- // this->currentBlock might not be the one which contain the changing symId,
- // like hoisting invariant, but more changed symId is overly conservative and safe.
- // symId in the hoisted to block is marked as JITOptimizedReg so it does't affect bailout.
- if (this->changedSyms)
- {
- this->changedSyms->Set(symId);
- if (this->capturedValuesCandidate != nullptr)
- {
- this->globOpt->changedSymsAfterIncBailoutCandidate->Set(symId);
- }
- }
- // else could be hit only in MergeValues and it is handled by MergeCapturedValues
- }
- void
- GlobOptBlockData::SetChangedSym(Sym* sym)
- {
- if (sym && sym->IsStackSym() && sym->AsStackSym()->HasByteCodeRegSlot())
- {
- SetChangedSym(sym->m_id);
- }
- }
- void
- GlobOptBlockData::SetValue(Value *val, Sym * sym)
- {
- ValueInfo *valueInfo = val->GetValueInfo();
- sym = this->globOpt->SetSymStore(valueInfo, sym);
- bool isStackSym = sym->IsStackSym();
- if (isStackSym && sym->AsStackSym()->IsFromByteCodeConstantTable())
- {
- // Put the constants in a global array. This will minimize the per-block info.
- this->globOpt->byteCodeConstantValueArray->Set(sym->m_id, val);
- this->globOpt->byteCodeConstantValueNumbersBv->Set(val->GetValueNumber());
- }
- else
- {
- this->SetValueToHashTable(this->symToValueMap, val, sym);
- this->SetChangedSym(sym);
- }
- }
- Value *
- GlobOptBlockData::SetValue(Value *val, IR::Opnd *opnd)
- {
- if (opnd)
- {
- Sym *sym;
- switch (opnd->GetKind())
- {
- case IR::OpndKindSym:
- sym = opnd->AsSymOpnd()->m_sym;
- break;
- case IR::OpndKindReg:
- sym = opnd->AsRegOpnd()->m_sym;
- break;
- default:
- sym = nullptr;
- }
- if (sym)
- {
- this->SetValue(val, sym);
- }
- }
- return val;
- }
- void
- GlobOptBlockData::SetValueToHashTable(GlobHashTable *valueNumberMap, Value *val, Sym *sym)
- {
- Value **pValue = valueNumberMap->FindOrInsertNew(sym);
- *pValue = val;
- }
- void
- GlobOptBlockData::MakeLive(StackSym *sym, const bool lossy)
- {
- Assert(sym);
- Assert(this);
- if(sym->IsTypeSpec())
- {
- const SymID varSymId = sym->GetVarEquivSym(this->globOpt->func)->m_id;
- if(sym->IsInt32())
- {
- this->liveInt32Syms->Set(varSymId);
- if(lossy)
- {
- this->liveLossyInt32Syms->Set(varSymId);
- }
- else
- {
- this->liveLossyInt32Syms->Clear(varSymId);
- }
- return;
- }
- if (sym->IsFloat64())
- {
- this->liveFloat64Syms->Set(varSymId);
- return;
- }
- }
- this->liveVarSyms->Set(sym->m_id);
- }
- bool
- GlobOptBlockData::IsLive(Sym const * sym) const
- {
- sym = StackSym::GetVarEquivStackSym_NoCreate(sym);
- return
- sym &&
- (
- this->liveVarSyms->Test(sym->m_id) ||
- this->liveInt32Syms->Test(sym->m_id) ||
- this->liveFloat64Syms->Test(sym->m_id)
- );
- }
- bool
- GlobOptBlockData::IsTypeSpecialized(Sym const * sym) const
- {
- return this->IsInt32TypeSpecialized(sym) || this->IsFloat64TypeSpecialized(sym);
- }
- bool
- GlobOptBlockData::IsSwitchInt32TypeSpecialized(IR::Instr const * instr) const
- {
- return GlobOpt::IsSwitchOptEnabledForIntTypeSpec(instr->m_func->GetTopFunc())
- && instr->GetSrc1()->IsRegOpnd()
- && this->IsInt32TypeSpecialized(instr->GetSrc1()->AsRegOpnd()->m_sym);
- }
- bool
- GlobOptBlockData::IsInt32TypeSpecialized(Sym const * sym) const
- {
- sym = StackSym::GetVarEquivStackSym_NoCreate(sym);
- return sym && this->liveInt32Syms->Test(sym->m_id) && !this->liveLossyInt32Syms->Test(sym->m_id);
- }
- bool
- GlobOptBlockData::IsFloat64TypeSpecialized(Sym const * sym) const
- {
- sym = StackSym::GetVarEquivStackSym_NoCreate(sym);
- return sym && this->liveFloat64Syms->Test(sym->m_id);
- }
- void
- GlobOptBlockData::KillStateForGeneratorYield(IR::Instr* yieldInstr)
- {
- this->liveInt32Syms->Minus(this->liveVarSyms);
- this->globOpt->ToVar(liveInt32Syms, this->globOpt->currentBlock, yieldInstr /* insertBeforeInstr */);
- this->liveInt32Syms->ClearAll();
- this->liveFloat64Syms->Minus(this->liveVarSyms);
- this->globOpt->ToVar(liveFloat64Syms, this->globOpt->currentBlock, yieldInstr /* insertBeforeInstr */);
- this->liveFloat64Syms->ClearAll();
- this->liveLossyInt32Syms->ClearAll();
- // Keep this->liveVarSyms as is
- // Keep this->argObjSyms as is
- this->hasCSECandidates = false;
- // No need to clear `isTempSrc` (used for in-place string concat)
- this->exprToValueMap->ClearAll();
- this->KillSymToValueMapForGeneratorYield();
- }
- void
- GlobOptBlockData::KillSymToValueMapForGeneratorYield()
- {
- // Remove illegal symToValueMap entries whose symstores don't have bytecode registers
- // Hash table bucket key-value visualization: { bucket.value: bucket.element }
- //
- // Idea:
- // Multiple symbols can map to the same value which has a symstore
- // (multiple keys map to same value).
- // Since the symstore might not have a bytecode register, our first pass
- // through the map attemps to use the symbol (key) as a symstore for that value.
- // This allows us to still retain such entries.
- // After the first pass, any symToValueMap entries whose symstores don't have
- // bytecode registers will be cleared.
- FOREACH_VALUEHASHTABLE_ENTRY(GlobHashBucket, bucket, this->symToValueMap)
- {
- if (bucket.element == nullptr)
- {
- continue;
- }
- Sym* symStore = bucket.element->GetValueInfo()->GetSymStore();
- if (symStore != nullptr && symStore->IsStackSym() && symStore->AsStackSym()->HasByteCodeRegSlot())
- {
- continue;
- }
- Sym* sym = bucket.value;
- if (sym != nullptr && sym->IsStackSym() && sym->AsStackSym()->HasByteCodeRegSlot())
- {
- bucket.element->GetValueInfo()->SetSymStore(sym);
- }
- }
- NEXT_VALUEHASHTABLE_ENTRY;
- // Remove illegal entries
- FOREACH_VALUEHASHTABLE_ENTRY_EDITING(GlobHashBucket, bucket, this->symToValueMap, iter)
- {
- Value* value = bucket.element;
- if (value == nullptr)
- {
- iter.RemoveCurrent(this->symToValueMap->alloc);
- }
- else
- {
- Sym* symStore = value->GetValueInfo()->GetSymStore();
- if (symStore == nullptr || !symStore->IsStackSym() || !symStore->AsStackSym()->HasByteCodeRegSlot())
- {
- iter.RemoveCurrent(this->symToValueMap->alloc);
- }
- }
- }
- NEXT_VALUEHASHTABLE_ENTRY_EDITING;
- }
- #if DBG_DUMP
- void
- GlobOptBlockData::DumpSymToValueMap() const
- {
- if (this->symToValueMap != nullptr)
- {
- this->symToValueMap->Dump(GlobOptBlockData::DumpSym);
- }
- }
- void
- GlobOptBlockData::DumpSym(Sym *sym)
- {
- ((Sym const*)sym)->Dump();
- }
- #endif
|