OSDN Git Service

* tree.c (max_int_size_in_bytes): New function, inspired from
[pf3gnuchains/gcc-fork.git] / gcc / ggc-page.c
index b395aaa..ae671db 100644 (file)
@@ -1,47 +1,81 @@
 /* "Bag-of-pages" garbage collector for the GNU compiler.
-   Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
+   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.  */
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.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"
+#include "params.h"
+#include "tree-flow.h"
+#ifdef ENABLE_VALGRIND_CHECKING
+# ifdef HAVE_VALGRIND_MEMCHECK_H
+#  include <valgrind/memcheck.h>
+# elif defined HAVE_MEMCHECK_H
+#  include <memcheck.h>
+# else
+#  include <valgrind.h>
+# endif
+#else
+/* Avoid #ifdef:s when we can help it.  */
+#define VALGRIND_DISCARD(x)
+#endif
+
+/* 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_ANYWHERE
-#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: 
+/* Strategy:
 
    This garbage-collecting allocator allocates objects on one of a set
    of pages.  Each page can allocate objects of a single size only;
@@ -56,7 +90,7 @@ Boston, MA 02111-1307, USA.  */
 
    Each page-entry also has a context depth, which is used to track
    pushing and popping of allocation contexts.  Only objects allocated
-   in the current (highest-numbered) context may be collected.  
+   in the current (highest-numbered) context may be collected.
 
    Page entries are arranged in an array of singly-linked lists.  The
    array is indexed by the allocation size, in bits, of the pages on
@@ -70,37 +104,18 @@ Boston, MA 02111-1307, USA.  */
    deallocated at the start of the next collection if they haven't
    been recycled by then.  */
 
-
-/* Define GGC_POISON to poison memory marked unused by the collector.  */
-#undef GGC_POISON
-
-/* Define GGC_ALWAYS_COLLECT to perform collection every time
-   ggc_collect is invoked.  Otherwise, collection is performed only
-   when a significant amount of memory has been allocated since the
-   last collection.  */
-#undef GGC_ALWAYS_COLLECT
-
-#ifdef ENABLE_GC_CHECKING
-#define GGC_POISON
-#endif
-#ifdef ENABLE_GC_ALWAYS_COLLECT
-#define GGC_ALWAYS_COLLECT
-#endif
-
 /* Define GGC_DEBUG_LEVEL to print debugging information.
      0: No debugging output.
      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
 
-/* 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
@@ -117,7 +132,7 @@ char *empty_string;
    The bottommost HOST_PAGE_SIZE_BITS are ignored, since page-entry
    pages are aligned on system page boundaries.  The next most
    significant PAGE_L2_BITS and PAGE_L1_BITS are the second and first
-   index values in the lookup table, respectively.  
+   index values in the lookup table, respectively.
 
    For 32-bit architectures and the settings below, there are no
    leftover bits.  For architectures with wider pointers, the lookup
@@ -135,15 +150,123 @@ 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 number of objects in P.  */
+#define OBJECTS_IN_PAGE(P) ((P)->bytes / OBJECT_SIZE ((P)->order))
+
+/* The size of an object on a page of the indicated ORDER.  */
+#define OBJECT_SIZE(ORDER) object_size_table[ORDER]
+
+/* For speed, we avoid doing a general integer divide to locate the
+   offset in the allocation bitmap, by precalculating numbers M, S
+   such that (O * M) >> S == O / Z (modulo 2^32), for any offset O
+   within the page which is evenly divisible by the object size Z.  */
+#define DIV_MULT(ORDER) inverse_table[ORDER].mult
+#define DIV_SHIFT(ORDER) inverse_table[ORDER].shift
+#define OFFSET_TO_BIT(OFFSET, ORDER) \
+  (((OFFSET) * DIV_MULT (ORDER)) >> DIV_SHIFT (ORDER))
+
+/* The number of extra orders, not corresponding to power-of-two sized
+   objects.  */
+
+#define NUM_EXTRA_ORDERS ARRAY_SIZE (extra_order_size_table)
+
+#define RTL_SIZE(NSLOTS) \
+  (RTX_HDR_SIZE + (NSLOTS) * sizeof (rtunion))
+
+#define TREE_EXP_SIZE(OPS) \
+  (sizeof (struct tree_exp) + ((OPS) - 1) * sizeof (tree))
+
+/* 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 stmt_ann_d),
+  sizeof (struct var_ann_d),
+  sizeof (struct tree_decl_non_common),
+  sizeof (struct tree_field_decl),
+  sizeof (struct tree_parm_decl),
+  sizeof (struct tree_var_decl),
+  sizeof (struct tree_list),
+  sizeof (struct tree_ssa_name),
+  sizeof (struct tree_function_decl),
+  sizeof (struct tree_binfo),
+  sizeof (struct function),
+  sizeof (struct basic_block_def),
+  sizeof (bitmap_element),
+  /* PHI nodes with one to three arguments are already covered by the
+     above sizes.  */
+  sizeof (struct tree_phi_node) + sizeof (struct phi_arg_d) * 3,
+  TREE_EXP_SIZE (2),
+  RTL_SIZE (2),                        /* MEM, PLUS, etc.  */
+  RTL_SIZE (9),                        /* INSN */
+};
+
+/* 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;
+    long double d;
+  } u;
+};
+
+/* The biggest alignment required.  */
+
+#define MAX_ALIGNMENT (offsetof (struct max_alignment, u))
+
+/* Compute the smallest nonnegative number which when added to X gives
+   a multiple of F.  */
+
+#define ROUND_UP_VALUE(x, f) ((f) - 1 - ((f) - 1 + (x)) % (f))
+
+/* Compute the smallest multiple of F that is >= X.  */
+
+#define ROUND_UP(x, f) (CEIL (x, f) * (f))
+
+/* 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];
+
+/* The Ith entry is a pair of numbers (mult, shift) such that
+   ((k * mult) >> shift) mod 2^32 == (k / OBJECT_SIZE(I)) mod 2^32,
+   for all k evenly divisible by OBJECT_SIZE(I).  */
+
+static struct
+{
+  size_t mult;
+  unsigned int shift;
+}
+inverse_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.  */
-typedef struct page_entry 
+typedef struct page_entry
 {
   /* The next page-entry with objects of the same size, or NULL if
      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;
@@ -151,9 +274,14 @@ typedef struct page_entry
   /* The address at which the memory is allocated.  */
   char *page;
 
-  /* Saved in-use bit vector for pages that aren't in the topmost
-     context during collection.  */
-  unsigned long *save_in_use_p;
+#ifdef USING_MALLOC_PAGE_GROUPS
+  /* Back pointer to the page group this page came from.  */
+  struct page_group *group;
+#endif
+
+  /* This is the index in the by_depth varray where this page table
+     can be found.  */
+  unsigned long index_by_depth;
 
   /* Context depth of this page.  */
   unsigned short context_depth;
@@ -174,6 +302,24 @@ typedef struct page_entry
   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
 
@@ -201,12 +347,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;
@@ -224,73 +370,190 @@ static struct globals
   /* Total amount of memory mapped.  */
   size_t bytes_mapped;
 
+  /* Bit N set if any allocations have been done at context depth N.  */
+  unsigned long context_depth_allocations;
+
+  /* Bit N set if any collections have been done at context depth N.  */
+  unsigned long context_depth_collections;
+
   /* The current depth in the context stack.  */
   unsigned short context_depth;
 
   /* A file descriptor open to /dev/zero for reading.  */
-#if defined (HAVE_MMAP_ANYWHERE) && !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;
 
+  /* Current number of elements in use in depth below.  */
+  unsigned int depth_in_use;
+
+  /* Maximum number of elements that can be used before resizing.  */
+  unsigned int depth_max;
+
+  /* Each element of this arry is an index in by_depth where the given
+     depth starts.  This structure is indexed by that given depth we
+     are interested in.  */
+  unsigned int *depth;
+
+  /* Current number of elements in use in by_depth below.  */
+  unsigned int by_depth_in_use;
+
+  /* Maximum number of elements that can be used before resizing.  */
+  unsigned int by_depth_max;
+
+  /* Each element of this array is a pointer to a page_entry, all
+     page_entries can be found in here by increasing depth.
+     index_by_depth in the page_entry is the index into this data
+     structure where that page_entry can be found.  This is used to
+     speed up finding all page_entries at a particular depth.  */
+  page_entry **by_depth;
 
-/* Compute DIVIDEND / DIVISOR, rounded up.  */
-#define DIV_ROUND_UP(Dividend, Divisor) \
-  (((Dividend) + (Divisor) - 1) / (Divisor))
+  /* Each element is a pointer to the saved in_use_p bits, if any,
+     zero otherwise.  We allocate them all together, to enable a
+     better runtime data access pattern.  */
+  unsigned long **save_in_use;
 
-/* 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)))
+#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
+  {
+    /* Total memory allocated with ggc_alloc.  */
+    unsigned long long total_allocated;
+    /* Total overhead for memory to be allocated with ggc_alloc.  */
+    unsigned long long total_overhead;
+
+    /* Total allocations and overhead for sizes less than 32, 64 and 128.
+       These sizes are interesting because they are typical cache line
+       sizes.  */
+   
+    unsigned long long total_allocated_under32;
+    unsigned long long total_overhead_under32;
+  
+    unsigned long long total_allocated_under64;
+    unsigned long long total_overhead_under64;
+  
+    unsigned long long total_allocated_under128;
+    unsigned long long total_overhead_under128;
+  
+    /* The allocations for each of the allocation orders.  */
+    unsigned long long total_allocated_per_order[NUM_ORDERS];
+
+    /* The overhead for each of the allocation orders.  */
+    unsigned long long total_overhead_per_order[NUM_ORDERS];
+  } stats;
+#endif
+} G;
 
 /* 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))
+
+/* 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.  Hosts that need a different value
+   can override this by defining GGC_QUIRE_SIZE explicitly.  */
+#ifndef GGC_QUIRE_SIZE
+# ifdef USING_MMAP
+#  define GGC_QUIRE_SIZE 256
+# else
+#  define GGC_QUIRE_SIZE 16
+# endif
+#endif
 
