[sgen] Typedef for GC descriptors.
[mono.git] / mono / metadata / sgen-mono.c
index eaf64033bd0db5784fe6cfddeb8be61586b0446a..7d8e12dc8ee6dea75793d6f05c24180311ec1b3f 100644 (file)
 #include "config.h"
 #ifdef HAVE_SGEN_GC
 
-#include "metadata/sgen-gc.h"
-#include "metadata/sgen-protocol.h"
+#include "sgen/sgen-gc.h"
+#include "sgen/sgen-protocol.h"
 #include "metadata/monitor.h"
-#include "metadata/sgen-layout-stats.h"
-#include "metadata/sgen-client.h"
-#include "metadata/sgen-cardtable.h"
+#include "sgen/sgen-layout-stats.h"
+#include "sgen/sgen-client.h"
+#include "sgen/sgen-cardtable.h"
+#include "sgen/sgen-pinning.h"
 #include "metadata/marshal.h"
 #include "metadata/method-builder.h"
 #include "metadata/abi-details.h"
 #include "metadata/mono-gc.h"
 #include "metadata/runtime.h"
+#include "metadata/sgen-bridge-internal.h"
+#include "metadata/gc-internal.h"
 #include "utils/mono-memory-model.h"
 #include "utils/mono-logger-internal.h"
 
+#ifdef HEAVY_STATISTICS
+static guint64 stat_wbarrier_set_arrayref = 0;
+static guint64 stat_wbarrier_value_copy = 0;
+static guint64 stat_wbarrier_object_copy = 0;
+
+static guint64 los_marked_cards;
+static guint64 los_array_cards;
+static guint64 los_array_remsets;
+#endif
+
+/* If set, mark stacks conservatively, even if precise marking is possible */
+static gboolean conservative_stack_mark = FALSE;
 /* If set, check that there are no references to the domain left at domain unload */
 gboolean sgen_mono_xdomain_checks = FALSE;
 
+/* Functions supplied by the runtime to be called by the GC */
+static MonoGCCallbacks gc_callbacks;
+
+#ifdef HAVE_KW_THREAD
+__thread SgenThreadInfo *sgen_thread_info;
+#else
+MonoNativeTlsKey thread_info_key;
+#endif
+
 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
 
 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
@@ -59,7 +83,7 @@ ptr_on_stack (void *ptr)
        gpointer stack_start = &stack_start;
        SgenThreadInfo *info = mono_thread_info_current ();
 
-       if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
+       if (ptr >= stack_start && ptr < (gpointer)info->client_info.stack_end)
                return TRUE;
        return FALSE;
 }
@@ -78,7 +102,7 @@ static void
 scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
 {
 #define SCAN_OBJECT_NOVTABLE
-#include "sgen-scan-object.h"
+#include "sgen/sgen-scan-object.h"
 }
 #endif
 
@@ -124,7 +148,7 @@ mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
 
        HEAVY_STAT (++stat_wbarrier_object_copy);
 
-       if (sgen_ptr_in_nursery (obj) || ptr_on_stack (obj)) {
+       if (sgen_ptr_in_nursery (obj) || ptr_on_stack (obj) || !SGEN_OBJECT_HAS_REFERENCES (src)) {
                size = mono_object_class (obj)->instance_size;
                mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
                                size - sizeof (MonoObject));
@@ -154,6 +178,18 @@ mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* va
        sgen_get_remset ()->wbarrier_set_field ((GCObject*)arr, slot_ptr, value);
 }
 
+void
+mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
+{
+       mono_gc_wbarrier_set_arrayref ((MonoArray*)obj, field_ptr, value);
+}
+
+void
+mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
+{
+       sgen_wbarrier_value_copy_bitmap (_dest, _src, size, bitmap);
+}
+
 static MonoMethod *write_barrier_conc_method;
 static MonoMethod *write_barrier_noconc_method;
 
@@ -215,13 +251,6 @@ mono_gc_get_specific_write_barrier (gboolean is_concurrent)
        MonoMethod **write_barrier_method_addr;
 #ifdef MANAGED_WBARRIER
        int i, nursery_check_labels [2];
-
-#ifdef HAVE_KW_THREAD
-       int stack_end_offset = -1;
-
-       MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
-       g_assert (stack_end_offset != -1);
-#endif
 #endif
 
        // FIXME: Maybe create a separate version for ctors (the branch would be
@@ -325,10 +354,10 @@ mono_gc_get_write_barrier (void)
  */
 
 /* Vtable of the objects used to fill out nursery fragments before a collection */
-static GCVTable *array_fill_vtable;
+static GCVTable array_fill_vtable;
 
-GCVTable*
-sgen_client_get_array_fill_vtable (void)
+static GCVTable
+get_array_fill_vtable (void)
 {
        if (!array_fill_vtable) {
                static MonoClass klass;
@@ -350,7 +379,7 @@ sgen_client_get_array_fill_vtable (void)
                vtable->gc_descr = mono_gc_make_descr_for_array (TRUE, &bmap, 0, 1);
                vtable->rank = 1;
 
-               array_fill_vtable = (GCVTable*)vtable;
+               array_fill_vtable = vtable;
        }
        return array_fill_vtable;
 }
@@ -366,7 +395,7 @@ sgen_client_array_fill_range (char *start, size_t size)
        }
 
        o = (MonoArray*)start;
-       o->obj.vtable = (MonoVTable*)sgen_client_get_array_fill_vtable ();
+       o->obj.vtable = (MonoVTable*)get_array_fill_vtable ();
        /* Mark this as not a real object */
        o->obj.synchronisation = GINT_TO_POINTER (-1);
        o->bounds = NULL;
@@ -421,7 +450,7 @@ mono_gc_get_vtable_bits (MonoClass *class)
 static gboolean
 is_finalization_aware (MonoObject *obj)
 {
-       MonoVTable *vt = ((MonoVTable*)SGEN_LOAD_VTABLE (obj));
+       MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
        return (vt->gc_bits & SGEN_GC_BIT_FINALIZER_AWARE) == SGEN_GC_BIT_FINALIZER_AWARE;
 }
 
