OSDN Git Service

PR rtl-optimization/42621
[pf3gnuchains/gcc-fork.git] / libobjc / sarray.c
1 /* Sparse Arrays for Objective C dispatch tables
2    Copyright (C) 1993, 1995, 1996, 2002, 2004, 2009 Free Software Foundation, Inc.
3
4 This file is part of GCC.
5
6 GCC is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10
11 GCC is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 Under Section 7 of GPL version 3, you are granted additional
17 permissions described in the GCC Runtime Library Exception, version
18 3.1, as published by the Free Software Foundation.
19
20 You should have received a copy of the GNU General Public License and
21 a copy of the GCC Runtime Library Exception along with this program;
22 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23 <http://www.gnu.org/licenses/>.  */
24
25
26 #include "objc/sarray.h"
27 #include "objc/runtime.h"
28 #include <stdio.h>
29 #include "assert.h"
30
31 int nbuckets = 0;                                       /* !T:MUTEX */
32 int nindices = 0;                                       /* !T:MUTEX */
33 int narrays = 0;                                        /* !T:MUTEX */
34 int idxsize = 0;                                        /* !T:MUTEX */
35
36 static void *first_free_data = NULL;                    /* !T:MUTEX */
37
38 #ifdef OBJC_SPARSE2
39 const char *__objc_sparse2_id = "2 level sparse indices";
40 #endif
41
42 #ifdef OBJC_SPARSE3
43 const char *__objc_sparse3_id = "3 level sparse indices";
44 #endif
45
46 /* This function removes any structures left over from free operations
47    that were not safe in a multi-threaded environment. */
48 void
49 sarray_remove_garbage (void)
50 {
51   void **vp;
52   void *np;
53   
54   objc_mutex_lock (__objc_runtime_mutex);
55
56   vp = first_free_data;
57   first_free_data = NULL;
58
59   while (vp) {
60     np = *vp;
61     objc_free (vp);
62     vp = np;
63   }
64   
65   objc_mutex_unlock (__objc_runtime_mutex);
66 }
67
68 /* Free a block of dynamically allocated memory.  If we are in multi-threaded
69    mode, it is ok to free it.  If not, we add it to the garbage heap to be
70    freed later. */
71
72 static void
73 sarray_free_garbage (void *vp)
74 {
75   objc_mutex_lock (__objc_runtime_mutex);
76   
77   if (__objc_runtime_threads_alive == 1) {
78     objc_free (vp);
79     if (first_free_data)
80       sarray_remove_garbage ();
81   }
82   else {
83     *(void **)vp = first_free_data;
84     first_free_data = vp;
85   }
86       
87   objc_mutex_unlock (__objc_runtime_mutex);
88 }
89
90 /* sarray_at_put : copies data in such a way as to be thread reader safe. */
91 void
92 sarray_at_put (struct sarray *array, sidx index, void *element)
93 {
94 #ifdef OBJC_SPARSE3
95   struct sindex **the_index;
96   struct sindex *new_index;
97 #endif
98   struct sbucket **the_bucket;
99   struct sbucket *new_bucket;
100 #ifdef OBJC_SPARSE3
101   size_t ioffset;
102 #endif
103   size_t boffset;
104   size_t eoffset;
105 #ifdef PRECOMPUTE_SELECTORS
106   union sofftype xx; 
107   xx.idx = index;
108 #ifdef OBJC_SPARSE3
109   ioffset = xx.off.ioffset;
110 #endif
111   boffset = xx.off.boffset;
112   eoffset = xx.off.eoffset;
113 #else /* not PRECOMPUTE_SELECTORS */
114 #ifdef OBJC_SPARSE3
115   ioffset = index/INDEX_CAPACITY;
116   boffset = (index/BUCKET_SIZE)%INDEX_SIZE;
117   eoffset = index%BUCKET_SIZE;
118 #else
119   boffset = index/BUCKET_SIZE;
120   eoffset = index%BUCKET_SIZE;
121 #endif
122 #endif /* not PRECOMPUTE_SELECTORS */
123
124   assert (soffset_decode (index) < array->capacity); /* Range check */
125
126 #ifdef OBJC_SPARSE3
127   the_index = &(array->indices[ioffset]);
128   the_bucket = &((*the_index)->buckets[boffset]);
129 #else
130   the_bucket = &(array->buckets[boffset]);
131 #endif
132   
133   if ((*the_bucket)->elems[eoffset] == element)
134     return;             /* great! we just avoided a lazy copy */
135
136 #ifdef OBJC_SPARSE3
137
138   /* First, perform lazy copy/allocation of index if needed */
139
140   if ((*the_index) == array->empty_index) {
141
142     /* The index was previously empty, allocate a new */
143     new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
144     memcpy (new_index, array->empty_index, sizeof (struct sindex));
145     new_index->version.version = array->version.version;
146     *the_index = new_index;                     /* Prepared for install. */
147     the_bucket = &((*the_index)->buckets[boffset]);
148     
149     nindices += 1;
150   } else if ((*the_index)->version.version != array->version.version) {
151
152     /* This index must be lazy copied */
153     struct sindex *old_index = *the_index;
154     new_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
155     memcpy (new_index, old_index, sizeof (struct sindex));
156     new_index->version.version = array->version.version;
157     *the_index = new_index;                     /* Prepared for install. */
158     the_bucket = &((*the_index)->buckets[boffset]);
159     
160     nindices += 1;
161   }
162
163 #endif /* OBJC_SPARSE3 */
164
165   /* next, perform lazy allocation/copy of the bucket if needed */
166
167   if ((*the_bucket) == array->empty_bucket) {
168
169     /* The bucket was previously empty (or something like that), */
170     /* allocate a new.  This is the effect of `lazy' allocation */  
171     new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
172     memcpy ((void *) new_bucket, (const void *) array->empty_bucket, 
173             sizeof (struct sbucket));
174     new_bucket->version.version = array->version.version;
175     *the_bucket = new_bucket;                   /* Prepared for install. */
176     
177     nbuckets += 1;
178
179   } else if ((*the_bucket)->version.version != array->version.version) {
180
181     /* Perform lazy copy. */
182     struct sbucket *old_bucket = *the_bucket;
183     new_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
184     memcpy (new_bucket, old_bucket, sizeof (struct sbucket));
185     new_bucket->version.version = array->version.version;
186     *the_bucket = new_bucket;                   /* Prepared for install. */
187     
188     nbuckets += 1;
189
190   }
191   (*the_bucket)->elems[eoffset] = element;
192 }
193
194 void
195 sarray_at_put_safe (struct sarray *array, sidx index, void *element)
196 {
197   if (soffset_decode (index) >= array->capacity)
198     sarray_realloc (array, soffset_decode (index) + 1);
199   sarray_at_put (array, index, element);
200 }
201
202 struct sarray *
203 sarray_new (int size, void *default_element)
204 {
205   struct sarray *arr;
206 #ifdef OBJC_SPARSE3
207   size_t num_indices = ((size - 1)/(INDEX_CAPACITY)) + 1;
208   struct sindex **new_indices;
209 #else /* OBJC_SPARSE2 */
210   size_t num_indices = ((size - 1)/BUCKET_SIZE) + 1;
211   struct sbucket **new_buckets;
212 #endif
213   size_t counter;
214
215   assert (size > 0);
216
217   /* Allocate core array */
218   arr = (struct sarray *) objc_malloc (sizeof (struct sarray));
219   arr->version.version = 0;
220   
221   /* Initialize members */
222 #ifdef OBJC_SPARSE3
223   arr->capacity = num_indices*INDEX_CAPACITY;
224   new_indices = (struct sindex **) 
225     objc_malloc (sizeof (struct sindex *) * num_indices);
226
227   arr->empty_index = (struct sindex *) objc_malloc (sizeof (struct sindex));
228   arr->empty_index->version.version = 0;
229   
230   narrays  += 1;
231   idxsize  += num_indices;
232   nindices += 1;
233
234 #else /* OBJC_SPARSE2 */
235   arr->capacity = num_indices*BUCKET_SIZE;
236   new_buckets = (struct sbucket **) 
237     objc_malloc (sizeof (struct sbucket *) * num_indices);
238   
239   narrays  += 1;
240   idxsize  += num_indices;
241
242 #endif
243
244   arr->empty_bucket = (struct sbucket *) objc_malloc (sizeof (struct sbucket));
245   arr->empty_bucket->version.version = 0;
246   
247   nbuckets += 1;
248
249   arr->ref_count = 1;
250   arr->is_copy_of = (struct sarray *) 0;
251   
252   for (counter = 0; counter < BUCKET_SIZE; counter++)
253     arr->empty_bucket->elems[counter] = default_element;
254
255 #ifdef OBJC_SPARSE3
256   for (counter = 0; counter < INDEX_SIZE; counter++)
257     arr->empty_index->buckets[counter] = arr->empty_bucket;
258
259   for (counter = 0; counter < num_indices; counter++)
260     new_indices[counter] = arr->empty_index;
261
262 #else /* OBJC_SPARSE2 */
263
264   for (counter = 0; counter < num_indices; counter++)
265     new_buckets[counter] = arr->empty_bucket;
266
267 #endif
268   
269 #ifdef OBJC_SPARSE3
270   arr->indices = new_indices;
271 #else /* OBJC_SPARSE2 */
272   arr->buckets = new_buckets;
273 #endif
274   
275   return arr;
276 }
277 \f
278
279 /* Reallocate the sparse array to hold `newsize' entries
280    Note: We really allocate and then free.  We have to do this to ensure that
281    any concurrent readers notice the update. */
282
283 void 
284 sarray_realloc (struct sarray *array, int newsize)
285 {
286 #ifdef OBJC_SPARSE3
287   size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
288   size_t new_max_index = ((newsize - 1)/INDEX_CAPACITY);
289   size_t rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
290
291   struct sindex **new_indices;
292   struct sindex **old_indices;
293   
294 #else /* OBJC_SPARSE2 */
295   size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
296   size_t new_max_index = ((newsize - 1)/BUCKET_SIZE);
297   size_t rounded_size = (new_max_index + 1) * BUCKET_SIZE;
298
299   struct sbucket **new_buckets;
300   struct sbucket **old_buckets;
301   
302 #endif
303
304   size_t counter;
305
306   assert (newsize > 0);
307
308   /* The size is the same, just ignore the request */
309   if (rounded_size <= array->capacity)
310     return;
311
312   assert (array->ref_count == 1);       /* stop if lazy copied... */
313
314   /* We are asked to extend the array -- allocate new bucket table, */
315   /* and insert empty_bucket in newly allocated places. */
316   if (rounded_size > array->capacity) 
317     {
318
319 #ifdef OBJC_SPARSE3
320       new_max_index += 4;
321       rounded_size = (new_max_index + 1) * INDEX_CAPACITY;
322       
323 #else /* OBJC_SPARSE2 */
324       new_max_index += 4;
325       rounded_size = (new_max_index + 1) * BUCKET_SIZE;
326 #endif
327       
328       /* update capacity */
329       array->capacity = rounded_size;
330
331 #ifdef OBJC_SPARSE3
332       /* alloc to force re-read by any concurrent readers. */
333       old_indices = array->indices;
334       new_indices = (struct sindex **)
335         objc_malloc ((new_max_index + 1) * sizeof (struct sindex *));
336 #else /* OBJC_SPARSE2 */
337       old_buckets = array->buckets;
338       new_buckets = (struct sbucket **)
339         objc_malloc ((new_max_index + 1) * sizeof (struct sbucket *));
340 #endif
341
342       /* copy buckets below old_max_index (they are still valid) */
343       for (counter = 0; counter <= old_max_index; counter++ ) {
344 #ifdef OBJC_SPARSE3
345         new_indices[counter] = old_indices[counter];
346 #else /* OBJC_SPARSE2 */
347         new_buckets[counter] = old_buckets[counter];
348 #endif
349       }
350
351 #ifdef OBJC_SPARSE3
352       /* reset entries above old_max_index to empty_bucket */
353       for (counter = old_max_index + 1; counter <= new_max_index; counter++)
354         new_indices[counter] = array->empty_index;
355 #else /* OBJC_SPARSE2 */
356       /* reset entries above old_max_index to empty_bucket */
357       for (counter = old_max_index + 1; counter <= new_max_index; counter++)
358         new_buckets[counter] = array->empty_bucket;
359 #endif
360       
361 #ifdef OBJC_SPARSE3
362       /* install the new indices */
363       array->indices = new_indices;
364 #else /* OBJC_SPARSE2 */
365       array->buckets = new_buckets;
366 #endif
367
368 #ifdef OBJC_SPARSE3
369       /* free the old indices */
370       sarray_free_garbage (old_indices);
371 #else /* OBJC_SPARSE2 */
372       sarray_free_garbage (old_buckets);
373 #endif
374       
375       idxsize += (new_max_index-old_max_index);
376       return;
377     }
378 }
379 \f
380
381 /* Free a sparse array allocated with sarray_new */
382
383 void 
384 sarray_free (struct sarray *array) {
385 #ifdef OBJC_SPARSE3
386   size_t old_max_index = (array->capacity - 1)/INDEX_CAPACITY;
387   struct sindex **old_indices;
388 #else
389   size_t old_max_index = (array->capacity - 1)/BUCKET_SIZE;
390   struct sbucket **old_buckets;
391 #endif
392   size_t counter = 0;
393
394   assert (array->ref_count != 0);       /* Freed multiple times!!! */
395
396   if (--(array->ref_count) != 0)        /* There exists copies of me */
397     return;
398
399 #ifdef OBJC_SPARSE3
400   old_indices = array->indices;
401 #else
402   old_buckets = array->buckets;
403 #endif
404
405   /* Free all entries that do not point to empty_bucket */
406   for (counter = 0; counter <= old_max_index; counter++ ) {
407 #ifdef OBJC_SPARSE3
408     struct sindex *idx = old_indices[counter];
409     if ((idx != array->empty_index) &&
410        (idx->version.version == array->version.version)) {
411       int c2; 
412       for (c2 = 0; c2 < INDEX_SIZE; c2++) {
413         struct sbucket *bkt = idx->buckets[c2];
414         if ((bkt != array->empty_bucket) &&
415            (bkt->version.version == array->version.version))
416           {
417             sarray_free_garbage (bkt);
418             nbuckets -= 1;
419           }
420       }
421       sarray_free_garbage (idx);
422       nindices -= 1;
423     }
424 #else /* OBJC_SPARSE2 */
425     struct sbucket *bkt = old_buckets[counter];
426     if ((bkt != array->empty_bucket) &&
427         (bkt->version.version == array->version.version))
428       {
429         sarray_free_garbage (bkt);
430         nbuckets -= 1;
431       }
432 #endif
433   }
434         
435 #ifdef OBJC_SPARSE3  
436   /* free empty_index */
437   if (array->empty_index->version.version == array->version.version) {
438     sarray_free_garbage (array->empty_index);
439     nindices -= 1;
440   }
441 #endif
442
443   /* free empty_bucket */
444   if (array->empty_bucket->version.version == array->version.version) {
445     sarray_free_garbage (array->empty_bucket);
446     nbuckets -= 1;
447   }
448   idxsize -= (old_max_index + 1);
449   narrays -= 1;
450
451 #ifdef OBJC_SPARSE3
452   /* free bucket table */
453   sarray_free_garbage (array->indices);
454
455 #else
456   /* free bucket table */
457   sarray_free_garbage (array->buckets);
458
459 #endif
460   
461   /* If this is a copy of another array, we free it (which might just
462    * decrement its reference count so it will be freed when no longer in use).
463    */
464   if (array->is_copy_of)
465     sarray_free (array->is_copy_of);
466
467   /* free array */
468   sarray_free_garbage (array);
469 }
470
471 /* This is a lazy copy.  Only the core of the structure is actually */
472 /* copied.   */
473
474 struct sarray *
475 sarray_lazy_copy (struct sarray *oarr)
476 {
477   struct sarray *arr;
478
479 #ifdef OBJC_SPARSE3
480   size_t num_indices = ((oarr->capacity - 1)/INDEX_CAPACITY) + 1;
481   struct sindex **new_indices;
482 #else /* OBJC_SPARSE2 */
483   size_t num_indices = ((oarr->capacity - 1)/BUCKET_SIZE) + 1;
484   struct sbucket **new_buckets;
485 #endif
486
487   /* Allocate core array */
488   arr = (struct sarray *) objc_malloc (sizeof (struct sarray)); /* !!! */
489   arr->version.version = oarr->version.version + 1;
490 #ifdef OBJC_SPARSE3
491   arr->empty_index = oarr->empty_index;
492 #endif
493   arr->empty_bucket = oarr->empty_bucket;
494   arr->ref_count = 1;
495   oarr->ref_count += 1;
496   arr->is_copy_of = oarr;
497   arr->capacity = oarr->capacity;
498   
499 #ifdef OBJC_SPARSE3
500   /* Copy bucket table */
501   new_indices = (struct sindex **) 
502     objc_malloc (sizeof (struct sindex *) * num_indices);
503   memcpy (new_indices, oarr->indices, sizeof (struct sindex *) * num_indices);
504   arr->indices = new_indices;
505 #else 
506   /* Copy bucket table */
507   new_buckets = (struct sbucket **) 
508     objc_malloc (sizeof (struct sbucket *) * num_indices);
509   memcpy (new_buckets, oarr->buckets, sizeof (struct sbucket *) * num_indices);
510   arr->buckets = new_buckets;
511 #endif
512
513   idxsize += num_indices;
514   narrays += 1;
515   
516   return arr;
517 }