diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a66fb08628f..c05b347dce7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2004-01-26 Richard Henderson + + * c-parse.in (extension): Use itype. + (SAVE_EXT_FLAGS): Don't allocate a tree. + (RESTORE_EXT_FLAGS): Don't read a tree. + 2004-01-26 Jan Hubicka * cselib.c (discard_useless_values): Clear out value pointer pointing diff --git a/gcc/ggc-common.c b/gcc/ggc-common.c index ecd6624487c..6ec7db7f264 100644 --- a/gcc/ggc-common.c +++ b/gcc/ggc-common.c @@ -147,6 +147,11 @@ ggc_realloc (void *x, size_t size) return ggc_alloc (size); old_size = ggc_get_size (x); + +#ifndef ENABLE_GC_ALWAYS_COLLECT + /* In completely-anal-checking mode, never re-use memory. This maximizes + the chance of catching the user retaining a pointer to the old block. + Otherwise, we get to consume the power-of-two overhead we had before. */ if (size <= old_size) { /* Mark the unwanted memory as unaccessible. We also need to make @@ -164,6 +169,7 @@ ggc_realloc (void *x, size_t size) VALGRIND_DISCARD (VALGRIND_MAKE_READABLE (x, size)); return x; } +#endif r = ggc_alloc (size); @@ -176,7 +182,7 @@ ggc_realloc (void *x, size_t size) memcpy (r, x, old_size); /* The old object is not supposed to be used anymore. */ - VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (x, old_size)); + ggc_free (x); return r; } diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c index bf18e3f00d7..1c625e6f017 100644 --- a/gcc/ggc-page.c +++ b/gcc/ggc-page.c @@ -401,6 +401,17 @@ static struct globals zero otherwise. We allocate them all together, to enable a better runtime data access pattern. */ unsigned long **save_in_use; + +#ifdef ENABLE_GC_ALWAYS_COLLECT + /* List of free objects to be verified as actually free on the + next collection. */ + struct free_object + { + void *object; + struct free_object *next; + } *free_object_list; +#endif + #ifdef GATHER_STATISTICS struct { @@ -894,7 +905,7 @@ adjust_depth (void) /* For a page that is no longer needed, put it on the free page list. */ -static inline void +static void free_page (page_entry *entry) { if (GGC_DEBUG_LEVEL >= 2) @@ -1049,16 +1060,19 @@ ggc_alloc_zone (size_t size, struct alloc_zone *zone ATTRIBUTE_UNUSED) void * ggc_alloc (size_t size) { - unsigned order, word, bit, object_offset; + size_t order, word, bit, object_offset, object_size; struct page_entry *entry; void *result; if (size <= 256) - order = size_lookup[size]; + { + order = size_lookup[size]; + object_size = OBJECT_SIZE (order); + } else { order = 9; - while (size > OBJECT_SIZE (order)) + while (size > (object_size = OBJECT_SIZE (order))) order++; } @@ -1121,7 +1135,7 @@ ggc_alloc (size_t size) /* Next time, try the next bit. */ entry->next_bit_hint = hint + 1; - object_offset = hint * OBJECT_SIZE (order); + object_offset = hint * object_size; } /* Set the in-use bit. */ @@ -1149,16 +1163,16 @@ ggc_alloc (size_t size) exact same semantics in presence of memory bugs, regardless of ENABLE_VALGRIND_CHECKING. We override this request below. Drop the handle to avoid handle leak. */ - VALGRIND_DISCARD (VALGRIND_MAKE_WRITABLE (result, OBJECT_SIZE (order))); + VALGRIND_DISCARD (VALGRIND_MAKE_WRITABLE (result, object_size)); /* `Poison' the entire allocated object, including any padding at the end. */ - memset (result, 0xaf, OBJECT_SIZE (order)); + memset (result, 0xaf, object_size); /* Make the bytes after the end of the object unaccessible. Discard the handle to avoid handle leak. */ VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS ((char *) result + size, - OBJECT_SIZE (order) - size)); + object_size - size)); #endif /* Tell Valgrind that the memory is there, but its content isn't @@ -1168,37 +1182,39 @@ ggc_alloc (size_t size) /* Keep track of how many bytes are being allocated. This information is used in deciding when to collect. */ - G.allocated += OBJECT_SIZE (order); + G.allocated += object_size; #ifdef GATHER_STATISTICS { - G.stats.total_overhead += OBJECT_SIZE (order) - size; - G.stats.total_allocated += OBJECT_SIZE(order); - G.stats.total_overhead_per_order[order] += OBJECT_SIZE (order) - size; - G.stats.total_allocated_per_order[order] += OBJECT_SIZE (order); + size_t overhead = object_size - size; - if (size <= 32){ - G.stats.total_overhead_under32 += OBJECT_SIZE (order) - size; - G.stats.total_allocated_under32 += OBJECT_SIZE(order); - } - - if (size <= 64){ - G.stats.total_overhead_under64 += OBJECT_SIZE (order) - size; - G.stats.total_allocated_under64 += OBJECT_SIZE(order); - } - - if (size <= 128){ - G.stats.total_overhead_under128 += OBJECT_SIZE (order) - size; - G.stats.total_allocated_under128 += OBJECT_SIZE(order); - } + G.stats.total_overhead += overhead; + G.stats.total_allocated += object_size; + G.stats.total_overhead_per_order[order] += overhead; + G.stats.total_allocated_per_order[order] += object_size; + if (size <= 32) + { + G.stats.total_overhead_under32 += overhead; + G.stats.total_allocated_under32 += object_size; + } + if (size <= 64) + { + G.stats.total_overhead_under64 += overhead; + G.stats.total_allocated_under64 += object_size; + } + if (size <= 128) + { + G.stats.total_overhead_under128 += overhead; + G.stats.total_allocated_under128 += object_size; + } } #endif - + if (GGC_DEBUG_LEVEL >= 3) fprintf (G.debug_file, "Allocating object, requested size=%lu, actual=%lu at %p on %p\n", - (unsigned long) size, (unsigned long) OBJECT_SIZE (order), result, + (unsigned long) size, (unsigned long) object_size, result, (void *) entry); return result; @@ -1279,6 +1295,78 @@ ggc_get_size (const void *p) page_entry *pe = lookup_page_table_entry (p); return OBJECT_SIZE (pe->order); } + +/* Release the memory for object P. */ + +void +ggc_free (void *p) +{ + page_entry *pe = lookup_page_table_entry (p); + size_t order = pe->order; + size_t size = OBJECT_SIZE (order); + + if (GGC_DEBUG_LEVEL >= 3) + fprintf (G.debug_file, + "Freeing object, actual size=%lu, at %p on %p\n", + (unsigned long) size, p, (void *) pe); + +#ifdef ENABLE_GC_CHECKING + /* Poison the data, to indicate the data is garbage. */ + VALGRIND_DISCARD (VALGRIND_MAKE_WRITABLE (p, size)); + memset (p, 0xa5, size); +#endif + /* Let valgrind know the object is free. */ + VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (p, size)); + +#ifdef ENABLE_GC_ALWAYS_COLLECT + /* In the completely-anal-checking mode, we do *not* immediately free + the data, but instead verify that the data is *actually* not + reachable the next time we collect. */ + { + struct free_object *fo = xmalloc (sizeof (struct free_object)); + fo->object = p; + fo->next = G.free_object_list; + G.free_object_list = fo; + } +#else + { + unsigned int bit_offset, word, bit; + + G.allocated -= size; + + /* Mark the object not-in-use. */ + bit_offset = OFFSET_TO_BIT (((const char *) p) - pe->page, order); + word = bit_offset / HOST_BITS_PER_LONG; + bit = bit_offset % HOST_BITS_PER_LONG; + pe->in_use_p[word] &= ~(1UL << bit); + + if (pe->num_free_objects++ == 0) + { + /* If the page is completely full, then it's supposed to + be after all pages that aren't. Since we've freed one + object from a page that was full, we need to move the + page to the head of the list. */ + + page_entry *p, *q; + for (q = NULL, p = G.pages[order]; ; q = p, p = p->next) + if (p == pe) + break; + if (q && q->num_free_objects == 0) + { + p = pe->next; + q->next = p; + if (!p) + G.page_tails[order] = q; + pe->next = G.pages[order]; + G.pages[order] = pe; + } + + /* Reset the hint bit to point to the only free object. */ + pe->next_bit_hint = bit_offset; + } + } +#endif +} /* Subroutine of init_ggc which computes the pair of numbers used to perform division by OBJECT_SIZE (order) and fills in inverse_table[]. @@ -1788,6 +1876,8 @@ ggc_collect (void) timevar_push (TV_GC); if (!quiet_flag) fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated / 1024); + if (GGC_DEBUG_LEVEL >= 2) + fprintf (G.debug_file, "BEGIN COLLECTING\n"); /* Zero the total allocated bytes. This will be recalculated in the sweep phase. */ @@ -1809,12 +1899,42 @@ ggc_collect (void) sweep_pages (); +#ifdef ENABLE_GC_ALWAYS_COLLECT + /* Validate that the reportedly free objects actually are. */ + { + struct free_object *f, *n; + for (f = G.free_object_list; f ; f = n) + { + page_entry *pe = lookup_page_table_entry (f->object); + + /* If the page entry is null, that means the entire page is free. + Otherwise, we have to examine the in-use bit for the object. */ + if (pe != NULL) + { + size_t bit, word; + bit = OFFSET_TO_BIT ((char *)f->object - pe->page, pe->order); + word = bit / HOST_BITS_PER_LONG; + bit = bit % HOST_BITS_PER_LONG; + + if (pe->in_use_p[word] & (1UL << bit)) + abort (); + } + + n = f->next; + free (f); + } + G.free_object_list = NULL; + } +#endif + G.allocated_last_gc = G.allocated; timevar_pop (TV_GC); if (!quiet_flag) fprintf (stderr, "%luk}", (unsigned long) G.allocated / 1024); + if (GGC_DEBUG_LEVEL >= 2) + fprintf (G.debug_file, "END COLLECTING\n"); } /* Print allocation statistics. */ diff --git a/gcc/ggc.h b/gcc/ggc.h index 8add2da33ba..fbd1e3489e2 100644 --- a/gcc/ggc.h +++ b/gcc/ggc.h @@ -223,6 +223,8 @@ extern void *ggc_alloc_cleared_zone (size_t, struct alloc_zone *); extern void *ggc_realloc (void *, size_t); /* Like ggc_alloc_cleared, but performs a multiplication. */ extern void *ggc_calloc (size_t, size_t); +/* Free a block. To be used when known for certain it's not reachable. */ +extern void ggc_free (void *); #define ggc_alloc_rtx(CODE) \ ((rtx) ggc_alloc_typed (gt_ggc_e_7rtx_def, RTX_SIZE (CODE)))