@@ -434,7 +463,7 @@ sgen_client_object_queued_for_finalization (GCObject *obj)
 #ifdef ENABLE_DTRACE
        if (G_UNLIKELY (MONO_GC_FINALIZE_ENQUEUE_ENABLED ())) {
                int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD;
-               GCVTable *vt = (GCVTable*)SGEN_LOAD_VTABLE (obj);
+               GCVTable vt = SGEN_LOAD_VTABLE (obj);
                MONO_GC_FINALIZE_ENQUEUE ((mword)obj, sgen_safe_object_get_size (obj),
                                sgen_client_vtable_get_namespace (vt), sgen_client_vtable_get_name (vt), gen,
                                sgen_client_object_has_critical_finalizer (obj));
@@ -451,6 +480,66 @@ mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
        fin_callbacks = *callbacks;
 }
 
+void
+sgen_client_run_finalize (MonoObject *obj)
+{
+       mono_gc_run_finalize (obj, NULL);
+}
+
+int
+mono_gc_invoke_finalizers (void)
+{
+       return sgen_gc_invoke_finalizers ();
+}
+
+gboolean
+mono_gc_pending_finalizers (void)
+{
+       return sgen_have_pending_finalizers ();
+}
+
+void
+sgen_client_finalize_notify (void)
+{
+       mono_gc_finalize_notify ();
+}
+
+void
+mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
+{
+       sgen_object_register_for_finalization (obj, user_data);
+}
+
+static gboolean
+object_in_domain_predicate (MonoObject *obj, void *user_data)
+{
+       MonoDomain *domain = user_data;
+       if (mono_object_domain (obj) == domain) {
+               SGEN_LOG (5, "Unregistering finalizer for object: %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
+               return TRUE;
+       }
+       return FALSE;
+}
+
+/**
+ * mono_gc_finalizers_for_domain:
+ * @domain: the unloading appdomain
+ * @out_array: output array
+ * @out_size: size of output array
+ *
+ * Store inside @out_array up to @out_size objects that belong to the unloading
+ * appdomain @domain. Returns the number of stored items. Can be called repeteadly
+ * until it returns 0.
+ * The items are removed from the finalizer data structure, so the caller is supposed
+ * to finalize them.
+ * @out_array should be on the stack to allow the GC to know the objects are still alive.
+ */
+int
+mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
+{
+       return sgen_gather_finalizers_if (object_in_domain_predicate, domain, out_array, out_size);
+}
+
 /*
  * Ephemerons
  */
@@ -459,12 +548,12 @@ typedef struct _EphemeronLinkNode EphemeronLinkNode;
 
 struct _EphemeronLinkNode {
        EphemeronLinkNode *next;
-       char *array;
+       MonoArray *array;
 };
 
 typedef struct {
-       void *key;
-       void *value;
+       GCObject *key;
+       GCObject *value;
 } Ephemeron;
 
 static EphemeronLinkNode *ephemeron_list;
@@ -505,17 +594,16 @@ sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx)
        CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
        SgenGrayQueue *queue = ctx.queue;
        EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
-       MonoArray *array;
        Ephemeron *cur, *array_end;
