[project @ 1999-02-11 17:40:23 by simonm]
[ghc-hetmet.git] / ghc / rts / GC.c
index 619aa5c..82860ce 100644 (file)
@@ -1,7 +1,9 @@
 /* -----------------------------------------------------------------------------
- * $Id: GC.c,v 1.19 1999/01/26 11:12:43 simonm Exp $
+ * $Id: GC.c,v 1.30 1999/02/11 17:40:26 simonm Exp $
  *
- * Two-space garbage collector
+ * (c) The GHC Team 1998-1999
+ *
+ * Generational garbage collector
  *
  * ---------------------------------------------------------------------------*/
 
@@ -103,7 +105,8 @@ 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_mutable_list(generation *g);
+static void           scavenge_mut_once_list(generation *g);
 
 #ifdef DEBUG
 static void gcCAFs(void);
@@ -191,7 +194,7 @@ void GarbageCollect(void (*get_roots)(void))
    * zeroMutableList below).
    */
   if (major_gc) { 
-    zeroMutableList(generations[RtsFlags.GcFlags.generations-1].mut_list);
+    zeroMutableList(generations[RtsFlags.GcFlags.generations-1].mut_once_list);
   }
 
   /* Save the old to-space if we're doing a two-space collection
@@ -205,6 +208,7 @@ void GarbageCollect(void (*get_roots)(void))
    * 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++) {
@@ -283,19 +287,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;
     }
   }
 
@@ -373,18 +385,21 @@ 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--) {
+         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;
          }
        }
       }
@@ -424,63 +439,6 @@ 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 * 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;
-      }
-    }
-    
-    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 
@@ -540,16 +498,13 @@ 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));
+         generations[g].max_blocks = (oldest_gen->max_blocks * g)
+              / (RtsFlags.GcFlags.generations-1);
        }
 
       /* for older generations... */
@@ -571,34 +526,97 @@ void GarbageCollect(void (*get_roots)(void))
     }
   }
   
+  /* Guess the amount of live data for stats. */
+  live = calcLive();
+
   /* 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 amount of data that was live this
+       * time:  this assumes (1) that the size of G0S0 will be roughly
+       * the same as last time, and (2) that the promotion rate will be
+       * constant.
+       *
+       * If we don't know how much was live in G0S0 (because there's no
+       * step 1), then assume 30% (which is usually an overestimate).
+       */
+      if (g0->n_steps == 1) {
+       needed += (g0s0->n_blocks * 30) / 100;
+      } else {
+       needed += g0->steps[1].n_blocks;
+      }
+
+      /* Now we have a rough guess at the number of blocks needed for
+       * the next GC, subtract this from the user's suggested heap size
+       * and use the rest for the allocation area.
+       */
+      blocks = (int)RtsFlags.GcFlags.heapSizeSuggestion - (int)needed;
+      
+      if (blocks < (int)RtsFlags.GcFlags.minAllocAreaSize) {
+       blocks = RtsFlags.GcFlags.minAllocAreaSize;
       }
+      
+      resizeNursery((nat)blocks);
     }
   }
 
@@ -634,33 +652,13 @@ void GarbageCollect(void (*get_roots)(void))
   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
@@ -694,7 +692,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.
@@ -709,7 +707,7 @@ traverse_weak_ptr_list(void)
 
   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;
@@ -719,9 +717,9 @@ traverse_weak_ptr_list(void)
 
     if ((new = isAlive(w->key))) {
       w->key = new;
-      /* evacuate the value and finaliser */
+      /* 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 */
@@ -741,12 +739,12 @@ traverse_weak_ptr_list(void)
   
   /* 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) {
     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;
   }
@@ -807,7 +805,7 @@ MarkRoot(StgClosure *root)
   return evacuate(root);
 }
 
-static inline void addBlock(step *step)
+static void addBlock(step *step)
 {
   bdescr *bd = allocBlock();
   bd->gen = step->gen;
@@ -828,17 +826,16 @@ static inline void addBlock(step *step)
 }
 
 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) {
     step = &generations[evac_gen].steps[0];
   }
@@ -850,11 +847,12 @@ 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;
   return (StgClosure *)dest;
 }
 
@@ -864,12 +862,11 @@ 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) {
     step = &generations[evac_gen].steps[0];
   }
@@ -878,12 +875,12 @@ copyPart(StgClosure *src, nat size_to_reserve, nat size_to_copy, bdescr *bd)
     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;
   return (StgClosure *)dest;
 }
 
@@ -897,25 +894,6 @@ upd_evacuee(StgClosure *p, StgClosure *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
 
    This just consists of removing the object from the (doubly-linked)
@@ -942,6 +920,7 @@ evacuate_large(StgPtr p, rtsBool mutable)
      */
     if (bd->gen->no < evac_gen) {
       failed_to_evac = rtsTrue;
+      TICK_GC_FAILED_PROMOTION();
     }
     return;
   }
