[sgen] Increase parallelization of minors
[mono.git] / mono / sgen / sgen-cardtable.c
1 /**
2  * \file
3  * Card table implementation for sgen
4  *
5  * Author:
6  *      Rodrigo Kumpera (rkumpera@novell.com)
7  *
8  * Copyright 2001-2003 Ximian, Inc
9  * Copyright 2003-2010 Novell, Inc.
10  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
11  * Copyright (C) 2012 Xamarin Inc
12  *
13  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14  */
15
16 #include "config.h"
17 #ifdef HAVE_SGEN_GC
18
19 #include <string.h>
20
21 #include "mono/sgen/sgen-gc.h"
22 #include "mono/sgen/sgen-cardtable.h"
23 #include "mono/sgen/sgen-memory-governor.h"
24 #include "mono/sgen/sgen-protocol.h"
25 #include "mono/sgen/sgen-layout-stats.h"
26 #include "mono/sgen/sgen-client.h"
27 #include "mono/sgen/gc-internal-agnostic.h"
28 #include "mono/utils/mono-memory-model.h"
29
30 //#define CARDTABLE_STATS
31
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 #ifdef HAVE_SYS_MMAN_H
36 #include <sys/mman.h>
37 #endif
38 #include <sys/types.h>
39
40 guint8 *sgen_cardtable;
41
42 static gboolean need_mod_union;
43
44 #ifdef HEAVY_STATISTICS
45 guint64 marked_cards;
46 guint64 scanned_cards;
47 guint64 scanned_objects;
48 guint64 remarked_cards;
49 static guint64 large_objects;
50 static guint64 bloby_objects;
51 #endif
52
53 mword
54 sgen_card_table_number_of_cards_in_range (mword address, mword size)
55 {
56         mword end = address + MAX (1, size) - 1;
57         return (end >> CARD_BITS) - (address >> CARD_BITS) + 1;
58 }
59
60 static void
61 sgen_card_table_wbarrier_set_field (GCObject *obj, gpointer field_ptr, GCObject* value)
62 {
63         *(void**)field_ptr = value;
64         if (need_mod_union || sgen_ptr_in_nursery (value))
65                 sgen_card_table_mark_address ((mword)field_ptr);
66         sgen_dummy_use (value);
67 }
68
69 static void
70 sgen_card_table_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
71 {
72         gpointer *dest = (gpointer *)dest_ptr;
73         gpointer *src = (gpointer *)src_ptr;
74
75         /*overlapping that required backward copying*/
76         if (src < dest && (src + count) > dest) {
77                 gpointer *start = dest;
78                 dest += count - 1;
79                 src += count - 1;
80
81                 for (; dest >= start; --src, --dest) {
82                         gpointer value = *src;
83                         SGEN_UPDATE_REFERENCE_ALLOW_NULL (dest, value);
84                         if (need_mod_union || sgen_ptr_in_nursery (value))
85                                 sgen_card_table_mark_address ((mword)dest);
86                         sgen_dummy_use (value);
87                 }
88         } else {
89                 gpointer *end = dest + count;
90                 for (; dest < end; ++src, ++dest) {
91                         gpointer value = *src;
92                         SGEN_UPDATE_REFERENCE_ALLOW_NULL (dest, value);
93                         if (need_mod_union || sgen_ptr_in_nursery (value))
94                                 sgen_card_table_mark_address ((mword)dest);
95                         sgen_dummy_use (value);
96                 }
97         }       
98 }
99
100 static void
101 sgen_card_table_wbarrier_value_copy (gpointer dest, gpointer src, int count, size_t element_size)
102 {
103         size_t size = count * element_size;
104
105         TLAB_ACCESS_INIT;
106         ENTER_CRITICAL_REGION;
107
108         mono_gc_memmove_atomic (dest, src, size);
109         sgen_card_table_mark_range ((mword)dest, size);
110
111         EXIT_CRITICAL_REGION;
112 }
113
114 static void
115 sgen_card_table_wbarrier_object_copy (GCObject* obj, GCObject *src)
116 {
117         size_t size = sgen_client_par_object_get_size (SGEN_LOAD_VTABLE_UNCHECKED (obj), obj);
118
119         TLAB_ACCESS_INIT;
120         ENTER_CRITICAL_REGION;
121
122         mono_gc_memmove_aligned ((char*)obj + SGEN_CLIENT_OBJECT_HEADER_SIZE, (char*)src + SGEN_CLIENT_OBJECT_HEADER_SIZE,
123                         size - SGEN_CLIENT_OBJECT_HEADER_SIZE);
124         sgen_card_table_mark_range ((mword)obj, size);
125
126         EXIT_CRITICAL_REGION;
127 }
128
129 static void
130 sgen_card_table_wbarrier_generic_nostore (gpointer ptr)
131 {
132         sgen_card_table_mark_address ((mword)ptr);      
133 }
134
135 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
136
137 guint8 *sgen_shadow_cardtable;
138
139 #define SGEN_CARDTABLE_END (sgen_cardtable + CARD_COUNT_IN_BYTES)
140
141 static gboolean
142 sgen_card_table_region_begin_scanning (mword start, mword size)
143 {
144         mword end = start + size;
145         /*XXX this can be improved to work on words and have a single loop induction var */
146         while (start < end) {
147                 if (sgen_card_table_card_begin_scanning (start))
148                         return TRUE;
149                 start += CARD_SIZE_IN_BYTES;
150         }
151         return FALSE;
152 }
153
154 #else
155
156 static gboolean
157 sgen_card_table_region_begin_scanning (mword start, mword size)
158 {
159         gboolean res = FALSE;
160         guint8 *card = sgen_card_table_get_card_address (start);
161         guint8 *end = card + sgen_card_table_number_of_cards_in_range (start, size);
162
163         /*XXX this can be improved to work on words and have a branchless body */
164         while (card != end) {
165                 if (*card++) {
166                         res = TRUE;
167                         break;
168                 }
169         }
170
171         memset (sgen_card_table_get_card_address (start), 0, size >> CARD_BITS);
172
173         return res;
174 }
175
176 #endif
177
178 /*FIXME this assumes that major blocks are multiple of 4K which is pretty reasonable */
179 gboolean
180 sgen_card_table_get_card_data (guint8 *data_dest, mword address, mword cards)
181 {
182         mword *start = (mword*)sgen_card_table_get_card_scan_address (address);
183         mword *dest = (mword*)data_dest;
184         mword *end = (mword*)(data_dest + cards);
185         mword mask = 0;
186
187         for (; dest < end; ++dest, ++start) {
188                 mword v = *start;
189                 *dest = v;
190                 mask |= v;
191
192 #ifndef SGEN_HAVE_OVERLAPPING_CARDS
193                 *start = 0;
194 #endif
195         }
196
197         return mask != 0;
198 }
199
200 void*
201 sgen_card_table_align_pointer (void *ptr)
202 {
203         return (void*)((mword)ptr & ~(CARD_SIZE_IN_BYTES - 1));
204 }
205
206 void
207 sgen_card_table_mark_range (mword address, mword size)
208 {
209         mword num_cards = sgen_card_table_number_of_cards_in_range (address, size);
210         guint8 *start = sgen_card_table_get_card_address (address);
211
212 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
213         /*
214          * FIXME: There's a theoretical bug here, namely that the card table is allocated so
215          * far toward the end of the address space that start + num_cards overflows.
216          */
217         guint8 *end = start + num_cards;
218         SGEN_ASSERT (0, num_cards <= CARD_COUNT_IN_BYTES, "How did we get an object larger than the card table?");
219         if (end > SGEN_CARDTABLE_END) {
220                 memset (start, 1, SGEN_CARDTABLE_END - start);
221                 memset (sgen_cardtable, 1, end - SGEN_CARDTABLE_END);
222                 return;
223         }
224 #endif
225
226         memset (start, 1, num_cards);
227 }
228
229 static gboolean
230 sgen_card_table_is_range_marked (guint8 *cards, mword address, mword size)
231 {
232         guint8 *end = cards + sgen_card_table_number_of_cards_in_range (address, size);
233
234         /*This is safe since this function is only called by code that only passes continuous card blocks*/
235         while (cards != end) {
236                 if (*cards++)
237                         return TRUE;
238         }
239         return FALSE;
240
241 }
242
243 static void
244 sgen_card_table_record_pointer (gpointer address)
245 {
246         *sgen_card_table_get_card_address ((mword)address) = 1;
247 }
248
249 static gboolean
250 sgen_card_table_find_address (char *addr)
251 {
252         return sgen_card_table_address_is_marked ((mword)addr);
253 }
254
255 static gboolean
256 sgen_card_table_find_address_with_cards (char *cards_start, guint8 *cards, char *addr)
257 {
258         cards_start = (char *)sgen_card_table_align_pointer (cards_start);
259         return cards [(addr - cards_start) >> CARD_BITS];
260 }
261
262 static void
263 update_mod_union (guint8 *dest, guint8 *start_card, size_t num_cards)
264 {
265         int i;
266         /* Marking from another thread can happen while we mark here */
267         for (i = 0; i < num_cards; ++i) {
268                 if (start_card [i])
269                         dest [i] = 1;
270         }
271 }
272
273 guint8*
274 sgen_card_table_alloc_mod_union (char *obj, mword obj_size)
275 {
276         size_t num_cards = sgen_card_table_number_of_cards_in_range ((mword) obj, obj_size);
277         guint8 *mod_union = (guint8 *)sgen_alloc_internal_dynamic (num_cards, INTERNAL_MEM_CARDTABLE_MOD_UNION, TRUE);
278         memset (mod_union, 0, num_cards);
279         return mod_union;
280 }
281
282 void
283 sgen_card_table_free_mod_union (guint8 *mod_union, char *obj, mword obj_size)
284 {
285         size_t num_cards = sgen_card_table_number_of_cards_in_range ((mword) obj, obj_size);
286         sgen_free_internal_dynamic (mod_union, num_cards, INTERNAL_MEM_CARDTABLE_MOD_UNION);
287 }
288
289 void
290 sgen_card_table_update_mod_union_from_cards (guint8 *dest, guint8 *start_card, size_t num_cards)
291 {
292         SGEN_ASSERT (0, dest, "Why don't we have a mod union?");
293         update_mod_union (dest, start_card, num_cards);
294 }
295
296 void
297 sgen_card_table_update_mod_union (guint8 *dest, char *obj, mword obj_size, size_t *out_num_cards)
298 {
299         guint8 *start_card = sgen_card_table_get_card_address ((mword)obj);
300 #ifndef SGEN_HAVE_OVERLAPPING_CARDS
301         guint8 *end_card = sgen_card_table_get_card_address ((mword)obj + obj_size - 1) + 1;
302 #endif
303         size_t num_cards;
304
305 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
306         size_t rest;
307
308         rest = num_cards = sgen_card_table_number_of_cards_in_range ((mword) obj, obj_size);
309
310         while (start_card + rest > SGEN_CARDTABLE_END) {
311                 size_t count = SGEN_CARDTABLE_END - start_card;
312                 sgen_card_table_update_mod_union_from_cards (dest, start_card, count);
313                 dest += count;
314                 rest -= count;
315                 start_card = sgen_cardtable;
316         }
317         num_cards = rest;
318 #else
319         num_cards = end_card - start_card;
320 #endif
321
322         sgen_card_table_update_mod_union_from_cards (dest, start_card, num_cards);
323
324         if (out_num_cards)
325                 *out_num_cards = num_cards;
326 }
327
328 /* Preclean cards and saves the cards that need to be scanned afterwards in cards_preclean */
329 void
330 sgen_card_table_preclean_mod_union (guint8 *cards, guint8 *cards_preclean, size_t num_cards)
331 {
332         size_t i;
333
334         memcpy (cards_preclean, cards, num_cards);
335         for (i = 0; i < num_cards; i++) {
336                 if (cards_preclean [i]) {
337                         cards [i] = 0;
338                 }
339         }
340         /*
341          * When precleaning we need to make sure the card cleaning
342          * takes place before the object is scanned. If we don't
343          * do this we could finish scanning the object and, before
344          * the cleaning of the card takes place, another thread
345          * could dirty the object, mark the mod_union card only for
346          * us to clean it back, without scanning the object again.
347          */
348         mono_memory_barrier ();
349 }
350
351 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
352
353 static void
354 move_cards_to_shadow_table (mword start, mword size)
355 {
356         guint8 *from = sgen_card_table_get_card_address (start);
357         guint8 *to = sgen_card_table_get_shadow_card_address (start);
358         size_t bytes = sgen_card_table_number_of_cards_in_range (start, size);
359
360         if (bytes >= CARD_COUNT_IN_BYTES) {
361                 memcpy (sgen_shadow_cardtable, sgen_cardtable, CARD_COUNT_IN_BYTES);
362         } else if (to + bytes > SGEN_SHADOW_CARDTABLE_END) {
363                 size_t first_chunk = SGEN_SHADOW_CARDTABLE_END - to;
364                 size_t second_chunk = MIN (CARD_COUNT_IN_BYTES, bytes) - first_chunk;
365
366                 memcpy (to, from, first_chunk);
367                 memcpy (sgen_shadow_cardtable, sgen_cardtable, second_chunk);
368         } else {
369                 memcpy (to, from, bytes);
370         }
371 }
372
373 static void
374 clear_cards (mword start, mword size)
375 {
376         guint8 *addr = sgen_card_table_get_card_address (start);
377         size_t bytes = sgen_card_table_number_of_cards_in_range (start, size);
378
379         if (bytes >= CARD_COUNT_IN_BYTES) {
380                 memset (sgen_cardtable, 0, CARD_COUNT_IN_BYTES);
381         } else if (addr + bytes > SGEN_CARDTABLE_END) {
382                 size_t first_chunk = SGEN_CARDTABLE_END - addr;
383
384                 memset (addr, 0, first_chunk);
385                 memset (sgen_cardtable, 0, bytes - first_chunk);
386         } else {
387                 memset (addr, 0, bytes);
388         }
389 }
390
391
392 #else
393
394 static void
395 clear_cards (mword start, mword size)
396 {
397         memset (sgen_card_table_get_card_address (start), 0, sgen_card_table_number_of_cards_in_range (start, size));
398 }
399
400
401 #endif
402
403 static void
404 sgen_card_table_clear_cards (void)
405 {
406         /*XXX we could do this in 2 ways. using mincore or iterating over all sections/los objects */
407         sgen_major_collector_iterate_block_ranges (clear_cards);
408         sgen_los_iterate_live_block_ranges (clear_cards);
409         sgen_wbroots_iterate_live_block_ranges (clear_cards);
410 }
411
412 static void
413 sgen_card_table_start_scan_remsets (void)
414 {
415 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
416         /*FIXME we should have a bit on each block/los object telling if the object have marked cards.*/
417         /*First we copy*/
418         sgen_major_collector_iterate_block_ranges (move_cards_to_shadow_table);
419         sgen_los_iterate_live_block_ranges (move_cards_to_shadow_table);
420         sgen_wbroots_iterate_live_block_ranges (move_cards_to_shadow_table);
421
422         /*Then we clear*/
423         sgen_card_table_clear_cards ();
424 #endif
425 }
426
427 guint8*
428 sgen_get_card_table_configuration (int *shift_bits, gpointer *mask)
429 {
430 #ifndef MANAGED_WBARRIER
431         return NULL;
432 #else
433         if (!sgen_cardtable)
434                 return NULL;
435
436         *shift_bits = CARD_BITS;
437 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
438         *mask = (gpointer)CARD_MASK;
439 #else
440         *mask = NULL;
441 #endif
442
443         return sgen_cardtable;
444 #endif
445 }
446
447 #if 0
448 void
449 sgen_card_table_dump_obj_card (GCObject *object, size_t size, void *dummy)
450 {
451         guint8 *start = sgen_card_table_get_card_scan_address (object);
452         guint8 *end = start + sgen_card_table_number_of_cards_in_range (object, size);
453         int cnt = 0;
454         printf ("--obj %p %d cards [%p %p]--", object, size, start, end);
455         for (; start < end; ++start) {
456                 if (cnt == 0)
457                         printf ("\n\t[%p] ", start);
458                 printf ("%x ", *start);
459                 ++cnt;
460                 if (cnt == 8)
461                         cnt = 0;
462         }
463         printf ("\n");
464 }
465 #endif
466
467 /*
468  * Cardtable scanning
469  */
470
471 #define MWORD_MASK (sizeof (mword) - 1)
472
473 static inline int
474 find_card_offset (mword card)
475 {
476 /*XXX Use assembly as this generates some pretty bad code */
477 #if (defined(__i386__) || defined(__arm__)) && defined(__GNUC__)
478         return  (__builtin_ffs (card) - 1) / 8;
479 #elif (defined(__x86_64__) || defined(__aarch64__)) && defined(__GNUC__)
480         return (__builtin_ffsll (card) - 1) / 8;
481 #elif defined(__s390x__)
482         return (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
483 #else
484         int i;
485         guint8 *ptr = (guint8 *) &card;
486         for (i = 0; i < sizeof (mword); ++i) {
487                 if (ptr[i])
488                         return i;
489         }
490         return 0;
491 #endif
492 }
493
494 guint8*
495 sgen_find_next_card (guint8 *card_data, guint8 *end)
496 {
497         mword *cards, *cards_end;
498         mword card;
499
500         while ((((mword)card_data) & MWORD_MASK) && card_data < end) {
501                 if (*card_data)
502                         return card_data;
503                 ++card_data;
504         }
505
506         if (card_data == end)
507                 return end;
508
509         cards = (mword*)card_data;
510         cards_end = (mword*)((mword)end & ~MWORD_MASK);
511         while (cards < cards_end) {
512                 card = *cards;
513                 if (card)
514                         return (guint8*)cards + find_card_offset (card);
515                 ++cards;
516         }
517
518         card_data = (guint8*)cards_end;
519         while (card_data < end) {
520                 if (*card_data)
521                         return card_data;
522                 ++card_data;
523         }
524
525         return end;
526 }
527
528 void
529 sgen_cardtable_scan_object (GCObject *obj, mword block_obj_size, guint8 *cards, ScanCopyContext ctx)
530 {
531         HEAVY_STAT (++large_objects);
532
533         if (sgen_client_cardtable_scan_object (obj, cards, ctx))
534                 return;
535
536         HEAVY_STAT (++bloby_objects);
537         if (cards) {
538                 if (sgen_card_table_is_range_marked (cards, (mword)obj, block_obj_size))
539                         ctx.ops->scan_object (obj, sgen_obj_get_descriptor (obj), ctx.queue);
540         } else if (sgen_card_table_region_begin_scanning ((mword)obj, block_obj_size)) {
541                 ctx.ops->scan_object (obj, sgen_obj_get_descriptor (obj), ctx.queue);
542         }
543
544         binary_protocol_card_scan (obj, sgen_safe_object_get_size (obj));
545 }
546
547 void
548 sgen_card_table_init (SgenRememberedSet *remset)
549 {
550         sgen_cardtable = (guint8 *)sgen_alloc_os_memory (CARD_COUNT_IN_BYTES, (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), "card table", MONO_MEM_ACCOUNT_SGEN_CARD_TABLE);
551
552 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
553         sgen_shadow_cardtable = (guint8 *)sgen_alloc_os_memory (CARD_COUNT_IN_BYTES, (SgenAllocFlags)(SGEN_ALLOC_INTERNAL | SGEN_ALLOC_ACTIVATE), "shadow card table", MONO_MEM_ACCOUNT_SGEN_SHADOW_CARD_TABLE);
554 #endif
555
556 #ifdef HEAVY_STATISTICS
557         mono_counters_register ("marked cards", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &marked_cards);
558         mono_counters_register ("scanned cards", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &scanned_cards);
559         mono_counters_register ("remarked cards", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &remarked_cards);
560
561         mono_counters_register ("cardtable scanned objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &scanned_objects);
562         mono_counters_register ("cardtable large objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &large_objects);
563         mono_counters_register ("cardtable bloby objects", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &bloby_objects);
564 #endif
565
566         remset->wbarrier_set_field = sgen_card_table_wbarrier_set_field;
567         remset->wbarrier_arrayref_copy = sgen_card_table_wbarrier_arrayref_copy;
568         remset->wbarrier_value_copy = sgen_card_table_wbarrier_value_copy;
569         remset->wbarrier_object_copy = sgen_card_table_wbarrier_object_copy;
570         remset->wbarrier_generic_nostore = sgen_card_table_wbarrier_generic_nostore;
571         remset->record_pointer = sgen_card_table_record_pointer;
572
573         remset->start_scan_remsets = sgen_card_table_start_scan_remsets;
574
575         remset->clear_cards = sgen_card_table_clear_cards;
576
577         remset->find_address = sgen_card_table_find_address;
578         remset->find_address_with_cards = sgen_card_table_find_address_with_cards;
579
580         need_mod_union = sgen_get_major_collector ()->is_concurrent;
581 }
582
583 #endif /*HAVE_SGEN_GC*/