-/* Skip garbage collection if the current allocation is not at least
-   this factor times the allocation at the end of the last collection.
-   In other words, total allocation must expand by (this factor minus
-   one) before collection is performed.  */
-#define GGC_MIN_EXPAND_FOR_GC (1.3)
+/* Initial guess as to how many page table entries we might need.  */
+#define INITIAL_PTE_COUNT 128
+\f
+static int ggc_allocated_p (const void *);
+static page_entry *lookup_page_table_entry (const void *);
+static void set_page_table_entry (void *, page_entry *);
+#ifdef USING_MMAP
+static char *alloc_anon (char *, size_t);
+#endif
+#ifdef USING_MALLOC_PAGE_GROUPS
+static size_t page_group_index (char *, char *);
+static void set_page_group_in_use (page_group *, char *);
+static void clear_page_group_in_use (page_group *, char *);
+#endif
+static struct page_entry * alloc_page (unsigned);
+static void free_page (struct page_entry *);
+static void release_pages (void);
+static void clear_marks (void);
+static void sweep_pages (void);
+static void ggc_recalculate_in_use_p (page_entry *);
+static void compute_inverse (unsigned);
+static inline void adjust_depth (void);
+static void move_ptes_to_front (int, int);
+
+void debug_print_page_list (int);
+static void push_depth (unsigned int);
+static void push_by_depth (page_entry *, unsigned long *);
+
+/* Push an entry onto G.depth.  */
+
+inline static void
+push_depth (unsigned int i)
+{
+  if (G.depth_in_use >= G.depth_max)
+    {
+      G.depth_max *= 2;
+      G.depth = xrealloc (G.depth, G.depth_max * sizeof (unsigned int));
+    }
+  G.depth[G.depth_in_use++] = i;
+}
 
-/* Bound `allocated_last_gc' to 4MB, to prevent the memory expansion
-   test from triggering too often when the heap is small.  */
-#define GGC_MIN_LAST_ALLOCATED (4 * 1024 * 1024)
+/* Push an entry onto G.by_depth and G.save_in_use.  */
 
-/* Allocate pages in chunks of this size, to throttle calls to mmap.
-   The first page is used, the rest go onto the free list.  */
-#define GGC_QUIRE_SIZE 16
+inline static void
+push_by_depth (page_entry *p, unsigned long *s)
+{
+  if (G.by_depth_in_use >= G.by_depth_max)
+    {
+      G.by_depth_max *= 2;
+      G.by_depth = xrealloc (G.by_depth,
+                            G.by_depth_max * sizeof (page_entry *));
+      G.save_in_use = xrealloc (G.save_in_use,
+                               G.by_depth_max * sizeof (unsigned long *));
+    }
+  G.by_depth[G.by_depth_in_use] = p;
+  G.save_in_use[G.by_depth_in_use++] = s;
+}
 
-\f
-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 *));
-static char *alloc_anon PARAMS ((char *, size_t));
-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 PARAMS ((void));
+#if (GCC_VERSION < 3001)
+#define prefetch(X) ((void) X)
+#else
+#define prefetch(X) __builtin_prefetch (X)
 #endif
 
-void debug_print_page_list PARAMS ((int));
-\f
-/* Returns non-zero if P was allocated in GC'able memory.  */
+#define save_in_use_p_i(__i) \
+  (G.save_in_use[__i])
+#define save_in_use_p(__p) \
+  (save_in_use_p_i (__p->index_by_depth))
+
+/* Returns nonzero if P was allocated in GC'able memory.  */
 
 static inline int
-ggc_allocated_p (p)
-     const void *p;
+ggc_allocated_p (const void *p)
 {
   page_entry ***base;
   size_t L1, L2;
@@ -311,19 +574,18 @@ 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);
 
   return base[L1] && base[L1][L2];
 }
 
-/* Traverse the page table and find the entry for a page. 
+/* Traverse the page table and find the entry for a page.
    Die (probably) if the object wasn't allocated via GC.  */
 
 static inline page_entry *
