OSDN Git Service

cp:
[pf3gnuchains/gcc-fork.git] / gcc / ggc-page.c
index 7687cef..ad3f815 100644 (file)
@@ -1,42 +1,62 @@
 /* "Bag-of-pages" garbage collector for the GNU compiler.
-   Copyright (C) 1999 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
-   This file is part of GNU CC.
+This file is part of GCC.
 
-   GNU CC is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
 
-   GNU CC is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
 
-   You should have received a copy of the GNU General Public License
-   along with GNU CC; see the file COPYING.  If not, write to
-   the Free Software Foundation, 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
 
 #include "config.h"
 #include "system.h"
 #include "tree.h"
 #include "rtl.h"
 #include "tm_p.h"
+#include "toplev.h"
 #include "varray.h"
 #include "flags.h"
 #include "ggc.h"
+#include "timevar.h"
+
+/* Prefer MAP_ANON(YMOUS) to /dev/zero, since we don't need to keep a
+   file open.  Prefer either to valloc.  */
+#ifdef HAVE_MMAP_ANON
+# undef HAVE_MMAP_DEV_ZERO
+
+# include <sys/mman.h>
+# ifndef MAP_FAILED
+#  define MAP_FAILED -1
+# endif
+# if !defined (MAP_ANONYMOUS) && defined (MAP_ANON)
+#  define MAP_ANONYMOUS MAP_ANON
+# endif
+# define USING_MMAP
 
-#ifdef HAVE_MMAP
-#include <sys/mman.h>
 #endif
 
-#ifndef MAP_FAILED
-#define MAP_FAILED -1
+#ifdef HAVE_MMAP_DEV_ZERO
+
+# include <sys/mman.h>
+# ifndef MAP_FAILED
+#  define MAP_FAILED -1
+# endif
+# define USING_MMAP
+
 #endif
 
-#if !defined (MAP_ANONYMOUS) && defined (MAP_ANON)
-#define MAP_ANONYMOUS MAP_ANON
+#ifndef USING_MMAP
+#define USING_MALLOC_PAGE_GROUPS
 #endif
 
 /* Stategy: 
    last collection.  */
 #undef GGC_ALWAYS_COLLECT
 
-/* If ENABLE_CHECKING is defined, enable GGC_POISON and
-   GGC_ALWAYS_COLLECT automatically.  */
-#ifdef ENABLE_CHECKING
+#ifdef ENABLE_GC_CHECKING
 #define GGC_POISON
+#endif
+#ifdef ENABLE_GC_ALWAYS_COLLECT
 #define GGC_ALWAYS_COLLECT
 #endif
 
      1: GC statistics only.
      2: Page-entry allocations/deallocations as well.
      3: Object allocations as well.
-     4: Object marks as well.   */
+     4: Object marks as well.  */
 #define GGC_DEBUG_LEVEL (0)
 \f
 #ifndef HOST_BITS_PER_PTR
 #define HOST_BITS_PER_PTR  HOST_BITS_PER_LONG
 #endif
 
-/* Timing information for collect execution goes into here.  */
-extern int gc_time;
-
-/* The "" allocated string.  */
-char *empty_string;
 \f
 /* A two-level tree is used to look up the page-entry for a given
    pointer.  Two chunks of the pointer's bits are extracted to index
@@ -136,6 +151,59 @@ char *empty_string;
 #define LOOKUP_L2(p) \
   (((size_t) (p) >> G.lg_pagesize) & ((1 << PAGE_L2_BITS) - 1))
 
+/* The number of objects per allocation page, for objects on a page of
+   the indicated ORDER.  */
+#define OBJECTS_PER_PAGE(ORDER) objects_per_page_table[ORDER]
+
+/* The size of an object on a page of the indicated ORDER.  */
+#define OBJECT_SIZE(ORDER) object_size_table[ORDER]
+
+/* The number of extra orders, not corresponding to power-of-two sized
+   objects.  */
+
+#define NUM_EXTRA_ORDERS \
+  (sizeof (extra_order_size_table) / sizeof (extra_order_size_table[0]))
+
+/* The Ith entry is the maximum size of an object to be stored in the
+   Ith extra order.  Adding a new entry to this array is the *only*
+   thing you need to do to add a new special allocation size.  */
+
+static const size_t extra_order_size_table[] = {
+  sizeof (struct tree_decl),
+  sizeof (struct tree_list)
+};
+
+/* The total number of orders.  */
+
+#define NUM_ORDERS (HOST_BITS_PER_PTR + NUM_EXTRA_ORDERS)
+
+/* We use this structure to determine the alignment required for
+   allocations.  For power-of-two sized allocations, that's not a
+   problem, but it does matter for odd-sized allocations.  */
+
+struct max_alignment {
+  char c;
+  union {
+    HOST_WIDEST_INT i;
+#ifdef HAVE_LONG_DOUBLE
+    long double d;
+#else
+    double d;
+#endif
+  } u;
+};
+
+/* The biggest alignment required.  */
+
+#define MAX_ALIGNMENT (offsetof (struct max_alignment, u))
+
+/* The Ith entry is the number of objects on a page or order I.  */
+
+static unsigned objects_per_page_table[NUM_ORDERS];
+
+/* The Ith entry is the size of an object on a page of order I.  */
+
+static size_t object_size_table[NUM_ORDERS];
 
 /* A page_entry records the status of an allocation page.  This
    structure is dynamically sized to fit the bitmap in_use_p.  */
