[project @ 1999-01-28 09:19:57 by simonpj]
[ghc-hetmet.git] / ghc / rts / GC.c
index 1c0bd5f..6521312 100644 (file)
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: GC.c,v 1.10 1999/01/18 12:23:04 simonm Exp $
+ * $Id: GC.c,v 1.21 1999/01/27 16:41:14 simonm Exp $
  *
  * Two-space garbage collector
  *
@@ -20,6 +20,7 @@
 #include "DebugProf.h"
 #include "SchedAPI.h"
 #include "Weak.h"
+#include "StablePriv.h"
 
 StgCAF* enteredCAFs;
 
@@ -84,6 +85,10 @@ static rtsBool weak_done;    /* all done for this pass */
  */
 static rtsBool failed_to_evac;
 
+/* Old to-space (used for two-space collector only)
+ */
+bdescr *old_to_space;
+
 /* -----------------------------------------------------------------------------
    Static function declarations
    -------------------------------------------------------------------------- */
@@ -165,6 +170,7 @@ void GarbageCollect(void (*get_roots)(void))
 
   /* Figure out which generation to collect
    */
+  N = 0;
   for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
     if (generations[g].steps[0].n_blocks >= generations[g].max_blocks) {
       N = g;
@@ -188,6 +194,13 @@ void GarbageCollect(void (*get_roots)(void))
     zeroMutableList(generations[RtsFlags.GcFlags.generations-1].mut_list);
   }
 
+  /* Save the old to-space if we're doing a two-space collection
+   */
+  if (RtsFlags.GcFlags.generations == 1) {
+    old_to_space = g0s0->to_space;
+    g0s0->to_space = NULL;
+  }
+
   /* Initialise to-space in all the generations/steps that we're
    * collecting.
    */
@@ -195,8 +208,12 @@ void GarbageCollect(void (*get_roots)(void))
     generations[g].mut_list = END_MUT_LIST;
 
     for (s = 0; s < generations[g].n_steps; s++) {
+
       /* generation 0, step 0 doesn't need to-space */
-      if (g == 0 && s == 0) { continue; }
+      if (g == 0 && s == 0 && RtsFlags.GcFlags.generations > 1) { 
+       continue; 
+      }
+
       /* Get a free block for to-space.  Extra blocks will be chained on
        * as necessary.
        */
@@ -250,21 +267,11 @@ void GarbageCollect(void (*get_roots)(void))
       step->to_blocks = 0;
       step->new_large_objects = NULL;
       step->scavenged_large_objects = NULL;
-#ifdef DEBUG
-      /* retain these so we can sanity-check later on */
-      step->old_scan    = step->scan;
-      step->old_scan_bd = step->scan_bd;
-#endif
     }
   }
 
   /* -----------------------------------------------------------------------
-   * follow all the roots that the application knows about.
-   */
-  evac_gen = 0;
-  get_roots();
-
-  /* follow all the roots that we know about:
+   * follow all the roots that we know about:
    *   - mutable lists from each generation > N
    * we want to *scavenge* these roots, not evacuate them: they're not
    * going to move in this GC.
@@ -277,23 +284,26 @@ void GarbageCollect(void (*get_roots)(void))
    */
   { 
     StgMutClosure *tmp, **pp;
-    for (g = RtsFlags.GcFlags.generations-1; g > N; g--) {
-      /* the act of scavenging the mutable list for this generation
-       * might place more objects on the mutable list itself.  So we
-       * place the current mutable list in a temporary, scavenge it,
-       * and then append it to the new list.
-       */
-      tmp = generations[g].mut_list;
+    for (g = N+1; g < RtsFlags.GcFlags.generations; g++) {
+      generations[g].saved_mut_list = generations[g].mut_list;
       generations[g].mut_list = END_MUT_LIST;
-      tmp = scavenge_mutable_list(tmp, g);
+    }
 
+    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;
       }
       *pp = tmp;
     }
-  }  
+  }
+
+  /* follow all the roots that the application knows about.
+   */
+  evac_gen = 0;
+  get_roots();
+
   /* And don't forget to mark the TSO if we got here direct from
    * Haskell! */
   if (CurrentTSO) {
@@ -308,6 +318,10 @@ void GarbageCollect(void (*get_roots)(void))
   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
@@ -383,6 +397,10 @@ void GarbageCollect(void (*get_roots)(void))
     }
   }
 
