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