blob: adabf64123fb1ef48943e39915fa3ac8e4bfaf23 [file] [log] [blame]
/*--------------------------------------------------------------------*/
/*--- begin guest_ppc_toIR.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.
*/
/* TODO 18/Nov/05:
Spot rld... cases which are simply left/right shifts and emit
Shl64/Shr64 accordingly.
Altivec
- datastream insns
- lvxl,stvxl: load/store with 'least recently used' hint
- vexptefp, vlogefp
LIMITATIONS:
Various, including:
- Some invalid forms of lswi and lswx are accepted when they should
not be.
- Floating Point:
- All exceptions disabled in FPSCR
- condition codes not set in FPSCR
- Altivec floating point:
- vmaddfp, vnmsubfp
Because we're using Java/IEEE mode (FPSCR[NJ]), rather than the
system default of Non-Java mode, we get some small errors
(lowest bit only).
This is because Non-Java mode brutally hacks denormalised results
to zero, whereas we keep maximum accuracy. However, using
Non-Java mode would give us more inaccuracy, as our intermediate
results would then be zeroed, too.
- AbiHints for the stack red zone are only emitted for
unconditional calls and returns (bl, blr). They should also be
emitted for conditional calls and returns, but we don't have a
way to express that right now. Ah well.
- Uses of Iop_{Add,Sub,Mul}32Fx4: the backend (host_ppc_isel.c)
ignores the rounding mode, and generates code that assumes
round-to-nearest. This means V will compute incorrect results
for uses of these IROps when the rounding mode (first) arg is
not mkU32(Irrm_NEAREST).
*/
/* "Special" instructions.
This instruction decoder can decode four special instructions
which mean nothing natively (are no-ops as far as regs/mem are
concerned) but have meaning for supporting Valgrind. A special
instruction is flagged by a 16-byte preamble:
32-bit mode: 5400183E 5400683E 5400E83E 5400983E
(rlwinm 0,0,3,0,31; rlwinm 0,0,13,0,31;
rlwinm 0,0,29,0,31; rlwinm 0,0,19,0,31)
64-bit mode: 78001800 78006800 7800E802 78009802
(rotldi 0,0,3; rotldi 0,0,13;
rotldi 0,0,61; rotldi 0,0,51)
Following that, one of the following 3 are allowed
(standard interpretation in parentheses):
7C210B78 (or 1,1,1) %R3 = client_request ( %R4 )
7C421378 (or 2,2,2) %R3 = guest_NRADDR
7C631B78 (or 3,3,3) branch-and-link-to-noredir %R11 Big endian
7C631B78 (or 3,3,3) branch-and-link-to-noredir %R12 Little endian
7C842378 (or 4,4,4) %R3 = guest_NRADDR_GPR2
7CA52B78 (or 5,5,5) IR injection
Any other bytes following the 16-byte preamble are illegal and
constitute a failure in instruction decoding. This all assumes
that the preamble will never occur except in specific code
fragments designed for Valgrind to catch.
*/
/* Little Endian notes */
/*
* Vector operations in little Endian mode behave in non-obvious ways at times.
* Below is an attempt at explaining this.
*
* LE/BE vector example
* With a vector of unsigned ints declared as follows:
* vector unsigned int vec_inA =
{ 0x11111111, 0x22222222, 0x33333333, 0x44444444 };
* The '0x11111111' word is word zero in both LE and BE format. But the
* loaded vector register will have word zero on the far left in BE mode and
* on the far right in LE mode. The lvx and stvx instructions work naturally
* for whatever endianness is in effect. For example, in LE mode, the stvx
* stores word zero (far right word) of the vector at the lowest memory
* address of the EA; in BE mode, stvx still stores word zero at the lowest
* memory address, but with word zero interpreted as the one at the far left
* of the register.
*
* The lxvd2x and stxvd2x instructions are not so well suited for LE mode.
* When the compiler generates an lxvd2x instruction to load the
* above-declared vector of unsigned integers, it loads the vector as two
* double words, but they are in BE word-wise format. To put the vector in
* the right order for LE, the compiler also generates an xxswapd after the
* load, which puts it in proper LE format. Similarly, the stxvd2x
* instruction has a BE bias, storing the vector in BE word-wise format. But
* the compiler also generates an xxswapd prior to the store, thus ensuring
* the vector is stored in memory in the correct LE order.
*
* Vector-flavored Iops, such Iop_V128Hito64, reference the hi and lo parts
* of a double words and words within a vector. Because of the reverse order
* of numbering for LE as described above, the high part refers to word 1 in
* LE format. When input data is saved to a guest state vector register
* (e.g., via Iop_64HLtoV128), it is first saved to memory and then the
* register is loaded via PPCInstr_AvLdSt, which does an lvx instruction.
* The saving of the data to memory must be done in proper LE order. For the
* inverse operation of extracting data from a vector register (e.g.,
* Iop_V128Hito64), the register is first saved (by PPCInstr_AvLdSt resulting
* in stvx), and then integer registers are loaded from the memory location
* from where the vector register was saved. Again, this must be done in
* proper LE order. So for these various vector Iops, we have LE-specific
* code in host_ppc_isel.c
*
* Another unique behavior of vectors in LE mode is with the vector scalar
* (VSX) operations that operate on "double word 0" of the source register,
* storing the result in "double word 0" of the output vector register. For
* these operations, "double word 0" is interpreted as "high half of the
* register" (i.e, the part on the left side).
*
*/
/* Translates PPC32/64 code to IR. */
/* References
#define PPC32
"PowerPC Microprocessor Family:
The Programming Environments Manual for 32-Bit Microprocessors"
02/21/2000
http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF778525699600719DF2
#define PPC64
"PowerPC Microprocessor Family:
Programming Environments Manual for 64-Bit Microprocessors"
06/10/2003
http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/F7E732FF811F783187256FDD004D3797
#define AV
"PowerPC Microprocessor Family:
AltiVec(TM) Technology Programming Environments Manual"
07/10/2003
http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/FBFA164F824370F987256D6A006F424D
*/
#include "libvex_basictypes.h"
#include "libvex_ir.h"
#include "libvex.h"
#include "libvex_emnote.h"
#include "libvex_guest_ppc32.h"
#include "libvex_guest_ppc64.h"
#include "main_util.h"
#include "main_globals.h"
#include "guest_generic_bb_to_IR.h"
#include "guest_ppc_defs.h"
/*------------------------------------------------------------*/
/*--- Globals ---*/
/*------------------------------------------------------------*/
/* These are set at the start of the translation of an insn, right
down in disInstr_PPC, so that we don't have to pass them around
endlessly. They are all constant during the translation of any
given insn. */
/* We need to know this to do sub-register accesses correctly. */
static VexEndness host_endness;
/* Pointer to the guest code area. */
static UChar* guest_code;
/* The guest address corresponding to guest_code[0]. */
static Addr64 guest_CIA_bbstart;
/* The guest address for the instruction currently being
translated. */
static Addr64 guest_CIA_curr_instr;
/* The IRSB* into which we're generating code. */
static IRSB* irsb;
/* Is our guest binary 32 or 64bit? Set at each call to
disInstr_PPC below. */
static Bool mode64 = False;
// Given a pointer to a function as obtained by "& functionname" in C,
// produce a pointer to the actual entry point for the function. For
// most platforms it's the identity function. Unfortunately, on
// ppc64-linux it isn't (sigh) and ditto for ppc32-aix5 and
// ppc64-aix5.
static void* fnptr_to_fnentry( VexAbiInfo* vbi, void* f )
{
if (vbi->host_ppc_calls_use_fndescrs) {
/* f is a pointer to a 3-word function descriptor, of which the
first word is the entry address. */
/* note, this is correct even with cross-jitting, since this is
purely a host issue, not a guest one. */
HWord* fdescr = (HWord*)f;
return (void*)(fdescr[0]);
} else {
/* Simple; "& f" points directly at the code for f. */
return f;
}
}
#define SIGN_BIT 0x8000000000000000ULL
#define SIGN_MASK 0x7fffffffffffffffULL
#define SIGN_BIT32 0x80000000
#define SIGN_MASK32 0x7fffffff
/*------------------------------------------------------------*/
/*--- Debugging output ---*/
/*------------------------------------------------------------*/
#define DIP(format, args...) \
if (vex_traceflags & VEX_TRACE_FE) \
vex_printf(format, ## args)
#define DIS(buf, format, args...) \
if (vex_traceflags & VEX_TRACE_FE) \
vex_sprintf(buf, format, ## args)
/*------------------------------------------------------------*/
/*--- Offsets of various parts of the ppc32/64 guest state ---*/
/*------------------------------------------------------------*/
#define offsetofPPCGuestState(_x) \
(mode64 ? offsetof(VexGuestPPC64State, _x) : \
offsetof(VexGuestPPC32State, _x))
#define OFFB_CIA offsetofPPCGuestState(guest_CIA)
#define OFFB_IP_AT_SYSCALL offsetofPPCGuestState(guest_IP_AT_SYSCALL)
#define OFFB_SPRG3_RO offsetofPPCGuestState(guest_SPRG3_RO)
#define OFFB_LR offsetofPPCGuestState(guest_LR)
#define OFFB_CTR offsetofPPCGuestState(guest_CTR)
#define OFFB_XER_SO offsetofPPCGuestState(guest_XER_SO)
#define OFFB_XER_OV offsetofPPCGuestState(guest_XER_OV)
#define OFFB_XER_CA offsetofPPCGuestState(guest_XER_CA)
#define OFFB_XER_BC offsetofPPCGuestState(guest_XER_BC)
#define OFFB_FPROUND offsetofPPCGuestState(guest_FPROUND)
#define OFFB_DFPROUND offsetofPPCGuestState(guest_DFPROUND)
#define OFFB_VRSAVE offsetofPPCGuestState(guest_VRSAVE)
#define OFFB_VSCR offsetofPPCGuestState(guest_VSCR)
#define OFFB_EMNOTE offsetofPPCGuestState(guest_EMNOTE)
#define OFFB_CMSTART offsetofPPCGuestState(guest_CMSTART)
#define OFFB_CMLEN offsetofPPCGuestState(guest_CMLEN)
#define OFFB_NRADDR offsetofPPCGuestState(guest_NRADDR)
#define OFFB_NRADDR_GPR2 offsetofPPCGuestState(guest_NRADDR_GPR2)
#define OFFB_TFHAR offsetofPPCGuestState(guest_TFHAR)
#define OFFB_TEXASR offsetofPPCGuestState(guest_TEXASR)
#define OFFB_TFIAR offsetofPPCGuestState(guest_TFIAR)
/*------------------------------------------------------------*/
/*--- Extract instruction fields --- */
/*------------------------------------------------------------*/
/* Extract field from insn, given idx (zero = lsb) and field length */
#define IFIELD( insn, idx, len ) ((insn >> idx) & ((1<<len)-1))
/* Extract primary opcode, instr[31:26] */
static UChar ifieldOPC( UInt instr ) {
return toUChar( IFIELD( instr, 26, 6 ) );
}
/* Extract 10-bit secondary opcode, instr[10:1] */
static UInt ifieldOPClo10 ( UInt instr) {
return IFIELD( instr, 1, 10 );
}
/* Extract 9-bit secondary opcode, instr[9:1] */
static UInt ifieldOPClo9 ( UInt instr) {
return IFIELD( instr, 1, 9 );
}
/* Extract 8-bit secondary opcode, instr[8:1] */
static UInt ifieldOPClo8 ( UInt instr) {
return IFIELD( instr, 1, 8 );
}
/* Extract 5-bit secondary opcode, instr[5:1] */
static UInt ifieldOPClo5 ( UInt instr) {
return IFIELD( instr, 1, 5 );
}
/* Extract RD (destination register) field, instr[25:21] */
static UChar ifieldRegDS( UInt instr ) {
return toUChar( IFIELD( instr, 21, 5 ) );
}
/* Extract XT (destination register) field, instr[0,25:21] */
static UChar ifieldRegXT ( UInt instr )
{
UChar upper_bit = toUChar (IFIELD (instr, 0, 1));
UChar lower_bits = toUChar (IFIELD (instr, 21, 5));
return (upper_bit << 5) | lower_bits;
}
/* Extract XS (store source register) field, instr[0,25:21] */
static inline UChar ifieldRegXS ( UInt instr )
{
return ifieldRegXT ( instr );
}
/* Extract RA (1st source register) field, instr[20:16] */
static UChar ifieldRegA ( UInt instr ) {
return toUChar( IFIELD( instr, 16, 5 ) );
}
/* Extract XA (1st source register) field, instr[2,20:16] */
static UChar ifieldRegXA ( UInt instr )
{
UChar upper_bit = toUChar (IFIELD (instr, 2, 1));
UChar lower_bits = toUChar (IFIELD (instr, 16, 5));
return (upper_bit << 5) | lower_bits;
}
/* Extract RB (2nd source register) field, instr[15:11] */
static UChar ifieldRegB ( UInt instr ) {
return toUChar( IFIELD( instr, 11, 5 ) );
}
/* Extract XB (2nd source register) field, instr[1,15:11] */
static UChar ifieldRegXB ( UInt instr )
{
UChar upper_bit = toUChar (IFIELD (instr, 1, 1));
UChar lower_bits = toUChar (IFIELD (instr, 11, 5));
return (upper_bit << 5) | lower_bits;
}
/* Extract RC (3rd source register) field, instr[10:6] */
static UChar ifieldRegC ( UInt instr ) {
return toUChar( IFIELD( instr, 6, 5 ) );
}
/* Extract XC (3rd source register) field, instr[3,10:6] */
static UChar ifieldRegXC ( UInt instr )
{
UChar upper_bit = toUChar (IFIELD (instr, 3, 1));
UChar lower_bits = toUChar (IFIELD (instr, 6, 5));
return (upper_bit << 5) | lower_bits;
}
/* Extract bit 10, instr[10] */
static UChar ifieldBIT10 ( UInt instr ) {
return toUChar( IFIELD( instr, 10, 1 ) );
}
/* Extract 2nd lowest bit, instr[1] */
static UChar ifieldBIT1 ( UInt instr ) {
return toUChar( IFIELD( instr, 1, 1 ) );
}
/* Extract lowest bit, instr[0] */
static UChar ifieldBIT0 ( UInt instr ) {
return toUChar( instr & 0x1 );
}
/* Extract unsigned bottom half, instr[15:0] */
static UInt ifieldUIMM16 ( UInt instr ) {
return instr & 0xFFFF;
}
/* Extract unsigned bottom 26 bits, instr[25:0] */
static UInt ifieldUIMM26 ( UInt instr ) {
return instr & 0x3FFFFFF;
}
/* Extract DM field, instr[9:8] */
static UChar ifieldDM ( UInt instr ) {
return toUChar( IFIELD( instr, 8, 2 ) );
}
/* Extract SHW field, instr[9:8] */
static inline UChar ifieldSHW ( UInt instr )
{
return ifieldDM ( instr );
}
/*------------------------------------------------------------*/
/*--- Guest-state identifiers ---*/
/*------------------------------------------------------------*/
typedef enum {
PPC_GST_CIA, // Current Instruction Address
PPC_GST_LR, // Link Register
PPC_GST_CTR, // Count Register
PPC_GST_XER, // Overflow, carry flags, byte count
PPC_GST_CR, // Condition Register
PPC_GST_FPSCR, // Floating Point Status/Control Register
PPC_GST_VRSAVE, // Vector Save/Restore Register
PPC_GST_VSCR, // Vector Status and Control Register
PPC_GST_EMWARN, // Emulation warnings
PPC_GST_CMSTART,// For icbi: start of area to invalidate
PPC_GST_CMLEN, // For icbi: length of area to invalidate
PPC_GST_IP_AT_SYSCALL, // the CIA of the most recently executed SC insn
PPC_GST_SPRG3_RO, // SPRG3
PPC_GST_TFHAR, // Transactional Failure Handler Address Register
PPC_GST_TFIAR, // Transactional Failure Instruction Address Register
PPC_GST_TEXASR, // Transactional EXception And Summary Register
PPC_GST_MAX
} PPC_GST;
#define MASK_FPSCR_RN 0x3ULL // Binary floating point rounding mode
#define MASK_FPSCR_DRN 0x700000000ULL // Decimal floating point rounding mode
#define MASK_VSCR_VALID 0x00010001
/*------------------------------------------------------------*/
/*--- FP Helpers ---*/
/*------------------------------------------------------------*/
/* Produce the 32-bit pattern corresponding to the supplied
float. */
static UInt float_to_bits ( Float f )
{
union { UInt i; Float f; } u;
vassert(4 == sizeof(UInt));
vassert(4 == sizeof(Float));
vassert(4 == sizeof(u));
u.f = f;
return u.i;
}
/*------------------------------------------------------------*/
/*--- Misc Helpers ---*/
/*------------------------------------------------------------*/
/* Generate mask with 1's from 'begin' through 'end',
wrapping if begin > end.
begin->end works from right to left, 0=lsb
*/
static UInt MASK32( UInt begin, UInt end )
{
UInt m1, m2, mask;
vassert(begin < 32);
vassert(end < 32);
m1 = ((UInt)(-1)) << begin;
m2 = ((UInt)(-1)) << end << 1;
mask = m1 ^ m2;
if (begin > end) mask = ~mask; // wrap mask
return mask;
}
static ULong MASK64( UInt begin, UInt end )
{
ULong m1, m2, mask;
vassert(begin < 64);
vassert(end < 64);
m1 = ((ULong)(-1)) << begin;
m2 = ((ULong)(-1)) << end << 1;
mask = m1 ^ m2;
if (begin > end) mask = ~mask; // wrap mask
return mask;
}
static Addr64 nextInsnAddr( void )
{
return guest_CIA_curr_instr + 4;
}
/*------------------------------------------------------------*/
/*--- Helper bits and pieces for deconstructing the ---*/
/*--- ppc32/64 insn stream. ---*/
/*------------------------------------------------------------*/
/* Add a statement to the list held by "irsb". */
static void stmt ( IRStmt* st )
{
addStmtToIRSB( irsb, st );
}
/* Generate a new temporary of the given type. */
static IRTemp newTemp ( IRType ty )
{
vassert(isPlausibleIRType(ty));
return newIRTemp( irsb->tyenv, ty );
}
/* Various simple conversions */
static UChar extend_s_5to8 ( UChar x )
{
return toUChar((((Int)x) << 27) >> 27);
}
static UInt extend_s_8to32( UChar x )
{
return (UInt)((((Int)x) << 24) >> 24);
}
static UInt extend_s_16to32 ( UInt x )
{
return (UInt)((((Int)x) << 16) >> 16);
}
static ULong extend_s_16to64 ( UInt x )
{
return (ULong)((((Long)x) << 48) >> 48);
}
static ULong extend_s_26to64 ( UInt x )
{
return (ULong)((((Long)x) << 38) >> 38);
}
static ULong extend_s_32to64 ( UInt x )
{
return (ULong)((((Long)x) << 32) >> 32);
}
/* Do a proper-endian load of a 32-bit word, regardless of the endianness
of the underlying host. */
static UInt getUIntPPCendianly ( UChar* p )
{
UInt w = 0;
if (host_endness == VexEndnessBE) {
w = (w << 8) | p[0];
w = (w << 8) | p[1];
w = (w << 8) | p[2];
w = (w << 8) | p[3];
} else {
w = (w << 8) | p[3];
w = (w << 8) | p[2];
w = (w << 8) | p[1];
w = (w << 8) | p[0];
}
return w;
}
/*------------------------------------------------------------*/
/*--- Helpers for constructing IR. ---*/
/*------------------------------------------------------------*/
static void assign ( IRTemp dst, IRExpr* e )
{
stmt( IRStmt_WrTmp(dst, e) );
}
/* This generates a normal (non store-conditional) store. */
static void store ( IRExpr* addr, IRExpr* data )
{
IRType tyA = typeOfIRExpr(irsb->tyenv, addr);
vassert(tyA == Ity_I32 || tyA == Ity_I64);
if (host_endness == VexEndnessBE)
stmt( IRStmt_Store(Iend_BE, addr, data) );
else
stmt( IRStmt_Store(Iend_LE, addr, data) );
}
static IRExpr* unop ( IROp op, IRExpr* a )
{
return IRExpr_Unop(op, a);
}
static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
{
return IRExpr_Binop(op, a1, a2);
}
static IRExpr* triop ( IROp op, IRExpr* a1, IRExpr* a2, IRExpr* a3 )
{
return IRExpr_Triop(op, a1, a2, a3);
}
static IRExpr* qop ( IROp op, IRExpr* a1, IRExpr* a2,
IRExpr* a3, IRExpr* a4 )
{
return IRExpr_Qop(op, a1, a2, a3, a4);
}
static IRExpr* mkexpr ( IRTemp tmp )
{
return IRExpr_RdTmp(tmp);
}
static IRExpr* mkU8 ( UChar i )
{
return IRExpr_Const(IRConst_U8(i));
}
static IRExpr* mkU16 ( UInt i )
{
return IRExpr_Const(IRConst_U16(i));
}
static IRExpr* mkU32 ( UInt i )
{
return IRExpr_Const(IRConst_U32(i));
}
static IRExpr* mkU64 ( ULong i )
{
return IRExpr_Const(IRConst_U64(i));
}
static IRExpr* mkV128 ( UShort i )
{
vassert(i == 0 || i == 0xffff);
return IRExpr_Const(IRConst_V128(i));
}
/* This generates a normal (non load-linked) load. */
static IRExpr* load ( IRType ty, IRExpr* addr )
{
if (host_endness == VexEndnessBE)
return IRExpr_Load(Iend_BE, ty, addr);
else
return IRExpr_Load(Iend_LE, ty, addr);
}
static IRStmt* stmt_load ( IRTemp result,
IRExpr* addr, IRExpr* storedata )
{
if (host_endness == VexEndnessBE)
return IRStmt_LLSC(Iend_BE, result, addr, storedata);
else
return IRStmt_LLSC(Iend_LE, result, addr, storedata);
}
static IRExpr* mkOR1 ( IRExpr* arg1, IRExpr* arg2 )
{
vassert(typeOfIRExpr(irsb->tyenv, arg1) == Ity_I1);
vassert(typeOfIRExpr(irsb->tyenv, arg2) == Ity_I1);
return unop(Iop_32to1, binop(Iop_Or32, unop(Iop_1Uto32, arg1),
unop(Iop_1Uto32, arg2)));
}
static IRExpr* mkAND1 ( IRExpr* arg1, IRExpr* arg2 )
{
vassert(typeOfIRExpr(irsb->tyenv, arg1) == Ity_I1);
vassert(typeOfIRExpr(irsb->tyenv, arg2) == Ity_I1);
return unop(Iop_32to1, binop(Iop_And32, unop(Iop_1Uto32, arg1),
unop(Iop_1Uto32, arg2)));
}
/* expand V128_8Ux16 to 2x V128_16Ux8's */
static void expand8Ux16( IRExpr* vIn,
/*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
{
IRTemp ones8x16 = newTemp(Ity_V128);
vassert(typeOfIRExpr(irsb->tyenv, vIn) == Ity_V128);
vassert(vEvn && *vEvn == IRTemp_INVALID);
vassert(vOdd && *vOdd == IRTemp_INVALID);
*vEvn = newTemp(Ity_V128);
*vOdd = newTemp(Ity_V128);
assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
assign( *vOdd, binop(Iop_MullEven8Ux16, mkexpr(ones8x16), vIn) );
assign( *vEvn, binop(Iop_MullEven8Ux16, mkexpr(ones8x16),
binop(Iop_ShrV128, vIn, mkU8(8))) );
}
/* expand V128_8Sx16 to 2x V128_16Sx8's */
static void expand8Sx16( IRExpr* vIn,
/*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
{
IRTemp ones8x16 = newTemp(Ity_V128);
vassert(typeOfIRExpr(irsb->tyenv, vIn) == Ity_V128);
vassert(vEvn && *vEvn == IRTemp_INVALID);
vassert(vOdd && *vOdd == IRTemp_INVALID);
*vEvn = newTemp(Ity_V128);
*vOdd = newTemp(Ity_V128);
assign( ones8x16, unop(Iop_Dup8x16, mkU8(0x1)) );
assign( *vOdd, binop(Iop_MullEven8Sx16, mkexpr(ones8x16), vIn) );
assign( *vEvn, binop(Iop_MullEven8Sx16, mkexpr(ones8x16),
binop(Iop_ShrV128, vIn, mkU8(8))) );
}
/* expand V128_16Uto8 to 2x V128_32Ux4's */
static void expand16Ux8( IRExpr* vIn,
/*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
{
IRTemp ones16x8 = newTemp(Ity_V128);
vassert(typeOfIRExpr(irsb->tyenv, vIn) == Ity_V128);
vassert(vEvn && *vEvn == IRTemp_INVALID);
vassert(vOdd && *vOdd == IRTemp_INVALID);
*vEvn = newTemp(Ity_V128);
*vOdd = newTemp(Ity_V128);
assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
assign( *vOdd, binop(Iop_MullEven16Ux8, mkexpr(ones16x8), vIn) );
assign( *vEvn, binop(Iop_MullEven16Ux8, mkexpr(ones16x8),
binop(Iop_ShrV128, vIn, mkU8(16))) );
}
/* expand V128_16Sto8 to 2x V128_32Sx4's */
static void expand16Sx8( IRExpr* vIn,
/*OUTs*/ IRTemp* vEvn, IRTemp* vOdd )
{
IRTemp ones16x8 = newTemp(Ity_V128);
vassert(typeOfIRExpr(irsb->tyenv, vIn) == Ity_V128);
vassert(vEvn && *vEvn == IRTemp_INVALID);
vassert(vOdd && *vOdd == IRTemp_INVALID);
*vEvn = newTemp(Ity_V128);
*vOdd = newTemp(Ity_V128);
assign( ones16x8, unop(Iop_Dup16x8, mkU16(0x1)) );
assign( *vOdd, binop(Iop_MullEven16Sx8, mkexpr(ones16x8), vIn) );
assign( *vEvn, binop(Iop_MullEven16Sx8, mkexpr(ones16x8),
binop(Iop_ShrV128, vIn, mkU8(16))) );
}
/* break V128 to 4xF64's*/
static void breakV128to4xF64( IRExpr* t128,
/*OUTs*/
IRTemp* t3, IRTemp* t2,
IRTemp* t1, IRTemp* t0 )
{
IRTemp hi64 = newTemp(Ity_I64);
IRTemp lo64 = newTemp(Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t128) == Ity_V128);
vassert(t0 && *t0 == IRTemp_INVALID);
vassert(t1 && *t1 == IRTemp_INVALID);
vassert(t2 && *t2 == IRTemp_INVALID);
vassert(t3 && *t3 == IRTemp_INVALID);
*t0 = newTemp(Ity_F64);
*t1 = newTemp(Ity_F64);
*t2 = newTemp(Ity_F64);
*t3 = newTemp(Ity_F64);
assign( hi64, unop(Iop_V128HIto64, t128) );
assign( lo64, unop(Iop_V128to64, t128) );
assign( *t3,
unop( Iop_F32toF64,
unop( Iop_ReinterpI32asF32,
unop( Iop_64HIto32, mkexpr( hi64 ) ) ) ) );
assign( *t2,
unop( Iop_F32toF64,
unop( Iop_ReinterpI32asF32, unop( Iop_64to32, mkexpr( hi64 ) ) ) ) );
assign( *t1,
unop( Iop_F32toF64,
unop( Iop_ReinterpI32asF32,
unop( Iop_64HIto32, mkexpr( lo64 ) ) ) ) );
assign( *t0,
unop( Iop_F32toF64,
unop( Iop_ReinterpI32asF32, unop( Iop_64to32, mkexpr( lo64 ) ) ) ) );
}
/* break V128 to 4xI32's, then sign-extend to I64's */
static void breakV128to4x64S( IRExpr* t128,
/*OUTs*/
IRTemp* t3, IRTemp* t2,
IRTemp* t1, IRTemp* t0 )
{
IRTemp hi64 = newTemp(Ity_I64);
IRTemp lo64 = newTemp(Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t128) == Ity_V128);
vassert(t0 && *t0 == IRTemp_INVALID);
vassert(t1 && *t1 == IRTemp_INVALID);
vassert(t2 && *t2 == IRTemp_INVALID);
vassert(t3 && *t3 == IRTemp_INVALID);
*t0 = newTemp(Ity_I64);
*t1 = newTemp(Ity_I64);
*t2 = newTemp(Ity_I64);
*t3 = newTemp(Ity_I64);
assign( hi64, unop(Iop_V128HIto64, t128) );
assign( lo64, unop(Iop_V128to64, t128) );
assign( *t3, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(hi64))) );
assign( *t2, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(hi64))) );
assign( *t1, unop(Iop_32Sto64, unop(Iop_64HIto32, mkexpr(lo64))) );
assign( *t0, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(lo64))) );
}
/* break V128 to 4xI32's, then zero-extend to I64's */
static void breakV128to4x64U ( IRExpr* t128,
/*OUTs*/
IRTemp* t3, IRTemp* t2,
IRTemp* t1, IRTemp* t0 )
{
IRTemp hi64 = newTemp(Ity_I64);
IRTemp lo64 = newTemp(Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t128) == Ity_V128);
vassert(t0 && *t0 == IRTemp_INVALID);
vassert(t1 && *t1 == IRTemp_INVALID);
vassert(t2 && *t2 == IRTemp_INVALID);
vassert(t3 && *t3 == IRTemp_INVALID);
*t0 = newTemp(Ity_I64);
*t1 = newTemp(Ity_I64);
*t2 = newTemp(Ity_I64);
*t3 = newTemp(Ity_I64);
assign( hi64, unop(Iop_V128HIto64, t128) );
assign( lo64, unop(Iop_V128to64, t128) );
assign( *t3, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(hi64))) );
assign( *t2, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(hi64))) );
assign( *t1, unop(Iop_32Uto64, unop(Iop_64HIto32, mkexpr(lo64))) );
assign( *t0, unop(Iop_32Uto64, unop(Iop_64to32, mkexpr(lo64))) );
}
static void breakV128to4x32( IRExpr* t128,
/*OUTs*/
IRTemp* t3, IRTemp* t2,
IRTemp* t1, IRTemp* t0 )
{
IRTemp hi64 = newTemp(Ity_I64);
IRTemp lo64 = newTemp(Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t128) == Ity_V128);
vassert(t0 && *t0 == IRTemp_INVALID);
vassert(t1 && *t1 == IRTemp_INVALID);
vassert(t2 && *t2 == IRTemp_INVALID);
vassert(t3 && *t3 == IRTemp_INVALID);
*t0 = newTemp(Ity_I32);
*t1 = newTemp(Ity_I32);
*t2 = newTemp(Ity_I32);
*t3 = newTemp(Ity_I32);
assign( hi64, unop(Iop_V128HIto64, t128) );
assign( lo64, unop(Iop_V128to64, t128) );
assign( *t3, unop(Iop_64HIto32, mkexpr(hi64)) );
assign( *t2, unop(Iop_64to32, mkexpr(hi64)) );
assign( *t1, unop(Iop_64HIto32, mkexpr(lo64)) );
assign( *t0, unop(Iop_64to32, mkexpr(lo64)) );
}
static IRExpr* mkV128from32( IRTemp t3, IRTemp t2,
IRTemp t1, IRTemp t0 )
{
return
binop( Iop_64HLtoV128,
binop(Iop_32HLto64, mkexpr(t3), mkexpr(t2)),
binop(Iop_32HLto64, mkexpr(t1), mkexpr(t0))
);
}
/* Signed saturating narrow 64S to 32 */
static IRExpr* mkQNarrow64Sto32 ( IRExpr* t64 )
{
IRTemp hi32 = newTemp(Ity_I32);
IRTemp lo32 = newTemp(Ity_I32);
vassert(typeOfIRExpr(irsb->tyenv, t64) == Ity_I64);
assign( hi32, unop(Iop_64HIto32, t64));
assign( lo32, unop(Iop_64to32, t64));
return IRExpr_ITE(
/* if (hi32 == (lo32 >>s 31)) */
binop(Iop_CmpEQ32, mkexpr(hi32),
binop( Iop_Sar32, mkexpr(lo32), mkU8(31))),
/* then: within signed-32 range: lo half good enough */
mkexpr(lo32),
/* else: sign dep saturate: 1->0x80000000, 0->0x7FFFFFFF */
binop(Iop_Add32, mkU32(0x7FFFFFFF),
binop(Iop_Shr32, mkexpr(hi32), mkU8(31))));
}
/* Unsigned saturating narrow 64S to 32 */
static IRExpr* mkQNarrow64Uto32 ( IRExpr* t64 )
{
IRTemp hi32 = newTemp(Ity_I32);
IRTemp lo32 = newTemp(Ity_I32);
vassert(typeOfIRExpr(irsb->tyenv, t64) == Ity_I64);
assign( hi32, unop(Iop_64HIto32, t64));
assign( lo32, unop(Iop_64to32, t64));
return IRExpr_ITE(
/* if (top 32 bits of t64 are 0) */
binop(Iop_CmpEQ32, mkexpr(hi32), mkU32(0)),
/* then: within unsigned-32 range: lo half good enough */
mkexpr(lo32),
/* else: positive saturate -> 0xFFFFFFFF */
mkU32(0xFFFFFFFF));
}
/* Signed saturate narrow 64->32, combining to V128 */
static IRExpr* mkV128from4x64S ( IRExpr* t3, IRExpr* t2,
IRExpr* t1, IRExpr* t0 )
{
vassert(typeOfIRExpr(irsb->tyenv, t3) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t2) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t1) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t0) == Ity_I64);
return binop(Iop_64HLtoV128,
binop(Iop_32HLto64,
mkQNarrow64Sto32( t3 ),
mkQNarrow64Sto32( t2 )),
binop(Iop_32HLto64,
mkQNarrow64Sto32( t1 ),
mkQNarrow64Sto32( t0 )));
}
/* Unsigned saturate narrow 64->32, combining to V128 */
static IRExpr* mkV128from4x64U ( IRExpr* t3, IRExpr* t2,
IRExpr* t1, IRExpr* t0 )
{
vassert(typeOfIRExpr(irsb->tyenv, t3) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t2) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t1) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv, t0) == Ity_I64);
return binop(Iop_64HLtoV128,
binop(Iop_32HLto64,
mkQNarrow64Uto32( t3 ),
mkQNarrow64Uto32( t2 )),
binop(Iop_32HLto64,
mkQNarrow64Uto32( t1 ),
mkQNarrow64Uto32( t0 )));
}
/* Simulate irops Iop_MullOdd*, since we don't have them */
#define MK_Iop_MullOdd8Ux16( expr_vA, expr_vB ) \
binop(Iop_MullEven8Ux16, \
binop(Iop_ShrV128, expr_vA, mkU8(8)), \
binop(Iop_ShrV128, expr_vB, mkU8(8)))
#define MK_Iop_MullOdd8Sx16( expr_vA, expr_vB ) \
binop(Iop_MullEven8Sx16, \
binop(Iop_ShrV128, expr_vA, mkU8(8)), \
binop(Iop_ShrV128, expr_vB, mkU8(8)))
#define MK_Iop_MullOdd16Ux8( expr_vA, expr_vB ) \
binop(Iop_MullEven16Ux8, \
binop(Iop_ShrV128, expr_vA, mkU8(16)), \
binop(Iop_ShrV128, expr_vB, mkU8(16)))
#define MK_Iop_MullOdd32Ux4( expr_vA, expr_vB ) \
binop(Iop_MullEven32Ux4, \
binop(Iop_ShrV128, expr_vA, mkU8(32)), \
binop(Iop_ShrV128, expr_vB, mkU8(32)))
#define MK_Iop_MullOdd16Sx8( expr_vA, expr_vB ) \
binop(Iop_MullEven16Sx8, \
binop(Iop_ShrV128, expr_vA, mkU8(16)), \
binop(Iop_ShrV128, expr_vB, mkU8(16)))
#define MK_Iop_MullOdd32Sx4( expr_vA, expr_vB ) \
binop(Iop_MullEven32Sx4, \
binop(Iop_ShrV128, expr_vA, mkU8(32)), \
binop(Iop_ShrV128, expr_vB, mkU8(32)))
static IRExpr* /* :: Ity_I64 */ mk64lo32Sto64 ( IRExpr* src )
{
vassert(typeOfIRExpr(irsb->tyenv, src) == Ity_I64);
return unop(Iop_32Sto64, unop(Iop_64to32, src));
}
static IRExpr* /* :: Ity_I64 */ mk64lo32Uto64 ( IRExpr* src )
{
vassert(typeOfIRExpr(irsb->tyenv, src) == Ity_I64);
return unop(Iop_32Uto64, unop(Iop_64to32, src));
}
static IROp mkSzOp ( IRType ty, IROp op8 )
{
Int adj;
vassert(ty == Ity_I8 || ty == Ity_I16 ||
ty == Ity_I32 || ty == Ity_I64);
vassert(op8 == Iop_Add8 || op8 == Iop_Sub8 || op8 == Iop_Mul8 ||
op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8 ||
op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8 ||
op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8 ||
op8 == Iop_Not8 );
adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : (ty==Ity_I32 ? 2 : 3));
return adj + op8;
}
/* Make sure we get valid 32 and 64bit addresses */
static Addr64 mkSzAddr ( IRType ty, Addr64 addr )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ( ty == Ity_I64 ?
(Addr64)addr :
(Addr64)extend_s_32to64( toUInt(addr) ) );
}
/* sz, ULong -> IRExpr */
static IRExpr* mkSzImm ( IRType ty, ULong imm64 )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ty == Ity_I64 ? mkU64(imm64) : mkU32((UInt)imm64);
}
/* sz, ULong -> IRConst */
static IRConst* mkSzConst ( IRType ty, ULong imm64 )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ( ty == Ity_I64 ?
IRConst_U64(imm64) :
IRConst_U32((UInt)imm64) );
}
/* Sign extend imm16 -> IRExpr* */
static IRExpr* mkSzExtendS16 ( IRType ty, UInt imm16 )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ( ty == Ity_I64 ?
mkU64(extend_s_16to64(imm16)) :
mkU32(extend_s_16to32(imm16)) );
}
/* Sign extend imm32 -> IRExpr* */
static IRExpr* mkSzExtendS32 ( IRType ty, UInt imm32 )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ( ty == Ity_I64 ?
mkU64(extend_s_32to64(imm32)) :
mkU32(imm32) );
}
/* IR narrows I32/I64 -> I8/I16/I32 */
static IRExpr* mkNarrowTo8 ( IRType ty, IRExpr* src )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ty == Ity_I64 ? unop(Iop_64to8, src) : unop(Iop_32to8, src);
}
static IRExpr* mkNarrowTo16 ( IRType ty, IRExpr* src )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ty == Ity_I64 ? unop(Iop_64to16, src) : unop(Iop_32to16, src);
}
static IRExpr* mkNarrowTo32 ( IRType ty, IRExpr* src )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
return ty == Ity_I64 ? unop(Iop_64to32, src) : src;
}
/* Signed/Unsigned IR widens I8/I16/I32 -> I32/I64 */
static IRExpr* mkWidenFrom8 ( IRType ty, IRExpr* src, Bool sined )
{
IROp op;
vassert(ty == Ity_I32 || ty == Ity_I64);
if (sined) op = (ty==Ity_I32) ? Iop_8Sto32 : Iop_8Sto64;
else op = (ty==Ity_I32) ? Iop_8Uto32 : Iop_8Uto64;
return unop(op, src);
}
static IRExpr* mkWidenFrom16 ( IRType ty, IRExpr* src, Bool sined )
{
IROp op;
vassert(ty == Ity_I32 || ty == Ity_I64);
if (sined) op = (ty==Ity_I32) ? Iop_16Sto32 : Iop_16Sto64;
else op = (ty==Ity_I32) ? Iop_16Uto32 : Iop_16Uto64;
return unop(op, src);
}
static IRExpr* mkWidenFrom32 ( IRType ty, IRExpr* src, Bool sined )
{
vassert(ty == Ity_I32 || ty == Ity_I64);
if (ty == Ity_I32)
return src;
return (sined) ? unop(Iop_32Sto64, src) : unop(Iop_32Uto64, src);
}
static Int integerGuestRegOffset ( UInt archreg )
{
vassert(archreg < 32);
// jrs: probably not necessary; only matters if we reference sub-parts
// of the ppc registers, but that isn't the case
// later: this might affect Altivec though?
switch (archreg) {
case 0: return offsetofPPCGuestState(guest_GPR0);
case 1: return offsetofPPCGuestState(guest_GPR1);
case 2: return offsetofPPCGuestState(guest_GPR2);
case 3: return offsetofPPCGuestState(guest_GPR3);
case 4: return offsetofPPCGuestState(guest_GPR4);
case 5: return offsetofPPCGuestState(guest_GPR5);
case 6: return offsetofPPCGuestState(guest_GPR6);
case 7: return offsetofPPCGuestState(guest_GPR7);
case 8: return offsetofPPCGuestState(guest_GPR8);
case 9: return offsetofPPCGuestState(guest_GPR9);
case 10: return offsetofPPCGuestState(guest_GPR10);
case 11: return offsetofPPCGuestState(guest_GPR11);
case 12: return offsetofPPCGuestState(guest_GPR12);
case 13: return offsetofPPCGuestState(guest_GPR13);
case 14: return offsetofPPCGuestState(guest_GPR14);
case 15: return offsetofPPCGuestState(guest_GPR15);
case 16: return offsetofPPCGuestState(guest_GPR16);
case 17: return offsetofPPCGuestState(guest_GPR17);
case 18: return offsetofPPCGuestState(guest_GPR18);
case 19: return offsetofPPCGuestState(guest_GPR19);
case 20: return offsetofPPCGuestState(guest_GPR20);
case 21: return offsetofPPCGuestState(guest_GPR21);
case 22: return offsetofPPCGuestState(guest_GPR22);
case 23: return offsetofPPCGuestState(guest_GPR23);
case 24: return offsetofPPCGuestState(guest_GPR24);
case 25: return offsetofPPCGuestState(guest_GPR25);
case 26: return offsetofPPCGuestState(guest_GPR26);
case 27: return offsetofPPCGuestState(guest_GPR27);
case 28: return offsetofPPCGuestState(guest_GPR28);
case 29: return offsetofPPCGuestState(guest_GPR29);
case 30: return offsetofPPCGuestState(guest_GPR30);
case 31: return offsetofPPCGuestState(guest_GPR31);
default: break;
}
vpanic("integerGuestRegOffset(ppc,be)"); /*notreached*/
}
static IRExpr* getIReg ( UInt archreg )
{
IRType ty = mode64 ? Ity_I64 : Ity_I32;
vassert(archreg < 32);
return IRExpr_Get( integerGuestRegOffset(archreg), ty );
}
/* Ditto, but write to a reg instead. */
static void putIReg ( UInt archreg, IRExpr* e )
{
IRType ty = mode64 ? Ity_I64 : Ity_I32;
vassert(archreg < 32);
vassert(typeOfIRExpr(irsb->tyenv, e) == ty );
stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
}
/* Floating point egisters are mapped to VSX registers[0..31]. */
static Int floatGuestRegOffset ( UInt archreg )
{
vassert(archreg < 32);
if (host_endness == VexEndnessLE) {
switch (archreg) {
case 0: return offsetofPPCGuestState(guest_VSR0 + 8);
case 1: return offsetofPPCGuestState(guest_VSR1 + 8);
case 2: return offsetofPPCGuestState(guest_VSR2 + 8);
case 3: return offsetofPPCGuestState(guest_VSR3 + 8);
case 4: return offsetofPPCGuestState(guest_VSR4 + 8);
case 5: return offsetofPPCGuestState(guest_VSR5 + 8);
case 6: return offsetofPPCGuestState(guest_VSR6 + 8);
case 7: return offsetofPPCGuestState(guest_VSR7 + 8);
case 8: return offsetofPPCGuestState(guest_VSR8 + 8);
case 9: return offsetofPPCGuestState(guest_VSR9 + 8);
case 10: return offsetofPPCGuestState(guest_VSR10 + 8);
case 11: return offsetofPPCGuestState(guest_VSR11 + 8);
case 12: return offsetofPPCGuestState(guest_VSR12 + 8);
case 13: return offsetofPPCGuestState(guest_VSR13 + 8);
case 14: return offsetofPPCGuestState(guest_VSR14 + 8);
case 15: return offsetofPPCGuestState(guest_VSR15 + 8);
case 16: return offsetofPPCGuestState(guest_VSR16 + 8);
case 17: return offsetofPPCGuestState(guest_VSR17 + 8);
case 18: return offsetofPPCGuestState(guest_VSR18 + 8);
case 19: return offsetofPPCGuestState(guest_VSR19 + 8);
case 20: return offsetofPPCGuestState(guest_VSR20 + 8);
case 21: return offsetofPPCGuestState(guest_VSR21 + 8);
case 22: return offsetofPPCGuestState(guest_VSR22 + 8);
case 23: return offsetofPPCGuestState(guest_VSR23 + 8);
case 24: return offsetofPPCGuestState(guest_VSR24 + 8);
case 25: return offsetofPPCGuestState(guest_VSR25 + 8);
case 26: return offsetofPPCGuestState(guest_VSR26 + 8);
case 27: return offsetofPPCGuestState(guest_VSR27 + 8);
case 28: return offsetofPPCGuestState(guest_VSR28 + 8);
case 29: return offsetofPPCGuestState(guest_VSR29 + 8);
case 30: return offsetofPPCGuestState(guest_VSR30 + 8);
case 31: return offsetofPPCGuestState(guest_VSR31 + 8);
default: break;
}
} else {
switch (archreg) {
case 0: return offsetofPPCGuestState(guest_VSR0);
case 1: return offsetofPPCGuestState(guest_VSR1);
case 2: return offsetofPPCGuestState(guest_VSR2);
case 3: return offsetofPPCGuestState(guest_VSR3);
case 4: return offsetofPPCGuestState(guest_VSR4);
case 5: return offsetofPPCGuestState(guest_VSR5);
case 6: return offsetofPPCGuestState(guest_VSR6);
case 7: return offsetofPPCGuestState(guest_VSR7);
case 8: return offsetofPPCGuestState(guest_VSR8);
case 9: return offsetofPPCGuestState(guest_VSR9);
case 10: return offsetofPPCGuestState(guest_VSR10);
case 11: return offsetofPPCGuestState(guest_VSR11);
case 12: return offsetofPPCGuestState(guest_VSR12);
case 13: return offsetofPPCGuestState(guest_VSR13);
case 14: return offsetofPPCGuestState(guest_VSR14);
case 15: return offsetofPPCGuestState(guest_VSR15);
case 16: return offsetofPPCGuestState(guest_VSR16);
case 17: return offsetofPPCGuestState(guest_VSR17);
case 18: return offsetofPPCGuestState(guest_VSR18);
case 19: return offsetofPPCGuestState(guest_VSR19);
case 20: return offsetofPPCGuestState(guest_VSR20);
case 21: return offsetofPPCGuestState(guest_VSR21);
case 22: return offsetofPPCGuestState(guest_VSR22);
case 23: return offsetofPPCGuestState(guest_VSR23);
case 24: return offsetofPPCGuestState(guest_VSR24);
case 25: return offsetofPPCGuestState(guest_VSR25);
case 26: return offsetofPPCGuestState(guest_VSR26);
case 27: return offsetofPPCGuestState(guest_VSR27);
case 28: return offsetofPPCGuestState(guest_VSR28);
case 29: return offsetofPPCGuestState(guest_VSR29);
case 30: return offsetofPPCGuestState(guest_VSR30);
case 31: return offsetofPPCGuestState(guest_VSR31);
default: break;
}
}
vpanic("floatGuestRegOffset(ppc)"); /*notreached*/
}
static IRExpr* getFReg ( UInt archreg )
{
vassert(archreg < 32);
return IRExpr_Get( floatGuestRegOffset(archreg), Ity_F64 );
}
/* Ditto, but write to a reg instead. */
static void putFReg ( UInt archreg, IRExpr* e )
{
vassert(archreg < 32);
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_F64);
stmt( IRStmt_Put(floatGuestRegOffset(archreg), e) );
}
/* get Decimal float value. Note, they share floating point register file. */
static IRExpr* getDReg(UInt archreg) {
IRExpr *e;
vassert( archreg < 32 );
e = IRExpr_Get( floatGuestRegOffset( archreg ), Ity_D64 );
return e;
}
static IRExpr* getDReg32(UInt archreg) {
IRExpr *e;
vassert( archreg < 32 );
e = IRExpr_Get( floatGuestRegOffset( archreg ), Ity_D32 );
return e;
}
/* Read a floating point register pair and combine their contents into a
128-bit value */
static IRExpr *getDReg_pair(UInt archreg) {
IRExpr *high = getDReg( archreg );
IRExpr *low = getDReg( archreg + 1 );
return binop( Iop_D64HLtoD128, high, low );
}
/* Ditto, but write to a reg instead. */
static void putDReg32(UInt archreg, IRExpr* e) {
vassert( archreg < 32 );
vassert( typeOfIRExpr(irsb->tyenv, e) == Ity_D32 );
stmt( IRStmt_Put( floatGuestRegOffset( archreg ), e ) );
}
static void putDReg(UInt archreg, IRExpr* e) {
vassert( archreg < 32 );
vassert( typeOfIRExpr(irsb->tyenv, e) == Ity_D64 );
stmt( IRStmt_Put( floatGuestRegOffset( archreg ), e ) );
}
/* Write a 128-bit floating point value into a register pair. */
static void putDReg_pair(UInt archreg, IRExpr *e) {
IRTemp low = newTemp( Ity_D64 );
IRTemp high = newTemp( Ity_D64 );
vassert( archreg < 32 );
vassert( typeOfIRExpr(irsb->tyenv, e) == Ity_D128 );
assign( low, unop( Iop_D128LOtoD64, e ) );
assign( high, unop( Iop_D128HItoD64, e ) );
stmt( IRStmt_Put( floatGuestRegOffset( archreg ), mkexpr( high ) ) );
stmt( IRStmt_Put( floatGuestRegOffset( archreg + 1 ), mkexpr( low ) ) );
}
static Int vsxGuestRegOffset ( UInt archreg )
{
vassert(archreg < 64);
switch (archreg) {
case 0: return offsetofPPCGuestState(guest_VSR0);
case 1: return offsetofPPCGuestState(guest_VSR1);
case 2: return offsetofPPCGuestState(guest_VSR2);
case 3: return offsetofPPCGuestState(guest_VSR3);
case 4: return offsetofPPCGuestState(guest_VSR4);
case 5: return offsetofPPCGuestState(guest_VSR5);
case 6: return offsetofPPCGuestState(guest_VSR6);
case 7: return offsetofPPCGuestState(guest_VSR7);
case 8: return offsetofPPCGuestState(guest_VSR8);
case 9: return offsetofPPCGuestState(guest_VSR9);
case 10: return offsetofPPCGuestState(guest_VSR10);
case 11: return offsetofPPCGuestState(guest_VSR11);
case 12: return offsetofPPCGuestState(guest_VSR12);
case 13: return offsetofPPCGuestState(guest_VSR13);
case 14: return offsetofPPCGuestState(guest_VSR14);
case 15: return offsetofPPCGuestState(guest_VSR15);
case 16: return offsetofPPCGuestState(guest_VSR16);
case 17: return offsetofPPCGuestState(guest_VSR17);
case 18: return offsetofPPCGuestState(guest_VSR18);
case 19: return offsetofPPCGuestState(guest_VSR19);
case 20: return offsetofPPCGuestState(guest_VSR20);
case 21: return offsetofPPCGuestState(guest_VSR21);
case 22: return offsetofPPCGuestState(guest_VSR22);
case 23: return offsetofPPCGuestState(guest_VSR23);
case 24: return offsetofPPCGuestState(guest_VSR24);
case 25: return offsetofPPCGuestState(guest_VSR25);
case 26: return offsetofPPCGuestState(guest_VSR26);
case 27: return offsetofPPCGuestState(guest_VSR27);
case 28: return offsetofPPCGuestState(guest_VSR28);
case 29: return offsetofPPCGuestState(guest_VSR29);
case 30: return offsetofPPCGuestState(guest_VSR30);
case 31: return offsetofPPCGuestState(guest_VSR31);
case 32: return offsetofPPCGuestState(guest_VSR32);
case 33: return offsetofPPCGuestState(guest_VSR33);
case 34: return offsetofPPCGuestState(guest_VSR34);
case 35: return offsetofPPCGuestState(guest_VSR35);
case 36: return offsetofPPCGuestState(guest_VSR36);
case 37: return offsetofPPCGuestState(guest_VSR37);
case 38: return offsetofPPCGuestState(guest_VSR38);
case 39: return offsetofPPCGuestState(guest_VSR39);
case 40: return offsetofPPCGuestState(guest_VSR40);
case 41: return offsetofPPCGuestState(guest_VSR41);
case 42: return offsetofPPCGuestState(guest_VSR42);
case 43: return offsetofPPCGuestState(guest_VSR43);
case 44: return offsetofPPCGuestState(guest_VSR44);
case 45: return offsetofPPCGuestState(guest_VSR45);
case 46: return offsetofPPCGuestState(guest_VSR46);
case 47: return offsetofPPCGuestState(guest_VSR47);
case 48: return offsetofPPCGuestState(guest_VSR48);
case 49: return offsetofPPCGuestState(guest_VSR49);
case 50: return offsetofPPCGuestState(guest_VSR50);
case 51: return offsetofPPCGuestState(guest_VSR51);
case 52: return offsetofPPCGuestState(guest_VSR52);
case 53: return offsetofPPCGuestState(guest_VSR53);
case 54: return offsetofPPCGuestState(guest_VSR54);
case 55: return offsetofPPCGuestState(guest_VSR55);
case 56: return offsetofPPCGuestState(guest_VSR56);
case 57: return offsetofPPCGuestState(guest_VSR57);
case 58: return offsetofPPCGuestState(guest_VSR58);
case 59: return offsetofPPCGuestState(guest_VSR59);
case 60: return offsetofPPCGuestState(guest_VSR60);
case 61: return offsetofPPCGuestState(guest_VSR61);
case 62: return offsetofPPCGuestState(guest_VSR62);
case 63: return offsetofPPCGuestState(guest_VSR63);
default: break;
}
vpanic("vsxGuestRegOffset(ppc)"); /*notreached*/
}
/* Vector registers are mapped to VSX registers[32..63]. */
static Int vectorGuestRegOffset ( UInt archreg )
{
vassert(archreg < 32);
switch (archreg) {
case 0: return offsetofPPCGuestState(guest_VSR32);
case 1: return offsetofPPCGuestState(guest_VSR33);
case 2: return offsetofPPCGuestState(guest_VSR34);
case 3: return offsetofPPCGuestState(guest_VSR35);
case 4: return offsetofPPCGuestState(guest_VSR36);
case 5: return offsetofPPCGuestState(guest_VSR37);
case 6: return offsetofPPCGuestState(guest_VSR38);
case 7: return offsetofPPCGuestState(guest_VSR39);
case 8: return offsetofPPCGuestState(guest_VSR40);
case 9: return offsetofPPCGuestState(guest_VSR41);
case 10: return offsetofPPCGuestState(guest_VSR42);
case 11: return offsetofPPCGuestState(guest_VSR43);
case 12: return offsetofPPCGuestState(guest_VSR44);
case 13: return offsetofPPCGuestState(guest_VSR45);
case 14: return offsetofPPCGuestState(guest_VSR46);
case 15: return offsetofPPCGuestState(guest_VSR47);
case 16: return offsetofPPCGuestState(guest_VSR48);
case 17: return offsetofPPCGuestState(guest_VSR49);
case 18: return offsetofPPCGuestState(guest_VSR50);
case 19: return offsetofPPCGuestState(guest_VSR51);
case 20: return offsetofPPCGuestState(guest_VSR52);
case 21: return offsetofPPCGuestState(guest_VSR53);
case 22: return offsetofPPCGuestState(guest_VSR54);
case 23: return offsetofPPCGuestState(guest_VSR55);
case 24: return offsetofPPCGuestState(guest_VSR56);
case 25: return offsetofPPCGuestState(guest_VSR57);
case 26: return offsetofPPCGuestState(guest_VSR58);
case 27: return offsetofPPCGuestState(guest_VSR59);
case 28: return offsetofPPCGuestState(guest_VSR60);
case 29: return offsetofPPCGuestState(guest_VSR61);
case 30: return offsetofPPCGuestState(guest_VSR62);
case 31: return offsetofPPCGuestState(guest_VSR63);
default: break;
}
vpanic("vextorGuestRegOffset(ppc)"); /*notreached*/
}
static IRExpr* getVReg ( UInt archreg )
{
vassert(archreg < 32);
return IRExpr_Get( vectorGuestRegOffset(archreg), Ity_V128 );
}
/* Ditto, but write to a reg instead. */
static void putVReg ( UInt archreg, IRExpr* e )
{
vassert(archreg < 32);
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_V128);
stmt( IRStmt_Put(vectorGuestRegOffset(archreg), e) );
}
/* Get contents of VSX guest register */
static IRExpr* getVSReg ( UInt archreg )
{
vassert(archreg < 64);
return IRExpr_Get( vsxGuestRegOffset(archreg), Ity_V128 );
}
/* Ditto, but write to a VSX reg instead. */
static void putVSReg ( UInt archreg, IRExpr* e )
{
vassert(archreg < 64);
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_V128);
stmt( IRStmt_Put(vsxGuestRegOffset(archreg), e) );
}
static Int guestCR321offset ( UInt cr )
{
switch (cr) {
case 0: return offsetofPPCGuestState(guest_CR0_321 );
case 1: return offsetofPPCGuestState(guest_CR1_321 );
case 2: return offsetofPPCGuestState(guest_CR2_321 );
case 3: return offsetofPPCGuestState(guest_CR3_321 );
case 4: return offsetofPPCGuestState(guest_CR4_321 );
case 5: return offsetofPPCGuestState(guest_CR5_321 );
case 6: return offsetofPPCGuestState(guest_CR6_321 );
case 7: return offsetofPPCGuestState(guest_CR7_321 );
default: vpanic("guestCR321offset(ppc)");
}
}
static Int guestCR0offset ( UInt cr )
{
switch (cr) {
case 0: return offsetofPPCGuestState(guest_CR0_0 );
case 1: return offsetofPPCGuestState(guest_CR1_0 );
case 2: return offsetofPPCGuestState(guest_CR2_0 );
case 3: return offsetofPPCGuestState(guest_CR3_0 );
case 4: return offsetofPPCGuestState(guest_CR4_0 );
case 5: return offsetofPPCGuestState(guest_CR5_0 );
case 6: return offsetofPPCGuestState(guest_CR6_0 );
case 7: return offsetofPPCGuestState(guest_CR7_0 );
default: vpanic("guestCR3offset(ppc)");
}
}
typedef enum {
_placeholder0,
_placeholder1,
_placeholder2,
BYTE,
HWORD,
WORD,
DWORD
} _popcount_data_type;
/* Generate an IR sequence to do a popcount operation on the supplied
IRTemp, and return a new IRTemp holding the result. 'ty' may be
Ity_I32 or Ity_I64 only. */
static IRTemp gen_POPCOUNT ( IRType ty, IRTemp src, _popcount_data_type data_type )
{
/* Do count across 2^data_type bits,
byte: data_type = 3
half word: data_type = 4
word: data_type = 5
double word: data_type = 6 (not supported for 32-bit type)
*/
Int shift[6];
_popcount_data_type idx, i;
IRTemp mask[6];
IRTemp old = IRTemp_INVALID;
IRTemp nyu = IRTemp_INVALID;
vassert(ty == Ity_I64 || ty == Ity_I32);
if (ty == Ity_I32) {
for (idx = 0; idx < WORD; idx++) {
mask[idx] = newTemp(ty);
shift[idx] = 1 << idx;
}
assign(mask[0], mkU32(0x55555555));
assign(mask[1], mkU32(0x33333333));
assign(mask[2], mkU32(0x0F0F0F0F));
assign(mask[3], mkU32(0x00FF00FF));
assign(mask[4], mkU32(0x0000FFFF));
old = src;
for (i = 0; i < data_type; i++) {
nyu = newTemp(ty);
assign(nyu,
binop(Iop_Add32,
binop(Iop_And32,
mkexpr(old),
mkexpr(mask[i])),
binop(Iop_And32,
binop(Iop_Shr32, mkexpr(old), mkU8(shift[i])),
mkexpr(mask[i]))));
old = nyu;
}
return nyu;
}
// else, ty == Ity_I64
vassert(mode64);
for (i = 0; i < DWORD; i++) {
mask[i] = newTemp( Ity_I64 );
shift[i] = 1 << i;
}
assign( mask[0], mkU64( 0x5555555555555555ULL ) );
assign( mask[1], mkU64( 0x3333333333333333ULL ) );
assign( mask[2], mkU64( 0x0F0F0F0F0F0F0F0FULL ) );
assign( mask[3], mkU64( 0x00FF00FF00FF00FFULL ) );
assign( mask[4], mkU64( 0x0000FFFF0000FFFFULL ) );
assign( mask[5], mkU64( 0x00000000FFFFFFFFULL ) );
old = src;
for (i = 0; i < data_type; i++) {
nyu = newTemp( Ity_I64 );
assign( nyu,
binop( Iop_Add64,
binop( Iop_And64, mkexpr( old ), mkexpr( mask[i] ) ),
binop( Iop_And64,
binop( Iop_Shr64, mkexpr( old ), mkU8( shift[i] ) ),
mkexpr( mask[i] ) ) ) );
old = nyu;
}
return nyu;
}
/* Special purpose population count function for
* vpopcntd in 32-bit mode.
*/
static IRTemp gen_vpopcntd_mode32 ( IRTemp src1, IRTemp src2 )
{
Int i, shift[6];
IRTemp mask[6];
IRTemp old = IRTemp_INVALID;
IRTemp nyu1 = IRTemp_INVALID;
IRTemp nyu2 = IRTemp_INVALID;
IRTemp retval = newTemp(Ity_I64);
vassert(!mode64);
for (i = 0; i < WORD; i++) {
mask[i] = newTemp(Ity_I32);
shift[i] = 1 << i;
}
assign(mask[0], mkU32(0x55555555));
assign(mask[1], mkU32(0x33333333));
assign(mask[2], mkU32(0x0F0F0F0F));
assign(mask[3], mkU32(0x00FF00FF));
assign(mask[4], mkU32(0x0000FFFF));
old = src1;
for (i = 0; i < WORD; i++) {
nyu1 = newTemp(Ity_I32);
assign(nyu1,
binop(Iop_Add32,
binop(Iop_And32,
mkexpr(old),
mkexpr(mask[i])),
binop(Iop_And32,
binop(Iop_Shr32, mkexpr(old), mkU8(shift[i])),
mkexpr(mask[i]))));
old = nyu1;
}
old = src2;
for (i = 0; i < WORD; i++) {
nyu2 = newTemp(Ity_I32);
assign(nyu2,
binop(Iop_Add32,
binop(Iop_And32,
mkexpr(old),
mkexpr(mask[i])),
binop(Iop_And32,
binop(Iop_Shr32, mkexpr(old), mkU8(shift[i])),
mkexpr(mask[i]))));
old = nyu2;
}
assign(retval, unop(Iop_32Uto64, binop(Iop_Add32, mkexpr(nyu1), mkexpr(nyu2))));
return retval;
}
// ROTL(src32/64, rot_amt5/6)
static IRExpr* /* :: Ity_I32/64 */ ROTL ( IRExpr* src,
IRExpr* rot_amt )
{
IRExpr *mask, *rot;
vassert(typeOfIRExpr(irsb->tyenv,rot_amt) == Ity_I8);
if (typeOfIRExpr(irsb->tyenv,src) == Ity_I64) {
// rot = (src << rot_amt) | (src >> (64-rot_amt))
mask = binop(Iop_And8, rot_amt, mkU8(63));
rot = binop(Iop_Or64,
binop(Iop_Shl64, src, mask),
binop(Iop_Shr64, src, binop(Iop_Sub8, mkU8(64), mask)));
} else {
// rot = (src << rot_amt) | (src >> (32-rot_amt))
mask = binop(Iop_And8, rot_amt, mkU8(31));
rot = binop(Iop_Or32,
binop(Iop_Shl32, src, mask),
binop(Iop_Shr32, src, binop(Iop_Sub8, mkU8(32), mask)));
}
/* Note: the ITE not merely an optimisation; it's needed
because otherwise the Shr is a shift by the word size when
mask denotes zero. For rotates by immediates, a lot of
this junk gets folded out. */
return IRExpr_ITE( binop(Iop_CmpNE8, mask, mkU8(0)),
/* non-zero rotate */ rot,
/* zero rotate */ src);
}
/* Standard effective address calc: (rA + rB) */
static IRExpr* ea_rA_idxd ( UInt rA, UInt rB )
{
IRType ty = mode64 ? Ity_I64 : Ity_I32;
vassert(rA < 32);
vassert(rB < 32);
return binop(mkSzOp(ty, Iop_Add8), getIReg(rA), getIReg(rB));
}
/* Standard effective address calc: (rA + simm) */
static IRExpr* ea_rA_simm ( UInt rA, UInt simm16 )
{
IRType ty = mode64 ? Ity_I64 : Ity_I32;
vassert(rA < 32);
return binop(mkSzOp(ty, Iop_Add8), getIReg(rA),
mkSzExtendS16(ty, simm16));
}
/* Standard effective address calc: (rA|0) */
static IRExpr* ea_rAor0 ( UInt rA )
{
IRType ty = mode64 ? Ity_I64 : Ity_I32;
vassert(rA < 32);
if (rA == 0) {
return mkSzImm(ty, 0);
} else {
return getIReg(rA);
}
}
/* Standard effective address calc: (rA|0) + rB */
static IRExpr* ea_rAor0_idxd ( UInt rA, UInt rB )
{
vassert(rA < 32);
vassert(rB < 32);
return (rA == 0) ? getIReg(rB) : ea_rA_idxd( rA, rB );
}
/* Standard effective address calc: (rA|0) + simm16 */
static IRExpr* ea_rAor0_simm ( UInt rA, UInt simm16 )
{
IRType ty = mode64 ? Ity_I64 : Ity_I32;
vassert(rA < 32);
if (rA == 0) {
return mkSzExtendS16(ty, simm16);
} else {
return ea_rA_simm( rA, simm16 );
}
}
/* Align effective address */
static IRExpr* addr_align( IRExpr* addr, UChar align )
{
IRType ty = mode64 ? Ity_I64 : Ity_I32;
Long mask;
switch (align) {
case 1: return addr; // byte aligned
case 2: mask = ((Long)-1) << 1; break; // half-word aligned
case 4: mask = ((Long)-1) << 2; break; // word aligned
case 16: mask = ((Long)-1) << 4; break; // quad-word aligned
default:
vex_printf("addr_align: align = %u\n", align);
vpanic("addr_align(ppc)");
}
vassert(typeOfIRExpr(irsb->tyenv,addr) == ty);
return binop( mkSzOp(ty, Iop_And8), addr, mkSzImm(ty, mask) );
}
/* Exit the trace if ADDR (intended to be a guest memory address) is
not ALIGN-aligned, generating a request for a SIGBUS followed by a
restart of the current insn. */
static void gen_SIGBUS_if_misaligned ( IRTemp addr, UChar align )
{
vassert(align == 4 || align == 8 || align == 16);
if (mode64) {
vassert(typeOfIRTemp(irsb->tyenv, addr) == Ity_I64);
stmt(
IRStmt_Exit(
binop(Iop_CmpNE64,
binop(Iop_And64, mkexpr(addr), mkU64(align-1)),
mkU64(0)),
Ijk_SigBUS,
IRConst_U64( guest_CIA_curr_instr ), OFFB_CIA
)
);
} else {
vassert(typeOfIRTemp(irsb->tyenv, addr) == Ity_I32);
stmt(
IRStmt_Exit(
binop(Iop_CmpNE32,
binop(Iop_And32, mkexpr(addr), mkU32(align-1)),
mkU32(0)),
Ijk_SigBUS,
IRConst_U32( guest_CIA_curr_instr ), OFFB_CIA
)
);
}
}
/* Generate AbiHints which mark points at which the ELF or PowerOpen
ABIs say that the stack red zone (viz, -N(r1) .. -1(r1), for some
N) becomes undefined. That is at function calls and returns. ELF
ppc32 doesn't have this "feature" (how fortunate for it). nia is
the address of the next instruction to be executed.
*/
static void make_redzone_AbiHint ( VexAbiInfo* vbi,
IRTemp nia, const HChar* who )
{
Int szB = vbi->guest_stack_redzone_size;
if (0) vex_printf("AbiHint: %s\n", who);
vassert(szB >= 0);
if (szB > 0) {
if (mode64) {
vassert(typeOfIRTemp(irsb->tyenv, nia) == Ity_I64);
stmt( IRStmt_AbiHint(
binop(Iop_Sub64, getIReg(1), mkU64(szB)),
szB,
mkexpr(nia)
));
} else {
vassert(typeOfIRTemp(irsb->tyenv, nia) == Ity_I32);
stmt( IRStmt_AbiHint(
binop(Iop_Sub32, getIReg(1), mkU32(szB)),
szB,
mkexpr(nia)
));
}
}
}
/*------------------------------------------------------------*/
/*--- Helpers for condition codes. ---*/
/*------------------------------------------------------------*/
/* Condition register layout.
In the hardware, CR is laid out like this. The leftmost end is the
most significant bit in the register; however the IBM documentation
numbers the bits backwards for some reason.
CR0 CR1 .......... CR6 CR7
0 .. 3 ....................... 28 .. 31 (IBM bit numbering)
31 28 3 0 (normal bit numbering)
Each CR field is 4 bits: [<,>,==,SO]
Hence in IBM's notation, BI=0 is CR7[SO], BI=1 is CR7[==], etc.
Indexing from BI to guest state:
let n = BI / 4
off = BI % 4
this references CR n:
off==0 -> guest_CRn_321 >> 3
off==1 -> guest_CRn_321 >> 2
off==2 -> guest_CRn_321 >> 1
off==3 -> guest_CRn_SO
Bear in mind the only significant bit in guest_CRn_SO is bit 0
(normal notation) and in guest_CRn_321 the significant bits are
3, 2 and 1 (normal notation).
*/
static void putCR321 ( UInt cr, IRExpr* e )
{
vassert(cr < 8);
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
stmt( IRStmt_Put(guestCR321offset(cr), e) );
}
static void putCR0 ( UInt cr, IRExpr* e )
{
vassert(cr < 8);
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
stmt( IRStmt_Put(guestCR0offset(cr), e) );
}
static IRExpr* /* :: Ity_I8 */ getCR0 ( UInt cr )
{
vassert(cr < 8);
return IRExpr_Get(guestCR0offset(cr), Ity_I8);
}
static IRExpr* /* :: Ity_I8 */ getCR321 ( UInt cr )
{
vassert(cr < 8);
return IRExpr_Get(guestCR321offset(cr), Ity_I8);
}
/* Fetch the specified CR bit (as per IBM/hardware notation) and
return it at the bottom of an I32; the top 31 bits are guaranteed
to be zero. */
static IRExpr* /* :: Ity_I32 */ getCRbit ( UInt bi )
{
UInt n = bi / 4;
UInt off = bi % 4;
vassert(bi < 32);
if (off == 3) {
/* Fetch the SO bit for this CR field */
/* Note: And32 is redundant paranoia iff guest state only has 0
or 1 in that slot. */
return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
} else {
/* Fetch the <, > or == bit for this CR field */
return binop( Iop_And32,
binop( Iop_Shr32,
unop(Iop_8Uto32, getCR321(n)),
mkU8(toUChar(3-off)) ),
mkU32(1) );
}
}
/* Dually, write the least significant bit of BIT to the specified CR
bit. Indexing as per getCRbit. */
static void putCRbit ( UInt bi, IRExpr* bit )
{
UInt n, off;
IRExpr* safe;
vassert(typeOfIRExpr(irsb->tyenv,bit) == Ity_I32);
safe = binop(Iop_And32, bit, mkU32(1));
n = bi / 4;
off = bi % 4;
vassert(bi < 32);
if (off == 3) {
/* This is the SO bit for this CR field */
putCR0(n, unop(Iop_32to8, safe));
} else {
off = 3 - off;
vassert(off == 1 || off == 2 || off == 3);
putCR321(
n,
unop( Iop_32to8,
binop( Iop_Or32,
/* old value with field masked out */
binop(Iop_And32, unop(Iop_8Uto32, getCR321(n)),
mkU32(~(1 << off))),
/* new value in the right place */
binop(Iop_Shl32, safe, mkU8(toUChar(off)))
)
)
);
}
}
/* Fetch the specified CR bit (as per IBM/hardware notation) and
return it somewhere in an I32; it does not matter where, but
whichever bit it is, all other bits are guaranteed to be zero. In
other words, the I32-typed expression will be zero if the bit is
zero and nonzero if the bit is 1. Write into *where the index
of where the bit will be. */
static
IRExpr* /* :: Ity_I32 */ getCRbit_anywhere ( UInt bi, Int* where )
{
UInt n = bi / 4;
UInt off = bi % 4;
vassert(bi < 32);
if (off == 3) {
/* Fetch the SO bit for this CR field */
/* Note: And32 is redundant paranoia iff guest state only has 0
or 1 in that slot. */
*where = 0;
return binop(Iop_And32, unop(Iop_8Uto32, getCR0(n)), mkU32(1));
} else {
/* Fetch the <, > or == bit for this CR field */
*where = 3-off;
return binop( Iop_And32,
unop(Iop_8Uto32, getCR321(n)),
mkU32(1 << (3-off)) );
}
}
/* Set the CR0 flags following an arithmetic operation.
(Condition Register CR0 Field Definition, PPC32 p60)
*/
static IRExpr* getXER_SO ( void );
static void set_CR0 ( IRExpr* result )
{
vassert(typeOfIRExpr(irsb->tyenv,result) == Ity_I32 ||
typeOfIRExpr(irsb->tyenv,result) == Ity_I64);
if (mode64) {
putCR321( 0, unop(Iop_64to8,
binop(Iop_CmpORD64S, result, mkU64(0))) );
} else {
putCR321( 0, unop(Iop_32to8,
binop(Iop_CmpORD32S, result, mkU32(0))) );
}
putCR0( 0, getXER_SO() );
}
/* Set the CR6 flags following an AltiVec compare operation.
* NOTE: This also works for VSX single-precision compares.
* */
static void set_AV_CR6 ( IRExpr* result, Bool test_all_ones )
{
/* CR6[0:3] = {all_ones, 0, all_zeros, 0}
all_ones = (v[0] && v[1] && v[2] && v[3])
all_zeros = ~(v[0] || v[1] || v[2] || v[3])
*/
IRTemp v0 = newTemp(Ity_V128);
IRTemp v1 = newTemp(Ity_V128);
IRTemp v2 = newTemp(Ity_V128);
IRTemp v3 = newTemp(Ity_V128);
IRTemp rOnes = newTemp(Ity_I8);
IRTemp rZeros = newTemp(Ity_I8);
vassert(typeOfIRExpr(irsb->tyenv,result) == Ity_V128);
assign( v0, result );
assign( v1, binop(Iop_ShrV128, result, mkU8(32)) );
assign( v2, binop(Iop_ShrV128, result, mkU8(64)) );
assign( v3, binop(Iop_ShrV128, result, mkU8(96)) );
assign( rZeros, unop(Iop_1Uto8,
binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
unop(Iop_Not32,
unop(Iop_V128to32,
binop(Iop_OrV128,
binop(Iop_OrV128, mkexpr(v0), mkexpr(v1)),
binop(Iop_OrV128, mkexpr(v2), mkexpr(v3))))
))) );
if (test_all_ones) {
assign( rOnes, unop(Iop_1Uto8,
binop(Iop_CmpEQ32, mkU32(0xFFFFFFFF),
unop(Iop_V128to32,
binop(Iop_AndV128,
binop(Iop_AndV128, mkexpr(v0), mkexpr(v1)),
binop(Iop_AndV128, mkexpr(v2), mkexpr(v3)))
))) );
putCR321( 6, binop(Iop_Or8,
binop(Iop_Shl8, mkexpr(rOnes), mkU8(3)),
binop(Iop_Shl8, mkexpr(rZeros), mkU8(1))) );
} else {
putCR321( 6, binop(Iop_Shl8, mkexpr(rZeros), mkU8(1)) );
}
putCR0( 6, mkU8(0) );
}
/*------------------------------------------------------------*/
/*--- Helpers for XER flags. ---*/
/*------------------------------------------------------------*/
static void putXER_SO ( IRExpr* e )
{
IRExpr* so;
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
so = binop(Iop_And8, e, mkU8(1));
stmt( IRStmt_Put( OFFB_XER_SO, so ) );
}
static void putXER_OV ( IRExpr* e )
{
IRExpr* ov;
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
ov = binop(Iop_And8, e, mkU8(1));
stmt( IRStmt_Put( OFFB_XER_OV, ov ) );
}
static void putXER_CA ( IRExpr* e )
{
IRExpr* ca;
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
ca = binop(Iop_And8, e, mkU8(1));
stmt( IRStmt_Put( OFFB_XER_CA, ca ) );
}
static void putXER_BC ( IRExpr* e )
{
IRExpr* bc;
vassert(typeOfIRExpr(irsb->tyenv, e) == Ity_I8);
bc = binop(Iop_And8, e, mkU8(0x7F));
stmt( IRStmt_Put( OFFB_XER_BC, bc ) );
}
static IRExpr* /* :: Ity_I8 */ getXER_SO ( void )
{
return IRExpr_Get( OFFB_XER_SO, Ity_I8 );
}
static IRExpr* /* :: Ity_I32 */ getXER_SO32 ( void )
{
return binop( Iop_And32, unop(Iop_8Uto32, getXER_SO()), mkU32(1) );
}
static IRExpr* /* :: Ity_I8 */ getXER_OV ( void )
{
return IRExpr_Get( OFFB_XER_OV, Ity_I8 );
}
static IRExpr* /* :: Ity_I32 */ getXER_OV32 ( void )
{
return binop( Iop_And32, unop(Iop_8Uto32, getXER_OV()), mkU32(1) );
}
static IRExpr* /* :: Ity_I32 */ getXER_CA32 ( void )
{
IRExpr* ca = IRExpr_Get( OFFB_XER_CA, Ity_I8 );
return binop( Iop_And32, unop(Iop_8Uto32, ca ), mkU32(1) );
}
static IRExpr* /* :: Ity_I8 */ getXER_BC ( void )
{
return IRExpr_Get( OFFB_XER_BC, Ity_I8 );
}
static IRExpr* /* :: Ity_I32 */ getXER_BC32 ( void )
{
IRExpr* bc = IRExpr_Get( OFFB_XER_BC, Ity_I8 );
return binop( Iop_And32, unop(Iop_8Uto32, bc), mkU32(0x7F) );
}
/* RES is the result of doing OP on ARGL and ARGR. Set %XER.OV and
%XER.SO accordingly. */
static void set_XER_OV_32( UInt op, IRExpr* res,
IRExpr* argL, IRExpr* argR )
{
IRTemp t64;
IRExpr* xer_ov;
vassert(op < PPCG_FLAG_OP_NUMBER);
vassert(typeOfIRExpr(irsb->tyenv,res) == Ity_I32);
vassert(typeOfIRExpr(irsb->tyenv,argL) == Ity_I32);
vassert(typeOfIRExpr(irsb->tyenv,argR) == Ity_I32);
# define INT32_MIN 0x80000000
# define XOR2(_aa,_bb) \
binop(Iop_Xor32,(_aa),(_bb))
# define XOR3(_cc,_dd,_ee) \
binop(Iop_Xor32,binop(Iop_Xor32,(_cc),(_dd)),(_ee))
# define AND3(_ff,_gg,_hh) \
binop(Iop_And32,binop(Iop_And32,(_ff),(_gg)),(_hh))
#define NOT(_jj) \
unop(Iop_Not32, (_jj))
switch (op) {
case /* 0 */ PPCG_FLAG_OP_ADD:
case /* 1 */ PPCG_FLAG_OP_ADDE:
/* (argL^argR^-1) & (argL^res) & (1<<31) ?1:0 */
// i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
xer_ov
= AND3( XOR3(argL,argR,mkU32(-1)),
XOR2(argL,res),
mkU32(INT32_MIN) );
/* xer_ov can only be 0 or 1<<31 */
xer_ov
= binop(Iop_Shr32, xer_ov, mkU8(31) );
break;
case /* 2 */ PPCG_FLAG_OP_DIVW:
/* (argL == INT32_MIN && argR == -1) || argR == 0 */
xer_ov
= mkOR1(
mkAND1(
binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)),
binop(Iop_CmpEQ32, argR, mkU32(-1))
),
binop(Iop_CmpEQ32, argR, mkU32(0) )
);
xer_ov
= unop(Iop_1Uto32, xer_ov);
break;
case /* 3 */ PPCG_FLAG_OP_DIVWU:
/* argR == 0 */
xer_ov
= unop(Iop_1Uto32, binop(Iop_CmpEQ32, argR, mkU32(0)));
break;
case /* 4 */ PPCG_FLAG_OP_MULLW:
/* OV true if result can't be represented in 32 bits
i.e sHi != sign extension of sLo */
t64 = newTemp(Ity_I64);
assign( t64, binop(Iop_MullS32, argL, argR) );
xer_ov
= binop( Iop_CmpNE32,
unop(Iop_64HIto32, mkexpr(t64)),
binop( Iop_Sar32,
unop(Iop_64to32, mkexpr(t64)),
mkU8(31))
);
xer_ov
= unop(Iop_1Uto32, xer_ov);
break;
case /* 5 */ PPCG_FLAG_OP_NEG:
/* argL == INT32_MIN */
xer_ov
= unop( Iop_1Uto32,
binop(Iop_CmpEQ32, argL, mkU32(INT32_MIN)) );
break;
case /* 6 */ PPCG_FLAG_OP_SUBF:
case /* 7 */ PPCG_FLAG_OP_SUBFC:
case /* 8 */ PPCG_FLAG_OP_SUBFE:
/* ((~argL)^argR^-1) & ((~argL)^res) & (1<<31) ?1:0; */
xer_ov
= AND3( XOR3(NOT(argL),argR,mkU32(-1)),
XOR2(NOT(argL),res),
mkU32(INT32_MIN) );
/* xer_ov can only be 0 or 1<<31 */
xer_ov
= binop(Iop_Shr32, xer_ov, mkU8(31) );
break;
case PPCG_FLAG_OP_DIVWEU:
xer_ov
= binop( Iop_Or32,
unop( Iop_1Uto32, binop( Iop_CmpEQ32, argR, mkU32( 0 ) ) ),
unop( Iop_1Uto32, binop( Iop_CmpLT32U, argR, argL ) ) );
break;
case PPCG_FLAG_OP_DIVWE:
/* If argR == 0 of if the result cannot fit in the 32-bit destination register,
* then OV <- 1. If dest reg is 0 AND both dividend and divisor are non-zero,
* an overflow is implied.
*/
xer_ov = binop( Iop_Or32,
unop( Iop_1Uto32, binop( Iop_CmpEQ32, argR, mkU32( 0 ) ) ),
unop( Iop_1Uto32, mkAND1( binop( Iop_CmpEQ32, res, mkU32( 0 ) ),
mkAND1( binop( Iop_CmpNE32, argL, mkU32( 0 ) ),
binop( Iop_CmpNE32, argR, mkU32( 0 ) ) ) ) ) );
break;
default:
vex_printf("set_XER_OV: op = %u\n", op);
vpanic("set_XER_OV(ppc)");
}
/* xer_ov MUST denote either 0 or 1, no other value allowed */
putXER_OV( unop(Iop_32to8, xer_ov) );
/* Update the summary overflow */
putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
# undef INT32_MIN
# undef AND3
# undef XOR3
# undef XOR2
# undef NOT
}
static void set_XER_OV_64( UInt op, IRExpr* res,
IRExpr* argL, IRExpr* argR )
{
IRExpr* xer_ov;
vassert(op < PPCG_FLAG_OP_NUMBER);
vassert(typeOfIRExpr(irsb->tyenv,res) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv,argL) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv,argR) == Ity_I64);
# define INT64_MIN 0x8000000000000000ULL
# define XOR2(_aa,_bb) \
binop(Iop_Xor64,(_aa),(_bb))
# define XOR3(_cc,_dd,_ee) \
binop(Iop_Xor64,binop(Iop_Xor64,(_cc),(_dd)),(_ee))
# define AND3(_ff,_gg,_hh) \
binop(Iop_And64,binop(Iop_And64,(_ff),(_gg)),(_hh))
#define NOT(_jj) \
unop(Iop_Not64, (_jj))
switch (op) {
case /* 0 */ PPCG_FLAG_OP_ADD:
case /* 1 */ PPCG_FLAG_OP_ADDE:
/* (argL^argR^-1) & (argL^res) & (1<<63) ? 1:0 */
// i.e. ((both_same_sign) & (sign_changed) & (sign_mask))
xer_ov
= AND3( XOR3(argL,argR,mkU64(-1)),
XOR2(argL,res),
mkU64(INT64_MIN) );
/* xer_ov can only be 0 or 1<<63 */
xer_ov
= unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
break;
case /* 2 */ PPCG_FLAG_OP_DIVW:
/* (argL == INT64_MIN && argR == -1) || argR == 0 */
xer_ov
= mkOR1(
mkAND1(
binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN)),
binop(Iop_CmpEQ64, argR, mkU64(-1))
),
binop(Iop_CmpEQ64, argR, mkU64(0) )
);
break;
case /* 3 */ PPCG_FLAG_OP_DIVWU:
/* argR == 0 */
xer_ov
= binop(Iop_CmpEQ64, argR, mkU64(0));
break;
case /* 4 */ PPCG_FLAG_OP_MULLW: {
/* OV true if result can't be represented in 64 bits
i.e sHi != sign extension of sLo */
xer_ov
= binop( Iop_CmpNE32,
unop(Iop_64HIto32, res),
binop( Iop_Sar32,
unop(Iop_64to32, res),
mkU8(31))
);
break;
}
case /* 5 */ PPCG_FLAG_OP_NEG:
/* argL == INT64_MIN */
xer_ov
= binop(Iop_CmpEQ64, argL, mkU64(INT64_MIN));
break;
case /* 6 */ PPCG_FLAG_OP_SUBF:
case /* 7 */ PPCG_FLAG_OP_SUBFC:
case /* 8 */ PPCG_FLAG_OP_SUBFE:
/* ((~argL)^argR^-1) & ((~argL)^res) & (1<<63) ?1:0; */
xer_ov
= AND3( XOR3(NOT(argL),argR,mkU64(-1)),
XOR2(NOT(argL),res),
mkU64(INT64_MIN) );
/* xer_ov can only be 0 or 1<<63 */
xer_ov
= unop(Iop_64to1, binop(Iop_Shr64, xer_ov, mkU8(63)));
break;
case PPCG_FLAG_OP_DIVDE:
/* If argR == 0, we must set the OV bit. But there's another condition
* where we can get overflow set for divde . . . when the
* result cannot fit in the 64-bit destination register. If dest reg is 0 AND
* both dividend and divisor are non-zero, it implies an overflow.
*/
xer_ov
= mkOR1( binop( Iop_CmpEQ64, argR, mkU64( 0 ) ),
mkAND1( binop( Iop_CmpEQ64, res, mkU64( 0 ) ),
mkAND1( binop( Iop_CmpNE64, argL, mkU64( 0 ) ),
binop( Iop_CmpNE64, argR, mkU64( 0 ) ) ) ) );
break;
case PPCG_FLAG_OP_DIVDEU:
/* If argR == 0 or if argL >= argR, set OV. */
xer_ov = mkOR1( binop( Iop_CmpEQ64, argR, mkU64( 0 ) ),
binop( Iop_CmpLE64U, argR, argL ) );
break;
case /* 18 */ PPCG_FLAG_OP_MULLD: {
IRTemp t128;
/* OV true if result can't be represented in 64 bits
i.e sHi != sign extension of sLo */
t128 = newTemp(Ity_I128);
assign( t128, binop(Iop_MullS64, argL, argR) );
xer_ov
= binop( Iop_CmpNE64,
unop(Iop_128HIto64, mkexpr(t128)),
binop( Iop_Sar64,
unop(Iop_128to64, mkexpr(t128)),
mkU8(63))
);
break;
}
default:
vex_printf("set_XER_OV: op = %u\n", op);
vpanic("set_XER_OV(ppc64)");
}
/* xer_ov MUST denote either 0 or 1, no other value allowed */
putXER_OV( unop(Iop_1Uto8, xer_ov) );
/* Update the summary overflow */
putXER_SO( binop(Iop_Or8, getXER_SO(), getXER_OV()) );
# undef INT64_MIN
# undef AND3
# undef XOR3
# undef XOR2
# undef NOT
}
static void set_XER_OV ( IRType ty, UInt op, IRExpr* res,
IRExpr* argL, IRExpr* argR )
{
if (ty == Ity_I32)
set_XER_OV_32( op, res, argL, argR );
else
set_XER_OV_64( op, res, argL, argR );
}
/* RES is the result of doing OP on ARGL and ARGR with the old %XER.CA
value being OLDCA. Set %XER.CA accordingly. */
static void set_XER_CA_32 ( UInt op, IRExpr* res,
IRExpr* argL, IRExpr* argR, IRExpr* oldca )
{
IRExpr* xer_ca;
vassert(op < PPCG_FLAG_OP_NUMBER);
vassert(typeOfIRExpr(irsb->tyenv,res) == Ity_I32);
vassert(typeOfIRExpr(irsb->tyenv,argL) == Ity_I32);
vassert(typeOfIRExpr(irsb->tyenv,argR) == Ity_I32);
vassert(typeOfIRExpr(irsb->tyenv,oldca) == Ity_I32);
/* Incoming oldca is assumed to hold the values 0 or 1 only. This
seems reasonable given that it's always generated by
getXER_CA32(), which masks it accordingly. In any case it being
0 or 1 is an invariant of the ppc guest state representation;
if it has any other value, that invariant has been violated. */
switch (op) {
case /* 0 */ PPCG_FLAG_OP_ADD:
/* res <u argL */
xer_ca
= unop(Iop_1Uto32, binop(Iop_CmpLT32U, res, argL));
break;
case /* 1 */ PPCG_FLAG_OP_ADDE:
/* res <u argL || (old_ca==1 && res==argL) */
xer_ca
= mkOR1(
binop(Iop_CmpLT32U, res, argL),
mkAND1(
binop(Iop_CmpEQ32, oldca, mkU32(1)),
binop(Iop_CmpEQ32, res, argL)
)
);
xer_ca
= unop(Iop_1Uto32, xer_ca);
break;
case /* 8 */ PPCG_FLAG_OP_SUBFE:
/* res <u argR || (old_ca==1 && res==argR) */
xer_ca
= mkOR1(
binop(Iop_CmpLT32U, res, argR),
mkAND1(
binop(Iop_CmpEQ32, oldca, mkU32(1)),
binop(Iop_CmpEQ32, res, argR)
)
);
xer_ca
= unop(Iop_1Uto32, xer_ca);
break;
case /* 7 */ PPCG_FLAG_OP_SUBFC:
case /* 9 */ PPCG_FLAG_OP_SUBFI:
/* res <=u argR */
xer_ca
= unop(Iop_1Uto32, binop(Iop_CmpLE32U, res, argR));
break;
case /* 10 */ PPCG_FLAG_OP_SRAW:
/* The shift amount is guaranteed to be in 0 .. 63 inclusive.
If it is <= 31, behave like SRAWI; else XER.CA is the sign
bit of argL. */
/* This term valid for shift amount < 32 only */
xer_ca
= binop(
Iop_And32,
binop(Iop_Sar32, argL, mkU8(31)),
binop( Iop_And32,
argL,
binop( Iop_Sub32,
binop(Iop_Shl32, mkU32(1),
unop(Iop_32to8,argR)),
mkU32(1) )
)
);
xer_ca
= IRExpr_ITE(
/* shift amt > 31 ? */
binop(Iop_CmpLT32U, mkU32(31), argR),
/* yes -- get sign bit of argL */
binop(Iop_Shr32, argL, mkU8(31)),
/* no -- be like srawi */
unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0)))
);
break;
case /* 11 */ PPCG_FLAG_OP_SRAWI:
/* xer_ca is 1 iff src was negative and bits_shifted_out !=
0. Since the shift amount is known to be in the range
0 .. 31 inclusive the following seems viable:
xer.ca == 1 iff the following is nonzero:
(argL >>s 31) -- either all 0s or all 1s
& (argL & (1<<argR)-1) -- the stuff shifted out */
xer_ca
= binop(
Iop_And32,
binop(Iop_Sar32, argL, mkU8(31)),
binop( Iop_And32,
argL,
binop( Iop_Sub32,
binop(Iop_Shl32, mkU32(1),
unop(Iop_32to8,argR)),
mkU32(1) )
)
);
xer_ca
= unop(Iop_1Uto32, binop(Iop_CmpNE32, xer_ca, mkU32(0)));
break;
default:
vex_printf("set_XER_CA: op = %u\n", op);
vpanic("set_XER_CA(ppc)");
}
/* xer_ca MUST denote either 0 or 1, no other value allowed */
putXER_CA( unop(Iop_32to8, xer_ca) );
}
static void set_XER_CA_64 ( UInt op, IRExpr* res,
IRExpr* argL, IRExpr* argR, IRExpr* oldca )
{
IRExpr* xer_ca;
vassert(op < PPCG_FLAG_OP_NUMBER);
vassert(typeOfIRExpr(irsb->tyenv,res) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv,argL) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv,argR) == Ity_I64);
vassert(typeOfIRExpr(irsb->tyenv,oldca) == Ity_I64);
/* Incoming oldca is assumed to hold the values 0 or 1 only. This
seems reasonable given that it's always generated by
getXER_CA32(), which masks it accordingly. In any case it being
0 or 1 is an invariant of the ppc guest state representation;
if it has any other value, that invariant has been violated. */
switch (op) {
case /* 0 */ PPCG_FLAG_OP_ADD:
/* res <u argL */
xer_ca
= unop(Iop_1Uto32, binop(Iop_CmpLT64U, res, argL));
break;
case /* 1 */ PPCG_FLAG_OP_ADDE:
/* res <u argL || (old_ca==1 && res==argL) */
xer_ca
= mkOR1(
binop(Iop_CmpLT64U, res, argL),
mkAND1(
binop(Iop_CmpEQ64, oldca, mkU64(1)),
binop(Iop_CmpEQ64, res, argL)
)
);
xer_ca
= unop(Iop_1Uto32, xer_ca);
break;
case /* 8 */ PPCG_FLAG_OP_SUBFE:
/* res <u argR || (old_ca==1 && res==argR) */
xer_ca
= mkOR1(
binop(Iop_CmpLT64U, res, argR),
mkAND1(
binop(Iop_CmpEQ64, oldca, mkU64(1)),
binop(Iop_CmpEQ64, res, argR)
)
);
xer_ca
= unop(Iop_1Uto32, xer_ca);
break;
case /* 7 */ PPCG_FLAG_OP_SUBFC:
case /* 9 */ PPCG_FLAG_OP_SUBFI:
/* res <=u argR */
xer_ca
= unop(Iop_1Uto32, binop(Iop_CmpLE64U, res, argR));
break;
case /* 10 */ PPCG_FLAG_OP_SRAW:
/* The shift amount is guaranteed to be in 0 .. 31 inclusive.
If it is <= 31, behave like SRAWI; else XER.CA is the sign
bit of argL. */
/* This term valid for shift amount < 31 only */
xer_ca
= binop(
Iop_And64,
binop(Iop_Sar64, argL, mkU8(31)),
binop( Iop_And64,
argL,
binop( Iop_Sub64,
binop(Iop_Shl64, mkU64(1),
unop(Iop_64to8,argR)),
mkU64(1) )
)
);
xer_ca
= IRExpr_ITE(
/* shift amt > 31 ? */
binop(Iop_CmpLT64U, mkU64(31), argR),
/* yes -- get sign bit of argL */
unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63))),
/* no -- be like srawi */
unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0)))