+  /* Now see which stable names are still alive
+   */
+  gcStablePtrTable(major_gc);
+
   /* 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.  
@@ -391,18 +409,23 @@ void GarbageCollect(void (*get_roots)(void))
    * twice the amount of live data plus whatever space the other
    * generations need.
    */
-  oldest_gen->max_blocks = 
-    stg_max(oldest_gen->steps[0].to_blocks * 2,
-           RtsFlags.GcFlags.minAllocAreaSize * 4);
-  if (oldest_gen->max_blocks > RtsFlags.GcFlags.maxHeapSize / 2) {
-    oldest_gen->max_blocks = RtsFlags.GcFlags.maxHeapSize / 2;
-    if (((int)oldest_gen->max_blocks - (int)oldest_gen->steps[0].to_blocks) < 
-       (RtsFlags.GcFlags.pcFreeHeap *
-        RtsFlags.GcFlags.maxHeapSize / 200)) {
-      heapOverflow();
+  if (RtsFlags.GcFlags.generations > 1) {
+    if (major_gc) {
+      oldest_gen->max_blocks = 
+       stg_max(oldest_gen->steps[0].to_blocks * RtsFlags.GcFlags.oldGenFactor,
+               RtsFlags.GcFlags.minOldGenSize);
+      if (oldest_gen->max_blocks > RtsFlags.GcFlags.maxHeapSize / 2) {
+       oldest_gen->max_blocks = RtsFlags.GcFlags.maxHeapSize / 2;
+       if (((int)oldest_gen->max_blocks - 
+            (int)oldest_gen->steps[0].to_blocks) < 
+           (RtsFlags.GcFlags.pcFreeHeap *
+            RtsFlags.GcFlags.maxHeapSize / 200)) {
+         heapOverflow();
+       }
+      }
     }
   }
-  
+
   /* run through all the generations/steps and tidy up 
    */
   for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
@@ -415,7 +438,7 @@ void GarbageCollect(void (*get_roots)(void))
       bdescr *next;
       step = &generations[g].steps[s];
 
-      if (!(g == 0 && s == 0)) {
+      if (!(g == 0 && s == 0 && RtsFlags.GcFlags.generations > 1)) {
        /* Tidy the end of the to-space chains */
        step->hp_bd->free = step->hp;
        step->hp_bd->link = NULL;
@@ -460,16 +483,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... */
@@ -491,6 +511,126 @@ void GarbageCollect(void (*get_roots)(void))
     }
   }
   
+  /* 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_);
+
+    /* 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->n_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;
+
+  } else {
+    /* Generational collector:
+     * estimate the amount of live data, and adjust the allocation
+     * area size if the user has given us a suggestion (+RTS -H<blah>)
+     */
+
+    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_);
+      }
+    }
+
+    if (RtsFlags.GcFlags.heapSizeSuggestion) {
+      nat avail_blocks = 
+       (RtsFlags.GcFlags.heapSizeSuggestion - live / BLOCK_SIZE_W) / 2;
+      nat blocks;
+      
+      if (avail_blocks > RtsFlags.GcFlags.minAllocAreaSize) {
+       blocks = avail_blocks;
+      } else {
+       blocks = RtsFlags.GcFlags.minAllocAreaSize;
+      }
+
+      if (blocks > g0s0->n_blocks) {
+       /* need to add some blocks on */
+       fprintf(stderr, "Increasing size of alloc area to %d blocks\n", blocks);
+       g0s0->blocks = allocNursery(g0s0->blocks, avail_blocks - g0s0->n_blocks);
+      } else {
+       bdescr *next_bd;
+       fprintf(stderr, "Decreasing size of alloc area 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;
+    }
+  }
+
   /* revert dead CAFs and update enteredCAFs list */
   revertDeadCAFs();
   
@@ -513,19 +653,6 @@ void GarbageCollect(void (*get_roots)(void))
   }
   current_nursery = g0s0->blocks;
 
-  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_);
-    }
-  }
-
   /* Free the small objects allocated via allocate(), since this will
    * all have been copied into G0S1 now.  
    */
@@ -541,21 +668,26 @@ void GarbageCollect(void (*get_roots)(void))
   
   /* check sanity after GC */
 #ifdef DEBUG
-  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));
-      IF_DEBUG(sanity, checkChain(generations[g].steps[s].large_objects));
+  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].old_scan_bd,
-                                generations[g].steps[s].old_scan));
-      IF_DEBUG(sanity, checkChain(generations[g].steps[s].large_objects));
+    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());
   }
