CodeGenNumberAllocator.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. #if !FLOATVAR
  7. /****************************************************************************
  8. * CodeGenNumberThreadAllocator
  9. *
  10. * Recycler can't be used by multiple threads. This allocator is used by
  11. * the JIT to allocate numbers in the background thread. It allocates
  12. * pages directly from the OS and then allocates numbers from these pages,
  13. * along with a linked list of chunks that hold the numbers so the native
  14. * entry points can keep these numbers alive. Then this memory is
  15. * integrated back into the main thread's recycler at the beginning of
  16. * GC.
  17. *
  18. * This class requires intimate knowledge of how recycler allocates memory
  19. * so the memory can be integrated back. So changes in the recycler
  20. * will affect this allocator.
  21. *
  22. * An alternative solution is to record the number we need to create,
  23. * and only create them on the main thread when the entry point is used.
  24. * However, since we don't have the address of the number at JIT time,
  25. * we will have to incur two indirections (loading the array of pointers to
  26. * numbers and loading the numbers) in the JIT code which is not desirable.
  27. *
  28. * Implementation details:
  29. *
  30. * The segments can be integrated back to the recycler's page allocator
  31. * immediately. They are all marked as used initially.
  32. *
  33. * Numbers and the linked list chunks are allocated in separate pages
  34. * as number will be a leaf page when integrated back to the recycler
  35. * and the linked list chunks will be normal pages.
  36. *
  37. * Generally, when a page is full, it is (almost) ready to be integrated
  38. * back to the recycler. However the number pages need to wait until
  39. * the referencing linked list chunk is integrated before it can be
  40. * integrated (otherwise, the recycler will not see the reference that
  41. * keeps the numbers alive). So when a number page is full, it will
  42. * go to pendingReferenceNumberBlock list. Then, when a linked list
  43. * chunk is full, all the pages in pendingReferenceNumberBlock can go to
  44. * pendingFlushNumberBlock list and the linked list chunk page will
  45. * go to pendingFlushChunkBlock.
  46. *
  47. * Once we finish jitting a function and the number link list is set on the
  48. * entry point, these pages are ready to be integrated back to recycler
  49. * and be moved to pendingIntegration*Pages lists. Access to the
  50. * pendingIntegration*Pages are synchronized, therefore the main thread
  51. * can do the integration before GC happens.
  52. *
  53. ****************************************************************************/
  54. struct CodeGenNumberChunk
  55. {
  56. static int const MaxNumberCount = 3;
  57. Field(Js::JavascriptNumber*) numbers[MaxNumberCount];
  58. Field(CodeGenNumberChunk*) next;
  59. };
  60. CompileAssert(
  61. sizeof(CodeGenNumberChunk) == HeapConstants::ObjectGranularity ||
  62. sizeof(CodeGenNumberChunk) == HeapConstants::ObjectGranularity * 2);
  63. class CodeGenNumberThreadAllocator
  64. {
  65. friend struct XProcNumberPageSegmentManager;
  66. public:
  67. CodeGenNumberThreadAllocator(Recycler * recycler);
  68. ~CodeGenNumberThreadAllocator();
  69. // All the public API's need to be guarded by critical sections.
  70. // Multiple jit threads access this.
  71. Js::JavascriptNumber * AllocNumber();
  72. CodeGenNumberChunk * AllocChunk();
  73. void Integrate();
  74. void FlushAllocations();
  75. private:
  76. // All allocations are small allocations
  77. const size_t BlockSize = SmallAllocationBlockAttributes::PageCount * AutoSystemInfo::PageSize;
  78. void AllocNewNumberBlock();
  79. void AllocNewChunkBlock();
  80. size_t GetNumberAllocSize();
  81. size_t GetChunkAllocSize();
  82. CriticalSection cs;
  83. Recycler * recycler;
  84. PageSegment * currentNumberSegment;
  85. PageSegment * currentChunkSegment;
  86. char * numberSegmentEnd;
  87. char * currentNumberBlockEnd;
  88. char * nextNumber;
  89. char * chunkSegmentEnd;
  90. char * currentChunkBlockEnd;
  91. char * nextChunk;
  92. bool hasNewNumberBlock;
  93. bool hasNewChunkBlock;
  94. struct BlockRecord
  95. {
  96. BlockRecord(__in_ecount_pagesize char * blockAddress, PageSegment * segment)
  97. : blockAddress(blockAddress), segment(segment)
  98. {
  99. }
  100. char * blockAddress;
  101. PageSegment * segment;
  102. };
  103. // Keep track of segments and pages that needs to be integrated to the recycler.
  104. uint pendingIntegrationNumberSegmentCount;
  105. uint pendingIntegrationChunkSegmentCount;
  106. size_t pendingIntegrationNumberSegmentPageCount;
  107. size_t pendingIntegrationChunkSegmentPageCount;
  108. DListBase<PageSegment> pendingIntegrationNumberSegment;
  109. DListBase<PageSegment> pendingIntegrationChunkSegment;
  110. SListBase<BlockRecord, NoThrowHeapAllocator> pendingIntegrationNumberBlock;
  111. SListBase<BlockRecord, NoThrowHeapAllocator> pendingIntegrationChunkBlock;
  112. // These are finished pages during the code gen of the current function
  113. // We can't integrate them until the code gen is done for the function,
  114. // because the references for the number is not set on the entry point yet.
  115. SListBase<BlockRecord, NoThrowHeapAllocator> pendingFlushNumberBlock;
  116. SListBase<BlockRecord, NoThrowHeapAllocator> pendingFlushChunkBlock;
  117. // Numbers are reference by the chunks, so we need to wait until that is ready
  118. // to be flushed before the number page can be flushed. Otherwise, we might have number
  119. // integrated back to the GC, but the chunk hasn't yet, thus GC won't see the reference.
  120. SListBase<BlockRecord, NoThrowHeapAllocator> pendingReferenceNumberBlock;
  121. };
  122. class CodeGenNumberAllocator
  123. {
  124. public:
  125. CodeGenNumberAllocator(CodeGenNumberThreadAllocator * threadAlloc, Recycler * recycler);
  126. // We should never call this function if we are using tagged float
  127. #if !FLOATVAR
  128. Js::JavascriptNumber * Alloc();
  129. #endif
  130. CodeGenNumberChunk * Finalize();
  131. private:
  132. Recycler * recycler;
  133. CodeGenNumberThreadAllocator * threadAlloc;
  134. CodeGenNumberChunk * chunk;
  135. CodeGenNumberChunk * chunkTail;
  136. uint currentChunkNumberCount;
  137. #if DBG
  138. bool finalized;
  139. #endif
  140. };
  141. namespace Js
  142. {
  143. class StaticType;
  144. }
  145. struct XProcNumberPageSegmentImpl : public XProcNumberPageSegment
  146. {
  147. XProcNumberPageSegmentImpl();
  148. Js::JavascriptNumber* AllocateNumber(Func* func, double value);
  149. unsigned int GetTotalSize() { return PageCount * AutoSystemInfo::PageSize; }
  150. void* GetEndAddress() { return (void*)(this->pageAddress + PageCount * AutoSystemInfo::PageSize); }
  151. void* GetCommitEndAddress() { return (void*)(this->pageAddress + this->committedEnd); }
  152. static const uint BlockSize = SmallAllocationBlockAttributes::PageCount*AutoSystemInfo::PageSize;
  153. static const uint PageCount = Memory::IdleDecommitPageAllocator::DefaultMaxAllocPageCount;
  154. static uint sizeCat;
  155. static void Initialize(bool recyclerVerifyEnabled, uint recyclerVerifyPad);
  156. };
  157. static_assert(sizeof(XProcNumberPageSegmentImpl) == sizeof(XProcNumberPageSegment), "should not have data member in XProcNumberPageSegmentImpl");
  158. struct XProcNumberPageSegmentManager
  159. {
  160. CriticalSection cs;
  161. XProcNumberPageSegmentImpl* segmentsList;
  162. Recycler* recycler;
  163. unsigned int integratedSegmentCount;
  164. XProcNumberPageSegmentManager(Recycler* recycler);
  165. ~XProcNumberPageSegmentManager();
  166. XProcNumberPageSegment * GetFreeSegment(Memory::ArenaAllocator* alloc);
  167. Field(Js::JavascriptNumber*)* RegisterSegments(XProcNumberPageSegment* segments);
  168. void Integrate();
  169. };
  170. #endif