]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
First pass at Memcheck.
authorJulian Seward <jseward@acm.org>
Wed, 3 Nov 2004 09:10:30 +0000 (09:10 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 3 Nov 2004 09:10:30 +0000 (09:10 +0000)
git-svn-id: svn://svn.valgrind.org/vex/trunk@480

VEX/head20041019/coregrind/toolfuncs.def
VEX/head20041019/coregrind/valgrind.vs
VEX/head20041019/memcheck/mc_from_ucode.c
VEX/head20041019/memcheck/mc_include.h
VEX/head20041019/memcheck/mc_main.c
VEX/head20041019/memcheck/mc_translate.c

index 83bb646641416ea557b7bfd1c36b3d555d68693f..62f6a2f8cf682e19e891b923dd810c9a420de9ab 100644 (file)
@@ -24,7 +24,7 @@ void,         post_clo_init
 ## always results in the same output, because basic blocks can be
 ## retranslated.  Unless you're doing something really strange...
 ## 'orig_addr' is the address of the first instruction in the block.
-IRBB*, instrument,     IRBB* bb,   VexGuestLayout* layout
+IRBB*, instrument,     IRBB* bb,  VexGuestLayout* layout,  IRType hWordTy
 
 ## Finish up, print out any results, etc.  `exitcode' is program's exit
 ## code.  The shadow (if the `shadow_regs' need is set) can be found with
index 02865d25032b41eb574fdd4b339d680d461a1758..76ebb190c15afc8abfd3f7ba7a957f05069abb47 100644 (file)
@@ -5,11 +5,14 @@ VALGRIND_2.1 {
                vgProf_*;
                 vgOff_*;
                 vgArch_*;
+               *IROp*;
                *IRExpr*;
                *IRStmt*;
                *IRBB*;
                *IRDirty*;
                *IRType*;
+               *IRTemp*;
+               *IRConst*;
                *IRCallee*;
                LibVEX_Alloc;
 
index a36fcbc21e170dee9af7bf5d840938e1e90501e1..f6f6b2ed490ada98c31dd41da499c1ec1de5de75 100644 (file)
@@ -45,6 +45,8 @@
 /* See the corresponding comment at the top of vg_from_ucode.c to find out
  * how all this works */
 
+#if 0
+
 /*----------------------------------------------------*/
 /*--- v-size (4, or 2 with OSO) insn emitters      ---*/
 /*----------------------------------------------------*/
@@ -656,6 +658,8 @@ void SK_(emit_XUInstr) ( UInstr* u, RRegSet regs_live_before )
    }
 }
 
+#endif
+
 /*--------------------------------------------------------------------*/
 /*--- end                                          mc_from_ucode.c ---*/
 /*--------------------------------------------------------------------*/
index 948cef8ab6c65bf12d035b062bfc0dd8dde3ab27..ae6ddc1b4d0993633a100458501d97b15e5dd620 100644 (file)
@@ -113,14 +113,9 @@ extern Bool MC_(clo_avoid_strlen_errors);
 /*--- Functions                                            ---*/
 /*------------------------------------------------------------*/
 
-/* Functions defined in mc_helpers.S */
-extern void MC_(helper_value_check4_fail) ( void );
-extern void MC_(helper_value_check2_fail) ( void );
-extern void MC_(helper_value_check1_fail) ( void );
-extern void MC_(helper_value_check0_fail) ( void );
-
-
 /* Functions defined in mc_main.c */
+extern REGPARM(1) void MC_(helperc_complain_undef) ( HWord );
+
 extern REGPARM(2) void MC_(helperc_STOREV4) ( Addr, UInt );
 extern REGPARM(2) void MC_(helperc_STOREV2) ( Addr, UInt );
 extern REGPARM(2) void MC_(helperc_STOREV1) ( Addr, UInt );
index a8369cfadf8a9d56804c1ee47699e3c30527b399..67a5ade37ccc9ee6ab315dfcd0f2e4139710ff66 100644 (file)
@@ -721,10 +721,14 @@ void mc_set_perms (Addr a, UInt len, Bool rr, Bool ww, Bool xx)
 
 static void mc_post_regs_write_init ( void )
 {
+#if 0
    UInt i;
    for (i = FIRST_ARCH_REG; i <= LAST_ARCH_REG; i++)
       VG_(set_shadow_archreg)( i, VGM_WORD_VALID );
    VG_(set_shadow_eflags)( VGM_EFLAGS_VALID );
+#else
+   VG_(printf)("mc_post_regs_write_init()\n");
+#endif
 }
 
 static void mc_post_reg_write(ThreadId tid, UInt reg)
