[project @ 1999-04-27 10:06:47 by sewardj]
[ghc-hetmet.git] / ghc / interpreter / optimise.c
index a891389..313116c 100644 (file)
@@ -7,8 +7,8 @@
  * Hugs version 1.4, December 1997
  *
  * $RCSfile: optimise.c,v $
- * $Revision: 1.4 $
- * $Date: 1999/03/09 14:51:09 $
+ * $Revision: 1.5 $
+ * $Date: 1999/04/27 10:06:57 $
  * ------------------------------------------------------------------------*/
 
 #include "prelude.h"
 #include "backend.h"
 #include "connect.h"
 #include "errors.h"
+#include "link.h"
+#include "Assembler.h"
+
+/* #define DEBUG_OPTIMISE */
 
 /* --------------------------------------------------------------------------
  * Local functions
  * ------------------------------------------------------------------------*/
 
-static StgAtom    optimiseAtom    Args((StgAtom));
-static StgVar     optimiseVar     Args((StgVar));
-static StgCaseAlt optimiseAlt     Args((StgCaseAlt));
-static StgPrimAlt optimisePrimAlt Args((StgPrimAlt));
-static StgExpr    optimiseExpr    Args((StgExpr));
+Int nLoopBreakersInlined;
+Int nLetvarsInlined;
+Int nTopvarsInlined;
+Int nCaseOfLet;
+Int nCaseOfCase;
+Int nCaseOfPrimCase;
+Int nCaseOfCon;
+Int nCaseOfOuter;
+Int nLetBindsDropped;
+Int nLetrecGroupsDropped;
+Int nLambdasMerged;
+Int nCaseDefaultsDropped;
+Int nAppsMerged;
+Int nLetsFloatedOutOfFn;
+Int nLetsFloatedIntoCase;
+Int nCasesFloatedOutOfFn;
+Int nBetaReductions;
+
+Int nTotSizeIn;
+Int nTotSizeOut;
+
+Int  rDepth;
+Bool copyInTopvar;
+Bool inDBuilder;
+
+static void local optimiseTopBind( StgVar v );
+
+typedef
+   enum {
+      CTX_SCRUT,
+      CTX_OTHER
+   }
+   InlineCtx;
+
+/* Exactly like whatIs except it avoids a fn call for STG tags */
+#define whatIsStg(xx) ((isPair(xx) ? (isTag(fst(xx)) ? fst(xx) : AP) : whatIs(xx)))
+
 
 /* --------------------------------------------------------------------------
- * A simple optimiser
+ * Transformation stats
  * ------------------------------------------------------------------------*/
 
-static StgAtom optimiseAtom(StgAtom a)
+void initOptStats ( void )
 {
-    switch (whatIs(a)) {
-    case STGVAR:
-            return optimiseVar(a);
-    /* Note that NAMEs have no free vars. */
-    default:
-            return a;
-    }
+   nLoopBreakersInlined  = 0;
+   nLetvarsInlined       = 0;
+   nTopvarsInlined       = 0;
+   nCaseOfLet            = 0;
+   nCaseOfCase           = 0;
+   nCaseOfPrimCase       = 0;
+   nCaseOfCon            = 0;
+   nCaseOfOuter          = 0;
+   nLetBindsDropped      = 0;
+   nLetrecGroupsDropped  = 0;
+   nLambdasMerged        = 0;
+   nCaseDefaultsDropped  = 0;
+   nAppsMerged           = 0;
+   nLetsFloatedOutOfFn   = 0;
+   nLetsFloatedIntoCase  = 0;
+   nCasesFloatedOutOfFn  = 0;
+   nBetaReductions       = 0;
+   nTotSizeIn            = 0;
+   nTotSizeOut           = 0;
+}
+
+void printOptStats ( FILE* f )
+{
+   fflush(stdout); fflush(stderr); fflush(f);
+   fprintf(f, "\n\n" );
+   fprintf(f, "Inlining:     topvar %-5d        letvar %-5d"
+              "      loopbrkr %-5d      betaredn %-5d\n",
+              nTopvarsInlined, nLetvarsInlined, nLoopBreakersInlined, 
+              nBetaReductions );
+   fprintf(f, "Case-of-:        let %-5d          case %-5d"
+              "           con %-5d         case# %-5d\n",
+              nCaseOfLet, nCaseOfCase, nCaseOfCon, nCaseOfPrimCase );
+   fprintf(f, "Dropped:     letbind %-5d      letgroup %-5d"
+              "       default %-5d\n",
+              nLetBindsDropped, nLetrecGroupsDropped, nCaseDefaultsDropped );
+   fprintf(f, "Merges:       lambda %-5d           app %-5d\n",
+              nLambdasMerged, nAppsMerged  );
+   fprintf(f, "Fn-float:        let %-5d          case %-5d\n",
+              nLetsFloatedOutOfFn, nCasesFloatedOutOfFn );
+   fprintf(f, "Misc:     case-outer %-5d let-into-case %-5d\n",
+              nCaseOfOuter, nLetsFloatedIntoCase );
+   fprintf(f, "total size:       in %-5d           out %-5d\n",
+              nTotSizeIn, nTotSizeOut );
+   fprintf(f, "\n" );
+}
+
+
+/* --------------------------------------------------------------------------
+ * How big is this STG tree (viz (primarily), do I want to inline it?)
+ * ------------------------------------------------------------------------*/
+
+Int stgSize_list ( List es )
+{
+   Int n = 0;
+   for (; nonNull(es); es=tl(es)) n += stgSize(hd(es));
+   return n;
+}
+
+Int stgSize ( StgExpr e )
+{
+   List xs;
+   Int n = 1;
+
+   if (isNull(e)) return 0;
+
+   switch(whatIsStg(e)) {
+      case STGVAR:
+         break;
+      case LETREC:
+         for (xs = stgLetBinds(e); nonNull(xs);xs=tl(xs)) 
+            n += stgSize(stgVarBody(hd(xs)));
+         n += stgSize(stgLetBody(e));
+         break;
+      case LAMBDA:
+         n += stgSize(stgLambdaBody(e));
+         break;
+      case CASE:
+         n += stgSize_list(stgCaseAlts(e));
+         n += stgSize(stgCaseScrut(e));
+         break;
+      case PRIMCASE:
+         n += stgSize_list(stgPrimCaseAlts(e));
+         n += stgSize(stgPrimCaseScrut(e));
+         break;
+      case STGAPP:
+         n += stgSize_list(stgAppArgs(e));
+         n += stgSize(stgAppFun(e));
+         break;
+      case STGPRIM:
+         n += stgSize_list(stgPrimArgs(e));
+         n += stgSize(stgPrimOp(e));
+         break;
+      case STGCON:
+         n += stgSize_list(stgConArgs(e));
+         n += stgSize(stgConCon(e));
+         break;
+      case DEEFALT:
+         n  = stgSize(stgDefaultBody(e));
+         break;
+      case CASEALT:
+         n  = stgSize(stgCaseAltBody(e));
+         break;
+      case PRIMALT:
+         n  = stgSize(stgPrimAltBody(e));
+         break;
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case CHARCELL:
+      case FLOATCELL:
+      case BIGCELL:
+      case NAME:
+      case TUPLE:
+         break;
+      default:
+         fprintf(stderr, "sizeStg: unknown stuff %d\n",whatIsStg(e));
+         assert(0);
+   }
+   return n;
+}
+
+
+/* --------------------------------------------------------------------------
+ * Stacks of pairs of collectable things.  Used to implement associations.
+ * cloneStg() uses its stack to map old var names to new ones.
+ * ------------------------------------------------------------------------*/
+
+#define M_PAIRS 400
+#define SP_NOT_IN_USE (-123456789)
+
+typedef
+   struct { Cell pfst; Cell psnd; } 
+   StgPair;
+
+static Int     spClone;
+static StgPair pairClone[M_PAIRS];
+
+void markPairs ( void )
+{
+   Int i;
+   if (spClone != SP_NOT_IN_USE) {
+      for (i = 0; i <= spClone; i++) {
+         mark(pairClone[i].pfst);
+         mark(pairClone[i].psnd);
+      }
+   }
+}
+
+void pushClone ( Cell a, Cell b )
+{
+   spClone++;
+   if (spClone >= M_PAIRS) internal("pushClone -- M_PAIRS too small");
+   pairClone[spClone].pfst = a;
+   pairClone[spClone].psnd = b;
+}
+
+void dropClone ( void )
+{
+   if (spClone < 0) internal("dropClone");
+   spClone--;
+}
+
+Cell findClone ( Cell x )
+{
+   Int i;
+   for (i = spClone; i >= 0; i--)
+      if (pairClone[i].pfst == x)
+         return pairClone[i].psnd;
+   return NIL;
+}
+
+
+/* --------------------------------------------------------------------------
+ * Cloning of STG trees
+ * ------------------------------------------------------------------------*/
+
+/* Clone v to create a new var.  Works for both StgVar and StgPrimVar. */
+StgVar cloneStgVar ( StgVar v )
+{
+  return ap(STGVAR,triple(stgVarBody(v),stgVarRep(v),NIL));
 }
 
