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.
42 // Is this class bridged or not, and should its dependencies be scanned or not?
43 // The result of this callback will be cached for use by is_opaque_object later.
44 static MonoGCBridgeObjectKind
45 class_kind (MonoClass
*klass
)
47 MonoGCBridgeObjectKind res
= bridge_callbacks
.bridge_class_kind (klass
);
49 /* If it's a bridge, nothing we can do about it. */
50 if (res
== GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS
|| res
== GC_BRIDGE_OPAQUE_BRIDGE_CLASS
)
53 /* Non bridge classes with no pointers will never point to a bridge, so we can savely ignore them. */
54 if (!klass
->has_references
) {
55 SGEN_LOG (6, "class %s is opaque\n", klass
->name
);
56 return GC_BRIDGE_OPAQUE_CLASS
;
59 /* Some arrays can be ignored */
60 if (klass
->rank
== 1) {
61 MonoClass
*elem_class
= klass
->element_class
;
63 /* FIXME the bridge check can be quite expensive, cache it at the class level. */
64 /* An array of a sealed type that is not a bridge will never get to a bridge */
65 if ((mono_class_get_flags (elem_class
) & TYPE_ATTRIBUTE_SEALED
) && !elem_class
->has_references
&& !bridge_callbacks
.bridge_class_kind (elem_class
)) {
66 SGEN_LOG (6, "class %s is opaque\n", klass
->name
);
67 return GC_BRIDGE_OPAQUE_CLASS
;
71 return GC_BRIDGE_TRANSPARENT_CLASS
;
74 //enable usage logging
75 // #define DUMP_GRAPH 1
77 /* Used in bridgeless_color_is_heavy:
78 * The idea here is as long as the reference fanin and fanout on a node are both 2 or greater, then
79 * removing that node will result in a net increase in edge count. So the question is which node
80 * removals are counterproductive (i.e., how many edges saved balances out one node added).
81 * The number of edges saved by preserving a node is (fanin*fanout - fanin - fanout).
83 * With all that in mind:
85 * - HEAVY_REFS_MIN is the number that *both* fanin and fanout must meet to preserve the node.
86 * - HEAVY_COMBINED_REFS_MIN is the number (fanin*fanout) must meet to preserve the node.
88 * Note HEAVY_COMBINED_REFS_MIN must be <= 2*INCOMING_COLORS_MAX, or we won't know the true fanin.
91 #define HEAVY_REFS_MIN 2
92 #define HEAVY_COMBINED_REFS_MIN 60
95 * The higher INCOMING_COLORS_BITS is the higher HEAVY_COMBINED_REFS_MIN can be (see above).
96 * However, API_INDEX_BITS + INCOMING_COLORS_BITS must be equal to 31, and if API_INDEX_BITS is too
97 * low then terrible things will happen if too many colors are generated. (The number of colors we
98 * will ever attempt to generate is currently naturally limited by the JNI GREF limit.)
101 #define API_INDEX_BITS 26
102 #define INCOMING_COLORS_BITS 5
104 #define API_INDEX_MAX ((1<<API_INDEX_BITS)-1)
105 #define INCOMING_COLORS_MAX ((1<<INCOMING_COLORS_BITS)-1)
117 We can split this data structure in two, those with bridges and those without
118 (and only bridgeless need to record incoming_colors)
121 // Colors (ColorDatas) linked to by objects with this color
122 DynPtrArray other_colors
;
123 // Bridge objects (GCObjects) held by objects with this color
125 // Index of this color's MonoGCBridgeSCC in the array passed to the client (or -1 for none)
126 signed api_index
: API_INDEX_BITS
;
127 // Count of colors that list this color in their other_colors
128 unsigned incoming_colors
: INCOMING_COLORS_BITS
;
129 unsigned visited
: 1;
132 // Represents one managed object. Equivalent of new/old bridge "HashEntry"
133 typedef struct _ScanData
{
134 // FIXME this can be eliminated; if we have a ScanData we generally looked it up from its GCObject
136 // We use the sgen lock_word in GCObject to store a pointer to the ScanData. Cache the original here to restore later:
140 // Tarjan algorithm index (order visited)
142 // Tarjan index of lowest-index object known reachable from here
143 signed low_index
: 27;
145 // See "ScanData state" enum above
147 unsigned is_bridge
: 1;
148 // Similar to lock_word, we use these bits in the GCObject as scratch space then restore them when done
149 unsigned obj_state
: 2;
152 /* Should color be made visible to client even though it has no bridges?
153 * True if we predict the number of reduced edges to be enough to justify the extra node.
155 static inline gboolean
156 bridgeless_color_is_heavy (ColorData
*data
) {
157 int fanin
= data
->incoming_colors
;
158 int fanout
= dyn_array_ptr_size (&data
->other_colors
);
159 return fanin
> HEAVY_REFS_MIN
&& fanout
> HEAVY_REFS_MIN
160 && fanin
*fanout
>= HEAVY_COMBINED_REFS_MIN
;
163 // Should color be made visible to client?
164 static inline gboolean
165 color_visible_to_client (ColorData
*data
) {
166 return dyn_array_ptr_size (&data
->bridges
) || bridgeless_color_is_heavy (data
);
169 // Stacks of ScanData objects used for tarjan algorithm.
170 // The Tarjan algorithm is normally defined recursively; here scan_stack simulates the call stack of a recursive algorithm,
171 // and loop_stack is the stack structure used by the algorithm itself.
172 static DynPtrArray scan_stack
, loop_stack
;
174 // GCObjects on which register_finalized_object has been called
175 static DynPtrArray registered_bridges
;
177 // As we traverse the graph, which ColorData objects are accessible from our current position?
178 static DynPtrArray color_merge_array
;
179 // Running hash of the contents of the color_merge_array.
180 static unsigned int color_merge_array_hash
;
182 static void color_merge_array_empty ()
184 dyn_array_ptr_empty (&color_merge_array
);
185 color_merge_array_hash
= 0;
188 static int ignored_objects
;
189 static int object_index
;
190 static int num_colors_with_bridges
;
192 static int xref_count
;
194 static size_t setup_time
, tarjan_time
, scc_setup_time
, gather_xref_time
, xref_setup_time
, cleanup_time
;
195 static SgenBridgeProcessor
*bridge_processor
;
197 #define BUCKET_SIZE 8184
200 #define NUM_SCAN_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ScanData))
202 typedef struct _ObjectBucket ObjectBucket
;
203 struct _ObjectBucket
{
206 ScanData data
[NUM_SCAN_ENTRIES
];
209 static ObjectBucket
*root_object_bucket
;
210 static ObjectBucket
*cur_object_bucket
;
211 static int object_data_count
;
213 // Arenas to allocate ScanData from
215 new_object_bucket (void)
217 ObjectBucket
*res
= (ObjectBucket
*)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET
);
218 res
->next_data
= &res
->data
[0];
223 object_alloc_init (void)
225 root_object_bucket
= cur_object_bucket
= new_object_bucket ();
229 alloc_object_data (void)
234 /* next_data points to the first free entry */
235 res
= cur_object_bucket
->next_data
;
236 if (res
>= &cur_object_bucket
->data
[NUM_SCAN_ENTRIES
]) {
237 ObjectBucket
*b
= new_object_bucket ();
238 cur_object_bucket
->next
= b
;
239 cur_object_bucket
= b
;
242 cur_object_bucket
->next_data
= res
+ 1;
248 free_object_buckets (void)
250 ObjectBucket
*cur
= root_object_bucket
;
252 object_data_count
= 0;
255 ObjectBucket
*tmp
= cur
->next
;
256 sgen_free_internal (cur
, INTERNAL_MEM_TARJAN_OBJ_BUCKET
);
260 root_object_bucket
= cur_object_bucket
= NULL
;
264 #define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData))
266 // Arenas for ColorDatas, same as ObjectBucket except items-per-bucket differs
267 typedef struct _ColorBucket ColorBucket
;
268 struct _ColorBucket
{
270 ColorData
*next_data
;
271 ColorData data
[NUM_COLOR_ENTRIES
];
274 static ColorBucket
*root_color_bucket
;
275 static ColorBucket
*cur_color_bucket
;
276 static int color_data_count
;
280 new_color_bucket (void)
282 ColorBucket
*res
= (ColorBucket
*)sgen_alloc_internal (INTERNAL_MEM_TARJAN_OBJ_BUCKET
);
283 res
->next_data
= &res
->data
[0];
288 color_alloc_init (void)
290 root_color_bucket
= cur_color_bucket
= new_color_bucket ();
294 alloc_color_data (void)
299 /* next_data points to the first free entry */
300 res
= cur_color_bucket
->next_data
;
301 if (res
>= &cur_color_bucket
->data
[NUM_COLOR_ENTRIES
]) {
302 ColorBucket
*b
= new_color_bucket ();
303 cur_color_bucket
->next
= b
;
304 cur_color_bucket
= b
;
307 cur_color_bucket
->next_data
= res
+ 1;
313 free_color_buckets (void)
315 ColorBucket
*cur
, *tmp
;
317 color_data_count
= 0;
319 for (cur
= root_color_bucket
; cur
; cur
= tmp
) {
321 for (cd
= &cur
->data
[0]; cd
< cur
->next_data
; ++cd
) {
322 dyn_array_ptr_uninit (&cd
->other_colors
);
323 dyn_array_ptr_uninit (&cd
->bridges
);
326 sgen_free_internal (cur
, INTERNAL_MEM_TARJAN_OBJ_BUCKET
);
328 root_color_bucket
= cur_color_bucket
= NULL
;
333 create_data (GCObject
*obj
)
335 mword
*o
= (mword
*)obj
;
336 ScanData
*res
= alloc_object_data ();
339 res
->index
= res
->low_index
= -1;
340 res
->state
= INITIAL
;
341 res
->is_bridge
= FALSE
;
342 res
->obj_state
= o
[0] & SGEN_VTABLE_BITS_MASK
;
343 res
->lock_word
= o
[1];
345 o
[0] |= SGEN_VTABLE_BITS_MASK
;
351 find_data (GCObject
*obj
)
354 mword
*o
= (mword
*)obj
;
355 if ((o
[0] & SGEN_VTABLE_BITS_MASK
) == SGEN_VTABLE_BITS_MASK
)
356 a
= (ScanData
*)o
[1];
361 clear_after_processing (void)
365 for (cur
= root_object_bucket
; cur
; cur
= cur
->next
) {
367 for (sd
= &cur
->data
[0]; sd
< cur
->next_data
; ++sd
) {
368 mword
*o
= (mword
*)sd
->obj
;
369 o
[0] &= ~SGEN_VTABLE_BITS_MASK
;
370 o
[0] |= sd
->obj_state
;
371 o
[1] = sd
->lock_word
;
377 bridge_object_forward (GCObject
*obj
)
380 mword
*o
= (mword
*)obj
;
381 if ((o
[0] & SGEN_VTABLE_BITS_MASK
) == SGEN_VTABLE_BITS_MASK
)
384 fwd
= SGEN_OBJECT_IS_FORWARDED (obj
);
385 return fwd
? fwd
: obj
;
390 safe_name_bridge (GCObject
*obj
)
392 GCVTable vt
= SGEN_LOAD_VTABLE (obj
);
393 return vt
->klass
->name
;
397 find_or_create_data (GCObject
*obj
)
399 ScanData
*entry
= find_data (obj
);
401 entry
= create_data (obj
);
413 The merge cache maps an ordered list of ColorDatas [the color_merge_array] to a single ColorData.
415 About cache bucket tuning: We tried 2/32, 2/128, 4/32, 4/128, 6/128 and 8/128.
417 The performance cost between 4/128 and 8/128 is so small since cache movement happens completely in the same cacheline,
418 making the extra space pretty much free.
420 The cost between 32 and 128 itens is minimal too, it's mostly a fixed, setup cost.
422 Memory wise, 4/32 takes 512 and 8/128 takes 8k, so it's quite reasonable.
425 #define ELEMENTS_PER_BUCKET 8
426 #define COLOR_CACHE_SIZE 128
427 static HashEntry merge_cache
[COLOR_CACHE_SIZE
][ELEMENTS_PER_BUCKET
];
428 static unsigned int hash_perturb
;
430 // If true, disable an optimization where sometimes SCC nodes are merged without a perfect check
431 static gboolean scc_precise_merge
;
434 mix_hash (uintptr_t source
)
436 unsigned int hash
= source
;
438 // The full hash determines whether two colors can be merged-- sometimes exclusively.
439 // This value changes every GC, so XORing it in before performing the hash will make the
440 // chance that two different colors will produce the same hash on successive GCs very low.
441 hash
= hash
^ hash_perturb
;
444 hash
= (((hash
* 215497) >> 16) ^ ((hash
* 1823231) + hash
));
446 // Mix in highest bits on 64-bit systems only
447 if (sizeof (source
) > 4)
448 hash
= hash
^ (source
>> 32);
456 memset (merge_cache
, 0, sizeof (merge_cache
));
458 // When using the precise merge debug option, we do not want the inconsistency caused by hash_perturb.
459 if (!scc_precise_merge
)
465 dyn_array_ptr_contains (DynPtrArray
*da
, void *x
)
468 for (i
= 0; i
< dyn_array_ptr_size (da
); ++i
)
469 if (dyn_array_ptr_get (da
, i
) == x
)
475 match_colors_estimate (DynPtrArray
*a
, DynPtrArray
*b
)
477 return dyn_array_ptr_size (a
) == dyn_array_ptr_size (b
);
482 match_colors (DynPtrArray
*a
, DynPtrArray
*b
)
485 if (dyn_array_ptr_size (a
) != dyn_array_ptr_size (b
))
488 for (i
= 0; i
< dyn_array_ptr_size (a
); ++i
) {
489 if (!dyn_array_ptr_contains (b
, dyn_array_ptr_get (a
, i
)))
495 // If scc_precise_merge, "semihits" refers to find_in_cache calls aborted because the merge array was too large.
496 // Otherwise "semihits" refers to cache hits where the match was only estimated.
497 static int cache_hits
, cache_semihits
, cache_misses
;
499 // The cache contains only non-bridged colors.
501 find_in_cache (int *insert_index
)
506 size
= dyn_array_ptr_size (&color_merge_array
);
508 /* Color equality checking is very expensive with a lot of elements, so when there are many
509 * elements we switch to a cheap comparison method which allows false positives. When false
510 * positives occur the worst that can happen is two items will be inappropriately merged
511 * and memory will be retained longer than it should be. We assume that will correct itself
512 * on the next GC (the hash_perturb mechanism increases the probability of this).
514 * Because this has *some* potential to create problems, if the user set the debug option
515 * 'enable-tarjan-precise-merge' we bail out early (and never merge SCCs with >3 colors).
517 gboolean color_merge_array_large
= size
> 3;
518 if (scc_precise_merge
&& color_merge_array_large
) {
523 unsigned int hash
= color_merge_array_hash
;
524 if (!hash
) // 0 is used to indicate an empty bucket entry
527 index
= hash
& (COLOR_CACHE_SIZE
- 1);
528 bucket
= merge_cache
[index
];
529 for (i
= 0; i
< ELEMENTS_PER_BUCKET
; ++i
) {
530 if (bucket
[i
].hash
!= hash
)
533 if (color_merge_array_large
) {
534 if (match_colors_estimate (&bucket
[i
].color
->other_colors
, &color_merge_array
)) {
536 return bucket
[i
].color
;
539 if (match_colors (&bucket
[i
].color
->other_colors
, &color_merge_array
)) {
541 return bucket
[i
].color
;
546 //move elements to the back
547 for (i
= ELEMENTS_PER_BUCKET
- 1; i
> 0; --i
)
548 bucket
[i
] = bucket
[i
- 1];
550 *insert_index
= index
;
551 bucket
[0].hash
= hash
;
555 // A color is needed for an SCC. If the SCC has bridges, the color MUST be newly allocated.
556 // If the SCC lacks bridges, the allocator MAY use the cache to merge it with an existing one.
558 new_color (gboolean has_bridges
)
562 /* XXX Try to find an equal one and return it */
564 cd
= find_in_cache (&cacheSlot
);
569 cd
= alloc_color_data ();
571 dyn_array_ptr_set_all (&cd
->other_colors
, &color_merge_array
);
574 for (int i
= 0; i
< dyn_array_ptr_size (&color_merge_array
); ++i
) {
575 ColorData
*points_to
= (ColorData
*)dyn_array_ptr_get (&color_merge_array
, i
);
576 points_to
->incoming_colors
= MIN (points_to
->incoming_colors
+ 1, INCOMING_COLORS_MAX
);
579 /* if cacheSlot >= 0, it means we prepared a given slot to receive the new color */
581 merge_cache
[cacheSlot
][0].color
= cd
;
588 register_bridge_object (GCObject
*obj
)
590 create_data (obj
)->is_bridge
= TRUE
;
594 is_opaque_object (GCObject
*obj
)
596 MonoVTable
*vt
= SGEN_LOAD_VTABLE (obj
);
597 if ((vt
->gc_bits
& SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT
) == SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT
) {
598 SGEN_LOG (6, "ignoring %s\n", vt
->klass
->name
);
605 // Called during DFS; visits one child. If it is a candidate to be scanned, pushes it to the stacks.
607 push_object (GCObject
*obj
)
610 obj
= bridge_object_forward (obj
);
613 printf ("\t= pushing %p %s -> ", obj
, safe_name_bridge (obj
));
616 /* Object types we can ignore */
617 if (is_opaque_object (obj
)) {
624 data
= find_data (obj
);
626 /* Already marked - XXX must be done this way as the bridge themselves are alive. */
627 if (data
&& data
->state
!= INITIAL
) {
629 printf ("already marked\n");
634 /* We only care about dead objects */
635 if (!data
&& sgen_object_is_live (obj
)) {
643 printf ("pushed!\n");
647 data
= create_data (obj
);
648 g_assert (data
->state
== INITIAL
);
649 g_assert (data
->index
== -1);
650 dyn_array_ptr_push (&scan_stack
, data
);
654 #define HANDLE_PTR(ptr,obj) do { \
655 GCObject *dst = (GCObject*)*(ptr); \
656 if (dst) push_object (dst); \
659 // dfs () function's queue-children-of-object operation.
661 push_all (ScanData
*data
)
663 GCObject
*obj
= data
->obj
;
664 char *start
= (char*)obj
;
665 mword desc
= sgen_obj_get_descriptor_safe (obj
);
668 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data
->obj
), data
->obj
, data
->index
, data
->color
);
671 #include "sgen/sgen-scan-object.h"
676 compute_low_index (ScanData
*data
, GCObject
*obj
)
681 obj
= bridge_object_forward (obj
);
682 other
= find_data (obj
);
685 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);
690 g_assert (other
->state
!= INITIAL
);
692 if ((other
->state
== SCANNED
|| other
->state
== FINISHED_ON_STACK
) && data
->low_index
> other
->low_index
)
693 data
->low_index
= other
->low_index
;
695 /* Compute the low color */
696 if (other
->color
== NULL
)
701 color_merge_array_hash
+= mix_hash ((uintptr_t) other
->color
);
702 dyn_array_ptr_add (&color_merge_array
, other
->color
);
708 #define HANDLE_PTR(ptr,obj) do { \
709 GCObject *dst = (GCObject*)*(ptr); \
710 if (dst) compute_low_index (data, dst); \
714 compute_low (ScanData
*data
)
716 GCObject
*obj
= data
->obj
;
717 char *start
= (char*)obj
;
718 mword desc
= sgen_obj_get_descriptor_safe (obj
);
720 #include "sgen/sgen-scan-object.h"
723 // A non-bridged object needs a single color describing the current merge array.
727 ColorData
*color
= NULL
;
728 int size
= dyn_array_ptr_size (&color_merge_array
);
730 // Merge array is empty-- this SCC points to no bridged colors.
731 // This SCC can be ignored completely.
735 // Merge array has one item-- this SCC points to a single bridged color.
736 // This SCC can be forwarded to the pointed-to color.
737 else if (size
== 1) {
738 color
= (ColorData
*)dyn_array_ptr_get (&color_merge_array
, 0);
740 // This SCC gets to talk to the color allocator.
742 color
= new_color (FALSE
);
748 create_scc (ScanData
*data
)
751 gboolean found
= FALSE
;
752 gboolean found_bridge
= FALSE
;
753 ColorData
*color_data
= NULL
;
755 for (i
= dyn_array_ptr_size (&loop_stack
) - 1; i
>= 0; --i
) {
756 ScanData
*other
= (ScanData
*)dyn_array_ptr_get (&loop_stack
, i
);
757 found_bridge
|= other
->is_bridge
;
758 if (found_bridge
|| other
== data
)
763 printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data
->obj
), data
->obj
, found_bridge
);
764 printf ("\tpoints-to-colors: ");
765 for (int i
= 0; i
< dyn_array_ptr_size (&color_merge_array
); ++i
)
766 printf ("%p ", dyn_array_ptr_get (&color_merge_array
, i
));
769 printf ("loop stack: ");
770 for (int i
= 0; i
< dyn_array_ptr_size (&loop_stack
); ++i
) {
771 ScanData
*other
= dyn_array_ptr_get (&loop_stack
, i
);
772 printf ("(%d/%d)", other
->index
, other
->low_index
);
778 color_data
= new_color (TRUE
);
779 ++num_colors_with_bridges
;
781 color_data
= reduce_color ();
784 while (dyn_array_ptr_size (&loop_stack
) > 0) {
785 ScanData
*other
= (ScanData
*)dyn_array_ptr_pop (&loop_stack
);
788 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
);
791 other
->color
= color_data
;
792 switch (other
->state
) {
793 case FINISHED_ON_STACK
:
794 other
->state
= FINISHED_OFF_STACK
;
796 case FINISHED_OFF_STACK
:
799 g_error ("Invalid state when building SCC %d", other
->state
);
802 if (other
->is_bridge
)
803 dyn_array_ptr_add (&color_data
->bridges
, other
->obj
);
812 for (i
= 0; i
< dyn_array_ptr_size (&color_merge_array
); ++i
) {
813 ColorData
*cd
= (ColorData
*)dyn_array_ptr_get (&color_merge_array
, i
);
814 g_assert (cd
->visited
);
817 color_merge_array_empty ();
818 found_bridge
= FALSE
;
824 g_assert (dyn_array_ptr_size (&scan_stack
) == 1);
825 g_assert (dyn_array_ptr_size (&loop_stack
) == 0);
827 color_merge_array_empty ();
829 while (dyn_array_ptr_size (&scan_stack
) > 0) {
830 ScanData
*data
= (ScanData
*)dyn_array_ptr_pop (&scan_stack
);
833 * Ignore finished objects on stack, they happen due to loops. For example:
839 * We start scanning from A and push C before B. So, after the first iteration, the scan stack will have: A C B.
840 * We then visit B, which will find C in its initial state and push again.
841 * Finally after finish with C and B, the stack will be left with "A C" and at this point C should be ignored.
843 * The above explains FINISHED_ON_STACK, to explain FINISHED_OFF_STACK, consider if the root was D, which pointed
844 * to A and C. A is processed first, leaving C on stack after that in the mentioned state.
846 if (data
->state
== FINISHED_ON_STACK
|| data
->state
== FINISHED_OFF_STACK
)
849 if (data
->state
== INITIAL
) {
850 g_assert (data
->index
== -1);
851 g_assert (data
->low_index
== -1);
853 data
->state
= SCANNED
;
854 data
->low_index
= data
->index
= object_index
++;
855 dyn_array_ptr_push (&scan_stack
, data
);
856 dyn_array_ptr_push (&loop_stack
, data
);
859 printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data
->obj
), data
->obj
, data
->index
, data
->color
);
864 g_assert (data
->state
== SCANNED
);
865 data
->state
= FINISHED_ON_STACK
;
868 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
);
871 /* Compute low index */
875 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
);
878 if (data
->index
== data
->low_index
)
885 register_finalized_object (GCObject
*obj
)
887 g_assert (sgen_need_bridge_processing ());
888 dyn_array_ptr_push (®istered_bridges
, obj
);
894 dyn_array_ptr_empty (®istered_bridges
);
900 dyn_array_ptr_empty (&scan_stack
);
901 dyn_array_ptr_empty (&loop_stack
);
902 dyn_array_ptr_empty (®istered_bridges
);
903 free_object_buckets ();
904 free_color_buckets ();
907 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 GCObject
*obj
= dyn_array_ptr_get (&cd
->bridges
, j
);
932 ScanData
*data
= find_or_create_data (obj
);
933 printf ("%d ", data
->index
);
944 step_timer (gint64
*timer
)
946 gint64 curtime
, diff
;
948 SGEN_TV_GETTIME (curtime
);
949 diff
= SGEN_TV_ELAPSED (*timer
, curtime
);
954 processing_stw_step (void)
960 if (!dyn_array_ptr_size (®istered_bridges
))
963 #if defined (DUMP_GRAPH)
964 printf ("-----------------\n");
967 SGEN_TV_GETTIME (curtime
);
969 object_alloc_init ();
972 bridge_count
= dyn_array_ptr_size (®istered_bridges
);
973 for (i
= 0; i
< bridge_count
; ++i
)
974 register_bridge_object ((GCObject
*)dyn_array_ptr_get (®istered_bridges
, i
));
976 setup_time
= step_timer (&curtime
);
978 for (i
= 0; i
< bridge_count
; ++i
) {
979 ScanData
*sd
= find_data ((GCObject
*)dyn_array_ptr_get (®istered_bridges
, i
));
980 if (sd
->state
== INITIAL
) {
981 dyn_array_ptr_push (&scan_stack
, sd
);
984 g_assert (sd
->state
== FINISHED_OFF_STACK
);
988 tarjan_time
= step_timer (&curtime
);
990 #if defined (DUMP_GRAPH)
991 printf ("----summary----\n");
992 printf ("bridges:\n");
993 for (int i
= 0; i
< bridge_count
; ++i
) {
994 ScanData
*sd
= find_data (dyn_array_ptr_get (®istered_bridges
, i
));
995 printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd
->obj
), sd
->obj
, sd
->index
, sd
->color
);
998 dump_color_table (" after tarjan", FALSE
);
1001 clear_after_processing ();
1006 gather_xrefs (ColorData
*color
)
1009 for (i
= 0; i
< dyn_array_ptr_size (&color
->other_colors
); ++i
) {
1010 ColorData
*src
= (ColorData
*)dyn_array_ptr_get (&color
->other_colors
, i
);
1013 src
->visited
= TRUE
;
1014 if (color_visible_to_client (src
))
1015 dyn_array_ptr_add (&color_merge_array
, src
);
1022 reset_xrefs (ColorData
*color
)
1025 for (i
= 0; i
< dyn_array_ptr_size (&color
->other_colors
); ++i
) {
1026 ColorData
*src
= (ColorData
*)dyn_array_ptr_get (&color
->other_colors
, i
);
1029 src
->visited
= FALSE
;
1030 if (!color_visible_to_client (src
))
1036 processing_build_callback_data (int generation
)
1042 g_assert (bridge_processor
->num_sccs
== 0 && bridge_processor
->num_xrefs
== 0);
1043 g_assert (!bridge_processor
->api_sccs
&& !bridge_processor
->api_xrefs
);
1045 if (!dyn_array_ptr_size (®istered_bridges
))
1048 SGEN_TV_GETTIME (curtime
);
1050 /*create API objects */
1052 #if defined (DUMP_GRAPH)
1053 printf ("***** API *****\n");
1054 printf ("number of SCCs %d\n", num_colors_with_bridges
);
1057 // Count the number of SCCs visible to the client
1059 for (cur
= root_color_bucket
; cur
; cur
= cur
->next
) {
1061 for (cd
= &cur
->data
[0]; cd
< cur
->next_data
; ++cd
) {
1062 if (color_visible_to_client (cd
))
1067 /* This is a straightforward translation from colors to the bridge callback format. */
1068 MonoGCBridgeSCC
**api_sccs
= (MonoGCBridgeSCC
**)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC
*) * num_sccs
, INTERNAL_MEM_BRIDGE_DATA
, TRUE
);
1072 // Convert visible SCCs, along with their bridged object list, to MonoGCBridgeSCCs in the client's SCC list
1073 for (cur
= root_color_bucket
; cur
; cur
= cur
->next
) {
1075 for (cd
= &cur
->data
[0]; cd
< cur
->next_data
; ++cd
) {
1076 int bridges
= dyn_array_ptr_size (&cd
->bridges
);
1077 if (!(bridges
|| bridgeless_color_is_heavy (cd
)))
1080 api_sccs
[api_index
] = (MonoGCBridgeSCC
*)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC
) + sizeof (MonoObject
*) * bridges
, INTERNAL_MEM_BRIDGE_DATA
, TRUE
);
1081 api_sccs
[api_index
]->is_alive
= FALSE
;
1082 api_sccs
[api_index
]->num_objs
= bridges
;
1084 cd
->api_index
= api_index
;
1086 for (j
= 0; j
< bridges
; ++j
)
1087 api_sccs
[api_index
]->objs
[j
] = (MonoObject
*)dyn_array_ptr_get (&cd
->bridges
, j
);
1089 g_assert(api_index
< API_INDEX_MAX
);
1094 scc_setup_time
= step_timer (&curtime
);
1096 // Eliminate non-visible SCCs from the SCC list and redistribute xrefs
1097 for (cur
= root_color_bucket
; cur
; cur
= cur
->next
) {
1099 for (cd
= &cur
->data
[0]; cd
< cur
->next_data
; ++cd
) {
1100 if (!color_visible_to_client (cd
))
1103 color_merge_array_empty ();
1106 dyn_array_ptr_set_all (&cd
->other_colors
, &color_merge_array
);
1107 xref_count
+= dyn_array_ptr_size (&cd
->other_colors
);
1111 gather_xref_time
= step_timer (&curtime
);
1113 #if defined (DUMP_GRAPH)
1114 printf ("TOTAL XREFS %d\n", xref_count
);
1115 dump_color_table (" after xref pass", TRUE
);
1118 // Write out xrefs array
1119 MonoGCBridgeXRef
*api_xrefs
= (MonoGCBridgeXRef
*)sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef
) * xref_count
, INTERNAL_MEM_BRIDGE_DATA
, TRUE
);
1121 for (cur
= root_color_bucket
; cur
; cur
= cur
->next
) {
1123 for (src
= &cur
->data
[0]; src
< cur
->next_data
; ++src
) {
1124 if (!color_visible_to_client (src
))
1127 for (j
= 0; j
< dyn_array_ptr_size (&src
->other_colors
); ++j
) {
1128 ColorData
*dest
= (ColorData
*)dyn_array_ptr_get (&src
->other_colors
, j
);
1129 g_assert (color_visible_to_client (dest
)); /* Supposedly we already eliminated all xrefs to non-visible objects. */
1131 api_xrefs
[xref_index
].src_scc_index
= src
->api_index
;
1132 api_xrefs
[xref_index
].dst_scc_index
= dest
->api_index
;
1139 g_assert (xref_count
== xref_index
);
1140 xref_setup_time
= step_timer (&curtime
);
1142 #if defined (DUMP_GRAPH)
1143 printf ("---xrefs:\n");
1144 for (int i
= 0; i
< xref_count
; ++i
)
1145 printf ("\t%d -> %d\n", api_xrefs
[i
].src_scc_index
, api_xrefs
[i
].dst_scc_index
);
1148 //FIXME move half of the cleanup to before the bridge callback?
1149 bridge_processor
->num_sccs
= num_sccs
;
1150 bridge_processor
->api_sccs
= api_sccs
;
1151 bridge_processor
->num_xrefs
= xref_count
;
1152 bridge_processor
->api_xrefs
= api_xrefs
;
1156 processing_after_callback (int generation
)
1159 int bridge_count
= dyn_array_ptr_size (®istered_bridges
);
1160 int object_count
= object_data_count
;
1161 int color_count
= color_data_count
;
1162 int colors_with_bridges_count
= num_colors_with_bridges
;
1164 SGEN_TV_GETTIME (curtime
);
1169 cleanup_time
= step_timer (&curtime
);
1171 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-hit %d cache-%s %d cache-miss %d setup %.2fms tarjan %.2fms scc-setup %.2fms gather-xref %.2fms xref-setup %.2fms cleanup %.2fms",
1172 bridge_count
, object_count
, ignored_objects
,
1173 color_count
, colors_with_bridges_count
, num_sccs
, xref_count
,
1174 cache_hits
, (scc_precise_merge
? "abstain" : "semihit"), cache_semihits
, cache_misses
,
1175 setup_time
/ 10000.0f
,
1176 tarjan_time
/ 10000.0f
,
1177 scc_setup_time
/ 10000.0f
,
1178 gather_xref_time
/ 10000.0f
,
1179 xref_setup_time
/ 10000.0f
,
1180 cleanup_time
/ 10000.0f
);
1182 cache_hits
= cache_semihits
= cache_misses
= 0;
1183 ignored_objects
= 0;
1187 describe_pointer (GCObject
*obj
)
1189 // HashEntry *entry;
1192 for (i
= 0; i
< dyn_array_ptr_size (®istered_bridges
); ++i
) {
1193 if (obj
== dyn_array_ptr_get (®istered_bridges
, i
)) {
1194 printf ("Pointer is a registered bridge object.\n");
1199 // entry = sgen_hash_table_lookup (&hash_table, obj);
1203 // printf ("Bridge hash table entry %p:\n", entry);
1204 // printf (" is bridge: %d\n", (int)entry->is_bridge);
1205 // printf (" is visited: %d\n", (int)entry->is_visited);
1209 set_config (const SgenBridgeProcessorConfig
*config
)
1211 if (config
->scc_precise_merge
) {
1213 scc_precise_merge
= TRUE
;
1218 sgen_tarjan_bridge_init (SgenBridgeProcessor
*collector
)
1220 collector
->reset_data
= reset_data
;
1221 collector
->processing_stw_step
= processing_stw_step
;
1222 collector
->processing_build_callback_data
= processing_build_callback_data
;
1223 collector
->processing_after_callback
= processing_after_callback
;
1224 collector
->class_kind
= class_kind
;
1225 collector
->register_finalized_object
= register_finalized_object
;
1226 collector
->describe_pointer
= describe_pointer
;
1227 collector
->set_config
= set_config
;
1229 sgen_register_fixed_internal_mem_type (INTERNAL_MEM_TARJAN_OBJ_BUCKET
, BUCKET_SIZE
);
1230 g_assert (sizeof (ObjectBucket
) <= BUCKET_SIZE
);
1231 g_assert (sizeof (ColorBucket
) <= BUCKET_SIZE
);
1232 g_assert (API_INDEX_BITS
+ INCOMING_COLORS_BITS
<= 31);
1233 bridge_processor
= collector
;