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