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