* The creation of symbols and bindings are two separate issues now.
[bpt/guile.git] / libguile / gc.c
index 4c4ab58..eaf8ec7 100644 (file)
@@ -1,15 +1,15 @@
 /* Copyright (C) 1995, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
- * 
+ *
  * This program 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.
- * 
+ *
  * This program 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 this software; see the file COPYING.  If not, write to
  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 /* Software engineering face-lift by Greg J. Badros, 11-Dec-1999,
    gjb@cs.washington.edu, http://www.cs.washington.edu/homes/gjb */
 
+/* #define DEBUGINFO */
+
 \f
 #include <stdio.h>
-#include "_scm.h"
-#include "stime.h"
-#include "stackchk.h"
-#include "struct.h"
-#include "genio.h"
-#include "weaks.h"
-#include "guardians.h"
-#include "smob.h"
-#include "unif.h"
-#include "async.h"
-
-#include "validate.h"
-#include "gc.h"
+#include "libguile/_scm.h"
+#include "libguile/eval.h"
+#include "libguile/stime.h"
+#include "libguile/stackchk.h"
+#include "libguile/struct.h"
+#include "libguile/smob.h"
+#include "libguile/unif.h"
+#include "libguile/async.h"
+#include "libguile/ports.h"
+#include "libguile/root.h"
+#include "libguile/strings.h"
+#include "libguile/vectors.h"
+#include "libguile/weaks.h"
+#include "libguile/hashtab.h"
+
+#include "libguile/validate.h"
+#include "libguile/gc.h"
+
+#ifdef GUILE_DEBUG_MALLOC
+#include "libguile/debug-malloc.h"
+#endif
 
 #ifdef HAVE_MALLOC_H
 #include <malloc.h>
 #endif
 
 \f
+
+unsigned int scm_gc_running_p = 0;
+
+\f
+
+#if (SCM_DEBUG_CELL_ACCESSES == 1)
+
+unsigned int scm_debug_cell_accesses_p = 0;
+
+
+/* Assert that the given object is a valid reference to a valid cell.  This
+ * test involves to determine whether the object is a cell pointer, whether
+ * this pointer actually points into a heap segment and whether the cell
+ * pointed to is not a free cell.
+ */
+void
+scm_assert_cell_valid (SCM cell)
+{
+  if (scm_debug_cell_accesses_p)
+    {
+      scm_debug_cell_accesses_p = 0;  /* disable to avoid recursion */
+
+      if (!scm_cellp (cell))
+       {
+         fprintf (stderr, "scm_assert_cell_valid: Not a cell object: %lx\n", SCM_UNPACK (cell));
+         abort ();
+       }
+      else if (!scm_gc_running_p)
+       {
+         /* Dirk::FIXME:: During garbage collection there occur references to
+            free cells.  This is allright during conservative marking, but
+            should not happen otherwise (I think).  The case of free cells
+            accessed during conservative marking is handled in function
+            scm_mark_locations.  However, there still occur accesses to free
+            cells during gc.  I don't understand why this happens.  If it is
+            a bug and gets fixed, the following test should also work while
+            gc is running.
+          */
+         if (SCM_FREE_CELL_P (cell))
+           {
+             fprintf (stderr, "scm_assert_cell_valid: Accessing free cell: %lx\n", SCM_UNPACK (cell));
+             abort ();
+           }
+       }
+      scm_debug_cell_accesses_p = 1;  /* re-enable */
+    }
+}
+
+
+SCM_DEFINE (scm_set_debug_cell_accesses_x, "set-debug-cell-accesses!", 1, 0, 0,
+           (SCM flag),
+           "If FLAG is #f, cell access checking is disabled.\n"
+           "If FLAG is #t, cell access checking is enabled.\n"
+           "This procedure only exists because the compile-time flag\n"
+           "SCM_DEBUG_CELL_ACCESSES was set to 1.\n")
+#define FUNC_NAME s_scm_set_debug_cell_accesses_x
+{
+  if (SCM_FALSEP (flag)) {
+    scm_debug_cell_accesses_p = 0;
+  } else if (SCM_EQ_P (flag, SCM_BOOL_T)) {
+    scm_debug_cell_accesses_p = 1;
+  } else {
+    SCM_WRONG_TYPE_ARG (1, flag);
+  }
+  return SCM_UNSPECIFIED;
+}
+#undef FUNC_NAME
+
+#endif  /* SCM_DEBUG_CELL_ACCESSES == 1 */
+
+\f
+
 /* {heap tuning parameters}
- * 
+ *
  * These are parameters for controlling memory allocation.  The heap
  * is the area out of which scm_cons, and object headers are allocated.
  *
  * will be used, and the heap will grow by SCM_HEAP_SEG_SIZE when more
  * heap is needed.  SCM_HEAP_SEG_SIZE must fit into type scm_sizet.  This code
  * is in scm_init_storage() and alloc_some_heap() in sys.c
- * 
+ *
  * If SCM_INIT_HEAP_SIZE can be allocated initially, the heap will grow by
  * SCM_EXPHEAP(scm_heap_size) when more heap is needed.
  *
  * is needed.
  *
  * INIT_MALLOC_LIMIT is the initial amount of malloc usage which will
- * trigger a GC. 
+ * trigger a GC.
  *
  * SCM_MTRIGGER_HYSTERESIS is the amount of malloc storage that must be
  * reclaimed by a GC triggered by must_malloc. If less than this is
  * reclaimed, the trigger threshold is raised. [I don't know what a
  * good value is. I arbitrarily chose 1/10 of the INIT_MALLOC_LIMIT to
- * work around a oscillation that caused almost constant GC.]  
+ * work around a oscillation that caused almost constant GC.]
  */
 
-#define SCM_INIT_HEAP_SIZE_1 (40000L * sizeof (scm_cell))
-#define SCM_CLUSTER_SIZE_1 2000L
-#define SCM_GC_TRIGGER_1 -50
+/*
+ * Heap size 45000 and 40% min yield gives quick startup and no extra
+ * heap allocation.  Having higher values on min yield may lead to
+ * large heaps, especially if code behaviour is varying its
+ * maximum consumption between different freelists.
+ */
+
+#define SCM_DATA_CELLS2CARDS(n) (((n) + SCM_GC_CARD_N_DATA_CELLS - 1) / SCM_GC_CARD_N_DATA_CELLS)
+#define SCM_CARDS_PER_CLUSTER SCM_DATA_CELLS2CARDS (2000L)
+#define SCM_CLUSTER_SIZE_1 (SCM_CARDS_PER_CLUSTER * SCM_GC_CARD_N_DATA_CELLS)
+int scm_default_init_heap_size_1 = (((SCM_DATA_CELLS2CARDS (45000L) + SCM_CARDS_PER_CLUSTER - 1)
+                                     / SCM_CARDS_PER_CLUSTER) * SCM_GC_CARD_SIZE);
+int scm_default_min_yield_1 = 40;
 
-#define SCM_INIT_HEAP_SIZE_2 (2500L * 2 * sizeof (scm_cell))
-#define SCM_CLUSTER_SIZE_2 1000L
+#define SCM_CLUSTER_SIZE_2 (SCM_CARDS_PER_CLUSTER * (SCM_GC_CARD_N_DATA_CELLS / 2))
+int scm_default_init_heap_size_2 = (((SCM_DATA_CELLS2CARDS (2500L * 2) + SCM_CARDS_PER_CLUSTER - 1)
+                                     / SCM_CARDS_PER_CLUSTER) * SCM_GC_CARD_SIZE);
 /* The following value may seem large, but note that if we get to GC at
  * all, this means that we have a numerically intensive application
  */
-#define SCM_GC_TRIGGER_2 -50
+int scm_default_min_yield_2 = 40;
 
-#define SCM_MAX_SEGMENT_SIZE 2097000L /* a little less (adm) than 2 Mb */
+int scm_default_max_segment_size = 2097000L;/* a little less (adm) than 2 Mb */
 
-#define SCM_MIN_HEAP_SEG_SIZE (2048L * sizeof (scm_cell))
+#define SCM_MIN_HEAP_SEG_SIZE (8 * SCM_GC_CARD_SIZE)
 #ifdef _QC
 # define SCM_HEAP_SEG_SIZE 32768L
 #else
 #define SCM_INIT_MALLOC_LIMIT 100000
 #define SCM_MTRIGGER_HYSTERESIS (SCM_INIT_MALLOC_LIMIT/10)
 
-/* CELL_UP and CELL_DN are used by scm_init_heap_seg to find scm_cell aligned inner
-   bounds for allocated storage */
+/* CELL_UP and CELL_DN are used by scm_init_heap_seg to find (scm_cell * span)
+   aligned inner bounds for allocated storage */
 
 #ifdef PROT386
 /*in 386 protected mode we must only adjust the offset */
-# define CELL_UP(p) MK_FP(FP_SEG(p), ~7&(FP_OFF(p)+7))
-# define CELL_DN(p) MK_FP(FP_SEG(p), ~7&FP_OFF(p))
+# define CELL_UP(p, span) MK_FP(FP_SEG(p), ~(8*(span)-1)&(FP_OFF(p)+8*(span)-1))
+# define CELL_DN(p, span) MK_FP(FP_SEG(p), ~(8*(span)-1)&FP_OFF(p))
 #else
 # ifdef _UNICOS
-#  define CELL_UP(p) (SCM_CELLPTR)(~1L & ((long)(p)+1L))
-#  define CELL_DN(p) (SCM_CELLPTR)(~1L & (long)(p))
+#  define CELL_UP(p, span) (SCM_CELLPTR)(~(span) & ((long)(p)+(span)))
+#  define CELL_DN(p, span) (SCM_CELLPTR)(~(span) & (long)(p))
 # else
-#  define CELL_UP(p) (SCM_CELLPTR)(~(sizeof(scm_cell)-1L) & ((long)(p)+sizeof(scm_cell)-1L))
-#  define CELL_DN(p) (SCM_CELLPTR)(~(sizeof(scm_cell)-1L) & (long)(p))
+#  define CELL_UP(p, span) (SCM_CELLPTR)(~(sizeof(scm_cell)*(span)-1L) & ((long)(p)+sizeof(scm_cell)*(span)-1L))
+#  define CELL_DN(p, span) (SCM_CELLPTR)(~(sizeof(scm_cell)*(span)-1L) & (long)(p))
 # endif                                /* UNICOS */
 #endif                         /* PROT386 */
 
+#define ALIGNMENT_SLACK(freelist) (SCM_GC_CARD_SIZE - 1)
+#define CLUSTER_SIZE_IN_BYTES(freelist) \
+    (((freelist)->cluster_size / (SCM_GC_CARD_N_DATA_CELLS / (freelist)->span)) * SCM_GC_CARD_SIZE)
 
 \f
 /* scm_freelists
  */
 
-#ifdef GUILE_NEW_GC_SCHEME
+typedef struct scm_freelist_t {
+  /* collected cells */
+  SCM cells;
+  /* number of cells left to collect before cluster is full */
+  unsigned int left_to_collect;
+  /* number of clusters which have been allocated */
+  unsigned int clusters_allocated;
+  /* a list of freelists, each of size cluster_size,
+   * except the last one which may be shorter
+   */
+  SCM clusters;
+  SCM *clustertail;
+  /* this is the number of objects in each cluster, including the spine cell */
+  int cluster_size;
+  /* indicates that we should grow heap instead of GC:ing
+   */
+  int grow_heap_p;
+  /* minimum yield on this list in order not to grow the heap
+   */
+  long min_yield;
+  /* defines min_yield as percent of total heap size
+   */
+  int min_yield_fraction;
+  /* number of cells per object on this list */
+  int span;
+  /* number of collected cells during last GC */
+  long collected;
+  /* number of collected cells during penultimate GC */
+  long collected_1;
+  /* total number of cells in heap segments
+   * belonging to this list.
+   */
+  long heap_size;
+} scm_freelist_t;
+
 SCM scm_freelist = SCM_EOL;
 scm_freelist_t scm_master_freelist = {
-  SCM_EOL, 0, SCM_EOL, 0, SCM_CLUSTER_SIZE_1, 0, 0, 0, 1, 0, 0
+  SCM_EOL, 0, 0, SCM_EOL, 0, SCM_CLUSTER_SIZE_1, 0, 0, 0, 1, 0, 0
 };
 SCM scm_freelist2 = SCM_EOL;
 scm_freelist_t scm_master_freelist2 = {
-  SCM_EOL, 0, SCM_EOL, 0, SCM_CLUSTER_SIZE_2, 0, 0, 0, 2, 0, 0
+  SCM_EOL, 0, 0, SCM_EOL, 0, SCM_CLUSTER_SIZE_2, 0, 0, 0, 2, 0, 0
 };
-#else
-scm_freelist_t scm_freelist = { SCM_EOL, 1, 0, 0 };
-scm_freelist_t scm_freelist2 = { SCM_EOL, 2, 0, 0 };
-#endif
 
 /* scm_mtrigger
  * is the number of bytes of must_malloc allocation needed to trigger gc.
  */
 unsigned long scm_mtrigger;
 
-
 /* scm_gc_heap_lock
  * If set, don't expand the heap.  Set only during gc, during which no allocation
  * is supposed to take place anyway.
@@ -189,28 +314,35 @@ int scm_gc_heap_lock = 0;
  * Don't pause for collection if this is set -- just
  * expand the heap.
  */
-
 int scm_block_gc = 1;
 
-/* If fewer than MIN_GC_YIELD cells are recovered during a garbage
- * collection (GC) more space is allocated for the heap.
- */
-#define MIN_GC_YIELD(freelist) (freelist->heap_size / 4)
-
 /* During collection, this accumulates objects holding
  * weak references.
  */
 SCM scm_weak_vectors;
 
+/* During collection, this accumulates structures which are to be freed.
+ */
+SCM scm_structs_to_free;
+
 /* GC Statistics Keeping
  */
 unsigned long scm_cells_allocated = 0;
 long scm_mallocated = 0;
-/* unsigned long scm_gc_cells_collected; */
+unsigned long scm_gc_cells_collected;
+unsigned long scm_gc_yield;
+static unsigned long scm_gc_yield_1 = 0; /* previous GC yield */
 unsigned long scm_gc_malloc_collected;
 unsigned long scm_gc_ports_collected;
-unsigned long scm_gc_rt;
 unsigned long scm_gc_time_taken = 0;
+static unsigned long t_before_gc;
+static unsigned long t_before_sweep;
+unsigned long scm_gc_mark_time_taken = 0;
+unsigned long scm_gc_sweep_time_taken = 0;
+unsigned long scm_gc_times = 0;
+unsigned long scm_gc_cells_swept = 0;
+double scm_gc_cells_marked_acc = 0.;
+double scm_gc_cells_swept_acc = 0.;
 
 SCM_SYMBOL (sym_cells_allocated, "cells-allocated");
 SCM_SYMBOL (sym_heap_size, "cell-heap-size");
@@ -218,9 +350,13 @@ SCM_SYMBOL (sym_mallocated, "bytes-malloced");
 SCM_SYMBOL (sym_mtrigger, "gc-malloc-threshold");
 SCM_SYMBOL (sym_heap_segments, "cell-heap-segments");
 SCM_SYMBOL (sym_gc_time_taken, "gc-time-taken");
+SCM_SYMBOL (sym_gc_mark_time_taken, "gc-mark-time-taken");
+SCM_SYMBOL (sym_gc_sweep_time_taken, "gc-sweep-time-taken");
+SCM_SYMBOL (sym_times, "gc-times");
+SCM_SYMBOL (sym_cells_marked, "cells-marked");
+SCM_SYMBOL (sym_cells_swept, "cells-swept");
 
