[project @ 1999-03-26 10:29:02 by simonm]
[ghc-hetmet.git] / ghc / rts / GC.c
index fb2eaa5..05728ca 100644 (file)
@@ -1,7 +1,9 @@
 /* -----------------------------------------------------------------------------
- * $Id: GC.c,v 1.17 1999/01/20 16:07:40 simonm Exp $
+ * $Id: GC.c,v 1.57 1999/03/26 10:29:04 simonm Exp $
  *
- * Two-space garbage collector
+ * (c) The GHC Team 1998-1999
+ *
+ * Generational garbage collector
  *
  * ---------------------------------------------------------------------------*/
 
@@ -20,6 +22,7 @@
 #include "DebugProf.h"
 #include "SchedAPI.h"
 #include "Weak.h"
+#include "StablePriv.h"
 
 StgCAF* enteredCAFs;
 
@@ -88,24 +91,32 @@ static rtsBool failed_to_evac;
  */
 bdescr *old_to_space;
 
+/* Data used for allocation area sizing.
+ */
+lnat new_blocks;               /* blocks allocated during this GC */
+lnat g0s0_pcnt_kept = 30;      /* percentage of g0s0 live at last minor GC */
+
 /* -----------------------------------------------------------------------------
    Static function declarations
    -------------------------------------------------------------------------- */
 
-static StgClosure *evacuate(StgClosure *q);
-static void    zeroStaticObjectList(StgClosure* first_static);
-static rtsBool traverse_weak_ptr_list(void);
-static void    zeroMutableList(StgMutClosure *first);
-static void    revertDeadCAFs(void);
+static StgClosure * evacuate                ( StgClosure *q );
+static void         zero_static_object_list ( StgClosure* first_static );
+static void         zero_mutable_list       ( StgMutClosure *first );
+static void         revert_dead_CAFs        ( void );
+
+static rtsBool      traverse_weak_ptr_list  ( void );
+static void         cleanup_weak_ptr_list   ( StgWeak **list );
 
-static void           scavenge_stack(StgPtr p, StgPtr stack_end);
-static void           scavenge_large(step *step);
-static void           scavenge(step *step);
-static void           scavenge_static(void);
-static StgMutClosure *scavenge_mutable_list(StgMutClosure *p, nat gen);
+static void         scavenge_stack          ( StgPtr p, StgPtr stack_end );
+static void         scavenge_large          ( step *step );
+static void         scavenge                ( step *step );
+static void         scavenge_static         ( void );
+static void         scavenge_mutable_list   ( generation *g );
+static void         scavenge_mut_once_list  ( generation *g );
 
 #ifdef DEBUG
-static void gcCAFs(void);
+static void         gcCAFs                  ( void );
 #endif
 
 /* -----------------------------------------------------------------------------
@@ -135,7 +146,7 @@ void GarbageCollect(void (*get_roots)(void))
 {
   bdescr *bd;
   step *step;
-  lnat live, allocated, collected = 0;
+  lnat live, allocated, collected = 0, copied = 0;
   nat g, s;
 
 #ifdef PROFILING
@@ -166,6 +177,10 @@ void GarbageCollect(void (*get_roots)(void))
   for ( bd = current_nursery->link; bd != NULL; bd = bd->link ) {
     allocated -= BLOCK_SIZE_W;
   }
+  if (current_nursery->free < current_nursery->start + BLOCK_SIZE_W) {
+    allocated -= (current_nursery->start + BLOCK_SIZE_W)
+      - current_nursery->free;
+  }
 
   /* Figure out which generation to collect
    */
