2010-04-05 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mono / metadata / sgen-gc.c
1 /*
2  * sgen-gc.c: Simple generational GC.
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *
7  * Copyright 2005-2010 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  *
25  * Copyright 2001-2003 Ximian, Inc
26  * Copyright 2003-2010 Novell, Inc.
27  * 
28  * Permission is hereby granted, free of charge, to any person obtaining
29  * a copy of this software and associated documentation files (the
30  * "Software"), to deal in the Software without restriction, including
31  * without limitation the rights to use, copy, modify, merge, publish,
32  * distribute, sublicense, and/or sell copies of the Software, and to
33  * permit persons to whom the Software is furnished to do so, subject to
34  * the following conditions:
35  * 
36  * The above copyright notice and this permission notice shall be
37  * included in all copies or substantial portions of the Software.
38  * 
39  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46  *
47  *
48  * Important: allocation provides always zeroed memory, having to do
49  * a memset after allocation is deadly for performance.
50  * Memory usage at startup is currently as follows:
51  * 64 KB pinned space
52  * 64 KB internal space
53  * size of nursery
54  * We should provide a small memory config with half the sizes
55  *
56  * We currently try to make as few mono assumptions as possible:
57  * 1) 2-word header with no GC pointers in it (first vtable, second to store the
58  *    forwarding ptr)
59  * 2) gc descriptor is the second word in the vtable (first word in the class)
60  * 3) 8 byte alignment is the minimum and enough (not true for special structures (SIMD), FIXME)
61  * 4) there is a function to get an object's size and the number of
62  *    elements in an array.
63  * 5) we know the special way bounds are allocated for complex arrays
64  * 6) we know about proxies and how to treat them when domains are unloaded
65  *
66  * Always try to keep stack usage to a minimum: no recursive behaviour
67  * and no large stack allocs.
68  *
69  * General description.
70  * Objects are initially allocated in a nursery using a fast bump-pointer technique.
71  * When the nursery is full we start a nursery collection: this is performed with a
72  * copying GC.
73  * When the old generation is full we start a copying GC of the old generation as well:
74  * this will be changed to mark&sweep with copying when fragmentation becomes to severe
75  * in the future.  Maybe we'll even do both during the same collection like IMMIX.
76  *
77  * The things that complicate this description are:
78  * *) pinned objects: we can't move them so we need to keep track of them
79  * *) no precise info of the thread stacks and registers: we need to be able to
80  *    quickly find the objects that may be referenced conservatively and pin them
81  *    (this makes the first issues more important)
82  * *) large objects are too expensive to be dealt with using copying GC: we handle them
83  *    with mark/sweep during major collections
84  * *) some objects need to not move even if they are small (interned strings, Type handles):
85  *    we use mark/sweep for them, too: they are not allocated in the nursery, but inside
86  *    PinnedChunks regions
87  */
88
89 /*
90  * TODO:
91
92  *) we could have a function pointer in MonoClass to implement
93   customized write barriers for value types
94
95  *) investigate the stuff needed to advance a thread to a GC-safe
96   point (single-stepping, read from unmapped memory etc) and implement it.
97   This would enable us to inline allocations and write barriers, for example,
98   or at least parts of them, like the write barrier checks.
99   We may need this also for handling precise info on stacks, even simple things
100   as having uninitialized data on the stack and having to wait for the prolog
101   to zero it. Not an issue for the last frame that we scan conservatively.
102   We could always not trust the value in the slots anyway.
103
104  *) modify the jit to save info about references in stack locations:
105   this can be done just for locals as a start, so that at least
106   part of the stack is handled precisely.
107
108  *) test/fix endianess issues
109
110  *) Implement a card table as the write barrier instead of remembered
111     sets?  Card tables are not easy to implement with our current
112     memory layout.  We have several different kinds of major heap
113     objects: Small objects in regular blocks, small objects in pinned
114     chunks and LOS objects.  If we just have a pointer we have no way
115     to tell which kind of object it points into, therefore we cannot
116     know where its card table is.  The least we have to do to make
117     this happen is to get rid of write barriers for indirect stores.
118     (See next item)
119
120  *) Get rid of write barriers for indirect stores.  We can do this by
121     telling the GC to wbarrier-register an object once we do an ldloca
122     or ldelema on it, and to unregister it once it's not used anymore
123     (it can only travel downwards on the stack).  The problem with
124     unregistering is that it needs to happen eventually no matter
125     what, even if exceptions are thrown, the thread aborts, etc.
126     Rodrigo suggested that we could do only the registering part and
127     let the collector find out (pessimistically) when it's safe to
128     unregister, namely when the stack pointer of the thread that
129     registered the object is higher than it was when the registering
130     happened.  This might make for a good first implementation to get
131     some data on performance.
132
133  *) Some sort of blacklist support?  Blacklists is a concept from the
134     Boehm GC: if during a conservative scan we find pointers to an
135     area which we might use as heap, we mark that area as unusable, so
136     pointer retention by random pinning pointers is reduced.
137
138  *) experiment with max small object size (very small right now - 2kb,
139     because it's tied to the max freelist size)
140
141   *) add an option to mmap the whole heap in one chunk: it makes for many
142      simplifications in the checks (put the nursery at the top and just use a single
143      check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
144      not flexible (too much of the address space may be used by default or we can't
145      increase the heap as needed) and we'd need a race-free mechanism to return memory
146      back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
147      was written to, munmap is needed, but the following mmap may not find the same segment
148      free...)
149
150  *) memzero the major fragments after restarting the world and optionally a smaller
151     chunk at a time
152
153  *) investigate having fragment zeroing threads
154
155  *) separate locks for finalization and other minor stuff to reduce
156     lock contention
157
158  *) try a different copying order to improve memory locality
159
160  *) a thread abort after a store but before the write barrier will
161     prevent the write barrier from executing
162
163  *) specialized dynamically generated markers/copiers
164
165  *) Dynamically adjust TLAB size to the number of threads.  If we have
166     too many threads that do allocation, we might need smaller TLABs,
167     and we might get better performance with larger TLABs if we only
168     have a handful of threads.  We could sum up the space left in all
169     assigned TLABs and if that's more than some percentage of the
170     nursery size, reduce the TLAB size.
171
172  */
173 #include "config.h"
174 #ifdef HAVE_SGEN_GC
175
176 #include <unistd.h>
177 #include <stdio.h>
178 #include <string.h>
179 #include <semaphore.h>
180 #include <signal.h>
181 #include <errno.h>
182 #include <assert.h>
183 #include <pthread.h>
184 #include "metadata/metadata-internals.h"
185 #include "metadata/class-internals.h"
186 #include "metadata/gc-internal.h"
187 #include "metadata/object-internals.h"
188 #include "metadata/threads.h"
189 #include "metadata/sgen-gc.h"
190 #include "metadata/sgen-archdep.h"
191 #include "metadata/mono-gc.h"
192 #include "metadata/method-builder.h"
193 #include "metadata/profiler-private.h"
194 #include "metadata/monitor.h"
195 #include "metadata/threadpool-internals.h"
196 #include "metadata/mempool-internals.h"
197 #include "metadata/marshal.h"
198 #include "utils/mono-mmap.h"
199 #include "utils/mono-time.h"
200 #include "utils/mono-semaphore.h"
201 #include "utils/mono-counters.h"
202
203 #include <mono/utils/memcheck.h>
204
205 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
206         a = i,
207
208 enum {
209 #include "mono/cil/opcode.def"
210         CEE_LAST
211 };
212
213 #undef OPDEF
214
215 /*
216  * ######################################################################
217  * ########  Types and constants used by the GC.
218  * ######################################################################
219  */
220 #if SIZEOF_VOID_P == 4
221 typedef guint32 mword;
222 #else
223 typedef guint64 mword;
224 #endif
225
226 static int gc_initialized = 0;
227 static int gc_debug_level = 0;
228 static FILE* gc_debug_file;
229 /* If set, do a minor collection before every allocation */
230 static gboolean collect_before_allocs = FALSE;
231 /* If set, do a heap consistency check before each minor collection */
232 static gboolean consistency_check_at_minor_collection = FALSE;
233 /* If set, check that there are no references to the domain left at domain unload */
234 static gboolean xdomain_checks = FALSE;
235 /* If not null, dump the heap after each collection into this file */
236 static FILE *heap_dump_file = NULL;
237 /* If set, mark stacks conservatively, even if precise marking is possible */
238 static gboolean conservative_stack_mark = FALSE;
239 /* If set, do a plausibility check on the scan_starts before and after
240    each collection */
241 static gboolean do_scan_starts_check = FALSE;
242
243 /*
244  * Turning on heavy statistics will turn off the managed allocator and
245  * the managed write barrier.
246  */
247 //#define HEAVY_STATISTICS
248
249 #ifdef HEAVY_STATISTICS
250 #define HEAVY_STAT(x)   x
251 #else
252 #define HEAVY_STAT(x)
253 #endif
254
255 #ifdef HEAVY_STATISTICS
256 static long stat_objects_alloced = 0;
257 static long stat_bytes_alloced = 0;
258 static long stat_objects_alloced_degraded = 0;
259 static long stat_bytes_alloced_degraded = 0;
260 static long stat_bytes_alloced_los = 0;
261 static long stat_copy_object_called_nursery = 0;
262 static long stat_objects_copied_nursery = 0;
263 static long stat_copy_object_called_major = 0;
264 static long stat_objects_copied_major = 0;
265
266 static long stat_copy_object_failed_from_space = 0;
267 static long stat_copy_object_failed_forwarded = 0;
268 static long stat_copy_object_failed_pinned = 0;
269 static long stat_copy_object_failed_large_pinned = 0;
270 static long stat_copy_object_failed_to_space = 0;
271
272 static long stat_store_remsets = 0;
273 static long stat_store_remsets_unique = 0;
274 static long stat_saved_remsets_1 = 0;
275 static long stat_saved_remsets_2 = 0;
276 static long stat_global_remsets_added = 0;
277 static long stat_global_remsets_processed = 0;
278
279 static long num_copy_object_called = 0;
280 static long num_objects_copied = 0;
281
282 static int stat_wbarrier_set_field = 0;
283 static int stat_wbarrier_set_arrayref = 0;
284 static int stat_wbarrier_arrayref_copy = 0;
285 static int stat_wbarrier_generic_store = 0;
286 static int stat_wbarrier_generic_store_remset = 0;
287 static int stat_wbarrier_set_root = 0;
288 static int stat_wbarrier_value_copy = 0;
289 static int stat_wbarrier_object_copy = 0;
290 #endif
291
292 static long time_minor_pre_collection_fragment_clear = 0;
293 static long time_minor_pinning = 0;
294 static long time_minor_scan_remsets = 0;
295 static long time_minor_scan_pinned = 0;
296 static long time_minor_scan_registered_roots = 0;
297 static long time_minor_scan_thread_data = 0;
298 static long time_minor_finish_gray_stack = 0;
299 static long time_minor_fragment_creation = 0;
300
301 static long time_major_pre_collection_fragment_clear = 0;
302 static long time_major_pinning = 0;
303 static long time_major_scan_pinned = 0;
304 static long time_major_scan_registered_roots = 0;
305 static long time_major_scan_thread_data = 0;
306 static long time_major_scan_alloc_pinned = 0;
307 static long time_major_scan_finalized = 0;
308 static long time_major_scan_big_objects = 0;
309 static long time_major_finish_gray_stack = 0;
310 static long time_major_sweep = 0;
311 static long time_major_fragment_creation = 0;
312
313 static long pinned_chunk_bytes_alloced = 0;
314 static long large_internal_bytes_alloced = 0;
315
316 enum {
317         INTERNAL_MEM_PIN_QUEUE,
318         INTERNAL_MEM_FRAGMENT,
319         INTERNAL_MEM_SECTION,
320         INTERNAL_MEM_SCAN_STARTS,
321         INTERNAL_MEM_FIN_TABLE,
322         INTERNAL_MEM_FINALIZE_ENTRY,
323         INTERNAL_MEM_DISLINK_TABLE,
324         INTERNAL_MEM_DISLINK,
325         INTERNAL_MEM_ROOTS_TABLE,
326         INTERNAL_MEM_ROOT_RECORD,
327         INTERNAL_MEM_STATISTICS,
328         INTERNAL_MEM_REMSET,
329         INTERNAL_MEM_GRAY_QUEUE,
330         INTERNAL_MEM_STORE_REMSET,
331         INTERNAL_MEM_MAX
332 };
333
334 static long small_internal_mem_bytes [INTERNAL_MEM_MAX];
335
336 /*
337 void
338 mono_gc_flush_info (void)
339 {
340         fflush (gc_debug_file);
341 }
342 */
343
344 #define MAX_DEBUG_LEVEL 2
345 #define DEBUG(level,a) do {if (G_UNLIKELY ((level) <= MAX_DEBUG_LEVEL && (level) <= gc_debug_level)) a;} while (0)
346
347 /* Define this to allow the user to change some of the constants by specifying
348  * their values in the MONO_GC_PARAMS environmental variable. See
349  * mono_gc_base_init for details. */
350 #define USER_CONFIG 1
351
352 #define TV_DECLARE(name) gint64 name
353 #define TV_GETTIME(tv) tv = mono_100ns_ticks ()
354 #define TV_ELAPSED(start,end) (int)((end-start) / 10)
355 #define TV_ELAPSED_MS(start,end) ((TV_ELAPSED((start),(end)) + 500) / 1000)
356
357 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
358
359 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
360
361 enum {
362         MEMORY_ROLE_GEN0,
363         MEMORY_ROLE_GEN1,
364         MEMORY_ROLE_PINNED
365 };
366
367 typedef struct _Block Block;
368 struct _Block {
369         void *next;
370         unsigned char role;
371 };
372
373 /* each request from the OS ends up in a GCMemSection */
374 typedef struct _GCMemSection GCMemSection;
375 struct _GCMemSection {
376         Block block;
377         char *data;
378         mword size;
379         /* pointer where more data could be allocated if it fits */
380         char *next_data;
381         char *end_data;
382         /* 
383          * scan starts is an array of pointers to objects equally spaced in the allocation area
384          * They let use quickly find pinned objects from pinning pointers.
385          */
386         char **scan_starts;
387         /* in major collections indexes in the pin_queue for objects that pin this section */
388         int pin_queue_start;
389         int pin_queue_end;
390         unsigned short num_scan_start;
391         gboolean is_to_space;
392 };
393
394 #define SIZEOF_GC_MEM_SECTION   ((sizeof (GCMemSection) + 7) & ~7)
395
396 /* large object space struct: 64+ KB */
397 /* we could make this limit much smaller to avoid memcpy copy
398  * and potentially have more room in the GC descriptor: need to measure
399  * This also means that such small OS objects will need to be
400  * allocated in a different way (using pinned chunks).
401  * We may want to put large but smaller than 64k objects in the fixed space
402  * when we move the object from one generation to another (to limit the
403  * pig in the snake effect).
404  * Note: it may be worth to have an optimized copy function, since we can
405  * assume that objects are aligned and have a multiple of 8 size.
406  * FIXME: This structure needs to be a multiple of 8 bytes in size: this is not
407  * true if MONO_ZERO_LEN_ARRAY is nonzero.
408  */
409 typedef struct _LOSObject LOSObject;
410 struct _LOSObject {
411         LOSObject *next;
412         mword size; /* this is the object size */
413         int dummy; /* to have a sizeof (LOSObject) a multiple of ALLOC_ALIGN  and data starting at same alignment */
414         guint16 role;
415         guint16 scanned;
416         char data [MONO_ZERO_LEN_ARRAY];
417 };
418
419 /* Pinned objects are allocated in the LOS space if bigger than half a page
420  * or from freelists otherwise. We assume that pinned objects are relatively few
421  * and they have a slow dying speed (like interned strings, thread objects).
422  * As such they will be collected only at major collections.
423  * free lists are not global: when we need memory we allocate a PinnedChunk.
424  * Each pinned chunk is made of several pages, the first of wich is used
425  * internally for bookeeping (here think of a page as 4KB). The bookeeping
426  * includes the freelists vectors and info about the object size of each page
427  * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
428  * a size is assigned to it, the page is divided in the proper chunks and each
429  * chunk is added to the freelist. To not waste space, the remaining space in the
430  * first page is used as objects of size 16 or 32 (need to measure which are more
431  * common).
432  * We use this same structure to allocate memory used internally by the GC, so
433  * we never use malloc/free if we need to alloc during collection: the world is stopped
434  * and malloc/free will deadlock.
435  * When we want to iterate over pinned objects, we just scan a page at a time
436  * linearly according to the size of objects in the page: the next pointer used to link
437  * the items in the freelist uses the same word as the vtable. Since we keep freelists
438  * for each pinned chunk, if the word points outside the pinned chunk it means
439  * it is an object.
440  * We could avoid this expensive scanning in creative ways. We could have a policy
441  * of putting in the pinned space only objects we know about that have no struct fields
442  * with references and we can easily use a even expensive write barrier for them,
443  * since pointer writes on such objects should be rare.
444  * The best compromise is to just alloc interned strings and System.MonoType in them.
445  * It would be nice to allocate MonoThread in it, too: must check that we properly
446  * use write barriers so we don't have to do any expensive scanning of the whole pinned
447  * chunk list during minor collections. We can avoid it now because we alloc in it only
448  * reference-free objects.
449  */
450 #define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
451 #define MAX_FREELIST_SIZE 2048
452 #define PINNED_PAGE_SIZE (4096)
453 #define PINNED_CHUNK_MIN_SIZE (4096*8)
454 typedef struct _PinnedChunk PinnedChunk;
455 struct _PinnedChunk {
456         Block block;
457         int num_pages;
458         int *page_sizes; /* a 0 means the page is still unused */
459         void **free_list;
460         void *start_data;
461         void *data [1]; /* page sizes and free lists are stored here */
462 };
463
464 /* The method used to clear the nursery */
465 /* Clearing at nursery collections is the safest, but has bad interactions with caches.
466  * Clearing at TLAB creation is much faster, but more complex and it might expose hard
467  * to find bugs.
468  */
469 typedef enum {
470         CLEAR_AT_GC,
471         CLEAR_AT_TLAB_CREATION
472 } NurseryClearPolicy;
473
474 static NurseryClearPolicy nursery_clear_policy = CLEAR_AT_TLAB_CREATION;
475
476 /* 
477  * If this is set, the nursery is aligned to an address aligned to its size, ie.
478  * a 1MB nursery will be aligned to an address divisible by 1MB. This allows us to
479  * speed up ptr_in_nursery () checks which are very frequent. This requires the
480  * nursery size to be a compile time constant.
481  */
482 #define ALIGN_NURSERY 1
483
484 /*
485  * The young generation is divided into fragments. This is because
486  * we can hand one fragments to a thread for lock-less fast alloc and
487  * because the young generation ends up fragmented anyway by pinned objects.
488  * Once a collection is done, a list of fragments is created. When doing
489  * thread local alloc we use smallish nurseries so we allow new threads to
490  * allocate memory from gen0 without triggering a collection. Threads that
491  * are found to allocate lots of memory are given bigger fragments. This
492  * should make the finalizer thread use little nursery memory after a while.
493  * We should start assigning threads very small fragments: if there are many
494  * threads the nursery will be full of reserved space that the threads may not
495  * use at all, slowing down allocation speed.
496  * Thread local allocation is done from areas of memory Hotspot calls Thread Local 
497  * Allocation Buffers (TLABs).
498  */
499 typedef struct _Fragment Fragment;
500
501 struct _Fragment {
502         Fragment *next;
503         char *fragment_start;
504         char *fragment_limit; /* the current soft limit for allocation */
505         char *fragment_end;
506 };
507
508 /* the runtime can register areas of memory as roots: we keep two lists of roots,
509  * a pinned root set for conservatively scanned roots and a normal one for
510  * precisely scanned roots (currently implemented as a single list).
511  */
512 typedef struct _RootRecord RootRecord;
513 struct _RootRecord {
514         RootRecord *next;
515         char *start_root;
516         char *end_root;
517         mword root_desc;
518 };
519
520 /* for use with write barriers */
521 typedef struct _RememberedSet RememberedSet;
522 struct _RememberedSet {
523         mword *store_next;
524         mword *end_set;
525         RememberedSet *next;
526         mword data [MONO_ZERO_LEN_ARRAY];
527 };
528
529 /*
530  * We're never actually using the first element.  It's always set to
531  * NULL to simplify the elimination of consecutive duplicate
532  * entries.
533  */
534 #define STORE_REMSET_BUFFER_SIZE        1024
535
536 typedef struct _GenericStoreRememberedSet GenericStoreRememberedSet;
537 struct _GenericStoreRememberedSet {
538         GenericStoreRememberedSet *next;
539         /* We need one entry less because the first entry of store
540            remset buffers is always a dummy and we don't copy it. */
541         gpointer data [STORE_REMSET_BUFFER_SIZE - 1];
542 };
543
544 /* we have 4 possible values in the low 2 bits */
545 enum {
546         REMSET_LOCATION, /* just a pointer to the exact location */
547         REMSET_RANGE,    /* range of pointer fields */
548         REMSET_OBJECT,   /* mark all the object for scanning */
549         REMSET_OTHER,    /* all others */
550         REMSET_TYPE_MASK = 0x3
551 };
552
553 /* Subtypes of REMSET_OTHER */
554 enum {
555         REMSET_VTYPE, /* a valuetype array described by a gc descriptor and a count */
556         REMSET_ROOT_LOCATION, /* a location inside a root */
557 };
558
559 #ifdef HAVE_KW_THREAD
560 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
561 #endif
562 static pthread_key_t remembered_set_key;
563 static RememberedSet *global_remset;
564 static RememberedSet *freed_thread_remsets;
565 //static int store_to_global_remset = 0;
566 static GenericStoreRememberedSet *generic_store_remsets = NULL;
567
568 /* FIXME: later choose a size that takes into account the RememberedSet struct
569  * and doesn't waste any alloc paddin space.
570  */
571 #define DEFAULT_REMSET_SIZE 1024
572 static RememberedSet* alloc_remset (int size, gpointer id);
573
574 /* Structure that corresponds to a MonoVTable: desc is a mword so requires
575  * no cast from a pointer to an integer
576  */
577 typedef struct {
578         MonoClass *klass;
579         mword desc;
580 } GCVTable;
581
582 /* these bits are set in the object vtable: we could merge them since an object can be
583  * either pinned or forwarded but not both.
584  * We store them in the vtable slot because the bits are used in the sync block for
585  * other purposes: if we merge them and alloc the sync blocks aligned to 8 bytes, we can change
586  * this and use bit 3 in the syncblock (with the lower two bits both set for forwarded, that
587  * would be an invalid combination for the monitor and hash code).
588  * The values are already shifted.
589  * The forwarding address is stored in the sync block.
590  */
591 #define FORWARDED_BIT 1
592 #define PINNED_BIT 2
593 #define VTABLE_BITS_MASK 0x3
594
595 /* returns NULL if not forwarded, or the forwarded address */
596 #define object_is_forwarded(obj) (((mword*)(obj))[0] & FORWARDED_BIT? (void*)(((mword*)(obj))[1]): NULL)
597 /* set the forwarded address fw_addr for object obj */
598 #define forward_object(obj,fw_addr) do {        \
599                 ((mword*)(obj))[0] |= FORWARDED_BIT;    \
600                 ((mword*)(obj))[1] = (mword)(fw_addr);  \
601         } while (0)
602
603 #define object_is_pinned(obj) (((mword*)(obj))[0] & PINNED_BIT)
604 #define pin_object(obj) do {    \
605                 ((mword*)(obj))[0] |= PINNED_BIT;       \
606         } while (0)
607 #define unpin_object(obj) do {  \
608                 ((mword*)(obj))[0] &= ~PINNED_BIT;      \
609         } while (0)
610
611 #ifdef ALIGN_NURSERY
612 #define ptr_in_nursery(ptr) (((mword)(ptr) & ~((1 << DEFAULT_NURSERY_BITS) - 1)) == (mword)nursery_start)
613 #else
614 #define ptr_in_nursery(ptr) ((char*)(ptr) >= nursery_start && (char*)(ptr) < nursery_real_end)
615 #endif
616
617 /*
618  * Since we set bits in the vtable, use the macro to load it from the pointer to
619  * an object that is potentially pinned.
620  */
621 #define LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~VTABLE_BITS_MASK)
622
623 static const char*
624 safe_name (void* obj)
625 {
626         MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
627         return vt->klass->name;
628 }
629
630 static inline guint
631 safe_object_get_size (MonoObject* o)
632 {
633         MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
634         if (klass == mono_defaults.string_class) {
635                 return sizeof (MonoString) + 2 * mono_string_length ((MonoString*) o) + 2;
636         } else if (klass->rank) {
637                 MonoArray *array = (MonoArray*)o;
638                 size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length (array);
639                 if (G_UNLIKELY (array->bounds)) {
640                         size += sizeof (mono_array_size_t) - 1;
641                         size &= ~(sizeof (mono_array_size_t) - 1);
642                         size += sizeof (MonoArrayBounds) * klass->rank;
643                 }
644                 return size;
645         } else {
646                 /* from a created object: the class must be inited already */
647                 return klass->instance_size;
648         }
649 }
650
651 /*
652  * ######################################################################
653  * ########  Global data.
654  * ######################################################################
655  */
656 static LOCK_DECLARE (gc_mutex);
657 static int gc_disabled = 0;
658 static int num_minor_gcs = 0;
659 static int num_major_gcs = 0;
660
661 #ifdef USER_CONFIG
662
663 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
664 //#define DEFAULT_NURSERY_SIZE (1024*512*125+4096*118)
665 #define DEFAULT_NURSERY_SIZE (default_nursery_size)
666 static int default_nursery_size = (1 << 20);
667 #ifdef ALIGN_NURSERY
668 /* The number of trailing 0 bits in DEFAULT_NURSERY_SIZE */
669 #define DEFAULT_NURSERY_BITS (default_nursery_bits)
670 static int default_nursery_bits = 20;
671 #endif
672
673 #else
674
675 #define DEFAULT_NURSERY_SIZE (1024*512*2)
676 #ifdef ALIGN_NURSERY
677 #define DEFAULT_NURSERY_BITS 20
678 #endif
679
680 #endif
681
682 #define MAJOR_SECTION_SIZE      (128*1024)
683 #define BLOCK_FOR_OBJECT(o)             ((Block*)(((mword)(o)) & ~(MAJOR_SECTION_SIZE - 1)))
684 #define MAJOR_SECTION_FOR_OBJECT(o)     ((GCMemSection*)BLOCK_FOR_OBJECT ((o)))
685 #define MIN_MINOR_COLLECTION_SECTION_ALLOWANCE  (DEFAULT_NURSERY_SIZE * 3 / MAJOR_SECTION_SIZE)
686 #define MIN_LOS_ALLOWANCE               (DEFAULT_NURSERY_SIZE * 2)
687 /* to quickly find the head of an object pinned by a conservative address
688  * we keep track of the objects allocated for each SCAN_START_SIZE memory
689  * chunk in the nursery or other memory sections. Larger values have less
690  * memory overhead and bigger runtime cost. 4-8 KB are reasonable values.
691  */
692 #define SCAN_START_SIZE (4096*2)
693 /* the minimum size of a fragment that we consider useful for allocation */
694 #define FRAGMENT_MIN_SIZE (512)
695 /* This is a fixed value used for pinned chunks, not the system pagesize */
696 #define FREELIST_PAGESIZE 4096
697
698 static mword pagesize = 4096;
699 static mword nursery_size;
700 static int degraded_mode = 0;
701
702 static int minor_collection_section_allowance;
703 static int minor_collection_sections_alloced = 0;
704 static int num_major_sections = 0;
705
706 static LOSObject *los_object_list = NULL;
707 static mword los_memory_usage = 0;
708 static mword los_num_objects = 0;
709 static mword next_los_collection = 2*1024*1024; /* 2 MB, need to tune */
710 static mword total_alloc = 0;
711 /* use this to tune when to do a major/minor collection */
712 static mword memory_pressure = 0;
713
714 static GCMemSection *section_list = NULL;
715 static GCMemSection *nursery_section = NULL;
716 static mword lowest_heap_address = ~(mword)0;
717 static mword highest_heap_address = 0;
718
719 static LOCK_DECLARE (interruption_mutex);
720
721 typedef struct _FinalizeEntry FinalizeEntry;
722 struct _FinalizeEntry {
723         FinalizeEntry *next;
724         void *object;
725 };
726
727 typedef struct _FinalizeEntryHashTable FinalizeEntryHashTable;
728 struct _FinalizeEntryHashTable {
729         FinalizeEntry **table;
730         mword size;
731         int num_registered;
732 };
733
734 typedef struct _DisappearingLink DisappearingLink;
735 struct _DisappearingLink {
736         DisappearingLink *next;
737         void **link;
738 };
739
740 typedef struct _DisappearingLinkHashTable DisappearingLinkHashTable;
741 struct _DisappearingLinkHashTable {
742         DisappearingLink **table;
743         mword size;
744         int num_links;
745 };
746
747 #define LARGE_INTERNAL_MEM_HEADER_MAGIC 0x7d289f3a
748
749 typedef struct _LargeInternalMemHeader LargeInternalMemHeader;
750 struct _LargeInternalMemHeader {
751         guint32 magic;
752         size_t size;
753         double data[0];
754 };
755
756 enum {
757         GENERATION_NURSERY,
758         GENERATION_OLD,
759         GENERATION_MAX
760 };
761
762 /*
763  * The link pointer is hidden by negating each bit.  We use the lowest
764  * bit of the link (before negation) to store whether it needs
765  * resurrection tracking.
766  */
767 #define HIDE_POINTER(p,t)       ((gpointer)(~((gulong)(p)|((t)?1:0))))
768 #define REVEAL_POINTER(p)       ((gpointer)((~(gulong)(p))&~3L))
769
770 #define DISLINK_OBJECT(d)       (REVEAL_POINTER (*(d)->link))
771 #define DISLINK_TRACK(d)        ((~(gulong)(*(d)->link)) & 1)
772
773 /*
774  * The finalizable hash has the object as the key, the 
775  * disappearing_link hash, has the link address as key.
776  */
777 static FinalizeEntryHashTable minor_finalizable_hash;
778 static FinalizeEntryHashTable major_finalizable_hash;
779 /* objects that are ready to be finalized */
780 static FinalizeEntry *fin_ready_list = NULL;
781 static FinalizeEntry *critical_fin_list = NULL;
782
783 static DisappearingLinkHashTable minor_disappearing_link_hash;
784 static DisappearingLinkHashTable major_disappearing_link_hash;
785
786 static int num_ready_finalizers = 0;
787 static int no_finalize = 0;
788
789 /* keep each size a multiple of ALLOC_ALIGN */
790 /* on 64 bit systems 8 is likely completely unused. */
791 static const int freelist_sizes [] = {
792         8, 16, 24, 32, 40, 48, 64, 80,
793         96, 128, 160, 192, 224, 256, 320, 384,
794         448, 512, 584, 680, 816, 1024, 1360, 2048};
795 #define FREELIST_NUM_SLOTS (sizeof (freelist_sizes) / sizeof (freelist_sizes [0]))
796
797 static char* max_pinned_chunk_addr = NULL;
798 static char* min_pinned_chunk_addr = (char*)-1;
799 /* pinned_chunk_list is used for allocations of objects that are never moved */
800 static PinnedChunk *pinned_chunk_list = NULL;
801 /* internal_chunk_list is used for allocating structures needed by the GC */
802 static PinnedChunk *internal_chunk_list = NULL;
803
804 static gboolean
805 obj_is_from_pinned_alloc (char *p)
806 {
807         return BLOCK_FOR_OBJECT (p)->role == MEMORY_ROLE_PINNED;
808 }
809
810 static int slot_for_size (size_t size);
811
812 static void
813 free_pinned_object (PinnedChunk *chunk, char *obj, size_t size)
814 {
815         void **p = (void**)obj;
816         int slot = slot_for_size (size);
817
818         g_assert (obj >= (char*)chunk->start_data && obj < ((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE));
819         *p = chunk->free_list [slot];
820         chunk->free_list [slot] = p;
821 }
822
823 enum {
824         ROOT_TYPE_NORMAL = 0, /* "normal" roots */
825         ROOT_TYPE_PINNED = 1, /* roots without a GC descriptor */
826         ROOT_TYPE_WBARRIER = 2, /* roots with a write barrier */
827         ROOT_TYPE_NUM
828 };
829
830 /* registered roots: the key to the hash is the root start address */
831 /* 
832  * Different kinds of roots are kept separate to speed up pin_from_roots () for example.
833  */
834 static RootRecord **roots_hash [ROOT_TYPE_NUM] = { NULL, NULL };
835 static int roots_hash_size [ROOT_TYPE_NUM] = { 0, 0, 0 };
836 static mword roots_size = 0; /* amount of memory in the root set */
837 static int num_roots_entries [ROOT_TYPE_NUM] = { 0, 0, 0 };
838
839 /* 
840  * The current allocation cursors
841  * We allocate objects in the nursery.
842  * The nursery is the area between nursery_start and nursery_real_end.
843  * Allocation is done from a Thread Local Allocation Buffer (TLAB). TLABs are allocated
844  * from nursery fragments.
845  * tlab_next is the pointer to the space inside the TLAB where the next object will 
846  * be allocated.
847  * tlab_temp_end is the pointer to the end of the temporary space reserved for
848  * the allocation: it allows us to set the scan starts at reasonable intervals.
849  * tlab_real_end points to the end of the TLAB.
850  * nursery_frag_real_end points to the end of the currently used nursery fragment.
851  * nursery_first_pinned_start points to the start of the first pinned object in the nursery
852  * nursery_last_pinned_end points to the end of the last pinned object in the nursery
853  * At the next allocation, the area of the nursery where objects can be present is
854  * between MIN(nursery_first_pinned_start, first_fragment_start) and
855  * MAX(nursery_last_pinned_end, nursery_frag_real_end)
856  */
857 static char *nursery_start = NULL;
858
859 /* eventually share with MonoThread? */
860 typedef struct _SgenThreadInfo SgenThreadInfo;
861
862 struct _SgenThreadInfo {
863         SgenThreadInfo *next;
864         ARCH_THREAD_TYPE id;
865         unsigned int stop_count; /* to catch duplicate signals */
866         int signal;
867         int skip;
868         volatile int in_critical_region;
869         void *stack_end;
870         void *stack_start;
871         void *stack_start_limit;
872         char **tlab_next_addr;
873         char **tlab_start_addr;
874         char **tlab_temp_end_addr;
875         char **tlab_real_end_addr;
876         gpointer **store_remset_buffer_addr;
877         long *store_remset_buffer_index_addr;
878         RememberedSet *remset;
879         gpointer runtime_data;
880         gpointer stopped_ip;    /* only valid if the thread is stopped */
881         MonoDomain *stopped_domain; /* ditto */
882         gpointer *stopped_regs;     /* ditto */
883 #ifndef HAVE_KW_THREAD
884         char *tlab_start;
885         char *tlab_next;
886         char *tlab_temp_end;
887         char *tlab_real_end;
888         gpointer *store_remset_buffer;
889         long store_remset_buffer_index;
890 #endif
891 };
892
893 #ifdef HAVE_KW_THREAD
894 #define TLAB_ACCESS_INIT
895 #define TLAB_START      tlab_start
896 #define TLAB_NEXT       tlab_next
897 #define TLAB_TEMP_END   tlab_temp_end
898 #define TLAB_REAL_END   tlab_real_end
899 #define REMEMBERED_SET  remembered_set
900 #define STORE_REMSET_BUFFER     store_remset_buffer
901 #define STORE_REMSET_BUFFER_INDEX       store_remset_buffer_index
902 #define IN_CRITICAL_REGION thread_info->in_critical_region
903 #else
904 static pthread_key_t thread_info_key;
905 #define TLAB_ACCESS_INIT        SgenThreadInfo *__thread_info__ = pthread_getspecific (thread_info_key)
906 #define TLAB_START      (__thread_info__->tlab_start)
907 #define TLAB_NEXT       (__thread_info__->tlab_next)
908 #define TLAB_TEMP_END   (__thread_info__->tlab_temp_end)
909 #define TLAB_REAL_END   (__thread_info__->tlab_real_end)
910 #define REMEMBERED_SET  (__thread_info__->remset)
911 #define STORE_REMSET_BUFFER     (__thread_info__->store_remset_buffer)
912 #define STORE_REMSET_BUFFER_INDEX       (__thread_info__->store_remset_buffer_index)
913 #define IN_CRITICAL_REGION (__thread_info__->in_critical_region)
914 #endif
915
916 /* we use the memory barrier only to prevent compiler reordering (a memory constraint may be enough) */
917 #define ENTER_CRITICAL_REGION do {IN_CRITICAL_REGION = 1;mono_memory_barrier ();} while (0)
918 #define EXIT_CRITICAL_REGION  do {IN_CRITICAL_REGION = 0;mono_memory_barrier ();} while (0)
919
920 /*
921  * FIXME: What is faster, a TLS variable pointing to a structure, or separate TLS 
922  * variables for next+temp_end ?
923  */
924 #ifdef HAVE_KW_THREAD
925 static __thread SgenThreadInfo *thread_info;
926 static __thread char *tlab_start;
927 static __thread char *tlab_next;
928 static __thread char *tlab_temp_end;
929 static __thread char *tlab_real_end;
930 static __thread gpointer *store_remset_buffer;
931 static __thread long store_remset_buffer_index;
932 /* Used by the managed allocator/wbarrier */
933 static __thread char **tlab_next_addr;
934 static __thread char *stack_end;
935 static __thread long *store_remset_buffer_index_addr;
936 #endif
937 static char *nursery_next = NULL;
938 static char *nursery_frag_real_end = NULL;
939 static char *nursery_real_end = NULL;
940 //static char *nursery_first_pinned_start = NULL;
941 static char *nursery_last_pinned_end = NULL;
942
943 /* The size of a TLAB */
944 /* The bigger the value, the less often we have to go to the slow path to allocate a new 
945  * one, but the more space is wasted by threads not allocating much memory.
946  * FIXME: Tune this.
947  * FIXME: Make this self-tuning for each thread.
948  */
949 static guint32 tlab_size = (1024 * 4);
950
951 /* fragments that are free and ready to be used for allocation */
952 static Fragment *nursery_fragments = NULL;
953 /* freeelist of fragment structures */
954 static Fragment *fragment_freelist = NULL;
955
956 /* 
957  * used when moving the objects
958  */
959 static char *to_space_bumper = NULL;
960 static char *to_space_top = NULL;
961 static GCMemSection *to_space_section = NULL;
962
963 /* objects bigger then this go into the large object space */
964 #define MAX_SMALL_OBJ_SIZE MAX_FREELIST_SIZE
965
966 /* Functions supplied by the runtime to be called by the GC */
967 static MonoGCCallbacks gc_callbacks;
968
969 /*
970  * ######################################################################
971  * ########  Macros and function declarations.
972  * ######################################################################
973  */
974
975 #define UPDATE_HEAP_BOUNDARIES(low,high) do {   \
976                 if ((mword)(low) < lowest_heap_address) \
977                         lowest_heap_address = (mword)(low);     \
978                 if ((mword)(high) > highest_heap_address)       \
979                         highest_heap_address = (mword)(high);   \
980         } while (0)
981 #define ADDR_IN_HEAP_BOUNDARIES(addr) ((p) >= lowest_heap_address && (p) < highest_heap_address)
982
983 inline static void*
984 align_pointer (void *ptr)
985 {
986         mword p = (mword)ptr;
987         p += sizeof (gpointer) - 1;
988         p &= ~ (sizeof (gpointer) - 1);
989         return (void*)p;
990 }
991
992 /* forward declarations */
993 static void* get_internal_mem          (size_t size, int type);
994 static void  free_internal_mem         (void *addr, int type);
995 static void* get_os_memory             (size_t size, int activate);
996 static void  free_os_memory            (void *addr, size_t size);
997 static G_GNUC_UNUSED void  report_internal_mem_usage (void);
998
999 static int stop_world (void);
1000 static int restart_world (void);
1001 static void scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise);
1002 static void scan_from_remsets (void *start_nursery, void *end_nursery);
1003 static void find_pinning_ref_from_thread (char *obj, size_t size);
1004 static void update_current_thread_stack (void *start);
1005 static GCMemSection* alloc_major_section (void);
1006 static void finalize_in_range (char *start, char *end, int generation);
1007 static void add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation);
1008 static void null_link_in_range (char *start, char *end, int generation);
1009 static void null_links_for_domain (MonoDomain *domain, int generation);
1010 static gboolean search_fragment_for_size (size_t size);
1011 static void mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end);
1012 static void clear_remsets (void);
1013 static void clear_tlabs (void);
1014 typedef void (*ScanPinnedObjectCallbackFunc) (PinnedChunk*, char*, size_t, void*);
1015 static void scan_pinned_objects (ScanPinnedObjectCallbackFunc callback, void *callback_data);
1016 static void sweep_pinned_objects (void);
1017 static void scan_from_pinned_objects (char *addr_start, char *addr_end);
1018 static void free_large_object (LOSObject *obj);
1019 static void free_major_section (GCMemSection *section);
1020 static void to_space_expand (void);
1021
1022 static void mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track);
1023
1024 void describe_ptr (char *ptr);
1025 void check_consistency (void);
1026 char* check_object (char *start);
1027
1028 void mono_gc_scan_for_specific_ref (MonoObject *key);
1029
1030 /*
1031  * ######################################################################
1032  * ########  GC descriptors
1033  * ######################################################################
1034  * Used to quickly get the info the GC needs about an object: size and
1035  * where the references are held.
1036  */
1037 /* objects are aligned to 8 bytes boundaries
1038  * A descriptor is a pointer in MonoVTable, so 32 or 64 bits of size.
1039  * The low 3 bits define the type of the descriptor. The other bits
1040  * depend on the type.
1041  * As a general rule the 13 remaining low bits define the size, either
1042  * of the whole object or of the elements in the arrays. While for objects
1043  * the size is already in bytes, for arrays we need to shift, because
1044  * array elements might be smaller than 8 bytes. In case of arrays, we
1045  * use two bits to describe what the additional high bits represents,
1046  * so the default behaviour can handle element sizes less than 2048 bytes.
1047  * The high 16 bits, if 0 it means the object is pointer-free.
1048  * This design should make it easy and fast to skip over ptr-free data.
1049  * The first 4 types should cover >95% of the objects.
1050  * Note that since the size of objects is limited to 64K, larger objects
1051  * will be allocated in the large object heap.
1052  * If we want 4-bytes alignment, we need to put vector and small bitmap
1053  * inside complex.
1054  */
1055 enum {
1056         DESC_TYPE_RUN_LENGTH,   /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
1057         DESC_TYPE_SMALL_BITMAP, /* 16 bits aligned byte size | 16-48 bit bitmap */
1058         DESC_TYPE_STRING,       /* nothing */
1059         DESC_TYPE_COMPLEX,      /* index for bitmap into complex_descriptors */
1060         DESC_TYPE_VECTOR,       /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1061         DESC_TYPE_ARRAY,        /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
1062         DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
1063         DESC_TYPE_COMPLEX_ARR,  /* index for bitmap into complex_descriptors */
1064         /* subtypes for arrays and vectors */
1065         DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value  */
1066         DESC_TYPE_V_REFS,       /* all the array elements are refs */
1067         DESC_TYPE_V_RUN_LEN,    /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
1068         DESC_TYPE_V_BITMAP      /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
1069 };
1070
1071 #define OBJECT_HEADER_WORDS (sizeof(MonoObject)/sizeof(gpointer))
1072 #define LOW_TYPE_BITS 3
1073 #define SMALL_BITMAP_SHIFT 16
1074 #define SMALL_BITMAP_SIZE (GC_BITS_PER_WORD - SMALL_BITMAP_SHIFT)
1075 #define VECTOR_INFO_SHIFT 14
1076 #define VECTOR_ELSIZE_SHIFT 3
1077 #define LARGE_BITMAP_SIZE (GC_BITS_PER_WORD - LOW_TYPE_BITS)
1078 #define MAX_SMALL_SIZE ((1 << SMALL_BITMAP_SHIFT) - 1)
1079 #define SMALL_SIZE_MASK 0xfff8
1080 #define MAX_ELEMENT_SIZE 0x3ff
1081 #define ELEMENT_SIZE_MASK (0x3ff << LOW_TYPE_BITS)
1082 #define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
1083 #define VECTOR_SUBTYPE_REFS    (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
1084 #define VECTOR_SUBTYPE_RUN_LEN (DESC_TYPE_V_RUN_LEN << VECTOR_INFO_SHIFT)
1085 #define VECTOR_SUBTYPE_BITMAP  (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
1086
1087 #define ALLOC_ALIGN 8
1088
1089
1090 /* Root bitmap descriptors are simpler: the lower three bits describe the type
1091  * and we either have 30/62 bitmap bits or nibble-based run-length,
1092  * or a complex descriptor, or a user defined marker function.
1093  */
1094 enum {
1095         ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
1096         ROOT_DESC_BITMAP,
1097         ROOT_DESC_RUN_LEN, 
1098         ROOT_DESC_COMPLEX,
1099         ROOT_DESC_USER,
1100         ROOT_DESC_TYPE_MASK = 0x7,
1101         ROOT_DESC_TYPE_SHIFT = 3,
1102 };
1103
1104 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
1105
1106 #define MAX_USER_DESCRIPTORS 16
1107
1108 static gsize* complex_descriptors = NULL;
1109 static int complex_descriptors_size = 0;
1110 static int complex_descriptors_next = 0;
1111 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
1112 static int user_descriptors_next = 0;
1113
1114 static int
1115 alloc_complex_descriptor (gsize *bitmap, int numbits)
1116 {
1117         int nwords, res, i;
1118
1119         numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
1120         nwords = numbits / GC_BITS_PER_WORD + 1;
1121
1122         LOCK_GC;
1123         res = complex_descriptors_next;
1124         /* linear search, so we don't have duplicates with domain load/unload
1125          * this should not be performance critical or we'd have bigger issues
1126          * (the number and size of complex descriptors should be small).
1127          */
1128         for (i = 0; i < complex_descriptors_next; ) {
1129                 if (complex_descriptors [i] == nwords) {
1130                         int j, found = TRUE;
1131                         for (j = 0; j < nwords - 1; ++j) {
1132                                 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
1133                                         found = FALSE;
1134                                         break;
1135                                 }
1136                         }
1137                         if (found) {
1138                                 UNLOCK_GC;
1139                                 return i;
1140                         }
1141                 }
1142                 i += complex_descriptors [i];
1143         }
1144         if (complex_descriptors_next + nwords > complex_descriptors_size) {
1145                 int new_size = complex_descriptors_size * 2 + nwords;
1146                 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
1147                 complex_descriptors_size = new_size;
1148         }
1149         DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
1150         complex_descriptors_next += nwords;
1151         complex_descriptors [res] = nwords;
1152         for (i = 0; i < nwords - 1; ++i) {
1153                 complex_descriptors [res + 1 + i] = bitmap [i];
1154                 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
1155         }
1156         UNLOCK_GC;
1157         return res;
1158 }
1159
1160 /*
1161  * Descriptor builders.
1162  */
1163 void*
1164 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
1165 {
1166         return (void*) DESC_TYPE_STRING;
1167 }
1168
1169 void*
1170 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
1171 {
1172         int first_set = -1, num_set = 0, last_set = -1, i;
1173         mword desc = 0;
1174         size_t stored_size = obj_size;
1175         stored_size += ALLOC_ALIGN - 1;
1176         stored_size &= ~(ALLOC_ALIGN - 1);
1177         for (i = 0; i < numbits; ++i) {
1178                 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1179                         if (first_set < 0)
1180                                 first_set = i;
1181                         last_set = i;
1182                         num_set++;
1183                 }
1184         }
1185         if (stored_size <= MAX_SMALL_OBJ_SIZE) {
1186                 /* check run-length encoding first: one byte offset, one byte number of pointers
1187                  * on 64 bit archs, we can have 3 runs, just one on 32.
1188                  * It may be better to use nibbles.
1189                  */
1190                 if (first_set < 0) {
1191                         desc = DESC_TYPE_RUN_LENGTH | stored_size;
1192                         DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %zd\n", (void*)desc, stored_size));
1193                         return (void*) desc;
1194                 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
1195                         desc = DESC_TYPE_RUN_LENGTH | stored_size | (first_set << 16) | (num_set << 24);
1196                         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));
1197                         return (void*) desc;
1198                 }
1199                 /* we know the 2-word header is ptr-free */
1200                 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1201                         desc = DESC_TYPE_SMALL_BITMAP | stored_size | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
1202                         DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1203                         return (void*) desc;
1204                 }
1205         }
1206         /* we know the 2-word header is ptr-free */
1207         if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
1208                 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
1209                 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %zd, last set: %d\n", (void*)desc, stored_size, last_set));
1210                 return (void*) desc;
1211         }
1212         /* it's a complex object ... */
1213         desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
1214         return (void*) desc;
1215 }
1216
1217 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
1218 void*
1219 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
1220 {
1221         int first_set = -1, num_set = 0, last_set = -1, i;
1222         mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
1223         for (i = 0; i < numbits; ++i) {
1224                 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
1225                         if (first_set < 0)
1226                                 first_set = i;
1227                         last_set = i;
1228                         num_set++;
1229                 }
1230         }
1231         if (elem_size <= MAX_ELEMENT_SIZE) {
1232                 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
1233                 if (!num_set) {
1234                         return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
1235                 }
1236                 /* Note: we also handle structs with just ref fields */
1237                 if (num_set * sizeof (gpointer) == elem_size) {
1238                         return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
1239                 }
1240                 /* FIXME: try run-len first */
1241                 /* Note: we can't skip the object header here, because it's not present */
1242                 if (last_set <= SMALL_BITMAP_SIZE) {
1243                         return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
1244                 }
1245         }
1246         /* it's am array of complex structs ... */
1247         desc = DESC_TYPE_COMPLEX_ARR;
1248         desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
1249         return (void*) desc;
1250 }
1251
1252 /* Return the bitmap encoded by a descriptor */
1253 gsize*
1254 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1255 {
1256         mword d = (mword)descr;
1257         gsize *bitmap;
1258
1259         switch (d & 0x7) {
1260         case DESC_TYPE_RUN_LENGTH: {            
1261                 int first_set = (d >> 16) & 0xff;
1262                 int num_set = (d >> 24) & 0xff;
1263                 int i;
1264
1265                 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
1266
1267                 for (i = first_set; i < first_set + num_set; ++i)
1268                         bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
1269
1270                 *numbits = first_set + num_set;
1271
1272                 return bitmap;
1273         }
1274         case DESC_TYPE_SMALL_BITMAP:
1275                 bitmap = g_new0 (gsize, 1);
1276
1277                 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
1278
1279             *numbits = GC_BITS_PER_WORD;
1280                 
1281                 return bitmap;
1282         default:
1283                 g_assert_not_reached ();
1284         }
1285 }
1286
1287 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
1288 #define STRING_SIZE(size,str) do {      \
1289                 (size) = sizeof (MonoString) + 2 * mono_string_length ((MonoString*)(str)) + 2; \
1290                 (size) += (ALLOC_ALIGN - 1);    \
1291                 (size) &= ~(ALLOC_ALIGN - 1);   \
1292         } while (0)
1293
1294 #define OBJ_RUN_LEN_SIZE(size,desc,obj) do { \
1295         (size) = (desc) & 0xfff8; \
1296     } while (0)
1297
1298 #define OBJ_BITMAP_SIZE(size,desc,obj) do { \
1299         (size) = (desc) & 0xfff8; \
1300     } while (0)
1301
1302 //#define PREFETCH(addr) __asm__ __volatile__ ("     prefetchnta     %0": : "m"(*(char *)(addr)))
1303 #define PREFETCH(addr)
1304
1305 /* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
1306 #define OBJ_RUN_LEN_FOREACH_PTR(desc,obj)       do {    \
1307                 if ((desc) & 0xffff0000) {      \
1308                         /* there are pointers */        \
1309                         void **_objptr_end;     \
1310                         void **_objptr = (void**)(obj); \
1311                         _objptr += ((desc) >> 16) & 0xff;       \
1312                         _objptr_end = _objptr + (((desc) >> 24) & 0xff);        \
1313                         while (_objptr < _objptr_end) { \
1314                                 HANDLE_PTR (_objptr, (obj));    \
1315                                 _objptr++;      \
1316                         }       \
1317                 }       \
1318         } while (0)
1319
1320 /* a bitmap desc means that there are pointer references or we'd have
1321  * choosen run-length, instead: add an assert to check.
1322  */
1323 #define OBJ_BITMAP_FOREACH_PTR(desc,obj)        do {    \
1324                 /* there are pointers */        \
1325                 void **_objptr = (void**)(obj); \
1326                 gsize _bmap = (desc) >> 16;     \
1327                 _objptr += OBJECT_HEADER_WORDS; \
1328                 while (_bmap) { \
1329                         if ((_bmap & 1)) {      \
1330                                 HANDLE_PTR (_objptr, (obj));    \
1331                         }       \
1332                         _bmap >>= 1;    \
1333                         ++_objptr;      \
1334                 }       \
1335         } while (0)
1336
1337 #define OBJ_LARGE_BITMAP_FOREACH_PTR(vt,obj)    do {    \
1338                 /* there are pointers */        \
1339                 void **_objptr = (void**)(obj); \
1340                 gsize _bmap = (vt)->desc >> LOW_TYPE_BITS;      \
1341                 _objptr += OBJECT_HEADER_WORDS; \
1342                 while (_bmap) { \
1343                         if ((_bmap & 1)) {      \
1344                                 HANDLE_PTR (_objptr, (obj));    \
1345                         }       \
1346                         _bmap >>= 1;    \
1347                         ++_objptr;      \
1348                 }       \
1349         } while (0)
1350
1351 #define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do {    \
1352                 /* there are pointers */        \
1353                 void **_objptr = (void**)(obj); \
1354                 gsize *bitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS);       \
1355                 int bwords = (*bitmap_data) - 1;        \
1356                 void **start_run = _objptr;     \
1357                 bitmap_data++;  \
1358                 if (0) {        \
1359                         MonoObject *myobj = (MonoObject*)obj;   \
1360                         g_print ("found %d at %p (0x%zx): %s.%s\n", bwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name);   \
1361                 }       \
1362                 while (bwords-- > 0) {  \
1363                         gsize _bmap = *bitmap_data++;   \
1364                         _objptr = start_run;    \
1365                         /*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/        \
1366                         while (_bmap) { \
1367                                 if ((_bmap & 1)) {      \
1368                                         HANDLE_PTR (_objptr, (obj));    \
1369                                 }       \
1370                                 _bmap >>= 1;    \
1371                                 ++_objptr;      \
1372                         }       \
1373                         start_run += GC_BITS_PER_WORD;  \
1374                 }       \
1375         } while (0)
1376
1377 /* this one is untested */
1378 #define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj)     do {    \
1379                 /* there are pointers */        \
1380                 gsize *mbitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS);      \
1381                 int mbwords = (*mbitmap_data++) - 1;    \
1382                 int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass);    \
1383                 char *e_start = (char*)(obj) +  G_STRUCT_OFFSET (MonoArray, vector);    \
1384                 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
1385                 if (0) {        \
1386                         MonoObject *myobj = (MonoObject*)start; \
1387                         g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name);  \
1388                 }       \
1389                 while (e_start < e_end) {       \
1390                         void **_objptr = (void**)e_start;       \
1391                         gsize *bitmap_data = mbitmap_data;      \
1392                         unsigned int bwords = mbwords;  \
1393                         while (bwords-- > 0) {  \
1394                                 gsize _bmap = *bitmap_data++;   \
1395                                 void **start_run = _objptr;     \
1396                                 /*g_print ("bitmap: 0x%x\n", _bmap);*/  \
1397                                 while (_bmap) { \
1398                                         if ((_bmap & 1)) {      \
1399                                                 HANDLE_PTR (_objptr, (obj));    \
1400                                         }       \
1401                                         _bmap >>= 1;    \
1402                                         ++_objptr;      \
1403                                 }       \
1404                                 _objptr = start_run + GC_BITS_PER_WORD; \
1405                         }       \
1406                         e_start += el_size;     \
1407                 }       \
1408         } while (0)
1409
1410 #define OBJ_VECTOR_FOREACH_PTR(vt,obj)  do {    \
1411                 /* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */     \
1412                 if ((vt)->desc & 0xffffc000) {  \
1413                         int el_size = ((vt)->desc >> 3) & MAX_ELEMENT_SIZE;     \
1414                         /* there are pointers */        \
1415                         int etype = (vt)->desc & 0xc000;        \
1416                         if (etype == (DESC_TYPE_V_REFS << 14)) {        \
1417                                 void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector));        \
1418                                 void **end_refs = (void**)((char*)p + el_size * mono_array_length ((MonoArray*)(obj))); \
1419                                 /* Note: this code can handle also arrays of struct with only references in them */     \
1420                                 while (p < end_refs) {  \
1421                                         HANDLE_PTR (p, (obj));  \
1422                                         ++p;    \
1423                                 }       \
1424                         } else if (etype == DESC_TYPE_V_RUN_LEN << 14) {        \
1425                                 int offset = ((vt)->desc >> 16) & 0xff; \
1426                                 int num_refs = ((vt)->desc >> 24) & 0xff;       \
1427                                 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector);     \
1428                                 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
1429                                 while (e_start < e_end) {       \
1430                                         void **p = (void**)e_start;     \
1431                                         int i;  \
1432                                         p += offset;    \
1433                                         for (i = 0; i < num_refs; ++i) {        \
1434                                                 HANDLE_PTR (p + i, (obj));      \
1435                                         }       \
1436                                         e_start += el_size;     \
1437                                 }       \
1438                         } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
1439                                 char *e_start = (char*)(obj) +  G_STRUCT_OFFSET (MonoArray, vector);    \
1440                                 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
1441                                 while (e_start < e_end) {       \
1442                                         void **p = (void**)e_start;     \
1443                                         gsize _bmap = (vt)->desc >> 16; \
1444                                         /* Note: there is no object header here to skip */      \
1445                                         while (_bmap) { \
1446                                                 if ((_bmap & 1)) {      \
1447                                                         HANDLE_PTR (p, (obj));  \
1448                                                 }       \
1449                                                 _bmap >>= 1;    \
1450                                                 ++p;    \
1451                                         }       \
1452                                         e_start += el_size;     \
1453                                 }       \
1454                         }       \
1455                 }       \
1456         } while (0)
1457
1458 #define COUNT_OBJECT_TYPES do {                                         \
1459         switch (desc & 0x7) {                                           \
1460         case DESC_TYPE_STRING: type_str++; break;                       \
1461         case DESC_TYPE_RUN_LENGTH: type_rlen++; break;                  \
1462         case DESC_TYPE_ARRAY: case DESC_TYPE_VECTOR: type_vector++; break; \
1463         case DESC_TYPE_SMALL_BITMAP: type_bitmap++; break;              \
1464         case DESC_TYPE_LARGE_BITMAP: type_lbit++; break;                \
1465         case DESC_TYPE_COMPLEX: type_complex++; break;                  \
1466         case DESC_TYPE_COMPLEX_ARR: type_complex++; break;              \
1467         default: g_assert_not_reached ();                               \
1468         }                                                               \
1469         } while (0)
1470
1471
1472 /*
1473  * ######################################################################
1474  * ########  Detecting and removing garbage.
1475  * ######################################################################
1476  * This section of code deals with detecting the objects no longer in use
1477  * and reclaiming the memory.
1478  */
1479
1480 #if 0
1481 static mword new_obj_references = 0;
1482 static mword obj_references_checked = 0;
1483
1484 #undef HANDLE_PTR
1485 #define HANDLE_PTR(ptr,obj)     do {    \
1486                 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
1487                         new_obj_references++;   \
1488                         /*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);*/ \
1489                 } else {        \
1490                         obj_references_checked++;       \
1491                 }       \
1492         } while (0)
1493
1494 static void __attribute__((noinline))
1495 scan_area (char *start, char *end)
1496 {
1497         GCVTable *vt;
1498         int type_str = 0, type_rlen = 0, type_bitmap = 0, type_vector = 0, type_lbit = 0, type_complex = 0;
1499         new_obj_references = 0;
1500         obj_references_checked = 0;
1501         while (start < end) {
1502                 if (!*(void**)start) {
1503                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1504                         continue;
1505                 }
1506                 vt = (GCVTable*)LOAD_VTABLE (start);
1507                 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
1508                 if (0) {
1509                         MonoObject *obj = (MonoObject*)start;
1510                         g_print ("found at %p (0x%zx): %s.%s\n", start, vt->desc, obj->vtable->klass->name_space, obj->vtable->klass->name);
1511                 }
1512
1513 #define SCAN_OBJECT_ACTION COUNT_OBJECT_TYPES
1514 #include "sgen-scan-object.h"
1515         }
1516         /*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);
1517         printf ("\tstrings: %d, runl: %d, vector: %d, bitmaps: %d, lbitmaps: %d, complex: %d\n",
1518                 type_str, type_rlen, type_vector, type_bitmap, type_lbit, type_complex);*/
1519 }
1520 #endif
1521
1522 static gboolean
1523 is_xdomain_ref_allowed (gpointer *ptr, char *obj, MonoDomain *domain)
1524 {
1525         MonoObject *o = (MonoObject*)(obj);
1526         MonoObject *ref = (MonoObject*)*(ptr);
1527         int offset = (char*)(ptr) - (char*)o;
1528
1529         if (o->vtable->klass == mono_defaults.thread_class && offset == G_STRUCT_OFFSET (MonoThread, internal_thread))
1530                 return TRUE;
1531         if (o->vtable->klass == mono_defaults.internal_thread_class && offset == G_STRUCT_OFFSET (MonoInternalThread, current_appcontext))
1532                 return TRUE;
1533         if (mono_class_has_parent (o->vtable->klass, mono_defaults.real_proxy_class) &&
1534                         offset == G_STRUCT_OFFSET (MonoRealProxy, unwrapped_server))
1535                 return TRUE;
1536         /* Thread.cached_culture_info */
1537         if (!strcmp (ref->vtable->klass->name_space, "System.Globalization") &&
1538                         !strcmp (ref->vtable->klass->name, "CultureInfo") &&
1539                         !strcmp(o->vtable->klass->name_space, "System") &&
1540                         !strcmp(o->vtable->klass->name, "Object[]"))
1541                 return TRUE;
1542         /*
1543          *  at System.IO.MemoryStream.InternalConstructor (byte[],int,int,bool,bool) [0x0004d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:121
1544          * at System.IO.MemoryStream..ctor (byte[]) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.IO/MemoryStream.cs:81
1545          * at (wrapper remoting-invoke-with-check) System.IO.MemoryStream..ctor (byte[]) <IL 0x00020, 0xffffffff>
1546          * at System.Runtime.Remoting.Messaging.CADMethodCallMessage.GetArguments () [0x0000d] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/CADMessages.cs:327
1547          * at System.Runtime.Remoting.Messaging.MethodCall..ctor (System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00017] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Messaging/MethodCall.cs:87
1548          * at System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) [0x00018] in /home/schani/Work/novell/trunk/mcs/class/corlib/System/AppDomain.cs:1213
1549          * at (wrapper remoting-invoke-with-check) System.AppDomain.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage,byte[]&,System.Runtime.Remoting.Messaging.CADMethodReturnMessage&) <IL 0x0003d, 0xffffffff>
1550          * at System.Runtime.Remoting.Channels.CrossAppDomainSink.ProcessMessageInDomain (byte[],System.Runtime.Remoting.Messaging.CADMethodCallMessage) [0x00008] in /home/schani/Work/novell/trunk/mcs/class/corlib/System.Runtime.Remoting.Channels/CrossAppDomainChannel.cs:198
1551          * at (wrapper runtime-invoke) object.runtime_invoke_CrossAppDomainSink/ProcessMessageRes_object_object (object,intptr,intptr,intptr) <IL 0x0004c, 0xffffffff>
1552          */
1553         if (!strcmp (ref->vtable->klass->name_space, "System") &&
1554                         !strcmp (ref->vtable->klass->name, "Byte[]") &&
1555                         !strcmp (o->vtable->klass->name_space, "System.IO") &&
1556                         !strcmp (o->vtable->klass->name, "MemoryStream"))
1557                 return TRUE;
1558         /* append_job() in threadpool.c */
1559         if (!strcmp (ref->vtable->klass->name_space, "System.Runtime.Remoting.Messaging") &&
1560                         !strcmp (ref->vtable->klass->name, "AsyncResult") &&
1561                         !strcmp (o->vtable->klass->name_space, "System") &&
1562                         !strcmp (o->vtable->klass->name, "Object[]") &&
1563                         mono_thread_pool_is_queue_array ((MonoArray*) o))
1564                 return TRUE;
1565         return FALSE;
1566 }
1567
1568 static void
1569 check_reference_for_xdomain (gpointer *ptr, char *obj, MonoDomain *domain)
1570 {
1571         MonoObject *o = (MonoObject*)(obj);
1572         MonoObject *ref = (MonoObject*)*(ptr);
1573         int offset = (char*)(ptr) - (char*)o;
1574         MonoClass *class;
1575         MonoClassField *field;
1576         char *str;
1577
1578         if (!ref || ref->vtable->domain == domain)
1579                 return;
1580         if (is_xdomain_ref_allowed (ptr, obj, domain))
1581                 return;
1582
1583         field = NULL;
1584         for (class = o->vtable->klass; class; class = class->parent) {
1585                 int i;
1586
1587                 for (i = 0; i < class->field.count; ++i) {
1588                         if (class->fields[i].offset == offset) {
1589                                 field = &class->fields[i];
1590                                 break;
1591                         }
1592                 }
1593                 if (field)
1594                         break;
1595         }
1596
1597         if (ref->vtable->klass == mono_defaults.string_class)
1598                 str = mono_string_to_utf8 ((MonoString*)ref);
1599         else
1600                 str = NULL;
1601         g_print ("xdomain reference in %p (%s.%s) at offset %d (%s) to %p (%s.%s) (%s)  -  pointed to by:\n",
1602                         o, o->vtable->klass->name_space, o->vtable->klass->name,
1603                         offset, field ? field->name : "",
1604                         ref, ref->vtable->klass->name_space, ref->vtable->klass->name, str ? str : "");
1605         mono_gc_scan_for_specific_ref (o);
1606         if (str)
1607                 g_free (str);
1608 }
1609
1610 #undef HANDLE_PTR
1611 #define HANDLE_PTR(ptr,obj)     check_reference_for_xdomain ((ptr), (obj), domain)
1612
1613 static char*
1614 scan_object_for_xdomain_refs (char *start)
1615 {
1616         MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
1617
1618         #include "sgen-scan-object.h"
1619
1620         return start;
1621 }
1622
1623 static void
1624 scan_area_for_xdomain_refs (char *start, char *end)
1625 {
1626         while (start < end) {
1627                 if (!*(void**)start) {
1628                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1629                         continue;
1630                 }
1631
1632                 start = scan_object_for_xdomain_refs (start);
1633         }
1634 }
1635
1636 #undef HANDLE_PTR
1637 #define HANDLE_PTR(ptr,obj) do {                \
1638         if ((MonoObject*)*(ptr) == key) {       \
1639         g_print ("found ref to %p in object %p (%s) at offset %zd\n",   \
1640                         key, (obj), safe_name ((obj)), ((char*)(ptr) - (char*)(obj))); \
1641         }                                                               \
1642         } while (0)
1643
1644 static char*
1645 scan_object_for_specific_ref (char *start, MonoObject *key)
1646 {
1647         #include "sgen-scan-object.h"
1648
1649         return start;
1650 }
1651
1652 static void
1653 scan_area_for_specific_ref (char *start, char *end, MonoObject *key)
1654 {
1655         while (start < end) {
1656                 if (!*(void**)start) {
1657                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1658                         continue;
1659                 }
1660
1661                 start = scan_object_for_specific_ref (start, key);
1662         }
1663 }
1664
1665 static void
1666 scan_pinned_object_for_specific_ref_callback (PinnedChunk *chunk, char *obj, size_t size, MonoObject *key)
1667 {
1668         scan_object_for_specific_ref (obj, key);
1669 }
1670
1671 static void
1672 check_root_obj_specific_ref (RootRecord *root, MonoObject *key, MonoObject *obj)
1673 {
1674         if (key != obj)
1675                 return;
1676         g_print ("found ref to %p in root record %p\n", key, root);
1677 }
1678
1679 static MonoObject *check_key = NULL;
1680 static RootRecord *check_root = NULL;
1681
1682 static void
1683 check_root_obj_specific_ref_from_marker (void **obj)
1684 {
1685         check_root_obj_specific_ref (check_root, check_key, *obj);
1686 }
1687
1688 static void
1689 scan_roots_for_specific_ref (MonoObject *key, int root_type)
1690 {
1691         int i;
1692         RootRecord *root;
1693         check_key = key;
1694         for (i = 0; i < roots_hash_size [root_type]; ++i) {
1695                 for (root = roots_hash [root_type][i]; root; root = root->next) {
1696                         void **start_root = (void**)root->start_root;
1697                         mword desc = root->root_desc;
1698
1699                         check_root = root;
1700
1701                         switch (desc & ROOT_DESC_TYPE_MASK) {
1702                         case ROOT_DESC_BITMAP:
1703                                 desc >>= ROOT_DESC_TYPE_SHIFT;
1704                                 while (desc) {
1705                                         if (desc & 1)
1706                                                 check_root_obj_specific_ref (root, key, *start_root);
1707                                         desc >>= 1;
1708                                         start_root++;
1709                                 }
1710                                 return;
1711                         case ROOT_DESC_COMPLEX: {
1712                                 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1713                                 int bwords = (*bitmap_data) - 1;
1714                                 void **start_run = start_root;
1715                                 bitmap_data++;
1716                                 while (bwords-- > 0) {
1717                                         gsize bmap = *bitmap_data++;
1718                                         void **objptr = start_run;
1719                                         while (bmap) {
1720                                                 if (bmap & 1)
1721                                                         check_root_obj_specific_ref (root, key, *objptr);
1722                                                 bmap >>= 1;
1723                                                 ++objptr;
1724                                         }
1725                                         start_run += GC_BITS_PER_WORD;
1726                                 }
1727                                 break;
1728                         }
1729                         case ROOT_DESC_USER: {
1730                                 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1731                                 marker (start_root, check_root_obj_specific_ref_from_marker);
1732                                 break;
1733                         }
1734                         case ROOT_DESC_RUN_LEN:
1735                                 g_assert_not_reached ();
1736                         default:
1737                                 g_assert_not_reached ();
1738                         }
1739                 }
1740         }
1741         check_key = NULL;
1742         check_root = NULL;
1743 }
1744
1745 void
1746 mono_gc_scan_for_specific_ref (MonoObject *key)
1747 {
1748         GCMemSection *section;
1749         LOSObject *bigobj;
1750         RootRecord *root;
1751         int i;
1752
1753         for (section = section_list; section; section = section->block.next)
1754                 scan_area_for_specific_ref (section->data, section->end_data, key);
1755
1756         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1757                 scan_object_for_specific_ref (bigobj->data, key);
1758
1759         scan_pinned_objects ((ScanPinnedObjectCallbackFunc)scan_pinned_object_for_specific_ref_callback, key);
1760
1761         scan_roots_for_specific_ref (key, ROOT_TYPE_NORMAL);
1762         scan_roots_for_specific_ref (key, ROOT_TYPE_WBARRIER);
1763
1764         for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
1765                 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
1766                         void **ptr = (void**)root->start_root;
1767
1768                         while (ptr < (void**)root->end_root) {
1769                                 check_root_obj_specific_ref (root, *ptr, key);
1770                                 ++ptr;
1771                         }
1772                 }
1773         }
1774 }
1775
1776 //#define BINARY_PROTOCOL
1777 #include "sgen-protocol.c"
1778
1779 static gboolean
1780 need_remove_object_for_domain (char *start, MonoDomain *domain)
1781 {
1782         if (mono_object_domain (start) == domain) {
1783                 DEBUG (4, fprintf (gc_debug_file, "Need to cleanup object %p\n", start));
1784                 binary_protocol_cleanup (start, (gpointer)LOAD_VTABLE (start), safe_object_get_size ((MonoObject*)start));
1785                 return TRUE;
1786         }
1787         return FALSE;
1788 }
1789
1790 static void
1791 process_object_for_domain_clearing (char *start, MonoDomain *domain)
1792 {
1793         GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
1794         if (vt->klass == mono_defaults.internal_thread_class)
1795                 g_assert (mono_object_domain (start) == mono_get_root_domain ());
1796         /* The object could be a proxy for an object in the domain
1797            we're deleting. */
1798         if (mono_class_has_parent (vt->klass, mono_defaults.real_proxy_class)) {
1799                 MonoObject *server = ((MonoRealProxy*)start)->unwrapped_server;
1800
1801                 /* The server could already have been zeroed out, so
1802                    we need to check for that, too. */
1803                 if (server && (!LOAD_VTABLE (server) || mono_object_domain (server) == domain)) {
1804                         DEBUG (4, fprintf (gc_debug_file, "Cleaning up remote pointer in %p to object %p\n",
1805                                         start, server));
1806                         ((MonoRealProxy*)start)->unwrapped_server = NULL;
1807                 }
1808         }
1809 }
1810
1811 static void __attribute__((noinline))
1812 scan_area_for_domain (MonoDomain *domain, char *start, char *end)
1813 {
1814         GCVTable *vt;
1815         gboolean remove;
1816
1817         while (start < end) {
1818                 if (!*(void**)start) {
1819                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1820                         continue;
1821                 }
1822                 vt = (GCVTable*)LOAD_VTABLE (start);
1823                 process_object_for_domain_clearing (start, domain);
1824                 remove = need_remove_object_for_domain (start, domain);
1825                 if (remove && ((MonoObject*)start)->synchronisation) {
1826                         void **dislink = mono_monitor_get_object_monitor_weak_link ((MonoObject*)start);
1827                         if (dislink)
1828                                 mono_gc_register_disappearing_link (NULL, dislink, FALSE);
1829                 }
1830
1831 #define SCAN_OBJECT_NOSCAN
1832 #define SCAN_OBJECT_ACTION do {                                         \
1833                         if (remove) memset (start, 0, skip_size);       \
1834                 } while (0)
1835 #include "sgen-scan-object.h"
1836         }
1837 }
1838
1839 static MonoDomain *check_domain = NULL;
1840
1841 static void
1842 check_obj_not_in_domain (void **o)
1843 {
1844         g_assert (((MonoObject*)(*o))->vtable->domain != check_domain);
1845 }
1846
1847 static void
1848 scan_for_registered_roots_in_domain (MonoDomain *domain, int root_type)
1849 {
1850         int i;
1851         RootRecord *root;
1852         check_domain = domain;
1853         for (i = 0; i < roots_hash_size [root_type]; ++i) {
1854                 for (root = roots_hash [root_type][i]; root; root = root->next) {
1855                         void **start_root = (void**)root->start_root;
1856                         mword desc = root->root_desc;
1857
1858                         /* The MonoDomain struct is allowed to hold
1859                            references to objects in its own domain. */
1860                         if (start_root == (void**)domain)
1861                                 continue;
1862
1863                         switch (desc & ROOT_DESC_TYPE_MASK) {
1864                         case ROOT_DESC_BITMAP:
1865                                 desc >>= ROOT_DESC_TYPE_SHIFT;
1866                                 while (desc) {
1867                                         if ((desc & 1) && *start_root)
1868                                                 check_obj_not_in_domain (*start_root);
1869                                         desc >>= 1;
1870                                         start_root++;
1871                                 }
1872                                 break;
1873                         case ROOT_DESC_COMPLEX: {
1874                                 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
1875                                 int bwords = (*bitmap_data) - 1;
1876                                 void **start_run = start_root;
1877                                 bitmap_data++;
1878                                 while (bwords-- > 0) {
1879                                         gsize bmap = *bitmap_data++;
1880                                         void **objptr = start_run;
1881                                         while (bmap) {
1882                                                 if ((bmap & 1) && *objptr)
1883                                                         check_obj_not_in_domain (*objptr);
1884                                                 bmap >>= 1;
1885                                                 ++objptr;
1886                                         }
1887                                         start_run += GC_BITS_PER_WORD;
1888                                 }
1889                                 break;
1890                         }
1891                         case ROOT_DESC_USER: {
1892                                 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
1893                                 marker (start_root, check_obj_not_in_domain);
1894                                 break;
1895                         }
1896                         case ROOT_DESC_RUN_LEN:
1897                                 g_assert_not_reached ();
1898                         default:
1899                                 g_assert_not_reached ();
1900                         }
1901                 }
1902         }
1903         check_domain = NULL;
1904 }
1905
1906 static void
1907 clear_domain_process_pinned_object_callback (PinnedChunk *chunk, char *obj, size_t size, MonoDomain *domain)
1908 {
1909         process_object_for_domain_clearing (obj, domain);
1910 }
1911
1912 static void
1913 clear_domain_free_pinned_object_callback (PinnedChunk *chunk, char *obj, size_t size, MonoDomain *domain)
1914 {
1915         if (need_remove_object_for_domain (obj, domain))
1916                 free_pinned_object (chunk, obj, size);
1917 }
1918
1919 static void
1920 scan_pinned_object_for_xdomain_refs_callback (PinnedChunk *chunk, char *obj, size_t size, gpointer dummy)
1921 {
1922         scan_object_for_xdomain_refs (obj);
1923 }
1924
1925 static void
1926 check_for_xdomain_refs (void)
1927 {
1928         GCMemSection *section;
1929         LOSObject *bigobj;
1930
1931         for (section = section_list; section; section = section->block.next)
1932                 scan_area_for_xdomain_refs (section->data, section->end_data);
1933
1934         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1935                 scan_object_for_xdomain_refs (bigobj->data);
1936
1937         scan_pinned_objects (scan_pinned_object_for_xdomain_refs_callback, NULL);
1938 }
1939
1940 /*
1941  * When appdomains are unloaded we can easily remove objects that have finalizers,
1942  * but all the others could still be present in random places on the heap.
1943  * We need a sweep to get rid of them even though it's going to be costly
1944  * with big heaps.
1945  * The reason we need to remove them is because we access the vtable and class
1946  * structures to know the object size and the reference bitmap: once the domain is
1947  * unloaded the point to random memory.
1948  */
1949 void
1950 mono_gc_clear_domain (MonoDomain * domain)
1951 {
1952         GCMemSection *section;
1953         LOSObject *bigobj, *prev;
1954         Fragment *frag;
1955         int i;
1956
1957         LOCK_GC;
1958         /* Clear all remaining nursery fragments */
1959         if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
1960                 g_assert (nursery_next <= nursery_frag_real_end);
1961                 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
1962                 for (frag = nursery_fragments; frag; frag = frag->next) {
1963                         memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
1964                 }
1965         }
1966
1967         if (xdomain_checks && domain != mono_get_root_domain ()) {
1968                 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_NORMAL);
1969                 scan_for_registered_roots_in_domain (domain, ROOT_TYPE_WBARRIER);
1970                 check_for_xdomain_refs ();
1971         }
1972
1973         for (section = section_list; section; section = section->block.next) {
1974                 scan_area_for_domain (domain, section->data, section->end_data);
1975         }
1976
1977         /* We need two passes over pinned and large objects because
1978            freeing such an object gives its memory back to the OS (in
1979            the case of large objects) or obliterates its vtable
1980            (pinned objects), but we might need to dereference a
1981            pointer from an object to another object if the first
1982            object is a proxy. */
1983         scan_pinned_objects ((ScanPinnedObjectCallbackFunc)clear_domain_process_pinned_object_callback, domain);
1984         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
1985                 process_object_for_domain_clearing (bigobj->data, domain);
1986
1987         prev = NULL;
1988         for (bigobj = los_object_list; bigobj;) {
1989                 if (need_remove_object_for_domain (bigobj->data, domain)) {
1990                         LOSObject *to_free = bigobj;
1991                         if (prev)
1992                                 prev->next = bigobj->next;
1993                         else
1994                                 los_object_list = bigobj->next;
1995                         bigobj = bigobj->next;
1996                         DEBUG (4, fprintf (gc_debug_file, "Freeing large object %p\n",
1997                                         bigobj->data));
1998                         free_large_object (to_free);
1999                         continue;
2000                 }
2001                 prev = bigobj;
2002                 bigobj = bigobj->next;
2003         }
2004         scan_pinned_objects ((ScanPinnedObjectCallbackFunc)clear_domain_free_pinned_object_callback, domain);
2005
2006         for (i = GENERATION_NURSERY; i < GENERATION_MAX; ++i)
2007                 null_links_for_domain (domain, i);
2008
2009         UNLOCK_GC;
2010 }
2011
2012 /*
2013  * add_to_global_remset:
2014  *
2015  *   The global remset contains locations which point into newspace after
2016  * a minor collection. This can happen if the objects they point to are pinned.
2017  */
2018 static void
2019 add_to_global_remset (gpointer ptr, gboolean root)
2020 {
2021         RememberedSet *rs;
2022
2023         DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
2024         binary_protocol_global_remset (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
2025
2026         g_assert (!root);
2027         g_assert (!ptr_in_nursery (ptr) && ptr_in_nursery (*(gpointer*)ptr));
2028
2029         HEAVY_STAT (++stat_global_remsets_added);
2030
2031         /* 
2032          * FIXME: If an object remains pinned, we need to add it at every minor collection.
2033          * To avoid uncontrolled growth of the global remset, only add each pointer once.
2034          */
2035         if (global_remset->store_next + 3 < global_remset->end_set) {
2036                 if (root) {
2037                         *(global_remset->store_next++) = (mword)ptr | REMSET_OTHER;
2038                         *(global_remset->store_next++) = (mword)REMSET_ROOT_LOCATION;
2039                 } else {
2040                         *(global_remset->store_next++) = (mword)ptr;
2041                 }
2042                 return;
2043         }
2044         rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
2045         rs->next = global_remset;
2046         global_remset = rs;
2047         if (root) {
2048                 *(global_remset->store_next++) = (mword)ptr | REMSET_OTHER;
2049                 *(global_remset->store_next++) = (mword)REMSET_ROOT_LOCATION;
2050         } else {
2051                 *(global_remset->store_next++) = (mword)ptr;
2052         }
2053
2054         {
2055                 int global_rs_size = 0;
2056
2057                 for (rs = global_remset; rs; rs = rs->next) {
2058                         global_rs_size += rs->store_next - rs->data;
2059                 }
2060                 DEBUG (4, fprintf (gc_debug_file, "Global remset now has size %d\n", global_rs_size));
2061         }
2062 }
2063
2064 #define MOVED_OBJECTS_NUM 64
2065 static void *moved_objects [MOVED_OBJECTS_NUM];
2066 static int moved_objects_idx = 0;
2067
2068 #include "sgen-gray.c"
2069
2070 /*
2071  * This is how the copying happens from the nursery to the old generation.
2072  * We assume that at this time all the pinned objects have been identified and
2073  * marked as such.
2074  * We run scan_object() for each pinned object so that each referenced
2075  * objects if possible are copied. The new gray objects created can have
2076  * scan_object() run on them right away, too.
2077  * Then we run copy_object() for the precisely tracked roots. At this point
2078  * all the roots are either gray or black. We run scan_object() on the gray
2079  * objects until no more gray objects are created.
2080  * At the end of the process we walk again the pinned list and we unmark
2081  * the pinned flag. As we go we also create the list of free space for use
2082  * in the next allocation runs.
2083  *
2084  * We need to remember objects from the old generation that point to the new one
2085  * (or just addresses?).
2086  *
2087  * copy_object could be made into a macro once debugged (use inline for now).
2088  */
2089
2090 static void __attribute__((noinline))
2091 copy_object (void **obj_slot, char *from_space_start, char *from_space_end)
2092 {
2093         static const void *copy_labels [] = { &&LAB_0, &&LAB_1, &&LAB_2, &&LAB_3, &&LAB_4, &&LAB_5, &&LAB_6, &&LAB_7, &&LAB_8 };
2094
2095         char *forwarded;
2096         char *obj = *obj_slot;
2097         mword objsize;
2098         MonoVTable *vt;
2099
2100         HEAVY_STAT (++num_copy_object_called);
2101
2102         if (!(obj >= from_space_start && obj < from_space_end)) {
2103                 DEBUG (9, fprintf (gc_debug_file, "Not copying %p because it's not in from space (%p-%p)\n",
2104                                                 obj, from_space_start, from_space_end));
2105                 HEAVY_STAT (++stat_copy_object_failed_from_space);
2106                 return;
2107         }
2108
2109         DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p from %p", obj, obj_slot));
2110
2111         /*
2112          * obj must belong to one of:
2113          *
2114          * 1. the nursery
2115          * 2. the LOS
2116          * 3. a pinned chunk
2117          * 4. a non-to-space section of the major heap
2118          * 5. a to-space section of the major heap
2119          *
2120          * In addition, objects in 1, 2 and 4 might also be pinned.
2121          * Objects in 1 and 4 might be forwarded.
2122          *
2123          * Before we can copy the object we must make sure that we are
2124          * allowed to, i.e. that the object not pinned, not already
2125          * forwarded and doesn't belong to the LOS, a pinned chunk, or
2126          * a to-space section.
2127          *
2128          * We are usually called for to-space objects (5) when we have
2129          * two remset entries for the same reference.  The first entry
2130          * copies the object and updates the reference and the second
2131          * calls us with the updated reference that points into
2132          * to-space.  There might also be other circumstances where we
2133          * get to-space objects.
2134          */
2135
2136         if ((forwarded = object_is_forwarded (obj))) {
2137                 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2138                 DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
2139                 HEAVY_STAT (++stat_copy_object_failed_forwarded);
2140                 *obj_slot = forwarded;
2141                 return;
2142         }
2143         if (object_is_pinned (obj)) {
2144                 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
2145                 DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
2146                 HEAVY_STAT (++stat_copy_object_failed_pinned);
2147                 return;
2148         }
2149
2150         objsize = safe_object_get_size ((MonoObject*)obj);
2151         objsize += ALLOC_ALIGN - 1;
2152         objsize &= ~(ALLOC_ALIGN - 1);
2153
2154         if (ptr_in_nursery (obj))
2155                 goto copy;
2156
2157         /*
2158          * At this point we know obj is not pinned, not forwarded and
2159          * belongs to 2, 3, 4, or 5.
2160          *
2161          * LOS object (2) are simple, at least until we always follow
2162          * the rule: if objsize > MAX_SMALL_OBJ_SIZE, pin the object
2163          * and return it.  At the end of major collections, we walk
2164          * the los list and if the object is pinned, it is marked,
2165          * otherwise it can be freed.
2166          *
2167          * Pinned chunks (3) and major heap sections (4, 5) both
2168          * reside in blocks, which are always aligned, so once we've
2169          * eliminated LOS objects, we can just access the block and
2170          * see whether it's a pinned chunk or a major heap section.
2171          */
2172         if (G_UNLIKELY (objsize > MAX_SMALL_OBJ_SIZE || obj_is_from_pinned_alloc (obj))) {
2173                 DEBUG (9, fprintf (gc_debug_file, " (marked LOS/Pinned %p (%s), size: %zd)\n", obj, safe_name (obj), objsize));
2174                 binary_protocol_pin (obj, (gpointer)LOAD_VTABLE (obj), safe_object_get_size ((MonoObject*)obj));
2175                 pin_object (obj);
2176                 HEAVY_STAT (++stat_copy_object_failed_large_pinned);
2177                 return;
2178         }
2179
2180         /*
2181          * Now we know the object is in a major heap section.  All we
2182          * need to do is check whether it's already in to-space (5) or
2183          * not (4).
2184          */
2185         if (MAJOR_SECTION_FOR_OBJECT (obj)->is_to_space) {
2186                 DEBUG (9, g_assert (objsize <= MAX_SMALL_OBJ_SIZE));
2187                 DEBUG (9, fprintf (gc_debug_file, " (already copied)\n"));
2188                 HEAVY_STAT (++stat_copy_object_failed_to_space);
2189                 return;
2190         }
2191
2192  copy:
2193         DEBUG (9, fprintf (gc_debug_file, " (to %p, %s size: %zd)\n", to_space_bumper, ((MonoObject*)obj)->vtable->klass->name, objsize));
2194         binary_protocol_copy (obj, to_space_bumper, ((MonoObject*)obj)->vtable, objsize);
2195
2196         HEAVY_STAT (++num_objects_copied);
2197
2198         /* Make sure we have enough space available */
2199         if (to_space_bumper + objsize > to_space_top) {
2200                 to_space_expand ();
2201                 DEBUG (8, g_assert (to_space_bumper + objsize <= to_space_top));
2202         }
2203
2204         if (objsize <= sizeof (gpointer) * 8) {
2205                 mword *dest = (mword*)to_space_bumper;
2206                 goto *copy_labels [objsize / sizeof (gpointer)];
2207         LAB_8:
2208                 (dest) [7] = ((mword*)obj) [7];
2209         LAB_7:
2210                 (dest) [6] = ((mword*)obj) [6];
2211         LAB_6:
2212                 (dest) [5] = ((mword*)obj) [5];
2213         LAB_5:
2214                 (dest) [4] = ((mword*)obj) [4];
2215         LAB_4:
2216                 (dest) [3] = ((mword*)obj) [3];
2217         LAB_3:
2218                 (dest) [2] = ((mword*)obj) [2];
2219         LAB_2:
2220                 (dest) [1] = ((mword*)obj) [1];
2221         LAB_1:
2222                 (dest) [0] = ((mword*)obj) [0];
2223         LAB_0:
2224                 ;
2225         } else {
2226 #if 0
2227                 {
2228                         int ecx;
2229                         char* esi = obj;
2230                         char* edi = to_space_bumper;
2231                         __asm__ __volatile__(
2232                                 "rep; movsl"
2233                                 : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
2234                                 : "0" (objsize/4), "1" (edi),"2" (esi)
2235                                 : "memory"
2236                                              );
2237                 }
2238 #else
2239                 memcpy (to_space_bumper, obj, objsize);
2240 #endif
2241         }
2242         /* adjust array->bounds */
2243         vt = ((MonoObject*)obj)->vtable;
2244         DEBUG (9, g_assert (vt->gc_descr));
2245         if (G_UNLIKELY (vt->rank && ((MonoArray*)obj)->bounds)) {
2246                 MonoArray *array = (MonoArray*)to_space_bumper;
2247                 array->bounds = (MonoArrayBounds*)((char*)to_space_bumper + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
2248                 DEBUG (9, fprintf (gc_debug_file, "Array instance %p: size: %zd, rank: %d, length: %d\n", array, objsize, vt->rank, mono_array_length (array)));
2249         }
2250         /* set the forwarding pointer */
2251         forward_object (obj, to_space_bumper);
2252         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
2253                 if (moved_objects_idx == MOVED_OBJECTS_NUM) {
2254                         mono_profiler_gc_moves (moved_objects, moved_objects_idx);
2255                         moved_objects_idx = 0;
2256                 }
2257                 moved_objects [moved_objects_idx++] = obj;
2258                 moved_objects [moved_objects_idx++] = to_space_bumper;
2259         }
2260         obj = to_space_bumper;
2261         to_space_section->scan_starts [((char*)obj - (char*)to_space_section->data)/SCAN_START_SIZE] = obj;
2262         to_space_bumper += objsize;
2263         DEBUG (9, fprintf (gc_debug_file, "Enqueuing gray object %p (%s)\n", obj, safe_name (obj)));
2264         GRAY_OBJECT_ENQUEUE (obj);
2265         DEBUG (8, g_assert (to_space_bumper <= to_space_top));
2266         *obj_slot = obj;
2267 }
2268
2269 /*
2270  * This is a variant of copy_object() that can be used for object locations
2271  * not in the heap. Ideally all the callers should later be changed to call
2272  * the real copy_object() that takes the location of the pointer.
2273  */
2274 static char*
2275 copy_object_no_heap (char *obj, char *from_space_start, char *from_space_end)
2276 {
2277         void *obj_ptr = (void*)obj;
2278         copy_object (&obj_ptr, from_space_start, from_space_end);
2279         return obj_ptr;
2280 }
2281
2282 #undef HANDLE_PTR
2283 #define HANDLE_PTR(ptr,obj)     do {    \
2284                 void *__old = *(ptr);   \
2285                 void *__copy;           \
2286                 if (__old) {    \
2287                         copy_object ((ptr), from_start, from_end);      \
2288                         __copy = *(ptr);        \
2289                         DEBUG (9, if (__old != __copy) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old));     \
2290                         if (G_UNLIKELY (ptr_in_nursery (__copy) && !ptr_in_nursery ((ptr)))) \
2291                                 add_to_global_remset ((ptr), FALSE);                                                    \
2292                 }       \
2293         } while (0)
2294
2295 /*
2296  * Scan the object pointed to by @start for references to
2297  * other objects between @from_start and @from_end and copy
2298  * them to the gray_objects area.
2299  * Returns a pointer to the end of the object.
2300  */
2301 static char*
2302 scan_object (char *start, char* from_start, char* from_end)
2303 {
2304 #include "sgen-scan-object.h"
2305
2306         return start;
2307 }
2308
2309 /*
2310  * drain_gray_stack:
2311  *
2312  *   Scan objects in the gray stack until the stack is empty. This should be called
2313  * frequently after each object is copied, to achieve better locality and cache
2314  * usage.
2315  */
2316 static void inline
2317 drain_gray_stack (char *start_addr, char *end_addr)
2318 {
2319         char *obj;
2320
2321         for (;;) {
2322                 GRAY_OBJECT_DEQUEUE (obj);
2323                 if (!obj)
2324                         break;
2325                 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", obj, safe_name (obj)));
2326                 scan_object (obj, start_addr, end_addr);
2327         }
2328 }
2329
2330 /*
2331  * scan_vtype:
2332  *
2333  * Scan the valuetype pointed to by START, described by DESC for references to
2334  * other objects between @from_start and @from_end and copy them to the gray_objects area.
2335  * Returns a pointer to the end of the object.
2336  */
2337 static char*
2338 scan_vtype (char *start, mword desc, char* from_start, char* from_end)
2339 {
2340         size_t skip_size;
2341
2342         /* The descriptors include info about the MonoObject header as well */
2343         start -= sizeof (MonoObject);
2344
2345         switch (desc & 0x7) {
2346         case DESC_TYPE_RUN_LENGTH:
2347                 OBJ_RUN_LEN_FOREACH_PTR (desc,start);
2348                 OBJ_RUN_LEN_SIZE (skip_size, desc, start);
2349                 g_assert (skip_size);
2350                 return start + skip_size;
2351         case DESC_TYPE_SMALL_BITMAP:
2352                 OBJ_BITMAP_FOREACH_PTR (desc,start);
2353                 OBJ_BITMAP_SIZE (skip_size, desc, start);
2354                 return start + skip_size;
2355         case DESC_TYPE_LARGE_BITMAP:
2356         case DESC_TYPE_COMPLEX:
2357                 // FIXME:
2358                 g_assert_not_reached ();
2359                 break;
2360         default:
2361                 // The other descriptors can't happen with vtypes
2362                 g_assert_not_reached ();
2363                 break;
2364         }
2365         return NULL;
2366 }
2367
2368 #include "sgen-pinning-stats.c"
2369
2370 /*
2371  * Addresses from start to end are already sorted. This function finds
2372  * the object header for each address and pins the object. The
2373  * addresses must be inside the passed section.  The (start of the)
2374  * address array is overwritten with the addresses of the actually
2375  * pinned objects.  Return the number of pinned objects.
2376  */
2377 static int
2378 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery)
2379 {
2380         void *last = NULL;
2381         int count = 0;
2382         void *search_start;
2383         void *last_obj = NULL;
2384         size_t last_obj_size = 0;
2385         void *addr;
2386         int idx;
2387         void **definitely_pinned = start;
2388         while (start < end) {
2389                 addr = *start;
2390                 /* the range check should be reduntant */
2391                 if (addr != last && addr >= start_nursery && addr < end_nursery) {
2392                         DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
2393                         /* multiple pointers to the same object */
2394                         if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
2395                                 start++;
2396                                 continue;
2397                         }
2398                         idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
2399                         g_assert (idx < section->num_scan_start);
2400                         search_start = (void*)section->scan_starts [idx];
2401                         if (!search_start || search_start > addr) {
2402                                 while (idx) {
2403                                         --idx;
2404                                         search_start = section->scan_starts [idx];
2405                                         if (search_start && search_start <= addr)
2406                                                 break;
2407                                 }
2408                                 if (!search_start || search_start > addr)
2409                                         search_start = start_nursery;
2410                         }
2411                         if (search_start < last_obj)
2412                                 search_start = (char*)last_obj + last_obj_size;
2413                         /* now addr should be in an object a short distance from search_start
2414                          * Note that search_start must point to zeroed mem or point to an object.
2415                          */
2416                         do {
2417                                 if (!*(void**)search_start) {
2418                                         mword p = (mword)search_start;
2419                                         p += sizeof (gpointer);
2420                                         p += ALLOC_ALIGN - 1;
2421                                         p &= ~(ALLOC_ALIGN - 1);
2422                                         search_start = (void*)p;
2423                                         continue;
2424                                 }
2425                                 last_obj = search_start;
2426                                 last_obj_size = safe_object_get_size ((MonoObject*)search_start);
2427                                 last_obj_size += ALLOC_ALIGN - 1;
2428                                 last_obj_size &= ~(ALLOC_ALIGN - 1);
2429                                 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %zd\n", last_obj, safe_name (last_obj), last_obj_size));
2430                                 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
2431                                         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));
2432                                         binary_protocol_pin (search_start, (gpointer)LOAD_VTABLE (search_start), safe_object_get_size (search_start));
2433                                         pin_object (search_start);
2434                                         if (heap_dump_file)
2435                                                 pin_stats_register_object (search_start, last_obj_size);
2436                                         definitely_pinned [count] = search_start;
2437                                         count++;
2438                                         break;
2439                                 }
2440                                 /* skip to the next object */
2441                                 search_start = (void*)((char*)search_start + last_obj_size);
2442                         } while (search_start <= addr);
2443                         /* we either pinned the correct object or we ignored the addr because
2444                          * it points to unused zeroed memory.
2445                          */
2446                         last = addr;
2447                 }
2448                 start++;
2449         }
2450         //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
2451         return count;
2452 }
2453
2454 static void** pin_queue;
2455 static int pin_queue_size = 0;
2456 static int next_pin_slot = 0;
2457
2458 static int
2459 new_gap (int gap)
2460 {
2461         gap = (gap * 10) / 13;
2462         if (gap == 9 || gap == 10)
2463                 return 11;
2464         if (gap < 1)
2465                 return 1;
2466         return gap;
2467 }
2468
2469 #if 0
2470 static int
2471 compare_addr (const void *a, const void *b)
2472 {
2473         return *(const void **)a - *(const void **)b;
2474 }
2475 #endif
2476
2477 /* sort the addresses in array in increasing order */
2478 static void
2479 sort_addresses (void **array, int size)
2480 {
2481         /*
2482          * qsort is slower as predicted.
2483          * qsort (array, size, sizeof (gpointer), compare_addr);
2484          * return;
2485          */
2486         int gap = size;
2487         int swapped, end;
2488         while (TRUE) {
2489                 int i;
2490                 gap = new_gap (gap);
2491                 swapped = FALSE;
2492                 end = size - gap;
2493                 for (i = 0; i < end; i++) {
2494                         int j = i + gap;
2495                         if (array [i] > array [j]) {
2496                                 void* val = array [i];
2497                                 array [i] = array [j];
2498                                 array [j] = val;
2499                                 swapped = TRUE;
2500                         }
2501                 }
2502                 if (gap == 1 && !swapped)
2503                         break;
2504         }
2505 }
2506
2507 static G_GNUC_UNUSED void
2508 print_nursery_gaps (void* start_nursery, void *end_nursery)
2509 {
2510         int i;
2511         gpointer first = start_nursery;
2512         gpointer next;
2513         for (i = 0; i < next_pin_slot; ++i) {
2514                 next = pin_queue [i];
2515                 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2516                 first = next;
2517         }
2518         next = end_nursery;
2519         fprintf (gc_debug_file, "Nursery range: %p-%p, size: %zd\n", first, next, (char*)next-(char*)first);
2520 }
2521
2522 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
2523 static void
2524 optimize_pin_queue (int start_slot)
2525 {
2526         void **start, **cur, **end;
2527         /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
2528         /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
2529         DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
2530         if ((next_pin_slot - start_slot) > 1)
2531                 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
2532         start = cur = pin_queue + start_slot;
2533         end = pin_queue + next_pin_slot;
2534         while (cur < end) {
2535                 *start = *cur++;
2536                 while (*start == *cur && cur < end)
2537                         cur++;
2538                 start++;
2539         };
2540         next_pin_slot = start - pin_queue;
2541         DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
2542         //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
2543         
2544 }
2545
2546 static int
2547 optimized_pin_queue_search (void *addr)
2548 {
2549         int first = 0, last = next_pin_slot;
2550         while (first < last) {
2551                 int middle = first + ((last - first) >> 1);
2552                 if (addr <= pin_queue [middle])
2553                         last = middle;
2554                 else
2555                         first = middle + 1;
2556         }
2557         g_assert (first == last);
2558         return first;
2559 }
2560
2561 static void
2562 find_optimized_pin_queue_area (void *start, void *end, int *first, int *last)
2563 {
2564         *first = optimized_pin_queue_search (start);
2565         *last = optimized_pin_queue_search (end);
2566 }
2567
2568 static void
2569 realloc_pin_queue (void)
2570 {
2571         int new_size = pin_queue_size? pin_queue_size + pin_queue_size/2: 1024;
2572         void **new_pin = get_internal_mem (sizeof (void*) * new_size, INTERNAL_MEM_PIN_QUEUE);
2573         memcpy (new_pin, pin_queue, sizeof (void*) * next_pin_slot);
2574         free_internal_mem (pin_queue, INTERNAL_MEM_PIN_QUEUE);
2575         pin_queue = new_pin;
2576         pin_queue_size = new_size;
2577         DEBUG (4, fprintf (gc_debug_file, "Reallocated pin queue to size: %d\n", new_size));
2578 }
2579
2580 #include "sgen-pinning.c"
2581
2582 /* 
2583  * Scan the memory between start and end and queue values which could be pointers
2584  * to the area between start_nursery and end_nursery for later consideration.
2585  * Typically used for thread stacks.
2586  */
2587 static void
2588 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery, int pin_type)
2589 {
2590         int count = 0;
2591         while (start < end) {
2592                 if (*start >= start_nursery && *start < end_nursery) {
2593                         /*
2594                          * *start can point to the middle of an object
2595                          * note: should we handle pointing at the end of an object?
2596                          * pinning in C# code disallows pointing at the end of an object
2597                          * but there is some small chance that an optimizing C compiler
2598                          * may keep the only reference to an object by pointing
2599                          * at the end of it. We ignore this small chance for now.
2600                          * Pointers to the end of an object are indistinguishable
2601                          * from pointers to the start of the next object in memory
2602                          * so if we allow that we'd need to pin two objects...
2603                          * We queue the pointer in an array, the
2604                          * array will then be sorted and uniqued. This way
2605                          * we can coalesce several pinning pointers and it should
2606                          * be faster since we'd do a memory scan with increasing
2607                          * addresses. Note: we can align the address to the allocation
2608                          * alignment, so the unique process is more effective.
2609                          */
2610                         mword addr = (mword)*start;
2611                         addr &= ~(ALLOC_ALIGN - 1);
2612                         if (addr >= (mword)start_nursery && addr < (mword)end_nursery)
2613                                 pin_stage_ptr ((void*)addr);
2614                         if (heap_dump_file)
2615                                 pin_stats_register_address ((char*)addr, pin_type);
2616                         DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
2617                         count++;
2618                 }
2619                 start++;
2620         }
2621         DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
2622 }
2623
2624 /* 
2625  * If generation is 0, just mark objects in the nursery, the others we don't care,
2626  * since they are not going to move anyway.
2627  * There are different areas that are scanned for pinned pointers:
2628  * *) the thread stacks (when jit support is ready only the unmanaged frames)
2629  * *) the pinned handle table
2630  * *) the pinned roots
2631  *
2632  * Note: when we'll use a write barrier for old to new gen references, we need to
2633  * keep track of old gen objects that point to pinned new gen objects because in that
2634  * case the referenced object will be moved maybe at the next collection, but there
2635  * is no write in the old generation area where the pinned object is referenced
2636  * and we may not consider it as reachable.
2637  */
2638 static G_GNUC_UNUSED void
2639 mark_pinned_objects (int generation)
2640 {
2641 }
2642
2643 /*
2644  * Debugging function: find in the conservative roots where @obj is being pinned.
2645  */
2646 static G_GNUC_UNUSED void
2647 find_pinning_reference (char *obj, size_t size)
2648 {
2649         RootRecord *root;
2650         int i;
2651         char *endobj = obj + size;
2652         for (i = 0; i < roots_hash_size [0]; ++i) {
2653                 for (root = roots_hash [0][i]; root; root = root->next) {
2654                         /* if desc is non-null it has precise info */
2655                         if (!root->root_desc) {
2656                                 char ** start = (char**)root->start_root;
2657                                 while (start < (char**)root->end_root) {
2658                                         if (*start >= obj && *start < endobj) {
2659                                                 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));
2660                                         }
2661                                         start++;
2662                                 }
2663                         }
2664                 }
2665         }
2666         find_pinning_ref_from_thread (obj, size);
2667 }
2668
2669 /*
2670  * The first thing we do in a collection is to identify pinned objects.
2671  * This function considers all the areas of memory that need to be
2672  * conservatively scanned.
2673  */
2674 static void
2675 pin_from_roots (void *start_nursery, void *end_nursery)
2676 {
2677         RootRecord *root;
2678         int i;
2679         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]));
2680         /* objects pinned from the API are inside these roots */
2681         for (i = 0; i < roots_hash_size [ROOT_TYPE_PINNED]; ++i) {
2682                 for (root = roots_hash [ROOT_TYPE_PINNED][i]; root; root = root->next) {
2683                         DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
2684                         conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery, PIN_TYPE_OTHER);
2685                 }
2686         }
2687         /* now deal with the thread stacks
2688          * in the future we should be able to conservatively scan only:
2689          * *) the cpu registers
2690          * *) the unmanaged stack frames
2691          * *) the _last_ managed stack frame
2692          * *) pointers slots in managed frames
2693          */
2694         scan_thread_data (start_nursery, end_nursery, FALSE);
2695
2696         evacuate_pin_staging_area ();
2697 }
2698
2699 /* Copy function called from user defined mark functions */
2700 static char *user_copy_n_start;
2701 static char *user_copy_n_end;
2702
2703 static void
2704 user_copy (void **addr)
2705 {
2706         copy_object (addr, user_copy_n_start, user_copy_n_end);
2707 }
2708
2709 /*
2710  * The memory area from start_root to end_root contains pointers to objects.
2711  * Their position is precisely described by @desc (this means that the pointer
2712  * can be either NULL or the pointer to the start of an object).
2713  * This functions copies them to to_space updates them.
2714  */
2715 static void
2716 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, mword desc)
2717 {
2718         switch (desc & ROOT_DESC_TYPE_MASK) {
2719         case ROOT_DESC_BITMAP:
2720                 desc >>= ROOT_DESC_TYPE_SHIFT;
2721                 while (desc) {
2722                         if ((desc & 1) && *start_root) {
2723                                 copy_object (start_root, n_start, n_end);
2724                                 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));
2725                                 drain_gray_stack (n_start, n_end);
2726                         }
2727                         desc >>= 1;
2728                         start_root++;
2729                 }
2730                 return;
2731         case ROOT_DESC_COMPLEX: {
2732                 gsize *bitmap_data = complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
2733                 int bwords = (*bitmap_data) - 1;
2734                 void **start_run = start_root;
2735                 bitmap_data++;
2736                 while (bwords-- > 0) {
2737                         gsize bmap = *bitmap_data++;
2738                         void **objptr = start_run;
2739                         while (bmap) {
2740                                 if ((bmap & 1) && *objptr) {
2741                                         copy_object (objptr, n_start, n_end);
2742                                         DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", objptr, *objptr));
2743                                         drain_gray_stack (n_start, n_end);
2744                                 }
2745                                 bmap >>= 1;
2746                                 ++objptr;
2747                         }
2748                         start_run += GC_BITS_PER_WORD;
2749                 }
2750                 break;
2751         }
2752         case ROOT_DESC_USER: {
2753                 MonoGCRootMarkFunc marker = user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
2754
2755                 user_copy_n_start = n_start;
2756                 user_copy_n_end = n_end;
2757                 marker (start_root, user_copy);
2758                 break;
2759         }
2760         case ROOT_DESC_RUN_LEN:
2761                 g_assert_not_reached ();
2762         default:
2763                 g_assert_not_reached ();
2764         }
2765 }
2766
2767 static Fragment*
2768 alloc_fragment (void)
2769 {
2770         Fragment *frag = fragment_freelist;
2771         if (frag) {
2772                 fragment_freelist = frag->next;
2773                 frag->next = NULL;
2774                 return frag;
2775         }
2776         frag = get_internal_mem (sizeof (Fragment), INTERNAL_MEM_FRAGMENT);
2777         frag->next = NULL;
2778         return frag;
2779 }
2780
2781 /* size must be a power of 2 */
2782 static void*
2783 get_os_memory_aligned (mword size, gboolean activate)
2784 {
2785         /* Allocate twice the memory to be able to put the block on an aligned address */
2786         char *mem = get_os_memory (size * 2, activate);
2787         char *aligned;
2788
2789         g_assert (mem);
2790
2791         aligned = (char*)((mword)(mem + (size - 1)) & ~(size - 1));
2792         g_assert (aligned >= mem && aligned + size <= mem + size * 2 && !((mword)aligned & (size - 1)));
2793
2794         if (aligned > mem)
2795                 free_os_memory (mem, aligned - mem);
2796         if (aligned + size < mem + size * 2)
2797                 free_os_memory (aligned + size, (mem + size * 2) - (aligned + size));
2798
2799         return aligned;
2800 }
2801
2802 /*
2803  * Allocate and setup the data structures needed to be able to allocate objects
2804  * in the nursery. The nursery is stored in nursery_section.
2805  */
2806 static void
2807 alloc_nursery (void)
2808 {
2809         GCMemSection *section;
2810         char *data;
2811         int scan_starts;
2812         Fragment *frag;
2813         int alloc_size;
2814
2815         if (nursery_section)
2816                 return;
2817         DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %zd\n", nursery_size));
2818         /* later we will alloc a larger area for the nursery but only activate
2819          * what we need. The rest will be used as expansion if we have too many pinned
2820          * objects in the existing nursery.
2821          */
2822         /* FIXME: handle OOM */
2823         section = get_internal_mem (SIZEOF_GC_MEM_SECTION, INTERNAL_MEM_SECTION);
2824
2825         g_assert (nursery_size == DEFAULT_NURSERY_SIZE);
2826         alloc_size = nursery_size;
2827 #ifdef ALIGN_NURSERY
2828         data = get_os_memory_aligned (alloc_size, TRUE);
2829 #else
2830         data = get_os_memory (alloc_size, TRUE);
2831 #endif
2832         nursery_start = data;
2833         nursery_real_end = nursery_start + nursery_size;
2834         UPDATE_HEAP_BOUNDARIES (nursery_start, nursery_real_end);
2835         nursery_next = nursery_start;
2836         total_alloc += alloc_size;
2837         DEBUG (4, fprintf (gc_debug_file, "Expanding nursery size (%p-%p): %zd, total: %zd\n", data, data + alloc_size, nursery_size, total_alloc));
2838         section->data = section->next_data = data;
2839         section->size = alloc_size;
2840         section->end_data = nursery_real_end;
2841         scan_starts = (alloc_size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
2842         section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
2843         section->num_scan_start = scan_starts;
2844         section->block.role = MEMORY_ROLE_GEN0;
2845
2846         /* add to the section list */
2847         section->block.next = section_list;
2848         section_list = section;
2849
2850         nursery_section = section;
2851
2852         /* Setup the single first large fragment */
2853         frag = alloc_fragment ();
2854         frag->fragment_start = nursery_start;
2855         frag->fragment_limit = nursery_start;
2856         frag->fragment_end = nursery_real_end;
2857         nursery_frag_real_end = nursery_real_end;
2858         /* FIXME: frag here is lost */
2859 }
2860
2861 static void
2862 scan_finalizer_entries (FinalizeEntry *list, char *start, char *end) {
2863         FinalizeEntry *fin;
2864
2865         for (fin = list; fin; fin = fin->next) {
2866                 if (!fin->object)
2867                         continue;
2868                 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2869                 copy_object (&fin->object, start, end);
2870         }
2871 }
2872
2873 /*
2874  * Update roots in the old generation. Since we currently don't have the
2875  * info from the write barriers, we just scan all the objects.
2876  */
2877 static G_GNUC_UNUSED void
2878 scan_old_generation (char *start, char* end)
2879 {
2880         GCMemSection *section;
2881         LOSObject *big_object;
2882         char *p;
2883
2884         for (section = section_list; section; section = section->block.next) {
2885                 if (section == nursery_section)
2886                         continue;
2887                 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)));
2888                 /* we have to deal with zeroed holes in old generation (truncated strings ...) */
2889                 p = section->data;
2890                 while (p < section->next_data) {
2891                         if (!*(void**)p) {
2892                                 p += ALLOC_ALIGN;
2893                                 continue;
2894                         }
2895                         DEBUG (8, fprintf (gc_debug_file, "Precise old object scan of %p (%s)\n", p, safe_name (p)));
2896                         p = scan_object (p, start, end);
2897                 }
2898         }
2899         /* scan the old object space, too */
2900         for (big_object = los_object_list; big_object; big_object = big_object->next) {
2901                 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));
2902                 scan_object (big_object->data, start, end);
2903         }
2904         /* scan the list of objects ready for finalization */
2905         scan_finalizer_entries (fin_ready_list, start, end);
2906         scan_finalizer_entries (critical_fin_list, start, end);
2907 }
2908
2909 static mword fragment_total = 0;
2910 /*
2911  * We found a fragment of free memory in the nursery: memzero it and if
2912  * it is big enough, add it to the list of fragments that can be used for
2913  * allocation.
2914  */
2915 static void
2916 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
2917 {
2918         Fragment *fragment;
2919         DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
2920         binary_protocol_empty (frag_start, frag_size);
2921         /* memsetting just the first chunk start is bound to provide better cache locality */
2922         if (nursery_clear_policy == CLEAR_AT_GC)
2923                 memset (frag_start, 0, frag_size);
2924         /* Not worth dealing with smaller fragments: need to tune */
2925         if (frag_size >= FRAGMENT_MIN_SIZE) {
2926                 fragment = alloc_fragment ();
2927                 fragment->fragment_start = frag_start;
2928                 fragment->fragment_limit = frag_start;
2929                 fragment->fragment_end = frag_end;
2930                 fragment->next = nursery_fragments;
2931                 nursery_fragments = fragment;
2932                 fragment_total += frag_size;
2933         } else {
2934                 /* Clear unused fragments, pinning depends on this */
2935                 memset (frag_start, 0, frag_size);
2936         }
2937 }
2938
2939 static int
2940 scan_needed_big_objects (char *start_addr, char *end_addr)
2941 {
2942         LOSObject *big_object;
2943         int count = 0;
2944         for (big_object = los_object_list; big_object; big_object = big_object->next) {
2945                 if (!big_object->scanned && object_is_pinned (big_object->data)) {
2946                         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));
2947                         scan_object (big_object->data, start_addr, end_addr);
2948                         drain_gray_stack (start_addr, end_addr);
2949                         big_object->scanned = TRUE;
2950                         count++;
2951                 }
2952         }
2953         return count;
2954 }
2955
2956 static const char*
2957 generation_name (int generation)
2958 {
2959         switch (generation) {
2960         case GENERATION_NURSERY: return "nursery";
2961         case GENERATION_OLD: return "old";
2962         default: g_assert_not_reached ();
2963         }
2964 }
2965
2966 static DisappearingLinkHashTable*
2967 get_dislink_hash_table (int generation)
2968 {
2969         switch (generation) {
2970         case GENERATION_NURSERY: return &minor_disappearing_link_hash;
2971         case GENERATION_OLD: return &major_disappearing_link_hash;
2972         default: g_assert_not_reached ();
2973         }
2974 }
2975
2976 static FinalizeEntryHashTable*
2977 get_finalize_entry_hash_table (int generation)
2978 {
2979         switch (generation) {
2980         case GENERATION_NURSERY: return &minor_finalizable_hash;
2981         case GENERATION_OLD: return &major_finalizable_hash;
2982         default: g_assert_not_reached ();
2983         }
2984 }
2985
2986 static void
2987 new_to_space_section (void)
2988 {
2989         /* FIXME: if the current to_space_section is empty, we don't
2990            have to allocate a new one */
2991
2992         to_space_section = alloc_major_section ();
2993         to_space_bumper = to_space_section->next_data;
2994         to_space_top = to_space_section->end_data;
2995 }
2996
2997 static void
2998 to_space_set_next_data (void)
2999 {
3000         g_assert (to_space_bumper >= to_space_section->next_data && to_space_bumper <= to_space_section->end_data);
3001         to_space_section->next_data = to_space_bumper;
3002 }
3003
3004 static void
3005 to_space_expand (void)
3006 {
3007         if (to_space_section) {
3008                 g_assert (to_space_top == to_space_section->end_data);
3009                 to_space_set_next_data ();
3010         }
3011
3012         new_to_space_section ();
3013 }
3014
3015 static void
3016 unset_to_space (void)
3017 {
3018         /* between collections the to_space_bumper is invalidated
3019            because degraded allocations might occur, so we set it to
3020            NULL, just to make it explicit */
3021         to_space_bumper = NULL;
3022
3023         /* don't unset to_space_section if we implement the FIXME in
3024            new_to_space_section */
3025         to_space_section = NULL;
3026 }
3027
3028 static gboolean
3029 object_is_in_to_space (char *obj)
3030 {
3031         mword objsize;
3032
3033         /* nursery */
3034         if (ptr_in_nursery (obj))
3035                 return FALSE;
3036
3037         objsize = safe_object_get_size ((MonoObject*)obj);
3038         objsize += ALLOC_ALIGN - 1;
3039         objsize &= ~(ALLOC_ALIGN - 1);
3040
3041         /* LOS */
3042         if (objsize > MAX_SMALL_OBJ_SIZE)
3043                 return FALSE;
3044
3045         /* pinned chunk */
3046         if (obj_is_from_pinned_alloc (obj))
3047                 return FALSE;
3048
3049         /* now we know it's in a major heap section */
3050         return MAJOR_SECTION_FOR_OBJECT (obj)->is_to_space;
3051 }
3052
3053 static void
3054 finish_gray_stack (char *start_addr, char *end_addr, int generation)
3055 {
3056         TV_DECLARE (atv);
3057         TV_DECLARE (btv);
3058         int fin_ready, bigo_scanned_num;
3059
3060         /*
3061          * We copied all the reachable objects. Now it's the time to copy
3062          * the objects that were not referenced by the roots, but by the copied objects.
3063          * we built a stack of objects pointed to by gray_start: they are
3064          * additional roots and we may add more items as we go.
3065          * We loop until gray_start == gray_objects which means no more objects have
3066          * been added. Note this is iterative: no recursion is involved.
3067          * We need to walk the LO list as well in search of marked big objects
3068          * (use a flag since this is needed only on major collections). We need to loop
3069          * here as well, so keep a counter of marked LO (increasing it in copy_object).
3070          *   To achieve better cache locality and cache usage, we drain the gray stack 
3071          * frequently, after each object is copied, and just finish the work here.
3072          */
3073         drain_gray_stack (start_addr, end_addr);
3074         TV_GETTIME (atv);
3075         //scan_old_generation (start_addr, end_addr);
3076         DEBUG (2, fprintf (gc_debug_file, "%s generation done\n", generation_name (generation)));
3077         /* walk the finalization queue and move also the objects that need to be
3078          * finalized: use the finalized objects as new roots so the objects they depend
3079          * on are also not reclaimed. As with the roots above, only objects in the nursery
3080          * are marked/copied.
3081          * We need a loop here, since objects ready for finalizers may reference other objects
3082          * that are fin-ready. Speedup with a flag?
3083          */
3084         do {
3085                 fin_ready = num_ready_finalizers;
3086                 finalize_in_range (start_addr, end_addr, generation);
3087                 if (generation == GENERATION_OLD)
3088                         finalize_in_range (nursery_start, nursery_real_end, GENERATION_NURSERY);
3089                 bigo_scanned_num = scan_needed_big_objects (start_addr, end_addr);
3090
3091                 /* drain the new stack that might have been created */
3092                 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin\n"));
3093                 drain_gray_stack (start_addr, end_addr);
3094         } while (fin_ready != num_ready_finalizers || bigo_scanned_num);
3095         TV_GETTIME (btv);
3096         DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan for %s generation: %d usecs\n", generation_name (generation), TV_ELAPSED (atv, btv)));
3097
3098         /*
3099          * handle disappearing links
3100          * Note we do this after checking the finalization queue because if an object
3101          * survives (at least long enough to be finalized) we don't clear the link.
3102          * This also deals with a possible issue with the monitor reclamation: with the Boehm
3103          * GC a finalized object my lose the monitor because it is cleared before the finalizer is
3104          * called.
3105          */
3106         g_assert (gray_object_queue_is_empty ());
3107         for (;;) {
3108                 null_link_in_range (start_addr, end_addr, generation);
3109                 if (generation == GENERATION_OLD)
3110                         null_link_in_range (start_addr, end_addr, GENERATION_NURSERY);
3111                 if (gray_object_queue_is_empty ())
3112                         break;
3113                 drain_gray_stack (start_addr, end_addr);
3114         }
3115
3116         g_assert (gray_object_queue_is_empty ());
3117         /* DEBUG (2, fprintf (gc_debug_file, "Copied from %s to old space: %d bytes (%p-%p)\n", generation_name (generation), (int)(to_space_bumper - to_space), to_space, to_space_bumper)); */
3118         to_space_set_next_data ();
3119 }
3120
3121 static void
3122 check_scan_starts (void)
3123 {
3124         GCMemSection *section;
3125         int i;
3126         if (!do_scan_starts_check)
3127                 return;
3128         for (section = section_list; section; section = section->block.next) {
3129                 for (i = 0; i < section->num_scan_start; ++i) {
3130                         if (section->scan_starts [i]) {
3131                                 guint size = safe_object_get_size ((MonoObject*) section->scan_starts [i]);
3132                                 g_assert (size >= sizeof (MonoObject) && size <= MAX_SMALL_OBJ_SIZE);
3133                         }
3134                 }
3135         }
3136 }
3137
3138 static int last_num_pinned = 0;
3139
3140 static void
3141 build_nursery_fragments (int start_pin, int end_pin)
3142 {
3143         char *frag_start, *frag_end;
3144         size_t frag_size;
3145         int i;
3146
3147         while (nursery_fragments) {
3148                 Fragment *next = nursery_fragments->next;
3149                 nursery_fragments->next = fragment_freelist;
3150                 fragment_freelist = nursery_fragments;
3151                 nursery_fragments = next;
3152         }
3153         frag_start = nursery_start;
3154         fragment_total = 0;
3155         /* clear scan starts */
3156         memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
3157         for (i = start_pin; i < end_pin; ++i) {
3158                 frag_end = pin_queue [i];
3159                 /* remove the pin bit from pinned objects */
3160                 unpin_object (frag_end);
3161                 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
3162                 frag_size = frag_end - frag_start;
3163                 if (frag_size)
3164                         add_nursery_frag (frag_size, frag_start, frag_end);
3165                 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
3166                 frag_size += ALLOC_ALIGN - 1;
3167                 frag_size &= ~(ALLOC_ALIGN - 1);
3168                 frag_start = (char*)pin_queue [i] + frag_size;
3169         }
3170         nursery_last_pinned_end = frag_start;
3171         frag_end = nursery_real_end;
3172         frag_size = frag_end - frag_start;
3173         if (frag_size)
3174                 add_nursery_frag (frag_size, frag_start, frag_end);
3175         if (!nursery_fragments) {
3176                 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", end_pin - start_pin));
3177                 for (i = start_pin; i < end_pin; ++i) {
3178                         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])));
3179                 }
3180                 degraded_mode = 1;
3181         }
3182
3183         nursery_next = nursery_frag_real_end = NULL;
3184
3185         /* Clear TLABs for all threads */
3186         clear_tlabs ();
3187 }
3188
3189 /* FIXME: later reduce code duplication here with the above
3190  * We don't keep track of section fragments for non-nursery sections yet, so
3191  * just memset to 0.
3192  */
3193 static void
3194 build_section_fragments (GCMemSection *section)
3195 {
3196         int i;
3197         char *frag_start, *frag_end;
3198         size_t frag_size;
3199
3200         /* clear scan starts */
3201         memset (section->scan_starts, 0, section->num_scan_start * sizeof (gpointer));
3202         frag_start = section->data;
3203         section->next_data = section->data;
3204         for (i = section->pin_queue_start; i < section->pin_queue_end; ++i) {
3205                 frag_end = pin_queue [i];
3206                 /* remove the pin bit from pinned objects */
3207                 unpin_object (frag_end);
3208                 if (frag_end >= section->data + section->size) {
3209                         frag_end = section->data + section->size;
3210                 } else {
3211                         section->scan_starts [((char*)frag_end - (char*)section->data)/SCAN_START_SIZE] = frag_end;
3212                 }
3213                 frag_size = frag_end - frag_start;
3214                 if (frag_size) {
3215                         binary_protocol_empty (frag_start, frag_size);
3216                         memset (frag_start, 0, frag_size);
3217                 }
3218                 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
3219                 frag_size += ALLOC_ALIGN - 1;
3220                 frag_size &= ~(ALLOC_ALIGN - 1);
3221                 frag_start = (char*)pin_queue [i] + frag_size;
3222                 section->next_data = MAX (section->next_data, frag_start);
3223         }
3224         frag_end = section->end_data;
3225         frag_size = frag_end - frag_start;
3226         if (frag_size) {
3227                 binary_protocol_empty (frag_start, frag_size);
3228                 memset (frag_start, 0, frag_size);
3229         }
3230 }
3231
3232 static void
3233 scan_from_registered_roots (char *addr_start, char *addr_end, int root_type)
3234 {
3235         int i;
3236         RootRecord *root;
3237         for (i = 0; i < roots_hash_size [root_type]; ++i) {
3238                 for (root = roots_hash [root_type][i]; root; root = root->next) {
3239                         DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
3240                         precisely_scan_objects_from ((void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc);
3241                 }
3242         }
3243 }
3244
3245 static void
3246 dump_occupied (char *start, char *end, char *section_start)
3247 {
3248         fprintf (heap_dump_file, "<occupied offset=\"%zd\" size=\"%zd\"/>\n", start - section_start, end - start);
3249 }
3250
3251 static void
3252 dump_section (GCMemSection *section, const char *type)
3253 {
3254         char *start = section->data;
3255         char *end = section->data + section->size;
3256         char *occ_start = NULL;
3257         GCVTable *vt;
3258         char *old_start = NULL; /* just for debugging */
3259
3260         fprintf (heap_dump_file, "<section type=\"%s\" size=\"%zu\">\n", type, section->size);
3261
3262         while (start < end) {
3263                 guint size;
3264                 MonoClass *class;
3265
3266                 if (!*(void**)start) {
3267                         if (occ_start) {
3268                                 dump_occupied (occ_start, start, section->data);
3269                                 occ_start = NULL;
3270                         }
3271                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
3272                         continue;
3273                 }
3274                 g_assert (start < section->next_data);
3275
3276                 if (!occ_start)
3277                         occ_start = start;
3278
3279                 vt = (GCVTable*)LOAD_VTABLE (start);
3280                 class = vt->klass;
3281
3282                 size = safe_object_get_size ((MonoObject*) start);
3283                 size += ALLOC_ALIGN - 1;
3284                 size &= ~(ALLOC_ALIGN - 1);
3285
3286                 /*
3287                 fprintf (heap_dump_file, "<object offset=\"%d\" class=\"%s.%s\" size=\"%d\"/>\n",
3288                                 start - section->data,
3289                                 vt->klass->name_space, vt->klass->name,
3290                                 size);
3291                 */
3292
3293                 old_start = start;
3294                 start += size;
3295         }
3296         if (occ_start)
3297                 dump_occupied (occ_start, start, section->data);
3298
3299         fprintf (heap_dump_file, "</section>\n");
3300 }
3301
3302 static void
3303 dump_object (MonoObject *obj, gboolean dump_location)
3304 {
3305         static char class_name [1024];
3306
3307         MonoClass *class = mono_object_class (obj);
3308         int i, j;
3309
3310         /*
3311          * Python's XML parser is too stupid to parse angle brackets
3312          * in strings, so we just ignore them;
3313          */
3314         i = j = 0;
3315         while (class->name [i] && j < sizeof (class_name) - 1) {
3316                 if (!strchr ("<>\"", class->name [i]))
3317                         class_name [j++] = class->name [i];
3318                 ++i;
3319         }
3320         g_assert (j < sizeof (class_name));
3321         class_name [j] = 0;
3322
3323         fprintf (heap_dump_file, "<object class=\"%s.%s\" size=\"%d\"",
3324                         class->name_space, class_name,
3325                         safe_object_get_size (obj));
3326         if (dump_location) {
3327                 const char *location;
3328                 if (ptr_in_nursery (obj))
3329                         location = "nursery";
3330                 else if (safe_object_get_size (obj) <= MAX_SMALL_OBJ_SIZE)
3331                         location = "major";
3332                 else
3333                         location = "LOS";
3334                 fprintf (heap_dump_file, " location=\"%s\"", location);
3335         }
3336         fprintf (heap_dump_file, "/>\n");
3337 }
3338
3339 static void
3340 dump_heap (const char *type, int num, const char *reason)
3341 {
3342         static char const *internal_mem_names [] = { "pin-queue", "fragment", "section", "scan-starts",
3343                                                      "fin-table", "finalize-entry", "dislink-table",
3344                                                      "dislink", "roots-table", "root-record", "statistics",
3345                                                      "remset", "gray-queue", "store-remset" };
3346
3347         GCMemSection *section;
3348         ObjectList *list;
3349         LOSObject *bigobj;
3350         int i;
3351
3352         fprintf (heap_dump_file, "<collection type=\"%s\" num=\"%d\"", type, num);
3353         if (reason)
3354                 fprintf (heap_dump_file, " reason=\"%s\"", reason);
3355         fprintf (heap_dump_file, ">\n");
3356         fprintf (heap_dump_file, "<other-mem-usage type=\"pinned-chunks\" size=\"%ld\"/>\n", pinned_chunk_bytes_alloced);
3357         fprintf (heap_dump_file, "<other-mem-usage type=\"large-internal\" size=\"%ld\"/>\n", large_internal_bytes_alloced);
3358         fprintf (heap_dump_file, "<other-mem-usage type=\"mempools\" size=\"%ld\"/>\n", mono_mempool_get_bytes_allocated ());
3359         for (i = 0; i < INTERNAL_MEM_MAX; ++i)
3360                 fprintf (heap_dump_file, "<other-mem-usage type=\"%s\" size=\"%ld\"/>\n", internal_mem_names [i], small_internal_mem_bytes [i]);
3361         fprintf (heap_dump_file, "<pinned type=\"stack\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_STACK]);
3362         /* fprintf (heap_dump_file, "<pinned type=\"static-data\" bytes=\"%d\"/>\n", pinned_byte_counts [PIN_TYPE_STATIC_DATA]); */
3363         fprintf (heap_dump_file, "<pinned type=\"other\" bytes=\"%zu\"/>\n", pinned_byte_counts [PIN_TYPE_OTHER]);
3364
3365         fprintf (heap_dump_file, "<pinned-objects>\n");
3366         for (list = pinned_objects; list; list = list->next)
3367                 dump_object (list->obj, TRUE);
3368         fprintf (heap_dump_file, "</pinned-objects>\n");
3369
3370         dump_section (nursery_section, "nursery");
3371
3372         for (section = section_list; section; section = section->block.next) {
3373                 if (section != nursery_section)
3374                         dump_section (section, "old");
3375         }
3376
3377         fprintf (heap_dump_file, "<los>\n");
3378         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next)
3379                 dump_object ((MonoObject*)bigobj->data, FALSE);
3380         fprintf (heap_dump_file, "</los>\n");
3381
3382         fprintf (heap_dump_file, "</collection>\n");
3383 }
3384
3385 static void
3386 init_stats (void)
3387 {
3388         static gboolean inited = FALSE;
3389
3390 #ifdef HEAVY_STATISTICS
3391         num_copy_object_called = 0;
3392         num_objects_copied = 0;
3393 #endif
3394
3395         if (inited)
3396                 return;
3397
3398         mono_counters_register ("Minor fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pre_collection_fragment_clear);
3399         mono_counters_register ("Minor pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_pinning);
3400         mono_counters_register ("Minor scan remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_remsets);
3401         mono_counters_register ("Minor scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_pinned);
3402         mono_counters_register ("Minor scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_registered_roots);
3403         mono_counters_register ("Minor scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_scan_thread_data);
3404         mono_counters_register ("Minor finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
3405         mono_counters_register ("Minor fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_fragment_creation);
3406
3407         mono_counters_register ("Major fragment clear", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pre_collection_fragment_clear);
3408         mono_counters_register ("Major pinning", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_pinning);
3409         mono_counters_register ("Major scan pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_pinned);
3410         mono_counters_register ("Major scan registered roots", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_registered_roots);
3411         mono_counters_register ("Major scan thread data", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_thread_data);
3412         mono_counters_register ("Major scan alloc_pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_alloc_pinned);
3413         mono_counters_register ("Major scan finalized", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_finalized);
3414         mono_counters_register ("Major scan big objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_scan_big_objects);
3415         mono_counters_register ("Major finish gray stack", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_minor_finish_gray_stack);
3416         mono_counters_register ("Major sweep", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_sweep);
3417         mono_counters_register ("Major fragment creation", MONO_COUNTER_GC | MONO_COUNTER_LONG, &time_major_fragment_creation);
3418
3419 #ifdef HEAVY_STATISTICS
3420         mono_counters_register ("WBarrier set field", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_field);
3421         mono_counters_register ("WBarrier set arrayref", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_arrayref);
3422         mono_counters_register ("WBarrier arrayref copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_arrayref_copy);
3423         mono_counters_register ("WBarrier generic store called", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store);
3424         mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
3425         mono_counters_register ("WBarrier set root", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_set_root);
3426         mono_counters_register ("WBarrier value copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_value_copy);
3427         mono_counters_register ("WBarrier object copy", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_object_copy);
3428
3429         mono_counters_register ("# objects allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced);
3430         mono_counters_register ("bytes allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced);
3431         mono_counters_register ("# objects allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_alloced_degraded);
3432         mono_counters_register ("bytes allocated degraded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_degraded);
3433         mono_counters_register ("bytes allocated in LOS", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_bytes_alloced_los);
3434         mono_counters_register ("# copy_object() called (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_nursery);
3435         mono_counters_register ("# objects copied (nursery)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_nursery);
3436         mono_counters_register ("# copy_object() called (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_called_major);
3437         mono_counters_register ("# objects copied (major)", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_objects_copied_major);
3438
3439         mono_counters_register ("# copy_object() failed from space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_from_space);
3440         mono_counters_register ("# copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_forwarded);
3441         mono_counters_register ("# copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_pinned);
3442         mono_counters_register ("# copy_object() failed large or pinned chunk", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_large_pinned);
3443         mono_counters_register ("# copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_copy_object_failed_to_space);
3444
3445         mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
3446         mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
3447         mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
3448         mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
3449         mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
3450         mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
3451 #endif
3452
3453         inited = TRUE;
3454 }
3455
3456 static void
3457 commit_stats (int generation)
3458 {
3459 #ifdef HEAVY_STATISTICS
3460         if (generation == GENERATION_NURSERY) {
3461                 stat_copy_object_called_nursery += num_copy_object_called;
3462                 stat_objects_copied_nursery += num_objects_copied;
3463         } else {
3464                 g_assert (generation == GENERATION_OLD);
3465                 stat_copy_object_called_major += num_copy_object_called;
3466                 stat_objects_copied_major += num_objects_copied;
3467         }
3468 #endif
3469 }
3470
3471 /*
3472  * Collect objects in the nursery.  Returns whether to trigger a major
3473  * collection.
3474  */
3475 static gboolean
3476 collect_nursery (size_t requested_size)
3477 {
3478         size_t max_garbage_amount;
3479         int i;
3480         char *orig_nursery_next;
3481         Fragment *frag;
3482         GCMemSection *section;
3483         int old_num_major_sections = num_major_sections;
3484         int sections_alloced;
3485         TV_DECLARE (all_atv);
3486         TV_DECLARE (all_btv);
3487         TV_DECLARE (atv);
3488         TV_DECLARE (btv);
3489
3490         init_stats ();
3491         binary_protocol_collection (GENERATION_NURSERY);
3492         check_scan_starts ();
3493
3494         degraded_mode = 0;
3495         orig_nursery_next = nursery_next;
3496         nursery_next = MAX (nursery_next, nursery_last_pinned_end);
3497         /* FIXME: optimize later to use the higher address where an object can be present */
3498         nursery_next = MAX (nursery_next, nursery_real_end);
3499
3500         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)));
3501         max_garbage_amount = nursery_next - nursery_start;
3502         g_assert (nursery_section->size >= max_garbage_amount);
3503
3504         /* world must be stopped already */
3505         TV_GETTIME (all_atv);
3506         TV_GETTIME (atv);
3507
3508         /* Clear all remaining nursery fragments, pinning depends on this */
3509         if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3510                 g_assert (orig_nursery_next <= nursery_frag_real_end);
3511                 memset (orig_nursery_next, 0, nursery_frag_real_end - orig_nursery_next);
3512                 for (frag = nursery_fragments; frag; frag = frag->next) {
3513                         memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
3514                 }
3515         }
3516
3517         TV_GETTIME (btv);
3518         time_minor_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3519
3520         if (xdomain_checks)
3521                 check_for_xdomain_refs ();
3522
3523         nursery_section->next_data = nursery_next;
3524
3525         if (!to_space_section) {
3526                 new_to_space_section ();
3527         } else {
3528                 /* we might have done degraded allocation since the
3529                    last collection */
3530                 g_assert (to_space_bumper <= to_space_section->next_data);
3531                 to_space_bumper = to_space_section->next_data;
3532
3533                 to_space_section->is_to_space = TRUE;
3534         }
3535         gray_object_queue_init ();
3536
3537         num_minor_gcs++;
3538         mono_stats.minor_gc_count ++;
3539         /* pin from pinned handles */
3540         init_pinning ();
3541         pin_from_roots (nursery_start, nursery_next);
3542         /* identify pinned objects */
3543         optimize_pin_queue (0);
3544         next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next);
3545         TV_GETTIME (atv);
3546         time_minor_pinning += TV_ELAPSED_MS (btv, atv);
3547         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (btv, atv)));
3548         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3549
3550         if (consistency_check_at_minor_collection)
3551                 check_consistency ();
3552
3553         /* 
3554          * walk all the roots and copy the young objects to the old generation,
3555          * starting from to_space
3556          */
3557
3558         scan_from_remsets (nursery_start, nursery_next);
3559         /* we don't have complete write barrier yet, so we scan all the old generation sections */
3560         TV_GETTIME (btv);
3561         time_minor_scan_remsets += TV_ELAPSED_MS (atv, btv);
3562         DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (atv, btv)));
3563
3564         /* the pinned objects are roots */
3565         for (i = 0; i < next_pin_slot; ++i) {
3566                 DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n", i, pin_queue [i], safe_name (pin_queue [i])));
3567                 scan_object (pin_queue [i], nursery_start, nursery_next);
3568         }
3569         TV_GETTIME (atv);
3570         time_minor_scan_pinned += TV_ELAPSED_MS (btv, atv);
3571         /* registered roots, this includes static fields */
3572         scan_from_registered_roots (nursery_start, nursery_next, ROOT_TYPE_NORMAL);
3573         scan_from_registered_roots (nursery_start, nursery_next, ROOT_TYPE_WBARRIER);
3574         TV_GETTIME (btv);
3575         time_minor_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3576         /* thread data */
3577         scan_thread_data (nursery_start, nursery_next, TRUE);
3578         TV_GETTIME (atv);
3579         time_minor_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3580         btv = atv;
3581
3582         finish_gray_stack (nursery_start, nursery_next, GENERATION_NURSERY);
3583         TV_GETTIME (atv);
3584         time_minor_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3585
3586         /* walk the pin_queue, build up the fragment list of free memory, unmark
3587          * pinned objects as we go, memzero() the empty fragments so they are ready for the
3588          * next allocations.
3589          */
3590         build_nursery_fragments (0, next_pin_slot);
3591         TV_GETTIME (btv);
3592         time_minor_fragment_creation += TV_ELAPSED_MS (atv, btv);
3593         DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %zd bytes available\n", TV_ELAPSED (atv, btv), fragment_total));
3594
3595         for (section = section_list; section; section = section->block.next) {
3596                 if (section->is_to_space)
3597                         section->is_to_space = FALSE;
3598         }
3599
3600         TV_GETTIME (all_btv);
3601         mono_stats.minor_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3602
3603         if (heap_dump_file)
3604                 dump_heap ("minor", num_minor_gcs - 1, NULL);
3605
3606         /* prepare the pin queue for the next collection */
3607         last_num_pinned = next_pin_slot;
3608         next_pin_slot = 0;
3609         if (fin_ready_list || critical_fin_list) {
3610                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3611                 mono_gc_finalize_notify ();
3612         }
3613         pin_stats_reset ();
3614
3615         g_assert (gray_object_queue_is_empty ());
3616
3617         commit_stats (GENERATION_NURSERY);
3618
3619         check_scan_starts ();
3620
3621         sections_alloced = num_major_sections - old_num_major_sections;
3622         minor_collection_sections_alloced += sections_alloced;
3623
3624         return minor_collection_sections_alloced > minor_collection_section_allowance;
3625 }
3626
3627 static void
3628 major_collection (const char *reason)
3629 {
3630         GCMemSection *section, *prev_section;
3631         LOSObject *bigobj, *prevbo;
3632         int i;
3633         PinnedChunk *chunk;
3634         Fragment *frag;
3635         TV_DECLARE (all_atv);
3636         TV_DECLARE (all_btv);
3637         TV_DECLARE (atv);
3638         TV_DECLARE (btv);
3639         /* FIXME: only use these values for the precise scan
3640          * note that to_space pointers should be excluded anyway...
3641          */
3642         char *heap_start = NULL;
3643         char *heap_end = (char*)-1;
3644         size_t copy_space_required = 0;
3645         int old_num_major_sections = num_major_sections;
3646         int num_major_sections_saved, save_target, allowance_target;
3647
3648         init_stats ();
3649         binary_protocol_collection (GENERATION_OLD);
3650         check_scan_starts ();
3651
3652         degraded_mode = 0;
3653         DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
3654         num_major_gcs++;
3655         mono_stats.major_gc_count ++;
3656
3657         /* world must be stopped already */
3658         TV_GETTIME (all_atv);
3659         TV_GETTIME (atv);
3660
3661         /* Clear all remaining nursery fragments, pinning depends on this */
3662         if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
3663                 g_assert (nursery_next <= nursery_frag_real_end);
3664                 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
3665                 for (frag = nursery_fragments; frag; frag = frag->next) {
3666                         memset (frag->fragment_start, 0, frag->fragment_end - frag->fragment_start);
3667                 }
3668         }
3669
3670         TV_GETTIME (btv);
3671         time_major_pre_collection_fragment_clear += TV_ELAPSED_MS (atv, btv);
3672
3673         if (xdomain_checks)
3674                 check_for_xdomain_refs ();
3675
3676         if (g_getenv ("MONO_GC_NO_MAJOR")) {
3677                 collect_nursery (0);
3678                 return;
3679         }
3680         nursery_section->next_data = nursery_real_end;
3681         /* we should also coalesce scanning from sections close to each other
3682          * and deal with pointers outside of the sections later.
3683          */
3684         /* The remsets are not useful for a major collection */
3685         clear_remsets ();
3686
3687         TV_GETTIME (atv);
3688         init_pinning ();
3689         DEBUG (6, fprintf (gc_debug_file, "Collecting pinned addresses\n"));
3690         pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address);
3691         optimize_pin_queue (0);
3692
3693         /*
3694          * pin_queue now contains all candidate pointers, sorted and
3695          * uniqued.  We must do two passes now to figure out which
3696          * objects are pinned.
3697          *
3698          * The first is to find within the pin_queue the area for each
3699          * section.  This requires that the pin_queue be sorted.  We
3700          * also process the LOS objects and pinned chunks here.
3701          *
3702          * The second, destructive, pass is to reduce the section
3703          * areas to pointers to the actually pinned objects.
3704          */
3705         DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
3706         /* first pass for the sections */
3707         for (section = section_list; section; section = section->block.next) {
3708                 int start, end;
3709                 DEBUG (6, fprintf (gc_debug_file, "Pinning from section %p (%p-%p)\n", section, section->data, section->end_data));
3710                 find_optimized_pin_queue_area (section->data, section->end_data, &start, &end);
3711                 DEBUG (6, fprintf (gc_debug_file, "Found %d pinning addresses in section %p (%d-%d)\n",
3712                                                 end - start, section, start, end));
3713                 section->pin_queue_start = start;
3714                 section->pin_queue_end = end;
3715         }
3716         /* identify possible pointers to the insize of large objects */
3717         DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
3718         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3719                 int start, end;
3720                 find_optimized_pin_queue_area (bigobj->data, (char*)bigobj->data + bigobj->size, &start, &end);
3721                 if (start != end) {
3722                         pin_object (bigobj->data);
3723                         if (heap_dump_file)
3724                                 pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
3725                         DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %zd from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
3726                 }
3727         }
3728         /* look for pinned addresses for pinned-alloc objects */
3729         DEBUG (6, fprintf (gc_debug_file, "Pinning from pinned-alloc objects\n"));
3730         for (chunk = pinned_chunk_list; chunk; chunk = chunk->block.next) {
3731                 int start, end;
3732                 find_optimized_pin_queue_area (chunk->start_data, (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE, &start, &end);
3733                 if (start != end)
3734                         mark_pinned_from_addresses (chunk, pin_queue + start, pin_queue + end);
3735         }
3736         /* second pass for the sections */
3737         for (section = section_list; section; section = section->block.next) {
3738                 int start = section->pin_queue_start;
3739                 int end = section->pin_queue_end;
3740                 if (start != end) {
3741                         int reduced_to;
3742                         reduced_to = pin_objects_from_addresses (section, pin_queue + start, pin_queue + end,
3743                                         section->data, section->next_data);
3744                         section->pin_queue_start = start;
3745                         section->pin_queue_end = start + reduced_to;
3746                 }
3747                 copy_space_required += (char*)section->next_data - (char*)section->data;
3748         }
3749
3750         TV_GETTIME (btv);
3751         time_major_pinning += TV_ELAPSED_MS (atv, btv);
3752         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
3753         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
3754
3755         new_to_space_section ();
3756         gray_object_queue_init ();
3757
3758         /* the old generation doesn't need to be scanned (no remembered sets or card
3759          * table needed either): the only objects that must survive are those pinned and
3760          * those referenced by the precise roots.
3761          * mark any section without pinned objects, so we can free it since we will be able to
3762          * move all the objects.
3763          */
3764         /* the pinned objects are roots (big objects are included in this list, too) */
3765         for (section = section_list; section; section = section->block.next) {
3766                 for (i = section->pin_queue_start; i < section->pin_queue_end; ++i) {
3767                         DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n",
3768                                                         i, pin_queue [i], safe_name (pin_queue [i])));
3769                         scan_object (pin_queue [i], heap_start, heap_end);
3770                 }
3771         }
3772         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
3773                 if (object_is_pinned (bigobj->data)) {
3774                         DEBUG (6, fprintf (gc_debug_file, "Precise object scan pinned LOS object %p (%s)\n",
3775                                                         bigobj->data, safe_name (bigobj->data)));
3776                         scan_object (bigobj->data, heap_start, heap_end);
3777                 }
3778         }
3779         TV_GETTIME (atv);
3780         time_major_scan_pinned += TV_ELAPSED_MS (btv, atv);
3781
3782         /* registered roots, this includes static fields */
3783         scan_from_registered_roots (heap_start, heap_end, ROOT_TYPE_NORMAL);
3784         scan_from_registered_roots (heap_start, heap_end, ROOT_TYPE_WBARRIER);
3785         TV_GETTIME (btv);
3786         time_major_scan_registered_roots += TV_ELAPSED_MS (atv, btv);
3787
3788         /* Threads */
3789         scan_thread_data (heap_start, heap_end, TRUE);
3790         TV_GETTIME (atv);
3791         time_major_scan_thread_data += TV_ELAPSED_MS (btv, atv);
3792
3793         /* alloc_pinned objects */
3794         scan_from_pinned_objects (heap_start, heap_end);
3795         TV_GETTIME (btv);
3796         time_major_scan_alloc_pinned += TV_ELAPSED_MS (atv, btv);
3797
3798         /* scan the list of objects ready for finalization */
3799         scan_finalizer_entries (fin_ready_list, heap_start, heap_end);
3800         scan_finalizer_entries (critical_fin_list, heap_start, heap_end);
3801         TV_GETTIME (atv);
3802         time_major_scan_finalized += TV_ELAPSED_MS (btv, atv);
3803         DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
3804
3805         /* we need to go over the big object list to see if any was marked and scan it
3806          * And we need to make this in a loop, considering that objects referenced by finalizable
3807          * objects could reference big objects (this happens in finish_gray_stack ())
3808          */
3809         scan_needed_big_objects (heap_start, heap_end);
3810         TV_GETTIME (btv);
3811         time_major_scan_big_objects += TV_ELAPSED_MS (atv, btv);
3812
3813         /* all the objects in the heap */
3814         finish_gray_stack (heap_start, heap_end, GENERATION_OLD);
3815         TV_GETTIME (atv);
3816         time_major_finish_gray_stack += TV_ELAPSED_MS (btv, atv);
3817
3818         unset_to_space ();
3819
3820         /* sweep the big objects list */
3821         prevbo = NULL;
3822         for (bigobj = los_object_list; bigobj;) {
3823                 if (object_is_pinned (bigobj->data)) {
3824                         unpin_object (bigobj->data);
3825                         bigobj->scanned = FALSE;
3826                 } else {
3827                         LOSObject *to_free;
3828                         /* not referenced anywhere, so we can free it */
3829                         if (prevbo)
3830                                 prevbo->next = bigobj->next;
3831                         else
3832                                 los_object_list = bigobj->next;
3833                         to_free = bigobj;
3834                         bigobj = bigobj->next;
3835                         free_large_object (to_free);
3836                         continue;
3837                 }
3838                 prevbo = bigobj;
3839                 bigobj = bigobj->next;
3840         }
3841         /* unpin objects from the pinned chunks and free the unmarked ones */
3842         sweep_pinned_objects ();
3843
3844         TV_GETTIME (btv);
3845         time_major_sweep += TV_ELAPSED_MS (atv, btv);
3846
3847         /* free the unused sections */
3848         prev_section = NULL;
3849         for (section = section_list; section;) {
3850                 /* to_space doesn't need handling here and the nursery is special */
3851                 if (section->is_to_space || section == nursery_section) {
3852                         if (section->is_to_space)
3853                                 section->is_to_space = FALSE;
3854                         prev_section = section;
3855                         section = section->block.next;
3856                         continue;
3857                 }
3858                 /* no pinning object, so the section is free */
3859                 if (section->pin_queue_start == section->pin_queue_end) {
3860                         GCMemSection *to_free;
3861                         if (prev_section)
3862                                 prev_section->block.next = section->block.next;
3863                         else
3864                                 section_list = section->block.next;
3865                         to_free = section;
3866                         section = section->block.next;
3867                         free_major_section (to_free);
3868                         continue;
3869                 } else {
3870                         DEBUG (6, fprintf (gc_debug_file, "Section %p has still pinned objects (%d)\n", section, section->pin_queue_end - section->pin_queue_start));
3871                         build_section_fragments (section);
3872                 }
3873                 prev_section = section;
3874                 section = section->block.next;
3875         }
3876
3877         /* walk the pin_queue, build up the fragment list of free memory, unmark
3878          * pinned objects as we go, memzero() the empty fragments so they are ready for the
3879          * next allocations.
3880          */
3881         build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
3882
3883         TV_GETTIME (atv);
3884         time_major_fragment_creation += TV_ELAPSED_MS (btv, atv);
3885
3886         TV_GETTIME (all_btv);
3887         mono_stats.major_gc_time_usecs += TV_ELAPSED (all_atv, all_btv);
3888
3889         if (heap_dump_file)
3890                 dump_heap ("major", num_major_gcs - 1, reason);
3891
3892         /* prepare the pin queue for the next collection */
3893         next_pin_slot = 0;
3894         if (fin_ready_list || critical_fin_list) {
3895                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
3896                 mono_gc_finalize_notify ();
3897         }
3898         pin_stats_reset ();
3899
3900         g_assert (gray_object_queue_is_empty ());
3901
3902         commit_stats (GENERATION_OLD);
3903
3904         num_major_sections_saved = MAX (old_num_major_sections - num_major_sections, 1);
3905
3906         save_target = num_major_sections / 2;
3907         /*
3908          * We aim to allow the allocation of as many sections as is
3909          * necessary to reclaim save_target sections in the next
3910          * collection.  We assume the collection pattern won't change.
3911          * In the last cycle, we had num_major_sections_saved for
3912          * minor_collection_sections_alloced.  Assuming things won't
3913          * change, this must be the same ratio as save_target for
3914          * allowance_target, i.e.
3915          *
3916          *    num_major_sections_saved            save_target
3917          * --------------------------------- == ----------------
3918          * minor_collection_sections_alloced    allowance_target
3919          *
3920          * hence:
3921          */
3922         allowance_target = save_target * minor_collection_sections_alloced / num_major_sections_saved;
3923
3924         minor_collection_section_allowance = MAX (MIN (allowance_target, num_major_sections), MIN_MINOR_COLLECTION_SECTION_ALLOWANCE);
3925
3926         minor_collection_sections_alloced = 0;
3927
3928         check_scan_starts ();
3929 }
3930
3931 /*
3932  * Allocate a new section of memory to be used as old generation.
3933  */
3934 static GCMemSection*
3935 alloc_major_section (void)
3936 {
3937         GCMemSection *section;
3938         int scan_starts;
3939
3940         section = get_os_memory_aligned (MAJOR_SECTION_SIZE, TRUE);
3941         section->next_data = section->data = (char*)section + SIZEOF_GC_MEM_SECTION;
3942         g_assert (!((mword)section->data & 7));
3943         section->size = MAJOR_SECTION_SIZE - SIZEOF_GC_MEM_SECTION;
3944         section->end_data = section->data + section->size;
3945         UPDATE_HEAP_BOUNDARIES (section->data, section->end_data);
3946         total_alloc += section->size;
3947         DEBUG (3, fprintf (gc_debug_file, "New major heap section: (%p-%p), total: %zd\n", section->data, section->end_data, total_alloc));
3948         scan_starts = (section->size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
3949         section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
3950         section->num_scan_start = scan_starts;
3951         section->block.role = MEMORY_ROLE_GEN1;
3952         section->is_to_space = TRUE;
3953
3954         /* add to the section list */
3955         section->block.next = section_list;
3956         section_list = section;
3957
3958         ++num_major_sections;
3959
3960         return section;
3961 }
3962
3963 static void
3964 free_major_section (GCMemSection *section)
3965 {
3966         DEBUG (3, fprintf (gc_debug_file, "Freed major section %p (%p-%p)\n", section, section->data, section->end_data));
3967         free_internal_mem (section->scan_starts, INTERNAL_MEM_SCAN_STARTS);
3968         free_os_memory (section, MAJOR_SECTION_SIZE);
3969         total_alloc -= MAJOR_SECTION_SIZE - SIZEOF_GC_MEM_SECTION;
3970
3971         --num_major_sections;
3972 }
3973
3974 /*
3975  * When deciding if it's better to collect or to expand, keep track
3976  * of how much garbage was reclaimed with the last collection: if it's too
3977  * little, expand.
3978  * This is called when we could not allocate a small object.
3979  */
3980 static void __attribute__((noinline))
3981 minor_collect_or_expand_inner (size_t size)
3982 {
3983         int do_minor_collection = 1;
3984
3985         if (!nursery_section) {
3986                 alloc_nursery ();
3987                 return;
3988         }
3989         if (do_minor_collection) {
3990                 stop_world ();
3991                 if (collect_nursery (size))
3992                         major_collection ("minor overflow");
3993                 DEBUG (2, fprintf (gc_debug_file, "Heap size: %zd, LOS size: %zd\n", total_alloc, los_memory_usage));
3994                 restart_world ();
3995                 /* this also sets the proper pointers for the next allocation */
3996                 if (!search_fragment_for_size (size)) {
3997                         int i;
3998                         /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
3999                         DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %zd alloc (%d pinned)\n", size, last_num_pinned));
4000                         for (i = 0; i < last_num_pinned; ++i) {
4001                                 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])));
4002                         }
4003                         degraded_mode = 1;
4004                 }
4005         }
4006         //report_internal_mem_usage ();
4007 }
4008
4009 /*
4010  * ######################################################################
4011  * ########  Memory allocation from the OS
4012  * ######################################################################
4013  * This section of code deals with getting memory from the OS and
4014  * allocating memory for GC-internal data structures.
4015  * Internal memory can be handled with a freelist for small objects.
4016  */
4017
4018 /*
4019  * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
4020  * This must not require any lock.
4021  */
4022 static void*
4023 get_os_memory (size_t size, int activate)
4024 {
4025         void *ptr;
4026         unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
4027
4028         prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
4029         size += pagesize - 1;
4030         size &= ~(pagesize - 1);
4031         ptr = mono_valloc (0, size, prot_flags);
4032         return ptr;
4033 }
4034
4035 /*
4036  * Free the memory returned by get_os_memory (), returning it to the OS.
4037  */
4038 static void
4039 free_os_memory (void *addr, size_t size)
4040 {
4041         mono_vfree (addr, size);
4042 }
4043
4044 /*
4045  * Debug reporting.
4046  */
4047 static void
4048 report_pinned_chunk (PinnedChunk *chunk, int seq) {
4049         void **p;
4050         int i, free_pages, num_free, free_mem;
4051         free_pages = 0;
4052         for (i = 0; i < chunk->num_pages; ++i) {
4053                 if (!chunk->page_sizes [i])
4054                         free_pages++;
4055         }
4056         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);
4057         free_mem = FREELIST_PAGESIZE * free_pages;
4058         for (i = 0; i < FREELIST_NUM_SLOTS; ++i) {
4059                 if (!chunk->free_list [i])
4060                         continue;
4061                 num_free = 0;
4062                 p = chunk->free_list [i];
4063                 while (p) {
4064                         num_free++;
4065                         p = *p;
4066                 }
4067                 printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
4068                 free_mem += freelist_sizes [i] * num_free;
4069         }
4070         printf ("\tfree memory in chunk: %d\n", free_mem);
4071 }
4072
4073 /*
4074  * Debug reporting.
4075  */
4076 static G_GNUC_UNUSED void
4077 report_internal_mem_usage (void) {
4078         PinnedChunk *chunk;
4079         int i;
4080         printf ("Internal memory usage:\n");
4081         i = 0;
4082         for (chunk = internal_chunk_list; chunk; chunk = chunk->block.next) {
4083                 report_pinned_chunk (chunk, i++);
4084         }
4085         printf ("Pinned memory usage:\n");
4086         i = 0;
4087         for (chunk = pinned_chunk_list; chunk; chunk = chunk->block.next) {
4088                 report_pinned_chunk (chunk, i++);
4089         }
4090 }
4091
4092 /*
4093  * the array of pointers from @start to @end contains conservative
4094  * pointers to objects inside @chunk: mark each referenced object
4095  * with the PIN bit.
4096  */
4097 static void
4098 mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end)
4099 {
4100         for (; start < end; start++) {
4101                 char *addr = *start;
4102                 int offset = (char*)addr - (char*)chunk;
4103                 int page = offset / FREELIST_PAGESIZE;
4104                 int obj_offset = page == 0? offset - ((char*)chunk->start_data - (char*)chunk): offset % FREELIST_PAGESIZE;
4105                 int slot_size = chunk->page_sizes [page];
4106                 void **ptr;
4107                 /* the page is not allocated */
4108                 if (!slot_size)
4109                         continue;
4110                 /* would be faster if we restrict the sizes to power of two,
4111                  * but that's a waste of memory: need to measure. it could reduce
4112                  * fragmentation since there are less pages needed, if for example
4113                  * someone interns strings of each size we end up with one page per
4114                  * interned string (still this is just ~40 KB): with more fine-grained sizes
4115                  * this increases the number of used pages.
4116                  */
4117                 if (page == 0) {
4118                         obj_offset /= slot_size;
4119                         obj_offset *= slot_size;
4120                         addr = (char*)chunk->start_data + obj_offset;
4121                 } else {
4122                         obj_offset /= slot_size;
4123                         obj_offset *= slot_size;
4124                         addr = (char*)chunk + page * FREELIST_PAGESIZE + obj_offset;
4125                 }
4126                 ptr = (void**)addr;
4127                 /* if the vtable is inside the chunk it's on the freelist, so skip */
4128                 if (*ptr && (*ptr < (void*)chunk->start_data || *ptr > (void*)((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE))) {
4129                         binary_protocol_pin (addr, (gpointer)LOAD_VTABLE (addr), safe_object_get_size ((MonoObject*)addr));
4130                         if (heap_dump_file && !object_is_pinned (addr))
4131                                 pin_stats_register_object ((char*) addr, safe_object_get_size ((MonoObject*) addr));
4132                         pin_object (addr);
4133                         DEBUG (6, fprintf (gc_debug_file, "Marked pinned object %p (%s) from roots\n", addr, safe_name (addr)));
4134                 }
4135         }
4136 }
4137
4138 static void
4139 scan_pinned_objects (ScanPinnedObjectCallbackFunc callback, void *callback_data)
4140 {
4141         PinnedChunk *chunk;
4142         int i, obj_size;
4143         char *p, *endp;
4144         void **ptr;
4145         void *end_chunk;
4146         for (chunk = pinned_chunk_list; chunk; chunk = chunk->block.next) {
4147                 end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
4148                 DEBUG (6, fprintf (gc_debug_file, "Scanning pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk));
4149                 for (i = 0; i < chunk->num_pages; ++i) {
4150                         obj_size = chunk->page_sizes [i];
4151                         if (!obj_size)
4152                                 continue;
4153                         p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
4154                         endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
4155                         DEBUG (6, fprintf (gc_debug_file, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp));
4156                         while (p + obj_size <= endp) {
4157                                 ptr = (void**)p;
4158                                 DEBUG (9, fprintf (gc_debug_file, "Considering %p (vtable: %p)\n", ptr, *ptr));
4159                                 /* if the first word (the vtable) is outside the chunk we have an object */
4160                                 if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk))
4161                                         callback (chunk, (char*)ptr, obj_size, callback_data);
4162                                 p += obj_size;
4163                         }
4164                 }
4165         }
4166 }
4167
4168 static void
4169 sweep_pinned_objects_callback (PinnedChunk *chunk, char *ptr, size_t size, void *data)
4170 {
4171         if (object_is_pinned (ptr)) {
4172                 unpin_object (ptr);
4173                 DEBUG (6, fprintf (gc_debug_file, "Unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
4174         } else {
4175                 DEBUG (6, fprintf (gc_debug_file, "Freeing unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
4176                 free_pinned_object (chunk, ptr, size);
4177         }
4178 }
4179
4180 static void
4181 sweep_pinned_objects (void)
4182 {
4183         scan_pinned_objects (sweep_pinned_objects_callback, NULL);
4184 }
4185
4186 static void
4187 scan_object_callback (PinnedChunk *chunk, char *ptr, size_t size, char **data)
4188 {
4189         DEBUG (6, fprintf (gc_debug_file, "Precise object scan of alloc_pinned %p (%s)\n", ptr, safe_name (ptr)));
4190         /* FIXME: Put objects without references into separate chunks
4191            which do not need to be scanned */
4192         scan_object (ptr, data [0], data [1]);
4193 }
4194
4195 static void
4196 scan_from_pinned_objects (char *addr_start, char *addr_end)
4197 {
4198         char *data [2] = { addr_start, addr_end };
4199         scan_pinned_objects ((ScanPinnedObjectCallbackFunc)scan_object_callback, data);
4200 }
4201
4202 /*
4203  * Find the slot number in the freelist for memory chunks that
4204  * can contain @size objects.
4205  */
4206 static int
4207 slot_for_size (size_t size)
4208 {
4209         int slot;
4210         /* do a binary search or lookup table later. */
4211         for (slot = 0; slot < FREELIST_NUM_SLOTS; ++slot) {
4212                 if (freelist_sizes [slot] >= size)
4213                         return slot;
4214         }
4215         g_assert_not_reached ();
4216         return -1;
4217 }
4218
4219 /*
4220  * Build a free list for @size memory chunks from the memory area between
4221  * start_page and end_page.
4222  */
4223 static void
4224 build_freelist (PinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
4225 {
4226         void **p, **end;
4227         int count = 0;
4228         /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
4229         p = (void**)start_page;
4230         end = (void**)(end_page - size);
4231         g_assert (!chunk->free_list [slot]);
4232         chunk->free_list [slot] = p;
4233         while ((char*)p + size <= (char*)end) {
4234                 count++;
4235                 *p = (void*)((char*)p + size);
4236                 p = *p;
4237         }
4238         *p = NULL;
4239         /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
4240 }
4241
4242 static PinnedChunk*
4243 alloc_pinned_chunk (void)
4244 {
4245         PinnedChunk *chunk;
4246         int offset;
4247         int size = MAJOR_SECTION_SIZE;
4248
4249         chunk = get_os_memory_aligned (size, TRUE);
4250         chunk->block.role = MEMORY_ROLE_PINNED;
4251
4252         UPDATE_HEAP_BOUNDARIES (chunk, ((char*)chunk + size));
4253         total_alloc += size;
4254         pinned_chunk_bytes_alloced += size;
4255
4256         /* setup the bookeeping fields */
4257         chunk->num_pages = size / FREELIST_PAGESIZE;
4258         offset = G_STRUCT_OFFSET (PinnedChunk, data);
4259         chunk->page_sizes = (void*)((char*)chunk + offset);
4260         offset += sizeof (int) * chunk->num_pages;
4261         offset += ALLOC_ALIGN - 1;
4262         offset &= ~(ALLOC_ALIGN - 1);
4263         chunk->free_list = (void*)((char*)chunk + offset);
4264         offset += sizeof (void*) * FREELIST_NUM_SLOTS;
4265         offset += ALLOC_ALIGN - 1;
4266         offset &= ~(ALLOC_ALIGN - 1);
4267         chunk->start_data = (void*)((char*)chunk + offset);
4268
4269         /* allocate the first page to the freelist */
4270         chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
4271         build_freelist (chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE, chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
4272         DEBUG (4, fprintf (gc_debug_file, "Allocated pinned chunk %p, size: %d\n", chunk, size));
4273         min_pinned_chunk_addr = MIN (min_pinned_chunk_addr, (char*)chunk->start_data);
4274         max_pinned_chunk_addr = MAX (max_pinned_chunk_addr, ((char*)chunk + size));
4275         return chunk;
4276 }
4277
4278 /* assumes freelist for slot is empty, so try to alloc a new page */
4279 static void*
4280 get_chunk_freelist (PinnedChunk *chunk, int slot)
4281 {
4282         int i;
4283         void **p;
4284         p = chunk->free_list [slot];
4285         if (p) {
4286                 chunk->free_list [slot] = *p;
4287                 return p;
4288         }
4289         for (i = 0; i < chunk->num_pages; ++i) {
4290                 int size;
4291                 if (chunk->page_sizes [i])
4292                         continue;
4293                 size = freelist_sizes [slot];
4294                 chunk->page_sizes [i] = size;
4295                 build_freelist (chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
4296                 break;
4297         }
4298         /* try again */
4299         p = chunk->free_list [slot];
4300         if (p) {
4301                 chunk->free_list [slot] = *p;
4302                 return p;
4303         }
4304         return NULL;
4305 }
4306
4307 static void*
4308 alloc_from_freelist (size_t size)
4309 {
4310         int slot;
4311         void *res = NULL;
4312         PinnedChunk *pchunk;
4313         slot = slot_for_size (size);
4314         /*g_print ("using slot %d for size %d (slot size: %d)\n", slot, size, freelist_sizes [slot]);*/
4315         g_assert (size <= freelist_sizes [slot]);
4316         for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->block.next) {
4317                 void **p = pchunk->free_list [slot];
4318                 if (p) {
4319                         /*g_print ("found freelist for slot %d in chunk %p, returning %p, next %p\n", slot, pchunk, p, *p);*/
4320                         pchunk->free_list [slot] = *p;
4321                         return p;
4322                 }
4323         }
4324         for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->block.next) {
4325                 res = get_chunk_freelist (pchunk, slot);
4326                 if (res)
4327                         return res;
4328         }
4329         pchunk = alloc_pinned_chunk ();
4330         /* FIXME: handle OOM */
4331         pchunk->block.next = pinned_chunk_list;
4332         pinned_chunk_list = pchunk;
4333         res = get_chunk_freelist (pchunk, slot);
4334         return res;
4335 }
4336
4337 /* used for the GC-internal data structures */
4338 static void*
4339 get_internal_mem (size_t size, int type)
4340 {
4341         int slot;
4342         void *res = NULL;
4343         PinnedChunk *pchunk;
4344
4345         if (size > freelist_sizes [FREELIST_NUM_SLOTS - 1]) {
4346                 LargeInternalMemHeader *mh;
4347
4348                 size += sizeof (LargeInternalMemHeader);
4349                 mh = get_os_memory (size, TRUE);
4350                 mh->magic = LARGE_INTERNAL_MEM_HEADER_MAGIC;
4351                 mh->size = size;
4352
4353                 large_internal_bytes_alloced += size;
4354
4355                 return mh->data;
4356         }
4357
4358         slot = slot_for_size (size);
4359         g_assert (size <= freelist_sizes [slot]);
4360
4361         small_internal_mem_bytes [type] += freelist_sizes [slot];
4362
4363         for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
4364                 void **p = pchunk->free_list [slot];
4365                 if (p) {
4366                         pchunk->free_list [slot] = *p;
4367                         memset (p, 0, size);
4368                         return p;
4369                 }
4370         }
4371         for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
4372                 res = get_chunk_freelist (pchunk, slot);
4373                 if (res) {
4374                         memset (res, 0, size);
4375                         return res;
4376                 }
4377         }
4378         pchunk = alloc_pinned_chunk ();
4379         /* FIXME: handle OOM */
4380         pchunk->block.next = internal_chunk_list;
4381         internal_chunk_list = pchunk;
4382         res = get_chunk_freelist (pchunk, slot);
4383         memset (res, 0, size);
4384         return res;
4385 }
4386
4387 static void
4388 free_internal_mem (void *addr, int type)
4389 {
4390         PinnedChunk *pchunk;
4391         LargeInternalMemHeader *mh;
4392         if (!addr)
4393                 return;
4394         for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->block.next) {
4395                 /*printf ("trying to free %p in %p (pages: %d)\n", addr, pchunk, pchunk->num_pages);*/
4396                 if (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE) {
4397                         int offset = (char*)addr - (char*)pchunk;
4398                         int page = offset / FREELIST_PAGESIZE;
4399                         int slot = slot_for_size (pchunk->page_sizes [page]);
4400                         void **p = addr;
4401                         *p = pchunk->free_list [slot];
4402                         pchunk->free_list [slot] = p;
4403
4404                         small_internal_mem_bytes [type] -= freelist_sizes [slot];
4405
4406                         return;
4407                 }
4408         }
4409         mh = (LargeInternalMemHeader*)((char*)addr - G_STRUCT_OFFSET (LargeInternalMemHeader, data));
4410         g_assert (mh->magic == LARGE_INTERNAL_MEM_HEADER_MAGIC);
4411         large_internal_bytes_alloced -= mh->size;
4412         free_os_memory (mh, mh->size);
4413 }
4414
4415 /*
4416  * ######################################################################
4417  * ########  Object allocation
4418  * ######################################################################
4419  * This section of code deals with allocating memory for objects.
4420  * There are several ways:
4421  * *) allocate large objects
4422  * *) allocate normal objects
4423  * *) fast lock-free allocation
4424  * *) allocation of pinned objects
4425  */
4426
4427 static void
4428 free_large_object (LOSObject *obj)
4429 {
4430         size_t size = obj->size;
4431         DEBUG (4, fprintf (gc_debug_file, "Freed large object %p, size %zd\n", obj->data, obj->size));
4432         binary_protocol_empty (obj->data, obj->size);
4433
4434         los_memory_usage -= size;
4435         size += sizeof (LOSObject);
4436         size += pagesize - 1;
4437         size &= ~(pagesize - 1);
4438         total_alloc -= size;
4439         los_num_objects--;
4440         free_os_memory (obj, size);
4441 }
4442
4443 /*
4444  * Objects with size >= 64KB are allocated in the large object space.
4445  * They are currently kept track of with a linked list.
4446  * They don't move, so there is no need to pin them during collection
4447  * and we avoid the memcpy overhead.
4448  */
4449 static void* __attribute__((noinline))
4450 alloc_large_inner (MonoVTable *vtable, size_t size)
4451 {
4452         LOSObject *obj;
4453         void **vtslot;
4454         size_t alloc_size;
4455
4456         g_assert (size > MAX_SMALL_OBJ_SIZE);
4457
4458         if (los_memory_usage > next_los_collection) {
4459                 static mword last_los_memory_usage = 0;
4460
4461                 mword los_memory_alloced;
4462                 mword old_los_memory_usage;
4463                 mword los_memory_saved;
4464                 mword save_target;
4465                 mword allowance_target;
4466                 mword allowance;
4467
4468                 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));
4469                 stop_world ();
4470
4471                 g_assert (los_memory_usage >= last_los_memory_usage);
4472                 los_memory_alloced = los_memory_usage - last_los_memory_usage;
4473                 old_los_memory_usage = los_memory_usage;
4474
4475                 major_collection ("LOS overflow");
4476
4477                 los_memory_saved = MAX (old_los_memory_usage - los_memory_usage, 1);
4478                 save_target = los_memory_usage / 2;
4479                 /*
4480                  * see the comment at the end of major_collection()
4481                  * for the explanation for this calculation.
4482                  */
4483                 allowance_target = (mword)((double)save_target * (double)los_memory_alloced / (double)los_memory_saved);
4484                 allowance = MAX (MIN (allowance_target, los_memory_usage), MIN_LOS_ALLOWANCE);
4485                 next_los_collection = los_memory_usage + allowance;
4486
4487                 last_los_memory_usage = los_memory_usage;
4488
4489                 restart_world ();
4490         }
4491         alloc_size = size;
4492         alloc_size += sizeof (LOSObject);
4493         alloc_size += pagesize - 1;
4494         alloc_size &= ~(pagesize - 1);
4495         /* FIXME: handle OOM */
4496         obj = get_os_memory (alloc_size, TRUE);
4497         obj->size = size;
4498         vtslot = (void**)obj->data;
4499         *vtslot = vtable;
4500         total_alloc += alloc_size;
4501         UPDATE_HEAP_BOUNDARIES (obj->data, (char*)obj->data + size);
4502         obj->next = los_object_list;
4503         los_object_list = obj;
4504         los_memory_usage += size;
4505         los_num_objects++;
4506         DEBUG (4, fprintf (gc_debug_file, "Allocated large object %p, vtable: %p (%s), size: %zd\n", obj->data, vtable, vtable->klass->name, size));
4507         binary_protocol_alloc (obj->data, vtable, size);
4508         return obj->data;
4509 }
4510
4511 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
4512  * an object of size @size
4513  * Return FALSE if not found (which means we need a collection)
4514  */
4515 static gboolean
4516 search_fragment_for_size (size_t size)
4517 {
4518         Fragment *frag, *prev;
4519         DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %zd\n", nursery_frag_real_end, size));
4520
4521         if (nursery_frag_real_end > nursery_next && nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4522                 /* Clear the remaining space, pinning depends on this */
4523                 memset (nursery_next, 0, nursery_frag_real_end - nursery_next);
4524
4525         prev = NULL;
4526         for (frag = nursery_fragments; frag; frag = frag->next) {
4527                 if (size <= (frag->fragment_end - frag->fragment_start)) {
4528                         /* remove from the list */
4529                         if (prev)
4530                                 prev->next = frag->next;
4531                         else
4532                                 nursery_fragments = frag->next;
4533                         nursery_next = frag->fragment_start;
4534                         nursery_frag_real_end = frag->fragment_end;
4535
4536                         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));
4537                         frag->next = fragment_freelist;
4538                         fragment_freelist = frag;
4539                         return TRUE;
4540                 }
4541                 prev = frag;
4542         }
4543         return FALSE;
4544 }
4545
4546 /*
4547  * size is already rounded up and we hold the GC lock.
4548  */
4549 static void*
4550 alloc_degraded (MonoVTable *vtable, size_t size)
4551 {
4552         GCMemSection *section;
4553         void **p = NULL;
4554         g_assert (size <= MAX_SMALL_OBJ_SIZE);
4555         HEAVY_STAT (++stat_objects_alloced_degraded);
4556         HEAVY_STAT (stat_bytes_alloced_degraded += size);
4557         for (section = section_list; section; section = section->block.next) {
4558                 if (section != nursery_section && (section->end_data - section->next_data) >= size) {
4559                         p = (void**)section->next_data;
4560                         break;
4561                 }
4562         }
4563         if (!p) {
4564                 section = alloc_major_section ();
4565                 section->is_to_space = FALSE;
4566                 /* FIXME: handle OOM */
4567                 p = (void**)section->next_data;
4568         }
4569         section->next_data += size;
4570         degraded_mode += size;
4571         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));
4572         *p = vtable;
4573         return p;
4574 }
4575
4576 /*
4577  * Provide a variant that takes just the vtable for small fixed-size objects.
4578  * The aligned size is already computed and stored in vt->gc_descr.
4579  * Note: every SCAN_START_SIZE or so we are given the chance to do some special
4580  * processing. We can keep track of where objects start, for example,
4581  * so when we scan the thread stacks for pinned objects, we can start
4582  * a search for the pinned object in SCAN_START_SIZE chunks.
4583  */
4584 static void*
4585 mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4586 {
4587         /* FIXME: handle OOM */
4588         void **p;
4589         char *new_next;
4590         gboolean res;
4591         TLAB_ACCESS_INIT;
4592
4593         HEAVY_STAT (++stat_objects_alloced);
4594         if (size <= MAX_SMALL_OBJ_SIZE)
4595                 HEAVY_STAT (stat_bytes_alloced += size);
4596         else
4597                 HEAVY_STAT (stat_bytes_alloced_los += size);
4598
4599         size += ALLOC_ALIGN - 1;
4600         size &= ~(ALLOC_ALIGN - 1);
4601
4602         g_assert (vtable->gc_descr);
4603
4604         if (G_UNLIKELY (collect_before_allocs)) {
4605                 if (nursery_section) {
4606                         stop_world ();
4607                         collect_nursery (0);
4608                         restart_world ();
4609                         if (!degraded_mode && !search_fragment_for_size (size)) {
4610                                 // FIXME:
4611                                 g_assert_not_reached ();
4612                         }
4613                 }
4614         }
4615
4616         /*
4617          * We must already have the lock here instead of after the
4618          * fast path because we might be interrupted in the fast path
4619          * (after confirming that new_next < TLAB_TEMP_END) by the GC,
4620          * and we'll end up allocating an object in a fragment which
4621          * no longer belongs to us.
4622          *
4623          * The managed allocator does not do this, but it's treated
4624          * specially by the world-stopping code.
4625          */
4626
4627         if (size > MAX_SMALL_OBJ_SIZE) {
4628                 p = alloc_large_inner (vtable, size);
4629         } else {
4630                 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4631
4632                 p = (void**)TLAB_NEXT;
4633                 /* FIXME: handle overflow */
4634                 new_next = (char*)p + size;
4635                 TLAB_NEXT = new_next;
4636
4637                 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4638                         /* Fast path */
4639
4640                         /* 
4641                          * FIXME: We might need a memory barrier here so the change to tlab_next is 
4642                          * visible before the vtable store.
4643                          */
4644
4645                         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4646                         binary_protocol_alloc (p , vtable, size);
4647                         g_assert (*p == NULL);
4648                         *p = vtable;
4649
4650                         g_assert (TLAB_NEXT == new_next);
4651
4652                         return p;
4653                 }
4654
4655                 /* Slow path */
4656
4657                 /* there are two cases: the object is too big or we run out of space in the TLAB */
4658                 /* we also reach here when the thread does its first allocation after a minor 
4659                  * collection, since the tlab_ variables are initialized to NULL.
4660                  * there can be another case (from ORP), if we cooperate with the runtime a bit:
4661                  * objects that need finalizers can have the high bit set in their size
4662                  * so the above check fails and we can readily add the object to the queue.
4663                  * This avoids taking again the GC lock when registering, but this is moot when
4664                  * doing thread-local allocation, so it may not be a good idea.
4665                  */
4666                 g_assert (TLAB_NEXT == new_next);
4667                 if (TLAB_NEXT >= TLAB_REAL_END) {
4668                         /* 
4669                          * Run out of space in the TLAB. When this happens, some amount of space
4670                          * remains in the TLAB, but not enough to satisfy the current allocation
4671                          * request. Currently, we retire the TLAB in all cases, later we could
4672                          * keep it if the remaining space is above a treshold, and satisfy the
4673                          * allocation directly from the nursery.
4674                          */
4675                         TLAB_NEXT -= size;
4676                         /* when running in degraded mode, we continue allocing that way
4677                          * for a while, to decrease the number of useless nursery collections.
4678                          */
4679                         if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
4680                                 p = alloc_degraded (vtable, size);
4681                                 return p;
4682                         }
4683
4684                         if (size > tlab_size) {
4685                                 /* Allocate directly from the nursery */
4686                                 if (nursery_next + size >= nursery_frag_real_end) {
4687                                         if (!search_fragment_for_size (size)) {
4688                                                 minor_collect_or_expand_inner (size);
4689                                                 if (degraded_mode) {
4690                                                         p = alloc_degraded (vtable, size);
4691                                                         return p;
4692                                                 }
4693                                         }
4694                                 }
4695
4696                                 p = (void*)nursery_next;
4697                                 nursery_next += size;
4698                                 if (nursery_next > nursery_frag_real_end) {
4699                                         // no space left
4700                                         g_assert (0);
4701                                 }
4702
4703                                 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4704                                         memset (p, 0, size);
4705                         } else {
4706                                 if (TLAB_START)
4707                                         DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
4708
4709                                 if (nursery_next + tlab_size >= nursery_frag_real_end) {
4710                                         res = search_fragment_for_size (tlab_size);
4711                                         if (!res) {
4712                                                 minor_collect_or_expand_inner (tlab_size);
4713                                                 if (degraded_mode) {
4714                                                         p = alloc_degraded (vtable, size);
4715                                                         return p;
4716                                                 }
4717                                         }
4718                                 }
4719
4720                                 /* Allocate a new TLAB from the current nursery fragment */
4721                                 TLAB_START = nursery_next;
4722                                 nursery_next += tlab_size;
4723                                 TLAB_NEXT = TLAB_START;
4724                                 TLAB_REAL_END = TLAB_START + tlab_size;
4725                                 TLAB_TEMP_END = TLAB_START + MIN (SCAN_START_SIZE, tlab_size);
4726
4727                                 if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
4728                                         memset (TLAB_START, 0, tlab_size);
4729
4730                                 /* Allocate from the TLAB */
4731                                 p = (void*)TLAB_NEXT;
4732                                 TLAB_NEXT += size;
4733                                 g_assert (TLAB_NEXT <= TLAB_REAL_END);
4734
4735                                 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4736                         }
4737                 } else {
4738                         /* Reached tlab_temp_end */
4739
4740                         /* record the scan start so we can find pinned objects more easily */
4741                         nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
4742                         /* we just bump tlab_temp_end as well */
4743                         TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SCAN_START_SIZE);
4744                         DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
4745                 }
4746         }
4747
4748         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4749         binary_protocol_alloc (p, vtable, size);
4750         *p = vtable;
4751
4752         return p;
4753 }
4754
4755 static void*
4756 mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
4757 {
4758         void **p;
4759         char *new_next;
4760         TLAB_ACCESS_INIT;
4761
4762         size += ALLOC_ALIGN - 1;
4763         size &= ~(ALLOC_ALIGN - 1);
4764
4765         g_assert (vtable->gc_descr);
4766         if (size <= MAX_SMALL_OBJ_SIZE) {
4767                 /* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
4768
4769                 p = (void**)TLAB_NEXT;
4770                 /* FIXME: handle overflow */
4771                 new_next = (char*)p + size;
4772                 TLAB_NEXT = new_next;
4773
4774                 if (G_LIKELY (new_next < TLAB_TEMP_END)) {
4775                         /* Fast path */
4776
4777                         /* 
4778                          * FIXME: We might need a memory barrier here so the change to tlab_next is 
4779                          * visible before the vtable store.
4780                          */
4781
4782                         HEAVY_STAT (++stat_objects_alloced);
4783                         HEAVY_STAT (stat_bytes_alloced += size);
4784
4785                         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4786                         binary_protocol_alloc (p, vtable, size);
4787                         g_assert (*p == NULL);
4788                         *p = vtable;
4789
4790                         g_assert (TLAB_NEXT == new_next);
4791
4792                         return p;
4793                 }
4794         }
4795         return NULL;
4796 }
4797
4798 void*
4799 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
4800 {
4801         void *res;
4802 #ifndef DISABLE_CRITICAL_REGION
4803         TLAB_ACCESS_INIT;
4804         ENTER_CRITICAL_REGION;
4805         res = mono_gc_try_alloc_obj_nolock (vtable, size);
4806         if (res) {
4807                 EXIT_CRITICAL_REGION;
4808                 return res;
4809         }
4810         EXIT_CRITICAL_REGION;
4811 #endif
4812         LOCK_GC;
4813         res = mono_gc_alloc_obj_nolock (vtable, size);
4814         UNLOCK_GC;
4815         return res;
4816 }
4817
4818 void*
4819 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
4820 {
4821         MonoArray *arr;
4822 #ifndef DISABLE_CRITICAL_REGION
4823         TLAB_ACCESS_INIT;
4824         ENTER_CRITICAL_REGION;
4825         arr = mono_gc_try_alloc_obj_nolock (vtable, size);
4826         if (arr) {
4827                 arr->max_length = max_length;
4828                 EXIT_CRITICAL_REGION;
4829                 return arr;
4830         }
4831         EXIT_CRITICAL_REGION;
4832 #endif
4833
4834         LOCK_GC;
4835
4836         arr = mono_gc_alloc_obj_nolock (vtable, size);
4837         arr->max_length = max_length;
4838
4839         UNLOCK_GC;
4840
4841         return arr;
4842 }
4843
4844 void*
4845 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
4846 {
4847         MonoArray *arr;
4848         MonoArrayBounds *bounds;
4849
4850         LOCK_GC;
4851
4852         arr = mono_gc_alloc_obj_nolock (vtable, size);
4853         arr->max_length = max_length;
4854
4855         bounds = (MonoArrayBounds*)((char*)arr + size - bounds_size);
4856         arr->bounds = bounds;
4857
4858         UNLOCK_GC;
4859
4860         return arr;
4861 }
4862
4863 void*
4864 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
4865 {
4866         MonoString *str;
4867 #ifndef DISABLE_CRITICAL_REGION
4868         TLAB_ACCESS_INIT;
4869         ENTER_CRITICAL_REGION;
4870         str = mono_gc_try_alloc_obj_nolock (vtable, size);
4871         if (str) {
4872                 str->length = len;
4873                 EXIT_CRITICAL_REGION;
4874                 return str;
4875         }
4876         EXIT_CRITICAL_REGION;
4877 #endif
4878
4879         LOCK_GC;
4880
4881         str = mono_gc_alloc_obj_nolock (vtable, size);
4882         str->length = len;
4883
4884         UNLOCK_GC;
4885
4886         return str;
4887 }
4888
4889 /*
4890  * To be used for interned strings and possibly MonoThread, reflection handles.
4891  * We may want to explicitly free these objects.
4892  */
4893 void*
4894 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
4895 {
4896         /* FIXME: handle OOM */
4897         void **p;
4898         size += ALLOC_ALIGN - 1;
4899         size &= ~(ALLOC_ALIGN - 1);
4900         LOCK_GC;
4901         if (size > MAX_FREELIST_SIZE) {
4902                 /* large objects are always pinned anyway */
4903                 p = alloc_large_inner (vtable, size);
4904         } else {
4905                 p = alloc_from_freelist (size);
4906                 memset (p, 0, size);
4907         }
4908         DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
4909         binary_protocol_alloc (p, vtable, size);
4910         *p = vtable;
4911         UNLOCK_GC;
4912         return p;
4913 }
4914
4915 /*
4916  * ######################################################################
4917  * ########  Finalization support
4918  * ######################################################################
4919  */
4920
4921 /*
4922  * this is valid for the nursery: if the object has been forwarded it means it's
4923  * still refrenced from a root. If it is pinned it's still alive as well.
4924  * Return TRUE if @obj is ready to be finalized.
4925  */
4926 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
4927
4928 static gboolean
4929 is_critical_finalizer (FinalizeEntry *entry)
4930 {
4931         MonoObject *obj;
4932         MonoClass *class;
4933
4934         if (!mono_defaults.critical_finalizer_object)
4935                 return FALSE;
4936
4937         obj = entry->object;
4938         class = ((MonoVTable*)LOAD_VTABLE (obj))->klass;
4939
4940         return mono_class_has_parent (class, mono_defaults.critical_finalizer_object);
4941 }
4942
4943 static void
4944 queue_finalization_entry (FinalizeEntry *entry) {
4945         if (is_critical_finalizer (entry)) {
4946                 entry->next = critical_fin_list;
4947                 critical_fin_list = entry;
4948         } else {
4949                 entry->next = fin_ready_list;
4950                 fin_ready_list = entry;
4951         }
4952 }
4953
4954 /* LOCKING: requires that the GC lock is held */
4955 static void
4956 rehash_fin_table (FinalizeEntryHashTable *hash_table)
4957 {
4958         FinalizeEntry **finalizable_hash = hash_table->table;
4959         mword finalizable_hash_size = hash_table->size;
4960         int i;
4961         unsigned int hash;
4962         FinalizeEntry **new_hash;
4963         FinalizeEntry *entry, *next;
4964         int new_size = g_spaced_primes_closest (hash_table->num_registered);
4965
4966         new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*), INTERNAL_MEM_FIN_TABLE);
4967         for (i = 0; i < finalizable_hash_size; ++i) {
4968                 for (entry = finalizable_hash [i]; entry; entry = next) {
4969                         hash = mono_object_hash (entry->object) % new_size;
4970                         next = entry->next;
4971                         entry->next = new_hash [hash];
4972                         new_hash [hash] = entry;
4973                 }
4974         }
4975         free_internal_mem (finalizable_hash, INTERNAL_MEM_FIN_TABLE);
4976         hash_table->table = new_hash;
4977         hash_table->size = new_size;
4978 }
4979
4980 /* LOCKING: requires that the GC lock is held */
4981 static void
4982 rehash_fin_table_if_necessary (FinalizeEntryHashTable *hash_table)
4983 {
4984         if (hash_table->num_registered >= hash_table->size * 2)
4985                 rehash_fin_table (hash_table);
4986 }
4987
4988 /* LOCKING: requires that the GC lock is held */
4989 static void
4990 finalize_in_range (char *start, char *end, int generation)
4991 {
4992         FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
4993         FinalizeEntry *entry, *prev;
4994         int i;
4995         FinalizeEntry **finalizable_hash = hash_table->table;
4996         mword finalizable_hash_size = hash_table->size;
4997
4998         if (no_finalize)
4999                 return;
5000         for (i = 0; i < finalizable_hash_size; ++i) {
5001                 prev = NULL;
5002                 for (entry = finalizable_hash [i]; entry;) {
5003                         if ((char*)entry->object >= start && (char*)entry->object < end && !object_is_in_to_space (entry->object)) {
5004                                 gboolean is_fin_ready = object_is_fin_ready (entry->object);
5005                                 char *copy = copy_object_no_heap (entry->object, start, end);
5006                                 if (is_fin_ready) {
5007                                         char *from;
5008                                         FinalizeEntry *next;
5009                                         /* remove and put in fin_ready_list */
5010                                         if (prev)
5011                                                 prev->next = entry->next;
5012                                         else
5013                                                 finalizable_hash [i] = entry->next;
5014                                         next = entry->next;
5015                                         num_ready_finalizers++;
5016                                         hash_table->num_registered--;
5017                                         queue_finalization_entry (entry);
5018                                         /* Make it survive */
5019                                         from = entry->object;
5020                                         entry->object = copy;
5021                                         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, hash_table->num_registered));
5022                                         entry = next;
5023                                         continue;
5024                                 } else {
5025                                         char *from = entry->object;
5026                                         if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
5027                                                 FinalizeEntry *next = entry->next;
5028                                                 unsigned int major_hash;
5029                                                 /* remove from the list */
5030                                                 if (prev)
5031                                                         prev->next = entry->next;
5032                                                 else
5033                                                         finalizable_hash [i] = entry->next;
5034                                                 hash_table->num_registered--;
5035
5036                                                 entry->object = copy;
5037
5038                                                 /* insert it into the major hash */
5039                                                 rehash_fin_table_if_necessary (&major_finalizable_hash);
5040                                                 major_hash = mono_object_hash ((MonoObject*) copy) %
5041                                                         major_finalizable_hash.size;
5042                                                 entry->next = major_finalizable_hash.table [major_hash];
5043                                                 major_finalizable_hash.table [major_hash] = entry;
5044                                                 major_finalizable_hash.num_registered++;
5045
5046                                                 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), from));
5047
5048                                                 entry = next;
5049                                                 continue;
5050                                         } else {
5051                                                 /* update pointer */
5052                                                 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", entry->object, safe_name (entry->object), from));
5053                                                 entry->object = copy;
5054                                         }
5055                                 }
5056                         }
5057                         prev = entry;
5058                         entry = entry->next;
5059                 }
5060         }
5061 }
5062
5063 /* LOCKING: requires that the GC lock is held */
5064 static void
5065 null_link_in_range (char *start, char *end, int generation)
5066 {
5067         DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
5068         DisappearingLink **disappearing_link_hash = hash->table;
5069         int disappearing_link_hash_size = hash->size;
5070         DisappearingLink *entry, *prev;
5071         int i;
5072         if (!hash->num_links)
5073                 return;
5074         for (i = 0; i < disappearing_link_hash_size; ++i) {
5075                 prev = NULL;
5076                 for (entry = disappearing_link_hash [i]; entry;) {
5077                         char *object = DISLINK_OBJECT (entry);
5078                         if (object >= start && object < end && !object_is_in_to_space (object)) {
5079                                 gboolean track = DISLINK_TRACK (entry);
5080                                 if (!track && object_is_fin_ready (object)) {
5081                                         void **p = entry->link;
5082                                         DisappearingLink *old;
5083                                         *p = NULL;
5084                                         /* remove from list */
5085                                         if (prev)
5086                                                 prev->next = entry->next;
5087                                         else
5088                                                 disappearing_link_hash [i] = entry->next;
5089                                         DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, object));
5090                                         old = entry->next;
5091                                         free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5092                                         entry = old;
5093                                         hash->num_links--;
5094                                         continue;
5095                                 } else {
5096                                         char *copy = copy_object_no_heap (object, start, end);
5097
5098                                         /* Update pointer if it's moved.  If the object
5099                                          * has been moved out of the nursery, we need to
5100                                          * remove the link from the minor hash table to
5101                                          * the major one.
5102                                          *
5103                                          * FIXME: what if an object is moved earlier?
5104                                          */
5105
5106                                         if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
5107                                                 void **link = entry->link;
5108                                                 DisappearingLink *old;
5109                                                 /* remove from list */
5110                                                 if (prev)
5111                                                         prev->next = entry->next;
5112                                                 else
5113                                                         disappearing_link_hash [i] = entry->next;
5114                                                 old = entry->next;
5115                                                 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5116                                                 entry = old;
5117                                                 hash->num_links--;
5118
5119                                                 add_or_remove_disappearing_link ((MonoObject*)copy, link,
5120                                                         track, GENERATION_OLD);
5121
5122                                                 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
5123
5124                                                 continue;
5125                                         } else {
5126                                                 /* We set the track resurrection bit to
5127                                                  * FALSE if the object is to be finalized
5128                                                  * so that the object can be collected in
5129                                                  * the next cycle (i.e. after it was
5130                                                  * finalized).
5131                                                  */
5132                                                 *entry->link = HIDE_POINTER (copy,
5133                                                         object_is_fin_ready (object) ? FALSE : track);
5134                                                 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->link, DISLINK_OBJECT (entry)));
5135                                         }
5136                                 }
5137                         }
5138                         prev = entry;
5139                         entry = entry->next;
5140                 }
5141         }
5142 }
5143
5144 /* LOCKING: requires that the GC lock is held */
5145 static void
5146 null_links_for_domain (MonoDomain *domain, int generation)
5147 {
5148         DisappearingLinkHashTable *hash = get_dislink_hash_table (generation);
5149         DisappearingLink **disappearing_link_hash = hash->table;
5150         int disappearing_link_hash_size = hash->size;
5151         DisappearingLink *entry, *prev;
5152         int i;
5153         for (i = 0; i < disappearing_link_hash_size; ++i) {
5154                 prev = NULL;
5155                 for (entry = disappearing_link_hash [i]; entry; ) {
5156                         char *object = DISLINK_OBJECT (entry);
5157                         /* FIXME: actually there should be no object
5158                            left in the domain with a non-null vtable
5159                            (provided we remove the Thread special
5160                            case) */
5161                         if (object && (!((MonoObject*)object)->vtable || mono_object_domain (object) == domain)) {
5162                                 DisappearingLink *next = entry->next;
5163
5164                                 if (prev)
5165                                         prev->next = next;
5166                                 else
5167                                         disappearing_link_hash [i] = next;
5168
5169                                 if (*(entry->link)) {
5170                                         *(entry->link) = NULL;
5171                                         g_warning ("Disappearing link %p not freed", entry->link);
5172                                 } else {
5173                                         free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5174                                 }
5175
5176                                 entry = next;
5177                                 continue;
5178                         }
5179                         prev = entry;
5180                         entry = entry->next;
5181                 }
5182         }
5183 }
5184
5185 /* LOCKING: requires that the GC lock is held */
5186 static int
5187 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
5188         FinalizeEntryHashTable *hash_table)
5189 {
5190         FinalizeEntry **finalizable_hash = hash_table->table;
5191         mword finalizable_hash_size = hash_table->size;
5192         FinalizeEntry *entry, *prev;
5193         int i, count;
5194
5195         if (no_finalize || !out_size || !out_array)
5196                 return 0;
5197         count = 0;
5198         for (i = 0; i < finalizable_hash_size; ++i) {
5199                 prev = NULL;
5200                 for (entry = finalizable_hash [i]; entry;) {
5201                         if (mono_object_domain (entry->object) == domain) {
5202                                 FinalizeEntry *next;
5203                                 /* remove and put in out_array */
5204                                 if (prev)
5205                                         prev->next = entry->next;
5206                                 else
5207                                         finalizable_hash [i] = entry->next;
5208                                 next = entry->next;
5209                                 hash_table->num_registered--;
5210                                 out_array [count ++] = entry->object;
5211                                 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", entry->object, safe_name (entry->object), num_ready_finalizers, hash_table->num_registered));
5212                                 entry = next;
5213                                 if (count == out_size)
5214                                         return count;
5215                                 continue;
5216                         }
5217                         prev = entry;
5218                         entry = entry->next;
5219                 }
5220         }
5221         return count;
5222 }
5223
5224 /**
5225  * mono_gc_finalizers_for_domain:
5226  * @domain: the unloading appdomain
5227  * @out_array: output array
5228  * @out_size: size of output array
5229  *
5230  * Store inside @out_array up to @out_size objects that belong to the unloading
5231  * appdomain @domain. Returns the number of stored items. Can be called repeteadly
5232  * until it returns 0.
5233  * The items are removed from the finalizer data structure, so the caller is supposed
5234  * to finalize them.
5235  * @out_array should be on the stack to allow the GC to know the objects are still alive.
5236  */
5237 int
5238 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
5239 {
5240         int result;
5241
5242         LOCK_GC;
5243         result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
5244         if (result < out_size) {
5245                 result += finalizers_for_domain (domain, out_array + result, out_size - result,
5246                         &major_finalizable_hash);
5247         }
5248         UNLOCK_GC;
5249
5250         return result;
5251 }
5252
5253 static void
5254 register_for_finalization (MonoObject *obj, void *user_data, int generation)
5255 {
5256         FinalizeEntryHashTable *hash_table = get_finalize_entry_hash_table (generation);
5257         FinalizeEntry **finalizable_hash;
5258         mword finalizable_hash_size;
5259         FinalizeEntry *entry, *prev;
5260         unsigned int hash;
5261         if (no_finalize)
5262                 return;
5263         g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
5264         hash = mono_object_hash (obj);
5265         LOCK_GC;
5266         rehash_fin_table_if_necessary (hash_table);
5267         finalizable_hash = hash_table->table;
5268         finalizable_hash_size = hash_table->size;
5269         hash %= finalizable_hash_size;
5270         prev = NULL;
5271         for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
5272                 if (entry->object == obj) {
5273                         if (!user_data) {
5274                                 /* remove from the list */
5275                                 if (prev)
5276                                         prev->next = entry->next;
5277                                 else
5278                                         finalizable_hash [hash] = entry->next;
5279                                 hash_table->num_registered--;
5280                                 DEBUG (5, fprintf (gc_debug_file, "Removed finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, hash_table->num_registered));
5281                                 free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
5282                         }
5283                         UNLOCK_GC;
5284                         return;
5285                 }
5286                 prev = entry;
5287         }
5288         if (!user_data) {
5289                 /* request to deregister, but already out of the list */
5290                 UNLOCK_GC;
5291                 return;
5292         }
5293         entry = get_internal_mem (sizeof (FinalizeEntry), INTERNAL_MEM_FINALIZE_ENTRY);
5294         entry->object = obj;
5295         entry->next = finalizable_hash [hash];
5296         finalizable_hash [hash] = entry;
5297         hash_table->num_registered++;
5298         DEBUG (5, fprintf (gc_debug_file, "Added finalizer %p for object: %p (%s) (%d) to %s table\n", entry, obj, obj->vtable->klass->name, hash_table->num_registered, generation_name (generation)));
5299         UNLOCK_GC;
5300 }
5301
5302 void
5303 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
5304 {
5305         if (ptr_in_nursery (obj))
5306                 register_for_finalization (obj, user_data, GENERATION_NURSERY);
5307         else
5308                 register_for_finalization (obj, user_data, GENERATION_OLD);
5309 }
5310
5311 static void
5312 rehash_dislink (DisappearingLinkHashTable *hash_table)
5313 {
5314         DisappearingLink **disappearing_link_hash = hash_table->table;
5315         int disappearing_link_hash_size = hash_table->size;
5316         int i;
5317         unsigned int hash;
5318         DisappearingLink **new_hash;
5319         DisappearingLink *entry, *next;
5320         int new_size = g_spaced_primes_closest (hash_table->num_links);
5321
5322         new_hash = get_internal_mem (new_size * sizeof (DisappearingLink*), INTERNAL_MEM_DISLINK_TABLE);
5323         for (i = 0; i < disappearing_link_hash_size; ++i) {
5324                 for (entry = disappearing_link_hash [i]; entry; entry = next) {
5325                         hash = mono_aligned_addr_hash (entry->link) % new_size;
5326                         next = entry->next;
5327                         entry->next = new_hash [hash];
5328                         new_hash [hash] = entry;
5329                 }
5330         }
5331         free_internal_mem (disappearing_link_hash, INTERNAL_MEM_DISLINK_TABLE);
5332         hash_table->table = new_hash;
5333         hash_table->size = new_size;
5334 }
5335
5336 /* LOCKING: assumes the GC lock is held */
5337 static void
5338 add_or_remove_disappearing_link (MonoObject *obj, void **link, gboolean track, int generation)
5339 {
5340         DisappearingLinkHashTable *hash_table = get_dislink_hash_table (generation);
5341         DisappearingLink *entry, *prev;
5342         unsigned int hash;
5343         DisappearingLink **disappearing_link_hash = hash_table->table;
5344         int disappearing_link_hash_size = hash_table->size;
5345
5346         if (hash_table->num_links >= disappearing_link_hash_size * 2) {
5347                 rehash_dislink (hash_table);
5348                 disappearing_link_hash = hash_table->table;
5349                 disappearing_link_hash_size = hash_table->size;
5350         }
5351         /* FIXME: add check that link is not in the heap */
5352         hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
5353         entry = disappearing_link_hash [hash];
5354         prev = NULL;
5355         for (; entry; entry = entry->next) {
5356                 /* link already added */
5357                 if (link == entry->link) {
5358                         /* NULL obj means remove */
5359                         if (obj == NULL) {
5360                                 if (prev)
5361                                         prev->next = entry->next;
5362                                 else
5363                                         disappearing_link_hash [hash] = entry->next;
5364                                 hash_table->num_links--;
5365                                 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n", entry, hash_table->num_links, generation_name (generation)));
5366                                 free_internal_mem (entry, INTERNAL_MEM_DISLINK);
5367                                 *link = NULL;
5368                         } else {
5369                                 *link = HIDE_POINTER (obj, track); /* we allow the change of object */
5370                         }
5371                         return;
5372                 }
5373                 prev = entry;
5374         }
5375         if (obj == NULL)
5376                 return;
5377         entry = get_internal_mem (sizeof (DisappearingLink), INTERNAL_MEM_DISLINK);
5378         *link = HIDE_POINTER (obj, track);
5379         entry->link = link;
5380         entry->next = disappearing_link_hash [hash];
5381         disappearing_link_hash [hash] = entry;
5382         hash_table->num_links++;
5383         DEBUG (5, fprintf (gc_debug_file, "Added dislink %p for object: %p (%s) at %p to %s table\n", entry, obj, obj->vtable->klass->name, link, generation_name (generation)));
5384 }
5385
5386 /* LOCKING: assumes the GC lock is held */
5387 static void
5388 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track)
5389 {
5390         add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_NURSERY);
5391         add_or_remove_disappearing_link (NULL, link, FALSE, GENERATION_OLD);
5392         if (obj) {
5393                 if (ptr_in_nursery (obj))
5394                         add_or_remove_disappearing_link (obj, link, track, GENERATION_NURSERY);
5395                 else
5396                         add_or_remove_disappearing_link (obj, link, track, GENERATION_OLD);
5397         }
5398 }
5399
5400 int
5401 mono_gc_invoke_finalizers (void)
5402 {
5403         FinalizeEntry *entry = NULL;
5404         gboolean entry_is_critical = FALSE;
5405         int count = 0;
5406         void *obj;
5407         /* FIXME: batch to reduce lock contention */
5408         while (fin_ready_list || critical_fin_list) {
5409                 LOCK_GC;
5410
5411                 if (entry) {
5412                         FinalizeEntry **list = entry_is_critical ? &critical_fin_list : &fin_ready_list;
5413
5414                         /* We have finalized entry in the last
5415                            interation, now we need to remove it from
5416                            the list. */
5417                         if (*list == entry)
5418                                 *list = entry->next;
5419                         else {
5420                                 FinalizeEntry *e = *list;
5421                                 while (e->next != entry)
5422                                         e = e->next;
5423                                 e->next = entry->next;
5424                         }
5425                         free_internal_mem (entry, INTERNAL_MEM_FINALIZE_ENTRY);
5426                         entry = NULL;
5427                 }
5428
5429                 /* Now look for the first non-null entry. */
5430                 for (entry = fin_ready_list; entry && !entry->object; entry = entry->next)
5431                         ;
5432                 if (entry) {
5433                         entry_is_critical = FALSE;
5434                 } else {
5435                         entry_is_critical = TRUE;
5436                         for (entry = critical_fin_list; entry && !entry->object; entry = entry->next)
5437                                 ;
5438                 }
5439
5440                 if (entry) {
5441                         g_assert (entry->object);
5442                         num_ready_finalizers--;
5443                         obj = entry->object;
5444                         entry->object = NULL;
5445                         DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
5446                 }
5447
5448                 UNLOCK_GC;
5449
5450                 if (!entry)
5451                         break;
5452
5453                 g_assert (entry->object == NULL);
5454                 count++;
5455                 /* the object is on the stack so it is pinned */
5456                 /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
5457                 mono_gc_run_finalize (obj, NULL);
5458         }
5459         g_assert (!entry);
5460         return count;
5461 }
5462
5463 gboolean
5464 mono_gc_pending_finalizers (void)
5465 {
5466         return fin_ready_list || critical_fin_list;
5467 }
5468
5469 /* Negative value to remove */
5470 void
5471 mono_gc_add_memory_pressure (gint64 value)
5472 {
5473         /* FIXME: Use interlocked functions */
5474         LOCK_GC;
5475         memory_pressure += value;
5476         UNLOCK_GC;
5477 }
5478
5479 /*
5480  * ######################################################################
5481  * ########  registered roots support
5482  * ######################################################################
5483  */
5484
5485 static void
5486 rehash_roots (gboolean pinned)
5487 {
5488         int i;
5489         unsigned int hash;
5490         RootRecord **new_hash;
5491         RootRecord *entry, *next;
5492         int new_size;
5493
5494         new_size = g_spaced_primes_closest (num_roots_entries [pinned]);
5495         new_hash = get_internal_mem (new_size * sizeof (RootRecord*), INTERNAL_MEM_ROOTS_TABLE);
5496         for (i = 0; i < roots_hash_size [pinned]; ++i) {
5497                 for (entry = roots_hash [pinned][i]; entry; entry = next) {
5498                         hash = mono_aligned_addr_hash (entry->start_root) % new_size;
5499                         next = entry->next;
5500                         entry->next = new_hash [hash];
5501                         new_hash [hash] = entry;
5502                 }
5503         }
5504         free_internal_mem (roots_hash [pinned], INTERNAL_MEM_ROOTS_TABLE);
5505         roots_hash [pinned] = new_hash;
5506         roots_hash_size [pinned] = new_size;
5507 }
5508
5509 static RootRecord*
5510 find_root (int root_type, char *start, guint32 addr_hash)
5511 {
5512         RootRecord *new_root;
5513
5514         guint32 hash = addr_hash % roots_hash_size [root_type];
5515         for (new_root = roots_hash [root_type][hash]; new_root; new_root = new_root->next) {
5516                 /* we allow changing the size and the descriptor (for thread statics etc) */
5517                 if (new_root->start_root == start) {
5518                         return new_root;
5519                 }
5520         }
5521
5522         return NULL;
5523 }
5524
5525 /*
5526  * We do not coalesce roots.
5527  */
5528 static int
5529 mono_gc_register_root_inner (char *start, size_t size, void *descr, int root_type)
5530 {
5531         RootRecord *new_root;
5532         unsigned int hash, addr_hash = mono_aligned_addr_hash (start);
5533         int i;
5534         LOCK_GC;
5535         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5536                 if (num_roots_entries [i] >= roots_hash_size [i] * 2)
5537                         rehash_roots (i);
5538         }
5539         for (i = 0; i < ROOT_TYPE_NUM; ++i) {
5540                 new_root = find_root (i, start, addr_hash);
5541                 /* we allow changing the size and the descriptor (for thread statics etc) */
5542                 if (new_root) {
5543                         size_t old_size = new_root->end_root - new_root->start_root;
5544                         new_root->end_root = new_root->start_root + size;
5545                         g_assert (((new_root->root_desc != 0) && (descr != NULL)) ||
5546                                           ((new_root->root_desc == 0) && (descr == NULL)));
5547                         new_root->root_desc = (mword)descr;
5548                         roots_size += size;
5549                         roots_size -= old_size;
5550                         UNLOCK_GC;
5551                         return TRUE;
5552                 }
5553         }
5554         new_root = get_internal_mem (sizeof (RootRecord), INTERNAL_MEM_ROOT_RECORD);
5555         if (new_root) {
5556                 new_root->start_root = start;
5557                 new_root->end_root = new_root->start_root + size;
5558                 new_root->root_desc = (mword)descr;
5559                 roots_size += size;
5560                 hash = addr_hash % roots_hash_size [root_type];
5561                 num_roots_entries [root_type]++;
5562                 new_root->next = roots_hash [root_type] [hash];
5563                 roots_hash [root_type][hash] = new_root;
5564                 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));
5565         } else {
5566                 UNLOCK_GC;
5567                 return FALSE;
5568         }
5569         UNLOCK_GC;
5570         return TRUE;
5571 }
5572
5573 int
5574 mono_gc_register_root (char *start, size_t size, void *descr)
5575 {
5576         return mono_gc_register_root_inner (start, size, descr, descr ? ROOT_TYPE_NORMAL : ROOT_TYPE_PINNED);
5577 }
5578
5579 int
5580 mono_gc_register_root_wbarrier (char *start, size_t size, void *descr)
5581 {
5582         return mono_gc_register_root_inner (start, size, descr, ROOT_TYPE_WBARRIER);
5583 }
5584
5585 void
5586 mono_gc_deregister_root (char* addr)
5587 {
5588         RootRecord *tmp, *prev;
5589         unsigned int hash, addr_hash = mono_aligned_addr_hash (addr);
5590         int root_type;
5591
5592         LOCK_GC;
5593         for (root_type = 0; root_type < ROOT_TYPE_NUM; ++root_type) {
5594                 hash = addr_hash % roots_hash_size [root_type];
5595                 tmp = roots_hash [root_type][hash];
5596                 prev = NULL;
5597                 while (tmp) {
5598                         if (tmp->start_root == (char*)addr) {
5599                                 if (prev)
5600                                         prev->next = tmp->next;
5601                                 else
5602                                         roots_hash [root_type][hash] = tmp->next;
5603                                 roots_size -= (tmp->end_root - tmp->start_root);
5604                                 num_roots_entries [root_type]--;
5605                                 DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
5606                                 free_internal_mem (tmp, INTERNAL_MEM_ROOT_RECORD);
5607                                 break;
5608                         }
5609                         prev = tmp;
5610                         tmp = tmp->next;
5611                 }
5612         }
5613         UNLOCK_GC;
5614 }
5615
5616 /*
5617  * ######################################################################
5618  * ########  Thread handling (stop/start code)
5619  * ######################################################################
5620  */
5621
5622 /* FIXME: handle large/small config */
5623 #define THREAD_HASH_SIZE 11
5624 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
5625
5626 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
5627
5628 #if USE_SIGNAL_BASED_START_STOP_WORLD
5629
5630 static MonoSemType suspend_ack_semaphore;
5631 static MonoSemType *suspend_ack_semaphore_ptr;
5632 static unsigned int global_stop_count = 0;
5633 #ifdef __APPLE__
5634 static int suspend_signal_num = SIGXFSZ;
5635 #else
5636 static int suspend_signal_num = SIGPWR;
5637 #endif
5638 static int restart_signal_num = SIGXCPU;
5639 static sigset_t suspend_signal_mask;
5640 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
5641
5642 /* LOCKING: assumes the GC lock is held */
5643 static SgenThreadInfo*
5644 thread_info_lookup (ARCH_THREAD_TYPE id)
5645 {
5646         unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
5647         SgenThreadInfo *info;
5648
5649         info = thread_table [hash];
5650         while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
5651                 info = info->next;
5652         }
5653         return info;
5654 }
5655
5656 static void
5657 update_current_thread_stack (void *start)
5658 {
5659         void *ptr = cur_thread_regs;
5660         SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
5661         
5662         info->stack_start = align_pointer (&ptr);
5663         g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
5664         ARCH_STORE_REGS (ptr);
5665         info->stopped_regs = ptr;
5666         if (gc_callbacks.thread_suspend_func)
5667                 gc_callbacks.thread_suspend_func (info->runtime_data, NULL);
5668 }
5669
5670 static const char*
5671 signal_desc (int signum)
5672 {
5673         if (signum == suspend_signal_num)
5674                 return "suspend";
5675         if (signum == restart_signal_num)
5676                 return "restart";
5677         return "unknown";
5678 }
5679
5680 /*
5681  * Define this and use the "xdomain-checks" MONO_GC_DEBUG option to
5682  * have cross-domain checks in the write barrier.
5683  */
5684 //#define XDOMAIN_CHECKS_IN_WBARRIER
5685
5686 #ifndef HEAVY_STATISTICS
5687 #define MANAGED_ALLOCATION
5688 #ifndef XDOMAIN_CHECKS_IN_WBARRIER
5689 #define MANAGED_WBARRIER
5690 #endif
5691 #endif
5692
5693 static gboolean
5694 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip);
5695
5696 static void
5697 wait_for_suspend_ack (int count)
5698 {
5699         int i, result;
5700
5701         for (i = 0; i < count; ++i) {
5702                 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
5703                         if (errno != EINTR) {
5704                                 g_error ("sem_wait ()");
5705                         }
5706                 }
5707         }
5708 }
5709
5710 /* LOCKING: assumes the GC lock is held */
5711 static int
5712 thread_handshake (int signum)
5713 {
5714         int count, i, result;
5715         SgenThreadInfo *info;
5716         pthread_t me = pthread_self ();
5717
5718         count = 0;
5719         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5720                 for (info = thread_table [i]; info; info = info->next) {
5721                         DEBUG (4, fprintf (gc_debug_file, "considering thread %p for signal %d (%s)\n", info, signum, signal_desc (signum)));
5722                         if (ARCH_THREAD_EQUALS (info->id, me)) {
5723                                 DEBUG (4, fprintf (gc_debug_file, "Skip (equal): %p, %p\n", (void*)me, (void*)info->id));
5724                                 continue;
5725                         }
5726                         /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
5727                                 continue;*/
5728                         result = pthread_kill (info->id, signum);
5729                         if (result == 0) {
5730                                 DEBUG (4, fprintf (gc_debug_file, "thread %p signal sent\n", info));
5731                                 count++;
5732                         } else {
5733                                 DEBUG (4, fprintf (gc_debug_file, "thread %p signal failed: %d (%s)\n", (void*)info->id, result, strerror (result)));
5734                                 info->skip = 1;
5735                         }
5736                 }
5737         }
5738
5739         wait_for_suspend_ack (count);
5740
5741         return count;
5742 }
5743
5744 static int
5745 restart_threads_until_none_in_managed_allocator (void)
5746 {
5747         SgenThreadInfo *info;
5748         int i, result, num_threads_died = 0;
5749         int sleep_duration = -1;
5750
5751         for (;;) {
5752                 int restart_count = 0, restarted_count = 0;
5753                 /* restart all threads that stopped in the
5754                    allocator */
5755                 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5756                         for (info = thread_table [i]; info; info = info->next) {
5757                                 if (info->skip)
5758                                         continue;
5759                                 if (!info->stack_start || info->in_critical_region ||
5760                                                 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip)) {
5761                                         binary_protocol_thread_restart ((gpointer)info->id);
5762                                         result = pthread_kill (info->id, restart_signal_num);
5763                                         if (result == 0) {
5764                                                 ++restart_count;
5765                                         } else {
5766                                                 info->skip = 1;
5767                                         }
5768                                 } else {
5769                                         /* we set the stopped_ip to
5770                                            NULL for threads which
5771                                            we're not restarting so
5772                                            that we can easily identify
5773                                            the others */
5774                                         info->stopped_ip = NULL;
5775                                         info->stopped_domain = NULL;
5776                                 }
5777                         }
5778                 }
5779                 /* if no threads were restarted, we're done */
5780                 if (restart_count == 0)
5781                         break;
5782
5783                 /* wait for the threads to signal their restart */
5784                 wait_for_suspend_ack (restart_count);
5785
5786                 if (sleep_duration < 0) {
5787                         sched_yield ();
5788                         sleep_duration = 0;
5789                 } else {
5790                         g_usleep (sleep_duration);
5791                         sleep_duration += 10;
5792                 }
5793
5794                 /* stop them again */
5795                 for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5796                         for (info = thread_table [i]; info; info = info->next) {
5797                                 if (info->skip || info->stopped_ip == NULL)
5798                                         continue;
5799                                 result = pthread_kill (info->id, suspend_signal_num);
5800                                 if (result == 0) {
5801                                         ++restarted_count;
5802                                 } else {
5803                                         info->skip = 1;
5804                                 }
5805                         }
5806                 }
5807                 /* some threads might have died */
5808                 num_threads_died += restart_count - restarted_count;
5809                 /* wait for the threads to signal their suspension
5810                    again */
5811                 wait_for_suspend_ack (restart_count);
5812         }
5813
5814         return num_threads_died;
5815 }
5816
5817 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
5818 static void
5819 suspend_handler (int sig, siginfo_t *siginfo, void *context)
5820 {
5821         SgenThreadInfo *info;
5822         pthread_t id;
5823         int stop_count;
5824         int old_errno = errno;
5825         gpointer regs [ARCH_NUM_REGS];
5826         gpointer stack_start;
5827
5828         id = pthread_self ();
5829         info = thread_info_lookup (id);
5830         info->stopped_domain = mono_domain_get ();
5831         info->stopped_ip = (gpointer) ARCH_SIGCTX_IP (context);
5832         stop_count = global_stop_count;
5833         /* duplicate signal */
5834         if (0 && info->stop_count == stop_count) {
5835                 errno = old_errno;
5836                 return;
5837         }
5838 #ifdef HAVE_KW_THREAD
5839         /* update the remset info in the thread data structure */
5840         info->remset = remembered_set;
5841 #endif
5842         stack_start = (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE;
5843         /* If stack_start is not within the limits, then don't set it
5844            in info and we will be restarted. */
5845         if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
5846                 info->stack_start = stack_start;
5847
5848                 ARCH_COPY_SIGCTX_REGS (regs, context);
5849                 info->stopped_regs = regs;
5850         } else {
5851                 g_assert (!info->stack_start);
5852         }
5853
5854         /* Notify the JIT */
5855         if (gc_callbacks.thread_suspend_func)
5856                 gc_callbacks.thread_suspend_func (info->runtime_data, context);
5857
5858         DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5859         /* notify the waiting thread */
5860         MONO_SEM_POST (suspend_ack_semaphore_ptr);
5861         info->stop_count = stop_count;
5862
5863         /* wait until we receive the restart signal */
5864         do {
5865                 info->signal = 0;
5866                 sigsuspend (&suspend_signal_mask);
5867         } while (info->signal != restart_signal_num);
5868
5869         DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5870         /* notify the waiting thread */
5871         MONO_SEM_POST (suspend_ack_semaphore_ptr);
5872
5873         errno = old_errno;
5874 }
5875
5876 static void
5877 restart_handler (int sig)
5878 {
5879         SgenThreadInfo *info;
5880         int old_errno = errno;
5881
5882         info = thread_info_lookup (pthread_self ());
5883         info->signal = restart_signal_num;
5884         DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)ARCH_GET_THREAD ()));
5885
5886         errno = old_errno;
5887 }
5888
5889 static void
5890 acquire_gc_locks (void)
5891 {
5892         LOCK_INTERRUPTION;
5893 }
5894
5895 static void
5896 release_gc_locks (void)
5897 {
5898         UNLOCK_INTERRUPTION;
5899 }
5900
5901 static TV_DECLARE (stop_world_time);
5902 static unsigned long max_pause_usec = 0;
5903
5904 /* LOCKING: assumes the GC lock is held */
5905 static int
5906 stop_world (void)
5907 {
5908         int count;
5909
5910         acquire_gc_locks ();
5911
5912         update_current_thread_stack (&count);
5913
5914         global_stop_count++;
5915         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 ()));
5916         TV_GETTIME (stop_world_time);
5917         count = thread_handshake (suspend_signal_num);
5918         count -= restart_threads_until_none_in_managed_allocator ();
5919         g_assert (count >= 0);
5920         DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
5921         return count;
5922 }
5923
5924 /* LOCKING: assumes the GC lock is held */
5925 static int
5926 restart_world (void)
5927 {
5928         int count, i;
5929         SgenThreadInfo *info;
5930         TV_DECLARE (end_sw);
5931         unsigned long usec;
5932
5933         /* notify the profiler of the leftovers */
5934         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) {
5935                 if (moved_objects_idx) {
5936                         mono_profiler_gc_moves (moved_objects, moved_objects_idx);
5937                         moved_objects_idx = 0;
5938                 }
5939         }
5940         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5941                 for (info = thread_table [i]; info; info = info->next) {
5942                         info->stack_start = NULL;
5943                         info->stopped_regs = NULL;
5944                 }
5945         }
5946
5947         release_gc_locks ();
5948
5949         count = thread_handshake (restart_signal_num);
5950         TV_GETTIME (end_sw);
5951         usec = TV_ELAPSED (stop_world_time, end_sw);
5952         max_pause_usec = MAX (usec, max_pause_usec);
5953         DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
5954         return count;
5955 }
5956
5957 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
5958
5959 void
5960 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
5961 {
5962         gc_callbacks = *callbacks;
5963 }
5964
5965 /* Variables holding start/end nursery so it won't have to be passed at every call */
5966 static void *scan_area_arg_start, *scan_area_arg_end;
5967
5968 void
5969 mono_gc_conservatively_scan_area (void *start, void *end)
5970 {
5971         conservatively_pin_objects_from (start, end, scan_area_arg_start, scan_area_arg_end, PIN_TYPE_STACK);
5972 }
5973
5974 void*
5975 mono_gc_scan_object (void *obj)
5976 {
5977         return copy_object_no_heap (obj, scan_area_arg_start, scan_area_arg_end);
5978 }
5979         
5980 /*
5981  * Mark from thread stacks and registers.
5982  */
5983 static void
5984 scan_thread_data (void *start_nursery, void *end_nursery, gboolean precise)
5985 {
5986         int i;
5987         SgenThreadInfo *info;
5988
5989         scan_area_arg_start = start_nursery;
5990         scan_area_arg_end = end_nursery;
5991
5992         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
5993                 for (info = thread_table [i]; info; info = info->next) {
5994                         if (info->skip) {
5995                                 DEBUG (3, 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));
5996                                 continue;
5997                         }
5998                         DEBUG (3, 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));
5999                         if (gc_callbacks.thread_mark_func && !conservative_stack_mark)
6000                                 gc_callbacks.thread_mark_func (info->runtime_data, info->stack_start, info->stack_end, precise);
6001                         else if (!precise)
6002                                 conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
6003
6004                         if (!precise)
6005                                 conservatively_pin_objects_from (info->stopped_regs, info->stopped_regs + ARCH_NUM_REGS,
6006                                                 start_nursery, end_nursery, PIN_TYPE_STACK);
6007                 }
6008         }
6009 }
6010
6011 static void
6012 find_pinning_ref_from_thread (char *obj, size_t size)
6013 {
6014         int i;
6015         SgenThreadInfo *info;
6016         char *endobj = obj + size;
6017
6018         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6019                 for (info = thread_table [i]; info; info = info->next) {
6020                         char **start = (char**)info->stack_start;
6021                         if (info->skip)
6022                                 continue;
6023                         while (start < (char**)info->stack_end) {
6024                                 if (*start >= obj && *start < endobj) {
6025                                         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));
6026                                 }
6027                                 start++;
6028                         }
6029
6030                         /* FIXME: check info->stopped_regs */
6031                 }
6032         }
6033 }
6034
6035 static gboolean
6036 ptr_on_stack (void *ptr)
6037 {
6038         gpointer stack_start = &stack_start;
6039         SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
6040
6041         if (ptr >= stack_start && ptr < (gpointer)info->stack_end)
6042                 return TRUE;
6043         return FALSE;
6044 }
6045
6046 static mword*
6047 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global)
6048 {
6049         void **ptr;
6050         mword count;
6051         mword desc;
6052
6053         if (global)
6054                 HEAVY_STAT (++stat_global_remsets_processed);
6055
6056         /* FIXME: exclude stack locations */
6057         switch ((*p) & REMSET_TYPE_MASK) {
6058         case REMSET_LOCATION:
6059                 ptr = (void**)(*p);
6060                 //__builtin_prefetch (ptr);
6061                 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
6062                         gpointer old = *ptr;
6063                         copy_object (ptr, start_nursery, end_nursery);
6064                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
6065                         if (old)
6066                                 binary_protocol_ptr_update (ptr, old, *ptr, (gpointer)LOAD_VTABLE (*ptr), safe_object_get_size (*ptr));
6067                         if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
6068                                 /*
6069                                  * If the object is pinned, each reference to it from nonpinned objects
6070                                  * becomes part of the global remset, which can grow very large.
6071                                  */
6072                                 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
6073                                 add_to_global_remset (ptr, FALSE);
6074                         }
6075                 } else {
6076                         DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
6077                 }
6078                 return p + 1;
6079         case REMSET_RANGE:
6080                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6081                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
6082                         return p + 2;
6083                 count = p [1];
6084                 while (count-- > 0) {
6085                         copy_object (ptr, start_nursery, end_nursery);
6086                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
6087                         if (!global && *ptr >= start_nursery && *ptr < end_nursery)
6088                                 add_to_global_remset (ptr, FALSE);
6089                         ++ptr;
6090                 }
6091                 return p + 2;
6092         case REMSET_OBJECT:
6093                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6094                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
6095                         return p + 1;
6096                 scan_object ((char*)ptr, start_nursery, end_nursery);
6097                 return p + 1;
6098         case REMSET_OTHER: {
6099                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
6100
6101                 switch (p [1]) {
6102                 case REMSET_VTYPE:
6103                         if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
6104                                 return p + 4;
6105                         desc = p [2];
6106                         count = p [3];
6107                         while (count-- > 0)
6108                                 ptr = (void**) scan_vtype ((char*)ptr, desc, start_nursery, end_nursery);
6109                         return p + 4;
6110                 case REMSET_ROOT_LOCATION:
6111                         /* Same as REMSET_LOCATION, but the address is not required to be in the heap */
6112                         copy_object (ptr, start_nursery, end_nursery);
6113                         DEBUG (9, fprintf (gc_debug_file, "Overwrote root location remset at %p with %p\n", ptr, *ptr));
6114                         if (!global && *ptr >= start_nursery && *ptr < end_nursery) {
6115                                 /*
6116                                  * If the object is pinned, each reference to it from nonpinned objects
6117                                  * becomes part of the global remset, which can grow very large.
6118                                  */
6119                                 DEBUG (9, fprintf (gc_debug_file, "Add to global remset because of pinning %p (%p %s)\n", ptr, *ptr, safe_name (*ptr)));
6120                                 add_to_global_remset (ptr, TRUE);
6121                         }
6122                         return p + 2;
6123                 default:
6124                         g_assert_not_reached ();
6125                 }
6126                 break;
6127         }
6128         default:
6129                 g_assert_not_reached ();
6130         }
6131         return NULL;
6132 }
6133
6134 #ifdef HEAVY_STATISTICS
6135 static mword*
6136 collect_store_remsets (RememberedSet *remset, mword *bumper)
6137 {
6138         mword *p = remset->data;
6139         mword last = 0;
6140         mword last1 = 0;
6141         mword last2 = 0;
6142
6143         while (p < remset->store_next) {
6144                 switch ((*p) & REMSET_TYPE_MASK) {
6145                 case REMSET_LOCATION:
6146                         *bumper++ = *p;
6147                         if (*p == last)
6148                                 ++stat_saved_remsets_1;
6149                         last = *p;
6150                         if (*p == last1 || *p == last2) {
6151                                 ++stat_saved_remsets_2;
6152                         } else {
6153                                 last2 = last1;
6154                                 last1 = *p;
6155                         }
6156                         p += 1;
6157                         break;
6158                 case REMSET_RANGE:
6159                         p += 2;
6160                         break;
6161                 case REMSET_OBJECT:
6162                         p += 1;
6163                         break;
6164                 case REMSET_OTHER:
6165                         switch (p [1]) {
6166                         case REMSET_VTYPE:
6167                                 p += 4;
6168                                 break;
6169                         case REMSET_ROOT_LOCATION:
6170                                 p += 2;
6171                                 break;
6172                         default:
6173                                 g_assert_not_reached ();
6174                         }
6175                         break;
6176                 default:
6177                         g_assert_not_reached ();
6178                 }
6179         }
6180
6181         return bumper;
6182 }
6183
6184 static void
6185 remset_stats (void)
6186 {
6187         RememberedSet *remset;
6188         int size = 0;
6189         SgenThreadInfo *info;
6190         int i;
6191         mword *addresses, *bumper, *p, *r;
6192
6193         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6194                 for (info = thread_table [i]; info; info = info->next) {
6195                         for (remset = info->remset; remset; remset = remset->next)
6196                                 size += remset->store_next - remset->data;
6197                 }
6198         }
6199         for (remset = freed_thread_remsets; remset; remset = remset->next)
6200                 size += remset->store_next - remset->data;
6201         for (remset = global_remset; remset; remset = remset->next)
6202                 size += remset->store_next - remset->data;
6203
6204         bumper = addresses = get_internal_mem (sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
6205
6206         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6207                 for (info = thread_table [i]; info; info = info->next) {
6208                         for (remset = info->remset; remset; remset = remset->next)
6209                                 bumper = collect_store_remsets (remset, bumper);
6210                 }
6211         }
6212         for (remset = global_remset; remset; remset = remset->next)
6213                 bumper = collect_store_remsets (remset, bumper);
6214         for (remset = freed_thread_remsets; remset; remset = remset->next)
6215                 bumper = collect_store_remsets (remset, bumper);
6216
6217         g_assert (bumper <= addresses + size);
6218
6219         stat_store_remsets += bumper - addresses;
6220
6221         sort_addresses ((void**)addresses, bumper - addresses);
6222         p = addresses;
6223         r = addresses + 1;
6224         while (r < bumper) {
6225                 if (*r != *p)
6226                         *++p = *r;
6227                 ++r;
6228         }
6229
6230         stat_store_remsets_unique += p - addresses;
6231
6232         free_internal_mem (addresses, INTERNAL_MEM_STATISTICS);
6233 }
6234 #endif
6235
6236 static void
6237 clear_thread_store_remset_buffer (SgenThreadInfo *info)
6238 {
6239         *info->store_remset_buffer_index_addr = 0;
6240         memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6241 }
6242
6243 static void
6244 scan_from_remsets (void *start_nursery, void *end_nursery)
6245 {
6246         int i;
6247         SgenThreadInfo *info;
6248         RememberedSet *remset;
6249         GenericStoreRememberedSet *store_remset;
6250         mword *p, *next_p, *store_pos;
6251
6252 #ifdef HEAVY_STATISTICS
6253         remset_stats ();
6254 #endif
6255
6256         /* the global one */
6257         for (remset = global_remset; remset; remset = remset->next) {
6258                 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));
6259                 store_pos = remset->data;
6260                 for (p = remset->data; p < remset->store_next; p = next_p) {
6261                         mword ptr;
6262
6263                         next_p = handle_remset (p, start_nursery, end_nursery, TRUE);
6264
6265                         /* 
6266                          * Clear global remsets of locations which no longer point to the 
6267                          * nursery. Otherwise, they could grow indefinitely between major 
6268                          * collections.
6269                          */
6270                         ptr = (p [0] & ~REMSET_TYPE_MASK);
6271                         if ((p [0] & REMSET_TYPE_MASK) == REMSET_LOCATION) {
6272                                 if (ptr_in_nursery (*(void**)ptr))
6273                                         *store_pos ++ = p [0];
6274                         } else {
6275                                 g_assert ((p [0] & REMSET_TYPE_MASK) == REMSET_OTHER);
6276                                 g_assert (p [1] == REMSET_ROOT_LOCATION);
6277                                 if (ptr_in_nursery (*(void**)ptr)) {
6278                                         *store_pos ++ = p [0];
6279                                         *store_pos ++ = p [1];
6280                                 }
6281                         }
6282                 }
6283
6284                 /* Truncate the remset */
6285                 remset->store_next = store_pos;
6286         }
6287
6288         /* the generic store ones */
6289         store_remset = generic_store_remsets;
6290         while (store_remset) {
6291                 GenericStoreRememberedSet *next = store_remset->next;
6292
6293                 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
6294                         gpointer addr = store_remset->data [i];
6295                         if (addr)
6296                                 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE);
6297                 }
6298
6299                 free_internal_mem (store_remset, INTERNAL_MEM_STORE_REMSET);
6300
6301                 store_remset = next;
6302         }
6303         generic_store_remsets = NULL;
6304
6305         /* the per-thread ones */
6306         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6307                 for (info = thread_table [i]; info; info = info->next) {
6308                         RememberedSet *next;
6309                         int j;
6310                         for (remset = info->remset; remset; remset = next) {
6311                                 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));
6312                                 for (p = remset->data; p < remset->store_next;) {
6313                                         p = handle_remset (p, start_nursery, end_nursery, FALSE);
6314                                 }
6315                                 remset->store_next = remset->data;
6316                                 next = remset->next;
6317                                 remset->next = NULL;
6318                                 if (remset != info->remset) {
6319                                         DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6320                                         free_internal_mem (remset, INTERNAL_MEM_REMSET);
6321                                 }
6322                         }
6323                         for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
6324                                 handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE);
6325                         clear_thread_store_remset_buffer (info);
6326                 }
6327         }
6328
6329         /* the freed thread ones */
6330         while (freed_thread_remsets) {
6331                 RememberedSet *next;
6332                 remset = freed_thread_remsets;
6333                 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for freed thread, range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
6334                 for (p = remset->data; p < remset->store_next;) {
6335                         p = handle_remset (p, start_nursery, end_nursery, FALSE);
6336                 }
6337                 next = remset->next;
6338                 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6339                 free_internal_mem (remset, INTERNAL_MEM_REMSET);
6340                 freed_thread_remsets = next;
6341         }
6342 }
6343
6344 /*
6345  * Clear the info in the remembered sets: we're doing a major collection, so
6346  * the per-thread ones are not needed and the global ones will be reconstructed
6347  * during the copy.
6348  */
6349 static void
6350 clear_remsets (void)
6351 {
6352         int i;
6353         SgenThreadInfo *info;
6354         RememberedSet *remset, *next;
6355
6356         /* the global list */
6357         for (remset = global_remset; remset; remset = next) {
6358                 remset->store_next = remset->data;
6359                 next = remset->next;
6360                 remset->next = NULL;
6361                 if (remset != global_remset) {
6362                         DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6363                         free_internal_mem (remset, INTERNAL_MEM_REMSET);
6364                 }
6365         }
6366         /* the generic store ones */
6367         while (generic_store_remsets) {
6368                 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
6369                 free_internal_mem (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
6370                 generic_store_remsets = gs_next;
6371         }
6372         /* the per-thread ones */
6373         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6374                 for (info = thread_table [i]; info; info = info->next) {
6375                         for (remset = info->remset; remset; remset = next) {
6376                                 remset->store_next = remset->data;
6377                                 next = remset->next;
6378                                 remset->next = NULL;
6379                                 if (remset != info->remset) {
6380                                         DEBUG (3, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
6381                                         free_internal_mem (remset, INTERNAL_MEM_REMSET);
6382                                 }
6383                         }
6384                         clear_thread_store_remset_buffer (info);
6385                 }
6386         }
6387
6388         /* the freed thread ones */
6389         while (freed_thread_remsets) {
6390                 next = freed_thread_remsets->next;
6391                 DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", freed_thread_remsets->data));
6392                 free_internal_mem (freed_thread_remsets, INTERNAL_MEM_REMSET);
6393                 freed_thread_remsets = next;
6394         }
6395 }
6396
6397 /*
6398  * Clear the thread local TLAB variables for all threads.
6399  */
6400 static void
6401 clear_tlabs (void)
6402 {
6403         SgenThreadInfo *info;
6404         int i;
6405
6406         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
6407                 for (info = thread_table [i]; info; info = info->next) {
6408                         /* A new TLAB will be allocated when the thread does its first allocation */
6409                         *info->tlab_start_addr = NULL;
6410                         *info->tlab_next_addr = NULL;
6411                         *info->tlab_temp_end_addr = NULL;
6412                         *info->tlab_real_end_addr = NULL;
6413                 }
6414         }
6415 }
6416
6417 /* LOCKING: assumes the GC lock is held */
6418 static SgenThreadInfo*
6419 gc_register_current_thread (void *addr)
6420 {
6421         int hash;
6422         SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
6423 #ifndef HAVE_KW_THREAD
6424         SgenThreadInfo *__thread_info__ = info;
6425 #endif
6426
6427         if (!info)
6428                 return NULL;
6429
6430         memset (info, 0, sizeof (SgenThreadInfo));
6431 #ifndef HAVE_KW_THREAD
6432         info->tlab_start = info->tlab_next = info->tlab_temp_end = info->tlab_real_end = NULL;
6433
6434         g_assert (!pthread_getspecific (thread_info_key));
6435         pthread_setspecific (thread_info_key, info);
6436 #else
6437         thread_info = info;
6438 #endif
6439
6440         info->id = ARCH_GET_THREAD ();
6441         info->stop_count = -1;
6442         info->skip = 0;
6443         info->signal = 0;
6444         info->stack_start = NULL;
6445         info->tlab_start_addr = &TLAB_START;
6446         info->tlab_next_addr = &TLAB_NEXT;
6447         info->tlab_temp_end_addr = &TLAB_TEMP_END;
6448         info->tlab_real_end_addr = &TLAB_REAL_END;
6449         info->store_remset_buffer_addr = &STORE_REMSET_BUFFER;
6450         info->store_remset_buffer_index_addr = &STORE_REMSET_BUFFER_INDEX;
6451         info->stopped_ip = NULL;
6452         info->stopped_domain = NULL;
6453         info->stopped_regs = NULL;
6454
6455         binary_protocol_thread_register ((gpointer)info->id);
6456
6457 #ifdef HAVE_KW_THREAD
6458         tlab_next_addr = &tlab_next;
6459         store_remset_buffer_index_addr = &store_remset_buffer_index;
6460 #endif
6461
6462         /* try to get it with attributes first */
6463 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
6464         {
6465                 size_t size;
6466                 void *sstart;
6467                 pthread_attr_t attr;
6468                 pthread_getattr_np (pthread_self (), &attr);
6469                 pthread_attr_getstack (&attr, &sstart, &size);
6470                 info->stack_start_limit = sstart;
6471                 info->stack_end = (char*)sstart + size;
6472                 pthread_attr_destroy (&attr);
6473         }
6474 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
6475                  info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
6476                  info->stack_start_limit = (char*)info->stack_end - pthread_get_stacksize_np (pthread_self ());
6477 #else
6478         {
6479                 /* FIXME: we assume the stack grows down */
6480                 gsize stack_bottom = (gsize)addr;
6481                 stack_bottom += 4095;
6482                 stack_bottom &= ~4095;
6483                 info->stack_end = (char*)stack_bottom;
6484         }
6485 #endif
6486
6487 #ifdef HAVE_KW_THREAD
6488         stack_end = info->stack_end;
6489 #endif
6490
6491         /* hash into the table */
6492         hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
6493         info->next = thread_table [hash];
6494         thread_table [hash] = info;
6495
6496         info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
6497         pthread_setspecific (remembered_set_key, info->remset);
6498 #ifdef HAVE_KW_THREAD
6499         remembered_set = info->remset;
6500 #endif
6501
6502         STORE_REMSET_BUFFER = get_internal_mem (sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE, INTERNAL_MEM_STORE_REMSET);
6503         STORE_REMSET_BUFFER_INDEX = 0;
6504
6505         DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
6506
6507         if (gc_callbacks.thread_attach_func)
6508                 info->runtime_data = gc_callbacks.thread_attach_func ();
6509
6510         return info;
6511 }
6512
6513 static void
6514 add_generic_store_remset_from_buffer (gpointer *buffer)
6515 {
6516         GenericStoreRememberedSet *remset = get_internal_mem (sizeof (GenericStoreRememberedSet), INTERNAL_MEM_STORE_REMSET);
6517         memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
6518         remset->next = generic_store_remsets;
6519         generic_store_remsets = remset;
6520 }
6521
6522 static void
6523 unregister_current_thread (void)
6524 {
6525         int hash;
6526         SgenThreadInfo *prev = NULL;
6527         SgenThreadInfo *p;
6528         RememberedSet *rset;
6529         ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
6530
6531         binary_protocol_thread_unregister ((gpointer)id);
6532
6533         hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
6534         p = thread_table [hash];
6535         assert (p);
6536         DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
6537         while (!ARCH_THREAD_EQUALS (p->id, id)) {
6538                 prev = p;
6539                 p = p->next;
6540         }
6541         if (prev == NULL) {
6542                 thread_table [hash] = p->next;
6543         } else {
6544                 prev->next = p->next;
6545         }
6546         if (p->remset) {
6547                 if (freed_thread_remsets) {
6548                         for (rset = p->remset; rset->next; rset = rset->next)
6549                                 ;
6550                         rset->next = freed_thread_remsets;
6551                         freed_thread_remsets = p->remset;
6552                 } else {
6553                         freed_thread_remsets = p->remset;
6554                 }
6555         }
6556         if (*p->store_remset_buffer_index_addr)
6557                 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
6558         free_internal_mem (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
6559         free (p);
6560 }
6561
6562 static void
6563 unregister_thread (void *k)
6564 {
6565         g_assert (!mono_domain_get ());
6566         LOCK_GC;
6567         unregister_current_thread ();
6568         UNLOCK_GC;
6569 }
6570
6571 gboolean
6572 mono_gc_register_thread (void *baseptr)
6573 {
6574         SgenThreadInfo *info;
6575
6576         LOCK_GC;
6577         init_stats ();
6578         info = thread_info_lookup (ARCH_GET_THREAD ());
6579         if (info == NULL)
6580                 info = gc_register_current_thread (baseptr);
6581         UNLOCK_GC;
6582         return info != NULL;
6583 }
6584
6585 #if USE_PTHREAD_INTERCEPT
6586
6587 #undef pthread_create
6588 #undef pthread_join
6589 #undef pthread_detach
6590
6591 typedef struct {
6592         void *(*start_routine) (void *);
6593         void *arg;
6594         int flags;
6595         MonoSemType registered;
6596 } SgenThreadStartInfo;
6597
6598 static void*
6599 gc_start_thread (void *arg)
6600 {
6601         SgenThreadStartInfo *start_info = arg;
6602         SgenThreadInfo* info;
6603         void *t_arg = start_info->arg;
6604         void *(*start_func) (void*) = start_info->start_routine;
6605         void *result;
6606         int post_result;
6607
6608         LOCK_GC;
6609         info = gc_register_current_thread (&result);
6610         UNLOCK_GC;
6611         post_result = MONO_SEM_POST (&(start_info->registered));
6612         g_assert (!post_result);
6613         result = start_func (t_arg);
6614         g_assert (!mono_domain_get ());
6615         /*
6616          * this is done by the pthread key dtor
6617         LOCK_GC;
6618         unregister_current_thread ();
6619         UNLOCK_GC;
6620         */
6621
6622         return result;
6623 }
6624
6625 int
6626 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
6627 {
6628         SgenThreadStartInfo *start_info;
6629         int result;
6630
6631         start_info = malloc (sizeof (SgenThreadStartInfo));
6632         if (!start_info)
6633                 return ENOMEM;
6634         result = MONO_SEM_INIT (&(start_info->registered), 0);
6635         g_assert (!result);
6636         start_info->arg = arg;
6637         start_info->start_routine = start_routine;
6638
6639         result = pthread_create (new_thread, attr, gc_start_thread, start_info);
6640         if (result == 0) {
6641                 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
6642                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
6643                 }
6644         }
6645         MONO_SEM_DESTROY (&(start_info->registered));
6646         free (start_info);
6647         return result;
6648 }
6649
6650 int
6651 mono_gc_pthread_join (pthread_t thread, void **retval)
6652 {
6653         return pthread_join (thread, retval);
6654 }
6655
6656 int
6657 mono_gc_pthread_detach (pthread_t thread)
6658 {
6659         return pthread_detach (thread);
6660 }
6661
6662 #endif /* USE_PTHREAD_INTERCEPT */
6663
6664 /*
6665  * ######################################################################
6666  * ########  Write barriers
6667  * ######################################################################
6668  */
6669
6670 static RememberedSet*
6671 alloc_remset (int size, gpointer id) {
6672         RememberedSet* res = get_internal_mem (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET);
6673         res->store_next = res->data;
6674         res->end_set = res->data + size;
6675         res->next = NULL;
6676         DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
6677         return res;
6678 }
6679
6680 /*
6681  * Note: the write barriers first do the needed GC work and then do the actual store:
6682  * this way the value is visible to the conservative GC scan after the write barrier
6683  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
6684  * the conservative scan, otherwise by the remembered set scan.
6685  */
6686 void
6687 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
6688 {
6689         RememberedSet *rs;
6690         TLAB_ACCESS_INIT;
6691         HEAVY_STAT (++stat_wbarrier_set_field);
6692         if (ptr_in_nursery (field_ptr)) {
6693                 *(void**)field_ptr = value;
6694                 return;
6695         }
6696         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
6697         LOCK_GC;
6698         rs = REMEMBERED_SET;
6699         if (rs->store_next < rs->end_set) {
6700                 *(rs->store_next++) = (mword)field_ptr;
6701                 *(void**)field_ptr = value;
6702                 UNLOCK_GC;
6703                 return;
6704         }
6705         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6706         rs->next = REMEMBERED_SET;
6707         REMEMBERED_SET = rs;
6708 #ifdef HAVE_KW_THREAD
6709         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6710 #endif
6711         *(rs->store_next++) = (mword)field_ptr;
6712         *(void**)field_ptr = value;
6713         UNLOCK_GC;
6714 }
6715
6716 void
6717 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
6718 {
6719         RememberedSet *rs;
6720         TLAB_ACCESS_INIT;
6721         HEAVY_STAT (++stat_wbarrier_set_arrayref);
6722         if (ptr_in_nursery (slot_ptr)) {
6723                 *(void**)slot_ptr = value;
6724                 return;
6725         }
6726         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
6727         LOCK_GC;
6728         rs = REMEMBERED_SET;
6729         if (rs->store_next < rs->end_set) {
6730                 *(rs->store_next++) = (mword)slot_ptr;
6731                 *(void**)slot_ptr = value;
6732                 UNLOCK_GC;
6733                 return;
6734         }
6735         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6736         rs->next = REMEMBERED_SET;
6737         REMEMBERED_SET = rs;
6738 #ifdef HAVE_KW_THREAD
6739         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6740 #endif
6741         *(rs->store_next++) = (mword)slot_ptr;
6742         *(void**)slot_ptr = value;
6743         UNLOCK_GC;
6744 }
6745
6746 void
6747 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
6748 {
6749         RememberedSet *rs;
6750         TLAB_ACCESS_INIT;
6751         HEAVY_STAT (++stat_wbarrier_arrayref_copy);
6752         LOCK_GC;
6753         memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
6754         if (ptr_in_nursery (dest_ptr)) {
6755                 UNLOCK_GC;
6756                 return;
6757         }
6758         rs = REMEMBERED_SET;
6759         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", dest_ptr, count));
6760         if (rs->store_next + 1 < rs->end_set) {
6761                 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6762                 *(rs->store_next++) = count;
6763                 UNLOCK_GC;
6764                 return;
6765         }
6766         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6767         rs->next = REMEMBERED_SET;
6768         REMEMBERED_SET = rs;
6769 #ifdef HAVE_KW_THREAD
6770         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6771 #endif
6772         *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
6773         *(rs->store_next++) = count;
6774         UNLOCK_GC;
6775 }
6776
6777 static char*
6778 find_object_for_ptr_in_area (char *ptr, char *start, char *end)
6779 {
6780         while (start < end) {
6781                 char *old_start;
6782
6783                 if (!*(void**)start) {
6784                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
6785                         continue;
6786                 }
6787
6788                 old_start = start;
6789
6790                 #define SCAN_OBJECT_NOSCAN
6791                 #include "sgen-scan-object.h"
6792
6793                 if (ptr >= old_start && ptr < start)
6794                         return old_start;
6795         }
6796
6797         return NULL;
6798 }
6799
6800 static char *found_obj;
6801
6802 static void
6803 find_object_for_ptr_in_pinned_chunk_callback (PinnedChunk *chunk, char *obj, size_t size, char *ptr)
6804 {
6805         if (ptr >= obj && ptr < obj + size) {
6806                 g_assert (!found_obj);
6807                 found_obj = obj;
6808         }
6809 }
6810
6811 /* for use in the debugger */
6812 char* find_object_for_ptr (char *ptr);
6813 char*
6814 find_object_for_ptr (char *ptr)
6815 {
6816         GCMemSection *section;
6817         LOSObject *bigobj;
6818
6819         for (section = section_list; section; section = section->block.next) {
6820                 if (ptr >= section->data && ptr < section->end_data)
6821                         return find_object_for_ptr_in_area (ptr, section->data, section->end_data);
6822         }
6823
6824         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
6825                 if (ptr >= bigobj->data && ptr < bigobj->data + bigobj->size)
6826                         return bigobj->data;
6827         }
6828
6829         found_obj = NULL;
6830         scan_pinned_objects ((ScanPinnedObjectCallbackFunc)find_object_for_ptr_in_pinned_chunk_callback, ptr);
6831         return found_obj;
6832 }
6833
6834 static void
6835 evacuate_remset_buffer (void)
6836 {
6837         gpointer *buffer;
6838         TLAB_ACCESS_INIT;
6839
6840         buffer = STORE_REMSET_BUFFER;
6841
6842         add_generic_store_remset_from_buffer (buffer);
6843         memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
6844
6845         STORE_REMSET_BUFFER_INDEX = 0;
6846 }
6847
6848 void
6849 mono_gc_wbarrier_generic_nostore (gpointer ptr)
6850 {
6851         gpointer *buffer;
6852         int index;
6853         TLAB_ACCESS_INIT;
6854
6855         HEAVY_STAT (++stat_wbarrier_generic_store);
6856
6857 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
6858         /* FIXME: ptr_in_heap must be called with the GC lock held */
6859         if (xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) {
6860                 char *start = find_object_for_ptr (ptr);
6861                 MonoObject *value = *(MonoObject**)ptr;
6862                 LOCK_GC;
6863                 g_assert (start);
6864                 if (start) {
6865                         MonoObject *obj = (MonoObject*)start;
6866                         if (obj->vtable->domain != value->vtable->domain)
6867                                 g_assert (is_xdomain_ref_allowed (ptr, start, obj->vtable->domain));
6868                 }
6869                 UNLOCK_GC;
6870         }
6871 #endif
6872
6873         LOCK_GC;
6874
6875         if (*(gpointer*)ptr)
6876                 binary_protocol_wbarrier (ptr, *(gpointer*)ptr, (gpointer)LOAD_VTABLE (*(gpointer*)ptr));
6877
6878         if (ptr_in_nursery (ptr) || ptr_on_stack (ptr) || !ptr_in_nursery (*(gpointer*)ptr)) {
6879                 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
6880                 UNLOCK_GC;
6881                 return;
6882         }
6883
6884         buffer = STORE_REMSET_BUFFER;
6885         index = STORE_REMSET_BUFFER_INDEX;
6886         /* This simple optimization eliminates a sizable portion of
6887            entries.  Comparing it to the last but one entry as well
6888            doesn't eliminate significantly more entries. */
6889         if (buffer [index] == ptr) {
6890                 UNLOCK_GC;
6891                 return;
6892         }
6893
6894         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
6895         HEAVY_STAT (++stat_wbarrier_generic_store_remset);
6896
6897         ++index;
6898         if (index >= STORE_REMSET_BUFFER_SIZE) {
6899                 evacuate_remset_buffer ();
6900                 index = STORE_REMSET_BUFFER_INDEX;
6901                 g_assert (index == 0);
6902                 ++index;
6903         }
6904         buffer [index] = ptr;
6905         STORE_REMSET_BUFFER_INDEX = index;
6906
6907         UNLOCK_GC;
6908 }
6909
6910 void
6911 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
6912 {
6913         DEBUG (8, fprintf (gc_debug_file, "Wbarrier store at %p to %p (%s)\n", ptr, value, value ? safe_name (value) : "null"));
6914         *(void**)ptr = value;
6915         if (ptr_in_nursery (value))
6916                 mono_gc_wbarrier_generic_nostore (ptr);
6917 }
6918
6919 void
6920 mono_gc_wbarrier_set_root (gpointer ptr, MonoObject *value)
6921 {
6922         RememberedSet *rs;
6923         TLAB_ACCESS_INIT;
6924         HEAVY_STAT (++stat_wbarrier_set_root);
6925         if (ptr_in_nursery (ptr))
6926                 return;
6927         DEBUG (8, fprintf (gc_debug_file, "Adding root remset at %p (%s)\n", ptr, value ? safe_name (value) : "null"));
6928
6929         rs = REMEMBERED_SET;
6930         if (rs->store_next + 2 < rs->end_set) {
6931                 *(rs->store_next++) = (mword)ptr | REMSET_OTHER;
6932                 *(rs->store_next++) = (mword)REMSET_ROOT_LOCATION;
6933                 *(void**)ptr = value;
6934                 return;
6935         }
6936         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6937         rs->next = REMEMBERED_SET;
6938         REMEMBERED_SET = rs;
6939 #ifdef HAVE_KW_THREAD
6940         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6941 #endif
6942         *(rs->store_next++) = (mword)ptr | REMSET_OTHER;
6943         *(rs->store_next++) = (mword)REMSET_ROOT_LOCATION;
6944
6945         *(void**)ptr = value;
6946 }
6947
6948 void
6949 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
6950 {
6951         RememberedSet *rs;
6952         TLAB_ACCESS_INIT;
6953         HEAVY_STAT (++stat_wbarrier_value_copy);
6954         g_assert (klass->valuetype);
6955         LOCK_GC;
6956         memmove (dest, src, count * mono_class_value_size (klass, NULL));
6957         rs = REMEMBERED_SET;
6958         if (ptr_in_nursery (dest) || ptr_on_stack (dest)) {
6959                 UNLOCK_GC;
6960                 return;
6961         }
6962         g_assert (klass->gc_descr_inited);
6963         DEBUG (8, fprintf (gc_debug_file, "Adding value remset at %p, count %d, descr %p for class %s (%p)\n", dest, count, klass->gc_descr, klass->name, klass));
6964
6965         if (rs->store_next + 3 < rs->end_set) {
6966                 *(rs->store_next++) = (mword)dest | REMSET_OTHER;
6967                 *(rs->store_next++) = (mword)REMSET_VTYPE;
6968                 *(rs->store_next++) = (mword)klass->gc_descr;
6969                 *(rs->store_next++) = (mword)count;
6970                 UNLOCK_GC;
6971                 return;
6972         }
6973         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
6974         rs->next = REMEMBERED_SET;
6975         REMEMBERED_SET = rs;
6976 #ifdef HAVE_KW_THREAD
6977         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
6978 #endif
6979         *(rs->store_next++) = (mword)dest | REMSET_OTHER;
6980         *(rs->store_next++) = (mword)REMSET_VTYPE;
6981         *(rs->store_next++) = (mword)klass->gc_descr;
6982         *(rs->store_next++) = (mword)count;
6983         UNLOCK_GC;
6984 }
6985
6986 /**
6987  * mono_gc_wbarrier_object_copy:
6988  *
6989  * Write barrier to call when obj is the result of a clone or copy of an object.
6990  */
6991 void
6992 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
6993 {
6994         RememberedSet *rs;
6995         int size;
6996
6997         TLAB_ACCESS_INIT;
6998         HEAVY_STAT (++stat_wbarrier_object_copy);
6999         rs = REMEMBERED_SET;
7000         DEBUG (6, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
7001         size = mono_object_class (obj)->instance_size;
7002         LOCK_GC;
7003         /* do not copy the sync state */
7004         memcpy ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
7005                         size - sizeof (MonoObject));
7006         if (ptr_in_nursery (obj) || ptr_on_stack (obj)) {
7007                 UNLOCK_GC;
7008                 return;
7009         }
7010         if (rs->store_next < rs->end_set) {
7011                 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
7012                 UNLOCK_GC;
7013                 return;
7014         }
7015         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
7016         rs->next = REMEMBERED_SET;
7017         REMEMBERED_SET = rs;
7018 #ifdef HAVE_KW_THREAD
7019         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
7020 #endif
7021         *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
7022         UNLOCK_GC;
7023 }
7024
7025 /*
7026  * ######################################################################
7027  * ########  Collector debugging
7028  * ######################################################################
7029  */
7030
7031 const char*descriptor_types [] = {
7032         "run_length",
7033         "small_bitmap",
7034         "string",
7035         "complex",
7036         "vector",
7037         "array",
7038         "large_bitmap",
7039         "complex_arr"
7040 };
7041
7042 void
7043 describe_ptr (char *ptr)
7044 {
7045         GCMemSection *section;
7046         MonoVTable *vtable;
7047         mword desc;
7048         int type;
7049
7050         if (ptr_in_nursery (ptr)) {
7051                 printf ("Pointer inside nursery.\n");
7052         } else {
7053                 for (section = section_list; section;) {
7054                         if (ptr >= section->data && ptr < section->data + section->size)
7055                                 break;
7056                         section = section->block.next;
7057                 }
7058
7059                 if (section) {
7060                         printf ("Pointer inside oldspace.\n");
7061                 } else if (obj_is_from_pinned_alloc (ptr)) {
7062                         printf ("Pointer is inside a pinned chunk.\n");
7063                 } else {
7064                         printf ("Pointer unknown.\n");
7065                         return;
7066                 }
7067         }
7068
7069         if (object_is_pinned (ptr))
7070                 printf ("Object is pinned.\n");
7071
7072         if (object_is_forwarded (ptr))
7073                 printf ("Object is forwared.\n");
7074
7075         // FIXME: Handle pointers to the inside of objects
7076         vtable = (MonoVTable*)LOAD_VTABLE (ptr);
7077
7078         printf ("VTable: %p\n", vtable);
7079         if (vtable == NULL) {
7080                 printf ("VTable is invalid (empty).\n");
7081                 return;
7082         }
7083         if (ptr_in_nursery (vtable)) {
7084                 printf ("VTable is invalid (points inside nursery).\n");
7085                 return;
7086         }
7087         printf ("Class: %s\n", vtable->klass->name);
7088
7089         desc = ((GCVTable*)vtable)->desc;
7090         printf ("Descriptor: %lx\n", (long)desc);
7091
7092         type = desc & 0x7;
7093         printf ("Descriptor type: %d (%s)\n", type, descriptor_types [type]);
7094 }
7095
7096 static mword*
7097 find_in_remset_loc (mword *p, char *addr, gboolean *found)
7098 {
7099         void **ptr;
7100         mword count, desc;
7101         size_t skip_size;
7102
7103         switch ((*p) & REMSET_TYPE_MASK) {
7104         case REMSET_LOCATION:
7105                 if (*p == (mword)addr)
7106                         *found = TRUE;
7107                 return p + 1;
7108         case REMSET_RANGE:
7109                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
7110                 count = p [1];
7111                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
7112                         *found = TRUE;
7113                 return p + 2;
7114         case REMSET_OBJECT:
7115                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
7116                 count = safe_object_get_size ((MonoObject*)ptr); 
7117                 count += (ALLOC_ALIGN - 1);
7118                 count &= (ALLOC_ALIGN - 1);
7119                 count /= sizeof (mword);
7120                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
7121                         *found = TRUE;
7122                 return p + 1;
7123         case REMSET_OTHER: {
7124                 switch (p [1]) {
7125                 case REMSET_VTYPE:
7126                         ptr = (void**)(*p & ~REMSET_TYPE_MASK);
7127                         desc = p [2];
7128                         count = p [3];
7129
7130                         switch (desc & 0x7) {
7131                         case DESC_TYPE_RUN_LENGTH:
7132                                 OBJ_RUN_LEN_SIZE (skip_size, desc, ptr);
7133                                 break;
7134                         case DESC_TYPE_SMALL_BITMAP:
7135                                 OBJ_BITMAP_SIZE (skip_size, desc, start);
7136                                 break;
7137                         default:
7138                                 // FIXME:
7139                                 g_assert_not_reached ();
7140                         }
7141
7142                         /* The descriptor includes the size of MonoObject */
7143                         skip_size -= sizeof (MonoObject);
7144                         skip_size *= count;
7145                         if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
7146                                 *found = TRUE;
7147
7148                         return p + 4;
7149                 case REMSET_ROOT_LOCATION:
7150                         return p + 2;
7151                 default:
7152                         g_assert_not_reached ();
7153                 }
7154                 break;
7155         }
7156         default:
7157                 g_assert_not_reached ();
7158         }
7159         return NULL;
7160 }
7161
7162 /*
7163  * Return whenever ADDR occurs in the remembered sets
7164  */
7165 static gboolean
7166 find_in_remsets (char *addr)
7167 {
7168         int i;
7169         SgenThreadInfo *info;
7170         RememberedSet *remset;
7171         GenericStoreRememberedSet *store_remset;
7172         mword *p;
7173         gboolean found = FALSE;
7174
7175         /* the global one */
7176         for (remset = global_remset; remset; remset = remset->next) {
7177                 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));
7178                 for (p = remset->data; p < remset->store_next;) {
7179                         p = find_in_remset_loc (p, addr, &found);
7180                         if (found)
7181                                 return TRUE;
7182                 }
7183         }
7184
7185         /* the generic store ones */
7186         for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
7187                 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
7188                         if (store_remset->data [i] == addr)
7189                                 return TRUE;
7190                 }
7191         }
7192
7193         /* the per-thread ones */
7194         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
7195                 for (info = thread_table [i]; info; info = info->next) {
7196                         int j;
7197                         for (remset = info->remset; remset; remset = remset->next) {
7198                                 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));
7199                                 for (p = remset->data; p < remset->store_next;) {
7200                                         p = find_in_remset_loc (p, addr, &found);
7201                                         if (found)
7202                                                 return TRUE;
7203                                 }
7204                         }
7205                         for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
7206                                 if ((*info->store_remset_buffer_addr) [j + 1] == addr)
7207                                         return TRUE;
7208                         }
7209                 }
7210         }
7211
7212         /* the freed thread ones */
7213         for (remset = freed_thread_remsets; remset; remset = remset->next) {
7214                 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for freed thread, range: %p-%p, size: %zd\n", remset->data, remset->store_next, remset->store_next - remset->data));
7215                 for (p = remset->data; p < remset->store_next;) {
7216                         p = find_in_remset_loc (p, addr, &found);
7217                         if (found)
7218                                 return TRUE;
7219                 }
7220         }
7221
7222         return FALSE;
7223 }
7224
7225 static gboolean missing_remsets;
7226
7227 /*
7228  * We let a missing remset slide if the target object is pinned,
7229  * because the store might have happened but the remset not yet added,
7230  * but in that case the target must be pinned.  We might theoretically
7231  * miss some missing remsets this way, but it's very unlikely.
7232  */
7233 #undef HANDLE_PTR
7234 #define HANDLE_PTR(ptr,obj)     do {    \
7235                 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
7236                 if (!find_in_remsets ((char*)(ptr))) { \
7237                 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); \
7238                 binary_protocol_missing_remset ((obj), (gpointer)LOAD_VTABLE ((obj)), (char*)(ptr) - (char*)(obj), *(ptr), (gpointer)LOAD_VTABLE(*(ptr)), object_is_pinned (*(ptr))); \
7239                 if (!object_is_pinned (*(ptr)))                         \
7240                         missing_remsets = TRUE;                         \
7241             } \
7242         } \
7243         } while (0)
7244
7245 /*
7246  * Check that each object reference inside the area which points into the nursery
7247  * can be found in the remembered sets.
7248  */
7249 static void __attribute__((noinline))
7250 check_remsets_for_area (char *start, char *end)
7251 {
7252         GCVTable *vt;
7253         int type_str = 0, type_rlen = 0, type_bitmap = 0, type_vector = 0, type_lbit = 0, type_complex = 0;
7254         while (start < end) {
7255                 if (!*(void**)start) {
7256                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
7257                         continue;
7258                 }
7259                 vt = (GCVTable*)LOAD_VTABLE (start);
7260                 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
7261                 if (0) {
7262                         MonoObject *obj = (MonoObject*)start;
7263                         g_print ("found at %p (0x%lx): %s.%s\n", start, (long)vt->desc, obj->vtable->klass->name_space, obj->vtable->klass->name);
7264                 }
7265
7266 #define SCAN_OBJECT_ACTION COUNT_OBJECT_TYPES
7267 #include "sgen-scan-object.h"
7268         }
7269 }
7270
7271 /*
7272  * Perform consistency check of the heap.
7273  *
7274  * Assumes the world is stopped.
7275  */
7276 void
7277 check_consistency (void)
7278 {
7279         GCMemSection *section;
7280
7281         // Need to add more checks
7282         // FIXME: Create a general heap enumeration function and use that
7283
7284         missing_remsets = FALSE;
7285
7286         DEBUG (1, fprintf (gc_debug_file, "Begin heap consistency check...\n"));
7287
7288         // Check that oldspace->newspace pointers are registered with the collector
7289         for (section = section_list; section; section = section->block.next) {
7290                 if (section->block.role == MEMORY_ROLE_GEN0)
7291                         continue;
7292                 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)));
7293                 check_remsets_for_area (section->data, section->next_data);
7294         }
7295
7296         DEBUG (1, fprintf (gc_debug_file, "Heap consistency check done.\n"));
7297
7298 #ifdef BINARY_PROTOCOL
7299         if (!binary_protocol_file)
7300 #endif
7301                 g_assert (!missing_remsets);
7302 }
7303
7304 /* Check that the reference is valid */
7305 #undef HANDLE_PTR
7306 #define HANDLE_PTR(ptr,obj)     do {    \
7307                 if (*(ptr)) {   \
7308                         g_assert (safe_name (*(ptr)) != NULL);  \
7309                 }       \
7310         } while (0)
7311
7312 /*
7313  * check_object:
7314  *
7315  *   Perform consistency check on an object. Currently we only check that the
7316  * reference fields are valid.
7317  */
7318 char*
7319 check_object (char *start)
7320 {
7321         if (!start)
7322                 return NULL;
7323
7324 #include "sgen-scan-object.h"
7325
7326         return start;
7327 }
7328
7329 /*
7330  * ######################################################################
7331  * ########  Other mono public interface functions.
7332  * ######################################################################
7333  */
7334
7335 void
7336 mono_gc_collect (int generation)
7337 {
7338         LOCK_GC;
7339         stop_world ();
7340         if (generation == 0) {
7341                 collect_nursery (0);
7342         } else {
7343                 major_collection ("user request");
7344         }
7345         restart_world ();
7346         UNLOCK_GC;
7347 }
7348
7349 int
7350 mono_gc_max_generation (void)
7351 {
7352         return 1;
7353 }
7354
7355 int
7356 mono_gc_collection_count (int generation)
7357 {
7358         if (generation == 0)
7359                 return num_minor_gcs;
7360         return num_major_gcs;
7361 }
7362
7363 int64_t
7364 mono_gc_get_used_size (void)
7365 {
7366         gint64 tot = 0;
7367         GCMemSection *section;
7368         LOCK_GC;
7369         tot = los_memory_usage;
7370         for (section = section_list; section; section = section->block.next) {
7371                 /* this is approximate... */
7372                 tot += section->next_data - section->data;
7373         }
7374         /* FIXME: account for pinned objects */
7375         UNLOCK_GC;
7376         return tot;
7377 }
7378
7379 int64_t
7380 mono_gc_get_heap_size (void)
7381 {
7382         return total_alloc;
7383 }
7384
7385 void
7386 mono_gc_disable (void)
7387 {
7388         LOCK_GC;
7389         gc_disabled++;
7390         UNLOCK_GC;
7391 }
7392
7393 void
7394 mono_gc_enable (void)
7395 {
7396         LOCK_GC;
7397         gc_disabled--;
7398         UNLOCK_GC;
7399 }
7400
7401 int
7402 mono_gc_get_los_limit (void)
7403 {
7404         return MAX_SMALL_OBJ_SIZE;
7405 }
7406
7407 gboolean
7408 mono_object_is_alive (MonoObject* o)
7409 {
7410         return TRUE;
7411 }
7412
7413 int
7414 mono_gc_get_generation (MonoObject *obj)
7415 {
7416         if (ptr_in_nursery (obj))
7417                 return 0;
7418         return 1;
7419 }
7420
7421 void
7422 mono_gc_enable_events (void)
7423 {
7424 }
7425
7426 void
7427 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
7428 {
7429         LOCK_GC;
7430         mono_gc_register_disappearing_link (obj, link_addr, track);
7431         UNLOCK_GC;
7432 }
7433
7434 void
7435 mono_gc_weak_link_remove (void **link_addr)
7436 {
7437         LOCK_GC;
7438         mono_gc_register_disappearing_link (NULL, link_addr, FALSE);
7439         UNLOCK_GC;
7440 }
7441
7442 MonoObject*
7443 mono_gc_weak_link_get (void **link_addr)
7444 {
7445         if (!*link_addr)
7446                 return NULL;
7447         return (MonoObject*) REVEAL_POINTER (*link_addr);
7448 }
7449
7450 void*
7451 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
7452 {
7453         if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
7454                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
7455         } else {
7456                 mword complex = alloc_complex_descriptor (bitmap, numbits);
7457                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
7458         }
7459 }
7460
7461 void*
7462 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
7463 {
7464         void *descr;
7465
7466         g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
7467         descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
7468         user_descriptors [user_descriptors_next ++] = marker;
7469
7470         return descr;
7471 }
7472
7473 void*
7474 mono_gc_alloc_fixed (size_t size, void *descr)
7475 {
7476         /* FIXME: do a single allocation */
7477         void *res = calloc (1, size);
7478         if (!res)
7479                 return NULL;
7480         if (!mono_gc_register_root (res, size, descr)) {
7481                 free (res);
7482                 res = NULL;
7483         }
7484         return res;
7485 }
7486
7487 void
7488 mono_gc_free_fixed (void* addr)
7489 {
7490         mono_gc_deregister_root (addr);
7491         free (addr);
7492 }
7493
7494 void*
7495 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
7496 {
7497         void *result;
7498         LOCK_INTERRUPTION;
7499         result = func (data);
7500         UNLOCK_INTERRUPTION;
7501         return result;
7502 }
7503
7504 gboolean
7505 mono_gc_is_gc_thread (void)
7506 {
7507         gboolean result;
7508         LOCK_GC;
7509         result = thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
7510         UNLOCK_GC;
7511         return result;
7512 }
7513
7514 #ifdef USER_CONFIG
7515
7516 /* Tries to extract a number from the passed string, taking in to account m, k
7517  * and g suffixes */
7518 static gboolean
7519 parse_environment_string_extract_number (gchar *str, glong *out)
7520 {
7521         char *endptr;
7522         int len = strlen (str), shift = 0;
7523         glong val;
7524         gboolean is_suffix = FALSE;
7525         char suffix;
7526
7527         switch (str [len - 1]) {
7528                 case 'g':
7529                 case 'G':
7530                         shift += 10;
7531                 case 'm':
7532                 case 'M':
7533                         shift += 10;
7534                 case 'k':
7535                 case 'K':
7536                         shift += 10;
7537                         is_suffix = TRUE;
7538                         suffix = str [len - 1];
7539                         break;
7540         }
7541
7542         errno = 0;
7543         val = strtol (str, &endptr, 10);
7544
7545         if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
7546                         || (errno != 0 && val == 0) || (endptr == str))
7547                 return FALSE;
7548
7549         if (is_suffix) {
7550                 if (*(endptr + 1)) /* Invalid string. */
7551                         return FALSE;
7552                 val <<= shift;
7553         }
7554
7555         *out = val;
7556         return TRUE;
7557 }
7558
7559 #endif 
7560
7561 void
7562 mono_gc_base_init (void)
7563 {
7564         char *env;
7565         char **opts, **ptr;
7566         struct sigaction sinfo;
7567
7568         LOCK_INIT (gc_mutex);
7569         LOCK_GC;
7570         if (gc_initialized) {
7571                 UNLOCK_GC;
7572                 return;
7573         }
7574         pagesize = mono_pagesize ();
7575         gc_debug_file = stderr;
7576
7577 #ifdef USER_CONFIG
7578
7579         if ((env = getenv ("MONO_GC_PARAMS"))) {
7580                 if (g_str_has_prefix (env, "nursery-size")) {
7581                         int index = 0;
7582                         long val;
7583                         while (env [index] && env [index++] != '=')
7584                                 ;
7585                         if (env [index] && parse_environment_string_extract_number (env
7586                                         + index, &val)) {
7587                                 default_nursery_size = val;
7588 #ifdef ALIGN_NURSERY
7589                                 if ((val & (val - 1))) {
7590                                         fprintf (stderr, "The nursery size must be a power of two.\n");
7591                                         exit (1);
7592                                 }
7593
7594                                 default_nursery_bits = 0;
7595                                 while (1 << (++ default_nursery_bits) != default_nursery_size)
7596                                         ;
7597 #endif
7598                         } else {
7599                                 fprintf (stderr, "nursery-size must be an integer.\n");
7600                                 exit (1);
7601                         }
7602                 } else {
7603                         fprintf (stderr, "MONO_GC_PARAMS must be of the form 'nursery-size=N' (where N is an integer, possibly with a k, m or a g suffix).\n");
7604                         exit (1);
7605                 }
7606         }
7607
7608 #endif
7609
7610         nursery_size = DEFAULT_NURSERY_SIZE;
7611
7612         minor_collection_section_allowance = MIN_MINOR_COLLECTION_SECTION_ALLOWANCE;
7613
7614         if ((env = getenv ("MONO_GC_DEBUG"))) {
7615                 opts = g_strsplit (env, ",", -1);
7616                 for (ptr = opts; ptr && *ptr; ptr ++) {
7617                         char *opt = *ptr;
7618                         if (opt [0] >= '0' && opt [0] <= '9') {
7619                                 gc_debug_level = atoi (opt);
7620                                 opt++;
7621                                 if (opt [0] == ':')
7622                                         opt++;
7623                                 if (opt [0]) {
7624                                         char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
7625                                         gc_debug_file = fopen (rf, "wb");
7626                                         if (!gc_debug_file)
7627                                                 gc_debug_file = stderr;
7628                                         g_free (rf);
7629                                 }
7630                         } else if (!strcmp (opt, "collect-before-allocs")) {
7631                                 collect_before_allocs = TRUE;
7632                         } else if (!strcmp (opt, "check-at-minor-collections")) {
7633                                 consistency_check_at_minor_collection = TRUE;
7634                         } else if (!strcmp (opt, "xdomain-checks")) {
7635                                 xdomain_checks = TRUE;
7636                         } else if (!strcmp (opt, "clear-at-gc")) {
7637                                 nursery_clear_policy = CLEAR_AT_GC;
7638                         } else if (!strcmp (opt, "conservative-stack-mark")) {
7639                                 conservative_stack_mark = TRUE;
7640                         } else if (!strcmp (opt, "check-scan-starts")) {
7641                                 do_scan_starts_check = TRUE;
7642                         } else if (g_str_has_prefix (opt, "heap-dump=")) {
7643                                 char *filename = strchr (opt, '=') + 1;
7644                                 nursery_clear_policy = CLEAR_AT_GC;
7645                                 heap_dump_file = fopen (filename, "w");
7646                                 if (heap_dump_file)
7647                                         fprintf (heap_dump_file, "<sgen-dump>\n");
7648 #ifdef BINARY_PROTOCOL
7649                         } else if (g_str_has_prefix (opt, "binary-protocol=")) {
7650                                 char *filename = strchr (opt, '=') + 1;
7651                                 binary_protocol_file = fopen (filename, "w");
7652 #endif
7653                         } else {
7654                                 fprintf (stderr, "Invalid format for the MONO_GC_DEBUG env variable: '%s'\n", env);
7655                                 fprintf (stderr, "The format is: MONO_GC_DEBUG=[l[:filename]|<option>]+ where l is a debug level 0-9.\n");
7656                                 fprintf (stderr, "Valid options are: collect-before-allocs, check-at-minor-collections, xdomain-checks, clear-at-gc.\n");
7657                                 exit (1);
7658                         }
7659                 }
7660                 g_strfreev (opts);
7661         }
7662
7663         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
7664         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
7665
7666         sigfillset (&sinfo.sa_mask);
7667         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
7668         sinfo.sa_sigaction = suspend_handler;
7669         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
7670                 g_error ("failed sigaction");
7671         }
7672
7673         sinfo.sa_handler = restart_handler;
7674         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
7675                 g_error ("failed sigaction");
7676         }
7677
7678         sigfillset (&suspend_signal_mask);
7679         sigdelset (&suspend_signal_mask, restart_signal_num);
7680
7681         global_remset = alloc_remset (1024, NULL);
7682         global_remset->next = NULL;
7683
7684         pthread_key_create (&remembered_set_key, unregister_thread);
7685
7686 #ifndef HAVE_KW_THREAD
7687         pthread_key_create (&thread_info_key, NULL);
7688 #endif
7689
7690         gc_initialized = TRUE;
7691         UNLOCK_GC;
7692         mono_gc_register_thread (&sinfo);
7693 }
7694
7695 int
7696 mono_gc_get_suspend_signal (void)
7697 {
7698         return suspend_signal_num;
7699 }
7700
7701 enum {
7702         ATYPE_NORMAL,
7703         ATYPE_VECTOR,
7704         ATYPE_SMALL,
7705         ATYPE_NUM
7706 };
7707
7708 #ifdef HAVE_KW_THREAD
7709 #define EMIT_TLS_ACCESS(mb,dummy,offset)        do {    \
7710         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
7711         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
7712         mono_mb_emit_i4 ((mb), (offset));               \
7713         } while (0)
7714 #else
7715 #define EMIT_TLS_ACCESS(mb,member,dummy)        do {    \
7716         mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
7717         mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
7718         mono_mb_emit_i4 ((mb), thread_info_key);        \
7719         mono_mb_emit_icon ((mb), G_STRUCT_OFFSET (SgenThreadInfo, member));     \
7720         mono_mb_emit_byte ((mb), CEE_ADD);              \
7721         mono_mb_emit_byte ((mb), CEE_LDIND_I);          \
7722         } while (0)
7723 #endif
7724
7725 #ifdef MANAGED_ALLOCATION
7726 /* FIXME: Do this in the JIT, where specialized allocation sequences can be created
7727  * for each class. This is currently not easy to do, as it is hard to generate basic 
7728  * blocks + branches, but it is easy with the linear IL codebase.
7729  *
7730  * For this to work we'd need to solve the TLAB race, first.  Now we
7731  * require the allocator to be in a few known methods to make sure
7732  * that they are executed atomically via the restart mechanism.
7733  */
7734 static MonoMethod*
7735 create_allocator (int atype)
7736 {
7737         int p_var, size_var;
7738         guint32 slowpath_branch, max_size_branch;
7739         MonoMethodBuilder *mb;
7740         MonoMethod *res;
7741         MonoMethodSignature *csig;
7742         static gboolean registered = FALSE;
7743         int tlab_next_addr_var, new_next_var;
7744         int num_params, i;
7745         const char *name = NULL;
7746         AllocatorWrapperInfo *info;
7747
7748 #ifdef HAVE_KW_THREAD
7749         int tlab_next_addr_offset = -1;
7750         int tlab_temp_end_offset = -1;
7751
7752         MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
7753         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
7754
7755         g_assert (tlab_next_addr_offset != -1);
7756         g_assert (tlab_temp_end_offset != -1);
7757 #endif
7758
7759         if (!registered) {
7760                 mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
7761                 mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
7762                 registered = TRUE;
7763         }
7764
7765         if (atype == ATYPE_SMALL) {
7766                 num_params = 1;
7767                 name = "AllocSmall";
7768         } else if (atype == ATYPE_NORMAL) {
7769                 num_params = 1;
7770                 name = "Alloc";
7771         } else if (atype == ATYPE_VECTOR) {
7772                 num_params = 2;
7773                 name = "AllocVector";
7774         } else {
7775                 g_assert_not_reached ();
7776         }
7777
7778         csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
7779         csig->ret = &mono_defaults.object_class->byval_arg;
7780         for (i = 0; i < num_params; ++i)
7781                 csig->params [i] = &mono_defaults.int_class->byval_arg;
7782
7783         mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
7784         size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
7785         if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7786                 /* size = vtable->klass->instance_size; */
7787                 mono_mb_emit_ldarg (mb, 0);
7788                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7789                 mono_mb_emit_byte (mb, CEE_ADD);
7790                 mono_mb_emit_byte (mb, CEE_LDIND_I);
7791                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, instance_size));
7792                 mono_mb_emit_byte (mb, CEE_ADD);
7793                 /* FIXME: assert instance_size stays a 4 byte integer */
7794                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7795                 mono_mb_emit_stloc (mb, size_var);
7796         } else if (atype == ATYPE_VECTOR) {
7797                 MonoExceptionClause *clause;
7798                 int pos, pos_leave;
7799                 MonoClass *oom_exc_class;
7800                 MonoMethod *ctor;
7801
7802                 /* n >  MONO_ARRAY_MAX_INDEX -> OverflowException */
7803                 mono_mb_emit_ldarg (mb, 1);
7804                 mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
7805                 pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
7806                 mono_mb_emit_exception (mb, "OverflowException", NULL);
7807                 mono_mb_patch_short_branch (mb, pos);
7808
7809                 clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
7810                 clause->try_offset = mono_mb_get_label (mb);
7811
7812                 /* vtable->klass->sizes.element_size */
7813                 mono_mb_emit_ldarg (mb, 0);
7814                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoVTable, klass));
7815                 mono_mb_emit_byte (mb, CEE_ADD);
7816                 mono_mb_emit_byte (mb, CEE_LDIND_I);
7817                 mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
7818                 mono_mb_emit_byte (mb, CEE_ADD);
7819                 mono_mb_emit_byte (mb, CEE_LDIND_U4);
7820
7821                 /* * n */
7822                 mono_mb_emit_ldarg (mb, 1);
7823                 mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
7824                 /* + sizeof (MonoArray) */
7825                 mono_mb_emit_icon (mb, sizeof (MonoArray));
7826                 mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
7827                 mono_mb_emit_stloc (mb, size_var);
7828
7829                 pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
7830
7831                 /* catch */
7832                 clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
7833                 clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
7834                 clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
7835                                 "System", "OverflowException");
7836                 g_assert (clause->data.catch_class);
7837                 clause->handler_offset = mono_mb_get_label (mb);
7838
7839                 oom_exc_class = mono_class_from_name (mono_defaults.corlib,
7840                                 "System", "OutOfMemoryException");
7841                 g_assert (oom_exc_class);
7842                 ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
7843                 g_assert (ctor);
7844
7845                 mono_mb_emit_byte (mb, CEE_POP);
7846                 mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
7847                 mono_mb_emit_byte (mb, CEE_THROW);
7848
7849                 clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
7850                 mono_mb_set_clauses (mb, 1, clause);
7851                 mono_mb_patch_branch (mb, pos_leave);
7852                 /* end catch */
7853         } else {
7854                 g_assert_not_reached ();
7855         }
7856
7857         /* size += ALLOC_ALIGN - 1; */
7858         mono_mb_emit_ldloc (mb, size_var);
7859         mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
7860         mono_mb_emit_byte (mb, CEE_ADD);
7861         /* size &= ~(ALLOC_ALIGN - 1); */
7862         mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
7863         mono_mb_emit_byte (mb, CEE_AND);
7864         mono_mb_emit_stloc (mb, size_var);
7865
7866         /* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
7867         if (atype != ATYPE_SMALL) {
7868                 mono_mb_emit_ldloc (mb, size_var);
7869                 mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
7870                 max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_S);
7871         }
7872
7873         /*
7874          * We need to modify tlab_next, but the JIT only supports reading, so we read
7875          * another tls var holding its address instead.
7876          */
7877
7878         /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
7879         tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7880         EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
7881         mono_mb_emit_stloc (mb, tlab_next_addr_var);
7882
7883         /* p = (void**)tlab_next; */
7884         p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7885         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7886         mono_mb_emit_byte (mb, CEE_LDIND_I);
7887         mono_mb_emit_stloc (mb, p_var);
7888         
7889         /* new_next = (char*)p + size; */
7890         new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
7891         mono_mb_emit_ldloc (mb, p_var);
7892         mono_mb_emit_ldloc (mb, size_var);
7893         mono_mb_emit_byte (mb, CEE_CONV_I);
7894         mono_mb_emit_byte (mb, CEE_ADD);
7895         mono_mb_emit_stloc (mb, new_next_var);
7896
7897         /* tlab_next = new_next */
7898         mono_mb_emit_ldloc (mb, tlab_next_addr_var);
7899         mono_mb_emit_ldloc (mb, new_next_var);
7900         mono_mb_emit_byte (mb, CEE_STIND_I);
7901
7902         /* if (G_LIKELY (new_next < tlab_temp_end)) */
7903         mono_mb_emit_ldloc (mb, new_next_var);
7904         EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
7905         slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
7906
7907         /* Slowpath */
7908         if (atype != ATYPE_SMALL)
7909                 mono_mb_patch_short_branch (mb, max_size_branch);
7910
7911         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
7912         mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
7913
7914         /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
7915         mono_mb_emit_ldarg (mb, 0);
7916         mono_mb_emit_ldloc (mb, size_var);
7917         if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
7918                 mono_mb_emit_icall (mb, mono_gc_alloc_obj);
7919         } else if (atype == ATYPE_VECTOR) {
7920                 mono_mb_emit_ldarg (mb, 1);
7921                 mono_mb_emit_icall (mb, mono_gc_alloc_vector);
7922         } else {
7923                 g_assert_not_reached ();
7924         }
7925         mono_mb_emit_byte (mb, CEE_RET);
7926
7927         /* Fastpath */
7928         mono_mb_patch_short_branch (mb, slowpath_branch);
7929
7930         /* FIXME: Memory barrier */
7931
7932         /* *p = vtable; */
7933         mono_mb_emit_ldloc (mb, p_var);
7934         mono_mb_emit_ldarg (mb, 0);
7935         mono_mb_emit_byte (mb, CEE_STIND_I);
7936
7937         if (atype == ATYPE_VECTOR) {
7938                 /* arr->max_length = max_length; */
7939                 mono_mb_emit_ldloc (mb, p_var);
7940                 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoArray, max_length));
7941                 mono_mb_emit_ldarg (mb, 1);
7942                 mono_mb_emit_byte (mb, CEE_STIND_I);
7943         }
7944
7945         /* return p */
7946         mono_mb_emit_ldloc (mb, p_var);
7947         mono_mb_emit_byte (mb, CEE_RET);
7948
7949         res = mono_mb_create_method (mb, csig, 8);
7950         mono_mb_free (mb);
7951         mono_method_get_header (res)->init_locals = FALSE;
7952
7953         info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
7954         info->alloc_type = atype;
7955         mono_marshal_set_wrapper_info (res, info);
7956
7957         return res;
7958 }
7959 #endif
7960
7961 static MonoMethod* alloc_method_cache [ATYPE_NUM];
7962 static MonoMethod *write_barrier_method;
7963
7964 static gboolean
7965 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
7966 {
7967         MonoJitInfo *ji;
7968         MonoMethod *method;
7969         int i;
7970
7971         if (!ip || !domain)
7972                 return FALSE;
7973         ji = mono_jit_info_table_find (domain, ip);
7974         if (!ji)
7975                 return FALSE;
7976         method = ji->method;
7977
7978         if (method == write_barrier_method)
7979                 return TRUE;
7980         for (i = 0; i < ATYPE_NUM; ++i)
7981                 if (method == alloc_method_cache [i])
7982                         return TRUE;
7983         return FALSE;
7984 }
7985
7986 /*
7987  * Generate an allocator method implementing the fast path of mono_gc_alloc_obj ().
7988  * The signature of the called method is:
7989  *      object allocate (MonoVTable *vtable)
7990  */
7991 MonoMethod*
7992 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
7993 {
7994 #ifdef MANAGED_ALLOCATION
7995         MonoClass *klass = vtable->klass;
7996
7997 #ifdef HAVE_KW_THREAD
7998         int tlab_next_offset = -1;
7999         int tlab_temp_end_offset = -1;
8000         MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
8001         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
8002
8003         if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
8004                 return NULL;
8005 #endif
8006
8007         if (!mono_runtime_has_tls_get ())
8008                 return NULL;
8009         if (klass->instance_size > tlab_size)
8010                 return NULL;
8011         if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
8012                 return NULL;
8013         if (klass->rank)
8014                 return NULL;
8015         if (klass->byval_arg.type == MONO_TYPE_STRING)
8016                 return NULL;
8017         if (collect_before_allocs)
8018                 return NULL;
8019
8020         if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
8021                 return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
8022         else
8023                 return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
8024 #else
8025         return NULL;
8026 #endif
8027 }
8028
8029 MonoMethod*
8030 mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
8031 {
8032 #ifdef MANAGED_ALLOCATION
8033         MonoClass *klass = vtable->klass;
8034
8035 #ifdef HAVE_KW_THREAD
8036         int tlab_next_offset = -1;
8037         int tlab_temp_end_offset = -1;
8038         MONO_THREAD_VAR_OFFSET (tlab_next, tlab_next_offset);
8039         MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
8040
8041         if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
8042                 return NULL;
8043 #endif
8044
8045         if (rank != 1)
8046                 return NULL;
8047         if (!mono_runtime_has_tls_get ())
8048                 return NULL;
8049         if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
8050                 return NULL;
8051         if (collect_before_allocs)
8052                 return NULL;
8053         g_assert (!klass->has_finalize && !klass->marshalbyref);
8054
8055         return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
8056 #else
8057         return NULL;
8058 #endif
8059 }
8060
8061 MonoMethod*
8062 mono_gc_get_managed_allocator_by_type (int atype)
8063 {
8064 #ifdef MANAGED_ALLOCATION
8065         MonoMethod *res;
8066
8067         if (!mono_runtime_has_tls_get ())
8068                 return NULL;
8069
8070         mono_loader_lock ();
8071         res = alloc_method_cache [atype];
8072         if (!res)
8073                 res = alloc_method_cache [atype] = create_allocator (atype);
8074         mono_loader_unlock ();
8075         return res;
8076 #else
8077         return NULL;
8078 #endif
8079 }
8080
8081 guint32
8082 mono_gc_get_managed_allocator_types (void)
8083 {
8084         return ATYPE_NUM;
8085 }
8086
8087
8088 MonoMethod*
8089 mono_gc_get_write_barrier (void)
8090 {
8091         MonoMethod *res;
8092         MonoMethodBuilder *mb;
8093         MonoMethodSignature *sig;
8094 #ifdef MANAGED_WBARRIER
8095         int label_no_wb_1, label_no_wb_2, label_no_wb_3, label_no_wb_4, label_need_wb, label_slow_path;
8096 #ifndef ALIGN_NURSERY
8097         int label_continue_1, label_continue_2, label_no_wb_5;
8098         int dereferenced_var;
8099 #endif
8100         int buffer_var, buffer_index_var, dummy_var;
8101
8102 #ifdef HAVE_KW_THREAD
8103         int stack_end_offset = -1, store_remset_buffer_offset = -1;
8104         int store_remset_buffer_index_offset = -1, store_remset_buffer_index_addr_offset = -1;
8105
8106         MONO_THREAD_VAR_OFFSET (stack_end, stack_end_offset);
8107         g_assert (stack_end_offset != -1);
8108         MONO_THREAD_VAR_OFFSET (store_remset_buffer, store_remset_buffer_offset);
8109         g_assert (store_remset_buffer_offset != -1);
8110         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index, store_remset_buffer_index_offset);
8111         g_assert (store_remset_buffer_index_offset != -1);
8112         MONO_THREAD_VAR_OFFSET (store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8113         g_assert (store_remset_buffer_index_addr_offset != -1);
8114 #endif
8115 #endif
8116
8117         // FIXME: Maybe create a separate version for ctors (the branch would be
8118         // correctly predicted more times)
8119         if (write_barrier_method)
8120                 return write_barrier_method;
8121
8122         /* Create the IL version of mono_gc_barrier_generic_store () */
8123         sig = mono_metadata_signature_alloc (mono_defaults.corlib, 1);
8124         sig->ret = &mono_defaults.void_class->byval_arg;
8125         sig->params [0] = &mono_defaults.int_class->byval_arg;
8126
8127         mb = mono_mb_new (mono_defaults.object_class, "wbarrier", MONO_WRAPPER_WRITE_BARRIER);
8128
8129 #ifdef MANAGED_WBARRIER
8130         if (mono_runtime_has_tls_get ()) {
8131 #ifdef ALIGN_NURSERY
8132                 // if (ptr_in_nursery (ptr)) return;
8133                 /*
8134                  * Masking out the bits might be faster, but we would have to use 64 bit
8135                  * immediates, which might be slower.
8136                  */
8137                 mono_mb_emit_ldarg (mb, 0);
8138                 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
8139                 mono_mb_emit_byte (mb, CEE_SHR_UN);
8140                 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
8141                 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BEQ);
8142
8143                 // if (!ptr_in_nursery (*ptr)) return;
8144                 mono_mb_emit_ldarg (mb, 0);
8145                 mono_mb_emit_byte (mb, CEE_LDIND_I);
8146                 mono_mb_emit_icon (mb, DEFAULT_NURSERY_BITS);
8147                 mono_mb_emit_byte (mb, CEE_SHR_UN);
8148                 mono_mb_emit_icon (mb, (mword)nursery_start >> DEFAULT_NURSERY_BITS);
8149                 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
8150 #else
8151
8152                 // if (ptr < (nursery_start)) goto continue;
8153                 mono_mb_emit_ldarg (mb, 0);
8154                 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
8155                 label_continue_1 = mono_mb_emit_branch (mb, CEE_BLT);
8156
8157                 // if (ptr >= nursery_real_end)) goto continue;
8158                 mono_mb_emit_ldarg (mb, 0);
8159                 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
8160                 label_continue_2 = mono_mb_emit_branch (mb, CEE_BGE);
8161
8162                 // Otherwise return
8163                 label_no_wb_1 = mono_mb_emit_branch (mb, CEE_BR);
8164
8165                 // continue:
8166                 mono_mb_patch_branch (mb, label_continue_1);
8167                 mono_mb_patch_branch (mb, label_continue_2);
8168
8169                 // Dereference and store in local var
8170                 dereferenced_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8171                 mono_mb_emit_ldarg (mb, 0);
8172                 mono_mb_emit_byte (mb, CEE_LDIND_I);
8173                 mono_mb_emit_stloc (mb, dereferenced_var);
8174
8175                 // if (*ptr < nursery_start) return;
8176                 mono_mb_emit_ldloc (mb, dereferenced_var);
8177                 mono_mb_emit_ptr (mb, (gpointer) nursery_start);
8178                 label_no_wb_2 = mono_mb_emit_branch (mb, CEE_BLT);
8179
8180                 // if (*ptr >= nursery_end) return;
8181                 mono_mb_emit_ldloc (mb, dereferenced_var);
8182                 mono_mb_emit_ptr (mb, (gpointer) nursery_real_end);
8183                 label_no_wb_5 = mono_mb_emit_branch (mb, CEE_BGE);
8184
8185 #endif 
8186                 // if (ptr >= stack_end) goto need_wb;
8187                 mono_mb_emit_ldarg (mb, 0);
8188                 EMIT_TLS_ACCESS (mb, stack_end, stack_end_offset);
8189                 label_need_wb = mono_mb_emit_branch (mb, CEE_BGE_UN);
8190
8191                 // if (ptr >= stack_start) return;
8192                 dummy_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8193                 mono_mb_emit_ldarg (mb, 0);
8194                 mono_mb_emit_ldloc_addr (mb, dummy_var);
8195                 label_no_wb_3 = mono_mb_emit_branch (mb, CEE_BGE_UN);
8196
8197                 // need_wb:
8198                 mono_mb_patch_branch (mb, label_need_wb);
8199
8200                 // buffer = STORE_REMSET_BUFFER;
8201                 buffer_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8202                 EMIT_TLS_ACCESS (mb, store_remset_buffer, store_remset_buffer_offset);
8203                 mono_mb_emit_stloc (mb, buffer_var);
8204
8205                 // buffer_index = STORE_REMSET_BUFFER_INDEX;
8206                 buffer_index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
8207                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index, store_remset_buffer_index_offset);
8208                 mono_mb_emit_stloc (mb, buffer_index_var);
8209
8210                 // if (buffer [buffer_index] == ptr) return;
8211                 mono_mb_emit_ldloc (mb, buffer_var);
8212                 mono_mb_emit_ldloc (mb, buffer_index_var);
8213                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8214                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8215                 mono_mb_emit_byte (mb, CEE_SHL);
8216                 mono_mb_emit_byte (mb, CEE_ADD);
8217                 mono_mb_emit_byte (mb, CEE_LDIND_I);
8218                 mono_mb_emit_ldarg (mb, 0);
8219                 label_no_wb_4 = mono_mb_emit_branch (mb, CEE_BEQ);
8220
8221                 // ++buffer_index;
8222                 mono_mb_emit_ldloc (mb, buffer_index_var);
8223                 mono_mb_emit_icon (mb, 1);
8224                 mono_mb_emit_byte (mb, CEE_ADD);
8225                 mono_mb_emit_stloc (mb, buffer_index_var);
8226
8227                 // if (buffer_index >= STORE_REMSET_BUFFER_SIZE) goto slow_path;
8228                 mono_mb_emit_ldloc (mb, buffer_index_var);
8229                 mono_mb_emit_icon (mb, STORE_REMSET_BUFFER_SIZE);
8230                 label_slow_path = mono_mb_emit_branch (mb, CEE_BGE);
8231
8232                 // buffer [buffer_index] = ptr;
8233                 mono_mb_emit_ldloc (mb, buffer_var);
8234                 mono_mb_emit_ldloc (mb, buffer_index_var);
8235                 g_assert (sizeof (gpointer) == 4 || sizeof (gpointer) == 8);
8236                 mono_mb_emit_icon (mb, sizeof (gpointer) == 4 ? 2 : 3);
8237                 mono_mb_emit_byte (mb, CEE_SHL);
8238                 mono_mb_emit_byte (mb, CEE_ADD);
8239                 mono_mb_emit_ldarg (mb, 0);
8240                 mono_mb_emit_byte (mb, CEE_STIND_I);
8241
8242                 // STORE_REMSET_BUFFER_INDEX = buffer_index;
8243                 EMIT_TLS_ACCESS (mb, store_remset_buffer_index_addr, store_remset_buffer_index_addr_offset);
8244                 mono_mb_emit_ldloc (mb, buffer_index_var);
8245                 mono_mb_emit_byte (mb, CEE_STIND_I);
8246
8247                 // return;
8248                 mono_mb_patch_branch (mb, label_no_wb_1);
8249                 mono_mb_patch_branch (mb, label_no_wb_2);
8250                 mono_mb_patch_branch (mb, label_no_wb_3);
8251                 mono_mb_patch_branch (mb, label_no_wb_4);
8252 #ifndef ALIGN_NURSERY
8253                 mono_mb_patch_branch (mb, label_no_wb_5);
8254 #endif
8255                 mono_mb_emit_byte (mb, CEE_RET);
8256
8257                 // slow path
8258                 mono_mb_patch_branch (mb, label_slow_path);
8259         }
8260 #endif
8261
8262         mono_mb_emit_ldarg (mb, 0);
8263         mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_nostore);
8264         mono_mb_emit_byte (mb, CEE_RET);
8265
8266         res = mono_mb_create_method (mb, sig, 16);
8267         mono_mb_free (mb);
8268
8269         mono_loader_lock ();
8270         if (write_barrier_method) {
8271                 /* Already created */
8272                 mono_free_method (res);
8273         } else {
8274                 /* double-checked locking */
8275                 mono_memory_barrier ();
8276                 write_barrier_method = res;
8277         }
8278         mono_loader_unlock ();
8279
8280         return write_barrier_method;
8281 }
8282
8283 #endif /* HAVE_SGEN_GC */
8284