-lookup_page_table_entry(p)
-     const void *p;
+lookup_page_table_entry (const void *p)
 {
   page_entry ***base;
   size_t L1, L2;
@@ -338,7 +600,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);
 
@@ -348,9 +610,7 @@ lookup_page_table_entry(p)
 /* Set the page table entry for a page.  */
 
 static void
-set_page_table_entry(p, entry)
-     void *p;
-     page_entry *entry;
+set_page_table_entry (void *p, page_entry *entry)
 {
   page_entry ***base;
   size_t L1, L2;
@@ -365,7 +625,7 @@ set_page_table_entry(p, entry)
       goto found;
 
   /* Not found -- allocate a new table.  */
-  table = (page_table) xcalloc (1, sizeof(*table));
+  table = xcalloc (1, sizeof(*table));
   table->next = G.lookup;
   table->high_bits = high_bits;
   G.lookup = table;
@@ -373,12 +633,12 @@ 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);
 
   if (base[L1] == NULL)
-    base[L1] = (page_entry **) xcalloc (PAGE_L2_SIZE, sizeof (page_entry *));
+    base[L1] = XCNEWVEC (page_entry *, PAGE_L2_SIZE);
 
   base[L1][L2] = entry;
 }
@@ -386,16 +646,15 @@ found:
 /* Prints the page-entry for object size ORDER, for debugging.  */
 
 void
