Add support for value types to mono_object_to_string
[mono.git] / mono / metadata / sgen-gc.c
index 32f7cc33461f6812749a5097616086794199bf73..affe3b06729e1ffef9ebd0dc3813694b64d4708a 100644 (file)
 #ifdef HAVE_PTHREAD_H
 #include <pthread.h>
 #endif
+#ifdef HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif
 #ifdef HAVE_SEMAPHORE_H
 #include <semaphore.h>
 #endif
 #include "metadata/object-internals.h"
 #include "metadata/threads.h"
 #include "metadata/sgen-cardtable.h"
-#include "metadata/sgen-ssb.h"
 #include "metadata/sgen-protocol.h"
 #include "metadata/sgen-archdep.h"
 #include "metadata/sgen-bridge.h"
 #include "metadata/sgen-cardtable.h"
 #include "metadata/sgen-pinning.h"
 #include "metadata/sgen-workers.h"
+#include "metadata/sgen-layout-stats.h"
 #include "utils/mono-mmap.h"
 #include "utils/mono-time.h"
 #include "utils/mono-semaphore.h"
@@ -260,8 +263,12 @@ guint32 collect_before_allocs = 0;
 static gboolean whole_heap_check_before_collection = FALSE;
 /* If set, do a heap consistency check before each minor collection */
 static gboolean consistency_check_at_minor_collection = FALSE;
+/* If set, do a mod union consistency check before each finishing collection pause */
+static gboolean mod_union_consistency_check = FALSE;
 /* If set, check whether mark bits are consistent after major collections */
 static gboolean check_mark_bits_after_major_collection = FALSE;
+/* If set, check that all nursery objects are pinned/not pinned, depending on context */
+static gboolean check_nursery_objects_pinned = FALSE;
 /* If set, do a few checks when the concurrent collector is used */
 static gboolean do_concurrent_checks = FALSE;
 /* If set, check that there are no references to the domain left at domain unload */
@@ -273,6 +280,12 @@ static gboolean conservative_stack_mark = FALSE;
 /* If set, do a plausibility check on the scan_starts before and after
    each collection */
 static gboolean do_scan_starts_check = FALSE;
+/*
+ * If the major collector is concurrent and this is FALSE, we will
+ * never initiate a synchronous major collection, unless requested via
+ * GC.Collect().
+ */
+static gboolean allow_synchronous_major = TRUE;
 static gboolean nursery_collection_is_parallel = FALSE;
 static gboolean disable_minor_collections = FALSE;
 static gboolean disable_major_collections = FALSE;
@@ -299,6 +312,7 @@ long long stat_nursery_copy_object_failed_forwarded = 0;
 long long stat_nursery_copy_object_failed_pinned = 0;
 long long stat_nursery_copy_object_failed_to_space = 0;
 
+static int stat_wbarrier_add_to_global_remset = 0;
 static int stat_wbarrier_set_field = 0;
 static int stat_wbarrier_set_arrayref = 0;
 static int stat_wbarrier_arrayref_copy = 0;
@@ -386,9 +400,6 @@ sgen_safe_name (void* obj)
  * ######################################################################
  */
 LOCK_DECLARE (gc_mutex);
-static int gc_disabled = 0;
-
-static gboolean use_cardtable;
 
 #define SCAN_START_SIZE        SGEN_SCAN_START_SIZE
 
@@ -476,10 +487,7 @@ MonoNativeTlsKey thread_info_key;
 
 #ifdef HAVE_KW_THREAD
 __thread SgenThreadInfo *sgen_thread_info;
-__thread gpointer *store_remset_buffer;
-__thread long store_remset_buffer_index;
 __thread char *stack_end;
-__thread long *store_remset_buffer_index_addr;
 #endif
 
 /* The size of a TLAB */
@@ -540,7 +548,7 @@ static void report_registered_roots (void);
 
 static void pin_from_roots (void *start_nursery, void *end_nursery, GrayQueue *queue);
 static int pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery, ScanCopyContext ctx);
-static void finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue);
+static void finish_gray_stack (int generation, GrayQueue *queue);
 
 void mono_gc_scan_for_specific_ref (MonoObject *key, gboolean precise);
 
@@ -551,11 +559,12 @@ static int mark_ephemerons_in_range (ScanCopyContext ctx);
 static void clear_unreachable_ephemerons (ScanCopyContext ctx);
 static void null_ephemerons_for_domain (MonoDomain *domain);
 
+static gboolean major_update_or_finish_concurrent_collection (gboolean force_finish);
+
 SgenObjectOperations current_object_ops;
 SgenMajorCollector major_collector;
 SgenMinorCollector sgen_minor_collector;
 static GrayQueue gray_queue;
-static GrayQueue remember_major_objects_gray_queue;
 
 static SgenRemeberedSet remset;
 
@@ -572,33 +581,6 @@ sgen_workers_get_job_gray_queue (WorkerData *worker_data)
        return worker_data ? &worker_data->private_gray_queue : WORKERS_DISTRIBUTE_GRAY_QUEUE;
 }
 
-static gboolean have_non_collection_major_object_remembers = FALSE;
-
-gboolean
-sgen_remember_major_object_for_concurrent_mark (char *obj)
-{
-       if (!major_collector.is_concurrent)
-               return FALSE;
-
-       g_assert (current_collection_generation == GENERATION_NURSERY || current_collection_generation == -1);
-
-       if (!concurrent_collection_in_progress)
-               return FALSE;
-
-       GRAY_OBJECT_ENQUEUE (&remember_major_objects_gray_queue, obj);
-
-       if (current_collection_generation != GENERATION_NURSERY) {
-               /*
-                * This happens when the mutator allocates large or
-                * pinned objects or when allocating in degraded
-                * mode.
-                */
-               have_non_collection_major_object_remembers = TRUE;
-       }
-
-       return TRUE;
-}
-
 static void
 gray_queue_redirect (SgenGrayQueue *queue)
 {
@@ -625,13 +607,6 @@ gray_queue_redirect (SgenGrayQueue *queue)
        }
 }
 
-static void
-redirect_major_object_remembers (void)
-{
-       gray_queue_redirect (&remember_major_objects_gray_queue);
-       have_non_collection_major_object_remembers = FALSE;
-}
-
 static gboolean
 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
 {
@@ -643,9 +618,12 @@ is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
                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_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") &&
@@ -916,6 +894,7 @@ process_object_for_domain_clearing (char *start, MonoDomain *domain)
                g_assert (mono_object_domain (start) == mono_get_root_domain ());
        /* The object could be a proxy for an object in the domain
           we're deleting. */
+#ifndef DISABLE_REMOTING
        if (mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) {
                MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
 
@@ -926,6 +905,7 @@ process_object_for_domain_clearing (char *start, MonoDomain *domain)
                        ((MonoRealProxy*)start)->unwrapped_server = NULL;
                }
        }
+#endif
 }
 
 static MonoDomain *check_domain = NULL;
@@ -1068,6 +1048,14 @@ mono_gc_clear_domain (MonoDomain * domain)
 
        LOCK_GC;
 
+       binary_protocol_domain_unload_begin (domain);
+
+       sgen_stop_world (0);
+
+       if (concurrent_collection_in_progress)
+               sgen_perform_collection (0, GENERATION_OLD, "clear domain", TRUE);
+       g_assert (!concurrent_collection_in_progress);
+
        sgen_process_fin_stage_entries ();
        sgen_process_dislink_stage_entries ();
 
