OSDN Git Service

Mon May 12 11:32:53 CEST 2003 Jan Hubicka <jh@suse.cz>
[pf3gnuchains/gcc-fork.git] / gcc / ggc-simple.c
index c9181a9..9964b89 100644 (file)
 /* Simple garbage collection for the GNU compiler.
-   Copyright (C) 1998 Free Software Foundation, Inc.
+   Copyright (C) 1998, 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
+   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, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "rtl.h"
 #include "tree.h"
-#include "ggc.h"
+#include "tm_p.h"
 #include "flags.h"
 #include "varray.h"
-#include "hash.h"
+#include "ggc.h"
+#include "toplev.h"
+#include "timevar.h"
+#include "params.h"
 
 /* Debugging flags.  */
-#undef GGC_DUMP
-#define GGC_POISON
-
-/* Global lists of roots, rtxs, and trees.  */
-
-struct ggc_root
-{
-  struct ggc_root *next;
-  void *base;
-  int nelt;
-  int size;
-  void (*cb) PROTO ((void *));
-};
 
-static struct ggc_root *roots;
+/* Zap memory before freeing to catch dangling pointers.  */
+#undef GGC_POISON
 
-struct ggc_rtx
-{
-  struct ggc_rtx *chain;
-  struct rtx_def rtx;
-};
+/* Collect statistics on how bushy the search tree is.  */
+#undef GGC_BALANCE
 
-static struct ggc_rtx *rtxs;
+/* Always verify that the to-be-marked memory is collectable.  */
+#undef GGC_ALWAYS_VERIFY
 
-struct ggc_rtvec
-{
-  struct ggc_rtvec *chain;
-  struct rtvec_def vec;
-};
+#ifdef ENABLE_GC_CHECKING
+#define GGC_POISON
+#define GGC_ALWAYS_VERIFY
+#endif
 
-static struct ggc_rtvec *vecs;
+#ifndef HOST_BITS_PER_PTR
+#define HOST_BITS_PER_PTR  HOST_BITS_PER_LONG
+#endif
 
-struct ggc_tree
-{
-  struct ggc_tree *chain;
-  union tree_node tree;
-};
+/* We'd like a balanced tree, but we don't really want to pay for the
+   cost of keeping the tree balanced.  We'll settle for the next best
+   thing -- nearly balanced.
 
-static struct ggc_tree *trees;
+   In this context, the most natural key is the node pointer itself,
+   but due to the way memory managers work, we'd be virtually certain
+   to wind up with a completely degenerate straight line.  What's needed
+   is to make something more variable, and yet predictable, be more
+   significant in the comparison.
 
-struct ggc_string
-{
-  struct ggc_string *chain;
-  int magic_mark;
-  char string[1];
-};
+   The handiest source of variability is the low bits of the pointer
+   value itself.  Any sort of bit/byte swap would do, but such machine
+   specific operations are not handy, and we don't want to put that much
+   effort into it.  */
 
-#define GGC_STRING_MAGIC       ((unsigned int)0xa1b2c3d4)
+#define PTR_KEY(p)     ((size_t)p << (HOST_BITS_PER_PTR - 8)               \
+                        | ((size_t)p & 0xff00) << (HOST_BITS_PER_PTR - 24) \
+                        | (size_t)p >> 16)
 
-static struct ggc_string *strings;
+/* GC'able memory; a node in a binary search tree.  */
 
-/* Some statistics.  */
+struct ggc_mem
+{
+  /* A combination of the standard left/right nodes, indexable by `<'.  */
+  struct ggc_mem *sub[2];
 
-static int n_rtxs_collected;
-static int n_vecs_collected;
-static int n_trees_collected;
-static int n_strings_collected;
-static int bytes_alloced_since_gc;
-extern int gc_time;
+  unsigned int mark : 1;
+  unsigned int context : 7;
+  unsigned int size : 24;
 
-#ifdef GGC_DUMP
-static FILE *dump;
+  /* Make sure the data is reasonably aligned.  */
+  union {
+    HOST_WIDEST_INT i;
+#ifdef HAVE_LONG_DOUBLE
+    long double d;
+#else
+    double d;
 #endif
+  } u;
+};
 
