a3f8296459a2500efea3372f8c4441cc822df519
[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-2011 Novell, Inc (http://www.novell.com)
6  * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
7  */
8
9 #include "config.h"
10
11 #include <string.h>
12
13 #define GC_I_HIDE_POINTERS
14 #include <mono/metadata/gc-internal.h>
15 #include <mono/metadata/mono-gc.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/metadata/runtime.h>
24 #include <mono/metadata/sgen-toggleref.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-logger-internal.h>
27 #include <mono/utils/mono-memory-model.h>
28 #include <mono/utils/mono-time.h>
29 #include <mono/utils/mono-threads.h>
30 #include <mono/utils/dtrace.h>
31 #include <mono/utils/gc_wrapper.h>
32 #include <mono/utils/mono-mutex.h>
33
34 #if HAVE_BOEHM_GC
35
36 #ifdef USE_INCLUDED_LIBGC
37 #undef TRUE
38 #undef FALSE
39 #define THREAD_LOCAL_ALLOC 1
40 #include "private/pthread_support.h"
41 #endif
42
43 #if defined(PLATFORM_MACOSX) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
44 void *pthread_get_stackaddr_np(pthread_t);
45 #endif
46
47 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
48 /*Boehm max heap cannot be smaller than 16MB*/
49 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
50 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
51
52 static gboolean gc_initialized = FALSE;
53 static mono_mutex_t mono_gc_lock;
54
55 static void*
56 boehm_thread_register (MonoThreadInfo* info, void *baseptr);
57 static void
58 boehm_thread_unregister (MonoThreadInfo *p);
59 static void
60 register_test_toggleref_callback (void);
61
62 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
63 static MonoGCFinalizerCallbacks fin_callbacks;
64
65 static void
66 mono_gc_warning (char *msg, GC_word arg)
67 {
68         mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
69 }
70
71 void
72 mono_gc_base_init (void)
73 {
74         MonoThreadInfoCallbacks cb;
75         const char *env;
76         int dummy;
77
78         if (gc_initialized)
79                 return;
80
81         /*
82          * Handle the case when we are called from a thread different from the main thread,
83          * confusing libgc.
84          * FIXME: Move this to libgc where it belongs.
85          *
86          * we used to do this only when running on valgrind,
87          * but it happens also in other setups.
88          */
89 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK) && !defined(__native_client__)
90         {
91                 size_t size;
92                 void *sstart;
93                 pthread_attr_t attr;
94                 pthread_getattr_np (pthread_self (), &attr);
95                 pthread_attr_getstack (&attr, &sstart, &size);
96                 pthread_attr_destroy (&attr); 
97                 /*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
98 #ifdef __ia64__
99                 /*
100                  * The calculation above doesn't seem to work on ia64, also we need to set
101                  * GC_register_stackbottom as well, but don't know how.
102                  */
103 #else
104                 /* apparently with some linuxthreads implementations sstart can be NULL,
105                  * fallback to the more imprecise method (bug# 78096).
106                  */
107                 if (sstart) {
108                         GC_stackbottom = (char*)sstart + size;
109                 } else {
110                         int dummy;
111                         gsize stack_bottom = (gsize)&dummy;
112                         stack_bottom += 4095;
113                         stack_bottom &= ~4095;
114                         GC_stackbottom = (char*)stack_bottom;
115                 }
116 #endif
117         }
118 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
119                 GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
120 #elif defined(__OpenBSD__)
121 #  include <pthread_np.h>
122         {
123                 stack_t ss;
124                 int rslt;
125
126                 rslt = pthread_stackseg_np(pthread_self(), &ss);
127                 g_assert (rslt == 0);
128
129                 GC_stackbottom = (char*)ss.ss_sp;
130         }
131 #elif defined(__native_client__)
132         /* Do nothing, GC_stackbottom is set correctly in libgc */
133 #else
134         {
135                 int dummy;
136                 gsize stack_bottom = (gsize)&dummy;
137                 stack_bottom += 4095;
138                 stack_bottom &= ~4095;
139                 /*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
140                 GC_stackbottom = (char*)stack_bottom;
141         }
142 #endif
143
144 #if !defined(PLATFORM_ANDROID)
145         /* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
146         GC_no_dls = TRUE;
147 #endif
148         GC_init ();
149         GC_oom_fn = mono_gc_out_of_memory;
150         GC_set_warn_proc (mono_gc_warning);
151         GC_finalize_on_demand = 1;
152         GC_finalizer_notifier = mono_gc_finalize_notify;
153
154 #ifdef HAVE_GC_GCJ_MALLOC
155         GC_init_gcj_malloc (5, NULL);
156 #endif
157
158 #ifdef HAVE_GC_ALLOW_REGISTER_THREADS
159         GC_allow_register_threads();
160 #endif
161
162         if ((env = g_getenv ("MONO_GC_PARAMS"))) {
163                 char **ptr, **opts = g_strsplit (env, ",", -1);
164                 for (ptr = opts; *ptr; ++ptr) {
165                         char *opt = *ptr;
166                         if (g_str_has_prefix (opt, "max-heap-size=")) {
167                                 size_t max_heap;
168
169                                 opt = strchr (opt, '=') + 1;
170                                 if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
171                                         if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
172                                                 fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
173                                                 exit (1);
174                                         }
175                                         GC_set_max_heap_size (max_heap);
176                                 } else {
177                                         fprintf (stderr, "max-heap-size must be an integer.\n");
178                                         exit (1);
179                                 }
180                                 continue;
181                         } else if (g_str_has_prefix (opt, "toggleref-test")) {
182                                 register_test_toggleref_callback ();
183                                 continue;
184                         } else {
185                                 /* Could be a parameter for sgen */
186                                 /*
187                                 fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
188                                 fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
189                                 exit (1);
190                                 */
191                         }
192                 }
193                 g_strfreev (opts);
194         }
195
196         memset (&cb, 0, sizeof (cb));
197         cb.thread_register = boehm_thread_register;
198         cb.thread_unregister = boehm_thread_unregister;
199         cb.mono_method_is_critical = (gpointer)mono_runtime_is_critical_method;
200 #ifndef HOST_WIN32
201         cb.thread_exit = mono_gc_pthread_exit;
202         cb.mono_gc_pthread_create = (gpointer)mono_gc_pthread_create;
203 #endif
204         
205         mono_threads_init (&cb, sizeof (MonoThreadInfo));
206         mono_mutex_init (&mono_gc_lock);
207
208         mono_thread_info_attach (&dummy);
209
210         mono_gc_enable_events ();
211         gc_initialized = TRUE;
212 }
213
214 /**
215  * mono_gc_collect:
216  * @generation: GC generation identifier
217  *
218  * Perform a garbage collection for the given generation, higher numbers
219  * mean usually older objects. Collecting a high-numbered generation
220  * implies collecting also the lower-numbered generations.
221  * The maximum value for @generation can be retrieved with a call to
222  * mono_gc_max_generation(), so this function is usually called as:
223  *
224  *      mono_gc_collect (mono_gc_max_generation ());
225  */
226 void
227 mono_gc_collect (int generation)
228 {
229 #ifndef DISABLE_PERFCOUNTERS
230         mono_perfcounters->gc_induced++;
231 #endif
232         GC_gcollect ();
233 }
234
235 /**
236  * mono_gc_max_generation:
237  *
238  * Get the maximum generation number used by the current garbage
239  * collector. The value will be 0 for the Boehm collector, 1 or more
240  * for the generational collectors.
241  *
242  * Returns: the maximum generation number.
243  */
244 int
245 mono_gc_max_generation (void)
246 {
247         return 0;
248 }
249
250 /**
251  * mono_gc_get_generation:
252  * @object: a managed object
253  *
254  * Get the garbage collector's generation that @object belongs to.
255  * Use this has a hint only.
256  *
257  * Returns: a garbage collector generation number
258  */
259 int
260 mono_gc_get_generation  (MonoObject *object)
261 {
262         return 0;
263 }
264
265 /**
266  * mono_gc_collection_count:
267  * @generation: a GC generation number
268  *
269  * Get how many times a garbage collection has been performed
270  * for the given @generation number.
271  *
272  * Returns: the number of garbage collections
273  */
274 int
275 mono_gc_collection_count (int generation)
276 {
277         return GC_gc_no;
278 }
279
280 /**
281  * mono_gc_add_memory_pressure:
282  * @value: amount of bytes
283  *
284  * Adjust the garbage collector's view of how many bytes of memory
285  * are indirectly referenced by managed objects (for example unmanaged
286  * memory holding image or other binary data).
287  * This is a hint only to the garbage collector algorithm.
288  * Note that negative amounts of @value will decrease the memory
289  * pressure.
290  */
291 void
292 mono_gc_add_memory_pressure (gint64 value)
293 {
294 }
295
296 /**
297  * mono_gc_get_used_size:
298  *
299  * Get the approximate amount of memory used by managed objects.
300  *
301  * Returns: the amount of memory used in bytes
302  */
303 int64_t
304 mono_gc_get_used_size (void)
305 {
306         return GC_get_heap_size () - GC_get_free_bytes ();
307 }
308
309 /**
310  * mono_gc_get_heap_size:
311  *
312  * Get the amount of memory used by the garbage collector.
313  *
314  * Returns: the size of the heap in bytes
315  */
316 int64_t
317 mono_gc_get_heap_size (void)
318 {
319         return GC_get_heap_size ();
320 }
321
322 gboolean
323 mono_gc_is_gc_thread (void)
324 {
325 #if GC_VERSION_MAJOR >= 7
326         return TRUE;
327 #elif defined(USE_INCLUDED_LIBGC)
328         return GC_thread_is_registered ();
329 #else
330         return TRUE;
331 #endif
332 }
333
334 extern int GC_thread_register_foreign (void *base_addr);
335
336 gboolean
337 mono_gc_register_thread (void *baseptr)
338 {
339         return mono_thread_info_attach (baseptr) != NULL;
340 }
341
342 static void*
343 boehm_thread_register (MonoThreadInfo* info, void *baseptr)
344 {
345 #if GC_VERSION_MAJOR >= 7
346         struct GC_stack_base sb;
347         int res;
348
349         res = GC_get_stack_base (&sb);
350         if (res != GC_SUCCESS) {
351                 sb.mem_base = baseptr;
352 #ifdef __ia64__
353                 /* Can't determine the register stack bounds */
354                 g_error ("mono_gc_register_thread failed ().\n");
355 #endif
356         }
357         res = GC_register_my_thread (&sb);
358         if ((res != GC_SUCCESS) && (res != GC_DUPLICATE)) {
359                 g_warning ("GC_register_my_thread () failed.\n");
360                 return NULL;
361         }
362         return info;
363 #else
364         if (mono_gc_is_gc_thread())
365                 return info;
366 #if defined(USE_INCLUDED_LIBGC) && !defined(HOST_WIN32)
367         return GC_thread_register_foreign (baseptr) ? info : NULL;
368 #else
369         return NULL;
370 #endif
371 #endif
372 }
373
374 static void
375 boehm_thread_unregister (MonoThreadInfo *p)
376 {
377         MonoNativeThreadId tid;
378
379         tid = mono_thread_info_get_tid (p);
380
381         if (p->runtime_thread)
382                 mono_threads_add_joinable_thread ((gpointer)tid);
383 }
384
385 gboolean
386 mono_object_is_alive (MonoObject* o)
387 {
388 #ifdef USE_INCLUDED_LIBGC
389         return GC_is_marked ((gpointer)o);
390 #else
391         return TRUE;
392 #endif
393 }
394
395 int
396 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
397 {
398         return 1;
399 }
400
401 #ifdef USE_INCLUDED_LIBGC
402
403 static gint64 gc_start_time;
404
405 static void
406 on_gc_notification (GCEventType event)
407 {
408         MonoGCEvent e = (MonoGCEvent)event;
409
410         switch (e) {
411         case MONO_GC_EVENT_PRE_STOP_WORLD:
412                 MONO_GC_WORLD_STOP_BEGIN ();
413                 mono_thread_info_suspend_lock ();
414                 break;
415
416         case MONO_GC_EVENT_POST_STOP_WORLD:
417                 MONO_GC_WORLD_STOP_END ();
418                 break;
419
420         case MONO_GC_EVENT_PRE_START_WORLD:
421                 MONO_GC_WORLD_RESTART_BEGIN (1);
422                 break;
423
424         case MONO_GC_EVENT_POST_START_WORLD:
425                 MONO_GC_WORLD_RESTART_END (1);
426                 mono_thread_info_suspend_unlock ();
427                 break;
428
429         case MONO_GC_EVENT_START:
430                 MONO_GC_BEGIN (1);
431 #ifndef DISABLE_PERFCOUNTERS
432                 if (mono_perfcounters)
433                         mono_perfcounters->gc_collections0++;
434 #endif
435                 gc_stats.major_gc_count ++;
436                 gc_start_time = mono_100ns_ticks ();
437                 break;
438
439         case MONO_GC_EVENT_END:
440                 MONO_GC_END (1);
441 #if defined(ENABLE_DTRACE) && defined(__sun__)
442                 /* This works around a dtrace -G problem on Solaris.
443                    Limit its actual use to when the probe is enabled. */
444                 if (MONO_GC_END_ENABLED ())
445                         sleep(0);
446 #endif
447
448 #ifndef DISABLE_PERFCOUNTERS
449                 if (mono_perfcounters) {
450                         guint64 heap_size = GC_get_heap_size ();
451                         guint64 used_size = heap_size - GC_get_free_bytes ();
452                         mono_perfcounters->gc_total_bytes = used_size;
453                         mono_perfcounters->gc_committed_bytes = heap_size;
454                         mono_perfcounters->gc_reserved_bytes = heap_size;
455                         mono_perfcounters->gc_gen0size = heap_size;
456                 }
457 #endif
458                 gc_stats.major_gc_time += mono_100ns_ticks () - gc_start_time;
459                 mono_trace_message (MONO_TRACE_GC, "gc took %d usecs", (mono_100ns_ticks () - gc_start_time) / 10);
460                 break;
461         }
462
463         mono_profiler_gc_event (e, 0);
464 }
465  
466 static void
467 on_gc_heap_resize (size_t new_size)
468 {
469         guint64 heap_size = GC_get_heap_size ();
470 #ifndef DISABLE_PERFCOUNTERS
471         if (mono_perfcounters) {
472                 mono_perfcounters->gc_committed_bytes = heap_size;
473                 mono_perfcounters->gc_reserved_bytes = heap_size;
474                 mono_perfcounters->gc_gen0size = heap_size;
475         }
476 #endif
477         mono_profiler_gc_heap_resize (new_size);
478 }
479
480 void
481 mono_gc_enable_events (void)
482 {
483         GC_notify_event = on_gc_notification;
484         GC_on_heap_resize = on_gc_heap_resize;
485 }
486
487 #else
488
489 void
490 mono_gc_enable_events (void)
491 {
492 }
493
494 #endif
495
496 int
497 mono_gc_register_root (char *start, size_t size, void *descr)
498 {
499         /* for some strange reason, they want one extra byte on the end */
500         GC_add_roots (start, start + size + 1);
501
502         return TRUE;
503 }
504
505 void
506 mono_gc_deregister_root (char* addr)
507 {
508 #ifndef HOST_WIN32
509         /* FIXME: libgc doesn't define this work win32 for some reason */
510         /* FIXME: No size info */
511         GC_remove_roots (addr, addr + sizeof (gpointer) + 1);
512 #endif
513 }
514
515 void
516 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
517 {
518         /* libgc requires that we use HIDE_POINTER... */
519         *link_addr = (void*)HIDE_POINTER (obj);
520         if (track)
521                 GC_REGISTER_LONG_LINK (link_addr, obj);
522         else
523                 GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
524 }
525
526 void
527 mono_gc_weak_link_remove (void **link_addr, gboolean track)
528 {
529         if (track)
530                 GC_unregister_long_link (link_addr);
531         else
532                 GC_unregister_disappearing_link (link_addr);
533         *link_addr = NULL;
534 }
535
536 static gpointer
537 reveal_link (gpointer link_addr)
538 {
539         void **link_a = link_addr;
540         return REVEAL_POINTER (*link_a);
541 }
542
543 MonoObject*
544 mono_gc_weak_link_get (void **link_addr)
545 {
546         MonoObject *obj = GC_call_with_alloc_lock (reveal_link, link_addr);
547         if (obj == (MonoObject *) -1)
548                 return NULL;
549         return obj;
550 }
551
552 void*
553 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
554 {
555         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
556 }
557
558 void*
559 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
560 {
561         return mono_gc_make_descr_from_bitmap (bitmap, numbits);
562 }
563
564 void*
565 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
566 {
567         /* libgc has no usable support for arrays... */
568         return GC_NO_DESCRIPTOR;
569 }
570
571 void*
572 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
573 {
574 #ifdef HAVE_GC_GCJ_MALLOC
575         /* It seems there are issues when the bitmap doesn't fit: play it safe */
576         if (numbits >= 30)
577                 return GC_NO_DESCRIPTOR;
578         else
579                 return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
580 #else
581         return NULL;
582 #endif
583 }
584
585 void*
586 mono_gc_make_root_descr_all_refs (int numbits)
587 {
588         return NULL;
589 }
590
591 void*
592 mono_gc_alloc_fixed (size_t size, void *descr)
593 {
594         /* To help track down typed allocation bugs */
595         /*
596         static int count;
597         count ++;
598         if (count == atoi (g_getenv ("COUNT2")))
599                 printf ("HIT!\n");
600         if (count > atoi (g_getenv ("COUNT2")))
601                 return GC_MALLOC (size);
602         */
603
604         if (descr)
605                 return GC_MALLOC_EXPLICITLY_TYPED (size, (GC_descr)descr);
606         else
607                 return GC_MALLOC (size);
608 }
609
610 void
611 mono_gc_free_fixed (void* addr)
612 {
613 }
614
615 int
616 mono_gc_invoke_finalizers (void)
617 {
618         /* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
619          * the 'mem_freed' variable is not initialized when there are no
620          * objects to finalize, which leads to strange behavior later on.
621          * The check is necessary to work around that bug.
622          */
623         if (GC_should_invoke_finalizers ())
624                 return GC_invoke_finalizers ();
625         return 0;
626 }
627
628 gboolean
629 mono_gc_pending_finalizers (void)
630 {
631         return GC_should_invoke_finalizers ();
632 }
633
634 void
635 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
636 {
637         *(void**)field_ptr = value;
638 }
639
640 void
641 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
642 {
643         *(void**)slot_ptr = value;
644 }
645
646 void
647 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
648 {
649         mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
650 }
651
652 void
653 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
654 {
655         *(void**)ptr = value;
656 }
657
658 void
659 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
660 {
661         InterlockedWritePointer (ptr, value);
662 }
663
664 void
665 mono_gc_wbarrier_generic_nostore (gpointer ptr)
666 {
667 }
668
669 void
670 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
671 {
672         mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
673 }
674
675 void
676 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
677 {
678         /* do not copy the sync state */
679         mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
680                         mono_object_class (obj)->instance_size - sizeof (MonoObject));
681 }
682
683 void
684 mono_gc_clear_domain (MonoDomain *domain)
685 {
686 }
687
688 int
689 mono_gc_get_suspend_signal (void)
690 {
691 #ifdef USE_INCLUDED_GC
692         return GC_get_suspend_signal ();
693 #else
694         return -1;
695 #endif
696 }
697
698 int
699 mono_gc_get_restart_signal (void)
700 {
701 #ifdef USE_INCLUDED_GC
702         return GC_get_restart_signal ();
703 #else
704         return -1;
705 #endif
706 }
707
708 #if defined(USE_INCLUDED_LIBGC) && defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
709 extern __thread MONO_TLS_FAST void* GC_thread_tls;
710 #include "metadata-internals.h"
711
712 static int
713 shift_amount (int v)
714 {
715         int i = 0;
716         while (!(v & (1 << i)))
717                 i++;
718         return i;
719 }
720
721 enum {
722         ATYPE_FREEPTR,
723         ATYPE_FREEPTR_FOR_BOX,
724         ATYPE_NORMAL,
725         ATYPE_GCJ,
726         ATYPE_STRING,
727         ATYPE_NUM
728 };
729
730 static MonoMethod*
731 create_allocator (int atype, int tls_key)
732 {
733         int index_var, bytes_var, my_fl_var, my_entry_var;
734         guint32 no_freelist_branch, not_small_enough_branch = 0;
735         guint32 size_overflow_branch = 0;
736         MonoMethodBuilder *mb;
737         MonoMethod *res;
738         MonoMethodSignature *csig;
739         AllocatorWrapperInfo *info;
740
741         if (atype == ATYPE_STRING) {
742                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
743                 csig->ret = &mono_defaults.string_class->byval_arg;
744                 csig->params [0] = &mono_defaults.int_class->byval_arg;
745                 csig->params [1] = &mono_defaults.int32_class->byval_arg;
746         } else {
747                 csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
748                 csig->ret = &mono_defaults.object_class->byval_arg;
749                 csig->params [0] = &mono_defaults.int_class->byval_arg;
750         }
751
752         mb = mono_mb_new (mono_defaults.object_class, "Alloc", MONO_WRAPPER_ALLOC);
753         bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
754         if (atype == ATYPE_STRING) {
755                 /* a string alloator method takes the args: (vtable, len) */
756                 /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
757                 mono_mb_emit_ldarg (mb, 1);
758                 mono_mb_emit_icon (mb, 1);
759                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
760                 mono_mb_emit_icon (mb, 1);
761                 mono_mb_emit_byte (mb, MONO_CEE_SHL);
762                 // sizeof (MonoString) might include padding
763                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
764                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
765                 mono_mb_emit_stloc (mb, bytes_var);
766         } else {
767                 /* bytes = vtable->klass->instance_size */
768                 mono_mb_emit_ldarg (mb, 0);
769                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
770                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
771                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
772                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
773                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
774                 /* FIXME: assert instance_size stays a 4 byte integer */
775                 mono_mb_emit_byte (mb, MONO_CEE_LDIND_U4);
776                 mono_mb_emit_stloc (mb, bytes_var);
777         }
778
779         /* this is needed for strings/arrays only as the other big types are never allocated with this method */
780         if (atype == ATYPE_STRING) {
781                 /* check for size */
782                 /* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
783                 mono_mb_emit_ldloc (mb, bytes_var);
784                 mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
785                 not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
786                 /* check for overflow */
787                 mono_mb_emit_ldloc (mb, bytes_var);
788                 mono_mb_emit_icon (mb, sizeof (MonoString));
789                 size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
790         }
791
792         /* int index = INDEX_FROM_BYTES(bytes); */
793         index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
794         
795         mono_mb_emit_ldloc (mb, bytes_var);
796         mono_mb_emit_icon (mb, GRANULARITY - 1);
797         mono_mb_emit_byte (mb, MONO_CEE_ADD);
798         mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
799         mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
800         mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
801         mono_mb_emit_byte (mb, MONO_CEE_SHL);
802         /* index var is already adjusted into bytes */
803         mono_mb_emit_stloc (mb, index_var);
804
805         my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
806         my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
807         /* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
808         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
809         mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
810         mono_mb_emit_i4 (mb, tls_key);
811         if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
812                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, ptrfree_freelists));
813         else if (atype == ATYPE_NORMAL)
814                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, normal_freelists));
815         else if (atype == ATYPE_GCJ)
816                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, gcj_freelists));
817         else
818                 g_assert_not_reached ();
819         mono_mb_emit_byte (mb, MONO_CEE_ADD);
820         mono_mb_emit_ldloc (mb, index_var);
821         mono_mb_emit_byte (mb, MONO_CEE_ADD);
822         mono_mb_emit_stloc (mb, my_fl_var);
823
824         /* my_entry = *my_fl; */
825         mono_mb_emit_ldloc (mb, my_fl_var);
826         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
827         mono_mb_emit_stloc (mb, my_entry_var);
828
829         /* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
830         mono_mb_emit_ldloc (mb, my_entry_var);
831         mono_mb_emit_icon (mb, HBLKSIZE);
832         no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
833
834         /* ptr_t next = obj_link(my_entry); *my_fl = next; */
835         mono_mb_emit_ldloc (mb, my_fl_var);
836         mono_mb_emit_ldloc (mb, my_entry_var);
837         mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
838         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
839
840         /* set the vtable and clear the words in the object */
841         mono_mb_emit_ldloc (mb, my_entry_var);
842         mono_mb_emit_ldarg (mb, 0);
843         mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
844
845         if (atype == ATYPE_FREEPTR) {
846                 int start_var, end_var, start_loop;
847                 /* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
848                  */
849                 start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
850                 end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
851                 mono_mb_emit_ldloc (mb, my_entry_var);
852                 mono_mb_emit_ldloc (mb, bytes_var);
853                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
854                 mono_mb_emit_stloc (mb, end_var);
855                 mono_mb_emit_ldloc (mb, my_entry_var);
856                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
857                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
858                 mono_mb_emit_stloc (mb, start_var);
859                 /*
860                  * do {
861                  *      *start++ = NULL;
862                  * } while (start < end);
863                  */
864                 start_loop = mono_mb_get_label (mb);
865                 mono_mb_emit_ldloc (mb, start_var);
866                 mono_mb_emit_icon (mb, 0);
867                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
868                 mono_mb_emit_ldloc (mb, start_var);
869                 mono_mb_emit_icon (mb, sizeof (gpointer));
870                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
871                 mono_mb_emit_stloc (mb, start_var);
872
873                 mono_mb_emit_ldloc (mb, start_var);
874                 mono_mb_emit_ldloc (mb, end_var);
875                 mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
876                 mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
877         } else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
878                 /* need to clear just the sync pointer */
879                 mono_mb_emit_ldloc (mb, my_entry_var);
880                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
881                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
882                 mono_mb_emit_icon (mb, 0);
883                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
884         }
885
886         if (atype == ATYPE_STRING) {
887                 /* need to set length and clear the last char */
888                 /* s->length = len; */
889                 mono_mb_emit_ldloc (mb, my_entry_var);
890                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
891                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
892                 mono_mb_emit_ldarg (mb, 1);
893                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
894                 /* s->chars [len] = 0; */
895                 mono_mb_emit_ldloc (mb, my_entry_var);
896                 mono_mb_emit_ldloc (mb, bytes_var);
897                 mono_mb_emit_icon (mb, 2);
898                 mono_mb_emit_byte (mb, MONO_CEE_SUB);
899                 mono_mb_emit_byte (mb, MONO_CEE_ADD);
900                 mono_mb_emit_icon (mb, 0);
901                 mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
902         }
903
904         /* return my_entry; */
905         mono_mb_emit_ldloc (mb, my_entry_var);
906         mono_mb_emit_byte (mb, MONO_CEE_RET);
907         
908         mono_mb_patch_short_branch (mb, no_freelist_branch);
909         if (not_small_enough_branch > 0)
910                 mono_mb_patch_short_branch (mb, not_small_enough_branch);
911         if (size_overflow_branch > 0)
912                 mono_mb_patch_short_branch (mb, size_overflow_branch);
913         /* the slow path: we just call back into the runtime */
914         if (atype == ATYPE_STRING) {
915                 mono_mb_emit_ldarg (mb, 1);
916                 mono_mb_emit_icall (mb, mono_string_alloc);
917         } else {
918                 mono_mb_emit_ldarg (mb, 0);
919                 mono_mb_emit_icall (mb, mono_object_new_specific);
920         }
921
922         mono_mb_emit_byte (mb, MONO_CEE_RET);
923
924         res = mono_mb_create_method (mb, csig, 8);
925         mono_mb_free (mb);
926         mono_method_get_header (res)->init_locals = FALSE;
927
928         info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
929         info->gc_name = "boehm";
930         info->alloc_type = atype;
931         mono_marshal_set_wrapper_info (res, info);
932
933         return res;
934 }
935
936 static MonoMethod* alloc_method_cache [ATYPE_NUM];
937
938 static G_GNUC_UNUSED gboolean
939 mono_gc_is_critical_method (MonoMethod *method)
940 {
941         int i;
942
943         for (i = 0; i < ATYPE_NUM; ++i)
944                 if (method == alloc_method_cache [i])
945                         return TRUE;
946
947         return FALSE;
948 }
949
950 /*
951  * If possible, generate a managed method that can quickly allocate objects in class
952  * @klass. The method will typically have an thread-local inline allocation sequence.
953  * The signature of the called method is:
954  *      object allocate (MonoVTable *vtable)
955  * Some of the logic here is similar to mono_class_get_allocation_ftn () i object.c,
956  * keep in sync.
957  * The thread local alloc logic is taken from libgc/pthread_support.c.
958  */
959
960 MonoMethod*
961 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box)
962 {
963         int offset = -1;
964         int atype;
965         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
966
967         /*g_print ("thread tls: %d\n", offset);*/
968         if (offset == -1)
969                 return NULL;
970         if (!SMALL_ENOUGH (klass->instance_size))
971                 return NULL;
972         if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
973                 return NULL;
974         if (klass->rank)
975                 return NULL;
976         if (mono_class_is_open_constructed_type (&klass->byval_arg))
977                 return NULL;
978         if (klass->byval_arg.type == MONO_TYPE_STRING) {
979                 atype = ATYPE_STRING;
980         } else if (!klass->has_references) {
981                 if (for_box)
982                         atype = ATYPE_FREEPTR_FOR_BOX;
983                 else
984                         atype = ATYPE_FREEPTR;
985         } else {
986                 return NULL;
987                 /*
988                  * disabled because we currently do a runtime choice anyway, to
989                  * deal with multiple appdomains.
990                 if (vtable->gc_descr != GC_NO_DESCRIPTOR)
991                         atype = ATYPE_GCJ;
992                 else
993                         atype = ATYPE_NORMAL;
994                 */
995         }
996         return mono_gc_get_managed_allocator_by_type (atype);
997 }
998
999 MonoMethod*
1000 mono_gc_get_managed_array_allocator (MonoClass *klass)
1001 {
1002         return NULL;
1003 }
1004
1005 /**
1006  * mono_gc_get_managed_allocator_by_type:
1007  *
1008  *   Return a managed allocator method corresponding to allocator type ATYPE.
1009  */
1010 MonoMethod*
1011 mono_gc_get_managed_allocator_by_type (int atype)
1012 {
1013         int offset = -1;
1014         MonoMethod *res;
1015         MONO_THREAD_VAR_OFFSET (GC_thread_tls, offset);
1016
1017         mono_tls_key_set_offset (TLS_KEY_BOEHM_GC_THREAD, offset);
1018
1019         res = alloc_method_cache [atype];
1020         if (res)
1021                 return res;
1022
1023         res = create_allocator (atype, TLS_KEY_BOEHM_GC_THREAD);
1024         mono_mutex_lock (&mono_gc_lock);
1025         if (alloc_method_cache [atype]) {
1026                 mono_free_method (res);
1027                 res = alloc_method_cache [atype];
1028         } else {
1029                 mono_memory_barrier ();
1030                 alloc_method_cache [atype] = res;
1031         }
1032         mono_mutex_unlock (&mono_gc_lock);
1033         return res;
1034 }
1035
1036 guint32
1037 mono_gc_get_managed_allocator_types (void)
1038 {
1039         return ATYPE_NUM;
1040 }
1041
1042 MonoMethod*
1043 mono_gc_get_write_barrier (void)
1044 {
1045         g_assert_not_reached ();
1046         return NULL;
1047 }
1048
1049 #else
1050
1051 static G_GNUC_UNUSED gboolean
1052 mono_gc_is_critical_method (MonoMethod *method)
1053 {
1054         return FALSE;
1055 }
1056
1057 MonoMethod*
1058 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box)
1059 {
1060         return NULL;
1061 }
1062
1063 MonoMethod*
1064 mono_gc_get_managed_array_allocator (MonoClass *klass)
1065 {
1066         return NULL;
1067 }
1068
1069 MonoMethod*
1070 mono_gc_get_managed_allocator_by_type (int atype)
1071 {
1072         return NULL;
1073 }
1074
1075 guint32
1076 mono_gc_get_managed_allocator_types (void)
1077 {
1078         return 0;
1079 }
1080
1081 MonoMethod*
1082 mono_gc_get_write_barrier (void)
1083 {
1084         g_assert_not_reached ();
1085         return NULL;
1086 }
1087
1088 #endif
1089
1090 const char *
1091 mono_gc_get_gc_name (void)
1092 {
1093         return "boehm";
1094 }
1095
1096 void*
1097 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1098 {
1099         return GC_call_with_alloc_lock (func, data);
1100 }
1101
1102 char*
1103 mono_gc_get_description (void)
1104 {
1105         return g_strdup (DEFAULT_GC_NAME);
1106 }
1107
1108 void
1109 mono_gc_set_desktop_mode (void)
1110 {
1111         GC_dont_expand = 1;
1112 }
1113
1114 gboolean
1115 mono_gc_is_moving (void)
1116 {
1117         return FALSE;
1118 }
1119
1120 gboolean
1121 mono_gc_is_disabled (void)
1122 {
1123         if (GC_dont_gc || g_getenv ("GC_DONT_GC"))
1124                 return TRUE;
1125         else
1126                 return FALSE;
1127 }
1128
1129 void
1130 mono_gc_wbarrier_value_copy_bitmap (gpointer _dest, gpointer _src, int size, unsigned bitmap)
1131 {
1132         g_assert_not_reached ();
1133 }
1134
1135
1136 guint8*
1137 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1138 {
1139         g_assert_not_reached ();
1140         return NULL;
1141 }
1142
1143 gboolean
1144 mono_gc_card_table_nursery_check (void)
1145 {
1146         g_assert_not_reached ();
1147         return TRUE;
1148 }
1149
1150 void*
1151 mono_gc_get_nursery (int *shift_bits, size_t *size)
1152 {
1153         return NULL;
1154 }
1155
1156 void
1157 mono_gc_set_current_thread_appdomain (MonoDomain *domain)
1158 {
1159 }
1160
1161 gboolean
1162 mono_gc_precise_stack_mark_enabled (void)
1163 {
1164         return FALSE;
1165 }
1166
1167 FILE *
1168 mono_gc_get_logfile (void)
1169 {
1170         return NULL;
1171 }
1172
1173 void
1174 mono_gc_conservatively_scan_area (void *start, void *end)
1175 {
1176         g_assert_not_reached ();
1177 }
1178
1179 void *
1180 mono_gc_scan_object (void *obj, void *gc_data)
1181 {
1182         g_assert_not_reached ();
1183         return NULL;
1184 }
1185
1186 gsize*
1187 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1188 {
1189         g_assert_not_reached ();
1190         return NULL;
1191 }
1192
1193 void
1194 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1195 {
1196 }
1197
1198 void
1199 mono_gc_set_stack_end (void *stack_end)
1200 {
1201 }
1202
1203 void mono_gc_set_skip_thread (gboolean value)
1204 {
1205 }
1206
1207 void
1208 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1209 {
1210         guint offset = 0;
1211
1212 #ifndef GC_DEBUG
1213         /* This assertion is not valid when GC_DEBUG is defined */
1214         g_assert (GC_base (obj) == (char*)obj - offset);
1215 #endif
1216
1217         GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1218 }
1219
1220 /*
1221  * These will call the redefined versions in libgc.
1222  */
1223
1224 #ifndef HOST_WIN32
1225
1226 int
1227 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1228 {
1229         return pthread_create (new_thread, attr, start_routine, arg);
1230 }
1231
1232 int
1233 mono_gc_pthread_join (pthread_t thread, void **retval)
1234 {
1235         return pthread_join (thread, retval);
1236 }
1237
1238 int
1239 mono_gc_pthread_detach (pthread_t thread)
1240 {
1241         return pthread_detach (thread);
1242 }
1243
1244 void
1245 mono_gc_pthread_exit (void *retval)
1246 {
1247         pthread_exit (retval);
1248         g_assert_not_reached ();
1249 }
1250
1251 #endif
1252
1253 #ifdef HOST_WIN32
1254 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1255 {
1256 #ifdef USE_INCLUDED_LIBGC
1257         return GC_DllMain (module_handle, reason, reserved);
1258 #else
1259         return TRUE;
1260 #endif
1261 }
1262 #endif
1263
1264 guint
1265 mono_gc_get_vtable_bits (MonoClass *class)
1266 {
1267         if (fin_callbacks.is_class_finalization_aware) {
1268                 if (fin_callbacks.is_class_finalization_aware (class))
1269                         return BOEHM_GC_BIT_FINALIZER_AWARE;
1270         }
1271         return 0;
1272 }
1273
1274 /*
1275  * mono_gc_register_altstack:
1276  *
1277  *   Register the dimensions of the normal stack and altstack with the collector.
1278  * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1279  */
1280 void
1281 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1282 {
1283 #ifdef USE_INCLUDED_LIBGC
1284         GC_register_altstack (stack, stack_size, altstack, altstack_size);
1285 #endif
1286 }
1287
1288 int
1289 mono_gc_get_los_limit (void)
1290 {
1291         return G_MAXINT;
1292 }
1293
1294 gboolean
1295 mono_gc_user_markers_supported (void)
1296 {
1297         return FALSE;
1298 }
1299
1300 void *
1301 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1302 {
1303         g_assert_not_reached ();
1304         return NULL;
1305 }
1306
1307 gboolean
1308 mono_gc_set_allow_synchronous_major (gboolean flag)
1309 {
1310         return flag;
1311 }
1312 /* Toggleref support */
1313
1314 void
1315 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1316 {
1317         GC_toggleref_add ((GC_PTR)object, (int)strong_ref);
1318 }
1319
1320 void
1321 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1322 {
1323         GC_toggleref_register_callback ((int (*) (GC_PTR obj)) proccess_toggleref);
1324 }
1325
1326 /* Test support code */
1327
1328 static MonoToggleRefStatus
1329 test_toggleref_callback (MonoObject *obj)
1330 {
1331         static MonoClassField *mono_toggleref_test_field;
1332         int status = MONO_TOGGLE_REF_DROP;
1333
1334         if (!mono_toggleref_test_field) {
1335                 mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1336                 g_assert (mono_toggleref_test_field);
1337         }
1338
1339         mono_field_get_value (obj, mono_toggleref_test_field, &status);
1340         printf ("toggleref-cb obj %d\n", status);
1341         return status;
1342 }
1343
1344 static void
1345 register_test_toggleref_callback (void)
1346 {
1347         mono_gc_toggleref_register_callback (test_toggleref_callback);
1348 }
1349
1350 static gboolean
1351 is_finalization_aware (MonoObject *obj)
1352 {
1353         MonoVTable *vt = obj->vtable;
1354         return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1355 }
1356
1357 static void
1358 fin_notifier (MonoObject *obj)
1359 {
1360         if (is_finalization_aware (obj))
1361                 fin_callbacks.object_queued_for_finalization (obj);
1362 }
1363
1364 void
1365 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1366 {
1367         if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1368                 g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1369
1370         fin_callbacks = *callbacks;
1371
1372         GC_set_finalizer_notify_proc ((void (*) (GC_PTR))fin_notifier);
1373 }
1374
1375 #endif /* no Boehm GC */