]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add a simple common-subexpression removal pass to iropt. Potentially
authorJulian Seward <jseward@acm.org>
Wed, 22 Sep 2004 19:49:27 +0000 (19:49 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 22 Sep 2004 19:49:27 +0000 (19:49 +0000)
important for optimising code making heavy use of GetI/PutI.

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

VEX/priv/host-x86/isel.c
VEX/priv/ir/irdefs.c
VEX/priv/ir/iropt.c
VEX/priv/main/vex_util.c
VEX/pub/libvex_ir.h

index ad7ac90ee718ec3224123c6782cc6137e0c76567..a53b0b722b7c719ea31b87e86e1eb242dfb9f6ce 100644 (file)
@@ -87,35 +87,8 @@ static Bool matchWrk ( MatchInfo* mi, IRExpr* p/*attern*/, IRExpr* e/*xpr*/ )
             return False;
          return True;
       case Iex_Const:
-       if (e->tag != Iex_Const) return False;
-       switch (p->Iex.Const.con->tag) {
-           case Ico_Bit: return e->Iex.Const.con->tag==Ico_Bit 
-                                   ? (p->Iex.Const.con->Ico.Bit 
-                                      == e->Iex.Const.con->Ico.Bit) 
-                                   : False;
-           case Ico_U8:  return e->Iex.Const.con->tag==Ico_U8 
-                                   ? (p->Iex.Const.con->Ico.U8 
-                                      == e->Iex.Const.con->Ico.U8) 
-                                   : False;
-           case Ico_U16: return e->Iex.Const.con->tag==Ico_U16 
-                                   ? (p->Iex.Const.con->Ico.U16 
-                                      == e->Iex.Const.con->Ico.U16) 
-                                   : False;
-           case Ico_U32: return e->Iex.Const.con->tag==Ico_U32 
-                                   ? (p->Iex.Const.con->Ico.U32 
-                                      == e->Iex.Const.con->Ico.U32) 
-                                   : False;
-           case Ico_U64: return e->Iex.Const.con->tag==Ico_U64 
-                                   ? (p->Iex.Const.con->Ico.U64 
-                                      == e->Iex.Const.con->Ico.U64) 
-                                   : False;
-           case Ico_F64: return e->Iex.Const.con->tag==Ico_F64 
-                                   ? (p->Iex.Const.con->Ico.F64 
-                                      == e->Iex.Const.con->Ico.F64) 
-                                   : False;
-       }
-        vpanic("matchIRExpr.Iex_Const");
-        /*NOTREACHED*/
+         if (e->tag != Iex_Const) return False;
+         return eqIRConst(p->Iex.Const.con, e->Iex.Const.con);
       default: 
          ppIRExpr(p);
          vpanic("match");
index e013ea02d8f2484ee9aa800f2b381cbd6382489f..323455cfe99acd438ff7655614a1345e76553dbd 100644 (file)
@@ -1227,6 +1227,26 @@ void sanityCheckIRBB ( IRBB* bb, IRType guest_word_size )
       sanityCheckFail(bb, NULL, "bb->next field has wrong type");
 }
 
+/*---------------------------------------------------------------*/
+/*--- Misc helper functions                                   ---*/
+/*---------------------------------------------------------------*/
+
+Bool eqIRConst ( IRConst* c1, IRConst* c2 )
+{
+   if (c1->tag != c2->tag)
+      return False;
+
+   switch (c1->tag) {
+      case Ico_Bit: return (1 & c1->Ico.Bit) == (1 & c2->Ico.Bit);
+      case Ico_U8:  return c1->Ico.U8  == c2->Ico.U8;
+      case Ico_U16: return c1->Ico.U16 == c2->Ico.U16;
+      case Ico_U32: return c1->Ico.U32 == c2->Ico.U32;
+      case Ico_U64: return c1->Ico.U64 == c2->Ico.U64;
+      case Ico_F64: return c1->Ico.F64 == c2->Ico.F64;
+      default: vpanic("eqIRConst");
+   }
+}
+
 
 /*---------------------------------------------------------------*/
 /*--- end                                         ir/irdefs.c ---*/
