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