-/* Local function prototypes.  */
-
-static void ggc_free_rtx PROTO ((struct ggc_rtx *r));
-static void ggc_free_tree PROTO ((struct ggc_tree *t));
-static void ggc_mark_rtx_ptr PROTO ((void *elt));
-static void ggc_mark_tree_ptr PROTO ((void *elt));
-static void ggc_mark_tree_varray_ptr PROTO ((void *elt));
-static void ggc_mark_tree_hash_table_ptr PROTO ((void *elt));
-static boolean ggc_mark_tree_hash_table_entry PROTO ((struct hash_entry *,
-                                                     hash_table_key));
-
-/* These allocators are dreadfully simple, with no caching whatsoever so
-   that Purify-like tools that do allocation versioning can catch errors.
-   This collector is never going to go fast anyway.  */
-
-rtx
-ggc_alloc_rtx (nslots)
-     int nslots;
+static struct globals
 {
-  struct ggc_rtx *n;
-  int size = sizeof(*n) + (nslots-1) * sizeof(rtunion);
+  /* Root of the object tree.  */
+  struct ggc_mem *root;
 
-  n = (struct ggc_rtx *) xmalloc (size);
-  bzero ((char *) n, size);
-  n->chain = rtxs;
-  rtxs = n;
+  /* Data bytes currently allocated.  */
+  size_t allocated;
 
-#ifdef GGC_DUMP
-  fprintf (dump, "alloc rtx %p\n", &n->rtx);
-#endif
+  /* Data objects currently allocated.  */
+  size_t objects;
 
-  bytes_alloced_since_gc += size;
+  /* Data bytes allocated at time of last GC.  */
+  size_t allocated_last_gc;
 
-  return &n->rtx;
-}
+  /* Current context level.  */
+  int context;
+} G;
 
-rtvec
-ggc_alloc_rtvec (nelt)
-     int nelt;
-{
-  struct ggc_rtvec *v;
-  int size = sizeof (*v) + (nelt - 1) * sizeof (rtunion);
+/* Local function prototypes.  */
 
-  v = (struct ggc_rtvec *) xmalloc (size);
-  bzero ((char *) v, size);
-  v->chain = vecs;
-  vecs = v;
+static void tree_insert PARAMS ((struct ggc_mem *));
+static int tree_lookup PARAMS ((struct ggc_mem *));
+static void clear_marks PARAMS ((struct ggc_mem *));
+static void sweep_objs PARAMS ((struct ggc_mem **));
+static void ggc_pop_context_1 PARAMS ((struct ggc_mem *, int));
 
-#ifdef GGC_DUMP
-  fprintf(dump, "alloc vec %p\n", &v->vec);
-#endif
+/* For use from debugger.  */
+extern void debug_ggc_tree PARAMS ((struct ggc_mem *, int));
 
-  bytes_alloced_since_gc += size;
+#ifdef GGC_BALANCE
+extern void debug_ggc_balance PARAMS ((void));
+#endif
+static void tally_leaves PARAMS ((struct ggc_mem *, int, size_t *, size_t *));
 
-  return &v->vec;
-}
+/* Insert V into the search tree.  */
 
-tree
-ggc_alloc_tree (length)
-     int length;
+static inline void
+tree_insert (v)
+     struct ggc_mem *v;
 {
-  struct ggc_tree *n;
-  int size = sizeof(*n) - sizeof(n->tree) + length;
-
-  n = (struct ggc_tree *) xmalloc (size);
-  bzero ((char *) n, size);
-  n->chain = trees;
-  trees = n;
+  size_t v_key = PTR_KEY (v);
+  struct ggc_mem *p, **pp;
 
-#ifdef GGC_DUMP
-  fprintf(dump, "alloc tree %p\n", &n->tree);
-#endif
-
-  bytes_alloced_since_gc += size;
-
-  return &n->tree;
+  for (pp = &G.root, p = *pp; p ; p = *pp)
+    {
+      size_t p_key = PTR_KEY (p);
+      pp = &p->sub[v_key < p_key];
+    }
+  *pp = v;
 }
 
-char *
-ggc_alloc_string (contents, length)
-     const char *contents;
-     int length;
+/* Return true if V is in the tree.  */
+
+static inline int
+tree_lookup (v)
+     struct ggc_mem *v;
 {
-  struct ggc_string *s;
-  int size;
+  size_t v_key = PTR_KEY (v);
+  struct ggc_mem *p = G.root;
 
-  if (length < 0)
+  while (p)
     {
-      if (contents == NULL)
-       return NULL;
-      length = strlen (contents);
+      size_t p_key = PTR_KEY (p);
+      if (p == v)
+       return 1;
+      p = p->sub[v_key < p_key];
     }
 
-  size = (s->string - (char *)s) + length + 1;
-  s = (struct ggc_string *) xmalloc(size);
-  s->chain = strings;
-  s->magic_mark = GGC_STRING_MAGIC;
-  if (contents)
-    bcopy (contents, s->string, length);
-  s->string[length] = 0;
-  strings = s;
-
-#ifdef GGC_DUMP
-  fprintf(dump, "alloc string %p\n", &n->tree);
-#endif
-
-  bytes_alloced_since_gc += size;
-
-  return s->string;
+  return 0;
 }
 
+/* Alloc SIZE bytes of GC'able memory.  If ZERO, clear the memory.  */
 
