mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-03 18:13:01 +00:00
662 lines
22 KiB
C
662 lines
22 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- Generate code for tool-specific UInstrs. ---*/
|
|
/*--- mc_from_ucode.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of MemCheck, a heavyweight Valgrind tool for
|
|
detecting memory errors.
|
|
|
|
Copyright (C) 2000-2004 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"
|
|
|
|
/*------------------------------------------------------------*/
|
|
/*--- 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 {
|
|
sk_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 {
|
|
sk_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_(skin_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 };
|
|
|
|
sk_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_(skin_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_(skin_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. */
|
|
sk_assert(sz == 0 || sz == 2 || sz == 4);
|
|
|
|
VG_(init_target)(&tgt);
|
|
|
|
sk_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_(skin_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_(skin_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_(skin_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:
|
|
sk_assert(lit == 0x00000000);
|
|
VG_(emit_movv_lit_offregmem) ( 4, 0x00000000,
|
|
VG_(shadow_reg_offset)(arch), R_EBP );
|
|
break;
|
|
case 2:
|
|
sk_assert(lit == 0xFFFF0000);
|
|
VG_(emit_movv_lit_offregmem) ( 2, 0x0000,
|
|
VG_(shadow_reg_offset)(arch), R_EBP );
|
|
break;
|
|
case 1:
|
|
sk_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_(skin_panic)("synth_PUTV(lit)");
|
|
}
|
|
|
|
} else {
|
|
|
|
UInt reg;
|
|
sk_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) sk_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_(skin_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_(skin_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_(skin_panic)("synth_TAG2_op");
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------*/
|
|
/*--- Generate code for a single UInstr. ---*/
|
|
/*----------------------------------------------------*/
|
|
|
|
void SK_(emit_XUInstr) ( UInstr* u, RRegSet regs_live_before )
|
|
{
|
|
switch (u->opcode) {
|
|
|
|
case SETV:
|
|
sk_assert(u->tag1 == RealReg);
|
|
synth_SETV ( u->size, u->val1 );
|
|
break;
|
|
|
|
case STOREV:
|
|
sk_assert(u->tag1 == RealReg || u->tag1 == Literal);
|
|
sk_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:
|
|
sk_assert(u->tag1 == RealReg);
|
|
sk_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:
|
|
sk_assert(u->tag1 == RealReg || u->tag1 == ArchReg);
|
|
synth_TESTV(u->size, u->tag1, u->val1);
|
|
break;
|
|
|
|
case GETV:
|
|
sk_assert(u->tag1 == ArchReg);
|
|
sk_assert(u->tag2 == RealReg);
|
|
synth_GETV(u->size, u->val1, u->val2);
|
|
break;
|
|
|
|
case GETVF:
|
|
sk_assert(u->tag1 == RealReg);
|
|
sk_assert(u->size == 0);
|
|
synth_GETVF(u->val1);
|
|
break;
|
|
|
|
case PUTV:
|
|
sk_assert(u->tag1 == RealReg || u->tag1 == Literal);
|
|
sk_assert(u->tag2 == ArchReg);
|
|
synth_PUTV(u->size, u->tag1,
|
|
u->tag1==Literal ? u->lit32 : u->val1,
|
|
u->val2 );
|
|
break;
|
|
|
|
case PUTVF:
|
|
sk_assert(u->tag1 == RealReg);
|
|
sk_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_(skin_panic)("emit_XUInstr: unhandled extension opcode");
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end mc_from_ucode.c ---*/
|
|
/*--------------------------------------------------------------------*/
|