@@ -187,10 +202,10 @@ void GarbageCollect(void (*get_roots)(void))
   scavenged_static_objects = END_OF_STATIC_LIST;
 
   /* zero the mutable list for the oldest generation (see comment by
-   * zeroMutableList below).
+   * zero_mutable_list below).
    */
   if (major_gc) { 
-    zeroMutableList(generations[RtsFlags.GcFlags.generations-1].mut_list);
+    zero_mutable_list(generations[RtsFlags.GcFlags.generations-1].mut_once_list);
   }
 
   /* Save the old to-space if we're doing a two-space collection
@@ -200,10 +215,16 @@ void GarbageCollect(void (*get_roots)(void))
     g0s0->to_space = NULL;
   }
 
+  /* Keep a count of how many new blocks we allocated during this GC
+   * (used for resizing the allocation area, later).
+   */
+  new_blocks = 0;
+
   /* Initialise to-space in all the generations/steps that we're
    * collecting.
    */
   for (g = 0; g <= N; g++) {
+    generations[g].mut_once_list = END_MUT_LIST;
     generations[g].mut_list = END_MUT_LIST;
 
     for (s = 0; s < generations[g].n_steps; s++) {
@@ -228,11 +249,12 @@ void GarbageCollect(void (*get_roots)(void))
       step->hpLim     = step->hp + BLOCK_SIZE_W;
       step->hp_bd     = bd;
       step->to_space  = bd;
-      step->to_blocks = 1; /* ???? */
+      step->to_blocks = 1;
       step->scan      = bd->start;
       step->scan_bd   = bd;
       step->new_large_objects = NULL;
       step->scavenged_large_objects = NULL;
+      new_blocks++;
       /* mark the large objects as not evacuated yet */
       for (bd = step->large_objects; bd; bd = bd->link) {
        bd->evacuated = 0;
@@ -257,6 +279,7 @@ void GarbageCollect(void (*get_roots)(void))
        step->hp_bd = bd;
        step->blocks = bd;
        step->n_blocks = 1;
+       new_blocks++;
       }
       /* Set the scan pointer for older generations: remember we
        * still have to scavenge objects that have been promoted. */
@@ -282,19 +305,27 @@ void GarbageCollect(void (*get_roots)(void))
    * it has already been evaced to gen 2.
    */
   { 
-    StgMutClosure *tmp, **pp;
-    for (g = N+1; g < RtsFlags.GcFlags.generations; g++) {
+    int st;
+    for (g = RtsFlags.GcFlags.generations-1; g > N; g--) {
       generations[g].saved_mut_list = generations[g].mut_list;
       generations[g].mut_list = END_MUT_LIST;
     }
 
+    /* Do the mut-once lists first */
     for (g = RtsFlags.GcFlags.generations-1; g > N; g--) {
-      tmp = scavenge_mutable_list(generations[g].saved_mut_list, g);
-      pp = &generations[g].mut_list;
-      while (*pp != END_MUT_LIST) {
-          pp = &(*pp)->mut_link;
+      scavenge_mut_once_list(&generations[g]);
+      evac_gen = g;
+      for (st = generations[g].n_steps-1; st >= 0; st--) {
+       scavenge(&generations[g].steps[st]);
+      }
+    }
+
+    for (g = RtsFlags.GcFlags.generations-1; g > N; g--) {
+      scavenge_mutable_list(&generations[g]);
+      evac_gen = g;
+      for (st = generations[g].n_steps-1; st >= 0; st--) {
+       scavenge(&generations[g].steps[st]);
       }
-      *pp = tmp;
     }
   }
 
@@ -312,11 +343,14 @@ void GarbageCollect(void (*get_roots)(void))
   /* Mark the weak pointer list, and prepare to detect dead weak
    * pointers.
    */
-  markWeakList();
   old_weak_ptr_list = weak_ptr_list;
   weak_ptr_list = NULL;
   weak_done = rtsFalse;
 
+  /* Mark the stable pointer table.
+   */
+  markStablePtrTable(major_gc);
+
 #ifdef INTERPRETER
   { 
       /* ToDo: To fix the caf leak, we need to make the commented out
@@ -324,22 +358,7 @@ void GarbageCollect(void (*get_roots)(void))
        * the CAF document.
        */
       extern void markHugsObjects(void);
-#if 0
-      /* ToDo: This (undefined) function should contain the scavenge
-       * loop immediately below this block of code - but I'm not sure
-       * enough of the details to do this myself.
-       */
-      scavengeEverything();
-      /* revert dead CAFs and update enteredCAFs list */
-      revertDeadCAFs();
-#endif      
       markHugsObjects();
-#if 0
-      /* This will keep the CAFs and the attached BCOs alive 
-       * but the values will have been reverted
-       */
-      scavengeEverything();
-#endif
   }
 #endif
 
@@ -368,18 +387,24 @@ void GarbageCollect(void (*get_roots)(void))
 
     /* scavenge each step in generations 0..maxgen */
     { 
-      int gen; 
+      int gen, st; 
+    loop2:
       for (gen = RtsFlags.GcFlags.generations-1; gen >= 0; gen--) {
-       for (s = 0; s < generations[gen].n_steps; s++) {
-         step = &generations[gen].steps[s];
+       for (st = generations[gen].n_steps-1; st >= 0 ; st--) {
+         if (gen == 0 && st == 0 && RtsFlags.GcFlags.generations > 1) { 
+           continue; 
+         }
+         step = &generations[gen].steps[st];
          evac_gen = gen;
          if (step->hp_bd != step->scan_bd || step->scan < step->hp) {
            scavenge(step);
            flag = rtsTrue;
+           goto loop2;
          }
          if (step->new_large_objects != NULL) {
            scavenge_large(step);
            flag = rtsTrue;
+           goto loop2;
          }
        }
       }
@@ -392,6 +417,18 @@ void GarbageCollect(void (*get_roots)(void))
     }
   }
 
+  /* Final traversal of the weak pointer list (see comment by
+   * cleanUpWeakPtrList below).
+   */
+  cleanup_weak_ptr_list(&weak_ptr_list);
+
+  /* Now see which stable names are still alive.
+   */
+  gcStablePtrTable(major_gc);
+
+  /* revert dead CAFs and update enteredCAFs list */
+  revert_dead_CAFs();
+  
   /* Set the maximum blocks for the oldest generation, based on twice
    * the amount of live data now, adjusted to fit the maximum heap
    * size if necessary.  
@@ -415,66 +452,11 @@ void GarbageCollect(void (*get_roots)(void))
        }
       }
     }
-  } else {
-    /* For a two-space collector, we need to resize the nursery. */
-
-    /* set up a new nursery.  Allocate a nursery size based on a
-     * function of the amount of live data (currently a factor of 2,
-     * should be configurable (ToDo)).  Use the blocks from the old
-     * nursery if possible, freeing up any left over blocks.
-     *
-     * If we get near the maximum heap size, then adjust our nursery
-     * size accordingly.  If the nursery is the same size as the live
-     * data (L), then we need 3L bytes.  We can reduce the size of the
-     * nursery to bring the required memory down near 2L bytes.
-     * 
-     * A normal 2-space collector would need 4L bytes to give the same
-     * performance we get from 3L bytes, reducing to the same
-     * performance at 2L bytes.  
-     */
-    nat blocks = g0s0->to_blocks;
-
-    if ( blocks * 4 > RtsFlags.GcFlags.maxHeapSize ) {
-      int adjusted_blocks;  /* signed on purpose */
-      int pc_free; 
-      
-      adjusted_blocks = (RtsFlags.GcFlags.maxHeapSize - 2 * blocks);
-      IF_DEBUG(gc, fprintf(stderr, "Near maximum heap size of 0x%x blocks, blocks = %d, adjusted to %d\n", RtsFlags.GcFlags.maxHeapSize, blocks, adjusted_blocks));
-      pc_free = adjusted_blocks * 100 / RtsFlags.GcFlags.maxHeapSize;
-      if (pc_free < RtsFlags.GcFlags.pcFreeHeap) /* might even be < 0 */ {
-       heapOverflow();
-      }
-      blocks = adjusted_blocks;
-      
-    } else {
-      blocks *= 2;
-      if (blocks < RtsFlags.GcFlags.minAllocAreaSize) {
-       blocks = RtsFlags.GcFlags.minAllocAreaSize;
-      }
-    }
-    
-    if (nursery_blocks < blocks) {
-      IF_DEBUG(gc, fprintf(stderr, "Increasing size of nursery to %d blocks\n", 
-                          blocks));
-      g0s0->blocks = allocNursery(g0s0->blocks, blocks-nursery_blocks);
-    } else {
-      bdescr *next_bd;
-      
-      IF_DEBUG(gc, fprintf(stderr, "Decreasing size of nursery to %d blocks\n", 
-                          blocks));
-      for (bd = g0s0->blocks; nursery_blocks > blocks; nursery_blocks--) {
-       next_bd = bd->link;
-       freeGroup(bd);
-       bd = next_bd;
-      }
-      g0s0->blocks = bd;
-    }
-
-    g0s0->n_blocks = nursery_blocks = blocks;
   }
 
   /* run through all the generations/steps and tidy up 
    */
+  copied = new_blocks * BLOCK_SIZE_W;
   for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
 
     if (g <= N) {
@@ -489,6 +471,11 @@ void GarbageCollect(void (*get_roots)(void))
        /* Tidy the end of the to-space chains */
        step->hp_bd->free = step->hp;
        step->hp_bd->link = NULL;
+       /* stats information: how much we copied */
+       if (g <= N) {
+         copied -= step->hp_bd->start + BLOCK_SIZE_W -
+           step->hp_bd->free;
+       }
       }
 
       /* for generations we collected... */
@@ -530,16 +517,16 @@ void GarbageCollect(void (*get_roots)(void))
         * between the maximum size of the oldest and youngest
         * generations.
         *
-        * max_blocks = alloc_area_size +  
-        *                 (oldgen_max_blocks - alloc_area_size) * G
-        *                 -----------------------------------------
-        *                              oldest_gen
+        * max_blocks =    oldgen_max_blocks * G
+        *                 ----------------------
+        *                      oldest_gen
         */
        if (g != 0) {
-         generations[g].max_blocks = 
-           RtsFlags.GcFlags.minAllocAreaSize +
-            (((oldest_gen->max_blocks - RtsFlags.GcFlags.minAllocAreaSize) * g)
-              / (RtsFlags.GcFlags.generations-1));
+#if 0
+         generations[g].max_blocks = (oldest_gen->max_blocks * g)
+              / (RtsFlags.GcFlags.generations-1);
+#endif
+         generations[g].max_blocks = oldest_gen->max_blocks;
        }
 
       /* for older generations... */
@@ -561,48 +548,123 @@ void GarbageCollect(void (*get_roots)(void))
     }
   }
   
+  /* Guess the amount of live data for stats. */
+  live = calcLive();
+
+  /* Free the small objects allocated via allocate(), since this will
+   * all have been copied into G0S1 now.  
+   */
+  if (small_alloc_list != NULL) {
+    freeChain(small_alloc_list);
+  }
+  small_alloc_list = NULL;
+  alloc_blocks = 0;
+  alloc_Hp = NULL;
+  alloc_HpLim = NULL;
+  alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
+
   /* Two-space collector:
    * Free the old to-space, and estimate the amount of live data.
    */
   if (RtsFlags.GcFlags.generations == 1) {
+    nat blocks;
+    
     if (old_to_space != NULL) {
       freeChain(old_to_space);
     }
     for (bd = g0s0->to_space; bd != NULL; bd = bd->link) {
       bd->evacuated = 0;       /* now from-space */
     }
-    live = g0s0->to_blocks * BLOCK_SIZE_W + 
-      ((lnat)g0s0->hp_bd->free - (lnat)g0s0->hp_bd->start) / sizeof(W_);
 
-  /* Generational collector:
-   * estimate the amount of live data.
-   */
+    /* For a two-space collector, we need to resize the nursery. */
+    
+    /* set up a new nursery.  Allocate a nursery size based on a
+     * function of the amount of live data (currently a factor of 2,
+     * should be configurable (ToDo)).  Use the blocks from the old
+     * nursery if possible, freeing up any left over blocks.
+     *
+     * If we get near the maximum heap size, then adjust our nursery
+     * size accordingly.  If the nursery is the same size as the live
+     * data (L), then we need 3L bytes.  We can reduce the size of the
+     * nursery to bring the required memory down near 2L bytes.
+     * 
+     * A normal 2-space collector would need 4L bytes to give the same
+     * performance we get from 3L bytes, reducing to the same
+     * performance at 2L bytes.  
+     */
+    blocks = g0s0->to_blocks;
+
+    if ( blocks * RtsFlags.GcFlags.oldGenFactor * 2 > 
+        RtsFlags.GcFlags.maxHeapSize ) {
+      int adjusted_blocks;  /* signed on purpose */
+      int pc_free; 
+      
+      adjusted_blocks = (RtsFlags.GcFlags.maxHeapSize - 2 * blocks);
+      IF_DEBUG(gc, fprintf(stderr, "Near maximum heap size of 0x%x blocks, blocks = %d, adjusted to %d\n", RtsFlags.GcFlags.maxHeapSize, blocks, adjusted_blocks));
+      pc_free = adjusted_blocks * 100 / RtsFlags.GcFlags.maxHeapSize;
+      if (pc_free < RtsFlags.GcFlags.pcFreeHeap) /* might even be < 0 */ {
+       heapOverflow();
+      }
+      blocks = adjusted_blocks;
+      
+    } else {
+      blocks *= RtsFlags.GcFlags.oldGenFactor;
+      if (blocks < RtsFlags.GcFlags.minAllocAreaSize) {
+       blocks = RtsFlags.GcFlags.minAllocAreaSize;
+      }
+    }
+    resizeNursery(blocks);
+    
   } else {
-    live = 0;
-    for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
-      for (s = 0; s < generations[g].n_steps; s++) {
-       /* approximate amount of live data (doesn't take into account slop
-        * at end of each block).  ToDo: this more accurately.
-        */
-       if (g == 0 && s == 0) { continue; }
-       step = &generations[g].steps[s];
-       live += step->n_blocks * BLOCK_SIZE_W + 
-         ((lnat)step->hp_bd->free -(lnat)step->hp_bd->start) / sizeof(W_);
+    /* Generational collector:
+     * If the user has given us a suggested heap size, adjust our
+     * allocation area to make best use of the memory available.
+     */
+
+    if (RtsFlags.GcFlags.heapSizeSuggestion) {
+      int blocks;
+      nat needed = calcNeeded();       /* approx blocks needed at next GC */
+
+      /* Guess how much will be live in generation 0 step 0 next time.
+       * A good approximation is the obtained by finding the
+       * percentage of g0s0 that was live at the last minor GC.
+       */
+      if (N == 0) {
+       g0s0_pcnt_kept = (new_blocks * 100) / g0s0->n_blocks;
       }
+
+      /* Estimate a size for the allocation area based on the
+       * information available.  We might end up going slightly under
+       * or over the suggested heap size, but we should be pretty
+       * close on average.
+       *
+       * Formula:            suggested - needed
+       *                ----------------------------
+       *                    1 + g0s0_pcnt_kept/100
+       *
+       * where 'needed' is the amount of memory needed at the next
+       * collection for collecting all steps except g0s0.
+       */
+      blocks = 
+       (((int)RtsFlags.GcFlags.heapSizeSuggestion - (int)needed) * 100) /
+       (100 + (int)g0s0_pcnt_kept);
+      
+      if (blocks < (int)RtsFlags.GcFlags.minAllocAreaSize) {
+       blocks = RtsFlags.GcFlags.minAllocAreaSize;
+      }
+      
+      resizeNursery((nat)blocks);
     }
   }
 
-  /* revert dead CAFs and update enteredCAFs list */
-  revertDeadCAFs();
-  
-  /* mark the garbage collected CAFs as dead */
+ /* mark the garbage collected CAFs as dead */
 #ifdef DEBUG
   if (major_gc) { gcCAFs(); }
 #endif
   
   /* zero the scavenged static object list */
   if (major_gc) {
-    zeroStaticObjectList(scavenged_static_objects);
+    zero_static_object_list(scavenged_static_objects);
   }
 
   /* Reset the nursery
@@ -611,46 +673,17 @@ void GarbageCollect(void (*get_roots)(void))
     bd->free = bd->start;
     ASSERT(bd->gen == g0);
     ASSERT(bd->step == g0s0);
+    IF_DEBUG(sanity,memset(bd->start, 0xaa, BLOCK_SIZE));
   }
   current_nursery = g0s0->blocks;
 
-  /* Free the small objects allocated via allocate(), since this will
-   * all have been copied into G0S1 now.  
-   */
-  if (small_alloc_list != NULL) {
-    freeChain(small_alloc_list);
-  }
-  small_alloc_list = NULL;
-  alloc_blocks = 0;
-  alloc_blocks_lim = RtsFlags.GcFlags.minAllocAreaSize;
-
-  /* start any pending finalisers */
-  scheduleFinalisers(old_weak_ptr_list);
+  /* start any pending finalizers */
+  scheduleFinalizers(old_weak_ptr_list);
   
   /* check sanity after GC */
-#ifdef DEBUG
-  if (RtsFlags.GcFlags.generations == 1) {
-    IF_DEBUG(sanity, checkHeap(g0s0->to_space, NULL));
-    IF_DEBUG(sanity, checkChain(g0s0->large_objects));
-  } else {
-
-    for (g = 0; g <= N; g++) {
-      for (s = 0; s < generations[g].n_steps; s++) {
-       if (g == 0 && s == 0) { continue; }
-       IF_DEBUG(sanity, checkHeap(generations[g].steps[s].blocks, NULL));
-      }
-    }
-    for (g = N+1; g < RtsFlags.GcFlags.generations; g++) {
-      for (s = 0; s < generations[g].n_steps; s++) {
-       IF_DEBUG(sanity, checkHeap(generations[g].steps[s].blocks,
-                                  generations[g].steps[s].blocks->start));
-       IF_DEBUG(sanity, checkChain(generations[g].steps[s].large_objects));
-      }
-    }
-    IF_DEBUG(sanity, checkFreeListSanity());
-  }
-#endif
+  IF_DEBUG(sanity, checkSanity(N));
 
+  /* extra GC trace info */
   IF_DEBUG(gc, stat_describe_gens());
 
 #ifdef DEBUG
@@ -667,7 +700,7 @@ void GarbageCollect(void (*get_roots)(void))
   IF_DEBUG(sanity, memInventory());
 
   /* ok, GC over: tell the stats department what happened. */
-  stat_endGC(allocated, collected, live, N);
+  stat_endGC(allocated, collected, live, copied, N);
 }
 
 /* -----------------------------------------------------------------------------
@@ -684,7 +717,7 @@ void GarbageCollect(void (*get_roots)(void))
    new live weak pointers, then all the currently unreachable ones are
    dead.
 
-   For generational GC: we just don't try to finalise weak pointers in
+   For generational GC: we just don't try to finalize weak pointers in
    older generations than the one we're collecting.  This could
    probably be optimised by keeping per-generation lists of weak
    pointers, but for a few weak pointers this scheme will work.
@@ -694,80 +727,70 @@ static rtsBool
 traverse_weak_ptr_list(void)
 {
   StgWeak *w, **last_w, *next_w;
-  StgClosure *target;
-  const StgInfoTable *info;
+  StgClosure *new;
   rtsBool flag = rtsFalse;
 
   if (weak_done) { return rtsFalse; }
 
-  /* doesn't matter where we evacuate values/finalisers to, since
+  /* doesn't matter where we evacuate values/finalizers to, since
    * these pointers are treated as roots (iff the keys are alive).
    */
   evac_gen = 0;
 
   last_w = &old_weak_ptr_list;
   for (w = old_weak_ptr_list; w; w = next_w) {
-    target = w->key;
-  loop:
-    /* ignore weak pointers in older generations */
-    if (!LOOKS_LIKE_STATIC(target) && Bdescr((P_)target)->gen->no > N) {
-      IF_DEBUG(weak, fprintf(stderr,"Weak pointer still alive (in old gen) at %p\n", w));
-      /* remove this weak ptr from the old_weak_ptr list */
-      *last_w = w->link;
-      /* and put it on the new weak ptr list */
-      next_w  = w->link;
-      w->link = weak_ptr_list;
-      weak_ptr_list = w;
-      flag = rtsTrue;
+
+    /* First, this weak pointer might have been evacuated.  If so,
+     * remove the forwarding pointer from the weak_ptr_list.
+     */
+    if (get_itbl(w)->type == EVACUATED) {
+      w = (StgWeak *)((StgEvacuated *)w)->evacuee;
+      *last_w = w;
+    }
+
+    /* There might be a DEAD_WEAK on the list if finalizeWeak# was
+     * called on a live weak pointer object.  Just remove it.
+     */
+    if (w->header.info == &DEAD_WEAK_info) {
+      next_w = ((StgDeadWeak *)w)->link;
+      *last_w = next_w;
       continue;
     }
 
-    info = get_itbl(target);
-    switch (info->type) {
-      
-    case IND:
-    case IND_STATIC:
-    case IND_PERM:
-    case IND_OLDGEN:           /* rely on compatible layout with StgInd */
-    case IND_OLDGEN_PERM:
-      /* follow indirections */
-      target = ((StgInd *)target)->indirectee;
-      goto loop;
+    ASSERT(get_itbl(w)->type == WEAK);
 
-    case EVACUATED:
-      /* If key is alive, evacuate value and finaliser and 
-       * place weak ptr on new weak ptr list.
-       */
-      IF_DEBUG(weak, fprintf(stderr,"Weak pointer still alive at %p\n", w));
-      w->key = ((StgEvacuated *)target)->evacuee;
+    /* Now, check whether the key is reachable.
+     */
+    if ((new = isAlive(w->key))) {
+      w->key = new;
+      /* evacuate the value and finalizer */
       w->value = evacuate(w->value);
-      w->finaliser = evacuate(w->finaliser);
-      
+      w->finalizer = evacuate(w->finalizer);
       /* remove this weak ptr from the old_weak_ptr list */
       *last_w = w->link;
-
       /* and put it on the new weak ptr list */
       next_w  = w->link;
       w->link = weak_ptr_list;
       weak_ptr_list = w;
       flag = rtsTrue;
-      break;
-
-    default:                   /* key is dead */
+      IF_DEBUG(weak, fprintf(stderr,"Weak pointer still alive at %p -> %p\n", w, w->key));
+      continue;
+    }
+    else {
       last_w = &(w->link);
       next_w = w->link;
-      break;
+      continue;
     }
   }
   
   /* If we didn't make any changes, then we can go round and kill all
    * the dead weak pointers.  The old_weak_ptr list is used as a list
-   * of pending finalisers later on.
+   * of pending finalizers later on.
    */
   if (flag == rtsFalse) {
+    cleanup_weak_ptr_list(&old_weak_ptr_list);
     for (w = old_weak_ptr_list; w; w = w->link) {
-      w->value = evacuate(w->value);
-      w->finaliser = evacuate(w->finaliser);
+      w->finalizer = evacuate(w->finalizer);
     }
     weak_done = rtsTrue;
   }
@@ -775,14 +798,93 @@ traverse_weak_ptr_list(void)
   return rtsTrue;
 }
 
+/* -----------------------------------------------------------------------------
+   After GC, the live weak pointer list may have forwarding pointers
+   on it, because a weak pointer object was evacuated after being
+   moved to the live weak pointer list.  We remove those forwarding
+   pointers here.
+
+   Also, we don't consider weak pointer objects to be reachable, but
+   we must nevertheless consider them to be "live" and retain them.
+   Therefore any weak pointer objects which haven't as yet been
+   evacuated need to be evacuated now.
+   -------------------------------------------------------------------------- */
+
+static void
+cleanup_weak_ptr_list ( StgWeak **list )
+{
+  StgWeak *w, **last_w;
+
+  last_w = list;
+  for (w = *list; w; w = w->link) {
+
+    if (get_itbl(w)->type == EVACUATED) {
+      w = (StgWeak *)((StgEvacuated *)w)->evacuee;
+      *last_w = w;
+    }
+
+    if (Bdescr((P_)w)->evacuated == 0) {
+      (StgClosure *)w = evacuate((StgClosure *)w);
+      *last_w = w;
+    }
+    last_w = &(w->link);
+  }
+}
+
+/* -----------------------------------------------------------------------------
+   isAlive determines whether the given closure is still alive (after
+   a garbage collection) or not.  It returns the new address of the
+   closure if it is alive, or NULL otherwise.
+   -------------------------------------------------------------------------- */
+
+StgClosure *
+isAlive(StgClosure *p)
+{
+  StgInfoTable *info;
+
+  while (1) {
+
+    info = get_itbl(p);
+
+    /* ToDo: for static closures, check the static link field.
+     * Problem here is that we sometimes don't set the link field, eg.
+     * for static closures with an empty SRT or CONSTR_STATIC_NOCAFs.
+     */
+
+    /* ignore closures in generations that we're not collecting. */
+    if (LOOKS_LIKE_STATIC(p) || Bdescr((P_)p)->gen->no > N) {
+      return p;
+    }
+    
+    switch (info->type) {
+      
+    case IND:
+    case IND_STATIC:
+    case IND_PERM:
+    case IND_OLDGEN:           /* rely on compatible layout with StgInd */
+    case IND_OLDGEN_PERM:
+      /* follow indirections */
+      p = ((StgInd *)p)->indirectee;
+      continue;
+      
+    case EVACUATED:
+      /* alive! */
+      return ((StgEvacuated *)p)->evacuee;
+
+    default:
+      /* dead. */
+      return NULL;
+    }
+  }
+}
+
 StgClosure *
 MarkRoot(StgClosure *root)
 {
-  root = evacuate(root);
-  return root;
+  return evacuate(root);
 }
 
-static inline void addBlock(step *step)
+static void addBlock(step *step)
 {
   bdescr *bd = allocBlock();
   bd->gen = step->gen;
@@ -800,22 +902,33 @@ static inline void addBlock(step *step)
   step->hpLim = step->hp + BLOCK_SIZE_W;
   step->hp_bd = bd;
   step->to_blocks++;
+  new_blocks++;
+}
+
+static __inline__ void 
+upd_evacuee(StgClosure *p, StgClosure *dest)
+{
+  p->header.info = &EVACUATED_info;
+  ((StgEvacuated *)p)->evacuee = dest;
 }
 
 static __inline__ StgClosure *
-copy(StgClosure *src, nat size, bdescr *bd)
+copy(StgClosure *src, nat size, step *step)
 {
-  step *step;
   P_ to, from, dest;
 
+  TICK_GC_WORDS_COPIED(size);
   /* Find out where we're going, using the handy "to" pointer in 
    * the step of the source object.  If it turns out we need to
    * evacuate to an older generation, adjust it here (see comment
    * by evacuate()).
    */
-  step = bd->step->to;
   if (step->gen->no < evac_gen) {
+#ifdef NO_EAGER_PROMOTION    
+    failed_to_evac = rtsTrue;
+#else
     step = &generations[evac_gen].steps[0];
+#endif
   }
 
   /* chain a new block onto the to-space for the destination step if
@@ -825,11 +938,13 @@ copy(StgClosure *src, nat size, bdescr *bd)
     addBlock(step);
   }
 
-  dest = step->hp;
-  step->hp += size;
-  for(to = dest, from = (P_)src; size>0; --size) {
+  for(to = step->hp, from = (P_)src; size>0; --size) {
     *to++ = *from++;
   }
+
+  dest = step->hp;
+  step->hp = to;
+  upd_evacuee(src,(StgClosure *)dest);
   return (StgClosure *)dest;
 }
 
@@ -839,57 +954,33 @@ copy(StgClosure *src, nat size, bdescr *bd)
  */
 
 static __inline__ StgClosure *
-copyPart(StgClosure *src, nat size_to_reserve, nat size_to_copy, bdescr *bd)
+copyPart(StgClosure *src, nat size_to_reserve, nat size_to_copy, step *step)
 {
-  step *step;
   P_ dest, to, from;
 
-  step = bd->step->to;
+  TICK_GC_WORDS_COPIED(size_to_copy);
   if (step->gen->no < evac_gen) {
+#ifdef NO_EAGER_PROMOTION    
+    failed_to_evac = rtsTrue;
+#else
     step = &generations[evac_gen].steps[0];
+#endif
   }
 
   if (step->hp + size_to_reserve >= step->hpLim) {
     addBlock(step);
   }
 
-  dest = step->hp;
-  step->hp += size_to_reserve;
-  for(to = dest, from = (P_)src; size_to_copy>0; --size_to_copy) {
+  for(to = step->hp, from = (P_)src; size_to_copy>0; --size_to_copy) {
     *to++ = *from++;
   }
   
+  dest = step->hp;
+  step->hp += size_to_reserve;
+  upd_evacuee(src,(StgClosure *)dest);
   return (StgClosure *)dest;
 }
 
-static __inline__ void 
-upd_evacuee(StgClosure *p, StgClosure *dest)
-{
-  StgEvacuated *q = (StgEvacuated *)p;
-
-  SET_INFO(q,&EVACUATED_info);
-  q->evacuee = dest;
-}
-
-/* -----------------------------------------------------------------------------
-   Evacuate a mutable object
-   
-   If we evacuate a mutable object to an old generation, cons the
-   object onto the older generation's mutable list.
-   -------------------------------------------------------------------------- */
-   
-static inline void
-evacuate_mutable(StgMutClosure *c)
-{
-  bdescr *bd;
-  
-  bd = Bdescr((P_)c);
-  if (bd->gen->no > 0) {
-    c->mut_link = bd->gen->mut_list;
-    bd->gen->mut_list = c;
-  }
-}
-
 /* -----------------------------------------------------------------------------
    Evacuate a large object
 
@@ -917,6 +1008,7 @@ evacuate_large(StgPtr p, rtsBool mutable)
      */
     if (bd->gen->no < evac_gen) {
       failed_to_evac = rtsTrue;
+      TICK_GC_FAILED_PROMOTION();
     }
     return;
   }
@@ -936,7 +1028,11 @@ evacuate_large(StgPtr p, rtsBool mutable)
    */
   step = bd->step->to;
   if (step->gen->no < evac_gen) {
+#ifdef NO_EAGER_PROMOTION    
+    failed_to_evac = rtsTrue;
+#else
     step = &generations[evac_gen].steps[0];
+#endif
   }
 
   bd->step = step;
@@ -946,7 +1042,7 @@ evacuate_large(StgPtr p, rtsBool mutable)
   bd->evacuated = 1;
 
   if (mutable) {
-    evacuate_mutable((StgMutClosure *)p);
+    recordMutable((StgMutClosure *)p);
   }
 }
 
@@ -978,7 +1074,7 @@ mkMutCons(StgClosure *ptr, generation *gen)
 
   SET_HDR(q,&MUT_CONS_info,CCS_GC);
   q->var = ptr;
-  evacuate_mutable((StgMutClosure *)q);
+  recordOldToNewPtrs((StgMutClosure *)q);
 
   return (StgClosure *)q;
 }
@@ -1014,10 +1110,11 @@ evacuate(StgClosure *q)
 {
   StgClosure *to;
   bdescr *bd = NULL;
+  step *step;
   const StgInfoTable *info;
 
 loop:
-  if (!LOOKS_LIKE_STATIC(q)) {
+  if (HEAP_ALLOCED(q)) {
     bd = Bdescr((P_)q);
     if (bd->gen->no > N) {
       /* Can't evacuate this object, because it's in a generation
@@ -1027,9 +1124,11 @@ loop:
       if (bd->gen->no < evac_gen) {
        /* nope */
        failed_to_evac = rtsTrue;
+       TICK_GC_FAILED_PROMOTION();
       }
       return q;
     }
+    step = bd->step->to;
   }
 
   /* make sure the info pointer is into text space */
@@ -1040,17 +1139,43 @@ loop:
   switch (info -> type) {
 
   case BCO:
-    to = copy(q,bco_sizeW(stgCast(StgBCO*,q)),bd);
-    upd_evacuee(q,to);
-    return to;
+    return copy(q,bco_sizeW(stgCast(StgBCO*,q)),step);
 
   case MUT_VAR:
+    ASSERT(q->header.info != &MUT_CONS_info);
   case MVAR:
-    to = copy(q,sizeW_fromITBL(info),bd);
-    upd_evacuee(q,to);
-    evacuate_mutable((StgMutClosure *)to);
+    to = copy(q,sizeW_fromITBL(info),step);
+    recordMutable((StgMutClosure *)to);
     return to;
 
+  case FUN_1_0:
+  case FUN_0_1:
+  case CONSTR_1_0:
+  case CONSTR_0_1:
+    return copy(q,sizeofW(StgHeader)+1,step);
+
+  case THUNK_1_0:              /* here because of MIN_UPD_SIZE */
+  case THUNK_0_1:
+  case THUNK_1_1:
+  case THUNK_0_2:
+  case THUNK_2_0:
+#ifdef NO_PROMOTE_THUNKS
+    if (bd->gen->no == 0 && 
+       bd->step->no != 0 &&
+       bd->step->no == bd->gen->n_steps-1) {
+      step = bd->step;
+    }
+#endif
+    return copy(q,sizeofW(StgHeader)+2,step);
+
+  case FUN_1_1:
+  case FUN_0_2:
+  case FUN_2_0:
+  case CONSTR_1_1:
+  case CONSTR_0_2:
+  case CONSTR_2_0:
+    return copy(q,sizeofW(StgHeader)+2,step);
+
   case FUN:
   case THUNK:
   case CONSTR:
@@ -1060,20 +1185,16 @@ loop:
   case CAF_ENTERED:
   case WEAK:
   case FOREIGN:
-    to = copy(q,sizeW_fromITBL(info),bd);
-    upd_evacuee(q,to);
-    return to;
+  case STABLE_NAME:
+    return copy(q,sizeW_fromITBL(info),step);
 
   case CAF_BLACKHOLE:
   case BLACKHOLE:
-    to = copyPart(q,BLACKHOLE_sizeW(),sizeofW(StgHeader),bd);
-    upd_evacuee(q,to);
-    return to;
+    return copyPart(q,BLACKHOLE_sizeW(),sizeofW(StgHeader),step);
 
   case BLACKHOLE_BQ:
-    to = copy(q,BLACKHOLE_sizeW(),bd); 
-    upd_evacuee(q,to);
-    evacuate_mutable((StgMutClosure *)to);
+    to = copy(q,BLACKHOLE_sizeW(),step); 
+    recordMutable((StgMutClosure *)to);
     return to;
 
   case THUNK_SELECTOR:
@@ -1085,13 +1206,18 @@ loop:
       selectee_info = get_itbl(selectee);
       switch (selectee_info->type) {
       case CONSTR:
+      case CONSTR_1_0:
+      case CONSTR_0_1:
+      case CONSTR_2_0:
+      case CONSTR_1_1:
+      case CONSTR_0_2:
       case CONSTR_STATIC:
        { 
-         StgNat32 offset = info->layout.selector_offset;
+         StgWord32 offset = info->layout.selector_offset;
 
          /* check that the size is in range */
          ASSERT(offset < 
-                (StgNat32)(selectee_info->layout.payload.ptrs + 
+                (StgWord32)(selectee_info->layout.payload.ptrs + 
                            selectee_info->layout.payload.nptrs));
 
          /* perform the selection! */
@@ -1101,11 +1227,12 @@ loop:
           * with the evacuation, just update the source address with
           * a pointer to the (evacuated) constructor field.
           */
-         if (IS_USER_PTR(q)) {
+         if (HEAP_ALLOCED(q)) {
            bdescr *bd = Bdescr((P_)q);
            if (bd->evacuated) {
              if (bd->gen->no < evac_gen) {
                failed_to_evac = rtsTrue;
+               TICK_GC_FAILED_PROMOTION();
              }
              return q;
            }
@@ -1134,6 +1261,11 @@ loop:
        goto selector_loop;
 
       case THUNK:
+      case THUNK_1_0:
+      case THUNK_0_1:
+      case THUNK_2_0:
+      case THUNK_1_1:
+      case THUNK_0_2:
       case THUNK_STATIC:
       case THUNK_SELECTOR:
        /* aargh - do recursively???? */
@@ -1148,9 +1280,7 @@ loop:
        barf("evacuate: THUNK_SELECTOR: strange selectee");
       }
     }
-    to = copy(q,THUNK_SELECTOR_sizeW(),bd);
-    upd_evacuee(q,to);
-    return to;
+    return copy(q,THUNK_SELECTOR_sizeW(),step);
 
   case IND:
   case IND_OLDGEN:
@@ -1158,30 +1288,35 @@ loop:
     q = ((StgInd*)q)->indirectee;
     goto loop;
 
-    /* ToDo: optimise STATIC_LINK for known cases.
-       - FUN_STATIC       : payload[0]
-       - THUNK_STATIC     : payload[1]
-       - IND_STATIC       : payload[1]
-    */
   case THUNK_STATIC:
+    if (info->srt_len > 0 && major_gc && 
+       THUNK_STATIC_LINK((StgClosure *)q) == NULL) {
+      THUNK_STATIC_LINK((StgClosure *)q) = static_objects;
+      static_objects = (StgClosure *)q;
+    }
+    return q;
+
   case FUN_STATIC:
-    if (info->srt_len == 0) {  /* small optimisation */
-      return q;
+    if (info->srt_len > 0 && major_gc && 
+       FUN_STATIC_LINK((StgClosure *)q) == NULL) {
+      FUN_STATIC_LINK((StgClosure *)q) = static_objects;
+      static_objects = (StgClosure *)q;
     }
-    /* fall through */
-  case CONSTR_STATIC:
+    return q;
+
   case IND_STATIC:
-    /* don't want to evacuate these, but we do want to follow pointers
-     * from SRTs  - see scavenge_static.
-     */
+    if (major_gc && IND_STATIC_LINK((StgClosure *)q) == NULL) {
+      IND_STATIC_LINK((StgClosure *)q) = static_objects;
+      static_objects = (StgClosure *)q;
+    }
+    return q;
 
-    /* put the object on the static list, if necessary.
-     */
+  case CONSTR_STATIC:
     if (major_gc && STATIC_LINK(info,(StgClosure *)q) == NULL) {
       STATIC_LINK(info,(StgClosure *)q) = static_objects;
       static_objects = (StgClosure *)q;
     }
-    /* fall through */
+    return q;
 
   case CONSTR_INTLIKE:
   case CONSTR_CHARLIKE:
@@ -1208,9 +1343,7 @@ loop:
   case PAP:
     /* these are special - the payload is a copy of a chunk of stack,
        tagging and all. */
-    to = copy(q,pap_sizeW(stgCast(StgPAP*,q)),bd);
-    upd_evacuee(q,to);
-    return to;
+    return copy(q,pap_sizeW(stgCast(StgPAP*,q)),step);
 
   case EVACUATED:
     /* Already evacuated, just return the forwarding address.
@@ -1225,11 +1358,11 @@ loop:
       if (Bdescr((P_)p)->gen->no < evac_gen) {
        /*      fprintf(stderr,"evac failed!\n");*/
        failed_to_evac = rtsTrue;
-      } 
+       TICK_GC_FAILED_PROMOTION();
+      }
     }
     return ((StgEvacuated*)q)->evacuee;
 
-  case MUT_ARR_WORDS:
   case ARR_WORDS:
     {
       nat size = arr_words_sizeW(stgCast(StgArrWords*,q)); 
@@ -1239,9 +1372,7 @@ loop:
        return q;
       } else {
        /* just copy the block */
-       to = copy(q,size,bd);
-       upd_evacuee(q,to);
-       return to;
+       return copy(q,size,step);
       }
     }
 
@@ -1255,10 +1386,9 @@ loop:
        to = q;
       } else {
        /* just copy the block */
-       to = copy(q,size,bd);
-       upd_evacuee(q,to);
+       to = copy(q,size,step);
        if (info->type == MUT_ARR_PTRS) {
-         evacuate_mutable((StgMutClosure *)to);
+         recordMutable((StgMutClosure *)to);
        }
       }
       return to;
@@ -1280,7 +1410,7 @@ loop:
        * list it contains.  
        */
       } else {
-       StgTSO *new_tso = (StgTSO *)copy((StgClosure *)tso,tso_sizeW(tso),bd);
+       StgTSO *new_tso = (StgTSO *)copy((StgClosure *)tso,tso_sizeW(tso),step);
 
        diff = (StgPtr)new_tso - (StgPtr)tso; /* In *words* */
 
@@ -1290,9 +1420,8 @@ loop:
        new_tso->splim = (StgPtr)new_tso->splim + diff;
        
        relocate_TSO(tso, new_tso);
-       upd_evacuee(q,(StgClosure *)new_tso);
 
-       evacuate_mutable((StgMutClosure *)new_tso);
+       recordMutable((StgMutClosure *)new_tso);
        return (StgClosure *)new_tso;
       }
     }