@@ -152,15 +220,17 @@ typedef struct page_entry
   /* The address at which the memory is allocated.  */
   char *page;
 
+#ifdef USING_MALLOC_PAGE_GROUPS
+  /* Back pointer to the page group this page came from.  */
+  struct page_group *group;
+#endif
+
   /* Saved in-use bit vector for pages that aren't in the topmost
      context during collection.  */
   unsigned long *save_in_use_p;
 
   /* Context depth of this page.  */
-  unsigned char context_depth;
-
-  /* The lg of size of objects allocated from this page.  */
-  unsigned char order;
+  unsigned short context_depth;
 
   /* The number of free objects remaining on this page.  */
   unsigned short num_free_objects;
@@ -169,12 +239,33 @@ typedef struct page_entry
      next allocation from this page.  */
   unsigned short next_bit_hint;
 
+  /* The lg of size of objects allocated from this page.  */
+  unsigned char order;
+
   /* A bit vector indicating whether or not objects are in use.  The
      Nth bit is one if the Nth object on this page is allocated.  This
      array is dynamically sized.  */
   unsigned long in_use_p[1];
 } page_entry;
 
+#ifdef USING_MALLOC_PAGE_GROUPS
+/* A page_group describes a large allocation from malloc, from which
+   we parcel out aligned pages.  */
+typedef struct page_group
+{
+  /* A linked list of all extant page groups.  */
+  struct page_group *next;
+
+  /* The address we received from malloc.  */
+  char *allocation;
+
+  /* The size of the block.  */
+  size_t alloc_size;
+
+  /* A bitmask of pages in use.  */
+  unsigned int in_use;
+} page_group;
+#endif
 
 #if HOST_BITS_PER_PTR <= 32
 
@@ -202,12 +293,12 @@ static struct globals
      If there are any pages with free objects, they will be at the
      head of the list.  NULL if there are no page-entries for this
      object size.  */
-  page_entry *pages[HOST_BITS_PER_PTR];
+  page_entry *pages[NUM_ORDERS];
 
   /* The Nth element in this array is the last page with objects of
      size 2^N.  NULL if there are no page-entries for this object
      size.  */
-  page_entry *page_tails[HOST_BITS_PER_PTR];
+  page_entry *page_tails[NUM_ORDERS];
 
   /* Lookup table for associating allocation pages with object addresses.  */
   page_table lookup;
@@ -226,34 +317,28 @@ static struct globals
   size_t bytes_mapped;
 
   /* The current depth in the context stack.  */
-  unsigned char context_depth;
+  unsigned short context_depth;
 
   /* A file descriptor open to /dev/zero for reading.  */
-#if defined (HAVE_MMAP) && !defined(MAP_ANONYMOUS)
+#if defined (HAVE_MMAP_DEV_ZERO)
   int dev_zero_fd;
 #endif
 
   /* A cache of free system pages.  */
   page_entry *free_pages;
 
+#ifdef USING_MALLOC_PAGE_GROUPS
+  page_group *page_groups;
+#endif
+
   /* The file descriptor for debugging output.  */
   FILE *debug_file;
 } G;
 
-
-/* Compute DIVIDEND / DIVISOR, rounded up.  */
-#define DIV_ROUND_UP(Dividend, Divisor) \
-  (((Dividend) + (Divisor) - 1) / (Divisor))
-
-/* The number of objects per allocation page, for objects of size
-   2^ORDER.  */
-#define OBJECTS_PER_PAGE(Order) \
-  ((Order) >= G.lg_pagesize ? 1 : G.pagesize / ((size_t)1 << (Order)))
-
 /* The size in bytes required to maintain a bitmap for the objects
    on a page-entry.  */
 #define BITMAP_SIZE(Num_objects) \
-  (DIV_ROUND_UP ((Num_objects), HOST_BITS_PER_LONG) * sizeof(long))
+  (CEIL ((Num_objects), HOST_BITS_PER_LONG) * sizeof(long))
 
 /* Skip garbage collection if the current allocation is not at least
    this factor times the allocation at the end of the last collection.
@@ -265,23 +350,35 @@ static struct globals
    test from triggering too often when the heap is small.  */
 #define GGC_MIN_LAST_ALLOCATED (4 * 1024 * 1024)
 
+/* Allocate pages in chunks of this size, to throttle calls to memory
+   allocation routines.  The first page is used, the rest go onto the
+   free list.  This cannot be larger than HOST_BITS_PER_INT for the
+   in_use bitmask for page_group.  */
+#define GGC_QUIRE_SIZE 16
 \f