-/* Freeing a bit of rtl isn't quite as simple as calling free, there are
-   a few associated bits that might need freeing as well.  */
-
-static void
-ggc_free_rtx (r)
-     struct ggc_rtx *r;
+void *
+ggc_alloc (size)
+     size_t size;
 {
-#ifdef GGC_DUMP
-  fprintf (dump, "collect rtx %p\n", &r->rtx);
-#endif
+  struct ggc_mem *x;
+
+  x = (struct ggc_mem *) xmalloc (offsetof (struct ggc_mem, u) + size);
+  x->sub[0] = NULL;
+  x->sub[1] = NULL;
+  x->mark = 0;
+  x->context = G.context;
+  x->size = size;
+
 #ifdef GGC_POISON
-  memset (r, 0xAA, sizeof(*r));
+  memset (&x->u, 0xaf, size);
 #endif
 
-  free (r);
+  tree_insert (x);
+  G.allocated += size;
+  G.objects += 1;
+
+  return &x->u;
 }
 
-/* Freeing an rtvec is as simple as calling free.  */
+/* Mark a node.  */
 
-static void
-ggc_free_rtvec (v)
-     struct ggc_rtvec *v;
+int
+ggc_set_mark (p)
+     const void *p;
 {
-#ifdef GGC_DUMP
-  fprintf(dump, "collect vec %p\n", &v->vec);
-#endif
-#ifdef GGC_POISON
-  memset (v, 0xBB, sizeof (*v) + ((GET_NUM_ELEM (&v->vec) - 1)
-                                 * sizeof (rtunion)));
+  struct ggc_mem *x;
+
+  x = (struct ggc_mem *) ((const char *)p - offsetof (struct ggc_mem, u));
+#ifdef GGC_ALWAYS_VERIFY
+  if (! tree_lookup (x))
+    abort ();
 #endif
 
-  free (v);
+  if (x->mark)
+    return 1;
+
+  x->mark = 1;
+  G.allocated += x->size;
+  G.objects += 1;
+
+  return 0;
 }
 
-/* Freeing a tree node is almost, but not quite, as simple as calling free.
-   Mostly we need to let the language clean up its lang_specific bits.  */
+/* Return 1 if P has been marked, zero otherwise.  */
 
-static void
-ggc_free_tree (t)
-     struct ggc_tree *t;
+int
+ggc_marked_p (p)
+     const void *p;
 {
-  switch (TREE_CODE_CLASS (TREE_CODE (&t->tree)))
-    {
-    case 'd': /* A decl node.  */
-    case 't': /* A type node.  */
-      lang_cleanup_tree (&t->tree);
-      break;
-    }
+  struct ggc_mem *x;
 
-#ifdef GGC_DUMP
-  fprintf (dump, "collect tree %p\n", &t->tree);
-#endif
-#ifdef GGC_POISON
-  memset(&t->tree.common, 0xCC, sizeof(t->tree.common));
+  x = (struct ggc_mem *) ((const char *)p - offsetof (struct ggc_mem, u));
+#ifdef GGC_ALWAYS_VERIFY
+  if (! tree_lookup (x))
+    abort ();
 #endif
 
-  free (t);
+   return x->mark;
 }
 
-/* Freeing a string is as simple as calling free.  */
+/* Return the size of the gc-able object P.  */
 
-static void
-ggc_free_string (s)
-     struct ggc_string *s;
+size_t
+ggc_get_size (p)
+     const void *p;
 {
-#ifdef GGC_DUMP
-  fprintf(dump, "collect string %p\n", s->string);
-#endif
-#ifdef GGC_POISON
-  s->magic_mark = 0xDDDDDDDD;
-  s->string[0] = 0xDD;
-#endif
+  struct ggc_mem *x
+    = (struct ggc_mem *) ((const char *)p - offsetof (struct ggc_mem, u));
+  return x->size;
+}
+
+/* Unmark all objects.  */
 
-  free (s);
+static void
+clear_marks (x)
+     struct ggc_mem *x;
+{
+  x->mark = 0;
+  if (x->sub[0])
+    clear_marks (x->sub[0]);
+  if (x->sub[1])
+    clear_marks (x->sub[1]);
 }
 
-/* Mark a node.  */
+/* Free all objects in the current context that are not marked.  */
 
