[sgen] Move DTrace support to Mono-specific code.
[mono.git] / mono / metadata / sgen-marksweep.c
1 /*
2  * sgen-marksweep.c: The Mark & Sweep major collector.
3  *
4  * Author:
5  *      Mark Probst <mark.probst@gmail.com>
6  *
7  * Copyright 2009-2010 Novell, Inc.
8  * Copyright (C) 2012 Xamarin Inc
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License 2.0 as published by the Free Software Foundation;
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License 2.0 along with this library; if not, write to the Free
21  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #include "config.h"
25
26 #ifdef HAVE_SGEN_GC
27
28 #include <math.h>
29 #include <errno.h>
30
31 #include "utils/mono-counters.h"
32 #include "utils/mono-semaphore.h"
33 #include "utils/mono-time.h"
34 #include "metadata/profiler-private.h"
35
36 #include "metadata/sgen-gc.h"
37 #include "metadata/sgen-protocol.h"
38 #include "metadata/sgen-cardtable.h"
39 #include "metadata/sgen-memory-governor.h"
40 #include "metadata/sgen-layout-stats.h"
41 #include "metadata/sgen-pointer-queue.h"
42 #include "metadata/sgen-pinning.h"
43 #include "metadata/sgen-workers.h"
44 #include "metadata/sgen-thread-pool.h"
45 #include "metadata/sgen-client.h"
46
47 #if defined(ARCH_MIN_MS_BLOCK_SIZE) && defined(ARCH_MIN_MS_BLOCK_SIZE_SHIFT)
48 #define MS_BLOCK_SIZE   ARCH_MIN_MS_BLOCK_SIZE
49 #define MS_BLOCK_SIZE_SHIFT     ARCH_MIN_MS_BLOCK_SIZE_SHIFT
50 #else
51 #define MS_BLOCK_SIZE_SHIFT     14      /* INT FASTENABLE */
52 #define MS_BLOCK_SIZE           (1 << MS_BLOCK_SIZE_SHIFT)
53 #endif
54 #define MAJOR_SECTION_SIZE      MS_BLOCK_SIZE
55 #define CARDS_PER_BLOCK (MS_BLOCK_SIZE / CARD_SIZE_IN_BYTES)
56
57 /*
58  * Don't allocate single blocks, but alloc a contingent of this many
59  * blocks in one swoop.  This must be a power of two.
60  */
61 #define MS_BLOCK_ALLOC_NUM      32
62
63 /*
64  * Number of bytes before the first object in a block.  At the start
65  * of a block is the MSBlockHeader, then opional padding, then come
66  * the objects, so this must be >= sizeof (MSBlockHeader).
67  */
68 #define MS_BLOCK_SKIP   ((sizeof (MSBlockHeader) + 15) & ~15)
69
70 #define MS_BLOCK_FREE   (MS_BLOCK_SIZE - MS_BLOCK_SKIP)
71
72 #define MS_NUM_MARK_WORDS       ((MS_BLOCK_SIZE / SGEN_ALLOC_ALIGN + sizeof (mword) * 8 - 1) / (sizeof (mword) * 8))
73
74 /*
75  * Blocks progress from one state to the next:
76  *
77  * SWEPT           The block is fully swept.  It might or might not be in
78  *                 a free list.
79  *
80  * MARKING         The block might or might not contain live objects.  If
81  *                 we're in between an initial collection pause and the
82  *                 finishing pause, the block might or might not be in a
83  *                 free list.
84  *
85  * CHECKING        The sweep thread is investigating the block to determine
86  *                 whether or not it contains live objects.  The block is
87  *                 not in a free list.
88  *
89  * NEED_SWEEPING   The block contains live objects but has not yet been
90  *                 swept.  It also contains free slots.  It is in a block
91  *                 free list.
92  *
93  * SWEEPING        The block is being swept.  It might be in a free list.
94  */
95
96 enum {
97         BLOCK_STATE_SWEPT,
98         BLOCK_STATE_MARKING,
99         BLOCK_STATE_CHECKING,
100         BLOCK_STATE_NEED_SWEEPING,
101         BLOCK_STATE_SWEEPING
102 };
103
104 typedef struct _MSBlockInfo MSBlockInfo;
105 struct _MSBlockInfo {
106         guint16 obj_size;
107         /*
108          * FIXME: Do we even need this? It's only used during sweep and might be worth
109          * recalculating to save the space.
110          */
111         guint16 obj_size_index;
112         /* FIXME: Reduce this - it only needs a byte. */
113         volatile gint32 state;
114         unsigned int pinned : 1;
115         unsigned int has_references : 1;
116         unsigned int has_pinned : 1;    /* means cannot evacuate */
117         unsigned int is_to_space : 1;
118         void ** volatile free_list;
119         MSBlockInfo * volatile next_free;
120         guint8 * volatile cardtable_mod_union;
121         mword mark_words [MS_NUM_MARK_WORDS];
122 };
123
124 #define MS_BLOCK_FOR_BLOCK_INFO(b)      ((char*)(b))
125
126 #define MS_BLOCK_OBJ(b,i)               (MS_BLOCK_FOR_BLOCK_INFO(b) + MS_BLOCK_SKIP + (b)->obj_size * (i))
127 #define MS_BLOCK_OBJ_FOR_SIZE(b,i,obj_size)             (MS_BLOCK_FOR_BLOCK_INFO(b) + MS_BLOCK_SKIP + (obj_size) * (i))
128 #define MS_BLOCK_DATA_FOR_OBJ(o)        ((char*)((mword)(o) & ~(mword)(MS_BLOCK_SIZE - 1)))
129
130 typedef struct {
131         MSBlockInfo info;
132 } MSBlockHeader;
133
134 #define MS_BLOCK_FOR_OBJ(o)             (&((MSBlockHeader*)MS_BLOCK_DATA_FOR_OBJ ((o)))->info)
135
136 /* object index will always be small */
137 #define MS_BLOCK_OBJ_INDEX(o,b) ((int)(((char*)(o) - (MS_BLOCK_FOR_BLOCK_INFO(b) + MS_BLOCK_SKIP)) / (b)->obj_size))
138
139 //casting to int is fine since blocks are 32k
140 #define MS_CALC_MARK_BIT(w,b,o)         do {                            \
141                 int i = ((int)((char*)(o) - MS_BLOCK_DATA_FOR_OBJ ((o)))) >> SGEN_ALLOC_ALIGN_BITS; \
142                 if (sizeof (mword) == 4) {                              \
143                         (w) = i >> 5;                                   \
144                         (b) = i & 31;                                   \
145                 } else {                                                \
146                         (w) = i >> 6;                                   \
147                         (b) = i & 63;                                   \
148                 }                                                       \
149         } while (0)
150
151 #define MS_MARK_BIT(bl,w,b)     ((bl)->mark_words [(w)] & (ONE_P << (b)))
152 #define MS_SET_MARK_BIT(bl,w,b) ((bl)->mark_words [(w)] |= (ONE_P << (b)))
153
154 #define MS_OBJ_ALLOCED(o,b)     (*(void**)(o) && (*(char**)(o) < MS_BLOCK_FOR_BLOCK_INFO (b) || *(char**)(o) >= MS_BLOCK_FOR_BLOCK_INFO (b) + MS_BLOCK_SIZE))
155
156 #define MS_BLOCK_OBJ_SIZE_FACTOR        (pow (2.0, 1.0 / 3))
157
158 /*
159  * This way we can lookup block object size indexes for sizes up to
160  * 256 bytes with a single load.
161  */
162 #define MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES      32
163
164 static int *block_obj_sizes;
165 static int num_block_obj_sizes;
166 static int fast_block_obj_size_indexes [MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES];
167
168 #define MS_BLOCK_FLAG_PINNED    1
169 #define MS_BLOCK_FLAG_REFS      2
170
171 #define MS_BLOCK_TYPE_MAX       4
172
173 static gboolean *evacuate_block_obj_sizes;
174 static float evacuation_threshold = 0.666f;
175 static float concurrent_evacuation_threshold = 0.666f;
176 static gboolean want_evacuation = FALSE;
177
178 static gboolean lazy_sweep = FALSE;
179
180 enum {
181         SWEEP_STATE_SWEPT,
182         SWEEP_STATE_NEED_SWEEPING,
183         SWEEP_STATE_SWEEPING,
184         SWEEP_STATE_SWEEPING_AND_ITERATING,
185         SWEEP_STATE_COMPACTING
186 };
187
188 static volatile int sweep_state = SWEEP_STATE_SWEPT;
189
190 static gboolean concurrent_mark;
191 static gboolean concurrent_sweep = TRUE;
192
193 #define BLOCK_IS_TAGGED_HAS_REFERENCES(bl)      SGEN_POINTER_IS_TAGGED_1 ((bl))
194 #define BLOCK_TAG_HAS_REFERENCES(bl)            SGEN_POINTER_TAG_1 ((bl))
195
196 #define BLOCK_IS_TAGGED_CHECKING(bl)            SGEN_POINTER_IS_TAGGED_2 ((bl))
197 #define BLOCK_TAG_CHECKING(bl)                  SGEN_POINTER_TAG_2 ((bl))
198
199 #define BLOCK_UNTAG(bl)                         SGEN_POINTER_UNTAG_12 ((bl))
200
201 #define BLOCK_TAG(bl)                           ((bl)->has_references ? BLOCK_TAG_HAS_REFERENCES ((bl)) : (bl))
202
203 /* all allocated blocks in the system */
204 static SgenPointerQueue allocated_blocks;
205
206 /* non-allocated block free-list */
207 static void *empty_blocks = NULL;
208 static size_t num_empty_blocks = 0;
209
210 #define FOREACH_BLOCK_NO_LOCK_CONDITION(cond,bl) {                      \
211         size_t __index;                                                 \
212         SGEN_ASSERT (0, (cond) && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); \
213         for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { \
214                 (bl) = BLOCK_UNTAG (allocated_blocks.data [__index]);
215 #define FOREACH_BLOCK_NO_LOCK(bl)                                       \
216         FOREACH_BLOCK_NO_LOCK_CONDITION(sgen_is_world_stopped (), bl)
217 #define FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK(bl,hr) {                   \
218         size_t __index;                                                 \
219         SGEN_ASSERT (0, sgen_is_world_stopped () && !sweep_in_progress (), "Can't iterate blocks while the world is running or sweep is in progress."); \
220         for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { \
221                 (bl) = allocated_blocks.data [__index];                 \
222                 (hr) = BLOCK_IS_TAGGED_HAS_REFERENCES ((bl));           \
223                 (bl) = BLOCK_UNTAG ((bl));
224 #define END_FOREACH_BLOCK_NO_LOCK       } }
225
226 static volatile size_t num_major_sections = 0;
227 /*
228  * One free block list for each block object size.  We add and remove blocks from these
229  * lists lock-free via CAS.
230  *
231  * Blocks accessed/removed from `free_block_lists`:
232  *   from the mutator (with GC lock held)
233  *   in nursery collections
234  *   in non-concurrent major collections
235  *   in the finishing pause of concurrent major collections (whole list is cleared)
236  *
237  * Blocks added to `free_block_lists`:
238  *   in the sweeping thread
239  *   during nursery collections
240  *   from domain clearing (with the world stopped and no sweeping happening)
241  *
242  * The only item of those that doesn't require the GC lock is the sweep thread.  The sweep
243  * thread only ever adds blocks to the free list, so the ABA problem can't occur.
244  */
245 static MSBlockInfo * volatile *free_block_lists [MS_BLOCK_TYPE_MAX];
246
247 static guint64 stat_major_blocks_alloced = 0;
248 static guint64 stat_major_blocks_freed = 0;
249 static guint64 stat_major_blocks_lazy_swept = 0;
250 static guint64 stat_major_objects_evacuated = 0;
251
252 #if SIZEOF_VOID_P != 8
253 static guint64 stat_major_blocks_freed_ideal = 0;
254 static guint64 stat_major_blocks_freed_less_ideal = 0;
255 static guint64 stat_major_blocks_freed_individual = 0;
256 static guint64 stat_major_blocks_alloced_less_ideal = 0;
257 #endif
258
259 #ifdef SGEN_COUNT_NUMBER_OF_MAJOR_OBJECTS_MARKED
260 static guint64 num_major_objects_marked = 0;
261 #define INC_NUM_MAJOR_OBJECTS_MARKED()  (++num_major_objects_marked)
262 #else
263 #define INC_NUM_MAJOR_OBJECTS_MARKED()
264 #endif
265
266 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
267 static mono_mutex_t scanned_objects_list_lock;
268 static SgenPointerQueue scanned_objects_list;
269
270 static void
271 add_scanned_object (void *ptr)
272 {
273         if (!binary_protocol_is_enabled ())
274                 return;
275
276         mono_mutex_lock (&scanned_objects_list_lock);
277         sgen_pointer_queue_add (&scanned_objects_list, ptr);
278         mono_mutex_unlock (&scanned_objects_list_lock);
279 }
280 #endif
281
282 static gboolean sweep_block (MSBlockInfo *block);
283
284 static int
285 ms_find_block_obj_size_index (size_t size)
286 {
287         int i;
288         SGEN_ASSERT (9, size <= SGEN_MAX_SMALL_OBJ_SIZE, "size %d is bigger than max small object size %d", size, SGEN_MAX_SMALL_OBJ_SIZE);
289         for (i = 0; i < num_block_obj_sizes; ++i)
290                 if (block_obj_sizes [i] >= size)
291                         return i;
292         g_error ("no object of size %d\n", size);
293 }
294
295 #define FREE_BLOCKS_FROM(lists,p,r)     (lists [((p) ? MS_BLOCK_FLAG_PINNED : 0) | ((r) ? MS_BLOCK_FLAG_REFS : 0)])
296 #define FREE_BLOCKS(p,r)                (FREE_BLOCKS_FROM (free_block_lists, (p), (r)))
297
298 #define MS_BLOCK_OBJ_SIZE_INDEX(s)                              \
299         (((s)+7)>>3 < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES ?      \
300          fast_block_obj_size_indexes [((s)+7)>>3] :             \
301          ms_find_block_obj_size_index ((s)))
302
303 static void*
304 major_alloc_heap (mword nursery_size, mword nursery_align, int the_nursery_bits)
305 {
306         char *start;
307         if (nursery_align)
308                 start = sgen_alloc_os_memory_aligned (nursery_size, nursery_align, SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE, "nursery");
309         else
310                 start = sgen_alloc_os_memory (nursery_size, SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE, "nursery");
311
312         return start;
313 }
314
315 static void
316 update_heap_boundaries_for_block (MSBlockInfo *block)
317 {
318         sgen_update_heap_boundaries ((mword)MS_BLOCK_FOR_BLOCK_INFO (block), (mword)MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE);
319 }
320
321 /*
322  * Thread safe
323  */
324 static void*
325 ms_get_empty_block (void)
326 {
327         char *p;
328         int i;
329         void *block, *empty, *next;
330
331  retry:
332         if (!empty_blocks) {
333                 /*
334                  * We try allocating MS_BLOCK_ALLOC_NUM blocks first.  If that's
335                  * unsuccessful, we halve the number of blocks and try again, until we're at
336                  * 1.  If that doesn't work, either, we assert.
337                  */
338                 int alloc_num = MS_BLOCK_ALLOC_NUM;
339                 for (;;) {
340                         p = sgen_alloc_os_memory_aligned (MS_BLOCK_SIZE * alloc_num, MS_BLOCK_SIZE, SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE,
341                                         alloc_num == 1 ? "major heap section" : NULL);
342                         if (p)
343                                 break;
344                         alloc_num >>= 1;
345                 }
346
347                 for (i = 0; i < alloc_num; ++i) {
348                         block = p;
349                         /*
350                          * We do the free list update one after the
351                          * other so that other threads can use the new
352                          * blocks as quickly as possible.
353                          */
354                         do {
355                                 empty = empty_blocks;
356                                 *(void**)block = empty;
357                         } while (SGEN_CAS_PTR ((gpointer*)&empty_blocks, block, empty) != empty);
358                         p += MS_BLOCK_SIZE;
359                 }
360
361                 SGEN_ATOMIC_ADD_P (num_empty_blocks, alloc_num);
362
363                 stat_major_blocks_alloced += alloc_num;
364 #if SIZEOF_VOID_P != 8
365                 if (alloc_num != MS_BLOCK_ALLOC_NUM)
366                         stat_major_blocks_alloced_less_ideal += alloc_num;
367 #endif
368         }
369
370         do {
371                 empty = empty_blocks;
372                 if (!empty)
373                         goto retry;
374                 block = empty;
375                 next = *(void**)block;
376         } while (SGEN_CAS_PTR (&empty_blocks, next, empty) != empty);
377
378         SGEN_ATOMIC_ADD_P (num_empty_blocks, -1);
379
380         *(void**)block = NULL;
381
382         g_assert (!((mword)block & (MS_BLOCK_SIZE - 1)));
383
384         return block;
385 }
386
387 /*
388  * This doesn't actually free a block immediately, but enqueues it into the `empty_blocks`
389  * list, where it will either be freed later on, or reused in nursery collections.
390  */
391 static void
392 ms_free_block (void *block)
393 {
394         void *empty;
395
396         sgen_memgov_release_space (MS_BLOCK_SIZE, SPACE_MAJOR);
397         memset (block, 0, MS_BLOCK_SIZE);
398
399         do {
400                 empty = empty_blocks;
401                 *(void**)block = empty;
402         } while (SGEN_CAS_PTR (&empty_blocks, block, empty) != empty);
403
404         SGEN_ATOMIC_ADD_P (num_empty_blocks, 1);
405
406         binary_protocol_block_free (block, MS_BLOCK_SIZE);
407 }
408
409 static gboolean
410 sweep_in_progress (void)
411 {
412         int state = sweep_state;
413         return state == SWEEP_STATE_SWEEPING ||
414                 state == SWEEP_STATE_SWEEPING_AND_ITERATING ||
415                 state == SWEEP_STATE_COMPACTING;
416 }
417
418 static inline gboolean
419 block_is_swept_or_marking (MSBlockInfo *block)
420 {
421         gint32 state = block->state;
422         return state == BLOCK_STATE_SWEPT || state == BLOCK_STATE_MARKING;
423 }
424
425 //#define MARKSWEEP_CONSISTENCY_CHECK
426
427 #ifdef MARKSWEEP_CONSISTENCY_CHECK
428 static void
429 check_block_free_list (MSBlockInfo *block, int size, gboolean pinned)
430 {
431         SGEN_ASSERT (0, !sweep_in_progress (), "Can't examine allocated blocks during sweep");
432         for (; block; block = block->next_free) {
433                 SGEN_ASSERT (0, block->state != BLOCK_STATE_CHECKING, "Can't have a block we're checking in a free list.");
434                 g_assert (block->obj_size == size);
435                 g_assert ((pinned && block->pinned) || (!pinned && !block->pinned));
436
437                 /* blocks in the free lists must have at least
438                    one free slot */
439                 g_assert (block->free_list);
440
441                 /* the block must be in the allocated_blocks array */
442                 g_assert (sgen_pointer_queue_find (&allocated_blocks, BLOCK_TAG (block)) != (size_t)-1);
443         }
444 }
445
446 static void
447 check_empty_blocks (void)
448 {
449         void *p;
450         size_t i = 0;
451         for (p = empty_blocks; p; p = *(void**)p)
452                 ++i;
453         g_assert (i == num_empty_blocks);
454 }
455
456 static void
457 consistency_check (void)
458 {
459         MSBlockInfo *block;
460         int i;
461
462         /* check all blocks */
463         FOREACH_BLOCK_NO_LOCK (block) {
464                 int count = MS_BLOCK_FREE / block->obj_size;
465                 int num_free = 0;
466                 void **free;
467
468                 /* count number of free slots */
469                 for (i = 0; i < count; ++i) {
470                         void **obj = (void**) MS_BLOCK_OBJ (block, i);
471                         if (!MS_OBJ_ALLOCED (obj, block))
472                                 ++num_free;
473                 }
474
475                 /* check free list */
476                 for (free = block->free_list; free; free = (void**)*free) {
477                         g_assert (MS_BLOCK_FOR_OBJ (free) == block);
478                         --num_free;
479                 }
480                 g_assert (num_free == 0);
481
482                 /* check all mark words are zero */
483                 if (!sgen_concurrent_collection_in_progress () && block_is_swept_or_marking (block)) {
484                         for (i = 0; i < MS_NUM_MARK_WORDS; ++i)
485                                 g_assert (block->mark_words [i] == 0);
486                 }
487         } END_FOREACH_BLOCK_NO_LOCK;
488
489         /* check free blocks */
490         for (i = 0; i < num_block_obj_sizes; ++i) {
491                 int j;
492                 for (j = 0; j < MS_BLOCK_TYPE_MAX; ++j)
493                         check_block_free_list (free_block_lists [j][i], block_obj_sizes [i], j & MS_BLOCK_FLAG_PINNED);
494         }
495
496         check_empty_blocks ();
497 }
498 #endif
499
500 static void
501 add_free_block (MSBlockInfo * volatile *free_blocks, int size_index, MSBlockInfo *block)
502 {
503         MSBlockInfo *old;
504         do {
505                 block->next_free = old = free_blocks [size_index];
506         } while (SGEN_CAS_PTR ((gpointer)&free_blocks [size_index], block, old) != old);
507 }
508
509 static void major_finish_sweep_checking (void);
510
511 static gboolean
512 ms_alloc_block (int size_index, gboolean pinned, gboolean has_references)
513 {
514         int size = block_obj_sizes [size_index];
515         int count = MS_BLOCK_FREE / size;
516         MSBlockInfo *info;
517         MSBlockInfo * volatile * free_blocks = FREE_BLOCKS (pinned, has_references);
518         char *obj_start;
519         int i;
520
521         if (!sgen_memgov_try_alloc_space (MS_BLOCK_SIZE, SPACE_MAJOR))
522                 return FALSE;
523
524         info = (MSBlockInfo*)ms_get_empty_block ();
525
526         SGEN_ASSERT (9, count >= 2, "block with %d objects, it must hold at least 2", count);
527
528         info->obj_size = size;
529         info->obj_size_index = size_index;
530         info->pinned = pinned;
531         info->has_references = has_references;
532         info->has_pinned = pinned;
533         /*
534          * Blocks that are to-space are not evacuated from.  During an major collection
535          * blocks are allocated for two reasons: evacuating objects from the nursery and
536          * evacuating them from major blocks marked for evacuation.  In both cases we don't
537          * want further evacuation.
538          */
539         info->is_to_space = (sgen_get_current_collection_generation () == GENERATION_OLD);
540         info->state = (info->is_to_space || sgen_concurrent_collection_in_progress ()) ? BLOCK_STATE_MARKING : BLOCK_STATE_SWEPT;
541         SGEN_ASSERT (6, !sweep_in_progress () || info->state == BLOCK_STATE_SWEPT, "How do we add a new block to be swept while sweeping?");
542         info->cardtable_mod_union = NULL;
543
544         update_heap_boundaries_for_block (info);
545
546         binary_protocol_block_alloc (info, MS_BLOCK_SIZE);
547
548         /* build free list */
549         obj_start = MS_BLOCK_FOR_BLOCK_INFO (info) + MS_BLOCK_SKIP;
550         info->free_list = (void**)obj_start;
551         /* we're skipping the last one - it must be nulled */
552         for (i = 0; i < count - 1; ++i) {
553                 char *next_obj_start = obj_start + size;
554                 *(void**)obj_start = next_obj_start;
555                 obj_start = next_obj_start;
556         }
557         /* the last one */
558         *(void**)obj_start = NULL;
559
560         add_free_block (free_blocks, size_index, info);
561
562         /*
563          * This is the only place where the `allocated_blocks` array can potentially grow.
564          * We need to make sure concurrent sweep isn't running when that happens, so in that
565          * specific case we just wait for sweep to finish.
566          */
567         if (sgen_pointer_queue_will_grow (&allocated_blocks))
568                 major_finish_sweep_checking ();
569
570         sgen_pointer_queue_add (&allocated_blocks, BLOCK_TAG (info));
571
572         SGEN_ATOMIC_ADD_P (num_major_sections, 1);
573         return TRUE;
574 }
575
576 static gboolean
577 obj_is_from_pinned_alloc (char *ptr)
578 {
579         MSBlockInfo *block;
580
581         FOREACH_BLOCK_NO_LOCK (block) {
582                 if (ptr >= MS_BLOCK_FOR_BLOCK_INFO (block) && ptr <= MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE)
583                         return block->pinned;
584         } END_FOREACH_BLOCK_NO_LOCK;
585         return FALSE;
586 }
587
588 static void
589 ensure_can_access_block_free_list (MSBlockInfo *block)
590 {
591  retry:
592         for (;;) {
593                 switch (block->state) {
594                 case BLOCK_STATE_SWEPT:
595                 case BLOCK_STATE_MARKING:
596                         return;
597                 case BLOCK_STATE_CHECKING:
598                         SGEN_ASSERT (0, FALSE, "How did we get a block that's being checked from a free list?");
599                         break;
600                 case BLOCK_STATE_NEED_SWEEPING:
601                         if (sweep_block (block))
602                                 ++stat_major_blocks_lazy_swept;
603                         break;
604                 case BLOCK_STATE_SWEEPING:
605                         /* FIXME: do this more elegantly */
606                         g_usleep (100);
607                         goto retry;
608                 default:
609                         SGEN_ASSERT (0, FALSE, "Illegal block state");
610                         break;
611                 }
612         }
613 }
614
615 static void*
616 unlink_slot_from_free_list_uncontested (MSBlockInfo * volatile *free_blocks, int size_index)
617 {
618         MSBlockInfo *block, *next_free_block;
619         void *obj, *next_free_slot;
620
621  retry:
622         block = free_blocks [size_index];
623         SGEN_ASSERT (9, block, "no free block to unlink from free_blocks %p size_index %d", free_blocks, size_index);
624
625         ensure_can_access_block_free_list (block);
626
627         obj = block->free_list;
628         SGEN_ASSERT (6, obj, "block %p in free list had no available object to alloc from", block);
629
630         next_free_slot = *(void**)obj;
631         if (next_free_slot) {
632                 block->free_list = next_free_slot;
633                 return obj;
634         }
635
636         next_free_block = block->next_free;
637         if (SGEN_CAS_PTR ((gpointer)&free_blocks [size_index], next_free_block, block) != block)
638                 goto retry;
639
640         block->free_list = NULL;
641         block->next_free = NULL;
642
643         return obj;
644 }
645
646 static void*
647 alloc_obj (GCVTable *vtable, size_t size, gboolean pinned, gboolean has_references)
648 {
649         int size_index = MS_BLOCK_OBJ_SIZE_INDEX (size);
650         MSBlockInfo * volatile * free_blocks = FREE_BLOCKS (pinned, has_references);
651         void *obj;
652
653         if (!free_blocks [size_index]) {
654                 if (G_UNLIKELY (!ms_alloc_block (size_index, pinned, has_references)))
655                         return NULL;
656         }
657
658         obj = unlink_slot_from_free_list_uncontested (free_blocks, size_index);
659
660         *(GCVTable**)obj = vtable;
661
662         return obj;
663 }
664
665 static void*
666 major_alloc_object (GCVTable *vtable, size_t size, gboolean has_references)
667 {
668         return alloc_obj (vtable, size, FALSE, has_references);
669 }
670
671 /*
672  * We're not freeing the block if it's empty.  We leave that work for
673  * the next major collection.
674  *
675  * This is just called from the domain clearing code, which runs in a
676  * single thread and has the GC lock, so we don't need an extra lock.
677  */
678 static void
679 free_object (char *obj, size_t size, gboolean pinned)
680 {
681         MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
682         int word, bit;
683         gboolean in_free_list;
684
685         SGEN_ASSERT (9, sweep_state == SWEEP_STATE_SWEPT, "Should have waited for sweep to free objects.");
686
687         ensure_can_access_block_free_list (block);
688         SGEN_ASSERT (9, (pinned && block->pinned) || (!pinned && !block->pinned), "free-object pinning mixup object %p pinned %d block %p pinned %d", obj, pinned, block, block->pinned);
689         SGEN_ASSERT (9, MS_OBJ_ALLOCED (obj, block), "object %p is already free", obj);
690         MS_CALC_MARK_BIT (word, bit, obj);
691         SGEN_ASSERT (9, !MS_MARK_BIT (block, word, bit), "object %p has mark bit set");
692
693         memset (obj, 0, size);
694
695         in_free_list = !!block->free_list;
696         *(void**)obj = block->free_list;
697         block->free_list = (void**)obj;
698
699         if (!in_free_list) {
700                 MSBlockInfo * volatile *free_blocks = FREE_BLOCKS (pinned, block->has_references);
701                 int size_index = MS_BLOCK_OBJ_SIZE_INDEX (size);
702                 SGEN_ASSERT (9, !block->next_free, "block %p doesn't have a free-list of object but belongs to a free-list of blocks");
703                 add_free_block (free_blocks, size_index, block);
704         }
705 }
706
707 static void
708 major_free_non_pinned_object (char *obj, size_t size)
709 {
710         free_object (obj, size, FALSE);
711 }
712
713 /* size is a multiple of SGEN_ALLOC_ALIGN */
714 static void*
715 major_alloc_small_pinned_obj (GCVTable *vtable, size_t size, gboolean has_references)
716 {
717         void *res;
718
719         res = alloc_obj (vtable, size, TRUE, has_references);
720          /*If we failed to alloc memory, we better try releasing memory
721           *as pinned alloc is requested by the runtime.
722           */
723          if (!res) {
724                 sgen_perform_collection (0, GENERATION_OLD, "pinned alloc failure", TRUE);
725                 res = alloc_obj (vtable, size, TRUE, has_references);
726          }
727          return res;
728 }
729
730 static void
731 free_pinned_object (char *obj, size_t size)
732 {
733         free_object (obj, size, TRUE);
734 }
735
736 /*
737  * size is already rounded up and we hold the GC lock.
738  */
739 static void*
740 major_alloc_degraded (GCVTable *vtable, size_t size)
741 {
742         void *obj = alloc_obj (vtable, size, FALSE, SGEN_VTABLE_HAS_REFERENCES (vtable));
743         if (G_LIKELY (obj)) {
744                 HEAVY_STAT (++stat_objects_alloced_degraded);
745                 HEAVY_STAT (stat_bytes_alloced_degraded += size);
746         }
747         return obj;
748 }
749
750 /*
751  * obj is some object.  If it's not in the major heap (i.e. if it's in
752  * the nursery or LOS), return FALSE.  Otherwise return whether it's
753  * been marked or copied.
754  */
755 static gboolean
756 major_is_object_live (char *obj)
757 {
758         MSBlockInfo *block;
759         int word, bit;
760         mword objsize;
761
762         if (sgen_ptr_in_nursery (obj))
763                 return FALSE;
764
765         objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((GCObject*)obj));
766
767         /* LOS */
768         if (objsize > SGEN_MAX_SMALL_OBJ_SIZE)
769                 return FALSE;
770
771         /* now we know it's in a major block */
772         block = MS_BLOCK_FOR_OBJ (obj);
773         SGEN_ASSERT (9, !block->pinned, "block %p is pinned, BTW why is this bad?");
774         MS_CALC_MARK_BIT (word, bit, obj);
775         return MS_MARK_BIT (block, word, bit) ? TRUE : FALSE;
776 }
777
778 static gboolean
779 major_ptr_is_in_non_pinned_space (char *ptr, char **start)
780 {
781         MSBlockInfo *block;
782
783         FOREACH_BLOCK_NO_LOCK (block) {
784                 if (ptr >= MS_BLOCK_FOR_BLOCK_INFO (block) && ptr <= MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE) {
785                         int count = MS_BLOCK_FREE / block->obj_size;
786                         int i;
787
788                         *start = NULL;
789                         for (i = 0; i <= count; ++i) {
790                                 if (ptr >= MS_BLOCK_OBJ (block, i) && ptr < MS_BLOCK_OBJ (block, i + 1)) {
791                                         *start = MS_BLOCK_OBJ (block, i);
792                                         break;
793                                 }
794                         }
795                         return !block->pinned;
796                 }
797         } END_FOREACH_BLOCK_NO_LOCK;
798         return FALSE;
799 }
800
801 static gboolean
802 try_set_sweep_state (int new, int expected)
803 {
804         int old = SGEN_CAS (&sweep_state, new, expected);
805         return old == expected;
806 }
807
808 static void
809 set_sweep_state (int new, int expected)
810 {
811         gboolean success = try_set_sweep_state (new, expected);
812         SGEN_ASSERT (0, success, "Could not set sweep state.");
813 }
814
815 static gboolean ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *have_checked);
816
817 static SgenThreadPoolJob * volatile sweep_job;
818
819 static void
820 major_finish_sweep_checking (void)
821 {
822         int block_index;
823         SgenThreadPoolJob *job;
824
825  retry:
826         switch (sweep_state) {
827         case SWEEP_STATE_SWEPT:
828         case SWEEP_STATE_NEED_SWEEPING:
829                 return;
830         case SWEEP_STATE_SWEEPING:
831                 if (try_set_sweep_state (SWEEP_STATE_SWEEPING_AND_ITERATING, SWEEP_STATE_SWEEPING))
832                         break;
833                 goto retry;
834         case SWEEP_STATE_SWEEPING_AND_ITERATING:
835                 SGEN_ASSERT (0, FALSE, "Is there another minor collection running?");
836                 goto retry;
837         case SWEEP_STATE_COMPACTING:
838                 goto wait;
839         default:
840                 SGEN_ASSERT (0, FALSE, "Invalid sweep state.");
841                 break;
842         }
843
844         /*
845          * We're running with the world stopped and the only other thread doing work is the
846          * sweep thread, which doesn't add blocks to the array, so we can safely access
847          * `next_slot`.
848          */
849         for (block_index = 0; block_index < allocated_blocks.next_slot; ++block_index)
850                 ensure_block_is_checked_for_sweeping (block_index, FALSE, NULL);
851
852         set_sweep_state (SWEEP_STATE_SWEEPING, SWEEP_STATE_SWEEPING_AND_ITERATING);
853
854  wait:
855         job = sweep_job;
856         if (job)
857                 sgen_thread_pool_job_wait (job);
858         SGEN_ASSERT (0, !sweep_job, "Why did the sweep job not null itself?");
859         SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "How is the sweep job done but we're not swept?");
860 }
861
862 static void
863 major_iterate_objects (IterateObjectsFlags flags, IterateObjectCallbackFunc callback, void *data)
864 {
865         gboolean sweep = flags & ITERATE_OBJECTS_SWEEP;
866         gboolean non_pinned = flags & ITERATE_OBJECTS_NON_PINNED;
867         gboolean pinned = flags & ITERATE_OBJECTS_PINNED;
868         MSBlockInfo *block;
869
870         major_finish_sweep_checking ();
871         FOREACH_BLOCK_NO_LOCK (block) {
872                 int count = MS_BLOCK_FREE / block->obj_size;
873                 int i;
874
875                 if (block->pinned && !pinned)
876                         continue;
877                 if (!block->pinned && !non_pinned)
878                         continue;
879                 if (sweep && lazy_sweep) {
880                         sweep_block (block);
881                         SGEN_ASSERT (6, block->state == BLOCK_STATE_SWEPT, "Block must be swept after sweeping");
882                 }
883
884                 for (i = 0; i < count; ++i) {
885                         void **obj = (void**) MS_BLOCK_OBJ (block, i);
886                         /*
887                          * We've finished sweep checking, but if we're sweeping lazily and
888                          * the flags don't require us to sweep, the block might still need
889                          * sweeping.  In that case, we need to consult the mark bits to tell
890                          * us whether an object slot is live.
891                          */
892                         if (!block_is_swept_or_marking (block)) {
893                                 int word, bit;
894                                 SGEN_ASSERT (6, !sweep && block->state == BLOCK_STATE_NEED_SWEEPING, "Has sweeping not finished?");
895                                 MS_CALC_MARK_BIT (word, bit, obj);
896                                 if (!MS_MARK_BIT (block, word, bit))
897                                         continue;
898                         }
899                         if (MS_OBJ_ALLOCED (obj, block))
900                                 callback ((char*)obj, block->obj_size, data);
901                 }
902         } END_FOREACH_BLOCK_NO_LOCK;
903 }
904
905 static gboolean
906 major_is_valid_object (char *object)
907 {
908         MSBlockInfo *block;
909
910         FOREACH_BLOCK_NO_LOCK (block) {
911                 int idx;
912                 char *obj;
913
914                 if ((MS_BLOCK_FOR_BLOCK_INFO (block) > object) || ((MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE) <= object))
915                         continue;
916
917                 idx = MS_BLOCK_OBJ_INDEX (object, block);
918                 obj = (char*)MS_BLOCK_OBJ (block, idx);
919                 if (obj != object)
920                         return FALSE;
921                 return MS_OBJ_ALLOCED (obj, block);
922         } END_FOREACH_BLOCK_NO_LOCK;
923
924         return FALSE;
925 }
926
927
928 static GCVTable*
929 major_describe_pointer (char *ptr)
930 {
931         MSBlockInfo *block;
932
933         FOREACH_BLOCK_NO_LOCK (block) {
934                 int idx;
935                 char *obj;
936                 gboolean live;
937                 GCVTable *vtable;
938                 int w, b;
939                 gboolean marked;
940
941                 if ((MS_BLOCK_FOR_BLOCK_INFO (block) > ptr) || ((MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE) <= ptr))
942                         continue;
943
944                 SGEN_LOG (0, "major-ptr (block %p sz %d pin %d ref %d)\n",
945                         MS_BLOCK_FOR_BLOCK_INFO (block), block->obj_size, block->pinned, block->has_references);
946
947                 idx = MS_BLOCK_OBJ_INDEX (ptr, block);
948                 obj = (char*)MS_BLOCK_OBJ (block, idx);
949                 live = MS_OBJ_ALLOCED (obj, block);
950                 vtable = live ? (GCVTable*)SGEN_LOAD_VTABLE (obj) : NULL;
951
952                 MS_CALC_MARK_BIT (w, b, obj);
953                 marked = MS_MARK_BIT (block, w, b);
954
955                 if (obj == ptr) {
956                         SGEN_LOG (0, "\t(");
957                         if (live)
958                                 SGEN_LOG (0, "object");
959                         else
960                                 SGEN_LOG (0, "dead-object");
961                 } else {
962                         if (live)
963                                 SGEN_LOG (0, "interior-ptr offset %td", ptr - obj);
964                         else
965                                 SGEN_LOG (0, "dead-interior-ptr offset %td", ptr - obj);
966                 }
967
968                 SGEN_LOG (0, " marked %d)\n", marked ? 1 : 0);
969
970                 return vtable;
971         } END_FOREACH_BLOCK_NO_LOCK;
972
973         return NULL;
974 }
975
976 static void
977 major_check_scan_starts (void)
978 {
979 }
980
981 static void
982 major_dump_heap (FILE *heap_dump_file)
983 {
984         MSBlockInfo *block;
985         int *slots_available = alloca (sizeof (int) * num_block_obj_sizes);
986         int *slots_used = alloca (sizeof (int) * num_block_obj_sizes);
987         int i;
988
989         for (i = 0; i < num_block_obj_sizes; ++i)
990                 slots_available [i] = slots_used [i] = 0;
991
992         FOREACH_BLOCK_NO_LOCK (block) {
993                 int index = ms_find_block_obj_size_index (block->obj_size);
994                 int count = MS_BLOCK_FREE / block->obj_size;
995
996                 slots_available [index] += count;
997                 for (i = 0; i < count; ++i) {
998                         if (MS_OBJ_ALLOCED (MS_BLOCK_OBJ (block, i), block))
999                                 ++slots_used [index];
1000                 }
1001         } END_FOREACH_BLOCK_NO_LOCK;
1002
1003         fprintf (heap_dump_file, "<occupancies>\n");
1004         for (i = 0; i < num_block_obj_sizes; ++i) {
1005                 fprintf (heap_dump_file, "<occupancy size=\"%d\" available=\"%d\" used=\"%d\" />\n",
1006                                 block_obj_sizes [i], slots_available [i], slots_used [i]);
1007         }
1008         fprintf (heap_dump_file, "</occupancies>\n");
1009
1010         FOREACH_BLOCK_NO_LOCK (block) {
1011                 int count = MS_BLOCK_FREE / block->obj_size;
1012                 int i;
1013                 int start = -1;
1014
1015                 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%zu\">\n", "old", (size_t)MS_BLOCK_FREE);
1016
1017                 for (i = 0; i <= count; ++i) {
1018                         if ((i < count) && MS_OBJ_ALLOCED (MS_BLOCK_OBJ (block, i), block)) {
1019                                 if (start < 0)
1020                                         start = i;
1021                         } else {
1022                                 if (start >= 0) {
1023                                         sgen_dump_occupied (MS_BLOCK_OBJ (block, start), MS_BLOCK_OBJ (block, i), MS_BLOCK_FOR_BLOCK_INFO (block));
1024                                         start = -1;
1025                                 }
1026                         }
1027                 }
1028
1029                 fprintf (heap_dump_file, "</section>\n");
1030         } END_FOREACH_BLOCK_NO_LOCK;
1031 }
1032
1033 static guint8*
1034 get_cardtable_mod_union_for_block (MSBlockInfo *block, gboolean allocate)
1035 {
1036         guint8 *mod_union = block->cardtable_mod_union;
1037         guint8 *other;
1038         if (mod_union)
1039                 return mod_union;
1040         else if (!allocate)
1041                 return NULL;
1042         mod_union = sgen_card_table_alloc_mod_union (MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE);
1043         other = SGEN_CAS_PTR ((gpointer*)&block->cardtable_mod_union, mod_union, NULL);
1044         if (!other) {
1045                 SGEN_ASSERT (0, block->cardtable_mod_union == mod_union, "Why did CAS not replace?");
1046                 return mod_union;
1047         }
1048         sgen_card_table_free_mod_union (mod_union, MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE);
1049         return other;
1050 }
1051
1052 static inline guint8*
1053 major_get_cardtable_mod_union_for_reference (char *ptr)
1054 {
1055         MSBlockInfo *block = MS_BLOCK_FOR_OBJ (ptr);
1056         size_t offset = sgen_card_table_get_card_offset (ptr, (char*)sgen_card_table_align_pointer (MS_BLOCK_FOR_BLOCK_INFO (block)));
1057         guint8 *mod_union = get_cardtable_mod_union_for_block (block, TRUE);
1058         SGEN_ASSERT (0, mod_union, "FIXME: optionally allocate the mod union if it's not here and CAS it in.");
1059         return &mod_union [offset];
1060 }
1061
1062 /*
1063  * Mark the mod-union card for `ptr`, which must be a reference within the object `obj`.
1064  */
1065 static void
1066 mark_mod_union_card (MonoObject *obj, void **ptr)
1067 {
1068         int type = sgen_obj_get_descriptor ((char*)obj) & DESC_TYPE_MASK;
1069         if (sgen_safe_object_is_small (obj, type)) {
1070                 guint8 *card_byte = major_get_cardtable_mod_union_for_reference ((char*)ptr);
1071                 SGEN_ASSERT (0, MS_BLOCK_FOR_OBJ (obj) == MS_BLOCK_FOR_OBJ (ptr), "How can an object and a reference inside it not be in the same block?");
1072                 *card_byte = 1;
1073         } else {
1074                 sgen_los_mark_mod_union_card (obj, ptr);
1075         }
1076 }
1077
1078 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
1079
1080 #define MS_MARK_OBJECT_AND_ENQUEUE_CHECKED(obj,desc,block,queue) do {   \
1081                 int __word, __bit;                                      \
1082                 MS_CALC_MARK_BIT (__word, __bit, (obj));                \
1083                 if (!MS_MARK_BIT ((block), __word, __bit) && MS_OBJ_ALLOCED ((obj), (block))) { \
1084                         MS_SET_MARK_BIT ((block), __word, __bit);       \
1085                         if (sgen_gc_descr_has_references (desc))                        \
1086                                 GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc)); \
1087                         binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((GCObject*)(obj))); \
1088                         INC_NUM_MAJOR_OBJECTS_MARKED ();                \
1089                 }                                                       \
1090         } while (0)
1091 #define MS_MARK_OBJECT_AND_ENQUEUE(obj,desc,block,queue) do {           \
1092                 int __word, __bit;                                      \
1093                 MS_CALC_MARK_BIT (__word, __bit, (obj));                \
1094                 SGEN_ASSERT (9, MS_OBJ_ALLOCED ((obj), (block)), "object %p not allocated", obj); \
1095                 if (!MS_MARK_BIT ((block), __word, __bit)) {            \
1096                         MS_SET_MARK_BIT ((block), __word, __bit);       \
1097                         if (sgen_gc_descr_has_references (desc))                        \
1098                                 GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc)); \
1099                         binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((GCObject*)(obj))); \
1100                         INC_NUM_MAJOR_OBJECTS_MARKED ();                \
1101                 }                                                       \
1102         } while (0)
1103
1104 static void
1105 pin_major_object (char *obj, SgenGrayQueue *queue)
1106 {
1107         MSBlockInfo *block;
1108
1109         if (concurrent_mark)
1110                 g_assert_not_reached ();
1111
1112         block = MS_BLOCK_FOR_OBJ (obj);
1113         block->has_pinned = TRUE;
1114         MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue);
1115 }
1116
1117 #include "sgen-major-copy-object.h"
1118
1119 static void
1120 major_copy_or_mark_object_concurrent (void **ptr, void *obj, SgenGrayQueue *queue)
1121 {
1122         SGEN_ASSERT (9, sgen_concurrent_collection_in_progress (), "Why are we scanning concurrently when there's no concurrent collection on?");
1123         SGEN_ASSERT (9, !sgen_workers_are_working () || sgen_thread_pool_is_thread_pool_thread (mono_native_thread_id_get ()), "We must not scan from two threads at the same time!");
1124
1125         g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
1126
1127         if (!sgen_ptr_in_nursery (obj)) {
1128                 mword objsize;
1129
1130                 objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((GCObject*)obj));
1131
1132                 if (objsize <= SGEN_MAX_SMALL_OBJ_SIZE) {
1133                         MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
1134                         MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue);
1135                 } else {
1136                         if (sgen_los_object_is_pinned (obj))
1137                                 return;
1138
1139                         binary_protocol_mark (obj, SGEN_LOAD_VTABLE (obj), sgen_safe_object_get_size (obj));
1140
1141                         sgen_los_pin_object (obj);
1142                         if (SGEN_OBJECT_HAS_REFERENCES (obj))
1143                                 GRAY_OBJECT_ENQUEUE (queue, obj, sgen_obj_get_descriptor (obj));
1144                         INC_NUM_MAJOR_OBJECTS_MARKED ();
1145                 }
1146         }
1147 }
1148
1149 static long long
1150 major_get_and_reset_num_major_objects_marked (void)
1151 {
1152 #ifdef SGEN_COUNT_NUMBER_OF_MAJOR_OBJECTS_MARKED
1153         long long num = num_major_objects_marked;
1154         num_major_objects_marked = 0;
1155         return num;
1156 #else
1157         return 0;
1158 #endif
1159 }
1160
1161 #define PREFETCH_CARDS          1       /* BOOL FASTENABLE */
1162 #if !PREFETCH_CARDS
1163 #undef PREFETCH_CARDS
1164 #endif
1165
1166 /* gcc 4.2.1 from xcode4 crashes on sgen_card_table_get_card_address () when this is enabled */
1167 #if defined(PLATFORM_MACOSX)
1168 #define GCC_VERSION (__GNUC__ * 10000 \
1169                                + __GNUC_MINOR__ * 100 \
1170                                + __GNUC_PATCHLEVEL__)
1171 #if GCC_VERSION <= 40300
1172 #undef PREFETCH_CARDS
1173 #endif
1174 #endif
1175
1176 #ifdef HEAVY_STATISTICS
1177 static guint64 stat_optimized_copy;
1178 static guint64 stat_optimized_copy_nursery;
1179 static guint64 stat_optimized_copy_nursery_forwarded;
1180 static guint64 stat_optimized_copy_nursery_pinned;
1181 static guint64 stat_optimized_copy_major;
1182 static guint64 stat_optimized_copy_major_small_fast;
1183 static guint64 stat_optimized_copy_major_small_slow;
1184 static guint64 stat_optimized_copy_major_large;
1185 static guint64 stat_optimized_copy_major_forwarded;
1186 static guint64 stat_optimized_copy_major_small_evacuate;
1187 static guint64 stat_optimized_major_scan;
1188 static guint64 stat_optimized_major_scan_no_refs;
1189
1190 static guint64 stat_drain_prefetch_fills;
1191 static guint64 stat_drain_prefetch_fill_failures;
1192 static guint64 stat_drain_loops;
1193 #endif
1194
1195 static void major_scan_object_with_evacuation (char *start, mword desc, SgenGrayQueue *queue);
1196
1197 #define COPY_OR_MARK_FUNCTION_NAME      major_copy_or_mark_object_no_evacuation
1198 #define SCAN_OBJECT_FUNCTION_NAME       major_scan_object_no_evacuation
1199 #define DRAIN_GRAY_STACK_FUNCTION_NAME  drain_gray_stack_no_evacuation
1200 #include "sgen-marksweep-drain-gray-stack.h"
1201
1202 #define COPY_OR_MARK_WITH_EVACUATION
1203 #define COPY_OR_MARK_FUNCTION_NAME      major_copy_or_mark_object_with_evacuation
1204 #define SCAN_OBJECT_FUNCTION_NAME       major_scan_object_with_evacuation
1205 #define DRAIN_GRAY_STACK_FUNCTION_NAME  drain_gray_stack_with_evacuation
1206 #include "sgen-marksweep-drain-gray-stack.h"
1207
1208 static gboolean
1209 drain_gray_stack (ScanCopyContext ctx)
1210 {
1211         gboolean evacuation = FALSE;
1212         int i;
1213         for (i = 0; i < num_block_obj_sizes; ++i) {
1214                 if (evacuate_block_obj_sizes [i]) {
1215                         evacuation = TRUE;
1216                         break;
1217                 }
1218         }
1219
1220         if (evacuation)
1221                 return drain_gray_stack_with_evacuation (ctx);
1222         else
1223                 return drain_gray_stack_no_evacuation (ctx);
1224 }
1225
1226 #include "sgen-marksweep-scan-object-concurrent.h"
1227
1228 static void
1229 major_copy_or_mark_object_canonical (void **ptr, SgenGrayQueue *queue)
1230 {
1231         major_copy_or_mark_object_with_evacuation (ptr, *ptr, queue);
1232 }
1233
1234 static void
1235 major_copy_or_mark_object_concurrent_canonical (void **ptr, SgenGrayQueue *queue)
1236 {
1237         major_copy_or_mark_object_concurrent (ptr, *ptr, queue);
1238 }
1239
1240 static void
1241 major_copy_or_mark_object_concurrent_finish_canonical (void **ptr, SgenGrayQueue *queue)
1242 {
1243         major_copy_or_mark_object_no_evacuation (ptr, *ptr, queue);
1244 }
1245
1246 static void
1247 mark_pinned_objects_in_block (MSBlockInfo *block, size_t first_entry, size_t last_entry, SgenGrayQueue *queue)
1248 {
1249         void **entry, **end;
1250         int last_index = -1;
1251
1252         if (first_entry == last_entry)
1253                 return;
1254
1255         block->has_pinned = TRUE;
1256
1257         entry = sgen_pinning_get_entry (first_entry);
1258         end = sgen_pinning_get_entry (last_entry);
1259
1260         for (; entry < end; ++entry) {
1261                 int index = MS_BLOCK_OBJ_INDEX (*entry, block);
1262                 char *obj;
1263                 SGEN_ASSERT (9, index >= 0 && index < MS_BLOCK_FREE / block->obj_size, "invalid object %p index %d max-index %d", *entry, index, MS_BLOCK_FREE / block->obj_size);
1264                 if (index == last_index)
1265                         continue;
1266                 obj = MS_BLOCK_OBJ (block, index);
1267                 MS_MARK_OBJECT_AND_ENQUEUE_CHECKED (obj, sgen_obj_get_descriptor (obj), block, queue);
1268                 last_index = index;
1269         }
1270 }
1271
1272 static inline void
1273 sweep_block_for_size (MSBlockInfo *block, int count, int obj_size)
1274 {
1275         int obj_index;
1276
1277         for (obj_index = 0; obj_index < count; ++obj_index) {
1278                 int word, bit;
1279                 void *obj = MS_BLOCK_OBJ_FOR_SIZE (block, obj_index, obj_size);
1280
1281                 MS_CALC_MARK_BIT (word, bit, obj);
1282                 if (MS_MARK_BIT (block, word, bit)) {
1283                         SGEN_ASSERT (9, MS_OBJ_ALLOCED (obj, block), "object %p not allocated", obj);
1284                 } else {
1285                         /* an unmarked object */
1286                         if (MS_OBJ_ALLOCED (obj, block)) {
1287                                 /*
1288                                  * FIXME: Merge consecutive
1289                                  * slots for lower reporting
1290                                  * overhead.  Maybe memset
1291                                  * will also benefit?
1292                                  */
1293                                 binary_protocol_empty (obj, obj_size);
1294                                 memset (obj, 0, obj_size);
1295                         }
1296                         *(void**)obj = block->free_list;
1297                         block->free_list = obj;
1298                 }
1299         }
1300 }
1301
1302 static inline gboolean
1303 try_set_block_state (MSBlockInfo *block, gint32 new_state, gint32 expected_state)
1304 {
1305         gint32 old_state = SGEN_CAS (&block->state, new_state, expected_state);
1306         gboolean success = old_state == expected_state;
1307         if (success)
1308                 binary_protocol_block_set_state (block, MS_BLOCK_SIZE, old_state, new_state);
1309         return success;
1310 }
1311
1312 static inline void
1313 set_block_state (MSBlockInfo *block, gint32 new_state, gint32 expected_state)
1314 {
1315         SGEN_ASSERT (6, block->state == expected_state, "Block state incorrect before set");
1316         block->state = new_state;
1317 }
1318
1319 /*
1320  * If `block` needs sweeping, sweep it and return TRUE.  Otherwise return FALSE.
1321  *
1322  * Sweeping means iterating through the block's slots and building the free-list from the
1323  * unmarked ones.  They will also be zeroed.  The mark bits will be reset.
1324  */
1325 static gboolean
1326 sweep_block (MSBlockInfo *block)
1327 {
1328         int count;
1329         void *reversed = NULL;
1330
1331  retry:
1332         switch (block->state) {
1333         case BLOCK_STATE_SWEPT:
1334                 return FALSE;
1335         case BLOCK_STATE_MARKING:
1336         case BLOCK_STATE_CHECKING:
1337                 SGEN_ASSERT (0, FALSE, "How did we get to sweep a block that's being marked or being checked?");
1338                 goto retry;
1339         case BLOCK_STATE_SWEEPING:
1340                 /* FIXME: Do this more elegantly */
1341                 g_usleep (100);
1342                 goto retry;
1343         case BLOCK_STATE_NEED_SWEEPING:
1344                 if (!try_set_block_state (block, BLOCK_STATE_SWEEPING, BLOCK_STATE_NEED_SWEEPING))
1345                         goto retry;
1346                 break;
1347         default:
1348                 SGEN_ASSERT (0, FALSE, "Illegal block state");
1349         }
1350
1351         SGEN_ASSERT (6, block->state == BLOCK_STATE_SWEEPING, "How did we get here without setting state to sweeping?");
1352
1353         count = MS_BLOCK_FREE / block->obj_size;
1354
1355         block->free_list = NULL;
1356
1357         /* Use inline instances specialized to constant sizes, this allows the compiler to replace the memset calls with inline code */
1358         // FIXME: Add more sizes
1359         switch (block->obj_size) {
1360         case 16:
1361                 sweep_block_for_size (block, count, 16);
1362                 break;
1363         default:
1364                 sweep_block_for_size (block, count, block->obj_size);
1365                 break;
1366         }
1367
1368         /* reset mark bits */
1369         memset (block->mark_words, 0, sizeof (mword) * MS_NUM_MARK_WORDS);
1370
1371         /* Reverse free list so that it's in address order */
1372         reversed = NULL;
1373         while (block->free_list) {
1374                 void *next = *(void**)block->free_list;
1375                 *(void**)block->free_list = reversed;
1376                 reversed = block->free_list;
1377                 block->free_list = next;
1378         }
1379         block->free_list = reversed;
1380
1381         mono_memory_write_barrier ();
1382
1383         set_block_state (block, BLOCK_STATE_SWEPT, BLOCK_STATE_SWEEPING);
1384
1385         return TRUE;
1386 }
1387
1388 static inline int
1389 bitcount (mword d)
1390 {
1391         int count = 0;
1392
1393 #ifdef __GNUC__
1394         if (sizeof (mword) == sizeof (unsigned long))
1395                 count += __builtin_popcountl (d);
1396         else
1397                 count += __builtin_popcount (d);
1398 #else
1399         while (d) {
1400                 count ++;
1401                 d &= (d - 1);
1402         }
1403 #endif
1404         return count;
1405 }
1406
1407 /* statistics for evacuation */
1408 static size_t *sweep_slots_available;
1409 static size_t *sweep_slots_used;
1410 static size_t *sweep_num_blocks;
1411
1412 static volatile size_t num_major_sections_before_sweep;
1413 static volatile size_t num_major_sections_freed_in_sweep;
1414
1415 static void
1416 sweep_start (void)
1417 {
1418         int i;
1419
1420         for (i = 0; i < num_block_obj_sizes; ++i)
1421                 sweep_slots_available [i] = sweep_slots_used [i] = sweep_num_blocks [i] = 0;
1422
1423         /* clear all the free lists */
1424         for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i) {
1425                 MSBlockInfo * volatile *free_blocks = free_block_lists [i];
1426                 int j;
1427                 for (j = 0; j < num_block_obj_sizes; ++j)
1428                         free_blocks [j] = NULL;
1429         }
1430 }
1431
1432 static void sweep_finish (void);
1433
1434 /*
1435  * If `wait` is TRUE and the block is currently being checked, this function will wait until
1436  * the checking has finished.
1437  *
1438  * Returns whether the block is still there.  If `wait` is FALSE, the return value will not
1439  * be correct, i.e. must not be used.
1440  */
1441 static gboolean
1442 ensure_block_is_checked_for_sweeping (int block_index, gboolean wait, gboolean *have_checked)
1443 {
1444         int count;
1445         gboolean have_live = FALSE;
1446         gboolean have_free = FALSE;
1447         int nused = 0;
1448         int block_state;
1449         int i;
1450         void *tagged_block;
1451         MSBlockInfo *block;
1452
1453         SGEN_ASSERT (6, sweep_in_progress (), "Why do we call this function if there's no sweep in progress?");
1454
1455         if (have_checked)
1456                 *have_checked = FALSE;
1457
1458  retry:
1459         tagged_block = *(void * volatile *)&allocated_blocks.data [block_index];
1460         if (!tagged_block)
1461                 return FALSE;
1462
1463         if (BLOCK_IS_TAGGED_CHECKING (tagged_block)) {
1464                 if (!wait)
1465                         return FALSE;
1466                 /* FIXME: do this more elegantly */
1467                 g_usleep (100);
1468                 goto retry;
1469         }
1470
1471         if (SGEN_CAS_PTR (&allocated_blocks.data [block_index], BLOCK_TAG_CHECKING (tagged_block), tagged_block) != tagged_block)
1472                 goto retry;
1473
1474         block = BLOCK_UNTAG (tagged_block);
1475         block_state = block->state;
1476
1477         if (!sweep_in_progress ()) {
1478                 SGEN_ASSERT (6, block_state != BLOCK_STATE_SWEEPING && block_state != BLOCK_STATE_CHECKING, "Invalid block state.");
1479                 if (!lazy_sweep)
1480                         SGEN_ASSERT (6, block_state != BLOCK_STATE_NEED_SWEEPING, "Invalid block state.");
1481         }
1482
1483         switch (block_state) {
1484         case BLOCK_STATE_SWEPT:
1485         case BLOCK_STATE_NEED_SWEEPING:
1486         case BLOCK_STATE_SWEEPING:
1487                 goto done;
1488         case BLOCK_STATE_MARKING:
1489                 break;
1490         case BLOCK_STATE_CHECKING:
1491                 SGEN_ASSERT (0, FALSE, "We set the CHECKING bit - how can the stage be CHECKING?");
1492                 goto done;
1493         default:
1494                 SGEN_ASSERT (0, FALSE, "Illegal block state");
1495                 break;
1496         }
1497
1498         SGEN_ASSERT (6, block->state == BLOCK_STATE_MARKING, "When we sweep all blocks must start out marking.");
1499         set_block_state (block, BLOCK_STATE_CHECKING, BLOCK_STATE_MARKING);
1500
1501         if (have_checked)
1502                 *have_checked = TRUE;
1503
1504         block->has_pinned = block->pinned;
1505
1506         block->is_to_space = FALSE;
1507
1508         count = MS_BLOCK_FREE / block->obj_size;
1509
1510         if (block->cardtable_mod_union) {
1511                 sgen_card_table_free_mod_union (block->cardtable_mod_union, MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE);
1512                 block->cardtable_mod_union = NULL;
1513         }
1514
1515         /* Count marked objects in the block */
1516         for (i = 0; i < MS_NUM_MARK_WORDS; ++i)
1517                 nused += bitcount (block->mark_words [i]);
1518
1519         if (nused)
1520                 have_live = TRUE;
1521         if (nused < count)
1522                 have_free = TRUE;
1523
1524         if (have_live) {
1525                 int obj_size_index = block->obj_size_index;
1526                 gboolean has_pinned = block->has_pinned;
1527
1528                 set_block_state (block, BLOCK_STATE_NEED_SWEEPING, BLOCK_STATE_CHECKING);
1529
1530                 /*
1531                  * FIXME: Go straight to SWEPT if there are no free slots.  We need
1532                  * to set the free slot list to NULL, though, and maybe update some
1533                  * statistics.
1534                  */
1535                 if (!lazy_sweep)
1536                         sweep_block (block);
1537
1538                 if (!has_pinned) {
1539                         ++sweep_num_blocks [obj_size_index];
1540                         sweep_slots_used [obj_size_index] += nused;
1541                         sweep_slots_available [obj_size_index] += count;
1542                 }
1543
1544                 /*
1545                  * If there are free slots in the block, add
1546                  * the block to the corresponding free list.
1547                  */
1548                 if (have_free) {
1549                         MSBlockInfo * volatile *free_blocks = FREE_BLOCKS (block->pinned, block->has_references);
1550
1551                         if (!lazy_sweep)
1552                                 SGEN_ASSERT (6, block->free_list, "How do we not have a free list when there are free slots?");
1553
1554                         add_free_block (free_blocks, obj_size_index, block);
1555                 }
1556
1557                 /* FIXME: Do we need the heap boundaries while we do nursery collections? */
1558                 update_heap_boundaries_for_block (block);
1559         } else {
1560                 /*
1561                  * Blocks without live objects are removed from the
1562                  * block list and freed.
1563                  */
1564                 SGEN_ASSERT (6, block_index < allocated_blocks.next_slot, "How did the number of blocks shrink?");
1565                 SGEN_ASSERT (6, allocated_blocks.data [block_index] == BLOCK_TAG_CHECKING (tagged_block), "How did the block move?");
1566
1567                 binary_protocol_empty (MS_BLOCK_OBJ (block, 0), (char*)MS_BLOCK_OBJ (block, count) - (char*)MS_BLOCK_OBJ (block, 0));
1568                 ms_free_block (block);
1569
1570                 SGEN_ATOMIC_ADD_P (num_major_sections, -1);
1571
1572                 tagged_block = NULL;
1573         }
1574
1575  done:
1576         allocated_blocks.data [block_index] = tagged_block;
1577         return !!tagged_block;
1578 }
1579
1580 static void
1581 sweep_job_func (void *thread_data_untyped, SgenThreadPoolJob *job)
1582 {
1583         int block_index;
1584         int num_blocks = num_major_sections_before_sweep;
1585
1586         SGEN_ASSERT (0, sweep_in_progress (), "Sweep thread called with wrong state");
1587         SGEN_ASSERT (0, num_blocks <= allocated_blocks.next_slot, "How did we lose blocks?");
1588
1589         /*
1590          * We traverse the block array from high to low.  Nursery collections will have to
1591          * cooperate with the sweep thread to finish sweeping, and they will traverse from
1592          * low to high, to avoid constantly colliding on the same blocks.
1593          */
1594         for (block_index = num_blocks - 1; block_index >= 0; --block_index) {
1595                 gboolean have_checked;
1596
1597                 /*
1598                  * The block might have been freed by another thread doing some checking
1599                  * work.
1600                  */
1601                 if (!ensure_block_is_checked_for_sweeping (block_index, TRUE, &have_checked))
1602                         ++num_major_sections_freed_in_sweep;
1603         }
1604
1605         while (!try_set_sweep_state (SWEEP_STATE_COMPACTING, SWEEP_STATE_SWEEPING)) {
1606                 /*
1607                  * The main GC thread is currently iterating over the block array to help us
1608                  * finish the sweep.  We have already finished, but we don't want to mess up
1609                  * that iteration, so we just wait for it.
1610                  */
1611                 g_usleep (100);
1612         }
1613
1614         if (SGEN_MAX_ASSERT_LEVEL >= 6) {
1615                 for (block_index = num_blocks; block_index < allocated_blocks.next_slot; ++block_index) {
1616                         MSBlockInfo *block = BLOCK_UNTAG (allocated_blocks.data [block_index]);
1617                         SGEN_ASSERT (6, block && block->state == BLOCK_STATE_SWEPT, "How did a new block to be swept get added while swept?");
1618                 }
1619         }
1620
1621         sgen_pointer_queue_remove_nulls (&allocated_blocks);
1622
1623         sweep_finish ();
1624
1625         sweep_job = NULL;
1626 }
1627
1628 static void
1629 sweep_finish (void)
1630 {
1631         mword total_evacuate_heap = 0;
1632         mword total_evacuate_saved = 0;
1633         int i;
1634
1635         for (i = 0; i < num_block_obj_sizes; ++i) {
1636                 float usage = (float)sweep_slots_used [i] / (float)sweep_slots_available [i];
1637                 if (sweep_num_blocks [i] > 5 && usage < evacuation_threshold) {
1638                         evacuate_block_obj_sizes [i] = TRUE;
1639                         /*
1640                         g_print ("slot size %d - %d of %d used\n",
1641                                         block_obj_sizes [i], slots_used [i], slots_available [i]);
1642                         */
1643                 } else {
1644                         evacuate_block_obj_sizes [i] = FALSE;
1645                 }
1646                 {
1647                         mword total_bytes = block_obj_sizes [i] * sweep_slots_available [i];
1648                         total_evacuate_heap += total_bytes;
1649                         if (evacuate_block_obj_sizes [i])
1650                                 total_evacuate_saved += total_bytes - block_obj_sizes [i] * sweep_slots_used [i];
1651                 }
1652         }
1653
1654         want_evacuation = (float)total_evacuate_saved / (float)total_evacuate_heap > (1 - concurrent_evacuation_threshold);
1655
1656         set_sweep_state (SWEEP_STATE_SWEPT, SWEEP_STATE_COMPACTING);
1657 }
1658
1659 static void
1660 major_sweep (void)
1661 {
1662         set_sweep_state (SWEEP_STATE_SWEEPING, SWEEP_STATE_NEED_SWEEPING);
1663
1664         sweep_start ();
1665
1666         SGEN_ASSERT (0, num_major_sections == allocated_blocks.next_slot, "We don't know how many blocks we have?");
1667
1668         num_major_sections_before_sweep = num_major_sections;
1669         num_major_sections_freed_in_sweep = 0;
1670
1671         SGEN_ASSERT (0, !sweep_job, "We haven't finished the last sweep?");
1672         if (concurrent_sweep) {
1673                 sweep_job = sgen_thread_pool_job_alloc ("sweep", sweep_job_func, sizeof (SgenThreadPoolJob));
1674                 sgen_thread_pool_job_enqueue (sweep_job);
1675         } else {
1676                 sweep_job_func (NULL, NULL);
1677         }
1678 }
1679
1680 static gboolean
1681 major_have_swept (void)
1682 {
1683         return sweep_state == SWEEP_STATE_SWEPT;
1684 }
1685
1686 static int count_pinned_ref;
1687 static int count_pinned_nonref;
1688 static int count_nonpinned_ref;
1689 static int count_nonpinned_nonref;
1690
1691 static void
1692 count_nonpinned_callback (char *obj, size_t size, void *data)
1693 {
1694         GCVTable *vtable = (GCVTable*)LOAD_VTABLE (obj);
1695
1696         if (sgen_client_vtable_has_references (vtable))
1697                 ++count_nonpinned_ref;
1698         else
1699                 ++count_nonpinned_nonref;
1700 }
1701
1702 static void
1703 count_pinned_callback (char *obj, size_t size, void *data)
1704 {
1705         GCVTable *vtable = (GCVTable*)LOAD_VTABLE (obj);
1706
1707         if (sgen_client_vtable_has_references (vtable))
1708                 ++count_pinned_ref;
1709         else
1710                 ++count_pinned_nonref;
1711 }
1712
1713 static G_GNUC_UNUSED void
1714 count_ref_nonref_objs (void)
1715 {
1716         int total;
1717
1718         count_pinned_ref = 0;
1719         count_pinned_nonref = 0;
1720         count_nonpinned_ref = 0;
1721         count_nonpinned_nonref = 0;
1722
1723         major_iterate_objects (ITERATE_OBJECTS_SWEEP_NON_PINNED, count_nonpinned_callback, NULL);
1724         major_iterate_objects (ITERATE_OBJECTS_SWEEP_PINNED, count_pinned_callback, NULL);
1725
1726         total = count_pinned_nonref + count_nonpinned_nonref + count_pinned_ref + count_nonpinned_ref;
1727
1728         g_print ("ref: %d pinned %d non-pinned   non-ref: %d pinned %d non-pinned  --  %.1f\n",
1729                         count_pinned_ref, count_nonpinned_ref,
1730                         count_pinned_nonref, count_nonpinned_nonref,
1731                         (count_pinned_nonref + count_nonpinned_nonref) * 100.0 / total);
1732 }
1733
1734 static int
1735 ms_calculate_block_obj_sizes (double factor, int *arr)
1736 {
1737         double target_size;
1738         int num_sizes = 0;
1739         int last_size = 0;
1740
1741         /*
1742          * Have every possible slot size starting with the minimal
1743          * object size up to and including four times that size.  Then
1744          * proceed by increasing geometrically with the given factor.
1745          */
1746
1747         for (int size = sizeof (MonoObject); size <= 4 * sizeof (MonoObject); size += SGEN_ALLOC_ALIGN) {
1748                 if (arr)
1749                         arr [num_sizes] = size;
1750                 ++num_sizes;
1751                 last_size = size;
1752         }
1753         target_size = (double)last_size;
1754
1755         do {
1756                 int target_count = (int)floor (MS_BLOCK_FREE / target_size);
1757                 int size = MIN ((MS_BLOCK_FREE / target_count) & ~(SGEN_ALLOC_ALIGN - 1), SGEN_MAX_SMALL_OBJ_SIZE);
1758
1759                 if (size != last_size) {
1760                         if (arr)
1761                                 arr [num_sizes] = size;
1762                         ++num_sizes;
1763                         last_size = size;
1764                 }
1765
1766                 target_size *= factor;
1767         } while (last_size < SGEN_MAX_SMALL_OBJ_SIZE);
1768
1769         return num_sizes;
1770 }
1771
1772 /* only valid during minor collections */
1773 static mword old_num_major_sections;
1774
1775 static void
1776 major_start_nursery_collection (void)
1777 {
1778 #ifdef MARKSWEEP_CONSISTENCY_CHECK
1779         consistency_check ();
1780 #endif
1781
1782         old_num_major_sections = num_major_sections;
1783 }
1784
1785 static void
1786 major_finish_nursery_collection (void)
1787 {
1788 #ifdef MARKSWEEP_CONSISTENCY_CHECK
1789         consistency_check ();
1790 #endif
1791 }
1792
1793 static void
1794 major_start_major_collection (void)
1795 {
1796         MSBlockInfo *block;
1797         int i;
1798
1799         major_finish_sweep_checking ();
1800
1801         /*
1802          * Clear the free lists for block sizes where we do evacuation.  For those block
1803          * sizes we will have to allocate new blocks.
1804          */
1805         for (i = 0; i < num_block_obj_sizes; ++i) {
1806                 if (!evacuate_block_obj_sizes [i])
1807                         continue;
1808
1809                 free_block_lists [0][i] = NULL;
1810                 free_block_lists [MS_BLOCK_FLAG_REFS][i] = NULL;
1811         }
1812
1813         if (lazy_sweep)
1814                 binary_protocol_sweep_begin (GENERATION_OLD, TRUE);
1815
1816         /* Sweep all unswept blocks and set them to MARKING */
1817         FOREACH_BLOCK_NO_LOCK (block) {
1818                 if (lazy_sweep)
1819                         sweep_block (block);
1820                 SGEN_ASSERT (0, block->state == BLOCK_STATE_SWEPT, "All blocks must be swept when we're pinning.");
1821                 set_block_state (block, BLOCK_STATE_MARKING, BLOCK_STATE_SWEPT);
1822         } END_FOREACH_BLOCK_NO_LOCK;
1823
1824         if (lazy_sweep)
1825                 binary_protocol_sweep_end (GENERATION_OLD, TRUE);
1826
1827         set_sweep_state (SWEEP_STATE_NEED_SWEEPING, SWEEP_STATE_SWEPT);
1828 }
1829
1830 static void
1831 major_finish_major_collection (ScannedObjectCounts *counts)
1832 {
1833 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
1834         if (binary_protocol_is_enabled ()) {
1835                 counts->num_scanned_objects = scanned_objects_list.next_slot;
1836
1837                 sgen_pointer_queue_sort_uniq (&scanned_objects_list);
1838                 counts->num_unique_scanned_objects = scanned_objects_list.next_slot;
1839
1840                 sgen_pointer_queue_clear (&scanned_objects_list);
1841         }
1842 #endif
1843 }
1844
1845 #if SIZEOF_VOID_P != 8
1846 static int
1847 compare_pointers (const void *va, const void *vb) {
1848         char *a = *(char**)va, *b = *(char**)vb;
1849         if (a < b)
1850                 return -1;
1851         if (a > b)
1852                 return 1;
1853         return 0;
1854 }
1855 #endif
1856
1857 /*
1858  * This is called with sweep completed and the world stopped.
1859  */
1860 static void
1861 major_free_swept_blocks (size_t allowance)
1862 {
1863         /* FIXME: This is probably too much.  It's assuming all objects are small. */
1864         size_t section_reserve = allowance / MS_BLOCK_SIZE;
1865
1866         SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Sweeping must have finished before freeing blocks");
1867
1868 #if SIZEOF_VOID_P != 8
1869         {
1870                 int i, num_empty_blocks_orig, num_blocks, arr_length;
1871                 void *block;
1872                 void **empty_block_arr;
1873                 void **rebuild_next;
1874
1875 #ifdef TARGET_WIN32
1876                 /*
1877                  * sgen_free_os_memory () asserts in mono_vfree () because windows doesn't like freeing the middle of
1878                  * a VirtualAlloc ()-ed block.
1879                  */
1880                 return;
1881 #endif
1882
1883                 if (num_empty_blocks <= section_reserve)
1884                         return;
1885                 SGEN_ASSERT (0, num_empty_blocks > 0, "section reserve can't be negative");
1886
1887                 num_empty_blocks_orig = num_empty_blocks;
1888                 empty_block_arr = (void**)sgen_alloc_internal_dynamic (sizeof (void*) * num_empty_blocks_orig,
1889                                 INTERNAL_MEM_MS_BLOCK_INFO_SORT, FALSE);
1890                 if (!empty_block_arr)
1891                         goto fallback;
1892
1893                 i = 0;
1894                 for (block = empty_blocks; block; block = *(void**)block)
1895                         empty_block_arr [i++] = block;
1896                 SGEN_ASSERT (0, i == num_empty_blocks, "empty block count wrong");
1897
1898                 sgen_qsort (empty_block_arr, num_empty_blocks, sizeof (void*), compare_pointers);
1899
1900                 /*
1901                  * We iterate over the free blocks, trying to find MS_BLOCK_ALLOC_NUM
1902                  * contiguous ones.  If we do, we free them.  If that's not enough to get to
1903                  * section_reserve, we halve the number of contiguous blocks we're looking
1904                  * for and have another go, until we're done with looking for pairs of
1905                  * blocks, at which point we give up and go to the fallback.
1906                  */
1907                 arr_length = num_empty_blocks_orig;
1908                 num_blocks = MS_BLOCK_ALLOC_NUM;
1909                 while (num_empty_blocks > section_reserve && num_blocks > 1) {
1910                         int first = -1;
1911                         int dest = 0;
1912
1913                         dest = 0;
1914                         for (i = 0; i < arr_length; ++i) {
1915                                 int d = dest;
1916                                 void *block = empty_block_arr [i];
1917                                 SGEN_ASSERT (6, block, "we're not shifting correctly");
1918                                 if (i != dest) {
1919                                         empty_block_arr [dest] = block;
1920                                         /*
1921                                          * This is not strictly necessary, but we're
1922                                          * cautious.
1923                                          */
1924                                         empty_block_arr [i] = NULL;
1925                                 }
1926                                 ++dest;
1927
1928                                 if (first < 0) {
1929                                         first = d;
1930                                         continue;
1931                                 }
1932
1933                                 SGEN_ASSERT (6, first >= 0 && d > first, "algorithm is wrong");
1934
1935                                 if ((char*)block != ((char*)empty_block_arr [d-1]) + MS_BLOCK_SIZE) {
1936                                         first = d;
1937                                         continue;
1938                                 }
1939
1940                                 if (d + 1 - first == num_blocks) {
1941                                         /*
1942                                          * We found num_blocks contiguous blocks.  Free them
1943                                          * and null their array entries.  As an optimization
1944                                          * we could, instead of nulling the entries, shift
1945                                          * the following entries over to the left, while
1946                                          * we're iterating.
1947                                          */
1948                                         int j;
1949                                         sgen_free_os_memory (empty_block_arr [first], MS_BLOCK_SIZE * num_blocks, SGEN_ALLOC_HEAP);
1950                                         for (j = first; j <= d; ++j)
1951                                                 empty_block_arr [j] = NULL;
1952                                         dest = first;
1953                                         first = -1;
1954
1955                                         num_empty_blocks -= num_blocks;
1956
1957                                         stat_major_blocks_freed += num_blocks;
1958                                         if (num_blocks == MS_BLOCK_ALLOC_NUM)
1959                                                 stat_major_blocks_freed_ideal += num_blocks;
1960                                         else
1961                                                 stat_major_blocks_freed_less_ideal += num_blocks;
1962
1963                                 }
1964                         }
1965
1966                         SGEN_ASSERT (6, dest <= i && dest <= arr_length, "array length is off");
1967                         arr_length = dest;
1968                         SGEN_ASSERT (6, arr_length == num_empty_blocks, "array length is off");
1969
1970                         num_blocks >>= 1;
1971                 }
1972
1973                 /* rebuild empty_blocks free list */
1974                 rebuild_next = (void**)&empty_blocks;
1975                 for (i = 0; i < arr_length; ++i) {
1976                         void *block = empty_block_arr [i];
1977                         SGEN_ASSERT (6, block, "we're missing blocks");
1978                         *rebuild_next = block;
1979                         rebuild_next = (void**)block;
1980                 }
1981                 *rebuild_next = NULL;
1982
1983                 /* free array */
1984                 sgen_free_internal_dynamic (empty_block_arr, sizeof (void*) * num_empty_blocks_orig, INTERNAL_MEM_MS_BLOCK_INFO_SORT);
1985         }
1986
1987         SGEN_ASSERT (0, num_empty_blocks >= 0, "we freed more blocks than we had in the first place?");
1988
1989  fallback:
1990         /*
1991          * This is our threshold.  If there's not more empty than used blocks, we won't
1992          * release uncontiguous blocks, in fear of fragmenting the address space.
1993          */
1994         if (num_empty_blocks <= num_major_sections)
1995                 return;
1996 #endif
1997
1998         while (num_empty_blocks > section_reserve) {
1999                 void *next = *(void**)empty_blocks;
2000                 sgen_free_os_memory (empty_blocks, MS_BLOCK_SIZE, SGEN_ALLOC_HEAP);
2001                 empty_blocks = next;
2002                 /*
2003                  * Needs not be atomic because this is running
2004                  * single-threaded.
2005                  */
2006                 --num_empty_blocks;
2007
2008                 ++stat_major_blocks_freed;
2009 #if SIZEOF_VOID_P != 8
2010                 ++stat_major_blocks_freed_individual;
2011 #endif
2012         }
2013 }
2014
2015 static void
2016 major_pin_objects (SgenGrayQueue *queue)
2017 {
2018         MSBlockInfo *block;
2019
2020         FOREACH_BLOCK_NO_LOCK (block) {
2021                 size_t first_entry, last_entry;
2022                 SGEN_ASSERT (6, block_is_swept_or_marking (block), "All blocks must be swept when we're pinning.");
2023                 sgen_find_optimized_pin_queue_area (MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SKIP, MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE,
2024                                 &first_entry, &last_entry);
2025                 mark_pinned_objects_in_block (block, first_entry, last_entry, queue);
2026         } END_FOREACH_BLOCK_NO_LOCK;
2027 }
2028
2029 static void
2030 major_init_to_space (void)
2031 {
2032 }
2033
2034 static void
2035 major_report_pinned_memory_usage (void)
2036 {
2037         g_assert_not_reached ();
2038 }
2039
2040 static gint64
2041 major_get_used_size (void)
2042 {
2043         gint64 size = 0;
2044         MSBlockInfo *block;
2045
2046         /*
2047          * We're holding the GC lock, but the sweep thread might be running.  Make sure it's
2048          * finished, then we can iterate over the block array.
2049          */
2050         major_finish_sweep_checking ();
2051
2052         FOREACH_BLOCK_NO_LOCK_CONDITION (TRUE, block) {
2053                 int count = MS_BLOCK_FREE / block->obj_size;
2054                 void **iter;
2055                 size += count * block->obj_size;
2056                 for (iter = block->free_list; iter; iter = (void**)*iter)
2057                         size -= block->obj_size;
2058         } END_FOREACH_BLOCK_NO_LOCK;
2059
2060         return size;
2061 }
2062
2063 /* FIXME: return number of bytes, not of sections */
2064 static size_t
2065 get_num_major_sections (void)
2066 {
2067         return num_major_sections;
2068 }
2069
2070 /*
2071  * Returns the number of bytes in blocks that were present when the last sweep was
2072  * initiated, and were not freed during the sweep.  They are the basis for calculating the
2073  * allowance.
2074  */
2075 static size_t
2076 get_bytes_survived_last_sweep (void)
2077 {
2078         SGEN_ASSERT (0, sweep_state == SWEEP_STATE_SWEPT, "Can only query unswept sections after sweep");
2079         return (num_major_sections_before_sweep - num_major_sections_freed_in_sweep) * MS_BLOCK_SIZE;
2080 }
2081
2082 static gboolean
2083 major_handle_gc_param (const char *opt)
2084 {
2085         if (g_str_has_prefix (opt, "evacuation-threshold=")) {
2086                 const char *arg = strchr (opt, '=') + 1;
2087                 int percentage = atoi (arg);
2088                 if (percentage < 0 || percentage > 100) {
2089                         fprintf (stderr, "evacuation-threshold must be an integer in the range 0-100.\n");
2090                         exit (1);
2091                 }
2092                 evacuation_threshold = (float)percentage / 100.0f;
2093                 return TRUE;
2094         } else if (!strcmp (opt, "lazy-sweep")) {
2095                 lazy_sweep = TRUE;
2096                 return TRUE;
2097         } else if (!strcmp (opt, "no-lazy-sweep")) {
2098                 lazy_sweep = FALSE;
2099                 return TRUE;
2100         } else if (!strcmp (opt, "concurrent-sweep")) {
2101                 concurrent_sweep = TRUE;
2102                 return TRUE;
2103         } else if (!strcmp (opt, "no-concurrent-sweep")) {
2104                 concurrent_sweep = FALSE;
2105                 return TRUE;
2106         }
2107
2108         return FALSE;
2109 }
2110
2111 static void
2112 major_print_gc_param_usage (void)
2113 {
2114         fprintf (stderr,
2115                         ""
2116                         "  evacuation-threshold=P (where P is a percentage, an integer in 0-100)\n"
2117                         "  (no-)lazy-sweep\n"
2118                         "  (no-)concurrent-sweep\n"
2119                         );
2120 }
2121
2122 /*
2123  * This callback is used to clear cards, move cards to the shadow table and do counting.
2124  */
2125 static void
2126 major_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
2127 {
2128         MSBlockInfo *block;
2129         gboolean has_references;
2130
2131         major_finish_sweep_checking ();
2132         FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK (block, has_references) {
2133                 if (has_references)
2134                         callback ((mword)MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE);
2135         } END_FOREACH_BLOCK_NO_LOCK;
2136 }
2137
2138 #ifdef HEAVY_STATISTICS
2139 extern guint64 marked_cards;
2140 extern guint64 scanned_cards;
2141 extern guint64 scanned_objects;
2142 extern guint64 remarked_cards;
2143 #endif
2144
2145 #define CARD_WORDS_PER_BLOCK (CARDS_PER_BLOCK / SIZEOF_VOID_P)
2146 /*
2147  * MS blocks are 16K aligned.
2148  * Cardtables are 4K aligned, at least.
2149  * This means that the cardtable of a given block is 32 bytes aligned.
2150  */
2151 static guint8*
2152 initial_skip_card (guint8 *card_data)
2153 {
2154         mword *cards = (mword*)card_data;
2155         mword card;
2156         int i;
2157         for (i = 0; i < CARD_WORDS_PER_BLOCK; ++i) {
2158                 card = cards [i];
2159                 if (card)
2160                         break;
2161         }
2162
2163         if (i == CARD_WORDS_PER_BLOCK)
2164                 return card_data + CARDS_PER_BLOCK;
2165
2166 #if defined(__i386__) && defined(__GNUC__)
2167         return card_data + i * 4 +  (__builtin_ffs (card) - 1) / 8;
2168 #elif defined(__x86_64__) && defined(__GNUC__)
2169         return card_data + i * 8 +  (__builtin_ffsll (card) - 1) / 8;
2170 #elif defined(__s390x__) && defined(__GNUC__)
2171         return card_data + i * 8 +  (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
2172 #else
2173         for (i = i * SIZEOF_VOID_P; i < CARDS_PER_BLOCK; ++i) {
2174                 if (card_data [i])
2175                         return &card_data [i];
2176         }
2177         return card_data;
2178 #endif
2179 }
2180
2181 #define MS_BLOCK_OBJ_INDEX_FAST(o,b,os) (((char*)(o) - ((b) + MS_BLOCK_SKIP)) / (os))
2182 #define MS_BLOCK_OBJ_FAST(b,os,i)                       ((b) + MS_BLOCK_SKIP + (os) * (i))
2183 #define MS_OBJ_ALLOCED_FAST(o,b)                (*(void**)(o) && (*(char**)(o) < (b) || *(char**)(o) >= (b) + MS_BLOCK_SIZE))
2184
2185 static void
2186 scan_card_table_for_block (MSBlockInfo *block, gboolean mod_union, ScanCopyContext ctx)
2187 {
2188         SgenGrayQueue *queue = ctx.queue;
2189         ScanObjectFunc scan_func = ctx.ops->scan_object;
2190 #ifndef SGEN_HAVE_OVERLAPPING_CARDS
2191         guint8 cards_copy [CARDS_PER_BLOCK];
2192 #endif
2193         gboolean small_objects;
2194         int block_obj_size;
2195         char *block_start;
2196         guint8 *card_data, *card_base;
2197         guint8 *card_data_end;
2198         char *scan_front = NULL;
2199
2200         block_obj_size = block->obj_size;
2201         small_objects = block_obj_size < CARD_SIZE_IN_BYTES;
2202
2203         block_start = MS_BLOCK_FOR_BLOCK_INFO (block);
2204
2205         /*
2206          * This is safe in face of card aliasing for the following reason:
2207          *
2208          * Major blocks are 16k aligned, or 32 cards aligned.
2209          * Cards aliasing happens in powers of two, so as long as major blocks are aligned to their
2210          * sizes, they won't overflow the cardtable overlap modulus.
2211          */
2212         if (mod_union) {
2213                 card_data = card_base = block->cardtable_mod_union;
2214                 /*
2215                  * This happens when the nursery collection that precedes finishing
2216                  * the concurrent collection allocates new major blocks.
2217                  */
2218                 if (!card_data)
2219                         return;
2220         } else {
2221 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
2222                 card_data = card_base = sgen_card_table_get_card_scan_address ((mword)block_start);
2223 #else
2224                 if (!sgen_card_table_get_card_data (cards_copy, (mword)block_start, CARDS_PER_BLOCK))
2225                         return;
2226                 card_data = card_base = cards_copy;
2227 #endif
2228         }
2229         card_data_end = card_data + CARDS_PER_BLOCK;
2230
2231         card_data += MS_BLOCK_SKIP >> CARD_BITS;
2232
2233         card_data = initial_skip_card (card_data);
2234         while (card_data < card_data_end) {
2235                 size_t card_index, first_object_index;
2236                 char *start;
2237                 char *end;
2238                 char *first_obj, *obj;
2239
2240                 HEAVY_STAT (++scanned_cards);
2241
2242                 if (!*card_data) {
2243                         ++card_data;
2244                         continue;
2245                 }
2246
2247                 card_index = card_data - card_base;
2248                 start = (char*)(block_start + card_index * CARD_SIZE_IN_BYTES);
2249                 end = start + CARD_SIZE_IN_BYTES;
2250
2251                 if (!block_is_swept_or_marking (block))
2252                         sweep_block (block);
2253
2254                 HEAVY_STAT (++marked_cards);
2255
2256                 if (small_objects)
2257                         sgen_card_table_prepare_card_for_scanning (card_data);
2258
2259                 /*
2260                  * If the card we're looking at starts at or in the block header, we
2261                  * must start at the first object in the block, without calculating
2262                  * the index of the object we're hypothetically starting at, because
2263                  * it would be negative.
2264                  */
2265                 if (card_index <= (MS_BLOCK_SKIP >> CARD_BITS))
2266                         first_object_index = 0;
2267                 else
2268                         first_object_index = MS_BLOCK_OBJ_INDEX_FAST (start, block_start, block_obj_size);
2269
2270                 obj = first_obj = (char*)MS_BLOCK_OBJ_FAST (block_start, block_obj_size, first_object_index);
2271
2272                 binary_protocol_card_scan (first_obj, end - first_obj);
2273
2274                 while (obj < end) {
2275                         if (obj < scan_front || !MS_OBJ_ALLOCED_FAST (obj, block_start))
2276                                 goto next_object;
2277
2278                         if (mod_union) {
2279                                 /* FIXME: do this more efficiently */
2280                                 int w, b;
2281                                 MS_CALC_MARK_BIT (w, b, obj);
2282                                 if (!MS_MARK_BIT (block, w, b))
2283                                         goto next_object;
2284                         }
2285
2286                         if (small_objects) {
2287                                 HEAVY_STAT (++scanned_objects);
2288                                 scan_func (obj, sgen_obj_get_descriptor (obj), queue);
2289                         } else {
2290                                 size_t offset = sgen_card_table_get_card_offset (obj, block_start);
2291                                 sgen_cardtable_scan_object (obj, block_obj_size, card_base + offset, mod_union, ctx);
2292                         }
2293                 next_object:
2294                         obj += block_obj_size;
2295                         g_assert (scan_front <= obj);
2296                         scan_front = obj;
2297                 }
2298
2299                 HEAVY_STAT (if (*card_data) ++remarked_cards);
2300
2301                 if (small_objects)
2302                         ++card_data;
2303                 else
2304                         card_data = card_base + sgen_card_table_get_card_offset (obj, block_start);
2305         }
2306 }
2307
2308 static void
2309 major_scan_card_table (gboolean mod_union, ScanCopyContext ctx)
2310 {
2311         MSBlockInfo *block;
2312         gboolean has_references;
2313
2314         if (!concurrent_mark)
2315                 g_assert (!mod_union);
2316
2317         major_finish_sweep_checking ();
2318         FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK (block, has_references) {
2319 #ifdef PREFETCH_CARDS
2320                 int prefetch_index = __index + 6;
2321                 if (prefetch_index < allocated_blocks.next_slot) {
2322                         MSBlockInfo *prefetch_block = BLOCK_UNTAG (allocated_blocks.data [prefetch_index]);
2323                         guint8 *prefetch_cards = sgen_card_table_get_card_scan_address ((mword)MS_BLOCK_FOR_BLOCK_INFO (prefetch_block));
2324                         PREFETCH_READ (prefetch_block);
2325                         PREFETCH_WRITE (prefetch_cards);
2326                         PREFETCH_WRITE (prefetch_cards + 32);
2327                 }
2328 #endif
2329
2330                 if (!has_references)
2331                         continue;
2332
2333                 scan_card_table_for_block (block, mod_union, ctx);
2334         } END_FOREACH_BLOCK_NO_LOCK;
2335 }
2336
2337 static void
2338 major_count_cards (long long *num_total_cards, long long *num_marked_cards)
2339 {
2340         MSBlockInfo *block;
2341         gboolean has_references;
2342         long long total_cards = 0;
2343         long long marked_cards = 0;
2344
2345         if (sweep_in_progress ()) {
2346                 *num_total_cards = -1;
2347                 *num_marked_cards = -1;
2348                 return;
2349         }
2350
2351         FOREACH_BLOCK_HAS_REFERENCES_NO_LOCK (block, has_references) {
2352                 guint8 *cards = sgen_card_table_get_card_scan_address ((mword) MS_BLOCK_FOR_BLOCK_INFO (block));
2353                 int i;
2354
2355                 if (!has_references)
2356                         continue;
2357
2358                 total_cards += CARDS_PER_BLOCK;
2359                 for (i = 0; i < CARDS_PER_BLOCK; ++i) {
2360                         if (cards [i])
2361                                 ++marked_cards;
2362                 }
2363         } END_FOREACH_BLOCK_NO_LOCK;
2364
2365         *num_total_cards = total_cards;
2366         *num_marked_cards = marked_cards;
2367 }
2368
2369 static void
2370 update_cardtable_mod_union (void)
2371 {
2372         MSBlockInfo *block;
2373
2374         FOREACH_BLOCK_NO_LOCK (block) {
2375                 size_t num_cards;
2376                 guint8 *mod_union = get_cardtable_mod_union_for_block (block, TRUE);
2377                 sgen_card_table_update_mod_union (mod_union, MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE, &num_cards);
2378                 SGEN_ASSERT (6, num_cards == CARDS_PER_BLOCK, "Number of cards calculation is wrong");
2379         } END_FOREACH_BLOCK_NO_LOCK;
2380 }
2381
2382 #undef pthread_create
2383
2384 static void
2385 post_param_init (SgenMajorCollector *collector)
2386 {
2387         collector->sweeps_lazily = lazy_sweep;
2388         collector->needs_thread_pool = concurrent_mark || concurrent_sweep;
2389 }
2390
2391 static void
2392 sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurrent)
2393 {
2394         int i;
2395
2396         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_MS_BLOCK_INFO, sizeof (MSBlockInfo));
2397
2398         num_block_obj_sizes = ms_calculate_block_obj_sizes (MS_BLOCK_OBJ_SIZE_FACTOR, NULL);
2399         block_obj_sizes = sgen_alloc_internal_dynamic (sizeof (int) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2400         ms_calculate_block_obj_sizes (MS_BLOCK_OBJ_SIZE_FACTOR, block_obj_sizes);
2401
2402         evacuate_block_obj_sizes = sgen_alloc_internal_dynamic (sizeof (gboolean) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2403         for (i = 0; i < num_block_obj_sizes; ++i)
2404                 evacuate_block_obj_sizes [i] = FALSE;
2405
2406         sweep_slots_available = sgen_alloc_internal_dynamic (sizeof (size_t) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2407         sweep_slots_used = sgen_alloc_internal_dynamic (sizeof (size_t) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2408         sweep_num_blocks = sgen_alloc_internal_dynamic (sizeof (size_t) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2409
2410         /*
2411         {
2412                 int i;
2413                 g_print ("block object sizes:\n");
2414                 for (i = 0; i < num_block_obj_sizes; ++i)
2415                         g_print ("%d\n", block_obj_sizes [i]);
2416         }
2417         */
2418
2419         for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i)
2420                 free_block_lists [i] = sgen_alloc_internal_dynamic (sizeof (MSBlockInfo*) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
2421
2422         for (i = 0; i < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES; ++i)
2423                 fast_block_obj_size_indexes [i] = ms_find_block_obj_size_index (i * 8);
2424         for (i = 0; i < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES * 8; ++i)
2425                 g_assert (MS_BLOCK_OBJ_SIZE_INDEX (i) == ms_find_block_obj_size_index (i));
2426
2427         mono_counters_register ("# major blocks allocated", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_alloced);
2428         mono_counters_register ("# major blocks freed", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed);
2429         mono_counters_register ("# major blocks lazy swept", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_lazy_swept);
2430         mono_counters_register ("# major objects evacuated", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_objects_evacuated);
2431 #if SIZEOF_VOID_P != 8
2432         mono_counters_register ("# major blocks freed ideally", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed_ideal);
2433         mono_counters_register ("# major blocks freed less ideally", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed_less_ideal);
2434         mono_counters_register ("# major blocks freed individually", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_freed_individual);
2435         mono_counters_register ("# major blocks allocated less ideally", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_major_blocks_alloced_less_ideal);
2436 #endif
2437
2438         collector->section_size = MAJOR_SECTION_SIZE;
2439
2440         concurrent_mark = is_concurrent;
2441         collector->is_concurrent = is_concurrent;
2442         collector->needs_thread_pool = is_concurrent || concurrent_sweep;
2443         if (is_concurrent)
2444                 collector->want_synchronous_collection = &want_evacuation;
2445         else
2446                 collector->want_synchronous_collection = NULL;
2447         collector->get_and_reset_num_major_objects_marked = major_get_and_reset_num_major_objects_marked;
2448         collector->supports_cardtable = TRUE;
2449
2450         collector->alloc_heap = major_alloc_heap;
2451         collector->is_object_live = major_is_object_live;
2452         collector->alloc_small_pinned_obj = major_alloc_small_pinned_obj;
2453         collector->alloc_degraded = major_alloc_degraded;
2454
2455         collector->alloc_object = major_alloc_object;
2456         collector->free_pinned_object = free_pinned_object;
2457         collector->iterate_objects = major_iterate_objects;
2458         collector->free_non_pinned_object = major_free_non_pinned_object;
2459         collector->pin_objects = major_pin_objects;
2460         collector->pin_major_object = pin_major_object;
2461         collector->scan_card_table = major_scan_card_table;
2462         collector->iterate_live_block_ranges = (void*)(void*) major_iterate_live_block_ranges;
2463         if (is_concurrent) {
2464                 collector->update_cardtable_mod_union = update_cardtable_mod_union;
2465                 collector->get_cardtable_mod_union_for_object = major_get_cardtable_mod_union_for_reference;
2466         }
2467         collector->init_to_space = major_init_to_space;
2468         collector->sweep = major_sweep;
2469         collector->have_swept = major_have_swept;
2470         collector->finish_sweeping = major_finish_sweep_checking;
2471         collector->free_swept_blocks = major_free_swept_blocks;
2472         collector->check_scan_starts = major_check_scan_starts;
2473         collector->dump_heap = major_dump_heap;
2474         collector->get_used_size = major_get_used_size;
2475         collector->start_nursery_collection = major_start_nursery_collection;
2476         collector->finish_nursery_collection = major_finish_nursery_collection;
2477         collector->start_major_collection = major_start_major_collection;
2478         collector->finish_major_collection = major_finish_major_collection;
2479         collector->ptr_is_in_non_pinned_space = major_ptr_is_in_non_pinned_space;
2480         collector->obj_is_from_pinned_alloc = obj_is_from_pinned_alloc;
2481         collector->report_pinned_memory_usage = major_report_pinned_memory_usage;
2482         collector->get_num_major_sections = get_num_major_sections;
2483         collector->get_bytes_survived_last_sweep = get_bytes_survived_last_sweep;
2484         collector->handle_gc_param = major_handle_gc_param;
2485         collector->print_gc_param_usage = major_print_gc_param_usage;
2486         collector->post_param_init = post_param_init;
2487         collector->is_valid_object = major_is_valid_object;
2488         collector->describe_pointer = major_describe_pointer;
2489         collector->count_cards = major_count_cards;
2490
2491         collector->major_ops_serial.copy_or_mark_object = major_copy_or_mark_object_canonical;
2492         collector->major_ops_serial.scan_object = major_scan_object_with_evacuation;
2493         if (is_concurrent) {
2494                 collector->major_ops_concurrent_start.copy_or_mark_object = major_copy_or_mark_object_concurrent_canonical;
2495                 collector->major_ops_concurrent_start.scan_object = major_scan_object_no_mark_concurrent_start;
2496
2497                 collector->major_ops_concurrent.copy_or_mark_object = major_copy_or_mark_object_concurrent_canonical;
2498                 collector->major_ops_concurrent.scan_object = major_scan_object_no_mark_concurrent;
2499
2500                 collector->major_ops_concurrent_finish.copy_or_mark_object = major_copy_or_mark_object_concurrent_finish_canonical;
2501                 collector->major_ops_concurrent_finish.scan_object = major_scan_object_no_evacuation;
2502                 collector->major_ops_concurrent_finish.scan_vtype = major_scan_vtype_concurrent_finish;
2503         }
2504
2505 #if !defined (FIXED_HEAP) && !defined (SGEN_PARALLEL_MARK)
2506         if (!is_concurrent)
2507                 collector->drain_gray_stack = drain_gray_stack;
2508
2509 #ifdef HEAVY_STATISTICS
2510         mono_counters_register ("Optimized copy", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy);
2511         mono_counters_register ("Optimized copy nursery", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_nursery);
2512         mono_counters_register ("Optimized copy nursery forwarded", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_nursery_forwarded);
2513         mono_counters_register ("Optimized copy nursery pinned", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_nursery_pinned);
2514         mono_counters_register ("Optimized copy major", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major);
2515         mono_counters_register ("Optimized copy major small fast", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_small_fast);
2516         mono_counters_register ("Optimized copy major small slow", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_small_slow);
2517         mono_counters_register ("Optimized copy major large", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_copy_major_large);
2518         mono_counters_register ("Optimized major scan", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_major_scan);
2519         mono_counters_register ("Optimized major scan no refs", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_optimized_major_scan_no_refs);
2520
2521         mono_counters_register ("Gray stack drain loops", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_drain_loops);
2522         mono_counters_register ("Gray stack prefetch fills", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_drain_prefetch_fills);
2523         mono_counters_register ("Gray stack prefetch failures", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_drain_prefetch_fill_failures);
2524 #endif
2525 #endif
2526
2527 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
2528         mono_mutex_init (&scanned_objects_list_lock);
2529 #endif
2530
2531         SGEN_ASSERT (0, SGEN_MAX_SMALL_OBJ_SIZE <= MS_BLOCK_FREE / 2, "MAX_SMALL_OBJ_SIZE must be at most MS_BLOCK_FREE / 2");
2532
2533         /*cardtable requires major pages to be 8 cards aligned*/
2534         g_assert ((MS_BLOCK_SIZE % (8 * CARD_SIZE_IN_BYTES)) == 0);
2535 }
2536
2537 void
2538 sgen_marksweep_init (SgenMajorCollector *collector)
2539 {
2540         sgen_marksweep_init_internal (collector, FALSE);
2541 }
2542
2543 void
2544 sgen_marksweep_conc_init (SgenMajorCollector *collector)
2545 {
2546         sgen_marksweep_init_internal (collector, TRUE);
2547 }
2548
2549 #endif