-  IF_DEBUG(sanity, checkFreeListSanity());
 #endif
 
   IF_DEBUG(gc, stat_describe_gens());
@@ -601,8 +733,7 @@ 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; }
@@ -614,56 +745,26 @@ traverse_weak_ptr_list(void)
 
   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;
-      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;
-
-    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;
+    if ((new = isAlive(w->key))) {
+      w->key = new;
+      /* evacuate the value and finaliser */
       w->value = evacuate(w->value);
       w->finaliser = evacuate(w->finaliser);
-      
       /* 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;
     }
   }
   
@@ -682,14 +783,60 @@ traverse_weak_ptr_list(void)
   return rtsTrue;
 }
 
+/* -----------------------------------------------------------------------------
+   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;
@@ -710,9 +857,8 @@ static inline void addBlock(step *step)
 }
 
 static __inline__ StgClosure *
-copy(StgClosure *src, W_ size, bdescr *bd)
+copy(StgClosure *src, nat size, step *step)
 {
-  step *step;
   P_ to, from, dest;
 
   /* Find out where we're going, using the handy "to" pointer in 
@@ -720,7 +866,6 @@ copy(StgClosure *src, W_ size, bdescr *bd)
    * 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];
   }
@@ -732,11 +877,39 @@ copy(StgClosure *src, W_ size, bdescr *bd)
     addBlock(step);
   }
 
+  for(to = step->hp, from = (P_)src; size>0; --size) {
+    *to++ = *from++;
+  }
+
   dest = step->hp;
-  step->hp += size;
-  for(to = dest, from = (P_)src; size>0; --size) {
+  step->hp = to;
+  return (StgClosure *)dest;
+}
+
+/* Special version of copy() for when we only want to copy the info
+ * pointer of an object, but reserve some padding after it.  This is
+ * used to optimise evacuation of BLACKHOLEs.
+ */
+
+static __inline__ StgClosure *
+copyPart(StgClosure *src, nat size_to_reserve, nat size_to_copy, step *step)
+{
+  P_ dest, to, from;
+
+  if (step->gen->no < evac_gen) {
+    step = &generations[evac_gen].steps[0];
+  }
+
+  if (step->hp + size_to_reserve >= step->hpLim) {
+    addBlock(step);
+  }
+
+  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;
 }
 
@@ -795,6 +968,7 @@ evacuate_large(StgPtr p, rtsBool mutable)
      */
     if (bd->gen->no < evac_gen) {
       failed_to_evac = rtsTrue;
+      TICK_GC_FAILED_PROMOTION();
     }
     return;
   }
@@ -892,6 +1066,7 @@ evacuate(StgClosure *q)
 {
   StgClosure *to;
   bdescr *bd = NULL;
+  step *step;
   const StgInfoTable *info;
 
 loop:
@@ -905,9 +1080,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 */
@@ -918,17 +1095,46 @@ 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:
   case MVAR:
-    to = copy(q,sizeW_fromITBL(info),bd);
+    to = copy(q,sizeW_fromITBL(info),step);
     upd_evacuee(q,to);
     evacuate_mutable((StgMutClosure *)to);
     return to;
 
+  case STABLE_NAME:
+    stable_ptr_table[((StgStableName *)q)->sn].keep = rtsTrue;
+    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;
+
   case FUN:
   case THUNK:
   case CONSTR:
@@ -938,18 +1144,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),step);
+    upd_evacuee(q,to);
+    return to;
+
   case BLACKHOLE_BQ:
-    /* ToDo: don't need to copy all the blackhole, some of it is
-     * just padding.
-     */
-    to = copy(q,BLACKHOLE_sizeW(),bd); 
+    to = copy(q,BLACKHOLE_sizeW(),step); 
     upd_evacuee(q,to);
+    evacuate_mutable((StgMutClosure *)to);
     return to;
 
   case THUNK_SELECTOR:
@@ -961,6 +1169,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;
@@ -982,6 +1195,7 @@ loop:
            if (bd->evacuated) {
              if (bd->gen->no < evac_gen) {
                failed_to_evac = rtsTrue;
+               TICK_GC_FAILED_PROMOTION();
              }
              return q;
            }
@@ -1010,6 +1224,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???? */
@@ -1024,7 +1243,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;
 
@@ -1084,7 +1303,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;
 
