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