Merge branch 'master' of github.com:mono/mono
[mono.git] / mono / metadata / sgen-marksweep.c
1 /*
2  * sgen-marksweep.c: Simple generational GC.
3  *
4  * Author:
5  *      Mark Probst <mark.probst@gmail.com>
6  *
7  * Copyright 2009-2010 Novell, Inc.
8  * 
9  * Permission is hereby granted, free of charge, to any person obtaining
10  * a copy of this software and associated documentation files (the
11  * "Software"), to deal in the Software without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  * 
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  * 
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28
29 #ifdef HAVE_SGEN_GC
30
31 #include <math.h>
32
33 #include "utils/mono-counters.h"
34 #include "metadata/object-internals.h"
35 #include "metadata/profiler-private.h"
36
37 #include "metadata/sgen-gc.h"
38 #include "metadata/sgen-protocol.h"
39 #include "metadata/sgen-cardtable.h"
40
41
42 #define DEBUG(l,x)
43
44 #define MS_BLOCK_SIZE   (16*1024)
45 #define MS_BLOCK_SIZE_SHIFT     14
46 #define MAJOR_SECTION_SIZE      MS_BLOCK_SIZE
47 #define CARDS_PER_BLOCK (MS_BLOCK_SIZE / CARD_SIZE_IN_BYTES)
48
49 #ifdef FIXED_HEAP
50 #define MS_DEFAULT_HEAP_NUM_BLOCKS      (32 * 1024) /* 512 MB */
51 #endif
52
53 /*
54  * Don't allocate single blocks, but alloc a contingent of this many
55  * blocks in one swoop.
56  */
57 #define MS_BLOCK_ALLOC_NUM      32
58
59 /*
60  * Number of bytes before the first object in a block.  At the start
61  * of a block is the MSBlockHeader, then opional padding, then come
62  * the objects, so this must be >= sizeof (MSBlockHeader).
63  */
64 #ifdef FIXED_HEAP
65 #define MS_BLOCK_SKIP   0
66 #else
67 #define MS_BLOCK_SKIP   16
68 #endif
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 #if SGEN_MAX_SMALL_OBJ_SIZE > MS_BLOCK_FREE / 2
75 #error MAX_SMALL_OBJ_SIZE must be at most MS_BLOCK_FREE / 2
76 #endif
77
78 typedef struct _MSBlockInfo MSBlockInfo;
79 struct _MSBlockInfo {
80         int obj_size;
81         gboolean pinned;
82         gboolean has_references;
83 #ifdef FIXED_HEAP
84         gboolean used;
85 #else
86         MSBlockInfo *next;
87 #endif
88         char *block;
89         void **free_list;
90         MSBlockInfo *next_free;
91         void **pin_queue_start;
92         int pin_queue_num_entries;
93         mword mark_words [MS_NUM_MARK_WORDS];
94 };
95
96 #ifdef FIXED_HEAP
97 static int ms_heap_num_blocks = MS_DEFAULT_HEAP_NUM_BLOCKS;
98
99 #define ms_heap_start   nursery_end
100 static char *ms_heap_end;
101
102 #define MS_PTR_IN_SMALL_MAJOR_HEAP(p)   ((char*)(p) >= ms_heap_start && (char*)(p) < ms_heap_end)
103
104 /* array of all all block infos in the system */
105 static MSBlockInfo *block_infos;
106 #endif
107
108 #define MS_BLOCK_OBJ(b,i)               ((b)->block + MS_BLOCK_SKIP + (b)->obj_size * (i))
109 #define MS_BLOCK_DATA_FOR_OBJ(o)        ((char*)((mword)(o) & ~(mword)(MS_BLOCK_SIZE - 1)))
110
111 #ifdef FIXED_HEAP
112 #define MS_BLOCK_FOR_OBJ(o)             (&block_infos [(mword)((char*)(o) - ms_heap_start) >> MS_BLOCK_SIZE_SHIFT])
113 #else
114 typedef struct {
115         MSBlockInfo *info;
116 } MSBlockHeader;
117
118 #define MS_BLOCK_FOR_OBJ(o)             (((MSBlockHeader*)MS_BLOCK_DATA_FOR_OBJ ((o)))->info)
119 #endif
120
121 #define MS_BLOCK_OBJ_INDEX(o,b) (((char*)(o) - ((b)->block + MS_BLOCK_SKIP)) / (b)->obj_size)
122
123 #define MS_CALC_MARK_BIT(w,b,o)         do {                            \
124                 int i = ((char*)(o) - MS_BLOCK_DATA_FOR_OBJ ((o))) >> SGEN_ALLOC_ALIGN_BITS; \
125                 if (sizeof (mword) == 4) {                              \
126                         (w) = i >> 5;                                   \
127                         (b) = i & 31;                                   \
128                 } else {                                                \
129                         (w) = i >> 6;                                   \
130                         (b) = i & 63;                                   \
131                 }                                                       \
132         } while (0)
133
134 #define MS_MARK_BIT(bl,w,b)     ((bl)->mark_words [(w)] & (1L << (b)))
135 #define MS_SET_MARK_BIT(bl,w,b) ((bl)->mark_words [(w)] |= (1L << (b)))
136 #define MS_PAR_SET_MARK_BIT(was_marked,bl,w,b)  do {                    \
137                 mword __old = (bl)->mark_words [(w)];                   \
138                 mword __bitmask = 1L << (b);                            \
139                 if (__old & __bitmask) {                                \
140                         was_marked = TRUE;                              \
141                         break;                                          \
142                 }                                                       \
143                 if (SGEN_CAS_PTR ((gpointer*)&(bl)->mark_words [(w)],   \
144                                                 (gpointer)(__old | __bitmask), \
145                                                 (gpointer)__old) ==     \
146                                 (gpointer)__old) {                      \
147                         was_marked = FALSE;                             \
148                         break;                                          \
149                 }                                                       \
150         } while (1)
151
152 #define MS_OBJ_ALLOCED(o,b)     (*(void**)(o) && (*(char**)(o) < (b)->block || *(char**)(o) >= (b)->block + MS_BLOCK_SIZE))
153
154 #define MS_BLOCK_OBJ_SIZE_FACTOR        (sqrt (2.0))
155
156 /*
157  * This way we can lookup block object size indexes for sizes up to
158  * 256 bytes with a single load.
159  */
160 #define MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES      32
161
162 static int *block_obj_sizes;
163 static int num_block_obj_sizes;
164 static int fast_block_obj_size_indexes [MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES];
165
166 #define MS_BLOCK_FLAG_PINNED    1
167 #define MS_BLOCK_FLAG_REFS      2
168
169 #define MS_BLOCK_TYPE_MAX       4
170
171 #ifdef SGEN_PARALLEL_MARK
172 static LOCK_DECLARE (ms_block_list_mutex);
173 #define LOCK_MS_BLOCK_LIST pthread_mutex_lock (&ms_block_list_mutex)
174 #define UNLOCK_MS_BLOCK_LIST pthread_mutex_unlock (&ms_block_list_mutex)
175 #else
176 #define LOCK_MS_BLOCK_LIST
177 #define UNLOCK_MS_BLOCK_LIST
178 #endif
179
180 /* we get this at init */
181 static int nursery_bits;
182 static char *nursery_start;
183 static char *nursery_end;
184
185 #define ptr_in_nursery(p)       (SGEN_PTR_IN_NURSERY ((p), nursery_bits, nursery_start, nursery_end))
186
187 #ifdef FIXED_HEAP
188 /* non-allocated block free-list */
189 static MSBlockInfo *empty_blocks = NULL;
190 #else
191 /* non-allocated block free-list */
192 static void *empty_blocks = NULL;
193 /* all allocated blocks in the system */
194 static MSBlockInfo *all_blocks;
195 static int num_empty_blocks = 0;
196 #endif
197
198 #ifdef FIXED_HEAP
199 #define FOREACH_BLOCK(bl)       {                                       \
200                 int __block_i;                                          \
201                 for (__block_i = 0; __block_i < ms_heap_num_blocks; ++__block_i) { \
202                         (bl) = &block_infos [__block_i];                \
203                         if (!(bl)->used) continue;
204 #define END_FOREACH_BLOCK       }}
205 #else
206 #define FOREACH_BLOCK(bl)       for ((bl) = all_blocks; (bl); (bl) = (bl)->next) {
207 #define END_FOREACH_BLOCK       }
208 #endif
209
210 static int num_major_sections = 0;
211 /* one free block list for each block object size */
212 static MSBlockInfo **free_block_lists [MS_BLOCK_TYPE_MAX];
213
214 static long long stat_major_blocks_alloced = 0;
215 static long long stat_major_blocks_freed = 0;
216
217 static int
218 ms_find_block_obj_size_index (int size)
219 {
220         int i;
221         DEBUG (9, g_assert (size <= SGEN_MAX_SMALL_OBJ_SIZE));
222         for (i = 0; i < num_block_obj_sizes; ++i)
223                 if (block_obj_sizes [i] >= size)
224                         return i;
225         g_assert_not_reached ();
226 }
227
228 #define FREE_BLOCKS(p,r) (free_block_lists [((p) ? MS_BLOCK_FLAG_PINNED : 0) | ((r) ? MS_BLOCK_FLAG_REFS : 0)])
229
230 #define MS_BLOCK_OBJ_SIZE_INDEX(s)                              \
231         (((s)+7)>>3 < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES ?      \
232          fast_block_obj_size_indexes [((s)+7)>>3] :             \
233          ms_find_block_obj_size_index ((s)))
234
235 #ifdef FIXED_HEAP
236 static void*
237 major_alloc_heap (mword nursery_size, mword nursery_align, int the_nursery_bits)
238 {
239         char *heap_start;
240         mword major_heap_size = ms_heap_num_blocks * MS_BLOCK_SIZE;
241         mword alloc_size = nursery_size + major_heap_size;
242         int i;
243
244         g_assert (ms_heap_num_blocks > 0);
245         g_assert (nursery_size % MS_BLOCK_SIZE == 0);
246         if (nursery_align)
247                 g_assert (nursery_align % MS_BLOCK_SIZE == 0);
248
249         nursery_start = mono_sgen_alloc_os_memory_aligned (alloc_size, nursery_align ? nursery_align : MS_BLOCK_SIZE, TRUE);
250         nursery_end = heap_start = nursery_start + nursery_size;
251         nursery_bits = the_nursery_bits;
252
253         ms_heap_end = heap_start + major_heap_size;
254
255         block_infos = mono_sgen_alloc_internal_dynamic (sizeof (MSBlockInfo) * ms_heap_num_blocks, INTERNAL_MEM_MS_BLOCK_INFO);
256
257         for (i = 0; i < ms_heap_num_blocks; ++i) {
258                 block_infos [i].block = heap_start + i * MS_BLOCK_SIZE;
259                 if (i < ms_heap_num_blocks - 1)
260                         block_infos [i].next_free = &block_infos [i + 1];
261                 else
262                         block_infos [i].next_free = NULL;
263         }
264
265         empty_blocks = &block_infos [0];
266
267         return nursery_start;
268 }
269 #else
270 static void*
271 major_alloc_heap (mword nursery_size, mword nursery_align, int the_nursery_bits)
272 {
273         if (nursery_align)
274                 nursery_start = mono_sgen_alloc_os_memory_aligned (nursery_size, nursery_align, TRUE);
275         else
276                 nursery_start = mono_sgen_alloc_os_memory (nursery_size, TRUE);
277
278         nursery_end = nursery_start + nursery_size;
279         nursery_bits = the_nursery_bits;
280
281         return nursery_start;
282 }
283 #endif
284
285 #ifdef FIXED_HEAP
286 static MSBlockInfo*
287 ms_get_empty_block (void)
288 {
289         MSBlockInfo *block;
290
291         g_assert (empty_blocks);
292
293         block = empty_blocks;
294         empty_blocks = empty_blocks->next_free;
295
296         block->used = TRUE;
297
298         mono_sgen_update_heap_boundaries ((mword)block->block, (mword)block->block + MS_BLOCK_SIZE);
299
300         return block;
301 }
302
303 static void
304 ms_free_block (MSBlockInfo *block)
305 {
306         block->next_free = empty_blocks;
307         empty_blocks = block;
308         block->used = FALSE;
309 }
310 #else
311 static void*
312 ms_get_empty_block (void)
313 {
314         char *p;
315         int i;
316         void *block, *empty, *next;
317
318  retry:
319         if (!empty_blocks) {
320                 p = mono_sgen_alloc_os_memory_aligned (MS_BLOCK_SIZE * MS_BLOCK_ALLOC_NUM, MS_BLOCK_SIZE, TRUE);
321
322                 for (i = 0; i < MS_BLOCK_ALLOC_NUM; ++i) {
323                         block = p;
324                         /*
325                          * We do the free list update one after the
326                          * other so that other threads can use the new
327                          * blocks as quickly as possible.
328                          */
329                         do {
330                                 empty = empty_blocks;
331                                 *(void**)block = empty;
332                         } while (SGEN_CAS_PTR (&empty_blocks, block, empty) != empty);
333                         p += MS_BLOCK_SIZE;
334                 }
335
336                 SGEN_ATOMIC_ADD (num_empty_blocks, MS_BLOCK_ALLOC_NUM);
337
338                 stat_major_blocks_alloced += MS_BLOCK_ALLOC_NUM;
339         }
340
341         do {
342                 empty = empty_blocks;
343                 if (!empty)
344                         goto retry;
345                 block = empty;
346                 next = *(void**)block;
347         } while (SGEN_CAS_PTR (&empty_blocks, next, empty) != empty);
348
349         SGEN_ATOMIC_ADD (num_empty_blocks, -1);
350
351         *(void**)block = NULL;
352
353         g_assert (!((mword)block & (MS_BLOCK_SIZE - 1)));
354
355         mono_sgen_update_heap_boundaries ((mword)block, (mword)block + MS_BLOCK_SIZE);
356
357         return block;
358 }
359
360 static void
361 ms_free_block (void *block)
362 {
363         void *empty;
364
365         memset (block, 0, MS_BLOCK_SIZE);
366
367         do {
368                 empty = empty_blocks;
369                 *(void**)block = empty;
370         } while (SGEN_CAS_PTR (&empty_blocks, block, empty) != empty);
371
372         SGEN_ATOMIC_ADD (num_empty_blocks, 1);
373 }
374 #endif
375
376 //#define MARKSWEEP_CONSISTENCY_CHECK
377
378 #ifdef MARKSWEEP_CONSISTENCY_CHECK
379 static void
380 check_block_free_list (MSBlockInfo *block, int size, gboolean pinned)
381 {
382         MSBlockInfo *b;
383
384         for (; block; block = block->next_free) {
385                 g_assert (block->obj_size == size);
386                 g_assert ((pinned && block->pinned) || (!pinned && !block->pinned));
387
388                 /* blocks in the free lists must have at least
389                    one free slot */
390                 g_assert (block->free_list);
391
392 #ifdef FIXED_HEAP
393                 /* the block must not be in the empty_blocks list */
394                 for (b = empty_blocks; b; b = b->next_free)
395                         g_assert (b != block);
396 #else
397                 /* the block must be in the all_blocks list */
398                 for (b = all_blocks; b; b = b->next) {
399                         if (b == block)
400                                 break;
401                 }
402                 g_assert (b == block);
403 #endif
404         }
405 }
406
407 static void
408 check_empty_blocks (void)
409 {
410 #ifndef FIXED_HEAP
411         void *p;
412         int i = 0;
413         for (p = empty_blocks; p; p = *(void**)p)
414                 ++i;
415         g_assert (i == num_empty_blocks);
416 #endif
417 }
418
419 static void
420 consistency_check (void)
421 {
422         MSBlockInfo *block;
423         int i;
424
425         /* check all blocks */
426         FOREACH_BLOCK (block) {
427                 int count = MS_BLOCK_FREE / block->obj_size;
428                 int num_free = 0;
429                 void **free;
430
431 #ifndef FIXED_HEAP
432                 /* check block header */
433                 g_assert (((MSBlockHeader*)block->block)->info == block);
434 #endif
435
436                 /* count number of free slots */
437                 for (i = 0; i < count; ++i) {
438                         void **obj = (void**) MS_BLOCK_OBJ (block, i);
439                         if (!MS_OBJ_ALLOCED (obj, block))
440                                 ++num_free;
441                 }
442
443                 /* check free list */
444                 for (free = block->free_list; free; free = (void**)*free) {
445                         g_assert (MS_BLOCK_FOR_OBJ (free) == block);
446                         --num_free;
447                 }
448                 g_assert (num_free == 0);
449
450                 /* check all mark words are zero */
451                 for (i = 0; i < MS_NUM_MARK_WORDS; ++i)
452                         g_assert (block->mark_words [i] == 0);
453         } END_FOREACH_BLOCK;
454
455         /* check free blocks */
456         for (i = 0; i < num_block_obj_sizes; ++i) {
457                 int j;
458                 for (j = 0; j < MS_BLOCK_TYPE_MAX; ++j)
459                         check_block_free_list (free_block_lists [j][i], block_obj_sizes [i], j & MS_BLOCK_FLAG_PINNED);
460         }
461
462         check_empty_blocks ();
463 }
464 #endif
465
466 static void
467 ms_alloc_block (int size_index, gboolean pinned, gboolean has_references)
468 {
469         int size = block_obj_sizes [size_index];
470         int count = MS_BLOCK_FREE / size;
471 #ifdef FIXED_HEAP
472         MSBlockInfo *info = ms_get_empty_block ();
473 #else
474         MSBlockInfo *info = mono_sgen_alloc_internal (INTERNAL_MEM_MS_BLOCK_INFO);
475         MSBlockHeader *header;
476 #endif
477         MSBlockInfo **free_blocks = FREE_BLOCKS (pinned, has_references);
478         char *obj_start;
479         int i;
480
481         DEBUG (9, g_assert (count >= 2));
482
483         info->obj_size = size;
484         info->pinned = pinned;
485         info->has_references = has_references;
486 #ifndef FIXED_HEAP
487         info->block = ms_get_empty_block ();
488
489         header = (MSBlockHeader*) info->block;
490         header->info = info;
491 #endif
492
493         /* build free list */
494         obj_start = info->block + MS_BLOCK_SKIP;
495         info->free_list = (void**)obj_start;
496         /* we're skipping the last one - it must be nulled */
497         for (i = 0; i < count - 1; ++i) {
498                 char *next_obj_start = obj_start + size;
499                 *(void**)obj_start = next_obj_start;
500                 obj_start = next_obj_start;
501         }
502         /* the last one */
503         *(void**)obj_start = NULL;
504
505         info->next_free = free_blocks [size_index];
506         free_blocks [size_index] = info;
507
508 #ifndef FIXED_HEAP
509         info->next = all_blocks;
510         all_blocks = info;
511 #endif
512
513         ++num_major_sections;
514 }
515
516 static gboolean
517 obj_is_from_pinned_alloc (char *obj)
518 {
519         MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
520         return block->pinned;
521 }
522
523 static void*
524 alloc_obj (int size, gboolean pinned, gboolean has_references)
525 {
526         int size_index = MS_BLOCK_OBJ_SIZE_INDEX (size);
527         MSBlockInfo **free_blocks = FREE_BLOCKS (pinned, has_references);
528         MSBlockInfo *block;
529         void *obj;
530
531         /* FIXME: try to do this without locking */
532
533         LOCK_MS_BLOCK_LIST;
534
535         if (!free_blocks [size_index])
536                 ms_alloc_block (size_index, pinned, has_references);
537
538         block = free_blocks [size_index];
539         DEBUG (9, g_assert (block));
540
541         obj = block->free_list;
542         DEBUG (9, g_assert (obj));
543
544         block->free_list = *(void**)obj;
545         if (!block->free_list) {
546                 free_blocks [size_index] = block->next_free;
547                 block->next_free = NULL;
548         }
549
550         UNLOCK_MS_BLOCK_LIST;
551
552         /*
553          * FIXME: This should not be necessary because it'll be
554          * overwritten by the vtable immediately.
555          */
556         *(void**)obj = NULL;
557
558         return obj;
559 }
560
561 static void*
562 major_alloc_object (int size, gboolean has_references)
563 {
564         return alloc_obj (size, FALSE, has_references);
565 }
566
567 /*
568  * We're not freeing the block if it's empty.  We leave that work for
569  * the next major collection.
570  *
571  * This is just called from the domain clearing code, which runs in a
572  * single thread and has the GC lock, so we don't need an extra lock.
573  */
574 static void
575 free_object (char *obj, size_t size, gboolean pinned)
576 {
577         MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
578         int word, bit;
579         DEBUG (9, g_assert ((pinned && block->pinned) || (!pinned && !block->pinned)));
580         DEBUG (9, g_assert (MS_OBJ_ALLOCED (obj, block)));
581         MS_CALC_MARK_BIT (word, bit, obj);
582         DEBUG (9, g_assert (!MS_MARK_BIT (block, word, bit)));
583         if (!block->free_list) {
584                 MSBlockInfo **free_blocks = FREE_BLOCKS (pinned, block->has_references);
585                 int size_index = MS_BLOCK_OBJ_SIZE_INDEX (size);
586                 DEBUG (9, g_assert (!block->next_free));
587                 block->next_free = free_blocks [size_index];
588                 free_blocks [size_index] = block;
589         }
590         memset (obj, 0, size);
591         *(void**)obj = block->free_list;
592         block->free_list = (void**)obj;
593 }
594
595 static void
596 major_free_non_pinned_object (char *obj, size_t size)
597 {
598         free_object (obj, size, FALSE);
599 }
600
601 /* size is a multiple of SGEN_ALLOC_ALIGN */
602 static void*
603 major_alloc_small_pinned_obj (size_t size, gboolean has_references)
604 {
605         return alloc_obj (size, TRUE, has_references);
606 }
607
608 static void
609 free_pinned_object (char *obj, size_t size)
610 {
611         free_object (obj, size, TRUE);
612 }
613
614 /*
615  * size is already rounded up and we hold the GC lock.
616  */
617 static void*
618 major_alloc_degraded (MonoVTable *vtable, size_t size)
619 {
620         void *obj;
621         int old_num_sections = num_major_sections;
622         obj = alloc_obj (size, FALSE, vtable->klass->has_references);
623         *(MonoVTable**)obj = vtable;
624         HEAVY_STAT (++stat_objects_alloced_degraded);
625         HEAVY_STAT (stat_bytes_alloced_degraded += size);
626         g_assert (num_major_sections >= old_num_sections);
627         mono_sgen_register_major_sections_alloced (num_major_sections - old_num_sections);
628         return obj;
629 }
630
631 #define MAJOR_OBJ_IS_IN_TO_SPACE(obj)   FALSE
632
633 /*
634  * obj is some object.  If it's not in the major heap (i.e. if it's in
635  * the nursery or LOS), return FALSE.  Otherwise return whether it's
636  * been marked or copied.
637  */
638 static gboolean
639 major_is_object_live (char *obj)
640 {
641         MSBlockInfo *block;
642         int word, bit;
643 #ifndef FIXED_HEAP
644         mword objsize;
645 #endif
646
647         if (ptr_in_nursery (obj))
648                 return FALSE;
649
650 #ifdef FIXED_HEAP
651         /* LOS */
652         if (!MS_PTR_IN_SMALL_MAJOR_HEAP (obj))
653                 return FALSE;
654 #else
655         objsize = SGEN_ALIGN_UP (mono_sgen_safe_object_get_size ((MonoObject*)obj));
656
657         /* LOS */
658         if (objsize > SGEN_MAX_SMALL_OBJ_SIZE)
659                 return FALSE;
660 #endif
661
662         /* now we know it's in a major block */
663         block = MS_BLOCK_FOR_OBJ (obj);
664         DEBUG (9, g_assert (!block->pinned));
665         MS_CALC_MARK_BIT (word, bit, obj);
666         return MS_MARK_BIT (block, word, bit) ? TRUE : FALSE;
667 }
668
669 static gboolean
670 major_ptr_is_in_non_pinned_space (char *ptr)
671 {
672         g_assert_not_reached ();
673 }
674
675 static void
676 major_iterate_objects (gboolean non_pinned, gboolean pinned, IterateObjectCallbackFunc callback, void *data)
677 {
678         MSBlockInfo *block;
679
680         FOREACH_BLOCK (block) {
681                 int count = MS_BLOCK_FREE / block->obj_size;
682                 int i;
683
684                 if (block->pinned && !pinned)
685                         continue;
686                 if (!block->pinned && !non_pinned)
687                         continue;
688
689                 for (i = 0; i < count; ++i) {
690                         void **obj = (void**) MS_BLOCK_OBJ (block, i);
691                         if (MS_OBJ_ALLOCED (obj, block))
692                                 callback ((char*)obj, block->obj_size, data);
693                 }
694         } END_FOREACH_BLOCK;
695 }
696
697 static void
698 major_check_scan_starts (void)
699 {
700 }
701
702 static void
703 major_dump_heap (FILE *heap_dump_file)
704 {
705         MSBlockInfo *block;
706
707         FOREACH_BLOCK (block) {
708                 int count = MS_BLOCK_FREE / block->obj_size;
709                 int i;
710                 int start = -1;
711
712                 fprintf (heap_dump_file, "<section type=\"%s\" size=\"%zu\">\n", "old", (size_t)MS_BLOCK_FREE);
713
714                 for (i = 0; i <= count; ++i) {
715                         if ((i < count) && MS_OBJ_ALLOCED (MS_BLOCK_OBJ (block, i), block)) {
716                                 if (start < 0)
717                                         start = i;
718                         } else {
719                                 if (start >= 0) {
720                                         mono_sgen_dump_occupied (MS_BLOCK_OBJ (block, start), MS_BLOCK_OBJ (block, i), block->block);
721                                         start = -1;
722                                 }
723                         }
724                 }
725
726                 fprintf (heap_dump_file, "</section>\n");
727         } END_FOREACH_BLOCK;
728 }
729
730 #define LOAD_VTABLE     SGEN_LOAD_VTABLE
731
732 #define MS_MARK_OBJECT_AND_ENQUEUE_CHECKED(obj,block,queue) do {        \
733                 int __word, __bit;                                      \
734                 MS_CALC_MARK_BIT (__word, __bit, (obj));                \
735                 if (!MS_MARK_BIT ((block), __word, __bit) && MS_OBJ_ALLOCED ((obj), (block))) { \
736                         MS_SET_MARK_BIT ((block), __word, __bit);       \
737                         if ((block)->has_references)                    \
738                                 GRAY_OBJECT_ENQUEUE ((queue), (obj));   \
739                         binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), mono_sgen_safe_object_get_size ((MonoObject*)(obj))); \
740                 }                                                       \
741         } while (0)
742 #define MS_MARK_OBJECT_AND_ENQUEUE(obj,block,queue) do {                \
743                 int __word, __bit;                                      \
744                 MS_CALC_MARK_BIT (__word, __bit, (obj));                \
745                 DEBUG (9, g_assert (MS_OBJ_ALLOCED ((obj), (block))));  \
746                 if (!MS_MARK_BIT ((block), __word, __bit)) {            \
747                         MS_SET_MARK_BIT ((block), __word, __bit);       \
748                         if ((block)->has_references)                    \
749                                 GRAY_OBJECT_ENQUEUE ((queue), (obj));   \
750                         binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), mono_sgen_safe_object_get_size ((MonoObject*)(obj))); \
751                 }                                                       \
752         } while (0)
753 #define MS_PAR_MARK_OBJECT_AND_ENQUEUE(obj,block,queue) do {            \
754                 int __word, __bit;                                      \
755                 gboolean __was_marked;                                  \
756                 DEBUG (9, g_assert (MS_OBJ_ALLOCED ((obj), (block))));  \
757                 MS_CALC_MARK_BIT (__word, __bit, (obj));                \
758                 MS_PAR_SET_MARK_BIT (__was_marked, (block), __word, __bit); \
759                 if (!__was_marked) {                                    \
760                         if ((block)->has_references)                    \
761                                 GRAY_OBJECT_ENQUEUE ((queue), (obj));   \
762                         binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), mono_sgen_safe_object_get_size ((MonoObject*)(obj))); \
763                 }                                                       \
764         } while (0)
765
766 #include "sgen-major-copy-object.h"
767
768 #ifdef SGEN_PARALLEL_MARK
769 static void
770 major_copy_or_mark_object (void **ptr, SgenGrayQueue *queue)
771 {
772         void *obj = *ptr;
773         mword vtable_word = *(mword*)obj;
774         MonoVTable *vt = (MonoVTable*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
775         mword objsize;
776         MSBlockInfo *block;
777
778         HEAVY_STAT (++stat_copy_object_called_major);
779
780         DEBUG (9, g_assert (obj));
781         DEBUG (9, g_assert (current_collection_generation == GENERATION_OLD));
782
783         if (ptr_in_nursery (obj)) {
784                 int word, bit;
785                 gboolean has_references;
786                 void *destination;
787
788                 if (vtable_word & SGEN_FORWARDED_BIT) {
789                         *ptr = (void*)vt;
790                         return;
791                 }
792
793                 if (vtable_word & SGEN_PINNED_BIT)
794                         return;
795
796                 HEAVY_STAT (++stat_objects_copied_major);
797
798                 objsize = SGEN_ALIGN_UP (mono_sgen_par_object_get_size (vt, (MonoObject*)obj));
799                 has_references = SGEN_VTABLE_HAS_REFERENCES (vt);
800
801                 destination = major_alloc_object (objsize, has_references);
802
803                 if (SGEN_CAS_PTR (obj, (void*)((mword)destination | SGEN_FORWARDED_BIT), vt) == vt) {
804                         gboolean was_marked;
805
806                         par_copy_object_no_checks (destination, vt, obj, objsize, has_references ? queue : NULL);
807                         obj = destination;
808                         *ptr = obj;
809
810                         /*
811                          * FIXME: If we make major_alloc_object() give
812                          * us the block info, too, we won't have to
813                          * re-fetch it here.
814                          */
815                         block = MS_BLOCK_FOR_OBJ (obj);
816                         MS_CALC_MARK_BIT (word, bit, obj);
817                         DEBUG (9, g_assert (!MS_MARK_BIT (block, word, bit)));
818                         MS_PAR_SET_MARK_BIT (was_marked, block, word, bit);
819                 } else {
820                         /*
821                          * FIXME: We have allocated destination, but
822                          * we cannot use it.  Give it back to the
823                          * allocator.
824                          */
825                         *(void**)destination = NULL;
826
827                         vtable_word = *(mword*)obj;
828                         g_assert (vtable_word & SGEN_FORWARDED_BIT);
829
830                         obj = (void*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
831
832                         *ptr = obj;
833                 }
834         } else {
835 #ifdef FIXED_HEAP
836                 if (MS_PTR_IN_SMALL_MAJOR_HEAP (obj))
837 #else
838                 objsize = SGEN_ALIGN_UP (mono_sgen_par_object_get_size (vt, (MonoObject*)obj));
839
840                 if (objsize <= SGEN_MAX_SMALL_OBJ_SIZE)
841 #endif
842                 {
843                         block = MS_BLOCK_FOR_OBJ (obj);
844                         MS_PAR_MARK_OBJECT_AND_ENQUEUE (obj, block, queue);
845                 } else {
846                         if (vtable_word & SGEN_PINNED_BIT)
847                                 return;
848                         binary_protocol_pin (obj, vt, mono_sgen_safe_object_get_size ((MonoObject*)obj));
849                         if (SGEN_CAS_PTR (obj, (void*)(vtable_word | SGEN_PINNED_BIT), (void*)vtable_word) == (void*)vtable_word) {
850                                 if (SGEN_VTABLE_HAS_REFERENCES (vt))
851                                         GRAY_OBJECT_ENQUEUE (queue, obj);
852                         } else {
853                                 g_assert (SGEN_OBJECT_IS_PINNED (obj));
854                         }
855                 }
856         }
857 }
858 #else
859 static void
860 major_copy_or_mark_object (void **ptr, SgenGrayQueue *queue)
861 {
862         void *obj = *ptr;
863         mword objsize;
864         MSBlockInfo *block;
865
866         HEAVY_STAT (++stat_copy_object_called_major);
867
868         DEBUG (9, g_assert (obj));
869         DEBUG (9, g_assert (current_collection_generation == GENERATION_OLD));
870
871         if (ptr_in_nursery (obj)) {
872                 int word, bit;
873                 char *forwarded;
874
875                 if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj))) {
876                         *ptr = forwarded;
877                         return;
878                 }
879                 if (SGEN_OBJECT_IS_PINNED (obj))
880                         return;
881
882                 HEAVY_STAT (++stat_objects_copied_major);
883
884                 obj = copy_object_no_checks (obj, queue);
885                 *ptr = obj;
886
887                 /*
888                  * FIXME: See comment for copy_object_no_checks().  If
889                  * we have that, we can let the allocation function
890                  * give us the block info, too, and we won't have to
891                  * re-fetch it.
892                  */
893                 block = MS_BLOCK_FOR_OBJ (obj);
894                 MS_CALC_MARK_BIT (word, bit, obj);
895                 DEBUG (9, g_assert (!MS_MARK_BIT (block, word, bit)));
896                 MS_SET_MARK_BIT (block, word, bit);
897         } else {
898 #ifdef FIXED_HEAP
899                 if (MS_PTR_IN_SMALL_MAJOR_HEAP (obj))
900 #else
901                 objsize = SGEN_ALIGN_UP (mono_sgen_safe_object_get_size ((MonoObject*)obj));
902
903                 if (objsize <= SGEN_MAX_SMALL_OBJ_SIZE)
904 #endif
905                 {
906                         block = MS_BLOCK_FOR_OBJ (obj);
907                         MS_MARK_OBJECT_AND_ENQUEUE (obj, block, queue);
908                 } else {
909                         if (SGEN_OBJECT_IS_PINNED (obj))
910                                 return;
911                         binary_protocol_pin (obj, (gpointer)SGEN_LOAD_VTABLE (obj), mono_sgen_safe_object_get_size ((MonoObject*)obj));
912                         SGEN_PIN_OBJECT (obj);
913                         /* FIXME: only enqueue if object has references */
914                         GRAY_OBJECT_ENQUEUE (queue, obj);
915                 }
916         }
917 }
918 #endif
919
920 #include "sgen-major-scan-object.h"
921
922 static void
923 mark_pinned_objects_in_block (MSBlockInfo *block, SgenGrayQueue *queue)
924 {
925         int i;
926         int last_index = -1;
927         int count = MS_BLOCK_FREE / block->obj_size;
928
929         for (i = 0; i < block->pin_queue_num_entries; ++i) {
930                 int index = MS_BLOCK_OBJ_INDEX (block->pin_queue_start [i], block);
931                 DEBUG (9, g_assert (index >= 0 && index < count));
932                 if (index == last_index)
933                         continue;
934                 MS_MARK_OBJECT_AND_ENQUEUE_CHECKED (MS_BLOCK_OBJ (block, index), block, queue);
935                 last_index = index;
936         }
937 }
938
939 static void
940 major_sweep (void)
941 {
942         int i;
943 #ifdef FIXED_HEAP
944         int j;
945 #else
946         MSBlockInfo **iter;
947 #endif
948
949         /* clear all the free lists */
950         for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i) {
951                 MSBlockInfo **free_blocks = free_block_lists [i];
952                 int j;
953                 for (j = 0; j < num_block_obj_sizes; ++j)
954                         free_blocks [j] = NULL;
955         }
956
957         /* traverse all blocks, free and zero unmarked objects */
958 #ifdef FIXED_HEAP
959         for (j = 0; j < ms_heap_num_blocks; ++j) {
960                 MSBlockInfo *block = &block_infos [j];
961 #else
962         iter = &all_blocks;
963         while (*iter) {
964                 MSBlockInfo *block = *iter;
965 #endif
966                 int count;
967                 gboolean have_live = FALSE;
968                 int obj_index;
969
970 #ifdef FIXED_HEAP
971                 if (!block->used)
972                         continue;
973 #endif
974
975                 count = MS_BLOCK_FREE / block->obj_size;
976                 block->free_list = NULL;
977
978                 for (obj_index = 0; obj_index < count; ++obj_index) {
979                         int word, bit;
980                         void *obj = MS_BLOCK_OBJ (block, obj_index);
981
982                         MS_CALC_MARK_BIT (word, bit, obj);
983                         if (MS_MARK_BIT (block, word, bit)) {
984                                 DEBUG (9, g_assert (MS_OBJ_ALLOCED (obj, block)));
985                                 have_live = TRUE;
986                         } else {
987                                 /* an unmarked object */
988                                 if (MS_OBJ_ALLOCED (obj, block)) {
989                                         binary_protocol_empty (obj, block->obj_size);
990                                         memset (obj, 0, block->obj_size);
991                                 }
992                                 *(void**)obj = block->free_list;
993                                 block->free_list = obj;
994                         }
995                 }
996
997                 /* reset mark bits */
998                 memset (block->mark_words, 0, sizeof (mword) * MS_NUM_MARK_WORDS);
999
1000                 /*
1001                  * FIXME: reverse free list so that it's in address
1002                  * order
1003                  */
1004
1005                 if (have_live) {
1006 #ifndef FIXED_HEAP
1007                         iter = &block->next;
1008 #endif
1009
1010                         /*
1011                          * If there are free slots in the block, add
1012                          * the block to the corresponding free list.
1013                          */
1014                         if (block->free_list) {
1015                                 MSBlockInfo **free_blocks = FREE_BLOCKS (block->pinned, block->has_references);
1016                                 int index = MS_BLOCK_OBJ_SIZE_INDEX (block->obj_size);
1017                                 block->next_free = free_blocks [index];
1018                                 free_blocks [index] = block;
1019                         }
1020                 } else {
1021                         /*
1022                          * Blocks without live objects are removed from the
1023                          * block list and freed.
1024                          */
1025 #ifdef FIXED_HEAP
1026                         ms_free_block (block);
1027 #else
1028                         *iter = block->next;
1029
1030                         ms_free_block (block->block);
1031                         mono_sgen_free_internal (block, INTERNAL_MEM_MS_BLOCK_INFO);
1032 #endif
1033
1034                         --num_major_sections;
1035                 }
1036         }
1037 }
1038
1039 static int count_pinned_ref;
1040 static int count_pinned_nonref;
1041 static int count_nonpinned_ref;
1042 static int count_nonpinned_nonref;
1043
1044 static void
1045 count_nonpinned_callback (char *obj, size_t size, void *data)
1046 {
1047         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
1048
1049         if (vtable->klass->has_references)
1050                 ++count_nonpinned_ref;
1051         else
1052                 ++count_nonpinned_nonref;
1053 }
1054
1055 static void
1056 count_pinned_callback (char *obj, size_t size, void *data)
1057 {
1058         MonoVTable *vtable = (MonoVTable*)LOAD_VTABLE (obj);
1059
1060         if (vtable->klass->has_references)
1061                 ++count_pinned_ref;
1062         else
1063                 ++count_pinned_nonref;
1064 }
1065
1066 static void __attribute__ ((unused))
1067 count_ref_nonref_objs (void)
1068 {
1069         int total;
1070
1071         count_pinned_ref = 0;
1072         count_pinned_nonref = 0;
1073         count_nonpinned_ref = 0;
1074         count_nonpinned_nonref = 0;
1075
1076         major_iterate_objects (TRUE, FALSE, count_nonpinned_callback, NULL);
1077         major_iterate_objects (FALSE, TRUE, count_pinned_callback, NULL);
1078
1079         total = count_pinned_nonref + count_nonpinned_nonref + count_pinned_ref + count_nonpinned_ref;
1080
1081         g_print ("ref: %d pinned %d non-pinned   non-ref: %d pinned %d non-pinned  --  %.1f\n",
1082                         count_pinned_ref, count_nonpinned_ref,
1083                         count_pinned_nonref, count_nonpinned_nonref,
1084                         (count_pinned_nonref + count_nonpinned_nonref) * 100.0 / total);
1085 }
1086
1087 static int
1088 ms_calculate_block_obj_sizes (double factor, int *arr)
1089 {
1090         double target_size = sizeof (MonoObject);
1091         int num_sizes = 0;
1092         int last_size = 0;
1093
1094         do {
1095                 int target_count = ceil (MS_BLOCK_FREE / target_size);
1096                 int size = MIN ((MS_BLOCK_FREE / target_count) & ~(SGEN_ALLOC_ALIGN - 1), SGEN_MAX_SMALL_OBJ_SIZE);
1097
1098                 if (size != last_size) {
1099                         if (arr)
1100                                 arr [num_sizes] = size;
1101                         ++num_sizes;
1102                         last_size = size;
1103                 }
1104
1105                 target_size *= factor;
1106         } while (last_size < SGEN_MAX_SMALL_OBJ_SIZE);
1107
1108         return num_sizes;
1109 }
1110
1111 /* only valid during minor collections */
1112 static int old_num_major_sections;
1113
1114 static void
1115 major_start_nursery_collection (void)
1116 {
1117 #ifdef MARKSWEEP_CONSISTENCY_CHECK
1118         consistency_check ();
1119 #endif
1120
1121         old_num_major_sections = num_major_sections;
1122 }
1123
1124 static void
1125 major_finish_nursery_collection (void)
1126 {
1127 #ifdef MARKSWEEP_CONSISTENCY_CHECK
1128         consistency_check ();
1129 #endif
1130         mono_sgen_register_major_sections_alloced (num_major_sections - old_num_major_sections);
1131 }
1132
1133 static void
1134 major_finish_major_collection (void)
1135 {
1136 #ifndef FIXED_HEAP
1137         int section_reserve = mono_sgen_get_minor_collection_allowance () / MS_BLOCK_SIZE;
1138
1139         /*
1140          * FIXME: We don't free blocks on 32 bit platforms because it
1141          * can lead to address space fragmentation, since we're
1142          * allocating blocks in larger contingents.
1143          */
1144         if (sizeof (mword) < 8)
1145                 return;
1146
1147         while (num_empty_blocks > section_reserve) {
1148                 void *next = *(void**)empty_blocks;
1149                 mono_sgen_free_os_memory (empty_blocks, MS_BLOCK_SIZE);
1150                 empty_blocks = next;
1151                 /*
1152                  * Needs not be atomic because this is running
1153                  * single-threaded.
1154                  */
1155                 --num_empty_blocks;
1156
1157                 ++stat_major_blocks_freed;
1158         }
1159 #endif
1160 }
1161
1162 static void
1163 major_find_pin_queue_start_ends (SgenGrayQueue *queue)
1164 {
1165         MSBlockInfo *block;
1166
1167         FOREACH_BLOCK (block) {
1168                 block->pin_queue_start = mono_sgen_find_optimized_pin_queue_area (block->block + MS_BLOCK_SKIP, block->block + MS_BLOCK_SIZE,
1169                                 &block->pin_queue_num_entries);
1170         } END_FOREACH_BLOCK;
1171 }
1172
1173 static void
1174 major_pin_objects (SgenGrayQueue *queue)
1175 {
1176         MSBlockInfo *block;
1177
1178         FOREACH_BLOCK (block) {
1179                 mark_pinned_objects_in_block (block, queue);
1180         } END_FOREACH_BLOCK;
1181 }
1182
1183 static void
1184 major_init_to_space (void)
1185 {
1186 }
1187
1188 static void
1189 major_report_pinned_memory_usage (void)
1190 {
1191         g_assert_not_reached ();
1192 }
1193
1194 static gint64
1195 major_get_used_size (void)
1196 {
1197         gint64 size = 0;
1198         MSBlockInfo *block;
1199
1200         FOREACH_BLOCK (block) {
1201                 int count = MS_BLOCK_FREE / block->obj_size;
1202                 void **iter;
1203                 size += count * block->obj_size;
1204                 for (iter = block->free_list; iter; iter = (void**)*iter)
1205                         size -= block->obj_size;
1206         } END_FOREACH_BLOCK;
1207
1208         return size;
1209 }
1210
1211 static int
1212 get_num_major_sections (void)
1213 {
1214         return num_major_sections;
1215 }
1216
1217 #ifdef FIXED_HEAP
1218 static gboolean
1219 major_handle_gc_param (const char *opt)
1220 {
1221         if (g_str_has_prefix (opt, "major-heap-size=")) {
1222                 const char *arg = strchr (opt, '=') + 1;
1223                 glong size;
1224                 if (!mono_sgen_parse_environment_string_extract_number (arg, &size))
1225                         return FALSE;
1226                 ms_heap_num_blocks = (size + MS_BLOCK_SIZE - 1) / MS_BLOCK_SIZE;
1227                 g_assert (ms_heap_num_blocks > 0);
1228                 return TRUE;
1229         }
1230
1231         return FALSE;
1232 }
1233
1234 static void
1235 major_print_gc_param_usage (void)
1236 {
1237         fprintf (stderr, "  major-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
1238 }
1239 #endif
1240
1241 #ifdef SGEN_HAVE_CARDTABLE
1242 static void
1243 major_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
1244 {
1245         MSBlockInfo *block;
1246
1247         FOREACH_BLOCK (block) {
1248                 if (block->has_references)
1249                         callback ((mword)block->block, MS_BLOCK_SIZE);
1250         } END_FOREACH_BLOCK;
1251 }
1252
1253 #define MS_BLOCK_OBJ_INDEX_FAST(o,b,os) (((char*)(o) - ((b) + MS_BLOCK_SKIP)) / (os))
1254 #define MS_BLOCK_OBJ_FAST(b,os,i)                       ((b) + MS_BLOCK_SKIP + (os) * (i))
1255 #define MS_OBJ_ALLOCED_FAST(o,b)                (*(void**)(o) && (*(char**)(o) < (b) || *(char**)(o) >= (b) + MS_BLOCK_SIZE))
1256
1257 static void
1258 major_scan_card_table (SgenGrayQueue *queue)
1259 {
1260         MSBlockInfo *block;
1261
1262         FOREACH_BLOCK (block) {
1263                 int i;
1264                 int block_obj_size;
1265                 char *start, *block_start;
1266
1267                 if (!block->has_references)
1268                         continue;
1269
1270                 block_obj_size = block->obj_size;
1271                 start = block_start = block->block;
1272
1273                 if (block_obj_size >= CARD_SIZE_IN_BYTES) {
1274                         guint8 cards [CARDS_PER_BLOCK];
1275                         char *obj = (char*)MS_BLOCK_OBJ_FAST (block_start, block_obj_size, 0);
1276                         char *end = start + MS_BLOCK_SIZE;
1277                         char *base = sgen_card_table_align_pointer (obj);
1278
1279                         sgen_card_table_get_card_data (cards, (mword)start, CARDS_PER_BLOCK);
1280
1281                         while (obj < end) {
1282                                 if (MS_OBJ_ALLOCED_FAST (obj, block_start)) {
1283                                         int card_offset = (obj - base) >> CARD_BITS;
1284                                         sgen_cardtable_scan_object (obj, block_obj_size, cards + card_offset, queue);
1285                                 }
1286                                 obj += block_obj_size;
1287                         }
1288                 } else {
1289                         for (i = 0; i < CARDS_PER_BLOCK; ++i, start += CARD_SIZE_IN_BYTES) {
1290                                 int index;
1291                                 char *obj, *end;
1292
1293                                 if (!sgen_card_table_card_begin_scanning ((mword)start))
1294                                         continue;
1295
1296                                 end = start + CARD_SIZE_IN_BYTES;
1297                                 if (i == 0)
1298                                         index = 0;
1299                                 else
1300                                         index = MS_BLOCK_OBJ_INDEX_FAST (start, block_start, block_obj_size);
1301
1302                                 obj = (char*)MS_BLOCK_OBJ_FAST (block_start, block_obj_size, index);
1303                                 while (obj < end) {
1304                                         if (MS_OBJ_ALLOCED_FAST (obj, block_start))
1305                                                 minor_scan_object (obj, queue);
1306                                         obj += block_obj_size;
1307                                 }
1308                         }
1309                 }
1310         } END_FOREACH_BLOCK;
1311 }
1312 #endif
1313
1314 void
1315 #ifdef SGEN_PARALLEL_MARK
1316 #ifdef FIXED_HEAP
1317 mono_sgen_marksweep_fixed_par_init
1318 #else
1319 mono_sgen_marksweep_par_init
1320 #endif
1321 #else
1322 #ifdef FIXED_HEAP
1323 mono_sgen_marksweep_fixed_init
1324 #else
1325 mono_sgen_marksweep_init
1326 #endif
1327 #endif
1328         (SgenMajorCollector *collector)
1329 {
1330         int i;
1331
1332 #ifndef FIXED_HEAP
1333         mono_sgen_register_fixed_internal_mem_type (INTERNAL_MEM_MS_BLOCK_INFO, sizeof (MSBlockInfo));
1334 #endif
1335
1336         num_block_obj_sizes = ms_calculate_block_obj_sizes (MS_BLOCK_OBJ_SIZE_FACTOR, NULL);
1337         block_obj_sizes = mono_sgen_alloc_internal_dynamic (sizeof (int) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES);
1338         ms_calculate_block_obj_sizes (MS_BLOCK_OBJ_SIZE_FACTOR, block_obj_sizes);
1339
1340         /*
1341         {
1342                 int i;
1343                 g_print ("block object sizes:\n");
1344                 for (i = 0; i < num_block_obj_sizes; ++i)
1345                         g_print ("%d\n", block_obj_sizes [i]);
1346         }
1347         */
1348
1349         for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i)
1350                 free_block_lists [i] = mono_sgen_alloc_internal_dynamic (sizeof (MSBlockInfo*) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES);
1351
1352         for (i = 0; i < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES; ++i)
1353                 fast_block_obj_size_indexes [i] = ms_find_block_obj_size_index (i * 8);
1354         for (i = 0; i < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES * 8; ++i)
1355                 g_assert (MS_BLOCK_OBJ_SIZE_INDEX (i) == ms_find_block_obj_size_index (i));
1356
1357         LOCK_INIT (ms_block_list_mutex);
1358
1359         mono_counters_register ("# major blocks allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_blocks_alloced);
1360         mono_counters_register ("# major blocks freed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_blocks_freed);
1361
1362         collector->section_size = MAJOR_SECTION_SIZE;
1363 #ifdef SGEN_PARALLEL_MARK
1364         collector->is_parallel = TRUE;
1365 #else
1366         collector->is_parallel = FALSE;
1367 #endif
1368         collector->supports_cardtable = !collector->is_parallel;
1369
1370         collector->alloc_heap = major_alloc_heap;
1371         collector->is_object_live = major_is_object_live;
1372         collector->alloc_small_pinned_obj = major_alloc_small_pinned_obj;
1373         collector->alloc_degraded = major_alloc_degraded;
1374         collector->copy_or_mark_object = major_copy_or_mark_object;
1375         collector->alloc_object = major_alloc_object;
1376         collector->free_pinned_object = free_pinned_object;
1377         collector->iterate_objects = major_iterate_objects;
1378         collector->free_non_pinned_object = major_free_non_pinned_object;
1379         collector->find_pin_queue_start_ends = major_find_pin_queue_start_ends;
1380         collector->pin_objects = major_pin_objects;
1381 #ifdef SGEN_HAVE_CARDTABLE
1382         collector->scan_card_table = major_scan_card_table;
1383         collector->iterate_live_block_ranges = (void*)(void*) major_iterate_live_block_ranges;
1384 #endif
1385         collector->init_to_space = major_init_to_space;
1386         collector->sweep = major_sweep;
1387         collector->check_scan_starts = major_check_scan_starts;
1388         collector->dump_heap = major_dump_heap;
1389         collector->get_used_size = major_get_used_size;
1390         collector->start_nursery_collection = major_start_nursery_collection;
1391         collector->finish_nursery_collection = major_finish_nursery_collection;
1392         collector->finish_major_collection = major_finish_major_collection;
1393         collector->ptr_is_in_non_pinned_space = major_ptr_is_in_non_pinned_space;
1394         collector->obj_is_from_pinned_alloc = obj_is_from_pinned_alloc;
1395         collector->report_pinned_memory_usage = major_report_pinned_memory_usage;
1396         collector->get_num_major_sections = get_num_major_sections;
1397 #ifdef FIXED_HEAP
1398         collector->handle_gc_param = major_handle_gc_param;
1399         collector->print_gc_param_usage = major_print_gc_param_usage;
1400 #else
1401         collector->handle_gc_param = NULL;
1402         collector->print_gc_param_usage = NULL;
1403 #endif
1404
1405         FILL_COLLECTOR_COPY_OBJECT (collector);
1406         FILL_COLLECTOR_SCAN_OBJECT (collector);
1407 }
1408
1409 #endif