Merge pull request #4051 from Unity-Technologies/registry-bug-fixes
[mono.git] / mono / metadata / sgen-mono.c
1 /*
2  * sgen-mono.c: SGen features specific to Mono.
3  *
4  * Copyright (C) 2014 Xamarin Inc
5  *
6  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
7  */
8
9 #include "config.h"
10 #ifdef HAVE_SGEN_GC
11
12 #include "sgen/sgen-gc.h"
13 #include "sgen/sgen-protocol.h"
14 #include "metadata/monitor.h"
15 #include "sgen/sgen-layout-stats.h"
16 #include "sgen/sgen-client.h"
17 #include "sgen/sgen-cardtable.h"
18 #include "sgen/sgen-pinning.h"
19 #include "sgen/sgen-thread-pool.h"
20 #include "metadata/marshal.h"
21 #include "metadata/method-builder.h"
22 #include "metadata/abi-details.h"
23 #include "metadata/mono-gc.h"
24 #include "metadata/runtime.h"
25 #include "metadata/sgen-bridge-internals.h"
26 #include "metadata/gc-internals.h"
27 #include "metadata/handle.h"
28 #include "utils/mono-memory-model.h"
29 #include "utils/mono-logger-internals.h"
30 #include "utils/mono-threads-coop.h"
31 #include "sgen/sgen-thread-pool.h"
32 #include "utils/mono-threads.h"
33 #include "metadata/w32handle.h"
34
35 #ifdef HEAVY_STATISTICS
36 static guint64 stat_wbarrier_set_arrayref = 0;
37 static guint64 stat_wbarrier_value_copy = 0;
38 static guint64 stat_wbarrier_object_copy = 0;
39
40 static guint64 los_marked_cards;
41 static guint64 los_array_cards;
42 static guint64 los_array_remsets;
43 #endif
44
45 /* If set, mark stacks conservatively, even if precise marking is possible */
46 static gboolean conservative_stack_mark = FALSE;
47 /* If set, check that there are no references to the domain left at domain unload */
48 gboolean sgen_mono_xdomain_checks = FALSE;
49
50 /* Functions supplied by the runtime to be called by the GC */
51 static MonoGCCallbacks gc_callbacks;
52
53 #ifdef HAVE_KW_THREAD
54 __thread SgenThreadInfo *sgen_thread_info;
55 #else
56 MonoNativeTlsKey thread_info_key;
57 #endif
58
59 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
60
61 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
62         a = i,
63
64 enum {
65 #include "mono/cil/opcode.def"
66         CEE_LAST
67 };
68
69 #undef OPDEF
70
71 /*
72  * Write barriers
73  */
74
75 static gboolean
76 ptr_on_stack (void *ptr)
77 {
78         gpointer stack_start = &stack_start;
79         SgenThreadInfo *info = mono_thread_info_current ();
80
81         if (ptr >= stack_start && ptr < (gpointer)info->client_info.stack_end)
82                 return TRUE;
83         return FALSE;
84 }
85
86 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
87 #undef HANDLE_PTR
88 #define HANDLE_PTR(ptr,obj) do {                                        \
89                 gpointer o = *(gpointer*)(ptr);                         \
90                 if ((o)) {                                              \
91                         gpointer d = ((char*)dest) + ((char*)(ptr) - (char*)(obj)); \
92                         binary_protocol_wbarrier (d, o, (gpointer) SGEN_LOAD_VTABLE (o)); \
93                 }                                                       \
94         } while (0)
95
96 static void
97 scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
98 {
99 #define SCAN_OBJECT_NOVTABLE
100 #include "sgen/sgen-scan-object.h"
101 }
102 #endif
103
104 void
105 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
106 {
107         HEAVY_STAT (++stat_wbarrier_value_copy);
108         g_assert (klass->valuetype);
109
110         SGEN_LOG (8, "Adding value remset at %p, count %d, descr %p for class %s (%p)", dest, count, (gpointer)klass->gc_descr, klass->name, klass);
111
112         if (sgen_ptr_in_nursery (dest) || ptr_on_stack (dest) || !sgen_gc_descr_has_references ((mword)klass->gc_descr)) {
113                 size_t element_size = mono_class_value_size (klass, NULL);
114                 size_t size = count * element_size;
115                 mono_gc_memmove_atomic (dest, src, size);               
116                 return;
117         }
118
119 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
120         if (binary_protocol_is_heavy_enabled ()) {
121                 size_t element_size = mono_class_value_size (klass, NULL);
122                 int i;
123                 for (i = 0; i < count; ++i) {
124                         scan_object_for_binary_protocol_copy_wbarrier ((char*)dest + i * element_size,
125                                         (char*)src + i * element_size - sizeof (MonoObject),
126                                         (mword) klass->gc_descr);
127                 }
128         }
129 #endif
130
131         sgen_get_remset ()->wbarrier_value_copy (dest, src, count, mono_class_value_size (klass, NULL));
132 }
133
134 /**
135  * mono_gc_wbarrier_object_copy:
136  *
137  * Write barrier to call when obj is the result of a clone or copy of an object.
138  */
139 void
140 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
141 {
142         int size;
143
144         HEAVY_STAT (++stat_wbarrier_object_copy);
145
146         SGEN_ASSERT (6, !ptr_on_stack (obj), "Why is this called for a non-reference type?");
147         if (sgen_ptr_in_nursery (obj) || !SGEN_OBJECT_HAS_REFERENCES (src)) {
148                 size = mono_object_class (obj)->instance_size;
149                 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
150                                 size - sizeof (MonoObject));
151                 return; 
152         }
153
154 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
155         if (binary_protocol_is_heavy_enabled ())
156                 scan_object_for_binary_protocol_copy_wbarrier (obj, (char*)src, (mword) src->vtable->gc_descr);
157 #endif
158
159         sgen_get_remset ()->wbarrier_object_copy (obj, src);
160 }
161
162 void
163 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
164 {
165         HEAVY_STAT (++stat_wbarrier_set_arrayref);
166         if (sgen_ptr_in_nursery (slot_ptr)) {
167                 *(void**)slot_ptr = value;
168                 return;
169         }
170         SGEN_LOG (8, "Adding remset at %p", slot_ptr);
171         if (value)
172                 binary_protocol_wbarrier (slot_ptr, value, value->vtable);
173
174         sgen_get_remset ()->wbarrier_set_field ((GCObject*)arr, slot_ptr, value);
175 }
176
177 void
178 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
179 {
180         mono_gc_wbarrier_set_arrayref ((MonoArray*)obj, field_ptr, value);
181 }
182
183 void
184 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
185 {
186         sgen_wbarrier_value_copy_bitmap (_dest, _src, size, bitmap);
187 }
188
189 int
190 mono_gc_get_suspend_signal (void)
191 {
192         return mono_threads_suspend_get_suspend_signal ();
193 }
194
195 int
196 mono_gc_get_restart_signal (void)
197 {
198         return mono_threads_suspend_get_restart_signal ();
199 }
200
201 static MonoMethod *write_barrier_conc_method;
202 static MonoMethod *write_barrier_noconc_method;
203
204 gboolean
205 sgen_is_critical_method (MonoMethod *method)
206 {
207         return sgen_is_managed_allocator (method);
208 }
209
210 gboolean
211 sgen_has_critical_method (void)
212 {
213         return sgen_has_managed_allocator ();
214 }
215
216 static gboolean
217 ip_in_critical_region (MonoDomain *domain, gpointer ip)
218 {
219         MonoJitInfo *ji;
220         MonoMethod *method;
221
222         /*
223          * We pass false for 'try_aot' so this becomes async safe.
224          * It won't find aot methods whose jit info is not yet loaded,
225          * so we preload their jit info in the JIT.
226          */
227         ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
228         if (!ji)
229                 return FALSE;
230
231         method = mono_jit_info_get_method (ji);
232
233         return mono_runtime_is_critical_method (method) || sgen_is_critical_method (method);
234 }
235
236 gboolean
237 mono_gc_is_critical_method (MonoMethod *method)
238 {
239         return sgen_is_critical_method (method);
240 }
241
242 #ifndef DISABLE_JIT
243
244 static void
245 emit_nursery_check (MonoMethodBuilder *mb, int *nursery_check_return_labels, gboolean is_concurrent)
246 {
247         int shifted_nursery_start = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
248
249         memset (nursery_check_return_labels, 0, sizeof (int) * 2);
250         // if (ptr_in_nursery (ptr)) return;
251         /*
252          * Masking out the bits might be faster, but we would have to use 64 bit
253          * immediates, which might be slower.
254          */
255         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
256         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_START);
257         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
258         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_BITS);
259         mono_mb_emit_byte (mb, CEE_SHR_UN);
260         mono_mb_emit_stloc (mb, shifted_nursery_start);
261
262         mono_mb_emit_ldarg (mb, 0);
263         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
264         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_BITS);
265         mono_mb_emit_byte (mb, CEE_SHR_UN);
266         mono_mb_emit_ldloc (mb, shifted_nursery_start);
267         nursery_check_return_labels [0] = mono_mb_emit_branch (mb, CEE_BEQ);
268
269         if (!is_concurrent) {
270                 // if (!ptr_in_nursery (*ptr)) return;
271                 mono_mb_emit_ldarg (mb, 0);
272                 mono_mb_emit_byte (mb, CEE_LDIND_I);
273                 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
274                 mono_mb_emit_byte (mb, CEE_MONO_LDPTR_NURSERY_BITS);
275                 mono_mb_emit_byte (mb, CEE_SHR_UN);
276                 mono_mb_emit_ldloc (mb, shifted_nursery_start);
277                 nursery_check_return_labels [1] = mono_mb_emit_branch (mb, CEE_BNE_UN);
278         }
279 }
280 #endif
281
282 MonoMethod*
283 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
284 {
285         MonoMethod *res;
286         MonoMethodBuilder *mb;
287         MonoMethodSignature *sig;
288         MonoMethod **write_barrier_method_addr;
289         WrapperInfo *info;
290 #ifdef MANAGED_WBARRIER
291         int i, nursery_check_labels [2];
292 #endif
293
294         // FIXME: Maybe create a separate version for ctors (the branch would be
295         // correctly predicted more times)
296         if (is_concurrent)
297                 write_barrier_method_addr = &write_barrier_conc_method;
298         else
299                 write_barrier_method_addr = &write_barrier_noconc_method;
300
301         if (*write_barrier_method_addr)
302                 return *write_barrier_method_addr;
303
304         /* Create the IL version of mono_gc_barrier_generic_store () */
305         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
306         sig->ret = &mono_defaults.void_class->byval_arg;
307         sig->params [0] = &mono_defaults.int_class->byval_arg;
308
309         if (is_concurrent)
310                 mb = mono_mb_new (mono_defaults.object_class, "wbarrier_conc", MONO_WRAPPER_WRITE_BARRIER);
311         else
312                 mb = mono_mb_new (mono_defaults.object_class, "wbarrier_noconc", MONO_WRAPPER_WRITE_BARRIER);
313
314 #ifndef DISABLE_JIT
315 #ifdef MANAGED_WBARRIER
316         emit_nursery_check (mb, nursery_check_labels, is_concurrent);
317         /*
318         addr = sgen_cardtable + ((address >> CARD_BITS) & CARD_MASK)
319         *addr = 1;
320
321         sgen_cardtable:
322                 LDC_PTR sgen_cardtable
323
324         address >> CARD_BITS
325                 LDARG_0
326                 LDC_I4 CARD_BITS
327                 SHR_UN
328         if (SGEN_HAVE_OVERLAPPING_CARDS) {
329                 LDC_PTR card_table_mask
330                 AND
331         }
332         AND
333         ldc_i4_1
334         stind_i1
335         */
336         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
337         mono_mb_emit_byte (mb, CEE_MONO_LDPTR_CARD_TABLE);
338         mono_mb_emit_ldarg (mb, 0);
339         mono_mb_emit_icon (mb, CARD_BITS);
340         mono_mb_emit_byte (mb, CEE_SHR_UN);
341         mono_mb_emit_byte (mb, CEE_CONV_I);
342 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
343 #if SIZEOF_VOID_P == 8
344         mono_mb_emit_icon8 (mb, CARD_MASK);
345 #else
346         mono_mb_emit_icon (mb, CARD_MASK);
347 #endif
348         mono_mb_emit_byte (mb, CEE_CONV_I);
349         mono_mb_emit_byte (mb, CEE_AND);
350 #endif
351         mono_mb_emit_byte (mb, CEE_ADD);
352         mono_mb_emit_icon (mb, 1);
353         mono_mb_emit_byte (mb, CEE_STIND_I1);
354
355         // return;
356         for (i = 0; i < 2; ++i) {
357                 if (nursery_check_labels [i])
358                         mono_mb_patch_branch (mb, nursery_check_labels [i]);
359         }
360         mono_mb_emit_byte (mb, CEE_RET);
361 #else
362         mono_mb_emit_ldarg (mb, 0);
363         mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
364         mono_mb_emit_byte (mb, CEE_RET);
365 #endif
366 #endif
367         res = mono_mb_create_method (mb, sig, 16);
368         info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
369         mono_marshal_set_wrapper_info (res, info);
370         mono_mb_free (mb);
371
372         LOCK_GC;
373         if (*write_barrier_method_addr) {
374                 /* Already created */
375                 mono_free_method (res);
376         } else {
377                 /* double-checked locking */
378                 mono_memory_barrier ();
379                 *write_barrier_method_addr = res;
380         }
381         UNLOCK_GC;
382
383         return *write_barrier_method_addr;
384 }
385
386 MonoMethod*
387 mono_gc_get_write_barrier (void)
388 {
389         return mono_gc_get_specific_write_barrier (major_collector.is_concurrent);
390 }
391
392 /*
393  * Dummy filler objects
394  */
395
396 /* Vtable of the objects used to fill out nursery fragments before a collection */
397 static GCVTable array_fill_vtable;
398
399 static GCVTable
400 get_array_fill_vtable (void)
401 {
402         if (!array_fill_vtable) {
403                 static MonoClass klass;
404                 static char _vtable[sizeof(MonoVTable)+8];
405                 MonoVTable* vtable = (MonoVTable*) ALIGN_TO((mword)_vtable, 8);
406                 gsize bmap;
407
408                 MonoDomain *domain = mono_get_root_domain ();
409                 g_assert (domain);
410
411                 klass.element_class = mono_defaults.byte_class;
412                 klass.rank = 1;
413                 klass.instance_size = MONO_SIZEOF_MONO_ARRAY;
414                 klass.sizes.element_size = 1;
415                 klass.name = "array_filler_type";
416
417                 vtable->klass = &klass;
418                 bmap = 0;
419                 vtable->gc_descr = mono_gc_make_descr_for_array (TRUE, &bmap, 0, 1);
420                 vtable->rank = 1;
421
422                 array_fill_vtable = vtable;
423         }
424         return array_fill_vtable;
425 }
426
427 gboolean
428 sgen_client_array_fill_range (char *start, size_t size)
429 {
430         MonoArray *o;
431
432         if (size < MONO_SIZEOF_MONO_ARRAY) {
433                 memset (start, 0, size);
434                 return FALSE;
435         }
436
437         o = (MonoArray*)start;
438         o->obj.vtable = (MonoVTable*)get_array_fill_vtable ();
439         /* Mark this as not a real object */
440         o->obj.synchronisation = (MonoThreadsSync *)GINT_TO_POINTER (-1);
441         o->bounds = NULL;
442         o->max_length = (mono_array_size_t)(size - MONO_SIZEOF_MONO_ARRAY);
443
444         return TRUE;
445 }
446
447 void
448 sgen_client_zero_array_fill_header (void *p, size_t size)
449 {
450         if (size >= MONO_SIZEOF_MONO_ARRAY) {
451                 memset (p, 0, MONO_SIZEOF_MONO_ARRAY);
452         } else {
453                 static guint8 zeros [MONO_SIZEOF_MONO_ARRAY];
454
455                 SGEN_ASSERT (0, !memcmp (p, zeros, size), "TLAB segment must be zeroed out.");
456         }
457 }
458
459 /*
460  * Finalization
461  */
462
463 static MonoGCFinalizerCallbacks fin_callbacks;
464
465 guint
466 mono_gc_get_vtable_bits (MonoClass *klass)
467 {
468         guint res = 0;
469         /* FIXME move this to the bridge code */
470         if (sgen_need_bridge_processing ()) {
471                 switch (sgen_bridge_class_kind (klass)) {
472                 case GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS:
473                 case GC_BRIDGE_OPAQUE_BRIDGE_CLASS:
474                         res = SGEN_GC_BIT_BRIDGE_OBJECT;
475                         break;
476                 case GC_BRIDGE_OPAQUE_CLASS:
477                         res = SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT;
478                         break;
479                 case GC_BRIDGE_TRANSPARENT_CLASS:
480                         break;
481                 }
482         }
483         if (fin_callbacks.is_class_finalization_aware) {
484                 if (fin_callbacks.is_class_finalization_aware (klass))
485                         res |= SGEN_GC_BIT_FINALIZER_AWARE;
486         }
487         return res;
488 }
489
490 static gboolean
491 is_finalization_aware (MonoObject *obj)
492 {
493         MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
494         return (vt->gc_bits & SGEN_GC_BIT_FINALIZER_AWARE) == SGEN_GC_BIT_FINALIZER_AWARE;
495 }
496
497 void
498 sgen_client_object_queued_for_finalization (GCObject *obj)
499 {
500         if (fin_callbacks.object_queued_for_finalization && is_finalization_aware (obj))
501                 fin_callbacks.object_queued_for_finalization (obj);
502
503 #ifdef ENABLE_DTRACE
504         if (G_UNLIKELY (MONO_GC_FINALIZE_ENQUEUE_ENABLED ())) {
505                 int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD;
506                 GCVTable vt = SGEN_LOAD_VTABLE (obj);
507                 MONO_GC_FINALIZE_ENQUEUE ((mword)obj, sgen_safe_object_get_size (obj),
508                                 sgen_client_vtable_get_namespace (vt), sgen_client_vtable_get_name (vt), gen,
509                                 sgen_client_object_has_critical_finalizer (obj));
510         }
511 #endif
512 }
513
514 void
515 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
516 {
517         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
518                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
519
520         fin_callbacks = *callbacks;
521 }
522
523 void
524 sgen_client_run_finalize (MonoObject *obj)
525 {
526         mono_gc_run_finalize (obj, NULL);
527 }
528
529 int
530 mono_gc_invoke_finalizers (void)
531 {
532         return sgen_gc_invoke_finalizers ();
533 }
534
535 MonoBoolean
536 mono_gc_pending_finalizers (void)
537 {
538         return sgen_have_pending_finalizers ();
539 }
540
541 void
542 sgen_client_finalize_notify (void)
543 {
544         mono_gc_finalize_notify ();
545 }
546
547 void
548 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
549 {
550         sgen_object_register_for_finalization (obj, user_data);
551 }
552
553 static gboolean
554 object_in_domain_predicate (MonoObject *obj, void *user_data)
555 {
556         MonoDomain *domain = (MonoDomain *)user_data;
557         if (mono_object_domain (obj) == domain) {
558                 SGEN_LOG (5, "Unregistering finalizer for object: %p (%s)", obj, sgen_client_vtable_get_name (SGEN_LOAD_VTABLE (obj)));
559                 return TRUE;
560         }
561         return FALSE;
562 }
563
564 /**
565  * mono_gc_finalizers_for_domain:
566  * @domain: the unloading appdomain
567  * @out_array: output array
568  * @out_size: size of output array
569  *
570  * Enqueue for finalization all objects that belong to the unloading appdomain @domain
571  * @suspend is used for early termination of the enqueuing process.
572  */
573 void
574 mono_gc_finalize_domain (MonoDomain *domain)
575 {
576         sgen_finalize_if (object_in_domain_predicate, domain);
577 }
578
579 void
580 mono_gc_suspend_finalizers (void)
581 {
582         sgen_set_suspend_finalizers ();
583 }
584
585 /*
586  * Ephemerons
587  */
588
589 typedef struct _EphemeronLinkNode EphemeronLinkNode;
590
591 struct _EphemeronLinkNode {
592         EphemeronLinkNode *next;
593         MonoArray *array;
594 };
595
596 typedef struct {
597        GCObject *key;
598        GCObject *value;
599 } Ephemeron;
600
601 static EphemeronLinkNode *ephemeron_list;
602
603 /* LOCKING: requires that the GC lock is held */
604 static void
605 null_ephemerons_for_domain (MonoDomain *domain)
606 {
607         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
608
609         while (current) {
610                 MonoObject *object = (MonoObject*)current->array;
611
612                 if (object)
613                         SGEN_ASSERT (0, object->vtable, "Can't have objects without vtables.");
614
615                 if (object && object->vtable->domain == domain) {
616                         EphemeronLinkNode *tmp = current;
617
618                         if (prev)
619                                 prev->next = current->next;
620                         else
621                                 ephemeron_list = current->next;
622
623                         current = current->next;
624                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
625                 } else {
626                         prev = current;
627                         current = current->next;
628                 }
629         }
630 }
631
632 /* LOCKING: requires that the GC lock is held */
633 void
634 sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx)
635 {
636         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
637         SgenGrayQueue *queue = ctx.queue;
638         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
639         Ephemeron *cur, *array_end;
640         GCObject *tombstone;
641
642         while (current) {
643                 MonoArray *array = current->array;
644
645                 if (!sgen_is_object_alive_for_current_gen ((GCObject*)array)) {
646                         EphemeronLinkNode *tmp = current;
647
648                         SGEN_LOG (5, "Dead Ephemeron array at %p", array);
649
650                         if (prev)
651                                 prev->next = current->next;
652                         else
653                                 ephemeron_list = current->next;
654
655                         current = current->next;
656                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
657
658                         continue;
659                 }
660
661                 copy_func ((GCObject**)&array, queue);
662                 current->array = array;
663
664                 SGEN_LOG (5, "Clearing unreachable entries for ephemeron array at %p", array);
665
666                 cur = mono_array_addr (array, Ephemeron, 0);
667                 array_end = cur + mono_array_length_fast (array);
668                 tombstone = SGEN_LOAD_VTABLE ((GCObject*)array)->domain->ephemeron_tombstone;
669
670                 for (; cur < array_end; ++cur) {
671                         GCObject *key = cur->key;
672
673                         if (!key || key == tombstone)
674                                 continue;
675
676                         SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
677                                 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
678                                 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
679
680                         if (!sgen_is_object_alive_for_current_gen (key)) {
681                                 cur->key = tombstone;
682                                 cur->value = NULL;
683                                 continue;
684                         }
685                 }
686                 prev = current;
687                 current = current->next;
688         }
689 }
690
691 /*
692 LOCKING: requires that the GC lock is held
693
694 Limitations: We scan all ephemerons on every collection since the current design doesn't allow for a simple nursery/mature split.
695 */
696 gboolean
697 sgen_client_mark_ephemerons (ScanCopyContext ctx)
698 {
699         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
700         SgenGrayQueue *queue = ctx.queue;
701         gboolean nothing_marked = TRUE;
702         EphemeronLinkNode *current = ephemeron_list;
703         Ephemeron *cur, *array_end;
704         GCObject *tombstone;
705
706         for (current = ephemeron_list; current; current = current->next) {
707                 MonoArray *array = current->array;
708                 SGEN_LOG (5, "Ephemeron array at %p", array);
709
710                 /*It has to be alive*/
711                 if (!sgen_is_object_alive_for_current_gen ((GCObject*)array)) {
712                         SGEN_LOG (5, "\tnot reachable");
713                         continue;
714                 }
715
716                 copy_func ((GCObject**)&array, queue);
717
718                 cur = mono_array_addr (array, Ephemeron, 0);
719                 array_end = cur + mono_array_length_fast (array);
720                 tombstone = SGEN_LOAD_VTABLE ((GCObject*)array)->domain->ephemeron_tombstone;
721
722                 for (; cur < array_end; ++cur) {
723                         GCObject *key = cur->key;
724
725                         if (!key || key == tombstone)
726                                 continue;
727
728                         SGEN_LOG (5, "[%zd] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
729                                 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
730                                 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
731
732                         if (sgen_is_object_alive_for_current_gen (key)) {
733                                 GCObject *value = cur->value;
734
735                                 copy_func (&cur->key, queue);
736                                 if (value) {
737                                         if (!sgen_is_object_alive_for_current_gen (value))
738                                                 nothing_marked = FALSE;
739                                         copy_func (&cur->value, queue);
740                                 }
741                         }
742                 }
743         }
744
745         SGEN_LOG (5, "Ephemeron run finished. Is it done %d", nothing_marked);
746         return nothing_marked;
747 }
748
749 gboolean
750 mono_gc_ephemeron_array_add (MonoObject *obj)
751 {
752         EphemeronLinkNode *node;
753
754         LOCK_GC;
755
756         node = (EphemeronLinkNode *)sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
757         if (!node) {
758                 UNLOCK_GC;
759                 return FALSE;
760         }
761         node->array = (MonoArray*)obj;
762         node->next = ephemeron_list;
763         ephemeron_list = node;
764
765         SGEN_LOG (5, "Registered ephemeron array %p", obj);
766
767         UNLOCK_GC;
768         return TRUE;
769 }
770
771 /*
772  * Appdomain handling
773  */
774
775 static gboolean
776 need_remove_object_for_domain (GCObject *start, MonoDomain *domain)
777 {
778         if (mono_object_domain (start) == domain) {
779                 SGEN_LOG (4, "Need to cleanup object %p", start);
780                 binary_protocol_cleanup (start, (gpointer)SGEN_LOAD_VTABLE (start), sgen_safe_object_get_size ((GCObject*)start));
781                 return TRUE;
782         }
783         return FALSE;
784 }
785
786 static void
787 process_object_for_domain_clearing (GCObject *start, MonoDomain *domain)
788 {
789         MonoVTable *vt = SGEN_LOAD_VTABLE (start);
790         if (vt->klass == mono_defaults.internal_thread_class)
791                 g_assert (mono_object_domain (start) == mono_get_root_domain ());
792         /* The object could be a proxy for an object in the domain
793            we're deleting. */
794 #ifndef DISABLE_REMOTING
795         if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) {
796                 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
797
798                 /* The server could already have been zeroed out, so
799                    we need to check for that, too. */
800                 if (server && (!SGEN_LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
801                         SGEN_LOG (4, "Cleaning up remote pointer in %p to object %p", start, server);
802                         ((MonoRealProxy*)start)->unwrapped_server = NULL;
803                 }
804         }
805 #endif
806 }
807
808 static gboolean
809 clear_domain_process_object (GCObject *obj, MonoDomain *domain)
810 {
811         gboolean remove;
812
813         process_object_for_domain_clearing (obj, domain);
814         remove = need_remove_object_for_domain (obj, domain);
815
816         if (remove && obj->synchronisation) {
817                 guint32 dislink = mono_monitor_get_object_monitor_gchandle (obj);
818                 if (dislink)
819                         mono_gchandle_free (dislink);
820         }
821
822         return remove;
823 }
824
825 static void
826 clear_domain_process_minor_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
827 {
828         if (clear_domain_process_object (obj, domain)) {
829                 CANARIFY_SIZE (size);
830                 memset (obj, 0, size);
831         }
832 }
833
834 static void
835 clear_domain_process_major_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
836 {
837         clear_domain_process_object (obj, domain);
838 }
839
840 static void
841 clear_domain_free_major_non_pinned_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
842 {
843         if (need_remove_object_for_domain (obj, domain))
844                 major_collector.free_non_pinned_object (obj, size);
845 }
846
847 static void
848 clear_domain_free_major_pinned_object_callback (GCObject *obj, size_t size, MonoDomain *domain)
849 {
850         if (need_remove_object_for_domain (obj, domain))
851                 major_collector.free_pinned_object (obj, size);
852 }
853
854 /*
855  * When appdomains are unloaded we can easily remove objects that have finalizers,
856  * but all the others could still be present in random places on the heap.
857  * We need a sweep to get rid of them even though it's going to be costly
858  * with big heaps.
859  * The reason we need to remove them is because we access the vtable and class
860  * structures to know the object size and the reference bitmap: once the domain is
861  * unloaded the point to random memory.
862  */
863 void
864 mono_gc_clear_domain (MonoDomain * domain)
865 {
866         LOSObject *bigobj, *prev;
867         int i;
868
869         LOCK_GC;
870
871         binary_protocol_domain_unload_begin (domain);
872
873         sgen_stop_world (0);
874
875         if (sgen_concurrent_collection_in_progress ())
876                 sgen_perform_collection (0, GENERATION_OLD, "clear domain", TRUE, FALSE);
877         SGEN_ASSERT (0, !sgen_concurrent_collection_in_progress (), "We just ordered a synchronous collection.  Why are we collecting concurrently?");
878
879         major_collector.finish_sweeping ();
880
881         sgen_process_fin_stage_entries ();
882
883         sgen_clear_nursery_fragments ();
884
885         if (sgen_mono_xdomain_checks && domain != mono_get_root_domain ()) {
886                 sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
887                 sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
888                 sgen_check_for_xdomain_refs ();
889         }
890
891         /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
892         to memory returned to the OS.*/
893         null_ephemerons_for_domain (domain);
894         sgen_null_links_for_domain (domain);
895
896         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
897                 sgen_remove_finalizers_if (object_in_domain_predicate, domain, i);
898
899         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
900                         (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE, TRUE);
901
902         /* We need two passes over major and large objects because
903            freeing such objects might give their memory back to the OS
904            (in the case of large objects) or obliterate its vtable
905            (pinned objects with major-copying or pinned and non-pinned
906            objects with major-mark&sweep), but we might need to
907            dereference a pointer from an object to another object if
908            the first object is a proxy. */
909         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
910         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
911                 clear_domain_process_object ((GCObject*)bigobj->data, domain);
912
913         prev = NULL;
914         for (bigobj = los_object_list; bigobj;) {
915                 if (need_remove_object_for_domain ((GCObject*)bigobj->data, domain)) {
916                         LOSObject *to_free = bigobj;
917                         if (prev)
918                                 prev->next = bigobj->next;
919                         else
920                                 los_object_list = bigobj->next;
921                         bigobj = bigobj->next;
922                         SGEN_LOG (4, "Freeing large object %p", bigobj->data);
923                         sgen_los_free_object (to_free);
924                         continue;
925                 }
926                 prev = bigobj;
927                 bigobj = bigobj->next;
928         }
929         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_NON_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
930         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
931
932         if (domain == mono_get_root_domain ()) {
933                 sgen_pin_stats_report ();
934                 sgen_object_layout_dump (stdout);
935         }
936
937         sgen_restart_world (0);
938
939         binary_protocol_domain_unload_end (domain);
940         binary_protocol_flush_buffers (FALSE);
941
942         UNLOCK_GC;
943 }
944
945 /*
946  * Allocation
947  */
948
949 void*
950 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
951 {
952         MonoObject *obj = sgen_alloc_obj (vtable, size);
953
954         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
955                 if (obj)
956                         mono_profiler_allocation (obj);
957         }
958
959         return obj;
960 }
961
962 void*
963 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
964 {
965         MonoObject *obj = sgen_alloc_obj_pinned (vtable, size);
966
967         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
968                 if (obj)
969                         mono_profiler_allocation (obj);
970         }
971
972         return obj;
973 }
974
975 void*
976 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
977 {
978         MonoObject *obj = sgen_alloc_obj_mature (vtable, size);
979
980         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS)) {
981                 if (obj)
982                         mono_profiler_allocation (obj);
983         }
984
985         return obj;
986 }
987
988 void*
989 mono_gc_alloc_fixed (size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
990 {
991         /* FIXME: do a single allocation */
992         void *res = g_calloc (1, size);
993         if (!res)
994                 return NULL;
995         if (!mono_gc_register_root ((char *)res, size, descr, source, msg)) {
996                 g_free (res);
997                 res = NULL;
998         }
999         return res;
1000 }
1001
1002 void
1003 mono_gc_free_fixed (void* addr)
1004 {
1005         mono_gc_deregister_root ((char *)addr);
1006         g_free (addr);
1007 }
1008
1009 /*
1010  * Managed allocator
1011  */
1012
1013 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1014 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1015 static gboolean use_managed_allocator = TRUE;
1016
1017 #ifdef MANAGED_ALLOCATION
1018
1019 #if defined(HAVE_KW_THREAD) || defined(TARGET_OSX) || defined(TARGET_WIN32) || defined(TARGET_ANDROID) || defined(TARGET_IOS)
1020
1021 // Cache the SgenThreadInfo pointer in a local 'var'.
1022 #define EMIT_TLS_ACCESS_VAR(mb, var) \
1023         do { \
1024                 var = mono_mb_add_local ((mb), &mono_defaults.int_class->byval_arg); \
1025                 mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
1026                 mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
1027                 mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_THREAD_INFO); \
1028                 mono_mb_emit_stloc ((mb), (var)); \
1029         } while (0)
1030
1031 #define EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR(mb, var) \
1032         do { \
1033                 mono_mb_emit_ldloc ((mb), (var)); \
1034                 mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenClientThreadInfo, in_critical_region)); \
1035                 mono_mb_emit_byte ((mb), CEE_ADD); \
1036         } while (0)
1037
1038 #define EMIT_TLS_ACCESS_NEXT_ADDR(mb, var)      do {    \
1039         mono_mb_emit_ldloc ((mb), (var));               \
1040         mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_next));       \
1041         mono_mb_emit_byte ((mb), CEE_ADD);              \
1042         } while (0)
1043
1044 #define EMIT_TLS_ACCESS_TEMP_END(mb, var)       do {    \
1045         mono_mb_emit_ldloc ((mb), (var));               \
1046         mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_temp_end));   \
1047         mono_mb_emit_byte ((mb), CEE_ADD);              \
1048         mono_mb_emit_byte ((mb), CEE_LDIND_I);          \
1049         } while (0)
1050
1051 #else
1052 #define EMIT_TLS_ACCESS_VAR(mb, _var)   do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
1053 #define EMIT_TLS_ACCESS_NEXT_ADDR(mb, _var)     do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
1054 #define EMIT_TLS_ACCESS_TEMP_END(mb, _var)      do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
1055 #define EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR(mb, _var)       do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
1056
1057 #endif
1058
1059 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
1060  * for each class. This is currently not easy to do, as it is hard to generate basic 
1061  * blocks + branches, but it is easy with the linear IL codebase.
1062  *
1063  * For this to work we'd need to solve the TLAB race, first.  Now we
1064  * require the allocator to be in a few known methods to make sure
1065  * that they are executed atomically via the restart mechanism.
1066  */
1067 static MonoMethod*
1068 create_allocator (int atype, ManagedAllocatorVariant variant)
1069 {
1070         int p_var, size_var, real_size_var, thread_var G_GNUC_UNUSED;
1071         gboolean slowpath = variant == MANAGED_ALLOCATOR_SLOW_PATH;
1072         guint32 slowpath_branch, max_size_branch;
1073         MonoMethodBuilder *mb;
1074         MonoMethod *res;
1075         MonoMethodSignature *csig;
1076         static gboolean registered = FALSE;
1077         int tlab_next_addr_var, new_next_var;
1078         const char *name = NULL;
1079         WrapperInfo *info;
1080         int num_params, i;
1081
1082         if (!registered) {
1083                 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
1084                 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
1085                 mono_register_jit_icall (mono_gc_alloc_string, "mono_gc_alloc_string", mono_create_icall_signature ("object ptr int int32"), FALSE);
1086                 registered = TRUE;
1087         }
1088
1089         if (atype == ATYPE_SMALL) {
1090                 name = slowpath ? "SlowAllocSmall" : "AllocSmall";
1091         } else if (atype == ATYPE_NORMAL) {
1092                 name = slowpath ? "SlowAlloc" : "Alloc";
1093         } else if (atype == ATYPE_VECTOR) {
1094                 name = slowpath ? "SlowAllocVector" : "AllocVector";
1095         } else if (atype == ATYPE_STRING) {
1096                 name = slowpath ? "SlowAllocString" : "AllocString";
1097         } else {
1098                 g_assert_not_reached ();
1099         }
1100
1101         if (atype == ATYPE_NORMAL)
1102                 num_params = 1;
1103         else
1104                 num_params = 2;
1105
1106         csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
1107         if (atype == ATYPE_STRING) {
1108                 csig->ret = &mono_defaults.string_class->byval_arg;
1109                 csig->params [0] = &mono_defaults.int_class->byval_arg;
1110                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
1111         } else {
1112                 csig->ret = &mono_defaults.object_class->byval_arg;
1113                 for (i = 0; i < num_params; i++)
1114                         csig->params [i] = &mono_defaults.int_class->byval_arg;
1115         }
1116
1117         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
1118
1119 #ifndef DISABLE_JIT
1120         if (slowpath) {
1121                 switch (atype) {
1122                 case ATYPE_NORMAL:
1123                 case ATYPE_SMALL:
1124                         mono_mb_emit_ldarg (mb, 0);
1125                         mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1126                         break;
1127                 case ATYPE_VECTOR:
1128                         mono_mb_emit_ldarg (mb, 0);
1129                         mono_mb_emit_ldarg (mb, 1);
1130                         mono_mb_emit_icall (mb, ves_icall_array_new_specific);
1131                         break;
1132                 case ATYPE_STRING:
1133                         mono_mb_emit_ldarg (mb, 1);
1134                         mono_mb_emit_icall (mb, ves_icall_string_alloc);
1135                         break;
1136                 default:
1137                         g_assert_not_reached ();
1138                 }
1139
1140                 goto done;
1141         }
1142
1143         /*
1144          * Tls access might call foreign code or code without jinfo. This can
1145          * only happen if we are outside of the critical region.
1146          */
1147         EMIT_TLS_ACCESS_VAR (mb, thread_var);
1148
1149         size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1150         if (atype == ATYPE_SMALL) {
1151                 /* size_var = size_arg */
1152                 mono_mb_emit_ldarg (mb, 1);
1153                 mono_mb_emit_stloc (mb, size_var);
1154         } else if (atype == ATYPE_NORMAL) {
1155                 /* size = vtable->klass->instance_size; */
1156                 mono_mb_emit_ldarg (mb, 0);
1157                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
1158                 mono_mb_emit_byte (mb, CEE_ADD);
1159                 mono_mb_emit_byte (mb, CEE_LDIND_I);
1160                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, instance_size));
1161                 mono_mb_emit_byte (mb, CEE_ADD);
1162                 /* FIXME: assert instance_size stays a 4 byte integer */
1163                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
1164                 mono_mb_emit_byte (mb, CEE_CONV_I);
1165                 mono_mb_emit_stloc (mb, size_var);
1166         } else if (atype == ATYPE_VECTOR) {
1167                 MonoExceptionClause *clause;
1168                 int pos, pos_leave, pos_error;
1169                 MonoClass *oom_exc_class;
1170                 MonoMethod *ctor;
1171
1172                 /*
1173                  * n > MONO_ARRAY_MAX_INDEX => OutOfMemoryException
1174                  * n < 0                    => OverflowException
1175                  *
1176                  * We can do an unsigned comparison to catch both cases, then in the error
1177                  * case compare signed to distinguish between them.
1178                  */
1179                 mono_mb_emit_ldarg (mb, 1);
1180                 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
1181                 mono_mb_emit_byte (mb, CEE_CONV_U);
1182                 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
1183
1184                 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1185                 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
1186                 mono_mb_emit_ldarg (mb, 1);
1187                 mono_mb_emit_icon (mb, 0);
1188                 pos_error = mono_mb_emit_short_branch (mb, CEE_BLT_S);
1189                 mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
1190                 mono_mb_patch_short_branch (mb, pos_error);
1191                 mono_mb_emit_exception (mb, "OverflowException", NULL);
1192
1193                 mono_mb_patch_short_branch (mb, pos);
1194
1195                 clause = (MonoExceptionClause *)mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
1196                 clause->try_offset = mono_mb_get_label (mb);
1197
1198                 /* vtable->klass->sizes.element_size */
1199                 mono_mb_emit_ldarg (mb, 0);
1200                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
1201                 mono_mb_emit_byte (mb, CEE_ADD);
1202                 mono_mb_emit_byte (mb, CEE_LDIND_I);
1203                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, sizes));
1204                 mono_mb_emit_byte (mb, CEE_ADD);
1205                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
1206                 mono_mb_emit_byte (mb, CEE_CONV_I);
1207
1208                 /* * n */
1209                 mono_mb_emit_ldarg (mb, 1);
1210                 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
1211                 /* + sizeof (MonoArray) */
1212                 mono_mb_emit_icon (mb, MONO_SIZEOF_MONO_ARRAY);
1213                 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
1214                 mono_mb_emit_stloc (mb, size_var);
1215
1216                 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
1217
1218                 /* catch */
1219                 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
1220                 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
1221                 clause->data.catch_class = mono_class_load_from_name (mono_defaults.corlib,
1222                                 "System", "OverflowException");
1223                 clause->handler_offset = mono_mb_get_label (mb);
1224
1225                 oom_exc_class = mono_class_load_from_name (mono_defaults.corlib,
1226                                 "System", "OutOfMemoryException");
1227                 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
1228                 g_assert (ctor);
1229
1230                 mono_mb_emit_byte (mb, CEE_POP);
1231                 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
1232                 mono_mb_emit_byte (mb, CEE_THROW);
1233
1234                 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
1235                 mono_mb_set_clauses (mb, 1, clause);
1236                 mono_mb_patch_branch (mb, pos_leave);
1237                 /* end catch */
1238         } else if (atype == ATYPE_STRING) {
1239                 int pos;
1240
1241                 /*
1242                  * a string allocator method takes the args: (vtable, len)
1243                  *
1244                  * bytes = offsetof (MonoString, chars) + ((len + 1) * 2)
1245                  *
1246                  * condition:
1247                  *
1248                  * bytes <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
1249                  *
1250                  * therefore:
1251                  *
1252                  * offsetof (MonoString, chars) + ((len + 1) * 2) <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
1253                  * len <= (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - offsetof (MonoString, chars)) / 2 - 1
1254                  */
1255                 mono_mb_emit_ldarg (mb, 1);
1256                 mono_mb_emit_icon (mb, (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - MONO_STRUCT_OFFSET (MonoString, chars)) / 2 - 1);
1257                 pos = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
1258
1259                 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1260                 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
1261                 mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
1262                 mono_mb_patch_short_branch (mb, pos);
1263
1264                 mono_mb_emit_ldarg (mb, 1);
1265                 mono_mb_emit_icon (mb, 1);
1266                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
1267                 //WE manually fold the above + 2 here
1268                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, chars) + 2);
1269                 mono_mb_emit_byte (mb, CEE_ADD);
1270                 mono_mb_emit_stloc (mb, size_var);
1271         } else {
1272                 g_assert_not_reached ();
1273         }
1274
1275 #ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
1276         EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
1277         mono_mb_emit_byte (mb, CEE_LDC_I4_1);
1278         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1279         mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
1280         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_NONE);
1281 #endif
1282
1283         if (nursery_canaries_enabled ()) {
1284                 real_size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1285                 mono_mb_emit_ldloc (mb, size_var);
1286                 mono_mb_emit_stloc(mb, real_size_var);
1287         }
1288         else
1289                 real_size_var = size_var;
1290
1291         /* size += ALLOC_ALIGN - 1; */
1292         mono_mb_emit_ldloc (mb, size_var);
1293         mono_mb_emit_icon (mb, SGEN_ALLOC_ALIGN - 1);
1294         mono_mb_emit_byte (mb, CEE_ADD);
1295         /* size &= ~(ALLOC_ALIGN - 1); */
1296         mono_mb_emit_icon (mb, ~(SGEN_ALLOC_ALIGN - 1));
1297         mono_mb_emit_byte (mb, CEE_AND);
1298         mono_mb_emit_stloc (mb, size_var);
1299
1300         /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
1301         if (atype != ATYPE_SMALL) {
1302                 mono_mb_emit_ldloc (mb, size_var);
1303                 mono_mb_emit_icon (mb, SGEN_MAX_SMALL_OBJ_SIZE);
1304                 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
1305         }
1306
1307         /*
1308          * We need to modify tlab_next, but the JIT only supports reading, so we read
1309          * another tls var holding its address instead.
1310          */
1311
1312         /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
1313         tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1314         EMIT_TLS_ACCESS_NEXT_ADDR (mb, thread_var);
1315         mono_mb_emit_stloc (mb, tlab_next_addr_var);
1316
1317         /* p = (void**)tlab_next; */
1318         p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1319         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
1320         mono_mb_emit_byte (mb, CEE_LDIND_I);
1321         mono_mb_emit_stloc (mb, p_var);
1322         
1323         /* new_next = (char*)p + size; */
1324         new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1325         mono_mb_emit_ldloc (mb, p_var);
1326         mono_mb_emit_ldloc (mb, size_var);
1327         mono_mb_emit_byte (mb, CEE_CONV_I);
1328         mono_mb_emit_byte (mb, CEE_ADD);
1329
1330         if (nursery_canaries_enabled ()) {
1331                         mono_mb_emit_icon (mb, CANARY_SIZE);
1332                         mono_mb_emit_byte (mb, CEE_ADD);
1333         }
1334         mono_mb_emit_stloc (mb, new_next_var);
1335
1336         /* if (G_LIKELY (new_next < tlab_temp_end)) */
1337         mono_mb_emit_ldloc (mb, new_next_var);
1338         EMIT_TLS_ACCESS_TEMP_END (mb, thread_var);
1339         slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1340
1341         /* Slowpath */
1342         if (atype != ATYPE_SMALL)
1343                 mono_mb_patch_short_branch (mb, max_size_branch);
1344
1345         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1346         mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
1347         /*
1348          * We are no longer in a critical section. We need to do this before calling
1349          * to unmanaged land in order to avoid stw deadlocks since unmanaged code
1350          * might take locks.
1351          */
1352 #ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
1353         EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
1354         mono_mb_emit_byte (mb, CEE_LDC_I4_0);
1355         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1356         mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
1357         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_NONE);
1358 #endif
1359
1360         /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
1361         mono_mb_emit_ldarg (mb, 0);
1362         mono_mb_emit_ldloc (mb, real_size_var);
1363         if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
1364                 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
1365         } else if (atype == ATYPE_VECTOR) {
1366                 mono_mb_emit_ldarg (mb, 1);
1367                 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
1368         } else if (atype == ATYPE_STRING) {
1369                 mono_mb_emit_ldarg (mb, 1);
1370                 mono_mb_emit_icall (mb, mono_gc_alloc_string);
1371         } else {
1372                 g_assert_not_reached ();
1373         }
1374         mono_mb_emit_byte (mb, CEE_RET);
1375
1376         /* Fastpath */
1377         mono_mb_patch_short_branch (mb, slowpath_branch);
1378
1379         /* FIXME: Memory barrier */
1380
1381         /* tlab_next = new_next */
1382         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
1383         mono_mb_emit_ldloc (mb, new_next_var);
1384         mono_mb_emit_byte (mb, CEE_STIND_I);
1385
1386         /* *p = vtable; */
1387         mono_mb_emit_ldloc (mb, p_var);
1388         mono_mb_emit_ldarg (mb, 0);
1389         mono_mb_emit_byte (mb, CEE_STIND_I);
1390
1391         /* mark object end with nursery word */
1392         if (nursery_canaries_enabled ()) {
1393                         mono_mb_emit_ldloc (mb, p_var);
1394                         mono_mb_emit_ldloc (mb, real_size_var);
1395                         mono_mb_emit_byte (mb, MONO_CEE_ADD);
1396                         mono_mb_emit_icon8 (mb, (mword) CANARY_STRING);
1397                         mono_mb_emit_icon (mb, CANARY_SIZE);
1398                         mono_mb_emit_byte (mb, MONO_CEE_PREFIX1);
1399                         mono_mb_emit_byte (mb, CEE_CPBLK);
1400         }
1401
1402         if (atype == ATYPE_VECTOR) {
1403                 /* arr->max_length = max_length; */
1404                 mono_mb_emit_ldloc (mb, p_var);
1405                 mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, max_length));
1406                 mono_mb_emit_ldarg (mb, 1);
1407 #ifdef MONO_BIG_ARRAYS
1408                 mono_mb_emit_byte (mb, CEE_STIND_I);
1409 #else
1410                 mono_mb_emit_byte (mb, CEE_STIND_I4);
1411 #endif
1412         } else  if (atype == ATYPE_STRING) {
1413                 /* need to set length and clear the last char */
1414                 /* s->length = len; */
1415                 mono_mb_emit_ldloc (mb, p_var);
1416                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, length));
1417                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1418                 mono_mb_emit_ldarg (mb, 1);
1419                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1420         }
1421
1422 #ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
1423         EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
1424         mono_mb_emit_byte (mb, CEE_LDC_I4_0);
1425         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1426         mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
1427 #else
1428         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1429         mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
1430 #endif
1431         /*
1432         We must make sure both vtable and max_length are globaly visible before returning to managed land.
1433         */
1434         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL);
1435
1436         /* return p */
1437         mono_mb_emit_ldloc (mb, p_var);
1438
1439  done:
1440         mono_mb_emit_byte (mb, CEE_RET);
1441 #endif
1442
1443         info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1444         info->d.alloc.gc_name = "sgen";
1445         info->d.alloc.alloc_type = atype;
1446
1447 #ifndef DISABLE_JIT
1448         mb->init_locals = FALSE;
1449 #endif
1450
1451         res = mono_mb_create (mb, csig, 8, info);
1452         mono_mb_free (mb);
1453
1454
1455         return res;
1456 }
1457 #endif
1458
1459 int
1460 mono_gc_get_aligned_size_for_allocator (int size)
1461 {
1462         return SGEN_ALIGN_UP (size);
1463 }
1464
1465 /*
1466  * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
1467  * The signature of the called method is:
1468  *      object allocate (MonoVTable *vtable)
1469  */
1470 MonoMethod*
1471 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1472 {
1473 #ifdef MANAGED_ALLOCATION
1474         if (collect_before_allocs)
1475                 return NULL;
1476         if (!mono_runtime_has_tls_get ())
1477                 return NULL;
1478         if (klass->instance_size > tlab_size)
1479                 return NULL;
1480         if (known_instance_size && ALIGN_TO (klass->instance_size, SGEN_ALLOC_ALIGN) >= SGEN_MAX_SMALL_OBJ_SIZE)
1481                 return NULL;
1482         if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1483                 return NULL;
1484         if (klass->rank)
1485                 return NULL;
1486         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
1487                 return NULL;
1488         if (klass->byval_arg.type == MONO_TYPE_STRING)
1489                 return mono_gc_get_managed_allocator_by_type (ATYPE_STRING, MANAGED_ALLOCATOR_REGULAR);
1490         /* Generic classes have dynamic field and can go above MAX_SMALL_OBJ_SIZE. */
1491         if (known_instance_size)
1492                 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL, MANAGED_ALLOCATOR_REGULAR);
1493         else
1494                 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL, MANAGED_ALLOCATOR_REGULAR);
1495 #else
1496         return NULL;
1497 #endif
1498 }
1499
1500 MonoMethod*
1501 mono_gc_get_managed_array_allocator (MonoClass *klass)
1502 {
1503 #ifdef MANAGED_ALLOCATION
1504         if (klass->rank != 1)
1505                 return NULL;
1506         if (!mono_runtime_has_tls_get ())
1507                 return NULL;
1508         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
1509                 return NULL;
1510         if (has_per_allocation_action)
1511                 return NULL;
1512         g_assert (!mono_class_has_finalizer (klass) && !mono_class_is_marshalbyref (klass));
1513
1514         return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR, MANAGED_ALLOCATOR_REGULAR);
1515 #else
1516         return NULL;
1517 #endif
1518 }
1519
1520 void
1521 sgen_set_use_managed_allocator (gboolean flag)
1522 {
1523         use_managed_allocator = flag;
1524 }
1525
1526 MonoMethod*
1527 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1528 {
1529 #ifdef MANAGED_ALLOCATION
1530         MonoMethod *res;
1531         MonoMethod **cache;
1532
1533         if (variant == MANAGED_ALLOCATOR_REGULAR && !use_managed_allocator)
1534                 return NULL;
1535
1536         if (variant == MANAGED_ALLOCATOR_REGULAR && !mono_runtime_has_tls_get ())
1537                 return NULL;
1538
1539         switch (variant) {
1540         case MANAGED_ALLOCATOR_REGULAR: cache = alloc_method_cache; break;
1541         case MANAGED_ALLOCATOR_SLOW_PATH: cache = slowpath_alloc_method_cache; break;
1542         default: g_assert_not_reached (); break;
1543         }
1544
1545         res = cache [atype];
1546         if (res)
1547                 return res;
1548
1549         res = create_allocator (atype, variant);
1550         LOCK_GC;
1551         if (cache [atype]) {
1552                 mono_free_method (res);
1553                 res = cache [atype];
1554         } else {
1555                 mono_memory_barrier ();
1556                 cache [atype] = res;
1557         }
1558         UNLOCK_GC;
1559
1560         return res;
1561 #else
1562         return NULL;
1563 #endif
1564 }
1565
1566 guint32
1567 mono_gc_get_managed_allocator_types (void)
1568 {
1569         return ATYPE_NUM;
1570 }
1571
1572 gboolean
1573 sgen_is_managed_allocator (MonoMethod *method)
1574 {
1575         int i;
1576
1577         for (i = 0; i < ATYPE_NUM; ++i)
1578                 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1579                         return TRUE;
1580         return FALSE;
1581 }
1582
1583 gboolean
1584 sgen_has_managed_allocator (void)
1585 {
1586         int i;
1587
1588         for (i = 0; i < ATYPE_NUM; ++i)
1589                 if (alloc_method_cache [i] || slowpath_alloc_method_cache [i])
1590                         return TRUE;
1591         return FALSE;
1592 }
1593
1594 /*
1595  * Cardtable scanning
1596  */
1597
1598 #define MWORD_MASK (sizeof (mword) - 1)
1599
1600 static inline int
1601 find_card_offset (mword card)
1602 {
1603 /*XXX Use assembly as this generates some pretty bad code */
1604 #if defined(__i386__) && defined(__GNUC__)
1605         return  (__builtin_ffs (card) - 1) / 8;
1606 #elif defined(__x86_64__) && defined(__GNUC__)
1607         return (__builtin_ffsll (card) - 1) / 8;
1608 #elif defined(__s390x__)
1609         return (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
1610 #else
1611         int i;
1612         guint8 *ptr = (guint8 *) &card;
1613         for (i = 0; i < sizeof (mword); ++i) {
1614                 if (ptr[i])
1615                         return i;
1616         }
1617         return 0;
1618 #endif
1619 }
1620
1621 static guint8*
1622 find_next_card (guint8 *card_data, guint8 *end)
1623 {
1624         mword *cards, *cards_end;
1625         mword card;
1626
1627         while ((((mword)card_data) & MWORD_MASK) && card_data < end) {
1628                 if (*card_data)
1629                         return card_data;
1630                 ++card_data;
1631         }
1632
1633         if (card_data == end)
1634                 return end;
1635
1636         cards = (mword*)card_data;
1637         cards_end = (mword*)((mword)end & ~MWORD_MASK);
1638         while (cards < cards_end) {
1639                 card = *cards;
1640                 if (card)
1641                         return (guint8*)cards + find_card_offset (card);
1642                 ++cards;
1643         }
1644
1645         card_data = (guint8*)cards_end;
1646         while (card_data < end) {
1647                 if (*card_data)
1648                         return card_data;
1649                 ++card_data;
1650         }
1651
1652         return end;
1653 }
1654
1655 #define ARRAY_OBJ_INDEX(ptr,array,elem_size) (((char*)(ptr) - ((char*)(array) + G_STRUCT_OFFSET (MonoArray, vector))) / (elem_size))
1656
1657 gboolean
1658 sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, ScanCopyContext ctx)
1659 {
1660         MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
1661         MonoClass *klass = vt->klass;
1662
1663         SGEN_ASSERT (0, SGEN_VTABLE_HAS_REFERENCES (vt), "Why would we ever call this on reference-free objects?");
1664
1665         if (vt->rank) {
1666                 MonoArray *arr = (MonoArray*)obj;
1667                 guint8 *card_data, *card_base;
1668                 guint8 *card_data_end;
1669                 char *obj_start = (char *)sgen_card_table_align_pointer (obj);
1670                 mword bounds_size;
1671                 mword obj_size = sgen_mono_array_size (vt, arr, &bounds_size, sgen_vtable_get_descriptor (vt));
1672                 /* We don't want to scan the bounds entries at the end of multidimensional arrays */
1673                 char *obj_end = (char*)obj + obj_size - bounds_size;
1674                 size_t card_count;
1675                 size_t extra_idx = 0;
1676
1677                 mword desc = (mword)klass->element_class->gc_descr;
1678                 int elem_size = mono_array_element_size (klass);
1679
1680 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1681                 guint8 *overflow_scan_end = NULL;
1682 #endif
1683
1684 #ifdef SGEN_OBJECT_LAYOUT_STATISTICS
1685                 if (klass->element_class->valuetype)
1686                         sgen_object_layout_scanned_vtype_array ();
1687                 else
1688                         sgen_object_layout_scanned_ref_array ();
1689 #endif
1690
1691                 if (cards)
1692                         card_data = cards;
1693                 else
1694                         card_data = sgen_card_table_get_card_scan_address ((mword)obj);
1695
1696                 card_base = card_data;
1697                 card_count = sgen_card_table_number_of_cards_in_range ((mword)obj, obj_size);
1698                 card_data_end = card_data + card_count;
1699
1700
1701 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1702                 /*Check for overflow and if so, setup to scan in two steps*/
1703                 if (!cards && card_data_end >= SGEN_SHADOW_CARDTABLE_END) {
1704                         overflow_scan_end = sgen_shadow_cardtable + (card_data_end - SGEN_SHADOW_CARDTABLE_END);
1705                         card_data_end = SGEN_SHADOW_CARDTABLE_END;
1706                 }
1707
1708 LOOP_HEAD:
1709 #endif
1710
1711                 card_data = find_next_card (card_data, card_data_end);
1712                 for (; card_data < card_data_end; card_data = find_next_card (card_data + 1, card_data_end)) {
1713                         size_t index;
1714                         size_t idx = (card_data - card_base) + extra_idx;
1715                         char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES);
1716                         char *card_end = start + CARD_SIZE_IN_BYTES;
1717                         char *first_elem, *elem;
1718
1719                         HEAVY_STAT (++los_marked_cards);
1720
1721                         if (!cards)
1722                                 sgen_card_table_prepare_card_for_scanning (card_data);
1723
1724                         card_end = MIN (card_end, obj_end);
1725
1726                         if (start <= (char*)arr->vector)
1727                                 index = 0;
1728                         else
1729                                 index = ARRAY_OBJ_INDEX (start, obj, elem_size);
1730
1731                         elem = first_elem = (char*)mono_array_addr_with_size_fast ((MonoArray*)obj, elem_size, index);
1732                         if (klass->element_class->valuetype) {
1733                                 ScanVTypeFunc scan_vtype_func = ctx.ops->scan_vtype;
1734
1735                                 for (; elem < card_end; elem += elem_size)
1736                                         scan_vtype_func (obj, elem, desc, ctx.queue BINARY_PROTOCOL_ARG (elem_size));
1737                         } else {
1738                                 ScanPtrFieldFunc scan_ptr_field_func = ctx.ops->scan_ptr_field;
1739
1740                                 HEAVY_STAT (++los_array_cards);
1741                                 for (; elem < card_end; elem += SIZEOF_VOID_P)
1742                                         scan_ptr_field_func (obj, (GCObject**)elem, ctx.queue);
1743                         }
1744
1745                         binary_protocol_card_scan (first_elem, elem - first_elem);
1746                 }
1747
1748 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1749                 if (overflow_scan_end) {
1750                         extra_idx = card_data - card_base;
1751                         card_base = card_data = sgen_shadow_cardtable;
1752                         card_data_end = overflow_scan_end;
1753                         overflow_scan_end = NULL;
1754                         goto LOOP_HEAD;
1755                 }
1756 #endif
1757                 return TRUE;
1758         }
1759
1760         return FALSE;
1761 }
1762
1763 /*
1764  * Array and string allocation
1765  */
1766
1767 void*
1768 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
1769 {
1770         MonoArray *arr;
1771         TLAB_ACCESS_INIT;
1772
1773         if (!SGEN_CAN_ALIGN_UP (size))
1774                 return NULL;
1775
1776 #ifndef DISABLE_CRITICAL_REGION
1777         ENTER_CRITICAL_REGION;
1778         arr = (MonoArray*)sgen_try_alloc_obj_nolock (vtable, size);
1779         if (arr) {
1780                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1781                 arr->max_length = (mono_array_size_t)max_length;
1782                 EXIT_CRITICAL_REGION;
1783                 goto done;
1784         }
1785         EXIT_CRITICAL_REGION;
1786 #endif
1787
1788         LOCK_GC;
1789
1790         arr = (MonoArray*)sgen_alloc_obj_nolock (vtable, size);
1791         if (G_UNLIKELY (!arr)) {
1792                 UNLOCK_GC;
1793                 return NULL;
1794         }
1795
1796         arr->max_length = (mono_array_size_t)max_length;
1797
1798         UNLOCK_GC;
1799
1800  done:
1801         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
1802                 mono_profiler_allocation (&arr->obj);
1803
1804         SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Vector has incorrect size.");
1805         return arr;
1806 }
1807
1808 void*
1809 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
1810 {
1811         MonoArray *arr;
1812         MonoArrayBounds *bounds;
1813         TLAB_ACCESS_INIT;
1814
1815         if (!SGEN_CAN_ALIGN_UP (size))
1816                 return NULL;
1817
1818 #ifndef DISABLE_CRITICAL_REGION
1819         ENTER_CRITICAL_REGION;
1820         arr = (MonoArray*)sgen_try_alloc_obj_nolock (vtable, size);
1821         if (arr) {
1822                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1823                 arr->max_length = (mono_array_size_t)max_length;
1824
1825                 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
1826                 arr->bounds = bounds;
1827                 EXIT_CRITICAL_REGION;
1828                 goto done;
1829         }
1830         EXIT_CRITICAL_REGION;
1831 #endif
1832
1833         LOCK_GC;
1834
1835         arr = (MonoArray*)sgen_alloc_obj_nolock (vtable, size);
1836         if (G_UNLIKELY (!arr)) {
1837                 UNLOCK_GC;
1838                 return NULL;
1839         }
1840
1841         arr->max_length = (mono_array_size_t)max_length;
1842
1843         bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
1844         arr->bounds = bounds;
1845
1846         UNLOCK_GC;
1847
1848  done:
1849         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
1850                 mono_profiler_allocation (&arr->obj);
1851
1852         SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Array has incorrect size.");
1853         return arr;
1854 }
1855
1856 void*
1857 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
1858 {
1859         MonoString *str;
1860         TLAB_ACCESS_INIT;
1861
1862         if (!SGEN_CAN_ALIGN_UP (size))
1863                 return NULL;
1864
1865 #ifndef DISABLE_CRITICAL_REGION
1866         ENTER_CRITICAL_REGION;
1867         str = (MonoString*)sgen_try_alloc_obj_nolock (vtable, size);
1868         if (str) {
1869                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1870                 str->length = len;
1871                 EXIT_CRITICAL_REGION;
1872                 goto done;
1873         }
1874         EXIT_CRITICAL_REGION;
1875 #endif
1876
1877         LOCK_GC;
1878
1879         str = (MonoString*)sgen_alloc_obj_nolock (vtable, size);
1880         if (G_UNLIKELY (!str)) {
1881                 UNLOCK_GC;
1882                 return NULL;
1883         }
1884
1885         str->length = len;
1886
1887         UNLOCK_GC;
1888
1889  done:
1890         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
1891                 mono_profiler_allocation (&str->object);
1892
1893         return str;
1894 }
1895
1896 /*
1897  * Strings
1898  */
1899
1900 void
1901 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1902 {
1903         mono_unichar2 *new_end = str->chars + new_length;
1904
1905         /* zero the discarded string. This null-delimits the string and allows
1906          * the space to be reclaimed by SGen. */
1907
1908         if (nursery_canaries_enabled () && sgen_ptr_in_nursery (str)) {
1909                 CHECK_CANARY_FOR_OBJECT ((GCObject*)str, TRUE);
1910                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2) + CANARY_SIZE);
1911                 memcpy (new_end + 1 , CANARY_STRING, CANARY_SIZE);
1912         } else {
1913                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1914         }
1915
1916         str->length = new_length;
1917 }
1918
1919 /*
1920  * Profiling
1921  */
1922
1923 #define GC_ROOT_NUM 32
1924 typedef struct {
1925         int count;              /* must be the first field */
1926         void *objects [GC_ROOT_NUM];
1927         int root_types [GC_ROOT_NUM];
1928         uintptr_t extra_info [GC_ROOT_NUM];
1929 } GCRootReport;
1930
1931 static void
1932 notify_gc_roots (GCRootReport *report)
1933 {
1934         if (!report->count)
1935                 return;
1936         mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
1937         report->count = 0;
1938 }
1939
1940 static void
1941 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
1942 {
1943         if (report->count == GC_ROOT_NUM)
1944                 notify_gc_roots (report);
1945         report->objects [report->count] = object;
1946         report->root_types [report->count] = rtype;
1947         report->extra_info [report->count++] = (uintptr_t)SGEN_LOAD_VTABLE (object)->klass;
1948 }
1949
1950 void
1951 sgen_client_nursery_objects_pinned (void **definitely_pinned, int count)
1952 {
1953         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1954                 GCRootReport report;
1955                 int idx;
1956                 report.count = 0;
1957                 for (idx = 0; idx < count; ++idx)
1958                         add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
1959                 notify_gc_roots (&report);
1960         }
1961 }
1962
1963 static void
1964 report_finalizer_roots_from_queue (SgenPointerQueue *queue)
1965 {
1966         GCRootReport report;
1967         size_t i;
1968
1969         report.count = 0;
1970         for (i = 0; i < queue->next_slot; ++i) {
1971                 void *obj = queue->data [i];
1972                 if (!obj)
1973                         continue;
1974                 add_profile_gc_root (&report, obj, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
1975         }
1976         notify_gc_roots (&report);
1977 }
1978
1979 static void
1980 report_finalizer_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
1981 {
1982         report_finalizer_roots_from_queue (fin_ready_queue);
1983         report_finalizer_roots_from_queue (critical_fin_queue);
1984 }
1985
1986 static GCRootReport *root_report;
1987
1988 static void
1989 single_arg_report_root (MonoObject **obj, void *gc_data)
1990 {
1991         if (*obj)
1992                 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
1993 }
1994
1995 static void
1996 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
1997 {
1998         switch (desc & ROOT_DESC_TYPE_MASK) {
1999         case ROOT_DESC_BITMAP:
2000                 desc >>= ROOT_DESC_TYPE_SHIFT;
2001                 while (desc) {
2002                         if ((desc & 1) && *start_root) {
2003                                 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
2004                         }
2005                         desc >>= 1;
2006                         start_root++;
2007                 }
2008                 return;
2009         case ROOT_DESC_COMPLEX: {
2010                 gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
2011                 gsize bwords = (*bitmap_data) - 1;
2012                 void **start_run = start_root;
2013                 bitmap_data++;
2014                 while (bwords-- > 0) {
2015                         gsize bmap = *bitmap_data++;
2016                         void **objptr = start_run;
2017                         while (bmap) {
2018                                 if ((bmap & 1) && *objptr) {
2019                                         add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
2020                                 }
2021                                 bmap >>= 1;
2022                                 ++objptr;
2023                         }
2024                         start_run += GC_BITS_PER_WORD;
2025                 }
2026                 break;
2027         }
2028         case ROOT_DESC_USER: {
2029                 MonoGCRootMarkFunc marker = (MonoGCRootMarkFunc)sgen_get_user_descriptor_func (desc);
2030                 root_report = report;
2031                 marker ((MonoObject**)start_root, single_arg_report_root, NULL);
2032                 break;
2033         }
2034         case ROOT_DESC_RUN_LEN:
2035                 g_assert_not_reached ();
2036         default:
2037                 g_assert_not_reached ();
2038         }
2039 }
2040
2041 static void
2042 report_registered_roots_by_type (int root_type)
2043 {
2044         GCRootReport report;
2045         void **start_root;
2046         RootRecord *root;
2047         report.count = 0;
2048         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) {
2049                 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
2050                 precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc);
2051         } SGEN_HASH_TABLE_FOREACH_END;
2052         notify_gc_roots (&report);
2053 }
2054
2055 static void
2056 report_registered_roots (void)
2057 {
2058         report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2059         report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2060 }
2061
2062 void
2063 sgen_client_collecting_minor (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
2064 {
2065         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2066                 report_registered_roots ();
2067         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2068                 report_finalizer_roots (fin_ready_queue, critical_fin_queue);
2069 }
2070
2071 static GCRootReport major_root_report;
2072 static gboolean profile_roots;
2073
2074 void
2075 sgen_client_collecting_major_1 (void)
2076 {
2077         profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
2078         memset (&major_root_report, 0, sizeof (GCRootReport));
2079 }
2080
2081 void
2082 sgen_client_pinned_los_object (GCObject *obj)
2083 {
2084         if (profile_roots)
2085                 add_profile_gc_root (&major_root_report, (char*)obj, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
2086 }
2087
2088 void
2089 sgen_client_collecting_major_2 (void)
2090 {
2091         if (profile_roots)
2092                 notify_gc_roots (&major_root_report);
2093
2094         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2095                 report_registered_roots ();
2096 }
2097
2098 void
2099 sgen_client_collecting_major_3 (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
2100 {
2101         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2102                 report_finalizer_roots (fin_ready_queue, critical_fin_queue);
2103 }
2104
2105 #define MOVED_OBJECTS_NUM 64
2106 static void *moved_objects [MOVED_OBJECTS_NUM];
2107 static int moved_objects_idx = 0;
2108
2109 static SgenPointerQueue moved_objects_queue = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_MOVED_OBJECT);
2110
2111 void
2112 mono_sgen_register_moved_object (void *obj, void *destination)
2113 {
2114         /*
2115          * This function can be called from SGen's worker threads. We want to try
2116          * and avoid exposing those threads to the profiler API, so queue up move
2117          * events and send them later when the main GC thread calls
2118          * mono_sgen_gc_event_moves ().
2119          *
2120          * TODO: Once SGen has multiple worker threads, we need to switch to a
2121          * lock-free data structure for the queue as multiple threads will be
2122          * adding to it at the same time.
2123          */
2124         if (sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ())) {
2125                 sgen_pointer_queue_add (&moved_objects_queue, obj);
2126                 sgen_pointer_queue_add (&moved_objects_queue, destination);
2127         } else {
2128                 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2129                         mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2130                         moved_objects_idx = 0;
2131                 }
2132
2133                 moved_objects [moved_objects_idx++] = obj;
2134                 moved_objects [moved_objects_idx++] = destination;
2135         }
2136 }
2137
2138 void
2139 mono_sgen_gc_event_moves (void)
2140 {
2141         while (!sgen_pointer_queue_is_empty (&moved_objects_queue)) {
2142                 void *dst = sgen_pointer_queue_pop (&moved_objects_queue);
2143                 void *src = sgen_pointer_queue_pop (&moved_objects_queue);
2144
2145                 mono_sgen_register_moved_object (src, dst);
2146         }
2147
2148         if (moved_objects_idx) {
2149                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2150                 moved_objects_idx = 0;
2151         }
2152 }
2153
2154 /*
2155  * Heap walking
2156  */
2157
2158 #define REFS_SIZE 128
2159 typedef struct {
2160         void *data;
2161         MonoGCReferences callback;
2162         int flags;
2163         int count;
2164         int called;
2165         MonoObject *refs [REFS_SIZE];
2166         uintptr_t offsets [REFS_SIZE];
2167 } HeapWalkInfo;
2168
2169 #undef HANDLE_PTR
2170 #define HANDLE_PTR(ptr,obj)     do {    \
2171                 if (*(ptr)) {   \
2172                         if (hwi->count == REFS_SIZE) {  \
2173                                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);    \
2174                                 hwi->count = 0; \
2175                                 hwi->called = 1;        \
2176                         }       \
2177                         hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start;  \
2178                         hwi->refs [hwi->count++] = *(ptr);      \
2179                 }       \
2180         } while (0)
2181
2182 static void
2183 collect_references (HeapWalkInfo *hwi, GCObject *obj, size_t size)
2184 {
2185         char *start = (char*)obj;
2186         mword desc = sgen_obj_get_descriptor (obj);
2187
2188 #include "sgen/sgen-scan-object.h"
2189 }
2190
2191 static void
2192 walk_references (GCObject *start, size_t size, void *data)
2193 {
2194         HeapWalkInfo *hwi = (HeapWalkInfo *)data;
2195         hwi->called = 0;
2196         hwi->count = 0;
2197         collect_references (hwi, start, size);
2198         if (hwi->count || !hwi->called)
2199                 hwi->callback (start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
2200 }
2201
2202 /**
2203  * mono_gc_walk_heap:
2204  * @flags: flags for future use
2205  * @callback: a function pointer called for each object in the heap
2206  * @data: a user data pointer that is passed to callback
2207  *
2208  * This function can be used to iterate over all the live objects in the heap:
2209  * for each object, @callback is invoked, providing info about the object's
2210  * location in memory, its class, its size and the objects it references.
2211  * For each referenced object it's offset from the object address is
2212  * reported in the offsets array.
2213  * The object references may be buffered, so the callback may be invoked
2214  * multiple times for the same object: in all but the first call, the size
2215  * argument will be zero.
2216  * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
2217  * profiler event handler.
2218  *
2219  * Returns: a non-zero value if the GC doesn't support heap walking
2220  */
2221 int
2222 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
2223 {
2224         HeapWalkInfo hwi;
2225
2226         hwi.flags = flags;
2227         hwi.callback = callback;
2228         hwi.data = data;
2229
2230         sgen_clear_nursery_fragments ();
2231         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE, TRUE);
2232
2233         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, walk_references, &hwi);
2234         sgen_los_iterate_objects (walk_references, &hwi);
2235
2236         return 0;
2237 }
2238
2239 /*
2240  * Threads
2241  */
2242
2243 void
2244 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
2245 {
2246         gc_callbacks = *callbacks;
2247 }
2248
2249 MonoGCCallbacks *
2250 mono_gc_get_gc_callbacks ()
2251 {
2252         return &gc_callbacks;
2253 }
2254
2255 void
2256 sgen_client_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback)
2257 {
2258         size_t stsize = 0;
2259         guint8 *staddr = NULL;
2260
2261 #ifndef HAVE_KW_THREAD
2262         g_assert (!mono_native_tls_get_value (thread_info_key));
2263         mono_native_tls_set_value (thread_info_key, info);
2264 #else
2265         sgen_thread_info = info;
2266 #endif
2267
2268         info->client_info.skip = 0;
2269
2270         info->client_info.stack_start = NULL;
2271
2272 #ifdef SGEN_POSIX_STW
2273         info->client_info.stop_count = -1;
2274         info->client_info.signal = 0;
2275 #endif
2276
2277         mono_thread_info_get_stack_bounds (&staddr, &stsize);
2278         if (staddr) {
2279                 info->client_info.stack_start_limit = staddr;
2280                 info->client_info.stack_end = staddr + stsize;
2281         } else {
2282                 gsize stack_bottom = (gsize)stack_bottom_fallback;
2283                 stack_bottom += 4095;
2284                 stack_bottom &= ~4095;
2285                 info->client_info.stack_end = (char*)stack_bottom;
2286         }
2287
2288         memset (&info->client_info.ctx, 0, sizeof (MonoContext));
2289
2290         if (mono_gc_get_gc_callbacks ()->thread_attach_func)
2291                 info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
2292
2293         binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
2294
2295         SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->client_info.stack_end);
2296
2297         info->client_info.info.handle_stack = mono_handle_stack_alloc ();
2298 }
2299
2300 void
2301 sgen_client_thread_unregister (SgenThreadInfo *p)
2302 {
2303         MonoNativeThreadId tid;
2304
2305 #ifndef HAVE_KW_THREAD
2306         mono_native_tls_set_value (thread_info_key, NULL);
2307 #else
2308         sgen_thread_info = NULL;
2309 #endif
2310
2311         tid = mono_thread_info_get_tid (p);
2312
2313         if (p->client_info.info.runtime_thread)
2314                 mono_threads_add_joinable_thread ((gpointer)tid);
2315
2316         if (mono_gc_get_gc_callbacks ()->thread_detach_func) {
2317                 mono_gc_get_gc_callbacks ()->thread_detach_func (p->client_info.runtime_data);
2318                 p->client_info.runtime_data = NULL;
2319         }
2320
2321         binary_protocol_thread_unregister ((gpointer)tid);
2322         SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid);
2323
2324         HandleStack *handles = (HandleStack*) p->client_info.info.handle_stack;
2325         p->client_info.info.handle_stack = NULL;
2326         mono_handle_stack_free (handles);
2327 }
2328
2329 void
2330 mono_gc_set_skip_thread (gboolean skip)
2331 {
2332         SgenThreadInfo *info = mono_thread_info_current ();
2333
2334         LOCK_GC;
2335         info->client_info.gc_disabled = skip;
2336         UNLOCK_GC;
2337 }
2338
2339 static gboolean
2340 thread_in_critical_region (SgenThreadInfo *info)
2341 {
2342         return info->client_info.in_critical_region;
2343 }
2344
2345 static void
2346 sgen_thread_attach (SgenThreadInfo *info)
2347 {
2348         if (mono_gc_get_gc_callbacks ()->thread_attach_func && !info->client_info.runtime_data)
2349                 info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
2350 }
2351
2352 static void
2353 sgen_thread_detach (SgenThreadInfo *p)
2354 {
2355         /* If a delegate is passed to native code and invoked on a thread we dont
2356          * know about, marshal will register it with mono_threads_attach_coop, but
2357          * we have no way of knowing when that thread goes away.  SGen has a TSD
2358          * so we assume that if the domain is still registered, we can detach
2359          * the thread
2360          */
2361         if (mono_thread_internal_current_is_attached ())
2362                 mono_thread_detach_internal (mono_thread_internal_current ());
2363 }
2364
2365 gboolean
2366 mono_gc_register_thread (void *baseptr)
2367 {
2368         return mono_thread_info_attach (baseptr) != NULL;
2369 }
2370
2371 gboolean
2372 mono_gc_is_gc_thread (void)
2373 {
2374         gboolean result;
2375         LOCK_GC;
2376         result = mono_thread_info_current () != NULL;
2377         UNLOCK_GC;
2378         return result;
2379 }
2380
2381 void
2382 sgen_client_thread_register_worker (void)
2383 {
2384         mono_thread_info_register_small_id ();
2385         mono_native_thread_set_name (mono_native_thread_id_get (), "SGen worker");
2386 }
2387
2388 /* Variables holding start/end nursery so it won't have to be passed at every call */
2389 static void *scan_area_arg_start, *scan_area_arg_end;
2390
2391 void
2392 mono_gc_conservatively_scan_area (void *start, void *end)
2393 {
2394         sgen_conservatively_pin_objects_from ((void **)start, (void **)end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
2395 }
2396
2397 void*
2398 mono_gc_scan_object (void *obj, void *gc_data)
2399 {
2400         ScanCopyContext *ctx = (ScanCopyContext *)gc_data;
2401         ctx->ops->copy_or_mark_object ((GCObject**)&obj, ctx->queue);
2402         return obj;
2403 }
2404
2405 /*
2406  * Mark from thread stacks and registers.
2407  */
2408 void
2409 sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx)
2410 {
2411         scan_area_arg_start = start_nursery;
2412         scan_area_arg_end = end_nursery;
2413
2414         FOREACH_THREAD (info) {
2415                 int skip_reason = 0;
2416                 void *aligned_stack_start;
2417
2418                 if (info->client_info.skip) {
2419                         SGEN_LOG (3, "Skipping dead thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start);
2420                         skip_reason = 1;
2421                 } else if (info->client_info.gc_disabled) {
2422                         SGEN_LOG (3, "GC disabled for thread %p, range: %p-%p, size: %zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start);
2423                         skip_reason = 2;
2424                 } else if (!mono_thread_info_is_live (info)) {
2425                         SGEN_LOG (3, "Skipping non-running thread %p, range: %p-%p, size: %zd (state %x)", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start, info->client_info.info.thread_state);
2426                         skip_reason = 3;
2427                 } else if (!info->client_info.stack_start) {
2428                         SGEN_LOG (3, "Skipping starting or detaching thread %p", info);
2429                         skip_reason = 4;
2430                 }
2431
2432                 binary_protocol_scan_stack ((gpointer)mono_thread_info_get_tid (info), info->client_info.stack_start, info->client_info.stack_end, skip_reason);
2433
2434                 if (skip_reason)
2435                         continue;
2436
2437                 g_assert (info->client_info.stack_start);
2438                 g_assert (info->client_info.stack_end);
2439
2440                 aligned_stack_start = (void*)(mword) ALIGN_TO ((mword)info->client_info.stack_start, SIZEOF_VOID_P);
2441 #ifdef HOST_WIN32
2442                 /* Windows uses a guard page before the committed stack memory pages to detect when the
2443                    stack needs to be grown. If we suspend a thread just after a function prolog has
2444                    decremented the stack pointer to point into the guard page but before the thread has
2445                    been able to read or write to that page, starting the stack scan at aligned_stack_start
2446                    will raise a STATUS_GUARD_PAGE_VIOLATION and the process will crash. This code uses
2447                    VirtualQuery() to determine whether stack_start points into the guard page and then
2448                    updates aligned_stack_start to point at the next non-guard page. */
2449                 MEMORY_BASIC_INFORMATION mem_info;
2450                 SIZE_T result = VirtualQuery(info->client_info.stack_start, &mem_info, sizeof(mem_info));
2451                 g_assert (result != 0);
2452                 if (mem_info.Protect & PAGE_GUARD) {
2453                         aligned_stack_start = ((char*) mem_info.BaseAddress) + mem_info.RegionSize;
2454                 }
2455 #endif
2456
2457                 g_assert (info->client_info.suspend_done);
2458                 SGEN_LOG (3, "Scanning thread %p, range: %p-%p, size: %zd, pinned=%zd", info, info->client_info.stack_start, info->client_info.stack_end, (char*)info->client_info.stack_end - (char*)info->client_info.stack_start, sgen_get_pinned_count ());
2459                 if (mono_gc_get_gc_callbacks ()->thread_mark_func && !conservative_stack_mark) {
2460                         mono_gc_get_gc_callbacks ()->thread_mark_func (info->client_info.runtime_data, (guint8 *)aligned_stack_start, (guint8 *)info->client_info.stack_end, precise, &ctx);
2461                 } else if (!precise) {
2462                         if (!conservative_stack_mark) {
2463                                 fprintf (stderr, "Precise stack mark not supported - disabling.\n");
2464                                 conservative_stack_mark = TRUE;
2465                         }
2466                         //FIXME we should eventually use the new stack_mark from coop
2467                         sgen_conservatively_pin_objects_from ((void **)aligned_stack_start, (void **)info->client_info.stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
2468                 }
2469
2470                 if (!precise) {
2471                         sgen_conservatively_pin_objects_from ((void**)&info->client_info.ctx, (void**)(&info->client_info.ctx + 1),
2472                                 start_nursery, end_nursery, PIN_TYPE_STACK);
2473
2474                         {
2475                                 // This is used on Coop GC for platforms where we cannot get the data for individual registers.
2476                                 // We force a spill of all registers into the stack and pass a chunk of data into sgen.
2477                                 //FIXME under coop, for now, what we need to ensure is that we scan any extra memory from info->client_info.stack_end to stack_mark
2478                                 MonoThreadUnwindState *state = &info->client_info.info.thread_saved_state [SELF_SUSPEND_STATE_INDEX];
2479                                 if (state && state->gc_stackdata) {
2480                                         sgen_conservatively_pin_objects_from ((void **)state->gc_stackdata, (void**)((char*)state->gc_stackdata + state->gc_stackdata_size),
2481                                                 start_nursery, end_nursery, PIN_TYPE_STACK);
2482                                 }
2483                         }
2484                 }
2485                 if (precise && info->client_info.info.handle_stack) {
2486                         mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, (GcScanFunc)ctx.ops->copy_or_mark_object, ctx.queue);
2487                 }
2488         } FOREACH_THREAD_END
2489 }
2490
2491 /*
2492  * mono_gc_set_stack_end:
2493  *
2494  *   Set the end of the current threads stack to STACK_END. The stack space between 
2495  * STACK_END and the real end of the threads stack will not be scanned during collections.
2496  */
2497 void
2498 mono_gc_set_stack_end (void *stack_end)
2499 {
2500         SgenThreadInfo *info;
2501
2502         LOCK_GC;
2503         info = mono_thread_info_current ();
2504         if (info) {
2505                 SGEN_ASSERT (0, stack_end < info->client_info.stack_end, "Can only lower stack end");
2506                 info->client_info.stack_end = stack_end;
2507         }
2508         UNLOCK_GC;
2509 }
2510
2511 /*
2512  * Roots
2513  */
2514
2515 int
2516 mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
2517 {
2518         return sgen_register_root (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED, source, msg);
2519 }
2520
2521 int
2522 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
2523 {
2524         return sgen_register_root (start, size, descr, ROOT_TYPE_WBARRIER, source, msg);
2525 }
2526
2527 void
2528 mono_gc_deregister_root (char* addr)
2529 {
2530         sgen_deregister_root (addr);
2531 }
2532
2533 /*
2534  * PThreads
2535  */
2536
2537 #ifndef HOST_WIN32
2538 int
2539 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
2540 {
2541         return pthread_create (new_thread, attr, start_routine, arg);
2542 }
2543 #endif
2544
2545 /*
2546  * Miscellaneous
2547  */
2548
2549 void
2550 sgen_client_total_allocated_heap_changed (size_t allocated_heap)
2551 {
2552         mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap);
2553 }
2554
2555 gboolean
2556 mono_gc_user_markers_supported (void)
2557 {
2558         return TRUE;
2559 }
2560
2561 gboolean
2562 mono_object_is_alive (MonoObject* o)
2563 {
2564         return TRUE;
2565 }
2566
2567 int
2568 mono_gc_get_generation (MonoObject *obj)
2569 {
2570         if (sgen_ptr_in_nursery (obj))
2571                 return 0;
2572         return 1;
2573 }
2574
2575 const char *
2576 mono_gc_get_gc_name (void)
2577 {
2578         return "sgen";
2579 }
2580
2581 char*
2582 mono_gc_get_description (void)
2583 {
2584 #ifdef HAVE_CONC_GC_AS_DEFAULT
2585         return g_strdup ("sgen (concurrent by default)");
2586 #else
2587         return g_strdup ("sgen");
2588 #endif
2589 }
2590
2591 void
2592 mono_gc_set_desktop_mode (void)
2593 {
2594 }
2595
2596 gboolean
2597 mono_gc_is_moving (void)
2598 {
2599         return TRUE;
2600 }
2601
2602 gboolean
2603 mono_gc_is_disabled (void)
2604 {
2605         return FALSE;
2606 }
2607
2608 #ifdef HOST_WIN32
2609 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
2610 {
2611         return TRUE;
2612 }
2613 #endif
2614
2615 int
2616 mono_gc_max_generation (void)
2617 {
2618         return 1;
2619 }
2620
2621 gboolean
2622 mono_gc_precise_stack_mark_enabled (void)
2623 {
2624         return !conservative_stack_mark;
2625 }
2626
2627 void
2628 mono_gc_collect (int generation)
2629 {
2630         sgen_gc_collect (generation);
2631 }
2632
2633 int
2634 mono_gc_collection_count (int generation)
2635 {
2636         return sgen_gc_collection_count (generation);
2637 }
2638
2639 int64_t
2640 mono_gc_get_used_size (void)
2641 {
2642         return (int64_t)sgen_gc_get_used_size ();
2643 }
2644
2645 int64_t
2646 mono_gc_get_heap_size (void)
2647 {
2648         return (int64_t)sgen_gc_get_total_heap_allocation ();
2649 }
2650
2651 MonoGCDescriptor
2652 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
2653 {
2654         return sgen_make_user_root_descriptor (marker);
2655 }
2656
2657 MonoGCDescriptor
2658 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
2659 {
2660         return SGEN_DESC_STRING;
2661 }
2662
2663 void*
2664 mono_gc_get_nursery (int *shift_bits, size_t *size)
2665 {
2666         *size = sgen_nursery_size;
2667         *shift_bits = DEFAULT_NURSERY_BITS;
2668         return sgen_get_nursery_start ();
2669 }
2670
2671 int
2672 mono_gc_get_los_limit (void)
2673 {
2674         return SGEN_MAX_SMALL_OBJ_SIZE;
2675 }
2676
2677 gpointer
2678 sgen_client_default_metadata (void)
2679 {
2680         return mono_domain_get ();
2681 }
2682
2683 gpointer
2684 sgen_client_metadata_for_object (GCObject *obj)
2685 {
2686         return mono_object_domain (obj);
2687 }
2688
2689 /**
2690  * mono_gchandle_is_in_domain:
2691  * @gchandle: a GCHandle's handle.
2692  * @domain: An application domain.
2693  *
2694  * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
2695  */
2696 gboolean
2697 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
2698 {
2699         MonoDomain *gchandle_domain = (MonoDomain *)sgen_gchandle_get_metadata (gchandle);
2700         return domain->domain_id == gchandle_domain->domain_id;
2701 }
2702
2703 /**
2704  * mono_gchandle_free_domain:
2705  * @unloading: domain that is unloading
2706  *
2707  * Function used internally to cleanup any GC handle for objects belonging
2708  * to the specified domain during appdomain unload.
2709  */
2710 void
2711 mono_gchandle_free_domain (MonoDomain *unloading)
2712 {
2713 }
2714
2715 static gpointer
2716 null_link_if_in_domain (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
2717 {
2718         MonoDomain *unloading_domain = (MonoDomain *)user;
2719         MonoDomain *obj_domain;
2720         gboolean is_weak = MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type);
2721         if (MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden)) {
2722                 MonoObject *obj = (MonoObject *)MONO_GC_REVEAL_POINTER (hidden, is_weak);
2723                 obj_domain = mono_object_domain (obj);
2724         } else {
2725                 obj_domain = (MonoDomain *)MONO_GC_REVEAL_POINTER (hidden, is_weak);
2726         }
2727         if (unloading_domain->domain_id == obj_domain->domain_id)
2728                 return NULL;
2729         return hidden;
2730 }
2731
2732 void
2733 sgen_null_links_for_domain (MonoDomain *domain)
2734 {
2735         guint type;
2736         for (type = HANDLE_TYPE_MIN; type < HANDLE_TYPE_MAX; ++type)
2737                 sgen_gchandle_iterate ((GCHandleType)type, GENERATION_OLD, null_link_if_in_domain, domain);
2738 }
2739
2740 void
2741 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
2742 {
2743         sgen_gchandle_set_target (gchandle, obj);
2744 }
2745
2746 void
2747 sgen_client_gchandle_created (int handle_type, GCObject *obj, guint32 handle)
2748 {
2749 #ifndef DISABLE_PERFCOUNTERS
2750         mono_perfcounters->gc_num_handles++;
2751 #endif
2752         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handle_type, handle, obj);
2753 }
2754
2755 void
2756 sgen_client_gchandle_destroyed (int handle_type, guint32 handle)
2757 {
2758 #ifndef DISABLE_PERFCOUNTERS
2759         mono_perfcounters->gc_num_handles--;
2760 #endif
2761         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handle_type, handle, NULL);
2762 }
2763
2764 void
2765 sgen_client_ensure_weak_gchandles_accessible (void)
2766 {
2767         /*
2768          * During the second bridge processing step the world is
2769          * running again.  That step processes all weak links once
2770          * more to null those that refer to dead objects.  Before that
2771          * is completed, those links must not be followed, so we
2772          * conservatively wait for bridge processing when any weak
2773          * link is dereferenced.
2774          */
2775         /* FIXME: A GC can occur after this check fails, in which case we
2776          * should wait for bridge processing but would fail to do so.
2777          */
2778         if (G_UNLIKELY (bridge_processing_in_progress))
2779                 mono_gc_wait_for_bridge_processing ();
2780 }
2781
2782 void*
2783 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
2784 {
2785         void *result;
2786         LOCK_INTERRUPTION;
2787         result = func (data);
2788         UNLOCK_INTERRUPTION;
2789         return result;
2790 }
2791
2792 void
2793 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
2794 {
2795         // FIXME:
2796 }
2797
2798 guint8*
2799 mono_gc_get_card_table (int *shift_bits, gpointer *mask)
2800 {
2801         return sgen_get_card_table_configuration (shift_bits, mask);
2802 }
2803
2804 gboolean
2805 mono_gc_card_table_nursery_check (void)
2806 {
2807         return !sgen_get_major_collector ()->is_concurrent;
2808 }
2809
2810 /* Negative value to remove */
2811 void
2812 mono_gc_add_memory_pressure (gint64 value)
2813 {
2814         /* FIXME: Implement at some point? */
2815 }
2816
2817 /*
2818  * Logging
2819  */
2820
2821 void
2822 sgen_client_degraded_allocation (size_t size)
2823 {
2824         static int last_major_gc_warned = -1;
2825         static int num_degraded = 0;
2826
2827         if (last_major_gc_warned < (int)gc_stats.major_gc_count) {
2828                 ++num_degraded;
2829                 if (num_degraded == 1 || num_degraded == 3)
2830                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Degraded allocation.  Consider increasing nursery-size if the warning persists.");
2831                 else if (num_degraded == 10)
2832                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Repeated degraded allocation.  Consider increasing nursery-size.");
2833                 last_major_gc_warned = gc_stats.major_gc_count;
2834         }
2835 }
2836
2837 /*
2838  * Debugging
2839  */
2840
2841 const char*
2842 sgen_client_description_for_internal_mem_type (int type)
2843 {
2844         switch (type) {
2845         case INTERNAL_MEM_EPHEMERON_LINK: return "ephemeron-link";
2846         case INTERNAL_MEM_MOVED_OBJECT: return "moved-object";
2847         default:
2848                 return NULL;
2849         }
2850 }
2851
2852 void
2853 sgen_client_pre_collection_checks (void)
2854 {
2855         if (sgen_mono_xdomain_checks) {
2856                 sgen_clear_nursery_fragments ();
2857                 sgen_check_for_xdomain_refs ();
2858         }
2859 }
2860
2861 gboolean
2862 sgen_client_vtable_is_inited (MonoVTable *vt)
2863 {
2864         return vt->klass->inited;
2865 }
2866
2867 const char*
2868 sgen_client_vtable_get_namespace (MonoVTable *vt)
2869 {
2870         return vt->klass->name_space;
2871 }
2872
2873 const char*
2874 sgen_client_vtable_get_name (MonoVTable *vt)
2875 {
2876         return vt->klass->name;
2877 }
2878
2879 /*
2880  * Initialization
2881  */
2882
2883 void
2884 sgen_client_init (void)
2885 {
2886         int dummy;
2887         MonoThreadInfoCallbacks cb;
2888
2889         cb.thread_register = sgen_thread_register;
2890         cb.thread_detach = sgen_thread_detach;
2891         cb.thread_unregister = sgen_thread_unregister;
2892         cb.thread_attach = sgen_thread_attach;
2893         cb.mono_thread_in_critical_region = thread_in_critical_region;
2894         cb.ip_in_critical_region = ip_in_critical_region;
2895
2896         mono_threads_init (&cb, sizeof (SgenThreadInfo));
2897
2898         ///* Keep this the default for now */
2899         /* Precise marking is broken on all supported targets. Disable until fixed. */
2900         conservative_stack_mark = TRUE;
2901
2902         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
2903
2904         mono_sgen_init_stw ();
2905
2906 #ifndef HAVE_KW_THREAD
2907         mono_native_tls_alloc (&thread_info_key, NULL);
2908 #if defined(TARGET_OSX) || defined(TARGET_WIN32) || defined(TARGET_ANDROID) || defined(TARGET_IOS)
2909         /* 
2910          * CEE_MONO_TLS requires the tls offset, not the key, so the code below only works on darwin,
2911          * where the two are the same.
2912          */
2913         mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, thread_info_key);
2914 #endif
2915 #else
2916         {
2917                 int tls_offset = -1;
2918                 MONO_THREAD_VAR_OFFSET (sgen_thread_info, tls_offset);
2919                 mono_tls_key_set_offset (TLS_KEY_SGEN_THREAD_INFO, tls_offset);
2920         }
2921 #endif
2922
2923         mono_gc_register_thread (&dummy);
2924 }
2925
2926 gboolean
2927 sgen_client_handle_gc_param (const char *opt)
2928 {
2929         if (g_str_has_prefix (opt, "stack-mark=")) {
2930                 opt = strchr (opt, '=') + 1;
2931                 if (!strcmp (opt, "precise")) {
2932                         conservative_stack_mark = FALSE;
2933                 } else if (!strcmp (opt, "conservative")) {
2934                         conservative_stack_mark = TRUE;
2935                 } else {
2936                         sgen_env_var_error (MONO_GC_PARAMS_NAME, conservative_stack_mark ? "Using `conservative`." : "Using `precise`.",
2937                                         "Invalid value `%s` for `stack-mark` option, possible values are: `precise`, `conservative`.", opt);
2938                 }
2939         } else if (g_str_has_prefix (opt, "bridge-implementation=")) {
2940                 opt = strchr (opt, '=') + 1;
2941                 sgen_set_bridge_implementation (opt);
2942         } else if (g_str_has_prefix (opt, "toggleref-test")) {
2943                 /* FIXME: This should probably in MONO_GC_DEBUG */
2944                 sgen_register_test_toggleref_callback ();
2945         } else if (!sgen_bridge_handle_gc_param (opt)) {
2946                 return FALSE;
2947         }
2948         return TRUE;
2949 }
2950
2951 void
2952 sgen_client_print_gc_params_usage (void)
2953 {
2954         fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
2955 }
2956
2957 gboolean
2958 sgen_client_handle_gc_debug (const char *opt)
2959 {
2960         if (!strcmp (opt, "xdomain-checks")) {
2961                 sgen_mono_xdomain_checks = TRUE;
2962         } else if (!strcmp (opt, "do-not-finalize")) {
2963                 mono_do_not_finalize = TRUE;
2964         } else if (g_str_has_prefix (opt, "do-not-finalize=")) {
2965                 opt = strchr (opt, '=') + 1;
2966                 mono_do_not_finalize = TRUE;
2967                 mono_do_not_finalize_class_names = g_strsplit (opt, ",", 0);
2968         } else if (!strcmp (opt, "log-finalizers")) {
2969                 log_finalizers = TRUE;
2970         } else if (!strcmp (opt, "no-managed-allocator")) {
2971                 sgen_set_use_managed_allocator (FALSE);
2972         } else if (!sgen_bridge_handle_gc_debug (opt)) {
2973                 return FALSE;
2974         }
2975         return TRUE;
2976 }
2977
2978 void
2979 sgen_client_print_gc_debug_usage (void)
2980 {
2981         fprintf (stderr, "  xdomain-checks\n");
2982         fprintf (stderr, "  do-not-finalize\n");
2983         fprintf (stderr, "  log-finalizers\n");
2984         fprintf (stderr, "  no-managed-allocator\n");
2985         sgen_bridge_print_gc_debug_usage ();
2986 }
2987
2988
2989 gpointer
2990 sgen_client_get_provenance (void)
2991 {
2992 #ifdef SGEN_OBJECT_PROVENANCE
2993         MonoGCCallbacks *cb = mono_gc_get_gc_callbacks ();
2994         gpointer (*get_provenance_func) (void);
2995         if (!cb)
2996                 return NULL;
2997         get_provenance_func = cb->get_provenance_func;
2998         if (get_provenance_func)
2999                 return get_provenance_func ();
3000         return NULL;
3001 #else
3002         return NULL;
3003 #endif
3004 }
3005
3006 void
3007 sgen_client_describe_invalid_pointer (GCObject *ptr)
3008 {
3009         sgen_bridge_describe_pointer (ptr);
3010 }
3011
3012 static gboolean gc_inited;
3013
3014 void
3015 mono_gc_base_init (void)
3016 {
3017         if (gc_inited)
3018                 return;
3019
3020         mono_counters_init ();
3021
3022 #ifndef HOST_WIN32
3023         mono_w32handle_init ();
3024 #endif
3025
3026 #ifdef HEAVY_STATISTICS
3027         mono_counters_register ("los marked cards", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_marked_cards);
3028         mono_counters_register ("los array cards scanned ", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_cards);
3029         mono_counters_register ("los array remsets", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_remsets);
3030
3031         mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_arrayref);
3032         mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_value_copy);
3033         mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_object_copy);
3034 #endif
3035
3036         sgen_gc_init ();
3037
3038 #if defined(HAVE_KW_THREAD)
3039         /* This can happen with using libmonosgen.so */
3040         if (mono_tls_key_get_offset (TLS_KEY_SGEN_THREAD_INFO) == -1)
3041                 sgen_set_use_managed_allocator (FALSE);
3042 #endif
3043
3044         gc_inited = TRUE;
3045 }
3046
3047 void
3048 mono_gc_base_cleanup (void)
3049 {
3050         sgen_thread_pool_shutdown ();
3051
3052         // We should have consumed any outstanding moves.
3053         g_assert (sgen_pointer_queue_is_empty (&moved_objects_queue));
3054 }
3055
3056 gboolean
3057 mono_gc_is_null (void)
3058 {
3059         return FALSE;
3060 }
3061
3062 #endif