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