| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011 |
- //-------------------------------------------------------------------------------------------------------
- // 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"
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- #define TESTTRACE_PHASE_INSTR(phase, instr, ...) \
- if(PHASE_TESTTRACE(phase, this->func)) \
- { \
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]; \
- Output::Print( \
- _u("Testtrace: %s function %s (%s): "), \
- Js::PhaseNames[phase], \
- instr->m_func->GetJITFunctionBody()->GetDisplayName(), \
- instr->m_func->GetDebugNumberSet(debugStringBuffer)); \
- Output::Print(__VA_ARGS__); \
- Output::Flush(); \
- }
- #else
- #define TESTTRACE_PHASE_INSTR(phase, instr, ...)
- #endif
- #if ENABLE_DEBUG_CONFIG_OPTIONS && DBG_DUMP
- #define TRACE_TESTTRACE_PHASE_INSTR(phase, instr, ...) \
- TRACE_PHASE_INSTR(phase, instr, __VA_ARGS__); \
- TESTTRACE_PHASE_INSTR(phase, instr, __VA_ARGS__);
- #else
- #define TRACE_TESTTRACE_PHASE_INSTR(phase, instr, ...) TESTTRACE_PHASE_INSTR(phase, instr, __VA_ARGS__);
- #endif
- GlobOpt::ArraySrcOpt::~ArraySrcOpt()
- {
- if (originalIndexOpnd != nullptr)
- {
- Assert(instr->m_opcode == Js::OpCode::IsIn);
- instr->ReplaceSrc1(originalIndexOpnd);
- }
- }
- bool GlobOpt::ArraySrcOpt::CheckOpCode()
- {
- switch (instr->m_opcode)
- {
- // SIMD_JS
- case Js::OpCode::Simd128_LdArr_F4:
- case Js::OpCode::Simd128_LdArr_I4:
- // no type-spec for Asm.js
- if (globOpt->GetIsAsmJSFunc())
- {
- return false;
- }
- // fall through
- case Js::OpCode::LdElemI_A:
- case Js::OpCode::LdMethodElem:
- if (!instr->GetSrc1()->IsIndirOpnd())
- {
- return false;
- }
- baseOwnerIndir = instr->GetSrc1()->AsIndirOpnd();
- baseOpnd = baseOwnerIndir->GetBaseOpnd();
- // LdMethodElem is currently not profiled
- isProfilableLdElem = instr->m_opcode != Js::OpCode::LdMethodElem;
- needsBoundChecks = true;
- needsHeadSegmentLength = true;
- needsHeadSegment = true;
- isLoad = true;
- break;
- // SIMD_JS
- case Js::OpCode::Simd128_StArr_F4:
- case Js::OpCode::Simd128_StArr_I4:
- // no type-spec for Asm.js
- if (globOpt->GetIsAsmJSFunc())
- {
- return false;
- }
- // fall through
- case Js::OpCode::StElemI_A:
- case Js::OpCode::StElemI_A_Strict:
- case Js::OpCode::StElemC:
- if (!instr->GetDst()->IsIndirOpnd())
- {
- return false;
- }
- baseOwnerIndir = instr->GetDst()->AsIndirOpnd();
- baseOpnd = baseOwnerIndir->GetBaseOpnd();
- isProfilableStElem = instr->m_opcode != Js::OpCode::StElemC;
- needsBoundChecks = isProfilableStElem;
- needsHeadSegmentLength = true;
- needsHeadSegment = true;
- isStore = true;
- break;
- case Js::OpCode::InlineArrayPush:
- case Js::OpCode::InlineArrayPop:
- {
- IR::Opnd * thisOpnd = instr->GetSrc1();
- // Abort if it not a LikelyArray or Object with Array - No point in doing array check elimination.
- if (!thisOpnd->IsRegOpnd() || !thisOpnd->GetValueType().IsLikelyArrayOrObjectWithArray())
- {
- return false;
- }
- baseOwnerInstr = instr;
- baseOpnd = thisOpnd->AsRegOpnd();
- isLoad = instr->m_opcode == Js::OpCode::InlineArrayPop;
- isStore = instr->m_opcode == Js::OpCode::InlineArrayPush;
- needsLength = true;
- needsHeadSegmentLength = true;
- needsHeadSegment = true;
- break;
- }
- case Js::OpCode::LdLen_A:
- if (!instr->GetSrc1()->IsRegOpnd())
- {
- return false;
- }
- baseOpnd = instr->GetSrc1()->AsRegOpnd();
- if (baseOpnd->GetValueType().IsLikelyObject() && baseOpnd->GetValueType().GetObjectType() == ObjectType::ObjectWithArray)
- {
- return false;
- }
- baseOwnerInstr = instr;
- needsLength = true;
- break;
- case Js::OpCode::IsIn:
- if (!globOpt->DoArrayMissingValueCheckHoist())
- {
- return false;
- }
- if (!instr->GetSrc1()->IsRegOpnd() && !instr->GetSrc1()->IsIntConstOpnd())
- {
- return false;
- }
- if (!instr->GetSrc2()->IsRegOpnd())
- {
- return false;
- }
- baseOpnd = instr->GetSrc2()->AsRegOpnd();
- if (baseOpnd->GetValueType().IsLikelyObject() && baseOpnd->GetValueType().GetObjectType() == ObjectType::ObjectWithArray)
- {
- return false;
- }
- if (!baseOpnd->GetValueType().IsLikelyAnyArray() || (baseOpnd->GetValueType().IsLikelyArrayOrObjectWithArray() && !baseOpnd->GetValueType().HasNoMissingValues()))
- {
- return false;
- }
- baseOwnerInstr = instr;
- needsBoundChecks = true;
- needsHeadSegmentLength = true;
- needsHeadSegment = true;
- break;
- default:
- return false;
- }
- return true;
- }
- void GlobOpt::ArraySrcOpt::TypeSpecIndex()
- {
- // Since this happens before type specialization, make sure that any necessary conversions are done, and that the index is int-specialized if possible such that the const flags are correct.
- if (!globOpt->IsLoopPrePass())
- {
- if (baseOwnerIndir)
- {
- globOpt->ToVarUses(instr, baseOwnerIndir, baseOwnerIndir == instr->GetDst(), nullptr);
- }
- else if (instr->m_opcode == Js::OpCode::IsIn && instr->GetSrc1()->IsRegOpnd())
- {
- // If the optimization is unable to eliminate the bounds checks, we need to restore the original var sym.
- Assert(originalIndexOpnd == nullptr);
- originalIndexOpnd = instr->GetSrc1()->Copy(func)->AsRegOpnd();
- globOpt->ToTypeSpecIndex(instr, instr->GetSrc1()->AsRegOpnd(), nullptr);
- }
- }
- if (baseOwnerIndir != nullptr)
- {
- indexOpnd = baseOwnerIndir->GetIndexOpnd();
- }
- else if (instr->m_opcode == Js::OpCode::IsIn)
- {
- indexOpnd = instr->GetSrc1();
- }
- if (indexOpnd != nullptr && indexOpnd->IsRegOpnd())
- {
- IR::RegOpnd * regOpnd = indexOpnd->AsRegOpnd();
- if (regOpnd->m_sym->IsTypeSpec())
- {
- Assert(regOpnd->m_sym->IsInt32());
- indexVarSym = regOpnd->m_sym->GetVarEquivSym(nullptr);
- }
- else
- {
- indexVarSym = regOpnd->m_sym;
- }
- indexValue = globOpt->CurrentBlockData()->FindValue(indexVarSym);
- }
- }
- void GlobOpt::ArraySrcOpt::UpdateValue(StackSym * newHeadSegmentSym, StackSym * newHeadSegmentLengthSym, StackSym * newLengthSym)
- {
- Assert(baseValueType.GetObjectType() == newBaseValueType.GetObjectType());
- Assert(newBaseValueType.IsObject());
- Assert(baseValueType.IsLikelyArray() || !newLengthSym);
- if (!(newHeadSegmentSym || newHeadSegmentLengthSym || newLengthSym))
- {
- // We're not adding new information to the value other than changing the value type. Preserve any existing
- // information and just change the value type.
- globOpt->ChangeValueType(globOpt->currentBlock, baseValue, newBaseValueType, true);
- return;
- }
- // Merge the new syms into the value while preserving any existing information, and change the value type
- if (baseArrayValueInfo)
- {
- if (!newHeadSegmentSym)
- {
- newHeadSegmentSym = baseArrayValueInfo->HeadSegmentSym();
- }
- if (!newHeadSegmentLengthSym)
- {
- newHeadSegmentLengthSym = baseArrayValueInfo->HeadSegmentLengthSym();
- }
- if (!newLengthSym)
- {
- newLengthSym = baseArrayValueInfo->LengthSym();
- }
- Assert(!baseArrayValueInfo->HeadSegmentSym() || newHeadSegmentSym == baseArrayValueInfo->HeadSegmentSym());
- Assert(!baseArrayValueInfo->HeadSegmentLengthSym() || newHeadSegmentLengthSym == baseArrayValueInfo->HeadSegmentLengthSym());
- Assert(!baseArrayValueInfo->LengthSym() || newLengthSym == baseArrayValueInfo->LengthSym());
- }
- ArrayValueInfo *const newBaseArrayValueInfo =
- ArrayValueInfo::New(
- globOpt->alloc,
- newBaseValueType,
- newHeadSegmentSym,
- newHeadSegmentLengthSym,
- newLengthSym,
- baseValueInfo->GetSymStore());
- globOpt->ChangeValueInfo(globOpt->currentBlock, baseValue, newBaseArrayValueInfo);
- };
- void GlobOpt::ArraySrcOpt::CheckVirtualArrayBounds()
- {
- #if ENABLE_FAST_ARRAYBUFFER
- if (baseValueType.IsLikelyOptimizedVirtualTypedArray() && !Js::IsSimd128LoadStore(instr->m_opcode) /*Always extract bounds for SIMD */)
- {
- if (isProfilableStElem ||
- !instr->IsDstNotAlwaysConvertedToInt32() ||
- ((baseValueType.GetObjectType() == ObjectType::Float32VirtualArray ||
- baseValueType.GetObjectType() == ObjectType::Float64VirtualArray) &&
- !instr->IsDstNotAlwaysConvertedToNumber()
- )
- )
- {
- // Unless we're in asm.js (where it is guaranteed that virtual typed array accesses cannot read/write beyond 4GB),
- // check the range of the index to make sure we won't access beyond the reserved memory beforing eliminating bounds
- // checks in jitted code.
- if (!globOpt->GetIsAsmJSFunc() && baseOwnerIndir)
- {
- if (indexOpnd)
- {
- IntConstantBounds idxConstantBounds;
- if (indexValue && indexValue->GetValueInfo()->TryGetIntConstantBounds(&idxConstantBounds))
- {
- BYTE indirScale = Lowerer::GetArrayIndirScale(baseValueType);
- int32 upperBound = idxConstantBounds.UpperBound();
- int32 lowerBound = idxConstantBounds.LowerBound();
- if (lowerBound >= 0 && ((static_cast<uint64>(upperBound) << indirScale) < MAX_ASMJS_ARRAYBUFFER_LENGTH))
- {
- eliminatedLowerBoundCheck = true;
- eliminatedUpperBoundCheck = true;
- canBailOutOnArrayAccessHelperCall = false;
- }
- }
- }
- }
- else
- {
- if (baseOwnerIndir == nullptr)
- {
- Assert(instr->m_opcode == Js::OpCode::InlineArrayPush ||
- instr->m_opcode == Js::OpCode::InlineArrayPop ||
- instr->m_opcode == Js::OpCode::LdLen_A ||
- instr->m_opcode == Js::OpCode::IsIn);
- }
- eliminatedLowerBoundCheck = true;
- eliminatedUpperBoundCheck = true;
- canBailOutOnArrayAccessHelperCall = false;
- }
- }
- }
- #endif
- }
- void GlobOpt::ArraySrcOpt::TryEliminiteBoundsCheck()
- {
- AnalysisAssert(indexOpnd != nullptr || baseOwnerIndir != nullptr);
- Assert(needsHeadSegmentLength);
- // Bound checks can be separated from the instruction only if it can bail out instead of making a helper call when a
- // bound check fails. And only if it would bail out, can we use a bound check to eliminate redundant bound checks later
- // on that path.
- doExtractBoundChecks = (headSegmentLengthIsAvailable || doHeadSegmentLengthLoad) && canBailOutOnArrayAccessHelperCall;
- // Get the index value
- if (indexOpnd != nullptr && indexOpnd->IsRegOpnd())
- {
- if (indexOpnd->AsRegOpnd()->m_sym->IsTypeSpec())
- {
- Assert(indexVarSym);
- Assert(indexValue);
- AssertVerify(indexValue->GetValueInfo()->TryGetIntConstantBounds(&indexConstantBounds));
- Assert(indexOpnd->GetType() == TyInt32 || indexOpnd->GetType() == TyUint32);
- Assert(
- (indexOpnd->GetType() == TyUint32) ==
- ValueInfo::IsGreaterThanOrEqualTo(
- indexValue,
- indexConstantBounds.LowerBound(),
- indexConstantBounds.UpperBound(),
- nullptr,
- 0,
- 0));
- if (indexOpnd->GetType() == TyUint32)
- {
- eliminatedLowerBoundCheck = true;
- }
- }
- else
- {
- doExtractBoundChecks = false; // Bound check instruction operates only on int-specialized operands
- if (!indexValue || !indexValue->GetValueInfo()->TryGetIntConstantBounds(&indexConstantBounds))
- {
- return;
- }
- if (ValueInfo::IsGreaterThanOrEqualTo(
- indexValue,
- indexConstantBounds.LowerBound(),
- indexConstantBounds.UpperBound(),
- nullptr,
- 0,
- 0))
- {
- eliminatedLowerBoundCheck = true;
- }
- }
- if (!eliminatedLowerBoundCheck &&
- ValueInfo::IsLessThan(
- indexValue,
- indexConstantBounds.LowerBound(),
- indexConstantBounds.UpperBound(),
- nullptr,
- 0,
- 0))
- {
- eliminatedUpperBoundCheck = true;
- doExtractBoundChecks = false;
- return;
- }
- }
- else
- {
- const int32 indexConstantValue = indexOpnd ? indexOpnd->AsIntConstOpnd()->AsInt32() : baseOwnerIndir->GetOffset();
- if (indexConstantValue < 0)
- {
- eliminatedUpperBoundCheck = true;
- doExtractBoundChecks = false;
- return;
- }
- if (indexConstantValue == INT32_MAX)
- {
- eliminatedLowerBoundCheck = true;
- doExtractBoundChecks = false;
- return;
- }
- indexConstantBounds = IntConstantBounds(indexConstantValue, indexConstantValue);
- eliminatedLowerBoundCheck = true;
- }
- if (!headSegmentLengthIsAvailable)
- {
- return;
- }
- headSegmentLengthValue = globOpt->CurrentBlockData()->FindValue(baseArrayValueInfo->HeadSegmentLengthSym());
- if (!headSegmentLengthValue)
- {
- if (doExtractBoundChecks)
- {
- headSegmentLengthConstantBounds = IntConstantBounds(0, Js::SparseArraySegmentBase::MaxLength);
- }
- return;
- }
- AssertVerify(headSegmentLengthValue->GetValueInfo()->TryGetIntConstantBounds(&headSegmentLengthConstantBounds));
- if (ValueInfo::IsLessThanOrEqualTo(
- indexValue,
- indexConstantBounds.LowerBound(),
- indexConstantBounds.UpperBound(),
- headSegmentLengthValue,
- headSegmentLengthConstantBounds.LowerBound(),
- headSegmentLengthConstantBounds.UpperBound(),
- -1
- ))
- {
- eliminatedUpperBoundCheck = true;
- if (eliminatedLowerBoundCheck)
- {
- doExtractBoundChecks = false;
- }
- }
- }
- void GlobOpt::ArraySrcOpt::CheckLoops()
- {
- if (!doArrayChecks && !doHeadSegmentLoad && !doHeadSegmentLengthLoad && !doLengthLoad)
- {
- return;
- }
- // Find the loops out of which array checks and head segment loads need to be hoisted
- for (Loop *loop = globOpt->currentBlock->loop; loop; loop = loop->parent)
- {
- const JsArrayKills loopKills(loop->jsArrayKills);
- Value *baseValueInLoopLandingPad = nullptr;
- if (((isLikelyJsArray || isLikelyVirtualTypedArray) && loopKills.KillsValueType(newBaseValueType)) ||
- !globOpt->OptIsInvariant(baseOpnd->m_sym, globOpt->currentBlock, loop, baseValue, true, true, &baseValueInLoopLandingPad) ||
- !(doArrayChecks || baseValueInLoopLandingPad->GetValueInfo()->IsObject()))
- {
- break;
- }
- // The value types should be the same, except:
- // - The value type in the landing pad is a type that can merge to a specific object type. Typically, these
- // cases will use BailOnNoProfile, but that can be disabled due to excessive bailouts. Those value types
- // merge aggressively to the other side's object type, so the value type may have started off as
- // Uninitialized, [Likely]Undefined|Null, [Likely]UninitializedObject, etc., and changed in the loop to an
- // array type during a prepass.
- // - StElems in the loop can kill the no-missing-values info.
- // - The native array type may be made more conservative based on profile data by an instruction in the loop.
- #if DBG
- if (!baseValueInLoopLandingPad->GetValueInfo()->CanMergeToSpecificObjectType())
- {
- ValueType landingPadValueType = baseValueInLoopLandingPad->GetValueInfo()->Type();
- Assert(landingPadValueType.IsSimilar(baseValueType)
- || (landingPadValueType.IsLikelyNativeArray() && landingPadValueType.Merge(baseValueType).IsSimilar(baseValueType))
- || (baseValueType.IsLikelyNativeArray() && baseValueType.Merge(landingPadValueType).IsSimilar(landingPadValueType))
- );
- }
- #endif
- if (doArrayChecks)
- {
- hoistChecksOutOfLoop = loop;
- }
- if (isLikelyJsArray && loopKills.KillsArrayHeadSegments())
- {
- Assert(loopKills.KillsArrayHeadSegmentLengths());
- if (!(doArrayChecks || doLengthLoad))
- {
- break;
- }
- }
- else
- {
- if (doHeadSegmentLoad || headSegmentIsAvailable)
- {
- // If the head segment is already available, we may need to rehoist the value including other
- // information. So, need to track the loop out of which the head segment length can be hoisted even if
- // the head segment length is not being loaded here.
- hoistHeadSegmentLoadOutOfLoop = loop;
- }
- if (isLikelyJsArray
- ? loopKills.KillsArrayHeadSegmentLengths()
- : loopKills.KillsTypedArrayHeadSegmentLengths())
- {
- if (!(doArrayChecks || doHeadSegmentLoad || doLengthLoad))
- {
- break;
- }
- }
- else if (doHeadSegmentLengthLoad || headSegmentLengthIsAvailable)
- {
- // If the head segment length is already available, we may need to rehoist the value including other
- // information. So, need to track the loop out of which the head segment length can be hoisted even if
- // the head segment length is not being loaded here.
- hoistHeadSegmentLengthLoadOutOfLoop = loop;
- }
- }
- if (isLikelyJsArray && loopKills.KillsArrayLengths())
- {
- if (!(doArrayChecks || doHeadSegmentLoad || doHeadSegmentLengthLoad))
- {
- break;
- }
- }
- else if (doLengthLoad || lengthIsAvailable)
- {
- // If the length is already available, we may need to rehoist the value including other information. So,
- // need to track the loop out of which the head segment length can be hoisted even if the length is not
- // being loaded here.
- hoistLengthLoadOutOfLoop = loop;
- }
- }
- }
- void GlobOpt::ArraySrcOpt::DoArrayChecks()
- {
- TRACE_TESTTRACE_PHASE_INSTR(Js::ArrayCheckHoistPhase, instr, _u("Separating array checks with bailout\n"));
- IR::Instr *bailOnNotArray = IR::Instr::New(Js::OpCode::BailOnNotArray, instr->m_func);
- bailOnNotArray->SetSrc1(baseOpnd);
- bailOnNotArray->GetSrc1()->SetIsJITOptimizedReg(true);
- const IR::BailOutKind bailOutKind = newBaseValueType.IsLikelyNativeArray() ? IR::BailOutOnNotNativeArray : IR::BailOutOnNotArray;
- if (hoistChecksOutOfLoop)
- {
- Assert(!(isLikelyJsArray && hoistChecksOutOfLoop->jsArrayKills.KillsValueType(newBaseValueType)));
- TRACE_PHASE_INSTR(
- Js::ArrayCheckHoistPhase,
- instr,
- _u("Hoisting array checks with bailout out of loop %u to landing pad block %u\n"),
- hoistChecksOutOfLoop->GetLoopNumber(),
- hoistChecksOutOfLoop->landingPad->GetBlockNum());
- TESTTRACE_PHASE_INSTR(Js::ArrayCheckHoistPhase, instr, _u("Hoisting array checks with bailout out of loop\n"));
- Assert(hoistChecksOutOfLoop->bailOutInfo);
- globOpt->EnsureBailTarget(hoistChecksOutOfLoop);
- InsertInstrInLandingPad(bailOnNotArray, hoistChecksOutOfLoop);
- bailOnNotArray = bailOnNotArray->ConvertToBailOutInstr(hoistChecksOutOfLoop->bailOutInfo, bailOutKind);
- }
- else
- {
- bailOnNotArray->SetByteCodeOffset(instr);
- insertBeforeInstr->InsertBefore(bailOnNotArray);
- globOpt->GenerateBailAtOperation(&bailOnNotArray, bailOutKind);
- shareableBailOutInfo = bailOnNotArray->GetBailOutInfo();
- shareableBailOutInfoOriginalOwner = bailOnNotArray;
- }
- baseValueType = newBaseValueType;
- baseOpnd->SetValueType(newBaseValueType);
- }
- void GlobOpt::ArraySrcOpt::DoLengthLoad()
- {
- Assert(baseValueType.IsArray());
- Assert(newLengthSym);
- TRACE_TESTTRACE_PHASE_INSTR(Js::Phase::ArrayLengthHoistPhase, instr, _u("Separating array length load\n"));
- // Create an initial value for the length
- globOpt->CurrentBlockData()->liveVarSyms->Set(newLengthSym->m_id);
- Value *const lengthValue = globOpt->NewIntRangeValue(0, INT32_MAX, false);
- globOpt->CurrentBlockData()->SetValue(lengthValue, newLengthSym);
- // SetValue above would have set the sym store to newLengthSym. This sym won't be used for copy-prop though, so
- // remove it as the sym store.
- globOpt->SetSymStoreDirect(lengthValue->GetValueInfo(), nullptr);
- // length = [array + offsetOf(length)]
- IR::Instr *const loadLength =
- IR::Instr::New(
- Js::OpCode::LdIndir,
- IR::RegOpnd::New(newLengthSym, newLengthSym->GetType(), instr->m_func),
- IR::IndirOpnd::New(
- baseOpnd,
- Js::JavascriptArray::GetOffsetOfLength(),
- newLengthSym->GetType(),
- instr->m_func),
- instr->m_func);
- loadLength->GetDst()->SetIsJITOptimizedReg(true);
- loadLength->GetSrc1()->AsIndirOpnd()->GetBaseOpnd()->SetIsJITOptimizedReg(true);
- // BailOnNegative length (BailOutOnIrregularLength)
- IR::Instr *bailOnIrregularLength = IR::Instr::New(Js::OpCode::BailOnNegative, instr->m_func);
- bailOnIrregularLength->SetSrc1(loadLength->GetDst());
- const IR::BailOutKind bailOutKind = IR::BailOutOnIrregularLength;
- if (hoistLengthLoadOutOfLoop)
- {
- Assert(!hoistLengthLoadOutOfLoop->jsArrayKills.KillsArrayLengths());
- TRACE_PHASE_INSTR(
- Js::Phase::ArrayLengthHoistPhase,
- instr,
- _u("Hoisting array length load out of loop %u to landing pad block %u\n"),
- hoistLengthLoadOutOfLoop->GetLoopNumber(),
- hoistLengthLoadOutOfLoop->landingPad->GetBlockNum());
- TESTTRACE_PHASE_INSTR(Js::Phase::ArrayLengthHoistPhase, instr, _u("Hoisting array length load out of loop\n"));
- Assert(hoistLengthLoadOutOfLoop->bailOutInfo);
- globOpt->EnsureBailTarget(hoistLengthLoadOutOfLoop);
- InsertInstrInLandingPad(loadLength, hoistLengthLoadOutOfLoop);
- InsertInstrInLandingPad(bailOnIrregularLength, hoistLengthLoadOutOfLoop);
- bailOnIrregularLength = bailOnIrregularLength->ConvertToBailOutInstr(hoistLengthLoadOutOfLoop->bailOutInfo, bailOutKind);
- // Hoist the length value
- for (InvariantBlockBackwardIterator it(
- globOpt,
- globOpt->currentBlock,
- hoistLengthLoadOutOfLoop->landingPad,
- baseOpnd->m_sym,
- baseValue->GetValueNumber());
- it.IsValid();
- it.MoveNext())
- {
- BasicBlock *const block = it.Block();
- block->globOptData.liveVarSyms->Set(newLengthSym->m_id);
- Assert(!block->globOptData.FindValue(newLengthSym));
- Value *const lengthValueCopy = globOpt->CopyValue(lengthValue, lengthValue->GetValueNumber());
- block->globOptData.SetValue(lengthValueCopy, newLengthSym);
- globOpt->SetSymStoreDirect(lengthValueCopy->GetValueInfo(), nullptr);
- }
- }
- else
- {
- loadLength->SetByteCodeOffset(instr);
- insertBeforeInstr->InsertBefore(loadLength);
- bailOnIrregularLength->SetByteCodeOffset(instr);
- insertBeforeInstr->InsertBefore(bailOnIrregularLength);
- if (shareableBailOutInfo)
- {
- ShareBailOut();
- bailOnIrregularLength = bailOnIrregularLength->ConvertToBailOutInstr(shareableBailOutInfo, bailOutKind);
- }
- else
- {
- globOpt->GenerateBailAtOperation(&bailOnIrregularLength, bailOutKind);
- shareableBailOutInfo = bailOnIrregularLength->GetBailOutInfo();
- shareableBailOutInfoOriginalOwner = bailOnIrregularLength;
- }
- }
- }
- void GlobOpt::ArraySrcOpt::DoHeadSegmentLengthLoad()
- {
- Assert(!isLikelyJsArray || newHeadSegmentSym || baseArrayValueInfo && baseArrayValueInfo->HeadSegmentSym());
- Assert(newHeadSegmentLengthSym);
- Assert(!headSegmentLengthValue);
- TRACE_TESTTRACE_PHASE_INSTR(Js::ArraySegmentHoistPhase, instr, _u("Separating array segment length load\n"));
- // Create an initial value for the head segment length
- globOpt->CurrentBlockData()->liveVarSyms->Set(newHeadSegmentLengthSym->m_id);
- headSegmentLengthValue = globOpt->NewIntRangeValue(0, Js::SparseArraySegmentBase::MaxLength, false);
- headSegmentLengthConstantBounds = IntConstantBounds(0, Js::SparseArraySegmentBase::MaxLength);
- globOpt->CurrentBlockData()->SetValue(headSegmentLengthValue, newHeadSegmentLengthSym);
- // SetValue above would have set the sym store to newHeadSegmentLengthSym. This sym won't be used for copy-prop
- // though, so remove it as the sym store.
- globOpt->SetSymStoreDirect(headSegmentLengthValue->GetValueInfo(), nullptr);
- StackSym *const headSegmentSym = isLikelyJsArray ? newHeadSegmentSym ? newHeadSegmentSym : baseArrayValueInfo->HeadSegmentSym() : nullptr;
- IR::Instr *const loadHeadSegmentLength =
- IR::Instr::New(
- Js::OpCode::LdIndir,
- IR::RegOpnd::New(newHeadSegmentLengthSym, newHeadSegmentLengthSym->GetType(), instr->m_func),
- IR::IndirOpnd::New(
- isLikelyJsArray ? IR::RegOpnd::New(headSegmentSym, headSegmentSym->GetType(), instr->m_func) : baseOpnd,
- isLikelyJsArray
- ? Js::SparseArraySegmentBase::GetOffsetOfLength()
- : Lowerer::GetArrayOffsetOfLength(baseValueType),
- newHeadSegmentLengthSym->GetType(),
- instr->m_func),
- instr->m_func);
- loadHeadSegmentLength->GetDst()->SetIsJITOptimizedReg(true);
- loadHeadSegmentLength->GetSrc1()->AsIndirOpnd()->GetBaseOpnd()->SetIsJITOptimizedReg(true);
- // We don't check the head segment length for negative (very large uint32) values. For JS arrays, the bound checks
- // cover that. For typed arrays, we currently don't allocate array buffers with more than 1 GB elements.
- if (hoistHeadSegmentLengthLoadOutOfLoop)
- {
- Assert(
- !(
- isLikelyJsArray
- ? hoistHeadSegmentLengthLoadOutOfLoop->jsArrayKills.KillsArrayHeadSegmentLengths()
- : hoistHeadSegmentLengthLoadOutOfLoop->jsArrayKills.KillsTypedArrayHeadSegmentLengths()
- ));
- TRACE_PHASE_INSTR(
- Js::ArraySegmentHoistPhase,
- instr,
- _u("Hoisting array segment length load out of loop %u to landing pad block %u\n"),
- hoistHeadSegmentLengthLoadOutOfLoop->GetLoopNumber(),
- hoistHeadSegmentLengthLoadOutOfLoop->landingPad->GetBlockNum());
- TESTTRACE_PHASE_INSTR(Js::ArraySegmentHoistPhase, instr, _u("Hoisting array segment length load out of loop\n"));
- InsertInstrInLandingPad(loadHeadSegmentLength, hoistHeadSegmentLengthLoadOutOfLoop);
- // Hoist the head segment length value
- for (InvariantBlockBackwardIterator it(
- globOpt,
- globOpt->currentBlock,
- hoistHeadSegmentLengthLoadOutOfLoop->landingPad,
- baseOpnd->m_sym,
- baseValue->GetValueNumber());
- it.IsValid();
- it.MoveNext())
- {
- BasicBlock *const block = it.Block();
- block->globOptData.liveVarSyms->Set(newHeadSegmentLengthSym->m_id);
- Assert(!block->globOptData.FindValue(newHeadSegmentLengthSym));
- Value *const headSegmentLengthValueCopy = globOpt->CopyValue(headSegmentLengthValue, headSegmentLengthValue->GetValueNumber());
- block->globOptData.SetValue(headSegmentLengthValueCopy, newHeadSegmentLengthSym);
- globOpt->SetSymStoreDirect(headSegmentLengthValueCopy->GetValueInfo(), nullptr);
- }
- }
- else
- {
- loadHeadSegmentLength->SetByteCodeOffset(instr);
- insertBeforeInstr->InsertBefore(loadHeadSegmentLength);
- instr->loadedArrayHeadSegmentLength = true;
- }
- }
- void GlobOpt::ArraySrcOpt::DoExtractBoundChecks()
- {
- Assert(!(eliminatedLowerBoundCheck && eliminatedUpperBoundCheck));
- Assert(baseOwnerIndir != nullptr || indexOpnd != nullptr);
- Assert(indexOpnd == nullptr || indexOpnd->IsIntConstOpnd() || indexOpnd->AsRegOpnd()->m_sym->IsTypeSpec());
- Assert(doHeadSegmentLengthLoad || headSegmentLengthIsAvailable);
- Assert(canBailOutOnArrayAccessHelperCall);
- Assert(!isStore || instr->m_opcode == Js::OpCode::StElemI_A || instr->m_opcode == Js::OpCode::StElemI_A_Strict || Js::IsSimd128LoadStore(instr->m_opcode));
- headSegmentLengthSym = headSegmentLengthIsAvailable ? baseArrayValueInfo->HeadSegmentLengthSym() : newHeadSegmentLengthSym;
- Assert(headSegmentLengthSym);
- Assert(headSegmentLengthValue);
- if (globOpt->DoBoundCheckHoist())
- {
- if (indexVarSym)
- {
- TRACE_PHASE_INSTR_VERBOSE(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Determining array bound check hoistability for index s%u\n"),
- indexVarSym->m_id);
- }
- else
- {
- TRACE_PHASE_INSTR_VERBOSE(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Determining array bound check hoistability for index %d\n"),
- indexConstantBounds.LowerBound());
- }
- globOpt->DetermineArrayBoundCheckHoistability(
- !eliminatedLowerBoundCheck,
- !eliminatedUpperBoundCheck,
- lowerBoundCheckHoistInfo,
- upperBoundCheckHoistInfo,
- isLikelyJsArray,
- indexVarSym,
- indexValue,
- indexConstantBounds,
- headSegmentLengthSym,
- headSegmentLengthValue,
- headSegmentLengthConstantBounds,
- hoistHeadSegmentLengthLoadOutOfLoop,
- failedToUpdateCompatibleLowerBoundCheck,
- failedToUpdateCompatibleUpperBoundCheck);
- }
- if (!eliminatedLowerBoundCheck)
- {
- DoLowerBoundCheck();
- }
- if (!eliminatedUpperBoundCheck)
- {
- DoUpperBoundCheck();
- }
- }
- void GlobOpt::ArraySrcOpt::DoLowerBoundCheck()
- {
- eliminatedLowerBoundCheck = true;
- Assert(indexVarSym);
- Assert(indexOpnd);
- Assert(indexValue);
- GlobOpt::ArrayLowerBoundCheckHoistInfo &hoistInfo = lowerBoundCheckHoistInfo;
- if (hoistInfo.HasAnyInfo())
- {
- BasicBlock *hoistBlock;
- if (hoistInfo.CompatibleBoundCheckBlock())
- {
- hoistBlock = hoistInfo.CompatibleBoundCheckBlock();
- TRACE_PHASE_INSTR(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Hoisting array lower bound check into existing bound check instruction in block %u\n"),
- hoistBlock->GetBlockNum());
- TESTTRACE_PHASE_INSTR(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Hoisting array lower bound check into existing bound check instruction\n"));
- }
- else
- {
- Assert(hoistInfo.Loop());
- BasicBlock *const landingPad = hoistInfo.Loop()->landingPad;
- hoistBlock = landingPad;
- StackSym *indexIntSym;
- if (hoistInfo.IndexSym() && hoistInfo.IndexSym()->IsVar())
- {
- if (!landingPad->globOptData.IsInt32TypeSpecialized(hoistInfo.IndexSym()))
- {
- // Int-specialize the index sym, as the BoundCheck instruction requires int operands. Specialize
- // it in this block if it is invariant, as the conversion will be hoisted along with value
- // updates.
- BasicBlock *specializationBlock = hoistInfo.Loop()->landingPad;
- IR::Instr *specializeBeforeInstr = nullptr;
- if (!globOpt->CurrentBlockData()->IsInt32TypeSpecialized(hoistInfo.IndexSym()) &&
- globOpt->OptIsInvariant(
- hoistInfo.IndexSym(),
- globOpt->currentBlock,
- hoistInfo.Loop(),
- globOpt->CurrentBlockData()->FindValue(hoistInfo.IndexSym()),
- false,
- true))
- {
- specializationBlock = globOpt->currentBlock;
- specializeBeforeInstr = insertBeforeInstr;
- }
- Assert(globOpt->tempBv->IsEmpty());
- globOpt->tempBv->Set(hoistInfo.IndexSym()->m_id);
- globOpt->ToInt32(globOpt->tempBv, specializationBlock, false, specializeBeforeInstr);
- globOpt->tempBv->ClearAll();
- Assert(landingPad->globOptData.IsInt32TypeSpecialized(hoistInfo.IndexSym()));
- }
- indexIntSym = hoistInfo.IndexSym()->GetInt32EquivSym(nullptr);
- Assert(indexIntSym);
- }
- else
- {
- indexIntSym = hoistInfo.IndexSym();
- Assert(!indexIntSym || indexIntSym->GetType() == TyInt32 || indexIntSym->GetType() == TyUint32);
- }
- if (hoistInfo.IndexSym())
- {
- Assert(hoistInfo.Loop()->bailOutInfo);
- globOpt->EnsureBailTarget(hoistInfo.Loop());
- bool needsMagnitudeAdjustment = false;
- if (hoistInfo.LoopCount())
- {
- // Generate the loop count and loop count based bound that will be used for the bound check
- if (!hoistInfo.LoopCount()->HasBeenGenerated())
- {
- globOpt->GenerateLoopCount(hoistInfo.Loop(), hoistInfo.LoopCount());
- }
- needsMagnitudeAdjustment = (hoistInfo.MaxMagnitudeChange() > 0)
- ? (hoistInfo.IndexOffset() < hoistInfo.MaxMagnitudeChange())
- : (hoistInfo.IndexOffset() > hoistInfo.MaxMagnitudeChange());
- globOpt->GenerateSecondaryInductionVariableBound(
- hoistInfo.Loop(),
- indexVarSym->GetInt32EquivSym(nullptr),
- hoistInfo.LoopCount(),
- hoistInfo.MaxMagnitudeChange(),
- needsMagnitudeAdjustment,
- hoistInfo.IndexSym());
- }
- IR::Opnd* lowerBound = IR::IntConstOpnd::New(0, TyInt32, instr->m_func, true);
- IR::Opnd* upperBound = IR::RegOpnd::New(indexIntSym, TyInt32, instr->m_func);
- int offset = needsMagnitudeAdjustment ? (hoistInfo.IndexOffset() - hoistInfo.Offset()) : hoistInfo.Offset();
- upperBound->SetIsJITOptimizedReg(true);
- // 0 <= indexSym + offset (src1 <= src2 + dst)
- IR::Instr *const boundCheck = globOpt->CreateBoundsCheckInstr(
- lowerBound,
- upperBound,
- offset,
- hoistInfo.IsLoopCountBasedBound()
- ? IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck
- : IR::BailOutOnFailedHoistedBoundCheck,
- hoistInfo.Loop()->bailOutInfo,
- hoistInfo.Loop()->bailOutInfo->bailOutFunc);
- InsertInstrInLandingPad(boundCheck, hoistInfo.Loop());
- TRACE_PHASE_INSTR(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Hoisting array lower bound check out of loop %u to landing pad block %u, as (0 <= s%u + %d)\n"),
- hoistInfo.Loop()->GetLoopNumber(),
- landingPad->GetBlockNum(),
- hoistInfo.IndexSym()->m_id,
- hoistInfo.Offset());
- TESTTRACE_PHASE_INSTR(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Hoisting array lower bound check out of loop\n"));
- // Record the bound check instruction as available
- const IntBoundCheck boundCheckInfo(
- ZeroValueNumber,
- hoistInfo.IndexValueNumber(),
- boundCheck,
- landingPad);
- {
- const bool added = globOpt->CurrentBlockData()->availableIntBoundChecks->AddNew(boundCheckInfo) >= 0;
- Assert(added || failedToUpdateCompatibleLowerBoundCheck);
- }
- for (InvariantBlockBackwardIterator it(globOpt, globOpt->currentBlock, landingPad, nullptr);
- it.IsValid();
- it.MoveNext())
- {
- const bool added = it.Block()->globOptData.availableIntBoundChecks->AddNew(boundCheckInfo) >= 0;
- Assert(added || failedToUpdateCompatibleLowerBoundCheck);
- }
- }
- }
- // Update values of the syms involved in the bound check to reflect the bound check
- if (hoistBlock != globOpt->currentBlock && hoistInfo.IndexSym() && hoistInfo.Offset() != INT32_MIN)
- {
- for (InvariantBlockBackwardIterator it(
- globOpt,
- globOpt->currentBlock->next,
- hoistBlock,
- hoistInfo.IndexSym(),
- hoistInfo.IndexValueNumber(),
- true);
- it.IsValid();
- it.MoveNext())
- {
- Value *const value = it.InvariantSymValue();
- IntConstantBounds constantBounds;
- AssertVerify(value->GetValueInfo()->TryGetIntConstantBounds(&constantBounds, true));
- ValueInfo *const newValueInfo =
- globOpt->UpdateIntBoundsForGreaterThanOrEqual(
- value,
- constantBounds,
- nullptr,
- IntConstantBounds(-hoistInfo.Offset(), -hoistInfo.Offset()),
- false);
- if (newValueInfo)
- {
- globOpt->ChangeValueInfo(nullptr, value, newValueInfo);
- if (it.Block() == globOpt->currentBlock && value == indexValue)
- {
- AssertVerify(newValueInfo->TryGetIntConstantBounds(&indexConstantBounds));
- }
- }
- }
- }
- }
- else
- {
- IR::Opnd* lowerBound = IR::IntConstOpnd::New(0, TyInt32, instr->m_func, true);
- IR::Opnd* upperBound = indexOpnd;
- upperBound->SetIsJITOptimizedReg(true);
- const int offset = 0;
- IR::Instr *boundCheck;
- if (shareableBailOutInfo)
- {
- ShareBailOut();
- boundCheck = globOpt->CreateBoundsCheckInstr(
- lowerBound,
- upperBound,
- offset,
- IR::BailOutOnArrayAccessHelperCall,
- shareableBailOutInfo,
- shareableBailOutInfo->bailOutFunc);
- }
- else
- {
- boundCheck = globOpt->CreateBoundsCheckInstr(
- lowerBound,
- upperBound,
- offset,
- instr->m_func);
- }
- boundCheck->SetByteCodeOffset(instr);
- insertBeforeInstr->InsertBefore(boundCheck);
- if (!shareableBailOutInfo)
- {
- globOpt->GenerateBailAtOperation(&boundCheck, IR::BailOutOnArrayAccessHelperCall);
- shareableBailOutInfo = boundCheck->GetBailOutInfo();
- shareableBailOutInfoOriginalOwner = boundCheck;
- }
- TRACE_PHASE_INSTR(
- Js::Phase::BoundCheckEliminationPhase,
- instr,
- _u("Separating array lower bound check, as (0 <= s%u)\n"),
- indexVarSym->m_id);
- TESTTRACE_PHASE_INSTR(
- Js::Phase::BoundCheckEliminationPhase,
- instr,
- _u("Separating array lower bound check\n"));
- if (globOpt->DoBoundCheckHoist())
- {
- // Record the bound check instruction as available
- const bool added =
- globOpt->CurrentBlockData()->availableIntBoundChecks->AddNew(
- IntBoundCheck(ZeroValueNumber, indexValue->GetValueNumber(), boundCheck, globOpt->currentBlock)) >= 0;
- Assert(added || failedToUpdateCompatibleLowerBoundCheck);
- }
- }
- // Update the index value to reflect the bound check
- ValueInfo *const newValueInfo =
- globOpt->UpdateIntBoundsForGreaterThanOrEqual(
- indexValue,
- indexConstantBounds,
- nullptr,
- IntConstantBounds(0, 0),
- false);
- if (newValueInfo)
- {
- globOpt->ChangeValueInfo(nullptr, indexValue, newValueInfo);
- AssertVerify(newValueInfo->TryGetIntConstantBounds(&indexConstantBounds));
- }
- }
- void GlobOpt::ArraySrcOpt::DoUpperBoundCheck()
- {
- eliminatedUpperBoundCheck = true;
- GlobOpt::ArrayUpperBoundCheckHoistInfo &hoistInfo = upperBoundCheckHoistInfo;
- if (hoistInfo.HasAnyInfo())
- {
- BasicBlock *hoistBlock;
- if (hoistInfo.CompatibleBoundCheckBlock())
- {
- hoistBlock = hoistInfo.CompatibleBoundCheckBlock();
- TRACE_PHASE_INSTR(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Hoisting array upper bound check into existing bound check instruction in block %u\n"),
- hoistBlock->GetBlockNum());
- TESTTRACE_PHASE_INSTR(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Hoisting array upper bound check into existing bound check instruction\n"));
- }
- else
- {
- Assert(hoistInfo.Loop());
- BasicBlock *const landingPad = hoistInfo.Loop()->landingPad;
- hoistBlock = landingPad;
- StackSym *indexIntSym;
- if (hoistInfo.IndexSym() && hoistInfo.IndexSym()->IsVar())
- {
- if (!landingPad->globOptData.IsInt32TypeSpecialized(hoistInfo.IndexSym()))
- {
- // Int-specialize the index sym, as the BoundCheck instruction requires int operands. Specialize it
- // in this block if it is invariant, as the conversion will be hoisted along with value updates.
- BasicBlock *specializationBlock = hoistInfo.Loop()->landingPad;
- IR::Instr *specializeBeforeInstr = nullptr;
- if (!globOpt->CurrentBlockData()->IsInt32TypeSpecialized(hoistInfo.IndexSym()) &&
- globOpt->OptIsInvariant(
- hoistInfo.IndexSym(),
- globOpt->currentBlock,
- hoistInfo.Loop(),
- globOpt->CurrentBlockData()->FindValue(hoistInfo.IndexSym()),
- false,
- true))
- {
- specializationBlock = globOpt->currentBlock;
- specializeBeforeInstr = insertBeforeInstr;
- }
- Assert(globOpt->tempBv->IsEmpty());
- globOpt->tempBv->Set(hoistInfo.IndexSym()->m_id);
- globOpt->ToInt32(globOpt->tempBv, specializationBlock, false, specializeBeforeInstr);
- globOpt->tempBv->ClearAll();
- Assert(landingPad->globOptData.IsInt32TypeSpecialized(hoistInfo.IndexSym()));
- }
- indexIntSym = hoistInfo.IndexSym()->GetInt32EquivSym(nullptr);
- Assert(indexIntSym);
- }
- else
- {
- indexIntSym = hoistInfo.IndexSym();
- Assert(!indexIntSym || indexIntSym->GetType() == TyInt32 || indexIntSym->GetType() == TyUint32);
- }
- Assert(hoistInfo.Loop()->bailOutInfo);
- globOpt->EnsureBailTarget(hoistInfo.Loop());
- bool needsMagnitudeAdjustment = false;
- if (hoistInfo.LoopCount())
- {
- // Generate the loop count and loop count based bound that will be used for the bound check
- if (!hoistInfo.LoopCount()->HasBeenGenerated())
- {
- globOpt->GenerateLoopCount(hoistInfo.Loop(), hoistInfo.LoopCount());
- }
- needsMagnitudeAdjustment = (hoistInfo.MaxMagnitudeChange() > 0)
- ? (hoistInfo.IndexOffset() < hoistInfo.MaxMagnitudeChange())
- : (hoistInfo.IndexOffset() > hoistInfo.MaxMagnitudeChange());
- globOpt->GenerateSecondaryInductionVariableBound(
- hoistInfo.Loop(),
- indexVarSym->GetInt32EquivSym(nullptr),
- hoistInfo.LoopCount(),
- hoistInfo.MaxMagnitudeChange(),
- needsMagnitudeAdjustment,
- hoistInfo.IndexSym());
- }
- IR::Opnd* lowerBound = indexIntSym
- ? static_cast<IR::Opnd *>(IR::RegOpnd::New(indexIntSym, TyInt32, instr->m_func))
- : IR::IntConstOpnd::New(
- hoistInfo.IndexConstantBounds().LowerBound(),
- TyInt32,
- instr->m_func);
- lowerBound->SetIsJITOptimizedReg(true);
- IR::Opnd* upperBound = IR::RegOpnd::New(headSegmentLengthSym, headSegmentLengthSym->GetType(), instr->m_func);
- upperBound->SetIsJITOptimizedReg(true);
- int offset = needsMagnitudeAdjustment ? (hoistInfo.IndexOffset() + hoistInfo.Offset()) : hoistInfo.Offset();
- // indexSym <= headSegmentLength + offset (src1 <= src2 + dst)
- IR::Instr *const boundCheck = globOpt->CreateBoundsCheckInstr(
- lowerBound,
- upperBound,
- offset,
- hoistInfo.IsLoopCountBasedBound()
- ? IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck
- : IR::BailOutOnFailedHoistedBoundCheck,
- hoistInfo.Loop()->bailOutInfo,
- hoistInfo.Loop()->bailOutInfo->bailOutFunc);
- InsertInstrInLandingPad(boundCheck, hoistInfo.Loop());
- if (indexIntSym)
- {
- TRACE_PHASE_INSTR(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Hoisting array upper bound check out of loop %u to landing pad block %u, as (s%u <= s%u + %d)\n"),
- hoistInfo.Loop()->GetLoopNumber(),
- landingPad->GetBlockNum(),
- hoistInfo.IndexSym()->m_id,
- headSegmentLengthSym->m_id,
- offset);
- }
- else
- {
- TRACE_PHASE_INSTR(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Hoisting array upper bound check out of loop %u to landing pad block %u, as (%d <= s%u + %d)\n"),
- hoistInfo.Loop()->GetLoopNumber(),
- landingPad->GetBlockNum(),
- hoistInfo.IndexConstantBounds().LowerBound(),
- headSegmentLengthSym->m_id,
- offset);
- }
- TESTTRACE_PHASE_INSTR(
- Js::Phase::BoundCheckHoistPhase,
- instr,
- _u("Hoisting array upper bound check out of loop\n"));
- // Record the bound check instruction as available
- const IntBoundCheck boundCheckInfo(
- hoistInfo.IndexValue() ? hoistInfo.IndexValueNumber() : ZeroValueNumber,
- hoistInfo.HeadSegmentLengthValue()->GetValueNumber(),
- boundCheck,
- landingPad);
- {
- const bool added = globOpt->CurrentBlockData()->availableIntBoundChecks->AddNew(boundCheckInfo) >= 0;
- Assert(added || failedToUpdateCompatibleUpperBoundCheck);
- }
- for (InvariantBlockBackwardIterator it(globOpt, globOpt->currentBlock, landingPad, nullptr);
- it.IsValid();
- it.MoveNext())
- {
- const bool added = it.Block()->globOptData.availableIntBoundChecks->AddNew(boundCheckInfo) >= 0;
- Assert(added || failedToUpdateCompatibleUpperBoundCheck);
- }
- }
- // Update values of the syms involved in the bound check to reflect the bound check
- Assert(!hoistInfo.Loop() || hoistBlock != globOpt->currentBlock);
- if (hoistBlock != globOpt->currentBlock)
- {
- for (InvariantBlockBackwardIterator it(globOpt, globOpt->currentBlock->next, hoistBlock, nullptr, InvalidValueNumber, true);
- it.IsValid();
- it.MoveNext())
- {
- BasicBlock *const block = it.Block();
- Value *leftValue;
- IntConstantBounds leftConstantBounds;
- if (hoistInfo.IndexSym())
- {
- leftValue = block->globOptData.FindValue(hoistInfo.IndexSym());
- if (!leftValue || leftValue->GetValueNumber() != hoistInfo.IndexValueNumber())
- {
- continue;
- }
- AssertVerify(leftValue->GetValueInfo()->TryGetIntConstantBounds(&leftConstantBounds, true));
- }
- else
- {
- leftValue = nullptr;
- leftConstantBounds = hoistInfo.IndexConstantBounds();
- }
- Value *const rightValue = block->globOptData.FindValue(headSegmentLengthSym);
- if (!rightValue)
- {
- continue;
- }
- Assert(rightValue->GetValueNumber() == headSegmentLengthValue->GetValueNumber());
- IntConstantBounds rightConstantBounds;
- AssertVerify(rightValue->GetValueInfo()->TryGetIntConstantBounds(&rightConstantBounds));
- ValueInfo *const newValueInfoForLessThanOrEqual =
- globOpt->UpdateIntBoundsForLessThanOrEqual(
- leftValue,
- leftConstantBounds,
- rightValue,
- rightConstantBounds,
- hoistInfo.Offset(),
- false);
- if (newValueInfoForLessThanOrEqual)
- {
- globOpt->ChangeValueInfo(nullptr, leftValue, newValueInfoForLessThanOrEqual);
- AssertVerify(newValueInfoForLessThanOrEqual->TryGetIntConstantBounds(&leftConstantBounds, true));
- if (block == globOpt->currentBlock && leftValue == indexValue)
- {
- Assert(newValueInfoForLessThanOrEqual->IsInt());
- indexConstantBounds = leftConstantBounds;
- }
- }
- if (hoistInfo.Offset() != INT32_MIN)
- {
- ValueInfo *const newValueInfoForGreaterThanOrEqual =
- globOpt->UpdateIntBoundsForGreaterThanOrEqual(
- rightValue,
- rightConstantBounds,
- leftValue,
- leftConstantBounds,
- -hoistInfo.Offset(),
- false);
- if (newValueInfoForGreaterThanOrEqual)
- {
- globOpt->ChangeValueInfo(nullptr, rightValue, newValueInfoForGreaterThanOrEqual);
- if (block == globOpt->currentBlock)
- {
- Assert(rightValue == headSegmentLengthValue);
- AssertVerify(newValueInfoForGreaterThanOrEqual->TryGetIntConstantBounds(&headSegmentLengthConstantBounds));
- }
- }
- }
- }
- }
- }
- else
- {
- IR::Opnd * lowerBound = indexOpnd ? indexOpnd : IR::IntConstOpnd::New(baseOwnerIndir->GetOffset(), TyInt32, instr->m_func);
- lowerBound->SetIsJITOptimizedReg(true);
- IR::Opnd* upperBound = IR::RegOpnd::New(headSegmentLengthSym, headSegmentLengthSym->GetType(), instr->m_func);
- upperBound->SetIsJITOptimizedReg(true);
- const int offset = -1;
- IR::Instr *boundCheck;
- // index <= headSegmentLength - 1 (src1 <= src2 + dst)
- if (shareableBailOutInfo)
- {
- ShareBailOut();
- boundCheck = globOpt->CreateBoundsCheckInstr(
- lowerBound,
- upperBound,
- offset,
- IR::BailOutOnArrayAccessHelperCall,
- shareableBailOutInfo,
- shareableBailOutInfo->bailOutFunc);
- }
- else
- {
- boundCheck = globOpt->CreateBoundsCheckInstr(
- lowerBound,
- upperBound,
- offset,
- instr->m_func);
- }
- boundCheck->SetByteCodeOffset(instr);
- insertBeforeInstr->InsertBefore(boundCheck);
- if (!shareableBailOutInfo)
- {
- globOpt->GenerateBailAtOperation(&boundCheck, IR::BailOutOnArrayAccessHelperCall);
- shareableBailOutInfo = boundCheck->GetBailOutInfo();
- shareableBailOutInfoOriginalOwner = boundCheck;
- }
- instr->extractedUpperBoundCheckWithoutHoisting = true;
- if (indexOpnd != nullptr && indexOpnd->IsRegOpnd())
- {
- TRACE_PHASE_INSTR(
- Js::Phase::BoundCheckEliminationPhase,
- instr,
- _u("Separating array upper bound check, as (s%u < s%u)\n"),
- indexVarSym->m_id,
- headSegmentLengthSym->m_id);
- }
- else
- {
- TRACE_PHASE_INSTR(
- Js::Phase::BoundCheckEliminationPhase,
- instr,
- _u("Separating array upper bound check, as (%d < s%u)\n"),
- indexOpnd ? indexOpnd->AsIntConstOpnd()->AsInt32() : baseOwnerIndir->GetOffset(),
- headSegmentLengthSym->m_id);
- }
- TESTTRACE_PHASE_INSTR(
- Js::Phase::BoundCheckEliminationPhase,
- instr,
- _u("Separating array upper bound check\n"));
- if (globOpt->DoBoundCheckHoist())
- {
- // Record the bound check instruction as available
- const bool added =
- globOpt->CurrentBlockData()->availableIntBoundChecks->AddNew(
- IntBoundCheck(
- indexValue ? indexValue->GetValueNumber() : ZeroValueNumber,
- headSegmentLengthValue->GetValueNumber(),
- boundCheck,
- globOpt->currentBlock)) >= 0;
- Assert(added || failedToUpdateCompatibleUpperBoundCheck);
- }
- }
- // Update the index and head segment length values to reflect the bound check
- ValueInfo *newValueInfo =
- globOpt->UpdateIntBoundsForLessThan(
- indexValue,
- indexConstantBounds,
- headSegmentLengthValue,
- headSegmentLengthConstantBounds,
- false);
- if (newValueInfo)
- {
- globOpt->ChangeValueInfo(nullptr, indexValue, newValueInfo);
- AssertVerify(newValueInfo->TryGetIntConstantBounds(&indexConstantBounds));
- }
- newValueInfo =
- globOpt->UpdateIntBoundsForGreaterThan(
- headSegmentLengthValue,
- headSegmentLengthConstantBounds,
- indexValue,
- indexConstantBounds,
- false);
- if (newValueInfo)
- {
- globOpt->ChangeValueInfo(nullptr, headSegmentLengthValue, newValueInfo);
- }
- }
- void GlobOpt::ArraySrcOpt::UpdateHoistedValueInfo()
- {
- // Iterate up to the root loop's landing pad until all necessary value info is updated
- uint hoistItemCount =
- static_cast<uint>(!!hoistChecksOutOfLoop) +
- !!hoistHeadSegmentLoadOutOfLoop +
- !!hoistHeadSegmentLengthLoadOutOfLoop +
- !!hoistLengthLoadOutOfLoop;
- if (hoistItemCount == 0)
- {
- return;
- }
- AnalysisAssert(globOpt->currentBlock->loop != nullptr);
- Loop * rootLoop = nullptr;
- for (Loop *loop = globOpt->currentBlock->loop; loop; loop = loop->parent)
- {
- rootLoop = loop;
- }
- AnalysisAssert(rootLoop != nullptr);
- ValueInfo *valueInfoToHoist = baseValueInfo;
- bool removeHeadSegment, removeHeadSegmentLength, removeLength;
- if (baseArrayValueInfo)
- {
- removeHeadSegment = baseArrayValueInfo->HeadSegmentSym() && !hoistHeadSegmentLoadOutOfLoop;
- removeHeadSegmentLength = baseArrayValueInfo->HeadSegmentLengthSym() && !hoistHeadSegmentLengthLoadOutOfLoop;
- removeLength = baseArrayValueInfo->LengthSym() && !hoistLengthLoadOutOfLoop;
- }
- else
- {
- removeLength = removeHeadSegmentLength = removeHeadSegment = false;
- }
- for (InvariantBlockBackwardIterator it(
- globOpt,
- globOpt->currentBlock,
- rootLoop->landingPad,
- baseOpnd->m_sym,
- baseValue->GetValueNumber());
- it.IsValid();
- it.MoveNext())
- {
- if (removeHeadSegment || removeHeadSegmentLength || removeLength)
- {
- // Remove information that shouldn't be there anymore, from the value info
- valueInfoToHoist =
- valueInfoToHoist->AsArrayValueInfo()->Copy(
- globOpt->alloc,
- !removeHeadSegment,
- !removeHeadSegmentLength,
- !removeLength);
- removeLength = removeHeadSegmentLength = removeHeadSegment = false;
- }
- BasicBlock *const block = it.Block();
- Value *const blockBaseValue = it.InvariantSymValue();
- globOpt->HoistInvariantValueInfo(valueInfoToHoist, blockBaseValue, block);
- // See if we have completed hoisting value info for one of the items
- if (hoistChecksOutOfLoop && block == hoistChecksOutOfLoop->landingPad)
- {
- // All other items depend on array checks, so we can just stop here
- hoistChecksOutOfLoop = nullptr;
- break;
- }
- if (hoistHeadSegmentLoadOutOfLoop && block == hoistHeadSegmentLoadOutOfLoop->landingPad)
- {
- hoistHeadSegmentLoadOutOfLoop = nullptr;
- if (--hoistItemCount == 0)
- {
- break;
- }
- if (valueInfoToHoist->IsArrayValueInfo() && valueInfoToHoist->AsArrayValueInfo()->HeadSegmentSym())
- {
- removeHeadSegment = true;
- }
- }
- if (hoistHeadSegmentLengthLoadOutOfLoop && block == hoistHeadSegmentLengthLoadOutOfLoop->landingPad)
- {
- hoistHeadSegmentLengthLoadOutOfLoop = nullptr;
- if (--hoistItemCount == 0)
- {
- break;
- }
- if (valueInfoToHoist->IsArrayValueInfo() && valueInfoToHoist->AsArrayValueInfo()->HeadSegmentLengthSym())
- {
- removeHeadSegmentLength = true;
- }
- }
- if (hoistLengthLoadOutOfLoop && block == hoistLengthLoadOutOfLoop->landingPad)
- {
- hoistLengthLoadOutOfLoop = nullptr;
- if (--hoistItemCount == 0)
- {
- break;
- }
- if (valueInfoToHoist->IsArrayValueInfo() && valueInfoToHoist->AsArrayValueInfo()->LengthSym())
- {
- removeLength = true;
- }
- }
- }
- }
- void GlobOpt::ArraySrcOpt::InsertInstrInLandingPad(IR::Instr *const instr, Loop *const hoistOutOfLoop)
- {
- if (hoistOutOfLoop->bailOutInfo->bailOutInstr)
- {
- instr->SetByteCodeOffset(hoistOutOfLoop->bailOutInfo->bailOutInstr);
- hoistOutOfLoop->bailOutInfo->bailOutInstr->InsertBefore(instr);
- }
- else
- {
- instr->SetByteCodeOffset(hoistOutOfLoop->landingPad->GetLastInstr());
- hoistOutOfLoop->landingPad->InsertAfter(instr);
- }
- };
- void GlobOpt::ArraySrcOpt::ShareBailOut()
- {
- Assert(shareableBailOutInfo);
- if (shareableBailOutInfo->bailOutInstr != shareableBailOutInfoOriginalOwner)
- {
- return;
- }
- Assert(shareableBailOutInfoOriginalOwner->GetBailOutInfo() == shareableBailOutInfo);
- IR::Instr *const sharedBailOut = shareableBailOutInfoOriginalOwner->ShareBailOut();
- Assert(sharedBailOut->GetBailOutInfo() == shareableBailOutInfo);
- shareableBailOutInfoOriginalOwner = nullptr;
- sharedBailOut->Unlink();
- insertBeforeInstr->InsertBefore(sharedBailOut);
- insertBeforeInstr = sharedBailOut;
- }
- void GlobOpt::ArraySrcOpt::InsertHeadSegmentLoad()
- {
- TRACE_TESTTRACE_PHASE_INSTR(Js::ArraySegmentHoistPhase, instr, _u("Separating array segment load\n"));
- Assert(newHeadSegmentSym);
- IR::RegOpnd *const headSegmentOpnd = IR::RegOpnd::New(newHeadSegmentSym, newHeadSegmentSym->GetType(), instr->m_func);
- headSegmentOpnd->SetIsJITOptimizedReg(true);
- IR::RegOpnd *const jitOptimizedBaseOpnd = baseOpnd->Copy(instr->m_func)->AsRegOpnd();
- jitOptimizedBaseOpnd->SetIsJITOptimizedReg(true);
- IR::Instr *loadObjectArray;
- if (baseValueType.GetObjectType() == ObjectType::ObjectWithArray)
- {
- loadObjectArray =
- IR::Instr::New(
- Js::OpCode::LdIndir,
- headSegmentOpnd,
- IR::IndirOpnd::New(
- jitOptimizedBaseOpnd,
- Js::DynamicObject::GetOffsetOfObjectArray(),
- jitOptimizedBaseOpnd->GetType(),
- instr->m_func),
- instr->m_func);
- }
- else
- {
- loadObjectArray = nullptr;
- }
- IR::Instr *const loadHeadSegment =
- IR::Instr::New(
- Js::OpCode::LdIndir,
- headSegmentOpnd,
- IR::IndirOpnd::New(
- loadObjectArray ? headSegmentOpnd : jitOptimizedBaseOpnd,
- Lowerer::GetArrayOffsetOfHeadSegment(baseValueType),
- headSegmentOpnd->GetType(),
- instr->m_func),
- instr->m_func);
- if (hoistHeadSegmentLoadOutOfLoop)
- {
- Assert(!(isLikelyJsArray && hoistHeadSegmentLoadOutOfLoop->jsArrayKills.KillsArrayHeadSegments()));
- TRACE_PHASE_INSTR(
- Js::ArraySegmentHoistPhase,
- instr,
- _u("Hoisting array segment load out of loop %u to landing pad block %u\n"),
- hoistHeadSegmentLoadOutOfLoop->GetLoopNumber(),
- hoistHeadSegmentLoadOutOfLoop->landingPad->GetBlockNum());
- TESTTRACE_PHASE_INSTR(Js::ArraySegmentHoistPhase, instr, _u("Hoisting array segment load out of loop\n"));
- if (loadObjectArray)
- {
- InsertInstrInLandingPad(loadObjectArray, hoistHeadSegmentLoadOutOfLoop);
- }
- InsertInstrInLandingPad(loadHeadSegment, hoistHeadSegmentLoadOutOfLoop);
- }
- else
- {
- if (loadObjectArray)
- {
- loadObjectArray->SetByteCodeOffset(instr);
- insertBeforeInstr->InsertBefore(loadObjectArray);
- }
- loadHeadSegment->SetByteCodeOffset(instr);
- insertBeforeInstr->InsertBefore(loadHeadSegment);
- instr->loadedArrayHeadSegment = true;
- }
- }
- void GlobOpt::ArraySrcOpt::Optimize()
- {
- if (!CheckOpCode())
- {
- return;
- }
- Assert(!(baseOwnerInstr && baseOwnerIndir));
- Assert(!needsHeadSegmentLength || needsHeadSegment);
- TypeSpecIndex();
- if (isProfilableStElem && !globOpt->IsLoopPrePass())
- {
- // If the dead-store pass decides to add the bailout kind IR::BailOutInvalidatedArrayHeadSegment, and the fast path is
- // generated, it may bail out before the operation is done, so this would need to be a pre-op bailout.
- if (instr->HasBailOutInfo())
- {
- Assert(instr->GetByteCodeOffset() != Js::Constants::NoByteCodeOffset && instr->GetBailOutInfo()->bailOutOffset <= instr->GetByteCodeOffset());
- const IR::BailOutKind bailOutKind = instr->GetBailOutKind();
- Assert(!(bailOutKind & ~IR::BailOutKindBits) || (bailOutKind & ~IR::BailOutKindBits) == IR::BailOutOnImplicitCallsPreOp);
- if (!(bailOutKind & ~IR::BailOutKindBits))
- {
- instr->SetBailOutKind(bailOutKind + IR::BailOutOnImplicitCallsPreOp);
- }
- }
- else
- {
- globOpt->GenerateBailAtOperation(&instr, IR::BailOutOnImplicitCallsPreOp);
- }
- }
- baseValue = globOpt->CurrentBlockData()->FindValue(baseOpnd->m_sym);
- if (baseValue == nullptr)
- {
- return;
- }
- baseValueInfo = baseValue->GetValueInfo();
- baseValueType = baseValueInfo->Type();
- baseOpnd->SetValueType(baseValueType);
- if (!baseValueType.IsLikelyAnyOptimizedArray() ||
- !globOpt->DoArrayCheckHoist(baseValueType, globOpt->currentBlock->loop, instr) ||
- (baseOwnerIndir && !globOpt->ShouldExpectConventionalArrayIndexValue(baseOwnerIndir)))
- {
- return;
- }
- isLikelyJsArray = !baseValueType.IsLikelyTypedArray();
- Assert(isLikelyJsArray == baseValueType.IsLikelyArrayOrObjectWithArray());
- Assert(!isLikelyJsArray == baseValueType.IsLikelyOptimizedTypedArray());
- if (!isLikelyJsArray && instr->m_opcode == Js::OpCode::LdMethodElem)
- {
- // Fast path is not generated in this case since the subsequent call will throw
- return;
- }
- isLikelyVirtualTypedArray = baseValueType.IsLikelyOptimizedVirtualTypedArray();
- Assert(!(isLikelyJsArray && isLikelyVirtualTypedArray));
- newBaseValueType = baseValueType.ToDefiniteObject();
- if (isLikelyJsArray && newBaseValueType.HasNoMissingValues() && !globOpt->DoArrayMissingValueCheckHoist())
- {
- newBaseValueType = newBaseValueType.SetHasNoMissingValues(false);
- }
- Assert((newBaseValueType == baseValueType) == baseValueType.IsObject());
- if (globOpt->IsLoopPrePass())
- {
- if (newBaseValueType != baseValueType)
- {
- if (globOpt->IsSafeToTransferInPrePass(baseOpnd, baseValue))
- {
- UpdateValue(nullptr, nullptr, nullptr);
- }
- else if (isLikelyJsArray && globOpt->IsOperationThatLikelyKillsJsArraysWithNoMissingValues(instr) && baseValueInfo->HasNoMissingValues())
- {
- globOpt->ChangeValueType(nullptr, baseValue, baseValueInfo->Type().SetHasNoMissingValues(false), true);
- }
- }
- // For javascript arrays and objects with javascript arrays:
- // - Implicit calls need to be disabled and calls cannot be allowed in the loop since the array vtable may be changed
- // into an ES5 array.
- // For typed arrays:
- // - A typed array's array buffer may be transferred to a web worker as part of an implicit call, in which case the
- // typed array's length is set to zero. Implicit calls need to be disabled if the typed array's head segment length
- // is going to be loaded and used later.
- // Since we don't know if the loop has kills after this instruction, the kill information may not be complete. If a kill
- // is found later, this information will be updated to not require disabling implicit calls.
- const bool kills = isLikelyJsArray ? globOpt->rootLoopPrePass->jsArrayKills.KillsValueType(newBaseValueType) : globOpt->rootLoopPrePass->jsArrayKills.KillsTypedArrayHeadSegmentLengths();
- if (!kills)
- {
- globOpt->rootLoopPrePass->needImplicitCallBailoutChecksForJsArrayCheckHoist = true;
- }
- return;
- }
- if (baseValueInfo->IsArrayValueInfo())
- {
- baseArrayValueInfo = baseValueInfo->AsArrayValueInfo();
- }
- doArrayChecks = !baseValueType.IsObject();
- doArraySegmentHoist =
- globOpt->DoArraySegmentHoist(baseValueType) &&
- instr->m_opcode != Js::OpCode::StElemC;
- headSegmentIsAvailable =
- baseArrayValueInfo &&
- baseArrayValueInfo->HeadSegmentSym();
- doHeadSegmentLoad =
- doArraySegmentHoist &&
- needsHeadSegment && !headSegmentIsAvailable;
- doArraySegmentLengthHoist =
- doArraySegmentHoist &&
- (isLikelyJsArray || globOpt->DoTypedArraySegmentLengthHoist(globOpt->currentBlock->loop));
- headSegmentLengthIsAvailable =
- baseArrayValueInfo &&
- baseArrayValueInfo->HeadSegmentLengthSym();
- doHeadSegmentLengthLoad =
- doArraySegmentLengthHoist &&
- (needsHeadSegmentLength || (!isLikelyJsArray && needsLength)) &&
- !headSegmentLengthIsAvailable;
- lengthIsAvailable =
- baseArrayValueInfo &&
- baseArrayValueInfo->LengthSym();
- doLengthLoad =
- globOpt->DoArrayLengthHoist() &&
- needsLength &&
- !lengthIsAvailable &&
- baseValueType.IsLikelyArray() &&
- globOpt->DoLdLenIntSpec(instr->m_opcode == Js::OpCode::LdLen_A ? instr : nullptr, baseValueType);
- newHeadSegmentSym = doHeadSegmentLoad ? StackSym::New(TyMachPtr, instr->m_func) : nullptr;
- newHeadSegmentLengthSym = doHeadSegmentLengthLoad ? StackSym::New(TyUint32, instr->m_func) : nullptr;
- newLengthSym = doLengthLoad ? StackSym::New(TyUint32, instr->m_func) : nullptr;
- if (Js::IsSimd128LoadStore(instr->m_opcode) || instr->m_opcode == Js::OpCode::IsIn)
- {
- // SIMD_JS
- // simd load/store never call helper
- canBailOutOnArrayAccessHelperCall = true;
- }
- else
- {
- canBailOutOnArrayAccessHelperCall =
- (isProfilableLdElem || isProfilableStElem) &&
- globOpt->DoEliminateArrayAccessHelperCall() &&
- !(
- instr->IsProfiledInstr() &&
- (
- isProfilableLdElem
- ? instr->AsProfiledInstr()->u.ldElemInfo->LikelyNeedsHelperCall()
- : instr->AsProfiledInstr()->u.stElemInfo->LikelyNeedsHelperCall()
- )
- );
- }
- CheckVirtualArrayBounds();
- if (needsBoundChecks && globOpt->DoBoundCheckElimination())
- {
- TryEliminiteBoundsCheck();
- }
- if (doArrayChecks || doHeadSegmentLoad || doHeadSegmentLengthLoad || doLengthLoad || doExtractBoundChecks)
- {
- CheckLoops();
- insertBeforeInstr = instr->GetInsertBeforeByteCodeUsesInstr();
- if (doArrayChecks)
- {
- DoArrayChecks();
- }
- if (doLengthLoad)
- {
- DoLengthLoad();
- }
- if (doHeadSegmentLoad && isLikelyJsArray)
- {
- // For javascript arrays, the head segment is required to load the head segment length
- InsertHeadSegmentLoad();
- }
- if (doHeadSegmentLengthLoad)
- {
- DoHeadSegmentLengthLoad();
- }
- if (doExtractBoundChecks)
- {
- DoExtractBoundChecks();
- }
- if (doHeadSegmentLoad && !isLikelyJsArray)
- {
- // For typed arrays, load the length first, followed by the bound checks, and then load the head segment. This
- // allows the length sym to become dead by the time of the head segment load, freeing up the register for use by the
- // head segment sym.
- InsertHeadSegmentLoad();
- }
- if (doArrayChecks || doHeadSegmentLoad || doHeadSegmentLengthLoad || doLengthLoad)
- {
- UpdateValue(newHeadSegmentSym, newHeadSegmentLengthSym, newLengthSym);
- baseValueInfo = baseValue->GetValueInfo();
- baseArrayValueInfo = baseValueInfo->IsArrayValueInfo() ? baseValueInfo->AsArrayValueInfo() : nullptr;
- UpdateHoistedValueInfo();
- }
- }
- IR::ArrayRegOpnd * baseArrayOpnd;
- if (baseArrayValueInfo != nullptr)
- {
- // Update the opnd to include the associated syms
- baseArrayOpnd =
- baseArrayValueInfo->CreateOpnd(
- baseOpnd,
- needsHeadSegment,
- needsHeadSegmentLength || (!isLikelyJsArray && needsLength),
- needsLength,
- eliminatedLowerBoundCheck,
- eliminatedUpperBoundCheck,
- instr->m_func);
- if (baseOwnerInstr != nullptr)
- {
- if (baseOwnerInstr->GetSrc1() == baseOpnd)
- {
- baseOwnerInstr->ReplaceSrc1(baseArrayOpnd);
- }
- else
- {
- Assert(baseOwnerInstr->GetSrc2() == baseOpnd);
- baseOwnerInstr->ReplaceSrc2(baseArrayOpnd);
- }
- }
- else
- {
- Assert(baseOwnerIndir);
- Assert(baseOwnerIndir->GetBaseOpnd() == baseOpnd);
- baseOwnerIndir->ReplaceBaseOpnd(baseArrayOpnd);
- }
- baseOpnd = baseArrayOpnd;
- }
- else
- {
- baseArrayOpnd = nullptr;
- }
- globOpt->ProcessNoImplicitCallArrayUses(baseOpnd, baseArrayOpnd, instr, isLikelyJsArray, isLoad || isStore || instr->m_opcode == Js::OpCode::IsIn);
- const auto OnEliminated = [&](const Js::Phase phase, const char *const eliminatedLoad)
- {
- TRACE_TESTTRACE_PHASE_INSTR(phase, instr, _u("Eliminating array %S\n"), eliminatedLoad);
- };
- OnEliminated(Js::Phase::ArrayCheckHoistPhase, "checks");
- if (baseArrayOpnd)
- {
- if (baseArrayOpnd->HeadSegmentSym())
- {
- OnEliminated(Js::Phase::ArraySegmentHoistPhase, "head segment load");
- }
- if (baseArrayOpnd->HeadSegmentLengthSym())
- {
- OnEliminated(Js::Phase::ArraySegmentHoistPhase, "head segment length load");
- }
- if (baseArrayOpnd->LengthSym())
- {
- OnEliminated(Js::Phase::ArrayLengthHoistPhase, "length load");
- }
- if (baseArrayOpnd->EliminatedLowerBoundCheck())
- {
- OnEliminated(Js::Phase::BoundCheckEliminationPhase, "lower bound check");
- }
- if (baseArrayOpnd->EliminatedUpperBoundCheck())
- {
- OnEliminated(Js::Phase::BoundCheckEliminationPhase, "upper bound check");
- }
- }
- if (instr->m_opcode == Js::OpCode::IsIn)
- {
- if (eliminatedLowerBoundCheck && eliminatedUpperBoundCheck)
- {
- TRACE_TESTTRACE_PHASE_INSTR(Js::Phase::BoundCheckEliminationPhase, instr, _u("Eliminating IsIn\n"));
- globOpt->CaptureByteCodeSymUses(instr);
- instr->m_opcode = Js::OpCode::Ld_A;
- IR::AddrOpnd * addrOpnd = IR::AddrOpnd::New(func->GetScriptContextInfo()->GetTrueAddr(), IR::AddrOpndKindDynamicVar, func, true);
- addrOpnd->SetValueType(ValueType::Boolean);
- instr->ReplaceSrc1(addrOpnd);
- instr->FreeSrc2();
- originalIndexOpnd->Free(func);
- originalIndexOpnd = nullptr;
- src1Val = globOpt->GetVarConstantValue(instr->GetSrc1()->AsAddrOpnd());
- src2Val = nullptr;
- }
- return;
- }
- if (!canBailOutOnArrayAccessHelperCall)
- {
- return;
- }
- // Bail out instead of generating a helper call. This helps to remove the array reference when the head segment and head
- // segment length are available, reduces code size, and allows bound checks to be separated.
- if (instr->HasBailOutInfo())
- {
- const IR::BailOutKind bailOutKind = instr->GetBailOutKind();
- Assert(
- !(bailOutKind & ~IR::BailOutKindBits) ||
- (bailOutKind & ~IR::BailOutKindBits) == IR::BailOutOnImplicitCallsPreOp);
- instr->SetBailOutKind(bailOutKind & IR::BailOutKindBits | IR::BailOutOnArrayAccessHelperCall);
- }
- else
- {
- globOpt->GenerateBailAtOperation(&instr, IR::BailOutOnArrayAccessHelperCall);
- }
- }
|