@@ -1373,7 +1502,24 @@ scavenge_srt(const StgInfoTable *info)
   srt = stgCast(StgClosure **,info->srt);
   srt_end = srt + info->srt_len;
   for (; srt < srt_end; srt++) {
-    evacuate(*srt);
+    /* Special-case to handle references to closures hiding out in DLLs, since
+       double indirections required to get at those. The code generator knows
+       which is which when generating the SRT, so it stores the (indirect)
+       reference to the DLL closure in the table by first adding one to it.
+       We check for this here, and undo the addition before evacuating it.
+
+       If the SRT entry hasn't got bit 0 set, the SRT entry points to a
+       closure that's fixed at link-time, and no extra magic is required.
+    */
+#ifdef HAVE_WIN32_DLL_SUPPORT
+    if ( stgCast(unsigned long,*srt) & 0x1 ) {
+       evacuate(*stgCast(StgClosure**,(stgCast(unsigned long, *srt) & ~0x1)));
+    } else {
+       evacuate(*srt);
+    }
+#else
+       evacuate(*srt);
+#endif
   }
 }
 
@@ -1451,6 +1597,54 @@ scavenge(step *step)
        break;
       }
 
+    case THUNK_2_0:
+    case FUN_2_0:
+      scavenge_srt(info);
+    case CONSTR_2_0:
+      ((StgClosure *)p)->payload[1] = evacuate(((StgClosure *)p)->payload[1]);
+      ((StgClosure *)p)->payload[0] = evacuate(((StgClosure *)p)->payload[0]);
+      p += sizeofW(StgHeader) + 2;
+      break;
+
+    case THUNK_1_0:
+      scavenge_srt(info);
+      ((StgClosure *)p)->payload[0] = evacuate(((StgClosure *)p)->payload[0]);
+      p += sizeofW(StgHeader) + 2; /* MIN_UPD_SIZE */
+      break;
+
+    case FUN_1_0:
+      scavenge_srt(info);
+    case CONSTR_1_0:
+      ((StgClosure *)p)->payload[0] = evacuate(((StgClosure *)p)->payload[0]);
+      p += sizeofW(StgHeader) + 1;
+      break;
+
+    case THUNK_0_1:
+      scavenge_srt(info);
+      p += sizeofW(StgHeader) + 2; /* MIN_UPD_SIZE */
+      break;
+
+    case FUN_0_1:
+      scavenge_srt(info);
+    case CONSTR_0_1:
+      p += sizeofW(StgHeader) + 1;
+      break;
+
+    case THUNK_0_2:
+    case FUN_0_2:
+      scavenge_srt(info);
+    case CONSTR_0_2:
+      p += sizeofW(StgHeader) + 2;
+      break;
+
+    case THUNK_1_1:
+    case FUN_1_1:
+      scavenge_srt(info);
+    case CONSTR_1_1:
+      ((StgClosure *)p)->payload[0] = evacuate(((StgClosure *)p)->payload[0]);
+      p += sizeofW(StgHeader) + 2;
+      break;
+
     case FUN:
     case THUNK:
       scavenge_srt(info);
