Ist_Store, IRLoadG, IRStoreG, LLSC, CAS and Dirty memory
loads/stores) was re-checked 11 May 2013. */
+
/*------------------------------------------------------------*/
/*--- Forward decls ---*/
/*------------------------------------------------------------*/
static IRExpr *i128_const_zero(void);
+
/*------------------------------------------------------------*/
/*--- Memcheck running state, and tmp management. ---*/
/*------------------------------------------------------------*/
}
}
+
/*------------------------------------------------------------*/
/*--- Generate shadow stmts from all kinds of IRStmts. ---*/
/*------------------------------------------------------------*/
/*------------------------------------------------------------*/
-/*--- Memcheck main ---*/
+/*--- Origin tracking stuff ---*/
/*------------------------------------------------------------*/
-static void schemeS ( MCEnv* mce, IRStmt* st );
-
-static Bool isBogusAtom ( IRAtom* at )
+/* Almost identical to findShadowTmpV. */
+static IRTemp findShadowTmpB ( MCEnv* mce, IRTemp orig )
{
- ULong n = 0;
- IRConst* con;
- tl_assert(isIRAtom(at));
- if (at->tag == Iex_RdTmp)
- return False;
- tl_assert(at->tag == Iex_Const);
- con = at->Iex.Const.con;
- switch (con->tag) {
- case Ico_U1: return False;
- case Ico_U8: n = (ULong)con->Ico.U8; break;
- case Ico_U16: n = (ULong)con->Ico.U16; break;
- case Ico_U32: n = (ULong)con->Ico.U32; break;
- case Ico_U64: n = (ULong)con->Ico.U64; break;
- case Ico_F32: return False;
- case Ico_F64: return False;
- case Ico_F32i: return False;
- case Ico_F64i: return False;
- case Ico_V128: return False;
- case Ico_V256: return False;
- default: ppIRExpr(at); tl_assert(0);
+ TempMapEnt* ent;
+ /* VG_(indexXA) range-checks 'orig', hence no need to check
+ here. */
+ ent = (TempMapEnt*)VG_(indexXA)( mce->tmpMap, (Word)orig );
+ tl_assert(ent->kind == Orig);
+ if (ent->shadowB == IRTemp_INVALID) {
+ IRTemp tmpB
+ = newTemp( mce, Ity_I32, BSh );
+ /* newTemp may cause mce->tmpMap to resize, hence previous results
+ from VG_(indexXA) are invalid. */
+ ent = (TempMapEnt*)VG_(indexXA)( mce->tmpMap, (Word)orig );
+ tl_assert(ent->kind == Orig);
+ tl_assert(ent->shadowB == IRTemp_INVALID);
+ ent->shadowB = tmpB;
}
- /* VG_(printf)("%llx\n", n); */
- return (/*32*/ n == 0xFEFEFEFFULL
- /*32*/ || n == 0x80808080ULL
- /*32*/ || n == 0x7F7F7F7FULL
- /*32*/ || n == 0x7EFEFEFFULL
- /*32*/ || n == 0x81010100ULL
- /*64*/ || n == 0xFFFFFFFFFEFEFEFFULL
- /*64*/ || n == 0xFEFEFEFEFEFEFEFFULL
- /*64*/ || n == 0x0000000000008080ULL
- /*64*/ || n == 0x8080808080808080ULL
- /*64*/ || n == 0x0101010101010101ULL
- );
+ return ent->shadowB;
}
-static Bool checkForBogusLiterals ( /*FLAT*/ IRStmt* st )
+static IRAtom* gen_maxU32 ( MCEnv* mce, IRAtom* b1, IRAtom* b2 )
{
- Int i;
- IRExpr* e;
- IRDirty* d;
- IRCAS* cas;
- switch (st->tag) {
- case Ist_WrTmp:
- e = st->Ist.WrTmp.data;
- switch (e->tag) {
- case Iex_Get:
- case Iex_RdTmp:
- return False;
- case Iex_Const:
- return isBogusAtom(e);
- case Iex_Unop:
- return isBogusAtom(e->Iex.Unop.arg)
- || e->Iex.Unop.op == Iop_GetMSBs8x16;
- case Iex_GetI:
- return isBogusAtom(e->Iex.GetI.ix);
- case Iex_Binop:
- return isBogusAtom(e->Iex.Binop.arg1)
- || isBogusAtom(e->Iex.Binop.arg2);
- case Iex_Triop:
- return isBogusAtom(e->Iex.Triop.details->arg1)
- || isBogusAtom(e->Iex.Triop.details->arg2)
- || isBogusAtom(e->Iex.Triop.details->arg3);
- case Iex_Qop:
- return isBogusAtom(e->Iex.Qop.details->arg1)
- || isBogusAtom(e->Iex.Qop.details->arg2)
- || isBogusAtom(e->Iex.Qop.details->arg3)
- || isBogusAtom(e->Iex.Qop.details->arg4);
- case Iex_ITE:
- return isBogusAtom(e->Iex.ITE.cond)
- || isBogusAtom(e->Iex.ITE.iftrue)
- || isBogusAtom(e->Iex.ITE.iffalse);
- case Iex_Load:
- return isBogusAtom(e->Iex.Load.addr);
- case Iex_CCall:
- for (i = 0; e->Iex.CCall.args[i]; i++)
- if (isBogusAtom(e->Iex.CCall.args[i]))
- return True;
- return False;
- default:
- goto unhandled;
- }
- case Ist_Dirty:
- d = st->Ist.Dirty.details;
- for (i = 0; d->args[i]; i++) {
- IRAtom* atom = d->args[i];
- if (LIKELY(!is_IRExpr_VECRET_or_GSPTR(atom))) {
- if (isBogusAtom(atom))
- return True;
- }
- }
- if (isBogusAtom(d->guard))
- return True;
- if (d->mAddr && isBogusAtom(d->mAddr))
- return True;
- return False;
- case Ist_Put:
- return isBogusAtom(st->Ist.Put.data);
- case Ist_PutI:
- return isBogusAtom(st->Ist.PutI.details->ix)
- || isBogusAtom(st->Ist.PutI.details->data);
- case Ist_Store:
- return isBogusAtom(st->Ist.Store.addr)
- || isBogusAtom(st->Ist.Store.data);
- case Ist_StoreG: {
- IRStoreG* sg = st->Ist.StoreG.details;
- return isBogusAtom(sg->addr) || isBogusAtom(sg->data)
- || isBogusAtom(sg->guard);
- }
- case Ist_LoadG: {
- IRLoadG* lg = st->Ist.LoadG.details;
- return isBogusAtom(lg->addr) || isBogusAtom(lg->alt)
- || isBogusAtom(lg->guard);
- }
- case Ist_Exit:
- return isBogusAtom(st->Ist.Exit.guard);
- case Ist_AbiHint:
- return isBogusAtom(st->Ist.AbiHint.base)
- || isBogusAtom(st->Ist.AbiHint.nia);
- case Ist_NoOp:
- case Ist_IMark:
- case Ist_MBE:
- return False;
- case Ist_CAS:
- cas = st->Ist.CAS.details;
- return isBogusAtom(cas->addr)
- || (cas->expdHi ? isBogusAtom(cas->expdHi) : False)
- || isBogusAtom(cas->expdLo)
- || (cas->dataHi ? isBogusAtom(cas->dataHi) : False)
- || isBogusAtom(cas->dataLo);
- case Ist_LLSC:
- return isBogusAtom(st->Ist.LLSC.addr)
- || (st->Ist.LLSC.storedata
- ? isBogusAtom(st->Ist.LLSC.storedata)
- : False);
- default:
- unhandled:
- ppIRStmt(st);
- VG_(tool_panic)("hasBogusLiterals");
- }
+ return assignNew( 'B', mce, Ity_I32, binop(Iop_Max32U, b1, b2) );
}
-IRSB* MC_(instrument) ( VgCallbackClosure* closure,
- IRSB* sb_in,
- const VexGuestLayout* layout,
- const VexGuestExtents* vge,
- const VexArchInfo* archinfo_host,
- IRType gWordTy, IRType hWordTy )
+/* Make a guarded origin load, with no special handling in the
+ didn't-happen case. A GUARD of NULL is assumed to mean "always
+ True".
+
+ Generate IR to do a shadow origins load from BASEADDR+OFFSET and
+ return the otag. The loaded size is SZB. If GUARD evaluates to
+ False at run time then the returned otag is zero.
+*/
+static IRAtom* gen_guarded_load_b ( MCEnv* mce, Int szB,
+ IRAtom* baseaddr,
+ Int offset, IRExpr* guard )
{
- Bool verboze = 0||False;
- Int i, j, first_stmt;
- IRStmt* st;
- MCEnv mce;
- IRSB* sb_out;
+ void* hFun;
+ const HChar* hName;
+ IRTemp bTmp;
+ IRDirty* di;
+ IRType aTy = typeOfIRExpr( mce->sb->tyenv, baseaddr );
+ IROp opAdd = aTy == Ity_I32 ? Iop_Add32 : Iop_Add64;
+ IRAtom* ea = baseaddr;
+ if (offset != 0) {
+ IRAtom* off = aTy == Ity_I32 ? mkU32( offset )
+ : mkU64( (Long)(Int)offset );
+ ea = assignNew( 'B', mce, aTy, binop(opAdd, ea, off));
+ }
+ bTmp = newTemp(mce, mce->hWordTy, BSh);
- if (gWordTy != hWordTy) {
- /* We don't currently support this case. */
- VG_(tool_panic)("host/guest word size mismatch");
+ switch (szB) {
+ case 1: hFun = (void*)&MC_(helperc_b_load1);
+ hName = "MC_(helperc_b_load1)";
+ break;
+ case 2: hFun = (void*)&MC_(helperc_b_load2);
+ hName = "MC_(helperc_b_load2)";
+ break;
+ case 4: hFun = (void*)&MC_(helperc_b_load4);
+ hName = "MC_(helperc_b_load4)";
+ break;
+ case 8: hFun = (void*)&MC_(helperc_b_load8);
+ hName = "MC_(helperc_b_load8)";
+ break;
+ case 16: hFun = (void*)&MC_(helperc_b_load16);
+ hName = "MC_(helperc_b_load16)";
+ break;
+ case 32: hFun = (void*)&MC_(helperc_b_load32);
+ hName = "MC_(helperc_b_load32)";
+ break;
+ default:
+ VG_(printf)("mc_translate.c: gen_load_b: unhandled szB == %d\n", szB);
+ tl_assert(0);
+ }
+ di = unsafeIRDirty_1_N(
+ bTmp, 1/*regparms*/, hName, VG_(fnptr_to_fnentry)( hFun ),
+ mkIRExprVec_1( ea )
+ );
+ if (guard) {
+ di->guard = guard;
+ /* Ideally the didn't-happen return value here would be
+ all-zeroes (unknown-origin), so it'd be harmless if it got
+ used inadvertently. We slum it out with the IR-mandated
+ default value (0b01 repeating, 0x55 etc) as that'll probably
+ trump all legitimate otags via Max32, and it's pretty
+ obviously bogus. */
+ }
+ /* no need to mess with any annotations. This call accesses
+ neither guest state nor guest memory. */
+ stmt( 'B', mce, IRStmt_Dirty(di) );
+ if (mce->hWordTy == Ity_I64) {
+ /* 64-bit host */
+ IRTemp bTmp32 = newTemp(mce, Ity_I32, BSh);
+ assign( 'B', mce, bTmp32, unop(Iop_64to32, mkexpr(bTmp)) );
+ return mkexpr(bTmp32);
+ } else {
+ /* 32-bit host */
+ return mkexpr(bTmp);
}
+}
- /* Check we're not completely nuts */
- tl_assert(sizeof(UWord) == sizeof(void*));
- tl_assert(sizeof(Word) == sizeof(void*));
- tl_assert(sizeof(Addr) == sizeof(void*));
- tl_assert(sizeof(ULong) == 8);
- tl_assert(sizeof(Long) == 8);
- tl_assert(sizeof(UInt) == 4);
- tl_assert(sizeof(Int) == 4);
- tl_assert(MC_(clo_mc_level) >= 1 && MC_(clo_mc_level) <= 3);
-
- /* Set up SB */
- sb_out = deepCopyIRSBExceptStmts(sb_in);
-
- /* Set up the running environment. Both .sb and .tmpMap are
- modified as we go along. Note that tmps are added to both
- .sb->tyenv and .tmpMap together, so the valid index-set for
- those two arrays should always be identical. */
- VG_(memset)(&mce, 0, sizeof(mce));
- mce.sb = sb_out;
- mce.trace = verboze;
- mce.layout = layout;
- mce.hWordTy = hWordTy;
- mce.bogusLiterals = False;
+/* Generate IR to do a shadow origins load from BASEADDR+OFFSET. The
+ loaded size is SZB. The load is regarded as unconditional (always
+ happens).
+*/
+static IRAtom* gen_load_b ( MCEnv* mce, Int szB, IRAtom* baseaddr,
+ Int offset )
+{
+ return gen_guarded_load_b(mce, szB, baseaddr, offset, NULL/*guard*/);
+}
- /* Do expensive interpretation for Iop_Add32 and Iop_Add64 on
- Darwin. 10.7 is mostly built with LLVM, which uses these for
- bitfield inserts, and we get a lot of false errors if the cheap
- interpretation is used, alas. Could solve this much better if
- we knew which of such adds came from x86/amd64 LEA instructions,
- since these are the only ones really needing the expensive
- interpretation, but that would require some way to tag them in
- the _toIR.c front ends, which is a lot of faffing around. So
- for now just use the slow and blunt-instrument solution. */
- mce.useLLVMworkarounds = False;
-# if defined(VGO_darwin)
- mce.useLLVMworkarounds = True;
-# endif
- mce.tmpMap = VG_(newXA)( VG_(malloc), "mc.MC_(instrument).1", VG_(free),
- sizeof(TempMapEnt));
- VG_(hintSizeXA) (mce.tmpMap, sb_in->tyenv->types_used);
- for (i = 0; i < sb_in->tyenv->types_used; i++) {
- TempMapEnt ent;
- ent.kind = Orig;
- ent.shadowV = IRTemp_INVALID;
- ent.shadowB = IRTemp_INVALID;
- VG_(addToXA)( mce.tmpMap, &ent );
- }
- tl_assert( VG_(sizeXA)( mce.tmpMap ) == sb_in->tyenv->types_used );
+/* The most general handler for guarded origin loads. A GUARD of NULL
+ is assumed to mean "always True".
- if (MC_(clo_expensive_definedness_checks)) {
- /* For expensive definedness checking skip looking for bogus
- literals. */
- mce.bogusLiterals = True;
- } else {
- /* Make a preliminary inspection of the statements, to see if there
- are any dodgy-looking literals. If there are, we generate
- extra-detailed (hence extra-expensive) instrumentation in
- places. Scan the whole bb even if dodgyness is found earlier,
- so that the flatness assertion is applied to all stmts. */
- Bool bogus = False;
+ Generate IR to do a shadow origin load from ADDR+BIAS and return
+ the B bits. The loaded type is TY. If GUARD evaluates to False at
+ run time then the returned B bits are simply BALT instead.
+*/
+static
+IRAtom* expr2ori_Load_guarded_General ( MCEnv* mce,
+ IRType ty,
+ IRAtom* addr, UInt bias,
+ IRAtom* guard, IRAtom* balt )
+{
+ /* If the guard evaluates to True, this will hold the loaded
+ origin. If the guard evaluates to False, this will be zero,
+ meaning "unknown origin", in which case we will have to replace
+ it using an ITE below. */
+ IRAtom* iftrue
+ = assignNew('B', mce, Ity_I32,
+ gen_guarded_load_b(mce, sizeofIRType(ty),
+ addr, bias, guard));
+ /* These are the bits we will return if the load doesn't take
+ place. */
+ IRAtom* iffalse
+ = balt;
+ /* Prepare the cond for the ITE. Convert a NULL cond into
+ something that iropt knows how to fold out later. */
+ IRAtom* cond
+ = guard == NULL ? mkU1(1) : guard;
+ /* And assemble the final result. */
+ return assignNew('B', mce, Ity_I32, IRExpr_ITE(cond, iftrue, iffalse));
+}
- for (i = 0; i < sb_in->stmts_used; i++) {
- st = sb_in->stmts[i];
- tl_assert(st);
- tl_assert(isFlatIRStmt(st));
- if (!bogus) {
- bogus = checkForBogusLiterals(st);
- if (0 && bogus) {
- VG_(printf)("bogus: ");
- ppIRStmt(st);
- VG_(printf)("\n");
- }
- if (bogus) break;
- }
- }
- mce.bogusLiterals = bogus;
+/* Generate a shadow origins store. guard :: Ity_I1 controls whether
+ the store really happens; NULL means it unconditionally does. */
+static void gen_store_b ( MCEnv* mce, Int szB,
+ IRAtom* baseaddr, Int offset, IRAtom* dataB,
+ IRAtom* guard )
+{
+ void* hFun;
+ const HChar* hName;
+ IRDirty* di;
+ IRType aTy = typeOfIRExpr( mce->sb->tyenv, baseaddr );
+ IROp opAdd = aTy == Ity_I32 ? Iop_Add32 : Iop_Add64;
+ IRAtom* ea = baseaddr;
+ if (guard) {
+ tl_assert(isOriginalAtom(mce, guard));
+ tl_assert(typeOfIRExpr(mce->sb->tyenv, guard) == Ity_I1);
}
-
- /* Copy verbatim any IR preamble preceding the first IMark */
-
- tl_assert(mce.sb == sb_out);
- tl_assert(mce.sb != sb_in);
-
- i = 0;
- while (i < sb_in->stmts_used && sb_in->stmts[i]->tag != Ist_IMark) {
-
- st = sb_in->stmts[i];
- tl_assert(st);
- tl_assert(isFlatIRStmt(st));
-
- stmt( 'C', &mce, sb_in->stmts[i] );
- i++;
+ if (offset != 0) {
+ IRAtom* off = aTy == Ity_I32 ? mkU32( offset )
+ : mkU64( (Long)(Int)offset );
+ ea = assignNew( 'B', mce, aTy, binop(opAdd, ea, off));
}
+ if (mce->hWordTy == Ity_I64)
+ dataB = assignNew( 'B', mce, Ity_I64, unop(Iop_32Uto64, dataB));
- /* Nasty problem. IR optimisation of the pre-instrumented IR may
- cause the IR following the preamble to contain references to IR
- temporaries defined in the preamble. Because the preamble isn't
- instrumented, these temporaries don't have any shadows.
- Nevertheless uses of them following the preamble will cause
- memcheck to generate references to their shadows. End effect is
- to cause IR sanity check failures, due to references to
- non-existent shadows. This is only evident for the complex
- preambles used for function wrapping on TOC-afflicted platforms
- (ppc64-linux).
-
- The following loop therefore scans the preamble looking for
- assignments to temporaries. For each one found it creates an
- assignment to the corresponding (V) shadow temp, marking it as
- 'defined'. This is the same resulting IR as if the main
- instrumentation loop before had been applied to the statement
- 'tmp = CONSTANT'.
-
- Similarly, if origin tracking is enabled, we must generate an
- assignment for the corresponding origin (B) shadow, claiming
- no-origin, as appropriate for a defined value.
- */
- for (j = 0; j < i; j++) {
- if (sb_in->stmts[j]->tag == Ist_WrTmp) {
- /* findShadowTmpV checks its arg is an original tmp;
- no need to assert that here. */
- IRTemp tmp_o = sb_in->stmts[j]->Ist.WrTmp.tmp;
- IRTemp tmp_v = findShadowTmpV(&mce, tmp_o);
- IRType ty_v = typeOfIRTemp(sb_out->tyenv, tmp_v);
- assign( 'V', &mce, tmp_v, definedOfType( ty_v ) );
- if (MC_(clo_mc_level) == 3) {
- IRTemp tmp_b = findShadowTmpB(&mce, tmp_o);
- tl_assert(typeOfIRTemp(sb_out->tyenv, tmp_b) == Ity_I32);
- assign( 'B', &mce, tmp_b, mkU32(0)/* UNKNOWN ORIGIN */);
- }
- if (0) {
- VG_(printf)("create shadow tmp(s) for preamble tmp [%d] ty ", j);
- ppIRType( ty_v );
- VG_(printf)("\n");
- }
- }
+ switch (szB) {
+ case 1: hFun = (void*)&MC_(helperc_b_store1);
+ hName = "MC_(helperc_b_store1)";
+ break;
+ case 2: hFun = (void*)&MC_(helperc_b_store2);
+ hName = "MC_(helperc_b_store2)";
+ break;
+ case 4: hFun = (void*)&MC_(helperc_b_store4);
+ hName = "MC_(helperc_b_store4)";
+ break;
+ case 8: hFun = (void*)&MC_(helperc_b_store8);
+ hName = "MC_(helperc_b_store8)";
+ break;
+ case 16: hFun = (void*)&MC_(helperc_b_store16);
+ hName = "MC_(helperc_b_store16)";
+ break;
+ case 32: hFun = (void*)&MC_(helperc_b_store32);
+ hName = "MC_(helperc_b_store32)";
+ break;
+ default:
+ tl_assert(0);
}
+ di = unsafeIRDirty_0_N( 2/*regparms*/,
+ hName, VG_(fnptr_to_fnentry)( hFun ),
+ mkIRExprVec_2( ea, dataB )
+ );
+ /* no need to mess with any annotations. This call accesses
+ neither guest state nor guest memory. */
+ if (guard) di->guard = guard;
+ stmt( 'B', mce, IRStmt_Dirty(di) );
+}
- /* Iterate over the remaining stmts to generate instrumentation. */
-
- tl_assert(sb_in->stmts_used > 0);
- tl_assert(i >= 0);
- tl_assert(i < sb_in->stmts_used);
- tl_assert(sb_in->stmts[i]->tag == Ist_IMark);
-
- for (/* use current i*/; i < sb_in->stmts_used; i++) {
-
- st = sb_in->stmts[i];
- first_stmt = sb_out->stmts_used;
+static IRAtom* narrowTo32 ( MCEnv* mce, IRAtom* e ) {
+ IRType eTy = typeOfIRExpr(mce->sb->tyenv, e);
+ if (eTy == Ity_I64)
+ return assignNew( 'B', mce, Ity_I32, unop(Iop_64to32, e) );
+ if (eTy == Ity_I32)
+ return e;
+ tl_assert(0);
+}
- if (verboze) {
- VG_(printf)("\n");
- ppIRStmt(st);
- VG_(printf)("\n");
- }
+static IRAtom* zWidenFrom32 ( MCEnv* mce, IRType dstTy, IRAtom* e ) {
+ IRType eTy = typeOfIRExpr(mce->sb->tyenv, e);
+ tl_assert(eTy == Ity_I32);
+ if (dstTy == Ity_I64)
+ return assignNew( 'B', mce, Ity_I64, unop(Iop_32Uto64, e) );
+ tl_assert(0);
+}
- if (MC_(clo_mc_level) == 3) {
- /* See comments on case Ist_CAS below. */
- if (st->tag != Ist_CAS)
- schemeS( &mce, st );
- }
- /* Generate instrumentation code for each stmt ... */
+static IRAtom* schemeE ( MCEnv* mce, IRExpr* e )
+{
+ tl_assert(MC_(clo_mc_level) == 3);
- switch (st->tag) {
+ switch (e->tag) {
- case Ist_WrTmp:
- assign( 'V', &mce, findShadowTmpV(&mce, st->Ist.WrTmp.tmp),
- expr2vbits( &mce, st->Ist.WrTmp.data) );
- break;
+ case Iex_GetI: {
+ IRRegArray* descr_b;
+ IRAtom *t1, *t2, *t3, *t4;
+ IRRegArray* descr = e->Iex.GetI.descr;
+ IRType equivIntTy
+ = MC_(get_otrack_reg_array_equiv_int_type)(descr);
+ /* If this array is unshadowable for whatever reason, use the
+ usual approximation. */
+ if (equivIntTy == Ity_INVALID)
+ return mkU32(0);
+ tl_assert(sizeofIRType(equivIntTy) >= 4);
+ tl_assert(sizeofIRType(equivIntTy) == sizeofIRType(descr->elemTy));
+ descr_b = mkIRRegArray( descr->base + 2*mce->layout->total_sizeB,
+ equivIntTy, descr->nElems );
+ /* Do a shadow indexed get of the same size, giving t1. Take
+ the bottom 32 bits of it, giving t2. Compute into t3 the
+ origin for the index (almost certainly zero, but there's
+ no harm in being completely general here, since iropt will
+ remove any useless code), and fold it in, giving a final
+ value t4. */
+ t1 = assignNew( 'B', mce, equivIntTy,
+ IRExpr_GetI( descr_b, e->Iex.GetI.ix,
+ e->Iex.GetI.bias ));
+ t2 = narrowTo32( mce, t1 );
+ t3 = schemeE( mce, e->Iex.GetI.ix );
+ t4 = gen_maxU32( mce, t2, t3 );
+ return t4;
+ }
+ case Iex_CCall: {
+ Int i;
+ IRAtom* here;
+ IRExpr** args = e->Iex.CCall.args;
+ IRAtom* curr = mkU32(0);
+ for (i = 0; args[i]; i++) {
+ tl_assert(i < 32);
+ tl_assert(isOriginalAtom(mce, args[i]));
+ /* Only take notice of this arg if the callee's
+ mc-exclusion mask does not say it is to be excluded. */
+ if (e->Iex.CCall.cee->mcx_mask & (1<<i)) {
+ /* the arg is to be excluded from definedness checking.
+ Do nothing. */
+ if (0) VG_(printf)("excluding %s(%d)\n",
+ e->Iex.CCall.cee->name, i);
+ } else {
+ /* calculate the arg's definedness, and pessimistically
+ merge it in. */
+ here = schemeE( mce, args[i] );
+ curr = gen_maxU32( mce, curr, here );
+ }
+ }
+ return curr;
+ }
+ case Iex_Load: {
+ Int dszB;
+ dszB = sizeofIRType(e->Iex.Load.ty);
+ /* assert that the B value for the address is already
+ available (somewhere) */
+ tl_assert(isIRAtom(e->Iex.Load.addr));
+ tl_assert(mce->hWordTy == Ity_I32 || mce->hWordTy == Ity_I64);
+ return gen_load_b( mce, dszB, e->Iex.Load.addr, 0 );
+ }
+ case Iex_ITE: {
+ IRAtom* b1 = schemeE( mce, e->Iex.ITE.cond );
+ IRAtom* b3 = schemeE( mce, e->Iex.ITE.iftrue );
+ IRAtom* b2 = schemeE( mce, e->Iex.ITE.iffalse );
+ return gen_maxU32( mce, b1, gen_maxU32( mce, b2, b3 ));
+ }
+ case Iex_Qop: {
+ IRAtom* b1 = schemeE( mce, e->Iex.Qop.details->arg1 );
+ IRAtom* b2 = schemeE( mce, e->Iex.Qop.details->arg2 );
+ IRAtom* b3 = schemeE( mce, e->Iex.Qop.details->arg3 );
+ IRAtom* b4 = schemeE( mce, e->Iex.Qop.details->arg4 );
+ return gen_maxU32( mce, gen_maxU32( mce, b1, b2 ),
+ gen_maxU32( mce, b3, b4 ) );
+ }
+ case Iex_Triop: {
+ IRAtom* b1 = schemeE( mce, e->Iex.Triop.details->arg1 );
+ IRAtom* b2 = schemeE( mce, e->Iex.Triop.details->arg2 );
+ IRAtom* b3 = schemeE( mce, e->Iex.Triop.details->arg3 );
+ return gen_maxU32( mce, b1, gen_maxU32( mce, b2, b3 ) );
+ }
+ case Iex_Binop: {
+ switch (e->Iex.Binop.op) {
+ case Iop_CasCmpEQ8: case Iop_CasCmpNE8:
+ case Iop_CasCmpEQ16: case Iop_CasCmpNE16:
+ case Iop_CasCmpEQ32: case Iop_CasCmpNE32:
+ case Iop_CasCmpEQ64: case Iop_CasCmpNE64:
+ /* Just say these all produce a defined result,
+ regardless of their arguments. See
+ COMMENT_ON_CasCmpEQ in this file. */
+ return mkU32(0);
+ default: {
+ IRAtom* b1 = schemeE( mce, e->Iex.Binop.arg1 );
+ IRAtom* b2 = schemeE( mce, e->Iex.Binop.arg2 );
+ return gen_maxU32( mce, b1, b2 );
+ }
+ }
+ tl_assert(0);
+ /*NOTREACHED*/
+ }
+ case Iex_Unop: {
+ IRAtom* b1 = schemeE( mce, e->Iex.Unop.arg );
+ return b1;
+ }
+ case Iex_Const:
+ return mkU32(0);
+ case Iex_RdTmp:
+ return mkexpr( findShadowTmpB( mce, e->Iex.RdTmp.tmp ));
+ case Iex_Get: {
+ Int b_offset = MC_(get_otrack_shadow_offset)(
+ e->Iex.Get.offset,
+ sizeofIRType(e->Iex.Get.ty)
+ );
+ tl_assert(b_offset >= -1
+ && b_offset <= mce->layout->total_sizeB -4);
+ if (b_offset >= 0) {
+ /* FIXME: this isn't an atom! */
+ return IRExpr_Get( b_offset + 2*mce->layout->total_sizeB,
+ Ity_I32 );
+ }
+ return mkU32(0);
+ }
+ default:
+ VG_(printf)("mc_translate.c: schemeE: unhandled: ");
+ ppIRExpr(e);
+ VG_(tool_panic)("memcheck:schemeE");
+ }
+}
- case Ist_Put:
- do_shadow_PUT( &mce,
- st->Ist.Put.offset,
- st->Ist.Put.data,
- NULL /* shadow atom */, NULL /* guard */ );
- break;
- case Ist_PutI:
- do_shadow_PUTI( &mce, st->Ist.PutI.details);
- break;
+static void do_origins_Dirty ( MCEnv* mce, IRDirty* d )
+{
+ // This is a hacked version of do_shadow_Dirty
+ Int i, k, n, toDo, gSz, gOff;
+ IRAtom *here, *curr;
+ IRTemp dst;
- case Ist_Store:
- do_shadow_Store( &mce, st->Ist.Store.end,
- st->Ist.Store.addr, 0/* addr bias */,
- st->Ist.Store.data,
- NULL /* shadow data */,
- NULL/*guard*/ );
- break;
+ /* First check the guard. */
+ curr = schemeE( mce, d->guard );
- case Ist_StoreG:
- do_shadow_StoreG( &mce, st->Ist.StoreG.details );
- break;
+ /* Now round up all inputs and maxU32 over them. */
- case Ist_LoadG:
- do_shadow_LoadG( &mce, st->Ist.LoadG.details );
- break;
+ /* Inputs: unmasked args
+ Note: arguments are evaluated REGARDLESS of the guard expression */
+ for (i = 0; d->args[i]; i++) {
+ IRAtom* arg = d->args[i];
+ if ( (d->cee->mcx_mask & (1<<i))
+ || UNLIKELY(is_IRExpr_VECRET_or_GSPTR(arg)) ) {
+ /* ignore this arg */
+ } else {
+ here = schemeE( mce, arg );
+ curr = gen_maxU32( mce, curr, here );
+ }
+ }
- case Ist_Exit:
- complainIfUndefined( &mce, st->Ist.Exit.guard, NULL );
- break;
+ /* Inputs: guest state that we read. */
+ for (i = 0; i < d->nFxState; i++) {
+ tl_assert(d->fxState[i].fx != Ifx_None);
+ if (d->fxState[i].fx == Ifx_Write)
+ continue;
- case Ist_IMark:
- break;
+ /* Enumerate the described state segments */
+ for (k = 0; k < 1 + d->fxState[i].nRepeats; k++) {
+ gOff = d->fxState[i].offset + k * d->fxState[i].repeatLen;
+ gSz = d->fxState[i].size;
- case Ist_NoOp:
- case Ist_MBE:
- break;
+ /* Ignore any sections marked as 'always defined'. */
+ if (isAlwaysDefd(mce, gOff, gSz)) {
+ if (0)
+ VG_(printf)("memcheck: Dirty gst: ignored off %d, sz %d\n",
+ gOff, gSz);
+ continue;
+ }
- case Ist_Dirty:
- do_shadow_Dirty( &mce, st->Ist.Dirty.details );
- break;
+ /* This state element is read or modified. So we need to
+ consider it. If larger than 4 bytes, deal with it in
+ 4-byte chunks. */
+ while (True) {
+ Int b_offset;
+ tl_assert(gSz >= 0);
+ if (gSz == 0) break;
+ n = gSz <= 4 ? gSz : 4;
+ /* update 'curr' with maxU32 of the state slice
+ gOff .. gOff+n-1 */
+ b_offset = MC_(get_otrack_shadow_offset)(gOff, 4);
+ if (b_offset != -1) {
+ /* Observe the guard expression. If it is false use 0, i.e.
+ nothing is known about the origin */
+ IRAtom *cond, *iffalse, *iftrue;
- case Ist_AbiHint:
- do_AbiHint( &mce, st->Ist.AbiHint.base,
- st->Ist.AbiHint.len,
- st->Ist.AbiHint.nia );
- break;
+ cond = assignNew( 'B', mce, Ity_I1, d->guard);
+ iffalse = mkU32(0);
+ iftrue = assignNew( 'B', mce, Ity_I32,
+ IRExpr_Get(b_offset
+ + 2*mce->layout->total_sizeB,
+ Ity_I32));
+ here = assignNew( 'B', mce, Ity_I32,
+ IRExpr_ITE(cond, iftrue, iffalse));
+ curr = gen_maxU32( mce, curr, here );
+ }
+ gSz -= n;
+ gOff += n;
+ }
+ }
+ }
- case Ist_CAS:
- do_shadow_CAS( &mce, st->Ist.CAS.details );
- /* Note, do_shadow_CAS copies the CAS itself to the output
- block, because it needs to add instrumentation both
- before and after it. Hence skip the copy below. Also
- skip the origin-tracking stuff (call to schemeS) above,
- since that's all tangled up with it too; do_shadow_CAS
- does it all. */
- break;
+ /* Inputs: memory */
- case Ist_LLSC:
- do_shadow_LLSC( &mce,
- st->Ist.LLSC.end,
- st->Ist.LLSC.result,
- st->Ist.LLSC.addr,
- st->Ist.LLSC.storedata );
+ if (d->mFx != Ifx_None) {
+ /* Because we may do multiple shadow loads/stores from the same
+ base address, it's best to do a single test of its
+ definedness right now. Post-instrumentation optimisation
+ should remove all but this test. */
+ tl_assert(d->mAddr);
+ here = schemeE( mce, d->mAddr );
+ curr = gen_maxU32( mce, curr, here );
+ }
+
+ /* Deal with memory inputs (reads or modifies) */
+ if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) {
+ toDo = d->mSize;
+ /* chew off 32-bit chunks. We don't care about the endianness
+ since it's all going to be condensed down to a single bit,
+ but nevertheless choose an endianness which is hopefully
+ native to the platform. */
+ while (toDo >= 4) {
+ here = gen_guarded_load_b( mce, 4, d->mAddr, d->mSize - toDo,
+ d->guard );
+ curr = gen_maxU32( mce, curr, here );
+ toDo -= 4;
+ }
+ /* handle possible 16-bit excess */
+ while (toDo >= 2) {
+ here = gen_guarded_load_b( mce, 2, d->mAddr, d->mSize - toDo,
+ d->guard );
+ curr = gen_maxU32( mce, curr, here );
+ toDo -= 2;
+ }
+ /* chew off the remaining 8-bit chunk, if any */
+ if (toDo == 1) {
+ here = gen_guarded_load_b( mce, 1, d->mAddr, d->mSize - toDo,
+ d->guard );
+ curr = gen_maxU32( mce, curr, here );
+ toDo -= 1;
+ }
+ tl_assert(toDo == 0);
+ }
+
+ /* Whew! So curr is a 32-bit B-value which should give an origin
+ of some use if any of the inputs to the helper are undefined.
+ Now we need to re-distribute the results to all destinations. */
+
+ /* Outputs: the destination temporary, if there is one. */
+ if (d->tmp != IRTemp_INVALID) {
+ dst = findShadowTmpB(mce, d->tmp);
+ assign( 'V', mce, dst, curr );
+ }
+
+ /* Outputs: guest state that we write or modify. */
+ for (i = 0; i < d->nFxState; i++) {
+ tl_assert(d->fxState[i].fx != Ifx_None);
+ if (d->fxState[i].fx == Ifx_Read)
+ continue;
+
+ /* Enumerate the described state segments */
+ for (k = 0; k < 1 + d->fxState[i].nRepeats; k++) {
+ gOff = d->fxState[i].offset + k * d->fxState[i].repeatLen;
+ gSz = d->fxState[i].size;
+
+ /* Ignore any sections marked as 'always defined'. */
+ if (isAlwaysDefd(mce, gOff, gSz))
+ continue;
+
+ /* This state element is written or modified. So we need to
+ consider it. If larger than 4 bytes, deal with it in
+ 4-byte chunks. */
+ while (True) {
+ Int b_offset;
+ tl_assert(gSz >= 0);
+ if (gSz == 0) break;
+ n = gSz <= 4 ? gSz : 4;
+ /* Write 'curr' to the state slice gOff .. gOff+n-1 */
+ b_offset = MC_(get_otrack_shadow_offset)(gOff, 4);
+ if (b_offset != -1) {
+
+ /* If the guard expression evaluates to false we simply Put
+ the value that is already stored in the guest state slot */
+ IRAtom *cond, *iffalse;
+
+ cond = assignNew('B', mce, Ity_I1,
+ d->guard);
+ iffalse = assignNew('B', mce, Ity_I32,
+ IRExpr_Get(b_offset +
+ 2*mce->layout->total_sizeB,
+ Ity_I32));
+ curr = assignNew('V', mce, Ity_I32,
+ IRExpr_ITE(cond, curr, iffalse));
+
+ stmt( 'B', mce, IRStmt_Put(b_offset
+ + 2*mce->layout->total_sizeB,
+ curr ));
+ }
+ gSz -= n;
+ gOff += n;
+ }
+ }
+ }
+
+ /* Outputs: memory that we write or modify. Same comments about
+ endianness as above apply. */
+ if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) {
+ toDo = d->mSize;
+ /* chew off 32-bit chunks */
+ while (toDo >= 4) {
+ gen_store_b( mce, 4, d->mAddr, d->mSize - toDo, curr,
+ d->guard );
+ toDo -= 4;
+ }
+ /* handle possible 16-bit excess */
+ while (toDo >= 2) {
+ gen_store_b( mce, 2, d->mAddr, d->mSize - toDo, curr,
+ d->guard );
+ toDo -= 2;
+ }
+ /* chew off the remaining 8-bit chunk, if any */
+ if (toDo == 1) {
+ gen_store_b( mce, 1, d->mAddr, d->mSize - toDo, curr,
+ d->guard );
+ toDo -= 1;
+ }
+ tl_assert(toDo == 0);
+ }
+}
+
+
+/* Generate IR for origin shadowing for a general guarded store. */
+static void do_origins_Store_guarded ( MCEnv* mce,
+ IREndness stEnd,
+ IRExpr* stAddr,
+ IRExpr* stData,
+ IRExpr* guard )
+{
+ Int dszB;
+ IRAtom* dataB;
+ /* assert that the B value for the address is already available
+ (somewhere), since the call to schemeE will want to see it.
+ XXXX how does this actually ensure that?? */
+ tl_assert(isIRAtom(stAddr));
+ tl_assert(isIRAtom(stData));
+ dszB = sizeofIRType( typeOfIRExpr(mce->sb->tyenv, stData ) );
+ dataB = schemeE( mce, stData );
+ gen_store_b( mce, dszB, stAddr, 0/*offset*/, dataB, guard );
+}
+
+
+/* Generate IR for origin shadowing for a plain store. */
+static void do_origins_Store_plain ( MCEnv* mce,
+ IREndness stEnd,
+ IRExpr* stAddr,
+ IRExpr* stData )
+{
+ do_origins_Store_guarded ( mce, stEnd, stAddr, stData,
+ NULL/*guard*/ );
+}
+
+
+/* ---- Dealing with LoadG/StoreG (not entirely simple) ---- */
+
+static void do_origins_StoreG ( MCEnv* mce, IRStoreG* sg )
+{
+ do_origins_Store_guarded( mce, sg->end, sg->addr,
+ sg->data, sg->guard );
+}
+
+static void do_origins_LoadG ( MCEnv* mce, IRLoadG* lg )
+{
+ IRType loadedTy = Ity_INVALID;
+ switch (lg->cvt) {
+ case ILGop_IdentV128: loadedTy = Ity_V128; break;
+ case ILGop_Ident64: loadedTy = Ity_I64; break;
+ case ILGop_Ident32: loadedTy = Ity_I32; break;
+ case ILGop_16Uto32: loadedTy = Ity_I16; break;
+ case ILGop_16Sto32: loadedTy = Ity_I16; break;
+ case ILGop_8Uto32: loadedTy = Ity_I8; break;
+ case ILGop_8Sto32: loadedTy = Ity_I8; break;
+ default: VG_(tool_panic)("schemeS.IRLoadG");
+ }
+ IRAtom* ori_alt
+ = schemeE( mce,lg->alt );
+ IRAtom* ori_final
+ = expr2ori_Load_guarded_General(mce, loadedTy,
+ lg->addr, 0/*addr bias*/,
+ lg->guard, ori_alt );
+ /* And finally, bind the origin to the destination temporary. */
+ assign( 'B', mce, findShadowTmpB(mce, lg->dst), ori_final );
+}
+
+
+static void schemeS ( MCEnv* mce, IRStmt* st )
+{
+ tl_assert(MC_(clo_mc_level) == 3);
+
+ switch (st->tag) {
+
+ case Ist_AbiHint:
+ /* The value-check instrumenter handles this - by arranging
+ to pass the address of the next instruction to
+ MC_(helperc_MAKE_STACK_UNINIT). This is all that needs to
+ happen for origin tracking w.r.t. AbiHints. So there is
+ nothing to do here. */
+ break;
+
+ case Ist_PutI: {
+ IRPutI *puti = st->Ist.PutI.details;
+ IRRegArray* descr_b;
+ IRAtom *t1, *t2, *t3, *t4;
+ IRRegArray* descr = puti->descr;
+ IRType equivIntTy
+ = MC_(get_otrack_reg_array_equiv_int_type)(descr);
+ /* If this array is unshadowable for whatever reason,
+ generate no code. */
+ if (equivIntTy == Ity_INVALID)
break;
+ tl_assert(sizeofIRType(equivIntTy) >= 4);
+ tl_assert(sizeofIRType(equivIntTy) == sizeofIRType(descr->elemTy));
+ descr_b
+ = mkIRRegArray( descr->base + 2*mce->layout->total_sizeB,
+ equivIntTy, descr->nElems );
+ /* Compute a value to Put - the conjoinment of the origin for
+ the data to be Put-ted (obviously) and of the index value
+ (not so obviously). */
+ t1 = schemeE( mce, puti->data );
+ t2 = schemeE( mce, puti->ix );
+ t3 = gen_maxU32( mce, t1, t2 );
+ t4 = zWidenFrom32( mce, equivIntTy, t3 );
+ stmt( 'B', mce, IRStmt_PutI( mkIRPutI(descr_b, puti->ix,
+ puti->bias, t4) ));
+ break;
+ }
- default:
- VG_(printf)("\n");
- ppIRStmt(st);
- VG_(printf)("\n");
- VG_(tool_panic)("memcheck: unhandled IRStmt");
+ case Ist_Dirty:
+ do_origins_Dirty( mce, st->Ist.Dirty.details );
+ break;
+
+ case Ist_Store:
+ do_origins_Store_plain( mce, st->Ist.Store.end,
+ st->Ist.Store.addr,
+ st->Ist.Store.data );
+ break;
+
+ case Ist_StoreG:
+ do_origins_StoreG( mce, st->Ist.StoreG.details );
+ break;
- } /* switch (st->tag) */
+ case Ist_LoadG:
+ do_origins_LoadG( mce, st->Ist.LoadG.details );
+ break;
- if (0 && verboze) {
- for (j = first_stmt; j < sb_out->stmts_used; j++) {
- VG_(printf)(" ");
- ppIRStmt(sb_out->stmts[j]);
- VG_(printf)("\n");
+ case Ist_LLSC: {
+ /* In short: treat a load-linked like a normal load followed
+ by an assignment of the loaded (shadow) data the result
+ temporary. Treat a store-conditional like a normal store,
+ and mark the result temporary as defined. */
+ if (st->Ist.LLSC.storedata == NULL) {
+ /* Load Linked */
+ IRType resTy
+ = typeOfIRTemp(mce->sb->tyenv, st->Ist.LLSC.result);
+ IRExpr* vanillaLoad
+ = IRExpr_Load(st->Ist.LLSC.end, resTy, st->Ist.LLSC.addr);
+ tl_assert(resTy == Ity_I64 || resTy == Ity_I32
+ || resTy == Ity_I16 || resTy == Ity_I8);
+ assign( 'B', mce, findShadowTmpB(mce, st->Ist.LLSC.result),
+ schemeE(mce, vanillaLoad));
+ } else {
+ /* Store conditional */
+ do_origins_Store_plain( mce, st->Ist.LLSC.end,
+ st->Ist.LLSC.addr,
+ st->Ist.LLSC.storedata );
+ /* For the rationale behind this, see comments at the
+ place where the V-shadow for .result is constructed, in
+ do_shadow_LLSC. In short, we regard .result as
+ always-defined. */
+ assign( 'B', mce, findShadowTmpB(mce, st->Ist.LLSC.result),
+ mkU32(0) );
}
- VG_(printf)("\n");
+ break;
}
- /* ... and finally copy the stmt itself to the output. Except,
- skip the copy of IRCASs; see comments on case Ist_CAS
- above. */
- if (st->tag != Ist_CAS)
- stmt('C', &mce, st);
- }
-
- /* Now we need to complain if the jump target is undefined. */
- first_stmt = sb_out->stmts_used;
+ case Ist_Put: {
+ Int b_offset
+ = MC_(get_otrack_shadow_offset)(
+ st->Ist.Put.offset,
+ sizeofIRType(typeOfIRExpr(mce->sb->tyenv, st->Ist.Put.data))
+ );
+ if (b_offset >= 0) {
+ /* FIXME: this isn't an atom! */
+ stmt( 'B', mce, IRStmt_Put(b_offset + 2*mce->layout->total_sizeB,
+ schemeE( mce, st->Ist.Put.data )) );
+ }
+ break;
+ }
- if (verboze) {
- VG_(printf)("sb_in->next = ");
- ppIRExpr(sb_in->next);
- VG_(printf)("\n\n");
- }
+ case Ist_WrTmp:
+ assign( 'B', mce, findShadowTmpB(mce, st->Ist.WrTmp.tmp),
+ schemeE(mce, st->Ist.WrTmp.data) );
+ break;
- complainIfUndefined( &mce, sb_in->next, NULL );
+ case Ist_MBE:
+ case Ist_NoOp:
+ case Ist_Exit:
+ case Ist_IMark:
+ break;
- if (0 && verboze) {
- for (j = first_stmt; j < sb_out->stmts_used; j++) {
- VG_(printf)(" ");
- ppIRStmt(sb_out->stmts[j]);
- VG_(printf)("\n");
- }
- VG_(printf)("\n");
+ default:
+ VG_(printf)("mc_translate.c: schemeS: unhandled: ");
+ ppIRStmt(st);
+ VG_(tool_panic)("memcheck:schemeS");
}
-
- /* If this fails, there's been some serious snafu with tmp management,
- that should be investigated. */
- tl_assert( VG_(sizeXA)( mce.tmpMap ) == mce.sb->tyenv->types_used );
- VG_(deleteXA)( mce.tmpMap );
-
- tl_assert(mce.sb == sb_out);
- return sb_out;
}
alreadyPresent = check_or_add( &pairs, guard, cee->addr );
if (alreadyPresent) {
sb_in->stmts[i] = IRStmt_NoOp();
- if (0) VG_(printf)("XX\n");
- }
- }
-
- tl_assert(pairs.pairs[N_TIDYING_PAIRS].entry == (void*)0x123);
- tl_assert(pairs.pairs[N_TIDYING_PAIRS].guard == (IRExpr*)0x456);
-
- return sb_in;
-}
-
-#undef N_TIDYING_PAIRS
-
-
-/*------------------------------------------------------------*/
-/*--- Origin tracking stuff ---*/
-/*------------------------------------------------------------*/
-
-/* Almost identical to findShadowTmpV. */
-static IRTemp findShadowTmpB ( MCEnv* mce, IRTemp orig )
-{
- TempMapEnt* ent;
- /* VG_(indexXA) range-checks 'orig', hence no need to check
- here. */
- ent = (TempMapEnt*)VG_(indexXA)( mce->tmpMap, (Word)orig );
- tl_assert(ent->kind == Orig);
- if (ent->shadowB == IRTemp_INVALID) {
- IRTemp tmpB
- = newTemp( mce, Ity_I32, BSh );
- /* newTemp may cause mce->tmpMap to resize, hence previous results
- from VG_(indexXA) are invalid. */
- ent = (TempMapEnt*)VG_(indexXA)( mce->tmpMap, (Word)orig );
- tl_assert(ent->kind == Orig);
- tl_assert(ent->shadowB == IRTemp_INVALID);
- ent->shadowB = tmpB;
- }
- return ent->shadowB;
-}
-
-static IRAtom* gen_maxU32 ( MCEnv* mce, IRAtom* b1, IRAtom* b2 )
-{
- return assignNew( 'B', mce, Ity_I32, binop(Iop_Max32U, b1, b2) );
-}
-
-
-/* Make a guarded origin load, with no special handling in the
- didn't-happen case. A GUARD of NULL is assumed to mean "always
- True".
-
- Generate IR to do a shadow origins load from BASEADDR+OFFSET and
- return the otag. The loaded size is SZB. If GUARD evaluates to
- False at run time then the returned otag is zero.
-*/
-static IRAtom* gen_guarded_load_b ( MCEnv* mce, Int szB,
- IRAtom* baseaddr,
- Int offset, IRExpr* guard )
-{
- void* hFun;
- const HChar* hName;
- IRTemp bTmp;
- IRDirty* di;
- IRType aTy = typeOfIRExpr( mce->sb->tyenv, baseaddr );
- IROp opAdd = aTy == Ity_I32 ? Iop_Add32 : Iop_Add64;
- IRAtom* ea = baseaddr;
- if (offset != 0) {
- IRAtom* off = aTy == Ity_I32 ? mkU32( offset )
- : mkU64( (Long)(Int)offset );
- ea = assignNew( 'B', mce, aTy, binop(opAdd, ea, off));
- }
- bTmp = newTemp(mce, mce->hWordTy, BSh);
-
- switch (szB) {
- case 1: hFun = (void*)&MC_(helperc_b_load1);
- hName = "MC_(helperc_b_load1)";
- break;
- case 2: hFun = (void*)&MC_(helperc_b_load2);
- hName = "MC_(helperc_b_load2)";
- break;
- case 4: hFun = (void*)&MC_(helperc_b_load4);
- hName = "MC_(helperc_b_load4)";
- break;
- case 8: hFun = (void*)&MC_(helperc_b_load8);
- hName = "MC_(helperc_b_load8)";
- break;
- case 16: hFun = (void*)&MC_(helperc_b_load16);
- hName = "MC_(helperc_b_load16)";
- break;
- case 32: hFun = (void*)&MC_(helperc_b_load32);
- hName = "MC_(helperc_b_load32)";
- break;
- default:
- VG_(printf)("mc_translate.c: gen_load_b: unhandled szB == %d\n", szB);
- tl_assert(0);
- }
- di = unsafeIRDirty_1_N(
- bTmp, 1/*regparms*/, hName, VG_(fnptr_to_fnentry)( hFun ),
- mkIRExprVec_1( ea )
- );
- if (guard) {
- di->guard = guard;
- /* Ideally the didn't-happen return value here would be
- all-zeroes (unknown-origin), so it'd be harmless if it got
- used inadvertently. We slum it out with the IR-mandated
- default value (0b01 repeating, 0x55 etc) as that'll probably
- trump all legitimate otags via Max32, and it's pretty
- obviously bogus. */
- }
- /* no need to mess with any annotations. This call accesses
- neither guest state nor guest memory. */
- stmt( 'B', mce, IRStmt_Dirty(di) );
- if (mce->hWordTy == Ity_I64) {
- /* 64-bit host */
- IRTemp bTmp32 = newTemp(mce, Ity_I32, BSh);
- assign( 'B', mce, bTmp32, unop(Iop_64to32, mkexpr(bTmp)) );
- return mkexpr(bTmp32);
- } else {
- /* 32-bit host */
- return mkexpr(bTmp);
- }
-}
-
-
-/* Generate IR to do a shadow origins load from BASEADDR+OFFSET. The
- loaded size is SZB. The load is regarded as unconditional (always
- happens).
-*/
-static IRAtom* gen_load_b ( MCEnv* mce, Int szB, IRAtom* baseaddr,
- Int offset )
-{
- return gen_guarded_load_b(mce, szB, baseaddr, offset, NULL/*guard*/);
-}
-
-
-/* The most general handler for guarded origin loads. A GUARD of NULL
- is assumed to mean "always True".
-
- Generate IR to do a shadow origin load from ADDR+BIAS and return
- the B bits. The loaded type is TY. If GUARD evaluates to False at
- run time then the returned B bits are simply BALT instead.
-*/
-static
-IRAtom* expr2ori_Load_guarded_General ( MCEnv* mce,
- IRType ty,
- IRAtom* addr, UInt bias,
- IRAtom* guard, IRAtom* balt )
-{
- /* If the guard evaluates to True, this will hold the loaded
- origin. If the guard evaluates to False, this will be zero,
- meaning "unknown origin", in which case we will have to replace
- it using an ITE below. */
- IRAtom* iftrue
- = assignNew('B', mce, Ity_I32,
- gen_guarded_load_b(mce, sizeofIRType(ty),
- addr, bias, guard));
- /* These are the bits we will return if the load doesn't take
- place. */
- IRAtom* iffalse
- = balt;
- /* Prepare the cond for the ITE. Convert a NULL cond into
- something that iropt knows how to fold out later. */
- IRAtom* cond
- = guard == NULL ? mkU1(1) : guard;
- /* And assemble the final result. */
- return assignNew('B', mce, Ity_I32, IRExpr_ITE(cond, iftrue, iffalse));
+ if (0) VG_(printf)("XX\n");
+ }
+ }
+
+ tl_assert(pairs.pairs[N_TIDYING_PAIRS].entry == (void*)0x123);
+ tl_assert(pairs.pairs[N_TIDYING_PAIRS].guard == (IRExpr*)0x456);
+
+ return sb_in;
}
+#undef N_TIDYING_PAIRS
-/* Generate a shadow origins store. guard :: Ity_I1 controls whether
- the store really happens; NULL means it unconditionally does. */
-static void gen_store_b ( MCEnv* mce, Int szB,
- IRAtom* baseaddr, Int offset, IRAtom* dataB,
- IRAtom* guard )
+
+/*------------------------------------------------------------*/
+/*--- Startup assertion checking ---*/
+/*------------------------------------------------------------*/
+
+void MC_(do_instrumentation_startup_checks)( void )
{
- void* hFun;
- const HChar* hName;
- IRDirty* di;
- IRType aTy = typeOfIRExpr( mce->sb->tyenv, baseaddr );
- IROp opAdd = aTy == Ity_I32 ? Iop_Add32 : Iop_Add64;
- IRAtom* ea = baseaddr;
- if (guard) {
- tl_assert(isOriginalAtom(mce, guard));
- tl_assert(typeOfIRExpr(mce->sb->tyenv, guard) == Ity_I1);
- }
- if (offset != 0) {
- IRAtom* off = aTy == Ity_I32 ? mkU32( offset )
- : mkU64( (Long)(Int)offset );
- ea = assignNew( 'B', mce, aTy, binop(opAdd, ea, off));
- }
- if (mce->hWordTy == Ity_I64)
- dataB = assignNew( 'B', mce, Ity_I64, unop(Iop_32Uto64, dataB));
+ /* Make a best-effort check to see that is_helperc_value_checkN_fail
+ is working as we expect. */
- switch (szB) {
- case 1: hFun = (void*)&MC_(helperc_b_store1);
- hName = "MC_(helperc_b_store1)";
- break;
- case 2: hFun = (void*)&MC_(helperc_b_store2);
- hName = "MC_(helperc_b_store2)";
- break;
- case 4: hFun = (void*)&MC_(helperc_b_store4);
- hName = "MC_(helperc_b_store4)";
- break;
- case 8: hFun = (void*)&MC_(helperc_b_store8);
- hName = "MC_(helperc_b_store8)";
- break;
- case 16: hFun = (void*)&MC_(helperc_b_store16);
- hName = "MC_(helperc_b_store16)";
- break;
- case 32: hFun = (void*)&MC_(helperc_b_store32);
- hName = "MC_(helperc_b_store32)";
- break;
- default:
- tl_assert(0);
- }
- di = unsafeIRDirty_0_N( 2/*regparms*/,
- hName, VG_(fnptr_to_fnentry)( hFun ),
- mkIRExprVec_2( ea, dataB )
- );
- /* no need to mess with any annotations. This call accesses
- neither guest state nor guest memory. */
- if (guard) di->guard = guard;
- stmt( 'B', mce, IRStmt_Dirty(di) );
-}
+# define CHECK(_expected, _string) \
+ tl_assert((_expected) == is_helperc_value_checkN_fail(_string))
-static IRAtom* narrowTo32 ( MCEnv* mce, IRAtom* e ) {
- IRType eTy = typeOfIRExpr(mce->sb->tyenv, e);
- if (eTy == Ity_I64)
- return assignNew( 'B', mce, Ity_I32, unop(Iop_64to32, e) );
- if (eTy == Ity_I32)
- return e;
- tl_assert(0);
-}
+ /* It should identify these 8, and no others, as targets. */
+ CHECK(True, "MC_(helperc_value_check8_fail_no_o)");
+ CHECK(True, "MC_(helperc_value_check4_fail_no_o)");
+ CHECK(True, "MC_(helperc_value_check0_fail_no_o)");
+ CHECK(True, "MC_(helperc_value_check1_fail_no_o)");
+ CHECK(True, "MC_(helperc_value_check8_fail_w_o)");
+ CHECK(True, "MC_(helperc_value_check0_fail_w_o)");
+ CHECK(True, "MC_(helperc_value_check1_fail_w_o)");
+ CHECK(True, "MC_(helperc_value_check4_fail_w_o)");
-static IRAtom* zWidenFrom32 ( MCEnv* mce, IRType dstTy, IRAtom* e ) {
- IRType eTy = typeOfIRExpr(mce->sb->tyenv, e);
- tl_assert(eTy == Ity_I32);
- if (dstTy == Ity_I64)
- return assignNew( 'B', mce, Ity_I64, unop(Iop_32Uto64, e) );
- tl_assert(0);
+ /* Ad-hoc selection of other strings gathered via a quick test. */
+ CHECK(False, "amd64g_dirtyhelper_CPUID_avx2");
+ CHECK(False, "amd64g_dirtyhelper_RDTSC");
+ CHECK(False, "MC_(helperc_b_load1)");
+ CHECK(False, "MC_(helperc_b_load2)");
+ CHECK(False, "MC_(helperc_b_load4)");
+ CHECK(False, "MC_(helperc_b_load8)");
+ CHECK(False, "MC_(helperc_b_load16)");
+ CHECK(False, "MC_(helperc_b_load32)");
+ CHECK(False, "MC_(helperc_b_store1)");
+ CHECK(False, "MC_(helperc_b_store2)");
+ CHECK(False, "MC_(helperc_b_store4)");
+ CHECK(False, "MC_(helperc_b_store8)");
+ CHECK(False, "MC_(helperc_b_store16)");
+ CHECK(False, "MC_(helperc_b_store32)");
+ CHECK(False, "MC_(helperc_LOADV8)");
+ CHECK(False, "MC_(helperc_LOADV16le)");
+ CHECK(False, "MC_(helperc_LOADV32le)");
+ CHECK(False, "MC_(helperc_LOADV64le)");
+ CHECK(False, "MC_(helperc_LOADV128le)");
+ CHECK(False, "MC_(helperc_LOADV256le)");
+ CHECK(False, "MC_(helperc_STOREV16le)");
+ CHECK(False, "MC_(helperc_STOREV32le)");
+ CHECK(False, "MC_(helperc_STOREV64le)");
+ CHECK(False, "MC_(helperc_STOREV8)");
+ CHECK(False, "track_die_mem_stack_8");
+ CHECK(False, "track_new_mem_stack_8_w_ECU");
+ CHECK(False, "MC_(helperc_MAKE_STACK_UNINIT_w_o)");
+ CHECK(False, "VG_(unknown_SP_update_w_ECU)");
+
+# undef CHECK
}
-static IRAtom* schemeE ( MCEnv* mce, IRExpr* e )
-{
- tl_assert(MC_(clo_mc_level) == 3);
+/*------------------------------------------------------------*/
+/*--- Memcheck main ---*/
+/*------------------------------------------------------------*/
- switch (e->tag) {
+static Bool isBogusAtom ( IRAtom* at )
+{
+ ULong n = 0;
+ IRConst* con;
+ tl_assert(isIRAtom(at));
+ if (at->tag == Iex_RdTmp)
+ return False;
+ tl_assert(at->tag == Iex_Const);
+ con = at->Iex.Const.con;
+ switch (con->tag) {
+ case Ico_U1: return False;
+ case Ico_U8: n = (ULong)con->Ico.U8; break;
+ case Ico_U16: n = (ULong)con->Ico.U16; break;
+ case Ico_U32: n = (ULong)con->Ico.U32; break;
+ case Ico_U64: n = (ULong)con->Ico.U64; break;
+ case Ico_F32: return False;
+ case Ico_F64: return False;
+ case Ico_F32i: return False;
+ case Ico_F64i: return False;
+ case Ico_V128: return False;
+ case Ico_V256: return False;
+ default: ppIRExpr(at); tl_assert(0);
+ }
+ /* VG_(printf)("%llx\n", n); */
+ return (/*32*/ n == 0xFEFEFEFFULL
+ /*32*/ || n == 0x80808080ULL
+ /*32*/ || n == 0x7F7F7F7FULL
+ /*32*/ || n == 0x7EFEFEFFULL
+ /*32*/ || n == 0x81010100ULL
+ /*64*/ || n == 0xFFFFFFFFFEFEFEFFULL
+ /*64*/ || n == 0xFEFEFEFEFEFEFEFFULL
+ /*64*/ || n == 0x0000000000008080ULL
+ /*64*/ || n == 0x8080808080808080ULL
+ /*64*/ || n == 0x0101010101010101ULL
+ );
+}
- case Iex_GetI: {
- IRRegArray* descr_b;
- IRAtom *t1, *t2, *t3, *t4;
- IRRegArray* descr = e->Iex.GetI.descr;
- IRType equivIntTy
- = MC_(get_otrack_reg_array_equiv_int_type)(descr);
- /* If this array is unshadowable for whatever reason, use the
- usual approximation. */
- if (equivIntTy == Ity_INVALID)
- return mkU32(0);
- tl_assert(sizeofIRType(equivIntTy) >= 4);
- tl_assert(sizeofIRType(equivIntTy) == sizeofIRType(descr->elemTy));
- descr_b = mkIRRegArray( descr->base + 2*mce->layout->total_sizeB,
- equivIntTy, descr->nElems );
- /* Do a shadow indexed get of the same size, giving t1. Take
- the bottom 32 bits of it, giving t2. Compute into t3 the
- origin for the index (almost certainly zero, but there's
- no harm in being completely general here, since iropt will
- remove any useless code), and fold it in, giving a final
- value t4. */
- t1 = assignNew( 'B', mce, equivIntTy,
- IRExpr_GetI( descr_b, e->Iex.GetI.ix,
- e->Iex.GetI.bias ));
- t2 = narrowTo32( mce, t1 );
- t3 = schemeE( mce, e->Iex.GetI.ix );
- t4 = gen_maxU32( mce, t2, t3 );
- return t4;
- }
- case Iex_CCall: {
- Int i;
- IRAtom* here;
- IRExpr** args = e->Iex.CCall.args;
- IRAtom* curr = mkU32(0);
- for (i = 0; args[i]; i++) {
- tl_assert(i < 32);
- tl_assert(isOriginalAtom(mce, args[i]));
- /* Only take notice of this arg if the callee's
- mc-exclusion mask does not say it is to be excluded. */
- if (e->Iex.CCall.cee->mcx_mask & (1<<i)) {
- /* the arg is to be excluded from definedness checking.
- Do nothing. */
- if (0) VG_(printf)("excluding %s(%d)\n",
- e->Iex.CCall.cee->name, i);
- } else {
- /* calculate the arg's definedness, and pessimistically
- merge it in. */
- here = schemeE( mce, args[i] );
- curr = gen_maxU32( mce, curr, here );
- }
+static Bool checkForBogusLiterals ( /*FLAT*/ IRStmt* st )
+{
+ Int i;
+ IRExpr* e;
+ IRDirty* d;
+ IRCAS* cas;
+ switch (st->tag) {
+ case Ist_WrTmp:
+ e = st->Ist.WrTmp.data;
+ switch (e->tag) {
+ case Iex_Get:
+ case Iex_RdTmp:
+ return False;
+ case Iex_Const:
+ return isBogusAtom(e);
+ case Iex_Unop:
+ return isBogusAtom(e->Iex.Unop.arg)
+ || e->Iex.Unop.op == Iop_GetMSBs8x16;
+ case Iex_GetI:
+ return isBogusAtom(e->Iex.GetI.ix);
+ case Iex_Binop:
+ return isBogusAtom(e->Iex.Binop.arg1)
+ || isBogusAtom(e->Iex.Binop.arg2);
+ case Iex_Triop:
+ return isBogusAtom(e->Iex.Triop.details->arg1)
+ || isBogusAtom(e->Iex.Triop.details->arg2)
+ || isBogusAtom(e->Iex.Triop.details->arg3);
+ case Iex_Qop:
+ return isBogusAtom(e->Iex.Qop.details->arg1)
+ || isBogusAtom(e->Iex.Qop.details->arg2)
+ || isBogusAtom(e->Iex.Qop.details->arg3)
+ || isBogusAtom(e->Iex.Qop.details->arg4);
+ case Iex_ITE:
+ return isBogusAtom(e->Iex.ITE.cond)
+ || isBogusAtom(e->Iex.ITE.iftrue)
+ || isBogusAtom(e->Iex.ITE.iffalse);
+ case Iex_Load:
+ return isBogusAtom(e->Iex.Load.addr);
+ case Iex_CCall:
+ for (i = 0; e->Iex.CCall.args[i]; i++)
+ if (isBogusAtom(e->Iex.CCall.args[i]))
+ return True;
+ return False;
+ default:
+ goto unhandled;
}
- return curr;
- }
- case Iex_Load: {
- Int dszB;
- dszB = sizeofIRType(e->Iex.Load.ty);
- /* assert that the B value for the address is already
- available (somewhere) */
- tl_assert(isIRAtom(e->Iex.Load.addr));
- tl_assert(mce->hWordTy == Ity_I32 || mce->hWordTy == Ity_I64);
- return gen_load_b( mce, dszB, e->Iex.Load.addr, 0 );
- }
- case Iex_ITE: {
- IRAtom* b1 = schemeE( mce, e->Iex.ITE.cond );
- IRAtom* b3 = schemeE( mce, e->Iex.ITE.iftrue );
- IRAtom* b2 = schemeE( mce, e->Iex.ITE.iffalse );
- return gen_maxU32( mce, b1, gen_maxU32( mce, b2, b3 ));
- }
- case Iex_Qop: {
- IRAtom* b1 = schemeE( mce, e->Iex.Qop.details->arg1 );
- IRAtom* b2 = schemeE( mce, e->Iex.Qop.details->arg2 );
- IRAtom* b3 = schemeE( mce, e->Iex.Qop.details->arg3 );
- IRAtom* b4 = schemeE( mce, e->Iex.Qop.details->arg4 );
- return gen_maxU32( mce, gen_maxU32( mce, b1, b2 ),
- gen_maxU32( mce, b3, b4 ) );
- }
- case Iex_Triop: {
- IRAtom* b1 = schemeE( mce, e->Iex.Triop.details->arg1 );
- IRAtom* b2 = schemeE( mce, e->Iex.Triop.details->arg2 );
- IRAtom* b3 = schemeE( mce, e->Iex.Triop.details->arg3 );
- return gen_maxU32( mce, b1, gen_maxU32( mce, b2, b3 ) );
- }
- case Iex_Binop: {
- switch (e->Iex.Binop.op) {
- case Iop_CasCmpEQ8: case Iop_CasCmpNE8:
- case Iop_CasCmpEQ16: case Iop_CasCmpNE16:
- case Iop_CasCmpEQ32: case Iop_CasCmpNE32:
- case Iop_CasCmpEQ64: case Iop_CasCmpNE64:
- /* Just say these all produce a defined result,
- regardless of their arguments. See
- COMMENT_ON_CasCmpEQ in this file. */
- return mkU32(0);
- default: {
- IRAtom* b1 = schemeE( mce, e->Iex.Binop.arg1 );
- IRAtom* b2 = schemeE( mce, e->Iex.Binop.arg2 );
- return gen_maxU32( mce, b1, b2 );
+ case Ist_Dirty:
+ d = st->Ist.Dirty.details;
+ for (i = 0; d->args[i]; i++) {
+ IRAtom* atom = d->args[i];
+ if (LIKELY(!is_IRExpr_VECRET_or_GSPTR(atom))) {
+ if (isBogusAtom(atom))
+ return True;
}
}
- tl_assert(0);
- /*NOTREACHED*/
- }
- case Iex_Unop: {
- IRAtom* b1 = schemeE( mce, e->Iex.Unop.arg );
- return b1;
+ if (isBogusAtom(d->guard))
+ return True;
+ if (d->mAddr && isBogusAtom(d->mAddr))
+ return True;
+ return False;
+ case Ist_Put:
+ return isBogusAtom(st->Ist.Put.data);
+ case Ist_PutI:
+ return isBogusAtom(st->Ist.PutI.details->ix)
+ || isBogusAtom(st->Ist.PutI.details->data);
+ case Ist_Store:
+ return isBogusAtom(st->Ist.Store.addr)
+ || isBogusAtom(st->Ist.Store.data);
+ case Ist_StoreG: {
+ IRStoreG* sg = st->Ist.StoreG.details;
+ return isBogusAtom(sg->addr) || isBogusAtom(sg->data)
+ || isBogusAtom(sg->guard);
}
- case Iex_Const:
- return mkU32(0);
- case Iex_RdTmp:
- return mkexpr( findShadowTmpB( mce, e->Iex.RdTmp.tmp ));
- case Iex_Get: {
- Int b_offset = MC_(get_otrack_shadow_offset)(
- e->Iex.Get.offset,
- sizeofIRType(e->Iex.Get.ty)
- );
- tl_assert(b_offset >= -1
- && b_offset <= mce->layout->total_sizeB -4);
- if (b_offset >= 0) {
- /* FIXME: this isn't an atom! */
- return IRExpr_Get( b_offset + 2*mce->layout->total_sizeB,
- Ity_I32 );
- }
- return mkU32(0);
+ case Ist_LoadG: {
+ IRLoadG* lg = st->Ist.LoadG.details;
+ return isBogusAtom(lg->addr) || isBogusAtom(lg->alt)
+ || isBogusAtom(lg->guard);
}
- default:
- VG_(printf)("mc_translate.c: schemeE: unhandled: ");
- ppIRExpr(e);
- VG_(tool_panic)("memcheck:schemeE");
+ case Ist_Exit:
+ return isBogusAtom(st->Ist.Exit.guard);
+ case Ist_AbiHint:
+ return isBogusAtom(st->Ist.AbiHint.base)
+ || isBogusAtom(st->Ist.AbiHint.nia);
+ case Ist_NoOp:
+ case Ist_IMark:
+ case Ist_MBE:
+ return False;
+ case Ist_CAS:
+ cas = st->Ist.CAS.details;
+ return isBogusAtom(cas->addr)
+ || (cas->expdHi ? isBogusAtom(cas->expdHi) : False)
+ || isBogusAtom(cas->expdLo)
+ || (cas->dataHi ? isBogusAtom(cas->dataHi) : False)
+ || isBogusAtom(cas->dataLo);
+ case Ist_LLSC:
+ return isBogusAtom(st->Ist.LLSC.addr)
+ || (st->Ist.LLSC.storedata
+ ? isBogusAtom(st->Ist.LLSC.storedata)
+ : False);
+ default:
+ unhandled:
+ ppIRStmt(st);
+ VG_(tool_panic)("hasBogusLiterals");
}
}
-static void do_origins_Dirty ( MCEnv* mce, IRDirty* d )
+IRSB* MC_(instrument) ( VgCallbackClosure* closure,
+ IRSB* sb_in,
+ const VexGuestLayout* layout,
+ const VexGuestExtents* vge,
+ const VexArchInfo* archinfo_host,
+ IRType gWordTy, IRType hWordTy )
{
- // This is a hacked version of do_shadow_Dirty
- Int i, k, n, toDo, gSz, gOff;
- IRAtom *here, *curr;
- IRTemp dst;
+ Bool verboze = 0||False;
+ Int i, j, first_stmt;
+ IRStmt* st;
+ MCEnv mce;
+ IRSB* sb_out;
- /* First check the guard. */
- curr = schemeE( mce, d->guard );
+ if (gWordTy != hWordTy) {
+ /* We don't currently support this case. */
+ VG_(tool_panic)("host/guest word size mismatch");
+ }
- /* Now round up all inputs and maxU32 over them. */
+ /* Check we're not completely nuts */
+ tl_assert(sizeof(UWord) == sizeof(void*));
+ tl_assert(sizeof(Word) == sizeof(void*));
+ tl_assert(sizeof(Addr) == sizeof(void*));
+ tl_assert(sizeof(ULong) == 8);
+ tl_assert(sizeof(Long) == 8);
+ tl_assert(sizeof(UInt) == 4);
+ tl_assert(sizeof(Int) == 4);
- /* Inputs: unmasked args
- Note: arguments are evaluated REGARDLESS of the guard expression */
- for (i = 0; d->args[i]; i++) {
- IRAtom* arg = d->args[i];
- if ( (d->cee->mcx_mask & (1<<i))
- || UNLIKELY(is_IRExpr_VECRET_or_GSPTR(arg)) ) {
- /* ignore this arg */
- } else {
- here = schemeE( mce, arg );
- curr = gen_maxU32( mce, curr, here );
- }
- }
+ tl_assert(MC_(clo_mc_level) >= 1 && MC_(clo_mc_level) <= 3);
- /* Inputs: guest state that we read. */
- for (i = 0; i < d->nFxState; i++) {
- tl_assert(d->fxState[i].fx != Ifx_None);
- if (d->fxState[i].fx == Ifx_Write)
- continue;
+ /* Set up SB */
+ sb_out = deepCopyIRSBExceptStmts(sb_in);
- /* Enumerate the described state segments */
- for (k = 0; k < 1 + d->fxState[i].nRepeats; k++) {
- gOff = d->fxState[i].offset + k * d->fxState[i].repeatLen;
- gSz = d->fxState[i].size;
+ /* Set up the running environment. Both .sb and .tmpMap are
+ modified as we go along. Note that tmps are added to both
+ .sb->tyenv and .tmpMap together, so the valid index-set for
+ those two arrays should always be identical. */
+ VG_(memset)(&mce, 0, sizeof(mce));
+ mce.sb = sb_out;
+ mce.trace = verboze;
+ mce.layout = layout;
+ mce.hWordTy = hWordTy;
+ mce.bogusLiterals = False;
- /* Ignore any sections marked as 'always defined'. */
- if (isAlwaysDefd(mce, gOff, gSz)) {
- if (0)
- VG_(printf)("memcheck: Dirty gst: ignored off %d, sz %d\n",
- gOff, gSz);
- continue;
- }
+ /* Do expensive interpretation for Iop_Add32 and Iop_Add64 on
+ Darwin. 10.7 is mostly built with LLVM, which uses these for
+ bitfield inserts, and we get a lot of false errors if the cheap
+ interpretation is used, alas. Could solve this much better if
+ we knew which of such adds came from x86/amd64 LEA instructions,
+ since these are the only ones really needing the expensive
+ interpretation, but that would require some way to tag them in
+ the _toIR.c front ends, which is a lot of faffing around. So
+ for now just use the slow and blunt-instrument solution. */
+ mce.useLLVMworkarounds = False;
+# if defined(VGO_darwin)
+ mce.useLLVMworkarounds = True;
+# endif
- /* This state element is read or modified. So we need to
- consider it. If larger than 4 bytes, deal with it in
- 4-byte chunks. */
- while (True) {
- Int b_offset;
- tl_assert(gSz >= 0);
- if (gSz == 0) break;
- n = gSz <= 4 ? gSz : 4;
- /* update 'curr' with maxU32 of the state slice
- gOff .. gOff+n-1 */
- b_offset = MC_(get_otrack_shadow_offset)(gOff, 4);
- if (b_offset != -1) {
- /* Observe the guard expression. If it is false use 0, i.e.
- nothing is known about the origin */
- IRAtom *cond, *iffalse, *iftrue;
+ mce.tmpMap = VG_(newXA)( VG_(malloc), "mc.MC_(instrument).1", VG_(free),
+ sizeof(TempMapEnt));
+ VG_(hintSizeXA) (mce.tmpMap, sb_in->tyenv->types_used);
+ for (i = 0; i < sb_in->tyenv->types_used; i++) {
+ TempMapEnt ent;
+ ent.kind = Orig;
+ ent.shadowV = IRTemp_INVALID;
+ ent.shadowB = IRTemp_INVALID;
+ VG_(addToXA)( mce.tmpMap, &ent );
+ }
+ tl_assert( VG_(sizeXA)( mce.tmpMap ) == sb_in->tyenv->types_used );
+
+ if (MC_(clo_expensive_definedness_checks)) {
+ /* For expensive definedness checking skip looking for bogus
+ literals. */
+ mce.bogusLiterals = True;
+ } else {
+ /* Make a preliminary inspection of the statements, to see if there
+ are any dodgy-looking literals. If there are, we generate
+ extra-detailed (hence extra-expensive) instrumentation in
+ places. Scan the whole bb even if dodgyness is found earlier,
+ so that the flatness assertion is applied to all stmts. */
+ Bool bogus = False;
+
+ for (i = 0; i < sb_in->stmts_used; i++) {
+ st = sb_in->stmts[i];
+ tl_assert(st);
+ tl_assert(isFlatIRStmt(st));
- cond = assignNew( 'B', mce, Ity_I1, d->guard);
- iffalse = mkU32(0);
- iftrue = assignNew( 'B', mce, Ity_I32,
- IRExpr_Get(b_offset
- + 2*mce->layout->total_sizeB,
- Ity_I32));
- here = assignNew( 'B', mce, Ity_I32,
- IRExpr_ITE(cond, iftrue, iffalse));
- curr = gen_maxU32( mce, curr, here );
+ if (!bogus) {
+ bogus = checkForBogusLiterals(st);
+ if (0 && bogus) {
+ VG_(printf)("bogus: ");
+ ppIRStmt(st);
+ VG_(printf)("\n");
}
- gSz -= n;
- gOff += n;
+ if (bogus) break;
}
}
+ mce.bogusLiterals = bogus;
}
- /* Inputs: memory */
+ /* Copy verbatim any IR preamble preceding the first IMark */
- if (d->mFx != Ifx_None) {
- /* Because we may do multiple shadow loads/stores from the same
- base address, it's best to do a single test of its
- definedness right now. Post-instrumentation optimisation
- should remove all but this test. */
- tl_assert(d->mAddr);
- here = schemeE( mce, d->mAddr );
- curr = gen_maxU32( mce, curr, here );
- }
+ tl_assert(mce.sb == sb_out);
+ tl_assert(mce.sb != sb_in);
- /* Deal with memory inputs (reads or modifies) */
- if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) {
- toDo = d->mSize;
- /* chew off 32-bit chunks. We don't care about the endianness
- since it's all going to be condensed down to a single bit,
- but nevertheless choose an endianness which is hopefully
- native to the platform. */
- while (toDo >= 4) {
- here = gen_guarded_load_b( mce, 4, d->mAddr, d->mSize - toDo,
- d->guard );
- curr = gen_maxU32( mce, curr, here );
- toDo -= 4;
- }
- /* handle possible 16-bit excess */
- while (toDo >= 2) {
- here = gen_guarded_load_b( mce, 2, d->mAddr, d->mSize - toDo,
- d->guard );
- curr = gen_maxU32( mce, curr, here );
- toDo -= 2;
- }
- /* chew off the remaining 8-bit chunk, if any */
- if (toDo == 1) {
- here = gen_guarded_load_b( mce, 1, d->mAddr, d->mSize - toDo,
- d->guard );
- curr = gen_maxU32( mce, curr, here );
- toDo -= 1;
- }
- tl_assert(toDo == 0);
- }
+ i = 0;
+ while (i < sb_in->stmts_used && sb_in->stmts[i]->tag != Ist_IMark) {
- /* Whew! So curr is a 32-bit B-value which should give an origin
- of some use if any of the inputs to the helper are undefined.
- Now we need to re-distribute the results to all destinations. */
+ st = sb_in->stmts[i];
+ tl_assert(st);
+ tl_assert(isFlatIRStmt(st));
- /* Outputs: the destination temporary, if there is one. */
- if (d->tmp != IRTemp_INVALID) {
- dst = findShadowTmpB(mce, d->tmp);
- assign( 'V', mce, dst, curr );
+ stmt( 'C', &mce, sb_in->stmts[i] );
+ i++;
}
- /* Outputs: guest state that we write or modify. */
- for (i = 0; i < d->nFxState; i++) {
- tl_assert(d->fxState[i].fx != Ifx_None);
- if (d->fxState[i].fx == Ifx_Read)
- continue;
+ /* Nasty problem. IR optimisation of the pre-instrumented IR may
+ cause the IR following the preamble to contain references to IR
+ temporaries defined in the preamble. Because the preamble isn't
+ instrumented, these temporaries don't have any shadows.
+ Nevertheless uses of them following the preamble will cause
+ memcheck to generate references to their shadows. End effect is
+ to cause IR sanity check failures, due to references to
+ non-existent shadows. This is only evident for the complex
+ preambles used for function wrapping on TOC-afflicted platforms
+ (ppc64-linux).
- /* Enumerate the described state segments */
- for (k = 0; k < 1 + d->fxState[i].nRepeats; k++) {
- gOff = d->fxState[i].offset + k * d->fxState[i].repeatLen;
- gSz = d->fxState[i].size;
+ The following loop therefore scans the preamble looking for
+ assignments to temporaries. For each one found it creates an
+ assignment to the corresponding (V) shadow temp, marking it as
+ 'defined'. This is the same resulting IR as if the main
+ instrumentation loop before had been applied to the statement
+ 'tmp = CONSTANT'.
- /* Ignore any sections marked as 'always defined'. */
- if (isAlwaysDefd(mce, gOff, gSz))
- continue;
+ Similarly, if origin tracking is enabled, we must generate an
+ assignment for the corresponding origin (B) shadow, claiming
+ no-origin, as appropriate for a defined value.
+ */
+ for (j = 0; j < i; j++) {
+ if (sb_in->stmts[j]->tag == Ist_WrTmp) {
+ /* findShadowTmpV checks its arg is an original tmp;
+ no need to assert that here. */
+ IRTemp tmp_o = sb_in->stmts[j]->Ist.WrTmp.tmp;
+ IRTemp tmp_v = findShadowTmpV(&mce, tmp_o);
+ IRType ty_v = typeOfIRTemp(sb_out->tyenv, tmp_v);
+ assign( 'V', &mce, tmp_v, definedOfType( ty_v ) );
+ if (MC_(clo_mc_level) == 3) {
+ IRTemp tmp_b = findShadowTmpB(&mce, tmp_o);
+ tl_assert(typeOfIRTemp(sb_out->tyenv, tmp_b) == Ity_I32);
+ assign( 'B', &mce, tmp_b, mkU32(0)/* UNKNOWN ORIGIN */);
+ }
+ if (0) {
+ VG_(printf)("create shadow tmp(s) for preamble tmp [%d] ty ", j);
+ ppIRType( ty_v );
+ VG_(printf)("\n");
+ }
+ }
+ }
- /* This state element is written or modified. So we need to
- consider it. If larger than 4 bytes, deal with it in
- 4-byte chunks. */
- while (True) {
- Int b_offset;
- tl_assert(gSz >= 0);
- if (gSz == 0) break;
- n = gSz <= 4 ? gSz : 4;
- /* Write 'curr' to the state slice gOff .. gOff+n-1 */
- b_offset = MC_(get_otrack_shadow_offset)(gOff, 4);
- if (b_offset != -1) {
+ /* Iterate over the remaining stmts to generate instrumentation. */
- /* If the guard expression evaluates to false we simply Put
- the value that is already stored in the guest state slot */
- IRAtom *cond, *iffalse;
+ tl_assert(sb_in->stmts_used > 0);
+ tl_assert(i >= 0);
+ tl_assert(i < sb_in->stmts_used);
+ tl_assert(sb_in->stmts[i]->tag == Ist_IMark);
- cond = assignNew('B', mce, Ity_I1,
- d->guard);
- iffalse = assignNew('B', mce, Ity_I32,
- IRExpr_Get(b_offset +
- 2*mce->layout->total_sizeB,
- Ity_I32));
- curr = assignNew('V', mce, Ity_I32,
- IRExpr_ITE(cond, curr, iffalse));
+ for (/* use current i*/; i < sb_in->stmts_used; i++) {
- stmt( 'B', mce, IRStmt_Put(b_offset
- + 2*mce->layout->total_sizeB,
- curr ));
- }
- gSz -= n;
- gOff += n;
- }
- }
- }
+ st = sb_in->stmts[i];
+ first_stmt = sb_out->stmts_used;
- /* Outputs: memory that we write or modify. Same comments about
- endianness as above apply. */
- if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) {
- toDo = d->mSize;
- /* chew off 32-bit chunks */
- while (toDo >= 4) {
- gen_store_b( mce, 4, d->mAddr, d->mSize - toDo, curr,
- d->guard );
- toDo -= 4;
- }
- /* handle possible 16-bit excess */
- while (toDo >= 2) {
- gen_store_b( mce, 2, d->mAddr, d->mSize - toDo, curr,
- d->guard );
- toDo -= 2;
- }
- /* chew off the remaining 8-bit chunk, if any */
- if (toDo == 1) {
- gen_store_b( mce, 1, d->mAddr, d->mSize - toDo, curr,
- d->guard );
- toDo -= 1;
+ if (verboze) {
+ VG_(printf)("\n");
+ ppIRStmt(st);
+ VG_(printf)("\n");
}
- tl_assert(toDo == 0);
- }
-}
+ if (MC_(clo_mc_level) == 3) {
+ /* See comments on case Ist_CAS below. */
+ if (st->tag != Ist_CAS)
+ schemeS( &mce, st );
+ }
-/* Generate IR for origin shadowing for a general guarded store. */
-static void do_origins_Store_guarded ( MCEnv* mce,
- IREndness stEnd,
- IRExpr* stAddr,
- IRExpr* stData,
- IRExpr* guard )
-{
- Int dszB;
- IRAtom* dataB;
- /* assert that the B value for the address is already available
- (somewhere), since the call to schemeE will want to see it.
- XXXX how does this actually ensure that?? */
- tl_assert(isIRAtom(stAddr));
- tl_assert(isIRAtom(stData));
- dszB = sizeofIRType( typeOfIRExpr(mce->sb->tyenv, stData ) );
- dataB = schemeE( mce, stData );
- gen_store_b( mce, dszB, stAddr, 0/*offset*/, dataB, guard );
-}
+ /* Generate instrumentation code for each stmt ... */
+ switch (st->tag) {
-/* Generate IR for origin shadowing for a plain store. */
-static void do_origins_Store_plain ( MCEnv* mce,
- IREndness stEnd,
- IRExpr* stAddr,
- IRExpr* stData )
-{
- do_origins_Store_guarded ( mce, stEnd, stAddr, stData,
- NULL/*guard*/ );
-}
+ case Ist_WrTmp:
+ assign( 'V', &mce, findShadowTmpV(&mce, st->Ist.WrTmp.tmp),
+ expr2vbits( &mce, st->Ist.WrTmp.data) );
+ break;
+ case Ist_Put:
+ do_shadow_PUT( &mce,
+ st->Ist.Put.offset,
+ st->Ist.Put.data,
+ NULL /* shadow atom */, NULL /* guard */ );
+ break;
-/* ---- Dealing with LoadG/StoreG (not entirely simple) ---- */
+ case Ist_PutI:
+ do_shadow_PUTI( &mce, st->Ist.PutI.details);
+ break;
-static void do_origins_StoreG ( MCEnv* mce, IRStoreG* sg )
-{
- do_origins_Store_guarded( mce, sg->end, sg->addr,
- sg->data, sg->guard );
-}
+ case Ist_Store:
+ do_shadow_Store( &mce, st->Ist.Store.end,
+ st->Ist.Store.addr, 0/* addr bias */,
+ st->Ist.Store.data,
+ NULL /* shadow data */,
+ NULL/*guard*/ );
+ break;
-static void do_origins_LoadG ( MCEnv* mce, IRLoadG* lg )
-{
- IRType loadedTy = Ity_INVALID;
- switch (lg->cvt) {
- case ILGop_IdentV128: loadedTy = Ity_V128; break;
- case ILGop_Ident64: loadedTy = Ity_I64; break;
- case ILGop_Ident32: loadedTy = Ity_I32; break;
- case ILGop_16Uto32: loadedTy = Ity_I16; break;
- case ILGop_16Sto32: loadedTy = Ity_I16; break;
- case ILGop_8Uto32: loadedTy = Ity_I8; break;
- case ILGop_8Sto32: loadedTy = Ity_I8; break;
- default: VG_(tool_panic)("schemeS.IRLoadG");
- }
- IRAtom* ori_alt
- = schemeE( mce,lg->alt );
- IRAtom* ori_final
- = expr2ori_Load_guarded_General(mce, loadedTy,
- lg->addr, 0/*addr bias*/,
- lg->guard, ori_alt );
- /* And finally, bind the origin to the destination temporary. */
- assign( 'B', mce, findShadowTmpB(mce, lg->dst), ori_final );
-}
+ case Ist_StoreG:
+ do_shadow_StoreG( &mce, st->Ist.StoreG.details );
+ break;
+ case Ist_LoadG:
+ do_shadow_LoadG( &mce, st->Ist.LoadG.details );
+ break;
-static void schemeS ( MCEnv* mce, IRStmt* st )
-{
- tl_assert(MC_(clo_mc_level) == 3);
+ case Ist_Exit:
+ complainIfUndefined( &mce, st->Ist.Exit.guard, NULL );
+ break;
- switch (st->tag) {
+ case Ist_IMark:
+ break;
- case Ist_AbiHint:
- /* The value-check instrumenter handles this - by arranging
- to pass the address of the next instruction to
- MC_(helperc_MAKE_STACK_UNINIT). This is all that needs to
- happen for origin tracking w.r.t. AbiHints. So there is
- nothing to do here. */
- break;
+ case Ist_NoOp:
+ case Ist_MBE:
+ break;
- case Ist_PutI: {
- IRPutI *puti = st->Ist.PutI.details;
- IRRegArray* descr_b;
- IRAtom *t1, *t2, *t3, *t4;
- IRRegArray* descr = puti->descr;
- IRType equivIntTy
- = MC_(get_otrack_reg_array_equiv_int_type)(descr);
- /* If this array is unshadowable for whatever reason,
- generate no code. */
- if (equivIntTy == Ity_INVALID)
+ case Ist_Dirty:
+ do_shadow_Dirty( &mce, st->Ist.Dirty.details );
break;
- tl_assert(sizeofIRType(equivIntTy) >= 4);
- tl_assert(sizeofIRType(equivIntTy) == sizeofIRType(descr->elemTy));
- descr_b
- = mkIRRegArray( descr->base + 2*mce->layout->total_sizeB,
- equivIntTy, descr->nElems );
- /* Compute a value to Put - the conjoinment of the origin for
- the data to be Put-ted (obviously) and of the index value
- (not so obviously). */
- t1 = schemeE( mce, puti->data );
- t2 = schemeE( mce, puti->ix );
- t3 = gen_maxU32( mce, t1, t2 );
- t4 = zWidenFrom32( mce, equivIntTy, t3 );
- stmt( 'B', mce, IRStmt_PutI( mkIRPutI(descr_b, puti->ix,
- puti->bias, t4) ));
- break;
- }
- case Ist_Dirty:
- do_origins_Dirty( mce, st->Ist.Dirty.details );
- break;
+ case Ist_AbiHint:
+ do_AbiHint( &mce, st->Ist.AbiHint.base,
+ st->Ist.AbiHint.len,
+ st->Ist.AbiHint.nia );
+ break;
- case Ist_Store:
- do_origins_Store_plain( mce, st->Ist.Store.end,
- st->Ist.Store.addr,
- st->Ist.Store.data );
- break;
+ case Ist_CAS:
+ do_shadow_CAS( &mce, st->Ist.CAS.details );
+ /* Note, do_shadow_CAS copies the CAS itself to the output
+ block, because it needs to add instrumentation both
+ before and after it. Hence skip the copy below. Also
+ skip the origin-tracking stuff (call to schemeS) above,
+ since that's all tangled up with it too; do_shadow_CAS
+ does it all. */
+ break;
- case Ist_StoreG:
- do_origins_StoreG( mce, st->Ist.StoreG.details );
- break;
+ case Ist_LLSC:
+ do_shadow_LLSC( &mce,
+ st->Ist.LLSC.end,
+ st->Ist.LLSC.result,
+ st->Ist.LLSC.addr,
+ st->Ist.LLSC.storedata );
+ break;
- case Ist_LoadG:
- do_origins_LoadG( mce, st->Ist.LoadG.details );
- break;
+ default:
+ VG_(printf)("\n");
+ ppIRStmt(st);
+ VG_(printf)("\n");
+ VG_(tool_panic)("memcheck: unhandled IRStmt");
- case Ist_LLSC: {
- /* In short: treat a load-linked like a normal load followed
- by an assignment of the loaded (shadow) data the result
- temporary. Treat a store-conditional like a normal store,
- and mark the result temporary as defined. */
- if (st->Ist.LLSC.storedata == NULL) {
- /* Load Linked */
- IRType resTy
- = typeOfIRTemp(mce->sb->tyenv, st->Ist.LLSC.result);
- IRExpr* vanillaLoad
- = IRExpr_Load(st->Ist.LLSC.end, resTy, st->Ist.LLSC.addr);
- tl_assert(resTy == Ity_I64 || resTy == Ity_I32
- || resTy == Ity_I16 || resTy == Ity_I8);
- assign( 'B', mce, findShadowTmpB(mce, st->Ist.LLSC.result),
- schemeE(mce, vanillaLoad));
- } else {
- /* Store conditional */
- do_origins_Store_plain( mce, st->Ist.LLSC.end,
- st->Ist.LLSC.addr,
- st->Ist.LLSC.storedata );
- /* For the rationale behind this, see comments at the
- place where the V-shadow for .result is constructed, in
- do_shadow_LLSC. In short, we regard .result as
- always-defined. */
- assign( 'B', mce, findShadowTmpB(mce, st->Ist.LLSC.result),
- mkU32(0) );
- }
- break;
- }
+ } /* switch (st->tag) */
- case Ist_Put: {
- Int b_offset
- = MC_(get_otrack_shadow_offset)(
- st->Ist.Put.offset,
- sizeofIRType(typeOfIRExpr(mce->sb->tyenv, st->Ist.Put.data))
- );
- if (b_offset >= 0) {
- /* FIXME: this isn't an atom! */
- stmt( 'B', mce, IRStmt_Put(b_offset + 2*mce->layout->total_sizeB,
- schemeE( mce, st->Ist.Put.data )) );
+ if (0 && verboze) {
+ for (j = first_stmt; j < sb_out->stmts_used; j++) {
+ VG_(printf)(" ");
+ ppIRStmt(sb_out->stmts[j]);
+ VG_(printf)("\n");
}
- break;
+ VG_(printf)("\n");
}
- case Ist_WrTmp:
- assign( 'B', mce, findShadowTmpB(mce, st->Ist.WrTmp.tmp),
- schemeE(mce, st->Ist.WrTmp.data) );
- break;
-
- case Ist_MBE:
- case Ist_NoOp:
- case Ist_Exit:
- case Ist_IMark:
- break;
-
- default:
- VG_(printf)("mc_translate.c: schemeS: unhandled: ");
- ppIRStmt(st);
- VG_(tool_panic)("memcheck:schemeS");
+ /* ... and finally copy the stmt itself to the output. Except,
+ skip the copy of IRCASs; see comments on case Ist_CAS
+ above. */
+ if (st->tag != Ist_CAS)
+ stmt('C', &mce, st);
}
-}
-
-/*------------------------------------------------------------*/
-/*--- Startup assertion checking ---*/
-/*------------------------------------------------------------*/
+ /* Now we need to complain if the jump target is undefined. */
+ first_stmt = sb_out->stmts_used;
-void MC_(do_instrumentation_startup_checks)( void )
-{
- /* Make a best-effort check to see that is_helperc_value_checkN_fail
- is working as we expect. */
+ if (verboze) {
+ VG_(printf)("sb_in->next = ");
+ ppIRExpr(sb_in->next);
+ VG_(printf)("\n\n");
+ }
-# define CHECK(_expected, _string) \
- tl_assert((_expected) == is_helperc_value_checkN_fail(_string))
+ complainIfUndefined( &mce, sb_in->next, NULL );
- /* It should identify these 8, and no others, as targets. */
- CHECK(True, "MC_(helperc_value_check8_fail_no_o)");
- CHECK(True, "MC_(helperc_value_check4_fail_no_o)");
- CHECK(True, "MC_(helperc_value_check0_fail_no_o)");
- CHECK(True, "MC_(helperc_value_check1_fail_no_o)");
- CHECK(True, "MC_(helperc_value_check8_fail_w_o)");
- CHECK(True, "MC_(helperc_value_check0_fail_w_o)");
- CHECK(True, "MC_(helperc_value_check1_fail_w_o)");
- CHECK(True, "MC_(helperc_value_check4_fail_w_o)");
+ if (0 && verboze) {
+ for (j = first_stmt; j < sb_out->stmts_used; j++) {
+ VG_(printf)(" ");
+ ppIRStmt(sb_out->stmts[j]);
+ VG_(printf)("\n");
+ }
+ VG_(printf)("\n");
+ }
- /* Ad-hoc selection of other strings gathered via a quick test. */
- CHECK(False, "amd64g_dirtyhelper_CPUID_avx2");
- CHECK(False, "amd64g_dirtyhelper_RDTSC");
- CHECK(False, "MC_(helperc_b_load1)");
- CHECK(False, "MC_(helperc_b_load2)");
- CHECK(False, "MC_(helperc_b_load4)");
- CHECK(False, "MC_(helperc_b_load8)");
- CHECK(False, "MC_(helperc_b_load16)");
- CHECK(False, "MC_(helperc_b_load32)");
- CHECK(False, "MC_(helperc_b_store1)");
- CHECK(False, "MC_(helperc_b_store2)");
- CHECK(False, "MC_(helperc_b_store4)");
- CHECK(False, "MC_(helperc_b_store8)");
- CHECK(False, "MC_(helperc_b_store16)");
- CHECK(False, "MC_(helperc_b_store32)");
- CHECK(False, "MC_(helperc_LOADV8)");
- CHECK(False, "MC_(helperc_LOADV16le)");
- CHECK(False, "MC_(helperc_LOADV32le)");
- CHECK(False, "MC_(helperc_LOADV64le)");
- CHECK(False, "MC_(helperc_LOADV128le)");
- CHECK(False, "MC_(helperc_LOADV256le)");
- CHECK(False, "MC_(helperc_STOREV16le)");
- CHECK(False, "MC_(helperc_STOREV32le)");
- CHECK(False, "MC_(helperc_STOREV64le)");
- CHECK(False, "MC_(helperc_STOREV8)");
- CHECK(False, "track_die_mem_stack_8");
- CHECK(False, "track_new_mem_stack_8_w_ECU");
- CHECK(False, "MC_(helperc_MAKE_STACK_UNINIT_w_o)");
- CHECK(False, "VG_(unknown_SP_update_w_ECU)");
+ /* If this fails, there's been some serious snafu with tmp management,
+ that should be investigated. */
+ tl_assert( VG_(sizeXA)( mce.tmpMap ) == mce.sb->tyenv->types_used );
+ VG_(deleteXA)( mce.tmpMap );
-# undef CHECK
+ tl_assert(mce.sb == sb_out);
+ return sb_out;
}