blob: 850a9e65e43cf1444fddf212f2c8d83fc8fe7672 [file] [log] [blame]
/*---------------------------------------------------------------*/
/*--- begin host_ppc_isel.c ---*/
/*---------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2004-2013 OpenWorks LLP
info@open-works.net
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.
Neither the names of the U.S. Department of Energy nor the
University of California nor the names of its contributors may be
used to endorse or promote products derived from this software
without prior written permission.
*/
#include "libvex_basictypes.h"
#include "libvex_ir.h"
#include "libvex.h"
#include "ir_match.h"
#include "main_util.h"
#include "main_globals.h"
#include "host_generic_regs.h"
#include "host_generic_simd64.h"
#include "host_ppc_defs.h"
/* GPR register class for ppc32/64 */
#define HRcGPR(__mode64) (__mode64 ? HRcInt64 : HRcInt32)
/*---------------------------------------------------------*/
/*--- Register Usage Conventions ---*/
/*---------------------------------------------------------*/
/*
Integer Regs
------------
GPR0 Reserved
GPR1 Stack Pointer
GPR2 not used - TOC pointer
GPR3:10 Allocateable
GPR11 if mode64: not used - calls by ptr / env ptr for some langs
GPR12 if mode64: not used - exceptions / global linkage code
GPR13 not used - Thread-specific pointer
GPR14:28 Allocateable
GPR29 Unused by us (reserved for the dispatcher)
GPR30 AltiVec temp spill register
GPR31 GuestStatePointer
Of Allocateable regs:
if (mode64)
GPR3:10 Caller-saved regs
else
GPR3:12 Caller-saved regs
GPR14:29 Callee-saved regs
GPR3 [Return | Parameter] - carrying reg
GPR4:10 Parameter-carrying regs
Floating Point Regs
-------------------
FPR0:31 Allocateable
FPR0 Caller-saved - scratch reg
if (mode64)
FPR1:13 Caller-saved - param & return regs
else
FPR1:8 Caller-saved - param & return regs
FPR9:13 Caller-saved regs
FPR14:31 Callee-saved regs
Vector Regs (on processors with the VMX feature)
-----------
VR0-VR1 Volatile scratch registers
VR2-VR13 Volatile vector parameters registers
VR14-VR19 Volatile scratch registers
VR20-VR31 Non-volatile registers
VRSAVE Non-volatile 32-bit register
*/
/*---------------------------------------------------------*/
/*--- PPC FP Status & Control Register Conventions ---*/
/*---------------------------------------------------------*/
/*
Vex-generated code expects to run with the FPU set as follows: all
exceptions masked. The rounding mode is set appropriately before
each floating point insn emitted (or left unchanged if known to be
correct already). There are a few fp insns (fmr,fneg,fabs,fnabs),
which are unaffected by the rm and so the rounding mode is not set
prior to them.
At least on MPC7447A (Mac Mini), frsqrte is also not affected by
rounding mode. At some point the ppc docs get sufficiently vague
that the only way to find out is to write test programs.
*/
/* Notes on the FP instruction set, 6 Feb 06.
What exns -> CR1 ? Sets FPRF ? Observes RM ?
-------------------------------------------------------------
fmr[.] if . n n
fneg[.] if . n n
fabs[.] if . n n
fnabs[.] if . n n
fadd[.] if . y y
fadds[.] if . y y
fcfid[.] (Si64->dbl) if . y y
fcfidU[.] (Ui64->dbl) if . y y
fcfids[.] (Si64->sngl) if . Y Y
fcfidus[.] (Ui64->sngl) if . Y Y
fcmpo (cmp, result n n n
fcmpu to crfD) n n n
fctid[.] (dbl->i64) if . ->undef y
fctidz[.] (dbl->i64) if . ->undef rounds-to-zero
fctiw[.] (dbl->i32) if . ->undef y
fctiwz[.] (dbl->i32) if . ->undef rounds-to-zero
fdiv[.] if . y y
fdivs[.] if . y y
fmadd[.] if . y y
fmadds[.] if . y y
fmsub[.] if . y y
fmsubs[.] if . y y
fmul[.] if . y y
fmuls[.] if . y y
(note: for fnm*, rounding happens before final negation)
fnmadd[.] if . y y
fnmadds[.] if . y y
fnmsub[.] if . y y
fnmsubs[.] if . y y
fre[.] if . y y
fres[.] if . y y
frsqrte[.] if . y apparently not
fsqrt[.] if . y y
fsqrts[.] if . y y
fsub[.] if . y y
fsubs[.] if . y y
fpscr: bits 30-31 (ibm) is RM
24-29 (ibm) are exnmasks/non-IEEE bit, all zero
15-19 (ibm) is FPRF: class, <, =, >, UNord
ppc fe(guest) makes fpscr read as all zeros except RM (and maybe FPRF
in future)
mcrfs - move fpscr field to CR field
mtfsfi[.] - 4 bit imm moved to fpscr field
mtfsf[.] - move frS[low 1/2] to fpscr but using 8-bit field mask
mtfsb1[.] - set given fpscr bit
mtfsb0[.] - clear given fpscr bit
mffs[.] - move all fpscr to frD[low 1/2]
For [.] presumably cr1 is set with exn summary bits, as per
main FP insns
A single precision store truncates/denormalises the in-register value,
but does not round it. This is so that flds followed by fsts is
always the identity.
*/
/*---------------------------------------------------------*/
/*--- misc helpers ---*/
/*---------------------------------------------------------*/
/* These are duplicated in guest-ppc/toIR.c */
static IRExpr* unop ( IROp op, IRExpr* a )
{
return IRExpr_Unop(op, a);
}
static IRExpr* mkU32 ( UInt i )
{
return IRExpr_Const(IRConst_U32(i));
}
static IRExpr* bind ( Int binder )
{
return IRExpr_Binder(binder);
}
static Bool isZeroU8 ( IRExpr* e )
{
return e->tag == Iex_Const
&& e->Iex.Const.con->tag == Ico_U8
&& e->Iex.Const.con->Ico.U8 == 0;
}
/*---------------------------------------------------------*/
/*--- 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.
- vregmapLo holds the primary register for the IRTemp.
- vregmapMedLo holds the secondary register for the IRTemp,
if any is needed. That's only for Ity_I64 temps
in 32 bit mode or Ity_I128 temps in 64-bit mode.
- vregmapMedHi is only for dealing with Ity_I128 temps in
32 bit mode. It holds bits 95:64 (Intel numbering)
of the IRTemp.
- vregmapHi is also only for dealing with Ity_I128 temps
in 32 bit mode. It holds the most significant bits
(127:96 in Intel numbering) of the IRTemp.
- 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 to tell us if the host is 32 or 64bit.
This is set at the start and does not change.
- An IRExpr*, which may be NULL, holding the IR expression (an
IRRoundingMode-encoded value) to which the FPU's rounding mode
was most recently set. Setting to NULL is always safe. Used to
avoid redundant settings of the FPU's rounding mode, as
described in set_FPU_rounding_mode below.
- A VexMiscInfo*, needed for knowing how to generate
function calls for this target.
- 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.
*/
typedef
struct {
/* Constant -- are set at the start and do not change. */
IRTypeEnv* type_env;
// 64-bit mode 32-bit mode
HReg* vregmapLo; // Low 64-bits [63:0] Low 32-bits [31:0]
HReg* vregmapMedLo; // high 64-bits[127:64] Next 32-bits [63:32]
HReg* vregmapMedHi; // unused Next 32-bits [95:64]
HReg* vregmapHi; // unused highest 32-bits [127:96]
Int n_vregmap;
/* 27 Jan 06: Not currently used, but should be */
UInt hwcaps;
Bool mode64;
VexAbiInfo* vbi;
Bool chainingAllowed;
Addr64 max_ga;
/* These are modified as we go along. */
HInstrArray* code;
Int vreg_ctr;
IRExpr* previous_rm;
}
ISelEnv;
static HReg lookupIRTemp ( ISelEnv* env, IRTemp tmp )
{
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
return env->vregmapLo[tmp];
}
static void lookupIRTempPair ( HReg* vrHI, HReg* vrLO,
ISelEnv* env, IRTemp tmp )
{
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
vassert(! hregIsInvalid(env->vregmapMedLo[tmp]));
*vrLO = env->vregmapLo[tmp];
*vrHI = env->vregmapMedLo[tmp];
}
/* Only for used in 32-bit mode */
static void lookupIRTempQuad ( HReg* vrHi, HReg* vrMedHi, HReg* vrMedLo,
HReg* vrLo, ISelEnv* env, IRTemp tmp )
{
vassert(!env->mode64);
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
vassert(! hregIsInvalid(env->vregmapMedLo[tmp]));
*vrHi = env->vregmapHi[tmp];
*vrMedHi = env->vregmapMedHi[tmp];
*vrMedLo = env->vregmapMedLo[tmp];
*vrLo = env->vregmapLo[tmp];
}
static void addInstr ( ISelEnv* env, PPCInstr* instr )
{
addHInstr(env->code, instr);
if (vex_traceflags & VEX_TRACE_VCODE) {
ppPPCInstr(instr, env->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 newVRegF ( ISelEnv* env )
{
HReg reg = mkHReg(env->vreg_ctr, HRcFlt64, True/*virtual reg*/);
env->vreg_ctr++;
return reg;
}
static HReg newVRegV ( ISelEnv* env )
{
HReg reg = mkHReg(env->vreg_ctr, HRcVec128, True/*virtual reg*/);
env->vreg_ctr++;
return reg;
}
/*---------------------------------------------------------*/
/*--- 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.
'Word' refers to the size of the native machine word, that is,
32-bit int in 32-bit mode and 64-bit int in 64-bit mode. '2Word'
therefore refers to a double-width (64/128-bit) quantity in two
integer registers.
*/
/* 32-bit mode: compute an I8/I16/I32 into a GPR.
64-bit mode: compute an I8/I16/I32/I64 into a GPR. */
static HReg iselWordExpr_R_wrk ( ISelEnv* env, IRExpr* e );
static HReg iselWordExpr_R ( ISelEnv* env, IRExpr* e );
/* 32-bit mode: Compute an I8/I16/I32 into a RH
(reg-or-halfword-immediate).
64-bit mode: Compute an I8/I16/I32/I64 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 PPCRH* iselWordExpr_RH_wrk ( ISelEnv* env,
Bool syned, IRExpr* e );
static PPCRH* iselWordExpr_RH ( ISelEnv* env,
Bool syned, IRExpr* e );
/* 32-bit mode: compute an I32 into a RI (reg or 32-bit immediate).
64-bit mode: compute an I64 into a RI (reg or 64-bit immediate). */
static PPCRI* iselWordExpr_RI_wrk ( ISelEnv* env, IRExpr* e );
static PPCRI* iselWordExpr_RI ( ISelEnv* env, IRExpr* e );
/* In 32 bit mode ONLY, 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 PPCRH* iselWordExpr_RH5u_wrk ( ISelEnv* env, IRExpr* e );
static PPCRH* iselWordExpr_RH5u ( ISelEnv* env, IRExpr* e );
/* In 64-bit mode ONLY, 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 PPCRH* iselWordExpr_RH6u_wrk ( ISelEnv* env, IRExpr* e );
static PPCRH* iselWordExpr_RH6u ( ISelEnv* env, IRExpr* e );
/* 32-bit mode: compute an I32 into an AMode.
64-bit mode: compute an I64 into an AMode.
Requires to know (xferTy) the type of data to be loaded/stored
using this amode. That is so that, for 64-bit code generation, any
PPCAMode_IR returned will have an index (immediate offset) field
that is guaranteed to be 4-aligned, if there is any chance that the
amode is to be used in ld/ldu/lda/std/stdu.
Since there are no such restrictions on 32-bit insns, xferTy is
ignored for 32-bit code generation. */
static PPCAMode* iselWordExpr_AMode_wrk ( ISelEnv* env, IRExpr* e, IRType xferTy );
static PPCAMode* iselWordExpr_AMode ( ISelEnv* env, IRExpr* e, IRType xferTy );
static void iselInt128Expr_to_32x4_wrk ( HReg* rHi, HReg* rMedHi,
HReg* rMedLo, HReg* rLo,
ISelEnv* env, IRExpr* e );
static void iselInt128Expr_to_32x4 ( HReg* rHi, HReg* rMedHi,
HReg* rMedLo, HReg* rLo,
ISelEnv* env, IRExpr* e );
/* 32-bit mode ONLY: compute an I64 into a GPR pair. */
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 PPCCondCode iselCondCode_wrk ( ISelEnv* env, IRExpr* e );
static PPCCondCode 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 HReg iselVecExpr_wrk ( ISelEnv* env, IRExpr* e );
static HReg iselVecExpr ( ISelEnv* env, IRExpr* e );
/* 64-bit mode ONLY. */
static HReg iselDfp32Expr_wrk ( ISelEnv* env, IRExpr* e );
static HReg iselDfp32Expr ( ISelEnv* env, IRExpr* e );
static HReg iselDfp64Expr_wrk ( ISelEnv* env, IRExpr* e );
static HReg iselDfp64Expr ( ISelEnv* env, IRExpr* e );
/* 64-bit mode ONLY: compute an D128 into a GPR64 pair. */
static void iselDfp128Expr_wrk ( HReg* rHi, HReg* rLo, ISelEnv* env,
IRExpr* e );
static void iselDfp128Expr ( HReg* rHi, HReg* rLo, ISelEnv* env,
IRExpr* e );
/*---------------------------------------------------------*/
/*--- ISEL: Misc helpers ---*/
/*---------------------------------------------------------*/
/* Make an int reg-reg move. */
static PPCInstr* 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 PPCInstr_Alu(Palu_OR, r_dst, r_src, PPCRH_Reg(r_src));
}
/* Advance/retreat %r1 by n. */
static void add_to_sp ( ISelEnv* env, UInt n )
{
HReg sp = StackFramePtr(env->mode64);
vassert(n <= 1024 && (n%16) == 0);
addInstr(env, PPCInstr_Alu( Palu_ADD, sp, sp,
PPCRH_Imm(True,toUShort(n)) ));
}
static void sub_from_sp ( ISelEnv* env, UInt n )
{
HReg sp = StackFramePtr(env->mode64);
vassert(n <= 1024 && (n%16) == 0);
addInstr(env, PPCInstr_Alu( Palu_SUB, sp, sp,
PPCRH_Imm(True,toUShort(n)) ));
}
/*
returns a quadword aligned address on the stack
- copies SP, adds 16bytes, aligns to quadword.
use sub_from_sp(32) before calling this,
as expects to have 32 bytes to play with.
*/
static HReg get_sp_aligned16 ( ISelEnv* env )
{
HReg r = newVRegI(env);
HReg align16 = newVRegI(env);
addInstr(env, mk_iMOVds_RR(r, StackFramePtr(env->mode64)));
// add 16
addInstr(env, PPCInstr_Alu( Palu_ADD, r, r,
PPCRH_Imm(True,toUShort(16)) ));
// mask to quadword
addInstr(env,
PPCInstr_LI(align16, 0xFFFFFFFFFFFFFFF0ULL, env->mode64));
addInstr(env, PPCInstr_Alu(Palu_AND, r,r, PPCRH_Reg(align16)));
return r;
}
/* Load 2*I32 regs to fp reg */
static HReg mk_LoadRR32toFPR ( ISelEnv* env,
HReg r_srcHi, HReg r_srcLo )
{
HReg fr_dst = newVRegF(env);
PPCAMode *am_addr0, *am_addr1;
vassert(!env->mode64);
vassert(hregClass(r_srcHi) == HRcInt32);
vassert(hregClass(r_srcLo) == HRcInt32);
sub_from_sp( env, 16 ); // Move SP down 16 bytes
am_addr0 = PPCAMode_IR( 0, StackFramePtr(env->mode64) );
am_addr1 = PPCAMode_IR( 4, StackFramePtr(env->mode64) );
// store hi,lo as Ity_I32's
addInstr(env, PPCInstr_Store( 4, am_addr0, r_srcHi, env->mode64 ));
addInstr(env, PPCInstr_Store( 4, am_addr1, r_srcLo, env->mode64 ));
// load as float
addInstr(env, PPCInstr_FpLdSt(True/*load*/, 8, fr_dst, am_addr0));
add_to_sp( env, 16 ); // Reset SP
return fr_dst;
}
/* Load I64 reg to fp reg */
static HReg mk_LoadR64toFPR ( ISelEnv* env, HReg r_src )
{
HReg fr_dst = newVRegF(env);
PPCAMode *am_addr0;
vassert(env->mode64);
vassert(hregClass(r_src) == HRcInt64);
sub_from_sp( env, 16 ); // Move SP down 16 bytes
am_addr0 = PPCAMode_IR( 0, StackFramePtr(env->mode64) );
// store as Ity_I64
addInstr(env, PPCInstr_Store( 8, am_addr0, r_src, env->mode64 ));
// load as float
addInstr(env, PPCInstr_FpLdSt(True/*load*/, 8, fr_dst, am_addr0));
add_to_sp( env, 16 ); // Reset SP
return fr_dst;
}
/* Given an amode, return one which references 4 bytes further
along. */
static PPCAMode* advance4 ( ISelEnv* env, PPCAMode* am )
{
PPCAMode* am4 = dopyPPCAMode( am );
if (am4->tag == Pam_IR
&& am4->Pam.IR.index + 4 <= 32767) {
am4->Pam.IR.index += 4;
} else {
vpanic("advance4(ppc,host)");
}
return am4;
}
/* Given a guest-state array descriptor, an index expression and a
bias, generate a PPCAMode pointing at the relevant piece of
guest state. */
static
PPCAMode* genGuestArrayOffset ( ISelEnv* env, IRRegArray* descr,
IRExpr* off, Int bias )
{
HReg rtmp, roff;
Int elemSz = sizeofIRType(descr->elemTy);
Int nElems = descr->nElems;
Int shift = 0;
/* Throw out any cases we don't need. In theory there might be a
day where we need to handle others, but not today. */
if (nElems != 16 && nElems != 32)
vpanic("genGuestArrayOffset(ppc host)(1)");
switch (elemSz) {
case 4: shift = 2; break;
case 8: shift = 3; break;
default: vpanic("genGuestArrayOffset(ppc host)(2)");
}
if (bias < -100 || bias > 100) /* somewhat arbitrarily */
vpanic("genGuestArrayOffset(ppc host)(3)");
if (descr->base < 0 || descr->base > 5000) /* somewhat arbitrarily */
vpanic("genGuestArrayOffset(ppc host)(4)");
/* Compute off into a reg, %off. Then return:
addi %tmp, %off, bias (if bias != 0)
andi %tmp, nElems-1
sldi %tmp, shift
addi %tmp, %tmp, base
... Baseblockptr + %tmp ...
*/
roff = iselWordExpr_R(env, off);
rtmp = newVRegI(env);
addInstr(env, PPCInstr_Alu(
Palu_ADD,
rtmp, roff,
PPCRH_Imm(True/*signed*/, toUShort(bias))));
addInstr(env, PPCInstr_Alu(
Palu_AND,
rtmp, rtmp,
PPCRH_Imm(False/*unsigned*/, toUShort(nElems-1))));
addInstr(env, PPCInstr_Shft(
Pshft_SHL,
env->mode64 ? False : True/*F:64-bit, T:32-bit shift*/,
rtmp, rtmp,
PPCRH_Imm(False/*unsigned*/, toUShort(shift))));
addInstr(env, PPCInstr_Alu(
Palu_ADD,
rtmp, rtmp,
PPCRH_Imm(True/*signed*/, toUShort(descr->base))));
return
PPCAMode_RR( GuestStatePtr(env->mode64), rtmp );
}
/*---------------------------------------------------------*/
/*--- 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;
}
}
/* 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 )
{
PPCCondCode cc;
HReg argregs[PPC_N_REGPARMS];
HReg tmpregs[PPC_N_REGPARMS];
Bool go_fast;
Int n_args, i, argreg;
UInt argiregs;
Bool mode64 = env->mode64;
/* 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;
/* Do we need to force use of an odd-even reg pair for 64-bit args?
JRS 31-07-2013: is this still relevant, now that we are not
generating code for 32-bit AIX ? */
Bool regalign_int64s
= (!mode64) && env->vbi->host_ppc32_regalign_int64_args;
/* Marshal args for a call and do the call.
This function only deals with a tiny set of possibilities, which
cover all helpers in practice. The restrictions are that only
arguments in registers are supported, hence only PPC_N_REGPARMS x
(mode32:32 | mode64:64) integer bits in total can be passed.
In fact the only supported arg type is (mode32:I32 | mode64:I64).
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.
Generating code which is both efficient and correct when
parameters are to be passed in registers is difficult, for the
reasons elaborated in detail in comments attached to
doHelperCall() in priv/host-x86/isel.c. Here, we use a variant
of the method described in those comments.
The problem is split into two cases: the fast scheme and the
slow scheme. In the fast scheme, arguments are computed
directly into the target (real) registers. This is only safe
when we can be sure that computation of each argument will not
trash any real registers set by computation of any other
argument.
In the slow scheme, all args are first computed into vregs, and
once they are all done, they are moved to the relevant real
regs. This always gives correct code, but it also gives a bunch
of vreg-to-rreg moves which are usually redundant but are hard
for the register allocator to get rid of.
To decide which scheme to use, all argument expressions are
first examined. If they are all so simple that it is clear they
will be evaluated without use of any fixed registers, use the
fast scheme, else use the slow scheme. Note also that only
unconditional calls may use the fast scheme, since having to
compute a condition expression could itself trash real
registers.
Note this requires being able to examine an expression and
determine whether or not evaluation of it might use a fixed
register. That requires knowledge of how the rest of this insn
selector works. Currently just the following 3 are regarded as
safe -- hopefully they cover the majority of arguments in
practice: IRExpr_Tmp IRExpr_Const IRExpr_Get.
*/
/* Note that the cee->regparms field is meaningless on PPC32/64 host
(since there is only one calling convention) and so we always
ignore it. */
n_args = 0;
for (i = 0; args[i]; i++)
n_args++;
if (n_args > PPC_N_REGPARMS) {
vpanic("doHelperCall(PPC): cannot currently handle > 8 args");
// PPC_N_REGPARMS
}
/* This is kind of stupid .. the arrays are sized as PPC_N_REGPARMS
but we then assume that that value is 8. */
vassert(PPC_N_REGPARMS == 8);
argregs[0] = hregPPC_GPR3(mode64);
argregs[1] = hregPPC_GPR4(mode64);
argregs[2] = hregPPC_GPR5(mode64);
argregs[3] = hregPPC_GPR6(mode64);
argregs[4] = hregPPC_GPR7(mode64);
argregs[5] = hregPPC_GPR8(mode64);
argregs[6] = hregPPC_GPR9(mode64);
argregs[7] = hregPPC_GPR10(mode64);
argiregs = 0;
tmpregs[0] = tmpregs[1] = tmpregs[2] =
tmpregs[3] = tmpregs[4] = tmpregs[5] =
tmpregs[6] = tmpregs[7] = 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++) {
IRExpr* arg = args[i];
if (UNLIKELY(arg->tag == Iex_BBPTR)) {
/* that's OK */
}
else if (UNLIKELY(arg->tag == Iex_VECRET)) {
/* This implies ill-formed IR, since if the IR was
well-formed, the return-type test above would have
filtered it out. */
vpanic("doHelperCall(PPC): invalid IR");
}
else if (mightRequireFixedRegs(arg)) {
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 < PPC_N_REGPARMS);
if (arg->tag == Iex_BBPTR) {
argiregs |= (1 << (argreg+3));
addInstr(env, mk_iMOVds_RR( argregs[argreg],
GuestStatePtr(mode64) ));
argreg++;
} else {
vassert(arg->tag != Iex_VECRET);
IRType ty = typeOfIRExpr(env->type_env, arg);
vassert(ty == Ity_I32 || ty == Ity_I64);
if (!mode64) {
if (ty == Ity_I32) {
argiregs |= (1 << (argreg+3));
addInstr(env,
mk_iMOVds_RR( argregs[argreg],
iselWordExpr_R(env, arg) ));
} else { // Ity_I64 in 32-bit mode
HReg rHi, rLo;
if (regalign_int64s && (argreg%2) == 1)
// ppc32 ELF abi spec for passing LONG_LONG
argreg++; // XXX: odd argreg => even rN
vassert(argreg < PPC_N_REGPARMS-1);
iselInt64Expr(&rHi,&rLo, env, arg);
argiregs |= (1 << (argreg+3));
addInstr(env, mk_iMOVds_RR( argregs[argreg++], rHi ));
argiregs |= (1 << (argreg+3));
addInstr(env, mk_iMOVds_RR( argregs[argreg], rLo));
}
} else { // mode64
argiregs |= (1 << (argreg+3));
addInstr(env, mk_iMOVds_RR( argregs[argreg],
iselWordExpr_R(env, arg) ));
}
argreg++;
} /* if (arg == IRExprP__BBPR) */
}
/* Fast scheme only applies for unconditional calls. Hence: */
cc = mk_PPCCondCode( Pct_ALWAYS, Pcf_NONE );
} else {
/* SLOW SCHEME; move via temporaries */
argreg = 0;
/* If we have a vector return type, allocate a place for it on
the stack and record its address. Rather than figure out the
complexities of PPC{32,64} ELF ABI stack frame layout, simply
drop the SP by 1024 and allocate the return point in the
middle. I think this should comfortably clear any ABI
mandated register save areas. Note that it doesn't maintain
the backchain as it should, since we're not doing st{d,w}u to
adjust the SP, but .. that doesn't seem to be a big deal.
Since we're not expecting to have to unwind out of here. */
HReg r_vecRetAddr = INVALID_HREG;
if (retTy == Ity_V128) {
r_vecRetAddr = newVRegI(env);
sub_from_sp(env, 512);
addInstr(env, mk_iMOVds_RR( r_vecRetAddr, StackFramePtr(mode64) ));
sub_from_sp(env, 512);
}
else if (retTy == Ity_V256) {
vassert(0); //ATC
r_vecRetAddr = newVRegI(env);
sub_from_sp(env, 512);
addInstr(env, mk_iMOVds_RR( r_vecRetAddr, StackFramePtr(mode64) ));
sub_from_sp(env, 512);
}
vassert(n_args >= 0 && n_args <= 8);
for (i = 0; i < n_args; i++) {
IRExpr* arg = args[i];
vassert(argreg < PPC_N_REGPARMS);
if (UNLIKELY(arg->tag == Iex_BBPTR)) {
tmpregs[argreg] = newVRegI(env);
addInstr(env, mk_iMOVds_RR( tmpregs[argreg],
GuestStatePtr(mode64) ));
nBBPTRs++;
}
else if (UNLIKELY(arg->tag == Iex_VECRET)) {
/* We stashed the address of the return slot earlier, so just
retrieve it now. */
vassert(!hregIsInvalid(r_vecRetAddr));
tmpregs[i] = r_vecRetAddr;
nVECRETs++;
}
else {
IRType ty = typeOfIRExpr(env->type_env, arg);
vassert(ty == Ity_I32 || ty == Ity_I64);
if (!mode64) {
if (ty == Ity_I32) {
tmpregs[argreg] = iselWordExpr_R(env, arg);
} else { // Ity_I64 in 32-bit mode
HReg rHi, rLo;
if (regalign_int64s && (argreg%2) == 1)
// ppc32 ELF abi spec for passing LONG_LONG
argreg++; // XXX: odd argreg => even rN
vassert(argreg < PPC_N_REGPARMS-1);
iselInt64Expr(&rHi,&rLo, env, arg);
tmpregs[argreg++] = rHi;
tmpregs[argreg] = rLo;
}
} else { // mode64
tmpregs[argreg] = iselWordExpr_R(env, arg);
}
}
argreg++;
}
/* 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 = mk_PPCCondCode( Pct_ALWAYS, Pcf_NONE );
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 );
}
}
/* 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+3));
addInstr( env, mk_iMOVds_RR( argregs[i], tmpregs[i] ) );
}
}
/* Do final checks, set the return values, and generate the call
instruction proper. */
if (retTy == Ity_V128 || retTy == Ity_V256) {
vassert(nVECRETs == 1);
} else {
vassert(nVECRETs == 0);
}
vassert(nBBPTRs == 0 || nBBPTRs == 1);
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:
/* Result is 512 bytes up the stack, and after it has been
retrieved, adjust SP upwards by 1024. */
*retloc = mk_RetLoc_spRel(RLPri_V128SpRel, 512);
*stackAdjustAfterCall = 1024;
break;
case Ity_V256:
vassert(0); // ATC
/* Ditto */
*retloc = mk_RetLoc_spRel(RLPri_V256SpRel, 512);
*stackAdjustAfterCall = 1024;
break;
default:
/* IR can denote other possible return types, but we don't
handle those here. */
vassert(0);
}
/* Finally, generate the call itself. This needs the *retloc value
set in the switch above, which is why it's at the end. */
ULong target = mode64 ? Ptr_to_ULong(cee->addr)
: toUInt(Ptr_to_ULong(cee->addr));
addInstr(env, PPCInstr_Call( cc, (Addr64)target, argiregs, *retloc ));
}
/*---------------------------------------------------------*/
/*--- ISEL: FP rounding mode helpers ---*/
/*---------------------------------------------------------*/
///* Set FPU's rounding mode to the default */
//static
//void set_FPU_rounding_default ( ISelEnv* env )
//{
// HReg fr_src = newVRegF(env);
// HReg r_src = newVRegI(env);
//
// /* Default rounding mode = 0x0
// Only supporting the rounding-mode bits - the rest of FPSCR is 0x0
// - so we can set the whole register at once (faster)
// note: upper 32 bits ignored by FpLdFPSCR
// */
// addInstr(env, PPCInstr_LI(r_src, 0x0, env->mode64));
// if (env->mode64) {
// fr_src = mk_LoadR64toFPR( env, r_src ); // 1*I64 -> F64
// } else {
// fr_src = mk_LoadRR32toFPR( env, r_src, r_src ); // 2*I32 -> F64
// }
// addInstr(env, PPCInstr_FpLdFPSCR( fr_src ));
//}
/* Convert IR rounding mode to PPC encoding */
static HReg roundModeIRtoPPC ( ISelEnv* env, HReg r_rmIR )
{
/*
rounding mode | PPC | IR
-----------------------------------------------
to nearest, ties to even | 000 | 000
to zero | 001 | 011
to +infinity | 010 | 010
to -infinity | 011 | 001
+++++ Below are the extended rounding modes for decimal floating point +++++
to nearest, ties away from 0 | 100 | 100
to nearest, ties toward 0 | 101 | 111
to away from 0 | 110 | 110
to prepare for shorter precision | 111 | 101
*/
HReg r_rmPPC = newVRegI(env);
HReg r_tmp1 = newVRegI(env);
HReg r_tmp2 = newVRegI(env);
vassert(hregClass(r_rmIR) == HRcGPR(env->mode64));
// r_rmPPC = XOR(r_rmIR, r_rmIR << 1) & 3
//
// slwi tmp1, r_rmIR, 1
// xor tmp1, r_rmIR, tmp1
// andi r_rmPPC, tmp1, 3
addInstr(env, PPCInstr_Shft(Pshft_SHL, True/*32bit shift*/,
r_tmp1, r_rmIR, PPCRH_Imm(False,1)));
addInstr( env, PPCInstr_Alu( Palu_AND,
r_tmp2, r_tmp1, PPCRH_Imm( False, 3 ) ) );
addInstr( env, PPCInstr_Alu( Palu_XOR,
r_rmPPC, r_rmIR, PPCRH_Reg( r_tmp2 ) ) );
return r_rmPPC;
}
/* Set the FPU's rounding mode: 'mode' is an I32-typed expression
denoting a value in the range 0 .. 7, indicating a round mode
encoded as per type IRRoundingMode. Set the PPC FPSCR to have the
same rounding. When the dfp_rm arg is True, set the decimal
floating point rounding mode bits (29:31); otherwise, set the
binary floating point rounding mode bits (62:63).
For speed & simplicity, we're setting the *entire* FPSCR here.
Setting the rounding mode is expensive. So this function tries to
avoid repeatedly setting the rounding mode to the same thing by
first comparing 'mode' to the 'mode' tree supplied in the previous
call to this function, if any. (The previous value is stored in
env->previous_rm.) If 'mode' is a single IR temporary 't' and
env->previous_rm is also just 't', then the setting is skipped.
This is safe because of the SSA property of IR: an IR temporary can
only be defined once and so will have the same value regardless of
where it appears in the block. Cool stuff, SSA.
A safety condition: all attempts to set the RM must be aware of
this mechanism - by being routed through the functions here.
Of course this only helps if blocks where the RM is set more than
once and it is set to the same value each time, *and* that value is
held in the same IR temporary each time. In order to assure the
latter as much as possible, the IR optimiser takes care to do CSE
on any block with any sign of floating point activity.
*/
static
void _set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode, Bool dfp_rm )
{
HReg fr_src = newVRegF(env);
HReg r_src;
vassert(typeOfIRExpr(env->type_env,mode) == Ity_I32);
/* Do we need to do anything? */
if (env->previous_rm
&& env->previous_rm->tag == Iex_RdTmp
&& mode->tag == Iex_RdTmp
&& env->previous_rm->Iex.RdTmp.tmp == mode->Iex.RdTmp.tmp) {
/* no - setting it to what it was before. */
vassert(typeOfIRExpr(env->type_env, env->previous_rm) == Ity_I32);
return;
}
/* No luck - we better set it, and remember what we set it to. */
env->previous_rm = mode;
/* Only supporting the rounding-mode bits - the rest of FPSCR is
0x0 - so we can set the whole register at once (faster). */
// Resolve rounding mode and convert to PPC representation
r_src = roundModeIRtoPPC( env, iselWordExpr_R(env, mode) );
// gpr -> fpr
if (env->mode64) {
if (dfp_rm) {
HReg r_tmp1 = newVRegI( env );
addInstr( env,
PPCInstr_Shft( Pshft_SHL, False/*64bit shift*/,
r_tmp1, r_src, PPCRH_Imm( False, 32 ) ) );
fr_src = mk_LoadR64toFPR( env, r_tmp1 );
} else {
fr_src = mk_LoadR64toFPR( env, r_src ); // 1*I64 -> F64
}
} else {
if (dfp_rm) {
HReg r_zero = newVRegI( env );
addInstr( env, PPCInstr_LI( r_zero, 0, env->mode64 ) );
fr_src = mk_LoadRR32toFPR( env, r_src, r_zero );
} else {
fr_src = mk_LoadRR32toFPR( env, r_src, r_src ); // 2*I32 -> F64
}
}
// Move to FPSCR
addInstr(env, PPCInstr_FpLdFPSCR( fr_src, dfp_rm ));
}
static void set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode )
{
_set_FPU_rounding_mode(env, mode, False);
}
static void set_FPU_DFP_rounding_mode ( ISelEnv* env, IRExpr* mode )
{
_set_FPU_rounding_mode(env, mode, True);
}
/*---------------------------------------------------------*/
/*--- ISEL: vector helpers ---*/
/*---------------------------------------------------------*/
/* Generate all-zeroes into a new vector register.
*/
static HReg generate_zeroes_V128 ( ISelEnv* env )
{
HReg dst = newVRegV(env);
addInstr(env, PPCInstr_AvBinary(Pav_XOR, dst, dst, dst));
return dst;
}
/* Generate all-ones into a new vector register.
*/
static HReg generate_ones_V128 ( ISelEnv* env )
{
HReg dst = newVRegV(env);
PPCVI5s * src = PPCVI5s_Imm(-1);
addInstr(env, PPCInstr_AvSplat(8, dst, src));
return dst;
}
/*
Generates code for AvSplat
- takes in IRExpr* of type 8|16|32
returns vector reg of duplicated lanes of input
- uses AvSplat(imm) for imms up to simm6.
otherwise must use store reg & load vector
*/
static HReg mk_AvDuplicateRI( ISelEnv* env, IRExpr* e )
{
HReg r_src;
HReg dst = newVRegV(env);
PPCRI* ri = iselWordExpr_RI(env, e);
IRType ty = typeOfIRExpr(env->type_env,e);
UInt sz = (ty == Ity_I8) ? 8 : (ty == Ity_I16) ? 16 : 32;
vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32);
/* special case: immediate */
if (ri->tag == Pri_Imm) {
Int simm32 = (Int)ri->Pri.Imm;
/* figure out if it's do-able with imm splats. */
if (simm32 >= -32 && simm32 <= 31) {
Char simm6 = (Char)simm32;
if (simm6 > 15) { /* 16:31 inclusive */
HReg v1 = newVRegV(env);
HReg v2 = newVRegV(env);
addInstr(env, PPCInstr_AvSplat(sz, v1, PPCVI5s_Imm(-16)));
addInstr(env, PPCInstr_AvSplat(sz, v2, PPCVI5s_Imm(simm6-16)));
addInstr(env,
(sz== 8) ? PPCInstr_AvBin8x16(Pav_SUBU, dst, v2, v1) :
(sz==16) ? PPCInstr_AvBin16x8(Pav_SUBU, dst, v2, v1)
: PPCInstr_AvBin32x4(Pav_SUBU, dst, v2, v1) );
return dst;
}
if (simm6 < -16) { /* -32:-17 inclusive */
HReg v1 = newVRegV(env);
HReg v2 = newVRegV(env);
addInstr(env, PPCInstr_AvSplat(sz, v1, PPCVI5s_Imm(-16)));
addInstr(env, PPCInstr_AvSplat(sz, v2, PPCVI5s_Imm(simm6+16)));
addInstr(env,
(sz== 8) ? PPCInstr_AvBin8x16(Pav_ADDU, dst, v2, v1) :
(sz==16) ? PPCInstr_AvBin16x8(Pav_ADDU, dst, v2, v1)
: PPCInstr_AvBin32x4(Pav_ADDU, dst, v2, v1) );
return dst;
}
/* simplest form: -16:15 inclusive */
addInstr(env, PPCInstr_AvSplat(sz, dst, PPCVI5s_Imm(simm6)));
return dst;
}
/* no luck; use the Slow way. */
r_src = newVRegI(env);
addInstr(env, PPCInstr_LI(r_src, (Long)simm32, env->mode64));
}
else {
r_src = ri->Pri.Reg;
}
/* default case: store r_src in lowest lane of 16-aligned mem,
load vector, splat lowest lane to dst */
{
/* CAB: Maybe faster to store r_src multiple times (sz dependent),
and simply load the vector? */
HReg r_aligned16;
HReg v_src = newVRegV(env);
PPCAMode *am_off12;
sub_from_sp( env, 32 ); // Move SP down
/* Get a 16-aligned address within our stack space */
r_aligned16 = get_sp_aligned16( env );
am_off12 = PPCAMode_IR( 12, r_aligned16 );
/* Store r_src in low word of 16-aligned mem */
addInstr(env, PPCInstr_Store( 4, am_off12, r_src, env->mode64 ));
/* Load src to vector[low lane] */
addInstr(env, PPCInstr_AvLdSt( True/*ld*/, 4, v_src, am_off12 ) );
add_to_sp( env, 32 ); // Reset SP
/* Finally, splat v_src[low_lane] to dst */
addInstr(env, PPCInstr_AvSplat(sz, dst, PPCVI5s_Reg(v_src)));
return dst;
}
}
/* for each lane of vSrc: lane == nan ? laneX = all 1's : all 0's */
static HReg isNan ( ISelEnv* env, HReg vSrc )
{
HReg zeros, msk_exp, msk_mnt, expt, mnts, vIsNan;
vassert(hregClass(vSrc) == HRcVec128);
zeros = mk_AvDuplicateRI(env, mkU32(0));
msk_exp = mk_AvDuplicateRI(env, mkU32(0x7F800000));
msk_mnt = mk_AvDuplicateRI(env, mkU32(0x7FFFFF));
expt = newVRegV(env);
mnts = newVRegV(env);
vIsNan = newVRegV(env);
/* 32bit float => sign(1) | exponent(8) | mantissa(23)
nan => exponent all ones, mantissa > 0 */
addInstr(env, PPCInstr_AvBinary(Pav_AND, expt, vSrc, msk_exp));
addInstr(env, PPCInstr_AvBin32x4(Pav_CMPEQU, expt, expt, msk_exp));
addInstr(env, PPCInstr_AvBinary(Pav_AND, mnts, vSrc, msk_mnt));
addInstr(env, PPCInstr_AvBin32x4(Pav_CMPGTU, mnts, mnts, zeros));
addInstr(env, PPCInstr_AvBinary(Pav_AND, vIsNan, expt, mnts));
return vIsNan;
}
/*---------------------------------------------------------*/
/*--- 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 ... */
# if 0
vex_printf("\n"); ppIRExpr(e); vex_printf("\n");
# endif
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 )
{
Bool mode64 = env->mode64;
MatchInfo mi;
DECLARE_PATTERN(p_32to1_then_1Uto8);
IRType ty = typeOfIRExpr(env->type_env,e);
vassert(ty == Ity_I8 || ty == Ity_I16 ||
ty == Ity_I32 || ((ty == Ity_I64) && mode64));
switch (e->tag) {
/* --------- TEMP --------- */
case Iex_RdTmp:
return lookupIRTemp(env, e->Iex.RdTmp.tmp);
/* --------- LOAD --------- */
case Iex_Load: {
HReg r_dst;
PPCAMode* am_addr;
if (e->Iex.Load.end != Iend_BE)
goto irreducible;
r_dst = newVRegI(env);
am_addr = iselWordExpr_AMode( env, e->Iex.Load.addr, ty/*of xfer*/ );
addInstr(env, PPCInstr_Load( toUChar(sizeofIRType(ty)),
r_dst, am_addr, mode64 ));
return r_dst;
/*NOTREACHED*/
}
/* --------- BINARY OP --------- */
case Iex_Binop: {
PPCAluOp aluOp;
PPCShftOp shftOp;
/* Is it an addition or logical style op? */
switch (e->Iex.Binop.op) {
case Iop_Add8: case Iop_Add16: case Iop_Add32: case Iop_Add64:
aluOp = Palu_ADD; break;
case Iop_Sub8: case Iop_Sub16: case Iop_Sub32: case Iop_Sub64:
aluOp = Palu_SUB; break;
case Iop_And8: case Iop_And16: case Iop_And32: case Iop_And64:
aluOp = Palu_AND; break;
case Iop_Or8: case Iop_Or16: case Iop_Or32: case Iop_Or64:
aluOp = Palu_OR; break;
case Iop_Xor8: case Iop_Xor16: case Iop_Xor32: case Iop_Xor64:
aluOp = Palu_XOR; break;
default:
aluOp = Palu_INVALID; break;
}
/* For commutative ops we assume any literal
values are on the second operand. */
if (aluOp != Palu_INVALID) {
HReg r_dst = newVRegI(env);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
PPCRH* ri_srcR = NULL;
/* get right arg into an RH, in the appropriate way */
switch (aluOp) {
case Palu_ADD: case Palu_SUB:
ri_srcR = iselWordExpr_RH(env, True/*signed*/,
e->Iex.Binop.arg2);
break;
case Palu_AND: case Palu_OR: case Palu_XOR:
ri_srcR = iselWordExpr_RH(env, False/*signed*/,
e->Iex.Binop.arg2);
break;
default:
vpanic("iselWordExpr_R_wrk-aluOp-arg2");
}
addInstr(env, PPCInstr_Alu(aluOp, r_dst, r_srcL, ri_srcR));
return r_dst;
}
/* a shift? */
switch (e->Iex.Binop.op) {
case Iop_Shl8: case Iop_Shl16: case Iop_Shl32: case Iop_Shl64:
shftOp = Pshft_SHL; break;
case Iop_Shr8: case Iop_Shr16: case Iop_Shr32: case Iop_Shr64:
shftOp = Pshft_SHR; break;
case Iop_Sar8: case Iop_Sar16: case Iop_Sar32: case Iop_Sar64:
shftOp = Pshft_SAR; break;
default:
shftOp = Pshft_INVALID; break;
}
/* we assume any literal values are on the second operand. */
if (shftOp != Pshft_INVALID) {
HReg r_dst = newVRegI(env);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
PPCRH* ri_srcR = NULL;
/* get right arg into an RH, in the appropriate way */
switch (shftOp) {
case Pshft_SHL: case Pshft_SHR: case Pshft_SAR:
if (!mode64)
ri_srcR = iselWordExpr_RH5u(env, e->Iex.Binop.arg2);
else
ri_srcR = iselWordExpr_RH6u(env, e->Iex.Binop.arg2);
break;
default:
vpanic("iselIntExpr_R_wrk-shftOp-arg2");
}
/* widen the left arg if needed */
if (shftOp == Pshft_SHR || shftOp == Pshft_SAR) {
if (ty == Ity_I8 || ty == Ity_I16) {
PPCRH* amt = PPCRH_Imm(False,
toUShort(ty == Ity_I8 ? 24 : 16));
HReg tmp = newVRegI(env);
addInstr(env, PPCInstr_Shft(Pshft_SHL,
True/*32bit shift*/,
tmp, r_srcL, amt));
addInstr(env, PPCInstr_Shft(shftOp,
True/*32bit shift*/,
tmp, tmp, amt));
r_srcL = tmp;
vassert(0); /* AWAITING TEST CASE */
}
}
/* Only 64 expressions need 64bit shifts,
32bit shifts are fine for all others */
if (ty == Ity_I64) {
vassert(mode64);
addInstr(env, PPCInstr_Shft(shftOp, False/*64bit shift*/,
r_dst, r_srcL, ri_srcR));
} else {
addInstr(env, PPCInstr_Shft(shftOp, True/*32bit shift*/,
r_dst, r_srcL, ri_srcR));
}
return r_dst;
}
/* How about a div? */
if (e->Iex.Binop.op == Iop_DivS32 ||
e->Iex.Binop.op == Iop_DivU32 ||
e->Iex.Binop.op == Iop_DivS32E ||
e->Iex.Binop.op == Iop_DivU32E) {
Bool syned = toBool((e->Iex.Binop.op == Iop_DivS32) || (e->Iex.Binop.op == Iop_DivS32E));
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,
PPCInstr_Div( ( ( e->Iex.Binop.op == Iop_DivU32E )
|| ( e->Iex.Binop.op == Iop_DivS32E ) ) ? True
: False,
syned,
True/*32bit div*/,
r_dst,
r_srcL,
r_srcR ) );
return r_dst;
}
if (e->Iex.Binop.op == Iop_DivS64 ||
e->Iex.Binop.op == Iop_DivU64 || e->Iex.Binop.op == Iop_DivS64E
|| e->Iex.Binop.op == Iop_DivU64E ) {
Bool syned = toBool((e->Iex.Binop.op == Iop_DivS64) ||(e->Iex.Binop.op == Iop_DivS64E));
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);
vassert(mode64);
addInstr( env,
PPCInstr_Div( ( ( e->Iex.Binop.op == Iop_DivS64E )
|| ( e->Iex.Binop.op
== Iop_DivU64E ) ) ? True
: False,
syned,
False/*64bit div*/,
r_dst,
r_srcL,
r_srcR ) );
return r_dst;
}
/* No? Anyone for a mul? */
if (e->Iex.Binop.op == Iop_Mul32
|| e->Iex.Binop.op == Iop_Mul64) {
Bool syned = False;
Bool sz32 = (e->Iex.Binop.op != Iop_Mul64);
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, PPCInstr_MulL(syned, False/*lo32*/, sz32,
r_dst, r_srcL, r_srcR));
return r_dst;
}
/* 32 x 32 -> 64 multiply */
if (mode64
&& (e->Iex.Binop.op == Iop_MullU32
|| e->Iex.Binop.op == Iop_MullS32)) {
HReg tLo = newVRegI(env);
HReg tHi = newVRegI(env);
HReg r_dst = newVRegI(env);
Bool syned = toBool(e->Iex.Binop.op == Iop_MullS32);
HReg r_srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r_srcR = iselWordExpr_R(env, e->Iex.Binop.arg2);
addInstr(env, PPCInstr_MulL(False/*signedness irrelevant*/,
False/*lo32*/, True/*32bit mul*/,
tLo, r_srcL, r_srcR));
addInstr(env, PPCInstr_MulL(syned,
True/*hi32*/, True/*32bit mul*/,
tHi, r_srcL, r_srcR));
addInstr(env, PPCInstr_Shft(Pshft_SHL, False/*64bit shift*/,
r_dst, tHi, PPCRH_Imm(False,32)));
addInstr(env, PPCInstr_Alu(Palu_OR,
r_dst, r_dst, PPCRH_Reg(tLo)));
return r_dst;
}
/* El-mutanto 3-way compare? */
if (e->Iex.Binop.op == Iop_CmpORD32S
|| e->Iex.Binop.op == Iop_CmpORD32U) {
Bool syned = toBool(e->Iex.Binop.op == Iop_CmpORD32S);
HReg dst = newVRegI(env);
HReg srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
PPCRH* srcR = iselWordExpr_RH(env, syned, e->Iex.Binop.arg2);
addInstr(env, PPCInstr_Cmp(syned, True/*32bit cmp*/,
7/*cr*/, srcL, srcR));
addInstr(env, PPCInstr_MfCR(dst));
addInstr(env, PPCInstr_Alu(Palu_AND, dst, dst,
PPCRH_Imm(False,7<<1)));
return dst;
}
if (e->Iex.Binop.op == Iop_CmpORD64S
|| e->Iex.Binop.op == Iop_CmpORD64U) {
Bool syned = toBool(e->Iex.Binop.op == Iop_CmpORD64S);
HReg dst = newVRegI(env);
HReg srcL = iselWordExpr_R(env, e->Iex.Binop.arg1);
PPCRH* srcR = iselWordExpr_RH(env, syned, e->Iex.Binop.arg2);
vassert(mode64);
addInstr(env, PPCInstr_Cmp(syned, False/*64bit cmp*/,
7/*cr*/, srcL, srcR));
addInstr(env, PPCInstr_MfCR(dst));
addInstr(env, PPCInstr_Alu(Palu_AND, dst, dst,
PPCRH_Imm(False,7<<1)));
return dst;
}
if (e->Iex.Binop.op == Iop_Max32U) {
HReg r1 = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r2 = iselWordExpr_R(env, e->Iex.Binop.arg2);
HReg rdst = newVRegI(env);
PPCCondCode cc = mk_PPCCondCode( Pct_TRUE, Pcf_7LT );
addInstr(env, mk_iMOVds_RR(rdst, r1));
addInstr(env, PPCInstr_Cmp(False/*unsigned*/, True/*32bit cmp*/,
7/*cr*/, rdst, PPCRH_Reg(r2)));
addInstr(env, PPCInstr_CMov(cc, rdst, PPCRI_Reg(r2)));
return rdst;
}
if (e->Iex.Binop.op == Iop_32HLto64) {
HReg r_Hi = iselWordExpr_R(env, e->Iex.Binop.arg1);
HReg r_Lo = iselWordExpr_R(env, e->Iex.Binop.arg2);
HReg r_Tmp = newVRegI(env);
HReg r_dst = newVRegI(env);
HReg msk = newVRegI(env);
vassert(mode64);
/* r_dst = OR( r_Hi<<32, r_Lo ) */
addInstr(env, PPCInstr_Shft(Pshft_SHL, False/*64bit shift*/,
r_dst, r_Hi, PPCRH_Imm(False,32)));
addInstr(env, PPCInstr_LI(msk, 0xFFFFFFFF, mode64));
addInstr(env, PPCInstr_Alu( Palu_AND, r_Tmp, r_Lo,
PPCRH_Reg(msk) ));
addInstr(env, PPCInstr_Alu( Palu_OR, r_dst, r_dst,
PPCRH_Reg(r_Tmp) ));
return r_dst;
}
if ((e->Iex.Binop.op == Iop_CmpF64) ||
(e->Iex.Binop.op == Iop_CmpD64) ||
(e->Iex.Binop.op == Iop_CmpD128)) {
HReg fr_srcL;
HReg fr_srcL_lo;
HReg fr_srcR;
HReg fr_srcR_lo;
HReg r_ccPPC = 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);
if (e->Iex.Binop.op == Iop_CmpF64) {
fr_srcL = iselDblExpr(env, e->Iex.Binop.arg1);
fr_srcR = iselDblExpr(env, e->Iex.Binop.arg2);
addInstr(env, PPCInstr_FpCmp(r_ccPPC, fr_srcL, fr_srcR));
} else if (e->Iex.Binop.op == Iop_CmpD64) {
fr_srcL = iselDfp64Expr(env, e->Iex.Binop.arg1);
fr_srcR = iselDfp64Expr(env, e->Iex.Binop.arg2);
addInstr(env, PPCInstr_Dfp64Cmp(r_ccPPC, fr_srcL, fr_srcR));
} else { // e->Iex.Binop.op == Iop_CmpD128
iselDfp128Expr(&fr_srcL, &fr_srcL_lo, env, e->Iex.Binop.arg1);
iselDfp128Expr(&fr_srcR, &fr_srcR_lo, env, e->Iex.Binop.arg2);
addInstr(env, PPCInstr_Dfp128Cmp(r_ccPPC, fr_srcL, fr_srcL_lo,
fr_srcR, fr_srcR_lo));
}
/* Map compare result from PPC to IR,
conforming to CmpF64 definition. */
/*
FP cmp result | PPC | IR
--------------------------
UN | 0x1 | 0x45
EQ | 0x2 | 0x40
GT | 0x4 | 0x00
LT | 0x8 | 0x01
*/
// r_ccIR_b0 = r_ccPPC[0] | r_ccPPC[3]
addInstr(env, PPCInstr_Shft(Pshft_SHR, True/*32bit shift*/,
r_ccIR_b0, r_ccPPC,
PPCRH_Imm(False,0x3)));
addInstr(env, PPCInstr_Alu(Palu_OR, r_ccIR_b0,
r_ccPPC, PPCRH_Reg(r_ccIR_b0)));
addInstr(env, PPCInstr_Alu(Palu_AND, r_ccIR_b0,
r_ccIR_b0, PPCRH_Imm(False,0x1)));
// r_ccIR_b2 = r_ccPPC[0]
addInstr(env, PPCInstr_Shft(Pshft_SHL, True/*32bit shift*/,
r_ccIR_b2, r_ccPPC,
PPCRH_Imm(False,0x2)));
addInstr(env, PPCInstr_Alu(Palu_AND, r_ccIR_b2,
r_ccIR_b2, PPCRH_Imm(False,0x4)));
// r_ccIR_b6 = r_ccPPC[0] | r_ccPPC[1]
addInstr(env, PPCInstr_Shft(Pshft_SHR, True/*32bit shift*/,
r_ccIR_b6, r_ccPPC,
PPCRH_Imm(False,0x1)));
addInstr(env, PPCInstr_Alu(Palu_OR, r_ccIR_b6,
r_ccPPC, PPCRH_Reg(r_ccIR_b6)));
addInstr(env, PPCInstr_Shft(Pshft_SHL, True/*32bit shift*/,
r_ccIR_b6, r_ccIR_b6,
PPCRH_Imm(False,0x6)));
addInstr(env, PPCInstr_Alu(Palu_AND, r_ccIR_b6,
r_ccIR_b6, PPCRH_Imm(False,0x40)));
// r_ccIR = r_ccIR_b0 | r_ccIR_b2 | r_ccIR_b6
addInstr(env, PPCInstr_Alu(Palu_OR, r_ccIR,
r_ccIR_b0, PPCRH_Reg(r_ccIR_b2)));
addInstr(env, PPCInstr_Alu(Palu_OR, r_ccIR,
r_ccIR, PPCRH_Reg(r_ccIR_b6)));
return r_ccIR;
}
if ( e->Iex.Binop.op == Iop_F64toI32S ||
e->Iex.Binop.op == Iop_F64toI32U ) {
/* This works in both mode64 and mode32. */
HReg r1 = StackFramePtr(env->mode64);
PPCAMode* zero_r1 = PPCAMode_IR( 0, r1 );
HReg fsrc = iselDblExpr(env, e->Iex.Binop.arg2);
HReg ftmp = newVRegF(env);
HReg idst = newVRegI(env);
/* Set host rounding mode */
set_FPU_rounding_mode( env, e->Iex.Binop.arg1 );
sub_from_sp( env, 16 );
addInstr(env, PPCInstr_FpCftI(False/*F->I*/, True/*int32*/,
e->Iex.Binop.op == Iop_F64toI32S ? True/*syned*/
: False,
True/*flt64*/,
ftmp, fsrc));
addInstr(env, PPCInstr_FpSTFIW(r1, ftmp));
addInstr(env, PPCInstr_Load(4, idst, zero_r1, mode64));
/* in 64-bit mode we need to sign-widen idst. */
if (mode64)
addInstr(env, PPCInstr_Unary(Pun_EXTSW, idst, idst));
add_to_sp( env, 16 );
///* Restore default FPU rounding. */
//set_FPU_rounding_default( env );
return idst;
}
if (e->Iex.Binop.op == Iop_F64toI64S || e->Iex.Binop.op == Iop_F64toI64U ) {
if (mode64) {
HReg r1 = StackFramePtr(env->mode64);
PPCAMode* zero_r1 = PPCAMode_IR( 0, r1 );
HReg fsrc = iselDblExpr(env, e->Iex.Binop.arg2);
HReg idst = newVRegI(env);
HReg ftmp = newVRegF(env);
/* Set host rounding mode */
set_FPU_rounding_mode( env, e->Iex.Binop.arg1 );
sub_from_sp( env, 16 );
addInstr(env, PPCInstr_FpCftI(False/*F->I*/, False/*int64*/,
( e->Iex.Binop.op == Iop_F64toI64S ) ? True
: False,
True, ftmp, fsrc));
addInstr(env, PPCInstr_FpLdSt(False/*store*/, 8, ftmp, zero_r1));
addInstr(env, PPCInstr_Load(8, idst, zero_r1, True/*mode64*/));
add_to_sp( env, 16 );
///* Restore default FPU rounding. */
//set_FPU_rounding_default( env );
return idst;
}
}
if (e->Iex.Binop.op == Iop_D64toI64S ) {
HReg r1 = StackFramePtr(env->mode64);
PPCAMode* zero_r1 = PPCAMode_IR( 0, r1 );
HReg fr_src = iselDfp64Expr(env, e->Iex.Binop.arg2);
HReg idst = newVRegI(env);
HReg ftmp = newVRegF(env);
/* Set host rounding mode */
set_FPU_DFP_rounding_mode( env, e->Iex.Binop.arg1 );
addInstr(env, PPCInstr_Dfp64Unary(Pfp_DCTFIX, ftmp, fr_src));
sub_from_sp( env, 16 );
addInstr(env, PPCInstr_FpLdSt(False/*store*/, 8, ftmp, zero_r1));
addInstr(env, PPCInstr_Load(8, idst, zero_r1, mode64));
add_to_sp( env, 16 );
///* Restore default FPU rounding. */
//set_FPU_rounding_default( env );
return idst;
}
if (e->Iex.Binop.op == Iop_D128toI64S ) {
PPCFpOp fpop = Pfp_DCTFIXQ;
HReg r_srcHi = newVRegF(env);
HReg r_srcLo = newVRegF(env);
HReg idst = newVRegI(env);
HReg ftmp = newVRegF(env);
PPCAMode* zero_r1 = PPCAMode_IR( 0, StackFramePtr(env->mode64) );
set_FPU_DFP_rounding_mode( env, e->Iex.Binop.arg1 );
iselDfp128Expr(&r_srcHi, &r_srcLo, env, e->Iex.Binop.arg2);
addInstr(env, PPCInstr_DfpD128toD64(fpop, ftmp, r_srcHi, r_srcLo));
// put the D64 result into an integer register
sub_from_sp( env, 16 );
addInstr(env, PPCInstr_FpLdSt(False/*store*/, 8, ftmp, zero_r1));
addInstr(env, PPCInstr_Load(8, idst, zero_r1, True/*mode64*/));
add_to_sp( env, 16 );
return idst;
}
break;
}
/* --------- UNARY OP --------- */
case Iex_Unop: {
IROp op_unop = e->Iex.Unop.op;
/* 1Uto8(32to1(expr32)) */
DEFINE_PATTERN(p_32to1_then_1Uto8,
unop(Iop_1Uto8,unop(Iop_32to1,bind(0))));
if (matchIRExpr(&mi,p_32to1_then_1Uto8,e)) {
IRExpr* expr32 = mi.bindee[0];
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, expr32);
addInstr(env, PPCInstr_Alu(Palu_AND, r_dst,
r_src, PPCRH_Imm(False,1)));
return r_dst;
}
/* 16Uto32(LDbe:I16(expr32)) */
{
DECLARE_PATTERN(p_LDbe16_then_16Uto32);
DEFINE_PATTERN(p_LDbe16_then_16Uto32,
unop(Iop_16Uto32,
IRExpr_Load(Iend_BE,Ity_I16,bind(0))) );
if (matchIRExpr(&mi,p_LDbe16_then_16Uto32,e)) {
HReg r_dst = newVRegI(env);
PPCAMode* amode
= iselWordExpr_AMode( env, mi.bindee[0], Ity_I16/*xfer*/ );
addInstr(env, PPCInstr_Load(2,r_dst,amode, mode64));
return r_dst;
}
}
switch (op_unop) {
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 = toUShort(op_unop==Iop_16Uto64 ? 0xFFFF :
op_unop==Iop_16Uto32 ? 0xFFFF : 0xFF);
addInstr(env, PPCInstr_Alu(Palu_AND,r_dst,r_src,
PPCRH_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,
PPCInstr_Shft(Pshft_SHL, False/*64bit shift*/,
r_dst, r_src, PPCRH_Imm(False,32)));
addInstr(env,
PPCInstr_Shft(Pshft_SHR, False/*64bit shift*/,
r_dst, r_dst, PPCRH_Imm(False,32)));
return r_dst;
}
case Iop_8Sto16:
case Iop_8Sto32:
case Iop_16Sto32: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
UShort amt = toUShort(op_unop==Iop_16Sto32 ? 16 : 24);
addInstr(env,
PPCInstr_Shft(Pshft_SHL, True/*32bit shift*/,
r_dst, r_src, PPCRH_Imm(False,amt)));
addInstr(env,
PPCInstr_Shft(Pshft_SAR, True/*32bit shift*/,
r_dst, r_dst, PPCRH_Imm(False,amt)));
return r_dst;
}
case Iop_8Sto64:
case Iop_16Sto64: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
UShort amt = toUShort(op_unop==Iop_8Sto64 ? 56 : 48);
vassert(mode64);
addInstr(env,
PPCInstr_Shft(Pshft_SHL, False/*64bit shift*/,
r_dst, r_src, PPCRH_Imm(False,amt)));
addInstr(env,
PPCInstr_Shft(Pshft_SAR, False/*64bit shift*/,
r_dst, r_dst, PPCRH_Imm(False,amt)));
return r_dst;
}
case Iop_32Sto64: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
vassert(mode64);
/* According to the IBM docs, in 64 bit mode, srawi r,r,0
sign extends the lower 32 bits into the upper 32 bits. */
addInstr(env,
PPCInstr_Shft(Pshft_SAR, True/*32bit shift*/,
r_dst, r_src, PPCRH_Imm(False,0)));
return r_dst;
}
case Iop_Not8:
case Iop_Not16:
case Iop_Not32:
case Iop_Not64: {
if (op_unop == Iop_Not64) vassert(mode64);
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, PPCInstr_Unary(Pun_NOT,r_dst,r_src));
return r_dst;
}
case Iop_64HIto32: {
if (!mode64) {
HReg rHi, rLo;
iselInt64Expr(&rHi,&rLo, env, e->Iex.Unop.arg);
return rHi; /* and abandon rLo .. poor wee thing :-) */
} else {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env,
PPCInstr_Shft(Pshft_SHR, False/*64bit shift*/,
r_dst, r_src, PPCRH_Imm(False,32)));
return r_dst;
}
}
case Iop_64to32: {
if (!mode64) {
HReg rHi, rLo;
iselInt64Expr(&rHi,&rLo, env, e->Iex.Unop.arg);
return rLo; /* similar stupid comment to the above ... */
} else {
/* This is a no-op. */
return iselWordExpr_R(env, e->Iex.Unop.arg);
}
}
case Iop_64to16: {
if (mode64) { /* This is a no-op. */
return iselWordExpr_R(env, e->Iex.Unop.arg);
}
break; /* evidently not used in 32-bit mode */
}
case Iop_16HIto8:
case Iop_32HIto16: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
UShort shift = toUShort(op_unop == Iop_16HIto8 ? 8 : 16);
addInstr(env,
PPCInstr_Shft(Pshft_SHR, True/*32bit shift*/,
r_dst, r_src, PPCRH_Imm(False,shift)));
return r_dst;
}
case Iop_128HIto64:
if (mode64) {
HReg rHi, rLo;
iselInt128Expr(&rHi,&rLo, env, e->Iex.Unop.arg);
return rHi; /* and abandon rLo .. poor wee thing :-) */
}
break;
case Iop_128to64:
if (mode64) {
HReg rHi, rLo;
iselInt128Expr(&rHi,&rLo, env, e->Iex.Unop.arg);
return rLo; /* similar stupid comment to the above ... */
}
break;
case Iop_1Uto64:
case Iop_1Uto32:
case Iop_1Uto8:
if ((op_unop != Iop_1Uto64) || mode64) {
HReg r_dst = newVRegI(env);
PPCCondCode cond = iselCondCode(env, e->Iex.Unop.arg);
addInstr(env, PPCInstr_Set(cond,r_dst));
return r_dst;
}
break;
case Iop_1Sto8:
case Iop_1Sto16:
case Iop_1Sto32: {
/* could do better than this, but for now ... */
HReg r_dst = newVRegI(env);
PPCCondCode cond = iselCondCode(env, e->Iex.Unop.arg);
addInstr(env, PPCInstr_Set(cond,r_dst));
addInstr(env,
PPCInstr_Shft(Pshft_SHL, True/*32bit shift*/,
r_dst, r_dst, PPCRH_Imm(False,31)));
addInstr(env,
PPCInstr_Shft(Pshft_SAR, True/*32bit shift*/,
r_dst, r_dst, PPCRH_Imm(False,31)));
return r_dst;
}
case Iop_1Sto64:
if (mode64) {
/* could do better than this, but for now ... */
HReg r_dst = newVRegI(env);
PPCCondCode cond = iselCondCode(env, e->Iex.Unop.arg);
addInstr(env, PPCInstr_Set(cond,r_dst));
addInstr(env, PPCInstr_Shft(Pshft_SHL, False/*64bit shift*/,
r_dst, r_dst, PPCRH_Imm(False,63)));
addInstr(env, PPCInstr_Shft(Pshft_SAR, False/*64bit shift*/,
r_dst, r_dst, PPCRH_Imm(False,63)));
return r_dst;
}
break;
case Iop_Clz32:
case Iop_Clz64: {
HReg r_src, r_dst;
PPCUnaryOp op_clz = (op_unop == Iop_Clz32) ? Pun_CLZ32 :
Pun_CLZ64;
if (op_unop == Iop_Clz64 && !mode64)
goto irreducible;
/* Count leading zeroes. */
r_dst = newVRegI(env);
r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, PPCInstr_Unary(op_clz,r_dst,r_src));
return r_dst;
}
case Iop_Left8:
case Iop_Left16:
case Iop_Left32:
case Iop_Left64: {
HReg r_src, r_dst;
if (op_unop == Iop_Left64 && !mode64)
goto irreducible;
r_dst = newVRegI(env);
r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, PPCInstr_Unary(Pun_NEG,r_dst,r_src));
addInstr(env, PPCInstr_Alu(Palu_OR, r_dst, r_dst, PPCRH_Reg(r_src)));
return r_dst;
}
case Iop_CmpwNEZ32: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
addInstr(env, PPCInstr_Unary(Pun_NEG,r_dst,r_src));
addInstr(env, PPCInstr_Alu(Palu_OR, r_dst, r_dst, PPCRH_Reg(r_src)));
addInstr(env, PPCInstr_Shft(Pshft_SAR, True/*32bit shift*/,
r_dst, r_dst, PPCRH_Imm(False, 31)));
return r_dst;
}
case Iop_CmpwNEZ64: {
HReg r_dst = newVRegI(env);
HReg r_src = iselWordExpr_R(env, e->Iex.Unop.arg);
if (!mode64) goto irreducible;
addInstr(env, PPCInstr_Unary(Pun_NEG,r_dst,r_src));
addInstr(env, PPCInstr_Alu(Palu_OR, r_dst, r_dst, PPCRH_Reg(r_src)));
addInstr(env, PPCInstr_Shft(Pshft_SAR, False/*64bit shift*/,
r_dst, r_dst, PPCRH_Imm(False, 63)));
return r_dst;
}
case Iop_V128to32: {
HReg r_aligned16;
HReg dst = newVRegI(env);
HReg vec = iselVecExpr(env, e->Iex.Unop.arg);
PPCAMode *am_off0, *am_off12;
sub_from_sp( env, 32 ); // Move SP down 32 bytes
// get a quadword aligned address within our stack space
r_aligned16 = get_sp_aligned16( env );
am_off0 = PPCAMode_IR( 0, r_aligned16 );
am_off12 = PPCAMode_IR( 12,r_aligned16 );
// store vec, load low word to dst
addInstr(env,
PPCInstr_AvLdSt( False/*store*/, 16, vec, am_off0 ));
addInstr(env,
PPCInstr_Load( 4, dst, am_off12, mode64 ));
add_to_sp( env, 32 ); // Reset SP
return dst;
}
case Iop_V128to64:
case Iop_V128HIto64:
if (mode64) {
HReg r_aligned16;
HReg dst = newVRegI(env);
HReg vec = iselVecExpr(env, e->Iex.Unop.arg);
PPCAMode *am_off0, *am_off8;
sub_from_sp( env, 32 ); // Move SP down 32 bytes
// get a quadword aligned address within our stack space
r_aligned16 = get_sp_aligned16( env );
am_off0 = PPCAMode_IR( 0, r_aligned16 );
am_off8 = PPCAMode_IR( 8 ,r_aligned16 );
// store vec, load low word (+8) or high (+0) to dst
addInstr(env,
PPCInstr_AvLdSt( False/*store*/, 16, vec, am_off0 ));
addInstr(env,
PPCInstr_Load(
8, dst,
op_unop == Iop_V128HIto64 ? am_off0 : am_off8,
mode64 ));
add_to_sp( env, 32 ); // Reset SP
return dst;
}
break;
case Iop_16to8:
case Iop_32to8:
case Iop_32to16:
case Iop_64to8:
/* These are no-ops. */
return iselWordExpr_R(env, e->Iex.Unop.arg);
/* ReinterpF64asI64(e) */
/* Given an IEEE754 double, produce an I64 with the same bit
pattern. */
case Iop_ReinterpF64asI64:
if (mode64) {
PPCAMode *am_addr;
HReg fr_src = iselDblExpr(env, e->Iex.Unop.arg);
HReg r_dst = newVRegI(env);
sub_from_sp( env, 16 ); // Move SP down 16 bytes
am_addr = PPCAMode_IR( 0, StackFramePtr(mode64) );
// store as F64
addInstr(env, PPCInstr_FpLdSt( False/*store*/, 8,
fr_src, am_addr ));
// load as Ity_I64
addInstr(env, PPCInstr_Load( 8, r_dst, am_addr, mode64 ));
add_to_sp( env, 16 ); // Reset SP
return r_dst;
}
break;
/* ReinterpF32asI32(e) */
/* Given an IEEE754 float, produce an I32 with the same bit
pattern. */
case Iop_ReinterpF32asI32: {
/* I believe this generates correct code for both 32- and
64-bit hosts. */
PPCAMode *am_addr;
HReg fr_src = iselFltExpr(env, e->Iex.Unop.arg);
HReg r_dst = newVRegI(env);
sub_from_sp( env, 16 ); // Move SP down 16 bytes
am_addr = PPCAMode_IR( 0, StackFramePtr(mode64) );
// store as F32
addInstr(env, PPCInstr_FpLdSt( False/*store*/, 4,
fr_src, am_addr ));
// load as Ity_I32
addInstr(env, PPCInstr_Load( 4, r_dst, am_addr, mode64 ));
add_to_sp( env, 16 ); // Reset SP
return r_dst;
}
break;
case Iop_ReinterpD64asI64:
if (mode64) {
PPCAMode *am_addr;
HReg fr_src = iselDfp64Expr(env, e->Iex.Unop.arg);
HReg r_dst = newVRegI(env);
sub_from_sp( env, 16 ); // Move SP down 16 bytes
am_addr = PPCAMode_IR( 0, StackFramePtr(mode64) );
// store as D64
addInstr(env, PPCInstr_FpLdSt( False/*store*/, 8,
fr_src, am_addr ));
// load as Ity_I64
addInstr(env, PPCInstr_Load( 8, r_dst, am_addr, mode64 ));
add_to_sp( env, 16 ); // Reset SP
return r_dst;
}
break;
case Iop_BCDtoDPB: {
/* the following is only valid in 64 bit mode */
if (!mode64) break;
PPCCondCode cc;
UInt argiregs;
HReg argregs[1];
HReg r_dst = newVRegI(env);
Int argreg;
HWord* fdescr;
argiregs = 0;
argreg = 0;
argregs[0] = hregPPC_GPR3(mode64);
argiregs |= (1 << (argreg+3));
addInstr(env, mk_iMOVds_RR( argregs[argreg++],
iselWordExpr_R(env, e->Iex.Unop.arg) ) );
cc = mk_PPCCondCode( Pct_ALWAYS, Pcf_NONE );
fdescr = (HWord*)h_calc_BCDtoDPB;
addInstr(env, PPCInstr_Call( cc, (Addr64)(fdescr[0]),
argiregs, mk_RetLoc_simple(RLPri_Int)) );
addInstr(env, mk_iMOVds_RR(r_dst, argregs[0]));
return r_dst;
}
case Iop_DPBtoBCD: {
/* the following is only valid in 64 bit mode */
if (!mode64) break;
PPCCondCode cc;
UInt argiregs;
HReg argregs[1];
HReg r_dst = newVRegI(env);
Int argreg;
HWord* fdescr;
argiregs = 0;
argreg = 0;
argregs[0] = hregPPC_GPR3(mode64);
argiregs |= (1 << (argreg+3));
addInstr(env, mk_iMOVds_RR( argregs[argreg++],
iselWordExpr_R(env, e->Iex.Unop.arg) ) );
cc = mk_PPCCondCode( Pct_ALWAYS, Pcf_NONE );
fdescr = (HWord*)h_calc_DPBtoBCD;
addInstr(env, PPCInstr_Call( cc, (Addr64)(fdescr[0]),
argiregs, mk_RetLoc_simple(RLPri_Int) ) );
addInstr(env, mk_iMOVds_RR(r_dst, argregs[0]));
return r_dst;
}
default:
break;
}
switch (e->Iex.Unop.op) {
case Iop_ExtractExpD64: {
HReg fr_dst = newVRegI(env);
HReg fr_src = iselDfp64Expr(env, e->Iex.Unop.arg);
HReg tmp = newVRegF(env);
PPCAMode* zero_r1 = PPCAMode_IR( 0, StackFramePtr(env->mode64) );
addInstr(env, PPCInstr_Dfp64Unary(Pfp_DXEX, tmp, fr_src));
// put the D64 result into a integer register
sub_from_sp( env, 16 );
addInstr(env, PPCInstr_FpLdSt(False/*store*/, 8, tmp, zero_r1));
addInstr(env, PPCInstr_Load(8, fr_dst, zero_r1, env->mode64));
add_to_sp( env, 16 );
return fr_dst;
}
case Iop_ExtractExpD128: {
HReg fr_dst = newVRegI(env);
HReg r_srcHi;
HReg r_srcLo;
HReg tmp = newVRegF(env);
PPCAMode* zero_r1 = PPCAMode_IR( 0, StackFramePtr(env->mode64) );
iselDfp128Expr(&r_srcHi, &r_srcLo, env, e->Iex.Unop.arg);
addInstr(env, PPCInstr_ExtractExpD128(Pfp_DXEXQ, tmp,
r_srcHi, r_srcLo));
sub_from_sp( env, 16 );
addInstr(env, PPCInstr_FpLdSt(False/*store*/, 8, tmp, zero_r1));
addInstr(env, PPCInstr_Load(8, fr_dst, zero_r1, env->mode64));
add_to_sp( env, 16 );
return fr_dst;
}
default:
break;
}
break;
}
/* --------- GET --------- */
case Iex_Get: {
if (ty == Ity_I8 || ty == Ity_I16 ||
ty == Ity_I32 || ((ty == Ity_I64) && mode64)) {
HReg r_dst = newVRegI(env);
PPCAMode* am_addr = PPCAMode_IR( e->Iex.Get.offset,
GuestStatePtr(mode64) );
addInstr(env, PPCInstr_Load( toUChar(sizeofIRType(ty)),
r_dst, am_addr, mode64 ));
return r_dst;
}
break;
}
case Iex_GetI: {
PPCAMode* src_am
= genGuestArrayOffset( env, e->Iex.GetI.descr,
e->Iex.GetI.ix, e->Iex.GetI.bias );
HReg r_dst = newVRegI(env);
if (mode64 && ty == Ity_I64) {
addInstr(env, PPCInstr_Load( toUChar(8),
r_dst, src_am, mode64 ));
return r_dst;
}
if ((!mode64) && ty == Ity_I32) {
addInstr(env, PPCInstr_Load( toUChar(4),
r_dst, src_am, mode64 ));
return r_dst;
}
break;
}
/* --------- CCALL --------- */
case Iex_CCall: {
vassert(ty == e->Iex.CCall.retty); /* well-formedness of IR */
/* be very restrictive for now. Only 32/64-bit ints allowed for
args, and 32 bits or host machine word for return type. */
if (!(ty == Ity_I32 || (mode64 && ty == Ity_I64)))
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);
/* GPR3 now holds the destination address from Pin_Goto */
HReg r_dst = newVRegI(env);
addInstr(env, mk_iMOVds_RR(r_dst, hregPPC_GPR3(mode64)));
return r_dst;
}
/* --------- 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(ppc)");
}
addInstr(env, PPCInstr_LI(r_dst, (ULong)l, mode64));
return r_dst;
}
/* --------- MULTIPLEX --------- */
case Iex_ITE: { // VFD
if ((ty == Ity_I8 || ty == Ity_I16 ||
ty == Ity_I32 || ((ty == Ity_I64) && mode64)) &&
typeOfIRExpr(env->type_env,e->Iex.ITE.cond) == Ity_I1) {
PPCRI* r1 = iselWordExpr_RI(env, e->Iex.ITE.iftrue);
HReg r0 = iselWordExpr_R(env, e->Iex.ITE.iffalse);
HReg r_dst = newVRegI(env);
addInstr(env, mk_iMOVds_RR(r_dst,r0));
PPCCondCode cc = iselCondCode(env, e->Iex.ITE.cond);
addInstr(env, PPCInstr_CMov(cc, r_dst, r1));
return r_dst;
}
break;
}
default:
break;
} /* switch (e->tag) */
/* We get here if no pattern matched. */
irreducible:
ppIRExpr(e);
vpanic("iselIntExpr_R(ppc): cannot reduce tree");
}
/*---------------------------------------------------------*/
/*--- 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 )
{
/* Is u the same as the sign-extend of its lower 16 bits? */
Int i = u & 0xFFFF;
i <<= 16;
i >>= 16;
return toBool(u == (UInt)i);
}
static Bool uLong_fits_in_16_bits ( ULong u )
{
/* Is u the same as the sign-extend of its lower 16 bits? */
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, PPCAMode* am )
{
Bool mode64 = env->mode64;
switch (am->tag) {
case Pam_IR:
/* Using uInt_fits_in_16_bits in 64-bit mode seems a bit bogus,
somehow, but I think it's OK. */
return toBool( hregClass(am->Pam.IR.base) == HRcGPR(mode64) &&
hregIsVirtual(am->Pam.IR.base) &&
uInt_fits_in_16_bits(am->Pam.IR.index) );
case Pam_RR:
return toBool( hregClass(am->Pam.RR.base) == HRcGPR(mode64) &&
hregIsVirtual(am->Pam.RR.base) &&
hregClass(am->Pam.RR.index) == HRcGPR(mode64) &&
hregIsVirtual(am->Pam.RR.index) );
default:
vpanic("sane_AMode: unknown ppc amode tag");
}
}
static
PPCAMode* iselWordExpr_AMode ( ISelEnv* env, IRExpr* e, IRType xferTy )
{
PPCAMode* am = iselWordExpr_AMode_wrk(env, e, xferTy);
vassert(sane_AMode(env, am));
return am;
}
/* DO NOT CALL THIS DIRECTLY ! */
static PPCAMode* iselWordExpr_AMode_wrk ( ISelEnv* env, IRExpr* e, IRType xferTy )
{
IRType ty = typeOfIRExpr(env->type_env,e);
if (env->mode64) {
/* If the data load/store type is I32 or I64, this amode might
be destined for use in ld/ldu/lwa/st/stu. In which case
insist that if it comes out as an _IR, the immediate must
have its bottom two bits be zero. This does assume that for
any other type (I8/I16/I128/F32/F64/V128) the amode will not
be parked in any such instruction. But that seems a
reasonable assumption. */
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 PPCAMode_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 PPCAMode_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 PPCAMode_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 PPCAMode_RR( r_idx, r_base );
}
}
/* Doesn't match anything in particular. Generate it into
a register and use that. */
return PPCAMode_IR( 0, iselWordExpr_R(env,e) );
}
/* --------------------- 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 PPCRH* iselWordExpr_RH ( ISelEnv* env, Bool syned, IRExpr* e )
{
PPCRH* ri = iselWordExpr_RH_wrk(env, syned, e);
/* sanity checks ... */
switch (ri->tag) {
case Prh_Imm:
vassert(ri->Prh.Imm.syned == syned);
if (syned)
vassert(ri->Prh.Imm.imm16 != 0x8000);
return ri;
case Prh_Reg:
vassert(hregClass(ri->Prh.Reg.reg) == HRcGPR(env->mode64));
vassert(hregIsVirtual(ri->Prh.Reg.reg));
return ri;
default:
vpanic("iselIntExpr_RH: unknown ppc RH tag");
}
}
/* DO NOT CALL THIS DIRECTLY ! */
static PPCRH* 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(ppch)");
}
l = (Long)u;
/* Now figure out if it's representable. */
if (!syned && u <= 65535) {
return PPCRH_Imm(False/*unsigned*/, toUShort(u & 0xFFFF));
}
if (syned && l >=