@@ -971,7 +950,7 @@ evacuate_large(StgPtr p, rtsBool mutable)
   bd->evacuated = 1;
 
   if (mutable) {
-    evacuate_mutable((StgMutClosure *)p);
+    recordMutable((StgMutClosure *)p);
   }
 }
 
@@ -1003,7 +982,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;
 }
@@ -1039,6 +1018,7 @@ evacuate(StgClosure *q)
 {
   StgClosure *to;
   bdescr *bd = NULL;
+  step *step;
   const StgInfoTable *info;
 
 loop:
@@ -1052,9 +1032,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 */
@@ -1065,20 +1047,44 @@ loop:
   switch (info -> type) {
 
   case BCO:
-    to = copy(q,bco_sizeW(stgCast(StgBCO*,q)),bd);
+    to = copy(q,bco_sizeW(stgCast(StgBCO*,q)),step);
     upd_evacuee(q,to);
     return to;
 
   case MUT_VAR:
+    ASSERT(q->header.info != &MUT_CONS_info);
   case MVAR:
-    to = copy(q,sizeW_fromITBL(info),bd);
+    to = copy(q,sizeW_fromITBL(info),step);
     upd_evacuee(q,to);
-    evacuate_mutable((StgMutClosure *)to);
+    recordMutable((StgMutClosure *)to);
     return to;
 
   case STABLE_NAME:
     stable_ptr_table[((StgStableName *)q)->sn].keep = rtsTrue;
-    to = copy(q,sizeofW(StgStableName),bd);
+    to = copy(q,sizeofW(StgStableName),step);
+    upd_evacuee(q,to);
+    return to;
+
+  case FUN_1_0:
+  case FUN_0_1:
+  case CONSTR_1_0:
+  case CONSTR_0_1:
+    to = copy(q,sizeofW(StgHeader)+1,step);
+    upd_evacuee(q,to);
+    return to;
+
+  case THUNK_1_0:              /* here because of MIN_UPD_SIZE */
+  case THUNK_0_1:
+  case FUN_1_1:
+  case FUN_0_2:
+  case FUN_2_0:
+  case THUNK_1_1:
+  case THUNK_0_2:
+  case THUNK_2_0:
+  case CONSTR_1_1:
+  case CONSTR_0_2:
+  case CONSTR_2_0:
+    to = copy(q,sizeofW(StgHeader)+2,step);
     upd_evacuee(q,to);
     return to;
 
@@ -1091,20 +1097,20 @@ loop:
   case CAF_ENTERED:
   case WEAK:
   case FOREIGN:
-    to = copy(q,sizeW_fromITBL(info),bd);
+    to = copy(q,sizeW_fromITBL(info),step);
     upd_evacuee(q,to);
     return to;
 
   case CAF_BLACKHOLE:
   case BLACKHOLE:
-    to = copyPart(q,BLACKHOLE_sizeW(),sizeofW(StgHeader),bd);
+    to = copyPart(q,BLACKHOLE_sizeW(),sizeofW(StgHeader),step);
     upd_evacuee(q,to);
     return to;
 
   case BLACKHOLE_BQ:
-    to = copy(q,BLACKHOLE_sizeW(),bd); 
+    to = copy(q,BLACKHOLE_sizeW(),step); 
     upd_evacuee(q,to);
-    evacuate_mutable((StgMutClosure *)to);
+    recordMutable((StgMutClosure *)to);
     return to;
 
   case THUNK_SELECTOR:
@@ -1116,6 +1122,11 @@ 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;
@@ -1137,6 +1148,7 @@ loop:
            if (bd->evacuated) {
              if (bd->gen->no < evac_gen) {
                failed_to_evac = rtsTrue;
+               TICK_GC_FAILED_PROMOTION();
              }
              return q;
            }
@@ -1165,6 +1177,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???? */
@@ -1179,7 +1196,7 @@ loop:
        barf("evacuate: THUNK_SELECTOR: strange selectee");
       }
     }
-    to = copy(q,THUNK_SELECTOR_sizeW(),bd);
+    to = copy(q,THUNK_SELECTOR_sizeW(),step);
     upd_evacuee(q,to);
     return to;
 
@@ -1239,7 +1256,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);
+    to = copy(q,pap_sizeW(stgCast(StgPAP*,q)),step);
     upd_evacuee(q,to);
     return to;
 