-debug_print_page_list (order)
-     int order;
+debug_print_page_list (int order)
 {
   page_entry *p;
-  printf ("Head=%p, Tail=%p:\n", (PTR) G.pages[order],
-         (PTR) G.page_tails[order]);
+  printf ("Head=%p, Tail=%p:\n", (void *) G.pages[order],
+         (void *) G.page_tails[order]);
   p = G.pages[order];
   while (p != NULL)
     {
-      printf ("%p(%1d|%3d) -> ", (PTR) p, p->context_depth,
+      printf ("%p(%1d|%3d) -> ", (void *) p, p->context_depth,
              p->num_free_objects);
       p = p->next;
     }
@@ -403,53 +662,70 @@ debug_print_page_list (order)
   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;
+alloc_anon (char *pref ATTRIBUTE_UNUSED, size_t size)
 {
-  char *page;
-
-#ifdef HAVE_MMAP_ANYWHERE
-#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 = mmap (pref, size, PROT_READ | PROT_WRITE,
+                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+#endif
+#ifdef HAVE_MMAP_DEV_ZERO
+  char *page = 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_ANYWHERE */
 
   /* Remember that we allocated this memory.  */
   G.bytes_mapped += size;
 
+  /* Pretend we don't have access to the allocated pages.  We'll enable
+     access to smaller pieces of the area in ggc_alloc.  Discard the
+     handle to avoid handle leak.  */
+  VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (page, 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 (char *allocation, char *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 (page_group *group, char *page)
+{
+  group->in_use |= 1 << page_group_index (group->allocation, page);
+}
+
+static inline void
+clear_page_group_in_use (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
    appropriate page_table list.  */
 
 static inline struct page_entry *
-alloc_page (order)
-     unsigned order;
+alloc_page (unsigned order)
 {
   struct page_entry *entry, *p, **pp;
   char *page;
@@ -457,25 +733,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)
        {
@@ -485,7 +771,7 @@ alloc_page (order)
       else
        free (p);
     }
-#ifdef HAVE_MMAP_ANYWHERE
+#ifdef USING_MMAP
   else if (entry_size == G.pagesize)
     {
       /* We want just one page.  Allocate a bunch of them and put the
@@ -494,25 +780,98 @@ alloc_page (order)
       struct page_entry *e, *f = G.free_pages;
       int i;
 
-      page = alloc_anon (NULL, entry_size * GGC_QUIRE_SIZE);
+      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, sizeof (struct page_entry));
-         e->bytes = entry_size;
-         e->page = page + i*entry_size;
+         e = 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;
     }
-#endif
   else
     page = alloc_anon (NULL, entry_size);
+#endif
+#ifdef USING_MALLOC_PAGE_GROUPS
+  else
+    {
+      /* 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;
+           }
+         gcc_assert (tail_slop >= sizeof (page_group));
+         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 = 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);
+    entry = xcalloc (1, page_entry_size);
 
   entry->bytes = entry_size;
   entry->page = page;
@@ -521,6 +880,13 @@ alloc_page (order)
   entry->num_free_objects = num_objects;
   entry->next_bit_hint = 1;
 
+  G.context_depth_allocations |= (unsigned long)1 << G.context_depth;
+
+#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]
@@ -529,26 +895,72 @@ alloc_page (order)
   set_page_table_entry (page, entry);
 
   if (GGC_DEBUG_LEVEL >= 2)
-    fprintf (G.debug_file, 
-            "Allocating page at %p, object size=%d, data %p-%p\n",
-            (PTR) entry, 1 << order, page, page + entry_size - 1);
+    fprintf (G.debug_file,
+            "Allocating page at %p, object size=%lu, data %p-%p\n",
+            (void *) entry, (unsigned long) OBJECT_SIZE (order), page,
+            page + entry_size - 1);
 
   return entry;
 }
 
-/* For a page that is no longer needed, put it on the free page list.  */
+/* Adjust the size of G.depth so that no index greater than the one
+   used by the top of the G.by_depth is used.  */
 
 static inline void
-free_page (entry)
-     page_entry *entry;
+adjust_depth (void)
+{
+  page_entry *top;
+
+  if (G.by_depth_in_use)
+    {
+      top = G.by_depth[G.by_depth_in_use-1];
+
+      /* Peel back indices in depth that index into by_depth, so that
+        as new elements are added to by_depth, we note the indices
+        of those elements, if they are for new context depths.  */
+      while (G.depth_in_use > (size_t)top->context_depth+1)
+       --G.depth_in_use;
+    }
+}
+
+/* For a page that is no longer needed, put it on the free page list.  */
+
+static void
+free_page (page_entry *entry)
 {
   if (GGC_DEBUG_LEVEL >= 2)
-    fprintf (G.debug_file, 
-            "Deallocating page at %p, data %p-%p\n", (PTR) entry,
+    fprintf (G.debug_file,
+            "Deallocating page at %p, data %p-%p\n", (void *) entry,
             entry->page, entry->page + entry->bytes - 1);
 
+  /* Mark the page as inaccessible.  Discard the handle to avoid handle
+     leak.  */
+  VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (entry->page, entry->bytes));
+
   set_page_table_entry (entry->page, NULL);
 
+#ifdef USING_MALLOC_PAGE_GROUPS
+  clear_page_group_in_use (entry->group, entry->page);
+#endif
+
+  if (G.by_depth_in_use > 1)
+    {
+      page_entry *top = G.by_depth[G.by_depth_in_use-1];
+      int i = entry->index_by_depth;
+
+      /* We cannot free a page from a context deeper than the current
+        one.  */
+      gcc_assert (entry->context_depth == top->context_depth);
+      
+      /* Put top element into freed slot.  */
+      G.by_depth[i] = top;
+      G.save_in_use[i] = G.save_in_use[G.by_depth_in_use-1];
+      top->index_by_depth = i;
+    }
+  --G.by_depth_in_use;
+
+  adjust_depth ();
+
   entry->next = G.free_pages;
   G.free_pages = entry;
 }
@@ -556,11 +968,10 @@ free_page (entry)
 /* Release the free page cache to the system.  */
 
 static void
-release_pages ()
+release_pages (void)
 {
+#ifdef USING_MMAP
   page_entry *p, *next;
-
-#ifdef HAVE_MMAP_ANYWHERE
   char *start;
   size_t len;
 
@@ -586,35 +997,51 @@ release_pages ()
       munmap (start, len);
       G.bytes_mapped -= len;
     }
-#else
-#ifdef HAVE_VALLOC
-
-  for (p = G.free_pages; p; p = next)
-    {
-      next = p->next;
-      free (p->page);
-      G.bytes_mapped -= p->bytes;
-      free (p);
-    }
-#endif /* HAVE_VALLOC */
-#endif /* HAVE_MMAP_ANYWHERE */
 
   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.  */
-
-static unsigned char const size_lookup[257] = 
-{ 
-  2, 2, 2, 2, 2, 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, 
-  6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 
-  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 
+   allocation requests.  The minimum allocation size is eight bytes.  */
+
+static unsigned char size_lookup[512] =
+{
+  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,
+  6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
   7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 
   7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
   8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
   8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
@@ -623,26 +1050,51 @@ static unsigned char const size_lookup[257] =
   8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
   8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
   8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
-  8
+  8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+  9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9
 };
 
-/* Allocate a chunk of memory of SIZE bytes.  If ZERO is non-zero, the
-   memory is zeroed; otherwise, its contents are undefined.  */
+/* Typed allocation function.  Does nothing special in this collector.  */
+
+void *
+ggc_alloc_typed_stat (enum gt_types_enum type ATTRIBUTE_UNUSED, size_t size
+                     MEM_STAT_DECL)
+{
+  return ggc_alloc_stat (size PASS_MEM_STAT);
+}
+
+/* Allocate a chunk of memory of SIZE bytes.  Its contents are undefined.  */
 
 void *
-ggc_alloc (size)
-     size_t size;
+ggc_alloc_stat (size_t size MEM_STAT_DECL)
 {
-  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];
+  if (size < 512)
+    {
+      order = size_lookup[size];
+      object_size = OBJECT_SIZE (order);
+    }
   else
     {
-      order = 9;
-      while (size > ((size_t) 1 << order))
+      order = 10;
+      while (size > (object_size = OBJECT_SIZE (order)))
        order++;
     }
 
@@ -656,13 +1108,27 @@ ggc_alloc (size)
     {
       struct page_entry *new_entry;
       new_entry = alloc_page (order);
-      
-      /* If this is the only entry, it's also the tail.  */
+
+      new_entry->index_by_depth = G.by_depth_in_use;
+      push_by_depth (new_entry, 0);
+
+      /* We can skip context depths, if we do, make sure we go all the
+        way to the new depth.  */
+      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 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;
-     
-      /* Put new pages at the head of the page list.  */
+      else
+       entry->prev = new_entry;
+
+      /* 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;
 
@@ -682,22 +1148,28 @@ ggc_alloc (size)
       unsigned hint = entry->next_bit_hint;
       word = hint / HOST_BITS_PER_LONG;
       bit = hint % HOST_BITS_PER_LONG;
-      
+
       /* If the hint didn't work, scan the bitmap from the beginning.  */
       if ((entry->in_use_p[word] >> bit) & 1)
        {
          word = bit = 0;
          while (~entry->in_use_p[word] == 0)
            ++word;
+
+#if GCC_VERSION >= 3004
+         bit = __builtin_ctzl (~entry->in_use_p[word]);
+#else
          while ((entry->in_use_p[word] >> bit) & 1)
            ++bit;
+#endif
+
          hint = word * HOST_BITS_PER_LONG + bit;
        }
 
       /* Next time, try the next bit.  */
       entry->next_bit_hint = hint + 1;
 
-      object_offset = hint << order;
+      object_offset = hint * object_size;
     }
 
   /* Set the in-use bit.  */
@@ -711,29 +1183,89 @@ ggc_alloc (size)
       && 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;
     }
 
   /* Calculate the object's address.  */
   result = entry->page + object_offset;
+#ifdef GATHER_STATISTICS
+  ggc_record_overhead (OBJECT_SIZE (order), OBJECT_SIZE (order) - size,
+                      result PASS_MEM_STAT);
+#endif
+
+#ifdef ENABLE_GC_CHECKING
+  /* Keep poisoning-by-writing-0xaf the object, in an attempt to keep the
+     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));
 
-#ifdef GGC_POISON
   /* `Poison' the entire allocated object, including any padding at
      the end.  */
-  memset (result, 0xaf, 1 << 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 - size));
 #endif
 
+  /* Tell Valgrind that the memory is there, but its content isn't
+     defined.  The bytes at the end of the object are still marked
+     unaccessible.  */
+  VALGRIND_DISCARD (VALGRIND_MAKE_WRITABLE (result, 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;
+
+  /* For timevar statistics.  */
+  timevar_ggc_mem_total += object_size;
+
+#ifdef GATHER_STATISTICS
+  {
+    size_t overhead = object_size - size;
+
+    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=%d, actual=%d at %p on %p\n",
-            (int) size, 1 << order, result, (PTR) entry);
+    fprintf (G.debug_file,
+            "Allocating object, requested size=%lu, actual=%lu at %p on %p\n",
+            (unsigned long) size, (unsigned long) object_size, result,
+            (void *) entry);
 
   return result;
 }
@@ -743,8 +1275,7 @@ ggc_alloc (size)
    static objects, stack variables, or memory allocated with malloc.  */
 
 int
-ggc_set_mark (p)
-     const void *p;
+ggc_set_mark (const void *p)
 {
   page_entry *entry;
   unsigned bit, word;
@@ -753,18 +1284,15 @@ ggc_set_mark (p)
   /* 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
+  gcc_assert (entry);
 
   /* 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) >> entry->order;
+  bit = OFFSET_TO_BIT (((const char *) p) - entry->page, 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;
 
@@ -778,38 +1306,171 @@ ggc_set_mark (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)
-     const void *p;
+int
+ggc_marked_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);
+  gcc_assert (entry);
+
+  /* Calculate the index of the object on the page; this is its bit
+     position in the in_use_p bitmap.  */
+  bit = OFFSET_TO_BIT (((const char *) p) - entry->page, 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)
-     const void *p;
+ggc_get_size (const void *p)
 {
   page_entry *pe = lookup_page_table_entry (p);
-  return 1 << pe->order;
+  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);
+
+#ifdef GATHER_STATISTICS
+  ggc_free_overhead (p);
+#endif
+
+  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 = XNEW (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)
+      {
+       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. 
+
+          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;
+         }
+
+       /* Reset the hint bit to point to the only free object.  */
+       pe->next_bit_hint = bit_offset;
+      }
+  }
+#endif
 }
 \f
-/* Initialize the ggc-mmap allocator.  */
+/* Subroutine of init_ggc which computes the pair of numbers used to
+   perform division by OBJECT_SIZE (order) and fills in inverse_table[].
 
+   This algorithm is taken from Granlund and Montgomery's paper
+   "Division by Invariant Integers using Multiplication"
+   (Proc. SIGPLAN PLDI, 1994), section 9 (Exact division by
+   constants).  */
+
+static void
+compute_inverse (unsigned order)
+{
+  size_t size, inv; 
+  unsigned int e;
+
+  size = OBJECT_SIZE (order);
+  e = 0;
+  while (size % 2 == 0)
+    {
+      e++;
+      size >>= 1;
+    }
+
+  inv = size;
+  while (inv * size != 1)
+    inv = inv * (2 - inv*size);
+
+  DIV_MULT (order) = inv;
+  DIV_SHIFT (order) = e;
+}
+
+/* Initialize the ggc-mmap allocator.  */
 void
-init_ggc ()
+init_ggc (void)
 {
+  unsigned order;
+
   G.pagesize = getpagesize();
   G.lg_pagesize = exact_log2 (G.pagesize);
 
-#if defined (HAVE_MMAP_ANYWHERE) && !defined(MAP_ANONYMOUS)
+#ifdef HAVE_MMAP_DEV_ZERO
   G.dev_zero_fd = open ("/dev/zero", O_RDONLY);
   if (G.dev_zero_fd == -1)
-    abort ();
+    internal_error ("open /dev/zero: %m");
 #endif
 
 #if 0
@@ -818,146 +1479,191 @@ init_ggc ()
   G.debug_file = stdout;
 #endif
 
-  G.allocated_last_gc = GGC_MIN_LAST_ALLOCATED;
-
-#ifdef HAVE_MMAP_ANYWHERE
+#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
           can't get something useful, give up.  */
 
        p = alloc_anon (NULL, G.pagesize);
-       if ((size_t)p & (G.pagesize - 1))
-         abort ();
+       gcc_assert (!((size_t)p & (G.pagesize - 1)));
       }
-    munmap (p, G.pagesize);
+
+    /* We have a good page, might as well hold onto it...  */
+    e = XCNEW (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];
+      object_size_table[order] = s;
+    }
+
+  /* Initialize the objects-per-page and inverse tables.  */
+  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;
+      compute_inverse (order);
+    }
+
+  /* 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.
+     Enforce alignment during lookup.  The resulting bin size must
+     have the same or bigger alignment than the apparent alignment
+     requirement from the size request (but not bigger alignment
+     than MAX_ALIGNMENT).  Consider an extra bin of size 76 (in
+     addition to the 64 and 128 byte sized bins).  A request of
+     allocation size of 72 bytes must be served from the 128 bytes
+     bin, because 72 bytes looks like a request for 8 byte aligned
+     memory, while the 76 byte bin can only serve chunks with a
+     guaranteed alignment of 4 bytes.  */
+  for (order = HOST_BITS_PER_PTR; order < NUM_ORDERS; ++order)
+    {
+      int i, mask;
+
+      /* Build an alignment mask that can be used for testing
+         size % 2*align.  If (size | MAX_ALIGNMENT) & mask is non-zero
+        then the requested size apparent alignment requirement 
+        (which is at most MAX_ALIGNMENT) is less or equal than what
+        the OBJECT_SIZE bin can guarantee.  */
+      mask = ~(((unsigned)-1) << ffs (OBJECT_SIZE (order)));
+      mask &= 2 * MAX_ALIGNMENT - 1;
+
+      /* All objects smaller than the OBJECT_SIZE for this ORDER could go
+        into ORDER.  Determine the cases for which that is profitable
+        and fulfilling the alignment requirements.  Stop searching
+        once a smaller bin with same or better alignment guarantee is
+        found.  */
+      for (i = OBJECT_SIZE (order); ; --i)
+       {
+         unsigned int old_sz = OBJECT_SIZE (size_lookup [i]);
+         if (!(old_sz & (mask >> 1))
+             && old_sz < OBJECT_SIZE (order))
+           break;
+
+         /* If object of size I are presently using a larger bin, we would
+            like to move them to ORDER.  However, we can only do that if we
+            can be sure they will be properly aligned.  They will be properly
+            aligned if either the ORDER bin is maximally aligned, or if
+            objects of size I cannot be more strictly aligned than the
+            alignment of this order.  */
+         if ((i | MAX_ALIGNMENT) & mask
+             && old_sz > OBJECT_SIZE (order))
+           size_lookup[i] = order;
+       }
+    }
+
+  /* Verify we got everything right with respect to alignment requests.  */
+  for (order = 1; order < 512; ++order)
+    gcc_assert (ffs (OBJECT_SIZE (size_lookup [order]))
+               >= ffs (order | MAX_ALIGNMENT));
+
+  G.depth_in_use = 0;
+  G.depth_max = 10;
+  G.depth = XNEWVEC (unsigned int, G.depth_max);
+
+  G.by_depth_in_use = 0;
+  G.by_depth_max = INITIAL_PTE_COUNT;
+  G.by_depth = XNEWVEC (page_entry *, G.by_depth_max);
+  G.save_in_use = XNEWVEC (unsigned long *, G.by_depth_max);
 }
 
-/* Increment the `GC context'.  Objects allocated in an outer context
-   are never freed, eliminating the need to register their roots.  */
+/* Start a new GGC zone.  */
 
-void
-ggc_push_context ()
+struct alloc_zone *
+new_ggc_zone (const char *name ATTRIBUTE_UNUSED)
 {
-  ++G.context_depth;
+  return NULL;
+}
 
-  /* Die on wrap.  */
-  if (G.context_depth == 0)
-    abort ();
+/* Destroy a GGC zone.  */
+void
+destroy_ggc_zone (struct alloc_zone *zone ATTRIBUTE_UNUSED)
+{
 }
 
 /* Merge the SAVE_IN_USE_P and IN_USE_P arrays in P so that IN_USE_P
    reflects reality.  Recalculate NUM_FREE_OBJECTS as well.  */
 
 static void
-ggc_recalculate_in_use_p (p)
-     page_entry *p;
+ggc_recalculate_in_use_p (page_entry *p)
 {
   unsigned int i;
   size_t num_objects;
 
-  /* Because the past-the-end bit in in_use_p is always set, we 
+  /* Because the past-the-end bit in in_use_p is always set, we
      pretend there is one additional object.  */
-  num_objects = OBJECTS_PER_PAGE (p->order) + 1;
+  num_objects = OBJECTS_IN_PAGE (p) + 1;
 
   /* Reset the free object count.  */
   p->num_free_objects = num_objects;
 
   /* 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));
+  for (i = 0;
+       i < CEIL (BITMAP_SIZE (num_objects),
+                sizeof (*p->in_use_p));
        ++i)
     {
       unsigned long j;
 
       /* Something is in use if it is marked, or if it was in use in a
         context further down the context stack.  */
-      p->in_use_p[i] |= p->save_in_use_p[i];
+      p->in_use_p[i] |= save_in_use_p (p)[i];
 
       /* Decrement the free object count for every object allocated.  */
       for (j = p->in_use_p[i]; j; j >>= 1)
        p->num_free_objects -= (j & 1);
     }
 
-  if (p->num_free_objects >= num_objects)
-    abort ();
-}
-
-/* Decrement the `GC context'.  All objects allocated since the 
-   previous ggc_push_context are migrated to the outer context.  */
-
-void
-ggc_pop_context ()
-{
-  unsigned order, depth;
-
-  depth = --G.context_depth;
-
-  /* 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++)
-    {
-      page_entry *p;
-
-      for (p = G.pages[order]; p != NULL; p = p->next)
-       {
-         if (p->context_depth > depth)
-           p->context_depth = depth;
-
-         /* If this page is now in the topmost context, and we'd
-            saved its allocation state, restore it.  */
-         else if (p->context_depth == depth && p->save_in_use_p)
-           {
-             ggc_recalculate_in_use_p (p);
-             free (p->save_in_use_p);
-             p->save_in_use_p = 0;
-           }
-       }
-    }
+  gcc_assert (p->num_free_objects < num_objects);
 }
 \f
 /* Unmark all objects.  */
 
