* gc.c: Removed #include "libguile/guardians.h".
[bpt/guile.git] / libguile / gc.c
index 806f19a..baedf21 100644 (file)
 /* 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 "weaks.h"
-#include "guardians.h"
-#include "smob.h"
-#include "unif.h"
-#include "async.h"
-#include "ports.h"
-#include "root.h"
-#include "strings.h"
-#include "vectors.h"
-
-#include "validate.h"
-#include "gc.h"
+#include "libguile/_scm.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/validate.h"
+#include "libguile/gc.h"
+
+#ifdef GUILE_DEBUG_MALLOC
+#include "libguile/debug-malloc.h"
+#endif
 
 #ifdef HAVE_MALLOC_H
 #include <malloc.h>
  * work around a oscillation that caused almost constant GC.]
  */
 
-#define SCM_INIT_HEAP_SIZE_1 (40000L * sizeof (scm_cell))
+/*
+ * 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_INIT_HEAP_SIZE_1 (45000L * sizeof (scm_cell))
 #define SCM_CLUSTER_SIZE_1 2000L
-#define SCM_GC_TRIGGER_1 -50
+#define SCM_MIN_YIELD_1 40
 
 #define SCM_INIT_HEAP_SIZE_2 (2500L * 2 * sizeof (scm_cell))
 #define SCM_CLUSTER_SIZE_2 1000L
 /* 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
+#define SCM_MIN_YIELD_2 40
 
 #define SCM_MAX_SEGMENT_SIZE 2097000L /* a little less (adm) than 2 Mb */
 
 #endif                         /* PROT386 */
 #define CLUSTER_SIZE_IN_BYTES(freelist) ((freelist)->cluster_size * (freelist)->span * sizeof(scm_cell))
 #define ALIGNMENT_SLACK(freelist) (sizeof (scm_cell) * (freelist)->span - 1)
+#ifdef GUILE_NEW_GC_SCHEME
+#define SCM_HEAP_SIZE \
+  (scm_master_freelist.heap_size + scm_master_freelist2.heap_size)
+#else
+#define SCM_HEAP_SIZE (scm_freelist.heap_size + scm_freelist2.heap_size)
+#endif
+#define SCM_MAX(A, B) ((A) > (B) ? (A) : (B))
 
 
 \f
@@ -170,41 +188,45 @@ typedef struct scm_freelist_t {
 #ifdef GUILE_NEW_GC_SCHEME
   /* number of cells left to collect before cluster is full */
   unsigned int left_to_collect;
-  /* a list of freelists, each of size gc_trigger,
-     except the last one which may be shorter */
+  /* 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 cells in each cluster, including the spine cell */
+  /* this is the number of objects in each cluster, including the spine cell */
   int cluster_size;
-  /* set to grow the heap when we run out of clusters
+  /* indicates that we should grow heap instead of GC:ing
    */
   int grow_heap_p;
-  /* minimum number of objects allocated before GC is triggered
+  /* minimum yield on this list in order not to grow the heap
    */
-  int gc_trigger;
-  /* defines gc_trigger as percent of heap size
-   * 0 => constant trigger
+  long min_yield;
+  /* defines min_yield as percent of total heap size
    */
-  int gc_trigger_fraction;
+  int min_yield_fraction;
 #endif
   /* number of cells per object on this list */
   int span;
   /* number of collected cells during last GC */
-  int collected;
+  long collected;
+  /* number of collected cells during penultimate GC */
+  long collected_1;
   /* total number of cells in heap segments
    * belonging to this list.
    */
-  int heap_size;
+  long heap_size;
 } scm_freelist_t;
 
 #ifdef GUILE_NEW_GC_SCHEME
 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 };
@@ -244,7 +266,11 @@ SCM scm_weak_vectors;
  */
 unsigned long scm_cells_allocated = 0;
 long scm_mallocated = 0;
-/* unsigned long scm_gc_cells_collected; */
+unsigned long scm_gc_cells_collected;
+#ifdef GUILE_NEW_GC_SCHEME
+unsigned long scm_gc_yield;
+static unsigned long scm_gc_yield_1 = 0; /* previous GC yield */
+#endif
 unsigned long scm_gc_malloc_collected;
 unsigned long scm_gc_ports_collected;
 unsigned long scm_gc_rt;