@@ -1102,6 +1106,11 @@ void MC_(helperc_value_check4_fail) ( void )
    MC_(record_value_error) ( VG_(get_current_tid)(), 4 );
 }
 
+REGPARM(1) void MC_(helperc_complain_undef) ( HWord sz )
+{
+   MC_(record_value_error) ( VG_(get_current_tid)(), (Int)sz );
+}
+
 
 /* ---------------------------------------------------------------------
    FPU load and store checks, called from generated code.
@@ -1692,6 +1701,7 @@ void SK_(pre_clo_init)(void)
    VG_(init_post_reg_write_clientreq_return)  ( & mc_post_reg_write );
    VG_(init_post_reg_write_clientcall_return) ( & mc_post_reg_write_clientcall );
 
+#if 0
    /* Three compact slots taken up by stack memory helpers */
    VG_(register_compact_helper)((Addr) & MC_(helper_value_check4_fail));
    VG_(register_compact_helper)((Addr) & MC_(helper_value_check0_fail));
@@ -1707,6 +1717,7 @@ void SK_(pre_clo_init)(void)
    VG_(register_noncompact_helper)((Addr) & MC_(fpu_write_check));
    VG_(register_noncompact_helper)((Addr) & MC_(fpu_read_check));
    VG_(register_noncompact_helper)((Addr) & MC_(helper_value_check1_fail));
+#endif
 
    VGP_(register_profile_event) ( VgpSetMem,   "set-mem-perms" );
    VGP_(register_profile_event) ( VgpCheckMem, "check-mem-perms" );
index af01e6d4fb34c75bbd507dfa8069d857c6f90d07..abd0962a646e697a971ea864f083b71c3dd3f1d2 100644 (file)
@@ -259,7 +259,7 @@ Int SK_(get_Xreg_usage)(UInstr* u, Tag tag, Int* regs, Bool* isWrites)
 /*------------------------------------------------------------*/
 /*--- New instrumentation machinery.                       ---*/
 /*------------------------------------------------------------*/
-
+#if 0
 static
 TagOp get_Tag_ImproveOR_TQ ( Int sz )
 {
@@ -1634,25 +1634,586 @@ static void vg_cleanup ( UCodeBlock* cb )
    vg_propagate_definedness ( cb );
    vg_delete_redundant_SETVs ( cb );
 }
+#endif
+
 
+#undef dis
 