-
-struct scm_heap_seg_data
+typedef struct scm_heap_seg_data_t
 {
   /* lower and upper bounds of the segment */
   SCM_CELLPTR bounds[2];
@@ -229,22 +365,92 @@ struct scm_heap_seg_data
      All segments usually point to the same one, scm_freelist.  */
   scm_freelist_t *freelist;
 
-  /* number of SCM words per object in this segment */
+  /* number of cells per object in this segment */
   int span;
+} scm_heap_seg_data_t;
 
-  /* If SEG_DATA->valid is non-zero, the conservative marking
-     functions will apply SEG_DATA->valid to the purported pointer and
-     SEG_DATA, and mark the object iff the function returns non-zero.
-     At the moment, I don't think anyone uses this.  */
-  int (*valid) ();
-};
 
 
+static scm_sizet init_heap_seg (SCM_CELLPTR, scm_sizet, scm_freelist_t *);
 
+typedef enum { return_on_error, abort_on_error } policy_on_error;
+static void alloc_some_heap (scm_freelist_t *, policy_on_error);
 
-static void scm_mark_weak_vector_spines (void);
-static scm_sizet init_heap_seg (SCM_CELLPTR, scm_sizet, scm_freelist_t *);
-static void alloc_some_heap (scm_freelist_t *);
+
+#define SCM_HEAP_SIZE \
+  (scm_master_freelist.heap_size + scm_master_freelist2.heap_size)
+#define SCM_MAX(A, B) ((A) > (B) ? (A) : (B))
+
+#define BVEC_GROW_SIZE  256
+#define BVEC_GROW_SIZE_IN_LIMBS (SCM_GC_CARD_BVEC_SIZE_IN_LIMBS * BVEC_GROW_SIZE)
+#define BVEC_GROW_SIZE_IN_BYTES (BVEC_GROW_SIZE_IN_LIMBS * sizeof (scm_c_bvec_limb_t))
+
+/* mark space allocation */
+
+typedef struct scm_mark_space_t
+{
+  scm_c_bvec_limb_t *bvec_space;
+  struct scm_mark_space_t *next;
+} scm_mark_space_t;
+
+static scm_mark_space_t *current_mark_space;
+static scm_mark_space_t **mark_space_ptr;
+static int current_mark_space_offset;
+static scm_mark_space_t *mark_space_head;
+
+static scm_c_bvec_limb_t *
+get_bvec ()
+{
+  scm_c_bvec_limb_t *res;
+
+  if (!current_mark_space)
+    {
+      SCM_SYSCALL (current_mark_space = (scm_mark_space_t *) malloc (sizeof (scm_mark_space_t)));
+      if (!current_mark_space)
+        scm_wta (SCM_UNDEFINED, "could not grow", "heap");
+
+      current_mark_space->bvec_space = NULL;
+      current_mark_space->next = NULL;
+
+      *mark_space_ptr = current_mark_space;
+      mark_space_ptr = &(current_mark_space->next);
+
+      return get_bvec ();
+    }
+
+  if (!(current_mark_space->bvec_space))
+    {
+      SCM_SYSCALL (current_mark_space->bvec_space =
+                   (scm_c_bvec_limb_t *) calloc (BVEC_GROW_SIZE_IN_BYTES, 1));
+      if (!(current_mark_space->bvec_space))
+        scm_wta (SCM_UNDEFINED, "could not grow", "heap");
+
+      current_mark_space_offset = 0;
+
+      return get_bvec ();
+    }
+
+  if (current_mark_space_offset == BVEC_GROW_SIZE_IN_LIMBS)
+    {
+      current_mark_space = NULL;
+
+      return get_bvec ();
+    }
+
+  res = current_mark_space->bvec_space + current_mark_space_offset;
+  current_mark_space_offset += SCM_GC_CARD_BVEC_SIZE_IN_LIMBS;
+
+  return res;
+}
+
+static void
+clear_mark_space ()
+{
+  scm_mark_space_t *ms;
+
+  for (ms = mark_space_head; ms; ms = ms->next)
+    memset (ms->bvec_space, 0, BVEC_GROW_SIZE_IN_BYTES);
+}
 
 
 \f
@@ -259,8 +465,8 @@ which_seg (SCM cell)
   int i;
 
   for (i = 0; i < scm_n_heap_segs; i++)
-    if (SCM_PTR_LE (scm_heap_table[i].bounds[0], (SCM_CELLPTR) cell)
-       && SCM_PTR_GT (scm_heap_table[i].bounds[1], (SCM_CELLPTR) cell))
+    if (SCM_PTR_LE (scm_heap_table[i].bounds[0], SCM2PTR (cell))
+       && SCM_PTR_GT (scm_heap_table[i].bounds[1], SCM2PTR (cell)))
       return i;
   fprintf (stderr, "which_seg: can't find segment containing cell %lx\n",
           SCM_UNPACK (cell));
@@ -268,14 +474,13 @@ which_seg (SCM cell)
 }
 
 
