* assembly.c (build_assembly_name): add arg for passing the assembly
[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/metadata/gc-internal.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/method-builder.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 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
27
28 static gboolean gc_initialized = FALSE;
29
30 static void
31 mono_gc_warning (char *msg, GC_word arg)
32 {
33         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
34 }
35
36 void
37 mono_gc_base_init (void)
38 {
39         if (gc_initialized)
40                 return;
41
42         /*
43          * Handle the case when we are called from a thread different from the main thread,
44          * confusing libgc.
45          * FIXME: Move this to libgc where it belongs.
46          *
47          * we used to do this only when running on valgrind,
48          * but it happens also in other setups.
49          */
50 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
51         {
52                 size_t size;
53                 void *sstart;
54                 pthread_attr_t attr;
55                 pthread_getattr_np (pthread_self (), &attr);
56                 pthread_attr_getstack (&attr, &sstart, &size);
57                 pthread_attr_destroy (&attr); 
58                 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
59 #ifdef __ia64__
60                 /*
61                  * The calculation above doesn't seem to work on ia64, also we need to set
62                  * GC_register_stackbottom as well, but don't know how.
63                  */
64 #else
65                 /* apparently with some linuxthreads implementations sstart can be NULL,
66                  * fallback to the more imprecise method (bug# 78096).
67                  */
68                 if (sstart) {
69                         GC_stackbottom = (char*)sstart + size;
70                 } else {
71                         int dummy;
72                         gsize stack_bottom = (gsize)&dummy;
73                         stack_bottom += 4095;
74                         stack_bottom &= ~4095;
75                         GC_stackbottom = (char*)stack_bottom;
76                 }
77 #endif
78         }
79 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
80                 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
81 #else
82         {
83                 int dummy;
84                 gsize stack_bottom = (gsize)&dummy;
85                 stack_bottom += 4095;
86                 stack_bottom &= ~4095;
87                 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
88                 GC_stackbottom = (char*)stack_bottom;
89         }
90 #endif
91
92         GC_init ();
93         GC_no_dls = TRUE;
94         GC_oom_fn = mono_gc_out_of_memory;
95         GC_set_warn_proc (mono_gc_warning);
96         GC_finalize_on_demand = 1;
97         GC_finalizer_notifier = mono_gc_finalize_notify;
98
99 #ifdef HAVE_GC_GCJ_MALLOC
100         GC_init_gcj_malloc (5, NULL);
101 #endif
102
103         gc_initialized = TRUE;
104 }
105
106 void
107 mono_gc_collect (int generation)
108 {
109         GC_gcollect ();
110 }
111
112 int
113 mono_gc_max_generation (void)
114 {
115         return 0;
116 }
117
118 int
119 mono_gc_get_generation  (MonoObject *object)
120 {
121         return 0;
122 }
123
124 int
125 mono_gc_collection_count (int generation)
126 {
127         return GC_gc_no;
128 }
129
130 void
131 mono_gc_add_memory_pressure (gint64 value)
132 {
133 }
134
135 gint64
136 mono_gc_get_used_size (void)
137 {
138         return GC_get_heap_size () - GC_get_free_bytes ();
139 }
140
141 gint64
142 mono_gc_get_heap_size (void)
143 {
144         return GC_get_heap_size ();
145 }
146
147 void
148 mono_gc_disable (void)
149 {
150 #ifdef HAVE_GC_ENABLE
151         GC_disable ();
152 #else
153         g_assert_not_reached ();
154 #endif
155 }
156
157 void
158 mono_gc_enable (void)
159 {
160 #ifdef HAVE_GC_ENABLE
161         GC_enable ();
162 #else
163         g_assert_not_reached ();
164 #endif
165 }
166
167 gboolean
168 mono_gc_is_gc_thread (void)
169 {
170 #if GC_VERSION_MAJOR >= 7
171         return TRUE;
172 #elif defined(USE_INCLUDED_LIBGC)
173         return GC_thread_is_registered ();
174 #else
175         return TRUE;
176 #endif
177 }
178
179 extern int GC_thread_register_foreign (void *base_addr);
180
181 gboolean
182 mono_gc_register_thread (void *baseptr)
183 {
184 #if GC_VERSION_MAJOR >= 7
185         struct GC_stack_base sb;
186         int res;
187
188         res = GC_get_stack_base (&sb);
189         if (res != GC_SUCCESS) {
190                 sb.mem_base = baseptr;
191 #ifdef __ia64__
192                 /* Can't determine the register stack bounds */
193                 g_error ("mono_gc_register_thread failed ().\n");
194 #endif
195         }
196         res = GC_register_my_thread (&sb);
197         if ((res != GC_SUCCESS) && (res != GC_DUPLICATE)) {
198                 g_warning ("GC_register_my_thread () failed.\n");
199                 return FALSE;
200         }
201         return TRUE;
202 #else
203         if (mono_gc_is_gc_thread())
204                 return TRUE;
205 #if defined(USE_INCLUDED_LIBGC) && !defined(PLATFORM_WIN32)
206         return GC_thread_register_foreign (baseptr);
207 #else
208         return FALSE;
209 #endif
210 #endif
211 }
212
213 gboolean
214 mono_object_is_alive (MonoObject* o)
215 {
216 #ifdef USE_INCLUDED_LIBGC
217         return GC_is_marked ((gpointer)o);
218 #else
219         return TRUE;
220 #endif
221 }
222
223 #ifdef USE_INCLUDED_LIBGC
224
225 static void
226 on_gc_notification (GCEventType event)
227 {
228         mono_profiler_gc_event ((MonoGCEvent) event, 0);
229 }
230  
231 static void
232 on_gc_heap_resize (size_t new_size)
233 {
234         mono_profiler_gc_heap_resize (new_size);
235 }
236
237 void
238 mono_gc_enable_events (void)
239 {
240         GC_notify_event = on_gc_notification;
241         GC_on_heap_resize = on_gc_heap_resize;
242 }
243
244 #else
245
246 void
247 mono_gc_enable_events (void)
248 {
249 }
250
251 #endif
252
253 int
254 mono_gc_register_root (char *start, size_t size, void *descr)
255 {
256         /* for some strange reason, they want one extra byte on the end */
257         GC_add_roots (start, start + size + 1);
258
259         return TRUE;
260 }
261
262 void
263 mono_gc_deregister_root (char* addr)
264 {
265 #ifndef PLATFORM_WIN32
266         /* FIXME: libgc doesn't define this work win32 for some reason */
267         /* FIXME: No size info */
268         GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
269 #endif
270 }
271
272 void
273 mono_gc_weak_link_add (void **link_addr, MonoObject *obj)
274 {
275         /* libgc requires that we use HIDE_POINTER... */
276         *link_addr = (void*)HIDE_POINTER (obj);
277         GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
278 }
279
280 void
281 mono_gc_weak_link_remove (void **link_addr)
282 {
283         GC_unregister_disappearing_link (link_addr);
284         *link_addr = NULL;
285 }
286
287 MonoObject*
288 mono_gc_weak_link_get (void **link_addr)
289 {
290         MonoObject *obj = REVEAL_POINTER (*link_addr);
291         if (obj == (MonoObject *) -1)
292                 return NULL;
293         return obj;
294 }
295
296 void*
297 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
298 {
299         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
300 }
301
302 void*
303 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
304 {
305         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
306 }
307
308 void*
309 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
310 {
311         /* libgc has no usable support for arrays... */
312         return GC_NO_DESCRIPTOR;
313 }
314
315 void*
316 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
317 {
318 #ifdef HAVE_GC_GCJ_MALLOC
319         /* It seems there are issues when the bitmap doesn't fit: play it safe */
320         if (numbits >= 30)
321                 return GC_NO_DESCRIPTOR;
322         else
323                 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
324 #else
325         return NULL;
326 #endif
327 }
328
329 void*
330 mono_gc_alloc_fixed (size_t size, void *descr)
331 {
332         return GC_MALLOC (size);
333 }
334
335 void
336 mono_gc_free_fixed (void* addr)
337 {
338 }
339
340 int
341 mono_gc_invoke_finalizers (void)
342 {
343         /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
344          * the 'mem_freed' variable is not initialized when there are no
345          * objects to finalize, which leads to strange behavior later on.
346          * The check is necessary to work around that bug.
347          */
348         if (GC_should_invoke_finalizers ())
349                 return GC_invoke_finalizers ();
350         return 0;
351 }
352
353 gboolean
354 mono_gc_pending_finalizers (void)
355 {
356         return GC_should_invoke_finalizers ();
357 }
358
359 void
360 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
361 {
362         *(void**)field_ptr = value;
363 }
364
365 void
366 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
367 {
368         *(void**)slot_ptr = value;
369 }
370
371 void
372 mono_gc_wbarrier_arrayref_copy (MonoArray *arr, gpointer slot_ptr, int count)
373 {
374         /* no need to do anything */
375 }
376
377 void
378 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
379 {
380         *(void**)ptr = value;
381 }
382
383 void
384 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
385 {
386 }
387
388 void
389 mono_gc_wbarrier_object (MonoObject *object)
390 {
391 }
392
393 #if defined(USE_INCLUDED_LIBGC) && defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
394 extern __thread MONO_TLS_FAST void* GC_thread_tls;
395 #include "metadata-internals.h"
396
397 static int
398 shift_amount (int v)
399 {
400         int i = 0;
401         while (!(v & (1 << i)))
402                 i++;
403         return i;
404 }
405
406 enum {
407         ATYPE_FREEPTR,
408         ATYPE_FREEPTR_FOR_BOX,
409         ATYPE_NORMAL,
410         ATYPE_GCJ,
411         ATYPE_STRING,
412         ATYPE_NUM
413 };
414
415 static MonoMethod*
416 create_allocator (int atype, int offset)
417 {
418         int index_var, bytes_var, my_fl_var, my_entry_var;
419         guint32 no_freelist_branch, not_small_enough_branch = 0;
420         guint32 size_overflow_branch = 0;
421         MonoMethodBuilder *mb;
422         MonoMethod *res;
423         MonoMethodSignature *csig;
424
425         if (atype == ATYPE_STRING) {
426                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
427                 csig->ret = &mono_defaults.string_class->byval_arg;
428                 csig->params [0] = &mono_defaults.int_class->byval_arg;
429                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
430         } else {
431                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
432                 csig->ret = &mono_defaults.object_class->byval_arg;
433                 csig->params [0] = &mono_defaults.int_class->byval_arg;
434         }
435
436         mb = mono_mb_new (mono_defaults.object_class, "Alloc", MONO_WRAPPER_ALLOC);
437         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
438         if (atype == ATYPE_STRING) {
439                 /* a string alloator method takes the args: (vtable, len) */
440                 /* bytes = (sizeof (MonoString) + ((len + 1) * 2)); */
441                 mono_mb_emit_ldarg (mb, 1);
442                 mono_mb_emit_icon (mb, 1);
443                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
444                 mono_mb_emit_icon (mb, 1);
445                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
446                 // sizeof (MonoString) might include padding
447                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
448                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
449                 mono_mb_emit_stloc (mb, bytes_var);
450         } else {
451                 /* bytes = vtable->klass->instance_size */
452                 mono_mb_emit_ldarg (mb, 0);
453                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
454                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
455                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
456                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
457                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
458                 /* FIXME: assert instance_size stays a 4 byte integer */
459                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_U4);
460                 mono_mb_emit_stloc (mb, bytes_var);
461         }
462
463         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
464         if (atype == ATYPE_STRING) {
465                 /* check for size */
466                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
467                 mono_mb_emit_ldloc (mb, bytes_var);
468                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
469                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
470                 /* check for overflow */
471                 mono_mb_emit_ldloc (mb, bytes_var);
472                 mono_mb_emit_icon (mb, sizeof (MonoString));
473                 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
474         }
475
476         /* int index = INDEX_FROM_BYTES(bytes); */
477         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
478         
479         mono_mb_emit_ldloc (mb, bytes_var);
480         mono_mb_emit_icon (mb, GRANULARITY - 1);
481         mono_mb_emit_byte (mb, MONO_CEE_ADD);
482         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
483         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
484         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
485         mono_mb_emit_byte (mb, MONO_CEE_SHL);
486         /* index var is already adjusted into bytes */
487         mono_mb_emit_stloc (mb, index_var);
488
489         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
490         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
491         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
492         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
493         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
494         mono_mb_emit_i4 (mb, offset);
495         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
496                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
497         else if (atype == ATYPE_NORMAL)
498                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
499         else if (atype == ATYPE_GCJ)
500                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
501         else
502                 g_assert_not_reached ();
503         mono_mb_emit_byte (mb, MONO_CEE_ADD);
504         mono_mb_emit_ldloc (mb, index_var);
505         mono_mb_emit_byte (mb, MONO_CEE_ADD);
506         mono_mb_emit_stloc (mb, my_fl_var);
507
508         /* my_entry = *my_fl; */
509         mono_mb_emit_ldloc (mb, my_fl_var);
510         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
511         mono_mb_emit_stloc (mb, my_entry_var);
512
513         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
514         mono_mb_emit_ldloc (mb, my_entry_var);
515         mono_mb_emit_icon (mb, HBLKSIZE);
516         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
517
518         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
519         mono_mb_emit_ldloc (mb, my_fl_var);
520         mono_mb_emit_ldloc (mb, my_entry_var);
521         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
522         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
523
524         /* set the vtable and clear the words in the object */
525         mono_mb_emit_ldloc (mb, my_entry_var);
526         mono_mb_emit_ldarg (mb, 0);
527         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
528
529         if (atype == ATYPE_FREEPTR) {
530                 int start_var, end_var, start_loop;
531                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
532                  */
533                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
534                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
535                 mono_mb_emit_ldloc (mb, my_entry_var);
536                 mono_mb_emit_ldloc (mb, bytes_var);
537                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
538                 mono_mb_emit_stloc (mb, end_var);
539                 mono_mb_emit_ldloc (mb, my_entry_var);
540                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
541                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
542                 mono_mb_emit_stloc (mb, start_var);
543                 /*
544                  * do {
545                  *      *start++ = NULL;
546                  * } while (start < end);
547                  */
548                 start_loop = mono_mb_get_label (mb);
549                 mono_mb_emit_ldloc (mb, start_var);
550                 mono_mb_emit_icon (mb, 0);
551                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
552                 mono_mb_emit_ldloc (mb, start_var);
553                 mono_mb_emit_icon (mb, sizeof (gpointer));
554                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
555                 mono_mb_emit_stloc (mb, start_var);
556
557                 mono_mb_emit_ldloc (mb, start_var);
558                 mono_mb_emit_ldloc (mb, end_var);
559                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
560                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
561         } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
562                 /* need to clear just the sync pointer */
563                 mono_mb_emit_ldloc (mb, my_entry_var);
564                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
565                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
566                 mono_mb_emit_icon (mb, 0);
567                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
568         }
569
570         if (atype == ATYPE_STRING) {
571                 /* need to set length and clear the last char */
572                 /* s->length = len; */
573                 mono_mb_emit_ldloc (mb, my_entry_var);
574                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
575                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
576                 mono_mb_emit_ldarg (mb, 1);
577                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
578                 /* s->chars [len] = 0; */
579                 mono_mb_emit_ldloc (mb, my_entry_var);
580                 mono_mb_emit_ldloc (mb, bytes_var);
581                 mono_mb_emit_icon (mb, 2);
582                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
583                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
584                 mono_mb_emit_icon (mb, 0);
585                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
586         }
587
588         /* return my_entry; */
589         mono_mb_emit_ldloc (mb, my_entry_var);
590         mono_mb_emit_byte (mb, MONO_CEE_RET);
591         
592         mono_mb_patch_short_branch (mb, no_freelist_branch);
593         if (not_small_enough_branch > 0)
594                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
595         if (size_overflow_branch > 0)
596                 mono_mb_patch_short_branch (mb, size_overflow_branch);
597         /* the slow path: we just call back into the runtime */
598         if (atype == ATYPE_STRING) {
599                 mono_mb_emit_ldarg (mb, 1);
600                 mono_mb_emit_icall (mb, mono_string_alloc);
601         } else {
602                 mono_mb_emit_ldarg (mb, 0);
603                 mono_mb_emit_icall (mb, mono_object_new_specific);
604         }
605
606         mono_mb_emit_byte (mb, MONO_CEE_RET);
607
608         res = mono_mb_create_method (mb, csig, 8);
609         mono_mb_free (mb);
610         mono_method_get_header (res)->init_locals = FALSE;
611         return res;
612 }
613
614 static MonoMethod* alloc_method_cache [ATYPE_NUM];
615
616 /*
617  * If possible, generate a managed method that can quickly allocate objects in class
618  * @klass. The method will typically have an thread-local inline allocation sequence.
619  * The signature of the called method is:
620  *      object allocate (MonoVTable *vtable)
621  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
622  * keep in sync.
623  * The thread local alloc logic is taken from libgc/pthread_support.c.
624  */
625
626 MonoMethod*
627 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
628 {
629         int offset = -1;
630         int atype;
631         MonoClass *klass = vtable->klass;
632         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
633
634         /*g_print ("thread tls: %d\n", offset);*/
635         if (offset == -1)
636                 return NULL;
637         if (!SMALL_ENOUGH (klass->instance_size))
638                 return NULL;
639         if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
640                 return NULL;
641         if (klass->rank)
642                 return NULL;
643         if (klass->byval_arg.type == MONO_TYPE_STRING) {
644                 atype = ATYPE_STRING;
645         } else if (!klass->has_references) {
646                 if (for_box)
647                         atype = ATYPE_FREEPTR_FOR_BOX;
648                 else
649                         atype = ATYPE_FREEPTR;
650         } else {
651                 return NULL;
652                 /*
653                  * disabled because we currently do a runtime choice anyway, to
654                  * deal with multiple appdomains.
655                 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
656                         atype = ATYPE_GCJ;
657                 else
658                         atype = ATYPE_NORMAL;
659                 */
660         }
661         return mono_gc_get_managed_allocator_by_type (atype);
662 }
663
664 /**
665  * mono_gc_get_managed_allocator_id:
666  *
667  *   Return a type for the managed allocator method MANAGED_ALLOC which can later be passed
668  * to mono_gc_get_managed_allocator_by_type () to get back this allocator method. This can be
669  * used by the AOT code to encode references to managed allocator methods.
670  */
671 int
672 mono_gc_get_managed_allocator_type (MonoMethod *managed_alloc)
673 {
674         int i;
675
676         mono_loader_lock ();
677         for (i = 0; i < ATYPE_NUM; ++i) {
678                 if (alloc_method_cache [i] == managed_alloc) {
679                         mono_loader_unlock ();
680                         return i;
681                 }
682         }
683         mono_loader_unlock ();
684
685         return -1;
686 }
687
688 /**
689  * mono_gc_get_managed_allocator_by_type:
690  *
691  *   Return a managed allocator method corresponding to allocator type ATYPE.
692  */
693 MonoMethod*
694 mono_gc_get_managed_allocator_by_type (int atype)
695 {
696         int offset = -1;
697         MonoMethod *res;
698         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
699
700         mono_loader_lock ();
701         res = alloc_method_cache [atype];
702         if (!res)
703                 res = alloc_method_cache [atype] = create_allocator (atype, offset);
704         mono_loader_unlock ();
705         return res;
706 }
707
708 #else
709
710 MonoMethod*
711 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
712 {
713         return NULL;
714 }
715
716 int
717 mono_gc_get_managed_allocator_type (MonoMethod *managed_alloc)
718 {
719         return -1;
720 }
721
722 MonoMethod*
723 mono_gc_get_managed_allocator_by_type (int atype)
724 {
725         return NULL;
726 }
727
728 #endif
729
730 #endif /* no Boehm GC */
731