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