Remove a note in README.macros that work in progress
[platform/upstream/libgc.git] / headers.c
1 /*
2  * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
3  * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
4  * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
5  *
6  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
7  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
8  *
9  * Permission is hereby granted to use or copy this program
10  * for any purpose,  provided the above notices are retained on all copies.
11  * Permission to modify the code and to distribute modified code is granted,
12  * provided the above notices are retained, and a notice that the code was
13  * modified is included with the above copyright notice.
14  */
15
16 #include "private/gc_priv.h"
17
18 /*
19  * This implements:
20  * 1. allocation of heap block headers
21  * 2. A map from addresses to heap block addresses to heap block headers
22  *
23  * Access speed is crucial.  We implement an index structure based on a 2
24  * level tree.
25  */
26
27 STATIC bottom_index * GC_all_bottom_indices = 0;
28                         /* Pointer to the first (lowest address)        */
29                         /* bottom_index.  Assumes the lock is held.     */
30
31 STATIC bottom_index * GC_all_bottom_indices_end = 0;
32                         /* Pointer to the last (highest address)        */
33                         /* bottom_index.  Assumes the lock is held.     */
34
35 /* Non-macro version of header location routine */
36 GC_INNER hdr * GC_find_header(ptr_t h)
37 {
38 #   ifdef HASH_TL
39         hdr * result;
40         GET_HDR(h, result);
41         return(result);
42 #   else
43         return(HDR_INNER(h));
44 #   endif
45 }
46
47 /* Handle a header cache miss.  Returns a pointer to the        */
48 /* header corresponding to p, if p can possibly be a valid      */
49 /* object pointer, and 0 otherwise.                             */
50 /* GUARANTEED to return 0 for a pointer past the first page     */
51 /* of an object unless both GC_all_interior_pointers is set     */
52 /* and p is in fact a valid object pointer.                     */
53 /* Never returns a pointer to a free hblk.                      */
54 GC_INNER hdr *
55 #ifdef PRINT_BLACK_LIST
56   GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce, ptr_t source)
57 #else
58   GC_header_cache_miss(ptr_t p, hdr_cache_entry *hce)
59 #endif
60 {
61   hdr *hhdr;
62   HC_MISS();
63   GET_HDR(p, hhdr);
64   if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
65     if (GC_all_interior_pointers) {
66       if (hhdr != 0) {
67         ptr_t current = p;
68
69         current = (ptr_t)HBLKPTR(current);
70         do {
71             current = current - HBLKSIZE*(word)hhdr;
72             hhdr = HDR(current);
73         } while(IS_FORWARDING_ADDR_OR_NIL(hhdr));
74         /* current points to near the start of the large object */
75         if (hhdr -> hb_flags & IGNORE_OFF_PAGE)
76             return 0;
77         if (HBLK_IS_FREE(hhdr)
78             || p - current >= (ptrdiff_t)(hhdr->hb_sz)) {
79             GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
80             /* Pointer past the end of the block */
81             return 0;
82         }
83       } else {
84         GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
85         /* And return zero: */
86       }
87       GC_ASSERT(hhdr == 0 || !HBLK_IS_FREE(hhdr));
88       return hhdr;
89       /* Pointers past the first page are probably too rare     */
90       /* to add them to the cache.  We don't.                   */
91       /* And correctness relies on the fact that we don't.      */
92     } else {
93       if (hhdr == 0) {
94         GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
95       }
96       return 0;
97     }
98   } else {
99     if (HBLK_IS_FREE(hhdr)) {
100       GC_ADD_TO_BLACK_LIST_NORMAL(p, source);
101       return 0;
102     } else {
103       hce -> block_addr = (word)(p) >> LOG_HBLKSIZE;
104       hce -> hce_hdr = hhdr;
105       return hhdr;
106     }
107   }
108 }
109
110 /* Routines to dynamically allocate collector data structures that will */
111 /* never be freed.                                                      */
112
113 static ptr_t scratch_free_ptr = 0;
114
115 /* GC_scratch_last_end_ptr is end point of last obtained scratch area.  */
116 /* GC_scratch_end_ptr is end point of current scratch area.             */
117
118 GC_INNER ptr_t GC_scratch_alloc(size_t bytes)
119 {
120     ptr_t result = scratch_free_ptr;
121     size_t bytes_to_get;
122
123     bytes = ROUNDUP_GRANULE_SIZE(bytes);
124     for (;;) {
125         scratch_free_ptr += bytes;
126         if ((word)scratch_free_ptr <= (word)GC_scratch_end_ptr) {
127             /* Unallocated space of scratch buffer has enough size. */
128             return result;
129         }
130
131         if (bytes >= MINHINCR * HBLKSIZE) {
132             bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes);
133             result = (ptr_t)GET_MEM(bytes_to_get);
134             GC_add_to_our_memory(result, bytes_to_get);
135             /* Undo scratch free area pointer update; get memory directly. */
136             scratch_free_ptr -= bytes;
137             if (result != NULL) {
138                 /* Update end point of last obtained area (needed only  */
139                 /* by GC_register_dynamic_libraries for some targets).  */
140                 GC_scratch_last_end_ptr = result + bytes;
141             }
142             return result;
143         }
144
145         bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(MINHINCR * HBLKSIZE);
146                                                 /* round up for safety */
147         result = (ptr_t)GET_MEM(bytes_to_get);
148         GC_add_to_our_memory(result, bytes_to_get);
149         if (NULL == result) {
150             WARN("Out of memory - trying to allocate requested amount"
151                  " (%" WARN_PRIdPTR " bytes)...\n", (word)bytes);
152             scratch_free_ptr -= bytes; /* Undo free area pointer update */
153             bytes_to_get = ROUNDUP_PAGESIZE_IF_MMAP(bytes);
154             result = (ptr_t)GET_MEM(bytes_to_get);
155             GC_add_to_our_memory(result, bytes_to_get);
156             return result;
157         }
158         /* Update scratch area pointers and retry.      */
159         scratch_free_ptr = result;
160         GC_scratch_end_ptr = scratch_free_ptr + bytes_to_get;
161         GC_scratch_last_end_ptr = GC_scratch_end_ptr;
162     }
163 }
164
165 static hdr * hdr_free_list = 0;
166
167 /* Return an uninitialized header */
168 static hdr * alloc_hdr(void)
169 {
170     hdr * result;
171
172     if (NULL == hdr_free_list) {
173         result = (hdr *)GC_scratch_alloc(sizeof(hdr));
174     } else {
175         result = hdr_free_list;
176         hdr_free_list = (hdr *) (result -> hb_next);
177     }
178     return(result);
179 }
180
181 GC_INLINE void free_hdr(hdr * hhdr)
182 {
183     hhdr -> hb_next = (struct hblk *) hdr_free_list;
184     hdr_free_list = hhdr;
185 }
186
187 #ifdef COUNT_HDR_CACHE_HITS
188   /* Used for debugging/profiling (the symbols are externally visible). */
189   word GC_hdr_cache_hits = 0;
190   word GC_hdr_cache_misses = 0;
191 #endif
192
193 GC_INNER void GC_init_headers(void)
194 {
195     unsigned i;
196
197     GC_all_nils = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index));
198     if (GC_all_nils == NULL) {
199       GC_err_printf("Insufficient memory for GC_all_nils\n");
200       EXIT();
201     }
202     BZERO(GC_all_nils, sizeof(bottom_index));
203     for (i = 0; i < TOP_SZ; i++) {
204         GC_top_index[i] = GC_all_nils;
205     }
206 }
207
208 /* Make sure that there is a bottom level index block for address addr. */
209 /* Return FALSE on failure.                                             */
210 static GC_bool get_index(word addr)
211 {
212     word hi = (word)(addr) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE);
213     bottom_index * r;
214     bottom_index * p;
215     bottom_index ** prev;
216     bottom_index *pi; /* old_p */
217     word i;
218
219     GC_ASSERT(I_HOLD_LOCK());
220 #   ifdef HASH_TL
221       i = TL_HASH(hi);
222
223       pi = p = GC_top_index[i];
224       while(p != GC_all_nils) {
225           if (p -> key == hi) return(TRUE);
226           p = p -> hash_link;
227       }
228 #   else
229       if (GC_top_index[hi] != GC_all_nils)
230         return TRUE;
231       i = hi;
232 #   endif
233     r = (bottom_index *)GC_scratch_alloc(sizeof(bottom_index));
234     if (EXPECT(NULL == r, FALSE))
235       return FALSE;
236     BZERO(r, sizeof(bottom_index));
237     r -> key = hi;
238 #   ifdef HASH_TL
239       r -> hash_link = pi;
240 #   endif
241
242     /* Add it to the list of bottom indices */
243       prev = &GC_all_bottom_indices;    /* pointer to p */
244       pi = 0;                           /* bottom_index preceding p */
245       while ((p = *prev) != 0 && p -> key < hi) {
246         pi = p;
247         prev = &(p -> asc_link);
248       }
249       r -> desc_link = pi;
250       if (0 == p) {
251         GC_all_bottom_indices_end = r;
252       } else {
253         p -> desc_link = r;
254       }
255       r -> asc_link = p;
256       *prev = r;
257
258       GC_top_index[i] = r;
259     return(TRUE);
260 }
261
262 /* Install a header for block h.        */
263 /* The header is uninitialized.         */
264 /* Returns the header or 0 on failure.  */
265 GC_INNER struct hblkhdr * GC_install_header(struct hblk *h)
266 {
267     hdr * result;
268
269     if (!get_index((word) h)) return(0);
270     result = alloc_hdr();
271     if (result) {
272       SET_HDR(h, result);
273 #     ifdef USE_MUNMAP
274         result -> hb_last_reclaimed = (unsigned short)GC_gc_no;
275 #     endif
276     }
277     return(result);
278 }
279
280 /* Set up forwarding counts for block h of size sz */
281 GC_INNER GC_bool GC_install_counts(struct hblk *h, size_t sz/* bytes */)
282 {
283     struct hblk * hbp;
284
285     for (hbp = h; (word)hbp < (word)h + sz; hbp += BOTTOM_SZ) {
286         if (!get_index((word)hbp))
287             return FALSE;
288         if ((word)hbp > GC_WORD_MAX - (word)BOTTOM_SZ * HBLKSIZE)
289             break; /* overflow of hbp+=BOTTOM_SZ is expected */
290     }
291     if (!get_index((word)h + sz - 1))
292         return FALSE;
293     for (hbp = h + 1; (word)hbp < (word)h + sz; hbp += 1) {
294         word i = HBLK_PTR_DIFF(hbp, h);
295
296         SET_HDR(hbp, (hdr *)(i > MAX_JUMP? MAX_JUMP : i));
297     }
298     return TRUE;
299 }
300
301 /* Remove the header for block h */
302 GC_INNER void GC_remove_header(struct hblk *h)
303 {
304     hdr **ha;
305     GET_HDR_ADDR(h, ha);
306     free_hdr(*ha);
307     *ha = 0;
308 }
309
310 /* Remove forwarding counts for h */
311 GC_INNER void GC_remove_counts(struct hblk *h, size_t sz/* bytes */)
312 {
313     struct hblk * hbp;
314
315     for (hbp = h+1; (word)hbp < (word)h + sz; hbp += 1) {
316         SET_HDR(hbp, 0);
317     }
318 }
319
320 /* Apply fn to all allocated blocks.  It is the caller responsibility   */
321 /* to avoid data race during the function execution (e.g. by holding    */
322 /* the allocation lock).                                                */
323 void GC_apply_to_all_blocks(void (*fn)(struct hblk *h, word client_data),
324                             word client_data)
325 {
326     signed_word j;
327     bottom_index * index_p;
328
329     for (index_p = GC_all_bottom_indices; index_p != 0;
330          index_p = index_p -> asc_link) {
331         for (j = BOTTOM_SZ-1; j >= 0;) {
332             if (!IS_FORWARDING_ADDR_OR_NIL(index_p->index[j])) {
333                 if (!HBLK_IS_FREE(index_p->index[j])) {
334                     (*fn)(((struct hblk *)
335                               (((index_p->key << LOG_BOTTOM_SZ) + (word)j)
336                                << LOG_HBLKSIZE)),
337                           client_data);
338                 }
339                 j--;
340              } else if (index_p->index[j] == 0) {
341                 j--;
342              } else {
343                 j -= (signed_word)(index_p->index[j]);
344              }
345          }
346      }
347 }
348
349 /* Get the next valid block whose address is at least h */
350 /* Return 0 if there is none.                           */
351 GC_INNER struct hblk * GC_next_used_block(struct hblk *h)
352 {
353     REGISTER bottom_index * bi;
354     REGISTER word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1);
355
356     GC_ASSERT(I_HOLD_LOCK());
357     GET_BI(h, bi);
358     if (bi == GC_all_nils) {
359         REGISTER word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE);
360
361         bi = GC_all_bottom_indices;
362         while (bi != 0 && bi -> key < hi) bi = bi -> asc_link;
363         j = 0;
364     }
365     while(bi != 0) {
366         while (j < BOTTOM_SZ) {
367             hdr * hhdr = bi -> index[j];
368             if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
369                 j++;
370             } else {
371                 if (!HBLK_IS_FREE(hhdr)) {
372                     return((struct hblk *)
373                               (((bi -> key << LOG_BOTTOM_SZ) + j)
374                                << LOG_HBLKSIZE));
375                 } else {
376                     j += divHBLKSZ(hhdr -> hb_sz);
377                 }
378             }
379         }
380         j = 0;
381         bi = bi -> asc_link;
382     }
383     return(0);
384 }
385
386 /* Get the last (highest address) block whose address is        */
387 /* at most h.  Return 0 if there is none.                       */
388 /* Unlike the above, this may return a free block.              */
389 GC_INNER struct hblk * GC_prev_block(struct hblk *h)
390 {
391     bottom_index * bi;
392     signed_word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1);
393
394     GC_ASSERT(I_HOLD_LOCK());
395     GET_BI(h, bi);
396     if (bi == GC_all_nils) {
397         word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE);
398         bi = GC_all_bottom_indices_end;
399         while (bi != 0 && bi -> key > hi) bi = bi -> desc_link;
400         j = BOTTOM_SZ - 1;
401     }
402     while(bi != 0) {
403         while (j >= 0) {
404             hdr * hhdr = bi -> index[j];
405             if (0 == hhdr) {
406                 --j;
407             } else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
408                 j -= (signed_word)hhdr;
409             } else {
410                 return((struct hblk *)
411                           (((bi -> key << LOG_BOTTOM_SZ) + j)
412                                << LOG_HBLKSIZE));
413             }
414         }
415         j = BOTTOM_SZ - 1;
416         bi = bi -> desc_link;
417     }
418     return(0);
419 }