TTSupport.h 55 KB

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