2 * sgen-tarjan-bridge.c: Tarjan-based bridge implementation.
4 * Copyright 2011 Novell, Inc (http://www.novell.com)
5 * Copyright 2014 Xamarin Inc (http://www.xamarin.com)
8 * Copyright 2001-2003 Ximian, Inc
9 * Copyright 2003-2010 Novell, Inc.
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
20 #include "sgen/sgen-gc.h"
21 #include "sgen-bridge-internals.h"
22 #include "sgen/sgen-hash-table.h"
23 #include "sgen/sgen-qsort.h"
24 #include "tabledefs.h"
25 #include "utils/mono-logger-internals.h"
42 dyn_array_uninit (DynArray *da, int elem_size)
44 if (da->capacity <= 0)
47 sgen_free_internal_dynamic (da->data, elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA);
52 dyn_array_ensure_capacity (DynArray *da, int capacity, int elem_size)
54 int old_capacity = da->capacity;
57 if (capacity <= old_capacity)
60 if (da->capacity == 0)
62 while (capacity > da->capacity)
65 new_data = (char *)sgen_alloc_internal_dynamic (elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA, TRUE);
67 memcpy (new_data, da->data, elem_size * da->size);
68 sgen_free_internal_dynamic (da->data, elem_size * old_capacity, INTERNAL_MEM_BRIDGE_DATA);
74 dyn_array_add (DynArray *da, int elem_size)
78 dyn_array_ensure_capacity (da, da->size + 1, elem_size);
80 p = da->data + da->size * elem_size;
88 dyn_array_ptr_uninit (DynPtrArray *da)
90 dyn_array_uninit (&da->array, sizeof (void*));
94 dyn_array_ptr_size (DynPtrArray *da)
96 return da->array.size;
100 dyn_array_ptr_set_size (DynPtrArray *da, int size)
102 da->array.size = size;
106 dyn_array_ptr_get (DynPtrArray *da, int x)
108 return ((void**)da->array.data)[x];
112 dyn_array_ptr_add (DynPtrArray *da, void *ptr)
114 void **p = (void **)dyn_array_add (&da->array, sizeof (void*));
118 #define dyn_array_ptr_push dyn_array_ptr_add
121 dyn_array_ptr_pop (DynPtrArray *da)
124 int size = da->array.size;
126 p = dyn_array_ptr_get (da, size - 1);
132 dyn_array_ptr_ensure_capacity (DynPtrArray *da, int capacity)
134 dyn_array_ensure_capacity (&da->array, capacity, sizeof (void*));
139 dyn_array_ptr_set_all (DynPtrArray *dst, DynPtrArray *src)
141 if (src->array.size > 0) {
142 dyn_array_ptr_ensure_capacity (dst, src->array.size);
143 memcpy (dst->array.data, src->array.data, src->array.size * sizeof (void*));
145 dst->array.size = src->array.size;
149 enable_accounting (void)
151 // bridge_accounting_enabled = TRUE;
152 // hash_table = (SgenHashTable)SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_HASH_TABLE, INTERNAL_MEM_BRIDGE_HASH_TABLE_ENTRY, sizeof (HashEntryWithAccounting), mono_aligned_addr_hash, NULL);
155 static MonoGCBridgeObjectKind
156 class_kind (MonoClass *klass)
158 MonoGCBridgeObjectKind res = bridge_callbacks.bridge_class_kind (klass);
160 /* If it's a bridge, nothing we can do about it. */
161 if (res == GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS || res == GC_BRIDGE_OPAQUE_BRIDGE_CLASS)
164 /* Non bridge classes with no pointers will never point to a bridge, so we can savely ignore them. */
165 if (!klass->has_references) {
166 SGEN_LOG (6, "class %s is opaque\n", klass->name);
167 return GC_BRIDGE_OPAQUE_CLASS;
170 /* Some arrays can be ignored */
171 if (klass->rank == 1) {
172 MonoClass *elem_class = klass->element_class;
174 /* FIXME the bridge check can be quite expensive, cache it at the class level. */
175 /* An array of a sealed type that is not a bridge will never get to a bridge */
176 if ((elem_class->flags & TYPE_ATTRIBUTE_SEALED) && !elem_class->has_references && !bridge_callbacks.bridge_class_kind (elem_class)) {
177 SGEN_LOG (6, "class %s is opaque\n", klass->name);
178 return GC_BRIDGE_OPAQUE_CLASS;
182 return GC_BRIDGE_TRANSPARENT_CLASS;
185 //enable unsage logging
186 // #define DUMP_GRAPH 1
197 We can split this data structure in two, those with bridges and those without
200 DynPtrArray other_colors;
203 unsigned visited : 1;
208 GCObject *obj; //XXX this can be eliminated.
216 unsigned is_bridge : 1;
217 unsigned obj_state : 2;
223 static DynPtrArray scan_stack, loop_stack, registered_bridges;
224 static DynPtrArray color_merge_array;
226 static int ignored_objects;
227 static int object_index;
228 static int num_colors_with_bridges;
229 static int xref_count;
231 static size_t setup_time, tarjan_time, scc_setup_time, gather_xref_time, xref_setup_time, cleanup_time;
232 static SgenBridgeProcessor *bridge_processor;
234 #define BUCKET_SIZE 8184
237 #define NUM_SCAN_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ScanData))
239 typedef struct _ObjectBucket ObjectBucket;
240 struct _ObjectBucket {
243 ScanData data [NUM_SCAN_ENTRIES];
246 static ObjectBucket *root_object_bucket;
247 static ObjectBucket *cur_object_bucket;
248 static int object_data_count;
251 new_object_bucket (void)
253 ObjectBucket *res = (ObjectBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
254 res->next_data = &res->data [0];
259 object_alloc_init (void)
261 root_object_bucket = cur_object_bucket = new_object_bucket ();
265 alloc_object_data (void)
270 /* next_data points to the first free entry */
271 res = cur_object_bucket->next_data;
272 if (res >= &cur_object_bucket->data [NUM_SCAN_ENTRIES]) {
273 ObjectBucket *b = new_object_bucket ();
274 cur_object_bucket->next = b;
275 cur_object_bucket = b;
278 cur_object_bucket->next_data = res + 1;
284 free_object_buckets (void)
286 ObjectBucket *cur = root_object_bucket;
288 object_data_count = 0;
291 ObjectBucket *tmp = cur->next;
292 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
296 root_object_bucket = cur_object_bucket = NULL;
300 #define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData))
302 typedef struct _ColorBucket ColorBucket;
303 struct _ColorBucket {
305 ColorData *next_data;
306 ColorData data [NUM_COLOR_ENTRIES];
309 static ColorBucket *root_color_bucket;
310 static ColorBucket *cur_color_bucket;
311 static int color_data_count;
315 new_color_bucket (void)
317 ColorBucket *res = (ColorBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
318 res->next_data = &res->data [0];
323 color_alloc_init (void)
325 root_color_bucket = cur_color_bucket = new_color_bucket ();
329 alloc_color_data (void)
334 /* next_data points to the first free entry */
335 res = cur_color_bucket->next_data;
336 if (res >= &cur_color_bucket->data [NUM_COLOR_ENTRIES]) {
337 ColorBucket *b = new_color_bucket ();
338 cur_color_bucket->next = b;
339 cur_color_bucket = b;
342 cur_color_bucket->next_data = res + 1;
348 free_color_buckets (void)
350 ColorBucket *cur, *tmp;
352 color_data_count = 0;
354 for (cur = root_color_bucket; cur; cur = tmp) {
356 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
357 dyn_array_ptr_uninit (&cd->other_colors);
358 dyn_array_ptr_uninit (&cd->bridges);
361 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
363 root_color_bucket = cur_color_bucket = NULL;
368 create_data (GCObject *obj)
370 mword *o = (mword*)obj;
371 ScanData *res = alloc_object_data ();
374 res->index = res->low_index = -1;
375 res->obj_state = o [0] & SGEN_VTABLE_BITS_MASK;
376 res->lock_word = o [1];
378 o [0] |= SGEN_VTABLE_BITS_MASK;
384 find_data (GCObject *obj)
387 mword *o = (mword*)obj;
388 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
389 a = (ScanData*)o [1];
394 clear_after_processing (void)
398 for (cur = root_object_bucket; cur; cur = cur->next) {
400 for (sd = &cur->data [0]; sd < cur->next_data; ++sd) {
401 mword *o = (mword*)sd->obj;
402 o [0] &= ~SGEN_VTABLE_BITS_MASK;
403 o [0] |= sd->obj_state;
404 o [1] = sd->lock_word;
410 bridge_object_forward (GCObject *obj)
413 mword *o = (mword*)obj;
414 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
417 fwd = SGEN_OBJECT_IS_FORWARDED (obj);
418 return fwd ? fwd : obj;
423 safe_name_bridge (GCObject *obj)
425 GCVTable vt = SGEN_LOAD_VTABLE (obj);
426 return vt->klass->name;
430 find_or_create_data (GCObject *obj)
432 ScanData *entry = find_data (obj);
434 entry = create_data (obj);
446 We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
448 The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline,
449 making the extra space pretty much free.
451 The cost between 32 and 128 itens is minimal too, it's mostly a fixed, setup cost.
453 Memory wise, 4/32 takes 512 and 8/128 takes 8k, so it's quite reasonable.
456 #define ELEMENTS_PER_BUCKET 8
457 #define COLOR_CACHE_SIZE 128
458 static HashEntry merge_cache [COLOR_CACHE_SIZE][ELEMENTS_PER_BUCKET];
461 mix_hash (size_t hash)
463 return (int)(((hash * 215497) >> 16) ^ ((hash * 1823231) + hash));
469 memset (merge_cache, 0, sizeof (merge_cache));
474 dyn_array_ptr_contains (DynPtrArray *da, void *x)
477 for (i = 0; i < dyn_array_ptr_size (da); ++i)
478 if (dyn_array_ptr_get (da, i) == x)
484 match_colors (DynPtrArray *a, DynPtrArray *b)
487 if (dyn_array_ptr_size (a) != dyn_array_ptr_size (b))
490 for (i = 0; i < dyn_array_ptr_size (a); ++i) {
491 if (!dyn_array_ptr_contains (b, dyn_array_ptr_get (a, i)))
497 static int cache_hits, cache_misses;
500 find_in_cache (int *insert_index)
503 int i, hash, size, index;
505 size = dyn_array_ptr_size (&color_merge_array);
506 /* Cache checking is very ineficient with a lot of elements*/
511 for (i = 0 ; i < size; ++i)
512 hash += mix_hash ((size_t)dyn_array_ptr_get (&color_merge_array, i));
516 index = hash & (COLOR_CACHE_SIZE - 1);
517 bucket = merge_cache [index];
518 for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) {
519 if (bucket [i].hash != hash)
521 if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) {
523 return bucket [i].color;
527 //move elements to the back
528 for (i = ELEMENTS_PER_BUCKET - 1; i > 0; --i)
529 bucket [i] = bucket [i - 1];
531 *insert_index = index;
532 bucket [0].hash = hash;
537 new_color (gboolean force_new)
541 /* XXX Try to find an equal one and return it */
543 cd = find_in_cache (&i);
548 cd = alloc_color_data ();
550 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
551 /* if i >= 0, it means we prepared a given slot to receive the new color */
553 merge_cache [i][0].color = cd;
560 register_bridge_object (GCObject *obj)
562 create_data (obj)->is_bridge = TRUE;
566 is_opaque_object (GCObject *obj)
568 MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
569 if ((vt->gc_bits & SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) {
570 SGEN_LOG (6, "ignoring %s\n", vt->klass->name);
578 push_object (GCObject *obj)
581 obj = bridge_object_forward (obj);
584 printf ("\t= pushing %p %s -> ", obj, safe_name_bridge (obj));
587 /* Object types we can ignore */
588 if (is_opaque_object (obj)) {
595 data = find_data (obj);
597 /* Already marked - XXX must be done this way as the bridge themselves are alive. */
598 if (data && data->state != INITIAL) {
600 printf ("already marked\n");
605 /* We only care about dead objects */
606 if (!data && sgen_object_is_live (obj)) {
614 printf ("pushed!\n");
618 data = create_data (obj);
619 g_assert (data->state == INITIAL);
620 g_assert (data->index == -1);
621 dyn_array_ptr_push (&scan_stack, data);
625 #define HANDLE_PTR(ptr,obj) do { \
626 GCObject *dst = (GCObject*)*(ptr); \
627 if (dst) push_object (dst); \
631 push_all (ScanData *data)
633 GCObject *obj = data->obj;
634 char *start = (char*)obj;
635 mword desc = sgen_obj_get_descriptor_safe (obj);
638 printf ("**scanning %p %s\n", obj, safe_name_bridge (obj));
641 #include "sgen/sgen-scan-object.h"
646 compute_low_index (ScanData *data, GCObject *obj)
650 obj = bridge_object_forward (obj);
652 other = find_data (obj);
655 printf ("\tcompute low %p ->%p (%s) %p (%d / %d)\n", data->obj, obj, safe_name_bridge (obj), other, other ? other->index : -2, other ? other->low_index : -2);
660 g_assert (other->state != INITIAL);
662 if ((other->state == SCANNED || other->state == FINISHED_ON_STACK) && data->low_index > other->low_index)
663 data->low_index = other->low_index;
665 /* Compute the low color */
666 if (other->color == NULL)
671 dyn_array_ptr_add (&color_merge_array, other->color);
677 #define HANDLE_PTR(ptr,obj) do { \
678 GCObject *dst = (GCObject*)*(ptr); \
679 if (dst) compute_low_index (data, dst); \
683 compute_low (ScanData *data)
685 GCObject *obj = data->obj;
686 char *start = (char*)obj;
687 mword desc = sgen_obj_get_descriptor_safe (obj);
689 #include "sgen/sgen-scan-object.h"
695 ColorData *color = NULL;
696 int size = dyn_array_ptr_size (&color_merge_array);
700 else if (size == 1) {
701 color = (ColorData *)dyn_array_ptr_get (&color_merge_array, 0);
703 color = new_color (FALSE);
709 create_scc (ScanData *data)
712 gboolean found = FALSE;
713 gboolean found_bridge = FALSE;
714 ColorData *color_data = NULL;
716 for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) {
717 ScanData *other = (ScanData *)dyn_array_ptr_get (&loop_stack, i);
718 found_bridge |= other->is_bridge;
719 if (found_bridge || other == data)
724 printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data->obj), data->obj, found_bridge);
725 printf ("\tpoints-to-colors: ");
726 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i)
727 printf ("%p ", dyn_array_ptr_get (&color_merge_array, i));
730 printf ("loop stack: ");
731 for (i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) {
732 ScanData *other = dyn_array_ptr_get (&loop_stack, i);
733 printf ("(%d/%d)", other->index, other->low_index);
739 color_data = new_color (TRUE);
740 ++num_colors_with_bridges;
742 color_data = reduce_color ();
745 while (dyn_array_ptr_size (&loop_stack) > 0) {
746 ScanData *other = (ScanData *)dyn_array_ptr_pop (&loop_stack);
749 printf ("\tmember %s (%p) index %d low-index %d color %p state %d\n", safe_name_bridge (other->obj), other->obj, other->index, other->low_index, other->color, other->state);
752 other->color = color_data;
753 switch (other->state) {
754 case FINISHED_ON_STACK:
755 other->state = FINISHED_OFF_STACK;
757 case FINISHED_OFF_STACK:
760 g_error ("Invalid state when building SCC %d", other->state);
763 if (other->is_bridge)
764 dyn_array_ptr_add (&color_data->bridges, other->obj);
773 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) {
774 ColorData *cd = (ColorData *)dyn_array_ptr_get (&color_merge_array, i);
775 g_assert (cd->visited);
778 dyn_array_ptr_set_size (&color_merge_array, 0);
779 found_bridge = FALSE;
785 g_assert (dyn_array_ptr_size (&scan_stack) == 1);
786 g_assert (dyn_array_ptr_size (&loop_stack) == 0);
788 dyn_array_ptr_set_size (&color_merge_array, 0);
790 while (dyn_array_ptr_size (&scan_stack) > 0) {
791 ScanData *data = (ScanData *)dyn_array_ptr_pop (&scan_stack);
794 * Ignore finished objects on stack, they happen due to loops. For example:
800 * We start scanning from A and push C before B. So, after the first iteration, the scan stack will have: A C B.
801 * We then visit B, which will find C in its initial state and push again.
802 * Finally after finish with C and B, the stack will be left with "A C" and at this point C should be ignored.
804 * The above explains FINISHED_ON_STACK, to explain FINISHED_OFF_STACK, consider if the root was D, which pointed
805 * to A and C. A is processed first, leaving C on stack after that in the mentioned state.
807 if (data->state == FINISHED_ON_STACK || data->state == FINISHED_OFF_STACK)
810 if (data->state == INITIAL) {
811 g_assert (data->index == -1);
812 g_assert (data->low_index == -1);
814 data->state = SCANNED;
815 data->low_index = data->index = object_index++;
816 dyn_array_ptr_push (&scan_stack, data);
817 dyn_array_ptr_push (&loop_stack, data);
820 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
825 g_assert (data->state == SCANNED);
826 data->state = FINISHED_ON_STACK;
829 printf ("-finishing %s (%p) index %d low-index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->low_index, data->color);
832 /* Compute low index */
836 printf ("-finished %s (%p) index %d low-index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->low_index, data->color);
839 if (data->index == data->low_index)
846 register_finalized_object (GCObject *obj)
848 g_assert (sgen_need_bridge_processing ());
849 dyn_array_ptr_push (®istered_bridges, obj);
855 dyn_array_ptr_set_size (®istered_bridges, 0);
861 dyn_array_ptr_set_size (&scan_stack, 0);
862 dyn_array_ptr_set_size (&loop_stack, 0);
863 dyn_array_ptr_set_size (®istered_bridges, 0);
864 free_object_buckets ();
865 free_color_buckets ();
868 num_colors_with_bridges = 0;
873 dump_color_table (const char *why, gboolean do_index)
877 printf ("colors%s:\n", why);
879 for (cur = root_color_bucket; cur; cur = cur->next, ++i) {
881 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
883 printf ("\t%d(%d):", i, cd->api_index);
885 printf ("\t%d: ", i);
886 for (j = 0; j < dyn_array_ptr_size (&cd->other_colors); ++j) {
887 printf ("%p ", dyn_array_ptr_get (&cd->other_colors, j));
889 if (dyn_array_ptr_size (&cd->bridges)) {
890 printf (" bridges: ");
891 for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) {
892 GCObject *obj = dyn_array_ptr_get (&cd->bridges, j);
893 ScanData *data = find_or_create_data (obj);
894 printf ("%d ", data->index);
905 step_timer (gint64 *timer)
907 gint64 curtime, diff;
909 SGEN_TV_GETTIME (curtime);
910 diff = SGEN_TV_ELAPSED (*timer, curtime);
915 processing_stw_step (void)
921 if (!dyn_array_ptr_size (®istered_bridges))
924 #if defined (DUMP_GRAPH)
925 printf ("-----------------\n");
928 * bridge_processing_in_progress must be set with the world
929 * stopped. If not there would be race conditions.
931 bridge_processing_in_progress = TRUE;
933 SGEN_TV_GETTIME (curtime);
935 object_alloc_init ();
938 bridge_count = dyn_array_ptr_size (®istered_bridges);
939 for (i = 0; i < bridge_count ; ++i)
940 register_bridge_object ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
942 setup_time = step_timer (&curtime);
944 for (i = 0; i < bridge_count; ++i) {
945 ScanData *sd = find_data ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
946 if (sd->state == INITIAL) {
947 dyn_array_ptr_push (&scan_stack, sd);
950 g_assert (sd->state == FINISHED_OFF_STACK);
954 tarjan_time = step_timer (&curtime);
956 #if defined (DUMP_GRAPH)
957 printf ("----summary----\n");
958 printf ("bridges:\n");
959 for (i = 0; i < bridge_count; ++i) {
960 ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i));
961 printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color);
964 dump_color_table (" after tarjan", FALSE);
967 clear_after_processing ();
972 gather_xrefs (ColorData *color)
975 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
976 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
980 if (dyn_array_ptr_size (&src->bridges))
981 dyn_array_ptr_add (&color_merge_array, src);
988 reset_xrefs (ColorData *color)
991 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
992 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
995 src->visited = FALSE;
996 if (!dyn_array_ptr_size (&src->bridges))
1002 processing_build_callback_data (int generation)
1005 MonoGCBridgeSCC **api_sccs;
1006 MonoGCBridgeXRef *api_xrefs;
1010 g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0);
1011 g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs);
1013 if (!dyn_array_ptr_size (®istered_bridges))
1016 SGEN_TV_GETTIME (curtime);
1018 /*create API objects */
1020 #if defined (DUMP_GRAPH)
1021 printf ("***** API *****\n");
1022 printf ("number of SCCs %d\n", num_colors_with_bridges);
1025 /* This is a straightforward translation from colors to the bridge callback format. */
1026 api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_colors_with_bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1027 api_index = xref_count = 0;
1029 for (cur = root_color_bucket; cur; cur = cur->next) {
1031 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1032 int bridges = dyn_array_ptr_size (&cd->bridges);
1036 api_sccs [api_index] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1037 api_sccs [api_index]->is_alive = FALSE;
1038 api_sccs [api_index]->num_objs = bridges;
1040 cd->api_index = api_index;
1042 for (j = 0; j < bridges; ++j)
1043 api_sccs [api_index]->objs [j] = (MonoObject *)dyn_array_ptr_get (&cd->bridges, j);
1048 scc_setup_time = step_timer (&curtime);
1050 for (cur = root_color_bucket; cur; cur = cur->next) {
1052 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1053 int bridges = dyn_array_ptr_size (&cd->bridges);
1057 dyn_array_ptr_set_size (&color_merge_array, 0);
1060 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
1061 xref_count += dyn_array_ptr_size (&cd->other_colors);
1065 gather_xref_time = step_timer (&curtime);
1067 #if defined (DUMP_GRAPH)
1068 printf ("TOTAL XREFS %d\n", xref_count);
1069 dump_color_table (" after xref pass", TRUE);
1072 api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1074 for (cur = root_color_bucket; cur; cur = cur->next) {
1076 for (src = &cur->data [0]; src < cur->next_data; ++src) {
1077 int bridges = dyn_array_ptr_size (&src->bridges);
1081 for (j = 0; j < dyn_array_ptr_size (&src->other_colors); ++j) {
1082 ColorData *dest = (ColorData *)dyn_array_ptr_get (&src->other_colors, j);
1083 g_assert (dyn_array_ptr_size (&dest->bridges)); /* We flattened the color graph, so this must never happen. */
1085 api_xrefs [api_index].src_scc_index = src->api_index;
1086 api_xrefs [api_index].dst_scc_index = dest->api_index;
1092 g_assert (xref_count == api_index);
1093 xref_setup_time = step_timer (&curtime);
1095 #if defined (DUMP_GRAPH)
1096 printf ("---xrefs:\n");
1097 for (i = 0; i < xref_count; ++i)
1098 printf ("\t%d -> %d\n", api_xrefs [i].src_scc_index, api_xrefs [i].dst_scc_index);
1101 //FIXME move half of the cleanup to before the bridge callback?
1102 bridge_processor->num_sccs = num_colors_with_bridges;
1103 bridge_processor->api_sccs = api_sccs;
1104 bridge_processor->num_xrefs = xref_count;
1105 bridge_processor->api_xrefs = api_xrefs;
1109 processing_after_callback (int generation)
1112 int bridge_count = dyn_array_ptr_size (®istered_bridges);
1113 int object_count = object_data_count;
1114 int color_count = color_data_count;
1115 int scc_count = num_colors_with_bridges;
1117 SGEN_TV_GETTIME (curtime);
1122 cleanup_time = step_timer (&curtime);
1124 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_TAR_BRIDGE bridges %d objects %d colors %d ignored %d sccs %d xref %d cache %d/%d setup %.2fms tarjan %.2fms scc-setup %.2fms gather-xref %.2fms xref-setup %.2fms cleanup %.2fms",
1125 bridge_count, object_count, color_count,
1126 ignored_objects, scc_count, xref_count,
1127 cache_hits, cache_misses,
1128 setup_time / 10000.0f,
1129 tarjan_time / 10000.0f,
1130 scc_setup_time / 10000.0f,
1131 gather_xref_time / 10000.0f,
1132 xref_setup_time / 10000.0f,
1133 cleanup_time / 10000.0f);
1135 cache_hits = cache_misses = 0;
1136 ignored_objects = 0;
1137 bridge_processing_in_progress = FALSE;
1141 describe_pointer (GCObject *obj)
1143 // HashEntry *entry;
1146 for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) {
1147 if (obj == dyn_array_ptr_get (®istered_bridges, i)) {
1148 printf ("Pointer is a registered bridge object.\n");
1153 // entry = sgen_hash_table_lookup (&hash_table, obj);
1157 // printf ("Bridge hash table entry %p:\n", entry);
1158 // printf (" is bridge: %d\n", (int)entry->is_bridge);
1159 // printf (" is visited: %d\n", (int)entry->is_visited);
1163 sgen_tarjan_bridge_init (SgenBridgeProcessor *collector)
1165 collector->reset_data = reset_data;
1166 collector->processing_stw_step = processing_stw_step;
1167 collector->processing_build_callback_data = processing_build_callback_data;
1168 collector->processing_after_callback = processing_after_callback;
1169 collector->class_kind = class_kind;
1170 collector->register_finalized_object = register_finalized_object;
1171 collector->describe_pointer = describe_pointer;
1172 collector->enable_accounting = enable_accounting;
1173 // collector->set_dump_prefix = set_dump_prefix;
1175 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET, BUCKET_SIZE);
1176 g_assert (sizeof (ObjectBucket) <= BUCKET_SIZE);
1177 g_assert (sizeof (ColorBucket) <= BUCKET_SIZE);
1178 bridge_processor = collector;