-static int ggc_allocated_p PROTO ((const void *));
-static page_entry *lookup_page_table_entry PROTO ((const void *));
-static void set_page_table_entry PROTO ((void *, page_entry *));
-static char *alloc_anon PROTO ((char *, size_t));
-static struct page_entry * alloc_page PROTO ((unsigned));
-static void free_page PROTO ((struct page_entry *));
-static void release_pages PROTO ((void));
-static void clear_marks PROTO ((void));
-static void sweep_pages PROTO ((void));
-static void ggc_recalculate_in_use_p PROTO ((page_entry *));
+static int ggc_allocated_p PARAMS ((const void *));
+static page_entry *lookup_page_table_entry PARAMS ((const void *));
+static void set_page_table_entry PARAMS ((void *, page_entry *));
+#ifdef USING_MMAP
+static char *alloc_anon PARAMS ((char *, size_t));
+#endif
+#ifdef USING_MALLOC_PAGE_GROUPS
+static size_t page_group_index PARAMS ((char *, char *));
+static void set_page_group_in_use PARAMS ((page_group *, char *));
+static void clear_page_group_in_use PARAMS ((page_group *, char *));
+#endif
+static struct page_entry * alloc_page PARAMS ((unsigned));
+static void free_page PARAMS ((struct page_entry *));
+static void release_pages PARAMS ((void));
+static void clear_marks PARAMS ((void));
+static void sweep_pages PARAMS ((void));
+static void ggc_recalculate_in_use_p PARAMS ((page_entry *));
 
 #ifdef GGC_POISON
-static void poison_pages PROTO ((void));
+static void poison_pages PARAMS ((void));
 #endif
 
-void debug_print_page_list PROTO ((int));
+void debug_print_page_list PARAMS ((int));
 \f
 /* Returns non-zero if P was allocated in GC'able memory.  */
 
@@ -308,7 +405,7 @@ ggc_allocated_p (p)
   base = &table->table[0];
 #endif
 
-  /* Extract the level 1 and 2 indicies.  */
+  /* Extract the level 1 and 2 indices.  */
   L1 = LOOKUP_L1 (p);
   L2 = LOOKUP_L2 (p);
 
@@ -335,7 +432,7 @@ lookup_page_table_entry(p)
   base = &table->table[0];
 #endif
 
-  /* Extract the level 1 and 2 indicies.  */
+  /* Extract the level 1 and 2 indices.  */
   L1 = LOOKUP_L1 (p);
   L2 = LOOKUP_L2 (p);
 
@@ -370,7 +467,7 @@ found:
   base = &table->table[0];
 #endif
 
-  /* Extract the level 1 and 2 indicies.  */
+  /* Extract the level 1 and 2 indices.  */
   L1 = LOOKUP_L1 (p);
   L2 = LOOKUP_L2 (p);
 
@@ -387,56 +484,78 @@ debug_print_page_list (order)
      int order;
 {
   page_entry *p;
-  printf ("Head=%p, Tail=%p:\n", G.pages[order], G.page_tails[order]);
+  printf ("Head=%p, Tail=%p:\n", (PTR) G.pages[order],
+         (PTR) G.page_tails[order]);
   p = G.pages[order];
   while (p != NULL)
     {
-      printf ("%p(%1d|%3d) -> ", p, p->context_depth, p->num_free_objects);
+      printf ("%p(%1d|%3d) -> ", (PTR) p, p->context_depth,
+             p->num_free_objects);
       p = p->next;
     }
   printf ("NULL\n");
   fflush (stdout);
 }
 
+#ifdef USING_MMAP
 /* Allocate SIZE bytes of anonymous memory, preferably near PREF,
-   (if non-null).  */
+   (if non-null).  The ifdef structure here is intended to cause a
+   compile error unless exactly one of the HAVE_* is defined.  */
 
 static inline char *
 alloc_anon (pref, size)
      char *pref ATTRIBUTE_UNUSED;
      size_t size;
 {
-  char *page;
-
-#ifdef HAVE_MMAP
-#ifdef MAP_ANONYMOUS
-  page = (char *) mmap (pref, size, PROT_READ | PROT_WRITE,
-                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-#else
-  page = (char *) mmap (pref, size, PROT_READ | PROT_WRITE,
-                       MAP_PRIVATE, G.dev_zero_fd, 0);
+#ifdef HAVE_MMAP_ANON
+  char *page = (char *) 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);
+#endif
+
   if (page == (char *) MAP_FAILED)
     {
-      fputs ("Virtual memory exhausted!\n", stderr);
-      exit(1);
+      perror ("virtual memory exhausted");
+      exit (FATAL_EXIT_CODE);
     }
-#else
-#ifdef HAVE_VALLOC
-  page = (char *) valloc (size);
-  if (!page)
-    {
-      fputs ("Virtual memory exhausted!\n", stderr);
-      exit(1);
-    }
-#endif /* HAVE_VALLOC */
-#endif /* HAVE_MMAP */
 
   /* Remember that we allocated this memory.  */
   G.bytes_mapped += size;
 
   return page;
 }