@@ -1101,6 +1320,7 @@ 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;
@@ -1115,7 +1335,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;
       }
@@ -1131,7 +1351,7 @@ 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);
@@ -1149,15 +1369,14 @@ loop:
       /* Large TSOs don't get moved, so no relocation is required.
        */
       if (size >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
-       evacuate_large((P_)q, rtsFalse);
-       tso->mut_link = NULL;   /* see below */
+       evacuate_large((P_)q, rtsTrue);
        return q;
 
       /* To evacuate a small TSO, we need to relocate the update frame
        * 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* */
 
@@ -1169,14 +1388,7 @@ loop:
        relocate_TSO(tso, new_tso);
        upd_evacuee(q,(StgClosure *)new_tso);
 
-       /* don't evac_mutable - these things are marked mutable as
-        * required.  We *do* need to zero the mut_link field, though:
-        * this TSO might have been on the mutable list for this
-        * generation, but we're collecting this generation anyway so
-        * we didn't follow the mutable list.
-        */
-       new_tso->mut_link = NULL;
-
+       evacuate_mutable((StgMutClosure *)new_tso);
        return (StgClosure *)new_tso;
       }
     }
@@ -1335,6 +1547,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);
@@ -1343,6 +1603,7 @@ scavenge(step *step)
     case CONSTR:
     case WEAK:
     case FOREIGN:
+    case STABLE_NAME:
     case IND_PERM:
     case IND_OLDGEN_PERM:
     case CAF_UNENTERED:
@@ -1375,9 +1636,13 @@ scavenge(step *step)
 
     case BLACKHOLE_BQ:
       { 
-       StgBlackHole *bh = (StgBlackHole *)p;
+       StgBlockingQueue *bh = (StgBlockingQueue *)p;
        (StgClosure *)bh->blocking_queue = 
          evacuate((StgClosure *)bh->blocking_queue);
+       if (failed_to_evac) {
+         failed_to_evac = rtsFalse;
+         evacuate_mutable((StgMutClosure *)bh);
+       }
        p += BLACKHOLE_sizeW();
        break;
       }
@@ -1527,8 +1792,23 @@ scavenge_one(StgPtr 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:
@@ -1549,14 +1829,6 @@ scavenge_one(StgPtr p)
   case BLACKHOLE:
       break;
 
-  case BLACKHOLE_BQ:
-    { 
-      StgBlackHole *bh = (StgBlackHole *)p;
-      (StgClosure *)bh->blocking_queue = 
-       evacuate((StgClosure *)bh->blocking_queue);
-      break;
-    }
-
   case THUNK_SELECTOR:
     { 
       StgSelector *s = (StgSelector *)p;
@@ -1744,6 +2016,15 @@ scavenge_mutable_list(StgMutClosure *p, nat gen)
       }
       continue;
       
+    case BLACKHOLE_BQ:
+      { 
+       StgBlockingQueue *bh = (StgBlockingQueue *)p;
+       (StgClosure *)bh->blocking_queue = 
+         evacuate((StgClosure *)bh->blocking_queue);
+       prev = &p->mut_link;
+       break;
+      }
+
     default:
       /* shouldn't have anything else on the mutables list */
       barf("scavenge_mutable_object: non-mutable object?");
@@ -1913,19 +2194,31 @@ scavenge_stack(StgPtr p, StgPtr stack_end)
          continue;
        } else {
          bdescr *bd = Bdescr((P_)frame->updatee);
-         ASSERT(type == BLACKHOLE || 
-                type == CAF_BLACKHOLE ||
-                type == BLACKHOLE_BQ);
+         step *step;
          if (bd->gen->no > N) { 
            if (bd->gen->no < evac_gen) {
              failed_to_evac = rtsTrue;
            }
            continue;
          }
-         to = copy(frame->updatee, BLACKHOLE_sizeW(), bd);
-         upd_evacuee(frame->updatee,to);
-         frame->updatee = to;
-         continue;
+         step = bd->step->to;
+         switch (type) {
+         case BLACKHOLE:
+         case CAF_BLACKHOLE:
+           to = copyPart(frame->updatee, BLACKHOLE_sizeW(), 
+                         sizeofW(StgHeader), step);
+           upd_evacuee(frame->updatee,to);
+           frame->updatee = to;
+           continue;
+         case BLACKHOLE_BQ:
+           to = copy(frame->updatee, BLACKHOLE_sizeW(), step);
+           upd_evacuee(frame->updatee,to);
+           frame->updatee = to;
+           evacuate_mutable((StgMutClosure *)to);
+           continue;
+         default:
+           barf("scavenge_stack: UPDATE_FRAME updatee");
+         }
        }
       }
 
