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