@@ -278,8 +304,6 @@ typedef struct scm_heap_seg_data_t
 
 
 
-
-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 *);
 
@@ -296,8 +320,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));
@@ -449,8 +473,8 @@ SCM_DEFINE (scm_free_list_length, "free-list-length", 0, 0, 0,
             "`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
@@ -629,6 +653,36 @@ scm_debug_newcell2 (void)
 
 \f
 
+#ifdef GUILE_NEW_GC_SCHEME
+static unsigned long
+master_cells_allocated (scm_freelist_t *master)
+{
+  int objects = master->clusters_allocated * (master->cluster_size - 1);
+  if (SCM_NULLP (master->clusters))
+    objects -= master->left_to_collect;
+  return master->span * objects;
+}
+
+static unsigned long
+freelist_length (SCM freelist)
+{
+  int n;
+  for (n = 0; SCM_NNULLP (freelist); freelist = SCM_CDR (freelist))
+    ++n;
+  return n;
+}
+
+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));
+}
+#endif
+
 /* {Scheme Interface to GC}
  */
 
@@ -660,15 +714,17 @@ SCM_DEFINE (scm_gc_stats, "gc-stats", 0, 0, 0,
     goto retry;
   scm_block_gc = 0;
 
-  /// ? ?? ?
+  /* 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;
+  local_scm_heap_size = SCM_HEAP_SIZE;
 #ifdef GUILE_NEW_GC_SCHEME
-  local_scm_heap_size = scm_master_freelist.heap_size; /*fixme*/
+  local_scm_cells_allocated = compute_cells_allocated ();
 #else
-  local_scm_heap_size = scm_freelist.heap_size; /*fixme*/
-#endif
   local_scm_cells_allocated = scm_cells_allocated;
+#endif
   local_scm_gc_time_taken = scm_gc_time_taken;
 
   answer = scm_listify (scm_cons (sym_gc_time_taken, scm_ulong2num (local_scm_gc_time_taken)),
@@ -688,7 +744,13 @@ void
 scm_gc_start (const char *what)
 {
   scm_gc_rt = SCM_INUM (scm_get_internal_run_time ());
-  /* scm_gc_cells_collected = 0; */
+  scm_gc_cells_collected = 0;
+#ifdef GUILE_NEW_GC_SCHEME
+  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));
+#endif
   scm_gc_malloc_collected = 0;
   scm_gc_ports_collected = 0;
 }
@@ -708,7 +770,7 @@ SCM_DEFINE (scm_object_address, "object-address", 1, 0, 0,
            "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
 
@@ -733,6 +795,38 @@ SCM_DEFINE (scm_gc, "gc", 0, 0, 0,
 
 #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.
  */
@@ -752,15 +846,25 @@ scm_gc_for_newcell (scm_freelist_t *master, SCM *freelist)
              alloc_some_heap (master);
            }
          else
-           scm_igc ("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);
+           }
        }
       cell = SCM_CAR (master->clusters);
       master->clusters = SCM_CDR (master->clusters);
+      ++master->clusters_allocated;
     }
   while (SCM_NULLP (cell));
   --scm_ints_disabled;
   *freelist = SCM_CDR (cell);
-  SCM_SETCAR (cell, scm_tc16_allocated);
+  SCM_SET_CELL_TYPE (cell, scm_tc16_allocated);
   return cell;
 }
 
@@ -811,11 +915,20 @@ scm_gc_for_newcell (scm_freelist_t *freelist)
 
 #endif /* GUILE_NEW_GC_SCHEME */
 
