[sgen] Don't check the value for nursery in the concurrent wbarrier.
[mono.git] / mono / metadata / sgen-cardtable.c
1 /*
2  * sgen-cardtable.c: Card table implementation for sgen
3  *
4  * Author:
5  *      Rodrigo Kumpera (rkumpera@novell.com)
6  *
7  * Copyright 2001-2003 Ximian, Inc
8  * Copyright 2003-2010 Novell, Inc.
9  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10  * Copyright (C) 2012 Xamarin Inc
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License 2.0 as published by the Free Software Foundation;
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public
22  * License 2.0 along with this library; if not, write to the Free
23  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25
26 #include "config.h"
27 #ifdef HAVE_SGEN_GC
28
29 #include "metadata/sgen-gc.h"
30 #include "metadata/sgen-cardtable.h"
31 #include "metadata/sgen-memory-governor.h"
32 #include "utils/mono-counters.h"
33 #include "utils/mono-time.h"
34 #include "utils/mono-memory-model.h"
35
36 #ifdef SGEN_HAVE_CARDTABLE
37
38 //#define CARDTABLE_STATS
39
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #ifdef HAVE_SYS_MMAN_H
44 #include <sys/mman.h>
45 #endif
46 #include <sys/types.h>
47
48 guint8 *sgen_cardtable;
49
50
51 #ifdef HEAVY_STATISTICS
52 long long marked_cards;
53 long long scanned_cards;
54 long long scanned_objects;
55 long long remarked_cards;
56
57 static long long los_marked_cards;
58 static long long large_objects;
59 static long long bloby_objects;
60 static long long los_array_cards;
61 static long long los_array_remsets;
62
63 #endif
64 static long long major_card_scan_time;
65 static long long los_card_scan_time;
66
67 static long long last_major_scan_time;
68 static long long last_los_scan_time;
69
70 static void sgen_card_tables_collect_stats (gboolean begin);
71
72
73 /*WARNING: This function returns the number of cards regardless of overflow in case of overlapping cards.*/
74 static mword
75 cards_in_range (mword address, mword size)
76 {
77         mword end = address + MAX (1, size) - 1;
78         return (end >> CARD_BITS) - (address >> CARD_BITS) + 1;
79 }
80
81 static void
82 sgen_card_table_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
83 {
84         *(void**)field_ptr = value;
85         if (sgen_ptr_in_nursery (value))
86                 sgen_card_table_mark_address ((mword)field_ptr);
87         sgen_dummy_use (value);
88 }
89
90 static void
91 sgen_card_table_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
92 {
93         *(void**)slot_ptr = value;
94         if (sgen_ptr_in_nursery (value))
95                 sgen_card_table_mark_address ((mword)slot_ptr);
96         sgen_dummy_use (value); 
97 }
98
99 static void
100 sgen_card_table_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
101 {
102         gpointer *dest = dest_ptr;
103         gpointer *src = src_ptr;
104
105         /*overlapping that required backward copying*/
106         if (src < dest && (src + count) > dest) {
107                 gpointer *start = dest;
108                 dest += count - 1;
109                 src += count - 1;
110
111                 for (; dest >= start; --src, --dest) {
112                         gpointer value = *src;
113                         *dest = value;
114                         if (sgen_ptr_in_nursery (value))
115                                 sgen_card_table_mark_address ((mword)dest);
116                         sgen_dummy_use (value);
117                 }
118         } else {
119                 gpointer *end = dest + count;
120                 for (; dest < end; ++src, ++dest) {
121                         gpointer value = *src;
122                         *dest = value;
123                         if (sgen_ptr_in_nursery (value))
124                                 sgen_card_table_mark_address ((mword)dest);
125                         sgen_dummy_use (value);
126                 }
127         }       
128 }
129
130 static void
131 sgen_card_table_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
132 {
133         size_t element_size = mono_class_value_size (klass, NULL);
134         size_t size = count * element_size;
135
136 #ifdef DISABLE_CRITICAL_REGION
137         LOCK_GC;
138 #else
139         TLAB_ACCESS_INIT;
140         ENTER_CRITICAL_REGION;
141 #endif
142         mono_gc_memmove (dest, src, size);
143         sgen_card_table_mark_range ((mword)dest, size);
144 #ifdef DISABLE_CRITICAL_REGION
145         UNLOCK_GC;
146 #else
147         EXIT_CRITICAL_REGION;
148 #endif
149 }
150
151 static void
152 sgen_card_table_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
153 {
154         int size = mono_object_class (obj)->instance_size;
155
156 #ifdef DISABLE_CRITICAL_REGION
157         LOCK_GC;
158 #else
159         TLAB_ACCESS_INIT;
160         ENTER_CRITICAL_REGION;
161 #endif
162         mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
163                         size - sizeof (MonoObject));
164         sgen_card_table_mark_range ((mword)obj, size);
165 #ifdef DISABLE_CRITICAL_REGION
166         UNLOCK_GC;
167 #else
168         EXIT_CRITICAL_REGION;
169 #endif  
170 }
171
172 static void
173 sgen_card_table_wbarrier_generic_nostore (gpointer ptr)
174 {
175         sgen_card_table_mark_address ((mword)ptr);      
176 }
177
178 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
179
180 guint8 *sgen_shadow_cardtable;
181
182 #define SGEN_SHADOW_CARDTABLE_END (sgen_shadow_cardtable + CARD_COUNT_IN_BYTES)
183 #define SGEN_CARDTABLE_END (sgen_cardtable + CARD_COUNT_IN_BYTES)
184
185 static gboolean
186 sgen_card_table_region_begin_scanning (mword start, mword end)
187 {
188         /*XXX this can be improved to work on words and have a single loop induction var */
189         while (start <= end) {
190                 if (sgen_card_table_card_begin_scanning (start))
191                         return TRUE;
192                 start += CARD_SIZE_IN_BYTES;
193         }
194         return FALSE;
195 }
196
197 #else
198
199 static gboolean
200 sgen_card_table_region_begin_scanning (mword start, mword size)
201 {
202         gboolean res = FALSE;
203         guint8 *card = sgen_card_table_get_card_address (start);
204         guint8 *end = card + cards_in_range (start, size);
205
206         /*XXX this can be improved to work on words and have a branchless body */
207         while (card != end) {
208                 if (*card++) {
209                         res = TRUE;
210                         break;
211                 }
212         }
213
214         memset (sgen_card_table_get_card_address (start), 0, size >> CARD_BITS);
215
216         return res;
217 }
218
219 #endif
220
221 /*FIXME this assumes that major blocks are multiple of 4K which is pretty reasonable */
222 gboolean
223 sgen_card_table_get_card_data (guint8 *data_dest, mword address, mword cards)
224 {
225         mword *start = (mword*)sgen_card_table_get_card_scan_address (address);
226         mword *dest = (mword*)data_dest;
227         mword *end = (mword*)(data_dest + cards);
228         mword mask = 0;
229
230         for (; dest < end; ++dest, ++start) {
231                 mword v = *start;
232                 *dest = v;
233                 mask |= v;
234
235 #ifndef SGEN_HAVE_OVERLAPPING_CARDS
236                 *start = 0;
237 #endif
238         }
239
240         return mask;
241 }
242
243 void*
244 sgen_card_table_align_pointer (void *ptr)
245 {
246         return (void*)((mword)ptr & ~(CARD_SIZE_IN_BYTES - 1));
247 }
248
249 void
250 sgen_card_table_mark_range (mword address, mword size)
251 {
252         memset (sgen_card_table_get_card_address (address), 1, cards_in_range (address, size));
253 }
254
255 static gboolean
256 sgen_card_table_is_range_marked (guint8 *cards, mword address, mword size)
257 {
258         guint8 *end = cards + cards_in_range (address, size);
259
260         /*This is safe since this function is only called by code that only passes continuous card blocks*/
261         while (cards != end) {
262                 if (*cards++)
263                         return TRUE;
264         }
265         return FALSE;
266
267 }
268
269 static void
270 sgen_card_table_record_pointer (gpointer address)
271 {
272         *sgen_card_table_get_card_address ((mword)address) = 1;
273 }
274
275 static gboolean
276 sgen_card_table_find_address (char *addr)
277 {
278         return sgen_card_table_address_is_marked ((mword)addr);
279 }
280
281 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
282
283 static void
284 move_cards_to_shadow_table (mword start, mword size)
285 {
286         guint8 *from = sgen_card_table_get_card_address (start);
287         guint8 *to = sgen_card_table_get_shadow_card_address (start);
288         size_t bytes = cards_in_range (start, size);
289
290         if (to + bytes > SGEN_SHADOW_CARDTABLE_END) {
291                 size_t first_chunk = SGEN_SHADOW_CARDTABLE_END - to;
292                 size_t second_chunk = MIN (CARD_COUNT_IN_BYTES, bytes) - first_chunk;
293
294                 memcpy (to, from, first_chunk);
295                 memcpy (sgen_shadow_cardtable, sgen_cardtable, second_chunk);
296         } else {
297                 memcpy (to, from, bytes);
298         }
299 }
300
301 static void
302 clear_cards (mword start, mword size)
303 {
304         guint8 *addr = sgen_card_table_get_card_address (start);
305         size_t bytes = cards_in_range (start, size);
306
307         if (addr + bytes > SGEN_CARDTABLE_END) {
308                 size_t first_chunk = SGEN_CARDTABLE_END - addr;
309
310                 memset (addr, 0, first_chunk);
311                 memset (sgen_cardtable, 0, bytes - first_chunk);
312         } else {
313                 memset (addr, 0, bytes);
314         }
315 }
316
317
318 #else
319
320 static void
321 clear_cards (mword start, mword size)
322 {
323         memset (sgen_card_table_get_card_address (start), 0, cards_in_range (start, size));
324 }
325
326
327 #endif
328
329 static void
330 sgen_card_table_prepare_for_major_collection (void)
331 {
332         /*XXX we could do this in 2 ways. using mincore or iterating over all sections/los objects */
333         sgen_major_collector_iterate_live_block_ranges (clear_cards);
334         sgen_los_iterate_live_block_ranges (clear_cards);
335 }
336
337 static void
338 sgen_card_table_finish_minor_collection (void)
339 {
340         sgen_card_tables_collect_stats (FALSE);
341 }
342
343 static void
344 sgen_card_table_finish_scan_remsets (void *start_nursery, void *end_nursery, SgenGrayQueue *queue)
345 {
346         SGEN_TV_DECLARE (atv);
347         SGEN_TV_DECLARE (btv);
348
349         sgen_card_tables_collect_stats (TRUE);
350
351 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
352         /*FIXME we should have a bit on each block/los object telling if the object have marked cards.*/
353         /*First we copy*/
354         sgen_major_collector_iterate_live_block_ranges (move_cards_to_shadow_table);
355         sgen_los_iterate_live_block_ranges (move_cards_to_shadow_table);
356
357         /*Then we clear*/
358         sgen_card_table_prepare_for_major_collection ();
359 #endif
360         SGEN_TV_GETTIME (atv);
361         sgen_major_collector_scan_card_table (queue);
362         SGEN_TV_GETTIME (btv);
363         last_major_scan_time = SGEN_TV_ELAPSED (atv, btv); 
364         major_card_scan_time += last_major_scan_time;
365         sgen_los_scan_card_table (queue);
366         SGEN_TV_GETTIME (atv);
367         last_los_scan_time = SGEN_TV_ELAPSED (btv, atv);
368         los_card_scan_time += last_los_scan_time;
369 }
370
371 guint8*
372 mono_gc_get_card_table (int *shift_bits, gpointer *mask)
373 {
374         if (!sgen_cardtable)
375                 return NULL;
376
377         *shift_bits = CARD_BITS;
378 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
379         *mask = (gpointer)CARD_MASK;
380 #else
381         *mask = NULL;
382 #endif
383
384         return sgen_cardtable;
385 }
386
387 gboolean
388 mono_gc_card_table_nursery_check (void)
389 {
390         return !major_collector.is_concurrent;
391 }
392
393 #if 0
394 static void
395 collect_faulted_cards (void)
396 {
397 #define CARD_PAGES (CARD_COUNT_IN_BYTES / 4096)
398         int i, count = 0;
399         unsigned char faulted [CARD_PAGES] = { 0 };
400         mincore (sgen_cardtable, CARD_COUNT_IN_BYTES, faulted);
401
402         for (i = 0; i < CARD_PAGES; ++i) {
403                 if (faulted [i])
404                         ++count;
405         }
406
407         printf ("TOTAL card pages %d faulted %d\n", CARD_PAGES, count);
408 }
409
410 void
411 sgen_card_table_dump_obj_card (char *object, size_t size, void *dummy)
412 {
413         guint8 *start = sgen_card_table_get_card_scan_address (object);
414         guint8 *end = start + cards_in_range (object, size);
415         int cnt = 0;
416         printf ("--obj %p %d cards [%p %p]--", object, size, start, end);
417         for (; start < end; ++start) {
418                 if (cnt == 0)
419                         printf ("\n\t[%p] ", start);
420                 printf ("%x ", *start);
421                 ++cnt;
422                 if (cnt == 8)
423                         cnt = 0;
424         }
425         printf ("\n");
426 }
427 #endif
428
429 #define MWORD_MASK (sizeof (mword) - 1)
430
431 static inline int
432 find_card_offset (mword card)
433 {
434 /*XXX Use assembly as this generates some pretty bad code */
435 #if defined(__i386__) && defined(__GNUC__)
436         return  (__builtin_ffs (card) - 1) / 8;
437 #elif defined(__x86_64__) && defined(__GNUC__)
438         return (__builtin_ffsll (card) - 1) / 8;
439 #elif defined(__s390x__)
440         return (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
441 #else
442         int i;
443         guint8 *ptr = (guint *) &card;
444         for (i = 0; i < sizeof (mword); ++i) {
445                 if (ptr[i])
446                         return i;
447         }
448         return 0;
449 #endif
450 }
451
452 static guint8*
453 find_next_card (guint8 *card_data, guint8 *end)
454 {
455         mword *cards, *cards_end;
456         mword card;
457
458         while ((((mword)card_data) & MWORD_MASK) && card_data < end) {
459                 if (*card_data)
460                         return card_data;
461                 ++card_data;
462         }
463
464         if (card_data == end)
465                 return end;
466
467         cards = (mword*)card_data;
468         cards_end = (mword*)((mword)end & ~MWORD_MASK);
469         while (cards < cards_end) {
470                 card = *cards;
471                 if (card)
472                         return (guint8*)cards + find_card_offset (card);
473                 ++cards;
474         }
475
476         card_data = (guint8*)cards_end;
477         while (card_data < end) {
478                 if (*card_data)
479                         return card_data;
480                 ++card_data;
481         }
482
483         return end;
484 }
485
486 void
487 sgen_cardtable_scan_object (char *obj, mword block_obj_size, guint8 *cards, SgenGrayQueue *queue)
488 {
489         MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
490         MonoClass *klass = vt->klass;
491
492         HEAVY_STAT (++large_objects);
493
494         if (!SGEN_VTABLE_HAS_REFERENCES (vt))
495                 return;
496
497         if (vt->rank) {
498                 guint8 *card_data, *card_base;
499                 guint8 *card_data_end;
500                 char *obj_start = sgen_card_table_align_pointer (obj);
501                 mword obj_size = sgen_par_object_get_size (vt, (MonoObject*)obj);
502                 char *obj_end = obj + obj_size;
503                 size_t card_count;
504                 int extra_idx = 0;
505
506                 MonoArray *arr = (MonoArray*)obj;
507                 mword desc = (mword)klass->element_class->gc_descr;
508                 int elem_size = mono_array_element_size (klass);
509
510 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
511                 guint8 *overflow_scan_end = NULL;
512 #endif
513
514                 if (cards)
515                         card_data = cards;
516                 else
517                         card_data = sgen_card_table_get_card_scan_address ((mword)obj);
518
519                 card_base = card_data;
520                 card_count = cards_in_range ((mword)obj, obj_size);
521                 card_data_end = card_data + card_count;
522
523
524 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
525                 /*Check for overflow and if so, setup to scan in two steps*/
526                 if (!cards && card_data_end >= SGEN_SHADOW_CARDTABLE_END) {
527                         overflow_scan_end = sgen_shadow_cardtable + (card_data_end - SGEN_SHADOW_CARDTABLE_END);
528                         card_data_end = SGEN_SHADOW_CARDTABLE_END;
529                 }
530
531 LOOP_HEAD:
532 #endif
533
534                 card_data = find_next_card (card_data, card_data_end);
535                 for (; card_data < card_data_end; card_data = find_next_card (card_data + 1, card_data_end)) {
536                         int index;
537                         int idx = (card_data - card_base) + extra_idx;
538                         char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES);
539                         char *card_end = start + CARD_SIZE_IN_BYTES;
540                         char *elem;
541
542                         HEAVY_STAT (++los_marked_cards);
543
544                         if (!cards)
545                                 sgen_card_table_prepare_card_for_scanning (card_data);
546
547                         card_end = MIN (card_end, obj_end);
548
549                         if (start <= (char*)arr->vector)
550                                 index = 0;
551                         else
552                                 index = ARRAY_OBJ_INDEX (start, obj, elem_size);
553
554                         elem = (char*)mono_array_addr_with_size ((MonoArray*)obj, elem_size, index);
555                         if (klass->element_class->valuetype) {
556                                 ScanVTypeFunc scan_vtype_func = sgen_get_current_object_ops ()->scan_vtype;
557
558                                 for (; elem < card_end; elem += elem_size)
559                                         scan_vtype_func (elem, desc, queue);
560                         } else {
561                                 CopyOrMarkObjectFunc copy_func = sgen_get_current_object_ops ()->copy_or_mark_object;
562
563                                 HEAVY_STAT (++los_array_cards);
564                                 for (; elem < card_end; elem += SIZEOF_VOID_P) {
565                                         gpointer new, old = *(gpointer*)elem;
566                                         if (G_UNLIKELY (sgen_ptr_in_nursery (old))) {
567                                                 HEAVY_STAT (++los_array_remsets);
568                                                 copy_func ((void**)elem, queue);
569                                                 new = *(gpointer*)elem;
570                                                 if (G_UNLIKELY (sgen_ptr_in_nursery (new)))
571                                                         sgen_add_to_global_remset (elem);
572                                         }
573                                 }
574                         }
575                 }
576
577 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
578                 if (overflow_scan_end) {
579                         extra_idx = card_data - card_base;
580                         card_base = card_data = sgen_shadow_cardtable;
581                         card_data_end = overflow_scan_end;
582                         overflow_scan_end = NULL;
583                         goto LOOP_HEAD;
584                 }
585 #endif
586
587         } else {
588                 HEAVY_STAT (++bloby_objects);
589                 if (cards) {
590                         if (sgen_card_table_is_range_marked (cards, (mword)obj, block_obj_size))
591                                 sgen_get_current_object_ops ()->scan_object (obj, queue);
592                 } else if (sgen_card_table_region_begin_scanning ((mword)obj, block_obj_size)) {
593                         sgen_get_current_object_ops ()->scan_object (obj, queue);
594                 }
595         }
596 }
597
598 #ifdef CARDTABLE_STATS
599
600 typedef struct {
601         int total, marked, remarked, gc_marked; 
602 } card_stats;
603
604 static card_stats major_stats, los_stats;
605 static card_stats *cur_stats;
606
607 static void
608 count_marked_cards (mword start, mword size)
609 {
610         mword end = start + size;
611         while (start <= end) {
612                 guint8 card = *sgen_card_table_get_card_address (start);
613                 ++cur_stats->total;
614                 if (card)
615                         ++cur_stats->marked;
616                 if (card == 2)
617                         ++cur_stats->gc_marked;
618                 start += CARD_SIZE_IN_BYTES;
619         }
620 }
621
622 static void
623 count_remarked_cards (mword start, mword size)
624 {
625         mword end = start + size;
626         while (start <= end) {
627                 if (sgen_card_table_address_is_marked (start)) {
628                         ++cur_stats->remarked;
629                         *sgen_card_table_get_card_address (start) = 2;
630                 }
631                 start += CARD_SIZE_IN_BYTES;
632         }
633 }
634
635 #endif
636
637 static void
638 sgen_card_tables_collect_stats (gboolean begin)
639 {
640 #ifdef CARDTABLE_STATS
641         if (begin) {
642                 memset (&major_stats, 0, sizeof (card_stats));
643                 memset (&los_stats, 0, sizeof (card_stats));
644                 cur_stats = &major_stats;
645                 sgen_major_collector_iterate_live_block_ranges (count_marked_cards);
646                 cur_stats = &los_stats;
647                 sgen_los_iterate_live_block_ranges (count_marked_cards);
648         } else {
649                 cur_stats = &major_stats;
650                 sgen_major_collector_iterate_live_block_ranges (count_remarked_cards);
651                 cur_stats = &los_stats;
652                 sgen_los_iterate_live_block_ranges (count_remarked_cards);
653                 printf ("cards major (t %d m %d g %d r %d)  los (t %d m %d g %d r %d) major_scan %.2fms los_scan %.2fms\n", 
654                         major_stats.total, major_stats.marked, major_stats.gc_marked, major_stats.remarked,
655                         los_stats.total, los_stats.marked, los_stats.gc_marked, los_stats.remarked,
656                         last_major_scan_time / 1000.0, last_los_scan_time / 1000.0);
657         }
658 #endif
659 }
660
661 void
662 sgen_card_table_init (SgenRemeberedSet *remset)
663 {
664         sgen_cardtable = sgen_alloc_os_memory (CARD_COUNT_IN_BYTES, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "card table");
665
666 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
667         sgen_shadow_cardtable = sgen_alloc_os_memory (CARD_COUNT_IN_BYTES, SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE, "shadow card table");
668 #endif
669
670 #ifdef HEAVY_STATISTICS
671         mono_counters_register ("marked cards", MONO_COUNTER_GC | MONO_COUNTER_LONG, &marked_cards);
672         mono_counters_register ("scanned cards", MONO_COUNTER_GC | MONO_COUNTER_LONG, &scanned_cards);
673         mono_counters_register ("remarked cards", MONO_COUNTER_GC | MONO_COUNTER_LONG, &remarked_cards);
674
675         mono_counters_register ("los marked cards", MONO_COUNTER_GC | MONO_COUNTER_LONG, &los_marked_cards);
676         mono_counters_register ("los array cards scanned ", MONO_COUNTER_GC | MONO_COUNTER_LONG, &los_array_cards);
677         mono_counters_register ("los array remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &los_array_remsets);
678         mono_counters_register ("cardtable scanned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &scanned_objects);
679         mono_counters_register ("cardtable large objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &large_objects);
680         mono_counters_register ("cardtable bloby objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &bloby_objects);
681 #endif
682         mono_counters_register ("cardtable major scan time", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &major_card_scan_time);
683         mono_counters_register ("cardtable los scan time", MONO_COUNTER_GC | MONO_COUNTER_TIME_INTERVAL, &los_card_scan_time);
684
685
686         remset->wbarrier_set_field = sgen_card_table_wbarrier_set_field;
687         remset->wbarrier_set_arrayref = sgen_card_table_wbarrier_set_arrayref;
688         remset->wbarrier_arrayref_copy = sgen_card_table_wbarrier_arrayref_copy;
689         remset->wbarrier_value_copy = sgen_card_table_wbarrier_value_copy;
690         remset->wbarrier_object_copy = sgen_card_table_wbarrier_object_copy;
691         remset->wbarrier_generic_nostore = sgen_card_table_wbarrier_generic_nostore;
692         remset->record_pointer = sgen_card_table_record_pointer;
693
694         remset->finish_scan_remsets = sgen_card_table_finish_scan_remsets;
695
696         remset->finish_minor_collection = sgen_card_table_finish_minor_collection;
697         remset->prepare_for_major_collection = sgen_card_table_prepare_for_major_collection;
698
699         remset->find_address = sgen_card_table_find_address;
700 }
701
702 #else
703
704 void
705 sgen_card_table_mark_address (mword address)
706 {
707         g_assert_not_reached ();
708 }
709
710 void
711 sgen_card_table_mark_range (mword address, mword size)
712 {
713         g_assert_not_reached ();
714 }
715
716 guint8*
717 mono_gc_get_card_table (int *shift_bits, gpointer *mask)
718 {
719         return NULL;
720 }
721
722 #endif
723
724 #endif /*HAVE_SGEN_GC*/