]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add IR level definitions and associated iropt hackery, to support
authorJulian Seward <jseward@acm.org>
Sun, 25 Nov 2012 15:14:44 +0000 (15:14 +0000)
committerJulian Seward <jseward@acm.org>
Sun, 25 Nov 2012 15:14:44 +0000 (15:14 +0000)
direct representation of conditional (guarded) loads and stores in IR.

git-svn-id: svn://svn.valgrind.org/vex/branches/COMEM@2570

VEX/priv/ir_defs.c
VEX/priv/ir_opt.c
VEX/pub/libvex_ir.h

index b356f60d1fd7db58b18450bd8cdf5715fd8597ec..fe8c3e9d552aa869d9341454247e1012ece64c5c 100644 (file)
@@ -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");
index 1537df686f148c6eb7813d2b36339a2765a0f9b2..c8d018f2b580dfd7efbddbe6bc43671c57b73b9a 100644 (file)
@@ -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 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));
index 99eaaaf6a01df409a8449d2dea3300fcac402a92..1a7a4d11bd667c2121663b8520c138bfe3dce13b 100644 (file)
    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 (<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.
 
@@ -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,