+SCM scm_after_gc_hook;
+
+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;
+
 void
 scm_igc (const char *what)
 {
   int j;
 
+  scm_c_hook_run (&scm_before_gc_c_hook, 0);
 #ifdef DEBUGINFO
   fprintf (stderr,
           SCM_NULLP (scm_freelist)
@@ -852,10 +965,6 @@ 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
   {
@@ -893,6 +1002,8 @@ scm_igc (const char *what)
       }
   }
 
+  scm_c_hook_run (&scm_before_mark_c_hook, 0);
+
 #ifndef USE_THREADS
 
   /* Protect from the C stack.  This must be the first marking
@@ -955,18 +1066,19 @@ scm_igc (const char *what)
   scm_gc_mark (scm_root->handle);
 #endif
 
-  scm_mark_weak_vector_spines ();
-
-  scm_guardian_zombify ();
+  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 ();
 
 #ifdef USE_THREADS
   SCM_THREAD_CRITICAL_SECTION_END;
 #endif
+  scm_c_hook_run (&scm_after_gc_c_hook, 0);
 }
 
 \f
@@ -1017,7 +1129,7 @@ gc_mark_nimp:
       if (SCM_GCMARKP (ptr))
        break;
       SCM_SETGCMARK (ptr);
-      scm_gc_mark (SCM_CELL_WORD (ptr, 2));
+      scm_gc_mark (SCM_CELL_OBJECT_2 (ptr));
       ptr = SCM_GCCDR (ptr);
       goto gc_mark_loop;
     case scm_tcs_cons_gloc:
@@ -1025,56 +1137,61 @@ gc_mark_nimp:
        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 */
+       switch (vtable_data [scm_vtable_index_vcell])
          {
          default:
-           scm_gc_mark (vcell);
-           ptr = SCM_GCCDR (ptr);
-           goto gc_mark_loop;
+           {
+             /* ptr is a gloc */
+             SCM gloc_car = SCM_PACK (word0);
+             scm_gc_mark (gloc_car);
+             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);
+             /* ptr is a struct */
+             SCM layout = SCM_PACK (vtable_data [scm_vtable_index_layout]);
+             int len = SCM_LENGTH (layout);
+             char * 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);
+             scm_bits_t * struct_data = (scm_bits_t *) SCM_UNPACK (SCM_GCCDR (ptr));
 
-             if (SCM_UNPACK (vtable_data[scm_struct_i_flags]) & SCM_STRUCTF_ENTITY)
+             if (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]);
+                 scm_gc_mark (SCM_PACK (struct_data[scm_struct_i_procedure]));
+                 scm_gc_mark (SCM_PACK (struct_data[scm_struct_i_setter]));
                }
              if (len)
                {
-                 for (x = 0; x < len - 2; x += 2, ++mem)
+                 int x;
+
+                 for (x = 0; x < len - 2; x += 2, ++struct_data)
                    if (fields_desc[x] == 'p')
-                     scm_gc_mark (*mem);
+                     scm_gc_mark (SCM_PACK (*struct_data));
                  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);
+                       for (x = *struct_data; x; --x)
+                         scm_gc_mark (SCM_PACK (*++struct_data));
                      else
-                       scm_gc_mark (*mem);
+                       scm_gc_mark (SCM_PACK (*struct_data));
                    }
                }
-             if (!SCM_CDR (vcell))
+             if (vtable_data [scm_vtable_index_vcell] == 0)
                {
-                 SCM_SETGCMARK (vcell);
-                 ptr = vtable_data[scm_vtable_index_vtable];
+                 vtable_data [scm_vtable_index_vcell] = 1;
+                 ptr = SCM_PACK (vtable_data [scm_vtable_index_vtable]);
                  goto gc_mark_loop;
                }
            }
@@ -1281,9 +1398,9 @@ scm_mark_locations (SCM_STACKITEM x[], scm_sizet n)
   register SCM_CELLPTR ptr;
 
   while (0 <= --m)
-    if (SCM_CELLP (*(SCM **) (& x[m])))
+    if (SCM_CELLP (* (SCM *) &x[m]))
       {
-       ptr = (SCM_CELLPTR) SCM2PTR ((*(SCM **) & x[m]));
+       ptr = SCM2PTR (* (SCM *) &x[m]);
        i = 0;
        j = scm_n_heap_segs - 1;
        if (   SCM_PTR_LE (scm_heap_table[i].bounds[0], ptr)
@@ -1323,12 +1440,12 @@ scm_mark_locations (SCM_STACKITEM x[], scm_sizet n)
                          break;
                      }
                  }
-               if (   !scm_heap_table[seg_id].valid
+               if (!scm_heap_table[seg_id].valid
                    || scm_heap_table[seg_id].valid (ptr,
                                                     &scm_heap_table[seg_id]))
-                  if (   scm_heap_table[seg_id].span == 1
-                      || SCM_DOUBLE_CELLP (*(SCM **) (& x[m])))
-                    scm_gc_mark (*(SCM *) & x[m]);
+                  if (scm_heap_table[seg_id].span == 1
+                     || SCM_DOUBLE_CELLP (* (SCM *) &x[m]))
+                    scm_gc_mark (* (SCM *) &x[m]);
                break;
              }
 
