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;
125 // Should color be made visible to client even though it has no bridges?
126 static inline gboolean bridgeless_color_is_heavy (ColorData *data) {
130 // Should color be made visible to client?
131 static inline gboolean color_visible_to_client (ColorData *data) {
132 return dyn_array_ptr_size (&data->bridges) || bridgeless_color_is_heavy (data);
135 // Stacks of ScanData objects used for tarjan algorithm.
136 // The Tarjan algorithm is normally defined recursively; here scan_stack simulates the call stack of a recursive algorithm,
137 // and loop_stack is the stack structure used by the algorithm itself.
138 static DynPtrArray scan_stack, loop_stack;
140 // GCObjects on which register_finalized_object has been called
141 static DynPtrArray registered_bridges;
144 static DynPtrArray color_merge_array;
146 static int ignored_objects;
147 static int object_index;
148 static int num_colors_with_bridges;
149 static int xref_count;
151 static size_t setup_time, tarjan_time, scc_setup_time, gather_xref_time, xref_setup_time, cleanup_time;
152 static SgenBridgeProcessor *bridge_processor;
154 #define BUCKET_SIZE 8184
157 #define NUM_SCAN_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ScanData))
159 typedef struct _ObjectBucket ObjectBucket;
160 struct _ObjectBucket {
163 ScanData data [NUM_SCAN_ENTRIES];
166 static ObjectBucket *root_object_bucket;
167 static ObjectBucket *cur_object_bucket;
168 static int object_data_count;
170 // Arenas to allocate ScanData from
172 new_object_bucket (void)
174 ObjectBucket *res = (ObjectBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
175 res->next_data = &res->data [0];
180 object_alloc_init (void)
182 root_object_bucket = cur_object_bucket = new_object_bucket ();
186 alloc_object_data (void)
191 /* next_data points to the first free entry */
192 res = cur_object_bucket->next_data;
193 if (res >= &cur_object_bucket->data [NUM_SCAN_ENTRIES]) {
194 ObjectBucket *b = new_object_bucket ();
195 cur_object_bucket->next = b;
196 cur_object_bucket = b;
199 cur_object_bucket->next_data = res + 1;
205 free_object_buckets (void)
207 ObjectBucket *cur = root_object_bucket;
209 object_data_count = 0;
212 ObjectBucket *tmp = cur->next;
213 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
217 root_object_bucket = cur_object_bucket = NULL;
221 #define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData))
223 // Arenas for ColorDatas, same as ObjectBucket except items-per-bucket differs
224 typedef struct _ColorBucket ColorBucket;
225 struct _ColorBucket {
227 ColorData *next_data;
228 ColorData data [NUM_COLOR_ENTRIES];
231 static ColorBucket *root_color_bucket;
232 static ColorBucket *cur_color_bucket;
233 static int color_data_count;
237 new_color_bucket (void)
239 ColorBucket *res = (ColorBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
240 res->next_data = &res->data [0];
245 color_alloc_init (void)
247 root_color_bucket = cur_color_bucket = new_color_bucket ();
251 alloc_color_data (void)
256 /* next_data points to the first free entry */
257 res = cur_color_bucket->next_data;
258 if (res >= &cur_color_bucket->data [NUM_COLOR_ENTRIES]) {
259 ColorBucket *b = new_color_bucket ();
260 cur_color_bucket->next = b;
261 cur_color_bucket = b;
264 cur_color_bucket->next_data = res + 1;
270 free_color_buckets (void)
272 ColorBucket *cur, *tmp;
274 color_data_count = 0;
276 for (cur = root_color_bucket; cur; cur = tmp) {
278 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
279 dyn_array_ptr_uninit (&cd->other_colors);
280 dyn_array_ptr_uninit (&cd->bridges);
283 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
285 root_color_bucket = cur_color_bucket = NULL;
290 create_data (GCObject *obj)
292 mword *o = (mword*)obj;
293 ScanData *res = alloc_object_data ();
296 res->index = res->low_index = -1;
297 res->state = INITIAL;
298 res->is_bridge = FALSE;
299 res->obj_state = o [0] & SGEN_VTABLE_BITS_MASK;
300 res->lock_word = o [1];
302 o [0] |= SGEN_VTABLE_BITS_MASK;
308 find_data (GCObject *obj)
311 mword *o = (mword*)obj;
312 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
313 a = (ScanData*)o [1];
318 clear_after_processing (void)
322 for (cur = root_object_bucket; cur; cur = cur->next) {
324 for (sd = &cur->data [0]; sd < cur->next_data; ++sd) {
325 mword *o = (mword*)sd->obj;
326 o [0] &= ~SGEN_VTABLE_BITS_MASK;
327 o [0] |= sd->obj_state;
328 o [1] = sd->lock_word;
334 bridge_object_forward (GCObject *obj)
337 mword *o = (mword*)obj;
338 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
341 fwd = SGEN_OBJECT_IS_FORWARDED (obj);
342 return fwd ? fwd : obj;
347 safe_name_bridge (GCObject *obj)
349 GCVTable vt = SGEN_LOAD_VTABLE (obj);
350 return vt->klass->name;
354 find_or_create_data (GCObject *obj)
356 ScanData *entry = find_data (obj);
358 entry = create_data (obj);
370 The merge cache maps an ordered list of ColorDatas [the color_merge_array] to a single ColorData.
372 About cache bucket tuning: We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
374 The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline,
375 making the extra space pretty much free.
377 The cost between 32 and 128 itens is minimal too, it's mostly a fixed, setup cost.
379 Memory wise, 4/32 takes 512 and 8/128 takes 8k, so it's quite reasonable.
382 #define ELEMENTS_PER_BUCKET 8
383 #define COLOR_CACHE_SIZE 128
384 static HashEntry merge_cache [COLOR_CACHE_SIZE][ELEMENTS_PER_BUCKET];
387 mix_hash (size_t hash)
389 return (int)(((hash * 215497) >> 16) ^ ((hash * 1823231) + hash));
395 memset (merge_cache, 0, sizeof (merge_cache));
400 dyn_array_ptr_contains (DynPtrArray *da, void *x)
403 for (i = 0; i < dyn_array_ptr_size (da); ++i)
404 if (dyn_array_ptr_get (da, i) == x)
410 match_colors (DynPtrArray *a, DynPtrArray *b)
413 if (dyn_array_ptr_size (a) != dyn_array_ptr_size (b))
416 for (i = 0; i < dyn_array_ptr_size (a); ++i) {
417 if (!dyn_array_ptr_contains (b, dyn_array_ptr_get (a, i)))
423 static int cache_hits, cache_misses, cache_abstains;
425 // The cache contains only non-bridged colors.
427 find_in_cache (int *insert_index)
430 int i, hash, size, index;
432 size = dyn_array_ptr_size (&color_merge_array);
433 /* Cache checking is very ineficient with a lot of elements*/
440 for (i = 0 ; i < size; ++i)
441 hash += mix_hash ((size_t)dyn_array_ptr_get (&color_merge_array, i));
445 index = hash & (COLOR_CACHE_SIZE - 1);
446 bucket = merge_cache [index];
447 for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) {
448 if (bucket [i].hash != hash)
450 if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) {
452 return bucket [i].color;
456 //move elements to the back
457 for (i = ELEMENTS_PER_BUCKET - 1; i > 0; --i)
458 bucket [i] = bucket [i - 1];
460 *insert_index = index;
461 bucket [0].hash = hash;
465 // A color is needed for an SCC. If the SCC has bridges, the color MUST be newly allocated.
466 // If the SCC lacks bridges, the allocator MAY use the cache to merge it with an existing one.
468 new_color (gboolean has_bridges)
472 /* XXX Try to find an equal one and return it */
474 cd = find_in_cache (&i);
479 cd = alloc_color_data ();
481 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
482 /* if i >= 0, it means we prepared a given slot to receive the new color */
484 merge_cache [i][0].color = cd;
491 register_bridge_object (GCObject *obj)
493 create_data (obj)->is_bridge = TRUE;
497 is_opaque_object (GCObject *obj)
499 MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
500 if ((vt->gc_bits & SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) {
501 SGEN_LOG (6, "ignoring %s\n", vt->klass->name);
508 // Called during DFS; visits one child. If it is a candidate to be scanned, pushes it to the stacks.
510 push_object (GCObject *obj)
513 obj = bridge_object_forward (obj);
516 printf ("\t= pushing %p %s -> ", obj, safe_name_bridge (obj));
519 /* Object types we can ignore */
520 if (is_opaque_object (obj)) {
527 data = find_data (obj);
529 /* Already marked - XXX must be done this way as the bridge themselves are alive. */
530 if (data && data->state != INITIAL) {
532 printf ("already marked\n");
537 /* We only care about dead objects */
538 if (!data && sgen_object_is_live (obj)) {
546 printf ("pushed!\n");
550 data = create_data (obj);
551 g_assert (data->state == INITIAL);
552 g_assert (data->index == -1);
553 dyn_array_ptr_push (&scan_stack, data);
557 #define HANDLE_PTR(ptr,obj) do { \
558 GCObject *dst = (GCObject*)*(ptr); \
559 if (dst) push_object (dst); \
562 // dfs () function's queue-children-of-object operation.
564 push_all (ScanData *data)
566 GCObject *obj = data->obj;
567 char *start = (char*)obj;
568 mword desc = sgen_obj_get_descriptor_safe (obj);
571 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
574 #include "sgen/sgen-scan-object.h"
579 compute_low_index (ScanData *data, GCObject *obj)
584 obj = bridge_object_forward (obj);
585 other = find_data (obj);
588 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);
593 g_assert (other->state != INITIAL);
595 if ((other->state == SCANNED || other->state == FINISHED_ON_STACK) && data->low_index > other->low_index)
596 data->low_index = other->low_index;
598 /* Compute the low color */
599 if (other->color == NULL)
604 dyn_array_ptr_add (&color_merge_array, other->color);
610 #define HANDLE_PTR(ptr,obj) do { \
611 GCObject *dst = (GCObject*)*(ptr); \
612 if (dst) compute_low_index (data, dst); \
616 compute_low (ScanData *data)
618 GCObject *obj = data->obj;
619 char *start = (char*)obj;
620 mword desc = sgen_obj_get_descriptor_safe (obj);
622 #include "sgen/sgen-scan-object.h"
625 // A non-bridged object needs a single color describing the current merge array.
629 ColorData *color = NULL;
630 int size = dyn_array_ptr_size (&color_merge_array);
632 // Merge array is empty-- this SCC points to no bridged colors.
633 // This SCC can be ignored completely.
637 // Merge array has one item-- this SCC points to a single bridged color.
638 // This SCC can be forwarded to the pointed-to color.
639 else if (size == 1) {
640 color = (ColorData *)dyn_array_ptr_get (&color_merge_array, 0);
642 // This SCC gets to talk to the color allocator.
644 color = new_color (FALSE);
650 create_scc (ScanData *data)
653 gboolean found = FALSE;
654 gboolean found_bridge = FALSE;
655 ColorData *color_data = NULL;
657 for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) {
658 ScanData *other = (ScanData *)dyn_array_ptr_get (&loop_stack, i);
659 found_bridge |= other->is_bridge;
660 if (found_bridge || other == data)
665 printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data->obj), data->obj, found_bridge);
666 printf ("\tpoints-to-colors: ");
667 for (int i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i)
668 printf ("%p ", dyn_array_ptr_get (&color_merge_array, i));
671 printf ("loop stack: ");
672 for (int i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) {
673 ScanData *other = dyn_array_ptr_get (&loop_stack, i);
674 printf ("(%d/%d)", other->index, other->low_index);
680 color_data = new_color (TRUE);
681 ++num_colors_with_bridges;
683 color_data = reduce_color ();
686 while (dyn_array_ptr_size (&loop_stack) > 0) {
687 ScanData *other = (ScanData *)dyn_array_ptr_pop (&loop_stack);
690 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);
693 other->color = color_data;
694 switch (other->state) {
695 case FINISHED_ON_STACK:
696 other->state = FINISHED_OFF_STACK;
698 case FINISHED_OFF_STACK:
701 g_error ("Invalid state when building SCC %d", other->state);
704 if (other->is_bridge)
705 dyn_array_ptr_add (&color_data->bridges, other->obj);
714 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) {
715 ColorData *cd = (ColorData *)dyn_array_ptr_get (&color_merge_array, i);
716 g_assert (cd->visited);
719 dyn_array_ptr_empty (&color_merge_array);
720 found_bridge = FALSE;
726 g_assert (dyn_array_ptr_size (&scan_stack) == 1);
727 g_assert (dyn_array_ptr_size (&loop_stack) == 0);
729 dyn_array_ptr_empty (&color_merge_array);
731 while (dyn_array_ptr_size (&scan_stack) > 0) {
732 ScanData *data = (ScanData *)dyn_array_ptr_pop (&scan_stack);
735 * Ignore finished objects on stack, they happen due to loops. For example:
741 * We start scanning from A and push C before B. So, after the first iteration, the scan stack will have: A C B.
742 * We then visit B, which will find C in its initial state and push again.
743 * Finally after finish with C and B, the stack will be left with "A C" and at this point C should be ignored.
745 * The above explains FINISHED_ON_STACK, to explain FINISHED_OFF_STACK, consider if the root was D, which pointed
746 * to A and C. A is processed first, leaving C on stack after that in the mentioned state.
748 if (data->state == FINISHED_ON_STACK || data->state == FINISHED_OFF_STACK)
751 if (data->state == INITIAL) {
752 g_assert (data->index == -1);
753 g_assert (data->low_index == -1);
755 data->state = SCANNED;
756 data->low_index = data->index = object_index++;
757 dyn_array_ptr_push (&scan_stack, data);
758 dyn_array_ptr_push (&loop_stack, data);
761 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
766 g_assert (data->state == SCANNED);
767 data->state = FINISHED_ON_STACK;
770 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);
773 /* Compute low index */
777 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);
780 if (data->index == data->low_index)
787 register_finalized_object (GCObject *obj)
789 g_assert (sgen_need_bridge_processing ());
790 dyn_array_ptr_push (®istered_bridges, obj);
796 dyn_array_ptr_empty (®istered_bridges);
802 dyn_array_ptr_empty (&scan_stack);
803 dyn_array_ptr_empty (&loop_stack);
804 dyn_array_ptr_empty (®istered_bridges);
805 free_object_buckets ();
806 free_color_buckets ();
809 num_colors_with_bridges = 0;
814 dump_color_table (const char *why, gboolean do_index)
818 printf ("colors%s:\n", why);
820 for (cur = root_color_bucket; cur; cur = cur->next, ++i) {
822 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
824 printf ("\t%d(%d):", i, cd->api_index);
826 printf ("\t%d: ", i);
827 for (j = 0; j < dyn_array_ptr_size (&cd->other_colors); ++j) {
828 printf ("%p ", dyn_array_ptr_get (&cd->other_colors, j));
830 if (dyn_array_ptr_size (&cd->bridges)) {
831 printf (" bridges: ");
832 for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) {
833 GCObject *obj = dyn_array_ptr_get (&cd->bridges, j);
834 ScanData *data = find_or_create_data (obj);
835 printf ("%d ", data->index);
846 step_timer (gint64 *timer)
848 gint64 curtime, diff;
850 SGEN_TV_GETTIME (curtime);
851 diff = SGEN_TV_ELAPSED (*timer, curtime);
856 processing_stw_step (void)
862 if (!dyn_array_ptr_size (®istered_bridges))
865 #if defined (DUMP_GRAPH)
866 printf ("-----------------\n");
869 SGEN_TV_GETTIME (curtime);
871 object_alloc_init ();
874 bridge_count = dyn_array_ptr_size (®istered_bridges);
875 for (i = 0; i < bridge_count ; ++i)
876 register_bridge_object ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
878 setup_time = step_timer (&curtime);
880 for (i = 0; i < bridge_count; ++i) {
881 ScanData *sd = find_data ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
882 if (sd->state == INITIAL) {
883 dyn_array_ptr_push (&scan_stack, sd);
886 g_assert (sd->state == FINISHED_OFF_STACK);
890 tarjan_time = step_timer (&curtime);
892 #if defined (DUMP_GRAPH)
893 printf ("----summary----\n");
894 printf ("bridges:\n");
895 for (int i = 0; i < bridge_count; ++i) {
896 ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i));
897 printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color);
900 dump_color_table (" after tarjan", FALSE);
903 clear_after_processing ();
908 gather_xrefs (ColorData *color)
911 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
912 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
916 if (color_visible_to_client (src))
917 dyn_array_ptr_add (&color_merge_array, src);
924 reset_xrefs (ColorData *color)
927 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
928 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
931 src->visited = FALSE;
932 if (!color_visible_to_client (src))
938 processing_build_callback_data (int generation)
941 MonoGCBridgeSCC **api_sccs;
942 MonoGCBridgeXRef *api_xrefs;
946 g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0);
947 g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs);
949 if (!dyn_array_ptr_size (®istered_bridges))
952 SGEN_TV_GETTIME (curtime);
954 /*create API objects */
956 #if defined (DUMP_GRAPH)
957 printf ("***** API *****\n");
958 printf ("number of SCCs %d\n", num_colors_with_bridges);
961 // Count the number of SCCs visible to the client
963 for (cur = root_color_bucket; cur; cur = cur->next) {
965 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
966 if (color_visible_to_client (cd))
971 /* This is a straightforward translation from colors to the bridge callback format. */
972 api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA, TRUE);
973 api_index = xref_count = 0;
975 // Convert visible SCCs, along with their bridged object list, to MonoGCBridgeSCCs in the client's SCC list
976 for (cur = root_color_bucket; cur; cur = cur->next) {
978 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
979 int bridges = dyn_array_ptr_size (&cd->bridges);
980 if (!(bridges || bridgeless_color_is_heavy (cd)))
983 api_sccs [api_index] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
984 api_sccs [api_index]->is_alive = FALSE;
985 api_sccs [api_index]->num_objs = bridges;
987 cd->api_index = api_index;
989 for (j = 0; j < bridges; ++j)
990 api_sccs [api_index]->objs [j] = (MonoObject *)dyn_array_ptr_get (&cd->bridges, j);
995 scc_setup_time = step_timer (&curtime);
997 // Eliminate non-visible SCCs from the SCC list and redistribute xrefs
998 for (cur = root_color_bucket; cur; cur = cur->next) {
1000 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1001 if (!color_visible_to_client (cd))
1004 dyn_array_ptr_empty (&color_merge_array);
1007 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
1008 xref_count += dyn_array_ptr_size (&cd->other_colors);
1012 gather_xref_time = step_timer (&curtime);
1014 #if defined (DUMP_GRAPH)
1015 printf ("TOTAL XREFS %d\n", xref_count);
1016 dump_color_table (" after xref pass", TRUE);
1019 // Write out xrefs array
1020 api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1022 for (cur = root_color_bucket; cur; cur = cur->next) {
1024 for (src = &cur->data [0]; src < cur->next_data; ++src) {
1025 if (!color_visible_to_client (src))
1028 for (j = 0; j < dyn_array_ptr_size (&src->other_colors); ++j) {
1029 ColorData *dest = (ColorData *)dyn_array_ptr_get (&src->other_colors, j);
1030 g_assert (color_visible_to_client (dest)); /* Supposedly we already eliminated all xrefs to non-visible objects. */
1032 api_xrefs [api_index].src_scc_index = src->api_index;
1033 api_xrefs [api_index].dst_scc_index = dest->api_index;
1039 g_assert (xref_count == api_index);
1040 xref_setup_time = step_timer (&curtime);
1042 #if defined (DUMP_GRAPH)
1043 printf ("---xrefs:\n");
1044 for (int i = 0; i < xref_count; ++i)
1045 printf ("\t%d -> %d\n", api_xrefs [i].src_scc_index, api_xrefs [i].dst_scc_index);
1048 //FIXME move half of the cleanup to before the bridge callback?
1049 bridge_processor->num_sccs = num_sccs;
1050 bridge_processor->api_sccs = api_sccs;
1051 bridge_processor->num_xrefs = xref_count;
1052 bridge_processor->api_xrefs = api_xrefs;
1056 processing_after_callback (int generation)
1059 int bridge_count = dyn_array_ptr_size (®istered_bridges);
1060 int object_count = object_data_count;
1061 int color_count = color_data_count;
1062 int scc_count = num_colors_with_bridges;
1064 SGEN_TV_GETTIME (curtime);
1069 cleanup_time = step_timer (&curtime);
1071 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/%d setup %.2fms tarjan %.2fms scc-setup %.2fms gather-xref %.2fms xref-setup %.2fms cleanup %.2fms",
1072 bridge_count, object_count, color_count,
1073 ignored_objects, scc_count, xref_count,
1074 cache_hits, cache_misses, cache_abstains,
1075 setup_time / 10000.0f,
1076 tarjan_time / 10000.0f,
1077 scc_setup_time / 10000.0f,
1078 gather_xref_time / 10000.0f,
1079 xref_setup_time / 10000.0f,
1080 cleanup_time / 10000.0f);
1082 cache_hits = cache_misses = cache_abstains = 0;
1083 ignored_objects = 0;
1087 describe_pointer (GCObject *obj)
1089 // HashEntry *entry;
1092 for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) {
1093 if (obj == dyn_array_ptr_get (®istered_bridges, i)) {
1094 printf ("Pointer is a registered bridge object.\n");
1099 // entry = sgen_hash_table_lookup (&hash_table, obj);
1103 // printf ("Bridge hash table entry %p:\n", entry);
1104 // printf (" is bridge: %d\n", (int)entry->is_bridge);
1105 // printf (" is visited: %d\n", (int)entry->is_visited);
1109 sgen_tarjan_bridge_init (SgenBridgeProcessor *collector)
1111 collector->reset_data = reset_data;
1112 collector->processing_stw_step = processing_stw_step;
1113 collector->processing_build_callback_data = processing_build_callback_data;
1114 collector->processing_after_callback = processing_after_callback;
1115 collector->class_kind = class_kind;
1116 collector->register_finalized_object = register_finalized_object;
1117 collector->describe_pointer = describe_pointer;
1118 collector->enable_accounting = enable_accounting;
1119 // collector->set_dump_prefix = set_dump_prefix;
1121 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET, BUCKET_SIZE);
1122 g_assert (sizeof (ObjectBucket) <= BUCKET_SIZE);
1123 g_assert (sizeof (ColorBucket) <= BUCKET_SIZE);
1124 bridge_processor = collector;