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"
53 #include "utils/mono-compiler.h"
71 dyn_array_init (DynArray *da)
79 dyn_array_uninit (DynArray *da, int elem_size)
81 if (da->capacity <= 0)
84 sgen_free_internal_dynamic (da->data, elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA);
89 dyn_array_ensure_capacity (DynArray *da, int capacity, int elem_size)
91 int old_capacity = da->capacity;
94 if (capacity <= old_capacity)
97 if (da->capacity == 0)
99 while (capacity > da->capacity)
102 new_data = sgen_alloc_internal_dynamic (elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA, TRUE);
104 memcpy (new_data, da->data, elem_size * da->size);
105 sgen_free_internal_dynamic (da->data, elem_size * old_capacity, INTERNAL_MEM_BRIDGE_DATA);
111 dyn_array_add (DynArray *da, int elem_size)
115 dyn_array_ensure_capacity (da, da->size + 1, elem_size);
117 p = da->data + da->size * elem_size;
125 dyn_array_ptr_init (DynPtrArray *da)
127 dyn_array_init (&da->array);
131 dyn_array_ptr_uninit (DynPtrArray *da)
133 dyn_array_uninit (&da->array, sizeof (void*));
137 dyn_array_ptr_size (DynPtrArray *da)
139 return da->array.size;
143 dyn_array_ptr_set_size (DynPtrArray *da, int size)
145 da->array.size = size;
149 dyn_array_ptr_get (DynPtrArray *da, int x)
151 return ((void**)da->array.data)[x];
155 dyn_array_ptr_add (DynPtrArray *da, void *ptr)
157 void **p = dyn_array_add (&da->array, sizeof (void*));
161 #define dyn_array_ptr_push dyn_array_ptr_add
164 dyn_array_ptr_pop (DynPtrArray *da)
167 int size = da->array.size;
169 p = dyn_array_ptr_get (da, size - 1);
175 dyn_array_ptr_ensure_capacity (DynPtrArray *da, int capacity)
177 dyn_array_ensure_capacity (&da->array, capacity, sizeof (void*));
182 dyn_array_ptr_set_all (DynPtrArray *dst, DynPtrArray *src)
184 if (src->array.size > 0) {
185 dyn_array_ptr_ensure_capacity (dst, src->array.size);
186 memcpy (dst->array.data, src->array.data, src->array.size * sizeof (void*));
188 dst->array.size = src->array.size;
192 enable_accounting (void)
194 // bridge_accounting_enabled = TRUE;
195 // 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);
198 static MonoGCBridgeObjectKind
199 class_kind (MonoClass *class)
201 MonoGCBridgeObjectKind res = bridge_callbacks.bridge_class_kind (class);
203 /* If it's a bridge, nothing we can do about it. */
204 if (res == GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS || res == GC_BRIDGE_OPAQUE_BRIDGE_CLASS)
207 /* Non bridge classes with no pointers will never point to a bridge, so we can savely ignore them. */
208 if (!class->has_references) {
209 SGEN_LOG (6, "class %s is opaque\n", class->name);
210 return GC_BRIDGE_OPAQUE_CLASS;
213 /* Some arrays can be ignored */
214 if (class->rank == 1) {
215 MonoClass *elem_class = class->element_class;
217 /* FIXME the bridge check can be quite expensive, cache it at the class level. */
218 /* An array of a sealed type that is not a bridge will never get to a bridge */
219 if ((elem_class->flags & TYPE_ATTRIBUTE_SEALED) && !elem_class->has_references && !bridge_callbacks.bridge_class_kind (elem_class)) {
220 SGEN_LOG (6, "class %s is opaque\n", class->name);
221 return GC_BRIDGE_OPAQUE_CLASS;
225 return GC_BRIDGE_TRANSPARENT_CLASS;
228 //enable unsage logging
229 // #define DUMP_GRAPH 1
240 We can split this data structure in two, those with bridges and those without
243 DynPtrArray other_colors;
246 unsigned visited : 1;
251 MonoObject *obj; //XXX this can be eliminated.
259 unsigned is_bridge : 1;
260 unsigned obj_state : 2;
266 static DynPtrArray scan_stack, loop_stack, registered_bridges;
267 static DynPtrArray color_merge_array;
269 static int ignored_objects;
270 static int object_index;
271 static int num_colors_with_bridges;
272 static int xref_count;
274 static size_t setup_time, tarjan_time, scc_setup_time, gather_xref_time, xref_setup_time, cleanup_time;
275 static SgenBridgeProcessor *bridge_processor;
277 #define BUCKET_SIZE 8184
280 #define NUM_SCAN_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ScanData))
282 typedef struct _ObjectBucket ObjectBucket;
283 struct _ObjectBucket {
286 ScanData data [NUM_SCAN_ENTRIES];
289 static ObjectBucket *root_object_bucket;
290 static ObjectBucket *cur_object_bucket;
291 static int object_data_count;
294 new_object_bucket (void)
296 ObjectBucket *res = sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
297 res->next_data = &res->data [0];
302 object_alloc_init (void)
304 root_object_bucket = cur_object_bucket = new_object_bucket ();
308 alloc_object_data (void)
313 /* next_data points to the first free entry */
314 res = cur_object_bucket->next_data;
315 if (res >= &cur_object_bucket->data [NUM_SCAN_ENTRIES]) {
316 ObjectBucket *b = new_object_bucket ();
317 cur_object_bucket->next = b;
318 cur_object_bucket = b;
321 cur_object_bucket->next_data = res + 1;
327 free_object_buckets (void)
329 ObjectBucket *cur = root_object_bucket;
331 object_data_count = 0;
334 ObjectBucket *tmp = cur->next;
335 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
339 root_object_bucket = cur_object_bucket = NULL;
343 #define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData))
345 typedef struct _ColorBucket ColorBucket;
346 struct _ColorBucket {
348 ColorData *next_data;
349 ColorData data [NUM_COLOR_ENTRIES];
352 static ColorBucket *root_color_bucket;
353 static ColorBucket *cur_color_bucket;
354 static int color_data_count;
358 new_color_bucket (void)
360 ColorBucket *res = sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
361 res->next_data = &res->data [0];
366 color_alloc_init (void)
368 root_color_bucket = cur_color_bucket = new_color_bucket ();
372 alloc_color_data (void)
377 /* next_data points to the first free entry */
378 res = cur_color_bucket->next_data;
379 if (res >= &cur_color_bucket->data [NUM_COLOR_ENTRIES]) {
380 ColorBucket *b = new_color_bucket ();
381 cur_color_bucket->next = b;
382 cur_color_bucket = b;
385 cur_color_bucket->next_data = res + 1;
391 free_color_buckets (void)
393 color_data_count = 0;
395 ColorBucket *cur, *tmp;
397 for (cur = root_color_bucket; cur; cur = tmp) {
399 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
400 dyn_array_ptr_uninit (&cd->other_colors);
401 dyn_array_ptr_uninit (&cd->bridges);
404 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
406 root_color_bucket = cur_color_bucket = NULL;
411 create_data (MonoObject *obj)
413 mword *o = (mword*)obj;
414 ScanData *res = alloc_object_data ();
417 res->index = res->low_index = -1;
418 res->obj_state = o [0] & SGEN_VTABLE_BITS_MASK;
419 res->lock_word = o [1];
421 o [0] |= SGEN_VTABLE_BITS_MASK;
427 find_data (MonoObject *obj)
430 mword *o = (mword*)obj;
431 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
432 a = (ScanData*)o [1];
437 clear_after_processing (void)
441 for (cur = root_object_bucket; cur; cur = cur->next) {
443 for (sd = &cur->data [0]; sd < cur->next_data; ++sd) {
444 mword *o = (mword*)sd->obj;
445 o [0] &= ~SGEN_VTABLE_BITS_MASK;
446 o [0] |= sd->obj_state;
447 o [1] = sd->lock_word;
453 bridge_object_forward (MonoObject *obj)
456 mword *o = (mword*)obj;
457 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
460 fwd = SGEN_OBJECT_IS_FORWARDED (obj);
461 return fwd ? fwd : obj;
465 safe_name_bridge (MonoObject *obj)
467 MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
468 return vt->klass->name;
472 find_or_create_data (MonoObject *obj)
474 ScanData *entry = find_data (obj);
476 entry = create_data (obj);
488 We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
490 The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline,
491 making the extra space pretty much free.
493 The cost between 32 and 128 itens is minimal too, it's mostly a fixed, setup cost.
495 Memory wise, 4/32 takes 512 and 8/128 takes 8k, so it's quite reasonable.
498 #define ELEMENTS_PER_BUCKET 8
499 #define COLOR_CACHE_SIZE 128
500 static HashEntry merge_cache [COLOR_CACHE_SIZE][ELEMENTS_PER_BUCKET];
503 mix_hash (size_t hash)
505 return ((hash * 215497) >> 16) ^ (hash * 1823231) + hash;
511 memset (merge_cache, 0, sizeof (merge_cache));
516 dyn_array_ptr_contains (DynPtrArray *da, void *x)
519 for (i = 0; i < dyn_array_ptr_size (da); ++i)
520 if (dyn_array_ptr_get (da, i) == x)
526 match_colors (DynPtrArray *a, DynPtrArray *b)
529 if (dyn_array_ptr_size (a) != dyn_array_ptr_size (b))
532 for (i = 0; i < dyn_array_ptr_size (a); ++i) {
533 if (!dyn_array_ptr_contains (b, dyn_array_ptr_get (a, i)))
539 static int cache_hits, cache_misses;
542 find_in_cache (int *insert_index)
545 int i, hash, size, index;
547 size = dyn_array_ptr_size (&color_merge_array);
548 /* Cache checking is very ineficient with a lot of elements*/
553 for (i = 0 ; i < size; ++i)
554 hash += mix_hash ((size_t)dyn_array_ptr_get (&color_merge_array, i));
558 index = hash & (COLOR_CACHE_SIZE - 1);
559 bucket = merge_cache [index];
560 for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) {
561 if (bucket [i].hash != hash)
563 if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) {
565 return bucket [i].color;
569 //move elements to the back
570 for (i = ELEMENTS_PER_BUCKET - 1; i > 0; --i)
571 bucket [i] = bucket [i - 1];
573 *insert_index = index;
574 bucket [0].hash = hash;
579 new_color (gboolean force_new)
583 /* XXX Try to find an equal one and return it */
585 cd = find_in_cache (&i);
590 cd = alloc_color_data ();
592 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
593 /* if i >= 0, it means we prepared a given slot to receive the new color */
595 merge_cache [i][0].color = cd;
602 register_bridge_object (MonoObject *obj)
604 create_data (obj)->is_bridge = TRUE;
608 is_opaque_object (MonoObject *obj)
610 MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
611 if ((vt->gc_bits & SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) {
612 SGEN_LOG (6, "ignoring %s\n", vt->klass->name);
620 push_object (MonoObject *obj)
623 obj = bridge_object_forward (obj);
626 printf ("\t= pushing %p %s -> ", obj, safe_name_bridge (obj));
629 /* Object types we can ignore */
630 if (is_opaque_object (obj)) {
637 data = find_data (obj);
639 /* Already marked - XXX must be done this way as the bridge themselves are alive. */
640 if (data && data->state != INITIAL) {
642 printf ("already marked\n");
647 /* We only care about dead objects */
648 if (!data && sgen_object_is_live (obj)) {
656 printf ("pushed!\n");
660 data = create_data (obj);
661 g_assert (data->state == INITIAL);
662 g_assert (data->index == -1);
663 dyn_array_ptr_push (&scan_stack, data);
667 #define HANDLE_PTR(ptr,obj) do { \
668 MonoObject *dst = (MonoObject*)*(ptr); \
669 if (dst) push_object (dst); \
673 push_all (ScanData *data)
675 MonoObject *obj = data->obj;
676 char *start = (char*)obj;
679 printf ("**scanning %p %s\n", obj, safe_name_bridge (obj));
682 #include "sgen-scan-object.h"
687 compute_low_index (ScanData *data, MonoObject *obj)
691 obj = bridge_object_forward (obj);
693 other = find_data (obj);
696 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);
701 g_assert (other->state != INITIAL);
703 if ((other->state == SCANNED || other->state == FINISHED_ON_STACK) && data->low_index > other->low_index)
704 data->low_index = other->low_index;
706 /* Compute the low color */
707 if (other->color == NULL)
712 dyn_array_ptr_add (&color_merge_array, other->color);
718 #define HANDLE_PTR(ptr,obj) do { \
719 MonoObject *dst = (MonoObject*)*(ptr); \
720 if (dst) compute_low_index (data, dst); \
724 compute_low (ScanData *data)
726 MonoObject *obj = data->obj;
727 char *start = (char*)obj;
729 #include "sgen-scan-object.h"
735 ColorData *color = NULL;
736 int size = dyn_array_ptr_size (&color_merge_array);
740 else if (size == 1) {
741 color = dyn_array_ptr_get (&color_merge_array, 0);
743 color = new_color (FALSE);
749 create_scc (ScanData *data)
752 gboolean found = FALSE;
753 gboolean found_bridge = FALSE;
754 ColorData *color_data = NULL;
756 for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) {
757 ScanData *other = dyn_array_ptr_get (&loop_stack, i);
758 found_bridge |= other->is_bridge;
759 if (found_bridge || other == data)
764 printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data->obj), data->obj, found_bridge);
765 printf ("\tpoints-to-colors: ");
766 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i)
767 printf ("%p ", dyn_array_ptr_get (&color_merge_array, i));
770 printf ("loop stack: ");
771 for (i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) {
772 ScanData *other = dyn_array_ptr_get (&loop_stack, i);
773 printf ("(%d/%d)", other->index, other->low_index);
779 color_data = new_color (TRUE);
780 ++num_colors_with_bridges;
782 color_data = reduce_color ();
785 while (dyn_array_ptr_size (&loop_stack) > 0) {
786 ScanData *other = dyn_array_ptr_pop (&loop_stack);
789 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);
792 other->color = color_data;
793 switch (other->state) {
794 case FINISHED_ON_STACK:
795 other->state = FINISHED_OFF_STACK;
797 case FINISHED_OFF_STACK:
800 g_error ("Invalid state when building SCC %d", other->state);
803 if (other->is_bridge)
804 dyn_array_ptr_add (&color_data->bridges, other->obj);
813 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) {
814 ColorData *cd = dyn_array_ptr_get (&color_merge_array, i);
815 g_assert (cd->visited);
818 dyn_array_ptr_set_size (&color_merge_array, 0);
819 found_bridge = FALSE;
825 g_assert (dyn_array_ptr_size (&scan_stack) == 1);
826 g_assert (dyn_array_ptr_size (&loop_stack) == 0);
828 dyn_array_ptr_set_size (&color_merge_array, 0);
830 while (dyn_array_ptr_size (&scan_stack) > 0) {
831 ScanData *data = dyn_array_ptr_pop (&scan_stack);
834 * Ignore finished objects on stack, they happen due to loops. For example:
840 * We start scanning from A and push C before B. So, after the first iteration, the scan stack will have: A C B.
841 * We then visit B, which will find C in its initial state and push again.
842 * Finally after finish with C and B, the stack will be left with "A C" and at this point C should be ignored.
844 * The above explains FINISHED_ON_STACK, to explain FINISHED_OFF_STACK, consider if the root was D, which pointed
845 * to A and C. A is processed first, leaving C on stack after that in the mentioned state.
847 if (data->state == FINISHED_ON_STACK || data->state == FINISHED_OFF_STACK)
850 if (data->state == INITIAL) {
851 g_assert (data->index == -1);
852 g_assert (data->low_index == -1);
854 data->state = SCANNED;
855 data->low_index = data->index = object_index++;
856 dyn_array_ptr_push (&scan_stack, data);
857 dyn_array_ptr_push (&loop_stack, data);
860 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
865 g_assert (data->state == SCANNED);
866 data->state = FINISHED_ON_STACK;
869 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);
872 /* Compute low index */
876 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);
879 if (data->index == data->low_index)
886 register_finalized_object (MonoObject *obj)
888 g_assert (sgen_need_bridge_processing ());
889 dyn_array_ptr_push (®istered_bridges, obj);
895 dyn_array_ptr_set_size (®istered_bridges, 0);
901 dyn_array_ptr_set_size (&scan_stack, 0);
902 dyn_array_ptr_set_size (&loop_stack, 0);
903 dyn_array_ptr_set_size (®istered_bridges, 0);
904 free_object_buckets ();
905 free_color_buckets ();
908 num_colors_with_bridges = 0;
912 dump_color_table (const char *why, gboolean do_index)
916 printf ("colors%s:\n", why);
918 for (cur = root_color_bucket; cur; cur = cur->next, ++i) {
920 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
922 printf ("\t%d(%d):", i, cd->api_index);
924 printf ("\t%d: ", i);
925 for (j = 0; j < dyn_array_ptr_size (&cd->other_colors); ++j) {
926 printf ("%p ", dyn_array_ptr_get (&cd->other_colors, j));
928 if (dyn_array_ptr_size (&cd->bridges)) {
929 printf (" bridges: ");
930 for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) {
931 MonoObject *obj = dyn_array_ptr_get (&cd->bridges, j);
932 ScanData *data = find_or_create_data (obj);
933 printf ("%d ", data->index);
943 step_timer (gint64 *timer)
945 gint64 curtime, diff;
947 SGEN_TV_GETTIME (curtime);
948 diff = SGEN_TV_ELAPSED (*timer, curtime);
953 processing_stw_step (void)
959 if (!dyn_array_ptr_size (®istered_bridges))
962 #if defined (DUMP_GRAPH)
963 printf ("-----------------\n");
966 * bridge_processing_in_progress must be set with the world
967 * stopped. If not there would be race conditions.
969 bridge_processing_in_progress = TRUE;
971 SGEN_TV_GETTIME (curtime);
973 object_alloc_init ();
976 bridge_count = dyn_array_ptr_size (®istered_bridges);
977 for (i = 0; i < bridge_count ; ++i)
978 register_bridge_object (dyn_array_ptr_get (®istered_bridges, i));
980 setup_time = step_timer (&curtime);
982 for (i = 0; i < bridge_count; ++i) {
983 ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i));
984 if (sd->state == INITIAL) {
985 dyn_array_ptr_push (&scan_stack, sd);
988 g_assert (sd->state == FINISHED_OFF_STACK);
992 tarjan_time = step_timer (&curtime);
994 #if defined (DUMP_GRAPH)
995 printf ("----summary----\n");
996 printf ("bridges:\n");
997 for (i = 0; i < bridge_count; ++i) {
998 ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i));
999 printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color);
1002 dump_color_table (" after tarjan", FALSE);
1005 clear_after_processing ();
1010 gather_xrefs (ColorData *color)
1013 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
1014 ColorData *src = dyn_array_ptr_get (&color->other_colors, i);
1017 src->visited = TRUE;
1018 if (dyn_array_ptr_size (&src->bridges))
1019 dyn_array_ptr_add (&color_merge_array, src);
1026 reset_xrefs (ColorData *color)
1029 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
1030 ColorData *src = dyn_array_ptr_get (&color->other_colors, i);
1033 src->visited = FALSE;
1034 if (!dyn_array_ptr_size (&src->bridges))
1040 is_bridge_object_alive (MonoObject *obj, void *data)
1042 SgenHashTable *table = data;
1043 unsigned char *value = sgen_hash_table_lookup (table, obj);
1050 processing_build_callback_data (int generation)
1053 MonoGCBridgeSCC **api_sccs;
1054 MonoGCBridgeXRef *api_xrefs;
1058 g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0);
1059 g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs);
1061 if (!dyn_array_ptr_size (®istered_bridges))
1064 SGEN_TV_GETTIME (curtime);
1066 /*create API objects */
1068 #if defined (DUMP_GRAPH)
1069 printf ("***** API *****\n");
1070 printf ("number of SCCs %d\n", num_colors_with_bridges);
1073 /* This is a straightforward translation from colors to the bridge callback format. */
1074 api_sccs = sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_colors_with_bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1075 api_index = xref_count = 0;
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 api_sccs [api_index] = sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1085 api_sccs [api_index]->is_alive = FALSE;
1086 api_sccs [api_index]->num_objs = bridges;
1088 cd->api_index = api_index;
1090 for (j = 0; j < bridges; ++j)
1091 api_sccs [api_index]->objs [j] = dyn_array_ptr_get (&cd->bridges, j);
1096 scc_setup_time = step_timer (&curtime);
1098 for (cur = root_color_bucket; cur; cur = cur->next) {
1100 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1101 int bridges = dyn_array_ptr_size (&cd->bridges);
1105 dyn_array_ptr_set_size (&color_merge_array, 0);
1108 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
1109 xref_count += dyn_array_ptr_size (&cd->other_colors);
1113 gather_xref_time = step_timer (&curtime);
1115 #if defined (DUMP_GRAPH)
1116 printf ("TOTAL XREFS %d\n", xref_count);
1117 dump_color_table (" after xref pass", TRUE);
1120 api_xrefs = sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1122 for (cur = root_color_bucket; cur; cur = cur->next) {
1124 for (src = &cur->data [0]; src < cur->next_data; ++src) {
1125 int bridges = dyn_array_ptr_size (&src->bridges);
1129 for (j = 0; j < dyn_array_ptr_size (&src->other_colors); ++j) {
1130 ColorData *dest = dyn_array_ptr_get (&src->other_colors, j);
1131 g_assert (dyn_array_ptr_size (&dest->bridges)); /* We flattened the color graph, so this must never happen. */
1133 api_xrefs [api_index].src_scc_index = src->api_index;
1134 api_xrefs [api_index].dst_scc_index = dest->api_index;
1140 g_assert (xref_count == api_index);
1141 xref_setup_time = step_timer (&curtime);
1143 #if defined (DUMP_GRAPH)
1144 printf ("---xrefs:\n");
1145 for (i = 0; i < xref_count; ++i)
1146 printf ("\t%d -> %d\n", api_xrefs [i].src_scc_index, api_xrefs [i].dst_scc_index);
1149 //FIXME move half of the cleanup to before the bridge callback?
1150 bridge_processor->num_sccs = num_colors_with_bridges;
1151 bridge_processor->api_sccs = api_sccs;
1152 bridge_processor->num_xrefs = xref_count;
1153 bridge_processor->api_xrefs = api_xrefs;
1157 processing_after_callback (int generation)
1160 int bridge_count = dyn_array_ptr_size (®istered_bridges);
1161 int object_count = object_data_count;
1162 int color_count = color_data_count;
1163 int scc_count = num_colors_with_bridges;
1165 SGEN_TV_GETTIME (curtime);
1170 cleanup_time = step_timer (&curtime);
1172 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",
1173 bridge_count, object_count, color_count,
1174 ignored_objects, scc_count, xref_count,
1175 cache_hits, cache_misses,
1176 setup_time / 10000.0f,
1177 tarjan_time / 10000.0f,
1178 scc_setup_time / 10000.0f,
1179 gather_xref_time / 10000.0f,
1180 xref_setup_time / 10000.0f,
1181 cleanup_time / 10000.0f);
1183 cache_hits = cache_misses = 0;
1184 ignored_objects = 0;
1185 bridge_processing_in_progress = FALSE;
1189 describe_pointer (MonoObject *obj)
1191 // HashEntry *entry;
1194 for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) {
1195 if (obj == dyn_array_ptr_get (®istered_bridges, i)) {
1196 printf ("Pointer is a registered bridge object.\n");
1201 // entry = sgen_hash_table_lookup (&hash_table, obj);
1205 // printf ("Bridge hash table entry %p:\n", entry);
1206 // printf (" is bridge: %d\n", (int)entry->is_bridge);
1207 // printf (" is visited: %d\n", (int)entry->is_visited);
1211 sgen_tarjan_bridge_init (SgenBridgeProcessor *collector)
1213 collector->reset_data = reset_data;
1214 collector->processing_stw_step = processing_stw_step;
1215 collector->processing_build_callback_data = processing_build_callback_data;
1216 collector->processing_after_callback = processing_after_callback;
1217 collector->class_kind = class_kind;
1218 collector->register_finalized_object = register_finalized_object;
1219 collector->describe_pointer = describe_pointer;
1220 collector->enable_accounting = enable_accounting;
1221 // collector->set_dump_prefix = set_dump_prefix;
1223 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET, BUCKET_SIZE);
1224 g_assert (sizeof (ObjectBucket) <= BUCKET_SIZE);
1225 g_assert (sizeof (ColorBucket) <= BUCKET_SIZE);
1226 bridge_processor = collector;