@@ -1348,9 +1465,9 @@ scm_cellp (SCM value)
   register int i, j;
   register SCM_CELLPTR ptr;
 
-  if SCM_CELLP (*(SCM **) (& value))
+  if (SCM_CELLP (value))
     {
-      ptr = (SCM_CELLPTR) SCM2PTR ((*(SCM **) & value));
+      ptr = SCM2PTR (value);
       i = 0;
       j = scm_n_heap_segs - 1;
       if (   SCM_PTR_LE (scm_heap_table[i].bounds[0], ptr)
@@ -1390,10 +1507,12 @@ scm_cellp (SCM value)
                        break;
                    }
                }
-             if (   !scm_heap_table[seg_id].valid
+             if (!scm_heap_table[seg_id].valid
                     || scm_heap_table[seg_id].valid (ptr,
                                                      &scm_heap_table[seg_id]))
-               return 1;
+               if (scm_heap_table[seg_id].span == 1
+                   || SCM_DOUBLE_CELLP (value))
+                 scm_gc_mark (value);
              break;
            }
 
@@ -1403,56 +1522,23 @@ scm_cellp (SCM value)
 }
 
 
-static void
-scm_mark_weak_vector_spines ()
-{
-  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);
-               }
-           }
-       }
-    }
-}
-
-
 #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))
     {
@@ -1462,8 +1548,17 @@ 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
 
@@ -1471,12 +1566,6 @@ void
 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;
@@ -1522,27 +1611,33 @@ scm_gc_sweep ()
       seg_size = CELL_DN (scm_heap_table[i].bounds[1], span) - ptr;
       for (j = seg_size + span; j -= span; ptr += span)
        {
-#ifdef SCM_POINTERS_MUNGED
-         scmptr = PTR2SCM (ptr);
-#endif
+         SCM scmptr = PTR2SCM (ptr);
+
          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;
+               scm_bits_t * vtable_data = (scm_bits_t *) word0; /* access as struct */
+               if (SCM_GCMARKP (scmptr))
                  {
-                   scm_struct_free_t free
-                     = (scm_struct_free_t) ((SCM*) vcell)[scm_struct_i_free];
-                   m += free ((SCM *) vcell, (SCM *) SCM_GCCDR (scmptr));
+                   if (vtable_data [scm_vtable_index_vcell] == 1)
+                     vtable_data [scm_vtable_index_vcell] = 0;
+                   goto cmrkcontinue;
+                 }
+               else 
+                 {
+                   if (vtable_data [scm_vtable_index_vcell] == 0
+                       || vtable_data [scm_vtable_index_vcell] == 1)
+                     {
+                       scm_struct_free_t free
+                         = (scm_struct_free_t) vtable_data[scm_struct_i_free];
+                       m += free (vtable_data, (scm_bits_t *) SCM_UNPACK (SCM_GCCDR (scmptr)));
+                     }
                  }
              }
              break;
@@ -1635,9 +1730,8 @@ scm_gc_sweep ()
            case scm_tc7_msymbol:
              if (SCM_GC8MARKP (scmptr))
                goto c8mrkcontinue;
-             m += (  SCM_LENGTH (scmptr)
-                   + 1
-                   + sizeof (SCM) * ((SCM *)SCM_CHARS (scmptr) - SCM_SLOTS(scmptr)));
+             m += (SCM_LENGTH (scmptr) + 1
+                   + (SCM_CHARS (scmptr) - (char *) SCM_SLOTS (scmptr)));
              scm_must_free ((char *)SCM_SLOTS (scmptr));
              break;
            case scm_tc7_contin:
@@ -1702,7 +1796,7 @@ scm_gc_sweep ()
                    k = SCM_SMOBNUM (scmptr);
                    if (!(k < scm_numsmob))
                      goto sweeperr;
-                   m += (scm_smobs[k].free) ((SCM) scmptr);
+                   m += (scm_smobs[k].free) (scmptr);
                    break;
                  }
                }
@@ -1734,7 +1828,7 @@ scm_gc_sweep ()
                 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_SET_CELL_TYPE (scmptr, scm_tc_free_cell);
              SCM_SETCDR (scmptr, nfreelist);
              nfreelist = scmptr;
            }
