diff --git a/ghc/rts/GC.c b/ghc/rts/GC.c
index 652131275c5fe4a2fab301010eee6b8f5e7e759a..17c056da882216e0d4627d8ab8737dba6734219b 100644
--- a/ghc/rts/GC.c
+++ b/ghc/rts/GC.c
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: GC.c,v 1.21 1999/01/27 16:41:14 simonm Exp $
+ * $Id: GC.c,v 1.22 1999/01/28 15:04:00 simonm Exp $
  *
  * Two-space garbage collector
  *
@@ -511,6 +511,9 @@ 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.
    */
@@ -523,8 +526,6 @@ void GarbageCollect(void (*get_roots)(void))
     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. */
     
@@ -563,71 +564,44 @@ void GarbageCollect(void (*get_roots)(void))
 	blocks = RtsFlags.GcFlags.minAllocAreaSize;
       }
     }
+    resizeNursery(blocks);
     
-    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>)
+     * If the user has given us a suggested heap size, adjust our
+     * allocation area to make best use of the memory available.
      */
 
-    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;
+      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 {
-	blocks = RtsFlags.GcFlags.minAllocAreaSize;
+	needed += g0->steps[1].n_blocks;
       }
 
-      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;
+      /* 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;
       }
-      g0s0->n_blocks = nursery_blocks = blocks;
+      
+      resizeNursery((nat)blocks);
     }
   }
 
@@ -667,29 +641,9 @@ void GarbageCollect(void (*get_roots)(void))
   scheduleFinalisers(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
diff --git a/ghc/rts/Storage.c b/ghc/rts/Storage.c
index 5117375f58b4da6b52a3da45b0c8e68c712dcc8e..1f080c58a75493d08b3d2293428d67e033fbf918 100644
--- a/ghc/rts/Storage.c
+++ b/ghc/rts/Storage.c
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: Storage.c,v 1.7 1999/01/26 16:16:30 simonm Exp $
+ * $Id: Storage.c,v 1.8 1999/01/28 15:04:02 simonm Exp $
  *
  * Storage manager front end
  *
@@ -14,6 +14,7 @@
 #include "MBlock.h"
 #include "gmp.h"
 #include "Weak.h"
+#include "Sanity.h"
 
 #include "Storage.h"
 #include "StoragePriv.h"
@@ -50,6 +51,11 @@ initStorage (void)
   step *step;
   generation *gen;
 
+  if (RtsFlags.GcFlags.heapSizeSuggestion > 
+      RtsFlags.GcFlags.maxHeapSize) {
+    barf("Suggested heap size (-H<size>) is larger than max. heap size (-M<size>)\n");
+  }
+
   initBlockAllocator();
   
   /* allocate generation info array */
@@ -171,6 +177,38 @@ allocNursery (bdescr *last_bd, nat blocks)
   return last_bd;
 }
 
+extern void
+resizeNursery ( nat blocks )
+{
+  bdescr *bd;
+
+  if (nursery_blocks == blocks) {
+    ASSERT(g0s0->n_blocks == blocks);
+    return;
+  }
+
+  else 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;
+}
+
 void
 exitStorage (void)
 {
@@ -344,6 +382,69 @@ stgDeallocForGMP (void *ptr STG_UNUSED,
     /* easy for us: the garbage collector does the dealloc'n */
 }
 
+/* -----------------------------------------------------------------------------
+   Stats and stuff
+   -------------------------------------------------------------------------- */
+
+/* Approximate the amount of live data in the heap.  To be called just
+ * after garbage collection (see GarbageCollect()).
+ */
+extern lnat 
+calcLive(void)
+{
+  nat g, s;
+  lnat live = 0;
+  step *step;
+
+  if (RtsFlags.GcFlags.generations == 1) {
+    live = g0s0->to_blocks * BLOCK_SIZE_W + 
+      ((lnat)g0s0->hp_bd->free - (lnat)g0s0->hp_bd->start) / sizeof(W_);
+  }
+
+  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).
+	 */
+      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_);
+    }
+  }
+  return live;
+}
+
+/* Approximate the number of blocks that will be needed at the next
+ * garbage collection.
+ *
+ * Assume: all data currently live will remain live.  Steps that will
+ * be collected next time will therefore need twice as many blocks
+ * since all the data will be copied.
+ */
+extern lnat 
+calcNeeded(void)
+{
+  lnat needed = 0;
+  nat g, s;
+  step *step;
+
+  for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
+    for (s = 0; s < generations[g].n_steps; s++) {
+      if (g == 0 && s == 0) { continue; }
+      step = &generations[g].steps[s];
+      if (generations[g].steps[0].n_blocks > generations[g].max_blocks) {
+	needed += 2 * step->n_blocks;
+      } else {
+	needed += step->n_blocks;
+      }
+    }
+  }
+  return needed;
+}
+
 /* -----------------------------------------------------------------------------
    Debugging
 
@@ -409,4 +510,33 @@ memInventory(void)
 #endif
 }
 
+/* Full heap sanity check. */
+
+extern void
+checkSanity(nat N)
+{
+  nat g, s;
+
+  if (RtsFlags.GcFlags.generations == 1) {
+    checkHeap(g0s0->to_space, NULL);
+    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; }
+	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++) {
+	checkHeap(generations[g].steps[s].blocks,
+		  generations[g].steps[s].blocks->start);
+	checkChain(generations[g].steps[s].large_objects);
+      }
+    }
+    checkFreeListSanity();
+  }
+}
+
 #endif
diff --git a/ghc/rts/StoragePriv.h b/ghc/rts/StoragePriv.h
index defe1425de16a66f99f50644c5c7a6e9a8a2f759..1c3266c04c0e8191048c5e4440626a10bf2f0b6c 100644
--- a/ghc/rts/StoragePriv.h
+++ b/ghc/rts/StoragePriv.h
@@ -1,5 +1,5 @@
 /* -----------------------------------------------------------------------------
- * $Id: StoragePriv.h,v 1.5 1999/01/19 17:06:05 simonm Exp $
+ * $Id: StoragePriv.h,v 1.6 1999/01/28 15:04:02 simonm Exp $
  *
  * Internal Storage Manger Interface
  *
@@ -106,7 +106,11 @@ extern nat nursery_blocks;
 extern nat alloc_blocks;
 extern nat alloc_blocks_lim;
 
-extern bdescr *allocNursery (bdescr *last_bd, nat blocks);
+extern bdescr *allocNursery ( bdescr *last_bd, nat blocks );
+extern void resizeNursery ( nat blocks );
+
+extern lnat calcLive( void );
+extern lnat calcNeeded( void );
 
 static inline void
 dbl_link_onto(bdescr *bd, bdescr **list)
@@ -127,6 +131,7 @@ dbl_link_onto(bdescr *bd, bdescr **list)
 
 #ifdef DEBUG
 extern void memInventory(void);
+extern void checkSanity(nat N);
 #endif
 
 #endif /* STORAGEPRIV_H */