-static inline void
-clear_marks ()
+static void
+clear_marks (void)
 {
   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);
       page_entry *p;
 
       for (p = G.pages[order]; p != NULL; p = p->next)
        {
-#ifdef ENABLE_CHECKING
+         size_t num_objects = OBJECTS_IN_PAGE (p);
+         size_t bitmap_size = BITMAP_SIZE (num_objects + 1);
+
          /* The data should be page-aligned.  */
-         if ((size_t) p->page & (G.pagesize - 1))
-           abort ();
-#endif
+         gcc_assert (!((size_t) p->page & (G.pagesize - 1)));
 
          /* Pages that aren't in the topmost context are not collected;
             nevertheless, we need their in-use bit vectors to store GC
             marks.  So, back them up first.  */
          if (p->context_depth < G.context_depth)
            {
-             if (! p->save_in_use_p)
-               p->save_in_use_p = xmalloc (bitmap_size);
-             memcpy (p->save_in_use_p, p->in_use_p, bitmap_size);
+             if (! save_in_use_p (p))
+               save_in_use_p (p) = xmalloc (bitmap_size);
+             memcpy (save_in_use_p (p), p->in_use_p, bitmap_size);
            }
 
          /* Reset reset the number of free objects and clear the
@@ -966,7 +1672,7 @@ clear_marks ()
          memset (p->in_use_p, 0, bitmap_size);
 
          /* Make sure the one-past-the-end bit is always set.  */
-         p->in_use_p[num_objects / HOST_BITS_PER_LONG] 
+         p->in_use_p[num_objects / HOST_BITS_PER_LONG]
            = ((unsigned long) 1 << (num_objects % HOST_BITS_PER_LONG));
        }
     }