@@ -1772,7 +1866,6 @@ scm_gc_sweep ()
 
 #ifndef GUILE_NEW_GC_SCHEME
       freelist->collected += n;
-      scm_cells_allocated += freelist->heap_size - freelist->collected;
 #endif
 
 #ifdef GUILE_DEBUG_FREELIST
@@ -1797,63 +1890,10 @@ scm_gc_sweep ()
   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);
+#ifdef GUILE_NEW_GC_SCHEME
+  scm_gc_yield -= scm_cells_allocated;
+#endif
   scm_mallocated -= m;
   scm_gc_malloc_collected = m;
 }
@@ -1870,6 +1910,7 @@ scm_gc_sweep ()
  * The primary purpose of the front end is to impose calls to gc.
  */
 
+
 /* scm_must_malloc
  * Return newly malloced storage or throw an error.
  *
@@ -1893,6 +1934,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;
        }
     }
@@ -1910,6 +1954,10 @@ 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;
     }
 
@@ -1936,6 +1984,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;
        }
     }
@@ -1953,6 +2004,9 @@ scm_must_realloc (void *where,
        else
          scm_mtrigger += scm_mtrigger / 2;
       }
+#ifdef GUILE_DEBUG_MALLOC
+      scm_malloc_reregister (where, ptr, what);
+#endif
       return ptr;
     }
 
@@ -1963,6 +2017,9 @@ scm_must_realloc (void *where,
 void
 scm_must_free (void *obj)
 {
+#ifdef GUILE_DEBUG_MALLOC
+  scm_malloc_unregister (obj);
+#endif
   if (obj)
     free (obj);
   else
@@ -1996,20 +2053,6 @@ scm_done_malloc (long size)
 }
 
 
-#ifdef GUILE_NEW_GC_SCHEME
-static void
-adjust_gc_trigger (scm_freelist_t *freelist)
-{
-  /* 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);
-}
-#endif
-
-
 \f
 
 /* {Heap Segments}
@@ -2053,12 +2096,6 @@ 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;
@@ -2093,8 +2130,8 @@ init_heap_seg (SCM_CELLPTR seg_org, scm_sizet size, scm_freelist_t *freelist)
   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;
+  scm_heap_table[new_seg_index].bounds[0] = ptr;
+  scm_heap_table[new_seg_index].bounds[1] = seg_end;
 
 
   /* Compute the least valid object pointer w/in this segment
@@ -2138,10 +2175,9 @@ init_heap_seg (SCM_CELLPTR seg_org, scm_sizet size, scm_freelist_t *freelist)
 
        while (ptr < seg_end)
          {
-#ifdef SCM_POINTERS_MUNGED
-           scmptr = PTR2SCM (ptr);
-#endif
-           SCM_SETCAR (scmptr, scm_tc_free_cell);
+           SCM scmptr = PTR2SCM (ptr);
+
+           SCM_SET_CELL_TYPE (scmptr, scm_tc_free_cell);
            SCM_SETCDR (scmptr, PTR2SCM (ptr + span));
            ptr += span;
          }
@@ -2156,18 +2192,14 @@ 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 scmptr = PTR2SCM (ptr);
+
       SCM_SETCAR (scmptr, (SCM) scm_tc_free_cell);
       SCM_SETCDR (scmptr, PTR2SCM (ptr + span));
       ptr += span;
@@ -2189,9 +2221,6 @@ init_heap_seg (SCM_CELLPTR seg_org, scm_sizet size, scm_freelist_t *freelist)
   fprintf (stderr, "H");
 #endif
   return size;
-#ifdef scmptr
-#undef scmptr
-#endif
 }
 
 #ifndef GUILE_NEW_GC_SCHEME
@@ -2215,7 +2244,7 @@ alloc_some_heap (scm_freelist_t *freelist)
 {
   scm_heap_seg_data_t * tmptable;
   SCM_CELLPTR ptr;
-  scm_sizet len;
+  long len;
 
   /* Critical code sections (such as the garbage collector)
    * aren't supposed to add heap segments.
@@ -2243,16 +2272,31 @@ alloc_some_heap (scm_freelist_t *freelist)
    */
 #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)
