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