@@ -1459,10 +1653,7 @@ scavenge(step *step)
     case CONSTR:
     case WEAK:
     case FOREIGN:
-    case IND_PERM:
-    case IND_OLDGEN_PERM:
-    case CAF_UNENTERED:
-    case CAF_ENTERED:
+    case STABLE_NAME:
       {
        StgPtr end;
 
@@ -1474,6 +1665,52 @@ scavenge(step *step)
        break;
       }
 
+    case IND_PERM:
+      if (step->gen->no != 0) {
+       SET_INFO(((StgClosure *)p), &IND_OLDGEN_PERM_info);
+      }
+      /* fall through */
+    case IND_OLDGEN_PERM:
+      ((StgIndOldGen *)p)->indirectee = 
+       evacuate(((StgIndOldGen *)p)->indirectee);
+      if (failed_to_evac) {
+       failed_to_evac = rtsFalse;
+       recordOldToNewPtrs((StgMutClosure *)p);
+      }
+      p += sizeofW(StgIndOldGen);
+      break;
+
+    case CAF_UNENTERED:
+      {
+       StgCAF *caf = (StgCAF *)p;
+
+       caf->body = evacuate(caf->body);
+       if (failed_to_evac) {
+         failed_to_evac = rtsFalse;
+         recordOldToNewPtrs((StgMutClosure *)p);
+       } else {
+         caf->mut_link = NULL;
+       }
+        p += sizeofW(StgCAF);
+       break;
+      }
+
+    case CAF_ENTERED:
+      {
+       StgCAF *caf = (StgCAF *)p;
+
+       caf->body = evacuate(caf->body);
+       caf->value = evacuate(caf->value);
+       if (failed_to_evac) {
+         failed_to_evac = rtsFalse;
+         recordOldToNewPtrs((StgMutClosure *)p);
+       } else {
+         caf->mut_link = NULL;
+       }
+        p += sizeofW(StgCAF);
+       break;
+      }
+
     case MUT_VAR:
       /* ignore MUT_CONSs */
       if (((StgMutVar *)p)->header.info != &MUT_CONS_info) {
@@ -1496,7 +1733,7 @@ scavenge(step *step)
          evacuate((StgClosure *)bh->blocking_queue);
        if (failed_to_evac) {
          failed_to_evac = rtsFalse;
-         evacuate_mutable((StgMutClosure *)bh);
+         recordMutable((StgMutClosure *)bh);
        }
        p += BLACKHOLE_sizeW();
        break;
@@ -1552,7 +1789,6 @@ scavenge(step *step)
       }
       
     case ARR_WORDS:
