| |
| /*--------------------------------------------------------------------*/ |
| /*--- Instrument UCode to perform memory checking operations. ---*/ |
| /*--- mc_translate.c ---*/ |
| /*--------------------------------------------------------------------*/ |
| |
| /* |
| This file is part of MemCheck, a heavyweight Valgrind skin for |
| detecting memory errors. |
| |
| Copyright (C) 2000-2003 Julian Seward |
| jseward@acm.org |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 2 of the |
| License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| The GNU General Public License is contained in the file COPYING. |
| */ |
| |
| #include "mc_include.h" |
| |
| /* --------------------------------------------------------------------- |
| Template functions for extending UCode |
| ------------------------------------------------------------------ */ |
| |
| /* Compare this with the restrictions on core instructions in |
| vg_translate.c:VG_(saneUInstr)(). Everything general said there applies |
| here too. |
| */ |
| Bool SK_(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 TRA1 (TR1 || A1) |
| # 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; |
| |
| /* 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 && TRA1 && 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_(skin_panic)("SK_(sane_XUInstr): unhandled opcode"); |
| } |
| # 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 TRA1 |
| # undef N2 |
| # undef N3 |
| # undef COND0 |
| # undef EXTRA4b0 |
| # undef JMPKIND0 |
| # undef CCALL0 |
| # undef XOTHER |
| } |
| |
| static Char* nameOfTagOp ( TagOp h ) |
| { |
| 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_(skin_panic)("vg_nameOfTagOp"); |
| } |
| } |
| |
| |
| Char* SK_(name_XUOpcode)(Opcode opc) |
| { |
| 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_(skin_panic)("SK_(name_XUOpcode): unhandled case"); |
| } |
| } |
| |
| /* --------------------------------------------------------------------- |
| Debugging stuff. |
| ------------------------------------------------------------------ */ |
| |
| void SK_(pp_XUInstr)(UInstr* u) |
| { |
| switch (u->opcode) { |
| |
| case TAG1: |
| VG_(printf)("\t"); |
| VG_(pp_UOperand)(u, 1, 4, False); |
| VG_(printf)(" = %s ( ", nameOfTagOp( u->val3 )); |
| VG_(pp_UOperand)(u, 1, 4, False); |
| VG_(printf)(" )"); |
| break; |
| |
| case TAG2: |
| VG_(printf)("\t"); |
| 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; |
| |
| case STOREV: case LOADV: |
| VG_(printf)("\t"); |
| VG_(pp_UOperand)(u, 1, u->size, u->opcode==LOADV); |
| VG_(printf)(", "); |
| VG_(pp_UOperand)(u, 2, u->size, u->opcode==STOREV); |
| break; |
| |
| case PUTVF: case GETVF: |
| VG_(printf)("\t"); |
| VG_(pp_UOperand)(u, 1, 0, False); |
| break; |
| |
| case GETV: case PUTV: |
| VG_(printf)("\t"); |
| 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; |
| |
| case TESTV: case SETV: |
| VG_(printf)("\t"); |
| VG_(pp_UOperand)(u, 1, u->size, False); |
| break; |
| |
| default: |
| VG_(printf)("unhandled opcode: %u\n", u->opcode); |
| VG_(skin_panic)("SK_(pp_XUInstr): unhandled opcode"); |
| } |
| |
| } |
| |
| Int SK_(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_(skin_panic)("SK_(get_Xreg_usage): unhandled opcode"); |
| } |
| return n; |
| |
| # undef RD |
| # undef WR |
| } |
| |
| /*------------------------------------------------------------*/ |
| /*--- New instrumentation machinery. ---*/ |
| /*------------------------------------------------------------*/ |
| |
| 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_(skin_panic)("get_Tag_ImproveOR_TQ"); |
| } |
| } |
| |
| |
| 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_(skin_panic)("get_Tag_ImproveAND_TQ"); |
| } |
| } |
| |
| |
| 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_(skin_panic)("get_Tag_Left"); |
| } |
| } |
| |
| |
| 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_(skin_panic)("get_Tag_UifU"); |
| } |
| } |
| |
| |
| 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_(skin_panic)("get_Tag_DifD"); |
| } |
| } |
| |
| |
| 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_(skin_panic)("get_Tag_PCast"); |
| } |
| |
| |
| 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; |
| |
| if (szs == 2 && szd == 4 && syned) return Tag_SWiden24; |
| if (szs == 2 && szd == 4 && !syned) return Tag_ZWiden24; |
| |
| VG_(printf)("get_Tag_Widen(%d,%d,%d)\n", (Int)syned, szs, szd); |
| VG_(skin_panic)("get_Tag_Widen"); |
| } |
| |
| /* Pessimally cast the spec'd shadow from one size to another. */ |
| static |
| void create_PCast ( UCodeBlock* cb, Int szs, Int szd, Int tempreg ) |
| { |
| if (szs == 0 && szd == 0) |
| return; |
| uInstr3(cb, TAG1, 0, TempReg, tempreg, |
| NoValue, 0, |
| Lit16, get_Tag_PCast(szs,szd)); |
| } |
| |
| |
| /* 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 ) |
| { |
| if (szs == szd) return; |
| uInstr3(cb, TAG1, 0, TempReg, tempreg, |
| NoValue, 0, |
| Lit16, get_Tag_Widen(signed_widen,szs,szd)); |
| } |
| |
| |
| /* Get the condition codes into a new shadow, at the given size. */ |
| static |
| Int create_GETVF ( UCodeBlock* cb, Int sz ) |
| { |
| Int tt = newShadow(cb); |
| uInstr1(cb, GETVF, 0, TempReg, tt); |
| create_PCast(cb, 0, sz, tt); |
| return tt; |
| } |
| |
| |
| /* Save the condition codes from the spec'd shadow. */ |
| static |
| void create_PUTVF ( UCodeBlock* cb, Int sz, Int tempreg ) |
| { |
| 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); |
| } |
| } |
| |
| |
| /* 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)); |
| } |
| |
| |
| /* Do UifU on ts and td, putting the result in td. */ |
| static |
| void create_UifU ( UCodeBlock* cb, Int sz, Int ts, Int td ) |
| { |
| uInstr3(cb, TAG2, 0, TempReg, ts, TempReg, td, |
| Lit16, get_Tag_UifU(sz)); |
| } |
| |
| |
| /* 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)); |
| } |
| |
| |
| /* 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)); |
| } |
| |
| |
| /* 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 ) |
| { |
| uInstr3(cb, TAG2, 0, TempReg, tval, TempReg, tqqq, |
| Lit16, get_Tag_ImproveOR_TQ(sz)); |
| } |
| |
| |
| /* 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.*/ |
| static |
| Int /* TempReg */ getOperandShadow ( UCodeBlock* cb, |
| Int sz, Int tag, Int val ) |
| { |
| Int sh; |
| sh = newShadow(cb); |
| if (tag == TempReg) { |
| uInstr2(cb, MOV, 4, TempReg, SHADOW(val), TempReg, sh); |
| return sh; |
| } |
| if (tag == Literal) { |
| uInstr1(cb, SETV, sz, TempReg, sh); |
| return sh; |
| } |
| if (tag == ArchReg) { |
| uInstr2(cb, GETV, sz, ArchReg, val, TempReg, sh); |
| return sh; |
| } |
| VG_(skin_panic)("getOperandShadow"); |
| } |
| |
| /* Create and return an instrumented version of cb_in. Free cb_in |
| before returning. */ |
| static UCodeBlock* memcheck_instrument ( UCodeBlock* cb_in ) |
| { |
| 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: |
| sk_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; |
| } |
| } |
| } |
| |
| 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) { |
| |
| case LOCK: |
| case NOP: |
| break; |
| |
| case INCEIP: |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| |
| /* 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: |
| sk_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: |
| sk_assert(u_in->tag2 == TempReg); |
| uInstr1(cb, SETV, 2, TempReg, SHADOW(u_in->val2)); |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| |
| case USESEG: |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| |
| /* 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 |
| skin. |
| |
| 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; |
| |
| /* 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; |
| |
| case PUT: |
| uInstr2(cb, PUTV, u_in->size, |
| TempReg, SHADOW(u_in->val1), |
| ArchReg, u_in->val2); |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| |
| 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; |
| |
| 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_(skin_panic)("memcheck_instrument: MOV"); |
| } |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| |
| /* Special case of add, where one of the operands is a literal. |
| lea1(t) = t + some literal. |
| Therefore: lea1#(qa) = left(qa) |
| */ |
| case LEA1: |
| sk_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; |
| |
| /* 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; |
| sk_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_(skin_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; |
| } |
| |
| /* 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; |
| |
| /* 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: |
| sk_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> |
| */ |
| /* sk_assert(u_in->flags_w != FlagsEmpty); */ |
| qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1); |
| /* We can safely modify qs; cast it to 0-size. */ |
| create_PCast(cb, u_in->size, 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; |
| |
| /* 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; |
| sk_assert(u_in->tag1 == TempReg || u_in->tag1 == Literal); |
| sk_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: |
| sk_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; |
| |
| /* not#(x) = x (since bitwise independent) */ |
| case NOT: |
| sk_assert(u_in->tag1 == TempReg); |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| |
| /* neg#(x) = left(x) (derivable from case for SUB) */ |
| case NEG: |
| sk_assert(u_in->tag1 == TempReg); |
| create_Left(cb, u_in->size, SHADOW(u_in->val1)); |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| |
| /* bswap#(x) = bswap(x) */ |
| case BSWAP: |
| sk_assert(u_in->tag1 == TempReg); |
| sk_assert(u_in->size == 4); |
| qd = SHADOW(u_in->val1); |
| uInstr1(cb, BSWAP, 4, TempReg, qd); |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| |
| /* cc2val#(qd) = pcast-0-to-size(eflags#) */ |
| case CC2VAL: |
| sk_assert(u_in->tag1 == TempReg); |
| sk_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; |
| |
| /* 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: |
| sk_assert(u_in->size == 4); |
| sk_assert(u_in->tag1 == TempReg); |
| sk_assert(u_in->tag2 == TempReg); |
| sk_assert(u_in->flags_r != FlagsEmpty); |
| sk_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; |
| |
| /* 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: |
| 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) { |
| sk_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; |
| |
| /* 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: |
| sk_assert(u_in->tag1 == TempReg); |
| sk_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; |
| |
| /* 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; |
| |
| /* 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; |
| |
| /* 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 { |
| sk_assert(u_in->tag1 == Literal); |
| } |
| if (u_in->cond != CondAlways) { |
| sk_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 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; |
| |
| /* 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; |
| |
| if (u_in->opcode == MMX2_MemRd || u_in->opcode == MMX2_MemWr) |
| sk_assert(u_in->size == 4 || u_in->size == 8); |
| |
| sk_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, |
| u_in->opcode==FPU_R ? (Addr) & MC_(fpu_read_check) |
| : (Addr) & MC_(fpu_write_check), |
| 2, 2, False); |
| |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| } |
| |
| /* For FPU insns not referencing memory, just copy thru. */ |
| case MMX1: case MMX2: case MMX3: |
| case FPU: |
| VG_(copy_UInstr)(cb, u_in); |
| break; |
| |
| /* 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_RegRd: |
| sk_assert(u_in->tag2 == TempReg); |
| sk_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; |
| |
| /* The MMX register is assumed to be fully defined, so |
| that's what this register becomes. */ |
| case MMX2_RegWr: |
| sk_assert(u_in->tag2 == TempReg); |
| sk_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_(skin_panic)( "memcheck_instrument: unhandled case"); |
| |
| } /* end of switch (u_in->opcode) */ |
| |
| } /* end of for loop */ |
| |
| VG_(free_UCodeBlock)(cb_in); |
| return cb; |
| } |
| |
| /*------------------------------------------------------------*/ |
| /*--- Clean up mem check instrumentation. ---*/ |
| /*------------------------------------------------------------*/ |
| |
| #define dis VG_(print_codegen) |
| |
| |
| #define VGC_IS_SHADOW(tempreg) ((tempreg % 2) == 1) |
| #define VGC_UNDEF ((UChar)100) |
| #define VGC_VALUE ((UChar)101) |
| |
| #define NOP_no_msg(uu) \ |
| do { VG_(new_NOP)(uu); } while (False) |
| |
| #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) |
| |
| #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) |
| |
| |
| |
| /* 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]) { |
| sk_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]) { |
| sk_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]) { |
| sk_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) { |
| sk_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; |
| } |
| |
| } |
| |
| } else { |
| /* Find out what this insn does to the temps. */ |
| k = VG_(get_reg_usage)(u, TempReg, &tempUse[0], &isWrites[0]); |
| sk_assert(0 <= k && k <= VG_MAX_REGS_USED); |
| for (j = k-1; j >= 0; j--) { |
| next_is_write[ tempUse[j] ] = isWrites[j]; |
| } |
| } |
| } |
| } |
| |
| |
| /* 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. |
| |
| 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 ) |
| { |
| 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. */ |
| |
| for (i = 0; i < VG_(get_num_instrs)(cb); i++) { |
| u = VG_(get_instr)(cb, i); |
| switch (u->opcode) { |
| |
| /* Tag-handling uinstrs. */ |
| |
| /* Deal with these quickly. */ |
| case NOP: |
| case LOCK: |
| case INCEIP: |
| break; |
| |
| /* Make a tag defined. */ |
| case SETV: |
| sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); |
| def[u->val1] = u->size; |
| break; |
| |
| /* Check definedness of a tag. */ |
| case TESTV: |
| sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); |
| if (def[u->val1] <= 4) { |
| sk_assert(def[u->val1] == u->size); |
| NOP_no_msg(u); |
| if (dis) |
| VG_(printf)(" at %2d: delete TESTV on defd arg\n", i); |
| } |
| break; |
| |
| /* 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: |
| sk_assert(u->tag2 == TempReg); |
| if (u->tag1 == TempReg) { |
| if (VGC_IS_SHADOW(u->val1)) { |
| sk_assert(VGC_IS_SHADOW(u->val2)); |
| def[u->val2] = def[u->val1]; |
| } |
| } |
| break; |
| |
| case PUTV: |
| sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); |
| if (def[u->val1] <= 4) { |
| sk_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_(skin_panic)("vg_cleanup(PUTV)"); |
| } |
| if (dis) |
| VG_(printf)( |
| " at %2d: propagate definedness into PUTV\n", i); |
| } |
| break; |
| |
| case STOREV: |
| sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); |
| if (def[u->val1] <= 4) { |
| sk_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_(skin_panic)("vg_cleanup(STOREV)"); |
| } |
| if (dis) |
| VG_(printf)( |
| " at %2d: propagate definedness into STandV\n", i); |
| } |
| break; |
| |
| /* Nothing interesting we can do with this, I think. */ |
| case PUTVF: |
| break; |
| |
| /* Tag handling operations. */ |
| case TAG2: |
| sk_assert(u->tag2 == TempReg && VGC_IS_SHADOW(u->val2)); |
| sk_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: |
| sk_assert(u->tag1 == TempReg && !VGC_IS_SHADOW(u->val1)); |
| break; |
| default: |
| sk_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: |
| sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); |
| sk_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. */ |
| sk_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. */ |
| sk_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 OR Q. So if Q is entirely defined, |
| ie all 0s, we get MOV T, Q. */ |
| if (def[u->val2] <= 4) { |
| sk_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; |
| } |
| break; |
| |
| case TAG1: |
| sk_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: |
| sk_assert(def[u->val1] == 4); |
| NOP_tag1_op(u); |
| break; |
| case Tag_PCast11: |
| sk_assert(def[u->val1] == 1); |
| NOP_tag1_op(u); |
| break; |
| /* Change size ... */ |
| case Tag_PCast40: |
| sk_assert(def[u->val1] == 4); |
| SETV_tag1_op(u,0); |
| def[u->val1] = 0; |
| break; |
| case Tag_PCast14: |
| sk_assert(def[u->val1] == 1); |
| SETV_tag1_op(u,4); |
| def[u->val1] = 4; |
| break; |
| case Tag_PCast12: |
| sk_assert(def[u->val1] == 1); |
| SETV_tag1_op(u,2); |
| def[u->val1] = 2; |
| break; |
| case Tag_PCast10: |
| sk_assert(def[u->val1] == 1); |
| SETV_tag1_op(u,0); |
| def[u->val1] = 0; |
| break; |
| case Tag_PCast02: |
| sk_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)); |
| 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]); |
| sk_assert(0 <= k && k <= VG_MAX_REGS_USED); |
| for (j = 0; j < k; j++) { |
| t = tempUse[j]; |
| sk_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; |
| } |
| } |
| } |
| } |
| } |
| |
| |
| /* Top level post-MemCheck-instrumentation cleanup function. */ |
| static void vg_cleanup ( UCodeBlock* cb ) |
| { |
| vg_propagate_definedness ( cb ); |
| vg_delete_redundant_SETVs ( cb ); |
| } |
| |
| |
| /* Caller will print out final instrumented code if necessary; we |
| print out intermediate instrumented code here if necessary. */ |
| UCodeBlock* SK_(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"); |
| } |
| vg_cleanup(cb); |
| if (dis) VG_(printf)("\n"); |
| } |
| return cb; |
| } |
| |
| #undef dis |
| |
| /*--------------------------------------------------------------------*/ |
| /*--- end mc_translate.c ---*/ |
| /*--------------------------------------------------------------------*/ |