#include "metadata/sgen-protocol.h"
#include "metadata/sgen-memory-governor.h"
#include "metadata/sgen-pinning.h"
+#include "metadata/threadpool-internals.h"
#define LOAD_VTABLE SGEN_LOAD_VTABLE
#undef HANDLE_PTR
#define HANDLE_PTR(ptr,obj) do { \
if (*(ptr) && sgen_ptr_in_nursery ((char*)*(ptr))) { \
- if (!sgen_get_remset ()->find_address ((char*)(ptr))) { \
+ if (!sgen_get_remset ()->find_address ((char*)(ptr)) && !sgen_cement_lookup (*(ptr))) { \
SGEN_LOG (0, "Oldspace->newspace reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
- binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
+ binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
if (!object_is_pinned (*(ptr))) \
missing_remsets = TRUE; \
} \
SGEN_LOG (1, "Begin heap consistency check...");
// Check that oldspace->newspace pointers are registered with the collector
- major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
+ major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_consistency_callback, NULL);
sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_consistency_callback, NULL);
if (*(ptr) && !sgen_ptr_in_nursery ((char*)*(ptr)) && !is_major_or_los_object_marked ((char*)*(ptr))) { \
if (!sgen_get_remset ()->find_address_with_cards (start, cards, (char*)(ptr))) { \
SGEN_LOG (0, "major->major reference %p at offset %td in object %p (%s.%s) not found in remsets.", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
- binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
+ binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (int) ((char*)(ptr) - (char*)(obj)), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
+ missing_remsets = TRUE; \
} \
} \
} while (0)
{
missing_remsets = FALSE;
- major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_mod_union_callback, (void*)FALSE);
+ major_collector.iterate_objects (ITERATE_OBJECTS_ALL, (IterateObjectCallbackFunc)check_mod_union_callback, (void*)FALSE);
sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_mod_union_callback, (void*)TRUE);
void
sgen_check_major_refs (void)
{
- major_collector.iterate_objects (TRUE, TRUE, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
+ major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)check_major_refs_callback, NULL);
sgen_los_iterate_objects ((IterateObjectCallbackFunc)check_major_refs_callback, NULL);
}
broken_heap = FALSE;
sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned, FALSE);
- major_collector.iterate_objects (TRUE, TRUE, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
+ major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
sgen_los_iterate_objects (verify_object_pointers_callback, (void*) (size_t) allow_missing_pinned);
g_assert (!broken_heap);
{
setup_valid_nursery_objects ();
- major_collector.iterate_objects (TRUE, TRUE, check_marked_callback, (void*)FALSE);
+ major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, check_marked_callback, (void*)FALSE);
sgen_los_iterate_objects (check_marked_callback, (void*)TRUE);
}
(IterateObjectCallbackFunc)check_nursery_objects_pinned_callback, (void*) (size_t) pinned /* (void*)&ctx */, FALSE);
}
+static gboolean scan_object_for_specific_ref_precise = TRUE;
+
+#undef HANDLE_PTR
+#define HANDLE_PTR(ptr,obj) do { \
+ if ((MonoObject*)*(ptr) == key) { \
+ g_print ("found ref to %p in object %p (%s) at offset %td\n", \
+ key, (obj), sgen_safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
+ } \
+ } while (0)
+
+static void
+scan_object_for_specific_ref (char *start, MonoObject *key)
+{
+ char *forwarded;
+
+ if ((forwarded = SGEN_OBJECT_IS_FORWARDED (start)))
+ start = forwarded;
+
+ if (scan_object_for_specific_ref_precise) {
+ #include "sgen-scan-object.h"
+ } else {
+ mword *words = (mword*)start;
+ size_t size = safe_object_get_size ((MonoObject*)start);
+ int i;
+ for (i = 0; i < size / sizeof (mword); ++i) {
+ if (words [i] == (mword)key) {
+ g_print ("found possible ref to %p in object %p (%s) at offset %td\n",
+ key, start, sgen_safe_name (start), i * sizeof (mword));
+ }
+ }
+ }
+}
+
+static void
+scan_object_for_specific_ref_callback (char *obj, size_t size, MonoObject *key)
+{
+ scan_object_for_specific_ref (obj, key);
+}
+
+static void
+check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
+{
+ if (key != obj)
+ return;
+ g_print ("found ref to %p in root record %p\n", key, root);
+}
+
+static MonoObject *check_key = NULL;
+static RootRecord *check_root = NULL;
+
+static void
+check_root_obj_specific_ref_from_marker (void **obj, void *gc_data)
+{
+ check_root_obj_specific_ref (check_root, check_key, *obj);
+}
+
+static void
+scan_roots_for_specific_ref (MonoObject *key, int root_type)
+{
+ void **start_root;
+ RootRecord *root;
+ check_key = key;
+
+ SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
+ mword desc = root->root_desc;
+
+ check_root = root;
+
+ switch (desc & ROOT_DESC_TYPE_MASK) {
+ case ROOT_DESC_BITMAP:
+ desc >>= ROOT_DESC_TYPE_SHIFT;
+ while (desc) {
+ if (desc & 1)
+ check_root_obj_specific_ref (root, key, *start_root);
+ desc >>= 1;
+ start_root++;
+ }
+ return;
+ case ROOT_DESC_COMPLEX: {
+ gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
+ int bwords = (int) ((*bitmap_data) - 1);
+ void **start_run = start_root;
+ bitmap_data++;
+ while (bwords-- > 0) {
+ gsize bmap = *bitmap_data++;
+ void **objptr = start_run;
+ while (bmap) {
+ if (bmap & 1)
+ check_root_obj_specific_ref (root, key, *objptr);
+ bmap >>= 1;
+ ++objptr;
+ }
+ start_run += GC_BITS_PER_WORD;
+ }
+ break;
+ }
+ case ROOT_DESC_USER: {
+ MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
+ marker (start_root, check_root_obj_specific_ref_from_marker, NULL);
+ break;
+ }
+ case ROOT_DESC_RUN_LEN:
+ g_assert_not_reached ();
+ default:
+ g_assert_not_reached ();
+ }
+ } SGEN_HASH_TABLE_FOREACH_END;
+
+ check_key = NULL;
+ check_root = NULL;
+}
+
+void
+mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise)
+{
+ void **ptr;
+ RootRecord *root;
+
+ scan_object_for_specific_ref_precise = precise;
+
+ sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
+ (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key, TRUE);
+
+ major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
+
+ sgen_los_iterate_objects ((IterateObjectCallbackFunc)scan_object_for_specific_ref_callback, key);
+
+ scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
+ scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
+
+ SGEN_HASH_TABLE_FOREACH (&roots_hash [ROOT_TYPE_PINNED], ptr, root) {
+ while (ptr < (void**)root->end_root) {
+ check_root_obj_specific_ref (root, *ptr, key);
+ ++ptr;
+ }
+ } SGEN_HASH_TABLE_FOREACH_END;
+}
+
+static MonoDomain *check_domain = NULL;
+
+static void
+check_obj_not_in_domain (void **o)
+{
+ g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
+}
+
+
+static void
+check_obj_not_in_domain_callback (void **o, void *gc_data)
+{
+ g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
+}
+
+void
+sgen_scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
+{
+ void **start_root;
+ RootRecord *root;
+ check_domain = domain;
+ SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
+ mword desc = root->root_desc;
+
+ /* The MonoDomain struct is allowed to hold
+ references to objects in its own domain. */
+ if (start_root == (void**)domain)
+ continue;
+
+ switch (desc & ROOT_DESC_TYPE_MASK) {
+ case ROOT_DESC_BITMAP:
+ desc >>= ROOT_DESC_TYPE_SHIFT;
+ while (desc) {
+ if ((desc & 1) && *start_root)
+ check_obj_not_in_domain (*start_root);
+ desc >>= 1;
+ start_root++;
+ }
+ break;
+ case ROOT_DESC_COMPLEX: {
+ gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
+ int bwords = (int)((*bitmap_data) - 1);
+ void **start_run = start_root;
+ bitmap_data++;
+ while (bwords-- > 0) {
+ gsize bmap = *bitmap_data++;
+ void **objptr = start_run;
+ while (bmap) {
+ if ((bmap & 1) && *objptr)
+ check_obj_not_in_domain (*objptr);
+ bmap >>= 1;
+ ++objptr;
+ }
+ start_run += GC_BITS_PER_WORD;
+ }
+ break;
+ }
+ case ROOT_DESC_USER: {
+ MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
+ marker (start_root, check_obj_not_in_domain_callback, NULL);
+ break;
+ }
+ case ROOT_DESC_RUN_LEN:
+ g_assert_not_reached ();
+ default:
+ g_assert_not_reached ();
+ }
+ } SGEN_HASH_TABLE_FOREACH_END;
+
+ check_domain = NULL;
+}
+
+static gboolean
+is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
+{
+ MonoObject *o = (MonoObject*)(obj);
+ MonoObject *ref = (MonoObject*)*(ptr);
+ size_t offset = (char*)(ptr) - (char*)o;
+
+ if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
+ return TRUE;
+ if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
+ return TRUE;
+
+#ifndef DISABLE_REMOTING
+ if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (o->vtable->klass, mono_defaults.real_proxy_class) &&
+ offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
+ return TRUE;
+#endif
+ /* Thread.cached_culture_info */
+ if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
+ !strcmp (ref->vtable->klass->name, "CultureInfo") &&
+ !strcmp(o->vtable->klass->name_space, "System") &&
+ !strcmp(o->vtable->klass->name, "Object[]"))
+ return TRUE;
+ /*
+ * at System.IO.MemoryStream.InternalConstructor (byte[],int,int,bool,bool) [0x0004d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:121
+ * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
+ * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
+ * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
+ * at System.Runtime.Remoting.Messaging.MethodCall..ctor (System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/MethodCall.cs:87
+ * at System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) [0x00018] in /home/schani/Work/novell/trunk/mcs/class/corlib/System/AppDomain.cs:1213
+ * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
+ * at System.Runtime.Remoting.Channels.CrossAppDomainSink.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00008] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Channels/CrossAppDomainChannel.cs:198
+ * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
+ */
+ if (!strcmp (ref->vtable->klass->name_space, "System") &&
+ !strcmp (ref->vtable->klass->name, "Byte[]") &&
+ !strcmp (o->vtable->klass->name_space, "System.IO") &&
+ !strcmp (o->vtable->klass->name, "MemoryStream"))
+ return TRUE;
+ /* append_job() in threadpool.c */
+ if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
+ !strcmp (ref->vtable->klass->name, "AsyncResult") &&
+ !strcmp (o->vtable->klass->name_space, "System") &&
+ !strcmp (o->vtable->klass->name, "Object[]") &&
+ mono_thread_pool_is_queue_array ((MonoArray*) o))
+ return TRUE;
+ return FALSE;
+}
+
+static void
+check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
+{
+ MonoObject *o = (MonoObject*)(obj);
+ MonoObject *ref = (MonoObject*)*(ptr);
+ size_t offset = (char*)(ptr) - (char*)o;
+ MonoClass *class;
+ MonoClassField *field;
+ char *str;
+
+ if (!ref || ref->vtable->domain == domain)
+ return;
+ if (is_xdomain_ref_allowed (ptr, obj, domain))
+ return;
+
+ field = NULL;
+ for (class = o->vtable->klass; class; class = class->parent) {
+ int i;
+
+ for (i = 0; i < class->field.count; ++i) {
+ if (class->fields[i].offset == offset) {
+ field = &class->fields[i];
+ break;
+ }
+ }
+ if (field)
+ break;
+ }
+
+ if (ref->vtable->klass == mono_defaults.string_class)
+ str = mono_string_to_utf8 ((MonoString*)ref);
+ else
+ str = NULL;
+ g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s) - pointed to by:\n",
+ o, o->vtable->klass->name_space, o->vtable->klass->name,
+ offset, field ? field->name : "",
+ ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
+ mono_gc_scan_for_specific_ref (o, TRUE);
+ if (str)
+ g_free (str);
+}
+
+#undef HANDLE_PTR
+#define HANDLE_PTR(ptr,obj) check_reference_for_xdomain ((ptr), (obj), domain)
+
+static void
+scan_object_for_xdomain_refs (char *start, mword size, void *data)
+{
+ MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
+
+ #include "sgen-scan-object.h"
+}
+
+void
+sgen_check_for_xdomain_refs (void)
+{
+ LOSObject *bigobj;
+
+ sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
+ (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL, FALSE);
+
+ major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)scan_object_for_xdomain_refs, NULL);
+
+ for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
+ scan_object_for_xdomain_refs (bigobj->data, sgen_los_object_size (bigobj), NULL);
+}
+
+static int
+compare_xrefs (const void *a_ptr, const void *b_ptr)
+{
+ const MonoGCBridgeXRef *a = a_ptr;
+ const MonoGCBridgeXRef *b = b_ptr;
+
+ if (a->src_scc_index < b->src_scc_index)
+ return -1;
+ if (a->src_scc_index > b->src_scc_index)
+ return 1;
+
+ if (a->dst_scc_index < b->dst_scc_index)
+ return -1;
+ if (a->dst_scc_index > b->dst_scc_index)
+ return 1;
+
+ return 0;
+}
+
+/*
+static void
+dump_processor_state (SgenBridgeProcessor *p)
+{
+ int i;
+
+ printf ("------\n");
+ printf ("SCCS %d\n", p->num_sccs);
+ for (i = 0; i < p->num_sccs; ++i) {
+ int j;
+ MonoGCBridgeSCC *scc = p->api_sccs [i];
+ printf ("\tSCC %d:", i);
+ for (j = 0; j < scc->num_objs; ++j) {
+ MonoObject *obj = scc->objs [j];
+ printf (" %p", obj);
+ }
+ printf ("\n");
+ }
+
+ printf ("XREFS %d\n", p->num_xrefs);
+ for (i = 0; i < p->num_xrefs; ++i)
+ printf ("\t%d -> %d\n", p->api_xrefs [i].src_scc_index, p->api_xrefs [i].dst_scc_index);
+
+ printf ("-------\n");
+}
+*/
+
+gboolean
+sgen_compare_bridge_processor_results (SgenBridgeProcessor *a, SgenBridgeProcessor *b)
+{
+ int i;
+ SgenHashTable obj_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), mono_aligned_addr_hash, NULL);
+ SgenHashTable b_scc_to_a_scc = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_BRIDGE_DEBUG, INTERNAL_MEM_BRIDGE_DEBUG, sizeof (int), g_direct_hash, NULL);
+ MonoGCBridgeXRef *a_xrefs, *b_xrefs;
+ size_t xrefs_alloc_size;
+
+ // dump_processor_state (a);
+ // dump_processor_state (b);
+
+ if (a->num_sccs != b->num_sccs)
+ g_error ("SCCS count expected %d but got %d", a->num_sccs, b->num_sccs);
+ if (a->num_xrefs != b->num_xrefs)
+ g_error ("SCCS count expected %d but got %d", a->num_xrefs, b->num_xrefs);
+
+ /*
+ * First we build a hash of each object in `a` to its respective SCC index within
+ * `a`. Along the way we also assert that no object is more than one SCC.
+ */
+ for (i = 0; i < a->num_sccs; ++i) {
+ int j;
+ MonoGCBridgeSCC *scc = a->api_sccs [i];
+
+ g_assert (scc->num_objs > 0);
+
+ for (j = 0; j < scc->num_objs; ++j) {
+ MonoObject *obj = scc->objs [j];
+ gboolean new_entry = sgen_hash_table_replace (&obj_to_a_scc, obj, &i, NULL);
+ g_assert (new_entry);
+ }
+ }
+
+ /*
+ * Now we check whether each of the objects in `b` are in `a`, and whether the SCCs
+ * of `b` contain the same sets of objects as those of `a`.
+ *
+ * While we're doing this, build a hash table to map from `b` SCC indexes to `a` SCC
+ * indexes.
+ */
+ for (i = 0; i < b->num_sccs; ++i) {
+ MonoGCBridgeSCC *scc = b->api_sccs [i];
+ MonoGCBridgeSCC *a_scc;
+ int *a_scc_index_ptr;
+ int a_scc_index;
+ int j;
+ gboolean new_entry;
+
+ g_assert (scc->num_objs > 0);
+ a_scc_index_ptr = sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [0]);
+ g_assert (a_scc_index_ptr);
+ a_scc_index = *a_scc_index_ptr;
+
+ //g_print ("A SCC %d -> B SCC %d\n", a_scc_index, i);
+
+ a_scc = a->api_sccs [a_scc_index];
+ g_assert (a_scc->num_objs == scc->num_objs);
+
+ for (j = 1; j < scc->num_objs; ++j) {
+ a_scc_index_ptr = sgen_hash_table_lookup (&obj_to_a_scc, scc->objs [j]);
+ g_assert (a_scc_index_ptr);
+ g_assert (*a_scc_index_ptr == a_scc_index);
+ }
+
+ new_entry = sgen_hash_table_replace (&b_scc_to_a_scc, GINT_TO_POINTER (i), &a_scc_index, NULL);
+ g_assert (new_entry);
+ }
+
+ /*
+ * Finally, check that we have the same xrefs. We do this by making copies of both
+ * xref arrays, and replacing the SCC indexes in the copy for `b` with the
+ * corresponding indexes in `a`. Then we sort both arrays and assert that they're
+ * the same.
+ *
+ * At the same time, check that no xref is self-referential and that there are no
+ * duplicate ones.
+ */
+
+ xrefs_alloc_size = a->num_xrefs * sizeof (MonoGCBridgeXRef);
+ a_xrefs = sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
+ b_xrefs = sgen_alloc_internal_dynamic (xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG, TRUE);
+
+ memcpy (a_xrefs, a->api_xrefs, xrefs_alloc_size);
+ for (i = 0; i < b->num_xrefs; ++i) {
+ MonoGCBridgeXRef *xref = &b->api_xrefs [i];
+ int *scc_index_ptr;
+
+ g_assert (xref->src_scc_index != xref->dst_scc_index);
+
+ scc_index_ptr = sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->src_scc_index));
+ g_assert (scc_index_ptr);
+ b_xrefs [i].src_scc_index = *scc_index_ptr;
+
+ scc_index_ptr = sgen_hash_table_lookup (&b_scc_to_a_scc, GINT_TO_POINTER (xref->dst_scc_index));
+ g_assert (scc_index_ptr);
+ b_xrefs [i].dst_scc_index = *scc_index_ptr;
+ }
+
+ qsort (a_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
+ qsort (b_xrefs, a->num_xrefs, sizeof (MonoGCBridgeXRef), compare_xrefs);
+
+ for (i = 0; i < a->num_xrefs; ++i) {
+ g_assert (a_xrefs [i].src_scc_index == b_xrefs [i].src_scc_index);
+ g_assert (a_xrefs [i].dst_scc_index == b_xrefs [i].dst_scc_index);
+ }
+
+ sgen_hash_table_clean (&obj_to_a_scc);
+ sgen_hash_table_clean (&b_scc_to_a_scc);
+ sgen_free_internal_dynamic (a_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
+ sgen_free_internal_dynamic (b_xrefs, xrefs_alloc_size, INTERNAL_MEM_BRIDGE_DEBUG);
+
+ return TRUE;
+}
+
#endif /*HAVE_SGEN_GC*/