-void
-ggc_mark_rtx (r)
-     rtx r;
+static void
+sweep_objs (root)
+     struct ggc_mem **root;
 {
-  const char *fmt;
-  int i;
-
-  if (r == NULL_RTX || r->gc_mark)
+  struct ggc_mem *x = *root;
+  if (!x)
     return;
-  r->gc_mark = 1;
 
-  /* ??? If (some of) these are really pass-dependant info, do we have
-     any right poking our noses in?  */
-  switch (GET_CODE (r))
-    {
-    case JUMP_INSN:
-      ggc_mark_rtx (JUMP_LABEL (r));
-      break;
-    case CODE_LABEL:
-      ggc_mark_rtx (LABEL_REFS (r));
-      break;
-    case LABEL_REF:
-      ggc_mark_rtx (LABEL_NEXTREF (r));
-      ggc_mark_rtx (CONTAINING_INSN (r));
-      break;
-    case ADDRESSOF:
-      ggc_mark_tree (ADDRESSOF_DECL (r));
-      break;
-    case CONST_DOUBLE:
-      ggc_mark_rtx (CONST_DOUBLE_CHAIN (r));
-      break;
-
-    default:
-      break;
-    }
+  sweep_objs (&x->sub[0]);
+  sweep_objs (&x->sub[1]);
 
-  for (fmt = GET_RTX_FORMAT (GET_CODE (r)), i = 0; *fmt ; ++fmt, ++i)
+  if (! x->mark && x->context >= G.context)
     {
-      switch (*fmt)
+      struct ggc_mem *l, *r;
+
+      l = x->sub[0];
+      r = x->sub[1];
+      if (!l)
+       *root = r;
+      else if (!r)
+       *root = l;
+      else if (!l->sub[1])
        {
-       case 'e': case 'u':
-         ggc_mark_rtx (XEXP (r, i));
-         break;
-       case 'V': case 'E':
-         ggc_mark_rtvec (XVEC (r, i));
-         break;
-       case 'S': case 's':
-         ggc_mark_string (XSTR (r, i));
-         break;
+         *root = l;
+         l->sub[1] = r;
+       }
+      else if (!r->sub[0])
+       {
+         *root = r;
+         r->sub[0] = l;
+       }
+      else
+       {
+         *root = l;
+         do {
+           root = &l->sub[1];
+         } while ((l = *root) != NULL);
+         *root = r;
        }
-    }
-}
-
-void
-ggc_mark_rtvec (v)
-     rtvec v;
-{
-  int i;
 
-  if (v == NULL || v->gc_mark)
-    return;
-  v->gc_mark = 1;
+#ifdef GGC_POISON
+      memset (&x->u, 0xA5, x->size);
+#endif
 
-  i = GET_NUM_ELEM (v);
-  while (--i >= 0)
-    ggc_mark_rtx (RTVEC_ELT (v, i));
+      free (x);
+    }
 }
 
+/* The top level mark-and-sweep routine.  */
+
 void
