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