From 4b19c853507cee42be71450f67b1fa6435cb74ef Mon Sep 17 00:00:00 2001 From: Julian Seward Date: Thu, 9 Jul 2009 10:52:03 +0000 Subject: [PATCH] Add support for IRStmt_CAS to the h_ instrumenter. This should stop exp-ptrcheck asserting on threaded programs on x86 and amd64. Vast amounts of fiddly complication that makes my head hurt. Urr. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@10418 --- exp-ptrcheck/h_main.c | 294 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 262 insertions(+), 32 deletions(-) diff --git a/exp-ptrcheck/h_main.c b/exp-ptrcheck/h_main.c index aaac5cc167..2e344d8bd8 100644 --- a/exp-ptrcheck/h_main.c +++ b/exp-ptrcheck/h_main.c @@ -2996,7 +2996,7 @@ static inline UWord do_store_conditional_64( Addr m/*dst*/, ULong t/*val*/ ) /* Apply nonptr_or_unknown to all the words intersecting [a, a+len). */ -static VG_REGPARM(2) +static inline VG_REGPARM(2) void nonptr_or_unknown_range ( Addr a, SizeT len ) { const SizeT wszB = sizeof(UWord); @@ -3009,6 +3009,34 @@ void nonptr_or_unknown_range ( Addr a, SizeT len ) } } +// Write to shadow memory, for a 32-bit store. Must only +// be used on 32-bit targets. +static inline VG_REGPARM(2) +void do_shadow_store4_P( Addr m, Seg* vseg ) +{ + tl_assert(sizeof(UWord) == 4); /* DO NOT REMOVE */ + if (VG_IS_4_ALIGNED(m)) { + set_mem_vseg( m, vseg ); + } else { + // straddling two words + nonptr_or_unknown_range(m, 4); + } +} + +// Write to shadow memory, for a 64-bit store. Must only +// be used on 64-bit targets. +static inline VG_REGPARM(2) +void do_shadow_store8_P( Addr m, Seg* vseg ) +{ + tl_assert(sizeof(UWord) == 8); /* DO NOT REMOVE */ + if (VG_IS_8_ALIGNED(m)) { + set_mem_vseg( m, vseg ); + } else { + // straddling two words + nonptr_or_unknown_range(m, 8); + } +} + // This handles 128 bit stores on 64 bit targets. The // store data is passed in 2 pieces, the most significant // bits first. @@ -3117,12 +3145,7 @@ void check_store8_P(Addr m, Seg* mptr_vseg, UWord t, Seg* t_vseg) check_load_or_store(/*is_write*/True, m, 8, mptr_vseg); // Actually *do* the STORE here *(ULong*)m = t; - if (VG_IS_8_ALIGNED(m)) { - set_mem_vseg( m, t_vseg ); - } else { - // straddling two words - nonptr_or_unknown_range(m, 8); - } + do_shadow_store8_P( m, t_vseg ); } // This handles 64 bit store-conditionals on 64 bit targets. It must @@ -3139,12 +3162,8 @@ UWord check_store8C_P(Addr m, Seg* mptr_vseg, UWord t, Seg* t_vseg) check_load_or_store(/*is_write*/True, m, 8, mptr_vseg); // Actually *do* the STORE here success = do_store_conditional_64( m, t ); - if (VG_IS_8_ALIGNED(m)) { - set_mem_vseg( m, t_vseg ); - } else { - // straddling two words - nonptr_or_unknown_range(m, 8); - } + if (success) + do_shadow_store8_P( m, t_vseg ); return success; } @@ -3161,12 +3180,7 @@ void check_store4_P(Addr m, Seg* mptr_vseg, UWord t, Seg* t_vseg) check_load_or_store(/*is_write*/True, m, 4, mptr_vseg); // Actually *do* the STORE here *(UInt*)m = t; - if (VG_IS_4_ALIGNED(m)) { - set_mem_vseg( m, t_vseg ); - } else { - // straddling two words - nonptr_or_unknown_range(m, 4); - } + do_shadow_store4_P( m, t_vseg ); } // This handles 32 bit store-conditionals on 32 bit targets. It must @@ -3183,12 +3197,8 @@ UWord check_store4C_P(Addr m, Seg* mptr_vseg, UWord t, Seg* t_vseg) check_load_or_store(/*is_write*/True, m, 4, mptr_vseg); // Actually *do* the STORE here success = do_store_conditional_32( m, t ); - if (VG_IS_4_ALIGNED(m)) { - set_mem_vseg( m, t_vseg ); - } else { - // straddling two words - nonptr_or_unknown_range(m, 4); - } + if (success) + do_shadow_store4_P( m, t_vseg ); return success; } @@ -3218,7 +3228,8 @@ UWord check_store4C(Addr m, Seg* mptr_vseg, UWord t) check_load_or_store(/*is_write*/True, m, 4, mptr_vseg); // Actually *do* the STORE here success = do_store_conditional_32( m, t ); - nonptr_or_unknown_range(m, 4); + if (success) + nonptr_or_unknown_range(m, 4); return success; } @@ -3852,8 +3863,10 @@ static IRTemp gen_dirty_W_WWWW ( PCEnv* pce, void* h_fn, HChar* h_nm, } /* Version of gen_dirty_W_WW with no return value. Callee must be a - VG_REGPARM(2) function.*/ -static void gen_dirty_v_WW ( PCEnv* pce, void* h_fn, HChar* h_nm, + VG_REGPARM(2) function. If guard is non-NULL then it is used to + conditionalise the call. */ +static void gen_dirty_v_WW ( PCEnv* pce, IRExpr* guard, + void* h_fn, HChar* h_nm, IRExpr* a1, IRExpr* a2 ) { IRDirty* di; @@ -3864,6 +3877,8 @@ static void gen_dirty_v_WW ( PCEnv* pce, void* h_fn, HChar* h_nm, di = unsafeIRDirty_0_N( 2/*regparms*/, h_nm, VG_(fnptr_to_fnentry)( h_fn ), mkIRExprVec_2( a1, a2 ) ); + if (guard) + di->guard = guard; stmt( 'I', pce, IRStmt_Dirty(di) ); } @@ -4255,9 +4270,10 @@ void instrument_arithop ( PCEnv* pce, static void gen_call_nonptr_or_unknown_range ( PCEnv* pce, + IRExpr* guard, IRAtom* addr, IRAtom* len ) { - gen_dirty_v_WW( pce, + gen_dirty_v_WW( pce, guard, &nonptr_or_unknown_range, "nonptr_or_unknown_range", addr, len ); @@ -4298,6 +4314,220 @@ static void schemeS ( PCEnv* pce, IRStmt* st ) switch (st->tag) { + case Ist_CAS: { + IRCAS* cas = st->Ist.CAS.details; + IRType elTy = typeOfIRExpr(pce->sb->tyenv, cas->expdLo); + if (cas->oldHi == IRTemp_INVALID) { + /* ------------ SINGLE CAS ------------ */ + /* -- single cas -- 32 bits, on 32-bit host -- */ + /* -- single cas -- 64 bits, on 64-bit host -- */ + /* -- viz, single cas, native-word case -- */ + if ( (pce->gWordTy == Ity_I32 && elTy == Ity_I32) + || (pce->gWordTy == Ity_I64 && elTy == Ity_I64) ) { + // 32 bit host translation scheme; 64-bit is analogous + // old# = check_load4_P(addr, addr#) + // old = CAS(addr:expd->new) [COPY] + // success = CmpEQ32(old,expd) + // if (success) do_shadow_store4_P(addr, new#) + IRTemp success; + Bool is64 = elTy == Ity_I64; + IROp cmpEQ = is64 ? Iop_CmpEQ64 : Iop_CmpEQ32; + void* r_fn = is64 ? &check_load8_P : &check_load4_P; + HChar* r_nm = is64 ? "check_load8_P" : "check_load4_P"; + void* w_fn = is64 ? &do_shadow_store8_P : &do_shadow_store4_P; + void* w_nm = is64 ? "do_shadow_store8_P" : "do_shadow_store4_P"; + IRExpr* addr = cas->addr; + IRExpr* addrV = schemeEw_Atom(pce, addr); + IRTemp old = cas->oldLo; + IRTemp oldV = newShadowTmp(pce, old); + IRExpr* nyu = cas->dataLo; + IRExpr* nyuV = schemeEw_Atom(pce, nyu); + IRExpr* expd = cas->expdLo; + assign( 'I', pce, oldV, + mkexpr( gen_dirty_W_WW( pce, r_fn, r_nm, addr, addrV ))); + stmt( 'C', pce, st ); + success = newTemp(pce, Ity_I1, NonShad); + assign('I', pce, success, binop(cmpEQ, mkexpr(old), expd)); + gen_dirty_v_WW( pce, mkexpr(success), w_fn, w_nm, addr, nyuV ); + } + else + /* -- single cas -- 8 or 16 bits, on 32-bit host -- */ + /* -- viz, single cas, 32-bit subword cases -- */ + if (pce->gWordTy == Ity_I32 + && (elTy == Ity_I8 || elTy == Ity_I16)) { + // 8-bit translation scheme; 16-bit is analogous + // check_load1(addr, addr#) + // old = CAS(addr:expd->new) [COPY] + // success = CmpEQ8(old,expd) + // if (success) nonptr_or_unknown_range(addr, 1) + IRTemp success; + Bool is16 = elTy == Ity_I16; + IRExpr* addr = cas->addr; + IRExpr* addrV = schemeEw_Atom(pce, addr); + IRTemp old = cas->oldLo; + IRExpr* expd = cas->expdLo; + void* h_fn = is16 ? &check_load2 : &check_load1; + HChar* h_nm = is16 ? "check_load2" : "check_load1"; + IROp cmpEQ = is16 ? Iop_CmpEQ16 : Iop_CmpEQ8; + Int szB = is16 ? 2 : 1; + gen_dirty_v_WW( pce, NULL, h_fn, h_nm, addr, addrV ); + stmt( 'C', pce, st ); + success = newTemp(pce, Ity_I1, NonShad); + assign('I', pce, success, + binop(cmpEQ, mkexpr(old), expd)); + gen_call_nonptr_or_unknown_range( pce, mkexpr(success), + addr, mkIRExpr_HWord(szB) ); + } + else + /* -- single cas -- 8, 16 or 32 bits, on 64-bit host -- */ + /* -- viz, single cas, 64-bit subword cases -- */ + if (pce->gWordTy == Ity_I64 + && (elTy == Ity_I8 || elTy == Ity_I16 || elTy == Ity_I32)) { + // 8-bit translation scheme; 16/32-bit are analogous + // check_load1(addr, addr#) + // old = CAS(addr:expd->new) [COPY] + // success = CmpEQ8(old,expd) + // if (success) nonptr_or_unknown_range(addr, 1) + IRTemp success; + Bool is16 = elTy == Ity_I16; + Bool is32 = elTy == Ity_I32; + IRExpr* addr = cas->addr; + IRExpr* addrV = schemeEw_Atom(pce, addr); + IRTemp old = cas->oldLo; + IRExpr* expd = cas->expdLo; + void* h_fn = is32 ? &check_load4 + : (is16 ? &check_load2 : &check_load1); + HChar* h_nm = is32 ? "check_load4" + : (is16 ? "check_load2" : "check_load1"); + IROp cmpEQ = is32 ? Iop_CmpEQ32 + : (is16 ? Iop_CmpEQ16 : Iop_CmpEQ8); + Int szB = is32 ? 4 : (is16 ? 2 : 1); + gen_dirty_v_WW( pce, NULL, h_fn, h_nm, addr, addrV ); + stmt( 'C', pce, st ); + success = newTemp(pce, Ity_I1, NonShad); + assign('I', pce, success, + binop(cmpEQ, mkexpr(old), expd)); + gen_call_nonptr_or_unknown_range( pce, mkexpr(success), + addr, mkIRExpr_HWord(szB) ); + } + else + goto unhandled; + } else { + /* ------------ DOUBLE CAS ------------ */ + /* Punt on bigendian DCAS. In fact it's probably trivial + to do; just swap the individual shadow loads/stores + around in memory, but we'd have to verify it, and there + is no use case. So punt. */ + if (cas->end != Iend_LE) + goto unhandled; + /* -- double cas -- 2 x 32 bits, on 32-bit host -- */ + /* -- double cas -- 2 x 64 bits, on 64-bit host -- */ + /* -- viz, double cas, native-word case -- */ + if ( (pce->gWordTy == Ity_I32 && elTy == Ity_I32) + || (pce->gWordTy == Ity_I64 && elTy == Ity_I64) ) { + // 32 bit host translation scheme; 64-bit is analogous + // oldHi# = check_load4_P(addr+4, addr#) + // oldLo# = check_load4_P(addr+0, addr#) + // oldHi/Lo = DCAS(addr:expdHi/Lo->newHi/Lo) [COPY] + // success = CmpEQ32(oldHi,expdHi) && CmpEQ32(oldLo,expdLo) + // = ((oldHi ^ expdHi) | (oldLo ^ expdLo)) == 0 + // if (success) do_shadow_store4_P(addr+4, newHi#) + // if (success) do_shadow_store4_P(addr+0, newLo#) + IRTemp diffHi, diffLo, diff, success, addrpp; + Bool is64 = elTy == Ity_I64; + void* r_fn = is64 ? &check_load8_P : &check_load4_P; + HChar* r_nm = is64 ? "check_load8_P" : "check_load4_P"; + void* w_fn = is64 ? &do_shadow_store8_P + : &do_shadow_store4_P; + void* w_nm = is64 ? "do_shadow_store8_P" + : "do_shadow_store4_P"; + IROp opADD = is64 ? Iop_Add64 : Iop_Add32; + IROp opXOR = is64 ? Iop_Xor64 : Iop_Xor32; + IROp opOR = is64 ? Iop_Or64 : Iop_Or32; + IROp opCmpEQ = is64 ? Iop_CmpEQ64 : Iop_CmpEQ32; + IRExpr* step = is64 ? mkU64(8) : mkU32(4); + IRExpr* zero = is64 ? mkU64(0) : mkU32(0); + IRExpr* addr = cas->addr; + IRExpr* addrV = schemeEw_Atom(pce, addr); + IRTemp oldLo = cas->oldLo; + IRTemp oldLoV = newShadowTmp(pce, oldLo); + IRTemp oldHi = cas->oldHi; + IRTemp oldHiV = newShadowTmp(pce, oldHi); + IRExpr* nyuLo = cas->dataLo; + IRExpr* nyuLoV = schemeEw_Atom(pce, nyuLo); + IRExpr* nyuHi = cas->dataHi; + IRExpr* nyuHiV = schemeEw_Atom(pce, nyuHi); + IRExpr* expdLo = cas->expdLo; + IRExpr* expdHi = cas->expdHi; + tl_assert(elTy == Ity_I32 || elTy == Ity_I64); + tl_assert(pce->gWordTy == elTy); + addrpp = newTemp(pce, elTy, NonShad); + assign('I', pce, addrpp, binop(opADD, addr, step)); + assign('I', pce, oldHiV, + mkexpr( gen_dirty_W_WW( pce, r_fn, r_nm, + mkexpr(addrpp), addrV )) + ); + assign('I', pce, oldLoV, + mkexpr( gen_dirty_W_WW( pce, r_fn, r_nm, + addr, addrV )) + ); + stmt( 'C', pce, st ); + diffHi = newTemp(pce, elTy, NonShad); + assign('I', pce, diffHi, + binop(opXOR, mkexpr(oldHi), expdHi)); + diffLo = newTemp(pce, elTy, NonShad); + assign('I', pce, diffLo, + binop(opXOR, mkexpr(oldLo), expdLo)); + diff = newTemp(pce, elTy, NonShad); + assign('I', pce, diff, + binop(opOR, mkexpr(diffHi), mkexpr(diffLo))); + success = newTemp(pce, Ity_I1, NonShad); + assign('I', pce, success, + binop(opCmpEQ, mkexpr(diff), zero)); + gen_dirty_v_WW( pce, mkexpr(success), + w_fn, w_nm, mkexpr(addrpp), nyuHiV ); + gen_dirty_v_WW( pce, mkexpr(success), + w_fn, w_nm, addr, nyuLoV ); + } + else + /* -- double cas -- 2 x 32 bits, on 64-bit host -- */ + if (pce->gWordTy == Ity_I64 && elTy == Ity_I32) { + // check_load8(addr, addr#) + // oldHi/Lo = DCAS(addr:expdHi/Lo->newHi/Lo) [COPY] + // success = CmpEQ32(oldHi,expdHi) && CmpEQ32(oldLo,expdLo) + // = ((oldHi ^ expdHi) | (oldLo ^ expdLo)) == 0 + // if (success) nonptr_or_unknown_range(addr, 8) + IRTemp diffHi, diffLo, diff, success; + IRExpr* addr = cas->addr; + IRExpr* addrV = schemeEw_Atom(pce, addr); + IRTemp oldLo = cas->oldLo; + IRTemp oldHi = cas->oldHi; + IRExpr* expdLo = cas->expdLo; + IRExpr* expdHi = cas->expdHi; + gen_dirty_v_WW( pce, NULL, &check_load8, "check_load8", + addr, addrV ); + stmt( 'C', pce, st ); + diffHi = newTemp(pce, Ity_I32, NonShad); + assign('I', pce, diffHi, + binop(Iop_Xor32, mkexpr(oldHi), expdHi)); + diffLo = newTemp(pce, Ity_I32, NonShad); + assign('I', pce, diffLo, + binop(Iop_Xor32, mkexpr(oldLo), expdLo)); + diff = newTemp(pce, Ity_I32, NonShad); + assign('I', pce, diff, + binop(Iop_Or32, mkexpr(diffHi), mkexpr(diffLo))); + success = newTemp(pce, Ity_I1, NonShad); + assign('I', pce, success, + binop(Iop_CmpEQ32, mkexpr(diff), mkU32(0))); + gen_call_nonptr_or_unknown_range( pce, mkexpr(success), + addr, mkU64(8) ); + } + else + goto unhandled; + } + break; + } + case Ist_Dirty: { Int i; IRDirty* di; @@ -4353,7 +4583,7 @@ static void schemeS ( PCEnv* pce, IRStmt* st ) if (di->mFx != Ifx_None) { tl_assert(di->mAddr && isIRAtom(di->mAddr)); tl_assert(di->mSize > 0); - gen_call_nonptr_or_unknown_range( pce, di->mAddr, + gen_call_nonptr_or_unknown_range( pce, NULL, di->mAddr, mkIRExpr_HWord(di->mSize)); } break; @@ -4750,7 +4980,7 @@ static void schemeS ( PCEnv* pce, IRStmt* st ) mkexpr( gen_dirty_W_WW( pce, h_fn, h_nm, addr, addrv )) ); } else { - gen_dirty_v_WW( pce, h_fn, h_nm, addr, addrv ); + gen_dirty_v_WW( pce, NULL, h_fn, h_nm, addr, addrv ); } } else { /* 64 bit host/guest (cough, cough) */ @@ -4779,7 +5009,7 @@ static void schemeS ( PCEnv* pce, IRStmt* st ) mkexpr( gen_dirty_W_WW( pce, h_fn, h_nm, addr, addrv )) ); } else { - gen_dirty_v_WW( pce, h_fn, h_nm, addr, addrv ); + gen_dirty_v_WW( pce, NULL, h_fn, h_nm, addr, addrv ); } } /* copy the original -- must happen after the helper call */ -- 2.47.3