@@ -2285,7 +2329,7 @@ alloc_some_heap (scm_freelist_t *freelist)
     while ((len >= SCM_MIN_HEAP_SEG_SIZE)
           && (len >= smallest))
       {
-        scm_sizet rounded_len = round_to_cluster_size(freelist, len);
+        scm_sizet rounded_len = round_to_cluster_size (freelist, len);
        SCM_SYSCALL (ptr = (SCM_CELLPTR) malloc (rounded_len));
        if (ptr)
          {
@@ -2300,7 +2344,6 @@ alloc_some_heap (scm_freelist_t *freelist)
 }
 
 
-
 SCM_DEFINE (scm_unhash_name, "unhash-name", 1, 0, 0,
             (SCM name),
            "")
@@ -2315,20 +2358,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_TRUE_P (name) || 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;
@@ -2416,7 +2462,7 @@ scm_unprotect_object (SCM obj)
   SCM *tail_ptr = &scm_protects;
 
   while (SCM_CONSP (*tail_ptr))
-    if (SCM_CAR (*tail_ptr) == obj)
+    if (SCM_EQ_P (SCM_CAR (*tail_ptr), obj))
       {
        *tail_ptr = SCM_CDR (*tail_ptr);
        break;
@@ -2466,7 +2512,12 @@ make_initial_segment (scm_sizet init_heap_size, scm_freelist_t *freelist)
   else
     scm_expmem = 1;
 
-  freelist->grow_heap_p = (freelist->heap_size < freelist->gc_trigger);
+#ifdef GUILE_NEW_GC_SCHEME
+  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);
+#endif
 
   return 0;
 }
@@ -2477,19 +2528,17 @@ 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;
 }
 
@@ -2499,7 +2548,7 @@ scm_init_storage (scm_sizet init_heap_size_1, int gc_trigger_1,
                  scm_sizet max_segment_size)
 #else
 int
-scm_init_storage (scm_sizet init_heap_size, scm_sizet init_heap2_size)
+scm_init_storage (scm_sizet init_heap_size_1, scm_sizet init_heap_size_2)
 #endif
 {
   scm_sizet j;
@@ -2519,10 +2568,10 @@ scm_init_storage (scm_sizet init_heap_size, scm_sizet init_heap2_size)
   scm_freelist2 = SCM_EOL;
   init_freelist (&scm_master_freelist,
                 1, SCM_CLUSTER_SIZE_1,
-                gc_trigger_1 ? gc_trigger_1 : SCM_GC_TRIGGER_1);
+                gc_trigger_1 ? gc_trigger_1 : SCM_MIN_YIELD_1);
   init_freelist (&scm_master_freelist2,
                 2, SCM_CLUSTER_SIZE_2,
-                gc_trigger_2 ? gc_trigger_2 : SCM_GC_TRIGGER_2);
+                gc_trigger_2 ? gc_trigger_2 : SCM_MIN_YIELD_2);
   scm_max_segment_size
     = max_segment_size ? max_segment_size : SCM_MAX_SEGMENT_SIZE;
 #else
@@ -2554,10 +2603,14 @@ scm_init_storage (scm_sizet init_heap_size, scm_sizet init_heap2_size)
     return 1;
 #endif
 
+  /* scm_hplims[0] can change. do not remove scm_heap_org */
   scm_heap_org = CELL_UP (scm_heap_table[0].bounds[0], 1);
 
-  /* scm_hplims[0] can change. do not remove scm_heap_org */
-  scm_weak_vectors = SCM_EOL;
+  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 **)
@@ -2579,15 +2632,15 @@ 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);
+  scm_symhash = scm_make_vector (SCM_MAKINUM (scm_symhash_dim), SCM_EOL);
+  scm_weak_symhash = scm_make_weak_key_hash_table (SCM_MAKINUM (scm_symhash_dim));
+  scm_symhash_vars = scm_make_vector (SCM_MAKINUM (scm_symhash_dim), 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_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
@@ -2598,5 +2651,12 @@ scm_init_storage (scm_sizet init_heap_size, scm_sizet init_heap2_size)
 void
 scm_init_gc ()
 {
-#include "gc.x"
+  scm_after_gc_hook = scm_create_hook ("after-gc-hook", 0);
+#include "libguile/gc.x"
 }
+
+/*
+  Local Variables:
+  c-file-style: "gnu"
+  End:
+*/