]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Looks like major changes, but in fact are just rearrangements and
authorJulian Seward <jseward@acm.org>
Wed, 29 Dec 2004 17:09:11 +0000 (17:09 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 29 Dec 2004 17:09:11 +0000 (17:09 +0000)
further commenting of code.  Rearrange sequence of procedures to
better reflect the flow of data through iropt.  Add documentation at
the start explaining more precisely what it does.

The only functional change is to do initial flattening even at
optimisation level 0, so we can disable most of iropt for debugging
purposes and still have a functioning Valgrind.

git-svn-id: svn://svn.valgrind.org/vex/trunk@679

VEX/priv/ir/iropt.c

index 8889f0fc6f59396ea30460cc40a83c332e480230..4f440551a2911984c22047ecacb42102f0f17485 100644 (file)
 #define DEBUG_IROPT 0
 
 
+/* What iropt does, 29 Dec 04.
+
+   It takes an IRBB and produces a new one with the same meaning,
+   defined thus:
+
+   After execution of the new BB, all guest state and guest memory is
+   the same as after execution of the original.  This is true
+   regardless of how the block was exited (at the end vs side exit).
+
+   In addition, parts of the guest state will be identical to that
+   created by execution of the original at the following observation
+   points:
+
+   * In a dirty helper call, any parts of the guest state that the
+     helper states that it reads or modifies will be up to date.
+     Also, guest memory will be up to date.  Parts of the guest state
+     not marked as being read or modified by the helper cannot be
+     assumed to be up-to-date at the point where the helper is called.
+
+   * Immediately prior to any load or store, those parts of the guest
+     state marked as requiring precise exceptions will be up to date.
+     Also, guest memory will be up to date.  Parts of the guest state
+     not marked as requiring precise exceptions cannot be assumed to
+     be up-to-date at the point of the load/store.
+
+   The relative order of loads and stores (including loads/stores of
+   guest memory done by dirty helpers annotated as such) is not
+   changed.  However, the relative order of loads with no intervening
+   stores/modifies may be changed.  
+
+   Transformation order
+   ~~~~~~~~~~~~~~~~~~~~
+
+   There are three levels of optimisation, controlled by
+   vex_control.iropt_level.  Define first:
+
+   "Cheap transformations" are the following sequence:
+      * Redundant-Get removal
+      * Redundant-Put removal
+      * Constant propagation/folding
+      * Dead code removal
+      * Specialisation of clean helper functions
+      * Dead code removal
+
+   "Expensive transformations" are the following sequence:
+      * CSE
+      * Folding of add/sub chains
+      * Redundant-GetI removal
+      * Redundant-PutI removal
+      * Dead code removal
+      * (redo flattening)
+
+   Then the transformations are as follows, as defined by
+   vex_control.iropt_level:
+
+   Level 0: 
+      * Flatten into atomic form.
+
+   Level 1: the following sequence:
+      * Flatten into atomic form.
+      * Cheap transformations.
+      * Flatten into atomic form.
+
+   Level 2: the following sequence
+      * Flatten into atomic form.
+      * Cheap transformations.
+      * If block contains GetI or PutI, Expensive transformations.
+      * Try unrolling loops.  Three possible outcomes:
+        - No effect: do nothing more.
+        - Unrolled a loop, and block does not contain GetI or PutI:
+          Do: * CSE
+              * Dead code removal
+              * Flatten into atomic form.
+        - Unrolled a loop, and block contains GetI or PutI:
+          Do: * Expensive transformations
+              * Cheap transformations
+              * Flatten into atomic form.
+*/
+
 /* Implementation notes, 12 Oct 04.
 
    TODO: improve pessimistic handling of precise exceptions
 /*--- Finite mappery, of a sort                               ---*/
 /*---------------------------------------------------------------*/
 
-/* General map from HWord-sized thing HWord-sized thing.  Could be
-   done faster by hashing. */
+/* General map from HWord-sized thing HWord-sized thing.  Could be by
+   hashing, but it's not clear whether or not this would really be any
+   faster. */
 
 typedef
    struct {
@@ -101,7 +181,7 @@ static HashHW* newHHW ( void )
 static Bool lookupHHW ( HashHW* h, /*OUT*/HWord* val, HWord key )
 {
    Int i;
-   //vex_printf("lookupHHW(%llx)\n", key );
+   /* vex_printf("lookupHHW(%llx)\n", key ); */
    for (i = 0; i < h->used; i++) {
       if (h->inuse[i] && h->key[i] == key) {
          if (val)
@@ -118,8 +198,8 @@ static Bool lookupHHW ( HashHW* h, /*OUT*/HWord* val, HWord key )
 static void addToHHW ( HashHW* h, HWord key, HWord val )
 {
    Int i, j;
+   /* vex_printf("addToHHW(%llx, %llx)\n", key, val); */
 
-   //vex_printf("addToHHW(%llx, %llx)\n", key, val);
    /* Find and replace existing binding, if any. */
    for (i = 0; i < h->used; i++) {
       if (h->inuse[i] && h->key[i] == key) {
@@ -158,7 +238,7 @@ static void addToHHW ( HashHW* h, HWord key, HWord val )
 
 
 /*---------------------------------------------------------------*/
-/*--- Flattening out a BB into pure SSA form                  ---*/
+/*--- Flattening out a BB into atomic SSA form                ---*/
 /*---------------------------------------------------------------*/
 
 /* Non-critical helper, heuristic for reducing the number of tmp-tmp
@@ -339,6 +419,7 @@ static void flatten_Stmt ( IRBB* bb, IRStmt* st )
    }
 }
 
+
 static IRBB* flatten_BB ( IRBB* in )
 {
    Int   i;
@@ -354,2885 +435,2889 @@ static IRBB* flatten_BB ( IRBB* in )
 }
 
 
-
 /*---------------------------------------------------------------*/
-/*--- Constant propagation and folding                        ---*/
+/*--- In-place removal of redundant GETs                      ---*/
 /*---------------------------------------------------------------*/
 
-/* The env in this section is a map from IRTemp to IRExpr*,
-   that is, an array indexed by IRTemp. */
+/* Scan forwards, building up an environment binding (min offset, max
+   offset) pairs to values, which will either be temps or constants.
 
-/* Are both expressions simply the same IRTemp ? */
-static Bool sameIRTemps ( IRExpr* e1, IRExpr* e2 )
+   On seeing 't = Get(minoff,maxoff)', look up (minoff,maxoff) in the
+   env and if it matches, replace the Get with the stored value.  If
+   there is no match, add a (minoff,maxoff) :-> t binding.
+
+   On seeing 'Put (minoff,maxoff) = t or c', first remove in the env
+   any binding which fully or partially overlaps with (minoff,maxoff).
+   Then add a new (minoff,maxoff) :-> t or c binding.  */
+
+/* Extract the min/max offsets from a guest state array descriptor. */
+
+inline
+static void getArrayBounds ( IRArray* descr, UInt* minoff, UInt* maxoff )
 {
-   return e1->tag == Iex_Tmp
-          && e2->tag == Iex_Tmp
-          && e1->Iex.Tmp.tmp == e2->Iex.Tmp.tmp;
+   *minoff = descr->base;
+   *maxoff = *minoff + descr->nElems*sizeofIRType(descr->elemTy) - 1;
+   vassert((*minoff & ~0xFFFF) == 0);
+   vassert((*maxoff & ~0xFFFF) == 0);
+   vassert(*minoff <= *maxoff);
 }
 
-static Bool notBool ( Bool b )
+/* Create keys, of the form ((minoffset << 16) | maxoffset). */
+
+static UInt mk_key_GetPut ( Int offset, IRType ty )
 {
-   if (b == True) return False;
-   if (b == False) return True;
-   vpanic("notBool");
+   /* offset should fit in 16 bits. */
+   UInt minoff = offset;
+   UInt maxoff = minoff + sizeofIRType(ty) - 1;
+   vassert((minoff & ~0xFFFF) == 0);
+   vassert((maxoff & ~0xFFFF) == 0);
+   return (minoff << 16) | maxoff;
 }
 
-static IRExpr* fold_Expr ( IRExpr* e )
+static UInt mk_key_GetIPutI ( IRArray* descr )
 {
-   Int     shift;
-   IRExpr* e2 = e; /* e2 is the result of folding e, if possible */
+   UInt minoff, maxoff;
+   getArrayBounds( descr, &minoff, &maxoff );
+   vassert((minoff & ~0xFFFF) == 0);
+   vassert((maxoff & ~0xFFFF) == 0);
+   return (minoff << 16) | maxoff;
+}
 
-   /* UNARY ops */
-   if (e->tag == Iex_Unop
-       && e->Iex.Unop.arg->tag == Iex_Const) {
-      switch (e->Iex.Unop.op) {
-         case Iop_1Uto8:
-            e2 = IRExpr_Const(IRConst_U8(
-                    e->Iex.Unop.arg->Iex.Const.con->Ico.U1
-                    ? 1 : 0));
-            break;
-         case Iop_1Uto32:
-            e2 = IRExpr_Const(IRConst_U32(
-                    e->Iex.Unop.arg->Iex.Const.con->Ico.U1
-                    ? 1 : 0));
-            break;
+/* Supposing h has keys of the form generated by mk_key_GetPut and
+   mk_key_GetIPutI, invalidate any key which overlaps (k_lo
+   .. k_hi). 
+*/
+static void invalidateOverlaps ( HashHW* h, UInt k_lo, UInt k_hi )
+{
+   Int  j;
+   UInt e_lo, e_hi;
+   vassert(k_lo <= k_hi);
+   /* invalidate any env entries which in any way overlap (k_lo
+      .. k_hi) */
+   /* vex_printf("invalidate %d .. %d\n", k_lo, k_hi ); */
 
-         case Iop_1Sto32:
-            e2 = IRExpr_Const(IRConst_U32(
-                    e->Iex.Unop.arg->Iex.Const.con->Ico.U1
-                    ? 0xFFFFFFFF : 0));
-            break;
-         case Iop_1Sto64:
-            e2 = IRExpr_Const(IRConst_U64(
-                    e->Iex.Unop.arg->Iex.Const.con->Ico.U1
-                    ? 0xFFFFFFFFFFFFFFFFULL : 0));
-            break;
+   for (j = 0; j < h->used; j++) {
+      if (!h->inuse[j]) 
+         continue;
+      e_lo = (((UInt)h->key[j]) >> 16) & 0xFFFF;
+      e_hi = ((UInt)h->key[j]) & 0xFFFF;
+      vassert(e_lo <= e_hi);
+      if (e_hi < k_lo || k_hi < e_lo)
+         continue; /* no overlap possible */
+      else
+         /* overlap; invalidate */
+         h->inuse[j] = False;
+   }
+}
 
-         case Iop_8Sto32: {
-            /* signed */ Int s32 = e->Iex.Unop.arg->Iex.Const.con->Ico.U8;
-            s32 <<= 24;
-            s32 >>= 24;
-            e2 = IRExpr_Const(IRConst_U32((UInt)s32));
-            break;
-         }
-         case Iop_8Uto32:
-            e2 = IRExpr_Const(IRConst_U32(
-                    0xFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U8));
-            break;
-         case Iop_16Uto32:
-            e2 = IRExpr_Const(IRConst_U32(
-                    0xFFFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U16));
-            break;
-         case Iop_32to16:
-            e2 = IRExpr_Const(IRConst_U16(
-                    0xFFFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U32));
-            break;
-         case Iop_32to8:
-            e2 = IRExpr_Const(IRConst_U8(
-                    0xFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U32));
-            break;
-         case Iop_32to1:
-            e2 = IRExpr_Const(IRConst_U1(
-                    0==e->Iex.Unop.arg->Iex.Const.con->Ico.U32
-                       ? False : True));
-            break;
 
-         case Iop_Not32:
-            e2 = IRExpr_Const(IRConst_U32(
-                    ~ (e->Iex.Unop.arg->Iex.Const.con->Ico.U32)));
-            break;
-         case Iop_Not16:
-            e2 = IRExpr_Const(IRConst_U16(
-                    ~ (e->Iex.Unop.arg->Iex.Const.con->Ico.U16)));
-            break;
-         case Iop_Not8:
-            e2 = IRExpr_Const(IRConst_U8(
-                    ~ (e->Iex.Unop.arg->Iex.Const.con->Ico.U8)));
-            break;
+static void redundant_get_removal_BB ( IRBB* bb )
+{
+   HashHW* env = newHHW();
+   UInt    key = 0; /* keep gcc -O happy */
+   Int     i, j;
+   HWord   val;
 
-         case Iop_Not1:
-            e2 = IRExpr_Const(IRConst_U1(
-                    notBool(e->Iex.Unop.arg->Iex.Const.con->Ico.U1)));
-            break;
+   for (i = 0; i < bb->stmts_used; i++) {
+      IRStmt* st = bb->stmts[i];
 
-         case Iop_64to32: {
-            ULong w64 = e->Iex.Unop.arg->Iex.Const.con->Ico.U64;
-            w64 &= 0x00000000FFFFFFFFULL;
-            e2 = IRExpr_Const(IRConst_U32( (UInt)w64 ));
-            break;
+      if (!st)
+         continue;
+
+      /* Deal with Gets */
+      if (st->tag == Ist_Tmp
+          && st->Ist.Tmp.data->tag == Iex_Get) {
+         /* st is 't = Get(...)'.  Look up in the environment and see
+            if the Get can be replaced. */
+         IRExpr* get = st->Ist.Tmp.data;
+         key = (HWord)mk_key_GetPut( get->Iex.Get.offset, 
+                                     get->Iex.Get.ty );
+         if (lookupHHW(env, &val, (HWord)key)) {
+            /* found it */
+            /* Note, we could do better here.  If the types are
+               different we don't do the substitution, since doing so
+               could lead to invalidly-typed IR.  An improvement would
+               be to stick in a reinterpret-style cast, although that
+               would make maintaining flatness more difficult. */
+            IRExpr* valE    = (IRExpr*)val;
+            Bool    typesOK = typeOfIRExpr(bb->tyenv,valE) 
+                              == st->Ist.Tmp.data->Iex.Get.ty;
+            if (typesOK && DEBUG_IROPT) {
+               vex_printf("rGET: "); ppIRExpr(get);
+               vex_printf("  ->  "); ppIRExpr(valE);
+               vex_printf("\n");
+            }
+            if (typesOK)
+               bb->stmts[i] = IRStmt_Tmp(st->Ist.Tmp.tmp, valE);
+         } else {
+            /* Not found, but at least we know that t and the Get(...)
+               are now associated.  So add a binding to reflect that
+               fact. */
+            addToHHW( env, (HWord)key, 
+                           (HWord)(IRExpr_Tmp(st->Ist.Tmp.tmp)) );
          }
+      }
 
-         case Iop_64HIto32: {
-            ULong w64 = e->Iex.Unop.arg->Iex.Const.con->Ico.U64;
-            w64 >>= 32;
-            e2 = IRExpr_Const(IRConst_U32( (UInt)w64 ));
-            break;
+      /* Deal with Puts: invalidate any env entries overlapped by this
+         Put */
+      if (st->tag == Ist_Put || st->tag == Ist_PutI) {
+         UInt k_lo, k_hi;
+         if (st->tag == Ist_Put) {
+            key = mk_key_GetPut( st->Ist.Put.offset, 
+                                 typeOfIRExpr(bb->tyenv,st->Ist.Put.data) );
+         } else {
+            vassert(st->tag == Ist_PutI);
+            key = mk_key_GetIPutI( st->Ist.PutI.descr );
          }
 
-         default: 
-            goto unhandled;
+         k_lo = (key >> 16) & 0xFFFF;
+         k_hi = key & 0xFFFF;
+         invalidateOverlaps(env, k_lo, k_hi);
+      }
+      else
+      if (st->tag == Ist_Dirty) {
+         /* Deal with dirty helpers which write or modify guest state.
+            Invalidate the entire env.  We could do a lot better
+            here. */
+         IRDirty* d      = st->Ist.Dirty.details;
+         Bool     writes = False;
+         for (j = 0; j < d->nFxState; j++) {
+            if (d->fxState[j].fx == Ifx_Modify 
+                || d->fxState[j].fx == Ifx_Write)
+            writes = True;
+         }
+         if (writes) {
+            /* dump the entire env (not clever, but correct ...) */
+            for (j = 0; j < env->used; j++)
+               env->inuse[j] = False;
+            if (0) vex_printf("rGET: trash env due to dirty helper\n");
+         }
       }
-   }
 
-   /* BINARY ops */
-   if (e->tag == Iex_Binop) {
-      if (e->Iex.Binop.arg1->tag == Iex_Const
-          && e->Iex.Binop.arg2->tag == Iex_Const) {
-         /* cases where both args are consts */
-         switch (e->Iex.Binop.op) {
+      /* add this one to the env, if appropriate */
+      if (st->tag == Ist_Put) {
+         vassert(isAtom(st->Ist.Put.data));
+         addToHHW( env, (HWord)key, (HWord)(st->Ist.Put.data));
+      }
 
-            /* --- Iop_Or --- */
-            case Iop_Or8:
-               e2 = IRExpr_Const(IRConst_U8(0xFF & 
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
-                        | e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
-               break;
-            case Iop_Or16:
-               e2 = IRExpr_Const(IRConst_U16(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U16
-                        | e->Iex.Binop.arg2->Iex.Const.con->Ico.U16)));
-               break;
-            case Iop_Or32:
-               e2 = IRExpr_Const(IRConst_U32(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
-                        | e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
-               break;
+   } /* for (i = 0; i < bb->stmts_used; i++) */
 
-            case Iop_Xor8:
-               e2 = IRExpr_Const(IRConst_U8(0xFF & 
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
-                        ^ e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
-               break;
-            case Iop_And8:
-               e2 = IRExpr_Const(IRConst_U8(0xFF & 
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
-                        & e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
-               break;
-            case Iop_Add8:
-               e2 = IRExpr_Const(IRConst_U8(0xFF & 
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
-                        + e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
-               break;
-            case Iop_Sub8:
-               e2 = IRExpr_Const(IRConst_U8(0xFF & 
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
-                        - e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
-               break;
-            case Iop_Sub32:
-               e2 = IRExpr_Const(IRConst_U32(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
-                        - e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
-               break;
-            case Iop_Add32:
-               e2 = IRExpr_Const(IRConst_U32(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
-                        + e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
-               break;
-            case Iop_Xor32:
-               e2 = IRExpr_Const(IRConst_U32(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
-                        ^ e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
-               break;
-            case Iop_And32:
-               e2 = IRExpr_Const(IRConst_U32(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
-                        & e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
-               break;
+}
 
-            case Iop_Mul32:
-               e2 = IRExpr_Const(IRConst_U32(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
-                        * e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
-               break;
-            case Iop_Shl32:
-               vassert(e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U8);
-               shift = (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U8);
-               if (shift >= 0 && shift <= 31)
-                  e2 = IRExpr_Const(IRConst_U32(
-                          (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
-                           << shift)));
-               break;
-            case Iop_Sar32: {
-               /* paranoid ... */
-               /*signed*/ Int s32;
-               vassert(e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U8);
-               s32   = (Int)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32);
-               shift = (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U8);
-               if (shift >= 0 && shift <= 31) {
-                  s32 >>=/*signed*/ shift;
-                  e2 = IRExpr_Const(IRConst_U32((UInt)s32));
-               }
-               break;
-            }
-            case Iop_Shr32: {
-               /* paranoid ... */
-               /*unsigned*/ UInt s32;
-               vassert(e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U8);
-               s32   = (Int)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32);
-               shift = (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U8);
-               if (shift >= 0 && shift <= 31) {
-                  s32 >>=/*unsigned*/ shift;
-                  e2 = IRExpr_Const(IRConst_U32((UInt)s32));
-               }
-               break;
-            }
-            case Iop_CmpEQ32:
-               e2 = IRExpr_Const(IRConst_U1(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
-                        == e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
-               break;
-            case Iop_CmpNE32:
-               e2 = IRExpr_Const(IRConst_U1(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
-                        != e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
-               break;
 
-            case Iop_CmpNE64:
-               e2 = IRExpr_Const(IRConst_U1(
-                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U64
-                        != e->Iex.Binop.arg2->Iex.Const.con->Ico.U64)));
-               break;
+/*---------------------------------------------------------------*/
+/*--- In-place removal of redundant PUTs                      ---*/
+/*---------------------------------------------------------------*/
 
-            case Iop_CmpNE8:
-               e2 = IRExpr_Const(IRConst_U1(
-                       ((0xFF & e->Iex.Binop.arg1->Iex.Const.con->Ico.U8)
-                        != (0xFF & e->Iex.Binop.arg2->Iex.Const.con->Ico.U8))));
-               break;
+/* Find any Get uses in st and invalidate any partially or fully
+   overlapping ranges listed in env.  Due to the flattening phase, the
+   only stmt kind we expect to find a Get on is IRStmt_Tmp. */
 
-            case Iop_CmpLE32U:
-               e2 = IRExpr_Const(IRConst_U1(
-                       ((UInt)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)
-                        <= (UInt)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32))));
-               break;
-            case Iop_CmpLE32S:
-               e2 = IRExpr_Const(IRConst_U1(
-                       ((Int)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)
-                        <= (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32))));
-               break;
+static void handle_gets_Stmt ( 
+               HashHW* env, 
+               IRStmt* st,
+               Bool (*preciseMemExnsFn)(Int,Int)
+            )
+{
+   Int     j;
+   UInt    key = 0; /* keep gcc -O happy */
+   Bool    isGet;
+   Bool    memRW = False;
+   IRExpr* e;
 
-            case Iop_CmpLT32S:
-               e2 = IRExpr_Const(IRConst_U1(
-                       ((Int)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)
-                        < (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32))));
+   switch (st->tag) {
+
+      /* This is the only interesting case.  Deal with Gets in the RHS
+         expression. */
+      case Ist_Tmp:
+         e = st->Ist.Tmp.data;
+         switch (e->tag) {
+            case Iex_Get:
+               isGet = True;
+               key = mk_key_GetPut ( e->Iex.Get.offset, e->Iex.Get.ty );
                break;
-            case Iop_CmpLT32U:
-               e2 = IRExpr_Const(IRConst_U1(
-                       ((UInt)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)
-                        < (UInt)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32))));
+            case Iex_GetI:
+               isGet = True;
+               key = mk_key_GetIPutI ( e->Iex.GetI.descr );
                break;
-
-            case Iop_32HLto64:
-               e2 = IRExpr_Const(IRConst_U64(
-                       (((ULong)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)) << 32)
-                       | ((ULong)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)) 
-                    ));
+            case Iex_LDle:
+               isGet = False;
+               memRW = True;
                break;
-            default:
-               goto unhandled;
+            default: 
+               isGet = False;
          }
+         if (isGet) {
+            UInt k_lo, k_hi;
+            k_lo = (key >> 16) & 0xFFFF;
+            k_hi = key & 0xFFFF;
+            invalidateOverlaps(env, k_lo, k_hi);
+         }
+         break;
 
-      } else {
-
-         /* other cases (identities, etc) */
-         /* Shl32(x,0) ==> x */
-         if (e->Iex.Binop.op == Iop_Shl32
-             && e->Iex.Binop.arg2->tag == Iex_Const
-             && e->Iex.Binop.arg2->Iex.Const.con->Ico.U8 == 0) {
-            e2 = e->Iex.Binop.arg1;
-         } else
-
-         /* Or32/Add32(x,0) ==> x */
-         if ((e->Iex.Binop.op == Iop_Add32 || e->Iex.Binop.op == Iop_Or32)
-             && e->Iex.Binop.arg2->tag == Iex_Const
-             && e->Iex.Binop.arg2->Iex.Const.con->Ico.U32 == 0) {
-            e2 = e->Iex.Binop.arg1;
-         } else
-
-         /* Or64/Add64(x,0) ==> x */
-         if ((e->Iex.Binop.op == Iop_Add64 || e->Iex.Binop.op == Iop_Or64)
-             && e->Iex.Binop.arg2->tag == Iex_Const
-             && e->Iex.Binop.arg2->Iex.Const.con->Ico.U64 == 0) {
-            e2 = e->Iex.Binop.arg1;
-         } else
-
-         /* And32(x,0xFFFFFFFF) ==> x */
-         if (e->Iex.Binop.op == Iop_And32
-             && e->Iex.Binop.arg2->tag == Iex_Const
-             && e->Iex.Binop.arg2->Iex.Const.con->Ico.U32 == 0xFFFFFFFF) {
-            e2 = e->Iex.Binop.arg1;
-         } else
+      /* Be very conservative for dirty helper calls; dump the entire
+         environment.  The helper might read guest state, in which
+         case it needs to be flushed first.  Also, the helper might
+         access guest memory, in which case all parts of the guest
+         state requiring precise exceptions needs to be flushed.  The
+         crude solution is just to flush everything; we could easily
+         enough do a lot better if needed. */
+      case Ist_Dirty:
+         for (j = 0; j < env->used; j++)
+            env->inuse[j] = False;
+         break;
 
-         /* Or32(0,x) ==> x */
-         if (e->Iex.Binop.op == Iop_Or32
-             && e->Iex.Binop.arg1->tag == Iex_Const
-             && e->Iex.Binop.arg1->Iex.Const.con->Ico.U32 == 0) {
-            e2 = e->Iex.Binop.arg2;
-         } else
+      /* all other cases are boring. */
+      case Ist_STle:
+         vassert(isAtom(st->Ist.STle.addr));
+         vassert(isAtom(st->Ist.STle.data));
+         memRW = True;
+         break;
 
-         /* Or8/16/32/64(t,t) ==> t, for some IRTemp t */
-         /* And8/16/32/64(t,t) ==> t, for some IRTemp t */
-         if (   (e->Iex.Binop.op == Iop_And64
-              || e->Iex.Binop.op == Iop_And32
-              || e->Iex.Binop.op == Iop_And16
-              || e->Iex.Binop.op == Iop_And8
-              || e->Iex.Binop.op == Iop_Or64
-              || e->Iex.Binop.op == Iop_Or32
-              || e->Iex.Binop.op == Iop_Or16
-              || e->Iex.Binop.op == Iop_Or8)
-             && sameIRTemps(e->Iex.Binop.arg1, e->Iex.Binop.arg2)) {
-            e2 = e->Iex.Binop.arg1;
-         }
+      case Ist_Exit:
+         vassert(isAtom(st->Ist.Exit.guard));
+         break;
 
-      }
-   }
+      case Ist_PutI:
+         vassert(isAtom(st->Ist.PutI.ix));
+         vassert(isAtom(st->Ist.PutI.data));
+         break;
 
-   /* Mux0X */
-   if (e->tag == Iex_Mux0X
-       && e->Iex.Mux0X.cond->tag == Iex_Const) {
-      Bool zero;
-      /* assured us by the IR type rules */
-      vassert(e->Iex.Mux0X.cond->Iex.Const.con->tag == Ico_U8);
-      zero = 0 == e->Iex.Mux0X.cond->Iex.Const.con->Ico.U8;
-      e2 = zero ? e->Iex.Mux0X.expr0 : e->Iex.Mux0X.exprX;
+      default:
+         vex_printf("\n");
+         ppIRStmt(st);
+         vex_printf("\n");
+         vpanic("handle_gets_Stmt");
    }
 
-   if (DEBUG_IROPT && e2 != e) {
-      vex_printf("FOLD: "); 
-      ppIRExpr(e); vex_printf("  ->  ");
-      ppIRExpr(e2); vex_printf("\n");
-   }
-
-   return e2;
+   if (memRW) {
+      /* This statement accesses memory.  So we need to dump all parts
+         of the environment corresponding to guest state that may not
+         be reordered with respect to memory references.  That means
+         at least the stack pointer. */
+      for (j = 0; j < env->used; j++) {
+         if (!env->inuse[j])
+            continue;
+         if (vex_control.iropt_precise_memory_exns) {
+            /* Precise exceptions required.  Flush all guest state. */
+            env->inuse[j] = False;
+         } else {
+            /* Just flush the minimal amount required, as computed by
+               preciseMemExnsFn. */
+            HWord k_lo = (env->key[j] >> 16) & 0xFFFF;
+            HWord k_hi = env->key[j] & 0xFFFF;
+            if (preciseMemExnsFn( k_lo, k_hi ))
+               env->inuse[j] = False;
+         }
+      }
+   } /* if (memRW) */
 
- unhandled:
-#  if 0
-   vex_printf("\n\n");
-   ppIRExpr(e);
-   vpanic("fold_Expr: no rule for the above");
-#  else
-   vex_printf("vex iropt: fold_Expr: no rule for: ");
-   ppIRExpr(e);
-   vex_printf("\n");
-   return e2;
-#  endif
 }
 
 
-/* Apply the subst to a simple 1-level expression -- guaranteed to be
-   1-level due to previous flattening pass. */
+/* Scan backwards, building up a set of (min offset, max
+   offset) pairs, indicating those parts of the guest state
+   for which the next event is a write.
 
-static IRExpr* subst_Expr ( IRExpr** env, IRExpr* ex )
-{
-   switch (ex->tag) {
-      case Iex_Tmp:
-         if (env[(Int)ex->Iex.Tmp.tmp] != NULL) {
-            return env[(Int)ex->Iex.Tmp.tmp];
-         } else {
-            /* not bound in env */
-            return ex;
-         }
+   On seeing a conditional exit, empty the set.
 
-      case Iex_Const:
-      case Iex_Get:
-         return ex;
+   On seeing 'Put (minoff,maxoff) = t or c', if (minoff,maxoff) is
+   completely within the set, remove the Put.  Otherwise, add
+   (minoff,maxoff) to the set.
 
-      case Iex_GetI:
-         vassert(isAtom(ex->Iex.GetI.ix));
-         return IRExpr_GetI(
-            ex->Iex.GetI.descr,
-            subst_Expr(env, ex->Iex.GetI.ix),
-            ex->Iex.GetI.bias
-         );
+   On seeing 'Get (minoff,maxoff)', remove any part of the set
+   overlapping (minoff,maxoff).
+*/
 
-      case Iex_Binop:
-         vassert(isAtom(ex->Iex.Binop.arg1));
-         vassert(isAtom(ex->Iex.Binop.arg2));
-         return IRExpr_Binop(
-                   ex->Iex.Binop.op,
-                   subst_Expr(env, ex->Iex.Binop.arg1),
-                   subst_Expr(env, ex->Iex.Binop.arg2)
-                );
+static void redundant_put_removal_BB ( 
+               IRBB* bb,
+               Bool (*preciseMemExnsFn)(Int,Int)
+            )
+{
+   Int     i, j;
+   Bool    isPut;
+   IRStmt* st;
+   UInt    key = 0; /* keep gcc -O happy */
 
-      case Iex_Unop:
-         vassert(isAtom(ex->Iex.Unop.arg));
-         return IRExpr_Unop(
-                   ex->Iex.Unop.op,
-                   subst_Expr(env, ex->Iex.Unop.arg)
-                );
+   HashHW* env = newHHW();
+   for (i = bb->stmts_used-1; i >= 0; i--) {
+      st = bb->stmts[i];
+      if (!st)
+         continue;
 
-      case Iex_LDle:
-         vassert(isAtom(ex->Iex.LDle.addr));
-         return IRExpr_LDle(
-                   ex->Iex.LDle.ty,
-                   subst_Expr(env, ex->Iex.LDle.addr)
-                );
+      /* Deal with conditional exits. */
+      if (st->tag == Ist_Exit) {
+         /* Since control may not get beyond this point, we must empty
+            out the set, since we can no longer claim that the next
+            event for any part of the guest state is definitely a
+            write. */
+         vassert(isAtom(st->Ist.Exit.guard));
+         for (j = 0; j < env->used; j++)
+            env->inuse[j] = False;
+         continue;
+      }
 
-      case Iex_CCall: {
-         Int      i;
-         IRExpr** args2 = sopyIRExprVec(ex->Iex.CCall.args);
-         for (i = 0; args2[i]; i++) {
-            vassert(isAtom(args2[i]));
-            args2[i] = subst_Expr(env, args2[i]);
+      /* Deal with Puts */
+      switch (st->tag) {
+         case Ist_Put: 
+            isPut = True;
+            key = mk_key_GetPut( st->Ist.Put.offset, 
+                                 typeOfIRExpr(bb->tyenv,st->Ist.Put.data) );
+            vassert(isAtom(st->Ist.Put.data));
+            break;
+         case Ist_PutI:
+            isPut = True;
+            key = mk_key_GetIPutI( st->Ist.PutI.descr );
+            vassert(isAtom(st->Ist.PutI.ix));
+            vassert(isAtom(st->Ist.PutI.data));
+            break;
+         default: 
+            isPut = False;
+      }
+      if (isPut && st->tag != Ist_PutI) {
+         /* See if any single entry in env overlaps this Put.  This is
+            simplistic in that the transformation is valid if, say, two
+            or more entries in the env overlap this Put, but the use of
+            lookupHHW will only find a single entry which exactly
+            overlaps this Put.  This is suboptimal but safe. */
+         if (lookupHHW(env, NULL, (HWord)key)) {
+            /* This Put is redundant because a later one will overwrite
+               it.  So NULL (nop) it out. */
+            if (DEBUG_IROPT) {
+               vex_printf("rPUT: "); ppIRStmt(st);
+               vex_printf("\n");
+            }
+            bb->stmts[i] = NULL;
+         } else {
+            /* We can't demonstrate that this Put is redundant, so add it
+               to the running collection. */
+            addToHHW(env, (HWord)key, 0);
          }
-         return IRExpr_CCall(
-                   ex->Iex.CCall.cee,
-                   ex->Iex.CCall.retty,
-                   args2 
-                );
+         continue;
       }
 
-      case Iex_Mux0X:
-         vassert(isAtom(ex->Iex.Mux0X.cond));
-         vassert(isAtom(ex->Iex.Mux0X.expr0));
-         vassert(isAtom(ex->Iex.Mux0X.exprX));
-         return IRExpr_Mux0X(
-                   subst_Expr(env, ex->Iex.Mux0X.cond),
-                   subst_Expr(env, ex->Iex.Mux0X.expr0),
-                   subst_Expr(env, ex->Iex.Mux0X.exprX)
-                );
-
-      default:
-         vex_printf("\n\n"); ppIRExpr(ex);
-         vpanic("subst_Expr");
-      
+      /* Deal with Gets.  These remove bits of the environment since
+         appearance of a Get means that the next event for that slice
+         of the guest state is no longer a write, but a read. */
+      handle_gets_Stmt( env, st, preciseMemExnsFn );
    }
 }
 
 
-/* Apply the subst to stmt, then fold the result as much as possible.
-   Much simplified due to stmt being previously flattened.  Returning
-   NULL means the statement has been turned into a no-op. */
+/*---------------------------------------------------------------*/
+/*--- Constant propagation and folding                        ---*/
+/*---------------------------------------------------------------*/
 
-static IRStmt* subst_and_fold_Stmt ( IRExpr** env, IRStmt* st )
-{
-#  if 0
-   vex_printf("\nsubst and fold stmt\n");
-   ppIRStmt(st);
-   vex_printf("\n");
-#  endif
+/* The env in this section is a map from IRTemp to IRExpr*,
+   that is, an array indexed by IRTemp. */
 
-   switch (st->tag) {
-      case Ist_Put:
-         vassert(isAtom(st->Ist.Put.data));
-         return IRStmt_Put(
-                   st->Ist.Put.offset, 
-                   fold_Expr(subst_Expr(env, st->Ist.Put.data)) 
-                );
+/* Are both expressions simply the same IRTemp ? */
+static Bool sameIRTemps ( IRExpr* e1, IRExpr* e2 )
+{
+   return e1->tag == Iex_Tmp
+          && e2->tag == Iex_Tmp
+          && e1->Iex.Tmp.tmp == e2->Iex.Tmp.tmp;
+}
 
-      case Ist_PutI:
-         vassert(isAtom(st->Ist.PutI.ix));
-         vassert(isAtom(st->Ist.PutI.data));
-         return IRStmt_PutI(
-                   st->Ist.PutI.descr,
-                   fold_Expr(subst_Expr(env, st->Ist.PutI.ix)),
-                   st->Ist.PutI.bias,
-                   fold_Expr(subst_Expr(env, st->Ist.PutI.data))
-                );
+static Bool notBool ( Bool b )
+{
+   if (b == True) return False;
+   if (b == False) return True;
+   vpanic("notBool");
+}
 
-      case Ist_Tmp:
-         /* This is the one place where an expr (st->Ist.Tmp.data) is
-            allowed to be more than just a constant or a tmp. */
-         return IRStmt_Tmp(
-                   st->Ist.Tmp.tmp,
-                   fold_Expr(subst_Expr(env, st->Ist.Tmp.data))
-                );
+static IRExpr* fold_Expr ( IRExpr* e )
+{
+   Int     shift;
+   IRExpr* e2 = e; /* e2 is the result of folding e, if possible */
 
-      case Ist_STle:
-         vassert(isAtom(st->Ist.STle.addr));
-         vassert(isAtom(st->Ist.STle.data));
-         return IRStmt_STle(
-                   fold_Expr(subst_Expr(env, st->Ist.STle.addr)),
-                   fold_Expr(subst_Expr(env, st->Ist.STle.data))
-                );
+   /* UNARY ops */
+   if (e->tag == Iex_Unop
+       && e->Iex.Unop.arg->tag == Iex_Const) {
+      switch (e->Iex.Unop.op) {
+         case Iop_1Uto8:
+            e2 = IRExpr_Const(IRConst_U8(
+                    e->Iex.Unop.arg->Iex.Const.con->Ico.U1
+                    ? 1 : 0));
+            break;
+         case Iop_1Uto32:
+            e2 = IRExpr_Const(IRConst_U32(
+                    e->Iex.Unop.arg->Iex.Const.con->Ico.U1
+                    ? 1 : 0));
+            break;
 
-      case Ist_Dirty: {
-         Int     i;
-         IRDirty *d, *d2;
-         d = st->Ist.Dirty.details;
-         d2 = emptyIRDirty();
-         *d2 = *d;
-         d2->args = sopyIRExprVec(d2->args);
-         if (d2->mFx != Ifx_None) {
-            vassert(isAtom(d2->mAddr));
-            d2->mAddr = fold_Expr(subst_Expr(env, d2->mAddr));
-         }
-         vassert(isAtom(d2->guard));
-         d2->guard = fold_Expr(subst_Expr(env, d2->guard));
-         for (i = 0; d2->args[i]; i++) {
-            vassert(isAtom(d2->args[i]));
-            d2->args[i] = fold_Expr(subst_Expr(env, d2->args[i]));
-         }
-         return IRStmt_Dirty(d2);
-      }
+         case Iop_1Sto32:
+            e2 = IRExpr_Const(IRConst_U32(
+                    e->Iex.Unop.arg->Iex.Const.con->Ico.U1
+                    ? 0xFFFFFFFF : 0));
+            break;
+         case Iop_1Sto64:
+            e2 = IRExpr_Const(IRConst_U64(
+                    e->Iex.Unop.arg->Iex.Const.con->Ico.U1
+                    ? 0xFFFFFFFFFFFFFFFFULL : 0));
+            break;
 
-      case Ist_Exit: {
-         IRExpr* fcond;
-         vassert(isAtom(st->Ist.Exit.guard));
-         fcond = fold_Expr(subst_Expr(env, st->Ist.Exit.guard));
-         if (fcond->tag == Iex_Const) {
-            /* Interesting.  The condition on this exit has folded down to
-               a constant. */
-            vassert(fcond->Iex.Const.con->tag == Ico_U1);
-            if (fcond->Iex.Const.con->Ico.U1 == False) {
-               /* exit is never going to happen, so dump the statement. */
-               return NULL;
-            } else {
-               vassert(fcond->Iex.Const.con->Ico.U1 == True);
-               /* Hmmm.  The exit has become unconditional.  Leave it as
-                  it is for now, since we'd have to truncate the BB at
-                  this point, which is tricky. */
-               /* fall out into the reconstruct-the-exit code. */
-               if (vex_control.iropt_verbosity > 0) 
-                  /* really a misuse of vex_control.iropt_verbosity */
-                  vex_printf("vex iropt: IRStmt_Exit became unconditional\n");
-            }
+         case Iop_8Sto32: {
+            /* signed */ Int s32 = e->Iex.Unop.arg->Iex.Const.con->Ico.U8;
+            s32 <<= 24;
+            s32 >>= 24;
+            e2 = IRExpr_Const(IRConst_U32((UInt)s32));
+            break;
          }
-         return IRStmt_Exit(fcond, st->Ist.Exit.jk, st->Ist.Exit.dst);
-      }
-
-   default:
-      vex_printf("\n"); ppIRStmt(st);
-      vpanic("subst_and_fold_Stmt");
-   }
-}
-
-
-IRBB* cprop_BB ( IRBB* in )
-{
-   Int      i;
-   IRBB*    out;
-   IRStmt*  st2;
-   Int      n_tmps = in->tyenv->types_used;
-   IRExpr** env = LibVEX_Alloc(n_tmps * sizeof(IRExpr*));
-
-   out = emptyIRBB();
-   out->tyenv = dopyIRTypeEnv( in->tyenv );
-
-   /* Set up the env with which travels forward.  This holds a
-      substitution, mapping IRTemps to atoms, that is, IRExprs which
-      are either IRTemps or IRConsts.  Thus, copy and constant
-      propagation is done.  The environment is to be applied as we
-      move along.  Keys are IRTemps.  Values are IRExpr*s.
-   */
-   for (i = 0; i < n_tmps; i++)
-      env[i] = NULL;
-
-   /* For each original SSA-form stmt ... */
-   for (i = 0; i < in->stmts_used; i++) {
-
-      /* First apply the substitution to the current stmt.  This
-         propagates in any constants and tmp-tmp assignments
-         accumulated prior to this point.  As part of the subst_Stmt
-         call, also then fold any constant expressions resulting. */
-
-      st2 = in->stmts[i];
-
-      /* perhaps st2 is already a no-op? */
-      if (!st2) continue;
-
-      st2 = subst_and_fold_Stmt( env, st2 );
-
-      /* If the statement has been folded into a no-op, forget it. */
-      if (!st2) continue;
-
-      /* Now consider what the stmt looks like.  If it's of the form
-         't = const' or 't1 = t2', add it to the running environment
-         and not to the output BB.  Otherwise, add it to the output
-         BB.  Note, we choose not to propagate const when const is an
-         F64i, so that F64i literals can be CSE'd later.  This helps
-         x86 floating point code generation. */
-
-      if (st2->tag == Ist_Tmp 
-          && st2->Ist.Tmp.data->tag == Iex_Const
-          && st2->Ist.Tmp.data->Iex.Const.con->tag != Ico_F64i) {
-         /* 't = const' -- add to env.  
-             The pair (IRTemp, IRExpr*) is added. */
-         env[(Int)(st2->Ist.Tmp.tmp)] = st2->Ist.Tmp.data;
-      }
-      else
-      if (st2->tag == Ist_Tmp && st2->Ist.Tmp.data->tag == Iex_Tmp) {
-         /* 't1 = t2' -- add to env.  
-             The pair (IRTemp, IRExpr*) is added. */
-         env[(Int)(st2->Ist.Tmp.tmp)] = st2->Ist.Tmp.data;
-      }
-      else {
-         /* Not interesting, copy st2 into the output block. */
-         addStmtToIRBB( out, st2 );
-      }
-   }
-
-   out->next     = subst_Expr( env, in->next );
-   out->jumpkind = in->jumpkind;
-   return out;
-}
-
-
-
-/*---------------------------------------------------------------*/
-/*--- Dead code (t = E) removal                               ---*/
-/*---------------------------------------------------------------*/
-
-/* The type of the HashHW map is: a map from IRTemp to nothing
-   -- really just operating a set or IRTemps.
-*/
-
-inline
-static void addUses_Temp ( Bool* set, IRTemp tmp )
-{
-   set[(Int)tmp] = True;
-}
-
-static void addUses_Expr ( Bool* set, IRExpr* e )
-{
-   Int i;
-   switch (e->tag) {
-      case Iex_GetI:
-         addUses_Expr(set, e->Iex.GetI.ix);
-         return;
-      case Iex_Mux0X:
-         addUses_Expr(set, e->Iex.Mux0X.cond);
-         addUses_Expr(set, e->Iex.Mux0X.expr0);
-         addUses_Expr(set, e->Iex.Mux0X.exprX);
-         return;
-      case Iex_CCall:
-         for (i = 0; e->Iex.CCall.args[i]; i++)
-            addUses_Expr(set, e->Iex.CCall.args[i]);
-         return;
-      case Iex_LDle:
-         addUses_Expr(set, e->Iex.LDle.addr);
-         return;
-      case Iex_Binop:
-         addUses_Expr(set, e->Iex.Binop.arg1);
-         addUses_Expr(set, e->Iex.Binop.arg2);
-         return;
-      case Iex_Unop:
-         addUses_Expr(set, e->Iex.Unop.arg);
-         return;
-      case Iex_Tmp:
-         addUses_Temp(set, e->Iex.Tmp.tmp);
-         return;
-      case Iex_Const:
-      case Iex_Get:
-         return;
-      default:
-         vex_printf("\n");
-         ppIRExpr(e);
-         vpanic("addUses_Expr");
-   }
-}
-
-static void addUses_Stmt ( Bool* set, IRStmt* st )
-{
-   Int      i;
-   IRDirty* d;
-   switch (st->tag) {
-      case Ist_PutI:
-         addUses_Expr(set, st->Ist.PutI.ix);
-         addUses_Expr(set, st->Ist.PutI.data);
-         return;
-      case Ist_Tmp:
-         addUses_Expr(set, st->Ist.Tmp.data);
-         return;
-      case Ist_Put:
-         addUses_Expr(set, st->Ist.Put.data);
-         return;
-      case Ist_STle:
-         addUses_Expr(set, st->Ist.STle.addr);
-         addUses_Expr(set, st->Ist.STle.data);
-         return;
-      case Ist_Dirty:
-         d = st->Ist.Dirty.details;
-         if (d->mFx != Ifx_None)
-            addUses_Expr(set, d->mAddr);
-         addUses_Expr(set, d->guard);
-         for (i = 0; d->args[i] != NULL; i++)
-            addUses_Expr(set, d->args[i]);
-         return;
-      case Ist_Exit:
-         addUses_Expr(set, st->Ist.Exit.guard);
-         return;
-      default:
-         vex_printf("\n");
-         ppIRStmt(st);
-         vpanic("addUses_Stmt");
-   }
-}
-
-
-/* Is this literally IRExpr_Const(IRConst_U1(False)) ? */
-static Bool isZeroU1 ( IRExpr* e )
-{
-   return e->tag == Iex_Const
-          && e->Iex.Const.con->tag == Ico_U1
-          && e->Iex.Const.con->Ico.U1 == False;
-}
-
-
-/* Note, this destructively modifies the given IRBB. */
-
-/* Scan backwards through statements, carrying a set of IRTemps which
-   are known to be used after the current point.  On encountering 't =
-   E', delete the binding if it is not used.  Otherwise, add any temp
-   uses to the set and keep on moving backwards. */
+         case Iop_8Uto32:
+            e2 = IRExpr_Const(IRConst_U32(
+                    0xFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U8));
+            break;
+         case Iop_16Uto32:
+            e2 = IRExpr_Const(IRConst_U32(
+                    0xFFFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U16));
+            break;
+         case Iop_32to16:
+            e2 = IRExpr_Const(IRConst_U16(
+                    0xFFFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U32));
+            break;
+         case Iop_32to8:
+            e2 = IRExpr_Const(IRConst_U8(
+                    0xFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U32));
+            break;
+         case Iop_32to1:
+            e2 = IRExpr_Const(IRConst_U1(
+                    0==e->Iex.Unop.arg->Iex.Const.con->Ico.U32
+                       ? False : True));
+            break;
 
-/* notstatic */ void do_deadcode_BB ( IRBB* bb )
-{
-   Int     i;
-   Int     n_tmps = bb->tyenv->types_used;
-   Bool*   set = LibVEX_Alloc(n_tmps * sizeof(Bool));
-   IRStmt* st;
+         case Iop_Not32:
+            e2 = IRExpr_Const(IRConst_U32(
+                    ~ (e->Iex.Unop.arg->Iex.Const.con->Ico.U32)));
+            break;
+         case Iop_Not16:
+            e2 = IRExpr_Const(IRConst_U16(
+                    ~ (e->Iex.Unop.arg->Iex.Const.con->Ico.U16)));
+            break;
+         case Iop_Not8:
+            e2 = IRExpr_Const(IRConst_U8(
+                    ~ (e->Iex.Unop.arg->Iex.Const.con->Ico.U8)));
+            break;
 
-   for (i = 0; i < n_tmps; i++)
-      set[i] = False;
+         case Iop_Not1:
+            e2 = IRExpr_Const(IRConst_U1(
+                    notBool(e->Iex.Unop.arg->Iex.Const.con->Ico.U1)));
+            break;
 
-   /* start off by recording IRTemp uses in the next field. */
-   addUses_Expr(set, bb->next);
+         case Iop_64to32: {
+            ULong w64 = e->Iex.Unop.arg->Iex.Const.con->Ico.U64;
+            w64 &= 0x00000000FFFFFFFFULL;
+            e2 = IRExpr_Const(IRConst_U32( (UInt)w64 ));
+            break;
+         }
 
-   /* Work backwards through the stmts */
-   for (i = bb->stmts_used-1; i >= 0; i--) {
-      st = bb->stmts[i];
-      if (!st)
-         continue;
-      if (st->tag == Ist_Tmp
-          && set[(Int)(st->Ist.Tmp.tmp)] == False) {
-          /* it's an IRTemp which never got used.  Delete it. */
-         if (DEBUG_IROPT) {
-            vex_printf("DEAD: ");
-            ppIRStmt(st);
-            vex_printf("\n");
+         case Iop_64HIto32: {
+            ULong w64 = e->Iex.Unop.arg->Iex.Const.con->Ico.U64;
+            w64 >>= 32;
+            e2 = IRExpr_Const(IRConst_U32( (UInt)w64 ));
+            break;
          }
-         bb->stmts[i] = NULL;
-      }
-      else
-      if (st->tag == Ist_Dirty
-          && st->Ist.Dirty.details->guard
-          && isZeroU1(st->Ist.Dirty.details->guard)) {
-         /* This is a dirty helper which will never get called.  Delete it. */
-         bb->stmts[i] = NULL;
-       }
-       else {
-         /* Note any IRTemp uses made by the current statement. */
-         addUses_Stmt(set, st);
+
+         default: 
+            goto unhandled;
       }
    }
-}
-
-
-/*---------------------------------------------------------------*/
-/*--- In-place removal of redundant GETs                      ---*/
-/*---------------------------------------------------------------*/
-
-/* Scan forwards, building up an environment binding (min offset, max
-   offset) pairs to values, which will either be temps or constants.
 
-   On seeing 't = Get(minoff,maxoff)', look up (minoff,maxoff) in the
-   env and if it matches, replace the Get with the stored value.  If
-   there is no match, add a (minoff,maxoff) :-> t binding.
+   /* BINARY ops */
+   if (e->tag == Iex_Binop) {
+      if (e->Iex.Binop.arg1->tag == Iex_Const
+          && e->Iex.Binop.arg2->tag == Iex_Const) {
+         /* cases where both args are consts */
+         switch (e->Iex.Binop.op) {
 
-   On seeing 'Put (minoff,maxoff) = t or c', first remove in the env
-   any binding which fully or partially overlaps with (minoff,maxoff).
-   Then add a new (minoff,maxoff) :-> t or c binding.  */
+            /* --- Iop_Or --- */
+            case Iop_Or8:
+               e2 = IRExpr_Const(IRConst_U8(0xFF & 
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
+                        | e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
+               break;
+            case Iop_Or16:
+               e2 = IRExpr_Const(IRConst_U16(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U16
+                        | e->Iex.Binop.arg2->Iex.Const.con->Ico.U16)));
+               break;
+            case Iop_Or32:
+               e2 = IRExpr_Const(IRConst_U32(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
+                        | e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
+               break;
 
-/* Extract the min/max offsets from a guest state array descriptor. */
+            case Iop_Xor8:
+               e2 = IRExpr_Const(IRConst_U8(0xFF & 
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
+                        ^ e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
+               break;
+            case Iop_And8:
+               e2 = IRExpr_Const(IRConst_U8(0xFF & 
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
+                        & e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
+               break;
+            case Iop_Add8:
+               e2 = IRExpr_Const(IRConst_U8(0xFF & 
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
+                        + e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
+               break;
+            case Iop_Sub8:
+               e2 = IRExpr_Const(IRConst_U8(0xFF & 
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U8
+                        - e->Iex.Binop.arg2->Iex.Const.con->Ico.U8)));
+               break;
+            case Iop_Sub32:
+               e2 = IRExpr_Const(IRConst_U32(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
+                        - e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
+               break;
+            case Iop_Add32:
+               e2 = IRExpr_Const(IRConst_U32(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
+                        + e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
+               break;
+            case Iop_Xor32:
+               e2 = IRExpr_Const(IRConst_U32(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
+                        ^ e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
+               break;
+            case Iop_And32:
+               e2 = IRExpr_Const(IRConst_U32(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
+                        & e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
+               break;
 
-inline
-static void getArrayBounds ( IRArray* descr, UInt* minoff, UInt* maxoff )
-{
-   *minoff = descr->base;
-   *maxoff = *minoff + descr->nElems*sizeofIRType(descr->elemTy) - 1;
-   vassert((*minoff & 0xFFFF0000) == 0);
-   vassert((*maxoff & 0xFFFF0000) == 0);
-   vassert(*minoff <= *maxoff);
-}
+            case Iop_Mul32:
+               e2 = IRExpr_Const(IRConst_U32(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
+                        * e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
+               break;
+            case Iop_Shl32:
+               vassert(e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U8);
+               shift = (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U8);
+               if (shift >= 0 && shift <= 31)
+                  e2 = IRExpr_Const(IRConst_U32(
+                          (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
+                           << shift)));
+               break;
+            case Iop_Sar32: {
+               /* paranoid ... */
+               /*signed*/ Int s32;
+               vassert(e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U8);
+               s32   = (Int)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32);
+               shift = (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U8);
+               if (shift >= 0 && shift <= 31) {
+                  s32 >>=/*signed*/ shift;
+                  e2 = IRExpr_Const(IRConst_U32((UInt)s32));
+               }
+               break;
+            }
+            case Iop_Shr32: {
+               /* paranoid ... */
+               /*unsigned*/ UInt s32;
+               vassert(e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U8);
+               s32   = (Int)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32);
+               shift = (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U8);
+               if (shift >= 0 && shift <= 31) {
+                  s32 >>=/*unsigned*/ shift;
+                  e2 = IRExpr_Const(IRConst_U32((UInt)s32));
+               }
+               break;
+            }
+            case Iop_CmpEQ32:
+               e2 = IRExpr_Const(IRConst_U1(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
+                        == e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
+               break;
+            case Iop_CmpNE32:
+               e2 = IRExpr_Const(IRConst_U1(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U32
+                        != e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)));
+               break;
 
-/* Create keys, of the form ((minoffset << 16) | maxoffset). */
+            case Iop_CmpNE64:
+               e2 = IRExpr_Const(IRConst_U1(
+                       (e->Iex.Binop.arg1->Iex.Const.con->Ico.U64
+                        != e->Iex.Binop.arg2->Iex.Const.con->Ico.U64)));
+               break;
 
-static UInt mk_key_GetPut ( Int offset, IRType ty )
-{
-   /* offset should fit in 16 bits. */
-   UInt minoff = offset;
-   UInt maxoff = minoff + sizeofIRType(ty) - 1;
-   vassert((minoff & 0xFFFF0000) == 0);
-   vassert((maxoff & 0xFFFF0000) == 0);
-   return (minoff << 16) | maxoff;
-}
+            case Iop_CmpNE8:
+               e2 = IRExpr_Const(IRConst_U1(
+                       ((0xFF & e->Iex.Binop.arg1->Iex.Const.con->Ico.U8)
+                        != (0xFF & e->Iex.Binop.arg2->Iex.Const.con->Ico.U8))));
+               break;
 
-static UInt mk_key_GetIPutI ( IRArray* descr )
-{
-   UInt minoff, maxoff;
-   getArrayBounds( descr, &minoff, &maxoff );
-   vassert((minoff & 0xFFFF0000) == 0);
-   vassert((maxoff & 0xFFFF0000) == 0);
-   return (minoff << 16) | maxoff;
-}
+            case Iop_CmpLE32U:
+               e2 = IRExpr_Const(IRConst_U1(
+                       ((UInt)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)
+                        <= (UInt)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32))));
+               break;
+            case Iop_CmpLE32S:
+               e2 = IRExpr_Const(IRConst_U1(
+                       ((Int)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)
+                        <= (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32))));
+               break;
 
-/* Supposing h has keys of the form generated by mk_key_GetPut and
-   mk_key_GetIPutI, invalidate any key which overlaps (k_lo
-   .. k_hi). 
-*/
-static void invalidateOverlaps ( HashHW* h, UInt k_lo, UInt k_hi )
-{
-   Int  j;
-   UInt e_lo, e_hi;
-   vassert(k_lo <= k_hi);
-   /* invalidate any env entries which in any way overlap (k_lo
-      .. k_hi) */
-   /* vex_printf("invalidate %d .. %d\n", k_lo, k_hi ); */
+            case Iop_CmpLT32S:
+               e2 = IRExpr_Const(IRConst_U1(
+                       ((Int)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)
+                        < (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32))));
+               break;
+            case Iop_CmpLT32U:
+               e2 = IRExpr_Const(IRConst_U1(
+                       ((UInt)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)
+                        < (UInt)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32))));
+               break;
 
-   for (j = 0; j < h->used; j++) {
-      if (!h->inuse[j]) 
-         continue;
-      e_lo = (((UInt)h->key[j]) >> 16) & 0xFFFF;
-      e_hi = ((UInt)h->key[j]) & 0xFFFF;
-      vassert(e_lo <= e_hi);
-      if (e_hi < k_lo || k_hi < e_lo)
-         continue; /* no overlap possible */
-      else
-         /* overlap; invalidate */
-         h->inuse[j] = False;
-   }
-}
+            case Iop_32HLto64:
+               e2 = IRExpr_Const(IRConst_U64(
+                       (((ULong)(e->Iex.Binop.arg1->Iex.Const.con->Ico.U32)) << 32)
+                       | ((ULong)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32)) 
+                    ));
+               break;
+            default:
+               goto unhandled;
+         }
 
+      } else {
 
-static void redundant_get_removal_BB ( IRBB* bb )
-{
-   HashHW* env = newHHW();
-   UInt    key = 0; /* keep gcc -O happy */
-   Int     i, j;
-   HWord   val;
+         /* other cases (identities, etc) */
+         /* Shl32(x,0) ==> x */
+         if (e->Iex.Binop.op == Iop_Shl32
+             && e->Iex.Binop.arg2->tag == Iex_Const
+             && e->Iex.Binop.arg2->Iex.Const.con->Ico.U8 == 0) {
+            e2 = e->Iex.Binop.arg1;
+         } else
 
-   for (i = 0; i < bb->stmts_used; i++) {
-      IRStmt* st = bb->stmts[i];
+         /* Or32/Add32(x,0) ==> x */
+         if ((e->Iex.Binop.op == Iop_Add32 || e->Iex.Binop.op == Iop_Or32)
+             && e->Iex.Binop.arg2->tag == Iex_Const
+             && e->Iex.Binop.arg2->Iex.Const.con->Ico.U32 == 0) {
+            e2 = e->Iex.Binop.arg1;
+         } else
 
-      if (!st)
-         continue;
+         /* Or64/Add64(x,0) ==> x */
+         if ((e->Iex.Binop.op == Iop_Add64 || e->Iex.Binop.op == Iop_Or64)
+             && e->Iex.Binop.arg2->tag == Iex_Const
+             && e->Iex.Binop.arg2->Iex.Const.con->Ico.U64 == 0) {
+            e2 = e->Iex.Binop.arg1;
+         } else
 
-      /* Deal with Gets */
-      if (st->tag == Ist_Tmp
-          && st->Ist.Tmp.data->tag == Iex_Get) {
-         /* st is 't = Get(...)'.  Look up in the environment and see
-            if the Get can be replaced. */
-         IRExpr* get = st->Ist.Tmp.data;
-         key = (HWord)mk_key_GetPut( get->Iex.Get.offset, 
-                                     get->Iex.Get.ty );
-         if (lookupHHW(env, &val, (HWord)key)) {
-            /* found it */
-            /* Note, we could do better here.  If the types are
-               different we don't do the substitution, since doing so
-               could lead to invalidly-typed IR.  An improvement would
-               be to stick in a reinterpret-style cast, although that
-               would make maintaining flatness more difficult. */
-            IRExpr* valE    = (IRExpr*)val;
-            Bool    typesOK = typeOfIRExpr(bb->tyenv,valE) 
-                              == st->Ist.Tmp.data->Iex.Get.ty;
-            if (typesOK && DEBUG_IROPT) {
-               vex_printf("rGET: "); ppIRExpr(get);
-               vex_printf("  ->  "); ppIRExpr(valE);
-               vex_printf("\n");
-            }
-            if (typesOK)
-               bb->stmts[i] = IRStmt_Tmp(st->Ist.Tmp.tmp, valE);
-         } else {
-            /* Not found, but at least we know that t and the Get(...)
-               are now associated.  So add a binding to reflect that
-               fact. */
-            addToHHW( env, (HWord)key, 
-                           (HWord)(IRExpr_Tmp(st->Ist.Tmp.tmp)) );
-         }
-      }
+         /* And32(x,0xFFFFFFFF) ==> x */
+         if (e->Iex.Binop.op == Iop_And32
+             && e->Iex.Binop.arg2->tag == Iex_Const
+             && e->Iex.Binop.arg2->Iex.Const.con->Ico.U32 == 0xFFFFFFFF) {
+            e2 = e->Iex.Binop.arg1;
+         } else
 
-      /* Deal with Puts: invalidate any env entries overlapped by this
-         Put */
-      if (st->tag == Ist_Put || st->tag == Ist_PutI) {
-         UInt k_lo, k_hi;
-         if (st->tag == Ist_Put) {
-            key = mk_key_GetPut( st->Ist.Put.offset, 
-                                 typeOfIRExpr(bb->tyenv,st->Ist.Put.data) );
-         } else {
-            vassert(st->tag == Ist_PutI);
-            key = mk_key_GetIPutI( st->Ist.PutI.descr );
-         }
+         /* Or32(0,x) ==> x */
+         if (e->Iex.Binop.op == Iop_Or32
+             && e->Iex.Binop.arg1->tag == Iex_Const
+             && e->Iex.Binop.arg1->Iex.Const.con->Ico.U32 == 0) {
+            e2 = e->Iex.Binop.arg2;
+         } else
 
-         k_lo = (key >> 16) & 0xFFFF;
-         k_hi = key & 0xFFFF;
-         invalidateOverlaps(env, k_lo, k_hi);
-      }
-      else
-      if (st->tag == Ist_Dirty) {
-         /* Deal with dirty helpers which write or modify guest state.
-            Invalidate the entire env.  We could do a lot better
-            here. */
-         IRDirty* d      = st->Ist.Dirty.details;
-         Bool     writes = False;
-         for (j = 0; j < d->nFxState; j++) {
-            if (d->fxState[j].fx == Ifx_Modify 
-                || d->fxState[j].fx == Ifx_Write)
-            writes = True;
-         }
-         if (writes) {
-            /* dump the entire env (not clever, but correct ...) */
-            for (j = 0; j < env->used; j++)
-               env->inuse[j] = False;
-            if (0) vex_printf("rGET: trash env due to dirty helper\n");
+         /* Or8/16/32/64(t,t) ==> t, for some IRTemp t */
+         /* And8/16/32/64(t,t) ==> t, for some IRTemp t */
+         if (   (e->Iex.Binop.op == Iop_And64
+              || e->Iex.Binop.op == Iop_And32
+              || e->Iex.Binop.op == Iop_And16
+              || e->Iex.Binop.op == Iop_And8
+              || e->Iex.Binop.op == Iop_Or64
+              || e->Iex.Binop.op == Iop_Or32
+              || e->Iex.Binop.op == Iop_Or16
+              || e->Iex.Binop.op == Iop_Or8)
+             && sameIRTemps(e->Iex.Binop.arg1, e->Iex.Binop.arg2)) {
+            e2 = e->Iex.Binop.arg1;
          }
-      }
 
-      /* add this one to the env, if appropriate */
-      if (st->tag == Ist_Put) {
-         vassert(isAtom(st->Ist.Put.data));
-         addToHHW( env, (HWord)key, (HWord)(st->Ist.Put.data));
       }
+   }
 
-   } /* for (i = 0; i < bb->stmts_used; i++) */
-
-}
+   /* Mux0X */
+   if (e->tag == Iex_Mux0X
+       && e->Iex.Mux0X.cond->tag == Iex_Const) {
+      Bool zero;
+      /* assured us by the IR type rules */
+      vassert(e->Iex.Mux0X.cond->Iex.Const.con->tag == Ico_U8);
+      zero = 0 == (0xFF & e->Iex.Mux0X.cond->Iex.Const.con->Ico.U8);
+      e2 = zero ? e->Iex.Mux0X.expr0 : e->Iex.Mux0X.exprX;
+   }
 
+   if (DEBUG_IROPT && e2 != e) {
+      vex_printf("FOLD: "); 
+      ppIRExpr(e); vex_printf("  ->  ");
+      ppIRExpr(e2); vex_printf("\n");
+   }
 
-/*---------------------------------------------------------------*/
-/*--- In-place removal of redundant PUTs                      ---*/
-/*---------------------------------------------------------------*/
+   return e2;
 
-/* Find any Get uses in st and invalidate any partially or fully
-   overlapping ranges listed in env.  Due to the flattening phase, the
-   only stmt kind we expect to find a Get on is IRStmt_Tmp. */
+ unhandled:
+#  if 0
+   vex_printf("\n\n");
+   ppIRExpr(e);
+   vpanic("fold_Expr: no rule for the above");
+#  else
+   vex_printf("vex iropt: fold_Expr: no rule for: ");
+   ppIRExpr(e);
+   vex_printf("\n");
+   return e2;
+#  endif
+}
 
-static void handle_gets_Stmt ( 
-               HashHW* env, 
-               IRStmt* st,
-               Bool (*preciseMemExnsFn)(Int,Int)
-            )
-{
-   Int     j;
-   UInt    key = 0; /* keep gcc -O happy */
-   Bool    isGet;
-   Bool    memRW = False;
-   IRExpr* e;
 
-   switch (st->tag) {
+/* Apply the subst to a simple 1-level expression -- guaranteed to be
+   1-level due to previous flattening pass. */
 
-      /* This is the only interesting case.  Deal with Gets in the RHS
-         expression. */
-      case Ist_Tmp:
-         e = st->Ist.Tmp.data;
-         switch (e->tag) {
-            case Iex_Get:
-               isGet = True;
-               key = mk_key_GetPut ( e->Iex.Get.offset, e->Iex.Get.ty );
-               break;
-            case Iex_GetI:
-               isGet = True;
-               key = mk_key_GetIPutI ( e->Iex.GetI.descr );
-               break;
-            case Iex_LDle:
-               isGet = False;
-               memRW = True;
-               break;
-            default: 
-               isGet = False;
-         }
-         if (isGet) {
-            UInt k_lo, k_hi;
-            k_lo = (key >> 16) & 0xFFFF;
-            k_hi = key & 0xFFFF;
-            invalidateOverlaps(env, k_lo, k_hi);
+static IRExpr* subst_Expr ( IRExpr** env, IRExpr* ex )
+{
+   switch (ex->tag) {
+      case Iex_Tmp:
+         if (env[(Int)ex->Iex.Tmp.tmp] != NULL) {
+            return env[(Int)ex->Iex.Tmp.tmp];
+         } else {
+            /* not bound in env */
+            return ex;
          }
-         break;
-
-      /* Be very conservative for dirty helper calls; dump the entire
-         environment.  The helper might read guest state, in which
-         case it needs to be flushed first.  Also, the helper might
-         access guest memory, in which case all parts of the guest
-         state requiring precise exceptions needs to be flushed.  The
-         crude solution is just to flush everything; we could easily
-         enough do a lot better if needed. */
-      case Ist_Dirty:
-         for (j = 0; j < env->used; j++)
-            env->inuse[j] = False;
-         break;
-
-      /* all other cases are boring. */
-      case Ist_STle:
-         vassert(isAtom(st->Ist.STle.addr));
-         vassert(isAtom(st->Ist.STle.data));
-         memRW = True;
-         break;
 
-      case Ist_Exit:
-         vassert(isAtom(st->Ist.Exit.guard));
-         break;
+      case Iex_Const:
+      case Iex_Get:
+         return ex;
 
-      case Ist_PutI:
-         vassert(isAtom(st->Ist.PutI.ix));
-         vassert(isAtom(st->Ist.PutI.data));
-         break;
+      case Iex_GetI:
+         vassert(isAtom(ex->Iex.GetI.ix));
+         return IRExpr_GetI(
+            ex->Iex.GetI.descr,
+            subst_Expr(env, ex->Iex.GetI.ix),
+            ex->Iex.GetI.bias
+         );
 
-      default:
-         vex_printf("\n");
-         ppIRStmt(st);
-         vex_printf("\n");
-         vpanic("handle_gets_Stmt");
-   }
+      case Iex_Binop:
+         vassert(isAtom(ex->Iex.Binop.arg1));
+         vassert(isAtom(ex->Iex.Binop.arg2));
+         return IRExpr_Binop(
+                   ex->Iex.Binop.op,
+                   subst_Expr(env, ex->Iex.Binop.arg1),
+                   subst_Expr(env, ex->Iex.Binop.arg2)
+                );
 
-   if (memRW) {
-      /* This statement accesses memory.  So we need to dump all parts
-         of the environment corresponding to guest state that may not
-         be reordered with respect to memory references.  That means
-         at least the stack pointer. */
-      for (j = 0; j < env->used; j++) {
-         if (!env->inuse[j])
-            continue;
-         if (vex_control.iropt_precise_memory_exns) {
-            /* Precise exceptions required.  Flush all guest state. */
-            env->inuse[j] = False;
-         } else {
-            /* Just flush the minimal amount required, as computed by
-               preciseMemExnsFn. */
-            HWord k_lo = (env->key[j] >> 16) & 0xFFFF;
-            HWord k_hi = env->key[j] & 0xFFFF;
-            if (preciseMemExnsFn( k_lo, k_hi ))
-               env->inuse[j] = False;
+      case Iex_Unop:
+         vassert(isAtom(ex->Iex.Unop.arg));
+         return IRExpr_Unop(
+                   ex->Iex.Unop.op,
+                   subst_Expr(env, ex->Iex.Unop.arg)
+                );
+
+      case Iex_LDle:
+         vassert(isAtom(ex->Iex.LDle.addr));
+         return IRExpr_LDle(
+                   ex->Iex.LDle.ty,
+                   subst_Expr(env, ex->Iex.LDle.addr)
+                );
+
+      case Iex_CCall: {
+         Int      i;
+         IRExpr** args2 = sopyIRExprVec(ex->Iex.CCall.args);
+         for (i = 0; args2[i]; i++) {
+            vassert(isAtom(args2[i]));
+            args2[i] = subst_Expr(env, args2[i]);
          }
+         return IRExpr_CCall(
+                   ex->Iex.CCall.cee,
+                   ex->Iex.CCall.retty,
+                   args2 
+                );
       }
-   } /* if (memRW) */
 
+      case Iex_Mux0X:
+         vassert(isAtom(ex->Iex.Mux0X.cond));
+         vassert(isAtom(ex->Iex.Mux0X.expr0));
+         vassert(isAtom(ex->Iex.Mux0X.exprX));
+         return IRExpr_Mux0X(
+                   subst_Expr(env, ex->Iex.Mux0X.cond),
+                   subst_Expr(env, ex->Iex.Mux0X.expr0),
+                   subst_Expr(env, ex->Iex.Mux0X.exprX)
+                );
+
+      default:
+         vex_printf("\n\n"); ppIRExpr(ex);
+         vpanic("subst_Expr");
+      
+   }
 }
 
 
-/* Scan backwards, building up a set of (min offset, max
-   offset) pairs, indicating those parts of the guest state
-   for which the next event is a write.
+/* Apply the subst to stmt, then fold the result as much as possible.
+   Much simplified due to stmt being previously flattened.  Returning
+   NULL means the statement has been turned into a no-op. */
 
-   On seeing a conditional exit, empty the set.
+static IRStmt* subst_and_fold_Stmt ( IRExpr** env, IRStmt* st )
+{
+#  if 0
+   vex_printf("\nsubst and fold stmt\n");
+   ppIRStmt(st);
+   vex_printf("\n");
+#  endif
 
-   On seeing 'Put (minoff,maxoff) = t or c', if (minoff,maxoff) is
-   completely within the set, remove the Put.  Otherwise, add
-   (minoff,maxoff) to the set.
+   switch (st->tag) {
+      case Ist_Put:
+         vassert(isAtom(st->Ist.Put.data));
+         return IRStmt_Put(
+                   st->Ist.Put.offset, 
+                   fold_Expr(subst_Expr(env, st->Ist.Put.data)) 
+                );
 
-   On seeing 'Get (minoff,maxoff)', remove any part of the set
-   overlapping (minoff,maxoff).
-*/
+      case Ist_PutI:
+         vassert(isAtom(st->Ist.PutI.ix));
+         vassert(isAtom(st->Ist.PutI.data));
+         return IRStmt_PutI(
+                   st->Ist.PutI.descr,
+                   fold_Expr(subst_Expr(env, st->Ist.PutI.ix)),
+                   st->Ist.PutI.bias,
+                   fold_Expr(subst_Expr(env, st->Ist.PutI.data))
+                );
 
-static void redundant_put_removal_BB ( 
-               IRBB* bb,
-               Bool (*preciseMemExnsFn)(Int,Int)
-            )
-{
-   Int     i, j;
-   Bool    isPut;
-   IRStmt* st;
-   UInt    key = 0; /* keep gcc -O happy */
+      case Ist_Tmp:
+         /* This is the one place where an expr (st->Ist.Tmp.data) is
+            allowed to be more than just a constant or a tmp. */
+         return IRStmt_Tmp(
+                   st->Ist.Tmp.tmp,
+                   fold_Expr(subst_Expr(env, st->Ist.Tmp.data))
+                );
 
-   HashHW* env = newHHW();
-   for (i = bb->stmts_used-1; i >= 0; i--) {
-      st = bb->stmts[i];
-      if (!st)
-         continue;
+      case Ist_STle:
+         vassert(isAtom(st->Ist.STle.addr));
+         vassert(isAtom(st->Ist.STle.data));
+         return IRStmt_STle(
+                   fold_Expr(subst_Expr(env, st->Ist.STle.addr)),
+                   fold_Expr(subst_Expr(env, st->Ist.STle.data))
+                );
 
-      /* Deal with conditional exits. */
-      if (st->tag == Ist_Exit) {
-         /* Since control may not get beyond this point, we must empty
-            out the set, since we can no longer claim that the next
-            event for any part of the guest state is definitely a
-            write. */
-         vassert(isAtom(st->Ist.Exit.guard));
-         for (j = 0; j < env->used; j++)
-            env->inuse[j] = False;
-         continue;
+      case Ist_Dirty: {
+         Int     i;
+         IRDirty *d, *d2;
+         d = st->Ist.Dirty.details;
+         d2 = emptyIRDirty();
+         *d2 = *d;
+         d2->args = sopyIRExprVec(d2->args);
+         if (d2->mFx != Ifx_None) {
+            vassert(isAtom(d2->mAddr));
+            d2->mAddr = fold_Expr(subst_Expr(env, d2->mAddr));
+         }
+         vassert(isAtom(d2->guard));
+         d2->guard = fold_Expr(subst_Expr(env, d2->guard));
+         for (i = 0; d2->args[i]; i++) {
+            vassert(isAtom(d2->args[i]));
+            d2->args[i] = fold_Expr(subst_Expr(env, d2->args[i]));
+         }
+         return IRStmt_Dirty(d2);
       }
 
-      /* Deal with Puts */
-      switch (st->tag) {
-         case Ist_Put: 
-            isPut = True;
-            key = mk_key_GetPut( st->Ist.Put.offset, 
-                                 typeOfIRExpr(bb->tyenv,st->Ist.Put.data) );
-            vassert(isAtom(st->Ist.Put.data));
-            break;
-         case Ist_PutI:
-            isPut = True;
-            key = mk_key_GetIPutI( st->Ist.PutI.descr );
-            vassert(isAtom(st->Ist.PutI.ix));
-            vassert(isAtom(st->Ist.PutI.data));
-            break;
-         default: 
-            isPut = False;
-      }
-      if (isPut && st->tag != Ist_PutI) {
-         /* See if any single entry in env overlaps this Put.  This is
-            simplistic in that the transformation is valid if, say, two
-            or more entries in the env overlap this Put, but the use of
-            lookupHHW will only find a single entry which exactly
-            overlaps this Put.  This is suboptimal but safe. */
-         if (lookupHHW(env, NULL, (HWord)key)) {
-            /* This Put is redundant because a later one will overwrite
-               it.  So NULL (nop) it out. */
-            if (DEBUG_IROPT) {
-               vex_printf("rPUT: "); ppIRStmt(st);
-               vex_printf("\n");
+      case Ist_Exit: {
+         IRExpr* fcond;
+         vassert(isAtom(st->Ist.Exit.guard));
+         fcond = fold_Expr(subst_Expr(env, st->Ist.Exit.guard));
+         if (fcond->tag == Iex_Const) {
+            /* Interesting.  The condition on this exit has folded down to
+               a constant. */
+            vassert(fcond->Iex.Const.con->tag == Ico_U1);
+            if (fcond->Iex.Const.con->Ico.U1 == False) {
+               /* exit is never going to happen, so dump the statement. */
+               return NULL;
+            } else {
+               vassert(fcond->Iex.Const.con->Ico.U1 == True);
+               /* Hmmm.  The exit has become unconditional.  Leave it as
+                  it is for now, since we'd have to truncate the BB at
+                  this point, which is tricky. */
+               /* fall out into the reconstruct-the-exit code. */
+               if (vex_control.iropt_verbosity > 0) 
+                  /* really a misuse of vex_control.iropt_verbosity */
+                  vex_printf("vex iropt: IRStmt_Exit became unconditional\n");
             }
-            bb->stmts[i] = NULL;
-         } else {
-            /* We can't demonstrate that this Put is redundant, so add it
-               to the running collection. */
-            addToHHW(env, (HWord)key, 0);
          }
-         continue;
+         return IRStmt_Exit(fcond, st->Ist.Exit.jk, st->Ist.Exit.dst);
       }
 
-      /* Deal with Gets.  These remove bits of the environment since
-         appearance of a Get means that the next event for that slice
-         of the guest state is no longer a write, but a read. */
-      handle_gets_Stmt( env, st, preciseMemExnsFn );
-   }
-}
+   default:
+      vex_printf("\n"); ppIRStmt(st);
+      vpanic("subst_and_fold_Stmt");
+   }
+}
+
+
+IRBB* cprop_BB ( IRBB* in )
+{
+   Int      i;
+   IRBB*    out;
+   IRStmt*  st2;
+   Int      n_tmps = in->tyenv->types_used;
+   IRExpr** env = LibVEX_Alloc(n_tmps * sizeof(IRExpr*));
+
+   out = emptyIRBB();
+   out->tyenv = dopyIRTypeEnv( in->tyenv );
+
+   /* Set up the env with which travels forward.  This holds a
+      substitution, mapping IRTemps to atoms, that is, IRExprs which
+      are either IRTemps or IRConsts.  Thus, copy and constant
+      propagation is done.  The environment is to be applied as we
+      move along.  Keys are IRTemps.  Values are IRExpr*s.
+   */
+   for (i = 0; i < n_tmps; i++)
+      env[i] = NULL;
 
+   /* For each original SSA-form stmt ... */
+   for (i = 0; i < in->stmts_used; i++) {
 
-/*---------------------------------------------------------------*/
-/*--- Specialisation of helper function calls, in             ---*/
-/*--- collaboration with the front end                        ---*/
-/*---------------------------------------------------------------*/
+      /* First apply the substitution to the current stmt.  This
+         propagates in any constants and tmp-tmp assignments
+         accumulated prior to this point.  As part of the subst_Stmt
+         call, also then fold any constant expressions resulting. */
 
-static 
-void spec_helpers_BB ( IRBB* bb,
-                       IRExpr* (*specHelper) ( Char*, IRExpr**) )   
-{
-   Int i;
-   IRStmt* st;
-   IRExpr* ex;
+      st2 = in->stmts[i];
 
-   for (i = bb->stmts_used-1; i >= 0; i--) {
-      st = bb->stmts[i];
+      /* perhaps st2 is already a no-op? */
+      if (!st2) continue;
 
-      if (!st 
-          || st->tag != Ist_Tmp
-          || st->Ist.Tmp.data->tag != Iex_CCall)
-        continue;
+      st2 = subst_and_fold_Stmt( env, st2 );
 
-      ex = (*specHelper)( st->Ist.Tmp.data->Iex.CCall.cee->name,
-                          st->Ist.Tmp.data->Iex.CCall.args );
-      if (!ex)
-        /* the front end can't think of a suitable replacement */
-        continue;
+      /* If the statement has been folded into a no-op, forget it. */
+      if (!st2) continue;
 
-      /* We got something better.  Install it in the bb. */
-      bb->stmts[i]
-         = IRStmt_Tmp(st->Ist.Tmp.tmp, ex);
+      /* Now consider what the stmt looks like.  If it's of the form
+         't = const' or 't1 = t2', add it to the running environment
+         and not to the output BB.  Otherwise, add it to the output
+         BB.  Note, we choose not to propagate const when const is an
+         F64i, so that F64i literals can be CSE'd later.  This helps
+         x86 floating point code generation. */
 
-      if (0) {
-         vex_printf("SPEC: ");
-         ppIRExpr(st->Ist.Tmp.data);
-         vex_printf("  -->  ");
-         ppIRExpr(ex);
-         vex_printf("\n");
+      if (st2->tag == Ist_Tmp 
+          && st2->Ist.Tmp.data->tag == Iex_Const
+          && st2->Ist.Tmp.data->Iex.Const.con->tag != Ico_F64i) {
+         /* 't = const' -- add to env.  
+             The pair (IRTemp, IRExpr*) is added. */
+         env[(Int)(st2->Ist.Tmp.tmp)] = st2->Ist.Tmp.data;
+      }
+      else
+      if (st2->tag == Ist_Tmp && st2->Ist.Tmp.data->tag == Iex_Tmp) {
+         /* 't1 = t2' -- add to env.  
+             The pair (IRTemp, IRExpr*) is added. */
+         env[(Int)(st2->Ist.Tmp.tmp)] = st2->Ist.Tmp.data;
+      }
+      else {
+         /* Not interesting, copy st2 into the output block. */
+         addStmtToIRBB( out, st2 );
       }
    }
+
+   out->next     = subst_Expr( env, in->next );
+   out->jumpkind = in->jumpkind;
+   return out;
 }
 
 
+
 /*---------------------------------------------------------------*/
-/*--- The tree builder                                        ---*/
+/*--- Dead code (t = E) removal                               ---*/
 /*---------------------------------------------------------------*/
 
-typedef
-   struct { 
-      Int     occ;          /* occurrence count for this tmp */
-      IRExpr* expr;         /* expr it is bound to, 
-                               or NULL if already 'used' */
-      Bool    eDoesLoad;    /* True <=> expr reads mem */
-      Bool    eDoesGet;     /* True <=> expr reads guest state */
-      Bool    invalidateMe; /* used when dumping bindings */
-      Int     origPos;      /* posn of the binder in the original bb */
-   }
-   TmpInfo;
-
-/* Given env :: IRTemp -> TmpInfo*
-   Add the use-occurrences of temps in this expression 
-   to the environment. 
+/* The type of the HashHW map is: a map from IRTemp to nothing
+   -- really just operating a set or IRTemps.
 */
-static void occCount_Temp ( TmpInfo** env, IRTemp tmp )
+
+inline
+static void addUses_Temp ( Bool* set, IRTemp tmp )
 {
-   TmpInfo* ti = env[(Int)tmp];
-   if (ti) {
-      ti->occ++;
-   } else {
-      ti               = LibVEX_Alloc(sizeof(TmpInfo));
-      ti->occ          = 1;
-      ti->expr         = NULL;
-      ti->eDoesLoad    = False;
-      ti->eDoesGet     = False;
-      ti->invalidateMe = False;
-      ti->origPos      = -1; /* filed in properly later */
-      env[(Int)tmp] = ti;
-   }
+   set[(Int)tmp] = True;
 }
 
-static void occCount_Expr ( TmpInfo** env, IRExpr* e )
+static void addUses_Expr ( Bool* set, IRExpr* e )
 {
    Int i;
-
    switch (e->tag) {
-
-      case Iex_Tmp: /* the only interesting case */
-         occCount_Temp(env, e->Iex.Tmp.tmp);
+      case Iex_GetI:
+         addUses_Expr(set, e->Iex.GetI.ix);
          return;
-
       case Iex_Mux0X:
-         occCount_Expr(env, e->Iex.Mux0X.cond);
-         occCount_Expr(env, e->Iex.Mux0X.expr0);
-         occCount_Expr(env, e->Iex.Mux0X.exprX);
+         addUses_Expr(set, e->Iex.Mux0X.cond);
+         addUses_Expr(set, e->Iex.Mux0X.expr0);
+         addUses_Expr(set, e->Iex.Mux0X.exprX);
+         return;
+      case Iex_CCall:
+         for (i = 0; e->Iex.CCall.args[i]; i++)
+            addUses_Expr(set, e->Iex.CCall.args[i]);
+         return;
+      case Iex_LDle:
+         addUses_Expr(set, e->Iex.LDle.addr);
+         return;
+      case Iex_Binop:
+         addUses_Expr(set, e->Iex.Binop.arg1);
+         addUses_Expr(set, e->Iex.Binop.arg2);
+         return;
+      case Iex_Unop:
+         addUses_Expr(set, e->Iex.Unop.arg);
+         return;
+      case Iex_Tmp:
+         addUses_Temp(set, e->Iex.Tmp.tmp);
+         return;
+      case Iex_Const:
+      case Iex_Get:
+         return;
+      default:
+         vex_printf("\n");
+         ppIRExpr(e);
+         vpanic("addUses_Expr");
+   }
+}
+
+static void addUses_Stmt ( Bool* set, IRStmt* st )
+{
+   Int      i;
+   IRDirty* d;
+   switch (st->tag) {
+      case Ist_PutI:
+         addUses_Expr(set, st->Ist.PutI.ix);
+         addUses_Expr(set, st->Ist.PutI.data);
+         return;
+      case Ist_Tmp:
+         addUses_Expr(set, st->Ist.Tmp.data);
+         return;
+      case Ist_Put:
+         addUses_Expr(set, st->Ist.Put.data);
+         return;
+      case Ist_STle:
+         addUses_Expr(set, st->Ist.STle.addr);
+         addUses_Expr(set, st->Ist.STle.data);
+         return;
+      case Ist_Dirty:
+         d = st->Ist.Dirty.details;
+         if (d->mFx != Ifx_None)
+            addUses_Expr(set, d->mAddr);
+         addUses_Expr(set, d->guard);
+         for (i = 0; d->args[i] != NULL; i++)
+            addUses_Expr(set, d->args[i]);
+         return;
+      case Ist_Exit:
+         addUses_Expr(set, st->Ist.Exit.guard);
          return;
+      default:
+         vex_printf("\n");
+         ppIRStmt(st);
+         vpanic("addUses_Stmt");
+   }
+}
+
+
+/* Is this literally IRExpr_Const(IRConst_U1(False)) ? */
+static Bool isZeroU1 ( IRExpr* e )
+{
+   return e->tag == Iex_Const
+          && e->Iex.Const.con->tag == Ico_U1
+          && e->Iex.Const.con->Ico.U1 == False;
+}
+
+
+/* Note, this destructively modifies the given IRBB. */
+
+/* Scan backwards through statements, carrying a set of IRTemps which
+   are known to be used after the current point.  On encountering 't =
+   E', delete the binding if it is not used.  Otherwise, add any temp
+   uses to the set and keep on moving backwards. */
+
+/* notstatic */ void do_deadcode_BB ( IRBB* bb )
+{
+   Int     i;
+   Int     n_tmps = bb->tyenv->types_used;
+   Bool*   set = LibVEX_Alloc(n_tmps * sizeof(Bool));
+   IRStmt* st;
+
+   for (i = 0; i < n_tmps; i++)
+      set[i] = False;
+
+   /* start off by recording IRTemp uses in the next field. */
+   addUses_Expr(set, bb->next);
+
+   /* Work backwards through the stmts */
+   for (i = bb->stmts_used-1; i >= 0; i--) {
+      st = bb->stmts[i];
+      if (!st)
+         continue;
+      if (st->tag == Ist_Tmp
+          && set[(Int)(st->Ist.Tmp.tmp)] == False) {
+          /* it's an IRTemp which never got used.  Delete it. */
+         if (DEBUG_IROPT) {
+            vex_printf("DEAD: ");
+            ppIRStmt(st);
+            vex_printf("\n");
+         }
+         bb->stmts[i] = NULL;
+      }
+      else
+      if (st->tag == Ist_Dirty
+          && st->Ist.Dirty.details->guard
+          && isZeroU1(st->Ist.Dirty.details->guard)) {
+         /* This is a dirty helper which will never get called.  Delete it. */
+         bb->stmts[i] = NULL;
+       }
+       else {
+         /* Note any IRTemp uses made by the current statement. */
+         addUses_Stmt(set, st);
+      }
+   }
+}
 
-      case Iex_Binop: 
-         occCount_Expr(env, e->Iex.Binop.arg1);
-         occCount_Expr(env, e->Iex.Binop.arg2);
-         return;
+/*---------------------------------------------------------------*/
+/*--- Specialisation of helper function calls, in             ---*/
+/*--- collaboration with the front end                        ---*/
+/*---------------------------------------------------------------*/
 
-      case Iex_Unop: 
-         occCount_Expr(env, e->Iex.Unop.arg);
-         return;
+static 
+void spec_helpers_BB ( IRBB* bb,
+                       IRExpr* (*specHelper) ( Char*, IRExpr**) )   
+{
+   Int i;
+   IRStmt* st;
+   IRExpr* ex;
 
-      case Iex_LDle: 
-         occCount_Expr(env, e->Iex.LDle.addr);
-         return;
+   for (i = bb->stmts_used-1; i >= 0; i--) {
+      st = bb->stmts[i];
 
-      case Iex_CCall:
-         for (i = 0; e->Iex.CCall.args[i]; i++)
-            occCount_Expr(env, e->Iex.CCall.args[i]);
-         return;
+      if (!st 
+          || st->tag != Ist_Tmp
+          || st->Ist.Tmp.data->tag != Iex_CCall)
+        continue;
 
-      case Iex_GetI:
-         occCount_Expr(env, e->Iex.GetI.ix);
-         return;
+      ex = (*specHelper)( st->Ist.Tmp.data->Iex.CCall.cee->name,
+                          st->Ist.Tmp.data->Iex.CCall.args );
+      if (!ex)
+        /* the front end can't think of a suitable replacement */
+        continue;
 
-      case Iex_Const:
-      case Iex_Get:
-         return;
+      /* We got something better.  Install it in the bb. */
+      bb->stmts[i]
+         = IRStmt_Tmp(st->Ist.Tmp.tmp, ex);
 
-      default: 
-         vex_printf("\n"); ppIRExpr(e); vex_printf("\n");
-         vpanic("occCount_Expr");
-    }
+      if (0) {
+         vex_printf("SPEC: ");
+         ppIRExpr(st->Ist.Tmp.data);
+         vex_printf("  -->  ");
+         ppIRExpr(ex);
+         vex_printf("\n");
+      }
+   }
 }
 
 
-/* Given env :: IRTemp -> TmpInfo*
-   Add the use-occurrences of temps in this expression 
-   to the environment. 
+/*---------------------------------------------------------------*/
+/*--- Common Subexpression Elimination                        ---*/
+/*---------------------------------------------------------------*/
+
+/* Expensive in time and space. */
+
+/* Uses two environments: 
+   a IRTemp -> IRTemp mapping 
+   a mapping from AvailExpr* to IRTemp 
 */
-static void occCount_Stmt ( TmpInfo** env, IRStmt* st )
-{
-   Int      i;
-   IRDirty* d;
-   switch (st->tag) {
-      case Ist_Tmp: 
-         occCount_Expr(env, st->Ist.Tmp.data); 
-         return; 
-      case Ist_Put: 
-         occCount_Expr(env, st->Ist.Put.data);
-         return;
-      case Ist_PutI:
-         occCount_Expr(env, st->Ist.PutI.ix);
-         occCount_Expr(env, st->Ist.PutI.data);
-         return;
-      case Ist_STle: 
-         occCount_Expr(env, st->Ist.STle.addr);
-         occCount_Expr(env, st->Ist.STle.data);
-         return;
-      case Ist_Dirty:
-         d = st->Ist.Dirty.details;
-         if (d->mFx != Ifx_None)
-            occCount_Expr(env, d->mAddr);
-         occCount_Expr(env, d->guard);
-         for (i = 0; d->args[i]; i++)
-            occCount_Expr(env, d->args[i]);
-         return;
-      case Ist_Exit:
-         occCount_Expr(env, st->Ist.Exit.guard);
-         return;
-      default: 
-         vex_printf("\n"); ppIRStmt(st); vex_printf("\n");
-         vpanic("occCount_Stmt");
-   }
-}
 
-/* Look up a binding for tmp in the env.  If found, return the bound
-   expression, and set the env's binding to NULL so it is marked as
-   used.  If not found, return NULL. */
+typedef
+   struct {
+      enum { Ut, Btt, Btc, Bct, Cf64i } tag;
+      union {
+         /* unop(tmp) */
+         struct {
+            IROp   op;
+            IRTemp arg;
+         } Ut;
+         /* binop(tmp,tmp) */
+         struct {
+            IROp   op;
+            IRTemp arg1;
+            IRTemp arg2;
+         } Btt;
+         /* binop(tmp,const) */
+         struct {
+            IROp    op;
+            IRTemp  arg1;
+            IRConst con2;
+         } Btc;
+         /* binop(const,tmp) */
+         struct {
+            IROp    op;
+            IRConst con1;
+            IRTemp  arg2;
+         } Bct;
+         /* F64i-style const */
+         struct {
+            ULong f64i;
+         } Cf64i;
+      } u;
+   }
+   AvailExpr;
 
-static IRExpr* tbSubst_Temp ( TmpInfo** env, IRTemp tmp )
+static Bool eq_AvailExpr ( AvailExpr* a1, AvailExpr* a2 )
 {
-   TmpInfo* ti;
-   IRExpr*  e;
-   ti = env[(Int)tmp];
-   if (ti){
-      e = ti->expr;
-      if (e) {
-         ti->expr = NULL;
-         return e;
-      } else {
-         return NULL;
-      }
-   } else {
-      return NULL;
+   if (a1->tag != a2->tag)
+      return False;
+   switch (a1->tag) {
+      case Ut: 
+         return a1->u.Ut.op == a2->u.Ut.op 
+                && a1->u.Ut.arg == a2->u.Ut.arg;
+      case Btt: 
+         return a1->u.Btt.op == a2->u.Btt.op
+                && a1->u.Btt.arg1 == a2->u.Btt.arg1
+                && a1->u.Btt.arg2 == a2->u.Btt.arg2;
+      case Btc: 
+         return a1->u.Btc.op == a2->u.Btc.op
+                && a1->u.Btc.arg1 == a2->u.Btc.arg1
+                && eqIRConst(&a1->u.Btc.con2, &a2->u.Btc.con2);
+      case Bct: 
+         return a1->u.Bct.op == a2->u.Bct.op
+                && a1->u.Bct.arg2 == a2->u.Bct.arg2
+                && eqIRConst(&a1->u.Bct.con1, &a2->u.Bct.con1);
+      case Cf64i: 
+         return a1->u.Cf64i.f64i == a2->u.Cf64i.f64i;
+      default: vpanic("eq_AvailExpr");
    }
 }
 
-/* Traverse e, looking for temps.  For each observed temp, see if env
-   contains a binding for the temp, and if so return the bound value.
-   The env has the property that any binding it holds is
-   'single-shot', so once a binding is used, it is marked as no longer
-   available, by setting its .expr field to NULL. */
-
-static IRExpr* tbSubst_Expr ( TmpInfo** env, IRExpr* e )
+static IRExpr* availExpr_to_IRExpr ( AvailExpr* ae ) 
 {
-   IRExpr*  e2;
-   IRExpr** args2;
-   Int      i;
-
-   switch (e->tag) {
-
-      case Iex_CCall:
-         args2 = sopyIRExprVec(e->Iex.CCall.args);
-         for (i = 0; args2[i]; i++)
-            args2[i] = tbSubst_Expr(env,args2[i]);
-         return IRExpr_CCall(e->Iex.CCall.cee,
-                   e->Iex.CCall.retty,
-                   args2
-                );
-      case Iex_Tmp:
-         e2 = tbSubst_Temp(env, e->Iex.Tmp.tmp);
-         return e2 ? e2 : e;
-      case Iex_Mux0X:
-         return IRExpr_Mux0X(
-                   tbSubst_Expr(env, e->Iex.Mux0X.cond),
-                   tbSubst_Expr(env, e->Iex.Mux0X.expr0),
-                   tbSubst_Expr(env, e->Iex.Mux0X.exprX)
-                );
-      case Iex_Binop:
-         return IRExpr_Binop(
-                   e->Iex.Binop.op,
-                   tbSubst_Expr(env, e->Iex.Binop.arg1),
-                   tbSubst_Expr(env, e->Iex.Binop.arg2)
-                );
-      case Iex_Unop:
-         return IRExpr_Unop(
-                   e->Iex.Unop.op,
-                   tbSubst_Expr(env, e->Iex.Unop.arg)
-                );
-      case Iex_LDle:
-         return IRExpr_LDle(
-                   e->Iex.LDle.ty,
-                  tbSubst_Expr(env, e->Iex.LDle.addr)
-                );
-      case Iex_GetI:
-         return IRExpr_GetI(
-                   e->Iex.GetI.descr,
-                   tbSubst_Expr(env, e->Iex.GetI.ix),
-                   e->Iex.GetI.bias
-                );
-      case Iex_Const:
-      case Iex_Get:
-         return e;
-      default: 
-         vex_printf("\n"); ppIRExpr(e); vex_printf("\n");
-         vpanic("tbSubst_Expr");
+   IRConst* con;
+   switch (ae->tag) {
+      case Ut:
+         return IRExpr_Unop( ae->u.Ut.op, IRExpr_Tmp(ae->u.Ut.arg) );
+      case Btt:
+         return IRExpr_Binop( ae->u.Btt.op,
+                              IRExpr_Tmp(ae->u.Btt.arg1),
+                              IRExpr_Tmp(ae->u.Btt.arg2) );
+      case Btc:
+         con = LibVEX_Alloc(sizeof(IRConst));
+         *con = ae->u.Btc.con2;
+         return IRExpr_Binop( ae->u.Btc.op,
+                              IRExpr_Tmp(ae->u.Btc.arg1), IRExpr_Const(con) );
+      case Bct:
+         con = LibVEX_Alloc(sizeof(IRConst));
+         *con = ae->u.Bct.con1;
+         return IRExpr_Binop( ae->u.Bct.op,
+                              IRExpr_Const(con), IRExpr_Tmp(ae->u.Bct.arg2) );
+      case Cf64i:
+         return IRExpr_Const(IRConst_F64i(ae->u.Cf64i.f64i));
+      default:
+         vpanic("availExpr_to_IRExpr");
    }
 }
 
-/* Same deal as tbSubst_Expr, except for stmts. */
-
-static IRStmt* tbSubst_Stmt ( TmpInfo** env, IRStmt* st )
+inline
+static IRTemp subst_AvailExpr_Temp ( HashHW* env, IRTemp tmp )
 {
-   Int      i;
-   IRDirty* d;
-   IRDirty* d2;
-   switch (st->tag) {
-      case Ist_STle:
-         return IRStmt_STle(
-                   tbSubst_Expr(env, st->Ist.STle.addr),
-                   tbSubst_Expr(env, st->Ist.STle.data)
-                );
-      case Ist_Tmp:
-         return IRStmt_Tmp(
-                   st->Ist.Tmp.tmp,
-                   tbSubst_Expr(env, st->Ist.Tmp.data)
-                );
-      case Ist_Put:
-         return IRStmt_Put(
-                   st->Ist.Put.offset,
-                   tbSubst_Expr(env, st->Ist.Put.data)
-                );
-      case Ist_PutI:
-         return IRStmt_PutI(
-                   st->Ist.PutI.descr,
-                   tbSubst_Expr(env, st->Ist.PutI.ix),
-                   st->Ist.PutI.bias,
-                   tbSubst_Expr(env, st->Ist.PutI.data)
-                );
+   HWord res;
+   /* env :: IRTemp -> IRTemp */
+   if (lookupHHW( env, &res, (HWord)tmp ))
+      return (IRTemp)res;
+   else
+      return tmp;
+}
 
-      case Ist_Exit:
-         return IRStmt_Exit(
-                   tbSubst_Expr(env, st->Ist.Exit.guard),
-                   st->Ist.Exit.jk,
-                   st->Ist.Exit.dst
-                );
-      case Ist_Dirty:
-         d = st->Ist.Dirty.details;
-         d2 = emptyIRDirty();
-         *d2 = *d;
-         if (d2->mFx != Ifx_None)
-            d2->mAddr = tbSubst_Expr(env, d2->mAddr);
-         d2->guard = tbSubst_Expr(env, d2->guard);
-         for (i = 0; d2->args[i]; i++)
-            d2->args[i] = tbSubst_Expr(env, d2->args[i]);
-         return IRStmt_Dirty(d2);
+static void subst_AvailExpr ( HashHW* env, AvailExpr* ae )
+{
+   /* env :: IRTemp -> IRTemp */
+   switch (ae->tag) {
+      case Ut:
+         ae->u.Ut.arg = subst_AvailExpr_Temp( env, ae->u.Ut.arg );
+         break;
+      case Btt:
+         ae->u.Btt.arg1 = subst_AvailExpr_Temp( env, ae->u.Btt.arg1 );
+         ae->u.Btt.arg2 = subst_AvailExpr_Temp( env, ae->u.Btt.arg2 );
+         break;
+      case Btc:
+         ae->u.Btc.arg1 = subst_AvailExpr_Temp( env, ae->u.Btc.arg1 );
+         break;
+      case Bct:
+         ae->u.Bct.arg2 = subst_AvailExpr_Temp( env, ae->u.Bct.arg2 );
+         break;
+      case Cf64i:
+         break;
       default: 
-         vex_printf("\n"); ppIRStmt(st); vex_printf("\n");
-         vpanic("tbSubst_Stmt");
+         vpanic("subst_AvailExpr");
    }
 }
 
-
-/* Traverse an expr, and detect if any part of it reads memory or does
-   a Get.  Be careful ... this really controls how much the
-   tree-builder can reorder the code, so getting it right is critical.
-*/
-static void setHints_Expr (Bool* doesLoad, Bool* doesGet, IRExpr* e )
+static AvailExpr* irExpr_to_AvailExpr ( IRExpr* e )
 {
-   Int i;
-   switch (e->tag) {
-      case Iex_CCall:
-         for (i = 0; e->Iex.CCall.args[i]; i++)
-            setHints_Expr(doesLoad, doesGet, e->Iex.CCall.args[i]);
-         return;
-      case Iex_Mux0X:
-         setHints_Expr(doesLoad, doesGet, e->Iex.Mux0X.cond);
-         setHints_Expr(doesLoad, doesGet, e->Iex.Mux0X.expr0);
-         setHints_Expr(doesLoad, doesGet, e->Iex.Mux0X.exprX);
-         return;
-      case Iex_Binop:
-         setHints_Expr(doesLoad, doesGet, e->Iex.Binop.arg1);
-         setHints_Expr(doesLoad, doesGet, e->Iex.Binop.arg2);
-         return;
-      case Iex_Unop:
-         setHints_Expr(doesLoad, doesGet, e->Iex.Unop.arg);
-         return;
-      case Iex_LDle:
-         *doesLoad = True;
-         setHints_Expr(doesLoad, doesGet, e->Iex.LDle.addr);
-         return;
-      case Iex_Get:
-         *doesGet = True;
-         return;
-      case Iex_GetI:
-         *doesGet = True;
-         setHints_Expr(doesLoad, doesGet, e->Iex.GetI.ix);
-         return;
-      case Iex_Tmp:
-      case Iex_Const:
-         return;
-      default: 
-         vex_printf("\n"); ppIRExpr(e); vex_printf("\n");
-         vpanic("setHints_Expr");
+   AvailExpr* ae;
+
+   if (e->tag == Iex_Unop
+       && e->Iex.Unop.arg->tag == Iex_Tmp) {
+      ae = LibVEX_Alloc(sizeof(AvailExpr));
+      ae->tag      = Ut;
+      ae->u.Ut.op  = e->Iex.Unop.op;
+      ae->u.Ut.arg = e->Iex.Unop.arg->Iex.Tmp.tmp;
+      return ae;
+   }
+
+   if (e->tag == Iex_Binop
+       && e->Iex.Binop.arg1->tag == Iex_Tmp
+       && e->Iex.Binop.arg2->tag == Iex_Tmp) {
+      ae = LibVEX_Alloc(sizeof(AvailExpr));
+      ae->tag        = Btt;
+      ae->u.Btt.op   = e->Iex.Binop.op;
+      ae->u.Btt.arg1 = e->Iex.Binop.arg1->Iex.Tmp.tmp;
+      ae->u.Btt.arg2 = e->Iex.Binop.arg2->Iex.Tmp.tmp;
+      return ae;
+   }
+
+   if (e->tag == Iex_Binop
+      && e->Iex.Binop.arg1->tag == Iex_Tmp
+      && e->Iex.Binop.arg2->tag == Iex_Const) {
+      ae = LibVEX_Alloc(sizeof(AvailExpr));
+      ae->tag        = Btc;
+      ae->u.Btc.op   = e->Iex.Binop.op;
+      ae->u.Btc.arg1 = e->Iex.Binop.arg1->Iex.Tmp.tmp;
+      ae->u.Btc.con2 = *(e->Iex.Binop.arg2->Iex.Const.con);
+      return ae;
+   }
+
+   if (e->tag == Iex_Binop
+      && e->Iex.Binop.arg1->tag == Iex_Const
+      && e->Iex.Binop.arg2->tag == Iex_Tmp) {
+      ae = LibVEX_Alloc(sizeof(AvailExpr));
+      ae->tag        = Bct;
+      ae->u.Bct.op   = e->Iex.Binop.op;
+      ae->u.Bct.arg2 = e->Iex.Binop.arg2->Iex.Tmp.tmp;
+      ae->u.Bct.con1 = *(e->Iex.Binop.arg1->Iex.Const.con);
+      return ae;
+   }
+
+   if (e->tag == Iex_Const
+       && e->Iex.Const.con->tag == Ico_F64i) {
+      ae = LibVEX_Alloc(sizeof(AvailExpr));
+      ae->tag          = Cf64i;
+      ae->u.Cf64i.f64i = e->Iex.Const.con->Ico.F64i;
+      return ae;
    }
+
+   return NULL;
 }
 
 
-static void dumpInvalidated ( TmpInfo** env, IRBB* bb, /*INOUT*/Int* j )
+/* The BB is modified in-place. */
+
+void do_cse_BB ( IRBB* bb )
 {
-   Int k, oldest_op, oldest_k;
-   TmpInfo* ti;
+   Int        i, j;
+   IRTemp     t, q;
+   IRStmt*    st;
+   AvailExpr* eprime;
 
-   /* Dump all the bindings to marked as invalidated, in order. */
-   while (True) {
-  
-      /* find the oldest bind marked 'invalidateMe'. */
-      oldest_op = 1<<30;
-      oldest_k =  1<<30;
-      for (k = 0; k < bb->tyenv->types_used; k++) {
-         ti = env[k];
-         if (!ti)
-            continue;
-         if (!ti->expr)
-            continue;
-         if (!ti->invalidateMe)
-           continue;
-         /* vex_printf("FOUND INVAL %d %d\n", ti->origPos, oldest_op); */
-         if (ti->origPos < oldest_op) {
-            oldest_op = ti->origPos;
-            oldest_k = k;
-         }
-      }
+   HashHW* tenv = newHHW(); /* :: IRTemp -> IRTemp */
+   HashHW* aenv = newHHW(); /* :: AvailExpr* -> IRTemp */
 
-      /* No more binds to invalidate. */
-      if (oldest_op == 1<<30)
-        return;
+   vassert(sizeof(IRTemp) <= sizeof(HWord));
 
-      /* the oldest bind to invalidate has been identified */
-      vassert(oldest_op != 1<<31 && oldest_k != 1<<31);
-      ti = env[oldest_k];
-      vassert(ti->expr && ti->invalidateMe);
+   //ppIRBB(bb);
+   //vex_printf("\n\n");
 
-      /* and invalidate it ... */
-      bb->stmts[*j] = IRStmt_Tmp( (IRTemp)oldest_k, ti->expr );
-      /*  vex_printf("**1  "); ppIRStmt(bb->stmts[*j]); vex_printf("\n"); */
-      (*j)++;
-      ti->invalidateMe = False;
-      ti->expr = NULL; /* no longer available for substitution */
+   /* Iterate forwards over the stmts.  
+      On seeing "t = E", where E is one of the 3 AvailExpr forms:
+         let E' = apply tenv substitution to E
+         search aenv for E'
+            if a mapping E' -> q is found, 
+               replace this stmt by "t = q"
+               and add binding t -> q to tenv
+            else
+               add binding E' -> t to aenv
+               replace this stmt by "t = E'"
+      Ignore any other kind of stmt.
+   */
+   for (i = 0; i < bb->stmts_used; i++) {
+      st = bb->stmts[i];
+
+      /* ignore not-interestings */
+      if ((!st) || st->tag != Ist_Tmp)
+         continue;
+
+      t = st->Ist.Tmp.tmp;
+      eprime = irExpr_to_AvailExpr(st->Ist.Tmp.data);
+      /* ignore if not of AvailExpr form */
+      if (!eprime)
+         continue;
+
+      /* vex_printf("considering: " ); ppIRStmt(st); vex_printf("\n"); */
+
+      /* apply tenv */
+      subst_AvailExpr( tenv, eprime );
+
+      /* search aenv for eprime, unfortunately the hard way */
+      for (j = 0; j < aenv->used; j++)
+         if (aenv->inuse[j] && eq_AvailExpr(eprime, (AvailExpr*)aenv->key[j]))
+            break;
 
-   } /* loop which dumps the binds marked for invalidation */
+      if (j < aenv->used) {
+         /* A binding E' -> q was found.  Replace stmt by "t = q" and
+            note the t->q binding in tenv. */
+         /* (this is the core of the CSE action) */
+         q = (IRTemp)aenv->val[j];
+         bb->stmts[i] = IRStmt_Tmp( t, IRExpr_Tmp(q) );
+         addToHHW( tenv, (HWord)t, (HWord)q );
+      } else {
+         /* No binding was found, so instead we add E' -> t to our
+            collection of available expressions, replace this stmt
+            with "t = E'", and move on. */
+         bb->stmts[i] = IRStmt_Tmp( t, availExpr_to_IRExpr(eprime) );
+         addToHHW( aenv, (HWord)eprime, (HWord)t );
+      }
+   }
+
+   //ppIRBB(bb);
+   //sanityCheckIRBB(bb, Ity_I32);
+   //vex_printf("\n\n");
+      
 }
 
 
+/*---------------------------------------------------------------*/
+/*--- Add32/Sub32 chain collapsing                            ---*/
+/*---------------------------------------------------------------*/
 
-/* notstatic */ void do_treebuild_BB ( IRBB* bb )
-{
-   Int      i, j, k;
-   Bool     invPut, invStore;
-   IRStmt*  st;
-   IRStmt*  st2;
-   TmpInfo* ti;
-   IRExpr*  next2;
+/* ----- Helper functions for Add32/Sub32 chain collapsing ----- */
 
-   /* Mapping from IRTemp to TmpInfo*. */
-   Int       n_tmps = bb->tyenv->types_used;
-   TmpInfo** env    = LibVEX_Alloc(n_tmps * sizeof(TmpInfo*));
+/* Is this expression "Add32(tmp,const)" or "Sub32(tmp,const)" ?  If
+   yes, set *tmp and *i32 appropriately.  *i32 is set as if the
+   root node is Add32, not Sub32. */
 
-   for (i = 0; i < n_tmps; i++)
-      env[i] = NULL;
+static Bool isAdd32OrSub32 ( IRExpr* e, IRTemp* tmp, Int* i32 )
+{ 
+   if (e->tag != Iex_Binop)
+      return False;
+   if (e->Iex.Binop.op != Iop_Add32 && e->Iex.Binop.op != Iop_Sub32)
+      return False;
+   if (e->Iex.Binop.arg1->tag != Iex_Tmp)
+      return False;
+   if (e->Iex.Binop.arg2->tag != Iex_Const)
+      return False;
+   *tmp = e->Iex.Binop.arg1->Iex.Tmp.tmp;
+   *i32 = (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32);
+   if (e->Iex.Binop.op == Iop_Sub32)
+      *i32 = -*i32;
+   return True;
+}
 
-   /* Phase 1.  Scan forwards in bb, counting use occurrences of each
-      temp.  Also count occurrences in the bb->next field. */
 
-   for (i = 0; i < bb->stmts_used; i++) {
-      st = bb->stmts[i];
-      if (!st)
-         continue;
-      occCount_Stmt( env, st );
-   }
-   occCount_Expr(env, bb->next );
+/* Figure out if tmp can be expressed as tmp2 +32 const, for some
+   other tmp2.  Scan backwards from the specified start point -- an
+   optimisation. */
 
-#  if 0
-   for (i = 0; i < env->used; i++) {
-      if (!env->inuse[i])
-        continue;
-      ppIRTemp( (IRTemp)(env->key[i]) );
-      vex_printf("  used %d\n", ((TmpInfo*)env->val[i])->occ );
-   }
-#  endif
+static Bool collapseChain ( IRBB* bb, Int startHere,
+                            IRTemp tmp,
+                            IRTemp* tmp2, Int* i32 )
+{
+   Int     j, ii;
+   IRTemp  vv;
+   IRStmt* st;
+   IRExpr* e;
 
-   /* Phase 2.  Fill in the origPos fields. */
+   /* the (var, con) pair contain the current 'representation' for
+      'tmp'.  We start with 'tmp + 0'.  */
+   IRTemp var = tmp;
+   Int    con = 0;
 
-   for (i = 0; i < bb->stmts_used; i++) {
-      st = bb->stmts[i];
-      if (!st)
+   /* Scan backwards to see if tmp can be replaced by some other tmp
+     +/- a constant. */
+   for (j = startHere; j >= 0; j--) {
+      st = bb->stmts[j];
+      if (!st || st->tag != Ist_Tmp) 
          continue;
-      if (st->tag != Ist_Tmp)
+      if (st->Ist.Tmp.tmp != var)
          continue;
-
-      ti = env[(Int)st->Ist.Tmp.tmp];
-      if (!ti) {
-         vex_printf("\n");
-         ppIRTemp(st->Ist.Tmp.tmp);
-         vex_printf("\n");
-         vpanic("treebuild_BB (phase 2): unmapped IRTemp");
-      }
-      ti->origPos = i;
+      e = st->Ist.Tmp.data;
+      if (!isAdd32OrSub32(e, &vv, &ii))
+         break;
+      var = vv;
+      con += ii;
    }
+   if (j == -1)
+      /* no earlier binding for var .. ill-formed IR */
+      vpanic("collapseChain");
 
-   /* Phase 3.  Scan forwards in bb.  
+   /* so, did we find anything interesting? */
+   if (var == tmp)
+      return False; /* no .. */
+      
+   *tmp2 = var;
+   *i32  = con;
+   return True;
+}
 
-      On seeing 't = E', occ(t)==1,  
-            let E'=env(E), set t's binding to be E', and
-            delete this stmt.
-            Also examine E' and set the hints for E' appropriately
-              (doesLoad? doesGet?)
 
-      On seeing any other stmt, 
-            let stmt' = env(stmt)
-            remove from env any 't=E' binds invalidated by stmt
-                emit the invalidated stmts
-            emit stmt'
+/* ------- Main function for Add32/Sub32 chain collapsing ------ */
 
-      Apply env to bb->next.
-   */
+static void collapse_AddSub_chains_BB ( IRBB* bb )
+{
+   IRStmt *st;
+   IRTemp var, var2;
+   Int    i, con, con2;
 
-   /* The stmts in bb are being reordered, and we are guaranteed to
-      end up with no more than the number we started with.  Use i to
-      be the cursor of the current stmt examined and j <= i to be that
-      for the current stmt being written. 
-   */
-   j = 0;
-   for (i = 0; i < bb->stmts_used; i++) {
+   for (i = bb->stmts_used-1; i >= 0; i--) {
       st = bb->stmts[i];
       if (!st)
          continue;
-     
-      if (st->tag == Ist_Tmp) {
-         /* vex_printf("acquire binding\n"); */
-         ti = env[st->Ist.Tmp.tmp];
-         if (!ti) {
-            vpanic("treebuild_BB (phase 2): unmapped IRTemp");
-         }
-         if (ti->occ == 1) {
-            /* ok, we have 't = E', occ(t)==1.  Do the abovementioned actions. */
-            IRExpr* e = st->Ist.Tmp.data;
-            IRExpr* e2 = tbSubst_Expr(env, e);
-            ti->expr = e2;
-            ti->eDoesLoad = ti->eDoesGet = False;
-            setHints_Expr(&ti->eDoesLoad, &ti->eDoesGet, e2);
-            /* don't advance j, as we are deleting this stmt and instead
-               holding it temporarily in the env. */
-            continue; /* the for (i = 0; i < bb->stmts_used; i++) loop */
-         }
-      }
 
-      /* we get here for any other kind of statement. */
-      /* 'use up' any bindings required by the current statement. */
-      st2 = tbSubst_Stmt(env, st);
+      /* Try to collapse 't1 = Add32/Sub32(t2, con)'. */
 
-      /* Now, before this stmt, dump any bindings it invalidates.
-         These need to be dumped in the order in which they originally
-         appeared.  (Stupid algorithm): first, mark all bindings which
-         need to be dumped.  Then, dump them in the order in which
-         they were defined. */
-      invPut = st->tag == Ist_Put 
-               || st->tag == Ist_PutI || st->tag == Ist_Dirty;
-      invStore = st->tag == Ist_STle
-               || st->tag == Ist_Dirty;
+      if (st->tag == Ist_Tmp
+          && isAdd32OrSub32(st->Ist.Tmp.data, &var, &con)) {
 
-      for (k = 0; k < n_tmps; k++) {
-         ti = env[k];
-         if (!ti)
-            continue;
-         if (!ti->expr)
-            continue;
+         /* So e1 is of the form Add32(var,con) or Sub32(var,-con).
+            Find out if var can be expressed as var2 + con2. */
+         if (collapseChain(bb, i-1, var, &var2, &con2)) {
+            if (DEBUG_IROPT) {
+               vex_printf("replacing1 ");
+               ppIRStmt(st);
+               vex_printf(" with ");
+            }
+            con2 += con;
+            bb->stmts[i] 
+               = IRStmt_Tmp(
+                    st->Ist.Tmp.tmp,
+                    (con2 >= 0) 
+                      ? IRExpr_Binop(Iop_Add32, 
+                                     IRExpr_Tmp(var2),
+                                     IRExpr_Const(IRConst_U32(con2)))
+                      : IRExpr_Binop(Iop_Sub32, 
+                                     IRExpr_Tmp(var2),
+                                     IRExpr_Const(IRConst_U32(-con2)))
+                 );
+            if (DEBUG_IROPT) {
+               ppIRStmt(bb->stmts[i]);
+               vex_printf("\n");
+            }
+         }
 
-         /* We have to invalidate this binding. */
-         ti->invalidateMe 
-            = /* a store invalidates loaded data */
-              (ti->eDoesLoad && invStore)
-              /* a put invalidates get'd data */
-              || (ti->eDoesGet && invPut)
-              /* a put invalidates loaded data.  Note, we could do
-                 much better here in the sense that we only need to
-                 invalidate trees containing loads if the Put in
-                 question is marked as requiring precise
-                 exceptions. */
-              || (ti->eDoesLoad && invPut);
-         /*
-         if (ti->invalidateMe)
-           vex_printf("SET INVAL\n");
-           */
+         continue;
+      }
+
+      /* Try to collapse 't1 = GetI[t2, con]'. */
+
+      if (st->tag == Ist_Tmp
+          && st->Ist.Tmp.data->tag == Iex_GetI
+          && st->Ist.Tmp.data->Iex.GetI.ix->tag == Iex_Tmp
+          && collapseChain(bb, i-1, st->Ist.Tmp.data->Iex.GetI.ix
+                                      ->Iex.Tmp.tmp, &var2, &con2)) {
+         if (DEBUG_IROPT) {
+            vex_printf("replacing3 ");
+            ppIRStmt(st);
+            vex_printf(" with ");
+         }
+         con2 += st->Ist.Tmp.data->Iex.GetI.bias;
+         bb->stmts[i]
+            = IRStmt_Tmp(
+                 st->Ist.Tmp.tmp,
+                 IRExpr_GetI(st->Ist.Tmp.data->Iex.GetI.descr,
+                             IRExpr_Tmp(var2),
+                             con2));
+         if (DEBUG_IROPT) {
+            ppIRStmt(bb->stmts[i]);
+            vex_printf("\n");
+         }
+         continue;
       }
 
-      dumpInvalidated ( env, bb, &j );
-
-      /* finally, emit the substituted statement */
-      bb->stmts[j] = st2;
-      /*  vex_printf("**2  "); ppIRStmt(bb->stmts[j]); vex_printf("\n"); */
-      j++;
+      /* Perhaps st is PutI[t, con] ? */
 
-      vassert(j <= i+1);
-   } /* for each stmt in the original bb ... */
+      if (st->tag == Ist_PutI
+          && st->Ist.PutI.ix->tag == Iex_Tmp
+          && collapseChain(bb, i-1, st->Ist.PutI.ix->Iex.Tmp.tmp, 
+                               &var2, &con2)) {
+         if (DEBUG_IROPT) {
+            vex_printf("replacing2 ");
+            ppIRStmt(st);
+            vex_printf(" with ");
+         }
+         con2 += st->Ist.PutI.bias;
+         bb->stmts[i]
+           = IRStmt_PutI(st->Ist.PutI.descr,
+                         IRExpr_Tmp(var2),
+                         con2,
+                         st->Ist.PutI.data);
+         if (DEBUG_IROPT) {
+            ppIRStmt(bb->stmts[i]);
+            vex_printf("\n");
+         }
+         continue;
+      }
 
-   /* Finally ... substitute the ->next field as much as possible, and
-      dump any left-over bindings.  Hmm.  Perhaps there should be no
-      left over bindings?  Or any left-over bindings are
-      by definition dead? */
-   next2 = tbSubst_Expr(env, bb->next);
-   bb->next = next2;
-   bb->stmts_used = j;
+   } /* for */
 }
 
 
-
 /*---------------------------------------------------------------*/
-/*--- Common Subexpression Elimination                        ---*/
+/*--- PutI/GetI transformations                               ---*/
 /*---------------------------------------------------------------*/
 
-/* Expensive in time and space. */
+/* ------- Helper functions for PutI/GetI transformations ------ */
 
-/* Uses two environments: 
-   a IRTemp -> IRTemp mapping 
-   a mapping from AvailExpr* to IRTemp 
+/* Do a1 and a2 denote identical values?  Safe answer: False 
 */
+static Bool identicalAtoms ( IRExpr* a1, IRExpr* a2 )
+{
+   vassert(isAtom(a1));
+   vassert(isAtom(a2));
+   if (a1->tag == Iex_Tmp && a2->tag == Iex_Tmp)
+      return a1->Iex.Tmp.tmp == a2->Iex.Tmp.tmp;
+   if (a1->tag == Iex_Const && a2->tag == Iex_Const)
+      return eqIRConst(a1->Iex.Const.con, a2->Iex.Const.con);
+   return False;
+}
+
+
+/* Determine, to the extent possible, the relationship between two
+   guest state accesses.  The possible outcomes are:
+
+   * Exact alias.  These two accesses denote precisely the same
+     piece of the guest state.
+
+   * Definitely no alias.  These two accesses are guaranteed not to
+     overlap any part of the guest state.
+
+   * Unknown -- if neither of the above can be established.
+
+   If in doubt, return Unknown.  */
 
 typedef
-   struct {
-      enum { Ut, Btt, Btc, Bct, Cf64i } tag;
-      union {
-         /* unop(tmp) */
-         struct {
-            IROp   op;
-            IRTemp arg;
-         } Ut;
-         /* binop(tmp,tmp) */
-         struct {
-            IROp   op;
-            IRTemp arg1;
-            IRTemp arg2;
-         } Btt;
-         /* binop(tmp,const) */
-         struct {
-            IROp    op;
-            IRTemp  arg1;
-            IRConst con2;
-         } Btc;
-         /* binop(const,tmp) */
-         struct {
-            IROp    op;
-            IRConst con1;
-            IRTemp  arg2;
-         } Bct;
-         /* F64i-style const */
-         struct {
-            ULong f64i;
-         } Cf64i;
-      } u;
-   }
-   AvailExpr;
+   enum { ExactAlias, NoAlias, UnknownAlias }
+   GSAliasing;
 
-static Bool eq_AvailExpr ( AvailExpr* a1, AvailExpr* a2 )
-{
-   if (a1->tag != a2->tag)
-      return False;
-   switch (a1->tag) {
-      case Ut: 
-         return a1->u.Ut.op == a2->u.Ut.op 
-                && a1->u.Ut.arg == a2->u.Ut.arg;
-      case Btt: 
-         return a1->u.Btt.op == a2->u.Btt.op
-                && a1->u.Btt.arg1 == a2->u.Btt.arg1
-                && a1->u.Btt.arg2 == a2->u.Btt.arg2;
-      case Btc: 
-         return a1->u.Btc.op == a2->u.Btc.op
-                && a1->u.Btc.arg1 == a2->u.Btc.arg1
-                && eqIRConst(&a1->u.Btc.con2, &a2->u.Btc.con2);
-      case Bct: 
-         return a1->u.Bct.op == a2->u.Bct.op
-                && a1->u.Bct.arg2 == a2->u.Bct.arg2
-                && eqIRConst(&a1->u.Bct.con1, &a2->u.Bct.con1);
-      case Cf64i: 
-         return a1->u.Cf64i.f64i == a2->u.Cf64i.f64i;
-      default: vpanic("eq_AvailExpr");
-   }
-}
 
-static IRExpr* availExpr_to_IRExpr ( AvailExpr* ae ) 
-{
-   IRConst* con;
-   switch (ae->tag) {
-      case Ut:
-         return IRExpr_Unop( ae->u.Ut.op, IRExpr_Tmp(ae->u.Ut.arg) );
-      case Btt:
-         return IRExpr_Binop( ae->u.Btt.op,
-                              IRExpr_Tmp(ae->u.Btt.arg1),
-                              IRExpr_Tmp(ae->u.Btt.arg2) );
-      case Btc:
-         con = LibVEX_Alloc(sizeof(IRConst));
-         *con = ae->u.Btc.con2;
-         return IRExpr_Binop( ae->u.Btc.op,
-                              IRExpr_Tmp(ae->u.Btc.arg1), IRExpr_Const(con) );
-      case Bct:
-         con = LibVEX_Alloc(sizeof(IRConst));
-         *con = ae->u.Bct.con1;
-         return IRExpr_Binop( ae->u.Bct.op,
-                              IRExpr_Const(con), IRExpr_Tmp(ae->u.Bct.arg2) );
-      case Cf64i:
-         return IRExpr_Const(IRConst_F64i(ae->u.Cf64i.f64i));
-      default:
-         vpanic("availExpr_to_IRExpr");
-   }
-}
+/* Produces the alias relation between an indexed guest
+   state access and a non-indexed access. */
 
-inline
-static IRTemp subst_AvailExpr_Temp ( HashHW* env, IRTemp tmp )
+static
+GSAliasing getAliasingRelation_IC ( IRArray* descr1, IRExpr* ix1,
+                                    Int offset2, IRType ty2 )
 {
-   HWord res;
-   /* env :: IRTemp -> IRTemp */
-   if (lookupHHW( env, &res, (HWord)tmp ))
-      return (IRTemp)res;
-   else
-      return tmp;
+   UInt minoff1, maxoff1, minoff2, maxoff2;
+
+   getArrayBounds( descr1, &minoff1, &maxoff1 );
+   minoff2 = offset2;
+   maxoff2 = minoff2 + sizeofIRType(ty2) - 1;
+
+   if (maxoff1 < minoff2 || maxoff2 < minoff1)
+      return NoAlias;
+
+   /* Could probably do better here if required.  For the moment
+      however just claim not to know anything more. */
+   return UnknownAlias;
 }
 
-static void subst_AvailExpr ( HashHW* env, AvailExpr* ae )
+
+/* Produces the alias relation between two indexed guest state
+   accesses. */
+
+static
+GSAliasing getAliasingRelation_II ( 
+              IRArray* descr1, IRExpr* ix1, Int bias1,
+              IRArray* descr2, IRExpr* ix2, Int bias2
+           )
 {
-   /* env :: IRTemp -> IRTemp */
-   switch (ae->tag) {
-      case Ut:
-         ae->u.Ut.arg = subst_AvailExpr_Temp( env, ae->u.Ut.arg );
-         break;
-      case Btt:
-         ae->u.Btt.arg1 = subst_AvailExpr_Temp( env, ae->u.Btt.arg1 );
-         ae->u.Btt.arg2 = subst_AvailExpr_Temp( env, ae->u.Btt.arg2 );
-         break;
-      case Btc:
-         ae->u.Btc.arg1 = subst_AvailExpr_Temp( env, ae->u.Btc.arg1 );
-         break;
-      case Bct:
-         ae->u.Bct.arg2 = subst_AvailExpr_Temp( env, ae->u.Bct.arg2 );
-         break;
-      case Cf64i:
-         break;
-      default: 
-         vpanic("subst_AvailExpr");
+   UInt minoff1, maxoff1, minoff2, maxoff2;
+   Int  iters;
+
+   /* First try hard to show they don't alias. */
+   getArrayBounds( descr1, &minoff1, &maxoff1 );
+   getArrayBounds( descr2, &minoff2, &maxoff2 );
+   if (maxoff1 < minoff2 || maxoff2 < minoff1)
+      return NoAlias;
+
+   /* So the two arrays at least partially overlap.  To get any
+      further we'll have to be sure that the descriptors are
+      identical. */
+   if (!eqIRArray(descr1, descr2))
+      return UnknownAlias;
+
+   /* The descriptors are identical.  Now the only difference can be
+      in the index expressions.  If they cannot be shown to be
+      identical, we have to say we don't know what the aliasing
+      relation will be.  Now, since the IR is flattened, the index
+      expressions should be atoms -- either consts or tmps.  So that
+      makes the comparison simple. */
+   vassert(isAtom(ix1));
+   vassert(isAtom(ix2));
+   if (!identicalAtoms(ix1,ix2))
+      return UnknownAlias;
+
+   /* Ok, the index expressions are identical.  So now the only way
+      they can be different is in the bias.  Normalise this
+      paranoidly, to reliably establish equality/non-equality. */
+
+   /* So now we know that the GetI and PutI index the same array
+      with the same base.  Are the offsets the same, modulo the
+      array size?  Do this paranoidly. */
+   vassert(descr1->nElems == descr2->nElems);
+   vassert(descr1->elemTy == descr2->elemTy);
+   vassert(descr1->base   == descr2->base);
+   iters = 0;
+   while (bias1 < 0 || bias2 < 0) {
+      bias1 += descr1->nElems;
+      bias2 += descr1->nElems;
+      iters++;
+      if (iters > 10)
+         vpanic("getAliasingRelation: iters");
    }
+   vassert(bias1 >= 0 && bias2 >= 0);
+   bias1 %= descr1->nElems;
+   bias2 %= descr1->nElems;
+   vassert(bias1 >= 0 && bias1 < descr1->nElems);
+   vassert(bias2 >= 0 && bias2 < descr1->nElems);
+
+   /* Finally, biasP and biasG are normalised into the range 
+      0 .. descrP/G->nElems - 1.  And so we can establish
+      equality/non-equality. */
+
+   return bias1==bias2 ? ExactAlias : NoAlias;
 }
 
-static AvailExpr* irExpr_to_AvailExpr ( IRExpr* e )
+
+/* Given the parts (descr, tmp, bias) for a GetI, scan backwards from
+   the given starting point to find, if any, a PutI which writes
+   exactly the same piece of guest state, and so return the expression
+   that the PutI writes.  This is the core of PutI-GetI forwarding. */
+
+static 
+IRExpr* findPutI ( IRBB* bb, Int startHere,
+                   IRArray* descrG, IRExpr* ixG, Int biasG )
 {
-   AvailExpr* ae;
+   Int        j;
+   IRStmt*    st;
+   GSAliasing relation;
 
-   if (e->tag == Iex_Unop
-       && e->Iex.Unop.arg->tag == Iex_Tmp) {
-      ae = LibVEX_Alloc(sizeof(AvailExpr));
-      ae->tag      = Ut;
-      ae->u.Ut.op  = e->Iex.Unop.op;
-      ae->u.Ut.arg = e->Iex.Unop.arg->Iex.Tmp.tmp;
-      return ae;
+   if (0) {
+      vex_printf("\nfindPutI ");
+      ppIRArray(descrG);
+      vex_printf(" ");
+      ppIRExpr(ixG);
+      vex_printf(" %d\n", biasG);
    }
 
-   if (e->tag == Iex_Binop
-       && e->Iex.Binop.arg1->tag == Iex_Tmp
-       && e->Iex.Binop.arg2->tag == Iex_Tmp) {
-      ae = LibVEX_Alloc(sizeof(AvailExpr));
-      ae->tag        = Btt;
-      ae->u.Btt.op   = e->Iex.Binop.op;
-      ae->u.Btt.arg1 = e->Iex.Binop.arg1->Iex.Tmp.tmp;
-      ae->u.Btt.arg2 = e->Iex.Binop.arg2->Iex.Tmp.tmp;
-      return ae;
-   }
+   /* Scan backwards in bb from startHere to find a suitable PutI
+      binding for (descrG, ixG, biasG), if any. */
 
-   if (e->tag == Iex_Binop
-      && e->Iex.Binop.arg1->tag == Iex_Tmp
-      && e->Iex.Binop.arg2->tag == Iex_Const) {
-      ae = LibVEX_Alloc(sizeof(AvailExpr));
-      ae->tag        = Btc;
-      ae->u.Btc.op   = e->Iex.Binop.op;
-      ae->u.Btc.arg1 = e->Iex.Binop.arg1->Iex.Tmp.tmp;
-      ae->u.Btc.con2 = *(e->Iex.Binop.arg2->Iex.Const.con);
-      return ae;
-   }
+   for (j = startHere; j >= 0; j--) {
+      st = bb->stmts[j];
+      if (!st) continue;
 
-   if (e->tag == Iex_Binop
-      && e->Iex.Binop.arg1->tag == Iex_Const
-      && e->Iex.Binop.arg2->tag == Iex_Tmp) {
-      ae = LibVEX_Alloc(sizeof(AvailExpr));
-      ae->tag        = Bct;
-      ae->u.Bct.op   = e->Iex.Binop.op;
-      ae->u.Bct.arg2 = e->Iex.Binop.arg2->Iex.Tmp.tmp;
-      ae->u.Bct.con1 = *(e->Iex.Binop.arg1->Iex.Const.con);
-      return ae;
-   }
+      if (st->tag == Ist_Put) {
+         /* Non-indexed Put.  This can't give a binding, but we do
+            need to check it doesn't invalidate the search by
+            overlapping any part of the indexed guest state. */
 
-   if (e->tag == Iex_Const
-       && e->Iex.Const.con->tag == Ico_F64i) {
-      ae = LibVEX_Alloc(sizeof(AvailExpr));
-      ae->tag          = Cf64i;
-      ae->u.Cf64i.f64i = e->Iex.Const.con->Ico.F64i;
-      return ae;
-   }
+         relation
+            = getAliasingRelation_IC(
+                 descrG, ixG,
+                 st->Ist.Put.offset,
+                 typeOfIRExpr(bb->tyenv,st->Ist.Put.data) );
 
-   return NULL;
-}
+         if (relation == NoAlias) {
+            /* we're OK; keep going */
+            continue;
+         } else {
+            /* relation == UnknownAlias || relation == ExactAlias */
+            /* If this assertion fails, we've found a Put which writes
+               an area of guest state which is read by a GetI.  Which
+               is unlikely (although not per se wrong). */
+            vassert(relation != ExactAlias);
+            /* This Put potentially writes guest state that the GetI
+               reads; we must fail. */
+            return NULL;
+         }
+      }
 
+      if (st->tag == Ist_PutI) {
 
-/* The BB is modified in-place. */
+         relation = getAliasingRelation_II(
+                       descrG, ixG, biasG,
+                       st->Ist.PutI.descr,
+                       st->Ist.PutI.ix,
+                       st->Ist.PutI.bias
+                    );
 
-void do_cse_BB ( IRBB* bb )
-{
-   Int        i, j;
-   IRTemp     t, q;
-   IRStmt*    st;
-   AvailExpr* eprime;
+         if (relation == NoAlias) {
+            /* This PutI definitely doesn't overlap.  Ignore it and
+               keep going. */
+            continue; /* the for j loop */
+         }
 
-   HashHW* tenv = newHHW(); /* :: IRTemp -> IRTemp */
-   HashHW* aenv = newHHW(); /* :: AvailExpr* -> IRTemp */
+         if (relation == UnknownAlias) {
+            /* We don't know if this PutI writes to the same guest
+               state that the GetI, or not.  So we have to give up. */
+            return NULL;
+         }
 
-   vassert(sizeof(IRTemp) <= sizeof(HWord));
+         /* Otherwise, we've found what we're looking for.  */
+         vassert(relation == ExactAlias);
+         return st->Ist.PutI.data;
 
-   //ppIRBB(bb);
-   //vex_printf("\n\n");
+      } /* if (st->tag == Ist_PutI) */
 
-   /* Iterate forwards over the stmts.  
-      On seeing "t = E", where E is one of the 3 AvailExpr forms:
-         let E' = apply tenv substitution to E
-         search aenv for E'
-            if a mapping E' -> q is found, 
-               replace this stmt by "t = q"
-               and add binding t -> q to tenv
-            else
-               add binding E' -> t to aenv
-               replace this stmt by "t = E'"
-      Ignore any other kind of stmt.
-   */
-   for (i = 0; i < bb->stmts_used; i++) {
-      st = bb->stmts[i];
+      if (st->tag == Ist_Dirty) {
+         /* Be conservative.  If the dirty call has any guest effects at
+            all, give up.  We could do better -- only give up if there
+            are any guest writes/modifies. */
+         if (st->Ist.Dirty.details->nFxState > 0)
+            return NULL;
+      }
 
-      /* ignore not-interestings */
-      if ((!st) || st->tag != Ist_Tmp)
-         continue;
+   } /* for */
 
-      t = st->Ist.Tmp.tmp;
-      eprime = irExpr_to_AvailExpr(st->Ist.Tmp.data);
-      /* ignore if not of AvailExpr form */
-      if (!eprime)
-         continue;
+   /* No valid replacement was found. */
+   return NULL;
+}
 
-      /* vex_printf("considering: " ); ppIRStmt(st); vex_printf("\n"); */
 
-      /* apply tenv */
-      subst_AvailExpr( tenv, eprime );
 
-      /* search aenv for eprime, unfortunately the hard way */
-      for (j = 0; j < aenv->used; j++)
-         if (aenv->inuse[j] && eq_AvailExpr(eprime, (AvailExpr*)aenv->key[j]))
-            break;
+/* Assuming pi is a PutI stmt, is s2 identical to it (in the sense
+   that it writes exactly the same piece of guest state) ?  Safe
+   answer: False. */
 
-      if (j < aenv->used) {
-         /* A binding E' -> q was found.  Replace stmt by "t = q" and
-            note the t->q binding in tenv. */
-         /* (this is the core of the CSE action) */
-         q = (IRTemp)aenv->val[j];
-         bb->stmts[i] = IRStmt_Tmp( t, IRExpr_Tmp(q) );
-         addToHHW( tenv, (HWord)t, (HWord)q );
-      } else {
-         /* No binding was found, so instead we add E' -> t to our
-            collection of available expressions, replace this stmt
-            with "t = E'", and move on. */
-         bb->stmts[i] = IRStmt_Tmp( t, availExpr_to_IRExpr(eprime) );
-         addToHHW( aenv, (HWord)eprime, (HWord)t );
-      }
-   }
+static Bool identicalPutIs ( IRStmt* pi, IRStmt* s2 )
+{
+   vassert(pi->tag == Ist_PutI);
+   if (s2->tag != Ist_PutI)
+      return False;
 
-   //ppIRBB(bb);
-   //sanityCheckIRBB(bb, Ity_I32);
-   //vex_printf("\n\n");
-      
+   return getAliasingRelation_II( 
+             pi->Ist.PutI.descr, pi->Ist.PutI.ix, pi->Ist.PutI.bias, 
+             s2->Ist.PutI.descr, s2->Ist.PutI.ix, s2->Ist.PutI.bias
+          )
+          == ExactAlias;
 }
 
 
-/*---------------------------------------------------------------*/
-/*--- Add32/Sub32 chain collapsing                            ---*/
-/*---------------------------------------------------------------*/
+/* Assuming pi is a PutI stmt, is s2 a Get/GetI/Put/PutI which might
+   overlap it?  Safe answer: True.  Note, we could do a lot better
+   than this if needed. */
 
-/* ----- Helper functions for Add32/Sub32 chain collapsing ----- */
+static 
+Bool guestAccessWhichMightOverlapPutI ( 
+        IRTypeEnv* tyenv, IRStmt* pi, IRStmt* s2 
+     )
+{
+   GSAliasing relation;
+   UInt       minoffP, maxoffP;
 
-/* Is this expression "Add32(tmp,const)" or "Sub32(tmp,const)" ?  If
-   yes, set *tmp and *i32 appropriately.  *i32 is set as if the
-   root node is Add32, not Sub32. */
+   vassert(pi->tag == Ist_PutI);
+   getArrayBounds(pi->Ist.PutI.descr, &minoffP, &maxoffP);
+   switch (s2->tag) {
 
-static Bool isAdd32OrSub32 ( IRExpr* e, IRTemp* tmp, Int* i32 )
-{ 
-   if (e->tag != Iex_Binop)
-      return False;
-   if (e->Iex.Binop.op != Iop_Add32 && e->Iex.Binop.op != Iop_Sub32)
-      return False;
-   if (e->Iex.Binop.arg1->tag != Iex_Tmp)
-      return False;
-   if (e->Iex.Binop.arg2->tag != Iex_Const)
-      return False;
-   *tmp = e->Iex.Binop.arg1->Iex.Tmp.tmp;
-   *i32 = (Int)(e->Iex.Binop.arg2->Iex.Const.con->Ico.U32);
-   if (e->Iex.Binop.op == Iop_Sub32)
-      *i32 = -*i32;
-   return True;
-}
+      case Ist_Dirty:
+         /* If the dirty call has any guest effects at all, give up.
+            Probably could do better. */
+         if (s2->Ist.Dirty.details->nFxState > 0)
+            return True;
+         return False;
 
+      case Ist_Put:
+         vassert(isAtom(s2->Ist.Put.data));
+         relation 
+            = getAliasingRelation_IC(
+                 pi->Ist.PutI.descr, pi->Ist.PutI.ix,
+                 s2->Ist.Put.offset, 
+                 typeOfIRExpr(tyenv,s2->Ist.Put.data)
+              );
+         goto have_relation;
 
-/* Figure out if tmp can be expressed as tmp2 +32 const, for some
-   other tmp2.  Scan backwards from the specified start point -- an
-   optimisation. */
+      case Ist_PutI:
+         vassert(isAtom(s2->Ist.PutI.ix));
+         vassert(isAtom(s2->Ist.PutI.data));
+         relation
+            = getAliasingRelation_II(
+                 pi->Ist.PutI.descr, pi->Ist.PutI.ix, pi->Ist.PutI.bias, 
+                 s2->Ist.PutI.descr, s2->Ist.PutI.ix, s2->Ist.PutI.bias
+              );
+         goto have_relation;
 
-static Bool collapseChain ( IRBB* bb, Int startHere,
-                            IRTemp tmp,
-                            IRTemp* tmp2, Int* i32 )
-{
-   Int     j, ii;
-   IRTemp  vv;
-   IRStmt* st;
-   IRExpr* e;
+      case Ist_Tmp:
+         if (s2->Ist.Tmp.data->tag == Iex_GetI) {
+            relation
+               = getAliasingRelation_II(
+                    pi->Ist.PutI.descr, pi->Ist.PutI.ix, 
+                                        pi->Ist.PutI.bias, 
+                    s2->Ist.Tmp.data->Iex.GetI.descr,
+                    s2->Ist.Tmp.data->Iex.GetI.ix,
+                    s2->Ist.Tmp.data->Iex.GetI.bias
+                 );
+            goto have_relation;
+         }
+         if (s2->Ist.Tmp.data->tag == Iex_Get) {
+            relation
+               = getAliasingRelation_IC(
+                    pi->Ist.PutI.descr, pi->Ist.PutI.ix,
+                    s2->Ist.Tmp.data->Iex.Get.offset,
+                    s2->Ist.Tmp.data->Iex.Get.ty
+                 );
+            goto have_relation;
+         }
+         return False;
 
-   /* the (var, con) pair contain the current 'representation' for
-      'tmp'.  We start with 'tmp + 0'.  */
-   IRTemp var = tmp;
-   Int    con = 0;
+      case Ist_STle:
+         vassert(isAtom(s2->Ist.STle.addr));
+         vassert(isAtom(s2->Ist.STle.data));
+         return False;
 
-   /* Scan backwards to see if tmp can be replaced by some other tmp
-     +/- a constant. */
-   for (j = startHere; j >= 0; j--) {
-      st = bb->stmts[j];
-      if (!st || st->tag != Ist_Tmp) 
-         continue;
-      if (st->Ist.Tmp.tmp != var)
-         continue;
-      e = st->Ist.Tmp.data;
-      if (!isAdd32OrSub32(e, &vv, &ii))
-         break;
-      var = vv;
-      con += ii;
+      default:
+         vex_printf("\n"); ppIRStmt(s2); vex_printf("\n");
+         vpanic("guestAccessWhichMightOverlapPutI");
    }
-   if (j == -1)
-      /* no earlier binding for var .. ill-formed IR */
-      vpanic("collapseChain");
 
-   /* so, did we find anything interesting? */
-   if (var == tmp)
-      return False; /* no .. */
-      
-   *tmp2 = var;
-   *i32  = con;
-   return True;
+  have_relation:
+   if (relation == NoAlias)
+      return False;
+   else
+      return True; /* ExactAlias or UnknownAlias */
 }
 
 
-/* ------- Main function for Add32/Sub32 chain collapsing ------ */
 
-static void collapse_AddSub_chains_BB ( IRBB* bb )
+/* ---------- PutI/GetI transformations main functions --------- */
+
+/* Remove redundant GetIs, to the extent that they can be detected.
+   bb is modified in-place. */
+
+static
+void do_redundant_GetI_elimination ( IRBB* bb )
 {
-   IRStmt *st;
-   IRTemp var, var2;
-   Int    i, con, con2;
+   Int     i;
+   IRStmt* st;
 
    for (i = bb->stmts_used-1; i >= 0; i--) {
       st = bb->stmts[i];
       if (!st)
          continue;
 
-      /* Try to collapse 't1 = Add32/Sub32(t2, con)'. */
-
       if (st->tag == Ist_Tmp
-          && isAdd32OrSub32(st->Ist.Tmp.data, &var, &con)) {
-
-         /* So e1 is of the form Add32(var,con) or Sub32(var,-con).
-            Find out if var can be expressed as var2 + con2. */
-         if (collapseChain(bb, i-1, var, &var2, &con2)) {
-            if (DEBUG_IROPT) {
-               vex_printf("replacing1 ");
-               ppIRStmt(st);
-               vex_printf(" with ");
-            }
-            con2 += con;
-            bb->stmts[i] 
-               = IRStmt_Tmp(
-                    st->Ist.Tmp.tmp,
-                    (con2 >= 0) 
-                      ? IRExpr_Binop(Iop_Add32, 
-                                     IRExpr_Tmp(var2),
-                                     IRExpr_Const(IRConst_U32(con2)))
-                      : IRExpr_Binop(Iop_Sub32, 
-                                     IRExpr_Tmp(var2),
-                                     IRExpr_Const(IRConst_U32(-con2)))
-                 );
+          && st->Ist.Tmp.data->tag == Iex_GetI
+          && st->Ist.Tmp.data->Iex.GetI.ix->tag == Iex_Tmp) {
+         IRArray* descr = st->Ist.Tmp.data->Iex.GetI.descr;
+         IRExpr*  ix    = st->Ist.Tmp.data->Iex.GetI.ix;
+         Int      bias  = st->Ist.Tmp.data->Iex.GetI.bias;
+         IRExpr*  replacement = findPutI(bb, i-1, descr, ix, bias);
+         if (replacement 
+             && isAtom(replacement)
+             /* Make sure we're doing a type-safe transformation! */
+             && typeOfIRExpr(bb->tyenv, replacement) == descr->elemTy) {
             if (DEBUG_IROPT) {
-               ppIRStmt(bb->stmts[i]);
+               vex_printf("rGI:  "); 
+               ppIRExpr(st->Ist.Tmp.data);
+               vex_printf(" -> ");
+               ppIRExpr(replacement);
                vex_printf("\n");
             }
+            bb->stmts[i] = IRStmt_Tmp(st->Ist.Tmp.tmp, replacement);
          }
-
-         continue;
       }
+   }
 
-      /* Try to collapse 't1 = GetI[t2, con]'. */
+}
 
-      if (st->tag == Ist_Tmp
-          && st->Ist.Tmp.data->tag == Iex_GetI
-          && st->Ist.Tmp.data->Iex.GetI.ix->tag == Iex_Tmp
-          && collapseChain(bb, i-1, st->Ist.Tmp.data->Iex.GetI.ix
-                                      ->Iex.Tmp.tmp, &var2, &con2)) {
-         if (DEBUG_IROPT) {
-            vex_printf("replacing3 ");
-            ppIRStmt(st);
-            vex_printf(" with ");
-         }
-         con2 += st->Ist.Tmp.data->Iex.GetI.bias;
-         bb->stmts[i]
-            = IRStmt_Tmp(
-                 st->Ist.Tmp.tmp,
-                 IRExpr_GetI(st->Ist.Tmp.data->Iex.GetI.descr,
-                             IRExpr_Tmp(var2),
-                             con2));
-         if (DEBUG_IROPT) {
-            ppIRStmt(bb->stmts[i]);
-            vex_printf("\n");
-         }
-         continue;
-      }
 
-      /* Perhaps st is PutI[t, con] ? */
+/* Remove redundant PutIs, to the extent which they can be detected.
+   bb is modified in-place. */
 
-      if (st->tag == Ist_PutI
-          && st->Ist.PutI.ix->tag == Iex_Tmp
-          && collapseChain(bb, i-1, st->Ist.PutI.ix->Iex.Tmp.tmp, 
-                               &var2, &con2)) {
-         if (DEBUG_IROPT) {
-            vex_printf("replacing2 ");
-            ppIRStmt(st);
-            vex_printf(" with ");
+static
+void do_redundant_PutI_elimination ( IRBB* bb )
+{
+   Int    i, j;
+   Bool   delete;
+   IRStmt *st, *stj;
+
+   for (i = 0; i < bb->stmts_used; i++) {
+      st = bb->stmts[i];
+      if (!st || st->tag != Ist_PutI)
+         continue;
+      /* Ok, search forwards from here to see if we can find another
+         PutI which makes this one redundant, and dodging various 
+         hazards.  Search forwards:
+         * If conditional exit, give up (because anything after that 
+           does not postdominate this put).
+         * If a Get which might overlap, give up (because this PutI 
+           not necessarily dead).
+         * If a Put which is identical, stop with success.
+         * If a Put which might overlap, but is not identical, give up.
+         * If a dirty helper call which might write guest state, give up.
+         * If a Put which definitely doesn't overlap, or any other 
+           kind of stmt, continue.
+      */
+      delete = False;
+      for (j = i+1; j < bb->stmts_used; j++) {
+         stj = bb->stmts[j];
+         if (!stj) 
+            continue;
+         if (identicalPutIs(st, stj)) {
+            /* success! */
+            delete = True;
+            break;
          }
-         con2 += st->Ist.PutI.bias;
-         bb->stmts[i]
-           = IRStmt_PutI(st->Ist.PutI.descr,
-                         IRExpr_Tmp(var2),
-                         con2,
-                         st->Ist.PutI.data);
+         if (stj->tag == Ist_Exit)
+            /* give up */
+            break;
+         if (st->tag == Ist_Dirty)
+            /* give up; could do better here */
+            break;
+         if (guestAccessWhichMightOverlapPutI(bb->tyenv, st, stj))
+            /* give up */
+           break;
+      }
+
+      if (delete) {
          if (DEBUG_IROPT) {
-            ppIRStmt(bb->stmts[i]);
+            vex_printf("rPI:  "); 
+            ppIRStmt(st); 
             vex_printf("\n");
          }
-         continue;
-      }
-
-   } /* for */
-}
-
-
-/*---------------------------------------------------------------*/
-/*--- PutI/GetI transformations                               ---*/
-/*---------------------------------------------------------------*/
-
-/* ------- Helper functions for PutI/GetI transformations ------ */
-
-/* Do a1 and a2 denote identical values?  Safe answer: False 
-*/
-static Bool identicalAtoms ( IRExpr* a1, IRExpr* a2 )
-{
-   vassert(isAtom(a1));
-   vassert(isAtom(a2));
-   if (a1->tag == Iex_Tmp && a2->tag == Iex_Tmp)
-      return a1->Iex.Tmp.tmp == a2->Iex.Tmp.tmp;
-   if (a1->tag == Iex_Const && a2->tag == Iex_Const)
-      return eqIRConst(a1->Iex.Const.con, a2->Iex.Const.con);
-   return False;
-}
+         bb->stmts[i] = NULL;
+      }
 
+   }
+}
 
-/* Determine, to the extent possible, the relationship between two
-   guest state accesses.  The possible outcomes are:
 
-   * Exact alias.  These two accesses denote precisely the same
-     piece of the guest state.
+/*---------------------------------------------------------------*/
+/*--- Loop unrolling                                          ---*/
+/*---------------------------------------------------------------*/
 
-   * Definely no alias.  These two accesses are guaranteed not to
-     overlap any part of the guest state.
+/* Adjust all tmp values (names) in e by delta.  e is destructively
+   modified. */
 
-   * Unknown -- if neither of the above can be established.
+static void deltaIRExpr ( IRExpr* e, Int delta )
+{
+   Int i;
+   switch (e->tag) {
+      case Iex_Tmp:
+         e->Iex.Tmp.tmp += delta;
+         break;
+      case Iex_Get:
+      case Iex_Const:
+         break;
+      case Iex_GetI:
+         deltaIRExpr(e->Iex.GetI.ix, delta);
+         break;
+      case Iex_Binop:
+         deltaIRExpr(e->Iex.Binop.arg1, delta);
+         deltaIRExpr(e->Iex.Binop.arg2, delta);
+         break;
+      case Iex_Unop:
+         deltaIRExpr(e->Iex.Unop.arg, delta);
+         break;
+      case Iex_LDle:
+         deltaIRExpr(e->Iex.LDle.addr, delta);
+         break;
+      case Iex_CCall:
+         for (i = 0; e->Iex.CCall.args[i]; i++)
+            deltaIRExpr(e->Iex.CCall.args[i], delta);
+         break;
+      case Iex_Mux0X:
+         deltaIRExpr(e->Iex.Mux0X.cond, delta);
+         deltaIRExpr(e->Iex.Mux0X.expr0, delta);
+         deltaIRExpr(e->Iex.Mux0X.exprX, delta);
+         break;
+      default: 
+         vex_printf("\n"); ppIRExpr(e); vex_printf("\n");
+         vpanic("deltaIRExpr");
+   }
+}
 
-   If in doubt, return Unknown.  */
+/* Adjust all tmp values (names) in st by delta.  st is destructively
+   modified. */
 
-typedef
-   enum { ExactAlias, NoAlias, UnknownAlias }
-   GSAliasing;
+static void deltaIRStmt ( IRStmt* st, Int delta )
+{
+   Int      i;
+   IRDirty* d;
+   switch (st->tag) {
+      case Ist_Put:
+         deltaIRExpr(st->Ist.Put.data, delta);
+         break;
+      case Ist_PutI:
+         deltaIRExpr(st->Ist.PutI.ix, delta);
+         deltaIRExpr(st->Ist.PutI.data, delta);
+         break;
+      case Ist_Tmp: 
+         st->Ist.Tmp.tmp += delta;
+         deltaIRExpr(st->Ist.Tmp.data, delta);
+         break;
+      case Ist_Exit:
+         deltaIRExpr(st->Ist.Exit.guard, delta);
+         break;
+      case Ist_STle:
+         deltaIRExpr(st->Ist.STle.addr, delta);
+         deltaIRExpr(st->Ist.STle.data, delta);
+         break;
+      case Ist_Dirty:
+         d = st->Ist.Dirty.details;
+         deltaIRExpr(d->guard, delta);
+         for (i = 0; d->args[i]; i++)
+            deltaIRExpr(d->args[i], delta);
+         if (d->tmp != IRTemp_INVALID)
+            d->tmp += delta;
+         if (d->mAddr)
+            deltaIRExpr(d->mAddr, delta);
+         break;
+      default: 
+         vex_printf("\n"); ppIRStmt(st); vex_printf("\n");
+         vpanic("deltaIRStmt");
+   }
+}
 
 
-/* Produces the alias relation between an indexed guest
-   state access and a non-indexed access. */
+/* If possible, return a loop-unrolled version of bb0.  The original
+   is changed.  If not possible, return NULL.  */
 
-static
-GSAliasing getAliasingRelation_IC ( IRArray* descr1, IRExpr* ix1,
-                                    Int offset2, IRType ty2 )
-{
-   UInt minoff1, maxoff1, minoff2, maxoff2;
+/* The two schemas considered are:
 
-   getArrayBounds( descr1, &minoff1, &maxoff1 );
-   minoff2 = offset2;
-   maxoff2 = minoff2 + sizeofIRType(ty2) - 1;
+     X: BODY; goto X
 
-   if (maxoff1 < minoff2 || maxoff2 < minoff1)
-      return NoAlias;
+     which unrolls to (eg)  X: BODY;BODY; goto X
 
-   /* Could probably do better here if required.  For the moment
-      however just claim not to know anything more. */
-   return UnknownAlias;
-}
+   and
 
+       X: BODY; if (c) goto X; goto Y
+   which trivially transforms to
+       X: BODY; if (!c) goto Y; goto X;
+   so it falls in the scope of the first case.  
 
-/* Produces the alias relation between two indexed guest state
-   accesses. */
+   X and Y must be literal (guest) addresses.
+*/
 
-static
-GSAliasing getAliasingRelation_II ( 
-              IRArray* descr1, IRExpr* ix1, Int bias1,
-              IRArray* descr2, IRExpr* ix2, Int bias2
-           )
+static Int calc_unroll_factor( IRBB* bb )
 {
-   UInt minoff1, maxoff1, minoff2, maxoff2;
-   Int  iters;
-
-   /* First try hard to show they don't alias. */
-   getArrayBounds( descr1, &minoff1, &maxoff1 );
-   getArrayBounds( descr2, &minoff2, &maxoff2 );
-   if (maxoff1 < minoff2 || maxoff2 < minoff1)
-      return NoAlias;
-
-   /* So the two arrays at least partially overlap.  To get any
-      further we'll have to be sure that the descriptors are
-      identical. */
-   if (!eqIRArray(descr1, descr2))
-      return UnknownAlias;
+   Int n_stmts, i;
 
-   /* The descriptors are identical.  Now the only difference can be
-      in the index expressions.  If they cannot be shown to be
-      identical, we have to say we don't know what the aliasing
-      relation will be.  Now, since the IR is flattened, the index
-      expressions should be atoms -- either consts or tmps.  So that
-      makes the comparison simple. */
-   vassert(isAtom(ix1));
-   vassert(isAtom(ix2));
-   if (!identicalAtoms(ix1,ix2))
-      return UnknownAlias;
+   n_stmts = 0;
+   for (i = 0; i < bb->stmts_used; i++)
+     if (bb->stmts[i])
+        n_stmts++;
 
-   /* Ok, the index expressions are identical.  So now the only way
-      they can be different is in the bias.  Normalise this
-      paranoidly, to reliably establish equality/non-equality. */
+   if (n_stmts <= vex_control.iropt_unroll_thresh/8) {
+      if (vex_control.iropt_verbosity > 0)
+         vex_printf("vex iropt: 8 x unrolling (%d sts -> %d sts)\n",
+                    n_stmts, 8* n_stmts);
+      return 8;
+   }
+   if (n_stmts <= vex_control.iropt_unroll_thresh/4) {
+      if (vex_control.iropt_verbosity > 0)
+         vex_printf("vex iropt: 4 x unrolling (%d sts -> %d sts)\n",
+                    n_stmts, 4* n_stmts);
+      return 4;
+   }
 
-   /* So now we know that the GetI and PutI index the same array
-      with the same base.  Are the offsets the same, modulo the
-      array size?  Do this paranoidly. */
-   vassert(descr1->nElems == descr2->nElems);
-   vassert(descr1->elemTy == descr2->elemTy);
-   vassert(descr1->base   == descr2->base);
-   iters = 0;
-   while (bias1 < 0 || bias2 < 0) {
-      bias1 += descr1->nElems;
-      bias2 += descr1->nElems;
-      iters++;
-      if (iters > 10)
-         vpanic("getAliasingRelation: iters");
+   if (n_stmts <= vex_control.iropt_unroll_thresh/2) {
+      if (vex_control.iropt_verbosity > 0)
+         vex_printf("vex iropt: 2 x unrolling (%d sts -> %d sts)\n",
+                    n_stmts, 2* n_stmts);
+      return 2;
    }
-   vassert(bias1 >= 0 && bias2 >= 0);
-   bias1 %= descr1->nElems;
-   bias2 %= descr1->nElems;
-   vassert(bias1 >= 0 && bias1 < descr1->nElems);
-   vassert(bias2 >= 0 && bias2 < descr1->nElems);
 
-   /* Finally, biasP and biasG are normalised into the range 
-      0 .. descrP/G->nElems - 1.  And so we can establish
-      equality/non-equality. */
+   if (vex_control.iropt_verbosity > 0)
+      vex_printf("vex iropt: not unrolling (%d sts)\n", n_stmts);
 
-   return bias1==bias2 ? ExactAlias : NoAlias;
+   return 1;
 }
 
 
-/* Given the parts (descr, tmp, bias) for a GetI, scan backwards from
-   the given starting point to find, if any, a PutI which writes
-   exactly the same piece of guest state, and so return the expression
-   that the PutI writes.  This is the core of PutI-GetI forwarding. */
-
-static 
-IRExpr* findPutI ( IRBB* bb, Int startHere,
-                   IRArray* descrG, IRExpr* ixG, Int biasG )
+static IRBB* maybe_loop_unroll_BB ( IRBB* bb0, Addr64 my_addr )
 {
-   Int        j;
-   IRStmt*    st;
-   GSAliasing relation;
+   Int      i, j, jmax, n_vars;
+   Bool     xxx_known;
+   Addr64   xxx_value, yyy_value;
+   IRExpr*  udst;
+   IRStmt*  st;
+   IRConst* con;
+   IRBB     *bb1, *bb2;
+   Int      unroll_factor;
 
-   if (0) {
-      vex_printf("\nfindPutI ");
-      ppIRArray(descrG);
-      vex_printf(" ");
-      ppIRExpr(ixG);
-      vex_printf(" %d\n", biasG);
+   if (vex_control.iropt_unroll_thresh <= 0)
+      return NULL;
+
+   /* First off, figure out if we can unroll this loop.  Do this
+      without modifying bb0. */
+
+   if (bb0->jumpkind != Ijk_Boring)
+      return NULL;
+
+   xxx_known = False;
+   xxx_value = 0;
+
+   /* Extract the next-guest address.  If it isn't a literal, we 
+      have to give up. */
+
+   udst = bb0->next;
+   if (udst->tag == Iex_Const
+       && (udst->Iex.Const.con->tag == Ico_U32
+           || udst->Iex.Const.con->tag == Ico_U64)) {
+      /* The BB ends in a jump to a literal location. */
+      xxx_known = True;
+      xxx_value = udst->Iex.Const.con->tag == Ico_U64
+                    ?  udst->Iex.Const.con->Ico.U64
+                    : (Addr64)(udst->Iex.Const.con->Ico.U32);
    }
 
-   /* Scan backwards in bb from startHere to find a suitable PutI
-      binding for (descrG, ixG, biasG), if any. */
+   if (!xxx_known)
+      return NULL;
 
-   for (j = startHere; j >= 0; j--) {
-      st = bb->stmts[j];
-      if (!st) continue;
+   /* Now we know the BB ends to a jump to a literal location.  If
+      it's a jump to itself (viz, idiom #1), move directly to the
+      unrolling stage, first cloning the bb so the original isn't
+      modified. */
+   if (xxx_value == my_addr) {
+      unroll_factor = calc_unroll_factor( bb0 );
+      if (unroll_factor < 2)
+         return NULL;
+      bb1 = dopyIRBB( bb0 );
+      bb0 = NULL;
+      udst = NULL; /* is now invalid */
+      goto do_unroll;
+   }
 
-      if (st->tag == Ist_Put) {
-         /* Non-indexed Put.  This can't give a binding, but we do
-            need to check it doesn't invalidate the search by
-            overlapping any part of the indexed guest state. */
+   /* Search for the second idiomatic form:
+        X: BODY; if (c) goto X; goto Y
+      We know Y, but need to establish that the last stmt
+      is 'if (c) goto X'.
+   */
+   yyy_value = xxx_value;
+   for (i = bb0->stmts_used-1; i >= 0; i--)
+      if (bb0->stmts[i])
+         break;
 
-         relation
-            = getAliasingRelation_IC(
-                 descrG, ixG,
-                 st->Ist.Put.offset,
-                 typeOfIRExpr(bb->tyenv,st->Ist.Put.data) );
+   if (i < 0)
+      return NULL; /* block with no stmts.  Strange. */
 
-         if (relation == NoAlias) {
-            /* we're OK; keep going */
-            continue;
-         } else {
-            /* relation == UnknownAlias || relation == ExactAlias */
-            /* If this assertion fails, we've found a Put which writes
-               an area of guest state which is read by a GetI.  Which
-               is unlikely (although not per se wrong). */
-            vassert(relation != ExactAlias);
-            /* This Put potentially writes guest state that the GetI
-               reads; we must fail. */
-            return NULL;
-         }
-      }
+   st = bb0->stmts[i];
+   if (st->tag != Ist_Exit)
+      return NULL;
+   if (st->Ist.Exit.jk != Ijk_Boring)
+      return NULL;
 
-      if (st->tag == Ist_PutI) {
+   con = st->Ist.Exit.dst;
+   vassert(con->tag == Ico_U32 || con->tag == Ico_U64);
 
-         relation = getAliasingRelation_II(
-                       descrG, ixG, biasG,
-                       st->Ist.PutI.descr,
-                       st->Ist.PutI.ix,
-                       st->Ist.PutI.bias
-                    );
+   xxx_value = con->tag == Ico_U64 
+                  ? st->Ist.Exit.dst->Ico.U64
+                  : (Addr64)(st->Ist.Exit.dst->Ico.U32);
 
-         if (relation == NoAlias) {
-            /* This PutI definitely doesn't overlap.  Ignore it and
-               keep going. */
-            continue; /* the for j loop */
-         }
+   /* If this assertion fails, we have some kind of type error. */
+   vassert(con->tag == udst->Iex.Const.con->tag);
 
-         if (relation == UnknownAlias) {
-            /* We don't know if this PutI writes to the same guest
-               state that the GetI, or not.  So we have to give up. */
-            return NULL;
-         }
+   if (xxx_value != my_addr)
+      /* We didn't find either idiom.  Give up. */
+      return NULL;
 
-         /* Otherwise, we've found what we're looking for.  */
-         vassert(relation == ExactAlias);
-         return st->Ist.PutI.data;
+   /* Ok, we found idiom #2.  Copy the BB, switch around the xxx and
+      yyy values (which makes it look like idiom #1), and go into
+      unrolling proper.  This means finding (again) the last stmt, in
+      the copied BB. */
 
-      } /* if (st->tag == Ist_PutI) */
+   unroll_factor = calc_unroll_factor( bb0 );
+   if (unroll_factor < 2)
+      return NULL;
 
-      if (st->tag == Ist_Dirty) {
-         /* Be conservative.  If the dirty call has any guest effects at
-            all, give up.  We could do better -- only give up if there
-            are any guest writes/modifies. */
-         if (st->Ist.Dirty.details->nFxState > 0)
-            return NULL;
-      }
+   bb1 = dopyIRBB( bb0 );
+   bb0 = NULL;
+   udst = NULL; /* is now invalid */
+   for (i = bb1->stmts_used-1; i >= 0; i--)
+      if (bb1->stmts[i])
+         break;
 
-   } /* for */
+   /* The next bunch of assertions should be true since we already
+      found and checked the last stmt in the original bb. */
 
-   /* No valid replacement was found. */
-   return NULL;
-}
+   vassert(i >= 0);
 
+   st = bb1->stmts[i];
+   vassert(st->tag == Ist_Exit);
 
+   con = st->Ist.Exit.dst;
+   vassert(con->tag == Ico_U32 || con->tag == Ico_U64);
 
-/* Assuming pi is a PutI stmt, is s2 identical to it (in the sense
-   that it writes exactly the same piece of guest state) ?  Safe
-   answer: False. */
+   udst = bb1->next;
+   vassert(udst->tag == Iex_Const);
+   vassert(udst->Iex.Const.con->tag == Ico_U32
+          || udst->Iex.Const.con->tag == Ico_U64);
+   vassert(con->tag == udst->Iex.Const.con->tag);
 
-static Bool identicalPutIs ( IRStmt* pi, IRStmt* s2 )
-{
-   vassert(pi->tag == Ist_PutI);
-   if (s2->tag != Ist_PutI)
-      return False;
+   /* switch the xxx and yyy fields around */
+   if (con->tag == Ico_U64) {
+      udst->Iex.Const.con->Ico.U64 = xxx_value;
+      con->Ico.U64 = yyy_value;
+   } else {
+      udst->Iex.Const.con->Ico.U32 = (UInt)xxx_value;
+      con->Ico.U64 = (UInt)yyy_value;
+   }
 
-   return getAliasingRelation_II( 
-             pi->Ist.PutI.descr, pi->Ist.PutI.ix, pi->Ist.PutI.bias, 
-             s2->Ist.PutI.descr, s2->Ist.PutI.ix, s2->Ist.PutI.bias
-          )
-          == ExactAlias;
-}
+   /* negate the test condition */
+   st->Ist.Exit.guard 
+      = IRExpr_Unop(Iop_Not1,dopyIRExpr(st->Ist.Exit.guard));
 
+   /* --- The unroller proper.  Both idioms are by now --- */
+   /* --- now converted to idiom 1. --- */
 
-/* Assuming pi is a PutI stmt, is s2 a Get/GetI/Put/PutI which might
-   overlap it?  Safe answer: True.  Note, we could do a lot better
-   than this if needed. */
+  do_unroll:
 
-static 
-Bool guestAccessWhichMightOverlapPutI ( 
-        IRTypeEnv* tyenv, IRStmt* pi, IRStmt* s2 
-     )
-{
-   GSAliasing relation;
-   UInt       minoffP, maxoffP;
+   vassert(unroll_factor == 2 
+           || unroll_factor == 4
+           || unroll_factor == 8);
 
-   vassert(pi->tag == Ist_PutI);
-   getArrayBounds(pi->Ist.PutI.descr, &minoffP, &maxoffP);
-   switch (s2->tag) {
+   jmax = unroll_factor==8 ? 3 : (unroll_factor==4 ? 2 : 1);
+   for (j = 1; j <= jmax; j++) {
 
-      case Ist_Dirty:
-         /* If the dirty call has any guest effects at all, give up.
-            Probably could do better. */
-         if (s2->Ist.Dirty.details->nFxState > 0)
-            return True;
-         return False;
+      n_vars = bb1->tyenv->types_used;
 
-      case Ist_Put:
-         vassert(isAtom(s2->Ist.Put.data));
-         relation 
-            = getAliasingRelation_IC(
-                 pi->Ist.PutI.descr, pi->Ist.PutI.ix,
-                 s2->Ist.Put.offset, 
-                 typeOfIRExpr(tyenv,s2->Ist.Put.data)
-              );
-         goto have_relation;
+      bb2 = dopyIRBB(bb1);
+      for (i = 0; i < n_vars; i++)
+         (void)newIRTemp(bb1->tyenv, bb2->tyenv->types[i]);
 
-      case Ist_PutI:
-         vassert(isAtom(s2->Ist.PutI.ix));
-         vassert(isAtom(s2->Ist.PutI.data));
-         relation
-            = getAliasingRelation_II(
-                 pi->Ist.PutI.descr, pi->Ist.PutI.ix, pi->Ist.PutI.bias, 
-                 s2->Ist.PutI.descr, s2->Ist.PutI.ix, s2->Ist.PutI.bias
-              );
-         goto have_relation;
+      for (i = 0; i < bb2->stmts_used; i++) {
+         if (bb2->stmts[i] == NULL)
+            continue;
+         /* deltaIRStmt destructively modifies the stmt, but 
+            that's OK since bb2 is a complete fresh copy of bb1. */
+         deltaIRStmt(bb2->stmts[i], n_vars);
+         addStmtToIRBB(bb1, bb2->stmts[i]);
+      }
+   }
 
-      case Ist_Tmp:
-         if (s2->Ist.Tmp.data->tag == Iex_GetI) {
-            relation
-               = getAliasingRelation_II(
-                    pi->Ist.PutI.descr, pi->Ist.PutI.ix, 
-                                        pi->Ist.PutI.bias, 
-                    s2->Ist.Tmp.data->Iex.GetI.descr,
-                    s2->Ist.Tmp.data->Iex.GetI.ix,
-                    s2->Ist.Tmp.data->Iex.GetI.bias
-                 );
-            goto have_relation;
-         }
-         if (s2->Ist.Tmp.data->tag == Iex_Get) {
-            relation
-               = getAliasingRelation_IC(
-                    pi->Ist.PutI.descr, pi->Ist.PutI.ix,
-                    s2->Ist.Tmp.data->Iex.Get.offset,
-                    s2->Ist.Tmp.data->Iex.Get.ty
-                 );
-            goto have_relation;
-         }
-         return False;
+   if (DEBUG_IROPT) {
+      vex_printf("\nUNROLLED (%llx)\n", my_addr);
+      ppIRBB(bb1);
+      vex_printf("\n");
+   }
 
-      case Ist_STle:
-         vassert(isAtom(s2->Ist.STle.addr));
-         vassert(isAtom(s2->Ist.STle.data));
-         return False;
+   /* Flattening; sigh.  The unroller succeeds in breaking flatness
+      by negating the test condition.  This should be fixed properly.
+      For the moment use this shotgun approach.  */
+   return flatten_BB(bb1);
+}
 
-      default:
-         vex_printf("\n"); ppIRStmt(s2); vex_printf("\n");
-         vpanic("guestAccessWhichMightOverlapPutI");
+
+/*---------------------------------------------------------------*/
+/*--- The tree builder                                        ---*/
+/*---------------------------------------------------------------*/
+
+/* This isn't part of IR optimisation.  Really it's a pass done prior
+   to instruction selection, which improves the code that the
+   instruction selector can produce. */
+
+typedef
+   struct { 
+      Int     occ;          /* occurrence count for this tmp */
+      IRExpr* expr;         /* expr it is bound to, 
+                               or NULL if already 'used' */
+      Bool    eDoesLoad;    /* True <=> expr reads mem */
+      Bool    eDoesGet;     /* True <=> expr reads guest state */
+      Bool    invalidateMe; /* used when dumping bindings */
+      Int     origPos;      /* posn of the binder in the original bb */
    }
+   TmpInfo;
 
-  have_relation:
-   if (relation == NoAlias)
-      return False;
-   else
-      return True; /* ExactAlias or UnknownAlias */
+/* Given env :: IRTemp -> TmpInfo*
+   Add the use-occurrences of temps in this expression 
+   to the environment. 
+*/
+static void occCount_Temp ( TmpInfo** env, IRTemp tmp )
+{
+   TmpInfo* ti = env[(Int)tmp];
+   if (ti) {
+      ti->occ++;
+   } else {
+      ti               = LibVEX_Alloc(sizeof(TmpInfo));
+      ti->occ          = 1;
+      ti->expr         = NULL;
+      ti->eDoesLoad    = False;
+      ti->eDoesGet     = False;
+      ti->invalidateMe = False;
+      ti->origPos      = -1; /* filed in properly later */
+      env[(Int)tmp] = ti;
+   }
 }
 
+static void occCount_Expr ( TmpInfo** env, IRExpr* e )
+{
+   Int i;
 
+   switch (e->tag) {
 
-/* ---------- PutI/GetI transformations main functions --------- */
+      case Iex_Tmp: /* the only interesting case */
+         occCount_Temp(env, e->Iex.Tmp.tmp);
+         return;
 
-/* Do PutI -> GetI forwarding.  bb is modified in-place. */
+      case Iex_Mux0X:
+         occCount_Expr(env, e->Iex.Mux0X.cond);
+         occCount_Expr(env, e->Iex.Mux0X.expr0);
+         occCount_Expr(env, e->Iex.Mux0X.exprX);
+         return;
 
-static
-void do_PutI_GetI_forwarding_BB ( IRBB* bb )
-{
-   Int     i;
-   IRStmt* st;
+      case Iex_Binop: 
+         occCount_Expr(env, e->Iex.Binop.arg1);
+         occCount_Expr(env, e->Iex.Binop.arg2);
+         return;
 
-   for (i = bb->stmts_used-1; i >= 0; i--) {
-      st = bb->stmts[i];
-      if (!st)
-         continue;
+      case Iex_Unop: 
+         occCount_Expr(env, e->Iex.Unop.arg);
+         return;
 
-      if (st->tag == Ist_Tmp
-          && st->Ist.Tmp.data->tag == Iex_GetI
-          && st->Ist.Tmp.data->Iex.GetI.ix->tag == Iex_Tmp) {
-         IRArray* descr = st->Ist.Tmp.data->Iex.GetI.descr;
-         IRExpr*  ix    = st->Ist.Tmp.data->Iex.GetI.ix;
-         Int      bias  = st->Ist.Tmp.data->Iex.GetI.bias;
-         IRExpr*  replacement = findPutI(bb, i-1, descr, ix, bias);
-         if (replacement 
-             && isAtom(replacement)
-             /* Make sure we're doing a type-safe transformation! */
-             && typeOfIRExpr(bb->tyenv, replacement) == descr->elemTy) {
-            if (DEBUG_IROPT) {
-               vex_printf("PiGi: "); 
-               ppIRExpr(st->Ist.Tmp.data);
-               vex_printf(" -> ");
-               ppIRExpr(replacement);
-               vex_printf("\n");
-            }
-            bb->stmts[i] = IRStmt_Tmp(st->Ist.Tmp.tmp, replacement);
-         }
-      }
-   }
+      case Iex_LDle: 
+         occCount_Expr(env, e->Iex.LDle.addr);
+         return;
 
-}
+      case Iex_CCall:
+         for (i = 0; e->Iex.CCall.args[i]; i++)
+            occCount_Expr(env, e->Iex.CCall.args[i]);
+         return;
 
-/* Remove redundant PutIs, to the extent which they can be detected.
-   bb is modified in-place. */
+      case Iex_GetI:
+         occCount_Expr(env, e->Iex.GetI.ix);
+         return;
 
-static
-void do_redundant_PutI_elimination ( IRBB* bb )
-{
-   Int    i, j;
-   Bool   delete;
-   IRStmt *st, *stj;
+      case Iex_Const:
+      case Iex_Get:
+         return;
 
-   for (i = 0; i < bb->stmts_used; i++) {
-      st = bb->stmts[i];
-      if (!st || st->tag != Ist_PutI)
-         continue;
-      /* Ok, search forwards from here to see if we can find another
-         PutI which makes this one redundant, and dodging various 
-         hazards.  Search forwards:
-         * If conditional exit, give up (because anything after that 
-           does not postdominate this put).
-         * If a Get which might overlap, give up (because this PutI 
-           not necessarily dead).
-         * If a Put which is identical, stop with success.
-         * If a Put which might overlap, but is not identical, give up.
-         * If a dirty helper call which might write guest state, give up.
-         * If a Put which definitely doesn't overlap, or any other 
-           kind of stmt, continue.
-      */
-      delete = False;
-      for (j = i+1; j < bb->stmts_used; j++) {
-         stj = bb->stmts[j];
-         if (!stj) 
-            continue;
-         if (identicalPutIs(st, stj)) {
-            /* success! */
-            delete = True;
-            break;
-         }
-         if (stj->tag == Ist_Exit)
-            /* give up */
-            break;
-         if (st->tag == Ist_Dirty)
-            /* give up; could do better here */
-            break;
-         if (guestAccessWhichMightOverlapPutI(bb->tyenv, st, stj))
-            /* give up */
-           break;
-      }
+      default: 
+         vex_printf("\n"); ppIRExpr(e); vex_printf("\n");
+         vpanic("occCount_Expr");
+    }
+}
 
-      if (delete) {
-         if (DEBUG_IROPT) {
-            vex_printf("rPI:  "); 
-            ppIRStmt(st); 
-            vex_printf("\n");
-         }
-         bb->stmts[i] = NULL;
-      }
 
+/* Given env :: IRTemp -> TmpInfo*
+   Add the use-occurrences of temps in this expression 
+   to the environment. 
+*/
+static void occCount_Stmt ( TmpInfo** env, IRStmt* st )
+{
+   Int      i;
+   IRDirty* d;
+   switch (st->tag) {
+      case Ist_Tmp: 
+         occCount_Expr(env, st->Ist.Tmp.data); 
+         return; 
+      case Ist_Put: 
+         occCount_Expr(env, st->Ist.Put.data);
+         return;
+      case Ist_PutI:
+         occCount_Expr(env, st->Ist.PutI.ix);
+         occCount_Expr(env, st->Ist.PutI.data);
+         return;
+      case Ist_STle: 
+         occCount_Expr(env, st->Ist.STle.addr);
+         occCount_Expr(env, st->Ist.STle.data);
+         return;
+      case Ist_Dirty:
+         d = st->Ist.Dirty.details;
+         if (d->mFx != Ifx_None)
+            occCount_Expr(env, d->mAddr);
+         occCount_Expr(env, d->guard);
+         for (i = 0; d->args[i]; i++)
+            occCount_Expr(env, d->args[i]);
+         return;
+      case Ist_Exit:
+         occCount_Expr(env, st->Ist.Exit.guard);
+         return;
+      default: 
+         vex_printf("\n"); ppIRStmt(st); vex_printf("\n");
+         vpanic("occCount_Stmt");
    }
 }
 
+/* Look up a binding for tmp in the env.  If found, return the bound
+   expression, and set the env's binding to NULL so it is marked as
+   used.  If not found, return NULL. */
 
-/*---------------------------------------------------------------*/
-/*--- Loop unrolling                                          ---*/
-/*---------------------------------------------------------------*/
+static IRExpr* tbSubst_Temp ( TmpInfo** env, IRTemp tmp )
+{
+   TmpInfo* ti;
+   IRExpr*  e;
+   ti = env[(Int)tmp];
+   if (ti){
+      e = ti->expr;
+      if (e) {
+         ti->expr = NULL;
+         return e;
+      } else {
+         return NULL;
+      }
+   } else {
+      return NULL;
+   }
+}
 
-/* Adjust all tmp values (names) in e by delta.  e is destructively
-   modified. */
+/* Traverse e, looking for temps.  For each observed temp, see if env
+   contains a binding for the temp, and if so return the bound value.
+   The env has the property that any binding it holds is
+   'single-shot', so once a binding is used, it is marked as no longer
+   available, by setting its .expr field to NULL. */
 
-static void deltaIRExpr ( IRExpr* e, Int delta )
+static IRExpr* tbSubst_Expr ( TmpInfo** env, IRExpr* e )
 {
-   Int i;
+   IRExpr*  e2;
+   IRExpr** args2;
+   Int      i;
+
    switch (e->tag) {
+
+      case Iex_CCall:
+         args2 = sopyIRExprVec(e->Iex.CCall.args);
+         for (i = 0; args2[i]; i++)
+            args2[i] = tbSubst_Expr(env,args2[i]);
+         return IRExpr_CCall(e->Iex.CCall.cee,
+                   e->Iex.CCall.retty,
+                   args2
+                );
       case Iex_Tmp:
-         e->Iex.Tmp.tmp += delta;
-         break;
-      case Iex_Get:
-      case Iex_Const:
-         break;
-      case Iex_GetI:
-         deltaIRExpr(e->Iex.GetI.ix, delta);
-         break;
-      case Iex_Binop:
-         deltaIRExpr(e->Iex.Binop.arg1, delta);
-         deltaIRExpr(e->Iex.Binop.arg2, delta);
-         break;
+         e2 = tbSubst_Temp(env, e->Iex.Tmp.tmp);
+         return e2 ? e2 : e;
+      case Iex_Mux0X:
+         return IRExpr_Mux0X(
+                   tbSubst_Expr(env, e->Iex.Mux0X.cond),
+                   tbSubst_Expr(env, e->Iex.Mux0X.expr0),
+                   tbSubst_Expr(env, e->Iex.Mux0X.exprX)
+                );
+      case Iex_Binop:
+         return IRExpr_Binop(
+                   e->Iex.Binop.op,
+                   tbSubst_Expr(env, e->Iex.Binop.arg1),
+                   tbSubst_Expr(env, e->Iex.Binop.arg2)
+                );
       case Iex_Unop:
-         deltaIRExpr(e->Iex.Unop.arg, delta);
-         break;
+         return IRExpr_Unop(
+                   e->Iex.Unop.op,
+                   tbSubst_Expr(env, e->Iex.Unop.arg)
+                );
       case Iex_LDle:
-         deltaIRExpr(e->Iex.LDle.addr, delta);
-         break;
-      case Iex_CCall:
-         for (i = 0; e->Iex.CCall.args[i]; i++)
-            deltaIRExpr(e->Iex.CCall.args[i], delta);
-         break;
-      case Iex_Mux0X:
-         deltaIRExpr(e->Iex.Mux0X.cond, delta);
-         deltaIRExpr(e->Iex.Mux0X.expr0, delta);
-         deltaIRExpr(e->Iex.Mux0X.exprX, delta);
-         break;
+         return IRExpr_LDle(
+                   e->Iex.LDle.ty,
+                  tbSubst_Expr(env, e->Iex.LDle.addr)
+                );
+      case Iex_GetI:
+         return IRExpr_GetI(
+                   e->Iex.GetI.descr,
+                   tbSubst_Expr(env, e->Iex.GetI.ix),
+                   e->Iex.GetI.bias
+                );
+      case Iex_Const:
+      case Iex_Get:
+         return e;
       default: 
          vex_printf("\n"); ppIRExpr(e); vex_printf("\n");
-         vpanic("deltaIRExpr");
+         vpanic("tbSubst_Expr");
    }
 }
 
-/* Adjust all tmp values (names) in st by delta.  st is destructively
-   modified. */
+/* Same deal as tbSubst_Expr, except for stmts. */
 
-static void deltaIRStmt ( IRStmt* st, Int delta )
+static IRStmt* tbSubst_Stmt ( TmpInfo** env, IRStmt* st )
 {
    Int      i;
    IRDirty* d;
+   IRDirty* d2;
    switch (st->tag) {
+      case Ist_STle:
+         return IRStmt_STle(
+                   tbSubst_Expr(env, st->Ist.STle.addr),
+                   tbSubst_Expr(env, st->Ist.STle.data)
+                );
+      case Ist_Tmp:
+         return IRStmt_Tmp(
+                   st->Ist.Tmp.tmp,
+                   tbSubst_Expr(env, st->Ist.Tmp.data)
+                );
       case Ist_Put:
-         deltaIRExpr(st->Ist.Put.data, delta);
-         break;
+         return IRStmt_Put(
+                   st->Ist.Put.offset,
+                   tbSubst_Expr(env, st->Ist.Put.data)
+                );
       case Ist_PutI:
-         deltaIRExpr(st->Ist.PutI.ix, delta);
-         deltaIRExpr(st->Ist.PutI.data, delta);
-         break;
-      case Ist_Tmp: 
-         st->Ist.Tmp.tmp += delta;
-         deltaIRExpr(st->Ist.Tmp.data, delta);
-         break;
+         return IRStmt_PutI(
+                   st->Ist.PutI.descr,
+                   tbSubst_Expr(env, st->Ist.PutI.ix),
+                   st->Ist.PutI.bias,
+                   tbSubst_Expr(env, st->Ist.PutI.data)
+                );
+
       case Ist_Exit:
-         deltaIRExpr(st->Ist.Exit.guard, delta);
-         break;
-      case Ist_STle:
-         deltaIRExpr(st->Ist.STle.addr, delta);
-         deltaIRExpr(st->Ist.STle.data, delta);
-         break;
+         return IRStmt_Exit(
+                   tbSubst_Expr(env, st->Ist.Exit.guard),
+                   st->Ist.Exit.jk,
+                   st->Ist.Exit.dst
+                );
       case Ist_Dirty:
          d = st->Ist.Dirty.details;
-         deltaIRExpr(d->guard, delta);
-         for (i = 0; d->args[i]; i++)
-            deltaIRExpr(d->args[i], delta);
-         if (d->tmp != IRTemp_INVALID)
-            d->tmp += delta;
-         if (d->mAddr)
-            deltaIRExpr(d->mAddr, delta);
-         break;
+         d2 = emptyIRDirty();
+         *d2 = *d;
+         if (d2->mFx != Ifx_None)
+            d2->mAddr = tbSubst_Expr(env, d2->mAddr);
+         d2->guard = tbSubst_Expr(env, d2->guard);
+         for (i = 0; d2->args[i]; i++)
+            d2->args[i] = tbSubst_Expr(env, d2->args[i]);
+         return IRStmt_Dirty(d2);
       default: 
          vex_printf("\n"); ppIRStmt(st); vex_printf("\n");
-         vpanic("deltaIRStmt");
+         vpanic("tbSubst_Stmt");
    }
 }
 
 
-/* If possible, return a loop-unrolled version of bb0.  The original
-   is changed.  If not possible, return NULL.  */
-
-/* The two schemas considered are:
-
-     X: BODY; goto X
-
-     which unrolls to (eg)  X: BODY;BODY; goto X
-
-   and
-
-       X: BODY; if (c) goto X; goto Y
-   which trivially transforms to
-       X: BODY; if (!c) goto Y; goto X;
-   so it falls in the scope of the first case.  
-
-   X and Y must be literal (guest) addresses.
+/* Traverse an expr, and detect if any part of it reads memory or does
+   a Get.  Be careful ... this really controls how much the
+   tree-builder can reorder the code, so getting it right is critical.
 */
-
-static Int calc_unroll_factor( IRBB* bb )
+static void setHints_Expr (Bool* doesLoad, Bool* doesGet, IRExpr* e )
 {
-   Int n_stmts, i;
-
-   n_stmts = 0;
-   for (i = 0; i < bb->stmts_used; i++)
-     if (bb->stmts[i])
-        n_stmts++;
-
-   if (n_stmts <= vex_control.iropt_unroll_thresh/8) {
-      if (vex_control.iropt_verbosity > 0)
-         vex_printf("vex iropt: 8 x unrolling (%d sts -> %d sts)\n",
-                    n_stmts, 8* n_stmts);
-      return 8;
-   }
-   if (n_stmts <= vex_control.iropt_unroll_thresh/4) {
-      if (vex_control.iropt_verbosity > 0)
-         vex_printf("vex iropt: 4 x unrolling (%d sts -> %d sts)\n",
-                    n_stmts, 4* n_stmts);
-      return 4;
-   }
-
-   if (n_stmts <= vex_control.iropt_unroll_thresh/2) {
-      if (vex_control.iropt_verbosity > 0)
-         vex_printf("vex iropt: 2 x unrolling (%d sts -> %d sts)\n",
-                    n_stmts, 2* n_stmts);
-      return 2;
+   Int i;
+   switch (e->tag) {
+      case Iex_CCall:
+         for (i = 0; e->Iex.CCall.args[i]; i++)
+            setHints_Expr(doesLoad, doesGet, e->Iex.CCall.args[i]);
+         return;
+      case Iex_Mux0X:
+         setHints_Expr(doesLoad, doesGet, e->Iex.Mux0X.cond);
+         setHints_Expr(doesLoad, doesGet, e->Iex.Mux0X.expr0);
+         setHints_Expr(doesLoad, doesGet, e->Iex.Mux0X.exprX);
+         return;
+      case Iex_Binop:
+         setHints_Expr(doesLoad, doesGet, e->Iex.Binop.arg1);
+         setHints_Expr(doesLoad, doesGet, e->Iex.Binop.arg2);
+         return;
+      case Iex_Unop:
+         setHints_Expr(doesLoad, doesGet, e->Iex.Unop.arg);
+         return;
+      case Iex_LDle:
+         *doesLoad = True;
+         setHints_Expr(doesLoad, doesGet, e->Iex.LDle.addr);
+         return;
+      case Iex_Get:
+         *doesGet = True;
+         return;
+      case Iex_GetI:
+         *doesGet = True;
+         setHints_Expr(doesLoad, doesGet, e->Iex.GetI.ix);
+         return;
+      case Iex_Tmp:
+      case Iex_Const:
+         return;
+      default: 
+         vex_printf("\n"); ppIRExpr(e); vex_printf("\n");
+         vpanic("setHints_Expr");
    }
-
-   if (vex_control.iropt_verbosity > 0)
-      vex_printf("vex iropt: not unrolling (%d sts)\n", n_stmts);
-
-   return 1;
 }
 
 
-static IRBB* maybe_loop_unroll_BB ( IRBB* bb0, Addr64 my_addr )
+static void dumpInvalidated ( TmpInfo** env, IRBB* bb, /*INOUT*/Int* j )
 {
-   Int      i, j, jmax, n_vars;
-   Bool     xxx_known;
-   Addr64   xxx_value, yyy_value;
-   IRExpr*  udst;
-   IRStmt*  st;
-   IRConst* con;
-   IRBB     *bb1, *bb2;
-   Int      unroll_factor;
-
-   if (vex_control.iropt_unroll_thresh <= 0)
-      return NULL;
-
-   /* First off, figure out if we can unroll this loop.  Do this
-      without modifying bb0. */
-
-   if (bb0->jumpkind != Ijk_Boring)
-      return NULL;
-
-   xxx_known = False;
-   xxx_value = 0;
-
-   /* Extract the next-guest address.  If it isn't a literal, we 
-      have to give up. */
-
-   udst = bb0->next;
-   if (udst->tag == Iex_Const
-       && (udst->Iex.Const.con->tag == Ico_U32
-           || udst->Iex.Const.con->tag == Ico_U64)) {
-      /* The BB ends in a jump to a literal location. */
-      xxx_known = True;
-      xxx_value = udst->Iex.Const.con->tag == Ico_U64
-                    ?  udst->Iex.Const.con->Ico.U64
-                    : (Addr64)(udst->Iex.Const.con->Ico.U32);
-   }
-
-   if (!xxx_known)
-      return NULL;
-
-   /* Now we know the BB ends to a jump to a literal location.  If
-      it's a jump to itself (viz, idiom #1), move directly to the
-      unrolling stage, first cloning the bb so the original isn't
-      modified. */
-   if (xxx_value == my_addr) {
-      unroll_factor = calc_unroll_factor( bb0 );
-      if (unroll_factor < 2)
-         return NULL;
-      bb1 = dopyIRBB( bb0 );
-      bb0 = NULL;
-      udst = NULL; /* is now invalid */
-      goto do_unroll;
-   }
-
-   /* Search for the second idiomatic form:
-        X: BODY; if (c) goto X; goto Y
-      We know Y, but need to establish that the last stmt
-      is 'if (c) goto X'.
-   */
-   yyy_value = xxx_value;
-   for (i = bb0->stmts_used-1; i >= 0; i--)
-      if (bb0->stmts[i])
-         break;
+   Int k, oldest_op, oldest_k;
+   TmpInfo* ti;
 
-   if (i < 0)
-      return NULL; /* block with no stmts.  Strange. */
+   /* Dump all the bindings to marked as invalidated, in order. */
+   while (True) {
+  
+      /* find the oldest bind marked 'invalidateMe'. */
+      oldest_op = 1<<30;
+      oldest_k =  1<<30;
+      for (k = 0; k < bb->tyenv->types_used; k++) {
+         ti = env[k];
+         if (!ti)
+            continue;
+         if (!ti->expr)
+            continue;
+         if (!ti->invalidateMe)
+           continue;
+         /* vex_printf("FOUND INVAL %d %d\n", ti->origPos, oldest_op); */
+         if (ti->origPos < oldest_op) {
+            oldest_op = ti->origPos;
+            oldest_k = k;
+         }
+      }
 
-   st = bb0->stmts[i];
-   if (st->tag != Ist_Exit)
-      return NULL;
-   if (st->Ist.Exit.jk != Ijk_Boring)
-      return NULL;
+      /* No more binds to invalidate. */
+      if (oldest_op == 1<<30)
+        return;
 
-   con = st->Ist.Exit.dst;
-   vassert(con->tag == Ico_U32 || con->tag == Ico_U64);
+      /* the oldest bind to invalidate has been identified */
+      vassert(oldest_op != 1<<31 && oldest_k != 1<<31);
+      ti = env[oldest_k];
+      vassert(ti->expr && ti->invalidateMe);
 
-   xxx_value = con->tag == Ico_U64 
-                  ? st->Ist.Exit.dst->Ico.U64
-                  : (Addr64)(st->Ist.Exit.dst->Ico.U32);
+      /* and invalidate it ... */
+      bb->stmts[*j] = IRStmt_Tmp( (IRTemp)oldest_k, ti->expr );
+      /*  vex_printf("**1  "); ppIRStmt(bb->stmts[*j]); vex_printf("\n"); */
+      (*j)++;
+      ti->invalidateMe = False;
+      ti->expr = NULL; /* no longer available for substitution */
 
-   /* If this assertion fails, we have some kind of type error. */
-   vassert(con->tag == udst->Iex.Const.con->tag);
+   } /* loop which dumps the binds marked for invalidation */
+}
 
-   if (xxx_value != my_addr)
-      /* We didn't find either idiom.  Give up. */
-      return NULL;
 
-   /* Ok, we found idiom #2.  Copy the BB, switch around the xxx and
-      yyy values (which makes it look like idiom #1), and go into
-      unrolling proper.  This means finding (again) the last stmt, in
-      the copied BB. */
 
-   unroll_factor = calc_unroll_factor( bb0 );
-   if (unroll_factor < 2)
-      return NULL;
+/* notstatic */ void do_treebuild_BB ( IRBB* bb )
+{
+   Int      i, j, k;
+   Bool     invPut, invStore;
+   IRStmt*  st;
+   IRStmt*  st2;
+   TmpInfo* ti;
+   IRExpr*  next2;
 
-   bb1 = dopyIRBB( bb0 );
-   bb0 = NULL;
-   udst = NULL; /* is now invalid */
-   for (i = bb1->stmts_used-1; i >= 0; i--)
-      if (bb1->stmts[i])
-         break;
+   /* Mapping from IRTemp to TmpInfo*. */
+   Int       n_tmps = bb->tyenv->types_used;
+   TmpInfo** env    = LibVEX_Alloc(n_tmps * sizeof(TmpInfo*));
 
-   /* The next bunch of assertions should be true since we already
-      found and checked the last stmt in the original bb. */
+   for (i = 0; i < n_tmps; i++)
+      env[i] = NULL;
 
-   vassert(i >= 0);
+   /* Phase 1.  Scan forwards in bb, counting use occurrences of each
+      temp.  Also count occurrences in the bb->next field. */
 
-   st = bb1->stmts[i];
-   vassert(st->tag == Ist_Exit);
+   for (i = 0; i < bb->stmts_used; i++) {
+      st = bb->stmts[i];
+      if (!st)
+         continue;
+      occCount_Stmt( env, st );
+   }
+   occCount_Expr(env, bb->next );
 
-   con = st->Ist.Exit.dst;
-   vassert(con->tag == Ico_U32 || con->tag == Ico_U64);
+#  if 0
+   for (i = 0; i < env->used; i++) {
+      if (!env->inuse[i])
+        continue;
+      ppIRTemp( (IRTemp)(env->key[i]) );
+      vex_printf("  used %d\n", ((TmpInfo*)env->val[i])->occ );
+   }
+#  endif
 
-   udst = bb1->next;
-   vassert(udst->tag == Iex_Const);
-   vassert(udst->Iex.Const.con->tag == Ico_U32
-          || udst->Iex.Const.con->tag == Ico_U64);
-   vassert(con->tag == udst->Iex.Const.con->tag);
+   /* Phase 2.  Fill in the origPos fields. */
 
-   /* switch the xxx and yyy fields around */
-   if (con->tag == Ico_U64) {
-      udst->Iex.Const.con->Ico.U64 = xxx_value;
-      con->Ico.U64 = yyy_value;
-   } else {
-      udst->Iex.Const.con->Ico.U32 = (UInt)xxx_value;
-      con->Ico.U64 = (UInt)yyy_value;
+   for (i = 0; i < bb->stmts_used; i++) {
+      st = bb->stmts[i];
+      if (!st)
+         continue;
+      if (st->tag != Ist_Tmp)
+         continue;
+
+      ti = env[(Int)st->Ist.Tmp.tmp];
+      if (!ti) {
+         vex_printf("\n");
+         ppIRTemp(st->Ist.Tmp.tmp);
+         vex_printf("\n");
+         vpanic("treebuild_BB (phase 2): unmapped IRTemp");
+      }
+      ti->origPos = i;
    }
 
-   /* negate the test condition */
-   st->Ist.Exit.guard 
-      = IRExpr_Unop(Iop_Not1,dopyIRExpr(st->Ist.Exit.guard));
+   /* Phase 3.  Scan forwards in bb.  
 
-   /* --- The unroller proper.  Both idioms are by now --- */
-   /* --- now converted to idiom 1. --- */
+      On seeing 't = E', occ(t)==1,  
+            let E'=env(E), set t's binding to be E', and
+            delete this stmt.
+            Also examine E' and set the hints for E' appropriately
+              (doesLoad? doesGet?)
 
-  do_unroll:
+      On seeing any other stmt, 
+            let stmt' = env(stmt)
+            remove from env any 't=E' binds invalidated by stmt
+                emit the invalidated stmts
+            emit stmt'
 
-   vassert(unroll_factor == 2 
-           || unroll_factor == 4
-           || unroll_factor == 8);
+      Apply env to bb->next.
+   */
 
-   jmax = unroll_factor==8 ? 3 : (unroll_factor==4 ? 2 : 1);
-   for (j = 1; j <= jmax; j++) {
+   /* The stmts in bb are being reordered, and we are guaranteed to
+      end up with no more than the number we started with.  Use i to
+      be the cursor of the current stmt examined and j <= i to be that
+      for the current stmt being written. 
+   */
+   j = 0;
+   for (i = 0; i < bb->stmts_used; i++) {
+      st = bb->stmts[i];
+      if (!st)
+         continue;
+     
+      if (st->tag == Ist_Tmp) {
+         /* vex_printf("acquire binding\n"); */
+         ti = env[st->Ist.Tmp.tmp];
+         if (!ti) {
+            vpanic("treebuild_BB (phase 2): unmapped IRTemp");
+         }
+         if (ti->occ == 1) {
+            /* ok, we have 't = E', occ(t)==1.  Do the abovementioned actions. */
+            IRExpr* e = st->Ist.Tmp.data;
+            IRExpr* e2 = tbSubst_Expr(env, e);
+            ti->expr = e2;
+            ti->eDoesLoad = ti->eDoesGet = False;
+            setHints_Expr(&ti->eDoesLoad, &ti->eDoesGet, e2);
+            /* don't advance j, as we are deleting this stmt and instead
+               holding it temporarily in the env. */
+            continue; /* the for (i = 0; i < bb->stmts_used; i++) loop */
+         }
+      }
 
-      n_vars = bb1->tyenv->types_used;
+      /* we get here for any other kind of statement. */
+      /* 'use up' any bindings required by the current statement. */
+      st2 = tbSubst_Stmt(env, st);
 
-      bb2 = dopyIRBB(bb1);
-      for (i = 0; i < n_vars; i++)
-         (void)newIRTemp(bb1->tyenv, bb2->tyenv->types[i]);
+      /* Now, before this stmt, dump any bindings it invalidates.
+         These need to be dumped in the order in which they originally
+         appeared.  (Stupid algorithm): first, mark all bindings which
+         need to be dumped.  Then, dump them in the order in which
+         they were defined. */
+      invPut = st->tag == Ist_Put 
+               || st->tag == Ist_PutI || st->tag == Ist_Dirty;
+      invStore = st->tag == Ist_STle
+               || st->tag == Ist_Dirty;
 
-      for (i = 0; i < bb2->stmts_used; i++) {
-         if (bb2->stmts[i] == NULL)
+      for (k = 0; k < n_tmps; k++) {
+         ti = env[k];
+         if (!ti)
             continue;
-         /* deltaIRStmt destructively modifies the stmt, but 
-            that's OK since bb2 is a complete fresh copy of bb1. */
-         deltaIRStmt(bb2->stmts[i], n_vars);
-         addStmtToIRBB(bb1, bb2->stmts[i]);
+         if (!ti->expr)
+            continue;
+
+         /* We have to invalidate this binding. */
+         ti->invalidateMe 
+            = /* a store invalidates loaded data */
+              (ti->eDoesLoad && invStore)
+              /* a put invalidates get'd data */
+              || (ti->eDoesGet && invPut)
+              /* a put invalidates loaded data.  Note, we could do
+                 much better here in the sense that we only need to
+                 invalidate trees containing loads if the Put in
+                 question is marked as requiring precise
+                 exceptions. */
+              || (ti->eDoesLoad && invPut);
+         /*
+         if (ti->invalidateMe)
+           vex_printf("SET INVAL\n");
+           */
       }
-   }
 
-   if (DEBUG_IROPT) {
-      vex_printf("\nUNROLLED (%llx)\n", my_addr);
-      ppIRBB(bb1);
-      vex_printf("\n");
-   }
+      dumpInvalidated ( env, bb, &j );
 
-   /* Flattening; sigh.  The unroller succeeds in breaking flatness
-      by negating the test condition.  This should be fixed properly.
-      For the moment use this shotgun approach.  */
-   return flatten_BB(bb1);
+      /* finally, emit the substituted statement */
+      bb->stmts[j] = st2;
+      /*  vex_printf("**2  "); ppIRStmt(bb->stmts[j]); vex_printf("\n"); */
+      j++;
+
+      vassert(j <= i+1);
+   } /* for each stmt in the original bb ... */
+
+   /* Finally ... substitute the ->next field as much as possible, and
+      dump any left-over bindings.  Hmm.  Perhaps there should be no
+      left over bindings?  Or any left-over bindings are
+      by definition dead? */
+   next2 = tbSubst_Expr(env, bb->next);
+   bb->next = next2;
+   bb->stmts_used = j;
 }
 
+
 /*---------------------------------------------------------------*/
 /*--- iropt main                                              ---*/
 /*---------------------------------------------------------------*/
@@ -3298,7 +3383,7 @@ IRBB* expensive_transformations( IRBB* bb )
 {
    do_cse_BB( bb );
    collapse_AddSub_chains_BB( bb );
-   do_PutI_GetI_forwarding_BB( bb );
+   do_redundant_GetI_elimination( bb );
    do_redundant_PutI_elimination( bb );
    do_deadcode_BB( bb );
    return flatten_BB( bb );
@@ -3380,9 +3465,6 @@ IRBB* do_iropt_BB ( IRBB* bb0,
    Bool do_expensive;
    IRBB *bb, *bb2;
 
-   /* Completely disable iropt? */
-   if (vex_control.iropt_level <= 0) return bb0;
-
    n_total++;
 
    /* First flatten the block out, since all other
@@ -3395,6 +3477,9 @@ IRBB* do_iropt_BB ( IRBB* bb0,
       ppIRBB(bb);
    }
 
+   /* If at level 0, stop now. */
+   if (vex_control.iropt_level <= 0) return bb;
+
    /* Now do a preliminary cleanup pass, and figure out if we also
       need to do 'expensive' optimisations.  Expensive optimisations
       are deemed necessary if the block contains any GetIs or PutIs.