-    case MUT_ARR_WORDS:
       /* nothing to follow */
       p += arr_words_sizeW(stgCast(StgArrWords*,p));
       break;
@@ -1582,7 +1818,7 @@ scavenge(step *step)
        }
        if (failed_to_evac) {
          /* we can do this easier... */
-         evacuate_mutable((StgMutClosure *)start);
+         recordMutable((StgMutClosure *)start);
          failed_to_evac = rtsFalse;
        }
        break;
@@ -1596,6 +1832,9 @@ scavenge(step *step)
        evac_gen = 0;
        /* chase the link field for any TSOs on the same queue */
        (StgClosure *)tso->link = evacuate((StgClosure *)tso->link);
+       if (tso->blocked_on) {
+         tso->blocked_on = evacuate(tso->blocked_on);
+       }
        /* scavenge this thread's stack */
        scavenge_stack(tso->sp, &(tso->stack[tso->stack_size]));
        evac_gen = saved_evac_gen;
@@ -1634,33 +1873,47 @@ scavenge(step *step)
    objects can have this property.
    -------------------------------------------------------------------------- */
 static rtsBool
-scavenge_one(StgPtr p)
+scavenge_one(StgClosure *p)
 {
   StgInfoTable *info;
   rtsBool no_luck;
 
-  ASSERT(p && (LOOKS_LIKE_GHC_INFO(GET_INFO((StgClosure *)p))
-              || IS_HUGS_CONSTR_INFO(GET_INFO((StgClosure *)p))));
+  ASSERT(p && (LOOKS_LIKE_GHC_INFO(GET_INFO(p))
+              || IS_HUGS_CONSTR_INFO(GET_INFO(p))));
 
-  info = get_itbl((StgClosure *)p);
+  info = get_itbl(p);
 
   switch (info -> type) {
 
   case FUN:
+  case FUN_1_0:                        /* hardly worth specialising these guys */
+  case FUN_0_1:
+  case FUN_1_1:
+  case FUN_0_2:
+  case FUN_2_0:
   case THUNK:
+  case THUNK_1_0:
+  case THUNK_0_1:
+  case THUNK_1_1:
+  case THUNK_0_2:
+  case THUNK_2_0:
   case CONSTR:
+  case CONSTR_1_0:
+  case CONSTR_0_1:
+  case CONSTR_1_1:
+  case CONSTR_0_2:
+  case CONSTR_2_0:
   case WEAK:
   case FOREIGN:
   case IND_PERM:
   case IND_OLDGEN_PERM:
   case CAF_UNENTERED:
-  case CAF_ENTERED:
     {
-      StgPtr end;
+      StgPtr q, end;
       
-      end = (P_)((StgClosure *)p)->payload + info->layout.payload.ptrs;
-      for (p = (P_)((StgClosure *)p)->payload; p < end; p++) {
-       (StgClosure *)*p = evacuate((StgClosure *)*p);
+      end = (P_)p->payload + info->layout.payload.ptrs;
+      for (q = (P_)p->payload; q < end; q++) {
+       (StgClosure *)*q = evacuate((StgClosure *)*q);
       }
       break;
     }
@@ -1673,7 +1926,7 @@ scavenge_one(StgPtr p)
     { 
       StgSelector *s = (StgSelector *)p;
       s->selectee = evacuate(s->selectee);
-       break;
+      break;
     }
     
   case AP_UPD: /* same as PAPs */
@@ -1682,7 +1935,7 @@ scavenge_one(StgPtr p)
      * evacuate the function pointer too...
      */
     { 
-      StgPAP* pap = stgCast(StgPAP*,p);
+      StgPAP* pap = (StgPAP *)p;
       
       pap->fun = evacuate(pap->fun);
       scavenge_stack((P_)pap->payload, (P_)pap->payload + pap->n_args);
@@ -1715,21 +1968,150 @@ scavenge_one(StgPtr p)
    remove non-mutable objects from the mutable list at this point.
    -------------------------------------------------------------------------- */
 
-static StgMutClosure *
-scavenge_mutable_list(StgMutClosure *p, nat gen)
+static void
+scavenge_mut_once_list(generation *gen)
 {
   StgInfoTable *info;
-  StgMutClosure *start;
-  StgMutClosure **prev;
+  StgMutClosure *p, *next, *new_list;
 
-  evac_gen = 0;
+  p = gen->mut_once_list;
+  new_list = END_MUT_LIST;
+  next = p->mut_link;
+
+  evac_gen = gen->no;
+  failed_to_evac = rtsFalse;
 
-  prev = &start;
-  start = p;
+  for (; p != END_MUT_LIST; p = next, next = p->mut_link) {
 
+    /* make sure the info pointer is into text space */
+    ASSERT(p && (LOOKS_LIKE_GHC_INFO(GET_INFO(p))
+                || IS_HUGS_CONSTR_INFO(GET_INFO(p))));
+    
+    info = get_itbl(p);
+    switch(info->type) {
+      
+    case IND_OLDGEN:
+    case IND_OLDGEN_PERM:
+    case IND_STATIC:
+      /* Try to pull the indirectee into this generation, so we can
+       * remove the indirection from the mutable list.  
+       */
+      ((StgIndOldGen *)p)->indirectee = 
+        evacuate(((StgIndOldGen *)p)->indirectee);
+      
+#if 0
+      /* Debugging code to print out the size of the thing we just
+       * promoted 
+       */
+      { 
+       StgPtr start = gen->steps[0].scan;
+       bdescr *start_bd = gen->steps[0].scan_bd;
+       nat size = 0;
+       scavenge(&gen->steps[0]);
+       if (start_bd != gen->steps[0].scan_bd) {
+         size += (P_)BLOCK_ROUND_UP(start) - start;
+         start_bd = start_bd->link;
+         while (start_bd != gen->steps[0].scan_bd) {
+           size += BLOCK_SIZE_W;
+           start_bd = start_bd->link;
+         }
+         size += gen->steps[0].scan -
+           (P_)BLOCK_ROUND_DOWN(gen->steps[0].scan);
+       } else {
+         size = gen->steps[0].scan - start;
+       }
+       fprintf(stderr,"evac IND_OLDGEN: %d bytes\n", size * sizeof(W_));
+      }
+#endif
+
+      /* failed_to_evac might happen if we've got more than two
+       * generations, we're collecting only generation 0, the
+       * indirection resides in generation 2 and the indirectee is
+       * in generation 1.
+       */
+      if (failed_to_evac) {
+       failed_to_evac = rtsFalse;
+       p->mut_link = new_list;
+       new_list = p;
+      } else {
+       /* the mut_link field of an IND_STATIC is overloaded as the
+        * static link field too (it just so happens that we don't need
+        * both at the same time), so we need to NULL it out when
+        * removing this object from the mutable list because the static
+        * link fields are all assumed to be NULL before doing a major
+        * collection. 
+        */
+       p->mut_link = NULL;
+      }
+      continue;
+      
+    case MUT_VAR:
+      /* MUT_CONS is a kind of MUT_VAR, except it that we try to remove
+       * it from the mutable list if possible by promoting whatever it
+       * points to.
+       */
+      ASSERT(p->header.info == &MUT_CONS_info);
+      if (scavenge_one(((StgMutVar *)p)->var) == rtsTrue) {
+       /* didn't manage to promote everything, so put the
+        * MUT_CONS back on the list.
+        */
+       p->mut_link = new_list;
+       new_list = p;
+      } 
+      continue;
+      
+    case CAF_ENTERED:
+      { 
+       StgCAF *caf = (StgCAF *)p;
+       caf->body  = evacuate(caf->body);
+       caf->value = evacuate(caf->value);
+       if (failed_to_evac) {
+         failed_to_evac = rtsFalse;
+         p->mut_link = new_list;
+         new_list = p;
+       } else {
+         p->mut_link = NULL;
+       }
+      }
+      continue;
+
+    case CAF_UNENTERED:
+      { 
+       StgCAF *caf = (StgCAF *)p;
+       caf->body  = evacuate(caf->body);
+       if (failed_to_evac) {
+         failed_to_evac = rtsFalse;
+         p->mut_link = new_list;
+         new_list = p;
+       } else {
+          p->mut_link = NULL;
+        }
+      }
+      continue;
+
+    default:
+      /* shouldn't have anything else on the mutables list */
+      barf("scavenge_mut_once_list: strange object?");
+    }
+  }
+
+  gen->mut_once_list = new_list;
+}
+
+
+static void
+scavenge_mutable_list(generation *gen)
+{
+  StgInfoTable *info;
+  StgMutClosure *p, *next;
+
+  p = gen->saved_mut_list;
+  next = p->mut_link;
+
+  evac_gen = 0;
   failed_to_evac = rtsFalse;
 
-  for (; p != END_MUT_LIST; p = *prev) {
+  for (; p != END_MUT_LIST; p = next, next = p->mut_link) {
 
     /* make sure the info pointer is into text space */
     ASSERT(p && (LOOKS_LIKE_GHC_INFO(GET_INFO(p))
@@ -1746,7 +2128,7 @@ scavenge_mutable_list(StgMutClosure *p, nat gen)
        StgPtr end, q;
        
        end = (P_)p + mut_arr_ptrs_sizeW((StgMutArrPtrs*)p);
-       evac_gen = gen;
+       evac_gen = gen->no;
        for (q = (P_)((StgMutArrPtrs *)p)->payload; q < end; q++) {
          (StgClosure *)*q = evacuate((StgClosure *)*q);
        }
@@ -1754,16 +2136,16 @@ scavenge_mutable_list(StgMutClosure *p, nat gen)
 
        if (failed_to_evac) {
          failed_to_evac = rtsFalse;
-         prev = &p->mut_link;
-       } else {
-         *prev = p->mut_link;
-       }
+         p->mut_link = gen->mut_list;
+         gen->mut_list = p;
+       } 
        continue;
       }
 
     case MUT_ARR_PTRS:
       /* follow everything */
-      prev = &p->mut_link;
+      p->mut_link = gen->mut_list;
+      gen->mut_list = p;
       {
        StgPtr end, q;
        
@@ -1779,21 +2161,10 @@ scavenge_mutable_list(StgMutClosure *p, nat gen)
        * it from the mutable list if possible by promoting whatever it
        * points to.
        */
-      if (p->header.info == &MUT_CONS_info) {
-       evac_gen = gen;
-       if (scavenge_one((P_)((StgMutVar *)p)->var) == rtsTrue) {
-         /* didn't manage to promote everything, so leave the
-          * MUT_CONS on the list.
-          */
-         prev = &p->mut_link;
-       } else {
-         *prev = p->mut_link;
-       }
-       evac_gen = 0;
-      } else {
-       ((StgMutVar *)p)->var = evacuate(((StgMutVar *)p)->var);
-       prev = &p->mut_link;
-      }
+      ASSERT(p->header.info != &MUT_CONS_info);
+      ((StgMutVar *)p)->var = evacuate(((StgMutVar *)p)->var);
+      p->mut_link = gen->mut_list;
+      gen->mut_list = p;
       continue;
       
     case MVAR:
@@ -1802,75 +2173,45 @@ scavenge_mutable_list(StgMutClosure *p, nat gen)
        (StgClosure *)mvar->head = evacuate((StgClosure *)mvar->head);
        (StgClosure *)mvar->tail = evacuate((StgClosure *)mvar->tail);
        (StgClosure *)mvar->value = evacuate((StgClosure *)mvar->value);
-       prev = &p->mut_link;
+       p->mut_link = gen->mut_list;
+       gen->mut_list = p;
        continue;
       }
 
     case TSO:
-      /* follow ptrs and remove this from the mutable list */
       { 
        StgTSO *tso = (StgTSO *)p;
 
-       /* Don't bother scavenging if this thread is dead 
-        */
-       if (!(tso->whatNext == ThreadComplete ||
-             tso->whatNext == ThreadKilled)) {
-         /* Don't need to chase the link field for any TSOs on the
-          * same queue. Just scavenge this thread's stack 
-          */
-         scavenge_stack(tso->sp, &(tso->stack[tso->stack_size]));
+       (StgClosure *)tso->link = evacuate((StgClosure *)tso->link);
+       if (tso->blocked_on) {
+         tso->blocked_on = evacuate(tso->blocked_on);
        }
+       scavenge_stack(tso->sp, &(tso->stack[tso->stack_size]));
 
        /* Don't take this TSO off the mutable list - it might still
         * point to some younger objects (because we set evac_gen to 0
         * above). 
         */
-       prev = &tso->mut_link;
+       tso->mut_link = gen->mut_list;
+       gen->mut_list = (StgMutClosure *)tso;
        continue;
       }
       
-    case IND_OLDGEN:
-    case IND_OLDGEN_PERM:
-    case IND_STATIC:
-      /* Try to pull the indirectee into this generation, so we can
-       * remove the indirection from the mutable list.  
-       */
-      evac_gen = gen;
-      ((StgIndOldGen *)p)->indirectee = 
-        evacuate(((StgIndOldGen *)p)->indirectee);
-      evac_gen = 0;
-
-      if (failed_to_evac) {
-       failed_to_evac = rtsFalse;
-       prev = &p->mut_link;
-      } else {
-       *prev = p->mut_link;
-       /* the mut_link field of an IND_STATIC is overloaded as the
-        * static link field too (it just so happens that we don't need
-        * both at the same time), so we need to NULL it out when
-        * removing this object from the mutable list because the static
-        * link fields are all assumed to be NULL before doing a major
-        * collection. 
-        */
-       p->mut_link = NULL;
-      }
-      continue;
-      
     case BLACKHOLE_BQ:
       { 
        StgBlockingQueue *bh = (StgBlockingQueue *)p;
        (StgClosure *)bh->blocking_queue = 
          evacuate((StgClosure *)bh->blocking_queue);
-       prev = &p->mut_link;
-       break;
+       p->mut_link = gen->mut_list;
+       gen->mut_list = p;
+       continue;
       }
 
     default:
       /* shouldn't have anything else on the mutables list */
-      barf("scavenge_mutable_object: non-mutable object?");
+      barf("scavenge_mut_list: strange object?");
     }
   }
-  return start;
 }
 
 static void
@@ -1915,8 +2256,8 @@ scavenge_static(void)
        if (failed_to_evac) {
          failed_to_evac = rtsFalse;
          scavenged_static_objects = STATIC_LINK(info,p);
-         ((StgMutClosure *)ind)->mut_link = oldest_gen->mut_list;
-         oldest_gen->mut_list = (StgMutClosure *)ind;
+         ((StgMutClosure *)ind)->mut_link = oldest_gen->mut_once_list;
+         oldest_gen->mut_once_list = (StgMutClosure *)ind;
        }
        break;
       }
@@ -1963,7 +2304,7 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
 {
   StgPtr q;
   const StgInfoTable* info;
-  StgNat32 bitmap;
+  StgWord32 bitmap;
 
   /* 
    * Each time around this loop, we are looking at a chunk of stack
@@ -1972,24 +2313,23 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
    */
 
   while (p < stack_end) {
-    q = *stgCast(StgPtr*,p);
+    q = *(P_ *)p;
 
     /* If we've got a tag, skip over that many words on the stack */
-    if (IS_ARG_TAG(stgCast(StgWord,q))) {
+    if (IS_ARG_TAG((W_)q)) {
       p += ARG_SIZE(q);
       p++; continue;
     }
      
     /* Is q a pointer to a closure?
      */
-    if (! LOOKS_LIKE_GHC_INFO(q)) {
 
+    if (! LOOKS_LIKE_GHC_INFO(q)) {
 #ifdef DEBUG
-      if (LOOKS_LIKE_STATIC(q)) { /* Is it a static closure? */
+      if ( 0 && LOOKS_LIKE_STATIC_CLOSURE(q) ) {  /* Is it a static closure? */
        ASSERT(closure_STATIC(stgCast(StgClosure*,q)));
-      } 
-      /* otherwise, must be a pointer into the allocation space.
-       */
+      }
+      /* otherwise, must be a pointer into the allocation space. */
 #endif
 
       (StgClosure *)*p = evacuate((StgClosure *)q);
@@ -2002,14 +2342,14 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
      * record.  All activation records have 'bitmap' style layout
      * info.
      */
-    info  = get_itbl(stgCast(StgClosure*,p));
+    info  = get_itbl((StgClosure *)p);
       
     switch (info->type) {
        
       /* Dynamic bitmap: the mask is stored on the stack */
     case RET_DYN:
-      bitmap = stgCast(StgRetDyn*,p)->liveness;
-      p      = &payloadWord(stgCast(StgRetDyn*,p),0);
+      bitmap = ((StgRetDyn *)p)->liveness;
+      p      = (P_)&((StgRetDyn *)p)->payload[0];
       goto small_bitmap;
 
       /* probably a slow-entry point return address: */
@@ -2026,7 +2366,7 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
       {
        StgUpdateFrame *frame = (StgUpdateFrame *)p;
        StgClosure *to;
-       StgClosureType type = get_itbl(frame->updatee)->type;
+       nat type = get_itbl(frame->updatee)->type;
 
        p += sizeofW(StgUpdateFrame);
        if (type == EVACUATED) {
@@ -2034,25 +2374,33 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
          continue;
        } else {
          bdescr *bd = Bdescr((P_)frame->updatee);
+         step *step;
          if (bd->gen->no > N) { 
            if (bd->gen->no < evac_gen) {
              failed_to_evac = rtsTrue;
            }
            continue;
          }
+
+         /* Don't promote blackholes */
+         step = bd->step;
+         if (!(step->gen->no == 0 && 
+               step->no != 0 &&
+               step->no == step->gen->n_steps-1)) {
+           step = step->to;
+         }
+
          switch (type) {
          case BLACKHOLE:
          case CAF_BLACKHOLE:
            to = copyPart(frame->updatee, BLACKHOLE_sizeW(), 
-                         sizeofW(StgHeader), bd);
-           upd_evacuee(frame->updatee,to);
+                         sizeofW(StgHeader), step);
            frame->updatee = to;
            continue;
          case BLACKHOLE_BQ:
-           to = copy(frame->updatee, BLACKHOLE_sizeW(), bd);
-           upd_evacuee(frame->updatee,to);
+           to = copy(frame->updatee, BLACKHOLE_sizeW(), step);
            frame->updatee = to;
-           evacuate_mutable((StgMutClosure *)to);
+           recordMutable((StgMutClosure *)to);
            continue;
          default:
            barf("scavenge_stack: UPDATE_FRAME updatee");
@@ -2159,7 +2507,6 @@ scavenge_large(step *step)
     /* only certain objects can be "large"... */
 
     case ARR_WORDS:
-    case MUT_ARR_WORDS:
       /* nothing to follow */
       continue;
 
@@ -2187,7 +2534,7 @@ scavenge_large(step *step)
        }
        evac_gen = 0;
        if (failed_to_evac) {
-         evacuate_mutable((StgMutClosure *)start);
+         recordMutable((StgMutClosure *)start);
        }
        continue;
       }
@@ -2211,6 +2558,9 @@ scavenge_large(step *step)
        tso = (StgTSO *)p;
        /* chase the link field for any TSOs on the same queue */
        (StgClosure *)tso->link = evacuate((StgClosure *)tso->link);
+       if (tso->blocked_on) {
+         tso->blocked_on = evacuate(tso->blocked_on);
+       }
        /* scavenge this thread's stack */
        scavenge_stack(tso->sp, &(tso->stack[tso->stack_size]));
        continue;
@@ -2223,7 +2573,7 @@ scavenge_large(step *step)
 }
 
 static void
-zeroStaticObjectList(StgClosure* first_static)
+zero_static_object_list(StgClosure* first_static)
 {
   StgClosure* p;
   StgClosure* link;
@@ -2245,7 +2595,7 @@ zeroStaticObjectList(StgClosure* first_static)
  * mutable list.
  */
 static void
-zeroMutableList(StgMutClosure *first)
+zero_mutable_list( StgMutClosure *first )
 {
   StgMutClosure *next, *c;
 
@@ -2270,35 +2620,27 @@ void RevertCAFs(void)
     caf->value = stgCast(StgClosure*,0xdeadbeef);
     caf->link  = stgCast(StgCAF*,0xdeadbeef);
   }
+  enteredCAFs = END_CAF_LIST;
 }
 
-void revertDeadCAFs(void)
+void revert_dead_CAFs(void)
 {
     StgCAF* caf = enteredCAFs;
     enteredCAFs = END_CAF_LIST;
     while (caf != END_CAF_LIST) {
-       StgCAF* next = caf->link;
-
-       switch(GET_INFO(caf)->type) {
-       case EVACUATED:
-           {
-               /* This object has been evacuated, it must be live. */
-               StgCAF* new = stgCast(StgCAF*,stgCast(StgEvacuated*,caf)->evacuee);
-               new->link = enteredCAFs;
-               enteredCAFs = new;
-               break;
-           }
-       case CAF_ENTERED:
-           {
-               SET_INFO(caf,&CAF_UNENTERED_info);
-               caf->value = stgCast(StgClosure*,0xdeadbeef);
-               caf->link  = stgCast(StgCAF*,0xdeadbeef);
-               break;
-           }
-       default:
-               barf("revertDeadCAFs: enteredCAFs list corrupted");
-       } 
-       caf = next;
+        StgCAF *next, *new;
+        next = caf->link;
+        new = (StgCAF*)isAlive((StgClosure*)caf);
+        if (new) {
+           new->link = enteredCAFs;
+           enteredCAFs = new;
+        } else {
+           ASSERT(0);
+           SET_INFO(caf,&CAF_UNENTERED_info);
+           caf->value = (StgClosure*)0xdeadbeef;
+           caf->link  = (StgCAF*)0xdeadbeef;
+        } 
+        caf = next;
     }
 }
 
@@ -2447,7 +2789,8 @@ threadSqueezeStack(StgTSO *tso)
    */
   
   next_frame = NULL;
-  while ((P_)frame < bottom - 1) {  /* bottom - 1 is the STOP_FRAME */
+  /* bottom - sizeof(StgStopFrame) is the STOP_FRAME */
+  while ((P_)frame < bottom - sizeofW(StgStopFrame)) {  
     prev_frame = frame->link;
     frame->link = next_frame;
     next_frame = frame;