+#endif
+#ifdef USING_MALLOC_PAGE_GROUPS
+/* Compute the index for this page into the page group.  */
+
+static inline size_t
+page_group_index (allocation, page)
+     char *allocation, *page;
+{
+  return (size_t) (page - allocation) >> G.lg_pagesize;
+}
+
+/* Set and clear the in_use bit for this page in the page group.  */
+
+static inline void
+set_page_group_in_use (group, page)
+     page_group *group;
+     char *page;
+{
+  group->in_use |= 1 << page_group_index (group->allocation, page);
+}
+
+static inline void
+clear_page_group_in_use (group, page)
+     page_group *group;
+     char *page;
+{
+  group->in_use &= ~(1 << page_group_index (group->allocation, page));
+}
+#endif
 
 /* Allocate a new page for allocating objects of size 2^ORDER,
    and return an entry for it.  The entry is not added to the
@@ -452,25 +571,35 @@ alloc_page (order)
   size_t bitmap_size;
   size_t page_entry_size;
   size_t entry_size;
+#ifdef USING_MALLOC_PAGE_GROUPS
+  page_group *group;
+#endif
 
   num_objects = OBJECTS_PER_PAGE (order);
   bitmap_size = BITMAP_SIZE (num_objects + 1);
   page_entry_size = sizeof (page_entry) - sizeof (long) + bitmap_size;
-  entry_size = num_objects * (1 << order);
+  entry_size = num_objects * OBJECT_SIZE (order);
+  if (entry_size < G.pagesize)
+    entry_size = G.pagesize;
 
   entry = NULL;
   page = NULL;
 
   /* Check the list of free pages for one we can use.  */
-  for (pp = &G.free_pages, p = *pp; p ; pp = &p->next, p = *pp)
+  for (pp = &G.free_pages, p = *pp; p; pp = &p->next, p = *pp)
     if (p->bytes == entry_size)
       break;
 
   if (p != NULL)
     {
-      /* Recycle the allocated memory from this page ... */
+      /* Recycle the allocated memory from this page ...  */
       *pp = p->next;
       page = p->page;
+
+#ifdef USING_MALLOC_PAGE_GROUPS
+      group = p->group;
+#endif
+
       /* ... and, if possible, the page entry itself.  */
       if (p->order == order)
        {
@@ -480,11 +609,105 @@ alloc_page (order)
       else
        free (p);
     }
+#ifdef USING_MMAP
+  else if (entry_size == G.pagesize)
+    {
+      /* We want just one page.  Allocate a bunch of them and put the
+        extras on the freelist.  (Can only do this optimization with
+        mmap for backing store.)  */
+      struct page_entry *e, *f = G.free_pages;
+      int i;
+
+      page = alloc_anon (NULL, G.pagesize * GGC_QUIRE_SIZE);
+
+      /* This loop counts down so that the chain will be in ascending
+        memory order.  */
+      for (i = GGC_QUIRE_SIZE - 1; i >= 1; i--)
+       {
+         e = (struct page_entry *) xcalloc (1, page_entry_size);
+         e->order = order;
+         e->bytes = G.pagesize;
+         e->page = page + (i << G.lg_pagesize);
+         e->next = f;
+         f = e;
+       }
+
+      G.free_pages = f;
+    }
+  else
+    page = alloc_anon (NULL, entry_size);
+#endif
+#ifdef USING_MALLOC_PAGE_GROUPS
   else
     {
-      /* Actually allocate the memory.  */
-      page = alloc_anon (NULL, entry_size);
+      /* Allocate a large block of memory and serve out the aligned
+        pages therein.  This results in much less memory wastage
+        than the traditional implementation of valloc.  */
+
+      char *allocation, *a, *enda;
+      size_t alloc_size, head_slop, tail_slop;
+      int multiple_pages = (entry_size == G.pagesize);
+
+      if (multiple_pages)
+       alloc_size = GGC_QUIRE_SIZE * G.pagesize;
+      else
+       alloc_size = entry_size + G.pagesize - 1;
+      allocation = xmalloc (alloc_size);
+
+      page = (char *) (((size_t) allocation + G.pagesize - 1) & -G.pagesize);
+      head_slop = page - allocation;
+      if (multiple_pages)
+       tail_slop = ((size_t) allocation + alloc_size) & (G.pagesize - 1);
+      else
+       tail_slop = alloc_size - entry_size - head_slop;
+      enda = allocation + alloc_size - tail_slop;
+
+      /* We allocated N pages, which are likely not aligned, leaving
+        us with N-1 usable pages.  We plan to place the page_group
+        structure somewhere in the slop.  */
+      if (head_slop >= sizeof (page_group))
+       group = (page_group *)page - 1;
+      else
+       {
+         /* We magically got an aligned allocation.  Too bad, we have
+            to waste a page anyway.  */
+         if (tail_slop == 0)
+           {
+             enda -= G.pagesize;
+             tail_slop += G.pagesize;
+           }
+         if (tail_slop < sizeof (page_group))
+           abort ();
+         group = (page_group *)enda;
+         tail_slop -= sizeof (page_group);
+       }
+
+      /* Remember that we allocated this memory.  */
+      group->next = G.page_groups;
+      group->allocation = allocation;
+      group->alloc_size = alloc_size;
+      group->in_use = 0;
+      G.page_groups = group;
+      G.bytes_mapped += alloc_size;
+
+      /* If we allocated multiple pages, put the rest on the free list.  */
+      if (multiple_pages)
+       {
+         struct page_entry *e, *f = G.free_pages;
+         for (a = enda - G.pagesize; a != page; a -= G.pagesize)
+           {
+             e = (struct page_entry *) xcalloc (1, page_entry_size);
+             e->order = order;
+             e->bytes = G.pagesize;
+             e->page = a;
+             e->group = group;
+             e->next = f;
+             f = e;
+           }
+         G.free_pages = f;
+       }
     }
+#endif
 
   if (entry == NULL)
     entry = (struct page_entry *) xcalloc (1, page_entry_size);
@@ -496,6 +719,11 @@ alloc_page (order)
   entry->num_free_objects = num_objects;
   entry->next_bit_hint = 1;
 
+#ifdef USING_MALLOC_PAGE_GROUPS
+  entry->group = group;
+  set_page_group_in_use (group, page);
+#endif
+
   /* Set the one-past-the-end in-use bit.  This acts as a sentry as we
      increment the hint.  */
   entry->in_use_p[num_objects / HOST_BITS_PER_LONG]
@@ -505,8 +733,9 @@ alloc_page (order)
 
   if (GGC_DEBUG_LEVEL >= 2)
     fprintf (G.debug_file, 
-            "Allocating page at %p, object size=%d, data %p-%p\n", entry,
-            1 << order, page, page + entry_size - 1);
+            "Allocating page at %p, object size=%ld, data %p-%p\n",
+            (PTR) entry, (long) OBJECT_SIZE (order), page,
+            page + entry_size - 1);
 
   return entry;
 }
