2 * sgen-bridge.c: Simple generational GC.
4 * Copyright 2011 Novell, Inc (http://www.novell.com)
5 * Copyright 2011 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 "utils/mono-logger-internal.h"
49 #include "utils/mono-time.h"
59 #define DYN_ARRAY_REF(da,i) ((void*)((da)->data + (i) * (da)->elem_size))
60 #define DYN_ARRAY_PTR_REF(da,i) (((void**)(da)->data) [(i)])
61 #define DYN_ARRAY_INT_REF(da,i) (((int*)(da)->data) [(i)])
62 #define DYN_ARRAY_PTR_STATIC_INITIALIZER { 0, sizeof (void*), 0, NULL }
63 #define DYN_ARRAY_INT_STATIC_INITIALIZER { 0, sizeof (int), 0, NULL }
66 dyn_array_init (DynArray
*da
, int elem_size
)
69 da
->elem_size
= elem_size
;
75 dyn_array_ptr_init (DynArray
*da
)
77 dyn_array_init (da
, sizeof (void*));
81 dyn_array_int_init (DynArray
*da
)
83 dyn_array_init (da
, sizeof (int));
87 dyn_array_uninit (DynArray
*da
)
89 if (da
->capacity
<= 0)
92 mono_sgen_free_internal_dynamic (da
->data
, da
->elem_size
* da
->capacity
, INTERNAL_MEM_BRIDGE_DATA
);
97 dyn_array_ensure_capacity (DynArray
*da
, int capacity
)
99 int old_capacity
= da
->capacity
;
102 if (capacity
<= old_capacity
)
105 if (da
->capacity
== 0)
107 while (capacity
> da
->capacity
)
110 new_data
= mono_sgen_alloc_internal_dynamic (da
->elem_size
* da
->capacity
, INTERNAL_MEM_BRIDGE_DATA
);
111 memcpy (new_data
, da
->data
, da
->elem_size
* da
->size
);
112 mono_sgen_free_internal_dynamic (da
->data
, da
->elem_size
* old_capacity
, INTERNAL_MEM_BRIDGE_DATA
);
117 dyn_array_add (DynArray
*da
)
121 dyn_array_ensure_capacity (da
, da
->size
+ 1);
123 p
= DYN_ARRAY_REF (da
, da
->size
);
129 dyn_array_ptr_add (DynArray
*da
, void *ptr
)
131 void **p
= dyn_array_add (da
);
135 #define dyn_array_ptr_push dyn_array_ptr_add
138 dyn_array_ptr_pop (DynArray
*da
)
141 g_assert (da
->size
> 0);
142 p
= DYN_ARRAY_PTR_REF (da
, da
->size
- 1);
148 dyn_array_int_add (DynArray
*da
, int x
)
150 int *p
= dyn_array_add (da
);
156 dyn_array_ptr_contains (DynArray *da, void *ptr)
159 for (i = 0; i < da->size; ++i)
160 if (DYN_ARRAY_PTR_REF (da, i) == ptr)
167 dyn_array_int_contains (DynArray
*da
, int x
)
170 for (i
= 0; i
< da
->size
; ++i
)
171 if (DYN_ARRAY_INT_REF (da
, i
) == x
)
176 static DynArray merge_array
;
179 dyn_array_int_merge (DynArray
*dst
, DynArray
*src
)
183 dyn_array_ensure_capacity (&merge_array
, dst
->size
+ src
->size
);
184 merge_array
.size
= 0;
186 for (i
= j
= 0; i
< dst
->size
|| j
< src
->size
; ) {
187 if (i
< dst
->size
&& j
< src
->size
) {
188 int a
= DYN_ARRAY_INT_REF (dst
, i
);
189 int b
= DYN_ARRAY_INT_REF (src
, j
);
191 dyn_array_int_add (&merge_array
, a
);
194 dyn_array_int_add (&merge_array
, a
);
198 dyn_array_int_add (&merge_array
, b
);
201 } else if (i
< dst
->size
) {
202 dyn_array_int_add (&merge_array
, DYN_ARRAY_INT_REF (dst
, i
));
205 dyn_array_int_add (&merge_array
, DYN_ARRAY_INT_REF (src
, j
));
210 if (merge_array
.size
> dst
->size
) {
211 dyn_array_ensure_capacity (dst
, merge_array
.size
);
212 memcpy (DYN_ARRAY_REF (dst
, 0), DYN_ARRAY_REF (&merge_array
, 0), merge_array
.size
* merge_array
.elem_size
);
213 dst
->size
= merge_array
.size
;
218 dyn_array_int_merge_one (DynArray
*array
, int value
)
222 int end
= array
->size
;
224 for (i
= 0; i
< end
; ++i
) {
225 if (DYN_ARRAY_INT_REF (array
, i
) == value
)
227 else if (DYN_ARRAY_INT_REF (array
, i
) > value
)
231 dyn_array_ensure_capacity (array
, array
->size
+ 1);
234 tmp
= DYN_ARRAY_INT_REF (array
, i
);
235 for (; i
<= end
; ++i
) {
236 DYN_ARRAY_INT_REF (array
, i
) = value
;
238 tmp
= DYN_ARRAY_INT_REF (array
, i
+ 1);
240 DYN_ARRAY_INT_REF (array
, end
+ 1) = tmp
;
242 DYN_ARRAY_INT_REF (array
, end
) = value
;
248 * FIXME: Optimizations:
250 * Don't allocate a scrs array for just one source. Most objects have
251 * just one source, so use the srcs pointer itself.
253 typedef struct _HashEntry
{
254 MonoObject
*obj
; /* This is a duplicate - it's already stored in the hash table */
266 typedef struct _SCC
{
269 int num_bridge_entries
;
270 DynArray xrefs
; /* these are incoming, not outgoing */
273 static SgenHashTable hash_table
= SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DATA
, INTERNAL_MEM_BRIDGE_DATA
, sizeof (HashEntry
), mono_aligned_addr_hash
, NULL
);
275 static MonoGCBridgeCallbacks bridge_callbacks
;
277 static int current_time
;
280 mono_gc_register_bridge_callbacks (MonoGCBridgeCallbacks
*callbacks
)
282 bridge_callbacks
= *callbacks
;
286 mono_sgen_is_bridge_object (MonoObject
*obj
)
288 return bridge_callbacks
.is_bridge_object (obj
);
292 mono_sgen_need_bridge_processing (void)
294 return bridge_callbacks
.cross_references
!= NULL
;
298 get_hash_entry (MonoObject
*obj
, gboolean
*existing
)
300 HashEntry
*entry
= mono_sgen_hash_table_lookup (&hash_table
, obj
);
311 memset (&new_entry
, 0, sizeof (HashEntry
));
314 dyn_array_ptr_init (&new_entry
.srcs
);
315 new_entry
.finishing_time
= -1;
316 new_entry
.scc_index
= -1;
318 mono_sgen_hash_table_replace (&hash_table
, obj
, &new_entry
);
320 return mono_sgen_hash_table_lookup (&hash_table
, obj
);
324 add_source (HashEntry
*entry
, HashEntry
*src
)
326 dyn_array_ptr_add (&entry
->srcs
, src
);
337 SGEN_HASH_TABLE_FOREACH (&hash_table
, obj
, entry
) {
338 total_srcs
+= entry
->srcs
.size
;
339 if (entry
->srcs
.size
> max_srcs
)
340 max_srcs
= entry
->srcs
.size
;
341 dyn_array_uninit (&entry
->srcs
);
342 } SGEN_HASH_TABLE_FOREACH_END
;
344 mono_sgen_hash_table_clean (&hash_table
);
346 dyn_array_uninit (&merge_array
);
347 //g_print ("total srcs %d - max %d\n", total_srcs, max_srcs);
351 register_bridge_object (MonoObject
*obj
)
353 HashEntry
*entry
= get_hash_entry (obj
, NULL
);
354 entry
->is_bridge
= TRUE
;
359 register_finishing_time (HashEntry
*entry
, int t
)
361 g_assert (entry
->finishing_time
< 0);
362 entry
->finishing_time
= t
;
366 object_is_live (MonoObject
**objp
)
368 MonoObject
*obj
= *objp
;
369 MonoObject
*fwd
= SGEN_OBJECT_IS_FORWARDED (obj
);
372 return mono_sgen_hash_table_lookup (&hash_table
, fwd
) == NULL
;
374 if (!mono_sgen_object_is_live (obj
))
376 return mono_sgen_hash_table_lookup (&hash_table
, obj
) == NULL
;
379 static DynArray registered_bridges
= DYN_ARRAY_PTR_STATIC_INITIALIZER
;
380 static DynArray dfs_stack
;
382 static int dsf1_passes
, dsf2_passes
;
386 #define HANDLE_PTR(ptr,obj) do { \
387 MonoObject *dst = (MonoObject*)*(ptr); \
388 if (dst && !object_is_live (&dst)) { \
389 dyn_array_ptr_push (&dfs_stack, obj_entry); \
390 dyn_array_ptr_push (&dfs_stack, get_hash_entry (dst, NULL)); \
395 dfs1 (HashEntry
*obj_entry
, HashEntry
*src
)
397 g_assert (dfs_stack
.size
== 0);
399 dyn_array_ptr_push (&dfs_stack
, src
);
400 dyn_array_ptr_push (&dfs_stack
, obj_entry
);
407 obj_entry
= dyn_array_ptr_pop (&dfs_stack
);
409 src
= dyn_array_ptr_pop (&dfs_stack
);
411 obj
= obj_entry
->obj
;
415 //g_print ("link %s -> %s\n", mono_sgen_safe_name (src->obj), mono_sgen_safe_name (obj));
416 add_source (obj_entry
, src
);
418 //g_print ("starting with %s\n", mono_sgen_safe_name (obj));
421 if (obj_entry
->is_visited
)
424 obj_entry
->is_visited
= TRUE
;
426 dyn_array_ptr_push (&dfs_stack
, obj_entry
);
427 /* NULL marks that the next entry is to be finished */
428 dyn_array_ptr_push (&dfs_stack
, NULL
);
430 #include "sgen-scan-object.h"
432 obj_entry
= dyn_array_ptr_pop (&dfs_stack
);
434 //g_print ("finish %s\n", mono_sgen_safe_name (obj_entry->obj));
435 register_finishing_time (obj_entry
, current_time
++);
437 } while (dfs_stack
.size
> 0);
441 scc_add_xref (SCC
*src
, SCC
*dst
)
443 g_assert (src
!= dst
);
444 g_assert (src
->index
!= dst
->index
);
446 if (dyn_array_int_contains (&dst
->xrefs
, src
->index
))
448 if (src
->num_bridge_entries
) {
449 dyn_array_int_merge_one (&dst
->xrefs
, src
->index
);
452 dyn_array_int_merge (&dst
->xrefs
, &src
->xrefs
);
453 for (i
= 0; i
< dst
->xrefs
.size
; ++i
)
454 g_assert (DYN_ARRAY_INT_REF (&dst
->xrefs
, i
) != dst
->index
);
459 scc_add_entry (SCC
*scc
, HashEntry
*entry
)
461 g_assert (entry
->scc_index
< 0);
462 entry
->scc_index
= scc
->index
;
463 if (entry
->is_bridge
)
464 ++scc
->num_bridge_entries
;
467 static DynArray sccs
;
468 static SCC
*current_scc
;
471 dfs2 (HashEntry
*entry
)
475 g_assert (dfs_stack
.size
== 0);
477 dyn_array_ptr_push (&dfs_stack
, entry
);
480 entry
= dyn_array_ptr_pop (&dfs_stack
);
483 if (entry
->scc_index
>= 0) {
484 if (entry
->scc_index
!= current_scc
->index
)
485 scc_add_xref (DYN_ARRAY_REF (&sccs
, entry
->scc_index
), current_scc
);
489 scc_add_entry (current_scc
, entry
);
491 for (i
= 0; i
< entry
->srcs
.size
; ++i
)
492 dyn_array_ptr_push (&dfs_stack
, DYN_ARRAY_PTR_REF (&entry
->srcs
, i
));
493 } while (dfs_stack
.size
> 0);
497 compare_hash_entries (const void *ep1
, const void *ep2
)
499 HashEntry
*e1
= *(HashEntry
**)ep1
;
500 HashEntry
*e2
= *(HashEntry
**)ep2
;
501 return e2
->finishing_time
- e1
->finishing_time
;
504 static unsigned long step_1
, step_2
, step_3
, step_4
, step_5
, step_6
, step_7
, step_8
;
505 static int fist_pass_links
, second_pass_links
, sccs_links
;
506 static int max_sccs_links
= 0;
509 mono_sgen_bridge_register_finalized_object (MonoObject
*obj
)
511 g_assert (mono_sgen_need_bridge_processing ());
512 dyn_array_ptr_push (®istered_bridges
, obj
);
516 mono_sgen_bridge_reset_data (void)
518 registered_bridges
.size
= 0;
522 mono_sgen_bridge_processing_stw_step (void)
525 SGEN_TV_DECLARE (atv
);
526 SGEN_TV_DECLARE (btv
);
528 if (!registered_bridges
.size
)
531 SGEN_TV_GETTIME (btv
);
535 dyn_array_ptr_init (&dfs_stack
);
536 dyn_array_int_init (&merge_array
);
539 for (i
= 0; i
< registered_bridges
.size
; ++i
)
540 dfs1 (register_bridge_object (DYN_ARRAY_PTR_REF (®istered_bridges
, i
)), NULL
);
542 SGEN_TV_GETTIME (atv
);
543 step_2
= SGEN_TV_ELAPSED (btv
, atv
);
547 mono_sgen_bridge_processing_finish (void)
550 int num_sccs
, num_xrefs
;
551 int max_entries
, max_xrefs
;
552 int hash_table_size
, sccs_size
;
555 int num_registered_bridges
;
556 HashEntry
**all_entries
;
557 MonoGCBridgeSCC
**api_sccs
;
558 MonoGCBridgeXRef
*api_xrefs
;
559 SGEN_TV_DECLARE (atv
);
560 SGEN_TV_DECLARE (btv
);
562 if (!registered_bridges
.size
)
565 SGEN_TV_GETTIME (atv
);
567 /* alloc and fill array of all entries */
569 all_entries
= mono_sgen_alloc_internal_dynamic (sizeof (HashEntry
*) * hash_table
.num_entries
, INTERNAL_MEM_BRIDGE_DATA
);
572 SGEN_HASH_TABLE_FOREACH (&hash_table
, obj
, entry
) {
573 g_assert (entry
->finishing_time
>= 0);
574 all_entries
[j
++] = entry
;
575 fist_pass_links
+= entry
->srcs
.size
;
576 } SGEN_HASH_TABLE_FOREACH_END
;
577 g_assert (j
== hash_table
.num_entries
);
578 hash_table_size
= hash_table
.num_entries
;
580 /* sort array according to decreasing finishing time */
582 qsort (all_entries
, hash_table
.num_entries
, sizeof (HashEntry
*), compare_hash_entries
);
584 SGEN_TV_GETTIME (btv
);
585 step_3
= SGEN_TV_ELAPSED (atv
, btv
);
587 /* second DFS pass */
589 dyn_array_init (&sccs
, sizeof (SCC
));
590 for (i
= 0; i
< hash_table
.num_entries
; ++i
) {
591 HashEntry
*entry
= all_entries
[i
];
592 if (entry
->scc_index
< 0) {
593 int index
= sccs
.size
;
594 current_scc
= dyn_array_add (&sccs
);
595 current_scc
->index
= index
;
596 current_scc
->num_bridge_entries
= 0;
597 current_scc
->api_index
= -1;
598 dyn_array_int_init (¤t_scc
->xrefs
);
604 sccs_size
= sccs
.size
;
606 for (i
= 0; i
< hash_table
.num_entries
; ++i
) {
607 HashEntry
*entry
= all_entries
[i
];
608 second_pass_links
+= entry
->srcs
.size
;
611 SGEN_TV_GETTIME (atv
);
612 step_4
= SGEN_TV_ELAPSED (btv
, atv
);
614 //g_print ("%d sccs\n", sccs.size);
616 dyn_array_uninit (&dfs_stack
);
618 /* init data for callback */
621 for (i
= 0; i
< sccs
.size
; ++i
) {
622 SCC
*scc
= DYN_ARRAY_REF (&sccs
, i
);
623 g_assert (scc
->index
== i
);
624 if (scc
->num_bridge_entries
)
626 sccs_links
+= scc
->xrefs
.size
;
627 max_sccs_links
= MAX (max_sccs_links
, scc
->xrefs
.size
);
630 api_sccs
= mono_sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC
*) * num_sccs
, INTERNAL_MEM_BRIDGE_DATA
);
633 for (i
= 0; i
< sccs
.size
; ++i
) {
634 SCC
*scc
= DYN_ARRAY_REF (&sccs
, i
);
635 if (!scc
->num_bridge_entries
)
638 api_sccs
[j
] = mono_sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeSCC
) + sizeof (MonoObject
*) * scc
->num_bridge_entries
, INTERNAL_MEM_BRIDGE_DATA
);
639 api_sccs
[j
]->num_objs
= scc
->num_bridge_entries
;
640 scc
->num_bridge_entries
= 0;
641 scc
->api_index
= j
++;
643 num_xrefs
+= scc
->xrefs
.size
;
646 SGEN_HASH_TABLE_FOREACH (&hash_table
, obj
, entry
) {
647 if (entry
->is_bridge
) {
648 SCC
*scc
= DYN_ARRAY_REF (&sccs
, entry
->scc_index
);
649 api_sccs
[scc
->api_index
]->objs
[scc
->num_bridge_entries
++] = entry
->obj
;
651 } SGEN_HASH_TABLE_FOREACH_END
;
653 api_xrefs
= mono_sgen_alloc_internal_dynamic (sizeof (MonoGCBridgeXRef
) * num_xrefs
, INTERNAL_MEM_BRIDGE_DATA
);
655 for (i
= 0; i
< sccs
.size
; ++i
) {
657 SCC
*scc
= DYN_ARRAY_REF (&sccs
, i
);
658 if (!scc
->num_bridge_entries
)
660 for (k
= 0; k
< scc
->xrefs
.size
; ++k
) {
661 SCC
*src_scc
= DYN_ARRAY_REF (&sccs
, DYN_ARRAY_INT_REF (&scc
->xrefs
, k
));
662 if (!src_scc
->num_bridge_entries
)
664 api_xrefs
[j
].src_scc_index
= src_scc
->api_index
;
665 api_xrefs
[j
].dst_scc_index
= scc
->api_index
;
670 SGEN_TV_GETTIME (btv
);
671 step_5
= SGEN_TV_ELAPSED (atv
, btv
);
676 max_entries
= max_xrefs
= 0;
677 for (i
= 0; i
< sccs
.size
; ++i
) {
678 SCC
*scc
= DYN_ARRAY_REF (&sccs
, i
);
679 if (scc
->num_bridge_entries
)
681 if (scc
->num_bridge_entries
> max_entries
)
682 max_entries
= scc
->num_bridge_entries
;
683 if (scc
->xrefs
.size
> max_xrefs
)
684 max_xrefs
= scc
->xrefs
.size
;
685 dyn_array_uninit (&scc
->xrefs
);
688 dyn_array_uninit (&sccs
);
690 mono_sgen_free_internal_dynamic (all_entries
, sizeof (HashEntry
*) * hash_table
.num_entries
, INTERNAL_MEM_BRIDGE_DATA
);
693 /* Empty the registered bridges array */
694 num_registered_bridges
= registered_bridges
.size
;
695 registered_bridges
.size
= 0;
697 SGEN_TV_GETTIME (atv
);
698 step_6
= SGEN_TV_ELAPSED (btv
, atv
);
700 //g_print ("%d sccs containing bridges - %d max bridge objects - %d max xrefs\n", j, max_entries, max_xrefs);
704 bridge_callbacks
.cross_references (num_sccs
, api_sccs
, num_xrefs
, api_xrefs
);
706 /*Release for finalization those objects we no longer care. */
707 SGEN_TV_GETTIME (btv
);
708 step_7
= SGEN_TV_ELAPSED (atv
, btv
);
710 for (i
= 0; i
< num_sccs
; ++i
) {
711 if (!api_sccs
[i
]->objs
[0])
713 for (j
= 0; j
< api_sccs
[i
]->num_objs
; ++j
)
714 mono_sgen_mark_bridge_object (api_sccs
[i
]->objs
[j
]);
717 /* free callback data */
719 for (i
= 0; i
< num_sccs
; ++i
) {
720 mono_sgen_free_internal_dynamic (api_sccs
[i
],
721 sizeof (MonoGCBridgeSCC
) + sizeof (MonoObject
*) * api_sccs
[i
]->num_objs
,
722 INTERNAL_MEM_BRIDGE_DATA
);
724 mono_sgen_free_internal_dynamic (api_sccs
, sizeof (MonoGCBridgeSCC
*) * num_sccs
, INTERNAL_MEM_BRIDGE_DATA
);
726 mono_sgen_free_internal_dynamic (api_xrefs
, sizeof (MonoGCBridgeXRef
) * num_xrefs
, INTERNAL_MEM_BRIDGE_DATA
);
728 SGEN_TV_GETTIME (atv
);
729 step_8
= SGEN_TV_ELAPSED (btv
, atv
);
731 mono_trace (G_LOG_LEVEL_INFO
, MONO_TRACE_GC
, "GC_BRIDGE num-objects %d num_hash_entries %d sccs size %d init %.2fms df1 %.2fms sort %.2fms dfs2 %.2fms setup-cb %.2fms free-data %.2fms user-cb %.2fms clenanup %.2fms links %d/%d/%d/%d dfs passes %d/%d",
732 num_registered_bridges
, hash_table_size
, sccs
.size
,
741 fist_pass_links
, second_pass_links
, sccs_links
, max_sccs_links
,
742 dsf1_passes
, dsf2_passes
);
744 step_1
= 0; /* We must cleanup since this value is used as an accumulator. */
747 static const char *bridge_class
;
750 bridge_test_is_bridge_object (MonoObject
*obj
)
752 return !strcmp (bridge_class
, obj
->vtable
->klass
->name
);
756 bridge_test_cross_reference (int num_sccs
, MonoGCBridgeSCC
**sccs
, int num_xrefs
, MonoGCBridgeXRef
*xrefs
)
759 for (i
= 0; i
< num_sccs
; ++i
) {
761 // g_print ("--- SCC %d\n", i);
762 for (j
= 0; j
< sccs
[i
]->num_objs
; ++j
) {
763 // g_print (" %s\n", mono_sgen_safe_name (sccs [i]->objs [j]));
764 if (i
& 1) /*retain half of the bridged objects */
765 sccs
[i
]->objs
[0] = NULL
;
768 for (i
= 0; i
< num_xrefs
; ++i
) {
769 g_assert (xrefs
[i
].src_scc_index
>= 0 && xrefs
[i
].src_scc_index
< num_sccs
);
770 g_assert (xrefs
[i
].dst_scc_index
>= 0 && xrefs
[i
].dst_scc_index
< num_sccs
);
771 // g_print ("%d -> %d\n", xrefs [i].src_scc_index, xrefs [i].dst_scc_index);
777 mono_sgen_register_test_bridge_callbacks (const char *bridge_class_name
)
779 MonoGCBridgeCallbacks callbacks
;
780 callbacks
.is_bridge_object
= bridge_test_is_bridge_object
;
781 callbacks
.cross_references
= bridge_test_cross_reference
;
782 mono_gc_register_bridge_callbacks (&callbacks
);
783 bridge_class
= bridge_class_name
;