-static StgVar optimiseVar(StgVar v)
+
+/* For each StgVar in origVars, make a new one with cloneStgVar,
+   and push the (old,new) pair on the clone pair stack.  Returns
+   the list of new vars.
+*/
+List cloneStg_addVars ( List origVars )
+{
+   List newVars = NIL;
+   while (nonNull(origVars)) {
+      StgVar newv = cloneStgVar(hd(origVars));
+      pushClone ( hd(origVars), newv );
+      newVars    = cons(newv,newVars);
+      origVars   = tl(origVars);
+   }
+   newVars = rev(newVars);
+   return newVars;
+}
+
+
+void cloneStg_dropVars ( List vs )
+{
+   for (; nonNull(vs); vs=tl(vs)) 
+      dropClone();
+}
+
+
+/* Print the clone pair stack.  Just for debugging purposes. */
+void ppCloneEnv ( char* s )
+{
+   Int i;
+   fflush(stdout);fflush(stderr);
+   printf ( "\nenv-%s\n", s );
+   for (i = 0; i <= spClone; i++) {
+      printf ( "\t" ); 
+      ppStgExpr(pairClone[i].pfst);
+      ppStgExpr(pairClone[i].psnd);
+      printf ( "\n" );
+   };
+   printf ( "vne-%s\n", s );
+}
+
+
+StgExpr cloneStg ( StgExpr e )
 {
-    StgRhs rhs = stgVarBody(v);
-fprintf(stderr,"optimiseVar ");printStg(stderr,v);fprintf(stderr,"\n");
-    /* short circuit: let x = y in ...x... --> let x = y in ...y... */
-    if (whatIs(rhs) == STGVAR && rhs != v) {
-       StgVar v1 = rhs;
-fprintf(stderr, "dumpable\n");
+   List xs, newvs;
+   StgVar newv;
+   StgExpr t;
 
-       /* find last variable in chain */
-       rhs = stgVarBody(v1);
-       while (whatIs(rhs) == STGVAR
-              && rhs != v  /* infinite loop */
-              ) {
-           v1 = rhs;
-           rhs = stgVarBody(rhs);
-       }
+   switch(whatIsStg(e)) {
+      case STGVAR:
+         newv = findClone(e);
+         if (nonNull(newv)) return newv; else return e;
+      case LETREC:
+         newvs = cloneStg_addVars ( stgLetBinds(e) );
+         for (xs = newvs; nonNull(xs);xs=tl(xs)) 
+            stgVarBody(hd(xs)) = cloneStg(stgVarBody(hd(xs)));
+         t = mkStgLet(newvs,cloneStg(stgLetBody(e)));
+         cloneStg_dropVars ( stgLetBinds(e) );
+         return t;
+      case LAMBDA:
+         newvs = cloneStg_addVars ( stgLambdaArgs(e) );
+         t = mkStgLambda(newvs, cloneStg(stgLambdaBody(e)));
+         cloneStg_dropVars ( stgLambdaArgs(e) );
+         return t;
+      case CASE:
+         xs = dupList(stgCaseAlts(e)); 
+         mapOver(cloneStg,xs);
+         return mkStgCase(cloneStg(stgCaseScrut(e)),xs);
+      case PRIMCASE:
+         xs = dupList(stgPrimCaseAlts(e));
+         mapOver(cloneStg,xs);
+         return mkStgPrimCase(cloneStg(stgPrimCaseScrut(e)),xs);
+      case STGAPP:
+         xs = dupList(stgAppArgs(e));
+         mapOver(cloneStg,xs);
+         return mkStgApp(cloneStg(stgAppFun(e)),xs);
+      case STGPRIM:
+         xs = dupList(stgPrimArgs(e));
+         mapOver(cloneStg,xs);
+         return mkStgPrim(cloneStg(stgPrimOp(e)),xs);
+      case STGCON:
+         xs = dupList(stgConArgs(e));
+         mapOver(cloneStg,xs);
+         return mkStgCon(cloneStg(stgConCon(e)),xs);
+      case DEEFALT:
+         newv = cloneStgVar(stgDefaultVar(e));
+         pushClone ( stgDefaultVar(e), newv );
+         t = mkStgDefault(newv,cloneStg(stgDefaultBody(e)));
+         dropClone();
+         return t;
+      case CASEALT:
+         newvs = cloneStg_addVars ( stgCaseAltVars(e) );
+         t = mkStgCaseAlt(stgCaseAltCon(e),newvs,
+                          cloneStg(stgCaseAltBody(e)));
+         cloneStg_dropVars ( stgCaseAltVars(e) );
+         return t;
+      case PRIMALT:
+         newvs = cloneStg_addVars ( stgPrimAltVars(e) );
+         t = mkStgPrimAlt(newvs, cloneStg(stgPrimAltBody(e)));
+         cloneStg_dropVars ( stgPrimAltVars(e) );
+         return t;
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case BIGCELL:
+      case CHARCELL:
+      case FLOATCELL:
+      case NAME:
+      case TUPLE:
+         return e;
+      default:
+         fprintf(stderr, "cloneStg: unknown stuff %d\n",whatIsStg(e));
+         assert(0);
+   }
+}
+
+
+/* Main entry point.  Checks against re-entrant use. */
+StgExpr cloneStgTop ( StgExpr e )
+{
+   StgExpr res;
+   if (spClone != SP_NOT_IN_USE) 
+      internal("cloneStgTop");
+   spClone = -1;
+   res = cloneStg ( e );
+   assert(spClone == -1);
+   spClone = SP_NOT_IN_USE;
+   return res;
+}
+
+
+
+/* --------------------------------------------------------------------------
+ * Sets of StgVars, used by the strongly-connected-components machinery.  
+ * Represented as an array of variables.  The vars
+ * must be in strictly nondecreasing order.  Each value may appear
+ * more than once, so as to make deletion relatively cheap.
+
+ * After a garbage collection happens, the values may have changed,
+ * so the array will need to be sorted.
+
+ * Using a binary search, membership costs O(log N).  Union and
+ * intersection cost O(N + M).  Deletion of a single element costs
+ * O(N) in the worst case, although if it happens infrequently
+ * compared to the other ops, it should asymptotically approach O(1).
+ * ------------------------------------------------------------------------*/
+
+#define M_VAR_SETS 4000
+#define MIN_VAR_SET_SIZE 4
+#define M_UNION_TMP 20000
+
+typedef
+   struct {
+      Int   nextfree;
+      Bool  inUse;
+      Int   size;
+      Int   used;
+      Cell* vs;
+   }
+   StgVarSetRec;
+
+typedef Int StgVarSet;
+
+StgVarSetRec varSet[M_VAR_SETS];
+Int varSet_nfree;
+Int varSet_nextfree;
+Cell union_tmp[M_UNION_TMP];
+
+#if 0 /* unused since unnecessary */
+/* Shellsort set elems to restore representation invariants */
+static Int shellCells_incs[10] 
+   = { 1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524 };
+static void shellCells ( Cell* a, Int lo, Int hi )
+{
+   Int i, j, h, N, hp;
+   Cell v;
+
+   N = hi - lo + 1; if (N < 2) return;
+   hp = 0; 
+   while (hp < 10 && shellCells_incs[hp] < N) hp++; hp--;
+
+   for (; hp >= 0; hp--) {
+      h = shellCells_incs[hp];
+      i = lo + h;
+      while (1) {
+         if (i > hi) break;
+         v = a[i];
+         j = i;
+         while (a[j-h] > v) {
+            a[j] = a[j-h]; j = j - h;
+            if (j <= (lo + h - 1)) break;
+         }
+         a[j] = v; i++;
+      }
+   }
+}
+#endif
+
+/* check that representation invariant still holds */
+static void checkCells ( Cell* a, Int lo, Int hi )
+{
+   Int i;
+   for (i = lo; i < hi; i++)
+      if (a[i] > a[i+1])
+         internal("checkCells");
+}
+
+
+/* Mark set contents for GC */
+void markStgVarSets ( void )
+{
+   Int i, j;
+   for (i = 0; i < M_VAR_SETS; i++)
+      if (varSet[i].inUse)
+         for (j = 0; j < varSet[i].used; j++)
+            mark(varSet[i].vs[j]);
+}
+
+
+/* Check representation invariants after GC */
+void checkStgVarSets ( void )
+{
+   Int i;
+   for (i = 0; i < M_VAR_SETS; i++)
+      if (varSet[i].inUse)
+         checkCells ( varSet[i].vs, 0, varSet[i].used-1 );
+}
+
+
+/* Allocate a set of a given size */
+StgVarSet allocStgVarSet ( Int size )
+{
+   Int i, j;
+   if (varSet_nextfree == -1)
+      internal("allocStgVarSet -- run out of var sets");
+   i = varSet_nextfree;
+   varSet_nextfree = varSet[i].nextfree;
+   varSet[i].inUse = TRUE;
+   j = MIN_VAR_SET_SIZE;
+   while (j <= size) j *= 2;
+   varSet[i].used = 0;
+   varSet[i].size = j;
+   varSet[i].vs = malloc(j * sizeof(StgVar) );
+   if (!varSet[i].vs) 
+      internal("allocStgVarSet -- can't malloc memory");
+   varSet_nfree--;
+   return i;
+}
 
-       /* Make all variables in chain point to v1
-        * This makes sure we always resolve cycles the same way
-        * as well as making things faster if we call optimiseVar again
+
+/* resize (upwards) */
+void resizeStgVarSet ( StgVarSet s, Int size )
+{
+   Cell* tmp;
+   Cell* tmp2;
+   Int i;
+   Int j = MIN_VAR_SET_SIZE;
+   while (j <= size) j *= 2;
+   if (j < varSet[s].size) return;
+   tmp = varSet[s].vs;
+   tmp2 = malloc( j * sizeof(StgVar) );
+   if (!tmp2) internal("resizeStgVarSet -- can't malloc memory");
+   varSet[s].vs = tmp2;
+   for (i = 0; i < varSet[s].used; i++)
+      tmp2[i] = tmp[i];
+   free(tmp);
+}
+
+
+/* Deallocation ... */
+void freeStgVarSet ( StgVarSet s )
+{
+   if (s < 0 || s >= M_VAR_SETS || 
+       !varSet[s].inUse || !varSet[s].vs)
+      internal("freeStgVarSet");
+   free(varSet[s].vs);
+   varSet[s].inUse = FALSE;
+   varSet[s].vs = NULL;
+   varSet[s].nextfree = varSet_nextfree;
+   varSet_nextfree = s;
+   varSet_nfree++;
+}
+
+
+/* Initialisation */
+void initStgVarSets ( void )
+{
+   Int i;
+   for (i = M_VAR_SETS-1; i >= 0; i--) {
+      varSet[i].inUse = FALSE;
+      varSet[i].vs = NULL;
+      varSet[i].nextfree = i+1;
+   }
+   varSet[M_VAR_SETS-1].nextfree = -1;
+   varSet_nextfree = 0;
+   varSet_nfree = M_VAR_SETS;
+}
+
+
+/* Find a var using binary search */
+Int findInStgVarSet ( StgVarSet s, StgVar v )
+{
+   Int lo, mid, hi;
+   lo = 0;
+   hi = varSet[s].used-1;
+   while (1) {
+      if (lo > hi) return -1;
+      mid = (hi+lo)/2;
+      if (varSet[s].vs[mid] == v) return mid;
+      if (varSet[s].vs[mid] < v) lo = mid+1; else hi = mid-1;
+   }
+}
+
+
+Bool elemStgVarSet ( StgVarSet s, StgVar v )
+{
+   return findInStgVarSet(s,v) != -1;
+}
+
+void ppSet ( StgVarSet s )
+{
+   Int i;
+   fprintf(stderr, "{ ");
+   for (i = 0; i < varSet[s].used; i++)
+      fprintf(stderr, "%d ", varSet[s].vs[i] );
+   fprintf(stderr, "}\n" );
+}
+
+
+void deleteFromStgVarSet ( StgVarSet s, StgVar v )
+{
+   Int i, j;
+   i = findInStgVarSet(s,v);
+   if (i == -1) return;
+   j = varSet[s].used-1;
+   for (; i < j; i++) varSet[s].vs[i] = varSet[s].vs[i+1];
+   varSet[s].used--;
+}
+
+
+void singletonStgVarSet ( StgVarSet s, StgVar v )
+{
+   varSet[s].used  = 1;
+   varSet[s].vs[0] = v;
+}
+
+
+void emptyStgVarSet ( StgVarSet s )
+{
+   varSet[s].used = 0;
+}
+
+
+void copyStgVarSets ( StgVarSet dst, StgVarSet src )
+{
+   Int i;
+   varSet[dst].used = varSet[src].used;
+   for (i = 0; i < varSet[dst].used; i++)
+      varSet[dst].vs[i] = varSet[src].vs[i];
+}
+
+
+Int sizeofVarSet ( StgVarSet s )
+{
+   return varSet[s].used;
+}
+
+
+void unionStgVarSets ( StgVarSet dst, StgVarSet src )
+{
+   StgVar v1;
+   Int pd, ps, i, res_used, tmp_used, dst_used, src_used;
+   StgVar* dst_vs;
+   StgVar* src_vs;
+   StgVar* tmp_vs;
+
+   dst_vs = varSet[dst].vs;
+
+   /* fast track a common (~ 50%) case */
+   if (varSet[src].used == 1) {
+      v1 = varSet[src].vs[0];
+      pd = findInStgVarSet(dst,v1);
+      if (pd != -1) return;
+      if (varSet[dst].used < varSet[dst].size) {
+         i = varSet[dst].used;
+         while (i > 0 && dst_vs[i-1] > v1) {
+            dst_vs[i] = dst_vs[i-1];
+            i--;
+         }
+         dst_vs[i] = v1;
+         varSet[dst].used++;
+         return;
+      }
+   }
+
+   res_used = varSet[dst].used + varSet[src].used;
+   if (res_used > M_UNION_TMP) 
+      internal("unionStgVarSets -- M_UNION_TMP too small");
+
+   resizeStgVarSet(dst,res_used);
+   dst_vs = varSet[dst].vs;
+   src_vs = varSet[src].vs;
+   tmp_vs = union_tmp;
+   tmp_used = 0;
+   dst_used = varSet[dst].used;
+   src_used = varSet[src].used;
+
+   /* merge the two sets into tmp */
+   pd = ps = 0;
+   while (pd < dst_used || ps < src_used) {
+      if (pd == dst_used)
+         tmp_vs[tmp_used++] = src_vs[ps++];
+      else
+      if (ps == src_used)
+         tmp_vs[tmp_used++] = dst_vs[pd++];
+      else {
+         StgVar vald = dst_vs[pd];
+         StgVar vals = src_vs[ps];
+         if (vald < vals)
+            tmp_vs[tmp_used++] = vald, pd++;
+         else
+         if (vald > vals)
+            tmp_vs[tmp_used++] = vals, ps++;
+         else
+            tmp_vs[tmp_used++] = vals, ps++, pd++;
+      }
+   }
+
+   /* copy setTmp back to dst */
+   varSet[dst].used = tmp_used;
+   for (i = 0; i < tmp_used; i++) {
+      dst_vs[i] = tmp_vs[i];
+   }
+}
+
+
+
+/* --------------------------------------------------------------------------
+ * Strongly-connected-components machinery for STG let bindings.
+ * Arranges let bindings in minimal mutually recursive groups, and
+ * then throws away any groups not referred to in the body of the let.
+ *
+ * How it works: does a bottom-up sweep of the tree.  Each call returns
+ * the set of variables free in the tree.  All nodes except LETREC are
+ * boring.  
+ * 
+ * When 'let v1=e1 .. vn=en in e' is encountered:
+ * -- recursively make a call on e.  This returns fvs(e) and scc-ifies
+ *    inside e as well.
+ * -- do recursive calls for e1 .. en too, giving fvs(e1) ... fvs(en).
+ *
+ * Then, using fvs(e1) ... fvs(en), the dependancy graph for v1 ... vn
+ * can be cheaply computed.  Using that, compute the strong components
+ * and rearrange the let binding accordingly.
+ * Finally, for each of the strong components, we can use fvs(en) to 
+ * cheaply determine if the component is used in the body of the let,
+ * and if not, it can be omitted.
+ *
+ * oaScc destructively modifies the tree -- when it gets to a let --
+ * we need to pass the address of the expression to scc, not the
+ * (more usual) heap index of it.
+ *
+ * The main requirement of this algorithm is an efficient implementation
+ * of sets of variables.  Because there is no name shadowing in these
+ * trees, either mentioned-sets or free-sets would be ok, although 
+ * free sets are presumably smaller.
+ * ------------------------------------------------------------------------*/
+
+
+#define  SCC             stgScc          /* make scc algorithm for StgVars */
+#define  LOWLINK         stgLowlink
+#define  DEPENDS(t)      thd3(t)
+#define  SETDEPENDS(c,v) thd3(c)=v
+#include "scc.c"
+#undef   SETDEPENDS
+#undef   DEPENDS
+#undef   LOWLINK
+#undef   SCC
+
+
+StgVarSet oaScc ( StgExpr* e_orig )
+{
+   Bool grpUsed;
+   StgExpr e;
+   StgVarSet e_fvs, s1, s2;
+   List bs, bs2, bs3, bsFinal, augs, augsL;
+
+   bs=bs2=bs3=bsFinal=augs=augsL=e_fvs=s1=s2=e=NIL;
+   grpUsed=FALSE;
+
+   e = *e_orig;
+
+   //fprintf(stderr,"\n==================\n");
+   //ppStgExpr(*e_orig);
+   //fprintf(stderr,"\n\n");fflush(stderr);fflush(stdout);
+
+
+   switch(whatIsStg(e)) {
+      case LETREC:
+         /* first, recurse into the let body */
+         e_fvs = oaScc(&stgLetBody(*e_orig));
+
+         /* Make bs :: [StgVar] and e :: Stgexpr. */
+         bs = stgLetBinds(e);
+         e  = stgLetBody(e);
+
+         /* make augs :: [(StgVar,fvs(bindee),NIL)] */
+         augs = NIL;
+         for (; nonNull(bs); bs=tl(bs)) {
+            StgVarSet fvs_bindee = oaScc(&stgVarBody(hd(bs)));
+            augs = cons( triple(hd(bs),mkInt(fvs_bindee),NIL), augs );
+         }
+
+        bs2=bs3=bsFinal=augsL=s1=s2=NIL;
+
+         /* In each of the triples in aug, replace the NIL field with 
+            a list of the let-bound vars appearing in the bindee.
+            ie, construct the adjacency list for the graph. 
+            giving 
+            augs :: [(StgVar,fvs(bindee),[pointers-back-to-this-list-of-pairs])]
+         */
+         for (bs=augs;nonNull(bs);bs=tl(bs)) {
+            augsL = NIL;
+            for (bs2=augs;nonNull(bs2);bs2=tl(bs2))
+               if (elemStgVarSet( intOf(snd3(hd(bs))), fst3(hd(bs2)) ))
+                  augsL = cons(hd(bs2),augsL);
+            thd3(hd(bs)) = augsL;
+         }
+
+        bs2=bs3=bsFinal=augsL=s1=s2=NIL;
+
+         /* Do the Biz.  
+            augs becomes :: [[(StgVar,fvs(bindee),aux_info_field)]] */
+         augs = stgScc(augs);
+
+         /* work backwards through augs, reconstructing the expression,
+            dumping any unused groups as you go.
         */
-       while (v != v1) {
-           StgRhs r = stgVarBody(v);
-           assert(whatIs(r) == STGVAR);
-           stgVarBody(v) = v1;
-           v = r;
-       }
-       return v1;
-    }
-    return v;
+         bsFinal = NIL;
+         for (augs=rev(augs); nonNull(augs); augs=tl(augs)) {
+            bs2 = NIL;
+            for (augsL=hd(augs);nonNull(augsL); augsL=tl(augsL))
+               bs2 = cons(fst3(hd(augsL)),bs2);
+            grpUsed = FALSE;
+            for (bs3=bs2;nonNull(bs3);bs3=tl(bs3))
+               if (elemStgVarSet(e_fvs,hd(bs3))) { grpUsed=TRUE; break; }
+            if (grpUsed) {
+               //e = mkStgLet(bs2,e);
+               bsFinal = dupOnto(bs2,bsFinal);
+               for (augsL=hd(augs);nonNull(augsL);augsL=tl(augsL)) {
+                  unionStgVarSets(e_fvs, intOf(snd3(hd(augsL))) );
+                  freeStgVarSet(intOf(snd3(hd(augsL))));
+               }
+            } else {
+               nLetrecGroupsDropped++;
+               for (augsL=hd(augs);nonNull(augsL);augsL=tl(augsL)) {
+                  freeStgVarSet(intOf(snd3(hd(augsL))));
+               }
+            }
+         }
+         //*e_orig = e;
+         *e_orig = mkStgLet(bsFinal,e);
+         return e_fvs;
+
+      case LAMBDA:
+         s1 = oaScc(&stgLambdaBody(e));
+         for (bs=stgLambdaArgs(e);nonNull(bs);bs=tl(bs))
+            deleteFromStgVarSet(s1,hd(bs));
+         return s1;
+      case CASE:
+         s1 = oaScc(&stgCaseScrut(e));
+         for (bs=stgCaseAlts(e);nonNull(bs);bs=tl(bs)) {
+            s2 = oaScc(&hd(bs));
+            unionStgVarSets(s1,s2);
+            freeStgVarSet(s2);
+         }
+         return s1;
+      case PRIMCASE:
+         s1 = oaScc(&stgPrimCaseScrut(e));
+         for (bs=stgPrimCaseAlts(e);nonNull(bs);bs=tl(bs)) {
+            s2 = oaScc(&hd(bs));
+            unionStgVarSets(s1,s2);
+            freeStgVarSet(s2);
+         }
+         return s1;
+      case STGAPP:
+         s1 = oaScc(&stgAppFun(e));
+         for (bs=stgAppArgs(e);nonNull(bs);bs=tl(bs)) {
+            s2 = oaScc(&hd(bs));
+            unionStgVarSets(s1,s2);
+            freeStgVarSet(s2);
+         }
+         return s1;
+      case STGPRIM:
+         s1 = oaScc(&stgPrimOp(e));
+         for (bs=stgPrimArgs(e);nonNull(bs);bs=tl(bs)) {
+            s2 = oaScc(&hd(bs));
+            unionStgVarSets(s1,s2);
+            freeStgVarSet(s2);
+         }
+         return s1;
+      case STGCON:
+         s1 = allocStgVarSet(0);
+         for (bs=stgPrimArgs(e);nonNull(bs);bs=tl(bs)) {
+            s2 = oaScc(&hd(bs));
+            unionStgVarSets(s1,s2);
+            freeStgVarSet(s2);
+         }
+         return s1;
+      case CASEALT:
+         s1 = oaScc(&stgCaseAltBody(e));
+         for (bs=stgCaseAltVars(e);nonNull(bs);bs=tl(bs))
+            deleteFromStgVarSet(s1,hd(bs));
+         return s1;
+      case DEEFALT:
+         s1 = oaScc(&stgDefaultBody(e));
+         deleteFromStgVarSet(s1,stgDefaultVar(e));
+         return s1;
+      case PRIMALT:
+         s1 = oaScc(&stgPrimAltBody(e));
+         for (bs=stgPrimAltVars(e);nonNull(bs);bs=tl(bs))
+            deleteFromStgVarSet(s1,hd(bs));
+         return s1;
+      case STGVAR:
+         s1 = allocStgVarSet(1);
+         singletonStgVarSet(s1,e);
+         return s1;
+      case NAME:
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case BIGCELL:
+      case CHARCELL:
+      case FLOATCELL:
+         return allocStgVarSet(0);
+         break;
+      default:
+         fprintf(stderr, "oaScc: unknown stuff %d\n",whatIsStg(e));
+         assert(0);
+   }
 }
 
