InlineCache.h 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  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. #define TypeWithAuxSlotTag(_t) \
  7. (reinterpret_cast<Type*>(reinterpret_cast<size_t>(_t) | InlineCacheAuxSlotTypeTag))
  8. #define TypeWithoutAuxSlotTag(_t) \
  9. (reinterpret_cast<Js::Type*>(reinterpret_cast<size_t>(_t) & ~InlineCacheAuxSlotTypeTag))
  10. #define TypeHasAuxSlotTag(_t) \
  11. (!!(reinterpret_cast<size_t>(_t) & InlineCacheAuxSlotTypeTag))
  12. #if defined(_M_IX86_OR_ARM32)
  13. #define PolymorphicInlineCacheShift 5 // On 32 bit architectures, the least 5 significant bits of a DynamicTypePointer is 0
  14. #else
  15. #define PolymorphicInlineCacheShift 6 // On 64 bit architectures, the least 6 significant bits of a DynamicTypePointer is 0
  16. #endif
  17. // TODO: OOP JIT, move equiv set to backend?
  18. // forward decl
  19. class JITType;
  20. template <class TAllocator> class JITTypeHolderBase;
  21. typedef JITTypeHolderBase<void> JITTypeHolder;
  22. typedef JITTypeHolderBase<Recycler> RecyclerJITTypeHolder;
  23. namespace Js
  24. {
  25. enum CacheType : byte
  26. {
  27. CacheType_None,
  28. CacheType_Local,
  29. CacheType_Proto,
  30. CacheType_LocalWithoutProperty,
  31. CacheType_Getter,
  32. CacheType_Setter,
  33. CacheType_TypeProperty,
  34. };
  35. enum SlotType : byte
  36. {
  37. SlotType_None,
  38. SlotType_Inline,
  39. SlotType_Aux,
  40. };
  41. struct PropertyCacheOperationInfo
  42. {
  43. PropertyCacheOperationInfo()
  44. : cacheType(CacheType_None), slotType(SlotType_None), isPolymorphic(false)
  45. {
  46. }
  47. CacheType cacheType;
  48. SlotType slotType;
  49. bool isPolymorphic;
  50. };
  51. struct JitTimeInlineCache;
  52. struct InlineCache
  53. {
  54. static const int CacheLayoutSelectorBitCount = 1;
  55. static const int RequiredAuxSlotCapacityBitCount = 15;
  56. static const bool IsPolymorphic = false;
  57. InlineCache() {}
  58. union
  59. {
  60. // Invariants:
  61. // - Type* fields do not overlap.
  62. // - "next" field is non-null iff the cache is linked in a list of proto-caches
  63. // (see ScriptContext::RegisterProtoInlineCache and ScriptContext::InvalidateProtoCaches).
  64. struct s_local
  65. {
  66. Type* type;
  67. // PatchPutValue caches here the type the object has before a new property is added.
  68. // If this type is hit again we can immediately change the object's type to "type"
  69. // and store the value into the slot "slotIndex".
  70. Type* typeWithoutProperty;
  71. union
  72. {
  73. struct
  74. {
  75. uint16 isLocal : 1;
  76. uint16 requiredAuxSlotCapacity : 15; // Maximum auxiliary slot capacity (for a path type) must be < 2^16
  77. };
  78. struct
  79. {
  80. uint16 rawUInt16; // Required for access from JIT-ed code
  81. };
  82. };
  83. uint16 slotIndex;
  84. } local;
  85. struct s_proto
  86. {
  87. uint16 isProto : 1;
  88. uint16 isMissing : 1;
  89. uint16 unused : 14;
  90. uint16 slotIndex;
  91. // It's OK for the type in proto layout to overlap with typeWithoutProperty in the local layout, because
  92. // we only use typeWithoutProperty on field stores, which can never have a proto layout.
  93. Type* type;
  94. DynamicObject* prototypeObject;
  95. } proto;
  96. struct s_accessor
  97. {
  98. DynamicObject *object;
  99. union
  100. {
  101. struct {
  102. uint16 isAccessor : 1;
  103. uint16 flags : 2;
  104. uint16 isOnProto : 1;
  105. uint16 unused : 12;
  106. };
  107. uint16 rawUInt16;
  108. };
  109. uint16 slotIndex;
  110. Type * type;
  111. } accessor;
  112. CompileAssert(sizeof(s_local) == sizeof(s_proto));
  113. CompileAssert(sizeof(s_local) == sizeof(s_accessor));
  114. } u;
  115. InlineCache** invalidationListSlotPtr;
  116. bool IsEmpty() const
  117. {
  118. return u.local.type == nullptr;
  119. }
  120. bool IsLocal() const
  121. {
  122. return u.local.isLocal;
  123. }
  124. bool IsProto() const
  125. {
  126. return u.proto.isProto;
  127. }
  128. DynamicObject * GetPrototypeObject() const
  129. {
  130. Assert(IsProto());
  131. return u.proto.prototypeObject;
  132. }
  133. DynamicObject * GetAccessorObject() const
  134. {
  135. Assert(IsAccessor());
  136. return u.accessor.object;
  137. }
  138. bool IsAccessor() const
  139. {
  140. return u.accessor.isAccessor;
  141. }
  142. bool IsAccessorOnProto() const
  143. {
  144. return IsAccessor() && u.accessor.isOnProto;
  145. }
  146. bool IsGetterAccessor() const
  147. {
  148. return IsAccessor() && !!(u.accessor.flags & InlineCacheGetterFlag);
  149. }
  150. bool IsGetterAccessorOnProto() const
  151. {
  152. return IsGetterAccessor() && u.accessor.isOnProto;
  153. }
  154. bool IsSetterAccessor() const
  155. {
  156. return IsAccessor() && !!(u.accessor.flags & InlineCacheSetterFlag);
  157. }
  158. bool IsSetterAccessorOnProto() const
  159. {
  160. return IsSetterAccessor() && u.accessor.isOnProto;
  161. }
  162. Type* GetRawType() const
  163. {
  164. return IsLocal() ? u.local.type : (IsProto() ? u.proto.type : (IsAccessor() ? u.accessor.type : nullptr));
  165. }
  166. Type* GetType() const
  167. {
  168. return TypeWithoutAuxSlotTag(GetRawType());
  169. }
  170. template<bool isAccessor>
  171. bool HasDifferentType(const bool isProto, const Type * type, const Type * typeWithoutProperty) const;
  172. bool HasType_Flags(const Type * type) const
  173. {
  174. return u.accessor.type == type || u.accessor.type == TypeWithAuxSlotTag(type);
  175. }
  176. bool HasDifferentType(const Type * type) const
  177. {
  178. return !IsEmpty() && GetType() != type;
  179. }
  180. bool RemoveFromInvalidationList()
  181. {
  182. if (this->invalidationListSlotPtr == nullptr)
  183. {
  184. return false;
  185. }
  186. *this->invalidationListSlotPtr = nullptr;
  187. this->invalidationListSlotPtr = nullptr;
  188. return true;
  189. }
  190. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  191. const char16 *LayoutString() const
  192. {
  193. if (IsEmpty())
  194. {
  195. return _u("Empty");
  196. }
  197. if (IsLocal())
  198. {
  199. return _u("Local");
  200. }
  201. if (IsAccessor())
  202. {
  203. return _u("Accessor");
  204. }
  205. return _u("Proto");
  206. }
  207. #endif
  208. public:
  209. void CacheLocal(
  210. Type *const type,
  211. const PropertyId propertyId,
  212. const PropertyIndex propertyIndex,
  213. const bool isInlineSlot,
  214. Type *const typeWithoutProperty,
  215. int requiredAuxSlotCapacity,
  216. ScriptContext *const requestContext);
  217. void CacheProto(
  218. DynamicObject *const prototypeObjectWithProperty,
  219. const PropertyId propertyId,
  220. const PropertyIndex propertyIndex,
  221. const bool isInlineSlot,
  222. const bool isMissing,
  223. Type *const type,
  224. ScriptContext *const requestContext);
  225. void CacheMissing(
  226. DynamicObject *const missingPropertyHolder,
  227. const PropertyId propertyId,
  228. const PropertyIndex propertyIndex,
  229. const bool isInlineSlot,
  230. Type *const type,
  231. ScriptContext *const requestContext);
  232. void CacheAccessor(
  233. const bool isGetter,
  234. const PropertyId propertyId,
  235. const PropertyIndex propertyIndex,
  236. const bool isInlineSlot,
  237. Type *const type,
  238. DynamicObject *const object,
  239. const bool isOnProto,
  240. ScriptContext *const requestContext);
  241. template<
  242. bool CheckLocal,
  243. bool CheckProto,
  244. bool CheckAccessor,
  245. bool CheckMissing,
  246. bool ReturnOperationInfo>
  247. bool TryGetProperty(
  248. Var const instance,
  249. RecyclableObject *const propertyObject,
  250. const PropertyId propertyId,
  251. Var *const propertyValue,
  252. ScriptContext *const requestContext,
  253. PropertyCacheOperationInfo *const operationInfo);
  254. template<
  255. bool CheckLocal,
  256. bool CheckLocalTypeWithoutProperty,
  257. bool CheckAccessor,
  258. bool ReturnOperationInfo>
  259. bool TrySetProperty(
  260. RecyclableObject *const object,
  261. const PropertyId propertyId,
  262. Var propertyValue,
  263. ScriptContext *const requestContext,
  264. PropertyCacheOperationInfo *const operationInfo,
  265. const PropertyOperationFlags propertyOperationFlags = PropertyOperation_None);
  266. bool PretendTryGetProperty(Type *const type, PropertyCacheOperationInfo * operationInfo);
  267. bool PretendTrySetProperty(Type *const type, Type *const oldType, PropertyCacheOperationInfo * operationInfo);
  268. void Clear();
  269. template <class TAllocator>
  270. InlineCache *Clone(TAllocator *const allocator);
  271. InlineCache *Clone(Js::PropertyId propertyId, ScriptContext* scriptContext);
  272. void CopyTo(PropertyId propertyId, ScriptContext * scriptContext, InlineCache * const clone);
  273. bool TryGetFixedMethodFromCache(Js::FunctionBody* functionBody, uint cacheId, Js::JavascriptFunction** pFixedMethod);
  274. bool GetGetterSetter(Type *const type, RecyclableObject **callee);
  275. bool GetCallApplyTarget(RecyclableObject* obj, RecyclableObject **callee);
  276. static uint GetGetterFlagMask()
  277. {
  278. // First bit is marked for isAccessor in the accessor cache layout.
  279. return InlineCacheGetterFlag << 1;
  280. }
  281. static uint GetSetterFlagMask()
  282. {
  283. // First bit is marked for isAccessor in the accessor cache layout.
  284. return InlineCacheSetterFlag << 1;
  285. }
  286. static uint GetGetterSetterFlagMask()
  287. {
  288. // First bit is marked for isAccessor in the accessor cache layout.
  289. return (InlineCacheGetterFlag | InlineCacheSetterFlag) << 1;
  290. }
  291. bool NeedsToBeRegisteredForProtoInvalidation() const;
  292. bool NeedsToBeRegisteredForStoreFieldInvalidation() const;
  293. #if DEBUG
  294. bool ConfirmCacheMiss(const Type * oldType, const PropertyValueInfo* info) const;
  295. bool NeedsToBeRegisteredForInvalidation() const;
  296. static void VerifyRegistrationForInvalidation(const InlineCache* cache, ScriptContext* scriptContext, Js::PropertyId propertyId);
  297. #endif
  298. #if DBG_DUMP
  299. void Dump();
  300. #endif
  301. };
  302. #if defined(_M_IX86_OR_ARM32)
  303. CompileAssert(sizeof(InlineCache) == 0x10);
  304. #else
  305. CompileAssert(sizeof(InlineCache) == 0x20);
  306. #endif
  307. CompileAssert(sizeof(InlineCache) == sizeof(InlineCacheAllocator::CacheLayout));
  308. CompileAssert(offsetof(InlineCache, invalidationListSlotPtr) == offsetof(InlineCacheAllocator::CacheLayout, strongRef));
  309. struct JitTimePolymorphicInlineCache;
  310. struct PolymorphicInlineCache sealed : public FinalizableObject
  311. {
  312. #ifdef INLINE_CACHE_STATS
  313. friend class Js::ScriptContext;
  314. #endif
  315. public:
  316. static const bool IsPolymorphic = true;
  317. private:
  318. FieldNoBarrier(InlineCache *) inlineCaches;
  319. Field(FunctionBody *) functionBody;
  320. Field(uint16) size;
  321. Field(bool) ignoreForEquivalentObjTypeSpec;
  322. Field(bool) cloneForJitTimeUse;
  323. Field(int32) inlineCachesFillInfo;
  324. // DList chaining all polymorphic inline caches of a FunctionBody together.
  325. // Since PolymorphicInlineCache is a leaf object, these references do not keep
  326. // the polymorphic inline caches alive. When a PolymorphicInlineCache is finalized,
  327. // it removes itself from the list and deletes its inline cache array.
  328. Field(PolymorphicInlineCache *) next;
  329. Field(PolymorphicInlineCache *) prev;
  330. PolymorphicInlineCache(InlineCache * inlineCaches, uint16 size, FunctionBody * functionBody)
  331. : inlineCaches(inlineCaches), functionBody(functionBody), size(size), ignoreForEquivalentObjTypeSpec(false), cloneForJitTimeUse(true), inlineCachesFillInfo(0), next(nullptr), prev(nullptr)
  332. {
  333. Assert((size == 0 && inlineCaches == nullptr) ||
  334. (inlineCaches != nullptr && size >= MinPolymorphicInlineCacheSize && size <= MaxPolymorphicInlineCacheSize));
  335. }
  336. public:
  337. static PolymorphicInlineCache * New(uint16 size, FunctionBody * functionBody);
  338. static uint16 GetInitialSize() { return MinPolymorphicInlineCacheSize; }
  339. bool CanAllocateBigger() { return GetSize() < MaxPolymorphicInlineCacheSize; }
  340. static uint16 GetNextSize(uint16 currentSize)
  341. {
  342. if (currentSize == MaxPolymorphicInlineCacheSize)
  343. {
  344. return 0;
  345. }
  346. else
  347. {
  348. Assert(currentSize >= MinPolymorphicInlineCacheSize && currentSize <= (MaxPolymorphicInlineCacheSize / 2));
  349. return currentSize * 2;
  350. }
  351. }
  352. template<bool isAccessor>
  353. bool HasDifferentType(const bool isProto, const Type * type, const Type * typeWithoutProperty) const;
  354. bool HasType_Flags(const Type * type) const;
  355. InlineCache * GetInlineCaches() const { return inlineCaches; }
  356. uint16 GetSize() const { return size; }
  357. PolymorphicInlineCache * GetNext() { return next; }
  358. bool GetIgnoreForEquivalentObjTypeSpec() const { return this->ignoreForEquivalentObjTypeSpec; }
  359. void SetIgnoreForEquivalentObjTypeSpec(bool value) { this->ignoreForEquivalentObjTypeSpec = value; }
  360. bool GetCloneForJitTimeUse() const { return this->cloneForJitTimeUse; }
  361. void SetCloneForJitTimeUse(bool value) { this->cloneForJitTimeUse = value; }
  362. uint32 GetInlineCachesFillInfo() { return this->inlineCachesFillInfo; }
  363. void UpdateInlineCachesFillInfo(uint32 index, bool set);
  364. bool IsFull();
  365. virtual void Finalize(bool isShutdown) override;
  366. virtual void Dispose(bool isShutdown) override { };
  367. virtual void Mark(Recycler *recycler) override { AssertMsg(false, "Mark called on object that isn't TrackableObject"); }
  368. void CacheLocal(
  369. Type *const type,
  370. const PropertyId propertyId,
  371. const PropertyIndex propertyIndex,
  372. const bool isInlineSlot,
  373. Type *const typeWithoutProperty,
  374. int requiredAuxSlotCapacity,
  375. ScriptContext *const requestContext);
  376. void CacheProto(
  377. DynamicObject *const prototypeObjectWithProperty,
  378. const PropertyId propertyId,
  379. const PropertyIndex propertyIndex,
  380. const bool isInlineSlot,
  381. const bool isMissing,
  382. Type *const type,
  383. ScriptContext *const requestContext);
  384. void CacheAccessor(
  385. const bool isGetter,
  386. const PropertyId propertyId,
  387. const PropertyIndex propertyIndex,
  388. const bool isInlineSlot,
  389. Type *const type,
  390. DynamicObject *const object,
  391. const bool isOnProto,
  392. ScriptContext *const requestContext);
  393. template<
  394. bool CheckLocal,
  395. bool CheckProto,
  396. bool CheckAccessor,
  397. bool CheckMissing,
  398. bool IsInlineCacheAvailable,
  399. bool ReturnOperationInfo>
  400. bool TryGetProperty(
  401. Var const instance,
  402. RecyclableObject *const propertyObject,
  403. const PropertyId propertyId,
  404. Var *const propertyValue,
  405. ScriptContext *const requestContext,
  406. PropertyCacheOperationInfo *const operationInfo,
  407. InlineCache *const inlineCacheToPopulate);
  408. template<
  409. bool CheckLocal,
  410. bool CheckLocalTypeWithoutProperty,
  411. bool CheckAccessor,
  412. bool ReturnOperationInfo,
  413. bool PopulateInlineCache>
  414. bool TrySetProperty(
  415. RecyclableObject *const object,
  416. const PropertyId propertyId,
  417. Var propertyValue,
  418. ScriptContext *const requestContext,
  419. PropertyCacheOperationInfo *const operationInfo,
  420. InlineCache *const inlineCacheToPopulate,
  421. const PropertyOperationFlags propertyOperationFlags = PropertyOperation_None);
  422. bool PretendTryGetProperty(Type *const type, PropertyCacheOperationInfo * operationInfo);
  423. bool PretendTrySetProperty(Type *const type, Type *const oldType, PropertyCacheOperationInfo * operationInfo);
  424. void CopyTo(PropertyId propertyId, ScriptContext* scriptContext, PolymorphicInlineCache *const clone);
  425. #if DBG_DUMP
  426. void Dump();
  427. #endif
  428. uint GetInlineCacheIndexForType(const Type * type) const
  429. {
  430. return (((size_t)type) >> PolymorphicInlineCacheShift) & (GetSize() - 1);
  431. }
  432. private:
  433. uint GetNextInlineCacheIndex(uint index) const
  434. {
  435. if (++index == GetSize())
  436. {
  437. index = 0;
  438. }
  439. return index;
  440. }
  441. template<bool CheckLocal, bool CheckProto, bool CheckAccessor>
  442. void CloneInlineCacheToEmptySlotInCollision(Type *const type, uint index);
  443. #ifdef CLONE_INLINECACHE_TO_EMPTYSLOT
  444. template <typename TDelegate>
  445. bool CheckClonedInlineCache(uint inlineCacheIndex, TDelegate mapper);
  446. #endif
  447. #if INTRUSIVE_TESTTRACE_PolymorphicInlineCache
  448. uint GetEntryCount()
  449. {
  450. uint count = 0;
  451. for (uint i = 0; i < size; ++i)
  452. {
  453. if (!inlineCaches[i].IsEmpty())
  454. {
  455. count++;
  456. }
  457. }
  458. return count;
  459. }
  460. #endif
  461. };
  462. #if ENABLE_NATIVE_CODEGEN
  463. class EquivalentTypeSet
  464. {
  465. private:
  466. Field(bool) sortedAndDuplicatesRemoved;
  467. Field(uint16) count;
  468. Field(RecyclerJITTypeHolder *) types;
  469. public:
  470. EquivalentTypeSet(RecyclerJITTypeHolder * types, uint16 count);
  471. uint16 GetCount() const
  472. {
  473. return this->count;
  474. }
  475. JITTypeHolder GetFirstType() const;
  476. JITTypeHolder GetType(uint16 index) const;
  477. bool GetSortedAndDuplicatesRemoved() const
  478. {
  479. return this->sortedAndDuplicatesRemoved;
  480. }
  481. bool Contains(const JITTypeHolder type, uint16 * pIndex = nullptr);
  482. static bool AreIdentical(EquivalentTypeSet * left, EquivalentTypeSet * right);
  483. static bool IsSubsetOf(EquivalentTypeSet * left, EquivalentTypeSet * right);
  484. void SortAndRemoveDuplicates();
  485. };
  486. #endif
  487. enum class CtorCacheGuardValues : intptr_t
  488. {
  489. TagFlag = 0x01,
  490. Invalid = 0x00,
  491. Special = TagFlag
  492. };
  493. ENUM_CLASS_HELPERS(CtorCacheGuardValues, intptr_t);
  494. #define MaxCachedSlotCount 65535
  495. struct ConstructorCache
  496. {
  497. friend class JavascriptFunction;
  498. struct GuardStruct
  499. {
  500. Field(CtorCacheGuardValues) value;
  501. };
  502. struct ContentStruct
  503. {
  504. Field(DynamicType*) type;
  505. Field(ScriptContext*) scriptContext;
  506. // In a pinch we could eliminate this and store type pending sharing in the type field as long
  507. // as the guard value flags fit below the object alignment boundary. However, this wouldn't
  508. // keep the type alive, so it would only work if we zeroed constructor caches before GC.
  509. Field(DynamicType*) pendingType;
  510. // We cache only types whose slotCount < 64K to ensure the slotCount field doesn't look like a pointer to the recycler.
  511. Field(int) slotCount;
  512. // This layout (i.e. one-byte bit fields first, then the one-byte updateAfterCtor, and then the two byte inlineSlotCount) is
  513. // chosen intentionally to make sure the whole four bytes never look like a pointer and create a false reference pinning something
  514. // in recycler heap. The isPopulated bit is always set when the cache holds any data - even if it got invalidated.
  515. Field(bool) isPopulated : 1;
  516. Field(bool) isPolymorphic : 1;
  517. Field(bool) typeUpdatePending : 1;
  518. Field(bool) ctorHasNoExplicitReturnValue : 1;
  519. Field(bool) skipDefaultNewObject : 1;
  520. // This field indicates that the type stored in this cache is the final type after constructor.
  521. Field(bool) typeIsFinal : 1;
  522. // This field indicates that the constructor cache has been invalidated due to a constructor's prototype property change.
  523. // We use this flag to determine if we should mark the cache as polymorphic and not attempt subsequent optimizations.
  524. // The cache may also be invalidated due to a guard invalidation resulting from some property change (e.g. in proto chain),
  525. // in which case we won't deem the cache polymorphic.
  526. Field(bool) hasPrototypeChanged : 1;
  527. Field(uint8) callCount;
  528. // Separate from the bit field below for convenient compare from the JIT-ed code. Doesn't currently increase the size.
  529. // If size becomes an issue, we could merge back into the bit field and use a TEST instead of CMP.
  530. Field(bool) updateAfterCtor;
  531. Field(int16) inlineSlotCount;
  532. };
  533. union
  534. {
  535. Field(GuardStruct) guard;
  536. Field(ContentStruct) content;
  537. };
  538. CompileAssert(offsetof(GuardStruct, value) == offsetof(ContentStruct, type));
  539. CompileAssert(sizeof(((GuardStruct*)nullptr)->value) == sizeof(((ContentStruct*)nullptr)->type));
  540. CompileAssert(static_cast<intptr_t>(CtorCacheGuardValues::Invalid) == static_cast<intptr_t>(NULL));
  541. static ConstructorCache DefaultInstance;
  542. public:
  543. ConstructorCache()
  544. {
  545. this->content.type = nullptr;
  546. this->content.scriptContext = nullptr;
  547. this->content.slotCount = 0;
  548. this->content.inlineSlotCount = 0;
  549. this->content.updateAfterCtor = false;
  550. this->content.ctorHasNoExplicitReturnValue = false;
  551. this->content.skipDefaultNewObject = false;
  552. this->content.isPopulated = false;
  553. this->content.isPolymorphic = false;
  554. this->content.typeUpdatePending = false;
  555. this->content.typeIsFinal = false;
  556. this->content.hasPrototypeChanged = false;
  557. this->content.callCount = 0;
  558. Assert(IsConsistent());
  559. }
  560. ConstructorCache(ConstructorCache const * other)
  561. {
  562. Assert(other != nullptr);
  563. this->content.type = other->content.type;
  564. this->content.scriptContext = other->content.scriptContext;
  565. this->content.slotCount = other->content.slotCount;
  566. this->content.inlineSlotCount = other->content.inlineSlotCount;
  567. this->content.updateAfterCtor = other->content.updateAfterCtor;
  568. this->content.ctorHasNoExplicitReturnValue = other->content.ctorHasNoExplicitReturnValue;
  569. this->content.skipDefaultNewObject = other->content.skipDefaultNewObject;
  570. this->content.isPopulated = other->content.isPopulated;
  571. this->content.isPolymorphic = other->content.isPolymorphic;
  572. this->content.typeUpdatePending = other->content.typeUpdatePending;
  573. this->content.typeIsFinal = other->content.typeIsFinal;
  574. this->content.hasPrototypeChanged = other->content.hasPrototypeChanged;
  575. this->content.callCount = other->content.callCount;
  576. Assert(IsConsistent());
  577. }
  578. static size_t const GetOffsetOfGuardValue() { return offsetof(Js::ConstructorCache, guard.value); }
  579. static size_t const GetSizeOfGuardValue() { return sizeof(((Js::ConstructorCache*)nullptr)->guard.value); }
  580. void Populate(DynamicType* type, ScriptContext* scriptContext, bool ctorHasNoExplicitReturnValue, bool updateAfterCtor)
  581. {
  582. Assert(scriptContext == type->GetScriptContext());
  583. Assert(type->GetIsShared());
  584. Assert(IsConsistent());
  585. Assert(!this->content.isPopulated || this->content.isPolymorphic);
  586. Assert(type->GetTypeHandler()->GetSlotCapacity() <= MaxCachedSlotCount);
  587. this->content.isPopulated = true;
  588. this->content.type = type;
  589. this->content.scriptContext = scriptContext;
  590. this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
  591. this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
  592. this->content.ctorHasNoExplicitReturnValue = ctorHasNoExplicitReturnValue;
  593. this->content.updateAfterCtor = updateAfterCtor;
  594. Assert(IsConsistent());
  595. }
  596. void PopulateForSkipDefaultNewObject(ScriptContext* scriptContext)
  597. {
  598. Assert(IsConsistent());
  599. Assert(!this->content.isPopulated);
  600. this->content.isPopulated = true;
  601. this->guard.value = CtorCacheGuardValues::Special;
  602. this->content.scriptContext = scriptContext;
  603. this->content.skipDefaultNewObject = true;
  604. Assert(IsConsistent());
  605. }
  606. bool TryUpdateAfterConstructor(DynamicType* type, ScriptContext* scriptContext)
  607. {
  608. Assert(scriptContext == type->GetScriptContext());
  609. Assert(type->GetTypeHandler()->GetMayBecomeShared());
  610. Assert(IsConsistent());
  611. Assert(this->content.isPopulated);
  612. Assert(this->content.scriptContext == scriptContext);
  613. Assert(!this->content.typeUpdatePending);
  614. Assert(this->content.ctorHasNoExplicitReturnValue);
  615. if (type->GetTypeHandler()->GetSlotCapacity() > MaxCachedSlotCount)
  616. {
  617. return false;
  618. }
  619. if (type->GetIsShared())
  620. {
  621. this->content.type = type;
  622. this->content.typeIsFinal = true;
  623. this->content.pendingType = nullptr;
  624. }
  625. else
  626. {
  627. AssertMsg(false, "No one calls this part of the code?");
  628. this->guard.value = CtorCacheGuardValues::Special;
  629. this->content.pendingType = type;
  630. this->content.typeUpdatePending = true;
  631. }
  632. this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
  633. this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
  634. Assert(IsConsistent());
  635. return true;
  636. }
  637. void UpdateInlineSlotCount()
  638. {
  639. Assert(IsConsistent());
  640. Assert(this->content.isPopulated);
  641. Assert(IsEnabled() || NeedsTypeUpdate());
  642. DynamicType* type = this->content.typeUpdatePending ? this->content.pendingType : this->content.type;
  643. DynamicTypeHandler* typeHandler = type->GetTypeHandler();
  644. // Inline slot capacity should never grow as a result of shrinking.
  645. Assert(typeHandler->GetInlineSlotCapacity() <= this->content.inlineSlotCount);
  646. // Slot capacity should never grow as a result of shrinking.
  647. Assert(typeHandler->GetSlotCapacity() <= this->content.slotCount);
  648. this->content.slotCount = typeHandler->GetSlotCapacity();
  649. this->content.inlineSlotCount = typeHandler->GetInlineSlotCapacity();
  650. Assert(IsConsistent());
  651. }
  652. void EnableAfterTypeUpdate()
  653. {
  654. Assert(IsConsistent());
  655. Assert(this->content.isPopulated);
  656. Assert(!IsEnabled());
  657. Assert(this->guard.value == CtorCacheGuardValues::Special);
  658. Assert(this->content.typeUpdatePending);
  659. Assert(this->content.slotCount == this->content.pendingType->GetTypeHandler()->GetSlotCapacity());
  660. Assert(this->content.inlineSlotCount == this->content.pendingType->GetTypeHandler()->GetInlineSlotCapacity());
  661. Assert(this->content.pendingType->GetIsShared());
  662. this->content.type = this->content.pendingType;
  663. this->content.typeIsFinal = true;
  664. this->content.pendingType = nullptr;
  665. this->content.typeUpdatePending = false;
  666. Assert(IsConsistent());
  667. }
  668. intptr_t GetRawGuardValue() const
  669. {
  670. return static_cast<intptr_t>(this->guard.value);
  671. }
  672. DynamicType* GetGuardValueAsType() const
  673. {
  674. return reinterpret_cast<DynamicType*>(this->guard.value & ~CtorCacheGuardValues::TagFlag);
  675. }
  676. DynamicType* GetType() const
  677. {
  678. Assert(static_cast<intptr_t>(this->guard.value & CtorCacheGuardValues::TagFlag) == 0);
  679. return this->content.type;
  680. }
  681. DynamicType* GetPendingType() const
  682. {
  683. return this->content.pendingType;
  684. }
  685. ScriptContext* GetScriptContext() const
  686. {
  687. return this->content.scriptContext;
  688. }
  689. int GetSlotCount() const
  690. {
  691. return this->content.slotCount;
  692. }
  693. int16 GetInlineSlotCount() const
  694. {
  695. return this->content.inlineSlotCount;
  696. }
  697. static bool IsDefault(const ConstructorCache* constructorCache)
  698. {
  699. return constructorCache == &ConstructorCache::DefaultInstance;
  700. }
  701. bool IsDefault() const
  702. {
  703. return IsDefault(this);
  704. }
  705. bool IsPopulated() const
  706. {
  707. Assert(IsConsistent());
  708. return this->content.isPopulated;
  709. }
  710. bool IsEmpty() const
  711. {
  712. Assert(IsConsistent());
  713. return !this->content.isPopulated;
  714. }
  715. bool IsPolymorphic() const
  716. {
  717. Assert(IsConsistent());
  718. return this->content.isPolymorphic;
  719. }
  720. bool GetSkipDefaultNewObject() const
  721. {
  722. return this->content.skipDefaultNewObject;
  723. }
  724. bool GetCtorHasNoExplicitReturnValue() const
  725. {
  726. return this->content.ctorHasNoExplicitReturnValue;
  727. }
  728. bool GetUpdateCacheAfterCtor() const
  729. {
  730. return this->content.updateAfterCtor;
  731. }
  732. bool GetTypeUpdatePending() const
  733. {
  734. return this->content.typeUpdatePending;
  735. }
  736. bool IsEnabled() const
  737. {
  738. return GetGuardValueAsType() != nullptr;
  739. }
  740. bool IsInvalidated() const
  741. {
  742. return this->guard.value == CtorCacheGuardValues::Invalid && this->content.isPopulated;
  743. }
  744. bool NeedsTypeUpdate() const
  745. {
  746. return this->guard.value == CtorCacheGuardValues::Special && this->content.typeUpdatePending;
  747. }
  748. uint8 CallCount() const
  749. {
  750. return content.callCount;
  751. }
  752. void IncCallCount()
  753. {
  754. ++content.callCount;
  755. Assert(content.callCount != 0);
  756. }
  757. bool NeedsUpdateAfterCtor() const
  758. {
  759. return this->content.updateAfterCtor;
  760. }
  761. bool IsNormal() const
  762. {
  763. return this->guard.value != CtorCacheGuardValues::Invalid && static_cast<intptr_t>(this->guard.value & CtorCacheGuardValues::TagFlag) == 0;
  764. }
  765. bool SkipDefaultNewObject() const
  766. {
  767. return this->guard.value == CtorCacheGuardValues::Special && this->content.skipDefaultNewObject;
  768. }
  769. bool IsSetUpForJit() const
  770. {
  771. return GetRawGuardValue() != NULL && !IsPolymorphic() && !NeedsUpdateAfterCtor() && (IsNormal() || SkipDefaultNewObject());
  772. }
  773. void ClearUpdateAfterCtor()
  774. {
  775. Assert(IsConsistent());
  776. Assert(this->content.isPopulated);
  777. Assert(this->content.updateAfterCtor);
  778. this->content.updateAfterCtor = false;
  779. Assert(IsConsistent());
  780. }
  781. static ConstructorCache* EnsureValidInstance(ConstructorCache* currentCache, ScriptContext* scriptContext);
  782. const void* GetAddressOfGuardValue() const
  783. {
  784. return reinterpret_cast<const void*>(&this->guard.value);
  785. }
  786. static uint32 GetOffsetOfUpdateAfterCtor()
  787. {
  788. return offsetof(ConstructorCache, content.updateAfterCtor);
  789. }
  790. void InvalidateAsGuard()
  791. {
  792. Assert(!IsDefault(this));
  793. this->guard.value = CtorCacheGuardValues::Invalid;
  794. // Make sure we don't leak the types.
  795. Assert(this->content.type == nullptr);
  796. Assert(this->content.pendingType == nullptr);
  797. Assert(IsInvalidated());
  798. Assert(IsConsistent());
  799. }
  800. #if DBG
  801. bool IsConsistent() const
  802. {
  803. return this->guard.value == CtorCacheGuardValues::Invalid ||
  804. (this->content.isPopulated && (
  805. (this->guard.value == CtorCacheGuardValues::Special && !this->content.updateAfterCtor && this->content.skipDefaultNewObject && !this->content.typeUpdatePending && this->content.slotCount == 0 && this->content.inlineSlotCount == 0 && this->content.pendingType == nullptr) ||
  806. (this->guard.value == CtorCacheGuardValues::Special && !this->content.updateAfterCtor && this->content.typeUpdatePending && !this->content.skipDefaultNewObject && this->content.pendingType != nullptr) ||
  807. ((this->guard.value & CtorCacheGuardValues::TagFlag) == CtorCacheGuardValues::Invalid && !this->content.skipDefaultNewObject && !this->content.typeUpdatePending && this->content.pendingType == nullptr)));
  808. }
  809. #endif
  810. #if DBG_DUMP
  811. void Dump() const;
  812. #endif
  813. private:
  814. void InvalidateOnPrototypeChange();
  815. };
  816. // Caches the result of an instanceof operator over a type and a function
  817. struct IsInstInlineCache
  818. {
  819. Type * type; // The type of object operand an inline cache caches a result for
  820. JavascriptFunction * function; // The function operand an inline cache caches a result for
  821. JavascriptBoolean * result; // The result of doing (object instanceof function) where object->type == this->type
  822. IsInstInlineCache * next; // Used to link together caches that have the same function operand
  823. public:
  824. bool IsEmpty() const { return type == nullptr; }
  825. bool TryGetResult(Var instance, JavascriptFunction * function, JavascriptBoolean ** result);
  826. void Cache(Type * instanceType, JavascriptFunction * function, JavascriptBoolean * result, ScriptContext * scriptContext);
  827. void Unregister(ScriptContext * scriptContext);
  828. static uint32 OffsetOfFunction();
  829. static uint32 OffsetOfResult();
  830. static uint32 OffsetOfType();
  831. private:
  832. void Set(Type * instanceType, JavascriptFunction * function, JavascriptBoolean * result);
  833. void Clear();
  834. };
  835. // Two-entry Type-indexed circular cache
  836. // cache IsConcatSpreadable() result unless user-defined [@@isConcatSpreadable] exists
  837. class IsConcatSpreadableCache
  838. {
  839. Type *type0, *type1;
  840. int lastAccess;
  841. BOOL result0, result1;
  842. public:
  843. IsConcatSpreadableCache() :
  844. type0(nullptr),
  845. type1(nullptr),
  846. result0(FALSE),
  847. result1(FALSE),
  848. lastAccess(1)
  849. {
  850. }
  851. bool TryGetIsConcatSpreadable(Type *type, _Out_ BOOL *result)
  852. {
  853. Assert(type != nullptr);
  854. Assert(result != nullptr);
  855. *result = FALSE;
  856. if (type0 == type)
  857. {
  858. *result = result0;
  859. lastAccess = 0;
  860. return true;
  861. }
  862. if (type1 == type)
  863. {
  864. *result = result1;
  865. lastAccess = 1;
  866. return true;
  867. }
  868. return false;
  869. }
  870. void CacheIsConcatSpreadable(Type *type, BOOL result)
  871. {
  872. Assert(type != nullptr);
  873. if (lastAccess == 0)
  874. {
  875. type1 = type;
  876. result1 = result;
  877. lastAccess = 1;
  878. }
  879. else
  880. {
  881. type0 = type;
  882. result0 = result;
  883. lastAccess = 0;
  884. }
  885. }
  886. void Invalidate()
  887. {
  888. type0 = nullptr;
  889. type1 = nullptr;
  890. }
  891. };
  892. #if defined(_M_IX86_OR_ARM32)
  893. CompileAssert(sizeof(IsInstInlineCache) == 0x10);
  894. #else
  895. CompileAssert(sizeof(IsInstInlineCache) == 0x20);
  896. #endif
  897. }