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