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"
27 #include "sgen-dynarray.h"
30 * See comments in sgen-bridge.h
32 * This bridge implementation is based on the tarjan algorithm for strongly
33 * connected components. It has two elements:
35 * - Standard tarjan SCC algorithm to convert graph to SCC forest
37 * - "Colors": We reduce the SCC forest to bridged-SCCs-only by using a
38 * "color" algorithm devised by Kumpera. Consider the set of bridged SCCs
39 * which is reachable from a given object. We call each such unique set a
40 * "color". We compute the set of colors and which colors contain links to
41 * which colors. The color graph then becomes the reduced SCC graph.
45 enable_accounting (void)
47 // bridge_accounting_enabled = TRUE;
48 // 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);
51 // Is this class bridged or not, and should its dependencies be scanned or not?
52 // The result of this callback will be cached for use by is_opaque_object later.
53 static MonoGCBridgeObjectKind
54 class_kind (MonoClass *klass)
56 MonoGCBridgeObjectKind res = bridge_callbacks.bridge_class_kind (klass);
58 /* If it's a bridge, nothing we can do about it. */
59 if (res == GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS || res == GC_BRIDGE_OPAQUE_BRIDGE_CLASS)
62 /* Non bridge classes with no pointers will never point to a bridge, so we can savely ignore them. */
63 if (!klass->has_references) {
64 SGEN_LOG (6, "class %s is opaque\n", klass->name);
65 return GC_BRIDGE_OPAQUE_CLASS;
68 /* Some arrays can be ignored */
69 if (klass->rank == 1) {
70 MonoClass *elem_class = klass->element_class;
72 /* FIXME the bridge check can be quite expensive, cache it at the class level. */
73 /* An array of a sealed type that is not a bridge will never get to a bridge */
74 if ((elem_class->flags & TYPE_ATTRIBUTE_SEALED) && !elem_class->has_references && !bridge_callbacks.bridge_class_kind (elem_class)) {
75 SGEN_LOG (6, "class %s is opaque\n", klass->name);
76 return GC_BRIDGE_OPAQUE_CLASS;
80 return GC_BRIDGE_TRANSPARENT_CLASS;
83 //enable usage logging
84 // #define DUMP_GRAPH 1
96 We can split this data structure in two, those with bridges and those without
99 // Colors (ColorDatas) linked to by objects with this color
100 DynPtrArray other_colors;
101 // Bridge objects (GCObjects) held by objects with this color
104 unsigned visited : 1;
107 // Represents one managed object. Equivalent of new/old bridge "HashEntry"
108 typedef struct _ScanData {
109 // FIXME this can be eliminated; if we have a ScanData we generally looked it up from its GCObject
111 // We use the sgen lock_word in GCObject to store a pointer to the ScanData. Cache the original here to restore later:
115 // Tarjan algorithm index (order visited)
117 // Tarjan index of lowest-index object known reachable from here
120 // See "ScanData state" enum above
122 unsigned is_bridge : 1;
123 // Similar to lock_word, we use these bits in the GCObject as scratch space then restore them when done
124 unsigned obj_state : 2;
129 // Stacks of ScanData objects used for tarjan algorithm.
130 // The Tarjan algorithm is normally defined recursively; here scan_stack simulates the call stack of a recursive algorithm,
131 // and loop_stack is the stack structure used by the algorithm itself.
132 static DynPtrArray scan_stack, loop_stack;
134 // GCObjects on which register_finalized_object has been called
135 static DynPtrArray registered_bridges;
138 static DynPtrArray color_merge_array;
140 static int ignored_objects;
141 static int object_index;
142 static int num_colors_with_bridges;
143 static int xref_count;
145 static size_t setup_time, tarjan_time, scc_setup_time, gather_xref_time, xref_setup_time, cleanup_time;
146 static SgenBridgeProcessor *bridge_processor;
148 #define BUCKET_SIZE 8184
151 #define NUM_SCAN_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ScanData))
153 typedef struct _ObjectBucket ObjectBucket;
154 struct _ObjectBucket {
157 ScanData data [NUM_SCAN_ENTRIES];
160 static ObjectBucket *root_object_bucket;
161 static ObjectBucket *cur_object_bucket;
162 static int object_data_count;
165 new_object_bucket (void)
167 ObjectBucket *res = (ObjectBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
168 res->next_data = &res->data [0];
173 object_alloc_init (void)
175 root_object_bucket = cur_object_bucket = new_object_bucket ();
179 alloc_object_data (void)
184 /* next_data points to the first free entry */
185 res = cur_object_bucket->next_data;
186 if (res >= &cur_object_bucket->data [NUM_SCAN_ENTRIES]) {
187 ObjectBucket *b = new_object_bucket ();
188 cur_object_bucket->next = b;
189 cur_object_bucket = b;
192 cur_object_bucket->next_data = res + 1;
198 free_object_buckets (void)
200 ObjectBucket *cur = root_object_bucket;
202 object_data_count = 0;
205 ObjectBucket *tmp = cur->next;
206 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
210 root_object_bucket = cur_object_bucket = NULL;
214 #define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData))
216 // Same as ObjectBucket except NUM_COLOR_ENTRIES and NUM_SCAN_ENTRIES differ
217 typedef struct _ColorBucket ColorBucket;
218 struct _ColorBucket {
220 ColorData *next_data;
221 ColorData data [NUM_COLOR_ENTRIES];
224 static ColorBucket *root_color_bucket;
225 static ColorBucket *cur_color_bucket;
226 static int color_data_count;
230 new_color_bucket (void)
232 ColorBucket *res = (ColorBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
233 res->next_data = &res->data [0];
238 color_alloc_init (void)
240 root_color_bucket = cur_color_bucket = new_color_bucket ();
244 alloc_color_data (void)
249 /* next_data points to the first free entry */
250 res = cur_color_bucket->next_data;
251 if (res >= &cur_color_bucket->data [NUM_COLOR_ENTRIES]) {
252 ColorBucket *b = new_color_bucket ();
253 cur_color_bucket->next = b;
254 cur_color_bucket = b;
257 cur_color_bucket->next_data = res + 1;
263 free_color_buckets (void)
265 ColorBucket *cur, *tmp;
267 color_data_count = 0;
269 for (cur = root_color_bucket; cur; cur = tmp) {
271 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
272 dyn_array_ptr_uninit (&cd->other_colors);
273 dyn_array_ptr_uninit (&cd->bridges);
276 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
278 root_color_bucket = cur_color_bucket = NULL;
283 create_data (GCObject *obj)
285 mword *o = (mword*)obj;
286 ScanData *res = alloc_object_data ();
289 res->index = res->low_index = -1;
290 res->state = INITIAL;
291 res->is_bridge = FALSE;
292 res->obj_state = o [0] & SGEN_VTABLE_BITS_MASK;
293 res->lock_word = o [1];
295 o [0] |= SGEN_VTABLE_BITS_MASK;
301 find_data (GCObject *obj)
304 mword *o = (mword*)obj;
305 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
306 a = (ScanData*)o [1];
311 clear_after_processing (void)
315 for (cur = root_object_bucket; cur; cur = cur->next) {
317 for (sd = &cur->data [0]; sd < cur->next_data; ++sd) {
318 mword *o = (mword*)sd->obj;
319 o [0] &= ~SGEN_VTABLE_BITS_MASK;
320 o [0] |= sd->obj_state;
321 o [1] = sd->lock_word;
327 bridge_object_forward (GCObject *obj)
330 mword *o = (mword*)obj;
331 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
334 fwd = SGEN_OBJECT_IS_FORWARDED (obj);
335 return fwd ? fwd : obj;
340 safe_name_bridge (GCObject *obj)
342 GCVTable vt = SGEN_LOAD_VTABLE (obj);
343 return vt->klass->name;
347 find_or_create_data (GCObject *obj)
349 ScanData *entry = find_data (obj);
351 entry = create_data (obj);
363 We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
365 The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline,
366 making the extra space pretty much free.
368 The cost between 32 and 128 itens is minimal too, it's mostly a fixed, setup cost.
370 Memory wise, 4/32 takes 512 and 8/128 takes 8k, so it's quite reasonable.
373 #define ELEMENTS_PER_BUCKET 8
374 #define COLOR_CACHE_SIZE 128
375 static HashEntry merge_cache [COLOR_CACHE_SIZE][ELEMENTS_PER_BUCKET];
378 mix_hash (size_t hash)
380 return (int)(((hash * 215497) >> 16) ^ ((hash * 1823231) + hash));
386 memset (merge_cache, 0, sizeof (merge_cache));
391 dyn_array_ptr_contains (DynPtrArray *da, void *x)
394 for (i = 0; i < dyn_array_ptr_size (da); ++i)
395 if (dyn_array_ptr_get (da, i) == x)
401 match_colors (DynPtrArray *a, DynPtrArray *b)
404 if (dyn_array_ptr_size (a) != dyn_array_ptr_size (b))
407 for (i = 0; i < dyn_array_ptr_size (a); ++i) {
408 if (!dyn_array_ptr_contains (b, dyn_array_ptr_get (a, i)))
414 static int cache_hits, cache_misses;
417 find_in_cache (int *insert_index)
420 int i, hash, size, index;
422 size = dyn_array_ptr_size (&color_merge_array);
423 /* Cache checking is very ineficient with a lot of elements*/
428 for (i = 0 ; i < size; ++i)
429 hash += mix_hash ((size_t)dyn_array_ptr_get (&color_merge_array, i));
433 index = hash & (COLOR_CACHE_SIZE - 1);
434 bucket = merge_cache [index];
435 for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) {
436 if (bucket [i].hash != hash)
438 if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) {
440 return bucket [i].color;
444 //move elements to the back
445 for (i = ELEMENTS_PER_BUCKET - 1; i > 0; --i)
446 bucket [i] = bucket [i - 1];
448 *insert_index = index;
449 bucket [0].hash = hash;
454 new_color (gboolean force_new)
458 /* XXX Try to find an equal one and return it */
460 cd = find_in_cache (&i);
465 cd = alloc_color_data ();
467 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
468 /* if i >= 0, it means we prepared a given slot to receive the new color */
470 merge_cache [i][0].color = cd;
477 register_bridge_object (GCObject *obj)
479 create_data (obj)->is_bridge = TRUE;
483 is_opaque_object (GCObject *obj)
485 MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
486 if ((vt->gc_bits & SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) {
487 SGEN_LOG (6, "ignoring %s\n", vt->klass->name);
494 // Called during DFS; visits one child. If it is a candidate to be scanned, pushes it to the stacks.
496 push_object (GCObject *obj)
499 obj = bridge_object_forward (obj);
502 printf ("\t= pushing %p %s -> ", obj, safe_name_bridge (obj));
505 /* Object types we can ignore */
506 if (is_opaque_object (obj)) {
513 data = find_data (obj);
515 /* Already marked - XXX must be done this way as the bridge themselves are alive. */
516 if (data && data->state != INITIAL) {
518 printf ("already marked\n");
523 /* We only care about dead objects */
524 if (!data && sgen_object_is_live (obj)) {
532 printf ("pushed!\n");
536 data = create_data (obj);
537 g_assert (data->state == INITIAL);
538 g_assert (data->index == -1);
539 dyn_array_ptr_push (&scan_stack, data);
543 #define HANDLE_PTR(ptr,obj) do { \
544 GCObject *dst = (GCObject*)*(ptr); \
545 if (dst) push_object (dst); \
548 // dfs () function's queue-children-of-object operation.
550 push_all (ScanData *data)
552 GCObject *obj = data->obj;
553 char *start = (char*)obj;
554 mword desc = sgen_obj_get_descriptor_safe (obj);
557 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
560 #include "sgen/sgen-scan-object.h"
565 compute_low_index (ScanData *data, GCObject *obj)
570 obj = bridge_object_forward (obj);
571 other = find_data (obj);
574 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);
579 g_assert (other->state != INITIAL);
581 if ((other->state == SCANNED || other->state == FINISHED_ON_STACK) && data->low_index > other->low_index)
582 data->low_index = other->low_index;
584 /* Compute the low color */
585 if (other->color == NULL)
590 dyn_array_ptr_add (&color_merge_array, other->color);
596 #define HANDLE_PTR(ptr,obj) do { \
597 GCObject *dst = (GCObject*)*(ptr); \
598 if (dst) compute_low_index (data, dst); \
602 compute_low (ScanData *data)
604 GCObject *obj = data->obj;
605 char *start = (char*)obj;
606 mword desc = sgen_obj_get_descriptor_safe (obj);
608 #include "sgen/sgen-scan-object.h"
614 ColorData *color = NULL;
615 int size = dyn_array_ptr_size (&color_merge_array);
619 else if (size == 1) {
620 color = (ColorData *)dyn_array_ptr_get (&color_merge_array, 0);
622 color = new_color (FALSE);
628 create_scc (ScanData *data)
631 gboolean found = FALSE;
632 gboolean found_bridge = FALSE;
633 ColorData *color_data = NULL;
635 for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) {
636 ScanData *other = (ScanData *)dyn_array_ptr_get (&loop_stack, i);
637 found_bridge |= other->is_bridge;
638 if (found_bridge || other == data)
643 printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data->obj), data->obj, found_bridge);
644 printf ("\tpoints-to-colors: ");
645 for (int i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i)
646 printf ("%p ", dyn_array_ptr_get (&color_merge_array, i));
649 printf ("loop stack: ");
650 for (int i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) {
651 ScanData *other = dyn_array_ptr_get (&loop_stack, i);
652 printf ("(%d/%d)", other->index, other->low_index);
658 color_data = new_color (TRUE);
659 ++num_colors_with_bridges;
661 color_data = reduce_color ();
664 while (dyn_array_ptr_size (&loop_stack) > 0) {
665 ScanData *other = (ScanData *)dyn_array_ptr_pop (&loop_stack);
668 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);
671 other->color = color_data;
672 switch (other->state) {
673 case FINISHED_ON_STACK:
674 other->state = FINISHED_OFF_STACK;
676 case FINISHED_OFF_STACK:
679 g_error ("Invalid state when building SCC %d", other->state);
682 if (other->is_bridge)
683 dyn_array_ptr_add (&color_data->bridges, other->obj);
692 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) {
693 ColorData *cd = (ColorData *)dyn_array_ptr_get (&color_merge_array, i);
694 g_assert (cd->visited);
697 dyn_array_ptr_empty (&color_merge_array);
698 found_bridge = FALSE;
704 g_assert (dyn_array_ptr_size (&scan_stack) == 1);
705 g_assert (dyn_array_ptr_size (&loop_stack) == 0);
707 dyn_array_ptr_empty (&color_merge_array);
709 while (dyn_array_ptr_size (&scan_stack) > 0) {
710 ScanData *data = (ScanData *)dyn_array_ptr_pop (&scan_stack);
713 * Ignore finished objects on stack, they happen due to loops. For example:
719 * We start scanning from A and push C before B. So, after the first iteration, the scan stack will have: A C B.
720 * We then visit B, which will find C in its initial state and push again.
721 * Finally after finish with C and B, the stack will be left with "A C" and at this point C should be ignored.
723 * The above explains FINISHED_ON_STACK, to explain FINISHED_OFF_STACK, consider if the root was D, which pointed
724 * to A and C. A is processed first, leaving C on stack after that in the mentioned state.
726 if (data->state == FINISHED_ON_STACK || data->state == FINISHED_OFF_STACK)
729 if (data->state == INITIAL) {
730 g_assert (data->index == -1);
731 g_assert (data->low_index == -1);
733 data->state = SCANNED;
734 data->low_index = data->index = object_index++;
735 dyn_array_ptr_push (&scan_stack, data);
736 dyn_array_ptr_push (&loop_stack, data);
739 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
744 g_assert (data->state == SCANNED);
745 data->state = FINISHED_ON_STACK;
748 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);
751 /* Compute low index */
755 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);
758 if (data->index == data->low_index)
765 register_finalized_object (GCObject *obj)
767 g_assert (sgen_need_bridge_processing ());
768 dyn_array_ptr_push (®istered_bridges, obj);
774 dyn_array_ptr_empty (®istered_bridges);
780 dyn_array_ptr_empty (&scan_stack);
781 dyn_array_ptr_empty (&loop_stack);
782 dyn_array_ptr_empty (®istered_bridges);
783 free_object_buckets ();
784 free_color_buckets ();
787 num_colors_with_bridges = 0;
792 dump_color_table (const char *why, gboolean do_index)
796 printf ("colors%s:\n", why);
798 for (cur = root_color_bucket; cur; cur = cur->next, ++i) {
800 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
802 printf ("\t%d(%d):", i, cd->api_index);
804 printf ("\t%d: ", i);
805 for (j = 0; j < dyn_array_ptr_size (&cd->other_colors); ++j) {
806 printf ("%p ", dyn_array_ptr_get (&cd->other_colors, j));
808 if (dyn_array_ptr_size (&cd->bridges)) {
809 printf (" bridges: ");
810 for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) {
811 GCObject *obj = dyn_array_ptr_get (&cd->bridges, j);
812 ScanData *data = find_or_create_data (obj);
813 printf ("%d ", data->index);
824 step_timer (gint64 *timer)
826 gint64 curtime, diff;
828 SGEN_TV_GETTIME (curtime);
829 diff = SGEN_TV_ELAPSED (*timer, curtime);
834 processing_stw_step (void)
840 if (!dyn_array_ptr_size (®istered_bridges))
843 #if defined (DUMP_GRAPH)
844 printf ("-----------------\n");
847 SGEN_TV_GETTIME (curtime);
849 object_alloc_init ();
852 bridge_count = dyn_array_ptr_size (®istered_bridges);
853 for (i = 0; i < bridge_count ; ++i)
854 register_bridge_object ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
856 setup_time = step_timer (&curtime);
858 for (i = 0; i < bridge_count; ++i) {
859 ScanData *sd = find_data ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
860 if (sd->state == INITIAL) {
861 dyn_array_ptr_push (&scan_stack, sd);
864 g_assert (sd->state == FINISHED_OFF_STACK);
868 tarjan_time = step_timer (&curtime);
870 #if defined (DUMP_GRAPH)
871 printf ("----summary----\n");
872 printf ("bridges:\n");
873 for (int i = 0; i < bridge_count; ++i) {
874 ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i));
875 printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color);
878 dump_color_table (" after tarjan", FALSE);
881 clear_after_processing ();
886 gather_xrefs (ColorData *color)
889 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
890 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
894 if (dyn_array_ptr_size (&src->bridges))
895 dyn_array_ptr_add (&color_merge_array, src);
902 reset_xrefs (ColorData *color)
905 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
906 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
909 src->visited = FALSE;
910 if (!dyn_array_ptr_size (&src->bridges))
916 processing_build_callback_data (int generation)
919 MonoGCBridgeSCC **api_sccs;
920 MonoGCBridgeXRef *api_xrefs;
924 g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0);
925 g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs);
927 if (!dyn_array_ptr_size (®istered_bridges))
930 SGEN_TV_GETTIME (curtime);
932 /*create API objects */
934 #if defined (DUMP_GRAPH)
935 printf ("***** API *****\n");
936 printf ("number of SCCs %d\n", num_colors_with_bridges);
939 /* This is a straightforward translation from colors to the bridge callback format. */
940 api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_colors_with_bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
941 api_index = xref_count = 0;
943 for (cur = root_color_bucket; cur; cur = cur->next) {
945 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
946 int bridges = dyn_array_ptr_size (&cd->bridges);
950 api_sccs [api_index] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
951 api_sccs [api_index]->is_alive = FALSE;
952 api_sccs [api_index]->num_objs = bridges;
954 cd->api_index = api_index;
956 for (j = 0; j < bridges; ++j)
957 api_sccs [api_index]->objs [j] = (MonoObject *)dyn_array_ptr_get (&cd->bridges, j);
962 scc_setup_time = step_timer (&curtime);
964 for (cur = root_color_bucket; cur; cur = cur->next) {
966 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
967 int bridges = dyn_array_ptr_size (&cd->bridges);
971 dyn_array_ptr_empty (&color_merge_array);
974 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
975 xref_count += dyn_array_ptr_size (&cd->other_colors);
979 gather_xref_time = step_timer (&curtime);
981 #if defined (DUMP_GRAPH)
982 printf ("TOTAL XREFS %d\n", xref_count);
983 dump_color_table (" after xref pass", TRUE);
986 api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE);
988 for (cur = root_color_bucket; cur; cur = cur->next) {
990 for (src = &cur->data [0]; src < cur->next_data; ++src) {
991 int bridges = dyn_array_ptr_size (&src->bridges);
995 for (j = 0; j < dyn_array_ptr_size (&src->other_colors); ++j) {
996 ColorData *dest = (ColorData *)dyn_array_ptr_get (&src->other_colors, j);
997 g_assert (dyn_array_ptr_size (&dest->bridges)); /* We flattened the color graph, so this must never happen. */
999 api_xrefs [api_index].src_scc_index = src->api_index;
1000 api_xrefs [api_index].dst_scc_index = dest->api_index;
1006 g_assert (xref_count == api_index);
1007 xref_setup_time = step_timer (&curtime);
1009 #if defined (DUMP_GRAPH)
1010 printf ("---xrefs:\n");
1011 for (int i = 0; i < xref_count; ++i)
1012 printf ("\t%d -> %d\n", api_xrefs [i].src_scc_index, api_xrefs [i].dst_scc_index);
1015 //FIXME move half of the cleanup to before the bridge callback?
1016 bridge_processor->num_sccs = num_colors_with_bridges;
1017 bridge_processor->api_sccs = api_sccs;
1018 bridge_processor->num_xrefs = xref_count;
1019 bridge_processor->api_xrefs = api_xrefs;
1023 processing_after_callback (int generation)
1026 int bridge_count = dyn_array_ptr_size (®istered_bridges);
1027 int object_count = object_data_count;
1028 int color_count = color_data_count;
1029 int scc_count = num_colors_with_bridges;
1031 SGEN_TV_GETTIME (curtime);
1036 cleanup_time = step_timer (&curtime);
1038 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",
1039 bridge_count, object_count, color_count,
1040 ignored_objects, scc_count, xref_count,
1041 cache_hits, cache_misses,
1042 setup_time / 10000.0f,
1043 tarjan_time / 10000.0f,
1044 scc_setup_time / 10000.0f,
1045 gather_xref_time / 10000.0f,
1046 xref_setup_time / 10000.0f,
1047 cleanup_time / 10000.0f);
1049 cache_hits = cache_misses = 0;
1050 ignored_objects = 0;
1054 describe_pointer (GCObject *obj)
1056 // HashEntry *entry;
1059 for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) {
1060 if (obj == dyn_array_ptr_get (®istered_bridges, i)) {
1061 printf ("Pointer is a registered bridge object.\n");
1066 // entry = sgen_hash_table_lookup (&hash_table, obj);
1070 // printf ("Bridge hash table entry %p:\n", entry);
1071 // printf (" is bridge: %d\n", (int)entry->is_bridge);
1072 // printf (" is visited: %d\n", (int)entry->is_visited);
1076 sgen_tarjan_bridge_init (SgenBridgeProcessor *collector)
1078 collector->reset_data = reset_data;
1079 collector->processing_stw_step = processing_stw_step;
1080 collector->processing_build_callback_data = processing_build_callback_data;
1081 collector->processing_after_callback = processing_after_callback;
1082 collector->class_kind = class_kind;
1083 collector->register_finalized_object = register_finalized_object;
1084 collector->describe_pointer = describe_pointer;
1085 collector->enable_accounting = enable_accounting;
1086 // collector->set_dump_prefix = set_dump_prefix;
1088 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET, BUCKET_SIZE);
1089 g_assert (sizeof (ObjectBucket) <= BUCKET_SIZE);
1090 g_assert (sizeof (ColorBucket) <= BUCKET_SIZE);
1091 bridge_processor = collector;