Merge branch 'feature-perpetual-pinning'
[mono.git] / mono / metadata / sgen-ssb.c
1 /*
2  * sgen-ssb.c: Remembered sets - sequential store buffer
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-ssb.h"
31 #include "metadata/sgen-protocol.h"
32 #include "utils/mono-counters.h"
33
34 #ifndef DISABLE_SGEN_REMSET
35
36 /*A two slots cache for recently inserted remsets */
37 static gpointer global_remset_cache [2];
38
39 static LOCK_DECLARE (global_remset_mutex);
40
41 #define LOCK_GLOBAL_REMSET mono_mutex_lock (&global_remset_mutex)
42 #define UNLOCK_GLOBAL_REMSET mono_mutex_unlock (&global_remset_mutex)
43
44 #ifdef HAVE_KW_THREAD
45 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
46 #endif
47 static MonoNativeTlsKey remembered_set_key;
48 static RememberedSet *global_remset;
49 static RememberedSet *freed_thread_remsets;
50 static GenericStoreRememberedSet *generic_store_remsets = NULL;
51
52 #ifdef HEAVY_STATISTICS
53 static int stat_wbarrier_generic_store_remset = 0;
54
55 static long long stat_store_remsets = 0;
56 static long long stat_store_remsets_unique = 0;
57 static long long stat_saved_remsets_1 = 0;
58 static long long stat_saved_remsets_2 = 0;
59 static long long stat_local_remsets_processed = 0;
60 static long long stat_global_remsets_added = 0;
61 static long long stat_global_remsets_readded = 0;
62 static long long stat_global_remsets_processed = 0;
63 static long long stat_global_remsets_discarded = 0;
64
65 #endif
66
67 static gboolean global_remset_location_was_not_added (gpointer ptr);
68
69
70 static void
71 clear_thread_store_remset_buffer (SgenThreadInfo *info)
72 {
73         *info->store_remset_buffer_index_addr = 0;
74         /* See the comment at the end of sgen_thread_unregister() */
75         if (*info->store_remset_buffer_addr)
76                 memset (*info->store_remset_buffer_addr, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
77 }
78
79 static size_t
80 remset_byte_size (RememberedSet *remset)
81 {
82         return sizeof (RememberedSet) + (remset->end_set - remset->data) * sizeof (gpointer);
83 }
84
85 static void
86 add_generic_store_remset_from_buffer (gpointer *buffer)
87 {
88         GenericStoreRememberedSet *remset = sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
89         memcpy (remset->data, buffer + 1, sizeof (gpointer) * (STORE_REMSET_BUFFER_SIZE - 1));
90         remset->next = generic_store_remsets;
91         generic_store_remsets = remset;
92 }
93
94 static void
95 evacuate_remset_buffer (void)
96 {
97         gpointer *buffer;
98         TLAB_ACCESS_INIT;
99
100         buffer = STORE_REMSET_BUFFER;
101
102         add_generic_store_remset_from_buffer (buffer);
103         memset (buffer, 0, sizeof (gpointer) * STORE_REMSET_BUFFER_SIZE);
104
105         STORE_REMSET_BUFFER_INDEX = 0;
106 }
107
108 /* FIXME: later choose a size that takes into account the RememberedSet struct
109  * and doesn't waste any alloc paddin space.
110  */
111 static RememberedSet*
112 sgen_alloc_remset (int size, gpointer id, gboolean global)
113 {
114         RememberedSet* res = sgen_alloc_internal_dynamic (sizeof (RememberedSet) + (size * sizeof (gpointer)), INTERNAL_MEM_REMSET, TRUE);
115         res->store_next = res->data;
116         res->end_set = res->data + size;
117         res->next = NULL;
118         SGEN_LOG (4, "Allocated%s remset size %d at %p for %p", global ? " global" : "", size, res->data, id);
119         return res;
120 }
121
122
123
124 static void
125 sgen_ssb_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
126 {
127         RememberedSet *rs;
128         TLAB_ACCESS_INIT;
129
130         LOCK_GC;
131         rs = REMEMBERED_SET;
132         if (rs->store_next < rs->end_set) {
133                 *(rs->store_next++) = (mword)field_ptr;
134                 *(void**)field_ptr = value;
135                 UNLOCK_GC;
136                 return;
137         }
138         rs = sgen_alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
139         rs->next = REMEMBERED_SET;
140         REMEMBERED_SET = rs;
141 #ifdef HAVE_KW_THREAD
142         mono_thread_info_current ()->remset = rs;
143 #endif
144         *(rs->store_next++) = (mword)field_ptr;
145         *(void**)field_ptr = value;
146         UNLOCK_GC;
147 }
148
149 static void
150 sgen_ssb_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
151 {
152         RememberedSet *rs;
153         TLAB_ACCESS_INIT;
154
155         LOCK_GC;
156         rs = REMEMBERED_SET;
157         if (rs->store_next < rs->end_set) {
158                 *(rs->store_next++) = (mword)slot_ptr;
159                 *(void**)slot_ptr = value;
160                 UNLOCK_GC;
161                 return;
162         }
163         rs = sgen_alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
164         rs->next = REMEMBERED_SET;
165         REMEMBERED_SET = rs;
166 #ifdef HAVE_KW_THREAD
167         mono_thread_info_current ()->remset = rs;
168 #endif
169         *(rs->store_next++) = (mword)slot_ptr;
170         *(void**)slot_ptr = value;
171         UNLOCK_GC;
172 }
173
174 static void
175 sgen_ssb_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
176 {
177         RememberedSet *rs;
178         TLAB_ACCESS_INIT;
179         LOCK_GC;
180         mono_gc_memmove (dest_ptr, src_ptr, count * sizeof (gpointer));
181
182         rs = REMEMBERED_SET;
183         SGEN_LOG (8, "Adding remset at %p, %d", dest_ptr, count);
184         if (rs->store_next + 1 < rs->end_set) {
185                 *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
186                 *(rs->store_next++) = count;
187                 UNLOCK_GC;
188                 return;
189         }
190         rs = sgen_alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
191         rs->next = REMEMBERED_SET;
192         REMEMBERED_SET = rs;
193 #ifdef HAVE_KW_THREAD
194         mono_thread_info_current ()->remset = rs;
195 #endif
196         *(rs->store_next++) = (mword)dest_ptr | REMSET_RANGE;
197         *(rs->store_next++) = count;
198
199         UNLOCK_GC;
200 }
201
202 static void
203 sgen_ssb_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
204 {
205         RememberedSet *rs;
206         size_t element_size = mono_class_value_size (klass, NULL);
207         size_t size = count * element_size;
208         TLAB_ACCESS_INIT;
209
210         g_assert (klass->gc_descr_inited);
211
212         LOCK_GC;
213         mono_gc_memmove (dest, src, size);
214         rs = REMEMBERED_SET;
215
216         if (rs->store_next + 4 < rs->end_set) {
217                 *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
218                 *(rs->store_next++) = (mword)klass->gc_descr;
219                 *(rs->store_next++) = (mword)count;
220                 *(rs->store_next++) = (mword)element_size;
221                 UNLOCK_GC;
222                 return;
223         }
224         rs = sgen_alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
225         rs->next = REMEMBERED_SET;
226         REMEMBERED_SET = rs;
227 #ifdef HAVE_KW_THREAD
228         mono_thread_info_current ()->remset = rs;
229 #endif
230         *(rs->store_next++) = (mword)dest | REMSET_VTYPE;
231         *(rs->store_next++) = (mword)klass->gc_descr;
232         *(rs->store_next++) = (mword)count;
233         *(rs->store_next++) = (mword)element_size;
234         UNLOCK_GC;
235 }       
236
237 static void
238 sgen_ssb_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
239 {
240         int size;
241         RememberedSet *rs;
242         TLAB_ACCESS_INIT;
243
244         size = mono_object_class (obj)->instance_size;
245
246         rs = REMEMBERED_SET;
247         SGEN_LOG (6, "Adding object remset for %p", obj);
248
249         LOCK_GC;
250         /* do not copy the sync state */
251         mono_gc_memmove ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
252                         size - sizeof (MonoObject));
253
254         if (rs->store_next < rs->end_set) {
255                 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
256                 UNLOCK_GC;
257                 return;
258         }
259         rs = sgen_alloc_remset (rs->end_set - rs->data, (void*)1, FALSE);
260         rs->next = REMEMBERED_SET;
261         REMEMBERED_SET = rs;
262
263         #ifdef HAVE_KW_THREAD
264         mono_thread_info_current ()->remset = rs;
265         #endif
266         *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
267         UNLOCK_GC;
268 }
269
270 static void
271 sgen_ssb_wbarrier_generic_nostore (gpointer ptr)
272 {
273         gpointer *buffer;
274         int index;
275         TLAB_ACCESS_INIT;
276
277         LOCK_GC;
278
279         buffer = STORE_REMSET_BUFFER;
280         index = STORE_REMSET_BUFFER_INDEX;
281         /* This simple optimization eliminates a sizable portion of
282            entries.  Comparing it to the last but one entry as well
283            doesn't eliminate significantly more entries. */
284         if (buffer [index] == ptr) {
285                 UNLOCK_GC;
286                 return;
287         }
288
289         HEAVY_STAT (++stat_wbarrier_generic_store_remset);
290
291         ++index;
292         if (index >= STORE_REMSET_BUFFER_SIZE) {
293                 evacuate_remset_buffer ();
294                 index = STORE_REMSET_BUFFER_INDEX;
295                 g_assert (index == 0);
296                 ++index;
297         }
298         buffer [index] = ptr;
299         STORE_REMSET_BUFFER_INDEX = index;
300
301         UNLOCK_GC;
302 }
303
304
305 #ifdef HEAVY_STATISTICS
306 static mword*
307 collect_store_remsets (RememberedSet *remset, mword *bumper)
308 {
309         mword *p = remset->data;
310         mword last = 0;
311         mword last1 = 0;
312         mword last2 = 0;
313
314         while (p < remset->store_next) {
315                 switch ((*p) & REMSET_TYPE_MASK) {
316                 case REMSET_LOCATION:
317                         *bumper++ = *p;
318                         if (*p == last)
319                                 ++stat_saved_remsets_1;
320                         last = *p;
321                         if (*p == last1 || *p == last2) {
322                                 ++stat_saved_remsets_2;
323                         } else {
324                                 last2 = last1;
325                                 last1 = *p;
326                         }
327                         p += 1;
328                         break;
329                 case REMSET_RANGE:
330                         p += 2;
331                         break;
332                 case REMSET_OBJECT:
333                         p += 1;
334                         break;
335                 case REMSET_VTYPE:
336                         p += 4;
337                         break;
338                 default:
339                         g_assert_not_reached ();
340                 }
341         }
342
343         return bumper;
344 }
345
346 static void
347 remset_stats (void)
348 {
349         RememberedSet *remset;
350         int size = 0;
351         SgenThreadInfo *info;
352         mword *addresses, *bumper, *p, *r;
353
354         FOREACH_THREAD (info) {
355                 for (remset = info->remset; remset; remset = remset->next)
356                         size += remset->store_next - remset->data;
357         } END_FOREACH_THREAD
358         for (remset = freed_thread_remsets; remset; remset = remset->next)
359                 size += remset->store_next - remset->data;
360         for (remset = global_remset; remset; remset = remset->next)
361                 size += remset->store_next - remset->data;
362
363         bumper = addresses = sgen_alloc_internal_dynamic (sizeof (mword) * size, INTERNAL_MEM_STATISTICS, TRUE);
364
365         FOREACH_THREAD (info) {
366                 for (remset = info->remset; remset; remset = remset->next)
367                         bumper = collect_store_remsets (remset, bumper);
368         } END_FOREACH_THREAD
369         for (remset = global_remset; remset; remset = remset->next)
370                 bumper = collect_store_remsets (remset, bumper);
371         for (remset = freed_thread_remsets; remset; remset = remset->next)
372                 bumper = collect_store_remsets (remset, bumper);
373
374         g_assert (bumper <= addresses + size);
375
376         stat_store_remsets += bumper - addresses;
377
378         sgen_sort_addresses ((void**)addresses, bumper - addresses);
379         p = addresses;
380         r = addresses + 1;
381         while (r < bumper) {
382                 if (*r != *p)
383                         *++p = *r;
384                 ++r;
385         }
386
387         stat_store_remsets_unique += p - addresses;
388
389         sgen_free_internal_dynamic (addresses, sizeof (mword) * size, INTERNAL_MEM_STATISTICS);
390 }
391 #endif
392
393
394 static mword*
395 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global, SgenGrayQueue *queue)
396 {
397         void **ptr;
398         mword count;
399         mword desc;
400
401         if (global)
402                 HEAVY_STAT (++stat_global_remsets_processed);
403         else
404                 HEAVY_STAT (++stat_local_remsets_processed);
405
406         /* FIXME: exclude stack locations */
407         switch ((*p) & REMSET_TYPE_MASK) {
408         case REMSET_LOCATION:
409                 ptr = (void**)(*p);
410                 //__builtin_prefetch (ptr);
411                 if (((void*)ptr < start_nursery || (void*)ptr >= end_nursery)) {
412                         gpointer old = *ptr;
413                         gpointer copy;
414
415                         sgen_get_current_object_ops ()->copy_or_mark_object (ptr, queue);
416                         copy = *ptr;
417                         SGEN_LOG (9, "Overwrote remset at %p with %p", ptr, copy);
418                         if (old)
419                                 binary_protocol_ptr_update (ptr, old, copy, (gpointer)SGEN_LOAD_VTABLE (copy), sgen_safe_object_get_size (copy));
420                         if (!global && copy >= start_nursery && copy < end_nursery) {
421                                 /*
422                                  * If the object is pinned, each reference to it from nonpinned objects
423                                  * becomes part of the global remset, which can grow very large.
424                                  */
425                                 SGEN_LOG (9, "Add to global remset because of pinning %p (%p %s)", ptr, copy, sgen_safe_name (copy));
426                                 sgen_add_to_global_remset (ptr, copy, FALSE);
427                         }
428                 } else {
429                         SGEN_LOG (9, "Skipping remset at %p holding %p", ptr, *ptr);
430                 }
431                 return p + 1;
432         case REMSET_RANGE: {
433                 CopyOrMarkObjectFunc copy_func = sgen_get_current_object_ops ()->copy_or_mark_object;
434
435                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
436                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
437                         return p + 2;
438                 count = p [1];
439                 while (count-- > 0) {
440                         gpointer copy;
441                         copy_func (ptr, queue);
442                         copy = *ptr;
443                         SGEN_LOG (9, "Overwrote remset at %p with %p (count: %d)", ptr, copy, (int)count);
444                         if (!global && copy >= start_nursery && copy < end_nursery)
445                                 sgen_add_to_global_remset (ptr, copy, FALSE);
446                         ++ptr;
447                 }
448                 return p + 2;
449         }
450         case REMSET_OBJECT:
451                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
452                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
453                         return p + 1;
454                 sgen_get_current_object_ops ()->scan_object ((char*)ptr, queue);
455                 return p + 1;
456         case REMSET_VTYPE: {
457                 size_t skip_size;
458
459                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
460                 if (((void*)ptr >= start_nursery && (void*)ptr < end_nursery))
461                         return p + 4;
462                 desc = p [1];
463                 count = p [2];
464                 skip_size = p [3];
465                 while (count-- > 0) {
466                         sgen_get_current_object_ops ()->scan_vtype ((char*)ptr, desc, queue);
467                         ptr = (void**)((char*)ptr + skip_size);
468                 }
469                 return p + 4;
470         }
471         default:
472                 g_assert_not_reached ();
473         }
474         return NULL;
475 }
476
477 static void
478 sgen_ssb_begin_scan_remsets (void *start_nursery, void *end_nursery, SgenGrayQueue *queue)
479 {
480         RememberedSet *remset;
481         mword *p, *next_p, *store_pos;
482
483         /* the global one */
484         for (remset = global_remset; remset; remset = remset->next) {
485                 SGEN_LOG (4, "Scanning global remset range: %p-%p, size: %td", remset->data, remset->store_next, remset->store_next - remset->data);
486                 store_pos = remset->data;
487                 for (p = remset->data; p < remset->store_next; p = next_p) {
488                         void **ptr = (void**)p [0];
489
490                         /*Ignore previously processed remset.*/
491                         if (!global_remset_location_was_not_added (ptr)) {
492                                 next_p = p + 1;
493                                 continue;
494                         }
495
496                         next_p = handle_remset (p, start_nursery, end_nursery, TRUE, queue);
497
498                         /* 
499                          * Clear global remsets of locations which no longer point to the 
500                          * nursery. Otherwise, they could grow indefinitely between major 
501                          * collections.
502                          *
503                          * Since all global remsets are location remsets, we don't need to unmask the pointer.
504                          */
505                         if (sgen_ptr_in_nursery (*ptr)) {
506                                 *store_pos ++ = p [0];
507                                 HEAVY_STAT (++stat_global_remsets_readded);
508                         }
509                 }
510
511                 /* Truncate the remset */
512                 remset->store_next = store_pos;
513         }
514 }
515
516 static void
517 sgen_ssb_finish_scan_remsets (void *start_nursery, void *end_nursery, SgenGrayQueue *queue)
518 {
519         int i;
520         SgenThreadInfo *info;
521         RememberedSet *remset;
522         GenericStoreRememberedSet *store_remset;
523         mword *p;
524
525 #ifdef HEAVY_STATISTICS
526         remset_stats ();
527 #endif
528
529         /* the generic store ones */
530         store_remset = generic_store_remsets;
531         while (store_remset) {
532                 GenericStoreRememberedSet *next = store_remset->next;
533
534                 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
535                         gpointer addr = store_remset->data [i];
536                         if (addr)
537                                 handle_remset ((mword*)&addr, start_nursery, end_nursery, FALSE, queue);
538                 }
539
540                 sgen_free_internal (store_remset, INTERNAL_MEM_STORE_REMSET);
541
542                 store_remset = next;
543         }
544         generic_store_remsets = NULL;
545
546         /* the per-thread ones */
547         FOREACH_THREAD (info) {
548                 RememberedSet *next;
549                 int j;
550                 for (remset = info->remset; remset; remset = next) {
551                         SGEN_LOG (4, "Scanning remset for thread %p, range: %p-%p, size: %td", info, remset->data, remset->store_next, remset->store_next - remset->data);
552                         for (p = remset->data; p < remset->store_next;)
553                                 p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
554                         remset->store_next = remset->data;
555                         next = remset->next;
556                         remset->next = NULL;
557                         if (remset != info->remset) {
558                                 SGEN_LOG (4, "Freed remset at %p", remset->data);
559                                 sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
560                         }
561                 }
562                 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j)
563                         handle_remset ((mword*)*info->store_remset_buffer_addr + j + 1, start_nursery, end_nursery, FALSE, queue);
564                 clear_thread_store_remset_buffer (info);
565         } END_FOREACH_THREAD
566
567         /* the freed thread ones */
568         while (freed_thread_remsets) {
569                 RememberedSet *next;
570                 remset = freed_thread_remsets;
571                 SGEN_LOG (4, "Scanning remset for freed thread, range: %p-%p, size: %td", remset->data, remset->store_next, remset->store_next - remset->data);
572                 for (p = remset->data; p < remset->store_next;)
573                         p = handle_remset (p, start_nursery, end_nursery, FALSE, queue);
574                 next = remset->next;
575                 SGEN_LOG (4, "Freed remset at %p", remset->data);
576                 sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
577                 freed_thread_remsets = next;
578         }
579 }
580
581
582 static void
583 sgen_ssb_cleanup_thread (SgenThreadInfo *p)
584 {
585         RememberedSet *rset;
586
587         if (p->remset) {
588                 if (freed_thread_remsets) {
589                         for (rset = p->remset; rset->next; rset = rset->next)
590                                 ;
591                         rset->next = freed_thread_remsets;
592                         freed_thread_remsets = p->remset;
593                 } else {
594                         freed_thread_remsets = p->remset;
595                 }
596         }
597
598         if (*p->store_remset_buffer_index_addr)
599                 add_generic_store_remset_from_buffer (*p->store_remset_buffer_addr);
600         sgen_free_internal (*p->store_remset_buffer_addr, INTERNAL_MEM_STORE_REMSET);
601
602         /*
603          * This is currently not strictly required, but we do it
604          * anyway in case we change thread unregistering:
605
606          * If the thread is removed from the thread list after
607          * unregistering (this is currently not the case), and a
608          * collection occurs, clear_remsets() would want to memset
609          * this buffer, which would either clobber memory or crash.
610          */
611         *p->store_remset_buffer_addr = NULL;
612 }
613
614 static void
615 sgen_ssb_register_thread (SgenThreadInfo *info)
616 {
617 #ifndef HAVE_KW_THREAD
618         SgenThreadInfo *__thread_info__ = info;
619 #endif
620
621         info->remset = sgen_alloc_remset (DEFAULT_REMSET_SIZE, info, FALSE);
622         mono_native_tls_set_value (remembered_set_key, info->remset);
623 #ifdef HAVE_KW_THREAD
624         remembered_set = info->remset;
625 #endif
626
627         STORE_REMSET_BUFFER = sgen_alloc_internal (INTERNAL_MEM_STORE_REMSET);
628         STORE_REMSET_BUFFER_INDEX = 0;
629 }
630
631 #ifdef HAVE_KW_THREAD
632 static void
633 sgen_ssb_fill_thread_info_for_suspend (SgenThreadInfo *info)
634 {
635         /* update the remset info in the thread data structure */
636         info->remset = remembered_set;
637 }
638 #endif
639
640 static void
641 sgen_ssb_prepare_for_minor_collection (void)
642 {
643         memset (global_remset_cache, 0, sizeof (global_remset_cache));
644 }
645
646 /*
647  * Clear the info in the remembered sets: we're doing a major collection, so
648  * the per-thread ones are not needed and the global ones will be reconstructed
649  * during the copy.
650  */
651 static void
652 sgen_ssb_prepare_for_major_collection (void)
653 {
654         SgenThreadInfo *info;
655         RememberedSet *remset, *next;
656         
657         sgen_ssb_prepare_for_minor_collection ();
658
659         /* the global list */
660         for (remset = global_remset; remset; remset = next) {
661                 remset->store_next = remset->data;
662                 next = remset->next;
663                 remset->next = NULL;
664                 if (remset != global_remset) {
665                         SGEN_LOG (4, "Freed remset at %p", remset->data);
666                         sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
667                 }
668         }
669         /* the generic store ones */
670         while (generic_store_remsets) {
671                 GenericStoreRememberedSet *gs_next = generic_store_remsets->next;
672                 sgen_free_internal (generic_store_remsets, INTERNAL_MEM_STORE_REMSET);
673                 generic_store_remsets = gs_next;
674         }
675         /* the per-thread ones */
676         FOREACH_THREAD (info) {
677                 for (remset = info->remset; remset; remset = next) {
678                         remset->store_next = remset->data;
679                         next = remset->next;
680                         remset->next = NULL;
681                         if (remset != info->remset) {
682                                 SGEN_LOG (3, "Freed remset at %p", remset->data);
683                                 sgen_free_internal_dynamic (remset, remset_byte_size (remset), INTERNAL_MEM_REMSET);
684                         }
685                 }
686                 clear_thread_store_remset_buffer (info);
687         } END_FOREACH_THREAD
688
689         /* the freed thread ones */
690         while (freed_thread_remsets) {
691                 next = freed_thread_remsets->next;
692                 SGEN_LOG (4, "Freed remset at %p", freed_thread_remsets->data);
693                 sgen_free_internal_dynamic (freed_thread_remsets, remset_byte_size (freed_thread_remsets), INTERNAL_MEM_REMSET);
694                 freed_thread_remsets = next;
695         }
696 }
697
698
699 /*
700  * Tries to check if a given remset location was already added to the global remset.
701  * It can
702  *
703  * A 2 entry, LRU cache of recently saw location remsets.
704  *
705  * It's hand-coded instead of done using loops to reduce the number of memory references on cache hit.
706  *
707  * Returns TRUE is the element was added..
708  */
709 static gboolean
710 global_remset_location_was_not_added (gpointer ptr)
711 {
712
713         gpointer first = global_remset_cache [0], second;
714         if (first == ptr) {
715                 HEAVY_STAT (++stat_global_remsets_discarded);
716                 return FALSE;
717         }
718
719         second = global_remset_cache [1];
720
721         if (second == ptr) {
722                 /*Move the second to the front*/
723                 global_remset_cache [0] = second;
724                 global_remset_cache [1] = first;
725
726                 HEAVY_STAT (++stat_global_remsets_discarded);
727                 return FALSE;
728         }
729
730         global_remset_cache [0] = second;
731         global_remset_cache [1] = ptr;
732         return TRUE;
733 }
734
735 static void
736 sgen_ssb_record_pointer (gpointer ptr)
737 {
738         RememberedSet *rs;
739         gboolean lock = sgen_collection_is_parallel ();
740         gpointer obj = *(gpointer*)ptr;
741
742         g_assert (!sgen_ptr_in_nursery (ptr) && sgen_ptr_in_nursery (obj));
743
744         if (lock)
745                 LOCK_GLOBAL_REMSET;
746
747         if (!global_remset_location_was_not_added (ptr))
748                 goto done;
749
750         /* 
751          * FIXME: If an object remains pinned, we need to add it at every minor collection.
752          * To avoid uncontrolled growth of the global remset, only add each pointer once.
753          */
754         if (global_remset->store_next + 3 < global_remset->end_set) {
755                 *(global_remset->store_next++) = (mword)ptr;
756                 goto done;
757         }
758         rs = sgen_alloc_remset (global_remset->end_set - global_remset->data, NULL, TRUE);
759         rs->next = global_remset;
760         global_remset = rs;
761         *(global_remset->store_next++) = (mword)ptr;
762
763 #if SGEN_MAX_DEBUG_LEVEL >= 4
764         {
765                 int global_rs_size = 0;
766
767                 for (rs = global_remset; rs; rs = rs->next) {
768                         global_rs_size += rs->store_next - rs->data;
769                 }
770                 SGEN_LOG (4, "Global remset now has size %d", global_rs_size);
771         }
772 #endif
773
774  done:
775         if (lock)
776                 UNLOCK_GLOBAL_REMSET;
777 }
778
779 /*
780  * ######################################################################
781  * ########  Debug support
782  * ######################################################################
783  */
784
785 static mword*
786 find_in_remset_loc (mword *p, char *addr, gboolean *found)
787 {
788         void **ptr;
789         mword count, desc;
790         size_t skip_size;
791
792         switch ((*p) & REMSET_TYPE_MASK) {
793         case REMSET_LOCATION:
794                 if (*p == (mword)addr)
795                         *found = TRUE;
796                 return p + 1;
797         case REMSET_RANGE:
798                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
799                 count = p [1];
800                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
801                         *found = TRUE;
802                 return p + 2;
803         case REMSET_OBJECT:
804                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
805                 count = sgen_safe_object_get_size ((MonoObject*)ptr); 
806                 count = SGEN_ALIGN_UP (count);
807                 count /= sizeof (mword);
808                 if ((void**)addr >= ptr && (void**)addr < ptr + count)
809                         *found = TRUE;
810                 return p + 1;
811         case REMSET_VTYPE:
812                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
813                 desc = p [1];
814                 count = p [2];
815                 skip_size = p [3];
816
817                 /* The descriptor includes the size of MonoObject */
818                 skip_size -= sizeof (MonoObject);
819                 skip_size *= count;
820                 if ((void**)addr >= ptr && (void**)addr < ptr + (skip_size / sizeof (gpointer)))
821                         *found = TRUE;
822
823                 return p + 4;
824         default:
825                 g_assert_not_reached ();
826         }
827         return NULL;
828 }
829 /*
830  * Return whenever ADDR occurs in the remembered sets
831  */
832 static gboolean
833 sgen_ssb_find_address (char *addr)
834 {
835         int i;
836         SgenThreadInfo *info;
837         RememberedSet *remset;
838         GenericStoreRememberedSet *store_remset;
839         mword *p;
840         gboolean found = FALSE;
841
842         /* the global one */
843         for (remset = global_remset; remset; remset = remset->next) {
844                 SGEN_LOG (4, "Scanning global remset range: %p-%p, size: %td", remset->data, remset->store_next, remset->store_next - remset->data);
845                 for (p = remset->data; p < remset->store_next;) {
846                         p = find_in_remset_loc (p, addr, &found);
847                         if (found)
848                                 return TRUE;
849                 }
850         }
851
852         /* the generic store ones */
853         for (store_remset = generic_store_remsets; store_remset; store_remset = store_remset->next) {
854                 for (i = 0; i < STORE_REMSET_BUFFER_SIZE - 1; ++i) {
855                         if (store_remset->data [i] == addr)
856                                 return TRUE;
857                 }
858         }
859
860         /* the per-thread ones */
861         FOREACH_THREAD (info) {
862                 int j;
863                 for (remset = info->remset; remset; remset = remset->next) {
864                         SGEN_LOG (4, "Scanning remset for thread %p, range: %p-%p, size: %td", info, remset->data, remset->store_next, remset->store_next - remset->data);
865                         for (p = remset->data; p < remset->store_next;) {
866                                 p = find_in_remset_loc (p, addr, &found);
867                                 if (found)
868                                         return TRUE;
869                         }
870                 }
871                 for (j = 0; j < *info->store_remset_buffer_index_addr; ++j) {
872                         if ((*info->store_remset_buffer_addr) [j + 1] == addr)
873                                 return TRUE;
874                 }
875         } END_FOREACH_THREAD
876
877         /* the freed thread ones */
878         for (remset = freed_thread_remsets; remset; remset = remset->next) {
879                 SGEN_LOG (4, "Scanning remset for freed thread, range: %p-%p, size: %td", remset->data, remset->store_next, remset->store_next - remset->data);
880                 for (p = remset->data; p < remset->store_next;) {
881                         p = find_in_remset_loc (p, addr, &found);
882                         if (found)
883                                 return TRUE;
884                 }
885         }
886
887         return FALSE;
888 }
889
890 void
891 sgen_ssb_init (SgenRemeberedSet *remset)
892 {
893         LOCK_INIT (global_remset_mutex);
894
895         global_remset = sgen_alloc_remset (1024, NULL, FALSE);
896         global_remset->next = NULL;
897
898         mono_native_tls_alloc (&remembered_set_key, NULL);
899
900 #ifdef HEAVY_STATISTICS
901         mono_counters_register ("WBarrier generic store stored", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wbarrier_generic_store_remset);
902
903         mono_counters_register ("Store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets);
904         mono_counters_register ("Unique store remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_store_remsets_unique);
905         mono_counters_register ("Saved remsets 1", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_1);
906         mono_counters_register ("Saved remsets 2", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_saved_remsets_2);
907         mono_counters_register ("Non-global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_local_remsets_processed);
908         mono_counters_register ("Global remsets added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_added);
909         mono_counters_register ("Global remsets re-added", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_readded);
910         mono_counters_register ("Global remsets processed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_processed);
911         mono_counters_register ("Global remsets discarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_global_remsets_discarded);
912 #endif
913
914         remset->wbarrier_set_field = sgen_ssb_wbarrier_set_field;
915         remset->wbarrier_set_arrayref = sgen_ssb_wbarrier_set_arrayref;
916         remset->wbarrier_arrayref_copy = sgen_ssb_wbarrier_arrayref_copy;
917         remset->wbarrier_value_copy = sgen_ssb_wbarrier_value_copy;
918         remset->wbarrier_object_copy = sgen_ssb_wbarrier_object_copy;
919         remset->wbarrier_generic_nostore = sgen_ssb_wbarrier_generic_nostore;
920         remset->record_pointer = sgen_ssb_record_pointer;
921
922         remset->begin_scan_remsets = sgen_ssb_begin_scan_remsets;
923         remset->finish_scan_remsets = sgen_ssb_finish_scan_remsets;
924
925         remset->register_thread = sgen_ssb_register_thread;
926         remset->cleanup_thread = sgen_ssb_cleanup_thread;
927 #ifdef HAVE_KW_THREAD
928         remset->fill_thread_info_for_suspend = sgen_ssb_fill_thread_info_for_suspend;
929 #endif
930
931         remset->prepare_for_minor_collection = sgen_ssb_prepare_for_minor_collection;
932         remset->prepare_for_major_collection = sgen_ssb_prepare_for_major_collection;
933
934         remset->find_address = sgen_ssb_find_address;
935 }
936
937 #else
938
939 void
940 sgen_ssb_init (SgenRemeberedSet *remset)
941 {
942         fprintf (stderr, "Error: Mono was configured using --enable-minimal=sgen_wbarrier.\n");
943         exit (1);
944 }
945
946 #endif /* DISABLE_SGEN_REMSET */
947
948 #endif /* HAVE_SGEN_GC */