-/* Caller will print out final instrumented code if necessary;  we
-   print out intermediate instrumented code here if necessary. */
-UCodeBlock* SK_(instrument) ( UCodeBlock* cb, Addr not_used )
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+/* An atom is either an IRExpr_Const or an IRExpr_Tmp, as defined by
+   isAtom() in libvex_ir.h.  Because this instrumenter expects flat
+   input, most of this code deals in atoms.  Usefully, a value atom
+   always has a V-value which is also an atom: constants are shadowed
+   by constants, and temps are shadowed by the corresponding shadow
+   temporary. */
+typedef  IRExpr  IRAtom;
+
+/* Check that both args are atoms and are identically-kinded. */
+static Bool sameKindedAtoms ( IRAtom* a1, IRAtom* a2 )
 {
-   cb = memcheck_instrument ( cb );
-   if (MC_(clo_cleanup)) {
-      if (dis) {
-         VG_(pp_UCodeBlock) ( cb, "Unimproved instrumented UCode:" );
-         VG_(printf)("Instrumentation improvements:\n");
-      }
-      vg_cleanup(cb);
-      if (dis) VG_(printf)("\n");
+   if (a1->tag == Iex_Tmp && a1->tag == Iex_Tmp)
+      return True;
+   if (a1->tag == Iex_Const && a1->tag == Iex_Const)
+      return True;
+   return False;
+}
+
+/* Carries around state during memcheck instrumentation. */
+typedef
+   struct {
+      /* MODIFIED: the bb being constructed.  IRStmts are added. */
+      IRBB* bb;
+
+      /* MODIFIED: a table [0 .. #temps_in_original_bb-1] which maps
+         original temps to their current their current shadow temp.
+         Initially all entries are IRTemp_INVALID.  Entries are added
+         lazily since many original temps are not used due to
+         optimisation prior to instrumentation.  Note that floating
+         point original tmps are shadowed by integer tmps of the same
+         size, and Bit-typed original tmps are shadowed by the type
+         Ity_I8. */
+      IRTemp* tmpMap;
+      Int     n_originalTmps; /* for range checking */
+
+      /* READONLY: the guest layout */
+      VexGuestLayout* layout;
+      /* READONLY: the host word type.  Needed for constructing
+         arguments of type 'HWord' to be passed to helper functions.
+         Ity_I32 or Ity_I64 only. */
+      IRType hWordTy;
    }
-   return cb;
+   MCEnv;
+
+static Bool isOriginalAtom ( MCEnv* mce, IRAtom* a1 )
+{
+   if (a1->tag == Iex_Const)
+      return True;
+   if (a1->tag == Iex_Tmp && a1->Iex.Tmp.tmp < mce->n_originalTmps)
+      return True;
+   return False;
 }
 
-#undef dis
+static Bool isShadowAtom ( MCEnv* mce, IRAtom* a1 )
+{
+   if (a1->tag == Iex_Const)
+      return True;
+   if (a1->tag == Iex_Tmp && a1->Iex.Tmp.tmp >= mce->n_originalTmps)
+      return True;
+   return False;
+}
+
+
+
+
+/* Shadow state is always accessed using integer types.  This returns
+   an integer type with the same size (as per sizeofIRType) as the
+   given type. 
+*/
+static IRType shadowType ( IRType ty )
+{
+   switch (ty) {
+      case Ity_I32: return ty;
+      default: VG_(skin_panic)("memcheck:shadowType");
+   }
+}
+
+/* Find the tmp currently shadowing the given original tmp.  If none
+   so far exists, allocate one.  */
+static IRTemp findShadowTmp ( MCEnv* mce, IRTemp orig )
+{
+   sk_assert(orig < mce->n_originalTmps);
+   if (mce->tmpMap[orig] == INVALID_IRTEMP) {
+      mce->tmpMap[orig] 
+         = newIRTemp(mce->bb->tyenv, 
+                     shadowType(mce->bb->tyenv->types[orig]));
+   }
+   return mce->tmpMap[orig];
+}
+
+/* Allocate a new shadow for the given original tmp.  This means any
+   previous shadow is abandoned.  This is needed because it is
+   necessary to give a new value to a shadow once it has been tested
+   for undefinedness, but unfortunately IR's SSA property disallows
+   this.  Instead we must abandon the old shadow, allocate a new one
+   and use that instead. */
+static void newShadowTmp ( MCEnv* mce, IRTemp orig )
+{
+   sk_assert(orig < mce->n_originalTmps);
+   mce->tmpMap[orig] 
+      = newIRTemp(mce->bb->tyenv, 
+                  shadowType(mce->bb->tyenv->types[orig]));
+}
+
+
+static IRExpr* expr2vbits ( MCEnv* mce, IRExpr* e );
+
+#define assign(_bb,_tmp,_expr)   \
+   addStmtToIRBB((_bb), IRStmt_Tmp((_tmp),(_expr)))
+#define stmt(_bb,_stmt)    \
+   addStmtToIRBB((_bb), (_stmt))
+
+
+#define binop(_op, _arg1, _arg2) IRExpr_Binop((_op),(_arg1),(_arg2))
+#define unop(_op, _arg)          IRExpr_Unop((_op),(_arg))
+#define mkU32(_n)                IRExpr_Const(IRConst_U32(_n))
+#define mkU64(_n)                IRExpr_Const(IRConst_U64(_n))
+#define mkexpr(_tmp)             IRExpr_Tmp((_tmp))
+
+static IRAtom* assignNew ( MCEnv* mce, IRType ty, IRExpr* e ) {
+   IRTemp t = newIRTemp(mce->bb->tyenv, ty);
+   assign(mce->bb, t, e);
+   return mkexpr(t);
+}
+
+/* Should only be supplied shadow types (I8/I16/I32/UI64). */
+static IRExpr* definedOfType ( IRType ty ) {
+   switch (ty) {
+      case Ity_I32: return mkU32(0);
+      default:      VG_(skin_panic)("memcheck:definedOfType");
+   }
+}
+
+static IRAtom* mkUifU32 ( MCEnv* mce, IRAtom* a1, IRAtom* a2 ) {
+   sk_assert(isShadowAtom(mce,a1));
+   sk_assert(isShadowAtom(mce,a2));
+   return assignNew(mce, Ity_I32, binop(Iop_Or32, a1, a2));
+}
+
+static IRExpr* mkLeft32 ( MCEnv* mce, IRAtom* a1 ) {
+   sk_assert(isShadowAtom(mce,a1));
+   /* It's safe to duplicate a1 since it's only an atom */
+   return assignNew(mce, Ity_I32, 
+                         binop(Iop_Or32, a1, unop(Iop_Neg32, a1)));
+}
+
+static void setHelperAnns ( MCEnv* mce, IRDirty* di ) {
+   di->nFxState = 2;
+   di->fxState[0].fx     = Ifx_Read;
+   di->fxState[0].offset = mce->layout->offset_SP;
+   di->fxState[0].size   = mce->layout->sizeof_SP;
+   di->fxState[1].fx     = Ifx_Read;
+   di->fxState[1].offset = mce->layout->offset_IP;
+   di->fxState[1].size   = mce->layout->sizeof_IP;
+}
+
+static IRAtom* genPCastTo( MCEnv* mce, IRAtom* vbits, IRType dst_ty ) {
+   /* First of all, collapse vbits down to a single bit. */
+   sk_assert(isShadowAtom(mce,vbits));
+   IRType  ty   = typeOfIRExpr(mce->bb->tyenv, vbits);
+   IRAtom* tmp1 = NULL;
+   switch (ty) {
+      case Ity_I32: 
+         tmp1 = assignNew(mce, Ity_Bit, binop(Iop_CmpNE32, vbits, mkU32(0)));
+         break;
+      default:
+         VG_(skin_panic)("genPCastTo(1)");
+   }
+   sk_assert(tmp1);
+   /* Now widen up to the dst type. */
+   switch (dst_ty) {
+      case Ity_Bit:
+         return tmp1;
+      case Ity_I32: 
+         return assignNew(mce, Ity_I32, unop(Iop_1Sto32, tmp1));
+      default: 
+         ppIRType(dst_ty);
+         VG_(skin_panic)("genPCastTo(2)");
+   }
+}
+
+/* Examine the always-defined sections declared in layout to see if
+   the (offset,size) section is within one.  Note, is is an error to
+   partially fall into such a region: (offset,size) should either be
+   completely in such a region or completely not-in such a region. 
+*/
+static Bool isAlwaysDefd ( MCEnv* mce, Int offset, Int size )
+{
+   Int minoffD, maxoffD, i;
+   Int minoff = offset;
+   Int maxoff = minoff + size - 1;
+   sk_assert((minoff & ~0xFFFF) == 0);
+   sk_assert((maxoff & ~0xFFFF) == 0);
+
+   for (i = 0; i < mce->layout->n_alwaysDefd; i++) {
+      minoffD = mce->layout->alwaysDefd[i].offset;
+      maxoffD = minoffD + mce->layout->alwaysDefd[i].size - 1;
+      sk_assert((minoffD & ~0xFFFF) == 0);
+      sk_assert((maxoffD & ~0xFFFF) == 0);
+
+      if (maxoff < minoffD || maxoffD < minoff)
+         continue; /* no overlap */
+      if (minoff >= minoffD && maxoff <= maxoffD)
+         return True; /* completely contained in an always-defd section */
+
+      VG_(skin_panic)("memcheck:isAlwaysDefd:partial overlap");
+   }
+   return False; /* could not find any containing section */
+}
+
+
+static
+void complainIfUndefined ( MCEnv* mce, IRAtom* vatom, IRAtom* atom )
+{
+   /* vatom is required to be the V-bits shadow for atom. */
+   sk_assert(isOriginalAtom(mce, atom));
+   sk_assert(isShadowAtom(mce, vatom));
+   sk_assert(sameKindedAtoms(atom, vatom));
+
+   IRType ty = typeOfIRExpr(mce->bb->tyenv, vatom);
+   Int    sz = sizeofIRType(ty);
+
+   IRExpr* cond = genPCastTo( mce, vatom, Ity_Bit );
+   /* cond will be 0 if all defined, and 1 if any not defined. */
+
+   IRDirty* di 
+      = unsafeIRDirty_0_N( 1/*regparms*/, 
+                           "MC_(helperc_complain_undef)",
+                           &MC_(helperc_complain_undef),
+                           mkIRExprVec_1( mkIRExpr_HWord(sz) ));
+   di->guard = cond;
+   setHelperAnns( mce, di );
+   stmt( mce->bb, IRStmt_Dirty(di));
+
+   /* Set the shadow tmp to be defined.  First, update the
+      orig->shadow tmp mapping to reflect the fact that this shadow is
+      getting a new value. */
+   sk_assert(isAtom(vatom));
+   /* sameKindedAtoms ... */
+   if (vatom->tag == Iex_Tmp) {
+      sk_assert(atom->tag == Iex_Tmp);
+      newShadowTmp(mce, atom->Iex.Tmp.tmp);
+      assign(mce->bb, findShadowTmp(mce, atom->Iex.Tmp.tmp), 
+                      definedOfType(ty));
+   }
+}
+
+
+/* Generate into bb suitable actions to shadow this Put.  If the state
+   slice is marked 'always defined', emit a complaint if any of the
+   supplied V bits are 1, and do not modify shadow state.  Otherwise,
+   write the supplied V bits to the shadow state. */
+static
+void do_shadow_PUT ( MCEnv* mce, Int offset, IRAtom* vatom, IRAtom* atom )
+{
+   /* vatom is required to be the V-bits shadow for atom. */
+   sk_assert(isOriginalAtom(mce, atom));
+   sk_assert(isShadowAtom(mce, vatom));
+   sk_assert(sameKindedAtoms(atom, vatom));
+   IRType ty = typeOfIRExpr(mce->bb->tyenv, vatom);
+   sk_assert(ty != Ity_Bit);
+   if (isAlwaysDefd(mce, offset, sizeofIRType(ty))) {
+      /* emit code to emit a complaint if any of the vbits are 1. */
+      complainIfUndefined(mce, vatom, atom);
+   } else {
+      /* Do a plain shadow Put. */
+      stmt( mce->bb, IRStmt_Put( offset + mce->layout->total_sizeB, vatom ) );
+   }
+}
+
+
+/* Return an expression which contains the V bits corresponding to the
+   given GET (passed in in pieces). 
+*/
+static 
+IRExpr* shadow_GET ( MCEnv* mce, Int offset, IRType ty )
+{
+   IRType tyS = shadowType(ty);
+   sk_assert(ty != Ity_Bit);
+   if (isAlwaysDefd(mce, offset, sizeofIRType(ty))) {
+      /* Always defined, return all zeroes of the relevant type */
+      return definedOfType(tyS);
+   } else {
+      /* return a cloned version of the Get that refers to the shadow
+         area. */
+      return IRExpr_Get( offset + mce->layout->total_sizeB, tyS );
+   }
+}
+
+
+/* Return an expression which contains the V bits corresponding to the
+   given GETI (passed in in pieces). 
+*/
+static
+IRExpr* shadow_GETI ( MCEnv* mce, IRArray* descr, IRAtom* ix, Int bias )
+{
+   IRType ty   = descr->elemTy;
+   IRType tyS  = shadowType(ty);
+   Int arrSize = descr->nElems * sizeofIRType(ty);
+   sk_assert(ty != Ity_Bit);
+   sk_assert(isOriginalAtom(mce,ix));
+   if (isAlwaysDefd(mce, descr->base, arrSize)) {
+      /* Always defined, return all zeroes of the relevant type */
+      return definedOfType(tyS);
+   } else {
+      /* return a cloned version of the Get that refers to the shadow
+         area. */
+      IRArray* new_descr 
+         = mkIRArray( descr->base + mce->layout->total_sizeB, 
+                      tyS, descr->nElems);
+      return IRExpr_GetI( new_descr, ix, bias );
+   }
+}
+
+
+static 
+IRExpr* expr2vbits_Binop ( MCEnv* mce,
+                           IROp op,
+                           IRExpr* atom1, IRExpr* atom2,
+                           IRExpr* vatom1, IRExpr* vatom2 )
+{
+   IRAtom* ta;
+   sk_assert(isOriginalAtom(mce,atom1));
+   sk_assert(isOriginalAtom(mce,atom2));
+   sk_assert(isShadowAtom(mce,vatom1));
+   sk_assert(isShadowAtom(mce,vatom2));
+   sk_assert(sameKindedAtoms(atom1,vatom1));
+   sk_assert(sameKindedAtoms(atom2,vatom2));
+   switch (op) {
+      case Iop_Sub32:
+      case Iop_Add32:
+         ta = mkUifU32(mce,vatom1,vatom2);
+         return mkLeft32(mce, ta);
+      default:
+         ppIROp(op);
+         VG_(skin_panic)("memcheck:expr2vbits_Binop");
+   }
+}
+
+
+static
+IRExpr* expr2vbits_LDle ( MCEnv* mce, IRType ty, IRAtom* addr )
+{
+   void*    helper;
+   Char*    hname;
+   IRDirty* di;
+   IRTemp   datavbits;
+
+   sk_assert(isOriginalAtom(mce,addr));
+
+   /* First, emit a definedness test for the address.  This also sets
+      the address (shadow) to 'defined' following the test. */
+   complainIfUndefined( mce, expr2vbits( mce, addr ), addr );
+
+   /* Now cook up a call to the relevant helper function, to read the
+      data V bits from shadow memory. */
+   switch (ty) {
+      case Ity_I32: helper = &MC_(helperc_LOADV4);
+                    hname = "MC_(helperc_LOADV4)";
+                    break;
+      case Ity_I16: helper = &MC_(helperc_LOADV2);
+                    hname = "MC_(helperc_LOADV2)";
+                    break;
+      case Ity_I8:  helper = &MC_(helperc_LOADV1);
+                    hname = "MC_(helperc_LOADV1)";
+                    break;
+      default:      VG_(skin_panic)("memcheck:do_shadow_LDle");
+   }
+
+   /* We need to have a place to park the V bits we're just about to
+      read. */
+   datavbits = newIRTemp(mce->bb->tyenv, ty);
+   di = unsafeIRDirty_1_N( datavbits, 
+                           1/*regparms*/, hname, helper, mkIRExprVec_1( addr ));
+   setHelperAnns( mce, di );
+   stmt( mce->bb, IRStmt_Dirty(di) );
+
+   return mkexpr(datavbits);
+}
+
+
+static
+IRExpr* expr2vbits ( MCEnv* mce, IRExpr* e )
+{
+   IRExpr *v1, *v2;
+   switch (e->tag) {
+
+      case Iex_Get:
+         return shadow_GET( mce, e->Iex.Get.offset, e->Iex.Get.ty );
+
+      case Iex_Tmp:
+         return IRExpr_Tmp( findShadowTmp(mce, e->Iex.Tmp.tmp) );
+
+      case Iex_Const:
+         return definedOfType(shadowType(typeOfIRExpr(mce->bb->tyenv, e)));
+
+      case Iex_Binop:
+         v1 = expr2vbits( mce, e->Iex.Binop.arg1);
+         v2 = expr2vbits( mce, e->Iex.Binop.arg2);
+         return expr2vbits_Binop(
+                   mce,
+                   e->Iex.Binop.op,
+                   e->Iex.Binop.arg1, e->Iex.Binop.arg2,
+                   v1, v2 
+                );
+
+      case Iex_LDle:
+         return expr2vbits_LDle( mce, e->Iex.LDle.ty, e->Iex.LDle.addr );
+
+      default: 
+         VG_(printf)("\n");
+         ppIRExpr(e);
+         VG_(printf)("\n");
+         VG_(skin_panic)("memcheck: expr2vbits");
+   }
+}
+
+static
+IRExpr* zwidenToHostWord ( MCEnv* mce, IRAtom* vatom )
+{
+   /* vatom is vbits-value and as such can only have an integer
+      type. */
+   sk_assert(isShadowAtom(mce,vatom));
+   IRType tyH = mce->hWordTy;
+   IRType ty  = typeOfIRExpr(mce->bb->tyenv, vatom);
+   switch (ty) {
+      case Ity_I32:
+         if (tyH == Ity_I32) return vatom;
+         goto unhandled;
+      unhandled:
+         default: VG_(skin_panic)("zwidenToHostWord");
+   }
+}
+
+static 
+void do_shadow_STle ( MCEnv* mce, IRAtom* addr, IRAtom* data )
+{
+   IRType   ty;
+   IRDirty* di;
+   IRExpr*  datavbits;
+   void*    helper = NULL;
+   Char*    hname = NULL;
+
+   ty = shadowType(typeOfIRExpr(mce->bb->tyenv, data));
+
+   sk_assert(isOriginalAtom(mce,addr));
+   sk_assert(isOriginalAtom(mce,data));
+
+   /* First, emit a definedness test for the address.  This also sets
+      the address (shadow) to 'defined' following the test. */
+   complainIfUndefined( mce, expr2vbits( mce, addr ), addr);
+
+   /* Now cook up a call to the relevant helper function, to write the
+      data V bits into shadow memory. */
+   datavbits = expr2vbits( mce, data );
+   switch (ty) {
+      case Ity_I32: helper = &MC_(helperc_STOREV4);
+                    hname = "MC_(helperc_STOREV4)";
+                    break;
+      case Ity_I16: helper = &MC_(helperc_STOREV2);
+                    hname = "MC_(helperc_STOREV2)";
+                    break;
+      case Ity_I8:  helper = &MC_(helperc_STOREV1);
+                    hname = "MC_(helperc_STOREV1)";
+                    break;
+      default:      VG_(skin_panic)("memcheck:do_shadow_STle");
+   }
+
+   di = unsafeIRDirty_0_N( 
+           2/*regparms*/, hname, helper, 
+           mkIRExprVec_2( addr,
+                          zwidenToHostWord( mce, datavbits )));
+   setHelperAnns( mce, di );
+   stmt( mce->bb, IRStmt_Dirty(di) );
+}
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+IRBB* SK_(instrument) ( IRBB* bb_in, VexGuestLayout* layout, IRType hWordTy )
+{
+   Bool verbose = True; 
+
+   Int i, j, n_types, first_stmt;
+   IRStmt* st;
+   MCEnv mce;
+
+   /* Set up BB */
+   IRBB* bb     = emptyIRBB();
+   bb->tyenv    = dopyIRTypeEnv(bb_in->tyenv);
+   bb->next     = dopyIRExpr(bb_in->next);
+   bb->jumpkind = bb_in->jumpkind;
+
+   /* Allocate shadow IRTemps.
+      0       ..   n_types-1 are the original IRTemps.
+      n_types .. 2*n_types-1 are the shadow IRTemps. 
+   */
+   n_types = bb->tyenv->types_used;
+   for (i = 0; i < n_types; i++)
+      newIRTemp(bb->tyenv, shadowType(bb->tyenv->types[i]));
+
+   /* Set up the running environment.  Only .bb is modified as we go
+      along. */
+   mce.bb             = bb;
+   mce.layout         = layout;
+   mce.n_originalTmps = n_types;
+   mce.hWordTy        = hWordTy;
+   mce.tmpMap         = LibVEX_Alloc(n_types * sizeof(IRTemp));
+   for (i = 0; i < n_types; i++)
+      mce.tmpMap[i] = INVALID_IRTEMP;
+
+   for (i = 0; i <  bb_in->stmts_used; i++) {
+      st = bb_in->stmts[i];
+      if (!st) continue;
+
+      sk_assert(isFlatIRStmt(st));
+      first_stmt = bb->stmts_used;
+
+      if (verbose) {
+         ppIRStmt(st);
+         VG_(printf)("\n\n");
+      }
+
+      switch (st->tag) {
+
+         case Ist_Tmp:
+            assign( bb, findShadowTmp(&mce, st->Ist.Tmp.tmp), 
+                        expr2vbits( &mce, st->Ist.Tmp.data) );
+            break;
+
+         case Ist_Put:
+            do_shadow_PUT( &mce, 
+                           st->Ist.Put.offset,
+                           expr2vbits( &mce, st->Ist.Put.data),
+                           st->Ist.Put.data );
+            break;
+
+         case Ist_STle:
+            do_shadow_STle( &mce, st->Ist.STle.addr, st->Ist.STle.data );
+            break;
+
+         default:
+            VG_(printf)("\n");
+            ppIRStmt(st);
+            VG_(printf)("\n");
+            VG_(skin_panic)("memcheck: unhandled IRStmt");
+
+      } /* switch (st->tag) */
+
+      if (verbose) {
+         for (j = first_stmt; j < bb->stmts_used; j++) {
+            VG_(printf)("   ");
+            ppIRStmt(bb->stmts[j]);
+            VG_(printf)("\n");
+         }
+         VG_(printf)("\n");
+      }
+
+      addStmtToIRBB(bb, st);
+
+   }
+
+   /* Uh, ok.  Now we need to complain if the jump target is
+      undefined. */
+   complainIfUndefined( &mce, expr2vbits( &mce, bb->next ), bb->next );
+
+   return bb;
+
+#  undef IRSHADOW
+}
 
 /*--------------------------------------------------------------------*/
 /*--- end                                           mc_translate.c ---*/