Merge branch 'master' into msbuilddll2
[mono.git] / mono / metadata / boehm-gc.c
1 /*
2  * boehm-gc.c: GC implementation using either the installed or included Boehm GC.
3  *
4  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
5  * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
6  * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
7  */
8
9 #include "config.h"
10
11 #include <string.h>
12
13 #define GC_I_HIDE_POINTERS
14 #include <mono/metadata/gc-internal.h>
15 #include <mono/metadata/mono-gc.h>
16 #include <mono/metadata/profiler-private.h>
17 #include <mono/metadata/class-internals.h>
18 #include <mono/metadata/method-builder.h>
19 #include <mono/metadata/opcodes.h>
20 #include <mono/metadata/domain-internals.h>
21 #include <mono/metadata/metadata-internals.h>
22 #include <mono/metadata/marshal.h>
23 #include <mono/metadata/runtime.h>
24 #include <mono/utils/atomic.h>
25 #include <mono/utils/mono-logger-internal.h>
26 #include <mono/utils/mono-memory-model.h>
27 #include <mono/utils/mono-time.h>
28 #include <mono/utils/mono-threads.h>
29 #include <mono/utils/dtrace.h>
30 #include <mono/utils/gc_wrapper.h>
31 #include <mono/utils/mono-mutex.h>
32
33 #if HAVE_BOEHM_GC
34
35 #ifdef USE_INCLUDED_LIBGC
36 #undef TRUE
37 #undef FALSE
38 #define THREAD_LOCAL_ALLOC 1
39 #include "private/pthread_support.h"
40 #endif
41
42 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
43 void *pthread_get_stackaddr_np(pthread_t);
44 #endif
45
46 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
47 /*Boehm max heap cannot be smaller than 16MB*/
48 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
49 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
50
51 static gboolean gc_initialized = FALSE;
52 static mono_mutex_t mono_gc_lock;
53
54 static void*
55 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
56
57 static void
58 mono_gc_warning (char *msg, GC_word arg)
59 {
60         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
61 }
62
63 void
64 mono_gc_base_init (void)
65 {
66         MonoThreadInfoCallbacks cb;
67         const char *env;
68
69         if (gc_initialized)
70                 return;
71
72         /*
73          * Handle the case when we are called from a thread different from the main thread,
74          * confusing libgc.
75          * FIXME: Move this to libgc where it belongs.
76          *
77          * we used to do this only when running on valgrind,
78          * but it happens also in other setups.
79          */
80 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
81         {
82                 size_t size;
83                 void *sstart;
84                 pthread_attr_t attr;
85                 pthread_getattr_np (pthread_self (), &attr);
86                 pthread_attr_getstack (&attr, &sstart, &size);
87                 pthread_attr_destroy (&attr); 
88                 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
89 #ifdef __ia64__
90                 /*
91                  * The calculation above doesn't seem to work on ia64, also we need to set
92                  * GC_register_stackbottom as well, but don't know how.
93                  */
94 #else
95                 /* apparently with some linuxthreads implementations sstart can be NULL,
96                  * fallback to the more imprecise method (bug# 78096).
97                  */
98                 if (sstart) {
99                         GC_stackbottom = (char*)sstart + size;
100                 } else {
101                         int dummy;
102                         gsize stack_bottom = (gsize)&dummy;
103                         stack_bottom += 4095;
104                         stack_bottom &= ~4095;
105                         GC_stackbottom = (char*)stack_bottom;
106                 }
107 #endif
108         }
109 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
110                 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
111 #elif defined(__OpenBSD__)
112 #  include <pthread_np.h>
113         {
114                 stack_t ss;
115                 int rslt;
116
117                 rslt = pthread_stackseg_np(pthread_self(), &ss);
118                 g_assert (rslt == 0);
119
120                 GC_stackbottom = (char*)ss.ss_sp;
121         }
122 #elif defined(__native_client__)
123         /* Do nothing, GC_stackbottom is set correctly in libgc */
124 #else
125         {
126                 int dummy;
127                 gsize stack_bottom = (gsize)&dummy;
128                 stack_bottom += 4095;
129                 stack_bottom &= ~4095;
130                 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
131                 GC_stackbottom = (char*)stack_bottom;
132         }
133 #endif
134
135 #if !defined(PLATFORM_ANDROID)
136         /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
137         GC_no_dls = TRUE;
138 #endif
139         GC_init ();
140         GC_oom_fn = mono_gc_out_of_memory;
141         GC_set_warn_proc (mono_gc_warning);
142         GC_finalize_on_demand = 1;
143         GC_finalizer_notifier = mono_gc_finalize_notify;
144
145 #ifdef HAVE_GC_GCJ_MALLOC
146         GC_init_gcj_malloc (5, NULL);
147 #endif
148
149 #ifdef HAVE_GC_ALLOW_REGISTER_THREADS
150         GC_allow_register_threads();
151 #endif
152
153         if ((env = g_getenv ("MONO_GC_PARAMS"))) {
154                 char **ptr, **opts = g_strsplit (env, ",", -1);
155                 for (ptr = opts; *ptr; ++ptr) {
156                         char *opt = *ptr;
157                         if (g_str_has_prefix (opt, "max-heap-size=")) {
158                                 glong max_heap;
159
160                                 opt = strchr (opt, '=') + 1;
161                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
162                                         if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
163                                                 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
164                                                 exit (1);
165                                         }
166                                         GC_set_max_heap_size (max_heap);
167                                 } else {
168                                         fprintf (stderr, "max-heap-size must be an integer.\n");
169                                         exit (1);
170                                 }
171                                 continue;
172                         } else {
173                                 /* Could be a parameter for sgen */
174                                 /*
175                                 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
176                                 fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
177                                 exit (1);
178                                 */
179                         }
180                 }
181                 g_strfreev (opts);
182         }
183
184         memset (&cb, 0, sizeof (cb));
185         cb.thread_register = boehm_thread_register;
186         cb.mono_method_is_critical = (gpointer)mono_runtime_is_critical_method;
187 #ifndef HOST_WIN32
188         cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
189 #endif
190         
191         mono_threads_init (&cb, sizeof (MonoThreadInfo));
192         mono_mutex_init (&mono_gc_lock);
193
194         mono_gc_enable_events ();
195         gc_initialized = TRUE;
196 }
197
198 /**
199  * mono_gc_collect:
200  * @generation: GC generation identifier
201  *
202  * Perform a garbage collection for the given generation, higher numbers
203  * mean usually older objects. Collecting a high-numbered generation
204  * implies collecting also the lower-numbered generations.
205  * The maximum value for @generation can be retrieved with a call to
206  * mono_gc_max_generation(), so this function is usually called as:
207  *
208  *      mono_gc_collect (mono_gc_max_generation ());
209  */
210 void
211 mono_gc_collect (int generation)
212 {
213 #ifndef DISABLE_PERFCOUNTERS
214         mono_perfcounters->gc_induced++;
215 #endif
216         GC_gcollect ();
217 }
218
219 /**
220  * mono_gc_max_generation:
221  *
222  * Get the maximum generation number used by the current garbage
223  * collector. The value will be 0 for the Boehm collector, 1 or more
224  * for the generational collectors.
225  *
226  * Returns: the maximum generation number.
227  */
228 int
229 mono_gc_max_generation (void)
230 {
231         return 0;
232 }
233
234 /**
235  * mono_gc_get_generation:
236  * @object: a managed object
237  *
238  * Get the garbage collector's generation that @object belongs to.
239  * Use this has a hint only.
240  *
241  * Returns: a garbage collector generation number
242  */
243 int
244 mono_gc_get_generation  (MonoObject *object)
245 {
246         return 0;
247 }
248
249 /**
250  * mono_gc_collection_count:
251  * @generation: a GC generation number
252  *
253  * Get how many times a garbage collection has been performed
254  * for the given @generation number.
255  *
256  * Returns: the number of garbage collections
257  */
258 int
259 mono_gc_collection_count (int generation)
260 {
261         return GC_gc_no;
262 }
263
264 /**
265  * mono_gc_add_memory_pressure:
266  * @value: amount of bytes
267  *
268  * Adjust the garbage collector's view of how many bytes of memory
269  * are indirectly referenced by managed objects (for example unmanaged
270  * memory holding image or other binary data).
271  * This is a hint only to the garbage collector algorithm.
272  * Note that negative amounts of @value will decrease the memory
273  * pressure.
274  */
275 void
276 mono_gc_add_memory_pressure (gint64 value)
277 {
278 }
279
280 /**
281  * mono_gc_get_used_size:
282  *
283  * Get the approximate amount of memory used by managed objects.
284  *
285  * Returns: the amount of memory used in bytes
286  */
287 int64_t
288 mono_gc_get_used_size (void)
289 {
290         return GC_get_heap_size () - GC_get_free_bytes ();
291 }
292
293 /**
294  * mono_gc_get_heap_size:
295  *
296  * Get the amount of memory used by the garbage collector.
297  *
298  * Returns: the size of the heap in bytes
299  */
300 int64_t
301 mono_gc_get_heap_size (void)
302 {
303         return GC_get_heap_size ();
304 }
305
306 gboolean
307 mono_gc_is_gc_thread (void)
308 {
309 #if GC_VERSION_MAJOR >= 7
310         return TRUE;
311 #elif defined(USE_INCLUDED_LIBGC)
312         return GC_thread_is_registered ();
313 #else
314         return TRUE;
315 #endif
316 }
317
318 extern int GC_thread_register_foreign (void *base_addr);
319
320 gboolean
321 mono_gc_register_thread (void *baseptr)
322 {
323         return mono_thread_info_attach (baseptr) != NULL;
324 }
325
326 static void*
327 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
328 {
329 #if GC_VERSION_MAJOR >= 7
330         struct GC_stack_base sb;
331         int res;
332
333         res = GC_get_stack_base (&sb);
334         if (res != GC_SUCCESS) {
335                 sb.mem_base = baseptr;
336 #ifdef __ia64__
337                 /* Can't determine the register stack bounds */
338                 g_error ("mono_gc_register_thread failed ().\n");
339 #endif
340         }
341         res = GC_register_my_thread (&sb);
342         if ((res != GC_SUCCESS) && (res != GC_DUPLICATE)) {
343                 g_warning ("GC_register_my_thread () failed.\n");
344                 return NULL;
345         }
346         return info;
347 #else
348         if (mono_gc_is_gc_thread())
349                 return info;
350 #if defined(USE_INCLUDED_LIBGC) && !defined(HOST_WIN32)
351         return GC_thread_register_foreign (baseptr) ? info : NULL;
352 #else
353         return NULL;
354 #endif
355 #endif
356 }
357
358 gboolean
359 mono_object_is_alive (MonoObject* o)
360 {
361 #ifdef USE_INCLUDED_LIBGC
362         return GC_is_marked ((gpointer)o);
363 #else
364         return TRUE;
365 #endif
366 }
367
368 int
369 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
370 {
371         return 1;
372 }
373
374 #ifdef USE_INCLUDED_LIBGC
375
376 static gint64 gc_start_time;
377
378 static void
379 on_gc_notification (GCEventType event)
380 {
381         MonoGCEvent e = (MonoGCEvent)event;
382
383         switch (e) {
384         case MONO_GC_EVENT_PRE_STOP_WORLD:
385                 MONO_GC_WORLD_STOP_BEGIN ();
386                 mono_thread_info_suspend_lock ();
387                 break;
388
389         case MONO_GC_EVENT_POST_STOP_WORLD:
390                 MONO_GC_WORLD_STOP_END ();
391                 break;
392
393         case MONO_GC_EVENT_PRE_START_WORLD:
394                 MONO_GC_WORLD_RESTART_BEGIN (1);
395                 break;
396
397         case MONO_GC_EVENT_POST_START_WORLD:
398                 MONO_GC_WORLD_RESTART_END (1);
399                 mono_thread_info_suspend_unlock ();
400                 break;
401
402         case MONO_GC_EVENT_START:
403                 MONO_GC_BEGIN (1);
404 #ifndef DISABLE_PERFCOUNTERS
405                 if (mono_perfcounters)
406                         mono_perfcounters->gc_collections0++;
407 #endif
408                 gc_stats.major_gc_count ++;
409                 gc_start_time = mono_100ns_ticks ();
410                 break;
411
412         case MONO_GC_EVENT_END:
413                 MONO_GC_END (1);
414 #if defined(ENABLE_DTRACE) && defined(__sun__)
415                 /* This works around a dtrace -G problem on Solaris.
416                    Limit its actual use to when the probe is enabled. */
417                 if (MONO_GC_END_ENABLED ())
418                         sleep(0);
419 #endif
420
421 #ifndef DISABLE_PERFCOUNTERS
422                 if (mono_perfcounters) {
423                         guint64 heap_size = GC_get_heap_size ();
424                         guint64 used_size = heap_size - GC_get_free_bytes ();
425                         mono_perfcounters->gc_total_bytes = used_size;
426                         mono_perfcounters->gc_committed_bytes = heap_size;
427                         mono_perfcounters->gc_reserved_bytes = heap_size;
428                         mono_perfcounters->gc_gen0size = heap_size;
429                 }
430 #endif
431                 gc_stats.major_gc_time_usecs += (mono_100ns_ticks () - gc_start_time) / 10;
432                 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
433                 break;
434         }
435
436         mono_profiler_gc_event (e, 0);
437 }
438  
439 static void
440 on_gc_heap_resize (size_t new_size)
441 {
442         guint64 heap_size = GC_get_heap_size ();
443 #ifndef DISABLE_PERFCOUNTERS
444         if (mono_perfcounters) {
445                 mono_perfcounters->gc_committed_bytes = heap_size;
446                 mono_perfcounters->gc_reserved_bytes = heap_size;
447                 mono_perfcounters->gc_gen0size = heap_size;
448         }
449 #endif
450         mono_profiler_gc_heap_resize (new_size);
451 }
452
453 void
454 mono_gc_enable_events (void)
455 {
456         GC_notify_event = on_gc_notification;
457         GC_on_heap_resize = on_gc_heap_resize;
458 }
459
460 #else
461
462 void
463 mono_gc_enable_events (void)
464 {
465 }
466
467 #endif
468
469 int
470 mono_gc_register_root (char *start, size_t size, void *descr)
471 {
472         /* for some strange reason, they want one extra byte on the end */
473         GC_add_roots (start, start + size + 1);
474
475         return TRUE;
476 }
477
478 void
479 mono_gc_deregister_root (char* addr)
480 {
481 #ifndef HOST_WIN32
482         /* FIXME: libgc doesn't define this work win32 for some reason */
483         /* FIXME: No size info */
484         GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
485 #endif
486 }
487
488 void
489 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
490 {
491         /* libgc requires that we use HIDE_POINTER... */
492         *link_addr = (void*)HIDE_POINTER (obj);
493         if (track)
494                 GC_REGISTER_LONG_LINK (link_addr, obj);
495         else
496                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
497 }
498
499 void
500 mono_gc_weak_link_remove (void **link_addr, gboolean track)
501 {
502         if (track)
503                 GC_unregister_long_link (link_addr);
504         else
505                 GC_unregister_disappearing_link (link_addr);
506         *link_addr = NULL;
507 }
508
509 static gpointer
510 reveal_link (gpointer link_addr)
511 {
512         void **link_a = link_addr;
513         return REVEAL_POINTER (*link_a);
514 }
515
516 MonoObject*
517 mono_gc_weak_link_get (void **link_addr)
518 {
519         MonoObject *obj = GC_call_with_alloc_lock (reveal_link, link_addr);
520         if (obj == (MonoObject *) -1)
521                 return NULL;
522         return obj;
523 }
524
525 void*
526 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
527 {
528         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
529 }
530
531 void*
532 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
533 {
534         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
535 }
536
537 void*
538 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
539 {
540         /* libgc has no usable support for arrays... */
541         return GC_NO_DESCRIPTOR;
542 }
543
544 void*
545 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
546 {
547 #ifdef HAVE_GC_GCJ_MALLOC
548         /* It seems there are issues when the bitmap doesn't fit: play it safe */
549         if (numbits >= 30)
550                 return GC_NO_DESCRIPTOR;
551         else
552                 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
553 #else
554         return NULL;
555 #endif
556 }
557
558 void*
559 mono_gc_make_root_descr_all_refs (int numbits)
560 {
561         return NULL;
562 }
563
564 void*
565 mono_gc_alloc_fixed (size_t size, void *descr)
566 {
567         /* To help track down typed allocation bugs */
568         /*
569         static int count;
570         count ++;
571         if (count == atoi (g_getenv ("COUNT2")))
572                 printf ("HIT!\n");
573         if (count > atoi (g_getenv ("COUNT2")))
574                 return GC_MALLOC (size);
575         */
576
577         if (descr)
578                 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
579         else
580                 return GC_MALLOC (size);
581 }
582
583 void
584 mono_gc_free_fixed (void* addr)
585 {
586 }
587
588 int
589 mono_gc_invoke_finalizers (void)
590 {
591         /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
592          * the 'mem_freed' variable is not initialized when there are no
593          * objects to finalize, which leads to strange behavior later on.
594          * The check is necessary to work around that bug.
595          */
596         if (GC_should_invoke_finalizers ())
597                 return GC_invoke_finalizers ();
598         return 0;
599 }
600
601 gboolean
602 mono_gc_pending_finalizers (void)
603 {
604         return GC_should_invoke_finalizers ();
605 }
606
607 void
608 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
609 {
610         *(void**)field_ptr = value;
611 }
612
613 void
614 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
615 {
616         *(void**)slot_ptr = value;
617 }
618
619 void
620 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
621 {
622         mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
623 }
624
625 void
626 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
627 {
628         *(void**)ptr = value;
629 }
630
631 void
632 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
633 {
634         InterlockedWritePointer (ptr, value);
635 }
636
637 void
638 mono_gc_wbarrier_generic_nostore (gpointer ptr)
639 {
640 }
641
642 void
643 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
644 {
645         mono_gc_memmove (dest, src, count * mono_class_value_size (klass, NULL));
646 }
647
648 void
649 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
650 {
651         /* do not copy the sync state */
652         mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
653                         mono_object_class (obj)->instance_size - sizeof (MonoObject));
654 }
655
656 void
657 mono_gc_clear_domain (MonoDomain *domain)
658 {
659 }
660
661 int
662 mono_gc_get_suspend_signal (void)
663 {
664 #ifdef USE_INCLUDED_GC
665         return GC_get_suspend_signal ();
666 #else
667         return -1;
668 #endif
669 }
670
671 #if defined(USE_INCLUDED_LIBGC) && defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
672 extern __thread MONO_TLS_FAST void* GC_thread_tls;
673 #include "metadata-internals.h"
674
675 static int
676 shift_amount (int v)
677 {
678         int i = 0;
679         while (!(v & (1 << i)))
680                 i++;
681         return i;
682 }
683
684 enum {
685         ATYPE_FREEPTR,
686         ATYPE_FREEPTR_FOR_BOX,
687         ATYPE_NORMAL,
688         ATYPE_GCJ,
689         ATYPE_STRING,
690         ATYPE_NUM
691 };
692
693 static MonoMethod*
694 create_allocator (int atype, int tls_key)
695 {
696         int index_var, bytes_var, my_fl_var, my_entry_var;
697         guint32 no_freelist_branch, not_small_enough_branch = 0;
698         guint32 size_overflow_branch = 0;
699         MonoMethodBuilder *mb;
700         MonoMethod *res;
701         MonoMethodSignature *csig;
702         AllocatorWrapperInfo *info;
703
704         if (atype == ATYPE_STRING) {
705                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
706                 csig->ret = &mono_defaults.string_class->byval_arg;
707                 csig->params [0] = &mono_defaults.int_class->byval_arg;
708                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
709         } else {
710                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
711                 csig->ret = &mono_defaults.object_class->byval_arg;
712                 csig->params [0] = &mono_defaults.int_class->byval_arg;
713         }
714
715         mb = mono_mb_new (mono_defaults.object_class, "Alloc", MONO_WRAPPER_ALLOC);
716         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
717         if (atype == ATYPE_STRING) {
718                 /* a string alloator method takes the args: (vtable, len) */
719                 /* bytes = (sizeof (MonoString) + ((len + 1) * 2)); */
720                 mono_mb_emit_ldarg (mb, 1);
721                 mono_mb_emit_icon (mb, 1);
722                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
723                 mono_mb_emit_icon (mb, 1);
724                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
725                 // sizeof (MonoString) might include padding
726                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
727                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
728                 mono_mb_emit_stloc (mb, bytes_var);
729         } else {
730                 /* bytes = vtable->klass->instance_size */
731                 mono_mb_emit_ldarg (mb, 0);
732                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
733                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
734                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
735                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
736                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
737                 /* FIXME: assert instance_size stays a 4 byte integer */
738                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_U4);
739                 mono_mb_emit_stloc (mb, bytes_var);
740         }
741
742         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
743         if (atype == ATYPE_STRING) {
744                 /* check for size */
745                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
746                 mono_mb_emit_ldloc (mb, bytes_var);
747                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
748                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
749                 /* check for overflow */
750                 mono_mb_emit_ldloc (mb, bytes_var);
751                 mono_mb_emit_icon (mb, sizeof (MonoString));
752                 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
753         }
754
755         /* int index = INDEX_FROM_BYTES(bytes); */
756         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
757         
758         mono_mb_emit_ldloc (mb, bytes_var);
759         mono_mb_emit_icon (mb, GRANULARITY - 1);
760         mono_mb_emit_byte (mb, MONO_CEE_ADD);
761         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
762         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
763         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
764         mono_mb_emit_byte (mb, MONO_CEE_SHL);
765         /* index var is already adjusted into bytes */
766         mono_mb_emit_stloc (mb, index_var);
767
768         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
769         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
770         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
771         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
772         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
773         mono_mb_emit_i4 (mb, tls_key);
774         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
775                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
776         else if (atype == ATYPE_NORMAL)
777                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
778         else if (atype == ATYPE_GCJ)
779                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
780         else
781                 g_assert_not_reached ();
782         mono_mb_emit_byte (mb, MONO_CEE_ADD);
783         mono_mb_emit_ldloc (mb, index_var);
784         mono_mb_emit_byte (mb, MONO_CEE_ADD);
785         mono_mb_emit_stloc (mb, my_fl_var);
786
787         /* my_entry = *my_fl; */
788         mono_mb_emit_ldloc (mb, my_fl_var);
789         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
790         mono_mb_emit_stloc (mb, my_entry_var);
791
792         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
793         mono_mb_emit_ldloc (mb, my_entry_var);
794         mono_mb_emit_icon (mb, HBLKSIZE);
795         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
796
797         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
798         mono_mb_emit_ldloc (mb, my_fl_var);
799         mono_mb_emit_ldloc (mb, my_entry_var);
800         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
801         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
802
803         /* set the vtable and clear the words in the object */
804         mono_mb_emit_ldloc (mb, my_entry_var);
805         mono_mb_emit_ldarg (mb, 0);
806         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
807
808         if (atype == ATYPE_FREEPTR) {
809                 int start_var, end_var, start_loop;
810                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
811                  */
812                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
813                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
814                 mono_mb_emit_ldloc (mb, my_entry_var);
815                 mono_mb_emit_ldloc (mb, bytes_var);
816                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
817                 mono_mb_emit_stloc (mb, end_var);
818                 mono_mb_emit_ldloc (mb, my_entry_var);
819                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
820                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
821                 mono_mb_emit_stloc (mb, start_var);
822                 /*
823                  * do {
824                  *      *start++ = NULL;
825                  * } while (start < end);
826                  */
827                 start_loop = mono_mb_get_label (mb);
828                 mono_mb_emit_ldloc (mb, start_var);
829                 mono_mb_emit_icon (mb, 0);
830                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
831                 mono_mb_emit_ldloc (mb, start_var);
832                 mono_mb_emit_icon (mb, sizeof (gpointer));
833                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
834                 mono_mb_emit_stloc (mb, start_var);
835
836                 mono_mb_emit_ldloc (mb, start_var);
837                 mono_mb_emit_ldloc (mb, end_var);
838                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
839                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
840         } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
841                 /* need to clear just the sync pointer */
842                 mono_mb_emit_ldloc (mb, my_entry_var);
843                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
844                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
845                 mono_mb_emit_icon (mb, 0);
846                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
847         }
848
849         if (atype == ATYPE_STRING) {
850                 /* need to set length and clear the last char */
851                 /* s->length = len; */
852                 mono_mb_emit_ldloc (mb, my_entry_var);
853                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
854                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
855                 mono_mb_emit_ldarg (mb, 1);
856                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
857                 /* s->chars [len] = 0; */
858                 mono_mb_emit_ldloc (mb, my_entry_var);
859                 mono_mb_emit_ldloc (mb, bytes_var);
860                 mono_mb_emit_icon (mb, 2);
861                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
862                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
863                 mono_mb_emit_icon (mb, 0);
864                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
865         }
866
867         /* return my_entry; */
868         mono_mb_emit_ldloc (mb, my_entry_var);
869         mono_mb_emit_byte (mb, MONO_CEE_RET);
870         
871         mono_mb_patch_short_branch (mb, no_freelist_branch);
872         if (not_small_enough_branch > 0)
873                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
874         if (size_overflow_branch > 0)
875                 mono_mb_patch_short_branch (mb, size_overflow_branch);
876         /* the slow path: we just call back into the runtime */
877         if (atype == ATYPE_STRING) {
878                 mono_mb_emit_ldarg (mb, 1);
879                 mono_mb_emit_icall (mb, mono_string_alloc);
880         } else {
881                 mono_mb_emit_ldarg (mb, 0);
882                 mono_mb_emit_icall (mb, mono_object_new_specific);
883         }
884
885         mono_mb_emit_byte (mb, MONO_CEE_RET);
886
887         res = mono_mb_create_method (mb, csig, 8);
888         mono_mb_free (mb);
889         mono_method_get_header (res)->init_locals = FALSE;
890
891         info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
892         info->gc_name = "boehm";
893         info->alloc_type = atype;
894         mono_marshal_set_wrapper_info (res, info);
895
896         return res;
897 }
898
899 static MonoMethod* alloc_method_cache [ATYPE_NUM];
900
901 static G_GNUC_UNUSED gboolean
902 mono_gc_is_critical_method (MonoMethod *method)
903 {
904         int i;
905
906         for (i = 0; i < ATYPE_NUM; ++i)
907                 if (method == alloc_method_cache [i])
908                         return TRUE;
909
910         return FALSE;
911 }
912
913 /*
914  * If possible, generate a managed method that can quickly allocate objects in class
915  * @klass. The method will typically have an thread-local inline allocation sequence.
916  * The signature of the called method is:
917  *      object allocate (MonoVTable *vtable)
918  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
919  * keep in sync.
920  * The thread local alloc logic is taken from libgc/pthread_support.c.
921  */
922
923 MonoMethod*
924 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box)
925 {
926         int offset = -1;
927         int atype;
928         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
929
930         /*g_print ("thread tls: %d\n", offset);*/
931         if (offset == -1)
932                 return NULL;
933         if (!SMALL_ENOUGH (klass->instance_size))
934                 return NULL;
935         if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
936                 return NULL;
937         if (klass->rank)
938                 return NULL;
939         if (mono_class_is_open_constructed_type (&klass->byval_arg))
940                 return NULL;
941         if (klass->byval_arg.type == MONO_TYPE_STRING) {
942                 atype = ATYPE_STRING;
943         } else if (!klass->has_references) {
944                 if (for_box)
945                         atype = ATYPE_FREEPTR_FOR_BOX;
946                 else
947                         atype = ATYPE_FREEPTR;
948         } else {
949                 return NULL;
950                 /*
951                  * disabled because we currently do a runtime choice anyway, to
952                  * deal with multiple appdomains.
953                 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
954                         atype = ATYPE_GCJ;
955                 else
956                         atype = ATYPE_NORMAL;
957                 */
958         }
959         return mono_gc_get_managed_allocator_by_type (atype);
960 }
961
962 MonoMethod*
963 mono_gc_get_managed_array_allocator (MonoClass *klass)
964 {
965         return NULL;
966 }
967
968 /**
969  * mono_gc_get_managed_allocator_by_type:
970  *
971  *   Return a managed allocator method corresponding to allocator type ATYPE.
972  */
973 MonoMethod*
974 mono_gc_get_managed_allocator_by_type (int atype)
975 {
976         int offset = -1;
977         MonoMethod *res;
978         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
979
980         mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
981
982         res = alloc_method_cache [atype];
983         if (res)
984                 return res;
985
986         res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD);
987         mono_mutex_lock (&mono_gc_lock);
988         if (alloc_method_cache [atype]) {
989                 mono_free_method (res);
990                 res = alloc_method_cache [atype];
991         } else {
992                 mono_memory_barrier ();
993                 alloc_method_cache [atype] = res;
994         }
995         mono_mutex_unlock (&mono_gc_lock);
996         return res;
997 }
998
999 guint32
1000 mono_gc_get_managed_allocator_types (void)
1001 {
1002         return ATYPE_NUM;
1003 }
1004
1005 MonoMethod*
1006 mono_gc_get_write_barrier (void)
1007 {
1008         g_assert_not_reached ();
1009         return NULL;
1010 }
1011
1012 #else
1013
1014 static G_GNUC_UNUSED gboolean
1015 mono_gc_is_critical_method (MonoMethod *method)
1016 {
1017         return FALSE;
1018 }
1019
1020 MonoMethod*
1021 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box)
1022 {
1023         return NULL;
1024 }
1025
1026 MonoMethod*
1027 mono_gc_get_managed_array_allocator (MonoClass *klass)
1028 {
1029         return NULL;
1030 }
1031
1032 MonoMethod*
1033 mono_gc_get_managed_allocator_by_type (int atype)
1034 {
1035         return NULL;
1036 }
1037
1038 guint32
1039 mono_gc_get_managed_allocator_types (void)
1040 {
1041         return 0;
1042 }
1043
1044 MonoMethod*
1045 mono_gc_get_write_barrier (void)
1046 {
1047         g_assert_not_reached ();
1048         return NULL;
1049 }
1050
1051 #endif
1052
1053 const char *
1054 mono_gc_get_gc_name (void)
1055 {
1056         return "boehm";
1057 }
1058
1059 void*
1060 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1061 {
1062         return GC_call_with_alloc_lock (func, data);
1063 }
1064
1065 char*
1066 mono_gc_get_description (void)
1067 {
1068         return g_strdup (DEFAULT_GC_NAME);
1069 }
1070
1071 void
1072 mono_gc_set_desktop_mode (void)
1073 {
1074         GC_dont_expand = 1;
1075 }
1076
1077 gboolean
1078 mono_gc_is_moving (void)
1079 {
1080         return FALSE;
1081 }
1082
1083 gboolean
1084 mono_gc_is_disabled (void)
1085 {
1086         if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1087                 return TRUE;
1088         else
1089                 return FALSE;
1090 }
1091
1092 void
1093 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1094 {
1095         g_assert_not_reached ();
1096 }
1097
1098
1099 guint8*
1100 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1101 {
1102         g_assert_not_reached ();
1103         return NULL;
1104 }
1105
1106 gboolean
1107 mono_gc_card_table_nursery_check (void)
1108 {
1109         g_assert_not_reached ();
1110         return TRUE;
1111 }
1112
1113 void*
1114 mono_gc_get_nursery (int *shift_bits, size_t *size)
1115 {
1116         return NULL;
1117 }
1118
1119 void
1120 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1121 {
1122 }
1123
1124 gboolean
1125 mono_gc_precise_stack_mark_enabled (void)
1126 {
1127         return FALSE;
1128 }
1129
1130 FILE *
1131 mono_gc_get_logfile (void)
1132 {
1133         return NULL;
1134 }
1135
1136 void
1137 mono_gc_conservatively_scan_area (void *start, void *end)
1138 {
1139         g_assert_not_reached ();
1140 }
1141
1142 void *
1143 mono_gc_scan_object (void *obj)
1144 {
1145         g_assert_not_reached ();
1146         return NULL;
1147 }
1148
1149 gsize*
1150 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1151 {
1152         g_assert_not_reached ();
1153         return NULL;
1154 }
1155
1156 void
1157 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1158 {
1159 }
1160
1161 void
1162 mono_gc_set_stack_end (void *stack_end)
1163 {
1164 }
1165
1166 void mono_gc_set_skip_thread (gboolean value)
1167 {
1168 }
1169
1170 void
1171 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1172 {
1173         guint offset = 0;
1174
1175 #ifndef GC_DEBUG
1176         /* This assertion is not valid when GC_DEBUG is defined */
1177         g_assert (GC_base (obj) == (char*)obj - offset);
1178 #endif
1179
1180         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1181 }
1182
1183 /*
1184  * These will call the redefined versions in libgc.
1185  */
1186
1187 #ifndef HOST_WIN32
1188
1189 int
1190 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1191 {
1192         return pthread_create (new_thread, attr, start_routine, arg);
1193 }
1194
1195 int
1196 mono_gc_pthread_join (pthread_t thread, void **retval)
1197 {
1198         return pthread_join (thread, retval);
1199 }
1200
1201 int
1202 mono_gc_pthread_detach (pthread_t thread)
1203 {
1204         return pthread_detach (thread);
1205 }
1206
1207 void
1208 mono_gc_pthread_exit (void *retval)
1209 {
1210         pthread_exit (retval);
1211 }
1212
1213 #endif
1214
1215 #ifdef HOST_WIN32
1216 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1217 {
1218 #ifdef USE_INCLUDED_LIBGC
1219         return GC_DllMain (module_handle, reason, reserved);
1220 #else
1221         return TRUE;
1222 #endif
1223 }
1224 #endif
1225
1226 guint
1227 mono_gc_get_vtable_bits (MonoClass *class)
1228 {
1229         return 0;
1230 }
1231
1232 /*
1233  * mono_gc_register_altstack:
1234  *
1235  *   Register the dimensions of the normal stack and altstack with the collector.
1236  * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1237  */
1238 void
1239 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1240 {
1241 #ifdef USE_INCLUDED_LIBGC
1242         GC_register_altstack (stack, stack_size, altstack, altstack_size);
1243 #endif
1244 }
1245
1246 int
1247 mono_gc_get_los_limit (void)
1248 {
1249         return G_MAXINT;
1250 }
1251
1252 gboolean
1253 mono_gc_user_markers_supported (void)
1254 {
1255         return FALSE;
1256 }
1257
1258 void *
1259 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1260 {
1261         g_assert_not_reached ();
1262         return NULL;
1263 }
1264
1265 gboolean
1266 mono_gc_set_allow_synchronous_major (gboolean flag)
1267 {
1268         return flag;
1269 }
1270
1271 #endif /* no Boehm GC */