Merge pull request #496 from nicolas-raoul/unit-test-for-issue2907
[mono.git] / mono / metadata / sgen-descriptor.c
1 /*
2  * sgen-descriptor.c: GC descriptors describe object layout.
3  *
4  * Copyright 2001-2003 Ximian, Inc
5  * Copyright 2003-2010 Novell, Inc.
6  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
7  * Copyright (C) 2012 Xamarin Inc
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License 2.0 as published by the Free Software Foundation;
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License 2.0 along with this library; if not, write to the Free
20  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22 #include "config.h"
23 #ifdef HAVE_SGEN_GC
24
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #ifdef HAVE_PTHREAD_H
29 #include <pthread.h>
30 #endif
31 #ifdef HAVE_SEMAPHORE_H
32 #include <semaphore.h>
33 #endif
34 #include <stdio.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <errno.h>
38 #include <assert.h>
39 #ifdef __MACH__
40 #undef _XOPEN_SOURCE
41 #endif
42 #ifdef __MACH__
43 #define _XOPEN_SOURCE
44 #endif
45
46 #include "metadata/sgen-gc.h"
47
48 #define MAX_USER_DESCRIPTORS 16
49
50 #define MAKE_ROOT_DESC(type,val) ((type) | ((val) << ROOT_DESC_TYPE_SHIFT))
51 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
52
53
54 static gsize* complex_descriptors = NULL;
55 static int complex_descriptors_size = 0;
56 static int complex_descriptors_next = 0;
57 static MonoGCRootMarkFunc user_descriptors [MAX_USER_DESCRIPTORS];
58 static int user_descriptors_next = 0;
59 static void *all_ref_root_descrs [32];
60
61
62 static int
63 alloc_complex_descriptor (gsize *bitmap, int numbits)
64 {
65         int nwords, res, i;
66
67         numbits = ALIGN_TO (numbits, GC_BITS_PER_WORD);
68         nwords = numbits / GC_BITS_PER_WORD + 1;
69
70         sgen_gc_lock ();
71         res = complex_descriptors_next;
72         /* linear search, so we don't have duplicates with domain load/unload
73          * this should not be performance critical or we'd have bigger issues
74          * (the number and size of complex descriptors should be small).
75          */
76         for (i = 0; i < complex_descriptors_next; ) {
77                 if (complex_descriptors [i] == nwords) {
78                         int j, found = TRUE;
79                         for (j = 0; j < nwords - 1; ++j) {
80                                 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
81                                         found = FALSE;
82                                         break;
83                                 }
84                         }
85                         if (found) {
86                                 sgen_gc_unlock ();
87                                 return i;
88                         }
89                 }
90                 i += complex_descriptors [i];
91         }
92         if (complex_descriptors_next + nwords > complex_descriptors_size) {
93                 int new_size = complex_descriptors_size * 2 + nwords;
94                 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
95                 complex_descriptors_size = new_size;
96         }
97         SGEN_LOG (6, "Complex descriptor %d, size: %d (total desc memory: %d)", res, nwords, complex_descriptors_size);
98         complex_descriptors_next += nwords;
99         complex_descriptors [res] = nwords;
100         for (i = 0; i < nwords - 1; ++i) {
101                 complex_descriptors [res + 1 + i] = bitmap [i];
102                 SGEN_LOG (6, "\tvalue: %p", (void*)complex_descriptors [res + 1 + i]);
103         }
104         sgen_gc_unlock ();
105         return res;
106 }
107
108 gsize*
109 sgen_get_complex_descriptor (mword desc)
110 {
111         return complex_descriptors + (desc >> LOW_TYPE_BITS);
112 }
113
114 /*
115  * Descriptor builders.
116  */
117 void*
118 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
119 {
120         return (void*) DESC_TYPE_RUN_LENGTH;
121 }
122
123 void*
124 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
125 {
126         int first_set = -1, num_set = 0, last_set = -1, i;
127         mword desc = 0;
128         size_t stored_size = obj_size;
129
130         stored_size += SGEN_ALLOC_ALIGN - 1;
131         stored_size &= ~(SGEN_ALLOC_ALIGN - 1);
132
133         for (i = 0; i < numbits; ++i) {
134                 if (bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
135                         if (first_set < 0)
136                                 first_set = i;
137                         last_set = i;
138                         num_set++;
139                 }
140         }
141
142         if (first_set < 0) {
143                 SGEN_LOG (6, "Ptrfree descriptor %p, size: %zd", (void*)desc, stored_size);
144                 if (stored_size <= MAX_RUNLEN_OBJECT_SIZE)
145                         return (void*)(DESC_TYPE_RUN_LENGTH | stored_size);
146                 return (void*)DESC_TYPE_COMPLEX_PTRFREE;
147         }
148
149         g_assert (!(stored_size & 0x7));
150
151         if (stored_size <= MAX_RUNLEN_OBJECT_SIZE) {
152                 /* check run-length encoding first: one byte offset, one byte number of pointers
153                  * on 64 bit archs, we can have 3 runs, just one on 32.
154                  * It may be better to use nibbles.
155                  */
156                 if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
157                         desc = DESC_TYPE_RUN_LENGTH | stored_size | (first_set << 16) | (num_set << 24);
158                         SGEN_LOG (6, "Runlen descriptor %p, size: %zd, first set: %d, num set: %d", (void*)desc, stored_size, first_set, num_set);
159                         return (void*) desc;
160                 }
161         }
162
163         /* we know the 2-word header is ptr-free */
164         if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
165                 desc = DESC_TYPE_SMALL_BITMAP | stored_size | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
166                 SGEN_LOG (6, "Smallbitmap descriptor %p, size: %zd, last set: %d", (void*)desc, stored_size, last_set);
167                 return (void*) desc;
168         }
169
170         /* we know the 2-word header is ptr-free */
171         if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
172                 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
173                 SGEN_LOG (6, "Largebitmap descriptor %p, size: %zd, last set: %d", (void*)desc, stored_size, last_set);
174                 return (void*) desc;
175         }
176         /* it's a complex object ... */
177         desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
178         return (void*) desc;
179 }
180
181 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
182 void*
183 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
184 {
185         int first_set = -1, num_set = 0, last_set = -1, i;
186         mword desc = DESC_TYPE_VECTOR | (vector ? VECTOR_KIND_SZARRAY : VECTOR_KIND_ARRAY);
187         for (i = 0; i < numbits; ++i) {
188                 if (elem_bitmap [i / GC_BITS_PER_WORD] & ((gsize)1 << (i % GC_BITS_PER_WORD))) {
189                         if (first_set < 0)
190                                 first_set = i;
191                         last_set = i;
192                         num_set++;
193                 }
194         }
195
196         if (first_set < 0) {
197                 if (elem_size <= MAX_ELEMENT_SIZE)
198                         return (void*)(desc | VECTOR_SUBTYPE_PTRFREE | (elem_size << VECTOR_ELSIZE_SHIFT));
199                 return (void*)DESC_TYPE_COMPLEX_PTRFREE;
200         }
201
202         if (elem_size <= MAX_ELEMENT_SIZE) {
203                 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
204                 if (!num_set) {
205                         return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
206                 }
207                 /* Note: we also handle structs with just ref fields */
208                 if (num_set * sizeof (gpointer) == elem_size) {
209                         return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
210                 }
211                 /* FIXME: try run-len first */
212                 /* Note: we can't skip the object header here, because it's not present */
213                 if (last_set <= SMALL_BITMAP_SIZE) {
214                         return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
215                 }
216         }
217         /* it's am array of complex structs ... */
218         desc = DESC_TYPE_COMPLEX_ARR;
219         desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
220         return (void*) desc;
221 }
222
223 /* Return the bitmap encoded by a descriptor */
224 gsize*
225 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
226 {
227         mword d = (mword)descr;
228         gsize *bitmap;
229
230         switch (d & 0x7) {
231         case DESC_TYPE_RUN_LENGTH: {            
232                 int first_set = (d >> 16) & 0xff;
233                 int num_set = (d >> 24) & 0xff;
234                 int i;
235
236                 bitmap = g_new0 (gsize, (first_set + num_set + 7) / 8);
237
238                 for (i = first_set; i < first_set + num_set; ++i)
239                         bitmap [i / GC_BITS_PER_WORD] |= ((gsize)1 << (i % GC_BITS_PER_WORD));
240
241                 *numbits = first_set + num_set;
242
243                 return bitmap;
244         }
245
246         case DESC_TYPE_SMALL_BITMAP:
247                 bitmap = g_new0 (gsize, 1);
248
249                 bitmap [0] = (d >> SMALL_BITMAP_SHIFT) << OBJECT_HEADER_WORDS;
250
251                 *numbits = GC_BITS_PER_WORD;
252                 return bitmap;
253
254         case DESC_TYPE_LARGE_BITMAP: {
255                 gsize bmap = (d >> LOW_TYPE_BITS) << OBJECT_HEADER_WORDS;
256
257                 bitmap = g_new0 (gsize, 1);
258                 bitmap [0] = bmap;
259                 *numbits = 0;
260                 while (bmap) {
261                         (*numbits) ++;
262                         bmap >>= 1;
263                 }
264                 return bitmap;
265         }
266
267         case DESC_TYPE_COMPLEX: {
268                 gsize *bitmap_data = sgen_get_complex_descriptor (d);
269                 int bwords = (*bitmap_data) - 1;
270                 int i;
271
272                 bitmap = g_new0 (gsize, bwords);
273                 *numbits = bwords * GC_BITS_PER_WORD;
274
275                 for (i = 0; i < bwords; ++i) {
276                         bitmap [i] = bitmap_data [i + 1];
277                 }
278
279                 return bitmap;
280         }
281
282         default:
283                 g_assert_not_reached ();
284         }
285 }
286
287 void*
288 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
289 {
290         if (numbits == 0) {
291                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, 0);
292         } else if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
293                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_BITMAP, bitmap [0]);
294         } else {
295                 mword complex = alloc_complex_descriptor (bitmap, numbits);
296                 return (void*)MAKE_ROOT_DESC (ROOT_DESC_COMPLEX, complex);
297         }
298 }
299
300 void*
301 mono_gc_make_root_descr_all_refs (int numbits)
302 {
303         gsize *gc_bitmap;
304         void *descr;
305         int num_bytes = numbits / 8;
306
307         if (numbits < 32 && all_ref_root_descrs [numbits])
308                 return all_ref_root_descrs [numbits];
309
310         gc_bitmap = g_malloc0 (ALIGN_TO (ALIGN_TO (numbits, 8) + 1, sizeof (gsize)));
311         memset (gc_bitmap, 0xff, num_bytes);
312         if (numbits < ((sizeof (*gc_bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) 
313                 gc_bitmap[0] = GUINT64_TO_LE(gc_bitmap[0]);
314         else if (numbits && num_bytes % (sizeof (*gc_bitmap)))
315                 gc_bitmap[num_bytes / 8] = GUINT64_TO_LE(gc_bitmap [num_bytes / 8]);
316         if (numbits % 8)
317                 gc_bitmap [numbits / 8] = (1 << (numbits % 8)) - 1;
318         descr = mono_gc_make_descr_from_bitmap (gc_bitmap, numbits);
319         g_free (gc_bitmap);
320
321         if (numbits < 32)
322                 all_ref_root_descrs [numbits] = descr;
323
324         return descr;
325 }
326
327 void*
328 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
329 {
330         void *descr;
331
332         g_assert (user_descriptors_next < MAX_USER_DESCRIPTORS);
333         descr = (void*)MAKE_ROOT_DESC (ROOT_DESC_USER, (mword)user_descriptors_next);
334         user_descriptors [user_descriptors_next ++] = marker;
335
336         return descr;
337 }
338
339 void*
340 sgen_get_complex_descriptor_bitmap (mword desc)
341 {
342         return complex_descriptors + (desc >> ROOT_DESC_TYPE_SHIFT);
343 }
344
345 MonoGCRootMarkFunc
346 sgen_get_user_descriptor_func (mword desc)
347 {
348         return user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
349 }
350
351 #endif