blob: a41ce4193ed4fc7fd62730c5cc262ffc71ffb7f5 [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- begin guest_mips_toIR.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2010-2013 RT-RK
mips-valgrind@rt-rk.com
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.
*/
/* Translates MIPS code to IR. */
#include "libvex_basictypes.h"
#include "libvex_ir.h"
#include "libvex.h"
#include "libvex_guest_mips32.h"
#include "libvex_guest_mips64.h"
#include "main_util.h"
#include "main_globals.h"
#include "guest_generic_bb_to_IR.h"
#include "guest_mips_defs.h"
/*------------------------------------------------------------*/
/*--- Globals ---*/
/*------------------------------------------------------------*/
/* These are set at the start of the translation of a instruction, so
that we don't have to pass them around endlessly. CONST means does
not change during translation of the instruction. */
/* CONST: what is the host's endianness? This has to do with float vs
double register accesses on VFP, but it's complex and not properly
thought out. */
static VexEndness host_endness;
/* Pointer to the guest code area. */
static const UChar *guest_code;
/* CONST: The guest address for the instruction currently being
translated. */
#if defined(VGP_mips32_linux)
static Addr32 guest_PC_curr_instr;
#else
static Addr64 guest_PC_curr_instr;
#endif
/* MOD: The IRSB* into which we're generating code. */
static IRSB *irsb;
/* Is our guest binary 32 or 64bit? Set at each call to
disInstr_MIPS below. */
static Bool mode64 = False;
/* CPU has FPU and 32 dbl. prec. FP registers. */
static Bool fp_mode64 = False;
/* Define 1.0 in single and double precision. */
#define ONE_SINGLE 0x3F800000
#define ONE_DOUBLE 0x3FF0000000000000ULL
/*------------------------------------------------------------*/
/*--- Debugging output ---*/
/*------------------------------------------------------------*/
#define DIP(format, args...) \
if (vex_traceflags & VEX_TRACE_FE) \
vex_printf(format, ## args)
/*------------------------------------------------------------*/
/*--- Helper bits and pieces for deconstructing the ---*/
/*--- mips insn stream. ---*/
/*------------------------------------------------------------*/
/* ---------------- Integer registers ---------------- */
static UInt integerGuestRegOffset(UInt iregNo)
{
/* Do we care about endianness here? We do if sub-parts of integer
registers are accessed, but I don't think that ever happens on
MIPS. */
UInt ret;
if (!mode64)
switch (iregNo) {
case 0:
ret = offsetof(VexGuestMIPS32State, guest_r0); break;
case 1:
ret = offsetof(VexGuestMIPS32State, guest_r1); break;
case 2:
ret = offsetof(VexGuestMIPS32State, guest_r2); break;
case 3:
ret = offsetof(VexGuestMIPS32State, guest_r3); break;
case 4:
ret = offsetof(VexGuestMIPS32State, guest_r4); break;
case 5:
ret = offsetof(VexGuestMIPS32State, guest_r5); break;
case 6:
ret = offsetof(VexGuestMIPS32State, guest_r6); break;
case 7:
ret = offsetof(VexGuestMIPS32State, guest_r7); break;
case 8:
ret = offsetof(VexGuestMIPS32State, guest_r8); break;
case 9:
ret = offsetof(VexGuestMIPS32State, guest_r9); break;
case 10:
ret = offsetof(VexGuestMIPS32State, guest_r10); break;
case 11:
ret = offsetof(VexGuestMIPS32State, guest_r11); break;
case 12:
ret = offsetof(VexGuestMIPS32State, guest_r12); break;
case 13:
ret = offsetof(VexGuestMIPS32State, guest_r13); break;
case 14:
ret = offsetof(VexGuestMIPS32State, guest_r14); break;
case 15:
ret = offsetof(VexGuestMIPS32State, guest_r15); break;
case 16:
ret = offsetof(VexGuestMIPS32State, guest_r16); break;
case 17:
ret = offsetof(VexGuestMIPS32State, guest_r17); break;
case 18:
ret = offsetof(VexGuestMIPS32State, guest_r18); break;
case 19:
ret = offsetof(VexGuestMIPS32State, guest_r19); break;
case 20:
ret = offsetof(VexGuestMIPS32State, guest_r20); break;
case 21:
ret = offsetof(VexGuestMIPS32State, guest_r21); break;
case 22:
ret = offsetof(VexGuestMIPS32State, guest_r22); break;
case 23:
ret = offsetof(VexGuestMIPS32State, guest_r23); break;
case 24:
ret = offsetof(VexGuestMIPS32State, guest_r24); break;
case 25:
ret = offsetof(VexGuestMIPS32State, guest_r25); break;
case 26:
ret = offsetof(VexGuestMIPS32State, guest_r26); break;
case 27:
ret = offsetof(VexGuestMIPS32State, guest_r27); break;
case 28:
ret = offsetof(VexGuestMIPS32State, guest_r28); break;
case 29:
ret = offsetof(VexGuestMIPS32State, guest_r29); break;
case 30:
ret = offsetof(VexGuestMIPS32State, guest_r30); break;
case 31:
ret = offsetof(VexGuestMIPS32State, guest_r31); break;
default:
vassert(0);
break;
}
else
switch (iregNo) {
case 0:
ret = offsetof(VexGuestMIPS64State, guest_r0); break;
case 1:
ret = offsetof(VexGuestMIPS64State, guest_r1); break;
case 2:
ret = offsetof(VexGuestMIPS64State, guest_r2); break;
case 3:
ret = offsetof(VexGuestMIPS64State, guest_r3); break;
case 4:
ret = offsetof(VexGuestMIPS64State, guest_r4); break;
case 5:
ret = offsetof(VexGuestMIPS64State, guest_r5); break;
case 6:
ret = offsetof(VexGuestMIPS64State, guest_r6); break;
case 7:
ret = offsetof(VexGuestMIPS64State, guest_r7); break;
case 8:
ret = offsetof(VexGuestMIPS64State, guest_r8); break;
case 9:
ret = offsetof(VexGuestMIPS64State, guest_r9); break;
case 10:
ret = offsetof(VexGuestMIPS64State, guest_r10); break;
case 11:
ret = offsetof(VexGuestMIPS64State, guest_r11); break;
case 12:
ret = offsetof(VexGuestMIPS64State, guest_r12); break;
case 13:
ret = offsetof(VexGuestMIPS64State, guest_r13); break;
case 14:
ret = offsetof(VexGuestMIPS64State, guest_r14); break;
case 15:
ret = offsetof(VexGuestMIPS64State, guest_r15); break;
case 16:
ret = offsetof(VexGuestMIPS64State, guest_r16); break;
case 17:
ret = offsetof(VexGuestMIPS64State, guest_r17); break;
case 18:
ret = offsetof(VexGuestMIPS64State, guest_r18); break;
case 19:
ret = offsetof(VexGuestMIPS64State, guest_r19); break;
case 20:
ret = offsetof(VexGuestMIPS64State, guest_r20); break;
case 21:
ret = offsetof(VexGuestMIPS64State, guest_r21); break;
case 22:
ret = offsetof(VexGuestMIPS64State, guest_r22); break;
case 23:
ret = offsetof(VexGuestMIPS64State, guest_r23); break;
case 24:
ret = offsetof(VexGuestMIPS64State, guest_r24); break;
case 25:
ret = offsetof(VexGuestMIPS64State, guest_r25); break;
case 26:
ret = offsetof(VexGuestMIPS64State, guest_r26); break;
case 27:
ret = offsetof(VexGuestMIPS64State, guest_r27); break;
case 28:
ret = offsetof(VexGuestMIPS64State, guest_r28); break;
case 29:
ret = offsetof(VexGuestMIPS64State, guest_r29); break;
case 30:
ret = offsetof(VexGuestMIPS64State, guest_r30); break;
case 31:
ret = offsetof(VexGuestMIPS64State, guest_r31); break;
default:
vassert(0);
break;
}
return ret;
}
#if defined(VGP_mips32_linux)
#define OFFB_PC offsetof(VexGuestMIPS32State, guest_PC)
#else
#define OFFB_PC offsetof(VexGuestMIPS64State, guest_PC)
#endif
/* ---------------- Floating point registers ---------------- */
static UInt floatGuestRegOffset(UInt fregNo)
{
vassert(fregNo < 32);
UInt ret;
if (!mode64)
switch (fregNo) {
case 0:
ret = offsetof(VexGuestMIPS32State, guest_f0); break;
case 1:
ret = offsetof(VexGuestMIPS32State, guest_f1); break;
case 2:
ret = offsetof(VexGuestMIPS32State, guest_f2); break;
case 3:
ret = offsetof(VexGuestMIPS32State, guest_f3); break;
case 4:
ret = offsetof(VexGuestMIPS32State, guest_f4); break;
case 5:
ret = offsetof(VexGuestMIPS32State, guest_f5); break;
case 6:
ret = offsetof(VexGuestMIPS32State, guest_f6); break;
case 7:
ret = offsetof(VexGuestMIPS32State, guest_f7); break;
case 8:
ret = offsetof(VexGuestMIPS32State, guest_f8); break;
case 9:
ret = offsetof(VexGuestMIPS32State, guest_f9); break;
case 10:
ret = offsetof(VexGuestMIPS32State, guest_f10); break;
case 11:
ret = offsetof(VexGuestMIPS32State, guest_f11); break;
case 12:
ret = offsetof(VexGuestMIPS32State, guest_f12); break;
case 13:
ret = offsetof(VexGuestMIPS32State, guest_f13); break;
case 14:
ret = offsetof(VexGuestMIPS32State, guest_f14); break;
case 15:
ret = offsetof(VexGuestMIPS32State, guest_f15); break;
case 16:
ret = offsetof(VexGuestMIPS32State, guest_f16); break;
case 17:
ret = offsetof(VexGuestMIPS32State, guest_f17); break;
case 18:
ret = offsetof(VexGuestMIPS32State, guest_f18); break;
case 19:
ret = offsetof(VexGuestMIPS32State, guest_f19); break;
case 20:
ret = offsetof(VexGuestMIPS32State, guest_f20); break;
case 21:
ret = offsetof(VexGuestMIPS32State, guest_f21); break;
case 22:
ret = offsetof(VexGuestMIPS32State, guest_f22); break;
case 23:
ret = offsetof(VexGuestMIPS32State, guest_f23); break;
case 24:
ret = offsetof(VexGuestMIPS32State, guest_f24); break;
case 25:
ret = offsetof(VexGuestMIPS32State, guest_f25); break;
case 26:
ret = offsetof(VexGuestMIPS32State, guest_f26); break;
case 27:
ret = offsetof(VexGuestMIPS32State, guest_f27); break;
case 28:
ret = offsetof(VexGuestMIPS32State, guest_f28); break;
case 29:
ret = offsetof(VexGuestMIPS32State, guest_f29); break;
case 30:
ret = offsetof(VexGuestMIPS32State, guest_f30); break;
case 31:
ret = offsetof(VexGuestMIPS32State, guest_f31); break;
default:
vassert(0);
break;
}
else
switch (fregNo) {
case 0:
ret = offsetof(VexGuestMIPS64State, guest_f0); break;
case 1:
ret = offsetof(VexGuestMIPS64State, guest_f1); break;
case 2:
ret = offsetof(VexGuestMIPS64State, guest_f2); break;
case 3:
ret = offsetof(VexGuestMIPS64State, guest_f3); break;
case 4:
ret = offsetof(VexGuestMIPS64State, guest_f4); break;
case 5:
ret = offsetof(VexGuestMIPS64State, guest_f5); break;
case 6:
ret = offsetof(VexGuestMIPS64State, guest_f6); break;
case 7:
ret = offsetof(VexGuestMIPS64State, guest_f7); break;
case 8:
ret = offsetof(VexGuestMIPS64State, guest_f8); break;
case 9:
ret = offsetof(VexGuestMIPS64State, guest_f9); break;
case 10:
ret = offsetof(VexGuestMIPS64State, guest_f10); break;
case 11:
ret = offsetof(VexGuestMIPS64State, guest_f11); break;
case 12:
ret = offsetof(VexGuestMIPS64State, guest_f12); break;
case 13:
ret = offsetof(VexGuestMIPS64State, guest_f13); break;
case 14:
ret = offsetof(VexGuestMIPS64State, guest_f14); break;
case 15:
ret = offsetof(VexGuestMIPS64State, guest_f15); break;
case 16:
ret = offsetof(VexGuestMIPS64State, guest_f16); break;
case 17:
ret = offsetof(VexGuestMIPS64State, guest_f17); break;
case 18:
ret = offsetof(VexGuestMIPS64State, guest_f18); break;
case 19:
ret = offsetof(VexGuestMIPS64State, guest_f19); break;
case 20:
ret = offsetof(VexGuestMIPS64State, guest_f20); break;
case 21:
ret = offsetof(VexGuestMIPS64State, guest_f21); break;
case 22:
ret = offsetof(VexGuestMIPS64State, guest_f22); break;
case 23:
ret = offsetof(VexGuestMIPS64State, guest_f23); break;
case 24:
ret = offsetof(VexGuestMIPS64State, guest_f24); break;
case 25:
ret = offsetof(VexGuestMIPS64State, guest_f25); break;
case 26:
ret = offsetof(VexGuestMIPS64State, guest_f26); break;
case 27:
ret = offsetof(VexGuestMIPS64State, guest_f27); break;
case 28:
ret = offsetof(VexGuestMIPS64State, guest_f28); break;
case 29:
ret = offsetof(VexGuestMIPS64State, guest_f29); break;
case 30:
ret = offsetof(VexGuestMIPS64State, guest_f30); break;
case 31:
ret = offsetof(VexGuestMIPS64State, guest_f31); break;
default:
vassert(0);
break;
}
return ret;
}
/* ---------------- MIPS32 DSP ASE(r2) accumulators ---------------- */
static UInt accumulatorGuestRegOffset(UInt acNo)
{
vassert(!mode64);
vassert(acNo <= 3);
UInt ret;
switch (acNo) {
case 0:
ret = offsetof(VexGuestMIPS32State, guest_ac0); break;
case 1:
ret = offsetof(VexGuestMIPS32State, guest_ac1); break;
case 2:
ret = offsetof(VexGuestMIPS32State, guest_ac2); break;
case 3:
ret = offsetof(VexGuestMIPS32State, guest_ac3); break;
default:
vassert(0);
break;
}
return ret;
}
/* Do a endian load of a 32-bit word, regardless of the endianness of the
underlying host. */
static inline UInt getUInt(const UChar * p)
{
UInt w = 0;
#if defined (_MIPSEL)
w = (w << 8) | p[3];
w = (w << 8) | p[2];
w = (w << 8) | p[1];
w = (w << 8) | p[0];
#elif defined (_MIPSEB)
w = (w << 8) | p[0];
w = (w << 8) | p[1];
w = (w << 8) | p[2];
w = (w << 8) | p[3];
#endif
return w;
}
#define BITS2(_b1,_b0) \
(((_b1) << 1) | (_b0))
#define BITS3(_b2,_b1,_b0) \
(((_b2) << 2) | ((_b1) << 1) | (_b0))
#define BITS4(_b3,_b2,_b1,_b0) \
(((_b3) << 3) | ((_b2) << 2) | ((_b1) << 1) | (_b0))
#define BITS5(_b4,_b3,_b2,_b1,_b0) \
(((_b4) << 4) | BITS4((_b3),(_b2),(_b1),(_b0)))
#define BITS6(_b5,_b4,_b3,_b2,_b1,_b0) \
((BITS2((_b5),(_b4)) << 4) \
| BITS4((_b3),(_b2),(_b1),(_b0)))
#define BITS8(_b7,_b6,_b5,_b4,_b3,_b2,_b1,_b0) \
((BITS4((_b7),(_b6),(_b5),(_b4)) << 4) \
| BITS4((_b3),(_b2),(_b1),(_b0)))
#define LOAD_STORE_PATTERN \
t1 = newTemp(mode64 ? Ity_I64 : Ity_I32); \
if(!mode64) \
assign(t1, binop(Iop_Add32, getIReg(rs), \
mkU32(extend_s_16to32(imm)))); \
else \
assign(t1, binop(Iop_Add64, getIReg(rs), \
mkU64(extend_s_16to64(imm)))); \
#define LOADX_STORE_PATTERN \
t1 = newTemp(mode64 ? Ity_I64 : Ity_I32); \
if(!mode64) \
assign(t1, binop(Iop_Add32, getIReg(regRs), getIReg(regRt))); \
else \
assign(t1, binop(Iop_Add64, getIReg(regRs), getIReg(regRt)));
#define LWX_SWX_PATTERN64 \
t2 = newTemp(Ity_I64); \
assign(t2, binop(Iop_And64, mkexpr(t1), mkU64(0xFFFFFFFFFFFFFFFCULL))); \
t4 = newTemp(Ity_I32); \
assign(t4, mkNarrowTo32( ty, binop(Iop_And64, \
mkexpr(t1), mkU64(0x3))));
#define LWX_SWX_PATTERN64_1 \
t2 = newTemp(Ity_I64); \
assign(t2, binop(Iop_And64, mkexpr(t1), mkU64(0xFFFFFFFFFFFFFFF8ULL))); \
t4 = newTemp(Ity_I64); \
assign(t4, binop(Iop_And64, mkexpr(t1), mkU64(0x7)));
#define LWX_SWX_PATTERN \
t2 = newTemp(Ity_I32); \
assign(t2, binop(Iop_And32, mkexpr(t1), mkU32(0xFFFFFFFC))); \
t4 = newTemp(Ity_I32); \
assign(t4, binop(Iop_And32, mkexpr(t1), mkU32(0x00000003)))
#define SXXV_PATTERN(op) \
putIReg(rd, binop(op, \
getIReg(rt), \
unop(Iop_32to8, \
binop(Iop_And32, \
getIReg(rs), \
mkU32(0x0000001F) \
) \
) \
) \
)
#define SXXV_PATTERN64(op) \
putIReg(rd, mkWidenFrom32(ty, binop(op, \
mkNarrowTo32(ty, getIReg(rt)), \
unop(Iop_32to8, \
binop(Iop_And32, \
mkNarrowTo32(ty, getIReg(rs)), \
mkU32(0x0000001F) \
) \
) \
), True \
))
#define SXX_PATTERN(op) \
putIReg(rd, binop(op, getIReg(rt), mkU8(sa)));
#define ALU_PATTERN(op) \
putIReg(rd, binop(op, getIReg(rs), getIReg(rt)));
#define ALUI_PATTERN(op) \
putIReg(rt, binop(op, getIReg(rs), mkU32(imm)));
#define ALUI_PATTERN64(op) \
putIReg(rt, binop(op, getIReg(rs), mkU64(imm)));
#define ALU_PATTERN64(op) \
putIReg(rd, mkWidenFrom32(ty, binop(op, \
mkNarrowTo32(ty, getIReg(rs)), \
mkNarrowTo32(ty, getIReg(rt))), True));
#define FP_CONDITIONAL_CODE \
t3 = newTemp(Ity_I32); \
assign(t3, binop(Iop_And32, \
IRExpr_ITE( binop(Iop_CmpEQ32, mkU32(cc), mkU32(0)), \
binop(Iop_Shr32, getFCSR(), mkU8(23)), \
binop(Iop_Shr32, getFCSR(), mkU8(24+cc))), \
mkU32(0x1)));
#define ILLEGAL_INSTRUCTON \
putPC(mkU32(guest_PC_curr_instr + 4)); \
dres.jk_StopHere = Ijk_SigILL; \
dres.whatNext = Dis_StopHere;
/*------------------------------------------------------------*/
/*--- Field helpers ---*/
/*------------------------------------------------------------*/
static UInt get_opcode(UInt mipsins)
{
return (0xFC000000 & mipsins) >> 26;
}
static UInt get_rs(UInt mipsins)
{
return (0x03E00000 & mipsins) >> 21;
}
static UInt get_rt(UInt mipsins)
{
return (0x001F0000 & mipsins) >> 16;
}
static UInt get_imm(UInt mipsins)
{
return (0x0000FFFF & mipsins);
}
static UInt get_instr_index(UInt mipsins)
{
return (0x03FFFFFF & mipsins);
}
static UInt get_rd(UInt mipsins)
{
return (0x0000F800 & mipsins) >> 11;
}
static UInt get_sa(UInt mipsins)
{
return (0x000007C0 & mipsins) >> 6;
}
static UInt get_function(UInt mipsins)
{
return (0x0000003F & mipsins);
}
static UInt get_ft(UInt mipsins)
{
return (0x001F0000 & mipsins) >> 16;
}
static UInt get_fs(UInt mipsins)
{
return (0x0000F800 & mipsins) >> 11;
}
static UInt get_fd(UInt mipsins)
{
return (0x000007C0 & mipsins) >> 6;
}
static UInt get_mov_cc(UInt mipsins)
{
return (0x001C0000 & mipsins) >> 18;
}
static UInt get_bc1_cc(UInt mipsins)
{
return (0x001C0000 & mipsins) >> 18;
}
static UInt get_fpc_cc(UInt mipsins)
{
return (0x00000700 & mipsins) >> 8;
}
static UInt get_tf(UInt mipsins)
{
return (0x00010000 & mipsins) >> 16;
}
static UInt get_nd(UInt mipsins)
{
return (0x00020000 & mipsins) >> 17;
}
static UInt get_fmt(UInt mipsins)
{
return (0x03E00000 & mipsins) >> 21;
}
static UInt get_FC(UInt mipsins)
{
return (0x000000F0 & mipsins) >> 4;
}
static UInt get_cond(UInt mipsins)
{
return (0x0000000F & mipsins);
}
/* for break & syscall */
static UInt get_code(UInt mipsins)
{
return (0xFFC0 & mipsins) >> 6;
}
static UInt get_lsb(UInt mipsins)
{
return (0x7C0 & mipsins) >> 6;
}
static UInt get_msb(UInt mipsins)
{
return (0x0000F800 & mipsins) >> 11;
}
static UInt get_rot(UInt mipsins)
{
return (0x00200000 & mipsins) >> 21;
}
static UInt get_rotv(UInt mipsins)
{
return (0x00000040 & mipsins) >> 6;
}
static UInt get_sel(UInt mipsins)
{
return (0x00000007 & mipsins);
}
/* Get acc number for all MIPS32 DSP ASE(r2) instructions that use them,
except for MFHI and MFLO. */
static UInt get_acNo(UInt mipsins)
{
return (0x00001800 & mipsins) >> 11;
}
/* Get accumulator number for MIPS32 DSP ASEr2 MFHI and MFLO instructions. */
static UInt get_acNo_mfhilo(UInt mipsins)
{
return (0x00600000 & mipsins) >> 21;
}
/* Get mask field (helper function for wrdsp instruction). */
static UInt get_wrdspMask(UInt mipsins)
{
return (0x001ff800 & mipsins) >> 11;
}
/* Get mask field (helper function for rddsp instruction). */
static UInt get_rddspMask(UInt mipsins)
{
return (0x03ff0000 & mipsins) >> 16;
}
/* Get shift field (helper function for DSP ASE instructions). */
static UInt get_shift(UInt mipsins)
{
return (0x03f00000 & mipsins) >> 20;
}
/* Get immediate field for DSP ASE instructions. */
static UInt get_dspImm(UInt mipsins)
{
return (0x03ff0000 & mipsins) >> 16;
}
static Bool branch_or_jump(const UChar * addr)
{
UInt fmt;
UInt cins = getUInt(addr);
UInt opcode = get_opcode(cins);
UInt rt = get_rt(cins);
UInt function = get_function(cins);
/* bgtz, blez, bne, beq, jal */
if (opcode == 0x07 || opcode == 0x06 || opcode == 0x05 || opcode == 0x04
|| opcode == 0x03 || opcode == 0x02) {
return True;
}
/* bgez */
if (opcode == 0x01 && rt == 0x01) {
return True;
}
/* bgezal */
if (opcode == 0x01 && rt == 0x11) {
return True;
}
/* bltzal */
if (opcode == 0x01 && rt == 0x10) {
return True;
}
/* bltz */
if (opcode == 0x01 && rt == 0x00) {
return True;
}
/* jalr */
if (opcode == 0x00 && function == 0x09) {
return True;
}
/* jr */
if (opcode == 0x00 && function == 0x08) {
return True;
}
if (opcode == 0x11) {
/*bc1f & bc1t */
fmt = get_fmt(cins);
if (fmt == 0x08) {
return True;
}
}
/* bposge32 */
if (opcode == 0x01 && rt == 0x1c) {
return True;
}
/* Cavium Specific instructions. */
if (opcode == 0x32 || opcode == 0x3A || opcode == 0x36 || opcode == 0x3E) {
/* BBIT0, BBIT1, BBIT032, BBIT132 */
return True;
}
return False;
}
static Bool is_Branch_or_Jump_and_Link(const UChar * addr)
{
UInt cins = getUInt(addr);
UInt opcode = get_opcode(cins);
UInt rt = get_rt(cins);
UInt function = get_function(cins);
/* jal */
if (opcode == 0x02) {
return True;
}
/* bgezal */
if (opcode == 0x01 && rt == 0x11) {
return True;
}
/* bltzal */
if (opcode == 0x01 && rt == 0x10) {
return True;
}
/* jalr */
if (opcode == 0x00 && function == 0x09) {
return True;
}
return False;
}
static Bool branch_or_link_likely(const UChar * addr)
{
UInt cins = getUInt(addr);
UInt opcode = get_opcode(cins);
UInt rt = get_rt(cins);
/* bgtzl, blezl, bnel, beql */
if (opcode == 0x17 || opcode == 0x16 || opcode == 0x15 || opcode == 0x14)
return True;
/* bgezl */
if (opcode == 0x01 && rt == 0x03)
return True;
/* bgezall */
if (opcode == 0x01 && rt == 0x13)
return True;
/* bltzall */
if (opcode == 0x01 && rt == 0x12)
return True;
/* bltzl */
if (opcode == 0x01 && rt == 0x02)
return True;
return False;
}
/*------------------------------------------------------------*/
/*--- Helper bits and pieces for creating IR fragments. ---*/
/*------------------------------------------------------------*/
static IRExpr *mkU8(UInt i)
{
vassert(i < 256);
return IRExpr_Const(IRConst_U8((UChar) i));
}
/* Create an expression node for a 16-bit integer constant. */
static IRExpr *mkU16(UInt i)
{
return IRExpr_Const(IRConst_U16(i));
}
/* Create an expression node for a 32-bit integer constant. */
static IRExpr *mkU32(UInt i)
{
return IRExpr_Const(IRConst_U32(i));
}
/* Create an expression node for a 64-bit integer constant. */
static IRExpr *mkU64(ULong i)
{
return IRExpr_Const(IRConst_U64(i));
}
static IRExpr *mkexpr(IRTemp tmp)
{
return IRExpr_RdTmp(tmp);
}
static IRExpr *unop(IROp op, IRExpr * a)
{
return IRExpr_Unop(op, a);
}
static IRExpr *binop(IROp op, IRExpr * a1, IRExpr * a2)
{
return IRExpr_Binop(op, a1, a2);
}
static IRExpr *triop(IROp op, IRExpr * a1, IRExpr * a2, IRExpr * a3)
{
return IRExpr_Triop(op, a1, a2, a3);
}
static IRExpr *qop ( IROp op, IRExpr * a1, IRExpr * a2, IRExpr * a3,
IRExpr * a4 )
{
return IRExpr_Qop(op, a1, a2, a3, a4);
}
static IRExpr *load(IRType ty, IRExpr * addr)
{
IRExpr *load1 = NULL;
#if defined (_MIPSEL)
load1 = IRExpr_Load(Iend_LE, ty, addr);
#elif defined (_MIPSEB)
load1 = IRExpr_Load(Iend_BE, ty, addr);
#endif
return load1;
}
/* Add a statement to the list held by "irsb". */
static void stmt(IRStmt * st)
{
addStmtToIRSB(irsb, st);
}
static void assign(IRTemp dst, IRExpr * e)
{
stmt(IRStmt_WrTmp(dst, e));
}
static void store(IRExpr * addr, IRExpr * data)
{
#if defined (_MIPSEL)
stmt(IRStmt_Store(Iend_LE, addr, data));
#elif defined (_MIPSEB)
stmt(IRStmt_Store(Iend_BE, addr, data));
#endif
}
/* Generate a new temporary of the given type. */
static IRTemp newTemp(IRType ty)
{
vassert(isPlausibleIRType(ty));
return newIRTemp(irsb->tyenv, ty);
}
/* Generate an expression for SRC rotated right by ROT. */
static IRExpr *genROR32(IRExpr * src, Int rot)
{
vassert(rot >= 0 && rot < 32);
if (rot == 0)
return src;
return binop(Iop_Or32, binop(Iop_Shl32, src, mkU8(32 - rot)),
binop(Iop_Shr32, src, mkU8(rot)));
}
static IRExpr *genRORV32(IRExpr * src, IRExpr * rs)
{
IRTemp t0 = newTemp(Ity_I8);
IRTemp t1 = newTemp(Ity_I8);
assign(t0, unop(Iop_32to8, binop(Iop_And32, rs, mkU32(0x0000001F))));
assign(t1, binop(Iop_Sub8, mkU8(32), mkexpr(t0)));
return binop(Iop_Or32, binop(Iop_Shl32, src, mkexpr(t1)),
binop(Iop_Shr32, src, mkexpr(t0)));
}
static UShort extend_s_10to16(UInt x)
{
return (UShort) ((((Int) x) << 22) >> 22);
}
static ULong extend_s_10to32(UInt x)
{
return (ULong)((((Long) x) << 22) >> 22);
}
static ULong extend_s_10to64(UInt x)
{
return (ULong)((((Long) x) << 54) >> 54);
}
static UInt extend_s_16to32(UInt x)
{
return (UInt) ((((Int) x) << 16) >> 16);
}
static UInt extend_s_18to32(UInt x)
{
return (UInt) ((((Int) x) << 14) >> 14);
}
static ULong extend_s_16to64 ( UInt x )
{
return (ULong) ((((Long) x) << 48) >> 48);
}
static ULong extend_s_18to64 ( UInt x )
{
return (ULong) ((((Long) x) << 46) >> 46);
}
static ULong extend_s_32to64 ( UInt x )
{
return (ULong) ((((Long) x) << 32) >> 32);
}
static void jmp_lit32 ( /*MOD*/ DisResult* dres, IRJumpKind kind, Addr32 d32 )
{
vassert(dres->whatNext == Dis_Continue);
vassert(dres->len == 0);
vassert(dres->continueAt == 0);
vassert(dres->jk_StopHere == Ijk_INVALID);
dres->whatNext = Dis_StopHere;
dres->jk_StopHere = kind;
stmt( IRStmt_Put( OFFB_PC, mkU32(d32) ) );
}
static void jmp_lit64 ( /*MOD*/ DisResult* dres, IRJumpKind kind, Addr64 d64 )
{
vassert(dres->whatNext == Dis_Continue);
vassert(dres->len == 0);
vassert(dres->continueAt == 0);
vassert(dres->jk_StopHere == Ijk_INVALID);
dres->whatNext = Dis_StopHere;
dres->jk_StopHere = kind;
stmt(IRStmt_Put(OFFB_PC, mkU64(d64)));
}
/* Get value from accumulator (helper function for MIPS32 DSP ASE instructions).
This function should be called before any other operation if widening
multiplications are used. */
static IRExpr *getAcc(UInt acNo)
{
vassert(!mode64);
vassert(acNo <= 3);
return IRExpr_Get(accumulatorGuestRegOffset(acNo), Ity_I64);
}
/* Get value from DSPControl register (helper function for MIPS32 DSP ASE
instructions). */
static IRExpr *getDSPControl(void)
{
vassert(!mode64);
return IRExpr_Get(offsetof(VexGuestMIPS32State, guest_DSPControl), Ity_I32);
}
/* Put value to DSPControl register. Expression e is written to DSPControl as
is. If only certain bits of DSPControl need to be changed, it should be done
before calling putDSPControl(). It could be done by reading DSPControl and
ORing it with appropriate mask. */
static void putDSPControl(IRExpr * e)
{
vassert(!mode64);
stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_DSPControl), e));
}
/* Fetch a byte from the guest insn stream. */
static UChar getIByte(Int delta)
{
return guest_code[delta];
}
static IRExpr *getIReg(UInt iregNo)
{
if (0 == iregNo) {
return mode64 ? mkU64(0x0) : mkU32(0x0);
} else {
IRType ty = mode64 ? Ity_I64 : Ity_I32;
vassert(iregNo < 32);
return IRExpr_Get(integerGuestRegOffset(iregNo), ty);
}
}
static IRExpr *getHI(void)
{
if (mode64)
return IRExpr_Get(offsetof(VexGuestMIPS64State, guest_HI), Ity_I64);
else
return IRExpr_Get(offsetof(VexGuestMIPS32State, guest_HI), Ity_I32);
}
static IRExpr *getLO(void)
{
if (mode64)
return IRExpr_Get(offsetof(VexGuestMIPS64State, guest_LO), Ity_I64);
else
return IRExpr_Get(offsetof(VexGuestMIPS32State, guest_LO), Ity_I32);
}
static IRExpr *getFCSR(void)
{
if (mode64)
return IRExpr_Get(offsetof(VexGuestMIPS64State, guest_FCSR), Ity_I32);
else
return IRExpr_Get(offsetof(VexGuestMIPS32State, guest_FCSR), Ity_I32);
}
/* Get byte from register reg, byte pos from 0 to 3 (or 7 for MIPS64) . */
static IRExpr *getByteFromReg(UInt reg, UInt byte_pos)
{
UInt pos = byte_pos * 8;
if (mode64)
return unop(Iop_64to8, binop(Iop_And64,
binop(Iop_Shr64, getIReg(reg), mkU8(pos)),
mkU64(0xFF)));
else
return unop(Iop_32to8, binop(Iop_And32,
binop(Iop_Shr32, getIReg(reg), mkU8(pos)),
mkU32(0xFF)));
}
static void putFCSR(IRExpr * e)
{
if (mode64)
stmt(IRStmt_Put(offsetof(VexGuestMIPS64State, guest_FCSR), e));
else
stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_FCSR), e));
}
/* fs - fpu source register number.
inst - fpu instruction that needs to be executed.
sz32 - size of source register.
opN - number of operads:
1 - unary operation.
2 - binary operation. */
static void calculateFCSR(UInt fs, UInt ft, UInt inst, Bool sz32, UInt opN)
{
IRDirty *d;
IRTemp fcsr = newTemp(Ity_I32);
/* IRExpr_BBPTR() => Need to pass pointer to guest state to helper. */
if (fp_mode64)
d = unsafeIRDirty_1_N(fcsr, 0,
"mips_dirtyhelper_calculate_FCSR_fp64",
&mips_dirtyhelper_calculate_FCSR_fp64,
mkIRExprVec_4(IRExpr_BBPTR(),
mkU32(fs),
mkU32(ft),
mkU32(inst)));
else
d = unsafeIRDirty_1_N(fcsr, 0,
"mips_dirtyhelper_calculate_FCSR_fp32",
&mips_dirtyhelper_calculate_FCSR_fp32,
mkIRExprVec_4(IRExpr_BBPTR(),
mkU32(fs),
mkU32(ft),
mkU32(inst)));
if (opN == 1) { /* Unary operation. */
/* Declare we're reading guest state. */
if (sz32 || fp_mode64)
d->nFxState = 2;
else
d->nFxState = 3;
vex_bzero(&d->fxState, sizeof(d->fxState));
d->fxState[0].fx = Ifx_Read; /* read */
if (mode64)
d->fxState[0].offset = offsetof(VexGuestMIPS64State, guest_FCSR);
else
d->fxState[0].offset = offsetof(VexGuestMIPS32State, guest_FCSR);
d->fxState[0].size = sizeof(UInt);
d->fxState[1].fx = Ifx_Read; /* read */
d->fxState[1].offset = floatGuestRegOffset(fs);
d->fxState[1].size = sizeof(ULong);
if (!(sz32 || fp_mode64)) {
d->fxState[2].fx = Ifx_Read; /* read */
d->fxState[2].offset = floatGuestRegOffset(fs+1);
d->fxState[2].size = sizeof(ULong);
}
} else if (opN == 2) { /* Binary operation. */
/* Declare we're reading guest state. */
if (sz32 || fp_mode64)
d->nFxState = 3;
else
d->nFxState = 5;
vex_bzero(&d->fxState, sizeof(d->fxState));
d->fxState[0].fx = Ifx_Read; /* read */
if (mode64)
d->fxState[0].offset = offsetof(VexGuestMIPS64State, guest_FCSR);
else
d->fxState[0].offset = offsetof(VexGuestMIPS32State, guest_FCSR);
d->fxState[0].size = sizeof(UInt);
d->fxState[1].fx = Ifx_Read; /* read */
d->fxState[1].offset = floatGuestRegOffset(fs);
d->fxState[1].size = sizeof(ULong);
d->fxState[2].fx = Ifx_Read; /* read */
d->fxState[2].offset = floatGuestRegOffset(ft);
d->fxState[2].size = sizeof(ULong);
if (!(sz32 || fp_mode64)) {
d->fxState[3].fx = Ifx_Read; /* read */
d->fxState[3].offset = floatGuestRegOffset(fs+1);
d->fxState[3].size = sizeof(ULong);
d->fxState[4].fx = Ifx_Read; /* read */
d->fxState[4].offset = floatGuestRegOffset(ft+1);
d->fxState[4].size = sizeof(ULong);
}
}
stmt(IRStmt_Dirty(d));
putFCSR(mkexpr(fcsr));
}
static IRExpr *getULR(void)
{
if (mode64)
return IRExpr_Get(offsetof(VexGuestMIPS64State, guest_ULR), Ity_I64);
else
return IRExpr_Get(offsetof(VexGuestMIPS32State, guest_ULR), Ity_I32);
}
static void putIReg(UInt archreg, IRExpr * e)
{
IRType ty = mode64 ? Ity_I64 : Ity_I32;
vassert(archreg < 32);
vassert(typeOfIRExpr(irsb->tyenv, e) == ty);
if (archreg != 0)
stmt(IRStmt_Put(integerGuestRegOffset(archreg), e));
}
static IRExpr *mkNarrowTo32(IRType ty, IRExpr * src)
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ty == Ity_I64 ? unop(Iop_64to32, src) : src;
}
static void putLO(IRExpr * e)
{
if (mode64) {
stmt(IRStmt_Put(offsetof(VexGuestMIPS64State, guest_LO), e));
} else {
stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_LO), e));
/* Add value to lower 32 bits of ac0 to maintain compatibility between
regular MIPS32 instruction set and MIPS DSP ASE. Keep higher 32bits
unchanged. */
IRTemp t_lo = newTemp(Ity_I32);
IRTemp t_hi = newTemp(Ity_I32);
assign(t_lo, e);
assign(t_hi, unop(Iop_64HIto32, getAcc(0)));
stmt(IRStmt_Put(accumulatorGuestRegOffset(0),
binop(Iop_32HLto64, mkexpr(t_hi), mkexpr(t_lo))));
}
}
static void putHI(IRExpr * e)
{
if (mode64) {
stmt(IRStmt_Put(offsetof(VexGuestMIPS64State, guest_HI), e));
} else {
stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_HI), e));
/* Add value to higher 32 bits of ac0 to maintain compatibility between
regular MIPS32 instruction set and MIPS DSP ASE. Keep lower 32bits
unchanged. */
IRTemp t_lo = newTemp(Ity_I32);
IRTemp t_hi = newTemp(Ity_I32);
assign(t_hi, e);
assign(t_lo, unop(Iop_64to32, getAcc(0)));
stmt(IRStmt_Put(accumulatorGuestRegOffset(0),
binop(Iop_32HLto64, mkexpr(t_hi), mkexpr(t_lo))));
}
}
/* Put value to accumulator(helper function for MIPS32 DSP ASE instructions). */
static void putAcc(UInt acNo, IRExpr * e)
{
vassert(!mode64);
vassert(acNo <= 3);
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I64);
stmt(IRStmt_Put(accumulatorGuestRegOffset(acNo), e));
/* If acNo = 0, split value to HI and LO regs in order to maintain compatibility
between MIPS32 and MIPS DSP ASE insn sets. */
if (0 == acNo) {
putLO(unop(Iop_64to32, e));
putHI(unop(Iop_64HIto32, e));
}
}
static IRExpr *mkNarrowTo8 ( IRType ty, IRExpr * src )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ty == Ity_I64 ? unop(Iop_64to8, src) : unop(Iop_32to8, src);
}
static void putPC(IRExpr * e)
{
stmt(IRStmt_Put(OFFB_PC, e));
}
static IRExpr *mkWidenFrom32(IRType ty, IRExpr * src, Bool sined)
{
vassert(ty == Ity_I32 || ty == Ity_I64);
if (ty == Ity_I32)
return src;
return (sined) ? unop(Iop_32Sto64, src) : unop(Iop_32Uto64, src);
}
/* Narrow 8/16/32 bit int expr to 8/16/32. Clearly only some
of these combinations make sense. */
static IRExpr *narrowTo(IRType dst_ty, IRExpr * e)
{
IRType src_ty = typeOfIRExpr(irsb->tyenv, e);
if (src_ty == dst_ty)
return e;
if (src_ty == Ity_I32 && dst_ty == Ity_I16)
return unop(Iop_32to16, e);
if (src_ty == Ity_I32 && dst_ty == Ity_I8)
return unop(Iop_32to8, e);
if (src_ty == Ity_I64 && dst_ty == Ity_I8) {
vassert(mode64);
return unop(Iop_64to8, e);
}
if (src_ty == Ity_I64 && dst_ty == Ity_I16) {
vassert(mode64);
return unop(Iop_64to16, e);
}
vpanic("narrowTo(mips)");
return 0;
}
static IRExpr *getLoFromF64(IRType ty, IRExpr * src)
{
vassert(ty == Ity_F32 || ty == Ity_F64);
if (ty == Ity_F64) {
IRTemp t0, t1;
t0 = newTemp(Ity_I64);
t1 = newTemp(Ity_I32);
assign(t0, unop(Iop_ReinterpF64asI64, src));
assign(t1, unop(Iop_64to32, mkexpr(t0)));
return unop(Iop_ReinterpI32asF32, mkexpr(t1));
} else
return src;
}
static IRExpr *mkWidenFromF32(IRType ty, IRExpr * src)
{
vassert(ty == Ity_F32 || ty == Ity_F64);
if (ty == Ity_F64) {
IRTemp t0 = newTemp(Ity_I32);
IRTemp t1 = newTemp(Ity_I64);
assign(t0, unop(Iop_ReinterpF32asI32, src));
assign(t1, binop(Iop_32HLto64, mkU32(0x0), mkexpr(t0)));
return unop(Iop_ReinterpI64asF64, mkexpr(t1));
} else
return src;
}
static IRExpr *dis_branch_likely(IRExpr * guard, UInt imm)
{
ULong branch_offset;
IRTemp t0;
/* PC = PC + (SignExtend(signed_immed_24) << 2)
An 18-bit signed offset (the 16-bit offset field shifted left 2 bits)
is added to the address of the instruction following
the branch (not the branch itself), in the branch delay slot, to form
a PC-relative effective target address. */
if (mode64)
branch_offset = extend_s_18to64(imm << 2);
else
branch_offset = extend_s_18to32(imm << 2);
t0 = newTemp(Ity_I1);
assign(t0, guard);
if (mode64)
stmt(IRStmt_Exit(mkexpr(t0), Ijk_Boring,
IRConst_U64(guest_PC_curr_instr + 8), OFFB_PC));
else
stmt(IRStmt_Exit(mkexpr(t0), Ijk_Boring,
IRConst_U32(guest_PC_curr_instr + 8), OFFB_PC));
irsb->jumpkind = Ijk_Boring;
if (mode64)
return mkU64(guest_PC_curr_instr + 4 + branch_offset);
else
return mkU32(guest_PC_curr_instr + 4 + branch_offset);
}
static void dis_branch(Bool link, IRExpr * guard, UInt imm, IRStmt ** set)
{
ULong branch_offset;
IRTemp t0;
if (link) { /* LR (GPR31) = addr of the 2nd instr after branch instr */
if (mode64)
putIReg(31, mkU64(guest_PC_curr_instr + 8));
else
putIReg(31, mkU32(guest_PC_curr_instr + 8));
}
/* PC = PC + (SignExtend(signed_immed_24) << 2)
An 18-bit signed offset (the 16-bit offset field shifted left 2 bits)
is added to the address of the instruction following
the branch (not the branch itself), in the branch delay slot, to form
a PC-relative effective target address. */
if (mode64)
branch_offset = extend_s_18to64(imm << 2);
else
branch_offset = extend_s_18to32(imm << 2);
t0 = newTemp(Ity_I1);
assign(t0, guard);
if (mode64)
*set = IRStmt_Exit(mkexpr(t0), link ? Ijk_Call : Ijk_Boring,
IRConst_U64(guest_PC_curr_instr + 4 + branch_offset),
OFFB_PC);
else
*set = IRStmt_Exit(mkexpr(t0), link ? Ijk_Call : Ijk_Boring,
IRConst_U32(guest_PC_curr_instr + 4 +
(UInt) branch_offset), OFFB_PC);
}
static IRExpr *getFReg(UInt fregNo)
{
vassert(fregNo < 32);
IRType ty = fp_mode64 ? Ity_F64 : Ity_F32;
return IRExpr_Get(floatGuestRegOffset(fregNo), ty);
}
static IRExpr *getDReg(UInt dregNo)
{
vassert(dregNo < 32);
if (fp_mode64) {
return IRExpr_Get(floatGuestRegOffset(dregNo), Ity_F64);
} else {
/* Read a floating point register pair and combine their contents into a
64-bit value */
IRTemp t0 = newTemp(Ity_F32);
IRTemp t1 = newTemp(Ity_F32);
IRTemp t2 = newTemp(Ity_F64);
IRTemp t3 = newTemp(Ity_I32);
IRTemp t4 = newTemp(Ity_I32);
IRTemp t5 = newTemp(Ity_I64);
assign(t0, getFReg(dregNo));
assign(t1, getFReg(dregNo + 1));
assign(t3, unop(Iop_ReinterpF32asI32, mkexpr(t0)));
assign(t4, unop(Iop_ReinterpF32asI32, mkexpr(t1)));
assign(t5, binop(Iop_32HLto64, mkexpr(t4), mkexpr(t3)));
assign(t2, unop(Iop_ReinterpI64asF64, mkexpr(t5)));
return mkexpr(t2);
}
}
static void putFReg(UInt dregNo, IRExpr * e)
{
vassert(dregNo < 32);
IRType ty = fp_mode64 ? Ity_F64 : Ity_F32;
vassert(typeOfIRExpr(irsb->tyenv, e) == ty);
stmt(IRStmt_Put(floatGuestRegOffset(dregNo), e));
}
static void putDReg(UInt dregNo, IRExpr * e)
{
if (fp_mode64) {
vassert(dregNo < 32);
IRType ty = Ity_F64;
vassert(typeOfIRExpr(irsb->tyenv, e) == ty);
stmt(IRStmt_Put(floatGuestRegOffset(dregNo), e));
} else {
vassert(dregNo < 32);
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_F64);
IRTemp t1 = newTemp(Ity_F64);
IRTemp t4 = newTemp(Ity_I32);
IRTemp t5 = newTemp(Ity_I32);
IRTemp t6 = newTemp(Ity_I64);
assign(t1, e);
assign(t6, unop(Iop_ReinterpF64asI64, mkexpr(t1)));
assign(t4, unop(Iop_64HIto32, mkexpr(t6))); /* hi */
assign(t5, unop(Iop_64to32, mkexpr(t6))); /* lo */
putFReg(dregNo, unop(Iop_ReinterpI32asF32, mkexpr(t5)));
putFReg(dregNo + 1, unop(Iop_ReinterpI32asF32, mkexpr(t4)));
}
}
static void setFPUCondCode(IRExpr * e, UInt cc)
{
if (cc == 0) {
putFCSR(binop(Iop_And32, getFCSR(), mkU32(0xFF7FFFFF)));
putFCSR(binop(Iop_Or32, getFCSR(), binop(Iop_Shl32, e, mkU8(23))));
} else {
putFCSR(binop(Iop_And32, getFCSR(), unop(Iop_Not32,
binop(Iop_Shl32, mkU32(0x01000000), mkU8(cc)))));
putFCSR(binop(Iop_Or32, getFCSR(), binop(Iop_Shl32, e, mkU8(24 + cc))));
}
}
static IRExpr* get_IR_roundingmode ( void )
{
/*
rounding mode | MIPS | IR
------------------------
to nearest | 00 | 00
to zero | 01 | 11
to +infinity | 10 | 10
to -infinity | 11 | 01
*/
IRTemp rm_MIPS = newTemp(Ity_I32);
/* Last two bits in FCSR are rounding mode. */
if (mode64)
assign(rm_MIPS, binop(Iop_And32, IRExpr_Get(offsetof(VexGuestMIPS64State,
guest_FCSR), Ity_I32), mkU32(3)));
else
assign(rm_MIPS, binop(Iop_And32, IRExpr_Get(offsetof(VexGuestMIPS32State,
guest_FCSR), Ity_I32), mkU32(3)));
/* rm_IR = XOR( rm_MIPS32, (rm_MIPS32 << 1) & 2) */
return binop(Iop_Xor32, mkexpr(rm_MIPS), binop(Iop_And32,
binop(Iop_Shl32, mkexpr(rm_MIPS), mkU8(1)), mkU32(2)));
}
/* sz, ULong -> IRExpr */
static IRExpr *mkSzImm ( IRType ty, ULong imm64 )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ty == Ity_I64 ? mkU64(imm64) : mkU32((UInt) imm64);
}
static IRConst *mkSzConst ( IRType ty, ULong imm64 )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return (ty == Ity_I64 ? IRConst_U64(imm64) : IRConst_U32((UInt) imm64));
}
/* Make sure we get valid 32 and 64bit addresses */
static Addr64 mkSzAddr ( IRType ty, Addr64 addr )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return (ty == Ity_I64 ? (Addr64) addr :
(Addr64) extend_s_32to64(toUInt(addr)));
}
/* Shift and Rotate instructions for MIPS64 */
static Bool dis_instr_shrt ( UInt theInstr )
{
UInt opc2 = get_function(theInstr);
UChar regRs = get_rs(theInstr);
UChar regRt = get_rt(theInstr);
UChar regRd = get_rd(theInstr);
UChar uImmsa = get_sa(theInstr);
Long sImmsa = extend_s_16to64(uImmsa);
IRType ty = mode64 ? Ity_I64 : Ity_I32;
IRTemp tmp = newTemp(ty);
IRTemp tmpOr = newTemp(ty);
IRTemp tmpRt = newTemp(ty);
IRTemp tmpRs = newTemp(ty);
IRTemp tmpRd = newTemp(ty);
assign(tmpRs, getIReg(regRs));
assign(tmpRt, getIReg(regRt));
switch (opc2) {
case 0x3A:
if ((regRs & 0x01) == 0) {
/* Doubleword Shift Right Logical - DSRL; MIPS64 */
DIP("dsrl r%u, r%u, %d", regRd, regRt, (Int)sImmsa);
assign(tmpRd, binop(Iop_Shr64, mkexpr(tmpRt), mkU8(uImmsa)));
putIReg(regRd, mkexpr(tmpRd));
} else if ((regRs & 0x01) == 1) {
/* Doubleword Rotate Right - DROTR; MIPS64r2 */
vassert(mode64);
DIP("drotr r%u, r%u, %d", regRd, regRt, (Int)sImmsa);
IRTemp tmpL = newTemp(ty);
IRTemp tmpR = newTemp(ty);
assign(tmpR, binop(Iop_Shr64, mkexpr(tmpRt), mkU8(uImmsa)));
assign(tmp, binop(Iop_Shl64, mkexpr(tmpRt), mkU8(63 - uImmsa)));
assign(tmpL, binop(Iop_Shl64, mkexpr(tmp), mkU8(1)));
assign(tmpRd, binop(Iop_Or64, mkexpr(tmpL), mkexpr(tmpR)));
putIReg(regRd, mkexpr(tmpRd));
} else
return False;
break;
case 0x3E:
if ((regRs & 0x01) == 0) {
/* Doubleword Shift Right Logical Plus 32 - DSRL32; MIPS64 */
DIP("dsrl32 r%u, r%u, %d", regRd, regRt, (Int)(sImmsa + 32));
assign(tmpRd, binop(Iop_Shr64, mkexpr(tmpRt), mkU8(uImmsa + 32)));
putIReg(regRd, mkexpr(tmpRd));
} else if ((regRs & 0x01) == 1) {
/* Doubleword Rotate Right Plus 32 - DROTR32; MIPS64r2 */
DIP("drotr32 r%u, r%u, %d", regRd, regRt, (Int)sImmsa);
vassert(mode64);
IRTemp tmpL = newTemp(ty);
IRTemp tmpR = newTemp(ty);
/* (tmpRt >> sa) | (tmpRt << (64 - sa)) */
assign(tmpR, binop(Iop_Shr64, mkexpr(tmpRt), mkU8(uImmsa + 32)));
assign(tmp, binop(Iop_Shl64, mkexpr(tmpRt),
mkU8(63 - (uImmsa + 32))));
assign(tmpL, binop(Iop_Shl64, mkexpr(tmp), mkU8(1)));
assign(tmpRd, binop(Iop_Or64, mkexpr(tmpL), mkexpr(tmpR)));
putIReg(regRd, mkexpr(tmpRd));
} else
return False;
break;
case 0x16:
if ((uImmsa & 0x01) == 0) {
/* Doubleword Shift Right Logical Variable - DSRLV; MIPS64 */
DIP("dsrlv r%u, r%u, r%u", regRd, regRt, regRs);
IRTemp tmpRs8 = newTemp(Ity_I8);
/* s = tmpRs[5..0] */
assign(tmp, binop(Iop_And64, mkexpr(tmpRs), mkU64(63)));
assign(tmpRs8, mkNarrowTo8(ty, mkexpr(tmp)));
assign(tmpRd, binop(Iop_Shr64, mkexpr(tmpRt), mkexpr(tmpRs8)));
putIReg(regRd, mkexpr(tmpRd));
} else if ((uImmsa & 0x01) == 1) {
/* Doubleword Rotate Right Variable - DROTRV; MIPS64r2 */
DIP("drotrv r%u, r%u, r%u", regRd, regRt, regRs);
IRTemp tmpL = newTemp(ty);
IRTemp tmpR = newTemp(ty);
IRTemp tmpRs8 = newTemp(Ity_I8);
IRTemp tmpLs8 = newTemp(Ity_I8);
IRTemp tmp64 = newTemp(ty);
/* s = tmpRs[5...0]
m = 64 - s
(tmpRt << s) | (tmpRt >> m) */
assign(tmp64, binop(Iop_And64, mkexpr(tmpRs), mkSzImm(ty, 63)));
assign(tmp, binop(Iop_Sub64, mkU64(63), mkexpr(tmp64)));
assign(tmpLs8, mkNarrowTo8(ty, mkexpr(tmp)));
assign(tmpRs8, mkNarrowTo8(ty, mkexpr(tmp64)));
assign(tmpR, binop(Iop_Shr64, mkexpr(tmpRt), mkexpr(tmpRs8)));
assign(tmpL, binop(Iop_Shl64, mkexpr(tmpRt), mkexpr(tmpLs8)));
assign(tmpRd, binop(Iop_Shl64, mkexpr(tmpL), mkU8(1)));
assign(tmpOr, binop(Iop_Or64, mkexpr(tmpRd), mkexpr(tmpR)));
putIReg(regRd, mkexpr(tmpOr));
} else
return False;
break;
case 0x38: /* Doubleword Shift Left Logical - DSLL; MIPS64 */
DIP("dsll r%u, r%u, %d", regRd, regRt, (Int)sImmsa);
vassert(mode64);
assign(tmpRd, binop(Iop_Shl64, mkexpr(tmpRt), mkU8(uImmsa)));
putIReg(regRd, mkexpr(tmpRd));
break;
case 0x3C: /* Doubleword Shift Left Logical Plus 32 - DSLL32; MIPS64 */
DIP("dsll32 r%u, r%u, %d", regRd, regRt, (Int)sImmsa);
assign(tmpRd, binop(Iop_Shl64, mkexpr(tmpRt), mkU8(uImmsa + 32)));
putIReg(regRd, mkexpr(tmpRd));
break;
case 0x14: { /* Doubleword Shift Left Logical Variable - DSLLV; MIPS64 */
DIP("dsllv r%u, r%u, r%u", regRd, regRt, regRs);
IRTemp tmpRs8 = newTemp(Ity_I8);
assign(tmp, binop(Iop_And64, mkexpr(tmpRs), mkSzImm(ty, 63)));
assign(tmpRs8, mkNarrowTo8(ty, mkexpr(tmp)));
assign(tmpRd, binop(Iop_Shl64, mkexpr(tmpRt), mkexpr(tmpRs8)));
putIReg(regRd, mkexpr(tmpRd));
break;
}
case 0x3B: /* Doubleword Shift Right Arithmetic - DSRA; MIPS64 */
DIP("dsra r%u, r%u, %d", regRd, regRt, (Int)sImmsa);
assign(tmpRd, binop(Iop_Sar64, mkexpr(tmpRt), mkU8(uImmsa)));
putIReg(regRd, mkexpr(tmpRd));
break;
case 0x3F: /* Doubleword Shift Right Arithmetic Plus 32 - DSRA32;
MIPS64 */
DIP("dsra32 r%u, r%u, %d", regRd, regRt, (Int)sImmsa);
assign(tmpRd, binop(Iop_Sar64, mkexpr(tmpRt), mkU8(uImmsa + 32)));
putIReg(regRd, mkexpr(tmpRd));
break;
case 0x17: { /* Doubleword Shift Right Arithmetic Variable - DSRAV;
MIPS64 */
DIP("dsrav r%u, r%u, r%u", regRd, regRt, regRs);
IRTemp tmpRs8 = newTemp(Ity_I8);
assign(tmp, binop(Iop_And64, mkexpr(tmpRs), mkSzImm(ty, 63)));
assign(tmpRs8, mkNarrowTo8(ty, mkexpr(tmp)));
assign(tmpRd, binop(Iop_Sar64, mkexpr(tmpRt), mkexpr(tmpRs8)));
putIReg(regRd, mkexpr(tmpRd));
break;
}
default:
return False;
}
return True;
}
static IROp mkSzOp ( IRType ty, IROp op8 )
{
Int adj;
vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32 || ty == Ity_I64);
vassert(op8 == Iop_Add8 || op8 == Iop_Sub8 || op8 == Iop_Mul8
|| op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8
|| op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8
|| op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8 || op8 == Iop_Not8);
adj = ty == Ity_I8 ? 0 : (ty == Ity_I16 ? 1 : (ty == Ity_I32 ? 2 : 3));
return adj + op8;
}
/*********************************************************/
/*--- Floating Point Compare ---*/
/*********************************************************/
/* Function that returns a string that represent mips cond
mnemonic for the input code. */
static const HChar* showCondCode(UInt code) {
const HChar* ret;
switch (code) {
case 0: ret = "f"; break;
case 1: ret = "un"; break;
case 2: ret = "eq"; break;
case 3: ret = "ueq"; break;
case 4: ret = "olt"; break;
case 5: ret = "ult"; break;
case 6: ret = "ole"; break;
case 7: ret = "ule"; break;
case 8: ret = "sf"; break;
case 9: ret = "ngle"; break;
case 10: ret = "seq"; break;
case 11: ret = "ngl"; break;
case 12: ret = "lt"; break;
case 13: ret = "nge"; break;
case 14: ret = "le"; break;
case 15: ret = "ngt"; break;
default: vpanic("showCondCode"); break;
}
return ret;
}
static Bool dis_instr_CCondFmt ( UInt cins )
{
IRTemp t0, t1, t2, t3, tmp5, tmp6;
IRTemp ccIR = newTemp(Ity_I32);
IRTemp ccMIPS = newTemp(Ity_I32);
UInt FC = get_FC(cins);
UInt fmt = get_fmt(cins);
UInt fs = get_fs(cins);
UInt ft = get_ft(cins);
UInt cond = get_cond(cins);
if (FC == 0x3) { /* C.cond.fmt */
UInt fpc_cc = get_fpc_cc(cins);
switch (fmt) {
case 0x10: { /* C.cond.S */
DIP("c.%s.s %d, f%d, f%d", showCondCode(cond), fpc_cc, fs, ft);
if (fp_mode64) {
t0 = newTemp(Ity_I32);
t1 = newTemp(Ity_I32);
t2 = newTemp(Ity_I32);
t3 = newTemp(Ity_I32);
tmp5 = newTemp(Ity_F64);
tmp6 = newTemp(Ity_F64);
assign(tmp5, unop(Iop_F32toF64, getLoFromF64(Ity_F64,
getFReg(fs))));
assign(tmp6, unop(Iop_F32toF64, getLoFromF64(Ity_F64,
getFReg(ft))));
assign(ccIR, binop(Iop_CmpF64, mkexpr(tmp5), mkexpr(tmp6)));
putHI(mkWidenFrom32(mode64 ? Ity_I64: Ity_I32,
mkexpr(ccIR), True));
/* Map compare result from IR to MIPS
FP cmp result | MIPS | IR
--------------------------
UN | 0x1 | 0x45
EQ | 0x2 | 0x40
GT | 0x4 | 0x00
LT | 0x8 | 0x01
*/
/* ccMIPS = Shl(1, (~(ccIR>>5) & 2) | ((ccIR ^ (ccIR>>6)) & 1) */
assign(ccMIPS, binop(Iop_Shl32, mkU32(1), unop(Iop_32to8,
binop(Iop_Or32, binop(Iop_And32, unop(Iop_Not32,
binop(Iop_Shr32, mkexpr(ccIR),mkU8(5))),mkU32(2)),
binop(Iop_And32, binop(Iop_Xor32, mkexpr(ccIR),
binop(Iop_Shr32, mkexpr(ccIR), mkU8(6))),
mkU32(1))))));
putLO(mkWidenFrom32(mode64 ? Ity_I64: Ity_I32,
mkexpr(ccMIPS), True));
/* UN */
assign(t0, binop(Iop_And32, mkexpr(ccMIPS), mkU32(0x1)));
/* EQ */
assign(t1, binop(Iop_And32, binop(Iop_Shr32, mkexpr(ccMIPS),
mkU8(0x1)), mkU32(0x1)));
/* NGT */
assign(t2, binop(Iop_And32, unop(Iop_Not32, binop(Iop_Shr32,
mkexpr(ccMIPS), mkU8(0x2))),mkU32(0x1)));
/* LT */
assign(t3, binop(Iop_And32, binop(Iop_Shr32, mkexpr(ccMIPS),
mkU8(0x3)), mkU32(0x1)));
switch (cond) {
case 0x0:
setFPUCondCode(mkU32(0), fpc_cc);
break;
case 0x1:
setFPUCondCode(mkexpr(t0), fpc_cc);
break;
case 0x2:
setFPUCondCode(mkexpr(t1), fpc_cc);
break;
case 0x3:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t1)),
fpc_cc);
break;
case 0x4:
setFPUCondCode(mkexpr(t3), fpc_cc);
break;
case 0x5:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t3)),
fpc_cc);
break;
case 0x6:
setFPUCondCode(binop(Iop_Or32, mkexpr(t3), mkexpr(t1)),
fpc_cc);
break;
case 0x7:
setFPUCondCode(mkexpr(t2), fpc_cc);
break;
case 0x8:
setFPUCondCode(mkU32(0), fpc_cc);
break;
case 0x9:
setFPUCondCode(mkexpr(t0), fpc_cc);
break;
case 0xA:
setFPUCondCode(mkexpr(t1), fpc_cc);
break;
case 0xB:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t1)),
fpc_cc);
break;
case 0xC:
setFPUCondCode(mkexpr(t3), fpc_cc);
break;
case 0xD:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t3)),
fpc_cc);
break;
case 0xE:
setFPUCondCode(binop(Iop_Or32, mkexpr(t3), mkexpr(t1)),
fpc_cc);
break;
case 0xF:
setFPUCondCode(mkexpr(t2), fpc_cc);
break;
default:
return False;
}
} else {
t0 = newTemp(Ity_I32);
t1 = newTemp(Ity_I32);
t2 = newTemp(Ity_I32);
t3 = newTemp(Ity_I32);
assign(ccIR, binop(Iop_CmpF64, unop(Iop_F32toF64, getFReg(fs)),
unop(Iop_F32toF64, getFReg(ft))));
/* Map compare result from IR to MIPS
FP cmp result | MIPS | IR
--------------------------
UN | 0x1 | 0x45
EQ | 0x2 | 0x40
GT | 0x4 | 0x00
LT | 0x8 | 0x01
*/
/* ccMIPS = Shl(1, (~(ccIR>>5) & 2) | ((ccIR ^ (ccIR>>6)) & 1) */
assign(ccMIPS, binop(Iop_Shl32, mkU32(1), unop(Iop_32to8,
binop(Iop_Or32, binop(Iop_And32, unop(Iop_Not32,
binop(Iop_Shr32, mkexpr(ccIR), mkU8(5))),
mkU32(2)), binop(Iop_And32,
binop(Iop_Xor32, mkexpr(ccIR),
binop(Iop_Shr32, mkexpr(ccIR), mkU8(6))),
mkU32(1))))));
/* UN */
assign(t0, binop(Iop_And32, mkexpr(ccMIPS), mkU32(0x1)));
/* EQ */
assign(t1, binop(Iop_And32, binop(Iop_Shr32, mkexpr(ccMIPS),
mkU8(0x1)), mkU32(0x1)));
/* NGT */
assign(t2, binop(Iop_And32, unop(Iop_Not32, binop(Iop_Shr32,
mkexpr(ccMIPS), mkU8(0x2))), mkU32(0x1)));
/* LT */
assign(t3, binop(Iop_And32, binop(Iop_Shr32, mkexpr(ccMIPS),
mkU8(0x3)), mkU32(0x1)));
switch (cond) {
case 0x0:
setFPUCondCode(mkU32(0), fpc_cc);
break;
case 0x1:
setFPUCondCode(mkexpr(t0), fpc_cc);
break;
case 0x2:
setFPUCondCode(mkexpr(t1), fpc_cc);
break;
case 0x3:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t1)),
fpc_cc);
break;
case 0x4:
setFPUCondCode(mkexpr(t3), fpc_cc);
break;
case 0x5:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t3)),
fpc_cc);
break;
case 0x6:
setFPUCondCode(binop(Iop_Or32, mkexpr(t3), mkexpr(t1)),
fpc_cc);
break;
case 0x7:
setFPUCondCode(mkexpr(t2), fpc_cc);
break;
case 0x8:
setFPUCondCode(mkU32(0), fpc_cc);
break;
case 0x9:
setFPUCondCode(mkexpr(t0), fpc_cc);
break;
case 0xA:
setFPUCondCode(mkexpr(t1), fpc_cc);
break;
case 0xB:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t1)),
fpc_cc);
break;
case 0xC:
setFPUCondCode(mkexpr(t3), fpc_cc);
break;
case 0xD:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t3)),
fpc_cc);
break;
case 0xE:
setFPUCondCode(binop(Iop_Or32, mkexpr(t3), mkexpr(t1)),
fpc_cc);
break;
case 0xF:
setFPUCondCode(mkexpr(t2), fpc_cc);
break;
default:
return False;
}
}
}
break;
case 0x11: { /* C.cond.D */
DIP("c.%s.d %d, f%d, f%d", showCondCode(cond), fpc_cc, fs, ft);
t0 = newTemp(Ity_I32);
t1 = newTemp(Ity_I32);
t2 = newTemp(Ity_I32);
t3 = newTemp(Ity_I32);
assign(ccIR, binop(Iop_CmpF64, getDReg(fs), getDReg(ft)));
/* Map compare result from IR to MIPS
FP cmp result | MIPS | IR
--------------------------
UN | 0x1 | 0x45
EQ | 0x2 | 0x40
GT | 0x4 | 0x00
LT | 0x8 | 0x01
*/
/* ccMIPS = Shl(1, (~(ccIR>>5) & 2) | ((ccIR ^ (ccIR>>6)) & 1) */
assign(ccMIPS, binop(Iop_Shl32, mkU32(1), unop(Iop_32to8,
binop(Iop_Or32, binop(Iop_And32, unop(Iop_Not32,
binop(Iop_Shr32, mkexpr(ccIR), mkU8(5))), mkU32(2)),
binop(Iop_And32, binop(Iop_Xor32, mkexpr(ccIR),
binop(Iop_Shr32, mkexpr(ccIR), mkU8(6))),
mkU32(1))))));
/* UN */
assign(t0, binop(Iop_And32, mkexpr(ccMIPS), mkU32(0x1)));
/* EQ */
assign(t1, binop(Iop_And32, binop(Iop_Shr32, mkexpr(ccMIPS),
mkU8(0x1)), mkU32(0x1)));
/* NGT */
assign(t2, binop(Iop_And32, unop(Iop_Not32, binop(Iop_Shr32,
mkexpr(ccMIPS), mkU8(0x2))), mkU32(0x1)));
/* LT */
assign(t3, binop(Iop_And32, binop(Iop_Shr32, mkexpr(ccMIPS),
mkU8(0x3)), mkU32(0x1)));
switch (cond) {
case 0x0:
setFPUCondCode(mkU32(0), fpc_cc);
break;
case 0x1:
setFPUCondCode(mkexpr(t0), fpc_cc);
break;
case 0x2:
setFPUCondCode(mkexpr(t1), fpc_cc);
break;
case 0x3:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t1)),
fpc_cc);
break;
case 0x4:
setFPUCondCode(mkexpr(t3), fpc_cc);
break;
case 0x5:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t3)),
fpc_cc);
break;
case 0x6:
setFPUCondCode(binop(Iop_Or32, mkexpr(t3), mkexpr(t1)),
fpc_cc);
break;
case 0x7:
setFPUCondCode(mkexpr(t2), fpc_cc);
break;
case 0x8:
setFPUCondCode(mkU32(0), fpc_cc);
break;
case 0x9:
setFPUCondCode(mkexpr(t0), fpc_cc);
break;
case 0xA:
setFPUCondCode(mkexpr(t1), fpc_cc);
break;
case 0xB:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t1)),
fpc_cc);
break;
case 0xC:
setFPUCondCode(mkexpr(t3), fpc_cc);
break;
case 0xD:
setFPUCondCode(binop(Iop_Or32, mkexpr(t0), mkexpr(t3)),
fpc_cc);
break;
case 0xE:
setFPUCondCode(binop(Iop_Or32, mkexpr(t3), mkexpr(t1)),
fpc_cc);
break;
case 0xF:
setFPUCondCode(mkexpr(t2), fpc_cc);
break;
default:
return False;
}
}
break;
default:
return False;
}
} else {
return False;
}
return True;
}
/*********************************************************/
/*--- Branch Instructions for mips64 ---*/
/*********************************************************/
static Bool dis_instr_branch ( UInt theInstr, DisResult * dres,
Bool(*resteerOkFn) (void *, Addr64),
void *callback_opaque, IRStmt ** set )
{
UInt jmpKind = 0;
UChar opc1 = get_opcode(theInstr);
UChar regRs = get_rs(theInstr);
UChar regRt = get_rt(theInstr);
UInt offset = get_imm(theInstr);
Long sOffset = extend_s_16to64(offset);
IRType ty = mode64 ? Ity_I64 : Ity_I32;
IROp opSlt = mode64 ? Iop_CmpLT64S : Iop_CmpLT32S;
IRTemp tmp = newTemp(ty);
IRTemp tmpRs = newTemp(ty);
IRTemp tmpRt = newTemp(ty);
IRTemp tmpLt = newTemp(ty);
IRTemp tmpReg0 = newTemp(ty);
UChar regLnk = 31; /* reg 31 is link reg in MIPS */
Addr64 addrTgt = 0;
Addr64 cia = guest_PC_curr_instr;
IRExpr *eConst0 = mkSzImm(ty, (UInt) 0);
IRExpr *eNia = mkSzImm(ty, cia + 8);
IRExpr *eCond = NULL;
assign(tmpRs, getIReg(regRs));
assign(tmpRt, getIReg(regRt));
assign(tmpReg0, getIReg(0));
eCond = binop(mkSzOp(ty, Iop_CmpNE8), mkexpr(tmpReg0), mkexpr(tmpReg0));
switch (opc1) {
case 0x01:
switch (regRt) {
case 0x00: { /* BLTZ rs, offset */
addrTgt = mkSzAddr(ty, cia + 4 + (sOffset << 2));
IRTemp tmpLtRes = newTemp(Ity_I1);
assign(tmp, eConst0);
assign(tmpLtRes, binop(opSlt, mkexpr(tmpRs), mkexpr(tmp)));
assign(tmpLt, mode64 ? unop(Iop_1Uto64, mkexpr(tmpLtRes)) :
unop(Iop_1Uto32, mkexpr(tmpLtRes)));
eCond = binop(mkSzOp(ty, Iop_CmpNE8), mkexpr(tmpLt),
mkexpr(tmpReg0));
jmpKind = Ijk_Boring;
break;
}
case 0x01: { /* BGEZ rs, offset */
IRTemp tmpLtRes = newTemp(Ity_I1);
addrTgt = mkSzAddr(ty, cia + 4 + (sOffset << 2));
assign(tmp, eConst0);
assign(tmpLtRes, binop(opSlt, mkexpr(tmpRs), mkexpr(tmp)));
assign(tmpLt, mode64 ? unop(Iop_1Uto64, mkexpr(tmpLtRes)) :
unop(Iop_1Uto32, mkexpr(tmpLtRes)));
eCond = binop(mkSzOp(ty, Iop_CmpEQ8), mkexpr(tmpLt),
mkexpr(tmpReg0));
jmpKind = Ijk_Boring;
break;
}
case 0x11: { /* BGEZAL rs, offset */
addrTgt = mkSzAddr(ty, cia + 4 + (sOffset << 2));
putIReg(regLnk, eNia);
IRTemp tmpLtRes = newTemp(Ity_I1);
assign(tmpLtRes, binop(opSlt, mkexpr(tmpRs), eConst0));
assign(tmpLt, mode64 ? unop(Iop_1Uto64, mkexpr(tmpLtRes)) :
unop(Iop_1Uto32, mkexpr(tmpLtRes)));
eCond = binop(mkSzOp(ty, Iop_CmpEQ8), mkexpr(tmpLt),
mkexpr(tmpReg0));
jmpKind = Ijk_Call;
break;
}
case 0x10: { /* BLTZAL rs, offset */
IRTemp tmpLtRes = newTemp(Ity_I1);
IRTemp tmpRes = newTemp(ty);
addrTgt = mkSzAddr(ty, cia + 4 + (sOffset << 2));
putIReg(regLnk, eNia);
assign(tmp, eConst0);
assign(tmpLtRes, binop(opSlt, mkexpr(tmpRs), mkexpr(tmp)));
assign(tmpRes, mode64 ? unop(Iop_1Uto64,
mkexpr(tmpLtRes)) : unop(Iop_1Uto32, mkexpr(tmpLtRes)));
eCond = binop(mkSzOp(ty, Iop_CmpNE8), mkexpr(tmpRes),
mkexpr(tmpReg0));
jmpKind = Ijk_Call;
break;
}
}
break;
default:
return False;
}
*set = IRStmt_Exit(eCond, jmpKind, mkSzConst(ty, addrTgt), OFFB_PC);
return True;
}
/*********************************************************/
/*--- Cavium Specific Instructions ---*/
/*********************************************************/
/* Convenience function to yield to thread scheduler */
static void jump_back(IRExpr *condition)
{
stmt( IRStmt_Exit(condition,
Ijk_Yield,
IRConst_U64( guest_PC_curr_instr ),
OFFB_PC) );
}
/* Based on s390_irgen_load_and_add32. */
static void mips_irgen_load_and_add32(IRTemp op1addr, IRTemp new_val,
UChar rd, Bool putIntoRd)
{
IRCAS *cas;
IRTemp old_mem = newTemp(Ity_I32);
IRTemp expd = newTemp(Ity_I32);
assign(expd, load(Ity_I32, mkexpr(op1addr)));
cas = mkIRCAS(IRTemp_INVALID, old_mem,
Iend_LE, mkexpr(op1addr),
NULL, mkexpr(expd), /* expected value */
NULL, mkexpr(new_val) /* new value */);
stmt(IRStmt_CAS(cas));
/* If old_mem contains the expected value, then the CAS succeeded.
Otherwise, it did not */
jump_back(binop(Iop_CmpNE32, mkexpr(old_mem), mkexpr(expd)));
if (putIntoRd)
putIReg(rd, mkWidenFrom32(Ity_I64, mkexpr(old_mem), True));
}
/* Based on s390_irgen_load_and_add64. */
static void mips_irgen_load_and_add64(IRTemp op1addr, IRTemp new_val,
UChar rd, Bool putIntoRd)
{
IRCAS *cas;
IRTemp old_mem = newTemp(Ity_I64);
IRTemp expd = newTemp(Ity_I64);
assign(expd, load(Ity_I64, mkexpr(op1addr)));
cas = mkIRCAS(IRTemp_INVALID, old_mem,
Iend_LE, mkexpr(op1addr),
NULL, mkexpr(expd), /* expected value */
NULL, mkexpr(new_val) /* new value */);
stmt(IRStmt_CAS(cas));
/* If old_mem contains the expected value, then the CAS succeeded.
Otherwise, it did not */
jump_back(binop(Iop_CmpNE64, mkexpr(old_mem), mkexpr(expd)));
if (putIntoRd)
putIReg(rd, mkexpr(old_mem));
}
static Bool dis_instr_CVM ( UInt theInstr )
{
UChar opc2 = get_function(theInstr);
UChar opc1 = get_opcode(theInstr);
UChar regRs = get_rs(theInstr);
UChar regRt = get_rt(theInstr);
UChar regRd = get_rd(theInstr);
UInt imm = get_imm(theInstr);
UChar lenM1 = get_msb(theInstr);
UChar p = get_lsb(theInstr);
IRType ty = mode64? Ity_I64 : Ity_I32;
IRTemp tmp = newTemp(ty);
IRTemp tmpRs = newTemp(ty);
IRTemp tmpRt = newTemp(ty);
IRTemp t1 = newTemp(ty);
UInt size;
assign(tmpRs, getIReg(regRs));
switch(opc1) {
case 0x1C: {
switch(opc2) {
case 0x03: { /* DMUL rd, rs, rt */
DIP("dmul r%d, r%d, r%d", regRd, regRs, regRt);
IRTemp t0 = newTemp(Ity_I128);
assign(t0, binop(Iop_MullU64, getIReg(regRs), getIReg(regRt)));
putIReg(regRd, unop(Iop_128to64, mkexpr(t0)));
break;
}
case 0x18: { /* Store Atomic Add Word - SAA; Cavium OCTEON */
DIP("saa r%u, (r%u)", regRt, regRs);
IRTemp addr = newTemp(Ity_I64);
IRTemp new = newTemp(Ity_I32);
assign (addr, getIReg(regRs));
assign(new, binop(Iop_Add32,
load(Ity_I32, mkexpr(addr)),
mkNarrowTo32(ty, getIReg(regRt))));
mips_irgen_load_and_add32(addr, new, 0, False);
break;
}
/* Store Atomic Add Doubleword - SAAD; Cavium OCTEON */
case 0x19: {
DIP( "saad r%u, (r%u)", regRt, regRs);
IRTemp addr = newTemp(Ity_I64);
IRTemp new = newTemp(Ity_I64);
assign (addr, getIReg(regRs));
assign(new, binop(Iop_Add64,
load(Ity_I64, mkexpr(addr)),
getIReg(regRt)));
mips_irgen_load_and_add64(addr, new, 0, False);
break;
}
/* LAI, LAID, LAD, LADD, LAS, LASD,
LAC, LACD, LAA, LAAD, LAW, LAWD */
case 0x1f: {
UInt opc3 = get_sa(theInstr);
IRTemp addr = newTemp(Ity_I64);
switch (opc3) {
/* Load Atomic Increment Word - LAI; Cavium OCTEON2 */
case 0x02: {
DIP("lai r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I32);
assign(addr, getIReg(regRs));
assign(new, binop(Iop_Add32,
load(Ity_I32, mkexpr(addr)),
mkU32(1)));
mips_irgen_load_and_add32(addr, new, regRd, True);
break;
}
/* Load Atomic Increment Doubleword - LAID; Cavium OCTEON2 */
case 0x03: {
DIP("laid r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I64);
assign(addr, getIReg(regRs));
assign(new, binop(Iop_Add64,
load(Ity_I64, mkexpr(addr)),
mkU64(1)));
mips_irgen_load_and_add64(addr, new, regRd, True);
break;
}
/* Load Atomic Decrement Word - LAD; Cavium OCTEON2 */
case 0x06: {
DIP("lad r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I32);
assign(addr, getIReg(regRs));
assign(new, binop(Iop_Sub32,
load(Ity_I32, mkexpr(addr)),
mkU32(1)));
mips_irgen_load_and_add32(addr, new, regRd, True);
break;
}
/* Load Atomic Decrement Doubleword - LADD; Cavium OCTEON2 */
case 0x07: {
DIP("ladd r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I64);
assign (addr, getIReg(regRs));
assign(new, binop(Iop_Sub64,
load(Ity_I64, mkexpr(addr)),
mkU64(1)));
mips_irgen_load_and_add64(addr, new, regRd, True);
break;
}
/* Load Atomic Set Word - LAS; Cavium OCTEON2 */
case 0x0a: {
DIP("las r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I32);
assign(addr, getIReg(regRs));
assign(new, mkU32(0xffffffff));
mips_irgen_load_and_add32(addr, new, regRd, True);
break;
}
/* Load Atomic Set Doubleword - LASD; Cavium OCTEON2 */
case 0x0b: {
DIP("lasd r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I64);
assign (addr, getIReg(regRs));
assign(new, mkU64(0xffffffffffffffffULL));
mips_irgen_load_and_add64(addr, new, regRd, True);
break;
}
/* Load Atomic Clear Word - LAC; Cavium OCTEON2 */
case 0x0e: {
DIP("lac r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I32);
assign (addr, getIReg(regRs));
assign(new, mkU32(0));
mips_irgen_load_and_add32(addr, new, regRd, True);
break;
}
/* Load Atomic Clear Doubleword - LACD; Cavium OCTEON2 */
case 0x0f: {
DIP("lacd r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I64);
assign(addr, getIReg(regRs));
assign(new, mkU64(0));
mips_irgen_load_and_add64(addr, new, regRd, True);
break;
}
/* Load Atomic Add Word - LAA; Cavium OCTEON2 */
case 0x12: {
DIP("laa r%u,(r%u),r%u\n", regRd, regRs, regRt);
IRTemp new = newTemp(Ity_I32);
assign(addr, getIReg(regRs));
assign(new, binop(Iop_Add32,
load(Ity_I32, mkexpr(addr)),
mkNarrowTo32(ty, getIReg(regRt))));
mips_irgen_load_and_add32(addr, new, regRd, True);
break;
}
/* Load Atomic Add Doubleword - LAAD; Cavium OCTEON2 */
case 0x13: {
DIP("laad r%u,(r%u),r%u\n", regRd, regRs, regRt);
IRTemp new = newTemp(Ity_I64);
assign (addr, getIReg(regRs));
assign(new, binop(Iop_Add64,
load(Ity_I64, mkexpr(addr)),
getIReg(regRt)));
mips_irgen_load_and_add64(addr, new, regRd, True);
break;
}
/* Load Atomic Swap Word - LAW; Cavium OCTEON2 */
case 0x16: {
DIP("law r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I32);
assign(addr, getIReg(regRs));
assign(new, mkNarrowTo32(ty, getIReg(regRt)));
mips_irgen_load_and_add32(addr, new, regRd, True);
break;
}
/* Load Atomic Swap Doubleword - LAWD; Cavium OCTEON2 */
case 0x17: {
DIP("lawd r%u,(r%u)\n", regRd, regRs);
IRTemp new = newTemp(Ity_I64);
assign(addr, getIReg(regRs));
assign(new, getIReg(regRt));
mips_irgen_load_and_add64(addr, new, regRd, True);
break;
}
default:
vex_printf("Unknown laxx instruction, opc3=0x%x\n", opc3);
vex_printf("Instruction=0x%08x\n", theInstr);
return False;
}
break;
}
/* Unsigned Byte Add - BADDU rd, rs, rt; Cavium OCTEON */
case 0x28: {
DIP("BADDU r%d, r%d, r%d", regRs, regRt, regRd);
IRTemp t0 = newTemp(Ity_I8);
assign(t0, binop(Iop_Add8,
mkNarrowTo8(ty, getIReg(regRs)),
mkNarrowTo8(ty, getIReg(regRt))));
if (mode64)
putIReg(regRd, binop(mkSzOp(ty, Iop_And8),
unop(Iop_8Uto64, mkexpr(t0)),
mkSzImm(ty, 0xFF)));
else
putIReg(regRd, binop(mkSzOp(ty, Iop_And8),
unop(Iop_8Uto32, mkexpr(t0)),
mkSzImm(ty, 0xFF)));
break;
}
case 0x2c: { /* Count Ones in a Word - POP; Cavium OCTEON */
int i, shift[5];
IRTemp mask[5];
IRTemp old = newTemp(ty);
IRTemp nyu = IRTemp_INVALID;
assign(old, getIReg(regRs));
DIP("pop r%d, r%d", regRd, regRs);
for (i = 0; i < 5; i++) {
mask[i] = newTemp(ty);
shift[i] = 1 << i;
}
if(mode64) {
assign(mask[0], mkU64(0x0000000055555555));
assign(mask[1], mkU64(0x0000000033333333));
assign(mask[2], mkU64(0x000000000F0F0F0F));
assign(mask[3], mkU64(0x0000000000FF00FF));
assign(mask[4], mkU64(0x000000000000FFFF));
for (i = 0; i < 5; i++) {
nyu = newTemp(ty);
assign(nyu,
binop(Iop_Add64,
binop(Iop_And64,
mkexpr(old), mkexpr(mask[i])),
binop(Iop_And64,
binop(Iop_Shr64,
mkexpr(old), mkU8(shift[i])),
mkexpr(mask[i]))));
old = nyu;
}
} else {
assign(mask[0], mkU32(0x55555555));
assign(mask[1], mkU32(0x33333333));
assign(mask[2], mkU32(0x0F0F0F0F));
assign(mask[3], mkU32(0x00FF00FF));
assign(mask[4], mkU32(0x0000FFFF));
assign(old, getIReg(regRs));
for (i = 0; i < 5; i++) {
nyu = newTemp(ty);
assign(nyu,
binop(Iop_Add32,
binop(Iop_And32,
mkexpr(old), mkexpr(mask[i])),
binop(Iop_And32,
binop(Iop_Shr32,
mkexpr(old), mkU8(shift[i])),
mkexpr(mask[i]))));
old = nyu;
}
}
putIReg(regRd, mkexpr(nyu));
break;
}
/* Count Ones in a Doubleword - DPOP; Cavium OCTEON */
case 0x2d: {
int i, shift[6];
IRTemp mask[6];
IRTemp old = newTemp(ty);
IRTemp nyu = IRTemp_INVALID;
DIP("dpop r%d, r%d", regRd, regRs);
for (i = 0; i < 6; i++) {
mask[i] = newTemp(ty);
shi