@@ -2233,7 +2526,7 @@ static void
 threadLazyBlackHole(StgTSO *tso)
 {
   StgUpdateFrame *update_frame;
-  StgBlackHole *bh;
+  StgBlockingQueue *bh;
   StgPtr stack_end;
 
   stack_end = &tso->stack[tso->stack_size];
@@ -2247,20 +2540,21 @@ threadLazyBlackHole(StgTSO *tso)
       break;
 
     case UPDATE_FRAME:
-      bh = stgCast(StgBlackHole*,update_frame->updatee);
+      bh = (StgBlockingQueue *)update_frame->updatee;
 
       /* if the thunk is already blackholed, it means we've also
        * already blackholed the rest of the thunks on this stack,
        * so we can stop early.
+       *
+       * The blackhole made for a CAF is a CAF_BLACKHOLE, so they
+       * don't interfere with this optimisation.
        */
+      if (bh->header.info == &BLACKHOLE_info) {
+       return;
+      }
 
-      /* Don't for now: when we enter a CAF, we create a black hole on
-       * the heap and make the update frame point to it.  Thus the
-       * above optimisation doesn't apply.
-       */
-      if (bh->header.info != &BLACKHOLE_info
-         && bh->header.info != &BLACKHOLE_BQ_info
-         && bh->header.info != &CAF_BLACKHOLE_info) {
+      if (bh->header.info != &BLACKHOLE_BQ_info &&
+         bh->header.info != &CAF_BLACKHOLE_info) {
        SET_INFO(bh,&BLACKHOLE_info);
       }
 
@@ -2310,8 +2604,8 @@ threadSqueezeStack(StgTSO *tso)
    * added to the stack, rather than the way we see them in this
    * walk. (It makes the next loop less confusing.)  
    *
-   * Could stop if we find an update frame pointing to a black hole,
-   * but see comment in threadLazyBlackHole().
+   * Stop if we find an update frame pointing to a black hole 
+   * (see comment in threadLazyBlackHole()).
    */
   
   next_frame = NULL;
@@ -2320,6 +2614,10 @@ threadSqueezeStack(StgTSO *tso)
     frame->link = next_frame;
     next_frame = frame;
     frame = prev_frame;
+    if (get_itbl(frame)->type == UPDATE_FRAME
+       && frame->updatee->header.info == &BLACKHOLE_info) {
+        break;
+    }
   }
 
   /* Now, we're at the bottom.  Frame points to the lowest update
@@ -2383,12 +2681,12 @@ threadSqueezeStack(StgTSO *tso)
        /* Sigh.  It has one.  Don't lose those threads! */
          if (GET_INFO(updatee_keep) == BLACKHOLE_BQ_info) {
          /* Urgh.  Two queues.  Merge them. */
-         P_ keep_tso = ((StgBlackHole *)updatee_keep)->blocking_queue;
+         P_ keep_tso = ((StgBlockingQueue *)updatee_keep)->blocking_queue;
          
          while (keep_tso->link != END_TSO_QUEUE) {
            keep_tso = keep_tso->link;
          }
-         keep_tso->link = ((StgBlackHole *)updatee_bypass)->blocking_queue;
+         keep_tso->link = ((StgBlockingQueue *)updatee_bypass)->blocking_queue;
 
        } else {
          /* For simplicity, just swap the BQ for the BH */
@@ -2416,11 +2714,9 @@ threadSqueezeStack(StgTSO *tso)
       /* Do lazy black-holing.
        */
       if (is_update_frame) {
-       StgBlackHole *bh = (StgBlackHole *)frame->updatee;
-       if (bh->header.info != &BLACKHOLE_info
-           && bh->header.info != &BLACKHOLE_BQ_info
-           && bh->header.info != &CAF_BLACKHOLE_info
-           ) {
+       StgBlockingQueue *bh = (StgBlockingQueue *)frame->updatee;
+       if (bh->header.info != &BLACKHOLE_BQ_info &&
+           bh->header.info != &CAF_BLACKHOLE_info) {
          SET_INFO(bh,&BLACKHOLE_info);
        }
       }