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