#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"
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 */
/* 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;
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;
* ######################################################################
*/
LOCK_DECLARE (gc_mutex);
-static int gc_disabled = 0;
-
-static gboolean use_cardtable;
#define SCAN_START_SIZE SGEN_SCAN_START_SIZE
#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 */
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);
static void init_stats (void);
static int mark_ephemerons_in_range (ScanCopyContext ctx);
-static void clear_unreachable_ephemerons (gboolean concurrent_cementing, 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);
SgenMajorCollector major_collector;
SgenMinorCollector sgen_minor_collector;
static GrayQueue gray_queue;
-static GrayQueue remember_major_objects_gray_queue;
static SgenRemeberedSet remset;
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)
{
}
}
-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)
{
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") &&
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;
((MonoRealProxy*)start)->unwrapped_server = NULL;
}
}
+#endif
}
static MonoDomain *check_domain = NULL;
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);
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;
}
* lock must be held. For serial collectors that is not necessary.
*/
void
-sgen_add_to_global_remset (gpointer ptr, gpointer obj, gboolean concurrent_cementing)
+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, !concurrent_cementing, "Concurrent cementing must only happen with the concurrent collector");
SGEN_ASSERT (5, current_collection_generation != -1, "Global remsets can only be added during collections");
} else {
if (current_collection_generation == -1)
- SGEN_ASSERT (5, concurrent_cementing, "Global remsets outside of collection pauses can only be added by the concurrent collector");
- if (concurrent_cementing)
- SGEN_ASSERT (5, concurrent_collection_in_progress, "Concurrent collection must be in process in order to add global remsets");
+ 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, concurrent_cementing || sgen_minor_collector.is_split, "Non-pinned objects can only remain in nursery if it is a split nursery");
- else if (sgen_cement_lookup_or_register (obj, concurrent_cementing))
+ 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);
-#ifdef ENABLE_DTRACE
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));
- HEAVY_STAT (++stat_global_remsets_added);
+#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),
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);
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
} 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);
* Clear ephemeron pairs with unreachable keys.
* We pass the copy func so we can figure out if an array was promoted or not.
*/
- clear_unreachable_ephemerons (generation == GENERATION_OLD && major_collector.is_concurrent, ctx);
+ clear_unreachable_ephemerons (ctx);
TV_GETTIME (btv);
SGEN_LOG (2, "Finalize queue handling scan for %s generation: %d usecs %d ephemeron rounds", generation_name (generation), TV_ELAPSED (atv, btv), ephemeron_rounds);
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);
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);
}
} 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
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 ();
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);
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);
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 ();
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 ();
if (whole_heap_check_before_collection)
sgen_check_whole_heap (finish_up_concurrent_mark);
- if (!major_collector.is_concurrent)
- sgen_cement_reset ();
-
TV_GETTIME (btv);
time_major_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
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 ();
}
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);
/*
* 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 ();
}
* Non-concurrent mark evacuates from the nursery, so it's
* sufficient to just scan pinned nursery objects.
*/
- if (major_collector.is_concurrent && sgen_minor_collector.is_split) {
+ if (concurrent_collection_in_progress && sgen_minor_collector.is_split) {
scan_nursery_objects (ctx);
} else {
sgen_pin_objects_in_section (nursery_section, ctx);
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 ();
}
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 ();
}
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);
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;
sgen_cement_concurrent_start ();
- }
- current_object_ops = major_collector.major_ops;
+ current_object_ops = major_collector.major_concurrent_ops;
+ } else {
+ current_object_ops = major_collector.major_ops;
+ }
reset_pinned_from_failed_allocation ();
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 ();
}
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 ();
if (do_concurrent_checks)
check_nursery_is_clean ();
+ } else {
+ current_object_ops = major_collector.major_ops;
}
/*
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);
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);
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.
sgen_pin_stats_reset ();
}
- if (major_collector.is_concurrent)
+ if (concurrent_collection_in_progress)
sgen_cement_concurrent_finish ();
sgen_cement_clear_below_threshold ();
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 ();
/* 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);
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 ();
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 ();
return FALSE;
}
+ if (mod_union_consistency_check)
+ sgen_check_mod_union_consistency ();
+
collect_nursery (&unpin_queue, TRUE);
- redirect_major_object_remembers ();
current_collection_generation = GENERATION_OLD;
major_finish_collection ("finishing", -1, TRUE);
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)
{
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);
}
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
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, FALSE);
+ if (!wait_to_finish)
+ collect_nursery (NULL, FALSE);
}
if (major_collector.is_concurrent && !wait_to_finish) {
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);
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);
/* LOCKING: requires that the GC lock is held */
static void
-clear_unreachable_ephemerons (gboolean concurrent_cementing, ScanCopyContext ctx)
+clear_unreachable_ephemerons (ScanCopyContext ctx)
{
CopyOrMarkObjectFunc copy_func = ctx.copy_func;
GrayQueue *queue = ctx.queue;
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)
{
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;
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
binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
-#ifdef HAVE_KW_THREAD
- store_remset_buffer_index_addr = &store_remset_buffer_index;
-#endif
-
/* try to get it with attributes first */
#if (defined(HAVE_PTHREAD_GETATTR_NP) || defined(HAVE_PTHREAD_ATTR_GET_NP)) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
{
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)
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)
{
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;
void
mono_gc_pthread_exit (void *retval)
{
+ mono_thread_info_dettach ();
pthread_exit (retval);
}
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)
{
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
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
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)
{
{
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)
{
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;
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
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);
} else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-conc")) {
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);
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;
}
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;
}
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;
} 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;
}
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 (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);
+ 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;
+ }
+
+ 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;
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)
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);
}
sgen_cement_init (cement_enabled);
- if ((env = getenv ("MONO_GC_DEBUG"))) {
+ 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++;
} 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")) {
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")) {
} 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");
#ifdef SGEN_BINARY_PROTOCOL
fprintf (stderr, " binary-protocol=<filename>\n");
#endif
- exit (1);
+ fprintf (stderr, "\n");
+
+ usage_printed = TRUE;
}
}
g_strfreev (opts);
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;
}
}
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;
}
return write_barrier_method || sgen_has_managed_allocator ();
}
+#ifndef DISABLE_JIT
+
static void
emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels)
{
}
#endif
}
+#endif
MonoMethod*
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
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);
-#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
+ mono_mb_emit_ptr (mb, (gpointer)CARD_MASK);
+ mono_mb_emit_byte (mb, CEE_AND);
#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);