| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726 |
- //-------------------------------------------------------------------------------------------------------
- // 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"
- /* ===================================================================================
- * TempTracker runs the mark temp algorithm. The template parameter provides information
- * what are valid temp use, temp transfer, or temp producing operations and what bit to
- * set once a symbol def can be marked temp.
- *
- * NumberTemp mark temp JavascriptNumber creation for math operations, run during deadstore
- *
- * ObjectTemp mark temp object allocations, run during backward pass so that it can provide
- * information to the globopt to install pre op bailout on implicit call while during stack
- * allocation objects.
- *
- * ObjectTempVerify runs a similar mark temp during deadstore in debug mode to assert
- * that globopt have install the pre op necessary and a marked temp def is still valid
- * as a mark temp
- *
- * The basic of the mark temp algorithm is very simple: we keep track if we have seen
- * any use of a symbol that is not a valid mark temp (the nonTempSyms bitvector)
- * and on definition of the symbol, if the all the use allow temp object (not in nonTempSyms
- * bitvector) then it is mark them able.
- *
- * However, the complication comes when the stack object is transferred to another symbol
- * and we are in a loop. We need to make sure that the stack object isn't still referred
- * by another symbol when we allocate the number/object in the next iteration
- *
- * For example:
- * Loop top:
- * s1 = NewScObject
- * = s6
- * s6 = s1
- * Goto Loop top
- *
- * We cannot mark them this case because when s1 is created, the object might still be
- * referred to by s6 from previous iteration, and thus if we mark them we would have
- * change the content of s6 as well.
- *
- * To detect this dependency, we conservatively collect "all" transfers in the pre pass
- * of the loop. We have to be conservative to detect reverse dependencies without
- * iterating more than 2 times for the loop.
- * =================================================================================== */
- JitArenaAllocator *
- TempTrackerBase::GetAllocator() const
- {
- return nonTempSyms.GetAllocator();
- }
- TempTrackerBase::TempTrackerBase(JitArenaAllocator * alloc, bool inLoop)
- : nonTempSyms(alloc), tempTransferredSyms(alloc)
- {
- if (inLoop)
- {
- tempTransferDependencies = HashTable<BVSparse<JitArenaAllocator> *>::New(alloc, 16);
- }
- else
- {
- tempTransferDependencies = nullptr;
- }
- }
- TempTrackerBase::~TempTrackerBase()
- {
- if (this->tempTransferDependencies != nullptr)
- {
- JitArenaAllocator * alloc = this->GetAllocator();
- FOREACH_HASHTABLE_ENTRY(BVSparse<JitArenaAllocator> *, bucket, this->tempTransferDependencies)
- {
- JitAdelete(alloc, bucket.element);
- }
- NEXT_HASHTABLE_ENTRY;
- this->tempTransferDependencies->Delete();
- }
- }
- void
- TempTrackerBase::MergeData(TempTrackerBase * fromData, bool deleteData)
- {
- nonTempSyms.Or(&fromData->nonTempSyms);
- tempTransferredSyms.Or(&fromData->tempTransferredSyms);
- MergeDependencies(tempTransferDependencies, fromData->tempTransferDependencies, deleteData);
- }
- void
- TempTrackerBase::AddTransferDependencies(int sourceId, SymID dstSymID, HashTable<BVSparse<JitArenaAllocator> *> * dependencies)
- {
- // Add to the transfer dependencies set
- BVSparse<JitArenaAllocator> ** pBVSparse = dependencies->FindOrInsertNew(sourceId);
- if (*pBVSparse == nullptr)
- {
- *pBVSparse = JitAnew(this->GetAllocator(), BVSparse<JitArenaAllocator>, this->GetAllocator());
- }
- AddTransferDependencies(*pBVSparse, dstSymID);
- }
- void
- TempTrackerBase::AddTransferDependencies(BVSparse<JitArenaAllocator> * bv, SymID dstSymID)
- {
- bv->Set(dstSymID);
- // Add the indirect transfers (always from tempTransferDependencies)
- BVSparse<JitArenaAllocator> *dstBVSparse = this->tempTransferDependencies->GetAndClear(dstSymID);
- if (dstBVSparse != nullptr)
- {
- bv->Or(dstBVSparse);
- JitAdelete(this->GetAllocator(), dstBVSparse);
- }
- }
- template <typename T>
- TempTracker<T>::TempTracker(JitArenaAllocator * alloc, bool inLoop):
- T(alloc, inLoop)
- {
- }
- template <typename T>
- void
- TempTracker<T>::MergeData(TempTracker<T> * fromData, bool deleteData)
- {
- TempTrackerBase::MergeData(fromData, deleteData);
- T::MergeData(fromData, deleteData);
- if (deleteData)
- {
- JitAdelete(this->GetAllocator(), fromData);
- }
- }
- void
- TempTrackerBase::OrHashTableOfBitVector(HashTable<BVSparse<JitArenaAllocator> *> * toData, HashTable<BVSparse<JitArenaAllocator> *> *& fromData, bool deleteData)
- {
- Assert(toData != nullptr);
- Assert(fromData != nullptr);
- toData->Or(fromData,
- [=](BVSparse<JitArenaAllocator> * bv1, BVSparse<JitArenaAllocator> * bv2) -> BVSparse<JitArenaAllocator> *
- {
- if (bv1 == nullptr)
- {
- if (deleteData)
- {
- return bv2;
- }
- return bv2->CopyNew(this->GetAllocator());
- }
- bv1->Or(bv2);
- if (deleteData)
- {
- JitAdelete(this->GetAllocator(), bv2);
- }
- return bv1;
- });
- if (deleteData)
- {
- fromData->Delete();
- fromData = nullptr;
- }
- }
- void
- TempTrackerBase::MergeDependencies(HashTable<BVSparse<JitArenaAllocator> *> * toData, HashTable<BVSparse<JitArenaAllocator> *> *& fromData, bool deleteData)
- {
- if (fromData != nullptr)
- {
- if (toData != nullptr)
- {
- OrHashTableOfBitVector(toData, fromData, deleteData);
- }
- else if (deleteData)
- {
- FOREACH_HASHTABLE_ENTRY(BVSparse<JitArenaAllocator> *, bucket, fromData)
- {
- JitAdelete(this->GetAllocator(), bucket.element);
- }
- NEXT_HASHTABLE_ENTRY;
- fromData->Delete();
- fromData = nullptr;
- }
- }
- }
- #if DBG_DUMP
- void
- TempTrackerBase::Dump(wchar_t const * traceName)
- {
- Output::Print(L"%s: Non temp syms:", traceName);
- this->nonTempSyms.Dump();
- Output::Print(L"%s: Temp transferred syms:", traceName);
- this->tempTransferredSyms.Dump();
- if (this->tempTransferDependencies != nullptr)
- {
- Output::Print(L"%s: Temp transfer dependencies:\n", traceName);
- this->tempTransferDependencies->Dump();
- }
- }
- #endif
- template <typename T>
- void
- TempTracker<T>::ProcessUse(StackSym * sym, BackwardPass * backwardPass)
- {
- // Don't care about type specialized syms
- if (!sym->IsVar())
- {
- return;
- }
- IR::Instr * instr = backwardPass->currentInstr;
- SymID usedSymID = sym->m_id;
- bool isTempPropertyTransferStore = T::IsTempPropertyTransferStore(instr, backwardPass);
- bool isTempUse = isTempPropertyTransferStore || T::IsTempUse(instr, sym, backwardPass);
- if (!isTempUse)
- {
- this->nonTempSyms.Set(usedSymID);
- }
- #if DBG
- if (T::DoTrace(backwardPass))
- {
- Output::Print(L"%s: %8s%4sTemp Use (s%-3d): ", T::GetTraceName(),
- backwardPass->IsPrePass() ? L"Prepass " : L"", isTempUse ? L"" : L"Non ", usedSymID);
- instr->DumpSimple();
- Output::Flush();
- }
- #endif
- if (T::IsTempTransfer(instr))
- {
- this->tempTransferredSyms.Set(usedSymID);
- // Track dependencies if we are in loop only
- if (this->tempTransferDependencies != nullptr)
- {
- IR::Opnd * dstOpnd = instr->GetDst();
- if (dstOpnd->IsRegOpnd())
- {
- SymID dstSymID = dstOpnd->AsRegOpnd()->m_sym->m_id;
- if (dstSymID != usedSymID)
- {
- // Record that the usedSymID may propagate to dstSymID and all the symbols
- // that it may propagate to as well
- AddTransferDependencies(usedSymID, dstSymID, this->tempTransferDependencies);
- #if DBG_DUMP
- if (T::DoTrace(backwardPass))
- {
- Output::Print(L"%s: %8s s%d -> s%d: ", T::GetTraceName(),
- backwardPass->IsPrePass() ? L"Prepass " : L"", dstSymID, usedSymID);
- (*this->tempTransferDependencies->Get(usedSymID))->Dump();
- }
- #endif
- }
- }
- }
- }
- if (isTempPropertyTransferStore)
- {
- this->tempTransferredSyms.Set(usedSymID);
- PropertySym * propertySym = instr->GetDst()->AsSymOpnd()->m_sym->AsPropertySym();
- PropagateTempPropertyTransferStoreDependencies(usedSymID, propertySym, backwardPass);
- #if DBG_DUMP
- if (T::DoTrace(backwardPass) && this->tempTransferDependencies)
- {
- Output::Print(L"%s: %8s (PropId:%d %s)+[] -> s%d: ", T::GetTraceName(),
- backwardPass->IsPrePass() ? L"Prepass " : L"", propertySym->m_propertyId,
- backwardPass->func->GetScriptContext()->GetPropertyNameLocked(propertySym->m_propertyId)->GetBuffer(), usedSymID);
- BVSparse<JitArenaAllocator> ** transferDependencies = this->tempTransferDependencies->Get(usedSymID);
- if (transferDependencies)
- {
- (*transferDependencies)->Dump();
- }
- else
- {
- Output::Print(L"[]\n");
- }
- }
- #endif
- }
- };
- template <typename T>
- void
- TempTracker<T>::MarkTemp(StackSym * sym, BackwardPass * backwardPass)
- {
- // Don't care about type specialized syms
- Assert(sym->IsVar());
- IR::Instr * instr = backwardPass->currentInstr;
- BOOLEAN nonTemp = this->nonTempSyms.TestAndClear(sym->m_id);
- BOOLEAN isTempTransferred;
- BVSparse<JitArenaAllocator> * bvTempTransferDependencies = nullptr;
- bool const isTransferOperation =
- T::IsTempTransfer(instr)
- || T::IsTempPropertyTransferLoad(instr, backwardPass)
- || T::IsTempIndirTransferLoad(instr, backwardPass);
- if (this->tempTransferDependencies != nullptr)
- {
- // Since we don't iterate "while (!changed)" in loops, we don't have complete accurate dataflow
- // for loop carried dependencies. So don't clear the dependency transfer info. WOOB:1121525
- // Check if this dst is transferred (assigned) to another symbol
- if (isTransferOperation)
- {
- isTempTransferred = this->tempTransferredSyms.Test(sym->m_id);
- }
- else
- {
- isTempTransferred = this->tempTransferredSyms.TestAndClear(sym->m_id);
- }
- // We only need to look at the dependencies if we are in a loop because of the back edge
- // Also we don't need to if we are in pre pass
- if (isTempTransferred)
- {
- if (!backwardPass->IsPrePass())
- {
- if (isTransferOperation)
- {
- // Transfer operation, load but not clear the information
- BVSparse<JitArenaAllocator> **pBv = this->tempTransferDependencies->Get(sym->m_id);
- if (pBv)
- {
- bvTempTransferDependencies = *pBv;
- }
- }
- else
- {
- // Non transfer operation, load and clear the information and the dst value is replaced
- bvTempTransferDependencies = this->tempTransferDependencies->GetAndClear(sym->m_id);
- }
- }
- else if (!isTransferOperation)
- {
- // In pre pass, and not a transfer operation (just an assign). We can clear the dependency info
- // and not look at it.
- this->tempTransferDependencies->Clear(sym->m_id);
- }
- }
- }
- else
- {
- isTempTransferred = this->tempTransferredSyms.TestAndClear(sym->m_id);
- }
- // Reset the dst is temp bit (we set it optimistically on the loop pre pass)
- bool dstIsTemp = false;
- bool dstIsTempTransferred = false;
- if (nonTemp)
- {
- #if DBG_DUMP
- if (T::DoTrace(backwardPass) && !backwardPass->IsPrePass() && T::CanMarkTemp(instr, backwardPass))
- {
- Output::Print(L"%s: Not temp (s%-03d):", T::GetTraceName(), sym->m_id);
- instr->DumpSimple();
- }
- #endif
- }
- else if (backwardPass->IsPrePass())
- {
- // On pre pass, we don't have complete information about whether it is tempable or
- // not from the back edge. If we already discovered that it is not a temp (above), then
- // we don't mark it, other wise, assume that it is okay to be tempable and have the
- // second pass set the bit correctly. The only works on dependency chain that is in order
- // e.g.
- // s1 = Add
- // s2 = s1
- // s3 = s2
- // The dependencies tracking to catch the case whether the dependency chain is out of order
- // e.g
- // s1 = Add
- // s3 = s2
- // s2 = s3
- Assert(isTransferOperation == T::IsTempTransfer(instr)
- || T::IsTempPropertyTransferLoad(instr, backwardPass)
- || T::IsTempIndirTransferLoad(instr, backwardPass));
- if (isTransferOperation)
- {
- dstIsTemp = true;
- }
- }
- else if (T::CanMarkTemp(instr, backwardPass))
- {
- dstIsTemp = true;
- if (isTempTransferred)
- {
- // Track whether the dst is transferred or not, and allocate separate stack slot for them
- // so that another dst will not overrides the value
- dstIsTempTransferred = true;
- // The temp is aliased, need to trace if there is another use of the set of aliased
- // sym that is still live so that we won't mark them this symbol and destroy the value
- if (bvTempTransferDependencies != nullptr)
- {
- // Inside a loop we need to track if any of the reg that we transferred to is still live
- // s1 = Add
- // = s2
- // s2 = s1
- // Since s2 is still live on the next iteration when we reassign s1, making s1 a temp
- // will cause the value of s2 to change before it's use.
- // The upwardExposedUses are the live regs, check if it intersect with the set
- // of dependency or not.
- #if DBG_DUMP
- if (T::DoTrace(backwardPass) && Js::Configuration::Global.flags.Verbose)
- {
- Output::Print(L"%s: Loop mark temp check instr:\n", T::GetTraceName());
- instr->DumpSimple();
- Output::Print(L"Transfer dependencies: ");
- bvTempTransferDependencies->Dump();
- Output::Print(L"Upward exposed Uses : ");
- backwardPass->currentBlock->upwardExposedUses->Dump();
- Output::Print(L"\n");
- }
- #endif
- BVSparse<JitArenaAllocator> * upwardExposedUses = backwardPass->currentBlock->upwardExposedUses;
- bool hasExposedDependencies = bvTempTransferDependencies->Test(upwardExposedUses)
- || T::HasExposedFieldDependencies(bvTempTransferDependencies, backwardPass);
- if (hasExposedDependencies)
- {
- #if DBG_DUMP
- if (T::DoTrace(backwardPass))
- {
- Output::Print(L"%s: Not temp (s%-03d): ", T::GetTraceName(), sym->m_id);
- instr->DumpSimple();
- Output::Print(L" Transferred exposed uses: ");
- JitArenaAllocator tempAllocator(L"temp", this->GetAllocator()->GetPageAllocator(), Js::Throw::OutOfMemory);
- bvTempTransferDependencies->AndNew(upwardExposedUses, &tempAllocator)->Dump();
- }
- #endif
- dstIsTemp = false;
- dstIsTempTransferred = false;
- #if DBG
- if (IsObjectTempVerify<T>())
- {
- dstIsTemp = ObjectTempVerify::DependencyCheck(instr, bvTempTransferDependencies, backwardPass);
- }
- #endif
- // Only ObjectTmepVerify would do the do anything here. All other returns false
- }
- }
- }
- }
- T::SetDstIsTemp(dstIsTemp, dstIsTempTransferred, instr, backwardPass);
- }
- NumberTemp::NumberTemp(JitArenaAllocator * alloc, bool inLoop)
- : TempTrackerBase(alloc, inLoop), elemLoadDependencies(alloc), nonTempElemLoad(false),
- upwardExposedMarkTempObjectLiveFields(alloc), upwardExposedMarkTempObjectSymsProperties(nullptr)
- {
- propertyIdsTempTransferDependencies = inLoop ? HashTable<BVSparse<JitArenaAllocator> *>::New(alloc, 16) : nullptr;
- }
- void
- NumberTemp::MergeData(NumberTemp * fromData, bool deleteData)
- {
- nonTempElemLoad = nonTempElemLoad || fromData->nonTempElemLoad;
- if (!nonTempElemLoad) // Don't bother merging other data if we already have a nonTempElemLoad
- {
- if (IsInLoop())
- {
- // in loop
- elemLoadDependencies.Or(&fromData->elemLoadDependencies);
- }
- MergeDependencies(propertyIdsTempTransferDependencies, fromData->propertyIdsTempTransferDependencies, deleteData);
- if (fromData->upwardExposedMarkTempObjectSymsProperties)
- {
- if (upwardExposedMarkTempObjectSymsProperties)
- {
- OrHashTableOfBitVector(upwardExposedMarkTempObjectSymsProperties, fromData->upwardExposedMarkTempObjectSymsProperties, deleteData);
- }
- else if (deleteData)
- {
- upwardExposedMarkTempObjectSymsProperties = fromData->upwardExposedMarkTempObjectSymsProperties;
- fromData->upwardExposedMarkTempObjectSymsProperties = nullptr;
- }
- else
- {
- upwardExposedMarkTempObjectSymsProperties = HashTable<BVSparse<JitArenaAllocator> *>::New(this->GetAllocator(), 16);
- OrHashTableOfBitVector(upwardExposedMarkTempObjectSymsProperties, fromData->upwardExposedMarkTempObjectSymsProperties, deleteData);
- }
- }
- upwardExposedMarkTempObjectLiveFields.Or(&fromData->upwardExposedMarkTempObjectLiveFields);
- }
- }
- bool
- NumberTemp::IsTempUse(IR::Instr * instr, Sym * sym, BackwardPass * backwardPass)
- {
- Js::OpCode opcode = instr->m_opcode;
- if (OpCodeAttr::NonTempNumberSources(opcode)
- || (OpCodeAttr::TempNumberTransfer(opcode) && !instr->dstIsTempNumber))
- {
- // For TypedArray stores, we don't store the Var object, so MarkTemp is valid
- if (opcode != Js::OpCode::StElemI_A
- || !instr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->GetValueType().IsLikelyOptimizedTypedArray())
- {
- // Mark the symbol as non-tempable if the instruction doesn't allow temp sources,
- // or it is transferred to a non-temp dst
- return false;
- }
- }
- return true;
- }
- bool
- NumberTemp::IsTempTransfer(IR::Instr * instr)
- {
- return OpCodeAttr::TempNumberTransfer(instr->m_opcode);
- }
- bool
- NumberTemp::IsTempProducing(IR::Instr * instr)
- {
- Js::OpCode opcode = instr->m_opcode;
- if (OpCodeAttr::TempNumberProducing(opcode))
- {
- return true;
- }
- // Loads from float typedArrays usually require a conversion to Var, which we can MarkTemp.
- if (opcode == Js::OpCode::LdElemI_A)
- {
- const ValueType baseValueType(instr->GetSrc1()->AsIndirOpnd()->GetBaseOpnd()->GetValueType());
- if (baseValueType.IsLikelyObject()
- && (baseValueType.GetObjectType() == ObjectType::Float32Array
- || baseValueType.GetObjectType() == ObjectType::Float64Array))
- {
- return true;
- }
- }
- return false;
- }
- bool
- NumberTemp::CanMarkTemp(IR::Instr * instr, BackwardPass * backwardPass)
- {
- if (IsTempTransfer(instr) || IsTempProducing(instr))
- {
- return true;
- }
- // REVIEW: this is added a long time ago, and I am not sure what this is for any more.
- if (OpCodeAttr::InlineCallInstr(instr->m_opcode))
- {
- return true;
- }
- if (NumberTemp::IsTempIndirTransferLoad(instr, backwardPass)
- || NumberTemp::IsTempPropertyTransferLoad(instr, backwardPass))
- {
- return true;
- }
- // the opcode is not temp producing or a transfer, this is not a tmp
- // Also mark calls which may get inlined.
- return false;
- }
- void
- NumberTemp::ProcessInstr(IR::Instr * instr, BackwardPass * backwardPass)
- {
- #if DBG
- if (instr->m_opcode == Js::OpCode::BailOnNoProfile)
- {
- // If we see BailOnNoProfile, we shouldn't have any successor to have any non temp syms
- Assert(!this->nonTempElemLoad);
- Assert(this->nonTempSyms.IsEmpty());
- Assert(this->tempTransferredSyms.IsEmpty());
- Assert(this->elemLoadDependencies.IsEmpty());
- Assert(this->upwardExposedMarkTempObjectLiveFields.IsEmpty());
- }
- #endif
- // We don't get to process all dst in MarkTemp. Do it here for the upwardExposedMarkTempObjectLiveFields
- if (!this->DoMarkTempNumbersOnTempObjects(backwardPass))
- {
- return;
- }
- IR::Opnd * dst = instr->GetDst();
- if (dst == nullptr || !dst->IsRegOpnd())
- {
- return;
- }
- StackSym * dstSym = dst->AsRegOpnd()->m_sym;
- if (!dstSym->IsVar())
- {
- dstSym = dstSym->GetVarEquivSym(nullptr);
- if (dstSym == nullptr)
- {
- return;
- }
- }
- SymID dstSymId = dstSym->m_id;
- if (this->upwardExposedMarkTempObjectSymsProperties)
- {
- // We are assigning to dstSym, it no longer has upward exposed use, get the information and clear it from the hash table
- BVSparse<JitArenaAllocator> * dstBv = this->upwardExposedMarkTempObjectSymsProperties->GetAndClear(dstSymId);
- if (dstBv)
- {
- // Clear the upward exposed live fields of all the property sym id associated to dstSym
- this->upwardExposedMarkTempObjectLiveFields.Minus(dstBv);
- if (ObjectTemp::IsTempTransfer(instr) && instr->GetSrc1()->IsRegOpnd())
- {
- // If it is transfer, copy the dst info to the src
- SymID srcStackSymId = instr->GetSrc1()->AsRegOpnd()->m_sym->AsStackSym()->m_id;
- SymTable * symTable = backwardPass->func->m_symTable;
- FOREACH_BITSET_IN_SPARSEBV(index, dstBv)
- {
- PropertySym * propertySym = symTable->FindPropertySym(srcStackSymId, index);
- if (propertySym)
- {
- this->upwardExposedMarkTempObjectLiveFields.Set(propertySym->m_id);
- }
- }
- NEXT_BITSET_IN_SPARSEBV;
- BVSparse<JitArenaAllocator> ** srcBv = this->upwardExposedMarkTempObjectSymsProperties->FindOrInsert(dstBv, srcStackSymId);
- if (srcBv)
- {
- (*srcBv)->Or(dstBv);
- JitAdelete(this->GetAllocator(), dstBv);
- }
- }
- else
- {
- JitAdelete(this->GetAllocator(), dstBv);
- }
- }
- }
- }
- void
- NumberTemp::SetDstIsTemp(bool dstIsTemp, bool dstIsTempTransferred, IR::Instr * instr, BackwardPass * backwardPass)
- {
- Assert(dstIsTemp || !dstIsTempTransferred);
- Assert(!instr->dstIsTempNumberTransferred);
- instr->dstIsTempNumber = dstIsTemp;
- instr->dstIsTempNumberTransferred = dstIsTempTransferred;
- #if DBG_DUMP
- if (!backwardPass->IsPrePass() && IsTempProducing(instr))
- {
- backwardPass->numMarkTempNumber += dstIsTemp;
- backwardPass->numMarkTempNumberTransferred += dstIsTempTransferred;
- }
- #endif
- }
- bool
- NumberTemp::IsTempPropertyTransferLoad(IR::Instr * instr, BackwardPass * backwardPass)
- {
- if (DoMarkTempNumbersOnTempObjects(backwardPass))
- {
- switch (instr->m_opcode)
- {
- case Js::OpCode::LdFld:
- case Js::OpCode::LdFldForTypeOf:
- case Js::OpCode::LdMethodFld:
- case Js::OpCode::LdFldForCallApplyTarget:
- case Js::OpCode::LdMethodFromFlags:
- {
- // Only care about load from possible stack allocated object.
- return instr->GetSrc1()->CanStoreTemp();
- }
- };
- // All other opcode shouldn't have sym opnd that can store temp, See ObjectTemp::IsTempUseOpCodeSym.
- Assert(instr->GetSrc1() == nullptr
- || instr->GetDst() == nullptr // this isn't a value loading instruction
- || instr->GetSrc1()->IsIndirOpnd() // this is detected in IsTempIndirTransferLoad
- || !instr->GetSrc1()->CanStoreTemp());
- }
- return false;
- }
- bool
- NumberTemp::IsTempPropertyTransferStore(IR::Instr * instr, BackwardPass * backwardPass)
- {
- if (DoMarkTempNumbersOnTempObjects(backwardPass))
- {
- switch (instr->m_opcode)
- {
- case Js::OpCode::InitFld:
- case Js::OpCode::StFld:
- case Js::OpCode::StFldStrict:
- {
- IR::Opnd * dst = instr->GetDst();
- Assert(dst->IsSymOpnd());
- if (!dst->CanStoreTemp())
- {
- return false;
- }
- // We don't mark temp store of numeric properties (e.g. object literal { 86: foo });
- // This should only happen for InitFld, as StFld should have changed to StElem
- PropertySym *propertySym = dst->AsSymOpnd()->m_sym->AsPropertySym();
- SymID propertySymId = this->GetRepresentativePropertySymId(propertySym, backwardPass);
- return !this->nonTempSyms.Test(propertySymId) &&
- !instr->m_func->GetScriptContext()->GetPropertyNameLocked(propertySym->m_propertyId)->IsNumeric();
- }
- };
- // All other opcode shouldn't have sym opnd that can store temp, see ObjectTemp::IsTempUseOpCodeSym.
- // We also never mark the dst indir as can store temp for StElemI_A because we don't know what property
- // it is storing in (or it could be an array index).
- Assert(instr->GetDst() == nullptr || !instr->GetDst()->CanStoreTemp());
- }
- return false;
- }
- bool
- NumberTemp::IsTempIndirTransferLoad(IR::Instr * instr, BackwardPass * backwardPass)
- {
- if (DoMarkTempNumbersOnTempObjects(backwardPass))
- {
- if (instr->m_opcode == Js::OpCode::LdElemI_A)
- {
- // If the index is an int, then we don't care about the non-temp use
- IR::Opnd * src1Opnd = instr->GetSrc1();
- IR::RegOpnd * indexOpnd = src1Opnd->AsIndirOpnd()->GetIndexOpnd();
- if (indexOpnd && (indexOpnd->m_sym->m_isNotInt || !indexOpnd->GetValueType().IsInt()))
- {
- return src1Opnd->CanStoreTemp();
- }
- }
- else
- {
- // All other opcode shouldn't have sym opnd that can store temp, See ObjectTemp::IsTempUseOpCodeSym.
- Assert(instr->GetSrc1() == nullptr || instr->GetSrc1()->IsSymOpnd()
- || !instr->GetSrc1()->CanStoreTemp());
- }
- }
- return false;
- }
- void
- NumberTemp::PropagateTempPropertyTransferStoreDependencies(SymID usedSymID, PropertySym * propertySym, BackwardPass * backwardPass)
- {
- Assert(!this->nonTempElemLoad);
- upwardExposedMarkTempObjectLiveFields.Clear(propertySym->m_id);
- if (!this->IsInLoop())
- {
- // Don't need to track dependencies outside of loop, as we already marked the
- // use as temp transfer already and we won't have a case where the "dst" is reused again (outside of loop)
- return;
- }
- Assert(this->tempTransferDependencies != nullptr);
- SymID dstSymID = this->GetRepresentativePropertySymId(propertySym, backwardPass);
- AddTransferDependencies(usedSymID, dstSymID, this->tempTransferDependencies);
- Js::PropertyId storedPropertyId = propertySym->m_propertyId;
- // The symbol this properties are transferred to
- BVSparse<JitArenaAllocator> ** pPropertyTransferDependencies = this->propertyIdsTempTransferDependencies->Get(storedPropertyId);
- BVSparse<JitArenaAllocator> * transferDependencies = nullptr;
- if (pPropertyTransferDependencies == nullptr)
- {
- if (elemLoadDependencies.IsEmpty())
- {
- // No dependencies to transfer
- return;
- }
- transferDependencies = &elemLoadDependencies;
- }
- else
- {
- transferDependencies = *pPropertyTransferDependencies;
- }
- BVSparse<JitArenaAllocator> ** pBVSparse = this->tempTransferDependencies->FindOrInsertNew(usedSymID);
- if (*pBVSparse == nullptr)
- {
- *pBVSparse = transferDependencies->CopyNew(this->GetAllocator());
- }
- else
- {
- (*pBVSparse)->Or(transferDependencies);
- }
- if (transferDependencies != &elemLoadDependencies)
- {
- // Always include the element load dependencies as well
- (*pBVSparse)->Or(&elemLoadDependencies);
- }
- // Track the propertySym as well for the case where the dependence is not carried by the use
- // Loop1
- // o.x = e
- // Loop2
- // f = o.x
- // = f
- // e = e + blah
- // Here, although we can detect that e and f has dependent relationship, f's life time doesn't cross with e's.
- // But o.x will keep the value of e alive, so e can't be mark temp because o.x is still in use (not f)
- // We will add the property sym int he dependency set and check with the upward exposed mark temp object live fields
- // that we keep track of in NumberTemp
- (*pBVSparse)->Set(propertySym->m_id);
- }
- SymID
- NumberTemp::GetRepresentativePropertySymId(PropertySym * propertySym, BackwardPass * backwardPass)
- {
- // Since we don't track alias with objects, all property accesses are all grouped together.
- // Use a single property sym id to represent a propertyId to track dependencies.
- SymID symId;
- Js::PropertyId propertyId = propertySym->m_propertyId;
- if (!backwardPass->numberTempRepresentativePropertySym->TryGetValue(propertyId, &symId))
- {
- symId = propertySym->m_id;
- backwardPass->numberTempRepresentativePropertySym->Add(propertyId, symId);
- }
- return symId;
- }
- void
- NumberTemp::ProcessIndirUse(IR::IndirOpnd * indirOpnd, IR::Instr * instr, BackwardPass * backwardPass)
- {
- Assert(backwardPass->DoMarkTempNumbersOnTempObjects());
- if (!NumberTemp::IsTempIndirTransferLoad(instr, backwardPass))
- {
- return;
- }
- bool isTempUse = instr->dstIsTempNumber;
- if (!isTempUse)
- {
- nonTempElemLoad = true;
- }
- else if (this->IsInLoop())
- {
- // We didn't already detect non temp use of this property id. so we should track the dependencies in loops
- IR::Opnd * dstOpnd = instr->GetDst();
- Assert(dstOpnd->IsRegOpnd());
- SymID dstSymID = dstOpnd->AsRegOpnd()->m_sym->m_id;
- // Use the no property id as a place holder for elem dependencies
- AddTransferDependencies(&elemLoadDependencies, dstSymID);
- #if DBG_DUMP
- if (NumberTemp::DoTrace(backwardPass))
- {
- Output::Print(L"%s: %8s s%d -> []: ", NumberTemp::GetTraceName(),
- backwardPass->IsPrePass() ? L"Prepass " : L"", dstSymID);
- elemLoadDependencies.Dump();
- }
- #endif
- }
- #if DBG_DUMP
- if (NumberTemp::DoTrace(backwardPass))
- {
- Output::Print(L"%s: %8s%4sTemp Use ([] )", NumberTemp::GetTraceName(),
- backwardPass->IsPrePass() ? L"Prepass " : L"", isTempUse ? L"" : L"Non ");
- instr->DumpSimple();
- }
- #endif
- }
- void
- NumberTemp::ProcessPropertySymUse(IR::SymOpnd * symOpnd, IR::Instr * instr, BackwardPass * backwardPass)
- {
- Assert(backwardPass->DoMarkTempNumbersOnTempObjects());
- // We only care about instruction that may transfer the property value
- if (!NumberTemp::IsTempPropertyTransferLoad(instr, backwardPass))
- {
- return;
- }
- PropertySym * propertySym = symOpnd->m_sym->AsPropertySym();
- upwardExposedMarkTempObjectLiveFields.Set(propertySym->m_id);
- if (upwardExposedMarkTempObjectSymsProperties == nullptr)
- {
- upwardExposedMarkTempObjectSymsProperties = HashTable<BVSparse<JitArenaAllocator> *>::New(this->GetAllocator(), 16);
- }
- BVSparse<JitArenaAllocator> ** bv = upwardExposedMarkTempObjectSymsProperties->FindOrInsertNew(propertySym->m_stackSym->m_id);
- if (*bv == nullptr)
- {
- *bv = JitAnew(this->GetAllocator(), BVSparse<JitArenaAllocator>, this->GetAllocator());
- }
- (*bv)->Set(propertySym->m_propertyId);
- SymID propertySymId = this->GetRepresentativePropertySymId(propertySym, backwardPass);
- bool isTempUse = instr->dstIsTempNumber;
- if (!isTempUse)
- {
- // Use of the value is non temp, track the property ID's property representative sym so we don't mark temp
- // assignment to this property on stack objects.
- this->nonTempSyms.Set(propertySymId);
- }
- else if (this->IsInLoop() && !this->nonTempSyms.Test(propertySymId))
- {
- // We didn't already detect non temp use of this property id. so we should track the dependencies in loops
- IR::Opnd * dstOpnd = instr->GetDst();
- Assert(dstOpnd->IsRegOpnd());
- SymID dstSymID = dstOpnd->AsRegOpnd()->m_sym->m_id;
- AddTransferDependencies(propertySym->m_propertyId, dstSymID, this->propertyIdsTempTransferDependencies);
- #if DBG_DUMP
- if (NumberTemp::DoTrace(backwardPass))
- {
- Output::Print(L"%s: %8s s%d -> PropId:%d %s: ", NumberTemp::GetTraceName(),
- backwardPass->IsPrePass() ? L"Prepass " : L"", dstSymID, propertySym->m_propertyId,
- backwardPass->func->GetScriptContext()->GetPropertyNameLocked(propertySym->m_propertyId)->GetBuffer());
- (*this->propertyIdsTempTransferDependencies->Get(propertySym->m_propertyId))->Dump();
- }
- #endif
- }
- #if DBG_DUMP
- if (NumberTemp::DoTrace(backwardPass))
- {
- Output::Print(L"%s: %8s%4sTemp Use (PropId:%d %s)", NumberTemp::GetTraceName(),
- backwardPass->IsPrePass() ? L"Prepass " : L"", isTempUse ? L"" : L"Non ", propertySym->m_propertyId,
- backwardPass->func->GetScriptContext()->GetPropertyNameLocked(propertySym->m_propertyId)->GetBuffer());
- instr->DumpSimple();
- }
- #endif
- }
- bool
- NumberTemp::HasExposedFieldDependencies(BVSparse<JitArenaAllocator> * bvTempTransferDependencies, BackwardPass * backwardPass)
- {
- if (!DoMarkTempNumbersOnTempObjects(backwardPass))
- {
- return false;
- }
- return bvTempTransferDependencies->Test(&upwardExposedMarkTempObjectLiveFields);
- }
- bool
- NumberTemp::DoMarkTempNumbersOnTempObjects(BackwardPass * backwardPass) const
- {
- return backwardPass->DoMarkTempNumbersOnTempObjects() && !this->nonTempElemLoad;
- }
- #if DBG
- void
- NumberTemp::Dump(wchar_t const * traceName)
- {
- if (nonTempElemLoad)
- {
- Output::Print(L"%s: Has Non Temp Elem Load\n", traceName);
- }
- else
- {
- Output::Print(L"%s: Non Temp Syms", traceName);
- this->nonTempSyms.Dump();
- if (this->propertyIdsTempTransferDependencies != nullptr)
- {
- Output::Print(L"%s: Temp transfer propertyId dependencies:\n", traceName);
- this->propertyIdsTempTransferDependencies->Dump();
- }
- }
- }
- #endif
- //=================================================================================================
- // ObjectTemp
- //=================================================================================================
- bool
- ObjectTemp::IsTempUse(IR::Instr * instr, Sym * sym, BackwardPass * backwardPass)
- {
- Js::OpCode opcode = instr->m_opcode;
- // If the opcode has implicit call and the profile say we have implicit call, then it is not a temp use
- // TODO: More precise implicit call tracking
- if (instr->HasAnyImplicitCalls()
- &&
- ((backwardPass->currentBlock->loop != nullptr ?
- !GlobOpt::ImplicitCallFlagsAllowOpts(backwardPass->currentBlock->loop) :
- !GlobOpt::ImplicitCallFlagsAllowOpts(backwardPass->func))
- || instr->CallsAccessor())
- )
- {
- return false;
- }
- return IsTempUseOpCodeSym(instr, opcode, sym);
- }
- bool
- ObjectTemp::IsTempUseOpCodeSym(IR::Instr * instr, Js::OpCode opcode, Sym * sym)
- {
- // Special case ArgOut_A which communicate information about CallDirect
- switch (opcode)
- {
- case Js::OpCode::ArgOut_A:
- return instr->dstIsTempObject;
- case Js::OpCode::LdFld:
- case Js::OpCode::LdFldForTypeOf:
- case Js::OpCode::LdMethodFld:
- case Js::OpCode::LdFldForCallApplyTarget:
- case Js::OpCode::LdMethodFromFlags:
- return instr->GetSrc1()->AsPropertySymOpnd()->GetObjectSym() == sym;
- case Js::OpCode::InitFld:
- if (Js::PropertyRecord::DefaultAttributesForPropertyId(
- instr->GetDst()->AsPropertySymOpnd()->GetPropertySym()->m_propertyId, true) & PropertyDeleted)
- {
- // If the property record is marked PropertyDeleted, the InitFld will cause a type handler conversion,
- // which may result in creation of a weak reference to the object itself.
- return false;
- }
- // Fall through
- case Js::OpCode::StFld:
- case Js::OpCode::StFldStrict:
- return
- !(instr->GetSrc1() && instr->GetSrc1()->GetStackSym() == sym) &&
- !(instr->GetSrc2() && instr->GetSrc2()->GetStackSym() == sym) &&
- instr->GetDst()->AsPropertySymOpnd()->GetObjectSym() == sym;
- case Js::OpCode::LdElemI_A:
- return instr->GetSrc1()->AsIndirOpnd()->GetBaseOpnd()->m_sym == sym;
- case Js::OpCode::StElemI_A:
- case Js::OpCode::StElemI_A_Strict:
- return instr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->m_sym == sym;
- case Js::OpCode::Memset:
- return instr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->m_sym == sym || instr->GetSrc1()->IsRegOpnd() && instr->GetSrc1()->AsRegOpnd()->m_sym == sym;
- case Js::OpCode::Memcopy:
- return instr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->m_sym == sym || instr->GetSrc1()->AsIndirOpnd()->GetBaseOpnd()->m_sym == sym;
- // Special case FromVar for now until we can allow CallsValueOf opcode to be accept temp use
- case Js::OpCode::FromVar:
- return true;
- }
- // TODO: Currently, when we disable implicit call, we still don't allow valueOf/toString that has no side effects
- // So we shouldn't mark them if we have use of the sym on opcode that does CallsValueOf yet.
- if (OpCodeAttr::CallsValueOf(opcode))
- {
- return false;
- }
- // Mark the symbol as non-tempable if the instruction doesn't allow temp sources,
- // or it is transferred to a non-temp dst
- return (OpCodeAttr::TempObjectSources(opcode)
- && (!OpCodeAttr::TempObjectTransfer(opcode) || instr->dstIsTempObject));
- }
- bool
- ObjectTemp::IsTempTransfer(IR::Instr * instr)
- {
- return OpCodeAttr::TempObjectTransfer(instr->m_opcode);
- }
- bool
- ObjectTemp::IsTempProducing(IR::Instr * instr)
- {
- Js::OpCode opcode = instr->m_opcode;
- if (OpCodeAttr::TempObjectProducing(opcode))
- {
- return true;
- }
- // TODO: Process NewScObject and CallI with isCtorCall when the ctor is fixed
- return false;
- }
- bool
- ObjectTemp::CanStoreTemp(IR::Instr * instr)
- {
- // In order to allow storing temp number on temp objects,
- // We have to make sure that if the instr is marked as dstIsTempObject
- // we will always generate the code to allocate the object on the stack (so no helper call).
- // Currently, we only do this for NewRegEx, NewScObjectSimple, NewScObjectLiteral and
- // NewScObjectNoCtor (where the ctor is inlined).
- // CONSIDER: review lowering of other TempObjectProducing opcode and see if we can always allocate on the stack
- // (for example, NewScArray should be able to, but plain NewScObject can't because the size depends on the
- // number inline slots)
- Js::OpCode opcode = instr->m_opcode;
- if (OpCodeAttr::TempObjectCanStoreTemp(opcode))
- {
- // Special cases where stack allocation doesn't happen
- #if ENABLE_REGEX_CONFIG_OPTIONS
- if (opcode == Js::OpCode::NewRegEx && REGEX_CONFIG_FLAG(RegexTracing))
- {
- return false;
- }
- #endif
- if (opcode == Js::OpCode::NewScObjectNoCtor)
- {
- if (PHASE_OFF(Js::FixedNewObjPhase, instr->m_func->GetJnFunction()) && PHASE_OFF(Js::ObjTypeSpecNewObjPhase, instr->m_func->GetTopFunc()))
- {
- return false;
- }
- // Only if we have BailOutFailedCtorGuardCheck would we generate a stack object.
- // Otherwise we will call the helper, which will not generate stack object.
- return instr->HasBailOutInfo();
- }
- return true;
- }
- return false;
- }
- bool
- ObjectTemp::CanMarkTemp(IR::Instr * instr, BackwardPass * backwardPass)
- {
- // We mark the ArgOut with the call in ProcessInstr, no need to do it here
- return IsTempProducing(instr) || IsTempTransfer(instr);
- }
- void
- ObjectTemp::ProcessBailOnNoProfile(IR::Instr * instr)
- {
- Assert(instr->m_opcode == Js::OpCode::BailOnNoProfile);
- // ObjectTemp is done during Backward pass, which hasn't change all succ to BailOnNoProfile
- // to dead yet, so we need to manually clear all the information
- this->nonTempSyms.ClearAll();
- this->tempTransferredSyms.ClearAll();
- if (this->tempTransferDependencies)
- {
- this->tempTransferDependencies->ClearAll();
- }
- }
- void
- ObjectTemp::ProcessInstr(IR::Instr * instr)
- {
- if (instr->m_opcode != Js::OpCode::CallDirect)
- {
- return;
- }
- IR::HelperCallOpnd * helper = instr->GetSrc1()->AsHelperCallOpnd();
- switch (helper->m_fnHelper)
- {
- case IR::JnHelperMethod::HelperString_Match:
- case IR::JnHelperMethod::HelperString_Replace:
- {
- // First (non-this) parameter is either a regexp or search string.
- // It doesn't escape.
- IR::Instr * instrArgDef;
- instr->FindCallArgumentOpnd(2, &instrArgDef);
- instrArgDef->dstIsTempObject = true;
- break;
- }
- case IR::JnHelperMethod::HelperRegExp_Exec:
- {
- IR::Instr * instrArgDef;
- instr->FindCallArgumentOpnd(1, &instrArgDef);
- instrArgDef->dstIsTempObject = true;
- break;
- }
- };
- }
- void
- ObjectTemp::SetDstIsTemp(bool dstIsTemp, bool dstIsTempTransferred, IR::Instr * instr, BackwardPass * backwardPass)
- {
- Assert(dstIsTemp || !dstIsTempTransferred);
- // ArgOut_A are marked by CallDirect and don't need to be set
- if (instr->m_opcode == Js::OpCode::ArgOut_A)
- {
- return;
- }
- instr->dstIsTempObject = dstIsTemp;
- if (!backwardPass->IsPrePass())
- {
- if (OpCodeAttr::TempObjectProducing(instr->m_opcode))
- {
- backwardPass->func->SetHasMarkTempObjects();
- #if DBG_DUMP
- backwardPass->numMarkTempObject += dstIsTemp;
- #endif
- }
- }
- }
- StackSym *
- ObjectTemp::GetStackSym(IR::Opnd * opnd, IR::PropertySymOpnd ** pPropertySymOpnd)
- {
- StackSym * stackSym = nullptr;
- switch (opnd->GetKind())
- {
- case IR::OpndKindReg:
- stackSym = opnd->AsRegOpnd()->m_sym;
- break;
- case IR::OpndKindSym:
- {
- IR::SymOpnd * symOpnd = opnd->AsSymOpnd();
- if (symOpnd->IsPropertySymOpnd())
- {
- IR::PropertySymOpnd * propertySymOpnd = symOpnd->AsPropertySymOpnd();
- *pPropertySymOpnd = propertySymOpnd;
- stackSym = propertySymOpnd->GetObjectSym();
- }
- else if (symOpnd->m_sym->IsPropertySym())
- {
- stackSym = symOpnd->m_sym->AsPropertySym()->m_stackSym;
- }
- break;
- }
- case IR::OpndKindIndir:
- stackSym = opnd->AsIndirOpnd()->GetBaseOpnd()->m_sym;
- break;
- };
- return stackSym;
- }
- #if DBG
- //=================================================================================================
- // ObjectTempVerify
- //=================================================================================================
- ObjectTempVerify::ObjectTempVerify(JitArenaAllocator * alloc, bool inLoop)
- : TempTrackerBase(alloc, inLoop), removedUpwardExposedUse(alloc)
- {
- }
- bool
- ObjectTempVerify::IsTempUse(IR::Instr * instr, Sym * sym, BackwardPass * backwardPass)
- {
- Js::OpCode opcode = instr->m_opcode;
- // If the opcode has implicit call and the profile say we have implicit call, then it is not a temp use.
- // TODO: More precise implicit call tracking
- bool isLandingPad = backwardPass->currentBlock->IsLandingPad();
- if (OpCodeAttr::HasImplicitCall(opcode) && !isLandingPad
- &&
- ((backwardPass->currentBlock->loop != nullptr ?
- !GlobOpt::ImplicitCallFlagsAllowOpts(backwardPass->currentBlock->loop) :
- !GlobOpt::ImplicitCallFlagsAllowOpts(backwardPass->func))
- || instr->CallsAccessor())
- )
- {
- return false;
- }
- if (!ObjectTemp::IsTempUseOpCodeSym(instr, opcode, sym))
- {
- // the opcode and sym is not a temp use, just return
- return false;
- }
- // In the backward pass, this would have been a temp use already. Continue to verify
- // if we have install sufficient bailout on implicit call
- if (isLandingPad || !GlobOpt::MayNeedBailOnImplicitCall(instr, nullptr, nullptr))
- {
- // Implicit call would not happen, or we are in the landing pad where implicit call is disabled.
- return true;
- }
- if (instr->HasBailOutInfo())
- {
- // make sure we have mark the bailout for mark temp object,
- // so that we won't optimize it away in DeadStoreImplicitCalls
- return ((instr->GetBailOutKind() & IR::BailOutMarkTempObject) != 0);
- }
- // Review (ObjTypeSpec): This is a bit conservative now that we don't revert from obj type specialized operations to live cache
- // access even if the operation is isolated. Once we decide a given instruction is an object type spec candidate, we know it
- // will never need an implicit call, so we could basically do opnd->IsObjTypeSpecOptimized() here, instead.
- if (GlobOpt::IsTypeCheckProtected(instr))
- {
- return true;
- }
- return false;
- }
- bool
- ObjectTempVerify::IsTempTransfer(IR::Instr * instr)
- {
- if (ObjectTemp::IsTempTransfer(instr)
- // Add the Ld_I4, and LdC_A_I4 as the forward pass might have changed Ld_A to these
- || instr->m_opcode == Js::OpCode::Ld_I4
- || instr->m_opcode == Js::OpCode::LdC_A_I4)
- {
- if (!instr->dstIsTempObject && instr->GetDst() && instr->GetDst()->IsRegOpnd()
- && instr->GetDst()->AsRegOpnd()->GetValueType().IsNotObject())
- {
- // Globopt has proved that dst is not an object, so this is not really an object transfer.
- // This prevents the case where glob opt turned a Conv_Num to Ld_A and expose additional
- // transfer.
- return false;
- }
- return true;
- }
- return false;
- }
- bool
- ObjectTempVerify::CanMarkTemp(IR::Instr * instr, BackwardPass * backwardPass)
- {
- // We mark the ArgOut with the call in ProcessInstr, no need to do it here
- return ObjectTemp::IsTempProducing(instr)
- || IsTempTransfer(instr);
- }
- void
- ObjectTempVerify::ProcessInstr(IR::Instr * instr, BackwardPass * backwardPass)
- {
- if (instr->m_opcode == Js::OpCode::InlineThrow)
- {
- // We cannot accurately track mark temp for any upward exposed symbol here
- this->removedUpwardExposedUse.Or(backwardPass->currentBlock->byteCodeUpwardExposedUsed);
- return;
- }
- if (instr->m_opcode != Js::OpCode::CallDirect)
- {
- return;
- }
- IR::HelperCallOpnd * helper = instr->GetSrc1()->AsHelperCallOpnd();
- switch (helper->m_fnHelper)
- {
- case IR::JnHelperMethod::HelperString_Match:
- case IR::JnHelperMethod::HelperString_Replace:
- {
- // First (non-this) parameter is either a regexp or search string
- // It doesn't escape
- IR::Instr * instrArgDef;
- instr->FindCallArgumentOpnd(2, &instrArgDef);
- Assert(instrArgDef->dstIsTempObject);
- break;
- }
- case IR::JnHelperMethod::HelperRegExp_Exec:
- {
- IR::Instr * instrArgDef;
- instr->FindCallArgumentOpnd(1, &instrArgDef);
- Assert(instrArgDef->dstIsTempObject);
- break;
- }
- };
- }
- void
- ObjectTempVerify::SetDstIsTemp(bool dstIsTemp, bool dstIsTempTransferred, IR::Instr * instr, BackwardPass * backwardPass)
- {
- Assert(dstIsTemp || !dstIsTempTransferred);
- // ArgOut_A are marked by CallDirect and don't need to be set
- if (instr->m_opcode == Js::OpCode::ArgOut_A)
- {
- return;
- }
- if (OpCodeAttr::TempObjectProducing(instr->m_opcode))
- {
- if (!backwardPass->IsPrePass())
- {
- if (dstIsTemp)
- {
- // Don't assert if we have detected a removed upward exposed use that could
- // expose a new mark temp object. Don't assert if it is set in removedUpwardExposedUse
- bool isBailOnNoProfileUpwardExposedUse =
- !!this->removedUpwardExposedUse.Test(instr->GetDst()->AsRegOpnd()->m_sym->m_id);
- #if DBG
- if (DoTrace(backwardPass) && !instr->dstIsTempObject && !isBailOnNoProfileUpwardExposedUse)
- {
- Output::Print(L"%s: Missed Mark Temp Object: ", GetTraceName());
- instr->DumpSimple();
- Output::Flush();
- }
- #endif
- // TODO: Unfortunately we still hit this a lot as we are not accounting for some of the globopt changes
- // to the IR. It is just reporting that we have missed mark temp object opportunity, so it doesn't
- // indicate a functional failure. Disable for now.
- // Assert(instr->dstIsTempObject || isBailOnNoProfileUpwardExposedUse);
- }
- else
- {
- // If we have marked the dst is temp in the backward pass, the globopt
- // should have maintained it, and it will be wrong to have detect that it is not
- // temp now in the deadstore pass (whether there is BailOnNoProfile or not)
- #if DBG
- if (DoTrace(backwardPass) && instr->dstIsTempObject)
- {
- Output::Print(L"%s: Invalid Mark Temp Object: ", GetTraceName());
- instr->DumpSimple();
- Output::Flush();
- }
- #endif
- Assert(!instr->dstIsTempObject);
- }
- }
- }
- else if (IsTempTransfer(instr))
- {
- // Only set the transfer
- instr->dstIsTempObject = dstIsTemp;
- }
- else
- {
- Assert(!dstIsTemp);
- Assert(!instr->dstIsTempObject);
- }
- // clear or transfer the bailOnNoProfile upward exposed use
- if (this->removedUpwardExposedUse.TestAndClear(instr->GetDst()->AsRegOpnd()->m_sym->m_id)
- && IsTempTransfer(instr) && instr->GetSrc1()->IsRegOpnd())
- {
- this->removedUpwardExposedUse.Set(instr->GetSrc1()->AsRegOpnd()->m_sym->m_id);
- }
- }
- void
- ObjectTempVerify::MergeData(ObjectTempVerify * fromData, bool deleteData)
- {
- this->removedUpwardExposedUse.Or(&fromData->removedUpwardExposedUse);
- }
- void
- ObjectTempVerify::MergeDeadData(BasicBlock * block)
- {
- MergeData(block->tempObjectVerifyTracker, false);
- if (!block->isDead)
- {
- // If there was dead flow to a block that is not dead, it might expose
- // new mark temp object, so all its current used (upwardExposedUsed) and optimized
- // use (byteCodeupwardExposedUsed) might not be trace for "missed" mark temp object
- this->removedUpwardExposedUse.Or(block->upwardExposedUses);
- if (block->byteCodeUpwardExposedUsed)
- {
- this->removedUpwardExposedUse.Or(block->byteCodeUpwardExposedUsed);
- }
- }
- }
- void
- ObjectTempVerify::NotifyBailOutRemoval(IR:: Instr * instr, BackwardPass * backwardPass)
- {
- Js::OpCode opcode = instr->m_opcode;
- switch (opcode)
- {
- case Js::OpCode::LdFld:
- case Js::OpCode::LdFldForTypeOf:
- case Js::OpCode::LdMethodFld:
- ((TempTracker<ObjectTempVerify> *)this)->ProcessUse(instr->GetSrc1()->AsPropertySymOpnd()->GetObjectSym(), backwardPass);
- break;
- case Js::OpCode::InitFld:
- case Js::OpCode::StFld:
- case Js::OpCode::StFldStrict:
- ((TempTracker<ObjectTempVerify> *)this)->ProcessUse(instr->GetDst()->AsPropertySymOpnd()->GetObjectSym(), backwardPass);
- break;
- case Js::OpCode::LdElemI_A:
- ((TempTracker<ObjectTempVerify> *)this)->ProcessUse(instr->GetSrc1()->AsIndirOpnd()->GetBaseOpnd()->m_sym, backwardPass);
- break;
- case Js::OpCode::StElemI_A:
- ((TempTracker<ObjectTempVerify> *)this)->ProcessUse(instr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->m_sym, backwardPass);
- break;
- }
- }
- void
- ObjectTempVerify::NotifyReverseCopyProp(IR::Instr * instr)
- {
- Assert(instr->GetDst());
- SymID symId = instr->GetDst()->AsRegOpnd()->m_sym->m_id;
- this->removedUpwardExposedUse.Clear(symId);
- this->nonTempSyms.Clear(symId);
- }
- void
- ObjectTempVerify::NotifyDeadStore(IR::Instr * instr, BackwardPass * backwardPass)
- {
- // Even if we dead store, simulate the uses
- IR::Opnd * src1 = instr->GetSrc1();
- if (src1)
- {
- IR::PropertySymOpnd * propertySymOpnd;
- StackSym * stackSym = ObjectTemp::GetStackSym(src1, &propertySymOpnd);
- if (stackSym)
- {
- ((TempTracker<ObjectTempVerify> *)this)->ProcessUse(stackSym, backwardPass);
- }
- IR::Opnd * src2 = instr->GetSrc2();
- if (src2)
- {
- stackSym = ObjectTemp::GetStackSym(src2, &propertySymOpnd);
- if (stackSym)
- {
- ((TempTracker<ObjectTempVerify> *)this)->ProcessUse(stackSym, backwardPass);
- }
- }
- }
- }
- void
- ObjectTempVerify::NotifyDeadByteCodeUses(IR::Instr * instr)
- {
- if (instr->GetDst())
- {
- SymID symId = instr->GetDst()->AsRegOpnd()->m_sym->m_id;
- this->removedUpwardExposedUse.Clear(symId);
- this->nonTempSyms.Clear(symId);
- }
- IR::ByteCodeUsesInstr *byteCodeUsesInstr = instr->AsByteCodeUsesInstr();
- BVSparse<JitArenaAllocator> * byteCodeUpwardExposedUsed = byteCodeUsesInstr->byteCodeUpwardExposedUsed;
- if (byteCodeUpwardExposedUsed != nullptr)
- {
- this->removedUpwardExposedUse.Or(byteCodeUpwardExposedUsed);
- }
- }
- bool
- ObjectTempVerify::DependencyCheck(IR::Instr * instr, BVSparse<JitArenaAllocator> * bvTempTransferDependencies, BackwardPass * backwardPass)
- {
- if (!instr->dstIsTempObject)
- {
- // The instruction is not marked as temp object anyway, no need to do extra check
- return false;
- }
- // Since our algorithm is conservative, there are cases where even though two defs are unrelated, the use will still
- // seem like overlapping and not mark-temp-able
- // For example:
- // = s6.blah
- // s1 = LdRootFld
- // s6 = s1
- // s1 = NewScObject // s1 is dependent of s6, and s6 is upward exposed.
- // = s6.blah
- // s6 = s1
- // Here, although s1 is mark temp able because the s6.blah use is not related, we only know that s1 is dependent of s6
- // so it looks like s1 may overlap through the iterations. The backward pass will be able to catch that and not mark temp them
- // However, the globopt may create situation like the above while it wasn't there in the backward phase
- // For example:
- // = s6.blah
- // s1 = LdRootFld g
- // s6 = s1
- // s1 = NewScObject
- // s7 = LdRootFld g
- // = s7.blah // Globopt copy prop s7 -> s6, creating the example above.
- // s6 = s1
- // This make it impossible to verify whether we did the right thing using the conservative algorithm.
- // Luckily, this case is very rare (ExprGen didn't hit it with > 100K test cases)
- // So we can use this rather expensive algorithm to find out if any of upward exposed used that we think overlaps
- // really get their value from the marked temp sym or not.
- // See unittest\Object\stackobject_dependency.js (with -maxinterpretcount:1 -off:inline)
- BasicBlock * currentBlock = backwardPass->currentBlock;
- BVSparse<JitArenaAllocator> * upwardExposedUses = currentBlock->upwardExposedUses;
- JitArenaAllocator tempAllocator(L"temp", instr->m_func->m_alloc->GetPageAllocator(), Js::Throw::OutOfMemory);
- BVSparse<JitArenaAllocator> * dependentSyms = bvTempTransferDependencies->AndNew(upwardExposedUses, &tempAllocator);
- BVSparse<JitArenaAllocator> * initialDependentSyms = dependentSyms->CopyNew();
- Assert(!dependentSyms->IsEmpty());
- struct BlockRecord
- {
- BasicBlock * block;
- BVSparse<JitArenaAllocator> * dependentSyms;
- };
- SList<BlockRecord> blockStack(&tempAllocator);
- JsUtil::BaseDictionary<BasicBlock *, BVSparse<JitArenaAllocator> *, JitArenaAllocator> processedSyms(&tempAllocator);
- IR::Instr * currentInstr = instr;
- Assert(instr->GetDst()->AsRegOpnd()->m_sym->IsVar());
- SymID markTempSymId = instr->GetDst()->AsRegOpnd()->m_sym->m_id;
- bool initial = true;
- while (true)
- {
- while (currentInstr != currentBlock->GetFirstInstr())
- {
- if (initial)
- {
- initial = false;
- }
- else if (currentInstr == instr)
- {
- if (dependentSyms->Test(markTempSymId))
- {
- // One of the dependent sym from the original set get it's value from the current marked temp dst.
- // The dst definitely cannot be temp because it's lifetime overlaps across iterations.
- return false;
- }
- // If we have already check the same dependent sym, no need to do it again.
- // It will produce the same result anyway.
- dependentSyms->Minus(initialDependentSyms);
- if (dependentSyms->IsEmpty())
- {
- break;
- }
- // Add in newly discovered dependentSym so we won't do it again when it come back here.
- initialDependentSyms->Or(dependentSyms);
- }
- if (currentInstr->GetDst() && currentInstr->GetDst()->IsRegOpnd())
- {
- // Clear the def and mark the src if it is transferred.
- // If the dst sym is a type specialized sym, clear the var sym instead.
- StackSym * dstSym = currentInstr->GetDst()->AsRegOpnd()->m_sym;
- if (!dstSym->IsVar())
- {
- dstSym = dstSym->GetVarEquivSym(nullptr);
- }
- if (dstSym && dependentSyms->TestAndClear(dstSym->m_id) &&
- IsTempTransfer(currentInstr) && currentInstr->GetSrc1()->IsRegOpnd())
- {
- // We only really care about var syms uses for object temp.
- StackSym * srcSym = currentInstr->GetSrc1()->AsRegOpnd()->m_sym;
- if (srcSym->IsVar())
- {
- dependentSyms->Set(srcSym->m_id);
- }
- }
- if (dependentSyms->IsEmpty())
- {
- // No more dependent sym, we found the def of all of them we can move on to the next block.
- break;
- }
- }
- currentInstr = currentInstr->m_prev;
- }
- if (currentBlock->isLoopHeader && !dependentSyms->IsEmpty())
- {
- Assert(currentInstr == currentBlock->GetFirstInstr());
- // If we have try to propagate the symbol through the loop before, we don't need to propagate it again.
- BVSparse<JitArenaAllocator> * currentLoopProcessedSyms = processedSyms.Lookup(currentBlock, nullptr);
- if (currentLoopProcessedSyms == nullptr)
- {
- processedSyms.Add(currentBlock, dependentSyms->CopyNew());
- }
- else
- {
- dependentSyms->Minus(currentLoopProcessedSyms);
- currentLoopProcessedSyms->Or(dependentSyms);
- }
- }
- if (!dependentSyms->IsEmpty())
- {
- Assert(currentInstr == currentBlock->GetFirstInstr());
- FOREACH_PREDECESSOR_BLOCK(predBlock, currentBlock)
- {
- if (predBlock->loop == nullptr)
- {
- // No need to track outside of loops.
- continue;
- }
- BlockRecord record;
- record.block = predBlock;
- record.dependentSyms = dependentSyms->CopyNew();
- blockStack.Prepend(record);
- }
- NEXT_PREDECESSOR_BLOCK;
- }
- JitAdelete(&tempAllocator, dependentSyms);
- if (blockStack.Empty())
- {
- // No more blocks. We are done.
- break;
- }
- currentBlock = blockStack.Head().block;
- dependentSyms = blockStack.Head().dependentSyms;
- blockStack.RemoveHead();
- currentInstr = currentBlock->GetLastInstr();
- }
- // All the dependent sym doesn't get their value from the marked temp def, so it can really be marked temp.
- #if DBG
- if (DoTrace(backwardPass))
- {
- Output::Print(L"%s: Unrelated overlap mark temp (s%-3d): ", GetTraceName(), markTempSymId);
- instr->DumpSimple();
- Output::Flush();
- }
- #endif
- return true;
- }
- #endif
- #if DBG
- bool
- NumberTemp::DoTrace(BackwardPass * backwardPass)
- {
- return PHASE_TRACE(Js::MarkTempNumberPhase, backwardPass->func);
- }
- bool
- ObjectTemp::DoTrace(BackwardPass * backwardPass)
- {
- return PHASE_TRACE(Js::MarkTempObjectPhase, backwardPass->func);
- }
- bool
- ObjectTempVerify::DoTrace(BackwardPass * backwardPass)
- {
- return PHASE_TRACE(Js::MarkTempObjectPhase, backwardPass->func);
- }
- #endif
- // explicit instantiation
- template class TempTracker<NumberTemp>;
- template class TempTracker<ObjectTemp>;
- #if DBG
- template class TempTracker<ObjectTempVerify>;
- #endif
|