-#ifdef GUILE_NEW_GC_SCHEME
 static void
 map_free_list (scm_freelist_t *master, SCM freelist)
 {
   int last_seg = -1, count = 0;
   SCM f;
-  
-  for (f = freelist; SCM_NIMP (f); f = SCM_CDR (f))
+
+  for (f = freelist; !SCM_NULLP (f); f = SCM_FREE_CELL_CDR (f))
     {
       int this_seg = which_seg (f);
 
@@ -293,34 +498,8 @@ map_free_list (scm_freelist_t *master, SCM freelist)
     fprintf (stderr, "  %5d %d-cells in segment %d\n",
             count, master->span, last_seg);
 }
-#else
-static void
-map_free_list (scm_freelist_t *freelist)
-{
-  int last_seg = -1, count = 0;
-  SCM f;
-  
-  for (f = freelist->cells; SCM_NIMP (f); f = SCM_CDR (f))
-    {
-      int this_seg = which_seg (f);
 
-      if (this_seg != last_seg)
-       {
-         if (last_seg != -1)
-           fprintf (stderr, "  %5d %d-cells in segment %d\n",
-                    count, freelist->span, last_seg);
-         last_seg = this_seg;
-         count = 0;
-       }
-      count++;
-    }
-  if (last_seg != -1)
-    fprintf (stderr, "  %5d %d-cells in segment %d\n",
-            count, freelist->span, last_seg);
-}
-#endif
-
-SCM_DEFINE (scm_map_free_list, "map-free-list", 0, 0, 0, 
+SCM_DEFINE (scm_map_free_list, "map-free-list", 0, 0, 0,
             (),
             "Print debugging information about the free-list.\n"
             "`map-free-list' is only included in --enable-guile-debug builds of Guile.")
@@ -336,20 +515,14 @@ SCM_DEFINE (scm_map_free_list, "map-free-list", 0, 0, 0,
             scm_heap_table[i].span,
             scm_heap_table[i].bounds[1] - scm_heap_table[i].bounds[0]);
   fprintf (stderr, ")\n");
-#ifdef GUILE_NEW_GC_SCHEME
   map_free_list (&scm_master_freelist, scm_freelist);
   map_free_list (&scm_master_freelist2, scm_freelist2);
-#else
-  map_free_list (&scm_freelist);
-  map_free_list (&scm_freelist2);
-#endif
   fflush (stderr);
 
   return SCM_UNSPECIFIED;
 }
 #undef FUNC_NAME
 
-#ifdef GUILE_NEW_GC_SCHEME
 static int last_cluster;
 static int last_size;
 
@@ -358,8 +531,8 @@ free_list_length (char *title, int i, SCM freelist)
 {
   SCM ls;
   int n = 0;
-  for (ls = freelist; SCM_NNULLP (ls); ls = SCM_CDR (ls))
-    if (SCM_UNPACK_CAR (ls) == scm_tc_free_cell)
+  for (ls = freelist; !SCM_NULLP (ls); ls = SCM_FREE_CELL_CDR (ls))
+    if (SCM_FREE_CELL_P (ls))
       ++n;
     else
       {
@@ -406,18 +579,17 @@ free_list_lengths (char *title, scm_freelist_t *master, SCM freelist)
   fprintf (stderr, "\ntotal %d objects\n\n", n);
 }
 
-SCM_DEFINE (scm_free_list_length, "free-list-length", 0, 0, 0, 
+SCM_DEFINE (scm_free_list_length, "free-list-length", 0, 0, 0,
             (),
             "Print debugging information about the free-list.\n"
             "`free-list-length' is only included in --enable-guile-debug builds of Guile.")
 #define FUNC_NAME s_scm_free_list_length
 {
-  free_list_lengths ("1-words", &scm_master_freelist, scm_freelist);
-  free_list_lengths ("2-words", &scm_master_freelist2, scm_freelist2);
+  free_list_lengths ("1-cells", &scm_master_freelist, scm_freelist);
+  free_list_lengths ("2-cells", &scm_master_freelist2, scm_freelist2);
   return SCM_UNSPECIFIED;
 }
 #undef FUNC_NAME
-#endif
 
 #endif
 
@@ -429,57 +601,36 @@ static unsigned long scm_newcell2_count;
 
 /* Search freelist for anything that isn't marked as a free cell.
    Abort if we find something.  */
-#ifdef GUILE_NEW_GC_SCHEME
 static void
 scm_check_freelist (SCM freelist)
 {
   SCM f;
   int i = 0;
 
-  for (f = freelist; SCM_NIMP (f); f = SCM_CDR (f), i++)
-    if (SCM_CAR (f) != (SCM) scm_tc_free_cell)
+  for (f = freelist; !SCM_NULLP (f); f = SCM_FREE_CELL_CDR (f), i++)
+    if (!SCM_FREE_CELL_P (f))
       {
        fprintf (stderr, "Bad cell in freelist on newcell %lu: %d'th elt\n",
                 scm_newcell_count, i);
-       fflush (stderr);
        abort ();
       }
 }
-#else
-static void
-scm_check_freelist (scm_freelist_t *freelist)
-{
-  SCM f;
-  int i = 0;
-
-  for (f = freelist->cells; SCM_NIMP (f); f = SCM_CDR (f), i++)
-    if (SCM_CAR (f) != (SCM) scm_tc_free_cell)
-      {
-       fprintf (stderr, "Bad cell in freelist on newcell %lu: %d'th elt\n",
-                scm_newcell_count, i);
-       fflush (stderr);
-       abort ();
-      }
-}
-#endif
 
-static int scm_debug_check_freelist = 0;
-
-SCM_DEFINE (scm_gc_set_debug_check_freelist_x, "gc-set-debug-check-freelist!", 1, 0, 0, 
+SCM_DEFINE (scm_gc_set_debug_check_freelist_x, "gc-set-debug-check-freelist!", 1, 0, 0,
             (SCM flag),
             "If FLAG is #t, check the freelist for consistency on each cell allocation.\n"
             "This procedure only exists because the GUILE_DEBUG_FREELIST \n"
             "compile-time flag was selected.\n")
 #define FUNC_NAME s_scm_gc_set_debug_check_freelist_x
 {
+  /* [cmm] I did a double-take when I read this code the first time.
+     well, FWIW. */
   SCM_VALIDATE_BOOL_COPY (1, flag, scm_debug_check_freelist);
   return SCM_UNSPECIFIED;
 }
 #undef FUNC_NAME
 
 
-#ifdef GUILE_NEW_GC_SCHEME
-
 SCM
 scm_debug_newcell (void)
 {
@@ -494,13 +645,12 @@ scm_debug_newcell (void)
 
   /* The rest of this is supposed to be identical to the SCM_NEWCELL
      macro.  */
-  if (SCM_IMP (scm_freelist))
+  if (SCM_NULLP (scm_freelist))
     new = scm_gc_for_newcell (&scm_master_freelist, &scm_freelist);
   else
     {
       new = scm_freelist;
-      scm_freelist = SCM_CDR (scm_freelist);
-      SCM_SETCAR (new, scm_tc16_allocated);
+      scm_freelist = SCM_FREE_CELL_CDR (scm_freelist);
     }
 
   return new;
@@ -520,82 +670,54 @@ scm_debug_newcell2 (void)
 
   /* The rest of this is supposed to be identical to the SCM_NEWCELL
      macro.  */
-  if (SCM_IMP (scm_freelist2))
+  if (SCM_NULLP (scm_freelist2))
     new = scm_gc_for_newcell (&scm_master_freelist2, &scm_freelist2);
   else
     {
       new = scm_freelist2;
-      scm_freelist2 = SCM_CDR (scm_freelist2);
-      SCM_SETCAR (new, scm_tc16_allocated);
+      scm_freelist2 = SCM_FREE_CELL_CDR (scm_freelist2);
     }
 
   return new;
 }
 
-#else /* GUILE_NEW_GC_SCHEME */
-
-SCM
-scm_debug_newcell (void)
-{
-  SCM new;
-
-  scm_newcell_count++;
-  if (scm_debug_check_freelist)
-    {
-      scm_check_freelist (&scm_freelist);
-      scm_gc();
-    }
+#endif /* GUILE_DEBUG_FREELIST */
 
-  /* The rest of this is supposed to be identical to the SCM_NEWCELL
-     macro.  */
-  if (SCM_IMP (scm_freelist.cells))
-    new = scm_gc_for_newcell (&scm_freelist);
-  else
-    {
-      new = scm_freelist.cells;
-      scm_freelist.cells = SCM_CDR (scm_freelist.cells);
-      SCM_SETCAR (new, scm_tc16_allocated);
-      ++scm_cells_allocated;
-    }
+\f
 
-  return new;
+static unsigned long
+master_cells_allocated (scm_freelist_t *master)
+{
+  /* the '- 1' below is to ignore the cluster spine cells. */
+  int objects = master->clusters_allocated * (master->cluster_size - 1);
+  if (SCM_NULLP (master->clusters))
+    objects -= master->left_to_collect;
+  return master->span * objects;
 }
 
-SCM
-scm_debug_newcell2 (void)
+static unsigned long
+freelist_length (SCM freelist)
 {
-  SCM new;
-
-  scm_newcell2_count++;
-  if (scm_debug_check_freelist) {
-    scm_check_freelist (&scm_freelist2);
-    scm_gc();
-  }
-
-  /* The rest of this is supposed to be identical to the SCM_NEWCELL2
-     macro.  */
-  if (SCM_IMP (scm_freelist2.cells))
-    new = scm_gc_for_newcell (&scm_freelist2);
-  else
-    {
-      new = scm_freelist2.cells;
-      scm_freelist2.cells = SCM_CDR (scm_freelist2.cells);
-      SCM_SETCAR (new, scm_tc16_allocated);
-      scm_cells_allocated += 2;
-    }
-
-  return new;
+  int n;
+  for (n = 0; !SCM_NULLP (freelist); freelist = SCM_FREE_CELL_CDR (freelist))
+    ++n;
+  return n;
 }
 
-#endif /* GUILE_NEW_GC_SCHEME */
-#endif /* GUILE_DEBUG_FREELIST */
-
-\f
+static unsigned long
+compute_cells_allocated ()
+{
+  return (scm_cells_allocated
+         + master_cells_allocated (&scm_master_freelist)
+         + master_cells_allocated (&scm_master_freelist2)
+         - scm_master_freelist.span * freelist_length (scm_freelist)
+         - scm_master_freelist2.span * freelist_length (scm_freelist2));
+}
 
 /* {Scheme Interface to GC}
  */
 
-SCM_DEFINE (scm_gc_stats, "gc-stats", 0, 0, 0, 
+SCM_DEFINE (scm_gc_stats, "gc-stats", 0, 0, 0,
             (),
            "Returns an association list of statistics about Guile's current use of storage.  ")
 #define FUNC_NAME s_scm_gc_stats
@@ -608,10 +730,17 @@ SCM_DEFINE (scm_gc_stats, "gc-stats", 0, 0, 0,
   long int local_scm_heap_size;
   long int local_scm_cells_allocated;
   long int local_scm_gc_time_taken;
+  long int local_scm_gc_times;
+  long int local_scm_gc_mark_time_taken;
+  long int local_scm_gc_sweep_time_taken;
+  double local_scm_gc_cells_swept;
+  double local_scm_gc_cells_marked;
   SCM answer;
 
   SCM_DEFER_INTS;
-  scm_block_gc = 1;
+
+  ++scm_block_gc;
+
  retry:
   heap_segs = SCM_EOL;
   n = scm_n_heap_segs;
@@ -621,24 +750,33 @@ SCM_DEFINE (scm_gc_stats, "gc-stats", 0, 0, 0,
                          heap_segs);
   if (scm_n_heap_segs != n)
     goto retry;
-  scm_block_gc = 0;
 
-  /// ? ?? ? 
+  --scm_block_gc;
+
+  /* Below, we cons to produce the resulting list.  We want a snapshot of
+   * the heap situation before consing.
+   */
   local_scm_mtrigger = scm_mtrigger;
   local_scm_mallocated = scm_mallocated;
-#ifdef GUILE_NEW_GC_SCHEME
-  local_scm_heap_size = scm_master_freelist.heap_size; /*fixme*/
-#else
-  local_scm_heap_size = scm_freelist.heap_size; /*fixme*/
-#endif
-  local_scm_cells_allocated = scm_cells_allocated;
+  local_scm_heap_size = SCM_HEAP_SIZE;
+  local_scm_cells_allocated = compute_cells_allocated ();
   local_scm_gc_time_taken = scm_gc_time_taken;
+  local_scm_gc_mark_time_taken = scm_gc_mark_time_taken;
+  local_scm_gc_sweep_time_taken = scm_gc_sweep_time_taken;
+  local_scm_gc_times = scm_gc_times;
+  local_scm_gc_cells_swept = scm_gc_cells_swept_acc;
+  local_scm_gc_cells_marked = scm_gc_cells_marked_acc;
 
   answer = scm_listify (scm_cons (sym_gc_time_taken, scm_ulong2num (local_scm_gc_time_taken)),
                        scm_cons (sym_cells_allocated, scm_ulong2num (local_scm_cells_allocated)),
                        scm_cons (sym_heap_size, scm_ulong2num (local_scm_heap_size)),
                        scm_cons (sym_mallocated, scm_ulong2num (local_scm_mallocated)),
                        scm_cons (sym_mtrigger, scm_ulong2num (local_scm_mtrigger)),
+                       scm_cons (sym_times, scm_ulong2num (local_scm_gc_times)),
+                        scm_cons (sym_gc_mark_time_taken, scm_ulong2num (local_scm_gc_mark_time_taken)),
+                        scm_cons (sym_gc_sweep_time_taken, scm_ulong2num (local_scm_gc_sweep_time_taken)),
+                        scm_cons (sym_cells_marked, scm_dbl2big (local_scm_gc_cells_marked)),
+                        scm_cons (sym_cells_swept, scm_dbl2big (local_scm_gc_cells_swept)),
                        scm_cons (sym_heap_segments, heap_segs),
                        SCM_UNDEFINED);
   SCM_ALLOW_INTS;
@@ -647,36 +785,46 @@ SCM_DEFINE (scm_gc_stats, "gc-stats", 0, 0, 0,
 #undef FUNC_NAME
 
 
-void 
-scm_gc_start (const char *what)
+static void
+gc_start_stats (const char *what)
 {
-  scm_gc_rt = SCM_INUM (scm_get_internal_run_time ());
-  /* scm_gc_cells_collected = 0; */
+  t_before_gc = scm_c_get_internal_run_time ();
+  scm_gc_cells_swept = 0;
+  scm_gc_cells_collected = 0;
+  scm_gc_yield_1 = scm_gc_yield;
+  scm_gc_yield = (scm_cells_allocated
+                 + master_cells_allocated (&scm_master_freelist)
+                 + master_cells_allocated (&scm_master_freelist2));
   scm_gc_malloc_collected = 0;
   scm_gc_ports_collected = 0;
 }
 
-void 
-scm_gc_end ()
+
+static void
+gc_end_stats ()
 {
-  scm_gc_rt = SCM_INUM (scm_get_internal_run_time ()) - scm_gc_rt;
-  scm_gc_time_taken += scm_gc_rt;
-  scm_system_async_mark (scm_gc_async);
+  unsigned long t = scm_c_get_internal_run_time ();
+  scm_gc_time_taken += (t - t_before_gc);
+  scm_gc_sweep_time_taken += (t - t_before_sweep);
+  ++scm_gc_times;
+
+  scm_gc_cells_marked_acc += scm_gc_cells_swept - scm_gc_cells_collected;
+  scm_gc_cells_swept_acc += scm_gc_cells_swept;
 }
 
 
-SCM_DEFINE (scm_object_address, "object-address", 1, 0, 0, 
+SCM_DEFINE (scm_object_address, "object-address", 1, 0, 0,
             (SCM obj),
            "Return an integer that for the lifetime of @var{obj} is uniquely\n"
            "returned by this function for @var{obj}")
 #define FUNC_NAME s_scm_object_address
 {
-  return scm_ulong2num ((unsigned long) obj);
+  return scm_ulong2num ((unsigned long) SCM_UNPACK (obj));
 }
 #undef FUNC_NAME
 
 
-SCM_DEFINE (scm_gc, "gc", 0, 0, 0, 
+SCM_DEFINE (scm_gc, "gc", 0, 0, 0,
            (),
            "Scans all of SCM objects and reclaims for further use those that are\n"
            "no longer accessible.")
@@ -694,7 +842,38 @@ SCM_DEFINE (scm_gc, "gc", 0, 0, 0,
 /* {C Interface For When GC is Triggered}
  */
 
-#ifdef GUILE_NEW_GC_SCHEME
+static void
+adjust_min_yield (scm_freelist_t *freelist)
+{
+  /* min yield is adjusted upwards so that next predicted total yield
+   * (allocated cells actually freed by GC) becomes
+   * `min_yield_fraction' of total heap size.  Note, however, that
+   * the absolute value of min_yield will correspond to `collected'
+   * on one master (the one which currently is triggering GC).
+   *
+   * The reason why we look at total yield instead of cells collected
+   * on one list is that we want to take other freelists into account.
+   * On this freelist, we know that (local) yield = collected cells,
+   * but that's probably not the case on the other lists.
+   *
+   * (We might consider computing a better prediction, for example
+   *  by computing an average over multiple GC:s.)
+   */
+  if (freelist->min_yield_fraction)
+    {
+      /* Pick largest of last two yields. */
+      int delta = ((SCM_HEAP_SIZE * freelist->min_yield_fraction / 100)
+                  - (long) SCM_MAX (scm_gc_yield_1, scm_gc_yield));
+#ifdef DEBUGINFO
+      fprintf (stderr, " after GC = %d, delta = %d\n",
+              scm_cells_allocated,
+              delta);
+#endif
+      if (delta > 0)
+       freelist->min_yield += delta;
+    }
+}
+
 
 /* When we get POSIX threads support, the master will be global and
  * common while the freelist will be individual for each thread.
@@ -709,24 +888,55 @@ scm_gc_for_newcell (scm_freelist_t *master, SCM *freelist)
     {
       if (SCM_NULLP (master->clusters))
        {
-         if (master->grow_heap_p)
+         if (master->grow_heap_p || scm_block_gc)
            {
+             /* In order to reduce gc frequency, try to allocate a new heap
+              * segment first, even if gc might find some free cells.  If we
+              * can't obtain a new heap segment, we will try gc later.
+              */
              master->grow_heap_p = 0;
-             alloc_some_heap (master);
+             alloc_some_heap (master, return_on_error);
+           }
+         if (SCM_NULLP (master->clusters))
+           {
+             /* The heap was not grown, either because it wasn't scheduled to
+              * grow, or because there was not enough memory available.  In
+              * both cases we have to try gc to get some free cells.
+              */
+#ifdef DEBUGINFO
+             fprintf (stderr, "allocated = %d, ",
+                      scm_cells_allocated
+                      + master_cells_allocated (&scm_master_freelist)
+                      + master_cells_allocated (&scm_master_freelist2));
+#endif
+             scm_igc ("cells");
+             adjust_min_yield (master);
+             if (SCM_NULLP (master->clusters))
+               {
+                 /* gc could not free any cells.  Now, we _must_ allocate a
+                  * new heap segment, because there is no other possibility
+                  * to provide a new cell for the caller.
+                  */
+                 alloc_some_heap (master, abort_on_error);
+               }
            }
-         else
-           scm_igc ("cells");
        }
       cell = SCM_CAR (master->clusters);
       master->clusters = SCM_CDR (master->clusters);
+      ++master->clusters_allocated;
     }
   while (SCM_NULLP (cell));
+
+#ifdef GUILE_DEBUG_FREELIST
+  scm_check_freelist (cell);
+#endif
+
   --scm_ints_disabled;
-  *freelist = SCM_CDR (cell);
-  SCM_SETCAR (cell, scm_tc16_allocated);
+  *freelist = SCM_FREE_CELL_CDR (cell);
   return cell;
 }
 
+
 #if 0
 /* This is a support routine which can be used to reserve a cluster
  * for some special use, such as debugging.  It won't be useful until
@@ -743,42 +953,21 @@ scm_alloc_cluster (scm_freelist_t *master)
 }
 #endif
 
-#else /* GUILE_NEW_GC_SCHEME */
-
-void
-scm_gc_for_alloc (scm_freelist_t *freelist)
-{
-  SCM_REDEFER_INTS;
-  scm_igc ("cells");
-#ifdef GUILE_DEBUG_FREELIST
-  fprintf (stderr, "Collected: %d, min_yield: %d\n",
-          freelist->collected, MIN_GC_YIELD (freelist));
-#endif
-  if ((freelist->collected < MIN_GC_YIELD (freelist))
-      || SCM_IMP (freelist->cells))
-    alloc_some_heap (freelist);
-  SCM_REALLOW_INTS;
-}
-
 
-SCM 
-scm_gc_for_newcell (scm_freelist_t *freelist)
-{
-  SCM fl;
-  scm_gc_for_alloc (freelist);
-  fl = freelist->cells;
-  freelist->cells = SCM_CDR (fl);
-  SCM_SETCAR (fl, scm_tc16_allocated);
-  return fl;
-}
+scm_c_hook_t scm_before_gc_c_hook;
+scm_c_hook_t scm_before_mark_c_hook;
+scm_c_hook_t scm_before_sweep_c_hook;
+scm_c_hook_t scm_after_sweep_c_hook;
+scm_c_hook_t scm_after_gc_c_hook;
 
-#endif /* GUILE_NEW_GC_SCHEME */
 
 void
 scm_igc (const char *what)
 {
   int j;
 
+  ++scm_gc_running_p;
+  scm_c_hook_run (&scm_before_gc_c_hook, 0);
 #ifdef DEBUGINFO
   fprintf (stderr,
           SCM_NULLP (scm_freelist)
@@ -792,14 +981,14 @@ scm_igc (const char *what)
 
   /* fprintf (stderr, "gc: %s\n", what); */
 
-  scm_gc_start (what);
-
   if (!scm_stack_base || scm_block_gc)
     {
-      scm_gc_end ();
+      --scm_gc_running_p;
       return;
     }
 
+  gc_start_stats (what);
+
   if (scm_mallocated < 0)
     /* The byte count of allocated objects has underflowed.  This is
        probably because you forgot to report the sizes of objects you
@@ -815,39 +1004,13 @@ scm_igc (const char *what)
 
   ++scm_gc_heap_lock;
 
-  scm_weak_vectors = SCM_EOL;
-
-  scm_guardian_gc_init ();
-
-  /* unprotect any struct types with no instances */
-#if 0
-  {
-    SCM type_list;
-    SCM * pos;
-
-    pos = &scm_type_obj_list;
-    type_list = scm_type_obj_list;
-    while (type_list != SCM_EOL)
-      if (SCM_VELTS (SCM_CAR (type_list))[scm_struct_i_refcnt])
-       {
-         pos = SCM_CDRLOC (type_list);
-         type_list = SCM_CDR (type_list);
-       }
-      else
-       {
-         *pos = SCM_CDR (type_list);
-         type_list = SCM_CDR (type_list);
-       }
-  }
-#endif
-
   /* flush dead entries from the continuation stack */
   {
     int x;
     int bound;
     SCM * elts;
     elts = SCM_VELTS (scm_continuation_stack);
-    bound = SCM_LENGTH (scm_continuation_stack);
+    bound = SCM_VECTOR_LENGTH (scm_continuation_stack);
     x = SCM_INUM (scm_continuation_stack_ptr);
     while (x < bound)
       {
@@ -856,16 +1019,13 @@ scm_igc (const char *what)
       }
   }
 
+  scm_c_hook_run (&scm_before_mark_c_hook, 0);
+
+  clear_mark_space ();
+
 #ifndef USE_THREADS
-  
-  /* Protect from the C stack.  This must be the first marking
-   * done because it provides information about what objects
-   * are "in-use" by the C code.   "in-use" objects are  those
-   * for which the values from SCM_LENGTH and SCM_CHARS must remain
-   * usable.   This requirement is stricter than a liveness
-   * requirement -- in particular, it constrains the implementation
-   * of scm_vector_set_length_x.
-   */
+
+  /* Mark objects on the C stack. */
   SCM_FLUSH_REGISTER_WINDOWS;
   /* This assumes that all registers are saved into the jmp_buf */
   setjmp (scm_save_regs_gc_mark);
@@ -875,22 +1035,11 @@ scm_igc (const char *what)
                          / sizeof (SCM_STACKITEM)));
 
   {
-    /* stack_len is long rather than scm_sizet in order to guarantee that
-       &stack_len is long aligned */
+    scm_sizet stack_len = scm_stack_size (scm_stack_base);
 #ifdef SCM_STACK_GROWS_UP
-#ifdef nosve
-    long stack_len = (SCM_STACKITEM *) (&stack_len) - scm_stack_base;
+    scm_mark_locations (scm_stack_base, stack_len);
 #else
-    long stack_len = scm_stack_size (scm_stack_base);
-#endif
-    scm_mark_locations (scm_stack_base, (scm_sizet) stack_len);
-#else
-#ifdef nosve
-    long stack_len = scm_stack_base - (SCM_STACKITEM *) (&stack_len);
-#else
-    long stack_len = scm_stack_size (scm_stack_base);
-#endif
-    scm_mark_locations ((scm_stack_base - stack_len), (scm_sizet) stack_len);
+    scm_mark_locations (scm_stack_base - stack_len, stack_len);
 #endif
   }
 
@@ -901,47 +1050,50 @@ scm_igc (const char *what)
 
 #endif /* USE_THREADS */
 
-  /* FIXME: insert a phase to un-protect string-data preserved
-   * in scm_vector_set_length_x.
-   */
-
   j = SCM_NUM_PROTECTS;
   while (j--)
     scm_gc_mark (scm_sys_protects[j]);
 
   /* FIXME: we should have a means to register C functions to be run
    * in different phases of GC
-   */ 
+   */
   scm_mark_subr_table ();
-  
+
 #ifndef USE_THREADS
   scm_gc_mark (scm_root->handle);
 #endif
-  
-  scm_mark_weak_vector_spines ();
 
-  scm_guardian_zombify ();
+  t_before_sweep = scm_c_get_internal_run_time ();
+  scm_gc_mark_time_taken += (t_before_sweep - t_before_gc);
+
+  scm_c_hook_run (&scm_before_sweep_c_hook, 0);
 
   scm_gc_sweep ();
 
+  scm_c_hook_run (&scm_after_sweep_c_hook, 0);
+
   --scm_gc_heap_lock;
-  scm_gc_end ();
+  gc_end_stats ();
 
 #ifdef USE_THREADS
   SCM_THREAD_CRITICAL_SECTION_END;
 #endif
+  scm_c_hook_run (&scm_after_gc_c_hook, 0);
+  --scm_gc_running_p;
 }
 
 \f
-/* {Mark/Sweep} 
+
+/* {Mark/Sweep}
  */
 
 
 
 /* Mark an object precisely.
  */
-void 
+void
 scm_gc_mark (SCM p)
+#define FUNC_NAME "scm_gc_mark"
 {
   register long i;
   register SCM ptr;
@@ -953,118 +1105,104 @@ gc_mark_loop:
     return;
 
 gc_mark_nimp:
-  if (SCM_NCELLP (ptr))
+  if (!SCM_CELLP (ptr))
+    SCM_MISC_ERROR ("rogue pointer in heap", SCM_EOL);
+
+#if (defined (GUILE_DEBUG_FREELIST))
+
+  if (SCM_GC_IN_CARD_HEADERP (SCM2PTR (ptr)))
     scm_wta (ptr, "rogue pointer in heap", NULL);
 
+#endif
+
+  if (SCM_GCMARKP (ptr))
+    return;
+
+  SCM_SETGCMARK (ptr);
+
   switch (SCM_TYP7 (ptr))
     {
     case scm_tcs_cons_nimcar:
-      if (SCM_GCMARKP (ptr))
-       break;
-      SCM_SETGCMARK (ptr);
-      if (SCM_IMP (SCM_CDR (ptr))) /* SCM_IMP works even with a GC mark */
+      if (SCM_IMP (SCM_CDR (ptr)))
        {
          ptr = SCM_CAR (ptr);
          goto gc_mark_nimp;
        }
       scm_gc_mark (SCM_CAR (ptr));
-      ptr = SCM_GCCDR (ptr);
+      ptr = SCM_CDR (ptr);
       goto gc_mark_nimp;
     case scm_tcs_cons_imcar:
-      if (SCM_GCMARKP (ptr))
-       break;
-      SCM_SETGCMARK (ptr);
-      ptr = SCM_GCCDR (ptr);
+      ptr = SCM_CDR (ptr);
       goto gc_mark_loop;
     case scm_tc7_pws:
-      if (SCM_GCMARKP (ptr))
-       break;
-      SCM_SETGCMARK (ptr);
-      scm_gc_mark (SCM_CELL_WORD (ptr, 2));
-      ptr = SCM_GCCDR (ptr);
+      scm_gc_mark (SCM_CELL_OBJECT_2 (ptr));
+      ptr = SCM_CDR (ptr);
       goto gc_mark_loop;
     case scm_tcs_cons_gloc:
-      if (SCM_GCMARKP (ptr))
-       break;
-      SCM_SETGCMARK (ptr);
       {
-       SCM vcell;
-       vcell = SCM_CAR (ptr) - 1L;
-       switch (SCM_UNPACK (SCM_CDR (vcell)))
+       /* Dirk:FIXME:: The following code is super ugly:  ptr may be a struct
+        * or a gloc.  If it is a gloc, the cell word #0 of ptr is a pointer
+        * to a heap cell.  If it is a struct, the cell word #0 of ptr is a
+        * pointer to a struct vtable data region. The fact that these are
+        * accessed in the same way restricts the possibilites to change the
+        * data layout of structs or heap cells.
+        */
+       scm_bits_t word0 = SCM_CELL_WORD_0 (ptr) - scm_tc3_cons_gloc;
+       scm_bits_t * vtable_data = (scm_bits_t *) word0; /* access as struct */
+       if (vtable_data [scm_vtable_index_vcell] != 0)
          {
-         default:
-           scm_gc_mark (vcell);
-           ptr = SCM_GCCDR (ptr);
-           goto gc_mark_loop;
-         case 1:               /* ! */
-         case 0:               /* ! */
-           {
-             SCM layout;
-             SCM * vtable_data;
-             int len;
-             char * fields_desc;
-             register SCM * mem;
-             register int x;
-
-             vtable_data = (SCM *)vcell;
-             layout = vtable_data[scm_vtable_index_layout];
-             len = SCM_LENGTH (layout);
-             fields_desc = SCM_CHARS (layout);
-             /* We're using SCM_GCCDR here like STRUCT_DATA, except
-                 that it removes the mark */
-             mem = (SCM *)SCM_GCCDR (ptr);
-             
-             if (SCM_UNPACK (vtable_data[scm_struct_i_flags]) & SCM_STRUCTF_ENTITY)
-               {
-                 scm_gc_mark (mem[scm_struct_i_procedure]);
-                 scm_gc_mark (mem[scm_struct_i_setter]);
-               }
-             if (len)
-               {
-                 for (x = 0; x < len - 2; x += 2, ++mem)
-                   if (fields_desc[x] == 'p')
-                     scm_gc_mark (*mem);
-                 if (fields_desc[x] == 'p')
-                   {
-                     int j;
-                     if (SCM_LAYOUT_TAILP (fields_desc[x + 1]))
-                       for (j = (long int) *mem; x; --x)
-                         scm_gc_mark (*++mem);
-                     else
-                       scm_gc_mark (*mem);
-                   }
-               }
-             if (!SCM_CDR (vcell))
-               {
-                 SCM_SETGCMARK (vcell);
-                 ptr = vtable_data[scm_vtable_index_vtable];
-                 goto gc_mark_loop;
-               }
-           }
+            /* ptr is a gloc */
+            SCM gloc_car = SCM_PACK (word0);
+            scm_gc_mark (gloc_car);
+            ptr = SCM_CDR (ptr);
+            goto gc_mark_loop;
+          }
+        else
+          {
+            /* ptr is a struct */
+            SCM layout = SCM_PACK (vtable_data [scm_vtable_index_layout]);
+            int len = SCM_SYMBOL_LENGTH (layout);
+            char * fields_desc = SCM_SYMBOL_CHARS (layout);
+            scm_bits_t * struct_data = (scm_bits_t *) SCM_STRUCT_DATA (ptr);
+
+            if (vtable_data[scm_struct_i_flags] & SCM_STRUCTF_ENTITY)
+              {
+                scm_gc_mark (SCM_PACK (struct_data[scm_struct_i_procedure]));
+                scm_gc_mark (SCM_PACK (struct_data[scm_struct_i_setter]));
+              }
+            if (len)
+              {
+                int x;
+
+                for (x = 0; x < len - 2; x += 2, ++struct_data)
+                  if (fields_desc[x] == 'p')
+                    scm_gc_mark (SCM_PACK (*struct_data));
+                if (fields_desc[x] == 'p')
+                  {
+                    if (SCM_LAYOUT_TAILP (fields_desc[x + 1]))
+                      for (x = *struct_data; x; --x)
+                        scm_gc_mark (SCM_PACK (*++struct_data));
+                    else
+                      scm_gc_mark (SCM_PACK (*struct_data));
+                  }
+              }
+            /* mark vtable */
+            ptr = SCM_PACK (vtable_data [scm_vtable_index_vtable]);
+            goto gc_mark_loop;
          }
       }
       break;
     case scm_tcs_closures:
-      if (SCM_GCMARKP (ptr))
-       break;
-      SCM_SETGCMARK (ptr);
       if (SCM_IMP (SCM_CDR (ptr)))
        {
          ptr = SCM_CLOSCAR (ptr);
          goto gc_mark_nimp;
        }
       scm_gc_mark (SCM_CLOSCAR (ptr));
-      ptr = SCM_GCCDR (ptr);
+      ptr = SCM_CDR (ptr);
       goto gc_mark_nimp;
     case scm_tc7_vector:
-    case scm_tc7_lvector:
-#ifdef CCLO
-    case scm_tc7_cclo:
-#endif
-      if (SCM_GC8MARKP (ptr))
-       break;
-      SCM_SETGC8MARK (ptr);
-      i = SCM_LENGTH (ptr);
+      i = SCM_VECTOR_LENGTH (ptr);
       if (i == 0)
        break;
       while (--i > 0)
@@ -1072,18 +1210,21 @@ gc_mark_nimp:
          scm_gc_mark (SCM_VELTS (ptr)[i]);
       ptr = SCM_VELTS (ptr)[0];
       goto gc_mark_loop;
-    case scm_tc7_contin:
-      if SCM_GC8MARKP
-       (ptr) break;
-      SCM_SETGC8MARK (ptr);
-      if (SCM_VELTS (ptr))
-       scm_mark_locations (SCM_VELTS_AS_STACKITEMS (ptr),
-                           (scm_sizet)
-                           (SCM_LENGTH (ptr) +
-                            (sizeof (SCM_STACKITEM) + -1 +
-                             sizeof (scm_contregs)) /
-                            sizeof (SCM_STACKITEM)));
-      break;
+#ifdef CCLO
+    case scm_tc7_cclo:
+      {
+       unsigned long int i = SCM_CCLO_LENGTH (ptr);
+       unsigned long int j;
+       for (j = 1; j != i; ++j)
+         {
+           SCM obj = SCM_CCLO_REF (ptr, j);
+           if (!SCM_IMP (obj))
+             scm_gc_mark (obj);
+         }
+       ptr = SCM_CCLO_REF (ptr, 0);
+       goto gc_mark_loop;
+      }
+#endif
 #ifdef HAVE_ARRAYS
     case scm_tc7_bvect:
     case scm_tc7_byvect:
@@ -1098,22 +1239,15 @@ gc_mark_nimp:
 #endif
 #endif
     case scm_tc7_string:
-      SCM_SETGC8MARK (ptr);
       break;
 
     case scm_tc7_substring:
-      if (SCM_GC8MARKP(ptr))
-       break;
-      SCM_SETGC8MARK (ptr);
       ptr = SCM_CDR (ptr);
       goto gc_mark_loop;
 
     case scm_tc7_wvect:
-      if (SCM_GC8MARKP(ptr))
-       break;
       SCM_WVECT_GC_CHAIN (ptr) = scm_weak_vectors;
       scm_weak_vectors = ptr;
-      SCM_SETGC8MARK (ptr);
       if (SCM_IS_WHVEC_ANY (ptr))
        {
          int x;
@@ -1121,10 +1255,10 @@ gc_mark_nimp:
          int weak_keys;
          int weak_values;
 
-         len = SCM_LENGTH (ptr);
+         len = SCM_VECTOR_LENGTH (ptr);
          weak_keys = SCM_IS_WHVEC (ptr) || SCM_IS_WHVEC_B (ptr);
          weak_values = SCM_IS_WHVEC_V (ptr) || SCM_IS_WHVEC_B (ptr);
-         
+
          for (x = 0; x < len; ++x)
            {
              SCM alist;
@@ -1141,7 +1275,7 @@ gc_mark_nimp:
 
                  kvpair = SCM_CAR (alist);
                  next_alist = SCM_CDR (alist);
-                 /* 
+                 /*
                   * Do not do this:
                   *    SCM_SETGCMARK (alist);
                   *    SCM_SETGCMARK (kvpair);
@@ -1161,7 +1295,7 @@ gc_mark_nimp:
                  if (!weak_keys)
                    scm_gc_mark (SCM_CAR (kvpair));
                  if (!weak_values)
-                   scm_gc_mark (SCM_GCCDR (kvpair));
+                   scm_gc_mark (SCM_CDR (kvpair));
                  alist = next_alist;
                }
              if (SCM_NIMP (alist))
@@ -1170,29 +1304,17 @@ gc_mark_nimp:
        }
       break;
 
-    case scm_tc7_msymbol:
-      if (SCM_GC8MARKP(ptr))
-       break;
-      SCM_SETGC8MARK (ptr);
-      scm_gc_mark (SCM_SYMBOL_FUNC (ptr));
-      ptr = SCM_SYMBOL_PROPS (ptr);
+    case scm_tc7_symbol:
+      ptr = SCM_PROP_SLOTS (ptr);
       goto gc_mark_loop;
-    case scm_tc7_ssymbol:
-      if (SCM_GC8MARKP(ptr))
-       break;
-      SCM_SETGC8MARK (ptr);
-      break;
     case scm_tcs_subrs:
       break;
     case scm_tc7_port:
       i = SCM_PTOBNUM (ptr);
       if (!(i < scm_numptob))
        goto def;
-      if (SCM_GC8MARKP (ptr))
-       break;
-      SCM_SETGC8MARK (ptr);
       if (SCM_PTAB_ENTRY(ptr))
-       scm_gc_mark (SCM_PTAB_ENTRY(ptr)->file_name);
+       scm_gc_mark (SCM_FILENAME (ptr));
       if (scm_ptobs[i].mark)
        {
          ptr = (scm_ptobs[i].mark) (ptr);
@@ -1202,14 +1324,10 @@ gc_mark_nimp:
        return;
       break;
     case scm_tc7_smob:
-      if (SCM_GC8MARKP (ptr))
-       break;
-      SCM_SETGC8MARK (ptr);
-      switch (SCM_GCTYP16 (ptr))
+      switch (SCM_TYP16 (ptr))
        { /* should be faster than going through scm_smobs */
        case scm_tc_free_cell:
          /* printf("found free_cell %X ", ptr); fflush(stdout); */
-        case scm_tc16_allocated:
        case scm_tc16_big:
        case scm_tc16_real:
        case scm_tc16_complex:
@@ -1228,194 +1346,134 @@ gc_mark_nimp:
        }
       break;
     default:
-    def:scm_wta (ptr, "unknown type in ", "gc_mark");
+    def:
+      SCM_MISC_ERROR ("unknown type", SCM_EOL);
     }
 }
+#undef FUNC_NAME
 
 
 /* Mark a Region Conservatively
  */
 
-void 
+void
 scm_mark_locations (SCM_STACKITEM x[], scm_sizet n)
 {
-  register long m = n;
-  register int i, j;
-  register SCM_CELLPTR ptr;
-
-  while (0 <= --m)
-    if (SCM_CELLP (*(SCM **) (& x[m])))
-      {
-       ptr = (SCM_CELLPTR) SCM2PTR ((*(SCM **) & x[m]));
-       i = 0;
-       j = scm_n_heap_segs - 1;
-       if (   SCM_PTR_LE (scm_heap_table[i].bounds[0], ptr)
-           && SCM_PTR_GT (scm_heap_table[j].bounds[1], ptr))
-         {
-           while (i <= j)
-             {
-               int seg_id;
-               seg_id = -1;
-               if (   (i == j)
-                   || SCM_PTR_GT (scm_heap_table[i].bounds[1], ptr))
-                 seg_id = i;
-               else if (SCM_PTR_LE (scm_heap_table[j].bounds[0], ptr))
-                 seg_id = j;
-               else
-                 {
-                   int k;
-                   k = (i + j) / 2;
-                   if (k == i)
-                     break;
-                   if (SCM_PTR_GT (scm_heap_table[k].bounds[1], ptr))
-                     {
-                       j = k;
-                       ++i;
-                       if (SCM_PTR_LE (scm_heap_table[i].bounds[0], ptr))
-                         continue;
-                       else
-                         break;
-                     }
-                   else if (SCM_PTR_LE (scm_heap_table[k].bounds[0], ptr))
-                     {
-                       i = k;
-                       --j;
-                       if (SCM_PTR_GT (scm_heap_table[j].bounds[1], ptr))
-                         continue;
-                       else
-                         break;
-                     }
-                 }
-               if (   !scm_heap_table[seg_id].valid
-                   || scm_heap_table[seg_id].valid (ptr,
-                                                    &scm_heap_table[seg_id]))
-                 scm_gc_mark (*(SCM *) & x[m]);
-               break;
-             }
-
-         }
-      }
-}
+  unsigned long m;
 
-
-/* The following is a C predicate which determines if an SCM value can be
-   regarded as a pointer to a cell on the heap.  The code is duplicated
-   from scm_mark_locations.  */
-
-
-int
-scm_cellp (SCM value)
-{
-  register int i, j;
-  register SCM_CELLPTR ptr;
-  
-  if SCM_CELLP (*(SCM **) (& value))
+  for (m = 0; m < n; ++m)
     {
-      ptr = (SCM_CELLPTR) SCM2PTR ((*(SCM **) & value));
-      i = 0;
-      j = scm_n_heap_segs - 1;
-      if (   SCM_PTR_LE (scm_heap_table[i].bounds[0], ptr)
-            && SCM_PTR_GT (scm_heap_table[j].bounds[1], ptr))
+      SCM obj = * (SCM *) &x[m];
+      if (SCM_CELLP (obj))
        {
-         while (i <= j)
+         SCM_CELLPTR ptr = SCM2PTR (obj);
+         int i = 0;
+         int j = scm_n_heap_segs - 1;
+         if (SCM_PTR_LE (scm_heap_table[i].bounds[0], ptr)
+             && SCM_PTR_GT (scm_heap_table[j].bounds[1], ptr))
            {
-             int seg_id;
-             seg_id = -1;
-             if (   (i == j)
-                    || SCM_PTR_GT (scm_heap_table[i].bounds[1], ptr))
-               seg_id = i;
-             else if (SCM_PTR_LE (scm_heap_table[j].bounds[0], ptr))
-               seg_id = j;
-             else
+             while (i <= j)
                {
-                 int k;
-                 k = (i + j) / 2;
-                 if (k == i)
-                   break;
-                 if (SCM_PTR_GT (scm_heap_table[k].bounds[1], ptr))
-                   {
-                     j = k;
-                     ++i;
-                     if (SCM_PTR_LE (scm_heap_table[i].bounds[0], ptr))
-                       continue;
-                     else
-                       break;
-                   }
-                 else if (SCM_PTR_LE (scm_heap_table[k].bounds[0], ptr))
+                 int seg_id;
+                 seg_id = -1;
+                 if ((i == j)
+                     || SCM_PTR_GT (scm_heap_table[i].bounds[1], ptr))
+                   seg_id = i;
+                 else if (SCM_PTR_LE (scm_heap_table[j].bounds[0], ptr))
+                   seg_id = j;
+                 else
                    {
-                     i = k;
-                     --j;
-                     if (SCM_PTR_GT (scm_heap_table[j].bounds[1], ptr))
-                       continue;
-                     else
+                     int k;
+                     k = (i + j) / 2;
+                     if (k == i)
                        break;
+                     if (SCM_PTR_GT (scm_heap_table[k].bounds[1], ptr))
+                       {
+                         j = k;
+                         ++i;
+                         if (SCM_PTR_LE (scm_heap_table[i].bounds[0], ptr))
+                           continue;
+                         else
+                           break;
+                       }
+                     else if (SCM_PTR_LE (scm_heap_table[k].bounds[0], ptr))
+                       {
+                         i = k;
+                         --j;
+                         if (SCM_PTR_GT (scm_heap_table[j].bounds[1], ptr))
+                           continue;
+                         else
+                           break;
+                       }
                    }
+
+                  if (SCM_GC_IN_CARD_HEADERP (ptr))
+                    break;
+
+                 if (scm_heap_table[seg_id].span == 1
+                     || SCM_DOUBLE_CELLP (obj))
+                    scm_gc_mark (obj);
+                  
+                 break;
                }
-             if (   !scm_heap_table[seg_id].valid
-                    || scm_heap_table[seg_id].valid (ptr,
-                                                     &scm_heap_table[seg_id]))
-               return 1;
-             break;
            }
-
        }
     }
-  return 0;
 }
 
 
-static void
-scm_mark_weak_vector_spines ()
+/* The function scm_cellp determines whether an SCM value can be regarded as a
+ * pointer to a cell on the heap.  Binary search is used in order to determine
+ * the heap segment that contains the cell.
+ */
+int
+scm_cellp (SCM value)
 {
-  SCM w;
-
-  for (w = scm_weak_vectors; w != SCM_EOL; w = SCM_WVECT_GC_CHAIN (w))
-    {
-      if (SCM_IS_WHVEC_ANY (w))
-       {
-         SCM *ptr;
-         SCM obj;
-         int j;
-         int n;
-
-         obj = w;
-         ptr = SCM_VELTS (w);
-         n = SCM_LENGTH (w);
-         for (j = 0; j < n; ++j)
-           {
-             SCM alist;
-
-             alist = ptr[j];
-             while (   SCM_CONSP (alist)
-                    && !SCM_GCMARKP (alist) 
-                    && SCM_CONSP  (SCM_CAR (alist)))
-               {
-                 SCM_SETGCMARK (alist);
-                 SCM_SETGCMARK (SCM_CAR (alist));
-                 alist = SCM_GCCDR (alist);
-               }
-           }
-       }
+  if (SCM_CELLP (value)) {
+    scm_cell * ptr = SCM2PTR (value);
+    unsigned int i = 0;
+    unsigned int j = scm_n_heap_segs - 1;
+
+    while (i < j) {
+      int k = (i + j) / 2;
+      if (SCM_PTR_GT (scm_heap_table[k].bounds[1], ptr)) {
+       j = k;
+      } else if (SCM_PTR_LE (scm_heap_table[k].bounds[0], ptr)) {
+       i = k + 1;
+      }
     }
+
+    if (SCM_PTR_LE (scm_heap_table[i].bounds[0], ptr)
+       && SCM_PTR_GT (scm_heap_table[i].bounds[1], ptr)
+       && (scm_heap_table[i].span == 1 || SCM_DOUBLE_CELLP (value))
+        && !SCM_GC_IN_CARD_HEADERP (ptr)
+        )
+      return 1;
+    else
+      return 0;
+  } else
+    return 0;
 }
 
 
-#ifdef GUILE_NEW_GC_SCHEME
 static void
 gc_sweep_freelist_start (scm_freelist_t *freelist)
 {
   freelist->cells = SCM_EOL;
   freelist->left_to_collect = freelist->cluster_size;
+  freelist->clusters_allocated = 0;
   freelist->clusters = SCM_EOL;
   freelist->clustertail = &freelist->clusters;
+  freelist->collected_1 = freelist->collected;
   freelist->collected = 0;
 }
 
 static void
 gc_sweep_freelist_finish (scm_freelist_t *freelist)
 {
+  int collected;
   *freelist->clustertail = freelist->cells;
-  if (SCM_NNULLP (freelist->cells))
+  if (!SCM_NULLP (freelist->cells))
     {
       SCM c = freelist->cells;
       SCM_SETCAR (c, SCM_CDR (c));
@@ -1423,21 +1481,32 @@ gc_sweep_freelist_finish (scm_freelist_t *freelist)
       freelist->collected +=
        freelist->span * (freelist->cluster_size - freelist->left_to_collect);
     }
-    
-  freelist->grow_heap_p = (freelist->collected < freelist->gc_trigger);
+  scm_gc_cells_collected += freelist->collected;
+
+  /* Although freelist->min_yield is used to test freelist->collected
+   * (which is the local GC yield for freelist), it is adjusted so
+   * that *total* yield is freelist->min_yield_fraction of total heap
+   * size.  This means that a too low yield is compensated by more
+   * heap on the list which is currently doing most work, which is
+   * just what we want.
+   */
+  collected = SCM_MAX (freelist->collected_1, freelist->collected);
+  freelist->grow_heap_p = (collected < freelist->min_yield);
 }
-#endif
 
-void 
+#define NEXT_DATA_CELL(ptr, span) \
+    do { \
+      scm_cell *nxt__ = CELL_UP ((char *) (ptr) + 1, (span)); \
+      (ptr) = (SCM_GC_IN_CARD_HEADERP (nxt__) ? \
+               CELL_UP (SCM_GC_CELL_CARD (nxt__) + SCM_GC_CARD_N_HEADER_CELLS, span) \
+               : nxt__); \
+    } while (0)
+
+void
 scm_gc_sweep ()
+#define FUNC_NAME "scm_gc_sweep"
 {
   register SCM_CELLPTR ptr;
-#ifdef SCM_POINTERS_MUNGED
-  register SCM scmptr;
-#else
-#undef scmptr
-#define scmptr (SCM)ptr
-#endif
   register SCM nfreelist;
   register scm_freelist_t *freelist;
   register long m;
@@ -1447,23 +1516,12 @@ scm_gc_sweep ()
 
   m = 0;
 
-#ifdef GUILE_NEW_GC_SCHEME
   gc_sweep_freelist_start (&scm_master_freelist);
   gc_sweep_freelist_start (&scm_master_freelist2);
-#else
-  /* Reset all free list pointers.  We'll reconstruct them completely
-     while scanning.  */
-  for (i = 0; i < scm_n_heap_segs; i++)
-    scm_heap_table[i].freelist->cells = SCM_EOL;
-#endif
-  
+
   for (i = 0; i < scm_n_heap_segs; i++)
     {
-#ifdef GUILE_NEW_GC_SCHEME
       register unsigned int left_to_collect;
-#else
-      register scm_sizet n = 0;
-#endif
       register scm_sizet j;
 
       /* Unmarked cells go onto the front of the freelist this heap
@@ -1474,148 +1532,127 @@ scm_gc_sweep ()
         simply don't assign nfreelist back into the real freelist.  */
       freelist = scm_heap_table[i].freelist;
       nfreelist = freelist->cells;
-#ifdef GUILE_NEW_GC_SCHEME
       left_to_collect = freelist->left_to_collect;
-#endif
       span = scm_heap_table[i].span;
 
-      ptr = CELL_UP (scm_heap_table[i].bounds[0]);
-      seg_size = CELL_DN (scm_heap_table[i].bounds[1]) - ptr;
+      ptr = CELL_UP (scm_heap_table[i].bounds[0], span);
+      seg_size = CELL_DN (scm_heap_table[i].bounds[1], span) - ptr;
+
+      /* use only data cells in seg_size */
+      seg_size = (seg_size / SCM_GC_CARD_N_CELLS) * (SCM_GC_CARD_N_DATA_CELLS / span) * span;
+
+      scm_gc_cells_swept += seg_size;
+
       for (j = seg_size + span; j -= span; ptr += span)
        {
-#ifdef SCM_POINTERS_MUNGED
-         scmptr = PTR2SCM (ptr);
-#endif
-         switch SCM_TYP7 (scmptr)
+         SCM scmptr;
+
+          if (SCM_GC_IN_CARD_HEADERP (ptr))
            {
+              SCM_CELLPTR nxt;
+
+              /* cheat here */
+              nxt = ptr;
+              NEXT_DATA_CELL (nxt, span);
+              j += span;
+
+              ptr = nxt - span;
+              continue;
+            }
+
+          scmptr = PTR2SCM (ptr);
+
+          if (SCM_GCMARKP (scmptr))
+              continue;
+
+         switch SCM_TYP7 (scmptr)
+            {
            case scm_tcs_cons_gloc:
-             if (SCM_GCMARKP (scmptr))
-               {
-                 if (SCM_CDR (SCM_CAR (scmptr) - 1) == (SCM)1)
-                   SCM_SETCDR (SCM_CAR (scmptr) - 1, (SCM) 0);
-                 goto cmrkcontinue;
-               }
              {
-               SCM vcell;
-               vcell = SCM_CAR (scmptr) - 1L;
-
-               if ((SCM_CDR (vcell) == 0) || (SCM_UNPACK (SCM_CDR (vcell)) == 1))
+               /* Dirk:FIXME:: Again, super ugly code:  scmptr may be a
+                * struct or a gloc.  See the corresponding comment in
+                * scm_gc_mark.
+                */
+               scm_bits_t word0 = (SCM_CELL_WORD_0 (scmptr)
+                                   - scm_tc3_cons_gloc);
+               /* access as struct */
+               scm_bits_t * vtable_data = (scm_bits_t *) word0;
+               if (vtable_data[scm_vtable_index_vcell] == 0)
                  {
-                   scm_struct_free_t free
-                     = (scm_struct_free_t) ((SCM*) vcell)[scm_struct_i_free];
-                   m += free ((SCM *) vcell, (SCM *) SCM_GCCDR (scmptr));
+                   /* Structs need to be freed in a special order.
+                    * This is handled by GC C hooks in struct.c.
+                    */
+                   SCM_SET_STRUCT_GC_CHAIN (scmptr, scm_structs_to_free);
+                   scm_structs_to_free = scmptr;
+                    continue;
                  }
+               /* fall through so that scmptr gets collected */
              }
              break;
            case scm_tcs_cons_imcar:
            case scm_tcs_cons_nimcar:
            case scm_tcs_closures:
            case scm_tc7_pws:
-             if (SCM_GCMARKP (scmptr))
-               goto cmrkcontinue;
              break;
            case scm_tc7_wvect:
-             if (SCM_GC8MARKP (scmptr))
-               {
-                 goto c8mrkcontinue;
-               }
-             else
-               {
-                 m += (2 + SCM_LENGTH (scmptr)) * sizeof (SCM);
-                 scm_must_free ((char *)(SCM_VELTS (scmptr) - 2));
-                 break;
-               }
-
+              m += (2 + SCM_VECTOR_LENGTH (scmptr)) * sizeof (SCM);
+              scm_must_free (SCM_VECTOR_BASE (scmptr) - 2);
+              break;
            case scm_tc7_vector:
-           case scm_tc7_lvector:
+             {
+               unsigned long int length = SCM_VECTOR_LENGTH (scmptr);
+               if (length > 0)
+                 {
+                   m += length * sizeof (scm_bits_t);
+                   scm_must_free (SCM_VECTOR_BASE (scmptr));
+                 }
+               break;
+             }
 #ifdef CCLO
            case scm_tc7_cclo:
-#endif
-             if (SCM_GC8MARKP (scmptr))
-               goto c8mrkcontinue;
-
-             m += (SCM_LENGTH (scmptr) * sizeof (SCM));
-           freechars:
-             scm_must_free (SCM_CHARS (scmptr));
-             /*        SCM_SETCHARS(scmptr, 0);*/
+             m += (SCM_CCLO_LENGTH (scmptr) * sizeof (SCM));
+             scm_must_free (SCM_CCLO_BASE (scmptr));
              break;
+#endif
 #ifdef HAVE_ARRAYS
            case scm_tc7_bvect:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
-             m += sizeof (long) * ((SCM_HUGE_LENGTH (scmptr) + SCM_LONG_BIT - 1) / SCM_LONG_BIT);
-             goto freechars;
+             {
+               unsigned long int length = SCM_BITVECTOR_LENGTH (scmptr);
+               if (length > 0)
+                 {
+                   m += sizeof (long) * ((length + SCM_LONG_BIT - 1) / SCM_LONG_BIT);
+                   scm_must_free (SCM_BITVECTOR_BASE (scmptr));
+                 }
+             }
+             break;
            case scm_tc7_byvect:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
-             m += SCM_HUGE_LENGTH (scmptr) * sizeof (char);
-             goto freechars;
            case scm_tc7_ivect:
            case scm_tc7_uvect:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
-             m += SCM_HUGE_LENGTH (scmptr) * sizeof (long);
-             goto freechars;
            case scm_tc7_svect:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
-             m += SCM_HUGE_LENGTH (scmptr) * sizeof (short);
-             goto freechars;
 #ifdef HAVE_LONG_LONGS
            case scm_tc7_llvect:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
-             m += SCM_HUGE_LENGTH (scmptr) * sizeof (long_long);
-             goto freechars;
 #endif
            case scm_tc7_fvect:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
-             m += SCM_HUGE_LENGTH (scmptr) * sizeof (float);
-             goto freechars;
            case scm_tc7_dvect:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
-             m += SCM_HUGE_LENGTH (scmptr) * sizeof (double);
-             goto freechars;
            case scm_tc7_cvect:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
-             m += SCM_HUGE_LENGTH (scmptr) * 2 * sizeof (double);
-             goto freechars;
+             m += SCM_UVECTOR_LENGTH (scmptr) * scm_uniform_element_size (scmptr);
+             scm_must_free (SCM_UVECTOR_BASE (scmptr));
+             break;
 #endif
            case scm_tc7_substring:
-             if (SCM_GC8MARKP (scmptr))
-               goto c8mrkcontinue;
              break;
            case scm_tc7_string:
-             if (SCM_GC8MARKP (scmptr))
-               goto c8mrkcontinue;
-             m += SCM_HUGE_LENGTH (scmptr) + 1;
-             goto freechars;
-           case scm_tc7_msymbol:
-             if (SCM_GC8MARKP (scmptr))
-               goto c8mrkcontinue;
-             m += (  SCM_LENGTH (scmptr)
-                   + 1
-                   + sizeof (SCM) * ((SCM *)SCM_CHARS (scmptr) - SCM_SLOTS(scmptr)));
-             scm_must_free ((char *)SCM_SLOTS (scmptr));
+             m += SCM_STRING_LENGTH (scmptr) + 1;
+             scm_must_free (SCM_STRING_CHARS (scmptr));
              break;
-           case scm_tc7_contin:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
-             m += SCM_LENGTH (scmptr) * sizeof (SCM_STACKITEM) + sizeof (scm_contregs);
-             if (SCM_VELTS (scmptr))
-               goto freechars;
-           case scm_tc7_ssymbol:
-             if SCM_GC8MARKP(scmptr)
-               goto c8mrkcontinue;
+           case scm_tc7_symbol:
+             m += SCM_SYMBOL_LENGTH (scmptr) + 1;
+             scm_must_free (SCM_SYMBOL_CHARS (scmptr));
              break;
            case scm_tcs_subrs:
+              /* the various "subrs" (primitives) are never freed */
              continue;
            case scm_tc7_port:
-             if SCM_GC8MARKP (scmptr)
-               goto c8mrkcontinue;
              if SCM_OPENP (scmptr)
                {
                  int k = SCM_PTOBNUM (scmptr);
@@ -1635,78 +1672,59 @@ scm_gc_sweep ()
                }
              break;
            case scm_tc7_smob:
-             switch SCM_GCTYP16 (scmptr)
+             switch SCM_TYP16 (scmptr)
                {
                case scm_tc_free_cell:
                case scm_tc16_real:
-                 if SCM_GC8MARKP (scmptr)
-                   goto c8mrkcontinue;
                  break;
 #ifdef SCM_BIGDIG
                case scm_tc16_big:
-                 if SCM_GC8MARKP (scmptr)
-                   goto c8mrkcontinue;
                  m += (SCM_NUMDIGS (scmptr) * SCM_BITSPERDIG / SCM_CHAR_BIT);
-                 goto freechars;
+                 scm_must_free (SCM_BDIGITS (scmptr));
+                 break;
 #endif /* def SCM_BIGDIG */
                case scm_tc16_complex:
-                 if SCM_GC8MARKP (scmptr)
-                   goto c8mrkcontinue;
-                 m += 2 * sizeof (double);
-                 goto freechars;
+                 m += sizeof (scm_complex_t);
+                 scm_must_free (SCM_COMPLEX_MEM (scmptr));
+                 break;
                default:
-                 if SCM_GC8MARKP (scmptr)
-                   goto c8mrkcontinue;
-
                  {
                    int k;
                    k = SCM_SMOBNUM (scmptr);
                    if (!(k < scm_numsmob))
                      goto sweeperr;
-                   m += (scm_smobs[k].free) ((SCM) scmptr);
+                   m += (scm_smobs[k].free) (scmptr);
                    break;
                  }
                }
              break;
            default:
-           sweeperr:scm_wta (scmptr, "unknown type in ", "gc_sweep");
+           sweeperr:
+             SCM_MISC_ERROR ("unknown type", SCM_EOL);
            }
-#if 0
-         if (SCM_CAR (scmptr) == (SCM) scm_tc_free_cell)
-           exit (2);
-#endif
-#ifndef GUILE_NEW_GC_SCHEME
-         n += span;
-#else
+
          if (!--left_to_collect)
            {
              SCM_SETCAR (scmptr, nfreelist);
              *freelist->clustertail = scmptr;
              freelist->clustertail = SCM_CDRLOC (scmptr);
-                 
+
              nfreelist = SCM_EOL;
              freelist->collected += span * freelist->cluster_size;
              left_to_collect = freelist->cluster_size;
            }
          else
-#endif
            {
              /* Stick the new cell on the front of nfreelist.  It's
                 critical that we mark this cell as freed; otherwise, the
                 conservative collector might trace it as some other type
                 of object.  */
-             SCM_SETCAR (scmptr, scm_tc_free_cell);
-             SCM_SETCDR (scmptr, nfreelist);
+             SCM_SET_CELL_TYPE (scmptr, scm_tc_free_cell);
+             SCM_SET_FREE_CELL_CDR (scmptr, nfreelist);
              nfreelist = scmptr;
            }
-         
-         continue;
-       c8mrkcontinue:
-         SCM_CLRGC8MARK (scmptr);
-         continue;
-       cmrkcontinue:
-         SCM_CLRGCMARK (scmptr);
        }
+
 #ifdef GC_FREE_SEGMENTS
       if (n == seg_size)
        {
@@ -1726,116 +1744,46 @@ scm_gc_sweep ()
          /* Update the real freelist pointer to point to the head of
             the list of free cells we've built for this segment.  */
          freelist->cells = nfreelist;
-#ifdef GUILE_NEW_GC_SCHEME
          freelist->left_to_collect = left_to_collect;
-#endif
        }
 
-#ifndef GUILE_NEW_GC_SCHEME
-      freelist->collected += n;
-      scm_cells_allocated += freelist->heap_size - freelist->collected;
-#endif
-
 #ifdef GUILE_DEBUG_FREELIST
-#ifdef GUILE_NEW_GC_SCHEME
-      scm_check_freelist (freelist == &scm_master_freelist
-                         ? scm_freelist
-                         : scm_freelist2);
-#else
-      scm_check_freelist (freelist);
-#endif
       scm_map_free_list ();
 #endif
     }
-  
-#ifdef GUILE_NEW_GC_SCHEME
+
   gc_sweep_freelist_finish (&scm_master_freelist);
   gc_sweep_freelist_finish (&scm_master_freelist2);
-  
+
   /* When we move to POSIX threads private freelists should probably
      be GC-protected instead. */
   scm_freelist = SCM_EOL;
   scm_freelist2 = SCM_EOL;
-#endif
-  
-  /* Scan weak vectors. */
-  {
-    SCM *ptr, w;
-    for (w = scm_weak_vectors; w != SCM_EOL; w = SCM_WVECT_GC_CHAIN (w))
-      {
-       if (!SCM_IS_WHVEC_ANY (w))
-         {
-           register long j, n;
-
-           ptr = SCM_VELTS (w);
-           n = SCM_LENGTH (w);
-           for (j = 0; j < n; ++j)
-             if (SCM_FREEP (ptr[j]))
-               ptr[j] = SCM_BOOL_F;
-         }
-       else /* if (SCM_IS_WHVEC_ANY (scm_weak_vectors[i])) */
-         {
-           SCM obj = w;
-           register long n = SCM_LENGTH (w);
-           register long j;
 
-           ptr = SCM_VELTS (w);
-
-           for (j = 0; j < n; ++j)
-             {
-               SCM * fixup;
-               SCM alist;
-               int weak_keys;
-               int weak_values;
-               
-               weak_keys = SCM_IS_WHVEC (obj) || SCM_IS_WHVEC_B (obj);
-               weak_values = SCM_IS_WHVEC_V (obj) || SCM_IS_WHVEC_B (obj);
-
-               fixup = ptr + j;
-               alist = *fixup;
-
-               while (   SCM_CONSP (alist)
-                      && SCM_CONSP (SCM_CAR (alist)))
-                 {
-                   SCM key;
-                   SCM value;
-
-                   key = SCM_CAAR (alist);
-                   value = SCM_CDAR (alist);
-                   if (   (weak_keys && SCM_FREEP (key))
-                       || (weak_values && SCM_FREEP (value)))
-                     {
-                       *fixup = SCM_CDR (alist);
-                     }
-                   else
-                     fixup = SCM_CDRLOC (alist);
-                   alist = SCM_CDR (alist);
-                 }
-             }
-         }
-      }
-  }
+  scm_cells_allocated = (SCM_HEAP_SIZE - scm_gc_cells_collected);
+  scm_gc_yield -= scm_cells_allocated;
   scm_mallocated -= m;
   scm_gc_malloc_collected = m;
 }
+#undef FUNC_NAME
 
 
 \f
-
 /* {Front end to malloc}
  *
- * scm_must_malloc, scm_must_realloc, scm_must_free, scm_done_malloc
+ * scm_must_malloc, scm_must_realloc, scm_must_free, scm_done_malloc,
+ * scm_done_free
  *
  * These functions provide services comperable to malloc, realloc, and
  * free.  They are for allocating malloced parts of scheme objects.
- * The primary purpose of the front end is to impose calls to gc.
- */
+ * The primary purpose of the front end is to impose calls to gc.  */
+
 
 /* scm_must_malloc
  * Return newly malloced storage or throw an error.
  *
  * The parameter WHAT is a string for error reporting.
- * If the threshold scm_mtrigger will be passed by this 
+ * If the threshold scm_mtrigger will be passed by this
  * allocation, or if the first call to malloc fails,
  * garbage collect -- on the presumption that some objects
  * using malloced storage may be collected.
@@ -1854,6 +1802,9 @@ scm_must_malloc (scm_sizet size, const char *what)
       if (NULL != ptr)
        {
          scm_mallocated = nm;
+#ifdef GUILE_DEBUG_MALLOC
+         scm_malloc_register (ptr, what);
+#endif
          return ptr;
        }
     }
@@ -1871,11 +1822,14 @@ scm_must_malloc (scm_sizet size, const char *what)
        else
          scm_mtrigger += scm_mtrigger / 2;
       }
+#ifdef GUILE_DEBUG_MALLOC
+      scm_malloc_register (ptr, what);
+#endif
+
       return ptr;
     }
 
-  scm_wta (SCM_MAKINUM (size), (char *) SCM_NALLOC, what);
-  return 0; /* never reached */
+  scm_memory_error (what);
 }
 
 
@@ -1897,6 +1851,9 @@ scm_must_realloc (void *where,
       if (NULL != ptr)
        {
          scm_mallocated = nm;
+#ifdef GUILE_DEBUG_MALLOC
+         scm_malloc_reregister (where, ptr, what);
+#endif
          return ptr;
        }
     }
@@ -1914,21 +1871,30 @@ scm_must_realloc (void *where,
        else
          scm_mtrigger += scm_mtrigger / 2;
       }
+#ifdef GUILE_DEBUG_MALLOC
+      scm_malloc_reregister (where, ptr, what);
+#endif
       return ptr;
     }
 
-  scm_wta (SCM_MAKINUM (size), (char *) SCM_NALLOC, what);
-  return 0; /* never reached */
+  scm_memory_error (what);
 }
 
-void 
+
+void
 scm_must_free (void *obj)
+#define FUNC_NAME "scm_must_free"
 {
+#ifdef GUILE_DEBUG_MALLOC
+  scm_malloc_unregister (obj);
+#endif
   if (obj)
     free (obj);
   else
-    scm_wta (SCM_INUM0, "already free", "");
+    SCM_MISC_ERROR ("freeing NULL pointer", SCM_EOL);
 }
+#undef FUNC_NAME
+
 
 /* Announce that there has been some malloc done that will be freed
  * during gc.  A typical use is for a smob that uses some malloced
@@ -1936,7 +1902,13 @@ scm_must_free (void *obj)
  * reason).  When a new object of this smob is created you call
  * scm_done_malloc with the size of the object.  When your smob free
  * function is called, be sure to include this size in the return
- * value. */
+ * value.
+ *
+ * If you can't actually free the memory in the smob free function,
+ * for whatever reason (like reference counting), you still can (and
+ * should) report the amount of memory freed when you actually free it.
+ * Do it by calling scm_done_malloc with the _negated_ size.  Clever,
+ * eh?  Or even better, call scm_done_free. */
 
 void
 scm_done_malloc (long size)
@@ -1956,23 +1928,14 @@ scm_done_malloc (long size)
     }
 }
 
-
-#ifdef GUILE_NEW_GC_SCHEME
-static void
-adjust_gc_trigger (scm_freelist_t *freelist)
+void
+scm_done_free (long size)
 {
-  /* Adjust GC trigger based on total heap size */
-  if (freelist->gc_trigger_fraction)
-    freelist->gc_trigger = ((scm_master_freelist.heap_size
-                            + scm_master_freelist2.heap_size)
-                           * freelist->gc_trigger_fraction
-                           / 100);
+  scm_mallocated -= size;
 }
-#endif
 
 
 \f
-
 /* {Heap Segments}
  *
  * Each heap segment is an array of objects of a particular size.
@@ -1996,47 +1959,49 @@ scm_sizet scm_max_segment_size;
  */
 SCM_CELLPTR scm_heap_org;
 
-struct scm_heap_seg_data * scm_heap_table = 0;
+scm_heap_seg_data_t * scm_heap_table = 0;
+static unsigned int heap_segment_table_size = 0;
 int scm_n_heap_segs = 0;
 
 /* init_heap_seg
- * initializes a new heap segment and return the number of objects it contains.
+ * initializes a new heap segment and returns the number of objects it contains.
  *
- * The segment origin, segment size in bytes, and the span of objects
- * in cells are input parameters.  The freelist is both input and output.
+ * The segment origin and segment size in bytes are input parameters.
+ * The freelist is both input and output.
  *
- * This function presume that the scm_heap_table has already been expanded
- * to accomodate a new segment record.
+ * This function presumes that the scm_heap_table has already been expanded
+ * to accomodate a new segment record and that the markbit space was reserved
+ * for all the cards in this segment.
  */
 
+#define INIT_CARD(card, span) \
+    do { \
+      SCM_GC_CARD_BVEC (card) = get_bvec (); \
+      if ((span) == 2) \
+        SCM_GC_SET_CARD_DOUBLECELL (card); \
+    } while (0)
 
-static scm_sizet 
+static scm_sizet
 init_heap_seg (SCM_CELLPTR seg_org, scm_sizet size, scm_freelist_t *freelist)
 {
   register SCM_CELLPTR ptr;
-#ifdef SCM_POINTERS_MUNGED
-  register SCM scmptr;
-#else
-#undef scmptr
-#define scmptr ptr
-#endif
   SCM_CELLPTR seg_end;
   int new_seg_index;
   int n_new_cells;
   int span = freelist->span;
-  
+
   if (seg_org == NULL)
     return 0;
 
-  ptr = seg_org;
-
-  size = (size / sizeof (scm_cell) / span) * span * sizeof (scm_cell);
+  /* Align the begin ptr up.
+   */
+  ptr = SCM_GC_CARD_UP (seg_org);
 
-  /* Compute the ceiling on valid object pointers w/in this segment. 
+  /* Compute the ceiling on valid object pointers w/in this segment.
    */
-  seg_end = CELL_DN ((char *) ptr + size);
+  seg_end = SCM_GC_CARD_DOWN ((char *)seg_org + size);
 
-  /* Find the right place and insert the segment record. 
+  /* Find the right place and insert the segment record.
    *
    */
   for (new_seg_index = 0;
@@ -2050,70 +2015,73 @@ init_heap_seg (SCM_CELLPTR seg_org, scm_sizet size, scm_freelist_t *freelist)
     for (i = scm_n_heap_segs; i > new_seg_index; --i)
       scm_heap_table[i] = scm_heap_table[i - 1];
   }
-  
+
   ++scm_n_heap_segs;
 
-  scm_heap_table[new_seg_index].valid = 0;
   scm_heap_table[new_seg_index].span = span;
   scm_heap_table[new_seg_index].freelist = freelist;
-  scm_heap_table[new_seg_index].bounds[0] = (SCM_CELLPTR)ptr;
-  scm_heap_table[new_seg_index].bounds[1] = (SCM_CELLPTR)seg_end;
-
-
-  /* Compute the least valid object pointer w/in this segment 
-   */
-  ptr = CELL_UP (ptr);
-
+  scm_heap_table[new_seg_index].bounds[0] = ptr;
+  scm_heap_table[new_seg_index].bounds[1] = seg_end;
 
   /*n_new_cells*/
   n_new_cells = seg_end - ptr;
 
-#ifdef GUILE_NEW_GC_SCHEME
-
   freelist->heap_size += n_new_cells;
 
-  /* Partition objects in this segment into clusters
-   */
+  /* Partition objects in this segment into clusters */
   {
     SCM clusters;
     SCM *clusterp = &clusters;
-    int n_cluster_cells = span * freelist->cluster_size;
 
-    while (n_new_cells > span) /* at least one spine + one freecell */
+    NEXT_DATA_CELL (ptr, span);
+    while (ptr < seg_end)
       {
-       /* Determine end of cluster
-        */
-       if (n_new_cells >= n_cluster_cells)
-         {
-           seg_end = ptr + n_cluster_cells;
-           n_new_cells -= n_cluster_cells;
-         }
-       else
-         {
-           seg_end = ptr + n_new_cells;
-           n_new_cells = 0;
-         }
+        scm_cell *nxt = ptr;
+        scm_cell *prv = NULL;
+        scm_cell *last_card = NULL;
+        int n_data_cells = (SCM_GC_CARD_N_DATA_CELLS / span) * SCM_CARDS_PER_CLUSTER - 1;
+        NEXT_DATA_CELL(nxt, span);
 
        /* Allocate cluster spine
         */
        *clusterp = PTR2SCM (ptr);
-       SCM_SETCAR (*clusterp, PTR2SCM (ptr + span));
+        SCM_SETCAR (*clusterp, PTR2SCM (nxt));
        clusterp = SCM_CDRLOC (*clusterp);
-       ptr += span;
-       
-       while (ptr < seg_end)
+        ptr = nxt;
+
+        while (n_data_cells--)
          {
-#ifdef SCM_POINTERS_MUNGED
-           scmptr = PTR2SCM (ptr);
-#endif
-           SCM_SETCAR (scmptr, scm_tc_free_cell);
-           SCM_SETCDR (scmptr, PTR2SCM (ptr + span));
-           ptr += span;
+            scm_cell *card = SCM_GC_CELL_CARD (ptr);
+           SCM scmptr = PTR2SCM (ptr);
+            nxt = ptr;
+            NEXT_DATA_CELL (nxt, span);
+            prv = ptr;
+
+            if (card != last_card)
+              {
+                INIT_CARD (card, span);
+                last_card = card;
+              }
+
+           SCM_SET_CELL_TYPE (scmptr, scm_tc_free_cell);
+           SCM_SETCDR (scmptr, PTR2SCM (nxt));
+
+            ptr = nxt;
          }
 
-       SCM_SETCDR (PTR2SCM (ptr - span), SCM_EOL);
+       SCM_SET_FREE_CELL_CDR (PTR2SCM (prv), SCM_EOL);
       }
-    
+
+    /* sanity check */
+    {
+      scm_cell *ref = seg_end;
+      NEXT_DATA_CELL (ref, span);
+      if (ref != ptr)
+        /* [cmm] looks like the segment size doesn't divide cleanly by
+           cluster size.  bad cmm! */
+        abort();
+    }
+
     /* Patch up the last cluster pointer in the segment
      * to join it to the input freelist.
      */
@@ -2121,131 +2089,137 @@ init_heap_seg (SCM_CELLPTR seg_org, scm_sizet size, scm_freelist_t *freelist)
     freelist->clusters = clusters;
   }
 
-  adjust_gc_trigger (&scm_master_freelist);
-  adjust_gc_trigger (&scm_master_freelist2);
-
-#else /* GUILE_NEW_GC_SCHEME */
-
-  /* Prepend objects in this segment to the freelist. 
-   */
-  while (ptr < seg_end)
-    {
-#ifdef SCM_POINTERS_MUNGED
-      scmptr = PTR2SCM (ptr);
-#endif
-      SCM_SETCAR (scmptr, (SCM) scm_tc_free_cell);
-      SCM_SETCDR (scmptr, PTR2SCM (ptr + span));
-      ptr += span;
-    }
-
-  ptr -= span;
-
-  /* Patch up the last freelist pointer in the segment
-   * to join it to the input freelist.
-   */
-  SCM_SETCDR (PTR2SCM (ptr), freelist->cells);
-  freelist->cells = PTR2SCM (CELL_UP (seg_org));
-
-  freelist->heap_size += n_new_cells;
-
-#endif /* GUILE_NEW_GC_SCHEME */
-
 #ifdef DEBUGINFO
   fprintf (stderr, "H");
 #endif
   return size;
-#ifdef scmptr
-#undef scmptr
-#endif
 }
 
+static scm_sizet
+round_to_cluster_size (scm_freelist_t *freelist, scm_sizet len)
+{
+  scm_sizet cluster_size_in_bytes = CLUSTER_SIZE_IN_BYTES (freelist);
+
+  return
+    (len + cluster_size_in_bytes - 1) / cluster_size_in_bytes * cluster_size_in_bytes
+    + ALIGNMENT_SLACK (freelist);
+}
 
-static void 
-alloc_some_heap (scm_freelist_t *freelist)
+static void
+alloc_some_heap (scm_freelist_t *freelist, policy_on_error error_policy)
+#define FUNC_NAME "alloc_some_heap"
 {
-  struct scm_heap_seg_data * tmptable;
   SCM_CELLPTR ptr;
-  scm_sizet len;
-  
-  /* Critical code sections (such as the garbage collector)
-   * aren't supposed to add heap segments.
-   */
-  if (scm_gc_heap_lock)
-    scm_wta (SCM_UNDEFINED, "need larger initial", "heap");
-
-  /* Expand the heap tables to have room for the new segment.
-   * Do not yet increment scm_n_heap_segs -- that is done by init_heap_seg
-   * only if the allocation of the segment itself succeeds.
-   */
-  len = (1 + scm_n_heap_segs) * sizeof (struct scm_heap_seg_data);
+  long len;
 
-  SCM_SYSCALL (tmptable = ((struct scm_heap_seg_data *)
-                      realloc ((char *)scm_heap_table, len)));
-  if (!tmptable)
-    scm_wta (SCM_UNDEFINED, "could not grow", "hplims");
-  else
-    scm_heap_table = tmptable;
+  if (scm_gc_heap_lock)
+    {
+      /* Critical code sections (such as the garbage collector) aren't
+       * supposed to add heap segments.
+       */
+      fprintf (stderr, "alloc_some_heap: Can not extend locked heap.\n");
+      abort ();
+    }
 
+  if (scm_n_heap_segs == heap_segment_table_size)
+    {
+      /* We have to expand the heap segment table to have room for the new
+       * segment.  Do not yet increment scm_n_heap_segs -- that is done by
+       * init_heap_seg only if the allocation of the segment itself succeeds.
+       */
+      unsigned int new_table_size = scm_n_heap_segs + 1;
+      size_t size = new_table_size * sizeof (scm_heap_seg_data_t);
+      scm_heap_seg_data_t * new_heap_table;
+
+      SCM_SYSCALL (new_heap_table = ((scm_heap_seg_data_t *)
+                                    realloc ((char *)scm_heap_table, size)));
+      if (!new_heap_table)
+       {
+         if (error_policy == abort_on_error)
+           {
+             fprintf (stderr, "alloc_some_heap: Could not grow heap segment table.\n");
+             abort ();
+           }
+         else
+           {
+             return;
+           }
+       }
+      else
+       {
+         scm_heap_table = new_heap_table;
+         heap_segment_table_size = new_table_size;
+       }
+    }
 
   /* Pick a size for the new heap segment.
-   * The rule for picking the size of a segment is explained in 
+   * The rule for picking the size of a segment is explained in
    * gc.h
    */
-#ifdef GUILE_NEW_GC_SCHEME
   {
-    /* Assure that the new segment is large enough for the new trigger */
-    int slack = freelist->gc_trigger - freelist->collected;
-    int min_cells = 100 * slack / (99 - freelist->gc_trigger_fraction);
+    /* Assure that the new segment is predicted to be large enough.
+     *
+     * New yield should at least equal GC fraction of new heap size, i.e.
+     *
+     *   y + dh > f * (h + dh)
+     *
+     *    y : yield
+     *    f : min yield fraction
+     *    h : heap size
+     *   dh : size of new heap segment
+     *
+     * This gives dh > (f * h - y) / (1 - f)
+     */
+    int f = freelist->min_yield_fraction;
+    long h = SCM_HEAP_SIZE;
+    long min_cells = (f * h - 100 * (long) scm_gc_yield) / (99 - f);
     len =  SCM_EXPHEAP (freelist->heap_size);
 #ifdef DEBUGINFO
     fprintf (stderr, "(%d < %d)", len, min_cells);
 #endif
     if (len < min_cells)
-      len = min_cells + 1;
+      len = min_cells + freelist->cluster_size;
     len *= sizeof (scm_cell);
+    /* force new sampling */
+    freelist->collected = LONG_MAX;
   }
-    
+
   if (len > scm_max_segment_size)
     len = scm_max_segment_size;
-#else
-  if (scm_expmem)
-    {
-      len = (scm_sizet) SCM_EXPHEAP (freelist->heap_size * sizeof (scm_cell));
-      if ((scm_sizet) SCM_EXPHEAP (freelist->heap_size * sizeof (scm_cell))
-         != len)
-       len = 0;
-    }
-  else
-    len = SCM_HEAP_SEG_SIZE;
-#endif /* GUILE_NEW_GC_SCHEME */
 
   {
     scm_sizet smallest;
 
-    smallest = (freelist->span * sizeof (scm_cell));
+    smallest = CLUSTER_SIZE_IN_BYTES (freelist);
+
     if (len < smallest)
-      len = (freelist->span * sizeof (scm_cell));
+      len = smallest;
 
     /* Allocate with decaying ambition. */
     while ((len >= SCM_MIN_HEAP_SEG_SIZE)
           && (len >= smallest))
       {
-       SCM_SYSCALL (ptr = (SCM_CELLPTR) malloc (len));
+        scm_sizet rounded_len = round_to_cluster_size (freelist, len);
+       SCM_SYSCALL (ptr = (SCM_CELLPTR) malloc (rounded_len));
        if (ptr)
          {
-           init_heap_seg (ptr, len, freelist);
+           init_heap_seg (ptr, rounded_len, freelist);
            return;
          }
        len /= 2;
       }
   }
 
-  scm_wta (SCM_UNDEFINED, "could not grow", "heap");
+  if (error_policy == abort_on_error)
+    {
+      fprintf (stderr, "alloc_some_heap: Could not grow heap.\n");
+      abort ();
+    }
 }
+#undef FUNC_NAME
 
 
-
-SCM_DEFINE (scm_unhash_name, "unhash-name", 1, 0, 0, 
+SCM_DEFINE (scm_unhash_name, "unhash-name", 1, 0, 0,
             (SCM name),
            "")
 #define FUNC_NAME s_scm_unhash_name
@@ -2259,20 +2233,23 @@ SCM_DEFINE (scm_unhash_name, "unhash-name", 1, 0, 0,
     {
       SCM_CELLPTR p;
       SCM_CELLPTR pbound;
-      p  = (SCM_CELLPTR)scm_heap_table[x].bounds[0];
-      pbound = (SCM_CELLPTR)scm_heap_table[x].bounds[1];
+      p  = scm_heap_table[x].bounds[0];
+      pbound = scm_heap_table[x].bounds[1];
       while (p < pbound)
        {
-         SCM incar;
-         incar = p->car;
-         if (1 == (7 & (int)incar))
+         SCM cell = PTR2SCM (p);
+         if (SCM_TYP3 (cell) == scm_tc3_cons_gloc)
            {
-             --incar;
-             if (   ((name == SCM_BOOL_T) || (SCM_CAR (incar) == name))
-                 && (SCM_CDR (incar) != 0)
-                 && (SCM_UNPACK (SCM_CDR (incar)) != 1))
+             /* Dirk:FIXME:: Again, super ugly code:  cell may be a gloc or a
+              * struct cell.  See the corresponding comment in scm_gc_mark.
+              */
+             scm_bits_t word0 = SCM_CELL_WORD_0 (cell) - scm_tc3_cons_gloc;
+             SCM gloc_car = SCM_PACK (word0); /* access as gloc */
+             SCM vcell = SCM_CELL_OBJECT_1 (gloc_car);
+             if ((SCM_EQ_P (name, SCM_BOOL_T) || SCM_EQ_P (SCM_CAR (gloc_car), name))
+                 && (SCM_UNPACK (vcell) != 0) && (SCM_UNPACK (vcell) != 1))
                {
-                 p->car = name;
+                 SCM_SET_CELL_OBJECT_0 (cell, name);
                }
            }
          ++p;
@@ -2291,7 +2268,9 @@ SCM_DEFINE (scm_unhash_name, "unhash-name", 1, 0, 0,
 
 void
 scm_remember (SCM *ptr)
-{ /* empty */ }
+{
+  /* empty */ 
+}
 
 
 /*
@@ -2325,26 +2304,32 @@ scm_permanent_object (SCM obj)
 }
 
 
-/* Protect OBJ from the garbage collector.  OBJ will not be freed,
-   even if all other references are dropped, until someone applies
-   scm_unprotect_object to it.  This function returns OBJ.
+/* Protect OBJ from the garbage collector.  OBJ will not be freed, even if all
+   other references are dropped, until the object is unprotected by calling
+   scm_unprotect_object (OBJ).  Calls to scm_protect/unprotect_object nest,
+   i. e. it is possible to protect the same object several times, but it is
+   necessary to unprotect the object the same number of times to actually get
+   the object unprotected.  It is an error to unprotect an object more often
+   than it has been protected before.  The function scm_protect_object returns
+   OBJ.
+*/
 
-   Calls to scm_protect_object nest.  For every object OBJ, there is a
-   counter which scm_protect_object(OBJ) increments and
-   scm_unprotect_object(OBJ) decrements, if it is greater than zero.  If
-   an object's counter is greater than zero, the garbage collector
-   will not free it.
+/* Implementation note:  For every object X, there is a counter which
+   scm_protect_object(X) increments and scm_unprotect_object(X) decrements.
+*/
 
-   Of course, that's not how it's implemented.  scm_protect_object and
-   scm_unprotect_object just maintain a list of references to things.
-   Since the GC knows about this list, all objects it mentions stay
-   alive.  scm_protect_object adds its argument to the list;
-   scm_unprotect_object removes the first occurrence of its argument
-   to the list.  */
 SCM
 scm_protect_object (SCM obj)
 {
-  scm_protects = scm_cons (obj, scm_protects);
+  SCM handle;
+
+  /* This critical section barrier will be replaced by a mutex. */
+  SCM_REDEFER_INTS;
+
+  handle = scm_hashq_create_handle_x (scm_protects, obj, SCM_MAKINUM (0));
+  SCM_SETCDR (handle, SCM_MAKINUM (SCM_INUM (SCM_CDR (handle)) + 1));
+
+  SCM_REALLOW_INTS;
 
   return obj;
 }
@@ -2357,16 +2342,28 @@ scm_protect_object (SCM obj)
 SCM
 scm_unprotect_object (SCM obj)
 {
-  SCM *tail_ptr = &scm_protects;
+  SCM handle;
 
-  while (SCM_CONSP (*tail_ptr))
-    if (SCM_CAR (*tail_ptr) == obj)
-      {
-       *tail_ptr = SCM_CDR (*tail_ptr);
-       break;
-      }
-    else
-      tail_ptr = SCM_CDRLOC (*tail_ptr);
+  /* This critical section barrier will be replaced by a mutex. */
+  SCM_REDEFER_INTS;
+
+  handle = scm_hashq_get_handle (scm_protects, obj);
+
+  if (SCM_IMP (handle))
+    {
+      fprintf (stderr, "scm_unprotect_object called on unprotected object\n");
+      abort ();
+    }
+  else
+    {
+      unsigned long int count = SCM_INUM (SCM_CDR (handle)) - 1;
+      if (count == 0)
+       scm_hashq_remove_x (scm_protects, obj);
+      else
+       SCM_SETCDR (handle, SCM_MAKINUM (count));
+    }
+
+  SCM_REALLOW_INTS;
 
   return obj;
 }
@@ -2396,111 +2393,110 @@ cleanup (int status, void *arg)
 static int
 make_initial_segment (scm_sizet init_heap_size, scm_freelist_t *freelist)
 {
-  if (!init_heap_seg ((SCM_CELLPTR) malloc (init_heap_size),
-                     init_heap_size,
+  scm_sizet rounded_size = round_to_cluster_size (freelist, init_heap_size);
+
+  if (!init_heap_seg ((SCM_CELLPTR) malloc (rounded_size),
+                     rounded_size,
                      freelist))
     {
-      init_heap_size = SCM_HEAP_SEG_SIZE;
-      if (!init_heap_seg ((SCM_CELLPTR) malloc (init_heap_size),
-                         init_heap_size,
+      rounded_size = round_to_cluster_size (freelist, SCM_HEAP_SEG_SIZE);
+      if (!init_heap_seg ((SCM_CELLPTR) malloc (rounded_size),
+                         rounded_size,
                          freelist))
        return 1;
     }
   else
     scm_expmem = 1;
 
-  freelist->grow_heap_p = (freelist->heap_size < freelist->gc_trigger);
-    
+  if (freelist->min_yield_fraction)
+    freelist->min_yield = (freelist->heap_size * freelist->min_yield_fraction
+                           / 100);
+  freelist->grow_heap_p = (freelist->heap_size < freelist->min_yield);
+
   return 0;
 }
 
 \f
-#ifdef GUILE_NEW_GC_SCHEME
 static void
 init_freelist (scm_freelist_t *freelist,
               int span,
               int cluster_size,
-              int gc_trigger)
+              int min_yield)
 {
   freelist->clusters = SCM_EOL;
   freelist->cluster_size = cluster_size + 1;
-  if (gc_trigger < 0)
-    freelist->gc_trigger_fraction = - gc_trigger;
-  else
-    {
-      freelist->gc_trigger = gc_trigger;
-      freelist->gc_trigger_fraction = 0;
-    }
+  freelist->left_to_collect = 0;
+  freelist->clusters_allocated = 0;
+  freelist->min_yield = 0;
+  freelist->min_yield_fraction = min_yield;
   freelist->span = span;
   freelist->collected = 0;
+  freelist->collected_1 = 0;
   freelist->heap_size = 0;
 }
 
+
+/* Get an integer from an environment variable.  */
+static int
+scm_i_getenv_int (const char *var, int def)
+{
+  char *end, *val = getenv (var);
+  long res;
+  if (!val)
+    return def;
+  res = strtol (val, &end, 10);
+  if (end == val)
+    return def;
+  return res;
+}
+
+
 int
-scm_init_storage (scm_sizet init_heap_size_1, int gc_trigger_1,
-                 scm_sizet init_heap_size_2, int gc_trigger_2,
-                 scm_sizet max_segment_size)
-#else
-int
-scm_init_storage (scm_sizet init_heap_size, scm_sizet init_heap2_size)
-#endif
+scm_init_storage ()
 {
+  scm_sizet gc_trigger_1;
+  scm_sizet gc_trigger_2;
+  scm_sizet init_heap_size_1;
+  scm_sizet init_heap_size_2;
   scm_sizet j;
 
-  if (!init_heap_size_1)
-    init_heap_size_1 = SCM_INIT_HEAP_SIZE_1;
-  if (!init_heap_size_2)
-    init_heap_size_2 = SCM_INIT_HEAP_SIZE_2;
-
   j = SCM_NUM_PROTECTS;
   while (j)
     scm_sys_protects[--j] = SCM_BOOL_F;
   scm_block_gc = 1;
 
-#ifdef GUILE_NEW_GC_SCHEME
   scm_freelist = SCM_EOL;
   scm_freelist2 = SCM_EOL;
-  init_freelist (&scm_master_freelist,
-                1, SCM_CLUSTER_SIZE_1,
-                gc_trigger_1 ? gc_trigger_1 : SCM_GC_TRIGGER_1);
-  init_freelist (&scm_master_freelist2,
-                2, SCM_CLUSTER_SIZE_2,
-                gc_trigger_2 ? gc_trigger_2 : SCM_GC_TRIGGER_2);
-  scm_max_segment_size
-    = max_segment_size ? max_segment_size : SCM_MAX_SEGMENT_SIZE;
-#else
-  scm_freelist.cells = SCM_EOL;
-  scm_freelist.span = 1;
-  scm_freelist.collected = 0;
-  scm_freelist.heap_size = 0;
-
-  scm_freelist2.cells = SCM_EOL;
-  scm_freelist2.span = 2;
-  scm_freelist2.collected = 0;
-  scm_freelist2.heap_size = 0;
-#endif
+  gc_trigger_1 = scm_i_getenv_int ("GUILE_MIN_YIELD_1", scm_default_min_yield_1);
+  init_freelist (&scm_master_freelist, 1, SCM_CLUSTER_SIZE_1, gc_trigger_1);
+  gc_trigger_2 = scm_i_getenv_int ("GUILE_MIN_YIELD_2", scm_default_min_yield_2);
+  init_freelist (&scm_master_freelist2, 2, SCM_CLUSTER_SIZE_2, gc_trigger_2);
+  scm_max_segment_size = scm_i_getenv_int ("GUILE_MAX_SEGMENT_SIZE", scm_default_max_segment_size);
 
   scm_expmem = 0;
 
   j = SCM_HEAP_SEG_SIZE;
   scm_mtrigger = SCM_INIT_MALLOC_LIMIT;
-  scm_heap_table = ((struct scm_heap_seg_data *)
-                   scm_must_malloc (sizeof (struct scm_heap_seg_data) * 2, "hplims"));
+  scm_heap_table = ((scm_heap_seg_data_t *)
+                   scm_must_malloc (sizeof (scm_heap_seg_data_t) * 2, "hplims"));
+  heap_segment_table_size = 2;
 
-#ifdef GUILE_NEW_GC_SCHEME
+  mark_space_ptr = &mark_space_head;
+
+  init_heap_size_1 = scm_i_getenv_int ("GUILE_INIT_SEGMENT_SIZE_1", scm_default_init_heap_size_1);
+  init_heap_size_2 = scm_i_getenv_int ("GUILE_INIT_SEGMENT_SIZE_2", scm_default_init_heap_size_2);
   if (make_initial_segment (init_heap_size_1, &scm_master_freelist) ||
       make_initial_segment (init_heap_size_2, &scm_master_freelist2))
     return 1;
-#else
-  if (make_initial_segment (init_heap_size_1, &scm_freelist) ||
-      make_initial_segment (init_heap_size_2, &scm_freelist2))
-    return 1;
-#endif
-
-  scm_heap_org = CELL_UP (scm_heap_table[0].bounds[0]);
 
   /* scm_hplims[0] can change. do not remove scm_heap_org */
-  scm_weak_vectors = SCM_EOL;
+  scm_heap_org = CELL_UP (scm_heap_table[0].bounds[0], 1);
+
+  scm_c_hook_init (&scm_before_gc_c_hook, 0, SCM_C_HOOK_NORMAL);
+  scm_c_hook_init (&scm_before_mark_c_hook, 0, SCM_C_HOOK_NORMAL);
+  scm_c_hook_init (&scm_before_sweep_c_hook, 0, SCM_C_HOOK_NORMAL);
+  scm_c_hook_init (&scm_after_sweep_c_hook, 0, SCM_C_HOOK_NORMAL);
+  scm_c_hook_init (&scm_after_gc_c_hook, 0, SCM_C_HOOK_NORMAL);
 
   /* Initialise the list of ports.  */
   scm_port_table = (scm_port **)
@@ -2522,24 +2518,95 @@ scm_init_storage (scm_sizet init_heap_size, scm_sizet init_heap2_size)
   scm_listofnull = scm_cons (SCM_EOL, SCM_EOL);
   scm_nullstr = scm_makstr (0L, 0);
   scm_nullvect = scm_make_vector (SCM_INUM0, SCM_UNDEFINED);
-  scm_symhash = scm_make_vector ((SCM) SCM_MAKINUM (scm_symhash_dim), SCM_EOL);
-  scm_weak_symhash = scm_make_weak_key_hash_table ((SCM) SCM_MAKINUM (scm_symhash_dim));
-  scm_symhash_vars = scm_make_vector ((SCM) SCM_MAKINUM (scm_symhash_dim), SCM_EOL);
+
+#define DEFAULT_SYMHASH_SIZE 277
+  scm_symhash = scm_make_vector (SCM_MAKINUM (DEFAULT_SYMHASH_SIZE), SCM_EOL);
+  scm_symbols = scm_make_weak_key_hash_table (SCM_MAKINUM (DEFAULT_SYMHASH_SIZE));
+  scm_symhash_vars = scm_make_vector (SCM_MAKINUM (DEFAULT_SYMHASH_SIZE), SCM_EOL);
+
   scm_stand_in_procs = SCM_EOL;
   scm_permobjs = SCM_EOL;
-  scm_protects = SCM_EOL;
-  scm_asyncs = SCM_EOL;
-  scm_sysintern ("most-positive-fixnum", (SCM) SCM_MAKINUM (SCM_MOST_POSITIVE_FIXNUM));
-  scm_sysintern ("most-negative-fixnum", (SCM) SCM_MAKINUM (SCM_MOST_NEGATIVE_FIXNUM));
+  scm_protects = scm_make_vector (SCM_MAKINUM (31), SCM_EOL);
+  scm_sysintern ("most-positive-fixnum", SCM_MAKINUM (SCM_MOST_POSITIVE_FIXNUM));
+  scm_sysintern ("most-negative-fixnum", SCM_MAKINUM (SCM_MOST_NEGATIVE_FIXNUM));
 #ifdef SCM_BIGDIG
   scm_sysintern ("bignum-radix", SCM_MAKINUM (SCM_BIGRAD));
 #endif
+
   return 0;
 }
+
 \f
 
+SCM scm_after_gc_hook;
+
+#if (SCM_DEBUG_DEPRECATED == 0)
+static SCM scm_gc_vcell;  /* the vcell for gc-thunk. */
+#endif  /* SCM_DEBUG_DEPRECATED == 0 */
+static SCM gc_async;
+
+
+/* The function gc_async_thunk causes the execution of the after-gc-hook.  It
+ * is run after the gc, as soon as the asynchronous events are handled by the
+ * evaluator.
+ */
+static SCM
+gc_async_thunk (void)
+{
+  scm_c_run_hook (scm_after_gc_hook, SCM_EOL);
+
+#if (SCM_DEBUG_DEPRECATED == 0)
+
+  /* The following code will be removed in Guile 1.5.  */
+  if (SCM_NFALSEP (scm_gc_vcell))
+    {
+      SCM proc = SCM_CDR (scm_gc_vcell);
+
+      if (SCM_NFALSEP (proc) && !SCM_UNBNDP (proc))
+       scm_apply (proc, SCM_EOL, SCM_EOL);
+    }
+
+#endif  /* SCM_DEBUG_DEPRECATED == 0 */
+
+  return SCM_UNSPECIFIED;
+}
+
+
+/* The function mark_gc_async is run by the scm_after_gc_c_hook at the end of
+ * the garbage collection.  The only purpose of this function is to mark the
+ * gc_async (which will eventually lead to the execution of the
+ * gc_async_thunk).
+ */
+static void *
+mark_gc_async (void * hook_data, void *func_data, void *data)
+{
+  scm_system_async_mark (gc_async);
+  return NULL;
+}
+
+
 void
 scm_init_gc ()
 {
-#include "gc.x"
+  SCM after_gc_thunk;
+
+  scm_after_gc_hook = scm_create_hook ("after-gc-hook", 0);
+
+#if (SCM_DEBUG_DEPRECATED == 0)
+  scm_gc_vcell = scm_sysintern ("gc-thunk", SCM_BOOL_F);
+#endif  /* SCM_DEBUG_DEPRECATED == 0 */
+  after_gc_thunk = scm_make_subr_opt ("%gc-thunk", scm_tc7_subr_0, gc_async_thunk, 0);
+  gc_async = scm_system_async (after_gc_thunk);  /* protected via scm_asyncs */
+
+  scm_c_hook_add (&scm_after_gc_c_hook, mark_gc_async, NULL, 0);
+
+#ifndef SCM_MAGIC_SNARFER
+#include "libguile/gc.x"
+#endif
 }
+
+/*
+  Local Variables:
+  c-file-style: "gnu"
+  End:
+*/