@@ -519,11 +748,15 @@ free_page (entry)
 {
   if (GGC_DEBUG_LEVEL >= 2)
     fprintf (G.debug_file, 
-            "Deallocating page at %p, data %p-%p\n", entry,
+            "Deallocating page at %p, data %p-%p\n", (PTR) entry,
             entry->page, entry->page + entry->bytes - 1);
 
   set_page_table_entry (entry->page, NULL);
 
+#ifdef USING_MALLOC_PAGE_GROUPS
+  clear_page_group_in_use (entry->group, entry->page);
+#endif
+
   entry->next = G.free_pages;
   G.free_pages = entry;
 }
@@ -533,63 +766,71 @@ free_page (entry)
 static void
 release_pages ()
 {
-#ifdef HAVE_MMAP
+#ifdef USING_MMAP
   page_entry *p, *next;
   char *start;
   size_t len;
 
+  /* Gather up adjacent pages so they are unmapped together.  */
   p = G.free_pages;
-  if (p == NULL)
-    return;
-
-  next = p->next;
-  start = p->page;
-  len = p->bytes;
-  free (p);
-  p = next;
 
   while (p)
     {
+      start = p->page;
       next = p->next;
-      /* Gather up adjacent pages so they are unmapped together.  */
-      if (p->page == start + len)
-       len += p->bytes;
-      else
-       {
-         munmap (start, len);
-         G.bytes_mapped -= len;
-         start = p->page;
-         len = p->bytes;
-       }
+      len = p->bytes;
       free (p);
       p = next;
-    }
 
-  munmap (start, len);
-  G.bytes_mapped -= len;
-#else
-#ifdef HAVE_VALLOC
-  page_entry *p, *next;
+      while (p && p->page == start + len)
+       {
+         next = p->next;
+         len += p->bytes;
+         free (p);
+         p = next;
+       }
 
-  for (p = G.free_pages; p ; p = next)
-    {
-      next = p->next;
-      free (p->page);
-      G.bytes_mapped -= p->bytes;
-      free (p);
+      munmap (start, len);
+      G.bytes_mapped -= len;
     }
-#endif /* HAVE_VALLOC */
-#endif /* HAVE_MMAP */
 
   G.free_pages = NULL;
+#endif
+#ifdef USING_MALLOC_PAGE_GROUPS
+  page_entry **pp, *p;
+  page_group **gp, *g;
+
+  /* Remove all pages from free page groups from the list.  */
+  pp = &G.free_pages;
+  while ((p = *pp) != NULL)
+    if (p->group->in_use == 0)
+      {
+       *pp = p->next;
+       free (p);
+      }
+    else
+      pp = &p->next;
+
+  /* Remove all free page groups, and release the storage.  */
+  gp = &G.page_groups;
+  while ((g = *gp) != NULL)
+    if (g->in_use == 0)
+      {
+       *gp = g->next;
+        G.bytes_mapped -= g->alloc_size;
+       free (g->allocation);
+      }
+    else
+      gp = &g->next;
+#endif
 }
 
 /* This table provides a fast way to determine ceil(log_2(size)) for
-   allocation requests.  The minimum allocation size is four bytes.  */
+   allocation requests.  The minimum allocation size is eight bytes.  */
 
