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