3 * The copy/mark and gray stack draining functions of the M&S major collector.
5 * Copyright (C) 2014 Xamarin Inc
7 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11 * COPY_OR_MARK_FUNCTION_NAME must be defined to be the function name of the copy/mark
14 * SCAN_OBJECT_FUNCTION_NAME must be defined to be the function name of the object scanning
17 * DRAIN_GRAY_STACK_FUNCTION_NAME must be defined to be the function name of the gray stack
20 * Define COPY_OR_MARK_WITH_EVACUATION to support evacuation.
23 /* Returns whether the object is still in the nursery. */
24 static inline MONO_ALWAYS_INLINE gboolean
25 COPY_OR_MARK_FUNCTION_NAME (GCObject
**ptr
, GCObject
*obj
, SgenGrayQueue
*queue
)
29 #ifdef HEAVY_STATISTICS
30 ++stat_optimized_copy
;
34 if ((forwarded
= SGEN_OBJECT_IS_FORWARDED (obj
)))
35 desc
= sgen_obj_get_descriptor_safe (forwarded
);
37 desc
= sgen_obj_get_descriptor_safe (obj
);
39 sgen_descriptor_count_copied_object (desc
);
43 SGEN_ASSERT (9, obj
, "null object from pointer %p", ptr
);
44 #if !defined(COPY_OR_MARK_CONCURRENT) && !defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION)
45 SGEN_ASSERT (9, current_collection_generation
== GENERATION_OLD
, "old gen parallel allocator called from a %d collection", current_collection_generation
);
48 if (sgen_ptr_in_nursery (obj
)) {
49 #if !defined(COPY_OR_MARK_CONCURRENT) && !defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION)
51 gboolean first
= TRUE
;
52 GCObject
*forwarded
, *old_obj
;
53 mword vtable_word
= *(mword
*)obj
;
55 HEAVY_STAT (++stat_optimized_copy_nursery
);
57 #if SGEN_MAX_DEBUG_LEVEL >= 9
58 if (sgen_nursery_is_to_space (obj
))
59 SGEN_ASSERT (9, !SGEN_VTABLE_IS_PINNED (vtable_word
) && !SGEN_VTABLE_IS_FORWARDED (vtable_word
), "To-space object can't be pinned or forwarded.");
62 if (SGEN_VTABLE_IS_PINNED (vtable_word
)) {
63 SGEN_ASSERT (9, !SGEN_VTABLE_IS_FORWARDED (vtable_word
), "Cannot be both pinned and forwarded.");
64 HEAVY_STAT (++stat_optimized_copy_nursery_pinned
);
67 if ((forwarded
= (GCObject
*)SGEN_VTABLE_IS_FORWARDED (vtable_word
))) {
68 HEAVY_STAT (++stat_optimized_copy_nursery_forwarded
);
69 SGEN_UPDATE_REFERENCE (ptr
, forwarded
);
70 return sgen_ptr_in_nursery (forwarded
);
73 /* An object in the nursery To Space has already been copied and grayed. Nothing to do. */
74 if (sgen_nursery_is_to_space (obj
))
77 #ifdef COPY_OR_MARK_WITH_EVACUATION
81 #ifdef COPY_OR_MARK_PARALLEL
82 obj
= copy_object_no_checks_par (obj
, queue
);
84 obj
= copy_object_no_checks (obj
, queue
);
86 if (G_UNLIKELY (old_obj
== obj
)) {
88 * If we fail to evacuate an object we just stop doing it for a
89 * given block size as all other will surely fail too.
91 /* FIXME: test this case somehow. */
92 if (!sgen_ptr_in_nursery (obj
)) {
94 block
= MS_BLOCK_FOR_OBJ (obj
);
95 size_index
= block
->obj_size_index
;
96 evacuate_block_obj_sizes
[size_index
] = FALSE
;
97 MS_MARK_OBJECT_AND_ENQUEUE (obj
, sgen_obj_get_descriptor (obj
), block
, queue
);
102 HEAVY_STAT (++stat_objects_copied_major
);
103 SGEN_UPDATE_REFERENCE (ptr
, obj
);
105 if (sgen_ptr_in_nursery (obj
))
109 * FIXME: See comment for copy_object_no_checks(). If
110 * we have that, we can let the allocation function
111 * give us the block info, too, and we won't have to
114 * FIXME (2): We should rework this to avoid all those nursery checks.
117 * For the split nursery allocator the object might
118 * still be in the nursery despite having being
119 * promoted, in which case we can't mark it.
121 block
= MS_BLOCK_FOR_OBJ (obj
);
122 MS_CALC_MARK_BIT (word
, bit
, obj
);
123 SGEN_ASSERT (9, !MS_MARK_BIT (block
, word
, bit
), "object %p already marked", obj
);
124 #ifdef COPY_OR_MARK_PARALLEL
125 MS_SET_MARK_BIT_PAR (block
, word
, bit
, first
);
127 MS_SET_MARK_BIT (block
, word
, bit
);
130 binary_protocol_mark (obj
, (gpointer
)SGEN_LOAD_VTABLE (obj
), sgen_safe_object_get_size (obj
));
135 mword vtable_word
= *(mword
*)obj
;
139 HEAVY_STAT (++stat_optimized_copy_major
);
141 #ifdef COPY_OR_MARK_WITH_EVACUATION
144 if ((forwarded
= (GCObject
*)SGEN_VTABLE_IS_FORWARDED (vtable_word
))) {
145 HEAVY_STAT (++stat_optimized_copy_major_forwarded
);
146 SGEN_UPDATE_REFERENCE (ptr
, forwarded
);
147 SGEN_ASSERT (9, !sgen_ptr_in_nursery (forwarded
), "Cannot be forwarded to nursery.");
153 SGEN_ASSERT (9, !SGEN_VTABLE_IS_PINNED (vtable_word
), "Pinned object in non-pinned block?");
155 /* We untag the vtable for concurrent M&S, in case bridge is running and it tagged it */
156 desc
= sgen_vtable_get_descriptor ((GCVTable
)SGEN_POINTER_UNTAG_VTABLE (vtable_word
));
157 type
= desc
& DESC_TYPE_MASK
;
159 if (sgen_safe_object_is_small (obj
, type
)) {
160 #ifdef HEAVY_STATISTICS
161 if (type
<= DESC_TYPE_MAX_SMALL_OBJ
)
162 ++stat_optimized_copy_major_small_fast
;
164 ++stat_optimized_copy_major_small_slow
;
167 block
= MS_BLOCK_FOR_OBJ (obj
);
169 #ifdef COPY_OR_MARK_CONCURRENT_WITH_EVACUATION
170 if (G_UNLIKELY (major_block_is_evacuating (block
))) {
172 * We don't copy within the concurrent phase. These objects will
173 * be handled below in the finishing pause, by scanning the mod-union
180 #ifdef COPY_OR_MARK_WITH_EVACUATION
181 if (major_block_is_evacuating (block
)) {
182 HEAVY_STAT (++stat_optimized_copy_major_small_evacuate
);
187 #ifdef COPY_OR_MARK_PARALLEL
188 MS_MARK_OBJECT_AND_ENQUEUE_PAR (obj
, desc
, block
, queue
);
190 MS_MARK_OBJECT_AND_ENQUEUE (obj
, desc
, block
, queue
);
193 gboolean first
= TRUE
;
194 HEAVY_STAT (++stat_optimized_copy_major_large
);
195 #ifdef COPY_OR_MARK_PARALLEL
196 first
= sgen_los_pin_object_par (obj
);
198 if (sgen_los_object_is_pinned (obj
))
201 sgen_los_pin_object (obj
);
205 binary_protocol_pin (obj
, (gpointer
)SGEN_LOAD_VTABLE (obj
), sgen_safe_object_get_size (obj
));
206 if (SGEN_OBJECT_HAS_REFERENCES (obj
))
207 #ifdef COPY_OR_MARK_PARALLEL
208 GRAY_OBJECT_ENQUEUE_PARALLEL (queue
, obj
, desc
);
210 GRAY_OBJECT_ENQUEUE_SERIAL (queue
, obj
, desc
);
221 SCAN_OBJECT_FUNCTION_NAME (GCObject
*full_object
, SgenDescriptor desc
, SgenGrayQueue
*queue
)
223 char *start
= (char*)full_object
;
225 #ifdef HEAVY_STATISTICS
226 ++stat_optimized_major_scan
;
227 if (!sgen_gc_descr_has_references (desc
))
228 ++stat_optimized_major_scan_no_refs
;
229 sgen_descriptor_count_scanned_object (desc
);
231 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
232 add_scanned_object (start
);
235 /* Now scan the object. */
238 #if defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION)
239 #define HANDLE_PTR(ptr,obj) do { \
240 GCObject *__old = *(ptr); \
241 binary_protocol_scan_process_reference ((full_object), (ptr), __old); \
242 if (__old && !sgen_ptr_in_nursery (__old)) { \
243 if (G_UNLIKELY (full_object && !sgen_ptr_in_nursery (ptr) && \
244 sgen_safe_object_is_small (__old, sgen_obj_get_descriptor (__old) & DESC_TYPE_MASK) && \
245 major_block_is_evacuating (MS_BLOCK_FOR_OBJ (__old)))) { \
246 mark_mod_union_card ((full_object), (void**)(ptr), __old); \
248 PREFETCH_READ (__old); \
249 COPY_OR_MARK_FUNCTION_NAME ((ptr), __old, queue); \
252 if (G_UNLIKELY (full_object && sgen_ptr_in_nursery (__old) && !sgen_ptr_in_nursery ((ptr)) && !sgen_cement_is_forced (__old))) \
253 mark_mod_union_card ((full_object), (void**)(ptr), __old); \
256 #elif defined(COPY_OR_MARK_CONCURRENT)
257 #define HANDLE_PTR(ptr,obj) do { \
258 GCObject *__old = *(ptr); \
259 binary_protocol_scan_process_reference ((full_object), (ptr), __old); \
260 if (__old && !sgen_ptr_in_nursery (__old)) { \
261 PREFETCH_READ (__old); \
262 COPY_OR_MARK_FUNCTION_NAME ((ptr), __old, queue); \
264 if (G_UNLIKELY (full_object && sgen_ptr_in_nursery (__old) && !sgen_ptr_in_nursery ((ptr)) && !sgen_cement_is_forced (__old))) \
265 mark_mod_union_card ((full_object), (void**)(ptr), __old); \
269 #define HANDLE_PTR(ptr,obj) do { \
270 GCObject *__old = *(ptr); \
271 binary_protocol_scan_process_reference ((full_object), (ptr), __old); \
273 gboolean __still_in_nursery = COPY_OR_MARK_FUNCTION_NAME ((ptr), __old, queue); \
274 if (G_UNLIKELY (__still_in_nursery && !sgen_ptr_in_nursery ((ptr)) && !SGEN_OBJECT_IS_CEMENTED (*(ptr)))) { \
275 GCObject *__copy = *(ptr); \
276 sgen_add_to_global_remset ((ptr), __copy); \
282 #define SCAN_OBJECT_PROTOCOL
283 #include "sgen-scan-object.h"
286 #ifdef SCAN_VTYPE_FUNCTION_NAME
288 SCAN_VTYPE_FUNCTION_NAME (GCObject
*full_object
, char *start
, SgenDescriptor desc
, SgenGrayQueue
*queue
BINARY_PROTOCOL_ARG (size_t size
))
290 SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP
;
292 #ifdef HEAVY_STATISTICS
293 /* FIXME: We're half scanning this object. How do we account for that? */
294 //add_scanned_object (start);
297 /* The descriptors include info about the object header as well */
298 start
-= SGEN_CLIENT_OBJECT_HEADER_SIZE
;
300 /* We use the same HANDLE_PTR from the obj scan function */
301 #define SCAN_OBJECT_NOVTABLE
302 #define SCAN_OBJECT_PROTOCOL
303 #include "sgen-scan-object.h"
305 SGEN_OBJECT_LAYOUT_STATISTICS_COMMIT_BITMAP
;
309 #ifdef SCAN_PTR_FIELD_FUNCTION_NAME
311 SCAN_PTR_FIELD_FUNCTION_NAME (GCObject
*full_object
, GCObject
**ptr
, SgenGrayQueue
*queue
)
314 * full_object is NULL if we scan unmanaged memory. This means we can't mark
315 * mod unions for it, so these types of roots currently don't have support
316 * for the concurrent collector (aka they need to be scanned as normal roots
317 * both in the start and finishing pause)
319 HANDLE_PTR (ptr
, NULL
);
324 DRAIN_GRAY_STACK_FUNCTION_NAME (SgenGrayQueue
*queue
)
326 #if defined(COPY_OR_MARK_CONCURRENT) || defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION) || defined(COPY_OR_MARK_PARALLEL)
328 for (i
= 0; i
< 32; i
++) {
335 HEAVY_STAT (++stat_drain_loops
);
337 #if defined(COPY_OR_MARK_PARALLEL)
338 GRAY_OBJECT_DEQUEUE_PARALLEL (queue
, &obj
, &desc
);
340 GRAY_OBJECT_DEQUEUE_SERIAL (queue
, &obj
, &desc
);
345 SCAN_OBJECT_FUNCTION_NAME (obj
, desc
, queue
);
350 #undef COPY_OR_MARK_PARALLEL
351 #undef COPY_OR_MARK_FUNCTION_NAME
352 #undef COPY_OR_MARK_WITH_EVACUATION
353 #undef COPY_OR_MARK_CONCURRENT
354 #undef COPY_OR_MARK_CONCURRENT_WITH_EVACUATION
355 #undef SCAN_OBJECT_FUNCTION_NAME
356 #undef SCAN_VTYPE_FUNCTION_NAME
357 #undef SCAN_PTR_FIELD_FUNCTION_NAME
358 #undef DRAIN_GRAY_STACK_FUNCTION_NAME