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