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