From: Julian Seward Date: Sun, 25 Nov 2012 15:14:44 +0000 (+0000) Subject: Add IR level definitions and associated iropt hackery, to support X-Git-Tag: svn/VALGRIND_3_9_0^2~152^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=33f382b26f25aa328f4af1864d89ad1d299db8d4;p=thirdparty%2Fvalgrind.git Add IR level definitions and associated iropt hackery, to support direct representation of conditional (guarded) loads and stores in IR. git-svn-id: svn://svn.valgrind.org/vex/branches/COMEM@2570 --- diff --git a/VEX/priv/ir_defs.c b/VEX/priv/ir_defs.c index b356f60d1f..fe8c3e9d55 100644 --- a/VEX/priv/ir_defs.c +++ b/VEX/priv/ir_defs.c @@ -1217,6 +1217,42 @@ void ppIRPutI ( IRPutI* puti ) ppIRExpr(puti->data); } +void ppIRStoreG ( IRStoreG* sg ) +{ + vex_printf("if ("); + ppIRExpr(sg->guard); + vex_printf(") ST%s(", sg->end==Iend_LE ? "le" : "be"); + ppIRExpr(sg->addr); + vex_printf(") = "); + ppIRExpr(sg->data); +} + +void ppIRLoadGOp ( IRLoadGOp cvt ) +{ + switch (cvt) { + case ILGop_INVALID: vex_printf("ILGop_INVALID"); break; + case ILGop_Ident32: vex_printf("Ident32"); break; + case ILGop_16Uto32: vex_printf("16Uto32"); break; + case ILGop_16Sto32: vex_printf("16Sto32"); break; + case ILGop_8Uto32: vex_printf("8Uto32"); break; + case ILGop_8Sto32: vex_printf("8Sto32"); break; + default: vpanic("ppIRLoadGOp"); + } +} + +void ppIRLoadG ( IRLoadG* lg ) +{ + ppIRTemp(lg->dst); + vex_printf(" = if-strict ("); + ppIRExpr(lg->guard); + vex_printf(") "); + ppIRLoadGOp(lg->cvt); + vex_printf("(LD%s(", lg->end==Iend_LE ? "le" : "be"); + ppIRExpr(lg->addr); + vex_printf(")) else "); + ppIRExpr(lg->alt); +} + void ppIRJumpKind ( IRJumpKind kind ) { switch (kind) { @@ -1298,6 +1334,12 @@ void ppIRStmt ( IRStmt* s ) vex_printf( ") = "); ppIRExpr(s->Ist.Store.data); break; + case Ist_StoreG: + ppIRStoreG(s->Ist.StoreG.details); + break; + case Ist_LoadG: + ppIRLoadG(s->Ist.LoadG.details); + break; case Ist_CAS: ppIRCAS(s->Ist.CAS.details); break; @@ -1738,6 +1780,33 @@ IRPutI* mkIRPutI ( IRRegArray* descr, IRExpr* ix, } +/* Constructors -- IRStoreG and IRLoadG */ + +IRStoreG* mkIRStoreG ( IREndness end, + IRExpr* addr, IRExpr* data, IRExpr* guard ) +{ + IRStoreG* sg = LibVEX_Alloc(sizeof(IRStoreG)); + sg->end = end; + sg->addr = addr; + sg->data = data; + sg->guard = guard; + return sg; +} + +IRLoadG* mkIRLoadG ( IREndness end, IRLoadGOp cvt, + IRTemp dst, IRExpr* addr, IRExpr* alt, IRExpr* guard ) +{ + IRLoadG* lg = LibVEX_Alloc(sizeof(IRLoadG)); + lg->end = end; + lg->cvt = cvt; + lg->dst = dst; + lg->addr = addr; + lg->alt = alt; + lg->guard = guard; + return lg; +} + + /* Constructors -- IRStmt */ IRStmt* IRStmt_NoOp ( void ) @@ -1792,6 +1861,21 @@ IRStmt* IRStmt_Store ( IREndness end, IRExpr* addr, IRExpr* data ) { vassert(end == Iend_LE || end == Iend_BE); return s; } +IRStmt* IRStmt_StoreG ( IREndness end, IRExpr* addr, IRExpr* data, + IRExpr* guard ) { + IRStmt* s = LibVEX_Alloc(sizeof(IRStmt)); + s->tag = Ist_StoreG; + s->Ist.StoreG.details = mkIRStoreG(end, addr, data, guard); + vassert(end == Iend_LE || end == Iend_BE); + return s; +} +IRStmt* IRStmt_LoadG ( IREndness end, IRLoadGOp cvt, IRTemp dst, + IRExpr* addr, IRExpr* alt, IRExpr* guard ) { + IRStmt* s = LibVEX_Alloc(sizeof(IRStmt)); + s->tag = Ist_LoadG; + s->Ist.LoadG.details = mkIRLoadG(end, cvt, dst, addr, alt, guard); + return s; +} IRStmt* IRStmt_CAS ( IRCAS* cas ) { IRStmt* s = LibVEX_Alloc(sizeof(IRStmt)); s->tag = Ist_CAS; @@ -2043,6 +2127,20 @@ IRStmt* deepCopyIRStmt ( IRStmt* s ) return IRStmt_Store(s->Ist.Store.end, deepCopyIRExpr(s->Ist.Store.addr), deepCopyIRExpr(s->Ist.Store.data)); + case Ist_StoreG: { + IRStoreG* sg = s->Ist.StoreG.details; + return IRStmt_StoreG(sg->end, + deepCopyIRExpr(sg->addr), + deepCopyIRExpr(sg->data), + deepCopyIRExpr(sg->guard)); + } + case Ist_LoadG: { + IRLoadG* lg = s->Ist.LoadG.details; + return IRStmt_LoadG(lg->end, lg->cvt, lg->dst, + deepCopyIRExpr(lg->addr), + deepCopyIRExpr(lg->alt), + deepCopyIRExpr(lg->guard)); + } case Ist_CAS: return IRStmt_CAS(deepCopyIRCAS(s->Ist.CAS.details)); case Ist_LLSC: @@ -2929,7 +3027,6 @@ IRType typeOfIRTemp ( IRTypeEnv* env, IRTemp tmp ) return env->types[tmp]; } - IRType typeOfIRConst ( IRConst* con ) { switch (con->tag) { @@ -2948,6 +3045,21 @@ IRType typeOfIRConst ( IRConst* con ) } } +void typeOfIRLoadGOp ( IRLoadGOp cvt, + /*OUT*/IRType* t_res, /*OUT*/IRType* t_arg ) +{ + switch (cvt) { + case ILGop_Ident32: + *t_res = Ity_I32; *t_arg = Ity_I32; break; + case ILGop_16Uto32: case ILGop_16Sto32: + *t_res = Ity_I32; *t_arg = Ity_I16; break; + case ILGop_8Uto32: case ILGop_8Sto32: + *t_res = Ity_I32; *t_arg = Ity_I8; break; + default: + vpanic("typeOfIRLoadGOp"); + } +} + IRType typeOfIRExpr ( IRTypeEnv* tyenv, IRExpr* e ) { IRType t_dst, t_arg1, t_arg2, t_arg3, t_arg4; @@ -3086,6 +3198,16 @@ Bool isFlatIRStmt ( IRStmt* st ) case Ist_Store: return toBool( isIRAtom(st->Ist.Store.addr) && isIRAtom(st->Ist.Store.data) ); + case Ist_StoreG: { + IRStoreG* sg = st->Ist.StoreG.details; + return toBool( isIRAtom(sg->addr) + && isIRAtom(sg->data) && isIRAtom(sg->guard) ); + } + case Ist_LoadG: { + IRLoadG* lg = st->Ist.LoadG.details; + return toBool( isIRAtom(lg->addr) + && isIRAtom(lg->alt) && isIRAtom(lg->guard) ); + } case Ist_CAS: cas = st->Ist.CAS.details; return toBool( isIRAtom(cas->addr) @@ -3258,10 +3380,12 @@ void useBeforeDef_Expr ( IRSB* bb, IRStmt* stmt, IRExpr* expr, Int* def_counts ) static void useBeforeDef_Stmt ( IRSB* bb, IRStmt* stmt, Int* def_counts ) { - Int i; - IRDirty* d; - IRCAS* cas; - IRPutI* puti; + Int i; + IRDirty* d; + IRCAS* cas; + IRPutI* puti; + IRLoadG* lg; + IRStoreG* sg; switch (stmt->tag) { case Ist_IMark: break; @@ -3284,6 +3408,18 @@ void useBeforeDef_Stmt ( IRSB* bb, IRStmt* stmt, Int* def_counts ) useBeforeDef_Expr(bb,stmt,stmt->Ist.Store.addr,def_counts); useBeforeDef_Expr(bb,stmt,stmt->Ist.Store.data,def_counts); break; + case Ist_StoreG: + sg = stmt->Ist.StoreG.details; + useBeforeDef_Expr(bb,stmt,sg->addr,def_counts); + useBeforeDef_Expr(bb,stmt,sg->data,def_counts); + useBeforeDef_Expr(bb,stmt,sg->guard,def_counts); + break; + case Ist_LoadG: + lg = stmt->Ist.LoadG.details; + useBeforeDef_Expr(bb,stmt,lg->addr,def_counts); + useBeforeDef_Expr(bb,stmt,lg->alt,def_counts); + useBeforeDef_Expr(bb,stmt,lg->guard,def_counts); + break; case Ist_CAS: cas = stmt->Ist.CAS.details; useBeforeDef_Expr(bb,stmt,cas->addr,def_counts); @@ -3571,18 +3707,54 @@ void tcStmt ( IRSB* bb, IRStmt* stmt, IRType gWordTy ) tcExpr( bb, stmt, stmt->Ist.WrTmp.data, gWordTy ); if (typeOfIRTemp(tyenv, stmt->Ist.WrTmp.tmp) != typeOfIRExpr(tyenv, stmt->Ist.WrTmp.data)) - sanityCheckFail(bb,stmt,"IRStmt.Put.Tmp: tmp and expr do not match"); + sanityCheckFail(bb,stmt, + "IRStmt.Put.Tmp: tmp and expr do not match"); break; case Ist_Store: tcExpr( bb, stmt, stmt->Ist.Store.addr, gWordTy ); tcExpr( bb, stmt, stmt->Ist.Store.data, gWordTy ); if (typeOfIRExpr(tyenv, stmt->Ist.Store.addr) != gWordTy) - sanityCheckFail(bb,stmt,"IRStmt.Store.addr: not :: guest word type"); + sanityCheckFail(bb,stmt, + "IRStmt.Store.addr: not :: guest word type"); if (typeOfIRExpr(tyenv, stmt->Ist.Store.data) == Ity_I1) - sanityCheckFail(bb,stmt,"IRStmt.Store.data: cannot Store :: Ity_I1"); + sanityCheckFail(bb,stmt, + "IRStmt.Store.data: cannot Store :: Ity_I1"); if (stmt->Ist.Store.end != Iend_LE && stmt->Ist.Store.end != Iend_BE) sanityCheckFail(bb,stmt,"Ist.Store.end: bogus endianness"); break; + case Ist_StoreG: { + IRStoreG* sg = stmt->Ist.StoreG.details; + tcExpr( bb, stmt, sg->addr, gWordTy ); + tcExpr( bb, stmt, sg->data, gWordTy ); + tcExpr( bb, stmt, sg->guard, gWordTy ); + if (typeOfIRExpr(tyenv, sg->addr) != gWordTy) + sanityCheckFail(bb,stmt,"IRStmtG...addr: not :: guest word type"); + if (typeOfIRExpr(tyenv, sg->data) == Ity_I1) + sanityCheckFail(bb,stmt,"IRStmtG...data: cannot Store :: Ity_I1"); + if (typeOfIRExpr(tyenv, sg->guard) != Ity_I1) + sanityCheckFail(bb,stmt,"IRStmtG...guard: not :: Ity_I1"); + if (sg->end != Iend_LE && sg->end != Iend_BE) + sanityCheckFail(bb,stmt,"IRStmtG...end: bogus endianness"); + break; + } + case Ist_LoadG: { + IRLoadG* lg = stmt->Ist.LoadG.details; + tcExpr( bb, stmt, lg->addr, gWordTy ); + tcExpr( bb, stmt, lg->alt, gWordTy ); + tcExpr( bb, stmt, lg->guard, gWordTy ); + if (typeOfIRExpr(tyenv, lg->guard) != Ity_I1) + sanityCheckFail(bb,stmt,"IRStmt.LoadG.guard: not :: Ity_I1"); + if (typeOfIRExpr(tyenv, lg->addr) != gWordTy) + sanityCheckFail(bb,stmt,"IRStmt.LoadG.addr: not " + ":: guest word type"); + if (typeOfIRExpr(tyenv, lg->alt) != typeOfIRTemp(tyenv, lg->dst)) + sanityCheckFail(bb,stmt,"IRStmt.LoadG: dst/alt type mismatch"); + IRTemp cvtRes = Ity_INVALID, cvtArg = Ity_INVALID; + typeOfIRLoadGOp(lg->cvt, &cvtRes, &cvtArg); + if (cvtRes != typeOfIRTemp(tyenv, lg->dst)) + sanityCheckFail(bb,stmt,"IRStmt.LoadG: dst/loaded type mismatch"); + break; + } case Ist_CAS: cas = stmt->Ist.CAS.details; /* make sure it's definitely either a CAS or a DCAS */ @@ -3692,15 +3864,6 @@ void tcStmt ( IRSB* bb, IRStmt* stmt, IRType gWordTy ) tcExpr( bb, stmt, d->guard, gWordTy ); if (typeOfIRExpr(tyenv, d->guard) != Ity_I1) sanityCheckFail(bb,stmt,"IRStmt.Dirty.guard not :: Ity_I1"); - /* A dirty helper that is executed conditionally (or not at - all) may not return a value. Hence if .tmp is not - IRTemp_INVALID, .guard must be manifestly True at JIT - time. */ - if (d->tmp != IRTemp_INVALID - && (d->guard->tag != Iex_Const - || d->guard->Iex.Const.con->Ico.U1 == 0)) - sanityCheckFail(bb,stmt,"IRStmt.Dirty with a return value" - " is executed under a condition"); /* check types, minimally */ if (d->tmp != IRTemp_INVALID && typeOfIRTemp(tyenv, d->tmp) == Ity_I1) @@ -3793,6 +3956,7 @@ void sanityCheckIRSB ( IRSB* bb, const HChar* caller, for (i = 0; i < bb->stmts_used; i++) { IRDirty* d; IRCAS* cas; + IRLoadG* lg; stmt = bb->stmts[i]; /* Check any temps used by this statement. */ useBeforeDef_Stmt(bb,stmt,def_counts); @@ -3808,11 +3972,19 @@ void sanityCheckIRSB ( IRSB* bb, const HChar* caller, sanityCheckFail(bb, stmt, "IRStmt.Tmp: destination tmp is assigned more than once"); break; - case Ist_Store: + case Ist_LoadG: + lg = stmt->Ist.LoadG.details; + if (lg->dst < 0 || lg->dst >= n_temps) + sanityCheckFail(bb, stmt, + "IRStmt.LoadG: destination tmp is out of range"); + def_counts[lg->dst]++; + if (def_counts[lg->dst] > 1) + sanityCheckFail(bb, stmt, + "IRStmt.LoadG: destination tmp is assigned more than once"); break; case Ist_Dirty: - if (stmt->Ist.Dirty.details->tmp != IRTemp_INVALID) { - d = stmt->Ist.Dirty.details; + d = stmt->Ist.Dirty.details; + if (d->tmp != IRTemp_INVALID) { if (d->tmp < 0 || d->tmp >= n_temps) sanityCheckFail(bb, stmt, "IRStmt.Dirty: destination tmp is out of range"); diff --git a/VEX/priv/ir_opt.c b/VEX/priv/ir_opt.c index 1537df686f..c8d018f2b5 100644 --- a/VEX/priv/ir_opt.c +++ b/VEX/priv/ir_opt.c @@ -400,10 +400,12 @@ static IRExpr* flatten_Expr ( IRSB* bb, IRExpr* ex ) static void flatten_Stmt ( IRSB* bb, IRStmt* st ) { Int i; - IRExpr *e1, *e2, *e3, *e4, *e5; - IRDirty *d, *d2; - IRCAS *cas, *cas2; - IRPutI *puti, *puti2; + IRExpr *e1, *e2, *e3, *e4, *e5; + IRDirty *d, *d2; + IRCAS *cas, *cas2; + IRPutI *puti, *puti2; + IRLoadG *lg; + IRStoreG *sg; switch (st->tag) { case Ist_Put: if (isIRAtom(st->Ist.Put.data)) { @@ -439,6 +441,21 @@ static void flatten_Stmt ( IRSB* bb, IRStmt* st ) e2 = flatten_Expr(bb, st->Ist.Store.data); addStmtToIRSB(bb, IRStmt_Store(st->Ist.Store.end, e1,e2)); break; + case Ist_StoreG: + sg = st->Ist.StoreG.details; + e1 = flatten_Expr(bb, sg->addr); + e2 = flatten_Expr(bb, sg->data); + e3 = flatten_Expr(bb, sg->guard); + addStmtToIRSB(bb, IRStmt_StoreG(sg->end, e1, e2, e3)); + break; + case Ist_LoadG: + lg = st->Ist.LoadG.details; + e1 = flatten_Expr(bb, lg->addr); + e2 = flatten_Expr(bb, lg->alt); + e3 = flatten_Expr(bb, lg->guard); + addStmtToIRSB(bb, IRStmt_LoadG(lg->end, lg->cvt, lg->dst, + e1, e2, e3)); + break; case Ist_CAS: cas = st->Ist.CAS.details; e1 = flatten_Expr(bb, cas->addr); @@ -763,7 +780,22 @@ static void handle_gets_Stmt ( vassert(isIRAtom(st->Ist.Store.data)); memRW = True; break; - + case Ist_StoreG: { + IRStoreG* sg = st->Ist.StoreG.details; + vassert(isIRAtom(sg->addr)); + vassert(isIRAtom(sg->data)); + vassert(isIRAtom(sg->guard)); + memRW = True; + break; + } + case Ist_LoadG: { + IRLoadG* lg = st->Ist.LoadG.details; + vassert(isIRAtom(lg->addr)); + vassert(isIRAtom(lg->alt)); + vassert(isIRAtom(lg->guard)); + memRW = True; + break; + } case Ist_Exit: vassert(isIRAtom(st->Ist.Exit.guard)); break; @@ -2407,6 +2439,62 @@ static IRStmt* subst_and_fold_Stmt ( IRExpr** env, IRStmt* st ) fold_Expr(env, subst_Expr(env, st->Ist.Store.data)) ); + case Ist_StoreG: { + IRStoreG* sg = st->Ist.StoreG.details; + vassert(isIRAtom(sg->addr)); + vassert(isIRAtom(sg->data)); + vassert(isIRAtom(sg->guard)); + IRExpr* faddr = fold_Expr(env, subst_Expr(env, sg->addr)); + IRExpr* fdata = fold_Expr(env, subst_Expr(env, sg->data)); + IRExpr* fguard = fold_Expr(env, subst_Expr(env, sg->guard)); + if (fguard->tag == Iex_Const) { + /* The condition on this store has folded down to a constant. */ + vassert(fguard->Iex.Const.con->tag == Ico_U1); + if (fguard->Iex.Const.con->Ico.U1 == False) { + return IRStmt_NoOp(); + } else { + vassert(fguard->Iex.Const.con->Ico.U1 == True); + return IRStmt_Store(sg->end, faddr, fdata); + } + } + return IRStmt_StoreG(sg->end, faddr, fdata, fguard); + } + + case Ist_LoadG: { + /* This is complicated. If the guard folds down to 'false', + we can replace it with a NoOp, but if the guard folds down + to 'true', we can't conveniently replace it with an + unconditional load, because doing so requires generating a + new temporary, and that is not easy to do at this + point. */ + IRLoadG* lg = st->Ist.LoadG.details; + vassert(isIRAtom(lg->addr)); + vassert(isIRAtom(lg->alt)); + vassert(isIRAtom(lg->guard)); + IRExpr* faddr = fold_Expr(env, subst_Expr(env, lg->addr)); + IRExpr* falt = fold_Expr(env, subst_Expr(env, lg->alt)); + IRExpr* fguard = fold_Expr(env, subst_Expr(env, lg->guard)); + if (fguard->tag == Iex_Const) { + /* The condition on this load has folded down to a constant. */ + vassert(fguard->Iex.Const.con->tag == Ico_U1); + if (fguard->Iex.Const.con->Ico.U1 == False) { + /* The load is not going to happen -- instead 'alt' is + assigned to 'dst'. */ + return IRStmt_WrTmp(lg->dst, falt); + } else { + vassert(fguard->Iex.Const.con->Ico.U1 == True); + /* The load is always going to happen. We want to + convert to an unconditional load and assign to 'dst' + (IRStmt_WrTmp). Problem is we need an extra temp to + hold the loaded value, but none is available. + Instead, reconstitute the conditional load (with + folded args, of course) and let the caller of this + routine deal with the problem. */ + } + } + return IRStmt_LoadG(lg->end, lg->cvt, lg->dst, faddr, falt, fguard); + } + case Ist_CAS: { IRCAS *cas, *cas2; cas = st->Ist.CAS.details; @@ -2480,8 +2568,6 @@ static IRStmt* subst_and_fold_Stmt ( IRExpr** env, IRStmt* st ) /* Interesting. The condition on this exit has folded down to a constant. */ vassert(fcond->Iex.Const.con->tag == Ico_U1); - vassert(fcond->Iex.Const.con->Ico.U1 == False - || fcond->Iex.Const.con->Ico.U1 == True); if (fcond->Iex.Const.con->Ico.U1 == False) { /* exit is never going to happen, so dump the statement. */ return IRStmt_NoOp(); @@ -2515,6 +2601,11 @@ IRSB* cprop_BB ( IRSB* in ) IRStmt* st2; Int n_tmps = in->tyenv->types_used; IRExpr** env = LibVEX_Alloc(n_tmps * sizeof(IRExpr*)); + /* Keep track of IRStmt_LoadGs that we need to revisit after + processing all the other statements. */ + const Int N_FIXUPS = 16; + Int fixups[N_FIXUPS]; /* indices in the stmt array of 'out' */ + Int n_fixups = 0; out = emptyIRSB(); out->tyenv = deepCopyIRTypeEnv( in->tyenv ); @@ -2542,40 +2633,124 @@ IRSB* cprop_BB ( IRSB* in ) st2 = subst_and_fold_Stmt( env, st2 ); - /* If the statement has been folded into a no-op, forget it. */ - if (st2->tag == Ist_NoOp) continue; + /* Deal with some post-folding special cases. */ + switch (st2->tag) { + + /* If the statement has been folded into a no-op, forget + it. */ + case Ist_NoOp: + continue; + + /* If the statement assigns to an IRTemp add it to the + running environment. This is for the benefit of copy + propagation and to allow sameIRExpr look through + IRTemps. */ + case Ist_WrTmp: { + vassert(env[(Int)(st2->Ist.WrTmp.tmp)] == NULL); + env[(Int)(st2->Ist.WrTmp.tmp)] = st2->Ist.WrTmp.data; + + /* 't1 = t2' -- don't add to BB; will be optimized out */ + if (st2->Ist.WrTmp.data->tag == Iex_RdTmp) + continue; + + /* 't = const' && 'const != F64i' -- don't add to 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->Ist.WrTmp.data->tag == Iex_Const + && st2->Ist.WrTmp.data->Iex.Const.con->tag != Ico_F64i) { + continue; + } + /* else add it to the output, as normal */ + break; + } - /* If the statement assigns to an IRTemp add it to the running - environment. This is for the benefit of copy propagation - and to allow sameIRExpr look through IRTemps. */ - if (st2->tag == Ist_WrTmp) { - vassert(env[(Int)(st2->Ist.WrTmp.tmp)] == NULL); - env[(Int)(st2->Ist.WrTmp.tmp)] = st2->Ist.WrTmp.data; - - /* 't1 = t2' -- don't add to BB; will be optimized out */ - if (st2->Ist.WrTmp.data->tag == Iex_RdTmp) continue; - - /* 't = const' && 'const != F64i' -- don't add to 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->Ist.WrTmp.data->tag == Iex_Const - && st2->Ist.WrTmp.data->Iex.Const.con->tag != Ico_F64i) continue; + case Ist_LoadG: { + IRLoadG* lg = st2->Ist.LoadG.details; + IRExpr* guard = lg->guard; + if (guard->tag == Iex_Const) { + /* The guard has folded to a constant, and that + constant must be 1:I1, since subst_and_fold_Stmt + folds out the case 0:I1 by itself. */ + vassert(guard->Iex.Const.con->tag == Ico_U1); + vassert(guard->Iex.Const.con->Ico.U1 == True); + /* Add a NoOp here as a placeholder, and make a note of + where it is in the output block. Afterwards we'll + come back here and transform the NoOp and the LoadG + into a load-convert pair. The fixups[] entry + refers to the inserted NoOp, and we expect to find + the relevant LoadG immediately after it. */ + vassert(n_fixups >= 0 && n_fixups <= N_FIXUPS); + if (n_fixups < N_FIXUPS) { + fixups[n_fixups++] = out->stmts_used; + addStmtToIRSB( out, IRStmt_NoOp() ); + } + } + /* And always add the LoadG to the output, regardless. */ + break; + } + + default: + break; } /* Not interesting, copy st2 into the output block. */ addStmtToIRSB( out, st2 ); } -#if STATS_IROPT +# if STATS_IROPT vex_printf("sameIRExpr: invoked = %u/%u equal = %u/%u max_nodes = %u\n", invocation_count, recursion_count, success_count, recursion_success_count, max_nodes_visited); -#endif +# endif out->next = subst_Expr( env, in->next ); out->jumpkind = in->jumpkind; out->offsIP = in->offsIP; + + /* Process any leftover unconditional LoadGs that we noticed + in the main pass. */ + vassert(n_fixups >= 0 && n_fixups <= N_FIXUPS); + for (i = 0; i < n_fixups; i++) { + Int ix = fixups[i]; + /* Carefully verify that the LoadG has the expected form. */ + vassert(ix >= 0 && ix+1 < out->stmts_used); + IRStmt* nop = out->stmts[ix]; + IRStmt* lgu = out->stmts[ix+1]; + vassert(nop->tag == Ist_NoOp); + vassert(lgu->tag == Ist_LoadG); + IRLoadG* lg = lgu->Ist.LoadG.details; + IRExpr* guard = lg->guard; + vassert(guard->Iex.Const.con->tag == Ico_U1); + vassert(guard->Iex.Const.con->Ico.U1 == True); + /* Figure out the load and result types, and the implied + conversion operation. */ + IRType cvtRes = Ity_INVALID, cvtArg = Ity_INVALID; + typeOfIRLoadGOp(lg->cvt, &cvtRes, &cvtArg); + IROp cvtOp = Iop_INVALID; + switch (lg->cvt) { + case ILGop_Ident32: break; + case ILGop_8Uto32: cvtOp = Iop_8Uto32; break; + case ILGop_8Sto32: cvtOp = Iop_8Sto32; break; + case ILGop_16Uto32: cvtOp = Iop_16Uto32; break; + case ILGop_16Sto32: cvtOp = Iop_16Sto32; break; + default: vpanic("cprop_BB: unhandled ILGOp"); + } + /* Replace the placeholder NoOp by the required unconditional + load. */ + IRTemp tLoaded = newIRTemp(out->tyenv, cvtArg); + out->stmts[ix] + = IRStmt_WrTmp(tLoaded, + IRExpr_Load(lg->end, cvtArg, lg->addr)); + /* Replace the LoadG by a conversion from the loaded value's + type to the required result type. */ + out->stmts[ix+1] + = IRStmt_WrTmp( + lg->dst, cvtOp == Iop_INVALID + ? IRExpr_RdTmp(tLoaded) + : IRExpr_Unop(cvtOp, IRExpr_RdTmp(tLoaded))); + } + return out; } @@ -2671,6 +2846,20 @@ static void addUses_Stmt ( Bool* set, IRStmt* st ) addUses_Expr(set, st->Ist.Store.addr); addUses_Expr(set, st->Ist.Store.data); return; + case Ist_StoreG: { + IRStoreG* sg = st->Ist.StoreG.details; + addUses_Expr(set, sg->addr); + addUses_Expr(set, sg->data); + addUses_Expr(set, sg->guard); + return; + } + case Ist_LoadG: { + IRLoadG* lg = st->Ist.LoadG.details; + addUses_Expr(set, lg->addr); + addUses_Expr(set, lg->alt); + addUses_Expr(set, lg->guard); + return; + } case Ist_CAS: cas = st->Ist.CAS.details; addUses_Expr(set, cas->addr); @@ -3477,7 +3666,7 @@ static Bool do_cse_BB ( IRSB* bb ) if (0) { ppIRSB(bb); vex_printf("\n\n"); } /* Iterate forwards over the stmts. - On seeing "t = E", where E is one of the 5 AvailExpr forms: + On seeing "t = E", where E is one of the AvailExpr forms: let E' = apply tenv substitution to E search aenv for E' if a mapping E' -> q is found, @@ -3507,11 +3696,12 @@ static Bool do_cse_BB ( IRSB* bb ) switch (st->tag) { case Ist_Dirty: case Ist_Store: case Ist_MBE: case Ist_CAS: case Ist_LLSC: + case Ist_StoreG: paranoia = 2; break; case Ist_Put: case Ist_PutI: paranoia = 1; break; case Ist_NoOp: case Ist_IMark: case Ist_AbiHint: - case Ist_WrTmp: case Ist_Exit: + case Ist_WrTmp: case Ist_Exit: case Ist_LoadG: paranoia = 0; break; default: vpanic("do_cse_BB(1)"); @@ -4212,6 +4402,21 @@ static void deltaIRStmt ( IRStmt* st, Int delta ) deltaIRExpr(st->Ist.Store.addr, delta); deltaIRExpr(st->Ist.Store.data, delta); break; + case Ist_StoreG: { + IRStoreG* sg = st->Ist.StoreG.details; + deltaIRExpr(sg->addr, delta); + deltaIRExpr(sg->data, delta); + deltaIRExpr(sg->guard, delta); + break; + } + case Ist_LoadG: { + IRLoadG* lg = st->Ist.LoadG.details; + lg->dst += delta; + deltaIRExpr(lg->addr, delta); + deltaIRExpr(lg->alt, delta); + deltaIRExpr(lg->guard, delta); + break; + } case Ist_CAS: if (st->Ist.CAS.details->oldHi != IRTemp_INVALID) st->Ist.CAS.details->oldHi += delta; @@ -4684,6 +4889,20 @@ static void aoccCount_Stmt ( UShort* uses, IRStmt* st ) aoccCount_Expr(uses, st->Ist.Store.addr); aoccCount_Expr(uses, st->Ist.Store.data); return; + case Ist_StoreG: { + IRStoreG* sg = st->Ist.StoreG.details; + aoccCount_Expr(uses, sg->addr); + aoccCount_Expr(uses, sg->data); + aoccCount_Expr(uses, sg->guard); + return; + } + case Ist_LoadG: { + IRLoadG* lg = st->Ist.LoadG.details; + aoccCount_Expr(uses, lg->addr); + aoccCount_Expr(uses, lg->alt); + aoccCount_Expr(uses, lg->guard); + return; + } case Ist_CAS: cas = st->Ist.CAS.details; aoccCount_Expr(uses, cas->addr); @@ -5000,6 +5219,20 @@ static IRStmt* atbSubst_Stmt ( ATmpInfo* env, IRStmt* st ) atbSubst_Expr(env, st->Ist.Store.addr), atbSubst_Expr(env, st->Ist.Store.data) ); + case Ist_StoreG: { + IRStoreG* sg = st->Ist.StoreG.details; + return IRStmt_StoreG(sg->end, + atbSubst_Expr(env, sg->addr), + atbSubst_Expr(env, sg->data), + atbSubst_Expr(env, sg->guard)); + } + case Ist_LoadG: { + IRLoadG* lg = st->Ist.LoadG.details; + return IRStmt_LoadG(lg->end, lg->cvt, lg->dst, + atbSubst_Expr(env, lg->addr), + atbSubst_Expr(env, lg->alt), + atbSubst_Expr(env, lg->guard)); + } case Ist_WrTmp: return IRStmt_WrTmp( st->Ist.WrTmp.tmp, @@ -5411,6 +5644,20 @@ static void considerExpensives ( /*OUT*/Bool* hasGetIorPutI, vassert(isIRAtom(st->Ist.Store.addr)); vassert(isIRAtom(st->Ist.Store.data)); break; + case Ist_StoreG: { + IRStoreG* sg = st->Ist.StoreG.details; + vassert(isIRAtom(sg->addr)); + vassert(isIRAtom(sg->data)); + vassert(isIRAtom(sg->guard)); + break; + } + case Ist_LoadG: { + IRLoadG* lg = st->Ist.LoadG.details; + vassert(isIRAtom(lg->addr)); + vassert(isIRAtom(lg->alt)); + vassert(isIRAtom(lg->guard)); + break; + } case Ist_CAS: cas = st->Ist.CAS.details; vassert(isIRAtom(cas->addr)); diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index 99eaaaf6a0..1a7a4d11bd 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -218,7 +218,7 @@ float, or a vector (SIMD) value. */ typedef enum { - Ity_INVALID=0x11000, + Ity_INVALID=0x1100, Ity_I1, Ity_I8, Ity_I16, @@ -248,7 +248,7 @@ extern Int sizeofIRType ( IRType ); /* IREndness is used in load IRExprs and store IRStmts. */ typedef enum { - Iend_LE=0x12000, /* little endian */ + Iend_LE=0x1200, /* little endian */ Iend_BE /* big endian */ } IREndness; @@ -261,7 +261,7 @@ typedef /* The various kinds of constant. */ typedef enum { - Ico_U1=0x13000, + Ico_U1=0x1300, Ico_U8, Ico_U16, Ico_U32, @@ -412,7 +412,7 @@ typedef /* -- Do not change this ordering. The IR generators rely on (eg) Iop_Add64 == IopAdd8 + 3. -- */ - Iop_INVALID=0x14000, + Iop_INVALID=0x1400, Iop_Add8, Iop_Add16, Iop_Add32, Iop_Add64, Iop_Sub8, Iop_Sub16, Iop_Sub32, Iop_Sub64, /* Signless mul. MullS/MullU is elsewhere. */ @@ -1543,7 +1543,7 @@ typedef struct _IRTriop IRTriop; /* forward declaration */ in the comments for IRExpr. */ typedef enum { - Iex_Binder=0x15000, + Iex_Binder=0x1900, Iex_Get, Iex_GetI, Iex_RdTmp, @@ -1752,7 +1752,7 @@ struct _IRExpr { } Iex; }; -/* ------------------ A ternary expression ---------------------- */ +/* Expression auxiliaries: a ternary expression. */ struct _IRTriop { IROp op; /* op-code */ IRExpr* arg1; /* operand 1 */ @@ -1760,7 +1760,7 @@ struct _IRTriop { IRExpr* arg3; /* operand 3 */ }; -/* ------------------ A quarternary expression ------------------ */ +/* Expression auxiliaries: a quarternary expression. */ struct _IRQop { IROp op; /* op-code */ IRExpr* arg1; /* operand 1 */ @@ -1865,7 +1865,7 @@ extern Bool eqIRAtom ( IRExpr*, IRExpr* ); */ typedef enum { - Ijk_INVALID=0x16000, + Ijk_INVALID=0x1A00, Ijk_Boring, /* not interesting; just goto next */ Ijk_Call, /* guest is doing a call */ Ijk_Ret, /* guest is doing a return */ @@ -1912,19 +1912,21 @@ extern void ppIRJumpKind ( IRJumpKind ); Dirty calls are statements rather than expressions for obvious reasons. If a dirty call is marked as writing guest state, any - values derived from the written parts of the guest state are - invalid. Similarly, if the dirty call is stated as writing - memory, any loaded values are invalidated by it. + pre-existing values derived from the written parts of the guest + state are invalid. Similarly, if the dirty call is stated as + writing memory, any pre-existing loaded values are invalidated by + it. In order that instrumentation is possible, the call must state, and state correctly: - * whether it reads, writes or modifies memory, and if so where - (only one chunk can be stated) + * whether it reads, writes or modifies memory, and if so where Only + one chunk can be stated, although it is allowed to repeat some + number of times at a fixed interval. * whether it reads, writes or modifies guest state, and if so which - pieces (several pieces may be stated, and currently their extents - must be known at translation-time). + pieces. Several pieces may be stated, and their extents must be + known at translation-time. Normally, code is generated to pass just the args to the helper. However, if .needsBBP is set, then an extra first argument is @@ -1934,9 +1936,11 @@ extern void ppIRJumpKind ( IRJumpKind ); call does not access guest state. IMPORTANT NOTE re GUARDS: Dirty calls are strict, very strict. The - arguments are evaluated REGARDLESS of the guard value. The order of - argument evaluation is unspecified. The guard expression is evaluated - AFTER the arguments have been evaluated. + arguments, and 'mFx' are evaluated REGARDLESS of the guard value. + The order of argument evaluation is unspecified. The guard + expression is evaluated AFTER the arguments and 'mFx' have been + evaluated. 'mFx' is expected (by Memcheck) to be a defined value + even if the guard evaluates to false. */ #define VEX_N_FXSTATE 7 /* enough for FXSAVE/FXRSTOR on x86 */ @@ -1944,7 +1948,7 @@ extern void ppIRJumpKind ( IRJumpKind ); /* Effects on resources (eg. registers, memory locations) */ typedef enum { - Ifx_None = 0x1700, /* no effect */ + Ifx_None=0x1B00, /* no effect */ Ifx_Read, /* reads the resource */ Ifx_Write, /* writes the resource */ Ifx_Modify, /* modifies the resource */ @@ -1958,10 +1962,10 @@ extern void ppIREffect ( IREffect ); typedef struct _IRDirty { /* What to call, and details of args/results. .guard must be - non-NULL. If .tmp is not IRTemp_INVALID (that is, the call - returns a result) then .guard must be demonstrably (at - JIT-time) always true, that is, the call must be - unconditional. Conditional calls that assign .tmp are not + non-NULL. If .tmp is not IRTemp_INVALID, then the call + returns a result which is placed in .tmp. If at runtime the + guard evaluates to false, .tmp has an all-ones bit pattern + written to it. Hence conditional calls that assign .tmp are allowed. */ IRCallee* cee; /* where to call */ IRExpr* guard; /* :: Ity_Bit. Controls whether call happens */ @@ -2031,7 +2035,7 @@ IRDirty* unsafeIRDirty_1_N ( IRTemp dst, typedef enum { - Imbe_Fence=0x18000, + Imbe_Fence=0x1C00, /* Needed only on ARM. It cancels a reservation made by a preceding Linked-Load, and needs to be handed through to the back end, just as LL and SC themselves are. */ @@ -2131,6 +2135,7 @@ extern IRCAS* deepCopyIRCAS ( IRCAS* ); /* ------------------ Circular Array Put ------------------ */ + typedef struct { IRRegArray* descr; /* Part of guest state treated as circular */ @@ -2147,6 +2152,85 @@ extern IRPutI* mkIRPutI ( IRRegArray* descr, IRExpr* ix, extern IRPutI* deepCopyIRPutI ( IRPutI* ); +/* --------------- Guarded loads and stores --------------- */ + +/* Conditional stores are straightforward. They are the same as + normal stores, with an extra 'guard' field :: Ity_I1 that + determines whether or not the store actually happens. If not, + memory is unmodified. + + The semantics of this is that 'addr' and 'data' are fully evaluated + even in the case where 'guard' evaluates to zero (false). +*/ +typedef + struct { + IREndness end; /* Endianness of the store */ + IRExpr* addr; /* store address */ + IRExpr* data; /* value to write */ + IRExpr* guard; /* Guarding value */ + } + IRStoreG; + +/* Conditional loads are a little more complex. 'addr' is the + address, 'guard' is the guarding condition. If the load takes + place, the loaded value is placed in 'dst'. If it does not take + place, 'alt' is copied to 'dst'. However, the loaded value is not + placed directly in 'dst' -- it is first subjected to the conversion + specified by 'cvt'. + + For example, imagine doing a conditional 8-bit load, in which the + loaded value is zero extended to 32 bits. Hence: + * 'dst' and 'alt' must have type I32 + * 'cvt' must be a unary op which converts I8 to I32. In this + example, it would be ILGop_8Uto32. + + There is no explicit indication of the type at which the load is + done, since that is inferrable from the arg type of 'cvt'. Note + that the types of 'alt' and 'dst' and the result type of 'cvt' must + all be the same. + + Semantically, 'addr' is evaluated even in the case where 'guard' + evaluates to zero (false), and 'alt' is evaluated even when 'guard' + evaluates to one (true). That is, 'addr' and 'alt' are always + evaluated. +*/ +typedef + enum { + ILGop_INVALID=0x1D00, + ILGop_Ident32, /* 32 bit, no conversion */ + ILGop_16Uto32, /* 16 bit load, Z-widen to 32 */ + ILGop_16Sto32, /* 16 bit load, S-widen to 32 */ + ILGop_8Uto32, /* 8 bit load, Z-widen to 32 */ + ILGop_8Sto32 /* 8 bit load, S-widen to 32 */ + } + IRLoadGOp; + +typedef + struct { + IREndness end; /* Endianness of the load */ + IRLoadGOp cvt; /* Conversion to apply to the loaded value */ + IRTemp dst; /* Destination (LHS) of assignment */ + IRExpr* addr; /* Address being loaded from */ + IRExpr* alt; /* Value if load is not done. */ + IRExpr* guard; /* Guarding value */ + } + IRLoadG; + +extern void ppIRStoreG ( IRStoreG* sg ); + +extern void ppIRLoadGOp ( IRLoadGOp cvt ); + +extern void ppIRLoadG ( IRLoadG* lg ); + +extern IRStoreG* mkIRStoreG ( IREndness end, + IRExpr* addr, IRExpr* data, + IRExpr* guard ); + +extern IRLoadG* mkIRLoadG ( IREndness end, IRLoadGOp cvt, + IRTemp dst, IRExpr* addr, IRExpr* alt, + IRExpr* guard ); + + /* ------------------ Statements ------------------ */ /* The different kinds of statements. Their meaning is explained @@ -2161,13 +2245,15 @@ extern IRPutI* deepCopyIRPutI ( IRPutI* ); typedef enum { - Ist_NoOp=0x19000, + Ist_NoOp=0x1E00, Ist_IMark, /* META */ Ist_AbiHint, /* META */ Ist_Put, Ist_PutI, Ist_WrTmp, Ist_Store, + Ist_LoadG, + Ist_StoreG, Ist_CAS, Ist_LLSC, Ist_Dirty, @@ -2288,6 +2374,24 @@ typedef IRExpr* data; /* value to write */ } Store; + /* Guarded store. Note that this is defined to evaluate all + expression fields (addr, data) even if the guard evaluates + to false. + ppIRStmt output: + if () ST() = */ + struct { + IRStoreG* details; + } StoreG; + + /* Guarded load. Note that this is defined to evaluate all + expression fields (addr, alt) even if the guard evaluates + to false. + ppIRStmt output: + t = if () (LD()) else */ + struct { + IRLoadG* details; + } LoadG; + /* Do an atomic compare-and-swap operation. Semantics are described above on a comment at the definition of IRCAS. @@ -2407,6 +2511,10 @@ extern IRStmt* IRStmt_Put ( Int off, IRExpr* data ); extern IRStmt* IRStmt_PutI ( IRPutI* details ); extern IRStmt* IRStmt_WrTmp ( IRTemp tmp, IRExpr* data ); extern IRStmt* IRStmt_Store ( IREndness end, IRExpr* addr, IRExpr* data ); +extern IRStmt* IRStmt_StoreG ( IREndness end, IRExpr* addr, IRExpr* data, + IRExpr* guard ); +extern IRStmt* IRStmt_LoadG ( IREndness end, IRLoadGOp cvt, IRTemp dst, + IRExpr* addr, IRExpr* alt, IRExpr* guard ); extern IRStmt* IRStmt_CAS ( IRCAS* details ); extern IRStmt* IRStmt_LLSC ( IREndness end, IRTemp result, IRExpr* addr, IRExpr* storedata ); @@ -2504,6 +2612,11 @@ extern IRType typeOfIRConst ( IRConst* ); extern IRType typeOfIRTemp ( IRTypeEnv*, IRTemp ); extern IRType typeOfIRExpr ( IRTypeEnv*, IRExpr* ); +/* What are the arg and result type for this IRLoadGOp? */ +extern void typeOfIRLoadGOp ( IRLoadGOp cvt, + /*OUT*/IRType* t_res, + /*OUT*/IRType* t_arg ); + /* Sanity check a BB of IR */ extern void sanityCheckIRSB ( IRSB* bb, const HChar* caller,