-ggc_mark_tree (t)
-     tree t;
+ggc_collect ()
 {
-  if (t == NULL_TREE || t->common.gc_mark)
-    return;
-  t->common.gc_mark = 1;
+  /* Avoid frequent unnecessary work by skipping collection if the
+     total allocations haven't expanded much since the last
+     collection.  */
+  size_t allocated_last_gc =
+    MAX (G.allocated_last_gc, (size_t)PARAM_VALUE (GGC_MIN_HEAPSIZE) * 1024);
 
-  /* Bits from common.  */
-  ggc_mark_tree (TREE_TYPE (t));
-  ggc_mark_tree (TREE_CHAIN (t));
+  size_t min_expand = allocated_last_gc * PARAM_VALUE (GGC_MIN_EXPAND) / 100;
 
-  /* Some nodes require special handling.  */
-  switch (TREE_CODE (t))
-    {
-    case TREE_LIST:
-      ggc_mark_tree (TREE_PURPOSE (t));
-      ggc_mark_tree (TREE_VALUE (t));
-      return;
+  if (G.allocated < allocated_last_gc + min_expand)
+    return;
 
-    case TREE_VEC:
-      {
-       int i = TREE_VEC_LENGTH (t);
-       while (--i >= 0)
-         ggc_mark_tree (TREE_VEC_ELT (t, i));
-       return;
-      }
-
-    case SAVE_EXPR:
-      ggc_mark_tree (TREE_OPERAND (t, 0));
-      ggc_mark_tree (SAVE_EXPR_CONTEXT (t));
-      ggc_mark_rtx (SAVE_EXPR_RTL (t));
-      return;
+#ifdef GGC_BALANCE
+  debug_ggc_balance ();
+#endif
 
-    case RTL_EXPR:
-      ggc_mark_rtx (RTL_EXPR_SEQUENCE (t));
-      ggc_mark_rtx (RTL_EXPR_RTL (t));
-      return;
+  timevar_push (TV_GC);
+  if (!quiet_flag)
+    fprintf (stderr, " {GC %luk -> ", (unsigned long)G.allocated / 1024);
 
-    case CALL_EXPR:
-      ggc_mark_tree (TREE_OPERAND (t, 0));
-      ggc_mark_tree (TREE_OPERAND (t, 1));
-      ggc_mark_rtx (CALL_EXPR_RTL (t));
-      return;
+  G.allocated = 0;
+  G.objects = 0;
 
-    case COMPLEX_CST:
-      ggc_mark_tree (TREE_REALPART (t));
-      ggc_mark_tree (TREE_IMAGPART (t));
-      break;
+  clear_marks (G.root);
+  ggc_mark_roots ();
+  sweep_objs (&G.root);
 
-    case STRING_CST:
-      ggc_mark_string (TREE_STRING_POINTER (t));
-      break;
+  G.allocated_last_gc = G.allocated;
 
-    case PARM_DECL:
-      ggc_mark_rtx (DECL_INCOMING_RTL (t));
-      break;
+  timevar_pop (TV_GC);
 
-    case IDENTIFIER_NODE:
-      ggc_mark_string (IDENTIFIER_POINTER (t));
-      lang_mark_tree (t);
-      return;
+  if (!quiet_flag)
+    fprintf (stderr, "%luk}", (unsigned long) G.allocated / 1024);
 
-    default:
-      break;
-    }
-  
-  /* But in general we can handle them by class.  */
-  switch (TREE_CODE_CLASS (TREE_CODE (t)))
-    {
-    case 'd': /* A decl node.  */
-      ggc_mark_tree (DECL_SIZE (t));
-      ggc_mark_tree (DECL_NAME (t));
-      ggc_mark_tree (DECL_CONTEXT (t));
-      ggc_mark_tree (DECL_ARGUMENTS (t));
-      ggc_mark_tree (DECL_RESULT (t));
-      ggc_mark_tree (DECL_INITIAL (t));
-      ggc_mark_tree (DECL_ABSTRACT_ORIGIN (t));
-      ggc_mark_tree (DECL_ASSEMBLER_NAME (t));
-      ggc_mark_tree (DECL_SECTION_NAME (t));
-      ggc_mark_tree (DECL_MACHINE_ATTRIBUTES (t));
-      ggc_mark_rtx (DECL_RTL (t));
-      ggc_mark_tree (DECL_VINDEX (t));
-      lang_mark_tree (t);
-      break;
-
-    case 't': /* A type node.  */
-      ggc_mark_tree (TYPE_SIZE (t));
-      ggc_mark_tree (TYPE_SIZE_UNIT (t));
-      ggc_mark_tree (TYPE_ATTRIBUTES (t));
-      ggc_mark_tree (TYPE_VALUES (t));
-      ggc_mark_tree (TYPE_POINTER_TO (t));
-      ggc_mark_tree (TYPE_REFERENCE_TO (t));
-      ggc_mark_tree (TYPE_NAME (t));
-      ggc_mark_tree (TYPE_MIN_VALUE (t));
-      ggc_mark_tree (TYPE_MAX_VALUE (t));
-      ggc_mark_tree (TYPE_NEXT_VARIANT (t));
-      ggc_mark_tree (TYPE_MAIN_VARIANT (t));
-      ggc_mark_tree (TYPE_BINFO (t));
-      ggc_mark_tree (TYPE_NONCOPIED_PARTS (t));
-      ggc_mark_tree (TYPE_CONTEXT (t));
-      lang_mark_tree (t);
-      break;
-
-    case 'b': /* A lexical block.  */
-      ggc_mark_tree (BLOCK_VARS (t));
-      ggc_mark_tree (BLOCK_TYPE_TAGS (t));
-      ggc_mark_tree (BLOCK_SUBBLOCKS (t));
-      ggc_mark_tree (BLOCK_SUPERCONTEXT (t));
-      ggc_mark_tree (BLOCK_ABSTRACT_ORIGIN (t));
-      ggc_mark_rtx (BLOCK_END_NOTE (t));
-      break;
-
-    case 'c': /* A constant.  */
-      ggc_mark_rtx (TREE_CST_RTL (t));
-      break;
-
-    case 'r': case '<': case '1':
-    case '2': case 'e': case 's': /* Expressions.  */
-      {
-       int i = tree_code_length[TREE_CODE (t)];
-       while (--i >= 0)
-         ggc_mark_tree (TREE_OPERAND (t, i));
-       break;
-      }
-    }
+#ifdef GGC_BALANCE
+  debug_ggc_balance ();
+#endif
 }
 
-/* Mark all the elements of the varray V, which contains trees.  */
+/* Called once to initialize the garbage collector.  */
 
 void
-ggc_mark_tree_varray (v)
-     varray_type v;
+init_ggc ()
 {
-  int i;
-
-  for (i = v->num_elements - 1; i >= 0; --i) 
-    ggc_mark_tree (VARRAY_TREE (v, i));
 }
 
-/* Mark the hash table-entry HE.  It's key field is really a tree.  */
+/* Start a new GGC context.  Memory allocated in previous contexts
+   will not be collected while the new context is active.  */
 