-void optimiseBind( StgVar v )
+
+
+/* --------------------------------------------------------------------------
+ * Occurrence analyser.  Marks each let-bound var with the number of times
+ * it is used, or some number >= OCC_IN_LAMBDA if it is used inside a lambda.
+ *
+ * Firstly, oaPre traverses the tree, attaching a mutable INT cell to each
+ * let bound var, and NIL-ing the counts on all other vars.
+ *
+ * Then oaCount traveses the tree.  Because variables are represented by
+ * pointers in the heap, we can just increment the count field of each
+ * variable we see.  However, to deal with lambdas, the Hugs stack holds
+ * all let-bound variables currently in scope, and the uppermost portion
+ * of the stack, stack(spBase .. sp) inclusive, denotes the variables
+ * introduced into scope since the nearest enclosing lambda.  When a 
+ * let-bound var is seen, we search stack(spBase .. sp).  If it appears
+ * there, no lambda exists between the binding site and this usage of the
+ * var, so we can safely increment its use.  Otherwise, we must set it to
+ * OCC_IN_LAMBDA.
+ *
+ * When passing a lambda, spBase is set to sp+1, so as to effectively
+ * empty the set of vars-bound-since-the-latest-lambda.
+ * 
+ * Because oaPre pre-annotates the tree with mutable INT cells, oaCount
+ * doesn't allocate any heap at all.
+ * ------------------------------------------------------------------------*/
+
+static int spBase;
+
+
+#define OCC_IN_LAMBDA 50  /* any number > 1 will do */
+#define nullCount(vv) stgVarInfo(vv)=NIL
+#define nullCounts(vvs) { List tt=(vvs);for(;nonNull(tt);tt=tl(tt)) nullCount(hd(tt));}
+
+
+
+void oaPre ( StgExpr e )
 {
-    StgRhs rhs;
-    rhs = stgVarBody(v);
-    switch (whatIs(rhs)) {
-    case STGCON:
-            mapOver(optimiseAtom,stgConArgs(rhs));
-           break;
-    default:
-            stgVarBody(v) = optimiseExpr(rhs);
-           break;
-    }
+   List bs;
+   switch(whatIsStg(e)) {
+      case LETREC:
+         for (bs = stgLetBinds(e);nonNull(bs);bs=tl(bs))
+            stgVarInfo(hd(bs)) = mkInt(0);
+         for (bs = stgLetBinds(e);nonNull(bs);bs=tl(bs))
+            oaPre(stgVarBody(hd(bs)));
+         oaPre(stgLetBody(e));
+         break;
+      case LAMBDA:
+         nullCounts(stgLambdaArgs(e));
+         oaPre(stgLambdaBody(e));
+         break;
+      case CASE:
+         oaPre(stgCaseScrut(e));
+         mapProc(oaPre,stgCaseAlts(e));
+         break;
+      case PRIMCASE:
+         oaPre(stgPrimCaseScrut(e));
+         mapProc(oaPre,stgPrimCaseAlts(e));
+         break;
+      case STGAPP:
+         oaPre(stgAppFun(e));
+         mapProc(oaPre,stgAppArgs(e));
+         break;
+      case STGPRIM:
+         mapProc(oaPre,stgPrimArgs(e));
+         break;
+      case STGCON:
+         mapProc(oaPre,stgConArgs(e));
+         break;
+      case CASEALT:
+         nullCounts(stgCaseAltVars(e));
+         oaPre(stgCaseAltBody(e));
+         break;
+      case DEEFALT:
+         nullCount(stgDefaultVar(e));
+         oaPre(stgDefaultBody(e));
+         break;
+      case PRIMALT:
+         nullCounts(stgPrimAltVars(e));
+         oaPre(stgPrimAltBody(e));
+         break;
+      case STGVAR:
+      case NAME:
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case BIGCELL:
+      case CHARCELL:
+      case FLOATCELL:
+         break;
+      default:
+         fprintf(stderr, "oaPre: unknown stuff %d\n",whatIsStg(e));
+         assert(0);
+   }
 }
 