@@ -1122,11 +1110,16 @@ mono_gc_clear_domain (MonoDomain * domain)
        major_collector.iterate_objects (TRUE, FALSE, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
        major_collector.iterate_objects (FALSE, TRUE, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
 
-       if (G_UNLIKELY (do_pin_stats)) {
-               if (domain == mono_get_root_domain ())
+       if (domain == mono_get_root_domain ()) {
+               if (G_UNLIKELY (do_pin_stats))
                        sgen_pin_stats_print_class_stats ();
+               sgen_object_layout_dump (stdout);
        }
 
+       sgen_restart_world (0, NULL);
+
+       binary_protocol_domain_unload_end (domain);
+
        UNLOCK_GC;
 }
 
@@ -1142,13 +1135,38 @@ mono_gc_clear_domain (MonoDomain * domain)
 void
 sgen_add_to_global_remset (gpointer ptr, gpointer obj)
 {
+       SGEN_ASSERT (5, sgen_ptr_in_nursery (obj), "Target pointer of global remset must be in the nursery");
+
+       HEAVY_STAT (++stat_wbarrier_add_to_global_remset);
+
+       if (!major_collector.is_concurrent) {
+               SGEN_ASSERT (5, current_collection_generation != -1, "Global remsets can only be added during collections");
+       } else {
+               if (current_collection_generation == -1)
+                       SGEN_ASSERT (5, sgen_concurrent_collection_in_progress (), "Global remsets outside of collection pauses can only be added by the concurrent collector");
+       }
+
+       if (!object_is_pinned (obj))
+               SGEN_ASSERT (5, sgen_minor_collector.is_split || sgen_concurrent_collection_in_progress (), "Non-pinned objects can only remain in nursery if it is a split nursery");
+       else if (sgen_cement_lookup_or_register (obj))
+               return;
+
        remset.record_pointer (ptr);
 
+       if (G_UNLIKELY (do_pin_stats))
+               sgen_pin_stats_register_global_remset (obj);
+
+       SGEN_LOG (8, "Adding global remset for %p", ptr);
+       binary_protocol_global_remset (ptr, obj, (gpointer)SGEN_LOAD_VTABLE (obj));
+
+
+#ifdef ENABLE_DTRACE
        if (G_UNLIKELY (MONO_GC_GLOBAL_REMSET_ADD_ENABLED ())) {
                MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
                MONO_GC_GLOBAL_REMSET_ADD ((mword)ptr, (mword)obj, sgen_safe_object_get_size (obj),
                                vt->klass->name_space, vt->klass->name);
        }
+#endif
 }
 
 /*
@@ -1270,6 +1288,8 @@ pin_objects_from_addresses (GCMemSection *section, void **start, void **end, voi
                                                        binary_protocol_pin (search_start,
                                                                        (gpointer)LOAD_VTABLE (search_start),
                                                                        safe_object_get_size (search_start));
+
+#ifdef ENABLE_DTRACE
                                                        if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
                                                                int gen = sgen_ptr_in_nursery (search_start) ? GENERATION_NURSERY : GENERATION_OLD;
                                                                MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (search_start);
@@ -1277,6 +1297,7 @@ pin_objects_from_addresses (GCMemSection *section, void **start, void **end, voi
                                                                                sgen_safe_object_get_size (search_start),
                                                                                vt->klass->name_space, vt->klass->name, gen);
                                                        }
+#endif
 
                                                        pin_object (search_start);
                                                        GRAY_OBJECT_ENQUEUE (queue, search_start);
@@ -1346,11 +1367,14 @@ sgen_pin_object (void *object, GrayQueue *queue)
        }
        GRAY_OBJECT_ENQUEUE (queue, object);
        binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
+
+#ifdef ENABLE_DTRACE
        if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
                int gen = sgen_ptr_in_nursery (object) ? GENERATION_NURSERY : GENERATION_OLD;
                MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (object);
                MONO_GC_OBJ_PINNED ((mword)object, sgen_safe_object_get_size (object), vt->klass->name_space, vt->klass->name, gen);
        }
+#endif
 }
 
 void
@@ -1519,6 +1543,7 @@ unpin_objects_from_queue (SgenGrayQueue *queue)
                GRAY_OBJECT_DEQUEUE (queue, addr);
                if (!addr)
                        break;
+               g_assert (SGEN_OBJECT_IS_PINNED (addr));
                SGEN_UNPIN_OBJECT (addr);
        }
 }
@@ -1856,7 +1881,7 @@ sgen_get_current_object_ops (void){
 
 
 static void
-finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *queue)
+finish_gray_stack (int generation, GrayQueue *queue)
 {
        TV_DECLARE (atv);
        TV_DECLARE (btv);
@@ -1864,6 +1889,8 @@ finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *
        CopyOrMarkObjectFunc copy_func = current_object_ops.copy_or_mark_object;
        ScanObjectFunc scan_func = current_object_ops.scan_object;
        ScanCopyContext ctx = { scan_func, copy_func, queue };
+       char *start_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_start () : NULL;
+       char *end_addr = generation == GENERATION_NURSERY ? sgen_get_nursery_end () : (char*)-1;
 
        /*
         * We copied all the reachable objects. Now it's the time to copy
@@ -1904,8 +1931,6 @@ finish_gray_stack (char *start_addr, char *end_addr, int generation, GrayQueue *
        } while (!done_with_ephemerons);
 
        sgen_scan_togglerefs (start_addr, end_addr, ctx);
-       if (generation == GENERATION_OLD)
-               sgen_scan_togglerefs (sgen_get_nursery_start (), sgen_get_nursery_end (), ctx);
 
        if (sgen_need_bridge_processing ()) {
                sgen_collect_bridge_objects (generation, ctx);
@@ -2187,6 +2212,7 @@ init_stats (void)
        mono_counters_register ("Number of pinned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_pinned_objects);
 
 #ifdef HEAVY_STATISTICS
+       mono_counters_register ("WBarrier remember pointer", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_add_to_global_remset);
        mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
        mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
        mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
@@ -2253,7 +2279,7 @@ sgen_collection_is_concurrent (void)
        case GENERATION_NURSERY:
                return FALSE;
        case GENERATION_OLD:
-               return major_collector.is_concurrent;
+               return concurrent_collection_in_progress;
        default:
                g_error ("Invalid current generation %d", current_collection_generation);
        }
@@ -2440,13 +2466,13 @@ init_gray_queue (void)
        } else {
                sgen_gray_object_queue_init (&gray_queue, NULL);
        }
+}
 
-       if (major_collector.is_concurrent) {
-               sgen_gray_object_queue_init_with_alloc_prepare (&remember_major_objects_gray_queue, NULL,
-                               gray_queue_redirect, sgen_workers_get_distribute_section_gray_queue ());
-       } else {
-               sgen_gray_object_queue_init_invalid (&remember_major_objects_gray_queue);
-       }
+static void
+pin_stage_object_callback (char *obj, size_t size, void *data)
+{
+       sgen_pin_stage_ptr (obj);
+       /* FIXME: do pin stats if enabled */
 }
 
 /*
@@ -2454,7 +2480,7 @@ init_gray_queue (void)
  * collection.
  */
 static gboolean
-collect_nursery (SgenGrayQueue *unpin_queue)
+collect_nursery (SgenGrayQueue *unpin_queue, gboolean finish_up_concurrent_mark)
 {
        gboolean needs_major;
        size_t max_garbage_amount;
@@ -2527,9 +2553,6 @@ collect_nursery (SgenGrayQueue *unpin_queue)
        stat_minor_gcs++;
        gc_stats.minor_gc_count ++;
 
-       if (remset.prepare_for_minor_collection)
-               remset.prepare_for_minor_collection ();
-
        MONO_GC_CHECKPOINT_1 (GENERATION_NURSERY);
 
        sgen_process_fin_stage_entries ();
@@ -2541,6 +2564,8 @@ collect_nursery (SgenGrayQueue *unpin_queue)
        sgen_init_pinning ();
        mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
        pin_from_roots (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+       /* pin cemented objects */
+       sgen_cement_iterate (pin_stage_object_callback, NULL);
        /* identify pinned objects */
        sgen_optimize_pin_queue (0);
        sgen_pinning_setup_section (nursery_section);
@@ -2559,20 +2584,12 @@ collect_nursery (SgenGrayQueue *unpin_queue)
 
        if (whole_heap_check_before_collection) {
                sgen_clear_nursery_fragments ();
-               sgen_check_whole_heap ();
+               sgen_check_whole_heap (finish_up_concurrent_mark);
        }
        if (consistency_check_at_minor_collection)
                sgen_check_consistency ();
 
        sgen_workers_start_all_workers ();
-
-       /*
-        * Perform the sequential part of remembered set scanning.
-        * This usually involves scanning global information that might later be produced by evacuation.
-        */
-       if (remset.begin_scan_remsets)
-               remset.begin_scan_remsets (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
-
        sgen_workers_start_marking ();
 
        frssjd = sgen_alloc_internal_dynamic (sizeof (FinishRememberedSetScanJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
@@ -2653,7 +2670,7 @@ collect_nursery (SgenGrayQueue *unpin_queue)
 
        MONO_GC_CHECKPOINT_8 (GENERATION_NURSERY);
 
-       finish_gray_stack (sgen_get_nursery_start (), nursery_next, GENERATION_NURSERY, &gray_queue);
+       finish_gray_stack (GENERATION_NURSERY, &gray_queue);
        TV_GETTIME (atv);
        time_minor_finish_gray_stack += TV_ELAPSED (btv, atv);
        mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, 0);
@@ -2710,11 +2727,12 @@ collect_nursery (SgenGrayQueue *unpin_queue)
                mono_gc_finalize_notify ();
        }
        sgen_pin_stats_reset ();
+       /* clear cemented hash */
+       sgen_cement_clear_below_threshold ();
 
        g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
 
-       if (remset.finish_minor_collection)
-               remset.finish_minor_collection ();
+       remset.finish_minor_collection ();
 
        check_scan_starts ();
 
@@ -2730,9 +2748,25 @@ collect_nursery (SgenGrayQueue *unpin_queue)
        MONO_GC_END (GENERATION_NURSERY);
        binary_protocol_collection_end (stat_minor_gcs - 1, GENERATION_NURSERY);
 
+       if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
+               sgen_check_nursery_objects_pinned (unpin_queue != NULL);
+
        return needs_major;
 }
 
+static void
+scan_nursery_objects_callback (char *obj, size_t size, ScanCopyContext *ctx)
+{
+       ctx->scan_func (obj, ctx->queue);
+}
+
+static void
+scan_nursery_objects (ScanCopyContext ctx)
+{
+       sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
+                       (IterateObjectCallbackFunc)scan_nursery_objects_callback, (void*)&ctx, FALSE);
+}
+
 static void
 major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concurrent_mark, gboolean scan_mod_union)
 {
@@ -2751,7 +2785,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
        ScanFinalizerEntriesJobData *sfejd_fin_ready, *sfejd_critical_fin;
        ScanCopyContext ctx;
 
-       if (major_collector.is_concurrent) {
+       if (concurrent_collection_in_progress) {
                /*This cleans up unused fragments */
                sgen_nursery_allocator_prepare_for_pinning ();
 
@@ -2770,7 +2804,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
        sgen_clear_nursery_fragments ();
 
        if (whole_heap_check_before_collection)
-               sgen_check_whole_heap ();
+               sgen_check_whole_heap (finish_up_concurrent_mark);
 
        TV_GETTIME (btv);
        time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
@@ -2789,7 +2823,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
                check_for_xdomain_refs ();
        }
 
-       if (!major_collector.is_concurrent) {
+       if (!concurrent_collection_in_progress) {
                /* Remsets are not useful for a major collection */
                remset.prepare_for_major_collection ();
        }
@@ -2801,6 +2835,23 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
        sgen_init_pinning ();
        SGEN_LOG (6, "Collecting pinned addresses");
        pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, WORKERS_DISTRIBUTE_GRAY_QUEUE);
+
+       if (!concurrent_collection_in_progress || finish_up_concurrent_mark) {
+               if (major_collector.is_concurrent) {
+                       /*
+                        * The concurrent major collector cannot evict
+                        * yet, so we need to pin cemented objects to
+                        * not break some asserts.
+                        *
+                        * FIXME: We could evict now!
+                        */
+                       sgen_cement_iterate (pin_stage_object_callback, NULL);
+               }
+
+               if (!concurrent_collection_in_progress)
+                       sgen_cement_reset ();
+       }
+
        sgen_optimize_pin_queue (0);
 
        /*
@@ -2809,7 +2860,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
         * before pinning has finished.  For the non-concurrent
         * collector we start the workers after pinning.
         */
-       if (major_collector.is_concurrent) {
+       if (concurrent_collection_in_progress) {
                sgen_workers_start_all_workers ();
                sgen_workers_start_marking ();
        }
@@ -2836,10 +2887,14 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
                int dummy;
                if (sgen_find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + sgen_los_object_size (bigobj), &dummy)) {
                        binary_protocol_pin (bigobj->data, (gpointer)LOAD_VTABLE (bigobj->data), safe_object_get_size (((MonoObject*)(bigobj->data))));
+
+#ifdef ENABLE_DTRACE
                        if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
                                MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (bigobj->data);
                                MONO_GC_OBJ_PINNED ((mword)bigobj->data, sgen_safe_object_get_size ((MonoObject*)bigobj->data), vt->klass->name_space, vt->klass->name, GENERATION_OLD);
                        }
+#endif
+
                        if (sgen_los_object_is_pinned (bigobj->data)) {
                                g_assert (finish_up_concurrent_mark);
                                continue;
@@ -2861,7 +2916,35 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
        ctx.scan_func = concurrent_collection_in_progress ? current_object_ops.scan_object : NULL;
        ctx.copy_func = NULL;
        ctx.queue = WORKERS_DISTRIBUTE_GRAY_QUEUE;
-       sgen_pin_objects_in_section (nursery_section, ctx);
+
+       /*
+        * Concurrent mark never follows references into the nursery.
+        * In the start and finish pauses we must scan live nursery
+        * objects, though.  We could simply scan all nursery objects,
+        * but that would be conservative.  The easiest way is to do a
+        * nursery collection, which copies all live nursery objects
+        * (except pinned ones, with the simple nursery) to the major
+        * heap.  Scanning the mod union table later will then scan
+        * those promoted objects, provided they're reachable.  Pinned
+        * objects in the nursery - which we can trivially find in the
+        * pinning queue - are treated as roots in the mark pauses.
+        *
+        * The split nursery complicates the latter part because
+        * non-pinned objects can survive in the nursery.  That's why
+        * we need to do a full front-to-back scan of the nursery,
+        * marking all objects.
+        *
+        * Non-concurrent mark evacuates from the nursery, so it's
+        * sufficient to just scan pinned nursery objects.
+        */
+       if (concurrent_collection_in_progress && sgen_minor_collector.is_split) {
+               scan_nursery_objects (ctx);
+       } else {
+               sgen_pin_objects_in_section (nursery_section, ctx);
+               if (check_nursery_objects_pinned && !sgen_minor_collector.is_split)
+                       sgen_check_nursery_objects_pinned (!concurrent_collection_in_progress || finish_up_concurrent_mark);
+       }
+
        major_collector.pin_objects (WORKERS_DISTRIBUTE_GRAY_QUEUE);
        if (old_next_pin_slot)
                *old_next_pin_slot = sgen_get_pinned_count ();
@@ -2877,7 +2960,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
        main_gc_thread = mono_native_thread_self ();
 #endif
 
-       if (!major_collector.is_concurrent) {
+       if (!concurrent_collection_in_progress && major_collector.is_parallel) {
                sgen_workers_start_all_workers ();
                sgen_workers_start_marking ();
        }
@@ -2946,7 +3029,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
        TV_GETTIME (btv);
        time_major_scan_big_objects += TV_ELAPSED (atv, btv);
 
-       if (major_collector.is_concurrent) {
+       if (concurrent_collection_in_progress) {
                /* prepare the pin queue for the next collection */
                sgen_finish_pinning ();
 
@@ -2958,7 +3041,7 @@ major_copy_or_mark_from_roots (int *old_next_pin_slot, gboolean finish_up_concur
 }
 
 static void
-major_start_collection (int *old_next_pin_slot)
+major_start_collection (gboolean concurrent, int *old_next_pin_slot)
 {
        MONO_GC_BEGIN (GENERATION_OLD);
        binary_protocol_collection_begin (stat_major_gcs, GENERATION_OLD);
@@ -2970,10 +3053,16 @@ major_start_collection (int *old_next_pin_slot)
 
        g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
 
-       if (major_collector.is_concurrent)
+       if (concurrent) {
+               g_assert (major_collector.is_concurrent);
                concurrent_collection_in_progress = TRUE;
 
-       current_object_ops = major_collector.major_ops;
+               sgen_cement_concurrent_start ();
+
+               current_object_ops = major_collector.major_concurrent_ops;
+       } else {
+               current_object_ops = major_collector.major_ops;
+       }
 
        reset_pinned_from_failed_allocation ();
 
@@ -2998,9 +3087,7 @@ major_start_collection (int *old_next_pin_slot)
 static void
 wait_for_workers_to_finish (void)
 {
-       g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue));
-
-       if (major_collector.is_parallel || major_collector.is_concurrent) {
+       if (concurrent_collection_in_progress || major_collector.is_parallel) {
                gray_queue_redirect (&gray_queue);
                sgen_workers_join ();
        }
@@ -3018,17 +3105,15 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca
        LOSObject *bigobj, *prevbo;
        TV_DECLARE (atv);
        TV_DECLARE (btv);
-       char *heap_start = NULL;
-       char *heap_end = (char*)-1;
 
        TV_GETTIME (btv);
 
-       if (major_collector.is_concurrent || major_collector.is_parallel)
+       if (concurrent_collection_in_progress || major_collector.is_parallel)
                wait_for_workers_to_finish ();
 
-       current_object_ops = major_collector.major_ops;
+       if (concurrent_collection_in_progress) {
+               current_object_ops = major_collector.major_concurrent_ops;
 
-       if (major_collector.is_concurrent) {
                major_copy_or_mark_from_roots (NULL, TRUE, scan_mod_union);
                wait_for_workers_to_finish ();
 
@@ -3036,6 +3121,8 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca
 
                if (do_concurrent_checks)
                        check_nursery_is_clean ();
+       } else {
+               current_object_ops = major_collector.major_ops;
        }
 
        /*
@@ -3047,7 +3134,7 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca
        g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
 
        /* all the objects in the heap */
-       finish_gray_stack (heap_start, heap_end, GENERATION_OLD, &gray_queue);
+       finish_gray_stack (GENERATION_OLD, &gray_queue);
        TV_GETTIME (atv);
        time_major_finish_gray_stack += TV_ELAPSED (btv, atv);
 
@@ -3060,7 +3147,7 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca
        sgen_workers_reset_data ();
 
        if (objects_pinned) {
-               g_assert (!major_collector.is_concurrent);
+               g_assert (!concurrent_collection_in_progress);
 
                /*This is slow, but we just OOM'd*/
                sgen_pin_queue_clear_discarded_entries (nursery_section, old_next_pin_slot);
@@ -3115,7 +3202,7 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca
        TV_GETTIME (btv);
        time_major_sweep += TV_ELAPSED (atv, btv);
 
-       if (!major_collector.is_concurrent) {
+       if (!concurrent_collection_in_progress) {
                /* walk the pin_queue, build up the fragment list of free memory, unmark
                 * pinned objects as we go, memzero() the empty fragments so they are ready for the
                 * next allocations.
@@ -3132,6 +3219,10 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca
                sgen_pin_stats_reset ();
        }
 
+       if (concurrent_collection_in_progress)
+               sgen_cement_concurrent_finish ();
+       sgen_cement_clear_below_threshold ();
+
        TV_GETTIME (atv);
        time_major_fragment_creation += TV_ELAPSED (btv, atv);
 
@@ -3152,7 +3243,7 @@ major_finish_collection (const char *reason, int old_next_pin_slot, gboolean sca
 
        g_assert (sgen_section_gray_queue_is_empty (sgen_workers_get_distribute_section_gray_queue ()));
 
-       if (major_collector.is_concurrent)
+       if (concurrent_collection_in_progress)
                concurrent_collection_in_progress = FALSE;
 
        check_scan_starts ();
@@ -3180,7 +3271,7 @@ major_do_collection (const char *reason)
        /* world must be stopped already */
        TV_GETTIME (all_atv);
 
-       major_start_collection (&old_next_pin_slot);
+       major_start_collection (FALSE, &old_next_pin_slot);
        major_finish_collection (reason, old_next_pin_slot, FALSE);
 
        TV_GETTIME (all_btv);
@@ -3205,7 +3296,7 @@ major_start_concurrent_collection (const char *reason)
        MONO_GC_CONCURRENT_START_BEGIN (GENERATION_OLD);
 
        // FIXME: store reason and pass it when finishing
-       major_start_collection (NULL);
+       major_start_collection (TRUE, NULL);
 
        gray_queue_redirect (&gray_queue);
        sgen_workers_wait_for_jobs ();
@@ -3225,8 +3316,6 @@ major_update_or_finish_concurrent_collection (gboolean force_finish)
        MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
 
        g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
-       if (!have_non_collection_major_object_remembers)
-               g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue));
 
        major_collector.update_cardtable_mod_union ();
        sgen_los_update_cardtable_mod_union ();
@@ -3236,12 +3325,17 @@ major_update_or_finish_concurrent_collection (gboolean force_finish)
                return FALSE;
        }
 
-       collect_nursery (&unpin_queue);
-       redirect_major_object_remembers ();
+       if (mod_union_consistency_check)
+               sgen_check_mod_union_consistency ();
+
+       collect_nursery (&unpin_queue, TRUE);
 
        current_collection_generation = GENERATION_OLD;
        major_finish_collection ("finishing", -1, TRUE);
 
+       if (whole_heap_check_before_collection)
+               sgen_check_whole_heap (FALSE);
+
        unpin_objects_from_queue (&unpin_queue);
        sgen_gray_object_queue_deinit (&unpin_queue);
 
@@ -3249,9 +3343,6 @@ major_update_or_finish_concurrent_collection (gboolean force_finish)
 
        current_collection_generation = -1;
 
-       if (whole_heap_check_before_collection)
-               sgen_check_whole_heap ();
-
        return TRUE;
 }
 
@@ -3299,6 +3390,9 @@ sgen_ensure_free_space (size_t size)
        sgen_perform_collection (size, generation_to_collect, reason, FALSE);
 }
 
+/*
+ * LOCKING: Assumes the GC lock is held.
+ */
 void
 sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish)
 {
@@ -3309,14 +3403,11 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
        const char *overflow_reason = NULL;
 
        MONO_GC_REQUESTED (generation_to_collect, requested_size, wait_to_finish ? 1 : 0);
+       if (wait_to_finish)
+               binary_protocol_collection_force (generation_to_collect);
 
        g_assert (generation_to_collect == GENERATION_NURSERY || generation_to_collect == GENERATION_OLD);
 
-       if (have_non_collection_major_object_remembers) {
-               g_assert (concurrent_collection_in_progress);
-               redirect_major_object_remembers ();
-       }
-
        memset (infos, 0, sizeof (infos));
        mono_profiler_gc_event (MONO_GC_EVENT_START, generation_to_collect);
 
@@ -3335,31 +3426,26 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
                }
                if (generation_to_collect == GENERATION_OLD)
                        goto done;
+       } else {
+               if (generation_to_collect == GENERATION_OLD &&
+                               allow_synchronous_major &&
+                               major_collector.want_synchronous_collection &&
+                               *major_collector.want_synchronous_collection) {
+                       wait_to_finish = TRUE;
+               }
        }
 
        //FIXME extract overflow reason
        if (generation_to_collect == GENERATION_NURSERY) {
-               if (collect_nursery (NULL)) {
+               if (collect_nursery (NULL, FALSE)) {
                        overflow_generation_to_collect = GENERATION_OLD;
                        overflow_reason = "Minor overflow";
                }
-               if (concurrent_collection_in_progress) {
-                       redirect_major_object_remembers ();
-                       sgen_workers_wake_up_all ();
-               }
        } else {
-               SgenGrayQueue unpin_queue;
-               SgenGrayQueue *unpin_queue_ptr;
-               memset (&unpin_queue, 0, sizeof (unpin_queue));
-
-               if (major_collector.is_concurrent && wait_to_finish)
-                       unpin_queue_ptr = &unpin_queue;
-               else
-                       unpin_queue_ptr = NULL;
-
                if (major_collector.is_concurrent) {
                        g_assert (!concurrent_collection_in_progress);
-                       collect_nursery (unpin_queue_ptr);
+                       if (!wait_to_finish)
+                               collect_nursery (NULL, FALSE);
                }
 
                if (major_collector.is_concurrent && !wait_to_finish) {
@@ -3372,11 +3458,6 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
                                overflow_reason = "Excessive pinning";
                        }
                }
-
-               if (unpin_queue_ptr) {
-                       unpin_objects_from_queue (unpin_queue_ptr);
-                       sgen_gray_object_queue_deinit (unpin_queue_ptr);
-               }
        }
 
        TV_GETTIME (gc_end);
@@ -3391,7 +3472,7 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
                infos [1].total_time = gc_end;
 
                if (overflow_generation_to_collect == GENERATION_NURSERY)
-                       collect_nursery (NULL);
+                       collect_nursery (NULL, FALSE);
                else
                        major_do_collection (overflow_reason);
 
@@ -3416,7 +3497,6 @@ sgen_perform_collection (size_t requested_size, int generation_to_collect, const
 
  done:
        g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
-       g_assert (sgen_gray_object_queue_is_empty (&remember_major_objects_gray_queue));
 
        sgen_restart_world (oldest_generation_collected, infos);
 
@@ -3552,12 +3632,14 @@ sgen_queue_finalization_entry (MonoObject *obj)
                fin_ready_list = entry;
        }
 
+#ifdef ENABLE_DTRACE
        if (G_UNLIKELY (MONO_GC_FINALIZE_ENQUEUE_ENABLED ())) {
                int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD;
                MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
                MONO_GC_FINALIZE_ENQUEUE ((mword)obj, sgen_safe_object_get_size (obj),
                                vt->klass->name_space, vt->klass->name, gen, critical);
        }
+#endif
 }
 
 gboolean
@@ -3859,13 +3941,6 @@ mono_gc_deregister_root (char* addr)
 
 unsigned int sgen_global_stop_count = 0;
 
-void
-sgen_fill_thread_info_for_suspend (SgenThreadInfo *info)
-{
-       if (remset.fill_thread_info_for_suspend)
-               remset.fill_thread_info_for_suspend (info);
-}
-
 int
 sgen_get_current_collection_generation (void)
 {
@@ -3969,10 +4044,6 @@ ptr_on_stack (void *ptr)
 static void*
 sgen_thread_register (SgenThreadInfo* info, void *addr)
 {
-#ifndef HAVE_KW_THREAD
-       SgenThreadInfo *__thread_info__ = info;
-#endif
-
        LOCK_GC;
 #ifndef HAVE_KW_THREAD
        info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
@@ -3992,8 +4063,6 @@ sgen_thread_register (SgenThreadInfo* info, void *addr)
        info->doing_handshake = FALSE;
        info->thread_is_dying = FALSE;
        info->stack_start = NULL;
-       info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
-       info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
        info->stopped_ip = NULL;
        info->stopped_domain = NULL;
 #ifdef USE_MONO_CTX
@@ -4006,22 +4075,29 @@ sgen_thread_register (SgenThreadInfo* info, void *addr)
 
        binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
 
-#ifdef HAVE_KW_THREAD
-       store_remset_buffer_index_addr = &store_remset_buffer_index;
+       /* try to get it with attributes first */
+#if (defined(HAVE_PTHREAD_GETATTR_NP) || defined(HAVE_PTHREAD_ATTR_GET_NP)) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
+  {
+     size_t size;
+     void *sstart;
+     pthread_attr_t attr;
+
+#if defined(HAVE_PTHREAD_GETATTR_NP)
+    /* Linux */
+    pthread_getattr_np (pthread_self (), &attr);
+#elif defined(HAVE_PTHREAD_ATTR_GET_NP)
+    /* BSD */
+    pthread_attr_init (&attr);
+    pthread_attr_get_np (pthread_self (), &attr);
+#else
+#error Cannot determine which API is needed to retrieve pthread attributes.
 #endif
 
-       /* try to get it with attributes first */
-#if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
-       {
-               size_t size;
-               void *sstart;
-               pthread_attr_t attr;
-               pthread_getattr_np (pthread_self (), &attr);
-               pthread_attr_getstack (&attr, &sstart, &size);
-               info->stack_start_limit = sstart;
-               info->stack_end = (char*)sstart + size;
-               pthread_attr_destroy (&attr);
-       }
+     pthread_attr_getstack (&attr, &sstart, &size);
+     info->stack_start_limit = sstart;
+     info->stack_end = (char*)sstart + size;
+     pthread_attr_destroy (&attr);
+  }
 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
                 info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
                 info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
@@ -4039,9 +4115,6 @@ sgen_thread_register (SgenThreadInfo* info, void *addr)
        stack_end = info->stack_end;
 #endif
 
-       if (remset.register_thread)
-               remset.register_thread (info);
-
        SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->stack_end);
 
        if (gc_callbacks.thread_attach_func)
@@ -4051,13 +4124,6 @@ sgen_thread_register (SgenThreadInfo* info, void *addr)
        return info;
 }
 
-static void
-sgen_wbarrier_cleanup_thread (SgenThreadInfo *p)
-{
-       if (remset.cleanup_thread)
-               remset.cleanup_thread (p);
-}
-
 static void
 sgen_thread_unregister (SgenThreadInfo *p)
 {
@@ -4109,7 +4175,6 @@ sgen_thread_unregister (SgenThreadInfo *p)
                gc_callbacks.thread_detach_func (p->runtime_data);
                p->runtime_data = NULL;
        }
-       sgen_wbarrier_cleanup_thread (p);
 
        mono_threads_unregister_current_thread (p);
        UNLOCK_GC;
@@ -4175,8 +4240,9 @@ mono_gc_pthread_detach (pthread_t thread)
 }
 
 void
-mono_gc_pthread_exit (void *retval)
+mono_gc_pthread_exit (void *retval) 
 {
+       mono_thread_info_dettach ();
        pthread_exit (retval);
 }
 
@@ -4558,22 +4624,6 @@ mono_gc_get_used_size (void)
        return tot;
 }
 
-void
-mono_gc_disable (void)
-{
-       LOCK_GC;
-       gc_disabled++;
-       UNLOCK_GC;
-}
-
-void
-mono_gc_enable (void)
-{
-       LOCK_GC;
-       gc_disabled--;
-       UNLOCK_GC;
-}
-
 int
 mono_gc_get_los_limit (void)
 {
@@ -4620,15 +4670,29 @@ mono_gc_weak_link_remove (void **link_addr, gboolean track)
 MonoObject*
 mono_gc_weak_link_get (void **link_addr)
 {
+       void * volatile *link_addr_volatile;
+       void *ptr;
+       MonoObject *obj;
+ retry:
+       link_addr_volatile = link_addr;
+       ptr = (void*)*link_addr_volatile;
        /*
-        * We must only load *link_addr once because it might change
-        * under our feet, and REVEAL_POINTER (NULL) results in an
-        * invalid reference.
+        * At this point we have a hidden pointer.  If the GC runs
+        * here, it will not recognize the hidden pointer as a
+        * reference, and if the object behind it is not referenced
+        * elsewhere, it will be freed.  Once the world is restarted
+        * we reveal the pointer, giving us a pointer to a freed
+        * object.  To make sure we don't return it, we load the
+        * hidden pointer again.  If it's still the same, we can be
+        * sure the object reference is valid.
         */
-       void *ptr = *link_addr;
-       if (!ptr)
+       if (ptr)
+               obj = (MonoObject*) REVEAL_POINTER (ptr);
+       else
                return NULL;
 
+       mono_memory_barrier ();
+
        /*
         * During the second bridge processing step the world is
         * running again.  That step processes all weak links once
@@ -4640,7 +4704,10 @@ mono_gc_weak_link_get (void **link_addr)
        if (G_UNLIKELY (bridge_processing_in_progress))
                mono_gc_wait_for_bridge_processing ();
 
-       return (MonoObject*) REVEAL_POINTER (ptr);
+       if ((void*)*link_addr_volatile != ptr)
+               goto retry;
+
+       return obj;
 }
 
 gboolean
@@ -4665,6 +4732,16 @@ mono_gc_ephemeron_array_add (MonoObject *obj)
        return TRUE;
 }
 
+gboolean
+mono_gc_set_allow_synchronous_major (gboolean flag)
+{
+       if (!major_collector.is_concurrent)
+               return flag;
+
+       allow_synchronous_major = flag;
+       return TRUE;
+}
+
 void*
 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
 {
@@ -4690,7 +4767,40 @@ is_critical_method (MonoMethod *method)
 {
        return mono_runtime_is_critical_method (method) || sgen_is_critical_method (method);
 }
-       
+
+void
+sgen_env_var_error (const char *env_var, const char *fallback, const char *description_format, ...)
+{
+       va_list ap;
+
+       va_start (ap, description_format);
+
+       fprintf (stderr, "Warning: In environment variable `%s': ", env_var);
+       vfprintf (stderr, description_format, ap);
+       if (fallback)
+               fprintf (stderr, " - %s", fallback);
+       fprintf (stderr, "\n");
+
+       va_end (ap);
+}
+
+static gboolean
+parse_double_in_interval (const char *env_var, const char *opt_name, const char *opt, double min, double max, double *result)
+{
+       char *endptr;
+       double val = strtod (opt, &endptr);
+       if (endptr == opt) {
+               sgen_env_var_error (env_var, "Using default value.", "`%s` must be a number.", opt_name);
+               return FALSE;
+       }
+       else if (val < min || val > max) {
+               sgen_env_var_error (env_var, "Using default value.", "`%s` must be between %.2f - %.2f.", opt_name, min, max);
+               return FALSE;
+       }
+       *result = val;
+       return TRUE;
+}
+
 void
 mono_gc_base_init (void)
 {
@@ -4707,6 +4817,7 @@ mono_gc_base_init (void)
        gboolean debug_print_allowance = FALSE;
        double allowance_ratio = 0, save_target = 0;
        gboolean have_split_nursery = FALSE;
+       gboolean cement_enabled = TRUE;
 
        do {
                result = InterlockedCompareExchange (&gc_initialized, -1, 0);
@@ -4746,7 +4857,7 @@ mono_gc_base_init (void)
 
        init_user_copy_or_mark_key ();
 
-       if ((env = getenv ("MONO_GC_PARAMS"))) {
+       if ((env = getenv (MONO_GC_PARAMS_NAME))) {
                opts = g_strsplit (env, ",", -1);
                for (ptr = opts; *ptr; ++ptr) {
                        char *opt = *ptr;
@@ -4765,12 +4876,11 @@ mono_gc_base_init (void)
        init_stats ();
        sgen_init_internal_allocator ();
        sgen_init_nursery_allocator ();
+       sgen_init_fin_weak_hash ();
 
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_GRAY_QUEUE, sizeof (GrayQueueSection));
-       g_assert (sizeof (GenericStoreRememberedSet) == sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
-       sgen_register_fixed_internal_mem_type (INTERNAL_MEM_STORE_REMSET, sizeof (GenericStoreRememberedSet));
        sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
 
 #ifndef HAVE_KW_THREAD
@@ -4790,17 +4900,19 @@ mono_gc_base_init (void)
                sgen_simple_nursery_init (&sgen_minor_collector);
        } else {
                if (!strcmp (minor_collector_opt, "simple")) {
+               use_simple_nursery:
                        sgen_simple_nursery_init (&sgen_minor_collector);
                } else if (!strcmp (minor_collector_opt, "split")) {
                        sgen_split_nursery_init (&sgen_minor_collector);
                        have_split_nursery = TRUE;
                } else {
-                       fprintf (stderr, "Unknown minor collector `%s'.\n", minor_collector_opt);
-                       exit (1);
+                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `simple` instead.", "Unknown minor collector `%s'.", minor_collector_opt);
+                       goto use_simple_nursery;
                }
        }
 
        if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
+       use_marksweep_major:
                sgen_marksweep_init (&major_collector);
        } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
                sgen_marksweep_fixed_init (&major_collector);
@@ -4809,21 +4921,16 @@ mono_gc_base_init (void)
        } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
                sgen_marksweep_fixed_par_init (&major_collector);
        } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-conc")) {
-               if (have_split_nursery) {
-                       fprintf (stderr, "Concurrent Mark&Sweep does not yet support the split nursery.\n");
-                       exit (1);
-               }
                sgen_marksweep_conc_init (&major_collector);
        } else {
-               fprintf (stderr, "Unknown major collector `%s'.\n", major_collector_opt);
-               exit (1);
+               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using `marksweep` instead.", "Unknown major collector `%s'.", major_collector_opt);
+               goto use_marksweep_major;
        }
 
-#ifdef SGEN_HAVE_CARDTABLE
-       use_cardtable = major_collector.supports_cardtable;
-#else
-       use_cardtable = FALSE;
-#endif
+       if (have_split_nursery && major_collector.is_parallel) {
+               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Disabling split minor collector.", "`minor=split` is not supported with the parallel collector yet.");
+               have_split_nursery = FALSE;
+       }
 
        num_workers = mono_cpu_count ();
        g_assert (num_workers > 0);
@@ -4837,44 +4944,25 @@ mono_gc_base_init (void)
        sgen_nursery_size = DEFAULT_NURSERY_SIZE;
 
        if (opts) {
+               gboolean usage_printed = FALSE;
+
                for (ptr = opts; *ptr; ++ptr) {
                        char *opt = *ptr;
+                       if (!strcmp (opt, ""))
+                               continue;
                        if (g_str_has_prefix (opt, "major="))
                                continue;
                        if (g_str_has_prefix (opt, "minor="))
                                continue;
-                       if (g_str_has_prefix (opt, "wbarrier=")) {
-                               opt = strchr (opt, '=') + 1;
-                               if (strcmp (opt, "remset") == 0) {
-                                       if (major_collector.is_concurrent) {
-                                               fprintf (stderr, "The concurrent collector does not support the SSB write barrier.\n");
-                                               exit (1);
-                                       }
-                                       use_cardtable = FALSE;
-                               } else if (strcmp (opt, "cardtable") == 0) {
-                                       if (!use_cardtable) {
-                                               if (major_collector.supports_cardtable)
-                                                       fprintf (stderr, "The cardtable write barrier is not supported on this platform.\n");
-                                               else
-                                                       fprintf (stderr, "The major collector does not support the cardtable write barrier.\n");
-                                               exit (1);
-                                       }
-                               } else {
-                                       fprintf (stderr, "wbarrier must either be `remset' or `cardtable'.");
-                                       exit (1);
-                               }
-                               continue;
-                       }
                        if (g_str_has_prefix (opt, "max-heap-size=")) {
+                               glong max_heap_candidate = 0;
                                opt = strchr (opt, '=') + 1;
-                               if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
-                                       if ((max_heap & (mono_pagesize () - 1))) {
-                                               fprintf (stderr, "max-heap-size size must be a multiple of %d.\n", mono_pagesize ());
-                                               exit (1);
-                                       }
+                               if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap_candidate)) {
+                                       max_heap = (max_heap_candidate + mono_pagesize () - 1) & ~(glong)(mono_pagesize () - 1);
+                                       if (max_heap != max_heap_candidate)
+                                               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Rounding up.", "`max-heap-size` size must be a multiple of %d.", mono_pagesize ());
                                } else {
-                                       fprintf (stderr, "max-heap-size must be an integer.\n");
-                                       exit (1);
+                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`max-heap-size` must be an integer.");
                                }
                                continue;
                        }
@@ -4882,12 +4970,11 @@ mono_gc_base_init (void)
                                opt = strchr (opt, '=') + 1;
                                if (*opt && mono_gc_parse_environment_string_extract_number (opt, &soft_limit)) {
                                        if (soft_limit <= 0) {
-                                               fprintf (stderr, "soft-heap-limit must be positive.\n");
-                                               exit (1);
+                                               sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be positive.");
+                                               soft_limit = 0;
                                        }
                                } else {
-                                       fprintf (stderr, "soft-heap-limit must be an integer.\n");
-                                       exit (1);
+                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, NULL, "`soft-heap-limit` must be an integer.");
                                }
                                continue;
                        }
@@ -4895,18 +4982,18 @@ mono_gc_base_init (void)
                                long val;
                                char *endptr;
                                if (!major_collector.is_parallel) {
-                                       fprintf (stderr, "The workers= option can only be used for parallel collectors.");
-                                       exit (1);
+                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "The `workers` option can only be used for parallel collectors.");
+                                       continue;
                                }
                                opt = strchr (opt, '=') + 1;
                                val = strtol (opt, &endptr, 10);
                                if (!*opt || *endptr) {
-                                       fprintf (stderr, "Cannot parse the workers= option value.");
-                                       exit (1);
+                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Cannot parse the `workers` option value.");
+                                       continue;
                                }
                                if (val <= 0 || val > 16) {
-                                       fprintf (stderr, "The number of workers must be in the range 1 to 16.");
-                                       exit (1);
+                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "The number of `workers` must be in the range 1 to 16.");
+                                       continue;
                                }
                                num_workers = (int)val;
                                continue;
@@ -4918,8 +5005,8 @@ mono_gc_base_init (void)
                                } else if (!strcmp (opt, "conservative")) {
                                        conservative_stack_mark = TRUE;
                                } else {
-                                       fprintf (stderr, "Invalid value '%s' for stack-mark= option, possible values are: 'precise', 'conservative'.\n", opt);
-                                       exit (1);
+                                       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);
                                }
                                continue;
                        }
@@ -4933,56 +5020,74 @@ mono_gc_base_init (void)
                                long val;
                                opt = strchr (opt, '=') + 1;
                                if (*opt && mono_gc_parse_environment_string_extract_number (opt, &val)) {
-                                       sgen_nursery_size = val;
 #ifdef SGEN_ALIGN_NURSERY
                                        if ((val & (val - 1))) {
-                                               fprintf (stderr, "The nursery size must be a power of two.\n");
-                                               exit (1);
+                                               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be a power of two.");
+                                               continue;
                                        }
 
                                        if (val < SGEN_MAX_NURSERY_WASTE) {
-                                               fprintf (stderr, "The nursery size must be at least %d bytes.\n", SGEN_MAX_NURSERY_WASTE);
-                                               exit (1);
+                                               sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.",
+                                                               "`nursery-size` must be at least %d bytes.\n", SGEN_MAX_NURSERY_WASTE);
+                                               continue;
                                        }
 
+                                       sgen_nursery_size = val;
                                        sgen_nursery_bits = 0;
                                        while (1 << (++ sgen_nursery_bits) != sgen_nursery_size)
                                                ;
+#else
+                                       sgen_nursery_size = val;
 #endif
                                } else {
-                                       fprintf (stderr, "nursery-size must be an integer.\n");
-                                       exit (1);
+                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`nursery-size` must be an integer.");
+                                       continue;
                                }
                                continue;
                        }
 #endif
                        if (g_str_has_prefix (opt, "save-target-ratio=")) {
-                               char *endptr;
+                               double val;
                                opt = strchr (opt, '=') + 1;
-                               save_target = strtod (opt, &endptr);
-                               if (endptr == opt) {
-                                       fprintf (stderr, "save-target-ratio must be a number.");
-                                       exit (1);
-                               }
-                               if (save_target < SGEN_MIN_SAVE_TARGET_RATIO || save_target > SGEN_MAX_SAVE_TARGET_RATIO) {
-                                       fprintf (stderr, "save-target-ratio must be between %.2f - %.2f.", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
-                                       exit (1);
+                               if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "save-target-ratio", opt,
+                                               SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO, &val)) {
+                                       save_target = val;
                                }
                                continue;
                        }
                        if (g_str_has_prefix (opt, "default-allowance-ratio=")) {
-                               char *endptr;
+                               double val;
                                opt = strchr (opt, '=') + 1;
-
-                               allowance_ratio = strtod (opt, &endptr);
-                               if (endptr == opt) {
-                                       fprintf (stderr, "save-target-ratio must be a number.");
-                                       exit (1);
+                               if (parse_double_in_interval (MONO_GC_PARAMS_NAME, "default-allowance-ratio", opt,
+                                               SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, &val)) {
+                                       allowance_ratio = val;
+                               }
+                               continue;
+                       }
+                       if (g_str_has_prefix (opt, "allow-synchronous-major=")) {
+                               if (!major_collector.is_concurrent) {
+                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`allow-synchronous-major` is only valid for the concurrent major collector.");
+                                       continue;
                                }
-                               if (allowance_ratio < SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO || allowance_ratio > SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO) {
-                                       fprintf (stderr, "default-allowance-ratio must be between %.2f - %.2f.", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO);
-                                       exit (1);
+
+                               opt = strchr (opt, '=') + 1;
+
+                               if (!strcmp (opt, "yes")) {
+                                       allow_synchronous_major = TRUE;
+                               } else if (!strcmp (opt, "no")) {
+                                       allow_synchronous_major = FALSE;
+                               } else {
+                                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "`allow-synchronous-major` must be either `yes' or `no'.");
+                                       continue;
                                }
+                       }
+
+                       if (!strcmp (opt, "cementing")) {
+                               cement_enabled = TRUE;
+                               continue;
+                       }
+                       if (!strcmp (opt, "no-cementing")) {
+                               cement_enabled = FALSE;
                                continue;
                        }
 
@@ -4992,14 +5097,22 @@ mono_gc_base_init (void)
                        if (sgen_minor_collector.handle_gc_param && sgen_minor_collector.handle_gc_param (opt))
                                continue;
 
-                       fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
+                       sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Unknown option `%s`.", opt);
+
+                       if (usage_printed)
+                               continue;
+
+                       fprintf (stderr, "\n%s must be a comma-delimited list of one or more of the following:\n", MONO_GC_PARAMS_NAME);
                        fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
                        fprintf (stderr, "  soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
                        fprintf (stderr, "  nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
-                       fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-par', 'marksweep-fixed' or 'marksweep-fixed-par')\n");
+                       fprintf (stderr, "  major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par', 'marksweep-fixed' or 'marksweep-fixed-par')\n");
                        fprintf (stderr, "  minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
                        fprintf (stderr, "  wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
                        fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
+                       fprintf (stderr, "  [no-]cementing\n");
+                       if (major_collector.is_concurrent)
+                               fprintf (stderr, "  allow-synchronous-major=FLAG (where FLAG is `yes' or `no')\n");
                        if (major_collector.print_gc_param_usage)
                                major_collector.print_gc_param_usage ();
                        if (sgen_minor_collector.print_gc_param_usage)
@@ -5007,7 +5120,9 @@ mono_gc_base_init (void)
                        fprintf (stderr, " Experimental options:\n");
                        fprintf (stderr, "  save-target-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_SAVE_TARGET_RATIO, SGEN_MAX_SAVE_TARGET_RATIO);
                        fprintf (stderr, "  default-allowance-ratio=R (where R must be between %.2f - %.2f).\n", SGEN_MIN_ALLOWANCE_NURSERY_SIZE_RATIO, SGEN_MAX_ALLOWANCE_NURSERY_SIZE_RATIO);
-                       exit (1);
+                       fprintf (stderr, "\n");
+
+                       usage_printed = TRUE;
                }
                g_strfreev (opts);
        }
@@ -5025,10 +5140,16 @@ mono_gc_base_init (void)
 
        alloc_nursery ();
 
-       if ((env = getenv ("MONO_GC_DEBUG"))) {
+       sgen_cement_init (cement_enabled);
+
+       if ((env = getenv (MONO_GC_DEBUG_NAME))) {
+               gboolean usage_printed = FALSE;
+
                opts = g_strsplit (env, ",", -1);
                for (ptr = opts; ptr && *ptr; ptr ++) {
                        char *opt = *ptr;
+                       if (!strcmp (opt, ""))
+                               continue;
                        if (opt [0] >= '0' && opt [0] <= '9') {
                                gc_debug_level = atoi (opt);
                                opt++;
@@ -5068,8 +5189,16 @@ mono_gc_base_init (void)
                        } else if (!strcmp (opt, "check-at-minor-collections")) {
                                consistency_check_at_minor_collection = TRUE;
                                nursery_clear_policy = CLEAR_AT_GC;
+                       } else if (!strcmp (opt, "mod-union-consistency-check")) {
+                               if (!major_collector.is_concurrent) {
+                                       sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`mod-union-consistency-check` only works with concurrent major collector.");
+                                       continue;
+                               }
+                               mod_union_consistency_check = TRUE;
                        } else if (!strcmp (opt, "check-mark-bits")) {
                                check_mark_bits_after_major_collection = TRUE;
+                       } else if (!strcmp (opt, "check-nursery-pinned")) {
+                               check_nursery_objects_pinned = TRUE;
                        } else if (!strcmp (opt, "xdomain-checks")) {
                                xdomain_checks = TRUE;
                        } else if (!strcmp (opt, "clear-at-gc")) {
@@ -5082,8 +5211,8 @@ mono_gc_base_init (void)
                                do_verify_nursery = TRUE;
                        } else if (!strcmp (opt, "check-concurrent")) {
                                if (!major_collector.is_concurrent) {
-                                       fprintf (stderr, "Error: check-concurrent only world with concurrent major collectors.\n");
-                                       exit (1);
+                                       sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "`check-concurrent` only works with concurrent major collectors.");
+                                       continue;
                                }
                                do_concurrent_checks = TRUE;
                        } else if (!strcmp (opt, "dump-nursery-at-minor-gc")) {
@@ -5106,17 +5235,20 @@ mono_gc_base_init (void)
                        } else if (g_str_has_prefix (opt, "binary-protocol=")) {
                                char *filename = strchr (opt, '=') + 1;
                                binary_protocol_init (filename);
-                               if (use_cardtable)
-                                       fprintf (stderr, "Warning: Cardtable write barriers will not be binary-protocolled.\n");
 #endif
                        } else {
-                               fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
-                               fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
-                               fprintf (stderr, "Valid options are:\n");
+                               sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "Unknown option `%s`.", opt);
+
+                               if (usage_printed)
+                                       continue;
+
+                               fprintf (stderr, "\n%s must be of the format [<l>[:<filename>]|<option>]+ where <l> is a debug level 0-9.\n", MONO_GC_DEBUG_NAME);
+                               fprintf (stderr, "Valid <option>s are:\n");
                                fprintf (stderr, "  collect-before-allocs[=<n>]\n");
                                fprintf (stderr, "  verify-before-allocs[=<n>]\n");
                                fprintf (stderr, "  check-at-minor-collections\n");
                                fprintf (stderr, "  check-mark-bits\n");
+                               fprintf (stderr, "  check-nursery-pinned\n");
                                fprintf (stderr, "  verify-before-collections\n");
                                fprintf (stderr, "  verify-nursery-at-minor-gc\n");
                                fprintf (stderr, "  dump-nursery-at-minor-gc\n");
@@ -5134,7 +5266,9 @@ mono_gc_base_init (void)
 #ifdef SGEN_BINARY_PROTOCOL
                                fprintf (stderr, "  binary-protocol=<filename>\n");
 #endif
-                               exit (1);
+                               fprintf (stderr, "\n");
+
+                               usage_printed = TRUE;
                        }
                }
                g_strfreev (opts);
@@ -5142,12 +5276,13 @@ mono_gc_base_init (void)
 
        if (major_collector.is_parallel) {
                if (heap_dump_file) {
-                       fprintf (stderr, "Error: Cannot do heap dump with the parallel collector.\n");
-                       exit (1);
+                       sgen_env_var_error (MONO_GC_DEBUG_NAME, "Disabling.", "Cannot do `heap-dump` with the parallel collector.");
+                       fclose (heap_dump_file);
+                       heap_dump_file = NULL;
                }
                if (do_pin_stats) {
-                       fprintf (stderr, "Error: Cannot gather pinning statistics with the parallel collector.\n");
-                       exit (1);
+                       sgen_env_var_error (MONO_GC_DEBUG_NAME, "Disabling.", "`print-pinning` is not supported with the parallel collector.");
+                       do_pin_stats = FALSE;
                }
        }
 
@@ -5158,15 +5293,7 @@ mono_gc_base_init (void)
 
        memset (&remset, 0, sizeof (remset));
 
-#ifdef SGEN_HAVE_CARDTABLE
-       if (use_cardtable)
-               sgen_card_table_init (&remset);
-       else
-#endif
-               sgen_ssb_init (&remset);
-
-       if (remset.register_thread)
-               remset.register_thread (mono_thread_info_current ());
+       sgen_card_table_init (&remset);
 
        gc_initialized = 1;
 }
@@ -5191,6 +5318,8 @@ sgen_has_critical_method (void)
        return write_barrier_method || sgen_has_managed_allocator ();
 }
 
+#ifndef DISABLE_JIT
+
 static void
 emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels)
 {
@@ -5256,6 +5385,7 @@ emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels)
        }
 #endif 
 }
+#endif
 
 MonoMethod*
 mono_gc_get_write_barrier (void)
@@ -5265,21 +5395,12 @@ mono_gc_get_write_barrier (void)
        MonoMethodSignature *sig;
 #ifdef MANAGED_WBARRIER
        int i, nursery_check_labels [3];
-       int label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
-       int buffer_var, buffer_index_var, dummy_var;
 
 #ifdef HAVE_KW_THREAD
-       int stack_end_offset = -1, store_remset_buffer_offset = -1;
-       int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
+       int stack_end_offset = -1;
 
        MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
        g_assert (stack_end_offset != -1);
-       MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
-       g_assert (store_remset_buffer_offset != -1);
-       MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
-       g_assert (store_remset_buffer_index_offset != -1);
-       MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
-       g_assert (store_remset_buffer_index_addr_offset != -1);
 #endif
 #endif
 
@@ -5295,133 +5416,52 @@ mono_gc_get_write_barrier (void)
 
        mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
 
+#ifndef DISABLE_JIT
 #ifdef MANAGED_WBARRIER
-       if (use_cardtable) {
-               emit_nursery_check (mb, nursery_check_labels);
-               /*
-               addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
-               *addr = 1;
-
-               sgen_cardtable: 
-                       LDC_PTR sgen_cardtable
-
-               address >> CARD_BITS
-                       LDARG_0
-                       LDC_I4 CARD_BITS
-                       SHR_UN
-               if (SGEN_HAVE_OVERLAPPING_CARDS) {
-                       LDC_PTR card_table_mask
-                       AND
-               }
+       emit_nursery_check (mb, nursery_check_labels);
+       /*
+       addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
+       *addr = 1;
+
+       sgen_cardtable:
+               LDC_PTR sgen_cardtable
+
+       address >> CARD_BITS
+               LDARG_0
+               LDC_I4 CARD_BITS
+               SHR_UN
+       if (SGEN_HAVE_OVERLAPPING_CARDS) {
+               LDC_PTR card_table_mask
                AND
-               ldc_i4_1
-               stind_i1
-               */
-               mono_mb_emit_ptr (mb, sgen_cardtable);
-               mono_mb_emit_ldarg (mb, 0);
-               mono_mb_emit_icon (mb, CARD_BITS);
-               mono_mb_emit_byte (mb, CEE_SHR_UN);
+       }
+       AND
+       ldc_i4_1
+       stind_i1
+       */
+       mono_mb_emit_ptr (mb, sgen_cardtable);
+       mono_mb_emit_ldarg (mb, 0);
+       mono_mb_emit_icon (mb, CARD_BITS);
+       mono_mb_emit_byte (mb, CEE_SHR_UN);
 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
-               mono_mb_emit_ptr (mb, (gpointer)CARD_MASK);
-               mono_mb_emit_byte (mb, CEE_AND);
+       mono_mb_emit_ptr (mb, (gpointer)CARD_MASK);
+       mono_mb_emit_byte (mb, CEE_AND);
 #endif
-               mono_mb_emit_byte (mb, CEE_ADD);
-               mono_mb_emit_icon (mb, 1);
-               mono_mb_emit_byte (mb, CEE_STIND_I1);
-
-               // return;
-               for (i = 0; i < 3; ++i) {
-                       if (nursery_check_labels [i])
-                               mono_mb_patch_branch (mb, nursery_check_labels [i]);
-               }               
-               mono_mb_emit_byte (mb, CEE_RET);
-       } else if (mono_runtime_has_tls_get ()) {
-               emit_nursery_check (mb, nursery_check_labels);
-
-               // if (ptr >= stack_end) goto need_wb;
-               mono_mb_emit_ldarg (mb, 0);
-               EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
-               label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
-
-               // if (ptr >= stack_start) return;
-               dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
-               mono_mb_emit_ldarg (mb, 0);
-               mono_mb_emit_ldloc_addr (mb, dummy_var);
-               label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
-
-               // need_wb:
-               mono_mb_patch_branch (mb, label_need_wb);
-
-               // buffer = STORE_REMSET_BUFFER;
-               buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
-               EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
-               mono_mb_emit_stloc (mb, buffer_var);
-
-               // buffer_index = STORE_REMSET_BUFFER_INDEX;
-               buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
-               EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
-               mono_mb_emit_stloc (mb, buffer_index_var);
-
-               // if (buffer [buffer_index] == ptr) return;
-               mono_mb_emit_ldloc (mb, buffer_var);
-               mono_mb_emit_ldloc (mb, buffer_index_var);
-               g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
-               mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
-               mono_mb_emit_byte (mb, CEE_SHL);
-               mono_mb_emit_byte (mb, CEE_ADD);
-               mono_mb_emit_byte (mb, CEE_LDIND_I);
-               mono_mb_emit_ldarg (mb, 0);
-               label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
-
-               // ++buffer_index;
-               mono_mb_emit_ldloc (mb, buffer_index_var);
-               mono_mb_emit_icon (mb, 1);
-               mono_mb_emit_byte (mb, CEE_ADD);
-               mono_mb_emit_stloc (mb, buffer_index_var);
-
-               // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
-               mono_mb_emit_ldloc (mb, buffer_index_var);
-               mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
-               label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
-
-               // buffer [buffer_index] = ptr;
-               mono_mb_emit_ldloc (mb, buffer_var);
-               mono_mb_emit_ldloc (mb, buffer_index_var);
-               g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
-               mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
-               mono_mb_emit_byte (mb, CEE_SHL);
-               mono_mb_emit_byte (mb, CEE_ADD);
-               mono_mb_emit_ldarg (mb, 0);
-               mono_mb_emit_byte (mb, CEE_STIND_I);
-
-               // STORE_REMSET_BUFFER_INDEX = buffer_index;
-               EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
-               mono_mb_emit_ldloc (mb, buffer_index_var);
-               mono_mb_emit_byte (mb, CEE_STIND_I);
-
-               // return;
-               for (i = 0; i < 3; ++i) {
-                       if (nursery_check_labels [i])
-                               mono_mb_patch_branch (mb, nursery_check_labels [i]);
-               }
-               mono_mb_patch_branch (mb, label_no_wb_3);
-               mono_mb_patch_branch (mb, label_no_wb_4);
-               mono_mb_emit_byte (mb, CEE_RET);
-
-               // slow path
-               mono_mb_patch_branch (mb, label_slow_path);
-
-               mono_mb_emit_ldarg (mb, 0);
-               mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
-               mono_mb_emit_byte (mb, CEE_RET);
-       } else
-#endif
-       {
-               mono_mb_emit_ldarg (mb, 0);
-               mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
-               mono_mb_emit_byte (mb, CEE_RET);
+       mono_mb_emit_byte (mb, CEE_ADD);
+       mono_mb_emit_icon (mb, 1);
+       mono_mb_emit_byte (mb, CEE_STIND_I1);
+
+       // return;
+       for (i = 0; i < 3; ++i) {
+               if (nursery_check_labels [i])
+                       mono_mb_patch_branch (mb, nursery_check_labels [i]);
        }
-
+       mono_mb_emit_byte (mb, CEE_RET);
+#else
+       mono_mb_emit_ldarg (mb, 0);
+       mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
+       mono_mb_emit_byte (mb, CEE_RET);
+#endif
+#endif
        res = mono_mb_create_method (mb, sig, 16);
        mono_mb_free (mb);
 
@@ -5567,7 +5607,7 @@ sgen_check_whole_heap_stw (void)
 {
        sgen_stop_world (0);
        sgen_clear_nursery_fragments ();
-       sgen_check_whole_heap ();
+       sgen_check_whole_heap (FALSE);
        sgen_restart_world (0, NULL);
 }