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)
7 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
8 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
10 * Permission is hereby granted to use or copy this program
11 * for any purpose, provided the above notices are retained on all copies.
12 * Permission to modify the code and to distribute modified code is granted,
13 * provided the above notices are retained, and a notice that the code was
14 * modified is included with the above copyright notice.
17 * Copyright 2001-2003 Ximian, Inc
18 * Copyright 2003-2010 Novell, Inc.
20 * Permission is hereby granted, free of charge, to any person obtaining
21 * a copy of this software and associated documentation files (the
22 * "Software"), to deal in the Software without restriction, including
23 * without limitation the rights to use, copy, modify, merge, publish,
24 * distribute, sublicense, and/or sell copies of the Software, and to
25 * permit persons to whom the Software is furnished to do so, subject to
26 * the following conditions:
28 * The above copyright notice and this permission notice shall be
29 * included in all copies or substantial portions of the Software.
31 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
47 #include "sgen-bridge.h"
48 #include "sgen-hash-table.h"
49 #include "sgen-qsort.h"
50 #include "tabledefs.h"
51 #include "utils/mono-logger-internal.h"
52 #include "utils/mono-time.h"
69 dyn_array_uninit (DynArray *da, int elem_size)
71 if (da->capacity <= 0)
74 sgen_free_internal_dynamic (da->data, elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA);
79 dyn_array_ensure_capacity (DynArray *da, int capacity, int elem_size)
81 int old_capacity = da->capacity;
84 if (capacity <= old_capacity)
87 if (da->capacity == 0)
89 while (capacity > da->capacity)
92 new_data = sgen_alloc_internal_dynamic (elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA, TRUE);
94 memcpy (new_data, da->data, elem_size * da->size);
95 sgen_free_internal_dynamic (da->data, elem_size * old_capacity, INTERNAL_MEM_BRIDGE_DATA);
101 dyn_array_add (DynArray *da, int elem_size)
105 dyn_array_ensure_capacity (da, da->size + 1, elem_size);
107 p = da->data + da->size * elem_size;
115 dyn_array_ptr_uninit (DynPtrArray *da)
117 dyn_array_uninit (&da->array, sizeof (void*));
121 dyn_array_ptr_size (DynPtrArray *da)
123 return da->array.size;
127 dyn_array_ptr_set_size (DynPtrArray *da, int size)
129 da->array.size = size;
133 dyn_array_ptr_get (DynPtrArray *da, int x)
135 return ((void**)da->array.data)[x];
139 dyn_array_ptr_add (DynPtrArray *da, void *ptr)
141 void **p = dyn_array_add (&da->array, sizeof (void*));
145 #define dyn_array_ptr_push dyn_array_ptr_add
148 dyn_array_ptr_pop (DynPtrArray *da)
151 int size = da->array.size;
153 p = dyn_array_ptr_get (da, size - 1);
159 dyn_array_ptr_ensure_capacity (DynPtrArray *da, int capacity)
161 dyn_array_ensure_capacity (&da->array, capacity, sizeof (void*));
166 dyn_array_ptr_set_all (DynPtrArray *dst, DynPtrArray *src)
168 if (src->array.size > 0) {
169 dyn_array_ptr_ensure_capacity (dst, src->array.size);
170 memcpy (dst->array.data, src->array.data, src->array.size * sizeof (void*));
172 dst->array.size = src->array.size;
176 enable_accounting (void)
178 // bridge_accounting_enabled = TRUE;
179 // 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);
182 static MonoGCBridgeObjectKind
183 class_kind (MonoClass *class)
185 MonoGCBridgeObjectKind res = bridge_callbacks.bridge_class_kind (class);
187 /* If it's a bridge, nothing we can do about it. */
188 if (res == GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS || res == GC_BRIDGE_OPAQUE_BRIDGE_CLASS)
191 /* Non bridge classes with no pointers will never point to a bridge, so we can savely ignore them. */
192 if (!class->has_references) {
193 SGEN_LOG (6, "class %s is opaque\n", class->name);
194 return GC_BRIDGE_OPAQUE_CLASS;
197 /* Some arrays can be ignored */
198 if (class->rank == 1) {
199 MonoClass *elem_class = class->element_class;
201 /* FIXME the bridge check can be quite expensive, cache it at the class level. */
202 /* An array of a sealed type that is not a bridge will never get to a bridge */
203 if ((elem_class->flags & TYPE_ATTRIBUTE_SEALED) && !elem_class->has_references && !bridge_callbacks.bridge_class_kind (elem_class)) {
204 SGEN_LOG (6, "class %s is opaque\n", class->name);
205 return GC_BRIDGE_OPAQUE_CLASS;
209 return GC_BRIDGE_TRANSPARENT_CLASS;
212 //enable unsage logging
213 // #define DUMP_GRAPH 1
224 We can split this data structure in two, those with bridges and those without
227 DynPtrArray other_colors;
230 unsigned visited : 1;
235 GCObject *obj; //XXX this can be eliminated.
243 unsigned is_bridge : 1;
244 unsigned obj_state : 2;
250 static DynPtrArray scan_stack, loop_stack, registered_bridges;
251 static DynPtrArray color_merge_array;
253 static int ignored_objects;
254 static int object_index;
255 static int num_colors_with_bridges;
256 static int xref_count;
258 static size_t setup_time, tarjan_time, scc_setup_time, gather_xref_time, xref_setup_time, cleanup_time;
259 static SgenBridgeProcessor *bridge_processor;
261 #define BUCKET_SIZE 8184
264 #define NUM_SCAN_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ScanData))
266 typedef struct _ObjectBucket ObjectBucket;
267 struct _ObjectBucket {
270 ScanData data [NUM_SCAN_ENTRIES];
273 static ObjectBucket *root_object_bucket;
274 static ObjectBucket *cur_object_bucket;
275 static int object_data_count;
278 new_object_bucket (void)
280 ObjectBucket *res = sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
281 res->next_data = &res->data [0];
286 object_alloc_init (void)
288 root_object_bucket = cur_object_bucket = new_object_bucket ();
292 alloc_object_data (void)
297 /* next_data points to the first free entry */
298 res = cur_object_bucket->next_data;
299 if (res >= &cur_object_bucket->data [NUM_SCAN_ENTRIES]) {
300 ObjectBucket *b = new_object_bucket ();
301 cur_object_bucket->next = b;
302 cur_object_bucket = b;
305 cur_object_bucket->next_data = res + 1;
311 free_object_buckets (void)
313 ObjectBucket *cur = root_object_bucket;
315 object_data_count = 0;
318 ObjectBucket *tmp = cur->next;
319 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
323 root_object_bucket = cur_object_bucket = NULL;
327 #define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData))
329 typedef struct _ColorBucket ColorBucket;
330 struct _ColorBucket {
332 ColorData *next_data;
333 ColorData data [NUM_COLOR_ENTRIES];
336 static ColorBucket *root_color_bucket;
337 static ColorBucket *cur_color_bucket;
338 static int color_data_count;
342 new_color_bucket (void)
344 ColorBucket *res = sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
345 res->next_data = &res->data [0];
350 color_alloc_init (void)
352 root_color_bucket = cur_color_bucket = new_color_bucket ();
356 alloc_color_data (void)
361 /* next_data points to the first free entry */
362 res = cur_color_bucket->next_data;
363 if (res >= &cur_color_bucket->data [NUM_COLOR_ENTRIES]) {
364 ColorBucket *b = new_color_bucket ();
365 cur_color_bucket->next = b;
366 cur_color_bucket = b;
369 cur_color_bucket->next_data = res + 1;
375 free_color_buckets (void)
377 ColorBucket *cur, *tmp;
379 color_data_count = 0;
381 for (cur = root_color_bucket; cur; cur = tmp) {
383 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
384 dyn_array_ptr_uninit (&cd->other_colors);
385 dyn_array_ptr_uninit (&cd->bridges);
388 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
390 root_color_bucket = cur_color_bucket = NULL;
395 create_data (GCObject *obj)
397 mword *o = (mword*)obj;
398 ScanData *res = alloc_object_data ();
401 res->index = res->low_index = -1;
402 res->obj_state = o [0] & SGEN_VTABLE_BITS_MASK;
403 res->lock_word = o [1];
405 o [0] |= SGEN_VTABLE_BITS_MASK;
411 find_data (GCObject *obj)
414 mword *o = (mword*)obj;
415 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
416 a = (ScanData*)o [1];
421 clear_after_processing (void)
425 for (cur = root_object_bucket; cur; cur = cur->next) {
427 for (sd = &cur->data [0]; sd < cur->next_data; ++sd) {
428 mword *o = (mword*)sd->obj;
429 o [0] &= ~SGEN_VTABLE_BITS_MASK;
430 o [0] |= sd->obj_state;
431 o [1] = sd->lock_word;
437 bridge_object_forward (GCObject *obj)
440 mword *o = (mword*)obj;
441 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
444 fwd = SGEN_OBJECT_IS_FORWARDED (obj);
445 return fwd ? fwd : obj;
450 safe_name_bridge (GCObject *obj)
452 GCVTable *vt = (GCVTable*)SGEN_LOAD_VTABLE (obj);
453 return vt->klass->name;
457 find_or_create_data (GCObject *obj)
459 ScanData *entry = find_data (obj);
461 entry = create_data (obj);
473 We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
475 The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline,
476 making the extra space pretty much free.
478 The cost between 32 and 128 itens is minimal too, it's mostly a fixed, setup cost.
480 Memory wise, 4/32 takes 512 and 8/128 takes 8k, so it's quite reasonable.
483 #define ELEMENTS_PER_BUCKET 8
484 #define COLOR_CACHE_SIZE 128
485 static HashEntry merge_cache [COLOR_CACHE_SIZE][ELEMENTS_PER_BUCKET];
488 mix_hash (size_t hash)
490 return (int)(((hash * 215497) >> 16) ^ ((hash * 1823231) + hash));
496 memset (merge_cache, 0, sizeof (merge_cache));
501 dyn_array_ptr_contains (DynPtrArray *da, void *x)
504 for (i = 0; i < dyn_array_ptr_size (da); ++i)
505 if (dyn_array_ptr_get (da, i) == x)
511 match_colors (DynPtrArray *a, DynPtrArray *b)
514 if (dyn_array_ptr_size (a) != dyn_array_ptr_size (b))
517 for (i = 0; i < dyn_array_ptr_size (a); ++i) {
518 if (!dyn_array_ptr_contains (b, dyn_array_ptr_get (a, i)))
524 static int cache_hits, cache_misses;
527 find_in_cache (int *insert_index)
530 int i, hash, size, index;
532 size = dyn_array_ptr_size (&color_merge_array);
533 /* Cache checking is very ineficient with a lot of elements*/
538 for (i = 0 ; i < size; ++i)
539 hash += mix_hash ((size_t)dyn_array_ptr_get (&color_merge_array, i));
543 index = hash & (COLOR_CACHE_SIZE - 1);
544 bucket = merge_cache [index];
545 for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) {
546 if (bucket [i].hash != hash)
548 if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) {
550 return bucket [i].color;
554 //move elements to the back
555 for (i = ELEMENTS_PER_BUCKET - 1; i > 0; --i)
556 bucket [i] = bucket [i - 1];
558 *insert_index = index;
559 bucket [0].hash = hash;
564 new_color (gboolean force_new)
568 /* XXX Try to find an equal one and return it */
570 cd = find_in_cache (&i);
575 cd = alloc_color_data ();
577 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
578 /* if i >= 0, it means we prepared a given slot to receive the new color */
580 merge_cache [i][0].color = cd;
587 register_bridge_object (GCObject *obj)
589 create_data (obj)->is_bridge = TRUE;
593 is_opaque_object (GCObject *obj)
595 MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
596 if ((vt->gc_bits & SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) {
597 SGEN_LOG (6, "ignoring %s\n", vt->klass->name);
605 push_object (GCObject *obj)
608 obj = bridge_object_forward (obj);
611 printf ("\t= pushing %p %s -> ", obj, safe_name_bridge (obj));
614 /* Object types we can ignore */
615 if (is_opaque_object (obj)) {
622 data = find_data (obj);
624 /* Already marked - XXX must be done this way as the bridge themselves are alive. */
625 if (data && data->state != INITIAL) {
627 printf ("already marked\n");
632 /* We only care about dead objects */
633 if (!data && sgen_object_is_live (obj)) {
641 printf ("pushed!\n");
645 data = create_data (obj);
646 g_assert (data->state == INITIAL);
647 g_assert (data->index == -1);
648 dyn_array_ptr_push (&scan_stack, data);
652 #define HANDLE_PTR(ptr,obj) do { \
653 GCObject *dst = (GCObject*)*(ptr); \
654 if (dst) push_object (dst); \
658 push_all (ScanData *data)
660 GCObject *obj = data->obj;
661 char *start = (char*)obj;
662 mword desc = sgen_obj_get_descriptor_safe (start);
665 printf ("**scanning %p %s\n", obj, safe_name_bridge (obj));
668 #include "sgen-scan-object.h"
673 compute_low_index (ScanData *data, GCObject *obj)
677 obj = bridge_object_forward (obj);
679 other = find_data (obj);
682 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);
687 g_assert (other->state != INITIAL);
689 if ((other->state == SCANNED || other->state == FINISHED_ON_STACK) && data->low_index > other->low_index)
690 data->low_index = other->low_index;
692 /* Compute the low color */
693 if (other->color == NULL)
698 dyn_array_ptr_add (&color_merge_array, other->color);
704 #define HANDLE_PTR(ptr,obj) do { \
705 GCObject *dst = (GCObject*)*(ptr); \
706 if (dst) compute_low_index (data, dst); \
710 compute_low (ScanData *data)
712 GCObject *obj = data->obj;
713 char *start = (char*)obj;
714 mword desc = sgen_obj_get_descriptor_safe (start);
716 #include "sgen-scan-object.h"
722 ColorData *color = NULL;
723 int size = dyn_array_ptr_size (&color_merge_array);
727 else if (size == 1) {
728 color = dyn_array_ptr_get (&color_merge_array, 0);
730 color = new_color (FALSE);
736 create_scc (ScanData *data)
739 gboolean found = FALSE;
740 gboolean found_bridge = FALSE;
741 ColorData *color_data = NULL;
743 for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) {
744 ScanData *other = dyn_array_ptr_get (&loop_stack, i);
745 found_bridge |= other->is_bridge;
746 if (found_bridge || other == data)
751 printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data->obj), data->obj, found_bridge);
752 printf ("\tpoints-to-colors: ");
753 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i)
754 printf ("%p ", dyn_array_ptr_get (&color_merge_array, i));
757 printf ("loop stack: ");
758 for (i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) {
759 ScanData *other = dyn_array_ptr_get (&loop_stack, i);
760 printf ("(%d/%d)", other->index, other->low_index);
766 color_data = new_color (TRUE);
767 ++num_colors_with_bridges;
769 color_data = reduce_color ();
772 while (dyn_array_ptr_size (&loop_stack) > 0) {
773 ScanData *other = dyn_array_ptr_pop (&loop_stack);
776 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);
779 other->color = color_data;
780 switch (other->state) {
781 case FINISHED_ON_STACK:
782 other->state = FINISHED_OFF_STACK;
784 case FINISHED_OFF_STACK:
787 g_error ("Invalid state when building SCC %d", other->state);
790 if (other->is_bridge)
791 dyn_array_ptr_add (&color_data->bridges, other->obj);
800 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) {
801 ColorData *cd = dyn_array_ptr_get (&color_merge_array, i);
802 g_assert (cd->visited);
805 dyn_array_ptr_set_size (&color_merge_array, 0);
806 found_bridge = FALSE;
812 g_assert (dyn_array_ptr_size (&scan_stack) == 1);
813 g_assert (dyn_array_ptr_size (&loop_stack) == 0);
815 dyn_array_ptr_set_size (&color_merge_array, 0);
817 while (dyn_array_ptr_size (&scan_stack) > 0) {
818 ScanData *data = dyn_array_ptr_pop (&scan_stack);
821 * Ignore finished objects on stack, they happen due to loops. For example:
827 * We start scanning from A and push C before B. So, after the first iteration, the scan stack will have: A C B.
828 * We then visit B, which will find C in its initial state and push again.
829 * Finally after finish with C and B, the stack will be left with "A C" and at this point C should be ignored.
831 * The above explains FINISHED_ON_STACK, to explain FINISHED_OFF_STACK, consider if the root was D, which pointed
832 * to A and C. A is processed first, leaving C on stack after that in the mentioned state.
834 if (data->state == FINISHED_ON_STACK || data->state == FINISHED_OFF_STACK)
837 if (data->state == INITIAL) {
838 g_assert (data->index == -1);
839 g_assert (data->low_index == -1);
841 data->state = SCANNED;
842 data->low_index = data->index = object_index++;
843 dyn_array_ptr_push (&scan_stack, data);
844 dyn_array_ptr_push (&loop_stack, data);
847 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
852 g_assert (data->state == SCANNED);
853 data->state = FINISHED_ON_STACK;
856 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);
859 /* Compute low index */
863 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);
866 if (data->index == data->low_index)
873 register_finalized_object (GCObject *obj)
875 g_assert (sgen_need_bridge_processing ());
876 dyn_array_ptr_push (®istered_bridges, obj);
882 dyn_array_ptr_set_size (®istered_bridges, 0);
888 dyn_array_ptr_set_size (&scan_stack, 0);
889 dyn_array_ptr_set_size (&loop_stack, 0);
890 dyn_array_ptr_set_size (®istered_bridges, 0);
891 free_object_buckets ();
892 free_color_buckets ();
895 num_colors_with_bridges = 0;
900 dump_color_table (const char *why, gboolean do_index)
904 printf ("colors%s:\n", why);
906 for (cur = root_color_bucket; cur; cur = cur->next, ++i) {
908 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
910 printf ("\t%d(%d):", i, cd->api_index);
912 printf ("\t%d: ", i);
913 for (j = 0; j < dyn_array_ptr_size (&cd->other_colors); ++j) {
914 printf ("%p ", dyn_array_ptr_get (&cd->other_colors, j));
916 if (dyn_array_ptr_size (&cd->bridges)) {
917 printf (" bridges: ");
918 for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) {
919 GCObject *obj = dyn_array_ptr_get (&cd->bridges, j);
920 ScanData *data = find_or_create_data (obj);
921 printf ("%d ", data->index);
932 step_timer (gint64 *timer)
934 gint64 curtime, diff;
936 SGEN_TV_GETTIME (curtime);
937 diff = SGEN_TV_ELAPSED (*timer, curtime);
942 processing_stw_step (void)
948 if (!dyn_array_ptr_size (®istered_bridges))
951 #if defined (DUMP_GRAPH)
952 printf ("-----------------\n");
955 * bridge_processing_in_progress must be set with the world
956 * stopped. If not there would be race conditions.
958 bridge_processing_in_progress = TRUE;
960 SGEN_TV_GETTIME (curtime);
962 object_alloc_init ();
965 bridge_count = dyn_array_ptr_size (®istered_bridges);
966 for (i = 0; i < bridge_count ; ++i)
967 register_bridge_object (dyn_array_ptr_get (®istered_bridges, i));
969 setup_time = step_timer (&curtime);
971 for (i = 0; i < bridge_count; ++i) {
972 ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i));
973 if (sd->state == INITIAL) {
974 dyn_array_ptr_push (&scan_stack, sd);
977 g_assert (sd->state == FINISHED_OFF_STACK);
981 tarjan_time = step_timer (&curtime);
983 #if defined (DUMP_GRAPH)
984 printf ("----summary----\n");
985 printf ("bridges:\n");
986 for (i = 0; i < bridge_count; ++i) {
987 ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i));
988 printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color);
991 dump_color_table (" after tarjan", FALSE);
994 clear_after_processing ();
999 gather_xrefs (ColorData *color)
1002 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
1003 ColorData *src = dyn_array_ptr_get (&color->other_colors, i);
1006 src->visited = TRUE;
1007 if (dyn_array_ptr_size (&src->bridges))
1008 dyn_array_ptr_add (&color_merge_array, src);
1015 reset_xrefs (ColorData *color)
1018 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
1019 ColorData *src = dyn_array_ptr_get (&color->other_colors, i);
1022 src->visited = FALSE;
1023 if (!dyn_array_ptr_size (&src->bridges))
1029 processing_build_callback_data (int generation)
1032 MonoGCBridgeSCC **api_sccs;
1033 MonoGCBridgeXRef *api_xrefs;
1037 g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0);
1038 g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs);
1040 if (!dyn_array_ptr_size (®istered_bridges))
1043 SGEN_TV_GETTIME (curtime);
1045 /*create API objects */
1047 #if defined (DUMP_GRAPH)
1048 printf ("***** API *****\n");
1049 printf ("number of SCCs %d\n", num_colors_with_bridges);
1052 /* This is a straightforward translation from colors to the bridge callback format. */
1053 api_sccs = sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_colors_with_bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1054 api_index = xref_count = 0;
1056 for (cur = root_color_bucket; cur; cur = cur->next) {
1058 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1059 int bridges = dyn_array_ptr_size (&cd->bridges);
1063 api_sccs [api_index] = sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1064 api_sccs [api_index]->is_alive = FALSE;
1065 api_sccs [api_index]->num_objs = bridges;
1067 cd->api_index = api_index;
1069 for (j = 0; j < bridges; ++j)
1070 api_sccs [api_index]->objs [j] = dyn_array_ptr_get (&cd->bridges, j);
1075 scc_setup_time = step_timer (&curtime);
1077 for (cur = root_color_bucket; cur; cur = cur->next) {
1079 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1080 int bridges = dyn_array_ptr_size (&cd->bridges);
1084 dyn_array_ptr_set_size (&color_merge_array, 0);
1087 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
1088 xref_count += dyn_array_ptr_size (&cd->other_colors);
1092 gather_xref_time = step_timer (&curtime);
1094 #if defined (DUMP_GRAPH)
1095 printf ("TOTAL XREFS %d\n", xref_count);
1096 dump_color_table (" after xref pass", TRUE);
1099 api_xrefs = sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1101 for (cur = root_color_bucket; cur; cur = cur->next) {
1103 for (src = &cur->data [0]; src < cur->next_data; ++src) {
1104 int bridges = dyn_array_ptr_size (&src->bridges);
1108 for (j = 0; j < dyn_array_ptr_size (&src->other_colors); ++j) {
1109 ColorData *dest = dyn_array_ptr_get (&src->other_colors, j);
1110 g_assert (dyn_array_ptr_size (&dest->bridges)); /* We flattened the color graph, so this must never happen. */
1112 api_xrefs [api_index].src_scc_index = src->api_index;
1113 api_xrefs [api_index].dst_scc_index = dest->api_index;
1119 g_assert (xref_count == api_index);
1120 xref_setup_time = step_timer (&curtime);
1122 #if defined (DUMP_GRAPH)
1123 printf ("---xrefs:\n");
1124 for (i = 0; i < xref_count; ++i)
1125 printf ("\t%d -> %d\n", api_xrefs [i].src_scc_index, api_xrefs [i].dst_scc_index);
1128 //FIXME move half of the cleanup to before the bridge callback?
1129 bridge_processor->num_sccs = num_colors_with_bridges;
1130 bridge_processor->api_sccs = api_sccs;
1131 bridge_processor->num_xrefs = xref_count;
1132 bridge_processor->api_xrefs = api_xrefs;
1136 processing_after_callback (int generation)
1139 int bridge_count = dyn_array_ptr_size (®istered_bridges);
1140 int object_count = object_data_count;
1141 int color_count = color_data_count;
1142 int scc_count = num_colors_with_bridges;
1144 SGEN_TV_GETTIME (curtime);
1149 cleanup_time = step_timer (&curtime);
1151 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",
1152 bridge_count, object_count, color_count,
1153 ignored_objects, scc_count, xref_count,
1154 cache_hits, cache_misses,
1155 setup_time / 10000.0f,
1156 tarjan_time / 10000.0f,
1157 scc_setup_time / 10000.0f,
1158 gather_xref_time / 10000.0f,
1159 xref_setup_time / 10000.0f,
1160 cleanup_time / 10000.0f);
1162 cache_hits = cache_misses = 0;
1163 ignored_objects = 0;
1164 bridge_processing_in_progress = FALSE;
1168 describe_pointer (GCObject *obj)
1170 // HashEntry *entry;
1173 for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) {
1174 if (obj == dyn_array_ptr_get (®istered_bridges, i)) {
1175 printf ("Pointer is a registered bridge object.\n");
1180 // entry = sgen_hash_table_lookup (&hash_table, obj);
1184 // printf ("Bridge hash table entry %p:\n", entry);
1185 // printf (" is bridge: %d\n", (int)entry->is_bridge);
1186 // printf (" is visited: %d\n", (int)entry->is_visited);
1190 sgen_tarjan_bridge_init (SgenBridgeProcessor *collector)
1192 collector->reset_data = reset_data;
1193 collector->processing_stw_step = processing_stw_step;
1194 collector->processing_build_callback_data = processing_build_callback_data;
1195 collector->processing_after_callback = processing_after_callback;
1196 collector->class_kind = class_kind;
1197 collector->register_finalized_object = register_finalized_object;
1198 collector->describe_pointer = describe_pointer;
1199 collector->enable_accounting = enable_accounting;
1200 // collector->set_dump_prefix = set_dump_prefix;
1202 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET, BUCKET_SIZE);
1203 g_assert (sizeof (ObjectBucket) <= BUCKET_SIZE);
1204 g_assert (sizeof (ColorBucket) <= BUCKET_SIZE);
1205 bridge_processor = collector;