-static StgCaseAlt optimiseAlt( StgCaseAlt alt )
-{
-    /* StgPat pat = stgCaseAltPat(alt); */
-    stgCaseAltBody(alt) = optimiseExpr(stgCaseAltBody(alt));
-    return alt;
-}
-
-static StgPrimAlt optimisePrimAlt( StgPrimAlt alt )
-{
-    /* List vs = stgPrimAltPats(alt); */
-    stgPrimAltBody(alt) = optimiseExpr(stgPrimAltBody(alt));
-    return alt;
-}
-
-static StgExpr optimiseExpr( StgExpr e )
-{
-    switch (whatIs(e)) {
-    case LETREC:
-       {
-           List binds = stgLetBinds(e);
-           {
-               /* First we filter out trivial bindings.
-                * this has to be done before optimising the individual
-                * bindings so that we don't get confused by the results
-                * of other optimisations.
-                */
-               List bs = binds;
-               binds = NIL;
-               for(; nonNull(bs); bs=tl(bs)) {
-                   StgVar b = optimiseVar(hd(bs));
-                   StgRhs rhs = stgVarBody(b);
-                   if (whatIs(rhs) == STGVAR && b != rhs) {
-                       /* This variable will be short-circuited
-                        * by optimiseVar so we can drop the binding
-                        * right now.
-                        */
-fprintf(stderr, "dropping bind ");printStg(stderr,b);fprintf(stderr, "\n");
-                   } else {
-fprintf(stderr, "retaining bind ");printStg(stderr,b);fprintf(stderr, "\n");
-                       binds = cons(hd(bs),binds);
-                   }
-               }
-               binds = rev(binds); /* preserve original order */
-           }
-            stgLetBody(e) = optimiseExpr(stgLetBody(e));
-           if (isNull(binds)) {
-               return stgLetBody(e);
-           } else {
-               mapProc(optimiseBind,binds);
-               stgLetBinds(e) = binds;
-           }
-           break;
-       }
-    case LAMBDA:
-            stgLambdaBody(e) = optimiseExpr(stgLambdaBody(e));
-           break;
-    case CASE:
-       { 
-           StgExpr scrut = optimiseExpr(stgCaseScrut(e));
-           StgExpr alts  = stgCaseAlts(e);
-           if (whatIs(scrut) == STGVAR
-               && whatIs(stgVarBody(scrut)) == STGCON
-               ) {
-               StgRhs rhs = stgVarBody(scrut);
-               StgDiscr d = stgConCon(rhs);
-               List  args = stgConArgs(rhs);
-               for(; nonNull(alts); alts=tl(alts)) {
-                   StgCaseAlt alt = hd(alts);
-                   StgPat     pat = stgCaseAltPat(alt);
-                   if (isDefaultPat(pat)) {  /* the easy case */
-                       StgExpr body = stgCaseAltBody(alt);
-                       stgVarBody(pat) = rhs;
-                       return optimiseExpr(body);
-                   } else if (stgPatDiscr(pat) == d) {
-                       /* The tricky case:
-                        * rebind all the pattern args to the con args
-                        * and rebind the pattern var to con
-                        * and run optimiser (to eliminate the binding)
-                        */
-                       StgExpr body  = stgCaseAltBody(alt);
-                       List    binds = stgPatVars(pat);
-                       {
-                           List vs = binds;
-                           for(; 
-                               nonNull(vs) && nonNull(args);
-                               vs = tl(vs), args=tl(args)
-                               ) {
-                               stgVarBody(hd(vs)) = hd(args);
-                           }
-                       }   
-                       binds = cons(pat,binds);  /* turn patvar into a var! */
-                       stgVarBody(pat) = rhs;
-
-                       /* This letrec will always be optimised away */
-                       body = makeStgLet(binds,body);
-                       return optimiseExpr(body);
-                   }
-               }
-               internal("optimiseExpr: no patterns matched");
-           }
-            stgCaseScrut(e) = scrut;
-            mapOver(optimiseAlt,alts);
-           break;
-       }
-    case PRIMCASE:
-            mapOver(optimisePrimAlt,stgPrimCaseAlts(e));
-            stgPrimCaseScrut(e) = optimiseExpr(stgPrimCaseScrut(e));
-           break;
-    case STGPRIM:
-            mapOver(optimiseAtom,stgPrimArgs(e));
-            /* primop is not a var */
-           break;
-    case STGAPP:
-            stgAppFun(e) = optimiseExpr(stgAppFun(e));
-            mapOver(optimiseAtom,stgAppArgs(e));
-            break;
-    case STGVAR:
-            return optimiseVar(e);
-    case NAME:
-            break;  /* Names are never free vars */
-    default:
-            internal("optimiseExpr");
-    }
-    return e;
+
+/* In oaCount:
+   -- the stack is always the set of let-bound vars currently
+      in scope.  viz, stack(0 .. sp) inclusive.
+   -- spBase is always >= 0 and <= sp.  
+      stack(spBase .. sp) inclusive will be the let vars bound
+      since the nearest enclosing lambda.  When entering a lambda,
+      we set spBase=sp+1 so as record this fact, and restore spBase
+      afterwards.
+*/
+void oaCount ( StgExpr e )
+{
+   List bs;
+   Int  spBase_saved;
+
+   switch(whatIsStg(e)) {
+      case LETREC:
+         for (bs = stgLetBinds(e);nonNull(bs);bs=tl(bs))
+            push(hd(bs));
+         for (bs = stgLetBinds(e);nonNull(bs);bs=tl(bs))
+            oaCount(stgVarBody(hd(bs)));
+         oaCount(stgLetBody(e));
+         for (bs = stgLetBinds(e);nonNull(bs);bs=tl(bs))
+            drop();
+         break;
+      case LAMBDA:
+         spBase_saved = spBase;
+         spBase = sp+1;
+         oaCount(stgLambdaBody(e));
+         spBase = spBase_saved;
+         break;
+      case CASE:
+         oaCount(stgCaseScrut(e));
+         mapProc(oaCount,stgCaseAlts(e));
+         break;
+      case PRIMCASE:
+         oaCount(stgPrimCaseScrut(e));
+         mapProc(oaCount,stgPrimCaseAlts(e));
+         break;
+      case STGAPP:
+         oaCount(stgAppFun(e));
+         mapProc(oaCount,stgAppArgs(e));
+         break;
+      case STGPRIM:
+         mapProc(oaCount,stgPrimArgs(e));
+         break;
+      case STGCON:
+         mapProc(oaCount,stgConArgs(e));
+         break;
+      case CASEALT:
+         nullCounts(stgCaseAltVars(e));
+         oaCount(stgCaseAltBody(e));
+         break;
+      case DEEFALT:
+         nullCount(stgDefaultVar(e));
+         oaCount(stgDefaultBody(e));
+         break;
+      case PRIMALT:
+         nullCounts(stgPrimAltVars(e));
+         oaCount(stgPrimAltBody(e));
+         break;
+      case STGVAR:
+         if (isInt(stgVarInfo(e))) {
+            Int i, j;
+            j = -1;
+            for (i = sp; i >= spBase; i--)
+               if (stack(i) == e) { j = i; break; };
+            if (j == -1)
+               stgVarInfo(e) = mkInt(OCC_IN_LAMBDA); else
+               stgVarInfo(e) = mkInt(1 + intOf(stgVarInfo(e)));
+         }
+         break;
+      case NAME:
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case BIGCELL:
+      case CHARCELL:
+      case FLOATCELL:
+         break;
+      default:
+         fprintf(stderr, "oaCount: unknown stuff %d\n",whatIsStg(e));
+         assert(0);
+   }
+}
+
+void stgTopSanity ( char*, StgVar );
+
+/* Top level entry point for the occurrence analyser. */
+void oaTop ( StgVar v )
+{
+   assert (varSet_nfree == M_VAR_SETS);
+   freeStgVarSet(oaScc(&stgVarBody(v)));
+   assert (varSet_nfree == M_VAR_SETS);
+   oaPre(stgVarBody(v));
+   clearStack(); spBase = 0;
+   oaCount(stgVarBody(v));
+   assert(stackEmpty());
+   stgTopSanity("oaTop",stgVarBody(v));
+}
+
+
+/* --------------------------------------------------------------------------
+ * Transformation machinery proper
+ * ------------------------------------------------------------------------*/
+
+#define streq(aa,bb) (strcmp((aa),(bb))==0)
+/* Return TRUE if the non-default alts in the given list are exhaustive.
+   If in doubt, return FALSE.
+*/
+Bool stgAltsExhaustive ( List alts )
+{
+   Int   nDefnCons;
+   Name  con;
+   Tycon t;
+   List  cs;
+   char* s;
+   List  alts0 = alts;
+   while (nonNull(alts) && isDefaultAlt(hd(alts))) alts=tl(alts);
+   if (isNull(alts)) {
+      return FALSE;
+   } else {
+      con = stgCaseAltCon(hd(alts));
+      /* special case: dictionary constructor */
+      if (strncmp("Make.",textToStr(name(con).text),5)==0)
+         return TRUE;
+      /* special case: constructor boxing an unboxed value. */
+      if (isBoxingCon(con))
+         return TRUE;
+      /* some other special cases which are not boxingCons */
+      s = textToStr(name(con).text);
+      if (streq(s,"Integer#")
+          || streq(s,"Ref#")
+          || streq(s,"PrimMutableArray#")
+          || streq(s,"PrimMutableByteArray#")
+          || streq(s,"PrimByteArray#")
+          || streq(s,"PrimArray#")
+         )
+         return TRUE;
+      if (strcmp("Ref#",textToStr(name(con).text))==0)
+         return TRUE;
+      /* special case: Tuples */
+      if (isTuple(con) || (isName(con) && con==nameUnit))
+         return TRUE;
+      if (isNull(name(con).parent)) internal("stgAltsExhaustive(1)");
+      t = name(con).parent;
+      cs = tycon(t).defn;
+      if (tycon(t).what != DATATYPE) internal("stgAltsExhaustive(2)");
+      nDefnCons = length(cs);
+      for (; nonNull(alts0);alts0=tl(alts0)) {
+         if (isDefaultAlt(hd(alts0))) continue;
+         nDefnCons--;
+      }
+   }
+   return nDefnCons == 0;
+}
+#undef streq
+
+
+/* If in doubt, return FALSE. 
+*/
+Bool isManifestCon ( StgExpr e )
+{
+   StgExpr altB;
+   switch (whatIsStg(e)) {
+      case STGCON: return TRUE;
+      case LETREC: return isManifestCon(stgLetBody(e));
+      case CASE:   if (length(stgCaseAlts(e))==1) {                      
+                      if (isDefaultAlt(hd(stgCaseAlts(e))))
+                         altB = stgDefaultBody(hd(stgCaseAlts(e))); else
+                         altB = stgCaseAltBody(hd(stgCaseAlts(e)));
+                         return isManifestCon(altB);
+                   } else {
+                      return FALSE;
+                   }
+      default:     return FALSE;
+   }
+}
+
+
+/* Like isManifestCon, but doesn't give up at non-singular cases */
+Bool constructsCon ( StgExpr e )
+{
+   List    as;
+   switch (whatIsStg(e)) {
+      case STGCON:   return TRUE;
+      case LETREC:   return constructsCon(stgLetBody(e));
+      case CASE:     for (as = stgCaseAlts(e); nonNull(as); as=tl(as))
+                        if (!constructsCon(hd(as))) return FALSE;
+                     return TRUE;
+      case PRIMCASE: for (as = stgPrimCaseAlts(e); nonNull(as); as=tl(as))
+                        if (!constructsCon(hd(as))) return FALSE;
+                     return TRUE;
+      case CASEALT:  return constructsCon(stgCaseAltBody(e));
+      case DEEFALT:  return constructsCon(stgDefaultBody(e));
+      case PRIMALT:  return constructsCon(stgPrimAltBody(e));
+      default:       return FALSE;
+   }
+}
+
+
+/* Inline v in the special case where expr is
+   case v of C a1 ... an -> E
+   and v's bindee returns a product constructed with C.
+   and v does not appear in E
+   and v does not appear in letDefs (ie, this expr isn't
+       part of the definition of v.
+*/
+void tryLoopbreakerHack ( List letDefs, StgExpr expr )
+{
+   List       alts;
+   StgExpr    scrut, ee, v_bindee;
+   StgCaseAlt alt;
+  
+   assert (whatIsStg(expr)==CASE);
+   alts      = stgCaseAlts(expr);
+   scrut     = stgCaseScrut(expr);
+   if (whatIsStg(scrut) != STGVAR || isNull(stgVarBody(scrut))) return;
+   if (length(alts) != 1 || isDefaultAlt(hd(alts))) return;
+   if (!stgAltsExhaustive(alts)) return;
+   alt       = hd(alts);
+   ee        = stgCaseAltBody(alt);
+   if (nonNull(cellIsMember(scrut,letDefs))) return;
+
+   v_bindee  = stgVarBody(scrut);
+   if (!isManifestCon(v_bindee)) return;
+
+   stgCaseScrut(expr) = cloneStgTop(v_bindee);
+   nLoopBreakersInlined++;
+}
+
+
+/* Traverse a tree.  Replace let-bound vars marked as used-once
+   by their definitions.  Replace references to top-level
+   values marked inlineMe with their bodies.  Carry around a list
+   of let-bound variables whose definitions we are currently in
+   so as to know not to inline let-bound vars in their own
+   definitions.
+*/
+StgExpr copyIn ( List letDefs, InlineCtx ctx, StgExpr e )
+{
+   List bs;
+
+   switch(whatIsStg(e)) {
+      // these are the only two interesting cases
+      case STGVAR:
+         assert(isPtr(stgVarInfo(e)) || isNull(stgVarInfo(e)) || 
+                isInt(stgVarInfo(e)));
+        if (isInt(stgVarInfo(e)) && intOf(stgVarInfo(e))==1) {
+            nLetvarsInlined++;
+            return cloneStgTop(stgVarBody(e)); 
+         } else
+            return e;
+      case NAME:
+         // if we're not inlining top vars on this round, do nothing
+         if (!copyInTopvar) return e;
+         // if it doesn't want to be inlined, do nothing
+         if (!name(e).inlineMe) return e;
+         // we decline to inline dictionary builders inside other builders
+         if (inDBuilder && name(e).isDBuilder) {
+          //fprintf(stderr, "decline to inline dbuilder %s\n", textToStr(name(e).text));
+            return e;
+         }
+         // in fact, only inline dict builders into a case scrutinee
+         if (name(e).isDBuilder && ctx != CTX_SCRUT)
+            return e;
+
+#if DEBUG_OPTIMISE
+assert( stgSize(stgVarBody(name(e).stgVar)) == name(e).stgSize );
+#endif
+
+         // only inline large dict builders if it returns a manifest con
+         if (name(e).isDBuilder &&
+             name(e).stgSize > 180 && 
+             !isManifestCon(stgVarBody(name(e).stgVar)))
+            return e;
+#if 0
+         // if it's huge, don't inline into a boring place
+         if (ctx != CTX_SCRUT &&
+             name(e).stgSize > 270)
+            return e;
+#endif
+
+         nTopvarsInlined++;
+         return cloneStgTop(stgVarBody(name(e).stgVar));
+
+      // the rest are a boring recursive traversal of the tree      
+      case LETREC:
+         stgLetBody(e) = copyIn(letDefs,CTX_OTHER,stgLetBody(e));
+         letDefs = dupOnto(stgLetBinds(e),letDefs);
+         for (bs=stgLetBinds(e);nonNull(bs);bs=tl(bs))
+            stgVarBody(hd(bs)) = copyIn(letDefs,CTX_OTHER,stgVarBody(hd(bs)));
+         break;
+      case LAMBDA:
+         stgLambdaBody(e) = copyIn(letDefs,CTX_OTHER,stgLambdaBody(e));
+         break;
+      case CASE:
+         stgCaseScrut(e) = copyIn(letDefs,CTX_SCRUT,stgCaseScrut(e));
+         map2Over(copyIn,letDefs,CTX_OTHER,stgCaseAlts(e));
+         if (copyInTopvar) tryLoopbreakerHack(letDefs,e);
+         break;
+      case PRIMCASE:
+         stgPrimCaseScrut(e) = copyIn(letDefs,CTX_OTHER,stgPrimCaseScrut(e));
+         map2Over(copyIn,letDefs,CTX_OTHER,stgPrimCaseAlts(e));
+         break;
+      case STGAPP:
+         stgAppFun(e) = copyIn(letDefs,CTX_OTHER,stgAppFun(e));
+         break;
+      case CASEALT:
+         stgCaseAltBody(e) = copyIn(letDefs,CTX_OTHER,stgCaseAltBody(e));
+         break;
+      case DEEFALT:
+         stgDefaultBody(e) = copyIn(letDefs,CTX_OTHER,stgDefaultBody(e));
+         break;
+      case PRIMALT:
+         stgPrimAltBody(e) = copyIn(letDefs,CTX_OTHER,stgPrimAltBody(e));
+         break;
+      case STGPRIM:
+      case STGCON:
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case CHARCELL:
+      case FLOATCELL:
+         break;
+      default:
+         fprintf(stderr, "copyIn: unknown stuff %d\n",whatIsStg(e));
+         ppStgExpr(e);
+         printf("\n");
+         print(e,1000);
+         printf("\n");
+         assert(0);
+   }
+   return e;
+}
+
+
+
+/* case (C a1 ... an) of
+      B ...       -> ...
+      C v1 ... vn -> e
+      D ...       -> ...
+   ==>
+   e with v1/a1 ... vn/an
+*/
+StgExpr doCaseOfCon ( StgExpr expr, Bool* done )
+{
+   StgExpr    scrut, e;
+   StgVar     apC;
+   StgCaseAlt theAlt;
+   List       alts, altvs, as, sub;
+
+   *done  = FALSE;
+   alts   = stgCaseAlts(expr);
+   scrut  = stgCaseScrut(expr);
+
+   apC    = stgConCon(scrut);
+
+   theAlt = NIL;
+   for (alts = stgCaseAlts(expr); nonNull(alts); alts=tl(alts))
+      if (!isDefaultAlt(hd(alts)) && stgCaseAltCon(hd(alts)) == apC) {
+         theAlt = hd(alts);
+         break;
+      }
+
+   if (isNull(theAlt)) return expr;
+   altvs  = stgCaseAltVars(theAlt);
+   e      = stgCaseAltBody(theAlt);
+   as     = stgConArgs(scrut);
+
+   if (length(as)!=length(altvs)) return expr;
+
+   sub = NIL;
+   while (nonNull(altvs)) {
+      sub   = cons(pair(hd(altvs),hd(as)),sub);
+      as    = tl(as);
+      altvs = tl(altvs);
+   }
+   nCaseOfCon++;
+   *done = TRUE;
+   return zubstExpr(sub,e);
+}
+
+
+/* case (let binds in e) of alts
+   ===>
+   let binds in case e of alts
+*/
+StgExpr doCaseOfLet ( StgExpr expr, Bool* done )
+{
+   StgExpr letexpr, e;
+   List    binds, alts;
+
+   letexpr = stgCaseScrut(expr);
+   e       = stgLetBody(letexpr);
+   binds   = stgLetBinds(letexpr);
+   alts    = stgCaseAlts(expr);
+   nCaseOfLet++;
+   *done   = TRUE;
+   return mkStgLet(binds,mkStgCase(e,alts));
+}
+
+
+
+/* case (case e of p1 -> e1 ... pn -> en) of
+      q1 -> h1
+      ...
+      qk -> hk
+   ===>
+   case e of 
+      p1 -> case e1 of q1 -> h1 ... qk -> hk
+      ...
+      pn -> case en of q1 -> h1 ... qk -> kl
+*/
+StgExpr doCaseOfCase ( StgExpr expr )
+{
+   StgExpr innercase, e, tmpcase, protocase;
+   List ps_n_es, qs_n_hs, newAlts;
+   StgCaseAlt newAlt, p_n_e;
+
+   nCaseOfCase++;
+
+   innercase = stgCaseScrut(expr);
+   e = stgCaseScrut(innercase);
+   ps_n_es = stgCaseAlts(innercase);
+   qs_n_hs = stgCaseAlts(expr);
+
+   /* protocase = case (hole-to-fill-in) of q1 -> h1 ... qk -> hk */
+   protocase = mkStgCase( mkInt(0), qs_n_hs);
+
+   newAlts = NIL;
+   for (;nonNull(ps_n_es);ps_n_es = tl(ps_n_es)) {
+      tmpcase = cloneStgTop(protocase);
+      p_n_e = hd(ps_n_es);
+      if (isDefaultAlt(p_n_e)) {
+         stgCaseScrut(tmpcase) = stgDefaultBody(p_n_e);
+         newAlt = mkStgDefault(stgDefaultVar(p_n_e), tmpcase);
+      } else {
+         stgCaseScrut(tmpcase) = stgCaseAltBody(p_n_e);
+         newAlt = mkStgCaseAlt(stgCaseAltCon(p_n_e),stgCaseAltVars(p_n_e),tmpcase);
+      }
+      newAlts = cons(newAlt,newAlts);
+   }
+   newAlts = rev(newAlts);
+   return
+      mkStgCase(e, newAlts);
+}
+
+
+
+/* case (case# e of p1 -> e1 ... pn -> en) of
+      q1 -> h1
+      ...
+      qk -> hk
+   ===>
+   case# e of 
+      p1 -> case e1 of q1 -> h1 ... qk -> hk
+      ...
+      pn -> case en of q1 -> h1 ... qk -> kl
+*/
+StgExpr doCaseOfPrimCase ( StgExpr expr )
+{
+   StgExpr innercase, e, tmpcase, protocase;
+   List ps_n_es, qs_n_hs, newAlts;
+   StgCaseAlt newAlt, p_n_e;
+
+   nCaseOfPrimCase++;
+
+   innercase = stgCaseScrut(expr);
+   e = stgPrimCaseScrut(innercase);
+   ps_n_es = stgPrimCaseAlts(innercase);
+   qs_n_hs = stgCaseAlts(expr);
+
+   /* protocase = case (hole-to-fill-in) of q1 -> h1 ... qk -> hk */
+   protocase = mkStgCase( mkInt(0), qs_n_hs);
+
+   newAlts = NIL;
+   for (;nonNull(ps_n_es);ps_n_es = tl(ps_n_es)) {
+      tmpcase = cloneStgTop(protocase);
+      p_n_e = hd(ps_n_es);
+      stgPrimCaseScrut(tmpcase) = stgPrimAltBody(p_n_e);
+      newAlt = mkStgPrimAlt(stgPrimAltVars(p_n_e),tmpcase);
+      newAlts = cons(newAlt,newAlts);
+   }
+   newAlts = rev(newAlts);  
+   return
+      mkStgPrimCase(e, newAlts);
+}
+
+
+Bool isStgCaseWithSingleNonDefaultAlt ( StgExpr e )
+{
+   return
+      whatIsStg(e)==CASE &&
+      length(stgCaseAlts(e))==1 &&
+      !isDefaultAlt(hd(stgCaseAlts(e)));
+}
+
+
+/* Do simplifications on an Stg tree.  Invariant is that the
+   input and output trees should have no name shadowing.
+
+   -- let { } in e
+      ===>
+      e
+
+   -- dump individual let-bindings with usage counts of zero
+
+   -- dump let-binding groups for which none of the bound vars
+      occur in the let body
+
+   -- (\v1 ... vn -> e) a1 ... am
+      ===>
+      -- the usual beta reduction.  There are no constraints on n and m, so
+         the result can be a lambda term (if n > m), or an application of e 
+         to the unused args (if n < m).
+
+
+  Scheme is: bottom-up traversal of the tree.  First simplify child
+  trees.  Then try to do local transformations.  If a local transformation 
+  succeeds, jump to the local-transformation code for whatever node
+  is produced -- so as to try and maximise the amount of work which
+  happens on each call to simplify.
+*/
+StgExpr simplify ( List caseEnv, StgExpr e )
+{
+   List bs, bs2;
+   Bool done;
+   Int  n;
+
+   restart:
+   switch(whatIsStg(e)) {
+      case STGVAR:
+         return e;
+      case NAME:
+         return e;
+
+      case LETREC:
+
+         /* first dump dead binds, so as not to waste effort simplifying them */
+         bs2=NIL;
+         for (bs=stgLetBinds(e);nonNull(bs);bs=tl(bs))
+            if (!isInt(stgVarInfo(hd(bs))) ||
+                intOf(stgVarInfo(hd(bs))) > 0) {
+               bs2=cons(hd(bs),bs2);
+            } else {
+               nLetBindsDropped++;
+            }
+         if (isNull(bs2)) { e = stgLetBody(e); goto restart; };
+         stgLetBinds(e) = rev(bs2);
+
+         for (bs=stgLetBinds(e);nonNull(bs);bs=tl(bs))
+            stgVarBody(hd(bs)) = simplify(caseEnv,stgVarBody(hd(bs)));
+         stgLetBody(e) = simplify(caseEnv,stgLetBody(e));
+
+         /* Merge let ... in let ... in e.  Grouping lets together
+            sometimes reduces the number of iterations needed.
+            oaScc should do this anyway, but this just to make sure.
+         */
+         while (whatIsStg(stgLetBody(e))==LETREC) {
+            stgLetBinds(e) = dupOnto(stgLetBinds(stgLetBody(e)),stgLetBinds(e));
+            stgLetBody(e) = stgLetBody(stgLetBody(e));
+         }
+
+         let_local:
+         /* let binds in case v-not-in-binds of singleAlt -> expr
+            ===>
+            case v-not-in-binds of singleAlt -> let binds in expr
+        */
+         if (isStgCaseWithSingleNonDefaultAlt(stgLetBody(e)) &&
+             whatIsStg(stgCaseScrut(stgLetBody(e)))==STGVAR &&
+             isNull(cellIsMember(stgCaseScrut(stgLetBody(e)),stgLetBinds(e)))) {
+            StgVar     v = stgCaseScrut(stgLetBody(e));
+            StgCaseAlt a = hd(stgCaseAlts(stgLetBody(e)));
+            nLetsFloatedIntoCase++;
+            e = mkStgCase( 
+                   v, 
+                   singleton( 
+                      mkStgCaseAlt(
+                         stgCaseAltCon(a),
+                         stgCaseAltVars(a), 
+                         mkStgLet(stgLetBinds(e),stgCaseAltBody(a))
+                      )
+                   )
+                );
+            assert(whatIsStg(e)==CASE);
+            goto case_local;
+         }
+          
+         break;
+
+      case LAMBDA:
+         stgLambdaBody(e) = simplify(caseEnv,stgLambdaBody(e));
+
+         lambda_local:
+         while (whatIsStg(stgLambdaBody(e))==LAMBDA) {
+            nLambdasMerged++;
+            stgLambdaArgs(e) = appendOnto(stgLambdaArgs(e),
+                                          stgLambdaArgs(stgLambdaBody(e)));
+            stgLambdaBody(e) = stgLambdaBody(stgLambdaBody(e));
+         }
+         break;
+
+
+      case CASE:
+         stgCaseScrut(e) = simplify(caseEnv,stgCaseScrut(e));
+         if (isStgCaseWithSingleNonDefaultAlt(e) &&
+             (whatIsStg(stgCaseScrut(e))==STGVAR ||
+              whatIsStg(stgCaseScrut(e))==NAME)) {
+            List caseEnv2 = cons(
+                               pair(stgCaseScrut(e),stgCaseAltVars(hd(stgCaseAlts(e)))),
+                               caseEnv
+                            );
+            map1Over(simplify,caseEnv2,stgCaseAlts(e));
+         } else {
+            map1Over(simplify,caseEnv,stgCaseAlts(e));
+         }
+
+         case_local:
+         /* zap redundant default alternatives */
+         if (stgAltsExhaustive(stgCaseAlts(e))) {
+            Bool droppedDef = FALSE;
+            bs2 = NIL;
+            for (bs = dupList(stgCaseAlts(e));nonNull(bs);bs=tl(bs))
+               if (!isDefaultAlt(hd(bs))) {
+                  bs2=cons(hd(bs),bs2); 
+               } else {
+                  droppedDef = TRUE;
+               }
+            bs2 = rev(bs2);
+            stgCaseAlts(e) = bs2;
+            if (droppedDef) nCaseDefaultsDropped++;
+         }
+        
+         switch (whatIsStg(stgCaseScrut(e))) {
+            case CASE:
+               /* attempt case-of-case */
+               n = length(stgCaseAlts(e));
+               if (n==1 || 
+                           (n <= 3 && 
+                            (stgSize(e)-stgSize(stgCaseScrut(e))) < 100 &&
+                            constructsCon(stgCaseScrut(e)))
+                  ) {
+                  e = doCaseOfCase(e);
+                  assert(whatIsStg(e)==CASE);
+                  goto case_local;
+               }
+               break;
+            case PRIMCASE:
+               /* attempt case-of-case# */
+               n = length(stgCaseAlts(e));
+               if (n==1 || 
+                           (n <= 3 && 
+                            (stgSize(e)-stgSize(stgCaseScrut(e))) < 100 &&
+                            constructsCon(stgCaseScrut(e)))
+                  ) {
+                  e = doCaseOfPrimCase(e);
+                  assert(whatIsStg(e)==PRIMCASE);
+                  goto primcase_local;
+               }
+               break;
+            case LETREC:
+               /* attempt case-of-let */
+               e = doCaseOfLet(e,&done);
+               if (done) { assert(whatIsStg(e)==LETREC); goto let_local; };
+               break;
+            case STGCON:
+              /* attempt case-of-constructor */
+               e = doCaseOfCon(e,&done);
+               /* we don't know what the result is, so can't jump to local */
+               break;
+            case NAME:
+            case STGVAR: {
+               /* attempt to remove case on something already cased on */
+               List outervs, innervs, sub;
+               Cell lookupResult;
+               if (!isStgCaseWithSingleNonDefaultAlt(e)) break;
+               lookupResult = cellAssoc(stgCaseScrut(e),caseEnv);
+               if (isNull(lookupResult)) break;
+               outervs = snd(lookupResult);
+               nCaseOfOuter++;
+               sub = NIL;
+               innervs = stgCaseAltVars(hd(stgCaseAlts(e)));
+               for (; nonNull(outervs) && nonNull(innervs);
+                      outervs=tl(outervs), innervs=tl(innervs))
+                  sub = cons(pair(hd(innervs),hd(outervs)),sub);
+               assert (isNull(outervs) && isNull(innervs));
+               return zubstExpr(sub, stgCaseAltBody(hd(stgCaseAlts(e))));
+              }
+            default:
+               break;
+         }
+         break;
+      case CASEALT:
+         stgCaseAltBody(e) = simplify(caseEnv,stgCaseAltBody(e));
+         break;
+      case DEEFALT:
+         stgDefaultBody(e) = simplify(caseEnv,stgDefaultBody(e));
+         break;
+      case PRIMALT:
+         stgPrimAltBody(e) = simplify(caseEnv,stgPrimAltBody(e));
+         break;
+      case PRIMCASE:
+         stgPrimCaseScrut(e) = simplify(caseEnv,stgPrimCaseScrut(e));
+         map1Over(simplify,caseEnv,stgPrimCaseAlts(e));
+         primcase_local:
+         break;
+      case STGAPP: {
+         List    sub, formals;
+         StgExpr subd_body;
+         StgExpr fun;
+         List    args;
+
+         stgAppFun(e) = simplify(caseEnv,stgAppFun(e));
+         map1Over(simplify,caseEnv,stgAppArgs(e));
+
+         fun  = stgAppFun(e);
+         args = stgAppArgs(e);
+
+         switch (whatIsStg(fun)) {
+            case STGAPP:
+               nAppsMerged++;
+               stgAppArgs(e) = appendOnto(stgAppArgs(fun),args);
+               stgAppFun(e) = stgAppFun(fun);
+               break;
+            case LETREC:
+               /* (let binds in f) args  ==> let binds in (f args) */
+               nLetsFloatedOutOfFn++;
+               e = mkStgLet(stgLetBinds(fun),mkStgApp(stgLetBody(fun),args));
+               assert(whatIsStg(e)==LETREC);
+               goto let_local;
+               break;
+            case CASE:
+               if (length(stgCaseAlts(fun))==1 && 
+                   !isDefaultAlt(hd(stgCaseAlts(fun)))) {
+                  StgCaseAlt theAlt = hd(stgCaseAlts(fun));
+                  /* (case e of alt -> f) args  ==> case e of alt -> f args */
+                  e = mkStgCase(
+                         stgCaseScrut(fun),
+                         singleton(mkStgCaseAlt(stgCaseAltCon(theAlt),
+                                                stgCaseAltVars(theAlt),
+                                                 mkStgApp(stgCaseAltBody(theAlt),args))
+                         )
+                      );
+                  nCasesFloatedOutOfFn++;
+                  assert(whatIsStg(e)==CASE);
+                  goto case_local;
+              }
+               break;
+            case LAMBDA: {
+               sub      = NIL;
+               formals  = stgLambdaArgs(fun);
+               while (nonNull(formals) && nonNull(args)) {
+                  sub     = cons(pair(hd(formals),hd(args)),sub);
+                  formals = tl(formals);
+                  args    = tl(args);
+               }
+               subd_body = zubstExpr(sub,stgLambdaBody(fun));
+
+               nBetaReductions++;
+               assert(isNull(formals) || isNull(args));
+               if (isNull(formals) && isNull(args)) {
+                  /* fn and args match exactly */
+                  e = subd_body;
+                  return e;
+               }
+               else
+               if (isNull(formals) && nonNull(args)) {
+                  /* more args than we could deal with.  Build a new Ap. */
+                  e = mkStgApp(subd_body,args);
+                  return e;
+               }
+               else
+              if (nonNull(formals) && isNull(args)) {
+                  /* partial application.  We get a new Lambda */
+                  e = mkStgLambda(formals,subd_body);
+                  return e;
+              }
+              }
+               break;
+            default:
+               break;
+         }
+         }
+         break;
+      case STGPRIM:
+         break;
+      case STGCON:
+         break;
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case CHARCELL:
+      case FLOATCELL:
+         break;
+      default:
+         fprintf(stderr, "simplify: unknown stuff %d\n",whatIsStg(e));
+         ppStgExpr(e);
+         printf("\n");
+         print(e,1000);
+         printf("\n");
+         assert(0);
+   }
+   return e;
 }
 
 
-void optimiseTopBind( StgVar v )
+/* Restore STG representation invariants broken by simplify.
+   -- Let-bind any constructor applications which appear
+      anywhere other than a let.
+   -- Let-bind non-atomic case scrutinees (ToDo).
+*/
+StgExpr restoreStg ( StgExpr e )
 {
-if (lastModule() != modulePrelude) {
-fflush(stdout); fflush(stderr);
-fprintf ( stderr, "------------------------------\n" );
-fflush(stderr);
-printStg ( stderr, v );
-fprintf(stderr, "\n" );
+   List bs;
+   StgVar newv;
+
+   if (isNull(e)) return e;
+
+   switch(whatIsStg(e)) {
+      case LETREC:
+         for (bs=stgLetBinds(e); nonNull(bs); bs=tl(bs)) {
+            if (whatIsStg(stgVarBody(hd(bs))) == STGCON) {
+             /* do nothing */
+            } 
+            else
+            if (whatIsStg(stgVarBody(hd(bs))) == LAMBDA) {
+               stgLambdaBody(stgVarBody(hd(bs))) 
+                  = restoreStg(stgLambdaBody(stgVarBody(hd(bs))));
+            }
+            else {
+               stgVarBody(hd(bs)) = restoreStg(stgVarBody(hd(bs)));
+            }
+         }      
+         stgLetBody(e) = restoreStg(stgLetBody(e));
+         break;
+      case LAMBDA:
+        /* note that the check in LETREC above ensures we won't
+            get here for legitimate (let-bound) lambdas. */
+         stgLambdaBody(e) = restoreStg(stgLambdaBody(e));
+         newv = mkStgVar(e,NIL);
+         e = mkStgLet(singleton(newv),newv);
+         break;
+      case CASE:
+         stgCaseScrut(e) = restoreStg(stgCaseScrut(e));
+         mapOver(restoreStg,stgCaseAlts(e));
+         if (!isAtomic(stgCaseScrut(e))) {
+            newv = mkStgVar(stgCaseScrut(e),NIL);
+            return mkStgLet(singleton(newv),mkStgCase(newv,stgCaseAlts(e)));
+         }
+         break;
+      case PRIMCASE:
+         stgPrimCaseScrut(e) = restoreStg(stgPrimCaseScrut(e));
+         mapOver(restoreStg,stgPrimCaseAlts(e));
+         break;
+      case STGAPP:
+         stgAppFun(e) = restoreStg(stgAppFun(e));
+         mapOver(restoreStg,stgAppArgs(e)); /* probably incorrect */
+         if (!isAtomic(stgAppFun(e))) {
+            newv = mkStgVar(stgAppFun(e),NIL);
+            e = mkStgLet(singleton(newv),mkStgApp(newv,stgAppArgs(e)));
+         }
+         break;
+      case STGPRIM:
+         mapOver(restoreStg,stgPrimArgs(e));
+         break;
+      case STGCON:
+        /* note that the check in LETREC above ensures we won't
+            get here for legitimate constructor applications. */
+         mapOver(restoreStg,stgConArgs(e));
+         newv = mkStgVar(e,NIL);
+         return mkStgLet(singleton(newv),newv);
+         break;
+      case CASEALT:
+         stgCaseAltBody(e) = restoreStg(stgCaseAltBody(e));
+         if (whatIsStg(stgCaseAltBody(e))==LAMBDA) {
+            newv = mkStgVar(stgCaseAltBody(e),NIL);
+            stgCaseAltBody(e) = mkStgLet(singleton(newv),newv);
+         }
+         break;
+      case DEEFALT:
+         stgDefaultBody(e) = restoreStg(stgDefaultBody(e));
+         if (whatIsStg(stgDefaultBody(e))==LAMBDA) {
+            newv = mkStgVar(stgDefaultBody(e),NIL);
+            stgDefaultBody(e) = mkStgLet(singleton(newv),newv);
+         }
+         break;
+      case PRIMALT:
+         stgPrimAltBody(e) = restoreStg(stgPrimAltBody(e));
+         break;
+      case STGVAR:
+      case NAME:
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case CHARCELL:
+      case FLOATCELL:
+         break;
+      default:
+         fprintf(stderr, "restoreStg: unknown stuff %d\n",whatIsStg(e));
+         ppStgExpr(e);
+         printf("\n");
+         assert(0);
+   }
+   return e;
 }
-optimiseBind ( v );
-if (lastModule() != modulePrelude) {
-printStg ( stderr,v );
-fprintf(stderr, "\n\n" );
-fflush(stderr);
+
+
+StgExpr restoreStgTop ( StgExpr e )
+{
+   if (whatIs(e)==LAMBDA)
+      stgLambdaBody(e) = restoreStg(stgLambdaBody(e)); else
+      e = restoreStg(e);
+   return e;
 }
+
+
+void simplTopRefs ( StgExpr e )
+{
+   List bs;
+
+   switch(whatIsStg(e)) {
+     /* the only interesting case */
+      case NAME:
+         if (name(e).inlineMe && !name(e).simplified) {
+            /* printf("\n((%d)) request for %s\n",rDepth, textToStr(name(e).text)); */
+            name(e).simplified = TRUE;
+            optimiseTopBind(name(e).stgVar);
+            /* printf("((%d)) done    for %s\n",rDepth, textToStr(name(e).text)); */
+         }
+         break;
+      case LETREC:
+         simplTopRefs(stgLetBody(e));
+         for (bs=stgLetBinds(e); nonNull(bs); bs=tl(bs))
+            simplTopRefs(stgVarBody(hd(bs)));
+         break;
+      case LAMBDA:
+         simplTopRefs(stgLambdaBody(e));
+         break;
+      case CASE:
+         simplTopRefs(stgCaseScrut(e));
+         mapProc(simplTopRefs,stgCaseAlts(e));
+         break;
+      case PRIMCASE:
+         simplTopRefs(stgPrimCaseScrut(e));
+         mapProc(simplTopRefs,stgPrimCaseAlts(e));
+         break;
+      case STGAPP:
+         simplTopRefs(stgAppFun(e));
+         mapProc(simplTopRefs,stgAppArgs(e));
+         break;
+      case STGCON:
+         mapProc(simplTopRefs,stgConArgs(e));
+         break;
+      case STGPRIM:
+         simplTopRefs(stgPrimOp(e));
+         mapProc(simplTopRefs,stgPrimArgs(e));
+         break;
+      case CASEALT:
+         simplTopRefs(stgCaseAltBody(e));
+         break;
+      case DEEFALT:
+         simplTopRefs(stgDefaultBody(e));
+         break;
+      case PRIMALT:
+         simplTopRefs(stgPrimAltBody(e));
+         break;
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case BIGCELL:
+      case CHARCELL:
+      case FLOATCELL:
+      case TUPLE:
+      case STGVAR:
+         break;
+      default:
+         fprintf(stderr, "simplTopRefs: unknown stuff %d\n",whatIsStg(e));
+         ppStgExpr(e);
+         printf("\n");
+         print(e,1000);
+         printf("\n");
+         assert(0);
+   }
+}
+
+char* maybeName ( StgVar v )
+{
+   Name n = nameFromStgVar(v);
+   if (isNull(n)) return "(unknown)";
+   return textToStr(name(n).text);
+}
+
+
+/* --------------------------------------------------------------------------
+ * Sanity checking (weak :-(
+ * ------------------------------------------------------------------------*/
+
+Bool stgError;
+
+int stgSanity_checkStack ( StgVar v )
+{
+   int i, j;
+   j = 0;
+   for (i = 0; i <= sp; i++)
+      if (stack(i)==v) j++;
+   return j;
+}
+
+void stgSanity_dropVar ( StgVar v )
+{
+   drop();
+}
+
+void stgSanity_pushVar ( StgVar v )
+{
+   if (stgSanity_checkStack(v) != 0) stgError = TRUE;
+   push(v);
+}
+
+
+void stgSanity ( StgExpr e )
+{
+   List bs;
+
+   switch(whatIsStg(e)) {
+      case LETREC:
+         mapProc(stgSanity_pushVar,stgLetBinds(e));
+         stgSanity(stgLetBody(e));
+         for (bs=stgLetBinds(e); nonNull(bs); bs=tl(bs))
+             stgSanity(stgVarBody(hd(bs)));
+         mapProc(stgSanity_dropVar,stgLetBinds(e));
+         break;
+      case LAMBDA:
+         mapProc(stgSanity_pushVar,stgLambdaArgs(e));
+         stgSanity(stgLambdaBody(e));
+         mapProc(stgSanity_dropVar,stgLambdaArgs(e));
+         break;
+      case CASE:
+         stgSanity(stgCaseScrut(e));
+         mapProc(stgSanity,stgCaseAlts(e));
+         break;
+      case PRIMCASE:
+         stgSanity(stgPrimCaseScrut(e));
+         mapProc(stgSanity,stgPrimCaseAlts(e));
+         break;
+      case STGAPP:
+         stgSanity(stgAppFun(e));
+         mapProc(stgSanity,stgAppArgs(e));
+         break;
+      case STGCON:
+         stgSanity(stgConCon(e));
+         mapProc(stgSanity,stgConArgs(e));
+         break;
+      case STGPRIM:
+         stgSanity(stgPrimOp(e));
+         mapProc(stgSanity,stgPrimArgs(e));
+         break;
+      case CASEALT:
+         mapProc(stgSanity_pushVar,stgCaseAltVars(e));
+         stgSanity(stgCaseAltBody(e));
+         mapProc(stgSanity_dropVar,stgCaseAltVars(e));
+         break;
+      case DEEFALT:
+         stgSanity_pushVar(stgDefaultVar(e));
+         stgSanity(stgDefaultBody(e));
+         stgSanity_dropVar(stgDefaultVar(e));
+         break;
+      case PRIMALT:
+         mapProc(stgSanity_pushVar,stgPrimAltVars(e));
+         stgSanity(stgPrimAltBody(e));
+         mapProc(stgSanity_dropVar,stgPrimAltVars(e));
+         break;
+      case STGVAR:
+         if (stgSanity_checkStack(e) == 1) break;
+         if (nonNull(nameFromStgVar(e))) return;
+         break;
+      case NAME:
+      case INTCELL:
+      case STRCELL:
+      case PTRCELL:
+      case CHARCELL:
+      case FLOATCELL:
+      case TUPLE:
+         break;
+      default:
+         fprintf(stderr, "stgSanity: unknown stuff %d\n",whatIsStg(e));
+         ppStgExpr(e);
+         printf("\n");
+         print(e,1000);
+         printf("\n");
+         assert(0);
+   }
+}
+
+
+void stgTopSanity ( char* caller, StgExpr e )
+{
+return;
+   clearStack();
+   assert(sp == -1);
+   stgError = FALSE;
+   stgSanity(e);
+   assert(sp == -1);
+   if (stgError) {
+      fprintf(stderr, "\n\nstgTopSanity (caller = %s):\n\n", caller );
+      ppStgExpr ( e );
+      printf( "\n\n" );
+      assert(0);
+   }
+}
+
+
+/* Check if e is in a form which the code generator can deal with.
+ * stgexpr-ness is what we need to enforce.  The extended version,
+ * expr, may only occur as the rhs of a let binding.
+ *
+ * stgexpr ::= case atom of alts
+ *           | case# primop{atom*} of primalts
+ *           | let v_i = expr_i in stgexpr
+ *           | var{atom*}
+ *
+ * expr ::= stgexpr
+ *        | \v_i -> stgexpr
+ *        | con{atoms}
+ *
+ *  alt ::= con vars -> stgexpr      (primalt and default similarly)
+ *
+ * atom ::= var | int | char etc     (unboxed, that is)
+ */
+Bool isStgExpr     ( StgExpr e );
+Bool isStgFullExpr ( StgExpr e );
+
+Bool isStgExpr ( StgExpr e )
+{
+   List bs;
+   switch (whatIs(e)) {
+      case LAMBDA:
+      case STGCON:
+         return FALSE;
+      case LETREC:
+         for (bs=stgLetBinds(e); nonNull(bs); bs=tl(bs))
+            if (!isStgFullExpr(stgVarBody(hd(bs))))
+               return FALSE;
+         return isStgExpr(stgLetBody(e));
+      case CASE:
+         for (bs=stgCaseAlts(e); nonNull(bs); bs=tl(bs))
+            if (!isStgExpr(hd(bs))) return FALSE;
+         return isAtomic(stgCaseScrut(e));
+      case PRIMCASE:
+         for (bs=stgPrimCaseAlts(e); nonNull(bs); bs=tl(bs))
+            if (!isStgExpr(hd(bs))) return FALSE;
+         if (isAtomic(stgPrimCaseScrut(e))) return TRUE;
+         if (whatIs(stgPrimCaseScrut(e))==STGPRIM)
+            return isStgExpr(stgPrimCaseScrut(e));
+         return FALSE;
+      case STGVAR:
+      case NAME:
+         return TRUE;
+      case STGAPP:
+         for (bs=stgAppArgs(e); nonNull(bs); bs=tl(bs))
+            if (!isAtomic(hd(bs))) return FALSE;
+         if (isStgVar(stgAppFun(e)) || isName(stgAppFun(e))) return TRUE;
+         return FALSE;
+      case STGPRIM:
+         for (bs=stgPrimArgs(e); nonNull(bs); bs=tl(bs))
+            if (!isAtomic(hd(bs))) return FALSE;
+         if (isName(stgPrimOp(e))) return TRUE;
+         return FALSE;
+      case CASEALT:
+         return isStgExpr(stgCaseAltBody(e));
+      case DEEFALT:
+         return isStgExpr(stgDefaultBody(e));
+      case PRIMALT:
+         return isStgExpr(stgPrimAltBody(e));
+      default:
+         return FALSE;
+   }
+}
+
+
+Bool isStgFullExpr ( StgExpr e )
+{
+   List bs;
+   switch (whatIs(e)) {
+      case LAMBDA:
+         return isStgExpr(stgLambdaBody(e));
+      case STGCON:
+         for (bs=stgConArgs(e); nonNull(bs); bs=tl(bs))
+            if (!isAtomic(hd(bs))) return FALSE;
+         if (isName(stgConCon(e)) || isTuple(stgConCon(e)))
+            return TRUE;
+         return FALSE;
+      default:
+         return isStgExpr(e);
+   }
+}
+
+
+/* --------------------------------------------------------------------------
+ * Top level calls
+ * ------------------------------------------------------------------------*/
+
+/* Set ddumpSimpl to TRUE if you want to see simplified code. */
+static Bool ddumpSimpl = FALSE;
+
+/* Leave this one alone ... */
+static Bool noisy;
+
+
+static void local optimiseTopBind( StgVar v )
+{
+   Bool ppPrel = FALSE;
+   Int  n, m;
+   Name naam;
+   Int  oldSize, newSize;
+   Bool me;
+
+   /* printf( "[[%d]] looking at %s\n", rDepth, maybeName(v)); */
+   assert(whatIsStg(v)==STGVAR);
+
+   rDepth++;
+   if (nonNull(stgVarBody(v))) simplTopRefs(stgVarBody(v));
+   rDepth--;
+
+   /* debugging ... */
+   //me= 0&& 0==strcmp("tcUnify",maybeName(v));
+   me= 0&& 0==strcmp("ttt",maybeName(v));
+
+   nTotSizeIn += stgSize(stgVarBody(v));
+   if (noisy) {
+      printf( "%28s: in %4d    ", maybeName(v),stgSize(stgVarBody(v))); 
+      fflush(stdout);
+   }
+
+   inDBuilder = FALSE;
+   naam = nameFromStgVar(v);
+   if (nonNull(naam) && name(naam).isDBuilder) inDBuilder = TRUE;
+
+#if DEBUG_OPTIMISE
+   if (nonNull(naam)) {
+      assert(name(naam).stgSize == stgSize(stgVarBody(name(naam).stgVar)));
+   }
+#endif
+
+   if (me) {
+      fflush(stdout); fflush(stderr);
+      fprintf ( stderr, "{{%d}}-----------------------------\n", -v );fflush(stderr);
+      printStg ( stderr, v );
+      fprintf(stderr, "\n" );
+   }
+
+   stgTopSanity ( "initial", stgVarBody(v));
+
+   if (nonNull(stgVarBody(v))) {
+      oldSize = -1;
+
+      for (n = 0; n < 8; n++) { // originally 7
+         if (noisy) printf("%4d", stgSize(stgVarBody(v)));
+         copyInTopvar = TRUE;
+         stgTopSanity ( "outer-1", stgVarBody(v));
+         oaTop ( v );
+         stgTopSanity ( "outer-2", stgVarBody(v));
+         stgVarBody(v) = copyIn ( NIL, CTX_OTHER, stgVarBody(v) );
+         stgTopSanity ( "outer-3", stgVarBody(v));
+         stgVarBody(v) = simplify ( NIL, stgVarBody(v) );
+         stgTopSanity ( "outer-4", stgVarBody(v));
+
+         for (m = 0; m < 3; m++) { // oprignally 3
+            if (noisy) printf("."); 
+            fflush(stdout);
+            copyInTopvar = FALSE;
+            stgTopSanity ( "inner-1", stgVarBody(v));
+            oaTop ( v );
+            stgTopSanity ( "inner-2", stgVarBody(v));
+            stgVarBody(v) = copyIn ( NIL, CTX_OTHER, stgVarBody(v) );
+            stgTopSanity ( "inner-3", stgVarBody(v));
+            stgVarBody(v) = simplify ( NIL, stgVarBody(v) );
+
+            if (me && 0) {
+               fprintf(stderr,"\n-%d- - - - - - - - - - - - - -\n", n+1);
+               printStg ( stderr,v );
+            }
+            stgTopSanity ( "inner-post", stgVarBody(v));
+
+         }
+
+         if (me && 1) {
+            fprintf(stderr,"\n-%d-=-=-=-=-=-=-=-=-=-=-=-=-=-\n", n+1);
+            printStg ( stderr,v );
+         }
+
+         stgTopSanity ( "outer-post", stgVarBody(v));
+
+         newSize = stgSize ( stgVarBody(v) );
+         if (newSize == oldSize) break;
+         oldSize = newSize;
+      }
+      n++; for (; n < 8; n++) for (m = 0; m <= 3+3; m++) if (noisy) printf ( " " );
+      if (noisy) printf(" --> %4d\n", stgSize(stgVarBody(v)) );
+      stgVarBody(v) = restoreStgTop ( stgVarBody(v) );
+
+      if (nonNull(naam)) {
+         assert(name(naam).stgVar == v);
+         name(naam).stgSize = stgSize(stgVarBody(v));
+      }
+
+#if DEBUG_OPTIMISE
+      /* debugging ... */
+      if (!isStgFullExpr(stgVarBody(v))) {
+         fprintf(stderr, "\n\nrestoreStg failed!\n\n" );
+         printStg(stderr, v);
+         fprintf(stderr, "\n" );
+         exit(1);
+      }
+#endif
+   }
+
+   nTotSizeOut += stgSize(stgVarBody(v));
+
+   if (me) {
+      fprintf(stderr,"\n=============================\n");
+      printStg ( stderr,v );
+      fprintf(stderr, "\n\n" );
+      fflush(stderr);
+      if (me) exit(1);
+   }
+}
+
+
+void optimiseTopBinds ( List bs )
+{
+   List t;
+   Name n;
+   Target ta = 0;
+
+   noisy = ddumpSimpl && (lastModule() != modulePrelude);
+
+   optimiser(RESET);
+   if (noisy) printf("\n");
+   initOptStats();
+
+   for (t = bs; nonNull(t); t=tl(t)) {
+      n = nameFromStgVar(hd(t));
+      if (isNull(n) || !name(n).simplified) {
+         rDepth = 0;
+         optimiseTopBind(hd(t));
+      }
+      soFar(ta++);
+   }
+   if (noisy) printOptStats ( stderr );
+   optimiser(RESET);
+}
+
+
+/* --------------------------------------------------------------------------
+ * Optimiser control:
+ * ------------------------------------------------------------------------*/
+
+Void optimiser(what)
+Int what; {
+
+    switch (what) {
+        case INSTALL :
+        case RESET   : spClone = SP_NOT_IN_USE;
+                       initStgVarSets();
+                       daSccs = NIL;
+                       break;
+
+        case MARK    : markPairs();
+                       markStgVarSets();
+                       mark(daSccs);
+                       break;
+
+        case GCDONE  : checkStgVarSets();
+                       break;
+    }
 }
 
 /*-------------------------------------------------------------------------*/