Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / sgen / sgen-marksweep-drain-gray-stack.h
1 /**
2  * \file
3  * The copy/mark and gray stack draining functions of the M&S major collector.
4  *
5  * Copyright (C) 2014 Xamarin Inc
6  *
7  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8  */
9
10 /*
11  * COPY_OR_MARK_FUNCTION_NAME must be defined to be the function name of the copy/mark
12  * function.
13  *
14  * SCAN_OBJECT_FUNCTION_NAME must be defined to be the function name of the object scanning
15  * function.
16  *
17  * DRAIN_GRAY_STACK_FUNCTION_NAME must be defined to be the function name of the gray stack
18  * draining function.
19  *
20  * Define COPY_OR_MARK_WITH_EVACUATION to support evacuation.
21  */
22
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)
26 {
27         MSBlockInfo *block;
28
29 #ifdef HEAVY_STATISTICS
30         ++stat_optimized_copy;
31         {
32                 GCObject *forwarded;
33                 SgenDescriptor desc;
34                 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj)))
35                         desc = sgen_obj_get_descriptor_safe (forwarded);
36                 else
37                         desc = sgen_obj_get_descriptor_safe (obj);
38
39                 sgen_descriptor_count_copied_object (desc);
40         }
41 #endif
42
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);
46 #endif
47
48         if (sgen_ptr_in_nursery (obj)) {
49 #if !defined(COPY_OR_MARK_CONCURRENT) && !defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION)
50                 int word, bit;
51                 gboolean first = TRUE;
52                 GCObject *forwarded, *old_obj;
53                 mword vtable_word = *(mword*)obj;
54
55                 HEAVY_STAT (++stat_optimized_copy_nursery);
56
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.");
60 #endif
61
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);
65                         return TRUE;
66                 }
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);
71                 }
72
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))
75                         return TRUE;
76
77 #ifdef COPY_OR_MARK_WITH_EVACUATION
78         do_copy_object:
79 #endif
80                 old_obj = obj;
81 #ifdef COPY_OR_MARK_PARALLEL
82                 obj = copy_object_no_checks_par (obj, queue);
83 #else
84                 obj = copy_object_no_checks (obj, queue);
85 #endif
86                 if (G_UNLIKELY (old_obj == obj)) {
87                         /*
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.
90                          */
91                         /* FIXME: test this case somehow. */
92                         if (!sgen_ptr_in_nursery (obj)) {
93                                 int size_index;
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);
98                                 return FALSE;
99                         }
100                         return TRUE;
101                 }
102                 HEAVY_STAT (++stat_objects_copied_major);
103                 SGEN_UPDATE_REFERENCE (ptr, obj);
104
105                 if (sgen_ptr_in_nursery (obj))
106                         return TRUE;
107
108                 /*
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
112                  * re-fetch it.
113                  *
114                  * FIXME (2): We should rework this to avoid all those nursery checks.
115                  */
116                 /*
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.
120                  */
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);
126 #else
127                 MS_SET_MARK_BIT (block, word, bit);
128 #endif
129                 if (first)
130                         binary_protocol_mark (obj, (gpointer)SGEN_LOAD_VTABLE (obj), sgen_safe_object_get_size (obj));
131
132                 return FALSE;
133 #endif
134         } else {
135                 mword vtable_word = *(mword*)obj;
136                 SgenDescriptor desc;
137                 int type;
138
139                 HEAVY_STAT (++stat_optimized_copy_major);
140
141 #ifdef COPY_OR_MARK_WITH_EVACUATION
142                 {
143                         GCObject *forwarded;
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.");
148                                 return FALSE;
149                         }
150                 }
151 #endif
152
153                 SGEN_ASSERT (9, !SGEN_VTABLE_IS_PINNED (vtable_word), "Pinned object in non-pinned block?");
154
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;
158
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;
163                         else
164                                 ++stat_optimized_copy_major_small_slow;
165 #endif
166
167                         block = MS_BLOCK_FOR_OBJ (obj);
168
169 #ifdef COPY_OR_MARK_CONCURRENT_WITH_EVACUATION
170                         if (G_UNLIKELY (major_block_is_evacuating (block))) {
171                                 /*
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
174                                  * card table.
175                                  */
176                                 return FALSE;
177                         }
178 #endif
179
180 #ifdef COPY_OR_MARK_WITH_EVACUATION
181                         if (major_block_is_evacuating (block)) {
182                                 HEAVY_STAT (++stat_optimized_copy_major_small_evacuate);
183                                 goto do_copy_object;
184                         }
185 #endif
186
187 #ifdef COPY_OR_MARK_PARALLEL
188                         MS_MARK_OBJECT_AND_ENQUEUE_PAR (obj, desc, block, queue);
189 #else
190                         MS_MARK_OBJECT_AND_ENQUEUE (obj, desc, block, queue);
191 #endif
192                 } else {
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);
197 #else
198                         if (sgen_los_object_is_pinned (obj))
199                                 first = FALSE;
200                         else
201                                 sgen_los_pin_object (obj);
202 #endif
203
204                         if (first) {
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);
209 #else
210                                         GRAY_OBJECT_ENQUEUE_SERIAL (queue, obj, desc);
211 #endif
212                         }
213                 }
214                 return FALSE;
215         }
216
217         return TRUE;
218 }
219
220 static void
221 SCAN_OBJECT_FUNCTION_NAME (GCObject *full_object, SgenDescriptor desc, SgenGrayQueue *queue)
222 {
223         char *start = (char*)full_object;
224
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);
230 #endif
231 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
232         add_scanned_object (start);
233 #endif
234
235         /* Now scan the object. */
236
237 #undef HANDLE_PTR
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); \
247                         } else {                                        \
248                                 PREFETCH_READ (__old);                  \
249                                 COPY_OR_MARK_FUNCTION_NAME ((ptr), __old, queue); \
250                         }                                               \
251                 } else {                                                \
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); \
254                         }                                               \
255                 } while (0)
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); \
263                 } else {                                                \
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); \
266                         }                                               \
267                 } while (0)
268 #else
269 #define HANDLE_PTR(ptr,obj)     do {                                    \
270                 GCObject *__old = *(ptr);                                       \
271                 binary_protocol_scan_process_reference ((full_object), (ptr), __old); \
272                 if (__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); \
277                         }                                               \
278                 }                                                       \
279         } while (0)
280 #endif
281
282 #define SCAN_OBJECT_PROTOCOL
283 #include "sgen-scan-object.h"
284 }
285
286 #ifdef SCAN_VTYPE_FUNCTION_NAME 
287 static void
288 SCAN_VTYPE_FUNCTION_NAME (GCObject *full_object, char *start, SgenDescriptor desc, SgenGrayQueue *queue BINARY_PROTOCOL_ARG (size_t size))
289 {
290         SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP;
291
292 #ifdef HEAVY_STATISTICS
293         /* FIXME: We're half scanning this object.  How do we account for that? */
294         //add_scanned_object (start);
295 #endif
296
297         /* The descriptors include info about the object header as well */
298         start -= SGEN_CLIENT_OBJECT_HEADER_SIZE;
299
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"
304
305         SGEN_OBJECT_LAYOUT_STATISTICS_COMMIT_BITMAP;
306 }
307 #endif
308
309 #ifdef SCAN_PTR_FIELD_FUNCTION_NAME
310 static void
311 SCAN_PTR_FIELD_FUNCTION_NAME (GCObject *full_object, GCObject **ptr, SgenGrayQueue *queue)
312 {
313         /*
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)
318          */
319         HANDLE_PTR (ptr, NULL);
320 }
321 #endif
322
323 static gboolean
324 DRAIN_GRAY_STACK_FUNCTION_NAME (SgenGrayQueue *queue)
325 {
326 #if defined(COPY_OR_MARK_CONCURRENT) || defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION) || defined(COPY_OR_MARK_PARALLEL)
327         int i;
328         for (i = 0; i < 32; i++) {
329 #else
330         for (;;) {
331 #endif
332                 GCObject *obj;
333                 SgenDescriptor desc;
334
335                 HEAVY_STAT (++stat_drain_loops);
336
337 #if defined(COPY_OR_MARK_PARALLEL)
338                 GRAY_OBJECT_DEQUEUE_PARALLEL (queue, &obj, &desc);
339 #else
340                 GRAY_OBJECT_DEQUEUE_SERIAL (queue, &obj, &desc);
341 #endif
342                 if (!obj)
343                         return TRUE;
344
345                 SCAN_OBJECT_FUNCTION_NAME (obj, desc, queue);
346         }
347         return FALSE;
348 }
349
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