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