-       char *tombstone;
+       GCObject *tombstone;
 
        while (current) {
-               char *object = current->array;
+               MonoArray *array = current->array;
 
-               if (!sgen_is_object_alive_for_current_gen (object)) {
+               if (!sgen_is_object_alive_for_current_gen ((GCObject*)array)) {
                        EphemeronLinkNode *tmp = current;
 
-                       SGEN_LOG (5, "Dead Ephemeron array at %p", object);
+                       SGEN_LOG (5, "Dead Ephemeron array at %p", array);
 
                        if (prev)
                                prev->next = current->next;
@@ -528,23 +616,22 @@ sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx)
                        continue;
                }
 
-               copy_func ((void**)&object, queue);
-               current->array = object;
+               copy_func ((GCObject**)&array, queue);
+               current->array = array;
 
-               SGEN_LOG (5, "Clearing unreachable entries for ephemeron array at %p", object);
+               SGEN_LOG (5, "Clearing unreachable entries for ephemeron array at %p", array);
 
-               array = (MonoArray*)object;
                cur = mono_array_addr (array, Ephemeron, 0);
                array_end = cur + mono_array_length_fast (array);
-               tombstone = (char*)((MonoVTable*)SGEN_LOAD_VTABLE (object))->domain->ephemeron_tombstone;
+               tombstone = SGEN_LOAD_VTABLE ((GCObject*)array)->domain->ephemeron_tombstone;
 
                for (; cur < array_end; ++cur) {
-                       char *key = (char*)cur->key;
+                       GCObject *key = cur->key;
 
                        if (!key || key == tombstone)
                                continue;
 
-                       SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
+                       SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
                                key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
                                cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
 
@@ -571,45 +658,43 @@ sgen_client_mark_ephemerons (ScanCopyContext ctx)
        SgenGrayQueue *queue = ctx.queue;
        gboolean nothing_marked = TRUE;
        EphemeronLinkNode *current = ephemeron_list;
-       MonoArray *array;
        Ephemeron *cur, *array_end;
-       char *tombstone;
+       GCObject *tombstone;
 
        for (current = ephemeron_list; current; current = current->next) {
-               char *object = current->array;
-               SGEN_LOG (5, "Ephemeron array at %p", object);
+               MonoArray *array = current->array;
+               SGEN_LOG (5, "Ephemeron array at %p", array);
 
                /*It has to be alive*/
-               if (!sgen_is_object_alive_for_current_gen (object)) {
+               if (!sgen_is_object_alive_for_current_gen ((GCObject*)array)) {
                        SGEN_LOG (5, "\tnot reachable");
                        continue;
                }
 
-               copy_func ((void**)&object, queue);
+               copy_func ((GCObject**)&array, queue);
 
-               array = (MonoArray*)object;
                cur = mono_array_addr (array, Ephemeron, 0);
                array_end = cur + mono_array_length_fast (array);
-               tombstone = (char*)((MonoVTable*)SGEN_LOAD_VTABLE (object))->domain->ephemeron_tombstone;
+               tombstone = SGEN_LOAD_VTABLE ((GCObject*)array)->domain->ephemeron_tombstone;
 
                for (; cur < array_end; ++cur) {
-                       char *key = cur->key;
+                       GCObject *key = cur->key;
 
                        if (!key || key == tombstone)
                                continue;
 
-                       SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
+                       SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
                                key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
                                cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
 
                        if (sgen_is_object_alive_for_current_gen (key)) {
-                               char *value = cur->value;
+                               GCObject *value = cur->value;
 
-                               copy_func ((void**)&cur->key, queue);
+                               copy_func (&cur->key, queue);
                                if (value) {
                                        if (!sgen_is_object_alive_for_current_gen (value))
                                                nothing_marked = FALSE;
-                                       copy_func ((void**)&cur->value, queue);
+                                       copy_func (&cur->value, queue);
                                }
                        }
                }
@@ -631,7 +716,7 @@ mono_gc_ephemeron_array_add (MonoObject *obj)
                UNLOCK_GC;
                return FALSE;
        }
-       node->array = (char*)obj;
+       node->array = (MonoArray*)obj;
        node->next = ephemeron_list;
        ephemeron_list = node;
 
@@ -653,12 +738,12 @@ mono_gc_set_current_thread_appdomain (MonoDomain *domain)
        /* Could be called from sgen_thread_unregister () with a NULL info */
        if (domain) {
                g_assert (info);
-               info->stopped_domain = domain;
+               info->client_info.stopped_domain = domain;
        }
 }
 
 static gboolean
-need_remove_object_for_domain (char *start, MonoDomain *domain)
+need_remove_object_for_domain (GCObject *start, MonoDomain *domain)
 {
        if (mono_object_domain (start) == domain) {
                SGEN_LOG (4, "Need to cleanup object %p", start);
@@ -669,9 +754,9 @@ need_remove_object_for_domain (char *start, MonoDomain *domain)
 }
 
 static void
-process_object_for_domain_clearing (char *start, MonoDomain *domain)
+process_object_for_domain_clearing (GCObject *start, MonoDomain *domain)
 {
-       MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (start);
+       MonoVTable *vt = SGEN_LOAD_VTABLE (start);
        if (vt->klass == mono_defaults.internal_thread_class)
                g_assert (mono_object_domain (start) == mono_get_root_domain ());
        /* The object could be a proxy for an object in the domain
@@ -691,15 +776,15 @@ process_object_for_domain_clearing (char *start, MonoDomain *domain)
 }
 
 static gboolean
-clear_domain_process_object (char *obj, MonoDomain *domain)
+clear_domain_process_object (GCObject *obj, MonoDomain *domain)
 {
        gboolean remove;
 
        process_object_for_domain_clearing (obj, domain);
        remove = need_remove_object_for_domain (obj, domain);
 
-       if (remove && ((MonoObject*)obj)->synchronisation) {
-               void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
+       if (remove && obj->synchronisation) {
+               void **dislink = mono_monitor_get_object_monitor_weak_link (obj);
                if (dislink)
                        sgen_register_disappearing_link (NULL, dislink, FALSE, TRUE);
        }
@@ -708,7 +793,7 @@ clear_domain_process_object (char *obj, MonoDomain *domain)
 }
 
 static void
-clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
+clear_domain_process_minor_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
 {
        if (clear_domain_process_object (obj, domain)) {
                CANARIFY_SIZE (size);
@@ -717,20 +802,20 @@ clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *
 }
 
 static void
-clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
+clear_domain_process_major_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
 {
        clear_domain_process_object (obj, domain);
 }
 
 static void
-clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
+clear_domain_free_major_non_pinned_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
 {
        if (need_remove_object_for_domain (obj, domain))
                major_collector.free_non_pinned_object (obj, size);
 }
 
 static void
-clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
+clear_domain_free_major_pinned_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
 {
        if (need_remove_object_for_domain (obj, domain))
                major_collector.free_pinned_object (obj, size);
@@ -779,10 +864,10 @@ mono_gc_clear_domain (MonoDomain * domain)
        null_ephemerons_for_domain (domain);
 
        for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
-               sgen_null_links_for_domain (domain, i);
+               sgen_null_links_if (object_in_domain_predicate, domain, i);
 
        for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
-               sgen_remove_finalizers_for_domain (domain, i);
+               sgen_remove_finalizers_if (object_in_domain_predicate, domain, i);
 
        sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
                        (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
@@ -796,11 +881,11 @@ mono_gc_clear_domain (MonoDomain * domain)
           the first object is a proxy. */
        major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
        for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
-               clear_domain_process_object (bigobj->data, domain);
+               clear_domain_process_object ((GCObject*)bigobj->data, domain);
 
        prev = NULL;
        for (bigobj = los_object_list; bigobj;) {
-               if (need_remove_object_for_domain (bigobj->data, domain)) {
+               if (need_remove_object_for_domain ((GCObject*)bigobj->data, domain)) {
                        LOSObject *to_free = bigobj;
                        if (prev)
                                prev->next = bigobj->next;
@@ -830,11 +915,81 @@ mono_gc_clear_domain (MonoDomain * domain)
        UNLOCK_GC;
 }
 
+/*
+ * Allocation
+ */
+
+static gboolean alloc_events = FALSE;
+
+void
+mono_gc_enable_alloc_events (void)
+{
+       alloc_events = TRUE;
+}
+
+void*
+mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
+{
+       MonoObject *obj = sgen_alloc_obj (vtable, size);
+
+       if (G_UNLIKELY (alloc_events))
+               mono_profiler_allocation (obj);
+
+       return obj;
+}
+
+void*
+mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
+{
+       MonoObject *obj = sgen_alloc_obj_pinned (vtable, size);
+
+       if (G_UNLIKELY (alloc_events))
+               mono_profiler_allocation (obj);
+
+       return obj;
+}
+
+void*
+mono_gc_alloc_mature (MonoVTable *vtable)
+{
+       MonoObject *obj = sgen_alloc_obj_mature (vtable, vtable->klass->instance_size);
+
+       if (obj && G_UNLIKELY (obj->vtable->klass->has_finalize))
+               mono_object_register_finalizer (obj);
+
+       if (G_UNLIKELY (alloc_events))
+               mono_profiler_allocation (obj);
+
+       return obj;
+}
+
+void*
+mono_gc_alloc_fixed (size_t size, void *descr)
+{
+       /* FIXME: do a single allocation */
+       void *res = calloc (1, size);
+       if (!res)
+               return NULL;
+       if (!mono_gc_register_root (res, size, descr)) {
+               free (res);
+               res = NULL;
+       }
+       return res;
+}
+
+void
+mono_gc_free_fixed (void* addr)
+{
+       mono_gc_deregister_root (addr);
+       free (addr);
+}
+
 /*
  * Managed allocator
  */
 
 static MonoMethod* alloc_method_cache [ATYPE_NUM];
+static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
 static gboolean use_managed_allocator = TRUE;
 
 #ifdef MANAGED_ALLOCATION
@@ -890,7 +1045,7 @@ static gboolean use_managed_allocator = TRUE;
  * that they are executed atomically via the restart mechanism.
  */
 static MonoMethod*
-create_allocator (int atype)
+create_allocator (int atype, gboolean slowpath)
 {
        int p_var, size_var;
        guint32 slowpath_branch, max_size_branch;
@@ -899,9 +1054,9 @@ create_allocator (int atype)
        MonoMethodSignature *csig;
        static gboolean registered = FALSE;
        int tlab_next_addr_var, new_next_var;
-       int num_params, i;
        const char *name = NULL;
        AllocatorWrapperInfo *info;
+       int num_params, i;
 
        if (!registered) {
                mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
@@ -911,21 +1066,22 @@ create_allocator (int atype)
        }
 
        if (atype == ATYPE_SMALL) {
-               num_params = 2;
-               name = "AllocSmall";
+               name = slowpath ? "SlowAllocSmall" : "AllocSmall";
        } else if (atype == ATYPE_NORMAL) {
-               num_params = 1;
-               name = "Alloc";
+               name = slowpath ? "SlowAlloc" : "Alloc";
        } else if (atype == ATYPE_VECTOR) {
-               num_params = 2;
-               name = "AllocVector";
+               name = slowpath ? "SlowAllocVector" : "AllocVector";
        } else if (atype == ATYPE_STRING) {
-               num_params = 2;
-               name = "AllocString";
+               name = slowpath ? "SlowAllocString" : "AllocString";
        } else {
                g_assert_not_reached ();
        }
 
+       if (atype == ATYPE_NORMAL)
+               num_params = 1;
+       else
+               num_params = 2;
+
        csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
        if (atype == ATYPE_STRING) {
                csig->ret = &mono_defaults.string_class->byval_arg;
@@ -933,13 +1089,36 @@ create_allocator (int atype)
                csig->params [1] = &mono_defaults.int32_class->byval_arg;
        } else {
                csig->ret = &mono_defaults.object_class->byval_arg;
-               for (i = 0; i < num_params; ++i)
+               for (i = 0; i < num_params; i++)
                        csig->params [i] = &mono_defaults.int_class->byval_arg;
        }
 
        mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
 
 #ifndef DISABLE_JIT
+       if (slowpath) {
+               switch (atype) {
+               case ATYPE_NORMAL:
+               case ATYPE_SMALL:
+                       mono_mb_emit_ldarg (mb, 0);
+                       mono_mb_emit_icall (mb, mono_object_new_specific);
+                       break;
+               case ATYPE_VECTOR:
+                       mono_mb_emit_ldarg (mb, 0);
+                       mono_mb_emit_ldarg (mb, 1);
+                       mono_mb_emit_icall (mb, mono_array_new_specific);
+                       break;
+               case ATYPE_STRING:
+                       mono_mb_emit_ldarg (mb, 1);
+                       mono_mb_emit_icall (mb, mono_string_alloc);
+                       break;
+               default:
+                       g_assert_not_reached ();
+               }
+
+               goto done;
+       }
+
        size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
        if (atype == ATYPE_SMALL) {
                /* size_var = size_arg */
@@ -1195,6 +1374,8 @@ create_allocator (int atype)
 
        /* return p */
        mono_mb_emit_ldloc (mb, p_var);
+
+ done:
        mono_mb_emit_byte (mb, CEE_RET);
 #endif
 
@@ -1237,17 +1418,17 @@ mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean know
                return NULL;
        if (known_instance_size && ALIGN_TO (klass->instance_size, SGEN_ALLOC_ALIGN) >= SGEN_MAX_SMALL_OBJ_SIZE)
                return NULL;
-       if (klass->has_finalize || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
+       if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
                return NULL;
        if (klass->rank)
                return NULL;
        if (klass->byval_arg.type == MONO_TYPE_STRING)
-               return mono_gc_get_managed_allocator_by_type (ATYPE_STRING);
+               return mono_gc_get_managed_allocator_by_type (ATYPE_STRING, FALSE);
        /* Generic classes have dynamic field and can go above MAX_SMALL_OBJ_SIZE. */
        if (known_instance_size)
-               return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
+               return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL, FALSE);
        else
-               return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
+               return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL, FALSE);
 #else
        return NULL;
 #endif
@@ -1267,7 +1448,7 @@ mono_gc_get_managed_array_allocator (MonoClass *klass)
                return NULL;
        g_assert (!mono_class_has_finalizer (klass) && !mono_class_is_marshalbyref (klass));
 
-       return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
+       return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR, FALSE);
 #else
        return NULL;
 #endif
@@ -1280,10 +1461,11 @@ sgen_set_use_managed_allocator (gboolean flag)
 }
 
 MonoMethod*
-mono_gc_get_managed_allocator_by_type (int atype)
+mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
 {
 #ifdef MANAGED_ALLOCATION
        MonoMethod *res;
+       MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
 
        if (!use_managed_allocator)
                return NULL;
@@ -1291,18 +1473,18 @@ mono_gc_get_managed_allocator_by_type (int atype)
        if (!mono_runtime_has_tls_get ())
                return NULL;
 
-       res = alloc_method_cache [atype];
+       res = cache [atype];
        if (res)
                return res;
 
-       res = create_allocator (atype);
+       res = create_allocator (atype, slowpath);
        LOCK_GC;
-       if (alloc_method_cache [atype]) {
+       if (cache [atype]) {
                mono_free_method (res);
-               res = alloc_method_cache [atype];
+               res = cache [atype];
        } else {
                mono_memory_barrier ();
-               alloc_method_cache [atype] = res;
+               cache [atype] = res;
        }
        UNLOCK_GC;
 
@@ -1324,7 +1506,7 @@ sgen_is_managed_allocator (MonoMethod *method)
        int i;
 
        for (i = 0; i < ATYPE_NUM; ++i)
-               if (method == alloc_method_cache [i])
+               if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
                        return TRUE;
        return FALSE;
 }
@@ -1335,7 +1517,7 @@ sgen_has_managed_allocator (void)
        int i;
 
        for (i = 0; i < ATYPE_NUM; ++i)
-               if (alloc_method_cache [i])
+               if (alloc_method_cache [i] || slowpath_alloc_method_cache [i])
                        return TRUE;
        return FALSE;
 }
@@ -1404,19 +1586,19 @@ find_next_card (guint8 *card_data, guint8 *end)
 #define ARRAY_OBJ_INDEX(ptr,array,elem_size) (((char*)(ptr) - ((char*)(array) + G_STRUCT_OFFSET (MonoArray, vector))) / (elem_size))
 
 gboolean
-sgen_client_cardtable_scan_object (char *obj, mword block_obj_size, guint8 *cards, gboolean mod_union, ScanCopyContext ctx)
+sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, gboolean mod_union, ScanCopyContext ctx)
 {
-       MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
+       MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
        MonoClass *klass = vt->klass;
 
-       SGEN_ASSERT (0, SGEN_VTABLE_HAS_REFERENCES ((GCVTable*)vt), "Why would we ever call this on reference-free objects?");
+       SGEN_ASSERT (0, SGEN_VTABLE_HAS_REFERENCES (vt), "Why would we ever call this on reference-free objects?");
 
        if (vt->rank) {
                guint8 *card_data, *card_base;
                guint8 *card_data_end;
                char *obj_start = sgen_card_table_align_pointer (obj);
-               mword obj_size = sgen_client_par_object_get_size (vt, (GCObject*)obj);
-               char *obj_end = obj + obj_size;
+               mword obj_size = sgen_client_par_object_get_size (vt, obj);
+               char *obj_end = (char*)obj + obj_size;
                size_t card_count;
                size_t extra_idx = 0;
 
@@ -1489,7 +1671,7 @@ LOOP_HEAD:
                                        gpointer new, old = *(gpointer*)elem;
                                        if ((mod_union && old) || G_UNLIKELY (sgen_ptr_in_nursery (old))) {
                                                HEAVY_STAT (++los_array_remsets);
-                                               copy_func ((void**)elem, ctx.queue);
+                                               copy_func ((GCObject**)elem, ctx.queue);
                                                new = *(gpointer*)elem;
                                                if (G_UNLIKELY (sgen_ptr_in_nursery (new)))
                                                        sgen_add_to_global_remset (elem, new);
@@ -1530,7 +1712,7 @@ mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
 
 #ifndef DISABLE_CRITICAL_REGION
        ENTER_CRITICAL_REGION;
-       arr = sgen_try_alloc_obj_nolock ((GCVTable*)vtable, size);
+       arr = sgen_try_alloc_obj_nolock (vtable, size);
        if (arr) {
                /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
                arr->max_length = (mono_array_size_t)max_length;
@@ -1542,7 +1724,7 @@ mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
 
        LOCK_GC;
 
-       arr = sgen_alloc_obj_nolock ((GCVTable*)vtable, size);
+       arr = sgen_alloc_obj_nolock (vtable, size);
        if (G_UNLIKELY (!arr)) {
                UNLOCK_GC;
                return mono_gc_out_of_memory (size);
@@ -1553,7 +1735,10 @@ mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
        UNLOCK_GC;
 
  done:
-       SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size ((GCVTable*)vtable, (GCObject*)arr)), "Vector has incorrect size.");
+       if (G_UNLIKELY (alloc_events))
+               mono_profiler_allocation (&arr->obj);
+
+       SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Vector has incorrect size.");
        return arr;
 }
 
@@ -1569,7 +1754,7 @@ mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uint
 
 #ifndef DISABLE_CRITICAL_REGION
        ENTER_CRITICAL_REGION;
-       arr = sgen_try_alloc_obj_nolock ((GCVTable*)vtable, size);
+       arr = sgen_try_alloc_obj_nolock (vtable, size);
        if (arr) {
                /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
                arr->max_length = (mono_array_size_t)max_length;
@@ -1584,7 +1769,7 @@ mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uint
 
        LOCK_GC;
 
-       arr = sgen_alloc_obj_nolock ((GCVTable*)vtable, size);
+       arr = sgen_alloc_obj_nolock (vtable, size);
        if (G_UNLIKELY (!arr)) {
                UNLOCK_GC;
                return mono_gc_out_of_memory (size);
@@ -1598,7 +1783,10 @@ mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uint
        UNLOCK_GC;
 
  done:
-       SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size ((GCVTable*)vtable, (GCObject*)arr)), "Array has incorrect size.");
+       if (G_UNLIKELY (alloc_events))
+               mono_profiler_allocation (&arr->obj);
+
+       SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Array has incorrect size.");
        return arr;
 }
 
@@ -1613,19 +1801,19 @@ mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
 
 #ifndef DISABLE_CRITICAL_REGION
        ENTER_CRITICAL_REGION;
-       str = sgen_try_alloc_obj_nolock ((GCVTable*)vtable, size);
+       str = sgen_try_alloc_obj_nolock (vtable, size);
        if (str) {
                /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
                str->length = len;
                EXIT_CRITICAL_REGION;
-               return str;
+               goto done;
        }
        EXIT_CRITICAL_REGION;
 #endif
 
        LOCK_GC;
 
-       str = sgen_alloc_obj_nolock ((GCVTable*)vtable, size);
+       str = sgen_alloc_obj_nolock (vtable, size);
        if (G_UNLIKELY (!str)) {
                UNLOCK_GC;
                return mono_gc_out_of_memory (size);
@@ -1635,6 +1823,10 @@ mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
 
        UNLOCK_GC;
 
+ done:
+       if (G_UNLIKELY (alloc_events))
+               mono_profiler_allocation (&str->object);
+
        return str;
 }
 
@@ -1651,7 +1843,7 @@ mono_gc_set_string_length (MonoString *str, gint32 new_length)
         * the space to be reclaimed by SGen. */
 
        if (nursery_canaries_enabled () && sgen_ptr_in_nursery (str)) {
-               CHECK_CANARY_FOR_OBJECT (str);
+               CHECK_CANARY_FOR_OBJECT ((GCObject*)str);
                memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2) + CANARY_SIZE);
                memcpy (new_end + 1 , CANARY_STRING, CANARY_SIZE);
        } else {
@@ -1689,7 +1881,7 @@ add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t ex
                notify_gc_roots (report);
        report->objects [report->count] = object;
        report->root_types [report->count] = rtype;
-       report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)SGEN_LOAD_VTABLE (object))->klass;
+       report->extra_info [report->count++] = (uintptr_t)SGEN_LOAD_VTABLE (object)->klass;
 }
 
 void
@@ -1771,7 +1963,7 @@ precisely_report_roots_from (GCRootReport *report, void** start_root, void** end
                break;
        }
        case ROOT_DESC_USER: {
-               MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
+               MonoGCRootMarkFunc marker = (MonoGCRootMarkFunc)sgen_get_user_descriptor_func (desc);
                root_report = report;
                marker (start_root, single_arg_report_root, NULL);
                break;
@@ -1824,10 +2016,10 @@ sgen_client_collecting_major_1 (void)
 }
 
 void
-sgen_client_pinned_los_object (char *obj)
+sgen_client_pinned_los_object (GCObject *obj)
 {
        if (profile_roots)
-               add_profile_gc_root (&major_root_report, obj, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
+               add_profile_gc_root (&major_root_report, (char*)obj, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
 }
 
 void
@@ -1902,22 +2094,23 @@ typedef struct {
        } while (0)
 
 static void
-collect_references (HeapWalkInfo *hwi, char *start, size_t size)
+collect_references (HeapWalkInfo *hwi, GCObject *obj, size_t size)
 {
-       mword desc = sgen_obj_get_descriptor (start);
+       char *start = (char*)obj;
+       mword desc = sgen_obj_get_descriptor (obj);
 
-#include "sgen-scan-object.h"
+#include "sgen/sgen-scan-object.h"
 }
 
 static void
-walk_references (char *start, size_t size, void *data)
+walk_references (GCObject *start, size_t size, void *data)
 {
        HeapWalkInfo *hwi = data;
        hwi->called = 0;
        hwi->count = 0;
        collect_references (hwi, start, size);
        if (hwi->count || !hwi->called)
-               hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
+               hwi->callback (start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
 }
 
 /**
@@ -1961,6 +2154,105 @@ mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
  * Threads
  */
 
+void
+mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
+{
+       gc_callbacks = *callbacks;
+}
+
+MonoGCCallbacks *
+mono_gc_get_gc_callbacks ()
+{
+       return &gc_callbacks;
+}
+
+void
+sgen_client_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback)
+{
+       size_t stsize = 0;
+       guint8 *staddr = NULL;
+
+#ifndef HAVE_KW_THREAD
+       g_assert (!mono_native_tls_get_value (thread_info_key));
+       mono_native_tls_set_value (thread_info_key, info);
+#else
+       sgen_thread_info = info;
+#endif
+
+       info->client_info.skip = 0;
+       info->client_info.stopped_ip = NULL;
+       info->client_info.stopped_domain = NULL;
+
+       info->client_info.stack_start = NULL;
+
+#ifdef SGEN_POSIX_STW
+       info->client_info.stop_count = -1;
+       info->client_info.signal = 0;
+#endif
+
+       /* On win32, stack_start_limit should be 0, since the stack can grow dynamically */
+       mono_thread_info_get_stack_bounds (&staddr, &stsize);
+       if (staddr) {
+#ifndef HOST_WIN32
+               info->client_info.stack_start_limit = staddr;
+#endif
+               info->client_info.stack_end = staddr + stsize;
+       } else {
+               gsize stack_bottom = (gsize)stack_bottom_fallback;
+               stack_bottom += 4095;
+               stack_bottom &= ~4095;
+               info->client_info.stack_end = (char*)stack_bottom;
+       }
+
+#ifdef USE_MONO_CTX
+       memset (&info->client_info.ctx, 0, sizeof (MonoContext));
+#else
+       memset (&info->client_info.regs, 0, sizeof (info->client_info.regs));
+#endif
+
+       if (mono_gc_get_gc_callbacks ()->thread_attach_func)
+               info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
+
+       binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
+
+       SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->client_info.stack_end);
+}
+
+void
+sgen_client_thread_unregister (SgenThreadInfo *p)
+{
+       MonoNativeThreadId tid;
+
+#ifndef HAVE_KW_THREAD
+       mono_native_tls_set_value (thread_info_key, NULL);
+#else
+       sgen_thread_info = NULL;
+#endif
+
+       tid = mono_thread_info_get_tid (p);
+
+       if (p->client_info.info.runtime_thread)
+               mono_threads_add_joinable_thread ((gpointer)tid);
+
+       if (mono_gc_get_gc_callbacks ()->thread_detach_func) {
+               mono_gc_get_gc_callbacks ()->thread_detach_func (p->client_info.runtime_data);
+               p->client_info.runtime_data = NULL;
+       }
+
+       binary_protocol_thread_unregister ((gpointer)tid);
+       SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid);
+}
+
+void
+mono_gc_set_skip_thread (gboolean skip)
+{
+       SgenThreadInfo *info = mono_thread_info_current ();
+
+       LOCK_GC;
+       info->client_info.gc_disabled = skip;
+       UNLOCK_GC;
+}
+
 static gboolean
 is_critical_method (MonoMethod *method)
 {
@@ -1970,7 +2262,14 @@ is_critical_method (MonoMethod *method)
 static gboolean
 thread_in_critical_region (SgenThreadInfo *info)
 {
-       return info->in_critical_region;
+       return info->client_info.in_critical_region;
+}
+
+static void
+sgen_thread_attach (SgenThreadInfo *info)
+{
+       if (mono_gc_get_gc_callbacks ()->thread_attach_func && !info->client_info.runtime_data)
+               info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
 }
 
 static void
@@ -1986,12 +2285,158 @@ sgen_thread_detach (SgenThreadInfo *p)
                mono_thread_detach_internal (mono_thread_internal_current ());
 }
 
+gboolean
+mono_gc_register_thread (void *baseptr)
+{
+       return mono_thread_info_attach (baseptr) != NULL;
+}
+
+gboolean
+mono_gc_is_gc_thread (void)
+{
+       gboolean result;
+       LOCK_GC;
+       result = mono_thread_info_current () != NULL;
+       UNLOCK_GC;
+       return result;
+}
+
+void
+sgen_client_thread_register_worker (void)
+{
+       mono_thread_info_register_small_id ();
+}
+
+/* Variables holding start/end nursery so it won't have to be passed at every call */
+static void *scan_area_arg_start, *scan_area_arg_end;
+
+void
+mono_gc_conservatively_scan_area (void *start, void *end)
+{
+       sgen_conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
+}
+
+void*
+mono_gc_scan_object (void *obj, void *gc_data)
+{
+       ScanCopyContext *ctx = gc_data;
+       ctx->ops->copy_or_mark_object ((GCObject**)&obj, ctx->queue);
+       return obj;
+}
+
+/*
+ * Mark from thread stacks and registers.
+ */
+void
+sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx)
+{
+       SgenThreadInfo *info;
+
+       scan_area_arg_start = start_nursery;
+       scan_area_arg_end = end_nursery;
+
+       FOREACH_THREAD (info) {
+               int skip_reason = 0;
+               if (info->client_info.skip) {
+                       SGEN_LOG (3, "Skipping dead thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start);
+                       skip_reason = 1;
+               } else if (info->client_info.gc_disabled) {
+                       SGEN_LOG (3, "GC disabled for thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start);
+                       skip_reason = 2;
+               } else if (!mono_thread_info_is_live (info)) {
+                       SGEN_LOG (3, "Skipping non-running thread %p, range: %p-%p, size: %zd (state %x)", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start, info->client_info.info.thread_state);
+                       skip_reason = 3;
+               }
+
+               binary_protocol_scan_stack ((gpointer)mono_thread_info_get_tid (info), info->client_info.stack_start, info->client_info.stack_end, skip_reason);
+
+               if (skip_reason)
+                       continue;
+
+               g_assert (info->client_info.suspend_done);
+               SGEN_LOG (3, "Scanning thread %p, range: %p-%p, size: %zd, pinned=%zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start, sgen_get_pinned_count ());
+               if (mono_gc_get_gc_callbacks ()->thread_mark_func && !conservative_stack_mark) {
+                       mono_gc_get_gc_callbacks ()->thread_mark_func (info->client_info.runtime_data, info->client_info.stack_start, info->client_info.stack_end, precise, &ctx);
+               } else if (!precise) {
+                       if (!conservative_stack_mark) {
+                               fprintf (stderr, "Precise stack mark not supported - disabling.\n");
+                               conservative_stack_mark = TRUE;
+                       }
+                       sgen_conservatively_pin_objects_from (info->client_info.stack_start, info->client_info.stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
+               }
+
+               if (!precise) {
+#ifdef USE_MONO_CTX
+                       sgen_conservatively_pin_objects_from ((void**)&info->client_info.ctx, (void**)&info->client_info.ctx + ARCH_NUM_REGS,
+                               start_nursery, end_nursery, PIN_TYPE_STACK);
+#else
+                       sgen_conservatively_pin_objects_from ((void**)&info->client_info.regs, (void**)&info->client_info.regs + ARCH_NUM_REGS,
+                                       start_nursery, end_nursery, PIN_TYPE_STACK);
+#endif
+               }
+       } END_FOREACH_THREAD
+}
+
+/*
+ * mono_gc_set_stack_end:
+ *
+ *   Set the end of the current threads stack to STACK_END. The stack space between 
+ * STACK_END and the real end of the threads stack will not be scanned during collections.
+ */
+void
+mono_gc_set_stack_end (void *stack_end)
+{
+       SgenThreadInfo *info;
+
+       LOCK_GC;
+       info = mono_thread_info_current ();
+       if (info) {
+               SGEN_ASSERT (0, stack_end < info->client_info.stack_end, "Can only lower stack end");
+               info->client_info.stack_end = stack_end;
+       }
+       UNLOCK_GC;
+}
+
+/*
+ * Roots
+ */
+
+int
+mono_gc_register_root (char *start, size_t size, void *descr)
+{
+       return sgen_register_root (start, size, (SgenDescriptor)descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
+}
+
+int
+mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
+{
+       return sgen_register_root (start, size, (SgenDescriptor)descr, ROOT_TYPE_WBARRIER);
+}
+
+void
+mono_gc_deregister_root (char* addr)
+{
+       sgen_deregister_root (addr);
+}
+
+/*
+ * PThreads
+ */
+
+#ifndef HOST_WIN32
+int
+mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
+{
+       return pthread_create (new_thread, attr, start_routine, arg);
+}
+#endif
+
 /*
  * Miscellaneous
  */
 
 void
-sgen_client_total_allocated_heap (mword allocated_heap)
+sgen_client_total_allocated_heap_changed (size_t allocated_heap)
 {
        mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap);
 }
@@ -2063,6 +2508,127 @@ mono_gc_max_generation (void)
        return 1;
 }
 
+gboolean
+mono_gc_precise_stack_mark_enabled (void)
+{
+       return !conservative_stack_mark;
+}
+
+void
+mono_gc_collect (int generation)
+{
+       sgen_gc_collect (generation);
+}
+
+int
+mono_gc_collection_count (int generation)
+{
+       return sgen_gc_collection_count (generation);
+}
+
+int64_t
+mono_gc_get_used_size (void)
+{
+       return (int64_t)sgen_gc_get_used_size ();
+}
+
+int64_t
+mono_gc_get_heap_size (void)
+{
+       return (int64_t)sgen_gc_get_total_heap_allocation ();
+}
+
+void*
+mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
+{
+       return (void*)sgen_make_user_root_descriptor ((SgenUserRootMarkFunc)marker);
+}
+
+void*
+mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
+{
+       return (void*)SGEN_DESC_STRING;
+}
+
+void*
+mono_gc_get_nursery (int *shift_bits, size_t *size)
+{
+       *size = sgen_nursery_size;
+       *shift_bits = DEFAULT_NURSERY_BITS;
+       return sgen_get_nursery_start ();
+}
+
+int
+mono_gc_get_los_limit (void)
+{
+       return SGEN_MAX_SMALL_OBJ_SIZE;
+}
+
+void
+mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
+{
+       sgen_register_disappearing_link (obj, link_addr, track, FALSE);
+}
+
+void
+mono_gc_weak_link_remove (void **link_addr, gboolean track)
+{
+       sgen_register_disappearing_link (NULL, link_addr, track, FALSE);
+}
+
+MonoObject*
+mono_gc_weak_link_get (void **link_addr)
+{
+       return sgen_weak_link_get (link_addr);
+}
+
+gboolean
+mono_gc_set_allow_synchronous_major (gboolean flag)
+{
+       return sgen_set_allow_synchronous_major (flag);
+}
+
+void*
+mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
+{
+       void *result;
+       LOCK_INTERRUPTION;
+       result = func (data);
+       UNLOCK_INTERRUPTION;
+       return result;
+}
+
+void
+mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
+{
+       // FIXME:
+}
+
+void
+sgen_client_out_of_memory (size_t size)
+{
+       mono_gc_out_of_memory (size);
+}
+
+guint8*
+mono_gc_get_card_table (int *shift_bits, gpointer *mask)
+{
+       return sgen_get_card_table_configuration (shift_bits, mask);
+}
+
+gboolean
+mono_gc_card_table_nursery_check (void)
+{
+       return !sgen_get_major_collector ()->is_concurrent;
+}
+
+/* Negative value to remove */
+void
+mono_gc_add_memory_pressure (gint64 value)
+{
+       /* FIXME: Implement at some point? */
+}
+
 /*
  * Logging
  */
@@ -2137,24 +2703,21 @@ sgen_client_pre_collection_checks (void)
        }
 }
 
-const char*
-sgen_client_object_safe_name (GCObject *obj)
+gboolean
+sgen_client_vtable_is_inited (MonoVTable *vt)
 {
-       MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
-       return vt->klass->name;
+       return vt->klass->inited;
 }
 
 const char*
-sgen_client_vtable_get_namespace (GCVTable *gc_vtable)
+sgen_client_vtable_get_namespace (MonoVTable *vt)
 {
-       MonoVTable *vt = (MonoVTable*)gc_vtable;
        return vt->klass->name_space;
 }
 
 const char*
-sgen_client_vtable_get_name (GCVTable *gc_vtable)
+sgen_client_vtable_get_name (MonoVTable *vt)
 {
-       MonoVTable *vt = (MonoVTable*)gc_vtable;
        return vt->klass->name;
 }
 
@@ -2162,15 +2725,10 @@ sgen_client_vtable_get_name (GCVTable *gc_vtable)
  * Initialization
  */
 
-void
-sgen_client_init_early (void)
-{
-       mono_counters_init ();
-}
-
 void
 sgen_client_init (void)
 {
+       int dummy;
        MonoThreadInfoCallbacks cb;
 
        cb.thread_register = sgen_thread_register;
@@ -2179,16 +2737,73 @@ sgen_client_init (void)
        cb.thread_attach = sgen_thread_attach;
        cb.mono_method_is_critical = (gpointer)is_critical_method;
        cb.mono_thread_in_critical_region = thread_in_critical_region;
-#ifndef HOST_WIN32
-       cb.thread_exit = mono_gc_pthread_exit;
-       cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
-#endif
 
        mono_threads_init (&cb, sizeof (SgenThreadInfo));
 
+       ///* Keep this the default for now */
+       /* Precise marking is broken on all supported targets. Disable until fixed. */
+       conservative_stack_mark = TRUE;
+
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
 
        mono_sgen_init_stw ();
+
+#ifndef HAVE_KW_THREAD
+       mono_native_tls_alloc (&thread_info_key, NULL);
+#if defined(__APPLE__) || defined (HOST_WIN32)
+       /* 
+        * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
+        * where the two are the same.
+        */
+       mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, thread_info_key);
+#endif
+#else
+       {
+               int tls_offset = -1;
+               MONO_THREAD_VAR_OFFSET (sgen_thread_info, tls_offset);
+               mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, tls_offset);
+       }
+#endif
+
+       /*
+        * This needs to happen before any internal allocations because
+        * it inits the small id which is required for hazard pointer
+        * operations.
+        */
+       sgen_os_init ();
+
+       mono_gc_register_thread (&dummy);
+}
+
+gboolean
+sgen_client_handle_gc_param (const char *opt)
+{
+       if (g_str_has_prefix (opt, "stack-mark=")) {
+               opt = strchr (opt, '=') + 1;
+               if (!strcmp (opt, "precise")) {
+                       conservative_stack_mark = FALSE;
+               } else if (!strcmp (opt, "conservative")) {
+                       conservative_stack_mark = TRUE;
+               } else {
+                       sgen_env_var_error (MONO_GC_PARAMS_NAME, conservative_stack_mark ? "Using `conservative`." : "Using `precise`.",
+                                       "Invalid value `%s` for `stack-mark` option, possible values are: `precise`, `conservative`.", opt);
+               }
+       } else if (g_str_has_prefix (opt, "bridge-implementation=")) {
+               opt = strchr (opt, '=') + 1;
+               sgen_set_bridge_implementation (opt);
+       } else if (g_str_has_prefix (opt, "toggleref-test")) {
+               /* FIXME: This should probably in MONO_GC_DEBUG */
+               sgen_register_test_toggleref_callback ();
+       } else {
+               return FALSE;
+       }
+       return TRUE;
+}
+
+void
+sgen_client_print_gc_params_usage (void)
+{
+       fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
 }
 
 gboolean
