2009-02-10 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / sgen-gc.c
1 /*
2  * sgen-gc.c: Simple generational GC.
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *
7  * Copyright 2005-2009 Novell, Inc (http://www.novell.com)
8  *
9  * Thread start/stop adapted from Boehm's GC:
10  * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
11  * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
12  * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
13  * Copyright (c) 2000-2004 by Hewlett-Packard Company.  All rights reserved.
14  *
15  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
17  *
18  * Permission is hereby granted to use or copy this program
19  * for any purpose,  provided the above notices are retained on all copies.
20  * Permission to modify the code and to distribute modified code is granted,
21  * provided the above notices are retained, and a notice that the code was
22  * modified is included with the above copyright notice.
23  *
24  * All the rest of the code is LGPL.
25  *
26  * Important: allocation provides always zeroed memory, having to do
27  * a memset after allocation is deadly for performance.
28  * Memory usage at startup is currently as follows:
29  * 64 KB pinned space
30  * 64 KB internal space
31  * size of nursery
32  * We should provide a small memory config with half the sizes
33  *
34  * We currently try to make as few mono assumptions as possible:
35  * 1) 2-word header with no GC pointers in it (first vtable, second to store the
36  *    forwarding ptr)
37  * 2) gc descriptor is the second word in the vtable (first word in the class)
38  * 3) 8 byte alignment is the minimum and enough (not true for special structures, FIXME)
39  * 4) there is a function to get an object's size and the number of
40  *    elements in an array.
41  * 5) we know the special way bounds are allocated for complex arrays
42  *
43  * Always try to keep stack usage to a minimum: no recursive behaviour
44  * and no large stack allocs.
45  *
46  * General description.
47  * Objects are initially allocated in a nursery using a fast bump-pointer technique.
48  * When the nursery is full we start a nursery collection: this is performed with a
49  * copying GC.
50  * When the old generation is full we start a copying GC of the old generation as well:
51  * this will be changed to mark/compact in the future.
52  * The things that complicate this description are:
53  * *) pinned objects: we can't move them so we need to keep track of them
54  * *) no precise info of the thread stacks and registers: we need to be able to
55  *    quickly find the objects that may be referenced conservatively and pin them
56  *    (this makes the first issues more important)
57  * *) large objects are too expensive to be dealt with using copying GC: we handle them
58  *    with mark/sweep during major collections
59  * *) some objects need to not move even if they are small (interned strings, Type handles):
60  *    we use mark/sweep for them, too: they are not allocated in the nursery, but inside
61  *    PinnedChunks regions
62  */
63
64 /*
65  * TODO:
66  *) change the jit to emit write barrier calls when needed (we
67   can have specialized write barriers): done with icalls, still need to
68   use some specialized barriers
69  *) we could have a function pointer in MonoClass to implement
70   customized write barriers for value types
71  *) the write barrier code could be isolated in a couple of functions: when a
72   thread is stopped if it's inside the barrier it is let go again
73   until we stop outside of them (not really needed, see below GC-safe points)
74  *) investigate the stuff needed to advance a thread to a GC-safe
75   point (single-stepping, read from unmapped memory etc) and implement it
76   Not needed yet: since we treat the objects reachable from the stack/regs as
77   roots, we store the ptr and exec the write barrier so there is no race.
78   We may need this to solve the issue with setting the length of arrays and strings.
79   We may need this also for handling precise info on stacks, even simple things
80   as having uninitialized data on the stack and having to wait for the prolog
81   to zero it. Not an issue for the last frame that we scan conservatively.
82   We could always not trust the value in the slots anyway.
83  *) make the jit info table lock free
84  *) modify the jit to save info about references in stack locations:
85   this can be done just for locals as a start, so that at least
86   part of the stack is handled precisely.
87  *) Make the debug printf stuff thread and signal safe.
88  *) test/fix 64 bit issues
89  *) test/fix endianess issues
90  *) port to non-Linux
91  *) add batch moving profile info
92  *) add more timing info
93  *) there is a possible race when an array or string is created: the vtable is set,
94     but the length is set only later so if the GC needs to scan the object in that window,
95     it won't get the correct size for the object. The object can't have references and it will
96     be pinned, but a free memory fragment may be created that overlaps with it.
97     We should change the array max_length field to be at the same offset as the string length:
98     this way we can have a single special alloc function for them that sets the length.
99     Multi-dim arrays have the same issue for rank == 1 for the bounds data.
100  *) implement a card table as the write barrier instead of remembered sets?
101  *) some sort of blacklist support?
102  *) fin_ready_list is part of the root set, too
103  *) consider lowering the large object min size to 16/32KB or so and benchmark
104  *) once mark-compact is implemented we could still keep the
105     copying collector for the old generation and use it if we think
106     it is better (small heaps and no pinning object in the old
107     generation)
108   *) avoid the memory store from copy_object when not needed.
109   *) optimize the write barriers fastpath to happen in managed code
110   *) add an option to mmap the whole heap in one chunk: it makes for many
111      simplifications in the checks (put the nursery at the top and just use a single
112      check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
113      not flexible (too much of the address space may be used by default or we can't
114      increase the heap as needed) and we'd need a race-free mechanism to return memory
115      back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
116      was written to, munmap is needed, but the following mmap may not find the same segment
117      free...)
118    *) memzero the fragments after restarting the world and optionally a smaller chunk at a time
119    *) an additional strategy to realloc/expand the nursery when fully pinned is to start
120       allocating objects in the old generation. This means that we can't optimize away write
121       barrier calls in ctors (but that is not valid for other reasons, too).
122    *) add write barriers to the Clone methods
123  */
124 #include "config.h"
125 #ifdef HAVE_SGEN_GC
126
127 #include <unistd.h>
128 #include <stdio.h>
129 #include <string.h>
130 #include <semaphore.h>
131 #include <signal.h>
132 #include <errno.h>
133 #include <assert.h>
134 #include <sys/types.h>
135 #include <sys/stat.h>
136 #include <sys/mman.h>
137 #include <sys/time.h>
138 #include <time.h>
139 #include <fcntl.h>
140 #include "metadata/metadata-internals.h"
141 #include "metadata/class-internals.h"
142 #include "metadata/gc-internal.h"
143 #include "metadata/object-internals.h"
144 #include "metadata/threads.h"
145 #include "metadata/sgen-gc.h"
146 #include "metadata/mono-gc.h"
147 #include "metadata/method-builder.h"
148 #include "metadata/profiler-private.h"
149 #include "utils/mono-mmap.h"
150
151 #ifdef HAVE_VALGRIND_MEMCHECK_H
152 #include <valgrind/memcheck.h>
153 #endif
154
155 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
156         a = i,
157
158 enum {
159 #include "mono/cil/opcode.def"
160         CEE_LAST
161 };
162
163 #undef OPDEF
164
165 /*
166  * ######################################################################
167  * ########  Types and constants used by the GC.
168  * ######################################################################
169  */
170 #if SIZEOF_VOID_P == 4
171 typedef guint32 mword;
172 #else
173 typedef guint64 mword;
174 #endif
175
176 static int gc_initialized = 0;
177 static int gc_debug_level = 0;
178 static FILE* gc_debug_file;
179 /* If set, do a minor collection before every allocation */
180 static gboolean collect_before_allocs = FALSE;
181 /* If set, do a heap consistency check before each minor collection */
182 static gboolean consistency_check_at_minor_collection = FALSE;
183
184 /*
185 void
186 mono_gc_flush_info (void)
187 {
188         fflush (gc_debug_file);
189 }
190 */
191
192 #define MAX_DEBUG_LEVEL 8
193 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
194
195 #define TV_DECLARE(name) struct timeval name
196 #define TV_GETTIME(tv) gettimeofday (&(tv), NULL)
197 #define TV_ELAPSED(start,end) (int)((((end).tv_sec - (start).tv_sec) * 1000000) + end.tv_usec - start.tv_usec)
198
199 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
200
201 enum {
202         MEMORY_ROLE_GEN0,
203         MEMORY_ROLE_GEN1,
204         MEMORY_ROLE_GEN2,
205         MEMORY_ROLE_FIXED,
206         MEMORY_ROLE_INTERNAL
207 };
208
209 /* each request from the OS ends up in a GCMemSection */
210 typedef struct _GCMemSection GCMemSection;
211 struct _GCMemSection {
212         GCMemSection *next;
213         char *data;
214         mword size;
215         /* pointer where more data could be allocated if it fits */
216         char *next_data;
217         char *end_data;
218         /* 
219          * scan starts is an array of pointers to objects equally spaced in the allocation area
220          * They let use quickly find pinned objects from pinning pointers.
221          */
222         char **scan_starts;
223         /* in major collections indexes in the pin_queue for objects that pin this section */
224         int pin_queue_start;
225         int pin_queue_end;
226         unsigned short num_scan_start;
227         unsigned char role;
228 };
229
230 /* large object space struct: 64+ KB */
231 /* we could make this limit much smaller to avoid memcpy copy
232  * and potentially have more room in the GC descriptor: need to measure
233  * This also means that such small OS objects will need to be
234  * allocated in a different way (using pinned chunks).
235  * We may want to put large but smaller than 64k objects in the fixed space
236  * when we move the object from one generation to another (to limit the
237  * pig in the snake effect).
238  * Note: it may be worth to have an optimized copy function, since we can
239  * assume that objects are aligned and have a multiple of 8 size.
240  * FIXME: This structure needs to be a multiple of 8 bytes in size: this is not
241  * true if MONO_ZERO_LEN_ARRAY is nonzero.
242  */
243 typedef struct _LOSObject LOSObject;
244 struct _LOSObject {
245         LOSObject *next;
246         mword size; /* this is the object size */
247         int dummy; /* to have a sizeof (LOSObject) a multiple of ALLOC_ALIGN  and data starting at same alignment */
248         guint16 role;
249         guint16 scanned;
250         char data [MONO_ZERO_LEN_ARRAY];
251 };
252
253 /* Pinned objects are allocated in the LOS space if bigger than half a page
254  * or from freelists otherwise. We assume that pinned objects are relatively few
255  * and they have a slow dying speed (like interned strings, thread objects).
256  * As such they will be collected only at major collections.
257  * free lists are not global: when we need memory we allocate a PinnedChunk.
258  * Each pinned chunk is made of several pages, the first of wich is used
259  * internally for bookeeping (here think of a page as 4KB). The bookeeping
260  * includes the freelists vectors and info about the object size of each page
261  * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
262  * a size is assigned to it, the page is divided in the proper chunks and each
263  * chunk is added to the freelist. To not waste space, the remaining space in the
264  * first page is used as objects of size 16 or 32 (need to measure which are more
265  * common).
266  * We use this same structure to allocate memory used internally by the GC, so
267  * we never use malloc/free if we need to alloc during collection: the world is stopped
268  * and malloc/free will deadlock.
269  * When we want to iterate over pinned objects, we just scan a page at a time
270  * linearly according to the size of objects in the page: the next pointer used to link
271  * the items in the freelist uses the same word as the vtable. Since we keep freelists
272  * for each pinned chunk, if the word points outside the pinned chunk it means
273  * it is an object.
274  * We could avoid this expensive scanning in creative ways. We could have a policy
275  * of putting in the pinned space only objects we know about that have no struct fields
276  * with references and we can easily use a even expensive write barrier for them,
277  * since pointer writes on such objects should be rare.
278  * The best compromise is to just alloc interned strings and System.MonoType in them.
279  * It would be nice to allocate MonoThread in it, too: must check that we properly
280  * use write barriers so we don't have to do any expensive scanning of the whole pinned
281  * chunk list during minor collections. We can avoid it now because we alloc in it only
282  * reference-free objects.
283  */
284 #define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
285 #define MAX_FREELIST_SIZE 2048
286 #define PINNED_PAGE_SIZE (4096)
287 #define PINNED_CHUNK_MIN_SIZE (4096*8)
288 typedef struct _PinnedChunk PinnedChunk;
289 struct _PinnedChunk {
290         PinnedChunk *next;
291         int num_pages;
292         int *page_sizes; /* a 0 means the page is still unused */
293         void **free_list;
294         void *start_data;
295         void *data [1]; /* page sizes and free lists are stored here */
296 };
297
298 /* The method used to clear the nursery */
299 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
300  * Clearing at TLAB creation is much faster, but more complex and it might expose hard
301  * to find bugs.
302  */
303 typedef enum {
304         CLEAR_AT_GC,
305         CLEAR_AT_TLAB_CREATION
306 } NurseryClearPolicy;
307
308 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
309
310 /* 
311  * If this is set, the nursery is aligned to an address aligned to its size, ie.
312  * a 1MB nursery will be aligned to an address divisible by 1MB. This allows us to
313  * speed up ptr_in_nursery () checks which are very frequent. This requires the
314  * nursery size to be a compile time constant.
315  */
316 #define ALIGN_NURSERY 1
317
318 /*
319  * The young generation is divided into fragments. This is because
320  * we can hand one fragments to a thread for lock-less fast alloc and
321  * because the young generation ends up fragmented anyway by pinned objects.
322  * Once a collection is done, a list of fragments is created. When doing
323  * thread local alloc we use smallish nurseries so we allow new threads to
324  * allocate memory from gen0 without triggering a collection. Threads that
325  * are found to allocate lots of memory are given bigger fragments. This
326  * should make the finalizer thread use little nursery memory after a while.
327  * We should start assigning threads very small fragments: if there are many
328  * threads the nursery will be full of reserved space that the threads may not
329  * use at all, slowing down allocation speed.
330  * Thread local allocation is done from areas of memory Hotspot calls Thread Local 
331  * Allocation Buffers (TLABs).
332  */
333 typedef struct _Fragment Fragment;
334
335 struct _Fragment {
336         Fragment *next;
337         char *fragment_start;
338         char *fragment_limit; /* the current soft limit for allocation */
339         char *fragment_end;
340 };
341
342 /* the runtime can register areas of memory as roots: we keep two lists of roots,
343  * a pinned root set for conservatively scanned roots and a normal one for
344  * precisely scanned roots (currently implemented as a single list).
345  */
346 typedef struct _RootRecord RootRecord;
347 struct _RootRecord {
348         RootRecord *next;
349         char *start_root;
350         char *end_root;
351         mword root_desc;
352 };
353
354 /* for use with write barriers */
355 typedef struct _RememberedSet RememberedSet;
356 struct _RememberedSet {
357         mword *store_next;
358         mword *end_set;
359         RememberedSet *next;
360         mword data [MONO_ZERO_LEN_ARRAY];
361 };
362
363 /* we have 4 possible values in the low 2 bits */
364 enum {
365         REMSET_LOCATION, /* just a pointer to the exact location */
366         REMSET_RANGE,    /* range of pointer fields */
367         REMSET_OBJECT,   /* mark all the object for scanning */
368         REMSET_OTHER,    /* all others */
369         REMSET_TYPE_MASK = 0x3
370 };
371
372 /* Subtypes of REMSET_OTHER */
373 enum {
374         REMSET_VTYPE, /* a valuetype described by a gc descriptor */
375         REMSET_ROOT_LOCATION, /* a location inside a root */
376 };
377
378 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
379 static pthread_key_t remembered_set_key;
380 static RememberedSet *global_remset;
381 //static int store_to_global_remset = 0;
382
383 /* FIXME: later choose a size that takes into account the RememberedSet struct
384  * and doesn't waste any alloc paddin space.
385  */
386 #define DEFAULT_REMSET_SIZE 1024
387 static RememberedSet* alloc_remset (int size, gpointer id);
388
389 /* Structure that corresponds to a MonoVTable: desc is a mword so requires
390  * no cast from a pointer to an integer
391  */
392 typedef struct {
393         MonoClass *klass;
394         mword desc;
395 } GCVTable;
396
397 /* these bits are set in the object vtable: we could merge them since an object can be
398  * either pinned or forwarded but not both.
399  * We store them in the vtable slot because the bits are used in the sync block for
400  * other purposes: if we merge them and alloc the sync blocks aligned to 8 bytes, we can change
401  * this and use bit 3 in the syncblock (with the lower two bits both set for forwarded, that
402  * would be an invalid combination for the monitor and hash code).
403  * The values are already shifted.
404  * The forwarding address is stored in the sync block.
405  */
406 #define FORWARDED_BIT 1
407 #define PINNED_BIT 2
408 #define VTABLE_BITS_MASK 0x3
409
410 /* returns NULL if not forwarded, or the forwarded address */
411 #define object_is_forwarded(obj) (((mword*)(obj))[0] & FORWARDED_BIT? (void*)(((mword*)(obj))[1]): NULL)
412 /* set the forwarded address fw_addr for object obj */
413 #define forward_object(obj,fw_addr) do {        \
414                 ((mword*)(obj))[0] |= FORWARDED_BIT;    \
415                 ((mword*)(obj))[1] = (mword)(fw_addr);  \
416         } while (0)
417
418 #define object_is_pinned(obj) (((mword*)(obj))[0] & PINNED_BIT)
419 #define pin_object(obj) do {    \
420                 ((mword*)(obj))[0] |= PINNED_BIT;       \
421         } while (0)
422 #define unpin_object(obj) do {  \
423                 ((mword*)(obj))[0] &= ~PINNED_BIT;      \
424         } while (0)
425
426 #ifdef ALIGN_NURSERY
427 #define ptr_in_nursery(ptr) (((mword)(ptr) & ~((1 << DEFAULT_NURSERY_BITS) - 1)) == (mword)nursery_start)
428 #else
429 #define ptr_in_nursery(ptr) ((char*)(ptr) >= nursery_start && (char*)(ptr) < nursery_real_end)
430 #endif
431
432 /*
433  * Since we set bits in the vtable, use the macro to load it from the pointer to
434  * an object that is potentially pinned.
435  */
436 #define LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~VTABLE_BITS_MASK)
437
438 static const char*
439 safe_name (void* obj)
440 {
441         MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
442         return vt->klass->name;
443 }
444
445 static inline guint
446 safe_object_get_size (MonoObject* o)
447 {
448         MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
449         if (klass == mono_defaults.string_class) {
450                 return sizeof (MonoString) + 2 * mono_string_length ((MonoString*) o) + 2;
451         } else if (klass->rank) {
452                 MonoArray *array = (MonoArray*)o;
453                 size_t size = sizeof (MonoArray) + mono_array_element_size (klass) * mono_array_length (array);
454                 if (G_UNLIKELY (array->bounds)) {
455                         size += 3;
456                         size &= ~3;
457                         size += sizeof (MonoArrayBounds) * klass->rank;
458                 }
459                 return size;
460         } else {
461                 /* from a created object: the class must be inited already */
462                 return klass->instance_size;
463         }
464 }
465
466 static inline gboolean
467 is_maybe_half_constructed (MonoObject *o)
468 {
469         MonoClass *klass;
470
471         klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
472         if ((klass == mono_defaults.string_class && mono_string_length ((MonoString*)o) == 0) ||
473                 (klass->rank && mono_array_length ((MonoArray*)o) == 0))
474                 return TRUE;
475         else
476                 return FALSE;
477 }
478
479 /*
480  * ######################################################################
481  * ########  Global data.
482  * ######################################################################
483  */
484 static LOCK_DECLARE (gc_mutex);
485 static int gc_disabled = 0;
486 static int num_minor_gcs = 0;
487 static int num_major_gcs = 0;
488
489 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
490 //#define DEFAULT_NURSERY_SIZE (1024*512*125+4096*118)
491 #define DEFAULT_NURSERY_SIZE (1024*512*2)
492 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
493 #define DEFAULT_NURSERY_BITS 20
494 #define DEFAULT_MAX_SECTION (DEFAULT_NURSERY_SIZE * 16)
495 #define DEFAULT_LOS_COLLECTION_TARGET (DEFAULT_NURSERY_SIZE * 2)
496 /* to quickly find the head of an object pinned by a conservative address
497  * we keep track of the objects allocated for each SCAN_START_SIZE memory
498  * chunk in the nursery or other memory sections. Larger values have less
499  * memory overhead and bigger runtime cost. 4-8 KB are reasonable values.
500  */
501 #define SCAN_START_SIZE (4096*2)
502 /* the minimum size of a fragment that we consider useful for allocation */
503 #define FRAGMENT_MIN_SIZE (512)
504 /* This is a fixed value used for pinned chunks, not the system pagesize */
505 #define FREELIST_PAGESIZE 4096
506
507 static mword pagesize = 4096;
508 static mword nursery_size = DEFAULT_NURSERY_SIZE;
509 static mword next_section_size = DEFAULT_NURSERY_SIZE * 4;
510 static mword max_section_size = DEFAULT_MAX_SECTION;
511 static int section_size_used = 0;
512 static int degraded_mode = 0;
513
514 static LOSObject *los_object_list = NULL;
515 static mword los_memory_usage = 0;
516 static mword los_num_objects = 0;
517 static mword next_los_collection = 2*1024*1024; /* 2 MB, need to tune */
518 static mword total_alloc = 0;
519 /* use this to tune when to do a major/minor collection */
520 static mword memory_pressure = 0;
521
522 static GCMemSection *section_list = NULL;
523 static GCMemSection *nursery_section = NULL;
524 static mword lowest_heap_address = ~(mword)0;
525 static mword highest_heap_address = 0;
526
527 typedef struct _FinalizeEntry FinalizeEntry;
528 struct _FinalizeEntry {
529         FinalizeEntry *next;
530         void *object;
531         void *data; /* can be a disappearing link or the data for the finalizer */
532         /* Note we could use just one pointer if we don't support multiple callbacks
533          * for finalizers and per-finalizer data and if we store the obj pointers
534          * in the link like libgc does
535          */
536 };
537
538 /*
539  * The finalizable hash has the object as the key, the 
540  * disappearing_link hash, has the link address as key.
541  */
542 static FinalizeEntry **finalizable_hash = NULL;
543 /* objects that are ready to be finalized */
544 static FinalizeEntry *fin_ready_list = NULL;
545 /* disappearing links use the same structure but a different list */
546 static FinalizeEntry **disappearing_link_hash = NULL;
547 static mword disappearing_link_hash_size = 0;
548 static mword finalizable_hash_size = 0;
549
550 static int num_registered_finalizers = 0;
551 static int num_ready_finalizers = 0;
552 static int num_disappearing_links = 0;
553 static int no_finalize = 0;
554
555 /* keep each size a multiple of ALLOC_ALIGN */
556 /* on 64 bit systems 8 is likely completely unused. */
557 static const int freelist_sizes [] = {
558         8, 16, 24, 32, 40, 48, 64, 80,
559         96, 128, 160, 192, 224, 256, 320, 384,
560         448, 512, 584, 680, 816, 1024, 1360, 2048};
561 #define FREELIST_NUM_SLOTS (sizeof (freelist_sizes) / sizeof (freelist_sizes [0]))
562
563 static char* max_pinned_chunk_addr = NULL;
564 static char* min_pinned_chunk_addr = (char*)-1;
565 /* pinned_chunk_list is used for allocations of objects that are never moved */
566 static PinnedChunk *pinned_chunk_list = NULL;
567 /* internal_chunk_list is used for allocating structures needed by the GC */
568 static PinnedChunk *internal_chunk_list = NULL;
569
570 static gboolean
571 obj_is_from_pinned_alloc (char *p)
572 {
573         PinnedChunk *chunk = pinned_chunk_list;
574         for (; chunk; chunk = chunk->next) {
575                 if (p >= (char*)chunk->start_data && p < ((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE))
576                         return TRUE;
577         }
578         return FALSE;
579 }
580
581 enum {
582         ROOT_TYPE_NORMAL = 0, /* "normal" roots */
583         ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
584         ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
585         ROOT_TYPE_NUM
586 };
587
588 /* registered roots: the key to the hash is the root start address */
589 /* 
590  * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
591  */
592 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
593 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
594 static mword roots_size = 0; /* amount of memory in the root set */
595 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
596
597 /* 
598  * The current allocation cursors
599  * We allocate objects in the nursery.
600  * The nursery is the area between nursery_start and nursery_real_end.
601  * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
602  * from nursery fragments.
603  * tlab_next is the pointer to the space inside the TLAB where the next object will 
604  * be allocated.
605  * tlab_temp_end is the pointer to the end of the temporary space reserved for
606  * the allocation: it allows us to set the scan starts at reasonable intervals.
607  * tlab_real_end points to the end of the TLAB.
608  * nursery_frag_real_end points to the end of the currently used nursery fragment.
609  * nursery_first_pinned_start points to the start of the first pinned object in the nursery
610  * nursery_last_pinned_end points to the end of the last pinned object in the nursery
611  * At the next allocation, the area of the nursery where objects can be present is
612  * between MIN(nursery_first_pinned_start, first_fragment_start) and
613  * MAX(nursery_last_pinned_end, nursery_frag_real_end)
614  */
615 static char *nursery_start = NULL;
616
617 /*
618  * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS 
619  * variables for next+temp_end ?
620  */
621 static __thread char *tlab_start;
622 static __thread char *tlab_next;
623 static __thread char *tlab_temp_end;
624 static __thread char *tlab_real_end;
625 /* Used by the managed allocator */
626 static __thread char **tlab_next_addr;
627 static char *nursery_next = NULL;
628 static char *nursery_frag_real_end = NULL;
629 static char *nursery_real_end = NULL;
630 //static char *nursery_first_pinned_start = NULL;
631 static char *nursery_last_pinned_end = NULL;
632
633 /* The size of a TLAB */
634 /* The bigger the value, the less often we have to go to the slow path to allocate a new 
635  * one, but the more space is wasted by threads not allocating much memory.
636  * FIXME: Tune this.
637  * FIXME: Make this self-tuning for each thread.
638  */
639 static guint32 tlab_size = (1024 * 4);
640
641 /* fragments that are free and ready to be used for allocation */
642 static Fragment *nursery_fragments = NULL;
643 /* freeelist of fragment structures */
644 static Fragment *fragment_freelist = NULL;
645
646 /* 
647  * used when moving the objects
648  * When the nursery is collected, objects are copied to to_space.
649  * The area between gray_first and gray_objects is used as a stack
650  * of objects that need their fields checked for more references
651  * to be copied.
652  * We should optimize somehow this mechanism to avoid rescanning
653  * ptr-free objects. The order is also probably not optimal: need to
654  * test cache misses and other graph traversal orders.
655  */
656 static char *to_space = NULL;
657 static char *gray_first = NULL;
658 static char *gray_objects = NULL;
659 static char *to_space_end = NULL;
660 static GCMemSection *to_space_section = NULL;
661
662 /* objects bigger then this go into the large object space */
663 #define MAX_SMALL_OBJ_SIZE 0xffff
664
665 /*
666  * ######################################################################
667  * ########  Macros and function declarations.
668  * ######################################################################
669  */
670
671 #define UPDATE_HEAP_BOUNDARIES(low,high) do {   \
672                 if ((mword)(low) < lowest_heap_address) \
673                         lowest_heap_address = (mword)(low);     \
674                 if ((mword)(high) > highest_heap_address)       \
675                         highest_heap_address = (mword)(high);   \
676         } while (0)
677
678 inline static void*
679 align_pointer (void *ptr)
680 {
681         mword p = (mword)ptr;
682         p += sizeof (gpointer) - 1;
683         p &= ~ (sizeof (gpointer) - 1);
684         return (void*)p;
685 }
686
687 /* forward declarations */
688 static void* get_internal_mem          (size_t size);
689 static void  free_internal_mem         (void *addr);
690 static void* get_os_memory             (size_t size, int activate);
691 static void  free_os_memory            (void *addr, size_t size);
692 static G_GNUC_UNUSED void  report_internal_mem_usage (void);
693
694 static int stop_world (void);
695 static int restart_world (void);
696 static void pin_thread_data (void *start_nursery, void *end_nursery);
697 static void scan_from_remsets (void *start_nursery, void *end_nursery);
698 static void find_pinning_ref_from_thread (char *obj, size_t size);
699 static void update_current_thread_stack (void *start);
700 static GCMemSection* alloc_section (size_t size);
701 static void finalize_in_range (char *start, char *end);
702 static void null_link_in_range (char *start, char *end);
703 static gboolean search_fragment_for_size (size_t size);
704 static void mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end);
705 static void clear_remsets (void);
706 static void clear_tlabs (void);
707 static char *find_tlab_next_from_address (char *addr);
708 static void sweep_pinned_objects (void);
709 static void scan_from_pinned_objects (char *addr_start, char *addr_end);
710 static void free_large_object (LOSObject *obj);
711 static void free_mem_section (GCMemSection *section);
712
713 void describe_ptr (char *ptr);
714 void check_consistency (void);
715 char* check_object (char *start);
716
717 /*
718  * ######################################################################
719  * ########  GC descriptors
720  * ######################################################################
721  * Used to quickly get the info the GC needs about an object: size and
722  * where the references are held.
723  */
724 /* objects are aligned to 8 bytes boundaries
725  * A descriptor is a pointer in MonoVTable, so 32 or 64 bits of size.
726  * The low 3 bits define the type of the descriptor. The other bits
727  * depend on the type.
728  * As a general rule the 13 remaining low bits define the size, either
729  * of the whole object or of the elements in the arrays. While for objects
730  * the size is already in bytes, for arrays we need to shift, because
731  * array elements might be smaller than 8 bytes. In case of arrays, we
732  * use two bits to describe what the additional high bits represents,
733  * so the default behaviour can handle element sizes less than 2048 bytes.
734  * The high 16 bits, if 0 it means the object is pointer-free.
735  * This design should make it easy and fast to skip over ptr-free data.
736  * The first 4 types should cover >95% of the objects.
737  * Note that since the size of objects is limited to 64K, larger objects
738  * will be allocated in the large object heap.
739  * If we want 4-bytes alignment, we need to put vector and small bitmap
740  * inside complex.
741  */
742 enum {
743         DESC_TYPE_RUN_LENGTH,   /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
744         DESC_TYPE_SMALL_BITMAP, /* 16 bits aligned byte size | 16-48 bit bitmap */
745         DESC_TYPE_STRING,       /* nothing */
746         DESC_TYPE_COMPLEX,      /* index for bitmap into complex_descriptors */
747         DESC_TYPE_VECTOR,       /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
748         DESC_TYPE_ARRAY,        /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
749         DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
750         DESC_TYPE_COMPLEX_ARR,  /* index for bitmap into complex_descriptors */
751         /* subtypes for arrays and vectors */
752         DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value  */
753         DESC_TYPE_V_REFS,       /* all the array elements are refs */
754         DESC_TYPE_V_RUN_LEN,    /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
755         DESC_TYPE_V_BITMAP      /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
756 };
757
758 #define OBJECT_HEADER_WORDS (sizeof(MonoObject)/sizeof(gpointer))
759 #define LOW_TYPE_BITS 3
760 #define SMALL_BITMAP_SHIFT 16
761 #define SMALL_BITMAP_SIZE (GC_BITS_PER_WORD - SMALL_BITMAP_SHIFT)
762 #define VECTOR_INFO_SHIFT 14
763 #define VECTOR_ELSIZE_SHIFT 3
764 #define LARGE_BITMAP_SIZE (GC_BITS_PER_WORD - LOW_TYPE_BITS)
765 #define MAX_SMALL_SIZE ((1 << SMALL_BITMAP_SHIFT) - 1)
766 #define SMALL_SIZE_MASK 0xfff8
767 #define MAX_ELEMENT_SIZE 0x3ff
768 #define ELEMENT_SIZE_MASK (0x3ff << LOW_TYPE_BITS)
769 #define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
770 #define VECTOR_SUBTYPE_REFS    (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
771 #define VECTOR_SUBTYPE_RUN_LEN (DESC_TYPE_V_RUN_LEN << VECTOR_INFO_SHIFT)
772 #define VECTOR_SUBTYPE_BITMAP  (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
773
774 #define ALLOC_ALIGN 8
775
776
777 /* Root bitmap descriptors are simpler: the lower three bits describe the type
778  * and we either have 30/62 bitmap bits or nibble-based run-length,
779  * or a complex descriptor, or a user defined marker function.
780  */
781 enum {
782         ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
783         ROOT_DESC_BITMAP,
784         ROOT_DESC_RUN_LEN, 
785         ROOT_DESC_COMPLEX,
786         ROOT_DESC_USER,
787         ROOT_DESC_TYPE_MASK = 0x7,
788         ROOT_DESC_TYPE_SHIFT = 3,
789 };
790
791 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
792
793 #define MAX_USER_DESCRIPTORS 16
794
795 static gsize* complex_descriptors = NULL;
796 static int complex_descriptors_size = 0;
797 static int complex_descriptors_next = 0;
798 static MonoGCMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
799 static int user_descriptors_next = 0;
800
801 static int
802 alloc_complex_descriptor (gsize *bitmap, int numbits)
803 {
804         int nwords = numbits/GC_BITS_PER_WORD + 2;
805         int res;
806         int i;
807
808         LOCK_GC;
809         res = complex_descriptors_next;
810         /* linear search, so we don't have duplicates with domain load/unload
811          * this should not be performance critical or we'd have bigger issues
812          * (the number and size of complex descriptors should be small).
813          */
814         for (i = 0; i < complex_descriptors_next; ) {
815                 if (complex_descriptors [i] == nwords) {
816                         int j, found = TRUE;
817                         for (j = 0; j < nwords - 1; ++j) {
818                                 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
819                                         found = FALSE;
820                                         break;
821                                 }
822                         }
823                         if (found) {
824                                 UNLOCK_GC;
825                                 return i;
826                         }
827                 }
828                 i += complex_descriptors [i];
829         }
830         if (complex_descriptors_next + nwords > complex_descriptors_size) {
831                 int new_size = complex_descriptors_size * 2 + nwords;
832                 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
833                 complex_descriptors_size = new_size;
834         }
835         DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
836         complex_descriptors_next += nwords;
837         complex_descriptors [res] = nwords;
838         for (i = 0; i < nwords - 1; ++i) {
839                 complex_descriptors [res + 1 + i] = bitmap [i];
840                 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
841         }
842         UNLOCK_GC;
843         return res;
844 }
845
846 /*
847  * Descriptor builders.
848  */
849 void*
850 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
851 {
852         return (void*) DESC_TYPE_STRING;
853 }
854
855 void*
856 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
857 {
858         int first_set = -1, num_set = 0, last_set = -1, i;
859         mword desc = 0;
860         size_t stored_size = obj_size;
861         stored_size += ALLOC_ALIGN - 1;
862         stored_size &= ~(ALLOC_ALIGN - 1);
863         for (i = 0; i < numbits; ++i) {
864                 if (bitmap [i / GC_BITS_PER_WORD] & (1 << (i % GC_BITS_PER_WORD))) {
865                         if (first_set < 0)
866                                 first_set = i;
867                         last_set = i;
868                         num_set++;
869                 }
870         }
871         if (stored_size <= MAX_SMALL_OBJ_SIZE) {
872                 /* check run-length encoding first: one byte offset, one byte number of pointers
873                  * on 64 bit archs, we can have 3 runs, just one on 32.
874                  * It may be better to use nibbles.
875                  */
876                 if (first_set < 0) {
877                         desc = DESC_TYPE_RUN_LENGTH | stored_size;
878                         DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
879                         return (void*) desc;
880                 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
881                         desc = DESC_TYPE_RUN_LENGTH | stored_size | (first_set << 16) | (num_set << 24);
882                         DEBUG (6, fprintf (gc_debug_file, "Runlen descriptor %p, size: %zd, first set: %d, num set: %d\n", (void*)desc, stored_size, first_set, num_set));
883                         return (void*) desc;
884                 }
885                 /* we know the 2-word header is ptr-free */
886                 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
887                         desc = DESC_TYPE_SMALL_BITMAP | stored_size | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
888                         DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
889                         return (void*) desc;
890                 }
891         }
892         /* we know the 2-word header is ptr-free */
893         if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
894                 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
895                 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
896                 return (void*) desc;
897         }
898         /* it's a complex object ... */
899         desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
900         return (void*) desc;
901 }
902
903 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
904 void*
905 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
906 {
907         int first_set = -1, num_set = 0, last_set = -1, i;
908         mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
909         for (i = 0; i < numbits; ++i) {
910                 if (elem_bitmap [i / GC_BITS_PER_WORD] & (1 << (i % GC_BITS_PER_WORD))) {
911                         if (first_set < 0)
912                                 first_set = i;
913                         last_set = i;
914                         num_set++;
915                 }
916         }
917         if (elem_size <= MAX_ELEMENT_SIZE) {
918                 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
919                 if (!num_set) {
920                         return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
921                 }
922                 /* Note: we also handle structs with just ref fields */
923                 if (num_set * sizeof (gpointer) == elem_size) {
924                         return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
925                 }
926                 /* FIXME: try run-len first */
927                 /* Note: we can't skip the object header here, because it's not present */
928                 if (last_set <= SMALL_BITMAP_SIZE) {
929                         return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
930                 }
931         }
932         /* it's am array of complex structs ... */
933         desc = DESC_TYPE_COMPLEX_ARR;
934         desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
935         return (void*) desc;
936 }
937
938 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
939 #define STRING_SIZE(size,str) do {      \
940                 (size) = sizeof (MonoString) + 2 * (mono_string_length ((MonoString*)(str)) + 1);       \
941                 (size) += (ALLOC_ALIGN - 1);    \
942                 (size) &= ~(ALLOC_ALIGN - 1);   \
943         } while (0)
944
945 #define OBJ_RUN_LEN_SIZE(size,desc,obj) do { \
946         (size) = (desc) & 0xfff8; \
947     } while (0)
948
949 #define OBJ_BITMAP_SIZE(size,desc,obj) do { \
950         (size) = (desc) & 0xfff8; \
951     } while (0)
952
953 //#define PREFETCH(addr) __asm__ __volatile__ ("     prefetchnta     %0": : "m"(*(char *)(addr)))
954 #define PREFETCH(addr)
955
956 /* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
957 #define OBJ_RUN_LEN_FOREACH_PTR(desc,obj)       do {    \
958                 if ((desc) & 0xffff0000) {      \
959                         /* there are pointers */        \
960                         void **_objptr_end;     \
961                         void **_objptr = (void**)(obj); \
962                         _objptr += ((desc) >> 16) & 0xff;       \
963                         _objptr_end = _objptr + (((desc) >> 24) & 0xff);        \
964                         while (_objptr < _objptr_end) { \
965                                 HANDLE_PTR (_objptr, (obj));    \
966                                 _objptr++;      \
967                         }       \
968                 }       \
969         } while (0)
970
971 /* a bitmap desc means that there are pointer references or we'd have
972  * choosen run-length, instead: add an assert to check.
973  */
974 #define OBJ_BITMAP_FOREACH_PTR(desc,obj)        do {    \
975                 /* there are pointers */        \
976                 void **_objptr = (void**)(obj); \
977                 gsize _bmap = (desc) >> 16;     \
978                 _objptr += OBJECT_HEADER_WORDS; \
979                 while (_bmap) { \
980                         if ((_bmap & 1)) {      \
981                                 HANDLE_PTR (_objptr, (obj));    \
982                         }       \
983                         _bmap >>= 1;    \
984                         ++_objptr;      \
985                 }       \
986         } while (0)
987
988 #define OBJ_LARGE_BITMAP_FOREACH_PTR(vt,obj)    do {    \
989                 /* there are pointers */        \
990                 void **_objptr = (void**)(obj); \
991                 gsize _bmap = (vt)->desc >> LOW_TYPE_BITS;      \
992                 _objptr += OBJECT_HEADER_WORDS; \
993                 while (_bmap) { \
994                         if ((_bmap & 1)) {      \
995                                 HANDLE_PTR (_objptr, (obj));    \
996                         }       \
997                         _bmap >>= 1;    \
998                         ++_objptr;      \
999                 }       \
1000         } while (0)
1001
1002 #define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do {    \
1003                 /* there are pointers */        \
1004                 void **_objptr = (void**)(obj); \
1005                 gsize *bitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS);       \
1006                 int bwords = (*bitmap_data) - 1;        \
1007                 void **start_run = _objptr;     \
1008                 bitmap_data++;  \
1009                 if (0) {        \
1010                         MonoObject *myobj = (MonoObject*)obj;   \
1011                         g_print ("found %d at %p (0x%zx): %s.%s\n", bwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name);   \
1012                 }       \
1013                 while (bwords-- > 0) {  \
1014                         gsize _bmap = *bitmap_data++;   \
1015                         _objptr = start_run;    \
1016                         /*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/        \
1017                         while (_bmap) { \
1018                                 if ((_bmap & 1)) {      \
1019                                         HANDLE_PTR (_objptr, (obj));    \
1020                                 }       \
1021                                 _bmap >>= 1;    \
1022                                 ++_objptr;      \
1023                         }       \
1024                         start_run += GC_BITS_PER_WORD;  \
1025                 }       \
1026         } while (0)
1027
1028 /* this one is untested */
1029 #define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj)     do {    \
1030                 /* there are pointers */        \
1031                 gsize *mbitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS);      \
1032                 int mbwords = (*mbitmap_data++) - 1;    \
1033                 int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass);    \
1034                 char *e_start = (char*)(obj) +  G_STRUCT_OFFSET (MonoArray, vector);    \
1035                 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
1036                 if (0) {        \
1037                         MonoObject *myobj = (MonoObject*)start; \
1038                         g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name);  \
1039                 }       \
1040                 while (e_start < e_end) {       \
1041                         void **_objptr = (void**)e_start;       \
1042                         gsize *bitmap_data = mbitmap_data;      \
1043                         unsigned int bwords = mbwords;  \
1044                         while (bwords-- > 0) {  \
1045                                 gsize _bmap = *bitmap_data++;   \
1046                                 void **start_run = _objptr;     \
1047                                 /*g_print ("bitmap: 0x%x\n", _bmap);*/  \
1048                                 while (_bmap) { \
1049                                         if ((_bmap & 1)) {      \
1050                                                 HANDLE_PTR (_objptr, (obj));    \
1051                                         }       \
1052                                         _bmap >>= 1;    \
1053                                         ++_objptr;      \
1054                                 }       \
1055                                 _objptr = start_run + GC_BITS_PER_WORD; \
1056                         }       \
1057                         e_start += el_size;     \
1058                 }       \
1059         } while (0)
1060
1061 #define OBJ_VECTOR_FOREACH_PTR(vt,obj)  do {    \
1062                 /* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */     \
1063                 if ((vt)->desc & 0xffffc000) {  \
1064                         int el_size = ((vt)->desc >> 3) & MAX_ELEMENT_SIZE;     \
1065                         /* there are pointers */        \
1066                         int etype = (vt)->desc & 0xc000;        \
1067                         if (etype == (DESC_TYPE_V_REFS << 14)) {        \
1068                                 void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector));        \
1069                                 void **end_refs = (void**)((char*)p + el_size * mono_array_length ((MonoArray*)(obj))); \
1070                                 /* Note: this code can handle also arrays of struct with only references in them */     \
1071                                 while (p < end_refs) {  \
1072                                         HANDLE_PTR (p, (obj));  \
1073                                         ++p;    \
1074                                 }       \
1075                         } else if (etype == DESC_TYPE_V_RUN_LEN << 14) {        \
1076                                 int offset = ((vt)->desc >> 16) & 0xff; \
1077                                 int num_refs = ((vt)->desc >> 24) & 0xff;       \
1078                                 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector);     \
1079                                 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
1080                                 while (e_start < e_end) {       \
1081                                         void **p = (void**)e_start;     \
1082                                         int i;  \
1083                                         p += offset;    \
1084                                         for (i = 0; i < num_refs; ++i) {        \
1085                                                 HANDLE_PTR (p + i, (obj));      \
1086                                         }       \
1087                                         e_start += el_size;     \
1088                                 }       \
1089                         } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
1090                                 char *e_start = (char*)(obj) +  G_STRUCT_OFFSET (MonoArray, vector);    \
1091                                 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
1092                                 while (e_start < e_end) {       \
1093                                         void **p = (void**)e_start;     \
1094                                         gsize _bmap = (vt)->desc >> 16; \
1095                                         /* Note: there is no object header here to skip */      \
1096                                         while (_bmap) { \
1097                                                 if ((_bmap & 1)) {      \
1098                                                         HANDLE_PTR (p, (obj));  \
1099                                                 }       \
1100                                                 _bmap >>= 1;    \
1101                                                 ++p;    \
1102                                         }       \
1103                                         e_start += el_size;     \
1104                                 }       \
1105                         }       \
1106                 }       \
1107         } while (0)
1108
1109 static mword new_obj_references = 0;
1110 static mword obj_references_checked = 0;
1111
1112 #undef HANDLE_PTR
1113 #define HANDLE_PTR(ptr,obj)     do {    \
1114                 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
1115                         new_obj_references++;   \
1116                         /*printf ("bogus ptr %p found at %p in object %p (%s.%s)\n", *(ptr), (ptr), o, o->vtable->klass->name_space, o->vtable->klass->name);*/ \
1117                 } else {        \
1118                         obj_references_checked++;       \
1119                 }       \
1120         } while (0)
1121
1122 /*
1123  * ######################################################################
1124  * ########  Detecting and removing garbage.
1125  * ######################################################################
1126  * This section of code deals with detecting the objects no longer in use
1127  * and reclaiming the memory.
1128  */
1129 #if 0
1130 static void __attribute__((noinline))
1131 scan_area (char *start, char *end)
1132 {
1133         GCVTable *vt;
1134         size_t skip_size;
1135         int type;
1136         int type_str = 0, type_rlen = 0, type_bitmap = 0, type_vector = 0, type_lbit = 0, type_complex = 0;
1137         mword desc;
1138         new_obj_references = 0;
1139         obj_references_checked = 0;
1140         while (start < end) {
1141                 if (!*(void**)start) {
1142                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1143                         continue;
1144                 }
1145                 vt = (GCVTable*)LOAD_VTABLE (start);
1146                 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
1147                 if (0) {
1148                         MonoObject *obj = (MonoObject*)start;
1149                         g_print ("found at %p (0x%zx): %s.%s\n", start, vt->desc, obj->vtable->klass->name_space, obj->vtable->klass->name);
1150                 }
1151                 desc = vt->desc;
1152                 type = desc & 0x7;
1153                 if (type == DESC_TYPE_STRING) {
1154                         STRING_SIZE (skip_size, start);
1155                         start += skip_size;
1156                         type_str++;
1157                         continue;
1158                 } else if (type == DESC_TYPE_RUN_LENGTH) {
1159                         OBJ_RUN_LEN_SIZE (skip_size, desc, start);
1160                         g_assert (skip_size);
1161                         OBJ_RUN_LEN_FOREACH_PTR (desc,start);
1162                         start += skip_size;
1163                         type_rlen++;
1164                         continue;
1165                 } else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
1166                         skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
1167                         skip_size *= mono_array_length ((MonoArray*)start);
1168                         skip_size += sizeof (MonoArray);
1169                         skip_size += (ALLOC_ALIGN - 1);
1170                         skip_size &= ~(ALLOC_ALIGN - 1);
1171                         OBJ_VECTOR_FOREACH_PTR (vt, start);
1172                         if (type == DESC_TYPE_ARRAY) {
1173                                 /* account for the bounds */
1174                         }
1175                         start += skip_size;
1176                         type_vector++;
1177                         continue;
1178                 } else if (type == DESC_TYPE_SMALL_BITMAP) {
1179                         OBJ_BITMAP_SIZE (skip_size, desc, start);
1180                         g_assert (skip_size);
1181                         OBJ_BITMAP_FOREACH_PTR (desc,start);
1182                         start += skip_size;
1183                         type_bitmap++;
1184                         continue;
1185                 } else if (type == DESC_TYPE_LARGE_BITMAP) {
1186                         skip_size = safe_object_get_size ((MonoObject*)start);
1187                         skip_size += (ALLOC_ALIGN - 1);
1188                         skip_size &= ~(ALLOC_ALIGN - 1);
1189                         OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
1190                         start += skip_size;
1191                         type_lbit++;
1192                         continue;
1193                 } else if (type == DESC_TYPE_COMPLEX) {
1194                         /* this is a complex object */
1195                         skip_size = safe_object_get_size ((MonoObject*)start);
1196                         skip_size += (ALLOC_ALIGN - 1);
1197                         skip_size &= ~(ALLOC_ALIGN - 1);
1198                         OBJ_COMPLEX_FOREACH_PTR (vt, start);
1199                         start += skip_size;
1200                         type_complex++;
1201                         continue;
1202                 } else if (type == DESC_TYPE_COMPLEX_ARR) {
1203                         /* this is an array of complex structs */
1204                         skip_size = mono_array_element_size (((MonoVTable*)vt)->klass);
1205                         skip_size *= mono_array_length ((MonoArray*)start);
1206                         skip_size += sizeof (MonoArray);
1207                         skip_size += (ALLOC_ALIGN - 1);
1208                         skip_size &= ~(ALLOC_ALIGN - 1);
1209                         OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
1210                         if (type == DESC_TYPE_ARRAY) {
1211                                 /* account for the bounds */
1212                         }
1213                         start += skip_size;
1214                         type_complex++;
1215                         continue;
1216                 } else {
1217                         g_assert (0);
1218                 }
1219         }
1220         /*printf ("references to new nursery %p-%p (size: %dk): %d, checked: %d\n", old_start, end, (end-old_start)/1024, new_obj_references, obj_references_checked);
1221         printf ("\tstrings: %d, runl: %d, vector: %d, bitmaps: %d, lbitmaps: %d, complex: %d\n",
1222                 type_str, type_rlen, type_vector, type_bitmap, type_lbit, type_complex);*/
1223 }
1224
1225 static void __attribute__((noinline))
1226 scan_area_for_domain (MonoDomain *domain, char *start, char *end)
1227 {
1228         GCVTable *vt;
1229         size_t skip_size;
1230         int type, remove;
1231         mword desc;
1232
1233         while (start < end) {
1234                 if (!*(void**)start) {
1235                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1236                         continue;
1237                 }
1238                 vt = (GCVTable*)LOAD_VTABLE (start);
1239                 /* handle threads someway (maybe insert the root domain vtable?) */
1240                 if (mono_object_domain (start) == domain && vt->klass != mono_defaults.thread_class) {
1241                         DEBUG (1, fprintf (gc_debug_file, "Need to cleanup object %p, (%s)\n", start, safe_name (start)));
1242                         remove = 1;
1243                 } else {
1244                         remove = 0;
1245                 }
1246                 desc = vt->desc;
1247                 type = desc & 0x7;
1248                 if (type == DESC_TYPE_STRING) {
1249                         STRING_SIZE (skip_size, start);
1250                         if (remove) memset (start, 0, skip_size);
1251                         start += skip_size;
1252                         continue;
1253                 } else if (type == DESC_TYPE_RUN_LENGTH) {
1254                         OBJ_RUN_LEN_SIZE (skip_size, desc, start);
1255                         g_assert (skip_size);
1256                         if (remove) memset (start, 0, skip_size);
1257                         start += skip_size;
1258                         continue;
1259                 } else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
1260                         skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
1261                         skip_size *= mono_array_length ((MonoArray*)start);
1262                         skip_size += sizeof (MonoArray);
1263                         skip_size += (ALLOC_ALIGN - 1);
1264                         skip_size &= ~(ALLOC_ALIGN - 1);
1265                         if (type == DESC_TYPE_ARRAY) {
1266                                 /* account for the bounds */
1267                         }
1268                         if (remove) memset (start, 0, skip_size);
1269                         start += skip_size;
1270                         continue;
1271                 } else if (type == DESC_TYPE_SMALL_BITMAP) {
1272                         OBJ_BITMAP_SIZE (skip_size, desc, start);
1273                         g_assert (skip_size);
1274                         if (remove) memset (start, 0, skip_size);
1275                         start += skip_size;
1276                         continue;
1277                 } else if (type == DESC_TYPE_LARGE_BITMAP) {
1278                         skip_size = safe_object_get_size ((MonoObject*)start);
1279                         skip_size += (ALLOC_ALIGN - 1);
1280                         skip_size &= ~(ALLOC_ALIGN - 1);
1281                         if (remove) memset (start, 0, skip_size);
1282                         start += skip_size;
1283                         continue;
1284                 } else if (type == DESC_TYPE_COMPLEX) {
1285                         /* this is a complex object */
1286                         skip_size = safe_object_get_size ((MonoObject*)start);
1287                         skip_size += (ALLOC_ALIGN - 1);
1288                         skip_size &= ~(ALLOC_ALIGN - 1);
1289                         if (remove) memset (start, 0, skip_size);
1290                         start += skip_size;
1291                         continue;
1292                 } else if (type == DESC_TYPE_COMPLEX_ARR) {
1293                         /* this is an array of complex structs */
1294                         skip_size = mono_array_element_size (((MonoVTable*)vt)->klass);
1295                         skip_size *= mono_array_length ((MonoArray*)start);
1296                         skip_size += sizeof (MonoArray);
1297                         skip_size += (ALLOC_ALIGN - 1);
1298                         skip_size &= ~(ALLOC_ALIGN - 1);
1299                         if (type == DESC_TYPE_ARRAY) {
1300                                 /* account for the bounds */
1301                         }
1302                         if (remove) memset (start, 0, skip_size);
1303                         start += skip_size;
1304                         continue;
1305                 } else {
1306                         g_assert (0);
1307                 }
1308         }
1309 }
1310
1311 /*
1312  * When appdomains are unloaded we can easily remove objects that have finalizers,
1313  * but all the others could still be present in random places on the heap.
1314  * We need a sweep to get rid of them even though it's going to be costly
1315  * with big heaps.
1316  * The reason we need to remove them is because we access the vtable and class
1317  * structures to know the object size and the reference bitmap: once the domain is
1318  * unloaded the point to random memory.
1319  */
1320 void
1321 mono_gc_clear_domain (MonoDomain * domain)
1322 {
1323         GCMemSection *section;
1324         LOCK_GC;
1325         for (section = section_list; section; section = section->next) {
1326                 scan_area_for_domain (domain, section->data, section->end_data);
1327         }
1328         /* FIXME: handle big and fixed objects (we remove, don't clear in this case) */
1329         UNLOCK_GC;
1330 }
1331 #endif
1332
1333 /*
1334  * add_to_global_remset:
1335  *
1336  *   The global remset contains locations which point into newspace after
1337  * a minor collection. This can happen if the objects they point to are pinned.
1338  */
1339 static void
1340 add_to_global_remset (gpointer ptr, gboolean root)
1341 {
1342         RememberedSet *rs;
1343
1344         DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1345
1346         /* 
1347          * FIXME: If an object remains pinned, we need to add it at every minor collection.
1348          * To avoid uncontrolled growth of the global remset, only add each pointer once.
1349          */
1350         if (global_remset->store_next + 3 < global_remset->end_set) {
1351                 if (root) {
1352                         *(global_remset->store_next++) = (mword)ptr | REMSET_OTHER;
1353                         *(global_remset->store_next++) = (mword)REMSET_ROOT_LOCATION;
1354                 } else {
1355                         *(global_remset->store_next++) = (mword)ptr;
1356                 }
1357                 return;
1358         }
1359         rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
1360         rs->next = global_remset;
1361         global_remset = rs;
1362         if (root) {
1363                 *(global_remset->store_next++) = (mword)ptr | REMSET_OTHER;
1364                 *(global_remset->store_next++) = (mword)REMSET_LOCATION;
1365         } else {
1366                 *(global_remset->store_next++) = (mword)ptr;
1367         }
1368
1369         {
1370                 int global_rs_size = 0;
1371
1372                 for (rs = global_remset; rs; rs = rs->next) {
1373                         global_rs_size += rs->store_next - rs->data;
1374                 }
1375                 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
1376         }
1377 }
1378
1379 /*
1380  * This is how the copying happens from the nursery to the old generation.
1381  * We assume that at this time all the pinned objects have been identified and
1382  * marked as such.
1383  * We run scan_object() for each pinned object so that each referenced
1384  * objects if possible are copied. The new gray objects created can have
1385  * scan_object() run on them right away, too.
1386  * Then we run copy_object() for the precisely tracked roots. At this point
1387  * all the roots are either gray or black. We run scan_object() on the gray
1388  * objects until no more gray objects are created.
1389  * At the end of the process we walk again the pinned list and we unmark
1390  * the pinned flag. As we go we also create the list of free space for use
1391  * in the next allocation runs.
1392  *
1393  * We need to remember objects from the old generation that point to the new one
1394  * (or just addresses?).
1395  *
1396  * copy_object could be made into a macro once debugged (use inline for now).
1397  */
1398
1399 static char* __attribute__((noinline))
1400 copy_object (char *obj, char *from_space_start, char *from_space_end)
1401 {
1402         static void *copy_labels [] = { &&LAB_0, &&LAB_1, &&LAB_2, &&LAB_3, &&LAB_4, &&LAB_5, &&LAB_6, &&LAB_7, &&LAB_8 };
1403
1404         /* 
1405          * FIXME: The second set of checks is only needed if we are called for tospace
1406          * objects too.
1407          */
1408         if (obj >= from_space_start && obj < from_space_end && (obj < to_space || obj >= to_space_end)) {
1409                 MonoVTable *vt;
1410                 char *forwarded;
1411                 mword objsize;
1412                 DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p", obj));
1413                 if ((forwarded = object_is_forwarded (obj))) {
1414                         g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr);
1415                         DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
1416                         return forwarded;
1417                 }
1418                 if (object_is_pinned (obj)) {
1419                         g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr);
1420                         DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
1421                         return obj;
1422                 }
1423                 objsize = safe_object_get_size ((MonoObject*)obj);
1424                 objsize += ALLOC_ALIGN - 1;
1425                 objsize &= ~(ALLOC_ALIGN - 1);
1426                 DEBUG (9, fprintf (gc_debug_file, " (to %p, %s size: %zd)\n", gray_objects, ((MonoObject*)obj)->vtable->klass->name, objsize));
1427                 /* FIXME: handle pinned allocs:
1428                  * Large objects are simple, at least until we always follow the rule:
1429                  * if objsize >= MAX_SMALL_OBJ_SIZE, pin the object and return it.
1430                  * At the end of major collections, we walk the los list and if
1431                  * the object is pinned, it is marked, otherwise it can be freed.
1432                  */
1433                 if (G_UNLIKELY (objsize >= MAX_SMALL_OBJ_SIZE || (obj >= min_pinned_chunk_addr && obj < max_pinned_chunk_addr && obj_is_from_pinned_alloc (obj)))) {
1434                         DEBUG (9, fprintf (gc_debug_file, "Marked LOS/Pinned %p (%s), size: %zd\n", obj, safe_name (obj), objsize));
1435                         pin_object (obj);
1436                         return obj;
1437                 }
1438                 /* ok, the object is not pinned, we can move it */
1439                 /* use a optimized memcpy here */
1440                 if (objsize <= sizeof (gpointer) * 8) {
1441                         mword *dest = (mword*)gray_objects;
1442                         goto *copy_labels [objsize / sizeof (gpointer)];
1443                 LAB_8:
1444                         (dest) [7] = ((mword*)obj) [7];
1445                 LAB_7:
1446                         (dest) [6] = ((mword*)obj) [6];
1447                 LAB_6:
1448                         (dest) [5] = ((mword*)obj) [5];
1449                 LAB_5:
1450                         (dest) [4] = ((mword*)obj) [4];
1451                 LAB_4:
1452                         (dest) [3] = ((mword*)obj) [3];
1453                 LAB_3:
1454                         (dest) [2] = ((mword*)obj) [2];
1455                 LAB_2:
1456                         (dest) [1] = ((mword*)obj) [1];
1457                 LAB_1:
1458                         (dest) [0] = ((mword*)obj) [0];
1459                 LAB_0:
1460                         ;
1461                 } else {
1462 #if 0
1463                 {
1464                         int ecx;
1465                         char* esi = obj;
1466                         char* edi = gray_objects;
1467                         __asm__ __volatile__(
1468                                 "rep; movsl"
1469                                 : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
1470                                 : "0" (objsize/4), "1" (edi),"2" (esi)
1471                                 : "memory"
1472                         );
1473                 }
1474 #else
1475                 memcpy (gray_objects, obj, objsize);
1476 #endif
1477                 }
1478                 /* adjust array->bounds */
1479                 vt = ((MonoObject*)obj)->vtable;
1480                 g_assert (vt->gc_descr);
1481                 if (G_UNLIKELY (vt->rank && ((MonoArray*)obj)->bounds)) {
1482                         MonoArray *array = (MonoArray*)gray_objects;
1483                         array->bounds = (MonoArrayBounds*)((char*)gray_objects + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
1484                         DEBUG (9, fprintf (gc_debug_file, "Array instance %p: size: %zd, rank: %d, length: %d\n", array, objsize, vt->rank, mono_array_length (array)));
1485                 }
1486                 /* set the forwarding pointer */
1487                 forward_object (obj, gray_objects);
1488                 obj = gray_objects;
1489                 to_space_section->scan_starts [((char*)obj - (char*)to_space_section->data)/SCAN_START_SIZE] = obj;
1490                 gray_objects += objsize;
1491                 DEBUG (8, g_assert (gray_objects <= to_space_end));
1492                 return obj;
1493         }
1494         return obj;
1495 }
1496
1497 #undef HANDLE_PTR
1498 #define HANDLE_PTR(ptr,obj)     do {    \
1499                 void *__old = *(ptr);   \
1500                 if (__old) {    \
1501                         *(ptr) = copy_object (__old, from_start, from_end);     \
1502                         DEBUG (9, if (__old != *(ptr)) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old));     \
1503                         if (G_UNLIKELY (*(ptr) >= (void*)from_start && *(ptr) < (void*)from_end) && !ptr_in_nursery (ptr)) \
1504                                 add_to_global_remset ((ptr), FALSE);                                                    \
1505                 }       \
1506         } while (0)
1507
1508 /*
1509  * Scan the object pointed to by @start for references to
1510  * other objects between @from_start and @from_end and copy
1511  * them to the gray_objects area.
1512  * Returns a pointer to the end of the object.
1513  */
1514 static char*
1515 scan_object (char *start, char* from_start, char* from_end)
1516 {
1517         GCVTable *vt;
1518         size_t skip_size;
1519         mword desc;
1520
1521         vt = (GCVTable*)LOAD_VTABLE (start);
1522         //type = vt->desc & 0x7;
1523
1524         /* gcc should be smart enough to remove the bounds check, but it isn't:( */
1525         desc = vt->desc;
1526         switch (desc & 0x7) {
1527         //if (type == DESC_TYPE_STRING) {
1528         case DESC_TYPE_STRING:
1529                 STRING_SIZE (skip_size, start);
1530                 return start + skip_size;
1531         //} else if (type == DESC_TYPE_RUN_LENGTH) {
1532         case DESC_TYPE_RUN_LENGTH:
1533                 OBJ_RUN_LEN_FOREACH_PTR (desc,start);
1534                 OBJ_RUN_LEN_SIZE (skip_size, desc, start);
1535                 g_assert (skip_size);
1536                 return start + skip_size;
1537         //} else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
1538         case DESC_TYPE_ARRAY:
1539         case DESC_TYPE_VECTOR:
1540                 OBJ_VECTOR_FOREACH_PTR (vt, start);
1541                 skip_size = safe_object_get_size ((MonoObject*)start);
1542 #if 0
1543                 skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
1544                 skip_size *= mono_array_length ((MonoArray*)start);
1545                 skip_size += sizeof (MonoArray);
1546 #endif
1547                 skip_size += (ALLOC_ALIGN - 1);
1548                 skip_size &= ~(ALLOC_ALIGN - 1);
1549                 return start + skip_size;
1550         //} else if (type == DESC_TYPE_SMALL_BITMAP) {
1551         case DESC_TYPE_SMALL_BITMAP:
1552                 OBJ_BITMAP_FOREACH_PTR (desc,start);
1553                 OBJ_BITMAP_SIZE (skip_size, desc, start);
1554                 return start + skip_size;
1555         //} else if (type == DESC_TYPE_LARGE_BITMAP) {
1556         case DESC_TYPE_LARGE_BITMAP:
1557                 OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
1558                 skip_size = safe_object_get_size ((MonoObject*)start);
1559                 skip_size += (ALLOC_ALIGN - 1);
1560                 skip_size &= ~(ALLOC_ALIGN - 1);
1561                 return start + skip_size;
1562         //} else if (type == DESC_TYPE_COMPLEX) {
1563         case DESC_TYPE_COMPLEX:
1564                 OBJ_COMPLEX_FOREACH_PTR (vt, start);
1565                 /* this is a complex object */
1566                 skip_size = safe_object_get_size ((MonoObject*)start);
1567                 skip_size += (ALLOC_ALIGN - 1);
1568                 skip_size &= ~(ALLOC_ALIGN - 1);
1569                 return start + skip_size;
1570         //} else if (type == DESC_TYPE_COMPLEX_ARR) {
1571         case DESC_TYPE_COMPLEX_ARR:
1572                 OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
1573                 /* this is an array of complex structs */
1574                 skip_size = safe_object_get_size ((MonoObject*)start);
1575 #if 0
1576                 skip_size = mono_array_element_size (((MonoObject*)start)->vtable->klass);
1577                 skip_size *= mono_array_length ((MonoArray*)start);
1578                 skip_size += sizeof (MonoArray);
1579 #endif
1580                 skip_size += (ALLOC_ALIGN - 1);
1581                 skip_size &= ~(ALLOC_ALIGN - 1);
1582                 return start + skip_size;
1583         }
1584         g_assert_not_reached ();
1585         return NULL;
1586 }
1587
1588 /*
1589  * drain_gray_stack:
1590  *
1591  *   Scan objects in the gray stack until the stack is empty. This should be called
1592  * frequently after each object is copied, to achieve better locality and cache
1593  * usage.
1594  */
1595 static void inline
1596 drain_gray_stack (char *start_addr, char *end_addr)
1597 {
1598         char *gray_start = gray_first;
1599
1600         while (gray_start < gray_objects) {
1601                 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", gray_start, safe_name (gray_start)));
1602                 gray_start = scan_object (gray_start, start_addr, end_addr);
1603         }
1604
1605         gray_first = gray_start;
1606 }
1607
1608 /*
1609  * scan_vtype:
1610  *
1611  * Scan the valuetype pointed to by START, described by DESC for references to
1612  * other objects between @from_start and @from_end and copy them to the gray_objects area.
1613  * Returns a pointer to the end of the object.
1614  */
1615 static char*
1616 scan_vtype (char *start, mword desc, char* from_start, char* from_end)
1617 {
1618         size_t skip_size;
1619
1620         /* The descriptors include info about the MonoObject header as well */
1621         start -= sizeof (MonoObject);
1622
1623         switch (desc & 0x7) {
1624         case DESC_TYPE_RUN_LENGTH:
1625                 OBJ_RUN_LEN_FOREACH_PTR (desc,start);
1626                 OBJ_RUN_LEN_SIZE (skip_size, desc, start);
1627                 g_assert (skip_size);
1628                 return start + skip_size;
1629         case DESC_TYPE_SMALL_BITMAP:
1630                 OBJ_BITMAP_FOREACH_PTR (desc,start);
1631                 OBJ_BITMAP_SIZE (skip_size, desc, start);
1632                 return start + skip_size;
1633         case DESC_TYPE_LARGE_BITMAP:
1634         case DESC_TYPE_COMPLEX:
1635                 // FIXME:
1636                 g_assert_not_reached ();
1637                 break;
1638         default:
1639                 // The other descriptors can't happen with vtypes
1640                 g_assert_not_reached ();
1641                 break;
1642         }
1643         return NULL;
1644 }
1645
1646 /*
1647  * Addresses from start to end are already sorted. This function finds the object header
1648  * for each address and pins the object. The addresses must be inside the passed section.
1649  * Return the number of pinned objects.
1650  */
1651 static int
1652 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery)
1653 {
1654         void *last = NULL;
1655         int count = 0;
1656         void *search_start;
1657         void *last_obj = NULL;
1658         size_t last_obj_size = 0;
1659         void *addr;
1660         int idx;
1661         void **definitely_pinned = start;
1662         while (start < end) {
1663                 addr = *start;
1664                 /* the range check should be reduntant */
1665                 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1666                         DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1667                         /* multiple pointers to the same object */
1668                         if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1669                                 start++;
1670                                 continue;
1671                         }
1672                         idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1673                         search_start = (void*)section->scan_starts [idx];
1674                         if (!search_start || search_start > addr) {
1675                                 while (idx) {
1676                                         --idx;
1677                                         search_start = section->scan_starts [idx];
1678                                         if (search_start && search_start <= addr)
1679                                                 break;
1680                                 }
1681                                 if (!search_start || search_start > addr)
1682                                         search_start = start_nursery;
1683                         }
1684                         if (search_start < last_obj)
1685                                 search_start = (char*)last_obj + last_obj_size;
1686                         /* now addr should be in an object a short distance from search_start
1687                          * Note that search_start must point to zeroed mem or point to an object.
1688                          */
1689                         do {
1690                                 if (!*(void**)search_start) {
1691                                         mword p = (mword)search_start;
1692                                         p += sizeof (gpointer);
1693                                         p += ALLOC_ALIGN - 1;
1694                                         p &= ~(ALLOC_ALIGN - 1);
1695                                         search_start = (void*)p;
1696                                         continue;
1697                                 }
1698                                 last_obj = search_start;
1699                                 last_obj_size = safe_object_get_size ((MonoObject*)search_start);
1700                                 last_obj_size += ALLOC_ALIGN - 1;
1701                                 last_obj_size &= ~(ALLOC_ALIGN - 1);
1702                                 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
1703                                 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1704                                         DEBUG (4, fprintf (gc_debug_file, "Pinned object %p, vtable %p (%s), count %d\n", search_start, *(void**)search_start, safe_name (search_start), count));
1705                                         pin_object (search_start);
1706                                         definitely_pinned [count] = search_start;
1707                                         count++;
1708                                         break;
1709                                 }
1710                                 /* skip to the next object */
1711                                 search_start = (void*)((char*)search_start + last_obj_size);
1712                         } while (search_start <= addr);
1713                         /* we either pinned the correct object or we ignored the addr because
1714                          * it points to unused zeroed memory.
1715                          */
1716                         last = addr;
1717                 }
1718                 start++;
1719         }
1720         //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1721         return count;
1722 }
1723
1724 static void** pin_queue;
1725 static int pin_queue_size = 0;
1726 static int next_pin_slot = 0;
1727
1728 static int
1729 new_gap (int gap)
1730 {
1731         gap = (gap * 10) / 13;
1732         if (gap == 9 || gap == 10)
1733                 return 11;
1734         if (gap < 1)
1735                 return 1;
1736         return gap;
1737 }
1738
1739 #if 0
1740 static int
1741 compare_addr (const void *a, const void *b)
1742 {
1743         return *(const void **)a - *(const void **)b;
1744 }
1745 #endif
1746
1747 /* sort the addresses in array in increasing order */
1748 static void
1749 sort_addresses (void **array, int size)
1750 {
1751         /*
1752          * qsort is slower as predicted.
1753          * qsort (array, size, sizeof (gpointer), compare_addr);
1754          * return;
1755          */
1756         int gap = size;
1757         int swapped, end;
1758         while (TRUE) {
1759                 int i;
1760                 gap = new_gap (gap);
1761                 swapped = FALSE;
1762                 end = size - gap;
1763                 for (i = 0; i < end; i++) {
1764                         int j = i + gap;
1765                         if (array [i] > array [j]) {
1766                                 void* val = array [i];
1767                                 array [i] = array [j];
1768                                 array [j] = val;
1769                                 swapped = TRUE;
1770                         }
1771                 }
1772                 if (gap == 1 && !swapped)
1773                         break;
1774         }
1775 }
1776
1777 static G_GNUC_UNUSED void
1778 print_nursery_gaps (void* start_nursery, void *end_nursery)
1779 {
1780         int i;
1781         gpointer first = start_nursery;
1782         gpointer next;
1783         for (i = 0; i < next_pin_slot; ++i) {
1784                 next = pin_queue [i];
1785                 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
1786                 first = next;
1787         }
1788         next = end_nursery;
1789         fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
1790 }
1791
1792 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1793 static void
1794 optimize_pin_queue (int start_slot)
1795 {
1796         void **start, **cur, **end;
1797         /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1798         /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1799         DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1800         if ((next_pin_slot - start_slot) > 1)
1801                 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1802         start = cur = pin_queue + start_slot;
1803         end = pin_queue + next_pin_slot;
1804         while (cur < end) {
1805                 *start = *cur++;
1806                 while (*start == *cur && cur < end)
1807                         cur++;
1808                 start++;
1809         };
1810         next_pin_slot = start - pin_queue;
1811         DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1812         //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1813         
1814 }
1815
1816 static void
1817 realloc_pin_queue (void)
1818 {
1819         int new_size = pin_queue_size? pin_queue_size + pin_queue_size/2: 1024;
1820         void **new_pin = get_internal_mem (sizeof (void*) * new_size);
1821         memcpy (new_pin, pin_queue, sizeof (void*) * next_pin_slot);
1822         free_internal_mem (pin_queue);
1823         pin_queue = new_pin;
1824         pin_queue_size = new_size;
1825         DEBUG (4, fprintf (gc_debug_file, "Reallocated pin queue to size: %d\n", new_size));
1826 }
1827
1828 /* 
1829  * Scan the memory between start and end and queue values which could be pointers
1830  * to the area between start_nursery and end_nursery for later consideration.
1831  * Typically used for thread stacks.
1832  */
1833 static void
1834 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery)
1835 {
1836         int count = 0;
1837         while (start < end) {
1838                 if (*start >= start_nursery && *start < end_nursery) {
1839                         /*
1840                          * *start can point to the middle of an object
1841                          * note: should we handle pointing at the end of an object?
1842                          * pinning in C# code disallows pointing at the end of an object
1843                          * but there is some small chance that an optimizing C compiler
1844                          * may keep the only reference to an object by pointing
1845                          * at the end of it. We ignore this small chance for now.
1846                          * Pointers to the end of an object are indistinguishable
1847                          * from pointers to the start of the next object in memory
1848                          * so if we allow that we'd need to pin two objects...
1849                          * We queue the pointer in an array, the
1850                          * array will then be sorted and uniqued. This way
1851                          * we can coalesce several pinning pointers and it should
1852                          * be faster since we'd do a memory scan with increasing
1853                          * addresses. Note: we can align the address to the allocation
1854                          * alignment, so the unique process is more effective.
1855                          */
1856                         mword addr = (mword)*start;
1857                         addr &= ~(ALLOC_ALIGN - 1);
1858                         if (next_pin_slot >= pin_queue_size)
1859                                 realloc_pin_queue ();
1860                         pin_queue [next_pin_slot++] = (void*)addr;
1861                         DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
1862                         count++;
1863                 }
1864                 start++;
1865         }
1866         DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
1867
1868 #ifdef HAVE_VALGRIND_MEMCHECK_H
1869         /*
1870          * The pinning addresses might come from undefined memory, this is normal. Since they
1871          * are used in lots of functions, we make the memory defined here instead of having
1872          * to add a supression for those functions.
1873          */
1874         VALGRIND_MAKE_MEM_DEFINED (pin_queue, next_pin_slot * sizeof (pin_queue [0]));
1875 #endif
1876 }
1877
1878 /* 
1879  * If generation is 0, just mark objects in the nursery, the others we don't care,
1880  * since they are not going to move anyway.
1881  * There are different areas that are scanned for pinned pointers:
1882  * *) the thread stacks (when jit support is ready only the unmanaged frames)
1883  * *) the pinned handle table
1884  * *) the pinned roots
1885  *
1886  * Note: when we'll use a write barrier for old to new gen references, we need to
1887  * keep track of old gen objects that point to pinned new gen objects because in that
1888  * case the referenced object will be moved maybe at the next collection, but there
1889  * is no write in the old generation area where the pinned object is referenced
1890  * and we may not consider it as reachable.
1891  */
1892 static G_GNUC_UNUSED void
1893 mark_pinned_objects (int generation)
1894 {
1895 }
1896
1897 /*
1898  * Debugging function: find in the conservative roots where @obj is being pinned.
1899  */
1900 static G_GNUC_UNUSED void
1901 find_pinning_reference (char *obj, size_t size)
1902 {
1903         RootRecord *root;
1904         int i;
1905         char *endobj = obj + size;
1906         for (i = 0; i < roots_hash_size [0]; ++i) {
1907                 for (root = roots_hash [0][i]; root; root = root->next) {
1908                         /* if desc is non-null it has precise info */
1909                         if (!root->root_desc) {
1910                                 char ** start = (char**)root->start_root;
1911                                 while (start < (char**)root->end_root) {
1912                                         if (*start >= obj && *start < endobj) {
1913                                                 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in pinned roots %p-%p (at %p in record %p)\n", obj, root->start_root, root->end_root, start, root));
1914                                         }
1915                                         start++;
1916                                 }
1917                         }
1918                 }
1919         }
1920         find_pinning_ref_from_thread (obj, size);
1921 }
1922
1923 /*
1924  * The first thing we do in a collection is to identify pinned objects.
1925  * This function considers all the areas of memory that need to be
1926  * conservatively scanned.
1927  */
1928 static void
1929 pin_from_roots (void *start_nursery, void *end_nursery)
1930 {
1931         RootRecord *root;
1932         int i;
1933         DEBUG (2, fprintf (gc_debug_file, "Scanning pinned roots (%d bytes, %d/%d entries)\n", (int)roots_size, num_roots_entries [ROOT_TYPE_NORMAL], num_roots_entries [ROOT_TYPE_PINNED]));
1934         /* objects pinned from the API are inside these roots */
1935         for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1936                 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1937                         DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
1938                         conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery);
1939                 }
1940         }
1941         /* now deal with the thread stacks
1942          * in the future we should be able to conservatively scan only:
1943          * *) the cpu registers
1944          * *) the unmanaged stack frames
1945          * *) the _last_ managed stack frame
1946          * *) pointers slots in managed frames
1947          */
1948         pin_thread_data (start_nursery, end_nursery);
1949 }
1950
1951 /* Copy function called from user defined mark functions */
1952 static char *user_copy_n_start;
1953 static char *user_copy_n_end;
1954
1955 static void*
1956 user_copy (void *addr)
1957 {
1958         if (addr)
1959                 return copy_object (addr, user_copy_n_start, user_copy_n_end);
1960         else
1961                 return NULL;
1962 }
1963
1964 /*
1965  * The memory area from start_root to end_root contains pointers to objects.
1966  * Their position is precisely described by @desc (this means that the pointer
1967  * can be either NULL or the pointer to the start of an object).
1968  * This functions copies them to to_space updates them.
1969  */
1970 static void
1971 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, mword desc)
1972 {
1973         switch (desc & ROOT_DESC_TYPE_MASK) {
1974         case ROOT_DESC_BITMAP:
1975                 desc >>= ROOT_DESC_TYPE_SHIFT;
1976                 while (desc) {
1977                         if ((desc & 1) && *start_root) {
1978                                 *start_root = copy_object (*start_root, n_start, n_end);
1979                                 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
1980                                 drain_gray_stack (n_start, n_end);
1981                         }
1982                         desc >>= 1;
1983                         start_root++;
1984                 }
1985                 return;
1986         case ROOT_DESC_COMPLEX: {
1987                 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1988                 int bwords = (*bitmap_data) - 1;
1989                 void **start_run = start_root;
1990                 bitmap_data++;
1991                 while (bwords-- > 0) {
1992                         gsize bmap = *bitmap_data++;
1993                         void **objptr = start_run;
1994                         while (bmap) {
1995                                 if ((bmap & 1) && *objptr) {
1996                                         *objptr = copy_object (*objptr, n_start, n_end);
1997                                         DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
1998                                         drain_gray_stack (n_start, n_end);
1999                                 }
2000                                 bmap >>= 1;
2001                                 ++objptr;
2002                         }
2003                         start_run += GC_BITS_PER_WORD;
2004                 }
2005                 break;
2006         }
2007         case ROOT_DESC_USER: {
2008                 MonoGCMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2009
2010                 user_copy_n_start = n_start;
2011                 user_copy_n_end = n_end;
2012                 marker (start_root, user_copy);
2013                 break;
2014         }
2015         case ROOT_DESC_RUN_LEN:
2016                 g_assert_not_reached ();
2017         default:
2018                 g_assert_not_reached ();
2019         }
2020 }
2021
2022 static Fragment*
2023 alloc_fragment (void)
2024 {
2025         Fragment *frag = fragment_freelist;
2026         if (frag) {
2027                 fragment_freelist = frag->next;
2028                 frag->next = NULL;
2029                 return frag;
2030         }
2031         frag = get_internal_mem (sizeof (Fragment));
2032         frag->next = NULL;
2033         return frag;
2034 }
2035
2036 /*
2037  * Allocate and setup the data structures needed to be able to allocate objects
2038  * in the nursery. The nursery is stored in nursery_section.
2039  */
2040 static void
2041 alloc_nursery (void)
2042 {
2043         GCMemSection *section;
2044         char *data;
2045         int scan_starts;
2046         Fragment *frag;
2047         int alloc_size;
2048
2049         if (nursery_section)
2050                 return;
2051         DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %zd\n", nursery_size));
2052         /* later we will alloc a larger area for the nursery but only activate
2053          * what we need. The rest will be used as expansion if we have too many pinned
2054          * objects in the existing nursery.
2055          */
2056         /* FIXME: handle OOM */
2057         section = get_internal_mem (sizeof (GCMemSection));
2058
2059 #ifdef ALIGN_NURSERY
2060         /* Allocate twice the memory to be able to put the nursery at an aligned address */
2061         g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2062
2063         alloc_size = nursery_size * 2;
2064         data = get_os_memory (alloc_size, TRUE);
2065         nursery_start = (void*)(((mword)data + (1 << DEFAULT_NURSERY_BITS) - 1) & ~((1 << DEFAULT_NURSERY_BITS) - 1));
2066         g_assert ((char*)nursery_start + nursery_size <= ((char*)data + alloc_size));
2067         /* FIXME: Use the remaining size for something else, if it is big enough */
2068 #else
2069         alloc_size = nursery_size;
2070         data = get_os_memory (alloc_size, TRUE);
2071         nursery_start = data;
2072 #endif
2073         nursery_real_end = nursery_start + nursery_size;
2074         UPDATE_HEAP_BOUNDARIES (nursery_start, nursery_real_end);
2075         nursery_next = nursery_start;
2076         total_alloc += alloc_size;
2077         DEBUG (4, fprintf (gc_debug_file, "Expanding heap size: %zd, total: %zd\n", nursery_size, total_alloc));
2078         section->data = section->next_data = data;
2079         section->size = alloc_size;
2080         section->end_data = nursery_real_end;
2081         scan_starts = alloc_size / SCAN_START_SIZE;
2082         section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts);
2083         section->num_scan_start = scan_starts;
2084         section->role = MEMORY_ROLE_GEN0;
2085
2086         /* add to the section list */
2087         section->next = section_list;
2088         section_list = section;
2089
2090         nursery_section = section;
2091
2092         /* Setup the single first large fragment */
2093         frag = alloc_fragment ();
2094         frag->fragment_start = nursery_start;
2095         frag->fragment_limit = nursery_start;
2096         frag->fragment_end = nursery_real_end;
2097         nursery_frag_real_end = nursery_real_end;
2098         /* FIXME: frag here is lost */
2099 }
2100
2101 /*
2102  * Update roots in the old generation. Since we currently don't have the
2103  * info from the write barriers, we just scan all the objects.
2104  */
2105 static G_GNUC_UNUSED void
2106 scan_old_generation (char *start, char* end)
2107 {
2108         GCMemSection *section;
2109         FinalizeEntry *fin;
2110         LOSObject *big_object;
2111         char *p;
2112         
2113         for (section = section_list; section; section = section->next) {
2114                 if (section == nursery_section)
2115                         continue;
2116                 DEBUG (2, fprintf (gc_debug_file, "Scan of old section: %p-%p, size: %d\n", section->data, section->next_data, (int)(section->next_data - section->data)));
2117                 /* we have to deal with zeroed holes in old generation (truncated strings ...) */
2118                 p = section->data;
2119                 while (p < section->next_data) {
2120                         if (!*(void**)p) {
2121                                 p += ALLOC_ALIGN;
2122                                 continue;
2123                         }
2124                         DEBUG (8, fprintf (gc_debug_file, "Precise old object scan of %p (%s)\n", p, safe_name (p)));
2125                         p = scan_object (p, start, end);
2126                 }
2127         }
2128         /* scan the old object space, too */
2129         for (big_object = los_object_list; big_object; big_object = big_object->next) {
2130                 DEBUG (5, fprintf (gc_debug_file, "Scan of big object: %p (%s), size: %zd\n", big_object->data, safe_name (big_object->data), big_object->size));
2131                 scan_object (big_object->data, start, end);
2132         }
2133         /* scan the list of objects ready for finalization */
2134         for (fin = fin_ready_list; fin; fin = fin->next) {
2135                 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2136                 fin->object = copy_object (fin->object, start, end);
2137         }
2138 }
2139
2140 static mword fragment_total = 0;
2141 /*
2142  * We found a fragment of free memory in the nursery: memzero it and if
2143  * it is big enough, add it to the list of fragments that can be used for
2144  * allocation.
2145  */
2146 static void
2147 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2148 {
2149         Fragment *fragment;
2150         DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2151         /* memsetting just the first chunk start is bound to provide better cache locality */
2152         if (nursery_clear_policy == CLEAR_AT_GC)
2153                 memset (frag_start, 0, frag_size);
2154         /* Not worth dealing with smaller fragments: need to tune */
2155         if (frag_size >= FRAGMENT_MIN_SIZE) {
2156                 fragment = alloc_fragment ();
2157                 fragment->fragment_start = frag_start;
2158                 fragment->fragment_limit = frag_start;
2159                 fragment->fragment_end = frag_end;
2160                 fragment->next = nursery_fragments;
2161                 nursery_fragments = fragment;
2162                 fragment_total += frag_size;
2163         } else {
2164                 /* Clear unused fragments, pinning depends on this */
2165                 memset (frag_start, 0, frag_size);
2166         }
2167 }
2168
2169 static int
2170 scan_needed_big_objects (char *start_addr, char *end_addr)
2171 {
2172         LOSObject *big_object;
2173         int count = 0;
2174         for (big_object = los_object_list; big_object; big_object = big_object->next) {
2175                 if (!big_object->scanned && object_is_pinned (big_object->data)) {
2176                         DEBUG (5, fprintf (gc_debug_file, "Scan of big object: %p (%s), size: %zd\n", big_object->data, safe_name (big_object->data), big_object->size));
2177                         scan_object (big_object->data, start_addr, end_addr);
2178                         big_object->scanned = TRUE;
2179                         count++;
2180                 }
2181         }
2182         return count;
2183 }
2184
2185 static void
2186 finish_gray_stack (char *start_addr, char *end_addr)
2187 {
2188         TV_DECLARE (atv);
2189         TV_DECLARE (btv);
2190         int fin_ready, bigo_scanned_num;
2191         char *gray_start;
2192
2193         /*
2194          * We copied all the reachable objects. Now it's the time to copy
2195          * the objects that were not referenced by the roots, but by the copied objects.
2196          * we built a stack of objects pointed to by gray_start: they are
2197          * additional roots and we may add more items as we go.
2198          * We loop until gray_start == gray_objects which means no more objects have
2199          * been added. Note this is iterative: no recursion is involved.
2200          * We need to walk the LO list as well in search of marked big objects
2201          * (use a flag since this is needed only on major collections). We need to loop
2202          * here as well, so keep a counter of marked LO (increasing it in copy_object).
2203          *   To achieve better cache locality and cache usage, we drain the gray stack 
2204          * frequently, after each object is copied, and just finish the work here.
2205          */
2206         gray_start = gray_first;
2207         while (gray_start < gray_objects) {
2208                 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", gray_start, safe_name (gray_start)));
2209                 gray_start = scan_object (gray_start, start_addr, end_addr);
2210         }
2211         TV_GETTIME (atv);
2212         //scan_old_generation (start_addr, end_addr);
2213         DEBUG (2, fprintf (gc_debug_file, "Old generation done\n"));
2214         /* walk the finalization queue and move also the objects that need to be
2215          * finalized: use the finalized objects as new roots so the objects they depend
2216          * on are also not reclaimed. As with the roots above, only objects in the nursery
2217          * are marked/copied.
2218          * We need a loop here, since objects ready for finalizers may reference other objects
2219          * that are fin-ready. Speedup with a flag?
2220          */
2221         do {
2222                 fin_ready = num_ready_finalizers;
2223                 finalize_in_range (start_addr, end_addr);
2224                 bigo_scanned_num = scan_needed_big_objects (start_addr, end_addr);
2225
2226                 /* drain the new stack that might have been created */
2227                 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin: %p-%p, size: %d\n", gray_start, gray_objects, (int)(gray_objects - gray_start)));
2228                 while (gray_start < gray_objects) {
2229                         DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", gray_start, safe_name (gray_start)));
2230                         gray_start = scan_object (gray_start, start_addr, end_addr);
2231                 }
2232         } while (fin_ready != num_ready_finalizers || bigo_scanned_num);
2233
2234         DEBUG (2, fprintf (gc_debug_file, "Copied to old space: %d bytes\n", (int)(gray_objects - to_space)));
2235         to_space = gray_start;
2236         to_space_section->next_data = to_space;
2237
2238         /*
2239          * handle disappearing links
2240          * Note we do this after checking the finalization queue because if an object
2241          * survives (at least long enough to be finalized) we don't clear the link.
2242          * This also deals with a possible issue with the monitor reclamation: with the Boehm
2243          * GC a finalized object my lose the monitor because it is cleared before the finalizer is
2244          * called.
2245          */
2246         null_link_in_range (start_addr, end_addr);
2247         TV_GETTIME (btv);
2248         DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2249 }
2250
2251 static int last_num_pinned = 0;
2252
2253 static void
2254 build_nursery_fragments (int start_pin, int end_pin)
2255 {
2256         char *frag_start, *frag_end;
2257         size_t frag_size;
2258         int i;
2259
2260         /* FIXME: handle non-NULL fragment_freelist */
2261         fragment_freelist = nursery_fragments;
2262         nursery_fragments = NULL;
2263         frag_start = nursery_start;
2264         fragment_total = 0;
2265         /* clear scan starts */
2266         memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
2267         for (i = start_pin; i < end_pin; ++i) {
2268                 frag_end = pin_queue [i];
2269                 /* remove the pin bit from pinned objects */
2270                 unpin_object (frag_end);
2271                 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
2272                 frag_size = frag_end - frag_start;
2273                 if (frag_size)
2274                         add_nursery_frag (frag_size, frag_start, frag_end);
2275                 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
2276                 frag_size += ALLOC_ALIGN - 1;
2277                 frag_size &= ~(ALLOC_ALIGN - 1);
2278                 frag_start = (char*)pin_queue [i] + frag_size;
2279                 /* 
2280                  * pin_queue [i] might point to a half-constructed string or vector whose
2281                  * length field is not set. In that case, frag_start points inside the 
2282                  * (zero initialized) object. Find the end of the object by scanning forward.
2283                  * 
2284                  */
2285                 if (is_maybe_half_constructed (pin_queue [i])) {
2286                         char *tlab_end;
2287
2288                         /* This is also hit for zero length arrays/strings */
2289
2290                         /* Find the end of the TLAB which contained this allocation */
2291                         tlab_end = find_tlab_next_from_address (pin_queue [i]);
2292
2293                         if (tlab_end) {
2294                                 while ((frag_start < tlab_end) && *(mword*)frag_start == 0)
2295                                         frag_start += sizeof (mword);
2296                         } else {
2297                                 /*
2298                                  * FIXME: The object is either not allocated in a TLAB, or it isn't a
2299                                  * half constructed object.
2300                                  */
2301                         }
2302                 }
2303         }
2304         nursery_last_pinned_end = frag_start;
2305         frag_end = nursery_real_end;
2306         frag_size = frag_end - frag_start;
2307         if (frag_size)
2308                 add_nursery_frag (frag_size, frag_start, frag_end);
2309         if (!nursery_fragments) {
2310                 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", end_pin - start_pin));
2311                 for (i = start_pin; i < end_pin; ++i) {
2312                         DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", pin_queue [i], safe_name (pin_queue [i]), safe_object_get_size (pin_queue [i])));
2313                 }
2314                 degraded_mode = 1;
2315         }
2316
2317         nursery_next = nursery_frag_real_end = NULL;
2318
2319         /* Clear TLABs for all threads */
2320         clear_tlabs ();
2321 }
2322
2323 /* FIXME: later reduce code duplication here with the above
2324  * We don't keep track of section fragments for non-nursery sections yet, so
2325  * just memset to 0.
2326  */
2327 static void
2328 build_section_fragments (GCMemSection *section)
2329 {
2330         int i;
2331         char *frag_start, *frag_end;
2332         size_t frag_size;
2333
2334         /* clear scan starts */
2335         memset (section->scan_starts, 0, section->num_scan_start * sizeof (gpointer));
2336         frag_start = section->data;
2337         section->next_data = section->data;
2338         for (i = section->pin_queue_start; i < section->pin_queue_end; ++i) {
2339                 frag_end = pin_queue [i];
2340                 /* remove the pin bit from pinned objects */
2341                 unpin_object (frag_end);
2342                 if (frag_end >= section->data + section->size) {
2343                         frag_end = section->data + section->size;
2344                 } else {
2345                         section->scan_starts [((char*)frag_end - (char*)section->data)/SCAN_START_SIZE] = frag_end;
2346                 }
2347                 frag_size = frag_end - frag_start;
2348                 if (frag_size)
2349                         memset (frag_start, 0, frag_size);
2350                 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
2351                 frag_size += ALLOC_ALIGN - 1;
2352                 frag_size &= ~(ALLOC_ALIGN - 1);
2353                 frag_start = (char*)pin_queue [i] + frag_size;
2354                 section->next_data = MAX (section->next_data, frag_start);
2355         }
2356         frag_end = section->end_data;
2357         frag_size = frag_end - frag_start;
2358         if (frag_size)
2359                 memset (frag_start, 0, frag_size);
2360 }
2361
2362 static void
2363 scan_from_registered_roots (char *addr_start, char *addr_end, int root_type)
2364 {
2365         int i;
2366         RootRecord *root;
2367         for (i = 0; i < roots_hash_size [root_type]; ++i) {
2368                 for (root = roots_hash [root_type][i]; root; root = root->next) {
2369                         DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2370                         precisely_scan_objects_from ((void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc);
2371                 }
2372         }
2373 }
2374
2375 /*
2376  * Collect objects in the nursery.
2377  */
2378 static void
2379 collect_nursery (size_t requested_size)
2380 {
2381         GCMemSection *section;
2382         size_t max_garbage_amount;
2383         int i;
2384         char *orig_nursery_next;
2385         Fragment *frag;
2386         TV_DECLARE (all_atv);
2387         TV_DECLARE (all_btv);
2388         TV_DECLARE (atv);
2389         TV_DECLARE (btv);
2390
2391         degraded_mode = 0;
2392         orig_nursery_next = nursery_next;
2393         nursery_next = MAX (nursery_next, nursery_last_pinned_end);
2394         /* FIXME: optimize later to use the higher address where an object can be present */
2395         nursery_next = MAX (nursery_next, nursery_real_end);
2396
2397         if (consistency_check_at_minor_collection)
2398                 check_consistency ();
2399
2400         DEBUG (1, fprintf (gc_debug_file, "Start nursery collection %d %p-%p, size: %d\n", num_minor_gcs, nursery_start, nursery_next, (int)(nursery_next - nursery_start)));
2401         max_garbage_amount = nursery_next - nursery_start;
2402
2403         /* Clear all remaining nursery fragments, pinning depends on this */
2404         if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
2405                 g_assert (orig_nursery_next <= nursery_frag_real_end);
2406                 memset (orig_nursery_next, 0, nursery_frag_real_end - orig_nursery_next);
2407                 for (frag = nursery_fragments; frag; frag = frag->next) {
2408                         memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
2409                 }
2410         }
2411
2412         /* 
2413          * not enough room in the old generation to store all the possible data from 
2414          * the nursery in a single continuous space.
2415          * We reset to_space if we allocated objects in degraded mode.
2416          */
2417         if (to_space_section)
2418                 to_space = gray_objects = gray_first = to_space_section->next_data;
2419         if ((to_space_end - to_space) < max_garbage_amount) {
2420                 section = alloc_section (nursery_section->size * 4);
2421                 g_assert (nursery_section->size >= max_garbage_amount);
2422                 to_space = gray_objects = gray_first = section->next_data;
2423                 to_space_end = section->end_data;
2424                 to_space_section = section;
2425         }
2426         DEBUG (2, fprintf (gc_debug_file, "To space setup: %p-%p in section %p\n", to_space, to_space_end, to_space_section));
2427         nursery_section->next_data = nursery_next;
2428
2429         num_minor_gcs++;
2430         mono_stats.minor_gc_count ++;
2431         /* world must be stopped already */
2432         TV_GETTIME (all_atv);
2433         TV_GETTIME (atv);
2434         /* pin from pinned handles */
2435         pin_from_roots (nursery_start, nursery_next);
2436         /* identify pinned objects */
2437         optimize_pin_queue (0);
2438         next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next);
2439         TV_GETTIME (btv);
2440         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
2441         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2442
2443         /* 
2444          * walk all the roots and copy the young objects to the old generation,
2445          * starting from to_space
2446          */
2447
2448         scan_from_remsets (nursery_start, nursery_next);
2449         /* we don't have complete write barrier yet, so we scan all the old generation sections */
2450         TV_GETTIME (atv);
2451         DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2452
2453         /* the pinned objects are roots */
2454         for (i = 0; i < next_pin_slot; ++i) {
2455                 DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n", i, pin_queue [i], safe_name (pin_queue [i])));
2456                 scan_object (pin_queue [i], nursery_start, nursery_next);
2457         }
2458         /* registered roots, this includes static fields */
2459         scan_from_registered_roots (nursery_start, nursery_next, ROOT_TYPE_NORMAL);
2460         /* alloc_pinned objects */
2461         scan_from_pinned_objects (nursery_start, nursery_next);
2462         TV_GETTIME (btv);
2463         DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2464
2465         finish_gray_stack (nursery_start, nursery_next);
2466
2467         /* walk the pin_queue, build up the fragment list of free memory, unmark
2468          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2469          * next allocations.
2470          */
2471         build_nursery_fragments (0, next_pin_slot);
2472         TV_GETTIME (atv);
2473         DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %zd bytes available\n", TV_ELAPSED (btv, atv), fragment_total));
2474
2475         TV_GETTIME (all_btv);
2476         mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2477
2478         /* prepare the pin queue for the next collection */
2479         last_num_pinned = next_pin_slot;
2480         next_pin_slot = 0;
2481         if (fin_ready_list) {
2482                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2483                 mono_gc_finalize_notify ();
2484         }
2485 }
2486
2487 static void
2488 major_collection (void)
2489 {
2490         GCMemSection *section, *prev_section;
2491         LOSObject *bigobj, *prevbo;
2492         int i;
2493         PinnedChunk *chunk;
2494         FinalizeEntry *fin;
2495         Fragment *frag;
2496         int count;
2497         TV_DECLARE (all_atv);
2498         TV_DECLARE (all_btv);
2499         TV_DECLARE (atv);
2500         TV_DECLARE (btv);
2501         /* FIXME: only use these values for the precise scan
2502          * note that to_space pointers should be excluded anyway...
2503          */
2504         char *heap_start = NULL;
2505         char *heap_end = (char*)-1;
2506         size_t copy_space_required = 0;
2507
2508         degraded_mode = 0;
2509         DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
2510         num_major_gcs++;
2511         mono_stats.major_gc_count ++;
2512
2513         /* Clear all remaining nursery fragments, pinning depends on this */
2514         if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
2515                 g_assert (nursery_next <= nursery_frag_real_end);
2516                 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
2517                 for (frag = nursery_fragments; frag; frag = frag->next) {
2518                         memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
2519                 }
2520         }
2521
2522         /* 
2523          * FIXME: implement Mark/Compact
2524          * Until that is done, we can just apply mostly the same alg as for the nursery:
2525          * this means we need a big section to potentially copy all the other sections, so
2526          * it is not ideal specially with large heaps.
2527          */
2528         if (g_getenv ("MONO_GC_NO_MAJOR")) {
2529                 collect_nursery (0);
2530                 return;
2531         }
2532         TV_GETTIME (all_atv);
2533         /* FIXME: make sure the nursery next_data ptr is updated */
2534         nursery_section->next_data = nursery_real_end;
2535         /* we should also coalesce scanning from sections close to each other
2536          * and deal with pointers outside of the sections later.
2537          */
2538         /* The remsets are not useful for a major collection */
2539         clear_remsets ();
2540         /* world must be stopped already */
2541         TV_GETTIME (atv);
2542         DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
2543         for (section = section_list; section; section = section->next) {
2544                 section->pin_queue_start = count = section->pin_queue_end = next_pin_slot;
2545                 pin_from_roots (section->data, section->next_data);
2546                 if (count != next_pin_slot) {
2547                         int reduced_to;
2548                         optimize_pin_queue (count);
2549                         DEBUG (6, fprintf (gc_debug_file, "Found %d pinning addresses in section %p (%d-%d)\n", next_pin_slot - count, section, count, next_pin_slot));
2550                         reduced_to = pin_objects_from_addresses (section, pin_queue + count, pin_queue + next_pin_slot, section->data, section->next_data);
2551                         section->pin_queue_end = next_pin_slot = count + reduced_to;
2552                 }
2553                 copy_space_required += (char*)section->next_data - (char*)section->data;
2554         }
2555         /* identify possible pointers to the insize of large objects */
2556         DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
2557         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2558                 count = next_pin_slot;
2559                 pin_from_roots (bigobj->data, (char*)bigobj->data + bigobj->size);
2560                 /* FIXME: this is only valid until we don't optimize the pin queue midway */
2561                 if (next_pin_slot != count) {
2562                         next_pin_slot = count;
2563                         pin_object (bigobj->data);
2564                         DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %zd from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
2565                 }
2566         }
2567         /* look for pinned addresses for pinned-alloc objects */
2568         DEBUG (6, fprintf (gc_debug_file, "Pinning from pinned-alloc objects\n"));
2569         for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2570                 count = next_pin_slot;
2571                 pin_from_roots (chunk->start_data, (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE);
2572                 /* FIXME: this is only valid until we don't optimize the pin queue midway */
2573                 if (next_pin_slot != count) {
2574                         mark_pinned_from_addresses (chunk, pin_queue + count, pin_queue + next_pin_slot);
2575                         next_pin_slot = count;
2576                 }
2577         }
2578
2579         TV_GETTIME (btv);
2580         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
2581         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2582
2583         /* allocate the big to space */
2584         DEBUG (4, fprintf (gc_debug_file, "Allocate tospace for size: %zd\n", copy_space_required));
2585         section = alloc_section (copy_space_required);
2586         to_space = gray_objects = gray_first = section->next_data;
2587         to_space_end = section->end_data;
2588         to_space_section = section;
2589
2590         /* the old generation doesn't need to be scanned (no remembered sets or card
2591          * table needed either): the only objects that must survive are those pinned and
2592          * those referenced by the precise roots.
2593          * mark any section without pinned objects, so we can free it since we will be able to
2594          * move all the objects.
2595          */
2596         /* the pinned objects are roots (big objects are included in this list, too) */
2597         for (i = 0; i < next_pin_slot; ++i) {
2598                 DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n", i, pin_queue [i], safe_name (pin_queue [i])));
2599                 scan_object (pin_queue [i], heap_start, heap_end);
2600         }
2601         /* registered roots, this includes static fields */
2602         scan_from_registered_roots (heap_start, heap_end, ROOT_TYPE_NORMAL);
2603         scan_from_registered_roots (heap_start, heap_end, ROOT_TYPE_WBARRIER);
2604         /* alloc_pinned objects */
2605         scan_from_pinned_objects (heap_start, heap_end);
2606         /* scan the list of objects ready for finalization */
2607         for (fin = fin_ready_list; fin; fin = fin->next) {
2608                 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2609                 fin->object = copy_object (fin->object, heap_start, heap_end);
2610         }
2611         TV_GETTIME (atv);
2612         DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2613
2614         /* we need to go over the big object list to see if any was marked and scan it
2615          * And we need to make this in a loop, considering that objects referenced by finalizable
2616          * objects could reference big objects (this happens in finish_gray_stack ())
2617          */
2618         scan_needed_big_objects (heap_start, heap_end);
2619         /* all the objects in the heap */
2620         finish_gray_stack (heap_start, heap_end);
2621
2622         /* sweep the big objects list */
2623         prevbo = NULL;
2624         for (bigobj = los_object_list; bigobj;) {
2625                 if (object_is_pinned (bigobj->data)) {
2626                         unpin_object (bigobj->data);
2627                         bigobj->scanned = FALSE;
2628                 } else {
2629                         LOSObject *to_free;
2630                         /* not referenced anywhere, so we can free it */
2631                         if (prevbo)
2632                                 prevbo->next = bigobj->next;
2633                         else
2634                                 los_object_list = bigobj->next;
2635                         to_free = bigobj;
2636                         bigobj = bigobj->next;
2637                         free_large_object (to_free);
2638                         continue;
2639                 }
2640                 prevbo = bigobj;
2641                 bigobj = bigobj->next;
2642         }
2643         /* unpin objects from the pinned chunks and free the unmarked ones */
2644         sweep_pinned_objects ();
2645
2646         /* free the unused sections */
2647         prev_section = NULL;
2648         for (section = section_list; section;) {
2649                 /* to_space doesn't need handling here and the nursery is special */
2650                 if (section == to_space_section || section == nursery_section) {
2651                         prev_section = section;
2652                         section = section->next;
2653                         continue;
2654                 }
2655                 /* no pinning object, so the section is free */
2656                 if (section->pin_queue_start == section->pin_queue_end) {
2657                         GCMemSection *to_free;
2658                         if (prev_section)
2659                                 prev_section->next = section->next;
2660                         else
2661                                 section_list = section->next;
2662                         to_free = section;
2663                         section = section->next;
2664                         free_mem_section (to_free);
2665                         continue;
2666                 } else {
2667                         DEBUG (6, fprintf (gc_debug_file, "Section %p has still pinned objects (%d)\n", section, section->pin_queue_end - section->pin_queue_start));
2668                         build_section_fragments (section);
2669                 }
2670                 prev_section = section;
2671                 section = section->next;
2672         }
2673
2674         /* walk the pin_queue, build up the fragment list of free memory, unmark
2675          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2676          * next allocations.
2677          */
2678         build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
2679
2680         TV_GETTIME (all_btv);
2681         mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
2682         /* prepare the pin queue for the next collection */
2683         next_pin_slot = 0;
2684         if (fin_ready_list) {
2685                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2686                 mono_gc_finalize_notify ();
2687         }
2688 }
2689
2690 /*
2691  * Allocate a new section of memory to be used as old generation.
2692  */
2693 static GCMemSection*
2694 alloc_section (size_t size)
2695 {
2696         GCMemSection *section;
2697         char *data;
2698         int scan_starts;
2699         size_t new_size = next_section_size;
2700
2701         if (size > next_section_size) {
2702                 new_size = size;
2703                 new_size += pagesize - 1;
2704                 new_size &= ~(pagesize - 1);
2705         }
2706         section_size_used++;
2707         if (section_size_used > 3) {
2708                 section_size_used = 0;
2709                 next_section_size *= 2;
2710                 if (next_section_size > max_section_size)
2711                         next_section_size = max_section_size;
2712         }
2713         section = get_internal_mem (sizeof (GCMemSection));
2714         data = get_os_memory (new_size, TRUE);
2715         section->data = section->next_data = data;
2716         section->size = new_size;
2717         section->end_data = data + new_size;
2718         UPDATE_HEAP_BOUNDARIES (data, section->end_data);
2719         total_alloc += new_size;
2720         DEBUG (2, fprintf (gc_debug_file, "Expanding heap size: %zd, total: %zd\n", new_size, total_alloc));
2721         section->data = data;
2722         section->size = new_size;
2723         scan_starts = new_size / SCAN_START_SIZE;
2724         section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts);
2725         section->num_scan_start = scan_starts;
2726         section->role = MEMORY_ROLE_GEN1;
2727
2728         /* add to the section list */
2729         section->next = section_list;
2730         section_list = section;
2731
2732         return section;
2733 }
2734
2735 static void
2736 free_mem_section (GCMemSection *section)
2737 {
2738         char *data = section->data;
2739         size_t size = section->size;
2740         DEBUG (2, fprintf (gc_debug_file, "Freed section %p, size %zd\n", data, size));
2741         free_os_memory (data, size);
2742         free_internal_mem (section);
2743         total_alloc -= size;
2744 }
2745
2746 /*
2747  * When deciding if it's better to collect or to expand, keep track
2748  * of how much garbage was reclaimed with the last collection: if it's too
2749  * little, expand.
2750  * This is called when we could not allocate a small object.
2751  */
2752 static void __attribute__((noinline))
2753 minor_collect_or_expand_inner (size_t size)
2754 {
2755         int do_minor_collection = 1;
2756
2757         if (!nursery_section) {
2758                 alloc_nursery ();
2759                 return;
2760         }
2761         if (do_minor_collection) {
2762                 stop_world ();
2763                 collect_nursery (size);
2764                 DEBUG (2, fprintf (gc_debug_file, "Heap size: %zd, LOS size: %zd\n", total_alloc, los_memory_usage));
2765                 restart_world ();
2766                 /* this also sets the proper pointers for the next allocation */
2767                 if (!search_fragment_for_size (size)) {
2768                         int i;
2769                         /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
2770                         DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
2771                         for (i = 0; i < last_num_pinned; ++i) {
2772                                 DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", pin_queue [i], safe_name (pin_queue [i]), safe_object_get_size (pin_queue [i])));
2773                         }
2774                         degraded_mode = 1;
2775                 }
2776         }
2777         //report_internal_mem_usage ();
2778 }
2779
2780 /*
2781  * ######################################################################
2782  * ########  Memory allocation from the OS
2783  * ######################################################################
2784  * This section of code deals with getting memory from the OS and
2785  * allocating memory for GC-internal data structures.
2786  * Internal memory can be handled with a freelist for small objects.
2787  */
2788
2789 /*
2790  * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
2791  * This must not require any lock.
2792  */
2793 static void*
2794 get_os_memory (size_t size, int activate)
2795 {
2796         void *ptr;
2797         unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
2798
2799         prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
2800         size += pagesize - 1;
2801         size &= ~(pagesize - 1);
2802         ptr = mono_valloc (0, size, prot_flags);
2803         return ptr;
2804 }
2805
2806 /*
2807  * Free the memory returned by get_os_memory (), returning it to the OS.
2808  */
2809 static void
2810 free_os_memory (void *addr, size_t size)
2811 {
2812         munmap (addr, size);
2813 }
2814
2815 /*
2816  * Debug reporting.
2817  */
2818 static void
2819 report_pinned_chunk (PinnedChunk *chunk, int seq) {
2820         void **p;
2821         int i, free_pages, num_free, free_mem;
2822         free_pages = 0;
2823         for (i = 0; i < chunk->num_pages; ++i) {
2824                 if (!chunk->page_sizes [i])
2825                         free_pages++;
2826         }
2827         printf ("Pinned chunk %d at %p, size: %d, pages: %d, free: %d\n", seq, chunk, chunk->num_pages * FREELIST_PAGESIZE, chunk->num_pages, free_pages);
2828         free_mem = FREELIST_PAGESIZE * free_pages;
2829         for (i = 0; i < FREELIST_NUM_SLOTS; ++i) {
2830                 if (!chunk->free_list [i])
2831                         continue;
2832                 num_free = 0;
2833                 p = chunk->free_list [i];
2834                 while (p) {
2835                         num_free++;
2836                         p = *p;
2837                 }
2838                 printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
2839                 free_mem += freelist_sizes [i] * num_free;
2840         }
2841         printf ("\tfree memory in chunk: %d\n", free_mem);
2842 }
2843
2844 /*
2845  * Debug reporting.
2846  */
2847 static G_GNUC_UNUSED void
2848 report_internal_mem_usage (void) {
2849         PinnedChunk *chunk;
2850         int i;
2851         printf ("Internal memory usage:\n");
2852         i = 0;
2853         for (chunk = internal_chunk_list; chunk; chunk = chunk->next) {
2854                 report_pinned_chunk (chunk, i++);
2855         }
2856         printf ("Pinned memory usage:\n");
2857         i = 0;
2858         for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2859                 report_pinned_chunk (chunk, i++);
2860         }
2861 }
2862
2863 /*
2864  * the array of pointers from @start to @end contains conservative
2865  * pointers to objects inside @chunk: mark each referenced object
2866  * with the PIN bit.
2867  */
2868 static void
2869 mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end)
2870 {
2871         for (; start < end; start++) {
2872                 char *addr = *start;
2873                 int offset = (char*)addr - (char*)chunk;
2874                 int page = offset / FREELIST_PAGESIZE;
2875                 int obj_offset = page == 0? offset - ((char*)chunk->start_data - (char*)chunk): offset % FREELIST_PAGESIZE;
2876                 int slot_size = chunk->page_sizes [page];
2877                 void **ptr;
2878                 /* the page is not allocated */
2879                 if (!slot_size)
2880                         continue;
2881                 /* would be faster if we restrict the sizes to power of two,
2882                  * but that's a waste of memory: need to measure. it could reduce
2883                  * fragmentation since there are less pages needed, if for example
2884                  * someone interns strings of each size we end up with one page per
2885                  * interned string (still this is just ~40 KB): with more fine-grained sizes
2886                  * this increases the number of used pages.
2887                  */
2888                 if (page == 0) {
2889                         obj_offset /= slot_size;
2890                         obj_offset *= slot_size;
2891                         addr = (char*)chunk->start_data + obj_offset;
2892                 } else {
2893                         obj_offset /= slot_size;
2894                         obj_offset *= slot_size;
2895                         addr = (char*)chunk + page * FREELIST_PAGESIZE + obj_offset;
2896                 }
2897                 ptr = (void**)addr;
2898                 /* if the vtable is inside the chunk it's on the freelist, so skip */
2899                 if (*ptr && (*ptr < (void*)chunk->start_data || *ptr > (void*)((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE))) {
2900                         pin_object (addr);
2901                         DEBUG (6, fprintf (gc_debug_file, "Marked pinned object %p (%s) from roots\n", addr, safe_name (addr)));
2902                 }
2903         }
2904 }
2905
2906 static void
2907 sweep_pinned_objects (void)
2908 {
2909         PinnedChunk *chunk;
2910         int i, obj_size;
2911         char *p, *endp;
2912         void **ptr;
2913         void *end_chunk;
2914         for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2915                 end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
2916                 DEBUG (6, fprintf (gc_debug_file, "Sweeping pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk));
2917                 for (i = 0; i < chunk->num_pages; ++i) {
2918                         obj_size = chunk->page_sizes [i];
2919                         if (!obj_size)
2920                                 continue;
2921                         p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
2922                         endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
2923                         DEBUG (6, fprintf (gc_debug_file, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp));
2924                         while (p + obj_size <= endp) {
2925                                 ptr = (void**)p;
2926                                 DEBUG (9, fprintf (gc_debug_file, "Considering %p (vtable: %p)\n", ptr, *ptr));
2927                                 /* if the first word (the vtable) is outside the chunk we have an object */
2928                                 if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk)) {
2929                                         if (object_is_pinned (ptr)) {
2930                                                 unpin_object (ptr);
2931                                                 DEBUG (6, fprintf (gc_debug_file, "Unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
2932                                         } else {
2933                                                 /* FIXME: add to freelist */
2934                                                 DEBUG (6, fprintf (gc_debug_file, "Going to free unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
2935                                         }
2936                                 }
2937                                 p += obj_size;
2938                         }
2939                 }
2940         }
2941 }
2942
2943 static void
2944 scan_from_pinned_objects (char *addr_start, char *addr_end)
2945 {
2946         PinnedChunk *chunk;
2947         int i, obj_size;
2948         char *p, *endp;
2949         void **ptr;
2950         void *end_chunk;
2951         for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2952                 end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
2953                 DEBUG (6, fprintf (gc_debug_file, "Scanning pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk));
2954                 for (i = 0; i < chunk->num_pages; ++i) {
2955                         obj_size = chunk->page_sizes [i];
2956                         if (!obj_size)
2957                                 continue;
2958                         p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
2959                         endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
2960                         DEBUG (6, fprintf (gc_debug_file, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp));
2961                         while (p + obj_size <= endp) {
2962                                 ptr = (void**)p;
2963                                 DEBUG (9, fprintf (gc_debug_file, "Considering %p (vtable: %p)\n", ptr, *ptr));
2964                                 /* if the first word (the vtable) is outside the chunk we have an object */
2965                                 if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk)) {
2966                                         DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of alloc_pinned %p (%s)\n", i, ptr, safe_name (ptr)));
2967                                         // FIXME: Put objects without references into separate chunks
2968                                         // which do not need to be scanned
2969                                         scan_object ((char*)ptr, addr_start, addr_end);
2970                                 }
2971                                 p += obj_size;
2972                         }
2973                 }
2974         }
2975 }
2976
2977 /*
2978  * Find the slot number in the freelist for memory chunks that
2979  * can contain @size objects.
2980  */
2981 static int
2982 slot_for_size (size_t size)
2983 {
2984         int slot;
2985         /* do a binary search or lookup table later. */
2986         for (slot = 0; slot < FREELIST_NUM_SLOTS; ++slot) {
2987                 if (freelist_sizes [slot] >= size)
2988                         return slot;
2989         }
2990         g_assert_not_reached ();
2991         return -1;
2992 }
2993
2994 /*
2995  * Build a free list for @size memory chunks from the memory area between
2996  * start_page and end_page.
2997  */
2998 static void
2999 build_freelist (PinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
3000 {
3001         void **p, **end;
3002         int count = 0;
3003         /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
3004         p = (void**)start_page;
3005         end = (void**)(end_page - size);
3006         g_assert (!chunk->free_list [slot]);
3007         chunk->free_list [slot] = p;
3008         while ((char*)p + size <= (char*)end) {
3009                 count++;
3010                 *p = (void*)((char*)p + size);
3011                 p = *p;
3012         }
3013         *p = NULL;
3014         /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
3015 }
3016
3017 static PinnedChunk*
3018 alloc_pinned_chunk (size_t size)
3019 {
3020         PinnedChunk *chunk;
3021         int offset;
3022
3023         size += pagesize; /* at least one page */
3024         size += pagesize - 1;
3025         size &= ~(pagesize - 1);
3026         if (size < PINNED_CHUNK_MIN_SIZE * 2)
3027                 size = PINNED_CHUNK_MIN_SIZE * 2;
3028         chunk = get_os_memory (size, TRUE);
3029         UPDATE_HEAP_BOUNDARIES (chunk, ((char*)chunk + size));
3030         total_alloc += size;
3031
3032         /* setup the bookeeping fields */
3033         chunk->num_pages = size / FREELIST_PAGESIZE;
3034         offset = G_STRUCT_OFFSET (PinnedChunk, data);
3035         chunk->page_sizes = (void*)((char*)chunk + offset);
3036         offset += sizeof (int) * chunk->num_pages;
3037         offset += ALLOC_ALIGN - 1;
3038         offset &= ~(ALLOC_ALIGN - 1);
3039         chunk->free_list = (void*)((char*)chunk + offset);
3040         offset += sizeof (void*) * FREELIST_NUM_SLOTS;
3041         offset += ALLOC_ALIGN - 1;
3042         offset &= ~(ALLOC_ALIGN - 1);
3043         chunk->start_data = (void*)((char*)chunk + offset);
3044
3045         /* allocate the first page to the freelist */
3046         chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
3047         build_freelist (chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE, chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
3048         DEBUG (4, fprintf (gc_debug_file, "Allocated pinned chunk %p, size: %zd\n", chunk, size));
3049         min_pinned_chunk_addr = MIN (min_pinned_chunk_addr, (char*)chunk->start_data);
3050         max_pinned_chunk_addr = MAX (max_pinned_chunk_addr, ((char*)chunk + size));
3051         return chunk;
3052 }
3053
3054 /* assumes freelist for slot is empty, so try to alloc a new page */
3055 static void*
3056 get_chunk_freelist (PinnedChunk *chunk, int slot)
3057 {
3058         int i;
3059         void **p;
3060         p = chunk->free_list [slot];
3061         if (p) {
3062                 chunk->free_list [slot] = *p;
3063                 return p;
3064         }
3065         for (i = 0; i < chunk->num_pages; ++i) {
3066                 int size;
3067                 if (chunk->page_sizes [i])
3068                         continue;
3069                 size = freelist_sizes [slot];
3070                 chunk->page_sizes [i] = size;
3071                 build_freelist (chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
3072                 break;
3073         }
3074         /* try again */
3075         p = chunk->free_list [slot];
3076         if (p) {
3077                 chunk->free_list [slot] = *p;
3078                 return p;
3079         }
3080         return NULL;
3081 }
3082
3083 static void*
3084 alloc_from_freelist (size_t size)
3085 {
3086         int slot;
3087         void *res = NULL;
3088         PinnedChunk *pchunk;
3089         slot = slot_for_size (size);
3090         /*g_print ("using slot %d for size %d (slot size: %d)\n", slot, size, freelist_sizes [slot]);*/
3091         g_assert (size <= freelist_sizes [slot]);
3092         for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->next) {
3093                 void **p = pchunk->free_list [slot];
3094                 if (p) {
3095                         /*g_print ("found freelist for slot %d in chunk %p, returning %p, next %p\n", slot, pchunk, p, *p);*/
3096                         pchunk->free_list [slot] = *p;
3097                         return p;
3098                 }
3099         }
3100         for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->next) {
3101                 res = get_chunk_freelist (pchunk, slot);
3102                 if (res)
3103                         return res;
3104         }
3105         pchunk = alloc_pinned_chunk (size);
3106         /* FIXME: handle OOM */
3107         pchunk->next = pinned_chunk_list;
3108         pinned_chunk_list = pchunk;
3109         res = get_chunk_freelist (pchunk, slot);
3110         return res;
3111 }
3112
3113 /* used for the GC-internal data structures */
3114 /* FIXME: add support for bigger sizes by allocating more than one page
3115  * in the chunk.
3116  */
3117 static void*
3118 get_internal_mem (size_t size)
3119 {
3120         return calloc (1, size);
3121 #if 0
3122         int slot;
3123         void *res = NULL;
3124         PinnedChunk *pchunk;
3125         slot = slot_for_size (size);
3126         g_assert (size <= freelist_sizes [slot]);
3127         for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->next) {
3128                 void **p = pchunk->free_list [slot];
3129                 if (p) {
3130                         pchunk->free_list [slot] = *p;
3131                         return p;
3132                 }
3133         }
3134         for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->next) {
3135                 res = get_chunk_freelist (pchunk, slot);
3136                 if (res)
3137                         return res;
3138         }
3139         pchunk = alloc_pinned_chunk (size);
3140         /* FIXME: handle OOM */
3141         pchunk->next = internal_chunk_list;
3142         internal_chunk_list = pchunk;
3143         res = get_chunk_freelist (pchunk, slot);
3144         return res;
3145 #endif
3146 }
3147
3148 static void
3149 free_internal_mem (void *addr)
3150 {
3151         free (addr);
3152 #if 0
3153         PinnedChunk *pchunk;
3154         for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->next) {
3155                 /*printf ("trying to free %p in %p (pages: %d)\n", addr, pchunk, pchunk->num_pages);*/
3156                 if (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE) {
3157                         int offset = (char*)addr - (char*)pchunk;
3158                         int page = offset / FREELIST_PAGESIZE;
3159                         int slot = slot_for_size (pchunk->page_sizes [page]);
3160                         void **p = addr;
3161                         *p = pchunk->free_list [slot];
3162                         pchunk->free_list [slot] = p;
3163                         return;
3164                 }
3165         }
3166         printf ("free of %p failed\n", addr);
3167         g_assert_not_reached ();
3168 #endif
3169 }
3170
3171 /*
3172  * ######################################################################
3173  * ########  Object allocation
3174  * ######################################################################
3175  * This section of code deals with allocating memory for objects.
3176  * There are several ways:
3177  * *) allocate large objects
3178  * *) allocate normal objects
3179  * *) fast lock-free allocation
3180  * *) allocation of pinned objects
3181  */
3182
3183 static void
3184 free_large_object (LOSObject *obj)
3185 {
3186         size_t size = obj->size;
3187         DEBUG (4, fprintf (gc_debug_file, "Freed large object %p, size %zd\n", obj->data, obj->size));
3188
3189         los_memory_usage -= size;
3190         size += sizeof (LOSObject);
3191         size += pagesize - 1;
3192         size &= ~(pagesize - 1);
3193         total_alloc -= size;
3194         los_num_objects--;
3195         free_os_memory (obj, size);
3196 }
3197
3198 /*
3199  * Objects with size >= 64KB are allocated in the large object space.
3200  * They are currently kept track of with a linked list.
3201  * They don't move, so there is no need to pin them during collection
3202  * and we avoid the memcpy overhead.
3203  */
3204 static void* __attribute__((noinline))
3205 alloc_large_inner (MonoVTable *vtable, size_t size)
3206 {
3207         LOSObject *obj;
3208         void **vtslot;
3209         size_t alloc_size;
3210         int just_did_major_gc = FALSE;
3211
3212         if (los_memory_usage > next_los_collection) {
3213                 DEBUG (4, fprintf (gc_debug_file, "Should trigger major collection: req size %zd (los already: %zu, limit: %zu)\n", size, los_memory_usage, next_los_collection));
3214                 just_did_major_gc = TRUE;
3215                 stop_world ();
3216                 major_collection ();
3217                 restart_world ();
3218                 /* later increase based on a percent of the heap size */
3219                 next_los_collection = los_memory_usage + 5*1024*1024;
3220         }
3221         alloc_size = size;
3222         alloc_size += sizeof (LOSObject);
3223         alloc_size += pagesize - 1;
3224         alloc_size &= ~(pagesize - 1);
3225         /* FIXME: handle OOM */
3226         obj = get_os_memory (alloc_size, TRUE);
3227         obj->size = size;
3228         vtslot = (void**)obj->data;
3229         *vtslot = vtable;
3230         total_alloc += alloc_size;
3231         UPDATE_HEAP_BOUNDARIES (obj->data, (char*)obj->data + size);
3232         obj->next = los_object_list;
3233         los_object_list = obj;
3234         los_memory_usage += size;
3235         los_num_objects++;
3236         DEBUG (4, fprintf (gc_debug_file, "Allocated large object %p, vtable: %p (%s), size: %zd\n", obj->data, vtable, vtable->klass->name, size));
3237         return obj->data;
3238 }
3239
3240 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
3241  * an object of size @size
3242  * Return FALSE if not found (which means we need a collection)
3243  */
3244 static gboolean
3245 search_fragment_for_size (size_t size)
3246 {
3247         Fragment *frag, *prev;
3248         DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
3249
3250         if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3251                 /* Clear the remaining space, pinning depends on this */
3252                 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3253
3254         prev = NULL;
3255         for (frag = nursery_fragments; frag; frag = frag->next) {
3256                 if (size <= (frag->fragment_end - frag->fragment_start)) {
3257                         /* remove from the list */
3258                         if (prev)
3259                                 prev->next = frag->next;
3260                         else
3261                                 nursery_fragments = frag->next;
3262                         nursery_next = frag->fragment_start;
3263                         nursery_frag_real_end = frag->fragment_end;
3264
3265                         DEBUG (4, fprintf (gc_debug_file, "Using nursery fragment %p-%p, size: %zd (req: %zd)\n", nursery_next, nursery_frag_real_end, nursery_frag_real_end - nursery_next, size));
3266                         frag->next = fragment_freelist;
3267                         fragment_freelist = frag;
3268                         return TRUE;
3269                 }
3270                 prev = frag;
3271         }
3272         return FALSE;
3273 }
3274
3275 /*
3276  * size is already rounded up and we hold the GC lock.
3277  */
3278 static void*
3279 alloc_degraded (MonoVTable *vtable, size_t size)
3280 {
3281         GCMemSection *section;
3282         void **p = NULL;
3283         for (section = section_list; section; section = section->next) {
3284                 if (section != nursery_section && (section->end_data - section->next_data) >= size) {
3285                         p = (void**)section->next_data;
3286                         break;
3287                 }
3288         }
3289         if (!p) {
3290                 section = alloc_section (nursery_section->size * 4);
3291                 /* FIXME: handle OOM */
3292                 p = (void**)section->next_data;
3293         }
3294         section->next_data += size;
3295         degraded_mode += size;
3296         DEBUG (3, fprintf (gc_debug_file, "Allocated (degraded) object %p, vtable: %p (%s), size: %zd in section %p\n", p, vtable, vtable->klass->name, size, section));
3297         *p = vtable;
3298         return p;
3299 }
3300
3301 /*
3302  * Provide a variant that takes just the vtable for small fixed-size objects.
3303  * The aligned size is already computed and stored in vt->gc_descr.
3304  * Note: every SCAN_START_SIZE or so we are given the chance to do some special
3305  * processing. We can keep track of where objects start, for example,
3306  * so when we scan the thread stacks for pinned objects, we can start
3307  * a search for the pinned object in SCAN_START_SIZE chunks.
3308  */
3309 void*
3310 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
3311 {
3312         /* FIXME: handle OOM */
3313         void **p;
3314         char *new_next;
3315         int dummy;
3316         gboolean res;
3317         size += ALLOC_ALIGN - 1;
3318         size &= ~(ALLOC_ALIGN - 1);
3319
3320         g_assert (vtable->gc_descr);
3321
3322         if (G_UNLIKELY (collect_before_allocs)) {
3323                 int dummy;
3324
3325                 if (nursery_section) {
3326                         LOCK_GC;
3327
3328                         update_current_thread_stack (&dummy);
3329                         stop_world ();
3330                         collect_nursery (0);
3331                         restart_world ();
3332                         if (!degraded_mode && !search_fragment_for_size (size)) {
3333                                 // FIXME:
3334                                 g_assert_not_reached ();
3335                         }
3336                         UNLOCK_GC;
3337                 }
3338         }
3339
3340         /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
3341
3342         p = (void**)tlab_next;
3343         /* FIXME: handle overflow */
3344         new_next = (char*)p + size;
3345         tlab_next = new_next;
3346
3347         if (G_LIKELY (new_next < tlab_temp_end)) {
3348                 /* Fast path */
3349
3350                 /* 
3351                  * FIXME: We might need a memory barrier here so the change to tlab_next is 
3352                  * visible before the vtable store.
3353                  */
3354
3355                 DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3356                 *p = vtable;
3357                 
3358                 return p;
3359         }
3360
3361         /* Slow path */
3362
3363         /* there are two cases: the object is too big or we run out of space in the TLAB */
3364         /* we also reach here when the thread does its first allocation after a minor 
3365          * collection, since the tlab_ variables are initialized to NULL.
3366          * there can be another case (from ORP), if we cooperate with the runtime a bit:
3367          * objects that need finalizers can have the high bit set in their size
3368          * so the above check fails and we can readily add the object to the queue.
3369          * This avoids taking again the GC lock when registering, but this is moot when
3370          * doing thread-local allocation, so it may not be a good idea.
3371          */
3372         LOCK_GC;
3373         if (size > MAX_SMALL_OBJ_SIZE) {
3374                 /* get ready for possible collection */
3375                 update_current_thread_stack (&dummy);
3376                 tlab_next -= size;
3377                 p = alloc_large_inner (vtable, size);
3378         } else {
3379                 if (tlab_next >= tlab_real_end) {
3380                         /* 
3381                          * Run out of space in the TLAB. When this happens, some amount of space
3382                          * remains in the TLAB, but not enough to satisfy the current allocation
3383                          * request. Currently, we retire the TLAB in all cases, later we could
3384                          * keep it if the remaining space is above a treshold, and satisfy the
3385                          * allocation directly from the nursery.
3386                          */
3387                         tlab_next -= size;
3388                         /* when running in degraded mode, we continue allocing that way
3389                          * for a while, to decrease the number of useless nursery collections.
3390                          */
3391                         if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
3392                                 p = alloc_degraded (vtable, size);
3393                                 UNLOCK_GC;
3394                                 return p;
3395                         }
3396
3397                         if (size > tlab_size) {
3398                                 /* Allocate directly from the nursery */
3399                                 if (nursery_next + size >= nursery_frag_real_end) {
3400                                         if (!search_fragment_for_size (size)) {
3401                                                 /* get ready for possible collection */
3402                                                 update_current_thread_stack (&dummy);
3403                                                 minor_collect_or_expand_inner (size);
3404                                                 if (degraded_mode) {
3405                                                         p = alloc_degraded (vtable, size);
3406                                                         UNLOCK_GC;
3407                                                         return p;
3408                                                 }
3409                                         }
3410                                 }
3411
3412                                 p = (void*)nursery_next;
3413                                 nursery_next += size;
3414                                 if (nursery_next > nursery_frag_real_end) {
3415                                         // no space left
3416                                         g_assert (0);
3417                                 }
3418
3419                                 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3420                                         memset (p, 0, size);
3421                         } else {
3422                                 if (tlab_start)
3423                                         DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", tlab_start, tlab_real_end, (long)(tlab_real_end - tlab_next - size)));
3424
3425                                 if (nursery_next + tlab_size >= nursery_frag_real_end) {
3426                                         res = search_fragment_for_size (tlab_size);
3427                                         if (!res) {
3428                                                 /* get ready for possible collection */
3429                                                 update_current_thread_stack (&dummy);
3430                                                 minor_collect_or_expand_inner (tlab_size);
3431                                                 if (degraded_mode) {
3432                                                         p = alloc_degraded (vtable, size);
3433                                                         UNLOCK_GC;
3434                                                         return p;
3435                                                 }
3436                                         }
3437                                 }
3438
3439                                 /* Allocate a new TLAB from the current nursery fragment */
3440                                 tlab_start = nursery_next;
3441                                 nursery_next += tlab_size;
3442                                 tlab_next = tlab_start;
3443                                 tlab_real_end = tlab_start + tlab_size;
3444                                 tlab_temp_end = tlab_start + MIN (SCAN_START_SIZE, tlab_size);
3445
3446                                 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
3447                                         memset (tlab_start, 0, tlab_size);
3448
3449                                 /* Allocate from the TLAB */
3450                                 p = (void*)tlab_next;
3451                                 tlab_next += size;
3452                                 g_assert (tlab_next <= tlab_real_end);
3453
3454                                 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3455                         }
3456                 } else {
3457                         /* Reached tlab_temp_end */
3458
3459                         /* record the scan start so we can find pinned objects more easily */
3460                         nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
3461                         /* we just bump tlab_temp_end as well */
3462                         tlab_temp_end = MIN (tlab_real_end, tlab_next + SCAN_START_SIZE);
3463                         DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", tlab_next, tlab_temp_end));
3464                 }
3465         }
3466
3467         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3468         *p = vtable;
3469
3470         UNLOCK_GC;
3471
3472         return p;
3473 }
3474
3475 /*
3476  * To be used for interned strings and possibly MonoThread, reflection handles.
3477  * We may want to explicitly free these objects.
3478  */
3479 void*
3480 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
3481 {
3482         /* FIXME: handle OOM */
3483         void **p;
3484         size += ALLOC_ALIGN - 1;
3485         size &= ~(ALLOC_ALIGN - 1);
3486         LOCK_GC;
3487         if (size > MAX_FREELIST_SIZE) {
3488                 update_current_thread_stack (&p);
3489                 /* large objects are always pinned anyway */
3490                 p = alloc_large_inner (vtable, size);
3491         } else {
3492                 p = alloc_from_freelist (size);
3493                 memset (p, 0, size);
3494         }
3495         DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
3496         *p = vtable;
3497         UNLOCK_GC;
3498         return p;
3499 }
3500
3501 /*
3502  * ######################################################################
3503  * ########  Finalization support
3504  * ######################################################################
3505  */
3506
3507 /*
3508  * this is valid for the nursery: if the object has been forwarded it means it's
3509  * still refrenced from a root. If it is pinned it's still alive as well.
3510  * Return TRUE if @obj is ready to be finalized.
3511  */
3512 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
3513
3514 static void
3515 finalize_in_range (char *start, char *end)
3516 {
3517         FinalizeEntry *entry, *prev;
3518         int i;
3519         if (no_finalize)
3520                 return;
3521         for (i = 0; i < finalizable_hash_size; ++i) {
3522                 prev = NULL;
3523                 for (entry = finalizable_hash [i]; entry;) {
3524                         if ((char*)entry->object >= start && (char*)entry->object < end && ((char*)entry->object < to_space || (char*)entry->object >= to_space_end)) {
3525                                 if (object_is_fin_ready (entry->object)) {
3526                                         char *from;
3527                                         FinalizeEntry *next;
3528                                         /* remove and put in fin_ready_list */
3529                                         if (prev)
3530                                                 prev->next = entry->next;
3531                                         else
3532                                                 finalizable_hash [i] = entry->next;
3533                                         next = entry->next;
3534                                         num_ready_finalizers++;
3535                                         num_registered_finalizers--;
3536                                         entry->next = fin_ready_list;
3537                                         fin_ready_list = entry;
3538                                         /* Make it survive */
3539                                         from = entry->object;
3540                                         entry->object = copy_object (entry->object, start, end);
3541                                         DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", entry->object, safe_name (entry->object), from, num_ready_finalizers, num_registered_finalizers));
3542                                         entry = next;
3543                                         continue;
3544                                 } else {
3545                                         /* update pointer */
3546                                         DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s)\n", entry->object, safe_name (entry->object)));
3547                                         entry->object = copy_object (entry->object, start, end);
3548                                 }
3549                         }
3550                         prev = entry;
3551                         entry = entry->next;
3552                 }
3553         }
3554 }
3555
3556 static void
3557 null_link_in_range (char *start, char *end)
3558 {
3559         FinalizeEntry *entry, *prev;
3560         int i;
3561         for (i = 0; i < disappearing_link_hash_size; ++i) {
3562                 prev = NULL;
3563                 for (entry = disappearing_link_hash [i]; entry;) {
3564                         if ((char*)entry->object >= start && (char*)entry->object < end && ((char*)entry->object < to_space || (char*)entry->object >= to_space_end)) {
3565                                 if (object_is_fin_ready (entry->object)) {
3566                                         void **p = entry->data;
3567                                         FinalizeEntry *old;
3568                                         *p = NULL;
3569                                         /* remove from list */
3570                                         if (prev)
3571                                                 prev->next = entry->next;
3572                                         else
3573                                                 disappearing_link_hash [i] = entry->next;
3574                                         DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, entry->object));
3575                                         old = entry->next;
3576                                         free_internal_mem (entry);
3577                                         entry = old;
3578                                         num_disappearing_links--;
3579                                         continue;
3580                                 } else {
3581                                         void **link;
3582                                         /* update pointer if it's moved
3583                                          * FIXME: what if an object is moved earlier?
3584                                          */
3585                                         entry->object = copy_object (entry->object, start, end);
3586                                         DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->data, entry->object));
3587                                         link = entry->data;
3588                                         *link = entry->object;
3589                                 }
3590                         }
3591                         prev = entry;
3592                         entry = entry->next;
3593                 }
3594         }
3595 }
3596
3597 /**
3598  * mono_gc_finalizers_for_domain:
3599  * @domain: the unloading appdomain
3600  * @out_array: output array
3601  * @out_size: size of output array
3602  *
3603  * Store inside @out_array up to @out_size objects that belong to the unloading
3604  * appdomain @domain. Returns the number of stored items. Can be called repeteadly
3605  * until it returns 0.
3606  * The items are removed from the finalizer data structure, so the caller is supposed
3607  * to finalize them.
3608  * @out_array should be on the stack to allow the GC to know the objects are still alive.
3609  */
3610 int
3611 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
3612 {
3613         FinalizeEntry *entry, *prev;
3614         int i, count;
3615         if (no_finalize || !out_size || !out_array)
3616                 return 0;
3617         count = 0;
3618         LOCK_GC;
3619         for (i = 0; i < finalizable_hash_size; ++i) {
3620                 prev = NULL;
3621                 for (entry = finalizable_hash [i]; entry;) {
3622                         if (mono_object_domain (entry->object) == domain) {
3623                                 FinalizeEntry *next;
3624                                 /* remove and put in out_array */
3625                                 if (prev)
3626                                         prev->next = entry->next;
3627                                 else
3628                                         finalizable_hash [i] = entry->next;
3629                                 next = entry->next;
3630                                 num_registered_finalizers--;
3631                                 out_array [count ++] = entry->object;
3632                                 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", entry->object, safe_name (entry->object), num_ready_finalizers, num_registered_finalizers));
3633                                 entry = next;
3634                                 if (count == out_size) {
3635                                         UNLOCK_GC;
3636                                         return count;
3637                                 }
3638                                 continue;
3639                         }
3640                         prev = entry;
3641                         entry = entry->next;
3642                 }
3643         }
3644         UNLOCK_GC;
3645         return count;
3646 }
3647
3648 static void
3649 rehash_fin_table (void)
3650 {
3651         int i;
3652         unsigned int hash;
3653         FinalizeEntry **new_hash;
3654         FinalizeEntry *entry, *next;
3655         int new_size = g_spaced_primes_closest (num_registered_finalizers);
3656
3657         new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*));
3658         for (i = 0; i < finalizable_hash_size; ++i) {
3659                 for (entry = finalizable_hash [i]; entry; entry = next) {
3660                         hash = mono_object_hash (entry->object) % new_size;
3661                         next = entry->next;
3662                         entry->next = new_hash [hash];
3663                         new_hash [hash] = entry;
3664                 }
3665         }
3666         free_internal_mem (finalizable_hash);
3667         finalizable_hash = new_hash;
3668         finalizable_hash_size = new_size;
3669 }
3670
3671 void
3672 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
3673 {
3674         FinalizeEntry *entry, *prev;
3675         unsigned int hash;
3676         if (no_finalize)
3677                 return;
3678         hash = mono_object_hash (obj);
3679         LOCK_GC;
3680         if (num_registered_finalizers >= finalizable_hash_size * 2)
3681                 rehash_fin_table ();
3682         hash %= finalizable_hash_size;
3683         prev = NULL;
3684         for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
3685                 if (entry->object == obj) {
3686                         if (user_data) {
3687                                 entry->data = user_data;
3688                         } else {
3689                                 /* remove from the list */
3690                                 if (prev)
3691                                         prev->next = entry->next;
3692                                 else
3693                                         finalizable_hash [hash] = entry->next;
3694                                 num_registered_finalizers--;
3695                                 DEBUG (5, fprintf (gc_debug_file, "Removed finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, num_registered_finalizers));
3696                                 free_internal_mem (entry);
3697                         }
3698                         UNLOCK_GC;
3699                         return;
3700                 }
3701                 prev = entry;
3702         }
3703         if (!user_data) {
3704                 /* request to deregister, but already out of the list */
3705                 UNLOCK_GC;
3706                 return;
3707         }
3708         entry = get_internal_mem (sizeof (FinalizeEntry));
3709         entry->object = obj;
3710         entry->data = user_data;
3711         entry->next = finalizable_hash [hash];
3712         finalizable_hash [hash] = entry;
3713         num_registered_finalizers++;
3714         DEBUG (5, fprintf (gc_debug_file, "Added finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, num_registered_finalizers));
3715         UNLOCK_GC;
3716 }
3717
3718 static void
3719 rehash_dislink (void)
3720 {
3721         int i;
3722         unsigned int hash;
3723         FinalizeEntry **new_hash;
3724         FinalizeEntry *entry, *next;
3725         int new_size = g_spaced_primes_closest (num_disappearing_links);
3726
3727         new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*));
3728         for (i = 0; i < disappearing_link_hash_size; ++i) {
3729                 for (entry = disappearing_link_hash [i]; entry; entry = next) {
3730                         hash = mono_aligned_addr_hash (entry->data) % new_size;
3731                         next = entry->next;
3732                         entry->next = new_hash [hash];
3733                         new_hash [hash] = entry;
3734                 }
3735         }
3736         free_internal_mem (disappearing_link_hash);
3737         disappearing_link_hash = new_hash;
3738         disappearing_link_hash_size = new_size;
3739 }
3740
3741 static void
3742 mono_gc_register_disappearing_link (MonoObject *obj, void *link)
3743 {
3744         FinalizeEntry *entry, *prev;
3745         unsigned int hash;
3746         LOCK_GC;
3747
3748         if (num_disappearing_links >= disappearing_link_hash_size * 2)
3749                 rehash_dislink ();
3750         /* FIXME: add check that link is not in the heap */
3751         hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
3752         entry = disappearing_link_hash [hash];
3753         prev = NULL;
3754         for (; entry; entry = entry->next) {
3755                 /* link already added */
3756                 if (link == entry->data) {
3757                         /* NULL obj means remove */
3758                         if (obj == NULL) {
3759                                 if (prev)
3760                                         prev->next = entry->next;
3761                                 else
3762                                         disappearing_link_hash [hash] = entry->next;
3763                                 num_disappearing_links--;
3764                                 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d)\n", entry, num_disappearing_links));
3765                                 free_internal_mem (entry);
3766                         } else {
3767                                 entry->object = obj; /* we allow the change of object */
3768                         }
3769                         UNLOCK_GC;
3770                         return;
3771                 }
3772                 prev = entry;
3773         }
3774         entry = get_internal_mem (sizeof (FinalizeEntry));
3775         entry->object = obj;
3776         entry->data = link;
3777         entry->next = disappearing_link_hash [hash];
3778         disappearing_link_hash [hash] = entry;
3779         num_disappearing_links++;
3780         DEBUG (5, fprintf (gc_debug_file, "Added dislink %p for object: %p (%s) at %p\n", entry, obj, obj->vtable->klass->name, link));
3781         UNLOCK_GC;
3782 }
3783
3784 int
3785 mono_gc_invoke_finalizers (void)
3786 {
3787         FinalizeEntry *entry;
3788         int count = 0;
3789         void *obj;
3790         /* FIXME: batch to reduce lock contention */
3791         while (fin_ready_list) {
3792                 LOCK_GC;
3793                 entry = fin_ready_list;
3794                 if (entry) {
3795                         fin_ready_list = entry->next;
3796                         num_ready_finalizers--;
3797                         obj = entry->object;
3798                         DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
3799                 }
3800                 UNLOCK_GC;
3801                 if (entry) {
3802                         void (*callback)(void *, void*) = entry->data;
3803                         entry->next = NULL;
3804                         obj = entry->object;
3805                         count++;
3806                         /* the object is on the stack so it is pinned */
3807                         /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
3808                         callback (obj, NULL);
3809                         free_internal_mem (entry);
3810                 }
3811         }
3812         return count;
3813 }
3814
3815 gboolean
3816 mono_gc_pending_finalizers (void)
3817 {
3818         return fin_ready_list != NULL;
3819 }
3820
3821 /* Negative value to remove */
3822 void
3823 mono_gc_add_memory_pressure (gint64 value)
3824 {
3825         /* FIXME: Use interlocked functions */
3826         LOCK_GC;
3827         memory_pressure += value;
3828         UNLOCK_GC;
3829 }
3830
3831 /*
3832  * ######################################################################
3833  * ########  registered roots support
3834  * ######################################################################
3835  */
3836
3837 static void
3838 rehash_roots (gboolean pinned)
3839 {
3840         int i;
3841         unsigned int hash;
3842         RootRecord **new_hash;
3843         RootRecord *entry, *next;
3844         int new_size;
3845
3846         new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
3847         new_hash = get_internal_mem (new_size * sizeof (RootRecord*));
3848         for (i = 0; i < roots_hash_size [pinned]; ++i) {
3849                 for (entry = roots_hash [pinned][i]; entry; entry = next) {
3850                         hash = mono_aligned_addr_hash (entry->start_root) % new_size;
3851                         next = entry->next;
3852                         entry->next = new_hash [hash];
3853                         new_hash [hash] = entry;
3854                 }
3855         }
3856         free_internal_mem (roots_hash [pinned]);
3857         roots_hash [pinned] = new_hash;
3858         roots_hash_size [pinned] = new_size;
3859 }
3860
3861 static RootRecord*
3862 find_root (int root_type, char *start, guint32 addr_hash)
3863 {
3864         RootRecord *new_root;
3865
3866         guint32 hash = addr_hash % roots_hash_size [root_type];
3867         for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
3868                 /* we allow changing the size and the descriptor (for thread statics etc) */
3869                 if (new_root->start_root == start) {
3870                         return new_root;
3871                 }
3872         }
3873
3874         return NULL;
3875 }
3876
3877 /*
3878  * We do not coalesce roots.
3879  */
3880 static int
3881 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
3882 {
3883         RootRecord *new_root;
3884         unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
3885         int i;
3886         LOCK_GC;
3887         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
3888                 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
3889                         rehash_roots (i);
3890         }
3891         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
3892                 new_root = find_root (i, start, addr_hash);
3893                 /* we allow changing the size and the descriptor (for thread statics etc) */
3894                 if (new_root) {
3895                         size_t old_size = new_root->end_root - new_root->start_root;
3896                         new_root->end_root = new_root->start_root + size;
3897                         g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
3898                                           ((new_root->root_desc == 0) && (descr == NULL)));
3899                         new_root->root_desc = (mword)descr;
3900                         roots_size += size;
3901                         roots_size -= old_size;
3902                         UNLOCK_GC;
3903                         return TRUE;
3904                 }
3905         }
3906         new_root = get_internal_mem (sizeof (RootRecord));
3907         if (new_root) {
3908                 new_root->start_root = start;
3909                 new_root->end_root = new_root->start_root + size;
3910                 new_root->root_desc = (mword)descr;
3911                 roots_size += size;
3912                 hash = addr_hash % roots_hash_size [root_type];
3913                 num_roots_entries [root_type]++;
3914                 new_root->next = roots_hash [root_type] [hash];
3915                 roots_hash [root_type][hash] = new_root;
3916                 DEBUG (3, fprintf (gc_debug_file, "Added root %p for range: %p-%p, descr: %p  (%d/%d bytes)\n", new_root, new_root->start_root, new_root->end_root, descr, (int)size, (int)roots_size));
3917         } else {
3918                 UNLOCK_GC;
3919                 return FALSE;
3920         }
3921         UNLOCK_GC;
3922         return TRUE;
3923 }
3924
3925 int
3926 mono_gc_register_root (char *start, size_t size, void *descr)
3927 {
3928         return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
3929 }
3930
3931 int
3932 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
3933 {
3934         return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
3935 }
3936
3937 void
3938 mono_gc_deregister_root (char* addr)
3939 {
3940         RootRecord *tmp, *prev;
3941         unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
3942         int root_type;
3943
3944         LOCK_GC;
3945         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
3946                 hash = addr_hash % roots_hash_size [root_type];
3947                 tmp = roots_hash [root_type][hash];
3948                 prev = NULL;
3949                 while (tmp) {
3950                         if (tmp->start_root == (char*)addr) {
3951                                 if (prev)
3952                                         prev->next = tmp->next;
3953                                 else
3954                                         roots_hash [root_type][hash] = tmp->next;
3955                                 roots_size -= (tmp->end_root - tmp->start_root);
3956                                 num_roots_entries [root_type]--;
3957                                 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
3958                                 free_internal_mem (tmp);
3959                                 break;
3960                         }
3961                         prev = tmp;
3962                         tmp = tmp->next;
3963                 }
3964         }
3965         UNLOCK_GC;
3966 }
3967
3968 /*
3969  * ######################################################################
3970  * ########  Thread handling (stop/start code)
3971  * ######################################################################
3972  */
3973
3974 /* eventually share with MonoThread? */
3975 typedef struct _SgenThreadInfo SgenThreadInfo;
3976
3977 struct _SgenThreadInfo {
3978         SgenThreadInfo *next;
3979         ARCH_THREAD_TYPE id;
3980         unsigned int stop_count; /* to catch duplicate signals */
3981         int signal;
3982         int skip;
3983         void *stack_end;
3984         void *stack_start;
3985         char **tlab_next_addr;
3986         char **tlab_start_addr;
3987         char **tlab_temp_end_addr;
3988         char **tlab_real_end_addr;
3989         RememberedSet *remset;
3990 };
3991
3992 /* FIXME: handle large/small config */
3993 #define THREAD_HASH_SIZE 11
3994 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
3995
3996 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
3997
3998 #if USE_SIGNAL_BASED_START_STOP_WORLD
3999
4000 static sem_t suspend_ack_semaphore;
4001 static unsigned int global_stop_count = 0;
4002 static int suspend_signal_num = SIGPWR;
4003 static int restart_signal_num = SIGXCPU;
4004 static sigset_t suspend_signal_mask;
4005 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
4006
4007 /* LOCKING: assumes the GC lock is held */
4008 static SgenThreadInfo*
4009 thread_info_lookup (ARCH_THREAD_TYPE id)
4010 {
4011         unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
4012         SgenThreadInfo *info;
4013
4014         info = thread_table [hash];
4015         while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
4016                 info = info->next;
4017         }
4018         return info;
4019 }
4020
4021 static void
4022 update_current_thread_stack (void *start)
4023 {
4024         void *ptr = cur_thread_regs;
4025         SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
4026         info->stack_start = align_pointer (&ptr);
4027         ARCH_STORE_REGS (ptr);
4028 }
4029
4030 static const char*
4031 signal_desc (int signum)
4032 {
4033         if (signum == suspend_signal_num)
4034                 return "suspend";
4035         if (signum == restart_signal_num)
4036                 return "restart";
4037         return "unknown";
4038 }
4039
4040 /* LOCKING: assumes the GC lock is held */
4041 static int
4042 thread_handshake (int signum)
4043 {
4044         int count, i, result;
4045         SgenThreadInfo *info;
4046         pthread_t me = pthread_self ();
4047
4048         count = 0;
4049         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4050                 for (info = thread_table [i]; info; info = info->next) {
4051                         DEBUG (4, fprintf (gc_debug_file, "considering thread %p for signal %d (%s)\n", info, signum, signal_desc (signum)));
4052                         if (ARCH_THREAD_EQUALS (info->id, me)) {
4053                                 DEBUG (4, fprintf (gc_debug_file, "Skip (equal): %p, %p\n", (void*)me, (void*)info->id));
4054                                 continue;
4055                         }
4056                         /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
4057                                 continue;*/
4058                         result = pthread_kill (info->id, signum);
4059                         if (result == 0) {
4060                                 DEBUG (4, fprintf (gc_debug_file, "thread %p signal sent\n", info));
4061                                 count++;
4062                         } else {
4063                                 DEBUG (4, fprintf (gc_debug_file, "thread %p signal failed: %d (%s)\n", (void*)info->id, result, strerror (result)));
4064                                 info->skip = 1;
4065                         }
4066                 }
4067         }
4068
4069         for (i = 0; i < count; ++i) {
4070                 while ((result = sem_wait (&suspend_ack_semaphore)) != 0) {
4071                         if (errno != EINTR) {
4072                                 g_error ("sem_wait ()");
4073                         }
4074                 }
4075         }
4076         return count;
4077 }
4078
4079 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
4080 static void
4081 suspend_handler (int sig)
4082 {
4083         SgenThreadInfo *info;
4084         pthread_t id;
4085         int stop_count;
4086         int old_errno = errno;
4087
4088         id = pthread_self ();
4089         info = thread_info_lookup (id);
4090         stop_count = global_stop_count;
4091         /* duplicate signal */
4092         if (0 && info->stop_count == stop_count) {
4093                 errno = old_errno;
4094                 return;
4095         }
4096         /* update the remset info in the thread data structure */
4097         info->remset = remembered_set;
4098         /* 
4099          * this includes the register values that the kernel put on the stack.
4100          * Write arch-specific code to only push integer regs and a more accurate
4101          * stack pointer.
4102          */
4103         info->stack_start = align_pointer (&id);
4104
4105         /* notify the waiting thread */
4106         sem_post (&suspend_ack_semaphore);
4107         info->stop_count = stop_count;
4108
4109         /* wait until we receive the restart signal */
4110         do {
4111                 info->signal = 0;
4112                 sigsuspend (&suspend_signal_mask);
4113         } while (info->signal != restart_signal_num);
4114
4115         /* notify the waiting thread */
4116         sem_post (&suspend_ack_semaphore);
4117         
4118         errno = old_errno;
4119 }
4120
4121 static void
4122 restart_handler (int sig)
4123 {
4124         SgenThreadInfo *info;
4125         int old_errno = errno;
4126
4127         info = thread_info_lookup (pthread_self ());
4128         info->signal = restart_signal_num;
4129
4130         errno = old_errno;
4131 }
4132
4133 static TV_DECLARE (stop_world_time);
4134 static unsigned long max_pause_usec = 0;
4135
4136 /* LOCKING: assumes the GC lock is held */
4137 static int
4138 stop_world (void)
4139 {
4140         int count;
4141
4142         global_stop_count++;
4143         DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, thread_info_lookup (ARCH_GET_THREAD ()), (gpointer)ARCH_GET_THREAD ()));
4144         TV_GETTIME (stop_world_time);
4145         count = thread_handshake (suspend_signal_num);
4146         DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
4147         return count;
4148 }
4149
4150 /* LOCKING: assumes the GC lock is held */
4151 static int
4152 restart_world (void)
4153 {
4154         int count;
4155         TV_DECLARE (end_sw);
4156         unsigned long usec;
4157
4158         count = thread_handshake (restart_signal_num);
4159         TV_GETTIME (end_sw);
4160         usec = TV_ELAPSED (stop_world_time, end_sw);
4161         max_pause_usec = MAX (usec, max_pause_usec);
4162         DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
4163         return count;
4164 }
4165
4166 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
4167
4168 /*
4169  * Identify objects pinned in a thread stack and its registers.
4170  */
4171 static void
4172 pin_thread_data (void *start_nursery, void *end_nursery)
4173 {
4174         int i;
4175         SgenThreadInfo *info;
4176
4177         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4178                 for (info = thread_table [i]; info; info = info->next) {
4179                         if (info->skip) {
4180                                 DEBUG (2, fprintf (gc_debug_file, "Skipping dead thread %p, range: %p-%p, size: %zd\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
4181                                 continue;
4182                         }
4183                         DEBUG (2, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %zd, pinned=%d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start, next_pin_slot));
4184                         conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery);
4185                 }
4186         }
4187         DEBUG (2, fprintf (gc_debug_file, "Scanning current thread registers, pinned=%d\n", next_pin_slot));
4188         conservatively_pin_objects_from ((void*)cur_thread_regs, (void*)(cur_thread_regs + ARCH_NUM_REGS), start_nursery, end_nursery);
4189 }
4190
4191 static void
4192 find_pinning_ref_from_thread (char *obj, size_t size)
4193 {
4194         int i;
4195         SgenThreadInfo *info;
4196         char *endobj = obj + size;
4197
4198         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4199                 for (info = thread_table [i]; info; info = info->next) {
4200                         char **start = (char**)info->stack_start;
4201                         if (info->skip)
4202                                 continue;
4203                         while (start < (char**)info->stack_end) {
4204                                 if (*start >= obj && *start < endobj) {
4205                                         DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, (gpointer)info->id, start, info->stack_start, info->stack_end));
4206                                 }
4207                                 start++;
4208                         }
4209                 }
4210         }
4211         /* FIXME: check register */
4212 }
4213
4214 /* return TRUE if ptr points inside the managed heap */
4215 static gboolean
4216 ptr_in_heap (void* ptr)
4217 {
4218         mword p = (mword)ptr;
4219         if (p < lowest_heap_address || p >= highest_heap_address)
4220                 return FALSE;
4221         /* FIXME: more checks */
4222         return TRUE;
4223 }
4224
4225 static mword*
4226 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global)
4227 {
4228         void **ptr;
4229         mword count;
4230         mword desc;
4231
4232         /* FIXME: exclude stack locations */
4233         switch ((*p) & REMSET_TYPE_MASK) {
4234         case REMSET_LOCATION:
4235                 ptr = (void**)(*p);
4236                 //__builtin_prefetch (ptr);
4237                 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery) && ptr_in_heap (ptr)) {
4238                         *ptr = copy_object (*ptr, start_nursery, end_nursery);
4239                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
4240                         if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
4241                                 /*
4242                                  * If the object is pinned, each reference to it from nonpinned objects
4243                                  * becomes part of the global remset, which can grow very large.
4244                                  */
4245                                 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
4246                                 add_to_global_remset (ptr, FALSE);
4247                         }
4248                 } else {
4249                         DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
4250                 }
4251                 return p + 1;
4252         case REMSET_RANGE:
4253                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4254                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery) || !ptr_in_heap (ptr))
4255                         return p + 2;
4256                 count = p [1];
4257                 while (count-- > 0) {
4258                         *ptr = copy_object (*ptr, start_nursery, end_nursery);
4259                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
4260                         if (!global && *ptr >= start_nursery && *ptr < end_nursery)
4261                                 add_to_global_remset (ptr, FALSE);
4262                         ++ptr;
4263                 }
4264                 return p + 2;
4265         case REMSET_OBJECT:
4266                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4267                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery) || !ptr_in_heap (ptr))
4268                         return p + 1;
4269                 scan_object (*ptr, start_nursery, end_nursery);
4270                 return p + 1;
4271         case REMSET_OTHER: {
4272                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4273
4274                 switch (p [1]) {
4275                 case REMSET_VTYPE:
4276                         if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery) || !ptr_in_heap (ptr))
4277                                 return p + 3;
4278                         desc = p [2];
4279                         scan_vtype ((char*)ptr, desc, start_nursery, end_nursery);
4280                         return p + 3;
4281                 case REMSET_ROOT_LOCATION:
4282                         /* Same as REMSET_LOCATION, but the address is not required to be in the heap */
4283                         *ptr = copy_object (*ptr, start_nursery, end_nursery);
4284                         DEBUG (9, fprintf (gc_debug_file, "Overwrote root location remset at %p with %p\n", ptr, *ptr));
4285                         if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
4286                                 /*
4287                                  * If the object is pinned, each reference to it from nonpinned objects
4288                                  * becomes part of the global remset, which can grow very large.
4289                                  */
4290                                 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
4291                                 add_to_global_remset (ptr, TRUE);
4292                         }
4293                         return p + 2;
4294                 default:
4295                         g_assert_not_reached ();
4296                 }
4297                 break;
4298         }
4299         default:
4300                 g_assert_not_reached ();
4301         }
4302         return NULL;
4303 }
4304
4305 static void
4306 scan_from_remsets (void *start_nursery, void *end_nursery)
4307 {
4308         int i;
4309         SgenThreadInfo *info;
4310         RememberedSet *remset, *next;
4311         mword *p, *next_p, *store_pos;
4312
4313         /* the global one */
4314         for (remset = global_remset; remset; remset = remset->next) {
4315                 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
4316                 store_pos = remset->data;
4317                 for (p = remset->data; p < remset->store_next; p = next_p) {
4318                         mword ptr;
4319
4320                         next_p = handle_remset (p, start_nursery, end_nursery, TRUE);
4321
4322                         /* 
4323                          * Clear global remsets of locations which no longer point to the 
4324                          * nursery. Otherwise, they could grow indefinitely between major 
4325                          * collections.
4326                          */
4327                         ptr = (p [0] & ~REMSET_TYPE_MASK);
4328                         if ((p [0] & REMSET_TYPE_MASK) == REMSET_LOCATION) {
4329                                 if (ptr_in_nursery (*(void**)ptr))
4330                                         *store_pos ++ = p [0];
4331                         } else {
4332                                 g_assert ((p [0] & REMSET_TYPE_MASK) == REMSET_OTHER);
4333                                 g_assert (p [1] == REMSET_ROOT_LOCATION);
4334                                 if (ptr_in_nursery (*(void**)ptr)) {
4335                                         *store_pos ++ = p [0];
4336                                         *store_pos ++ = p [1];
4337                                 }
4338                         }
4339                 }
4340
4341                 /* Truncate the remset */
4342                 remset->store_next = store_pos;
4343         }
4344
4345         /* the per-thread ones */
4346         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4347                 for (info = thread_table [i]; info; info = info->next) {
4348                         for (remset = info->remset; remset; remset = next) {
4349                                 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %zd\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
4350                                 for (p = remset->data; p < remset->store_next;) {
4351                                         p = handle_remset (p, start_nursery, end_nursery, FALSE);
4352                                 }
4353                                 remset->store_next = remset->data;
4354                                 next = remset->next;
4355                                 remset->next = NULL;
4356                                 if (remset != info->remset) {
4357                                         DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
4358                                         free_internal_mem (remset);
4359                                 }
4360                         }
4361                 }
4362         }
4363 }
4364
4365 /*
4366  * Clear the info in the remembered sets: we're doing a major collection, so
4367  * the per-thread ones are not needed and the global ones will be reconstructed
4368  * during the copy.
4369  */
4370 static void
4371 clear_remsets (void)
4372 {
4373         int i;
4374         SgenThreadInfo *info;
4375         RememberedSet *remset, *next;
4376
4377         /* the global list */
4378         for (remset = global_remset; remset; remset = next) {
4379                 remset->store_next = remset->data;
4380                 next = remset->next;
4381                 remset->next = NULL;
4382                 if (remset != global_remset) {
4383                         DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
4384                         free_internal_mem (remset);
4385                 }
4386         }
4387         /* the per-thread ones */
4388         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4389                 for (info = thread_table [i]; info; info = info->next) {
4390                         for (remset = info->remset; remset; remset = next) {
4391                                 remset->store_next = remset->data;
4392                                 next = remset->next;
4393                                 remset->next = NULL;
4394                                 if (remset != info->remset) {
4395                                         DEBUG (1, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
4396                                         free_internal_mem (remset);
4397                                 }
4398                         }
4399                 }
4400         }
4401 }
4402
4403 /*
4404  * Clear the thread local TLAB variables for all threads.
4405  */
4406 static void
4407 clear_tlabs (void)
4408 {
4409         SgenThreadInfo *info;
4410         int i;
4411
4412         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4413                 for (info = thread_table [i]; info; info = info->next) {
4414                         /* A new TLAB will be allocated when the thread does its first allocation */
4415                         *info->tlab_start_addr = NULL;
4416                         *info->tlab_next_addr = NULL;
4417                         *info->tlab_temp_end_addr = NULL;
4418                         *info->tlab_real_end_addr = NULL;
4419                 }
4420         }
4421 }
4422
4423 /*
4424  * Find the tlab_next value of the TLAB which contains ADDR.
4425  */
4426 static char*
4427 find_tlab_next_from_address (char *addr)
4428 {
4429         SgenThreadInfo *info;
4430         int i;
4431
4432         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4433                 for (info = thread_table [i]; info; info = info->next) {
4434                         if (addr >= *info->tlab_start_addr && addr < *info->tlab_next_addr)
4435                                 return *info->tlab_next_addr;
4436                 }
4437         }
4438
4439         return NULL;
4440 }
4441
4442 /* LOCKING: assumes the GC lock is held */
4443 static SgenThreadInfo*
4444 gc_register_current_thread (void *addr)
4445 {
4446         int hash;
4447         SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
4448         if (!info)
4449                 return NULL;
4450         info->id = ARCH_GET_THREAD ();
4451         info->stop_count = -1;
4452         info->skip = 0;
4453         info->signal = 0;
4454         info->stack_start = NULL;
4455         info->tlab_start_addr = &tlab_start;
4456         info->tlab_next_addr = &tlab_next;
4457         info->tlab_temp_end_addr = &tlab_temp_end;
4458         info->tlab_real_end_addr = &tlab_real_end;
4459
4460         tlab_next_addr = &tlab_next;
4461
4462         /* try to get it with attributes first */
4463 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
4464         {
4465                 size_t size;
4466                 void *sstart;
4467                 pthread_attr_t attr;
4468                 pthread_getattr_np (pthread_self (), &attr);
4469                 pthread_attr_getstack (&attr, &sstart, &size);
4470                 info->stack_end = (char*)sstart + size;
4471                 pthread_attr_destroy (&attr);
4472         }
4473 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
4474                  info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
4475 #else
4476         {
4477                 /* FIXME: we assume the stack grows down */
4478                 gsize stack_bottom = (gsize)addr;
4479                 stack_bottom += 4095;
4480                 stack_bottom &= ~4095;
4481                 info->stack_end = (char*)stack_bottom;
4482         }
4483 #endif
4484
4485         /* hash into the table */
4486         hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
4487         info->next = thread_table [hash];
4488         thread_table [hash] = info;
4489
4490         remembered_set = info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
4491         pthread_setspecific (remembered_set_key, remembered_set);
4492         DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
4493         return info;
4494 }
4495
4496 static void
4497 unregister_current_thread (void)
4498 {
4499         int hash;
4500         SgenThreadInfo *prev = NULL;
4501         SgenThreadInfo *p;
4502         RememberedSet *rset;
4503         ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
4504
4505         hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
4506         p = thread_table [hash];
4507         assert (p);
4508         DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
4509         while (!ARCH_THREAD_EQUALS (p->id, id)) {
4510                 prev = p;
4511                 p = p->next;
4512         }
4513         if (prev == NULL) {
4514                 thread_table [hash] = p->next;
4515         } else {
4516                 prev->next = p->next;
4517         }
4518         rset = p->remset;
4519         /* FIXME: transfer remsets if any */
4520         while (rset) {
4521                 RememberedSet *next = rset->next;
4522                 free_internal_mem (rset);
4523                 rset = next;
4524         }
4525         free (p);
4526 }
4527
4528 static void
4529 unregister_thread (void *k)
4530 {
4531         LOCK_GC;
4532         unregister_current_thread ();
4533         UNLOCK_GC;
4534 }
4535
4536 gboolean
4537 mono_gc_register_thread (void *baseptr)
4538 {
4539         SgenThreadInfo *info;
4540         LOCK_GC;
4541         info = thread_info_lookup (ARCH_GET_THREAD ());
4542         if (info == NULL)
4543                 info = gc_register_current_thread (baseptr);
4544         UNLOCK_GC;
4545         return info != NULL;
4546 }
4547
4548 #if USE_PTHREAD_INTERCEPT
4549
4550 #undef pthread_create
4551 #undef pthread_join
4552 #undef pthread_detach
4553
4554 typedef struct {
4555         void *(*start_routine) (void *);
4556         void *arg;
4557         int flags;
4558         sem_t registered;
4559 } SgenThreadStartInfo;
4560
4561 static void*
4562 gc_start_thread (void *arg)
4563 {
4564         SgenThreadStartInfo *start_info = arg;
4565         SgenThreadInfo* info;
4566         void *t_arg = start_info->arg;
4567         void *(*start_func) (void*) = start_info->start_routine;
4568         void *result;
4569
4570         LOCK_GC;
4571         info = gc_register_current_thread (&result);
4572         UNLOCK_GC;
4573         sem_post (&(start_info->registered));
4574         result = start_func (t_arg);
4575         /*
4576          * this is done by the pthread key dtor
4577         LOCK_GC;
4578         unregister_current_thread ();
4579         UNLOCK_GC;
4580         */
4581
4582         return result;
4583 }
4584
4585 int
4586 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
4587 {
4588         SgenThreadStartInfo *start_info;
4589         int result;
4590
4591         start_info = malloc (sizeof (SgenThreadStartInfo));
4592         if (!start_info)
4593                 return ENOMEM;
4594         sem_init (&(start_info->registered), 0, 0);
4595         start_info->arg = arg;
4596         start_info->start_routine = start_routine;
4597
4598         result = pthread_create (new_thread, attr, gc_start_thread, start_info);
4599         if (result == 0) {
4600                 while (sem_wait (&(start_info->registered)) != 0) {
4601                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
4602                 }
4603         }
4604         sem_destroy (&(start_info->registered));
4605         free (start_info);
4606         return result;
4607 }
4608
4609 int
4610 mono_gc_pthread_join (pthread_t thread, void **retval)
4611 {
4612         return pthread_join (thread, retval);
4613 }
4614
4615 int
4616 mono_gc_pthread_detach (pthread_t thread)
4617 {
4618         return pthread_detach (thread);
4619 }
4620
4621 #endif /* USE_PTHREAD_INTERCEPT */
4622
4623 /*
4624  * ######################################################################
4625  * ########  Write barriers
4626  * ######################################################################
4627  */
4628
4629 static RememberedSet*
4630 alloc_remset (int size, gpointer id) {
4631         RememberedSet* res = get_internal_mem (sizeof (RememberedSet) + (size * sizeof (gpointer)));
4632         res->store_next = res->data;
4633         res->end_set = res->data + size;
4634         res->next = NULL;
4635         DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
4636         return res;
4637 }
4638
4639 /*
4640  * Note: the write barriers first do the needed GC work and then do the actual store:
4641  * this way the value is visible to the conservative GC scan after the write barrier
4642  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
4643  * the conservative scan, otherwise by the remembered set scan. FIXME: figure out what
4644  * happens when we need to record which pointers contain references to the new generation.
4645  * The write barrier will be executed, but the pointer is still not stored.
4646  */
4647 void
4648 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
4649 {
4650         RememberedSet *rs;
4651         if (ptr_in_nursery (field_ptr)) {
4652                 *(void**)field_ptr = value;
4653                 return;
4654         }
4655         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
4656         rs = remembered_set;
4657         if (rs->store_next < rs->end_set) {
4658                 *(rs->store_next++) = (mword)field_ptr;
4659                 *(void**)field_ptr = value;
4660                 return;
4661         }
4662         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4663         rs->next = remembered_set;
4664         remembered_set = rs;
4665         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4666         *(rs->store_next++) = (mword)field_ptr;
4667         *(void**)field_ptr = value;
4668 }
4669
4670 void
4671 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
4672 {
4673         RememberedSet *rs = remembered_set;
4674         if (ptr_in_nursery (slot_ptr)) {
4675                 *(void**)slot_ptr = value;
4676                 return;
4677         }
4678         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
4679         if (rs->store_next < rs->end_set) {
4680                 *(rs->store_next++) = (mword)slot_ptr;
4681                 *(void**)slot_ptr = value;
4682                 return;
4683         }
4684         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4685         rs->next = remembered_set;
4686         remembered_set = rs;
4687         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4688         *(rs->store_next++) = (mword)slot_ptr;
4689         *(void**)slot_ptr = value;
4690 }
4691
4692 void
4693 mono_gc_wbarrier_arrayref_copy (MonoArray *arr, gpointer slot_ptr, int count)
4694 {
4695         RememberedSet *rs = remembered_set;
4696         if (ptr_in_nursery (slot_ptr))
4697                 return;
4698         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", slot_ptr, count));
4699         if (rs->store_next + 1 < rs->end_set) {
4700                 *(rs->store_next++) = (mword)slot_ptr | REMSET_RANGE;
4701                 *(rs->store_next++) = count;
4702                 return;
4703         }
4704         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4705         rs->next = remembered_set;
4706         remembered_set = rs;
4707         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4708         *(rs->store_next++) = (mword)slot_ptr | REMSET_RANGE;
4709         *(rs->store_next++) = count;
4710 }
4711
4712 void
4713 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
4714 {
4715         RememberedSet *rs;
4716         if (ptr_in_nursery (ptr)) {
4717                 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
4718                 *(void**)ptr = value;
4719                 return;
4720         }
4721         rs = remembered_set;
4722         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p (%s)\n", ptr, value ? safe_name (value) : "null"));
4723         /* FIXME: ensure it is on the heap */
4724         if (rs->store_next < rs->end_set) {
4725                 *(rs->store_next++) = (mword)ptr;
4726                 *(void**)ptr = value;
4727                 return;
4728         }
4729         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4730         rs->next = remembered_set;
4731         remembered_set = rs;
4732         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4733         *(rs->store_next++) = (mword)ptr;
4734         *(void**)ptr = value;
4735 }
4736
4737 void
4738 mono_gc_wbarrier_set_root (gpointer ptr, MonoObject *value)
4739 {
4740         RememberedSet *rs = remembered_set;
4741         if (ptr_in_nursery (ptr))
4742                 return;
4743         DEBUG (8, fprintf (gc_debug_file, "Adding root remset at %p (%s)\n", ptr, value ? safe_name (value) : "null"));
4744
4745         if (rs->store_next + 2 < rs->end_set) {
4746                 *(rs->store_next++) = (mword)ptr | REMSET_OTHER;
4747                 *(rs->store_next++) = (mword)REMSET_ROOT_LOCATION;
4748                 *(void**)ptr = value;
4749                 return;
4750         }
4751         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4752         rs->next = remembered_set;
4753         remembered_set = rs;
4754         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4755         *(rs->store_next++) = (mword)ptr | REMSET_OTHER;
4756         *(rs->store_next++) = (mword)REMSET_ROOT_LOCATION;
4757
4758         *(void**)ptr = value;
4759 }
4760
4761 void
4762 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
4763 {
4764         RememberedSet *rs = remembered_set;
4765         if (ptr_in_nursery (dest))
4766                 return;
4767         DEBUG (8, fprintf (gc_debug_file, "Adding value remset at %p, count %d for class %s\n", dest, count, klass->name));
4768
4769         if (rs->store_next + 2 < rs->end_set) {
4770                 *(rs->store_next++) = (mword)dest | REMSET_OTHER;
4771                 *(rs->store_next++) = (mword)REMSET_VTYPE;
4772                 *(rs->store_next++) = (mword)klass->gc_descr;
4773                 return;
4774         }
4775         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4776         rs->next = remembered_set;
4777         remembered_set = rs;
4778         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4779         *(rs->store_next++) = (mword)dest | REMSET_OTHER;
4780         *(rs->store_next++) = (mword)REMSET_VTYPE;
4781         *(rs->store_next++) = (mword)klass->gc_descr;
4782 }
4783
4784 /**
4785  * mono_gc_wbarrier_object:
4786  *
4787  * Write barrier to call when obj is the result of a clone or copy of an object.
4788  */
4789 void
4790 mono_gc_wbarrier_object (MonoObject* obj)
4791 {
4792         RememberedSet *rs = remembered_set;
4793         DEBUG (1, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
4794         if (rs->store_next < rs->end_set) {
4795                 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
4796                 return;
4797         }
4798         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4799         rs->next = remembered_set;
4800         remembered_set = rs;
4801         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4802         *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
4803 }
4804
4805 /*
4806  * ######################################################################
4807  * ########  Collector debugging
4808  * ######################################################################
4809  */
4810
4811 const char*descriptor_types [] = {
4812         "run_length",
4813         "small_bitmap",
4814         "string",
4815         "complex",
4816         "vector",
4817         "array",
4818         "large_bitmap",
4819         "complex_arr"
4820 };
4821
4822 void
4823 describe_ptr (char *ptr)
4824 {
4825         GCMemSection *section;
4826         MonoVTable *vtable;
4827         mword desc;
4828         int type;
4829
4830         if (ptr_in_nursery (ptr)) {
4831                 printf ("Pointer inside nursery.\n");
4832         } else {
4833                 for (section = section_list; section;) {
4834                         if (ptr >= section->data && ptr < section->data + section->size)
4835                                 break;
4836                         section = section->next;
4837                 }
4838
4839                 if (section) {
4840                         printf ("Pointer inside oldspace.\n");
4841                 } else if (obj_is_from_pinned_alloc (ptr)) {
4842                         printf ("Pointer is inside a pinned chunk.\n");
4843                 } else {
4844                         printf ("Pointer unknown.\n");
4845                         return;
4846                 }
4847         }
4848
4849         if (object_is_pinned (ptr))
4850                 printf ("Object is pinned.\n");
4851
4852         if (object_is_forwarded (ptr))
4853                 printf ("Object is forwared.\n");
4854
4855         // FIXME: Handle pointers to the inside of objects
4856         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
4857
4858         printf ("VTable: %p\n", vtable);
4859         if (vtable == NULL) {
4860                 printf ("VTable is invalid (empty).\n");
4861                 return;
4862         }
4863         if (ptr_in_nursery (vtable)) {
4864                 printf ("VTable is invalid (points inside nursery).\n");
4865                 return;
4866         }
4867         printf ("Class: %s\n", vtable->klass->name);
4868
4869         desc = ((GCVTable*)vtable)->desc;
4870         printf ("Descriptor: %lx\n", desc);
4871
4872         type = desc & 0x7;
4873         printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
4874 }
4875
4876 static mword*
4877 find_in_remset_loc (mword *p, char *addr, gboolean *found)
4878 {
4879         void **ptr;
4880         mword count, desc;
4881         size_t skip_size;
4882
4883         switch ((*p) & REMSET_TYPE_MASK) {
4884         case REMSET_LOCATION:
4885                 if (*p == (mword)addr)
4886                         *found = TRUE;
4887                 return p + 1;
4888         case REMSET_RANGE:
4889                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4890                 count = p [1];
4891                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
4892                         *found = TRUE;
4893                 return p + 2;
4894         case REMSET_OBJECT:
4895                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4896                 count = safe_object_get_size ((MonoObject*)ptr); 
4897                 count += (ALLOC_ALIGN - 1);
4898                 count &= (ALLOC_ALIGN - 1);
4899                 count /= sizeof (mword);
4900                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
4901                         *found = TRUE;
4902                 return p + 1;
4903         case REMSET_OTHER: {
4904                 switch (p [1]) {
4905                 case REMSET_VTYPE:
4906                         ptr = (void**)(*p & ~REMSET_TYPE_MASK);
4907                         desc = p [2];
4908
4909                         switch (desc & 0x7) {
4910                         case DESC_TYPE_RUN_LENGTH:
4911                                 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
4912                                 /* The descriptor includes the size of MonoObject */
4913                                 skip_size -= sizeof (MonoObject);
4914                                 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
4915                                         *found = TRUE;
4916                                 break;
4917                         default:
4918                                 // FIXME:
4919                                 g_assert_not_reached ();
4920                         }
4921
4922                         return p + 3;
4923                 case REMSET_ROOT_LOCATION:
4924                         return p + 2;
4925                 default:
4926                         g_assert_not_reached ();
4927                 }
4928                 break;
4929         }
4930         default:
4931                 g_assert_not_reached ();
4932         }
4933         return NULL;
4934 }
4935
4936 /*
4937  * Return whenever ADDR occurs in the remembered sets
4938  */
4939 static gboolean
4940 find_in_remsets (char *addr)
4941 {
4942         int i;
4943         SgenThreadInfo *info;
4944         RememberedSet *remset;
4945         mword *p;
4946         gboolean found = FALSE;
4947
4948         /* the global one */
4949         for (remset = global_remset; remset; remset = remset->next) {
4950                 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
4951                 for (p = remset->data; p < remset->store_next;) {
4952                         p = find_in_remset_loc (p, addr, &found);
4953                         if (found)
4954                                 return TRUE;
4955                 }
4956         }
4957         /* the per-thread ones */
4958         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
4959                 for (info = thread_table [i]; info; info = info->next) {
4960                         for (remset = info->remset; remset; remset = remset->next) {
4961                                 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %zd\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
4962                                 for (p = remset->data; p < remset->store_next;) {
4963                                         p = find_in_remset_loc (p, addr, &found);
4964                                         if (found)
4965                                                 return TRUE;
4966                                 }
4967                         }
4968                 }
4969         }
4970
4971         return FALSE;
4972 }
4973
4974 #undef HANDLE_PTR
4975 #define HANDLE_PTR(ptr,obj)     do {    \
4976                 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
4977             if (!find_in_remsets ((char*)(ptr))) { \
4978                 fprintf (gc_debug_file, "Oldspace->newspace reference %p at offset %zd in object %p (%s.%s) not found in remsets.\n", *(ptr), (char*)(ptr) - (char*)(obj), (obj), ((MonoObject*)(obj))->vtable->klass->name_space, ((MonoObject*)(obj))->vtable->klass->name); \
4979                 g_assert_not_reached (); \
4980             } \
4981         } \
4982         } while (0)
4983
4984 /*
4985  * Check that each object reference inside the area which points into the nursery
4986  * can be found in the remembered sets.
4987  */
4988 static void __attribute__((noinline))
4989 check_remsets_for_area (char *start, char *end)
4990 {
4991         GCVTable *vt;
4992         size_t skip_size;
4993         int type;
4994         int type_str = 0, type_rlen = 0, type_bitmap = 0, type_vector = 0, type_lbit = 0, type_complex = 0;
4995         mword desc;
4996         new_obj_references = 0;
4997         obj_references_checked = 0;
4998         while (start < end) {
4999                 if (!*(void**)start) {
5000                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
5001                         continue;
5002                 }
5003                 vt = (GCVTable*)LOAD_VTABLE (start);
5004                 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
5005                 if (0) {
5006                         MonoObject *obj = (MonoObject*)start;
5007                         g_print ("found at %p (0x%lx): %s.%s\n", start, (long)vt->desc, obj->vtable->klass->name_space, obj->vtable->klass->name);
5008                 }
5009                 desc = vt->desc;
5010                 type = desc & 0x7;
5011                 if (type == DESC_TYPE_STRING) {
5012                         STRING_SIZE (skip_size, start);
5013                         start += skip_size;
5014                         type_str++;
5015                         continue;
5016                 } else if (type == DESC_TYPE_RUN_LENGTH) {
5017                         OBJ_RUN_LEN_SIZE (skip_size, desc, start);
5018                         g_assert (skip_size);
5019                         OBJ_RUN_LEN_FOREACH_PTR (desc,start);
5020                         start += skip_size;
5021                         type_rlen++;
5022                         continue;
5023                 } else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
5024                         skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
5025                         skip_size *= mono_array_length ((MonoArray*)start);
5026                         skip_size += sizeof (MonoArray);
5027                         skip_size += (ALLOC_ALIGN - 1);
5028                         skip_size &= ~(ALLOC_ALIGN - 1);
5029                         OBJ_VECTOR_FOREACH_PTR (vt, start);
5030                         if (((MonoArray*)start)->bounds) {
5031                                 /* account for the bounds */
5032                                 skip_size += sizeof (MonoArrayBounds) * vt->klass->rank;
5033                         }
5034                         start += skip_size;
5035                         type_vector++;
5036                         continue;
5037                 } else if (type == DESC_TYPE_SMALL_BITMAP) {
5038                         OBJ_BITMAP_SIZE (skip_size, desc, start);
5039                         g_assert (skip_size);
5040                         OBJ_BITMAP_FOREACH_PTR (desc,start);
5041                         start += skip_size;
5042                         type_bitmap++;
5043                         continue;
5044                 } else if (type == DESC_TYPE_LARGE_BITMAP) {
5045                         skip_size = safe_object_get_size ((MonoObject*)start);
5046                         skip_size += (ALLOC_ALIGN - 1);
5047                         skip_size &= ~(ALLOC_ALIGN - 1);
5048                         OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
5049                         start += skip_size;
5050                         type_lbit++;
5051                         continue;
5052                 } else if (type == DESC_TYPE_COMPLEX) {
5053                         /* this is a complex object */
5054                         skip_size = safe_object_get_size ((MonoObject*)start);
5055                         skip_size += (ALLOC_ALIGN - 1);
5056                         skip_size &= ~(ALLOC_ALIGN - 1);
5057                         OBJ_COMPLEX_FOREACH_PTR (vt, start);
5058                         start += skip_size;
5059                         type_complex++;
5060                         continue;
5061                 } else if (type == DESC_TYPE_COMPLEX_ARR) {
5062                         /* this is an array of complex structs */
5063                         skip_size = mono_array_element_size (((MonoVTable*)vt)->klass);
5064                         skip_size *= mono_array_length ((MonoArray*)start);
5065                         skip_size += sizeof (MonoArray);
5066                         skip_size += (ALLOC_ALIGN - 1);
5067                         skip_size &= ~(ALLOC_ALIGN - 1);
5068                         OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
5069                         if (((MonoArray*)start)->bounds) {
5070                                 /* account for the bounds */
5071                                 skip_size += sizeof (MonoArrayBounds) * vt->klass->rank;
5072                         }
5073                         start += skip_size;
5074                         type_complex++;
5075                         continue;
5076                 } else {
5077                         g_assert (0);
5078                 }
5079         }
5080 }
5081
5082 /*
5083  * Perform consistency check of the heap.
5084  *
5085  * Assumes the world is stopped.
5086  */
5087 void
5088 check_consistency (void)
5089 {
5090         GCMemSection *section;
5091
5092         // Need to add more checks
5093         // FIXME: Create a general heap enumeration function and use that
5094
5095         DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
5096
5097         // Check that oldspace->newspace pointers are registered with the collector
5098         for (section = section_list; section; section = section->next) {
5099                 if (section->role == MEMORY_ROLE_GEN0)
5100                         continue;
5101                 DEBUG (2, fprintf (gc_debug_file, "Scan of old section: %p-%p, size: %d\n", section->data, section->next_data, (int)(section->next_data - section->data)));
5102                 check_remsets_for_area (section->data, section->next_data);
5103         }
5104
5105         DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
5106 }
5107
5108 /* Check that the reference is valid */
5109 #undef HANDLE_PTR
5110 #define HANDLE_PTR(ptr,obj)     do {    \
5111                 if (*(ptr)) {   \
5112                         g_assert (safe_name (*(ptr)) != NULL);  \
5113                 }       \
5114         } while (0)
5115
5116 /*
5117  * check_object:
5118  *
5119  *   Perform consistency check on an object. Currently we only check that the
5120  * reference fields are valid.
5121  */
5122 char*
5123 check_object (char *start)
5124 {
5125         GCVTable *vt;
5126         size_t skip_size;
5127         mword desc;
5128
5129         if (!start)
5130                 return NULL;
5131
5132         vt = (GCVTable*)LOAD_VTABLE (start);
5133         //type = vt->desc & 0x7;
5134
5135         desc = vt->desc;
5136         switch (desc & 0x7) {
5137         case DESC_TYPE_STRING:
5138                 STRING_SIZE (skip_size, start);
5139                 return start + skip_size;
5140         case DESC_TYPE_RUN_LENGTH:
5141                 OBJ_RUN_LEN_FOREACH_PTR (desc,start);
5142                 OBJ_RUN_LEN_SIZE (skip_size, desc, start);
5143                 g_assert (skip_size);
5144                 return start + skip_size;
5145         case DESC_TYPE_ARRAY:
5146         case DESC_TYPE_VECTOR:
5147                 OBJ_VECTOR_FOREACH_PTR (vt, start);
5148                 skip_size = safe_object_get_size ((MonoObject*)start);
5149                 skip_size += (ALLOC_ALIGN - 1);
5150                 skip_size &= ~(ALLOC_ALIGN - 1);
5151                 return start + skip_size;
5152         case DESC_TYPE_SMALL_BITMAP:
5153                 OBJ_BITMAP_FOREACH_PTR (desc,start);
5154                 OBJ_BITMAP_SIZE (skip_size, desc, start);
5155                 return start + skip_size;
5156         case DESC_TYPE_LARGE_BITMAP:
5157                 OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
5158                 skip_size = safe_object_get_size ((MonoObject*)start);
5159                 skip_size += (ALLOC_ALIGN - 1);
5160                 skip_size &= ~(ALLOC_ALIGN - 1);
5161                 return start + skip_size;
5162         case DESC_TYPE_COMPLEX:
5163                 OBJ_COMPLEX_FOREACH_PTR (vt, start);
5164                 /* this is a complex object */
5165                 skip_size = safe_object_get_size ((MonoObject*)start);
5166                 skip_size += (ALLOC_ALIGN - 1);
5167                 skip_size &= ~(ALLOC_ALIGN - 1);
5168                 return start + skip_size;
5169         case DESC_TYPE_COMPLEX_ARR:
5170                 OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
5171                 /* this is an array of complex structs */
5172                 skip_size = safe_object_get_size ((MonoObject*)start);
5173                 skip_size += (ALLOC_ALIGN - 1);
5174                 skip_size &= ~(ALLOC_ALIGN - 1);
5175                 return start + skip_size;
5176         }
5177         g_assert_not_reached ();
5178         return NULL;
5179 }
5180
5181 /*
5182  * ######################################################################
5183  * ########  Other mono public interface functions.
5184  * ######################################################################
5185  */
5186
5187 void
5188 mono_gc_collect (int generation)
5189 {
5190         LOCK_GC;
5191         update_current_thread_stack (&generation);
5192         stop_world ();
5193         if (generation == 0) {
5194                 collect_nursery (0);
5195         } else {
5196                 major_collection ();
5197         }
5198         restart_world ();
5199         UNLOCK_GC;
5200 }
5201
5202 int
5203 mono_gc_max_generation (void)
5204 {
5205         return 1;
5206 }
5207
5208 int
5209 mono_gc_collection_count (int generation)
5210 {
5211         if (generation == 0)
5212                 return num_minor_gcs;
5213         return num_major_gcs;
5214 }
5215
5216 gint64
5217 mono_gc_get_used_size (void)
5218 {
5219         gint64 tot = 0;
5220         GCMemSection *section;
5221         LOCK_GC;
5222         tot = los_memory_usage;
5223         for (section = section_list; section; section = section->next) {
5224                 /* this is approximate... */
5225                 tot += section->next_data - section->data;
5226         }
5227         /* FIXME: account for pinned objects */
5228         UNLOCK_GC;
5229         return tot;
5230 }
5231
5232 gint64
5233 mono_gc_get_heap_size (void)
5234 {
5235         return total_alloc;
5236 }
5237
5238 void
5239 mono_gc_disable (void)
5240 {
5241         LOCK_GC;
5242         gc_disabled++;
5243         UNLOCK_GC;
5244 }
5245
5246 void
5247 mono_gc_enable (void)
5248 {
5249         LOCK_GC;
5250         gc_disabled--;
5251         UNLOCK_GC;
5252 }
5253
5254 gboolean
5255 mono_object_is_alive (MonoObject* o)
5256 {
5257         return TRUE;
5258 }
5259
5260 int
5261 mono_gc_get_generation (MonoObject *obj)
5262 {
5263         if (ptr_in_nursery (obj))
5264                 return 0;
5265         return 1;
5266 }
5267
5268 void
5269 mono_gc_enable_events (void)
5270 {
5271 }
5272
5273 void
5274 mono_gc_weak_link_add (void **link_addr, MonoObject *obj)
5275 {
5276         mono_gc_register_disappearing_link (obj, link_addr);
5277         *link_addr = obj;
5278 }
5279
5280 void
5281 mono_gc_weak_link_remove (void **link_addr)
5282 {
5283         mono_gc_register_disappearing_link (NULL, link_addr);
5284         *link_addr = NULL;
5285 }
5286
5287 MonoObject*
5288 mono_gc_weak_link_get (void **link_addr)
5289 {
5290         return *link_addr;
5291 }
5292
5293 void*
5294 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
5295 {
5296         if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
5297                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
5298         } else {
5299                 mword complex = alloc_complex_descriptor (bitmap, numbits + 1);
5300                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
5301         }
5302 }
5303
5304 void*
5305 mono_gc_make_root_descr_user (MonoGCMarkFunc marker)
5306 {
5307         void *descr;
5308
5309         g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
5310         descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
5311         user_descriptors [user_descriptors_next ++] = marker;
5312
5313         return descr;
5314 }
5315
5316 void*
5317 mono_gc_alloc_fixed (size_t size, void *descr)
5318 {
5319         /* FIXME: do a single allocation */
5320         void *res = calloc (1, size);
5321         if (!res)
5322                 return NULL;
5323         if (!mono_gc_register_root (res, size, descr)) {
5324                 free (res);
5325                 res = NULL;
5326         }
5327         return res;
5328 }
5329
5330 void
5331 mono_gc_free_fixed (void* addr)
5332 {
5333         mono_gc_deregister_root (addr);
5334         free (addr);
5335 }
5336
5337 gboolean
5338 mono_gc_is_gc_thread (void)
5339 {
5340         gboolean result;
5341         LOCK_GC;
5342         result = thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
5343         UNLOCK_GC;
5344         return result;
5345 }
5346
5347 void
5348 mono_gc_base_init (void)
5349 {
5350         char *env;
5351         char **opts, **ptr;
5352         struct sigaction sinfo;
5353
5354         LOCK_INIT (gc_mutex);
5355         LOCK_GC;
5356         if (gc_initialized) {
5357                 UNLOCK_GC;
5358                 return;
5359         }
5360         pagesize = mono_pagesize ();
5361         gc_debug_file = stderr;
5362         if ((env = getenv ("MONO_GC_DEBUG"))) {
5363                 opts = g_strsplit (env, ",", -1);
5364                 for (ptr = opts; ptr && *ptr; ptr ++) {
5365                         char *opt = *ptr;
5366                         if (opt [0] >= '0' && opt [0] <= '9') {
5367                                 gc_debug_level = atoi (opt);
5368                                 opt++;
5369                                 if (opt [0] == ':')
5370                                         opt++;
5371                                 if (opt [0]) {
5372                                         char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
5373                                         gc_debug_file = fopen (rf, "wb");
5374                                         if (!gc_debug_file)
5375                                                 gc_debug_file = stderr;
5376                                         g_free (rf);
5377                                 }
5378                         } else if (!strcmp (opt, "collect-before-allocs")) {
5379                                 collect_before_allocs = TRUE;
5380                         } else if (!strcmp (opt, "check-at-minor-collections")) {
5381                                 consistency_check_at_minor_collection = TRUE;
5382                         } else {
5383                                 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
5384                                 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
5385                                 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections.\n");
5386                                 exit (1);
5387                         }
5388                 }
5389                 g_strfreev (opts);
5390         }
5391
5392         sem_init (&suspend_ack_semaphore, 0, 0);
5393
5394         sigfillset (&sinfo.sa_mask);
5395         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
5396         sinfo.sa_handler = suspend_handler;
5397         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
5398                 g_error ("failed sigaction");
5399         }
5400
5401         sinfo.sa_handler = restart_handler;
5402         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
5403                 g_error ("failed sigaction");
5404         }
5405
5406         sigfillset (&suspend_signal_mask);
5407         sigdelset (&suspend_signal_mask, restart_signal_num);
5408
5409         global_remset = alloc_remset (1024, NULL);
5410         global_remset->next = NULL;
5411
5412         pthread_key_create (&remembered_set_key, unregister_thread);
5413         gc_initialized = TRUE;
5414         UNLOCK_GC;
5415         mono_gc_register_thread (&sinfo);
5416 }
5417
5418 enum {
5419         ATYPE_NORMAL,
5420         ATYPE_NUM
5421 };
5422
5423 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
5424  * for each class. This is currently not easy to do, as it is hard to generate basic 
5425  * blocks + branches, but it is easy with the linear IL codebase.
5426  */
5427 static MonoMethod*
5428 create_allocator (int atype)
5429 {
5430         int tlab_next_addr_offset = -1;
5431         int tlab_temp_end_offset = -1;
5432         int p_var, size_var, tlab_next_addr_var, new_next_var;
5433         guint32 slowpath_branch;
5434         MonoMethodBuilder *mb;
5435         MonoMethod *res;
5436         MonoMethodSignature *csig;
5437         static gboolean registered = FALSE;
5438
5439         MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
5440         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
5441
5442         g_assert (tlab_next_addr_offset != -1);
5443         g_assert (tlab_temp_end_offset != -1);
5444
5445         g_assert (atype == ATYPE_NORMAL);
5446
5447         if (!registered) {
5448                 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
5449                 registered = TRUE;
5450         }
5451
5452         csig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
5453         csig->ret = &mono_defaults.object_class->byval_arg;
5454         csig->params [0] = &mono_defaults.int_class->byval_arg;
5455
5456         mb = mono_mb_new (mono_defaults.object_class, "Alloc", MONO_WRAPPER_ALLOC);
5457         size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
5458         /* size = vtable->klass->instance_size; */
5459         mono_mb_emit_ldarg (mb, 0);
5460         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
5461         mono_mb_emit_byte (mb, CEE_ADD);
5462         mono_mb_emit_byte (mb, CEE_LDIND_I);
5463         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
5464         mono_mb_emit_byte (mb, CEE_ADD);
5465         /* FIXME: assert instance_size stays a 4 byte integer */
5466         mono_mb_emit_byte (mb, CEE_LDIND_U4);
5467         mono_mb_emit_stloc (mb, size_var);
5468
5469         /* size += ALLOC_ALIGN - 1; */
5470         mono_mb_emit_ldloc (mb, size_var);
5471         mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
5472         mono_mb_emit_byte (mb, CEE_ADD);
5473         /* size &= ~(ALLOC_ALIGN - 1); */
5474         mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
5475         mono_mb_emit_byte (mb, CEE_AND);
5476         mono_mb_emit_stloc (mb, size_var);
5477
5478         /*
5479          * We need to modify tlab_next, but the JIT only supports reading, so we read
5480          * another tls var holding its address instead.
5481          */
5482
5483         /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
5484         tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5485         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
5486         mono_mb_emit_byte (mb, CEE_MONO_TLS);
5487         mono_mb_emit_i4 (mb, tlab_next_addr_offset);
5488         mono_mb_emit_stloc (mb, tlab_next_addr_var);
5489
5490         /* p = (void**)tlab_next; */
5491         p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5492         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
5493         mono_mb_emit_byte (mb, CEE_LDIND_I);
5494         mono_mb_emit_stloc (mb, p_var);
5495         
5496         /* new_next = (char*)p + size; */
5497         new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5498         mono_mb_emit_ldloc (mb, p_var);
5499         mono_mb_emit_ldloc (mb, size_var);
5500         mono_mb_emit_byte (mb, CEE_CONV_I);
5501         mono_mb_emit_byte (mb, CEE_ADD);
5502         mono_mb_emit_stloc (mb, new_next_var);
5503
5504         /* tlab_next = new_next */
5505         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
5506         mono_mb_emit_ldloc (mb, new_next_var);
5507         mono_mb_emit_byte (mb, CEE_STIND_I);
5508
5509         /* if (G_LIKELY (new_next < tlab_temp_end)) */
5510         mono_mb_emit_ldloc (mb, new_next_var);
5511         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
5512         mono_mb_emit_byte (mb, CEE_MONO_TLS);
5513         mono_mb_emit_i4 (mb, tlab_temp_end_offset);
5514         slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
5515
5516         /* Slowpath */
5517
5518         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
5519         mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
5520
5521         /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
5522         mono_mb_emit_ldarg (mb, 0);
5523         mono_mb_emit_ldloc (mb, size_var);
5524         mono_mb_emit_icall (mb, mono_gc_alloc_obj);
5525         mono_mb_emit_byte (mb, CEE_RET);
5526
5527         /* Fastpath */
5528         mono_mb_patch_short_branch (mb, slowpath_branch);
5529
5530         /* FIXME: Memory barrier */
5531
5532         /* *p = vtable; */
5533         mono_mb_emit_ldloc (mb, p_var);
5534         mono_mb_emit_ldarg (mb, 0);
5535         mono_mb_emit_byte (mb, CEE_STIND_I);
5536         
5537         /* return p */
5538         mono_mb_emit_ldloc (mb, p_var);
5539         mono_mb_emit_byte (mb, CEE_RET);
5540
5541         res = mono_mb_create_method (mb, csig, 8);
5542         mono_mb_free (mb);
5543         mono_method_get_header (res)->init_locals = FALSE;
5544         return res;
5545 }
5546
5547 static MonoMethod* alloc_method_cache [ATYPE_NUM];
5548
5549 /*
5550  * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
5551  * The signature of the called method is:
5552  *      object allocate (MonoVTable *vtable)
5553  */
5554 MonoMethod*
5555 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
5556 {
5557         int tlab_next_offset = -1;
5558         int tlab_temp_end_offset = -1;
5559         MonoClass *klass = vtable->klass;
5560         MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
5561         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
5562
5563         if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
5564                 return NULL;
5565         if (klass->instance_size > tlab_size)
5566                 return NULL;
5567         if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
5568                 return NULL;
5569         if (klass->rank)
5570                 return NULL;
5571         if (klass->byval_arg.type == MONO_TYPE_STRING)
5572                 return NULL;
5573         if (collect_before_allocs)
5574                 return NULL;
5575
5576         return mono_gc_get_managed_allocator_by_type (0);
5577 }
5578
5579 int
5580 mono_gc_get_managed_allocator_type (MonoMethod *managed_alloc)
5581 {
5582         return 0;
5583 }
5584
5585 MonoMethod*
5586 mono_gc_get_managed_allocator_by_type (int atype)
5587 {
5588         MonoMethod *res;
5589
5590         mono_loader_lock ();
5591         res = alloc_method_cache [atype];
5592         if (!res)
5593                 res = alloc_method_cache [atype] = create_allocator (atype);
5594         mono_loader_unlock ();
5595         return res;
5596 }
5597
5598 guint32
5599 mono_gc_get_managed_allocator_types (void)
5600 {
5601         return ATYPE_NUM;
5602 }
5603
5604 static MonoMethod *write_barrier_method;
5605
5606 MonoMethod*
5607 mono_gc_get_write_barrier (void)
5608 {
5609         MonoMethod *res;
5610         int remset_offset = -1;
5611         int remset_var, next_var;
5612         MonoMethodBuilder *mb;
5613         MonoMethodSignature *sig;
5614         int label1, label2;
5615
5616         MONO_THREAD_VAR_OFFSET (remembered_set, remset_offset);
5617
5618         // FIXME: Maybe create a separate version for ctors (the branch would be
5619         // correctly predicted more times)
5620         if (write_barrier_method)
5621                 return write_barrier_method;
5622
5623         /* Create the IL version of mono_gc_barrier_generic_store () */
5624         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
5625         sig->ret = &mono_defaults.void_class->byval_arg;
5626         sig->params [0] = &mono_defaults.int_class->byval_arg;
5627         sig->params [1] = &mono_defaults.object_class->byval_arg;
5628
5629         mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
5630
5631         /* ptr_in_nursery () check */
5632 #ifdef ALIGN_NURSERY
5633         /* 
5634          * Masking out the bits might be faster, but we would have to use 64 bit
5635          * immediates, which might be slower.
5636          */
5637         mono_mb_emit_ldarg (mb, 0);
5638         mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
5639         mono_mb_emit_byte (mb, CEE_SHR_UN);
5640         mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
5641         label1 = mono_mb_emit_branch (mb, CEE_BNE_UN);
5642 #else
5643         // FIXME:
5644         g_assert_not_reached ();
5645 #endif
5646
5647         /* Don't need write barrier case */
5648         /* do the assignment */
5649         mono_mb_emit_ldarg (mb, 0);
5650         mono_mb_emit_ldarg (mb, 1);
5651         /* Don't use STIND_REF, as it would cause infinite recursion */
5652         mono_mb_emit_byte (mb, CEE_STIND_I);
5653         mono_mb_emit_byte (mb, CEE_RET);
5654
5655         /* Need write barrier case */
5656         mono_mb_patch_branch (mb, label1);
5657
5658         if (remset_offset == -1)
5659                 // FIXME:
5660                 g_assert_not_reached ();
5661
5662         // remset_var = remembered_set;
5663         remset_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5664         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
5665         mono_mb_emit_byte (mb, CEE_MONO_TLS);
5666         mono_mb_emit_i4 (mb, remset_offset);
5667         mono_mb_emit_stloc (mb, remset_var);
5668
5669         // next_var = rs->store_next
5670         next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
5671         mono_mb_emit_ldloc (mb, remset_var);
5672         mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (RememberedSet, store_next));
5673         mono_mb_emit_byte (mb, CEE_LDIND_I);
5674         mono_mb_emit_stloc (mb, next_var);
5675
5676         // if (rs->store_next < rs->end_set) {
5677         mono_mb_emit_ldloc (mb, next_var);
5678         mono_mb_emit_ldloc (mb, remset_var);
5679         mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (RememberedSet, end_set));
5680         mono_mb_emit_byte (mb, CEE_LDIND_I);
5681         label2 = mono_mb_emit_branch (mb, CEE_BGE);
5682
5683         /* write barrier fast path */
5684         // *(rs->store_next++) = (mword)ptr;
5685         mono_mb_emit_ldloc (mb, next_var);
5686         mono_mb_emit_ldarg (mb, 0);
5687         mono_mb_emit_byte (mb, CEE_STIND_I);
5688
5689         mono_mb_emit_ldloc (mb, next_var);
5690         mono_mb_emit_icon (mb, sizeof (gpointer));
5691         mono_mb_emit_byte (mb, CEE_ADD);
5692         mono_mb_emit_stloc (mb, next_var);
5693
5694         mono_mb_emit_ldloc (mb, remset_var);
5695         mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (RememberedSet, store_next));
5696         mono_mb_emit_ldloc (mb, next_var);
5697         mono_mb_emit_byte (mb, CEE_STIND_I);
5698
5699         // *(void**)ptr = value;
5700         mono_mb_emit_ldarg (mb, 0);
5701         mono_mb_emit_ldarg (mb, 1);
5702         mono_mb_emit_byte (mb, CEE_STIND_I);
5703         mono_mb_emit_byte (mb, CEE_RET);
5704
5705         /* write barrier slow path */
5706         mono_mb_patch_branch (mb, label2);
5707
5708         mono_mb_emit_ldarg (mb, 0);
5709         mono_mb_emit_ldarg (mb, 1);
5710         mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_store);
5711         mono_mb_emit_byte (mb, CEE_RET);
5712
5713         res = mono_mb_create_method (mb, sig, 16);
5714         mono_mb_free (mb);
5715
5716         mono_loader_lock ();
5717         if (write_barrier_method) {
5718                 /* Already created */
5719                 mono_free_method (res);
5720         } else {
5721                 /* double-checked locking */
5722                 mono_memory_barrier ();
5723                 write_barrier_method = res;
5724         }
5725         mono_loader_unlock ();
5726
5727         return write_barrier_method;
5728 }
5729
5730 #endif /* HAVE_SGEN_GC */
5731