diff --git a/mono/metadata/metadata.h b/mono/metadata/metadata.h index 542f9e108e4c9..56392a8e03170 100644 --- a/mono/metadata/metadata.h +++ b/mono/metadata/metadata.h @@ -301,7 +301,10 @@ typedef struct { struct _MonoArrayType { MonoClass *eklass; + // Number of dimensions of the array uint8_t rank; + + // Arrays recording known upper and lower index bounds for each dimension uint8_t numsizes; uint8_t numlobounds; int *sizes; diff --git a/mono/metadata/sgen-bridge-internals.h b/mono/metadata/sgen-bridge-internals.h index df6a4bb8cdee7..73d9b844c0e00 100644 --- a/mono/metadata/sgen-bridge-internals.h +++ b/mono/metadata/sgen-bridge-internals.h @@ -45,6 +45,8 @@ typedef struct { void (*register_finalized_object) (GCObject *object); void (*describe_pointer) (GCObject *object); void (*enable_accounting) (void); + + // Optional-- used for debugging void (*set_dump_prefix) (const char *prefix); /* diff --git a/mono/metadata/sgen-bridge.h b/mono/metadata/sgen-bridge.h index 38dc44637cc05..c1cb4ac1def76 100644 --- a/mono/metadata/sgen-bridge.h +++ b/mono/metadata/sgen-bridge.h @@ -9,20 +9,23 @@ * unreachable objects. We use it in monodroid to do garbage collection across * the Mono and Java heaps. * - * The client can designate some objects as "bridged", which means that they - * participate in the bridge processing step once SGen considers them + * The client (Monodroid) can designate some objects as "bridged", which means + * that they participate in the bridge processing step once SGen considers them * unreachable, i.e., dead. Bridged objects must be registered for * finalization. * * When SGen is done marking, it puts together a list of all dead bridged - * objects and then does a strongly connected component analysis over their - * object graph. That graph will usually contain non-bridged objects, too. + * objects. This is passed to the bridge processor, which does an analysis to + * simplify the graph: It replaces strongly-connected components with single + * nodes, and then removes any nodes corresponding to components which do not + * contain bridged objects. * - * The output of the SCC analysis is passed to the `cross_references()` - * callback. It is expected to set the `is_alive` flag on those strongly - * connected components that it wishes to be kept alive. Only bridged objects - * will be reported to the callback, i.e., non-bridged objects are removed from - * the callback graph. + * The output of the SCC analysis is passed to the client's `cross_references()` + * callback. This consists of 2 arrays, an array of SCCs (MonoGCBridgeSCC), + * and an array of "xrefs" (edges between SCCs, MonoGCBridgeXRef). Edges are + * encoded as pairs of "API indices", ie indexes in the SCC array. The client + * is expected to set the `is_alive` flag on those strongly connected components + * that it wishes to be kept alive. * * In monodroid each bridged object has a corresponding Java mirror object. In * the bridge callback it reifies the Mono object graph in the Java heap so that @@ -37,6 +40,10 @@ * point all links to bridged objects that don't have `is_alive` set are nulled. * Note that weak links to non-bridged objects reachable from bridged objects * are not nulled. This might be considered a bug. + * + * There are three different implementations of the bridge processor, each of + * which implements 8 callbacks (see SgenBridgeProcessor). The implementations + * differ in the algorithm they use to compute the "simplified" SCC graph. */ #ifndef _MONO_SGEN_BRIDGE_H_ diff --git a/mono/metadata/sgen-dynarray.h b/mono/metadata/sgen-dynarray.h new file mode 100644 index 0000000000000..f5a6a61be6e19 --- /dev/null +++ b/mono/metadata/sgen-dynarray.h @@ -0,0 +1,346 @@ +/* + * Copyright 2016 Xamarin, Inc. + * + * Licensed under the MIT license. See LICENSE file in the project root for full license information. + */ + + // Growable array implementation used by sgen-new-bridge and sgen-tarjan-bridge. + +typedef struct { + int size; + int capacity; /* if negative, data points to another DynArray's data */ + char *data; +} DynArray; + +/*Specializations*/ + +// IntArray supports an optimization (in sgen-new-bridge.c): If capacity is less than 0 it is a "copy" and does not own its buffer. +typedef struct { + DynArray array; +} DynIntArray; + +// PtrArray supports an optimization: If size is equal to 1 it is a "singleton" and data points to the single held item, not to a buffer. +typedef struct { + DynArray array; +} DynPtrArray; + +typedef struct { + DynArray array; +} DynSCCArray; + +static void +dyn_array_init (DynArray *da) +{ + da->size = 0; + da->capacity = 0; + da->data = NULL; +} + +static void +dyn_array_uninit (DynArray *da, int elem_size) +{ + if (da->capacity < 0) { + dyn_array_init (da); + return; + } + + if (da->capacity == 0) + return; + + sgen_free_internal_dynamic (da->data, elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA); + da->data = NULL; +} + +static void +dyn_array_empty (DynArray *da) +{ + if (da->capacity < 0) + dyn_array_init (da); + else + da->size = 0; +} + +static char * +dyn_array_ensure_capacity_internal (DynArray *da, int capacity, int elem_size) +{ + if (da->capacity <= 0) + da->capacity = 2; + while (capacity > da->capacity) + da->capacity *= 2; + + return (char *)sgen_alloc_internal_dynamic (elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA, TRUE); +} + +static void +dyn_array_ensure_capacity (DynArray *da, int capacity, int elem_size) +{ + int old_capacity = da->capacity; + char *new_data; + + g_assert (capacity > 0); + + if (capacity <= old_capacity) + return; + + new_data = dyn_array_ensure_capacity_internal (da, capacity, elem_size); + memcpy (new_data, da->data, elem_size * da->size); + if (old_capacity > 0) + sgen_free_internal_dynamic (da->data, elem_size * old_capacity, INTERNAL_MEM_BRIDGE_DATA); + da->data = new_data; +} + +static gboolean +dyn_array_is_copy (DynArray *da) +{ + return da->capacity < 0; +} + +static void +dyn_array_ensure_independent (DynArray *da, int elem_size) +{ + if (!dyn_array_is_copy (da)) + return; + dyn_array_ensure_capacity (da, da->size, elem_size); + g_assert (da->capacity > 0); +} + +static void* +dyn_array_add (DynArray *da, int elem_size) +{ + void *p; + + dyn_array_ensure_capacity (da, da->size + 1, elem_size); + + p = da->data + da->size * elem_size; + ++da->size; + return p; +} + +static void +dyn_array_copy (DynArray *dst, DynArray *src, int elem_size) +{ + dyn_array_uninit (dst, elem_size); + + if (src->size == 0) + return; + + dst->size = src->size; + dst->capacity = -1; + dst->data = src->data; +} + +/* int */ +static void +dyn_array_int_init (DynIntArray *da) +{ + dyn_array_init (&da->array); +} + +static void +dyn_array_int_uninit (DynIntArray *da) +{ + dyn_array_uninit (&da->array, sizeof (int)); +} + +static int +dyn_array_int_size (DynIntArray *da) +{ + return da->array.size; +} + +#ifdef NEW_XREFS +static void +dyn_array_int_empty (DynIntArray *da) +{ + dyn_array_empty (&da->array); +} +#endif + +static void +dyn_array_int_add (DynIntArray *da, int x) +{ + int *p = (int *)dyn_array_add (&da->array, sizeof (int)); + *p = x; +} + +static int +dyn_array_int_get (DynIntArray *da, int x) +{ + return ((int*)da->array.data)[x]; +} + +#ifdef NEW_XREFS +static void +dyn_array_int_set (DynIntArray *da, int idx, int val) +{ + ((int*)da->array.data)[idx] = val; +} +#endif + +static void +dyn_array_int_ensure_independent (DynIntArray *da) +{ + dyn_array_ensure_independent (&da->array, sizeof (int)); +} + +static void +dyn_array_int_copy (DynIntArray *dst, DynIntArray *src) +{ + dyn_array_copy (&dst->array, &src->array, sizeof (int)); +} + +static gboolean +dyn_array_int_is_copy (DynIntArray *da) +{ + return dyn_array_is_copy (&da->array); +} + +/* ptr */ + +static void +dyn_array_ptr_init (DynPtrArray *da) +{ + dyn_array_init (&da->array); +} + +static void +dyn_array_ptr_uninit (DynPtrArray *da) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) + dyn_array_ptr_init (da); + else +#endif + dyn_array_uninit (&da->array, sizeof (void*)); +} + +static int +dyn_array_ptr_size (DynPtrArray *da) +{ + return da->array.size; +} + +static void +dyn_array_ptr_empty (DynPtrArray *da) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) + dyn_array_ptr_init (da); + else +#endif + dyn_array_empty (&da->array); +} + +static void* +dyn_array_ptr_get (DynPtrArray *da, int x) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) { + g_assert (x == 0); + return da->array.data; + } +#endif + return ((void**)da->array.data)[x]; +} + +static void +dyn_array_ptr_set (DynPtrArray *da, int x, void *ptr) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) { + g_assert (x == 0); + da->array.data = ptr; + } else +#endif + { + ((void**)da->array.data)[x] = ptr; + } +} + +static void +dyn_array_ptr_add (DynPtrArray *da, void *ptr) +{ + void **p; + +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 0) { + da->array.capacity = 1; + da->array.size = 1; + p = (void**)&da->array.data; + } else if (da->array.capacity == 1) { + void *ptr0 = da->array.data; + void **p0; + dyn_array_init (&da->array); + p0 = (void **)dyn_array_add (&da->array, sizeof (void*)); + *p0 = ptr0; + p = (void **)dyn_array_add (&da->array, sizeof (void*)); + } else +#endif + { + p = (void **)dyn_array_add (&da->array, sizeof (void*)); + } + *p = ptr; +} + +#define dyn_array_ptr_push dyn_array_ptr_add + +static void* +dyn_array_ptr_pop (DynPtrArray *da) +{ + int size = da->array.size; + void *p; + g_assert (size > 0); +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (da->array.capacity == 1) { + p = dyn_array_ptr_get (da, 0); + dyn_array_init (&da->array); + } else +#endif + { + g_assert (da->array.capacity > 1); + dyn_array_ensure_independent (&da->array, sizeof (void*)); + p = dyn_array_ptr_get (da, size - 1); + --da->array.size; + } + return p; +} + +static void +dyn_array_ptr_ensure_capacity (DynPtrArray *da, int capacity) +{ +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (capacity == 1 && da->array.capacity < 1) { + da->array.capacity = 1; + } else if (da->array.capacity == 1) // TODO size==1 + { + if (capacity > 1) + { + void *ptr = dyn_array_ptr_get (da, 0); + da->array.data = dyn_array_ensure_capacity_internal(&da->array, capacity, sizeof (void*)); + dyn_array_ptr_set (da, 0, ptr); + } + } +#endif + { + dyn_array_ensure_capacity (&da->array, capacity, sizeof (void*)); + } +} + +static void +dyn_array_ptr_set_all (DynPtrArray *dst, DynPtrArray *src) +{ + const int copysize = src->array.size; + if (copysize > 0) { + dyn_array_ptr_ensure_capacity (dst, copysize); + +#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY + if (copysize == 1) { + dyn_array_ptr_set (dst, 0, dyn_array_ptr_get (src, 0)); + } else +#endif + { + memcpy (dst->array.data, src->array.data, copysize * sizeof (void*)); + } + } + dst->array.size = src->array.size; +} diff --git a/mono/metadata/sgen-new-bridge.c b/mono/metadata/sgen-new-bridge.c index a1eb82a714fcd..4631c0c5430f2 100644 --- a/mono/metadata/sgen-new-bridge.c +++ b/mono/metadata/sgen-new-bridge.c @@ -24,6 +24,11 @@ #include "tabledefs.h" #include "utils/mono-logger-internals.h" +#define OPTIMIZATION_COPY +#define OPTIMIZATION_FORWARD +#define OPTIMIZATION_SINGLETON_DYN_ARRAY +#include "sgen-dynarray.h" + //#define NEW_XREFS #ifdef NEW_XREFS //#define TEST_NEW_XREFS @@ -39,31 +44,6 @@ #define XREFS old_xrefs #endif -#define OPTIMIZATION_COPY -#define OPTIMIZATION_FORWARD -#define OPTIMIZATION_SINGLETON_DYN_ARRAY - -typedef struct { - int size; - int capacity; /* if negative, data points to another DynArray's data */ - char *data; -} DynArray; - -/*Specializations*/ - -typedef struct { - DynArray array; -} DynIntArray; - -typedef struct { - DynArray array; -} DynPtrArray; - -typedef struct { - DynArray array; -} DynSCCArray; - - /* * Bridge data for a single managed object * @@ -131,266 +111,6 @@ static gboolean bridge_accounting_enabled = FALSE; static SgenBridgeProcessor *bridge_processor; /* Core functions */ -/* public */ - -/* private */ - -static void -dyn_array_init (DynArray *da) -{ - da->size = 0; - da->capacity = 0; - da->data = NULL; -} - -static void -dyn_array_uninit (DynArray *da, int elem_size) -{ - if (da->capacity < 0) { - dyn_array_init (da); - return; - } - - if (da->capacity == 0) - return; - - sgen_free_internal_dynamic (da->data, elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA); - da->data = NULL; -} - -static void -dyn_array_empty (DynArray *da) -{ - if (da->capacity < 0) - dyn_array_init (da); - else - da->size = 0; -} - -static void -dyn_array_ensure_capacity (DynArray *da, int capacity, int elem_size) -{ - int old_capacity = da->capacity; - char *new_data; - - g_assert (capacity > 0); - - if (capacity <= old_capacity) - return; - - if (old_capacity <= 0) - da->capacity = 2; - while (capacity > da->capacity) - da->capacity *= 2; - - new_data = (char *)sgen_alloc_internal_dynamic (elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA, TRUE); - memcpy (new_data, da->data, elem_size * da->size); - if (old_capacity > 0) - sgen_free_internal_dynamic (da->data, elem_size * old_capacity, INTERNAL_MEM_BRIDGE_DATA); - da->data = new_data; -} - -static gboolean -dyn_array_is_copy (DynArray *da) -{ - return da->capacity < 0; -} - -static void -dyn_array_ensure_independent (DynArray *da, int elem_size) -{ - if (!dyn_array_is_copy (da)) - return; - dyn_array_ensure_capacity (da, da->size, elem_size); - g_assert (da->capacity > 0); -} - -static void* -dyn_array_add (DynArray *da, int elem_size) -{ - void *p; - - dyn_array_ensure_capacity (da, da->size + 1, elem_size); - - p = da->data + da->size * elem_size; - ++da->size; - return p; -} - -static void -dyn_array_copy (DynArray *dst, DynArray *src, int elem_size) -{ - dyn_array_uninit (dst, elem_size); - - if (src->size == 0) - return; - - dst->size = src->size; - dst->capacity = -1; - dst->data = src->data; -} - -/* int */ -static void -dyn_array_int_init (DynIntArray *da) -{ - dyn_array_init (&da->array); -} - -static void -dyn_array_int_uninit (DynIntArray *da) -{ - dyn_array_uninit (&da->array, sizeof (int)); -} - -static int -dyn_array_int_size (DynIntArray *da) -{ - return da->array.size; -} - -#ifdef NEW_XREFS -static void -dyn_array_int_empty (DynIntArray *da) -{ - dyn_array_empty (&da->array); -} -#endif - -static void -dyn_array_int_add (DynIntArray *da, int x) -{ - int *p = (int *)dyn_array_add (&da->array, sizeof (int)); - *p = x; -} - -static int -dyn_array_int_get (DynIntArray *da, int x) -{ - return ((int*)da->array.data)[x]; -} - -#ifdef NEW_XREFS -static void -dyn_array_int_set (DynIntArray *da, int idx, int val) -{ - ((int*)da->array.data)[idx] = val; -} -#endif - -static void -dyn_array_int_ensure_independent (DynIntArray *da) -{ - dyn_array_ensure_independent (&da->array, sizeof (int)); -} - -static void -dyn_array_int_copy (DynIntArray *dst, DynIntArray *src) -{ - dyn_array_copy (&dst->array, &src->array, sizeof (int)); -} - -static gboolean -dyn_array_int_is_copy (DynIntArray *da) -{ - return dyn_array_is_copy (&da->array); -} - -/* ptr */ - -static void -dyn_array_ptr_init (DynPtrArray *da) -{ - dyn_array_init (&da->array); -} - -static void -dyn_array_ptr_uninit (DynPtrArray *da) -{ -#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY - if (da->array.capacity == 1) - dyn_array_ptr_init (da); - else -#endif - dyn_array_uninit (&da->array, sizeof (void*)); -} - -static int -dyn_array_ptr_size (DynPtrArray *da) -{ - return da->array.size; -} - -static void -dyn_array_ptr_empty (DynPtrArray *da) -{ -#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY - if (da->array.capacity == 1) - dyn_array_ptr_init (da); - else -#endif - dyn_array_empty (&da->array); -} - -static void* -dyn_array_ptr_get (DynPtrArray *da, int x) -{ -#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY - if (da->array.capacity == 1) { - g_assert (x == 0); - return da->array.data; - } -#endif - return ((void**)da->array.data)[x]; -} - -static void -dyn_array_ptr_add (DynPtrArray *da, void *ptr) -{ - void **p; - -#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY - if (da->array.capacity == 0) { - da->array.capacity = 1; - da->array.size = 1; - p = (void**)&da->array.data; - } else if (da->array.capacity == 1) { - void *ptr0 = da->array.data; - void **p0; - dyn_array_init (&da->array); - p0 = (void **)dyn_array_add (&da->array, sizeof (void*)); - *p0 = ptr0; - p = (void **)dyn_array_add (&da->array, sizeof (void*)); - } else -#endif - { - p = (void **)dyn_array_add (&da->array, sizeof (void*)); - } - *p = ptr; -} - -#define dyn_array_ptr_push dyn_array_ptr_add - -static void* -dyn_array_ptr_pop (DynPtrArray *da) -{ - int size = da->array.size; - void *p; - g_assert (size > 0); -#ifdef OPTIMIZATION_SINGLETON_DYN_ARRAY - if (da->array.capacity == 1) { - p = dyn_array_ptr_get (da, 0); - dyn_array_init (&da->array); - } else -#endif - { - g_assert (da->array.capacity > 1); - dyn_array_ensure_independent (&da->array, sizeof (void*)); - p = dyn_array_ptr_get (da, size - 1); - --da->array.size; - } - return p; -} /*SCC */ diff --git a/mono/metadata/sgen-tarjan-bridge.c b/mono/metadata/sgen-tarjan-bridge.c index 66dff8d1d1c35..76c5ac64a90d1 100644 --- a/mono/metadata/sgen-tarjan-bridge.c +++ b/mono/metadata/sgen-tarjan-bridge.c @@ -24,126 +24,22 @@ #include "tabledefs.h" #include "utils/mono-logger-internals.h" -typedef struct { - int size; - int capacity; - char *data; -} DynArray; - -/*Specializations*/ - -typedef struct { - DynArray array; -} DynPtrArray; - -/* private */ - -static void -dyn_array_uninit (DynArray *da, int elem_size) -{ - if (da->capacity <= 0) - return; +#include "sgen-dynarray.h" - sgen_free_internal_dynamic (da->data, elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA); - da->data = NULL; -} - -static void -dyn_array_ensure_capacity (DynArray *da, int capacity, int elem_size) -{ - int old_capacity = da->capacity; - char *new_data; - - if (capacity <= old_capacity) - return; - - if (da->capacity == 0) - da->capacity = 2; - while (capacity > da->capacity) - da->capacity *= 2; - - new_data = (char *)sgen_alloc_internal_dynamic (elem_size * da->capacity, INTERNAL_MEM_BRIDGE_DATA, TRUE); - if (da->data) { - memcpy (new_data, da->data, elem_size * da->size); - sgen_free_internal_dynamic (da->data, elem_size * old_capacity, INTERNAL_MEM_BRIDGE_DATA); - } - da->data = new_data; -} - -static void* -dyn_array_add (DynArray *da, int elem_size) -{ - void *p; - - dyn_array_ensure_capacity (da, da->size + 1, elem_size); - - p = da->data + da->size * elem_size; - ++da->size; - return p; -} - -/* ptr */ - -static void -dyn_array_ptr_uninit (DynPtrArray *da) -{ - dyn_array_uninit (&da->array, sizeof (void*)); -} - -static int -dyn_array_ptr_size (DynPtrArray *da) -{ - return da->array.size; -} - -static void -dyn_array_ptr_set_size (DynPtrArray *da, int size) -{ - da->array.size = size; -} - -static void* -dyn_array_ptr_get (DynPtrArray *da, int x) -{ - return ((void**)da->array.data)[x]; -} - -static void -dyn_array_ptr_add (DynPtrArray *da, void *ptr) -{ - void **p = (void **)dyn_array_add (&da->array, sizeof (void*)); - *p = ptr; -} - -#define dyn_array_ptr_push dyn_array_ptr_add - -static void* -dyn_array_ptr_pop (DynPtrArray *da) -{ - void *p; - int size = da->array.size; - g_assert (size > 0); - p = dyn_array_ptr_get (da, size - 1); - --da->array.size; - return p; -} - -static void -dyn_array_ptr_ensure_capacity (DynPtrArray *da, int capacity) -{ - dyn_array_ensure_capacity (&da->array, capacity, sizeof (void*)); -} - - -static void -dyn_array_ptr_set_all (DynPtrArray *dst, DynPtrArray *src) -{ - if (src->array.size > 0) { - dyn_array_ptr_ensure_capacity (dst, src->array.size); - memcpy (dst->array.data, src->array.data, src->array.size * sizeof (void*)); - } - dst->array.size = src->array.size; -} +/* + * See comments in sgen-bridge.h + * + * This bridge implementation is based on the tarjan algorithm for strongly + * connected components. It has two elements: + * + * - Standard tarjan SCC algorithm to convert graph to SCC forest + * + * - "Colors": We reduce the SCC forest to bridged-SCCs-only by using a + * "color" algorithm devised by Kumpera. Consider the set of bridged SCCs + * which is reachable from a given object. We call each such unique set a + * "color". We compute the set of colors and which colors contain links to + * which colors. The color graph then becomes the reduced SCC graph. + */ static void enable_accounting (void) @@ -152,6 +48,8 @@ enable_accounting (void) // 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); } +// Is this class bridged or not, and should its dependencies be scanned or not? +// The result of this callback will be cached for use by is_opaque_object later. static MonoGCBridgeObjectKind class_kind (MonoClass *klass) { @@ -182,9 +80,10 @@ class_kind (MonoClass *klass) return GC_BRIDGE_TRANSPARENT_CLASS; } -//enable unsage logging +//enable usage logging // #define DUMP_GRAPH 1 +// ScanData state enum { INITIAL, SCANNED, @@ -197,30 +96,45 @@ enum { We can split this data structure in two, those with bridges and those without */ typedef struct { + // Colors (ColorDatas) linked to by objects with this color DynPtrArray other_colors; + // Bridge objects (GCObjects) held by objects with this color DynPtrArray bridges; int api_index : 31; unsigned visited : 1; } ColorData; - -typedef struct { - GCObject *obj; //XXX this can be eliminated. +// Represents one managed object. Equivalent of new/old bridge "HashEntry" +typedef struct _ScanData { + // FIXME this can be eliminated; if we have a ScanData we generally looked it up from its GCObject + GCObject *obj; + // We use the sgen lock_word in GCObject to store a pointer to the ScanData. Cache the original here to restore later: mword lock_word; ColorData *color; + // Tarjan algorithm index (order visited) int index; + // Tarjan index of lowest-index object known reachable from here int low_index : 27; + // See "ScanData state" enum above unsigned state : 2; unsigned is_bridge : 1; + // Similar to lock_word, we use these bits in the GCObject as scratch space then restore them when done unsigned obj_state : 2; } ScanData; +// Stacks of ScanData objects used for tarjan algorithm. +// The Tarjan algorithm is normally defined recursively; here scan_stack simulates the call stack of a recursive algorithm, +// and loop_stack is the stack structure used by the algorithm itself. +static DynPtrArray scan_stack, loop_stack; -static DynPtrArray scan_stack, loop_stack, registered_bridges; +// GCObjects on which register_finalized_object has been called +static DynPtrArray registered_bridges; + +// ColorData objects static DynPtrArray color_merge_array; static int ignored_objects; @@ -299,6 +213,7 @@ free_object_buckets (void) //ColorData buckets #define NUM_COLOR_ENTRIES ((BUCKET_SIZE - SIZEOF_VOID_P * 2) / sizeof (ColorData)) +// Same as ObjectBucket except NUM_COLOR_ENTRIES and NUM_SCAN_ENTRIES differ typedef struct _ColorBucket ColorBucket; struct _ColorBucket { ColorBucket *next; @@ -372,6 +287,8 @@ create_data (GCObject *obj) res->obj = obj; res->color = NULL; res->index = res->low_index = -1; + res->state = INITIAL; + res->is_bridge = FALSE; res->obj_state = o [0] & SGEN_VTABLE_BITS_MASK; res->lock_word = o [1]; @@ -574,6 +491,7 @@ is_opaque_object (GCObject *obj) return FALSE; } +// Called during DFS; visits one child. If it is a candidate to be scanned, pushes it to the stacks. static void push_object (GCObject *obj) { @@ -627,6 +545,7 @@ push_object (GCObject *obj) if (dst) push_object (dst); \ } while (0) +// dfs () function's queue-children-of-object operation. static void push_all (ScanData *data) { @@ -635,7 +554,7 @@ push_all (ScanData *data) mword desc = sgen_obj_get_descriptor_safe (obj); #if DUMP_GRAPH - printf ("**scanning %p %s\n", obj, safe_name_bridge (obj)); + printf ("+scanning %s (%p) index %d color %p\n", safe_name_bridge (data->obj), data->obj, data->index, data->color); #endif #include "sgen/sgen-scan-object.h" @@ -647,8 +566,8 @@ compute_low_index (ScanData *data, GCObject *obj) { ScanData *other; ColorData *cd; - obj = bridge_object_forward (obj); + obj = bridge_object_forward (obj); other = find_data (obj); #if DUMP_GRAPH @@ -723,12 +642,12 @@ create_scc (ScanData *data) #if DUMP_GRAPH printf ("|SCC rooted in %s (%p) has bridge %d\n", safe_name_bridge (data->obj), data->obj, found_bridge); printf ("\tpoints-to-colors: "); - for (i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) + for (int i = 0; i < dyn_array_ptr_size (&color_merge_array); ++i) printf ("%p ", dyn_array_ptr_get (&color_merge_array, i)); printf ("\n"); printf ("loop stack: "); - for (i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) { + for (int i = 0; i < dyn_array_ptr_size (&loop_stack); ++i) { ScanData *other = dyn_array_ptr_get (&loop_stack, i); printf ("(%d/%d)", other->index, other->low_index); } @@ -775,7 +694,7 @@ create_scc (ScanData *data) g_assert (cd->visited); cd->visited = FALSE; } - dyn_array_ptr_set_size (&color_merge_array, 0); + dyn_array_ptr_empty (&color_merge_array); found_bridge = FALSE; } @@ -785,7 +704,7 @@ dfs (void) g_assert (dyn_array_ptr_size (&scan_stack) == 1); g_assert (dyn_array_ptr_size (&loop_stack) == 0); - dyn_array_ptr_set_size (&color_merge_array, 0); + dyn_array_ptr_empty (&color_merge_array); while (dyn_array_ptr_size (&scan_stack) > 0) { ScanData *data = (ScanData *)dyn_array_ptr_pop (&scan_stack); @@ -852,15 +771,15 @@ register_finalized_object (GCObject *obj) static void reset_data (void) { - dyn_array_ptr_set_size (®istered_bridges, 0); + dyn_array_ptr_empty (®istered_bridges); } static void cleanup (void) { - dyn_array_ptr_set_size (&scan_stack, 0); - dyn_array_ptr_set_size (&loop_stack, 0); - dyn_array_ptr_set_size (®istered_bridges, 0); + dyn_array_ptr_empty (&scan_stack); + dyn_array_ptr_empty (&loop_stack); + dyn_array_ptr_empty (®istered_bridges); free_object_buckets (); free_color_buckets (); reset_cache (); @@ -951,7 +870,7 @@ processing_stw_step (void) #if defined (DUMP_GRAPH) printf ("----summary----\n"); printf ("bridges:\n"); - for (i = 0; i < bridge_count; ++i) { + for (int i = 0; i < bridge_count; ++i) { ScanData *sd = find_data (dyn_array_ptr_get (®istered_bridges, i)); printf ("\t%s (%p) index %d color %p\n", safe_name_bridge (sd->obj), sd->obj, sd->index, sd->color); } @@ -1049,7 +968,7 @@ processing_build_callback_data (int generation) if (!bridges) continue; - dyn_array_ptr_set_size (&color_merge_array, 0); + dyn_array_ptr_empty (&color_merge_array); gather_xrefs (cd); reset_xrefs (cd); dyn_array_ptr_set_all (&cd->other_colors, &color_merge_array); @@ -1089,7 +1008,7 @@ processing_build_callback_data (int generation) #if defined (DUMP_GRAPH) printf ("---xrefs:\n"); - for (i = 0; i < xref_count; ++i) + for (int i = 0; i < xref_count; ++i) printf ("\t%d -> %d\n", api_xrefs [i].src_scc_index, api_xrefs [i].dst_scc_index); #endif diff --git a/mono/sgen/sgen-gc.h b/mono/sgen/sgen-gc.h index b0ac3dbcb10d5..192c570f08b23 100644 --- a/mono/sgen/sgen-gc.h +++ b/mono/sgen/sgen-gc.h @@ -287,6 +287,7 @@ sgen_get_nursery_end (void) List of what each bit on of the vtable gc bits means. */ enum { + // When the Java bridge has determined an object is "bridged", it uses these two bits to cache that information. SGEN_GC_BIT_BRIDGE_OBJECT = 1, SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT = 2, SGEN_GC_BIT_FINALIZER_AWARE = 4, diff --git a/mono/tests/sgen-bridge-pathologies.cs b/mono/tests/sgen-bridge-pathologies.cs index 6cc184c7939d7..68fc0a5d06033 100644 --- a/mono/tests/sgen-bridge-pathologies.cs +++ b/mono/tests/sgen-bridge-pathologies.cs @@ -198,6 +198,7 @@ static void RunTest (ThreadStart setup) t.Join (); for (int i = 0; i < 5; ++i) { + Console.WriteLine("-GC {0}/5-", i); GC.Collect (); GC.WaitForPendingFinalizers (); }