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
84 /* Used in bridgeless_color_is_heavy:
85 * The idea here is as long as the reference fanin and fanout on a node are both 2 or greater, then
86 * removing that node will result in a net increase in edge count. So the question is which node
87 * removals are counterproductive (i.e., how many edges saved balances out one node added).
88 * The number of edges saved by preserving a node is (fanin*fanout - fanin - fanout).
90 * With all that in mind:
92 * - HEAVY_REFS_MIN is the number that *both* fanin and fanout must meet to preserve the node.
93 * - HEAVY_COMBINED_REFS_MIN is the number (fanin*fanout) must meet to preserve the node.
95 * Note HEAVY_COMBINED_REFS_MIN must be <= 2*INCOMING_COLORS_MAX, or we won't know the true fanin.
98 #define HEAVY_REFS_MIN 2
99 #define HEAVY_COMBINED_REFS_MIN 60
101 /* Used in ColorData:
102 * The higher INCOMING_COLORS_BITS is the higher HEAVY_COMBINED_REFS_MIN can be (see above).
103 * However, API_INDEX_BITS + INCOMING_COLORS_BITS must be equal to 31, and if API_INDEX_BITS is too
104 * low then terrible things will happen if too many colors are generated. (The number of colors we
105 * will ever attempt to generate is currently naturally limited by the JNI GREF limit.)
108 #define API_INDEX_BITS 26
109 #define INCOMING_COLORS_BITS 5
111 #define API_INDEX_MAX ((1<<API_INDEX_BITS)-1)
112 #define INCOMING_COLORS_MAX ((1<<INCOMING_COLORS_BITS)-1)
124 We can split this data structure in two, those with bridges and those without
125 (and only bridgeless need to record incoming_colors)
128 // Colors (ColorDatas) linked to by objects with this color
129 DynPtrArray other_colors;
130 // Bridge objects (GCObjects) held by objects with this color
132 // Index of this color's MonoGCBridgeSCC in the array passed to the client (or -1 for none)
133 signed api_index : API_INDEX_BITS;
134 // Count of colors that list this color in their other_colors
135 unsigned incoming_colors : INCOMING_COLORS_BITS;
136 unsigned visited : 1;
139 // Represents one managed object. Equivalent of new/old bridge "HashEntry"
140 typedef struct _ScanData {
141 // FIXME this can be eliminated; if we have a ScanData we generally looked it up from its GCObject
143 // We use the sgen lock_word in GCObject to store a pointer to the ScanData. Cache the original here to restore later:
147 // Tarjan algorithm index (order visited)
149 // Tarjan index of lowest-index object known reachable from here
150 signed low_index : 27;
152 // See "ScanData state" enum above
154 unsigned is_bridge : 1;
155 // Similar to lock_word, we use these bits in the GCObject as scratch space then restore them when done
156 unsigned obj_state : 2;
159 /* Should color be made visible to client even though it has no bridges?
160 * True if we predict the number of reduced edges to be enough to justify the extra node.
162 static inline gboolean
163 bridgeless_color_is_heavy (ColorData *data) {
164 int fanin = data->incoming_colors;
165 int fanout = dyn_array_ptr_size (&data->other_colors);
166 return fanin > HEAVY_REFS_MIN && fanout > HEAVY_REFS_MIN
167 && fanin*fanout >= HEAVY_COMBINED_REFS_MIN;
170 // Should color be made visible to client?
171 static inline gboolean
172 color_visible_to_client (ColorData *data) {
173 return dyn_array_ptr_size (&data->bridges) || bridgeless_color_is_heavy (data);
176 // Stacks of ScanData objects used for tarjan algorithm.
177 // The Tarjan algorithm is normally defined recursively; here scan_stack simulates the call stack of a recursive algorithm,
178 // and loop_stack is the stack structure used by the algorithm itself.
179 static DynPtrArray scan_stack, loop_stack;
181 // GCObjects on which register_finalized_object has been called
182 static DynPtrArray registered_bridges;
184 // As we traverse the graph, which ColorData objects are accessible from our current position?
185 static DynPtrArray color_merge_array;
186 // Running hash of the contents of the color_merge_array.
187 static int color_merge_array_hash;
189 static void color_merge_array_empty ()
191 dyn_array_ptr_empty (&color_merge_array);
192 color_merge_array_hash = 0;
195 static int ignored_objects;
196 static int object_index;
197 static int num_colors_with_bridges;
199 static int xref_count;
201 static size_t setup_time, tarjan_time, scc_setup_time, gather_xref_time, xref_setup_time, cleanup_time;
202 static SgenBridgeProcessor *bridge_processor;
204 #define BUCKET_SIZE 8184
207 #define NUM_SCAN_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ScanData))
209 typedef struct _ObjectBucket ObjectBucket;
210 struct _ObjectBucket {
213 ScanData data [NUM_SCAN_ENTRIES];
216 static ObjectBucket *root_object_bucket;
217 static ObjectBucket *cur_object_bucket;
218 static int object_data_count;
220 // Arenas to allocate ScanData from
222 new_object_bucket (void)
224 ObjectBucket *res = (ObjectBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
225 res->next_data = &res->data [0];
230 object_alloc_init (void)
232 root_object_bucket = cur_object_bucket = new_object_bucket ();
236 alloc_object_data (void)
241 /* next_data points to the first free entry */
242 res = cur_object_bucket->next_data;
243 if (res >= &cur_object_bucket->data [NUM_SCAN_ENTRIES]) {
244 ObjectBucket *b = new_object_bucket ();
245 cur_object_bucket->next = b;
246 cur_object_bucket = b;
249 cur_object_bucket->next_data = res + 1;
255 free_object_buckets (void)
257 ObjectBucket *cur = root_object_bucket;
259 object_data_count = 0;
262 ObjectBucket *tmp = cur->next;
263 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
267 root_object_bucket = cur_object_bucket = NULL;
271 #define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData))
273 // Arenas for ColorDatas, same as ObjectBucket except items-per-bucket differs
274 typedef struct _ColorBucket ColorBucket;
275 struct _ColorBucket {
277 ColorData *next_data;
278 ColorData data [NUM_COLOR_ENTRIES];
281 static ColorBucket *root_color_bucket;
282 static ColorBucket *cur_color_bucket;
283 static int color_data_count;
287 new_color_bucket (void)
289 ColorBucket *res = (ColorBucket *)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET);
290 res->next_data = &res->data [0];
295 color_alloc_init (void)
297 root_color_bucket = cur_color_bucket = new_color_bucket ();
301 alloc_color_data (void)
306 /* next_data points to the first free entry */
307 res = cur_color_bucket->next_data;
308 if (res >= &cur_color_bucket->data [NUM_COLOR_ENTRIES]) {
309 ColorBucket *b = new_color_bucket ();
310 cur_color_bucket->next = b;
311 cur_color_bucket = b;
314 cur_color_bucket->next_data = res + 1;
320 free_color_buckets (void)
322 ColorBucket *cur, *tmp;
324 color_data_count = 0;
326 for (cur = root_color_bucket; cur; cur = tmp) {
328 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
329 dyn_array_ptr_uninit (&cd->other_colors);
330 dyn_array_ptr_uninit (&cd->bridges);
333 sgen_free_internal (cur, INTERNAL_MEM_TARJAN_OBJ_BUCKET);
335 root_color_bucket = cur_color_bucket = NULL;
340 create_data (GCObject *obj)
342 mword *o = (mword*)obj;
343 ScanData *res = alloc_object_data ();
346 res->index = res->low_index = -1;
347 res->state = INITIAL;
348 res->is_bridge = FALSE;
349 res->obj_state = o [0] & SGEN_VTABLE_BITS_MASK;
350 res->lock_word = o [1];
352 o [0] |= SGEN_VTABLE_BITS_MASK;
358 find_data (GCObject *obj)
361 mword *o = (mword*)obj;
362 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
363 a = (ScanData*)o [1];
368 clear_after_processing (void)
372 for (cur = root_object_bucket; cur; cur = cur->next) {
374 for (sd = &cur->data [0]; sd < cur->next_data; ++sd) {
375 mword *o = (mword*)sd->obj;
376 o [0] &= ~SGEN_VTABLE_BITS_MASK;
377 o [0] |= sd->obj_state;
378 o [1] = sd->lock_word;
384 bridge_object_forward (GCObject *obj)
387 mword *o = (mword*)obj;
388 if ((o [0] & SGEN_VTABLE_BITS_MASK) == SGEN_VTABLE_BITS_MASK)
391 fwd = SGEN_OBJECT_IS_FORWARDED (obj);
392 return fwd ? fwd : obj;
397 safe_name_bridge (GCObject *obj)
399 GCVTable vt = SGEN_LOAD_VTABLE (obj);
400 return vt->klass->name;
404 find_or_create_data (GCObject *obj)
406 ScanData *entry = find_data (obj);
408 entry = create_data (obj);
420 The merge cache maps an ordered list of ColorDatas [the color_merge_array] to a single ColorData.
422 About cache bucket tuning: We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
424 The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline,
425 making the extra space pretty much free.
427 The cost between 32 and 128 itens is minimal too, it's mostly a fixed, setup cost.
429 Memory wise, 4/32 takes 512 and 8/128 takes 8k, so it's quite reasonable.
432 #define ELEMENTS_PER_BUCKET 8
433 #define COLOR_CACHE_SIZE 128
434 static HashEntry merge_cache [COLOR_CACHE_SIZE][ELEMENTS_PER_BUCKET];
436 // Ensure the hash function does not produce the same values on every run
437 static int hash_perturb;
440 mix_hash (size_t hash)
442 return (int)(((hash * 215497) >> 16) ^ ((hash * 1823231) + hash) ^ hash_perturb);
448 memset (merge_cache, 0, sizeof (merge_cache));
454 dyn_array_ptr_contains (DynPtrArray *da, void *x)
457 for (i = 0; i < dyn_array_ptr_size (da); ++i)
458 if (dyn_array_ptr_get (da, i) == x)
464 match_colors_estimate (DynPtrArray *a, DynPtrArray *b)
466 return dyn_array_ptr_size (a) == dyn_array_ptr_size (b);
471 match_colors (DynPtrArray *a, DynPtrArray *b)
474 if (dyn_array_ptr_size (a) != dyn_array_ptr_size (b))
477 for (i = 0; i < dyn_array_ptr_size (a); ++i) {
478 if (!dyn_array_ptr_contains (b, dyn_array_ptr_get (a, i)))
484 static int cache_hits, cache_semihits, cache_misses;
486 // The cache contains only non-bridged colors.
488 find_in_cache (int *insert_index)
491 int i, hash, size, index;
493 size = dyn_array_ptr_size (&color_merge_array);
495 hash = color_merge_array_hash;
496 if (!hash) // 0 is used to indicate an empty bucket entry
499 index = hash & (COLOR_CACHE_SIZE - 1);
500 bucket = merge_cache [index];
501 for (i = 0; i < ELEMENTS_PER_BUCKET; ++i) {
502 if (bucket [i].hash != hash)
505 /* Color equality checking is very expensive with a lot of elements, so when there are many
506 * elements we switch to a cheap comparison method which allows false positives. When false
507 * positives occur the worst that can happen is two items will be inappropriately merged
508 * and memory will be retained longer than it should be. We assume that will correct itself
509 * on the next GC (the hash_perturb mechanism increases the probability of this).
512 if (match_colors_estimate (&bucket [i].color->other_colors, &color_merge_array)) {
514 return bucket [i].color;
517 if (match_colors (&bucket [i].color->other_colors, &color_merge_array)) {
519 return bucket [i].color;
524 //move elements to the back
525 for (i = ELEMENTS_PER_BUCKET - 1; i > 0; --i)
526 bucket [i] = bucket [i - 1];
528 *insert_index = index;
529 bucket [0].hash = hash;
533 // A color is needed for an SCC. If the SCC has bridges, the color MUST be newly allocated.
534 // If the SCC lacks bridges, the allocator MAY use the cache to merge it with an existing one.
536 new_color (gboolean has_bridges)
540 /* XXX Try to find an equal one and return it */
542 cd = find_in_cache (&cacheSlot);
547 cd = alloc_color_data ();
549 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
552 for (int i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) {
553 ColorData *points_to = (ColorData *)dyn_array_ptr_get (&color_merge_array, i);
554 points_to->incoming_colors = MIN (points_to->incoming_colors + 1, INCOMING_COLORS_MAX);
557 /* if cacheSlot >= 0, it means we prepared a given slot to receive the new color */
559 merge_cache [cacheSlot][0].color = cd;
566 register_bridge_object (GCObject *obj)
568 create_data (obj)->is_bridge = TRUE;
572 is_opaque_object (GCObject *obj)
574 MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
575 if ((vt->gc_bits & SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT) {
576 SGEN_LOG (6, "ignoring %s\n", vt->klass->name);
583 // Called during DFS; visits one child. If it is a candidate to be scanned, pushes it to the stacks.
585 push_object (GCObject *obj)
588 obj = bridge_object_forward (obj);
591 printf ("\t= pushing %p %s -> ", obj, safe_name_bridge (obj));
594 /* Object types we can ignore */
595 if (is_opaque_object (obj)) {
602 data = find_data (obj);
604 /* Already marked - XXX must be done this way as the bridge themselves are alive. */
605 if (data && data->state != INITIAL) {
607 printf ("already marked\n");
612 /* We only care about dead objects */
613 if (!data && sgen_object_is_live (obj)) {
621 printf ("pushed!\n");
625 data = create_data (obj);
626 g_assert (data->state == INITIAL);
627 g_assert (data->index == -1);
628 dyn_array_ptr_push (&scan_stack, data);
632 #define HANDLE_PTR(ptr,obj) do { \
633 GCObject *dst = (GCObject*)*(ptr); \
634 if (dst) push_object (dst); \
637 // dfs () function's queue-children-of-object operation.
639 push_all (ScanData *data)
641 GCObject *obj = data->obj;
642 char *start = (char*)obj;
643 mword desc = sgen_obj_get_descriptor_safe (obj);
646 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
649 #include "sgen/sgen-scan-object.h"
654 compute_low_index (ScanData *data, GCObject *obj)
659 obj = bridge_object_forward (obj);
660 other = find_data (obj);
663 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);
668 g_assert (other->state != INITIAL);
670 if ((other->state == SCANNED || other->state == FINISHED_ON_STACK) && data->low_index > other->low_index)
671 data->low_index = other->low_index;
673 /* Compute the low color */
674 if (other->color == NULL)
679 color_merge_array_hash += mix_hash ((size_t) other->color);
680 dyn_array_ptr_add (&color_merge_array, other->color);
686 #define HANDLE_PTR(ptr,obj) do { \
687 GCObject *dst = (GCObject*)*(ptr); \
688 if (dst) compute_low_index (data, dst); \
692 compute_low (ScanData *data)
694 GCObject *obj = data->obj;
695 char *start = (char*)obj;
696 mword desc = sgen_obj_get_descriptor_safe (obj);
698 #include "sgen/sgen-scan-object.h"
701 // A non-bridged object needs a single color describing the current merge array.
705 ColorData *color = NULL;
706 int size = dyn_array_ptr_size (&color_merge_array);
708 // Merge array is empty-- this SCC points to no bridged colors.
709 // This SCC can be ignored completely.
713 // Merge array has one item-- this SCC points to a single bridged color.
714 // This SCC can be forwarded to the pointed-to color.
715 else if (size == 1) {
716 color = (ColorData *)dyn_array_ptr_get (&color_merge_array, 0);
718 // This SCC gets to talk to the color allocator.
720 color = new_color (FALSE);
726 create_scc (ScanData *data)
729 gboolean found = FALSE;
730 gboolean found_bridge = FALSE;
731 ColorData *color_data = NULL;
733 for (i = dyn_array_ptr_size (&loop_stack) - 1; i >= 0; --i) {
734 ScanData *other = (ScanData *)dyn_array_ptr_get (&loop_stack, i);
735 found_bridge |= other->is_bridge;
736 if (found_bridge || other == data)
741 printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data->obj), data->obj, found_bridge);
742 printf ("\tpoints-to-colors: ");
743 for (int i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i)
744 printf ("%p ", dyn_array_ptr_get (&color_merge_array, i));
747 printf ("loop stack: ");
748 for (int i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) {
749 ScanData *other = dyn_array_ptr_get (&loop_stack, i);
750 printf ("(%d/%d)", other->index, other->low_index);
756 color_data = new_color (TRUE);
757 ++num_colors_with_bridges;
759 color_data = reduce_color ();
762 while (dyn_array_ptr_size (&loop_stack) > 0) {
763 ScanData *other = (ScanData *)dyn_array_ptr_pop (&loop_stack);
766 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);
769 other->color = color_data;
770 switch (other->state) {
771 case FINISHED_ON_STACK:
772 other->state = FINISHED_OFF_STACK;
774 case FINISHED_OFF_STACK:
777 g_error ("Invalid state when building SCC %d", other->state);
780 if (other->is_bridge)
781 dyn_array_ptr_add (&color_data->bridges, other->obj);
790 for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) {
791 ColorData *cd = (ColorData *)dyn_array_ptr_get (&color_merge_array, i);
792 g_assert (cd->visited);
795 color_merge_array_empty ();
796 found_bridge = FALSE;
802 g_assert (dyn_array_ptr_size (&scan_stack) == 1);
803 g_assert (dyn_array_ptr_size (&loop_stack) == 0);
805 color_merge_array_empty ();
807 while (dyn_array_ptr_size (&scan_stack) > 0) {
808 ScanData *data = (ScanData *)dyn_array_ptr_pop (&scan_stack);
811 * Ignore finished objects on stack, they happen due to loops. For example:
817 * We start scanning from A and push C before B. So, after the first iteration, the scan stack will have: A C B.
818 * We then visit B, which will find C in its initial state and push again.
819 * Finally after finish with C and B, the stack will be left with "A C" and at this point C should be ignored.
821 * The above explains FINISHED_ON_STACK, to explain FINISHED_OFF_STACK, consider if the root was D, which pointed
822 * to A and C. A is processed first, leaving C on stack after that in the mentioned state.
824 if (data->state == FINISHED_ON_STACK || data->state == FINISHED_OFF_STACK)
827 if (data->state == INITIAL) {
828 g_assert (data->index == -1);
829 g_assert (data->low_index == -1);
831 data->state = SCANNED;
832 data->low_index = data->index = object_index++;
833 dyn_array_ptr_push (&scan_stack, data);
834 dyn_array_ptr_push (&loop_stack, data);
837 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color);
842 g_assert (data->state == SCANNED);
843 data->state = FINISHED_ON_STACK;
846 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);
849 /* Compute low index */
853 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);
856 if (data->index == data->low_index)
863 register_finalized_object (GCObject *obj)
865 g_assert (sgen_need_bridge_processing ());
866 dyn_array_ptr_push (®istered_bridges, obj);
872 dyn_array_ptr_empty (®istered_bridges);
878 dyn_array_ptr_empty (&scan_stack);
879 dyn_array_ptr_empty (&loop_stack);
880 dyn_array_ptr_empty (®istered_bridges);
881 free_object_buckets ();
882 free_color_buckets ();
885 num_colors_with_bridges = 0;
890 dump_color_table (const char *why, gboolean do_index)
894 printf ("colors%s:\n", why);
896 for (cur = root_color_bucket; cur; cur = cur->next, ++i) {
898 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
900 printf ("\t%d(%d):", i, cd->api_index);
902 printf ("\t%d: ", i);
903 for (j = 0; j < dyn_array_ptr_size (&cd->other_colors); ++j) {
904 printf ("%p ", dyn_array_ptr_get (&cd->other_colors, j));
906 if (dyn_array_ptr_size (&cd->bridges)) {
907 printf (" bridges: ");
908 for (j = 0; j < dyn_array_ptr_size (&cd->bridges); ++j) {
909 GCObject *obj = dyn_array_ptr_get (&cd->bridges, j);
910 ScanData *data = find_or_create_data (obj);
911 printf ("%d ", data->index);
922 step_timer (gint64 *timer)
924 gint64 curtime, diff;
926 SGEN_TV_GETTIME (curtime);
927 diff = SGEN_TV_ELAPSED (*timer, curtime);
932 processing_stw_step (void)
938 if (!dyn_array_ptr_size (®istered_bridges))
941 #if defined (DUMP_GRAPH)
942 printf ("-----------------\n");
945 SGEN_TV_GETTIME (curtime);
947 object_alloc_init ();
950 bridge_count = dyn_array_ptr_size (®istered_bridges);
951 for (i = 0; i < bridge_count ; ++i)
952 register_bridge_object ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
954 setup_time = step_timer (&curtime);
956 for (i = 0; i < bridge_count; ++i) {
957 ScanData *sd = find_data ((GCObject *)dyn_array_ptr_get (®istered_bridges, i));
958 if (sd->state == INITIAL) {
959 dyn_array_ptr_push (&scan_stack, sd);
962 g_assert (sd->state == FINISHED_OFF_STACK);
966 tarjan_time = step_timer (&curtime);
968 #if defined (DUMP_GRAPH)
969 printf ("----summary----\n");
970 printf ("bridges:\n");
971 for (int i = 0; i < bridge_count; ++i) {
972 ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i));
973 printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color);
976 dump_color_table (" after tarjan", FALSE);
979 clear_after_processing ();
984 gather_xrefs (ColorData *color)
987 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
988 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
992 if (color_visible_to_client (src))
993 dyn_array_ptr_add (&color_merge_array, src);
1000 reset_xrefs (ColorData *color)
1003 for (i = 0; i < dyn_array_ptr_size (&color->other_colors); ++i) {
1004 ColorData *src = (ColorData *)dyn_array_ptr_get (&color->other_colors, i);
1007 src->visited = FALSE;
1008 if (!color_visible_to_client (src))
1014 processing_build_callback_data (int generation)
1020 g_assert (bridge_processor->num_sccs == 0 && bridge_processor->num_xrefs == 0);
1021 g_assert (!bridge_processor->api_sccs && !bridge_processor->api_xrefs);
1023 if (!dyn_array_ptr_size (®istered_bridges))
1026 SGEN_TV_GETTIME (curtime);
1028 /*create API objects */
1030 #if defined (DUMP_GRAPH)
1031 printf ("***** API *****\n");
1032 printf ("number of SCCs %d\n", num_colors_with_bridges);
1035 // Count the number of SCCs visible to the client
1037 for (cur = root_color_bucket; cur; cur = cur->next) {
1039 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1040 if (color_visible_to_client (cd))
1045 /* This is a straightforward translation from colors to the bridge callback format. */
1046 MonoGCBridgeSCC **api_sccs = (MonoGCBridgeSCC **)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC*) * num_sccs, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1050 // Convert visible SCCs, along with their bridged object list, to MonoGCBridgeSCCs in the client's SCC list
1051 for (cur = root_color_bucket; cur; cur = cur->next) {
1053 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1054 int bridges = dyn_array_ptr_size (&cd->bridges);
1055 if (!(bridges || bridgeless_color_is_heavy (cd)))
1058 api_sccs [api_index] = (MonoGCBridgeSCC *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC) + sizeof (MonoObject*) * bridges, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1059 api_sccs [api_index]->is_alive = FALSE;
1060 api_sccs [api_index]->num_objs = bridges;
1062 cd->api_index = api_index;
1064 for (j = 0; j < bridges; ++j)
1065 api_sccs [api_index]->objs [j] = (MonoObject *)dyn_array_ptr_get (&cd->bridges, j);
1067 g_assert(api_index < API_INDEX_MAX);
1072 scc_setup_time = step_timer (&curtime);
1074 // Eliminate non-visible SCCs from the SCC list and redistribute xrefs
1075 for (cur = root_color_bucket; cur; cur = cur->next) {
1077 for (cd = &cur->data [0]; cd < cur->next_data; ++cd) {
1078 if (!color_visible_to_client (cd))
1081 color_merge_array_empty ();
1084 dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array);
1085 xref_count += dyn_array_ptr_size (&cd->other_colors);
1089 gather_xref_time = step_timer (&curtime);
1091 #if defined (DUMP_GRAPH)
1092 printf ("TOTAL XREFS %d\n", xref_count);
1093 dump_color_table (" after xref pass", TRUE);
1096 // Write out xrefs array
1097 MonoGCBridgeXRef *api_xrefs = (MonoGCBridgeXRef *)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef) * xref_count, INTERNAL_MEM_BRIDGE_DATA, TRUE);
1099 for (cur = root_color_bucket; cur; cur = cur->next) {
1101 for (src = &cur->data [0]; src < cur->next_data; ++src) {
1102 if (!color_visible_to_client (src))
1105 for (j = 0; j < dyn_array_ptr_size (&src->other_colors); ++j) {
1106 ColorData *dest = (ColorData *)dyn_array_ptr_get (&src->other_colors, j);
1107 g_assert (color_visible_to_client (dest)); /* Supposedly we already eliminated all xrefs to non-visible objects. */
1109 api_xrefs [xref_index].src_scc_index = src->api_index;
1110 api_xrefs [xref_index].dst_scc_index = dest->api_index;
1117 g_assert (xref_count == xref_index);
1118 xref_setup_time = step_timer (&curtime);
1120 #if defined (DUMP_GRAPH)
1121 printf ("---xrefs:\n");
1122 for (int i = 0; i < xref_count; ++i)
1123 printf ("\t%d -> %d\n", api_xrefs [i].src_scc_index, api_xrefs [i].dst_scc_index);
1126 //FIXME move half of the cleanup to before the bridge callback?
1127 bridge_processor->num_sccs = num_sccs;
1128 bridge_processor->api_sccs = api_sccs;
1129 bridge_processor->num_xrefs = xref_count;
1130 bridge_processor->api_xrefs = api_xrefs;
1134 processing_after_callback (int generation)
1137 int bridge_count = dyn_array_ptr_size (®istered_bridges);
1138 int object_count = object_data_count;
1139 int color_count = color_data_count;
1140 int colors_with_bridges_count = num_colors_with_bridges;
1142 SGEN_TV_GETTIME (curtime);
1147 cleanup_time = step_timer (&curtime);
1149 mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "GC_TAR_BRIDGE bridges %d objects %d opaque %d colors %d colors-bridged %d colors-visible %d xref %d cache %d/%d/%d setup %.2fms tarjan %.2fms scc-setup %.2fms gather-xref %.2fms xref-setup %.2fms cleanup %.2fms",
1150 bridge_count, object_count, ignored_objects,
1151 color_count, colors_with_bridges_count, num_sccs, xref_count,
1152 cache_hits, cache_semihits, cache_misses,
1153 setup_time / 10000.0f,
1154 tarjan_time / 10000.0f,
1155 scc_setup_time / 10000.0f,
1156 gather_xref_time / 10000.0f,
1157 xref_setup_time / 10000.0f,
1158 cleanup_time / 10000.0f);
1160 cache_hits = cache_semihits = cache_misses = 0;
1161 ignored_objects = 0;
1165 describe_pointer (GCObject *obj)
1167 // HashEntry *entry;
1170 for (i = 0; i < dyn_array_ptr_size (®istered_bridges); ++i) {
1171 if (obj == dyn_array_ptr_get (®istered_bridges, i)) {
1172 printf ("Pointer is a registered bridge object.\n");
1177 // entry = sgen_hash_table_lookup (&hash_table, obj);
1181 // printf ("Bridge hash table entry %p:\n", entry);
1182 // printf (" is bridge: %d\n", (int)entry->is_bridge);
1183 // printf (" is visited: %d\n", (int)entry->is_visited);
1187 sgen_tarjan_bridge_init (SgenBridgeProcessor *collector)
1189 collector->reset_data = reset_data;
1190 collector->processing_stw_step = processing_stw_step;
1191 collector->processing_build_callback_data = processing_build_callback_data;
1192 collector->processing_after_callback = processing_after_callback;
1193 collector->class_kind = class_kind;
1194 collector->register_finalized_object = register_finalized_object;
1195 collector->describe_pointer = describe_pointer;
1196 collector->enable_accounting = enable_accounting;
1197 // collector->set_dump_prefix = set_dump_prefix;
1199 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET, BUCKET_SIZE);
1200 g_assert (sizeof (ObjectBucket) <= BUCKET_SIZE);
1201 g_assert (sizeof (ColorBucket) <= BUCKET_SIZE);
1202 g_assert (API_INDEX_BITS + INCOMING_COLORS_BITS <= 31);
1203 bridge_processor = collector;