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