@@ -2196,7 +2811,13 @@ sgen_client_handle_gc_debug (const char *opt)
 {
        if (!strcmp (opt, "xdomain-checks")) {
                sgen_mono_xdomain_checks = TRUE;
-       } else {
+       } else if (!strcmp (opt, "do-not-finalize")) {
+               do_not_finalize = TRUE;
+       } else if (!strcmp (opt, "log-finalizers")) {
+               log_finalizers = TRUE;
+       } else if (!strcmp (opt, "no-managed-allocator")) {
+               sgen_set_use_managed_allocator (FALSE);
+       } else if (!sgen_bridge_handle_gc_debug (opt)) {
                return FALSE;
        }
        return TRUE;
@@ -2206,6 +2827,66 @@ void
 sgen_client_print_gc_debug_usage (void)
 {
        fprintf (stderr, "  xdomain-checks\n");
+       fprintf (stderr, "  do-not-finalize\n");
+       fprintf (stderr, "  log-finalizers\n");
+       fprintf (stderr, "  no-managed-allocator\n");
+       sgen_bridge_print_gc_debug_usage ();
+}
+
+
+gpointer
+sgen_client_get_provenance (void)
+{
+#ifdef SGEN_OBJECT_PROVENANCE
+       MonoGCCallbacks *cb = mono_gc_get_gc_callbacks ();
+       gpointer (*get_provenance_func) (void);
+       if (!cb)
+               return NULL;
+       get_provenance_func = cb->get_provenance_func;
+       if (get_provenance_func)
+               return get_provenance_func ();
+       return NULL;
+#else
+       return NULL;
+#endif
+}
+
+void
+sgen_client_describe_invalid_pointer (GCObject *ptr)
+{
+       sgen_bridge_describe_pointer (ptr);
+}
+
+void
+mono_gc_base_init (void)
+{
+       mono_counters_init ();
+
+#ifdef HEAVY_STATISTICS
+       mono_counters_register ("los marked cards", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_marked_cards);
+       mono_counters_register ("los array cards scanned ", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_cards);
+       mono_counters_register ("los array remsets", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_remsets);
+
+       mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_arrayref);
+       mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_value_copy);
+       mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_object_copy);
+#endif
+
+       sgen_gc_init ();
+
+       if (nursery_canaries_enabled ())
+               sgen_set_use_managed_allocator (FALSE);
+}
+
+void
+mono_gc_base_cleanup (void)
+{
+}
+
+gboolean
+mono_gc_is_null (void)
+{
+       return FALSE;
 }
 
 #endif