From: Julian Seward Date: Wed, 22 Sep 2004 19:49:27 +0000 (+0000) Subject: Add a simple common-subexpression removal pass to iropt. Potentially X-Git-Tag: svn/VALGRIND_3_0_1^2~1051 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=abc1c2f3541b3eab8afc5336de968d144b1fdf35;p=thirdparty%2Fvalgrind.git Add a simple common-subexpression removal pass to iropt. Potentially important for optimising code making heavy use of GetI/PutI. git-svn-id: svn://svn.valgrind.org/vex/trunk@283 --- diff --git a/VEX/priv/host-x86/isel.c b/VEX/priv/host-x86/isel.c index ad7ac90ee7..a53b0b722b 100644 --- a/VEX/priv/host-x86/isel.c +++ b/VEX/priv/host-x86/isel.c @@ -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"); diff --git a/VEX/priv/ir/irdefs.c b/VEX/priv/ir/irdefs.c index e013ea02d8..323455cfe9 100644 --- a/VEX/priv/ir/irdefs.c +++ b/VEX/priv/ir/irdefs.c @@ -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 ---*/ diff --git a/VEX/priv/ir/iropt.c b/VEX/priv/ir/iropt.c index ad3db6cb0f..f4fd3f4cd5 100644 --- a/VEX/priv/ir/iropt.c +++ b/VEX/priv/ir/iropt.c @@ -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; } diff --git a/VEX/priv/main/vex_util.c b/VEX/priv/main/vex_util.c index 56ec372860..b154cd451b 100644 --- a/VEX/priv/main/vex_util.c +++ b/VEX/priv/main/vex_util.c @@ -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; diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index a6bfa2869b..59a815328c 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -64,6 +64,7 @@ extern IRConst* IRConst_F64 ( Double ); extern IRConst* IRConst_F64i ( ULong ); extern void ppIRConst ( IRConst* ); +extern Bool eqIRConst ( IRConst*, IRConst* ); /* ------------------ Temporaries ------------------ */