@@ -975,22 +1681,22 @@ clear_marks ()
 /* Free all empty pages.  Partially empty pages need no attention
    because the `mark' bit doubles as an `unused' bit.  */
 
-static inline void
-sweep_pages ()
+static void
+sweep_pages (void)
 {
   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 num_objects;
       size_t live_objects;
       page_entry *p, *previous;
       int done;
-       
+
       p = G.pages[order];
       if (p == NULL)
        continue;
@@ -1003,11 +1709,13 @@ sweep_pages ()
          /* Loop until all entries have been examined.  */
          done = (p == last);
 
+         num_objects = OBJECTS_IN_PAGE (p);
+
          /* Add all live objects on this page to the count of
              allocated memory.  */
          live_objects = num_objects - p->num_free_objects;
 
-         G.allocated += ((size_t) 1 << order) * live_objects;
+         G.allocated += OBJECT_SIZE (order) * live_objects;
 
          /* Only objects on pages in the topmost context should get
             collected.  */
@@ -1017,10 +1725,17 @@ sweep_pages ()
          /* 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])
@@ -1037,6 +1752,7 @@ sweep_pages ()
                {
                  /* 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...  */
@@ -1047,6 +1763,11 @@ sweep_pages ()
                    G.pages[order] = next;
                  else
                    previous->next = next;
+
+                 /* And update the backpointer in NEXT if necessary.  */
+                 if (next)
+                   next->prev = previous;
+
                  p = previous;
                }
            }
@@ -1058,8 +1779,19 @@ sweep_pages ()
          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;
@@ -1068,7 +1800,7 @@ sweep_pages ()
 
          previous = p;
          p = next;
-       } 
+       }
       while (! done);
 
       /* Now, restore the in_use_p vectors for any pages from contexts
@@ -1079,22 +1811,22 @@ sweep_pages ()
     }
 }
 
-#ifdef GGC_POISON
+#ifdef ENABLE_GC_CHECKING
 /* Clobber all free objects.  */
 
-static inline void
-poison_pages ()
+static void
+poison_pages (void)
 {
   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)
        {
+         size_t num_objects;
          size_t i;
 
          if (p->context_depth != G.context_depth)
@@ -1104,114 +1836,520 @@ poison_pages ()
               contexts.  */
            continue;
 
+         num_objects = OBJECTS_IN_PAGE (p);
          for (i = 0; i < num_objects; i++)
            {
              size_t word, bit;
              word = i / HOST_BITS_PER_LONG;
              bit = i % HOST_BITS_PER_LONG;
              if (((p->in_use_p[word] >> bit) & 1) == 0)
-               memset (p->page + i * size, 0xa5, size);
+               {
+                 char *object = p->page + i * size;
+
+                 /* Keep poison-by-write when we expect to use Valgrind,
+                    so the exact same memory semantics is kept, in case
+                    there are memory errors.  We override this request
+                    below.  */
+                 VALGRIND_DISCARD (VALGRIND_MAKE_WRITABLE (object, size));
+                 memset (object, 0xa5, size);
+
+                 /* Drop the handle to avoid handle leak.  */
+                 VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (object, size));
+               }
            }
        }
     }
 }
+#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.  */
+      gcc_assert (!(pe->in_use_p[word] & (1UL << bit)));
+
+      /* 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.  */
 
 void
