|
From: <sv...@va...> - 2013-01-17 14:24:44
|
sewardj 2013-01-17 14:24:35 +0000 (Thu, 17 Jan 2013)
New Revision: 13236
Log:
Merge, from branches/COMEM, revisions 13139 to 13235.
Modified directories:
trunk/
Modified files:
trunk/cachegrind/cg_main.c
trunk/callgrind/main.c
trunk/callgrind/sim.c
trunk/drd/drd_load_store.c
trunk/helgrind/hg_main.c
trunk/lackey/lk_main.c
trunk/memcheck/mc_translate.c
Modified: trunk/
Modified: trunk/memcheck/mc_translate.c (+429 -121)
===================================================================
--- trunk/memcheck/mc_translate.c 2013-01-16 22:07:02 +00:00 (rev 13235)
+++ trunk/memcheck/mc_translate.c 2013-01-17 14:24:35 +00:00 (rev 13236)
@@ -43,6 +43,56 @@
#include "mc_include.h"
+/* Comments re guarded loads and stores, and conditional dirty calls
+ that access memory, JRS 2012-Dec-14, for branches/COMEM, r13180.
+
+ Currently Memcheck generates code that checks the definedness of
+ addresses in such cases, regardless of the what the guard value is
+ (at runtime). This could potentially lead to false positives if we
+ ever construct IR in which a guarded memory access happens, and the
+ address is undefined when the guard is false. However, at the
+ moment I am not aware of any situations where such IR is generated.
+
+ The obvious thing to do is generate conditionalised checking code
+ in such cases. However:
+
+ * it's more complex to verify
+
+ * it is cheaper to always do the check -- basically a check if
+ the shadow value is nonzero, and conditional call to report
+ an error if so -- than it is to conditionalise the check.
+
+ * currently the implementation is incomplete. complainIfUndefined
+ can correctly conditionalise the check and complaint as per its
+ third argument. However, the part of it that then sets the
+ shadow to 'defined' (see comments at the top of said fn) ignores
+ the guard.
+
+ Therefore, removing this functionality in r13181 until we know we
+ need it. To reinstate, do the following:
+
+ * back out r13181 (which also adds this comment)
+
+ * undo (== reinstate the non-NULL 3rd args) in the following two
+ chunks, which were removed in r13142. These are the only two
+ places where complainIfUndefined is actually used with a guard.
+
+ // First, emit a definedness test for the address. This also sets
+ // the address (shadow) to 'defined' following the test.
+ - complainIfUndefined( mce, addr, guard );
+ + complainIfUndefined( mce, addr, NULL );
+
+ and
+
+ IRType tyAddr;
+ tl_assert(d->mAddr);
+ - complainIfUndefined(mce, d->mAddr, d->guard);
+ + complainIfUndefined(mce, d->mAddr, NULL);
+
+ * fix complainIfUndefined to conditionalise setting the shadow temp
+ to 'defined', as described above.
+*/
+
/* FIXMEs JRS 2011-June-16.
Check the interpretation for vector narrowing and widening ops,
@@ -1090,17 +1140,20 @@
}
-/* Check the supplied **original** atom for undefinedness, and emit a
+/* Check the supplied *original* |atom| for undefinedness, and emit a
complaint if so. Once that happens, mark it as defined. This is
possible because the atom is either a tmp or literal. If it's a
tmp, it will be shadowed by a tmp, and so we can set the shadow to
be defined. In fact as mentioned above, we will have to allocate a
new tmp to carry the new 'defined' shadow value, and update the
original->tmp mapping accordingly; we cannot simply assign a new
- value to an existing shadow tmp as this breaks SSAness -- resulting
- in the post-instrumentation sanity checker spluttering in disapproval.
+ value to an existing shadow tmp as this breaks SSAness.
+
+ It may be that any resulting complaint should only be emitted
+ conditionally, as defined by |guard|. If |guard| is NULL then it
+ is assumed to be always-true.
*/
-static void complainIfUndefined ( MCEnv* mce, IRAtom* atom, IRExpr *guard )
+static void complainIfUndefined ( MCEnv* mce, IRAtom* atom )
{
IRAtom* vatom;
IRType ty;
@@ -1233,16 +1286,6 @@
VG_(fnptr_to_fnentry)( fn ), args );
di->guard = cond;
- /* If the complaint is to be issued under a guard condition, AND that
- guard condition. */
- if (guard) {
- IRAtom *g1 = assignNew('V', mce, Ity_I32, unop(Iop_1Uto32, di->guard));
- IRAtom *g2 = assignNew('V', mce, Ity_I32, unop(Iop_1Uto32, guard));
- IRAtom *e = assignNew('V', mce, Ity_I32, binop(Iop_And32, g1, g2));
-
- di->guard = assignNew('V', mce, Ity_I1, unop(Iop_32to1, e));
- }
-
setHelperAnns( mce, di );
stmt( 'V', mce, IRStmt_Dirty(di));
@@ -1374,7 +1417,7 @@
arrSize = descr->nElems * sizeofIRType(ty);
tl_assert(ty != Ity_I1);
tl_assert(isOriginalAtom(mce,ix));
- complainIfUndefined(mce, ix, NULL);
+ complainIfUndefined(mce, ix);
if (isAlwaysDefd(mce, descr->base, arrSize)) {
/* later: no ... */
/* emit code to emit a complaint if any of the vbits are 1. */
@@ -1423,7 +1466,7 @@
Int arrSize = descr->nElems * sizeofIRType(ty);
tl_assert(ty != Ity_I1);
tl_assert(isOriginalAtom(mce,ix));
- complainIfUndefined(mce, ix, NULL);
+ complainIfUndefined(mce, ix);
if (isAlwaysDefd(mce, descr->base, arrSize)) {
/* Always defined, return all zeroes of the relevant type */
return definedOfType(tyS);
@@ -2569,15 +2612,15 @@
/* IRRoundingModeDFP(I32) x I8 x D128 -> D128 */
return mkLazy3(mce, Ity_I128, vatom1, vatom2, vatom3);
case Iop_ExtractV128:
- complainIfUndefined(mce, atom3, NULL);
+ complainIfUndefined(mce, atom3);
return assignNew('V', mce, Ity_V128, triop(op, vatom1, vatom2, atom3));
case Iop_Extract64:
- complainIfUndefined(mce, atom3, NULL);
+ complainIfUndefined(mce, atom3);
return assignNew('V', mce, Ity_I64, triop(op, vatom1, vatom2, atom3));
case Iop_SetElem8x8:
case Iop_SetElem16x4:
case Iop_SetElem32x2:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_I64, triop(op, vatom1, atom2, vatom3));
default:
ppIROp(op);
@@ -2644,7 +2687,7 @@
case Iop_ShlN32x2:
case Iop_ShlN8x8:
/* Same scheme as with all other shifts. */
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_I64, binop(op, vatom1, atom2));
case Iop_QNarrowBin32Sto16Sx4:
@@ -2727,25 +2770,25 @@
case Iop_QShlN8Sx8:
case Iop_QShlN8x8:
case Iop_QSalN8x8:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast8x8(mce, vatom1);
case Iop_QShlN16Sx4:
case Iop_QShlN16x4:
case Iop_QSalN16x4:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast16x4(mce, vatom1);
case Iop_QShlN32Sx2:
case Iop_QShlN32x2:
case Iop_QSalN32x2:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast32x2(mce, vatom1);
case Iop_QShlN64Sx1:
case Iop_QShlN64x1:
case Iop_QSalN64x1:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast32x2(mce, vatom1);
case Iop_PwMax32Sx2:
@@ -2842,13 +2885,13 @@
return assignNew('V', mce, Ity_I64, binop(op, vatom1, vatom2));
case Iop_GetElem8x8:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_I8, binop(op, vatom1, atom2));
case Iop_GetElem16x4:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_I16, binop(op, vatom1, atom2));
case Iop_GetElem32x2:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_I32, binop(op, vatom1, atom2));
/* Perm8x8: rearrange values in left arg using steering values
@@ -2878,7 +2921,7 @@
/* Same scheme as with all other shifts. Note: 22 Oct 05:
this is wrong now, scalar shifts are done properly lazily.
Vector shifts should be fixed too. */
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_V128, binop(op, vatom1, atom2));
/* V x V shifts/rotates are done using the standard lazy scheme. */
@@ -2925,14 +2968,14 @@
case Iop_F32ToFixed32Sx4_RZ:
case Iop_Fixed32UToF32x4_RN:
case Iop_Fixed32SToF32x4_RN:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast32x4(mce, vatom1);
case Iop_F32ToFixed32Ux2_RZ:
case Iop_F32ToFixed32Sx2_RZ:
case Iop_Fixed32UToF32x2_RN:
case Iop_Fixed32SToF32x2_RN:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast32x2(mce, vatom1);
case Iop_QSub8Ux16:
@@ -3089,25 +3132,25 @@
case Iop_QShlN8Sx16:
case Iop_QShlN8x16:
case Iop_QSalN8x16:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast8x16(mce, vatom1);
case Iop_QShlN16Sx8:
case Iop_QShlN16x8:
case Iop_QSalN16x8:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast16x8(mce, vatom1);
case Iop_QShlN32Sx4:
case Iop_QShlN32x4:
case Iop_QSalN32x4:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast32x4(mce, vatom1);
case Iop_QShlN64Sx2:
case Iop_QShlN64x2:
case Iop_QSalN64x2:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return mkPCast32x4(mce, vatom1);
case Iop_Mull32Sx2:
@@ -3170,16 +3213,16 @@
return assignNew('V', mce, Ity_V128, binop(op, vatom1, vatom2));
case Iop_GetElem8x16:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_I8, binop(op, vatom1, atom2));
case Iop_GetElem16x8:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_I16, binop(op, vatom1, atom2));
case Iop_GetElem32x4:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_I32, binop(op, vatom1, atom2));
case Iop_GetElem64x2:
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_I64, binop(op, vatom1, atom2));
/* Perm8x16: rearrange values in left arg using steering values
@@ -3238,7 +3281,7 @@
/* Same scheme as with all other shifts. Note: 10 Nov 05:
this is wrong now, scalar shifts are done properly lazily.
Vector shifts should be fixed too. */
- complainIfUndefined(mce, atom2, NULL);
+ complainIfUndefined(mce, atom2);
return assignNew('V', mce, Ity_V128, binop(op, vatom1, atom2));
/* I128-bit data-steering */
@@ -3626,6 +3669,11 @@
static
IRExpr* expr2vbits_Unop ( MCEnv* mce, IROp op, IRAtom* atom )
{
+ /* For the widening operations {8,16,32}{U,S}to{16,32,64}, the
+ selection of shadow operation implicitly duplicates the logic in
+ do_shadow_LoadG and should be kept in sync (in the very unlikely
+ event that the interpretation of such widening ops changes in
+ future). See comment in do_shadow_LoadG. */
IRAtom* vatom = expr2vbits( mce, atom );
tl_assert(isOriginalAtom(mce,atom));
switch (op) {
@@ -3932,11 +3980,13 @@
}
-/* Worker function; do not call directly. */
+/* Worker function; do not call directly. See comments on
+ expr2vbits_Load for the meaning of 'guard'. If 'guard' evaluates
+ to False at run time, the returned value is all-ones. */
static
IRAtom* expr2vbits_Load_WRK ( MCEnv* mce,
IREndness end, IRType ty,
- IRAtom* addr, UInt bias )
+ IRAtom* addr, UInt bias, IRAtom* guard )
{
void* helper;
const HChar* hname;
@@ -3949,7 +3999,7 @@
/* First, emit a definedness test for the address. This also sets
the address (shadow) to 'defined' following the test. */
- complainIfUndefined( mce, addr, NULL );
+ complainIfUndefined( mce, addr );
/* Now cook up a call to the relevant helper function, to read the
data V bits from shadow memory. */
@@ -4012,16 +4062,34 @@
hname, VG_(fnptr_to_fnentry)( helper ),
mkIRExprVec_1( addrAct ));
setHelperAnns( mce, di );
+ if (guard) {
+ di->guard = guard;
+ /* Ideally the didn't-happen return value here would be all-ones
+ (all-undefined), so it'd be obvious if it got used
+ inadvertantly. We can get by with the IR-mandated default
+ value (0b01 repeating, 0x55 etc) as that'll still look pretty
+ undefined if it ever leaks out. */
+ }
stmt( 'V', mce, IRStmt_Dirty(di) );
return mkexpr(datavbits);
}
+/* Generate IR to do a shadow load. The helper is expected to check
+ the validity of the address and return the V bits for that address.
+ This can optionally be controlled by a guard, which is assumed to
+ be True if NULL. In the case where the guard is False at runtime,
+ the helper will return the didn't-do-the-call value of all-ones.
+ Since all ones means "completely undefined result", the caller of
+ this function will need to fix up the result somehow in that
+ case.
+*/
static
IRAtom* expr2vbits_Load ( MCEnv* mce,
IREndness end, IRType ty,
- IRAtom* addr, UInt bias )
+ IRAtom* addr, UInt bias,
+ IRAtom* guard )
{
tl_assert(end == Iend_LE || end == Iend_BE);
switch (shadowTypeV(ty)) {
@@ -4029,15 +4097,15 @@
case Ity_I16:
case Ity_I32:
case Ity_I64:
- return expr2vbits_Load_WRK(mce, end, ty, addr, bias);
+ return expr2vbits_Load_WRK(mce, end, ty, addr, bias, guard);
case Ity_V128: {
IRAtom *v64hi, *v64lo;
if (end == Iend_LE) {
- v64lo = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+0);
- v64hi = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+8);
+ v64lo = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+0, guard);
+ v64hi = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+8, guard);
} else {
- v64hi = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+0);
- v64lo = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+8);
+ v64hi = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+0, guard);
+ v64lo = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+8, guard);
}
return assignNew( 'V', mce,
Ity_V128,
@@ -4047,10 +4115,14 @@
/* V256-bit case -- phrased in terms of 64 bit units (Qs),
with Q3 being the most significant lane. */
if (end == Iend_BE) goto unhandled;
- IRAtom* v64Q0 = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+0);
- IRAtom* v64Q1 = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+8);
- IRAtom* v64Q2 = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+16);
- IRAtom* v64Q3 = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+24);
+ IRAtom* v64Q0
+ = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+0, guard);
+ IRAtom* v64Q1
+ = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+8, guard);
+ IRAtom* v64Q2
+ = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+16, guard);
+ IRAtom* v64Q3
+ = expr2vbits_Load_WRK(mce, end, Ity_I64, addr, bias+24, guard);
return assignNew( 'V', mce,
Ity_V256,
IRExpr_Qop(Iop_64x4toV256,
@@ -4063,29 +4135,86 @@
}
-/* If there is no guard expression or the guard is always TRUE this function
- behaves like expr2vbits_Load. If the guard is not true at runtime, an
- all-bits-defined bit pattern will be returned.
- It is assumed that definedness of GUARD has already been checked at the call
- site. */
+/* The most general handler for guarded loads. Assumes the
+ definedness of GUARD and ADDR have already been checked by the
+ caller. A GUARD of NULL is assumed to mean "always True".
+
+ Generate IR to do a shadow load from ADDR and return the V bits.
+ The loaded type is TY. The loaded data is then (shadow) widened by
+ using VWIDEN, which can be Iop_INVALID to denote a no-op. If GUARD
+ evaluates to False at run time then the returned Vbits are simply
+ VALT instead. Note therefore that the argument type of VWIDEN must
+ be TY and the result type of VWIDEN must equal the type of VALT.
+*/
static
-IRAtom* expr2vbits_guarded_Load ( MCEnv* mce,
- IREndness end, IRType ty,
- IRAtom* addr, UInt bias, IRAtom *guard )
+IRAtom* expr2vbits_Load_guarded_General ( MCEnv* mce,
+ IREndness end, IRType ty,
+ IRAtom* addr, UInt bias,
+ IRAtom* guard,
+ IROp vwiden, IRAtom* valt )
{
- if (guard) {
- IRAtom *cond, *iffalse, *iftrue;
+ /* Sanity check the conversion operation, and also set TYWIDE. */
+ IRType tyWide = Ity_INVALID;
+ switch (vwiden) {
+ case Iop_INVALID:
+ tyWide = ty;
+ break;
+ case Iop_16Uto32: case Iop_16Sto32: case Iop_8Uto32: case Iop_8Sto32:
+ tyWide = Ity_I32;
+ break;
+ default:
+ VG_(tool_panic)("memcheck:expr2vbits_Load_guarded_General");
+ }
- cond = assignNew('V', mce, Ity_I8, unop(Iop_1Uto8, guard));
- iftrue = assignNew('V', mce, ty,
- expr2vbits_Load(mce, end, ty, addr, bias));
- iffalse = assignNew('V', mce, ty, definedOfType(ty));
+ /* If the guard evaluates to True, this will hold the loaded V bits
+ at TY. If the guard evaluates to False, this will be all
+ ones, meaning "all undefined", in which case we will have to
+ replace it using a Mux0X below. */
+ IRAtom* iftrue1
+ = assignNew('V', mce, ty,
+ expr2vbits_Load(mce, end, ty, addr, bias, guard));
+ /* Now (shadow-) widen the loaded V bits to the desired width. In
+ the guard-is-False case, the allowable widening operators will
+ in the worst case (unsigned widening) at least leave the
+ pre-widened part as being marked all-undefined, and in the best
+ case (signed widening) mark the whole widened result as
+ undefined. Anyway, it doesn't matter really, since in this case
+ we will replace said value with the default value |valt| using a
+ Mux0X. */
+ IRAtom* iftrue2
+ = vwiden == Iop_INVALID
+ ? iftrue1
+ : assignNew('V', mce, tyWide, unop(vwiden, iftrue1));
+ /* These are the V bits we will return if the load doesn't take
+ place. */
+ IRAtom* iffalse
+ = valt;
+ /* Prepare the cond for the Mux0X. Convert a NULL cond into
+ something that iropt knows how to fold out later. */
+ IRAtom* cond
+ = guard == NULL
+ ? mkU8(1)
+ : assignNew('V', mce, Ity_I8, unop(Iop_1Uto8, guard));
+ /* And assemble the final result. */
+ return assignNew('V', mce, tyWide, IRExpr_Mux0X(cond, iffalse, iftrue2));
+}
- return assignNew('V', mce, ty, IRExpr_Mux0X(cond, iffalse, iftrue));
- }
- /* No guard expression or unconditional load */
- return expr2vbits_Load(mce, end, ty, addr, bias);
+/* A simpler handler for guarded loads, in which there is no
+ conversion operation, and the default V bit return (when the guard
+ evaluates to False at runtime) is "all defined". If there is no
+ guard expression or the guard is always TRUE this function behaves
+ like expr2vbits_Load. It is assumed that definedness of GUARD and
+ ADDR has already been checked at the call site. */
+static
+IRAtom* expr2vbits_Load_guarded_Simple ( MCEnv* mce,
+ IREndness end, IRType ty,
+ IRAtom* addr, UInt bias,
+ IRAtom *guard )
+{
+ return expr2vbits_Load_guarded_General(
+ mce, end, ty, addr, bias, guard, Iop_INVALID, definedOfType(ty)
+ );
}
@@ -4164,7 +4293,8 @@
case Iex_Load:
return expr2vbits_Load( mce, e->Iex.Load.end,
e->Iex.Load.ty,
- e->Iex.Load.addr, 0/*addr bias*/ );
+ e->Iex.Load.addr, 0/*addr bias*/,
+ NULL/* guard == "always True"*/ );
case Iex_CCall:
return mkLazyN( mce, e->Iex.CCall.args,
@@ -4234,13 +4364,17 @@
}
-/* Generate a shadow store. addr is always the original address atom.
- You can pass in either originals or V-bits for the data atom, but
- obviously not both. guard :: Ity_I1 controls whether the store
- really happens; NULL means it unconditionally does. Note that
- guard itself is not checked for definedness; the caller of this
- function must do that if necessary. */
+/* Generate a shadow store. |addr| is always the original address
+ atom. You can pass in either originals or V-bits for the data
+ atom, but obviously not both. This function generates a check for
+ the definedness of |addr|. That check is performed regardless of
+ whether |guard| is true or not.
+ |guard| :: Ity_I1 controls whether the store really happens; NULL
+ means it unconditionally does. Note that |guard| itself is not
+ checked for definedness; the caller of this function must do that
+ if necessary.
+*/
static
void do_shadow_Store ( MCEnv* mce,
IREndness end,
@@ -4298,7 +4432,7 @@
/* First, emit a definedness test for the address. This also sets
the address (shadow) to 'defined' following the test. */
- complainIfUndefined( mce, addr, guard );
+ complainIfUndefined( mce, addr );
/* Now decide which helper function to call to write the data V
bits into shadow memory. */
@@ -4525,7 +4659,7 @@
# endif
/* First check the guard. */
- complainIfUndefined(mce, d->guard, NULL);
+ complainIfUndefined(mce, d->guard);
/* Now round up all inputs and PCast over them. */
curr = definedOfType(Ity_I32);
@@ -4599,7 +4733,7 @@
should remove all but this test. */
IRType tyAddr;
tl_assert(d->mAddr);
- complainIfUndefined(mce, d->mAddr, d->guard);
+ complainIfUndefined(mce, d->mAddr);
tyAddr = typeOfIRExpr(mce->sb->tyenv, d->mAddr);
tl_assert(tyAddr == Ity_I32 || tyAddr == Ity_I64);
@@ -4616,8 +4750,8 @@
while (toDo >= 4) {
here = mkPCastTo(
mce, Ity_I32,
- expr2vbits_guarded_Load ( mce, end, Ity_I32, d->mAddr,
- d->mSize - toDo, d->guard )
+ expr2vbits_Load_guarded_Simple(
+ mce, end, Ity_I32, d->mAddr, d->mSize - toDo, d->guard )
);
curr = mkUifU32(mce, here, curr);
toDo -= 4;
@@ -4626,8 +4760,8 @@
while (toDo >= 2) {
here = mkPCastTo(
mce, Ity_I32,
- expr2vbits_guarded_Load ( mce, end, Ity_I16, d->mAddr,
- d->mSize - toDo, d->guard )
+ expr2vbits_Load_guarded_Simple(
+ mce, end, Ity_I16, d->mAddr, d->mSize - toDo, d->guard )
);
curr = mkUifU32(mce, here, curr);
toDo -= 2;
@@ -4636,8 +4770,8 @@
if (toDo == 1) {
here = mkPCastTo(
mce, Ity_I32,
- expr2vbits_guarded_Load ( mce, end, Ity_I8, d->mAddr,
- d->mSize - toDo, d->guard )
+ expr2vbits_Load_guarded_Simple(
+ mce, end, Ity_I8, d->mAddr, d->mSize - toDo, d->guard )
);
curr = mkUifU32(mce, here, curr);
toDo -= 1;
@@ -5007,7 +5141,8 @@
'V', mce, elemTy,
expr2vbits_Load(
mce,
- cas->end, elemTy, cas->addr, 0/*Addr bias*/
+ cas->end, elemTy, cas->addr, 0/*Addr bias*/,
+ NULL/*always happens*/
));
bind_shadow_tmp_to_orig('V', mce, mkexpr(cas->oldLo), voldLo);
if (otrak) {
@@ -5135,14 +5270,16 @@
'V', mce, elemTy,
expr2vbits_Load(
mce,
- cas->end, elemTy, cas->addr, memOffsHi/*Addr bias*/
+ cas->end, elemTy, cas->addr, memOffsHi/*Addr bias*/,
+ NULL/*always happens*/
));
voldLo
= assignNew(
'V', mce, elemTy,
expr2vbits_Load(
mce,
- cas->end, elemTy, cas->addr, memOffsLo/*Addr bias*/
+ cas->end, elemTy, cas->addr, memOffsLo/*Addr bias*/,
+ NULL/*always happens*/
));
bind_shadow_tmp_to_orig('V', mce, mkexpr(cas->oldHi), voldHi);
bind_shadow_tmp_to_orig('V', mce, mkexpr(cas->oldLo), voldLo);
@@ -5229,7 +5366,8 @@
|| resTy == Ity_I16 || resTy == Ity_I8);
assign( 'V', mce, resTmp,
expr2vbits_Load(
- mce, stEnd, resTy, stAddr, 0/*addr bias*/));
+ mce, stEnd, resTy, stAddr, 0/*addr bias*/,
+ NULL/*always happens*/) );
} else {
/* Store Conditional */
/* Stay sane */
@@ -5263,6 +5401,58 @@
}
+/* ---- Dealing with LoadG/StoreG (not entirely simple) ---- */
+
+static void do_shadow_StoreG ( MCEnv* mce, IRStoreG* sg )
+{
+ if (0) VG_(printf)("XXXX StoreG\n");
+ complainIfUndefined(mce, sg->guard);
+ /* do_shadow_Store will check the definedness of sg->addr. */
+ do_shadow_Store( mce, sg->end,
+ sg->addr, 0/* addr bias */,
+ sg->data,
+ NULL /* shadow data */,
+ sg->guard );
+}
+
+static void do_shadow_LoadG ( MCEnv* mce, IRLoadG* lg )
+{
+ if (0) VG_(printf)("XXXX LoadG\n");
+ complainIfUndefined(mce, lg->guard);
+ /* expr2vbits_Load_guarded_General will check the definedness of
+ lg->addr. */
+
+ /* Look at the LoadG's built-in conversion operation, to determine
+ the source (actual loaded data) type, and the equivalent IROp.
+ NOTE that implicitly we are taking a widening operation to be
+ applied to original atoms and producing one that applies to V
+ bits. Since signed and unsigned widening are self-shadowing,
+ this is a straight copy of the op (modulo swapping from the
+ IRLoadGOp form to the IROp form). Note also therefore that this
+ implicitly duplicates the logic to do with said widening ops in
+ expr2vbits_Unop. See comment at the start of expr2vbits_Unop. */
+ IROp vwiden = Iop_INVALID;
+ IRType loadedTy = Ity_INVALID;
+ switch (lg->cvt) {
+ case ILGop_Ident32: loadedTy = Ity_I32; vwiden = Iop_INVALID; break;
+ case ILGop_16Uto32: loadedTy = Ity_I16; vwiden = Iop_16Uto32; break;
+ case ILGop_16Sto32: loadedTy = Ity_I16; vwiden = Iop_16Sto32; break;
+ case ILGop_8Uto32: loadedTy = Ity_I8; vwiden = Iop_8Uto32; break;
+ case ILGop_8Sto32: loadedTy = Ity_I8; vwiden = Iop_8Sto32; break;
+ default: VG_(tool_panic)("do_shadow_LoadG");
+ }
+
+ IRAtom* vbits_alt
+ = expr2vbits( mce, lg->alt );
+ IRAtom* vbits_final
+ = expr2vbits_Load_guarded_General(mce, lg->end, loadedTy,
+ lg->addr, 0/*addr bias*/,
+ lg->guard, vwiden, vbits_alt );
+ /* And finally, bind the V bits to the destination temporary. */
+ assign( 'V', mce, findShadowTmpV(mce, lg->dst), vbits_final );
+}
+
+
/*------------------------------------------------------------*/
/*--- Memcheck main ---*/
/*------------------------------------------------------------*/
@@ -5365,6 +5555,16 @@
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:
@@ -5602,8 +5802,16 @@
NULL/*guard*/ );
break;
+ case Ist_StoreG:
+ do_shadow_StoreG( &mce, st->Ist.StoreG.details );
+ break;
+
+ case Ist_LoadG:
+ do_shadow_LoadG( &mce, st->Ist.LoadG.details );
+ break;
+
case Ist_Exit:
- complainIfUndefined( &mce, st->Ist.Exit.guard, NULL );
+ complainIfUndefined( &mce, st->Ist.Exit.guard );
break;
case Ist_IMark:
@@ -5674,7 +5882,7 @@
VG_(printf)("\n\n");
}
- complainIfUndefined( &mce, sb_in->next, NULL );
+ complainIfUndefined( &mce, sb_in->next );
if (0 && verboze) {
for (j = first_stmt; j < sb_out->stmts_used; j++) {
@@ -5872,8 +6080,18 @@
return assignNew( 'B', mce, Ity_I32, binop(Iop_Max32U, b1, b2) );
}
-static IRAtom* gen_load_b ( MCEnv* mce, Int szB,
- IRAtom* baseaddr, Int offset )
+
+/* 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;
@@ -5916,6 +6134,15 @@
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 inadvertantly. 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) );
@@ -5930,25 +6157,56 @@
}
}
-static IRAtom* gen_guarded_load_b ( MCEnv* mce, Int szB, IRAtom* baseaddr,
- Int offset, IRAtom* guard )
+
+/* 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 )
{
- if (guard) {
- IRAtom *cond, *iffalse, *iftrue;
+ return gen_guarded_load_b(mce, szB, baseaddr, offset, NULL/*guard*/);
+}
- cond = assignNew('B', mce, Ity_I8, unop(Iop_1Uto8, guard));
- iftrue = assignNew('B', mce, Ity_I32,
- gen_load_b(mce, szB, baseaddr, offset));
- iffalse = mkU32(0);
- return assignNew('B', mce, Ity_I32, IRExpr_Mux0X(cond, iffalse, iftrue));
- }
+/* The most general handler for guarded origin loads. A GUARD of NULL
+ is assumed to mean "always True".
- return gen_load_b(mce, szB, baseaddr, offset);
+ 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 a Mux0X 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 Mux0X. Convert a NULL cond into
+ something that iropt knows how to fold out later. */
+ IRAtom* cond
+ = guard == NULL
+ ? mkU8(1)
+ : assignNew('B', mce, Ity_I8, unop(Iop_1Uto8, guard));
+ /* And assemble the final result. */
+ return assignNew('B', mce, Ity_I32, IRExpr_Mux0X(cond, iffalse, iftrue));
}
-/* Generate a shadow store. guard :: Ity_I1 controls whether the
- store really happens; NULL means it unconditionally does. */
+
+/* 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 )
@@ -6345,8 +6603,8 @@
}
/* handle possible 16-bit excess */
while (toDo >= 2) {
- gen_store_b( mce, 2, d->mAddr, d->mSize - toDo, curr,
- d->guard );
+ gen_store_b( mce, 2, d->mAddr, d->mSize - toDo, curr,
+ d->guard );
toDo -= 2;
}
/* chew off the remaining 8-bit chunk, if any */
@@ -6360,10 +6618,12 @@
}
-static void do_origins_Store ( MCEnv* mce,
- IREndness stEnd,
- IRExpr* stAddr,
- IRExpr* stData )
+/* 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;
@@ -6374,11 +6634,51 @@
tl_assert(isIRAtom(stData));
dszB = sizeofIRType( typeOfIRExpr(mce->sb->tyenv, stData ) );
dataB = schemeE( mce, stData );
- gen_store_b( mce, dszB, stAddr, 0/*offset*/, dataB,
- NULL/*guard*/ );
+ 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_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);
@@ -6426,11 +6726,19 @@
break;
case Ist_Store:
- do_origins_Store( mce, st->Ist.Store.end,
- st->Ist.Store.addr,
- st->Ist.Store.data );
+ 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;
+
+ case Ist_LoadG:
+ do_origins_LoadG( mce, st->Ist.LoadG.details );
+ break;
+
case Ist_LLSC: {
/* In short: treat a load-linked like a normal load followed
by an assignment of the loaded (shadow) data the result
@@ -6448,9 +6756,9 @@
schemeE(mce, vanillaLoad));
} else {
/* Store conditional */
- do_origins_Store( mce, st->Ist.LLSC.end,
- st->Ist.LLSC.addr,
- st->Ist.LLSC.storedata );
+ 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
Property changed: trunk (+0 -0)
___________________________________________________________________
Name: svn:mergeinfo
- /branches/TCHAIN:12477-12516
+ /branches/COMEM:13142-13235
/branches/TCHAIN:12477-12516
Modified: trunk/callgrind/main.c (+75 -6)
===================================================================
--- trunk/callgrind/main.c 2013-01-16 22:07:02 +00:00 (rev 13235)
+++ trunk/callgrind/main.c 2013-01-17 14:24:35 +00:00 (rev 13236)
@@ -669,6 +669,50 @@
}
static
+void addEvent_D_guarded ( ClgState* clgs, InstrInfo* inode,
+ Int datasize, IRAtom* ea, IRAtom* guard,
+ Bool isWrite )
+{
+ tl_assert(isIRAtom(ea));
+ tl_assert(guard);
+ tl_assert(isIRAtom(guard));
+ tl_assert(datasize >= 1);
+ if (!CLG_(clo).simulate_cache) return;
+ tl_assert(datasize <= CLG_(min_line_size));
+
+ /* Adding guarded memory actions and merging them with the existing
+ queue is too complex. Simply flush the queue and add this
+ action immediately. Since guarded loads and stores are pretty
+ rare, this is not thought likely to cause any noticeable
+ performance loss as a result of the loss of event-merging
+ opportunities. */
+ tl_assert(clgs->events_used >= 0);
+ flushEvents(clgs);
+ tl_assert(clgs->events_used == 0);
+ /* Same as case Ev_Dw / case Ev_Dr in flushEvents, except with guard */
+ IRExpr* i_node_expr;
+ const HChar* helperName;
+ void* helperAddr;
+ IRExpr** argv;
+ Int regparms;
+ IRDirty* di;
+ i_node_expr = mkIRExpr_HWord( (HWord)inode );
+ helperName = isWrite ? CLG_(cachesim).log_0I1Dw_name
+ : CLG_(cachesim).log_0I1Dr_name;
+ helperAddr = isWrite ? CLG_(cachesim).log_0I1Dw
+ : CLG_(cachesim).log_0I1Dr;
+ argv = mkIRExprVec_3( i_node_expr,
+ ea, mkIRExpr_HWord( datasize ) );
+ regparms = 3;
+ di = unsafeIRDirty_0_N(
+ regparms,
+ helperName, VG_(fnptr_to_fnentry)( helperAddr ),
+ argv );
+ di->guard = guard;
+ addStmtToIRSB( clgs->sbOut, IRStmt_Dirty(di) );
+}
+
+static
void addEvent_Bc ( ClgState* clgs, InstrInfo* inode, IRAtom* guard )
{
Event* evt;
@@ -912,14 +956,14 @@
VexArchInfo* archinfo_host,
IRType gWordTy, IRType hWordTy )
{
- Int i;
- IRStmt* st;
- Addr origAddr;
+ Int i;
+ IRStmt* st;
+ Addr origAddr;
InstrInfo* curr_inode = NULL;
- ClgState clgs;
- UInt cJumps = 0;
+ ClgState clgs;
+ UInt cJumps = 0;
+ IRTypeEnv* tyenv = sbIn->tyenv;
-
if (gWordTy != hWordTy) {
/* We don't currently support this case. */
VG_(tool_panic)("host/guest word size mismatch");
@@ -1022,6 +1066,31 @@
break;
}
+ case Ist_StoreG: {
+ IRStoreG* sg = st->Ist.StoreG.details;
+ IRExpr* data = sg->data;
+ IRExpr* addr = sg->addr;
+ IRType type = typeOfIRExpr(tyenv, data);
+ tl_assert(type != Ity_INVALID);
+ addEvent_D_guarded( &clgs, curr_inode,
+ sizeofIRType(type), addr, sg->guard,
+ True/*isWrite*/ );
+ break;
+ }
+
+ case Ist_LoadG: {
+ IRLoadG* lg = st->Ist.LoadG.details;
+ IRType type = Ity_INVALID; /* loaded type */
+ IRType typeWide = Ity_INVALID; /* after implicit widening */
+ IRExpr* addr = lg->addr;
+ typeOfIRLoadGOp(lg->cvt, &typeWide, &type);
+ tl_assert(type != Ity_INVALID);
+ addEvent_D_guarded( &clgs, curr_inode,
+ sizeofIRType(type), addr, lg->guard,
+ False/*!isWrite*/ );
+ break;
+ }
+
case Ist_Dirty: {
Int dataSize;
IRDirty* d = st->Ist.Dirty.details;
Modified: trunk/callgrind/sim.c (+4 -0)
===================================================================
--- trunk/callgrind/sim.c 2013-01-16 22:07:02 +00:00 (rev 13235)
+++ trunk/callgrind/sim.c 2013-01-17 14:24:35 +00:00 (rev 13236)
@@ -1189,6 +1189,9 @@
}
+/* Note that addEvent_D_guarded assumes that log_0I1Dr and log_0I1Dw
+ have exactly the same prototype. If you change them, you must
+ change addEvent_D_guarded too. */
VG_REGPARM(3)
static void log_0I1Dr(InstrInfo* ii, Addr data_addr, Word data_size)
{
@@ -1248,6 +1251,7 @@
}
}
+/* See comment on log_0I1Dr. */
VG_REGPARM(3)
static void log_0I1Dw(InstrInfo* ii, Addr data_addr, Word data_size)
{
Modified: trunk/helgrind/hg_main.c (+71 -14)
===================================================================
--- trunk/helgrind/hg_main.c 2013-01-16 22:07:02 +00:00 (rev 13235)
+++ trunk/helgrind/hg_main.c 2013-01-17 14:24:35 +00:00 (rev 13236)
@@ -4126,18 +4126,40 @@
/*--- Instrumentation ---*/
/*--------------------------------------------------------------*/
+#define unop(_op, _arg1) IRExpr_Unop((_op),(_arg1))
#define binop(_op, _arg1, _arg2) IRExpr_Binop((_op),(_arg1),(_arg2))
#define mkexpr(_tmp) IRExpr_RdTmp((_tmp))
#define mkU32(_n) IRExpr_Const(IRConst_U32(_n))
#define mkU64(_n) IRExpr_Const(IRConst_U64(_n))
#define assign(_t, _e) IRStmt_WrTmp((_t), (_e))
+/* This takes and returns atoms, of course. Not full IRExprs. */
+static IRExpr* mk_And1 ( IRSB* sbOut, IRExpr* arg1, IRExpr* arg2 )
+{
+ tl_assert(arg1 && arg2);
+ tl_assert(isIRAtom(arg1));
+ tl_assert(isIRAtom(arg2));
+ /* Generate 32to1(And32(1Uto32(arg1), 1Uto32(arg2))). Appalling
+ code, I know. */
+ IRTemp wide1 = newIRTemp(sbOut->tyenv, Ity_I32);
+ IRTemp wide2 = newIRTemp(sbOut->tyenv, Ity_I32);
+ IRTemp anded = newIRTemp(sbOut->tyenv, Ity_I32);
+ IRTemp res = newIRTemp(sbOut->tyenv, Ity_I1);
+ addStmtToIRSB(sbOut, assign(wide1, unop(Iop_1Uto32, arg1)));
+ addStmtToIRSB(sbOut, assign(wide2, unop(Iop_1Uto32, arg2)));
+ addStmtToIRSB(sbOut, assign(anded, binop(Iop_And32, mkexpr(wide1),
+ mkexpr(wide2))));
+ addStmtToIRSB(sbOut, assign(res, unop(Iop_32to1, mkexpr(anded))));
+ return mkexpr(res);
+}
+
static void instrument_mem_access ( IRSB* sbOut,
IRExpr* addr,
Int szB,
Bool isStore,
Int hWordTy_szB,
- Int goff_sp )
+ Int goff_sp,
+ IRExpr* guard ) /* NULL => True */
{
IRType tyAddr = Ity_INVALID;
const HChar* hName = NULL;
@@ -4273,17 +4295,25 @@
: binop(Iop_Add64, mkexpr(addr_minus_sp), mkU64(rz_szB)))
);
- IRTemp guard = newIRTemp(sbOut->tyenv, Ity_I1);
+ /* guardA == "guard on the address" */
+ IRTemp guardA = newIRTemp(sbOut->tyenv, Ity_I1);
addStmtToIRSB(
sbOut,
- assign(guard,
+ assign(guardA,
tyAddr == Ity_I32
? binop(Iop_CmpLT32U, mkU32(THRESH), mkexpr(diff))
: binop(Iop_CmpLT64U, mkU64(THRESH), mkexpr(diff)))
);
- di->guard = mkexpr(guard);
+ di->guard = mkexpr(guardA);
}
+ /* If there's a guard on the access itself (as supplied by the
+ caller of this routine), we need to AND that in to any guard we
+ might already have. */
+ if (guard) {
+ di->guard = mk_And1(sbOut, di->guard, guard);
+ }
+
/* Add the helper. */
addStmtToIRSB( sbOut, IRStmt_Dirty(di) );
}
@@ -4428,7 +4458,8 @@
(isDCAS ? 2 : 1)
* sizeofIRType(typeOfIRExpr(bbIn->tyenv, cas->dataLo)),
False/*!isStore*/,
- sizeofIRType(hWordTy), goff_sp
+ sizeofIRType(hWordTy), goff_sp,
+ NULL/*no-guard*/
);
}
break;
@@ -4448,7 +4479,8 @@
st->Ist.LLSC.addr,
sizeofIRType(dataTy),
False/*!isStore*/,
- sizeofIRType(hWordTy), goff_sp
+ sizeofIRType(hWordTy), goff_sp,
+ NULL/*no-guard*/
);
}
} else {
@@ -4459,22 +4491,46 @@
}
case Ist_Store:
- /* It seems we pretend that store-conditionals don't
- exist, viz, just ignore them ... */
if (!inLDSO) {
instrument_mem_access(
bbOut,
st->Ist.Store.addr,
sizeofIRType(typeOfIRExpr(bbIn->tyenv, st->Ist.Store.data)),
True/*isStore*/,
- sizeofIRType(hWordTy), goff_sp
+ sizeofIRType(hWordTy), goff_sp,
+ NULL/*no-guard*/
);
}
break;
+ case Ist_StoreG: {
+ IRStoreG* sg = st->Ist.StoreG.details;
+ IRExpr* data = sg->data;
+ IRExpr* addr = sg->addr;
+ IRType type = typeOfIRExpr(bbIn->tyenv, data);
+ tl_assert(type != Ity_INVALID);
+ instrument_mem_access( bbOut, addr, sizeofIRType(type),
+ True/*isStore*/,
+ sizeofIRType(hWordTy),
+ goff_sp, sg->guard );
+ break;
+ }
+
+ case Ist_LoadG: {
+ IRLoadG* lg = st->Ist.LoadG.details;
+ IRType type = Ity_INVALID; /* loaded type */
+ IRType typeWide = Ity_INVALID; /* after implicit widening */
+ IRExpr* addr = lg->addr;
+ typeOfIRLoadGOp(lg->cvt, &typeWide, &type);
+ tl_assert(type != Ity_INVALID);
+ instrument_mem_access( bbOut, addr, sizeofIRType(type),
+ False/*!isStore*/,
+ sizeofIRType(hWordTy),
+ goff_sp, lg->guard );
+ break;
+ }
+
case Ist_WrTmp: {
- /* ... whereas here we don't care whether a load is a
- vanilla one or a load-linked. */
IRExpr* data = st->Ist.WrTmp.data;
if (data->tag == Iex_Load) {
if (!inLDSO) {
@@ -4483,7 +4539,8 @@
data->Iex.Load.addr,
sizeofIRType(data->Iex.Load.ty),
False/*!isStore*/,
- sizeofIRType(hWordTy), goff_sp
+ sizeofIRType(hWordTy), goff_sp,
+ NULL/*no-guard*/
);
}
}
@@ -4503,7 +4560,7 @@
if (!inLDSO) {
instrument_mem_access(
bbOut, d->mAddr, dataSize, False/*!isStore*/,
- sizeofIRType(hWordTy), goff_sp
+ sizeofIRType(hWordTy), goff_sp, NULL/*no-guard*/
);
}
}
@@ -4511,7 +4568,7 @@
if (!inLDSO) {
instrument_mem_access(
bbOut, d->mAddr, dataSize, True/*isStore*/,
- sizeofIRType(hWordTy), goff_sp
+ sizeofIRType(hWordTy), goff_sp, NULL/*no-guard*/
);
}
}
Modified: trunk/lackey/lk_main.c (+102 -26)
===================================================================
--- trunk/lackey/lk_main.c 2013-01-16 22:07:02 +00:00 (rev 13235)
+++ trunk/lackey/lk_main.c 2013-01-17 14:24:35 +00:00 (rev 13236)
@@ -292,6 +292,10 @@
/*--- Stuff for --detailed-counts ---*/
/*------------------------------------------------------------*/
+typedef
+ IRExpr
+ IRAtom;
+
/* --- Operations --- */
typedef enum { OpLoad=0, OpStore=1, OpAlu=2 } Op;
@@ -351,8 +355,10 @@
(*detail)++;
}
-/* A helper that adds the instrumentation for a detail. */
-static void instrument_detail(IRSB* sb, Op op, IRType type)
+/* A helper that adds the instrumentation for a detail. guard ::
+ Ity_I1 is the guarding condition for the event. If NULL it is
+ assumed to mean "always True". */
+static void instrument_detail(IRSB* sb, Op op, IRType type, IRAtom* guard)
{
IRDirty* di;
IRExpr** argv;
@@ -365,6 +371,7 @@
di = unsafeIRDirty_0_N( 1, "increment_detail",
VG_(fnptr_to_fnentry)( &increment_detail ),
argv);
+ if (guard) di->guard = guard;
addStmtToIRSB( sb, IRStmt_Dirty(di) );
}
@@ -391,10 +398,6 @@
#define MAX_DSIZE 512
-typedef
- IRExpr
- IRAtom;
-
typedef
enum { Event_Ir, Event_Dr, Event_Dw, Event_Dm }
EventKind;
@@ -404,6 +407,7 @@
EventKind ekind;
IRAtom* addr;
Int size;
+ IRAtom* guard; /* :: Ity_I1, or NULL=="always True" */
}
Event;
@@ -500,6 +504,9 @@
di = unsafeIRDirty_0_N( /*regparms*/2,
helperName, VG_(fnptr_to_fnentry)( helperAddr ),
argv );
+ if (ev->guard) {
+ di->guard = ev->guard;
+ }
addStmtToIRSB( sb, IRStmt_Dirty(di) );
}
@@ -524,11 +531,13 @@
evt->ekind = Event_Ir;
evt->addr = iaddr;
evt->size = isize;
+ evt->guard = NULL;
events_used++;
}
+/* Add a guarded read event. */
static
-void addEvent_Dr ( IRSB* sb, IRAtom* daddr, Int dsize )
+void addEvent_Dr_guarded ( IRSB* sb, IRAtom* daddr, Int dsize, IRAtom* guard )
{
Event* evt;
tl_assert(clo_trace_mem);
@@ -541,10 +550,41 @@
evt->ekind = Event_Dr;
evt->addr = daddr;
evt->size = dsize;
+ evt->guard = guard;
events_used++;
}
+/* Add an ordinary read event, by adding a guarded read event with an
+ always-true guard. */
static
+void addEvent_Dr ( IRSB* sb, IRAtom* daddr, Int dsize )
+{
+ addEvent_Dr_guarded(sb, daddr, dsize, NULL);
+}
+
+/* Add a guarded write event. */
+static
+void addEvent_Dw_guarded ( IRSB* sb, IRAtom* daddr, Int dsize, IRAtom* guard )
+{
+ Event* evt;
+ tl_assert(clo_trace_mem);
+ tl_assert(isIRAtom(daddr));
+ tl_assert(dsize >= 1 && dsize <= MAX_DSIZE);
+ if (events_used == N_EVENTS)
+ flushEvents(sb);
+ tl_assert(events_used >= 0 && events_used < N_EVENTS);
+ evt = &events[events_used];
+ evt->ekind = Event_Dw;
+ evt->addr = daddr;
+ evt->size = dsize;
+ evt->guard = guard;
+ events_used++;
+}
+
+/* Add an ordinary write event. Try to merge it with an immediately
+ preceding ordinary read event of the same size to the same
+ address. */
+static
void addEvent_Dw ( IRSB* sb, IRAtom* daddr, Int dsize )
{
Event* lastEvt;
@@ -556,9 +596,10 @@
// Is it possible to merge this write with the preceding read?
lastEvt = &events[events_used-1];
if (events_used > 0
- && lastEvt->ekind == Event_Dr
- && lastEvt->size == dsize
- && eqIRAtom(lastEvt->addr, daddr))
+ && lastEvt->ekind == Event_Dr
+ && lastEvt->size == dsize
+ && lastEvt->guard == NULL
+ && eqIRAtom(lastEvt->addr, daddr))
{
lastEvt->ekind = Event_Dm;
return;
@@ -572,6 +613,7 @@
evt->ekind = Event_Dw;
evt->size = dsize;
evt->addr = daddr;
+ evt->guard = NULL;
events_used++;
}
@@ -613,7 +655,6 @@
Int i;
IRSB* sbOut;
HChar fnname[100];
- IRType type;
IRTypeEnv* tyenv = sbIn->tyenv;
Addr iaddr = 0, dst;
UInt ilen = 0;
@@ -734,18 +775,18 @@
}
if (clo_detailed_counts) {
IRExpr* expr = st->Ist.WrTmp.data;
- type = typeOfIRExpr(sbOut->tyenv, expr);
+ IRType type = typeOfIRExpr(sbOut->tyenv, expr);
tl_assert(type != Ity_INVALID);
switch (expr->tag) {
case Iex_Load:
- instrument_detail( sbOut, OpLoad, type );
+ instrument_detail( sbOut, OpLoad, type, NULL/*guard*/ );
break;
case Iex_Unop:
case Iex_Binop:
case Iex_Triop:
case Iex_Qop:
case Iex_Mux0X:
- instrument_detail( sbOut, OpAlu, type );
+ instrument_detail( sbOut, OpAlu, type, NULL/*guard*/ );
break;
default:
break;
@@ -754,20 +795,54 @@
addStmtToIRSB( sbOut, st );
break;
- case Ist_Store:
+ case Ist_Store: {
+ IRExpr* data = st->Ist.Store.data;
+ IRType type = typeOfIRExpr(tyenv, data);
+ tl_assert(type != Ity_INVALID);
if (clo_trace_mem) {
- IRExpr* data = st->Ist.Store.data;
addEvent_Dw( sbOut, st->Ist.Store.addr,
- sizeofIRType(typeOfIRExpr(tyenv, data)) );
+ sizeofIRType(type) );
}
if (clo_detailed_counts) {
- type = typeOfIRExpr(sbOut->tyenv, st->Ist.Store.data);
- tl_assert(type != Ity_INVALID);
- instrument_detail( sbOut, OpStore, type );
+ instrument_detail( sbOut, OpStore, type, NULL/*guard*/ );
}
addStmtToIRSB( sbOut, st );
break;
+ }
+ case Ist_StoreG: {
+ IRStoreG* sg = st->Ist.StoreG.details;
+ IRExpr* data = sg->data;
+ IRType type = typeOfIRExpr(tyenv, data);
+ tl_assert(type != Ity_INVALID);
+ if (clo_trace_mem) {
+ addEvent_Dw_guarded( sbOut, sg->addr,
+ sizeofIRType(type), sg->guard );
+ }
+ if (clo_detailed_counts) {
+ instrument_detail( sbOut, OpStore, type, sg->guard );
+ }
+ addStmtToIRSB( sbOut, st );
+ break;
+ }
+
+ case Ist_LoadG: {
+ IRLoadG* lg = st->Ist.LoadG.details;
+ IRType type = Ity_INVALID; /* loaded type */
+ IRType typeWide = Ity_INVALID; /* after implicit widening */
+ typeOfIRLoadGOp(lg->cvt, &typeWide, &type);
+ tl_assert(type != Ity_INVALID);
+ if (clo_trace_mem) {
+ addEvent_Dr_guarded( sbOut, lg->addr,
+ sizeofIRType(type), lg->guard );
+ }
+ if (clo_detailed_counts) {
+ instrument_detail( sbOut, OpLoad, type, lg->guard );
+ }
+ addStmtToIRSB( sbOut, st );
+ break;
+ }
+
case Ist_Dirty: {
if (clo_trace_mem) {
Int dsize;
@@ -810,12 +885,12 @@
addEvent_Dw( sbOut, cas->addr, dataSize );
}
if (clo_detailed_counts) {
- instrument_detail( sbOut, OpLoad, dataTy );
+ instrument_detail( sbOut, OpLoad, dataTy, NULL/*guard*/ );
if (cas->dataHi != NULL) /* dcas */
- instrument_detail( sbOut, OpLoad, dataTy );
- instrument_detail( sbOut, OpStore, dataTy );
+ instrument_detail( sbOut, OpLoad, dataTy, NULL/*guard*/ );
+ instrument_detail( sbOut, OpStore, dataTy, NULL/*guard*/ );
if (cas->dataHi != NULL) /* dcas */
- instrument_detail( sbOut, OpStore, dataTy );
+ instrument_detail( sbOut, OpStore, dataTy, NULL/*guard*/ );
}
addStmtToIRSB( sbOut, st );
break;
@@ -833,7 +908,7 @@
flushEvents(sbOut);
}
if (clo_detailed_counts)
- instrument_detail( sbOut, OpLoad, dataTy );
+ instrument_detail( sbOut, OpLoad, dataTy, NULL/*guard*/ );
} else {
/* SC */
dataTy = typeOfIRExpr(tyenv, st->Ist.LLSC.storedata);
@@ -841,7 +916,7 @@
addEvent_Dw( sbOut, st->Ist.LLSC.addr,
sizeofIRType(dataTy) );
if (clo_detailed_counts)
- instrument_detail( sbOut, OpStore, dataTy );
+ instrument_detail( sbOut, OpStore, dataTy, NULL/*guard*/ );
}
addStmtToIRSB( sbOut, st );
break;
@@ -893,6 +968,7 @@
break;
default:
+ ppIRStmt(st);
tl_assert(0);
}
}
Modified: trunk/drd/drd_load_store.c (+70 -29)
===================================================================
--- trunk/drd/drd_load_store.c 2013-01-16 22:07:02 +00:00 (rev 13235)
+++ trunk/drd/drd_load_store.c 2013-01-17 14:24:35 +00:00 (rev 13236)
@@ -344,22 +344,23 @@
* Instrument the client code to trace a memory load (--trace-addr).
*/
static IRExpr* instr_trace_mem_load(IRSB* const bb, IRExpr* addr_expr,
- ...
[truncated message content] |