-static boolean
-ggc_mark_tree_hash_table_entry (he, k)
-     struct hash_entry *he;
-     hash_table_key k ATTRIBUTE_UNUSED;
+void
+ggc_push_context ()
 {
-  ggc_mark_tree ((tree) he->key);
-  return true;
+  G.context++;
+
+  /* We only allocated 7 bits in the node for the context.  This
+     should be more than enough.  */
+  if (G.context >= 128)
+    abort ();
 }
 
-/* Mark all the elements of the hash-table H, which contains trees.  */
+/* Finish a GC context.  Any uncollected memory in the new context
+   will be merged with the old context.  */
 
 void
-ggc_mark_tree_hash_table (ht)
-     struct hash_table *ht;
+ggc_pop_context ()
 {
-  hash_traverse (ht, ggc_mark_tree_hash_table_entry, /*info=*/0);
+  G.context--;
+  if (G.root)
+    ggc_pop_context_1 (G.root, G.context);
 }
 
-void
-ggc_mark_string (s)
-     char *s;
-{
-  unsigned int *magic = (unsigned int *)s - 1;
-
-  if (s == NULL)
-    return;
-
-  if ((*magic & ~(unsigned)1) != GGC_STRING_MAGIC)
-    return;   /* abort? */
-  *magic = GGC_STRING_MAGIC | 1;
+static void
+ggc_pop_context_1 (x, c)
+     struct ggc_mem *x;
+     int c;
+{
+  if (x->context > c)
+    x->context = c;
+  if (x->sub[0])
+    ggc_pop_context_1 (x->sub[0], c);
+  if (x->sub[1])
+    ggc_pop_context_1 (x->sub[1], c);
 }
 
-/* The top level mark-and-sweep routine.  */
+/* Dump a tree.  */
 
 void
-ggc_collect ()
+debug_ggc_tree (p, indent)
+     struct ggc_mem *p;
+     int indent;
 {
-  struct ggc_rtx *r, **rp;
-  struct ggc_rtvec *v, **vp;
-  struct ggc_tree *t, **tp;
-  struct ggc_string *s, **sp;
-  struct ggc_root *x;
-  int time, n_rtxs, n_trees, n_vecs, n_strings;
-
-#ifndef ENABLE_CHECKING
-  /* See if it's even worth our while.  */
-  if (bytes_alloced_since_gc < 64*1024)
-    return;
-#endif
-
-  if (!quiet_flag)
-    fputs (" {GC ", stderr);
-
-  time = get_run_time ();
-
-  /* Clean out all of the GC marks.  */
-  for (r = rtxs; r != NULL; r = r->chain)
-    r->rtx.gc_mark = 0;
-  for (v = vecs; v != NULL; v = v->chain)
-    v->vec.gc_mark = 0;
-  for (t = trees; t != NULL; t = t->chain)
-    t->tree.common.gc_mark = 0;
-  for (s = strings; s != NULL; s = s->chain)
-    s->magic_mark = GGC_STRING_MAGIC;
-
-  /* Mark through all the roots.  */
-  for (x = roots; x != NULL; x = x->next)
-    {
-      char *elt = x->base;
-      int s = x->size, n = x->nelt;
-      void (*cb) PROTO ((void *)) = x->cb;
-      int i;
-
-      for (i = 0; i < n; ++i, elt += s)
-       (*cb)(elt);
-    }
-
-  /* Sweep the resulting dead nodes.  */
-  rp = &rtxs, r = rtxs, n_rtxs = 0;
-  while (r != NULL)
-    {
-      struct ggc_rtx *chain = r->chain;
-      if (!r->rtx.gc_mark)
-        {
-         ggc_free_rtx (r);
-         *rp = chain;
-         n_rtxs++;
-        }
-      else
-       rp = &r->chain;
-      r = chain;
-    }
-  *rp = NULL;
-  n_rtxs_collected += n_rtxs;
-
-  vp = &vecs, v = vecs, n_vecs = 0;
-  while (v != NULL)
-    {
-      struct ggc_rtvec *chain = v->chain;
-      if (!v->vec.gc_mark)
-        {
-         ggc_free_rtvec (v);
-         *vp = chain;
-         n_vecs++;
-        }
-      else
-       vp = &v->chain;
-      v = chain;
-    }
-  *vp = NULL;
-  n_vecs_collected += n_vecs;
+  int i;
 
-  tp = &trees, t = trees, n_trees = 0;
-  while (t != NULL)
+  if (!p)
     {
-      struct ggc_tree *chain = t->chain;
-      if (!t->tree.common.gc_mark)
-        {
-         ggc_free_tree (t);
-         *tp = chain;
-         n_trees++;
-        }
-      else
-       tp = &t->chain;
-      t = chain;
+      fputs ("(nil)\n", stderr);
+      return;
     }
-  *tp = NULL;
-  n_trees_collected += n_trees;
 
-  sp = &strings, s = strings, n_strings = 0;
-  while (s != NULL)
-    {
-      struct ggc_string *chain = s->chain;
-      if (!(s->magic_mark & 1))
-        {
-         ggc_free_string (s);
-         *sp = chain;
-         n_strings++;
-        }
-      else
-       sp = &s->chain;
-      s = chain;
-    }
-  *sp = NULL;
-  n_strings_collected += n_strings;
+  if (p->sub[0])
+    debug_ggc_tree (p->sub[0], indent + 1);
 
-  gc_time += time = get_run_time () - time;
+  for (i = 0; i < indent; ++i)
+    putc (' ', stderr);
+  fprintf (stderr, "%lx %p\n", (unsigned long)PTR_KEY (p), (PTR) p);
 
-  if (!quiet_flag)
-    {
-      time = (time + 500) / 1000;
-      fprintf (stderr, "%d,%d,%d,%d %d.%03d}", n_rtxs, n_vecs, n_trees,
-              n_strings, time / 1000, time % 1000);
-    }
+  if (p->sub[1])
+    debug_ggc_tree (p->sub[1], indent + 1);
 }
 
-/* Manipulate global roots that are needed between calls to gc.  */
+#ifdef GGC_BALANCE
+/* Collect tree balance metrics  */
+
+#include <math.h>
 
 void
-ggc_add_root (base, nelt, size, cb)
-     void *base;
-     int nelt, size;
-     void (*cb) PROTO ((void *));
+debug_ggc_balance ()
 {
-  struct ggc_root *x = (struct ggc_root *) xmalloc (sizeof(*x));
+  size_t nleaf, sumdepth;
 
-  x->next = roots;
-  x->base = base;
-  x->nelt = nelt;
-  x->size = size;
-  x->cb = cb;
+  nleaf = sumdepth = 0;
+  tally_leaves (G.root, 0, &nleaf, &sumdepth);
 
-  roots = x;
+  fprintf (stderr, " {B %.2f,%.1f,%.1f}",
+          /* In a balanced tree, leaf/node should approach 1/2.  */
+          (float)nleaf / (float)G.objects,
+          /* In a balanced tree, average leaf depth should approach lg(n).  */
+          (float)sumdepth / (float)nleaf,
+          log ((double) G.objects) / M_LN2);
 }
+#endif
 
-void
-ggc_add_rtx_root (base, nelt)
-     rtx *base;
-     int nelt;
+/* Used by debug_ggc_balance, and also by ggc_print_statistics.  */
+static void
+tally_leaves (x, depth, nleaf, sumdepth)
+     struct ggc_mem *x;
+     int depth;
+     size_t *nleaf;
+     size_t *sumdepth;
 {
-  ggc_add_root (base, nelt, sizeof(rtx), ggc_mark_rtx_ptr);
+  if (! x->sub[0] && !x->sub[1])
+    {
+      *nleaf += 1;
+      *sumdepth += depth;
+    }
+  else
+    {
+      if (x->sub[0])
+       tally_leaves (x->sub[0], depth + 1, nleaf, sumdepth);
+      if (x->sub[1])
+       tally_leaves (x->sub[1], depth + 1, nleaf, sumdepth);
+    }
 }
 
+#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'))
+
+/* Report on GC memory usage.  */
 void
-ggc_add_tree_root (base, nelt)
-     tree *base;
-     int nelt;
+ggc_print_statistics ()
 {
-  ggc_add_root (base, nelt, sizeof(tree), ggc_mark_tree_ptr);
-}
+  struct ggc_statistics stats;
+  size_t nleaf = 0, sumdepth = 0;
 
-/* Add V (a varray full of trees) to the list of GC roots.  */
+  /* Clear the statistics.  */
+  memset (&stats, 0, sizeof (stats));
 
-void
-ggc_add_tree_varray_root (base, nelt)
-     varray_type *base;
-     int nelt;
-{
-  ggc_add_root (base, nelt, sizeof (varray_type), 
-               ggc_mark_tree_varray_ptr);
-}
+  /* Make sure collection will really occur.  */
+  G.allocated_last_gc = 0;
 
-/* Add HT (a hash-table where ever key is a tree) to the list of GC
-   roots.  */
+  /* Collect and print the statistics common across collectors.  */
+  ggc_print_common_statistics (stderr, &stats);
 
-void
-ggc_add_tree_hash_table_root (base, nelt)
-     struct hash_table **base;
-     int nelt;
+  /* Report on tree balancing.  */
+  tally_leaves (G.root, 0, &nleaf, &sumdepth);
+
+  fprintf (stderr, "\n\
+Total internal data (bytes)\t%ld%c\n\
+Number of leaves in tree\t%lu\n\
+Average leaf depth\t\t%.1f\n",
+          SCALE(G.objects * offsetof (struct ggc_mem, u)),
+          LABEL(G.objects * offsetof (struct ggc_mem, u)),
+          (unsigned long)nleaf, (double)sumdepth / (double)nleaf);
+
+  /* Report overall memory usage.  */
+  fprintf (stderr, "\n\
+Total objects allocated\t\t%ld\n\
+Total memory in GC arena\t%ld%c\n",
+          (unsigned long)G.objects,
+          SCALE(G.allocated), LABEL(G.allocated));
+}
+\f
+struct ggc_pch_data *
+init_ggc_pch ()
 {
-  ggc_add_root (base, nelt, sizeof (struct hash_table *), 
-               ggc_mark_tree_hash_table_ptr);
+  sorry ("Generating PCH files is not supported when using ggc-simple.c");
+  /* It could be supported, but the code is not yet written.  */
+  return NULL;
 }
 
