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