[sgen] `GCObject` and `GCVTable` instead of `MonoObject` and `MonoVTable`.
[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 "metadata/sgen-gc.h"
24 #include "metadata/sgen-protocol.h"
25 #include "metadata/monitor.h"
26 #include "metadata/sgen-layout-stats.h"
27 #include "metadata/sgen-client.h"
28 #include "metadata/sgen-cardtable.h"
29 #include "metadata/marshal.h"
30 #include "metadata/method-builder.h"
31 #include "metadata/abi-details.h"
32 #include "metadata/profiler-private.h"
33 #include "utils/mono-memory-model.h"
34
35 /* If set, check that there are no references to the domain left at domain unload */
36 gboolean sgen_mono_xdomain_checks = FALSE;
37
38 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
39
40 /*
41  * Write barriers
42  */
43
44 static gboolean
45 ptr_on_stack (void *ptr)
46 {
47         gpointer stack_start = &stack_start;
48         SgenThreadInfo *info = mono_thread_info_current ();
49
50         if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
51                 return TRUE;
52         return FALSE;
53 }
54
55 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
56 #undef HANDLE_PTR
57 #define HANDLE_PTR(ptr,obj) do {                                        \
58                 gpointer o = *(gpointer*)(ptr);                         \
59                 if ((o)) {                                              \
60                         gpointer d = ((char*)dest) + ((char*)(ptr) - (char*)(obj)); \
61                         binary_protocol_wbarrier (d, o, (gpointer) SGEN_LOAD_VTABLE (o)); \
62                 }                                                       \
63         } while (0)
64
65 static void
66 scan_object_for_binary_protocol_copy_wbarrier (gpointer dest, char *start, mword desc)
67 {
68 #define SCAN_OBJECT_NOVTABLE
69 #include "sgen-scan-object.h"
70 }
71 #endif
72
73 void
74 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
75 {
76         HEAVY_STAT (++stat_wbarrier_value_copy);
77         g_assert (klass->valuetype);
78
79         SGEN_LOG (8, "Adding value remset at %p, count %d, descr %p for class %s (%p)", dest, count, klass->gc_descr, klass->name, klass);
80
81         if (sgen_ptr_in_nursery (dest) || ptr_on_stack (dest) || !sgen_gc_descr_has_references ((mword)klass->gc_descr)) {
82                 size_t element_size = mono_class_value_size (klass, NULL);
83                 size_t size = count * element_size;
84                 mono_gc_memmove_atomic (dest, src, size);               
85                 return;
86         }
87
88 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
89         if (binary_protocol_is_heavy_enabled ()) {
90                 size_t element_size = mono_class_value_size (klass, NULL);
91                 int i;
92                 for (i = 0; i < count; ++i) {
93                         scan_object_for_binary_protocol_copy_wbarrier ((char*)dest + i * element_size,
94                                         (char*)src + i * element_size - sizeof (MonoObject),
95                                         (mword) klass->gc_descr);
96                 }
97         }
98 #endif
99
100         sgen_get_remset ()->wbarrier_value_copy (dest, src, count, mono_class_value_size (klass, NULL));
101 }
102
103 /**
104  * mono_gc_wbarrier_object_copy:
105  *
106  * Write barrier to call when obj is the result of a clone or copy of an object.
107  */
108 void
109 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
110 {
111         int size;
112
113         HEAVY_STAT (++stat_wbarrier_object_copy);
114
115         if (sgen_ptr_in_nursery (obj) || ptr_on_stack (obj)) {
116                 size = mono_object_class (obj)->instance_size;
117                 mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
118                                 size - sizeof (MonoObject));
119                 return; 
120         }
121
122 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
123         if (binary_protocol_is_heavy_enabled ())
124                 scan_object_for_binary_protocol_copy_wbarrier (obj, (char*)src, (mword) src->vtable->gc_descr);
125 #endif
126
127         sgen_get_remset ()->wbarrier_object_copy ((GCObject*)obj, (GCObject*)src);
128 }
129
130 void
131 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
132 {
133         HEAVY_STAT (++stat_wbarrier_set_arrayref);
134         if (sgen_ptr_in_nursery (slot_ptr)) {
135                 *(void**)slot_ptr = value;
136                 return;
137         }
138         SGEN_LOG (8, "Adding remset at %p", slot_ptr);
139         if (value)
140                 binary_protocol_wbarrier (slot_ptr, value, value->vtable);
141
142         sgen_get_remset ()->wbarrier_set_field ((GCObject*)arr, slot_ptr, (GCObject*)value);
143 }
144
145 /*
146  * Dummy filler objects
147  */
148
149 /* Vtable of the objects used to fill out nursery fragments before a collection */
150 static GCVTable *array_fill_vtable;
151
152 GCVTable*
153 sgen_client_get_array_fill_vtable (void)
154 {
155         if (!array_fill_vtable) {
156                 static MonoClass klass;
157                 static char _vtable[sizeof(MonoVTable)+8];
158                 MonoVTable* vtable = (MonoVTable*) ALIGN_TO(_vtable, 8);
159                 gsize bmap;
160
161                 MonoDomain *domain = mono_get_root_domain ();
162                 g_assert (domain);
163
164                 klass.element_class = mono_defaults.byte_class;
165                 klass.rank = 1;
166                 klass.instance_size = sizeof (MonoArray);
167                 klass.sizes.element_size = 1;
168                 klass.name = "array_filler_type";
169
170                 vtable->klass = &klass;
171                 bmap = 0;
172                 vtable->gc_descr = mono_gc_make_descr_for_array (TRUE, &bmap, 0, 1);
173                 vtable->rank = 1;
174
175                 array_fill_vtable = (GCVTable*)vtable;
176         }
177         return array_fill_vtable;
178 }
179
180 gboolean
181 sgen_client_array_fill_range (char *start, size_t size)
182 {
183         MonoArray *o;
184
185         if (size < sizeof (MonoArray)) {
186                 memset (start, 0, size);
187                 return FALSE;
188         }
189
190         o = (MonoArray*)start;
191         o->obj.vtable = (MonoVTable*)sgen_client_get_array_fill_vtable ();
192         /* Mark this as not a real object */
193         o->obj.synchronisation = GINT_TO_POINTER (-1);
194         o->bounds = NULL;
195         o->max_length = (mono_array_size_t)(size - sizeof (MonoArray));
196
197         return TRUE;
198 }
199
200 void
201 sgen_client_zero_array_fill_header (void *p, size_t size)
202 {
203         if (size >= sizeof (MonoArray)) {
204                 memset (p, 0, sizeof (MonoArray));
205         } else {
206                 static guint8 zeros [sizeof (MonoArray)];
207
208                 SGEN_ASSERT (0, !memcmp (p, zeros, size), "TLAB segment must be zeroed out.");
209         }
210 }
211
212 /*
213  * Finalization
214  */
215
216 static MonoGCFinalizerCallbacks fin_callbacks;
217
218 guint
219 mono_gc_get_vtable_bits (MonoClass *class)
220 {
221         guint res = 0;
222         /* FIXME move this to the bridge code */
223         if (sgen_need_bridge_processing ()) {
224                 switch (sgen_bridge_class_kind (class)) {
225                 case GC_BRIDGE_TRANSPARENT_BRIDGE_CLASS:
226                 case GC_BRIDGE_OPAQUE_BRIDGE_CLASS:
227                         res = SGEN_GC_BIT_BRIDGE_OBJECT;
228                         break;
229                 case GC_BRIDGE_OPAQUE_CLASS:
230                         res = SGEN_GC_BIT_BRIDGE_OPAQUE_OBJECT;
231                         break;
232                 case GC_BRIDGE_TRANSPARENT_CLASS:
233                         break;
234                 }
235         }
236         if (fin_callbacks.is_class_finalization_aware) {
237                 if (fin_callbacks.is_class_finalization_aware (class))
238                         res |= SGEN_GC_BIT_FINALIZER_AWARE;
239         }
240         return res;
241 }
242
243 static gboolean
244 is_finalization_aware (MonoObject *obj)
245 {
246         MonoVTable *vt = ((MonoVTable*)SGEN_LOAD_VTABLE (obj));
247         return (vt->gc_bits & SGEN_GC_BIT_FINALIZER_AWARE) == SGEN_GC_BIT_FINALIZER_AWARE;
248 }
249
250 void
251 sgen_client_object_queued_for_finalization (GCObject *gc_obj)
252 {
253         MonoObject *obj = (MonoObject*)gc_obj;
254         if (fin_callbacks.object_queued_for_finalization && is_finalization_aware (obj))
255                 fin_callbacks.object_queued_for_finalization (obj);
256 }
257
258 void
259 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
260 {
261         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
262                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
263
264         fin_callbacks = *callbacks;
265 }
266
267 /*
268  * Ephemerons
269  */
270
271 typedef struct _EphemeronLinkNode EphemeronLinkNode;
272
273 struct _EphemeronLinkNode {
274         EphemeronLinkNode *next;
275         char *array;
276 };
277
278 typedef struct {
279        void *key;
280        void *value;
281 } Ephemeron;
282
283 static EphemeronLinkNode *ephemeron_list;
284
285 /* LOCKING: requires that the GC lock is held */
286 static void
287 null_ephemerons_for_domain (MonoDomain *domain)
288 {
289         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
290
291         while (current) {
292                 MonoObject *object = (MonoObject*)current->array;
293
294                 if (object)
295                         SGEN_ASSERT (0, object->vtable, "Can't have objects without vtables.");
296
297                 if (object && object->vtable->domain == domain) {
298                         EphemeronLinkNode *tmp = current;
299
300                         if (prev)
301                                 prev->next = current->next;
302                         else
303                                 ephemeron_list = current->next;
304
305                         current = current->next;
306                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
307                 } else {
308                         prev = current;
309                         current = current->next;
310                 }
311         }
312 }
313
314 /* LOCKING: requires that the GC lock is held */
315 void
316 sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx)
317 {
318         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
319         SgenGrayQueue *queue = ctx.queue;
320         EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
321         MonoArray *array;
322         Ephemeron *cur, *array_end;
323         char *tombstone;
324
325         while (current) {
326                 char *object = current->array;
327
328                 if (!sgen_is_object_alive_for_current_gen (object)) {
329                         EphemeronLinkNode *tmp = current;
330
331                         SGEN_LOG (5, "Dead Ephemeron array at %p", object);
332
333                         if (prev)
334                                 prev->next = current->next;
335                         else
336                                 ephemeron_list = current->next;
337
338                         current = current->next;
339                         sgen_free_internal (tmp, INTERNAL_MEM_EPHEMERON_LINK);
340
341                         continue;
342                 }
343
344                 copy_func ((void**)&object, queue);
345                 current->array = object;
346
347                 SGEN_LOG (5, "Clearing unreachable entries for ephemeron array at %p", object);
348
349                 array = (MonoArray*)object;
350                 cur = mono_array_addr (array, Ephemeron, 0);
351                 array_end = cur + mono_array_length_fast (array);
352                 tombstone = (char*)((MonoVTable*)SGEN_LOAD_VTABLE (object))->domain->ephemeron_tombstone;
353
354                 for (; cur < array_end; ++cur) {
355                         char *key = (char*)cur->key;
356
357                         if (!key || key == tombstone)
358                                 continue;
359
360                         SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
361                                 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
362                                 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
363
364                         if (!sgen_is_object_alive_for_current_gen (key)) {
365                                 cur->key = tombstone;
366                                 cur->value = NULL;
367                                 continue;
368                         }
369                 }
370                 prev = current;
371                 current = current->next;
372         }
373 }
374
375 /*
376 LOCKING: requires that the GC lock is held
377
378 Limitations: We scan all ephemerons on every collection since the current design doesn't allow for a simple nursery/mature split.
379 */
380 gboolean
381 sgen_client_mark_ephemerons (ScanCopyContext ctx)
382 {
383         CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
384         SgenGrayQueue *queue = ctx.queue;
385         gboolean nothing_marked = TRUE;
386         EphemeronLinkNode *current = ephemeron_list;
387         MonoArray *array;
388         Ephemeron *cur, *array_end;
389         char *tombstone;
390
391         for (current = ephemeron_list; current; current = current->next) {
392                 char *object = current->array;
393                 SGEN_LOG (5, "Ephemeron array at %p", object);
394
395                 /*It has to be alive*/
396                 if (!sgen_is_object_alive_for_current_gen (object)) {
397                         SGEN_LOG (5, "\tnot reachable");
398                         continue;
399                 }
400
401                 copy_func ((void**)&object, queue);
402
403                 array = (MonoArray*)object;
404                 cur = mono_array_addr (array, Ephemeron, 0);
405                 array_end = cur + mono_array_length_fast (array);
406                 tombstone = (char*)((MonoVTable*)SGEN_LOAD_VTABLE (object))->domain->ephemeron_tombstone;
407
408                 for (; cur < array_end; ++cur) {
409                         char *key = cur->key;
410
411                         if (!key || key == tombstone)
412                                 continue;
413
414                         SGEN_LOG (5, "[%td] key %p (%s) value %p (%s)", cur - mono_array_addr (array, Ephemeron, 0),
415                                 key, sgen_is_object_alive_for_current_gen (key) ? "reachable" : "unreachable",
416                                 cur->value, cur->value && sgen_is_object_alive_for_current_gen (cur->value) ? "reachable" : "unreachable");
417
418                         if (sgen_is_object_alive_for_current_gen (key)) {
419                                 char *value = cur->value;
420
421                                 copy_func ((void**)&cur->key, queue);
422                                 if (value) {
423                                         if (!sgen_is_object_alive_for_current_gen (value))
424                                                 nothing_marked = FALSE;
425                                         copy_func ((void**)&cur->value, queue);
426                                 }
427                         }
428                 }
429         }
430
431         SGEN_LOG (5, "Ephemeron run finished. Is it done %d", nothing_marked);
432         return nothing_marked;
433 }
434
435 gboolean
436 mono_gc_ephemeron_array_add (MonoObject *obj)
437 {
438         EphemeronLinkNode *node;
439
440         LOCK_GC;
441
442         node = sgen_alloc_internal (INTERNAL_MEM_EPHEMERON_LINK);
443         if (!node) {
444                 UNLOCK_GC;
445                 return FALSE;
446         }
447         node->array = (char*)obj;
448         node->next = ephemeron_list;
449         ephemeron_list = node;
450
451         SGEN_LOG (5, "Registered ephemeron array %p", obj);
452
453         UNLOCK_GC;
454         return TRUE;
455 }
456
457 /*
458  * Appdomain handling
459  */
460
461 static gboolean
462 need_remove_object_for_domain (char *start, MonoDomain *domain)
463 {
464         if (mono_object_domain (start) == domain) {
465                 SGEN_LOG (4, "Need to cleanup object %p", start);
466                 binary_protocol_cleanup (start, (gpointer)SGEN_LOAD_VTABLE (start), sgen_safe_object_get_size ((GCObject*)start));
467                 return TRUE;
468         }
469         return FALSE;
470 }
471
472 static void
473 process_object_for_domain_clearing (char *start, MonoDomain *domain)
474 {
475         MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (start);
476         if (vt->klass == mono_defaults.internal_thread_class)
477                 g_assert (mono_object_domain (start) == mono_get_root_domain ());
478         /* The object could be a proxy for an object in the domain
479            we're deleting. */
480 #ifndef DISABLE_REMOTING
481         if (mono_defaults.real_proxy_class->supertypes && mono_class_has_parent_fast (vt->klass, mono_defaults.real_proxy_class)) {
482                 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
483
484                 /* The server could already have been zeroed out, so
485                    we need to check for that, too. */
486                 if (server && (!SGEN_LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
487                         SGEN_LOG (4, "Cleaning up remote pointer in %p to object %p", start, server);
488                         ((MonoRealProxy*)start)->unwrapped_server = NULL;
489                 }
490         }
491 #endif
492 }
493
494 static gboolean
495 clear_domain_process_object (char *obj, MonoDomain *domain)
496 {
497         gboolean remove;
498
499         process_object_for_domain_clearing (obj, domain);
500         remove = need_remove_object_for_domain (obj, domain);
501
502         if (remove && ((MonoObject*)obj)->synchronisation) {
503                 void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)obj);
504                 if (dislink)
505                         sgen_register_disappearing_link (NULL, dislink, FALSE, TRUE);
506         }
507
508         return remove;
509 }
510
511 static void
512 clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
513 {
514         if (clear_domain_process_object (obj, domain)) {
515                 CANARIFY_SIZE (size);
516                 memset (obj, 0, size);
517         }
518 }
519
520 static void
521 clear_domain_process_major_object_callback (char *obj, size_t size, MonoDomain *domain)
522 {
523         clear_domain_process_object (obj, domain);
524 }
525
526 static void
527 clear_domain_free_major_non_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
528 {
529         if (need_remove_object_for_domain (obj, domain))
530                 major_collector.free_non_pinned_object (obj, size);
531 }
532
533 static void
534 clear_domain_free_major_pinned_object_callback (char *obj, size_t size, MonoDomain *domain)
535 {
536         if (need_remove_object_for_domain (obj, domain))
537                 major_collector.free_pinned_object (obj, size);
538 }
539
540 /*
541  * When appdomains are unloaded we can easily remove objects that have finalizers,
542  * but all the others could still be present in random places on the heap.
543  * We need a sweep to get rid of them even though it's going to be costly
544  * with big heaps.
545  * The reason we need to remove them is because we access the vtable and class
546  * structures to know the object size and the reference bitmap: once the domain is
547  * unloaded the point to random memory.
548  */
549 void
550 mono_gc_clear_domain (MonoDomain * domain)
551 {
552         LOSObject *bigobj, *prev;
553         int i;
554
555         LOCK_GC;
556
557         binary_protocol_domain_unload_begin (domain);
558
559         sgen_stop_world (0);
560
561         if (sgen_concurrent_collection_in_progress ())
562                 sgen_perform_collection (0, GENERATION_OLD, "clear domain", TRUE);
563         SGEN_ASSERT (0, !sgen_concurrent_collection_in_progress (), "We just ordered a synchronous collection.  Why are we collecting concurrently?");
564
565         major_collector.finish_sweeping ();
566
567         sgen_process_fin_stage_entries ();
568         sgen_process_dislink_stage_entries ();
569
570         sgen_clear_nursery_fragments ();
571
572         if (sgen_mono_xdomain_checks && domain != mono_get_root_domain ()) {
573                 sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
574                 sgen_scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
575                 sgen_check_for_xdomain_refs ();
576         }
577
578         /*Ephemerons and dislinks must be processed before LOS since they might end up pointing
579         to memory returned to the OS.*/
580         null_ephemerons_for_domain (domain);
581
582         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
583                 sgen_null_links_for_domain (domain, i);
584
585         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
586                 sgen_remove_finalizers_for_domain (domain, i);
587
588         sgen_scan_area_with_callback (nursery_section->data, nursery_section->end_data,
589                         (IterateObjectCallbackFunc)clear_domain_process_minor_object_callback, domain, FALSE);
590
591         /* We need two passes over major and large objects because
592            freeing such objects might give their memory back to the OS
593            (in the case of large objects) or obliterate its vtable
594            (pinned objects with major-copying or pinned and non-pinned
595            objects with major-mark&sweep), but we might need to
596            dereference a pointer from an object to another object if
597            the first object is a proxy. */
598         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_ALL, (IterateObjectCallbackFunc)clear_domain_process_major_object_callback, domain);
599         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
600                 clear_domain_process_object (bigobj->data, domain);
601
602         prev = NULL;
603         for (bigobj = los_object_list; bigobj;) {
604                 if (need_remove_object_for_domain (bigobj->data, domain)) {
605                         LOSObject *to_free = bigobj;
606                         if (prev)
607                                 prev->next = bigobj->next;
608                         else
609                                 los_object_list = bigobj->next;
610                         bigobj = bigobj->next;
611                         SGEN_LOG (4, "Freeing large object %p", bigobj->data);
612                         sgen_los_free_object (to_free);
613                         continue;
614                 }
615                 prev = bigobj;
616                 bigobj = bigobj->next;
617         }
618         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_NON_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_non_pinned_object_callback, domain);
619         major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
620
621         if (domain == mono_get_root_domain ()) {
622                 sgen_pin_stats_print_class_stats ();
623                 sgen_object_layout_dump (stdout);
624         }
625
626         sgen_restart_world (0, NULL);
627
628         binary_protocol_domain_unload_end (domain);
629         binary_protocol_flush_buffers (FALSE);
630
631         UNLOCK_GC;
632 }
633
634 /*
635  * Managed allocator
636  */
637
638 static MonoMethod* alloc_method_cache [ATYPE_NUM];
639 static gboolean use_managed_allocator = TRUE;
640
641 #ifdef MANAGED_ALLOCATION
642 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
643         a = i,
644
645 enum {
646 #include "mono/cil/opcode.def"
647         CEE_LAST
648 };
649
650 #undef OPDEF
651
652 #ifdef HAVE_KW_THREAD
653
654 #define EMIT_TLS_ACCESS_NEXT_ADDR(mb)   do {    \
655         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
656         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
657         mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_TLAB_NEXT_ADDR);            \
658         } while (0)
659
660 #define EMIT_TLS_ACCESS_TEMP_END(mb)    do {    \
661         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
662         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
663         mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_TLAB_TEMP_END);             \
664         } while (0)
665
666 #else
667
668 #if defined(__APPLE__) || defined (HOST_WIN32)
669 #define EMIT_TLS_ACCESS_NEXT_ADDR(mb)   do {    \
670         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
671         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
672         mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_THREAD_INFO);       \
673         mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_next_addr));  \
674         mono_mb_emit_byte ((mb), CEE_ADD);              \
675         mono_mb_emit_byte ((mb), CEE_LDIND_I);          \
676         } while (0)
677
678 #define EMIT_TLS_ACCESS_TEMP_END(mb)    do {    \
679         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
680         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
681         mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_THREAD_INFO);       \
682         mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_temp_end));   \
683         mono_mb_emit_byte ((mb), CEE_ADD);              \
684         mono_mb_emit_byte ((mb), CEE_LDIND_I);          \
685         } while (0)
686
687 #else
688 #define EMIT_TLS_ACCESS_NEXT_ADDR(mb)   do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
689 #define EMIT_TLS_ACCESS_TEMP_END(mb)    do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
690 #endif
691
692 #endif
693
694 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
695  * for each class. This is currently not easy to do, as it is hard to generate basic 
696  * blocks + branches, but it is easy with the linear IL codebase.
697  *
698  * For this to work we'd need to solve the TLAB race, first.  Now we
699  * require the allocator to be in a few known methods to make sure
700  * that they are executed atomically via the restart mechanism.
701  */
702 static MonoMethod*
703 create_allocator (int atype)
704 {
705         int p_var, size_var;
706         guint32 slowpath_branch, max_size_branch;
707         MonoMethodBuilder *mb;
708         MonoMethod *res;
709         MonoMethodSignature *csig;
710         static gboolean registered = FALSE;
711         int tlab_next_addr_var, new_next_var;
712         int num_params, i;
713         const char *name = NULL;
714         AllocatorWrapperInfo *info;
715
716         if (!registered) {
717                 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
718                 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
719                 mono_register_jit_icall (mono_gc_alloc_string, "mono_gc_alloc_string", mono_create_icall_signature ("object ptr int int32"), FALSE);
720                 registered = TRUE;
721         }
722
723         if (atype == ATYPE_SMALL) {
724                 num_params = 2;
725                 name = "AllocSmall";
726         } else if (atype == ATYPE_NORMAL) {
727                 num_params = 1;
728                 name = "Alloc";
729         } else if (atype == ATYPE_VECTOR) {
730                 num_params = 2;
731                 name = "AllocVector";
732         } else if (atype == ATYPE_STRING) {
733                 num_params = 2;
734                 name = "AllocString";
735         } else {
736                 g_assert_not_reached ();
737         }
738
739         csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
740         if (atype == ATYPE_STRING) {
741                 csig->ret = &mono_defaults.string_class->byval_arg;
742                 csig->params [0] = &mono_defaults.int_class->byval_arg;
743                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
744         } else {
745                 csig->ret = &mono_defaults.object_class->byval_arg;
746                 for (i = 0; i < num_params; ++i)
747                         csig->params [i] = &mono_defaults.int_class->byval_arg;
748         }
749
750         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
751
752 #ifndef DISABLE_JIT
753         size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
754         if (atype == ATYPE_SMALL) {
755                 /* size_var = size_arg */
756                 mono_mb_emit_ldarg (mb, 1);
757                 mono_mb_emit_stloc (mb, size_var);
758         } else if (atype == ATYPE_NORMAL) {
759                 /* size = vtable->klass->instance_size; */
760                 mono_mb_emit_ldarg (mb, 0);
761                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
762                 mono_mb_emit_byte (mb, CEE_ADD);
763                 mono_mb_emit_byte (mb, CEE_LDIND_I);
764                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, instance_size));
765                 mono_mb_emit_byte (mb, CEE_ADD);
766                 /* FIXME: assert instance_size stays a 4 byte integer */
767                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
768                 mono_mb_emit_byte (mb, CEE_CONV_I);
769                 mono_mb_emit_stloc (mb, size_var);
770         } else if (atype == ATYPE_VECTOR) {
771                 MonoExceptionClause *clause;
772                 int pos, pos_leave, pos_error;
773                 MonoClass *oom_exc_class;
774                 MonoMethod *ctor;
775
776                 /*
777                  * n > MONO_ARRAY_MAX_INDEX => OutOfMemoryException
778                  * n < 0                    => OverflowException
779                  *
780                  * We can do an unsigned comparison to catch both cases, then in the error
781                  * case compare signed to distinguish between them.
782                  */
783                 mono_mb_emit_ldarg (mb, 1);
784                 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
785                 mono_mb_emit_byte (mb, CEE_CONV_U);
786                 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
787
788                 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
789                 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
790                 mono_mb_emit_ldarg (mb, 1);
791                 mono_mb_emit_icon (mb, 0);
792                 pos_error = mono_mb_emit_short_branch (mb, CEE_BLT_S);
793                 mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
794                 mono_mb_patch_short_branch (mb, pos_error);
795                 mono_mb_emit_exception (mb, "OverflowException", NULL);
796
797                 mono_mb_patch_short_branch (mb, pos);
798
799                 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
800                 clause->try_offset = mono_mb_get_label (mb);
801
802                 /* vtable->klass->sizes.element_size */
803                 mono_mb_emit_ldarg (mb, 0);
804                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
805                 mono_mb_emit_byte (mb, CEE_ADD);
806                 mono_mb_emit_byte (mb, CEE_LDIND_I);
807                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, sizes));
808                 mono_mb_emit_byte (mb, CEE_ADD);
809                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
810                 mono_mb_emit_byte (mb, CEE_CONV_I);
811
812                 /* * n */
813                 mono_mb_emit_ldarg (mb, 1);
814                 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
815                 /* + sizeof (MonoArray) */
816                 mono_mb_emit_icon (mb, sizeof (MonoArray));
817                 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
818                 mono_mb_emit_stloc (mb, size_var);
819
820                 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
821
822                 /* catch */
823                 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
824                 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
825                 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
826                                 "System", "OverflowException");
827                 g_assert (clause->data.catch_class);
828                 clause->handler_offset = mono_mb_get_label (mb);
829
830                 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
831                                 "System", "OutOfMemoryException");
832                 g_assert (oom_exc_class);
833                 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
834                 g_assert (ctor);
835
836                 mono_mb_emit_byte (mb, CEE_POP);
837                 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
838                 mono_mb_emit_byte (mb, CEE_THROW);
839
840                 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
841                 mono_mb_set_clauses (mb, 1, clause);
842                 mono_mb_patch_branch (mb, pos_leave);
843                 /* end catch */
844         } else if (atype == ATYPE_STRING) {
845                 int pos;
846
847                 /*
848                  * a string allocator method takes the args: (vtable, len)
849                  *
850                  * bytes = offsetof (MonoString, chars) + ((len + 1) * 2)
851                  *
852                  * condition:
853                  *
854                  * bytes <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
855                  *
856                  * therefore:
857                  *
858                  * offsetof (MonoString, chars) + ((len + 1) * 2) <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
859                  * len <= (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - offsetof (MonoString, chars)) / 2 - 1
860                  */
861                 mono_mb_emit_ldarg (mb, 1);
862                 mono_mb_emit_icon (mb, (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - MONO_STRUCT_OFFSET (MonoString, chars)) / 2 - 1);
863                 pos = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
864
865                 mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
866                 mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
867                 mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
868                 mono_mb_patch_short_branch (mb, pos);
869
870                 mono_mb_emit_ldarg (mb, 1);
871                 mono_mb_emit_icon (mb, 1);
872                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
873                 //WE manually fold the above + 2 here
874                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, chars) + 2);
875                 mono_mb_emit_byte (mb, CEE_ADD);
876                 mono_mb_emit_stloc (mb, size_var);
877         } else {
878                 g_assert_not_reached ();
879         }
880
881         if (atype != ATYPE_SMALL) {
882                 /* size += ALLOC_ALIGN - 1; */
883                 mono_mb_emit_ldloc (mb, size_var);
884                 mono_mb_emit_icon (mb, SGEN_ALLOC_ALIGN - 1);
885                 mono_mb_emit_byte (mb, CEE_ADD);
886                 /* size &= ~(ALLOC_ALIGN - 1); */
887                 mono_mb_emit_icon (mb, ~(SGEN_ALLOC_ALIGN - 1));
888                 mono_mb_emit_byte (mb, CEE_AND);
889                 mono_mb_emit_stloc (mb, size_var);
890         }
891
892         /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
893         if (atype != ATYPE_SMALL) {
894                 mono_mb_emit_ldloc (mb, size_var);
895                 mono_mb_emit_icon (mb, SGEN_MAX_SMALL_OBJ_SIZE);
896                 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
897         }
898
899         /*
900          * We need to modify tlab_next, but the JIT only supports reading, so we read
901          * another tls var holding its address instead.
902          */
903
904         /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
905         tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
906         EMIT_TLS_ACCESS_NEXT_ADDR (mb);
907         mono_mb_emit_stloc (mb, tlab_next_addr_var);
908
909         /* p = (void**)tlab_next; */
910         p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
911         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
912         mono_mb_emit_byte (mb, CEE_LDIND_I);
913         mono_mb_emit_stloc (mb, p_var);
914         
915         /* new_next = (char*)p + size; */
916         new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
917         mono_mb_emit_ldloc (mb, p_var);
918         mono_mb_emit_ldloc (mb, size_var);
919         mono_mb_emit_byte (mb, CEE_CONV_I);
920         mono_mb_emit_byte (mb, CEE_ADD);
921         mono_mb_emit_stloc (mb, new_next_var);
922
923         /* if (G_LIKELY (new_next < tlab_temp_end)) */
924         mono_mb_emit_ldloc (mb, new_next_var);
925         EMIT_TLS_ACCESS_TEMP_END (mb);
926         slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
927
928         /* Slowpath */
929         if (atype != ATYPE_SMALL)
930                 mono_mb_patch_short_branch (mb, max_size_branch);
931
932         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
933         mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
934
935         /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
936         mono_mb_emit_ldarg (mb, 0);
937         mono_mb_emit_ldloc (mb, size_var);
938         if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
939                 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
940         } else if (atype == ATYPE_VECTOR) {
941                 mono_mb_emit_ldarg (mb, 1);
942                 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
943         } else if (atype == ATYPE_STRING) {
944                 mono_mb_emit_ldarg (mb, 1);
945                 mono_mb_emit_icall (mb, mono_gc_alloc_string);
946         } else {
947                 g_assert_not_reached ();
948         }
949         mono_mb_emit_byte (mb, CEE_RET);
950
951         /* Fastpath */
952         mono_mb_patch_short_branch (mb, slowpath_branch);
953
954         /* FIXME: Memory barrier */
955
956         /* tlab_next = new_next */
957         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
958         mono_mb_emit_ldloc (mb, new_next_var);
959         mono_mb_emit_byte (mb, CEE_STIND_I);
960
961         /*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. */
962         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
963         mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
964         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL);
965
966         /* *p = vtable; */
967         mono_mb_emit_ldloc (mb, p_var);
968         mono_mb_emit_ldarg (mb, 0);
969         mono_mb_emit_byte (mb, CEE_STIND_I);
970
971         if (atype == ATYPE_VECTOR) {
972                 /* arr->max_length = max_length; */
973                 mono_mb_emit_ldloc (mb, p_var);
974                 mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, max_length));
975                 mono_mb_emit_ldarg (mb, 1);
976 #ifdef MONO_BIG_ARRAYS
977                 mono_mb_emit_byte (mb, CEE_STIND_I);
978 #else
979                 mono_mb_emit_byte (mb, CEE_STIND_I4);
980 #endif
981         } else  if (atype == ATYPE_STRING) {
982                 /* need to set length and clear the last char */
983                 /* s->length = len; */
984                 mono_mb_emit_ldloc (mb, p_var);
985                 mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, length));
986                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
987                 mono_mb_emit_ldarg (mb, 1);
988                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
989                 /* s->chars [len] = 0; */
990                 mono_mb_emit_ldloc (mb, p_var);
991                 mono_mb_emit_ldloc (mb, size_var);
992                 mono_mb_emit_icon (mb, 2);
993                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
994                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
995                 mono_mb_emit_icon (mb, 0);
996                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
997         }
998
999         /*
1000         We must make sure both vtable and max_length are globaly visible before returning to managed land.
1001         */
1002         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1003         mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
1004         mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL);
1005
1006         /* return p */
1007         mono_mb_emit_ldloc (mb, p_var);
1008         mono_mb_emit_byte (mb, CEE_RET);
1009 #endif
1010
1011         res = mono_mb_create_method (mb, csig, 8);
1012         mono_mb_free (mb);
1013         mono_method_get_header (res)->init_locals = FALSE;
1014
1015         info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
1016         info->gc_name = "sgen";
1017         info->alloc_type = atype;
1018         mono_marshal_set_wrapper_info (res, info);
1019
1020         return res;
1021 }
1022 #endif
1023
1024 int
1025 mono_gc_get_aligned_size_for_allocator (int size)
1026 {
1027         int aligned_size = size;
1028         aligned_size += SGEN_ALLOC_ALIGN - 1;
1029         aligned_size &= ~(SGEN_ALLOC_ALIGN - 1);
1030         return aligned_size;
1031 }
1032
1033 /*
1034  * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
1035  * The signature of the called method is:
1036  *      object allocate (MonoVTable *vtable)
1037  */
1038 MonoMethod*
1039 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1040 {
1041 #ifdef MANAGED_ALLOCATION
1042         if (collect_before_allocs)
1043                 return NULL;
1044         if (!mono_runtime_has_tls_get ())
1045                 return NULL;
1046         if (klass->instance_size > tlab_size)
1047                 return NULL;
1048         if (known_instance_size && ALIGN_TO (klass->instance_size, SGEN_ALLOC_ALIGN) >= SGEN_MAX_SMALL_OBJ_SIZE)
1049                 return NULL;
1050         if (klass->has_finalize || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
1051                 return NULL;
1052         if (klass->rank)
1053                 return NULL;
1054         if (klass->byval_arg.type == MONO_TYPE_STRING)
1055                 return mono_gc_get_managed_allocator_by_type (ATYPE_STRING);
1056         /* Generic classes have dynamic field and can go above MAX_SMALL_OBJ_SIZE. */
1057         if (known_instance_size)
1058                 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
1059         else
1060                 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
1061 #else
1062         return NULL;
1063 #endif
1064 }
1065
1066 MonoMethod*
1067 mono_gc_get_managed_array_allocator (MonoClass *klass)
1068 {
1069 #ifdef MANAGED_ALLOCATION
1070         if (klass->rank != 1)
1071                 return NULL;
1072         if (!mono_runtime_has_tls_get ())
1073                 return NULL;
1074         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
1075                 return NULL;
1076         if (has_per_allocation_action)
1077                 return NULL;
1078         g_assert (!mono_class_has_finalizer (klass) && !mono_class_is_marshalbyref (klass));
1079
1080         return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
1081 #else
1082         return NULL;
1083 #endif
1084 }
1085
1086 void
1087 sgen_set_use_managed_allocator (gboolean flag)
1088 {
1089         use_managed_allocator = flag;
1090 }
1091
1092 MonoMethod*
1093 mono_gc_get_managed_allocator_by_type (int atype)
1094 {
1095 #ifdef MANAGED_ALLOCATION
1096         MonoMethod *res;
1097
1098         if (!use_managed_allocator)
1099                 return NULL;
1100
1101         if (!mono_runtime_has_tls_get ())
1102                 return NULL;
1103
1104         res = alloc_method_cache [atype];
1105         if (res)
1106                 return res;
1107
1108         res = create_allocator (atype);
1109         LOCK_GC;
1110         if (alloc_method_cache [atype]) {
1111                 mono_free_method (res);
1112                 res = alloc_method_cache [atype];
1113         } else {
1114                 mono_memory_barrier ();
1115                 alloc_method_cache [atype] = res;
1116         }
1117         UNLOCK_GC;
1118
1119         return res;
1120 #else
1121         return NULL;
1122 #endif
1123 }
1124
1125 guint32
1126 mono_gc_get_managed_allocator_types (void)
1127 {
1128         return ATYPE_NUM;
1129 }
1130
1131 gboolean
1132 sgen_is_managed_allocator (MonoMethod *method)
1133 {
1134         int i;
1135
1136         for (i = 0; i < ATYPE_NUM; ++i)
1137                 if (method == alloc_method_cache [i])
1138                         return TRUE;
1139         return FALSE;
1140 }
1141
1142 gboolean
1143 sgen_has_managed_allocator (void)
1144 {
1145         int i;
1146
1147         for (i = 0; i < ATYPE_NUM; ++i)
1148                 if (alloc_method_cache [i])
1149                         return TRUE;
1150         return FALSE;
1151 }
1152
1153 /*
1154  * Cardtable scanning
1155  */
1156
1157 #define MWORD_MASK (sizeof (mword) - 1)
1158
1159 static inline int
1160 find_card_offset (mword card)
1161 {
1162 /*XXX Use assembly as this generates some pretty bad code */
1163 #if defined(__i386__) && defined(__GNUC__)
1164         return  (__builtin_ffs (card) - 1) / 8;
1165 #elif defined(__x86_64__) && defined(__GNUC__)
1166         return (__builtin_ffsll (card) - 1) / 8;
1167 #elif defined(__s390x__)
1168         return (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
1169 #else
1170         int i;
1171         guint8 *ptr = (guint8 *) &card;
1172         for (i = 0; i < sizeof (mword); ++i) {
1173                 if (ptr[i])
1174                         return i;
1175         }
1176         return 0;
1177 #endif
1178 }
1179
1180 static guint8*
1181 find_next_card (guint8 *card_data, guint8 *end)
1182 {
1183         mword *cards, *cards_end;
1184         mword card;
1185
1186         while ((((mword)card_data) & MWORD_MASK) && card_data < end) {
1187                 if (*card_data)
1188                         return card_data;
1189                 ++card_data;
1190         }
1191
1192         if (card_data == end)
1193                 return end;
1194
1195         cards = (mword*)card_data;
1196         cards_end = (mword*)((mword)end & ~MWORD_MASK);
1197         while (cards < cards_end) {
1198                 card = *cards;
1199                 if (card)
1200                         return (guint8*)cards + find_card_offset (card);
1201                 ++cards;
1202         }
1203
1204         card_data = (guint8*)cards_end;
1205         while (card_data < end) {
1206                 if (*card_data)
1207                         return card_data;
1208                 ++card_data;
1209         }
1210
1211         return end;
1212 }
1213
1214 #define ARRAY_OBJ_INDEX(ptr,array,elem_size) (((char*)(ptr) - ((char*)(array) + G_STRUCT_OFFSET (MonoArray, vector))) / (elem_size))
1215
1216 gboolean
1217 sgen_client_cardtable_scan_object (char *obj, mword block_obj_size, guint8 *cards, gboolean mod_union, ScanCopyContext ctx)
1218 {
1219         MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
1220         MonoClass *klass = vt->klass;
1221
1222         SGEN_ASSERT (0, SGEN_VTABLE_HAS_REFERENCES ((GCVTable*)vt), "Why would we ever call this on reference-free objects?");
1223
1224         if (vt->rank) {
1225                 guint8 *card_data, *card_base;
1226                 guint8 *card_data_end;
1227                 char *obj_start = sgen_card_table_align_pointer (obj);
1228                 mword obj_size = sgen_client_par_object_get_size ((GCVTable*)vt, (GCObject*)obj);
1229                 char *obj_end = obj + obj_size;
1230                 size_t card_count;
1231                 size_t extra_idx = 0;
1232
1233                 MonoArray *arr = (MonoArray*)obj;
1234                 mword desc = (mword)klass->element_class->gc_descr;
1235                 int elem_size = mono_array_element_size (klass);
1236
1237 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1238                 guint8 *overflow_scan_end = NULL;
1239 #endif
1240
1241 #ifdef SGEN_OBJECT_LAYOUT_STATISTICS
1242                 if (klass->element_class->valuetype)
1243                         sgen_object_layout_scanned_vtype_array ();
1244                 else
1245                         sgen_object_layout_scanned_ref_array ();
1246 #endif
1247
1248                 if (cards)
1249                         card_data = cards;
1250                 else
1251                         card_data = sgen_card_table_get_card_scan_address ((mword)obj);
1252
1253                 card_base = card_data;
1254                 card_count = sgen_card_table_number_of_cards_in_range ((mword)obj, obj_size);
1255                 card_data_end = card_data + card_count;
1256
1257
1258 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1259                 /*Check for overflow and if so, setup to scan in two steps*/
1260                 if (!cards && card_data_end >= SGEN_SHADOW_CARDTABLE_END) {
1261                         overflow_scan_end = sgen_shadow_cardtable + (card_data_end - SGEN_SHADOW_CARDTABLE_END);
1262                         card_data_end = SGEN_SHADOW_CARDTABLE_END;
1263                 }
1264
1265 LOOP_HEAD:
1266 #endif
1267
1268                 card_data = find_next_card (card_data, card_data_end);
1269                 for (; card_data < card_data_end; card_data = find_next_card (card_data + 1, card_data_end)) {
1270                         size_t index;
1271                         size_t idx = (card_data - card_base) + extra_idx;
1272                         char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES);
1273                         char *card_end = start + CARD_SIZE_IN_BYTES;
1274                         char *first_elem, *elem;
1275
1276                         HEAVY_STAT (++los_marked_cards);
1277
1278                         if (!cards)
1279                                 sgen_card_table_prepare_card_for_scanning (card_data);
1280
1281                         card_end = MIN (card_end, obj_end);
1282
1283                         if (start <= (char*)arr->vector)
1284                                 index = 0;
1285                         else
1286                                 index = ARRAY_OBJ_INDEX (start, obj, elem_size);
1287
1288                         elem = first_elem = (char*)mono_array_addr_with_size_fast ((MonoArray*)obj, elem_size, index);
1289                         if (klass->element_class->valuetype) {
1290                                 ScanVTypeFunc scan_vtype_func = ctx.ops->scan_vtype;
1291
1292                                 for (; elem < card_end; elem += elem_size)
1293                                         scan_vtype_func (obj, elem, desc, ctx.queue BINARY_PROTOCOL_ARG (elem_size));
1294                         } else {
1295                                 CopyOrMarkObjectFunc copy_func = ctx.ops->copy_or_mark_object;
1296
1297                                 HEAVY_STAT (++los_array_cards);
1298                                 for (; elem < card_end; elem += SIZEOF_VOID_P) {
1299                                         gpointer new, old = *(gpointer*)elem;
1300                                         if ((mod_union && old) || G_UNLIKELY (sgen_ptr_in_nursery (old))) {
1301                                                 HEAVY_STAT (++los_array_remsets);
1302                                                 copy_func ((void**)elem, ctx.queue);
1303                                                 new = *(gpointer*)elem;
1304                                                 if (G_UNLIKELY (sgen_ptr_in_nursery (new)))
1305                                                         sgen_add_to_global_remset (elem, new);
1306                                         }
1307                                 }
1308                         }
1309
1310                         binary_protocol_card_scan (first_elem, elem - first_elem);
1311                 }
1312
1313 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
1314                 if (overflow_scan_end) {
1315                         extra_idx = card_data - card_base;
1316                         card_base = card_data = sgen_shadow_cardtable;
1317                         card_data_end = overflow_scan_end;
1318                         overflow_scan_end = NULL;
1319                         goto LOOP_HEAD;
1320                 }
1321 #endif
1322                 return TRUE;
1323         }
1324
1325         return FALSE;
1326 }
1327
1328 /*
1329  * Array and string allocation
1330  */
1331
1332 void*
1333 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
1334 {
1335         MonoArray *arr;
1336         TLAB_ACCESS_INIT;
1337
1338         if (!SGEN_CAN_ALIGN_UP (size))
1339                 return NULL;
1340
1341 #ifndef DISABLE_CRITICAL_REGION
1342         ENTER_CRITICAL_REGION;
1343         arr = sgen_try_alloc_obj_nolock ((GCVTable*)vtable, size);
1344         if (arr) {
1345                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1346                 arr->max_length = (mono_array_size_t)max_length;
1347                 EXIT_CRITICAL_REGION;
1348                 goto done;
1349         }
1350         EXIT_CRITICAL_REGION;
1351 #endif
1352
1353         LOCK_GC;
1354
1355         arr = sgen_alloc_obj_nolock ((GCVTable*)vtable, size);
1356         if (G_UNLIKELY (!arr)) {
1357                 UNLOCK_GC;
1358                 return mono_gc_out_of_memory (size);
1359         }
1360
1361         arr->max_length = (mono_array_size_t)max_length;
1362
1363         UNLOCK_GC;
1364
1365  done:
1366         SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size ((GCVTable*)vtable, (GCObject*)arr)), "Vector has incorrect size.");
1367         return arr;
1368 }
1369
1370 void*
1371 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
1372 {
1373         MonoArray *arr;
1374         MonoArrayBounds *bounds;
1375         TLAB_ACCESS_INIT;
1376
1377         if (!SGEN_CAN_ALIGN_UP (size))
1378                 return NULL;
1379
1380 #ifndef DISABLE_CRITICAL_REGION
1381         ENTER_CRITICAL_REGION;
1382         arr = sgen_try_alloc_obj_nolock ((GCVTable*)vtable, size);
1383         if (arr) {
1384                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1385                 arr->max_length = (mono_array_size_t)max_length;
1386
1387                 bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
1388                 arr->bounds = bounds;
1389                 EXIT_CRITICAL_REGION;
1390                 goto done;
1391         }
1392         EXIT_CRITICAL_REGION;
1393 #endif
1394
1395         LOCK_GC;
1396
1397         arr = sgen_alloc_obj_nolock ((GCVTable*)vtable, size);
1398         if (G_UNLIKELY (!arr)) {
1399                 UNLOCK_GC;
1400                 return mono_gc_out_of_memory (size);
1401         }
1402
1403         arr->max_length = (mono_array_size_t)max_length;
1404
1405         bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
1406         arr->bounds = bounds;
1407
1408         UNLOCK_GC;
1409
1410  done:
1411         SGEN_ASSERT (6, SGEN_ALIGN_UP (size) == SGEN_ALIGN_UP (sgen_client_par_object_get_size ((GCVTable*)vtable, (GCObject*)arr)), "Array has incorrect size.");
1412         return arr;
1413 }
1414
1415 void*
1416 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
1417 {
1418         MonoString *str;
1419         TLAB_ACCESS_INIT;
1420
1421         if (!SGEN_CAN_ALIGN_UP (size))
1422                 return NULL;
1423
1424 #ifndef DISABLE_CRITICAL_REGION
1425         ENTER_CRITICAL_REGION;
1426         str = sgen_try_alloc_obj_nolock ((GCVTable*)vtable, size);
1427         if (str) {
1428                 /*This doesn't require fencing since EXIT_CRITICAL_REGION already does it for us*/
1429                 str->length = len;
1430                 EXIT_CRITICAL_REGION;
1431                 return str;
1432         }
1433         EXIT_CRITICAL_REGION;
1434 #endif
1435
1436         LOCK_GC;
1437
1438         str = sgen_alloc_obj_nolock ((GCVTable*)vtable, size);
1439         if (G_UNLIKELY (!str)) {
1440                 UNLOCK_GC;
1441                 return mono_gc_out_of_memory (size);
1442         }
1443
1444         str->length = len;
1445
1446         UNLOCK_GC;
1447
1448         return str;
1449 }
1450
1451 /*
1452  * Strings
1453  */
1454
1455 void
1456 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1457 {
1458         mono_unichar2 *new_end = str->chars + new_length;
1459
1460         /* zero the discarded string. This null-delimits the string and allows
1461          * the space to be reclaimed by SGen. */
1462
1463         if (nursery_canaries_enabled () && sgen_ptr_in_nursery (str)) {
1464                 CHECK_CANARY_FOR_OBJECT (str);
1465                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2) + CANARY_SIZE);
1466                 memcpy (new_end + 1 , CANARY_STRING, CANARY_SIZE);
1467         } else {
1468                 memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1469         }
1470
1471         str->length = new_length;
1472 }
1473
1474 /*
1475  * Profiling
1476  */
1477
1478 #define GC_ROOT_NUM 32
1479 typedef struct {
1480         int count;              /* must be the first field */
1481         void *objects [GC_ROOT_NUM];
1482         int root_types [GC_ROOT_NUM];
1483         uintptr_t extra_info [GC_ROOT_NUM];
1484 } GCRootReport;
1485
1486 static void
1487 notify_gc_roots (GCRootReport *report)
1488 {
1489         if (!report->count)
1490                 return;
1491         mono_profiler_gc_roots (report->count, report->objects, report->root_types, report->extra_info);
1492         report->count = 0;
1493 }
1494
1495 static void
1496 add_profile_gc_root (GCRootReport *report, void *object, int rtype, uintptr_t extra_info)
1497 {
1498         if (report->count == GC_ROOT_NUM)
1499                 notify_gc_roots (report);
1500         report->objects [report->count] = object;
1501         report->root_types [report->count] = rtype;
1502         report->extra_info [report->count++] = (uintptr_t)((MonoVTable*)SGEN_LOAD_VTABLE (object))->klass;
1503 }
1504
1505 void
1506 sgen_client_nursery_objects_pinned (void **definitely_pinned, int count)
1507 {
1508         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS) {
1509                 GCRootReport report;
1510                 int idx;
1511                 report.count = 0;
1512                 for (idx = 0; idx < count; ++idx)
1513                         add_profile_gc_root (&report, definitely_pinned [idx], MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
1514                 notify_gc_roots (&report);
1515         }
1516 }
1517
1518 static void
1519 report_finalizer_roots_from_queue (SgenPointerQueue *queue)
1520 {
1521         GCRootReport report;
1522         size_t i;
1523
1524         report.count = 0;
1525         for (i = 0; i < queue->next_slot; ++i) {
1526                 void *obj = queue->data [i];
1527                 if (!obj)
1528                         continue;
1529                 add_profile_gc_root (&report, obj, MONO_PROFILE_GC_ROOT_FINALIZER, 0);
1530         }
1531         notify_gc_roots (&report);
1532 }
1533
1534 static void
1535 report_finalizer_roots (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
1536 {
1537         report_finalizer_roots_from_queue (fin_ready_queue);
1538         report_finalizer_roots_from_queue (critical_fin_queue);
1539 }
1540
1541 static GCRootReport *root_report;
1542
1543 static void
1544 single_arg_report_root (void **obj, void *gc_data)
1545 {
1546         if (*obj)
1547                 add_profile_gc_root (root_report, *obj, MONO_PROFILE_GC_ROOT_OTHER, 0);
1548 }
1549
1550 static void
1551 precisely_report_roots_from (GCRootReport *report, void** start_root, void** end_root, mword desc)
1552 {
1553         switch (desc & ROOT_DESC_TYPE_MASK) {
1554         case ROOT_DESC_BITMAP:
1555                 desc >>= ROOT_DESC_TYPE_SHIFT;
1556                 while (desc) {
1557                         if ((desc & 1) && *start_root) {
1558                                 add_profile_gc_root (report, *start_root, MONO_PROFILE_GC_ROOT_OTHER, 0);
1559                         }
1560                         desc >>= 1;
1561                         start_root++;
1562                 }
1563                 return;
1564         case ROOT_DESC_COMPLEX: {
1565                 gsize *bitmap_data = sgen_get_complex_descriptor_bitmap (desc);
1566                 gsize bwords = (*bitmap_data) - 1;
1567                 void **start_run = start_root;
1568                 bitmap_data++;
1569                 while (bwords-- > 0) {
1570                         gsize bmap = *bitmap_data++;
1571                         void **objptr = start_run;
1572                         while (bmap) {
1573                                 if ((bmap & 1) && *objptr) {
1574                                         add_profile_gc_root (report, *objptr, MONO_PROFILE_GC_ROOT_OTHER, 0);
1575                                 }
1576                                 bmap >>= 1;
1577                                 ++objptr;
1578                         }
1579                         start_run += GC_BITS_PER_WORD;
1580                 }
1581                 break;
1582         }
1583         case ROOT_DESC_USER: {
1584                 MonoGCRootMarkFunc marker = sgen_get_user_descriptor_func (desc);
1585                 root_report = report;
1586                 marker (start_root, single_arg_report_root, NULL);
1587                 break;
1588         }
1589         case ROOT_DESC_RUN_LEN:
1590                 g_assert_not_reached ();
1591         default:
1592                 g_assert_not_reached ();
1593         }
1594 }
1595
1596 static void
1597 report_registered_roots_by_type (int root_type)
1598 {
1599         GCRootReport report;
1600         void **start_root;
1601         RootRecord *root;
1602         report.count = 0;
1603         SGEN_HASH_TABLE_FOREACH (&roots_hash [root_type], start_root, root) {
1604                 SGEN_LOG (6, "Precise root scan %p-%p (desc: %p)", start_root, root->end_root, (void*)root->root_desc);
1605                 precisely_report_roots_from (&report, start_root, (void**)root->end_root, root->root_desc);
1606         } SGEN_HASH_TABLE_FOREACH_END;
1607         notify_gc_roots (&report);
1608 }
1609
1610 static void
1611 report_registered_roots (void)
1612 {
1613         report_registered_roots_by_type (ROOT_TYPE_NORMAL);
1614         report_registered_roots_by_type (ROOT_TYPE_WBARRIER);
1615 }
1616
1617 void
1618 sgen_client_collecting_minor (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
1619 {
1620         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
1621                 report_registered_roots ();
1622         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
1623                 report_finalizer_roots (fin_ready_queue, critical_fin_queue);
1624 }
1625
1626 static GCRootReport major_root_report;
1627 static gboolean profile_roots;
1628
1629 void
1630 sgen_client_collecting_major_1 (void)
1631 {
1632         profile_roots = mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS;
1633         memset (&major_root_report, 0, sizeof (GCRootReport));
1634 }
1635
1636 void
1637 sgen_client_pinned_los_object (char *obj)
1638 {
1639         if (profile_roots)
1640                 add_profile_gc_root (&major_root_report, obj, MONO_PROFILE_GC_ROOT_PINNING | MONO_PROFILE_GC_ROOT_MISC, 0);
1641 }
1642
1643 void
1644 sgen_client_collecting_major_2 (void)
1645 {
1646         if (profile_roots)
1647                 notify_gc_roots (&major_root_report);
1648
1649         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
1650                 report_registered_roots ();
1651 }
1652
1653 void
1654 sgen_client_collecting_major_3 (SgenPointerQueue *fin_ready_queue, SgenPointerQueue *critical_fin_queue)
1655 {
1656         if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
1657                 report_finalizer_roots (fin_ready_queue, critical_fin_queue);
1658 }
1659
1660 /*
1661  * Debugging
1662  */
1663
1664 const char*
1665 sgen_client_description_for_internal_mem_type (int type)
1666 {
1667         switch (type) {
1668         case INTERNAL_MEM_EPHEMERON_LINK: return "ephemeron-link";
1669         default:
1670                 return NULL;
1671         }
1672 }
1673
1674 void
1675 sgen_client_pre_collection_checks (void)
1676 {
1677         if (sgen_mono_xdomain_checks) {
1678                 sgen_clear_nursery_fragments ();
1679                 sgen_check_for_xdomain_refs ();
1680         }
1681 }
1682
1683 const char*
1684 sgen_client_object_safe_name (GCObject *obj)
1685 {
1686         MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
1687         return vt->klass->name;
1688 }
1689
1690 const char*
1691 sgen_client_vtable_get_namespace (GCVTable *gc_vtable)
1692 {
1693         MonoVTable *vt = (MonoVTable*)gc_vtable;
1694         return vt->klass->name_space;
1695 }
1696
1697 const char*
1698 sgen_client_vtable_get_name (GCVTable *gc_vtable)
1699 {
1700         MonoVTable *vt = (MonoVTable*)gc_vtable;
1701         return vt->klass->name;
1702 }
1703
1704 /*
1705  * Initialization
1706  */
1707
1708 void
1709 sgen_client_init (void)
1710 {
1711         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_EPHEMERON_LINK, sizeof (EphemeronLinkNode));
1712 }
1713
1714 gboolean
1715 sgen_client_handle_gc_debug (const char *opt)
1716 {
1717         if (!strcmp (opt, "xdomain-checks")) {
1718                 sgen_mono_xdomain_checks = TRUE;
1719         } else {
1720                 return FALSE;
1721         }
1722         return TRUE;
1723 }
1724
1725 void
1726 sgen_client_print_gc_debug_usage (void)
1727 {
1728         fprintf (stderr, "  xdomain-checks\n");
1729 }
1730
1731 #endif