| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLanguagePch.h"
- using namespace Js;
- #if ENABLE_PROFILE_INFO
- Var ProfilingHelpers::ProfiledLdElem(
- const Var base,
- const Var varIndex,
- FunctionBody *const functionBody,
- const ProfileId profileId,
- bool didArrayAccessHelperCall,
- bool bailedOutOnArraySpecialization)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdElem);
- Assert(base);
- Assert(varIndex);
- Assert(functionBody);
- Assert(profileId != Constants::NoProfileId);
- LdElemInfo ldElemInfo;
- if (bailedOutOnArraySpecialization)
- {
- ldElemInfo.disableAggressiveSpecialization = true;
- }
- // Only enable fast path if the javascript array is not cross site
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(base);
- #endif
- const bool isJsArray = !TaggedNumber::Is(base) && VirtualTableInfo<JavascriptArray>::HasVirtualTable(base);
- const bool fastPath = isJsArray;
- if(fastPath)
- {
- JavascriptArray *const array = UnsafeVarTo<JavascriptArray>(base);
- ldElemInfo.arrayType = ValueType::FromArray(ObjectType::Array, array, TypeIds_Array).ToLikely();
- const Var element = ProfiledLdElem_FastPath(array, varIndex, functionBody->GetScriptContext(), &ldElemInfo);
- ldElemInfo.elemType = ldElemInfo.elemType.Merge(element);
- functionBody->GetDynamicProfileInfo()->RecordElementLoad(functionBody, profileId, ldElemInfo);
- return element;
- }
- Assert(!isJsArray);
- bool isObjectWithArray;
- TypeId arrayTypeId;
- JavascriptArray *const array =
- JavascriptArray::GetArrayForArrayOrObjectWithArray(base, &isObjectWithArray, &arrayTypeId);
- if (didArrayAccessHelperCall)
- {
- ldElemInfo.neededHelperCall = true;
- }
- do // while(false)
- {
- // The fast path is only for JavascriptArray and doesn't cover native arrays, objects with internal arrays, or typed
- // arrays, but we still need to profile the array
- uint32 headSegmentLength;
- if(array)
- {
- ldElemInfo.arrayType =
- (
- isObjectWithArray
- ? ValueType::FromObjectArray(array)
- : ValueType::FromArray(ObjectType::Array, array, arrayTypeId)
- ).ToLikely();
- SparseArraySegmentBase *const head = array->GetHead();
- Assert(head->left == 0);
- headSegmentLength = head->length;
- }
- else if(TypedArrayBase::TryGetLengthForOptimizedTypedArray(base, &headSegmentLength, &arrayTypeId))
- {
- bool isVirtual = (VirtualTableInfoBase::GetVirtualTable(base) == ValueType::GetVirtualTypedArrayVtable(arrayTypeId));
- ldElemInfo.arrayType = ValueType::FromTypeId(arrayTypeId, isVirtual).ToLikely();
- }
- else if(Js::VarIs<Js::RecyclableObject>(base))
- {
- ldElemInfo.arrayType = ValueType::FromObject(Js::UnsafeVarTo<Js::RecyclableObject>(base)).ToLikely();
- break;
- }
- else
- {
- break;
- }
- if(!TaggedInt::Is(varIndex))
- {
- ldElemInfo.neededHelperCall = true;
- break;
- }
- const int32 index = TaggedInt::ToInt32(varIndex);
- const uint32 offset = index;
- if(index < 0 || offset >= headSegmentLength || (array && array->IsMissingHeadSegmentItem(offset)))
- {
- ldElemInfo.neededHelperCall = true;
- break;
- }
- } while(false);
- ScriptContext* scriptContext = functionBody->GetScriptContext();
- RecyclableObject* cacheOwner;
- PropertyRecordUsageCache* propertyRecordUsageCache;
- Var element = nullptr;
- if (JavascriptOperators::GetPropertyRecordUsageCache(varIndex, scriptContext, &propertyRecordUsageCache, &cacheOwner))
- {
- PropertyCacheOperationInfo operationInfo;
- element = JavascriptOperators::GetElementIWithCache<true /* ReturnOperationInfo */>(base, cacheOwner, propertyRecordUsageCache, scriptContext, &operationInfo);
- ldElemInfo.flags = DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType);
- ldElemInfo.flags = DynamicProfileInfo::MergeFldInfoFlags(ldElemInfo.flags, DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
- }
- else
- {
- element = JavascriptOperators::OP_GetElementI(base, varIndex, scriptContext);
- }
- const ValueType arrayType(ldElemInfo.GetArrayType());
- if(!arrayType.IsUninitialized() || ldElemInfo.flags != Js::FldInfo_NoInfo)
- {
- if(array && arrayType.IsLikelyObject() && arrayType.GetObjectType() == ObjectType::Array && !arrayType.HasIntElements())
- {
- JavascriptOperators::UpdateNativeArrayProfileInfoToCreateVarArray(
- array,
- arrayType.HasFloatElements(),
- arrayType.HasVarElements());
- }
- ldElemInfo.elemType = ValueType::Uninitialized.Merge(element);
- functionBody->GetDynamicProfileInfo()->RecordElementLoad(functionBody, profileId, ldElemInfo);
- return element;
- }
- functionBody->GetDynamicProfileInfo()->RecordElementLoadAsProfiled(functionBody, profileId);
- return element;
- JIT_HELPER_END(ProfiledLdElem);
- }
- Var ProfilingHelpers::ProfiledLdElem_FastPath(
- JavascriptArray *const array,
- const Var varIndex,
- ScriptContext *const scriptContext,
- LdElemInfo *const ldElemInfo)
- {
- Assert(array);
- Assert(varIndex);
- Assert(scriptContext);
- do // while(false)
- {
- Assert(!array->IsCrossSiteObject());
- if (!TaggedInt::Is(varIndex))
- {
- break;
- }
- int32 index = TaggedInt::ToInt32(varIndex);
- if (index < 0)
- {
- break;
- }
- if(ldElemInfo)
- {
- SparseArraySegment<Var> *const head = static_cast<SparseArraySegment<Var> *>(array->GetHead());
- Assert(head->left == 0);
- const uint32 offset = index;
- if(offset < head->length)
- {
- const Var element = head->elements[offset];
- if(!SparseArraySegment<Var>::IsMissingItem(&element))
- {
- // Successful fastpath
- return element;
- }
- }
- ldElemInfo->neededHelperCall = true;
- }
- SparseArraySegment<Var> *seg = (SparseArraySegment<Var>*)array->GetLastUsedSegment();
- if ((uint32) index < seg->left)
- {
- break;
- }
- uint32 index2 = index - seg->left;
- if (index2 < seg->length)
- {
- Var elem = seg->elements[index2];
- if (elem != SparseArraySegment<Var>::GetMissingItem())
- {
- // Successful fastpath
- return elem;
- }
- }
- } while(false);
- if(ldElemInfo)
- {
- ldElemInfo->neededHelperCall = true;
- }
- return JavascriptOperators::OP_GetElementI(array, varIndex, scriptContext);
- }
- void ProfilingHelpers::ProfiledStElem_DefaultFlags(
- const Var base,
- const Var varIndex,
- const Var value,
- FunctionBody *const functionBody,
- const ProfileId profileId)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledStElem_DefaultFlags);
- JIT_HELPER_SAME_ATTRIBUTES(ProfiledStElem_DefaultFlags, ProfiledStElem);
- ProfiledStElem(base, varIndex, value, functionBody, profileId, PropertyOperation_None, false, false);
- JIT_HELPER_END(ProfiledStElem_DefaultFlags);
- }
- void ProfilingHelpers::ProfiledStElem(
- const Var base,
- const Var varIndex,
- const Var value,
- FunctionBody *const functionBody,
- const ProfileId profileId,
- const PropertyOperationFlags flags,
- bool didArrayAccessHelperCall,
- bool bailedOutOnArraySpecialization)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledStElem);
- Assert(base);
- Assert(varIndex);
- Assert(value);
- Assert(functionBody);
- Assert(profileId != Constants::NoProfileId);
- StElemInfo stElemInfo;
- if (bailedOutOnArraySpecialization)
- {
- stElemInfo.disableAggressiveSpecialization = true;
- }
- // Only enable fast path if the javascript array is not cross site
- const bool isJsArray = !TaggedNumber::Is(base) && VirtualTableInfo<JavascriptArray>::HasVirtualTable(base);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- const bool fastPath = isJsArray && !JavascriptOperators::SetElementMayHaveImplicitCalls(scriptContext);
- if(fastPath)
- {
- JavascriptArray *const array = UnsafeVarTo<JavascriptArray>(base);
- stElemInfo.arrayType = ValueType::FromArray(ObjectType::Array, array, TypeIds_Array).ToLikely();
- stElemInfo.createdMissingValue = array->HasNoMissingValues();
- ProfiledStElem_FastPath(array, varIndex, value, scriptContext, flags, &stElemInfo);
- stElemInfo.createdMissingValue &= !array->HasNoMissingValues();
- functionBody->GetDynamicProfileInfo()->RecordElementStore(functionBody, profileId, stElemInfo);
- return;
- }
- JavascriptArray *array;
- bool isObjectWithArray;
- TypeId arrayTypeId;
- if(isJsArray)
- {
- array = UnsafeVarTo<JavascriptArray>(base);
- isObjectWithArray = false;
- arrayTypeId = TypeIds_Array;
- }
- else
- {
- array = JavascriptArray::GetArrayForArrayOrObjectWithArray(base, &isObjectWithArray, &arrayTypeId);
- }
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(base);
- #endif
- do // while(false)
- {
- // The fast path is only for JavascriptArray and doesn't cover native arrays, objects with internal arrays, or typed
- // arrays, but we still need to profile the array
- uint32 length;
- uint32 headSegmentLength;
- if(array)
- {
- stElemInfo.arrayType =
- (
- isObjectWithArray
- ? ValueType::FromObjectArray(array)
- : ValueType::FromArray(ObjectType::Array, array, arrayTypeId)
- ).ToLikely();
- stElemInfo.createdMissingValue = array->HasNoMissingValues();
- length = array->GetLength();
- SparseArraySegmentBase *const head = array->GetHead();
- Assert(head->left == 0);
- headSegmentLength = head->length;
- }
- else if(TypedArrayBase::TryGetLengthForOptimizedTypedArray(base, &headSegmentLength, &arrayTypeId))
- {
- length = headSegmentLength;
- bool isVirtual = (VirtualTableInfoBase::GetVirtualTable(base) == ValueType::GetVirtualTypedArrayVtable(arrayTypeId));
- stElemInfo.arrayType = ValueType::FromTypeId(arrayTypeId, isVirtual).ToLikely();
- if (!TaggedNumber::Is(value) && !JavascriptNumber::Is_NoTaggedIntCheck(value))
- {
- // Non-number stored to a typed array. A helper call will be needed to convert the value.
- stElemInfo.neededHelperCall = true;
- }
- }
- else
- {
- break;
- }
- if (didArrayAccessHelperCall)
- {
- stElemInfo.neededHelperCall = true;
- }
- if(!TaggedInt::Is(varIndex))
- {
- stElemInfo.neededHelperCall = true;
- break;
- }
- const int32 index = TaggedInt::ToInt32(varIndex);
- if(index < 0)
- {
- stElemInfo.neededHelperCall = true;
- break;
- }
- const uint32 offset = index;
- if(offset >= headSegmentLength)
- {
- stElemInfo.storedOutsideHeadSegmentBounds = true;
- if(!isObjectWithArray && offset >= length)
- {
- stElemInfo.storedOutsideArrayBounds = true;
- }
- break;
- }
- if(array && array->IsMissingHeadSegmentItem(offset))
- {
- stElemInfo.filledMissingValue = true;
- }
- } while(false);
- RecyclableObject* cacheOwner;
- PropertyRecordUsageCache* propertyRecordUsageCache;
- TypeId instanceType = JavascriptOperators::GetTypeId(base);
- bool isTypedArray = (instanceType >= TypeIds_Int8Array && instanceType <= TypeIds_Float64Array);
- if (!isTypedArray && JavascriptOperators::GetPropertyRecordUsageCache(varIndex, scriptContext, &propertyRecordUsageCache, &cacheOwner))
- {
- RecyclableObject* object = nullptr;
- bool result = JavascriptOperators::GetPropertyObjectForSetElementI(base, cacheOwner, scriptContext, &object);
- Assert(result);
- PropertyCacheOperationInfo operationInfo;
- JavascriptOperators::SetElementIWithCache<true /* ReturnOperationInfo */>(base, object, cacheOwner, value, propertyRecordUsageCache, scriptContext, flags, &operationInfo);
- stElemInfo.flags = DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType);
- stElemInfo.flags = DynamicProfileInfo::MergeFldInfoFlags(stElemInfo.flags, DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
- }
- else
- {
- JavascriptOperators::OP_SetElementI(base, varIndex, value, scriptContext, flags);
- }
- if(!stElemInfo.GetArrayType().IsUninitialized() || stElemInfo.flags != Js::FldInfo_NoInfo)
- {
- if(array)
- {
- stElemInfo.createdMissingValue &= !array->HasNoMissingValues();
- }
- functionBody->GetDynamicProfileInfo()->RecordElementStore(functionBody, profileId, stElemInfo);
- return;
- }
- functionBody->GetDynamicProfileInfo()->RecordElementStoreAsProfiled(functionBody, profileId);
- JIT_HELPER_END(ProfiledStElem);
- }
- void ProfilingHelpers::ProfiledStElem_FastPath(
- JavascriptArray *const array,
- const Var varIndex,
- const Var value,
- ScriptContext *const scriptContext,
- const PropertyOperationFlags flags,
- StElemInfo *const stElemInfo)
- {
- Assert(array);
- Assert(varIndex);
- Assert(value);
- Assert(scriptContext);
- Assert(!JavascriptOperators::SetElementMayHaveImplicitCalls(scriptContext));
- do // while(false)
- {
- if (!TaggedInt::Is(varIndex))
- {
- break;
- }
- int32 index = TaggedInt::ToInt32(varIndex);
- if (index < 0)
- {
- break;
- }
- if(stElemInfo)
- {
- SparseArraySegmentBase *const head = array->GetHead();
- Assert(head->left == 0);
- const uint32 offset = index;
- if(offset >= head->length)
- {
- stElemInfo->storedOutsideHeadSegmentBounds = true;
- if(offset >= array->GetLength())
- {
- stElemInfo->storedOutsideArrayBounds = true;
- }
- }
- if(offset < head->size)
- {
- array->DirectProfiledSetItemInHeadSegmentAt(offset, value, stElemInfo);
- return;
- }
- }
- SparseArraySegment<Var>* lastUsedSeg = (SparseArraySegment<Var>*)array->GetLastUsedSegment();
- if (lastUsedSeg == NULL ||
- (uint32) index < lastUsedSeg->left)
- {
- break;
- }
- uint32 index2 = index - lastUsedSeg->left;
- if (index2 < lastUsedSeg->size)
- {
- // Successful fastpath
- array->DirectSetItemInLastUsedSegmentAt(index2, value);
- return;
- }
- } while(false);
- if(stElemInfo)
- {
- stElemInfo->neededHelperCall = true;
- }
- JavascriptOperators::OP_SetElementI(array, varIndex, value, scriptContext, flags);
- }
- JavascriptArray *ProfilingHelpers::ProfiledNewScArray(
- const uint length,
- FunctionBody *const functionBody,
- const ProfileId profileId)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledNewScArray);
- Assert(functionBody);
- Assert(profileId != Constants::NoProfileId);
- // Not creating native array here if the function is unoptimized, because it turns out to be tricky to
- // get the initialization right if GlobOpt doesn't give us bailout. It's possible, but we should see
- // a use case before spending time on it.
- ArrayCallSiteInfo *const arrayInfo =
- functionBody->GetDynamicProfileInfo()->GetArrayCallSiteInfo(functionBody, profileId);
- Assert(arrayInfo);
- if (length > SparseArraySegmentBase::INLINE_CHUNK_SIZE || (functionBody->GetHasTry() && PHASE_OFF((Js::OptimizeTryCatchPhase), functionBody)))
- {
- arrayInfo->SetIsNotNativeArray();
- }
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- JavascriptArray *array;
- if (arrayInfo->IsNativeIntArray())
- {
- JavascriptNativeIntArray *const intArray = scriptContext->GetLibrary()->CreateNativeIntArrayLiteral(length);
- Recycler *recycler = scriptContext->GetRecycler();
- intArray->SetArrayCallSite(profileId, recycler->CreateWeakReferenceHandle(functionBody));
- array = intArray;
- }
- else if (arrayInfo->IsNativeFloatArray())
- {
- JavascriptNativeFloatArray *const floatArray = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(length);
- Recycler *recycler = scriptContext->GetRecycler();
- floatArray->SetArrayCallSite(profileId, recycler->CreateWeakReferenceHandle(functionBody));
- array = floatArray;
- }
- else
- {
- array = scriptContext->GetLibrary()->CreateArrayLiteral(length);
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- array->CheckForceES5Array();
- #endif
- return array;
- JIT_HELPER_END(ProfiledNewScArray);
- }
- Var ProfilingHelpers::ProfiledNewScObjArray_Jit(
- const Var callee,
- void *const framePointer,
- const ProfileId profileId,
- const ProfileId arrayProfileId,
- CallInfo callInfo,
- ...)
- {
- ARGUMENTS(args, callee, framePointer, profileId, arrayProfileId, callInfo);
- ScriptFunction* func = UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- JIT_HELPER_REENTRANT_HEADER(ProfiledNewScObjArray);
- return
- ProfiledNewScObjArray(
- callee,
- args,
- func,
- profileId,
- arrayProfileId);
- JIT_HELPER_END(ProfiledNewScObjArray);
- }
- Var ProfilingHelpers::ProfiledNewScObjArraySpread_Jit(
- const Js::AuxArray<uint32> *spreadIndices,
- const Var callee,
- void *const framePointer,
- const ProfileId profileId,
- const ProfileId arrayProfileId,
- CallInfo callInfo,
- ...)
- {
- ARGUMENTS(args, spreadIndices, callee, framePointer, profileId, arrayProfileId, callInfo);
- Js::ScriptFunction *function = UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- ScriptContext* scriptContext = function->GetScriptContext();
- JIT_HELPER_REENTRANT_HEADER(ProfiledNewScObjArraySpread);
- // GetSpreadSize ensures that spreadSize < 2^24
- uint32 spreadSize = 0;
- if (spreadIndices != nullptr)
- {
- Arguments outArgs(CallInfo(args.Info.Flags, 0), nullptr);
- spreadSize = JavascriptFunction::GetSpreadSize(args, spreadIndices, scriptContext);
- Assert(spreadSize == (((1 << 24) - 1) & spreadSize));
- // Allocate room on the stack for the spread args.
- outArgs.Info.Count = spreadSize;
- const unsigned STACK_ARGS_ALLOCA_THRESHOLD = 8; // Number of stack args we allow before using _alloca
- Var stackArgs[STACK_ARGS_ALLOCA_THRESHOLD];
- size_t outArgsSize = 0;
- if (outArgs.Info.Count > STACK_ARGS_ALLOCA_THRESHOLD)
- {
- PROBE_STACK(scriptContext, outArgs.Info.Count * sizeof(Var) + Js::Constants::MinStackDefault); // args + function call
- outArgsSize = outArgs.Info.Count * sizeof(Var);
- outArgs.Values = (Var*)_alloca(outArgsSize);
- ZeroMemory(outArgs.Values, outArgsSize);
- }
- else
- {
- outArgs.Values = stackArgs;
- outArgsSize = STACK_ARGS_ALLOCA_THRESHOLD * sizeof(Var);
- ZeroMemory(outArgs.Values, outArgsSize); // We may not use all of the elements
- }
- JavascriptFunction::SpreadArgs(args, outArgs, spreadIndices, scriptContext);
- return
- ProfiledNewScObjArray(
- callee,
- outArgs,
- function,
- profileId,
- arrayProfileId);
- }
- else
- {
- return
- ProfiledNewScObjArray(
- callee,
- args,
- function,
- profileId,
- arrayProfileId);
- }
- JIT_HELPER_END(ProfiledNewScObjArraySpread);
- }
- Var ProfilingHelpers::ProfiledNewScObjArray(
- const Var callee,
- const Arguments args,
- ScriptFunction *const caller,
- const ProfileId profileId,
- const ProfileId arrayProfileId)
- {
- Assert(callee);
- Assert(args.Info.Count != 0);
- Assert(caller);
- Assert(profileId != Constants::NoProfileId);
- Assert(arrayProfileId != Constants::NoProfileId);
- FunctionBody *const callerFunctionBody = caller->GetFunctionBody();
- DynamicProfileInfo *const profileInfo = callerFunctionBody->GetDynamicProfileInfo();
- ArrayCallSiteInfo *const arrayInfo = profileInfo->GetArrayCallSiteInfo(callerFunctionBody, arrayProfileId);
- Assert(arrayInfo);
- ScriptContext *const scriptContext = callerFunctionBody->GetScriptContext();
- FunctionInfo *const calleeFunctionInfo = JavascriptOperators::GetConstructorFunctionInfo(callee, scriptContext);
- if (calleeFunctionInfo != &JavascriptArray::EntryInfo::NewInstance)
- {
- // It may be worth checking the object that we actually got back from the ctor, but
- // we should at least not keep bailing out at this call site.
- arrayInfo->SetIsNotNativeArray();
- return ProfiledNewScObject(callee, args, callerFunctionBody, profileId);
- }
- profileInfo->RecordCallSiteInfo(
- callerFunctionBody,
- profileId,
- calleeFunctionInfo,
- caller,
- args.Info.Count,
- true);
- args.Values[0] = nullptr;
- Var array;
- Js::RecyclableObject* calleeObject = UnsafeVarTo<RecyclableObject>(callee);
- if (arrayInfo->IsNativeIntArray())
- {
- array = JavascriptNativeIntArray::NewInstance(calleeObject, args);
- if (VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(array))
- {
- JavascriptNativeIntArray *const intArray = static_cast<JavascriptNativeIntArray *>(array);
- intArray->SetArrayCallSite(arrayProfileId, scriptContext->GetRecycler()->CreateWeakReferenceHandle(callerFunctionBody));
- }
- else
- {
- arrayInfo->SetIsNotNativeIntArray();
- if (VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(array))
- {
- JavascriptNativeFloatArray *const floatArray = static_cast<JavascriptNativeFloatArray *>(array);
- floatArray->SetArrayCallSite(arrayProfileId, scriptContext->GetRecycler()->CreateWeakReferenceHandle(callerFunctionBody));
- }
- else
- {
- arrayInfo->SetIsNotNativeArray();
- }
- }
- }
- else if (arrayInfo->IsNativeFloatArray())
- {
- array = JavascriptNativeFloatArray::NewInstance(calleeObject, args);
- if (VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(array))
- {
- JavascriptNativeFloatArray *const floatArray = static_cast<JavascriptNativeFloatArray *>(array);
- floatArray->SetArrayCallSite(arrayProfileId, scriptContext->GetRecycler()->CreateWeakReferenceHandle(callerFunctionBody));
- }
- else
- {
- arrayInfo->SetIsNotNativeArray();
- }
- }
- else
- {
- array = JavascriptArray::NewInstance(calleeObject, args);
- }
- return CrossSite::MarshalVar(scriptContext, array, calleeObject->GetScriptContext());
- }
- Var ProfilingHelpers::ProfiledNewScObject(
- const Var callee,
- const Arguments args,
- FunctionBody *const callerFunctionBody,
- const ProfileId profileId,
- const InlineCacheIndex inlineCacheIndex,
- const Js::AuxArray<uint32> *spreadIndices)
- {
- Assert(callee);
- Assert(args.Info.Count != 0);
- Assert(callerFunctionBody);
- Assert(profileId != Constants::NoProfileId);
- ScriptContext *const scriptContext = callerFunctionBody->GetScriptContext();
- if(!TaggedNumber::Is(callee))
- {
- const auto calleeObject = JavascriptOperators::GetCallableObjectOrThrow(callee, scriptContext);
- const auto calleeFunctionInfo =
- calleeObject->GetTypeId() == TypeIds_Function
- ? UnsafeVarTo<JavascriptFunction>(calleeObject)->GetFunctionInfo()
- : nullptr;
- DynamicProfileInfo *profileInfo = callerFunctionBody->GetDynamicProfileInfo();
- profileInfo->RecordCallSiteInfo(
- callerFunctionBody,
- profileId,
- calleeFunctionInfo,
- calleeFunctionInfo ? static_cast<JavascriptFunction *>(calleeObject) : nullptr,
- args.Info.Count,
- true,
- inlineCacheIndex);
- // We need to record information here, most importantly so that we handle array subclass
- // creation properly, since optimizing those cases is important
- Var retVal = nullptr;
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- retVal = JavascriptOperators::NewScObject(callee, args, scriptContext, spreadIndices);
- }
- END_SAFE_REENTRANT_CALL
- profileInfo->RecordReturnTypeOnCallSiteInfo(callerFunctionBody, profileId, retVal);
- return retVal;
- }
- BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
- {
- return JavascriptOperators::NewScObject(callee, args, scriptContext, spreadIndices);
- }
- END_SAFE_REENTRANT_CALL
- }
- void ProfilingHelpers::ProfileLdSlot(const Var value, FunctionBody *const functionBody, const ProfileId profileId)
- {
- JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ProfileLdSlot);
- Assert(value);
- Assert(functionBody);
- Assert(profileId != Constants::NoProfileId);
- functionBody->GetDynamicProfileInfo()->RecordSlotLoad(functionBody, profileId, value);
- JIT_HELPER_END(ProfileLdSlot);
- }
- Var ProfilingHelpers::ProfiledLdLen_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- const ProfileId profileId,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdLen);
- ScriptFunction * const scriptFunction = UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- FunctionBody * functionBody = scriptFunction->GetFunctionBody();
- DynamicProfileInfo * profileInfo = functionBody->GetDynamicProfileInfo();
- LdLenInfo ldLenInfo;
- ldLenInfo.arrayType = ValueType::Uninitialized.Merge(instance);
- profileInfo->RecordLengthLoad(functionBody, profileId, ldLenInfo);
- return
- ProfiledLdFld<false, false, false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- scriptFunction->GetFunctionBody(),
- instance);
- JIT_HELPER_END(ProfiledLdLen);
- }
- Var ProfilingHelpers::ProfiledLdFld_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdFld);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- return
- ProfiledLdFld<false, false, false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- scriptFunction->GetFunctionBody(),
- instance);
- JIT_HELPER_END(ProfiledLdFld);
- }
- Var ProfilingHelpers::ProfiledLdSuperFld_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- void *const framePointer,
- const Var thisInstance)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdSuperFld);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- return
- ProfiledLdFld<false, false, false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- scriptFunction->GetFunctionBody(),
- thisInstance);
- JIT_HELPER_END(ProfiledLdSuperFld);
- }
- Var ProfilingHelpers::ProfiledLdFldForTypeOf_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdFldForTypeOf);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- return ProfiledLdFldForTypeOf<false, false, false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- scriptFunction->GetFunctionBody());
- JIT_HELPER_END(ProfiledLdFldForTypeOf);
- }
- Var ProfilingHelpers::ProfiledLdFld_CallApplyTarget_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdFld_CallApplyTarget);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- return
- ProfiledLdFld<false, false, true>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- scriptFunction->GetFunctionBody(),
- instance);
- JIT_HELPER_END(ProfiledLdFld_CallApplyTarget);
- }
- Var ProfilingHelpers::ProfiledLdMethodFld_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdMethodFld);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- return
- ProfiledLdFld<false, true, false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- scriptFunction->GetFunctionBody(),
- instance);
- JIT_HELPER_END(ProfiledLdMethodFld);
- }
- Var ProfilingHelpers::ProfiledLdRootFld_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdRootFld);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- return
- ProfiledLdFld<true, false, false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- scriptFunction->GetFunctionBody(),
- instance);
- JIT_HELPER_END(ProfiledLdRootFld);
- }
- Var ProfilingHelpers::ProfiledLdRootFldForTypeOf_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdRootFldForTypeOf);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- return ProfiledLdFldForTypeOf<true, false, false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- scriptFunction->GetFunctionBody());
- JIT_HELPER_END(ProfiledLdRootFldForTypeOf);
- }
- Var ProfilingHelpers::ProfiledLdRootMethodFld_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledLdRootMethodFld);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- return
- ProfiledLdFld<true, true, false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- scriptFunction->GetFunctionBody(),
- instance);
- JIT_HELPER_END(ProfiledLdRootMethodFld);
- }
- template<bool Root, bool Method, bool CallApplyTarget>
- Var ProfilingHelpers::ProfiledLdFld(
- const Var instance,
- const PropertyId propertyId,
- InlineCache *const inlineCache,
- const InlineCacheIndex inlineCacheIndex,
- FunctionBody *const functionBody,
- const Var thisInstance)
- {
- Assert(instance);
- Assert(thisInstance);
- Assert(propertyId != Constants::NoProperty);
- Assert(inlineCache);
- Assert(functionBody);
- Assert(inlineCacheIndex < functionBody->GetInlineCacheCount());
- Assert(!Root || inlineCacheIndex >= functionBody->GetRootObjectLoadInlineCacheStart());
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- DynamicProfileInfo *const dynamicProfileInfo = functionBody->GetDynamicProfileInfo();
- Var value;
- FldInfoFlags fldInfoFlags = FldInfo_NoInfo;
- if (Root || (VarIs<RecyclableObject>(instance) && VarIs<RecyclableObject>(thisInstance)))
- {
- RecyclableObject *const object = UnsafeVarTo<RecyclableObject>(instance);
- RecyclableObject *const thisObject = UnsafeVarTo<RecyclableObject>(thisInstance);
- if (!Root && Method && (propertyId == PropertyIds::apply || propertyId == PropertyIds::call) && VarIs<ScriptFunction>(object))
- {
- // If the property being loaded is "apply"/"call", make an optimistic assumption that apply/call is not overridden and
- // undefer the function right here if it was defer parsed before. This is required so that the load of "apply"/"call"
- // happens from the same "type". Otherwise, we will have a polymorphic cache for load of "apply"/"call".
- ScriptFunction *fn = UnsafeVarTo<ScriptFunction>(object);
- if (fn->GetType()->GetEntryPoint() == JavascriptFunction::DeferredParsingThunk)
- {
- JavascriptFunction::DeferredParse(&fn);
- }
- }
- PropertyCacheOperationInfo operationInfo;
- PropertyValueInfo propertyValueInfo;
- PropertyValueInfo::SetCacheInfo(&propertyValueInfo, functionBody, inlineCache, inlineCacheIndex, true);
- if (!CacheOperators::TryGetProperty<true, true, true, !Root && !Method, true, !Root, true, false, true, false>(
- object,
- Root,
- object,
- propertyId,
- &value,
- scriptContext,
- &operationInfo,
- &propertyValueInfo))
- {
- const auto PatchGetValue = &JavascriptOperators::PatchGetValueWithThisPtrNoFastPath;
- const auto PatchGetRootValue = &JavascriptOperators::PatchGetRootValueNoFastPath_Var;
- const auto PatchGetMethod = &JavascriptOperators::PatchGetMethodNoFastPath;
- const auto PatchGetRootMethod = &JavascriptOperators::PatchGetRootMethodNoFastPath_Var;
- const auto PatchGet =
- Root
- ? Method ? PatchGetRootMethod : PatchGetRootValue
- : PatchGetMethod ;
- value = (!Root && !Method) ? PatchGetValue(functionBody, inlineCache, inlineCacheIndex, object, propertyId, thisObject) :
- PatchGet(functionBody, inlineCache, inlineCacheIndex, object, propertyId);
- CacheOperators::PretendTryGetProperty<true, false>(object->GetType(), &operationInfo, &propertyValueInfo);
- }
- else if (!Root && !Method)
- {
- // Inline cache hit. oldflags must match the new ones. If not there is mark it as polymorphic as there is likely
- // a bailout to interpreter and change in the inline cache type.
- const FldInfoFlags oldflags = dynamicProfileInfo->GetFldInfo(functionBody, inlineCacheIndex)->flags;
- if ((oldflags != FldInfo_NoInfo) &&
- !(oldflags & DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType)))
- {
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_Polymorphic);
- }
- }
- if (!Root && operationInfo.isPolymorphic)
- {
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_Polymorphic);
- }
- fldInfoFlags =
- DynamicProfileInfo::MergeFldInfoFlags(
- fldInfoFlags,
- DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType));
- fldInfoFlags =
- DynamicProfileInfo::MergeFldInfoFlags(
- fldInfoFlags,
- DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
- if (!Method)
- {
- UpdateFldInfoFlagsForGetSetInlineCandidate(
- object,
- fldInfoFlags,
- operationInfo.cacheType,
- inlineCache,
- functionBody);
- if (!Root && CallApplyTarget)
- {
- UpdateFldInfoFlagsForCallApplyInlineCandidate(
- object,
- fldInfoFlags,
- operationInfo.cacheType,
- inlineCache,
- functionBody);
- }
- }
- }
- else
- {
- Assert(!Root);
- const auto PatchGetValue = &JavascriptOperators::PatchGetValue<false, InlineCache>;
- const auto PatchGetMethod = &JavascriptOperators::PatchGetMethod<false, InlineCache>;
- const auto PatchGet = Method ? PatchGetMethod : PatchGetValue;
- value = PatchGet(functionBody, inlineCache, inlineCacheIndex, instance, propertyId);
- }
- dynamicProfileInfo->RecordFieldAccess(functionBody, inlineCacheIndex, value, fldInfoFlags);
- return value;
- }
- template<bool Root, bool Method, bool CallApplyTarget>
- Var ProfilingHelpers::ProfiledLdFldForTypeOf(
- const Var instance,
- const PropertyId propertyId,
- InlineCache *const inlineCache,
- const InlineCacheIndex inlineCacheIndex,
- FunctionBody *const functionBody)
- {
- Var val = nullptr;
- ScriptContext *scriptContext = functionBody->GetScriptContext();
- BEGIN_PROFILED_TYPEOF_ERROR_HANDLER(scriptContext);
- val = ProfiledLdFld<Root, Method, CallApplyTarget>(
- instance,
- propertyId,
- inlineCache,
- inlineCacheIndex,
- functionBody,
- instance);
- END_PROFILED_TYPEOF_ERROR_HANDLER(scriptContext, val, functionBody, inlineCacheIndex);
- return val;
- }
- void ProfilingHelpers::ProfiledStFld_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- const Var value,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledStFld);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- ProfiledStFld<false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- value,
- PropertyOperation_None,
- scriptFunction,
- instance);
- JIT_HELPER_END(ProfiledStFld);
- }
- void ProfilingHelpers::ProfiledStSuperFld_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- const Var value,
- void *const framePointer,
- const Var thisInstance)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledStSuperFld);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- ProfiledStFld<false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- value,
- PropertyOperation_None,
- scriptFunction,
- thisInstance);
- JIT_HELPER_END(ProfiledStSuperFld);
- }
- void ProfilingHelpers::ProfiledStFld_Strict_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- const Var value,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledStFld_Strict);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- ProfiledStFld<false>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- value,
- PropertyOperation_StrictMode,
- scriptFunction,
- instance);
- JIT_HELPER_END(ProfiledStFld_Strict);
- }
- void ProfilingHelpers::ProfiledStRootFld_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- const Var value,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledStRootFld);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- ProfiledStFld<true>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- value,
- PropertyOperation_Root,
- scriptFunction,
- instance);
- JIT_HELPER_END(ProfiledStRootFld);
- }
- void ProfilingHelpers::ProfiledStRootFld_Strict_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- const Var value,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledStRootFld_Strict);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- ProfiledStFld<true>(
- instance,
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- value,
- PropertyOperation_StrictModeRoot,
- scriptFunction,
- instance);
- JIT_HELPER_END(ProfiledStRootFld_Strict);
- }
- template<bool Root>
- void ProfilingHelpers::ProfiledStFld(
- const Var instance,
- const PropertyId propertyId,
- InlineCache *const inlineCache,
- const InlineCacheIndex inlineCacheIndex,
- const Var value,
- const PropertyOperationFlags flags,
- ScriptFunction *const scriptFunction,
- const Var thisInstance)
- {
- Assert(instance);
- Assert(thisInstance);
- Assert(propertyId != Constants::NoProperty);
- Assert(inlineCache);
- Assert(scriptFunction);
- FunctionBody *const functionBody = scriptFunction->GetFunctionBody();
- Assert(inlineCacheIndex < functionBody->GetInlineCacheCount());
- Assert(value);
- #if ENABLE_COPYONACCESS_ARRAY
- JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
- #endif
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- FldInfoFlags fldInfoFlags = FldInfo_NoInfo;
- if(Root || (VarIs<RecyclableObject>(instance) && VarIs<RecyclableObject>(thisInstance)))
- {
- RecyclableObject *const object = UnsafeVarTo<RecyclableObject>(instance);
- RecyclableObject *const thisObject = UnsafeVarTo<RecyclableObject>(thisInstance);
- PropertyCacheOperationInfo operationInfo;
- PropertyValueInfo propertyValueInfo;
- PropertyValueInfo::SetCacheInfo(&propertyValueInfo, functionBody, inlineCache, inlineCacheIndex, true);
- if(!CacheOperators::TrySetProperty<true, true, true, true, !Root, true, false, true>(
- object,
- Root,
- propertyId,
- value,
- scriptContext,
- flags,
- &operationInfo,
- &propertyValueInfo))
- {
- ThreadContext* threadContext = scriptContext->GetThreadContext();
- ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
- threadContext->ClearImplicitCallFlags();
- Type *const oldType = object->GetType();
- if (Root)
- {
- JavascriptOperators::PatchPutRootValueNoFastPath(functionBody, inlineCache, inlineCacheIndex, object, propertyId, value, flags);
- }
- else
- {
- JavascriptOperators::PatchPutValueWithThisPtrNoFastPath(functionBody, inlineCache, inlineCacheIndex, object, propertyId, value, thisObject, flags);
- }
- CacheOperators::PretendTrySetProperty<true, false>(
- object->GetType(),
- oldType,
- &operationInfo,
- &propertyValueInfo);
- // Setting to __proto__ property invokes a setter and changes the prototype.So, although PatchPut* populates the cache,
- // the setter invalidates it (since it changes the prototype). PretendTrySetProperty looks at the inline cache type to
- // update the cacheType on PropertyOperationInfo, which is used in populating the field info flags for this operation on
- // the profile. Since the cache was invalidated, we don't get a match with either the type of the object with property or
- // without it and the cacheType defaults to CacheType_None. This leads the profile info to say that this operation doesn't
- // cause an accessor implicit call and JIT then doesn't kill live fields across it and ends up putting a BailOutOnImplicitCalls
- // if there were live fields. This bailout always bails out.
- Js::ImplicitCallFlags accessorCallFlag = (Js::ImplicitCallFlags)(Js::ImplicitCall_Accessor & ~Js::ImplicitCall_None);
- if ((threadContext->GetImplicitCallFlags() & accessorCallFlag) != 0)
- {
- operationInfo.cacheType = CacheType_Setter;
- }
- threadContext->SetImplicitCallFlags((Js::ImplicitCallFlags)(savedImplicitCallFlags | threadContext->GetImplicitCallFlags()));
- }
- // Only make the field polymorphic if we are not using the root object inline cache
- if(operationInfo.isPolymorphic && inlineCacheIndex < functionBody->GetRootObjectStoreInlineCacheStart())
- {
- // should not be a load inline cache
- Assert(inlineCacheIndex < functionBody->GetRootObjectLoadInlineCacheStart());
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_Polymorphic);
- }
- fldInfoFlags =
- DynamicProfileInfo::MergeFldInfoFlags(
- fldInfoFlags,
- DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType));
- fldInfoFlags =
- DynamicProfileInfo::MergeFldInfoFlags(
- fldInfoFlags,
- DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
- UpdateFldInfoFlagsForGetSetInlineCandidate(
- object,
- fldInfoFlags,
- operationInfo.cacheType,
- inlineCache,
- functionBody);
- if(scriptFunction->GetConstructorCache()->NeedsUpdateAfterCtor())
- {
- // This function has only 'this' statements and is being used as a constructor. When the constructor exits, the
- // function object's constructor cache will be updated with the type produced by the constructor. From that
- // point on, when the same function object is used as a constructor, the a new object with the final type will
- // be created. Whatever is stored in the inline cache currently will cause cache misses after the constructor
- // cache update. So, just clear it now so that the caches won't be flagged as polymorphic.
- inlineCache->RemoveFromInvalidationListAndClear(scriptContext->GetThreadContext());
- }
- }
- else
- {
- JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPath<false>(
- functionBody,
- inlineCache,
- inlineCacheIndex,
- instance,
- propertyId,
- value,
- thisInstance,
- flags);
- }
- functionBody->GetDynamicProfileInfo()->RecordFieldAccess(functionBody, inlineCacheIndex, nullptr, fldInfoFlags);
- }
- void ProfilingHelpers::ProfiledInitFld_Jit(
- const Var instance,
- const PropertyId propertyId,
- const InlineCacheIndex inlineCacheIndex,
- const Var value,
- void *const framePointer)
- {
- JIT_HELPER_REENTRANT_HEADER(ProfiledInitFld);
- ScriptFunction *const scriptFunction =
- UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
- ProfiledInitFld(
- VarTo<RecyclableObject>(instance),
- propertyId,
- GetInlineCache(scriptFunction, inlineCacheIndex),
- inlineCacheIndex,
- value,
- scriptFunction->GetFunctionBody());
- JIT_HELPER_END(ProfiledInitFld);
- }
- void ProfilingHelpers::ProfiledInitFld(
- RecyclableObject *const object,
- const PropertyId propertyId,
- InlineCache *const inlineCache,
- const InlineCacheIndex inlineCacheIndex,
- const Var value,
- FunctionBody *const functionBody)
- {
- Assert(object);
- Assert(propertyId != Constants::NoProperty);
- Assert(inlineCache);
- Assert(functionBody);
- Assert(inlineCacheIndex < functionBody->GetInlineCacheCount());
- Assert(value);
- ScriptContext *const scriptContext = functionBody->GetScriptContext();
- FldInfoFlags fldInfoFlags = FldInfo_NoInfo;
- PropertyCacheOperationInfo operationInfo;
- PropertyValueInfo propertyValueInfo;
- PropertyValueInfo::SetCacheInfo(&propertyValueInfo, functionBody, inlineCache, inlineCacheIndex, true);
- if(!CacheOperators::TrySetProperty<true, true, true, true, true, true, false, true>(
- object,
- false,
- propertyId,
- value,
- scriptContext,
- PropertyOperation_None,
- &operationInfo,
- &propertyValueInfo))
- {
- Type *const oldType = object->GetType();
- JavascriptOperators::PatchInitValueNoFastPath(
- functionBody,
- inlineCache,
- inlineCacheIndex,
- object,
- propertyId,
- value);
- CacheOperators::PretendTrySetProperty<true, false>(object->GetType(), oldType, &operationInfo, &propertyValueInfo);
- }
- // Only make the field polymorphic if the we are not using the root object inline cache
- if(operationInfo.isPolymorphic && inlineCacheIndex < functionBody->GetRootObjectStoreInlineCacheStart())
- {
- // should not be a load inline cache
- Assert(inlineCacheIndex < functionBody->GetRootObjectLoadInlineCacheStart());
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_Polymorphic);
- }
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType));
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
- functionBody->GetDynamicProfileInfo()->RecordFieldAccess(functionBody, inlineCacheIndex, nullptr, fldInfoFlags);
- }
- void ProfilingHelpers::UpdateFldInfoFlagsForGetSetInlineCandidate(
- RecyclableObject *const object,
- FldInfoFlags &fldInfoFlags,
- const CacheType cacheType,
- InlineCache *const inlineCache,
- FunctionBody *const functionBody)
- {
- RecyclableObject *callee = nullptr;
- if((cacheType & (CacheType_Getter | CacheType_Setter)) && inlineCache->GetGetterSetter(object, &callee))
- {
- const bool canInline = functionBody->GetDynamicProfileInfo()->RecordLdFldCallSiteInfo(functionBody, callee, false /*callApplyTarget*/);
- if(canInline)
- {
- //updates this fldInfoFlags passed by reference.
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_InlineCandidate);
- }
- }
- }
- void ProfilingHelpers::UpdateFldInfoFlagsForCallApplyInlineCandidate(
- RecyclableObject *const object,
- FldInfoFlags &fldInfoFlags,
- const CacheType cacheType,
- InlineCache *const inlineCache,
- FunctionBody *const functionBody)
- {
- RecyclableObject *callee = nullptr;
- if(!(fldInfoFlags & FldInfo_Polymorphic) && inlineCache->GetCallApplyTarget(object, &callee))
- {
- const bool canInline = functionBody->GetDynamicProfileInfo()->RecordLdFldCallSiteInfo(functionBody, callee, true /*callApplyTarget*/);
- if(canInline)
- {
- //updates the fldInfoFlags passed by reference.
- fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_InlineCandidate);
- }
- }
- }
- InlineCache *ProfilingHelpers::GetInlineCache(ScriptFunction *const scriptFunction, const InlineCacheIndex inlineCacheIndex)
- {
- Assert(scriptFunction);
- Assert(inlineCacheIndex < scriptFunction->GetFunctionBody()->GetInlineCacheCount());
- return
- scriptFunction->GetHasInlineCaches()
- ? UnsafeVarTo<ScriptFunctionWithInlineCache>(scriptFunction)->GetInlineCache(inlineCacheIndex)
- : scriptFunction->GetFunctionBody()->GetInlineCache(inlineCacheIndex);
- }
- #endif
|