2
0

CodeGenNumberAllocator.h 6.0 KB

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