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
This commit is contained in:
Julian Seward 2009-07-09 10:52:03 +00:00
parent 338cd1a59a
commit 4b19c85350

View File

@ -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 */