MarkContext.inl 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. inline
  6. bool MarkContext::AddMarkedObject(void * objectAddress, size_t objectSize)
  7. {
  8. Assert(objectAddress != nullptr);
  9. Assert(objectSize > 0);
  10. Assert(objectSize % sizeof(void *) == 0);
  11. FAULTINJECT_MEMORY_MARK_NOTHROW(_u("AddMarkedObject"), objectSize);
  12. #if DBG_DUMP
  13. if (recycler->forceTraceMark || recycler->GetRecyclerFlagsTable().Trace.IsEnabled(Js::MarkPhase))
  14. {
  15. Output::Print(_u(" %p"), objectAddress);
  16. }
  17. #endif
  18. RECYCLER_STATS_INTERLOCKED_INC(recycler, scanCount);
  19. MarkCandidate markCandidate;
  20. #if defined(_WIN32) && defined(_M_X64)
  21. // Enabling store forwards. The intrinsic generates stores matching the load in size.
  22. // This enables skipping caches and forwarding the store data to the following load.
  23. *(__m128i *)&markCandidate = _mm_set_epi64x(objectSize, (__int64)objectAddress);
  24. #else
  25. markCandidate.obj = (void**)objectAddress;
  26. markCandidate.byteCount = objectSize;
  27. #endif
  28. return markStack.Push(markCandidate);
  29. }
  30. #ifdef RECYCLER_VISITED_HOST
  31. inline bool MarkContext::AddPreciselyTracedObject(IRecyclerVisitedObject* obj)
  32. {
  33. FAULTINJECT_MEMORY_MARK_NOTHROW(_u("AddPreciselyTracedObject"), 0);
  34. return preciseStack.Push(obj);
  35. }
  36. #endif
  37. #if ENABLE_CONCURRENT_GC
  38. inline
  39. bool MarkContext::AddTrackedObject(FinalizableObject * obj)
  40. {
  41. Assert(obj != nullptr);
  42. #if ENABLE_CONCURRENT_GC
  43. Assert(recycler->DoQueueTrackedObject());
  44. #endif
  45. #if ENABLE_PARTIAL_GC
  46. Assert(!recycler->inPartialCollectMode);
  47. #endif
  48. FAULTINJECT_MEMORY_MARK_NOTHROW(_u("AddTrackedObject"), 0);
  49. return trackStack.Push(obj);
  50. }
  51. #endif
  52. template <bool parallel, bool interior, bool doSpecialMark>
  53. NO_SANITIZE_ADDRESS
  54. inline
  55. void MarkContext::ScanMemory(void ** obj, size_t byteCount
  56. ADDRESS_SANITIZER_APPEND(void *asanFakeStack))
  57. {
  58. Assert(byteCount != 0);
  59. Assert(byteCount % sizeof(void *) == 0);
  60. void ** objEnd = obj + (byteCount / sizeof(void *));
  61. void * parentObject = (void*)obj;
  62. #if DBG_DUMP
  63. if (recycler->forceTraceMark || recycler->GetRecyclerFlagsTable().Trace.IsEnabled(Js::MarkPhase))
  64. {
  65. Output::Print(_u("Scanning %p(%8d): "), obj, byteCount);
  66. }
  67. #endif
  68. #if __has_feature(address_sanitizer)
  69. void *fakeFrameBegin = nullptr;
  70. void *fakeFrameEnd = nullptr;
  71. #endif
  72. do
  73. {
  74. // We need to ensure that the compiler does not reintroduce reads to the object after inlining.
  75. // This could cause the value to change after the marking checks (e.g., the null/low address check).
  76. // Intrinsics avoid the expensive memory barrier on ARM (due to /volatile:ms).
  77. #if defined(_M_ARM64)
  78. void * candidate = reinterpret_cast<void *>(__iso_volatile_load64(reinterpret_cast<volatile __int64 *>(obj)));
  79. #elif defined(_M_ARM)
  80. void * candidate = reinterpret_cast<void *>(__iso_volatile_load32(reinterpret_cast<volatile __int32 *>(obj)));
  81. #else
  82. void * candidate = *(static_cast<void * volatile *>(obj));
  83. #endif
  84. #if DBG
  85. this->parentRef = obj;
  86. #endif
  87. #if __has_feature(address_sanitizer)
  88. bool isFakeStackAddr = false;
  89. if (asanFakeStack)
  90. {
  91. void *beg = nullptr;
  92. void *end = nullptr;
  93. isFakeStackAddr = __asan_addr_is_in_fake_stack(asanFakeStack, candidate, &beg, &end) != nullptr;
  94. if (isFakeStackAddr && (beg != fakeFrameBegin || end != fakeFrameEnd))
  95. {
  96. ScanMemory<parallel, interior, doSpecialMark>((void**)beg, (char*)end - (char*)beg);
  97. fakeFrameBegin = beg;
  98. fakeFrameEnd = end;
  99. }
  100. }
  101. if (!isFakeStackAddr)
  102. {
  103. #endif
  104. Mark<parallel, interior, doSpecialMark>(candidate, parentObject);
  105. #if __has_feature(address_sanitizer)
  106. }
  107. #endif
  108. obj++;
  109. } while (obj != objEnd);
  110. #if DBG
  111. this->parentRef = nullptr;
  112. #endif
  113. #if DBG_DUMP
  114. if (recycler->forceTraceMark || recycler->GetRecyclerFlagsTable().Trace.IsEnabled(Js::MarkPhase))
  115. {
  116. Output::Print(_u("\n"));
  117. Output::Flush();
  118. }
  119. #endif
  120. }
  121. template <bool parallel, bool interior>
  122. inline
  123. void MarkContext::ScanObject(void ** obj, size_t byteCount)
  124. {
  125. BEGIN_DUMP_OBJECT(recycler, obj);
  126. ScanMemory<parallel, interior, false>(obj, byteCount);
  127. END_DUMP_OBJECT(recycler);
  128. }
  129. template <bool parallel, bool interior, bool doSpecialMark>
  130. inline
  131. void MarkContext::Mark(void * candidate, void * parentReference)
  132. {
  133. // We should never reach here while we are processing Rescan.
  134. // Otherwise our rescanState could be out of sync with mark state.
  135. Assert(!recycler->isProcessingRescan);
  136. #if defined(RECYCLER_STATS) || !defined(_M_X64)
  137. if ((size_t)candidate < 0x10000)
  138. {
  139. RECYCLER_STATS_INTERLOCKED_INC(recycler, tryMarkNullCount);
  140. return;
  141. }
  142. #endif
  143. if (interior)
  144. {
  145. recycler->heapBlockMap.MarkInterior<parallel, doSpecialMark>(candidate, this);
  146. return;
  147. }
  148. #if defined(RECYCLER_STATS) || !defined(_M_X64)
  149. if (!HeapInfo::IsAlignedAddress(candidate))
  150. {
  151. RECYCLER_STATS_INTERLOCKED_INC(recycler, tryMarkUnalignedCount);
  152. return;
  153. }
  154. #endif
  155. recycler->heapBlockMap.Mark<parallel, doSpecialMark>(candidate, this);
  156. #ifdef RECYCLER_MARK_TRACK
  157. this->OnObjectMarked(candidate, parentReference);
  158. #endif
  159. }
  160. inline
  161. void MarkContext::MarkTrackedObject(FinalizableObject * trackedObject)
  162. {
  163. #if ENABLE_CONCURRENT_GC
  164. Assert(!recycler->queueTrackedObject);
  165. #if ENABLE_ALLOCATIONS_DURING_CONCURRENT_SWEEP
  166. Assert(!recycler->IsConcurrentExecutingState() && !recycler->IsConcurrentSweepState());
  167. #else
  168. Assert(!recycler->IsConcurrentExecutingState());
  169. #endif
  170. #endif
  171. #if ENABLE_PARTIAL_GC
  172. Assert(!recycler->inPartialCollectMode);
  173. #endif
  174. Assert(!(recycler->collectionState == CollectionStateParallelMark));
  175. // Mark is not expected to throw.
  176. BEGIN_NO_EXCEPTION
  177. {
  178. trackedObject->Mark(recycler);
  179. }
  180. END_NO_EXCEPTION
  181. }
  182. template <bool parallel, bool interior>
  183. inline
  184. void MarkContext::ProcessMark()
  185. {
  186. #ifdef RECYCLER_STRESS
  187. if (recycler->GetRecyclerFlagsTable().RecyclerInduceFalsePositives)
  188. {
  189. // InduceFalsePositives logic doesn't support parallel marking
  190. if (!parallel)
  191. {
  192. recycler->heapBlockMap.InduceFalsePositives(recycler);
  193. }
  194. }
  195. #endif
  196. #ifdef RECYCLER_VISITED_HOST
  197. // Flip between processing the generic mark stack (conservatively traced with ScanMemory) and
  198. // the precise stack (precisely traced via IRecyclerVisitedObject::Trace). Each of those
  199. // operations on an object has the potential to add new marked objects to either or both
  200. // stacks so we must loop until they are both empty.
  201. while (!markStack.IsEmpty() || !preciseStack.IsEmpty())
  202. #endif
  203. {
  204. // It is possible that when the stacks were split, only one of them had any chunks to process.
  205. // If that is the case, one of the stacks might not be initialized, so we must check !IsEmpty before popping.
  206. if (!markStack.IsEmpty())
  207. {
  208. #if defined(_M_IX86) || defined(_M_X64)
  209. MarkCandidate current, next;
  210. while (markStack.Pop(&current))
  211. {
  212. // Process entries and prefetch as we go.
  213. while (markStack.Pop(&next))
  214. {
  215. // Prefetch the next entry so it's ready when we need it.
  216. _mm_prefetch((char *)next.obj, _MM_HINT_T0);
  217. // Process the previously retrieved entry.
  218. ScanObject<parallel, interior>(current.obj, current.byteCount);
  219. _mm_prefetch((char *)*(next.obj), _MM_HINT_T0);
  220. current = next;
  221. }
  222. // The stack is empty, but we still have a previously retrieved entry; process it now.
  223. ScanObject<parallel, interior>(current.obj, current.byteCount);
  224. // Processing that entry may have generated more entries in the mark stack, so continue the loop.
  225. }
  226. #else
  227. // _mm_prefetch intrinsic is specific to Intel platforms.
  228. // CONSIDER: There does seem to be a compiler intrinsic for prefetch on ARM,
  229. // however, the information on this is scarce, so for now just don't do prefetch on ARM.
  230. MarkCandidate current;
  231. while (markStack.Pop(&current))
  232. {
  233. ScanObject<parallel, interior>(current.obj, current.byteCount);
  234. }
  235. #endif
  236. }
  237. Assert(markStack.IsEmpty());
  238. #ifdef RECYCLER_VISITED_HOST
  239. if (!preciseStack.IsEmpty())
  240. {
  241. MarkContextWrapper<parallel> markContextWrapper(this);
  242. IRecyclerVisitedObject* tracedObject;
  243. while (preciseStack.Pop(&tracedObject))
  244. {
  245. tracedObject->Trace(&markContextWrapper);
  246. }
  247. }
  248. Assert(preciseStack.IsEmpty());
  249. #endif
  250. }
  251. }