-static unsigned char const size_lookup[257] = 
-{ 
-  2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 
+static unsigned char size_lookup[257] = 
+{
+  3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 
   4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
   5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
   6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
@@ -612,9 +853,8 @@ static unsigned char const size_lookup[257] =
    memory is zeroed; otherwise, its contents are undefined.  */
 
 void *
-ggc_alloc_obj (size, zero)
+ggc_alloc (size)
      size_t size;
-     int zero;
 {
   unsigned order, word, bit, object_offset;
   struct page_entry *entry;
@@ -625,7 +865,7 @@ ggc_alloc_obj (size, zero)
   else
     {
       order = 9;
-      while (size > ((size_t) 1 << order))
+      while (size > OBJECT_SIZE (order))
        order++;
     }
 
@@ -680,7 +920,7 @@ ggc_alloc_obj (size, zero)
       /* Next time, try the next bit.  */
       entry->next_bit_hint = hint + 1;
 
-      object_offset = hint << order;
+      object_offset = hint * OBJECT_SIZE (order);
     }
 
   /* Set the in-use bit.  */
@@ -704,22 +944,19 @@ ggc_alloc_obj (size, zero)
   result = entry->page + object_offset;
 
 #ifdef GGC_POISON
-  /* `Poison' the entire allocated object before zeroing the requested area,
-     so that bytes beyond the end, if any, will not necessarily be zero.  */
-  memset (result, 0xaf, 1 << order);
+  /* `Poison' the entire allocated object, including any padding at
+     the end.  */
+  memset (result, 0xaf, OBJECT_SIZE (order));
 #endif
 
-  if (zero)
-    memset (result, 0, size);
-
   /* Keep track of how many bytes are being allocated.  This
      information is used in deciding when to collect.  */
-  G.allocated += (size_t) 1 << order;
+  G.allocated += OBJECT_SIZE (order);
 
   if (GGC_DEBUG_LEVEL >= 3)
     fprintf (G.debug_file, 
-            "Allocating object, requested size=%d, actual=%d at %p on %p\n",
-            (int) size, 1 << order, result, entry);
+            "Allocating object, requested size=%ld, actual=%ld at %p on %p\n",
+            (long) size, (long) OBJECT_SIZE (order), result, (PTR) entry);
 
   return result;
 }
@@ -730,7 +967,7 @@ ggc_alloc_obj (size, zero)
 
 int
 ggc_set_mark (p)
-     void *p;
+     const void *p;
 {
   page_entry *entry;
   unsigned bit, word;
@@ -746,11 +983,11 @@ ggc_set_mark (p)
 
   /* Calculate the index of the object on the page; this is its bit
      position in the in_use_p bitmap.  */
-  bit = (((char *) p) - entry->page) >> entry->order;
+  bit = (((const char *) p) - entry->page) / OBJECT_SIZE (entry->order);
   word = bit / HOST_BITS_PER_LONG;
   mask = (unsigned long) 1 << (bit % HOST_BITS_PER_LONG);
   
-  /* If the bit was previously set, skip it. */
+  /* If the bit was previously set, skip it.  */
   if (entry->in_use_p[word] & mask)
     return 1;
 
@@ -758,32 +995,49 @@ ggc_set_mark (p)
   entry->in_use_p[word] |= mask;
   entry->num_free_objects -= 1;
 
-  G.allocated += (size_t) 1 << entry->order;
-
   if (GGC_DEBUG_LEVEL >= 4)
     fprintf (G.debug_file, "Marking %p\n", p);
 
   return 0;
 }
 
-/* Mark P, but check first that it was allocated by the collector.  */
+/* Return 1 if P has been marked, zero otherwise. 
+   P must have been allocated by the GC allocator; it mustn't point to
+   static objects, stack variables, or memory allocated with malloc.  */
 
-void
-ggc_mark_if_gcable (p)
-     void *p;
+int
+ggc_marked_p (p)
+     const void *p;
 {
-  if (p && ggc_allocated_p (p))
-    ggc_set_mark (p);
+  page_entry *entry;
+  unsigned bit, word;
+  unsigned long mask;
+
+  /* Look up the page on which the object is alloced.  If the object
+     wasn't allocated by the collector, we'll probably die.  */
+  entry = lookup_page_table_entry (p);
+#ifdef ENABLE_CHECKING
+  if (entry == NULL)
+    abort ();
+#endif
+
+  /* Calculate the index of the object on the page; this is its bit
+     position in the in_use_p bitmap.  */
+  bit = (((const char *) p) - entry->page) / OBJECT_SIZE (entry->order);
+  word = bit / HOST_BITS_PER_LONG;
+  mask = (unsigned long) 1 << (bit % HOST_BITS_PER_LONG);
+  
+  return (entry->in_use_p[word] & mask) != 0;
 }
 
 /* Return the size of the gc-able object P.  */
 
 size_t
 ggc_get_size (p)
-     void *p;
+     const void *p;
 {
   page_entry *pe = lookup_page_table_entry (p);
-  return 1 << pe->order;
+  return OBJECT_SIZE (pe->order);
 }
 \f
 /* Initialize the ggc-mmap allocator.  */
