/** * \file * The copy/mark and gray stack draining functions of the M&S major collector. * * Copyright (C) 2014 Xamarin Inc * * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ /* * COPY_OR_MARK_FUNCTION_NAME must be defined to be the function name of the copy/mark * function. * * SCAN_OBJECT_FUNCTION_NAME must be defined to be the function name of the object scanning * function. * * DRAIN_GRAY_STACK_FUNCTION_NAME must be defined to be the function name of the gray stack * draining function. * * Define COPY_OR_MARK_WITH_EVACUATION to support evacuation. */ /* Returns whether the object is still in the nursery. */ static inline MONO_ALWAYS_INLINE gboolean COPY_OR_MARK_FUNCTION_NAME (GCObject **ptr, GCObject *obj, SgenGrayQueue *queue) { MSBlockInfo *block; #ifdef HEAVY_STATISTICS ++stat_optimized_copy; { GCObject *forwarded; SgenDescriptor desc; if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj))) desc = sgen_obj_get_descriptor_safe (forwarded); else desc = sgen_obj_get_descriptor_safe (obj); sgen_descriptor_count_copied_object (desc); } #endif SGEN_ASSERT (9, obj, "null object from pointer %p", ptr); #if !defined(COPY_OR_MARK_CONCURRENT) && !defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION) SGEN_ASSERT (9, current_collection_generation == GENERATION_OLD, "old gen parallel allocator called from a %d collection", current_collection_generation); #endif if (sgen_ptr_in_nursery (obj)) { #if !defined(COPY_OR_MARK_CONCURRENT) && !defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION) int word, bit; gboolean first = TRUE; GCObject *forwarded, *old_obj; mword vtable_word = *(mword*)obj; HEAVY_STAT (++stat_optimized_copy_nursery); #if SGEN_MAX_DEBUG_LEVEL >= 9 if (sgen_nursery_is_to_space (obj)) SGEN_ASSERT (9, !SGEN_VTABLE_IS_PINNED (vtable_word) && !SGEN_VTABLE_IS_FORWARDED (vtable_word), "To-space object can't be pinned or forwarded."); #endif if (SGEN_VTABLE_IS_PINNED (vtable_word)) { SGEN_ASSERT (9, !SGEN_VTABLE_IS_FORWARDED (vtable_word), "Cannot be both pinned and forwarded."); HEAVY_STAT (++stat_optimized_copy_nursery_pinned); return TRUE; } if ((forwarded = (GCObject *)SGEN_VTABLE_IS_FORWARDED (vtable_word))) { HEAVY_STAT (++stat_optimized_copy_nursery_forwarded); SGEN_UPDATE_REFERENCE (ptr, forwarded); return sgen_ptr_in_nursery (forwarded); } /* An object in the nursery To Space has already been copied and grayed. Nothing to do. */ if (sgen_nursery_is_to_space (obj)) return TRUE; #ifdef COPY_OR_MARK_WITH_EVACUATION do_copy_object: #endif old_obj = obj; #ifdef COPY_OR_MARK_PARALLEL obj = copy_object_no_checks_par (obj, queue); #else obj = copy_object_no_checks (obj, queue); #endif if (G_UNLIKELY (old_obj == obj)) { /* * If we fail to evacuate an object we just stop doing it for a * given block size as all other will surely fail too. */ /* FIXME: test this case somehow. */ if (!sgen_ptr_in_nursery (obj)) { int size_index; block = MS_BLOCK_FOR_OBJ (obj); size_index = block->obj_size_index; evacuate_block_obj_sizes [size_index] = FALSE; MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue); return FALSE; } return TRUE; } HEAVY_STAT (++stat_objects_copied_major); SGEN_UPDATE_REFERENCE (ptr, obj); if (sgen_ptr_in_nursery (obj)) return TRUE; /* * FIXME: See comment for copy_object_no_checks(). If * we have that, we can let the allocation function * give us the block info, too, and we won't have to * re-fetch it. * * FIXME (2): We should rework this to avoid all those nursery checks. */ /* * For the split nursery allocator the object might * still be in the nursery despite having being * promoted, in which case we can't mark it. */ block = MS_BLOCK_FOR_OBJ (obj); MS_CALC_MARK_BIT (word, bit, obj); SGEN_ASSERT (9, !MS_MARK_BIT (block, word, bit), "object %p already marked", obj); #ifdef COPY_OR_MARK_PARALLEL MS_SET_MARK_BIT_PAR (block, word, bit, first); #else MS_SET_MARK_BIT (block, word, bit); #endif if (first) binary_protocol_mark (obj, (gpointer)SGEN_LOAD_VTABLE (obj), sgen_safe_object_get_size (obj)); return FALSE; #endif } else { mword vtable_word = *(mword*)obj; SgenDescriptor desc; int type; HEAVY_STAT (++stat_optimized_copy_major); #ifdef COPY_OR_MARK_WITH_EVACUATION { GCObject *forwarded; if ((forwarded = (GCObject *)SGEN_VTABLE_IS_FORWARDED (vtable_word))) { HEAVY_STAT (++stat_optimized_copy_major_forwarded); SGEN_UPDATE_REFERENCE (ptr, forwarded); SGEN_ASSERT (9, !sgen_ptr_in_nursery (forwarded), "Cannot be forwarded to nursery."); return FALSE; } } #endif SGEN_ASSERT (9, !SGEN_VTABLE_IS_PINNED (vtable_word), "Pinned object in non-pinned block?"); /* We untag the vtable for concurrent M&S, in case bridge is running and it tagged it */ desc = sgen_vtable_get_descriptor ((GCVTable)SGEN_POINTER_UNTAG_VTABLE (vtable_word)); type = desc & DESC_TYPE_MASK; if (sgen_safe_object_is_small (obj, type)) { #ifdef HEAVY_STATISTICS if (type <= DESC_TYPE_MAX_SMALL_OBJ) ++stat_optimized_copy_major_small_fast; else ++stat_optimized_copy_major_small_slow; #endif block = MS_BLOCK_FOR_OBJ (obj); #ifdef COPY_OR_MARK_CONCURRENT_WITH_EVACUATION if (G_UNLIKELY (major_block_is_evacuating (block))) { /* * We don't copy within the concurrent phase. These objects will * be handled below in the finishing pause, by scanning the mod-union * card table. */ return FALSE; } #endif #ifdef COPY_OR_MARK_WITH_EVACUATION if (major_block_is_evacuating (block)) { HEAVY_STAT (++stat_optimized_copy_major_small_evacuate); goto do_copy_object; } #endif #ifdef COPY_OR_MARK_PARALLEL MS_MARK_OBJECT_AND_ENQUEUE_PAR (obj, desc, block, queue); #else MS_MARK_OBJECT_AND_ENQUEUE (obj, desc, block, queue); #endif } else { gboolean first = TRUE; HEAVY_STAT (++stat_optimized_copy_major_large); #ifdef COPY_OR_MARK_PARALLEL first = sgen_los_pin_object_par (obj); #else if (sgen_los_object_is_pinned (obj)) first = FALSE; else sgen_los_pin_object (obj); #endif if (first) { binary_protocol_pin (obj, (gpointer)SGEN_LOAD_VTABLE (obj), sgen_safe_object_get_size (obj)); if (SGEN_OBJECT_HAS_REFERENCES (obj)) #ifdef COPY_OR_MARK_PARALLEL GRAY_OBJECT_ENQUEUE_PARALLEL (queue, obj, desc); #else GRAY_OBJECT_ENQUEUE_SERIAL (queue, obj, desc); #endif } } return FALSE; } return TRUE; } static void SCAN_OBJECT_FUNCTION_NAME (GCObject *full_object, SgenDescriptor desc, SgenGrayQueue *queue) { char *start = (char*)full_object; #ifdef HEAVY_STATISTICS ++stat_optimized_major_scan; if (!sgen_gc_descr_has_references (desc)) ++stat_optimized_major_scan_no_refs; sgen_descriptor_count_scanned_object (desc); #endif #ifdef SGEN_HEAVY_BINARY_PROTOCOL add_scanned_object (start); #endif /* Now scan the object. */ #undef HANDLE_PTR #if defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION) #define HANDLE_PTR(ptr,obj) do { \ GCObject *__old = *(ptr); \ binary_protocol_scan_process_reference ((full_object), (ptr), __old); \ if (__old && !sgen_ptr_in_nursery (__old)) { \ if (G_UNLIKELY (full_object && !sgen_ptr_in_nursery (ptr) && \ sgen_safe_object_is_small (__old, sgen_obj_get_descriptor (__old) & DESC_TYPE_MASK) && \ major_block_is_evacuating (MS_BLOCK_FOR_OBJ (__old)))) { \ mark_mod_union_card ((full_object), (void**)(ptr), __old); \ } else { \ PREFETCH_READ (__old); \ COPY_OR_MARK_FUNCTION_NAME ((ptr), __old, queue); \ } \ } else { \ if (G_UNLIKELY (full_object && sgen_ptr_in_nursery (__old) && !sgen_ptr_in_nursery ((ptr)) && !sgen_cement_is_forced (__old))) \ mark_mod_union_card ((full_object), (void**)(ptr), __old); \ } \ } while (0) #elif defined(COPY_OR_MARK_CONCURRENT) #define HANDLE_PTR(ptr,obj) do { \ GCObject *__old = *(ptr); \ binary_protocol_scan_process_reference ((full_object), (ptr), __old); \ if (__old && !sgen_ptr_in_nursery (__old)) { \ PREFETCH_READ (__old); \ COPY_OR_MARK_FUNCTION_NAME ((ptr), __old, queue); \ } else { \ if (G_UNLIKELY (full_object && sgen_ptr_in_nursery (__old) && !sgen_ptr_in_nursery ((ptr)) && !sgen_cement_is_forced (__old))) \ mark_mod_union_card ((full_object), (void**)(ptr), __old); \ } \ } while (0) #else #define HANDLE_PTR(ptr,obj) do { \ GCObject *__old = *(ptr); \ binary_protocol_scan_process_reference ((full_object), (ptr), __old); \ if (__old) { \ gboolean __still_in_nursery = COPY_OR_MARK_FUNCTION_NAME ((ptr), __old, queue); \ if (G_UNLIKELY (__still_in_nursery && !sgen_ptr_in_nursery ((ptr)) && !SGEN_OBJECT_IS_CEMENTED (*(ptr)))) { \ GCObject *__copy = *(ptr); \ sgen_add_to_global_remset ((ptr), __copy); \ } \ } \ } while (0) #endif #define SCAN_OBJECT_PROTOCOL #include "sgen-scan-object.h" } #ifdef SCAN_VTYPE_FUNCTION_NAME static void SCAN_VTYPE_FUNCTION_NAME (GCObject *full_object, char *start, SgenDescriptor desc, SgenGrayQueue *queue BINARY_PROTOCOL_ARG (size_t size)) { SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP; #ifdef HEAVY_STATISTICS /* FIXME: We're half scanning this object. How do we account for that? */ //add_scanned_object (start); #endif /* The descriptors include info about the object header as well */ start -= SGEN_CLIENT_OBJECT_HEADER_SIZE; /* We use the same HANDLE_PTR from the obj scan function */ #define SCAN_OBJECT_NOVTABLE #define SCAN_OBJECT_PROTOCOL #include "sgen-scan-object.h" SGEN_OBJECT_LAYOUT_STATISTICS_COMMIT_BITMAP; } #endif #ifdef SCAN_PTR_FIELD_FUNCTION_NAME static void SCAN_PTR_FIELD_FUNCTION_NAME (GCObject *full_object, GCObject **ptr, SgenGrayQueue *queue) { /* * full_object is NULL if we scan unmanaged memory. This means we can't mark * mod unions for it, so these types of roots currently don't have support * for the concurrent collector (aka they need to be scanned as normal roots * both in the start and finishing pause) */ HANDLE_PTR (ptr, NULL); } #endif static gboolean DRAIN_GRAY_STACK_FUNCTION_NAME (SgenGrayQueue *queue) { #if defined(COPY_OR_MARK_CONCURRENT) || defined(COPY_OR_MARK_CONCURRENT_WITH_EVACUATION) || defined(COPY_OR_MARK_PARALLEL) int i; for (i = 0; i < 32; i++) { #else for (;;) { #endif GCObject *obj; SgenDescriptor desc; HEAVY_STAT (++stat_drain_loops); #if defined(COPY_OR_MARK_PARALLEL) GRAY_OBJECT_DEQUEUE_PARALLEL (queue, &obj, &desc); #else GRAY_OBJECT_DEQUEUE_SERIAL (queue, &obj, &desc); #endif if (!obj) return TRUE; SCAN_OBJECT_FUNCTION_NAME (obj, desc, queue); } return FALSE; } #undef COPY_OR_MARK_PARALLEL #undef COPY_OR_MARK_FUNCTION_NAME #undef COPY_OR_MARK_WITH_EVACUATION #undef COPY_OR_MARK_CONCURRENT #undef COPY_OR_MARK_CONCURRENT_WITH_EVACUATION #undef SCAN_OBJECT_FUNCTION_NAME #undef SCAN_VTYPE_FUNCTION_NAME #undef SCAN_PTR_FIELD_FUNCTION_NAME #undef DRAIN_GRAY_STACK_FUNCTION_NAME