ProfilingHelpers.cpp 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLanguagePch.h"
  6. using namespace Js;
  7. #if ENABLE_PROFILE_INFO
  8. Var ProfilingHelpers::ProfiledLdElem(
  9. const Var base,
  10. const Var varIndex,
  11. FunctionBody *const functionBody,
  12. const ProfileId profileId,
  13. bool didArrayAccessHelperCall,
  14. bool bailedOutOnArraySpecialization)
  15. {
  16. JIT_HELPER_REENTRANT_HEADER(ProfiledLdElem);
  17. Assert(base);
  18. Assert(varIndex);
  19. Assert(functionBody);
  20. Assert(profileId != Constants::NoProfileId);
  21. LdElemInfo ldElemInfo;
  22. if (bailedOutOnArraySpecialization)
  23. {
  24. ldElemInfo.disableAggressiveSpecialization = true;
  25. }
  26. // Only enable fast path if the javascript array is not cross site
  27. #if ENABLE_COPYONACCESS_ARRAY
  28. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(base);
  29. #endif
  30. const bool isJsArray = !TaggedNumber::Is(base) && VirtualTableInfo<JavascriptArray>::HasVirtualTable(base);
  31. const bool fastPath = isJsArray;
  32. if(fastPath)
  33. {
  34. JavascriptArray *const array = UnsafeVarTo<JavascriptArray>(base);
  35. ldElemInfo.arrayType = ValueType::FromArray(ObjectType::Array, array, TypeIds_Array).ToLikely();
  36. const Var element = ProfiledLdElem_FastPath(array, varIndex, functionBody->GetScriptContext(), &ldElemInfo);
  37. ldElemInfo.elemType = ldElemInfo.elemType.Merge(element);
  38. functionBody->GetDynamicProfileInfo()->RecordElementLoad(functionBody, profileId, ldElemInfo);
  39. return element;
  40. }
  41. Assert(!isJsArray);
  42. bool isObjectWithArray;
  43. TypeId arrayTypeId;
  44. JavascriptArray *const array =
  45. JavascriptArray::GetArrayForArrayOrObjectWithArray(base, &isObjectWithArray, &arrayTypeId);
  46. if (didArrayAccessHelperCall)
  47. {
  48. ldElemInfo.neededHelperCall = true;
  49. }
  50. do // while(false)
  51. {
  52. // The fast path is only for JavascriptArray and doesn't cover native arrays, objects with internal arrays, or typed
  53. // arrays, but we still need to profile the array
  54. uint32 headSegmentLength;
  55. if(array)
  56. {
  57. ldElemInfo.arrayType =
  58. (
  59. isObjectWithArray
  60. ? ValueType::FromObjectArray(array)
  61. : ValueType::FromArray(ObjectType::Array, array, arrayTypeId)
  62. ).ToLikely();
  63. SparseArraySegmentBase *const head = array->GetHead();
  64. Assert(head->left == 0);
  65. headSegmentLength = head->length;
  66. }
  67. else if(TypedArrayBase::TryGetLengthForOptimizedTypedArray(base, &headSegmentLength, &arrayTypeId))
  68. {
  69. bool isVirtual = (VirtualTableInfoBase::GetVirtualTable(base) == ValueType::GetVirtualTypedArrayVtable(arrayTypeId));
  70. ldElemInfo.arrayType = ValueType::FromTypeId(arrayTypeId, isVirtual).ToLikely();
  71. }
  72. else if(Js::VarIs<Js::RecyclableObject>(base))
  73. {
  74. ldElemInfo.arrayType = ValueType::FromObject(Js::UnsafeVarTo<Js::RecyclableObject>(base)).ToLikely();
  75. break;
  76. }
  77. else
  78. {
  79. break;
  80. }
  81. if(!TaggedInt::Is(varIndex))
  82. {
  83. ldElemInfo.neededHelperCall = true;
  84. break;
  85. }
  86. const int32 index = TaggedInt::ToInt32(varIndex);
  87. const uint32 offset = index;
  88. if(index < 0 || offset >= headSegmentLength || (array && array->IsMissingHeadSegmentItem(offset)))
  89. {
  90. ldElemInfo.neededHelperCall = true;
  91. break;
  92. }
  93. } while(false);
  94. ScriptContext* scriptContext = functionBody->GetScriptContext();
  95. RecyclableObject* cacheOwner;
  96. PropertyRecordUsageCache* propertyRecordUsageCache;
  97. Var element = nullptr;
  98. if (JavascriptOperators::GetPropertyRecordUsageCache(varIndex, scriptContext, &propertyRecordUsageCache, &cacheOwner))
  99. {
  100. PropertyCacheOperationInfo operationInfo;
  101. element = JavascriptOperators::GetElementIWithCache<true /* ReturnOperationInfo */>(base, cacheOwner, propertyRecordUsageCache, scriptContext, &operationInfo);
  102. ldElemInfo.flags = DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType);
  103. ldElemInfo.flags = DynamicProfileInfo::MergeFldInfoFlags(ldElemInfo.flags, DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
  104. }
  105. else
  106. {
  107. element = JavascriptOperators::OP_GetElementI(base, varIndex, scriptContext);
  108. }
  109. const ValueType arrayType(ldElemInfo.GetArrayType());
  110. if(!arrayType.IsUninitialized() || ldElemInfo.flags != Js::FldInfo_NoInfo)
  111. {
  112. if(array && arrayType.IsLikelyObject() && arrayType.GetObjectType() == ObjectType::Array && !arrayType.HasIntElements())
  113. {
  114. JavascriptOperators::UpdateNativeArrayProfileInfoToCreateVarArray(
  115. array,
  116. arrayType.HasFloatElements(),
  117. arrayType.HasVarElements());
  118. }
  119. ldElemInfo.elemType = ValueType::Uninitialized.Merge(element);
  120. functionBody->GetDynamicProfileInfo()->RecordElementLoad(functionBody, profileId, ldElemInfo);
  121. return element;
  122. }
  123. functionBody->GetDynamicProfileInfo()->RecordElementLoadAsProfiled(functionBody, profileId);
  124. return element;
  125. JIT_HELPER_END(ProfiledLdElem);
  126. }
  127. Var ProfilingHelpers::ProfiledLdElem_FastPath(
  128. JavascriptArray *const array,
  129. const Var varIndex,
  130. ScriptContext *const scriptContext,
  131. LdElemInfo *const ldElemInfo)
  132. {
  133. Assert(array);
  134. Assert(varIndex);
  135. Assert(scriptContext);
  136. do // while(false)
  137. {
  138. Assert(!array->IsCrossSiteObject());
  139. if (!TaggedInt::Is(varIndex))
  140. {
  141. break;
  142. }
  143. int32 index = TaggedInt::ToInt32(varIndex);
  144. if (index < 0)
  145. {
  146. break;
  147. }
  148. if(ldElemInfo)
  149. {
  150. SparseArraySegment<Var> *const head = static_cast<SparseArraySegment<Var> *>(array->GetHead());
  151. Assert(head->left == 0);
  152. const uint32 offset = index;
  153. if(offset < head->length)
  154. {
  155. const Var element = head->elements[offset];
  156. if(!SparseArraySegment<Var>::IsMissingItem(&element))
  157. {
  158. // Successful fastpath
  159. return element;
  160. }
  161. }
  162. ldElemInfo->neededHelperCall = true;
  163. }
  164. SparseArraySegment<Var> *seg = (SparseArraySegment<Var>*)array->GetLastUsedSegment();
  165. if ((uint32) index < seg->left)
  166. {
  167. break;
  168. }
  169. uint32 index2 = index - seg->left;
  170. if (index2 < seg->length)
  171. {
  172. Var elem = seg->elements[index2];
  173. if (elem != SparseArraySegment<Var>::GetMissingItem())
  174. {
  175. // Successful fastpath
  176. return elem;
  177. }
  178. }
  179. } while(false);
  180. if(ldElemInfo)
  181. {
  182. ldElemInfo->neededHelperCall = true;
  183. }
  184. return JavascriptOperators::OP_GetElementI(array, varIndex, scriptContext);
  185. }
  186. void ProfilingHelpers::ProfiledStElem_DefaultFlags(
  187. const Var base,
  188. const Var varIndex,
  189. const Var value,
  190. FunctionBody *const functionBody,
  191. const ProfileId profileId)
  192. {
  193. JIT_HELPER_REENTRANT_HEADER(ProfiledStElem_DefaultFlags);
  194. JIT_HELPER_SAME_ATTRIBUTES(ProfiledStElem_DefaultFlags, ProfiledStElem);
  195. ProfiledStElem(base, varIndex, value, functionBody, profileId, PropertyOperation_None, false, false);
  196. JIT_HELPER_END(ProfiledStElem_DefaultFlags);
  197. }
  198. void ProfilingHelpers::ProfiledStElem(
  199. const Var base,
  200. const Var varIndex,
  201. const Var value,
  202. FunctionBody *const functionBody,
  203. const ProfileId profileId,
  204. const PropertyOperationFlags flags,
  205. bool didArrayAccessHelperCall,
  206. bool bailedOutOnArraySpecialization)
  207. {
  208. JIT_HELPER_REENTRANT_HEADER(ProfiledStElem);
  209. Assert(base);
  210. Assert(varIndex);
  211. Assert(value);
  212. Assert(functionBody);
  213. Assert(profileId != Constants::NoProfileId);
  214. StElemInfo stElemInfo;
  215. if (bailedOutOnArraySpecialization)
  216. {
  217. stElemInfo.disableAggressiveSpecialization = true;
  218. }
  219. // Only enable fast path if the javascript array is not cross site
  220. const bool isJsArray = !TaggedNumber::Is(base) && VirtualTableInfo<JavascriptArray>::HasVirtualTable(base);
  221. ScriptContext *const scriptContext = functionBody->GetScriptContext();
  222. const bool fastPath = isJsArray && !JavascriptOperators::SetElementMayHaveImplicitCalls(scriptContext);
  223. if(fastPath)
  224. {
  225. JavascriptArray *const array = UnsafeVarTo<JavascriptArray>(base);
  226. stElemInfo.arrayType = ValueType::FromArray(ObjectType::Array, array, TypeIds_Array).ToLikely();
  227. stElemInfo.createdMissingValue = array->HasNoMissingValues();
  228. ProfiledStElem_FastPath(array, varIndex, value, scriptContext, flags, &stElemInfo);
  229. stElemInfo.createdMissingValue &= !array->HasNoMissingValues();
  230. functionBody->GetDynamicProfileInfo()->RecordElementStore(functionBody, profileId, stElemInfo);
  231. return;
  232. }
  233. JavascriptArray *array;
  234. bool isObjectWithArray;
  235. TypeId arrayTypeId;
  236. if(isJsArray)
  237. {
  238. array = UnsafeVarTo<JavascriptArray>(base);
  239. isObjectWithArray = false;
  240. arrayTypeId = TypeIds_Array;
  241. }
  242. else
  243. {
  244. array = JavascriptArray::GetArrayForArrayOrObjectWithArray(base, &isObjectWithArray, &arrayTypeId);
  245. }
  246. #if ENABLE_COPYONACCESS_ARRAY
  247. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(base);
  248. #endif
  249. do // while(false)
  250. {
  251. // The fast path is only for JavascriptArray and doesn't cover native arrays, objects with internal arrays, or typed
  252. // arrays, but we still need to profile the array
  253. uint32 length;
  254. uint32 headSegmentLength;
  255. if(array)
  256. {
  257. stElemInfo.arrayType =
  258. (
  259. isObjectWithArray
  260. ? ValueType::FromObjectArray(array)
  261. : ValueType::FromArray(ObjectType::Array, array, arrayTypeId)
  262. ).ToLikely();
  263. stElemInfo.createdMissingValue = array->HasNoMissingValues();
  264. length = array->GetLength();
  265. SparseArraySegmentBase *const head = array->GetHead();
  266. Assert(head->left == 0);
  267. headSegmentLength = head->length;
  268. }
  269. else if(TypedArrayBase::TryGetLengthForOptimizedTypedArray(base, &headSegmentLength, &arrayTypeId))
  270. {
  271. length = headSegmentLength;
  272. bool isVirtual = (VirtualTableInfoBase::GetVirtualTable(base) == ValueType::GetVirtualTypedArrayVtable(arrayTypeId));
  273. stElemInfo.arrayType = ValueType::FromTypeId(arrayTypeId, isVirtual).ToLikely();
  274. if (!TaggedNumber::Is(value) && !JavascriptNumber::Is_NoTaggedIntCheck(value))
  275. {
  276. // Non-number stored to a typed array. A helper call will be needed to convert the value.
  277. stElemInfo.neededHelperCall = true;
  278. }
  279. }
  280. else
  281. {
  282. break;
  283. }
  284. if (didArrayAccessHelperCall)
  285. {
  286. stElemInfo.neededHelperCall = true;
  287. }
  288. if(!TaggedInt::Is(varIndex))
  289. {
  290. stElemInfo.neededHelperCall = true;
  291. break;
  292. }
  293. const int32 index = TaggedInt::ToInt32(varIndex);
  294. if(index < 0)
  295. {
  296. stElemInfo.neededHelperCall = true;
  297. break;
  298. }
  299. const uint32 offset = index;
  300. if(offset >= headSegmentLength)
  301. {
  302. stElemInfo.storedOutsideHeadSegmentBounds = true;
  303. if(!isObjectWithArray && offset >= length)
  304. {
  305. stElemInfo.storedOutsideArrayBounds = true;
  306. }
  307. break;
  308. }
  309. if(array && array->IsMissingHeadSegmentItem(offset))
  310. {
  311. stElemInfo.filledMissingValue = true;
  312. }
  313. } while(false);
  314. RecyclableObject* cacheOwner;
  315. PropertyRecordUsageCache* propertyRecordUsageCache;
  316. TypeId instanceType = JavascriptOperators::GetTypeId(base);
  317. bool isTypedArray = (instanceType >= TypeIds_Int8Array && instanceType <= TypeIds_Float64Array);
  318. if (!isTypedArray && JavascriptOperators::GetPropertyRecordUsageCache(varIndex, scriptContext, &propertyRecordUsageCache, &cacheOwner))
  319. {
  320. RecyclableObject* object = nullptr;
  321. bool result = JavascriptOperators::GetPropertyObjectForSetElementI(base, cacheOwner, scriptContext, &object);
  322. Assert(result);
  323. PropertyCacheOperationInfo operationInfo;
  324. JavascriptOperators::SetElementIWithCache<true /* ReturnOperationInfo */>(base, object, cacheOwner, value, propertyRecordUsageCache, scriptContext, flags, &operationInfo);
  325. stElemInfo.flags = DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType);
  326. stElemInfo.flags = DynamicProfileInfo::MergeFldInfoFlags(stElemInfo.flags, DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
  327. }
  328. else
  329. {
  330. JavascriptOperators::OP_SetElementI(base, varIndex, value, scriptContext, flags);
  331. }
  332. if(!stElemInfo.GetArrayType().IsUninitialized() || stElemInfo.flags != Js::FldInfo_NoInfo)
  333. {
  334. if(array)
  335. {
  336. stElemInfo.createdMissingValue &= !array->HasNoMissingValues();
  337. }
  338. functionBody->GetDynamicProfileInfo()->RecordElementStore(functionBody, profileId, stElemInfo);
  339. return;
  340. }
  341. functionBody->GetDynamicProfileInfo()->RecordElementStoreAsProfiled(functionBody, profileId);
  342. JIT_HELPER_END(ProfiledStElem);
  343. }
  344. void ProfilingHelpers::ProfiledStElem_FastPath(
  345. JavascriptArray *const array,
  346. const Var varIndex,
  347. const Var value,
  348. ScriptContext *const scriptContext,
  349. const PropertyOperationFlags flags,
  350. StElemInfo *const stElemInfo)
  351. {
  352. Assert(array);
  353. Assert(varIndex);
  354. Assert(value);
  355. Assert(scriptContext);
  356. Assert(!JavascriptOperators::SetElementMayHaveImplicitCalls(scriptContext));
  357. do // while(false)
  358. {
  359. if (!TaggedInt::Is(varIndex))
  360. {
  361. break;
  362. }
  363. int32 index = TaggedInt::ToInt32(varIndex);
  364. if (index < 0)
  365. {
  366. break;
  367. }
  368. if(stElemInfo)
  369. {
  370. SparseArraySegmentBase *const head = array->GetHead();
  371. Assert(head->left == 0);
  372. const uint32 offset = index;
  373. if(offset >= head->length)
  374. {
  375. stElemInfo->storedOutsideHeadSegmentBounds = true;
  376. if(offset >= array->GetLength())
  377. {
  378. stElemInfo->storedOutsideArrayBounds = true;
  379. }
  380. }
  381. if(offset < head->size)
  382. {
  383. array->DirectProfiledSetItemInHeadSegmentAt(offset, value, stElemInfo);
  384. return;
  385. }
  386. }
  387. SparseArraySegment<Var>* lastUsedSeg = (SparseArraySegment<Var>*)array->GetLastUsedSegment();
  388. if (lastUsedSeg == NULL ||
  389. (uint32) index < lastUsedSeg->left)
  390. {
  391. break;
  392. }
  393. uint32 index2 = index - lastUsedSeg->left;
  394. if (index2 < lastUsedSeg->size)
  395. {
  396. // Successful fastpath
  397. array->DirectSetItemInLastUsedSegmentAt(index2, value);
  398. return;
  399. }
  400. } while(false);
  401. if(stElemInfo)
  402. {
  403. stElemInfo->neededHelperCall = true;
  404. }
  405. JavascriptOperators::OP_SetElementI(array, varIndex, value, scriptContext, flags);
  406. }
  407. JavascriptArray *ProfilingHelpers::ProfiledNewScArray(
  408. const uint length,
  409. FunctionBody *const functionBody,
  410. const ProfileId profileId)
  411. {
  412. JIT_HELPER_REENTRANT_HEADER(ProfiledNewScArray);
  413. Assert(functionBody);
  414. Assert(profileId != Constants::NoProfileId);
  415. // Not creating native array here if the function is unoptimized, because it turns out to be tricky to
  416. // get the initialization right if GlobOpt doesn't give us bailout. It's possible, but we should see
  417. // a use case before spending time on it.
  418. ArrayCallSiteInfo *const arrayInfo =
  419. functionBody->GetDynamicProfileInfo()->GetArrayCallSiteInfo(functionBody, profileId);
  420. Assert(arrayInfo);
  421. if (length > SparseArraySegmentBase::INLINE_CHUNK_SIZE || (functionBody->GetHasTry() && PHASE_OFF((Js::OptimizeTryCatchPhase), functionBody)))
  422. {
  423. arrayInfo->SetIsNotNativeArray();
  424. }
  425. ScriptContext *const scriptContext = functionBody->GetScriptContext();
  426. JavascriptArray *array;
  427. if (arrayInfo->IsNativeIntArray())
  428. {
  429. JavascriptNativeIntArray *const intArray = scriptContext->GetLibrary()->CreateNativeIntArrayLiteral(length);
  430. Recycler *recycler = scriptContext->GetRecycler();
  431. intArray->SetArrayCallSite(profileId, recycler->CreateWeakReferenceHandle(functionBody));
  432. array = intArray;
  433. }
  434. else if (arrayInfo->IsNativeFloatArray())
  435. {
  436. JavascriptNativeFloatArray *const floatArray = scriptContext->GetLibrary()->CreateNativeFloatArrayLiteral(length);
  437. Recycler *recycler = scriptContext->GetRecycler();
  438. floatArray->SetArrayCallSite(profileId, recycler->CreateWeakReferenceHandle(functionBody));
  439. array = floatArray;
  440. }
  441. else
  442. {
  443. array = scriptContext->GetLibrary()->CreateArrayLiteral(length);
  444. }
  445. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  446. array->CheckForceES5Array();
  447. #endif
  448. return array;
  449. JIT_HELPER_END(ProfiledNewScArray);
  450. }
  451. Var ProfilingHelpers::ProfiledNewScObjArray_Jit(
  452. const Var callee,
  453. void *const framePointer,
  454. const ProfileId profileId,
  455. const ProfileId arrayProfileId,
  456. CallInfo callInfo,
  457. ...)
  458. {
  459. ARGUMENTS(args, callee, framePointer, profileId, arrayProfileId, callInfo);
  460. ScriptFunction* func = UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  461. JIT_HELPER_REENTRANT_HEADER(ProfiledNewScObjArray);
  462. return
  463. ProfiledNewScObjArray(
  464. callee,
  465. args,
  466. func,
  467. profileId,
  468. arrayProfileId);
  469. JIT_HELPER_END(ProfiledNewScObjArray);
  470. }
  471. Var ProfilingHelpers::ProfiledNewScObjArraySpread_Jit(
  472. const Js::AuxArray<uint32> *spreadIndices,
  473. const Var callee,
  474. void *const framePointer,
  475. const ProfileId profileId,
  476. const ProfileId arrayProfileId,
  477. CallInfo callInfo,
  478. ...)
  479. {
  480. ARGUMENTS(args, spreadIndices, callee, framePointer, profileId, arrayProfileId, callInfo);
  481. Js::ScriptFunction *function = UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  482. ScriptContext* scriptContext = function->GetScriptContext();
  483. JIT_HELPER_REENTRANT_HEADER(ProfiledNewScObjArraySpread);
  484. // GetSpreadSize ensures that spreadSize < 2^24
  485. uint32 spreadSize = 0;
  486. if (spreadIndices != nullptr)
  487. {
  488. Arguments outArgs(CallInfo(args.Info.Flags, 0), nullptr);
  489. spreadSize = JavascriptFunction::GetSpreadSize(args, spreadIndices, scriptContext);
  490. Assert(spreadSize == (((1 << 24) - 1) & spreadSize));
  491. // Allocate room on the stack for the spread args.
  492. outArgs.Info.Count = spreadSize;
  493. const unsigned STACK_ARGS_ALLOCA_THRESHOLD = 8; // Number of stack args we allow before using _alloca
  494. Var stackArgs[STACK_ARGS_ALLOCA_THRESHOLD];
  495. size_t outArgsSize = 0;
  496. if (outArgs.Info.Count > STACK_ARGS_ALLOCA_THRESHOLD)
  497. {
  498. PROBE_STACK(scriptContext, outArgs.Info.Count * sizeof(Var) + Js::Constants::MinStackDefault); // args + function call
  499. outArgsSize = outArgs.Info.Count * sizeof(Var);
  500. outArgs.Values = (Var*)_alloca(outArgsSize);
  501. ZeroMemory(outArgs.Values, outArgsSize);
  502. }
  503. else
  504. {
  505. outArgs.Values = stackArgs;
  506. outArgsSize = STACK_ARGS_ALLOCA_THRESHOLD * sizeof(Var);
  507. ZeroMemory(outArgs.Values, outArgsSize); // We may not use all of the elements
  508. }
  509. JavascriptFunction::SpreadArgs(args, outArgs, spreadIndices, scriptContext);
  510. return
  511. ProfiledNewScObjArray(
  512. callee,
  513. outArgs,
  514. function,
  515. profileId,
  516. arrayProfileId);
  517. }
  518. else
  519. {
  520. return
  521. ProfiledNewScObjArray(
  522. callee,
  523. args,
  524. function,
  525. profileId,
  526. arrayProfileId);
  527. }
  528. JIT_HELPER_END(ProfiledNewScObjArraySpread);
  529. }
  530. Var ProfilingHelpers::ProfiledNewScObjArray(
  531. const Var callee,
  532. const Arguments args,
  533. ScriptFunction *const caller,
  534. const ProfileId profileId,
  535. const ProfileId arrayProfileId)
  536. {
  537. Assert(callee);
  538. Assert(args.Info.Count != 0);
  539. Assert(caller);
  540. Assert(profileId != Constants::NoProfileId);
  541. Assert(arrayProfileId != Constants::NoProfileId);
  542. FunctionBody *const callerFunctionBody = caller->GetFunctionBody();
  543. DynamicProfileInfo *const profileInfo = callerFunctionBody->GetDynamicProfileInfo();
  544. ArrayCallSiteInfo *const arrayInfo = profileInfo->GetArrayCallSiteInfo(callerFunctionBody, arrayProfileId);
  545. Assert(arrayInfo);
  546. ScriptContext *const scriptContext = callerFunctionBody->GetScriptContext();
  547. FunctionInfo *const calleeFunctionInfo = JavascriptOperators::GetConstructorFunctionInfo(callee, scriptContext);
  548. if (calleeFunctionInfo != &JavascriptArray::EntryInfo::NewInstance)
  549. {
  550. // It may be worth checking the object that we actually got back from the ctor, but
  551. // we should at least not keep bailing out at this call site.
  552. arrayInfo->SetIsNotNativeArray();
  553. return ProfiledNewScObject(callee, args, callerFunctionBody, profileId);
  554. }
  555. profileInfo->RecordCallSiteInfo(
  556. callerFunctionBody,
  557. profileId,
  558. calleeFunctionInfo,
  559. caller,
  560. args.Info.Count,
  561. true);
  562. args.Values[0] = nullptr;
  563. Var array;
  564. Js::RecyclableObject* calleeObject = UnsafeVarTo<RecyclableObject>(callee);
  565. if (arrayInfo->IsNativeIntArray())
  566. {
  567. array = JavascriptNativeIntArray::NewInstance(calleeObject, args);
  568. if (VirtualTableInfo<JavascriptNativeIntArray>::HasVirtualTable(array))
  569. {
  570. JavascriptNativeIntArray *const intArray = static_cast<JavascriptNativeIntArray *>(array);
  571. intArray->SetArrayCallSite(arrayProfileId, scriptContext->GetRecycler()->CreateWeakReferenceHandle(callerFunctionBody));
  572. }
  573. else
  574. {
  575. arrayInfo->SetIsNotNativeIntArray();
  576. if (VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(array))
  577. {
  578. JavascriptNativeFloatArray *const floatArray = static_cast<JavascriptNativeFloatArray *>(array);
  579. floatArray->SetArrayCallSite(arrayProfileId, scriptContext->GetRecycler()->CreateWeakReferenceHandle(callerFunctionBody));
  580. }
  581. else
  582. {
  583. arrayInfo->SetIsNotNativeArray();
  584. }
  585. }
  586. }
  587. else if (arrayInfo->IsNativeFloatArray())
  588. {
  589. array = JavascriptNativeFloatArray::NewInstance(calleeObject, args);
  590. if (VirtualTableInfo<JavascriptNativeFloatArray>::HasVirtualTable(array))
  591. {
  592. JavascriptNativeFloatArray *const floatArray = static_cast<JavascriptNativeFloatArray *>(array);
  593. floatArray->SetArrayCallSite(arrayProfileId, scriptContext->GetRecycler()->CreateWeakReferenceHandle(callerFunctionBody));
  594. }
  595. else
  596. {
  597. arrayInfo->SetIsNotNativeArray();
  598. }
  599. }
  600. else
  601. {
  602. array = JavascriptArray::NewInstance(calleeObject, args);
  603. }
  604. return CrossSite::MarshalVar(scriptContext, array, calleeObject->GetScriptContext());
  605. }
  606. Var ProfilingHelpers::ProfiledNewScObject(
  607. const Var callee,
  608. const Arguments args,
  609. FunctionBody *const callerFunctionBody,
  610. const ProfileId profileId,
  611. const InlineCacheIndex inlineCacheIndex,
  612. const Js::AuxArray<uint32> *spreadIndices)
  613. {
  614. Assert(callee);
  615. Assert(args.Info.Count != 0);
  616. Assert(callerFunctionBody);
  617. Assert(profileId != Constants::NoProfileId);
  618. ScriptContext *const scriptContext = callerFunctionBody->GetScriptContext();
  619. if(!TaggedNumber::Is(callee))
  620. {
  621. const auto calleeObject = JavascriptOperators::GetCallableObjectOrThrow(callee, scriptContext);
  622. const auto calleeFunctionInfo =
  623. calleeObject->GetTypeId() == TypeIds_Function
  624. ? UnsafeVarTo<JavascriptFunction>(calleeObject)->GetFunctionInfo()
  625. : nullptr;
  626. DynamicProfileInfo *profileInfo = callerFunctionBody->GetDynamicProfileInfo();
  627. profileInfo->RecordCallSiteInfo(
  628. callerFunctionBody,
  629. profileId,
  630. calleeFunctionInfo,
  631. calleeFunctionInfo ? static_cast<JavascriptFunction *>(calleeObject) : nullptr,
  632. args.Info.Count,
  633. true,
  634. inlineCacheIndex);
  635. // We need to record information here, most importantly so that we handle array subclass
  636. // creation properly, since optimizing those cases is important
  637. Var retVal = nullptr;
  638. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  639. {
  640. retVal = JavascriptOperators::NewScObject(callee, args, scriptContext, spreadIndices);
  641. }
  642. END_SAFE_REENTRANT_CALL
  643. profileInfo->RecordReturnTypeOnCallSiteInfo(callerFunctionBody, profileId, retVal);
  644. return retVal;
  645. }
  646. BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
  647. {
  648. return JavascriptOperators::NewScObject(callee, args, scriptContext, spreadIndices);
  649. }
  650. END_SAFE_REENTRANT_CALL
  651. }
  652. void ProfilingHelpers::ProfileLdSlot(const Var value, FunctionBody *const functionBody, const ProfileId profileId)
  653. {
  654. JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ProfileLdSlot);
  655. Assert(value);
  656. Assert(functionBody);
  657. Assert(profileId != Constants::NoProfileId);
  658. functionBody->GetDynamicProfileInfo()->RecordSlotLoad(functionBody, profileId, value);
  659. JIT_HELPER_END(ProfileLdSlot);
  660. }
  661. Var ProfilingHelpers::ProfiledLdLen_Jit(
  662. const Var instance,
  663. const PropertyId propertyId,
  664. const InlineCacheIndex inlineCacheIndex,
  665. const ProfileId profileId,
  666. void *const framePointer)
  667. {
  668. JIT_HELPER_REENTRANT_HEADER(ProfiledLdLen);
  669. ScriptFunction * const scriptFunction = UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  670. FunctionBody * functionBody = scriptFunction->GetFunctionBody();
  671. DynamicProfileInfo * profileInfo = functionBody->GetDynamicProfileInfo();
  672. LdLenInfo ldLenInfo;
  673. ldLenInfo.arrayType = ValueType::Uninitialized.Merge(instance);
  674. profileInfo->RecordLengthLoad(functionBody, profileId, ldLenInfo);
  675. return
  676. ProfiledLdFld<false, false, false>(
  677. instance,
  678. propertyId,
  679. GetInlineCache(scriptFunction, inlineCacheIndex),
  680. inlineCacheIndex,
  681. scriptFunction->GetFunctionBody(),
  682. instance);
  683. JIT_HELPER_END(ProfiledLdLen);
  684. }
  685. Var ProfilingHelpers::ProfiledLdFld_Jit(
  686. const Var instance,
  687. const PropertyId propertyId,
  688. const InlineCacheIndex inlineCacheIndex,
  689. void *const framePointer)
  690. {
  691. JIT_HELPER_REENTRANT_HEADER(ProfiledLdFld);
  692. ScriptFunction *const scriptFunction =
  693. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  694. return
  695. ProfiledLdFld<false, false, false>(
  696. instance,
  697. propertyId,
  698. GetInlineCache(scriptFunction, inlineCacheIndex),
  699. inlineCacheIndex,
  700. scriptFunction->GetFunctionBody(),
  701. instance);
  702. JIT_HELPER_END(ProfiledLdFld);
  703. }
  704. Var ProfilingHelpers::ProfiledLdSuperFld_Jit(
  705. const Var instance,
  706. const PropertyId propertyId,
  707. const InlineCacheIndex inlineCacheIndex,
  708. void *const framePointer,
  709. const Var thisInstance)
  710. {
  711. JIT_HELPER_REENTRANT_HEADER(ProfiledLdSuperFld);
  712. ScriptFunction *const scriptFunction =
  713. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  714. return
  715. ProfiledLdFld<false, false, false>(
  716. instance,
  717. propertyId,
  718. GetInlineCache(scriptFunction, inlineCacheIndex),
  719. inlineCacheIndex,
  720. scriptFunction->GetFunctionBody(),
  721. thisInstance);
  722. JIT_HELPER_END(ProfiledLdSuperFld);
  723. }
  724. Var ProfilingHelpers::ProfiledLdFldForTypeOf_Jit(
  725. const Var instance,
  726. const PropertyId propertyId,
  727. const InlineCacheIndex inlineCacheIndex,
  728. void *const framePointer)
  729. {
  730. JIT_HELPER_REENTRANT_HEADER(ProfiledLdFldForTypeOf);
  731. ScriptFunction *const scriptFunction =
  732. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  733. return ProfiledLdFldForTypeOf<false, false, false>(
  734. instance,
  735. propertyId,
  736. GetInlineCache(scriptFunction, inlineCacheIndex),
  737. inlineCacheIndex,
  738. scriptFunction->GetFunctionBody());
  739. JIT_HELPER_END(ProfiledLdFldForTypeOf);
  740. }
  741. Var ProfilingHelpers::ProfiledLdFld_CallApplyTarget_Jit(
  742. const Var instance,
  743. const PropertyId propertyId,
  744. const InlineCacheIndex inlineCacheIndex,
  745. void *const framePointer)
  746. {
  747. JIT_HELPER_REENTRANT_HEADER(ProfiledLdFld_CallApplyTarget);
  748. ScriptFunction *const scriptFunction =
  749. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  750. return
  751. ProfiledLdFld<false, false, true>(
  752. instance,
  753. propertyId,
  754. GetInlineCache(scriptFunction, inlineCacheIndex),
  755. inlineCacheIndex,
  756. scriptFunction->GetFunctionBody(),
  757. instance);
  758. JIT_HELPER_END(ProfiledLdFld_CallApplyTarget);
  759. }
  760. Var ProfilingHelpers::ProfiledLdMethodFld_Jit(
  761. const Var instance,
  762. const PropertyId propertyId,
  763. const InlineCacheIndex inlineCacheIndex,
  764. void *const framePointer)
  765. {
  766. JIT_HELPER_REENTRANT_HEADER(ProfiledLdMethodFld);
  767. ScriptFunction *const scriptFunction =
  768. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  769. return
  770. ProfiledLdFld<false, true, false>(
  771. instance,
  772. propertyId,
  773. GetInlineCache(scriptFunction, inlineCacheIndex),
  774. inlineCacheIndex,
  775. scriptFunction->GetFunctionBody(),
  776. instance);
  777. JIT_HELPER_END(ProfiledLdMethodFld);
  778. }
  779. Var ProfilingHelpers::ProfiledLdRootFld_Jit(
  780. const Var instance,
  781. const PropertyId propertyId,
  782. const InlineCacheIndex inlineCacheIndex,
  783. void *const framePointer)
  784. {
  785. JIT_HELPER_REENTRANT_HEADER(ProfiledLdRootFld);
  786. ScriptFunction *const scriptFunction =
  787. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  788. return
  789. ProfiledLdFld<true, false, false>(
  790. instance,
  791. propertyId,
  792. GetInlineCache(scriptFunction, inlineCacheIndex),
  793. inlineCacheIndex,
  794. scriptFunction->GetFunctionBody(),
  795. instance);
  796. JIT_HELPER_END(ProfiledLdRootFld);
  797. }
  798. Var ProfilingHelpers::ProfiledLdRootFldForTypeOf_Jit(
  799. const Var instance,
  800. const PropertyId propertyId,
  801. const InlineCacheIndex inlineCacheIndex,
  802. void *const framePointer)
  803. {
  804. JIT_HELPER_REENTRANT_HEADER(ProfiledLdRootFldForTypeOf);
  805. ScriptFunction *const scriptFunction =
  806. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  807. return ProfiledLdFldForTypeOf<true, false, false>(
  808. instance,
  809. propertyId,
  810. GetInlineCache(scriptFunction, inlineCacheIndex),
  811. inlineCacheIndex,
  812. scriptFunction->GetFunctionBody());
  813. JIT_HELPER_END(ProfiledLdRootFldForTypeOf);
  814. }
  815. Var ProfilingHelpers::ProfiledLdRootMethodFld_Jit(
  816. const Var instance,
  817. const PropertyId propertyId,
  818. const InlineCacheIndex inlineCacheIndex,
  819. void *const framePointer)
  820. {
  821. JIT_HELPER_REENTRANT_HEADER(ProfiledLdRootMethodFld);
  822. ScriptFunction *const scriptFunction =
  823. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  824. return
  825. ProfiledLdFld<true, true, false>(
  826. instance,
  827. propertyId,
  828. GetInlineCache(scriptFunction, inlineCacheIndex),
  829. inlineCacheIndex,
  830. scriptFunction->GetFunctionBody(),
  831. instance);
  832. JIT_HELPER_END(ProfiledLdRootMethodFld);
  833. }
  834. template<bool Root, bool Method, bool CallApplyTarget>
  835. Var ProfilingHelpers::ProfiledLdFld(
  836. const Var instance,
  837. const PropertyId propertyId,
  838. InlineCache *const inlineCache,
  839. const InlineCacheIndex inlineCacheIndex,
  840. FunctionBody *const functionBody,
  841. const Var thisInstance)
  842. {
  843. Assert(instance);
  844. Assert(thisInstance);
  845. Assert(propertyId != Constants::NoProperty);
  846. Assert(inlineCache);
  847. Assert(functionBody);
  848. Assert(inlineCacheIndex < functionBody->GetInlineCacheCount());
  849. Assert(!Root || inlineCacheIndex >= functionBody->GetRootObjectLoadInlineCacheStart());
  850. #if ENABLE_COPYONACCESS_ARRAY
  851. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
  852. #endif
  853. ScriptContext *const scriptContext = functionBody->GetScriptContext();
  854. DynamicProfileInfo *const dynamicProfileInfo = functionBody->GetDynamicProfileInfo();
  855. Var value;
  856. FldInfoFlags fldInfoFlags = FldInfo_NoInfo;
  857. if (Root || (VarIs<RecyclableObject>(instance) && VarIs<RecyclableObject>(thisInstance)))
  858. {
  859. RecyclableObject *const object = UnsafeVarTo<RecyclableObject>(instance);
  860. RecyclableObject *const thisObject = UnsafeVarTo<RecyclableObject>(thisInstance);
  861. if (!Root && Method && (propertyId == PropertyIds::apply || propertyId == PropertyIds::call) && VarIs<ScriptFunction>(object))
  862. {
  863. // If the property being loaded is "apply"/"call", make an optimistic assumption that apply/call is not overridden and
  864. // undefer the function right here if it was defer parsed before. This is required so that the load of "apply"/"call"
  865. // happens from the same "type". Otherwise, we will have a polymorphic cache for load of "apply"/"call".
  866. ScriptFunction *fn = UnsafeVarTo<ScriptFunction>(object);
  867. if (fn->GetType()->GetEntryPoint() == JavascriptFunction::DeferredParsingThunk)
  868. {
  869. JavascriptFunction::DeferredParse(&fn);
  870. }
  871. }
  872. PropertyCacheOperationInfo operationInfo;
  873. PropertyValueInfo propertyValueInfo;
  874. PropertyValueInfo::SetCacheInfo(&propertyValueInfo, functionBody, inlineCache, inlineCacheIndex, true);
  875. if (!CacheOperators::TryGetProperty<true, true, true, !Root && !Method, true, !Root, true, false, true, false>(
  876. object,
  877. Root,
  878. object,
  879. propertyId,
  880. &value,
  881. scriptContext,
  882. &operationInfo,
  883. &propertyValueInfo))
  884. {
  885. const auto PatchGetValue = &JavascriptOperators::PatchGetValueWithThisPtrNoFastPath;
  886. const auto PatchGetRootValue = &JavascriptOperators::PatchGetRootValueNoFastPath_Var;
  887. const auto PatchGetMethod = &JavascriptOperators::PatchGetMethodNoFastPath;
  888. const auto PatchGetRootMethod = &JavascriptOperators::PatchGetRootMethodNoFastPath_Var;
  889. const auto PatchGet =
  890. Root
  891. ? Method ? PatchGetRootMethod : PatchGetRootValue
  892. : PatchGetMethod ;
  893. value = (!Root && !Method) ? PatchGetValue(functionBody, inlineCache, inlineCacheIndex, object, propertyId, thisObject) :
  894. PatchGet(functionBody, inlineCache, inlineCacheIndex, object, propertyId);
  895. CacheOperators::PretendTryGetProperty<true, false>(object->GetType(), &operationInfo, &propertyValueInfo);
  896. }
  897. else if (!Root && !Method)
  898. {
  899. // Inline cache hit. oldflags must match the new ones. If not there is mark it as polymorphic as there is likely
  900. // a bailout to interpreter and change in the inline cache type.
  901. const FldInfoFlags oldflags = dynamicProfileInfo->GetFldInfo(functionBody, inlineCacheIndex)->flags;
  902. if ((oldflags != FldInfo_NoInfo) &&
  903. !(oldflags & DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType)))
  904. {
  905. fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_Polymorphic);
  906. }
  907. }
  908. if (!Root && operationInfo.isPolymorphic)
  909. {
  910. fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_Polymorphic);
  911. }
  912. fldInfoFlags =
  913. DynamicProfileInfo::MergeFldInfoFlags(
  914. fldInfoFlags,
  915. DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType));
  916. fldInfoFlags =
  917. DynamicProfileInfo::MergeFldInfoFlags(
  918. fldInfoFlags,
  919. DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
  920. if (!Method)
  921. {
  922. UpdateFldInfoFlagsForGetSetInlineCandidate(
  923. object,
  924. fldInfoFlags,
  925. operationInfo.cacheType,
  926. inlineCache,
  927. functionBody);
  928. if (!Root && CallApplyTarget)
  929. {
  930. UpdateFldInfoFlagsForCallApplyInlineCandidate(
  931. object,
  932. fldInfoFlags,
  933. operationInfo.cacheType,
  934. inlineCache,
  935. functionBody);
  936. }
  937. }
  938. }
  939. else
  940. {
  941. Assert(!Root);
  942. const auto PatchGetValue = &JavascriptOperators::PatchGetValue<false, InlineCache>;
  943. const auto PatchGetMethod = &JavascriptOperators::PatchGetMethod<false, InlineCache>;
  944. const auto PatchGet = Method ? PatchGetMethod : PatchGetValue;
  945. value = PatchGet(functionBody, inlineCache, inlineCacheIndex, instance, propertyId);
  946. }
  947. dynamicProfileInfo->RecordFieldAccess(functionBody, inlineCacheIndex, value, fldInfoFlags);
  948. return value;
  949. }
  950. template<bool Root, bool Method, bool CallApplyTarget>
  951. Var ProfilingHelpers::ProfiledLdFldForTypeOf(
  952. const Var instance,
  953. const PropertyId propertyId,
  954. InlineCache *const inlineCache,
  955. const InlineCacheIndex inlineCacheIndex,
  956. FunctionBody *const functionBody)
  957. {
  958. Var val = nullptr;
  959. ScriptContext *scriptContext = functionBody->GetScriptContext();
  960. BEGIN_PROFILED_TYPEOF_ERROR_HANDLER(scriptContext);
  961. val = ProfiledLdFld<Root, Method, CallApplyTarget>(
  962. instance,
  963. propertyId,
  964. inlineCache,
  965. inlineCacheIndex,
  966. functionBody,
  967. instance);
  968. END_PROFILED_TYPEOF_ERROR_HANDLER(scriptContext, val, functionBody, inlineCacheIndex);
  969. return val;
  970. }
  971. void ProfilingHelpers::ProfiledStFld_Jit(
  972. const Var instance,
  973. const PropertyId propertyId,
  974. const InlineCacheIndex inlineCacheIndex,
  975. const Var value,
  976. void *const framePointer)
  977. {
  978. JIT_HELPER_REENTRANT_HEADER(ProfiledStFld);
  979. ScriptFunction *const scriptFunction =
  980. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  981. ProfiledStFld<false>(
  982. instance,
  983. propertyId,
  984. GetInlineCache(scriptFunction, inlineCacheIndex),
  985. inlineCacheIndex,
  986. value,
  987. PropertyOperation_None,
  988. scriptFunction,
  989. instance);
  990. JIT_HELPER_END(ProfiledStFld);
  991. }
  992. void ProfilingHelpers::ProfiledStSuperFld_Jit(
  993. const Var instance,
  994. const PropertyId propertyId,
  995. const InlineCacheIndex inlineCacheIndex,
  996. const Var value,
  997. void *const framePointer,
  998. const Var thisInstance)
  999. {
  1000. JIT_HELPER_REENTRANT_HEADER(ProfiledStSuperFld);
  1001. ScriptFunction *const scriptFunction =
  1002. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  1003. ProfiledStFld<false>(
  1004. instance,
  1005. propertyId,
  1006. GetInlineCache(scriptFunction, inlineCacheIndex),
  1007. inlineCacheIndex,
  1008. value,
  1009. PropertyOperation_None,
  1010. scriptFunction,
  1011. thisInstance);
  1012. JIT_HELPER_END(ProfiledStSuperFld);
  1013. }
  1014. void ProfilingHelpers::ProfiledStFld_Strict_Jit(
  1015. const Var instance,
  1016. const PropertyId propertyId,
  1017. const InlineCacheIndex inlineCacheIndex,
  1018. const Var value,
  1019. void *const framePointer)
  1020. {
  1021. JIT_HELPER_REENTRANT_HEADER(ProfiledStFld_Strict);
  1022. ScriptFunction *const scriptFunction =
  1023. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  1024. ProfiledStFld<false>(
  1025. instance,
  1026. propertyId,
  1027. GetInlineCache(scriptFunction, inlineCacheIndex),
  1028. inlineCacheIndex,
  1029. value,
  1030. PropertyOperation_StrictMode,
  1031. scriptFunction,
  1032. instance);
  1033. JIT_HELPER_END(ProfiledStFld_Strict);
  1034. }
  1035. void ProfilingHelpers::ProfiledStRootFld_Jit(
  1036. const Var instance,
  1037. const PropertyId propertyId,
  1038. const InlineCacheIndex inlineCacheIndex,
  1039. const Var value,
  1040. void *const framePointer)
  1041. {
  1042. JIT_HELPER_REENTRANT_HEADER(ProfiledStRootFld);
  1043. ScriptFunction *const scriptFunction =
  1044. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  1045. ProfiledStFld<true>(
  1046. instance,
  1047. propertyId,
  1048. GetInlineCache(scriptFunction, inlineCacheIndex),
  1049. inlineCacheIndex,
  1050. value,
  1051. PropertyOperation_Root,
  1052. scriptFunction,
  1053. instance);
  1054. JIT_HELPER_END(ProfiledStRootFld);
  1055. }
  1056. void ProfilingHelpers::ProfiledStRootFld_Strict_Jit(
  1057. const Var instance,
  1058. const PropertyId propertyId,
  1059. const InlineCacheIndex inlineCacheIndex,
  1060. const Var value,
  1061. void *const framePointer)
  1062. {
  1063. JIT_HELPER_REENTRANT_HEADER(ProfiledStRootFld_Strict);
  1064. ScriptFunction *const scriptFunction =
  1065. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  1066. ProfiledStFld<true>(
  1067. instance,
  1068. propertyId,
  1069. GetInlineCache(scriptFunction, inlineCacheIndex),
  1070. inlineCacheIndex,
  1071. value,
  1072. PropertyOperation_StrictModeRoot,
  1073. scriptFunction,
  1074. instance);
  1075. JIT_HELPER_END(ProfiledStRootFld_Strict);
  1076. }
  1077. template<bool Root>
  1078. void ProfilingHelpers::ProfiledStFld(
  1079. const Var instance,
  1080. const PropertyId propertyId,
  1081. InlineCache *const inlineCache,
  1082. const InlineCacheIndex inlineCacheIndex,
  1083. const Var value,
  1084. const PropertyOperationFlags flags,
  1085. ScriptFunction *const scriptFunction,
  1086. const Var thisInstance)
  1087. {
  1088. Assert(instance);
  1089. Assert(thisInstance);
  1090. Assert(propertyId != Constants::NoProperty);
  1091. Assert(inlineCache);
  1092. Assert(scriptFunction);
  1093. FunctionBody *const functionBody = scriptFunction->GetFunctionBody();
  1094. Assert(inlineCacheIndex < functionBody->GetInlineCacheCount());
  1095. Assert(value);
  1096. #if ENABLE_COPYONACCESS_ARRAY
  1097. JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
  1098. #endif
  1099. ScriptContext *const scriptContext = functionBody->GetScriptContext();
  1100. FldInfoFlags fldInfoFlags = FldInfo_NoInfo;
  1101. if(Root || (VarIs<RecyclableObject>(instance) && VarIs<RecyclableObject>(thisInstance)))
  1102. {
  1103. RecyclableObject *const object = UnsafeVarTo<RecyclableObject>(instance);
  1104. RecyclableObject *const thisObject = UnsafeVarTo<RecyclableObject>(thisInstance);
  1105. PropertyCacheOperationInfo operationInfo;
  1106. PropertyValueInfo propertyValueInfo;
  1107. PropertyValueInfo::SetCacheInfo(&propertyValueInfo, functionBody, inlineCache, inlineCacheIndex, true);
  1108. if(!CacheOperators::TrySetProperty<true, true, true, true, !Root, true, false, true>(
  1109. object,
  1110. Root,
  1111. propertyId,
  1112. value,
  1113. scriptContext,
  1114. flags,
  1115. &operationInfo,
  1116. &propertyValueInfo))
  1117. {
  1118. ThreadContext* threadContext = scriptContext->GetThreadContext();
  1119. ImplicitCallFlags savedImplicitCallFlags = threadContext->GetImplicitCallFlags();
  1120. threadContext->ClearImplicitCallFlags();
  1121. Type *const oldType = object->GetType();
  1122. if (Root)
  1123. {
  1124. JavascriptOperators::PatchPutRootValueNoFastPath(functionBody, inlineCache, inlineCacheIndex, object, propertyId, value, flags);
  1125. }
  1126. else
  1127. {
  1128. JavascriptOperators::PatchPutValueWithThisPtrNoFastPath(functionBody, inlineCache, inlineCacheIndex, object, propertyId, value, thisObject, flags);
  1129. }
  1130. CacheOperators::PretendTrySetProperty<true, false>(
  1131. object->GetType(),
  1132. oldType,
  1133. &operationInfo,
  1134. &propertyValueInfo);
  1135. // Setting to __proto__ property invokes a setter and changes the prototype.So, although PatchPut* populates the cache,
  1136. // the setter invalidates it (since it changes the prototype). PretendTrySetProperty looks at the inline cache type to
  1137. // update the cacheType on PropertyOperationInfo, which is used in populating the field info flags for this operation on
  1138. // the profile. Since the cache was invalidated, we don't get a match with either the type of the object with property or
  1139. // without it and the cacheType defaults to CacheType_None. This leads the profile info to say that this operation doesn't
  1140. // cause an accessor implicit call and JIT then doesn't kill live fields across it and ends up putting a BailOutOnImplicitCalls
  1141. // if there were live fields. This bailout always bails out.
  1142. Js::ImplicitCallFlags accessorCallFlag = (Js::ImplicitCallFlags)(Js::ImplicitCall_Accessor & ~Js::ImplicitCall_None);
  1143. if ((threadContext->GetImplicitCallFlags() & accessorCallFlag) != 0)
  1144. {
  1145. operationInfo.cacheType = CacheType_Setter;
  1146. }
  1147. threadContext->SetImplicitCallFlags((Js::ImplicitCallFlags)(savedImplicitCallFlags | threadContext->GetImplicitCallFlags()));
  1148. }
  1149. // Only make the field polymorphic if we are not using the root object inline cache
  1150. if(operationInfo.isPolymorphic && inlineCacheIndex < functionBody->GetRootObjectStoreInlineCacheStart())
  1151. {
  1152. // should not be a load inline cache
  1153. Assert(inlineCacheIndex < functionBody->GetRootObjectLoadInlineCacheStart());
  1154. fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_Polymorphic);
  1155. }
  1156. fldInfoFlags =
  1157. DynamicProfileInfo::MergeFldInfoFlags(
  1158. fldInfoFlags,
  1159. DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType));
  1160. fldInfoFlags =
  1161. DynamicProfileInfo::MergeFldInfoFlags(
  1162. fldInfoFlags,
  1163. DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
  1164. UpdateFldInfoFlagsForGetSetInlineCandidate(
  1165. object,
  1166. fldInfoFlags,
  1167. operationInfo.cacheType,
  1168. inlineCache,
  1169. functionBody);
  1170. if(scriptFunction->GetConstructorCache()->NeedsUpdateAfterCtor())
  1171. {
  1172. // This function has only 'this' statements and is being used as a constructor. When the constructor exits, the
  1173. // function object's constructor cache will be updated with the type produced by the constructor. From that
  1174. // point on, when the same function object is used as a constructor, the a new object with the final type will
  1175. // be created. Whatever is stored in the inline cache currently will cause cache misses after the constructor
  1176. // cache update. So, just clear it now so that the caches won't be flagged as polymorphic.
  1177. inlineCache->RemoveFromInvalidationListAndClear(scriptContext->GetThreadContext());
  1178. }
  1179. }
  1180. else
  1181. {
  1182. JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPath<false>(
  1183. functionBody,
  1184. inlineCache,
  1185. inlineCacheIndex,
  1186. instance,
  1187. propertyId,
  1188. value,
  1189. thisInstance,
  1190. flags);
  1191. }
  1192. functionBody->GetDynamicProfileInfo()->RecordFieldAccess(functionBody, inlineCacheIndex, nullptr, fldInfoFlags);
  1193. }
  1194. void ProfilingHelpers::ProfiledInitFld_Jit(
  1195. const Var instance,
  1196. const PropertyId propertyId,
  1197. const InlineCacheIndex inlineCacheIndex,
  1198. const Var value,
  1199. void *const framePointer)
  1200. {
  1201. JIT_HELPER_REENTRANT_HEADER(ProfiledInitFld);
  1202. ScriptFunction *const scriptFunction =
  1203. UnsafeVarTo<ScriptFunction>(JavascriptCallStackLayout::FromFramePointer(framePointer)->functionObject);
  1204. ProfiledInitFld(
  1205. VarTo<RecyclableObject>(instance),
  1206. propertyId,
  1207. GetInlineCache(scriptFunction, inlineCacheIndex),
  1208. inlineCacheIndex,
  1209. value,
  1210. scriptFunction->GetFunctionBody());
  1211. JIT_HELPER_END(ProfiledInitFld);
  1212. }
  1213. void ProfilingHelpers::ProfiledInitFld(
  1214. RecyclableObject *const object,
  1215. const PropertyId propertyId,
  1216. InlineCache *const inlineCache,
  1217. const InlineCacheIndex inlineCacheIndex,
  1218. const Var value,
  1219. FunctionBody *const functionBody)
  1220. {
  1221. Assert(object);
  1222. Assert(propertyId != Constants::NoProperty);
  1223. Assert(inlineCache);
  1224. Assert(functionBody);
  1225. Assert(inlineCacheIndex < functionBody->GetInlineCacheCount());
  1226. Assert(value);
  1227. ScriptContext *const scriptContext = functionBody->GetScriptContext();
  1228. FldInfoFlags fldInfoFlags = FldInfo_NoInfo;
  1229. PropertyCacheOperationInfo operationInfo;
  1230. PropertyValueInfo propertyValueInfo;
  1231. PropertyValueInfo::SetCacheInfo(&propertyValueInfo, functionBody, inlineCache, inlineCacheIndex, true);
  1232. if(!CacheOperators::TrySetProperty<true, true, true, true, true, true, false, true>(
  1233. object,
  1234. false,
  1235. propertyId,
  1236. value,
  1237. scriptContext,
  1238. PropertyOperation_None,
  1239. &operationInfo,
  1240. &propertyValueInfo))
  1241. {
  1242. Type *const oldType = object->GetType();
  1243. JavascriptOperators::PatchInitValueNoFastPath(
  1244. functionBody,
  1245. inlineCache,
  1246. inlineCacheIndex,
  1247. object,
  1248. propertyId,
  1249. value);
  1250. CacheOperators::PretendTrySetProperty<true, false>(object->GetType(), oldType, &operationInfo, &propertyValueInfo);
  1251. }
  1252. // Only make the field polymorphic if the we are not using the root object inline cache
  1253. if(operationInfo.isPolymorphic && inlineCacheIndex < functionBody->GetRootObjectStoreInlineCacheStart())
  1254. {
  1255. // should not be a load inline cache
  1256. Assert(inlineCacheIndex < functionBody->GetRootObjectLoadInlineCacheStart());
  1257. fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_Polymorphic);
  1258. }
  1259. fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType));
  1260. fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
  1261. functionBody->GetDynamicProfileInfo()->RecordFieldAccess(functionBody, inlineCacheIndex, nullptr, fldInfoFlags);
  1262. }
  1263. void ProfilingHelpers::UpdateFldInfoFlagsForGetSetInlineCandidate(
  1264. RecyclableObject *const object,
  1265. FldInfoFlags &fldInfoFlags,
  1266. const CacheType cacheType,
  1267. InlineCache *const inlineCache,
  1268. FunctionBody *const functionBody)
  1269. {
  1270. RecyclableObject *callee = nullptr;
  1271. if((cacheType & (CacheType_Getter | CacheType_Setter)) && inlineCache->GetGetterSetter(object, &callee))
  1272. {
  1273. const bool canInline = functionBody->GetDynamicProfileInfo()->RecordLdFldCallSiteInfo(functionBody, callee, false /*callApplyTarget*/);
  1274. if(canInline)
  1275. {
  1276. //updates this fldInfoFlags passed by reference.
  1277. fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_InlineCandidate);
  1278. }
  1279. }
  1280. }
  1281. void ProfilingHelpers::UpdateFldInfoFlagsForCallApplyInlineCandidate(
  1282. RecyclableObject *const object,
  1283. FldInfoFlags &fldInfoFlags,
  1284. const CacheType cacheType,
  1285. InlineCache *const inlineCache,
  1286. FunctionBody *const functionBody)
  1287. {
  1288. RecyclableObject *callee = nullptr;
  1289. if(!(fldInfoFlags & FldInfo_Polymorphic) && inlineCache->GetCallApplyTarget(object, &callee))
  1290. {
  1291. const bool canInline = functionBody->GetDynamicProfileInfo()->RecordLdFldCallSiteInfo(functionBody, callee, true /*callApplyTarget*/);
  1292. if(canInline)
  1293. {
  1294. //updates the fldInfoFlags passed by reference.
  1295. fldInfoFlags = DynamicProfileInfo::MergeFldInfoFlags(fldInfoFlags, FldInfo_InlineCandidate);
  1296. }
  1297. }
  1298. }
  1299. InlineCache *ProfilingHelpers::GetInlineCache(ScriptFunction *const scriptFunction, const InlineCacheIndex inlineCacheIndex)
  1300. {
  1301. Assert(scriptFunction);
  1302. Assert(inlineCacheIndex < scriptFunction->GetFunctionBody()->GetInlineCacheCount());
  1303. return
  1304. scriptFunction->GetHasInlineCaches()
  1305. ? UnsafeVarTo<ScriptFunctionWithInlineCache>(scriptFunction)->GetInlineCache(inlineCacheIndex)
  1306. : scriptFunction->GetFunctionBody()->GetInlineCache(inlineCacheIndex);
  1307. }
  1308. #endif