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<sz> %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 ---*/
/*--------------------------------------------------------------------*/
/*--------------------------------------------------------------------*/
-/*--- Instrument UCode to perform memory checking operations. ---*/
+/*--- Instrument IR to perform memory checking operations. ---*/
/*--- mc_translate.c ---*/
/*--------------------------------------------------------------------*/
#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<<i)) {
+ /* the arg is to be excluded from definedness checking. Do
+ nothing. */
+ if (0) VG_(printf)("excluding %s(%d)\n", cee->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 <user42@zip.com.au>
- */
- /* 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<<i)) {
+ /* ignore this arg */
+ } else {
+ here = mkPCastTo( mce, Ity_I32, expr2vbits(mce, d->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 ---*/