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