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