Thu Sep 13 11:55:55 CEST 2007 Paolo Molaro <lupus@ximian.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  */
5
6 #include "config.h"
7 #define GC_I_HIDE_POINTERS
8 #include <mono/os/gc_wrapper.h>
9 #include <mono/metadata/mono-gc.h>
10 #include <mono/metadata/gc-internal.h>
11 #include <mono/metadata/profiler-private.h>
12 #include <mono/metadata/class-internals.h>
13 #include <mono/metadata/marshal.h>
14 #include <mono/metadata/opcodes.h>
15 #include <mono/utils/mono-logger.h>
16
17 #if HAVE_BOEHM_GC
18
19 #ifdef USE_INCLUDED_LIBGC
20 #undef TRUE
21 #undef FALSE
22 #define THREAD_LOCAL_ALLOC 1
23 #include "private/pthread_support.h"
24 #endif
25
26 static void
27 mono_gc_warning (char *msg, GC_word arg)
28 {
29         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
30 }
31
32 void
33 mono_gc_base_init (void)
34 {
35         GC_no_dls = TRUE;
36         GC_oom_fn = mono_gc_out_of_memory;
37         GC_set_warn_proc (mono_gc_warning);
38         GC_finalize_on_demand = 1;
39         GC_finalizer_notifier = mono_gc_finalize_notify;
40 }
41
42 void
43 mono_gc_collect (int generation)
44 {
45         GC_gcollect ();
46 }
47
48 int
49 mono_gc_max_generation (void)
50 {
51         return 0;
52 }
53
54 int
55 mono_gc_get_generation  (MonoObject *object)
56 {
57         return 0;
58 }
59
60 int
61 mono_gc_collection_count (int generation)
62 {
63         return GC_gc_no;
64 }
65
66 void
67 mono_gc_add_memory_pressure (gint64 value)
68 {
69 }
70
71 gint64
72 mono_gc_get_used_size (void)
73 {
74         return GC_get_heap_size () - GC_get_free_bytes ();
75 }
76
77 gint64
78 mono_gc_get_heap_size (void)
79 {
80         return GC_get_heap_size ();
81 }
82
83 void
84 mono_gc_disable (void)
85 {
86 #ifdef HAVE_GC_ENABLE
87         GC_disable ();
88 #else
89         g_assert_not_reached ();
90 #endif
91 }
92
93 void
94 mono_gc_enable (void)
95 {
96 #ifdef HAVE_GC_ENABLE
97         GC_enable ();
98 #else
99         g_assert_not_reached ();
100 #endif
101 }
102
103 gboolean
104 mono_gc_is_gc_thread (void)
105 {
106 #ifdef USE_INCLUDED_LIBGC
107         return GC_thread_is_registered ();
108 #else
109         return TRUE;
110 #endif
111 }
112
113 extern int GC_thread_register_foreign (void *base_addr);
114
115 gboolean
116 mono_gc_register_thread (void *baseptr)
117 {
118         if (mono_gc_is_gc_thread())
119                 return TRUE;
120 #if defined(USE_INCLUDED_LIBGC) && !defined(PLATFORM_WIN32)
121         return GC_thread_register_foreign (baseptr);
122 #else
123         return FALSE;
124 #endif
125 }
126
127 gboolean
128 mono_object_is_alive (MonoObject* o)
129 {
130 #ifdef USE_INCLUDED_LIBGC
131         return GC_is_marked (o);
132 #else
133         return TRUE;
134 #endif
135 }
136
137 #ifdef USE_INCLUDED_LIBGC
138
139 static void
140 on_gc_notification (GCEventType event)
141 {
142         mono_profiler_gc_event ((MonoGCEvent) event, 0);
143 }
144  
145 static void
146 on_gc_heap_resize (size_t new_size)
147 {
148         mono_profiler_gc_heap_resize (new_size);
149 }
150
151 void
152 mono_gc_enable_events (void)
153 {
154         GC_notify_event = on_gc_notification;
155         GC_on_heap_resize = on_gc_heap_resize;
156 }
157
158 #else
159
160 void
161 mono_gc_enable_events (void)
162 {
163 }
164
165 #endif
166
167 void
168 mono_gc_weak_link_add (void **link_addr, MonoObject *obj)
169 {
170         /* libgc requires that we use HIDE_POINTER... */
171         *link_addr = (void*)HIDE_POINTER (obj);
172         GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
173 }
174
175 void
176 mono_gc_weak_link_remove (void **link_addr)
177 {
178         GC_unregister_disappearing_link (link_addr);
179         *link_addr = NULL;
180 }
181
182 MonoObject*
183 mono_gc_weak_link_get (void **link_addr)
184 {
185         MonoObject *obj = REVEAL_POINTER (*link_addr);
186         if (obj == (MonoObject *) -1)
187                 return NULL;
188         return obj;
189 }
190
191 void*
192 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
193 {
194         return NULL;
195 }
196
197 void*
198 mono_gc_alloc_fixed (size_t size, void *descr)
199 {
200         return GC_MALLOC (size);
201 }
202
203 void
204 mono_gc_free_fixed (void* addr)
205 {
206 }
207
208 int
209 mono_gc_invoke_finalizers (void)
210 {
211         /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
212          * the 'mem_freed' variable is not initialized when there are no
213          * objects to finalize, which leads to strange behavior later on.
214          * The check is necessary to work around that bug.
215          */
216         if (GC_should_invoke_finalizers ())
217                 return GC_invoke_finalizers ();
218         return 0;
219 }
220
221 gboolean
222 mono_gc_pending_finalizers (void)
223 {
224         return GC_should_invoke_finalizers ();
225 }
226
227 void
228 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
229 {
230         *(void**)field_ptr = value;
231 }
232
233 void
234 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
235 {
236         *(void**)slot_ptr = value;
237 }
238
239 void
240 mono_gc_wbarrier_arrayref_copy (MonoArray *arr, gpointer slot_ptr, int count)
241 {
242         /* no need to do anything */
243 }
244
245 void
246 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
247 {
248         *(void**)ptr = value;
249 }
250
251 void
252 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
253 {
254 }
255
256 void
257 mono_gc_wbarrier_object (MonoObject *object)
258 {
259 }
260
261 #if defined(USE_INCLUDED_LIBGC) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
262 extern __thread MONO_TLS_FAST void* GC_thread_tls;
263 #include "metadata-internals.h"
264
265 static int
266 shift_amount (int v)
267 {
268         int i = 0;
269         while (!(v & (1 << i)))
270                 i++;
271         return i;
272 }
273
274 enum {
275         ATYPE_FREEPTR,
276         ATYPE_FREEPTR_FOR_BOX,
277         ATYPE_NORMAL,
278         ATYPE_GCJ,
279         ATYPE_NUM
280 };
281
282 static MonoMethod*
283 create_allocator (int atype, int offset)
284 {
285         int index_var, bytes_var, my_fl_var, my_entry_var;
286         guint32 no_freelist_branch, not_small_enough_branch = 0;
287         MonoMethodBuilder *mb;
288         MonoMethod *res;
289         MonoMethodSignature *csig;
290
291         csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
292         csig->ret = &mono_defaults.object_class->byval_arg;
293         csig->params [0] = &mono_defaults.int_class->byval_arg;
294         
295         mb = mono_mb_new (mono_defaults.object_class, "Alloc", MONO_WRAPPER_ALLOC);
296         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
297         /* bytes = vtable->klass->instance_size */
298         mono_mb_emit_ldarg (mb, 0);
299         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
300         mono_mb_emit_byte (mb, MONO_CEE_ADD);
301         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
302         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
303         mono_mb_emit_byte (mb, MONO_CEE_ADD);
304         /* FIXME: assert instance_size stays a 4 byte integer */
305         mono_mb_emit_byte (mb, MONO_CEE_LDIND_U4);
306         mono_mb_emit_stloc (mb, bytes_var);
307
308 #if 0
309         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
310         if (atype != ATYPE_FREEPTR && atype != ATYPE_FREEPTR_FOR_BOX) {
311                 /* check for size */
312                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
313                 mono_mb_emit_ldloc (mb, bytes_var);
314                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
315                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
316         }
317 #endif
318
319         /* int index = INDEX_FROM_BYTES(bytes); */
320         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
321         
322         mono_mb_emit_ldloc (mb, bytes_var);
323         mono_mb_emit_icon (mb, GRANULARITY - 1);
324         mono_mb_emit_byte (mb, MONO_CEE_ADD);
325         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
326         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
327         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
328         mono_mb_emit_byte (mb, MONO_CEE_SHL);
329         /* index var is already adjusted into bytes */
330         mono_mb_emit_stloc (mb, index_var);
331
332         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
333         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
334         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
335         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
336         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
337         mono_mb_emit_i4 (mb, offset);
338         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX)
339                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
340         else if (atype == ATYPE_NORMAL)
341                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
342         else if (atype == ATYPE_GCJ)
343                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
344         else
345                 g_assert_not_reached ();
346         mono_mb_emit_byte (mb, MONO_CEE_ADD);
347         mono_mb_emit_ldloc (mb, index_var);
348         mono_mb_emit_byte (mb, MONO_CEE_ADD);
349         mono_mb_emit_stloc (mb, my_fl_var);
350
351         /* my_entry = *my_fl; */
352         mono_mb_emit_ldloc (mb, my_fl_var);
353         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
354         mono_mb_emit_stloc (mb, my_entry_var);
355
356         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
357         mono_mb_emit_ldloc (mb, my_entry_var);
358         mono_mb_emit_icon (mb, HBLKSIZE);
359         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
360
361         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
362         mono_mb_emit_ldloc (mb, my_fl_var);
363         mono_mb_emit_ldloc (mb, my_entry_var);
364         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
365         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
366
367         /* set the vtable and clear the words in the object */
368         mono_mb_emit_ldloc (mb, my_entry_var);
369         mono_mb_emit_ldarg (mb, 0);
370         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
371
372         if (atype == ATYPE_FREEPTR) {
373                 int start_var, end_var, start_loop;
374                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
375                  */
376                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
377                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
378                 mono_mb_emit_ldloc (mb, my_entry_var);
379                 mono_mb_emit_ldloc (mb, bytes_var);
380                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
381                 mono_mb_emit_stloc (mb, end_var);
382                 mono_mb_emit_ldloc (mb, my_entry_var);
383                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
384                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
385                 mono_mb_emit_stloc (mb, start_var);
386                 /*
387                  * do {
388                  *      *start++ = NULL;
389                  * } while (start < end);
390                  */
391                 start_loop = mono_mb_get_label (mb);
392                 mono_mb_emit_ldloc (mb, start_var);
393                 mono_mb_emit_icon (mb, 0);
394                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
395                 mono_mb_emit_ldloc (mb, start_var);
396                 mono_mb_emit_icon (mb, sizeof (gpointer));
397                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
398                 mono_mb_emit_stloc (mb, start_var);
399
400                 mono_mb_emit_ldloc (mb, start_var);
401                 mono_mb_emit_ldloc (mb, end_var);
402                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
403 //              g_print ("distance: %d\n", start_loop - (mono_mb_get_label (mb) + 1));
404                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
405         } else if (atype == ATYPE_FREEPTR_FOR_BOX) {
406                 /* need to clear just the sync pointer */
407                 mono_mb_emit_ldloc (mb, my_entry_var);
408                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
409                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
410                 mono_mb_emit_icon (mb, 0);
411                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
412         }
413
414         /* return my_entry; */
415         mono_mb_emit_ldloc (mb, my_entry_var);
416         mono_mb_emit_byte (mb, MONO_CEE_RET);
417         
418         mono_mb_patch_short_branch (mb, no_freelist_branch);
419         if (not_small_enough_branch > 0)
420                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
421         /* the slow path: we just call back into the runtime */
422         mono_mb_emit_ldarg (mb, 0);
423         mono_mb_emit_icall (mb, mono_object_new_specific);
424
425         mono_mb_emit_byte (mb, MONO_CEE_RET);
426
427         res = mono_mb_create_method (mb, csig, 8);
428         mono_mb_free (mb);
429         mono_method_get_header (res)->init_locals = FALSE;
430         return res;
431 }
432
433 static MonoMethod* alloc_method_cache [ATYPE_NUM];
434 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
435
436 /*
437  * If possible, generate a managed method that can quickly allocate objects in class
438  * @klass. The method will typically have an thread-local inline allocation sequence.
439  * The signature of the called method is:
440  *      object allocate (MonoVTable *vtable)
441  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
442  * keep in sync.
443  * The thread local alloc logic is taken from libgc/pthread_support.c.
444  */
445
446 MonoMethod*
447 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
448 {
449         int offset = -1;
450         int atype;
451         MonoClass *klass = vtable->klass;
452         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
453
454         /*g_print ("thread tls: %d\n", offset);*/
455         if (offset == -1)
456                 return NULL;
457         if (!SMALL_ENOUGH (klass->instance_size))
458                 return NULL;
459         if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
460                 return NULL;
461         if (klass->rank || klass->byval_arg.type == MONO_TYPE_STRING)
462                 return NULL;
463         /* now we have only the simple cases: ptrfree, gcj, non-gcj: we handle only the first as a test */
464         if (!klass->has_references) {
465                 if (for_box)
466                         atype = ATYPE_FREEPTR_FOR_BOX;
467                 else
468                         atype = ATYPE_FREEPTR;
469         } else {
470                 return NULL;
471                 /*
472                  * disabled because we currently do a runtime choice anyway, to
473                  * deal with multiple appdomains.
474                 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
475                         atype = ATYPE_GCJ;
476                 else
477                         atype = ATYPE_NORMAL;
478                 */
479         }
480         return mono_gc_get_managed_allocator_by_type (atype);
481 }
482
483 /**
484  * mono_gc_get_managed_allocator_id:
485  *
486  *   Return a type for the managed allocator method MANAGED_ALLOC which can later be passed
487  * to mono_gc_get_managed_allocator_by_type () to get back this allocator method. This can be
488  * used by the AOT code to encode references to managed allocator methods.
489  */
490 int
491 mono_gc_get_managed_allocator_type (MonoMethod *managed_alloc)
492 {
493         int i;
494
495         mono_loader_lock ();
496         for (i = 0; i < ATYPE_NUM; ++i) {
497                 if (alloc_method_cache [i] == managed_alloc) {
498                         mono_loader_unlock ();
499                         return i;
500                 }
501         }
502         mono_loader_unlock ();
503
504         return -1;
505 }
506
507 /**
508  * mono_gc_get_managed_allocator_by_type:
509  *
510  *   Return a managed allocator method corresponding to allocator type ATYPE.
511  */
512 MonoMethod*
513 mono_gc_get_managed_allocator_by_type (int atype)
514 {
515         int offset = -1;
516         MonoMethod *res;
517         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
518
519         mono_loader_lock ();
520         res = alloc_method_cache [atype];
521         if (!res)
522                 res = alloc_method_cache [atype] = create_allocator (atype, offset);
523         mono_loader_unlock ();
524         return res;
525 }
526
527 #else
528
529 MonoMethod*
530 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
531 {
532         return NULL;
533 }
534
535 int
536 mono_gc_get_managed_allocator_type (MonoMethod *managed_alloc)
537 {
538         return -1;
539 }
540
541 MonoMethod*
542 mono_gc_get_managed_allocator_by_type (int atype)
543 {
544         return NULL;
545 }
546
547 #endif
548
549 #endif /* no Boehm GC */
550