blob: c3bbd9a10f8bd63dba6697a196b93e883543cecb [file] [log] [blame]
/*---------------------------------------------------------------*/
/*--- begin host_mips_isel.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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
The GNU General Public License is contained in the file COPYING.
*/
#include "libvex_basictypes.h"
#include "libvex_ir.h"
#include "libvex.h"
#include "main_util.h"
#include "main_globals.h"
#include "host_generic_regs.h"
#include "host_generic_simd64.h" /* for 64-bit SIMD helpers */
#include "host_mips_defs.h"
/*---------------------------------------------------------*/
/*--- Register Usage Conventions ---*/
/*---------------------------------------------------------*/
/* Integer Regs
------------
ZERO0 Reserved
GPR12:22 Allocateable
23 GuestStatePointer
SP StackFramePointer
RA LinkRegister */
static Bool mode64 = False;
/* Host CPU has FPU and 32 dbl. prec. FP registers. */
static Bool fp_mode64 = False;
/* GPR register class for mips32/64 */
#define HRcGPR(__mode64) (__mode64 ? HRcInt64 : HRcInt32)
/* FPR register class for mips32/64 */
#define HRcFPR(__mode64) (__mode64 ? HRcFlt64 : HRcFlt32)
/* guest_COND offset */
#define COND_OFFSET(__mode64) (__mode64 ? 612 : 448)
/*---------------------------------------------------------*/
/*--- ISelEnv ---*/
/*---------------------------------------------------------*/
/* This carries around:
- A mapping from IRTemp to IRType, giving the type of any IRTemp we
might encounter. This is computed before insn selection starts,
and does not change.
- A mapping from IRTemp to HReg. This tells the insn selector
which virtual register(s) are associated with each IRTemp
temporary. This is computed before insn selection starts, and
does not change. We expect this mapping to map precisely the
same set of IRTemps as the type mapping does.
- vregmap holds the primary register for the IRTemp.
- vregmapHI is only used for 64-bit integer-typed
IRTemps. It holds the identity of a second
32-bit virtual HReg, which holds the high half
of the value.
- The code array, that is, the insns selected so far.
- A counter, for generating new virtual registers.
- The host subarchitecture we are selecting insns for.
This is set at the start and does not change.
- A Bool for indicating whether we may generate chain-me
instructions for control flow transfers, or whether we must use
XAssisted.
- The maximum guest address of any guest insn in this block.
Actually, the address of the highest-addressed byte from any insn
in this block. Is set at the start and does not change. This is
used for detecting jumps which are definitely forward-edges from
this block, and therefore can be made (chained) to the fast entry
point of the destination, thereby avoiding the destination's
event check.
Note, this is all (well, mostly) host-independent.
*/
typedef
struct {
/* Constant -- are set at the start and do not change. */
IRTypeEnv* type_env;
HReg* vregmap;
HReg* vregmapHI;
Int n_vregmap;
UInt hwcaps;
Bool mode64;
Bool fp_mode64;
Bool chainingAllowed;
Addr64 max_ga;
/* These are modified as we go along. */
HInstrArray* code;
Int vreg_ctr;
}
ISelEnv;
static HReg lookupIRTemp(ISelEnv * env, IRTemp tmp)
{
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
return env->vregmap[tmp];
}
static void lookupIRTemp64(HReg * vrHI, HReg * vrLO, ISelEnv * env, IRTemp tmp)
{
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
vassert(! hregIsInvalid(env->vregmapHI[tmp]));
*vrLO = env->vregmap[tmp];
*vrHI = env->vregmapHI[tmp];
}
static void
lookupIRTempPair(HReg * vrHI, HReg * vrLO, ISelEnv * env, IRTemp tmp)
{
vassert(env->mode64);
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
vassert(! hregIsInvalid(env->vregmapHI[tmp]));
*vrLO = env->vregmap[tmp];
*vrHI = env->vregmapHI[tmp];
}
static void addInstr(ISelEnv * env, MIPSInstr * instr)
{
addHInstr(env->code, instr);
if (vex_traceflags & VEX_TRACE_VCODE) {
ppMIPSInstr(instr, mode64);
vex_printf("\n");
}
}
static HReg newVRegI(ISelEnv * env)
{
HReg reg = mkHReg(env->vreg_ctr, HRcGPR(env->mode64),
True /*virtual reg */ );
env->vreg_ctr++;
return reg;
}
static HReg newVRegD(ISelEnv * env)
{
HReg reg = mkHReg(env->vreg_ctr, HRcFlt64, True /*virtual reg */ );
env->vreg_ctr++;
return reg;
}
static HReg newVRegF(ISelEnv * env)
{
HReg reg = mkHReg(env->vreg_ctr, HRcFPR(env->fp_mode64),
True /*virtual reg */ );
env->vreg_ctr++;
return reg;
}
static void add_to_sp(ISelEnv * env, UInt n)
{
HReg sp = StackPointer(mode64);
vassert(n < 256 && (n % 8) == 0);
if (mode64)
addInstr(env, MIPSInstr_Alu(Malu_DADD, sp, sp, MIPSRH_Imm(True,
toUShort(n))));
else
addInstr(env, MIPSInstr_Alu(Malu_ADD, sp, sp, MIPSRH_Imm(True,
toUShort(n))));
}
static void sub_from_sp(ISelEnv * env, UInt n)
{
HReg sp = StackPointer(mode64);
vassert(n < 256 && (n % 8) == 0);
if (mode64)
addInstr(env, MIPSInstr_Alu(Malu_DSUB, sp, sp,
MIPSRH_Imm(True, toUShort(n))));
else
addInstr(env, MIPSInstr_Alu(Malu_SUB, sp, sp,
MIPSRH_Imm(True, toUShort(n))));
}
/*---------------------------------------------------------*/
/*--- ISEL: Forward declarations ---*/
/*---------------------------------------------------------*/
/* These are organised as iselXXX and iselXXX_wrk pairs. The
iselXXX_wrk do the real work, but are not to be called directly.
For each XXX, iselXXX calls its iselXXX_wrk counterpart, then
checks that all returned registers are virtual. You should not
call the _wrk version directly.
*/
/* 32-bit mode: Compute an I8/I16/I32 into a RH
(reg-or-halfword-immediate).
It's important to specify whether the immediate is to be regarded
as signed or not. If yes, this will never return -32768 as an
immediate; this guaranteed that all signed immediates that are
return can have their sign inverted if need be.
*/
static MIPSRH *iselWordExpr_RH_wrk(ISelEnv * env, Bool syned, IRExpr * e);
static MIPSRH *iselWordExpr_RH(ISelEnv * env, Bool syned, IRExpr * e);
/* Compute an I8 into a reg-or-5-bit-unsigned-immediate, the latter being an
immediate in the range 1 .. 31 inclusive. Used for doing shift amounts. */
static MIPSRH *iselWordExpr_RH5u_wrk(ISelEnv * env, IRExpr * e);
static MIPSRH *iselWordExpr_RH5u(ISelEnv * env, IRExpr * e);
/* Compute an I8 into a reg-or-6-bit-unsigned-immediate, the latter being an
immediate in the range 1 .. 63 inclusive. Used for doing shift amounts. */
static MIPSRH *iselWordExpr_RH6u_wrk(ISelEnv * env, IRExpr * e);
static MIPSRH *iselWordExpr_RH6u(ISelEnv * env, IRExpr * e);
/* compute an I8/I16/I32 into a GPR*/
static HReg iselWordExpr_R_wrk(ISelEnv * env, IRExpr * e);
static HReg iselWordExpr_R(ISelEnv * env, IRExpr * e);
/* compute an I32 into an AMode. */
static MIPSAMode *iselWordExpr_AMode_wrk(ISelEnv * env, IRExpr * e,
IRType xferTy);
static MIPSAMode *iselWordExpr_AMode(ISelEnv * env, IRExpr * e, IRType xferTy);
static void iselInt64Expr_wrk(HReg * rHi, HReg * rLo, ISelEnv * env,
IRExpr * e);
static void iselInt64Expr(HReg * rHi, HReg * rLo, ISelEnv * env, IRExpr * e);
/* 64-bit mode ONLY: compute an I128 into a GPR64 pair. */
static void iselInt128Expr_wrk(HReg * rHi, HReg * rLo,
ISelEnv * env, IRExpr * e);
static void iselInt128Expr(HReg * rHi, HReg * rLo, ISelEnv * env, IRExpr * e);
static MIPSCondCode iselCondCode_wrk(ISelEnv * env, IRExpr * e);
static MIPSCondCode iselCondCode(ISelEnv * env, IRExpr * e);
static HReg iselDblExpr_wrk(ISelEnv * env, IRExpr * e);
static HReg iselDblExpr(ISelEnv * env, IRExpr * e);
static HReg iselFltExpr_wrk(ISelEnv * env, IRExpr * e);
static HReg iselFltExpr(ISelEnv * env, IRExpr * e);
static void set_MIPS_rounding_mode(ISelEnv * env, IRExpr * mode)
{
/*
rounding mode | MIPS | IR
------------------------
to nearest | 00 | 00
to zero | 01 | 11
to +infinity | 10 | 10
to -infinity | 11 | 01
*/
/* rm_MIPS32 = XOR(rm_IR , (rm_IR << 1)) & 2 */
HReg irrm = iselWordExpr_R(env, mode);
HReg tmp = newVRegI(env);
HReg fcsr_old = newVRegI(env);
MIPSAMode *am_addr;
addInstr(env, MIPSInstr_Shft(Mshft_SLL, True, tmp, irrm,
MIPSRH_Imm(False, 1)));
addInstr(env, MIPSInstr_Alu(Malu_XOR, tmp, irrm, MIPSRH_Reg(tmp)));
addInstr(env, MIPSInstr_Alu(Malu_AND, irrm, tmp, MIPSRH_Imm(False, 3)));
/* save old value of FCSR */
addInstr(env, MIPSInstr_MfFCSR(fcsr_old));
sub_from_sp(env, 8); /* Move SP down 8 bytes */
am_addr = MIPSAMode_IR(0, StackPointer(mode64));
/* store old FCSR to stack */
addInstr(env, MIPSInstr_Store(4, am_addr, fcsr_old, mode64));
/* set new value of FCSR */
addInstr(env, MIPSInstr_MtFCSR(irrm));
}
static void set_MIPS_rounding_default(ISelEnv * env)
{
HReg fcsr = newVRegI(env);
/* load as float */
MIPSAMode *am_addr;
am_addr = MIPSAMode_IR(0, StackPointer(mode64));
addInstr(env, MIPSInstr_Load(4, fcsr, am_addr, mode64));
add_to_sp(env, 8); /* Reset SP */
/* set new value of FCSR*/
addInstr(env, MIPSInstr_MtFCSR(fcsr));
}
/*---------------------------------------------------------*/
/*--- ISEL: Misc helpers ---*/
/*---------------------------------------------------------*/
/* Make an int reg-reg move. */
static MIPSInstr *mk_iMOVds_RR(HReg r_dst, HReg r_src)
{
vassert(hregClass(r_dst) == hregClass(r_src));
vassert(hregClass(r_src) == HRcInt32 || hregClass(r_src) == HRcInt64);
return MIPSInstr_Alu(Malu_OR, r_dst, r_src, MIPSRH_Reg(r_src));
}
/*---------------------------------------------------------*/
/*--- ISEL: Function call helpers ---*/
/*---------------------------------------------------------*/
/* Used only in doHelperCall. See big comment in doHelperCall re
handling of register-parameter args. This function figures out
whether evaluation of an expression might require use of a fixed
register. If in doubt return True (safe but suboptimal).
*/
static Bool mightRequireFixedRegs(IRExpr * e)
{
switch (e->tag) {
case Iex_RdTmp:
case Iex_Const:
case Iex_Get:
return False;
default:
return True;
}
}
/* Load 2*I32 regs to fp reg */
static HReg mk_LoadRR32toFPR(ISelEnv * env, HReg r_srcHi, HReg r_srcLo)
{
HReg fr_dst = newVRegD(env);
MIPSAMode *am_addr0, *am_addr1;
vassert(hregClass(r_srcHi) == HRcInt32);
vassert(hregClass(r_srcLo) == HRcInt32);
sub_from_sp(env, 16); /* Move SP down 16 bytes */
am_addr0 = MIPSAMode_IR(0, StackPointer(mode64));
am_addr1 = MIPSAMode_IR(4, StackPointer(mode64));
/* store hi,lo as Ity_I32's */
#if defined (_MIPSEL)
addInstr(env, MIPSInstr_Store(4, am_addr0, r_srcLo, mode64));
addInstr(env, MIPSInstr_Store(4, am_addr1, r_srcHi, mode64));
#elif defined (_MIPSEB)
addInstr(env, MIPSInstr_Store(4, am_addr0, r_srcHi, mode64));
addInstr(env, MIPSInstr_Store(4, am_addr1, r_srcLo, mode64));
#else
/* Stop gcc on other platforms complaining about am_addr1 being set
but not used. */
(void)am_addr1;
#endif
/* load as float */
addInstr(env, MIPSInstr_FpLdSt(True /*load */ , 8, fr_dst, am_addr0));
add_to_sp(env, 16); /* Reset SP */
return fr_dst;
}
/* Do a complete function call. |guard| is a Ity_Bit expression
indicating whether or not the call happens. If guard==NULL, the
call is unconditional. |retloc| is set to indicate where the
return value is after the call. The caller (of this fn) must
generate code to add |stackAdjustAfterCall| to the stack pointer
after the call is done. */
static void doHelperCall(/*OUT*/UInt* stackAdjustAfterCall,
/*OUT*/RetLoc* retloc,
ISelEnv* env,
IRExpr* guard,
IRCallee* cee, IRType retTy, IRExpr** args )
{
MIPSCondCode cc;
HReg argregs[MIPS_N_REGPARMS];
HReg tmpregs[MIPS_N_REGPARMS];
Bool go_fast;
Int n_args, i, argreg;
UInt argiregs;
HReg src = INVALID_HREG;
/* Set default returns. We'll update them later if needed. */
*stackAdjustAfterCall = 0;
*retloc = mk_RetLoc_INVALID();
/* These are used for cross-checking that IR-level constraints on
the use of IRExpr_VECRET() and IRExpr_BBPTR() are observed. */
UInt nVECRETs = 0;
UInt nBBPTRs = 0;
/* MIPS O32 calling convention: up to four registers ($a0 ... $a3)
are allowed to be used for passing integer arguments. They correspond
to regs GPR4 ... GPR7. Note that the cee->regparms field is meaningless
on MIPS host (since we only implement one calling convention) and so we
always ignore it. */
/* MIPS 64 calling convention: up to four registers ($a0 ... $a7)
are allowed to be used for passing integer arguments. They correspond
to regs GPR4 ... GPR11. Note that the cee->regparms field is meaningless
on MIPS host (since we only implement one calling convention) and so we
always ignore it. */
/* The return type can be I{64,32,16,8} or V{128,256}. In the
latter two cases, it is expected that |args| will contain the
special node IRExpr_VECRET(), in which case this routine
generates code to allocate space on the stack for the vector
return value. Since we are not passing any scalars on the
stack, it is enough to preallocate the return space before
marshalling any arguments, in this case.
|args| may also contain IRExpr_BBPTR(), in which case the value
in the guest state pointer register is passed as the
corresponding argument. */
n_args = 0;
for (i = 0; args[i]; i++) {
IRExpr* arg = args[i];
if (UNLIKELY(arg->tag == Iex_VECRET)) {
nVECRETs++;
} else if (UNLIKELY(arg->tag == Iex_BBPTR)) {
nBBPTRs++;
}
n_args++;
}
if (n_args > MIPS_N_REGPARMS) {
vpanic("doHelperCall(MIPS): cannot currently handle > 4 or 8 args");
}
if (mode64) {
argregs[0] = hregMIPS_GPR4(mode64);
argregs[1] = hregMIPS_GPR5(mode64);
argregs[2] = hregMIPS_GPR6(mode64);
argregs[3] = hregMIPS_GPR7(mode64);
argregs[4] = hregMIPS_GPR8(mode64);
argregs[5] = hregMIPS_GPR9(mode64);
argregs[6] = hregMIPS_GPR10(mode64);
argregs[7] = hregMIPS_GPR11(mode64);
argiregs = 0;
tmpregs[0] = tmpregs[1] = tmpregs[2] =
tmpregs[3] = tmpregs[4] = tmpregs[5] =
tmpregs[6] = tmpregs[7] = INVALID_HREG;
} else {
argregs[0] = hregMIPS_GPR4(mode64);
argregs[1] = hregMIPS_GPR5(mode64);
argregs[2] = hregMIPS_GPR6(mode64);
argregs[3] = hregMIPS_GPR7(mode64);
argiregs = 0;
tmpregs[0] = tmpregs[1] = tmpregs[2] = tmpregs[3] = INVALID_HREG;
}
/* First decide which scheme (slow or fast) is to be used. First assume the
fast scheme, and select slow if any contraindications (wow) appear. */
go_fast = True;
/* We'll need space on the stack for the return value. Avoid
possible complications with nested calls by using the slow
scheme. */
if (retTy == Ity_V128 || retTy == Ity_V256)
go_fast = False;
if (go_fast && guard) {
if (guard->tag == Iex_Const && guard->Iex.Const.con->tag == Ico_U1
&& guard->Iex.Const.con->Ico.U1 == True) {
/* unconditional */
} else {
/* Not manifestly unconditional -- be conservative. */
go_fast = False;
}
}
if (go_fast) {
for (i = 0; i < n_args; i++) {
if (mightRequireFixedRegs(args[i])) {
go_fast = False;
break;
}
}
}
/* At this point the scheme to use has been established. Generate
code to get the arg values into the argument rregs. */
if (go_fast) {
/* FAST SCHEME */
argreg = 0;
for (i = 0; i < n_args; i++) {
IRExpr* arg = args[i];
vassert(argreg < MIPS_N_REGPARMS);
IRType aTy = Ity_INVALID;
if (LIKELY(!is_IRExpr_VECRET_or_BBPTR(arg)))
aTy = typeOfIRExpr(env->type_env, arg);
if (aTy == Ity_I32 || mode64) {
argiregs |= (1 << (argreg + 4));
addInstr(env, mk_iMOVds_RR(argregs[argreg],
iselWordExpr_R(env, arg)));
argreg++;
} else if (aTy == Ity_I64) { /* Ity_I64 */
if (argreg & 1) {
argreg++;
argiregs |= (1 << (argreg + 4));
}
HReg rHi, rLo;
iselInt64Expr(&rHi, &rLo, env, arg);
argiregs |= (1 << (argreg + 4));
addInstr(env, mk_iMOVds_RR( argregs[argreg++], rHi ));
argiregs |= (1 << (argreg + 4));
addInstr(env, mk_iMOVds_RR( argregs[argreg], rLo));
argreg++;
} else if (arg->tag == Iex_BBPTR) {
vassert(0); // ATC
addInstr(env, mk_iMOVds_RR(argregs[argreg],
GuestStatePointer(mode64)));
argreg++;
} else if (arg->tag == Iex_VECRET) {
// If this happens, it denotes ill-formed IR.
vassert(0);
}
}
/* Fast scheme only applies for unconditional calls. Hence: */
cc = MIPScc_AL;
} else {
/* SLOW SCHEME; move via temporaries */
argreg = 0;
for (i = 0; i < n_args; i++) {
vassert(argreg < MIPS_N_REGPARMS);
IRExpr* arg = args[i];
IRType aTy = Ity_INVALID;
if (LIKELY(!is_IRExpr_VECRET_or_BBPTR(arg)))
aTy = typeOfIRExpr(env->type_env, arg);
if (aTy == Ity_I32 || (mode64 && arg->tag != Iex_BBPTR)) {
tmpregs[argreg] = iselWordExpr_R(env, arg);
argreg++;
} else if (aTy == Ity_I64) { /* Ity_I64 */
if (argreg & 1)
argreg++;
if (argreg + 1 >= MIPS_N_REGPARMS)
vassert(0); /* out of argregs */
HReg raHi, raLo;
iselInt64Expr(&raHi, &raLo, env, arg);
tmpregs[argreg] = raLo;
argreg++;
tmpregs[argreg] = raHi;
argreg++;
} else if (arg->tag == Iex_BBPTR) {
tmpregs[argreg] = GuestStatePointer(mode64);
argreg++;
}
else if (arg->tag == Iex_VECRET) {
// If this happens, it denotes ill-formed IR
vassert(0);
}
}
/* Now we can compute the condition. We can't do it earlier
because the argument computations could trash the condition
codes. Be a bit clever to handle the common case where the
guard is 1:Bit. */
cc = MIPScc_AL;
if (guard) {
if (guard->tag == Iex_Const && guard->Iex.Const.con->tag == Ico_U1
&& guard->Iex.Const.con->Ico.U1 == True) {
/* unconditional -- do nothing */
} else {
cc = iselCondCode(env, guard);
src = iselWordExpr_R(env, guard);
}
}
/* Move the args to their final destinations. */
for (i = 0; i < argreg; i++) {
if (hregIsInvalid(tmpregs[i])) /* Skip invalid regs */
continue;
/* None of these insns, including any spill code that might
be generated, may alter the condition codes. */
argiregs |= (1 << (i + 4));
addInstr(env, mk_iMOVds_RR(argregs[i], tmpregs[i]));
}
}
/* Do final checks, set the return values, and generate the call
instruction proper. */
vassert(nBBPTRs == 0 || nBBPTRs == 1);
vassert(nVECRETs == (retTy == Ity_V128 || retTy == Ity_V256) ? 1 : 0);
vassert(*stackAdjustAfterCall == 0);
vassert(is_RetLoc_INVALID(*retloc));
switch (retTy) {
case Ity_INVALID:
/* Function doesn't return a value. */
*retloc = mk_RetLoc_simple(RLPri_None);
break;
case Ity_I64:
*retloc = mk_RetLoc_simple(mode64 ? RLPri_Int : RLPri_2Int);
break;
case Ity_I32: case Ity_I16: case Ity_I8:
*retloc = mk_RetLoc_simple(RLPri_Int);
break;
case Ity_V128:
vassert(0); // ATC
*retloc = mk_RetLoc_spRel(RLPri_V128SpRel, 0);
*stackAdjustAfterCall = 16;
break;
case Ity_V256:
vassert(0); // ATC
*retloc = mk_RetLoc_spRel(RLPri_V256SpRel, 0);
*stackAdjustAfterCall = 32;
break;
default:
/* IR can denote other possible return types, but we don't
handle those here. */
vassert(0);
}
ULong target = mode64 ? Ptr_to_ULong(cee->addr) :
toUInt(Ptr_to_ULong(cee->addr));
/* Finally, generate the call itself. This needs the *retloc value
set in the switch above, which is why it's at the end. */
if (cc == MIPScc_AL)
addInstr(env, MIPSInstr_CallAlways(cc, (Addr64)target, argiregs,
*retloc));
else
addInstr(env, MIPSInstr_Call(cc, (Addr64)target, argiregs, src, *retloc));
}
/*---------------------------------------------------------*/
/*--- ISEL: Integer expression auxiliaries ---*/
/*---------------------------------------------------------*/
/* --------------------- AMODEs --------------------- */
/* Return an AMode which computes the value of the specified
expression, possibly also adding insns to the code list as a
result. The expression may only be a word-size one.
*/
static Bool uInt_fits_in_16_bits(UInt u)
{
Int i = u & 0xFFFF;
i <<= 16;
i >>= 16;
return toBool(u == (UInt) i);
}
static Bool uLong_fits_in_16_bits ( ULong u )
{
Long i = u & 0xFFFFULL;
i <<= 48;
i >>= 48;
return toBool(u == (ULong) i);
}
static Bool uLong_is_4_aligned ( ULong u )
{
return toBool((u & 3ULL) == 0);
}
static Bool sane_AMode(ISelEnv * env, MIPSAMode * am)
{
switch (am->tag) {
case Mam_IR:
return toBool(hregClass(am->Mam.IR.base) == HRcGPR(mode64) &&
hregIsVirtual(am->Mam.IR.base) &&
uInt_fits_in_16_bits(am->Mam.IR.index));
case Mam_RR:
return toBool(hregClass(am->Mam.RR.base) == HRcGPR(mode64) &&
hregIsVirtual(am->Mam.RR.base) &&
hregClass(am->Mam.RR.index) == HRcGPR(mode64) &&
hregIsVirtual(am->Mam.RR.index));
default:
vpanic("sane_AMode: unknown mips amode tag");
}
}
static MIPSAMode *iselWordExpr_AMode(ISelEnv * env, IRExpr * e, IRType xferTy)
{
MIPSAMode *am = iselWordExpr_AMode_wrk(env, e, xferTy);
vassert(sane_AMode(env, am));
return am;
}
/* DO NOT CALL THIS DIRECTLY ! */
static MIPSAMode *iselWordExpr_AMode_wrk(ISelEnv * env, IRExpr * e,
IRType xferTy)
{
IRType ty = typeOfIRExpr(env->type_env, e);
if (env->mode64) {
Bool aligned4imm = toBool(xferTy == Ity_I32 || xferTy == Ity_I64);
vassert(ty == Ity_I64);
/* Add64(expr,i), where i == sign-extend of (i & 0xFFFF) */
if (e->tag == Iex_Binop && e->Iex.Binop.op == Iop_Add64
&& e->Iex.Binop.arg2->tag == Iex_Const
&& e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U64
&& (aligned4imm ?
uLong_is_4_aligned(e->Iex.Binop.arg2->Iex.Const.con->Ico.U64) : True)
&& uLong_fits_in_16_bits(e->Iex.Binop.arg2->Iex.Const.con->Ico.U64)) {
return MIPSAMode_IR((Int) e->Iex.Binop.arg2->Iex.Const.con->Ico.U64,
iselWordExpr_R(env, e->Iex.Binop.arg1));
}
/* Add64(expr,expr) */
if (e->tag == Iex_Binop && e->Iex.Binop.op == Iop_Add64) {
HReg r_base = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r_idx = iselWordExpr_R(env, e->Iex.Binop.arg2);
return MIPSAMode_RR(r_idx, r_base);
}
} else {
vassert(ty == Ity_I32);
/* Add32(expr,i), where i == sign-extend of (i & 0xFFFF) */
if (e->tag == Iex_Binop
&& e->Iex.Binop.op == Iop_Add32
&& e->Iex.Binop.arg2->tag == Iex_Const
&& e->Iex.Binop.arg2->Iex.Const.con->tag == Ico_U32
&& uInt_fits_in_16_bits(e->Iex.Binop.arg2->Iex.Const.con-> Ico.U32)) {
return MIPSAMode_IR((Int) e->Iex.Binop.arg2->Iex.Const.con->Ico.U32,
iselWordExpr_R(env, e->Iex.Binop.arg1));
}
/* Add32(expr,expr) */
if (e->tag == Iex_Binop && e->Iex.Binop.op == Iop_Add32) {
HReg r_base = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r_idx = iselWordExpr_R(env, e->Iex.Binop.arg2);
return MIPSAMode_RR(r_idx, r_base);
}
}
/* Doesn't match anything in particular. Generate it into
a register and use that. */
return MIPSAMode_IR(0, iselWordExpr_R(env, e));
}
/*---------------------------------------------------------*/
/*--- ISEL: Integer expressions (64/32/16/8 bit) ---*/
/*---------------------------------------------------------*/
/* Select insns for an integer-typed expression, and add them to the
code list. Return a reg holding the result. This reg will be a
virtual register. THE RETURNED REG MUST NOT BE MODIFIED. If you
want to modify it, ask for a new vreg, copy it in there, and modify
the copy. The register allocator will do its best to map both
vregs to the same real register, so the copies will often disappear
later in the game.
This should handle expressions of 64, 32, 16 and 8-bit type.
All results are returned in a (mode64 ? 64bit : 32bit) register.
For 16- and 8-bit expressions, the upper (32/48/56 : 16/24) bits
are arbitrary, so you should mask or sign extend partial values
if necessary.
*/
static HReg iselWordExpr_R(ISelEnv * env, IRExpr * e)
{
HReg r = iselWordExpr_R_wrk(env, e);
/* sanity checks ... */
vassert(hregClass(r) == HRcGPR(env->mode64));
vassert(hregIsVirtual(r));
return r;
}
/* DO NOT CALL THIS DIRECTLY ! */
static HReg iselWordExpr_R_wrk(ISelEnv * env, IRExpr * e)
{
UInt argiregs = 0;
IRType ty = typeOfIRExpr(env->type_env, e);
vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32 || ty == Ity_I1
|| ty == Ity_F32 || (ty == Ity_I64 && mode64)
|| (ty == Ity_I128 && mode64));
switch (e->tag) {
/* --------- TEMP --------- */
case Iex_RdTmp:
return lookupIRTemp(env, e->Iex.RdTmp.tmp);
/* --------- LOAD --------- */
case Iex_Load: {
HReg r_dst = newVRegI(env);
MIPSAMode *am_addr = iselWordExpr_AMode(env, e->Iex.Load.addr, ty);
if (e->Iex.Load.end != Iend_LE
&& e->Iex.Load.end != Iend_BE)
goto irreducible;
addInstr(env, MIPSInstr_Load(toUChar(sizeofIRType(ty)),
r_dst, am_addr, mode64));
return r_dst;
}
/* --------- BINARY OP --------- */
case Iex_Binop: {
MIPSAluOp aluOp;
MIPSShftOp shftOp;
/* Is it an addition or logical style op? */
switch (e->Iex.Binop.op) {
case Iop_Add8:
case Iop_Add16:
case Iop_Add32:
aluOp = Malu_ADD;
break;
case Iop_Sub8:
case Iop_Sub16:
case Iop_Sub32:
aluOp = Malu_SUB;
break;
case Iop_Sub64:
aluOp = Malu_DSUB;
break;
case Iop_And8:
case Iop_And16:
case Iop_And32:
case Iop_And64:
aluOp = Malu_AND;
break;
case Iop_Or8:
case Iop_Or16:
case Iop_Or32:
case Iop_Or64:
aluOp = Malu_OR;
break;
case Iop_Xor8:
case Iop_Xor16:
case Iop_Xor32:
case Iop_Xor64:
aluOp = Malu_XOR;
break;
case Iop_Add64:
aluOp = Malu_DADD;
break;
default:
aluOp = Malu_INVALID;
break;
}
/* For commutative ops we assume any literal
values are on the second operand. */
if (aluOp != Malu_INVALID) {
HReg r_dst = newVRegI(env);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
MIPSRH *ri_srcR = NULL;
/* get right arg into an RH, in the appropriate way */
switch (aluOp) {
case Malu_ADD:
case Malu_SUB:
case Malu_DADD:
case Malu_DSUB:
ri_srcR = iselWordExpr_RH(env, True /*signed */ ,
e->Iex.Binop.arg2);
break;
case Malu_AND:
case Malu_OR:
case Malu_XOR:
ri_srcR = iselWordExpr_RH(env, False /*unsigned */,
e->Iex.Binop.arg2);
break;
default:
vpanic("iselWordExpr_R_wrk-aluOp-arg2");
}
addInstr(env, MIPSInstr_Alu(aluOp, r_dst, r_srcL, ri_srcR));
return r_dst;
}
/* a shift? */
switch (e->Iex.Binop.op) {
case Iop_Shl32:
case Iop_Shl64:
shftOp = Mshft_SLL;
break;
case Iop_Shr32:
case Iop_Shr64:
shftOp = Mshft_SRL;
break;
case Iop_Sar32:
case Iop_Sar64:
shftOp = Mshft_SRA;
break;
default:
shftOp = Mshft_INVALID;
break;
}
/* we assume any literal values are on the second operand. */
if (shftOp != Mshft_INVALID) {
HReg r_dst = newVRegI(env);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
MIPSRH *ri_srcR;
if (mode64)
ri_srcR = iselWordExpr_RH6u(env, e->Iex.Binop.arg2);
else
ri_srcR = iselWordExpr_RH5u(env, e->Iex.Binop.arg2);
if (ty == Ity_I8) {
vassert(0);
} else if (ty == Ity_I32) {
if (mode64 && (shftOp == Mshft_SRA || shftOp == Mshft_SRL)) {
HReg tmp = newVRegI(env);
HReg r_srcL_se = newVRegI(env);
/* SRA, SRAV, SRL, SRLV: On 64-bit processors, if GPR rt does
not contain a sign-extended 32-bit value (bits 63..31
equal), then the result of the operation is UNPREDICTABLE.
So we need to sign-extend r_srcL:
DSLLV tmp, r_srcL, 32
DSRAV r_srcL_se, tmp, 32
*/
addInstr(env, MIPSInstr_Shft(Mshft_SLL, False, tmp,
r_srcL, MIPSRH_Imm(False, 32)));
addInstr(env, MIPSInstr_Shft(Mshft_SRA, False, r_srcL_se,
tmp, MIPSRH_Imm(False, 32)));
/* And finally do the shift. */
addInstr(env, MIPSInstr_Shft(shftOp, True /*32bit shift */,
r_dst, r_srcL_se, ri_srcR));
} else
addInstr(env, MIPSInstr_Shft(shftOp, True /*32bit shift */,
r_dst, r_srcL, ri_srcR));
} else if (ty == Ity_I64) {
vassert(mode64);
addInstr(env, MIPSInstr_Shft(shftOp, False/*64bit shift */,
r_dst, r_srcL, ri_srcR));
} else
goto irreducible;
return r_dst;
}
/* Cmp*32*(x,y) ? */
if (e->Iex.Binop.op == Iop_CmpEQ32
|| e->Iex.Binop.op == Iop_CmpEQ16
|| e->Iex.Binop.op == Iop_CmpNE32
|| e->Iex.Binop.op == Iop_CmpNE64
|| e->Iex.Binop.op == Iop_CmpLT32S
|| e->Iex.Binop.op == Iop_CmpLT32U
|| e->Iex.Binop.op == Iop_CmpLT64U
|| e->Iex.Binop.op == Iop_CmpLE32U
|| e->Iex.Binop.op == Iop_CmpLE32S
|| e->Iex.Binop.op == Iop_CmpLE64S
|| e->Iex.Binop.op == Iop_CmpLT64S
|| e->Iex.Binop.op == Iop_CmpEQ64
|| e->Iex.Binop.op == Iop_CasCmpEQ32
|| e->Iex.Binop.op == Iop_CasCmpEQ64) {
Bool syned = (e->Iex.Binop.op == Iop_CmpLT32S
|| e->Iex.Binop.op == Iop_CmpLE32S
|| e->Iex.Binop.op == Iop_CmpLT64S
|| e->Iex.Binop.op == Iop_CmpLE64S);
Bool size32;
HReg dst = newVRegI(env);
HReg r1 = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r2 = iselWordExpr_R(env, e->Iex.Binop.arg2);
MIPSCondCode cc;
switch (e->Iex.Binop.op) {
case Iop_CmpEQ32:
case Iop_CasCmpEQ32:
cc = MIPScc_EQ;
size32 = True;
break;
case Iop_CmpEQ16:
cc = MIPScc_EQ;
size32 = True;
break;
case Iop_CmpNE32:
cc = MIPScc_NE;
size32 = True;
break;
case Iop_CmpNE64:
cc = MIPScc_NE;
size32 = True;
break;
case Iop_CmpLT32S:
cc = MIPScc_LT;
size32 = True;
break;
case Iop_CmpLT32U:
cc = MIPScc_LO;
size32 = True;
break;
case Iop_CmpLT64U:
cc = MIPScc_LO;
size32 = False;
break;
case Iop_CmpLE32U:
cc = MIPScc_LE;
size32 = True;
break;
case Iop_CmpLE32S:
cc = MIPScc_LE;
size32 = True;
break;
case Iop_CmpLE64S:
cc = MIPScc_LE;
size32 = False;
break;
case Iop_CmpLT64S:
cc = MIPScc_LT;
size32 = False;
break;
case Iop_CmpEQ64:
case Iop_CasCmpEQ64:
cc = MIPScc_EQ;
size32 = False;
break;
default:
vpanic("iselCondCode(mips): CmpXX32 or CmpXX64");
}
addInstr(env, MIPSInstr_Cmp(syned, size32, dst, r1, r2, cc));
return dst;
}
if (e->Iex.Binop.op == Iop_Max32U) {
HReg tmp = newVRegI(env);
HReg r_dst = newVRegI(env);
HReg argL = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg argR = iselWordExpr_R(env, e->Iex.Binop.arg2);
MIPSRH *argRH = iselWordExpr_RH(env, False /*signed */ ,
e->Iex.Binop.arg2);
/* max (v0, s0)
------------
slt v1, v0, s0
movn v0, s0, v1 */
addInstr(env, MIPSInstr_Alu(Malu_SLT, tmp, argL, argRH));
addInstr(env, mk_iMOVds_RR(r_dst, argL));
addInstr(env, MIPSInstr_MoveCond(MMoveCond_movn, r_dst, argR, tmp));
return r_dst;
}
if (e->Iex.Binop.op == Iop_Mul32 || e->Iex.Binop.op == Iop_Mul64) {
Bool sz32 = (e->Iex.Binop.op == Iop_Mul32);
HReg r_dst = newVRegI(env);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2);
addInstr(env, MIPSInstr_Mul(False/*Unsigned or Signed */ ,
False /*widen */ ,
sz32 /*32bit or 64bit */,
r_dst, r_srcL, r_srcR));
return r_dst;
}
if (e->Iex.Binop.op == Iop_MullU32 || e->Iex.Binop.op == Iop_MullS32) {
HReg r_dst = newVRegI(env);
HReg tHi = newVRegI(env);
HReg tLo = newVRegI(env);
HReg tLo_1 = newVRegI(env);
HReg tHi_1 = newVRegI(env);
HReg mask = newVRegI(env);
Bool syned = toBool(e->Iex.Binop.op == Iop_MullS32);
Bool size = toBool(e->Iex.Binop.op == Iop_MullS32)
|| toBool(e->Iex.Binop.op == Iop_MullU32);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2);
addInstr(env, MIPSInstr_Mul(syned /*Unsigned or Signed */ ,
True /*widen */ ,
size /*32bit or 64bit mul */ ,
r_dst, r_srcL, r_srcR));
addInstr(env, MIPSInstr_Mfhi(tHi));
addInstr(env, MIPSInstr_Mflo(tLo));
addInstr(env, MIPSInstr_Shft(Mshft_SLL, False, tHi_1,
tHi, MIPSRH_Imm(False, 32)));
addInstr(env, MIPSInstr_LI(mask, 0xffffffff));
addInstr(env, MIPSInstr_Alu(Malu_AND, tLo_1, tLo,
MIPSRH_Reg(mask)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_dst, tHi_1,
MIPSRH_Reg(tLo_1)));
return r_dst;
}
if (e->Iex.Binop.op == Iop_CmpF64) {
HReg r_srcL, r_srcR;
if (mode64) {
r_srcL = iselFltExpr(env, e->Iex.Binop.arg1);
r_srcR = iselFltExpr(env, e->Iex.Binop.arg2);
} else {
r_srcL = iselDblExpr(env, e->Iex.Binop.arg1);
r_srcR = iselDblExpr(env, e->Iex.Binop.arg2);
}
HReg tmp = newVRegI(env);
HReg r_ccMIPS = newVRegI(env);
HReg r_ccIR = newVRegI(env);
HReg r_ccIR_b0 = newVRegI(env);
HReg r_ccIR_b2 = newVRegI(env);
HReg r_ccIR_b6 = newVRegI(env);
/* Create in dst, the IRCmpF64Result encoded result. */
/* chech for EQ */
addInstr(env, MIPSInstr_FpCompare(Mfp_CMP_EQ, tmp, r_srcL, r_srcR));
addInstr(env, MIPSInstr_Shft(Mshft_SLL, True, r_ccMIPS, tmp,
MIPSRH_Imm(False, 1)));
/* chech for UN */
addInstr(env, MIPSInstr_FpCompare(Mfp_CMP_UN, tmp, r_srcL, r_srcR));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_ccMIPS, r_ccMIPS,
MIPSRH_Reg(tmp)));
/* chech for LT */
addInstr(env, MIPSInstr_FpCompare(Mfp_CMP_LT, tmp, r_srcL, r_srcR));
addInstr(env, MIPSInstr_Shft(Mshft_SLL, True, tmp,
tmp, MIPSRH_Imm(False, 2)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_ccMIPS, r_ccMIPS,
MIPSRH_Reg(tmp)));
/* chech for GT */
addInstr(env, MIPSInstr_FpCompare(Mfp_CMP_NGT,
tmp, r_srcL, r_srcR));
addInstr(env, MIPSInstr_Shft(Mshft_SLL, True, tmp, tmp,
MIPSRH_Imm(False, 3)));
addInstr(env, MIPSInstr_Alu(Malu_NOR, tmp, tmp, MIPSRH_Reg(tmp)));
addInstr(env, MIPSInstr_Alu(Malu_AND, tmp, tmp,
MIPSRH_Imm(False, 8)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_ccMIPS, r_ccMIPS,
MIPSRH_Reg(tmp)));
/* Map compare result from MIPS to IR,
conforming to CmpF64 definition.
FP cmp result | MIPS | IR
--------------------------
UN | 0x1 | 0x45
EQ | 0x2 | 0x40
GT | 0x4 | 0x00
LT | 0x8 | 0x01
*/
/* r_ccIR_b0 = r_ccMIPS[0] | r_ccMIPS[3] */
addInstr(env, MIPSInstr_Shft(Mshft_SRL, True, r_ccIR_b0, r_ccMIPS,
MIPSRH_Imm(False, 0x3)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_ccIR_b0, r_ccMIPS,
MIPSRH_Reg(r_ccIR_b0)));
addInstr(env, MIPSInstr_Alu(Malu_AND, r_ccIR_b0, r_ccIR_b0,
MIPSRH_Imm(False, 0x1)));
/* r_ccIR_b2 = r_ccMIPS[0] */
addInstr(env, MIPSInstr_Shft(Mshft_SLL, True, r_ccIR_b2, r_ccMIPS,
MIPSRH_Imm(False, 0x2)));
addInstr(env, MIPSInstr_Alu(Malu_AND, r_ccIR_b2, r_ccIR_b2,
MIPSRH_Imm(False, 0x4)));
/* r_ccIR_b6 = r_ccMIPS[0] | r_ccMIPS[1] */
addInstr(env, MIPSInstr_Shft(Mshft_SRL, True, r_ccIR_b6,
r_ccMIPS, MIPSRH_Imm(False, 0x1)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_ccIR_b6, r_ccMIPS,
MIPSRH_Reg(r_ccIR_b6)));
addInstr(env, MIPSInstr_Shft(Mshft_SLL, True, r_ccIR_b6, r_ccIR_b6,
MIPSRH_Imm(False, 0x6)));
addInstr(env, MIPSInstr_Alu(Malu_AND, r_ccIR_b6, r_ccIR_b6,
MIPSRH_Imm(False, 0x40)));
/* r_ccIR = r_ccIR_b0 | r_ccIR_b2 | r_ccIR_b6 */
addInstr(env, MIPSInstr_Alu(Malu_OR, r_ccIR, r_ccIR_b0,
MIPSRH_Reg(r_ccIR_b2)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_ccIR, r_ccIR,
MIPSRH_Reg(r_ccIR_b6)));
return r_ccIR;
}
if (e->Iex.Binop.op == Iop_DivModU64to32 ||
e->Iex.Binop.op == Iop_DivModS64to32) {
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
HReg mask = newVRegI(env);
HReg tLo_1 = newVRegI(env);
HReg tHi_1 = newVRegI(env);
HReg r_dst = newVRegI(env);
Bool syned = toBool(e->Iex.Binop.op == Iop_DivModS64to32);
HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
addInstr(env, MIPSInstr_Div(syned, True, r_srcL, r_srcR));
addInstr(env, MIPSInstr_Mfhi(tHi));
addInstr(env, MIPSInstr_Mflo(tLo));
addInstr(env, MIPSInstr_Shft(Mshft_SLL, False, tHi_1, tHi,
MIPSRH_Imm(False, 32)));
addInstr(env, MIPSInstr_LI(mask, 0xffffffff));
addInstr(env, MIPSInstr_Alu(Malu_AND, tLo_1, tLo,
MIPSRH_Reg(mask)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_dst, tHi_1,
MIPSRH_Reg(tLo_1)));
return r_dst;
}
if (e->Iex.Binop.op == Iop_8HLto16
|| e->Iex.Binop.op == Iop_16HLto32) {
HReg tHi = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg tLo = iselWordExpr_R(env, e->Iex.Binop.arg2);
HReg tLo_1 = newVRegI(env);
HReg tHi_1 = newVRegI(env);
HReg r_dst = newVRegI(env);
UInt shift = 0;
UInt mask = 0;
switch (e->Iex.Binop.op) {
case Iop_8HLto16:
shift = 8;
mask = 0xff;
break;
case Iop_16HLto32:
shift = 16;
mask = 0xffff;
break;
default:
break;
}
/* sll tHi_1, tHi, shift
and tLo_1, tLo, mask
or r_dst, tHi_1, tLo_1 */
addInstr(env, MIPSInstr_Shft(Mshft_SLL, True, tHi_1, tHi,
MIPSRH_Imm(False, shift)));
addInstr(env, MIPSInstr_Alu(Malu_AND, tLo_1, tLo,
MIPSRH_Imm(False, mask)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_dst, tHi_1,
MIPSRH_Reg(tLo_1)));
return r_dst;
}
if (e->Iex.Binop.op == Iop_32HLto64) {
vassert(mode64);
HReg tHi = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg tLo = iselWordExpr_R(env, e->Iex.Binop.arg2);
HReg tLo_1 = newVRegI(env);
HReg tHi_1 = newVRegI(env);
HReg r_dst = newVRegI(env);
HReg mask = newVRegI(env);
addInstr(env, MIPSInstr_Shft(Mshft_SLL, False, tHi_1, tHi,
MIPSRH_Imm(False, 32)));
addInstr(env, MIPSInstr_LI(mask, 0xffffffff));
addInstr(env, MIPSInstr_Alu(Malu_AND, tLo_1, tLo,
MIPSRH_Reg(mask)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_dst, tHi_1,
MIPSRH_Reg(tLo_1)));
return r_dst;
}
if (e->Iex.Binop.op == Iop_F32toI64S) {
vassert(mode64);
HReg valS = newVRegI(env);
HReg tmpF = newVRegF(env);
HReg valF = iselFltExpr(env, e->Iex.Binop.arg2);
/* CVTLS tmpF, valF */
set_MIPS_rounding_mode(env, e->Iex.Binop.arg1);
addInstr(env, MIPSInstr_FpConvert(Mfp_CVTLS, tmpF, valF));
set_MIPS_rounding_default(env);
/* Doubleword Move from Floating Point
dmfc1 valS, tmpF */
addInstr(env, MIPSInstr_FpGpMove(MFpGpMove_dmfc1, valS, tmpF));
return valS;
}
if (e->Iex.Binop.op == Iop_F64toI32S) {
HReg valD;
if (mode64)
valD = iselFltExpr(env, e->Iex.Binop.arg2);
else
valD = iselDblExpr(env, e->Iex.Binop.arg2);
HReg valS = newVRegF(env);
HReg r_dst = newVRegI(env);
/* CVTWD valS, valD */
set_MIPS_rounding_mode(env, e->Iex.Binop.arg1);
addInstr(env, MIPSInstr_FpConvert(Mfp_CVTWD, valS, valD));
set_MIPS_rounding_default(env);
/* Move Word From Floating Point
mfc1 r_dst, valS */
addInstr(env, MIPSInstr_FpGpMove(MFpGpMove_mfc1, r_dst, valS));
return r_dst;
}
/* -------- DSP ASE -------- */
/* All used cases involving host-side helper calls. */
void* fn = NULL;
switch (e->Iex.Binop.op) {
case Iop_HAdd8Ux4:
fn = &h_generic_calc_HAdd8Ux4; break;
case Iop_HSub8Ux4:
fn = &h_generic_calc_HSub8Ux4; break;
case Iop_HSub16Sx2:
fn = &h_generic_calc_HSub16Sx2; break;
case Iop_QSub8Ux4:
fn = &h_generic_calc_QSub8Ux4; break;
default:
break;
}
/* What's the retloc? */
RetLoc rloc = mk_RetLoc_INVALID();
if (ty == Ity_I32) {
rloc = mk_RetLoc_simple(RLPri_Int);
}
else if (ty == Ity_I64) {
rloc = mode64 ? mk_RetLoc_simple(RLPri_Int) :
mk_RetLoc_simple(RLPri_2Int);
}
else {
goto irreducible;
}
if (fn) {
HReg regL = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg regR = iselWordExpr_R(env, e->Iex.Binop.arg2);
HReg res = newVRegI(env);
addInstr(env, mk_iMOVds_RR(hregMIPS_GPR4(env->mode64), regL));
addInstr(env, mk_iMOVds_RR(hregMIPS_GPR5(env->mode64), regR));
argiregs |= (1 << 4);
argiregs |= (1 << 5);
addInstr(env, MIPSInstr_CallAlways( MIPScc_AL,
(HWord)Ptr_to_ULong(fn),
argiregs, rloc));
addInstr(env, mk_iMOVds_RR(res, hregMIPS_GPR2(env->mode64)));
return res;
}
break;
}
/* --------- UNARY OP --------- */
case Iex_Unop: {
IROp op_unop = e->Iex.Unop.op;
switch (op_unop) {
case Iop_1Sto8:
case Iop_1Sto16:
case Iop_1Sto32:
case Iop_8Sto16:
case Iop_8Sto32:
case Iop_16Sto32:
case Iop_16Sto64:
case Iop_8Sto64:
case Iop_1Sto64: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
Bool sz32;
UShort amt;
switch (op_unop) {
case Iop_1Sto8:
amt = 31;
sz32 = True;
break;
case Iop_1Sto16:
amt = 31;
sz32 = True;
break;
case Iop_1Sto32:
amt = 31;
sz32 = True;
break;
case Iop_16Sto32:
amt = 16;
sz32 = True;
break;
case Iop_16Sto64:
amt = 48;
sz32 = False;
break;
case Iop_8Sto16:
amt = 24;
sz32 = True;
break;
case Iop_8Sto32:
amt = 24;
sz32 = True;
break;
case Iop_8Sto64:
amt = 56;
sz32 = False;
break;
case Iop_1Sto64:
amt = 63;
sz32 = False;
break;
default:
vassert(0);
}
addInstr(env, MIPSInstr_Shft(Mshft_SLL, sz32, r_dst, r_src,
MIPSRH_Imm(False, amt)));
addInstr(env, MIPSInstr_Shft(Mshft_SRA, sz32, r_dst, r_dst,
MIPSRH_Imm(False, amt)));
return r_dst;
}
/* not(x) = nor(x,x) */
case Iop_Not1: {
HReg r_dst = newVRegI(env);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Unop.arg);
MIPSRH *r_srcR = MIPSRH_Reg(r_srcL);
addInstr(env, MIPSInstr_LI(r_dst, 0x1));
addInstr(env, MIPSInstr_Alu(Malu_SUB, r_dst, r_dst, r_srcR));
return r_dst;
}
case Iop_Not8:
case Iop_Not16:
case Iop_Not32:
case Iop_Not64: {
HReg r_dst = newVRegI(env);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Unop.arg);
MIPSRH *r_srcR = MIPSRH_Reg(r_srcL);
addInstr(env, MIPSInstr_Alu(Malu_NOR, r_dst, r_srcL, r_srcR));
return r_dst;
}
case Iop_ReinterpF32asI32: {
HReg fr_src = iselFltExpr(env, e->Iex.Unop.arg);
HReg r_dst = newVRegI(env);
/* Move Word From Floating Point
mfc1 r_dst, fr_src */
addInstr(env, MIPSInstr_FpGpMove(MFpGpMove_mfc1, r_dst, fr_src));
return r_dst;
}
case Iop_ReinterpF64asI64: {
vassert(mode64);
HReg fr_src = iselFltExpr(env, e->Iex.Unop.arg);
HReg r_dst = newVRegI(env);
/* Doubleword Move from Floating Point
mfc1 r_dst, fr_src */
addInstr(env, MIPSInstr_FpGpMove(MFpGpMove_dmfc1, r_dst, fr_src));
return r_dst;
}
case Iop_F64toI32S: {
HReg valD;
if (mode64)
valD = iselFltExpr(env, e->Iex.Binop.arg2);
else
valD = iselDblExpr(env, e->Iex.Binop.arg2);
HReg valS = newVRegF(env);
HReg r_dst = newVRegI(env);
set_MIPS_rounding_mode(env, e->Iex.Binop.arg1);
addInstr(env, MIPSInstr_FpConvert(Mfp_CVTWD, valS, valD));
set_MIPS_rounding_default(env);
/* Move Word From Floating Point
mfc1 r_dst, valS */
addInstr(env, MIPSInstr_FpGpMove(MFpGpMove_mfc1, r_dst, valS));
return r_dst;
}
case Iop_16to8:
case Iop_32to1:
case Iop_32to8:
case Iop_32to16:
return iselWordExpr_R(env, e->Iex.Unop.arg);
case Iop_32HIto16: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, MIPSInstr_Shft(Mshft_SRL, True /* 32bit shift */,
r_dst, r_src, MIPSRH_Imm(False, 16)));
return r_dst;
}
case Iop_64to1:
case Iop_64to8: {
vassert(mode64);
HReg r_src, r_dst;
UShort mask = (op_unop == Iop_64to1) ? 0x1 : 0xFF;
r_dst = newVRegI(env);
r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, MIPSInstr_Alu(Malu_AND, r_dst, r_src,
MIPSRH_Imm(False, mask)));
return r_dst;
}
case Iop_16HIto8: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, MIPSInstr_Shft(Mshft_SRL, True /* 32bit shift */,
r_dst, r_src, MIPSRH_Imm(False, 8)));
return r_dst;
}
case Iop_1Uto8:
case Iop_1Uto32:
case Iop_1Uto64:
case Iop_8Uto16:
case Iop_8Uto32:
case Iop_8Uto64:
case Iop_16Uto32:
case Iop_16Uto64: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
UShort mask = 0;
switch (op_unop) {
case Iop_1Uto64:
vassert(mode64);
case Iop_1Uto8:
case Iop_1Uto32:
mask = toUShort(0x1);
break;
case Iop_8Uto64:
vassert(mode64);
case Iop_8Uto16:
case Iop_8Uto32:
mask = toUShort(0xFF);
break;
case Iop_16Uto64:
vassert(mode64);
case Iop_16Uto32:
mask = toUShort(0xFFFF);
break;
default:
vassert(0);
break;
}
addInstr(env, MIPSInstr_Alu(Malu_AND, r_dst, r_src,
MIPSRH_Imm(False, mask)));
return r_dst;
}
case Iop_32Uto64: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
vassert(mode64);
addInstr(env, MIPSInstr_Shft(Mshft_SLL, False /*!32bit shift */,
r_dst, r_src, MIPSRH_Imm(False, 32)));
addInstr(env, MIPSInstr_Shft(Mshft_SRL, False /*!32bit shift */,
r_dst, r_dst, MIPSRH_Imm(False, 32)));
return r_dst;
}
case Iop_64HIto32: {
if (env->mode64) {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, MIPSInstr_Shft(Mshft_SRA, False /*64bit shift */,
r_dst, r_src, MIPSRH_Imm(True, 32)));
return r_dst;
} else {
HReg rHi, rLo;
iselInt64Expr(&rHi, &rLo, env, e->Iex.Unop.arg);
return rHi;
}
}
case Iop_64to32: {
if (env->mode64) {
HReg r_dst = newVRegI(env);
r_dst = iselWordExpr_R(env, e->Iex.Unop.arg);
return r_dst;
} else {
HReg rHi, rLo;
iselInt64Expr(&rHi, &rLo, env, e->Iex.Unop.arg);
return rLo;
}
}
case Iop_64to16: {
vassert(env->mode64);
HReg r_dst = newVRegI(env);
r_dst = iselWordExpr_R(env, e->Iex.Unop.arg);
return r_dst;
}
case Iop_32Sto64: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
vassert(mode64);
addInstr(env, MIPSInstr_Shft(Mshft_SLL, True /*!32bit shift */,
r_dst, r_src, MIPSRH_Imm(True, 0)));
return r_dst;
}
case Iop_CmpNEZ8:
case Iop_CmpNEZ16: {
HReg r_dst = newVRegI(env);
HReg tmp = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
UShort mask = (op_unop == Iop_CmpNEZ8) ? 0xFF : 0xFFFF;
addInstr(env, MIPSInstr_Alu(Malu_AND, tmp, r_src,
MIPSRH_Imm(False, mask)));
addInstr(env, MIPSInstr_Cmp(False, True, r_dst, tmp,
hregMIPS_GPR0(mode64), MIPScc_NE));
return r_dst;
}
case Iop_CmpNEZ32: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, MIPSInstr_Cmp(False, True, r_dst, r_src,
hregMIPS_GPR0(mode64), MIPScc_NE));
return r_dst;
}
case Iop_CmpwNEZ32: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, MIPSInstr_Alu(Malu_SUB, r_dst, hregMIPS_GPR0(mode64),
MIPSRH_Reg(r_src)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_dst, r_dst,
MIPSRH_Reg(r_src)));
addInstr(env, MIPSInstr_Shft(Mshft_SRA, True, r_dst, r_dst,
MIPSRH_Imm(False, 31)));
return r_dst;
}
case Iop_Left8:
case Iop_Left16:
case Iop_Left32:
case Iop_Left64: {
if (op_unop == Iop_Left64 && !mode64)
goto irreducible;
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
MIPSAluOp op = (op_unop == Iop_Left64) ? Malu_DSUB : Malu_SUB;
addInstr(env, MIPSInstr_Alu(op, r_dst,
hregMIPS_GPR0(mode64),
MIPSRH_Reg(r_src)));
addInstr(env, MIPSInstr_Alu(Malu_OR, r_dst, r_dst,
MIPSRH_Reg(r_src)));
return r_dst;
}
case Iop_Clz64:
vassert(mode64);
case Iop_Clz32: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
MIPSUnaryOp op = (op_unop == Iop_Clz64) ? Mun_DCLZ : Mun_CLZ;
addInstr(env, MIPSInstr_Unary(op, r_dst, r_src));
return r_dst;
}
case Iop_CmpNEZ64: {
HReg hi, lo;
HReg r_dst = newVRegI(env);
HReg r_src;
if (env->mode64) {
r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
} else {
r_src = newVRegI(env);
iselInt64Expr(&hi, &lo, env, e->Iex.Unop.arg);
addInstr(env, MIPSInstr_Alu(Malu_OR, r_src, lo, MIPSRH_Reg(hi)));
}
addInstr(env, MIPSInstr_Cmp(False, !(env->mode64), r_dst, r_src,
hregMIPS_GPR0(mode64), MIPScc_NE));
return r_dst;
}
case Iop_CmpwNEZ64: {
HReg tmp1;
HReg tmp2 = newVRegI(env);
vassert(env->mode64);
tmp1 = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, MIPSInstr_Alu(Malu_DSUB, tmp2, hregMIPS_GPR0(mode64),
MIPSRH_Reg(tmp1)));
addInstr(env, MIPSInstr_Alu(Malu_OR, tmp2, tmp2, MIPSRH_Reg(tmp1)));
addInstr(env, MIPSInstr_Shft(Mshft_SRA, False, tmp2, tmp2,
MIPSRH_Imm (False, 63)));
return tmp2;
}
case Iop_128HIto64: {
vassert(mode64);
HReg rHi, rLo;
iselInt128Expr(&rHi, &rLo, env, e->Iex.Unop.arg);
return rHi; /* and abandon rLo .. poor wee thing :-) */
}
case Iop_128to64: {
vassert(mode64);
HReg rHi, rLo;
iselInt128Expr(&rHi, &rLo, env, e->Iex.Unop.arg);
return rLo; /* and abandon rLo .. poor wee thing :-) */
}
default:
break;
}
/* -------- DSP ASE -------- */
/* All Unop cases involving host-side helper calls. */
void* fn = NULL;
switch (e->Iex.Unop.op) {
case Iop_CmpNEZ16x2:
fn = &h_generic_calc_CmpNEZ16x2; break;
case Iop_CmpNEZ8x4:
fn = &h_generic_calc_CmpNEZ8x4; break;
default:
break;
}
RetLoc rloc = mk_RetLoc_INVALID();
if (ty == Ity_I32) {
rloc = mk_RetLoc_simple(RLPri_Int);
}
else if (ty == Ity_I64) {
rloc = mode64 ? mk_RetLoc_simple(RLPri_Int) :
mk_RetLoc_simple(RLPri_2Int);
}
else {
goto irreducible;
}
if (fn) {
HReg regL = iselWordExpr_R(env, e->Iex.Unop.arg);
HReg res = newVRegI(env);
addInstr(env, mk_iMOVds_RR(hregMIPS_GPR4(env->mode64), regL));
argiregs |= (1 << 4);
addInstr(env, MIPSInstr_CallAlways( MIPScc_AL,
(HWord)Ptr_to_ULong(fn),
argiregs, rloc));
addInstr(env, mk_iMOVds_RR(res, hregMIPS_GPR2(env->mode64)));
return res;
}
break;
}
/* --------- GET --------- */
case Iex_Get: {
if (ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32
|| ((ty == Ity_I64) && mode64)) {
HReg r_dst = newVRegI(env);
MIPSAMode *am_addr = MIPSAMode_IR(e->Iex.Get.offset,
GuestStatePointer(mode64));
addInstr(env, MIPSInstr_Load(toUChar(sizeofIRType(ty)), r_dst, am_addr,
mode64));
return r_dst;
}
break;
}
/* --------- ITE --------- */
case Iex_ITE: {
if ((ty == Ity_I8 || ty == Ity_I16 ||
ty == Ity_I32 || ((ty == Ity_I64))) &&
typeOfIRExpr(env->type_env, e->Iex.ITE.cond) == Ity_I1) {
HReg r_dst = iselWordExpr_R(env, e->Iex.ITE.iffalse);
HReg r1 = iselWordExpr_R(env, e->Iex.ITE.iftrue);
HReg r_cond = iselWordExpr_R(env, e->Iex.ITE.cond);
/*
* r_dst = r0
* movn r_dst, r1, r_cond
*/
addInstr(env, MIPSInstr_MoveCond(MMoveCond_movn, r_dst, r1, r_cond));
return r_dst;
}
break;
}
/* --------- LITERAL --------- */
/* 32/16/8-bit literals */
case Iex_Const: {
Long l;
HReg r_dst = newVRegI(env);
IRConst *con = e->Iex.Const.con;
switch (con->tag) {
case Ico_U64:
if (!mode64)
goto irreducible;
l = (Long) con->Ico.U64;
break;
case Ico_U32:
l = (Long) (Int) con->Ico.U32;
break;
case Ico_U16:
l = (Long) (Int) (Short) con->Ico.U16;
break;
case Ico_U8:
l = (Long) (Int) (Char) con->Ico.U8;
break;
default:
vpanic("iselIntExpr_R.const(mips)");
}
addInstr(env, MIPSInstr_LI(r_dst, (ULong) l));
return r_dst;
}
/* --------- CCALL --------- */
case Iex_CCall: {
HReg r_dst = newVRegI(env);
vassert(ty == e->Iex.CCall.retty);
/* be very restrictive for now. Only 32/64-bit ints allowed for
args, and 64 and 32 bits for return type. Don't forget to change
the RetLoc if more return types are allowed in future. */
if (e->Iex.CCall.retty != Ity_I64 && e->Iex.CCall.retty != Ity_I32)
goto irreducible;
/* Marshal args, do the call, clear stack. */
UInt addToSp = 0;
RetLoc rloc = mk_RetLoc_INVALID();
doHelperCall(&addToSp, &rloc, env, NULL/*guard*/, e->Iex.CCall.cee,
e->Iex.CCall.retty, e->Iex.CCall.args );
vassert(is_sane_RetLoc(rloc));
vassert(rloc.pri == RLPri_Int);
vassert(addToSp == 0);
addInstr(env, mk_iMOVds_RR(r_dst, hregMIPS_GPR2(mode64)));
return r_dst;
}
default:
break;
} /* end switch(e->tag) */
/* We get here if no pattern matched. */
irreducible:
vex_printf("--------------->\n");
if (e->tag == Iex_RdTmp)
vex_printf("Iex_RdTmp \n");
ppIRExpr(e);
vpanic("iselWordExpr_R(mips): cannot reduce tree");
}
/* --------------------- RH --------------------- */
/* Compute an I8/I16/I32 (and I64, in 64-bit mode) into a RH
(reg-or-halfword-immediate). It's important to specify whether the
immediate is to be regarded as signed or not. If yes, this will
never return -32768 as an immediate; this guaranteed that all
signed immediates that are return can have their sign inverted if
need be. */
static MIPSRH *iselWordExpr_RH(ISelEnv * env, Bool syned, IRExpr * e)
{
MIPSRH *ri = iselWordExpr_RH_wrk(env, syned, e);
/* sanity checks ... */
switch (ri->tag) {
case Mrh_Imm:
vassert(ri->Mrh.Imm.syned == syned);
if (syned)
vassert(ri->Mrh.Imm.imm16 != 0x8000);
return ri;
case Mrh_Reg:
vassert(hregClass(ri->Mrh.Reg.reg) == HRcGPR(env->mode64));
vassert(hregIsVirtual(ri->Mrh.Reg.reg));
return ri;
default:
vpanic("iselIntExpr_RH: unknown mips RH tag");
}
}
/* DO NOT CALL THIS DIRECTLY ! */
static MIPSRH *iselWordExpr_RH_wrk(ISelEnv * env, Bool syned, IRExpr * e)
{
ULong u;
Long l;
IRType ty = typeOfIRExpr(env->type_env, e);
vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32 ||
((ty == Ity_I64) && env->mode64));
/* special case: immediate */
if (e->tag == Iex_Const) {
IRConst *con = e->Iex.Const.con;
/* What value are we aiming to generate? */
switch (con->tag) {
/* Note: Not sign-extending - we carry 'syned' around */
case Ico_U64:
vassert(env->mode64);
u = con->Ico.U64;
break;
case Ico_U32:
u = 0xFFFFFFFF & con->Ico.U32;
break;
case Ico_U16:
u = 0x0000FFFF & con->Ico.U16;
break;
case Ico_U8:
u = 0x000000FF & con->Ico.U8;
break;
default:
vpanic("iselIntExpr_RH.Iex_Const(mips)");
}
l = (Long) u;
/* Now figure out if it's representable. */
if (!syned && u <= 65535) {
return MIPSRH_Imm(False /*unsigned */ , toUShort(u & 0xFFFF));
}
if (syned && l >= -32767 && l <= 32767) {
return MIPSRH_Imm(True /*signed */ , toUShort(u & 0xFFFF));
}
/* no luck; use the Slow Way. */
}
/* default case: calculate into a register and return that */
return MIPSRH_Reg(iselWordExpr_R(env, e));
}
/* --------------------- RH5u --------------------- */
/* Compute an I8 into a reg-or-5-bit-unsigned-immediate, the latter
being an immediate in the range 1 .. 31 inclusive. Used for doing
shift amounts. */
static MIPSRH *iselWordExpr_RH5u(ISelEnv * env, IRExpr * e)
{
MIPSRH *ri;
ri = iselWordExpr_RH5u_wrk(env, e);
/* sanity checks ... */
switch (ri->tag) {
case Mrh_Imm:
vassert(ri->Mrh.Imm.imm16 >= 1 && ri->Mrh.Imm.imm16 <= 31);
vassert(!ri->Mrh.Imm.syned);
return ri;
case Mrh_Reg:
vassert(hregClass(ri->Mrh.Reg.reg) == HRcInt32);
vassert(hregIsVirtual(ri->Mrh.Reg.reg));
return ri;
default:
vpanic("iselIntExpr_RH5u: unknown mips RH tag");
}
}
/* DO NOT CALL THIS DIRECTLY ! */
static MIPSRH *iselWordExpr_RH5u_wrk(ISelEnv * env, IRExpr * e)
{
IRType ty = typeOfIRExpr(env->type_env, e);
vassert(ty == Ity_I8);
/* special case: immediate */
if (e->tag == Iex_Const
&& e->Iex.Const.con->tag == Ico_U8
&& e->Iex.Const.con->Ico.U8 >= 1 && e->Iex.Const.con->Ico.U8 <= 31) {
return MIPSRH_Imm(False /*unsigned */ , e->Iex.Const.con->Ico.U8);
}
/* default case: calculate into a register and return that */
return MIPSRH_Reg(iselWordExpr_R(env, e));
}
/* --------------------- RH6u --------------------- */
/* Only used in 64-bit mode. */
static MIPSRH *iselWordExpr_RH6u ( ISelEnv * env, IRExpr * e )
{
MIPSRH *ri;
ri = iselWordExpr_RH6u_wrk(env, e);
/* sanity checks ... */
switch (ri->tag) {
case Mrh_Imm:
vassert(ri->Mrh.Imm.imm16 >= 1 && ri->Mrh.Imm.imm16 <= 63);
vassert(!ri->Mrh.Imm.syned);
return ri;
case Mrh_Reg:
vassert(hregClass(ri->Mrh.Reg.reg) == HRcGPR(env->mode64));
vassert(hregIsVirtual(ri->Mrh.Reg.reg));
return ri;
default:
vpanic("iselIntExpr_RH6u: unknown mips64 RI tag");
}
}
/* DO NOT CALL THIS DIRECTLY ! */
static MIPSRH *iselWordExpr_RH6u_wrk ( ISelEnv * env, IRExpr * e )
{
IRType ty = typeOfIRExpr(env->type_env, e);
vassert(ty == Ity_I8);
/* special case: immediate */
if (e->tag == Iex_Const
&& e->Iex.Const.con->tag == Ico_U8
&& e->Iex.Const.con->Ico.U8 >= 1 && e->Iex.Const.con->Ico.U8 <= 63)
{
return MIPSRH_Imm(False /*unsigned */ ,
e->Iex.Const.con->Ico.U8);
}
/* default case: calculate into a register and return that */
return MIPSRH_Reg(iselWordExpr_R(env, e));
}
/* --------------------- CONDCODE --------------------- */
/* Generate code to evaluated a bit-typed expression, returning the
condition code which would correspond when the expression would
notionally have returned 1. */
static MIPSCondCode iselCondCode(ISelEnv * env, IRExpr * e)
{
MIPSCondCode cc = iselCondCode_wrk(env,e);
vassert(cc != MIPScc_NV);
return cc;
}
/* DO NOT CALL THIS DIRECTLY ! */
static MIPSCondCode iselCondCode_wrk(ISelEnv * env, IRExpr * e)
{
vassert(e);
vassert(typeOfIRExpr(env->type_env, e) == Ity_I1);
/* Cmp*32*(x,y) ? */
if (e->Iex.Binop.op == Iop_CmpEQ32
|| e->Iex.Binop.op == Iop_CmpNE32
|| e->Iex.Binop.op == Iop_CmpNE64
|| e->Iex.Binop.op == Iop_CmpLT32S
|| e->Iex.Binop.op == Iop_CmpLT32U
|| e->Iex.Binop.op == Iop_CmpLT64U
|| e->Iex.Binop.op == Iop_CmpLE32S
|| e->Iex.Binop.op == Iop_CmpLE64S
|| e->Iex.Binop.op == Iop_CmpLT64S
|| e->Iex.Binop.op == Iop_CmpEQ64
|| e->Iex.Binop.op == Iop_CasCmpEQ32
|| e->Iex.Binop.op == Iop_CasCmpEQ64) {
Bool syned = (e->Iex.Binop.op == Iop_CmpLT32S
|| e->Iex.Binop.op == Iop_CmpLE32S
|| e->Iex.Binop.op == Iop_CmpLT64S
|| e->Iex.Binop.op == Iop_CmpLE64S);
Bool size32;
HReg dst = newVRegI(env);
HReg r1 = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r2 = iselWordExpr_R(env, e->Iex.Binop.arg2);
MIPSCondCode cc;
switch (e->Iex.Binop.op) {
case Iop_CmpEQ32:
case Iop_CasCmpEQ32:
cc = MIPScc_EQ;
size32 = True;
break;
case Iop_CmpNE32:
cc = MIPScc_NE;
size32 = True;
break;
case Iop_CmpNE64:
cc = MIPScc_NE;
size32 = True;
break;
case Iop_CmpLT32S:
cc = MIPScc_LT;
size32 = True;
break;
case Iop_CmpLT32U:
cc = MIPScc_LO;
size32 = True;
break;
case Iop_CmpLT64U:
cc = MIPScc_LO;
size32 = False;
break;
case Iop_CmpLE32S:
cc = MIPScc_LE;
size32 = True;
break;
case Iop_CmpLE64S:
cc = MIPScc_LE;
size32 = False;
break;
case Iop_CmpLT64S:
cc = MIPScc_LT;
size32 = False;
break;
case Iop_CmpEQ64:
case Iop_CasCmpEQ64:
cc = MIPScc_EQ;
size32 = False;
break;
default:
vpanic("iselCondCode(mips): CmpXX32 or CmpXX64");
break;
}
addInstr(env, MIPSInstr_Cmp(syned, size32, dst, r1, r2, cc));
/* Store result to guest_COND */
MIPSAMode *am_addr = MIPSAMode_IR(0, GuestStatePointer(mode64));
addInstr(env, MIPSInstr_Store(4,
MIPSAMode_IR(am_addr->Mam.IR.index + COND_OFFSET(mode64),
am_addr->Mam.IR.base),
dst, mode64));
return cc;
}
if (e->Iex.Binop.op == Iop_Not1) {
HReg r_dst = newVRegI(env);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Unop.arg);
MIPSRH *r_srcR = MIPSRH_Reg(r_srcL);
addInstr(env, MIPSInstr_LI(r_dst, 0x1));
addInstr(env, MIPSInstr_Alu(Malu_SUB, r_dst, r_dst, r_srcR));
/* Store result to guest_COND */
MIPSAMode *am_addr = MIPSAMode_IR(0, GuestStatePointer(mode64));
addInstr(env, MIPSInstr_Store(4,
MIPSAMode_IR(am_addr->Mam.IR.index + COND_OFFSET(mode64),
am_addr->Mam.IR.base),
r_dst, mode64));
return MIPScc_NE;
}
if (e->tag == Iex_RdTmp || e->tag == Iex_Unop) {
HReg r_dst = iselWordExpr_R_wrk(env, e);
/* Store result to guest_COND */
MIPSAMode *am_addr = MIPSAMode_IR(0, GuestStatePointer(mode64));
addInstr(env, MIPSInstr_Store(4,
MIPSAMode_IR(am_addr->Mam.IR.index + COND_OFFSET(mode64),
am_addr->Mam.IR.base),
r_dst, mode64));
return MIPScc_EQ;
}
vex_printf("iselCondCode(mips): No such tag(%u)\n", e->tag);
ppIRExpr(e);
vpanic("iselCondCode(mips)");
}
/*---------------------------------------------------------*/
/*--- ISEL: Integer expressions (128 bit) ---*/
/*---------------------------------------------------------*/
/* 64-bit mode ONLY: compute a 128-bit value into a register pair,
which is returned as the first two parameters. As with
iselWordExpr_R, these may be either real or virtual regs; in any
case they must not be changed by subsequent code emitted by the
caller. */
static void iselInt128Expr(HReg * rHi, HReg * rLo, ISelEnv * env, IRExpr * e)
{
vassert(env->mode64);
iselInt128Expr_wrk(rHi, rLo, env, e);
vassert(hregClass(*rHi) == HRcGPR(env->mode64));
vassert(hregIsVirtual(*rHi));
vassert(hregClass(*rLo) == HRcGPR(env->mode64));
vassert(hregIsVirtual(*rLo));
}
/* DO NOT CALL THIS DIRECTLY ! */
static void iselInt128Expr_wrk(HReg * rHi, HReg * rLo, ISelEnv * env,
IRExpr * e)
{
vassert(e);
vassert(typeOfIRExpr(env->type_env, e) == Ity_I128);
/* read 128-bit IRTemp */
if (e->tag == Iex_RdTmp) {
lookupIRTempPair(rHi, rLo, env, e->Iex.RdTmp.tmp);
return;
}
/* --------- BINARY ops --------- */
if (e->tag == Iex_Binop) {
switch (e->Iex.Binop.op) {
/* 64 x 64 -> 128 multiply */
case Iop_MullU64:
case Iop_MullS64: {
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
Bool syned = toBool(e->Iex.Binop.op == Iop_MullS64);
HReg r_dst = newVRegI(env);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2);
addInstr(env, MIPSInstr_Mul(syned, True, False /*64bit mul */ ,
r_dst, r_srcL, r_srcR));
addInstr(env, MIPSInstr_Mfhi(tHi));
addInstr(env, MIPSInstr_Mflo(tLo));
*rHi = tHi;
*rLo = tLo;
return;
}
/* 64HLto128(e1,e2) */
case Iop_64HLto128:
*rHi = iselWordExpr_R(env, e->Iex.Binop.arg1);
*rLo = iselWordExpr_R(env, e->Iex.Binop.arg2);
return;
case Iop_DivModS64to64: {
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2);
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
Bool syned = toBool(e->Iex.Binop.op == Iop_DivModS64to64);
addInstr(env, MIPSInstr_Div(syned, False, r_srcL, r_srcR));
addInstr(env, MIPSInstr_Mfhi(tHi));
addInstr(env, MIPSInstr_Mflo(tLo));
*rHi = tHi;
*rLo = tLo;
return;
}
case Iop_DivModU128to64:
case Iop_DivModS128to64: {
vassert(mode64);
HReg rHi1, rLo1;
iselInt128Expr(&rHi1, &rLo1, env, e->Iex.Binop.arg1);
HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2);
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
Bool syned = toBool(e->Iex.Binop.op == Iop_DivModS128to64);
addInstr(env, MIPSInstr_Div(syned, False, rLo1, r_srcR));
addInstr(env, MIPSInstr_Mfhi(tHi));
addInstr(env, MIPSInstr_Mflo(tLo));
*rHi = tHi;
*rLo = tLo;
return;
}
default:
break;
}
}
vex_printf("iselInt128Expr(mips64): No such tag(%u)\n", e->tag);
ppIRExpr(e);
vpanic("iselInt128Expr(mips64)");
}
/*---------------------------------------------------------*/
/*--- ISEL: Integer expressions (64 bit) ---*/
/*---------------------------------------------------------*/
/* 32-bit mode ONLY. Compute a 64-bit value into the register
* pair HI, LO. HI and LO must not be changed by subsequent
* code emitted by the caller. */
static void iselInt64Expr(HReg * rHi, HReg * rLo, ISelEnv * env, IRExpr * e)
{
vassert(!env->mode64);
iselInt64Expr_wrk(rHi, rLo, env, e);
vassert(hregClass(*rHi) == HRcInt32);
vassert(hregIsVirtual(*rHi));
vassert(hregClass(*rLo) == HRcInt32);
vassert(hregIsVirtual(*rLo));
}
/* DO NOT CALL THIS DIRECTLY ! */
static void iselInt64Expr_wrk(HReg * rHi, HReg * rLo, ISelEnv * env, IRExpr * e)
{
vassert(e);
vassert(typeOfIRExpr(env->type_env, e) == Ity_I64);
/* read 64-bit IRTemp */
if (e->tag == Iex_RdTmp) {
lookupIRTemp64(rHi, rLo, env, e->Iex.RdTmp.tmp);
return;
}
/* 64-bit load */
if (e->tag == Iex_Load) {
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
HReg r_addr = iselWordExpr_R(env, e->Iex.Load.addr);
addInstr(env, MIPSInstr_Load(4, tHi, MIPSAMode_IR(0, r_addr), mode64));
addInstr(env, MIPSInstr_Load(4, tLo, MIPSAMode_IR(4, r_addr), mode64));
*rHi = tHi;
*rLo = tLo;
return;
}
/* 64-bit literal */
if (e->tag == Iex_Const) {
ULong w64 = e->Iex.Const.con->Ico.U64;
UInt wHi = toUInt(w64 >> 32);
UInt wLo = toUInt(w64);
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
vassert(e->Iex.Const.con->tag == Ico_U64);
if (wLo == wHi) {
/* Save a precious Int register in this special case. */
addInstr(env, MIPSInstr_LI(tLo, (ULong) wLo));
*rHi = tLo;
*rLo = tLo;
} else {
addInstr(env, MIPSInstr_LI(tHi, (ULong) wHi));
addInstr(env, MIPSInstr_LI(tLo, (ULong) wLo));
*rHi = tHi;
*rLo = tLo;
}
return;
}
/* 64-bit GET */
if (e->tag == Iex_Get) {
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
MIPSAMode *am_addr = MIPSAMode_IR(e->Iex.Get.offset,
GuestStatePointer(mode64));
addInstr(env, MIPSInstr_Load(4, tLo, am_addr, mode64));
addInstr(env, MIPSInstr_Load(4, tHi, nextMIPSAModeInt(am_addr), mode64));
*rHi = tHi;
*rLo = tLo;
return;
}
/* 64-bit ITE */
if (e->tag == Iex_ITE) {
vassert(typeOfIRExpr(env->type_env, e->Iex.ITE.cond) == Ity_I1);
HReg expr0Lo, expr0Hi;
HReg expr1Lo, expr1Hi;
HReg desLo = newVRegI(env);
HReg desHi = newVRegI(env);
HReg cond = iselWordExpr_R(env, e->Iex.ITE.cond);
/* expr0Hi:expr0Lo = iffalse */
/* expr1Hi:expr1Lo = iftrue */
iselInt64Expr(&expr0Hi, &expr0Lo, env, e->Iex.ITE.iffalse);
iselInt64Expr(&expr1Hi, &expr1Lo, env, e->Iex.ITE.iftrue);
/* move desLo, expr0Lo
* move desHi, expr0Hi
* movn desLo, expr1Lo, cond
* movn desHi, expr1Hi, cond */
addInstr(env, mk_iMOVds_RR(desLo, expr0Lo));
addInstr(env, mk_iMOVds_RR(desHi, expr0Hi));
addInstr(env, MIPSInstr_MoveCond(MMoveCond_movn, desLo, expr1Lo, cond));
addInstr(env, MIPSInstr_MoveCond(MMoveCond_movn, desHi, expr1Hi, cond));
*rHi = desHi;
*rLo = desLo;
return;
}
/* --------- BINARY ops --------- */
if (e->tag == Iex_Binop) {
IROp op_binop = e->Iex.Binop.op;
switch (op_binop) {
/* 32 x 32 -> 64 multiply */
/* Add64 */
case Iop_Add64: {
HReg xLo, xHi, yLo, yHi, carryBit;
HReg tHi = newVRegI(env);
HReg tHi1 = newVRegI(env);
HReg tLo = newVRegI(env);
carryBit = newVRegI(env);
Bool size32 = True;
MIPSCondCode cc = MIPScc_LO;
iselInt64Expr(&xHi, &xLo, env, e->Iex.Binop.arg1);
iselInt64Expr(&yHi, &yLo, env, e->Iex.Binop.arg2);
addInstr(env, MIPSInstr_Alu(Malu_ADD, tLo, xLo, MIPSRH_Reg(yLo)));
/* Check carry. */
addInstr(env, MIPSInstr_Cmp(False, size32, carryBit, tLo, xLo, cc));
addInstr(env, MIPSInstr_Alu(Malu_ADD, tHi1, xHi, MIPSRH_Reg(yHi)));
addInstr(env, MIPSInstr_Alu(Malu_ADD, tHi, tHi1,
MIPSRH_Reg(carryBit)));
*rHi = tHi;
*rLo = tLo;
return;
}
case Iop_Sub64: {
HReg xLo, xHi, yLo, yHi, borrow;
Bool size32 = True;
MIPSCondCode cc = MIPScc_LO;
HReg tHi = newVRegI(env);
HReg tLo = newVRegI(env);
borrow = newVRegI(env);
iselInt64Expr(&xHi, &xLo, env, e->Iex.Binop.arg1);
iselInt64Expr(&yHi, &yLo, env, e->Iex.Binop.arg2);
addInstr(env, MIPSInstr_Alu(Malu_SUB, tLo, xLo, MIPSRH_Reg(yLo)));
/* Check if borrow is nedded. */
addInstr(env, MIPSInstr_Cmp(False, size32, borrow, xLo, yLo, cc));
addInstr(env, MIPSInstr_Alu(Malu_ADD, yHi, yHi,
MIPSRH_Reg(borrow)));
addInstr(env, MIPSInstr_Alu(Malu_SUB, tHi, xHi, MIPSRH_Reg(yHi)));
*rHi = tHi;
*rLo = tLo;
return;
}
case Iop_MullU32:
case Iop_MullS32: {
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
HReg r_dst = newVRegI(env);
Bool syned = toBool(op_binop == Iop_MullS32);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2);
addInstr(env, MIPSInstr_Mul(syned /*Unsigned or Signed */,
True /*widen */ , True,
r_dst, r_srcL, r_srcR));
addInstr(env, MIPSInstr_Mfhi(tHi));
addInstr(env, MIPSInstr_Mflo(tLo));
*rHi = tHi;
*rLo = tLo;
return;
}
case Iop_DivModS64to32:
case Iop_DivModU64to32: {
HReg r_sHi, r_sLo;
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
Bool syned = toBool(op_binop == Iop_DivModS64to32);
HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2);
iselInt64Expr(&r_sHi, &r_sLo, env, e->Iex.Binop.arg1);
addInstr(env, MIPSInstr_Div(syned, True, r_sLo, r_srcR));
addInstr(env, MIPSInstr_Mfhi(tHi));
addInstr(env, MIPSInstr_Mflo(tLo));
*rHi = tHi;
*rLo = tLo;
return;
}
/* 32HLto64(e1,e2) */
case Iop_32HLto64:
*rHi = iselWordExpr_R(env, e->Iex.Binop.arg1);
*rLo = iselWordExpr_R(env, e->Iex.Binop.arg2);
return;
/* Or64/And64/Xor64 */
case Iop_Or64:
case Iop_And64:
case Iop_Xor64: {
HReg xLo, xHi, yLo, yHi;
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
MIPSAluOp op = (op_binop == Iop_Or64) ? Malu_OR :
(op_binop == Iop_And64) ? Malu_AND : Malu_XOR;
iselInt64Expr(&xHi, &xLo, env, e->Iex.Binop.arg1);
iselInt64Expr(&yHi, &yLo, env, e->Iex.Binop.arg2);
addInstr(env, MIPSInstr_Alu(op, tHi, xHi, MIPSRH_Reg(yHi)));
addInstr(env, MIPSInstr_Alu(op, tLo, xLo, MIPSRH_Reg(yLo)));
*rHi = tHi;
*rLo = tLo;
return;
}
case Iop_Shr64: {
#if defined (_MIPSEL)
/* 64-bit logical shift right based on what gcc generates:
<shift>:
nor v0, zero, a2
sll a3, a1, 0x1
sllv a3, a3, v0
srlv v0, a0, a2
srlv v1, a1, a2
andi a0, a2, 0x20
or v0, a3, v0
movn v0, v1, a0
jr ra
movn v1, zero, a0
*/
HReg a0, a1;
HReg a0tmp = newVRegI(env);
HReg a2 = newVRegI(env);
HReg a3 = newVRegI(env);
HReg v0 = newVRegI(env);
HReg v1 = newVRegI(env);
HReg