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