index ad3db6cb0fffc3043ed9a7b82cd4a4c956d8d9ea..f4fd3f4cd515d72e0ef6ed4bb089404fac25c2e9 100644 (file)
@@ -329,7 +329,8 @@ static IRBB* flatten_BB ( IRBB* in )
    out = emptyIRBB();
    out->tyenv = copyIRTypeEnv( in->tyenv );
    for (i = 0; i < in->stmts_used; i++)
-      flatten_Stmt( out, in->stmts[i] );
+      if (in->stmts[i])
+         flatten_Stmt( out, in->stmts[i] );
    out->next     = flatten_Expr( out, in->next );
    out->jumpkind = in->jumpkind;
    return out;
@@ -375,6 +376,10 @@ static IRExpr* fold_Expr ( IRExpr* e )
             e2 = IRExpr_Const(IRConst_U32(
                     0xFFFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U16));
             break;
+         case Iop_32to8:
+            e2 = IRExpr_Const(IRConst_U8(
+                    0xFF & e->Iex.Unop.arg->Iex.Const.con->Ico.U32));
+            break;
          case Iop_Not32:
             e2 = IRExpr_Const(IRConst_U32(
                     ~ (e->Iex.Unop.arg->Iex.Const.con->Ico.U32)));
@@ -400,6 +405,11 @@ static IRExpr* fold_Expr ( IRExpr* e )
                        (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
@@ -1752,10 +1762,226 @@ static void treebuild_BB ( IRBB* bb )
 
 
 
+/*---------------------------------------------------------------*/
+/*--- Common Subexpression Elimination                        ---*/
+/*---------------------------------------------------------------*/
+
+/* Expensive in time and space. */
+
+/* Uses two environments: 
+   a IRTemp -> IRTemp mapping 
+   a mapping from AvailExpr* to IRTemp 
+*/
+
+typedef
+   struct {
+      enum { Ut, Btt, Btc } tag;
+      union {
+         /* unop(tmp) */
+         struct {
+            IROp op;
+            IRTemp arg;
+         } Ut;
+         struct {
+            IROp op;
+            IRTemp arg1;
+            IRTemp arg2;
+         } Btt;
+         struct {
+            IROp op;
+            IRTemp arg1;
+            IRConst con2;
+         } Btc;
+      } u;
+   }
+   AvailExpr;
+
+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);
+      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) );
+      default:
+         vpanic("availExpr_to_IRExpr");
+   }
+}
+
+inline
+static IRTemp subst_AvailExpr_Temp ( Hash64* env, IRTemp tmp )
+{
+   ULong res;
+   /* env :: IRTemp -> IRTemp */
+   if (lookupH64( env, &res, (ULong)tmp ))
+      return (IRTemp)res;
+   else
+      return tmp;
+}
+
+static void subst_AvailExpr ( Hash64* 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;
+      default: 
+         vpanic("subst_AvailExpr");
+   }
+}
+
+static AvailExpr* irExpr_to_AvailExpr ( IRExpr* e )
+{
+   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;
+   }
+
+   return NULL;
+}
+
+
+/* The BB is modified in-place. */
+
+static void cse_BB ( IRBB* bb )
+{
+   Int        i, j;
+   IRTemp     t, q;
+   IRStmt*    st;
+   AvailExpr* eprime;
+
+   Hash64* tenv = newH64(); /* :: IRTemp -> IRTemp */
+   Hash64* aenv = newH64(); /* :: AvailExpr -> IRTemp */
+
+   //ppIRBB(bb);
+   //vex_printf("\n\n");
+
+   /* 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.expr);
+      /* 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;
+
+      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) );
+         addToH64( tenv, (ULong)t, (ULong)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) );
+         addToH64( aenv, (ULong)eprime, (ULong)t );
+      }
+   }
+
+   //ppIRBB(bb);
+   //sanityCheckIRBB(bb, Ity_I32);
+   //vex_printf("\n\n");
+      
+}
+
+
 /*---------------------------------------------------------------*/
 /*--- iropt main                                              ---*/
 /*---------------------------------------------------------------*/
 
+static Bool iropt_verbose = False;
+
+
 /* Rules of the game:
 
    - IRExpr/IRStmt trees should be treated as immutable, as they
@@ -1763,58 +1989,165 @@ static void treebuild_BB ( IRBB* bb )
      instead construct and return a new one if needed.
 */
 
-/* exported from this file */
-IRBB* do_iropt_BB ( IRBB* bb0,
-                    IRExpr* (*specHelper) ( Char*, IRExpr**) )
-{
-   Bool verbose = False;
-   IRBB *flat, *cpd;
-
-   flat = flatten_BB ( bb0 );
-   if (verbose) {
-      vex_printf("\n************************ FLAT\n\n" );
-      ppIRBB(flat);
-   }
+/* Do a simple cleanup pass on bb.  This is: redundant Get removal,
+   redundant Put removal, constant propagation, dead code removal,
+   clean helper specialisation, and dead code removal (again).
+*/
 
-   redundant_get_removal_BB ( flat );
-   if (verbose) {
+static 
+IRBB* baseline_cleanup ( IRBB* bb,
+                         IRExpr* (*specHelper) ( Char*, IRExpr**) )
+{
+   redundant_get_removal_BB ( bb );
+   if (iropt_verbose) {
       vex_printf("\n========= REDUNDANT GET\n\n" );
-      ppIRBB(flat);
+      ppIRBB(bb);
    }
 
-   redundant_put_removal_BB ( flat );
-   if (verbose) {
+   redundant_put_removal_BB ( bb );
+   if (iropt_verbose) {
       vex_printf("\n========= REDUNDANT PUT\n\n" );
-      ppIRBB(flat);
+      ppIRBB(bb);
    }
 
-   cpd = cprop_BB ( flat );
-   if (verbose) {
+   bb = cprop_BB ( bb );
+   if (iropt_verbose) {
       vex_printf("\n========= CPROPD\n\n" );
-      ppIRBB(cpd);
+      ppIRBB(bb);
    }
 
-   dead_BB ( cpd );
-   if (verbose) {
+   dead_BB ( bb );
+   if (iropt_verbose) {
       vex_printf("\n========= DEAD\n\n" );
-      ppIRBB(cpd);
+      ppIRBB(bb);
    }
 
-   spec_helpers_BB ( cpd, specHelper );
-   dead_BB ( cpd );
-   if (verbose) {
+   spec_helpers_BB ( bb, specHelper );
+   dead_BB ( bb );
+   if (iropt_verbose) {
       vex_printf("\n========= SPECd \n\n" );
-      ppIRBB(cpd);
+      ppIRBB(bb);
    }
 
-   treebuild_BB ( cpd );
-   if (verbose) {
-      vex_printf("\n========= TREEd \n\n" );
-      ppIRBB(cpd);
+   return bb;
+}
+
+/* Scan a flattened BB to see if it has any GetI or PutIs in it.  Used
+   as a heuristic hack to see if iropt needs to do expensive
+   optimisations (CSE, PutI -> GetI forwarding) to improve code with
+   those in. 
+*/
+static Bool hasGetIorPutI ( IRBB* bb )
+{
+   Int i, j;
+   IRStmt* st;
+   IRDirty* d;
+
+   for (i = 0; i < bb->stmts_used; i++) {
+      st = bb->stmts[i];
+      if (!st)
+         continue;
+
+      switch (st->tag) {
+         case Ist_PutI: 
+            return True;
+         case Ist_Tmp:  
+            if (st->Ist.Tmp.expr->tag == Iex_GetI)
+               return True;
+            break;
+         case Ist_Put:
+            vassert(isAtom(st->Ist.Put.expr));
+            break;
+         case Ist_STle:
+            vassert(isAtom(st->Ist.STle.addr));
+            vassert(isAtom(st->Ist.STle.data));
+            break;
+         case Ist_Exit:
+            vassert(isAtom(st->Ist.Exit.cond));
+            break;
+         case Ist_Dirty:
+            d = st->Ist.Dirty.details;
+            for (j = 0; d->args[j]; j++)
+               vassert(isAtom(d->args[j]));
+            if (d->mFx != Ifx_None)
+               vassert(isAtom(d->mAddr));
+            break;
+         default: 
+            ppIRStmt(st);
+            vpanic("hasGetIorPutI");
+      }
+
+   }
+   return False;
+
+}
+
+
+/* exported from this file */
+/* The main iropt entry point. */
+
+IRBB* do_iropt_BB ( IRBB* bb0,
+                    IRExpr* (*specHelper) ( Char*, IRExpr**) )
+{
+   static UInt n_total     = 0;
+   static UInt n_expensive = 0;
+
+   Bool show_res = False;
+
+   IRBB *bb;
+   n_total++;
+
+   /* First flatten the block out, since all other
+      phases assume flat code. */
+
+   bb = flatten_BB ( bb0 );
+
+   if (iropt_verbose) {
+      vex_printf("\n========= FLAT\n\n" );
+      ppIRBB(bb);
+   }
+
+   /* Now do a preliminary cleanup pass. */
+
+   bb = baseline_cleanup( bb, specHelper );
+
+   /* If there are GetI/PutI in this block, do some expensive
+      transformations:
+
+    - CSE 
+    - re-run of the baseline cleanup
+
+   */
+
+   if (hasGetIorPutI(bb)) {
+      n_expensive++;
+      vex_printf("***** EXPENSIVE %d %d\n", n_total, n_expensive);
+      //ppIRBB(bb); vex_printf("\n\n");
+      cse_BB( bb );
+      /*
+      ppIRBB(bb); vex_printf("\n\n");
+      dead_BB( bb );
+      bb = cprop_BB ( bb );
+      dead_BB(bb);
+      */
+      //ppIRBB(bb); vex_printf("\n\nQQQQ\n");
+      bb = baseline_cleanup( flatten_BB(bb), specHelper );
+      //vassert(0);
+      //      vex_printf("expensive done\n");
+      //show_res = True;
    }
 
-   return cpd;
+   /* Finally, rebuild trees, for the benefit of instruction
+      selection. */
+
+   treebuild_BB ( bb );
+   if (show_res || iropt_verbose) {
+      vex_printf("\n========= TREEd \n\n" );
+      ppIRBB(bb);
+   }
 
+   return bb;
 }
 
 
index 56ec372860b43c4044586582bc4a2ecc016c2590..b154cd451bfe94d42476bfa87ecc6112d320bf6a 100644 (file)
@@ -24,7 +24,7 @@
    MByte/sec.  Once the size increases enough to fall out of the cache
    into memory, the rate falls by about a factor of 3. 
 */
-#define N_TEMPORARY_BYTES 400000
+#define N_TEMPORARY_BYTES 1000000
 
 static Char temporary[N_TEMPORARY_BYTES];
 static Int  temporary_used = 0;
index a6bfa2869bf29ed00504e29d17373d12395e331d..59a815328cad8a066d92bb93778a853967747a64 100644 (file)
@@ -64,6 +64,7 @@ extern IRConst* IRConst_F64  ( Double );
 extern IRConst* IRConst_F64i ( ULong );
 
 extern void ppIRConst ( IRConst* );
+extern Bool eqIRConst ( IRConst*, IRConst* );
 
 
 /* ------------------ Temporaries ------------------ */