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 "tabledefs.h"
23 #include "utils/mono-logger-internals.h"
25 #include "sgen-dynarray.h"
28 * See comments in sgen-bridge.h
30 * This bridge implementation is based on the tarjan algorithm for strongly
31 * connected components. It has two elements:
33 * - Standard tarjan SCC algorithm to convert graph to SCC forest
35 * - "Colors": We reduce the SCC forest to bridged-SCCs-only by using a
36 * "color" algorithm devised by Kumpera. Consider the set of bridged SCCs
37 * which is reachable from a given object. We call each such unique set a
38 * "color". We compute the set of colors and which colors contain links to
39 * which colors. The color graph then becomes the reduced SCC graph.
43 enable_accounting (void)
45 // bridge_accounting_enabled = TRUE;
46 // 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);
49 // Is this class bridged or not, and should its dependencies be scanned or not?
50 // The result of this callback will be cached for use by is_opaque_object later.
51 static MonoGCBridgeObjectKind
52 class_kind (MonoClass *klass)
54 MonoGCBridgeObjectKind res = bridge_callbacks.bridge_class_kind (klass);
56 /* If it's a bridge, nothing we can do about it. */
57 if (res == GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS || res == GC_BRIDGE_OPAQUE_BRIDGE_CLASS)
60 /* Non bridge classes with no pointers will never point to a bridge, so we can savely ignore them. */
61 if (!klass->has_references) {
62 SGEN_LOG (6, "class %s is opaque\n", klass->name);
63 return GC_BRIDGE_OPAQUE_CLASS;
66 /* Some arrays can be ignored */
67 if (klass->rank == 1) {
68 MonoClass *elem_class = klass->element_class;
70 /* FIXME the bridge check can be quite expensive, cache it at the class level. */
71 /* An array of a sealed type that is not a bridge will never get to a bridge */
72 if ((elem_class->flags & TYPE_ATTRIBUTE_SEALED) && !elem_class->has_references && !bridge_callbacks.bridge_class_kind (elem_class)) {
73 SGEN_LOG (6, "class %s is opaque\n", klass->name);
74 return GC_BRIDGE_OPAQUE_CLASS;
78 return GC_BRIDGE_TRANSPARENT_CLASS;
81 //enable usage logging
82 // #define DUMP_GRAPH 1
94 We can split this data structure in two, those with bridges and those without
97 // Colors (ColorDatas) linked to by objects with this color
98 DynPtrArray other_colors;
99 // Bridge objects (GCObjects) held by objects with this color
102 unsigned visited : 1;
105 // Represents one managed object. Equivalent of new/old bridge "HashEntry"
106 typedef struct _ScanData {
107 // FIXME this can be eliminated; if we have a ScanData we generally looked it up from its GCObject
109 // We use the sgen lock_word in GCObject to store a pointer to the ScanData. Cache the original here to restore later:
113 // Tarjan algorithm index (order visited)
115 // Tarjan index of lowest-index object known reachable from here
118 // See "ScanData state" enum above
120 unsigned is_bridge : 1;
121 // Similar to lock_word, we use these bits in the GCObject as scratch space then restore them when done
122 unsigned obj_state : 2;
127 // Stacks of ScanData objects used for tarjan algorithm.
128 // The Tarjan algorithm is normally defined recursively; here scan_stack simulates the call stack of a recursive algorithm,
129 // and loop_stack is the stack structure used by the algorithm itself.
130 static DynPtrArray scan_stack, loop_stack;
132 // GCObjects on which register_finalized_object has been called
133 static DynPtrArray registered_bridges;
136 static DynPtrArray color_merge_array;
138 static int ignored_objects;
139 static int object_index;
140 static int num_colors_with_bridges;
141 static int xref_count;
143 static size_t setup_time, tarjan_time, scc_setup_time, gather_xref_time, xref_setup_time, cleanup_time;
144 static SgenBridgeProcessor *bridge_processor;
146 #define BUCKET_SIZE 8184
149 #define NUM_SCAN_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ScanData))
151 typedef struct _ObjectBucket ObjectBucket;
152 struct _ObjectBucket {
155 ScanData data [NUM_SCAN_ENTRIES];
158 static ObjectBucket *root_object_bucket;
159 static ObjectBucket *cur_object_bucket;
160 static int object_data_count;
163 new_object_bucket (void)
165 ObjectBucket *res = (ObjectBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
166 res->next_data = &res->data [0];
171 object_alloc_init (void)
173 root_object_bucket = cur_object_bucket = new_object_bucket ();
177 alloc_object_data (void)
182 /* next_data points to the first free entry */
183 res = cur_object_bucket->next_data;
184 if (res >= &cur_object_bucket->data [NUM_SCAN_ENTRIES]) {
185 ObjectBucket *b = new_object_bucket ();
186 cur_object_bucket->next = b;
187 cur_object_bucket = b;
190 cur_object_bucket->next_data = res + 1;
196 free_object_buckets (void)
198 ObjectBucket *cur = root_object_bucket;
200 object_data_count = 0;
203 ObjectBucket *tmp = cur->next;
204 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
208 root_object_bucket = cur_object_bucket = NULL;
212 #define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData))
214 // Same as ObjectBucket except NUM_COLOR_ENTRIES and NUM_SCAN_ENTRIES differ
215 typedef struct _ColorBucket ColorBucket;
216 struct _ColorBucket {
218 ColorData *next_data;
219 ColorData data [NUM_COLOR_ENTRIES];
222 static ColorBucket *root_color_bucket;
223 static ColorBucket *cur_color_bucket;
224 static int color_data_count;
228 new_color_bucket (void)
230 ColorBucket *res = (ColorBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
231 res->next_data = &res->data [0];
236 color_alloc_init (void)
238 root_color_bucket = cur_color_bucket = new_color_bucket ();
242 alloc_color_data (void)
247 /* next_data points to the first free entry */
248 res = cur_color_bucket->next_data;
249 if (res >= &cur_color_bucket->data [NUM_COLOR_ENTRIES]) {
250 ColorBucket *b = new_color_bucket ();
251 cur_color_bucket->next = b;
252 cur_color_bucket = b;
255 cur_color_bucket->next_data = res + 1;
261 free_color_buckets (void)
263 ColorBucket *cur, *tmp;
265 color_data_count = 0;
267 for (cur = root_color_bucket; cur; cur = tmp) {
269 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
270 dyn_array_ptr_uninit (&cd->other_colors);
271 dyn_array_ptr_uninit (&cd->bridges);
274 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
276 root_color_bucket = cur_color_bucket = NULL;
281 create_data (GCObject *obj)
283 mword *o = (mword*)obj;
284 ScanData *res = alloc_object_data ();
287 res->index = res->low_index = -1;
288 res->state = INITIAL;
289 res->is_bridge = FALSE;
290 res->obj_state = o [0] & SGEN_VTABLE_BITS_MASK;
291 res->lock_word = o [1];
293 o [0] |= SGEN_VTABLE_BITS_MASK;
299 find_data (GCObject *obj)
302 mword *o = (mword*)obj;
303 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
304 a = (ScanData*)o [1];
309 clear_after_processing (void)
313 for (cur = root_object_bucket; cur; cur = cur->next) {
315 for (sd = &cur->data [0]; sd < cur->next_data; ++sd) {
316 mword *o = (mword*)sd->obj;
317 o [0] &= ~SGEN_VTABLE_BITS_MASK;
318 o [0] |= sd->obj_state;
319 o [1] = sd->lock_word;
325 bridge_object_forward (GCObject *obj)
328 mword *o = (mword*)obj;
329 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
332 fwd = SGEN_OBJECT_IS_FORWARDED (obj);
333 return fwd ? fwd : obj;
338 safe_name_bridge (GCObject *obj)
340 GCVTable vt = SGEN_LOAD_VTABLE (obj);
341 return vt->klass->name;
345 find_or_create_data (GCObject *obj)
347 ScanData *entry = find_data (obj);
349 entry = create_data (obj);
361 We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
363 The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline,
364 making the extra space pretty much free.
366 The cost between 32 and 128 itens is minimal too, it's mostly a fixed, setup cost.
368 Memory wise, 4/32 takes 512 and 8/128 takes 8k, so it's quite reasonable.
371 #define ELEMENTS_PER_BUCKET 8
372 #define COLOR_CACHE_SIZE 128
373 static HashEntry merge_cache [COLOR_CACHE_SIZE][ELEMENTS_PER_BUCKET];
376 mix_hash (size_t hash)
378 return (int)(((hash * 215497) >> 16) ^ ((hash * 1823231) + hash));
384 memset (merge_cache, 0, sizeof (merge_cache));
389 dyn_array_ptr_contains (DynPtrArray *da, void *x)
392 for (i = 0; i < dyn_array_ptr_size (da); ++i)
393 if (dyn_array_ptr_get (da, i) == x)
399 match_colors (DynPtrArray *a, DynPtrArray *b)
402 if (dyn_array_ptr_size (a) != dyn_array_ptr_size (b))
405 for (i = 0; i < dyn_array_ptr_size (a); ++i) {
406 if (!dyn_array_ptr_contains (b, dyn_array_ptr_get (a, i)))
412 static int cache_hits, cache_misses;
415 find_in_cache (int *insert_index)
418 int i, hash, size, index;
420 size = dyn_array_ptr_size (&color_merge_array);
421 /* Cache checking is very ineficient with a lot of elements*/
426 for (i = 0 ; i < size; ++i)
427 hash += mix_hash ((size_t)dyn_array_ptr_get (&color_merge_array, i));
431 index = hash & (COLOR_CACHE_SIZE - 1);
432 bucket = merge_cache [index];
433 for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) {
434 if (bucket [i].hash != hash)
436 if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) {
438 return bucket [i].color;
442 //move elements to the back
443 for (i = ELEMENTS_PER_BUCKET - 1; i > 0; --i)
444 bucket [i] = bucket [i - 1];
446 *insert_index = index;
447 bucket [0].hash = hash;
452 new_color (gboolean force_new)
456 /* XXX Try to find an equal one and return it */
458 cd = find_in_cache (&i);
463 cd = alloc_color_data ();
465 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
466 /* if i >= 0, it means we prepared a given slot to receive the new color */
468 merge_cache [i][0].color = cd;
475 register_bridge_object (GCObject *obj)
477 create_data (obj)->is_bridge = TRUE;
481 is_opaque_object (GCObject *obj)
483 MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
484 if ((vt->gc_bits & SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) {
485 SGEN_LOG (6, "ignoring %s\n", vt->klass->name);
492 // Called during DFS; visits one child. If it is a candidate to be scanned, pushes it to the stacks.
494 push_object (GCObject *obj)
497 obj = bridge_object_forward (obj);
500 printf ("\t= pushing %p %s -> ", obj, safe_name_bridge (obj));
503 /* Object types we can ignore */
504 if (is_opaque_object (obj)) {
511 data = find_data (obj);
513 /* Already marked - XXX must be done this way as the bridge themselves are alive. */
514 if (data && data->state != INITIAL) {
516 printf ("already marked\n");
521 /* We only care about dead objects */
522 if (!data && sgen_object_is_live (obj)) {
530 printf ("pushed!\n");
534 data = create_data (obj);
535 g_assert (data->state == INITIAL);
536 g_assert (data->index == -1);
537 dyn_array_ptr_push (&scan_stack, data);
541 #define HANDLE_PTR(ptr,obj) do { \
542 GCObject *dst = (GCObject*)*(ptr); \
543 if (dst) push_object (dst); \
546 // dfs () function's queue-children-of-object operation.
548 push_all (ScanData *data)
550 GCObject *obj = data->obj;
551 char *start = (char*)obj;
552 mword desc = sgen_obj_get_descriptor_safe (obj);
555 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
558 #include "sgen/sgen-scan-object.h"
563 compute_low_index (ScanData *data, GCObject *obj)
568 obj = bridge_object_forward (obj);
569 other = find_data (obj);
572 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);
577 g_assert (other->state != INITIAL);
579 if ((other->state == SCANNED || other->state == FINISHED_ON_STACK) && data->low_index > other->low_index)
580 data->low_index = other->low_index;
582 /* Compute the low color */
583 if (other->color == NULL)
588 dyn_array_ptr_add (&color_merge_array, other->color);
594 #define HANDLE_PTR(ptr,obj) do { \
595 GCObject *dst = (GCObject*)*(ptr); \
596 if (dst) compute_low_index (data, dst); \
600 compute_low (ScanData *data)
602 GCObject *obj = data->obj;
603 char *start = (char*)obj;
604 mword desc = sgen_obj_get_descriptor_safe (obj);
606 #include "sgen/sgen-scan-object.h"
612 ColorData *color = NULL;
613 int size = dyn_array_ptr_size (&color_merge_array);
617 else if (size == 1) {
618 color = (ColorData *)dyn_array_ptr_get (&color_merge_array, 0);
620 color = new_color (FALSE);
626 create_scc (ScanData *data)
629 gboolean found = FALSE;
630 gboolean found_bridge = FALSE;
631 ColorData *color_data = NULL;
633 for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) {
634 ScanData *other = (ScanData *)dyn_array_ptr_get (&loop_stack, i);
635 found_bridge |= other->is_bridge;
636 if (found_bridge || other == data)
641 printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data->obj), data->obj, found_bridge);
642 printf ("\tpoints-to-colors: ");
643 for (int i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i)
644 printf ("%p ", dyn_array_ptr_get (&color_merge_array, i));
647 printf ("loop stack: ");
648 for (int i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) {
649 ScanData *other = dyn_array_ptr_get (&loop_stack, i);
650 printf ("(%d/%d)", other->index, other->low_index);
656 color_data = new_color (TRUE);
657 ++num_colors_with_bridges;
659 color_data = reduce_color ();
662 while (dyn_array_ptr_size (&loop_stack) > 0) {
663 ScanData *other = (ScanData *)dyn_array_ptr_pop (&loop_stack);
666 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);
669 other->color = color_data;
670 switch (other->state) {
671 case FINISHED_ON_STACK:
672 other->state = FINISHED_OFF_STACK;
674 case FINISHED_OFF_STACK:
677 g_error ("Invalid state when building SCC %d", other->state);
680 if (other->is_bridge)
681 dyn_array_ptr_add (&color_data->bridges, other->obj);
690 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) {
691 ColorData *cd = (ColorData *)dyn_array_ptr_get (&color_merge_array, i);
692 g_assert (cd->visited);
695 dyn_array_ptr_empty (&color_merge_array);
696 found_bridge = FALSE;
702 g_assert (dyn_array_ptr_size (&scan_stack) == 1);
703 g_assert (dyn_array_ptr_size (&loop_stack) == 0);
705 dyn_array_ptr_empty (&color_merge_array);
707 while (dyn_array_ptr_size (&scan_stack) > 0) {
708 ScanData *data = (ScanData *)dyn_array_ptr_pop (&scan_stack);
711 * Ignore finished objects on stack, they happen due to loops. For example:
717 * We start scanning from A and push C before B. So, after the first iteration, the scan stack will have: A C B.
718 * We then visit B, which will find C in its initial state and push again.
719 * Finally after finish with C and B, the stack will be left with "A C" and at this point C should be ignored.
721 * The above explains FINISHED_ON_STACK, to explain FINISHED_OFF_STACK, consider if the root was D, which pointed
722 * to A and C. A is processed first, leaving C on stack after that in the mentioned state.
724 if (data->state == FINISHED_ON_STACK || data->state == FINISHED_OFF_STACK)
727 if (data->state == INITIAL) {
728 g_assert (data->index == -1);
729 g_assert (data->low_index == -1);
731 data->state = SCANNED;
732 data->low_index = data->index = object_index++;
733 dyn_array_ptr_push (&scan_stack, data);
734 dyn_array_ptr_push (&loop_stack, data);
737 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
742 g_assert (data->state == SCANNED);
743 data->state = FINISHED_ON_STACK;
746 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);
749 /* Compute low index */
753 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);
756 if (data->index == data->low_index)
763 register_finalized_object (GCObject *obj)
765 g_assert (sgen_need_bridge_processing ());
766 dyn_array_ptr_push (®istered_bridges, obj);
772 dyn_array_ptr_empty (®istered_bridges);
778 dyn_array_ptr_empty (&scan_stack);
779 dyn_array_ptr_empty (&loop_stack);
780 dyn_array_ptr_empty (®istered_bridges);
781 free_object_buckets ();
782 free_color_buckets ();
785 num_colors_with_bridges = 0;
790 dump_color_table (const char *why, gboolean do_index)
794 printf ("colors%s:\n", why);
796 for (cur = root_color_bucket; cur; cur = cur->next, ++i) {
798 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
800 printf ("\t%d(%d):", i, cd->api_index);
802 printf ("\t%d: ", i);
803 for (j = 0; j < dyn_array_ptr_size (&cd->other_colors); ++j) {
804 printf ("%p ", dyn_array_ptr_get (&cd->other_colors, j));
806 if (dyn_array_ptr_size (&cd->bridges)) {
807 printf (" bridges: ");
808 for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) {
809 GCObject *obj = dyn_array_ptr_get (&cd->bridges, j);
810 ScanData *data = find_or_create_data (obj);
811 printf ("%d ", data->index);
822 step_timer (gint64 *timer)
824 gint64 curtime, diff;
826 SGEN_TV_GETTIME (curtime);
827 diff = SGEN_TV_ELAPSED (*timer, curtime);
832 processing_stw_step (void)
838 if (!dyn_array_ptr_size (®istered_bridges))
841 #if defined (DUMP_GRAPH)
842 printf ("-----------------\n");
845 SGEN_TV_GETTIME (curtime);
847 object_alloc_init ();
850 bridge_count = dyn_array_ptr_size (®istered_bridges);
851 for (i = 0; i < bridge_count ; ++i)
852 register_bridge_object ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
854 setup_time = step_timer (&curtime);
856 for (i = 0; i < bridge_count; ++i) {
857 ScanData *sd = find_data ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
858 if (sd->state == INITIAL) {
859 dyn_array_ptr_push (&scan_stack, sd);
862 g_assert (sd->state == FINISHED_OFF_STACK);
866 tarjan_time = step_timer (&curtime);
868 #if defined (DUMP_GRAPH)
869 printf ("----summary----\n");
870 printf ("bridges:\n");
871 for (int i = 0; i < bridge_count; ++i) {
872 ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i));
873 printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color);
876 dump_color_table (" after tarjan", FALSE);
879 clear_after_processing ();
884 gather_xrefs (ColorData *color)
887 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
888 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
892 if (dyn_array_ptr_size (&src->bridges))
893 dyn_array_ptr_add (&color_merge_array, src);
900 reset_xrefs (ColorData *color)
903 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
904 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
907 src->visited = FALSE;
908 if (!dyn_array_ptr_size (&src->bridges))
914 processing_build_callback_data (int generation)
917 MonoGCBridgeSCC **api_sccs;
918 MonoGCBridgeXRef *api_xrefs;
922 g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0);
923 g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs);
925 if (!dyn_array_ptr_size (®istered_bridges))
928 SGEN_TV_GETTIME (curtime);
930 /*create API objects */
932 #if defined (DUMP_GRAPH)
933 printf ("***** API *****\n");
934 printf ("number of SCCs %d\n", num_colors_with_bridges);
937 /* This is a straightforward translation from colors to the bridge callback format. */
938 api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_colors_with_bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
939 api_index = xref_count = 0;
941 for (cur = root_color_bucket; cur; cur = cur->next) {
943 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
944 int bridges = dyn_array_ptr_size (&cd->bridges);
948 api_sccs [api_index] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
949 api_sccs [api_index]->is_alive = FALSE;
950 api_sccs [api_index]->num_objs = bridges;
952 cd->api_index = api_index;
954 for (j = 0; j < bridges; ++j)
955 api_sccs [api_index]->objs [j] = (MonoObject *)dyn_array_ptr_get (&cd->bridges, j);
960 scc_setup_time = step_timer (&curtime);
962 for (cur = root_color_bucket; cur; cur = cur->next) {
964 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
965 int bridges = dyn_array_ptr_size (&cd->bridges);
969 dyn_array_ptr_empty (&color_merge_array);
972 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
973 xref_count += dyn_array_ptr_size (&cd->other_colors);
977 gather_xref_time = step_timer (&curtime);
979 #if defined (DUMP_GRAPH)
980 printf ("TOTAL XREFS %d\n", xref_count);
981 dump_color_table (" after xref pass", TRUE);
984 api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE);
986 for (cur = root_color_bucket; cur; cur = cur->next) {
988 for (src = &cur->data [0]; src < cur->next_data; ++src) {
989 int bridges = dyn_array_ptr_size (&src->bridges);
993 for (j = 0; j < dyn_array_ptr_size (&src->other_colors); ++j) {
994 ColorData *dest = (ColorData *)dyn_array_ptr_get (&src->other_colors, j);
995 g_assert (dyn_array_ptr_size (&dest->bridges)); /* We flattened the color graph, so this must never happen. */
997 api_xrefs [api_index].src_scc_index = src->api_index;
998 api_xrefs [api_index].dst_scc_index = dest->api_index;
1004 g_assert (xref_count == api_index);
1005 xref_setup_time = step_timer (&curtime);
1007 #if defined (DUMP_GRAPH)
1008 printf ("---xrefs:\n");
1009 for (int i = 0; i < xref_count; ++i)
1010 printf ("\t%d -> %d\n", api_xrefs [i].src_scc_index, api_xrefs [i].dst_scc_index);
1013 //FIXME move half of the cleanup to before the bridge callback?
1014 bridge_processor->num_sccs = num_colors_with_bridges;
1015 bridge_processor->api_sccs = api_sccs;
1016 bridge_processor->num_xrefs = xref_count;
1017 bridge_processor->api_xrefs = api_xrefs;
1021 processing_after_callback (int generation)
1024 int bridge_count = dyn_array_ptr_size (®istered_bridges);
1025 int object_count = object_data_count;
1026 int color_count = color_data_count;
1027 int scc_count = num_colors_with_bridges;
1029 SGEN_TV_GETTIME (curtime);
1034 cleanup_time = step_timer (&curtime);
1036 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",
1037 bridge_count, object_count, color_count,
1038 ignored_objects, scc_count, xref_count,
1039 cache_hits, cache_misses,
1040 setup_time / 10000.0f,
1041 tarjan_time / 10000.0f,
1042 scc_setup_time / 10000.0f,
1043 gather_xref_time / 10000.0f,
1044 xref_setup_time / 10000.0f,
1045 cleanup_time / 10000.0f);
1047 cache_hits = cache_misses = 0;
1048 ignored_objects = 0;
1052 describe_pointer (GCObject *obj)
1054 // HashEntry *entry;
1057 for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) {
1058 if (obj == dyn_array_ptr_get (®istered_bridges, i)) {
1059 printf ("Pointer is a registered bridge object.\n");
1064 // entry = sgen_hash_table_lookup (&hash_table, obj);
1068 // printf ("Bridge hash table entry %p:\n", entry);
1069 // printf (" is bridge: %d\n", (int)entry->is_bridge);
1070 // printf (" is visited: %d\n", (int)entry->is_visited);
1074 sgen_tarjan_bridge_init (SgenBridgeProcessor *collector)
1076 collector->reset_data = reset_data;
1077 collector->processing_stw_step = processing_stw_step;
1078 collector->processing_build_callback_data = processing_build_callback_data;
1079 collector->processing_after_callback = processing_after_callback;
1080 collector->class_kind = class_kind;
1081 collector->register_finalized_object = register_finalized_object;
1082 collector->describe_pointer = describe_pointer;
1083 collector->enable_accounting = enable_accounting;
1084 // collector->set_dump_prefix = set_dump_prefix;
1086 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET, BUCKET_SIZE);
1087 g_assert (sizeof (ObjectBucket) <= BUCKET_SIZE);
1088 g_assert (sizeof (ColorBucket) <= BUCKET_SIZE);
1089 bridge_processor = collector;