direct representation of conditional (guarded) loads and stores in IR.
git-svn-id: svn://svn.valgrind.org/vex/branches/COMEM@2570
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) {
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;
}
+/* 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 )
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;
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:
return env->types[tmp];
}
-
IRType typeOfIRConst ( IRConst* con )
{
switch (con->tag) {
}
}
+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;
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)
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;
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);
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 */
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)
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);
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");
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)) {
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);
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;
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;
/* 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();
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 );
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;
}
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);
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,
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)");
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;
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);
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,
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));
float, or a vector (SIMD) value. */
typedef
enum {
- Ity_INVALID=0x11000,
+ Ity_INVALID=0x1100,
Ity_I1,
Ity_I8,
Ity_I16,
/* 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;
/* The various kinds of constant. */
typedef
enum {
- Ico_U1=0x13000,
+ Ico_U1=0x1300,
Ico_U8,
Ico_U16,
Ico_U32,
/* -- 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. */
in the comments for IRExpr. */
typedef
enum {
- Iex_Binder=0x15000,
+ Iex_Binder=0x1900,
Iex_Get,
Iex_GetI,
Iex_RdTmp,
} Iex;
};
-/* ------------------ A ternary expression ---------------------- */
+/* Expression auxiliaries: a ternary expression. */
struct _IRTriop {
IROp op; /* op-code */
IRExpr* arg1; /* operand 1 */
IRExpr* arg3; /* operand 3 */
};
-/* ------------------ A quarternary expression ------------------ */
+/* Expression auxiliaries: a quarternary expression. */
struct _IRQop {
IROp op; /* op-code */
IRExpr* arg1; /* operand 1 */
*/
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 */
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
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 */
/* 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 */
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 */
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. */
/* ------------------ Circular Array Put ------------------ */
+
typedef
struct {
IRRegArray* descr; /* Part of guest state treated as circular */
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
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,
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 (<guard>) ST<end>(<addr>) = <data> */
+ 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<tmp> = if (<guard>) <cvt>(LD<end>(<addr>)) else <alt> */
+ struct {
+ IRLoadG* details;
+ } LoadG;
+
/* Do an atomic compare-and-swap operation. Semantics are
described above on a comment at the definition of IRCAS.
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 );
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,