DynamicProfileInfo.h 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970
  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. // DisableJit-TODO
  7. #if ENABLE_PROFILE_INFO
  8. #ifdef DYNAMIC_PROFILE_MUTATOR
  9. class DynamicProfileMutatorImpl;
  10. #endif
  11. #define PolymorphicInlineCacheUtilizationMinValue 0
  12. #define PolymorphicInlineCacheUtilizationMaxValue 0xFF
  13. #define PolymorphicInlineCacheUtilizationThreshold 0x80
  14. #define PolymorphicInlineCacheUtilizationIncrement 10
  15. #define PolymorphicInlineCacheUtilizationDecrement 1
  16. namespace IR
  17. {
  18. enum BailOutKind : uint
  19. {
  20. #define BAIL_OUT_KIND_LAST(n) n
  21. #define BAIL_OUT_KIND(n, ...) BAIL_OUT_KIND_LAST(n),
  22. #define BAIL_OUT_KIND_VALUE_LAST(n, v) n = v
  23. #define BAIL_OUT_KIND_VALUE(n, v) BAIL_OUT_KIND_VALUE_LAST(n, v),
  24. #include "BailOutKind.h"
  25. };
  26. ENUM_CLASS_HELPERS(BailOutKind, uint);
  27. CompileAssert(BailOutKind::BailOutKindEnd < BailOutKind::BailOutKindBitsStart);
  28. bool IsTypeCheckBailOutKind(BailOutKind kind);
  29. bool IsEquivalentTypeCheckBailOutKind(BailOutKind kind);
  30. BailOutKind EquivalentToMonoTypeCheckBailOutKind(BailOutKind kind);
  31. }
  32. #if ENABLE_DEBUG_CONFIG_OPTIONS
  33. const char *GetBailOutKindName(IR::BailOutKind kind);
  34. bool IsValidBailOutKindAndBits(IR::BailOutKind bailOutKind);
  35. #endif
  36. namespace Js
  37. {
  38. enum CacheType : byte;
  39. enum SlotType : byte;
  40. struct PolymorphicCallSiteInfo;
  41. // Information about dynamic profile information loaded from cache.
  42. // Used to verify whether the loaded information matches the paired function body.
  43. class DynamicProfileFunctionInfo
  44. {
  45. public:
  46. Field(Js::ArgSlot) paramInfoCount;
  47. Field(ProfileId) ldElemInfoCount;
  48. Field(ProfileId) stElemInfoCount;
  49. Field(ProfileId) arrayCallSiteCount;
  50. Field(ProfileId) slotInfoCount;
  51. Field(ProfileId) callSiteInfoCount;
  52. Field(ProfileId) returnTypeInfoCount;
  53. Field(ProfileId) divCount;
  54. Field(ProfileId) switchCount;
  55. Field(uint) loopCount;
  56. Field(uint) fldInfoCount;
  57. };
  58. enum ThisType : BYTE
  59. {
  60. ThisType_Unknown = 0,
  61. ThisType_Simple,
  62. ThisType_Mapped
  63. };
  64. struct ThisInfo
  65. {
  66. Field(ValueType) valueType;
  67. Field(ThisType) thisType;
  68. ThisInfo() : thisType(ThisType_Unknown)
  69. {
  70. }
  71. };
  72. struct CallSiteInfo
  73. {
  74. Field(ValueType) returnType;
  75. Field(uint16) isArgConstant : 13;
  76. Field(uint16) isConstructorCall : 1;
  77. Field(uint16) dontInline : 1;
  78. Field(uint16) isPolymorphic : 1;
  79. Field(InlineCacheIndex) ldFldInlineCacheId;
  80. union _u_type
  81. {
  82. struct
  83. {
  84. Field(Js::SourceId) sourceId;
  85. Field(Js::LocalFunctionId) functionId;
  86. } functionData;
  87. // As of now polymorphic info is allocated only if the source Id is current
  88. Field(PolymorphicCallSiteInfo*) polymorphicCallSiteInfo;
  89. _u_type() {}
  90. } u;
  91. };
  92. // TODO: include ImplicitCallFlags in this structure
  93. struct LoopFlags
  94. {
  95. // maintain the bits and the enum at the same time, it must match
  96. bool isInterpreted : 1;
  97. bool memopMinCountReached : 1;
  98. enum
  99. {
  100. INTERPRETED,
  101. MEMOP_MIN_COUNT_FOUND,
  102. COUNT
  103. };
  104. LoopFlags() :
  105. isInterpreted(false),
  106. memopMinCountReached(false)
  107. {
  108. CompileAssert((sizeof(LoopFlags) * 8) >= LoopFlags::COUNT);
  109. }
  110. // Right now supports up to 8 bits.
  111. typedef byte LoopFlags_t;
  112. LoopFlags(uint64 flags)
  113. {
  114. Assert(flags >> LoopFlags::COUNT == 0);
  115. LoopFlags_t* thisFlags = (LoopFlags_t *)this;
  116. CompileAssert(sizeof(LoopFlags_t) == sizeof(LoopFlags));
  117. *thisFlags = (LoopFlags_t)flags;
  118. }
  119. };
  120. enum FldInfoFlags : BYTE
  121. {
  122. FldInfo_NoInfo = 0x00,
  123. FldInfo_FromLocal = 0x01,
  124. FldInfo_FromProto = 0x02,
  125. FldInfo_FromLocalWithoutProperty = 0x04,
  126. FldInfo_FromAccessor = 0x08,
  127. FldInfo_Polymorphic = 0x10,
  128. FldInfo_FromInlineSlots = 0x20,
  129. FldInfo_FromAuxSlots = 0x40,
  130. FldInfo_InlineCandidate = 0x80
  131. };
  132. struct FldInfo
  133. {
  134. typedef struct { ValueType::TSize f1; byte f2; byte f3; } TSize;
  135. ValueType valueType;
  136. FldInfoFlags flags;
  137. byte polymorphicInlineCacheUtilization;
  138. bool ShouldUsePolymorphicInlineCache()
  139. {
  140. #if DBG
  141. if (PHASE_FORCE1(PolymorphicInlineCachePhase))
  142. {
  143. return true;
  144. }
  145. #endif
  146. return polymorphicInlineCacheUtilization > PolymorphicInlineCacheUtilizationThreshold;
  147. }
  148. bool WasLdFldProfiled() const
  149. {
  150. return !valueType.IsUninitialized();
  151. }
  152. static uint32 GetOffsetOfFlags() { return offsetof(FldInfo, flags); }
  153. };
  154. CompileAssert(sizeof(FldInfo::TSize) == sizeof(FldInfo));
  155. struct LdElemInfo
  156. {
  157. ValueType arrayType;
  158. ValueType elemType;
  159. union
  160. {
  161. struct
  162. {
  163. bool wasProfiled : 1;
  164. bool neededHelperCall : 1;
  165. };
  166. byte bits;
  167. };
  168. LdElemInfo() : bits(0)
  169. {
  170. wasProfiled = true;
  171. }
  172. void Merge(const LdElemInfo &other)
  173. {
  174. arrayType = arrayType.Merge(other.arrayType);
  175. elemType = elemType.Merge(other.elemType);
  176. bits |= other.bits;
  177. }
  178. ValueType GetArrayType() const
  179. {
  180. return arrayType;
  181. }
  182. ValueType GetElementType() const
  183. {
  184. return elemType;
  185. }
  186. bool WasProfiled() const
  187. {
  188. return wasProfiled;
  189. }
  190. bool LikelyNeedsHelperCall() const
  191. {
  192. return neededHelperCall;
  193. }
  194. };
  195. struct StElemInfo
  196. {
  197. ValueType arrayType;
  198. union
  199. {
  200. struct
  201. {
  202. bool wasProfiled : 1;
  203. bool createdMissingValue : 1;
  204. bool filledMissingValue : 1;
  205. bool neededHelperCall : 1;
  206. bool storedOutsideHeadSegmentBounds : 1;
  207. bool storedOutsideArrayBounds : 1;
  208. };
  209. byte bits;
  210. };
  211. StElemInfo() : bits(0)
  212. {
  213. wasProfiled = true;
  214. }
  215. void Merge(const StElemInfo &other)
  216. {
  217. arrayType = arrayType.Merge(other.arrayType);
  218. bits |= other.bits;
  219. }
  220. ValueType GetArrayType() const
  221. {
  222. return arrayType;
  223. }
  224. bool WasProfiled() const
  225. {
  226. return wasProfiled;
  227. }
  228. bool LikelyCreatesMissingValue() const
  229. {
  230. return createdMissingValue;
  231. }
  232. bool LikelyFillsMissingValue() const
  233. {
  234. return filledMissingValue;
  235. }
  236. bool LikelyNeedsHelperCall() const
  237. {
  238. return createdMissingValue || filledMissingValue || neededHelperCall || storedOutsideHeadSegmentBounds;
  239. }
  240. bool LikelyStoresOutsideHeadSegmentBounds() const
  241. {
  242. return createdMissingValue || storedOutsideHeadSegmentBounds;
  243. }
  244. bool LikelyStoresOutsideArrayBounds() const
  245. {
  246. return storedOutsideArrayBounds;
  247. }
  248. };
  249. struct ArrayCallSiteInfo
  250. {
  251. union {
  252. struct {
  253. byte isNotNativeInt : 1;
  254. byte isNotNativeFloat : 1;
  255. #if ENABLE_COPYONACCESS_ARRAY
  256. byte isNotCopyOnAccessArray : 1;
  257. byte copyOnAccessArrayCacheIndex : 5;
  258. #endif
  259. };
  260. byte bits;
  261. };
  262. #if DBG
  263. uint functionNumber;
  264. ProfileId callSiteNumber;
  265. #endif
  266. bool IsNativeIntArray() const { return !(bits & NotNativeIntBit) && !PHASE_OFF1(NativeArrayPhase); }
  267. bool IsNativeFloatArray() const { return !(bits & NotNativeFloatBit) && !PHASE_OFF1(NativeArrayPhase); }
  268. bool IsNativeArray() const { return IsNativeFloatArray(); }
  269. void SetIsNotNativeIntArray();
  270. void SetIsNotNativeFloatArray();
  271. void SetIsNotNativeArray();
  272. static uint32 GetOffsetOfBits() { return offsetof(ArrayCallSiteInfo, bits); }
  273. static byte const NotNativeIntBit = 1;
  274. static byte const NotNativeFloatBit = 2;
  275. };
  276. class DynamicProfileInfo;
  277. typedef SListBase<DynamicProfileInfo*, Recycler> DynamicProfileInfoList;
  278. class DynamicProfileInfo
  279. {
  280. public:
  281. static DynamicProfileInfo* New(Recycler* recycler, FunctionBody* functionBody, bool persistsAcrossScriptContexts = false);
  282. void Initialize(FunctionBody *const functionBody);
  283. public:
  284. static bool IsEnabledForAtLeastOneFunction(const ScriptContext *const scriptContext);
  285. static bool IsEnabled(const FunctionBody *const functionBody);
  286. private:
  287. static bool IsEnabled_OptionalFunctionBody(const FunctionBody *const functionBody, const ScriptContext *const scriptContext);
  288. public:
  289. static bool IsEnabledForAtLeastOneFunction(const Js::Phase phase, const ScriptContext *const scriptContext);
  290. static bool IsEnabled(const Js::Phase phase, const FunctionBody *const functionBody);
  291. private:
  292. static bool IsEnabled_OptionalFunctionBody(const Js::Phase phase, const FunctionBody *const functionBody, const ScriptContext *const scriptContext);
  293. public:
  294. static bool EnableImplicitCallFlags(const FunctionBody *const functionBody);
  295. static Var EnsureDynamicProfileInfoThunk(RecyclableObject * function, CallInfo callInfo, ...);
  296. #ifdef DYNAMIC_PROFILE_STORAGE
  297. bool HasFunctionBody() const { return hasFunctionBody; }
  298. FunctionBody * GetFunctionBody() const { Assert(hasFunctionBody); return functionBody; }
  299. #endif
  300. void RecordElementLoad(FunctionBody* functionBody, ProfileId ldElemId, const LdElemInfo& info);
  301. void RecordElementLoadAsProfiled(FunctionBody *const functionBody, const ProfileId ldElemId);
  302. const LdElemInfo *GetLdElemInfo() const { return ldElemInfo; }
  303. void RecordElementStore(FunctionBody* functionBody, ProfileId stElemId, const StElemInfo& info);
  304. void RecordElementStoreAsProfiled(FunctionBody *const functionBody, const ProfileId stElemId);
  305. const StElemInfo *GetStElemInfo() const { return stElemInfo; }
  306. ArrayCallSiteInfo *GetArrayCallSiteInfo(FunctionBody *functionBody, ProfileId index) const;
  307. ArrayCallSiteInfo *GetArrayCallSiteInfo() const { return arrayCallSiteInfo; }
  308. void RecordFieldAccess(FunctionBody* functionBody, uint fieldAccessId, Var object, FldInfoFlags flags);
  309. void RecordPolymorphicFieldAccess(FunctionBody *functionBody, uint fieldAccessid);
  310. bool HasPolymorphicFldAccess() const { return bits.hasPolymorphicFldAccess; }
  311. FldInfo * GetFldInfo(FunctionBody* functionBody, uint fieldAccessId) const;
  312. FldInfo * GetFldInfo() const { return fldInfo; }
  313. void RecordSlotLoad(FunctionBody* functionBody, ProfileId slotLoadId, Var object);
  314. ValueType GetSlotLoad(FunctionBody* functionBody, ProfileId slotLoadId) const;
  315. ValueType * GetSlotInfo() const { return slotInfo; }
  316. void RecordThisInfo(Var object, ThisType thisType);
  317. ThisInfo GetThisInfo() const;
  318. void RecordDivideResultType(FunctionBody* body, ProfileId divideId, Var object);
  319. ValueType GetDivideResultType(FunctionBody* body, ProfileId divideId) const;
  320. ValueType * GetDivideTypeInfo() const { return divideTypeInfo; }
  321. void RecordModulusOpType(FunctionBody* body, ProfileId profileId, bool isModByPowerOf2);
  322. bool IsModulusOpByPowerOf2(FunctionBody* body, ProfileId profileId) const;
  323. void RecordSwitchType(FunctionBody* body, ProfileId switchId, Var object);
  324. ValueType GetSwitchType(FunctionBody* body, ProfileId switchId) const;
  325. ValueType * GetSwitchTypeInfo() const { return switchTypeInfo; }
  326. void RecordCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, FunctionInfo * calleeFunctionInfo, JavascriptFunction* calleeFunction, ArgSlot actualArgCount, bool isConstructorCall, InlineCacheIndex ldFldInlineCacheId = Js::Constants::NoInlineCacheIndex);
  327. void RecordConstParameterAtCallSite(ProfileId callSiteId, int argNum);
  328. static bool HasCallSiteInfo(FunctionBody* functionBody);
  329. bool HasCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId); // Does a particular callsite have ProfileInfo?
  330. FunctionInfo * GetCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, bool *isConstructorCall, bool *isPolymorphicCall);
  331. CallSiteInfo * GetCallSiteInfo() const { return callSiteInfo; }
  332. uint16 GetConstantArgInfo(ProfileId callSiteId);
  333. uint GetLdFldCacheIndexFromCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId);
  334. bool GetPolymorphicCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, bool *isConstructorCall, __inout_ecount(functionBodyArrayLength) FunctionBody** functionBodyArray, uint functionBodyArrayLength);
  335. bool RecordLdFldCallSiteInfo(FunctionBody* functionBody, RecyclableObject* callee, bool callApplyTarget);
  336. bool HasLdFldCallSiteInfo() const;
  337. void RecordReturnTypeOnCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, Var object);
  338. void RecordReturnType(FunctionBody* functionBody, ProfileId callSiteId, Var object);
  339. ValueType GetReturnType(FunctionBody* functionBody, Js::OpCode opcode, ProfileId callSiteId) const;
  340. ValueType * GetReturnTypeInfo() const { return returnTypeInfo; }
  341. void RecordParameterInfo(FunctionBody* functionBody, ArgSlot index, Var object);
  342. ValueType GetParameterInfo(FunctionBody* functionBody, ArgSlot index) const;
  343. ValueType * GetParameterInfo() const { return parameterInfo; }
  344. void RecordLoopImplicitCallFlags(FunctionBody* functionBody, uint loopNum, ImplicitCallFlags flags);
  345. ImplicitCallFlags GetLoopImplicitCallFlags(FunctionBody* functionBody, uint loopNum) const;
  346. ImplicitCallFlags * GetLoopImplicitCallFlags() const { return loopImplicitCallFlags; }
  347. void RecordImplicitCallFlags(ImplicitCallFlags flags);
  348. ImplicitCallFlags GetImplicitCallFlags() const;
  349. static void Save(ScriptContext * scriptContext);
  350. void UpdateFunctionInfo(FunctionBody* functionBody, Recycler* allocator);
  351. void ResetAllPolymorphicCallSiteInfo();
  352. bool CallSiteHasProfileData(ProfileId callSiteId)
  353. {
  354. return this->callSiteInfo[callSiteId].isPolymorphic
  355. || this->callSiteInfo[callSiteId].u.functionData.sourceId != NoSourceId
  356. || this->callSiteInfo[callSiteId].dontInline;
  357. }
  358. static bool IsProfiledCallOp(OpCode op);
  359. static bool IsProfiledReturnTypeOp(OpCode op);
  360. static FldInfoFlags FldInfoFlagsFromCacheType(CacheType cacheType);
  361. static FldInfoFlags FldInfoFlagsFromSlotType(SlotType slotType);
  362. static FldInfoFlags MergeFldInfoFlags(FldInfoFlags oldFlags, FldInfoFlags newFlags);
  363. const static uint maxPolymorphicInliningSize = 4;
  364. #if DBG_DUMP
  365. static void DumpScriptContext(ScriptContext * scriptContext);
  366. static char16 const * GetImplicitCallFlagsString(ImplicitCallFlags flags);
  367. #endif
  368. #ifdef RUNTIME_DATA_COLLECTION
  369. static void DumpScriptContextToFile(ScriptContext * scriptContext);
  370. #endif
  371. #if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
  372. static bool NeedProfileInfoList();
  373. #endif
  374. #ifdef DYNAMIC_PROFILE_MUTATOR
  375. friend class DynamicProfileMutatorImpl;
  376. #endif
  377. #if JS_PROFILE_DATA_INTERFACE
  378. friend class ProfileDataObject;
  379. #endif
  380. private:
  381. // Have the dynamicProfileFunctionInfo after loaded from cache.
  382. // Replaced with the function body it is verified and matched (See DynamicProfileInfo::MatchFunctionBody)
  383. Field(DynamicProfileFunctionInfo *) dynamicProfileFunctionInfo;
  384. Field(CallSiteInfo *) callSiteInfo;
  385. Field(ValueType *) returnTypeInfo; // return type of calls for non inline call sites
  386. Field(ValueType *) divideTypeInfo;
  387. Field(ValueType *) switchTypeInfo;
  388. Field(LdElemInfo *) ldElemInfo;
  389. Field(StElemInfo *) stElemInfo;
  390. Field(ArrayCallSiteInfo *) arrayCallSiteInfo;
  391. Field(ValueType *) parameterInfo;
  392. Field(FldInfo *) fldInfo;
  393. Field(ValueType *) slotInfo;
  394. Field(ImplicitCallFlags *) loopImplicitCallFlags;
  395. Field(ImplicitCallFlags) implicitCallFlags;
  396. Field(BVFixed*) loopFlags;
  397. Field(ThisInfo) thisInfo;
  398. // TODO (jedmiad): Consider storing a pair of property ID bit vectors indicating which properties are
  399. // known to be non-fixed or non-equivalent. We could turn these on if we bailed out of fixed field type
  400. // checks and equivalent type checks in a way that indicates one of these failures as opposed to type
  401. // mismatch.
  402. struct Bits
  403. {
  404. Field(bool) disableAggressiveIntTypeSpec : 1;
  405. Field(bool) disableAggressiveIntTypeSpec_jitLoopBody : 1;
  406. Field(bool) disableAggressiveMulIntTypeSpec : 1;
  407. Field(bool) disableAggressiveMulIntTypeSpec_jitLoopBody : 1;
  408. Field(bool) disableDivIntTypeSpec : 1;
  409. Field(bool) disableDivIntTypeSpec_jitLoopBody : 1;
  410. Field(bool) disableLossyIntTypeSpec : 1;
  411. // TODO: put this flag in LoopFlags if we can find a reliable way to determine the loopNumber in bailout for a hoisted instr
  412. Field(bool) disableMemOp : 1;
  413. Field(bool) disableTrackCompoundedIntOverflow : 1;
  414. Field(bool) disableFloatTypeSpec : 1;
  415. Field(bool) disableCheckThis : 1;
  416. Field(bool) disableArrayCheckHoist : 1;
  417. Field(bool) disableArrayCheckHoist_jitLoopBody : 1;
  418. Field(bool) disableArrayMissingValueCheckHoist : 1;
  419. Field(bool) disableArrayMissingValueCheckHoist_jitLoopBody : 1;
  420. Field(bool) disableJsArraySegmentHoist : 1;
  421. Field(bool) disableJsArraySegmentHoist_jitLoopBody : 1;
  422. Field(bool) disableArrayLengthHoist : 1;
  423. Field(bool) disableArrayLengthHoist_jitLoopBody : 1;
  424. Field(bool) disableTypedArrayTypeSpec : 1;
  425. Field(bool) disableTypedArrayTypeSpec_jitLoopBody : 1;
  426. Field(bool) disableLdLenIntSpec : 1;
  427. Field(bool) disableBoundCheckHoist : 1;
  428. Field(bool) disableBoundCheckHoist_jitLoopBody : 1;
  429. Field(bool) disableLoopCountBasedBoundCheckHoist : 1;
  430. Field(bool) disableLoopCountBasedBoundCheckHoist_jitLoopBody : 1;
  431. Field(bool) hasPolymorphicFldAccess : 1;
  432. Field(bool) hasLdFldCallSite : 1; // getters, setters, .apply (possibly .call too in future)
  433. Field(bool) disableFloorInlining : 1;
  434. Field(bool) disableNoProfileBailouts : 1;
  435. Field(bool) disableSwitchOpt : 1;
  436. Field(bool) disableEquivalentObjTypeSpec : 1;
  437. Field(bool) disableObjTypeSpec_jitLoopBody : 1;
  438. Field(bool) disablePowIntIntTypeSpec : 1;
  439. Field(bool) disableLoopImplicitCallInfo : 1;
  440. Field(bool) disableStackArgOpt : 1;
  441. Field(bool) disableTagCheck : 1;
  442. };
  443. Field(Bits) bits;
  444. Field(uint32) m_recursiveInlineInfo; // Bit is set for each callsites where the function is called recursively
  445. Field(uint32) polymorphicCacheState;
  446. Field(uint32) bailOutOffsetForLastRejit;
  447. Field(bool) hasFunctionBody; // this is likely 1, try avoid 4-byte GC force reference
  448. Field(BYTE) currentInlinerVersion; // Used to detect when inlining profile changes
  449. Field(uint16) rejitCount;
  450. #if DBG
  451. Field(bool) persistsAcrossScriptContexts;
  452. #endif
  453. static JavascriptMethod EnsureDynamicProfileInfo(Js::ScriptFunction * function);
  454. #if DBG_DUMP
  455. static void DumpList(DynamicProfileInfoList * profileInfoList, ArenaAllocator * dynamicProfileInfoAllocator);
  456. static void DumpProfiledValue(char16 const * name, uint * value, uint count);
  457. static void DumpProfiledValue(char16 const * name, ValueType * value, uint count);
  458. static void DumpProfiledValue(char16 const * name, CallSiteInfo * callSiteInfo, uint count);
  459. static void DumpProfiledValue(char16 const * name, ArrayCallSiteInfo * arrayCallSiteInfo, uint count);
  460. static void DumpProfiledValue(char16 const * name, ImplicitCallFlags * loopImplicitCallFlags, uint count);
  461. template<class TData, class FGetValueType>
  462. static void DumpProfiledValuesGroupedByValue(const char16 *const name, const TData *const data, const uint count, const FGetValueType GetValueType, ArenaAllocator *const dynamicProfileInfoAllocator);
  463. static void DumpFldInfoFlags(char16 const * name, FldInfo * fldInfo, uint count, FldInfoFlags value, char16 const * valueName);
  464. static void DumpLoopInfo(FunctionBody *fbody);
  465. #endif
  466. bool IsPolymorphicCallSite(Js::LocalFunctionId curFunctionId, Js::SourceId curSourceId, Js::LocalFunctionId oldFunctionId, Js::SourceId oldSourceId);
  467. void CreatePolymorphicDynamicProfileCallSiteInfo(FunctionBody * funcBody, ProfileId callSiteId, Js::LocalFunctionId functionId, Js::LocalFunctionId oldFunctionId, Js::SourceId sourceId, Js::SourceId oldSourceId);
  468. void ResetPolymorphicCallSiteInfo(ProfileId callSiteId, Js::LocalFunctionId functionId);
  469. void SetFunctionIdSlotForNewPolymorphicCall(ProfileId callSiteId, Js::LocalFunctionId curFunctionId, Js::SourceId curSourceId, Js::FunctionBody *inliner);
  470. void RecordPolymorphicCallSiteInfo(FunctionBody* functionBody, ProfileId callSiteId, FunctionInfo * calleeFunctionInfo);
  471. #ifdef RUNTIME_DATA_COLLECTION
  472. static CriticalSection s_csOutput;
  473. template <typename T>
  474. static void WriteData(const T& data, FILE * file);
  475. #if defined(_MSC_VER) && !defined(__clang__)
  476. template <>
  477. static void WriteData<char16 const *>(char16 const * const& sz, FILE * file);
  478. template <>
  479. static void WriteData<FunctionInfo *>(FunctionInfo * const& functionInfo, FILE * file); // Not defined, to prevent accidentally writing function info
  480. template <>
  481. static void WriteData<FunctionBody *>(FunctionBody * const& functionInfo, FILE * file);
  482. #endif
  483. template <typename T>
  484. static void WriteArray(uint count, T * arr, FILE * file);
  485. template <typename T>
  486. static void WriteArray(uint count, WriteBarrierPtr<T> arr, FILE * file);
  487. #endif
  488. #if DBG_DUMP || defined(DYNAMIC_PROFILE_STORAGE) || defined(RUNTIME_DATA_COLLECTION)
  489. Field(FunctionBody *) functionBody; // This will only be populated if NeedProfileInfoList is true
  490. #endif
  491. #ifdef DYNAMIC_PROFILE_STORAGE
  492. // Used by de-serialize
  493. DynamicProfileInfo();
  494. template <typename T>
  495. static DynamicProfileInfo * Deserialize(T * reader, Recycler* allocator, Js::LocalFunctionId * functionId);
  496. template <typename T>
  497. bool Serialize(T * writer);
  498. static void UpdateSourceDynamicProfileManagers(ScriptContext * scriptContext);
  499. #endif
  500. static Js::LocalFunctionId const CallSiteMixed = (Js::LocalFunctionId)-1;
  501. static Js::LocalFunctionId const CallSiteCrossContext = (Js::LocalFunctionId)-2;
  502. static Js::LocalFunctionId const CallSiteNonFunction = (Js::LocalFunctionId)-3;
  503. static Js::LocalFunctionId const CallSiteNoInfo = (Js::LocalFunctionId)-4;
  504. static Js::LocalFunctionId const StartInvalidFunction = (Js::LocalFunctionId)-4;
  505. static Js::SourceId const NoSourceId = (SourceId)-1;
  506. static Js::SourceId const BuiltInSourceId = (SourceId)-2;
  507. static Js::SourceId const CurrentSourceId = (SourceId)-3; // caller and callee in the same file
  508. static Js::SourceId const InvalidSourceId = (SourceId)-4;
  509. bool MatchFunctionBody(FunctionBody * functionBody);
  510. DynamicProfileInfo(FunctionBody * functionBody);
  511. friend class SourceDynamicProfileManager;
  512. public:
  513. bool IsAggressiveIntTypeSpecDisabled(const bool isJitLoopBody) const
  514. {
  515. return
  516. isJitLoopBody
  517. ? this->bits.disableAggressiveIntTypeSpec_jitLoopBody
  518. : this->bits.disableAggressiveIntTypeSpec;
  519. }
  520. void DisableAggressiveIntTypeSpec(const bool isJitLoopBody)
  521. {
  522. this->bits.disableAggressiveIntTypeSpec_jitLoopBody = true;
  523. if (!isJitLoopBody)
  524. {
  525. this->bits.disableAggressiveIntTypeSpec = true;
  526. }
  527. }
  528. bool IsAggressiveMulIntTypeSpecDisabled(const bool isJitLoopBody) const
  529. {
  530. return
  531. isJitLoopBody
  532. ? this->bits.disableAggressiveMulIntTypeSpec_jitLoopBody
  533. : this->bits.disableAggressiveMulIntTypeSpec;
  534. }
  535. void DisableAggressiveMulIntTypeSpec(const bool isJitLoopBody)
  536. {
  537. this->bits.disableAggressiveMulIntTypeSpec_jitLoopBody = true;
  538. if (!isJitLoopBody)
  539. {
  540. this->bits.disableAggressiveMulIntTypeSpec = true;
  541. }
  542. }
  543. bool IsDivIntTypeSpecDisabled(const bool isJitLoopBody) const
  544. {
  545. return
  546. isJitLoopBody
  547. ? this->bits.disableDivIntTypeSpec_jitLoopBody
  548. : this->bits.disableDivIntTypeSpec;
  549. }
  550. void DisableDivIntTypeSpec(const bool isJitLoopBody)
  551. {
  552. this->bits.disableDivIntTypeSpec_jitLoopBody = true;
  553. if (!isJitLoopBody)
  554. {
  555. this->bits.disableDivIntTypeSpec = true;
  556. }
  557. }
  558. bool IsLossyIntTypeSpecDisabled() const { return bits.disableLossyIntTypeSpec; }
  559. void DisableLossyIntTypeSpec() { this->bits.disableLossyIntTypeSpec = true; }
  560. LoopFlags GetLoopFlags(int loopNumber) const
  561. {
  562. Assert(loopFlags);
  563. return loopFlags->GetRange<LoopFlags>(loopNumber * LoopFlags::COUNT, LoopFlags::COUNT);
  564. }
  565. BVFixed * GetLoopFlags() const { return loopFlags; }
  566. void SetLoopInterpreted(int loopNumber) { loopFlags->Set(loopNumber * LoopFlags::COUNT + LoopFlags::INTERPRETED); }
  567. void SetMemOpMinReached(int loopNumber) { loopFlags->Set(loopNumber * LoopFlags::COUNT + LoopFlags::MEMOP_MIN_COUNT_FOUND); }
  568. bool IsMemOpDisabled() const { return this->bits.disableMemOp; }
  569. void DisableMemOp() { this->bits.disableMemOp = true; }
  570. bool IsTrackCompoundedIntOverflowDisabled() const { return this->bits.disableTrackCompoundedIntOverflow; }
  571. void DisableTrackCompoundedIntOverflow() { this->bits.disableTrackCompoundedIntOverflow = true; }
  572. bool IsFloatTypeSpecDisabled() const { return this->bits.disableFloatTypeSpec; }
  573. void DisableFloatTypeSpec() { this->bits.disableFloatTypeSpec = true; }
  574. bool IsCheckThisDisabled() const { return this->bits.disableCheckThis; }
  575. void DisableCheckThis() { this->bits.disableCheckThis = true; }
  576. bool IsLoopImplicitCallInfoDisabled() const { return this->bits.disableLoopImplicitCallInfo; }
  577. void DisableLoopImplicitCallInfo() { this->bits.disableLoopImplicitCallInfo = true; }
  578. bool IsArrayCheckHoistDisabled(const bool isJitLoopBody) const
  579. {
  580. return
  581. isJitLoopBody
  582. ? this->bits.disableArrayCheckHoist_jitLoopBody
  583. : this->bits.disableArrayCheckHoist;
  584. }
  585. void DisableArrayCheckHoist(const bool isJitLoopBody)
  586. {
  587. this->bits.disableArrayCheckHoist_jitLoopBody = true;
  588. if (!isJitLoopBody)
  589. {
  590. this->bits.disableArrayCheckHoist = true;
  591. }
  592. }
  593. bool IsArrayMissingValueCheckHoistDisabled(const bool isJitLoopBody) const
  594. {
  595. return
  596. isJitLoopBody
  597. ? this->bits.disableArrayMissingValueCheckHoist_jitLoopBody
  598. : this->bits.disableArrayMissingValueCheckHoist;
  599. }
  600. void DisableArrayMissingValueCheckHoist(const bool isJitLoopBody)
  601. {
  602. this->bits.disableArrayMissingValueCheckHoist_jitLoopBody = true;
  603. if (!isJitLoopBody)
  604. {
  605. this->bits.disableArrayMissingValueCheckHoist = true;
  606. }
  607. }
  608. bool IsJsArraySegmentHoistDisabled(const bool isJitLoopBody) const
  609. {
  610. return
  611. isJitLoopBody
  612. ? this->bits.disableJsArraySegmentHoist_jitLoopBody
  613. : this->bits.disableJsArraySegmentHoist;
  614. }
  615. void DisableJsArraySegmentHoist(const bool isJitLoopBody)
  616. {
  617. this->bits.disableJsArraySegmentHoist_jitLoopBody = true;
  618. if (!isJitLoopBody)
  619. {
  620. this->bits.disableJsArraySegmentHoist = true;
  621. }
  622. }
  623. bool IsArrayLengthHoistDisabled(const bool isJitLoopBody) const
  624. {
  625. return
  626. isJitLoopBody
  627. ? this->bits.disableArrayLengthHoist_jitLoopBody
  628. : this->bits.disableArrayLengthHoist;
  629. }
  630. void DisableArrayLengthHoist(const bool isJitLoopBody)
  631. {
  632. this->bits.disableArrayLengthHoist_jitLoopBody = true;
  633. if (!isJitLoopBody)
  634. {
  635. this->bits.disableArrayLengthHoist = true;
  636. }
  637. }
  638. bool IsTypedArrayTypeSpecDisabled(const bool isJitLoopBody) const
  639. {
  640. return
  641. isJitLoopBody
  642. ? this->bits.disableTypedArrayTypeSpec_jitLoopBody
  643. : this->bits.disableTypedArrayTypeSpec;
  644. }
  645. void DisableTypedArrayTypeSpec(const bool isJitLoopBody)
  646. {
  647. this->bits.disableTypedArrayTypeSpec_jitLoopBody = true;
  648. if (!isJitLoopBody)
  649. {
  650. this->bits.disableTypedArrayTypeSpec = true;
  651. }
  652. }
  653. bool IsLdLenIntSpecDisabled() const { return this->bits.disableLdLenIntSpec; }
  654. void DisableLdLenIntSpec() { this->bits.disableLdLenIntSpec = true; }
  655. bool IsBoundCheckHoistDisabled(const bool isJitLoopBody) const
  656. {
  657. return
  658. isJitLoopBody
  659. ? this->bits.disableBoundCheckHoist_jitLoopBody
  660. : this->bits.disableBoundCheckHoist;
  661. }
  662. void DisableBoundCheckHoist(const bool isJitLoopBody)
  663. {
  664. this->bits.disableBoundCheckHoist_jitLoopBody = true;
  665. if (!isJitLoopBody)
  666. {
  667. this->bits.disableBoundCheckHoist = true;
  668. }
  669. }
  670. bool IsLoopCountBasedBoundCheckHoistDisabled(const bool isJitLoopBody) const
  671. {
  672. return
  673. isJitLoopBody
  674. ? this->bits.disableLoopCountBasedBoundCheckHoist_jitLoopBody
  675. : this->bits.disableLoopCountBasedBoundCheckHoist;
  676. }
  677. void DisableLoopCountBasedBoundCheckHoist(const bool isJitLoopBody)
  678. {
  679. this->bits.disableLoopCountBasedBoundCheckHoist_jitLoopBody = true;
  680. if (!isJitLoopBody)
  681. {
  682. this->bits.disableLoopCountBasedBoundCheckHoist = true;
  683. }
  684. }
  685. BYTE GetInlinerVersion() { return this->currentInlinerVersion; }
  686. uint32 GetPolymorphicCacheState() const { return this->polymorphicCacheState; }
  687. uint32 GetRecursiveInlineInfo() const { return this->m_recursiveInlineInfo; }
  688. void SetHasNewPolyFieldAccess(FunctionBody *functionBody);
  689. bool IsFloorInliningDisabled() const { return this->bits.disableFloorInlining; }
  690. void DisableFloorInlining() { this->bits.disableFloorInlining = true; }
  691. bool IsNoProfileBailoutsDisabled() const { return this->bits.disableNoProfileBailouts; }
  692. void DisableNoProfileBailouts() { this->bits.disableNoProfileBailouts = true; }
  693. bool IsSwitchOptDisabled() const { return this->bits.disableSwitchOpt; }
  694. void DisableSwitchOpt() { this->bits.disableSwitchOpt = true; }
  695. bool IsStackArgOptDisabled() const { return this->bits.disableStackArgOpt; }
  696. void DisableStackArgOpt() { this->bits.disableStackArgOpt = true; }
  697. bool IsEquivalentObjTypeSpecDisabled() const { return this->bits.disableEquivalentObjTypeSpec; }
  698. void DisableEquivalentObjTypeSpec() { this->bits.disableEquivalentObjTypeSpec = true; }
  699. bool IsObjTypeSpecDisabledInJitLoopBody() const { return this->bits.disableObjTypeSpec_jitLoopBody; }
  700. void DisableObjTypeSpecInJitLoopBody() { this->bits.disableObjTypeSpec_jitLoopBody = true; }
  701. bool IsPowIntIntTypeSpecDisabled() const { return bits.disablePowIntIntTypeSpec; }
  702. void DisablePowIntIntTypeSpec() { this->bits.disablePowIntIntTypeSpec = true; }
  703. bool IsTagCheckDisabled() const { return bits.disableTagCheck; }
  704. void DisableTagCheck() { this->bits.disableTagCheck = true; }
  705. static bool IsCallSiteNoInfo(Js::LocalFunctionId functionId) { return functionId == CallSiteNoInfo; }
  706. int IncRejitCount() { return this->rejitCount++; }
  707. int GetRejitCount() { return this->rejitCount; }
  708. void SetBailOutOffsetForLastRejit(uint32 offset) { this->bailOutOffsetForLastRejit = offset; }
  709. uint32 GetBailOutOffsetForLastRejit() { return this->bailOutOffsetForLastRejit; }
  710. #if DBG_DUMP
  711. void Dump(FunctionBody* functionBody, ArenaAllocator * dynamicProfileInfoAllocator = nullptr);
  712. #endif
  713. private:
  714. static void InstantiateForceInlinedMembers();
  715. };
  716. struct PolymorphicCallSiteInfo
  717. {
  718. Field(Js::LocalFunctionId) functionIds[DynamicProfileInfo::maxPolymorphicInliningSize];
  719. Field(Js::SourceId) sourceIds[DynamicProfileInfo::maxPolymorphicInliningSize];
  720. Field(PolymorphicCallSiteInfo *) next;
  721. bool GetFunction(uint index, Js::LocalFunctionId *functionId, Js::SourceId *sourceId)
  722. {
  723. Assert(index < DynamicProfileInfo::maxPolymorphicInliningSize);
  724. Assert(functionId);
  725. Assert(sourceId);
  726. if (DynamicProfileInfo::IsCallSiteNoInfo(functionIds[index]))
  727. {
  728. return false;
  729. }
  730. *functionId = functionIds[index];
  731. *sourceId = sourceIds[index];
  732. return true;
  733. }
  734. };
  735. #ifdef DYNAMIC_PROFILE_STORAGE
  736. class BufferReader
  737. {
  738. public:
  739. BufferReader(__in_ecount(length) char const * buffer, size_t length) : current(buffer), lengthLeft(length) {}
  740. template <typename T>
  741. bool Read(T * data)
  742. {
  743. if (lengthLeft < sizeof(T))
  744. {
  745. return false;
  746. }
  747. *data = *(T *)current;
  748. current += sizeof(T);
  749. lengthLeft -= sizeof(T);
  750. return true;
  751. }
  752. template <typename T>
  753. bool Peek(T * data)
  754. {
  755. if (lengthLeft < sizeof(T))
  756. {
  757. return false;
  758. }
  759. *data = *(T *)current;
  760. return true;
  761. }
  762. template <typename T>
  763. bool ReadArray(__inout_ecount(len) T * data, size_t len)
  764. {
  765. size_t size = sizeof(T) * len;
  766. if (lengthLeft < size)
  767. {
  768. return false;
  769. }
  770. memcpy_s(data, size, current, size);
  771. current += size;
  772. lengthLeft -= size;
  773. return true;
  774. }
  775. private:
  776. char const * current;
  777. size_t lengthLeft;
  778. };
  779. class BufferSizeCounter
  780. {
  781. public:
  782. BufferSizeCounter() : count(0) {}
  783. size_t GetByteCount() const { return count; }
  784. template <typename T>
  785. bool Write(T const& data)
  786. {
  787. return WriteArray(&data, 1);
  788. }
  789. #if DBG_DUMP
  790. void Log(DynamicProfileInfo* info) {}
  791. #endif
  792. template <typename T>
  793. bool WriteArray(__in_ecount(len) T * data, size_t len)
  794. {
  795. count += sizeof(T) * len;
  796. return true;
  797. }
  798. template <typename T>
  799. bool WriteArray(WriteBarrierPtr<T> data, size_t len)
  800. {
  801. return WriteArray(static_cast<T*>(data), len);
  802. }
  803. private:
  804. size_t count;
  805. };
  806. class BufferWriter
  807. {
  808. public:
  809. BufferWriter(__in_ecount(length) char * buffer, size_t length) : current(buffer), lengthLeft(length) {}
  810. template <typename T>
  811. bool Write(T const& data)
  812. {
  813. return WriteArray(&data, 1);
  814. }
  815. #if DBG_DUMP
  816. void Log(DynamicProfileInfo* info);
  817. #endif
  818. template <typename T>
  819. bool WriteArray(__in_ecount(len) T * data, size_t len)
  820. {
  821. size_t size = sizeof(T) * len;
  822. if (lengthLeft < size)
  823. {
  824. return false;
  825. }
  826. memcpy_s(current, size, data, size);
  827. current += size;
  828. lengthLeft -= size;
  829. return true;
  830. }
  831. template <typename T>
  832. bool WriteArray(WriteBarrierPtr<T> data, size_t len)
  833. {
  834. return WriteArray(static_cast<T*>(data), len);
  835. }
  836. private:
  837. char * current;
  838. size_t lengthLeft;
  839. };
  840. #endif
  841. };
  842. #endif