TTSupport.h 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
  4. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  5. //-------------------------------------------------------------------------------------------------------
  6. #pragma once
  7. //This file contains definitions for general-ish purpose structures/algorithms that we use in the TTD system
  8. //We may want to replace them with other versions (e.g. that are already in the codebase) at some later time
  9. #if ENABLE_TTD
  10. class HostScriptContextCallbackFunctor;
  11. namespace TTD
  12. {
  13. class ThreadContextTTD;
  14. class ScriptContextTTD;
  15. class RuntimeContextInfo;
  16. class ExecutionInfoManager;
  17. //We typedef Js::Var into a TTD version that has the same bit layout but we want to avoid confusion
  18. //if this bit layout is for the "live" state or potentially only for the snapshot state or the representations change later
  19. typedef Js::Var TTDVar;
  20. namespace NSSnapType
  21. {
  22. struct SnapPropertyRecord;
  23. struct SnapHandlerPropertyEntry;
  24. struct SnapHandler;
  25. struct SnapType;
  26. }
  27. namespace NSSnapValues
  28. {
  29. struct SnapPrimitiveValue;
  30. struct SlotArrayInfo;
  31. struct ScriptFunctionScopeInfo;
  32. struct SnapFunctionBodyScopeChain;
  33. struct TopLevelScriptLoadFunctionBodyResolveInfo;
  34. struct TopLevelNewFunctionBodyResolveInfo;
  35. struct TopLevelEvalFunctionBodyResolveInfo;
  36. struct FunctionBodyResolveInfo;
  37. struct SnapContext;
  38. }
  39. namespace NSSnapObjects
  40. {
  41. struct SnapObject;
  42. }
  43. class SnapShot;
  44. class SnapshotExtractor;
  45. class TTDExceptionFramePopper;
  46. struct SingleCallCounter;
  47. namespace NSLogEvents
  48. {
  49. struct EventLogEntry;
  50. }
  51. class EventLog;
  52. class TTDebuggerAbortException;
  53. class TTDebuggerSourceLocation;
  54. }
  55. void _NOINLINE __declspec(noreturn) TTDAbort_unrecoverable_error(const char* msg);
  56. ////////
  57. //Memory allocators used by the TT code
  58. template <typename T>
  59. T* TTD_MEM_ALLOC_CHECK(T* alloc)
  60. {
  61. if (alloc == nullptr)
  62. {
  63. TTDAssert(false, "OOM in TTD");
  64. }
  65. return alloc;
  66. }
  67. #define TT_HEAP_NEW(T, ...) TTD_MEM_ALLOC_CHECK(HeapNewNoThrow(T, __VA_ARGS__))
  68. #define TT_HEAP_ALLOC_ARRAY(T, SIZE_IN_ELEMENTS) TTD_MEM_ALLOC_CHECK(HeapNewNoThrowArray(T, SIZE_IN_ELEMENTS))
  69. #define TT_HEAP_ALLOC_ARRAY_ZERO(T, SIZE_IN_ELEMENTS) TTD_MEM_ALLOC_CHECK(HeapNewNoThrowArrayZ(T, SIZE_IN_ELEMENTS))
  70. #define TT_HEAP_DELETE(T, ELEM) HeapDelete(ELEM)
  71. #define TT_HEAP_FREE_ARRAY(T, ELEM, SIZE_IN_ELEMENTS) HeapDeleteArray(SIZE_IN_ELEMENTS, ELEM)
  72. ////////
  73. //The default number of elements per arrayblock, the size of "small" arrays and the block size we use when extracting
  74. #define TTD_ARRAY_BLOCK_SIZE 0x200
  75. #define TTD_ARRAY_SMALL_ARRAY 0x100
  76. //Convert from Js::Var to TTDVar
  77. #define TTD_CONVERT_JSVAR_TO_TTDVAR(X) ((TTD::TTDVar)(X))
  78. #define TTD_CONVERT_TTDVAR_TO_JSVAR(X) ((Js::Var)(X))
  79. //The ids for objects in a snapshot
  80. typedef uint64 TTD_PTR_ID;
  81. #define TTD_INVALID_PTR_ID 0ul
  82. #define TTD_CONVERT_VAR_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(PointerValue(X))
  83. #define TTD_CONVERT_TYPEINFO_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
  84. #define TTD_CONVERT_FUNCTIONBODY_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
  85. #define TTD_CONVERT_ENV_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
  86. #define TTD_CONVERT_SLOTARRAY_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
  87. #define TTD_CONVERT_SCOPE_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
  88. #define TTD_CONVERT_DEBUGSCOPE_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(X)
  89. //Promises have a wide range of heap allocated bits -- we define He-Man casts for all of them -- ugly but so is having a bunch of specific functions
  90. #define TTD_CONVERT_PROMISE_INFO_TO_PTR_ID(X) reinterpret_cast<TTD_PTR_ID>(PointerValue(X))
  91. #define TTD_CONVERT_PROMISE_INFO_TO_SPECIFIC_TYPE(T, X) static_cast<T*>(X)
  92. #define TTD_COERCE_PTR_ID_TO_VAR(X) (reinterpret_cast<Js::Var>(X))
  93. #define TTD_COERCE_PTR_ID_TO_FUNCTIONBODY(X) (reinterpret_cast<Js::FunctionBody*>(X))
  94. //The representation of LOG ids (based on object pointers)
  95. typedef uint64 TTD_LOG_PTR_ID;
  96. #define TTD_INVALID_LOG_PTR_ID 0ul
  97. #define TTD_CONVERT_OBJ_TO_LOG_PTR_ID(X) reinterpret_cast<TTD_LOG_PTR_ID>(X)
  98. //The representation of an identifier (currently access path) for a well known object/primitive/function body/etc. in the JS engine or HOST
  99. typedef const char16* TTD_WELLKNOWN_TOKEN;
  100. #define TTD_INVALID_WELLKNOWN_TOKEN nullptr
  101. #define TTD_DIAGNOSTIC_COMPARE_WELLKNOWN_TOKENS(T1, T2) ((T1 == T2) || ((T1 != TTD_INVALID_WELLKNOWN_TOKEN) && (T2 != TTD_INVALID_WELLKNOWN_TOKEN) && wcscmp(T1, T2) == 0))
  102. ////////
  103. //2MB slabs in allocator with a threshold of 4Kb to allocate a single block in our large space
  104. #define TTD_SLAB_BLOCK_ALLOCATION_SIZE_LARGE 2097152
  105. #define TTD_SLAB_BLOCK_ALLOCATION_SIZE_MID 262144
  106. #define TTD_SLAB_BLOCK_ALLOCATION_SIZE_SMALL 32768
  107. //A threshold of 4Kb to allocate a single block in our large space
  108. #define TTD_SLAB_LARGE_BLOCK_SIZE 4096
  109. #define TTD_WORD_ALIGN_ALLOC_SIZE(ASIZE) (((ASIZE) + 0x3) & 0xFFFFFFFC)
  110. #define TTD_SLAB_BLOCK_SIZE TTD_WORD_ALIGN_ALLOC_SIZE(sizeof(SlabBlock))
  111. #define TTD_SLAB_BLOCK_USABLE_SIZE(SLABSIZE) (SLABSIZE - TTD_SLAB_BLOCK_SIZE)
  112. #define TTD_LARGE_SLAB_BLOCK_SIZE TTD_WORD_ALIGN_ALLOC_SIZE(sizeof(LargeSlabBlock))
  113. //allocation increments for the array list -- we pick sizes that are just larger than the number generated by a "simple" programs
  114. #define TTD_ARRAY_LIST_SIZE_LARGE 4096
  115. #define TTD_ARRAY_LIST_SIZE_DEFAULT 2048
  116. #define TTD_ARRAY_LIST_SIZE_MID 512
  117. #define TTD_ARRAY_LIST_SIZE_SMALL 128
  118. #define TTD_ARRAY_LIST_SIZE_XSMALL 32
  119. //A basic universal hash function for our dictionary
  120. #define TTD_DICTIONARY_LOAD_FACTOR 2
  121. #define TTD_DICTIONARY_HASH(X, P) ((uint32)((X) % P))
  122. #define TTD_DICTIONARY_INDEX(X, M) ((uint32)((X) & (M - 1)))
  123. //Support for our mark table
  124. #define TTD_MARK_TABLE_INIT_SIZE 65536
  125. #define TTD_MARK_TABLE_INIT_H2PRIME 32749
  126. #define TTD_MARK_TABLE_HASH1(X, S) ((uint32)((X) & (S - 1)))
  127. #define TTD_MARK_TABLE_HASH2(X, P) ((uint32)((X) % P))
  128. #define TTD_MARK_TABLE_INDEX(X, M) ((uint32)((X) & (M - 1)))
  129. #define TTD_TABLE_FACTORLOAD_BASE(C, P1, P2) if(targetSize < C) { *powerOf2 = C; *closePrime = P1; *midPrime = P2; }
  130. #define TTD_TABLE_FACTORLOAD(C, P1, P2) else TTD_TABLE_FACTORLOAD_BASE(C, P1, P2)
  131. #define TTD_TABLE_FACTORLOAD_FINAL(C, P1, P2) else { *powerOf2 = C; *closePrime = P1; *midPrime = P2; }
  132. namespace TTD
  133. {
  134. //Mode of the time-travel debugger
  135. enum class TTDMode
  136. {
  137. Invalid = 0x0,
  138. CurrentlyEnabled = 0x1, //The TTD system is enabled and actively performing record/replay/debug
  139. RecordMode = 0x2, //The system is being run in Record mode
  140. ReplayMode = 0x4, //The system is being run in Replay mode
  141. DebuggerAttachedMode = 0x8, //The system is running with a debugger attached
  142. RecordDebuggerMode = (RecordMode | DebuggerAttachedMode),
  143. ReplayDebuggerMode = (ReplayMode | DebuggerAttachedMode),
  144. AnyMode = (RecordMode | ReplayMode | RecordDebuggerMode | ReplayDebuggerMode),
  145. ExcludedExecutionTTAction = 0x20, //Set when the system is executing code on behalf of the TTD system (so we don't want to record/replay things for it)
  146. ExcludedExecutionDebuggerAction = 0x40, //Set when the system is executing code on behalf of the Debugger system (so we don't want to record/replay things for it)
  147. AnyExcludedMode = (ExcludedExecutionTTAction | ExcludedExecutionDebuggerAction),
  148. DebuggerSuppressGetter = 0x200, //Set when the system is doing a property access for the debugger (so we don't want to accidentally trigger a getter execution)
  149. DebuggerSuppressBreakpoints = 0x400, //Set to prevent breakpoints (or break on exception) when moving in TT mode
  150. DebuggerLogBreakpoints = 0x800 //Set to indicate we want to log breakpoints encountered when executing (but not actually halt)
  151. };
  152. DEFINE_ENUM_FLAG_OPERATORS(TTDMode)
  153. class TTModeStack
  154. {
  155. private:
  156. TTDMode * m_stackEntries;
  157. uint32 m_stackTop;
  158. uint32 m_stackMax;
  159. public:
  160. TTModeStack();
  161. ~TTModeStack();
  162. uint32 Count() const;
  163. TTDMode GetAt(uint32 index) const;
  164. void SetAt(uint32 index, TTDMode m);
  165. void Push(TTDMode m);
  166. TTDMode Peek() const;
  167. void Pop();
  168. };
  169. //Typedef for list of contexts that we are recording/replaying on -- used in the EventLog
  170. typedef JsUtil::List<Js::ScriptContext*, HeapAllocator> TTDContextList;
  171. namespace NSSnapObjects
  172. {
  173. //An enumeration of tags for the SnapObjects (to support dispatch when parsing)
  174. //IMPORTANT: When adding a new SnapObject subclass you need to add a corresponding typeid here
  175. enum class SnapObjectType : int
  176. {
  177. Invalid = 0x0,
  178. SnapUnhandledObject,
  179. SnapDynamicObject,
  180. SnapExternalObject,
  181. SnapScriptFunctionObject,
  182. SnapRuntimeFunctionObject,
  183. SnapExternalFunctionObject,
  184. SnapRuntimeRevokerFunctionObject,
  185. SnapBoundFunctionObject,
  186. SnapActivationObject,
  187. SnapBlockActivationObject,
  188. SnapPseudoActivationObject,
  189. SnapConsoleScopeActivationObject,
  190. SnapHeapArgumentsObject,
  191. SnapES5HeapArgumentsObject,
  192. SnapBoxedValueObject,
  193. SnapDateObject,
  194. SnapRegexObject,
  195. SnapErrorObject,
  196. SnapArrayObject,
  197. SnapNativeIntArrayObject,
  198. SnapNativeFloatArrayObject,
  199. SnapES5ArrayObject,
  200. SnapArrayBufferObject,
  201. SnapTypedArrayObject,
  202. SnapSetObject,
  203. SnapMapObject,
  204. SnapProxyObject,
  205. SnapPromiseObject,
  206. SnapPromiseResolveOrRejectFunctionObject,
  207. SnapPromiseReactionTaskFunctionObject,
  208. SnapPromiseAllResolveElementFunctionObject,
  209. SnapPromiseAllSettledResolveOrRejectElementFunctionObject,
  210. SnapPromiseAnyRejectElementFunctionObject,
  211. SnapGeneratorFunction,
  212. SnapGeneratorVirtualScriptFunction,
  213. SnapAsyncFunction,
  214. SnapGenerator,
  215. SnapAwaitObject,
  216. JavascriptAsyncSpawnExecutorFunction,
  217. JavascriptAsyncSpawnStepFunction,
  218. //objects that should always be well known but which may have other info we want to restore
  219. SnapWellKnownObject,
  220. Limit
  221. };
  222. DEFINE_ENUM_FLAG_OPERATORS(SnapObjectType);
  223. }
  224. //A struct that maintains the relation between a globally stable top-level body counter and the PTR id it has in this particular script context
  225. struct TopLevelFunctionInContextRelation
  226. {
  227. //The globally unique body counter id from the log
  228. uint32 TopLevelBodyCtr;
  229. //The PTR_ID that is used to refer to this top-level body within the given script context
  230. TTD_PTR_ID ContextSpecificBodyPtrId;
  231. };
  232. //Function pointer definitions and a struct for writing data out of memory (presumably to stable storage)
  233. typedef void* JsTTDStreamHandle;
  234. typedef JsTTDStreamHandle(CALLBACK *TTDOpenResourceStreamCallback)(size_t uriLength, const char* uri, size_t filenameLength, const char* filename, bool read, bool write);
  235. typedef bool(CALLBACK *TTDReadBytesFromStreamCallback)(JsTTDStreamHandle handle, byte* buff, size_t size, size_t* readCount);
  236. typedef bool(CALLBACK *TTDWriteBytesToStreamCallback)(JsTTDStreamHandle handle, const byte* buff, size_t size, size_t* writtenCount);
  237. typedef void(CALLBACK *TTDFlushAndCloseStreamCallback)(JsTTDStreamHandle handle, bool read, bool write);
  238. struct TTDataIOInfo
  239. {
  240. TTDOpenResourceStreamCallback pfOpenResourceStream;
  241. TTDReadBytesFromStreamCallback pfReadBytesFromStream;
  242. TTDWriteBytesToStreamCallback pfWriteBytesToStream;
  243. TTDFlushAndCloseStreamCallback pfFlushAndCloseStream;
  244. //Current location that we are writing TT data into as a utf8 encoded uri (we may have several sub paths from the root for writing different parts of the log)
  245. size_t ActiveTTUriLength;
  246. const char* ActiveTTUri;
  247. };
  248. //Function pointer definitions for creating/interacting with external objects
  249. typedef void(CALLBACK *TTDCreateExternalObjectCallback)(Js::ScriptContext* ctx, Js::Var prototype, Js::Var* object);
  250. typedef void(CALLBACK *TTDCreateJsRTContextCallback)(void* runtimeHandle, Js::ScriptContext** ctx); //Create and pin the context so it does not get GC'd
  251. typedef void(CALLBACK *TTDReleaseJsRTContextCallback)(FinalizableObject* jsrtCtx); //Release an un-needed context during replay
  252. typedef void(CALLBACK *TTDSetActiveJsRTContext)(void* runtimeHandle, Js::ScriptContext* ctx); //Set active jsrtcontext
  253. struct ExternalObjectFunctions
  254. {
  255. TTDCreateExternalObjectCallback pfCreateExternalObject;
  256. TTDCreateJsRTContextCallback pfCreateJsRTContextCallback;
  257. TTDReleaseJsRTContextCallback pfReleaseJsRTContextCallback;
  258. TTDSetActiveJsRTContext pfSetActiveJsRTContext;
  259. };
  260. namespace UtilSupport
  261. {
  262. //A basic auto-managed null terminated string with value semantics
  263. class TTAutoString
  264. {
  265. private:
  266. int64 m_allocSize;
  267. char16* m_contents;
  268. char16* m_optFormatBuff;
  269. public:
  270. TTAutoString();
  271. TTAutoString(const char16* str);
  272. TTAutoString(const TTAutoString& str);
  273. TTAutoString& operator=(const TTAutoString& str);
  274. ~TTAutoString();
  275. void Clear();
  276. TTAutoString(TTAutoString&& str) = delete;
  277. TTAutoString& operator=(TTAutoString&& str) = delete;
  278. bool IsNullString() const;
  279. void Append(const char16* str, size_t start = 0, size_t end = SIZE_T_MAX);
  280. void Append(const TTAutoString& str, size_t start = 0, size_t end = SIZE_T_MAX);
  281. void Append(uint64 val);
  282. void Append(LPCUTF8 strBegin, LPCUTF8 strEnd);
  283. int32 GetLength() const;
  284. char16 GetCharAt(int32 pos) const;
  285. const char16* GetStrValue() const;
  286. };
  287. }
  288. //Given a target capacity return (1) the nearest upper power of 2, (2) a good close prime, and (3) a good mid prime
  289. void LoadValuesForHashTables(uint32 targetSize, uint32* powerOf2, uint32* closePrime, uint32* midPrime);
  290. //////////////////
  291. //String representation to use when copying things into/between slab allocators
  292. struct TTString
  293. {
  294. //Length of the string in terms of char16s -- excluding any '\0' termination
  295. uint32 Length;
  296. //The char contents of the string (null terminated -- may be null if empty)
  297. char16* Contents;
  298. };
  299. //initialize and return true if the given string should map to a nullptr char* representaiton
  300. void InitializeAsNullPtrTTString(TTString& str);
  301. bool IsNullPtrTTString(const TTString& str);
  302. #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
  303. //This is for diagnostic purposes only
  304. bool TTStringEQForDiagnostics(const TTString& str1, const TTString& str2);
  305. #endif
  306. //A class that implements a simple slab memory allocator
  307. template <int32 canUnlink>
  308. class SlabAllocatorBase
  309. {
  310. private:
  311. //A block that the slab allocator uses
  312. struct SlabBlock
  313. {
  314. //The actual block for the data -- should be laid out immediately after this
  315. byte* BlockData;
  316. //The previous/next block in the list
  317. SlabBlock* Previous;
  318. SlabBlock* Next;
  319. //The reference counter for this -- invalid if canUnlink == 0
  320. int32 RefCounter;
  321. };
  322. //A "large" block allocation list
  323. struct LargeSlabBlock
  324. {
  325. //The actual block for the data -- should be laid out immediately after this
  326. byte* BlockData;
  327. //The size of the block in bytes (includes both LargeSlabBlock and data memory)
  328. uint32 TotalBlockSize;
  329. //The previous/next block in the list
  330. LargeSlabBlock* Previous;
  331. LargeSlabBlock* Next;
  332. //Place well known meta-data ptr distance here (0) so we know it is a large block (not a regular slab alloc).
  333. ptrdiff_t MetaDataSential;
  334. };
  335. //We inline the fields for the current/end of the block that we currently are allocating from a
  336. byte* m_currPos;
  337. byte* m_endPos;
  338. //The size we want to allocate for each SlabBlock -- set in constructor and unchanged after
  339. const uint32 m_slabBlockSize;
  340. //The list of slab blocks used in the allocator (the current block to allocate from is always at the head)
  341. SlabBlock* m_headBlock;
  342. //The large allocation list
  343. LargeSlabBlock* m_largeBlockList;
  344. //Allow us to speculatively reserve blocks for data and make sure we don't double allocate anything
  345. uint32 m_reserveActiveBytes;
  346. LargeSlabBlock* m_reserveActiveLargeBlock;
  347. #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
  348. //The amount of allocated memory with useful data
  349. uint64 m_totalAllocatedSize;
  350. #endif
  351. //Get a new block in the slab
  352. void AddNewBlock()
  353. {
  354. byte* allocBlock = TT_HEAP_ALLOC_ARRAY(byte, this->m_slabBlockSize);
  355. TTDAssert((reinterpret_cast<uint64>(allocBlock) & 0x3) == 0, "We have non-word aligned allocations so all our later work is not so useful");
  356. SlabBlock* newBlock = (SlabBlock*)allocBlock;
  357. byte* dataArray = (allocBlock + TTD_SLAB_BLOCK_SIZE);
  358. this->m_currPos = dataArray;
  359. this->m_endPos = dataArray + TTD_SLAB_BLOCK_USABLE_SIZE(this->m_slabBlockSize);
  360. newBlock->BlockData = dataArray;
  361. newBlock->Next = nullptr;
  362. newBlock->Previous = this->m_headBlock;
  363. newBlock->RefCounter = 0;
  364. this->m_headBlock->Next = newBlock;
  365. this->m_headBlock = newBlock;
  366. }
  367. //Allocate a byte* of the the template size (word aligned)
  368. template <size_t n>
  369. byte* SlabAllocateTypeRawSize()
  370. {
  371. TTDAssert(this->m_reserveActiveBytes == 0, "Don't double allocate memory.");
  372. TTDAssert(n <= TTD_SLAB_LARGE_BLOCK_SIZE, "Don't allocate large requests in the bump pool.");
  373. uint32 desiredsize = TTD_WORD_ALIGN_ALLOC_SIZE(n + canUnlink); //make alloc size word aligned
  374. TTDAssert((desiredsize % 4 == 0) & (desiredsize >= (n + canUnlink)) & (desiredsize < TTD_SLAB_BLOCK_USABLE_SIZE(this->m_slabBlockSize)), "We can never allocate a block this big with the slab allocator!!");
  375. if (this->m_currPos + desiredsize > this->m_endPos)
  376. {
  377. this->AddNewBlock();
  378. }
  379. byte* res = this->m_currPos;
  380. this->m_currPos += desiredsize;
  381. #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
  382. this->m_totalAllocatedSize += TTD_WORD_ALIGN_ALLOC_SIZE(n);
  383. #endif
  384. if (canUnlink)
  385. {
  386. TTDAssert(canUnlink == sizeof(ptrdiff_t), "We need enough space for a ptr to the meta-data.");
  387. //record the block associated with this allocation
  388. *((ptrdiff_t*)res) = res - ((byte*)this->m_headBlock);
  389. res += canUnlink;
  390. //update the ctr for this block
  391. this->m_headBlock->RefCounter++;
  392. }
  393. return res;
  394. }
  395. //Allocate a byte* of the the given size (word aligned)
  396. template <bool reserve, bool commit>
  397. byte* SlabAllocateRawSize(size_t requestedBytes)
  398. {
  399. TTDAssert(requestedBytes != 0, "Don't allocate empty arrays.");
  400. TTDAssert(requestedBytes <= TTD_SLAB_LARGE_BLOCK_SIZE, "Don't allocate large requests in the bump pool.");
  401. byte* res = nullptr;
  402. uint32 desiredsize = TTD_WORD_ALIGN_ALLOC_SIZE(requestedBytes + canUnlink); //make alloc size word aligned
  403. TTDAssert((desiredsize % 4 == 0) & (desiredsize >= (requestedBytes + canUnlink)) & (desiredsize < TTD_SLAB_BLOCK_USABLE_SIZE(this->m_slabBlockSize)), "We can never allocate a block this big with the slab allocator!!");
  404. if (reserve)
  405. {
  406. TTDAssert(this->m_reserveActiveBytes == 0, "Don't double allocate memory.");
  407. if (this->m_currPos + desiredsize > this->m_endPos)
  408. {
  409. this->AddNewBlock();
  410. }
  411. res = this->m_currPos;
  412. if (canUnlink)
  413. {
  414. TTDAssert(canUnlink == sizeof(ptrdiff_t), "We need enough space for a ptr to the meta-data.");
  415. //record the block associated with this allocation
  416. *((ptrdiff_t*)res) = res - ((byte*)this->m_headBlock);
  417. res += canUnlink;
  418. }
  419. }
  420. if (commit)
  421. {
  422. this->m_currPos += desiredsize;
  423. #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
  424. this->m_totalAllocatedSize += desiredsize;
  425. #endif
  426. if (canUnlink)
  427. {
  428. this->m_headBlock->RefCounter++;
  429. }
  430. }
  431. if (reserve && !commit)
  432. {
  433. this->m_reserveActiveBytes = desiredsize;
  434. }
  435. if (!reserve && commit)
  436. {
  437. TTDAssert(desiredsize <= this->m_reserveActiveBytes, "We are commiting more that we reserved.");
  438. this->m_reserveActiveBytes = 0;
  439. }
  440. return res;
  441. }
  442. //Commit the allocation of a large block
  443. void CommitLargeBlockAllocation(LargeSlabBlock* newBlock, size_t blockSize)
  444. {
  445. #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
  446. this->m_totalAllocatedSize += blockSize;
  447. #endif
  448. if (this->m_largeBlockList != nullptr)
  449. {
  450. this->m_largeBlockList->Next = newBlock;
  451. }
  452. this->m_largeBlockList = newBlock;
  453. }
  454. //Allocate a byte* of the the given size (word aligned) in the large block pool
  455. template<bool commit>
  456. byte* SlabAllocateLargeBlockSize(size_t requestedBytes)
  457. {
  458. TTDAssert(requestedBytes > TTD_SLAB_LARGE_BLOCK_SIZE, "Don't allocate small requests in the large pool.");
  459. uint32 desiredsize = TTD_WORD_ALIGN_ALLOC_SIZE(requestedBytes + TTD_LARGE_SLAB_BLOCK_SIZE); //make alloc size word aligned
  460. TTDAssert((desiredsize % 4 == 0) & (desiredsize >= (requestedBytes + TTD_LARGE_SLAB_BLOCK_SIZE)), "We can never allocate a block this big with the slab allocator!!");
  461. TTDAssert(this->m_reserveActiveBytes == 0, "Don't double allocate memory.");
  462. byte* tmp = TT_HEAP_ALLOC_ARRAY(byte, desiredsize);
  463. LargeSlabBlock* newBlock = (LargeSlabBlock*)tmp;
  464. newBlock->BlockData = (tmp + TTD_LARGE_SLAB_BLOCK_SIZE);
  465. newBlock->TotalBlockSize = desiredsize;
  466. newBlock->Previous = this->m_largeBlockList;
  467. newBlock->Next = nullptr;
  468. newBlock->MetaDataSential = 0;
  469. if (commit)
  470. {
  471. this->CommitLargeBlockAllocation(newBlock, desiredsize);
  472. }
  473. else
  474. {
  475. this->m_reserveActiveBytes = desiredsize;
  476. this->m_reserveActiveLargeBlock = newBlock;
  477. }
  478. return newBlock->BlockData;
  479. }
  480. public:
  481. SlabAllocatorBase(uint32 slabBlockSize)
  482. : m_largeBlockList(nullptr), m_slabBlockSize(slabBlockSize)
  483. {
  484. byte* allocBlock = TT_HEAP_ALLOC_ARRAY(byte, this->m_slabBlockSize);
  485. TTDAssert((reinterpret_cast<uint64>(allocBlock) & 0x3) == 0, "We have non-word aligned allocations so all our later work is not so useful");
  486. this->m_headBlock = (SlabBlock*)allocBlock;
  487. byte* dataArray = (allocBlock + TTD_SLAB_BLOCK_SIZE);
  488. this->m_currPos = dataArray;
  489. this->m_endPos = dataArray + TTD_SLAB_BLOCK_USABLE_SIZE(this->m_slabBlockSize);
  490. this->m_headBlock->BlockData = dataArray;
  491. this->m_headBlock->Next = nullptr;
  492. this->m_headBlock->Previous = nullptr;
  493. this->m_headBlock->RefCounter = 0;
  494. #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
  495. this->m_totalAllocatedSize = 0;
  496. #endif
  497. this->m_reserveActiveBytes = 0;
  498. this->m_reserveActiveLargeBlock = nullptr;
  499. }
  500. ~SlabAllocatorBase()
  501. {
  502. SlabBlock* currBlock = this->m_headBlock;
  503. while (currBlock != nullptr)
  504. {
  505. SlabBlock* tmp = currBlock;
  506. currBlock = currBlock->Previous;
  507. TT_HEAP_FREE_ARRAY(byte, (byte*)tmp, this->m_slabBlockSize);
  508. }
  509. LargeSlabBlock* currLargeBlock = this->m_largeBlockList;
  510. while (currLargeBlock != nullptr)
  511. {
  512. LargeSlabBlock* tmp = currLargeBlock;
  513. currLargeBlock = currLargeBlock->Previous;
  514. TT_HEAP_FREE_ARRAY(byte, (byte*)tmp, tmp->TotalBlockSize);
  515. }
  516. if (this->m_reserveActiveLargeBlock != nullptr)
  517. {
  518. TT_HEAP_FREE_ARRAY(byte, (byte*)this->m_reserveActiveLargeBlock, this->m_reserveActiveBytes);
  519. this->m_reserveActiveLargeBlock = nullptr;
  520. }
  521. }
  522. //eliminate some potentially dangerous operators
  523. SlabAllocatorBase(const SlabAllocatorBase&) = delete;
  524. SlabAllocatorBase& operator=(SlabAllocatorBase const&) = delete;
  525. //clone a null terminated char16* string (or nullptr) into the allocator -- currently only used for wellknown tokens
  526. const char16* CopyRawNullTerminatedStringInto(const char16* str)
  527. {
  528. if (str == nullptr)
  529. {
  530. return nullptr;
  531. }
  532. else
  533. {
  534. size_t length = wcslen(str) + 1;
  535. size_t byteLength = length * sizeof(char16);
  536. char16* res = this->SlabAllocateArray<char16>(length);
  537. js_memcpy_s(res, byteLength, str, byteLength);
  538. return res;
  539. }
  540. }
  541. //clone a string into the allocator of a known length
  542. void CopyStringIntoWLength(const char16* str, uint32 length, TTString& into)
  543. {
  544. TTDAssert(str != nullptr, "Not allowed for string + length");
  545. into.Length = length;
  546. into.Contents = this->SlabAllocateArray<char16>(into.Length + 1);
  547. //don't js_memcpy if the contents length is 0
  548. js_memcpy_s(into.Contents, into.Length * sizeof(char16), str, length * sizeof(char16));
  549. into.Contents[into.Length] = '\0';
  550. }
  551. //initialize and allocate the memory for a string of a given length (but don't set contents yet)
  552. void InitializeAndAllocateWLength(uint32 length, TTString& into)
  553. {
  554. into.Length = length;
  555. into.Contents = this->SlabAllocateArray<char16>(into.Length + 1);
  556. into.Contents[0] = '\0';
  557. }
  558. //clone a string into the allocator
  559. void CopyNullTermStringInto(const char16* str, TTString& into)
  560. {
  561. if (str == nullptr)
  562. {
  563. into.Length = 0;
  564. into.Contents = nullptr;
  565. }
  566. else
  567. {
  568. this->CopyStringIntoWLength(str, (uint32)wcslen(str), into);
  569. }
  570. }
  571. //Return the memory that contains useful data in this slab & the same as the reserved space
  572. void ComputeMemoryUsed(uint64* usedSpace, uint64* reservedSpace) const
  573. {
  574. uint64 memreserved = 0;
  575. for (SlabBlock* currBlock = this->m_headBlock; currBlock != nullptr; currBlock = currBlock->Previous)
  576. {
  577. memreserved += (uint64)this->m_slabBlockSize;
  578. }
  579. for (LargeSlabBlock* currLargeBlock = this->m_largeBlockList; currLargeBlock != nullptr; currLargeBlock = currLargeBlock->Previous)
  580. {
  581. memreserved += (uint64)(currLargeBlock->TotalBlockSize);
  582. }
  583. #if ENABLE_TTD_INTERNAL_DIAGNOSTICS
  584. *usedSpace = this->m_totalAllocatedSize;
  585. #else
  586. *usedSpace = 0;
  587. #endif
  588. *reservedSpace = memreserved;
  589. }
  590. //Allocate with "new" from the slab allocator
  591. template<typename T, typename... Args>
  592. T* SlabNew(Args... args)
  593. {
  594. return new (this->SlabAllocateTypeRawSize<sizeof(T)>()) T(args...);
  595. }
  596. //Allocate a T* of the size needed for the template type
  597. template <typename T>
  598. T* SlabAllocateStruct()
  599. {
  600. return (T*)this->SlabAllocateTypeRawSize<sizeof(T)>();
  601. }
  602. //Allocate T* of the size needed to hold count elements of the given type
  603. template <typename T>
  604. T* SlabAllocateArray(size_t count)
  605. {
  606. size_t size = count * sizeof(T);
  607. if (size <= TTD_SLAB_LARGE_BLOCK_SIZE)
  608. {
  609. return (T*)this->SlabAllocateRawSize<true, true>(size);
  610. }
  611. else
  612. {
  613. return (T*)this->SlabAllocateLargeBlockSize<true>(size);
  614. }
  615. }
  616. //Allocate T* of the size needed to hold count elements of the given type
  617. template <typename T>
  618. T* SlabAllocateArrayZ(size_t count)
  619. {
  620. T* res = nullptr;
  621. size_t size = count * sizeof(T);
  622. if (size <= TTD_SLAB_LARGE_BLOCK_SIZE)
  623. {
  624. res = (T*)this->SlabAllocateRawSize<true, true>(size);
  625. }
  626. else
  627. {
  628. res = (T*)this->SlabAllocateLargeBlockSize<true>(size);
  629. }
  630. memset(res, 0, size);
  631. return res;
  632. }
  633. //Allocate T* of the size needed to hold count (a compile time constant) number of elements of the given type
  634. template <typename T, size_t count>
  635. T* SlabAllocateFixedSizeArray()
  636. {
  637. if (count * sizeof(T) <= TTD_SLAB_LARGE_BLOCK_SIZE)
  638. {
  639. return (T*)this->SlabAllocateRawSize<true, true>(count * sizeof(T));
  640. }
  641. else
  642. {
  643. return (T*)this->SlabAllocateLargeBlockSize<true>(count * sizeof(T));
  644. }
  645. }
  646. //Reserve BUT DO NOT COMMIT and allocation of size needed to hold count elements of the given type
  647. template <typename T>
  648. T* SlabReserveArraySpace(size_t count)
  649. {
  650. size_t size = count * sizeof(T);
  651. if (size <= TTD_SLAB_LARGE_BLOCK_SIZE)
  652. {
  653. return (T*)this->SlabAllocateRawSize<true, false>(size);
  654. }
  655. else
  656. {
  657. return (T*)this->SlabAllocateLargeBlockSize<false>(size);
  658. }
  659. }
  660. //Commit the allocation of count (or fewer) elements of the given type that were reserved previously
  661. template <typename T>
  662. void SlabCommitArraySpace(size_t actualCount, size_t reservedCount)
  663. {
  664. TTDAssert(this->m_reserveActiveBytes != 0, "We don't have anything reserved.");
  665. size_t reservedSize = reservedCount * sizeof(T);
  666. if (reservedSize <= TTD_SLAB_LARGE_BLOCK_SIZE)
  667. {
  668. TTDAssert(this->m_reserveActiveLargeBlock == nullptr, "We should not have a large block active!!!");
  669. size_t actualSize = actualCount * sizeof(T);
  670. this->SlabAllocateRawSize<false, true>(actualSize);
  671. }
  672. else
  673. {
  674. TTDAssert(this->m_reserveActiveLargeBlock != nullptr, "We should have a large block active!!!");
  675. this->CommitLargeBlockAllocation(this->m_reserveActiveLargeBlock, this->m_reserveActiveBytes);
  676. this->m_reserveActiveLargeBlock = nullptr;
  677. this->m_reserveActiveBytes = 0;
  678. }
  679. }
  680. //Abort the allocation of the elements of the given type that were reserved previously
  681. template <typename T>
  682. void SlabAbortArraySpace(size_t reservedCount)
  683. {
  684. TTDAssert(this->m_reserveActiveBytes != 0, "We don't have anything reserved.");
  685. size_t reservedSize = reservedCount * sizeof(T);
  686. if (reservedSize <= TTD_SLAB_LARGE_BLOCK_SIZE)
  687. {
  688. TTDAssert(this->m_reserveActiveLargeBlock == nullptr, "We should not have a large block active!!!");
  689. this->m_reserveActiveBytes = 0;
  690. }
  691. else
  692. {
  693. TTDAssert(this->m_reserveActiveLargeBlock != nullptr, "We should have a large block active!!!");
  694. TT_HEAP_FREE_ARRAY(byte, (byte*)this->m_reserveActiveLargeBlock, this->m_reserveActiveBytes);
  695. this->m_reserveActiveLargeBlock = nullptr;
  696. this->m_reserveActiveBytes = 0;
  697. }
  698. }
  699. //If allowed unlink the memory allocation specified and free the block if it is no longer used by anyone
  700. void UnlinkAllocation(const void* allocation)
  701. {
  702. TTDAssert(canUnlink != 0, "Unlink not allowed with this slab allocator.");
  703. TTDAssert(this->m_reserveActiveBytes == 0, "We don't have anything reserved.");
  704. //get the meta-data for this allocation and see if it is a
  705. byte* realBase = ((byte*)allocation) - canUnlink;
  706. ptrdiff_t offset = *((ptrdiff_t*)realBase);
  707. if (offset == 0)
  708. {
  709. //it is a large allocation just free it
  710. LargeSlabBlock* largeBlock = (LargeSlabBlock*)(((byte*)allocation) - TTD_LARGE_SLAB_BLOCK_SIZE);
  711. if (largeBlock == this->m_largeBlockList)
  712. {
  713. TTDAssert(largeBlock->Next == nullptr, "Should always have a null next at head");
  714. this->m_largeBlockList = this->m_largeBlockList->Previous;
  715. if (this->m_largeBlockList != nullptr)
  716. {
  717. this->m_largeBlockList->Next = nullptr;
  718. }
  719. }
  720. else
  721. {
  722. if (largeBlock->Next != nullptr)
  723. {
  724. largeBlock->Next->Previous = largeBlock->Previous;
  725. }
  726. if (largeBlock->Previous != nullptr)
  727. {
  728. largeBlock->Previous->Next = largeBlock->Next;
  729. }
  730. }
  731. TT_HEAP_FREE_ARRAY(byte, (byte*)largeBlock, largeBlock->TotalBlockSize);
  732. }
  733. else
  734. {
  735. //lookup the slab block and do ref counting
  736. SlabBlock* block = (SlabBlock*)(realBase - offset);
  737. block->RefCounter--;
  738. if (block->RefCounter == 0)
  739. {
  740. if (block == this->m_headBlock)
  741. {
  742. //we always need a head block to allocate out of -- so instead of deleting just reset it
  743. this->m_currPos = this->m_headBlock->BlockData;
  744. this->m_endPos = this->m_headBlock->BlockData + TTD_SLAB_BLOCK_USABLE_SIZE(this->m_slabBlockSize);
  745. memset(this->m_currPos, 0, TTD_SLAB_BLOCK_USABLE_SIZE(this->m_slabBlockSize));
  746. this->m_headBlock->RefCounter = 0;
  747. }
  748. else
  749. {
  750. if (block->Next != nullptr)
  751. {
  752. block->Next->Previous = block->Previous;
  753. }
  754. if (block->Previous != nullptr)
  755. {
  756. block->Previous->Next = block->Next;
  757. }
  758. TT_HEAP_FREE_ARRAY(byte, (byte*)block, this->m_slabBlockSize);
  759. }
  760. }
  761. }
  762. }
  763. //Unlink the memory used by a string
  764. void UnlinkString(const TTString& str)
  765. {
  766. if (str.Contents != nullptr)
  767. {
  768. this->UnlinkAllocation(str.Contents);
  769. }
  770. }
  771. };
  772. //The standard slab allocator implemention does not allow for unlinking
  773. typedef SlabAllocatorBase<0> SlabAllocator;
  774. //The unlinkable slab allocator implemention (as expected) allows for unlinking
  775. typedef SlabAllocatorBase<sizeof(ptrdiff_t)> UnlinkableSlabAllocator;
  776. //////////////////
  777. //An (unordered) array list class that has fast insertion/write-into and does not move contents
  778. template<typename T, size_t allocSize>
  779. class UnorderedArrayList
  780. {
  781. private:
  782. struct UnorderedArrayListLink
  783. {
  784. //The current end of the allocated data in the block
  785. T* CurrPos;
  786. //The last valid position in the block -- a little unusual but then the "alloc part does not depend on the outcome of the if condition"
  787. T* EndPos;
  788. //The actual block for the data
  789. T* BlockData;
  790. //The next block in the list
  791. UnorderedArrayListLink* Next;
  792. };
  793. //The the data in this
  794. UnorderedArrayListLink m_inlineHeadBlock;
  795. //the allocators we use for this work
  796. SlabAllocator* m_alloc;
  797. void AddArrayLink()
  798. {
  799. UnorderedArrayListLink* copiedOldBlock = this->m_alloc->template SlabAllocateStruct<UnorderedArrayListLink>();
  800. *copiedOldBlock = this->m_inlineHeadBlock;
  801. T* newBlockData = this->m_alloc->template SlabAllocateFixedSizeArray<T, allocSize>();
  802. this->m_inlineHeadBlock.BlockData = newBlockData;
  803. this->m_inlineHeadBlock.CurrPos = newBlockData;
  804. this->m_inlineHeadBlock.EndPos = newBlockData + allocSize;
  805. this->m_inlineHeadBlock.Next = copiedOldBlock;
  806. }
  807. public:
  808. UnorderedArrayList(SlabAllocator* alloc)
  809. : m_alloc(alloc)
  810. {
  811. T* newBlockData = this->m_alloc->template SlabAllocateFixedSizeArray<T, allocSize>();
  812. this->m_inlineHeadBlock.BlockData = newBlockData;
  813. this->m_inlineHeadBlock.CurrPos = newBlockData;
  814. this->m_inlineHeadBlock.EndPos = newBlockData + allocSize;
  815. this->m_inlineHeadBlock.Next = nullptr;
  816. }
  817. ~UnorderedArrayList()
  818. {
  819. //everything is slab allocated so disapears with that
  820. }
  821. //Add the entry to the unordered list
  822. void AddEntry(T data)
  823. {
  824. TTDAssert(this->m_inlineHeadBlock.CurrPos <= this->m_inlineHeadBlock.EndPos, "We are off the end of the array");
  825. TTDAssert((((byte*)this->m_inlineHeadBlock.CurrPos) - ((byte*)this->m_inlineHeadBlock.BlockData)) / sizeof(T) <= allocSize, "We are off the end of the array");
  826. if (this->m_inlineHeadBlock.CurrPos == this->m_inlineHeadBlock.EndPos)
  827. {
  828. this->AddArrayLink();
  829. }
  830. *(this->m_inlineHeadBlock.CurrPos) = data;
  831. this->m_inlineHeadBlock.CurrPos++;
  832. }
  833. //Get the next uninitialized entry (at the front of the sequence)
  834. //We expect the caller to initialize this memory appropriately
  835. T* NextOpenEntry()
  836. {
  837. TTDAssert(this->m_inlineHeadBlock.CurrPos <= this->m_inlineHeadBlock.EndPos, "We are off the end of the array");
  838. TTDAssert((((byte*)this->m_inlineHeadBlock.CurrPos) - ((byte*)this->m_inlineHeadBlock.BlockData)) / sizeof(T) <= allocSize, "We are off the end of the array");
  839. if (this->m_inlineHeadBlock.CurrPos == this->m_inlineHeadBlock.EndPos)
  840. {
  841. this->AddArrayLink();
  842. }
  843. T* entry = this->m_inlineHeadBlock.CurrPos;
  844. this->m_inlineHeadBlock.CurrPos++;
  845. return entry;
  846. }
  847. //NOT constant time
  848. uint32 Count() const
  849. {
  850. size_t count = (((byte*)this->m_inlineHeadBlock.CurrPos) - ((byte*)this->m_inlineHeadBlock.BlockData)) / sizeof(T);
  851. TTDAssert(count <= allocSize, "We somehow wrote in too much data.");
  852. for (UnorderedArrayListLink* curr = this->m_inlineHeadBlock.Next; curr != nullptr; curr = curr->Next)
  853. {
  854. size_t ncount = (((byte*)curr->CurrPos) - ((byte*)curr->BlockData)) / sizeof(T);
  855. TTDAssert(ncount <= allocSize, "We somehow wrote in too much data.");
  856. count += ncount;
  857. }
  858. return (uint32)count;
  859. }
  860. class Iterator
  861. {
  862. private:
  863. UnorderedArrayListLink m_currLink;
  864. T* m_currEntry;
  865. public:
  866. Iterator(const UnorderedArrayListLink& head)
  867. : m_currLink(head), m_currEntry(head.BlockData)
  868. {
  869. //check for empty list and invalidate the iter if it is
  870. if (this->m_currEntry == this->m_currLink.CurrPos)
  871. {
  872. this->m_currEntry = nullptr;
  873. }
  874. }
  875. const T* Current() const
  876. {
  877. return this->m_currEntry;
  878. }
  879. T* Current()
  880. {
  881. return this->m_currEntry;
  882. }
  883. bool IsValid() const
  884. {
  885. return this->m_currEntry != nullptr;
  886. }
  887. void MoveNext()
  888. {
  889. this->m_currEntry++;
  890. if (this->m_currEntry == this->m_currLink.CurrPos)
  891. {
  892. if (this->m_currLink.Next == nullptr)
  893. {
  894. this->m_currEntry = nullptr;
  895. }
  896. else
  897. {
  898. this->m_currLink = *(this->m_currLink.Next);
  899. this->m_currEntry = this->m_currLink.BlockData;
  900. }
  901. }
  902. }
  903. };
  904. Iterator GetIterator() const
  905. {
  906. return Iterator(this->m_inlineHeadBlock);
  907. }
  908. };
  909. //////////////////
  910. //A specialized map for going from TTD_PTR_ID values to some information associated with them
  911. template <typename Tag, typename T>
  912. class TTDIdentifierDictionary
  913. {
  914. private:
  915. //An entry struct for the dictionary
  916. struct Entry
  917. {
  918. Tag Key;
  919. T Data;
  920. };
  921. //The 2 prime numbers we use for our hasing function
  922. uint32 m_h1Prime;
  923. uint32 m_h2Prime;
  924. //The hash max capcity and data array
  925. uint32 m_capacity;
  926. Entry* m_hashArray;
  927. //Count of elements in the dictionary
  928. uint32 m_count;
  929. template <bool findEmpty>
  930. Entry* FindSlotForId(Tag id) const
  931. {
  932. TTDAssert(this->m_h1Prime != 0 && this->m_h2Prime != 0, "Not valid!!");
  933. TTDAssert(this->m_hashArray != nullptr, "Not valid!!");
  934. Tag searchKey = findEmpty ? 0 : id;
  935. //h1Prime is less than table size by construction so we dont need to re-index
  936. uint32 primaryIndex = TTD_DICTIONARY_HASH(id, this->m_h1Prime);
  937. if (this->m_hashArray[primaryIndex].Key == searchKey)
  938. {
  939. return (this->m_hashArray + primaryIndex);
  940. }
  941. //do a hash for the second offset to avoid clustering and then do linear probing
  942. uint32 offset = TTD_DICTIONARY_HASH(id, this->m_h2Prime);
  943. uint32 probeIndex = TTD_DICTIONARY_INDEX(primaryIndex + offset, this->m_capacity);
  944. while (true)
  945. {
  946. Entry* curr = (this->m_hashArray + probeIndex);
  947. if (curr->Key == searchKey)
  948. {
  949. return curr;
  950. }
  951. probeIndex = TTD_DICTIONARY_INDEX(probeIndex + 1, this->m_capacity);
  952. TTDAssert(probeIndex != TTD_DICTIONARY_INDEX(primaryIndex + offset, this->m_capacity), "The key is not here (or we messed up).");
  953. }
  954. }
  955. void InitializeEntry(Entry* entry, Tag id, const T& item)
  956. {
  957. entry->Key = id;
  958. entry->Data = item;
  959. }
  960. public:
  961. void Unload()
  962. {
  963. if (this->m_hashArray != nullptr)
  964. {
  965. TT_HEAP_FREE_ARRAY(Entry, this->m_hashArray, this->m_capacity);
  966. this->m_hashArray = nullptr;
  967. this->m_capacity = 0;
  968. this->m_count = 0;
  969. this->m_h1Prime = 0;
  970. this->m_h2Prime = 0;
  971. }
  972. }
  973. void Initialize(uint32 capacity)
  974. {
  975. TTDAssert(this->m_hashArray == nullptr, "Should not already be initialized.");
  976. uint32 desiredSize = capacity * TTD_DICTIONARY_LOAD_FACTOR;
  977. LoadValuesForHashTables(desiredSize, &(this->m_capacity), &(this->m_h1Prime), &(this->m_h2Prime));
  978. this->m_hashArray = TT_HEAP_ALLOC_ARRAY_ZERO(Entry, this->m_capacity);
  979. }
  980. TTDIdentifierDictionary()
  981. : m_h1Prime(0), m_h2Prime(0), m_capacity(0), m_hashArray(nullptr), m_count(0)
  982. {
  983. }
  984. ~TTDIdentifierDictionary()
  985. {
  986. this->Unload();
  987. }
  988. void MoveDataInto(TTDIdentifierDictionary& src)
  989. {
  990. this->m_h1Prime = src.m_h1Prime;
  991. this->m_h2Prime = src.m_h2Prime;
  992. this->m_capacity = src.m_capacity;
  993. this->m_hashArray = src.m_hashArray;
  994. this->m_count = src.m_count;
  995. src.m_hashArray = TT_HEAP_ALLOC_ARRAY_ZERO(Entry, src.m_capacity);
  996. src.m_count = 0;
  997. }
  998. bool IsValid() const
  999. {
  1000. return this->m_hashArray != nullptr;
  1001. }
  1002. uint32 Count() const
  1003. {
  1004. return this->m_count;
  1005. }
  1006. bool Contains(Tag id) const
  1007. {
  1008. TTDAssert(this->m_h1Prime != 0 && this->m_h2Prime != 0, "Not valid!!");
  1009. TTDAssert(this->m_hashArray != nullptr, "Not valid!!");
  1010. //h1Prime is less than table size by construction so we dont need to re-index
  1011. uint32 primaryIndex = TTD_DICTIONARY_HASH(id, this->m_h1Prime);
  1012. if (this->m_hashArray[primaryIndex].Key == id)
  1013. {
  1014. return true;
  1015. }
  1016. if (this->m_hashArray[primaryIndex].Key == 0)
  1017. {
  1018. return false;
  1019. }
  1020. //do a hash for the second offset to avoid clustering and then do linear probing
  1021. uint32 offset = TTD_DICTIONARY_HASH(id, this->m_h2Prime);
  1022. uint32 probeIndex = TTD_DICTIONARY_INDEX(primaryIndex + offset, this->m_capacity);
  1023. while (true)
  1024. {
  1025. Entry* curr = (this->m_hashArray + probeIndex);
  1026. if (curr->Key == id)
  1027. {
  1028. return true;
  1029. }
  1030. if (curr->Key == 0)
  1031. {
  1032. return false;
  1033. }
  1034. probeIndex = TTD_DICTIONARY_INDEX(probeIndex + 1, this->m_capacity);
  1035. TTDAssert(probeIndex != TTD_DICTIONARY_INDEX(primaryIndex + offset, this->m_capacity), "The key is not here (or we messed up).");
  1036. }
  1037. }
  1038. void AddItem(Tag id, const T& item)
  1039. {
  1040. TTDAssert(this->m_count * TTD_DICTIONARY_LOAD_FACTOR < this->m_capacity, "The dictionary is being sized incorrectly and will likely have poor performance");
  1041. Entry* entry = this->FindSlotForId<true>(id);
  1042. InitializeEntry(entry, id, item);
  1043. this->m_count++;
  1044. }
  1045. //Lookup an item which is known to exist in the dictionary (and errors if it is not present)
  1046. const T& LookupKnownItem(Tag id) const
  1047. {
  1048. Entry* entry = this->FindSlotForId<false>(id);
  1049. return entry->Data;
  1050. }
  1051. };
  1052. //////////////////
  1053. //A simple table to do object marking
  1054. //
  1055. //TODO: This table isn't great performance wise (around 2/3 of the time for a snapshot is spent in the mark-phase)
  1056. // Right now perf. isn't a limiting factor but we probably want to improve on this.
  1057. //
  1058. //Mark the table and what kind of value is at the address
  1059. enum class MarkTableTag : byte
  1060. {
  1061. Clear = 0x0,
  1062. //
  1063. TypeHandlerTag = 0x1,
  1064. TypeTag = 0x2,
  1065. PrimitiveObjectTag = 0x3,
  1066. CompoundObjectTag = 0x4,
  1067. FunctionBodyTag = 0x5,
  1068. EnvironmentTag = 0x6,
  1069. SlotArrayTag = 0x7,
  1070. KindTagCount = 0x8,
  1071. AllKindMask = 0xF,
  1072. //
  1073. JsWellKnownObj = 0x10, //mark for objects that the JS runtime creates and are well known (Array, Undefined, ...)
  1074. SpecialTagMask = 0xF0
  1075. };
  1076. DEFINE_ENUM_FLAG_OPERATORS(MarkTableTag);
  1077. class MarkTable
  1078. {
  1079. private:
  1080. //The addresses and their marks
  1081. uint64 * m_addrArray;
  1082. MarkTableTag* m_markArray;
  1083. //Capcity and count of the table (we use capcity for fast & hashing instead of %);
  1084. uint32 m_capcity;
  1085. uint32 m_count;
  1086. uint32 m_h2Prime;
  1087. //iterator position
  1088. uint32 m_iterPos;
  1089. //Counts of how many handlers/types/... we have marked
  1090. uint32 m_handlerCounts[(uint32)MarkTableTag::KindTagCount];
  1091. int32 FindIndexForKey(uint64 addr) const
  1092. {
  1093. TTDAssert(this->m_addrArray != nullptr, "Not valid!!");
  1094. uint32 primaryMask = this->m_capcity - 1;
  1095. uint32 primaryIndex = TTD_MARK_TABLE_HASH1(addr, this->m_capcity);
  1096. uint64 primaryAddr = this->m_addrArray[primaryIndex];
  1097. if ((primaryAddr == addr) | (primaryAddr == 0))
  1098. {
  1099. return (int32)primaryIndex;
  1100. }
  1101. //do a hash for the second offset to avoid clustering and then do linear probing
  1102. uint32 offset = TTD_MARK_TABLE_HASH2(addr, this->m_h2Prime);
  1103. uint32 probeIndex = TTD_MARK_TABLE_INDEX(primaryIndex + offset, this->m_capcity);
  1104. while (true)
  1105. {
  1106. uint64 currAddr = this->m_addrArray[probeIndex];
  1107. if ((currAddr == addr) | (currAddr == 0))
  1108. {
  1109. return (int32)probeIndex;
  1110. }
  1111. probeIndex = TTD_MARK_TABLE_INDEX(probeIndex + 1, this->m_capcity);
  1112. TTDAssert(probeIndex != ((primaryIndex + offset) & primaryMask), "We messed up.");
  1113. }
  1114. }
  1115. void Grow()
  1116. {
  1117. uint32 oldCapacity = this->m_capcity;
  1118. uint64* oldAddrArray = this->m_addrArray;
  1119. MarkTableTag* oldMarkArray = this->m_markArray;
  1120. this->m_capcity = this->m_capcity << 1; //double capacity
  1121. uint32 dummyPowerOf2 = 0;
  1122. uint32 dummyNearPrime = 0;
  1123. LoadValuesForHashTables(this->m_capcity, &dummyPowerOf2, &dummyNearPrime, &(this->m_h2Prime));
  1124. this->m_addrArray = TT_HEAP_ALLOC_ARRAY_ZERO(uint64, this->m_capcity);
  1125. this->m_markArray = TT_HEAP_ALLOC_ARRAY_ZERO(MarkTableTag, this->m_capcity);
  1126. for (uint32 i = 0; i < oldCapacity; ++i)
  1127. {
  1128. int32 idx = this->FindIndexForKey(oldAddrArray[i]);
  1129. this->m_addrArray[idx] = oldAddrArray[i];
  1130. this->m_markArray[idx] = oldMarkArray[i];
  1131. }
  1132. TT_HEAP_FREE_ARRAY(uint64, oldAddrArray, oldCapacity);
  1133. TT_HEAP_FREE_ARRAY(MarkTableTag, oldMarkArray, oldCapacity);
  1134. }
  1135. int32 FindIndexForKeyWGrow(const void* addr)
  1136. {
  1137. //keep the load factor < 25%
  1138. if ((this->m_capcity >> 2) < this->m_count)
  1139. {
  1140. this->Grow();
  1141. }
  1142. return this->FindIndexForKey(reinterpret_cast<uint64>(addr));
  1143. }
  1144. public:
  1145. MarkTable();
  1146. ~MarkTable();
  1147. void Clear();
  1148. //Check if the address has the given tag (return true if it *DOES NOT*) and put the mark tag value in the location
  1149. template <MarkTableTag kindtag>
  1150. bool MarkAndTestAddr(const void* vaddr)
  1151. {
  1152. int32 idx = this->FindIndexForKeyWGrow(vaddr);
  1153. //we really want to do the check on m_markArray but since we know nothing has been cleared we can check the addrArray for better cache behavior
  1154. bool notMarked = this->m_addrArray[idx] == 0;
  1155. if (notMarked)
  1156. {
  1157. this->m_addrArray[idx] = reinterpret_cast<uint64>(vaddr);
  1158. this->m_markArray[idx] = kindtag;
  1159. this->m_count++;
  1160. (this->m_handlerCounts[(uint32)kindtag])++;
  1161. }
  1162. TTDAssert(this->m_markArray[idx] == kindtag, "We had some sort of collision.");
  1163. return notMarked;
  1164. }
  1165. //Mark the address as containing a well known object of the given kind
  1166. template <MarkTableTag specialtag>
  1167. void MarkAddrWithSpecialInfo(const void* vaddr)
  1168. {
  1169. int32 idx = this->FindIndexForKey(reinterpret_cast<uint64>(vaddr));
  1170. if (this->m_markArray[idx] != MarkTableTag::Clear)
  1171. {
  1172. this->m_markArray[idx] |= specialtag;
  1173. }
  1174. }
  1175. //Return true if the location is marked
  1176. bool IsMarked(const void* vaddr) const
  1177. {
  1178. int32 idx = this->FindIndexForKey(reinterpret_cast<uint64>(vaddr));
  1179. return this->m_markArray[idx] != MarkTableTag::Clear;
  1180. }
  1181. //Return true if the location is tagged as well-known
  1182. bool IsTaggedAsWellKnown(const void* vaddr) const
  1183. {
  1184. int32 idx = this->FindIndexForKey(reinterpret_cast<uint64>(vaddr));
  1185. return (this->m_markArray[idx] & MarkTableTag::JsWellKnownObj) != MarkTableTag::Clear;
  1186. }
  1187. //return clear if no more addresses
  1188. MarkTableTag GetTagValue() const
  1189. {
  1190. if (this->m_iterPos >= this->m_capcity)
  1191. {
  1192. return MarkTableTag::Clear;
  1193. }
  1194. else
  1195. {
  1196. return this->m_markArray[this->m_iterPos];
  1197. }
  1198. }
  1199. bool GetTagValueIsWellKnown() const
  1200. {
  1201. return (this->m_markArray[this->m_iterPos] & MarkTableTag::JsWellKnownObj) != MarkTableTag::Clear;
  1202. }
  1203. template<typename T>
  1204. T GetPtrValue() const
  1205. {
  1206. return reinterpret_cast<T>(this->m_addrArray[this->m_iterPos]);
  1207. }
  1208. //Clear the mark at the given address
  1209. void ClearMark(const void* vaddr)
  1210. {
  1211. int32 idx = this->FindIndexForKey(reinterpret_cast<uint64>(vaddr));
  1212. //DON'T CLEAR THE ADDR JUST CLEAR THE MARK
  1213. this->m_markArray[idx] = MarkTableTag::Clear;
  1214. }
  1215. template <MarkTableTag kindtag>
  1216. uint32 GetCountForTag()
  1217. {
  1218. return this->m_handlerCounts[(uint32)kindtag];
  1219. }
  1220. void MoveToNextAddress()
  1221. {
  1222. this->m_iterPos++;
  1223. while (this->m_iterPos < this->m_capcity)
  1224. {
  1225. MarkTableTag tag = this->m_markArray[this->m_iterPos];
  1226. if ((tag & MarkTableTag::AllKindMask) != MarkTableTag::Clear)
  1227. {
  1228. return;
  1229. }
  1230. this->m_iterPos++;
  1231. }
  1232. }
  1233. void InitializeIter()
  1234. {
  1235. this->m_iterPos = 0;
  1236. if (this->m_markArray[0] == MarkTableTag::Clear)
  1237. {
  1238. this->MoveToNextAddress();
  1239. }
  1240. }
  1241. };
  1242. }
  1243. #endif