2
0

RecyclerWriteBarrierManager.h 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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. namespace Memory
  7. {
  8. //#define RECYCLER_WRITE_BARRIER_INLINE_RECYCLER
  9. #ifdef RECYCLER_WRITE_BARRIER
  10. // Controls whether we're using a 128 byte granularity card table or a 4K granularity byte array to indicate that a range of memory is dirty
  11. #define RECYCLER_WRITE_BARRIER_BYTE
  12. #if GLOBAL_ENABLE_WRITE_BARRIER
  13. // Controls whether the JIT is software write barrier aware
  14. #define RECYCLER_WRITE_BARRIER_JIT
  15. #endif
  16. // Controls whether we can allocate SWB memory or not
  17. // Turning this on leaves the rest of the SWB infrastructure intact,
  18. // it'll simply allocate SWB objects are regular objects
  19. #define RECYCLER_WRITE_BARRIER_ALLOC
  20. #ifdef RECYCLER_WRITE_BARRIER_ALLOC
  21. // Controls which page allocator to allocate SWB objects in
  22. // By default, they use the recycler page allocator
  23. // The following switches allow the user to either use the thread (leaf) page allocator or an entirely new page allocator for SWB objects
  24. //#define RECYCLER_WRITE_BARRIER_ALLOC_THREAD_PAGE
  25. #define RECYCLER_WRITE_BARRIER_ALLOC_SEPARATE_PAGE
  26. #if defined(RECYCLER_WRITE_BARRIER_ALLOC_THREAD_PAGE) && defined(RECYCLER_WRITE_BARRIER_ALLOC_SEPARATE_PAGE)
  27. #error Not supported
  28. #endif
  29. #endif // RECYCLER_WRITE_BARRIER_ALLOC
  30. #ifdef RECYCLER_WRITE_BARRIER_BYTE
  31. #define DIRTYBIT 0x01
  32. #if ENABLE_DEBUG_CONFIG_OPTIONS
  33. #define WRITE_BARRIER_PAGE_BIT 0x2
  34. #else
  35. #define WRITE_BARRIER_PAGE_BIT 0x0
  36. #endif
  37. // indicate the barrier has ever been cleared
  38. #if ENABLE_DEBUG_CONFIG_OPTIONS
  39. #define WRITE_BARRIER_CLEAR_MARK 0x4
  40. #else
  41. #define WRITE_BARRIER_CLEAR_MARK 0x0
  42. #endif
  43. #endif
  44. #ifdef TARGET_64
  45. #ifdef RECYCLER_WRITE_BARRIER_BYTE
  46. #define X64_WB_DIAG 1
  47. class X64WriteBarrierCardTableManager
  48. {
  49. public:
  50. X64WriteBarrierCardTableManager() :
  51. _cardTable(nullptr)
  52. {
  53. }
  54. ~X64WriteBarrierCardTableManager();
  55. BYTE * Initialize();
  56. // Called when a script thread is initialized
  57. bool OnThreadInit();
  58. // Called when a page allocator segment is allocated
  59. bool OnSegmentAlloc(_In_ char* segmentAddress, DECLSPEC_GUARD_OVERFLOW size_t numPages);
  60. // Called when a page allocator segment is freed
  61. bool OnSegmentFree(_In_ char* segmentAddress, size_t numPages);
  62. // Get the card table for the 64 bit address space
  63. BYTE * GetAddressOfCardTable() { return _cardTable; }
  64. #if ENABLE_DEBUG_CONFIG_OPTIONS
  65. void AssertWriteToAddress(_In_ void* address)
  66. {
  67. Assert(_cardTable);
  68. Assert(committedSections.Test(GetSectionIndex(address)));
  69. }
  70. BOOLEAN IsCardTableCommited(_In_ uintptr_t index)
  71. {
  72. return committedSections.Test((BVIndex)(index/ AutoSystemInfo::PageSize));
  73. }
  74. BOOLEAN IsCardTableCommited(_In_ void* address)
  75. {
  76. return committedSections.Test(GetSectionIndex(address));
  77. }
  78. #endif
  79. private:
  80. BVIndex GetSectionIndex(void* address);
  81. // In our card table scheme, 4KB of memory is mapped to 1 byte indicating whether
  82. // it's written to or not. A page in Windows is also 4KB. Therefore, a page's worth
  83. // of memory in the card table will track the state of 16MB of memory, which is what
  84. // we term as a section. A bit set in this bit vector tracks whether a page corresponding
  85. // to said 16MB is committed in the card table.
  86. typedef BVSparse<HeapAllocator> CommittedSectionBitVector;
  87. BYTE* _cardTable;
  88. size_t _cardTableNumEntries;
  89. CriticalSection _cardTableInitCriticalSection;
  90. #ifdef X64_WB_DIAG
  91. enum CommitState
  92. {
  93. CommitStateOnSegmentAlloc,
  94. CommitStateOnNeedCommit,
  95. CommitStateOnSectionCommitted,
  96. CommitStateOnCommitBitSet,
  97. CommitStateFailedMaxAddressExceeded,
  98. CommitStateFailedVirtualAlloc,
  99. CommitStateFailedCommitBitSet
  100. };
  101. char* _lastSegmentAddress;
  102. BYTE* _lastSectionStart;
  103. BYTE* _lastSectionEnd;
  104. size_t _lastSegmentNumPages;
  105. size_t _lastSectionIndexStart;
  106. size_t _lastSectionIndexLast;
  107. CommitState _lastCommitState;
  108. char* _stackbase;
  109. char* _stacklimit;
  110. #endif
  111. #ifdef X64_WB_DIAG
  112. #define SetCommitState(state) this->_lastCommitState = CommitState##state
  113. #else
  114. #define SetCommitState(state)
  115. #endif
  116. static CommittedSectionBitVector committedSections;
  117. };
  118. #else
  119. #error Not implemented
  120. #endif
  121. #endif
  122. class RecyclerWriteBarrierManager
  123. {
  124. public:
  125. static void WriteBarrier(void * address);
  126. static void WriteBarrier(void * address, size_t bytes);
  127. #if ENABLE_DEBUG_CONFIG_OPTIONS
  128. static void ToggleBarrier(void * address, size_t bytes, bool enable);
  129. static bool IsBarrierAddress(void * address);
  130. static bool IsBarrierAddress(uintptr_t index);
  131. static void VerifyIsBarrierAddress(void * address, size_t bytes);
  132. static void VerifyIsNotBarrierAddress(void * address, size_t bytes);
  133. static void VerifyIsBarrierAddress(void * address);
  134. static bool Initialize();
  135. #endif
  136. #if ENABLE_DEBUG_CONFIG_OPTIONS
  137. static bool IsCardTableCommited(_In_ uintptr_t index)
  138. {
  139. #ifdef TARGET_64
  140. return x64CardTableManager.IsCardTableCommited(index) != FALSE;
  141. #else
  142. return true;
  143. #endif
  144. }
  145. static bool IsCardTableCommitedAddress(_In_ void* address)
  146. {
  147. #ifdef TARGET_64
  148. return x64CardTableManager.IsCardTableCommited(address) != FALSE;
  149. #else
  150. return true;
  151. #endif
  152. }
  153. #endif
  154. // For JIT
  155. static uintptr_t GetCardTableIndex(void * address);
  156. #ifdef RECYCLER_WRITE_BARRIER_BYTE
  157. #ifdef TARGET_64
  158. static BYTE * GetAddressOfCardTable() { return x64CardTableManager.GetAddressOfCardTable(); }
  159. #else
  160. static BYTE * GetAddressOfCardTable() { return cardTable; }
  161. #endif
  162. #else
  163. static DWORD * GetAddressOfCardTable() { return cardTable; }
  164. #endif
  165. // For GC
  166. #ifdef TARGET_64
  167. static bool OnThreadInit();
  168. static bool OnSegmentAlloc(_In_ char* segment, DECLSPEC_GUARD_OVERFLOW size_t pageCount);
  169. static bool OnSegmentFree(_In_ char* segment, size_t pageCount);
  170. #endif
  171. static void ResetWriteBarrier(void * address, size_t pageCount);
  172. #ifdef RECYCLER_WRITE_BARRIER_BYTE
  173. static BYTE GetWriteBarrier(void * address);
  174. #else
  175. static DWORD GetWriteBarrier(void * address);
  176. #endif
  177. static size_t const s_WriteBarrierPageSize = 4096;
  178. static uint const s_BitArrayCardTableShift = 7;
  179. static uint const s_BytesPerCardBit = 1 << s_BitArrayCardTableShift; // 128 = 1 << 7
  180. static uint const s_BytesPerCard = s_BytesPerCardBit * 32; // 4K = 1 << 12 = 128 << 5
  181. private:
  182. #ifdef RECYCLER_WRITE_BARRIER_BYTE
  183. #ifdef TARGET_64
  184. // On AMD64, we use a different scheme
  185. // As of Windows 8.1, the process user-mode address space is 128TB
  186. // We still use a write barrier page size of 4KB
  187. // If we used a static card table, the address space needed for the array would be 32 GB
  188. // To get around this, we instead reserve 32 GB of address space for the card table
  189. // The page allocator will commit the relevant parts of the card table when
  190. // needed. Since the card table is dynamically allocated at runtime, we need one additional
  191. // indirection to look up the card.
  192. static X64WriteBarrierCardTableManager x64CardTableManager;
  193. static BYTE* cardTable; // 1 byte per 4096
  194. #else
  195. static BYTE cardTable[1 * 1024 * 1024]; // 1 byte per 4096
  196. #endif
  197. #else
  198. static DWORD cardTable[1 * 1024 * 1024]; // 128 bytes per bit, 4096 per DWORD
  199. #endif
  200. };
  201. #ifdef RECYCLER_TRACE
  202. #define SwbTrace(flags, ...) \
  203. if (flags.Trace.IsEnabled(Js::MemoryAllocationPhase)) \
  204. { \
  205. Output::Print(__VA_ARGS__); \
  206. }
  207. #define GlobalSwbVerboseTrace(...) \
  208. if (Js::Configuration::Global.flags.Verbose && \
  209. Js::Configuration::Global.flags.Trace.IsEnabled(Js::MemoryAllocationPhase)) \
  210. { \
  211. Output::Print(__VA_ARGS__); \
  212. }
  213. #define SwbVerboseTrace(flags, ...) \
  214. if (flags.Verbose && \
  215. flags.Trace.IsEnabled(Js::MemoryAllocationPhase)) \
  216. { \
  217. Output::Print(__VA_ARGS__); \
  218. }
  219. #else
  220. #define SwbTrace(...)
  221. #define SwbVerboseTrace(...)
  222. #define GlobalSwbVerboseTrace(...)
  223. #endif
  224. #endif
  225. }