CacheOperators.inl 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  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. #pragma once
  6. namespace Js
  7. {
  8. template<
  9. bool CheckLocal,
  10. bool CheckProto,
  11. bool CheckAccessor,
  12. bool CheckMissing,
  13. bool CheckPolymorphicInlineCache,
  14. bool CheckTypePropertyCache,
  15. bool IsInlineCacheAvailable,
  16. bool IsPolymorphicInlineCacheAvailable,
  17. bool ReturnOperationInfo,
  18. bool OutputExistence /*When set, propertyValue represents whether the property exists on the instance, not its actual value*/>
  19. inline bool CacheOperators::TryGetProperty(
  20. Var const instance,
  21. const bool isRoot,
  22. RecyclableObject *const object,
  23. const PropertyId propertyId,
  24. Var *const propertyValue,
  25. ScriptContext *const requestContext,
  26. PropertyCacheOperationInfo * operationInfo,
  27. PropertyValueInfo *const propertyValueInfo)
  28. {
  29. CompileAssert(IsInlineCacheAvailable || IsPolymorphicInlineCacheAvailable);
  30. Assert(!CheckTypePropertyCache || !isRoot);
  31. Assert(propertyValueInfo);
  32. Assert(IsInlineCacheAvailable == !!propertyValueInfo->GetInlineCache());
  33. Assert(IsPolymorphicInlineCacheAvailable == !!propertyValueInfo->GetPolymorphicInlineCache());
  34. Assert(!ReturnOperationInfo || operationInfo);
  35. if(CheckLocal || CheckProto || CheckAccessor || CheckMissing)
  36. {
  37. InlineCache *const inlineCache = IsInlineCacheAvailable ? propertyValueInfo->GetInlineCache() : nullptr;
  38. if(IsInlineCacheAvailable)
  39. {
  40. if (inlineCache->TryGetProperty<CheckLocal, CheckProto, CheckAccessor, CheckMissing, ReturnOperationInfo, OutputExistence>(
  41. instance,
  42. object,
  43. propertyId,
  44. propertyValue,
  45. requestContext,
  46. operationInfo))
  47. {
  48. return true;
  49. }
  50. if(ReturnOperationInfo)
  51. {
  52. operationInfo->isPolymorphic = inlineCache->HasDifferentType(object->GetType());
  53. }
  54. }
  55. else if(ReturnOperationInfo)
  56. {
  57. operationInfo->isPolymorphic = true;
  58. }
  59. if(CheckPolymorphicInlineCache)
  60. {
  61. Assert(IsPolymorphicInlineCacheAvailable || propertyValueInfo->GetFunctionBody());
  62. PolymorphicInlineCache *const polymorphicInlineCache =
  63. IsPolymorphicInlineCacheAvailable
  64. ? propertyValueInfo->GetPolymorphicInlineCache()
  65. : propertyValueInfo->GetFunctionBody()->GetPolymorphicInlineCache(
  66. propertyValueInfo->GetInlineCacheIndex());
  67. if ((IsPolymorphicInlineCacheAvailable || polymorphicInlineCache) &&
  68. polymorphicInlineCache->TryGetProperty<
  69. CheckLocal,
  70. CheckProto,
  71. CheckAccessor,
  72. CheckMissing,
  73. IsInlineCacheAvailable,
  74. ReturnOperationInfo,
  75. OutputExistence
  76. >(
  77. instance,
  78. object,
  79. propertyId,
  80. propertyValue,
  81. requestContext,
  82. operationInfo,
  83. inlineCache
  84. ))
  85. {
  86. return true;
  87. }
  88. }
  89. }
  90. if(!CheckTypePropertyCache)
  91. {
  92. return false;
  93. }
  94. TypePropertyCache *const typePropertyCache = object->GetType()->GetPropertyCache();
  95. if(!typePropertyCache ||
  96. !typePropertyCache->TryGetProperty<OutputExistence>(
  97. CheckMissing,
  98. object,
  99. propertyId,
  100. propertyValue,
  101. requestContext,
  102. ReturnOperationInfo ? operationInfo : nullptr,
  103. propertyValueInfo))
  104. {
  105. return false;
  106. }
  107. if(!ReturnOperationInfo || operationInfo->cacheType == CacheType_TypeProperty)
  108. {
  109. return true;
  110. }
  111. // The property access was cached in an inline cache. Get the proper property operation info.
  112. PretendTryGetProperty<IsInlineCacheAvailable, IsPolymorphicInlineCacheAvailable>(
  113. object->GetType(),
  114. operationInfo,
  115. propertyValueInfo);
  116. return true;
  117. }
  118. template<
  119. bool CheckLocal,
  120. bool CheckLocalTypeWithoutProperty,
  121. bool CheckAccessor,
  122. bool CheckPolymorphicInlineCache,
  123. bool CheckTypePropertyCache,
  124. bool IsInlineCacheAvailable,
  125. bool IsPolymorphicInlineCacheAvailable,
  126. bool ReturnOperationInfo>
  127. inline bool CacheOperators::TrySetProperty(
  128. RecyclableObject *const object,
  129. const bool isRoot,
  130. const PropertyId propertyId,
  131. Var propertyValue,
  132. ScriptContext *const requestContext,
  133. const PropertyOperationFlags propertyOperationFlags,
  134. PropertyCacheOperationInfo * operationInfo,
  135. PropertyValueInfo *const propertyValueInfo)
  136. {
  137. CompileAssert(IsInlineCacheAvailable || IsPolymorphicInlineCacheAvailable);
  138. Assert(!CheckTypePropertyCache || !isRoot);
  139. Assert(propertyValueInfo);
  140. Assert(IsInlineCacheAvailable == !!propertyValueInfo->GetInlineCache());
  141. Assert(IsPolymorphicInlineCacheAvailable == !!propertyValueInfo->GetPolymorphicInlineCache());
  142. Assert(!ReturnOperationInfo || operationInfo);
  143. if(CheckLocal || CheckLocalTypeWithoutProperty || CheckAccessor)
  144. {
  145. InlineCache *const inlineCache = IsInlineCacheAvailable ? propertyValueInfo->GetInlineCache() : nullptr;
  146. if(IsInlineCacheAvailable)
  147. {
  148. if (inlineCache->TrySetProperty<CheckLocal, CheckLocalTypeWithoutProperty, CheckAccessor, ReturnOperationInfo>(
  149. object,
  150. propertyId,
  151. propertyValue,
  152. requestContext,
  153. operationInfo,
  154. propertyOperationFlags))
  155. {
  156. return true;
  157. }
  158. if(ReturnOperationInfo)
  159. {
  160. operationInfo->isPolymorphic = inlineCache->HasDifferentType(object->GetType());
  161. }
  162. }
  163. else if(ReturnOperationInfo)
  164. {
  165. operationInfo->isPolymorphic = true;
  166. }
  167. if(CheckPolymorphicInlineCache)
  168. {
  169. Assert(IsPolymorphicInlineCacheAvailable || propertyValueInfo->GetFunctionBody());
  170. PolymorphicInlineCache *const polymorphicInlineCache =
  171. IsPolymorphicInlineCacheAvailable
  172. ? propertyValueInfo->GetPolymorphicInlineCache()
  173. : propertyValueInfo->GetFunctionBody()->GetPolymorphicInlineCache(
  174. propertyValueInfo->GetInlineCacheIndex());
  175. if ((IsPolymorphicInlineCacheAvailable || polymorphicInlineCache) &&
  176. polymorphicInlineCache->TrySetProperty<
  177. CheckLocal,
  178. CheckLocalTypeWithoutProperty,
  179. CheckAccessor,
  180. IsInlineCacheAvailable,
  181. ReturnOperationInfo
  182. >(
  183. object,
  184. propertyId,
  185. propertyValue,
  186. requestContext,
  187. operationInfo,
  188. inlineCache,
  189. propertyOperationFlags
  190. ))
  191. {
  192. return true;
  193. }
  194. }
  195. }
  196. if(!CheckTypePropertyCache)
  197. {
  198. return false;
  199. }
  200. TypePropertyCache *const typePropertyCache = object->GetType()->GetPropertyCache();
  201. if(!typePropertyCache ||
  202. !typePropertyCache->TrySetProperty(
  203. object,
  204. propertyId,
  205. propertyValue,
  206. requestContext,
  207. ReturnOperationInfo ? operationInfo : nullptr,
  208. propertyValueInfo))
  209. {
  210. return false;
  211. }
  212. if(!ReturnOperationInfo || operationInfo->cacheType == CacheType_TypeProperty)
  213. {
  214. return true;
  215. }
  216. // The property access was cached in an inline cache. Get the proper property operation info.
  217. PretendTrySetProperty<IsInlineCacheAvailable, IsPolymorphicInlineCacheAvailable>(
  218. object->GetType(),
  219. object->GetType(),
  220. operationInfo,
  221. propertyValueInfo);
  222. return true;
  223. }
  224. template<
  225. bool IsInlineCacheAvailable,
  226. bool IsPolymorphicInlineCacheAvailable>
  227. inline void CacheOperators::PretendTryGetProperty(
  228. Type *const type,
  229. PropertyCacheOperationInfo *operationInfo,
  230. PropertyValueInfo *const propertyValueInfo)
  231. {
  232. CompileAssert(IsInlineCacheAvailable || IsPolymorphicInlineCacheAvailable);
  233. Assert(propertyValueInfo);
  234. Assert(IsInlineCacheAvailable == !!propertyValueInfo->GetInlineCache());
  235. Assert(!IsPolymorphicInlineCacheAvailable || propertyValueInfo->GetPolymorphicInlineCache());
  236. Assert(operationInfo);
  237. if (IsInlineCacheAvailable && propertyValueInfo->GetInlineCache()->PretendTryGetProperty(type, operationInfo))
  238. {
  239. return;
  240. }
  241. Assert(IsPolymorphicInlineCacheAvailable || propertyValueInfo->GetFunctionBody());
  242. PolymorphicInlineCache *const polymorphicInlineCache =
  243. IsPolymorphicInlineCacheAvailable
  244. ? propertyValueInfo->GetPolymorphicInlineCache()
  245. : propertyValueInfo->GetFunctionBody()->GetPolymorphicInlineCache(propertyValueInfo->GetInlineCacheIndex());
  246. if (IsPolymorphicInlineCacheAvailable || polymorphicInlineCache)
  247. {
  248. polymorphicInlineCache->PretendTryGetProperty(type, operationInfo);
  249. }
  250. }
  251. template<
  252. bool IsInlineCacheAvailable,
  253. bool IsPolymorphicInlineCacheAvailable>
  254. inline void CacheOperators::PretendTrySetProperty(
  255. Type *const type,
  256. Type *const oldType,
  257. PropertyCacheOperationInfo * operationInfo,
  258. PropertyValueInfo *const propertyValueInfo)
  259. {
  260. CompileAssert(IsInlineCacheAvailable || IsPolymorphicInlineCacheAvailable);
  261. Assert(propertyValueInfo);
  262. Assert(IsInlineCacheAvailable == !!propertyValueInfo->GetInlineCache());
  263. Assert(!IsPolymorphicInlineCacheAvailable || propertyValueInfo->GetPolymorphicInlineCache());
  264. Assert(operationInfo);
  265. if (IsInlineCacheAvailable && propertyValueInfo->GetInlineCache()->PretendTrySetProperty(type, oldType, operationInfo))
  266. {
  267. return;
  268. }
  269. Assert(IsPolymorphicInlineCacheAvailable || propertyValueInfo->GetFunctionBody());
  270. PolymorphicInlineCache *const polymorphicInlineCache =
  271. IsPolymorphicInlineCacheAvailable
  272. ? propertyValueInfo->GetPolymorphicInlineCache()
  273. : propertyValueInfo->GetFunctionBody()->GetPolymorphicInlineCache(propertyValueInfo->GetInlineCacheIndex());
  274. if (IsPolymorphicInlineCacheAvailable || polymorphicInlineCache)
  275. {
  276. polymorphicInlineCache->PretendTrySetProperty(type, oldType, operationInfo);
  277. }
  278. }
  279. template<
  280. bool IsAccessor,
  281. bool IsRead,
  282. bool IncludeTypePropertyCache>
  283. inline void CacheOperators::Cache(
  284. const bool isProto,
  285. DynamicObject *const objectWithProperty,
  286. const bool isRoot,
  287. Type *const type,
  288. Type *const typeWithoutProperty,
  289. const PropertyId propertyId,
  290. const PropertyIndex propertyIndex,
  291. const bool isInlineSlot,
  292. const bool isMissing,
  293. const int requiredAuxSlotCapacity,
  294. const PropertyValueInfo *const info,
  295. ScriptContext *const requestContext)
  296. {
  297. CompileAssert(!IsAccessor || !IncludeTypePropertyCache);
  298. Assert(info);
  299. Assert(objectWithProperty);
  300. if(!IsAccessor)
  301. {
  302. if(!isProto)
  303. {
  304. Assert(type == objectWithProperty->GetType());
  305. }
  306. else
  307. {
  308. Assert(IsRead);
  309. Assert(type != objectWithProperty->GetType());
  310. }
  311. }
  312. else
  313. {
  314. Assert(!isRoot); // could still be root object, but the parameter will be false and shouldn't be used for accessors
  315. Assert(!typeWithoutProperty);
  316. Assert(requiredAuxSlotCapacity == 0);
  317. }
  318. if(IsRead)
  319. {
  320. Assert(!typeWithoutProperty);
  321. Assert(requiredAuxSlotCapacity == 0);
  322. Assert(CanCachePropertyRead(objectWithProperty, requestContext));
  323. if(!IsAccessor && isProto && PropertyValueInfo::PrototypeCacheDisabled(info))
  324. {
  325. return;
  326. }
  327. // Before allowing proxies to cache, we would need to solve various issues (see JavascriptProxy::GetPropertyQuery).
  328. Assert(!JavascriptProxy::Is(objectWithProperty));
  329. }
  330. else
  331. {
  332. Assert(CanCachePropertyWrite(objectWithProperty, requestContext));
  333. // TODO(ianhall): the following assert would let global const properties slip through when they shadow
  334. // a global property. Reason being DictionaryTypeHandler::IsWritable cannot tell if it should check
  335. // the global property or the global let/const. Fix this by updating IsWritable to recognize isRoot.
  336. // Built-in Function.prototype properties 'length', 'arguments', and 'caller' are special cases.
  337. Assert(
  338. objectWithProperty->IsWritable(propertyId) ||
  339. (isRoot && RootObjectBase::FromVar(objectWithProperty)->IsLetConstGlobal(propertyId)) ||
  340. JavascriptFunction::IsBuiltinProperty(objectWithProperty, propertyId));
  341. }
  342. const bool includeTypePropertyCache =
  343. IncludeTypePropertyCache &&
  344. !isRoot &&
  345. (info->GetFunctionBody()
  346. ? !PHASE_OFF(Js::TypePropertyCachePhase, info->GetFunctionBody())
  347. : !PHASE_OFF1(Js::TypePropertyCachePhase)
  348. );
  349. bool createTypePropertyCache = false;
  350. PolymorphicInlineCache *polymorphicInlineCache = info->GetPolymorphicInlineCache();
  351. if(!polymorphicInlineCache && info->GetFunctionBody())
  352. {
  353. polymorphicInlineCache = info->GetFunctionBody()->GetPolymorphicInlineCache(info->GetInlineCacheIndex());
  354. }
  355. InlineCache *const inlineCache = info->GetInlineCache();
  356. if(inlineCache)
  357. {
  358. const bool tryCreatePolymorphicInlineCache = !polymorphicInlineCache && info->GetFunctionBody();
  359. if((includeTypePropertyCache || tryCreatePolymorphicInlineCache) &&
  360. inlineCache->HasDifferentType<IsAccessor>(isProto, type, typeWithoutProperty))
  361. {
  362. if(tryCreatePolymorphicInlineCache)
  363. {
  364. polymorphicInlineCache =
  365. info->GetFunctionBody()->CreateNewPolymorphicInlineCache(
  366. info->GetInlineCacheIndex(),
  367. propertyId,
  368. inlineCache);
  369. }
  370. if(includeTypePropertyCache)
  371. {
  372. createTypePropertyCache = true;
  373. }
  374. }
  375. if(!IsAccessor)
  376. {
  377. if(!isProto)
  378. {
  379. inlineCache->CacheLocal(
  380. type,
  381. propertyId,
  382. propertyIndex,
  383. isInlineSlot,
  384. typeWithoutProperty,
  385. requiredAuxSlotCapacity,
  386. requestContext);
  387. }
  388. else
  389. {
  390. inlineCache->CacheProto(
  391. objectWithProperty,
  392. propertyId,
  393. propertyIndex,
  394. isInlineSlot,
  395. isMissing,
  396. type,
  397. requestContext);
  398. }
  399. }
  400. else
  401. {
  402. inlineCache->CacheAccessor(
  403. IsRead,
  404. propertyId,
  405. propertyIndex,
  406. isInlineSlot,
  407. type,
  408. objectWithProperty,
  409. isProto,
  410. requestContext);
  411. }
  412. }
  413. if(polymorphicInlineCache)
  414. {
  415. // Don't resize a polymorphic inline cache from full JIT because it currently doesn't rejit to use the new
  416. // polymorphic inline cache. Once resized, bailouts would populate only the new set of caches and full JIT would
  417. // continue to use to old set of caches.
  418. Assert(!info->AllowResizingPolymorphicInlineCache() || info->GetFunctionBody() || info->GetPropertyRecordUsageCache());
  419. if(((includeTypePropertyCache && !createTypePropertyCache) || info->AllowResizingPolymorphicInlineCache()) &&
  420. polymorphicInlineCache->HasDifferentType<IsAccessor>(isProto, type, typeWithoutProperty))
  421. {
  422. if(info->AllowResizingPolymorphicInlineCache() && polymorphicInlineCache->CanAllocateBigger())
  423. {
  424. if (info->GetFunctionBody())
  425. {
  426. Assert(polymorphicInlineCache == info->GetFunctionBody()->GetPolymorphicInlineCache(info->GetInlineCacheIndex()));
  427. polymorphicInlineCache =
  428. info->GetFunctionBody()->CreateBiggerPolymorphicInlineCache(
  429. info->GetInlineCacheIndex(),
  430. propertyId);
  431. }
  432. else
  433. {
  434. Assert(!info->GetFunctionBody());
  435. Assert(polymorphicInlineCache == (IsRead ? info->GetPropertyRecordUsageCache()->GetLdElemInlineCache() : info->GetPropertyRecordUsageCache()->GetStElemInlineCache()));
  436. polymorphicInlineCache = info->GetPropertyRecordUsageCache()->CreateBiggerPolymorphicInlineCache(IsRead);
  437. }
  438. }
  439. if(includeTypePropertyCache)
  440. {
  441. createTypePropertyCache = true;
  442. }
  443. }
  444. if(!IsAccessor)
  445. {
  446. if(!isProto)
  447. {
  448. polymorphicInlineCache->CacheLocal(
  449. type,
  450. propertyId,
  451. propertyIndex,
  452. isInlineSlot,
  453. typeWithoutProperty,
  454. requiredAuxSlotCapacity,
  455. requestContext);
  456. }
  457. else
  458. {
  459. polymorphicInlineCache->CacheProto(
  460. objectWithProperty,
  461. propertyId,
  462. propertyIndex,
  463. isInlineSlot,
  464. isMissing,
  465. type,
  466. requestContext);
  467. }
  468. }
  469. else
  470. {
  471. polymorphicInlineCache->CacheAccessor(
  472. IsRead,
  473. propertyId,
  474. propertyIndex,
  475. isInlineSlot,
  476. type,
  477. objectWithProperty,
  478. isProto,
  479. requestContext);
  480. }
  481. }
  482. if(!includeTypePropertyCache)
  483. {
  484. return;
  485. }
  486. Assert(!IsAccessor);
  487. TypePropertyCache *typePropertyCache = type->GetPropertyCache();
  488. if(!typePropertyCache)
  489. {
  490. if(!createTypePropertyCache)
  491. {
  492. return;
  493. }
  494. typePropertyCache = type->CreatePropertyCache();
  495. }
  496. if(isProto)
  497. {
  498. typePropertyCache->Cache(
  499. propertyId,
  500. propertyIndex,
  501. isInlineSlot,
  502. info->IsWritable() && info->IsStoreFieldCacheEnabled(),
  503. isMissing,
  504. objectWithProperty,
  505. type);
  506. typePropertyCache = objectWithProperty->GetType()->GetPropertyCache();
  507. if(!typePropertyCache)
  508. {
  509. return;
  510. }
  511. }
  512. typePropertyCache->Cache(
  513. propertyId,
  514. propertyIndex,
  515. isInlineSlot,
  516. info->IsWritable() && info->IsStoreFieldCacheEnabled());
  517. }
  518. }