From: Julian Seward Date: Mon, 22 Nov 2004 20:19:51 +0000 (+0000) Subject: Make Memcheck compilable. X-Git-Tag: svn/VALGRIND_3_0_0~1253 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d6a28c4ff3f3070d1a5a43e32aed6760236e1118;p=thirdparty%2Fvalgrind.git Make Memcheck compilable. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3065 --- diff --git a/memcheck/mac_shared.h b/memcheck/mac_shared.h index f9a6e30910..2b57a5e742 100644 --- a/memcheck/mac_shared.h +++ b/memcheck/mac_shared.h @@ -234,8 +234,8 @@ extern UInt MAC_(event_ctr)[N_PROF_EVENTS]; #define VGM_WORD_VALID 0 #define VGM_WORD_INVALID 0xFFFFFFFF -#define VGM_EFLAGS_VALID 0xFFFFFFFE -#define VGM_EFLAGS_INVALID 0xFFFFFFFF /* not used */ +#define VGM_WORD64_VALID 0x0ULL +#define VGM_WORD64_INVALID 0xFFFFFFFFFFFFFFFFULL /*------------------------------------------------------------*/ /*--- Command line options + defaults ---*/ diff --git a/memcheck/mc_from_ucode.c b/memcheck/mc_from_ucode.c index 0680effc88..705849db63 100644 --- a/memcheck/mc_from_ucode.c +++ b/memcheck/mc_from_ucode.c @@ -29,633 +29,6 @@ The GNU General Public License is contained in the file COPYING. */ -#include "mc_include.h" - -/*------------------------------------------------------------*/ -/*--- Renamings of frequently-used global functions. ---*/ -/*------------------------------------------------------------*/ - -#define dis VG_(print_codegen) - -/*------------------------------------------------------------*/ -/*--- Instruction emission -- turning final uinstrs back ---*/ -/*--- into x86 code. ---*/ -/*------------------------------------------------------------*/ - -/* See the corresponding comment at the top of vg_from_ucode.c to find out - * how all this works */ - -/*----------------------------------------------------*/ -/*--- v-size (4, or 2 with OSO) insn emitters ---*/ -/*----------------------------------------------------*/ - -static void emit_testv_lit_reg ( Int sz, UInt lit, Int reg ) -{ - VG_(new_emit)(False, FlagsEmpty, FlagsOSZACP); - if (sz == 2) { - VG_(emitB) ( 0x66 ); - } else { - tl_assert(sz == 4); - } - VG_(emitB) ( 0xF7 ); /* Grp3 Ev */ - VG_(emit_amode_ereg_greg) ( reg, 0 /* Grp3 subopcode for TEST */ ); - if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit ); - if (dis) - VG_(printf)("\n\t\ttest%c $0x%x, %s\n", nameISize(sz), - lit, nameIReg(sz,reg)); -} - -static void emit_testv_lit_offregmem ( Int sz, UInt lit, Int off, Int reg ) -{ - VG_(new_emit)(False, FlagsEmpty, FlagsOSZACP); - if (sz == 2) { - VG_(emitB) ( 0x66 ); - } else { - tl_assert(sz == 4); - } - VG_(emitB) ( 0xF7 ); /* Grp3 Ev */ - VG_(emit_amode_offregmem_reg) ( off, reg, 0 /* Grp3 subopcode for TEST */ ); - if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit ); - if (dis) - VG_(printf)("\n\t\ttest%c $%d, 0x%x(%s)\n", - nameISize(sz), lit, off, nameIReg(4,reg) ); -} - -/*----------------------------------------------------*/ -/*--- Instruction synthesisers ---*/ -/*----------------------------------------------------*/ - -/* Synthesise a minimal test (and which discards result) of reg32 - against lit. It's always safe do simply - emit_testv_lit_reg ( 4, lit, reg32 ) - but we try to do better when possible. -*/ -static void synth_minimal_test_lit_reg ( UInt lit, Int reg32 ) -{ - if ((lit & 0xFFFFFF00) == 0 && reg32 < 4) { - /* We can get away with a byte insn. */ - VG_(emit_testb_lit_reg) ( False, lit, reg32 ); - } - else - if ((lit & 0xFFFF0000) == 0) { - /* Literal fits in 16 bits; do a word insn. */ - emit_testv_lit_reg ( 2, lit, reg32 ); - } - else { - /* Totally general ... */ - emit_testv_lit_reg ( 4, lit, reg32 ); - } -} - -/*----------------------------------------------------*/ -/*--- Top level of the uinstr -> x86 translation. ---*/ -/*----------------------------------------------------*/ - -static void synth_LOADV ( Int sz, Int a_reg, Int tv_reg, - RRegSet regs_live_before, - RRegSet regs_live_after ) -{ - Addr helper; - UInt argv[] = { a_reg }; - UInt tagv[] = { RealReg }; - - switch (sz) { - case 4: helper = (Addr) & MC_(helperc_LOADV4); break; - case 2: helper = (Addr) & MC_(helperc_LOADV2); break; - case 1: helper = (Addr) & MC_(helperc_LOADV1); break; - default: VG_(tool_panic)("synth_LOADV"); - } - VG_(synth_ccall) ( helper, 1, 1, argv, tagv, tv_reg, - regs_live_before, regs_live_after ); -} - - -static void synth_STOREV ( Int sz, Int tv_tag, Int tv_val, Int a_reg, - RRegSet regs_live_before, - RRegSet regs_live_after ) -{ - Addr helper; - UInt argv[] = { a_reg, tv_val }; - Tag tagv[] = { RealReg, tv_tag }; - - tl_assert(tv_tag == RealReg || tv_tag == Literal); - switch (sz) { - case 4: helper = (Addr) MC_(helperc_STOREV4); break; - case 2: helper = (Addr) MC_(helperc_STOREV2); break; - case 1: helper = (Addr) MC_(helperc_STOREV1); break; - default: VG_(tool_panic)("synth_STOREV"); - } - VG_(synth_ccall) ( helper, 2, 2, argv, tagv, INVALID_REALREG, - regs_live_before, regs_live_after ); -} - - -static void synth_SETV ( Int sz, Int reg ) -{ - UInt val; - switch (sz) { - case 4: val = 0x00000000; break; - case 2: val = 0xFFFF0000; break; - case 1: val = 0xFFFFFF00; break; - case 0: val = 0xFFFFFFFE; break; - default: VG_(tool_panic)("synth_SETV"); - } - VG_(emit_movv_lit_reg) ( 4, val, reg ); -} - - -static void synth_TESTV ( Int sz, Int tag, Int val ) -{ - Int tgt; /* jump target */ - - /* Important note. Note that that the calls to - MC_(helper_value_check[0124]_fail) must be compact helpers due to - the codegen scheme used below. Since there are a shortage of - compact helper slots, and since the size==1 case is never - actually used, we assert against it. */ - tl_assert(sz == 0 || sz == 2 || sz == 4); - - VG_(init_target)(&tgt); - - tl_assert(tag == ArchReg || tag == RealReg); - if (tag == ArchReg) { - switch (sz) { - case 4: - emit_testv_lit_offregmem ( - 4, 0xFFFFFFFF, VG_(shadow_reg_offset)(val), R_EBP ); - break; - case 2: - emit_testv_lit_offregmem ( - 4, 0x0000FFFF, VG_(shadow_reg_offset)(val), R_EBP ); - break; - case 1: - if (val < 4) { - emit_testv_lit_offregmem ( - 4, 0x000000FF, VG_(shadow_reg_offset)(val), R_EBP ); - } else { - emit_testv_lit_offregmem ( - 4, 0x0000FF00, VG_(shadow_reg_offset)(val-4), R_EBP ); - } - break; - case 0: - /* should never happen */ - default: - VG_(tool_panic)("synth_TESTV(ArchReg)"); - } - } else { - switch (sz) { - case 4: - /* Works, but holds the entire 32-bit literal, hence - generating a 6-byte insn. We want to know if any bits - in the reg are set, but since this is for the full reg, - we might as well compare it against zero, which can be - done with a shorter insn. */ - /* synth_minimal_test_lit_reg ( 0xFFFFFFFF, val ); */ - VG_(emit_cmpl_zero_reg) ( False, val ); - break; - case 2: - synth_minimal_test_lit_reg ( 0x0000FFFF, val ); - break; - case 1: - synth_minimal_test_lit_reg ( 0x000000FF, val ); - break; - case 0: - synth_minimal_test_lit_reg ( 0x00000001, val ); - break; - default: - VG_(tool_panic)("synth_TESTV(RealReg)"); - } - } - - /* predict taken because we assume failures are rare */ - VG_(emit_jcondshort_target) ( False, CondZ, &tgt, JP_TAKEN ); - - VG_(synth_call) ( - False, - ( sz==4 - ? VG_(helper_offset)((Addr) & MC_(helper_value_check4_fail)) - : ( sz==2 - ? VG_(helper_offset)((Addr) & MC_(helper_value_check2_fail)) - : ( sz==1 - ? VG_(helper_offset)((Addr) & MC_(helper_value_check1_fail)) - : VG_(helper_offset)((Addr) & MC_(helper_value_check0_fail))))), - False, FlagsEmpty, FlagsOSZACP /* helpers don't preserve flags */ - ); - VG_(target_forward)(&tgt); -} - - -static void synth_GETV ( Int sz, Int arch, Int reg ) -{ - /* VG_(printf)("synth_GETV %d of Arch %s\n", sz, nameIReg(sz, arch)); */ - switch (sz) { - case 4: - VG_(emit_movv_offregmem_reg) ( 4, VG_(shadow_reg_offset)(arch), - R_EBP, reg ); - break; - case 2: - VG_(emit_movzwl_offregmem_reg) ( False, VG_(shadow_reg_offset)(arch), - R_EBP, reg ); - VG_(emit_nonshiftopv_lit_reg) ( False, 4, OR, 0xFFFF0000, reg ); - break; - case 1: - if (arch < 4) { - VG_(emit_movzbl_offregmem_reg) ( False, VG_(shadow_reg_offset)(arch), - R_EBP, reg ); - } else { - VG_(emit_movzbl_offregmem_reg) ( False, VG_(shadow_reg_offset)(arch-4)+1, - R_EBP, reg ); - } - VG_(emit_nonshiftopv_lit_reg) ( False, 4, OR, 0xFFFFFF00, reg ); - break; - default: - VG_(tool_panic)("synth_GETV"); - } -} - - -static void synth_PUTV ( Int sz, Int srcTag, UInt lit_or_reg, Int arch ) -{ - if (srcTag == Literal) { - /* PUTV with a Literal is only ever used to set the corresponding - ArchReg to `all valid'. Should really be a kind of SETV. */ - UInt lit = lit_or_reg; - switch (sz) { - case 4: - tl_assert(lit == 0x00000000); - VG_(emit_movv_lit_offregmem) ( 4, 0x00000000, - VG_(shadow_reg_offset)(arch), R_EBP ); - break; - case 2: - tl_assert(lit == 0xFFFF0000); - VG_(emit_movv_lit_offregmem) ( 2, 0x0000, - VG_(shadow_reg_offset)(arch), R_EBP ); - break; - case 1: - tl_assert(lit == 0xFFFFFF00); - if (arch < 4) { - VG_(emit_movb_lit_offregmem) ( 0x00, - VG_(shadow_reg_offset)(arch), R_EBP ); - } else { - VG_(emit_movb_lit_offregmem) ( 0x00, - VG_(shadow_reg_offset)(arch-4)+1, - R_EBP ); - } - break; - default: - VG_(tool_panic)("synth_PUTV(lit)"); - } - - } else { - - UInt reg; - tl_assert(srcTag == RealReg); - - if (sz == 1 && lit_or_reg >= 4) { - VG_(emit_swapl_reg_EAX) ( lit_or_reg ); - reg = R_EAX; - } else { - reg = lit_or_reg; - } - - if (sz == 1) tl_assert(reg < 4); - - switch (sz) { - case 4: - VG_(emit_movv_reg_offregmem) ( 4, reg, - VG_(shadow_reg_offset)(arch), R_EBP ); - break; - case 2: - VG_(emit_movv_reg_offregmem) ( 2, reg, - VG_(shadow_reg_offset)(arch), R_EBP ); - break; - case 1: - if (arch < 4) { - VG_(emit_movb_reg_offregmem) ( reg, - VG_(shadow_reg_offset)(arch), R_EBP ); - } else { - VG_(emit_movb_reg_offregmem) ( reg, - VG_(shadow_reg_offset)(arch-4)+1, R_EBP ); - } - break; - default: - VG_(tool_panic)("synth_PUTV(reg)"); - } - - if (sz == 1 && lit_or_reg >= 4) { - VG_(emit_swapl_reg_EAX) ( lit_or_reg ); - } - } -} - - -static void synth_GETVF ( Int reg ) -{ - VG_(emit_movv_offregmem_reg) ( 4, VG_(shadow_flags_offset)(), R_EBP, reg ); - /* paranoia only; should be unnecessary ... */ - /* VG_(emit_nonshiftopv_lit_reg) ( 4, OR, 0xFFFFFFFE, reg ); */ -} - - -static void synth_PUTVF ( UInt reg ) -{ - VG_(emit_movv_reg_offregmem) ( 4, reg, VG_(shadow_flags_offset)(), R_EBP ); -} - - -static void synth_TAG1_op ( TagOp op, Int reg, RRegSet regs_live_after ) -{ - switch (op) { - - /* Scheme is - neg %reg -- CF = %reg==0 ? 0 : 1 - sbbl %reg, %reg -- %reg = -CF - or 0xFFFFFFFE, %reg -- invalidate all bits except lowest - */ - case Tag_PCast40: - VG_(emit_unaryopv_reg)(False, 4, NEG, reg); - VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFFFE, reg); - break; - case Tag_PCast20: - VG_(emit_unaryopv_reg)(False, 2, NEG, reg); - VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFFFE, reg); - break; - case Tag_PCast10: - if (reg >= 4) { - VG_(emit_swapl_reg_EAX)(reg); - VG_(emit_unaryopb_reg)(False, NEG, R_EAX); - VG_(emit_swapl_reg_EAX)(reg); - } else { - VG_(emit_unaryopb_reg)(False, NEG, reg); - } - VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFFFE, reg); - break; - - /* Scheme is - andl $1, %reg -- %reg is 0 or 1 - negl %reg -- %reg is 0 or 0xFFFFFFFF - and possibly an OR to invalidate unused bits. - */ - case Tag_PCast04: - VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x00000001, reg); - VG_(emit_unaryopv_reg)(False, 4, NEG, reg); - break; - case Tag_PCast02: - VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x00000001, reg); - VG_(emit_unaryopv_reg)(False, 4, NEG, reg); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, reg); - break; - case Tag_PCast01: - VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x00000001, reg); - VG_(emit_unaryopv_reg)(False, 4, NEG, reg); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFF00, reg); - break; - - /* Scheme is - shl $24, %reg -- make irrelevant bits disappear - negl %reg -- CF = %reg==0 ? 0 : 1 - sbbl %reg, %reg -- %reg = -CF - and possibly an OR to invalidate unused bits. - */ - case Tag_PCast14: - VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); - VG_(emit_unaryopv_reg)(False, 4, NEG, reg); - VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); - break; - case Tag_PCast12: - VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); - VG_(emit_unaryopv_reg)(False, 4, NEG, reg); - VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, reg); - break; - case Tag_PCast11: - VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); - VG_(emit_unaryopv_reg)(False, 4, NEG, reg); - VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFF00, reg); - break; - - /* We use any non-live reg (except %reg) as a temporary, - or push/pop %ebp if none available: - (%dead_reg = any dead reg, else choose anything other than %reg) - (pushl %dead_reg if live) - movl %reg, %dead_reg - negl %dead_reg - orl %dead_reg, %reg - (popl %dead_reg if live) - This sequence turns out to be correct regardless of the - operation width. - */ - case Tag_Left4: - case Tag_Left2: - case Tag_Left1: { - Bool push = True; - UInt dead_reg = R_ESP; - Int i, reg_of_i; - - for (i = 0; i < VG_MAX_REALREGS; i++) { - if (! IS_RREG_LIVE(i, regs_live_after)) { - reg_of_i = VG_(rank_to_realreg)(i); - if (reg != reg_of_i) { - dead_reg = reg_of_i; - push = False; - break; - } - } - } - - if (push) { - dead_reg = (reg != R_EAX) ? R_EAX : R_EBX; - VG_(emit_pushv_reg)(4, dead_reg); - } - - VG_(emit_movv_reg_reg)(4, reg, dead_reg); - VG_(emit_unaryopv_reg)(False, 4, NEG, dead_reg); - VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, dead_reg, reg); - - if (push) - VG_(emit_popv_reg)(4, dead_reg); - break; - } - - /* These are all fairly obvious; do the op and then, if - necessary, invalidate unused bits. */ - case Tag_SWiden14: - VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); - VG_(emit_shiftopv_lit_reg)(False, 4, SAR, 24, reg); - break; - case Tag_SWiden24: - VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 16, reg); - VG_(emit_shiftopv_lit_reg)(False, 4, SAR, 16, reg); - break; - case Tag_SWiden12: - VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); - VG_(emit_shiftopv_lit_reg)(False, 4, SAR, 24, reg); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, reg); - break; - case Tag_ZWiden14: - VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x000000FF, reg); - break; - case Tag_ZWiden24: - VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x0000FFFF, reg); - break; - case Tag_ZWiden12: - VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x000000FF, reg); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, reg); - break; - - default: - VG_(tool_panic)("synth_TAG1_op"); - } -} - - -static void synth_TAG2_op ( TagOp op, Int regs, Int regd ) -{ - switch (op) { - - /* UifU is implemented by OR, since 1 means Undefined. */ - case Tag_UifU4: - case Tag_UifU2: - case Tag_UifU1: - case Tag_UifU0: - VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, regs, regd); - break; - - /* DifD is implemented by AND, since 0 means Defined. */ - case Tag_DifD4: - case Tag_DifD2: - case Tag_DifD1: - VG_(emit_nonshiftopv_reg_reg)(False, 4, AND, regs, regd); - break; - - /* ImproveAND(value, tags) = value OR tags. - Defined (0) value 0s give defined (0); all other -> undefined (1). - value is in regs; tags is in regd. - Be paranoid and invalidate unused bits; I don't know whether - or not this is actually necessary. */ - case Tag_ImproveAND4_TQ: - VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, regs, regd); - break; - case Tag_ImproveAND2_TQ: - VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, regs, regd); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, regd); - break; - case Tag_ImproveAND1_TQ: - VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, regs, regd); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFF00, regd); - break; - - /* ImproveOR(value, tags) = (not value) OR tags. - Defined (0) value 1s give defined (0); all other -> undefined (1). - value is in regs; tags is in regd. - To avoid trashing value, this is implemented (re de Morgan) as - not (value AND (not tags)) - Be paranoid and invalidate unused bits; I don't know whether - or not this is actually necessary. */ - case Tag_ImproveOR4_TQ: - VG_(emit_unaryopv_reg)(False, 4, NOT, regd); - VG_(emit_nonshiftopv_reg_reg)(False, 4, AND, regs, regd); - VG_(emit_unaryopv_reg)(False, 4, NOT, regd); - break; - case Tag_ImproveOR2_TQ: - VG_(emit_unaryopv_reg)(False, 4, NOT, regd); - VG_(emit_nonshiftopv_reg_reg)(False, 4, AND, regs, regd); - VG_(emit_unaryopv_reg)(False, 4, NOT, regd); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, regd); - break; - case Tag_ImproveOR1_TQ: - VG_(emit_unaryopv_reg)(False, 4, NOT, regd); - VG_(emit_nonshiftopv_reg_reg)(False, 4, AND, regs, regd); - VG_(emit_unaryopv_reg)(False, 4, NOT, regd); - VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFF00, regd); - break; - - default: - VG_(tool_panic)("synth_TAG2_op"); - } -} - -/*----------------------------------------------------*/ -/*--- Generate code for a single UInstr. ---*/ -/*----------------------------------------------------*/ - -void TL_(emit_XUInstr) ( UInstr* u, RRegSet regs_live_before ) -{ - switch (u->opcode) { - - case SETV: - tl_assert(u->tag1 == RealReg); - synth_SETV ( u->size, u->val1 ); - break; - - case STOREV: - tl_assert(u->tag1 == RealReg || u->tag1 == Literal); - tl_assert(u->tag2 == RealReg); - synth_STOREV ( u->size, u->tag1, - u->tag1==Literal ? u->lit32 : u->val1, - u->val2, - regs_live_before, u->regs_live_after ); - break; - - case LOADV: - tl_assert(u->tag1 == RealReg); - tl_assert(u->tag2 == RealReg); - if (0) - VG_(emit_AMD_prefetch_reg) ( u->val1 ); - synth_LOADV ( u->size, u->val1, u->val2, - regs_live_before, u->regs_live_after ); - break; - - case TESTV: - tl_assert(u->tag1 == RealReg || u->tag1 == ArchReg); - synth_TESTV(u->size, u->tag1, u->val1); - break; - - case GETV: - tl_assert(u->tag1 == ArchReg); - tl_assert(u->tag2 == RealReg); - synth_GETV(u->size, u->val1, u->val2); - break; - - case GETVF: - tl_assert(u->tag1 == RealReg); - tl_assert(u->size == 0); - synth_GETVF(u->val1); - break; - - case PUTV: - tl_assert(u->tag1 == RealReg || u->tag1 == Literal); - tl_assert(u->tag2 == ArchReg); - synth_PUTV(u->size, u->tag1, - u->tag1==Literal ? u->lit32 : u->val1, - u->val2 ); - break; - - case PUTVF: - tl_assert(u->tag1 == RealReg); - tl_assert(u->size == 0); - synth_PUTVF(u->val1); - break; - - case TAG1: - synth_TAG1_op ( u->val3, u->val1, u->regs_live_after ); - break; - - case TAG2: - synth_TAG2_op ( u->val3, u->val1, u->val2 ); - break; - - default: - VG_(printf)("emit_XUInstr: unhandled extension insn:\n"); - VG_(pp_UInstr)(0,u); - VG_(tool_panic)("emit_XUInstr: unhandled extension opcode"); - } -} - /*--------------------------------------------------------------------*/ /*--- end mc_from_ucode.c ---*/ /*--------------------------------------------------------------------*/ diff --git a/memcheck/mc_include.h b/memcheck/mc_include.h index 01fbbfea91..f19b0244b8 100644 --- a/memcheck/mc_include.h +++ b/memcheck/mc_include.h @@ -39,63 +39,6 @@ #include "mac_shared.h" #include "mc_asm.h" -/*------------------------------------------------------------*/ -/*--- Types ---*/ -/*------------------------------------------------------------*/ - -/* UCode extension for efficient memory checking operations */ -typedef - enum { - /* uinstrs which are not needed for mere translation of x86 code, - only for instrumentation of it. */ - LOADV = DUMMY_FINAL_UOPCODE + 1, - STOREV, - GETV, - PUTV, - TESTV, - SETV, - /* Get/set the v-bit (and it is only one bit) for the simulated - %eflags register. */ - GETVF, - PUTVF, - - /* Do a unary or binary tag op. Only for post-instrumented - code. For TAG1, first and only arg is a TempReg, and is both - arg and result reg. For TAG2, first arg is src, second is - dst, in the normal way; both are TempRegs. In both cases, - 3rd arg is a RiCHelper with a Lit16 tag. This indicates - which tag op to do. */ - TAG1, - TAG2 - } - MemCheckOpcode; - - -/* Lists the names of value-tag operations used in instrumented - code. These are the third argument to TAG1 and TAG2 uinsns. */ -typedef - enum { - /* Unary. */ - Tag_PCast40, Tag_PCast20, Tag_PCast10, - Tag_PCast01, Tag_PCast02, Tag_PCast04, - - Tag_PCast14, Tag_PCast12, Tag_PCast11, - - Tag_Left4, Tag_Left2, Tag_Left1, - - Tag_SWiden14, Tag_SWiden24, Tag_SWiden12, - Tag_ZWiden14, Tag_ZWiden24, Tag_ZWiden12, - - /* Binary; 1st is rd; 2nd is rd+wr */ - Tag_UifU4, Tag_UifU2, Tag_UifU1, Tag_UifU0, - Tag_DifD4, Tag_DifD2, Tag_DifD1, - - Tag_ImproveAND4_TQ, Tag_ImproveAND2_TQ, Tag_ImproveAND1_TQ, - Tag_ImproveOR4_TQ, Tag_ImproveOR2_TQ, Tag_ImproveOR1_TQ, - Tag_DebugFn - } - TagOp; - /*------------------------------------------------------------*/ /*--- Command line options ---*/ @@ -113,20 +56,21 @@ extern Bool MC_(clo_avoid_strlen_errors); /*--- Functions ---*/ /*------------------------------------------------------------*/ -/* Functions defined in mc_helpers.S */ -extern void MC_(helper_value_check4_fail) ( void ); -extern void MC_(helper_value_check2_fail) ( void ); -extern void MC_(helper_value_check1_fail) ( void ); -extern void MC_(helper_value_check0_fail) ( void ); - /* Functions defined in mc_main.c */ +extern REGPARM(1) void MC_(helperc_complain_undef) ( HWord ); +extern void MC_(helperc_value_check4_fail) ( void ); +extern void MC_(helperc_value_check1_fail) ( void ); +extern void MC_(helperc_value_check0_fail) ( void ); + +extern REGPARM(1) void MC_(helperc_STOREV8) ( Addr, ULong ); extern REGPARM(2) void MC_(helperc_STOREV4) ( Addr, UInt ); extern REGPARM(2) void MC_(helperc_STOREV2) ( Addr, UInt ); extern REGPARM(2) void MC_(helperc_STOREV1) ( Addr, UInt ); - + extern REGPARM(1) UInt MC_(helperc_LOADV1) ( Addr ); extern REGPARM(1) UInt MC_(helperc_LOADV2) ( Addr ); extern REGPARM(1) UInt MC_(helperc_LOADV4) ( Addr ); +extern REGPARM(1) ULong MC_(helperc_LOADV8) ( Addr ); extern REGPARM(2) void MC_(fpu_write_check) ( Addr addr, SizeT size ); extern REGPARM(2) void MC_(fpu_read_check) ( Addr addr, SizeT size ); diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c index 7a3007536e..9bca42d457 100644 --- a/memcheck/mc_main.c +++ b/memcheck/mc_main.c @@ -93,12 +93,16 @@ /*--- Function declarations. ---*/ /*------------------------------------------------------------*/ -static UInt mc_rd_V4_SLOWLY ( Addr a ); -static UInt mc_rd_V2_SLOWLY ( Addr a ); -static UInt mc_rd_V1_SLOWLY ( Addr a ); +static ULong mc_rd_V8_SLOWLY ( Addr a ); +static UInt mc_rd_V4_SLOWLY ( Addr a ); +static UInt mc_rd_V2_SLOWLY ( Addr a ); +static UInt mc_rd_V1_SLOWLY ( Addr a ); + +static void mc_wr_V8_SLOWLY ( Addr a, ULong vbytes ); static void mc_wr_V4_SLOWLY ( Addr a, UInt vbytes ); static void mc_wr_V2_SLOWLY ( Addr a, UInt vbytes ); static void mc_wr_V1_SLOWLY ( Addr a, UInt vbytes ); + static void mc_fpu_read_check_SLOWLY ( Addr addr, SizeT size ); static void mc_fpu_write_check_SLOWLY ( Addr addr, SizeT size ); @@ -746,10 +750,12 @@ void mc_set_perms (Addr a, SizeT len, Bool rr, Bool ww, Bool xx) static void mc_post_regs_write_init ( void ) { +#if 0 UInt i; for (i = FIRST_ARCH_REG; i <= LAST_ARCH_REG; i++) VG_(set_shadow_archreg)( i, VGM_WORD_VALID ); VG_(set_shadow_eflags)( VGM_EFLAGS_VALID ); +#endif } static void mc_post_reg_write(ThreadId tid, UInt reg) @@ -798,13 +804,90 @@ static __inline__ UInt shiftRight16 ( UInt x ) } -/* Read/write 1/2/4 sized V bytes, and emit an address error if +/* Read/write 1/2/4/8 sized V bytes, and emit an address error if needed. */ -/* VG_(helperc_{LD,ST}V{1,2,4}) handle the common case fast. +/* MC_(helperc_{LD,ST}V{1,2,4,8}) handle the common case fast. Under all other circumstances, it defers to the relevant _SLOWLY function, which can handle all situations. */ + +/* ------------------------ Size = 8 ------------------------ */ + +REGPARM(1) +ULong MC_(helperc_LOADV8) ( Addr a ) +{ +# ifdef VG_DEBUG_MEMORY + return mc_rd_V8_SLOWLY(a); +# else + if (IS_ALIGNED8_ADDR(a)) { + UInt sec_no = shiftRight16(a) & 0xFFFF; + SecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + UChar abits = sm->abits[a_off]; + if (abits == VGM_BYTE_VALID) { + /* a is 8-aligned, mapped, and addressible. */ + UInt v_off = a & 0xFFFF; + /* LITTLE-ENDIAN */ + UInt vLo = ((UInt*)(sm->vbyte))[ (v_off >> 2) ]; + UInt vHi = ((UInt*)(sm->vbyte))[ (v_off >> 2) + 1 ]; + return ( ((ULong)vHi) << 32 ) | ((ULong)vLo); + } else { + return mc_rd_V8_SLOWLY(a); + } + } + else + if (IS_ALIGNED4_ADDR(a)) { + /* LITTLE-ENDIAN */ + UInt vLo = MC_(helperc_LOADV4)(a+0); + UInt vHi = MC_(helperc_LOADV4)(a+4); + return ( ((ULong)vHi) << 32 ) | ((ULong)vLo); + } + else + return mc_rd_V8_SLOWLY(a); +# endif +} + +REGPARM(1) +void MC_(helperc_STOREV8) ( Addr a, ULong vbytes ) +{ +# ifdef VG_DEBUG_MEMORY + mc_wr_V8_SLOWLY(a, vbytes); +# else + if (IS_ALIGNED8_ADDR(a)) { + UInt sec_no = shiftRight16(a) & 0xFFFF; + SecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + UChar abits = sm->abits[a_off]; + if (abits == VGM_BYTE_VALID) { + /* a is 8-aligned, mapped, and addressible. */ + UInt v_off = a & 0xFFFF; + UInt vHi = (UInt)(vbytes >> 32); + UInt vLo = (UInt)vbytes; + /* LITTLE-ENDIAN */ + ((UInt*)(sm->vbyte))[ (v_off >> 2) ] = vLo; + ((UInt*)(sm->vbyte))[ (v_off >> 2) + 1 ] = vHi; + } else { + mc_wr_V8_SLOWLY(a, vbytes); + } + return; + } + else + if (IS_ALIGNED4_ADDR(a)) { + UInt vHi = (UInt)(vbytes >> 32); + UInt vLo = (UInt)vbytes; + /* LITTLE-ENDIAN */ + MC_(helperc_STOREV4)(a+0, vLo); + MC_(helperc_STOREV4)(a+4, vHi); + return; + } + else + mc_wr_V8_SLOWLY(a, vbytes); +# endif +} + +/* ------------------------ Size = 4 ------------------------ */ + REGPARM(1) UInt MC_(helperc_LOADV4) ( Addr a ) { @@ -855,6 +938,8 @@ void MC_(helperc_STOREV4) ( Addr a, UInt vbytes ) # endif } +/* ------------------------ Size = 2 ------------------------ */ + REGPARM(1) UInt MC_(helperc_LOADV2) ( Addr a ) { @@ -899,6 +984,8 @@ void MC_(helperc_STOREV2) ( Addr a, UInt vbytes ) # endif } +/* ------------------------ Size = 1 ------------------------ */ + REGPARM(1) UInt MC_(helperc_LOADV1) ( Addr a ) { @@ -946,9 +1033,129 @@ void MC_(helperc_STOREV1) ( Addr a, UInt vbytes ) /*------------------------------------------------------------*/ /*--- Fallback functions to handle cases that the above ---*/ -/*--- VG_(helperc_{LD,ST}V{1,2,4}) can't manage. ---*/ +/*--- VG_(helperc_{LD,ST}V{1,2,4,8}) can't manage. ---*/ /*------------------------------------------------------------*/ +/* ------------------------ Size = 8 ------------------------ */ + +static ULong mc_rd_V8_SLOWLY ( Addr a ) +{ + Bool a0ok, a1ok, a2ok, a3ok, a4ok, a5ok, a6ok, a7ok; + UInt vb0, vb1, vb2, vb3, vb4, vb5, vb6, vb7; + + PROF_EVENT(70); + + /* First establish independently the addressibility of the 4 bytes + involved. */ + a0ok = get_abit(a+0) == VGM_BIT_VALID; + a1ok = get_abit(a+1) == VGM_BIT_VALID; + a2ok = get_abit(a+2) == VGM_BIT_VALID; + a3ok = get_abit(a+3) == VGM_BIT_VALID; + a4ok = get_abit(a+4) == VGM_BIT_VALID; + a5ok = get_abit(a+5) == VGM_BIT_VALID; + a6ok = get_abit(a+6) == VGM_BIT_VALID; + a7ok = get_abit(a+7) == VGM_BIT_VALID; + + /* Also get the validity bytes for the address. */ + vb0 = (UInt)get_vbyte(a+0); + vb1 = (UInt)get_vbyte(a+1); + vb2 = (UInt)get_vbyte(a+2); + vb3 = (UInt)get_vbyte(a+3); + vb4 = (UInt)get_vbyte(a+4); + vb5 = (UInt)get_vbyte(a+5); + vb6 = (UInt)get_vbyte(a+6); + vb7 = (UInt)get_vbyte(a+7); + + /* Now distinguish 3 cases */ + + /* Case 1: the address is completely valid, so: + - no addressing error + - return V bytes as read from memory + */ + if (a0ok && a1ok && a2ok && a3ok && a4ok && a5ok && a6ok && a7ok) { + ULong vw = VGM_WORD64_INVALID; + vw <<= 8; vw |= vb7; + vw <<= 8; vw |= vb6; + vw <<= 8; vw |= vb5; + vw <<= 8; vw |= vb4; + vw <<= 8; vw |= vb3; + vw <<= 8; vw |= vb2; + vw <<= 8; vw |= vb1; + vw <<= 8; vw |= vb0; + return vw; + } + + /* Case 2: the address is completely invalid. + - emit addressing error + - return V word indicating validity. + This sounds strange, but if we make loads from invalid addresses + give invalid data, we also risk producing a number of confusing + undefined-value errors later, which confuses the fact that the + error arose in the first place from an invalid address. + */ + /* VG_(printf)("%p (%d %d %d %d)\n", a, a0ok, a1ok, a2ok, a3ok); */ + if (!MAC_(clo_partial_loads_ok) + || ((a & 7) != 0) + || (!a0ok && !a1ok && !a2ok && !a3ok && !a4ok && !a5ok && !a6ok && !a7ok)) { + MAC_(record_address_error)( VG_(get_current_tid)(), a, 8, False ); + return VGM_WORD64_VALID; + } + + /* Case 3: the address is partially valid. + - no addressing error + - returned V word is invalid where the address is invalid, + and contains V bytes from memory otherwise. + Case 3 is only allowed if MC_(clo_partial_loads_ok) is True + (which is the default), and the address is 4-aligned. + If not, Case 2 will have applied. + */ + tl_assert(MAC_(clo_partial_loads_ok)); + { + ULong vw = VGM_WORD64_INVALID; + vw <<= 8; vw |= (a7ok ? vb7 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a6ok ? vb6 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a5ok ? vb5 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a4ok ? vb4 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a3ok ? vb3 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a2ok ? vb2 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a1ok ? vb1 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a0ok ? vb0 : VGM_BYTE_INVALID); + return vw; + } +} + +static void mc_wr_V8_SLOWLY ( Addr a, ULong vbytes ) +{ + /* Check the address for validity. */ + Bool aerr = False; + PROF_EVENT(71); + + if (get_abit(a+0) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+1) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+2) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+3) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+4) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+5) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+6) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+7) != VGM_BIT_VALID) aerr = True; + + /* Store the V bytes, remembering to do it little-endian-ly. */ + set_vbyte( a+0, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+1, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+2, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+3, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+4, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+5, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+6, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+7, vbytes & 0x000000FF ); + + /* If an address error has happened, report it. */ + if (aerr) + MAC_(record_address_error)( VG_(get_current_tid)(), a, 8, True ); +} + +/* ------------------------ Size = 4 ------------------------ */ + static UInt mc_rd_V4_SLOWLY ( Addr a ) { Bool a0ok, a1ok, a2ok, a3ok; @@ -1042,6 +1249,8 @@ static void mc_wr_V4_SLOWLY ( Addr a, UInt vbytes ) MAC_(record_address_error)( VG_(get_current_tid)(), a, 4, True ); } +/* ------------------------ Size = 2 ------------------------ */ + static UInt mc_rd_V2_SLOWLY ( Addr a ) { /* Check the address for validity. */ @@ -1083,6 +1292,8 @@ static void mc_wr_V2_SLOWLY ( Addr a, UInt vbytes ) MAC_(record_address_error)( VG_(get_current_tid)(), a, 2, True ); } +/* ------------------------ Size = 1 ------------------------ */ + static UInt mc_rd_V1_SLOWLY ( Addr a ) { /* Check the address for validity. */ @@ -1145,6 +1356,11 @@ void MC_(helperc_value_check4_fail) ( void ) MC_(record_value_error) ( VG_(get_current_tid)(), 4 ); } +REGPARM(1) void MC_(helperc_complain_undef) ( HWord sz ) +{ + MC_(record_value_error) ( VG_(get_current_tid)(), (Int)sz ); +} + /* --------------------------------------------------------------------- FPU load and store checks, called from generated code. @@ -1987,22 +2203,6 @@ void TL_(pre_clo_init)(void) VG_(init_post_reg_write_clientreq_return) ( & mc_post_reg_write ); VG_(init_post_reg_write_clientcall_return) ( & mc_post_reg_write_clientcall ); - /* Three compact slots taken up by stack memory helpers */ - VG_(register_compact_helper)((Addr) & MC_(helper_value_check4_fail)); - VG_(register_compact_helper)((Addr) & MC_(helper_value_check0_fail)); - VG_(register_compact_helper)((Addr) & MC_(helper_value_check2_fail)); - VG_(register_compact_helper)((Addr) & MC_(helperc_STOREV4)); - VG_(register_compact_helper)((Addr) & MC_(helperc_LOADV4)); - - /* These two made non-compact because 2-byte transactions are rare. */ - VG_(register_noncompact_helper)((Addr) & MC_(helperc_STOREV2)); - VG_(register_noncompact_helper)((Addr) & MC_(helperc_STOREV1)); - VG_(register_noncompact_helper)((Addr) & MC_(helperc_LOADV2)); - VG_(register_noncompact_helper)((Addr) & MC_(helperc_LOADV1)); - VG_(register_noncompact_helper)((Addr) & MC_(fpu_write_check)); - VG_(register_noncompact_helper)((Addr) & MC_(fpu_read_check)); - VG_(register_noncompact_helper)((Addr) & MC_(helper_value_check1_fail)); - VGP_(register_profile_event) ( VgpSetMem, "set-mem-perms" ); VGP_(register_profile_event) ( VgpCheckMem, "check-mem-perms" ); VGP_(register_profile_event) ( VgpESPAdj, "adjust-ESP" ); diff --git a/memcheck/mc_translate.c b/memcheck/mc_translate.c index 8d03ed79bd..a8723efe83 100644 --- a/memcheck/mc_translate.c +++ b/memcheck/mc_translate.c @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------*/ -/*--- Instrument UCode to perform memory checking operations. ---*/ +/*--- Instrument IR to perform memory checking operations. ---*/ /*--- mc_translate.c ---*/ /*--------------------------------------------------------------------*/ @@ -31,1628 +31,1617 @@ #include "mc_include.h" -/* --------------------------------------------------------------------- - Template functions for extending UCode - ------------------------------------------------------------------ */ -/* Compare this with the restrictions on core instructions in - vg_translate.c:is_sane_UInstr(). Everything general said there - applies here too. -*/ -Bool TL_(sane_XUInstr)(Bool beforeRA, Bool beforeLiveness, UInstr* u) -{ -// SSS: duplicating these macros really sucks -# define LIT0 (u->lit32 == 0) -# define LIT1 (!(LIT0)) -# define LITm (u->tag1 == Literal ? True : LIT0 ) -# define SZ0 (u->size == 0) -# define SZi (u->size == 4 || u->size == 2 || u->size == 1) -# define SZj (u->size == 4 || u->size == 2 || u->size == 1 || u->size == 0) -# define CC0 (u->flags_r == FlagsEmpty && u->flags_w == FlagsEmpty) -# define TR1 (beforeRA ? (u->tag1 == TempReg) : (u->tag1 == RealReg)) -# define TR2 (beforeRA ? (u->tag2 == TempReg) : (u->tag2 == RealReg)) -# define A1 (u->tag1 == ArchReg) -# define A2 (u->tag2 == ArchReg) -# define L1 (u->tag1 == Literal && u->val1 == 0) -# define Ls1 (u->tag1 == Lit16) -# define Ls3 (u->tag3 == Lit16) -# define TRL1 (TR1 || L1) -# define N2 (u->tag2 == NoValue) -# define N3 (u->tag3 == NoValue) -# define COND0 (u->cond == 0) -# define EXTRA4b0 (u->extra4b == 0) -# define SG_WD0 (u->signed_widen == 0) -# define JMPKIND0 (u->jmpkind == 0) -# define CCALL0 (u->argc==0 && u->regparms_n==0 && u->has_ret_val==0 && \ - ( beforeLiveness \ - ? u->regs_live_after == ALL_RREGS_LIVE \ - : True )) -# define XOTHER (COND0 && EXTRA4b0 && SG_WD0 && JMPKIND0 && CCALL0) - - Int n_lits = 0; - if (u->tag1 == Literal) n_lits++; - if (u->tag2 == Literal) n_lits++; - if (u->tag3 == Literal) n_lits++; - if (n_lits > 1) - return False; +/*------------------------------------------------------------*/ +/*--- Forward decls ---*/ +/*------------------------------------------------------------*/ + +struct _MCEnv; + +static IRType shadowType ( IRType ty ); +static IRExpr* expr2vbits ( struct _MCEnv* mce, IRExpr* e ); + + +/*------------------------------------------------------------*/ +/*--- Memcheck running state, and tmp management. ---*/ +/*------------------------------------------------------------*/ - /* Fields not checked: val1, val2, val3 */ - - switch (u->opcode) { - - /* Fields checked: lit32 size flags_r/w tag1 tag2 tag3 (rest) */ - case LOADV: return LIT0 && SZi && CC0 && TR1 && TR2 && N3 && XOTHER; - case STOREV: return LITm && SZi && CC0 && TRL1 && TR2 && N3 && XOTHER; - case GETV: return LIT0 && SZi && CC0 && A1 && TR2 && N3 && XOTHER; - case PUTV: return LITm && SZi && CC0 && TRL1 && A2 && N3 && XOTHER; - case GETVF: - case PUTVF: return LIT0 && SZ0 && CC0 && TR1 && N2 && N3 && XOTHER; - case TESTV: - case SETV: return LIT0 && SZj && CC0 && TR1 && N2 && N3 && XOTHER; - case TAG1: return LIT0 && SZ0 && CC0 && TR1 && N2 && Ls3 && XOTHER; - case TAG2: return LIT0 && SZ0 && CC0 && TR1 && TR2 && Ls3 && XOTHER; - default: - VG_(printf)("unhandled opcode: %u\n", u->opcode); - VG_(tool_panic)("TL_(sane_XUInstr): unhandled opcode"); +/* Carries around state during memcheck instrumentation. */ +typedef + struct _MCEnv { + /* MODIFIED: the bb being constructed. IRStmts are added. */ + IRBB* bb; + + /* MODIFIED: a table [0 .. #temps_in_original_bb-1] which maps + original temps to their current their current shadow temp. + Initially all entries are IRTemp_INVALID. Entries are added + lazily since many original temps are not used due to + optimisation prior to instrumentation. Note that floating + point original tmps are shadowed by integer tmps of the same + size, and Bit-typed original tmps are shadowed by the type + Ity_I8. See comment below. */ + IRTemp* tmpMap; + Int n_originalTmps; /* for range checking */ + + /* READONLY: the guest layout. This indicates which parts of + the guest state should be regarded as 'always defined'. */ + VexGuestLayout* layout; + /* READONLY: the host word type. Needed for constructing + arguments of type 'HWord' to be passed to helper functions. + Ity_I32 or Ity_I64 only. */ + IRType hWordTy; } -# undef LIT0 -# undef LIT1 -# undef LITm -# undef SZ0 -# undef SZi -# undef SZj -# undef CC0 -# undef TR1 -# undef TR2 -# undef A1 -# undef A2 -# undef L1 -# undef Ls1 -# undef Ls3 -# undef TRL1 -# undef N2 -# undef N3 -# undef COND0 -# undef EXTRA4b0 -# undef JMPKIND0 -# undef CCALL0 -# undef XOTHER -} + MCEnv; + +/* SHADOW TMP MANAGEMENT. Shadow tmps are allocated lazily (on + demand), as they are encountered. This is for two reasons. + + (1) (less important reason): Many original tmps are unused due to + initial IR optimisation, and we do not want to spaces in tables + tracking them. + + Shadow IRTemps are therefore allocated on demand. mce.tmpMap is a + table indexed [0 .. n_types-1], which gives the current shadow for + each original tmp, or INVALID_IRTEMP if none is so far assigned. + It is necessary to support making multiple assignments to a shadow + -- specifically, after testing a shadow for definedness, it needs + to be made defined. But IR's SSA property disallows this. + + (2) (more important reason): Therefore, when a shadow needs to get + a new value, a new temporary is created, the value is assigned to + that, and the tmpMap is updated to reflect the new binding. + + A corollary is that if the tmpMap maps a given tmp to + INVALID_IRTEMP and we are hoping to read that shadow tmp, it means + there's a read-before-write error in the original tmps. The IR + sanity checker should catch all such anomalies, however. +*/ -static Char* nameOfTagOp ( TagOp h ) +/* Find the tmp currently shadowing the given original tmp. If none + so far exists, allocate one. */ +static IRTemp findShadowTmp ( MCEnv* mce, IRTemp orig ) { - switch (h) { - case Tag_PCast40: return "PCast40"; - case Tag_PCast20: return "PCast20"; - case Tag_PCast10: return "PCast10"; - case Tag_PCast01: return "PCast01"; - case Tag_PCast02: return "PCast02"; - case Tag_PCast04: return "PCast04"; - case Tag_PCast14: return "PCast14"; - case Tag_PCast12: return "PCast12"; - case Tag_PCast11: return "PCast11"; - case Tag_Left4: return "Left4"; - case Tag_Left2: return "Left2"; - case Tag_Left1: return "Left1"; - case Tag_SWiden14: return "SWiden14"; - case Tag_SWiden24: return "SWiden24"; - case Tag_SWiden12: return "SWiden12"; - case Tag_ZWiden14: return "ZWiden14"; - case Tag_ZWiden24: return "ZWiden24"; - case Tag_ZWiden12: return "ZWiden12"; - case Tag_UifU4: return "UifU4"; - case Tag_UifU2: return "UifU2"; - case Tag_UifU1: return "UifU1"; - case Tag_UifU0: return "UifU0"; - case Tag_DifD4: return "DifD4"; - case Tag_DifD2: return "DifD2"; - case Tag_DifD1: return "DifD1"; - case Tag_ImproveAND4_TQ: return "ImproveAND4_TQ"; - case Tag_ImproveAND2_TQ: return "ImproveAND2_TQ"; - case Tag_ImproveAND1_TQ: return "ImproveAND1_TQ"; - case Tag_ImproveOR4_TQ: return "ImproveOR4_TQ"; - case Tag_ImproveOR2_TQ: return "ImproveOR2_TQ"; - case Tag_ImproveOR1_TQ: return "ImproveOR1_TQ"; - case Tag_DebugFn: return "DebugFn"; - default: VG_(tool_panic)("vg_nameOfTagOp"); + tl_assert(orig < mce->n_originalTmps); + if (mce->tmpMap[orig] == IRTemp_INVALID) { + mce->tmpMap[orig] + = newIRTemp(mce->bb->tyenv, + shadowType(mce->bb->tyenv->types[orig])); } + return mce->tmpMap[orig]; } - -Char* TL_(name_XUOpcode)(Opcode opc) +/* Allocate a new shadow for the given original tmp. This means any + previous shadow is abandoned. This is needed because it is + necessary to give a new value to a shadow once it has been tested + for undefinedness, but unfortunately IR's SSA property disallows + this. Instead we must abandon the old shadow, allocate a new one + and use that instead. */ +static void newShadowTmp ( MCEnv* mce, IRTemp orig ) { - switch (opc) { - case GETVF: return "GETVF"; - case PUTVF: return "PUTVF"; - case TAG1: return "TAG1"; - case TAG2: return "TAG2"; - case LOADV: return "LOADV"; - case STOREV: return "STOREV"; - case GETV: return "GETV"; - case PUTV: return "PUTV"; - case TESTV: return "TESTV"; - case SETV: return "SETV"; - default: - VG_(printf)("unhandled opcode: %u\n", opc); - VG_(tool_panic)("TL_(name_XUOpcode): unhandled case"); - } + tl_assert(orig < mce->n_originalTmps); + mce->tmpMap[orig] + = newIRTemp(mce->bb->tyenv, + shadowType(mce->bb->tyenv->types[orig])); } -/* --------------------------------------------------------------------- - Debugging stuff. - ------------------------------------------------------------------ */ -void TL_(pp_XUInstr)(UInstr* u) +/*------------------------------------------------------------*/ +/*--- IRAtoms -- a subset of IRExprs ---*/ +/*------------------------------------------------------------*/ + +/* An atom is either an IRExpr_Const or an IRExpr_Tmp, as defined by + isAtom() in libvex_ir.h. Because this instrumenter expects flat + input, most of this code deals in atoms. Usefully, a value atom + always has a V-value which is also an atom: constants are shadowed + by constants, and temps are shadowed by the corresponding shadow + temporary. */ + +typedef IRExpr IRAtom; + +/* (used for sanity checks only): is this an atom which looks + like it's from original code? */ +static Bool isOriginalAtom ( MCEnv* mce, IRAtom* a1 ) { - switch (u->opcode) { - case TAG1: - VG_(pp_UOperand)(u, 1, 4, False); - VG_(printf)(" = %s ( ", nameOfTagOp( u->val3 )); - VG_(pp_UOperand)(u, 1, 4, False); - VG_(printf)(" )"); - break; + if (a1->tag == Iex_Const) + return True; + if (a1->tag == Iex_Tmp && a1->Iex.Tmp.tmp < mce->n_originalTmps) + return True; + return False; +} - case TAG2: - VG_(pp_UOperand)(u, 2, 4, False); - VG_(printf)(" = %s ( ", nameOfTagOp( u->val3 )); - VG_(pp_UOperand)(u, 1, 4, False); - VG_(printf)(", "); - VG_(pp_UOperand)(u, 2, 4, False); - VG_(printf)(" )"); - break; +/* (used for sanity checks only): is this an atom which looks + like it's from shadow code? */ +static Bool isShadowAtom ( MCEnv* mce, IRAtom* a1 ) +{ + if (a1->tag == Iex_Const) + return True; + if (a1->tag == Iex_Tmp && a1->Iex.Tmp.tmp >= mce->n_originalTmps) + return True; + return False; +} - case STOREV: case LOADV: - VG_(pp_UOperand)(u, 1, u->size, u->opcode==LOADV); - VG_(printf)(", "); - VG_(pp_UOperand)(u, 2, u->size, u->opcode==STOREV); - break; +/* (used for sanity checks only): check that both args are atoms and + are identically-kinded. */ +static Bool sameKindedAtoms ( IRAtom* a1, IRAtom* a2 ) +{ + if (a1->tag == Iex_Tmp && a1->tag == Iex_Tmp) + return True; + if (a1->tag == Iex_Const && a1->tag == Iex_Const) + return True; + return False; +} - case PUTVF: case GETVF: - VG_(pp_UOperand)(u, 1, 0, False); - break; - case GETV: case PUTV: - VG_(pp_UOperand)(u, 1, u->opcode==PUTV ? 4 : u->size, False); - VG_(printf)(", "); - VG_(pp_UOperand)(u, 2, u->opcode==GETV ? 4 : u->size, False); - break; +/*------------------------------------------------------------*/ +/*--- Type management ---*/ +/*------------------------------------------------------------*/ - case TESTV: case SETV: - VG_(pp_UOperand)(u, 1, u->size, False); - break; +/* Shadow state is always accessed using integer types. This returns + an integer type with the same size (as per sizeofIRType) as the + given type. The only valid shadow types are Bit, I8, I16, I32, + I64. */ - default: - VG_(printf)("unhandled opcode: %u\n", u->opcode); - VG_(tool_panic)("TL_(pp_XUInstr): unhandled opcode"); +static IRType shadowType ( IRType ty ) +{ + switch (ty) { + case Ity_I1: + case Ity_I8: + case Ity_I16: + case Ity_I32: + case Ity_I64: return ty; + case Ity_F32: return Ity_I32; + case Ity_F64: return Ity_I64; + default: ppIRType(ty); + VG_(tool_panic)("memcheck:shadowType"); } +} +/* Produce a 'defined' value of the given shadow type. Should only be + supplied shadow types (Bit/I8/I16/I32/UI64). */ +static IRExpr* definedOfType ( IRType ty ) { + switch (ty) { + case Ity_I1: return IRExpr_Const(IRConst_U1(False)); + case Ity_I8: return IRExpr_Const(IRConst_U8(0)); + case Ity_I16: return IRExpr_Const(IRConst_U16(0)); + case Ity_I32: return IRExpr_Const(IRConst_U32(0)); + case Ity_I64: return IRExpr_Const(IRConst_U64(0)); + default: VG_(tool_panic)("memcheck:definedOfType"); + } } -Int TL_(get_Xreg_usage)(UInstr* u, Tag tag, Int* regs, Bool* isWrites) -{ -# define RD(ono) VG_UINSTR_READS_REG(ono, regs, isWrites) -# define WR(ono) VG_UINSTR_WRITES_REG(ono, regs, isWrites) - - Int n = 0; - switch (u->opcode) { - case TAG1: RD(1); WR(1); break; - case TAG2: RD(1); RD(2); WR(2); break; - case LOADV: RD(1); WR(2); break; - case STOREV: RD(1); RD(2); break; - case GETV: WR(2); break; - case PUTV: RD(1); break; - case TESTV: RD(1); break; - case SETV: WR(1); break; - case PUTVF: RD(1); break; - case GETVF: WR(1); break; - default: - VG_(printf)("unhandled opcode: %u\n", u->opcode); - VG_(tool_panic)("TL_(get_Xreg_usage): unhandled opcode"); - } - return n; +/*------------------------------------------------------------*/ +/*--- Constructing IR fragments ---*/ +/*------------------------------------------------------------*/ -# undef RD -# undef WR +/* assign value to tmp */ +#define assign(_bb,_tmp,_expr) \ + addStmtToIRBB((_bb), IRStmt_Tmp((_tmp),(_expr))) + +/* add stmt to a bb */ +#define stmt(_bb,_stmt) \ + addStmtToIRBB((_bb), (_stmt)) + +/* build various kinds of expressions */ +#define binop(_op, _arg1, _arg2) IRExpr_Binop((_op),(_arg1),(_arg2)) +#define unop(_op, _arg) IRExpr_Unop((_op),(_arg)) +#define mkU8(_n) IRExpr_Const(IRConst_U8(_n)) +#define mkU16(_n) IRExpr_Const(IRConst_U16(_n)) +#define mkU32(_n) IRExpr_Const(IRConst_U32(_n)) +#define mkU64(_n) IRExpr_Const(IRConst_U64(_n)) +#define mkexpr(_tmp) IRExpr_Tmp((_tmp)) + +/* bind the given expression to a new temporary, and return the + temporary. This effectively converts an arbitrary expression into + an atom. */ +static IRAtom* assignNew ( MCEnv* mce, IRType ty, IRExpr* e ) { + IRTemp t = newIRTemp(mce->bb->tyenv, ty); + assign(mce->bb, t, e); + return mkexpr(t); } + /*------------------------------------------------------------*/ -/*--- New instrumentation machinery. ---*/ +/*--- Constructing definedness primitive ops ---*/ /*------------------------------------------------------------*/ -static -TagOp get_Tag_ImproveOR_TQ ( Int sz ) -{ - switch (sz) { - case 4: return Tag_ImproveOR4_TQ; - case 2: return Tag_ImproveOR2_TQ; - case 1: return Tag_ImproveOR1_TQ; - default: VG_(tool_panic)("get_Tag_ImproveOR_TQ"); - } +/* --------- Defined-if-either-defined --------- */ + +static IRAtom* mkDifD8 ( MCEnv* mce, IRAtom* a1, IRAtom* a2 ) { + tl_assert(isShadowAtom(mce,a1)); + tl_assert(isShadowAtom(mce,a2)); + return assignNew(mce, Ity_I8, binop(Iop_And8, a1, a2)); } +static IRAtom* mkDifD16 ( MCEnv* mce, IRAtom* a1, IRAtom* a2 ) { + tl_assert(isShadowAtom(mce,a1)); + tl_assert(isShadowAtom(mce,a2)); + return assignNew(mce, Ity_I16, binop(Iop_And16, a1, a2)); +} -static -TagOp get_Tag_ImproveAND_TQ ( Int sz ) -{ - switch (sz) { - case 4: return Tag_ImproveAND4_TQ; - case 2: return Tag_ImproveAND2_TQ; - case 1: return Tag_ImproveAND1_TQ; - default: VG_(tool_panic)("get_Tag_ImproveAND_TQ"); - } +static IRAtom* mkDifD32 ( MCEnv* mce, IRAtom* a1, IRAtom* a2 ) { + tl_assert(isShadowAtom(mce,a1)); + tl_assert(isShadowAtom(mce,a2)); + return assignNew(mce, Ity_I32, binop(Iop_And32, a1, a2)); } +/* --------- Undefined-if-either-undefined --------- */ -static -TagOp get_Tag_Left ( Int sz ) -{ - switch (sz) { - case 4: return Tag_Left4; - case 2: return Tag_Left2; - case 1: return Tag_Left1; - default: VG_(tool_panic)("get_Tag_Left"); - } +static IRAtom* mkUifU8 ( MCEnv* mce, IRAtom* a1, IRAtom* a2 ) { + tl_assert(isShadowAtom(mce,a1)); + tl_assert(isShadowAtom(mce,a2)); + return assignNew(mce, Ity_I8, binop(Iop_Or8, a1, a2)); } +static IRAtom* mkUifU16 ( MCEnv* mce, IRAtom* a1, IRAtom* a2 ) { + tl_assert(isShadowAtom(mce,a1)); + tl_assert(isShadowAtom(mce,a2)); + return assignNew(mce, Ity_I16, binop(Iop_Or16, a1, a2)); +} -static -TagOp get_Tag_UifU ( Int sz ) -{ - switch (sz) { - case 4: return Tag_UifU4; - case 2: return Tag_UifU2; - case 1: return Tag_UifU1; - case 0: return Tag_UifU0; - default: VG_(tool_panic)("get_Tag_UifU"); - } +static IRAtom* mkUifU32 ( MCEnv* mce, IRAtom* a1, IRAtom* a2 ) { + tl_assert(isShadowAtom(mce,a1)); + tl_assert(isShadowAtom(mce,a2)); + return assignNew(mce, Ity_I32, binop(Iop_Or32, a1, a2)); } +static IRAtom* mkUifU64 ( MCEnv* mce, IRAtom* a1, IRAtom* a2 ) { + tl_assert(isShadowAtom(mce,a1)); + tl_assert(isShadowAtom(mce,a2)); + return assignNew(mce, Ity_I64, binop(Iop_Or64, a1, a2)); +} -static -TagOp get_Tag_DifD ( Int sz ) -{ - switch (sz) { - case 4: return Tag_DifD4; - case 2: return Tag_DifD2; - case 1: return Tag_DifD1; - default: VG_(tool_panic)("get_Tag_DifD"); +static IRAtom* mkUifU ( MCEnv* mce, IRType vty, IRAtom* a1, IRAtom* a2 ) { + switch (vty) { + case Ity_I16: return mkUifU16(mce, a1, a2); + case Ity_I32: return mkUifU32(mce, a1, a2); + case Ity_I64: return mkUifU64(mce, a1, a2); + default: + VG_(printf)("\n"); ppIRType(vty); VG_(printf)("\n"); + VG_(tool_panic)("memcheck:mkUifU"); } } +/* --------- The Left-family of operations. --------- */ -static -TagOp get_Tag_PCast ( Int szs, Int szd ) -{ - if (szs == 4 && szd == 0) return Tag_PCast40; - if (szs == 2 && szd == 0) return Tag_PCast20; - if (szs == 1 && szd == 0) return Tag_PCast10; - if (szs == 0 && szd == 1) return Tag_PCast01; - if (szs == 0 && szd == 2) return Tag_PCast02; - if (szs == 0 && szd == 4) return Tag_PCast04; - if (szs == 1 && szd == 4) return Tag_PCast14; - if (szs == 1 && szd == 2) return Tag_PCast12; - if (szs == 1 && szd == 1) return Tag_PCast11; - VG_(printf)("get_Tag_PCast(%d,%d)\n", szs, szd); - VG_(tool_panic)("get_Tag_PCast"); +static IRAtom* mkLeft8 ( MCEnv* mce, IRAtom* a1 ) { + tl_assert(isShadowAtom(mce,a1)); + /* It's safe to duplicate a1 since it's only an atom */ + return assignNew(mce, Ity_I8, + binop(Iop_Or8, a1, + assignNew(mce, Ity_I8, + /* unop(Iop_Neg8, a1)))); */ + binop(Iop_Sub8, mkU8(0), a1) ))); } +static IRAtom* mkLeft16 ( MCEnv* mce, IRAtom* a1 ) { + tl_assert(isShadowAtom(mce,a1)); + /* It's safe to duplicate a1 since it's only an atom */ + return assignNew(mce, Ity_I16, + binop(Iop_Or16, a1, + assignNew(mce, Ity_I16, + /* unop(Iop_Neg16, a1)))); */ + binop(Iop_Sub16, mkU16(0), a1) ))); +} -static -TagOp get_Tag_Widen ( Bool syned, Int szs, Int szd ) -{ - if (szs == 1 && szd == 2 && syned) return Tag_SWiden12; - if (szs == 1 && szd == 2 && !syned) return Tag_ZWiden12; - - if (szs == 1 && szd == 4 && syned) return Tag_SWiden14; - if (szs == 1 && szd == 4 && !syned) return Tag_ZWiden14; +static IRAtom* mkLeft32 ( MCEnv* mce, IRAtom* a1 ) { + tl_assert(isShadowAtom(mce,a1)); + /* It's safe to duplicate a1 since it's only an atom */ + return assignNew(mce, Ity_I32, + binop(Iop_Or32, a1, + assignNew(mce, Ity_I32, + /* unop(Iop_Neg32, a1)))); */ + binop(Iop_Sub32, mkU32(0), a1) ))); +} - if (szs == 2 && szd == 4 && syned) return Tag_SWiden24; - if (szs == 2 && szd == 4 && !syned) return Tag_ZWiden24; +/* --------- 'Improvement' functions for AND/OR. --------- */ - VG_(printf)("get_Tag_Widen(%d,%d,%d)\n", (Int)syned, szs, szd); - VG_(tool_panic)("get_Tag_Widen"); +/* ImproveAND(data, vbits) = data OR vbits. Defined (0) data 0s give + defined (0); all other -> undefined (1). +*/ +static IRAtom* mkImproveAND8 ( MCEnv* mce, IRAtom* data, IRAtom* vbits ) +{ + tl_assert(isOriginalAtom(mce, data)); + tl_assert(isShadowAtom(mce, vbits)); + tl_assert(sameKindedAtoms(data, vbits)); + return assignNew(mce, Ity_I8, binop(Iop_Or8, data, vbits)); } -/* Pessimally cast the spec'd shadow from one size to another. */ -static -void create_PCast ( UCodeBlock* cb, Int szs, Int szd, Int tempreg ) +static IRAtom* mkImproveAND16 ( MCEnv* mce, IRAtom* data, IRAtom* vbits ) { - if (szs == 0 && szd == 0) - return; - uInstr3(cb, TAG1, 0, TempReg, tempreg, - NoValue, 0, - Lit16, get_Tag_PCast(szs,szd)); + tl_assert(isOriginalAtom(mce, data)); + tl_assert(isShadowAtom(mce, vbits)); + tl_assert(sameKindedAtoms(data, vbits)); + return assignNew(mce, Ity_I16, binop(Iop_Or16, data, vbits)); } +static IRAtom* mkImproveAND32 ( MCEnv* mce, IRAtom* data, IRAtom* vbits ) +{ + tl_assert(isOriginalAtom(mce, data)); + tl_assert(isShadowAtom(mce, vbits)); + tl_assert(sameKindedAtoms(data, vbits)); + return assignNew(mce, Ity_I32, binop(Iop_Or32, data, vbits)); +} -/* Create a signed or unsigned widen of the spec'd shadow from one - size to another. The only allowed size transitions are 1->2, 1->4 - and 2->4. */ -static -void create_Widen ( UCodeBlock* cb, Bool signed_widen, - Int szs, Int szd, Int tempreg ) +/* ImproveOR(data, vbits) = ~data OR vbits. Defined (0) data 1s give + defined (0); all other -> undefined (1). +*/ +static IRAtom* mkImproveOR8 ( MCEnv* mce, IRAtom* data, IRAtom* vbits ) { - if (szs == szd) return; - uInstr3(cb, TAG1, 0, TempReg, tempreg, - NoValue, 0, - Lit16, get_Tag_Widen(signed_widen,szs,szd)); + tl_assert(isOriginalAtom(mce, data)); + tl_assert(isShadowAtom(mce, vbits)); + tl_assert(sameKindedAtoms(data, vbits)); + return assignNew( + mce, Ity_I8, + binop(Iop_Or8, + assignNew(mce, Ity_I8, unop(Iop_Not8, data)), + vbits) ); } +static IRAtom* mkImproveOR16 ( MCEnv* mce, IRAtom* data, IRAtom* vbits ) +{ + tl_assert(isOriginalAtom(mce, data)); + tl_assert(isShadowAtom(mce, vbits)); + tl_assert(sameKindedAtoms(data, vbits)); + return assignNew( + mce, Ity_I16, + binop(Iop_Or16, + assignNew(mce, Ity_I16, unop(Iop_Not16, data)), + vbits) ); +} -/* Get the condition codes into a new shadow, at the given size. */ -static -Int create_GETVF ( UCodeBlock* cb, Int sz ) +static IRAtom* mkImproveOR32 ( MCEnv* mce, IRAtom* data, IRAtom* vbits ) { - Int tt = newShadow(cb); - uInstr1(cb, GETVF, 0, TempReg, tt); - create_PCast(cb, 0, sz, tt); - return tt; + tl_assert(isOriginalAtom(mce, data)); + tl_assert(isShadowAtom(mce, vbits)); + tl_assert(sameKindedAtoms(data, vbits)); + return assignNew( + mce, Ity_I32, + binop(Iop_Or32, + assignNew(mce, Ity_I32, unop(Iop_Not32, data)), + vbits) ); } +/* --------- Pessimising casts. --------- */ -/* Save the condition codes from the spec'd shadow. */ -static -void create_PUTVF ( UCodeBlock* cb, Int sz, Int tempreg ) +static IRAtom* mkPCastTo( MCEnv* mce, IRType dst_ty, IRAtom* vbits ) { - if (sz == 0) { - uInstr1(cb, PUTVF, 0, TempReg, tempreg); - } else { - Int tt = newShadow(cb); - uInstr2(cb, MOV, 4, TempReg, tempreg, TempReg, tt); - create_PCast(cb, sz, 0, tt); - uInstr1(cb, PUTVF, 0, TempReg, tt); + /* Note, dst_ty is a shadow type, not an original type. */ + /* First of all, collapse vbits down to a single bit. */ + tl_assert(isShadowAtom(mce,vbits)); + IRType ty = typeOfIRExpr(mce->bb->tyenv, vbits); + IRAtom* tmp1 = NULL; + switch (ty) { + case Ity_I1: + tmp1 = vbits; + break; + case Ity_I8: + tmp1 = assignNew(mce, Ity_I1, binop(Iop_CmpNE8, vbits, mkU8(0))); + break; + case Ity_I16: + tmp1 = assignNew(mce, Ity_I1, binop(Iop_CmpNE16, vbits, mkU16(0))); + break; + case Ity_I32: + tmp1 = assignNew(mce, Ity_I1, binop(Iop_CmpNE32, vbits, mkU32(0))); + break; + case Ity_I64: + tmp1 = assignNew(mce, Ity_I1, binop(Iop_CmpNE64, vbits, mkU64(0))); + break; + default: + VG_(tool_panic)("mkPCastTo(1)"); + } + tl_assert(tmp1); + /* Now widen up to the dst type. */ + switch (dst_ty) { + case Ity_I1: + return tmp1; + case Ity_I8: + return assignNew(mce, Ity_I8, unop(Iop_1Sto8, tmp1)); + case Ity_I16: + return assignNew(mce, Ity_I16, unop(Iop_1Sto16, tmp1)); + case Ity_I32: + return assignNew(mce, Ity_I32, unop(Iop_1Sto32, tmp1)); + case Ity_I64: + return assignNew(mce, Ity_I64, unop(Iop_1Sto64, tmp1)); + default: + ppIRType(dst_ty); + VG_(tool_panic)("mkPCastTo(2)"); } } -/* Do Left on the spec'd shadow. */ -static -void create_Left ( UCodeBlock* cb, Int sz, Int tempreg ) -{ - uInstr3(cb, TAG1, 0, - TempReg, tempreg, - NoValue, 0, - Lit16, get_Tag_Left(sz)); +/*------------------------------------------------------------*/ +/*--- Emit a test and complaint if something is undefined. ---*/ +/*------------------------------------------------------------*/ + +/* Set the annotations on a dirty helper to indicate that the stack + pointer and instruction pointers might be read. This is the + behaviour of all 'emit-a-complaint' style functions we might + call. */ + +static void setHelperAnns ( MCEnv* mce, IRDirty* di ) { + di->nFxState = 2; + di->fxState[0].fx = Ifx_Read; + di->fxState[0].offset = mce->layout->offset_SP; + di->fxState[0].size = mce->layout->sizeof_SP; + di->fxState[1].fx = Ifx_Read; + di->fxState[1].offset = mce->layout->offset_IP; + di->fxState[1].size = mce->layout->sizeof_IP; } -/* Do UifU on ts and td, putting the result in td. */ -static -void create_UifU ( UCodeBlock* cb, Int sz, Int ts, Int td ) +/* 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. +*/ +static void complainIfUndefined ( MCEnv* mce, IRAtom* atom ) { - uInstr3(cb, TAG2, 0, TempReg, ts, TempReg, td, - Lit16, get_Tag_UifU(sz)); -} + /* Since the original expression is atomic, there's no duplicated + work generated by making multiple V-expressions for it. So we + don't really care about the possibility that someone else may + also create a V-interpretion for it. */ + tl_assert(isOriginalAtom(mce, atom)); + IRAtom* vatom = expr2vbits( mce, atom ); + tl_assert(isShadowAtom(mce, vatom)); + tl_assert(sameKindedAtoms(atom, vatom)); + IRType ty = typeOfIRExpr(mce->bb->tyenv, vatom); -/* Do DifD on ts and td, putting the result in td. */ -static -void create_DifD ( UCodeBlock* cb, Int sz, Int ts, Int td ) -{ - uInstr3(cb, TAG2, 0, TempReg, ts, TempReg, td, - Lit16, get_Tag_DifD(sz)); -} + /* sz is only used for constructing the error message */ + Int sz = ty==Ity_I1 ? 0 : sizeofIRType(ty); + IRAtom* cond = mkPCastTo( mce, Ity_I1, vatom ); + /* cond will be 0 if all defined, and 1 if any not defined. */ -/* Do HelpAND on value tval and tag tqqq, putting the result in - tqqq. */ -static -void create_ImproveAND_TQ ( UCodeBlock* cb, Int sz, Int tval, Int tqqq ) -{ - uInstr3(cb, TAG2, 0, TempReg, tval, TempReg, tqqq, - Lit16, get_Tag_ImproveAND_TQ(sz)); + IRDirty* di; + switch (sz) { + case 0: + di = unsafeIRDirty_0_N( 0/*regparms*/, + "MC_(helperc_value_check0_fail)", + &MC_(helperc_value_check0_fail), + mkIRExprVec_0() + ); + break; + case 1: + di = unsafeIRDirty_0_N( 0/*regparms*/, + "MC_(helperc_value_check1_fail)", + &MC_(helperc_value_check1_fail), + mkIRExprVec_0() + ); + break; + case 4: + di = unsafeIRDirty_0_N( 0/*regparms*/, + "MC_(helperc_value_check4_fail)", + &MC_(helperc_value_check4_fail), + mkIRExprVec_0() + ); + break; + default: + di = unsafeIRDirty_0_N( 1/*regparms*/, + "MC_(helperc_complain_undef)", + &MC_(helperc_complain_undef), + mkIRExprVec_1( mkIRExpr_HWord( sz )) + ); + break; + } + di->guard = cond; + setHelperAnns( mce, di ); + stmt( mce->bb, IRStmt_Dirty(di)); + + /* Set the shadow tmp to be defined. First, update the + orig->shadow tmp mapping to reflect the fact that this shadow is + getting a new value. */ + tl_assert(isAtom(vatom)); + /* sameKindedAtoms ... */ + if (vatom->tag == Iex_Tmp) { + tl_assert(atom->tag == Iex_Tmp); + newShadowTmp(mce, atom->Iex.Tmp.tmp); + assign(mce->bb, findShadowTmp(mce, atom->Iex.Tmp.tmp), + definedOfType(ty)); + } } -/* Do HelpOR on value tval and tag tqqq, putting the result in - tqqq. */ -static -void create_ImproveOR_TQ ( UCodeBlock* cb, Int sz, Int tval, Int tqqq ) +/*------------------------------------------------------------*/ +/*--- Shadowing PUTs/GETs, and indexed variants thereof ---*/ +/*------------------------------------------------------------*/ + +/* Examine the always-defined sections declared in layout to see if + the (offset,size) section is within one. Note, is is an error to + partially fall into such a region: (offset,size) should either be + completely in such a region or completely not-in such a region. +*/ +static Bool isAlwaysDefd ( MCEnv* mce, Int offset, Int size ) { - uInstr3(cb, TAG2, 0, TempReg, tval, TempReg, tqqq, - Lit16, get_Tag_ImproveOR_TQ(sz)); + Int minoffD, maxoffD, i; + Int minoff = offset; + Int maxoff = minoff + size - 1; + tl_assert((minoff & ~0xFFFF) == 0); + tl_assert((maxoff & ~0xFFFF) == 0); + + for (i = 0; i < mce->layout->n_alwaysDefd; i++) { + minoffD = mce->layout->alwaysDefd[i].offset; + maxoffD = minoffD + mce->layout->alwaysDefd[i].size - 1; + tl_assert((minoffD & ~0xFFFF) == 0); + tl_assert((maxoffD & ~0xFFFF) == 0); + + if (maxoff < minoffD || maxoffD < minoff) + continue; /* no overlap */ + if (minoff >= minoffD && maxoff <= maxoffD) + return True; /* completely contained in an always-defd section */ + + VG_(tool_panic)("memcheck:isAlwaysDefd:partial overlap"); + } + return False; /* could not find any containing section */ } -/* Get the shadow for an operand described by (tag, val). Emit code - to do this and return the identity of the shadow holding the - result. The result tag is always copied into a new shadow, so it - can be modified without trashing the original.*/ +/* Generate into bb suitable actions to shadow this Put. If the state + slice is marked 'always defined', do nothing. Otherwise, write the + supplied V bits to the shadow state. We can pass in either an + original atom or a V-atom, but not both. In the former case the + relevant V-bits are then generated from the original. +*/ static -Int /* TempReg */ getOperandShadow ( UCodeBlock* cb, - Int sz, Int tag, Int val ) +void do_shadow_PUT ( MCEnv* mce, Int offset, + IRAtom* atom, IRAtom* vatom ) { - Int sh; - sh = newShadow(cb); - if (tag == TempReg) { - uInstr2(cb, MOV, 4, TempReg, SHADOW(val), TempReg, sh); - return sh; + if (atom) { + tl_assert(!vatom); + tl_assert(isOriginalAtom(mce, atom)); + vatom = expr2vbits( mce, atom ); + } else { + tl_assert(vatom); + tl_assert(isShadowAtom(mce, vatom)); } - if (tag == Literal) { - uInstr1(cb, SETV, sz, TempReg, sh); - return sh; - } - if (tag == ArchReg) { - uInstr2(cb, GETV, sz, ArchReg, val, TempReg, sh); - return sh; + + IRType ty = typeOfIRExpr(mce->bb->tyenv, vatom); + tl_assert(ty != Ity_I1); + if (isAlwaysDefd(mce, offset, sizeofIRType(ty))) { + /* later: no ... */ + /* emit code to emit a complaint if any of the vbits are 1. */ + /* complainIfUndefined(mce, atom); */ + } else { + /* Do a plain shadow Put. */ + stmt( mce->bb, IRStmt_Put( offset + mce->layout->total_sizeB, vatom ) ); } - VG_(tool_panic)("getOperandShadow"); } -/* Create and return an instrumented version of cb_in. Free cb_in - before returning. */ -static UCodeBlock* memcheck_instrument ( UCodeBlock* cb_in ) + +/* Return an expression which contains the V bits corresponding to the + given GETI (passed in in pieces). +*/ +static +void do_shadow_PUTI ( MCEnv* mce, + IRArray* descr, IRAtom* ix, Int bias, IRAtom* atom ) { - UCodeBlock* cb; - Int i, j; - UInstr* u_in; - Int qs, qd, qt, qtt; - Bool bogusLiterals; - - cb = VG_(setup_UCodeBlock)(cb_in); - - /* Scan the block to look for bogus literals. These are magic - numbers which particularly appear in hand-optimised / inlined - implementations of strlen() et al which cause so much trouble - (spurious reports of uninit-var uses). Purpose of this horrible - hack is to disable some checks any such literals are present in - this basic block. */ - bogusLiterals = False; - - if (MC_(clo_avoid_strlen_errors)) { - for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { - u_in = VG_(get_instr)(cb_in, i); - switch (u_in->opcode) { - case ADD: case SUB: case MOV: - if (u_in->size == 4 && u_in->tag1 == Literal) - goto literal; - break; - case LEA1: - tl_assert(u_in->size == 4); - goto literal; - default: - break; - } - continue; - literal: - if (u_in->lit32 == 0xFEFEFEFF || - u_in->lit32 == 0x80808080 || - u_in->lit32 == 0x00008080) { - bogusLiterals = True; - break; - } - } + tl_assert(isOriginalAtom(mce,atom)); + IRAtom* vatom = expr2vbits( mce, atom ); + tl_assert(sameKindedAtoms(atom, vatom)); + IRType ty = descr->elemTy; + IRType tyS = shadowType(ty); + Int arrSize = descr->nElems * sizeofIRType(ty); + tl_assert(ty != Ity_I1); + tl_assert(isOriginalAtom(mce,ix)); + complainIfUndefined(mce,ix); + if (isAlwaysDefd(mce, descr->base, arrSize)) { + /* later: no ... */ + /* emit code to emit a complaint if any of the vbits are 1. */ + /* complainIfUndefined(mce, atom); */ + } else { + /* Do a cloned version of the Put that refers to the shadow + area. */ + IRArray* new_descr + = mkIRArray( descr->base + mce->layout->total_sizeB, + tyS, descr->nElems); + stmt( mce->bb, IRStmt_PutI( new_descr, ix, bias, vatom )); } +} - for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { - u_in = VG_(get_instr)(cb_in, i); - qs = qd = qt = qtt = INVALID_TEMPREG; - switch (u_in->opcode) { +/* Return an expression which contains the V bits corresponding to the + given GET (passed in in pieces). +*/ +static +IRExpr* shadow_GET ( MCEnv* mce, Int offset, IRType ty ) +{ + IRType tyS = shadowType(ty); + tl_assert(ty != Ity_I1); + if (isAlwaysDefd(mce, offset, sizeofIRType(ty))) { + /* Always defined, return all zeroes of the relevant type */ + return definedOfType(tyS); + } else { + /* return a cloned version of the Get that refers to the shadow + area. */ + return IRExpr_Get( offset + mce->layout->total_sizeB, tyS ); + } +} - case LOCK: - case NOP: - break; - case INCEIP: - VG_(copy_UInstr)(cb, u_in); - break; +/* Return an expression which contains the V bits corresponding to the + given GETI (passed in in pieces). +*/ +static +IRExpr* shadow_GETI ( MCEnv* mce, IRArray* descr, IRAtom* ix, Int bias ) +{ + IRType ty = descr->elemTy; + IRType tyS = shadowType(ty); + Int arrSize = descr->nElems * sizeofIRType(ty); + tl_assert(ty != Ity_I1); + tl_assert(isOriginalAtom(mce,ix)); + complainIfUndefined(mce,ix); + if (isAlwaysDefd(mce, descr->base, arrSize)) { + /* Always defined, return all zeroes of the relevant type */ + return definedOfType(tyS); + } else { + /* return a cloned version of the Get that refers to the shadow + area. */ + IRArray* new_descr + = mkIRArray( descr->base + mce->layout->total_sizeB, + tyS, descr->nElems); + return IRExpr_GetI( new_descr, ix, bias ); + } +} - /* The segment registers do not have their definedness - tracked. We therefore make fake shadows on GETSEG and - test them on PUTSEG. This will catch writing garbage to a - segment register; therefore we can assume it to be defined - when read (GETSEGd). Since the first arg of USESEG is - fetched by GETSEG, we can assume it to be defined, and so - the definedness of the result is simply the definedness of - the second (virtual_address) arg of USESEG. The upshot of - all this is that instrumentation of USESEG is a no-op! */ - - case PUTSEG: - tl_assert(u_in->tag1 == TempReg); - uInstr1(cb, TESTV, 2, TempReg, SHADOW(u_in->val1)); - uInstr1(cb, SETV, 2, TempReg, SHADOW(u_in->val1)); - VG_(copy_UInstr)(cb, u_in); - break; - case GETSEG: - tl_assert(u_in->tag2 == TempReg); - uInstr1(cb, SETV, 2, TempReg, SHADOW(u_in->val2)); - VG_(copy_UInstr)(cb, u_in); - break; +/*------------------------------------------------------------*/ +/*--- Generating approximations for unknown operations, ---*/ +/*--- using lazy-propagate semantics ---*/ +/*------------------------------------------------------------*/ - case USESEG: - VG_(copy_UInstr)(cb, u_in); - break; +/* Lazy propagation of undefinedness from two values, resulting in the + specified shadow type. +*/ +static +IRAtom* mkLazy2 ( MCEnv* mce, IRType finalVty, IRAtom* va1, IRAtom* va2 ) +{ + /* force everything via 32-bit intermediaries. */ + IRAtom* at; + tl_assert(isShadowAtom(mce,va1)); + tl_assert(isShadowAtom(mce,va2)); + at = mkPCastTo(mce, Ity_I32, va1); + at = mkUifU(mce, Ity_I32, at, mkPCastTo(mce, Ity_I32, va2)); + at = mkPCastTo(mce, finalVty, at); + return at; +} - /* Loads and stores. Test the V bits for the address. 24 - Mar 02: since the address is A-checked anyway, there's not - really much point in doing the V-check too, unless you - think that you might use addresses which are undefined but - still addressible. Hence the optionalisation of the V - check. 15 Dec 02: optionalisation removed, since it no - longer makes much sense given we also have an addrcheck - tool. - - The LOADV/STOREV does an addressibility check for the - address. */ - - case LOAD: - uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1)); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1)); - uInstr2(cb, LOADV, u_in->size, - TempReg, u_in->val1, - TempReg, SHADOW(u_in->val2)); - VG_(copy_UInstr)(cb, u_in); - break; - case STORE: - uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val2)); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); - uInstr2(cb, STOREV, u_in->size, - TempReg, SHADOW(u_in->val1), - TempReg, u_in->val2); - VG_(copy_UInstr)(cb, u_in); - break; +/* Do the lazy propagation game from a null-terminated vector of + atoms. This is presumably the arguments to a helper call, so the + IRCallee info is also supplied in order that we can know which + arguments should be ignored (via the .mcx_mask field). +*/ +static +IRAtom* mkLazyN ( MCEnv* mce, + IRAtom** exprvec, IRType finalVtype, IRCallee* cee ) +{ + Int i; + IRAtom* here; + IRAtom* curr = definedOfType(Ity_I32); + for (i = 0; exprvec[i]; i++) { + tl_assert(i < 32); + tl_assert(isOriginalAtom(mce, exprvec[i])); + /* Only take notice of this arg if the callee's mc-exclusion + mask does not say it is to be excluded. */ + if (cee->mcx_mask & (1<name, i); + } else { + /* calculate the arg's definedness, and pessimistically merge + it in. */ + here = mkPCastTo( mce, Ity_I32, expr2vbits(mce, exprvec[i]) ); + curr = mkUifU32(mce, here, curr); + } + } + return mkPCastTo(mce, finalVtype, curr ); +} - /* Moving stuff around. Make the V bits follow accordingly, - but don't do anything else. */ - case GET: - uInstr2(cb, GETV, u_in->size, - ArchReg, u_in->val1, - TempReg, SHADOW(u_in->val2)); - VG_(copy_UInstr)(cb, u_in); - break; +/*------------------------------------------------------------*/ +/*--- Generating expensive sequences for exact carry-chain ---*/ +/*--- propagation in add/sub and related operations. ---*/ +/*------------------------------------------------------------*/ - case PUT: - uInstr2(cb, PUTV, u_in->size, - TempReg, SHADOW(u_in->val1), - ArchReg, u_in->val2); - VG_(copy_UInstr)(cb, u_in); - break; +static +IRAtom* expensiveAdd32 ( MCEnv* mce, IRAtom* qaa, IRAtom* qbb, + IRAtom* aa, IRAtom* bb ) +{ + tl_assert(isShadowAtom(mce,qaa)); + tl_assert(isShadowAtom(mce,qbb)); + tl_assert(isOriginalAtom(mce,aa)); + tl_assert(isOriginalAtom(mce,bb)); + tl_assert(sameKindedAtoms(qaa,aa)); + tl_assert(sameKindedAtoms(qbb,bb)); + + IRType ty = Ity_I32; + IROp opAND = Iop_And32; + IROp opOR = Iop_Or32; + IROp opXOR = Iop_Xor32; + IROp opNOT = Iop_Not32; + IROp opADD = Iop_Add32; + + IRAtom *a_min, *b_min, *a_max, *b_max; + + // a_min = aa & ~qaa + a_min = assignNew(mce,ty, + binop(opAND, aa, + assignNew(mce,ty, unop(opNOT, qaa)))); + + // b_min = bb & ~qbb + b_min = assignNew(mce,ty, + binop(opAND, bb, + assignNew(mce,ty, unop(opNOT, qbb)))); + + // a_max = aa | qaa + a_max = assignNew(mce,ty, binop(opOR, aa, qaa)); + + // b_max = bb | qbb + b_max = assignNew(mce,ty, binop(opOR, bb, qbb)); + + // result = (qaa | qbb) | ((a_min + b_min) ^ (a_max + b_max)) + return + assignNew(mce,ty, + binop( opOR, + assignNew(mce,ty, binop(opOR, qaa, qbb)), + assignNew(mce,ty, + binop(opXOR, assignNew(mce,ty, binop(opADD, a_min, b_min)), + assignNew(mce,ty, binop(opADD, a_max, b_max)) + ) + ) + ) + ); +} - case GETF: - /* This is not the smartest way to do it, but should work. */ - qd = create_GETVF(cb, u_in->size); - uInstr2(cb, MOV, 4, TempReg, qd, TempReg, SHADOW(u_in->val1)); - VG_(copy_UInstr)(cb, u_in); - break; - case PUTF: - create_PUTVF(cb, u_in->size, SHADOW(u_in->val1)); - VG_(copy_UInstr)(cb, u_in); - break; +/*------------------------------------------------------------*/ +/*--- Generate shadow values from all kinds of IRExprs. ---*/ +/*------------------------------------------------------------*/ - case MOV: - switch (u_in->tag1) { - case TempReg: - uInstr2(cb, MOV, 4, - TempReg, SHADOW(u_in->val1), - TempReg, SHADOW(u_in->val2)); - break; - case Literal: - uInstr1(cb, SETV, u_in->size, - TempReg, SHADOW(u_in->val2)); - break; - default: - VG_(tool_panic)("memcheck_instrument: MOV"); - } - VG_(copy_UInstr)(cb, u_in); - break; +static +IRAtom* expr2vbits_Binop ( MCEnv* mce, + IROp op, + IRAtom* atom1, IRAtom* atom2 ) +{ + IRType and_or_ty; + IRAtom* (*uifu) (MCEnv*, IRAtom*, IRAtom*); + IRAtom* (*difd) (MCEnv*, IRAtom*, IRAtom*); + IRAtom* (*improve) (MCEnv*, IRAtom*, IRAtom*); + + IRAtom* vatom1 = expr2vbits( mce, atom1 ); + IRAtom* vatom2 = expr2vbits( mce, atom2 ); + + tl_assert(isOriginalAtom(mce,atom1)); + tl_assert(isOriginalAtom(mce,atom2)); + tl_assert(isShadowAtom(mce,vatom1)); + tl_assert(isShadowAtom(mce,vatom2)); + tl_assert(sameKindedAtoms(atom1,vatom1)); + tl_assert(sameKindedAtoms(atom2,vatom2)); + switch (op) { + + case Iop_RoundF64: + case Iop_F64toI64: + /* First arg is I32 (rounding mode), second is F64 (data). */ + return mkLazy2(mce, Ity_I64, vatom1, vatom2); + + case Iop_PRemC3210F64: case Iop_PRem1C3210F64: + /* Takes two F64 args. */ + case Iop_F64toI32: + /* First arg is I32 (rounding mode), second is F64 (data). */ + return mkLazy2(mce, Ity_I32, vatom1, vatom2); + + case Iop_F64toI16: + /* First arg is I32 (rounding mode), second is F64 (data). */ + return mkLazy2(mce, Ity_I16, vatom1, vatom2); + + case Iop_ScaleF64: + case Iop_Yl2xF64: + case Iop_Yl2xp1F64: + case Iop_PRemF64: + case Iop_AtanF64: + case Iop_AddF64: + case Iop_DivF64: + case Iop_SubF64: + case Iop_MulF64: + return mkLazy2(mce, Ity_I64, vatom1, vatom2); + + case Iop_CmpF64: + return mkLazy2(mce, Ity_I32, vatom1, vatom2); + + /* non-FP after here */ + + case Iop_DivModU64to32: + case Iop_DivModS64to32: + return mkLazy2(mce, Ity_I64, vatom1, vatom2); + + case Iop_16HLto32: + return assignNew(mce, Ity_I32, + binop(Iop_16HLto32, vatom1, vatom2)); + case Iop_32HLto64: + return assignNew(mce, Ity_I64, + binop(Iop_32HLto64, vatom1, vatom2)); + + case Iop_MullS32: + case Iop_MullU32: { + IRAtom* vLo32 = mkLeft32(mce, mkUifU32(mce, vatom1,vatom2)); + IRAtom* vHi32 = mkPCastTo(mce, Ity_I32, vLo32); + return assignNew(mce, Ity_I64, binop(Iop_32HLto64, vHi32, vLo32)); + } - /* Special case of add, where one of the operands is a literal. - lea1(t) = t + some literal. - Therefore: lea1#(qa) = left(qa) - */ - case LEA1: - tl_assert(u_in->size == 4 && !VG_(any_flag_use)(u_in)); - qs = SHADOW(u_in->val1); - qd = SHADOW(u_in->val2); - uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qd); - create_Left(cb, u_in->size, qd); - VG_(copy_UInstr)(cb, u_in); - break; + case Iop_MullS16: + case Iop_MullU16: { + IRAtom* vLo16 = mkLeft16(mce, mkUifU16(mce, vatom1,vatom2)); + IRAtom* vHi16 = mkPCastTo(mce, Ity_I16, vLo16); + return assignNew(mce, Ity_I32, binop(Iop_16HLto32, vHi16, vLo16)); + } - /* Another form of add. - lea2(ts,tt,shift) = ts + (tt << shift); shift is a literal - and is 0,1,2 or 3. - lea2#(qs,qt) = left(qs `UifU` (qt << shift)). - Note, subtly, that the shift puts zeroes at the bottom of qt, - meaning Valid, since the corresponding shift of tt puts - zeroes at the bottom of tb. - */ - case LEA2: { - Int shift; - tl_assert(u_in->size == 4 && !VG_(any_flag_use)(u_in)); - switch (u_in->extra4b) { - case 1: shift = 0; break; - case 2: shift = 1; break; - case 4: shift = 2; break; - case 8: shift = 3; break; - default: VG_(tool_panic)( "memcheck_instrument(LEA2)" ); - } - qs = SHADOW(u_in->val1); - qt = SHADOW(u_in->val2); - qd = SHADOW(u_in->val3); - uInstr2(cb, MOV, 4, TempReg, qt, TempReg, qd); - if (shift > 0) { - uInstr2(cb, SHL, 4, Literal, 0, TempReg, qd); - uLiteral(cb, shift); - } - create_UifU(cb, 4, qs, qd); - create_Left(cb, u_in->size, qd); - VG_(copy_UInstr)(cb, u_in); - break; - } + case Iop_MullS8: + case Iop_MullU8: { + IRAtom* vLo8 = mkLeft8(mce, mkUifU8(mce, vatom1,vatom2)); + IRAtom* vHi8 = mkPCastTo(mce, Ity_I8, vLo8); + return assignNew(mce, Ity_I16, binop(Iop_8HLto16, vHi8, vLo8)); + } - /* inc#/dec#(qd) = q `UifU` left(qd) = left(qd) */ - case INC: case DEC: - qd = SHADOW(u_in->val1); - create_Left(cb, u_in->size, qd); - if (u_in->flags_w != FlagsEmpty) - create_PUTVF(cb, u_in->size, qd); - VG_(copy_UInstr)(cb, u_in); - break; + case Iop_Add32: +# if 0 + return expensiveAdd32(mce, vatom1,vatom2, atom1,atom2); +# endif + case Iop_Sub32: + case Iop_Mul32: + return mkLeft32(mce, mkUifU32(mce, vatom1,vatom2)); + + case Iop_Mul16: + case Iop_Add16: + case Iop_Sub16: + return mkLeft16(mce, mkUifU16(mce, vatom1,vatom2)); + + case Iop_Sub8: + case Iop_Add8: + return mkLeft8(mce, mkUifU8(mce, vatom1,vatom2)); + + case Iop_CmpLE32S: case Iop_CmpLE32U: + case Iop_CmpLT32U: case Iop_CmpLT32S: + case Iop_CmpEQ32: case Iop_CmpNE32: + return mkPCastTo(mce, Ity_I1, mkUifU32(mce, vatom1,vatom2)); + + case Iop_CmpEQ16: case Iop_CmpNE16: + return mkPCastTo(mce, Ity_I1, mkUifU16(mce, vatom1,vatom2)); + + case Iop_CmpEQ8: case Iop_CmpNE8: + return mkPCastTo(mce, Ity_I1, mkUifU8(mce, vatom1,vatom2)); + + case Iop_Shl32: case Iop_Shr32: case Iop_Sar32: + /* Complain if the shift amount is undefined. Then simply + shift the first arg's V bits by the real shift amount. */ + complainIfUndefined(mce, atom2); + return assignNew(mce, Ity_I32, binop(op, vatom1, atom2)); + + case Iop_Shl16: case Iop_Shr16: + /* Same scheme as with 32-bit shifts. */ + complainIfUndefined(mce, atom2); + return assignNew(mce, Ity_I16, binop(op, vatom1, atom2)); + + case Iop_Shl8: case Iop_Shr8: + /* Same scheme as with 32-bit shifts. */ + complainIfUndefined(mce, atom2); + return assignNew(mce, Ity_I8, binop(op, vatom1, atom2)); + + case Iop_Shl64: case Iop_Shr64: + /* Same scheme as with 32-bit shifts. */ + complainIfUndefined(mce, atom2); + return assignNew(mce, Ity_I64, binop(op, vatom1, atom2)); + + case Iop_And32: + uifu = mkUifU32; difd = mkDifD32; + and_or_ty = Ity_I32; improve = mkImproveAND32; goto do_And_Or; + case Iop_And16: + uifu = mkUifU16; difd = mkDifD16; + and_or_ty = Ity_I16; improve = mkImproveAND16; goto do_And_Or; + case Iop_And8: + uifu = mkUifU8; difd = mkDifD8; + and_or_ty = Ity_I8; improve = mkImproveAND8; goto do_And_Or; + + case Iop_Or32: + uifu = mkUifU32; difd = mkDifD32; + and_or_ty = Ity_I32; improve = mkImproveOR32; goto do_And_Or; + case Iop_Or16: + uifu = mkUifU16; difd = mkDifD16; + and_or_ty = Ity_I16; improve = mkImproveOR16; goto do_And_Or; + case Iop_Or8: + uifu = mkUifU8; difd = mkDifD8; + and_or_ty = Ity_I8; improve = mkImproveOR8; goto do_And_Or; + + do_And_Or: + return + assignNew( + mce, + and_or_ty, + difd(mce, uifu(mce, vatom1, vatom2), + difd(mce, improve(mce, atom1, vatom1), + improve(mce, atom2, vatom2) ) ) ); + + case Iop_Xor8: + return mkUifU8(mce, vatom1, vatom2); + case Iop_Xor16: + return mkUifU16(mce, vatom1, vatom2); + case Iop_Xor32: + return mkUifU32(mce, vatom1, vatom2); - /* This is a HACK (approximation :-) */ - /* rcl#/rcr#(qs,qd) - = let q0 = pcast-sz-0(qd) `UifU` pcast-sz-0(qs) `UifU` eflags# - eflags# = q0 - qd =pcast-0-sz(q0) - Ie, cast everything down to a single bit, then back up. - This assumes that any bad bits infect the whole word and - the eflags. - */ - case RCL: case RCR: - tl_assert(u_in->flags_r != FlagsEmpty); - /* The following assertion looks like it makes sense, but is - actually wrong. Consider this: - rcll %eax - imull %eax, %eax - The rcll writes O and C but so does the imull, so the O and C - write of the rcll is annulled by the prior improvement pass. - Noticed by Kevin Ryde - */ - /* tl_assert(u_in->flags_w != FlagsEmpty); */ - qs = getOperandShadow(cb, 1, u_in->tag1, u_in->val1); - /* We can safely modify qs; cast it to 0-size. */ - create_PCast(cb, 1, 0, qs); - qd = SHADOW(u_in->val2); - create_PCast(cb, u_in->size, 0, qd); - /* qs is cast-to-0(shift count#), and qd is cast-to-0(value#). */ - create_UifU(cb, 0, qs, qd); - /* qs is now free; reuse it for the flag definedness. */ - qs = create_GETVF(cb, 0); - create_UifU(cb, 0, qs, qd); - create_PUTVF(cb, 0, qd); - create_PCast(cb, 0, u_in->size, qd); - VG_(copy_UInstr)(cb, u_in); - break; + default: + ppIROp(op); + VG_(tool_panic)("memcheck:expr2vbits_Binop"); + } +} - /* for OP in shl shr sar rol ror - (qs is shift count#, qd is value to be OP#d) - OP(ts,td) - OP#(qs,qd) - = pcast-1-sz(qs) `UifU` OP(ts,qd) - So we apply OP to the tag bits too, and then UifU with - the shift count# to take account of the possibility of it - being undefined. - - A bit subtle: - ROL/ROR rearrange the tag bits as per the value bits. - SHL/SHR shifts zeroes into the value, and corresponding - zeroes indicating Definedness into the tag. - SAR copies the top bit of the value downwards, and therefore - SAR also copies the definedness of the top bit too. - So in all five cases, we just apply the same op to the tag - bits as is applied to the value bits. Neat! - */ - case SHL: - case SHR: case SAR: - case ROL: case ROR: { - Int t_amount = INVALID_TEMPREG; - tl_assert(u_in->tag1 == TempReg || u_in->tag1 == Literal); - tl_assert(u_in->tag2 == TempReg); - qd = SHADOW(u_in->val2); - - /* Make qs hold shift-count# and make - t_amount be a TempReg holding the shift count. */ - if (u_in->tag1 == Literal) { - t_amount = newTemp(cb); - uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_amount); - uLiteral(cb, u_in->lit32); - qs = SHADOW(t_amount); - uInstr1(cb, SETV, 1, TempReg, qs); - } else { - t_amount = u_in->val1; - qs = SHADOW(u_in->val1); - } - - uInstr2(cb, u_in->opcode, - u_in->size, - TempReg, t_amount, - TempReg, qd); - qt = newShadow(cb); - uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qt); - create_PCast(cb, 1, u_in->size, qt); - create_UifU(cb, u_in->size, qt, qd); - VG_(copy_UInstr)(cb, u_in); - break; - } - /* One simple tag operation. */ - case WIDEN: - tl_assert(u_in->tag1 == TempReg); - create_Widen(cb, u_in->signed_widen, u_in->extra4b, u_in->size, - SHADOW(u_in->val1)); - VG_(copy_UInstr)(cb, u_in); - break; +static +IRExpr* expr2vbits_Unop ( MCEnv* mce, IROp op, IRAtom* atom ) +{ + IRAtom* vatom = expr2vbits( mce, atom ); + tl_assert(isOriginalAtom(mce,atom)); + switch (op) { + + case Iop_F32toF64: + case Iop_I32toF64: + case Iop_I64toF64: + case Iop_NegF64: + case Iop_SinF64: + case Iop_CosF64: + case Iop_TanF64: + case Iop_SqrtF64: + case Iop_AbsF64: + case Iop_2xm1F64: + return mkPCastTo(mce, Ity_I64, vatom); + + case Iop_F64toF32: + case Iop_Clz32: + case Iop_Ctz32: + return mkPCastTo(mce, Ity_I32, vatom); + + case Iop_32Sto64: + case Iop_32Uto64: + return assignNew(mce, Ity_I64, unop(op, vatom)); + + case Iop_64to32: + case Iop_64HIto32: + case Iop_1Uto32: + case Iop_8Uto32: + case Iop_16Uto32: + case Iop_16Sto32: + case Iop_8Sto32: + return assignNew(mce, Ity_I32, unop(op, vatom)); + + case Iop_8Sto16: + case Iop_8Uto16: + case Iop_32to16: + case Iop_32HIto16: + return assignNew(mce, Ity_I16, unop(op, vatom)); + + case Iop_1Uto8: + case Iop_16to8: + case Iop_32to8: + return assignNew(mce, Ity_I8, unop(op, vatom)); + + case Iop_32to1: + return assignNew(mce, Ity_I1, unop(Iop_32to1, vatom)); + + case Iop_ReinterpF64asI64: + case Iop_ReinterpI64asF64: + case Iop_Not32: + case Iop_Not16: + case Iop_Not8: + case Iop_Not1: + return vatom; + default: + ppIROp(op); + VG_(tool_panic)("memcheck:expr2vbits_Unop"); + } +} - /* not#(x) = x (since bitwise independent) */ - case NOT: - tl_assert(u_in->tag1 == TempReg); - VG_(copy_UInstr)(cb, u_in); - break; - /* neg#(x) = left(x) (derivable from case for SUB) */ - case NEG: - tl_assert(u_in->tag1 == TempReg); - create_Left(cb, u_in->size, SHADOW(u_in->val1)); - VG_(copy_UInstr)(cb, u_in); - break; +static +IRAtom* expr2vbits_LDle ( MCEnv* mce, IRType ty, IRAtom* addr, UInt bias ) +{ + void* helper; + Char* hname; + IRDirty* di; + IRTemp datavbits; + IRAtom* addrAct; + + tl_assert(isOriginalAtom(mce,addr)); + + /* First, emit a definedness test for the address. This also sets + the address (shadow) to 'defined' following the test. */ + complainIfUndefined( mce, addr ); + + /* Now cook up a call to the relevant helper function, to read the + data V bits from shadow memory. */ + ty = shadowType(ty); + switch (ty) { + case Ity_I64: helper = &MC_(helperc_LOADV8); + hname = "MC_(helperc_LOADV8)"; + break; + case Ity_I32: helper = &MC_(helperc_LOADV4); + hname = "MC_(helperc_LOADV4)"; + break; + case Ity_I16: helper = &MC_(helperc_LOADV2); + hname = "MC_(helperc_LOADV2)"; + break; + case Ity_I8: helper = &MC_(helperc_LOADV1); + hname = "MC_(helperc_LOADV1)"; + break; + default: ppIRType(ty); + VG_(tool_panic)("memcheck:do_shadow_LDle"); + } - /* bswap#(x) = bswap(x) */ - case BSWAP: - tl_assert(u_in->tag1 == TempReg); - tl_assert(u_in->size == 4); - qd = SHADOW(u_in->val1); - uInstr1(cb, BSWAP, 4, TempReg, qd); - VG_(copy_UInstr)(cb, u_in); - break; + /* Generate the actual address into addrAct. */ + if (bias == 0) { + addrAct = addr; + } else { + IRType tyAddr = mce->hWordTy; + tl_assert( tyAddr == Ity_I32 || tyAddr == Ity_I64 ); + IROp mkAdd = tyAddr==Ity_I32 ? Iop_Add32 : Iop_Add64; + IRAtom* eBias = tyAddr==Ity_I32 ? mkU32(bias) : mkU64(bias); + addrAct = assignNew(mce, tyAddr, binop(mkAdd, addr, eBias) ); + } - /* cc2val#(qd) = pcast-0-to-size(eflags#) */ - case CC2VAL: - tl_assert(u_in->tag1 == TempReg); - tl_assert(u_in->flags_r != FlagsEmpty); - qt = create_GETVF(cb, u_in->size); - uInstr2(cb, MOV, 4, TempReg, qt, TempReg, SHADOW(u_in->val1)); - VG_(copy_UInstr)(cb, u_in); - break; + /* We need to have a place to park the V bits we're just about to + read. */ + datavbits = newIRTemp(mce->bb->tyenv, ty); + di = unsafeIRDirty_1_N( datavbits, + 1/*regparms*/, hname, helper, + mkIRExprVec_1( addrAct )); + setHelperAnns( mce, di ); + stmt( mce->bb, IRStmt_Dirty(di) ); - /* cmov#(qs,qd) = cmov(qs,qd) - That is, do the cmov of tags using the same flags as for - the data (obviously). However, first do a test on the - validity of the flags. - */ - case CMOV: - tl_assert(u_in->size == 4); - tl_assert(u_in->tag1 == TempReg); - tl_assert(u_in->tag2 == TempReg); - tl_assert(u_in->flags_r != FlagsEmpty); - tl_assert(u_in->flags_w == FlagsEmpty); - qs = SHADOW(u_in->val1); - qd = SHADOW(u_in->val2); - qt = create_GETVF(cb, 0); - uInstr1(cb, TESTV, 0, TempReg, qt); - /* qt should never be referred to again. Nevertheless - ... */ - uInstr1(cb, SETV, 0, TempReg, qt); - - uInstr2(cb, CMOV, 4, TempReg, qs, TempReg, qd); - uCond(cb, u_in->cond); - uFlagsRWU(cb, u_in->flags_r, u_in->flags_w, FlagsEmpty); - - VG_(copy_UInstr)(cb, u_in); - break; + return mkexpr(datavbits); +} - /* add#/sub#(qs,qd) - = qs `UifU` qd `UifU` left(qs) `UifU` left(qd) - = left(qs) `UifU` left(qd) - = left(qs `UifU` qd) - adc#/sbb#(qs,qd) - = left(qs `UifU` qd) `UifU` pcast(eflags#) - Second arg (dest) is TempReg. - First arg (src) is Literal or TempReg or ArchReg. - */ - case ADD: case SUB: - case ADC: case SBB: - case MUL: - qd = SHADOW(u_in->val2); - qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1); - create_UifU(cb, u_in->size, qs, qd); - create_Left(cb, u_in->size, qd); - if (u_in->opcode == ADC || u_in->opcode == SBB) { - tl_assert(u_in->flags_r != FlagsEmpty); - qt = create_GETVF(cb, u_in->size); - create_UifU(cb, u_in->size, qt, qd); - } - if (u_in->flags_w != FlagsEmpty) { - create_PUTVF(cb, u_in->size, qd); - } - VG_(copy_UInstr)(cb, u_in); - break; - /* xor#(qs,qd) = qs `UifU` qd */ - case XOR: - qd = SHADOW(u_in->val2); - qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1); - create_UifU(cb, u_in->size, qs, qd); - if (u_in->flags_w != FlagsEmpty) { - create_PUTVF(cb, u_in->size, qd); - } - VG_(copy_UInstr)(cb, u_in); - break; +static +IRAtom* expr2vbits_Mux0X ( MCEnv* mce, + IRAtom* cond, IRAtom* expr0, IRAtom* exprX ) +{ + IRAtom *vbitsC, *vbits0, *vbitsX; + IRType ty; + /* Given Mux0X(cond,expr0,exprX), generate + Mux0X(cond,expr0#,exprX#) `UifU` PCast(cond#) + That is, steer the V bits like the originals, but trash the + result if the steering value is undefined. This gives + lazy propagation. */ + tl_assert(isOriginalAtom(mce, cond)); + tl_assert(isOriginalAtom(mce, expr0)); + tl_assert(isOriginalAtom(mce, exprX)); + + vbitsC = expr2vbits(mce, cond); + vbits0 = expr2vbits(mce, expr0); + vbitsX = expr2vbits(mce, exprX); + ty = typeOfIRExpr(mce->bb->tyenv, vbits0); + + return + mkUifU(mce, ty, assignNew(mce, ty, IRExpr_Mux0X(cond, vbits0, vbitsX)), + mkPCastTo(mce, ty, vbitsC) ); +} + +/* --------- This is the main expression-handling function. --------- */ - /* and#/or#(qs,qd) - = (qs `UifU` qd) `DifD` improve(vs,qs) - `DifD` improve(vd,qd) - where improve is the relevant one of - Improve{AND,OR}_TQ - Use the following steps, with qt as a temp: - qt = improve(vd,qd) - qd = qs `UifU` qd - qd = qt `DifD` qd - qt = improve(vs,qs) - qd = qt `DifD` qd - */ - case AND: case OR: - tl_assert(u_in->tag1 == TempReg); - tl_assert(u_in->tag2 == TempReg); - qd = SHADOW(u_in->val2); - qs = SHADOW(u_in->val1); - qt = newShadow(cb); - - /* qt = improve(vd,qd) */ - uInstr2(cb, MOV, 4, TempReg, qd, TempReg, qt); - if (u_in->opcode == AND) - create_ImproveAND_TQ(cb, u_in->size, u_in->val2, qt); - else - create_ImproveOR_TQ(cb, u_in->size, u_in->val2, qt); - /* qd = qs `UifU` qd */ - create_UifU(cb, u_in->size, qs, qd); - /* qd = qt `DifD` qd */ - create_DifD(cb, u_in->size, qt, qd); - /* qt = improve(vs,qs) */ - uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qt); - if (u_in->opcode == AND) - create_ImproveAND_TQ(cb, u_in->size, u_in->val1, qt); - else - create_ImproveOR_TQ(cb, u_in->size, u_in->val1, qt); - /* qd = qt `DifD` qd */ - create_DifD(cb, u_in->size, qt, qd); - /* So, finally qd is the result tag. */ - if (u_in->flags_w != FlagsEmpty) { - create_PUTVF(cb, u_in->size, qd); - } - VG_(copy_UInstr)(cb, u_in); - break; +static +IRExpr* expr2vbits ( MCEnv* mce, IRExpr* e ) +{ + switch (e->tag) { - /* Machinery to do with supporting CALLM. Copy the start and - end markers only to make the result easier to read - (debug); they generate no code and have no effect. - */ - case CALLM_S: case CALLM_E: - VG_(copy_UInstr)(cb, u_in); - break; + case Iex_Get: + return shadow_GET( mce, e->Iex.Get.offset, e->Iex.Get.ty ); - /* Copy PUSH and POP verbatim. Arg/result absval - calculations are done when the associated CALL is - processed. CLEAR has no effect on absval calculations but - needs to be copied. - */ - case PUSH: case POP: case CLEAR: - VG_(copy_UInstr)(cb, u_in); - break; + case Iex_GetI: + return shadow_GETI( mce, e->Iex.GetI.descr, + e->Iex.GetI.ix, e->Iex.GetI.bias ); - /* In short: - callm#(a1# ... an#) = (a1# `UifU` ... `UifU` an#) - We have to decide on a size to do the computation at, - although the choice doesn't affect correctness. We will - do a pcast to the final size anyway, so the only important - factor is to choose a size which minimises the total - number of casts needed. Valgrind: just use size 0, - regardless. It may not be very good for performance - but does simplify matters, mainly by reducing the number - of different pessimising casts which have to be implemented. - */ - case CALLM: { - UInstr* uu; - Bool res_used; - - /* Now generate the code. Get the final result absval - into qt. */ - qt = newShadow(cb); - qtt = newShadow(cb); - uInstr1(cb, SETV, 0, TempReg, qt); - for (j = i-1; VG_(get_instr)(cb_in, j)->opcode != CALLM_S; j--) { - uu = VG_(get_instr)(cb_in, j); - if (uu->opcode != PUSH) continue; - /* cast via a temporary */ - uInstr2(cb, MOV, 4, TempReg, SHADOW(uu->val1), - TempReg, qtt); - create_PCast(cb, uu->size, 0, qtt); - create_UifU(cb, 0, qtt, qt); - } - /* Remembering also that flags read count as inputs. */ - if (u_in->flags_r != FlagsEmpty) { - qtt = create_GETVF(cb, 0); - create_UifU(cb, 0, qtt, qt); - } - - /* qt now holds the result tag. If any results from the - call are used, either by fetching with POP or - implicitly by writing the flags, we copy the result - absval to the relevant location. If not used, the call - must have been for its side effects, so we test qt here - and now. Note that this assumes that all values - removed by POP continue to be live. So dead args - *must* be removed with CLEAR, not by POPping them into - a dummy tempreg. - */ - res_used = False; - for (j = i+1; VG_(get_instr)(cb_in, j)->opcode != CALLM_E; j++) { - uu = VG_(get_instr)(cb_in, j); - if (uu->opcode != POP) continue; - /* Cast via a temp. */ - uInstr2(cb, MOV, 4, TempReg, qt, TempReg, qtt); - create_PCast(cb, 0, uu->size, qtt); - uInstr2(cb, MOV, 4, TempReg, qtt, - TempReg, SHADOW(uu->val1)); - res_used = True; - } - if (u_in->flags_w != FlagsEmpty) { - create_PUTVF(cb, 0, qt); - res_used = True; - } - if (!res_used) { - uInstr1(cb, TESTV, 0, TempReg, qt); - /* qt should never be referred to again. Nevertheless - ... */ - uInstr1(cb, SETV, 0, TempReg, qt); - } - VG_(copy_UInstr)(cb, u_in); - break; - } - /* Whew ... */ - - case JMP: - if (u_in->tag1 == TempReg) { - uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1)); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1)); - } else { - tl_assert(u_in->tag1 == Literal); - } - if (u_in->cond != CondAlways) { - tl_assert(u_in->flags_r != FlagsEmpty); - qt = create_GETVF(cb, 0); - if (/* HACK */ bogusLiterals) { - if (0) - VG_(printf)("ignore TESTV due to bogus literal\n"); - } else { - uInstr1(cb, TESTV, 0, TempReg, qt); - } - /* qt should never be referred to again. Nevertheless - ... */ - uInstr1(cb, SETV, 0, TempReg, qt); - } - VG_(copy_UInstr)(cb, u_in); - break; + case Iex_Tmp: + return IRExpr_Tmp( findShadowTmp(mce, e->Iex.Tmp.tmp) ); - case JIFZ: - uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1)); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1)); - VG_(copy_UInstr)(cb, u_in); - break; + case Iex_Const: + return definedOfType(shadowType(typeOfIRExpr(mce->bb->tyenv, e))); - /* Emit a check on the address used. The value loaded into the - FPU is checked by the call to fpu_{read/write}_check(). */ - case MMX2_MemRd: case MMX2_MemWr: - case FPU_R: case FPU_W: { - Int t_size = INVALID_TEMPREG; - Bool is_load; - - if (u_in->opcode == MMX2_MemRd || u_in->opcode == MMX2_MemWr) - tl_assert(u_in->size == 4 || u_in->size == 8); - - is_load = u_in->opcode==FPU_R || u_in->opcode==MMX2_MemRd; - tl_assert(u_in->tag2 == TempReg); - uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val2)); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); - - t_size = newTemp(cb); - uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); - uLiteral(cb, u_in->size); - uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, t_size); - uCCall(cb, is_load ? (Addr) & MC_(fpu_read_check) - : (Addr) & MC_(fpu_write_check), - 2, 2, False); - - VG_(copy_UInstr)(cb, u_in); - break; - } + case Iex_Binop: + return expr2vbits_Binop( + mce, + e->Iex.Binop.op, + e->Iex.Binop.arg1, e->Iex.Binop.arg2 + ); - case MMX2a1_MemRd: { - Int t_size = INVALID_TEMPREG; + case Iex_Unop: + return expr2vbits_Unop( mce, e->Iex.Unop.op, e->Iex.Unop.arg ); - tl_assert(u_in->size == 8); + case Iex_LDle: + return expr2vbits_LDle( mce, e->Iex.LDle.ty, + e->Iex.LDle.addr, 0/*addr bias*/ ); - tl_assert(u_in->tag3 == TempReg); - uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val3)); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val3)); + case Iex_CCall: + return mkLazyN( mce, e->Iex.CCall.args, + e->Iex.CCall.retty, + e->Iex.CCall.cee ); - t_size = newTemp(cb); - uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); - uLiteral(cb, u_in->size); - uInstr2(cb, CCALL, 0, TempReg, u_in->val3, TempReg, t_size); - uCCall(cb, (Addr) & MC_(fpu_read_check), 2, 2, False); - - VG_(copy_UInstr)(cb, u_in); - break; - } + case Iex_Mux0X: + return expr2vbits_Mux0X( mce, e->Iex.Mux0X.cond, e->Iex.Mux0X.expr0, + e->Iex.Mux0X.exprX); - /* SSE ins referencing scalar integer registers */ - case SSE2g_RegWr: - case SSE2g1_RegWr: - case SSE2e1_RegRd: - case SSE3g_RegWr: - case SSE3e_RegRd: - case SSE3e_RegWr: - case SSE3g1_RegWr: - case SSE3e1_RegRd: - tl_assert(u_in->tag3 == TempReg); - - if (u_in->opcode == SSE2e1_RegRd || u_in->opcode == SSE3e1_RegRd) { - tl_assert(u_in->size == 2); - } else { - tl_assert(u_in->size == 4); - } - - /* Is it a read ? Better check the V bits right now. */ - if ( u_in->opcode == SSE2e1_RegRd - || u_in->opcode == SSE3e_RegRd - || u_in->opcode == SSE3e1_RegRd ) - uInstr1(cb, TESTV, u_in->size, - TempReg, SHADOW(u_in->val3)); - - /* And for both read and write, set the register to be - defined. */ - uInstr1(cb, SETV, u_in->size, - TempReg, SHADOW(u_in->val3)); - - VG_(copy_UInstr)(cb, u_in); - break; + default: + VG_(printf)("\n"); + ppIRExpr(e); + VG_(printf)("\n"); + VG_(tool_panic)("memcheck: expr2vbits"); + } +} - /* ... and the same deal for SSE insns referencing memory */ - case SSE3a_MemRd: - case SSE3a_MemWr: - case SSE2a_MemWr: - case SSE2a_MemRd: - case SSE3a1_MemRd: - case SSE2a1_MemRd: { - Bool is_load; - Int t_size; - - tl_assert(u_in->size == 4 || u_in->size == 8 - || u_in->size == 16 || u_in->size == 512); - - t_size = INVALID_TEMPREG; - is_load = u_in->opcode==SSE2a_MemRd - || u_in->opcode==SSE3a_MemRd - || u_in->opcode==SSE2a1_MemRd - || u_in->opcode==SSE3a1_MemRd; - - tl_assert(u_in->tag3 == TempReg); - - uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val3)); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val3)); - t_size = newTemp(cb); - uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); - uLiteral(cb, u_in->size); - uInstr2(cb, CCALL, 0, TempReg, u_in->val3, TempReg, t_size); - uCCall(cb, is_load ? (Addr) & MC_(fpu_read_check) - : (Addr) & MC_(fpu_write_check), - 2, 2, False); - - VG_(copy_UInstr)(cb, u_in); - break; - } +/*------------------------------------------------------------*/ +/*--- Generate shadow stmts from all kinds of IRStmts. ---*/ +/*------------------------------------------------------------*/ - case SSE3ag_MemRd_RegWr: - { - Int t_size; - - tl_assert(u_in->size == 4 || u_in->size == 8); - tl_assert(u_in->tag1 == TempReg); - uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1)); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1)); - t_size = newTemp(cb); - uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); - uLiteral(cb, u_in->size); - uInstr2(cb, CCALL, 0, TempReg, u_in->val1, TempReg, t_size); - uCCall(cb, (Addr) MC_(fpu_read_check), 2, 2, False ); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); - VG_(copy_UInstr)(cb, u_in); - break; - } - - /* For MMX and SSE insns not referencing memory, just - make sure the eflags are defined if the instruction - read them, and make them defined it it writes them. */ - case SSE5: case SSE4: case SSE3: - case MMX1: case MMX2: case MMX3: - case FPU: - if (u_in->flags_r != FlagsEmpty) { - qt = create_GETVF(cb, 0); - uInstr1(cb, TESTV, 0, TempReg, qt); - /* qt should never be referred to again. Nevertheless - ... */ - uInstr1(cb, SETV, 0, TempReg, qt); - } - if (u_in->flags_w != FlagsEmpty) { - qd = newTemp(cb); - uInstr2(cb, MOV, 4, Literal, 0, TempReg, qd); - uLiteral(cb, 0); - create_PUTVF(cb, 0, qd); - } - VG_(copy_UInstr)(cb, u_in); - break; +/* Widen a value to the host word size. */ - /* Since we don't track definedness of values inside the - MMX state, we'd better check that the (int) reg being - read here is defined. */ - case MMX2_ERegRd: - tl_assert(u_in->tag2 == TempReg); - tl_assert(u_in->size == 4); - uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val2)); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); - VG_(copy_UInstr)(cb, u_in); - break; +static +IRExpr* zwidenToHostWord ( MCEnv* mce, IRAtom* vatom ) +{ + /* vatom is vbits-value and as such can only have a shadow type. */ + tl_assert(isShadowAtom(mce,vatom)); + + IRType ty = typeOfIRExpr(mce->bb->tyenv, vatom); + IRType tyH = mce->hWordTy; + + if (tyH == Ity_I32) { + switch (ty) { + case Ity_I32: return vatom; + case Ity_I16: return assignNew(mce, tyH, unop(Iop_16Uto32, vatom)); + case Ity_I8: return assignNew(mce, tyH, unop(Iop_8Uto32, vatom)); + default: goto unhandled; + } + } else { + goto unhandled; + } + unhandled: + VG_(printf)("\nty = "); ppIRType(ty); VG_(printf)("\n"); + VG_(tool_panic)("zwidenToHostWord"); +} - /* The MMX register is assumed to be fully defined, so - that's what this register becomes. */ - case MMX2_ERegWr: - tl_assert(u_in->tag2 == TempReg); - tl_assert(u_in->size == 4); - uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); - VG_(copy_UInstr)(cb, u_in); - break; - default: - VG_(pp_UInstr)(0, u_in); - VG_(tool_panic)( "memcheck_instrument: unhandled case"); +/* 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. */ - } /* end of switch (u_in->opcode) */ +static +void do_shadow_STle ( MCEnv* mce, + IRAtom* addr, UInt bias, + IRAtom* data, IRAtom* vdata ) +{ + IRType ty; + IRDirty* di; + void* helper = NULL; + Char* hname = NULL; + IRAtom* addrAct; + + if (data) { + tl_assert(!vdata); + tl_assert(isOriginalAtom(mce, data)); + tl_assert(bias == 0); + vdata = expr2vbits( mce, data ); + } else { + tl_assert(vdata); + } - } /* end of for loop */ + tl_assert(isOriginalAtom(mce,addr)); + tl_assert(isShadowAtom(mce,vdata)); + + ty = typeOfIRExpr(mce->bb->tyenv, vdata); + + /* First, emit a definedness test for the address. This also sets + the address (shadow) to 'defined' following the test. */ + complainIfUndefined( mce, addr ); + + /* Now cook up a call to the relevant helper function, to write the + data V bits into shadow memory. */ + switch (ty) { + case Ity_I64: helper = &MC_(helperc_STOREV8); + hname = "MC_(helperc_STOREV8)"; + break; + case Ity_I32: helper = &MC_(helperc_STOREV4); + hname = "MC_(helperc_STOREV4)"; + break; + case Ity_I16: helper = &MC_(helperc_STOREV2); + hname = "MC_(helperc_STOREV2)"; + break; + case Ity_I8: helper = &MC_(helperc_STOREV1); + hname = "MC_(helperc_STOREV1)"; + break; + default: VG_(tool_panic)("memcheck:do_shadow_STle"); + } + + /* Generate the actual address into addrAct. */ + if (bias == 0) { + addrAct = addr; + } else { + IRType tyAddr = mce->hWordTy; + tl_assert( tyAddr == Ity_I32 || tyAddr == Ity_I64 ); + IROp mkAdd = tyAddr==Ity_I32 ? Iop_Add32 : Iop_Add64; + IRAtom* eBias = tyAddr==Ity_I32 ? mkU32(bias) : mkU64(bias); + addrAct = assignNew(mce, tyAddr, binop(mkAdd, addr, eBias) ); + } - VG_(free_UCodeBlock)(cb_in); - return cb; + if (ty == Ity_I64) { + /* We can't do this with regparm 2 on 32-bit platforms, since + the back ends aren't clever enough to handle 64-bit regparm + args. Therefore be different. */ + di = unsafeIRDirty_0_N( + 1/*regparms*/, hname, helper, + mkIRExprVec_2( addrAct, vdata )); + } else { + di = unsafeIRDirty_0_N( + 2/*regparms*/, hname, helper, + mkIRExprVec_2( addrAct, + zwidenToHostWord( mce, vdata ))); + } + setHelperAnns( mce, di ); + stmt( mce->bb, IRStmt_Dirty(di) ); } -/*------------------------------------------------------------*/ -/*--- Clean up mem check instrumentation. ---*/ -/*------------------------------------------------------------*/ -#define dis VG_(print_codegen) +/* Do lazy pessimistic propagation through a dirty helper call, by + looking at the annotations on it. This is the most complex part of + Memcheck. */ +static IRType szToITy ( Int n ) +{ + switch (n) { + case 1: return Ity_I8; + case 2: return Ity_I16; + case 4: return Ity_I32; + case 8: return Ity_I64; + default: VG_(tool_panic)("szToITy(memcheck)"); + } +} -#define VGC_IS_SHADOW(tempreg) ((tempreg % 2) == 1) -#define VGC_UNDEF ((UChar)100) -#define VGC_VALUE ((UChar)101) +static +void do_shadow_Dirty ( MCEnv* mce, IRDirty* d ) +{ + Int i, offset, toDo; + IRAtom* src; + IRType tyAddr, tySrc, tyDst; + IRTemp dst; + + /* First check the guard. */ + complainIfUndefined(mce, d->guard); + + /* Now round up all inputs and PCast over them. */ + IRAtom* here; + IRAtom* curr = definedOfType(Ity_I32); + + /* Inputs: unmasked args */ + for (i = 0; d->args[i]; i++) { + if (d->cee->mcx_mask & (1<args[i]) ); + curr = mkUifU32(mce, here, curr); + } + } -#define NOP_no_msg(uu) \ - do { VG_(new_NOP)(uu); } while (False) + /* 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; + /* This state element is read or modified. So we need to + consider it. */ + tySrc = szToITy( d->fxState[i].size ); + src = assignNew( mce, tySrc, + shadow_GET(mce, d->fxState[i].offset, tySrc ) ); + here = mkPCastTo( mce, Ity_I32, src ); + curr = mkUifU32(mce, here, curr); + } -#define NOP_tag1_op(uu) \ - do { VG_(new_NOP)(uu); \ - if (dis) \ - VG_(printf)(" at %2d: delete %s due to defd arg\n", \ - i, nameOfTagOp(u->val3)); \ - } while (False) + /* Inputs: memory. First set up some info needed regardless of + whether we're doing reads or writes. */ + tyAddr = Ity_INVALID; + + 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); + complainIfUndefined(mce, d->mAddr); + + tyAddr = typeOfIRExpr(mce->bb->tyenv, d->mAddr); + tl_assert(tyAddr == Ity_I32 || tyAddr == Ity_I64); + tl_assert(tyAddr == mce->hWordTy); /* not really right */ + } -#define SETV_tag1_op(uu,newsz) \ - do { uu->opcode = SETV; \ - uu->size = newsz; \ - uu->tag2 = uu->tag3 = NoValue; \ - if (dis) \ - VG_(printf)(" at %2d: convert %s to SETV%d " \ - "due to defd arg\n", \ - i, nameOfTagOp(u->val3), newsz); \ - } while (False) + /* Deal with memory inputs (reads or modifies) */ + if (d->mFx == Ifx_Read || d->mFx == Ifx_Modify) { + offset = 0; + toDo = d->mSize; + /* chew off 32-bit chunks */ + while (toDo >= 4) { + here = mkPCastTo( + mce, Ity_I32, + expr2vbits_LDle ( mce, Ity_I32, + d->mAddr, d->mSize - toDo ) + ); + curr = mkUifU32(mce, here, curr); + toDo -= 4; + } + /* chew off 16-bit chunks */ + while (toDo >= 2) { + here = mkPCastTo( + mce, Ity_I32, + expr2vbits_LDle ( mce, Ity_I16, + d->mAddr, d->mSize - toDo ) + ); + curr = mkUifU32(mce, here, curr); + toDo -= 2; + } + tl_assert(toDo == 0); /* also need to handle 1-byte excess */ + } + /* Whew! So curr is a 32-bit V-value summarising pessimistically + all the inputs to the helper. 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 = findShadowTmp(mce, d->tmp); + tyDst = typeOfIRTemp(mce->bb->tyenv, d->tmp); + assign( mce->bb, dst, mkPCastTo( mce, tyDst, curr) ); + } -/* Run backwards and delete SETVs on shadow temps for which the next - action is a write. Needs an env saying whether or not the next - action is a write. The supplied UCodeBlock is destructively - modified. -*/ -static void vg_delete_redundant_SETVs ( UCodeBlock* cb ) -{ - Int i, j, k; - Int n_temps = VG_(get_num_temps)(cb); - Bool* next_is_write; - UInstr* u; - Int tempUse[VG_MAX_REGS_USED]; - Bool isWrites[VG_MAX_REGS_USED]; - - if (n_temps == 0) return; - - next_is_write = VG_(malloc)(n_temps * sizeof(Bool)); - - for (i = 0; i < n_temps; i++) next_is_write[i] = True; - - for (i = VG_(get_num_instrs)(cb) - 1; i >= 0; i--) { - u = VG_(get_instr)(cb, i); - - /* Occasionally there will be GETVs, TAG1s and TAG2s calculating - values which are never used. These first three cases get rid - of them. */ - - if (u->opcode == GETV && VGC_IS_SHADOW(u->val2) - && next_is_write[u->val2]) { - tl_assert(u->val2 < n_temps); - VG_(new_NOP)(u); - if (dis) - VG_(printf)(" at %2d: delete GETV\n", i); - } else - - if (u->opcode == TAG1 && VGC_IS_SHADOW(u->val1) - && next_is_write[u->val1]) { - tl_assert(u->val1 < n_temps); - VG_(new_NOP)(u); - if (dis) - VG_(printf)(" at %2d: delete TAG1\n", i); - } else - - if (u->opcode == TAG2 && VGC_IS_SHADOW(u->val2) - && next_is_write[u->val2]) { - tl_assert(u->val2 < n_temps); - VG_(new_NOP)(u); - if (dis) - VG_(printf)(" at %2d: delete TAG2\n", i); - } else - - /* The bulk of the cleanup work of this function is done by - the code from here downwards. */ - - if (u->opcode == MOV && VGC_IS_SHADOW(u->val2) - && next_is_write[u->val2]) { - /* This MOV is pointless because the target is dead at this - point. Delete it. */ - VG_(new_NOP)(u); - if (dis) - VG_(printf)(" at %2d: delete MOV\n", i); - } else - - if (u->opcode == SETV) { - if (u->tag1 == TempReg) { - tl_assert(VGC_IS_SHADOW(u->val1)); - if (next_is_write[u->val1]) { - /* This write is pointless, so annul it. */ - VG_(new_NOP)(u); - if (dis) - VG_(printf)(" at %2d: delete SETV\n", i); - } else { - /* This write has a purpose; don't annul it, but do - notice that we did it. */ - next_is_write[u->val1] = True; - } - - } + /* 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; + /* this state element is written or modified. So we need to + consider it. */ + tyDst = szToITy( d->fxState[i].size ); + do_shadow_PUT( mce, d->fxState[i].offset, + NULL, /* original atom */ + mkPCastTo( mce, tyDst, curr ) ); + } - } else { - /* Find out what this insn does to the temps. */ - k = VG_(get_reg_usage)(u, TempReg, &tempUse[0], &isWrites[0]); - tl_assert(0 <= k && k <= VG_MAX_REGS_USED); - for (j = k-1; j >= 0; j--) { - next_is_write[ tempUse[j] ] = isWrites[j]; - } + /* Outputs: memory that we write or modify. */ + if (d->mFx == Ifx_Write || d->mFx == Ifx_Modify) { + offset = 0; + toDo = d->mSize; + /* chew off 32-bit chunks */ + while (toDo >= 4) { + do_shadow_STle( mce, d->mAddr, d->mSize - toDo, + NULL, /* original data */ + mkPCastTo( mce, Ity_I32, curr ) ); + toDo -= 4; + } + /* chew off 16-bit chunks */ + while (toDo >= 2) { + do_shadow_STle( mce, d->mAddr, d->mSize - toDo, + NULL, /* original data */ + mkPCastTo( mce, Ity_I16, curr ) ); + toDo -= 2; } + tl_assert(toDo == 0); /* also need to handle 1-byte excess */ } -} +} -/* Run forwards, propagating and using the is-completely-defined - property. This removes a lot of redundant tag-munging code. - Unfortunately it requires intimate knowledge of how each uinstr and - tagop modifies its arguments. This duplicates knowledge of uinstr - tempreg uses embodied in VG_(get_reg_usage)(), which is unfortunate. - The supplied UCodeBlock* is modified in-place. - For each value temp, def[] should hold VGC_VALUE. +/*------------------------------------------------------------*/ +/*--- Memcheck main ---*/ +/*------------------------------------------------------------*/ - For each shadow temp, def[] may hold 4,2,1 or 0 iff that shadow is - definitely known to be fully defined at that size. In all other - circumstances a shadow's def[] entry is VGC_UNDEF, meaning possibly - undefined. In cases of doubt, VGC_UNDEF is always safe. -*/ -static void vg_propagate_definedness ( UCodeBlock* cb ) +#if 0 /* UNUSED */ +static Bool isBogusAtom ( IRAtom* at ) { - Int i, j, k, t; - Int n_temps = VG_(get_num_temps)(cb); - UChar* def; - UInstr* u; - Int tempUse[VG_MAX_REGS_USED]; - Bool isWrites[VG_MAX_REGS_USED]; - - if (n_temps == 0) return; - - def = VG_(malloc)(n_temps * sizeof(UChar)); - - for (i = 0; i < n_temps; i++) - def[i] = VGC_IS_SHADOW(i) ? VGC_UNDEF : VGC_VALUE; - - /* Run forwards, detecting and using the all-defined property. */ + ULong n = 0; + IRConst* con; + tl_assert(isAtom(at)); + if (at->tag == Iex_Tmp) + return False; + tl_assert(at->tag == Iex_Const); + con = at->Iex.Const.con; + switch (con->tag) { + 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; + default: ppIRExpr(at); tl_assert(0); + } + /* VG_(printf)("%llx\n", n); */ + return (n == 0xFEFEFEFF + || n == 0x80808080 + || n == 0x1010101 + || n == 1010100); +} - for (i = 0; i < VG_(get_num_instrs)(cb); i++) { - u = VG_(get_instr)(cb, i); - switch (u->opcode) { +static Bool checkForBogusLiterals ( /*FLAT*/ IRStmt* st ) +{ + Int i; + IRExpr* e; + switch (st->tag) { + case Ist_Tmp: + e = st->Ist.Tmp.data; + switch (e->tag) { + case Iex_Get: + case Iex_Tmp: + return False; + case Iex_Unop: + return isBogusAtom(e->Iex.Unop.arg); + case Iex_Binop: + return isBogusAtom(e->Iex.Binop.arg1) + || isBogusAtom(e->Iex.Binop.arg2); + case Iex_Mux0X: + return isBogusAtom(e->Iex.Mux0X.cond) + || isBogusAtom(e->Iex.Mux0X.expr0) + || isBogusAtom(e->Iex.Mux0X.exprX); + case Iex_LDle: + return isBogusAtom(e->Iex.LDle.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_Put: + return isBogusAtom(st->Ist.Put.data); + case Ist_STle: + return isBogusAtom(st->Ist.STle.addr) + || isBogusAtom(st->Ist.STle.data); + case Ist_Exit: + return isBogusAtom(st->Ist.Exit.cond); + default: + unhandled: + ppIRStmt(st); + VG_(tool_panic)("hasBogusLiterals"); + } +} +#endif /* UNUSED */ - /* Tag-handling uinstrs. */ - /* Deal with these quickly. */ - case NOP: - case LOCK: - case INCEIP: - break; +IRBB* TL_(instrument) ( IRBB* bb_in, VexGuestLayout* layout, IRType hWordTy ) +{ + Bool verboze = False; //True; + + /* Bool hasBogusLiterals = False; */ + + Int i, j, first_stmt; + IRStmt* st; + MCEnv mce; + + /* Set up BB */ + IRBB* bb = emptyIRBB(); + bb->tyenv = dopyIRTypeEnv(bb_in->tyenv); + bb->next = dopyIRExpr(bb_in->next); + bb->jumpkind = bb_in->jumpkind; + + /* Set up the running environment. Only .bb is modified as we go + along. */ + mce.bb = bb; + mce.layout = layout; + mce.n_originalTmps = bb->tyenv->types_used; + mce.hWordTy = hWordTy; + mce.tmpMap = LibVEX_Alloc(mce.n_originalTmps * sizeof(IRTemp)); + for (i = 0; i < mce.n_originalTmps; i++) + mce.tmpMap[i] = IRTemp_INVALID; + + /* Iterate over the stmts. */ + + for (i = 0; i < bb_in->stmts_used; i++) { + st = bb_in->stmts[i]; + if (!st) continue; + + tl_assert(isFlatIRStmt(st)); + + /* + if (!hasBogusLiterals) { + hasBogusLiterals = checkForBogusLiterals(st); + if (hasBogusLiterals) { + VG_(printf)("bogus: "); + ppIRStmt(st); + VG_(printf)("\n"); + } + } + */ + first_stmt = bb->stmts_used; - /* Make a tag defined. */ - case SETV: - tl_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); - def[u->val1] = u->size; - break; + if (verboze) { + ppIRStmt(st); + VG_(printf)("\n\n"); + } - /* Check definedness of a tag. */ - case TESTV: - tl_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); - if (def[u->val1] <= 4) { - tl_assert(def[u->val1] == u->size); - NOP_no_msg(u); - if (dis) - VG_(printf)(" at %2d: delete TESTV on defd arg\n", i); - } - break; + switch (st->tag) { - /* Applies to both values and tags. Propagate Definedness - property through copies. Note that this isn't optional; - we *have* to do this to keep def[] correct. */ - case MOV: - tl_assert(u->tag2 == TempReg); - if (u->tag1 == TempReg) { - if (VGC_IS_SHADOW(u->val1)) { - tl_assert(VGC_IS_SHADOW(u->val2)); - def[u->val2] = def[u->val1]; - } - } + case Ist_Tmp: + assign( bb, findShadowTmp(&mce, st->Ist.Tmp.tmp), + expr2vbits( &mce, st->Ist.Tmp.data) ); break; - case PUTV: - tl_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); - if (def[u->val1] <= 4) { - tl_assert(def[u->val1] == u->size); - u->tag1 = Literal; - u->val1 = 0; - switch (u->size) { - case 4: u->lit32 = 0x00000000; break; - case 2: u->lit32 = 0xFFFF0000; break; - case 1: u->lit32 = 0xFFFFFF00; break; - default: VG_(tool_panic)("vg_cleanup(PUTV)"); - } - if (dis) - VG_(printf)( - " at %2d: propagate definedness into PUTV\n", i); - } + case Ist_Put: + do_shadow_PUT( &mce, + st->Ist.Put.offset, + st->Ist.Put.data, + NULL /* shadow atom */ ); break; - case STOREV: - tl_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); - if (def[u->val1] <= 4) { - tl_assert(def[u->val1] == u->size); - u->tag1 = Literal; - u->val1 = 0; - switch (u->size) { - case 4: u->lit32 = 0x00000000; break; - case 2: u->lit32 = 0xFFFF0000; break; - case 1: u->lit32 = 0xFFFFFF00; break; - default: VG_(tool_panic)("vg_cleanup(STOREV)"); - } - if (dis) - VG_(printf)( - " at %2d: propagate definedness into STandV\n", i); - } + case Ist_PutI: + do_shadow_PUTI( &mce, + st->Ist.PutI.descr, + st->Ist.PutI.ix, + st->Ist.PutI.bias, + st->Ist.PutI.data ); break; - /* Nothing interesting we can do with this, I think. */ - case PUTVF: + case Ist_STle: + do_shadow_STle( &mce, st->Ist.STle.addr, 0/* addr bias */, + st->Ist.STle.data, + NULL /* shadow data */ ); break; - /* Tag handling operations. */ - case TAG2: - tl_assert(u->tag2 == TempReg && VGC_IS_SHADOW(u->val2)); - tl_assert(u->tag3 == Lit16); - /* Ultra-paranoid "type" checking. */ - switch (u->val3) { - case Tag_ImproveAND4_TQ: case Tag_ImproveAND2_TQ: - case Tag_ImproveAND1_TQ: case Tag_ImproveOR4_TQ: - case Tag_ImproveOR2_TQ: case Tag_ImproveOR1_TQ: - tl_assert(u->tag1 == TempReg && !VGC_IS_SHADOW(u->val1)); - break; - default: - tl_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); - break; - } - switch (u->val3) { - Int sz; - case Tag_UifU4: - sz = 4; goto do_UifU; - case Tag_UifU2: - sz = 2; goto do_UifU; - case Tag_UifU1: - sz = 1; goto do_UifU; - case Tag_UifU0: - sz = 0; goto do_UifU; - do_UifU: - tl_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); - tl_assert(u->tag2 == TempReg && VGC_IS_SHADOW(u->val2)); - if (def[u->val1] <= 4) { - /* UifU. The first arg is defined, so result is - simply second arg. Delete this operation. */ - tl_assert(def[u->val1] == sz); - NOP_no_msg(u); - if (dis) - VG_(printf)( - " at %2d: delete UifU%d due to defd arg1\n", - i, sz); - } - else - if (def[u->val2] <= 4) { - /* UifU. The second arg is defined, so result is - simply first arg. Copy to second. */ - tl_assert(def[u->val2] == sz); - u->opcode = MOV; - u->size = 4; - u->tag3 = NoValue; - def[u->val2] = def[u->val1]; - if (dis) - VG_(printf)( - " at %2d: change UifU%d to MOV due to defd" - " arg2\n", - i, sz); - } - break; - case Tag_ImproveAND4_TQ: - sz = 4; goto do_ImproveAND; - case Tag_ImproveAND1_TQ: - sz = 1; goto do_ImproveAND; - do_ImproveAND: - /* Implements Q = T AND Q. So if Q is entirely defined, - ie all 0s, we get MOV T, Q. */ - if (def[u->val2] <= 4) { - tl_assert(def[u->val2] == sz); - u->size = 4; /* Regardless of sz */ - u->opcode = MOV; - u->tag3 = NoValue; - def[u->val2] = VGC_UNDEF; - if (dis) - VG_(printf)( - " at %2d: change ImproveAND%d_TQ to MOV due " - "to defd arg2\n", - i, sz); - } - break; - default: - goto unhandled; - } + case Ist_Exit: + /* if (!hasBogusLiterals) */ + complainIfUndefined( &mce, st->Ist.Exit.guard ); break; - case TAG1: - tl_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); - if (def[u->val1] > 4) break; - /* We now know that the arg to the op is entirely defined. - If the op changes the size of the arg, we must replace - it with a SETV at the new size. If it doesn't change - the size, we can delete it completely. */ - switch (u->val3) { - /* Maintain the same size ... */ - case Tag_Left4: - tl_assert(def[u->val1] == 4); - NOP_tag1_op(u); - break; - case Tag_PCast11: - tl_assert(def[u->val1] == 1); - NOP_tag1_op(u); - break; - /* Change size ... */ - case Tag_PCast40: - tl_assert(def[u->val1] == 4); - SETV_tag1_op(u,0); - def[u->val1] = 0; - break; - case Tag_PCast14: - tl_assert(def[u->val1] == 1); - SETV_tag1_op(u,4); - def[u->val1] = 4; - break; - case Tag_PCast12: - tl_assert(def[u->val1] == 1); - SETV_tag1_op(u,2); - def[u->val1] = 2; - break; - case Tag_PCast10: - tl_assert(def[u->val1] == 1); - SETV_tag1_op(u,0); - def[u->val1] = 0; - break; - case Tag_PCast02: - tl_assert(def[u->val1] == 0); - SETV_tag1_op(u,2); - def[u->val1] = 2; - break; - default: - goto unhandled; - } - if (dis) - VG_(printf)( - " at %2d: delete TAG1 %s due to defd arg\n", - i, nameOfTagOp(u->val3)); + case Ist_Dirty: + do_shadow_Dirty( &mce, st->Ist.Dirty.details ); break; default: - unhandled: - /* We don't know how to handle this uinstr. Be safe, and - set to VGC_VALUE or VGC_UNDEF all temps written by it. */ - k = VG_(get_reg_usage)(u, TempReg, &tempUse[0], &isWrites[0]); - tl_assert(0 <= k && k <= VG_MAX_REGS_USED); - for (j = 0; j < k; j++) { - t = tempUse[j]; - tl_assert(t >= 0 && t < n_temps); - if (!isWrites[j]) { - /* t is read; ignore it. */ - if (0&& VGC_IS_SHADOW(t) && def[t] <= 4) - VG_(printf)("ignoring def %d at %s %s\n", - def[t], - VG_(name_UOpcode)(True, u->opcode), - (u->opcode == TAG1 || u->opcode == TAG2) - ? nameOfTagOp(u->val3) - : (Char*)""); - } else { - /* t is written; better nullify it. */ - def[t] = VGC_IS_SHADOW(t) ? VGC_UNDEF : VGC_VALUE; - } - } + VG_(printf)("\n"); + ppIRStmt(st); + VG_(printf)("\n"); + VG_(tool_panic)("memcheck: unhandled IRStmt"); + + } /* switch (st->tag) */ + + if (verboze) { + for (j = first_stmt; j < bb->stmts_used; j++) { + VG_(printf)(" "); + ppIRStmt(bb->stmts[j]); + VG_(printf)("\n"); + } + VG_(printf)("\n"); } + + addStmtToIRBB(bb, st); + } -} + /* Now we need to complain if the jump target is undefined. */ + first_stmt = bb->stmts_used; -/* Top level post-MemCheck-instrumentation cleanup function. */ -static void vg_cleanup ( UCodeBlock* cb ) -{ - vg_propagate_definedness ( cb ); - vg_delete_redundant_SETVs ( cb ); -} + if (verboze) { + VG_(printf)("bb->next = "); + ppIRExpr(bb->next); + VG_(printf)("\n\n"); + } + complainIfUndefined( &mce, bb->next ); -/* Caller will print out final instrumented code if necessary; we - print out intermediate instrumented code here if necessary. */ -UCodeBlock* TL_(instrument) ( UCodeBlock* cb, Addr not_used ) -{ - cb = memcheck_instrument ( cb ); - if (MC_(clo_cleanup)) { - if (dis) { - VG_(pp_UCodeBlock) ( cb, "Unimproved instrumented UCode:" ); - VG_(printf)("Instrumentation improvements:\n"); + if (verboze) { + for (j = first_stmt; j < bb->stmts_used; j++) { + VG_(printf)(" "); + ppIRStmt(bb->stmts[j]); + VG_(printf)("\n"); } - vg_cleanup(cb); - if (dis) VG_(printf)("\n"); + VG_(printf)("\n"); } - return cb; -} -#undef dis + return bb; +} /*--------------------------------------------------------------------*/ /*--- end mc_translate.c ---*/