/* "Bag-of-pages" garbage collector for the GNU compiler.
- Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
+ Free Software Foundation, Inc.
This file is part of GCC.
this is the last page-entry. */
struct page_entry *next;
+ /* The previous page-entry with objects of the same size, or NULL if
+ this is the first page-entry. The PREV pointer exists solely to
+ keep the cost of ggc_free manageable. */
+ struct page_entry *prev;
+
/* The number of bytes allocated. (This will always be a multiple
of the host system page size.) */
size_t bytes;
static inline void adjust_depth (void);
static void move_ptes_to_front (int, int);
-#ifdef ENABLE_GC_CHECKING
-static void poison_pages (void);
-#endif
-
void debug_print_page_list (int);
static void push_depth (unsigned int);
static void push_by_depth (page_entry *, unsigned long *);
alloc_anon (char *pref ATTRIBUTE_UNUSED, size_t size)
{
#ifdef HAVE_MMAP_ANON
- char *page = (char *) mmap (pref, size, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ char *page = mmap (pref, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
#endif
#ifdef HAVE_MMAP_DEV_ZERO
- char *page = (char *) mmap (pref, size, PROT_READ | PROT_WRITE,
- MAP_PRIVATE, G.dev_zero_fd, 0);
+ char *page = mmap (pref, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, G.dev_zero_fd, 0);
#endif
if (page == (char *) MAP_FAILED)
/* Typed allocation function. Does nothing special in this collector. */
void *
-ggc_alloc_typed (enum gt_types_enum type ATTRIBUTE_UNUSED, size_t size)
+ggc_alloc_typed_stat (enum gt_types_enum type ATTRIBUTE_UNUSED, size_t size
+ MEM_STAT_DECL)
{
- return ggc_alloc (size);
+ return ggc_alloc_stat (size PASS_MEM_STAT);
}
/* Zone allocation function. Does nothing special in this collector. */
void *
-ggc_alloc_zone (size_t size, struct alloc_zone *zone ATTRIBUTE_UNUSED)
+ggc_alloc_zone_stat (size_t size, struct alloc_zone *zone ATTRIBUTE_UNUSED
+ MEM_STAT_DECL)
{
- return ggc_alloc (size);
+ return ggc_alloc_stat (size PASS_MEM_STAT);
}
/* Allocate a chunk of memory of SIZE bytes. Its contents are undefined. */
void *
-ggc_alloc (size_t size)
+ggc_alloc_stat (size_t size MEM_STAT_DECL)
{
size_t order, word, bit, object_offset, object_size;
struct page_entry *entry;
while (new_entry->context_depth >= G.depth_in_use)
push_depth (G.by_depth_in_use-1);
- /* If this is the only entry, it's also the tail. */
+ /* If this is the only entry, it's also the tail. If it is not
+ the only entry, then we must update the PREV pointer of the
+ ENTRY (G.pages[order]) to point to our new page entry. */
if (entry == NULL)
G.page_tails[order] = new_entry;
+ else
+ entry->prev = new_entry;
- /* Put new pages at the head of the page list. */
+ /* Put new pages at the head of the page list. By definition the
+ entry at the head of the list always has a NULL pointer. */
new_entry->next = entry;
+ new_entry->prev = NULL;
entry = new_entry;
G.pages[order] = new_entry;
&& entry->next != NULL
&& entry->next->num_free_objects > 0)
{
+ /* We have a new head for the list. */
G.pages[order] = entry->next;
+
+ /* We are moving ENTRY to the end of the page table list.
+ The new page at the head of the list will have NULL in
+ its PREV field and ENTRY will have NULL in its NEXT field. */
+ entry->next->prev = NULL;
entry->next = NULL;
+
+ /* Append ENTRY to the tail of the list. */
+ entry->prev = G.page_tails[order];
G.page_tails[order]->next = entry;
G.page_tails[order] = entry;
}
+#ifdef GATHER_STATISTICS
+ ggc_record_overhead (OBJECT_SIZE (order), OBJECT_SIZE (order) - size PASS_MEM_STAT);
+#endif
/* Calculate the object's address. */
result = entry->page + object_offset;
if (pe->num_free_objects++ == 0)
{
+ page_entry *p, *q;
+
/* 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 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;
+ PE is the node we want to move. Q is the previous node
+ and P is the next node in the list. */
+ q = pe->prev;
if (q && q->num_free_objects == 0)
{
p = pe->next;
+
q->next = p;
+
+ /* If PE was at the end of the list, then Q becomes the
+ new end of the list. If PE was not the end of the
+ list, then we need to update the PREV field for P. */
if (!p)
G.page_tails[order] = q;
+ else
+ p->prev = q;
+
+ /* Move PE to the head of the list. */
pe->next = G.pages[order];
+ pe->prev = NULL;
+ G.pages[order]->prev = pe;
G.pages[order] = pe;
}
\f
/* Unmark all objects. */
-static inline void
+static void
clear_marks (void)
{
unsigned order;
/* Free all empty pages. Partially empty pages need no attention
because the `mark' bit doubles as an `unused' bit. */
-static inline void
+static void
sweep_pages (void)
{
unsigned order;
/* Remove the page if it's empty. */
else if (live_objects == 0)
{
+ /* If P was the first page in the list, then NEXT
+ becomes the new first page in the list, otherwise
+ splice P out of the forward pointers. */
if (! previous)
G.pages[order] = next;
else
previous->next = next;
+
+ /* Splice P out of the back pointers too. */
+ if (next)
+ next->prev = previous;
/* Are we removing the last element? */
if (p == G.page_tails[order])
{
/* Move p to the end of the list. */
p->next = NULL;
+ p->prev = G.page_tails[order];
G.page_tails[order]->next = p;
/* Update the tail pointer... */
G.pages[order] = next;
else
previous->next = next;
+
+ /* And update the backpointer in NEXT if necessary. */
+ if (next)
+ next->prev = previous;
+
p = previous;
}
}
else if (p != G.pages[order])
{
previous->next = p->next;
+
+ /* Update the backchain in the next node if it exists. */
+ if (p->next)
+ p->next->prev = previous;
+
+ /* Move P to the head of the list. */
p->next = G.pages[order];
+ p->prev = NULL;
+ G.pages[order]->prev = p;
+
+ /* Update the head pointer. */
G.pages[order] = p;
+
/* Are we moving the last element? */
if (G.page_tails[order] == p)
G.page_tails[order] = previous;
#ifdef ENABLE_GC_CHECKING
/* Clobber all free objects. */
-static inline void
+static void
poison_pages (void)
{
unsigned order;
}
}
}
+#else
+#define poison_pages()
+#endif
+
+#ifdef ENABLE_GC_ALWAYS_COLLECT
+/* Validate that the reportedly free objects actually are. */
+
+static void
+validate_free_objects (void)
+{
+ struct free_object *f, *next, *still_free = NULL;
+
+ for (f = G.free_object_list; f ; f = next)
+ {
+ page_entry *pe = lookup_page_table_entry (f->object);
+ 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;
+ next = f->next;
+
+ /* Make certain it isn't visible from any root. Notice that we
+ do this check before sweep_pages merges save_in_use_p. */
+ if (pe->in_use_p[word] & (1UL << bit))
+ abort ();
+
+ /* If the object comes from an outer context, then retain the
+ free_object entry, so that we can verify that the address
+ isn't live on the stack in some outer context. */
+ if (pe->context_depth != G.context_depth)
+ {
+ f->next = still_free;
+ still_free = f;
+ }
+ else
+ free (f);
+ }
+
+ G.free_object_list = still_free;
+}
+#else
+#define validate_free_objects()
#endif
/* Top level mark-and-sweep routine. */
clear_marks ();
ggc_mark_roots ();
-
-#ifdef ENABLE_GC_CHECKING
poison_pages ();
-#endif
-
+ validate_free_objects ();
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);