-ggc_collect ()
+ggc_collect (void)
 {
   /* Avoid frequent unnecessary work by skipping collection if the
      total allocations haven't expanded much since the last
      collection.  */
-#ifndef GGC_ALWAYS_COLLECT
-  if (G.allocated < GGC_MIN_EXPAND_FOR_GC * G.allocated_last_gc)
+  float allocated_last_gc =
+    MAX (G.allocated_last_gc, (size_t)PARAM_VALUE (GGC_MIN_HEAPSIZE) * 1024);
+
+  float min_expand = allocated_last_gc * PARAM_VALUE (GGC_MIN_EXPAND) / 100;
+
+  if (G.allocated < allocated_last_gc + min_expand && !ggc_force_collect)
     return;
-#endif
 
   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.  */
   G.allocated = 0;
 
-  /* Release the pages we freed the last time we collected, but didn't 
+  /* Release the pages we freed the last time we collected, but didn't
      reuse in the interim.  */
   release_pages ();
 
+  /* Indicate that we've seen collections at this context depth.  */
+  G.context_depth_collections = ((unsigned long)1 << (G.context_depth + 1)) - 1;
+
   clear_marks ();
   ggc_mark_roots ();
-  
-#ifdef GGC_POISON
-  poison_pages ();
+#ifdef GATHER_STATISTICS
+  ggc_prune_overhead_list ();
 #endif
-
+  poison_pages ();
+  validate_free_objects ();
   sweep_pages ();
 
   G.allocated_last_gc = G.allocated;
-  if (G.allocated_last_gc < GGC_MIN_LAST_ALLOCATED)
-    G.allocated_last_gc = GGC_MIN_LAST_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.  */
+#define SCALE(x) ((unsigned long) ((x) < 1024*10 \
+                 ? (x) \
+                 : ((x) < 1024*1024*10 \
+                    ? (x) / 1024 \
+                    : (x) / (1024*1024))))
+#define STAT_LABEL(x) ((x) < 1024*10 ? ' ' : ((x) < 1024*1024*10 ? 'k' : 'M'))
 
 void