@@ -1256,11 +1273,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)); 
@@ -1270,7 +1287,7 @@ loop:
        return q;
       } else {
        /* just copy the block */
-       to = copy(q,size,bd);
+       to = copy(q,size,step);
        upd_evacuee(q,to);
        return to;
       }
@@ -1286,10 +1303,10 @@ loop:
        to = q;
       } else {
        /* just copy the block */
-       to = copy(q,size,bd);
+       to = copy(q,size,step);
        upd_evacuee(q,to);
        if (info->type == MUT_ARR_PTRS) {
-         evacuate_mutable((StgMutClosure *)to);
+         recordMutable((StgMutClosure *)to);
        }
       }
       return to;
@@ -1311,7 +1328,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* */
 
@@ -1323,7 +1340,7 @@ loop:
        relocate_TSO(tso, new_tso);
        upd_evacuee(q,(StgClosure *)new_tso);
 
-       evacuate_mutable((StgMutClosure *)new_tso);
+       recordMutable((StgMutClosure *)new_tso);
        return (StgClosure *)new_tso;
       }
     }
@@ -1482,6 +1499,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);
@@ -1528,7 +1593,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;
@@ -1584,7 +1649,6 @@ scavenge(step *step)
       }
       
     case ARR_WORDS:
-    case MUT_ARR_WORDS:
       /* nothing to follow */
       p += arr_words_sizeW(stgCast(StgArrWords*,p));
       break;
@@ -1614,7 +1678,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;
@@ -1666,21 +1730,36 @@ 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:
@@ -1688,11 +1767,11 @@ scavenge_one(StgPtr p)
   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;
     }
@@ -1705,7 +1784,7 @@ scavenge_one(StgPtr p)
     { 
       StgSelector *s = (StgSelector *)p;
       s->selectee = evacuate(s->selectee);
-       break;
+      break;
     }
     
   case AP_UPD: /* same as PAPs */
@@ -1714,7 +1793,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);
@@ -1747,21 +1826,122 @@ 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;
 
-  prev = &start;
-  start = p;
+  evac_gen = gen->no;
+  failed_to_evac = rtsFalse;
+
+  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;
+      
+    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, *new_list;
+
+  p = gen->saved_mut_list;
+  new_list = gen->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))
@@ -1778,7 +1958,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);
        }
@@ -1786,16 +1966,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 = new_list;
+         new_list = p;
+       } 
        continue;
       }
 
     case MUT_ARR_PTRS:
       /* follow everything */
-      prev = &p->mut_link;
+      p->mut_link = new_list;
+      new_list = p;
       {
        StgPtr end, q;
        
@@ -1811,21 +1991,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 = new_list;
+      new_list = p;
       continue;
       
     case MVAR:
@@ -1834,7 +2003,8 @@ 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 = new_list;
+       new_list = p;
        continue;
       }
 
@@ -1857,52 +2027,28 @@ scavenge_mutable_list(StgMutClosure *p, nat gen)
         * point to some younger objects (because we set evac_gen to 0
         * above). 
         */
-       prev = &tso->mut_link;
+       tso->mut_link = new_list;
+       new_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 = new_list;
+       new_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;
+
+  gen->mut_list = new_list;
 }
 
 static void
@@ -1947,8 +2093,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;
       }
@@ -2066,25 +2212,27 @@ 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;
          }
+         step = bd->step->to;
          switch (type) {
          case BLACKHOLE:
          case CAF_BLACKHOLE:
            to = copyPart(frame->updatee, BLACKHOLE_sizeW(), 
-                         sizeofW(StgHeader), bd);
+                         sizeofW(StgHeader), step);
            upd_evacuee(frame->updatee,to);
            frame->updatee = to;
            continue;
          case BLACKHOLE_BQ:
-           to = copy(frame->updatee, BLACKHOLE_sizeW(), bd);
+           to = copy(frame->updatee, BLACKHOLE_sizeW(), step);
            upd_evacuee(frame->updatee,to);
            frame->updatee = to;
-           evacuate_mutable((StgMutClosure *)to);
+           recordMutable((StgMutClosure *)to);
            continue;
          default:
            barf("scavenge_stack: UPDATE_FRAME updatee");
@@ -2191,7 +2339,6 @@ scavenge_large(step *step)
     /* only certain objects can be "large"... */
 
     case ARR_WORDS:
-    case MUT_ARR_WORDS:
       /* nothing to follow */
       continue;
 
@@ -2219,7 +2366,7 @@ scavenge_large(step *step)
        }
        evac_gen = 0;
        if (failed_to_evac) {
-         evacuate_mutable((StgMutClosure *)start);
+         recordMutable((StgMutClosure *)start);
        }
        continue;
       }