@@ -791,10 +1045,12 @@ ggc_get_size (p)
 void
 init_ggc ()
 {
+  unsigned order;
+
   G.pagesize = getpagesize();
   G.lg_pagesize = exact_log2 (G.pagesize);
 
-#if defined (HAVE_MMAP) && !defined(MAP_ANONYMOUS)
+#ifdef HAVE_MMAP_DEV_ZERO
   G.dev_zero_fd = open ("/dev/zero", O_RDONLY);
   if (G.dev_zero_fd == -1)
     abort ();
@@ -808,13 +1064,14 @@ init_ggc ()
 
   G.allocated_last_gc = GGC_MIN_LAST_ALLOCATED;
 
-#ifdef HAVE_MMAP
+#ifdef USING_MMAP
   /* StunOS has an amazing off-by-one error for the first mmap allocation
      after fiddling with RLIMIT_STACK.  The result, as hard as it is to
      believe, is an unaligned page allocation, which would cause us to
      hork badly if we tried to use it.  */
   {
     char *p = alloc_anon (NULL, G.pagesize);
+    struct page_entry *e;
     if ((size_t)p & (G.pagesize - 1))
       {
        /* How losing.  Discard this one and try another.  If we still
@@ -824,12 +1081,50 @@ init_ggc ()
        if ((size_t)p & (G.pagesize - 1))
          abort ();
       }
-    munmap (p, G.pagesize);
+
+    /* We have a good page, might as well hold onto it...  */
+    e = (struct page_entry *) xcalloc (1, sizeof (struct page_entry));
+    e->bytes = G.pagesize;
+    e->page = p;
+    e->next = G.free_pages;
+    G.free_pages = e;
   }
 #endif
 
-  empty_string = ggc_alloc_string ("", 0);
-  ggc_add_string_root (&empty_string, 1);
+  /* Initialize the object size table.  */
+  for (order = 0; order < HOST_BITS_PER_PTR; ++order)
+    object_size_table[order] = (size_t) 1 << order;
+  for (order = HOST_BITS_PER_PTR; order < NUM_ORDERS; ++order)
+    {
+      size_t s = extra_order_size_table[order - HOST_BITS_PER_PTR];
+
+      /* If S is not a multiple of the MAX_ALIGNMENT, then round it up
+        so that we're sure of getting aligned memory.  */
+      s = CEIL (s, MAX_ALIGNMENT) * MAX_ALIGNMENT;
+      object_size_table[order] = s;
+    }
+
+  /* Initialize the objects-per-page table.  */
+  for (order = 0; order < NUM_ORDERS; ++order)
+    {
+      objects_per_page_table[order] = G.pagesize / OBJECT_SIZE (order);
+      if (objects_per_page_table[order] == 0)
+       objects_per_page_table[order] = 1;
+    }
+
+  /* Reset the size_lookup array to put appropriately sized objects in
+     the special orders.  All objects bigger than the previous power
+     of two, but no greater than the special size, should go in the
+     new order.  */
+  for (order = HOST_BITS_PER_PTR; order < NUM_ORDERS; ++order)
+    {
+      int o;
+      int i;
+
+      o = size_lookup[OBJECT_SIZE (order)];
+      for (i = OBJECT_SIZE (order); size_lookup [i] == o; --i)
+       size_lookup[i] = order;
+    }
 }
 
 /* Increment the `GC context'.  Objects allocated in an outer context
@@ -864,8 +1159,8 @@ ggc_recalculate_in_use_p (p)
 
   /* Combine the IN_USE_P and SAVE_IN_USE_P arrays.  */
   for (i = 0; 
-       i < DIV_ROUND_UP (BITMAP_SIZE (num_objects),
-                        sizeof (*p->in_use_p));
+       i < CEIL (BITMAP_SIZE (num_objects),
+                sizeof (*p->in_use_p));
        ++i)
     {
       unsigned long j;
@@ -896,7 +1191,7 @@ ggc_pop_context ()
   /* Any remaining pages in the popped context are lowered to the new
      current context; i.e. objects allocated in the popped context and
      left over are imported into the previous context.  */
-  for (order = 2; order < HOST_BITS_PER_PTR; order++)
+  for (order = 2; order < NUM_ORDERS; order++)
     {
       page_entry *p;
 
@@ -924,7 +1219,7 @@ clear_marks ()
 {
   unsigned order;
 
-  for (order = 2; order < HOST_BITS_PER_PTR; order++)
+  for (order = 2; order < NUM_ORDERS; order++)
     {
       size_t num_objects = OBJECTS_PER_PAGE (order);
       size_t bitmap_size = BITMAP_SIZE (num_objects + 1);
@@ -968,13 +1263,14 @@ sweep_pages ()
 {
   unsigned order;
 
-  for (order = 2; order < HOST_BITS_PER_PTR; order++)
+  for (order = 2; order < NUM_ORDERS; order++)
     {
       /* The last page-entry to consider, regardless of entries
         placed at the end of the list.  */
       page_entry * const last = G.page_tails[order];
 
       size_t num_objects = OBJECTS_PER_PAGE (order);
+      size_t live_objects;
       page_entry *p, *previous;
       int done;
        
@@ -990,13 +1286,19 @@ sweep_pages ()
          /* Loop until all entries have been examined.  */
          done = (p == last);
 
+         /* Add all live objects on this page to the count of
+             allocated memory.  */
+         live_objects = num_objects - p->num_free_objects;
+
+         G.allocated += OBJECT_SIZE (order) * live_objects;
+
          /* Only objects on pages in the topmost context should get
             collected.  */
          if (p->context_depth < G.context_depth)
            ;
 
          /* Remove the page if it's empty.  */
-         else if (p->num_free_objects == num_objects)
+         else if (live_objects == 0)
            {
              if (! previous)
                G.pages[order] = next;
@@ -1068,10 +1370,10 @@ poison_pages ()
 {
   unsigned order;
 
-  for (order = 2; order < HOST_BITS_PER_PTR; order++)
+  for (order = 2; order < NUM_ORDERS; order++)
     {
       size_t num_objects = OBJECTS_PER_PAGE (order);
-      size_t size = (size_t) 1 << order;
+      size_t size = OBJECT_SIZE (order);
       page_entry *p;
 
       for (p = G.pages[order]; p != NULL; p = p->next)
@@ -1103,8 +1405,6 @@ poison_pages ()
 void
 ggc_collect ()
 {
-  int time;
-
   /* Avoid frequent unnecessary work by skipping collection if the
      total allocations haven't expanded much since the last
      collection.  */
@@ -1113,12 +1413,12 @@ ggc_collect ()
     return;
 #endif
 
-  time = get_run_time ();
+  timevar_push (TV_GC);
   if (!quiet_flag)
-    fprintf (stderr, " {GC %luk -> ", (unsigned long)G.allocated / 1024);
+    fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated / 1024);
 
-  /* Zero the total allocated bytes.  We'll reaccumulate this while
-     marking.  */
+  /* Zero the total allocated bytes.  This will be recalculated in the
+     sweep phase.  */
   G.allocated = 0;
 
   /* Release the pages we freed the last time we collected, but didn't 
@@ -1138,32 +1438,35 @@ ggc_collect ()
   if (G.allocated_last_gc < GGC_MIN_LAST_ALLOCATED)
     G.allocated_last_gc = GGC_MIN_LAST_ALLOCATED;
 
-  time = get_run_time () - time;
-  gc_time += time;
+  timevar_pop (TV_GC);
 
   if (!quiet_flag)
-    {
-      fprintf (stderr, "%luk in %.3f}", 
-              (unsigned long) G.allocated / 1024, time * 1e-6);
-    }
+    fprintf (stderr, "%luk}", (unsigned long) G.allocated / 1024);
 }
 
 /* Print allocation statistics.  */
+#define SCALE(x) ((unsigned long) ((x) < 1024*10 \
+                 ? (x) \
+                 : ((x) < 1024*1024*10 \
+                    ? (x) / 1024 \
+                    : (x) / (1024*1024))))
+#define LABEL(x) ((x) < 1024*10 ? ' ' : ((x) < 1024*1024*10 ? 'k' : 'M'))
 
 void
-ggc_page_print_statistics ()
+ggc_print_statistics ()
 {
   struct ggc_statistics stats;
   unsigned int i;
+  size_t total_overhead = 0;
 
   /* Clear the statistics.  */
-  bzero (&stats, sizeof (stats));
+  memset (&stats, 0, sizeof (stats));
   
   /* Make sure collection will really occur.  */
   G.allocated_last_gc = 0;
 
   /* Collect and print the statistics common across collectors.  */
-  ggc_print_statistics (stderr, &stats);
+  ggc_print_common_statistics (stderr, &stats);
 
   /* Release free pages so that we will not count the bytes allocated
      there as part of the total allocated memory.  */
@@ -1171,34 +1474,41 @@ ggc_page_print_statistics ()
 
   /* Collect some information about the various sizes of 
      allocation.  */
-  fprintf (stderr, "\n%-4s%-16s%-16s\n", "Log", "Allocated", "Used");
-  for (i = 0; i < HOST_BITS_PER_PTR; ++i)
+  fprintf (stderr, "\n%-5s %10s  %10s  %10s\n",
+          "Size", "Allocated", "Used", "Overhead");
+  for (i = 0; i < NUM_ORDERS; ++i)
     {
       page_entry *p;
       size_t allocated;
       size_t in_use;
+      size_t overhead;
 
       /* Skip empty entries.  */
       if (!G.pages[i])
        continue;
 
-      allocated = in_use = 0;
+      overhead = allocated = in_use = 0;
 
       /* Figure out the total number of bytes allocated for objects of
-        this size, and how many of them are actually in use.  */
+        this size, and how many of them are actually in use.  Also figure
+        out how much memory the page table is using.  */
       for (p = G.pages[i]; p; p = p->next)
        {
          allocated += p->bytes;
          in_use += 
-           (OBJECTS_PER_PAGE (i) - p->num_free_objects) * (1 << i);
+           (OBJECTS_PER_PAGE (i) - p->num_free_objects) * OBJECT_SIZE (i);
+
+         overhead += (sizeof (page_entry) - sizeof (long)
+                      + BITMAP_SIZE (OBJECTS_PER_PAGE (i) + 1));
        }
-      fprintf (stderr, "%-3d %-15lu %-15lu\n", i, 
-              (unsigned long) allocated, (unsigned long) in_use);
+      fprintf (stderr, "%-5d %10ld%c %10ld%c %10ld%c\n", OBJECT_SIZE (i),
+              SCALE (allocated), LABEL (allocated),
+              SCALE (in_use), LABEL (in_use),
+              SCALE (overhead), LABEL (overhead));
+      total_overhead += overhead;
     }
-
-  /* Print out some global information.  */
-  fprintf (stderr, "\nTotal bytes marked: %lu\n", 
-          (unsigned long) G.allocated);
-  fprintf (stderr, "Total bytes mapped: %lu\n", 
-          (unsigned long) G.bytes_mapped);
+  fprintf (stderr, "%-5s %10ld%c %10ld%c %10ld%c\n", "Total",
+          SCALE (G.bytes_mapped), LABEL (G.bytes_mapped),
+          SCALE (G.allocated), LABEL(G.allocated),
+          SCALE (total_overhead), LABEL (total_overhead));
 }