-ggc_page_print_statistics ()
+ggc_print_statistics (void)
 {
   struct ggc_statistics stats;
   unsigned int i;
+  size_t total_overhead = 0;
 
   /* Clear the statistics.  */
   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.  */
   release_pages ();
 
-  /* Collect some information about the various sizes of 
+  /* 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,
+           "Memory still allocated at the end of the compilation process\n");
+  fprintf (stderr, "%-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);
+         in_use +=
+           (OBJECTS_IN_PAGE (p) - p->num_free_objects) * OBJECT_SIZE (i);
+
+         overhead += (sizeof (page_entry) - sizeof (long)
+                      + BITMAP_SIZE (OBJECTS_IN_PAGE (p) + 1));
        }
-      fprintf (stderr, "%-3d %-15lu %-15lu\n", i, 
-              (unsigned long) allocated, (unsigned long) in_use);
+      fprintf (stderr, "%-5lu %10lu%c %10lu%c %10lu%c\n",
+              (unsigned long) OBJECT_SIZE (i),
+              SCALE (allocated), STAT_LABEL (allocated),
+              SCALE (in_use), STAT_LABEL (in_use),
+              SCALE (overhead), STAT_LABEL (overhead));
+      total_overhead += overhead;
+    }
+  fprintf (stderr, "%-5s %10lu%c %10lu%c %10lu%c\n", "Total",
+          SCALE (G.bytes_mapped), STAT_LABEL (G.bytes_mapped),
+          SCALE (G.allocated), STAT_LABEL(G.allocated),
+          SCALE (total_overhead), STAT_LABEL (total_overhead));
+
+#ifdef GATHER_STATISTICS  
+  {
+    fprintf (stderr, "\nTotal allocations and overheads during the compilation process\n");
+
+    fprintf (stderr, "Total Overhead:                        %10lld\n",
+             G.stats.total_overhead);
+    fprintf (stderr, "Total Allocated:                       %10lld\n",
+             G.stats.total_allocated);
+
+    fprintf (stderr, "Total Overhead  under  32B:            %10lld\n",
+             G.stats.total_overhead_under32);
+    fprintf (stderr, "Total Allocated under  32B:            %10lld\n",
+             G.stats.total_allocated_under32);
+    fprintf (stderr, "Total Overhead  under  64B:            %10lld\n",
+             G.stats.total_overhead_under64);
+    fprintf (stderr, "Total Allocated under  64B:            %10lld\n",
+             G.stats.total_allocated_under64);
+    fprintf (stderr, "Total Overhead  under 128B:            %10lld\n",
+             G.stats.total_overhead_under128);
+    fprintf (stderr, "Total Allocated under 128B:            %10lld\n",
+             G.stats.total_allocated_under128);
+   
+    for (i = 0; i < NUM_ORDERS; i++)
+      if (G.stats.total_allocated_per_order[i])
+        {
+          fprintf (stderr, "Total Overhead  page size %7d:     %10lld\n",
+                   OBJECT_SIZE (i), G.stats.total_overhead_per_order[i]);
+          fprintf (stderr, "Total Allocated page size %7d:     %10lld\n",
+                   OBJECT_SIZE (i), G.stats.total_allocated_per_order[i]);
+        }
+  }
+#endif
+}
+\f
+struct ggc_pch_data
+{
+  struct ggc_pch_ondisk
+  {
+    unsigned totals[NUM_ORDERS];
+  } d;
+  size_t base[NUM_ORDERS];
+  size_t written[NUM_ORDERS];
+};
+
+struct ggc_pch_data *
+init_ggc_pch (void)
+{
+  return XCNEW (struct ggc_pch_data);
+}
+
+void
+ggc_pch_count_object (struct ggc_pch_data *d, void *x ATTRIBUTE_UNUSED,
+                     size_t size, bool is_string ATTRIBUTE_UNUSED,
+                     enum gt_types_enum type ATTRIBUTE_UNUSED)
+{
+  unsigned order;
+
+  if (size < 512)
+    order = size_lookup[size];
+  else
+    {
+      order = 10;
+      while (size > OBJECT_SIZE (order))
+       order++;
+    }
+
+  d->d.totals[order]++;
+}
+
+size_t
+ggc_pch_total_size (struct ggc_pch_data *d)
+{
+  size_t a = 0;
+  unsigned i;
+
+  for (i = 0; i < NUM_ORDERS; i++)
+    a += ROUND_UP (d->d.totals[i] * OBJECT_SIZE (i), G.pagesize);
+  return a;
+}
+
+void
+ggc_pch_this_base (struct ggc_pch_data *d, void *base)
+{
+  size_t a = (size_t) base;
+  unsigned i;
+
+  for (i = 0; i < NUM_ORDERS; i++)
+    {
+      d->base[i] = a;
+      a += ROUND_UP (d->d.totals[i] * OBJECT_SIZE (i), G.pagesize);
+    }
+}
+
+
+char *
+ggc_pch_alloc_object (struct ggc_pch_data *d, void *x ATTRIBUTE_UNUSED,
+                     size_t size, bool is_string ATTRIBUTE_UNUSED,
+                     enum gt_types_enum type ATTRIBUTE_UNUSED)
+{
+  unsigned order;
+  char *result;
+
+  if (size < 512)
+    order = size_lookup[size];
+  else
+    {
+      order = 10;
+      while (size > OBJECT_SIZE (order))
+       order++;
+    }
+
+  result = (char *) d->base[order];
+  d->base[order] += OBJECT_SIZE (order);
+  return result;
+}
+
+void
+ggc_pch_prepare_write (struct ggc_pch_data *d ATTRIBUTE_UNUSED,
+                      FILE *f ATTRIBUTE_UNUSED)
+{
+  /* Nothing to do.  */
+}
+
+void
+ggc_pch_write_object (struct ggc_pch_data *d ATTRIBUTE_UNUSED,
+                     FILE *f, void *x, void *newx ATTRIBUTE_UNUSED,
+                     size_t size, bool is_string ATTRIBUTE_UNUSED)
+{
+  unsigned order;
+  static const char emptyBytes[256];
+
+  if (size < 512)
+    order = size_lookup[size];
+  else
+    {
+      order = 10;
+      while (size > OBJECT_SIZE (order))
+       order++;
+    }
+
+  if (fwrite (x, size, 1, f) != 1)
+    fatal_error ("can't write PCH file: %m");
+
+  /* If SIZE is not the same as OBJECT_SIZE(order), then we need to pad the
+     object out to OBJECT_SIZE(order).  This happens for strings.  */
+
+  if (size != OBJECT_SIZE (order))
+    {
+      unsigned padding = OBJECT_SIZE(order) - size;
+
+      /* To speed small writes, we use a nulled-out array that's larger
+         than most padding requests as the source for our null bytes.  This
+         permits us to do the padding with fwrite() rather than fseek(), and
+         limits the chance the OS may try to flush any outstanding writes.  */
+      if (padding <= sizeof(emptyBytes))
+        {
+          if (fwrite (emptyBytes, 1, padding, f) != padding)
+            fatal_error ("can't write PCH file");
+        }
+      else
+        {
+          /* Larger than our buffer?  Just default to fseek.  */
+          if (fseek (f, padding, SEEK_CUR) != 0)
+            fatal_error ("can't write PCH file");
+        }
+    }
+
+  d->written[order]++;
+  if (d->written[order] == d->d.totals[order]
+      && fseek (f, ROUND_UP_VALUE (d->d.totals[order] * OBJECT_SIZE (order),
+                                  G.pagesize),
+               SEEK_CUR) != 0)
+    fatal_error ("can't write PCH file: %m");
+}
+
+void
+ggc_pch_finish (struct ggc_pch_data *d, FILE *f)
+{
+  if (fwrite (&d->d, sizeof (d->d), 1, f) != 1)
+    fatal_error ("can't write PCH file: %m");
+  free (d);
+}
+
+/* Move the PCH PTE entries just added to the end of by_depth, to the
+   front.  */
+
+static void
+move_ptes_to_front (int count_old_page_tables, int count_new_page_tables)
+{
+  unsigned i;
+
+  /* First, we swap the new entries to the front of the varrays.  */
+  page_entry **new_by_depth;
+  unsigned long **new_save_in_use;
+
+  new_by_depth = XNEWVEC (page_entry *, G.by_depth_max);
+  new_save_in_use = XNEWVEC (unsigned long *, G.by_depth_max);
+
+  memcpy (&new_by_depth[0],
+         &G.by_depth[count_old_page_tables],
+         count_new_page_tables * sizeof (void *));
+  memcpy (&new_by_depth[count_new_page_tables],
+         &G.by_depth[0],
+         count_old_page_tables * sizeof (void *));
+  memcpy (&new_save_in_use[0],
+         &G.save_in_use[count_old_page_tables],
+         count_new_page_tables * sizeof (void *));
+  memcpy (&new_save_in_use[count_new_page_tables],
+         &G.save_in_use[0],
+         count_old_page_tables * sizeof (void *));
+
+  free (G.by_depth);
+  free (G.save_in_use);
+
+  G.by_depth = new_by_depth;
+  G.save_in_use = new_save_in_use;
+
+  /* Now update all the index_by_depth fields.  */
+  for (i = G.by_depth_in_use; i > 0; --i)
+    {
+      page_entry *p = G.by_depth[i-1];
+      p->index_by_depth = i-1;
     }
 
-  /* 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);
+  /* And last, we update the depth pointers in G.depth.  The first
+     entry is already 0, and context 0 entries always start at index
+     0, so there is nothing to update in the first slot.  We need a
+     second slot, only if we have old ptes, and if we do, they start
+     at index count_new_page_tables.  */
+  if (count_old_page_tables)
+    push_depth (count_new_page_tables);
+}
+
+void
+ggc_pch_read (FILE *f, void *addr)
+{
+  struct ggc_pch_ondisk d;
+  unsigned i;
+  char *offs = addr;
+  unsigned long count_old_page_tables;
+  unsigned long count_new_page_tables;
+
+  count_old_page_tables = G.by_depth_in_use;
+
+  /* We've just read in a PCH file.  So, every object that used to be
+     allocated is now free.  */
+  clear_marks ();
+#ifdef ENABLE_GC_CHECKING
+  poison_pages ();
+#endif
+
+  /* No object read from a PCH file should ever be freed.  So, set the
+     context depth to 1, and set the depth of all the currently-allocated
+     pages to be 1 too.  PCH pages will have depth 0.  */
+  gcc_assert (!G.context_depth);
+  G.context_depth = 1;
+  for (i = 0; i < NUM_ORDERS; i++)
+    {
+      page_entry *p;
+      for (p = G.pages[i]; p != NULL; p = p->next)
+       p->context_depth = G.context_depth;
+    }
+
+  /* Allocate the appropriate page-table entries for the pages read from
+     the PCH file.  */
+  if (fread (&d, sizeof (d), 1, f) != 1)
+    fatal_error ("can't read PCH file: %m");
+
+  for (i = 0; i < NUM_ORDERS; i++)
+    {
+      struct page_entry *entry;
+      char *pte;
+      size_t bytes;
+      size_t num_objs;
+      size_t j;
+
+      if (d.totals[i] == 0)
+       continue;
+
+      bytes = ROUND_UP (d.totals[i] * OBJECT_SIZE (i), G.pagesize);
+      num_objs = bytes / OBJECT_SIZE (i);
+      entry = xcalloc (1, (sizeof (struct page_entry)
+                          - sizeof (long)
+                          + BITMAP_SIZE (num_objs + 1)));
+      entry->bytes = bytes;
+      entry->page = offs;
+      entry->context_depth = 0;
+      offs += bytes;
+      entry->num_free_objects = 0;
+      entry->order = i;
+
+      for (j = 0;
+          j + HOST_BITS_PER_LONG <= num_objs + 1;
+          j += HOST_BITS_PER_LONG)
+       entry->in_use_p[j / HOST_BITS_PER_LONG] = -1;
+      for (; j < num_objs + 1; j++)
+       entry->in_use_p[j / HOST_BITS_PER_LONG]
+         |= 1L << (j % HOST_BITS_PER_LONG);
+
+      for (pte = entry->page;
+          pte < entry->page + entry->bytes;
+          pte += G.pagesize)
+       set_page_table_entry (pte, entry);
+
+      if (G.page_tails[i] != NULL)
+       G.page_tails[i]->next = entry;
+      else
+       G.pages[i] = entry;
+      G.page_tails[i] = entry;
+
+      /* We start off by just adding all the new information to the
+        end of the varrays, later, we will move the new information
+        to the front of the varrays, as the PCH page tables are at
+        context 0.  */
+      push_by_depth (entry, 0);
+    }
+
+  /* Now, we update the various data structures that speed page table
+     handling.  */
+  count_new_page_tables = G.by_depth_in_use - count_old_page_tables;
+
+  move_ptes_to_front (count_old_page_tables, count_new_page_tables);
+
+  /* Update the statistics.  */
+  G.allocated = G.allocated_last_gc = offs - (char *)addr;
 }