-void
-ggc_del_root (base)
-     void *base;
+void 
+ggc_pch_count_object (d, x, size)
+     struct ggc_pch_data *d ATTRIBUTE_UNUSED;
+     void *x ATTRIBUTE_UNUSED;
+     size_t size ATTRIBUTE_UNUSED;
 {
-  struct ggc_root *x, **p;
-
-  p = &roots, x = roots;
-  while (x)
-    {
-      if (x->base == base)
-       {
-         *p = x->next;
-         free (x);
-         return;
-       }
-      p = &x->next;
-      x = x->next;
-    }
-
-  abort();
 }
-
-static void
-ggc_mark_rtx_ptr (elt)
-     void *elt;
+     
+size_t
+ggc_pch_total_size (d)
+     struct ggc_pch_data *d ATTRIBUTE_UNUSED;
 {
-  ggc_mark_rtx (*(rtx *)elt);
+  return 0;
 }
 
-static void
-ggc_mark_tree_ptr (elt)
-     void *elt;
+void
+ggc_pch_this_base (d, base)
+     struct ggc_pch_data *d ATTRIBUTE_UNUSED;
+     void *base ATTRIBUTE_UNUSED;
 {
-  ggc_mark_tree (*(tree *)elt);
 }
 
-/* Type-correct function to pass to ggc_add_root.  It just forwards
-   ELT (which is really a varray_type *) to ggc_mark_tree_varray.  */
 
