Merge pull request #4014 from BrzVlad/feature-tls-refactor
[mono.git] / mono / metadata / sgen-mono.c
1 /*
2  * sgen-mono.c: SGen features specific to Mono.
3  *
4  * Copyright (C) 2014 Xamarin Inc
5  *
6  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
7  */
8
9 #include "config.h"
10 #ifdef HAVE_SGEN_GC
11
12 #include "sgen/sgen-gc.h"
13 #include "sgen/sgen-protocol.h"
14 #include "metadata/monitor.h"
15 #include "sgen/sgen-layout-stats.h"
16 #include "sgen/sgen-client.h"
17 #include "sgen/sgen-cardtable.h"
18 #include "sgen/sgen-pinning.h"
19 #include "sgen/sgen-thread-pool.h"
20 #include "metadata/marshal.h"
21 #include "metadata/method-builder.h"
22 #include "metadata/abi-details.h"
23 #include "metadata/mono-gc.h"
24 #include "metadata/runtime.h"
25 #include "metadata/sgen-bridge-internals.h"
26 #include "metadata/gc-internals.h"
27 #include "metadata/handle.h"
28 #include "utils/mono-memory-model.h"
29 #include "utils/mono-logger-internals.h"
30 #include "utils/mono-threads-coop.h"
31 #include "sgen/sgen-thread-pool.h"
32 #include "utils/mono-threads.h"
33 #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 slowpath_branch, max_size_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         slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1323
1324         /* Slowpath */
1325         if (atype != ATYPE_SMALL)
1326                 mono_mb_patch_short_branch (mb, max_size_branch);
1327
1328         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1329         mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
1330         /*
1331          * We are no longer in a critical section. We need to do this before calling
1332          * to unmanaged land in order to avoid stw deadlocks since unmanaged code
1333          * might take locks.
1334          */
1335 #ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
1336         EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
1337         mono_mb_emit_byte (mb, CEE_LDC_I4_0);
1338         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1339         mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
1340         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_NONE);
1341 #endif
1342
1343         /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
1344         mono_mb_emit_ldarg (mb, 0);
1345         mono_mb_emit_ldloc (mb, 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         mono_mb_emit_byte (mb, CEE_RET);
1358
1359         /* Fastpath */
1360         mono_mb_patch_short_branch (mb, slowpath_branch);
1361
1362         /* FIXME: Memory barrier */
1363
1364         /* tlab_next = new_next */
1365         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
1366         mono_mb_emit_ldloc (mb, new_next_var);
1367         mono_mb_emit_byte (mb, CEE_STIND_I);
1368
1369         /* *p = vtable; */
1370         mono_mb_emit_ldloc (mb, p_var);
1371         mono_mb_emit_ldarg (mb, 0);
1372         mono_mb_emit_byte (mb, CEE_STIND_I);
1373
1374         /* mark object end with nursery word */
1375         if (nursery_canaries_enabled ()) {
1376                         mono_mb_emit_ldloc (mb, p_var);
1377                         mono_mb_emit_ldloc (mb, real_size_var);
1378                         mono_mb_emit_byte (mb, MONO_CEE_ADD);
1379                         mono_mb_emit_icon8 (mb, (mword) CANARY_STRING);
1380                         mono_mb_emit_icon (mb, CANARY_SIZE);
1381                         mono_mb_emit_byte (mb, MONO_CEE_PREFIX1);
1382                         mono_mb_emit_byte (mb, CEE_CPBLK);
1383         }
1384
1385         if (atype == ATYPE_VECTOR) {
1386                 /* arr->max_length = max_length; */
1387                 mono_mb_emit_ldloc (mb, p_var);
1388                 mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, max_length));
1389                 mono_mb_emit_ldarg (mb, 1);
1390 #ifdef MONO_BIG_ARRAYS
1391                 mono_mb_emit_byte (mb, CEE_STIND_I);
1392 #else
1393                 mono_mb_emit_byte (mb, CEE_STIND_I4);
1394 #endif
1395         } else  if (atype == ATYPE_STRING) {
1396                 /* need to set length and clear the last char */
1397                 /* s->length = len; */
1398                 mono_mb_emit_ldloc (mb, p_var);
1399                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, length));
1400                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
1401                 mono_mb_emit_ldarg (mb, 1);
1402                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1403         }
1404
1405 #ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
1406         EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
1407         mono_mb_emit_byte (mb, CEE_LDC_I4_0);
1408         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1409         mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
1410 #else
1411         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1412         mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
1413 #endif
1414         /*
1415         We must make sure both vtable and max_length are globaly visible before returning to managed land.
1416         */
1417         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL);
1418
1419         /* return p */
1420         mono_mb_emit_ldloc (mb, p_var);
1421
1422  done:
1423         mono_mb_emit_byte (mb, CEE_RET);
1424 #endif
1425
1426         info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1427         info->d.alloc.gc_name = "sgen";
1428         info->d.alloc.alloc_type = atype;
1429
1430 #ifndef DISABLE_JIT
1431         mb->init_locals = FALSE;
1432 #endif
1433
1434         res = mono_mb_create (mb, csig, 8, info);
1435         mono_mb_free (mb);
1436
1437
1438         return res;
1439 }
1440 #endif
1441
1442 int
1443 mono_gc_get_aligned_size_for_allocator (int size)
1444 {
1445         return SGEN_ALIGN_UP (size);
1446 }
1447
1448 /*
1449  * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
1450  * The signature of the called method is:
1451  *      object allocate (MonoVTable *vtable)
1452  */
1453 MonoMethod*
1454 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1455 {
1456 #ifdef MANAGED_ALLOCATION
1457         if (collect_before_allocs)
1458                 return NULL;
1459         if (klass->instance_size > tlab_size)
1460                 return NULL;
1461         if (known_instance_size && ALIGN_TO (klass->instance_size, SGEN_ALLOC_ALIGN) >= SGEN_MAX_SMALL_OBJ_SIZE)
1462                 return NULL;
1463         if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1464                 return NULL;
1465         if (klass->rank)
1466                 return NULL;
1467         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
1468                 return NULL;
1469         if (klass->byval_arg.type == MONO_TYPE_STRING)
1470                 return mono_gc_get_managed_allocator_by_type (ATYPE_STRING, MANAGED_ALLOCATOR_REGULAR);
1471         /* Generic classes have dynamic field and can go above MAX_SMALL_OBJ_SIZE. */
1472         if (known_instance_size)
1473                 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL, MANAGED_ALLOCATOR_REGULAR);
1474         else
1475                 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL, MANAGED_ALLOCATOR_REGULAR);
1476 #else
1477         return NULL;
1478 #endif
1479 }
1480
1481 MonoMethod*
1482 mono_gc_get_managed_array_allocator (MonoClass *klass)
1483 {
1484 #ifdef MANAGED_ALLOCATION
1485         if (klass->rank != 1)
1486                 return NULL;
1487         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
1488                 return NULL;
1489         if (has_per_allocation_action)
1490                 return NULL;
1491         g_assert (!mono_class_has_finalizer (klass) && !mono_class_is_marshalbyref (klass));
1492
1493         return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR, MANAGED_ALLOCATOR_REGULAR);
1494 #else
1495         return NULL;
1496 #endif
1497 }
1498
1499 void
1500 sgen_set_use_managed_allocator (gboolean flag)
1501 {
1502         use_managed_allocator = flag;
1503 }
1504
1505 MonoMethod*
1506 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1507 {
1508 #ifdef MANAGED_ALLOCATION
1509         MonoMethod *res;
1510         MonoMethod **cache;
1511
1512         if (variant == MANAGED_ALLOCATOR_REGULAR && !use_managed_allocator)
1513                 return NULL;
1514
1515         switch (variant) {
1516         case MANAGED_ALLOCATOR_REGULAR: cache = alloc_method_cache; break;
1517         case MANAGED_ALLOCATOR_SLOW_PATH: cache = slowpath_alloc_method_cache; break;
1518         default: g_assert_not_reached (); break;
1519         }
1520
1521         res = cache [atype];
1522         if (res)
1523                 return res;
1524
1525         res = create_allocator (atype, variant);
1526         LOCK_GC;
1527         if (cache [atype]) {
1528                 mono_free_method (res);
1529                 res = cache [atype];
1530         } else {
1531                 mono_memory_barrier ();
1532                 cache [atype] = res;
1533         }
1534         UNLOCK_GC;
1535
1536         return res;
1537 #else
1538         return NULL;
1539 #endif
1540 }
1541
1542 guint32
1543 mono_gc_get_managed_allocator_types (void)
1544 {
1545         return ATYPE_NUM;
1546 }
1547
1548 gboolean
1549 sgen_is_managed_allocator (MonoMethod *method)
1550 {
1551         int i;
1552
1553         for (i = 0; i < ATYPE_NUM; ++i)
1554                 if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1555                         return TRUE;
1556         return FALSE;
1557 }
1558
1559 gboolean
1560 sgen_has_managed_allocator (void)
1561 {
1562         int i;
1563
1564         for (i = 0; i < ATYPE_NUM; ++i)
1565                 if (alloc_method_cache [i] || slowpath_alloc_method_cache [i])
1566                         return TRUE;
1567         return FALSE;
1568 }
1569
1570 /*
1571  * Cardtable scanning
1572  */
1573
1574 #define MWORD_MASK (sizeof (mword) - 1)
1575
1576 static inline int
1577 find_card_offset (mword card)
1578 {
1579 /*XXX Use assembly as this generates some pretty bad code */
1580 #if defined(__i386__) && defined(__GNUC__)
1581         return  (__builtin_ffs (card) - 1) / 8;
1582 #elif defined(__x86_64__) && defined(__GNUC__)
1583         return (__builtin_ffsll (card) - 1) / 8;
1584 #elif defined(__s390x__)
1585         return (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
1586 #else
1587         int i;
1588         guint8 *ptr = (guint8 *) &card;
1589         for (i = 0; i < sizeof (mword); ++i) {
1590                 if (ptr[i])
1591                         return i;
1592         }
1593         return 0;
1594 #endif
1595 }
1596
1597 static guint8*
1598 find_next_card (guint8 *card_data, guint8 *end)
1599 {
1600         mword *cards, *cards_end;
1601         mword card;
1602
1603         while ((((mword)card_data) & MWORD_MASK) && card_data < end) {
1604                 if (*card_data)
1605                         return card_data;
1606                 ++card_data;
1607         }
1608
1609         if (card_data == end)
1610                 return end;
1611
1612         cards = (mword*)card_data;
1613         cards_end = (mword*)((mword)end & ~MWORD_MASK);
1614         while (cards < cards_end) {
1615                 card = *cards;
1616                 if (card)
1617                         return (guint8*)cards + find_card_offset (card);
1618                 ++cards;
1619         }
1620
1621         card_data = (guint8*)cards_end;
1622         while (card_data < end) {
1623                 if (*card_data)
1624                         return card_data;
1625                 ++card_data;
1626         }
1627
1628         return end;
1629 }
1630
1631 #define ARRAY_OBJ_INDEX(ptr,array,elem_size) (((char*)(ptr) - ((char*)(array) + G_STRUCT_OFFSET (MonoArray, vector))) / (elem_size))
1632
1633 gboolean
1634 sgen_client_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, ScanCopyContext ctx)
1635 {
1636         MonoVTable *vt = SGEN_LOAD_VTABLE (obj);
1637         MonoClass *klass = vt->klass;
1638
1639         SGEN_ASSERT (0, SGEN_VTABLE_HAS_REFERENCES (vt), "Why would we ever call this on reference-free objects?");
1640
1641         if (vt->rank) {
1642                 MonoArray *arr = (MonoArray*)obj;
1643                 guint8 *card_data, *card_base;
1644                 guint8 *card_data_end;
1645                 char *obj_start = (char *)sgen_card_table_align_pointer (obj);
1646                 mword bounds_size;
1647                 mword obj_size = sgen_mono_array_size (vt, arr, &bounds_size, sgen_vtable_get_descriptor (vt));
1648                 /* We don't want to scan the bounds entries at the end of multidimensional arrays */
1649                 char *obj_end = (char*)obj + obj_size - bounds_size;
1650                 size_t card_count;
1651                 size_t extra_idx = 0;
1652
1653                 mword desc = (mword)klass->element_class->gc_descr;
1654                 int elem_size = mono_array_element_size (klass);
1655
1656 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1657                 guint8 *overflow_scan_end = NULL;
1658 #endif
1659
1660 #ifdef SGEN_OBJECT_LAYOUT_STATISTICS
1661                 if (klass->element_class->valuetype)
1662                         sgen_object_layout_scanned_vtype_array ();
1663                 else
1664                         sgen_object_layout_scanned_ref_array ();
1665 #endif
1666
1667                 if (cards)
1668                         card_data = cards;
1669                 else
1670                         card_data = sgen_card_table_get_card_scan_address ((mword)obj);
1671
1672                 card_base = card_data;
1673                 card_count = sgen_card_table_number_of_cards_in_range ((mword)obj, obj_size);
1674                 card_data_end = card_data + card_count;
1675
1676
1677 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1678                 /*Check for overflow and if so, setup to scan in two steps*/
1679                 if (!cards && card_data_end >= SGEN_SHADOW_CARDTABLE_END) {
1680                         overflow_scan_end = sgen_shadow_cardtable + (card_data_end - SGEN_SHADOW_CARDTABLE_END);
1681                         card_data_end = SGEN_SHADOW_CARDTABLE_END;
1682                 }
1683
1684 LOOP_HEAD:
1685 #endif
1686
1687                 card_data = find_next_card (card_data, card_data_end);
1688                 for (; card_data < card_data_end; card_data = find_next_card (card_data + 1, card_data_end)) {
1689                         size_t index;
1690                         size_t idx = (card_data - card_base) + extra_idx;
1691                         char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES);
1692                         char *card_end = start + CARD_SIZE_IN_BYTES;
1693                         char *first_elem, *elem;
1694
1695                         HEAVY_STAT (++los_marked_cards);
1696
1697                         if (!cards)
1698                                 sgen_card_table_prepare_card_for_scanning (card_data);
1699
1700                         card_end = MIN (card_end, obj_end);
1701
1702                         if (start <= (char*)arr->vector)
1703                                 index = 0;
1704                         else
1705                                 index = ARRAY_OBJ_INDEX (start, obj, elem_size);
1706
1707                         elem = first_elem = (char*)mono_array_addr_with_size_fast ((MonoArray*)obj, elem_size, index);
1708                         if (klass->element_class->valuetype) {
1709                                 ScanVTypeFunc scan_vtype_func = ctx.ops->scan_vtype;
1710
1711                                 for (; elem < card_end; elem += elem_size)
1712                                         scan_vtype_func (obj, elem, desc, ctx.queue BINARY_PROTOCOL_ARG (elem_size));
1713                         } else {
1714                                 ScanPtrFieldFunc scan_ptr_field_func = ctx.ops->scan_ptr_field;
1715
1716                                 HEAVY_STAT (++los_array_cards);
1717                                 for (; elem < card_end; elem += SIZEOF_VOID_P)
1718                                         scan_ptr_field_func (obj, (GCObject**)elem, ctx.queue);
1719                         }
1720
1721                         binary_protocol_card_scan (first_elem, elem - first_elem);
1722                 }
1723
1724 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1725                 if (overflow_scan_end) {
1726                         extra_idx = card_data - card_base;
1727                         card_base = card_data = sgen_shadow_cardtable;
1728                         card_data_end = overflow_scan_end;
1729                         overflow_scan_end = NULL;
1730                         goto LOOP_HEAD;
1731                 }
1732 #endif
1733                 return TRUE;
1734         }
1735
1736         return FALSE;
1737 }
1738
1739 /*
1740  * Array and string allocation
1741  */
1742
1743 void*
1744 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
1745 {
1746         MonoArray *arr;
1747         TLAB_ACCESS_INIT;
1748
1749         if (!SGEN_CAN_ALIGN_UP (size))
1750                 return NULL;
1751
1752 #ifndef DISABLE_CRITICAL_REGION
1753         ENTER_CRITICAL_REGION;
1754         arr = (MonoArray*)sgen_try_alloc_obj_nolock (vtable, size);
1755         if (arr) {
1756                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1757                 arr->max_length = (mono_array_size_t)max_length;
1758                 EXIT_CRITICAL_REGION;
1759                 goto done;
1760         }
1761         EXIT_CRITICAL_REGION;
1762 #endif
1763
1764         LOCK_GC;
1765
1766         arr = (MonoArray*)sgen_alloc_obj_nolock (vtable, size);
1767         if (G_UNLIKELY (!arr)) {
1768                 UNLOCK_GC;
1769                 return NULL;
1770         }
1771
1772         arr->max_length = (mono_array_size_t)max_length;
1773
1774         UNLOCK_GC;
1775
1776  done:
1777         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
1778                 mono_profiler_allocation (&arr->obj);
1779
1780         SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Vector has incorrect size.");
1781         return arr;
1782 }
1783
1784 void*
1785 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
1786 {
1787         MonoArray *arr;
1788         MonoArrayBounds *bounds;
1789         TLAB_ACCESS_INIT;
1790
1791         if (!SGEN_CAN_ALIGN_UP (size))
1792                 return NULL;
1793
1794 #ifndef DISABLE_CRITICAL_REGION
1795         ENTER_CRITICAL_REGION;
1796         arr = (MonoArray*)sgen_try_alloc_obj_nolock (vtable, size);
1797         if (arr) {
1798                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1799                 arr->max_length = (mono_array_size_t)max_length;
1800
1801                 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
1802                 arr->bounds = bounds;
1803                 EXIT_CRITICAL_REGION;
1804                 goto done;
1805         }
1806         EXIT_CRITICAL_REGION;
1807 #endif
1808
1809         LOCK_GC;
1810
1811         arr = (MonoArray*)sgen_alloc_obj_nolock (vtable, size);
1812         if (G_UNLIKELY (!arr)) {
1813                 UNLOCK_GC;
1814                 return NULL;
1815         }
1816
1817         arr->max_length = (mono_array_size_t)max_length;
1818
1819         bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
1820         arr->bounds = bounds;
1821
1822         UNLOCK_GC;
1823
1824  done:
1825         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
1826                 mono_profiler_allocation (&arr->obj);
1827
1828         SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size (vtable, (GCObject*)arr)), "Array has incorrect size.");
1829         return arr;
1830 }
1831
1832 void*
1833 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
1834 {
1835         MonoString *str;
1836         TLAB_ACCESS_INIT;
1837
1838         if (!SGEN_CAN_ALIGN_UP (size))
1839                 return NULL;
1840
1841 #ifndef DISABLE_CRITICAL_REGION
1842         ENTER_CRITICAL_REGION;
1843         str = (MonoString*)sgen_try_alloc_obj_nolock (vtable, size);
1844         if (str) {
1845                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1846                 str->length = len;
1847                 EXIT_CRITICAL_REGION;
1848                 goto done;
1849         }
1850         EXIT_CRITICAL_REGION;
1851 #endif
1852
1853         LOCK_GC;
1854
1855         str = (MonoString*)sgen_alloc_obj_nolock (vtable, size);
1856         if (G_UNLIKELY (!str)) {
1857                 UNLOCK_GC;
1858                 return NULL;
1859         }
1860
1861         str->length = len;
1862
1863         UNLOCK_GC;
1864
1865  done:
1866         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_ALLOCATIONS))
1867                 mono_profiler_allocation (&str->object);
1868
1869         return str;
1870 }
1871
1872 /*
1873  * Strings
1874  */
1875
1876 void
1877 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1878 {
1879         mono_unichar2 *new_end = str->chars + new_length;
1880
1881         /* zero the discarded string. This null-delimits the string and allows
1882          * the space to be reclaimed by SGen. */
1883
1884         if (nursery_canaries_enabled () && sgen_ptr_in_nursery (str)) {
1885                 CHECK_CANARY_FOR_OBJECT ((GCObject*)str, TRUE);
1886                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2) + CANARY_SIZE);
1887                 memcpy (new_end + 1 , CANARY_STRING, CANARY_SIZE);
1888         } else {
1889                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1890         }
1891
1892         str->length = new_length;
1893 }
1894
1895 /*
1896  * Profiling
1897  */
1898
1899 #define GC_ROOT_NUM 32
1900 typedef struct {
1901         int count;              /* must be the first field */
1902         void *objects [GC_ROOT_NUM];
1903         int root_types [GC_ROOT_NUM];
1904         uintptr_t extra_info [GC_ROOT_NUM];
1905 } GCRootReport;
1906
1907 static void
1908 notify_gc_roots (GCRootReport *report)
1909 {
1910         if (!report->count)
1911                 return;
1912         mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
1913         report->count = 0;
1914 }
1915
1916 static void
1917 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
1918 {
1919         if (report->count == GC_ROOT_NUM)
1920                 notify_gc_roots (report);
1921         report->objects [report->count] = object;
1922         report->root_types [report->count] = rtype;
1923         report->extra_info [report->count++] = (uintptr_t)SGEN_LOAD_VTABLE (object)->klass;
1924 }
1925
1926 void
1927 sgen_client_nursery_objects_pinned (void **definitely_pinned, int count)
1928 {
1929         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1930                 GCRootReport report;
1931                 int idx;
1932                 report.count = 0;
1933                 for (idx = 0; idx < count; ++idx)
1934                         add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
1935                 notify_gc_roots (&report);
1936         }
1937 }
1938
1939 static void
1940 report_finalizer_roots_from_queue (SgenPointerQueue *queue)
1941 {
1942         GCRootReport report;
1943         size_t i;
1944
1945         report.count = 0;
1946         for (i = 0; i < queue->next_slot; ++i) {
1947                 void *obj = queue->data [i];
1948                 if (!obj)
1949                         continue;
1950                 add_profile_gc_root (&report, obj, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
1951         }
1952         notify_gc_roots (&report);
1953 }
1954
1955 static void
1956 report_finalizer_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
1957 {
1958         report_finalizer_roots_from_queue (fin_ready_queue);
1959         report_finalizer_roots_from_queue (critical_fin_queue);
1960 }
1961
1962 static GCRootReport *root_report;
1963
1964 static void
1965 single_arg_report_root (MonoObject **obj, void *gc_data)
1966 {
1967         if (*obj)
1968                 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
1969 }
1970
1971 static void
1972 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
1973 {
1974         switch (desc & ROOT_DESC_TYPE_MASK) {
1975         case ROOT_DESC_BITMAP:
1976                 desc >>= ROOT_DESC_TYPE_SHIFT;
1977                 while (desc) {
1978                         if ((desc & 1) && *start_root) {
1979                                 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
1980                         }
1981                         desc >>= 1;
1982                         start_root++;
1983                 }
1984                 return;
1985         case ROOT_DESC_COMPLEX: {
1986                 gsize *bitmap_data = (gsize *)sgen_get_complex_descriptor_bitmap (desc);
1987                 gsize bwords = (*bitmap_data) - 1;
1988                 void **start_run = start_root;
1989                 bitmap_data++;
1990                 while (bwords-- > 0) {
1991                         gsize bmap = *bitmap_data++;
1992                         void **objptr = start_run;
1993                         while (bmap) {
1994                                 if ((bmap & 1) && *objptr) {
1995                                         add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
1996                                 }
1997                                 bmap >>= 1;
1998                                 ++objptr;
1999                         }
2000                         start_run += GC_BITS_PER_WORD;
2001                 }
2002                 break;
2003         }
2004         case ROOT_DESC_USER: {
2005                 MonoGCRootMarkFunc marker = (MonoGCRootMarkFunc)sgen_get_user_descriptor_func (desc);
2006                 root_report = report;
2007                 marker ((MonoObject**)start_root, single_arg_report_root, NULL);
2008                 break;
2009         }
2010         case ROOT_DESC_RUN_LEN:
2011                 g_assert_not_reached ();
2012         default:
2013                 g_assert_not_reached ();
2014         }
2015 }
2016
2017 static void
2018 report_registered_roots_by_type (int root_type)
2019 {
2020         GCRootReport report;
2021         void **start_root;
2022         RootRecord *root;
2023         report.count = 0;
2024         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], void **, start_root, RootRecord *, root) {
2025                 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
2026                 precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc);
2027         } SGEN_HASH_TABLE_FOREACH_END;
2028         notify_gc_roots (&report);
2029 }
2030
2031 static void
2032 report_registered_roots (void)
2033 {
2034         report_registered_roots_by_type (ROOT_TYPE_NORMAL);
2035         report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
2036 }
2037
2038 void
2039 sgen_client_collecting_minor (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
2040 {
2041         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2042                 report_registered_roots ();
2043         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2044                 report_finalizer_roots (fin_ready_queue, critical_fin_queue);
2045 }
2046
2047 static GCRootReport major_root_report;
2048 static gboolean profile_roots;
2049
2050 void
2051 sgen_client_collecting_major_1 (void)
2052 {
2053         profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
2054         memset (&major_root_report, 0, sizeof (GCRootReport));
2055 }
2056
2057 void
2058 sgen_client_pinned_los_object (GCObject *obj)
2059 {
2060         if (profile_roots)
2061                 add_profile_gc_root (&major_root_report, (char*)obj, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
2062 }
2063
2064 void
2065 sgen_client_collecting_major_2 (void)
2066 {
2067         if (profile_roots)
2068                 notify_gc_roots (&major_root_report);
2069
2070         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2071                 report_registered_roots ();
2072 }
2073
2074 void
2075 sgen_client_collecting_major_3 (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
2076 {
2077         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
2078                 report_finalizer_roots (fin_ready_queue, critical_fin_queue);
2079 }
2080
2081 #define MOVED_OBJECTS_NUM 64
2082 static void *moved_objects [MOVED_OBJECTS_NUM];
2083 static int moved_objects_idx = 0;
2084
2085 static SgenPointerQueue moved_objects_queue = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_MOVED_OBJECT);
2086
2087 void
2088 mono_sgen_register_moved_object (void *obj, void *destination)
2089 {
2090         /*
2091          * This function can be called from SGen's worker threads. We want to try
2092          * and avoid exposing those threads to the profiler API, so queue up move
2093          * events and send them later when the main GC thread calls
2094          * mono_sgen_gc_event_moves ().
2095          *
2096          * TODO: Once SGen has multiple worker threads, we need to switch to a
2097          * lock-free data structure for the queue as multiple threads will be
2098          * adding to it at the same time.
2099          */
2100         if (sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ())) {
2101                 sgen_pointer_queue_add (&moved_objects_queue, obj);
2102                 sgen_pointer_queue_add (&moved_objects_queue, destination);
2103         } else {
2104                 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2105                         mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2106                         moved_objects_idx = 0;
2107                 }
2108
2109                 moved_objects [moved_objects_idx++] = obj;
2110                 moved_objects [moved_objects_idx++] = destination;
2111         }
2112 }
2113
2114 void
2115 mono_sgen_gc_event_moves (void)
2116 {
2117         while (!sgen_pointer_queue_is_empty (&moved_objects_queue)) {
2118                 void *dst = sgen_pointer_queue_pop (&moved_objects_queue);
2119                 void *src = sgen_pointer_queue_pop (&moved_objects_queue);
2120
2121                 mono_sgen_register_moved_object (src, dst);
2122         }
2123
2124         if (moved_objects_idx) {
2125                 mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2126                 moved_objects_idx = 0;
2127         }
2128 }
2129
2130 /*
2131  * Heap walking
2132  */
2133
2134 #define REFS_SIZE 128
2135 typedef struct {
2136         void *data;
2137         MonoGCReferences callback;
2138         int flags;
2139         int count;
2140         int called;
2141         MonoObject *refs [REFS_SIZE];
2142         uintptr_t offsets [REFS_SIZE];
2143 } HeapWalkInfo;
2144
2145 #undef HANDLE_PTR
2146 #define HANDLE_PTR(ptr,obj)     do {    \
2147                 if (*(ptr)) {   \
2148                         if (hwi->count == REFS_SIZE) {  \
2149                                 hwi->callback ((MonoObject*)start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);    \
2150                                 hwi->count = 0; \
2151                                 hwi->called = 1;        \
2152                         }       \
2153                         hwi->offsets [hwi->count] = (char*)(ptr)-(char*)start;  \
2154                         hwi->refs [hwi->count++] = *(ptr);      \
2155                 }       \
2156         } while (0)
2157
2158 static void
2159 collect_references (HeapWalkInfo *hwi, GCObject *obj, size_t size)
2160 {
2161         char *start = (char*)obj;
2162         mword desc = sgen_obj_get_descriptor (obj);
2163
2164 #include "sgen/sgen-scan-object.h"
2165 }
2166
2167 static void
2168 walk_references (GCObject *start, size_t size, void *data)
2169 {
2170         HeapWalkInfo *hwi = (HeapWalkInfo *)data;
2171         hwi->called = 0;
2172         hwi->count = 0;
2173         collect_references (hwi, start, size);
2174         if (hwi->count || !hwi->called)
2175                 hwi->callback (start, mono_object_class (start), hwi->called? 0: size, hwi->count, hwi->refs, hwi->offsets, hwi->data);
2176 }
2177
2178 /**
2179  * mono_gc_walk_heap:
2180  * @flags: flags for future use
2181  * @callback: a function pointer called for each object in the heap
2182  * @data: a user data pointer that is passed to callback
2183  *
2184  * This function can be used to iterate over all the live objects in the heap:
2185  * for each object, @callback is invoked, providing info about the object's
2186  * location in memory, its class, its size and the objects it references.
2187  * For each referenced object it's offset from the object address is
2188  * reported in the offsets array.
2189  * The object references may be buffered, so the callback may be invoked
2190  * multiple times for the same object: in all but the first call, the size
2191  * argument will be zero.
2192  * Note that this function can be only called in the #MONO_GC_EVENT_PRE_START_WORLD
2193  * profiler event handler.
2194  *
2195  * Returns: a non-zero value if the GC doesn't support heap walking
2196  */
2197 int
2198 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
2199 {
2200         HeapWalkInfo hwi;
2201
2202         hwi.flags = flags;
2203         hwi.callback = callback;
2204         hwi.data = data;
2205
2206         sgen_clear_nursery_fragments ();
2207         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data, walk_references, &hwi, FALSE, TRUE);
2208
2209         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, walk_references, &hwi);
2210         sgen_los_iterate_objects (walk_references, &hwi);
2211
2212         return 0;
2213 }
2214
2215 /*
2216  * Threads
2217  */
2218
2219 void
2220 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
2221 {
2222         gc_callbacks = *callbacks;
2223 }
2224
2225 MonoGCCallbacks *
2226 mono_gc_get_gc_callbacks ()
2227 {
2228         return &gc_callbacks;
2229 }
2230
2231 void
2232 sgen_client_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback)
2233 {
2234         size_t stsize = 0;
2235         guint8 *staddr = NULL;
2236
2237         mono_tls_set_sgen_thread_info (info);
2238
2239         info->client_info.skip = 0;
2240
2241         info->client_info.stack_start = NULL;
2242
2243 #ifdef SGEN_POSIX_STW
2244         info->client_info.stop_count = -1;
2245         info->client_info.signal = 0;
2246 #endif
2247
2248         mono_thread_info_get_stack_bounds (&staddr, &stsize);
2249         if (staddr) {
2250                 info->client_info.stack_start_limit = staddr;
2251                 info->client_info.stack_end = staddr + stsize;
2252         } else {
2253                 gsize stack_bottom = (gsize)stack_bottom_fallback;
2254                 stack_bottom += 4095;
2255                 stack_bottom &= ~4095;
2256                 info->client_info.stack_end = (char*)stack_bottom;
2257         }
2258
2259         memset (&info->client_info.ctx, 0, sizeof (MonoContext));
2260
2261         if (mono_gc_get_gc_callbacks ()->thread_attach_func)
2262                 info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
2263
2264         binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
2265
2266         SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->client_info.stack_end);
2267
2268         info->client_info.info.handle_stack = mono_handle_stack_alloc ();
2269 }
2270
2271 void
2272 sgen_client_thread_unregister (SgenThreadInfo *p)
2273 {
2274         MonoNativeThreadId tid;
2275
2276         mono_tls_set_sgen_thread_info (NULL);
2277
2278         tid = mono_thread_info_get_tid (p);
2279
2280         if (p->client_info.info.runtime_thread)
2281                 mono_threads_add_joinable_thread ((gpointer)tid);
2282
2283         if (mono_gc_get_gc_callbacks ()->thread_detach_func) {
2284                 mono_gc_get_gc_callbacks ()->thread_detach_func (p->client_info.runtime_data);
2285                 p->client_info.runtime_data = NULL;
2286         }
2287
2288         binary_protocol_thread_unregister ((gpointer)tid);
2289         SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid);
2290
2291         HandleStack *handles = (HandleStack*) p->client_info.info.handle_stack;
2292         p->client_info.info.handle_stack = NULL;
2293         mono_handle_stack_free (handles);
2294 }
2295
2296 void
2297 mono_gc_set_skip_thread (gboolean skip)
2298 {
2299         SgenThreadInfo *info = mono_thread_info_current ();
2300
2301         LOCK_GC;
2302         info->client_info.gc_disabled = skip;
2303         UNLOCK_GC;
2304 }
2305
2306 static gboolean
2307 thread_in_critical_region (SgenThreadInfo *info)
2308 {
2309         return info->client_info.in_critical_region;
2310 }
2311
2312 static void
2313 sgen_thread_attach (SgenThreadInfo *info)
2314 {
2315         if (mono_gc_get_gc_callbacks ()->thread_attach_func && !info->client_info.runtime_data)
2316                 info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
2317 }
2318
2319 static void
2320 sgen_thread_detach (SgenThreadInfo *p)
2321 {
2322         /* If a delegate is passed to native code and invoked on a thread we dont
2323          * know about, marshal will register it with mono_threads_attach_coop, but
2324          * we have no way of knowing when that thread goes away.  SGen has a TSD
2325          * so we assume that if the domain is still registered, we can detach
2326          * the thread
2327          */
2328         if (mono_thread_internal_current_is_attached ())
2329                 mono_thread_detach_internal (mono_thread_internal_current ());
2330 }
2331
2332 gboolean
2333 mono_gc_register_thread (void *baseptr)
2334 {
2335         return mono_thread_info_attach (baseptr) != NULL;
2336 }
2337
2338 gboolean
2339 mono_gc_is_gc_thread (void)
2340 {
2341         gboolean result;
2342         LOCK_GC;
2343         result = mono_thread_info_current () != NULL;
2344         UNLOCK_GC;
2345         return result;
2346 }
2347
2348 void
2349 sgen_client_thread_register_worker (void)
2350 {
2351         mono_thread_info_register_small_id ();
2352         mono_native_thread_set_name (mono_native_thread_id_get (), "SGen worker");
2353 }
2354
2355 /* Variables holding start/end nursery so it won't have to be passed at every call */
2356 static void *scan_area_arg_start, *scan_area_arg_end;
2357
2358 void
2359 mono_gc_conservatively_scan_area (void *start, void *end)
2360 {
2361         sgen_conservatively_pin_objects_from ((void **)start, (void **)end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
2362 }
2363
2364 void*
2365 mono_gc_scan_object (void *obj, void *gc_data)
2366 {
2367         ScanCopyContext *ctx = (ScanCopyContext *)gc_data;
2368         ctx->ops->copy_or_mark_object ((GCObject**)&obj, ctx->queue);
2369         return obj;
2370 }
2371
2372 /*
2373  * Mark from thread stacks and registers.
2374  */
2375 void
2376 sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise, ScanCopyContext ctx)
2377 {
2378         scan_area_arg_start = start_nursery;
2379         scan_area_arg_end = end_nursery;
2380
2381         FOREACH_THREAD (info) {
2382                 int skip_reason = 0;
2383                 void *aligned_stack_start;
2384
2385                 if (info->client_info.skip) {
2386                         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);
2387                         skip_reason = 1;
2388                 } else if (info->client_info.gc_disabled) {
2389                         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);
2390                         skip_reason = 2;
2391                 } else if (!mono_thread_info_is_live (info)) {
2392                         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);
2393                         skip_reason = 3;
2394                 } else if (!info->client_info.stack_start) {
2395                         SGEN_LOG (3, "Skipping starting or detaching thread %p", info);
2396                         skip_reason = 4;
2397                 }
2398
2399                 binary_protocol_scan_stack ((gpointer)mono_thread_info_get_tid (info), info->client_info.stack_start, info->client_info.stack_end, skip_reason);
2400
2401                 if (skip_reason)
2402                         continue;
2403
2404                 g_assert (info->client_info.stack_start);
2405                 g_assert (info->client_info.stack_end);
2406
2407                 aligned_stack_start = (void*)(mword) ALIGN_TO ((mword)info->client_info.stack_start, SIZEOF_VOID_P);
2408 #ifdef HOST_WIN32
2409                 /* Windows uses a guard page before the committed stack memory pages to detect when the
2410                    stack needs to be grown. If we suspend a thread just after a function prolog has
2411                    decremented the stack pointer to point into the guard page but before the thread has
2412                    been able to read or write to that page, starting the stack scan at aligned_stack_start
2413                    will raise a STATUS_GUARD_PAGE_VIOLATION and the process will crash. This code uses
2414                    VirtualQuery() to determine whether stack_start points into the guard page and then
2415                    updates aligned_stack_start to point at the next non-guard page. */
2416                 MEMORY_BASIC_INFORMATION mem_info;
2417                 SIZE_T result = VirtualQuery(info->client_info.stack_start, &mem_info, sizeof(mem_info));
2418                 g_assert (result != 0);
2419                 if (mem_info.Protect & PAGE_GUARD) {
2420                         aligned_stack_start = ((char*) mem_info.BaseAddress) + mem_info.RegionSize;
2421                 }
2422 #endif
2423
2424                 g_assert (info->client_info.suspend_done);
2425                 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 ());
2426                 if (mono_gc_get_gc_callbacks ()->thread_mark_func && !conservative_stack_mark) {
2427                         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);
2428                 } else if (!precise) {
2429                         if (!conservative_stack_mark) {
2430                                 fprintf (stderr, "Precise stack mark not supported - disabling.\n");
2431                                 conservative_stack_mark = TRUE;
2432                         }
2433                         //FIXME we should eventually use the new stack_mark from coop
2434                         sgen_conservatively_pin_objects_from ((void **)aligned_stack_start, (void **)info->client_info.stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
2435                 }
2436
2437                 if (!precise) {
2438                         sgen_conservatively_pin_objects_from ((void**)&info->client_info.ctx, (void**)(&info->client_info.ctx + 1),
2439                                 start_nursery, end_nursery, PIN_TYPE_STACK);
2440
2441                         {
2442                                 // This is used on Coop GC for platforms where we cannot get the data for individual registers.
2443                                 // We force a spill of all registers into the stack and pass a chunk of data into sgen.
2444                                 //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
2445                                 MonoThreadUnwindState *state = &info->client_info.info.thread_saved_state [SELF_SUSPEND_STATE_INDEX];
2446                                 if (state && state->gc_stackdata) {
2447                                         sgen_conservatively_pin_objects_from ((void **)state->gc_stackdata, (void**)((char*)state->gc_stackdata + state->gc_stackdata_size),
2448                                                 start_nursery, end_nursery, PIN_TYPE_STACK);
2449                                 }
2450                         }
2451                 }
2452                 if (precise && info->client_info.info.handle_stack) {
2453                         mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, (GcScanFunc)ctx.ops->copy_or_mark_object, ctx.queue);
2454                 }
2455         } FOREACH_THREAD_END
2456 }
2457
2458 /*
2459  * mono_gc_set_stack_end:
2460  *
2461  *   Set the end of the current threads stack to STACK_END. The stack space between 
2462  * STACK_END and the real end of the threads stack will not be scanned during collections.
2463  */
2464 void
2465 mono_gc_set_stack_end (void *stack_end)
2466 {
2467         SgenThreadInfo *info;
2468
2469         LOCK_GC;
2470         info = mono_thread_info_current ();
2471         if (info) {
2472                 SGEN_ASSERT (0, stack_end < info->client_info.stack_end, "Can only lower stack end");
2473                 info->client_info.stack_end = stack_end;
2474         }
2475         UNLOCK_GC;
2476 }
2477
2478 /*
2479  * Roots
2480  */
2481
2482 int
2483 mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
2484 {
2485         return sgen_register_root (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED, source, msg);
2486 }
2487
2488 int
2489 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
2490 {
2491         return sgen_register_root (start, size, descr, ROOT_TYPE_WBARRIER, source, msg);
2492 }
2493
2494 void
2495 mono_gc_deregister_root (char* addr)
2496 {
2497         sgen_deregister_root (addr);
2498 }
2499
2500 /*
2501  * PThreads
2502  */
2503
2504 #ifndef HOST_WIN32
2505 int
2506 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
2507 {
2508         return pthread_create (new_thread, attr, start_routine, arg);
2509 }
2510 #endif
2511
2512 /*
2513  * Miscellaneous
2514  */
2515
2516 void
2517 sgen_client_total_allocated_heap_changed (size_t allocated_heap)
2518 {
2519         mono_runtime_resource_check_limit (MONO_RESOURCE_GC_HEAP, allocated_heap);
2520 }
2521
2522 gboolean
2523 mono_gc_user_markers_supported (void)
2524 {
2525         return TRUE;
2526 }
2527
2528 gboolean
2529 mono_object_is_alive (MonoObject* o)
2530 {
2531         return TRUE;
2532 }
2533
2534 int
2535 mono_gc_get_generation (MonoObject *obj)
2536 {
2537         if (sgen_ptr_in_nursery (obj))
2538                 return 0;
2539         return 1;
2540 }
2541
2542 const char *
2543 mono_gc_get_gc_name (void)
2544 {
2545         return "sgen";
2546 }
2547
2548 char*
2549 mono_gc_get_description (void)
2550 {
2551 #ifdef HAVE_CONC_GC_AS_DEFAULT
2552         return g_strdup ("sgen (concurrent by default)");
2553 #else
2554         return g_strdup ("sgen");
2555 #endif
2556 }
2557
2558 void
2559 mono_gc_set_desktop_mode (void)
2560 {
2561 }
2562
2563 gboolean
2564 mono_gc_is_moving (void)
2565 {
2566         return TRUE;
2567 }
2568
2569 gboolean
2570 mono_gc_is_disabled (void)
2571 {
2572         return FALSE;
2573 }
2574
2575 #ifdef HOST_WIN32
2576 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
2577 {
2578         return TRUE;
2579 }
2580 #endif
2581
2582 int
2583 mono_gc_max_generation (void)
2584 {
2585         return 1;
2586 }
2587
2588 gboolean
2589 mono_gc_precise_stack_mark_enabled (void)
2590 {
2591         return !conservative_stack_mark;
2592 }
2593
2594 void
2595 mono_gc_collect (int generation)
2596 {
2597         sgen_gc_collect (generation);
2598 }
2599
2600 int
2601 mono_gc_collection_count (int generation)
2602 {
2603         return sgen_gc_collection_count (generation);
2604 }
2605
2606 int64_t
2607 mono_gc_get_used_size (void)
2608 {
2609         return (int64_t)sgen_gc_get_used_size ();
2610 }
2611
2612 int64_t
2613 mono_gc_get_heap_size (void)
2614 {
2615         return (int64_t)sgen_gc_get_total_heap_allocation ();
2616 }
2617
2618 MonoGCDescriptor
2619 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
2620 {
2621         return sgen_make_user_root_descriptor (marker);
2622 }
2623
2624 MonoGCDescriptor
2625 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
2626 {
2627         return SGEN_DESC_STRING;
2628 }
2629
2630 void*
2631 mono_gc_get_nursery (int *shift_bits, size_t *size)
2632 {
2633         *size = sgen_nursery_size;
2634         *shift_bits = DEFAULT_NURSERY_BITS;
2635         return sgen_get_nursery_start ();
2636 }
2637
2638 int
2639 mono_gc_get_los_limit (void)
2640 {
2641         return SGEN_MAX_SMALL_OBJ_SIZE;
2642 }
2643
2644 gpointer
2645 sgen_client_default_metadata (void)
2646 {
2647         return mono_domain_get ();
2648 }
2649
2650 gpointer
2651 sgen_client_metadata_for_object (GCObject *obj)
2652 {
2653         return mono_object_domain (obj);
2654 }
2655
2656 /**
2657  * mono_gchandle_is_in_domain:
2658  * @gchandle: a GCHandle's handle.
2659  * @domain: An application domain.
2660  *
2661  * Returns: TRUE if the object wrapped by the @gchandle belongs to the specific @domain.
2662  */
2663 gboolean
2664 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
2665 {
2666         MonoDomain *gchandle_domain = (MonoDomain *)sgen_gchandle_get_metadata (gchandle);
2667         return domain->domain_id == gchandle_domain->domain_id;
2668 }
2669
2670 /**
2671  * mono_gchandle_free_domain:
2672  * @unloading: domain that is unloading
2673  *
2674  * Function used internally to cleanup any GC handle for objects belonging
2675  * to the specified domain during appdomain unload.
2676  */
2677 void
2678 mono_gchandle_free_domain (MonoDomain *unloading)
2679 {
2680 }
2681
2682 static gpointer
2683 null_link_if_in_domain (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
2684 {
2685         MonoDomain *unloading_domain = (MonoDomain *)user;
2686         MonoDomain *obj_domain;
2687         gboolean is_weak = MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type);
2688         if (MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden)) {
2689                 MonoObject *obj = (MonoObject *)MONO_GC_REVEAL_POINTER (hidden, is_weak);
2690                 obj_domain = mono_object_domain (obj);
2691         } else {
2692                 obj_domain = (MonoDomain *)MONO_GC_REVEAL_POINTER (hidden, is_weak);
2693         }
2694         if (unloading_domain->domain_id == obj_domain->domain_id)
2695                 return NULL;
2696         return hidden;
2697 }
2698
2699 void
2700 sgen_null_links_for_domain (MonoDomain *domain)
2701 {
2702         guint type;
2703         for (type = HANDLE_TYPE_MIN; type < HANDLE_TYPE_MAX; ++type)
2704                 sgen_gchandle_iterate ((GCHandleType)type, GENERATION_OLD, null_link_if_in_domain, domain);
2705 }
2706
2707 void
2708 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
2709 {
2710         sgen_gchandle_set_target (gchandle, obj);
2711 }
2712
2713 void
2714 sgen_client_gchandle_created (int handle_type, GCObject *obj, guint32 handle)
2715 {
2716 #ifndef DISABLE_PERFCOUNTERS
2717         mono_perfcounters->gc_num_handles++;
2718 #endif
2719         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handle_type, handle, obj);
2720 }
2721
2722 void
2723 sgen_client_gchandle_destroyed (int handle_type, guint32 handle)
2724 {
2725 #ifndef DISABLE_PERFCOUNTERS
2726         mono_perfcounters->gc_num_handles--;
2727 #endif
2728         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handle_type, handle, NULL);
2729 }
2730
2731 void
2732 sgen_client_ensure_weak_gchandles_accessible (void)
2733 {
2734         /*
2735          * During the second bridge processing step the world is
2736          * running again.  That step processes all weak links once
2737          * more to null those that refer to dead objects.  Before that
2738          * is completed, those links must not be followed, so we
2739          * conservatively wait for bridge processing when any weak
2740          * link is dereferenced.
2741          */
2742         /* FIXME: A GC can occur after this check fails, in which case we
2743          * should wait for bridge processing but would fail to do so.
2744          */
2745         if (G_UNLIKELY (bridge_processing_in_progress))
2746                 mono_gc_wait_for_bridge_processing ();
2747 }
2748
2749 void*
2750 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
2751 {
2752         void *result;
2753         LOCK_INTERRUPTION;
2754         result = func (data);
2755         UNLOCK_INTERRUPTION;
2756         return result;
2757 }
2758
2759 void
2760 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
2761 {
2762         // FIXME:
2763 }
2764
2765 guint8*
2766 mono_gc_get_card_table (int *shift_bits, gpointer *mask)
2767 {
2768         return sgen_get_card_table_configuration (shift_bits, mask);
2769 }
2770
2771 gboolean
2772 mono_gc_card_table_nursery_check (void)
2773 {
2774         return !sgen_get_major_collector ()->is_concurrent;
2775 }
2776
2777 /* Negative value to remove */
2778 void
2779 mono_gc_add_memory_pressure (gint64 value)
2780 {
2781         /* FIXME: Implement at some point? */
2782 }
2783
2784 /*
2785  * Logging
2786  */
2787
2788 void
2789 sgen_client_degraded_allocation (size_t size)
2790 {
2791         static int last_major_gc_warned = -1;
2792         static int num_degraded = 0;
2793
2794         if (last_major_gc_warned < (int)gc_stats.major_gc_count) {
2795                 ++num_degraded;
2796                 if (num_degraded == 1 || num_degraded == 3)
2797                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Degraded allocation.  Consider increasing nursery-size if the warning persists.");
2798                 else if (num_degraded == 10)
2799                         mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_GC, "Warning: Repeated degraded allocation.  Consider increasing nursery-size.");
2800                 last_major_gc_warned = gc_stats.major_gc_count;
2801         }
2802 }
2803
2804 /*
2805  * Debugging
2806  */
2807
2808 const char*
2809 sgen_client_description_for_internal_mem_type (int type)
2810 {
2811         switch (type) {
2812         case INTERNAL_MEM_EPHEMERON_LINK: return "ephemeron-link";
2813         case INTERNAL_MEM_MOVED_OBJECT: return "moved-object";
2814         default:
2815                 return NULL;
2816         }
2817 }
2818
2819 void
2820 sgen_client_pre_collection_checks (void)
2821 {
2822         if (sgen_mono_xdomain_checks) {
2823                 sgen_clear_nursery_fragments ();
2824                 sgen_check_for_xdomain_refs ();
2825         }
2826 }
2827
2828 gboolean
2829 sgen_client_vtable_is_inited (MonoVTable *vt)
2830 {
2831         return vt->klass->inited;
2832 }
2833
2834 const char*
2835 sgen_client_vtable_get_namespace (MonoVTable *vt)
2836 {
2837         return vt->klass->name_space;
2838 }
2839
2840 const char*
2841 sgen_client_vtable_get_name (MonoVTable *vt)
2842 {
2843         return vt->klass->name;
2844 }
2845
2846 /*
2847  * Initialization
2848  */
2849
2850 void
2851 sgen_client_init (void)
2852 {
2853         int dummy;
2854         MonoThreadInfoCallbacks cb;
2855
2856         cb.thread_register = sgen_thread_register;
2857         cb.thread_detach = sgen_thread_detach;
2858         cb.thread_unregister = sgen_thread_unregister;
2859         cb.thread_attach = sgen_thread_attach;
2860         cb.mono_thread_in_critical_region = thread_in_critical_region;
2861         cb.ip_in_critical_region = ip_in_critical_region;
2862
2863         mono_threads_init (&cb, sizeof (SgenThreadInfo));
2864
2865         ///* Keep this the default for now */
2866         /* Precise marking is broken on all supported targets. Disable until fixed. */
2867         conservative_stack_mark = TRUE;
2868
2869         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
2870
2871         mono_sgen_init_stw ();
2872
2873         mono_tls_init_gc_keys ();
2874
2875         mono_gc_register_thread (&dummy);
2876 }
2877
2878 gboolean
2879 sgen_client_handle_gc_param (const char *opt)
2880 {
2881         if (g_str_has_prefix (opt, "stack-mark=")) {
2882                 opt = strchr (opt, '=') + 1;
2883                 if (!strcmp (opt, "precise")) {
2884                         conservative_stack_mark = FALSE;
2885                 } else if (!strcmp (opt, "conservative")) {
2886                         conservative_stack_mark = TRUE;
2887                 } else {
2888                         sgen_env_var_error (MONO_GC_PARAMS_NAME, conservative_stack_mark ? "Using `conservative`." : "Using `precise`.",
2889                                         "Invalid value `%s` for `stack-mark` option, possible values are: `precise`, `conservative`.", opt);
2890                 }
2891         } else if (g_str_has_prefix (opt, "bridge-implementation=")) {
2892                 opt = strchr (opt, '=') + 1;
2893                 sgen_set_bridge_implementation (opt);
2894         } else if (g_str_has_prefix (opt, "toggleref-test")) {
2895                 /* FIXME: This should probably in MONO_GC_DEBUG */
2896                 sgen_register_test_toggleref_callback ();
2897         } else if (!sgen_bridge_handle_gc_param (opt)) {
2898                 return FALSE;
2899         }
2900         return TRUE;
2901 }
2902
2903 void
2904 sgen_client_print_gc_params_usage (void)
2905 {
2906         fprintf (stderr, "  stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
2907 }
2908
2909 gboolean
2910 sgen_client_handle_gc_debug (const char *opt)
2911 {
2912         if (!strcmp (opt, "xdomain-checks")) {
2913                 sgen_mono_xdomain_checks = TRUE;
2914         } else if (!strcmp (opt, "do-not-finalize")) {
2915                 mono_do_not_finalize = TRUE;
2916         } else if (g_str_has_prefix (opt, "do-not-finalize=")) {
2917                 opt = strchr (opt, '=') + 1;
2918                 mono_do_not_finalize = TRUE;
2919                 mono_do_not_finalize_class_names = g_strsplit (opt, ",", 0);
2920         } else if (!strcmp (opt, "log-finalizers")) {
2921                 log_finalizers = TRUE;
2922         } else if (!strcmp (opt, "no-managed-allocator")) {
2923                 sgen_set_use_managed_allocator (FALSE);
2924         } else if (!sgen_bridge_handle_gc_debug (opt)) {
2925                 return FALSE;
2926         }
2927         return TRUE;
2928 }
2929
2930 void
2931 sgen_client_print_gc_debug_usage (void)
2932 {
2933         fprintf (stderr, "  xdomain-checks\n");
2934         fprintf (stderr, "  do-not-finalize\n");
2935         fprintf (stderr, "  log-finalizers\n");
2936         fprintf (stderr, "  no-managed-allocator\n");
2937         sgen_bridge_print_gc_debug_usage ();
2938 }
2939
2940
2941 gpointer
2942 sgen_client_get_provenance (void)
2943 {
2944 #ifdef SGEN_OBJECT_PROVENANCE
2945         MonoGCCallbacks *cb = mono_gc_get_gc_callbacks ();
2946         gpointer (*get_provenance_func) (void);
2947         if (!cb)
2948                 return NULL;
2949         get_provenance_func = cb->get_provenance_func;
2950         if (get_provenance_func)
2951                 return get_provenance_func ();
2952         return NULL;
2953 #else
2954         return NULL;
2955 #endif
2956 }
2957
2958 void
2959 sgen_client_describe_invalid_pointer (GCObject *ptr)
2960 {
2961         sgen_bridge_describe_pointer (ptr);
2962 }
2963
2964 static gboolean gc_inited;
2965
2966 void
2967 mono_gc_base_init (void)
2968 {
2969         if (gc_inited)
2970                 return;
2971
2972         mono_counters_init ();
2973
2974 #ifndef HOST_WIN32
2975         mono_w32handle_init ();
2976 #endif
2977
2978 #ifdef HEAVY_STATISTICS
2979         mono_counters_register ("los marked cards", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_marked_cards);
2980         mono_counters_register ("los array cards scanned ", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_cards);
2981         mono_counters_register ("los array remsets", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &los_array_remsets);
2982
2983         mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_set_arrayref);
2984         mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_value_copy);
2985         mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_wbarrier_object_copy);
2986 #endif
2987
2988         sgen_gc_init ();
2989
2990         gc_inited = TRUE;
2991 }
2992
2993 void
2994 mono_gc_base_cleanup (void)
2995 {
2996         sgen_thread_pool_shutdown ();
2997
2998         // We should have consumed any outstanding moves.
2999         g_assert (sgen_pointer_queue_is_empty (&moved_objects_queue));
3000 }
3001
3002 gboolean
3003 mono_gc_is_null (void)
3004 {
3005         return FALSE;
3006 }
3007
3008 #endif