-static void
-ggc_mark_tree_varray_ptr (elt)
-     void *elt;
+char *
+ggc_pch_alloc_object (d, x, size)
+     struct ggc_pch_data *d ATTRIBUTE_UNUSED;
+     void *x ATTRIBUTE_UNUSED;
+     size_t size ATTRIBUTE_UNUSED;
 {
-  ggc_mark_tree_varray (*(varray_type *)elt);
+  return NULL;
 }
 
-/* Type-correct function to pass to ggc_add_root.  It just forwards
-   ELT (which is really a struct hash_table **) to
-   ggc_mark_tree_hash_table.  */
-
-static void
-ggc_mark_tree_hash_table_ptr (elt)
-     void *elt;
+void 
+ggc_pch_prepare_write (d, f)
+     struct ggc_pch_data * d ATTRIBUTE_UNUSED;
+     FILE * f ATTRIBUTE_UNUSED;
 {
-  ggc_mark_tree_hash_table (*(struct hash_table **) elt);
 }
 
-#ifdef GGC_DUMP
-/* Don't enable this unless you want a really really lot of data.  */
-static void __attribute__((constructor))
-init(void)
+void
+ggc_pch_write_object (d, f, x, newx, size)
+     struct ggc_pch_data * d ATTRIBUTE_UNUSED;
+     FILE *f ATTRIBUTE_UNUSED;
+     void *x ATTRIBUTE_UNUSED;
+     void *newx ATTRIBUTE_UNUSED;
+     size_t size ATTRIBUTE_UNUSED;
 {
-  dump = fopen ("zgcdump", "w");
-  setlinebuf (dump);
 }
-#endif
 
-#if 0
-/* GDB really should have a memory search function.  Since this is just
-   for initial debugging, I won't even pretend to get the __data_start
-   to work on any but alpha-dec-linux-gnu.  */
-static void **
-search_data(void **start, void *target)
+void
+ggc_pch_finish (d, f)
+     struct ggc_pch_data * d ATTRIBUTE_UNUSED;
+     FILE *f ATTRIBUTE_UNUSED;
 {
-  extern void *__data_start[];
-  void **_end = (void **)sbrk(0);
+}
 
-  if (start == NULL)
-    start = __data_start;
-  while (start < _end)
-    {
-      if (*start == target)
-        return start;
-      start++;
-    }
-  return NULL;
+void
+ggc_pch_read (f, addr)
+     FILE *f ATTRIBUTE_UNUSED;
+     void *addr ATTRIBUTE_UNUSED;
+{
+  /* This should be impossible, since we won't generate any valid PCH
+     files for this configuration.  */
+  abort ();
 }
-#endif