/*--------------------------------------------------------------------*/ /*--- begin guest_ppc_toIR.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2004-2015 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 const 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) static void* fnptr_to_fnentry( const 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_TEXASRU offsetofPPCGuestState(guest_TEXASRU) #define OFFB_TFIAR offsetofPPCGuestState(guest_TFIAR) #define OFFB_PPR offsetofPPCGuestState(guest_PPR) #define OFFB_PSPB offsetofPPCGuestState(guest_PSPB) /*------------------------------------------------------------*/ /*--- 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_TEXASRU, // Transactional EXception And Summary Register Upper PPC_GST_PPR, // Program Priority register PPC_GST_PPR32, // Upper 32-bits of Program Priority register PPC_GST_PSPB, /* Problem State Priority Boost register, Note, the * register is initialized to a non-zero value. Currently * Valgrind is not supporting the register value to * automatically decrement. Could be added later if * needed. */ 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 ( const 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; ULong mask; switch (align) { case 1: return addr; // byte aligned case 2: mask = ~0ULL << 1; break; // half-word aligned case 4: mask = ~0ULL << 2; break; // word aligned case 16: mask = ~0ULL << 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 == 2 || 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 ( const 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))) ); 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_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 = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))); break; case /* 12 */ PPCG_FLAG_OP_SRAD: /* The shift amount is guaranteed to be in 0 .. 63 inclusive. If it is <= 63, behave like SRADI; else XER.CA is the sign bit of argL. */ /* This term valid for shift amount < 63 only */ xer_ca = binop( Iop_And64, binop(Iop_Sar64, argL, mkU8(63)), binop( Iop_And64, argL, binop( Iop_Sub64, binop(Iop_Shl64, mkU64(1), unop(Iop_64to8,argR)), mkU64(1) ) ) ); xer_ca = IRExpr_ITE( /* shift amt > 63 ? */ binop(Iop_CmpLT64U, mkU64(63), argR), /* yes -- get sign bit of argL */ unop(Iop_64to32, binop(Iop_Shr64, argL, mkU8(63))), /* no -- be like sradi */ unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))) ); break; case /* 13 */ PPCG_FLAG_OP_SRADI: /* 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 .. 63 inclusive, the following seems viable: xer.ca == 1 iff the following is nonzero: (argL >>s 63) -- either all 0s or all 1s & (argL & (1<<argR)-1) -- the stuff shifted out */ xer_ca = binop( Iop_And64, binop(Iop_Sar64, argL, mkU8(63)), binop( Iop_And64, argL, binop( Iop_Sub64, binop(Iop_Shl64, mkU64(1), unop(Iop_64to8,argR)), mkU64(1) ) ) ); xer_ca = unop(Iop_1Uto32, binop(Iop_CmpNE64, xer_ca, mkU64(0))); break; default: vex_printf("set_XER_CA: op = %u\n", op); vpanic("set_XER_CA(ppc64)"); } /* xer_ca MUST denote either 0 or 1, no other value allowed */ putXER_CA( unop(Iop_32to8, xer_ca) ); } static void set_XER_CA ( IRType ty, UInt op, IRExpr* res, IRExpr* argL, IRExpr* argR, IRExpr* oldca ) { if (ty == Ity_I32) set_XER_CA_32( op, res, argL, argR, oldca ); else set_XER_CA_64( op, res, argL, argR, oldca ); } /*------------------------------------------------------------*/ /*--- Read/write to guest-state --- */ /*------------------------------------------------------------*/ static IRExpr* /* :: Ity_I32/64 */ getGST ( PPC_GST reg ) { IRType ty = mode64 ? Ity_I64 : Ity_I32; switch (reg) { case PPC_GST_SPRG3_RO: return IRExpr_Get( OFFB_SPRG3_RO, ty ); case PPC_GST_CIA: return IRExpr_Get( OFFB_CIA, ty ); case PPC_GST_LR: return IRExpr_Get( OFFB_LR, ty ); case PPC_GST_CTR: return IRExpr_Get( OFFB_CTR, ty ); case PPC_GST_VRSAVE: return IRExpr_Get( OFFB_VRSAVE, Ity_I32 ); case PPC_GST_VSCR: return binop(Iop_And32, IRExpr_Get( OFFB_VSCR,Ity_I32 ), mkU32(MASK_VSCR_VALID)); case PPC_GST_CR: { /* Synthesise the entire CR into a single word. Expensive. */ # define FIELD(_n) \ binop(Iop_Shl32, \ unop(Iop_8Uto32, \ binop(Iop_Or8, \ binop(Iop_And8, getCR321(_n), mkU8(7<<1)), \ binop(Iop_And8, getCR0(_n), mkU8(1)) \ ) \ ), \ mkU8(4 * (7-(_n))) \ ) return binop(Iop_Or32, binop(Iop_Or32, binop(Iop_Or32, FIELD(0), FIELD(1)), binop(Iop_Or32, FIELD(2), FIELD(3)) ), binop(Iop_Or32, binop(Iop_Or32, FIELD(4), FIELD(5)), binop(Iop_Or32, FIELD(6), FIELD(7)) ) ); # undef FIELD } case PPC_GST_XER: return binop(Iop_Or32, binop(Iop_Or32, binop( Iop_Shl32, getXER_SO32(), mkU8(31)), binop( Iop_Shl32, getXER_OV32(), mkU8(30))), binop(Iop_Or32, binop( Iop_Shl32, getXER_CA32(), mkU8(29)), getXER_BC32())); case PPC_GST_TFHAR: return IRExpr_Get( OFFB_TFHAR, ty ); case PPC_GST_TEXASR: return IRExpr_Get( OFFB_TEXASR, ty ); case PPC_GST_TEXASRU: return IRExpr_Get( OFFB_TEXASRU, ty ); case PPC_GST_TFIAR: return IRExpr_Get( OFFB_TFIAR, ty ); case PPC_GST_PPR: return IRExpr_Get( OFFB_PPR, ty ); case PPC_GST_PPR32: return unop( Iop_64HIto32, IRExpr_Get( OFFB_PPR, ty ) ); case PPC_GST_PSPB: return IRExpr_Get( OFFB_PSPB, ty ); default: vex_printf("getGST(ppc): reg = %u", reg); vpanic("getGST(ppc)"); } } /* Get a masked word from the given reg */ static IRExpr* /* ::Ity_I32 */ getGST_masked ( PPC_GST reg, UInt mask ) { IRTemp val = newTemp(Ity_I32); vassert( reg < PPC_GST_MAX ); switch (reg) { case PPC_GST_FPSCR: { /* Vex-generated code expects the FPSCR to be set as follows: all exceptions masked, round-to-nearest. This corresponds to a FPSCR value of 0x0. */ /* In the lower 32 bits of FPSCR, we're only keeping track of * the binary floating point rounding mode, so if the mask isn't * asking for this, just return 0x0. */ if (mask & MASK_FPSCR_RN) { assign( val, unop( Iop_8Uto32, IRExpr_Get( OFFB_FPROUND, Ity_I8 ) ) ); } else { assign( val, mkU32(0x0) ); } break; } default: vex_printf("getGST_masked(ppc): reg = %u", reg); vpanic("getGST_masked(ppc)"); } if (mask != 0xFFFFFFFF) { return binop(Iop_And32, mkexpr(val), mkU32(mask)); } else { return mkexpr(val); } } /* Get a masked word from the given reg */ static IRExpr* /* ::Ity_I32 */getGST_masked_upper(PPC_GST reg, ULong mask) { IRExpr * val; vassert( reg < PPC_GST_MAX ); switch (reg) { case PPC_GST_FPSCR: { /* In the upper 32 bits of FPSCR, we're only keeping track * of the decimal floating point rounding mode, so if the mask * isn't asking for this, just return 0x0. */ if (mask & MASK_FPSCR_DRN) { val = binop( Iop_And32, unop( Iop_8Uto32, IRExpr_Get( OFFB_DFPROUND, Ity_I8 ) ), unop( Iop_64HIto32, mkU64( mask ) ) ); } else { val = mkU32( 0x0ULL ); } break; } default: vex_printf( "getGST_masked_upper(ppc): reg = %u", reg ); vpanic( "getGST_masked_upper(ppc)" ); } return val; } /* Fetch the specified REG[FLD] nibble (as per IBM/hardware notation) and return it at the bottom of an I32; the top 27 bits are guaranteed to be zero. */ static IRExpr* /* ::Ity_I32 */ getGST_field ( PPC_GST reg, UInt fld ) { UInt shft, mask; vassert( fld < 8 ); vassert( reg < PPC_GST_MAX ); shft = 4*(7-fld); mask = 0xF<<shft; switch (reg) { case PPC_GST_XER: vassert(fld ==7); return binop(Iop_Or32, binop(Iop_Or32, binop(Iop_Shl32, getXER_SO32(), mkU8(3)), binop(Iop_Shl32, getXER_OV32(), mkU8(2))), binop( Iop_Shl32, getXER_CA32(), mkU8(1))); break; default: if (shft == 0) return getGST_masked( reg, mask ); else return binop(Iop_Shr32, getGST_masked( reg, mask ), mkU8(toUChar( shft ))); } } static void putGST ( PPC_GST reg, IRExpr* src ) { IRType ty = mode64 ? Ity_I64 : Ity_I32; IRType ty_src = typeOfIRExpr(irsb->tyenv,src ); vassert( reg < PPC_GST_MAX ); switch (reg) { case PPC_GST_IP_AT_SYSCALL: vassert( ty_src == ty ); stmt( IRStmt_Put( OFFB_IP_AT_SYSCALL, src ) ); break; case PPC_GST_CIA: vassert( ty_src == ty ); stmt( IRStmt_Put( OFFB_CIA, src ) ); break; case PPC_GST_LR: vassert( ty_src == ty ); stmt( IRStmt_Put( OFFB_LR, src ) ); break; case PPC_GST_CTR: vassert( ty_src == ty ); stmt( IRStmt_Put( OFFB_CTR, src ) ); break; case PPC_GST_VRSAVE: vassert( ty_src == Ity_I32 ); stmt( IRStmt_Put( OFFB_VRSAVE,src)); break; case PPC_GST_VSCR: vassert( ty_src == Ity_I32 ); stmt( IRStmt_Put( OFFB_VSCR, binop(Iop_And32, src, mkU32(MASK_VSCR_VALID)) ) ); break; case PPC_GST_XER: vassert( ty_src == Ity_I32 ); putXER_SO( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(31))) ); putXER_OV( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(30))) ); putXER_CA( unop(Iop_32to8, binop(Iop_Shr32, src, mkU8(29))) ); putXER_BC( unop(Iop_32to8, src) ); break; case PPC_GST_EMWARN: vassert( ty_src == Ity_I32 ); stmt( IRStmt_Put( OFFB_EMNOTE,src) ); break; case PPC_GST_CMSTART: vassert( ty_src == ty ); stmt( IRStmt_Put( OFFB_CMSTART, src) ); break; case PPC_GST_CMLEN: vassert( ty_src == ty ); stmt( IRStmt_Put( OFFB_CMLEN, src) ); break; case PPC_GST_TEXASR: vassert( ty_src == Ity_I64 ); stmt( IRStmt_Put( OFFB_TEXASR, src ) ); break; case PPC_GST_TEXASRU: vassert( ty_src == Ity_I32 ); stmt( IRStmt_Put( OFFB_TEXASRU, src ) ); break; case PPC_GST_TFIAR: vassert( ty_src == Ity_I64 ); stmt( IRStmt_Put( OFFB_TFIAR, src ) ); break; case PPC_GST_TFHAR: vassert( ty_src == Ity_I64 ); stmt( IRStmt_Put( OFFB_TFHAR, src ) ); break; case PPC_GST_PPR32: case PPC_GST_PPR: { /* The Program Priority Register (PPR) stores the priority in * bits [52:50]. The user setable priorities are: * * 001 very low * 010 low * 011 medium low * 100 medium * 101 medium high * * If the argument is not between 0b001 and 0b100 the priority is set * to 0b100. The priority can only be set to 0b101 if the the Problem * State Boost Register is non-zero. The value of the PPR is not * changed if the input is not valid. */ IRTemp not_valid = newTemp(Ity_I64); IRTemp has_perm = newTemp(Ity_I64); IRTemp new_src = newTemp(Ity_I64); IRTemp PSPB_val = newTemp(Ity_I64); IRTemp value = newTemp(Ity_I64); vassert(( ty_src == Ity_I64 ) || ( ty_src == Ity_I32 )); assign( PSPB_val, binop( Iop_32HLto64, mkU32( 0 ), IRExpr_Get( OFFB_PSPB, Ity_I32 ) ) ); if( reg == PPC_GST_PPR32 ) { vassert( ty_src == Ity_I32 ); assign( value, binop( Iop_32HLto64, mkU32(0), binop( Iop_And32, binop( Iop_Shr32, src, mkU8( 18 ) ), mkU32( 0x7 ) ) ) ); } else { vassert( ty_src == Ity_I64 ); assign( value, binop( Iop_And64, binop( Iop_Shr64, src, mkU8( 50 ) ), mkU64( 0x7 ) ) ); } assign( has_perm, binop( Iop_And64, unop( Iop_1Sto64, binop( Iop_CmpEQ64, mkexpr( PSPB_val ), mkU64( 0 ) ) ), unop( Iop_1Sto64, binop( Iop_CmpEQ64, mkU64( 0x5 ), mkexpr( value ) ) ) ) ); assign( not_valid, binop( Iop_Or64, unop( Iop_1Sto64, binop( Iop_CmpEQ64, mkexpr( value ), mkU64( 0 ) ) ), unop( Iop_1Sto64, binop( Iop_CmpLT64U, mkU64( 0x5 ), mkexpr( value ) ) ) ) ); assign( new_src, binop( Iop_Or64, binop( Iop_And64, unop( Iop_Not64, mkexpr( not_valid ) ), src ), binop( Iop_And64, mkexpr( not_valid ), binop( Iop_Or64, binop( Iop_And64, mkexpr( has_perm), binop( Iop_Shl64, mkexpr( value ), mkU8( 50 ) ) ), binop( Iop_And64, IRExpr_Get( OFFB_PPR, ty ), unop( Iop_Not64, mkexpr( has_perm ) ) ) ) ) ) ); /* make sure we only set the valid bit field [52:50] */ stmt( IRStmt_Put( OFFB_PPR, binop( Iop_And64, mkexpr( new_src ), mkU64( 0x1C000000000000) ) ) ); break; } default: vex_printf("putGST(ppc): reg = %u", reg); vpanic("putGST(ppc)"); } } /* Write masked src to the given reg */ static void putGST_masked ( PPC_GST reg, IRExpr* src, ULong mask ) { IRType ty = mode64 ? Ity_I64 : Ity_I32; vassert( reg < PPC_GST_MAX ); vassert( typeOfIRExpr( irsb->tyenv,src ) == Ity_I64 ); switch (reg) { case PPC_GST_FPSCR: { /* Allow writes to either binary or decimal floating point Rounding Mode. */ /* If any part of |mask| covers FPSCR.RN, update the bits of FPSCR.RN by copying in |src| for locations where the corresponding bit in |mask| is 1, and leaving it unchanged for corresponding |mask| zero bits. */ if (mask & MASK_FPSCR_RN) { stmt( IRStmt_Put( OFFB_FPROUND, unop( Iop_32to8, binop( Iop_Or32, binop( Iop_And32, unop(Iop_64to32, src), mkU32(MASK_FPSCR_RN & mask) ), binop( Iop_And32, unop(Iop_8Uto32, IRExpr_Get(OFFB_FPROUND,Ity_I8)), mkU32(MASK_FPSCR_RN & ~mask) ) ) ) ) ); } /* Similarly, update FPSCR.DRN if any bits of |mask| corresponding to FPSCR.DRN are set. */ if (mask & MASK_FPSCR_DRN) { stmt( IRStmt_Put( OFFB_DFPROUND, unop( Iop_32to8, binop( Iop_Or32, binop( Iop_And32, unop(Iop_64HIto32, src), mkU32((MASK_FPSCR_DRN & mask) >> 32) ), binop( Iop_And32, unop(Iop_8Uto32, IRExpr_Get(OFFB_DFPROUND,Ity_I8)), mkU32((MASK_FPSCR_DRN & ~mask) >> 32) ) ) ) ) ); } /* Give EmNote for attempted writes to: - Exception Controls - Non-IEEE Mode */ if (mask & 0xFC) { // Exception Control, Non-IEE mode VexEmNote ew = EmWarn_PPCexns; /* If any of the src::exception_control bits are actually set, side-exit to the next insn, reporting the warning, so that Valgrind's dispatcher sees the warning. */ putGST( PPC_GST_EMWARN, mkU32(ew) ); stmt( IRStmt_Exit( binop(Iop_CmpNE32, mkU32(ew), mkU32(EmNote_NONE)), Ijk_EmWarn, mkSzConst( ty, nextInsnAddr()), OFFB_CIA )); } /* Ignore all other writes */ break; } default: vex_printf("putGST_masked(ppc): reg = %u", reg); vpanic("putGST_masked(ppc)"); } } /* Write the least significant nibble of src to the specified REG[FLD] (as per IBM/hardware notation). */ static void putGST_field ( PPC_GST reg, IRExpr* src, UInt fld ) { UInt shft; ULong mask; vassert( typeOfIRExpr(irsb->tyenv,src ) == Ity_I32 ); vassert( fld < 16 ); vassert( reg < PPC_GST_MAX ); if (fld < 8) shft = 4*(7-fld); else shft = 4*(15-fld); mask = 0xF; mask = mask << shft; switch (reg) { case PPC_GST_CR: putCR0 (fld, binop(Iop_And8, mkU8(1 ), unop(Iop_32to8, src))); putCR321(fld, binop(Iop_And8, mkU8(7<<1), unop(Iop_32to8, src))); break; default: { IRExpr * src64 = unop( Iop_32Uto64, src ); if (shft == 0) { putGST_masked( reg, src64, mask ); } else { putGST_masked( reg, binop( Iop_Shl64, src64, mkU8( toUChar( shft ) ) ), mask ); } } } } /*------------------------------------------------------------*/ /* Helpers for VSX instructions that do floating point * operations and need to determine if a src contains a * special FP value. * *------------------------------------------------------------*/ #define NONZERO_FRAC_MASK 0x000fffffffffffffULL #define FP_FRAC_PART(x) binop( Iop_And64, \ mkexpr( x ), \ mkU64( NONZERO_FRAC_MASK ) ) // Returns exponent part of a single precision floating point as I32 static IRExpr * fp_exp_part_sp(IRTemp src) { return binop( Iop_And32, binop( Iop_Shr32, mkexpr( src ), mkU8( 23 ) ), mkU32( 0xff ) ); } // Returns exponent part of floating point as I32 static IRExpr * fp_exp_part(IRTemp src, Bool sp) { IRExpr * exp; if (sp) return fp_exp_part_sp(src); if (!mode64) exp = binop( Iop_And32, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( src ) ), mkU8( 20 ) ), mkU32( 0x7ff ) ); else exp = unop( Iop_64to32, binop( Iop_And64, binop( Iop_Shr64, mkexpr( src ), mkU8( 52 ) ), mkU64( 0x7ff ) ) ); return exp; } static IRExpr * is_Inf_sp(IRTemp src) { IRTemp frac_part = newTemp(Ity_I32); IRExpr * Inf_exp; assign( frac_part, binop( Iop_And32, mkexpr(src), mkU32(0x007fffff)) ); Inf_exp = binop( Iop_CmpEQ32, fp_exp_part( src, True /*single precision*/ ), mkU32( 0xff ) ); return mkAND1( Inf_exp, binop( Iop_CmpEQ32, mkexpr( frac_part ), mkU32( 0 ) ) ); } // Infinity: exp = 7ff and fraction is zero; s = 0/1 static IRExpr * is_Inf(IRTemp src, Bool sp) { IRExpr * Inf_exp, * hi32, * low32; IRTemp frac_part; if (sp) return is_Inf_sp(src); frac_part = newTemp(Ity_I64); assign( frac_part, FP_FRAC_PART(src) ); Inf_exp = binop( Iop_CmpEQ32, fp_exp_part( src, False /*not single precision*/ ), mkU32( 0x7ff ) ); hi32 = unop( Iop_64HIto32, mkexpr( frac_part ) ); low32 = unop( Iop_64to32, mkexpr( frac_part ) ); return mkAND1( Inf_exp, binop( Iop_CmpEQ32, binop( Iop_Or32, low32, hi32 ), mkU32( 0 ) ) ); } static IRExpr * is_Zero_sp(IRTemp src) { IRTemp sign_less_part = newTemp(Ity_I32); assign( sign_less_part, binop( Iop_And32, mkexpr( src ), mkU32( SIGN_MASK32 ) ) ); return binop( Iop_CmpEQ32, mkexpr( sign_less_part ), mkU32( 0 ) ); } // Zero: exp is zero and fraction is zero; s = 0/1 static IRExpr * is_Zero(IRTemp src, Bool sp) { IRExpr * hi32, * low32; IRTemp sign_less_part; if (sp) return is_Zero_sp(src); sign_less_part = newTemp(Ity_I64); assign( sign_less_part, binop( Iop_And64, mkexpr( src ), mkU64( SIGN_MASK ) ) ); hi32 = unop( Iop_64HIto32, mkexpr( sign_less_part ) ); low32 = unop( Iop_64to32, mkexpr( sign_less_part ) ); return binop( Iop_CmpEQ32, binop( Iop_Or32, low32, hi32 ), mkU32( 0 ) ); } /* SNAN: s = 1/0; exp = 0x7ff; fraction is nonzero, with highest bit '1' * QNAN: s = 1/0; exp = 0x7ff; fraction is nonzero, with highest bit '0' * This function returns an IRExpr value of '1' for any type of NaN. */ static IRExpr * is_NaN(IRTemp src) { IRExpr * NaN_exp, * hi32, * low32; IRTemp frac_part = newTemp(Ity_I64); assign( frac_part, FP_FRAC_PART(src) ); hi32 = unop( Iop_64HIto32, mkexpr( frac_part ) ); low32 = unop( Iop_64to32, mkexpr( frac_part ) ); NaN_exp = binop( Iop_CmpEQ32, fp_exp_part( src, False /*not single precision*/ ), mkU32( 0x7ff ) ); return mkAND1( NaN_exp, binop( Iop_CmpNE32, binop( Iop_Or32, low32, hi32 ), mkU32( 0 ) ) ); } /* This function returns an IRExpr value of '1' for any type of NaN. * The passed 'src' argument is assumed to be Ity_I32. */ static IRExpr * is_NaN_32(IRTemp src) { #define NONZERO_FRAC_MASK32 0x007fffffULL #define FP_FRAC_PART32(x) binop( Iop_And32, \ mkexpr( x ), \ mkU32( NONZERO_FRAC_MASK32 ) ) IRExpr * frac_part = FP_FRAC_PART32(src); IRExpr * exp_part = binop( Iop_And32, binop( Iop_Shr32, mkexpr( src ), mkU8( 23 ) ), mkU32( 0x0ff ) ); IRExpr * NaN_exp = binop( Iop_CmpEQ32, exp_part, mkU32( 0xff ) ); return mkAND1( NaN_exp, binop( Iop_CmpNE32, frac_part, mkU32( 0 ) ) ); } /* This function takes an Ity_I32 input argument interpreted * as a single-precision floating point value. If src is a * SNaN, it is changed to a QNaN and returned; otherwise, * the original value is returned. */ static IRExpr * handle_SNaN_to_QNaN_32(IRExpr * src) { #define SNAN_MASK32 0x00400000 IRTemp tmp = newTemp(Ity_I32); IRTemp mask = newTemp(Ity_I32); IRTemp is_SNAN = newTemp(Ity_I1); vassert( typeOfIRExpr(irsb->tyenv, src ) == Ity_I32 ); assign(tmp, src); /* check if input is SNaN, if it is convert to QNaN */ assign( is_SNAN, mkAND1( is_NaN_32( tmp ), binop( Iop_CmpEQ32, binop( Iop_And32, mkexpr( tmp ), mkU32( SNAN_MASK32 ) ), mkU32( 0 ) ) ) ); /* create mask with QNaN bit set to make it a QNaN if tmp is SNaN */ assign ( mask, binop( Iop_And32, unop( Iop_1Sto32, mkexpr( is_SNAN ) ), mkU32( SNAN_MASK32 ) ) ); return binop( Iop_Or32, mkexpr( mask ), mkexpr( tmp) ); } /* This helper function performs the negation part of operations of the form: * "Negate Multiply-<op>" * where "<op>" is either "Add" or "Sub". * * This function takes one argument -- the floating point intermediate result (converted to * Ity_I64 via Iop_ReinterpF64asI64) that was obtained from the "Multip-<op>" part of * the operation described above. */ static IRTemp getNegatedResult(IRTemp intermediateResult) { ULong signbit_mask = 0x8000000000000000ULL; IRTemp signbit_32 = newTemp(Ity_I32); IRTemp resultantSignbit = newTemp(Ity_I1); IRTemp negatedResult = newTemp(Ity_I64); assign( signbit_32, binop( Iop_Shr32, unop( Iop_64HIto32, binop( Iop_And64, mkexpr( intermediateResult ), mkU64( signbit_mask ) ) ), mkU8( 31 ) ) ); /* We negate the signbit if and only if the intermediate result from the * multiply-<op> was NOT a NaN. This is an XNOR predicate. */ assign( resultantSignbit, unop( Iop_Not1, binop( Iop_CmpEQ32, binop( Iop_Xor32, mkexpr( signbit_32 ), unop( Iop_1Uto32, is_NaN( intermediateResult ) ) ), mkU32( 1 ) ) ) ); assign( negatedResult, binop( Iop_Or64, binop( Iop_And64, mkexpr( intermediateResult ), mkU64( ~signbit_mask ) ), binop( Iop_32HLto64, binop( Iop_Shl32, unop( Iop_1Uto32, mkexpr( resultantSignbit ) ), mkU8( 31 ) ), mkU32( 0 ) ) ) ); return negatedResult; } /* This helper function performs the negation part of operations of the form: * "Negate Multiply-<op>" * where "<op>" is either "Add" or "Sub". * * This function takes one argument -- the floating point intermediate result (converted to * Ity_I32 via Iop_ReinterpF32asI32) that was obtained from the "Multip-<op>" part of * the operation described above. */ static IRTemp getNegatedResult_32(IRTemp intermediateResult) { UInt signbit_mask = 0x80000000; IRTemp signbit_32 = newTemp(Ity_I32); IRTemp resultantSignbit = newTemp(Ity_I1); IRTemp negatedResult = newTemp(Ity_I32); assign( signbit_32, binop( Iop_Shr32, binop( Iop_And32, mkexpr( intermediateResult ), mkU32( signbit_mask ) ), mkU8( 31 ) ) ); /* We negate the signbit if and only if the intermediate result from the * multiply-<op> was NOT a NaN. This is an XNOR predicate. */ assign( resultantSignbit, unop( Iop_Not1, binop( Iop_CmpEQ32, binop( Iop_Xor32, mkexpr( signbit_32 ), unop( Iop_1Uto32, is_NaN_32( intermediateResult ) ) ), mkU32( 1 ) ) ) ); assign( negatedResult, binop( Iop_Or32, binop( Iop_And32, mkexpr( intermediateResult ), mkU32( ~signbit_mask ) ), binop( Iop_Shl32, unop( Iop_1Uto32, mkexpr( resultantSignbit ) ), mkU8( 31 ) ) ) ); return negatedResult; } /*------------------------------------------------------------*/ /* Transactional memory helpers * *------------------------------------------------------------*/ static ULong generate_TMreason( UInt failure_code, UInt persistant, UInt nest_overflow, UInt tm_exact ) { ULong tm_err_code = ( (ULong) 0) << (63-6) /* Failure code */ | ( (ULong) persistant) << (63-7) /* Failure persistant */ | ( (ULong) 0) << (63-8) /* Disallowed */ | ( (ULong) nest_overflow) << (63-9) /* Nesting Overflow */ | ( (ULong) 0) << (63-10) /* Footprint Overflow */ | ( (ULong) 0) << (63-11) /* Self-Induced Conflict */ | ( (ULong) 0) << (63-12) /* Non-Transactional Conflict */ | ( (ULong) 0) << (63-13) /* Transactional Conflict */ | ( (ULong) 0) << (63-14) /* Translation Invalidation Conflict */ | ( (ULong) 0) << (63-15) /* Implementation-specific */ | ( (ULong) 0) << (63-16) /* Instruction Fetch Conflict */ | ( (ULong) 0) << (63-30) /* Reserved */ | ( (ULong) 0) << (63-31) /* Abort */ | ( (ULong) 0) << (63-32) /* Suspend */ | ( (ULong) 0) << (63-33) /* Reserved */ | ( (ULong) 0) << (63-35) /* Privilege */ | ( (ULong) 0) << (63-36) /* Failure Summary */ | ( (ULong) tm_exact) << (63-37) /* TFIAR Exact */ | ( (ULong) 0) << (63-38) /* ROT */ | ( (ULong) 0) << (63-51) /* Reserved */ | ( (ULong) 0) << (63-63); /* Transaction Level */ return tm_err_code; } static void storeTMfailure( Addr64 err_address, ULong tm_reason, Addr64 handler_address ) { putGST( PPC_GST_TFIAR, mkU64( err_address ) ); putGST( PPC_GST_TEXASR, mkU64( tm_reason ) ); putGST( PPC_GST_TEXASRU, mkU32( 0 ) ); putGST( PPC_GST_TFHAR, mkU64( handler_address ) ); } /*------------------------------------------------------------*/ /*--- Integer Instruction Translation --- */ /*------------------------------------------------------------*/ /* Integer Arithmetic Instructions */ static Bool dis_int_arith ( UInt theInstr ) { /* D-Form, XO-Form */ UChar opc1 = ifieldOPC(theInstr); UChar rD_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UInt uimm16 = ifieldUIMM16(theInstr); UChar rB_addr = ifieldRegB(theInstr); UChar flag_OE = ifieldBIT10(theInstr); UInt opc2 = ifieldOPClo9(theInstr); UChar flag_rC = ifieldBIT0(theInstr); Long simm16 = extend_s_16to64(uimm16); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp rA = newTemp(ty); IRTemp rB = newTemp(ty); IRTemp rD = newTemp(ty); Bool do_rc = False; assign( rA, getIReg(rA_addr) ); assign( rB, getIReg(rB_addr) ); // XO-Form: rD, rA, rB switch (opc1) { /* D-Form */ case 0x0C: // addic (Add Immediate Carrying, PPC32 p351 DIP("addic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16); assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA), mkSzExtendS16(ty, uimm16) ) ); set_XER_CA( ty, PPCG_FLAG_OP_ADD, mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16), mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ ); break; case 0x0D: // addic. (Add Immediate Carrying and Record, PPC32 p352) DIP("addic. r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16); assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA), mkSzExtendS16(ty, uimm16) ) ); set_XER_CA( ty, PPCG_FLAG_OP_ADD, mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16), mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ ); do_rc = True; // Always record to CR flag_rC = 1; break; case 0x0E: // addi (Add Immediate, PPC32 p350) // li rD,val == addi rD,0,val // la disp(rA) == addi rD,rA,disp if ( rA_addr == 0 ) { DIP("li r%u,%d\n", rD_addr, (Int)simm16); assign( rD, mkSzExtendS16(ty, uimm16) ); } else { DIP("addi r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16); assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA), mkSzExtendS16(ty, uimm16) ) ); } break; case 0x0F: // addis (Add Immediate Shifted, PPC32 p353) // lis rD,val == addis rD,0,val if ( rA_addr == 0 ) { DIP("lis r%u,%d\n", rD_addr, (Int)simm16); assign( rD, mkSzExtendS32(ty, uimm16 << 16) ); } else { DIP("addis r%u,r%u,0x%x\n", rD_addr, rA_addr, (UInt)simm16); assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA), mkSzExtendS32(ty, uimm16 << 16) ) ); } break; case 0x07: // mulli (Multiply Low Immediate, PPC32 p490) DIP("mulli r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16); if (mode64) assign( rD, unop(Iop_128to64, binop(Iop_MullS64, mkexpr(rA), mkSzExtendS16(ty, uimm16))) ); else assign( rD, unop(Iop_64to32, binop(Iop_MullS32, mkexpr(rA), mkSzExtendS16(ty, uimm16))) ); break; case 0x08: // subfic (Subtract from Immediate Carrying, PPC32 p540) DIP("subfic r%u,r%u,%d\n", rD_addr, rA_addr, (Int)simm16); // rD = simm16 - rA assign( rD, binop( mkSzOp(ty, Iop_Sub8), mkSzExtendS16(ty, uimm16), mkexpr(rA)) ); set_XER_CA( ty, PPCG_FLAG_OP_SUBFI, mkexpr(rD), mkexpr(rA), mkSzExtendS16(ty, uimm16), mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ ); break; /* XO-Form */ case 0x1F: do_rc = True; // All below record to CR switch (opc2) { case 0x10A: // add (Add, PPC32 p347) DIP("add%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA), mkexpr(rB) ) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_ADD, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; case 0x00A: // addc (Add Carrying, PPC32 p348) DIP("addc%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA), mkexpr(rB)) ); set_XER_CA( ty, PPCG_FLAG_OP_ADD, mkexpr(rD), mkexpr(rA), mkexpr(rB), mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_ADD, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; case 0x08A: { // adde (Add Extended, PPC32 p349) IRTemp old_xer_ca = newTemp(ty); DIP("adde%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); // rD = rA + rB + XER[CA] assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) ); assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA), binop( mkSzOp(ty, Iop_Add8), mkexpr(rB), mkexpr(old_xer_ca))) ); set_XER_CA( ty, PPCG_FLAG_OP_ADDE, mkexpr(rD), mkexpr(rA), mkexpr(rB), mkexpr(old_xer_ca) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_ADDE, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; } case 0x0EA: { // addme (Add to Minus One Extended, PPC32 p354) IRTemp old_xer_ca = newTemp(ty); IRExpr *min_one; if (rB_addr != 0) { vex_printf("dis_int_arith(ppc)(addme,rB_addr)\n"); return False; } DIP("addme%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); // rD = rA + (-1) + XER[CA] // => Just another form of adde assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) ); min_one = mkSzImm(ty, (Long)-1); assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA), binop( mkSzOp(ty, Iop_Add8), min_one, mkexpr(old_xer_ca)) )); set_XER_CA( ty, PPCG_FLAG_OP_ADDE, mkexpr(rD), mkexpr(rA), min_one, mkexpr(old_xer_ca) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_ADDE, mkexpr(rD), mkexpr(rA), min_one ); } break; } case 0x0CA: { // addze (Add to Zero Extended, PPC32 p355) IRTemp old_xer_ca = newTemp(ty); if (rB_addr != 0) { vex_printf("dis_int_arith(ppc)(addze,rB_addr)\n"); return False; } DIP("addze%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); // rD = rA + (0) + XER[CA] // => Just another form of adde assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) ); assign( rD, binop( mkSzOp(ty, Iop_Add8), mkexpr(rA), mkexpr(old_xer_ca)) ); set_XER_CA( ty, PPCG_FLAG_OP_ADDE, mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0), mkexpr(old_xer_ca) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_ADDE, mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) ); } break; } case 0x1EB: // divw (Divide Word, PPC32 p388) DIP("divw%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); if (mode64) { /* Note: XER settings are mode independent, and reflect the overflow of the low-order 32bit result CR0[LT|GT|EQ] are undefined if flag_rC && mode64 */ /* rD[hi32] are undefined: setting them to sign of lo32 - makes set_CR0 happy */ IRExpr* dividend = mk64lo32Sto64( mkexpr(rA) ); IRExpr* divisor = mk64lo32Sto64( mkexpr(rB) ); assign( rD, mk64lo32Uto64( binop(Iop_DivS64, dividend, divisor) ) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_DIVW, mkexpr(rD), dividend, divisor ); } } else { assign( rD, binop(Iop_DivS32, mkexpr(rA), mkexpr(rB)) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_DIVW, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } } /* Note: if (0x8000_0000 / -1) or (x / 0) => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1 => But _no_ exception raised. */ break; case 0x1CB: // divwu (Divide Word Unsigned, PPC32 p389) DIP("divwu%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); if (mode64) { /* Note: XER settings are mode independent, and reflect the overflow of the low-order 32bit result CR0[LT|GT|EQ] are undefined if flag_rC && mode64 */ IRExpr* dividend = mk64lo32Uto64( mkexpr(rA) ); IRExpr* divisor = mk64lo32Uto64( mkexpr(rB) ); assign( rD, mk64lo32Uto64( binop(Iop_DivU64, dividend, divisor) ) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_DIVWU, mkexpr(rD), dividend, divisor ); } } else { assign( rD, binop(Iop_DivU32, mkexpr(rA), mkexpr(rB)) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_DIVWU, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } } /* Note: ditto comment divw, for (x / 0) */ break; case 0x04B: // mulhw (Multiply High Word, PPC32 p488) if (flag_OE != 0) { vex_printf("dis_int_arith(ppc)(mulhw,flag_OE)\n"); return False; } DIP("mulhw%s r%u,r%u,r%u\n", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); if (mode64) { /* rD[hi32] are undefined: setting them to sign of lo32 - makes set_CR0 happy */ assign( rD, binop(Iop_Sar64, binop(Iop_Mul64, mk64lo32Sto64( mkexpr(rA) ), mk64lo32Sto64( mkexpr(rB) )), mkU8(32)) ); } else { assign( rD, unop(Iop_64HIto32, binop(Iop_MullS32, mkexpr(rA), mkexpr(rB))) ); } break; case 0x00B: // mulhwu (Multiply High Word Unsigned, PPC32 p489) if (flag_OE != 0) { vex_printf("dis_int_arith(ppc)(mulhwu,flag_OE)\n"); return False; } DIP("mulhwu%s r%u,r%u,r%u\n", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); if (mode64) { /* rD[hi32] are undefined: setting them to sign of lo32 - makes set_CR0 happy */ assign( rD, binop(Iop_Sar64, binop(Iop_Mul64, mk64lo32Uto64( mkexpr(rA) ), mk64lo32Uto64( mkexpr(rB) ) ), mkU8(32)) ); } else { assign( rD, unop(Iop_64HIto32, binop(Iop_MullU32, mkexpr(rA), mkexpr(rB))) ); } break; case 0x0EB: // mullw (Multiply Low Word, PPC32 p491) DIP("mullw%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); if (mode64) { /* rD[hi32] are undefined: setting them to sign of lo32 - set_XER_OV() and set_CR0() depend on this */ IRExpr *a = unop(Iop_64to32, mkexpr(rA) ); IRExpr *b = unop(Iop_64to32, mkexpr(rB) ); assign( rD, binop(Iop_MullS32, a, b) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_MULLW, mkexpr(rD), unop(Iop_32Uto64, a), unop(Iop_32Uto64, b) ); } } else { assign( rD, unop(Iop_64to32, binop(Iop_MullU32, mkexpr(rA), mkexpr(rB))) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_MULLW, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } } break; case 0x068: // neg (Negate, PPC32 p493) if (rB_addr != 0) { vex_printf("dis_int_arith(ppc)(neg,rB_addr)\n"); return False; } DIP("neg%s%s r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr); // rD = (~rA) + 1 assign( rD, binop( mkSzOp(ty, Iop_Add8), unop( mkSzOp(ty, Iop_Not8), mkexpr(rA) ), mkSzImm(ty, 1)) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_NEG, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; case 0x028: // subf (Subtract From, PPC32 p537) DIP("subf%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); // rD = rB - rA assign( rD, binop( mkSzOp(ty, Iop_Sub8), mkexpr(rB), mkexpr(rA)) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_SUBF, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; case 0x008: // subfc (Subtract from Carrying, PPC32 p538) DIP("subfc%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); // rD = rB - rA assign( rD, binop( mkSzOp(ty, Iop_Sub8), mkexpr(rB), mkexpr(rA)) ); set_XER_CA( ty, PPCG_FLAG_OP_SUBFC, mkexpr(rD), mkexpr(rA), mkexpr(rB), mkSzImm(ty, 0)/*old xer.ca, which is ignored*/ ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_SUBFC, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; case 0x088: {// subfe (Subtract from Extended, PPC32 p539) IRTemp old_xer_ca = newTemp(ty); DIP("subfe%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); // rD = (log not)rA + rB + XER[CA] assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) ); assign( rD, binop( mkSzOp(ty, Iop_Add8), unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)), binop( mkSzOp(ty, Iop_Add8), mkexpr(rB), mkexpr(old_xer_ca))) ); set_XER_CA( ty, PPCG_FLAG_OP_SUBFE, mkexpr(rD), mkexpr(rA), mkexpr(rB), mkexpr(old_xer_ca) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_SUBFE, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; } case 0x0E8: { // subfme (Subtract from -1 Extended, PPC32 p541) IRTemp old_xer_ca = newTemp(ty); IRExpr *min_one; if (rB_addr != 0) { vex_printf("dis_int_arith(ppc)(subfme,rB_addr)\n"); return False; } DIP("subfme%s%s r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr); // rD = (log not)rA + (-1) + XER[CA] // => Just another form of subfe assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) ); min_one = mkSzImm(ty, (Long)-1); assign( rD, binop( mkSzOp(ty, Iop_Add8), unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)), binop( mkSzOp(ty, Iop_Add8), min_one, mkexpr(old_xer_ca))) ); set_XER_CA( ty, PPCG_FLAG_OP_SUBFE, mkexpr(rD), mkexpr(rA), min_one, mkexpr(old_xer_ca) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_SUBFE, mkexpr(rD), mkexpr(rA), min_one ); } break; } case 0x0C8: { // subfze (Subtract from Zero Extended, PPC32 p542) IRTemp old_xer_ca = newTemp(ty); if (rB_addr != 0) { vex_printf("dis_int_arith(ppc)(subfze,rB_addr)\n"); return False; } DIP("subfze%s%s r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr); // rD = (log not)rA + (0) + XER[CA] // => Just another form of subfe assign( old_xer_ca, mkWidenFrom32(ty, getXER_CA32(), False) ); assign( rD, binop( mkSzOp(ty, Iop_Add8), unop( mkSzOp(ty, Iop_Not8), mkexpr(rA)), mkexpr(old_xer_ca)) ); set_XER_CA( ty, PPCG_FLAG_OP_SUBFE, mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0), mkexpr(old_xer_ca) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_SUBFE, mkexpr(rD), mkexpr(rA), mkSzImm(ty, 0) ); } break; } /* 64bit Arithmetic */ case 0x49: // mulhd (Multiply High DWord, PPC64 p539) if (flag_OE != 0) { vex_printf("dis_int_arith(ppc)(mulhd,flagOE)\n"); return False; } DIP("mulhd%s r%u,r%u,r%u\n", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); assign( rD, unop(Iop_128HIto64, binop(Iop_MullS64, mkexpr(rA), mkexpr(rB))) ); break; case 0x9: // mulhdu (Multiply High DWord Unsigned, PPC64 p540) if (flag_OE != 0) { vex_printf("dis_int_arith(ppc)(mulhdu,flagOE)\n"); return False; } DIP("mulhdu%s r%u,r%u,r%u\n", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); assign( rD, unop(Iop_128HIto64, binop(Iop_MullU64, mkexpr(rA), mkexpr(rB))) ); break; case 0xE9: // mulld (Multiply Low DWord, PPC64 p543) DIP("mulld%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); assign( rD, binop(Iop_Mul64, mkexpr(rA), mkexpr(rB)) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_MULLD, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; case 0x1E9: // divd (Divide DWord, PPC64 p419) DIP("divd%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); assign( rD, binop(Iop_DivS64, mkexpr(rA), mkexpr(rB)) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_DIVW, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; /* Note: if (0x8000_0000_0000_0000 / -1) or (x / 0) => rD=undef, if(flag_rC) CR7=undef, if(flag_OE) XER_OV=1 => But _no_ exception raised. */ case 0x1C9: // divdu (Divide DWord Unsigned, PPC64 p420) DIP("divdu%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); assign( rD, binop(Iop_DivU64, mkexpr(rA), mkexpr(rB)) ); if (flag_OE) { set_XER_OV( ty, PPCG_FLAG_OP_DIVWU, mkexpr(rD), mkexpr(rA), mkexpr(rB) ); } break; /* Note: ditto comment divd, for (x / 0) */ case 0x18B: // divweu (Divide Word Extended Unsigned) { /* * If (RA) >= (RB), or if an attempt is made to perform the division * <anything> / 0 * then the contents of register RD are undefined as are (if Rc=1) the contents of * the LT, GT, and EQ bits of CR Field 0. In these cases, if OE=1 then OV is set * to 1. */ IRTemp res = newTemp(Ity_I32); IRExpr * dividend, * divisor; DIP("divweu%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); if (mode64) { dividend = unop( Iop_64to32, mkexpr( rA ) ); divisor = unop( Iop_64to32, mkexpr( rB ) ); assign( res, binop( Iop_DivU32E, dividend, divisor ) ); assign( rD, binop( Iop_32HLto64, mkU32( 0 ), mkexpr( res ) ) ); } else { dividend = mkexpr( rA ); divisor = mkexpr( rB ); assign( res, binop( Iop_DivU32E, dividend, divisor ) ); assign( rD, mkexpr( res) ); } if (flag_OE) { set_XER_OV_32( PPCG_FLAG_OP_DIVWEU, mkexpr(res), dividend, divisor ); } break; } case 0x1AB: // divwe (Divide Word Extended) { /* * If the quotient cannot be represented in 32 bits, or if an * attempt is made to perform the division * <anything> / 0 * then the contents of register RD are undefined as are (if * Rc=1) the contents of the LT, GT, and EQ bits of CR * Field 0. In these cases, if OE=1 then OV is set to 1. */ IRTemp res = newTemp(Ity_I32); IRExpr * dividend, * divisor; DIP("divwe%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); if (mode64) { dividend = unop( Iop_64to32, mkexpr( rA ) ); divisor = unop( Iop_64to32, mkexpr( rB ) ); assign( res, binop( Iop_DivS32E, dividend, divisor ) ); assign( rD, binop( Iop_32HLto64, mkU32( 0 ), mkexpr( res ) ) ); } else { dividend = mkexpr( rA ); divisor = mkexpr( rB ); assign( res, binop( Iop_DivS32E, dividend, divisor ) ); assign( rD, mkexpr( res) ); } if (flag_OE) { set_XER_OV_32( PPCG_FLAG_OP_DIVWE, mkexpr(res), dividend, divisor ); } break; } case 0x1A9: // divde (Divide Doubleword Extended) /* * If the quotient cannot be represented in 64 bits, or if an * attempt is made to perform the division * <anything> / 0 * then the contents of register RD are undefined as are (if * Rc=1) the contents of the LT, GT, and EQ bits of CR * Field 0. In these cases, if OE=1 then OV is set to 1. */ DIP("divde%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); assign( rD, binop(Iop_DivS64E, mkexpr(rA), mkexpr(rB)) ); if (flag_OE) { set_XER_OV_64( PPCG_FLAG_OP_DIVDE, mkexpr( rD ), mkexpr( rA ), mkexpr( rB ) ); } break; case 0x189: // divdeuo (Divide Doubleword Extended Unsigned) // Same CR and OV rules as given for divweu above DIP("divdeu%s%s r%u,r%u,r%u\n", flag_OE ? "o" : "", flag_rC ? ".":"", rD_addr, rA_addr, rB_addr); assign( rD, binop(Iop_DivU64E, mkexpr(rA), mkexpr(rB)) ); if (flag_OE) { set_XER_OV_64( PPCG_FLAG_OP_DIVDEU, mkexpr( rD ), mkexpr( rA ), mkexpr( rB ) ); } break; default: vex_printf("dis_int_arith(ppc)(opc2)\n"); return False; } break; default: vex_printf("dis_int_arith(ppc)(opc1)\n"); return False; } putIReg( rD_addr, mkexpr(rD) ); if (do_rc && flag_rC) { set_CR0( mkexpr(rD) ); } return True; } /* Integer Compare Instructions */ static Bool dis_int_cmp ( UInt theInstr ) { /* D-Form, X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); UChar b22 = toUChar( IFIELD( theInstr, 22, 1 ) ); UChar flag_L = toUChar( IFIELD( theInstr, 21, 1 ) ); UChar rA_addr = ifieldRegA(theInstr); UInt uimm16 = ifieldUIMM16(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRExpr *a = getIReg(rA_addr); IRExpr *b; if (!mode64 && flag_L==1) { // L==1 invalid for 32 bit. vex_printf("dis_int_cmp(ppc)(flag_L)\n"); return False; } if (b22 != 0) { vex_printf("dis_int_cmp(ppc)(b22)\n"); return False; } switch (opc1) { case 0x0B: // cmpi (Compare Immediate, PPC32 p368) DIP("cmpi cr%u,%u,r%u,%d\n", crfD, flag_L, rA_addr, (Int)extend_s_16to32(uimm16)); b = mkSzExtendS16( ty, uimm16 ); if (flag_L == 1) { putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b))); } else { a = mkNarrowTo32( ty, a ); b = mkNarrowTo32( ty, b ); putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32S, a, b))); } putCR0( crfD, getXER_SO() ); break; case 0x0A: // cmpli (Compare Logical Immediate, PPC32 p370) DIP("cmpli cr%u,%u,r%u,0x%x\n", crfD, flag_L, rA_addr, uimm16); b = mkSzImm( ty, uimm16 ); if (flag_L == 1) { putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b))); } else { a = mkNarrowTo32( ty, a ); b = mkNarrowTo32( ty, b ); putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b))); } putCR0( crfD, getXER_SO() ); break; /* X Form */ case 0x1F: if (b0 != 0) { vex_printf("dis_int_cmp(ppc)(0x1F,b0)\n"); return False; } b = getIReg(rB_addr); switch (opc2) { case 0x000: // cmp (Compare, PPC32 p367) DIP("cmp cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr); /* Comparing a reg with itself produces a result which doesn't depend on the contents of the reg. Therefore remove the false dependency, which has been known to cause memcheck to produce false errors. */ if (rA_addr == rB_addr) a = b = typeOfIRExpr(irsb->tyenv,a) == Ity_I64 ? mkU64(0) : mkU32(0); if (flag_L == 1) { putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64S, a, b))); } else { a = mkNarrowTo32( ty, a ); b = mkNarrowTo32( ty, b ); putCR321(crfD, unop(Iop_32to8,binop(Iop_CmpORD32S, a, b))); } putCR0( crfD, getXER_SO() ); break; case 0x020: // cmpl (Compare Logical, PPC32 p369) DIP("cmpl cr%u,%u,r%u,r%u\n", crfD, flag_L, rA_addr, rB_addr); /* Comparing a reg with itself produces a result which doesn't depend on the contents of the reg. Therefore remove the false dependency, which has been known to cause memcheck to produce false errors. */ if (rA_addr == rB_addr) a = b = typeOfIRExpr(irsb->tyenv,a) == Ity_I64 ? mkU64(0) : mkU32(0); if (flag_L == 1) { putCR321(crfD, unop(Iop_64to8, binop(Iop_CmpORD64U, a, b))); } else { a = mkNarrowTo32( ty, a ); b = mkNarrowTo32( ty, b ); putCR321(crfD, unop(Iop_32to8, binop(Iop_CmpORD32U, a, b))); } putCR0( crfD, getXER_SO() ); break; default: vex_printf("dis_int_cmp(ppc)(opc2)\n"); return False; } break; default: vex_printf("dis_int_cmp(ppc)(opc1)\n"); return False; } return True; } /* Integer Logical Instructions */ static Bool dis_int_logic ( UInt theInstr ) { /* D-Form, X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar rS_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UInt uimm16 = ifieldUIMM16(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar flag_rC = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp rS = newTemp(ty); IRTemp rA = newTemp(ty); IRTemp rB = newTemp(ty); IRExpr* irx; Bool do_rc = False; assign( rS, getIReg(rS_addr) ); assign( rB, getIReg(rB_addr) ); switch (opc1) { case 0x1C: // andi. (AND Immediate, PPC32 p358) DIP("andi. r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16); assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS), mkSzImm(ty, uimm16)) ); do_rc = True; // Always record to CR flag_rC = 1; break; case 0x1D: // andis. (AND Immediate Shifted, PPC32 p359) DIP("andis r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16); assign( rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS), mkSzImm(ty, uimm16 << 16)) ); do_rc = True; // Always record to CR flag_rC = 1; break; case 0x18: // ori (OR Immediate, PPC32 p497) DIP("ori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16); assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS), mkSzImm(ty, uimm16)) ); break; case 0x19: // oris (OR Immediate Shifted, PPC32 p498) DIP("oris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16); assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS), mkSzImm(ty, uimm16 << 16)) ); break; case 0x1A: // xori (XOR Immediate, PPC32 p550) DIP("xori r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16); assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS), mkSzImm(ty, uimm16)) ); break; case 0x1B: // xoris (XOR Immediate Shifted, PPC32 p551) DIP("xoris r%u,r%u,0x%x\n", rA_addr, rS_addr, uimm16); assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS), mkSzImm(ty, uimm16 << 16)) ); break; /* X Form */ case 0x1F: do_rc = True; // All below record to CR, except for where we return at case end. switch (opc2) { case 0x01C: // and (AND, PPC32 p356) DIP("and%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); assign(rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS), mkexpr(rB))); break; case 0x03C: // andc (AND with Complement, PPC32 p357) DIP("andc%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); assign(rA, binop( mkSzOp(ty, Iop_And8), mkexpr(rS), unop( mkSzOp(ty, Iop_Not8), mkexpr(rB)))); break; case 0x01A: { // cntlzw (Count Leading Zeros Word, PPC32 p371) IRExpr* lo32; if (rB_addr!=0) { vex_printf("dis_int_logic(ppc)(cntlzw,rB_addr)\n"); return False; } DIP("cntlzw%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr); // mode64: count in low word only lo32 = mode64 ? unop(Iop_64to32, mkexpr(rS)) : mkexpr(rS); // Iop_Clz32 undefined for arg==0, so deal with that case: irx = binop(Iop_CmpNE32, lo32, mkU32(0)); assign(rA, mkWidenFrom32(ty, IRExpr_ITE( irx, unop(Iop_Clz32, lo32), mkU32(32)), False)); // TODO: alternatively: assign(rA, verbose_Clz32(rS)); break; } case 0x11C: // eqv (Equivalent, PPC32 p396) DIP("eqv%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); assign( rA, unop( mkSzOp(ty, Iop_Not8), binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS), mkexpr(rB))) ); break; case 0x3BA: // extsb (Extend Sign Byte, PPC32 p397 if (rB_addr!=0) { vex_printf("dis_int_logic(ppc)(extsb,rB_addr)\n"); return False; } DIP("extsb%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr); if (mode64) assign( rA, unop(Iop_8Sto64, unop(Iop_64to8, mkexpr(rS))) ); else assign( rA, unop(Iop_8Sto32, unop(Iop_32to8, mkexpr(rS))) ); break; case 0x39A: // extsh (Extend Sign Half Word, PPC32 p398) if (rB_addr!=0) { vex_printf("dis_int_logic(ppc)(extsh,rB_addr)\n"); return False; } DIP("extsh%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr); if (mode64) assign( rA, unop(Iop_16Sto64, unop(Iop_64to16, mkexpr(rS))) ); else assign( rA, unop(Iop_16Sto32, unop(Iop_32to16, mkexpr(rS))) ); break; case 0x1DC: // nand (NAND, PPC32 p492) DIP("nand%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); assign( rA, unop( mkSzOp(ty, Iop_Not8), binop( mkSzOp(ty, Iop_And8), mkexpr(rS), mkexpr(rB))) ); break; case 0x07C: // nor (NOR, PPC32 p494) DIP("nor%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); assign( rA, unop( mkSzOp(ty, Iop_Not8), binop( mkSzOp(ty, Iop_Or8), mkexpr(rS), mkexpr(rB))) ); break; case 0x1BC: // or (OR, PPC32 p495) if ((!flag_rC) && rS_addr == rB_addr) { DIP("mr r%u,r%u\n", rA_addr, rS_addr); assign( rA, mkexpr(rS) ); } else { DIP("or%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS), mkexpr(rB)) ); } break; case 0x19C: // orc (OR with Complement, PPC32 p496) DIP("orc%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); assign( rA, binop( mkSzOp(ty, Iop_Or8), mkexpr(rS), unop(mkSzOp(ty, Iop_Not8), mkexpr(rB)))); break; case 0x13C: // xor (XOR, PPC32 p549) DIP("xor%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); assign( rA, binop( mkSzOp(ty, Iop_Xor8), mkexpr(rS), mkexpr(rB)) ); break; /* 64bit Integer Logical Instructions */ case 0x3DA: // extsw (Extend Sign Word, PPC64 p430) if (rB_addr!=0) { vex_printf("dis_int_logic(ppc)(extsw,rB_addr)\n"); return False; } DIP("extsw%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr); assign(rA, unop(Iop_32Sto64, unop(Iop_64to32, mkexpr(rS)))); break; case 0x03A: // cntlzd (Count Leading Zeros DWord, PPC64 p401) if (rB_addr!=0) { vex_printf("dis_int_logic(ppc)(cntlzd,rB_addr)\n"); return False; } DIP("cntlzd%s r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr); // Iop_Clz64 undefined for arg==0, so deal with that case: irx = binop(Iop_CmpNE64, mkexpr(rS), mkU64(0)); assign(rA, IRExpr_ITE( irx, unop(Iop_Clz64, mkexpr(rS)), mkU64(64) )); // TODO: alternatively: assign(rA, verbose_Clz64(rS)); break; case 0x1FC: // cmpb (Power6: compare bytes) DIP("cmpb r%u,r%u,r%u\n", rA_addr, rS_addr, rB_addr); if (mode64) assign( rA, unop( Iop_V128to64, binop( Iop_CmpEQ8x16, binop( Iop_64HLtoV128, mkU64(0), mkexpr(rS) ), binop( Iop_64HLtoV128, mkU64(0), mkexpr(rB) ) )) ); else assign( rA, unop( Iop_V128to32, binop( Iop_CmpEQ8x16, unop( Iop_32UtoV128, mkexpr(rS) ), unop( Iop_32UtoV128, mkexpr(rB) ) )) ); break; case 0x2DF: { // mftgpr (move floating-point to general purpose register) IRTemp frB = newTemp(Ity_F64); DIP("mftgpr r%u,fr%u\n", rS_addr, rB_addr); assign( frB, getFReg(rB_addr)); // always F64 if (mode64) assign( rA, unop( Iop_ReinterpF64asI64, mkexpr(frB)) ); else assign( rA, unop( Iop_64to32, unop( Iop_ReinterpF64asI64, mkexpr(frB))) ); putIReg( rS_addr, mkexpr(rA)); return True; } case 0x25F: { // mffgpr (move floating-point from general purpose register) IRTemp frA = newTemp(Ity_F64); DIP("mffgpr fr%u,r%u\n", rS_addr, rB_addr); if (mode64) assign( frA, unop( Iop_ReinterpI64asF64, mkexpr(rB)) ); else assign( frA, unop( Iop_ReinterpI64asF64, unop( Iop_32Uto64, mkexpr(rB))) ); putFReg( rS_addr, mkexpr(frA)); return True; } case 0x1FA: // popcntd (population count doubleword { DIP("popcntd r%u,r%u\n", rA_addr, rS_addr); IRTemp result = gen_POPCOUNT(ty, rS, DWORD); putIReg( rA_addr, mkexpr(result) ); return True; } case 0x17A: // popcntw (Population Count Words) { DIP("popcntw r%u,r%u\n", rA_addr, rS_addr); if (mode64) { IRTemp resultHi, resultLo; IRTemp argLo = newTemp(Ity_I32); IRTemp argHi = newTemp(Ity_I32); assign(argLo, unop(Iop_64to32, mkexpr(rS))); assign(argHi, unop(Iop_64HIto32, mkexpr(rS))); resultLo = gen_POPCOUNT(Ity_I32, argLo, WORD); resultHi = gen_POPCOUNT(Ity_I32, argHi, WORD); putIReg( rA_addr, binop(Iop_32HLto64, mkexpr(resultHi), mkexpr(resultLo))); } else { IRTemp result = gen_POPCOUNT(ty, rS, WORD); putIReg( rA_addr, mkexpr(result) ); } return True; } case 0x7A: // popcntb (Population Count Byte) { DIP("popcntb r%u,r%u\n", rA_addr, rS_addr); if (mode64) { IRTemp resultHi, resultLo; IRTemp argLo = newTemp(Ity_I32); IRTemp argHi = newTemp(Ity_I32); assign(argLo, unop(Iop_64to32, mkexpr(rS))); assign(argHi, unop(Iop_64HIto32, mkexpr(rS))); resultLo = gen_POPCOUNT(Ity_I32, argLo, BYTE); resultHi = gen_POPCOUNT(Ity_I32, argHi, BYTE); putIReg( rA_addr, binop(Iop_32HLto64, mkexpr(resultHi), mkexpr(resultLo))); } else { IRTemp result = gen_POPCOUNT(ty, rS, BYTE); putIReg( rA_addr, mkexpr(result) ); } return True; } case 0x0FC: // bpermd (Bit Permute Doubleword) { /* This is a lot of rigmarole to emulate bpermd like this, as it * could be done much faster by implementing a call to the native * instruction. However, where possible I want to avoid using new * native instructions so that we can use valgrind to emulate those * instructions on older PPC64 hardware. */ #define BPERMD_IDX_MASK 0x00000000000000FFULL #define BPERMD_BIT_MASK 0x8000000000000000ULL int i; IRExpr * rS_expr = mkexpr(rS); IRExpr * res = binop(Iop_And64, mkU64(0), mkU64(0)); DIP("bpermd r%u,r%u,r%u\n", rA_addr, rS_addr, rB_addr); for (i = 0; i < 8; i++) { IRTemp idx_tmp = newTemp( Ity_I64 ); IRTemp perm_bit = newTemp( Ity_I64 ); IRTemp idx = newTemp( Ity_I8 ); IRTemp idx_LT64 = newTemp( Ity_I1 ); IRTemp idx_LT64_ity64 = newTemp( Ity_I64 ); assign( idx_tmp, binop( Iop_And64, mkU64( BPERMD_IDX_MASK ), rS_expr ) ); assign( idx_LT64, binop( Iop_CmpLT64U, mkexpr( idx_tmp ), mkU64( 64 ) ) ); assign( idx, binop( Iop_And8, unop( Iop_1Sto8, mkexpr(idx_LT64) ), unop( Iop_64to8, mkexpr( idx_tmp ) ) ) ); /* If idx_LT64 == 0, we must force the perm bit to '0'. Below, we se idx * to determine which bit of rB to use for the perm bit, and then we shift * that bit to the MSB position. We AND that with a 64-bit-ized idx_LT64 * to set the final perm bit. */ assign( idx_LT64_ity64, unop( Iop_32Uto64, unop( Iop_1Uto32, mkexpr(idx_LT64 ) ) ) ); assign( perm_bit, binop( Iop_And64, mkexpr( idx_LT64_ity64 ), binop( Iop_Shr64, binop( Iop_And64, mkU64( BPERMD_BIT_MASK ), binop( Iop_Shl64, mkexpr( rB ), mkexpr( idx ) ) ), mkU8( 63 ) ) ) ); res = binop( Iop_Or64, res, binop( Iop_Shl64, mkexpr( perm_bit ), mkU8( i ) ) ); rS_expr = binop( Iop_Shr64, rS_expr, mkU8( 8 ) ); } putIReg(rA_addr, res); return True; } default: vex_printf("dis_int_logic(ppc)(opc2)\n"); return False; } break; default: vex_printf("dis_int_logic(ppc)(opc1)\n"); return False; } putIReg( rA_addr, mkexpr(rA) ); if (do_rc && flag_rC) { set_CR0( mkexpr(rA) ); } return True; } /* Integer Parity Instructions */ static Bool dis_int_parity ( UInt theInstr ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar rS_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp rS = newTemp(ty); IRTemp rA = newTemp(ty); IRTemp iTot1 = newTemp(Ity_I32); IRTemp iTot2 = newTemp(Ity_I32); IRTemp iTot3 = newTemp(Ity_I32); IRTemp iTot4 = newTemp(Ity_I32); IRTemp iTot5 = newTemp(Ity_I32); IRTemp iTot6 = newTemp(Ity_I32); IRTemp iTot7 = newTemp(Ity_I32); IRTemp iTot8 = newTemp(Ity_I32); IRTemp rS1 = newTemp(ty); IRTemp rS2 = newTemp(ty); IRTemp rS3 = newTemp(ty); IRTemp rS4 = newTemp(ty); IRTemp rS5 = newTemp(ty); IRTemp rS6 = newTemp(ty); IRTemp rS7 = newTemp(ty); IRTemp iHi = newTemp(Ity_I32); IRTemp iLo = newTemp(Ity_I32); IROp to_bit = (mode64 ? Iop_64to1 : Iop_32to1); IROp shr_op = (mode64 ? Iop_Shr64 : Iop_Shr32); if (opc1 != 0x1f || rB_addr || b0) { vex_printf("dis_int_parity(ppc)(0x1F,opc1:rB|b0)\n"); return False; } assign( rS, getIReg(rS_addr) ); switch (opc2) { case 0xba: // prtyd (Parity Doubleword, ISA 2.05 p320) DIP("prtyd r%u,r%u\n", rA_addr, rS_addr); assign( iTot1, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS))) ); assign( rS1, binop(shr_op, mkexpr(rS), mkU8(8)) ); assign( iTot2, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS1))), mkexpr(iTot1)) ); assign( rS2, binop(shr_op, mkexpr(rS1), mkU8(8)) ); assign( iTot3, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS2))), mkexpr(iTot2)) ); assign( rS3, binop(shr_op, mkexpr(rS2), mkU8(8)) ); assign( iTot4, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS3))), mkexpr(iTot3)) ); if (mode64) { assign( rS4, binop(shr_op, mkexpr(rS3), mkU8(8)) ); assign( iTot5, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS4))), mkexpr(iTot4)) ); assign( rS5, binop(shr_op, mkexpr(rS4), mkU8(8)) ); assign( iTot6, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS5))), mkexpr(iTot5)) ); assign( rS6, binop(shr_op, mkexpr(rS5), mkU8(8)) ); assign( iTot7, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS6))), mkexpr(iTot6)) ); assign( rS7, binop(shr_op, mkexpr(rS6), mkU8(8)) ); assign( iTot8, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS7))), mkexpr(iTot7)) ); assign( rA, unop(Iop_32Uto64, binop(Iop_And32, mkexpr(iTot8), mkU32(1))) ); } else assign( rA, mkexpr(iTot4) ); break; case 0x9a: // prtyw (Parity Word, ISA 2.05 p320) assign( iTot1, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS))) ); assign( rS1, binop(shr_op, mkexpr(rS), mkU8(8)) ); assign( iTot2, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS1))), mkexpr(iTot1)) ); assign( rS2, binop(shr_op, mkexpr(rS1), mkU8(8)) ); assign( iTot3, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS2))), mkexpr(iTot2)) ); assign( rS3, binop(shr_op, mkexpr(rS2), mkU8(8)) ); assign( iTot4, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS3))), mkexpr(iTot3)) ); assign( iLo, unop(Iop_1Uto32, unop(Iop_32to1, mkexpr(iTot4) )) ); if (mode64) { assign( rS4, binop(shr_op, mkexpr(rS3), mkU8(8)) ); assign( iTot5, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS4))) ); assign( rS5, binop(shr_op, mkexpr(rS4), mkU8(8)) ); assign( iTot6, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS5))), mkexpr(iTot5)) ); assign( rS6, binop(shr_op, mkexpr(rS5), mkU8(8)) ); assign( iTot7, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS6))), mkexpr(iTot6)) ); assign( rS7, binop(shr_op, mkexpr(rS6), mkU8(8))); assign( iTot8, binop(Iop_Add32, unop(Iop_1Uto32, unop(to_bit, mkexpr(rS7))), mkexpr(iTot7)) ); assign( iHi, binop(Iop_And32, mkU32(1), mkexpr(iTot8)) ), assign( rA, binop(Iop_32HLto64, mkexpr(iHi), mkexpr(iLo)) ); } else assign( rA, binop(Iop_Or32, mkU32(0), mkexpr(iLo)) ); break; default: vex_printf("dis_int_parity(ppc)(opc2)\n"); return False; } putIReg( rA_addr, mkexpr(rA) ); return True; } /* Integer Rotate Instructions */ static Bool dis_int_rot ( UInt theInstr ) { /* M-Form, MDS-Form */ UChar opc1 = ifieldOPC(theInstr); UChar rS_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UChar sh_imm = rB_addr; UChar MaskBeg = toUChar( IFIELD( theInstr, 6, 5 ) ); UChar MaskEnd = toUChar( IFIELD( theInstr, 1, 5 ) ); UChar msk_imm = toUChar( IFIELD( theInstr, 5, 6 ) ); UChar opc2 = toUChar( IFIELD( theInstr, 2, 3 ) ); UChar b1 = ifieldBIT1(theInstr); UChar flag_rC = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp rS = newTemp(ty); IRTemp rA = newTemp(ty); IRTemp rB = newTemp(ty); IRTemp rot = newTemp(ty); IRExpr *r; UInt mask32; ULong mask64; assign( rS, getIReg(rS_addr) ); assign( rB, getIReg(rB_addr) ); switch (opc1) { case 0x14: { // rlwimi (Rotate Left Word Imm then Mask Insert, PPC32 p500) DIP("rlwimi%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd); if (mode64) { // tmp32 = (ROTL(rS_Lo32, Imm) // rA = ((tmp32 || tmp32) & mask64) | (rA & ~mask64) mask64 = MASK64(31-MaskEnd, 31-MaskBeg); r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) ); r = unop(Iop_32Uto64, r); assign( rot, binop(Iop_Or64, r, binop(Iop_Shl64, r, mkU8(32))) ); assign( rA, binop(Iop_Or64, binop(Iop_And64, mkexpr(rot), mkU64(mask64)), binop(Iop_And64, getIReg(rA_addr), mkU64(~mask64))) ); } else { // rA = (ROTL(rS, Imm) & mask) | (rA & ~mask); mask32 = MASK32(31-MaskEnd, 31-MaskBeg); r = ROTL(mkexpr(rS), mkU8(sh_imm)); assign( rA, binop(Iop_Or32, binop(Iop_And32, mkU32(mask32), r), binop(Iop_And32, getIReg(rA_addr), mkU32(~mask32))) ); } break; } case 0x15: { // rlwinm (Rotate Left Word Imm then AND with Mask, PPC32 p501) vassert(MaskBeg < 32); vassert(MaskEnd < 32); vassert(sh_imm < 32); if (mode64) { IRTemp rTmp = newTemp(Ity_I64); mask64 = MASK64(31-MaskEnd, 31-MaskBeg); DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd); // tmp32 = (ROTL(rS_Lo32, Imm) // rA = ((tmp32 || tmp32) & mask64) r = ROTL( unop(Iop_64to32, mkexpr(rS) ), mkU8(sh_imm) ); r = unop(Iop_32Uto64, r); assign( rTmp, r ); r = NULL; assign( rot, binop(Iop_Or64, mkexpr(rTmp), binop(Iop_Shl64, mkexpr(rTmp), mkU8(32))) ); assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) ); } else { if (MaskBeg == 0 && sh_imm+MaskEnd == 31) { /* Special-case the ,n,0,31-n form as that is just n-bit shift left, PPC32 p501 */ DIP("slwi%s r%u,r%u,%d\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm); assign( rA, binop(Iop_Shl32, mkexpr(rS), mkU8(sh_imm)) ); } else if (MaskEnd == 31 && sh_imm+MaskBeg == 32) { /* Special-case the ,32-n,n,31 form as that is just n-bit unsigned shift right, PPC32 p501 */ DIP("srwi%s r%u,r%u,%d\n", flag_rC ? ".":"", rA_addr, rS_addr, MaskBeg); assign( rA, binop(Iop_Shr32, mkexpr(rS), mkU8(MaskBeg)) ); } else { /* General case. */ mask32 = MASK32(31-MaskEnd, 31-MaskBeg); DIP("rlwinm%s r%u,r%u,%d,%d,%d\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm, MaskBeg, MaskEnd); // rA = ROTL(rS, Imm) & mask assign( rA, binop(Iop_And32, ROTL(mkexpr(rS), mkU8(sh_imm)), mkU32(mask32)) ); } } break; } case 0x17: { // rlwnm (Rotate Left Word then AND with Mask, PPC32 p503 DIP("rlwnm%s r%u,r%u,r%u,%d,%d\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr, MaskBeg, MaskEnd); if (mode64) { mask64 = MASK64(31-MaskEnd, 31-MaskBeg); /* weird insn alert! tmp32 = (ROTL(rS_Lo32, rB[0-4]) rA = ((tmp32 || tmp32) & mask64) */ // note, ROTL does the masking, so we don't do it here r = ROTL( unop(Iop_64to32, mkexpr(rS)), unop(Iop_64to8, mkexpr(rB)) ); r = unop(Iop_32Uto64, r); assign(rot, binop(Iop_Or64, r, binop(Iop_Shl64, r, mkU8(32)))); assign( rA, binop(Iop_And64, mkexpr(rot), mkU64(mask64)) ); } else { mask32 = MASK32(31-MaskEnd, 31-MaskBeg); // rA = ROTL(rS, rB[0-4]) & mask // note, ROTL does the masking, so we don't do it here assign( rA, binop(Iop_And32, ROTL(mkexpr(rS), unop(Iop_32to8, mkexpr(rB))), mkU32(mask32)) ); } break; } /* 64bit Integer Rotates */ case 0x1E: { msk_imm = ((msk_imm & 1) << 5) | (msk_imm >> 1); sh_imm |= b1 << 5; vassert( msk_imm < 64 ); vassert( sh_imm < 64 ); switch (opc2) { case 0x4: { /* r = ROTL64( rS, rB_lo6) */ r = ROTL( mkexpr(rS), unop(Iop_64to8, mkexpr(rB)) ); if (b1 == 0) { // rldcl (Rotl DWord, Clear Left, PPC64 p555) DIP("rldcl%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr, msk_imm); // note, ROTL does the masking, so we don't do it here mask64 = MASK64(0, 63-msk_imm); assign( rA, binop(Iop_And64, r, mkU64(mask64)) ); break; } else { // rldcr (Rotl DWord, Clear Right, PPC64 p556) DIP("rldcr%s r%u,r%u,r%u,%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr, msk_imm); mask64 = MASK64(63-msk_imm, 63); assign( rA, binop(Iop_And64, r, mkU64(mask64)) ); break; } break; } case 0x2: // rldic (Rotl DWord Imm, Clear, PPC64 p557) DIP("rldic%s r%u,r%u,%u,%u\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm, msk_imm); r = ROTL(mkexpr(rS), mkU8(sh_imm)); mask64 = MASK64(sh_imm, 63-msk_imm); assign( rA, binop(Iop_And64, r, mkU64(mask64)) ); break; // later: deal with special case: (msk_imm==0) => SHL(sh_imm) /* Hmm... looks like this'll do the job more simply: r = SHL(rS, sh_imm) m = ~(1 << (63-msk_imm)) assign(rA, r & m); */ case 0x0: // rldicl (Rotl DWord Imm, Clear Left, PPC64 p558) if (mode64 && sh_imm + msk_imm == 64 && msk_imm >= 1 && msk_imm <= 63) { /* special-case the ,64-n,n form as that is just unsigned shift-right by n */ DIP("srdi%s r%u,r%u,%u\n", flag_rC ? ".":"", rA_addr, rS_addr, msk_imm); assign( rA, binop(Iop_Shr64, mkexpr(rS), mkU8(msk_imm)) ); } else { DIP("rldicl%s r%u,r%u,%u,%u\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm, msk_imm); r = ROTL(mkexpr(rS), mkU8(sh_imm)); mask64 = MASK64(0, 63-msk_imm); assign( rA, binop(Iop_And64, r, mkU64(mask64)) ); } break; case 0x1: // rldicr (Rotl DWord Imm, Clear Right, PPC64 p559) if (mode64 && sh_imm + msk_imm == 63 && sh_imm >= 1 && sh_imm <= 63) { /* special-case the ,n,63-n form as that is just shift-left by n */ DIP("sldi%s r%u,r%u,%u\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm); assign( rA, binop(Iop_Shl64, mkexpr(rS), mkU8(sh_imm)) ); } else { DIP("rldicr%s r%u,r%u,%u,%u\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm, msk_imm); r = ROTL(mkexpr(rS), mkU8(sh_imm)); mask64 = MASK64(63-msk_imm, 63); assign( rA, binop(Iop_And64, r, mkU64(mask64)) ); } break; case 0x3: { // rldimi (Rotl DWord Imm, Mask Insert, PPC64 p560) IRTemp rA_orig = newTemp(ty); DIP("rldimi%s r%u,r%u,%u,%u\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm, msk_imm); r = ROTL(mkexpr(rS), mkU8(sh_imm)); mask64 = MASK64(sh_imm, 63-msk_imm); assign( rA_orig, getIReg(rA_addr) ); assign( rA, binop(Iop_Or64, binop(Iop_And64, mkU64(mask64), r), binop(Iop_And64, mkU64(~mask64), mkexpr(rA_orig))) ); break; } default: vex_printf("dis_int_rot(ppc)(opc2)\n"); return False; } break; } default: vex_printf("dis_int_rot(ppc)(opc1)\n"); return False; } putIReg( rA_addr, mkexpr(rA) ); if (flag_rC) { set_CR0( mkexpr(rA) ); } return True; } /* Integer Load Instructions */ static Bool dis_int_load ( UInt theInstr ) { /* D-Form, X-Form, DS-Form */ UChar opc1 = ifieldOPC(theInstr); UChar rD_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UInt uimm16 = ifieldUIMM16(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b1 = ifieldBIT1(theInstr); UChar b0 = ifieldBIT0(theInstr); Int simm16 = extend_s_16to32(uimm16); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA = newTemp(ty); IRExpr* val; switch (opc1) { case 0x1F: // register offset assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) ); break; case 0x38: // immediate offset: 64bit: lq: maskoff // lowest 4 bits of immediate before forming EA simm16 = simm16 & 0xFFFFFFF0; assign( EA, ea_rAor0_simm( rA_addr, simm16 ) ); break; case 0x3A: // immediate offset: 64bit: ld/ldu/lwa: mask off // lowest 2 bits of immediate before forming EA simm16 = simm16 & 0xFFFFFFFC; assign( EA, ea_rAor0_simm( rA_addr, simm16 ) ); break; default: // immediate offset assign( EA, ea_rAor0_simm( rA_addr, simm16 ) ); break; } switch (opc1) { case 0x22: // lbz (Load B & Zero, PPC32 p433) DIP("lbz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr); val = load(Ity_I8, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom8(ty, val, False) ); break; case 0x23: // lbzu (Load B & Zero, Update, PPC32 p434) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(lbzu,rA_addr|rD_addr)\n"); return False; } DIP("lbzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr); val = load(Ity_I8, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom8(ty, val, False) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x2A: // lha (Load HW Alg, PPC32 p445) DIP("lha r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr); val = load(Ity_I16, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom16(ty, val, True) ); break; case 0x2B: // lhau (Load HW Alg, Update, PPC32 p446) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(lhau,rA_addr|rD_addr)\n"); return False; } DIP("lhau r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr); val = load(Ity_I16, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom16(ty, val, True) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x28: // lhz (Load HW & Zero, PPC32 p450) DIP("lhz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr); val = load(Ity_I16, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom16(ty, val, False) ); break; case 0x29: // lhzu (Load HW & and Zero, Update, PPC32 p451) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(lhzu,rA_addr|rD_addr)\n"); return False; } DIP("lhzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr); val = load(Ity_I16, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom16(ty, val, False) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x20: // lwz (Load W & Zero, PPC32 p460) DIP("lwz r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr); val = load(Ity_I32, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom32(ty, val, False) ); break; case 0x21: // lwzu (Load W & Zero, Update, PPC32 p461)) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(lwzu,rA_addr|rD_addr)\n"); return False; } DIP("lwzu r%u,%d(r%u)\n", rD_addr, (Int)simm16, rA_addr); val = load(Ity_I32, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom32(ty, val, False) ); putIReg( rA_addr, mkexpr(EA) ); break; /* X Form */ case 0x1F: if (b0 != 0) { vex_printf("dis_int_load(ppc)(Ox1F,b0)\n"); return False; } switch (opc2) { case 0x077: // lbzux (Load B & Zero, Update Indexed, PPC32 p435) DIP("lbzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n"); return False; } val = load(Ity_I8, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom8(ty, val, False) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x057: // lbzx (Load B & Zero, Indexed, PPC32 p436) DIP("lbzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); val = load(Ity_I8, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom8(ty, val, False) ); break; case 0x177: // lhaux (Load HW Alg, Update Indexed, PPC32 p447) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(lhaux,rA_addr|rD_addr)\n"); return False; } DIP("lhaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); val = load(Ity_I16, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom16(ty, val, True) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x157: // lhax (Load HW Alg, Indexed, PPC32 p448) DIP("lhax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); val = load(Ity_I16, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom16(ty, val, True) ); break; case 0x137: // lhzux (Load HW & Zero, Update Indexed, PPC32 p452) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(lhzux,rA_addr|rD_addr)\n"); return False; } DIP("lhzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); val = load(Ity_I16, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom16(ty, val, False) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x117: // lhzx (Load HW & Zero, Indexed, PPC32 p453) DIP("lhzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); val = load(Ity_I16, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom16(ty, val, False) ); break; case 0x037: // lwzux (Load W & Zero, Update Indexed, PPC32 p462) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(lwzux,rA_addr|rD_addr)\n"); return False; } DIP("lwzux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); val = load(Ity_I32, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom32(ty, val, False) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x017: // lwzx (Load W & Zero, Indexed, PPC32 p463) DIP("lwzx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); val = load(Ity_I32, mkexpr(EA)); putIReg( rD_addr, mkWidenFrom32(ty, val, False) ); break; /* 64bit Loads */ case 0x035: // ldux (Load DWord, Update Indexed, PPC64 p475) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(ldux,rA_addr|rD_addr)\n"); return False; } DIP("ldux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); putIReg( rD_addr, load(Ity_I64, mkexpr(EA)) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x015: // ldx (Load DWord, Indexed, PPC64 p476) DIP("ldx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); putIReg( rD_addr, load(Ity_I64, mkexpr(EA)) ); break; case 0x175: // lwaux (Load W Alg, Update Indexed, PPC64 p501) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(lwaux,rA_addr|rD_addr)\n"); return False; } DIP("lwaux r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); putIReg( rD_addr, unop(Iop_32Sto64, load(Ity_I32, mkexpr(EA))) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x155: // lwax (Load W Alg, Indexed, PPC64 p502) DIP("lwax r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); putIReg( rD_addr, unop(Iop_32Sto64, load(Ity_I32, mkexpr(EA))) ); break; default: vex_printf("dis_int_load(ppc)(opc2)\n"); return False; } break; /* DS Form - 64bit Loads. In each case EA will have been formed with the lowest 2 bits masked off the immediate offset. */ case 0x3A: switch ((b1<<1) | b0) { case 0x0: // ld (Load DWord, PPC64 p472) DIP("ld r%u,%d(r%u)\n", rD_addr, simm16, rA_addr); putIReg( rD_addr, load(Ity_I64, mkexpr(EA)) ); break; case 0x1: // ldu (Load DWord, Update, PPC64 p474) if (rA_addr == 0 || rA_addr == rD_addr) { vex_printf("dis_int_load(ppc)(ldu,rA_addr|rD_addr)\n"); return False; } DIP("ldu r%u,%d(r%u)\n", rD_addr, simm16, rA_addr); putIReg( rD_addr, load(Ity_I64, mkexpr(EA)) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x2: // lwa (Load Word Alg, PPC64 p499) DIP("lwa r%u,%d(r%u)\n", rD_addr, simm16, rA_addr); putIReg( rD_addr, unop(Iop_32Sto64, load(Ity_I32, mkexpr(EA))) ); break; default: vex_printf("dis_int_load(ppc)(0x3A, opc2)\n"); return False; } break; case 0x38: { IRTemp high = newTemp(ty); IRTemp low = newTemp(ty); /* DQ Form - 128bit Loads. Lowest bits [1:0] are the PT field. */ DIP("lq r%u,%d(r%u)\n", rD_addr, simm16, rA_addr); /* NOTE: there are some changes to XER[41:42] that have not been * implemented. */ // trap if EA misaligned on 16 byte address if (mode64) { if (host_endness == VexEndnessBE) { assign(high, load(ty, mkexpr( EA ) ) ); assign(low, load(ty, binop( Iop_Add64, mkexpr( EA ), mkU64( 8 ) ) ) ); } else { assign(low, load(ty, mkexpr( EA ) ) ); assign(high, load(ty, binop( Iop_Add64, mkexpr( EA ), mkU64( 8 ) ) ) ); } } else { assign(high, load(ty, binop( Iop_Add32, mkexpr( EA ), mkU32( 4 ) ) ) ); assign(low, load(ty, binop( Iop_Add32, mkexpr( EA ), mkU32( 12 ) ) ) ); } gen_SIGBUS_if_misaligned( EA, 16 ); putIReg( rD_addr, mkexpr( high) ); putIReg( rD_addr+1, mkexpr( low) ); break; } default: vex_printf("dis_int_load(ppc)(opc1)\n"); return False; } return True; } /* Integer Store Instructions */ static Bool dis_int_store ( UInt theInstr, const VexAbiInfo* vbi ) { /* D-Form, X-Form, DS-Form */ UChar opc1 = ifieldOPC(theInstr); UInt rS_addr = ifieldRegDS(theInstr); UInt rA_addr = ifieldRegA(theInstr); UInt uimm16 = ifieldUIMM16(theInstr); UInt rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b1 = ifieldBIT1(theInstr); UChar b0 = ifieldBIT0(theInstr); Int simm16 = extend_s_16to32(uimm16); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp rS = newTemp(ty); IRTemp rB = newTemp(ty); IRTemp EA = newTemp(ty); assign( rB, getIReg(rB_addr) ); assign( rS, getIReg(rS_addr) ); switch (opc1) { case 0x1F: // register offset assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) ); break; case 0x3E: // immediate offset: 64bit: std/stdu/stq: mask off // lowest 2 bits of immediate before forming EA simm16 = simm16 & 0xFFFFFFFC; default: // immediate offset assign( EA, ea_rAor0_simm( rA_addr, simm16 ) ); break; } switch (opc1) { case 0x26: // stb (Store B, PPC32 p509) DIP("stb r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); store( mkexpr(EA), mkNarrowTo8(ty, mkexpr(rS)) ); break; case 0x27: // stbu (Store B, Update, PPC32 p510) if (rA_addr == 0 ) { vex_printf("dis_int_store(ppc)(stbu,rA_addr)\n"); return False; } DIP("stbu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); putIReg( rA_addr, mkexpr(EA) ); store( mkexpr(EA), mkNarrowTo8(ty, mkexpr(rS)) ); break; case 0x2C: // sth (Store HW, PPC32 p522) DIP("sth r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); store( mkexpr(EA), mkNarrowTo16(ty, mkexpr(rS)) ); break; case 0x2D: // sthu (Store HW, Update, PPC32 p524) if (rA_addr == 0) { vex_printf("dis_int_store(ppc)(sthu,rA_addr)\n"); return False; } DIP("sthu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); putIReg( rA_addr, mkexpr(EA) ); store( mkexpr(EA), mkNarrowTo16(ty, mkexpr(rS)) ); break; case 0x24: // stw (Store W, PPC32 p530) DIP("stw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); store( mkexpr(EA), mkNarrowTo32(ty, mkexpr(rS)) ); break; case 0x25: // stwu (Store W, Update, PPC32 p534) if (rA_addr == 0) { vex_printf("dis_int_store(ppc)(stwu,rA_addr)\n"); return False; } DIP("stwu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); putIReg( rA_addr, mkexpr(EA) ); store( mkexpr(EA), mkNarrowTo32(ty, mkexpr(rS)) ); break; /* X Form : all these use EA_indexed */ case 0x1F: if (b0 != 0) { vex_printf("dis_int_store(ppc)(0x1F,b0)\n"); return False; } switch (opc2) { case 0x0F7: // stbux (Store B, Update Indexed, PPC32 p511) if (rA_addr == 0) { vex_printf("dis_int_store(ppc)(stbux,rA_addr)\n"); return False; } DIP("stbux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); putIReg( rA_addr, mkexpr(EA) ); store( mkexpr(EA), mkNarrowTo8(ty, mkexpr(rS)) ); break; case 0x0D7: // stbx (Store B Indexed, PPC32 p512) DIP("stbx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); store( mkexpr(EA), mkNarrowTo8(ty, mkexpr(rS)) ); break; case 0x1B7: // sthux (Store HW, Update Indexed, PPC32 p525) if (rA_addr == 0) { vex_printf("dis_int_store(ppc)(sthux,rA_addr)\n"); return False; } DIP("sthux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); putIReg( rA_addr, mkexpr(EA) ); store( mkexpr(EA), mkNarrowTo16(ty, mkexpr(rS)) ); break; case 0x197: // sthx (Store HW Indexed, PPC32 p526) DIP("sthx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); store( mkexpr(EA), mkNarrowTo16(ty, mkexpr(rS)) ); break; case 0x0B7: // stwux (Store W, Update Indexed, PPC32 p535) if (rA_addr == 0) { vex_printf("dis_int_store(ppc)(stwux,rA_addr)\n"); return False; } DIP("stwux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); putIReg( rA_addr, mkexpr(EA) ); store( mkexpr(EA), mkNarrowTo32(ty, mkexpr(rS)) ); break; case 0x097: // stwx (Store W Indexed, PPC32 p536) DIP("stwx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); store( mkexpr(EA), mkNarrowTo32(ty, mkexpr(rS)) ); break; /* 64bit Stores */ case 0x0B5: // stdux (Store DWord, Update Indexed, PPC64 p584) if (rA_addr == 0) { vex_printf("dis_int_store(ppc)(stdux,rA_addr)\n"); return False; } DIP("stdux r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); putIReg( rA_addr, mkexpr(EA) ); store( mkexpr(EA), mkexpr(rS) ); break; case 0x095: // stdx (Store DWord Indexed, PPC64 p585) DIP("stdx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); store( mkexpr(EA), mkexpr(rS) ); break; default: vex_printf("dis_int_store(ppc)(opc2)\n"); return False; } break; /* DS Form - 64bit Stores. In each case EA will have been formed with the lowest 2 bits masked off the immediate offset. */ case 0x3E: switch ((b1<<1) | b0) { case 0x0: // std (Store DWord, PPC64 p580) if (!mode64) return False; DIP("std r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); store( mkexpr(EA), mkexpr(rS) ); break; case 0x1: // stdu (Store DWord, Update, PPC64 p583) if (!mode64) return False; DIP("stdu r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); putIReg( rA_addr, mkexpr(EA) ); store( mkexpr(EA), mkexpr(rS) ); break; case 0x2: { // stq (Store QuadWord, Update, PPC64 p583) IRTemp EA_hi = newTemp(ty); IRTemp EA_lo = newTemp(ty); DIP("stq r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); if (mode64) { if (host_endness == VexEndnessBE) { /* upper 64-bits */ assign( EA_hi, ea_rAor0_simm( rA_addr, simm16 ) ); /* lower 64-bits */ assign( EA_lo, ea_rAor0_simm( rA_addr, simm16+8 ) ); } else { /* upper 64-bits */ assign( EA_hi, ea_rAor0_simm( rA_addr, simm16+8 ) ); /* lower 64-bits */ assign( EA_lo, ea_rAor0_simm( rA_addr, simm16 ) ); } } else { /* upper half of upper 64-bits */ assign( EA_hi, ea_rAor0_simm( rA_addr, simm16+4 ) ); /* lower half of upper 64-bits */ assign( EA_lo, ea_rAor0_simm( rA_addr, simm16+12 ) ); } store( mkexpr(EA_hi), mkexpr(rS) ); store( mkexpr(EA_lo), getIReg( rS_addr+1 ) ); break; } default: vex_printf("dis_int_load(ppc)(0x3A, opc2)\n"); return False; } break; default: vex_printf("dis_int_store(ppc)(opc1)\n"); return False; } return True; } /* Integer Load/Store Multiple Instructions */ static Bool dis_int_ldst_mult ( UInt theInstr ) { /* D-Form */ UChar opc1 = ifieldOPC(theInstr); UChar rD_addr = ifieldRegDS(theInstr); UChar rS_addr = rD_addr; UChar rA_addr = ifieldRegA(theInstr); UInt uimm16 = ifieldUIMM16(theInstr); Int simm16 = extend_s_16to32(uimm16); IRType ty = mode64 ? Ity_I64 : Ity_I32; IROp mkAdd = mode64 ? Iop_Add64 : Iop_Add32; IRTemp EA = newTemp(ty); UInt r = 0; UInt ea_off = 0; IRExpr* irx_addr; assign( EA, ea_rAor0_simm( rA_addr, simm16 ) ); switch (opc1) { case 0x2E: // lmw (Load Multiple Word, PPC32 p454) if (rA_addr >= rD_addr) { vex_printf("dis_int_ldst_mult(ppc)(lmw,rA_addr)\n"); return False; } DIP("lmw r%u,%d(r%u)\n", rD_addr, simm16, rA_addr); for (r = rD_addr; r <= 31; r++) { irx_addr = binop(mkAdd, mkexpr(EA), mode64 ? mkU64(ea_off) : mkU32(ea_off)); putIReg( r, mkWidenFrom32(ty, load(Ity_I32, irx_addr ), False) ); ea_off += 4; } break; case 0x2F: // stmw (Store Multiple Word, PPC32 p527) DIP("stmw r%u,%d(r%u)\n", rS_addr, simm16, rA_addr); for (r = rS_addr; r <= 31; r++) { irx_addr = binop(mkAdd, mkexpr(EA), mode64 ? mkU64(ea_off) : mkU32(ea_off)); store( irx_addr, mkNarrowTo32(ty, getIReg(r)) ); ea_off += 4; } break; default: vex_printf("dis_int_ldst_mult(ppc)(opc1)\n"); return False; } return True; } /* Integer Load/Store String Instructions */ static void generate_lsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32 IRTemp EA, // EA Int rD, // first dst register Int maxBytes ) // 32 or 128 { Int i, shift = 24; IRExpr* e_nbytes = mkexpr(tNBytes); IRExpr* e_EA = mkexpr(EA); IRType ty = mode64 ? Ity_I64 : Ity_I32; vassert(rD >= 0 && rD < 32); rD--; if (rD < 0) rD = 31; for (i = 0; i < maxBytes; i++) { /* if (nBytes < (i+1)) goto NIA; */ stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)), Ijk_Boring, mkSzConst( ty, nextInsnAddr()), OFFB_CIA )); /* when crossing into a new dest register, set it to zero. */ if ((i % 4) == 0) { rD++; if (rD == 32) rD = 0; putIReg(rD, mkSzImm(ty, 0)); shift = 24; } /* rD |= (8Uto32(*(EA+i))) << shift */ vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24); putIReg( rD, mkWidenFrom32( ty, binop( Iop_Or32, mkNarrowTo32(ty, getIReg(rD)), binop( Iop_Shl32, unop( Iop_8Uto32, load( Ity_I8, binop( mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i))) ), mkU8(toUChar(shift)) ) ), /*Signed*/False ) ); shift -= 8; } } static void generate_stsw_sequence ( IRTemp tNBytes, // # bytes, :: Ity_I32 IRTemp EA, // EA Int rS, // first src register Int maxBytes ) // 32 or 128 { Int i, shift = 24; IRExpr* e_nbytes = mkexpr(tNBytes); IRExpr* e_EA = mkexpr(EA); IRType ty = mode64 ? Ity_I64 : Ity_I32; vassert(rS >= 0 && rS < 32); rS--; if (rS < 0) rS = 31; for (i = 0; i < maxBytes; i++) { /* if (nBytes < (i+1)) goto NIA; */ stmt( IRStmt_Exit( binop(Iop_CmpLT32U, e_nbytes, mkU32(i+1)), Ijk_Boring, mkSzConst( ty, nextInsnAddr() ), OFFB_CIA )); /* check for crossing into a new src register. */ if ((i % 4) == 0) { rS++; if (rS == 32) rS = 0; shift = 24; } /* *(EA+i) = 32to8(rS >> shift) */ vassert(shift == 0 || shift == 8 || shift == 16 || shift == 24); store( binop( mkSzOp(ty,Iop_Add8), e_EA, mkSzImm(ty,i)), unop( Iop_32to8, binop( Iop_Shr32, mkNarrowTo32( ty, getIReg(rS) ), mkU8( toUChar(shift) ))) ); shift -= 8; } } static Bool dis_int_ldst_str ( UInt theInstr, /*OUT*/Bool* stopHere ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar rD_addr = ifieldRegDS(theInstr); UChar rS_addr = rD_addr; UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UChar NumBytes = rB_addr; UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp t_EA = newTemp(ty); IRTemp t_nbytes = IRTemp_INVALID; *stopHere = False; if (opc1 != 0x1F || b0 != 0) { vex_printf("dis_int_ldst_str(ppc)(opc1)\n"); return False; } switch (opc2) { case 0x255: // lswi (Load String Word Immediate, PPC32 p455) /* NB: does not reject the case where RA is in the range of registers to be loaded. It should. */ DIP("lswi r%u,r%u,%d\n", rD_addr, rA_addr, NumBytes); assign( t_EA, ea_rAor0(rA_addr) ); if (NumBytes == 8 && !mode64) { /* Special case hack */ /* rD = Mem[EA]; (rD+1)%32 = Mem[EA+4] */ putIReg( rD_addr, load(Ity_I32, mkexpr(t_EA)) ); putIReg( (rD_addr+1) % 32, load(Ity_I32, binop(Iop_Add32, mkexpr(t_EA), mkU32(4))) ); } else { t_nbytes = newTemp(Ity_I32); assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) ); generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 32 ); *stopHere = True; } return True; case 0x215: // lswx (Load String Word Indexed, PPC32 p456) /* NB: does not reject the case where RA is in the range of registers to be loaded. It should. Although considering that that can only be detected at run time, it's not easy to do so. */ if (rD_addr == rA_addr || rD_addr == rB_addr) return False; if (rD_addr == 0 && rA_addr == 0) return False; DIP("lswx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); t_nbytes = newTemp(Ity_I32); assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) ); assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) ); generate_lsw_sequence( t_nbytes, t_EA, rD_addr, 128 ); *stopHere = True; return True; case 0x2D5: // stswi (Store String Word Immediate, PPC32 p528) DIP("stswi r%u,r%u,%d\n", rS_addr, rA_addr, NumBytes); assign( t_EA, ea_rAor0(rA_addr) ); if (NumBytes == 8 && !mode64) { /* Special case hack */ /* Mem[EA] = rD; Mem[EA+4] = (rD+1)%32 */ store( mkexpr(t_EA), getIReg(rD_addr) ); store( binop(Iop_Add32, mkexpr(t_EA), mkU32(4)), getIReg((rD_addr+1) % 32) ); } else { t_nbytes = newTemp(Ity_I32); assign( t_nbytes, mkU32(NumBytes==0 ? 32 : NumBytes) ); generate_stsw_sequence( t_nbytes, t_EA, rD_addr, 32 ); *stopHere = True; } return True; case 0x295: // stswx (Store String Word Indexed, PPC32 p529) DIP("stswx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); t_nbytes = newTemp(Ity_I32); assign( t_EA, ea_rAor0_idxd(rA_addr,rB_addr) ); assign( t_nbytes, unop( Iop_8Uto32, getXER_BC() ) ); generate_stsw_sequence( t_nbytes, t_EA, rS_addr, 128 ); *stopHere = True; return True; default: vex_printf("dis_int_ldst_str(ppc)(opc2)\n"); return False; } return True; } /* ------------------------------------------------------------------ Integer Branch Instructions ------------------------------------------------------------------ */ /* Branch helper function ok = BO[2] | ((CTR[0] != 0) ^ BO[1]) Returns an I32 which is 0x00000000 if the ctr condition failed and 0xFFFFFFFF otherwise. */ static IRExpr* /* :: Ity_I32 */ branch_ctr_ok( UInt BO ) { IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp ok = newTemp(Ity_I32); if ((BO >> 2) & 1) { // independent of ctr assign( ok, mkU32(0xFFFFFFFF) ); } else { if ((BO >> 1) & 1) { // ctr == 0 ? assign( ok, unop( Iop_1Sto32, binop( mkSzOp(ty, Iop_CmpEQ8), getGST( PPC_GST_CTR ), mkSzImm(ty,0))) ); } else { // ctr != 0 ? assign( ok, unop( Iop_1Sto32, binop( mkSzOp(ty, Iop_CmpNE8), getGST( PPC_GST_CTR ), mkSzImm(ty,0))) ); } } return mkexpr(ok); } /* Branch helper function cond_ok = BO[4] | (CR[BI] == BO[3]) Returns an I32 which is either 0 if the condition failed or some arbitrary nonzero value otherwise. */ static IRExpr* /* :: Ity_I32 */ branch_cond_ok( UInt BO, UInt BI ) { Int where; IRTemp res = newTemp(Ity_I32); IRTemp cr_bi = newTemp(Ity_I32); if ((BO >> 4) & 1) { assign( res, mkU32(1) ); } else { // ok = (CR[BI] == BO[3]) Note, the following relies on // getCRbit_anywhere returning a value which // is either zero or has exactly 1 bit set. assign( cr_bi, getCRbit_anywhere( BI, &where ) ); if ((BO >> 3) & 1) { /* We can use cr_bi as-is. */ assign( res, mkexpr(cr_bi) ); } else { /* We have to invert the sense of the information held in cr_bi. For that we need to know which bit getCRbit_anywhere regards as significant. */ assign( res, binop(Iop_Xor32, mkexpr(cr_bi), mkU32(1<<where)) ); } } return mkexpr(res); } /* Integer Branch Instructions */ static Bool dis_branch ( UInt theInstr, const VexAbiInfo* vbi, /*OUT*/DisResult* dres, Bool (*resteerOkFn)(void*,Addr), void* callback_opaque ) { UChar opc1 = ifieldOPC(theInstr); UChar BO = ifieldRegDS(theInstr); UChar BI = ifieldRegA(theInstr); UInt BD_u16 = ifieldUIMM16(theInstr) & 0xFFFFFFFC; /* mask off */ UChar b11to15 = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UInt LI_u26 = ifieldUIMM26(theInstr) & 0xFFFFFFFC; /* mask off */ UChar flag_AA = ifieldBIT1(theInstr); UChar flag_LK = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; Addr64 tgt = 0; UInt BD = extend_s_16to32(BD_u16); IRTemp do_branch = newTemp(Ity_I32); IRTemp ctr_ok = newTemp(Ity_I32); IRTemp cond_ok = newTemp(Ity_I32); IRExpr* e_nia = mkSzImm(ty, nextInsnAddr()); IRConst* c_nia = mkSzConst(ty, nextInsnAddr()); IRTemp lr_old = newTemp(ty); /* Hack to pass through code that just wants to read the PC */ if (theInstr == 0x429F0005) { DIP("bcl 0x%x, 0x%x (a.k.a mr lr,cia+4)\n", BO, BI); putGST( PPC_GST_LR, e_nia ); return True; } /* The default what-next. Individual cases can override it. */ dres->whatNext = Dis_StopHere; vassert(dres->jk_StopHere == Ijk_INVALID); switch (opc1) { case 0x12: // b (Branch, PPC32 p360) if (flag_AA) { tgt = mkSzAddr( ty, extend_s_26to64(LI_u26) ); } else { tgt = mkSzAddr( ty, guest_CIA_curr_instr + (Long)extend_s_26to64(LI_u26) ); } if (mode64) { DIP("b%s%s 0x%llx\n", flag_LK ? "l" : "", flag_AA ? "a" : "", tgt); } else { DIP("b%s%s 0x%x\n", flag_LK ? "l" : "", flag_AA ? "a" : "", (Addr32)tgt); } if (flag_LK) { putGST( PPC_GST_LR, e_nia ); if (vbi->guest_ppc_zap_RZ_at_bl && vbi->guest_ppc_zap_RZ_at_bl( (ULong)tgt) ) { IRTemp t_tgt = newTemp(ty); assign(t_tgt, mode64 ? mkU64(tgt) : mkU32(tgt) ); make_redzone_AbiHint( vbi, t_tgt, "branch-and-link (unconditional call)" ); } } if (resteerOkFn( callback_opaque, tgt )) { dres->whatNext = Dis_ResteerU; dres->continueAt = tgt; } else { dres->jk_StopHere = flag_LK ? Ijk_Call : Ijk_Boring; ; putGST( PPC_GST_CIA, mkSzImm(ty, tgt) ); } break; case 0x10: // bc (Branch Conditional, PPC32 p361) DIP("bc%s%s 0x%x, 0x%x, 0x%x\n", flag_LK ? "l" : "", flag_AA ? "a" : "", BO, BI, BD); if (!(BO & 0x4)) { putGST( PPC_GST_CTR, binop(mkSzOp(ty, Iop_Sub8), getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) ); } /* This is a bit subtle. ctr_ok is either all 0s or all 1s. cond_ok is either zero or nonzero, since that's the cheapest way to compute it. Anding them together gives a value which is either zero or non zero and so that's what we must test for in the IRStmt_Exit. */ assign( ctr_ok, branch_ctr_ok( BO ) ); assign( cond_ok, branch_cond_ok( BO, BI ) ); assign( do_branch, binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) ); if (flag_AA) { tgt = mkSzAddr(ty, extend_s_16to64(BD_u16)); } else { tgt = mkSzAddr(ty, guest_CIA_curr_instr + (Long)extend_s_16to64(BD_u16)); } if (flag_LK) putGST( PPC_GST_LR, e_nia ); stmt( IRStmt_Exit( binop(Iop_CmpNE32, mkexpr(do_branch), mkU32(0)), flag_LK ? Ijk_Call : Ijk_Boring, mkSzConst(ty, tgt), OFFB_CIA ) ); dres->jk_StopHere = Ijk_Boring; putGST( PPC_GST_CIA, e_nia ); break; case 0x13: /* For bclr and bcctr, it appears that the lowest two bits of b11to15 are a branch hint, and so we only need to ensure it's of the form 000XX. */ if ((b11to15 & ~3) != 0) { vex_printf("dis_int_branch(ppc)(0x13,b11to15)(%d)\n", b11to15); return False; } switch (opc2) { case 0x210: // bcctr (Branch Cond. to Count Register, PPC32 p363) if ((BO & 0x4) == 0) { // "decr and test CTR" option invalid vex_printf("dis_int_branch(ppc)(bcctr,BO)\n"); return False; } DIP("bcctr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI); assign( cond_ok, branch_cond_ok( BO, BI ) ); /* FIXME: this is confusing. lr_old holds the old value of ctr, not lr :-) */ assign( lr_old, addr_align( getGST( PPC_GST_CTR ), 4 )); if (flag_LK) putGST( PPC_GST_LR, e_nia ); stmt( IRStmt_Exit( binop(Iop_CmpEQ32, mkexpr(cond_ok), mkU32(0)), Ijk_Boring, c_nia, OFFB_CIA )); if (flag_LK && vbi->guest_ppc_zap_RZ_at_bl) { make_redzone_AbiHint( vbi, lr_old, "b-ctr-l (indirect call)" ); } dres->jk_StopHere = flag_LK ? Ijk_Call : Ijk_Boring;; putGST( PPC_GST_CIA, mkexpr(lr_old) ); break; case 0x010: { // bclr (Branch Cond. to Link Register, PPC32 p365) Bool vanilla_return = False; if ((BO & 0x14 /* 1z1zz */) == 0x14 && flag_LK == 0) { DIP("blr\n"); vanilla_return = True; } else { DIP("bclr%s 0x%x, 0x%x\n", flag_LK ? "l" : "", BO, BI); } if (!(BO & 0x4)) { putGST( PPC_GST_CTR, binop(mkSzOp(ty, Iop_Sub8), getGST( PPC_GST_CTR ), mkSzImm(ty, 1)) ); } /* See comments above for 'bc' about this */ assign( ctr_ok, branch_ctr_ok( BO ) ); assign( cond_ok, branch_cond_ok( BO, BI ) ); assign( do_branch, binop(Iop_And32, mkexpr(cond_ok), mkexpr(ctr_ok)) ); assign( lr_old, addr_align( getGST( PPC_GST_LR ), 4 )); if (flag_LK) putGST( PPC_GST_LR, e_nia ); stmt( IRStmt_Exit( binop(Iop_CmpEQ32, mkexpr(do_branch), mkU32(0)), Ijk_Boring, c_nia, OFFB_CIA )); if (vanilla_return && vbi->guest_ppc_zap_RZ_at_blr) { make_redzone_AbiHint( vbi, lr_old, "branch-to-lr (unconditional return)" ); } /* blrl is pretty strange; it's like a return that sets the return address of its caller to the insn following this one. Mark it as a return. */ dres->jk_StopHere = Ijk_Ret; /* was flag_LK ? Ijk_Call : Ijk_Ret; */ putGST( PPC_GST_CIA, mkexpr(lr_old) ); break; } default: vex_printf("dis_int_branch(ppc)(opc2)\n"); return False; } break; default: vex_printf("dis_int_branch(ppc)(opc1)\n"); return False; } return True; } /* Condition Register Logical Instructions */ static Bool dis_cond_logic ( UInt theInstr ) { /* XL-Form */ UChar opc1 = ifieldOPC(theInstr); UChar crbD_addr = ifieldRegDS(theInstr); UChar crfD_addr = toUChar( IFIELD(theInstr, 23, 3) ); UChar crbA_addr = ifieldRegA(theInstr); UChar crfS_addr = toUChar( IFIELD(theInstr, 18, 3) ); UChar crbB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRTemp crbD = newTemp(Ity_I32); IRTemp crbA = newTemp(Ity_I32); IRTemp crbB = newTemp(Ity_I32); if (opc1 != 19 || b0 != 0) { vex_printf("dis_cond_logic(ppc)(opc1)\n"); return False; } if (opc2 == 0) { // mcrf (Move Cond Reg Field, PPC32 p464) if (((crbD_addr & 0x3) != 0) || ((crbA_addr & 0x3) != 0) || (crbB_addr != 0)) { vex_printf("dis_cond_logic(ppc)(crbD|crbA|crbB != 0)\n"); return False; } DIP("mcrf cr%u,cr%u\n", crfD_addr, crfS_addr); putCR0( crfD_addr, getCR0( crfS_addr) ); putCR321( crfD_addr, getCR321(crfS_addr) ); } else { assign( crbA, getCRbit(crbA_addr) ); if (crbA_addr == crbB_addr) crbB = crbA; else assign( crbB, getCRbit(crbB_addr) ); switch (opc2) { case 0x101: // crand (Cond Reg AND, PPC32 p372) DIP("crand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr); assign( crbD, binop(Iop_And32, mkexpr(crbA), mkexpr(crbB)) ); break; case 0x081: // crandc (Cond Reg AND w. Complement, PPC32 p373) DIP("crandc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr); assign( crbD, binop(Iop_And32, mkexpr(crbA), unop(Iop_Not32, mkexpr(crbB))) ); break; case 0x121: // creqv (Cond Reg Equivalent, PPC32 p374) DIP("creqv crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr); assign( crbD, unop(Iop_Not32, binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB))) ); break; case 0x0E1: // crnand (Cond Reg NAND, PPC32 p375) DIP("crnand crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr); assign( crbD, unop(Iop_Not32, binop(Iop_And32, mkexpr(crbA), mkexpr(crbB))) ); break; case 0x021: // crnor (Cond Reg NOR, PPC32 p376) DIP("crnor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr); assign( crbD, unop(Iop_Not32, binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB))) ); break; case 0x1C1: // cror (Cond Reg OR, PPC32 p377) DIP("cror crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr); assign( crbD, binop(Iop_Or32, mkexpr(crbA), mkexpr(crbB)) ); break; case 0x1A1: // crorc (Cond Reg OR w. Complement, PPC32 p378) DIP("crorc crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr); assign( crbD, binop(Iop_Or32, mkexpr(crbA), unop(Iop_Not32, mkexpr(crbB))) ); break; case 0x0C1: // crxor (Cond Reg XOR, PPC32 p379) DIP("crxor crb%d,crb%d,crb%d\n", crbD_addr, crbA_addr, crbB_addr); assign( crbD, binop(Iop_Xor32, mkexpr(crbA), mkexpr(crbB)) ); break; default: vex_printf("dis_cond_logic(ppc)(opc2)\n"); return False; } putCRbit( crbD_addr, mkexpr(crbD) ); } return True; } /* Trap instructions */ /* Do the code generation for a trap. Returned Bool is true iff this is an unconditional trap. If the two arg IRExpr*s are Ity_I32s then the comparison is 32-bit. If they are Ity_I64s then they are 64-bit, and we must be disassembling 64-bit instructions. */ static Bool do_trap ( UChar TO, IRExpr* argL0, IRExpr* argR0, Addr64 cia ) { IRTemp argL, argR; IRExpr *argLe, *argRe, *cond, *tmp; Bool is32bit = typeOfIRExpr(irsb->tyenv, argL0 ) == Ity_I32; IROp opAND = is32bit ? Iop_And32 : Iop_And64; IROp opOR = is32bit ? Iop_Or32 : Iop_Or64; IROp opCMPORDS = is32bit ? Iop_CmpORD32S : Iop_CmpORD64S; IROp opCMPORDU = is32bit ? Iop_CmpORD32U : Iop_CmpORD64U; IROp opCMPNE = is32bit ? Iop_CmpNE32 : Iop_CmpNE64; IROp opCMPEQ = is32bit ? Iop_CmpEQ32 : Iop_CmpEQ64; IRExpr* const0 = is32bit ? mkU32(0) : mkU64(0); IRExpr* const2 = is32bit ? mkU32(2) : mkU64(2); IRExpr* const4 = is32bit ? mkU32(4) : mkU64(4); IRExpr* const8 = is32bit ? mkU32(8) : mkU64(8); const UChar b11100 = 0x1C; const UChar b00111 = 0x07; if (is32bit) { vassert( typeOfIRExpr(irsb->tyenv, argL0) == Ity_I32 ); vassert( typeOfIRExpr(irsb->tyenv, argR0) == Ity_I32 ); } else { vassert( typeOfIRExpr(irsb->tyenv, argL0) == Ity_I64 ); vassert( typeOfIRExpr(irsb->tyenv, argR0) == Ity_I64 ); vassert( mode64 ); } if ((TO & b11100) == b11100 || (TO & b00111) == b00111) { /* Unconditional trap. Just do the exit without testing the arguments. */ stmt( IRStmt_Exit( binop(opCMPEQ, const0, const0), Ijk_SigTRAP, mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia), OFFB_CIA )); return True; /* unconditional trap */ } if (is32bit) { argL = newTemp(Ity_I32); argR = newTemp(Ity_I32); } else { argL = newTemp(Ity_I64); argR = newTemp(Ity_I64); } assign( argL, argL0 ); assign( argR, argR0 ); argLe = mkexpr(argL); argRe = mkexpr(argR); cond = const0; if (TO & 16) { // L <s R tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const8); cond = binop(opOR, tmp, cond); } if (TO & 8) { // L >s R tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const4); cond = binop(opOR, tmp, cond); } if (TO & 4) { // L == R tmp = binop(opAND, binop(opCMPORDS, argLe, argRe), const2); cond = binop(opOR, tmp, cond); } if (TO & 2) { // L <u R tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const8); cond = binop(opOR, tmp, cond); } if (TO & 1) { // L >u R tmp = binop(opAND, binop(opCMPORDU, argLe, argRe), const4); cond = binop(opOR, tmp, cond); } stmt( IRStmt_Exit( binop(opCMPNE, cond, const0), Ijk_SigTRAP, mode64 ? IRConst_U64(cia) : IRConst_U32((UInt)cia), OFFB_CIA )); return False; /* not an unconditional trap */ } static Bool dis_trapi ( UInt theInstr, /*OUT*/DisResult* dres ) { /* D-Form */ UChar opc1 = ifieldOPC(theInstr); UChar TO = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UInt uimm16 = ifieldUIMM16(theInstr); ULong simm16 = extend_s_16to64(uimm16); Addr64 cia = guest_CIA_curr_instr; IRType ty = mode64 ? Ity_I64 : Ity_I32; Bool uncond = False; switch (opc1) { case 0x03: // twi (Trap Word Immediate, PPC32 p548) uncond = do_trap( TO, mode64 ? unop(Iop_64to32, getIReg(rA_addr)) : getIReg(rA_addr), mkU32( (UInt)simm16 ), cia ); if (TO == 4) { DIP("tweqi r%u,%d\n", rA_addr, (Int)simm16); } else { DIP("tw%di r%u,%d\n", TO, rA_addr, (Int)simm16); } break; case 0x02: // tdi if (!mode64) return False; uncond = do_trap( TO, getIReg(rA_addr), mkU64( (ULong)simm16 ), cia ); if (TO == 4) { DIP("tdeqi r%u,%d\n", rA_addr, (Int)simm16); } else { DIP("td%di r%u,%d\n", TO, rA_addr, (Int)simm16); } break; default: return False; } if (uncond) { /* If the trap shows signs of being unconditional, don't continue decoding past it. */ putGST( PPC_GST_CIA, mkSzImm( ty, nextInsnAddr() )); dres->jk_StopHere = Ijk_Boring; dres->whatNext = Dis_StopHere; } return True; } static Bool dis_trap ( UInt theInstr, /*OUT*/DisResult* dres ) { /* X-Form */ UInt opc2 = ifieldOPClo10(theInstr); UChar TO = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); Addr64 cia = guest_CIA_curr_instr; IRType ty = mode64 ? Ity_I64 : Ity_I32; Bool uncond = False; if (ifieldBIT0(theInstr) != 0) return False; switch (opc2) { case 0x004: // tw (Trap Word, PPC64 p540) uncond = do_trap( TO, mode64 ? unop(Iop_64to32, getIReg(rA_addr)) : getIReg(rA_addr), mode64 ? unop(Iop_64to32, getIReg(rB_addr)) : getIReg(rB_addr), cia ); if (TO == 4) { DIP("tweq r%u,r%u\n", rA_addr, rB_addr); } else { DIP("tw%d r%u,r%u\n", TO, rA_addr, rB_addr); } break; case 0x044: // td (Trap Doubleword, PPC64 p534) if (!mode64) return False; uncond = do_trap( TO, getIReg(rA_addr), getIReg(rB_addr), cia ); if (TO == 4) { DIP("tdeq r%u,r%u\n", rA_addr, rB_addr); } else { DIP("td%d r%u,r%u\n", TO, rA_addr, rB_addr); } break; default: return False; } if (uncond) { /* If the trap shows signs of being unconditional, don't continue decoding past it. */ putGST( PPC_GST_CIA, mkSzImm( ty, nextInsnAddr() )); dres->jk_StopHere = Ijk_Boring; dres->whatNext = Dis_StopHere; } return True; } /* System Linkage Instructions */ static Bool dis_syslink ( UInt theInstr, const VexAbiInfo* abiinfo, DisResult* dres ) { IRType ty = mode64 ? Ity_I64 : Ity_I32; if (theInstr != 0x44000002) { vex_printf("dis_syslink(ppc)(theInstr)\n"); return False; } // sc (System Call, PPC32 p504) DIP("sc\n"); /* Copy CIA into the IP_AT_SYSCALL pseudo-register, so that on Darwin Valgrind can back the guest up to this instruction if it needs to restart the syscall. */ putGST( PPC_GST_IP_AT_SYSCALL, getGST( PPC_GST_CIA ) ); /* It's important that all ArchRegs carry their up-to-date value at this point. So we declare an end-of-block here, which forces any TempRegs caching ArchRegs to be flushed. */ putGST( PPC_GST_CIA, mkSzImm( ty, nextInsnAddr() )); dres->whatNext = Dis_StopHere; dres->jk_StopHere = Ijk_Sys_syscall; return True; } /* Memory Synchronization Instructions Note on Reservations: We rely on the assumption that V will in fact only allow one thread at once to run. In effect, a thread can make a reservation, but we don't check any stores it does. Instead, the reservation is cancelled when the scheduler switches to another thread (run_thread_for_a_while()). */ static Bool dis_memsync ( UInt theInstr ) { /* X-Form, XL-Form */ UChar opc1 = ifieldOPC(theInstr); UInt b11to25 = IFIELD(theInstr, 11, 15); UChar flag_L = ifieldRegDS(theInstr); UInt b11to20 = IFIELD(theInstr, 11, 10); UInt M0 = IFIELD(theInstr, 11, 5); UChar rD_addr = ifieldRegDS(theInstr); UChar rS_addr = rD_addr; UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA = newTemp(ty); assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) ); switch (opc1) { /* XL-Form */ case 0x13: // isync (Instruction Synchronize, PPC32 p432) if (opc2 != 0x096) { vex_printf("dis_memsync(ppc)(0x13,opc2)\n"); return False; } if (b11to25 != 0 || b0 != 0) { vex_printf("dis_memsync(ppc)(0x13,b11to25|b0)\n"); return False; } DIP("isync\n"); stmt( IRStmt_MBE(Imbe_Fence) ); break; /* X-Form */ case 0x1F: switch (opc2) { case 0x356: // eieio or mbar (Enforce In-Order Exec of I/O, PPC32 p394) if (M0 == 0) { if (b11to20 != 0 || b0 != 0) { vex_printf("dis_memsync(ppc)(eieio,b11to20|b0)\n"); return False; } DIP("eieio\n"); } else { if (b11to20 != 0 || b0 != 0) { vex_printf("dis_memsync(ppc)(mbar,b11to20|b0)\n"); return False; } DIP("mbar %d\n", M0); } /* Insert a memory fence, just to be on the safe side. */ stmt( IRStmt_MBE(Imbe_Fence) ); break; case 0x014: { // lwarx (Load Word and Reserve Indexed, PPC32 p458) IRTemp res; /* According to the PowerPC ISA version 2.05, b0 (called EH in the documentation) is merely a hint bit to the hardware, I think as to whether or not contention is likely. So we can just ignore it. */ DIP("lwarx r%u,r%u,r%u,EH=%u\n", rD_addr, rA_addr, rB_addr, b0); // trap if misaligned gen_SIGBUS_if_misaligned( EA, 4 ); // and actually do the load res = newTemp(Ity_I32); stmt( stmt_load(res, mkexpr(EA), NULL/*this is a load*/) ); putIReg( rD_addr, mkWidenFrom32(ty, mkexpr(res), False) ); break; } case 0x034: { // lbarx (Load Word and Reserve Indexed) IRTemp res; /* According to the PowerPC ISA version 2.05, b0 (called EH in the documentation) is merely a hint bit to the hardware, I think as to whether or not contention is likely. So we can just ignore it. */ DIP("lbarx r%u,r%u,r%u,EH=%u\n", rD_addr, rA_addr, rB_addr, b0); // and actually do the load res = newTemp(Ity_I8); stmt( stmt_load(res, mkexpr(EA), NULL/*this is a load*/) ); putIReg( rD_addr, mkWidenFrom8(ty, mkexpr(res), False) ); break; } case 0x074: { // lharx (Load Word and Reserve Indexed) IRTemp res; /* According to the PowerPC ISA version 2.05, b0 (called EH in the documentation) is merely a hint bit to the hardware, I think as to whether or not contention is likely. So we can just ignore it. */ DIP("lharx r%u,r%u,r%u,EH=%u\n", rD_addr, rA_addr, rB_addr, b0); // trap if misaligned gen_SIGBUS_if_misaligned( EA, 2 ); // and actually do the load res = newTemp(Ity_I16); stmt( stmt_load(res, mkexpr(EA), NULL/*this is a load*/) ); putIReg( rD_addr, mkWidenFrom16(ty, mkexpr(res), False) ); break; } case 0x096: { // stwcx. (Store Word Conditional Indexed, PPC32 p532) // Note this has to handle stwcx. in both 32- and 64-bit modes, // so isn't quite as straightforward as it might otherwise be. IRTemp rS = newTemp(Ity_I32); IRTemp resSC; if (b0 != 1) { vex_printf("dis_memsync(ppc)(stwcx.,b0)\n"); return False; } DIP("stwcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); // trap if misaligned gen_SIGBUS_if_misaligned( EA, 4 ); // Get the data to be stored, and narrow to 32 bits if necessary assign( rS, mkNarrowTo32(ty, getIReg(rS_addr)) ); // Do the store, and get success/failure bit into resSC resSC = newTemp(Ity_I1); stmt( stmt_load( resSC, mkexpr(EA), mkexpr(rS)) ); // Set CR0[LT GT EQ S0] = 0b000 || XER[SO] on failure // Set CR0[LT GT EQ S0] = 0b001 || XER[SO] on success putCR321(0, binop(Iop_Shl8, unop(Iop_1Uto8, mkexpr(resSC)), mkU8(1))); putCR0(0, getXER_SO()); /* Note: If resaddr != lwarx_resaddr, CR0[EQ] is undefined, and whether rS is stored is dependent on that value. */ /* So I guess we can just ignore this case? */ break; } case 0x2B6: { // stbcx. (Store Byte Conditional Indexed) // Note this has to handle stbcx. in both 32- and 64-bit modes, // so isn't quite as straightforward as it might otherwise be. IRTemp rS = newTemp(Ity_I8); IRTemp resSC; if (b0 != 1) { vex_printf("dis_memsync(ppc)(stbcx.,b0)\n"); return False; } DIP("stbcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); // Get the data to be stored, and narrow to 32 bits if necessary assign( rS, mkNarrowTo8(ty, getIReg(rS_addr)) ); // Do the store, and get success/failure bit into resSC resSC = newTemp(Ity_I1); stmt( stmt_load( resSC, mkexpr(EA), mkexpr(rS)) ); // Set CR0[LT GT EQ S0] = 0b000 || XER[SO] on failure // Set CR0[LT GT EQ S0] = 0b001 || XER[SO] on success putCR321(0, binop(Iop_Shl8, unop(Iop_1Uto8, mkexpr(resSC)), mkU8(1))); putCR0(0, getXER_SO()); /* Note: If resaddr != lbarx_resaddr, CR0[EQ] is undefined, and whether rS is stored is dependent on that value. */ /* So I guess we can just ignore this case? */ break; } case 0x2D6: { // sthcx. (Store Word Conditional Indexed, PPC32 p532) // Note this has to handle sthcx. in both 32- and 64-bit modes, // so isn't quite as straightforward as it might otherwise be. IRTemp rS = newTemp(Ity_I16); IRTemp resSC; if (b0 != 1) { vex_printf("dis_memsync(ppc)(stwcx.,b0)\n"); return False; } DIP("sthcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); // trap if misaligned gen_SIGBUS_if_misaligned( EA, 2 ); // Get the data to be stored, and narrow to 16 bits if necessary assign( rS, mkNarrowTo16(ty, getIReg(rS_addr)) ); // Do the store, and get success/failure bit into resSC resSC = newTemp(Ity_I1); stmt( stmt_load( resSC, mkexpr(EA), mkexpr(rS)) ); // Set CR0[LT GT EQ S0] = 0b000 || XER[SO] on failure // Set CR0[LT GT EQ S0] = 0b001 || XER[SO] on success putCR321(0, binop(Iop_Shl8, unop(Iop_1Uto8, mkexpr(resSC)), mkU8(1))); putCR0(0, getXER_SO()); /* Note: If resaddr != lharx_resaddr, CR0[EQ] is undefined, and whether rS is stored is dependent on that value. */ /* So I guess we can just ignore this case? */ break; } case 0x256: // sync (Synchronize, PPC32 p543), // also lwsync (L==1), ptesync (L==2) /* http://sources.redhat.com/ml/binutils/2000-12/msg00311.html The PowerPC architecture used in IBM chips has expanded the sync instruction into two variants: lightweight sync and heavyweight sync. The original sync instruction is the new heavyweight sync and lightweight sync is a strict subset of the heavyweight sync functionality. This allows the programmer to specify a less expensive operation on high-end systems when the full sync functionality is not necessary. The basic "sync" mnemonic now utilizes an operand. "sync" without an operand now becomes a extended mnemonic for heavyweight sync. Processors without the lwsync instruction will not decode the L field and will perform a heavyweight sync. Everything is backward compatible. sync = sync 0 lwsync = sync 1 ptesync = sync 2 *** TODO - not implemented *** */ if (b11to20 != 0 || b0 != 0) { vex_printf("dis_memsync(ppc)(sync/lwsync,b11to20|b0)\n"); return False; } if (flag_L != 0/*sync*/ && flag_L != 1/*lwsync*/) { vex_printf("dis_memsync(ppc)(sync/lwsync,flag_L)\n"); return False; } DIP("%ssync\n", flag_L == 1 ? "lw" : ""); /* Insert a memory fence. It's sometimes important that these are carried through to the generated code. */ stmt( IRStmt_MBE(Imbe_Fence) ); break; /* 64bit Memsync */ case 0x054: { // ldarx (Load DWord and Reserve Indexed, PPC64 p473) IRTemp res; /* According to the PowerPC ISA version 2.05, b0 (called EH in the documentation) is merely a hint bit to the hardware, I think as to whether or not contention is likely. So we can just ignore it. */ if (!mode64) return False; DIP("ldarx r%u,r%u,r%u,EH=%u\n", rD_addr, rA_addr, rB_addr, b0); // trap if misaligned gen_SIGBUS_if_misaligned( EA, 8 ); // and actually do the load res = newTemp(Ity_I64); stmt( stmt_load( res, mkexpr(EA), NULL/*this is a load*/) ); putIReg( rD_addr, mkexpr(res) ); break; } case 0x0D6: { // stdcx. (Store DWord Condition Indexd, PPC64 p581) // A marginally simplified version of the stwcx. case IRTemp rS = newTemp(Ity_I64); IRTemp resSC; if (b0 != 1) { vex_printf("dis_memsync(ppc)(stdcx.,b0)\n"); return False; } if (!mode64) return False; DIP("stdcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); // trap if misaligned gen_SIGBUS_if_misaligned( EA, 8 ); // Get the data to be stored assign( rS, getIReg(rS_addr) ); // Do the store, and get success/failure bit into resSC resSC = newTemp(Ity_I1); stmt( stmt_load( resSC, mkexpr(EA), mkexpr(rS)) ); // Set CR0[LT GT EQ S0] = 0b000 || XER[SO] on failure // Set CR0[LT GT EQ S0] = 0b001 || XER[SO] on success putCR321(0, binop(Iop_Shl8, unop(Iop_1Uto8, mkexpr(resSC)), mkU8(1))); putCR0(0, getXER_SO()); /* Note: If resaddr != lwarx_resaddr, CR0[EQ] is undefined, and whether rS is stored is dependent on that value. */ /* So I guess we can just ignore this case? */ break; } /* 128bit Memsync */ case 0x114: { // lqarx (Load QuadWord and Reserve Indexed) IRTemp res_hi = newTemp(ty); IRTemp res_lo = newTemp(ty); /* According to the PowerPC ISA version 2.07, b0 (called EH in the documentation) is merely a hint bit to the hardware, I think as to whether or not contention is likely. So we can just ignore it. */ DIP("lqarx r%u,r%u,r%u,EH=%u\n", rD_addr, rA_addr, rB_addr, b0); // trap if misaligned gen_SIGBUS_if_misaligned( EA, 16 ); // and actually do the load if (mode64) { if (host_endness == VexEndnessBE) { stmt( stmt_load( res_hi, mkexpr(EA), NULL/*this is a load*/) ); stmt( stmt_load( res_lo, binop(Iop_Add64, mkexpr(EA), mkU64(8) ), NULL/*this is a load*/) ); } else { stmt( stmt_load( res_lo, mkexpr(EA), NULL/*this is a load*/) ); stmt( stmt_load( res_hi, binop(Iop_Add64, mkexpr(EA), mkU64(8) ), NULL/*this is a load*/) ); } } else { stmt( stmt_load( res_hi, binop( Iop_Add32, mkexpr(EA), mkU32(4) ), NULL/*this is a load*/) ); stmt( stmt_load( res_lo, binop( Iop_Add32, mkexpr(EA), mkU32(12) ), NULL/*this is a load*/) ); } putIReg( rD_addr, mkexpr(res_hi) ); putIReg( rD_addr+1, mkexpr(res_lo) ); break; } case 0x0B6: { // stqcx. (Store QuadWord Condition Indexd, PPC64) // A marginally simplified version of the stwcx. case IRTemp rS_hi = newTemp(ty); IRTemp rS_lo = newTemp(ty); IRTemp resSC; if (b0 != 1) { vex_printf("dis_memsync(ppc)(stqcx.,b0)\n"); return False; } DIP("stqcx. r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); // trap if misaligned gen_SIGBUS_if_misaligned( EA, 16 ); // Get the data to be stored assign( rS_hi, getIReg(rS_addr) ); assign( rS_lo, getIReg(rS_addr+1) ); // Do the store, and get success/failure bit into resSC resSC = newTemp(Ity_I1); if (mode64) { if (host_endness == VexEndnessBE) { stmt( stmt_load( resSC, mkexpr(EA), mkexpr(rS_hi) ) ); store( binop( Iop_Add64, mkexpr(EA), mkU64(8) ), mkexpr(rS_lo) ); } else { stmt( stmt_load( resSC, mkexpr(EA), mkexpr(rS_lo) ) ); store( binop( Iop_Add64, mkexpr(EA), mkU64(8) ), mkexpr(rS_hi) ); } } else { stmt( stmt_load( resSC, binop( Iop_Add32, mkexpr(EA), mkU32(4) ), mkexpr(rS_hi) ) ); store( binop(Iop_Add32, mkexpr(EA), mkU32(12) ), mkexpr(rS_lo) ); } // Set CR0[LT GT EQ S0] = 0b000 || XER[SO] on failure // Set CR0[LT GT EQ S0] = 0b001 || XER[SO] on success putCR321(0, binop( Iop_Shl8, unop(Iop_1Uto8, mkexpr(resSC) ), mkU8(1))); putCR0(0, getXER_SO()); break; } default: vex_printf("dis_memsync(ppc)(opc2)\n"); return False; } break; default: vex_printf("dis_memsync(ppc)(opc1)\n"); return False; } return True; } /* Integer Shift Instructions */ static Bool dis_int_shift ( UInt theInstr ) { /* X-Form, XS-Form */ UChar opc1 = ifieldOPC(theInstr); UChar rS_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UChar sh_imm = rB_addr; UInt opc2 = ifieldOPClo10(theInstr); UChar b1 = ifieldBIT1(theInstr); UChar flag_rC = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp rA = newTemp(ty); IRTemp rS = newTemp(ty); IRTemp rB = newTemp(ty); IRTemp outofrange = newTemp(Ity_I1); IRTemp rS_lo32 = newTemp(Ity_I32); IRTemp rB_lo32 = newTemp(Ity_I32); IRExpr* e_tmp; assign( rS, getIReg(rS_addr) ); assign( rB, getIReg(rB_addr) ); assign( rS_lo32, mkNarrowTo32(ty, mkexpr(rS)) ); assign( rB_lo32, mkNarrowTo32(ty, mkexpr(rB)) ); if (opc1 == 0x1F) { switch (opc2) { case 0x018: { // slw (Shift Left Word, PPC32 p505) DIP("slw%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); /* rA = rS << rB */ /* ppc32 semantics are: slw(x,y) = (x << (y & 31)) -- primary result & ~((y << 26) >>s 31) -- make result 0 for y in 32 .. 63 */ e_tmp = binop( Iop_And32, binop( Iop_Shl32, mkexpr(rS_lo32), unop( Iop_32to8, binop(Iop_And32, mkexpr(rB_lo32), mkU32(31)))), unop( Iop_Not32, binop( Iop_Sar32, binop(Iop_Shl32, mkexpr(rB_lo32), mkU8(26)), mkU8(31))) ); assign( rA, mkWidenFrom32(ty, e_tmp, /* Signed */False) ); break; } case 0x318: { // sraw (Shift Right Alg Word, PPC32 p506) IRTemp sh_amt = newTemp(Ity_I32); DIP("sraw%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); /* JRS: my reading of the (poorly worded) PPC32 doc p506 is: amt = rB & 63 rA = Sar32( rS, amt > 31 ? 31 : amt ) XER.CA = amt > 31 ? sign-of-rS : (computation as per srawi) */ assign( sh_amt, binop(Iop_And32, mkU32(0x3F), mkexpr(rB_lo32)) ); assign( outofrange, binop(Iop_CmpLT32U, mkU32(31), mkexpr(sh_amt)) ); e_tmp = binop( Iop_Sar32, mkexpr(rS_lo32), unop( Iop_32to8, IRExpr_ITE( mkexpr(outofrange), mkU32(31), mkexpr(sh_amt)) ) ); assign( rA, mkWidenFrom32(ty, e_tmp, /* Signed */True) ); set_XER_CA( ty, PPCG_FLAG_OP_SRAW, mkexpr(rA), mkWidenFrom32(ty, mkexpr(rS_lo32), True), mkWidenFrom32(ty, mkexpr(sh_amt), True ), mkWidenFrom32(ty, getXER_CA32(), True) ); break; } case 0x338: // srawi (Shift Right Alg Word Immediate, PPC32 p507) DIP("srawi%s r%u,r%u,%d\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm); vassert(sh_imm < 32); if (mode64) { assign( rA, binop(Iop_Sar64, binop(Iop_Shl64, getIReg(rS_addr), mkU8(32)), mkU8(32 + sh_imm)) ); } else { assign( rA, binop(Iop_Sar32, mkexpr(rS_lo32), mkU8(sh_imm)) ); } set_XER_CA( ty, PPCG_FLAG_OP_SRAWI, mkexpr(rA), mkWidenFrom32(ty, mkexpr(rS_lo32), /* Syned */True), mkSzImm(ty, sh_imm), mkWidenFrom32(ty, getXER_CA32(), /* Syned */False) ); break; case 0x218: // srw (Shift Right Word, PPC32 p508) DIP("srw%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); /* rA = rS >>u rB */ /* ppc32 semantics are: srw(x,y) = (x >>u (y & 31)) -- primary result & ~((y << 26) >>s 31) -- make result 0 for y in 32 .. 63 */ e_tmp = binop( Iop_And32, binop( Iop_Shr32, mkexpr(rS_lo32), unop( Iop_32to8, binop(Iop_And32, mkexpr(rB_lo32), mkU32(31)))), unop( Iop_Not32, binop( Iop_Sar32, binop(Iop_Shl32, mkexpr(rB_lo32), mkU8(26)), mkU8(31)))); assign( rA, mkWidenFrom32(ty, e_tmp, /* Signed */False) ); break; /* 64bit Shifts */ case 0x01B: // sld (Shift Left DWord, PPC64 p568) DIP("sld%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); /* rA = rS << rB */ /* ppc64 semantics are: slw(x,y) = (x << (y & 63)) -- primary result & ~((y << 57) >>s 63) -- make result 0 for y in 64 .. */ assign( rA, binop( Iop_And64, binop( Iop_Shl64, mkexpr(rS), unop( Iop_64to8, binop(Iop_And64, mkexpr(rB), mkU64(63)))), unop( Iop_Not64, binop( Iop_Sar64, binop(Iop_Shl64, mkexpr(rB), mkU8(57)), mkU8(63)))) ); break; case 0x31A: { // srad (Shift Right Alg DWord, PPC64 p570) IRTemp sh_amt = newTemp(Ity_I64); DIP("srad%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); /* amt = rB & 127 rA = Sar64( rS, amt > 63 ? 63 : amt ) XER.CA = amt > 63 ? sign-of-rS : (computation as per srawi) */ assign( sh_amt, binop(Iop_And64, mkU64(0x7F), mkexpr(rB)) ); assign( outofrange, binop(Iop_CmpLT64U, mkU64(63), mkexpr(sh_amt)) ); assign( rA, binop( Iop_Sar64, mkexpr(rS), unop( Iop_64to8, IRExpr_ITE( mkexpr(outofrange), mkU64(63), mkexpr(sh_amt)) )) ); set_XER_CA( ty, PPCG_FLAG_OP_SRAD, mkexpr(rA), mkexpr(rS), mkexpr(sh_amt), mkWidenFrom32(ty, getXER_CA32(), /* Syned */False) ); break; } case 0x33A: case 0x33B: // sradi (Shr Alg DWord Imm, PPC64 p571) sh_imm |= b1<<5; vassert(sh_imm < 64); DIP("sradi%s r%u,r%u,%u\n", flag_rC ? ".":"", rA_addr, rS_addr, sh_imm); assign( rA, binop(Iop_Sar64, getIReg(rS_addr), mkU8(sh_imm)) ); set_XER_CA( ty, PPCG_FLAG_OP_SRADI, mkexpr(rA), getIReg(rS_addr), mkU64(sh_imm), mkWidenFrom32(ty, getXER_CA32(), /* Syned */False) ); break; case 0x21B: // srd (Shift Right DWord, PPC64 p574) DIP("srd%s r%u,r%u,r%u\n", flag_rC ? ".":"", rA_addr, rS_addr, rB_addr); /* rA = rS >>u rB */ /* ppc semantics are: srw(x,y) = (x >>u (y & 63)) -- primary result & ~((y << 57) >>s 63) -- make result 0 for y in 64 .. 127 */ assign( rA, binop( Iop_And64, binop( Iop_Shr64, mkexpr(rS), unop( Iop_64to8, binop(Iop_And64, mkexpr(rB), mkU64(63)))), unop( Iop_Not64, binop( Iop_Sar64, binop(Iop_Shl64, mkexpr(rB), mkU8(57)), mkU8(63)))) ); break; default: vex_printf("dis_int_shift(ppc)(opc2)\n"); return False; } } else { vex_printf("dis_int_shift(ppc)(opc1)\n"); return False; } putIReg( rA_addr, mkexpr(rA) ); if (flag_rC) { set_CR0( mkexpr(rA) ); } return True; } /* Integer Load/Store Reverse Instructions */ /* Generates code to swap the byte order in an Ity_I32. */ static IRExpr* /* :: Ity_I32 */ gen_byterev32 ( IRTemp t ) { vassert(typeOfIRTemp(irsb->tyenv, t) == Ity_I32); return binop(Iop_Or32, binop(Iop_Shl32, mkexpr(t), mkU8(24)), binop(Iop_Or32, binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)), mkU32(0x00FF0000)), binop(Iop_Or32, binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)), mkU32(0x0000FF00)), binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(24)), mkU32(0x000000FF) ) ))); } /* Generates code to swap the byte order in the lower half of an Ity_I32, and zeroes the upper half. */ static IRExpr* /* :: Ity_I32 */ gen_byterev16 ( IRTemp t ) { vassert(typeOfIRTemp(irsb->tyenv, t) == Ity_I32); return binop(Iop_Or32, binop(Iop_And32, binop(Iop_Shl32, mkexpr(t), mkU8(8)), mkU32(0x0000FF00)), binop(Iop_And32, binop(Iop_Shr32, mkexpr(t), mkU8(8)), mkU32(0x000000FF)) ); } static Bool dis_int_ldst_rev ( UInt theInstr ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar rD_addr = ifieldRegDS(theInstr); UChar rS_addr = rD_addr; UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA = newTemp(ty); IRTemp w1 = newTemp(Ity_I32); IRTemp w2 = newTemp(Ity_I32); if (opc1 != 0x1F || b0 != 0) { vex_printf("dis_int_ldst_rev(ppc)(opc1|b0)\n"); return False; } assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) ); switch (opc2) { case 0x316: // lhbrx (Load Halfword Byte-Reverse Indexed, PPC32 p449) DIP("lhbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); assign( w1, unop(Iop_16Uto32, load(Ity_I16, mkexpr(EA))) ); assign( w2, gen_byterev16(w1) ); putIReg( rD_addr, mkWidenFrom32(ty, mkexpr(w2), /* Signed */False) ); break; case 0x216: // lwbrx (Load Word Byte-Reverse Indexed, PPC32 p459) DIP("lwbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); assign( w1, load(Ity_I32, mkexpr(EA)) ); assign( w2, gen_byterev32(w1) ); putIReg( rD_addr, mkWidenFrom32(ty, mkexpr(w2), /* Signed */False) ); break; case 0x214: // ldbrx (Load Doubleword Byte-Reverse Indexed) { IRExpr * nextAddr; IRTemp w3 = newTemp( Ity_I32 ); IRTemp w4 = newTemp( Ity_I32 ); DIP("ldbrx r%u,r%u,r%u\n", rD_addr, rA_addr, rB_addr); assign( w1, load( Ity_I32, mkexpr( EA ) ) ); assign( w2, gen_byterev32( w1 ) ); nextAddr = binop( mkSzOp( ty, Iop_Add8 ), mkexpr( EA ), ty == Ity_I64 ? mkU64( 4 ) : mkU32( 4 ) ); assign( w3, load( Ity_I32, nextAddr ) ); assign( w4, gen_byterev32( w3 ) ); if (host_endness == VexEndnessLE) putIReg( rD_addr, binop( Iop_32HLto64, mkexpr( w2 ), mkexpr( w4 ) ) ); else putIReg( rD_addr, binop( Iop_32HLto64, mkexpr( w4 ), mkexpr( w2 ) ) ); break; } case 0x396: // sthbrx (Store Half Word Byte-Reverse Indexed, PPC32 p523) DIP("sthbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); assign( w1, mkNarrowTo32(ty, getIReg(rS_addr)) ); store( mkexpr(EA), unop(Iop_32to16, gen_byterev16(w1)) ); break; case 0x296: // stwbrx (Store Word Byte-Reverse Indxd, PPC32 p531) DIP("stwbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); assign( w1, mkNarrowTo32(ty, getIReg(rS_addr)) ); store( mkexpr(EA), gen_byterev32(w1) ); break; case 0x294: // stdbrx (Store Doubleword Byte-Reverse Indexed) { IRTemp lo = newTemp(Ity_I32); IRTemp hi = newTemp(Ity_I32); IRTemp rS = newTemp(Ity_I64); assign( rS, getIReg( rS_addr ) ); DIP("stdbrx r%u,r%u,r%u\n", rS_addr, rA_addr, rB_addr); assign(lo, unop(Iop_64HIto32, mkexpr(rS))); assign(hi, unop(Iop_64to32, mkexpr(rS))); store( mkexpr( EA ), binop( Iop_32HLto64, gen_byterev32( hi ), gen_byterev32( lo ) ) ); break; } default: vex_printf("dis_int_ldst_rev(ppc)(opc2)\n"); return False; } return True; } /* Processor Control Instructions */ static Bool dis_proc_ctl ( const VexAbiInfo* vbi, UInt theInstr ) { UChar opc1 = ifieldOPC(theInstr); /* X-Form */ UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) ); UChar rD_addr = ifieldRegDS(theInstr); UInt b11to20 = IFIELD( theInstr, 11, 10 ); /* XFX-Form */ UChar rS_addr = rD_addr; UInt SPR = b11to20; UInt TBR = b11to20; UChar b20 = toUChar( IFIELD( theInstr, 20, 1 ) ); UInt CRM = IFIELD( theInstr, 12, 8 ); UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) ); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp rS = newTemp(ty); assign( rS, getIReg(rS_addr) ); /* Reorder SPR field as per PPC32 p470 */ SPR = ((SPR & 0x1F) << 5) | ((SPR >> 5) & 0x1F); /* Reorder TBR field as per PPC32 p475 */ TBR = ((TBR & 31) << 5) | ((TBR >> 5) & 31); /* b0 = 0, inst is treated as floating point inst for reservation purposes * b0 = 1, inst is treated as vector inst for reservation purposes */ if (opc1 != 0x1F) { vex_printf("dis_proc_ctl(ppc)(opc1|b%d)\n", b0); return False; } switch (opc2) { /* X-Form */ case 0x200: { // mcrxr (Move to Cond Register from XER, PPC32 p466) if (b21to22 != 0 || b11to20 != 0) { vex_printf("dis_proc_ctl(ppc)(mcrxr,b21to22|b11to20)\n"); return False; } DIP("mcrxr crf%d\n", crfD); /* Move XER[0-3] (the top 4 bits of XER) to CR[crfD] */ putGST_field( PPC_GST_CR, getGST_field( PPC_GST_XER, 7 ), crfD ); // Clear XER[0-3] putXER_SO( mkU8(0) ); putXER_OV( mkU8(0) ); putXER_CA( mkU8(0) ); break; } case 0x013: // b11to20==0: mfcr (Move from Cond Register, PPC32 p467) // b20==1 & b11==0: mfocrf (Move from One CR Field) // However it seems that the 'mfcr' behaviour is an acceptable // implementation of mfocr (from the 2.02 arch spec) if (b11to20 == 0) { DIP("mfcr r%u\n", rD_addr); putIReg( rD_addr, mkWidenFrom32(ty, getGST( PPC_GST_CR ), /* Signed */False) ); break; } if (b20 == 1 && b11 == 0) { DIP("mfocrf r%u,%u\n", rD_addr, CRM); putIReg( rD_addr, mkWidenFrom32(ty, getGST( PPC_GST_CR ), /* Signed */False) ); break; } /* not decodable */ return False; /* XFX-Form */ case 0x153: // mfspr (Move from Special-Purpose Register, PPC32 p470) switch (SPR) { // Choose a register... case 0x1: DIP("mfxer r%u\n", rD_addr); putIReg( rD_addr, mkWidenFrom32(ty, getGST( PPC_GST_XER ), /* Signed */False) ); break; case 0x8: DIP("mflr r%u\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_LR ) ); break; case 0x9: DIP("mfctr r%u\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_CTR ) ); break; case 0x80: // 128 DIP("mfspr r%u (TFHAR)\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_TFHAR) ); break; case 0x81: // 129 DIP("mfspr r%u (TFIAR)\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_TFIAR) ); break; case 0x82: // 130 DIP("mfspr r%u (TEXASR)\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_TEXASR) ); break; case 0x83: // 131 DIP("mfspr r%u (TEXASRU)\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_TEXASRU) ); break; case 0x9F: // 159 DIP("mfspr r%u (PSPB)\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_PSPB) ); break; case 0x380: // 896 DIP("mfspr r%u (PPR)\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_PPR) ); break; case 0x382: // 898 DIP("mfspr r%u (PPR)32\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_PPR32) ); break; case 0x100: DIP("mfvrsave r%u\n", rD_addr); putIReg( rD_addr, mkWidenFrom32(ty, getGST( PPC_GST_VRSAVE ), /* Signed */False) ); break; case 0x103: DIP("mfspr r%u, SPRG3(readonly)\n", rD_addr); putIReg( rD_addr, getGST( PPC_GST_SPRG3_RO ) ); break; /* Even a lowly PPC7400 can run the associated helper, so no obvious need for feature testing at this point. */ case 268 /* 0x10C */: case 269 /* 0x10D */: { UInt arg = SPR==268 ? 0 : 1; IRTemp val = newTemp(Ity_I32); IRExpr** args = mkIRExprVec_1( mkU32(arg) ); IRDirty* d = unsafeIRDirty_1_N( val, 0/*regparms*/, "ppc32g_dirtyhelper_MFSPR_268_269", fnptr_to_fnentry (vbi, &ppc32g_dirtyhelper_MFSPR_268_269), args ); /* execute the dirty call, dumping the result in val. */ stmt( IRStmt_Dirty(d) ); putIReg( rD_addr, mkWidenFrom32(ty, mkexpr(val), False/*unsigned*/) ); DIP("mfspr r%u,%u", rD_addr, SPR); break; } /* Again, runs natively on PPC7400 (7447, really). Not bothering with a feature test. */ case 287: /* 0x11F */ { IRTemp val = newTemp(Ity_I32); IRExpr** args = mkIRExprVec_0(); IRDirty* d = unsafeIRDirty_1_N( val, 0/*regparms*/, "ppc32g_dirtyhelper_MFSPR_287", fnptr_to_fnentry (vbi, &ppc32g_dirtyhelper_MFSPR_287), args ); /* execute the dirty call, dumping the result in val. */ stmt( IRStmt_Dirty(d) ); putIReg( rD_addr, mkWidenFrom32(ty, mkexpr(val), False/*unsigned*/) ); DIP("mfspr r%u,%u", rD_addr, SPR); break; } default: vex_printf("dis_proc_ctl(ppc)(mfspr,SPR)(0x%x)\n", SPR); return False; } break; case 0x173: { // mftb (Move from Time Base, PPC32 p475) IRTemp val = newTemp(Ity_I64); IRExpr** args = mkIRExprVec_0(); IRDirty* d = unsafeIRDirty_1_N( val, 0/*regparms*/, "ppcg_dirtyhelper_MFTB", fnptr_to_fnentry(vbi, &ppcg_dirtyhelper_MFTB), args ); /* execute the dirty call, dumping the result in val. */ stmt( IRStmt_Dirty(d) ); switch (TBR) { case 269: DIP("mftbu r%u", rD_addr); putIReg( rD_addr, mkWidenFrom32(ty, unop(Iop_64HIto32, mkexpr(val)), /* Signed */False) ); break; case 268: DIP("mftb r%u", rD_addr); putIReg( rD_addr, (mode64) ? mkexpr(val) : unop(Iop_64to32, mkexpr(val)) ); break; default: return False; /* illegal instruction */ } break; } case 0x090: { // b20==0: mtcrf (Move to Cond Register Fields, PPC32 p477) // b20==1: mtocrf (Move to One Cond Reg Field) Int cr; UChar shft; if (b11 != 0) return False; if (b20 == 1) { /* ppc64 v2.02 spec says mtocrf gives undefined outcome if > 1 field is written. It seems more robust to decline to decode the insn if so. */ switch (CRM) { case 0x01: case 0x02: case 0x04: case 0x08: case 0x10: case 0x20: case 0x40: case 0x80: break; default: return False; } } DIP("%s 0x%x,r%u\n", b20==1 ? "mtocrf" : "mtcrf", CRM, rS_addr); /* Write to each field specified by CRM */ for (cr = 0; cr < 8; cr++) { if ((CRM & (1 << (7-cr))) == 0) continue; shft = 4*(7-cr); putGST_field( PPC_GST_CR, binop(Iop_Shr32, mkNarrowTo32(ty, mkexpr(rS)), mkU8(shft)), cr ); } break; } case 0x1D3: // mtspr (Move to Special-Purpose Register, PPC32 p483) switch (SPR) { // Choose a register... case 0x1: DIP("mtxer r%u\n", rS_addr); putGST( PPC_GST_XER, mkNarrowTo32(ty, mkexpr(rS)) ); break; case 0x8: DIP("mtlr r%u\n", rS_addr); putGST( PPC_GST_LR, mkexpr(rS) ); break; case 0x9: DIP("mtctr r%u\n", rS_addr); putGST( PPC_GST_CTR, mkexpr(rS) ); break; case 0x100: DIP("mtvrsave r%u\n", rS_addr); putGST( PPC_GST_VRSAVE, mkNarrowTo32(ty, mkexpr(rS)) ); break; case 0x80: // 128 DIP("mtspr r%u (TFHAR)\n", rS_addr); putGST( PPC_GST_TFHAR, mkexpr(rS) ); break; case 0x81: // 129 DIP("mtspr r%u (TFIAR)\n", rS_addr); putGST( PPC_GST_TFIAR, mkexpr(rS) ); break; case 0x82: // 130 DIP("mtspr r%u (TEXASR)\n", rS_addr); putGST( PPC_GST_TEXASR, mkexpr(rS) ); break; case 0x9F: // 159 DIP("mtspr r%u (PSPB)\n", rS_addr); putGST( PPC_GST_PSPB, mkexpr(rS) ); break; case 0x380: // 896 DIP("mtspr r%u (PPR)\n", rS_addr); putGST( PPC_GST_PPR, mkexpr(rS) ); break; case 0x382: // 898 DIP("mtspr r%u (PPR32)\n", rS_addr); putGST( PPC_GST_PPR32, mkexpr(rS) ); break; default: vex_printf("dis_proc_ctl(ppc)(mtspr,SPR)(%u)\n", SPR); return False; } break; case 0x33: // mfvsrd { UChar XS = ifieldRegXS( theInstr ); UChar rA_addr = ifieldRegA(theInstr); IRExpr * high64; IRTemp vS = newTemp( Ity_V128 ); DIP("mfvsrd r%u,vsr%d\n", rA_addr, XS); /* XS = SX || S * For SX=0, mfvsrd is treated as a Floating-Point * instruction in terms of resource availability. * For SX=1, mfvsrd is treated as a Vector instruction in * terms of resource availability. * FIXME: NEED TO FIGURE OUT HOW TO IMPLEMENT THE RESOURCE AVAILABILITY PART */ assign( vS, getVSReg( XS ) ); high64 = unop( Iop_V128HIto64, mkexpr( vS ) ); putIReg( rA_addr, (mode64) ? high64 : unop( Iop_64to32, high64 ) ); break; } case 0x73: // mfvsrwz { UChar XS = ifieldRegXS( theInstr ); UChar rA_addr = ifieldRegA(theInstr); IRExpr * high64; IRTemp vS = newTemp( Ity_V128 ); DIP("mfvsrwz r%u,vsr%d\n", rA_addr, XS); /* XS = SX || S * For SX=0, mfvsrwz is treated as a Floating-Point * instruction in terms of resource availability. * For SX=1, mfvsrwz is treated as a Vector instruction in * terms of resource availability. * FIXME: NEED TO FIGURE OUT HOW TO IMPLEMENT THE RESOURCE AVAILABILITY PART */ assign( vS, getVSReg( XS ) ); high64 = unop( Iop_V128HIto64, mkexpr( vS ) ); /* move value to the destination setting the upper 32-bits to zero */ putIReg( rA_addr, (mode64) ? binop( Iop_And64, high64, mkU64( 0xFFFFFFFF ) ) : unop( Iop_64to32, binop( Iop_And64, high64, mkU64( 0xFFFFFFFF ) ) ) ); break; } case 0xB3: // mtvsrd { UChar XT = ifieldRegXT( theInstr ); UChar rA_addr = ifieldRegA(theInstr); IRTemp rA = newTemp(ty); DIP("mtvsrd vsr%d,r%u\n", XT, rA_addr); /* XS = SX || S * For SX=0, mfvsrd is treated as a Floating-Point * instruction in terms of resource availability. * For SX=1, mfvsrd is treated as a Vector instruction in * terms of resource availability. * FIXME: NEED TO FIGURE OUT HOW TO IMPLEMENT THE RESOURCE AVAILABILITY PART */ assign( rA, getIReg(rA_addr) ); if (mode64) putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( rA ), mkU64( 0 ) ) ); else putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkU32( 0 ), mkexpr( rA ) ), mkU64( 0 ) ) ); break; } case 0xD3: // mtvsrwa { UChar XT = ifieldRegXT( theInstr ); UChar rA_addr = ifieldRegA(theInstr); IRTemp rA = newTemp( Ity_I32 ); DIP("mtvsrwa vsr%d,r%u\n", XT, rA_addr); /* XS = SX || S * For SX=0, mtvsrwa is treated as a Floating-Point * instruction in terms of resource availability. * For SX=1, mtvsrwa is treated as a Vector instruction in * terms of resource availability. * FIXME: NEED TO FIGURE OUT HOW TO IMPLEMENT THE RESOURCE AVAILABILITY PART */ if (mode64) assign( rA, unop( Iop_64to32, getIReg( rA_addr ) ) ); else assign( rA, getIReg(rA_addr) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_32Sto64, mkexpr( rA ) ), mkU64( 0 ) ) ); break; } case 0xF3: // mtvsrwz { UChar XT = ifieldRegXT( theInstr ); UChar rA_addr = ifieldRegA(theInstr); IRTemp rA = newTemp( Ity_I32 ); DIP("mtvsrwz vsr%d,r%u\n", rA_addr, XT); /* XS = SX || S * For SX=0, mtvsrwz is treated as a Floating-Point * instruction in terms of resource availability. * For SX=1, mtvsrwz is treated as a Vector instruction in * terms of resource availability. * FIXME: NEED TO FIGURE OUT HOW TO IMPLEMENT THE RESOURCE AVAILABILITY PART */ if (mode64) assign( rA, unop( Iop_64to32, getIReg( rA_addr ) ) ); else assign( rA, getIReg(rA_addr) ); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkU32( 0 ), mkexpr ( rA ) ), mkU64( 0 ) ) ); break; } default: vex_printf("dis_proc_ctl(ppc)(opc2)\n"); return False; } return True; } /* Cache Management Instructions */ static Bool dis_cache_manage ( UInt theInstr, DisResult* dres, const VexArchInfo* guest_archinfo ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar b21to25 = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); UInt lineszB = guest_archinfo->ppc_icache_line_szB; Bool is_dcbzl = False; IRType ty = mode64 ? Ity_I64 : Ity_I32; // Check for valid hint values for dcbt and dcbtst as currently described in // ISA 2.07. If valid, then we simply set b21to25 to zero since we have no // means of modeling the hint anyway. if (opc1 == 0x1F && ((opc2 == 0x116) || (opc2 == 0xF6))) { if (b21to25 == 0x10 || b21to25 < 0x10) b21to25 = 0; } if (opc1 == 0x1F && opc2 == 0x116 && b21to25 == 0x11) b21to25 = 0; if (opc1 == 0x1F && opc2 == 0x3F6) { // dcbz if (b21to25 == 1) { is_dcbzl = True; b21to25 = 0; if (!(guest_archinfo->ppc_dcbzl_szB)) { vex_printf("dis_cache_manage(ppc)(dcbzl not supported by host)\n"); return False; } } } if (opc1 != 0x1F || b0 != 0) { if (0) vex_printf("dis_cache_manage %d %d\n", opc1, b0); vex_printf("dis_cache_manage(ppc)(opc1|b0)\n"); return False; } /* stay sane .. */ vassert(lineszB == 16 || lineszB == 32 || lineszB == 64 || lineszB == 128); switch (opc2) { //zz case 0x2F6: // dcba (Data Cache Block Allocate, PPC32 p380) //zz vassert(0); /* AWAITING TEST CASE */ //zz DIP("dcba r%u,r%u\n", rA_addr, rB_addr); //zz if (0) vex_printf("vex ppc->IR: kludged dcba\n"); //zz break; case 0x056: // dcbf (Data Cache Block Flush, PPC32 p382) DIP("dcbf r%u,r%u\n", rA_addr, rB_addr); /* nop as far as vex is concerned */ break; case 0x036: // dcbst (Data Cache Block Store, PPC32 p384) DIP("dcbst r%u,r%u\n", rA_addr, rB_addr); /* nop as far as vex is concerned */ break; case 0x116: // dcbt (Data Cache Block Touch, PPC32 p385) DIP("dcbt r%u,r%u\n", rA_addr, rB_addr); /* nop as far as vex is concerned */ break; case 0x0F6: // dcbtst (Data Cache Block Touch for Store, PPC32 p386) DIP("dcbtst r%u,r%u\n", rA_addr, rB_addr); /* nop as far as vex is concerned */ break; case 0x3F6: { // dcbz (Data Cache Block Clear to Zero, PPC32 p387) // dcbzl (Data Cache Block Clear to Zero Long, bug#135264) /* Clear all bytes in cache block at (rA|0) + rB. */ IRTemp EA = newTemp(ty); IRTemp addr = newTemp(ty); IRExpr* irx_addr; UInt i; UInt clearszB; if (is_dcbzl) { clearszB = guest_archinfo->ppc_dcbzl_szB; DIP("dcbzl r%u,r%u\n", rA_addr, rB_addr); } else { clearszB = guest_archinfo->ppc_dcbz_szB; DIP("dcbz r%u,r%u\n", rA_addr, rB_addr); } assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) ); if (mode64) { /* Round EA down to the start of the containing block. */ assign( addr, binop( Iop_And64, mkexpr(EA), mkU64( ~((ULong)clearszB-1) )) ); for (i = 0; i < clearszB / 8; i++) { irx_addr = binop( Iop_Add64, mkexpr(addr), mkU64(i*8) ); store( irx_addr, mkU64(0) ); } } else { /* Round EA down to the start of the containing block. */ assign( addr, binop( Iop_And32, mkexpr(EA), mkU32( ~(clearszB-1) )) ); for (i = 0; i < clearszB / 4; i++) { irx_addr = binop( Iop_Add32, mkexpr(addr), mkU32(i*4) ); store( irx_addr, mkU32(0) ); } } break; } case 0x3D6: { // icbi (Instruction Cache Block Invalidate, PPC32 p431) /* Invalidate all translations containing code from the cache block at (rA|0) + rB. */ IRTemp EA = newTemp(ty); IRTemp addr = newTemp(ty); DIP("icbi r%u,r%u\n", rA_addr, rB_addr); assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) ); /* Round EA down to the start of the containing block. */ assign( addr, binop( mkSzOp(ty, Iop_And8), mkexpr(EA), mkSzImm(ty, ~(((ULong)lineszB)-1) )) ); putGST( PPC_GST_CMSTART, mkexpr(addr) ); putGST( PPC_GST_CMLEN, mkSzImm(ty, lineszB) ); /* be paranoid ... */ stmt( IRStmt_MBE(Imbe_Fence) ); putGST( PPC_GST_CIA, mkSzImm(ty, nextInsnAddr())); dres->jk_StopHere = Ijk_InvalICache; dres->whatNext = Dis_StopHere; break; } default: vex_printf("dis_cache_manage(ppc)(opc2)\n"); return False; } return True; } /*------------------------------------------------------------*/ /*--- Floating Point Helpers ---*/ /*------------------------------------------------------------*/ /* --------- Synthesise a 2-bit FPU rounding mode. --------- */ /* Produces a value in 0 .. 3, which is encoded as per the type IRRoundingMode. PPCRoundingMode encoding is different to IRRoundingMode, so need to map it. */ static IRExpr* /* :: Ity_I32 */ get_IR_roundingmode ( void ) { /* rounding mode | PPC | IR ------------------------ to nearest | 00 | 00 to zero | 01 | 11 to +infinity | 10 | 10 to -infinity | 11 | 01 */ IRTemp rm_PPC32 = newTemp(Ity_I32); assign( rm_PPC32, getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ) ); // rm_IR = XOR( rm_PPC32, (rm_PPC32 << 1) & 2) return binop( Iop_Xor32, mkexpr(rm_PPC32), binop( Iop_And32, binop(Iop_Shl32, mkexpr(rm_PPC32), mkU8(1)), mkU32(2) )); } /* The DFP IR rounding modes were chosen such that the existing PPC to IR * mapping would still work with the extended three bit DFP rounding * mode designator. * rounding mode | PPC | IR * ----------------------------------------------- * to nearest, ties to even | 000 | 000 * to zero | 001 | 011 * to +infinity | 010 | 010 * to -infinity | 011 | 001 * 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 */ static IRExpr* /* :: Ity_I32 */ get_IR_roundingmode_DFP( void ) { IRTemp rm_PPC32 = newTemp( Ity_I32 ); assign( rm_PPC32, getGST_masked_upper( PPC_GST_FPSCR, MASK_FPSCR_DRN ) ); // rm_IR = XOR( rm_PPC32, (rm_PPC32 << 1) & 2) return binop( Iop_Xor32, mkexpr( rm_PPC32 ), binop( Iop_And32, binop( Iop_Shl32, mkexpr( rm_PPC32 ), mkU8( 1 ) ), mkU32( 2 ) ) ); } #define NANmaskSingle 0x7F800000 #define NANmaskDouble 0x7FF00000 static IRExpr * Check_NaN( IRExpr * value, IRExpr * Hi32Mask ) { IRTemp exp_zero = newTemp(Ity_I8); IRTemp frac_mask = newTemp(Ity_I32); IRTemp frac_not_zero = newTemp(Ity_I8); /* Check if the result is QNAN or SNAN and not +infinity or -infinity. * The input value is always 64-bits, for single precision values, the * lower 32 bits must be zero. * * Single Pricision * [62:54] exponent field is equal to 0xFF for NAN and Infinity. * [53:32] fraction field is zero for Infinity and non-zero for NAN * [31:0] unused for single precision representation * * Double Pricision * [62:51] exponent field is equal to 0xFF for NAN and Infinity. * [50:0] fraction field is zero for Infinity and non-zero for NAN * * Returned result is a U32 value of 0xFFFFFFFF for NaN and 0 otherwise. */ assign( frac_mask, unop( Iop_Not32, binop( Iop_Or32, mkU32( 0x80000000ULL ), Hi32Mask) ) ); assign( exp_zero, unop( Iop_1Sto8, binop( Iop_CmpEQ32, binop( Iop_And32, unop( Iop_64HIto32, unop( Iop_ReinterpF64asI64, value ) ), Hi32Mask ), Hi32Mask ) ) ); assign( frac_not_zero, binop( Iop_Or8, unop( Iop_1Sto8, binop( Iop_CmpNE32, binop( Iop_And32, unop( Iop_64HIto32, unop( Iop_ReinterpF64asI64, value ) ), mkexpr( frac_mask ) ), mkU32( 0x0 ) ) ), unop( Iop_1Sto8, binop( Iop_CmpNE32, binop( Iop_And32, unop( Iop_64to32, unop( Iop_ReinterpF64asI64, value ) ), mkU32( 0xFFFFFFFF ) ), mkU32( 0x0 ) ) ) ) ); return unop( Iop_8Sto32, binop( Iop_And8, mkexpr( exp_zero ), mkexpr( frac_not_zero ) ) ); } static IRExpr * Complement_non_NaN( IRExpr * value, IRExpr * nan_mask ) { /* This function will only complement the 64-bit floating point value if it * is not Nan. NaN is not a signed value. Need to do computations using * 32-bit operands to ensure it will run in 32-bit mode. */ return binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_And32, nan_mask, unop( Iop_64HIto32, unop( Iop_ReinterpF64asI64, value ) ) ), binop( Iop_And32, unop( Iop_Not32, nan_mask ), unop( Iop_64HIto32, unop( Iop_ReinterpF64asI64, unop( Iop_NegF64, value ) ) ) ) ), unop( Iop_64to32, unop( Iop_ReinterpF64asI64, value ) ) ); } /*------------------------------------------------------------*/ /*--- Floating Point Instruction Translation ---*/ /*------------------------------------------------------------*/ /* Floating Point Load Instructions */ static Bool dis_fp_load ( UInt theInstr ) { /* X-Form, D-Form */ UChar opc1 = ifieldOPC(theInstr); UChar frD_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); UInt uimm16 = ifieldUIMM16(theInstr); Int simm16 = extend_s_16to32(uimm16); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA = newTemp(ty); IRTemp rA = newTemp(ty); IRTemp rB = newTemp(ty); IRTemp iHi = newTemp(Ity_I32); IRTemp iLo = newTemp(Ity_I32); assign( rA, getIReg(rA_addr) ); assign( rB, getIReg(rB_addr) ); /* These are completely straightforward from a rounding and status bits perspective: no rounding involved and no funny status or CR bits affected. */ switch (opc1) { case 0x30: // lfs (Load Float Single, PPC32 p441) DIP("lfs fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr); assign( EA, ea_rAor0_simm(rA_addr, simm16) ); putFReg( frD_addr, unop(Iop_F32toF64, load(Ity_F32, mkexpr(EA))) ); break; case 0x31: // lfsu (Load Float Single, Update, PPC32 p442) if (rA_addr == 0) return False; DIP("lfsu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr); assign( EA, ea_rA_simm(rA_addr, simm16) ); putFReg( frD_addr, unop(Iop_F32toF64, load(Ity_F32, mkexpr(EA))) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x32: // lfd (Load Float Double, PPC32 p437) DIP("lfd fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr); assign( EA, ea_rAor0_simm(rA_addr, simm16) ); putFReg( frD_addr, load(Ity_F64, mkexpr(EA)) ); break; case 0x33: // lfdu (Load Float Double, Update, PPC32 p438) if (rA_addr == 0) return False; DIP("lfdu fr%u,%d(r%u)\n", frD_addr, simm16, rA_addr); assign( EA, ea_rA_simm(rA_addr, simm16) ); putFReg( frD_addr, load(Ity_F64, mkexpr(EA)) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x1F: if (b0 != 0) { vex_printf("dis_fp_load(ppc)(instr,b0)\n"); return False; } switch(opc2) { case 0x217: // lfsx (Load Float Single Indexed, PPC32 p444) DIP("lfsx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr); assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) ); putFReg( frD_addr, unop( Iop_F32toF64, load(Ity_F32, mkexpr(EA))) ); break; case 0x237: // lfsux (Load Float Single, Update Indxd, PPC32 p443) if (rA_addr == 0) return False; DIP("lfsux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr); assign( EA, ea_rA_idxd(rA_addr, rB_addr) ); putFReg( frD_addr, unop(Iop_F32toF64, load(Ity_F32, mkexpr(EA))) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x257: // lfdx (Load Float Double Indexed, PPC32 p440) DIP("lfdx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr); assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) ); putFReg( frD_addr, load(Ity_F64, mkexpr(EA)) ); break; case 0x277: // lfdux (Load Float Double, Update Indxd, PPC32 p439) if (rA_addr == 0) return False; DIP("lfdux fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr); assign( EA, ea_rA_idxd(rA_addr, rB_addr) ); putFReg( frD_addr, load(Ity_F64, mkexpr(EA)) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x357: // lfiwax (Load Float As Integer, Indxd, ISA 2.05 p120) DIP("lfiwax fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr); assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) ); assign( iLo, load(Ity_I32, mkexpr(EA)) ); assign( iHi, binop(Iop_Sub32, mkU32(0), binop(Iop_Shr32, mkexpr(iLo), mkU8(31))) ); putFReg( frD_addr, unop(Iop_ReinterpI64asF64, binop(Iop_32HLto64, mkexpr(iHi), mkexpr(iLo))) ); break; case 0x377: // lfiwzx (Load floating-point as integer word, zero indexed { IRTemp dw = newTemp( Ity_I64 ); DIP("lfiwzx fr%u,r%u,r%u\n", frD_addr, rA_addr, rB_addr); assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) ); assign( iLo, load(Ity_I32, mkexpr(EA)) ); assign( dw, binop( Iop_32HLto64, mkU32( 0 ), mkexpr( iLo ) ) ); putFReg( frD_addr, unop( Iop_ReinterpI64asF64, mkexpr( dw ) ) ); break; } default: vex_printf("dis_fp_load(ppc)(opc2)\n"); return False; } break; default: vex_printf("dis_fp_load(ppc)(opc1)\n"); return False; } return True; } /* Floating Point Store Instructions */ static Bool dis_fp_store ( UInt theInstr ) { /* X-Form, D-Form */ UChar opc1 = ifieldOPC(theInstr); UChar frS_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); Int uimm16 = ifieldUIMM16(theInstr); Int simm16 = extend_s_16to32(uimm16); IRTemp frS = newTemp(Ity_F64); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA = newTemp(ty); IRTemp rA = newTemp(ty); IRTemp rB = newTemp(ty); assign( frS, getFReg(frS_addr) ); assign( rA, getIReg(rA_addr) ); assign( rB, getIReg(rB_addr) ); /* These are straightforward from a status bits perspective: no funny status or CR bits affected. For single precision stores, the values are truncated and denormalised (not rounded) to turn them into single precision values. */ switch (opc1) { case 0x34: // stfs (Store Float Single, PPC32 p518) DIP("stfs fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr); assign( EA, ea_rAor0_simm(rA_addr, simm16) ); /* Use Iop_TruncF64asF32 to truncate and possible denormalise the value to be stored in the correct way, without any rounding. */ store( mkexpr(EA), unop(Iop_TruncF64asF32, mkexpr(frS)) ); break; case 0x35: // stfsu (Store Float Single, Update, PPC32 p519) if (rA_addr == 0) return False; DIP("stfsu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr); assign( EA, ea_rA_simm(rA_addr, simm16) ); /* See comment for stfs */ store( mkexpr(EA), unop(Iop_TruncF64asF32, mkexpr(frS)) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x36: // stfd (Store Float Double, PPC32 p513) DIP("stfd fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr); assign( EA, ea_rAor0_simm(rA_addr, simm16) ); store( mkexpr(EA), mkexpr(frS) ); break; case 0x37: // stfdu (Store Float Double, Update, PPC32 p514) if (rA_addr == 0) return False; DIP("stfdu fr%u,%d(r%u)\n", frS_addr, simm16, rA_addr); assign( EA, ea_rA_simm(rA_addr, simm16) ); store( mkexpr(EA), mkexpr(frS) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x1F: if (b0 != 0) { vex_printf("dis_fp_store(ppc)(instr,b0)\n"); return False; } switch(opc2) { case 0x297: // stfsx (Store Float Single Indexed, PPC32 p521) DIP("stfsx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr); assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) ); /* See note for stfs */ store( mkexpr(EA), unop(Iop_TruncF64asF32, mkexpr(frS)) ); break; case 0x2B7: // stfsux (Store Float Sgl, Update Indxd, PPC32 p520) if (rA_addr == 0) return False; DIP("stfsux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr); assign( EA, ea_rA_idxd(rA_addr, rB_addr) ); /* See note for stfs */ store( mkexpr(EA), unop(Iop_TruncF64asF32, mkexpr(frS)) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x2D7: // stfdx (Store Float Double Indexed, PPC32 p516) DIP("stfdx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr); assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) ); store( mkexpr(EA), mkexpr(frS) ); break; case 0x2F7: // stfdux (Store Float Dbl, Update Indxd, PPC32 p515) if (rA_addr == 0) return False; DIP("stfdux fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr); assign( EA, ea_rA_idxd(rA_addr, rB_addr) ); store( mkexpr(EA), mkexpr(frS) ); putIReg( rA_addr, mkexpr(EA) ); break; case 0x3D7: // stfiwx (Store Float as Int, Indexed, PPC32 p517) // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX) DIP("stfiwx fr%u,r%u,r%u\n", frS_addr, rA_addr, rB_addr); assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) ); store( mkexpr(EA), unop(Iop_64to32, unop(Iop_ReinterpF64asI64, mkexpr(frS))) ); break; default: vex_printf("dis_fp_store(ppc)(opc2)\n"); return False; } break; default: vex_printf("dis_fp_store(ppc)(opc1)\n"); return False; } return True; } /* Floating Point Arith Instructions */ static Bool dis_fp_arith ( UInt theInstr ) { /* A-Form */ UChar opc1 = ifieldOPC(theInstr); UChar frD_addr = ifieldRegDS(theInstr); UChar frA_addr = ifieldRegA(theInstr); UChar frB_addr = ifieldRegB(theInstr); UChar frC_addr = ifieldRegC(theInstr); UChar opc2 = ifieldOPClo5(theInstr); UChar flag_rC = ifieldBIT0(theInstr); IRTemp frD = newTemp(Ity_F64); IRTemp frA = newTemp(Ity_F64); IRTemp frB = newTemp(Ity_F64); IRTemp frC = newTemp(Ity_F64); IRExpr* rm = get_IR_roundingmode(); /* By default, we will examine the results of the operation and set fpscr[FPRF] accordingly. */ Bool set_FPRF = True; /* By default, if flag_RC is set, we will clear cr1 after the operation. In reality we should set cr1 to indicate the exception status of the operation, but since we're not simulating exceptions, the exception status will appear to be zero. Hence cr1 should be cleared if this is a . form insn. */ Bool clear_CR1 = True; assign( frA, getFReg(frA_addr)); assign( frB, getFReg(frB_addr)); assign( frC, getFReg(frC_addr)); switch (opc1) { case 0x3B: switch (opc2) { case 0x12: // fdivs (Floating Divide Single, PPC32 p407) if (frC_addr != 0) return False; DIP("fdivs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frB_addr); assign( frD, triop( Iop_DivF64r32, rm, mkexpr(frA), mkexpr(frB) )); break; case 0x14: // fsubs (Floating Subtract Single, PPC32 p430) if (frC_addr != 0) return False; DIP("fsubs%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frB_addr); assign( frD, triop( Iop_SubF64r32, rm, mkexpr(frA), mkexpr(frB) )); break; case 0x15: // fadds (Floating Add Single, PPC32 p401) if (frC_addr != 0) return False; DIP("fadds%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frB_addr); assign( frD, triop( Iop_AddF64r32, rm, mkexpr(frA), mkexpr(frB) )); break; case 0x16: // fsqrts (Floating SqRt (Single-Precision), PPC32 p428) // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX) if (frA_addr != 0 || frC_addr != 0) return False; DIP("fsqrts%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); // however illogically, on ppc970 this insn behaves identically // to fsqrt (double-precision). So use SqrtF64, not SqrtF64r32. assign( frD, binop( Iop_SqrtF64, rm, mkexpr(frB) )); break; case 0x18: // fres (Floating Reciprocal Estimate Single, PPC32 p421) // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX) if (frA_addr != 0 || frC_addr != 0) return False; DIP("fres%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); { IRExpr* ieee_one = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL)); assign( frD, triop( Iop_DivF64r32, rm, ieee_one, mkexpr(frB) )); } break; case 0x19: // fmuls (Floating Multiply Single, PPC32 p414) if (frB_addr != 0) return False; DIP("fmuls%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr); assign( frD, triop( Iop_MulF64r32, rm, mkexpr(frA), mkexpr(frC) )); break; case 0x1A: // frsqrtes (Floating Recip SqRt Est Single) // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX) // Undocumented instruction? if (frA_addr != 0 || frC_addr != 0) return False; DIP("frsqrtes%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( frD, unop(Iop_RSqrtEst5GoodF64, mkexpr(frB)) ); break; default: vex_printf("dis_fp_arith(ppc)(3B: opc2)\n"); return False; } break; case 0x3F: switch (opc2) { case 0x12: // fdiv (Floating Div (Double-Precision), PPC32 p406) if (frC_addr != 0) return False; DIP("fdiv%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frB_addr); assign( frD, triop(Iop_DivF64, rm, mkexpr(frA), mkexpr(frB)) ); break; case 0x14: // fsub (Floating Sub (Double-Precision), PPC32 p429) if (frC_addr != 0) return False; DIP("fsub%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frB_addr); assign( frD, triop(Iop_SubF64, rm, mkexpr(frA), mkexpr(frB)) ); break; case 0x15: // fadd (Floating Add (Double-Precision), PPC32 p400) if (frC_addr != 0) return False; DIP("fadd%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frB_addr); assign( frD, triop(Iop_AddF64, rm, mkexpr(frA), mkexpr(frB)) ); break; case 0x16: // fsqrt (Floating SqRt (Double-Precision), PPC32 p427) // NOTE: POWERPC OPTIONAL, "General-Purpose Group" (PPC32_FX) if (frA_addr != 0 || frC_addr != 0) return False; DIP("fsqrt%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( frD, binop(Iop_SqrtF64, rm, mkexpr(frB)) ); break; case 0x17: { // fsel (Floating Select, PPC32 p426) // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX) IRTemp cc = newTemp(Ity_I32); IRTemp cc_b0 = newTemp(Ity_I32); DIP("fsel%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr, frB_addr); // cc: UN == 0x41, LT == 0x01, GT == 0x00, EQ == 0x40 // => GT|EQ == (cc & 0x1 == 0) assign( cc, binop(Iop_CmpF64, mkexpr(frA), IRExpr_Const(IRConst_F64(0))) ); assign( cc_b0, binop(Iop_And32, mkexpr(cc), mkU32(1)) ); // frD = (frA >= 0.0) ? frC : frB // = (cc_b0 == 0) ? frC : frB assign( frD, IRExpr_ITE( binop(Iop_CmpEQ32, mkexpr(cc_b0), mkU32(0)), mkexpr(frC), mkexpr(frB) )); /* One of the rare ones which don't mess with FPRF */ set_FPRF = False; break; } case 0x18: // fre (Floating Reciprocal Estimate) // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX) // Note: unclear whether this insn really exists or not // ppc970 doesn't have it, but POWER5 does if (frA_addr != 0 || frC_addr != 0) return False; DIP("fre%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); { IRExpr* ieee_one = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL)); assign( frD, triop( Iop_DivF64, rm, ieee_one, mkexpr(frB) )); } break; case 0x19: // fmul (Floating Mult (Double Precision), PPC32 p413) if (frB_addr != 0) vex_printf("dis_fp_arith(ppc)(instr,fmul)\n"); DIP("fmul%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr); assign( frD, triop(Iop_MulF64, rm, mkexpr(frA), mkexpr(frC)) ); break; case 0x1A: // frsqrte (Floating Recip SqRt Est., PPC32 p424) // NOTE: POWERPC OPTIONAL, "Graphics Group" (PPC32_GX) if (frA_addr != 0 || frC_addr != 0) return False; DIP("frsqrte%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( frD, unop(Iop_RSqrtEst5GoodF64, mkexpr(frB)) ); break; default: vex_printf("dis_fp_arith(ppc)(3F: opc2)\n"); return False; } break; default: vex_printf("dis_fp_arith(ppc)(opc1)\n"); return False; } putFReg( frD_addr, mkexpr(frD) ); if (set_FPRF) { // XXX XXX XXX FIXME // set FPRF from frD } if (flag_rC && clear_CR1) { putCR321( 1, mkU8(0) ); putCR0( 1, mkU8(0) ); } return True; } /* Floating Point Mult-Add Instructions */ static Bool dis_fp_multadd ( UInt theInstr ) { /* A-Form */ UChar opc1 = ifieldOPC(theInstr); UChar frD_addr = ifieldRegDS(theInstr); UChar frA_addr = ifieldRegA(theInstr); UChar frB_addr = ifieldRegB(theInstr); UChar frC_addr = ifieldRegC(theInstr); UChar opc2 = ifieldOPClo5(theInstr); UChar flag_rC = ifieldBIT0(theInstr); IRTemp frD = newTemp(Ity_F64); IRTemp frA = newTemp(Ity_F64); IRTemp frB = newTemp(Ity_F64); IRTemp frC = newTemp(Ity_F64); IRTemp rmt = newTemp(Ity_I32); IRTemp tmp = newTemp(Ity_F64); IRTemp sign_tmp = newTemp(Ity_I64); IRTemp nan_mask = newTemp(Ity_I32); IRExpr* rm; /* By default, we will examine the results of the operation and set fpscr[FPRF] accordingly. */ Bool set_FPRF = True; /* By default, if flag_RC is set, we will clear cr1 after the operation. In reality we should set cr1 to indicate the exception status of the operation, but since we're not simulating exceptions, the exception status will appear to be zero. Hence cr1 should be cleared if this is a . form insn. */ Bool clear_CR1 = True; /* Bind the rounding mode expression to a temp; there's no point in creating gratuitous CSEs, as we know we'll need to use it twice. */ assign( rmt, get_IR_roundingmode() ); rm = mkexpr(rmt); assign( frA, getFReg(frA_addr)); assign( frB, getFReg(frB_addr)); assign( frC, getFReg(frC_addr)); /* The rounding in this is all a bit dodgy. The idea is to only do one rounding. That clearly isn't achieveable without dedicated four-input IR primops, although in the single precision case we can sort-of simulate it by doing the inner multiply in double precision. In the negated cases, the negation happens after rounding. */ switch (opc1) { case 0x3B: switch (opc2) { case 0x1C: // fmsubs (Floating Mult-Subtr Single, PPC32 p412) DIP("fmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr, frB_addr); assign( frD, qop( Iop_MSubF64r32, rm, mkexpr(frA), mkexpr(frC), mkexpr(frB) )); break; case 0x1D: // fmadds (Floating Mult-Add Single, PPC32 p409) DIP("fmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr, frB_addr); assign( frD, qop( Iop_MAddF64r32, rm, mkexpr(frA), mkexpr(frC), mkexpr(frB) )); break; case 0x1E: // fnmsubs (Float Neg Mult-Subtr Single, PPC32 p420) case 0x1F: // fnmadds (Floating Negative Multiply-Add Single, PPC32 p418) if (opc2 == 0x1E) { DIP("fnmsubs%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr, frB_addr); assign( tmp, qop( Iop_MSubF64r32, rm, mkexpr(frA), mkexpr(frC), mkexpr(frB) ) ); } else { DIP("fnmadds%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr, frB_addr); assign( tmp, qop( Iop_MAddF64r32, rm, mkexpr(frA), mkexpr(frC), mkexpr(frB) ) ); } assign( nan_mask, Check_NaN( mkexpr( tmp ), mkU32( NANmaskSingle ) ) ); assign( sign_tmp, Complement_non_NaN( mkexpr( tmp ), mkexpr( nan_mask ) ) ); assign( frD, unop( Iop_ReinterpI64asF64, mkexpr( sign_tmp ) ) ); break; default: vex_printf("dis_fp_multadd(ppc)(3B: opc2)\n"); return False; } break; case 0x3F: switch (opc2) { case 0x1C: // fmsub (Float Mult-Sub (Dbl Precision), PPC32 p411) DIP("fmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr, frB_addr); assign( frD, qop( Iop_MSubF64, rm, mkexpr(frA), mkexpr(frC), mkexpr(frB) )); break; case 0x1D: // fmadd (Float Mult-Add (Dbl Precision), PPC32 p408) DIP("fmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr, frB_addr); assign( frD, qop( Iop_MAddF64, rm, mkexpr(frA), mkexpr(frC), mkexpr(frB) )); break; case 0x1E: // fnmsub (Float Neg Mult-Subtr (Dbl Precision), PPC32 p419) case 0x1F: // fnmadd (Float Neg Mult-Add (Dbl Precision), PPC32 p417) if (opc2 == 0x1E) { DIP("fnmsub%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr, frB_addr); assign( tmp, qop( Iop_MSubF64, rm, mkexpr(frA), mkexpr(frC), mkexpr(frB) ) ); } else { DIP("fnmadd%s fr%u,fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frC_addr, frB_addr); assign( tmp, qop( Iop_MAddF64, rm, mkexpr(frA), mkexpr(frC), mkexpr(frB) )); } assign( nan_mask, Check_NaN( mkexpr( tmp ), mkU32( NANmaskDouble ) ) ); assign( sign_tmp, Complement_non_NaN( mkexpr( tmp ), mkexpr( nan_mask ) ) ); assign( frD, unop( Iop_ReinterpI64asF64, mkexpr( sign_tmp ) ) ); break; default: vex_printf("dis_fp_multadd(ppc)(3F: opc2)\n"); return False; } break; default: vex_printf("dis_fp_multadd(ppc)(opc1)\n"); return False; } putFReg( frD_addr, mkexpr(frD) ); if (set_FPRF) { // XXX XXX XXX FIXME // set FPRF from frD } if (flag_rC && clear_CR1) { putCR321( 1, mkU8(0) ); putCR0( 1, mkU8(0) ); } return True; } /* * fe_flag is set to 1 if any of the following conditions occurs: * - The floating-point operand in register FRB is a Zero, a * NaN, an Infinity, or a negative value. * - e_b is less than or equal to: -970 for double precision; -103 for single precision * Otherwise fe_flag is set to 0. * * fg_flag is set to 1 if either of the following conditions occurs. * - The floating-point operand in register FRB is a Zero, an * Infinity, or a denormalized value. * Otherwise fg_flag is set to 0. * */ static void do_fp_tsqrt(IRTemp frB_Int, Bool sp, IRTemp * fe_flag_tmp, IRTemp * fg_flag_tmp) { // The following temps are for holding intermediate results IRTemp e_b = newTemp(Ity_I32); IRExpr * fe_flag, * fg_flag; IRTemp frB_exp_shR = newTemp(Ity_I32); UInt bias = sp? 127 : 1023; IRExpr * frbNaN, * frbDenorm, * frBNeg; IRExpr * eb_LTE; IRTemp frbZero_tmp = newTemp(Ity_I1); IRTemp frbInf_tmp = newTemp(Ity_I1); *fe_flag_tmp = newTemp(Ity_I32); *fg_flag_tmp = newTemp(Ity_I32); assign( frB_exp_shR, fp_exp_part( frB_Int, sp ) ); assign(e_b, binop( Iop_Sub32, mkexpr(frB_exp_shR), mkU32( bias ) )); ////////////////// fe_flag tests BEGIN ////////////////////// /* We first do all tests that may result in setting fe_flag to '1'. * (NOTE: These tests are similar to those used for ftdiv. See do_fp_tdiv() * for details.) */ frbNaN = sp ? is_NaN_32(frB_Int) : is_NaN(frB_Int); assign( frbInf_tmp, is_Inf(frB_Int, sp) ); assign( frbZero_tmp, is_Zero(frB_Int, sp ) ); { // Test_value = -970 for double precision UInt test_value = sp ? 0xffffff99 : 0xfffffc36; eb_LTE = binop( Iop_CmpLE32S, mkexpr( e_b ), mkU32( test_value ) ); } frBNeg = binop( Iop_CmpEQ32, binop( Iop_Shr32, sp ? mkexpr( frB_Int ) : unop( Iop_64HIto32, mkexpr( frB_Int ) ), mkU8( 31 ) ), mkU32( 1 ) ); ////////////////// fe_flag tests END ////////////////////// ////////////////// fg_flag tests BEGIN ////////////////////// /* * The following tests were already performed above in the fe_flag * tests. So these conditions will result in both fe_ and fg_ flags * being set. * - Test if FRB is Zero * - Test if FRB is an Infinity */ /* * Test if FRB holds a denormalized value. A denormalized value is one where * the exp is 0 and the fraction is non-zero. */ if (sp) { IRTemp frac_part = newTemp(Ity_I32); assign( frac_part, binop( Iop_And32, mkexpr(frB_Int), mkU32(0x007fffff)) ); frbDenorm = mkAND1( binop( Iop_CmpEQ32, mkexpr( frB_exp_shR ), mkU32( 0 ) ), binop( Iop_CmpNE32, mkexpr( frac_part ), mkU32( 0 ) ) ); } else { IRExpr * hi32, * low32, * fraction_is_nonzero; IRTemp frac_part = newTemp(Ity_I64); assign( frac_part, FP_FRAC_PART(frB_Int) ); hi32 = unop( Iop_64HIto32, mkexpr( frac_part ) ); low32 = unop( Iop_64to32, mkexpr( frac_part ) ); fraction_is_nonzero = binop( Iop_CmpNE32, binop( Iop_Or32, low32, hi32 ), mkU32( 0 ) ); frbDenorm = mkAND1( binop( Iop_CmpEQ32, mkexpr( frB_exp_shR ), mkU32( 0 ) ), fraction_is_nonzero ); } ////////////////// fg_flag tests END ////////////////////// ///////////////////////// fe_flag = mkOR1( mkexpr( frbZero_tmp ), mkOR1( frbNaN, mkOR1( mkexpr( frbInf_tmp ), mkOR1( frBNeg, eb_LTE ) ) ) ); fe_flag = unop(Iop_1Uto32, fe_flag); fg_flag = mkOR1( mkexpr( frbZero_tmp ), mkOR1( mkexpr( frbInf_tmp ), frbDenorm ) ); fg_flag = unop(Iop_1Uto32, fg_flag); assign (*fg_flag_tmp, fg_flag); assign (*fe_flag_tmp, fe_flag); } /* * fe_flag is set to 1 if any of the following conditions occurs: * - The double-precision floating-point operand in register FRA is a NaN or an * Infinity. * - The double-precision floating-point operand in register FRB is a Zero, a * NaN, or an Infinity. * - e_b is less than or equal to -1022. * - e_b is greater than or equal to 1021. * - The double-precision floating-point operand in register FRA is not a zero * and the difference, e_a - e_b, is greater than or equal to 1023. * - The double-precision floating-point operand in register FRA is not a zero * and the difference, e_a - e_b, is less than or equal to -1021. * - The double-precision floating-point operand in register FRA is not a zero * and e_a is less than or equal to -970 * Otherwise fe_flag is set to 0. * * fg_flag is set to 1 if either of the following conditions occurs. * - The double-precision floating-point operand in register FRA is an Infinity. * - The double-precision floating-point operand in register FRB is a Zero, an * Infinity, or a denormalized value. * Otherwise fg_flag is set to 0. * */ static void _do_fp_tdiv(IRTemp frA_int, IRTemp frB_int, Bool sp, IRTemp * fe_flag_tmp, IRTemp * fg_flag_tmp) { // The following temps are for holding intermediate results IRTemp e_a = newTemp(Ity_I32); IRTemp e_b = newTemp(Ity_I32); IRTemp frA_exp_shR = newTemp(Ity_I32); IRTemp frB_exp_shR = newTemp(Ity_I32); UInt bias = sp? 127 : 1023; *fe_flag_tmp = newTemp(Ity_I32); *fg_flag_tmp = newTemp(Ity_I32); /* The following variables hold boolean results from tests * that are OR'ed together for setting the fe_ and fg_ flags. * For some cases, the booleans are used more than once, so * I make those IRTemp's instead of IRExpr's. */ IRExpr * fraNaN, * frbNaN, * frbDenorm; IRExpr * eb_LTE, * eb_GTE, * ea_eb_GTE, * ea_eb_LTE, * ea_LTE; IRTemp fraInf_tmp = newTemp(Ity_I1); IRTemp frbZero_tmp = newTemp(Ity_I1); IRTemp frbInf_tmp = newTemp(Ity_I1); IRTemp fraNotZero_tmp = newTemp(Ity_I1); /* The following are the flags that are set by OR'ing the results of * all the tests done for tdiv. These flags are the input to the specified CR. */ IRExpr * fe_flag, * fg_flag; // Create temps that will be used throughout the following tests. assign( frA_exp_shR, fp_exp_part( frA_int, sp ) ); assign( frB_exp_shR, fp_exp_part( frB_int, sp ) ); /* Let e_[a|b] be the unbiased exponent: i.e. exp - 1023. */ assign(e_a, binop( Iop_Sub32, mkexpr(frA_exp_shR), mkU32( bias ) )); assign(e_b, binop( Iop_Sub32, mkexpr(frB_exp_shR), mkU32( bias ) )); ////////////////// fe_flag tests BEGIN ////////////////////// /* We first do all tests that may result in setting fe_flag to '1'. */ /* * Test if the double-precision floating-point operand in register FRA is * a NaN: */ fraNaN = sp ? is_NaN_32(frA_int) : is_NaN(frA_int); /* * Test if the double-precision floating-point operand in register FRA is * an Infinity. */ assign(fraInf_tmp, is_Inf(frA_int, sp)); /* * Test if the double-precision floating-point operand in register FRB is * a NaN: */ frbNaN = sp ? is_NaN_32(frB_int) : is_NaN(frB_int); /* * Test if the double-precision floating-point operand in register FRB is * an Infinity. */ assign( frbInf_tmp, is_Inf(frB_int, sp) ); /* * Test if the double-precision floating-point operand in register FRB is * a Zero. */ assign( frbZero_tmp, is_Zero(frB_int, sp) ); /* * Test if e_b <= -1022 for double precision; * or e_b <= -126 for single precision */ { UInt test_value = sp ? 0xffffff82 : 0xfffffc02; eb_LTE = binop(Iop_CmpLE32S, mkexpr(e_b), mkU32(test_value)); } /* * Test if e_b >= 1021 (i.e., 1021 < e_b) for double precision; * or e_b >= -125 (125 < e_b) for single precision */ { Int test_value = sp ? 125 : 1021; eb_GTE = binop(Iop_CmpLT32S, mkU32(test_value), mkexpr(e_b)); } /* * Test if FRA != Zero and (e_a - e_b) >= bias */ assign( fraNotZero_tmp, unop( Iop_Not1, is_Zero( frA_int, sp ) ) ); ea_eb_GTE = mkAND1( mkexpr( fraNotZero_tmp ), binop( Iop_CmpLT32S, mkU32( bias ), binop( Iop_Sub32, mkexpr( e_a ), mkexpr( e_b ) ) ) ); /* * Test if FRA != Zero and (e_a - e_b) <= [-1021 (double precision) or -125 (single precision)] */ { UInt test_value = sp ? 0xffffff83 : 0xfffffc03; ea_eb_LTE = mkAND1( mkexpr( fraNotZero_tmp ), binop( Iop_CmpLE32S, binop( Iop_Sub32, mkexpr( e_a ), mkexpr( e_b ) ), mkU32( test_value ) ) ); } /* * Test if FRA != Zero and e_a <= [-970 (double precision) or -103 (single precision)] */ { UInt test_value = 0xfffffc36; //Int test_value = -970; ea_LTE = mkAND1( mkexpr( fraNotZero_tmp ), binop( Iop_CmpLE32S, mkexpr( e_a ), mkU32( test_value ) ) ); } ////////////////// fe_flag tests END ////////////////////// ////////////////// fg_flag tests BEGIN ////////////////////// /* * The following tests were already performed above in the fe_flag * tests. So these conditions will result in both fe_ and fg_ flags * being set. * - Test if FRA is an Infinity * - Test if FRB ix Zero * - Test if FRB is an Infinity */ /* * Test if FRB holds a denormalized value. A denormalized value is one where * the exp is 0 and the fraction is non-zero. */ { IRExpr * fraction_is_nonzero; if (sp) { fraction_is_nonzero = binop( Iop_CmpNE32, FP_FRAC_PART32(frB_int), mkU32( 0 ) ); } else { IRExpr * hi32, * low32; IRTemp frac_part = newTemp(Ity_I64); assign( frac_part, FP_FRAC_PART(frB_int) ); hi32 = unop( Iop_64HIto32, mkexpr( frac_part ) ); low32 = unop( Iop_64to32, mkexpr( frac_part ) ); fraction_is_nonzero = binop( Iop_CmpNE32, binop( Iop_Or32, low32, hi32 ), mkU32( 0 ) ); } frbDenorm = mkAND1( binop( Iop_CmpEQ32, mkexpr( frB_exp_shR ), mkU32( 0x0 ) ), fraction_is_nonzero ); } ////////////////// fg_flag tests END ////////////////////// fe_flag = mkOR1( fraNaN, mkOR1( mkexpr( fraInf_tmp ), mkOR1( mkexpr( frbZero_tmp ), mkOR1( frbNaN, mkOR1( mkexpr( frbInf_tmp ), mkOR1( eb_LTE, mkOR1( eb_GTE, mkOR1( ea_eb_GTE, mkOR1( ea_eb_LTE, ea_LTE ) ) ) ) ) ) ) ) ); fe_flag = unop(Iop_1Uto32, fe_flag); fg_flag = mkOR1( mkexpr( fraInf_tmp ), mkOR1( mkexpr( frbZero_tmp ), mkOR1( mkexpr( frbInf_tmp ), frbDenorm ) ) ); fg_flag = unop(Iop_1Uto32, fg_flag); assign(*fe_flag_tmp, fe_flag); assign(*fg_flag_tmp, fg_flag); } /* See description for _do_fp_tdiv() above. */ static IRExpr * do_fp_tdiv(IRTemp frA_int, IRTemp frB_int) { IRTemp fe_flag, fg_flag; ///////////////////////// /* The CR field consists of fl_flag || fg_flag || fe_flag || 0b0 * where fl_flag == 1 on ppc64. */ IRExpr * fl_flag = unop(Iop_Not32, mkU32(0xFFFFFE)); fe_flag = fg_flag = IRTemp_INVALID; _do_fp_tdiv(frA_int, frB_int, False/*not single precision*/, &fe_flag, &fg_flag); return binop( Iop_Or32, binop( Iop_Or32, binop( Iop_Shl32, fl_flag, mkU8( 3 ) ), binop( Iop_Shl32, mkexpr(fg_flag), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag), mkU8( 1 ) ) ); } static Bool dis_fp_tests ( UInt theInstr ) { UChar opc1 = ifieldOPC(theInstr); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); UChar frB_addr = ifieldRegB(theInstr); UChar b0 = ifieldBIT0(theInstr); UInt opc2 = ifieldOPClo10(theInstr); IRTemp frB_I64 = newTemp(Ity_I64); if (opc1 != 0x3F || b0 != 0 ){ vex_printf("dis_fp_tests(ppc)(ftdiv)\n"); return False; } assign( frB_I64, unop( Iop_ReinterpF64asI64, getFReg( frB_addr ) ) ); switch (opc2) { case 0x080: // ftdiv { UChar frA_addr = ifieldRegA(theInstr); IRTemp frA_I64 = newTemp(Ity_I64); UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) ); if (b21to22 != 0 ) { vex_printf("dis_fp_tests(ppc)(ftdiv)\n"); return False; } assign( frA_I64, unop( Iop_ReinterpF64asI64, getFReg( frA_addr ) ) ); putGST_field( PPC_GST_CR, do_fp_tdiv(frA_I64, frB_I64), crfD ); DIP("ftdiv crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr); break; } case 0x0A0: // ftsqrt { IRTemp flags = newTemp(Ity_I32); IRTemp fe_flag, fg_flag; fe_flag = fg_flag = IRTemp_INVALID; UChar b18to22 = toUChar( IFIELD( theInstr, 18, 5 ) ); if ( b18to22 != 0) { vex_printf("dis_fp_tests(ppc)(ftsqrt)\n"); return False; } DIP("ftsqrt crf%d,fr%u\n", crfD, frB_addr); do_fp_tsqrt(frB_I64, False /* not single precision*/, &fe_flag, &fg_flag); /* The CR field consists of fl_flag || fg_flag || fe_flag || 0b0 * where fl_flag == 1 on ppc64. */ assign( flags, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag), mkU8( 1 ) ) ) ); putGST_field( PPC_GST_CR, mkexpr(flags), crfD ); break; } default: vex_printf("dis_fp_tests(ppc)(opc2)\n"); return False; } return True; } /* Floating Point Compare Instructions */ static Bool dis_fp_cmp ( UInt theInstr ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) ); UChar frA_addr = ifieldRegA(theInstr); UChar frB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRTemp ccIR = newTemp(Ity_I32); IRTemp ccPPC32 = newTemp(Ity_I32); IRTemp frA = newTemp(Ity_F64); IRTemp frB = newTemp(Ity_F64); if (opc1 != 0x3F || b21to22 != 0 || b0 != 0) { vex_printf("dis_fp_cmp(ppc)(instr)\n"); return False; } assign( frA, getFReg(frA_addr)); assign( frB, getFReg(frB_addr)); assign( ccIR, binop(Iop_CmpF64, mkexpr(frA), mkexpr(frB)) ); /* Map compare result from IR to PPC32 */ /* FP cmp result | PPC | IR -------------------------- UN | 0x1 | 0x45 EQ | 0x2 | 0x40 GT | 0x4 | 0x00 LT | 0x8 | 0x01 */ // ccPPC32 = Shl(1, (~(ccIR>>5) & 2) // | ((ccIR ^ (ccIR>>6)) & 1) assign( ccPPC32, binop( Iop_Shl32, mkU32(1), unop( Iop_32to8, binop( Iop_Or32, binop( Iop_And32, unop( Iop_Not32, binop(Iop_Shr32, mkexpr(ccIR), mkU8(5)) ), mkU32(2) ), binop( Iop_And32, binop( Iop_Xor32, mkexpr(ccIR), binop(Iop_Shr32, mkexpr(ccIR), mkU8(6)) ), mkU32(1) ) ) ) ) ); putGST_field( PPC_GST_CR, mkexpr(ccPPC32), crfD ); /* CAB: TODO?: Support writing cc to FPSCR->FPCC ? putGST_field( PPC_GST_FPSCR, mkexpr(ccPPC32), 4 ); */ // XXX XXX XXX FIXME // Also write the result into FPRF (it's not entirely clear how) /* Note: Differences between fcmpu and fcmpo are only in exception flag settings, which aren't supported anyway. */ switch (opc2) { case 0x000: // fcmpu (Floating Compare Unordered, PPC32 p403) DIP("fcmpu crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr); break; case 0x020: // fcmpo (Floating Compare Ordered, PPC32 p402) DIP("fcmpo crf%d,fr%u,fr%u\n", crfD, frA_addr, frB_addr); break; default: vex_printf("dis_fp_cmp(ppc)(opc2)\n"); return False; } return True; } /* Floating Point Rounding/Conversion Instructions */ static Bool dis_fp_round ( UInt theInstr ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar b16to20 = ifieldRegA(theInstr); UChar frD_addr = ifieldRegDS(theInstr); UChar frB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar flag_rC = ifieldBIT0(theInstr); IRTemp frD = newTemp(Ity_F64); IRTemp frB = newTemp(Ity_F64); IRTemp r_tmp32 = newTemp(Ity_I32); IRTemp r_tmp64 = newTemp(Ity_I64); IRExpr* rm = get_IR_roundingmode(); /* By default, we will examine the results of the operation and set fpscr[FPRF] accordingly. */ Bool set_FPRF = True; /* By default, if flag_RC is set, we will clear cr1 after the operation. In reality we should set cr1 to indicate the exception status of the operation, but since we're not simulating exceptions, the exception status will appear to be zero. Hence cr1 should be cleared if this is a . form insn. */ Bool clear_CR1 = True; if ((!(opc1 == 0x3F || opc1 == 0x3B)) || b16to20 != 0) { vex_printf("dis_fp_round(ppc)(instr)\n"); return False; } assign( frB, getFReg(frB_addr)); if (opc1 == 0x3B) { /* The fcfid[u]s instructions (from ISA 2.06) are a bit odd because * they're very similar to the other instructions handled here, but have * a different primary opcode. */ switch (opc2) { case 0x34E: // fcfids (Float convert from signed DWord to single precision) DIP("fcfids%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, unop( Iop_ReinterpF64asI64, mkexpr(frB)) ); assign( frD, binop( Iop_RoundF64toF32, rm, binop( Iop_I64StoF64, rm, mkexpr( r_tmp64 ) ) ) ); goto putFR; case 0x3Ce: // fcfidus (Float convert from unsigned DWord to single precision) DIP("fcfidus%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, unop( Iop_ReinterpF64asI64, mkexpr(frB)) ); assign( frD, unop( Iop_F32toF64, binop( Iop_I64UtoF32, rm, mkexpr( r_tmp64 ) ) ) ); goto putFR; } } switch (opc2) { case 0x00C: // frsp (Float Round to Single, PPC32 p423) DIP("frsp%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( frD, binop( Iop_RoundF64toF32, rm, mkexpr(frB) )); break; case 0x00E: // fctiw (Float Conv to Int, PPC32 p404) DIP("fctiw%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp32, binop(Iop_F64toI32S, rm, mkexpr(frB)) ); assign( frD, unop( Iop_ReinterpI64asF64, unop( Iop_32Uto64, mkexpr(r_tmp32)))); /* FPRF is undefined after fctiw. Leave unchanged. */ set_FPRF = False; break; case 0x00F: // fctiwz (Float Conv to Int, Round to Zero, PPC32 p405) DIP("fctiwz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp32, binop(Iop_F64toI32S, mkU32(Irrm_ZERO), mkexpr(frB) )); assign( frD, unop( Iop_ReinterpI64asF64, unop( Iop_32Uto64, mkexpr(r_tmp32)))); /* FPRF is undefined after fctiwz. Leave unchanged. */ set_FPRF = False; break; case 0x08F: case 0x08E: // fctiwu[z] DIP("fctiwu%s%s fr%u,fr%u\n", opc2 == 0x08F ? "z" : "", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp32, binop( Iop_F64toI32U, opc2 == 0x08F ? mkU32( Irrm_ZERO ) : rm, mkexpr( frB ) ) ); assign( frD, unop( Iop_ReinterpI64asF64, unop( Iop_32Uto64, mkexpr(r_tmp32)))); /* FPRF is undefined after fctiwz. Leave unchanged. */ set_FPRF = False; break; case 0x32E: // fctid (Float Conv to Int DWord, PPC64 p437) DIP("fctid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, binop(Iop_F64toI64S, rm, mkexpr(frB)) ); assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) ); /* FPRF is undefined after fctid. Leave unchanged. */ set_FPRF = False; break; case 0x32F: // fctidz (Float Conv to Int DWord, Round to Zero, PPC64 p437) DIP("fctidz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, binop(Iop_F64toI64S, mkU32(Irrm_ZERO), mkexpr(frB)) ); assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) ); /* FPRF is undefined after fctidz. Leave unchanged. */ set_FPRF = False; break; case 0x3AE: case 0x3AF: // fctidu[z] (Float Conv to Int DWord Unsigned [Round to Zero]) { DIP("fctidu%s%s fr%u,fr%u\n", opc2 == 0x3AE ? "" : "z", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, binop(Iop_F64toI64U, opc2 == 0x3AE ? rm : mkU32(Irrm_ZERO), mkexpr(frB)) ); assign( frD, unop( Iop_ReinterpI64asF64, mkexpr(r_tmp64)) ); /* FPRF is undefined after fctidz. Leave unchanged. */ set_FPRF = False; break; } case 0x34E: // fcfid (Float Conv from Int DWord, PPC64 p434) DIP("fcfid%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, unop( Iop_ReinterpF64asI64, mkexpr(frB)) ); assign( frD, binop(Iop_I64StoF64, rm, mkexpr(r_tmp64)) ); break; case 0x3CE: // fcfidu (Float convert from unsigned DWord) DIP("fcfidu%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, unop( Iop_ReinterpF64asI64, mkexpr(frB)) ); assign( frD, binop( Iop_I64UtoF64, rm, mkexpr( r_tmp64 ) ) ); break; case 0x188: case 0x1A8: case 0x1C8: case 0x1E8: // frin, friz, frip, frim switch(opc2) { case 0x188: // frin (Floating Round to Integer Nearest) DIP("frin%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, binop(Iop_F64toI64S, mkU32(Irrm_NEAREST), mkexpr(frB)) ); break; case 0x1A8: // friz (Floating Round to Integer Toward Zero) DIP("friz%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, binop(Iop_F64toI64S, mkU32(Irrm_ZERO), mkexpr(frB)) ); break; case 0x1C8: // frip (Floating Round to Integer Plus) DIP("frip%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, binop(Iop_F64toI64S, mkU32(Irrm_PosINF), mkexpr(frB)) ); break; case 0x1E8: // frim (Floating Round to Integer Minus) DIP("frim%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( r_tmp64, binop(Iop_F64toI64S, mkU32(Irrm_NegINF), mkexpr(frB)) ); break; } /* don't use the rounded integer if frB is outside -9e18..9e18 */ /* F64 has only log10(2**52) significant digits anyway */ /* need to preserve sign of zero */ /* frD = (fabs(frB) > 9e18) ? frB : (sign(frB)) ? -fabs((double)r_tmp64) : (double)r_tmp64 */ assign(frD, IRExpr_ITE( binop(Iop_CmpNE8, unop(Iop_32to8, binop(Iop_CmpF64, IRExpr_Const(IRConst_F64(9e18)), unop(Iop_AbsF64, mkexpr(frB)))), mkU8(0)), mkexpr(frB), IRExpr_ITE( binop(Iop_CmpNE32, binop(Iop_Shr32, unop(Iop_64HIto32, unop(Iop_ReinterpF64asI64, mkexpr(frB))), mkU8(31)), mkU32(0)), unop(Iop_NegF64, unop( Iop_AbsF64, binop(Iop_I64StoF64, mkU32(0), mkexpr(r_tmp64)) )), binop(Iop_I64StoF64, mkU32(0), mkexpr(r_tmp64) ) ) )); break; default: vex_printf("dis_fp_round(ppc)(opc2)\n"); return False; } putFR: putFReg( frD_addr, mkexpr(frD) ); if (set_FPRF) { // XXX XXX XXX FIXME // set FPRF from frD } if (flag_rC && clear_CR1) { putCR321( 1, mkU8(0) ); putCR0( 1, mkU8(0) ); } return True; } /* Floating Point Pair Instructions */ static Bool dis_fp_pair ( UInt theInstr ) { /* X-Form/DS-Form */ UChar opc1 = ifieldOPC(theInstr); UChar frT_hi_addr = ifieldRegDS(theInstr); UChar frT_lo_addr = frT_hi_addr + 1; UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt uimm16 = ifieldUIMM16(theInstr); Int simm16 = extend_s_16to32(uimm16); UInt opc2 = ifieldOPClo10(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA_hi = newTemp(ty); IRTemp EA_lo = newTemp(ty); IRTemp frT_hi = newTemp(Ity_F64); IRTemp frT_lo = newTemp(Ity_F64); UChar b0 = ifieldBIT0(theInstr); Bool is_load = 0; if ((frT_hi_addr %2) != 0) { vex_printf("dis_fp_pair(ppc) : odd frT register\n"); return False; } switch (opc1) { case 0x1F: // register offset switch(opc2) { case 0x317: // lfdpx (FP Load Double Pair X-form, ISA 2.05 p125) DIP("ldpx fr%u,r%u,r%u\n", frT_hi_addr, rA_addr, rB_addr); is_load = 1; break; case 0x397: // stfdpx (FP STORE Double Pair X-form, ISA 2.05 p125) DIP("stdpx fr%u,r%u,r%u\n", frT_hi_addr, rA_addr, rB_addr); break; default: vex_printf("dis_fp_pair(ppc) : X-form wrong opc2\n"); return False; } if (b0 != 0) { vex_printf("dis_fp_pair(ppc)(0x1F,b0)\n"); return False; } assign( EA_hi, ea_rAor0_idxd( rA_addr, rB_addr ) ); break; case 0x39: // lfdp (FP Load Double Pair DS-form, ISA 2.05 p125) DIP("lfdp fr%u,%d(r%u)\n", frT_hi_addr, simm16, rA_addr); assign( EA_hi, ea_rAor0_simm( rA_addr, simm16 ) ); is_load = 1; break; case 0x3d: // stfdp (FP Store Double Pair DS-form, ISA 2.05 p125) DIP("stfdp fr%u,%d(r%u)\n", frT_hi_addr, simm16, rA_addr); assign( EA_hi, ea_rAor0_simm( rA_addr, simm16 ) ); break; default: // immediate offset vex_printf("dis_fp_pair(ppc)(instr)\n"); return False; } if (mode64) assign( EA_lo, binop(Iop_Add64, mkexpr(EA_hi), mkU64(8)) ); else assign( EA_lo, binop(Iop_Add32, mkexpr(EA_hi), mkU32(8)) ); assign( frT_hi, getFReg(frT_hi_addr) ); assign( frT_lo, getFReg(frT_lo_addr) ); if (is_load) { putFReg( frT_hi_addr, load(Ity_F64, mkexpr(EA_hi)) ); putFReg( frT_lo_addr, load(Ity_F64, mkexpr(EA_lo)) ); } else { store( mkexpr(EA_hi), mkexpr(frT_hi) ); store( mkexpr(EA_lo), mkexpr(frT_lo) ); } return True; } /* Floating Point Merge Instructions */ static Bool dis_fp_merge ( UInt theInstr ) { /* X-Form */ UInt opc2 = ifieldOPClo10(theInstr); UChar frD_addr = ifieldRegDS(theInstr); UChar frA_addr = ifieldRegA(theInstr); UChar frB_addr = ifieldRegB(theInstr); IRTemp frD = newTemp(Ity_F64); IRTemp frA = newTemp(Ity_F64); IRTemp frB = newTemp(Ity_F64); assign( frA, getFReg(frA_addr)); assign( frB, getFReg(frB_addr)); switch (opc2) { case 0x3c6: // fmrgew floating merge even word DIP("fmrgew fr%u,fr%u,fr%u\n", frD_addr, frA_addr, frB_addr); assign( frD, unop( Iop_ReinterpI64asF64, binop( Iop_32HLto64, unop( Iop_64HIto32, unop( Iop_ReinterpF64asI64, mkexpr(frA) ) ), unop( Iop_64HIto32, unop( Iop_ReinterpF64asI64, mkexpr(frB) ) ) ) ) ); break; case 0x346: // fmrgow floating merge odd word DIP("fmrgow fr%u,fr%u,fr%u\n", frD_addr, frA_addr, frB_addr); assign( frD, unop( Iop_ReinterpI64asF64, binop( Iop_32HLto64, unop( Iop_64to32, unop( Iop_ReinterpF64asI64, mkexpr(frA) ) ), unop( Iop_64to32, unop( Iop_ReinterpF64asI64, mkexpr(frB) ) ) ) ) ); break; default: vex_printf("dis_fp_merge(ppc)(opc2)\n"); return False; } putFReg( frD_addr, mkexpr(frD) ); return True; } /* Floating Point Move Instructions */ static Bool dis_fp_move ( UInt theInstr ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar frD_addr = ifieldRegDS(theInstr); UChar frA_addr = ifieldRegA(theInstr); UChar frB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar flag_rC = ifieldBIT0(theInstr); IRTemp frD = newTemp(Ity_F64); IRTemp frB = newTemp(Ity_F64); IRTemp itmpB = newTemp(Ity_F64); IRTemp frA; IRTemp signA; IRTemp hiD; if (opc1 != 0x3F || (frA_addr != 0 && opc2 != 0x008)) { vex_printf("dis_fp_move(ppc)(instr)\n"); return False; } assign( frB, getFReg(frB_addr)); switch (opc2) { case 0x008: // fcpsgn (Floating Copy Sign, ISA_V2.05 p126) DIP("fcpsgn%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frA_addr, frB_addr); signA = newTemp(Ity_I32); hiD = newTemp(Ity_I32); itmpB = newTemp(Ity_I64); frA = newTemp(Ity_F64); assign( frA, getFReg(frA_addr) ); /* get A's sign bit */ assign(signA, binop(Iop_And32, unop(Iop_64HIto32, unop(Iop_ReinterpF64asI64, mkexpr(frA))), mkU32(0x80000000)) ); assign( itmpB, unop(Iop_ReinterpF64asI64, mkexpr(frB)) ); /* mask off B's sign bit and or in A's sign bit */ assign(hiD, binop(Iop_Or32, binop(Iop_And32, unop(Iop_64HIto32, mkexpr(itmpB)), /* frB's high 32 bits */ mkU32(0x7fffffff)), mkexpr(signA)) ); /* combine hiD/loB into frD */ assign( frD, unop(Iop_ReinterpI64asF64, binop(Iop_32HLto64, mkexpr(hiD), unop(Iop_64to32, mkexpr(itmpB)))) ); /* frB's low 32 bits */ break; case 0x028: // fneg (Floating Negate, PPC32 p416) DIP("fneg%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( frD, unop( Iop_NegF64, mkexpr(frB) )); break; case 0x048: // fmr (Floating Move Register, PPC32 p410) DIP("fmr%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( frD, mkexpr(frB) ); break; case 0x088: // fnabs (Floating Negative Absolute Value, PPC32 p415) DIP("fnabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( frD, unop( Iop_NegF64, unop( Iop_AbsF64, mkexpr(frB) ))); break; case 0x108: // fabs (Floating Absolute Value, PPC32 p399) DIP("fabs%s fr%u,fr%u\n", flag_rC ? ".":"", frD_addr, frB_addr); assign( frD, unop( Iop_AbsF64, mkexpr(frB) )); break; default: vex_printf("dis_fp_move(ppc)(opc2)\n"); return False; } putFReg( frD_addr, mkexpr(frD) ); /* None of these change FPRF. cr1 is set in the usual way though, if flag_rC is set. */ if (flag_rC) { putCR321( 1, mkU8(0) ); putCR0( 1, mkU8(0) ); } return True; } /* Floating Point Status/Control Register Instructions */ static Bool dis_fp_scr ( UInt theInstr, Bool GX_level ) { /* Many forms - see each switch case */ UChar opc1 = ifieldOPC(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar flag_rC = ifieldBIT0(theInstr); if (opc1 != 0x3F) { vex_printf("dis_fp_scr(ppc)(instr)\n"); return False; } switch (opc2) { case 0x026: { // mtfsb1 (Move to FPSCR Bit 1, PPC32 p479) // Bit crbD of the FPSCR is set. UChar crbD = ifieldRegDS(theInstr); UInt b11to20 = IFIELD(theInstr, 11, 10); if (b11to20 != 0) { vex_printf("dis_fp_scr(ppc)(instr,mtfsb1)\n"); return False; } DIP("mtfsb1%s crb%d \n", flag_rC ? ".":"", crbD); putGST_masked( PPC_GST_FPSCR, mkU64( 1 <<( 31 - crbD ) ), 1ULL << ( 31 - crbD ) ); break; } case 0x040: { // mcrfs (Move to Condition Register from FPSCR, PPC32 p465) UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); UChar b21to22 = toUChar( IFIELD( theInstr, 21, 2 ) ); UChar crfS = toUChar( IFIELD( theInstr, 18, 3 ) ); UChar b11to17 = toUChar( IFIELD( theInstr, 11, 7 ) ); IRTemp tmp = newTemp(Ity_I32); IRExpr* fpscr_all; if (b21to22 != 0 || b11to17 != 0 || flag_rC != 0) { vex_printf("dis_fp_scr(ppc)(instr,mcrfs)\n"); return False; } DIP("mcrfs crf%d,crf%d\n", crfD, crfS); vassert(crfD < 8); vassert(crfS < 8); fpscr_all = getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ); assign( tmp, binop(Iop_And32, binop(Iop_Shr32,fpscr_all,mkU8(4 * (7-crfS))), mkU32(0xF)) ); putGST_field( PPC_GST_CR, mkexpr(tmp), crfD ); break; } case 0x046: { // mtfsb0 (Move to FPSCR Bit 0, PPC32 p478) // Bit crbD of the FPSCR is cleared. UChar crbD = ifieldRegDS(theInstr); UInt b11to20 = IFIELD(theInstr, 11, 10); if (b11to20 != 0) { vex_printf("dis_fp_scr(ppc)(instr,mtfsb0)\n"); return False; } DIP("mtfsb0%s crb%d\n", flag_rC ? ".":"", crbD); putGST_masked( PPC_GST_FPSCR, mkU64( 0 ), 1ULL << ( 31 - crbD ) ); break; } case 0x086: { // mtfsfi (Move to FPSCR Field Immediate, PPC32 p481) UInt crfD = IFIELD( theInstr, 23, 3 ); UChar b16to22 = toUChar( IFIELD( theInstr, 16, 7 ) ); UChar IMM = toUChar( IFIELD( theInstr, 12, 4 ) ); UChar b11 = toUChar( IFIELD( theInstr, 11, 1 ) ); UChar Wbit; if (b16to22 != 0 || b11 != 0) { vex_printf("dis_fp_scr(ppc)(instr,mtfsfi)\n"); return False; } DIP("mtfsfi%s crf%u,%d\n", flag_rC ? ".":"", crfD, IMM); if (GX_level) { /* This implies that Decimal Floating Point is supported, and the * FPSCR must be managed as a 64-bit register. */ Wbit = toUChar( IFIELD(theInstr, 16, 1) ); } else { Wbit = 0; } crfD = crfD + (8 * (1 - Wbit) ); putGST_field( PPC_GST_FPSCR, mkU32( IMM ), crfD ); break; } case 0x247: { // mffs (Move from FPSCR, PPC32 p468) UChar frD_addr = ifieldRegDS(theInstr); UInt b11to20 = IFIELD(theInstr, 11, 10); IRExpr* fpscr_lower = getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN ); IRExpr* fpscr_upper = getGST_masked_upper( PPC_GST_FPSCR, MASK_FPSCR_DRN ); if (b11to20 != 0) { vex_printf("dis_fp_scr(ppc)(instr,mffs)\n"); return False; } DIP("mffs%s fr%u\n", flag_rC ? ".":"", frD_addr); putFReg( frD_addr, unop( Iop_ReinterpI64asF64, binop( Iop_32HLto64, fpscr_upper, fpscr_lower ) ) ); break; } case 0x2C7: { // mtfsf (Move to FPSCR Fields, PPC32 p480) UChar b25 = toUChar( IFIELD(theInstr, 25, 1) ); UChar FM = toUChar( IFIELD(theInstr, 17, 8) ); UChar frB_addr = ifieldRegB(theInstr); IRTemp frB = newTemp(Ity_F64); IRTemp rB_64 = newTemp( Ity_I64 ); Int i; ULong mask; UChar Wbit; #define BFP_MASK_SEED 0x3000000000000000ULL #define DFP_MASK_SEED 0x7000000000000000ULL if (GX_level) { /* This implies that Decimal Floating Point is supported, and the * FPSCR must be managed as a 64-bit register. */ Wbit = toUChar( IFIELD(theInstr, 16, 1) ); } else { Wbit = 0; } if (b25 == 1) { /* new 64 bit move variant for power 6. If L field (bit 25) is * a one do a full 64 bit move. Note, the FPSCR is not really * properly modeled. This instruciton only changes the value of * the rounding mode. The HW exception bits do not get set in * the simulator. 1/12/09 */ DIP("mtfsf%s %d,fr%u (L=1)\n", flag_rC ? ".":"", FM, frB_addr); mask = 0xFF; } else { DIP("mtfsf%s %d,fr%u\n", flag_rC ? ".":"", FM, frB_addr); // Build 32bit mask from FM: mask = 0; for (i=0; i<8; i++) { if ((FM & (1<<(7-i))) == 1) { /* FPSCR field k is set to the contents of the corresponding * field of register FRB, where k = i+8x(1-W). In the Power * ISA, register field numbering is from left to right, so field * 15 is the least significant field in a 64-bit register. To * generate the mask, we set all the appropriate rounding mode * bits in the highest order nibble (field 0) and shift right * 'k x nibble length'. */ if (Wbit) mask |= DFP_MASK_SEED >> ( 4 * ( i + 8 * ( 1 - Wbit ) ) ); else mask |= BFP_MASK_SEED >> ( 4 * ( i + 8 * ( 1 - Wbit ) ) ); } } } assign( frB, getFReg(frB_addr)); assign( rB_64, unop( Iop_ReinterpF64asI64, mkexpr( frB ) ) ); putGST_masked( PPC_GST_FPSCR, mkexpr( rB_64 ), mask ); break; } default: vex_printf("dis_fp_scr(ppc)(opc2)\n"); return False; } return True; } /*------------------------------------------------------------*/ /*--- Decimal Floating Point (DFP) Helper functions ---*/ /*------------------------------------------------------------*/ #define DFP_LONG 1 #define DFP_EXTND 2 #define DFP_LONG_BIAS 398 #define DFP_LONG_ENCODED_FIELD_MASK 0x1F00 #define DFP_EXTND_BIAS 6176 #define DFP_EXTND_ENCODED_FIELD_MASK 0x1F000 #define DFP_LONG_EXP_MSK 0XFF #define DFP_EXTND_EXP_MSK 0XFFF #define DFP_G_FIELD_LONG_MASK 0x7FFC0000 // upper 32-bits only #define DFP_LONG_GFIELD_RT_SHIFT (63 - 13 - 32) // adj for upper 32-bits #define DFP_G_FIELD_EXTND_MASK 0x7FFFC000 // upper 32-bits only #define DFP_EXTND_GFIELD_RT_SHIFT (63 - 17 - 32) //adj for upper 32 bits #define DFP_T_FIELD_LONG_MASK 0x3FFFF // mask for upper 32-bits #define DFP_T_FIELD_EXTND_MASK 0x03FFFF // mask for upper 32-bits #define DFP_LONG_EXP_MAX 369 // biased max #define DFP_LONG_EXP_MIN 0 // biased min #define DFP_EXTND_EXP_MAX 6111 // biased max #define DFP_EXTND_EXP_MIN 0 // biased min #define DFP_LONG_MAX_SIG_DIGITS 16 #define DFP_EXTND_MAX_SIG_DIGITS 34 #define MAX_DIGITS_IN_STRING 8 #define AND(x, y) binop( Iop_And32, x, y ) #define AND4(w, x, y, z) AND( AND( w, x ), AND( y, z ) ) #define OR(x, y) binop( Iop_Or32, x, y ) #define OR3(x, y, z) OR( x, OR( y, z ) ) #define OR4(w, x, y, z) OR( OR( w, x ), OR( y, z ) ) #define NOT(x) unop( Iop_1Uto32, unop( Iop_Not1, unop( Iop_32to1, mkexpr( x ) ) ) ) #define SHL(value, by) binop( Iop_Shl32, value, mkU8( by ) ) #define SHR(value, by) binop( Iop_Shr32, value, mkU8( by ) ) #define BITS5(_b4,_b3,_b2,_b1,_b0) \ (((_b4) << 4) | ((_b3) << 3) | ((_b2) << 2) | \ ((_b1) << 1) | ((_b0) << 0)) static IRExpr * Gfield_encoding( IRExpr * lmexp, IRExpr * lmd32 ) { IRTemp lmd_07_mask = newTemp( Ity_I32 ); IRTemp lmd_8_mask = newTemp( Ity_I32 ); IRTemp lmd_9_mask = newTemp( Ity_I32 ); IRTemp lmexp_00_mask = newTemp( Ity_I32 ); IRTemp lmexp_01_mask = newTemp( Ity_I32 ); IRTemp lmexp_10_mask = newTemp( Ity_I32 ); IRTemp lmd_07_val = newTemp( Ity_I32 ); IRTemp lmd_8_val = newTemp( Ity_I32 ); IRTemp lmd_9_val = newTemp( Ity_I32 ); /* The encodig is as follows: * lmd - left most digit * lme - left most 2-bits of the exponent * * lmd * 0 - 7 (lmexp << 3) | lmd * 8 0b11000 (24 decimal) if lme=0b00; * 0b11010 (26 decimal) if lme=0b01; * 0b11100 (28 decimal) if lme=0b10; * 9 0b11001 (25 decimal) if lme=0b00; * 0b11011 (27 decimal) if lme=0b01; * 0b11101 (29 decimal) if lme=0b10; */ /* Generate the masks for each condition */ assign( lmd_07_mask, unop( Iop_1Sto32, binop( Iop_CmpLE32U, lmd32, mkU32( 7 ) ) ) ); assign( lmd_8_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, lmd32, mkU32( 8 ) ) ) ); assign( lmd_9_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, lmd32, mkU32( 9 ) ) ) ); assign( lmexp_00_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, lmexp, mkU32( 0 ) ) ) ); assign( lmexp_01_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, lmexp, mkU32( 1 ) ) ) ); assign( lmexp_10_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, lmexp, mkU32( 2 ) ) ) ); /* Generate the values for each LMD condition, assuming the condition * is TRUE. */ assign( lmd_07_val, binop( Iop_Or32, binop( Iop_Shl32, lmexp, mkU8( 3 ) ), lmd32 ) ); assign( lmd_8_val, binop( Iop_Or32, binop( Iop_Or32, binop( Iop_And32, mkexpr( lmexp_00_mask ), mkU32( 24 ) ), binop( Iop_And32, mkexpr( lmexp_01_mask ), mkU32( 26 ) ) ), binop( Iop_And32, mkexpr( lmexp_10_mask ), mkU32( 28 ) ) ) ); assign( lmd_9_val, binop( Iop_Or32, binop( Iop_Or32, binop( Iop_And32, mkexpr( lmexp_00_mask ), mkU32( 25 ) ), binop( Iop_And32, mkexpr( lmexp_01_mask ), mkU32( 27 ) ) ), binop( Iop_And32, mkexpr( lmexp_10_mask ), mkU32( 29 ) ) ) ); /* generate the result from the possible LMD values */ return binop( Iop_Or32, binop( Iop_Or32, binop( Iop_And32, mkexpr( lmd_07_mask ), mkexpr( lmd_07_val ) ), binop( Iop_And32, mkexpr( lmd_8_mask ), mkexpr( lmd_8_val ) ) ), binop( Iop_And32, mkexpr( lmd_9_mask ), mkexpr( lmd_9_val ) ) ); } static void Get_lmd( IRTemp * lmd, IRExpr * gfield_0_4 ) { /* Extract the exponent and the left most digit of the mantissa * from the G field bits [0:4]. */ IRTemp lmd_07_mask = newTemp( Ity_I32 ); IRTemp lmd_8_00_mask = newTemp( Ity_I32 ); IRTemp lmd_8_01_mask = newTemp( Ity_I32 ); IRTemp lmd_8_10_mask = newTemp( Ity_I32 ); IRTemp lmd_9_00_mask = newTemp( Ity_I32 ); IRTemp lmd_9_01_mask = newTemp( Ity_I32 ); IRTemp lmd_9_10_mask = newTemp( Ity_I32 ); IRTemp lmd_07_val = newTemp( Ity_I32 ); IRTemp lmd_8_val = newTemp( Ity_I32 ); IRTemp lmd_9_val = newTemp( Ity_I32 ); /* The left most digit (LMD) encoding is as follows: * lmd * 0 - 7 (lmexp << 3) | lmd * 8 0b11000 (24 decimal) if lme=0b00; * 0b11010 (26 decimal) if lme=0b01; * 0b11100 (28 decimal) if lme=0b10 * 9 0b11001 (25 decimal) if lme=0b00; * 0b11011 (27 decimal) if lme=0b01; * 0b11101 (29 decimal) if lme=0b10; */ /* Generate the masks for each condition of LMD and exponent bits */ assign( lmd_07_mask, unop( Iop_1Sto32, binop( Iop_CmpLE32U, gfield_0_4, mkU32( BITS5(1,0,1,1,1) ) ) ) ); assign( lmd_8_00_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, gfield_0_4, mkU32( BITS5(1,1,0,0,0) ) ) ) ); assign( lmd_8_01_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, gfield_0_4, mkU32( BITS5(1,1,0,1,0) ) ) ) ); assign( lmd_8_10_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, gfield_0_4, mkU32( BITS5(1,1,1,0,0) ) ) ) ); assign( lmd_9_00_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, gfield_0_4, mkU32( BITS5(1,1,0,0,1) ) ) ) ); assign( lmd_9_01_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, gfield_0_4, mkU32( BITS5(1,1,0,1,1) ) ) ) ); assign( lmd_9_10_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, gfield_0_4, mkU32( BITS5(1,1,1,0,1) ) ) ) ); /* Generate the values for each LMD condition, assuming the condition * is TRUE. */ assign( lmd_07_val, binop( Iop_And32, gfield_0_4, mkU32( 0x7 ) ) ); assign( lmd_8_val, mkU32( 0x8 ) ); assign( lmd_9_val, mkU32( 0x9 ) ); assign( *lmd, OR( OR3 ( AND( mkexpr( lmd_07_mask ), mkexpr( lmd_07_val ) ), AND( mkexpr( lmd_8_00_mask ), mkexpr( lmd_8_val ) ), AND( mkexpr( lmd_8_01_mask ), mkexpr( lmd_8_val ) )), OR4( AND( mkexpr( lmd_8_10_mask ), mkexpr( lmd_8_val ) ), AND( mkexpr( lmd_9_00_mask ), mkexpr( lmd_9_val ) ), AND( mkexpr( lmd_9_01_mask ), mkexpr( lmd_9_val ) ), AND( mkexpr( lmd_9_10_mask ), mkexpr( lmd_9_val ) ) ) ) ); } #define DIGIT1_SHR 4 // shift digit 1 to bottom 4 bits #define DIGIT2_SHR 8 // shift digit 2 to bottom 4 bits #define DIGIT3_SHR 12 #define DIGIT4_SHR 16 #define DIGIT5_SHR 20 #define DIGIT6_SHR 24 #define DIGIT7_SHR 28 static IRExpr * bcd_digit_inval( IRExpr * bcd_u, IRExpr * bcd_l ) { /* 60-bit BCD string stored in two 32-bit values. Check that each, * digit is a valid BCD number, i.e. less then 9. */ IRTemp valid = newTemp( Ity_I32 ); assign( valid, AND4( AND4 ( unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, bcd_l, mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_l, mkU8 ( DIGIT1_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_l, mkU8 ( DIGIT2_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_l, mkU8 ( DIGIT3_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ) ), AND4 ( unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_l, mkU8 ( DIGIT4_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_l, mkU8 ( DIGIT5_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_l, mkU8 ( DIGIT6_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_l, mkU8 ( DIGIT7_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ) ), AND4( unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, bcd_u, mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_u, mkU8 ( DIGIT1_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_u, mkU8 ( DIGIT2_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_u, mkU8 ( DIGIT3_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ) ), AND4( unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_u, mkU8 ( DIGIT4_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_u, mkU8 ( DIGIT5_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_u, mkU8 ( DIGIT6_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpLE32U, binop( Iop_And32, binop( Iop_Shr32, bcd_u, mkU8 ( DIGIT7_SHR ) ), mkU32 ( 0xF ) ), mkU32( 0x9 ) ) ) ) ) ); return unop( Iop_Not32, mkexpr( valid ) ); } #undef DIGIT1_SHR #undef DIGIT2_SHR #undef DIGIT3_SHR #undef DIGIT4_SHR #undef DIGIT5_SHR #undef DIGIT6_SHR #undef DIGIT7_SHR static IRExpr * Generate_neg_sign_mask( IRExpr * sign ) { return binop( Iop_Or32, unop( Iop_1Sto32, binop( Iop_CmpEQ32, sign, mkU32( 0xB ) ) ), unop( Iop_1Sto32, binop( Iop_CmpEQ32, sign, mkU32( 0xD ) ) ) ); } static IRExpr * Generate_pos_sign_mask( IRExpr * sign ) { return binop( Iop_Or32, binop( Iop_Or32, unop( Iop_1Sto32, binop( Iop_CmpEQ32, sign, mkU32( 0xA ) ) ), unop( Iop_1Sto32, binop( Iop_CmpEQ32, sign, mkU32( 0xC ) ) ) ), binop( Iop_Or32, unop( Iop_1Sto32, binop( Iop_CmpEQ32, sign, mkU32( 0xE ) ) ), unop( Iop_1Sto32, binop( Iop_CmpEQ32, sign, mkU32( 0xF ) ) ) ) ); } static IRExpr * Generate_sign_bit( IRExpr * pos_sign_mask, IRExpr * neg_sign_mask ) { return binop( Iop_Or32, binop( Iop_And32, neg_sign_mask, mkU32( 0x80000000 ) ), binop( Iop_And32, pos_sign_mask, mkU32( 0x00000000 ) ) ); } static IRExpr * Generate_inv_mask( IRExpr * invalid_bcd_mask, IRExpr * pos_sign_mask, IRExpr * neg_sign_mask ) /* first argument is all 1's if the BCD string had an invalid digit in it. */ { return binop( Iop_Or32, invalid_bcd_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, binop( Iop_Or32, pos_sign_mask, neg_sign_mask ), mkU32( 0x0 ) ) ) ); } static void Generate_132_bit_bcd_string( IRExpr * frBI64_hi, IRExpr * frBI64_lo, IRTemp * top_12_l, IRTemp * mid_60_u, IRTemp * mid_60_l, IRTemp * low_60_u, IRTemp * low_60_l) { IRTemp tmplow60 = newTemp( Ity_I64 ); IRTemp tmpmid60 = newTemp( Ity_I64 ); IRTemp tmptop12 = newTemp( Ity_I64 ); IRTemp low_50 = newTemp( Ity_I64 ); IRTemp mid_50 = newTemp( Ity_I64 ); IRTemp top_10 = newTemp( Ity_I64 ); IRTemp top_12_u = newTemp( Ity_I32 ); // only needed for a dummy arg /* Convert the 110-bit densely packed BCD string to a 128-bit BCD string */ /* low_50[49:0] = ((frBI64_lo[49:32] << 14) | frBI64_lo[31:0]) */ assign( low_50, binop( Iop_32HLto64, binop( Iop_And32, unop( Iop_64HIto32, frBI64_lo ), mkU32( 0x3FFFF ) ), unop( Iop_64to32, frBI64_lo ) ) ); /* Convert the 50 bit densely packed BCD string to a 60 bit * BCD string. */ assign( tmplow60, unop( Iop_DPBtoBCD, mkexpr( low_50 ) ) ); assign( *low_60_u, unop( Iop_64HIto32, mkexpr( tmplow60 ) ) ); assign( *low_60_l, unop( Iop_64to32, mkexpr( tmplow60 ) ) ); /* mid_50[49:0] = ((frBI64_hi[35:32] << 14) | frBI64_hi[31:18]) | * ((frBI64_hi[17:0] << 14) | frBI64_lo[63:50]) */ assign( mid_50, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_Shl32, binop( Iop_And32, unop( Iop_64HIto32, frBI64_hi ), mkU32( 0xF ) ), mkU8( 14 ) ), binop( Iop_Shr32, unop( Iop_64to32, frBI64_hi ), mkU8( 18 ) ) ), binop( Iop_Or32, binop( Iop_Shl32, unop( Iop_64to32, frBI64_hi ), mkU8( 14 ) ), binop( Iop_Shr32, unop( Iop_64HIto32, frBI64_lo ), mkU8( 18 ) ) ) ) ); /* Convert the 50 bit densely packed BCD string to a 60 bit * BCD string. */ assign( tmpmid60, unop( Iop_DPBtoBCD, mkexpr( mid_50 ) ) ); assign( *mid_60_u, unop( Iop_64HIto32, mkexpr( tmpmid60 ) ) ); assign( *mid_60_l, unop( Iop_64to32, mkexpr( tmpmid60 ) ) ); /* top_10[49:0] = frBI64_hi[45:36]) | */ assign( top_10, binop( Iop_32HLto64, mkU32( 0 ), binop( Iop_And32, binop( Iop_Shr32, unop( Iop_64HIto32, frBI64_hi ), mkU8( 4 ) ), mkU32( 0x3FF ) ) ) ); /* Convert the 10 bit densely packed BCD string to a 12 bit * BCD string. */ assign( tmptop12, unop( Iop_DPBtoBCD, mkexpr( top_10 ) ) ); assign( top_12_u, unop( Iop_64HIto32, mkexpr( tmptop12 ) ) ); assign( *top_12_l, unop( Iop_64to32, mkexpr( tmptop12 ) ) ); } static void Count_zeros( int start, IRExpr * init_cnt, IRExpr * init_flag, IRTemp * final_cnt, IRTemp * final_flag, IRExpr * string ) { IRTemp cnt[MAX_DIGITS_IN_STRING + 1];IRTemp flag[MAX_DIGITS_IN_STRING+1]; int digits = MAX_DIGITS_IN_STRING; int i; cnt[start-1] = newTemp( Ity_I8 ); flag[start-1] = newTemp( Ity_I8 ); assign( cnt[start-1], init_cnt); assign( flag[start-1], init_flag); for ( i = start; i <= digits; i++) { cnt[i] = newTemp( Ity_I8 ); flag[i] = newTemp( Ity_I8 ); assign( cnt[i], binop( Iop_Add8, mkexpr( cnt[i-1] ), binop(Iop_And8, unop( Iop_1Uto8, binop(Iop_CmpEQ32, binop(Iop_And32, string, mkU32( 0xF << ( ( digits - i ) * 4) ) ), mkU32( 0 ) ) ), binop( Iop_Xor8, /* complement flag */ mkexpr( flag[i - 1] ), mkU8( 0xFF ) ) ) ) ); /* set flag to 1 if digit was not a zero */ assign( flag[i], binop(Iop_Or8, unop( Iop_1Sto8, binop(Iop_CmpNE32, binop(Iop_And32, string, mkU32( 0xF << ( (digits - i) * 4) ) ), mkU32( 0 ) ) ), mkexpr( flag[i - 1] ) ) ); } *final_cnt = cnt[digits]; *final_flag = flag[digits]; } static IRExpr * Count_leading_zeros_60( IRExpr * lmd, IRExpr * upper_28, IRExpr * low_32 ) { IRTemp num_lmd = newTemp( Ity_I8 ); IRTemp num_upper = newTemp( Ity_I8 ); IRTemp num_low = newTemp( Ity_I8 ); IRTemp lmd_flag = newTemp( Ity_I8 ); IRTemp upper_flag = newTemp( Ity_I8 ); IRTemp low_flag = newTemp( Ity_I8 ); assign( num_lmd, unop( Iop_1Uto8, binop( Iop_CmpEQ32, lmd, mkU32( 0 ) ) ) ); assign( lmd_flag, unop( Iop_Not8, mkexpr( num_lmd ) ) ); Count_zeros( 2, mkexpr( num_lmd ), mkexpr( lmd_flag ), &num_upper, &upper_flag, upper_28 ); Count_zeros( 1, mkexpr( num_upper ), mkexpr( upper_flag ), &num_low, &low_flag, low_32 ); return mkexpr( num_low ); } static IRExpr * Count_leading_zeros_128( IRExpr * lmd, IRExpr * top_12_l, IRExpr * mid_60_u, IRExpr * mid_60_l, IRExpr * low_60_u, IRExpr * low_60_l) { IRTemp num_lmd = newTemp( Ity_I8 ); IRTemp num_top = newTemp( Ity_I8 ); IRTemp num_mid_u = newTemp( Ity_I8 ); IRTemp num_mid_l = newTemp( Ity_I8 ); IRTemp num_low_u = newTemp( Ity_I8 ); IRTemp num_low_l = newTemp( Ity_I8 ); IRTemp lmd_flag = newTemp( Ity_I8 ); IRTemp top_flag = newTemp( Ity_I8 ); IRTemp mid_u_flag = newTemp( Ity_I8 ); IRTemp mid_l_flag = newTemp( Ity_I8 ); IRTemp low_u_flag = newTemp( Ity_I8 ); IRTemp low_l_flag = newTemp( Ity_I8 ); /* Check the LMD, digit 16, to see if it is zero. */ assign( num_lmd, unop( Iop_1Uto8, binop( Iop_CmpEQ32, lmd, mkU32( 0 ) ) ) ); assign( lmd_flag, unop( Iop_Not8, mkexpr( num_lmd ) ) ); Count_zeros( 6, mkexpr( num_lmd ), mkexpr( lmd_flag ), &num_top, &top_flag, top_12_l ); Count_zeros( 1, mkexpr( num_top ), mkexpr( top_flag ), &num_mid_u, &mid_u_flag, binop( Iop_Or32, binop( Iop_Shl32, mid_60_u, mkU8( 2 ) ), binop( Iop_Shr32, mid_60_l, mkU8( 30 ) ) ) ); Count_zeros( 2, mkexpr( num_mid_u ), mkexpr( mid_u_flag ), &num_mid_l, &mid_l_flag, mid_60_l ); Count_zeros( 1, mkexpr( num_mid_l ), mkexpr( mid_l_flag ), &num_low_u, &low_u_flag, binop( Iop_Or32, binop( Iop_Shl32, low_60_u, mkU8( 2 ) ), binop( Iop_Shr32, low_60_l, mkU8( 30 ) ) ) ); Count_zeros( 2, mkexpr( num_low_u ), mkexpr( low_u_flag ), &num_low_l, &low_l_flag, low_60_l ); return mkexpr( num_low_l ); } static IRExpr * Check_unordered(IRExpr * val) { IRTemp gfield0to5 = newTemp( Ity_I32 ); /* Extract G[0:4] */ assign( gfield0to5, binop( Iop_And32, binop( Iop_Shr32, unop( Iop_64HIto32, val ), mkU8( 26 ) ), mkU32( 0x1F ) ) ); /* Check for unordered, return all 1'x if true */ return binop( Iop_Or32, /* QNaN check */ unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( gfield0to5 ), mkU32( 0x1E ) ) ), unop( Iop_1Sto32, /* SNaN check */ binop( Iop_CmpEQ32, mkexpr( gfield0to5 ), mkU32( 0x1F ) ) ) ); } #undef AND #undef AND4 #undef OR #undef OR3 #undef OR4 #undef NOT #undef SHR #undef SHL #undef BITS5 /*------------------------------------------------------------*/ /*--- Decimal Floating Point (DFP) instruction translation ---*/ /*------------------------------------------------------------*/ /* DFP Arithmetic instructions */ static Bool dis_dfp_arith(UInt theInstr) { UInt opc2 = ifieldOPClo10( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frA_addr = ifieldRegA( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); UChar flag_rC = ifieldBIT0( theInstr ); IRTemp frA = newTemp( Ity_D64 ); IRTemp frB = newTemp( Ity_D64 ); IRTemp frS = newTemp( Ity_D64 ); IRExpr* round = get_IR_roundingmode_DFP(); /* By default, if flag_RC is set, we will clear cr1 after the * operation. In reality we should set cr1 to indicate the * exception status of the operation, but since we're not * simulating exceptions, the exception status will appear to be * zero. Hence cr1 should be cleared if this is a . form insn. */ Bool clear_CR1 = True; assign( frA, getDReg( frA_addr ) ); assign( frB, getDReg( frB_addr ) ); switch (opc2) { case 0x2: // dadd DIP( "dadd%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frS, triop( Iop_AddD64, round, mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x202: // dsub DIP( "dsub%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frS, triop( Iop_SubD64, round, mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x22: // dmul DIP( "dmul%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frS, triop( Iop_MulD64, round, mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x222: // ddiv DIP( "ddiv%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frS, triop( Iop_DivD64, round, mkexpr( frA ), mkexpr( frB ) ) ); break; } putDReg( frS_addr, mkexpr( frS ) ); if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } /* Quad DFP Arithmetic instructions */ static Bool dis_dfp_arithq(UInt theInstr) { UInt opc2 = ifieldOPClo10( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frA_addr = ifieldRegA( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); UChar flag_rC = ifieldBIT0( theInstr ); IRTemp frA = newTemp( Ity_D128 ); IRTemp frB = newTemp( Ity_D128 ); IRTemp frS = newTemp( Ity_D128 ); IRExpr* round = get_IR_roundingmode_DFP(); /* By default, if flag_RC is set, we will clear cr1 after the * operation. In reality we should set cr1 to indicate the * exception status of the operation, but since we're not * simulating exceptions, the exception status will appear to be * zero. Hence cr1 should be cleared if this is a . form insn. */ Bool clear_CR1 = True; assign( frA, getDReg_pair( frA_addr ) ); assign( frB, getDReg_pair( frB_addr ) ); switch (opc2) { case 0x2: // daddq DIP( "daddq%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frS, triop( Iop_AddD128, round, mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x202: // dsubq DIP( "dsubq%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frS, triop( Iop_SubD128, round, mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x22: // dmulq DIP( "dmulq%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frS, triop( Iop_MulD128, round, mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x222: // ddivq DIP( "ddivq%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frS, triop( Iop_DivD128, round, mkexpr( frA ), mkexpr( frB ) ) ); break; } putDReg_pair( frS_addr, mkexpr( frS ) ); if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } /* DFP 64-bit logical shift instructions */ static Bool dis_dfp_shift(UInt theInstr) { UInt opc2 = ifieldOPClo9( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frA_addr = ifieldRegA( theInstr ); UChar shift_val = IFIELD(theInstr, 10, 6); UChar flag_rC = ifieldBIT0( theInstr ); IRTemp frA = newTemp( Ity_D64 ); IRTemp frS = newTemp( Ity_D64 ); Bool clear_CR1 = True; assign( frA, getDReg( frA_addr ) ); switch (opc2) { case 0x42: // dscli DIP( "dscli%s fr%u,fr%u,%u\n", flag_rC ? ".":"", frS_addr, frA_addr, shift_val ); assign( frS, binop( Iop_ShlD64, mkexpr( frA ), mkU8( shift_val ) ) ); break; case 0x62: // dscri DIP( "dscri%s fr%u,fr%u,%u\n", flag_rC ? ".":"", frS_addr, frA_addr, shift_val ); assign( frS, binop( Iop_ShrD64, mkexpr( frA ), mkU8( shift_val ) ) ); break; } putDReg( frS_addr, mkexpr( frS ) ); if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } /* Quad DFP logical shift instructions */ static Bool dis_dfp_shiftq(UInt theInstr) { UInt opc2 = ifieldOPClo9( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frA_addr = ifieldRegA( theInstr ); UChar shift_val = IFIELD(theInstr, 10, 6); UChar flag_rC = ifieldBIT0( theInstr ); IRTemp frA = newTemp( Ity_D128 ); IRTemp frS = newTemp( Ity_D128 ); Bool clear_CR1 = True; assign( frA, getDReg_pair( frA_addr ) ); switch (opc2) { case 0x42: // dscliq DIP( "dscliq%s fr%u,fr%u,%u\n", flag_rC ? ".":"", frS_addr, frA_addr, shift_val ); assign( frS, binop( Iop_ShlD128, mkexpr( frA ), mkU8( shift_val ) ) ); break; case 0x62: // dscriq DIP( "dscriq%s fr%u,fr%u,%u\n", flag_rC ? ".":"", frS_addr, frA_addr, shift_val ); assign( frS, binop( Iop_ShrD128, mkexpr( frA ), mkU8( shift_val ) ) ); break; } putDReg_pair( frS_addr, mkexpr( frS ) ); if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } /* DFP 64-bit format conversion instructions */ static Bool dis_dfp_fmt_conv(UInt theInstr) { UInt opc2 = ifieldOPClo10( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); IRExpr* round = get_IR_roundingmode_DFP(); UChar flag_rC = ifieldBIT0( theInstr ); IRTemp frB; IRTemp frS; Bool clear_CR1 = True; switch (opc2) { case 0x102: //dctdp DIP( "dctdp%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); frB = newTemp( Ity_D32 ); frS = newTemp( Ity_D64 ); assign( frB, getDReg32( frB_addr ) ); assign( frS, unop( Iop_D32toD64, mkexpr( frB ) ) ); putDReg( frS_addr, mkexpr( frS ) ); break; case 0x302: // drsp DIP( "drsp%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); frB = newTemp( Ity_D64 ); frS = newTemp( Ity_D32 ); assign( frB, getDReg( frB_addr ) ); assign( frS, binop( Iop_D64toD32, round, mkexpr( frB ) ) ); putDReg32( frS_addr, mkexpr( frS ) ); break; case 0x122: // dctfix { IRTemp tmp = newTemp( Ity_I64 ); DIP( "dctfix%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); frB = newTemp( Ity_D64 ); frS = newTemp( Ity_D64 ); assign( frB, getDReg( frB_addr ) ); assign( tmp, binop( Iop_D64toI64S, round, mkexpr( frB ) ) ); assign( frS, unop( Iop_ReinterpI64asD64, mkexpr( tmp ) ) ); putDReg( frS_addr, mkexpr( frS ) ); } break; case 0x322: // dcffix DIP( "dcffix%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); frB = newTemp( Ity_D64 ); frS = newTemp( Ity_D64 ); assign( frB, getDReg( frB_addr ) ); assign( frS, binop( Iop_I64StoD64, round, unop( Iop_ReinterpD64asI64, mkexpr( frB ) ) ) ); putDReg( frS_addr, mkexpr( frS ) ); break; } if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } /* Quad DFP format conversion instructions */ static Bool dis_dfp_fmt_convq(UInt theInstr) { UInt opc2 = ifieldOPClo10( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); IRExpr* round = get_IR_roundingmode_DFP(); IRTemp frB64 = newTemp( Ity_D64 ); IRTemp frB128 = newTemp( Ity_D128 ); IRTemp frS64 = newTemp( Ity_D64 ); IRTemp frS128 = newTemp( Ity_D128 ); UChar flag_rC = ifieldBIT0( theInstr ); Bool clear_CR1 = True; switch (opc2) { case 0x102: // dctqpq DIP( "dctqpq%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); assign( frB64, getDReg( frB_addr ) ); assign( frS128, unop( Iop_D64toD128, mkexpr( frB64 ) ) ); putDReg_pair( frS_addr, mkexpr( frS128 ) ); break; case 0x122: // dctfixq { IRTemp tmp = newTemp( Ity_I64 ); DIP( "dctfixq%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); assign( frB128, getDReg_pair( frB_addr ) ); assign( tmp, binop( Iop_D128toI64S, round, mkexpr( frB128 ) ) ); assign( frS64, unop( Iop_ReinterpI64asD64, mkexpr( tmp ) ) ); putDReg( frS_addr, mkexpr( frS64 ) ); } break; case 0x302: //drdpq DIP( "drdpq%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); assign( frB128, getDReg_pair( frB_addr ) ); assign( frS64, binop( Iop_D128toD64, round, mkexpr( frB128 ) ) ); putDReg( frS_addr, mkexpr( frS64 ) ); break; case 0x322: // dcffixq { /* Have to introduce an IOP for this instruction so it will work * on POWER 6 because emulating the instruction requires a POWER 7 * DFP instruction in the emulation code. */ DIP( "dcffixq%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); assign( frB64, getDReg( frB_addr ) ); assign( frS128, unop( Iop_I64StoD128, unop( Iop_ReinterpD64asI64, mkexpr( frB64 ) ) ) ); putDReg_pair( frS_addr, mkexpr( frS128 ) ); break; } } if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } static Bool dis_dfp_round( UInt theInstr ) { UChar frS_addr = ifieldRegDS(theInstr); UChar R = IFIELD(theInstr, 16, 1); UChar RMC = IFIELD(theInstr, 9, 2); UChar frB_addr = ifieldRegB( theInstr ); UChar flag_rC = ifieldBIT0( theInstr ); IRTemp frB = newTemp( Ity_D64 ); IRTemp frS = newTemp( Ity_D64 ); UInt opc2 = ifieldOPClo8( theInstr ); Bool clear_CR1 = True; switch (opc2) { /* drintn, is the same as drintx. The only difference is this * instruction does not generate an exception for an inexact operation. * Currently not supporting inexact exceptions. */ case 0x63: // drintx case 0xE3: // drintn DIP( "drintx/drintn%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); /* NOTE, this instruction takes a DFP value and rounds to the * neares floating point integer value, i.e. fractional part * is zero. The result is a floating point number. */ /* pass the value of R and RMC in the same field */ assign( frB, getDReg( frB_addr ) ); assign( frS, binop( Iop_RoundD64toInt, mkU32( ( R << 3 ) | RMC ), mkexpr( frB ) ) ); putDReg( frS_addr, mkexpr( frS ) ); break; default: vex_printf("dis_dfp_round(ppc)(opc2)\n"); return False; } if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } static Bool dis_dfp_roundq(UInt theInstr) { UChar frS_addr = ifieldRegDS( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); UChar R = IFIELD(theInstr, 16, 1); UChar RMC = IFIELD(theInstr, 9, 2); UChar flag_rC = ifieldBIT0( theInstr ); IRTemp frB = newTemp( Ity_D128 ); IRTemp frS = newTemp( Ity_D128 ); Bool clear_CR1 = True; UInt opc2 = ifieldOPClo8( theInstr ); switch (opc2) { /* drintnq, is the same as drintxq. The only difference is this * instruction does not generate an exception for an inexact operation. * Currently not supporting inexact exceptions. */ case 0x63: // drintxq case 0xE3: // drintnq DIP( "drintxq/drintnq%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); /* pass the value of R and RMC in the same field */ assign( frB, getDReg_pair( frB_addr ) ); assign( frS, binop( Iop_RoundD128toInt, mkU32( ( R << 3 ) | RMC ), mkexpr( frB ) ) ); putDReg_pair( frS_addr, mkexpr( frS ) ); break; default: vex_printf("dis_dfp_roundq(ppc)(opc2)\n"); return False; } if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } static Bool dis_dfp_quantize_sig_rrnd(UInt theInstr) { UInt opc2 = ifieldOPClo8( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frA_addr = ifieldRegA( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); UChar flag_rC = ifieldBIT0( theInstr ); UInt TE_value = IFIELD(theInstr, 16, 4); UInt TE_sign = IFIELD(theInstr, 20, 1); UInt RMC = IFIELD(theInstr, 9, 2); IRTemp frA = newTemp( Ity_D64 ); IRTemp frB = newTemp( Ity_D64 ); IRTemp frS = newTemp( Ity_D64 ); Bool clear_CR1 = True; assign( frB, getDReg( frB_addr ) ); switch (opc2) { case 0x43: // dquai DIP( "dquai%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); IRTemp TE_I64 = newTemp( Ity_I64 ); /* Generate a reference DFP value frA with the desired exponent * given by TE using significand from frB. Need to add the bias * 398 to TE. TE is stored as a 2's complement number. */ if (TE_sign == 1) { /* Take 2's complement of the 5-bit value and subtract from bias. * Bias is adjusted for the +1 required when taking 2's complement. */ assign( TE_I64, unop( Iop_32Uto64, binop( Iop_Sub32, mkU32( 397 ), binop( Iop_And32, mkU32( 0xF ), unop( Iop_Not32, mkU32( TE_value ) ) ) ) ) ); } else { assign( TE_I64, unop( Iop_32Uto64, binop( Iop_Add32, mkU32( 398 ), mkU32( TE_value ) ) ) ); } assign( frA, binop( Iop_InsertExpD64, mkexpr( TE_I64 ), unop( Iop_ReinterpI64asD64, mkU64( 1 ) ) ) ); assign( frS, triop( Iop_QuantizeD64, mkU32( RMC ), mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x3: // dqua DIP( "dqua%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frA, getDReg( frA_addr ) ); assign( frS, triop( Iop_QuantizeD64, mkU32( RMC ), mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x23: // drrnd { IRTemp tmp = newTemp( Ity_I8 ); DIP( "drrnd%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frA, getDReg( frA_addr ) ); /* Iop_64to8 not supported in 32 bit mode, do it in two steps. */ assign( tmp, unop( Iop_32to8, unop( Iop_64to32, unop( Iop_ReinterpD64asI64, mkexpr( frA ) ) ) ) ); assign( frS, triop( Iop_SignificanceRoundD64, mkU32( RMC ), mkexpr( tmp ), mkexpr( frB ) ) ); } break; default: vex_printf("dis_dfp_quantize_sig_rrnd(ppc)(opc2)\n"); return False; } putDReg( frS_addr, mkexpr( frS ) ); if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } static Bool dis_dfp_quantize_sig_rrndq(UInt theInstr) { UInt opc2 = ifieldOPClo8( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frA_addr = ifieldRegA( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); UChar flag_rC = ifieldBIT0( theInstr ); UInt TE_value = IFIELD(theInstr, 16, 4); UInt TE_sign = IFIELD(theInstr, 20, 1); UInt RMC = IFIELD(theInstr, 9, 2); IRTemp frA = newTemp( Ity_D128 ); IRTemp frB = newTemp( Ity_D128 ); IRTemp frS = newTemp( Ity_D128 ); Bool clear_CR1 = True; assign( frB, getDReg_pair( frB_addr ) ); switch (opc2) { case 0x43: // dquaiq DIP( "dquaiq%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); IRTemp TE_I64 = newTemp( Ity_I64 ); /* Generate a reference DFP value frA with the desired exponent * given by TE using significand of 1. Need to add the bias * 6176 to TE. */ if (TE_sign == 1) { /* Take 2's complement of the 5-bit value and subtract from bias. * Bias adjusted for the +1 required when taking 2's complement. */ assign( TE_I64, unop( Iop_32Uto64, binop( Iop_Sub32, mkU32( 6175 ), binop( Iop_And32, mkU32( 0xF ), unop( Iop_Not32, mkU32( TE_value ) ) ) ) ) ); } else { assign( TE_I64, unop( Iop_32Uto64, binop( Iop_Add32, mkU32( 6176 ), mkU32( TE_value ) ) ) ); } assign( frA, binop( Iop_InsertExpD128, mkexpr( TE_I64 ), unop( Iop_D64toD128, unop( Iop_ReinterpI64asD64, mkU64( 1 ) ) ) ) ); assign( frS, triop( Iop_QuantizeD128, mkU32( RMC ), mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x3: // dquaq DIP( "dquaiq%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frA, getDReg_pair( frA_addr ) ); assign( frS, triop( Iop_QuantizeD128, mkU32( RMC ), mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x23: // drrndq { IRTemp tmp = newTemp( Ity_I8 ); DIP( "drrndq%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frA, getDReg_pair( frA_addr ) ); assign( tmp, unop( Iop_32to8, unop( Iop_64to32, unop( Iop_ReinterpD64asI64, unop( Iop_D128HItoD64, mkexpr( frA ) ) ) ) ) ); assign( frS, triop( Iop_SignificanceRoundD128, mkU32( RMC ), mkexpr( tmp ), mkexpr( frB ) ) ); } break; default: vex_printf("dis_dfp_quantize_sig_rrndq(ppc)(opc2)\n"); return False; } putDReg_pair( frS_addr, mkexpr( frS ) ); if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } static Bool dis_dfp_extract_insert(UInt theInstr) { UInt opc2 = ifieldOPClo10( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frA_addr = ifieldRegA( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); UChar flag_rC = ifieldBIT0( theInstr ); Bool clear_CR1 = True; IRTemp frA = newTemp( Ity_D64 ); IRTemp frB = newTemp( Ity_D64 ); IRTemp frS = newTemp( Ity_D64 ); IRTemp tmp = newTemp( Ity_I64 ); assign( frA, getDReg( frA_addr ) ); assign( frB, getDReg( frB_addr ) ); switch (opc2) { case 0x162: // dxex DIP( "dxex%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( tmp, unop( Iop_ExtractExpD64, mkexpr( frB ) ) ); assign( frS, unop( Iop_ReinterpI64asD64, mkexpr( tmp ) ) ); break; case 0x362: // diex DIP( "diex%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frS, binop( Iop_InsertExpD64, unop( Iop_ReinterpD64asI64, mkexpr( frA ) ), mkexpr( frB ) ) ); break; default: vex_printf("dis_dfp_extract_insert(ppc)(opc2)\n"); return False; } putDReg( frS_addr, mkexpr( frS ) ); if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } static Bool dis_dfp_extract_insertq(UInt theInstr) { UInt opc2 = ifieldOPClo10( theInstr ); UChar frS_addr = ifieldRegDS( theInstr ); UChar frA_addr = ifieldRegA( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); UChar flag_rC = ifieldBIT0( theInstr ); IRTemp frA = newTemp( Ity_D64 ); IRTemp frB = newTemp( Ity_D128 ); IRTemp frS64 = newTemp( Ity_D64 ); IRTemp frS = newTemp( Ity_D128 ); IRTemp tmp = newTemp( Ity_I64 ); Bool clear_CR1 = True; assign( frB, getDReg_pair( frB_addr ) ); switch (opc2) { case 0x162: // dxexq DIP( "dxexq%s fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frB_addr ); /* Instruction actually returns a 64-bit result. So as to be * consistent and not have to add a new struct, the emulation returns * the 64-bit result in the upper and lower register. */ assign( tmp, unop( Iop_ExtractExpD128, mkexpr( frB ) ) ); assign( frS64, unop( Iop_ReinterpI64asD64, mkexpr( tmp ) ) ); putDReg( frS_addr, mkexpr( frS64 ) ); break; case 0x362: // diexq DIP( "diexq%s fr%u,fr%u,fr%u\n", flag_rC ? ".":"", frS_addr, frA_addr, frB_addr ); assign( frA, getDReg( frA_addr ) ); assign( frS, binop( Iop_InsertExpD128, unop( Iop_ReinterpD64asI64, mkexpr( frA ) ), mkexpr( frB ) ) ); putDReg_pair( frS_addr, mkexpr( frS ) ); break; default: vex_printf("dis_dfp_extract_insertq(ppc)(opc2)\n"); return False; } if (flag_rC && clear_CR1) { putCR321( 1, mkU8( 0 ) ); putCR0( 1, mkU8( 0 ) ); } return True; } /* DFP 64-bit comparison instructions */ static Bool dis_dfp_compare(UInt theInstr) { /* X-Form */ UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); // AKA BF UChar frA_addr = ifieldRegA( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); UInt opc1 = ifieldOPC( theInstr ); IRTemp frA; IRTemp frB; IRTemp ccIR = newTemp( Ity_I32 ); IRTemp ccPPC32 = newTemp( Ity_I32 ); /* Note: Differences between dcmpu and dcmpo are only in exception flag settings, which aren't supported anyway. */ switch (opc1) { case 0x3B: /* dcmpo and dcmpu, DFP 64-bit */ DIP( "dcmpo %u,fr%u,fr%u\n", crfD, frA_addr, frB_addr ); frA = newTemp( Ity_D64 ); frB = newTemp( Ity_D64 ); assign( frA, getDReg( frA_addr ) ); assign( frB, getDReg( frB_addr ) ); assign( ccIR, binop( Iop_CmpD64, mkexpr( frA ), mkexpr( frB ) ) ); break; case 0x3F: /* dcmpoq and dcmpuq,DFP 128-bit */ DIP( "dcmpoq %u,fr%u,fr%u\n", crfD, frA_addr, frB_addr ); frA = newTemp( Ity_D128 ); frB = newTemp( Ity_D128 ); assign( frA, getDReg_pair( frA_addr ) ); assign( frB, getDReg_pair( frB_addr ) ); assign( ccIR, binop( Iop_CmpD128, mkexpr( frA ), mkexpr( frB ) ) ); break; default: vex_printf("dis_dfp_compare(ppc)(opc2)\n"); return False; } /* Map compare result from IR to PPC32 */ /* FP cmp result | PPC | IR -------------------------- UN | 0x1 | 0x45 EQ | 0x2 | 0x40 GT | 0x4 | 0x00 LT | 0x8 | 0x01 */ assign( ccPPC32, binop( Iop_Shl32, mkU32( 1 ), unop( Iop_32to8, binop( Iop_Or32, binop( Iop_And32, unop( Iop_Not32, binop( Iop_Shr32, mkexpr( ccIR ), mkU8( 5 ) ) ), mkU32( 2 ) ), binop( Iop_And32, binop( Iop_Xor32, mkexpr( ccIR ), binop( Iop_Shr32, mkexpr( ccIR ), mkU8( 6 ) ) ), mkU32( 1 ) ) ) ) ) ); putGST_field( PPC_GST_CR, mkexpr( ccPPC32 ), crfD ); return True; } /* Test class/group/exponent/significance instructions. */ static Bool dis_dfp_exponent_test ( UInt theInstr ) { UChar frA_addr = ifieldRegA( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); IRTemp frA = newTemp( Ity_D64 ); IRTemp frB = newTemp( Ity_D64 ); IRTemp frA128 = newTemp( Ity_D128 ); IRTemp frB128 = newTemp( Ity_D128 ); UInt opc1 = ifieldOPC( theInstr ); IRTemp gfield_A = newTemp( Ity_I32 ); IRTemp gfield_B = newTemp( Ity_I32 ); IRTemp gfield_mask = newTemp( Ity_I32 ); IRTemp exponent_A = newTemp( Ity_I32 ); IRTemp exponent_B = newTemp( Ity_I32 ); IRTemp A_NaN_true = newTemp( Ity_I32 ); IRTemp B_NaN_true = newTemp( Ity_I32 ); IRTemp A_inf_true = newTemp( Ity_I32 ); IRTemp B_inf_true = newTemp( Ity_I32 ); IRTemp A_equals_B = newTemp( Ity_I32 ); IRTemp finite_number = newTemp( Ity_I32 ); IRTemp cc0 = newTemp( Ity_I32 ); IRTemp cc1 = newTemp( Ity_I32 ); IRTemp cc2 = newTemp( Ity_I32 ); IRTemp cc3 = newTemp( Ity_I32 ); /* The dtstex and dtstexg instructions only differ in the size of the * exponent field. The following switch statement takes care of the size * specific setup. Once the value of the exponents, the G-field shift * and mask is setup the remaining code is identical. */ switch (opc1) { case 0x3b: // dtstex Extended instruction setup DIP("dtstex %u,r%u,r%d\n", crfD, frA_addr, frB_addr); assign( frA, getDReg( frA_addr ) ); assign( frB, getDReg( frB_addr ) ); assign( gfield_mask, mkU32( DFP_G_FIELD_LONG_MASK ) ); assign(exponent_A, unop( Iop_64to32, unop( Iop_ExtractExpD64, mkexpr( frA ) ) ) ); assign(exponent_B, unop( Iop_64to32, unop( Iop_ExtractExpD64, mkexpr( frB ) ) ) ); break; case 0x3F: // dtstexq Quad instruction setup DIP("dtstexq %u,r%u,r%d\n", crfD, frA_addr, frB_addr); assign( frA128, getDReg_pair( frA_addr ) ); assign( frB128, getDReg_pair( frB_addr ) ); assign( frA, unop( Iop_D128HItoD64, mkexpr( frA128 ) ) ); assign( frB, unop( Iop_D128HItoD64, mkexpr( frB128 ) ) ); assign( gfield_mask, mkU32( DFP_G_FIELD_EXTND_MASK ) ); assign( exponent_A, unop( Iop_64to32, unop( Iop_ExtractExpD128, mkexpr( frA128 ) ) ) ); assign( exponent_B, unop( Iop_64to32, unop( Iop_ExtractExpD128, mkexpr( frB128 ) ) ) ); break; default: vex_printf("dis_dfp_exponent_test(ppc)(opc2)\n"); return False; } /* Extract the Gfield */ assign( gfield_A, binop( Iop_And32, mkexpr( gfield_mask ), unop( Iop_64HIto32, unop( Iop_ReinterpD64asI64, mkexpr(frA) ) ) ) ); assign( gfield_B, binop( Iop_And32, mkexpr( gfield_mask ), unop( Iop_64HIto32, unop( Iop_ReinterpD64asI64, mkexpr(frB) ) ) ) ); /* check for NAN */ assign( A_NaN_true, binop(Iop_Or32, unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( gfield_A ), mkU32( 0x7C000000 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( gfield_A ), mkU32( 0x7E000000 ) ) ) ) ); assign( B_NaN_true, binop(Iop_Or32, unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( gfield_B ), mkU32( 0x7C000000 ) ) ), unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( gfield_B ), mkU32( 0x7E000000 ) ) ) ) ); /* check for infinity */ assign( A_inf_true, unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( gfield_A ), mkU32( 0x78000000 ) ) ) ); assign( B_inf_true, unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( gfield_B ), mkU32( 0x78000000 ) ) ) ); assign( finite_number, unop( Iop_Not32, binop( Iop_Or32, binop( Iop_Or32, mkexpr( A_NaN_true ), mkexpr( B_NaN_true ) ), binop( Iop_Or32, mkexpr( A_inf_true ), mkexpr( B_inf_true ) ) ) ) ); /* Calculate the condition code bits * If QNaN,SNaN, +infinity, -infinity then cc0, cc1 and cc2 are zero * regardless of the value of the comparisons and cc3 is 1. Otherwise, * cc0, cc1 and cc0 reflect the results of the comparisons. */ assign( A_equals_B, binop( Iop_Or32, unop( Iop_1Uto32, binop( Iop_CmpEQ32, mkexpr( exponent_A ), mkexpr( exponent_B ) ) ), binop( Iop_Or32, binop( Iop_And32, mkexpr( A_inf_true ), mkexpr( B_inf_true ) ), binop( Iop_And32, mkexpr( A_NaN_true ), mkexpr( B_NaN_true ) ) ) ) ); assign( cc0, binop( Iop_And32, mkexpr( finite_number ), binop( Iop_Shl32, unop( Iop_1Uto32, binop( Iop_CmpLT32U, mkexpr( exponent_A ), mkexpr( exponent_B ) ) ), mkU8( 3 ) ) ) ); assign( cc1, binop( Iop_And32, mkexpr( finite_number ), binop( Iop_Shl32, unop( Iop_1Uto32, binop( Iop_CmpLT32U, mkexpr( exponent_B ), mkexpr( exponent_A ) ) ), mkU8( 2 ) ) ) ); assign( cc2, binop( Iop_Shl32, binop( Iop_And32, mkexpr( A_equals_B ), mkU32( 1 ) ), mkU8( 1 ) ) ); assign( cc3, binop( Iop_And32, unop( Iop_Not32, mkexpr( A_equals_B ) ), binop( Iop_And32, mkU32( 0x1 ), binop( Iop_Or32, binop( Iop_Or32, mkexpr ( A_inf_true ), mkexpr ( B_inf_true ) ), binop( Iop_Or32, mkexpr ( A_NaN_true ), mkexpr ( B_NaN_true ) ) ) ) ) ); /* store the condition code */ putGST_field( PPC_GST_CR, binop( Iop_Or32, mkexpr( cc0 ), binop( Iop_Or32, mkexpr( cc1 ), binop( Iop_Or32, mkexpr( cc2 ), mkexpr( cc3 ) ) ) ), crfD ); return True; } /* Test class/group/exponent/significance instructions. */ static Bool dis_dfp_class_test ( UInt theInstr ) { UChar frA_addr = ifieldRegA( theInstr ); IRTemp frA = newTemp( Ity_D64 ); IRTemp abs_frA = newTemp( Ity_D64 ); IRTemp frAI64_hi = newTemp( Ity_I64 ); IRTemp frAI64_lo = newTemp( Ity_I64 ); UInt opc1 = ifieldOPC( theInstr ); UInt opc2 = ifieldOPClo9( theInstr ); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); // AKA BF UInt DCM = IFIELD( theInstr, 10, 6 ); IRTemp DCM_calc = newTemp( Ity_I32 ); UInt max_exp = 0; UInt min_exp = 0; IRTemp min_subnormalD64 = newTemp( Ity_D64 ); IRTemp min_subnormalD128 = newTemp( Ity_D128 ); IRTemp significand64 = newTemp( Ity_D64 ); IRTemp significand128 = newTemp( Ity_D128 ); IRTemp exp_min_normal = newTemp( Ity_I64 ); IRTemp exponent = newTemp( Ity_I32 ); IRTemp infinity_true = newTemp( Ity_I32 ); IRTemp SNaN_true = newTemp( Ity_I32 ); IRTemp QNaN_true = newTemp( Ity_I32 ); IRTemp subnormal_true = newTemp( Ity_I32 ); IRTemp normal_true = newTemp( Ity_I32 ); IRTemp extreme_true = newTemp( Ity_I32 ); IRTemp lmd = newTemp( Ity_I32 ); IRTemp lmd_zero_true = newTemp( Ity_I32 ); IRTemp zero_true = newTemp( Ity_I32 ); IRTemp sign = newTemp( Ity_I32 ); IRTemp field = newTemp( Ity_I32 ); IRTemp ccIR_zero = newTemp( Ity_I32 ); IRTemp ccIR_subnormal = newTemp( Ity_I32 ); /* UInt size = DFP_LONG; JRS:unused */ IRTemp gfield = newTemp( Ity_I32 ); IRTemp gfield_0_4_shift = newTemp( Ity_I8 ); IRTemp gfield_mask = newTemp( Ity_I32 ); IRTemp dcm0 = newTemp( Ity_I32 ); IRTemp dcm1 = newTemp( Ity_I32 ); IRTemp dcm2 = newTemp( Ity_I32 ); IRTemp dcm3 = newTemp( Ity_I32 ); IRTemp dcm4 = newTemp( Ity_I32 ); IRTemp dcm5 = newTemp( Ity_I32 ); /* The only difference between the dtstdc and dtstdcq instructions is * size of the T and G fields. The calculation of the 4 bit field * is the same. Setup the parameters and values that are DFP size * specific. The rest of the code is independent of the DFP size. * * The Io_CmpD64 is used below. The instruction sets the ccIR values. * The interpretation of the ccIR values is as follows: * * DFP cmp result | IR * -------------------------- * UN | 0x45 * EQ | 0x40 * GT | 0x00 * LT | 0x01 */ assign( frA, getDReg( frA_addr ) ); assign( frAI64_hi, unop( Iop_ReinterpD64asI64, mkexpr( frA ) ) ); assign( abs_frA, unop( Iop_ReinterpI64asD64, binop( Iop_And64, unop( Iop_ReinterpD64asI64, mkexpr( frA ) ), mkU64( 0x7FFFFFFFFFFFFFFFULL ) ) ) ); assign( gfield_0_4_shift, mkU8( 31 - 5 ) ); // G-field[0:4] switch (opc1) { case 0x3b: // dtstdc, dtstdg DIP("dtstd%s %u,r%u,%u\n", opc2 == 0xc2 ? "c" : "g", crfD, frA_addr, DCM); /* setup the parameters for the long format of the two instructions */ assign( frAI64_lo, mkU64( 0 ) ); assign( gfield_mask, mkU32( DFP_G_FIELD_LONG_MASK ) ); max_exp = DFP_LONG_EXP_MAX; min_exp = DFP_LONG_EXP_MIN; assign( exponent, unop( Iop_64to32, unop( Iop_ExtractExpD64, mkexpr( frA ) ) ) ); assign( significand64, unop( Iop_ReinterpI64asD64, mkU64( 0x2234000000000001ULL ) ) ); // dfp 1.0 assign( exp_min_normal,mkU64( 398 - 383 ) ); assign( min_subnormalD64, binop( Iop_InsertExpD64, mkexpr( exp_min_normal ), mkexpr( significand64 ) ) ); assign( ccIR_subnormal, binop( Iop_CmpD64, mkexpr( abs_frA ), mkexpr( min_subnormalD64 ) ) ); /* compare absolute value of frA with zero */ assign( ccIR_zero, binop( Iop_CmpD64, mkexpr( abs_frA ), unop( Iop_ReinterpI64asD64, mkU64( 0x2238000000000000ULL ) ) ) ); /* size = DFP_LONG; JRS: unused */ break; case 0x3F: // dtstdcq, dtstdgq DIP("dtstd%sq %u,r%u,%u\n", opc2 == 0xc2 ? "c" : "g", crfD, frA_addr, DCM); /* setup the parameters for the extended format of the * two instructions */ assign( frAI64_lo, unop( Iop_ReinterpD64asI64, getDReg( frA_addr+1 ) ) ); assign( gfield_mask, mkU32( DFP_G_FIELD_EXTND_MASK ) ); max_exp = DFP_EXTND_EXP_MAX; min_exp = DFP_EXTND_EXP_MIN; assign( exponent, unop( Iop_64to32, unop( Iop_ExtractExpD128, getDReg_pair( frA_addr) ) ) ); /* create quand exponent for minimum normal number */ assign( exp_min_normal, mkU64( 6176 - 6143 ) ); assign( significand128, unop( Iop_D64toD128, unop( Iop_ReinterpI64asD64, mkU64( 0x2234000000000001ULL ) ) ) ); // dfp 1.0 assign( min_subnormalD128, binop( Iop_InsertExpD128, mkexpr( exp_min_normal ), mkexpr( significand128 ) ) ); assign( ccIR_subnormal, binop( Iop_CmpD128, binop( Iop_D64HLtoD128, unop( Iop_ReinterpI64asD64, binop( Iop_And64, unop( Iop_ReinterpD64asI64, mkexpr( frA ) ), mkU64( 0x7FFFFFFFFFFFFFFFULL ) ) ), getDReg( frA_addr+1 ) ), mkexpr( min_subnormalD128 ) ) ); assign( ccIR_zero, binop( Iop_CmpD128, binop( Iop_D64HLtoD128, mkexpr( abs_frA ), getDReg( frA_addr+1 ) ), unop( Iop_D64toD128, unop( Iop_ReinterpI64asD64, mkU64( 0x0ULL ) ) ) ) ); /* size = DFP_EXTND; JRS:unused */ break; default: vex_printf("dis_dfp_class_test(ppc)(opc2)\n"); return False; } /* The G-field is in the upper 32-bits. The I64 logical operations * do not seem to be supported in 32-bit mode so keep things as 32-bit * operations. */ assign( gfield, binop( Iop_And32, mkexpr( gfield_mask ), unop( Iop_64HIto32, mkexpr(frAI64_hi) ) ) ); /* There is a lot of code that is the same to do the class and group * instructions. Later there is an if statement to handle the specific * instruction. * * Will be using I32 values, compares, shifts and logical operations for * this code as the 64-bit compare, shifts, logical operations are not * supported in 32-bit mode. */ /* Check the bits for Infinity, QNaN or Signaling NaN */ assign( infinity_true, unop( Iop_1Sto32, binop( Iop_CmpEQ32, binop( Iop_And32, mkU32( 0x7C000000 ), mkexpr( gfield ) ), mkU32( 0x78000000 ) ) ) ); assign( SNaN_true, unop( Iop_1Sto32, binop( Iop_CmpEQ32, binop( Iop_And32, mkU32( 0x7E000000 ), mkexpr( gfield ) ), mkU32( 0x7E000000 ) ) ) ); assign( QNaN_true, binop( Iop_And32, unop( Iop_1Sto32, binop( Iop_CmpEQ32, binop( Iop_And32, mkU32( 0x7E000000 ), mkexpr( gfield ) ), mkU32( 0x7C000000 ) ) ), unop( Iop_Not32, mkexpr( SNaN_true ) ) ) ); assign( zero_true, binop( Iop_And32, unop(Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( ccIR_zero ), mkU32( 0x40 ) ) ), // ccIR code for Equal unop( Iop_Not32, binop( Iop_Or32, mkexpr( infinity_true ), binop( Iop_Or32, mkexpr( QNaN_true ), mkexpr( SNaN_true ) ) ) ) ) ); /* Do compare of frA the minimum normal value. Comparison is size * depenent and was done above to get the ccIR value. */ assign( subnormal_true, binop( Iop_And32, binop( Iop_Or32, unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( ccIR_subnormal ), mkU32( 0x40 ) ) ), // ccIR code for Equal unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( ccIR_subnormal ), mkU32( 0x1 ) ) ) ), // ccIR code for LT unop( Iop_Not32, binop( Iop_Or32, binop( Iop_Or32, mkexpr( infinity_true ), mkexpr( zero_true) ), binop( Iop_Or32, mkexpr( QNaN_true ), mkexpr( SNaN_true ) ) ) ) ) ); /* Normal number is not subnormal, infinity, NaN or Zero */ assign( normal_true, unop( Iop_Not32, binop( Iop_Or32, binop( Iop_Or32, mkexpr( infinity_true ), mkexpr( zero_true ) ), binop( Iop_Or32, mkexpr( subnormal_true ), binop( Iop_Or32, mkexpr( QNaN_true ), mkexpr( SNaN_true ) ) ) ) ) ); /* Calculate the DCM bit field based on the tests for the specific * instruction */ if (opc2 == 0xC2) { // dtstdc, dtstdcq /* DCM[0:5] Bit Data Class definition * 0 Zero * 1 Subnormal * 2 Normal * 3 Infinity * 4 Quiet NaN * 5 Signaling NaN */ assign( dcm0, binop( Iop_Shl32, mkexpr( zero_true ), mkU8( 5 ) ) ); assign( dcm1, binop( Iop_Shl32, binop( Iop_And32, mkexpr( subnormal_true ), mkU32( 1 ) ), mkU8( 4 ) ) ); assign( dcm2, binop( Iop_Shl32, binop( Iop_And32, mkexpr( normal_true ), mkU32( 1 ) ), mkU8( 3 ) ) ); assign( dcm3, binop( Iop_Shl32, binop( Iop_And32, mkexpr( infinity_true), mkU32( 1 ) ), mkU8( 2 ) ) ); assign( dcm4, binop( Iop_Shl32, binop( Iop_And32, mkexpr( QNaN_true ), mkU32( 1 ) ), mkU8( 1 ) ) ); assign( dcm5, binop( Iop_And32, mkexpr( SNaN_true), mkU32( 1 ) ) ); } else if (opc2 == 0xE2) { // dtstdg, dtstdgq /* check if the exponent is extreme */ assign( extreme_true, binop( Iop_Or32, unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( exponent ), mkU32( max_exp ) ) ), unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( exponent ), mkU32( min_exp ) ) ) ) ); /* Check if LMD is zero */ Get_lmd( &lmd, binop( Iop_Shr32, mkexpr( gfield ), mkU8( 31 - 5 ) ) ); assign( lmd_zero_true, unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( lmd ), mkU32( 0 ) ) ) ); /* DCM[0:5] Bit Data Class definition * 0 Zero with non-extreme exponent * 1 Zero with extreme exponent * 2 Subnormal or (Normal with extreme exponent) * 3 Normal with non-extreme exponent and * leftmost zero digit in significand * 4 Normal with non-extreme exponent and * leftmost nonzero digit in significand * 5 Special symbol (Infinity, QNaN, or SNaN) */ assign( dcm0, binop( Iop_Shl32, binop( Iop_And32, binop( Iop_And32, unop( Iop_Not32, mkexpr( extreme_true ) ), mkexpr( zero_true ) ), mkU32( 0x1 ) ), mkU8( 5 ) ) ); assign( dcm1, binop( Iop_Shl32, binop( Iop_And32, binop( Iop_And32, mkexpr( extreme_true ), mkexpr( zero_true ) ), mkU32( 0x1 ) ), mkU8( 4 ) ) ); assign( dcm2, binop( Iop_Shl32, binop( Iop_And32, binop( Iop_Or32, binop( Iop_And32, mkexpr( extreme_true ), mkexpr( normal_true ) ), mkexpr( subnormal_true ) ), mkU32( 0x1 ) ), mkU8( 3 ) ) ); assign( dcm3, binop( Iop_Shl32, binop( Iop_And32, binop( Iop_And32, binop( Iop_And32, unop( Iop_Not32, mkexpr( extreme_true ) ), mkexpr( normal_true ) ), unop( Iop_1Sto32, binop( Iop_CmpEQ32, mkexpr( lmd ), mkU32( 0 ) ) ) ), mkU32( 0x1 ) ), mkU8( 2 ) ) ); assign( dcm4, binop( Iop_Shl32, binop( Iop_And32, binop( Iop_And32, binop( Iop_And32, unop( Iop_Not32, mkexpr( extreme_true ) ), mkexpr( normal_true ) ), unop( Iop_1Sto32, binop( Iop_CmpNE32, mkexpr( lmd ), mkU32( 0 ) ) ) ), mkU32( 0x1 ) ), mkU8( 1 ) ) ); assign( dcm5, binop( Iop_And32, binop( Iop_Or32, mkexpr( SNaN_true), binop( Iop_Or32, mkexpr( QNaN_true), mkexpr( infinity_true) ) ), mkU32( 0x1 ) ) ); } /* create DCM field */ assign( DCM_calc, binop( Iop_Or32, mkexpr( dcm0 ), binop( Iop_Or32, mkexpr( dcm1 ), binop( Iop_Or32, mkexpr( dcm2 ), binop( Iop_Or32, mkexpr( dcm3 ), binop( Iop_Or32, mkexpr( dcm4 ), mkexpr( dcm5 ) ) ) ) ) ) ); /* Get the sign of the DFP number, ignore sign for QNaN */ assign( sign, unop( Iop_1Uto32, binop( Iop_CmpEQ32, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frAI64_hi ) ), mkU8( 63 - 32 ) ), mkU32( 1 ) ) ) ); /* This instruction generates a four bit field to be stored in the * condition code register. The condition code register consists of 7 * fields. The field to be written to is specified by the BF (AKA crfD) * field. * * The field layout is as follows: * * Field Meaning * 0000 Operand positive with no match * 0100 Operand positive with at least one match * 0001 Operand negative with no match * 0101 Operand negative with at least one match */ assign( field, binop( Iop_Or32, binop( Iop_Shl32, mkexpr( sign ), mkU8( 3 ) ), binop( Iop_Shl32, unop( Iop_1Uto32, binop( Iop_CmpNE32, binop( Iop_And32, mkU32( DCM ), mkexpr( DCM_calc ) ), mkU32( 0 ) ) ), mkU8( 1 ) ) ) ); putGST_field( PPC_GST_CR, mkexpr( field ), crfD ); return True; } static Bool dis_dfp_bcd(UInt theInstr) { UInt opc2 = ifieldOPClo10( theInstr ); ULong sp = IFIELD(theInstr, 19, 2); ULong s = IFIELD(theInstr, 20, 1); UChar frT_addr = ifieldRegDS( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); IRTemp frB = newTemp( Ity_D64 ); IRTemp frBI64 = newTemp( Ity_I64 ); IRTemp result = newTemp( Ity_I64 ); IRTemp resultD64 = newTemp( Ity_D64 ); IRTemp bcd64 = newTemp( Ity_I64 ); IRTemp bcd_u = newTemp( Ity_I32 ); IRTemp bcd_l = newTemp( Ity_I32 ); IRTemp dbcd_u = newTemp( Ity_I32 ); IRTemp dbcd_l = newTemp( Ity_I32 ); IRTemp lmd = newTemp( Ity_I32 ); assign( frB, getDReg( frB_addr ) ); assign( frBI64, unop( Iop_ReinterpD64asI64, mkexpr( frB ) ) ); switch ( opc2 ) { case 0x142: // ddedpd DFP Decode DPD to BCD DIP( "ddedpd %llu,r%u,r%u\n", sp, frT_addr, frB_addr ); assign( bcd64, unop( Iop_DPBtoBCD, mkexpr( frBI64 ) ) ); assign( bcd_u, unop( Iop_64HIto32, mkexpr( bcd64 ) ) ); assign( bcd_l, unop( Iop_64to32, mkexpr( bcd64 ) ) ); if ( ( sp == 0 ) || ( sp == 1 ) ) { /* Unsigned BCD string */ Get_lmd( &lmd, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64 ) ), mkU8( 31 - 5 ) ) ); // G-field[0:4] assign( result, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_Shl32, mkexpr( lmd ), mkU8( 28 ) ), mkexpr( bcd_u ) ), mkexpr( bcd_l ) ) ); } else { /* Signed BCD string, the cases for sp 2 and 3 only differ in how * the positive and negative values are encoded in the least * significant bits. */ IRTemp sign = newTemp( Ity_I32 ); if (sp == 2) { /* Positive sign = 0xC, negative sign = 0xD */ assign( sign, binop( Iop_Or32, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64 ) ), mkU8( 31 ) ), mkU32( 0xC ) ) ); } else if ( sp == 3 ) { /* Positive sign = 0xF, negative sign = 0xD */ IRTemp tmp32 = newTemp( Ity_I32 ); /* Complement sign bit then OR into bit position 1 */ assign( tmp32, binop( Iop_Xor32, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64 ) ), mkU8( 30 ) ), mkU32( 0x2 ) ) ); assign( sign, binop( Iop_Or32, mkexpr( tmp32 ), mkU32( 0xD ) ) ); } else { vpanic( "The impossible happened: dis_dfp_bcd(ppc), undefined SP field" ); } /* Put sign in bottom 4 bits, move most significant 4-bits from * bcd_l to bcd_u. */ assign( result, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_Shr32, mkexpr( bcd_l ), mkU8( 28 ) ), binop( Iop_Shl32, mkexpr( bcd_u ), mkU8( 4 ) ) ), binop( Iop_Or32, mkexpr( sign ), binop( Iop_Shl32, mkexpr( bcd_l ), mkU8( 4 ) ) ) ) ); } putDReg( frT_addr, unop( Iop_ReinterpI64asD64, mkexpr( result ) ) ); break; case 0x342: // denbcd DFP Encode BCD to DPD { IRTemp valid_mask = newTemp( Ity_I32 ); IRTemp invalid_mask = newTemp( Ity_I32 ); IRTemp without_lmd = newTemp( Ity_I64 ); IRTemp tmp64 = newTemp( Ity_I64 ); IRTemp dbcd64 = newTemp( Ity_I64 ); IRTemp left_exp = newTemp( Ity_I32 ); IRTemp g0_4 = newTemp( Ity_I32 ); DIP( "denbcd %llu,r%u,r%u\n", s, frT_addr, frB_addr ); if ( s == 0 ) { /* Unsigned BCD string */ assign( dbcd64, unop( Iop_BCDtoDPB, mkexpr(frBI64 ) ) ); assign( dbcd_u, unop( Iop_64HIto32, mkexpr( dbcd64 ) ) ); assign( dbcd_l, unop( Iop_64to32, mkexpr( dbcd64 ) ) ); assign( lmd, binop( Iop_Shr32, binop( Iop_And32, unop( Iop_64HIto32, mkexpr( frBI64 ) ), mkU32( 0xF0000000 ) ), mkU8( 28 ) ) ); assign( invalid_mask, bcd_digit_inval( unop( Iop_64HIto32, mkexpr( frBI64 ) ), unop( Iop_64to32, mkexpr( frBI64 ) ) ) ); assign( valid_mask, unop( Iop_Not32, mkexpr( invalid_mask ) ) ); assign( without_lmd, unop( Iop_ReinterpD64asI64, binop( Iop_InsertExpD64, mkU64( DFP_LONG_BIAS ), unop( Iop_ReinterpI64asD64, binop( Iop_32HLto64, mkexpr( dbcd_u ), mkexpr( dbcd_l ) ) ) ) ) ); assign( left_exp, binop( Iop_Shr32, binop( Iop_And32, unop( Iop_64HIto32, mkexpr( without_lmd ) ), mkU32( 0x60000000 ) ), mkU8( 29 ) ) ); assign( g0_4, binop( Iop_Shl32, Gfield_encoding( mkexpr( left_exp ), mkexpr( lmd ) ), mkU8( 26 ) ) ); assign( tmp64, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_And32, unop( Iop_64HIto32, mkexpr( without_lmd ) ), mkU32( 0x83FFFFFF ) ), mkexpr( g0_4 ) ), unop( Iop_64to32, mkexpr( without_lmd ) ) ) ); } else if ( s == 1 ) { IRTemp sign = newTemp( Ity_I32 ); IRTemp sign_bit = newTemp( Ity_I32 ); IRTemp pos_sign_mask = newTemp( Ity_I32 ); IRTemp neg_sign_mask = newTemp( Ity_I32 ); IRTemp tmp = newTemp( Ity_I64 ); /* Signed BCD string, least significant 4 bits are sign bits * positive sign = 0xC, negative sign = 0xD */ assign( tmp, unop( Iop_BCDtoDPB, binop( Iop_32HLto64, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64 ) ), mkU8( 4 ) ), binop( Iop_Or32, binop( Iop_Shr32, unop( Iop_64to32, mkexpr( frBI64 ) ), mkU8( 4 ) ), binop( Iop_Shl32, unop( Iop_64HIto32, mkexpr( frBI64 ) ), mkU8( 28 ) ) ) ) ) ); assign( dbcd_u, unop( Iop_64HIto32, mkexpr( tmp ) ) ); assign( dbcd_l, unop( Iop_64to32, mkexpr( tmp ) ) ); /* Get the sign of the BCD string. */ assign( sign, binop( Iop_And32, unop( Iop_64to32, mkexpr( frBI64 ) ), mkU32( 0xF ) ) ); assign( neg_sign_mask, Generate_neg_sign_mask( mkexpr( sign ) ) ); assign( pos_sign_mask, Generate_pos_sign_mask( mkexpr( sign ) ) ); assign( sign_bit, Generate_sign_bit( mkexpr( pos_sign_mask ), mkexpr( neg_sign_mask ) ) ); /* Check for invalid sign and BCD digit. Don't check the bottom * four bits of bcd_l as that is the sign value. */ assign( invalid_mask, Generate_inv_mask( bcd_digit_inval( unop( Iop_64HIto32, mkexpr( frBI64 ) ), binop( Iop_Shr32, unop( Iop_64to32, mkexpr( frBI64 ) ), mkU8( 4 ) ) ), mkexpr( pos_sign_mask ), mkexpr( neg_sign_mask ) ) ); assign( valid_mask, unop( Iop_Not32, mkexpr( invalid_mask ) ) ); /* Generate the result assuming the sign value was valid. */ assign( tmp64, unop( Iop_ReinterpD64asI64, binop( Iop_InsertExpD64, mkU64( DFP_LONG_BIAS ), unop( Iop_ReinterpI64asD64, binop( Iop_32HLto64, binop( Iop_Or32, mkexpr( dbcd_u ), mkexpr( sign_bit ) ), mkexpr( dbcd_l ) ) ) ) ) ); } /* Generate the value to store depending on the validity of the * sign value and the validity of the BCD digits. */ assign( resultD64, unop( Iop_ReinterpI64asD64, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_And32, mkexpr( valid_mask ), unop( Iop_64HIto32, mkexpr( tmp64 ) ) ), binop( Iop_And32, mkU32( 0x7C000000 ), mkexpr( invalid_mask ) ) ), binop( Iop_Or32, binop( Iop_And32, mkexpr( valid_mask ), unop( Iop_64to32, mkexpr( tmp64 ) ) ), binop( Iop_And32, mkU32( 0x0 ), mkexpr( invalid_mask ) ) ) ) ) ); putDReg( frT_addr, mkexpr( resultD64 ) ); } break; default: vpanic( "ERROR: dis_dfp_bcd(ppc), undefined opc2 case " ); return False; } return True; } static Bool dis_dfp_bcdq( UInt theInstr ) { UInt opc2 = ifieldOPClo10( theInstr ); ULong sp = IFIELD(theInstr, 19, 2); ULong s = IFIELD(theInstr, 20, 1); IRTemp frB_hi = newTemp( Ity_D64 ); IRTemp frB_lo = newTemp( Ity_D64 ); IRTemp frBI64_hi = newTemp( Ity_I64 ); IRTemp frBI64_lo = newTemp( Ity_I64 ); UChar frT_addr = ifieldRegDS( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); IRTemp lmd = newTemp( Ity_I32 ); IRTemp result_hi = newTemp( Ity_I64 ); IRTemp result_lo = newTemp( Ity_I64 ); assign( frB_hi, getDReg( frB_addr ) ); assign( frB_lo, getDReg( frB_addr + 1 ) ); assign( frBI64_hi, unop( Iop_ReinterpD64asI64, mkexpr( frB_hi ) ) ); assign( frBI64_lo, unop( Iop_ReinterpD64asI64, mkexpr( frB_lo ) ) ); switch ( opc2 ) { case 0x142: // ddedpdq DFP Decode DPD to BCD { IRTemp low_60_u = newTemp( Ity_I32 ); IRTemp low_60_l = newTemp( Ity_I32 ); IRTemp mid_60_u = newTemp( Ity_I32 ); IRTemp mid_60_l = newTemp( Ity_I32 ); IRTemp top_12_l = newTemp( Ity_I32 ); DIP( "ddedpdq %llu,r%u,r%u\n", sp, frT_addr, frB_addr ); /* Note, instruction only stores the lower 32 BCD digits in * the result */ Generate_132_bit_bcd_string( mkexpr( frBI64_hi ), mkexpr( frBI64_lo ), &top_12_l, &mid_60_u, &mid_60_l, &low_60_u, &low_60_l ); if ( ( sp == 0 ) || ( sp == 1 ) ) { /* Unsigned BCD string */ assign( result_hi, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_Shl32, mkexpr( top_12_l ), mkU8( 24 ) ), binop( Iop_Shr32, mkexpr( mid_60_u ), mkU8( 4 ) ) ), binop( Iop_Or32, binop( Iop_Shl32, mkexpr( mid_60_u ), mkU8( 28 ) ), binop( Iop_Shr32, mkexpr( mid_60_l ), mkU8( 4 ) ) ) ) ); assign( result_lo, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_Shl32, mkexpr( mid_60_l ), mkU8( 28 ) ), mkexpr( low_60_u ) ), mkexpr( low_60_l ) ) ); } else { /* Signed BCD string, the cases for sp 2 and 3 only differ in how * the positive and negative values are encoded in the least * significant bits. */ IRTemp sign = newTemp( Ity_I32 ); if ( sp == 2 ) { /* Positive sign = 0xC, negative sign = 0xD */ assign( sign, binop( Iop_Or32, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64_hi ) ), mkU8( 31 ) ), mkU32( 0xC ) ) ); } else if ( sp == 3 ) { IRTemp tmp32 = newTemp( Ity_I32 ); /* Positive sign = 0xF, negative sign = 0xD. * Need to complement sign bit then OR into bit position 1. */ assign( tmp32, binop( Iop_Xor32, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64_hi ) ), mkU8( 30 ) ), mkU32( 0x2 ) ) ); assign( sign, binop( Iop_Or32, mkexpr( tmp32 ), mkU32( 0xD ) ) ); } else { vpanic( "The impossible happened: dis_dfp_bcd(ppc), undefined SP field" ); } assign( result_hi, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_Shl32, mkexpr( top_12_l ), mkU8( 28 ) ), mkexpr( mid_60_u ) ), mkexpr( mid_60_l ) ) ); assign( result_lo, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_Shl32, mkexpr( low_60_u ), mkU8( 4 ) ), binop( Iop_Shr32, mkexpr( low_60_l ), mkU8( 28 ) ) ), binop( Iop_Or32, binop( Iop_Shl32, mkexpr( low_60_l ), mkU8( 4 ) ), mkexpr( sign ) ) ) ); } putDReg( frT_addr, unop( Iop_ReinterpI64asD64, mkexpr( result_hi ) ) ); putDReg( frT_addr + 1, unop( Iop_ReinterpI64asD64, mkexpr( result_lo ) ) ); } break; case 0x342: // denbcdq DFP Encode BCD to DPD { IRTemp valid_mask = newTemp( Ity_I32 ); IRTemp invalid_mask = newTemp( Ity_I32 ); IRTemp result128 = newTemp( Ity_D128 ); IRTemp dfp_significand = newTemp( Ity_D128 ); IRTemp tmp_hi = newTemp( Ity_I64 ); IRTemp tmp_lo = newTemp( Ity_I64 ); IRTemp dbcd_top_l = newTemp( Ity_I32 ); IRTemp dbcd_mid_u = newTemp( Ity_I32 ); IRTemp dbcd_mid_l = newTemp( Ity_I32 ); IRTemp dbcd_low_u = newTemp( Ity_I32 ); IRTemp dbcd_low_l = newTemp( Ity_I32 ); IRTemp bcd_top_8 = newTemp( Ity_I64 ); IRTemp bcd_mid_60 = newTemp( Ity_I64 ); IRTemp bcd_low_60 = newTemp( Ity_I64 ); IRTemp sign_bit = newTemp( Ity_I32 ); IRTemp tmptop10 = newTemp( Ity_I64 ); IRTemp tmpmid50 = newTemp( Ity_I64 ); IRTemp tmplow50 = newTemp( Ity_I64 ); IRTemp inval_bcd_digit_mask = newTemp( Ity_I32 ); DIP( "denbcd %llu,r%u,r%u\n", s, frT_addr, frB_addr ); if ( s == 0 ) { /* Unsigned BCD string */ assign( sign_bit, mkU32( 0 ) ); // set to zero for unsigned string assign( bcd_top_8, binop( Iop_32HLto64, mkU32( 0 ), binop( Iop_And32, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64_hi ) ), mkU8( 24 ) ), mkU32( 0xFF ) ) ) ); assign( bcd_mid_60, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_Shr32, unop( Iop_64to32, mkexpr( frBI64_hi ) ), mkU8( 28 ) ), binop( Iop_Shl32, unop( Iop_64HIto32, mkexpr( frBI64_hi ) ), mkU8( 4 ) ) ), binop( Iop_Or32, binop( Iop_Shl32, unop( Iop_64to32, mkexpr( frBI64_hi ) ), mkU8( 4 ) ), binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64_lo ) ), mkU8( 28 ) ) ) ) ); /* Note, the various helper functions ignores top 4-bits */ assign( bcd_low_60, mkexpr( frBI64_lo ) ); assign( tmptop10, unop( Iop_BCDtoDPB, mkexpr( bcd_top_8 ) ) ); assign( dbcd_top_l, unop( Iop_64to32, mkexpr( tmptop10 ) ) ); assign( tmpmid50, unop( Iop_BCDtoDPB, mkexpr( bcd_mid_60 ) ) ); assign( dbcd_mid_u, unop( Iop_64HIto32, mkexpr( tmpmid50 ) ) ); assign( dbcd_mid_l, unop( Iop_64to32, mkexpr( tmpmid50 ) ) ); assign( tmplow50, unop( Iop_BCDtoDPB, mkexpr( bcd_low_60 ) ) ); assign( dbcd_low_u, unop( Iop_64HIto32, mkexpr( tmplow50 ) ) ); assign( dbcd_low_l, unop( Iop_64to32, mkexpr( tmplow50 ) ) ); /* The entire BCD string fits in lower 110-bits. The LMD = 0, * value is not part of the final result. Only the right most * BCD digits are stored. */ assign( lmd, mkU32( 0 ) ); assign( invalid_mask, binop( Iop_Or32, bcd_digit_inval( mkU32( 0 ), unop( Iop_64to32, mkexpr( bcd_top_8 ) ) ), binop( Iop_Or32, bcd_digit_inval( unop( Iop_64HIto32, mkexpr( bcd_mid_60 ) ), unop( Iop_64to32, mkexpr( bcd_mid_60 ) ) ), bcd_digit_inval( unop( Iop_64HIto32, mkexpr( bcd_low_60 ) ), unop( Iop_64to32, mkexpr( bcd_low_60 ) ) ) ) ) ); } else if ( s == 1 ) { IRTemp sign = newTemp( Ity_I32 ); IRTemp zero = newTemp( Ity_I32 ); IRTemp pos_sign_mask = newTemp( Ity_I32 ); IRTemp neg_sign_mask = newTemp( Ity_I32 ); /* The sign of the BCD string is stored in lower 4 bits */ assign( sign, binop( Iop_And32, unop( Iop_64to32, mkexpr( frBI64_lo ) ), mkU32( 0xF ) ) ); assign( neg_sign_mask, Generate_neg_sign_mask( mkexpr( sign ) ) ); assign( pos_sign_mask, Generate_pos_sign_mask( mkexpr( sign ) ) ); assign( sign_bit, Generate_sign_bit( mkexpr( pos_sign_mask ), mkexpr( neg_sign_mask ) ) ); /* Generate the value assuminig the sign and BCD digits are vaild */ assign( bcd_top_8, binop( Iop_32HLto64, mkU32( 0x0 ), binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64_hi ) ), mkU8( 28 ) ) ) ); /* The various helper routines ignore the upper 4-bits */ assign( bcd_mid_60, mkexpr( frBI64_hi ) ); /* Remove bottom four sign bits */ assign( bcd_low_60, binop( Iop_32HLto64, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64_lo ) ), mkU8( 4 ) ), binop( Iop_Or32, binop( Iop_Shl32, unop( Iop_64HIto32, mkexpr( frBI64_lo ) ), mkU8( 28 ) ), binop( Iop_Shr32, unop( Iop_64to32, mkexpr( frBI64_lo ) ), mkU8( 4 ) ) ) ) ); assign( tmptop10, unop( Iop_BCDtoDPB, mkexpr(bcd_top_8 ) ) ); assign( dbcd_top_l, unop( Iop_64to32, mkexpr( tmptop10 ) ) ); assign( tmpmid50, unop( Iop_BCDtoDPB, mkexpr(bcd_mid_60 ) ) ); assign( dbcd_mid_u, unop( Iop_64HIto32, mkexpr( tmpmid50 ) ) ); assign( dbcd_mid_l, unop( Iop_64to32, mkexpr( tmpmid50 ) ) ); assign( tmplow50, unop( Iop_BCDtoDPB, mkexpr( bcd_low_60 ) ) ); assign( dbcd_low_u, unop( Iop_64HIto32, mkexpr( tmplow50 ) ) ); assign( dbcd_low_l, unop( Iop_64to32, mkexpr( tmplow50 ) ) ); /* The entire BCD string fits in lower 110-bits. The LMD value * is not stored in the final result for the DFP Long instruction. */ assign( lmd, mkU32( 0 ) ); /* Check for invalid sign and invalid BCD digit. Don't check the * bottom four bits of frBI64_lo as that is the sign value. */ assign( zero, mkU32( 0 ) ); assign( inval_bcd_digit_mask, binop( Iop_Or32, bcd_digit_inval( mkexpr( zero ), unop( Iop_64to32, mkexpr( bcd_top_8 ) ) ), binop( Iop_Or32, bcd_digit_inval( unop( Iop_64HIto32, mkexpr( bcd_mid_60 ) ), unop( Iop_64to32, mkexpr( bcd_mid_60 ) ) ), bcd_digit_inval( unop( Iop_64HIto32, mkexpr( frBI64_lo ) ), binop( Iop_Shr32, unop( Iop_64to32, mkexpr( frBI64_lo ) ), mkU8( 4 ) ) ) ) ) ); assign( invalid_mask, Generate_inv_mask( mkexpr( inval_bcd_digit_mask ), mkexpr( pos_sign_mask ), mkexpr( neg_sign_mask ) ) ); } assign( valid_mask, unop( Iop_Not32, mkexpr( invalid_mask ) ) ); /* Calculate the value of the result assuming sign and BCD digits * are all valid. */ assign( dfp_significand, binop( Iop_D64HLtoD128, unop( Iop_ReinterpI64asD64, binop( Iop_32HLto64, binop( Iop_Or32, mkexpr( sign_bit ), mkexpr( dbcd_top_l ) ), binop( Iop_Or32, binop( Iop_Shl32, mkexpr( dbcd_mid_u ), mkU8( 18 ) ), binop( Iop_Shr32, mkexpr( dbcd_mid_l ), mkU8( 14 ) ) ) ) ), unop( Iop_ReinterpI64asD64, binop( Iop_32HLto64, binop( Iop_Or32, mkexpr( dbcd_low_u ), binop( Iop_Shl32, mkexpr( dbcd_mid_l ), mkU8( 18 ) ) ), mkexpr( dbcd_low_l ) ) ) ) ); /* Break the result back down to 32-bit chunks and replace chunks. * If there was an invalid BCD digit or invalid sign value, replace * the calculated result with the invalid bit string. */ assign( result128, binop( Iop_InsertExpD128, mkU64( DFP_EXTND_BIAS ), mkexpr( dfp_significand ) ) ); assign( tmp_hi, unop( Iop_ReinterpD64asI64, unop( Iop_D128HItoD64, mkexpr( result128 ) ) ) ); assign( tmp_lo, unop( Iop_ReinterpD64asI64, unop( Iop_D128LOtoD64, mkexpr( result128 ) ) ) ); assign( result_hi, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_And32, mkexpr( valid_mask ), unop( Iop_64HIto32, mkexpr( tmp_hi ) ) ), binop( Iop_And32, mkU32( 0x7C000000 ), mkexpr( invalid_mask ) ) ), binop( Iop_Or32, binop( Iop_And32, mkexpr( valid_mask ), unop( Iop_64to32, mkexpr( tmp_hi ) ) ), binop( Iop_And32, mkU32( 0x0 ), mkexpr( invalid_mask ) ) ) ) ); assign( result_lo, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_And32, mkexpr( valid_mask ), unop( Iop_64HIto32, mkexpr( tmp_lo ) ) ), binop( Iop_And32, mkU32( 0x0 ), mkexpr( invalid_mask ) ) ), binop( Iop_Or32, binop( Iop_And32, mkexpr( valid_mask ), unop( Iop_64to32, mkexpr( tmp_lo ) ) ), binop( Iop_And32, mkU32( 0x0 ), mkexpr( invalid_mask ) ) ) ) ); putDReg( frT_addr, unop( Iop_ReinterpI64asD64, mkexpr( result_hi ) ) ); putDReg( frT_addr + 1, unop( Iop_ReinterpI64asD64, mkexpr( result_lo ) ) ); } break; default: vpanic( "ERROR: dis_dfp_bcdq(ppc), undefined opc2 case " ); break; } return True; } static Bool dis_dfp_significant_digits( UInt theInstr ) { UChar frA_addr = ifieldRegA( theInstr ); UChar frB_addr = ifieldRegB( theInstr ); IRTemp frA = newTemp( Ity_D64 ); UInt opc1 = ifieldOPC( theInstr ); IRTemp B_sig = newTemp( Ity_I8 ); IRTemp K = newTemp( Ity_I8 ); IRTemp lmd_B = newTemp( Ity_I32 ); IRTemp field = newTemp( Ity_I32 ); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); // AKA BF IRTemp Unordered_true = newTemp( Ity_I32 ); IRTemp Eq_true_mask = newTemp( Ity_I32 ); IRTemp Lt_true_mask = newTemp( Ity_I32 ); IRTemp Gt_true_mask = newTemp( Ity_I32 ); IRTemp KisZero_true_mask = newTemp( Ity_I32 ); IRTemp KisZero_false_mask = newTemp( Ity_I32 ); /* Get the reference singificance stored in frA */ assign( frA, getDReg( frA_addr ) ); /* Convert from 64 bit to 8 bits in two steps. The Iop_64to8 is not * supported in 32-bit mode. */ assign( K, unop( Iop_32to8, binop( Iop_And32, unop( Iop_64to32, unop( Iop_ReinterpD64asI64, mkexpr( frA ) ) ), mkU32( 0x3F ) ) ) ); switch ( opc1 ) { case 0x3b: // dtstsf DFP Test Significance { IRTemp frB = newTemp( Ity_D64 ); IRTemp frBI64 = newTemp( Ity_I64 ); IRTemp B_bcd_u = newTemp( Ity_I32 ); IRTemp B_bcd_l = newTemp( Ity_I32 ); IRTemp tmp64 = newTemp( Ity_I64 ); DIP( "dtstsf %u,r%u,r%u\n", crfD, frA_addr, frB_addr ); assign( frB, getDReg( frB_addr ) ); assign( frBI64, unop( Iop_ReinterpD64asI64, mkexpr( frB ) ) ); /* Get the BCD string for the value stored in a series of I32 values. * Count the number of leading zeros. Subtract the number of leading * zeros from 16 (maximum number of significant digits in DFP * Long). */ Get_lmd( &lmd_B, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64 ) ), mkU8( 31 - 5 ) ) ); // G-field[0:4] assign( tmp64, unop( Iop_DPBtoBCD, mkexpr( frBI64 ) ) ); assign( B_bcd_u, unop( Iop_64HIto32, mkexpr( tmp64 ) ) ); assign( B_bcd_l, unop( Iop_64to32, mkexpr( tmp64 ) ) ); assign( B_sig, binop( Iop_Sub8, mkU8( DFP_LONG_MAX_SIG_DIGITS ), Count_leading_zeros_60( mkexpr( lmd_B ), mkexpr( B_bcd_u ), mkexpr( B_bcd_l ) ) ) ); assign( Unordered_true, Check_unordered( mkexpr( frBI64 ) ) ); } break; case 0x3F: // dtstsfq DFP Test Significance { IRTemp frB_hi = newTemp( Ity_D64 ); IRTemp frB_lo = newTemp( Ity_D64 ); IRTemp frBI64_hi = newTemp( Ity_I64 ); IRTemp frBI64_lo = newTemp( Ity_I64 ); IRTemp B_low_60_u = newTemp( Ity_I32 ); IRTemp B_low_60_l = newTemp( Ity_I32 ); IRTemp B_mid_60_u = newTemp( Ity_I32 ); IRTemp B_mid_60_l = newTemp( Ity_I32 ); IRTemp B_top_12_l = newTemp( Ity_I32 ); DIP( "dtstsfq %u,r%u,r%u\n", crfD, frA_addr, frB_addr ); assign( frB_hi, getDReg( frB_addr ) ); assign( frB_lo, getDReg( frB_addr + 1 ) ); assign( frBI64_hi, unop( Iop_ReinterpD64asI64, mkexpr( frB_hi ) ) ); assign( frBI64_lo, unop( Iop_ReinterpD64asI64, mkexpr( frB_lo ) ) ); /* Get the BCD string for the value stored in a series of I32 values. * Count the number of leading zeros. Subtract the number of leading * zeros from 32 (maximum number of significant digits in DFP * extended). */ Get_lmd( &lmd_B, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frBI64_hi ) ), mkU8( 31 - 5 ) ) ); // G-field[0:4] Generate_132_bit_bcd_string( mkexpr( frBI64_hi ), mkexpr( frBI64_lo ), &B_top_12_l, &B_mid_60_u, &B_mid_60_l, &B_low_60_u, &B_low_60_l ); assign( B_sig, binop( Iop_Sub8, mkU8( DFP_EXTND_MAX_SIG_DIGITS ), Count_leading_zeros_128( mkexpr( lmd_B ), mkexpr( B_top_12_l ), mkexpr( B_mid_60_u ), mkexpr( B_mid_60_l ), mkexpr( B_low_60_u ), mkexpr( B_low_60_l ) ) ) ); assign( Unordered_true, Check_unordered( mkexpr( frBI64_hi ) ) ); } break; } /* Compare (16 - cnt[0]) against K and set the condition code field * accordingly. * * The field layout is as follows: * * bit[3:0] Description * 3 K != 0 and K < Number of significant digits if FRB * 2 K != 0 and K > Number of significant digits if FRB OR K = 0 * 1 K != 0 and K = Number of significant digits if FRB * 0 K ? Number of significant digits if FRB */ assign( Eq_true_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, unop( Iop_8Uto32, mkexpr( K ) ), unop( Iop_8Uto32, mkexpr( B_sig ) ) ) ) ); assign( Lt_true_mask, unop( Iop_1Sto32, binop( Iop_CmpLT32U, unop( Iop_8Uto32, mkexpr( K ) ), unop( Iop_8Uto32, mkexpr( B_sig ) ) ) ) ); assign( Gt_true_mask, unop( Iop_1Sto32, binop( Iop_CmpLT32U, unop( Iop_8Uto32, mkexpr( B_sig ) ), unop( Iop_8Uto32, mkexpr( K ) ) ) ) ); assign( KisZero_true_mask, unop( Iop_1Sto32, binop( Iop_CmpEQ32, unop( Iop_8Uto32, mkexpr( K ) ), mkU32( 0 ) ) ) ); assign( KisZero_false_mask, unop( Iop_1Sto32, binop( Iop_CmpNE32, unop( Iop_8Uto32, mkexpr( K ) ), mkU32( 0 ) ) ) ); assign( field, binop( Iop_Or32, binop( Iop_And32, mkexpr( KisZero_false_mask ), binop( Iop_Or32, binop( Iop_And32, mkexpr( Lt_true_mask ), mkU32( 0x8 ) ), binop( Iop_Or32, binop( Iop_And32, mkexpr( Gt_true_mask ), mkU32( 0x4 ) ), binop( Iop_And32, mkexpr( Eq_true_mask ), mkU32( 0x2 ) ) ) ) ), binop( Iop_And32, mkexpr( KisZero_true_mask ), mkU32( 0x4 ) ) ) ); putGST_field( PPC_GST_CR, binop( Iop_Or32, binop( Iop_And32, mkexpr( Unordered_true ), mkU32( 0x1 ) ), binop( Iop_And32, unop( Iop_Not32, mkexpr( Unordered_true ) ), mkexpr( field ) ) ), crfD ); return True; } /*------------------------------------------------------------*/ /*--- AltiVec Instruction Translation ---*/ /*------------------------------------------------------------*/ /* Altivec Cache Control Instructions (Data Streams) */ static Bool dis_av_datastream ( UInt theInstr ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar flag_T = toUChar( IFIELD( theInstr, 25, 1 ) ); UChar flag_A = flag_T; UChar b23to24 = toUChar( IFIELD( theInstr, 23, 2 ) ); UChar STRM = toUChar( IFIELD( theInstr, 21, 2 ) ); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); if (opc1 != 0x1F || b23to24 != 0 || b0 != 0) { vex_printf("dis_av_datastream(ppc)(instr)\n"); return False; } switch (opc2) { case 0x156: // dst (Data Stream Touch, AV p115) DIP("dst%s r%u,r%u,%d\n", flag_T ? "t" : "", rA_addr, rB_addr, STRM); break; case 0x176: // dstst (Data Stream Touch for Store, AV p117) DIP("dstst%s r%u,r%u,%d\n", flag_T ? "t" : "", rA_addr, rB_addr, STRM); break; case 0x336: // dss (Data Stream Stop, AV p114) if (rA_addr != 0 || rB_addr != 0) { vex_printf("dis_av_datastream(ppc)(opc2,dst)\n"); return False; } if (flag_A == 0) { DIP("dss %d\n", STRM); } else { DIP("dssall\n"); } break; default: vex_printf("dis_av_datastream(ppc)(opc2)\n"); return False; } return True; } /* AltiVec Processor Control Instructions */ static Bool dis_av_procctl ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UInt opc2 = IFIELD( theInstr, 0, 11 ); if (opc1 != 0x4) { vex_printf("dis_av_procctl(ppc)(instr)\n"); return False; } switch (opc2) { case 0x604: // mfvscr (Move from VSCR, AV p129) if (vA_addr != 0 || vB_addr != 0) { vex_printf("dis_av_procctl(ppc)(opc2,dst)\n"); return False; } DIP("mfvscr v%d\n", vD_addr); putVReg( vD_addr, unop(Iop_32UtoV128, getGST( PPC_GST_VSCR )) ); break; case 0x644: { // mtvscr (Move to VSCR, AV p130) IRTemp vB = newTemp(Ity_V128); if (vD_addr != 0 || vA_addr != 0) { vex_printf("dis_av_procctl(ppc)(opc2,dst)\n"); return False; } DIP("mtvscr v%d\n", vB_addr); assign( vB, getVReg(vB_addr)); putGST( PPC_GST_VSCR, unop(Iop_V128to32, mkexpr(vB)) ); break; } default: vex_printf("dis_av_procctl(ppc)(opc2)\n"); return False; } return True; } /* * VSX scalar and vector convert instructions */ static Bool dis_vx_conv ( UInt theInstr, UInt opc2 ) { /* XX2-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT( theInstr ); UChar XB = ifieldRegXB( theInstr ); IRTemp xB, xB2; IRTemp b3, b2, b1, b0; xB = xB2 = IRTemp_INVALID; if (opc1 != 0x3C) { vex_printf( "dis_vx_conv(ppc)(instr)\n" ); return False; } /* Create and assign temps only as needed for the given instruction. */ switch (opc2) { // scalar double-precision floating point argument case 0x2B0: case 0x0b0: case 0x290: case 0x212: case 0x216: case 0x090: xB = newTemp(Ity_F64); assign( xB, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XB ) ) ) ); break; // vector double-precision floating point arguments case 0x1b0: case 0x312: case 0x390: case 0x190: case 0x3B0: xB = newTemp(Ity_F64); xB2 = newTemp(Ity_F64); assign( xB, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XB ) ) ) ); assign( xB2, unop( Iop_ReinterpI64asF64, unop( Iop_V128to64, getVSReg( XB ) ) ) ); break; // vector single precision or [un]signed integer word arguments case 0x130: case 0x392: case 0x330: case 0x310: case 0x110: case 0x1f0: case 0x1d0: b3 = b2 = b1 = b0 = IRTemp_INVALID; breakV128to4x32(getVSReg(XB), &b3, &b2, &b1, &b0); break; // vector [un]signed integer doubleword argument case 0x3f0: case 0x370: case 0x3d0: case 0x350: xB = newTemp(Ity_I64); assign( xB, unop( Iop_V128HIto64, getVSReg( XB ) ) ); xB2 = newTemp(Ity_I64); assign( xB2, unop( Iop_V128to64, getVSReg( XB ) ) ); break; // scalar [un]signed integer doubleword argument case 0x250: case 0x270: case 0x2D0: case 0x2F0: xB = newTemp(Ity_I64); assign( xB, unop( Iop_V128HIto64, getVSReg( XB ) ) ); break; // scalar single precision argument case 0x292: // xscvspdp xB = newTemp(Ity_I32); assign( xB, handle_SNaN_to_QNaN_32(unop( Iop_64HIto32, unop( Iop_V128HIto64, getVSReg( XB ) ) ) ) ); break; case 0x296: // xscvspdpn (non signaling version of xscvpdp) xB = newTemp(Ity_I32); assign( xB, unop( Iop_64HIto32, unop( Iop_V128HIto64, getVSReg( XB ) ) ) ); break; /* Certain instructions have their complete implementation in the main switch statement * that follows this one; thus we have a "do nothing" case for those instructions here. */ case 0x170: case 0x150: break; // do nothing default: vex_printf( "dis_vx_conv(ppc)(opc2)\n" ); return False; } switch (opc2) { case 0x2B0: // xscvdpsxds (VSX Scalar truncate Double-Precision to integer and Convert // to Signed Integer Doubleword format with Saturate) DIP("xscvdpsxds v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_F64toI64S, mkU32( Irrm_ZERO ), mkexpr( xB ) ), mkU64( 0 ) ) ); break; case 0x0b0: // xscvdpsxws (VSX Scalar truncate Double-Precision to integer and // Convert to Signed Integer Word format with Saturate) DIP("xscvdpsxws v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_32Sto64, binop( Iop_F64toI32S, mkU32( Irrm_ZERO ), mkexpr( xB ) ) ), mkU64( 0ULL ) ) ); break; case 0x290: // xscvdpuxds (VSX Scalar truncate Double-Precision integer and Convert // to Unsigned Integer Doubleword format with Saturate) DIP("xscvdpuxds v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_F64toI64U, mkU32( Irrm_ZERO ), mkexpr( xB ) ), mkU64( 0ULL ) ) ); break; case 0x270: // xscvsxdsp (VSX Scalar Convert and round Signed Integer Doubleword // to Single-Precision format) DIP("xscvsxdsp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, get_IR_roundingmode(), binop( Iop_I64StoF64, get_IR_roundingmode(), mkexpr( xB ) ) ) ), mkU64( 0 ) ) ); break; case 0x2F0: // xscvsxddp (VSX Scalar Convert and round Signed Integer Doubleword to // Double-Precision format) DIP("xscvsxddp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_I64StoF64, get_IR_roundingmode(), mkexpr( xB ) ) ), mkU64( 0 ) ) ); break; case 0x250: // xscvuxdsp (VSX Scalar Convert and round Unsigned Integer // Doubleword to Singel-Precision format) DIP("xscvuxdsp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, get_IR_roundingmode(), binop( Iop_I64UtoF64, get_IR_roundingmode(), mkexpr( xB ) ) ) ), mkU64( 0 ) ) ); break; case 0x2D0: // xscvuxddp (VSX Scalar Convert and round Unsigned Integer Doubleword to // Double-Precision format) DIP("xscvuxddp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_I64UtoF64, get_IR_roundingmode(), mkexpr( xB ) ) ), mkU64( 0 ) ) ); break; case 0x1b0: // xvcvdpsxws (VSX Vector truncate Double-Precision to integer and Convert // to Signed Integer Word format with Saturate) { IRTemp hiResult_32 = newTemp(Ity_I32); IRTemp loResult_32 = newTemp(Ity_I32); IRExpr* rmZero = mkU32(Irrm_ZERO); DIP("xvcvdpsxws v%u,v%u\n", XT, XB); assign(hiResult_32, binop(Iop_F64toI32S, rmZero, mkexpr(xB))); assign(loResult_32, binop(Iop_F64toI32S, rmZero, mkexpr(xB2))); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_32Sto64, mkexpr( hiResult_32 ) ), unop( Iop_32Sto64, mkexpr( loResult_32 ) ) ) ); break; } case 0x130: case 0x110: // xvcvspsxws, xvcvspuxws // (VSX Vector truncate Single-Precision to integer and // Convert to [Un]signed Integer Word format with Saturate) { IRExpr * b0_result, * b1_result, * b2_result, * b3_result; IRTemp tempResult = newTemp(Ity_V128); IRTemp res0 = newTemp(Ity_I32); IRTemp res1 = newTemp(Ity_I32); IRTemp res2 = newTemp(Ity_I32); IRTemp res3 = newTemp(Ity_I32); IRTemp hi64 = newTemp(Ity_I64); IRTemp lo64 = newTemp(Ity_I64); Bool un_signed = (opc2 == 0x110); IROp op = un_signed ? Iop_QFtoI32Ux4_RZ : Iop_QFtoI32Sx4_RZ; DIP("xvcvsp%sxws v%u,v%u\n", un_signed ? "u" : "s", XT, XB); /* The xvcvsp{s|u}xws instruction is similar to vct{s|u}xs, except if src is a NaN, * then result is set to 0x80000000. */ assign(tempResult, unop(op, getVSReg(XB))); assign( hi64, unop(Iop_V128HIto64, mkexpr(tempResult)) ); assign( lo64, unop(Iop_V128to64, mkexpr(tempResult)) ); assign( res3, unop(Iop_64HIto32, mkexpr(hi64)) ); assign( res2, unop(Iop_64to32, mkexpr(hi64)) ); assign( res1, unop(Iop_64HIto32, mkexpr(lo64)) ); assign( res0, unop(Iop_64to32, mkexpr(lo64)) ); b3_result = IRExpr_ITE(is_NaN_32(b3), // then: result is 0x{8|0}80000000 mkU32(un_signed ? 0x00000000 : 0x80000000), // else: result is from the Iop_QFtoI32{s|u}x4_RZ mkexpr(res3)); b2_result = IRExpr_ITE(is_NaN_32(b2), // then: result is 0x{8|0}80000000 mkU32(un_signed ? 0x00000000 : 0x80000000), // else: result is from the Iop_QFtoI32{s|u}x4_RZ mkexpr(res2)); b1_result = IRExpr_ITE(is_NaN_32(b1), // then: result is 0x{8|0}80000000 mkU32(un_signed ? 0x00000000 : 0x80000000), // else: result is from the Iop_QFtoI32{s|u}x4_RZ mkexpr(res1)); b0_result = IRExpr_ITE(is_NaN_32(b0), // then: result is 0x{8|0}80000000 mkU32(un_signed ? 0x00000000 : 0x80000000), // else: result is from the Iop_QFtoI32{s|u}x4_RZ mkexpr(res0)); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, b3_result, b2_result ), binop( Iop_32HLto64, b1_result, b0_result ) ) ); break; } case 0x212: // xscvdpsp (VSX Scalar round Double-Precision to single-precision and // Convert to Single-Precision format DIP("xscvdpsp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop( Iop_RoundF64toF32, get_IR_roundingmode(), mkexpr( xB ) ) ) ), mkU32( 0 ) ), mkU64( 0ULL ) ) ); break; case 0x216: /* xscvdpspn (VSX Scalar convert scalar Single-Precision to vector Single-Precision non-signalling */ DIP("xscvdpspn v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, mkexpr( xB ) ) ), mkU32( 0 ) ), mkU64( 0ULL ) ) ); break; case 0x090: // xscvdpuxws (VSX Scalar truncate Double-Precision to integer // and Convert to Unsigned Integer Word format with Saturate) DIP("xscvdpuxws v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkU32( 0 ), binop( Iop_F64toI32U, mkU32( Irrm_ZERO ), mkexpr( xB ) ) ), mkU64( 0ULL ) ) ); break; case 0x292: // xscvspdp (VSX Scalar Convert Single-Precision to Double-Precision format, signaling) DIP("xscvspdp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, unop( Iop_F32toF64, unop( Iop_ReinterpI32asF32, mkexpr( xB ) ) ) ), mkU64( 0ULL ) ) ); break; case 0x296: // xscvspdpn (VSX Scalar Convert Single-Precision to Double-Precision format Non signaling) DIP("xscvspdpn v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, unop( Iop_F32toF64, unop( Iop_ReinterpI32asF32, mkexpr( xB ) ) ) ), mkU64( 0ULL ) ) ); break; case 0x312: // xvcvdpsp (VSX Vector round Double-Precision to single-precision // and Convert to Single-Precision format) DIP("xvcvdpsp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop( Iop_RoundF64toF32, get_IR_roundingmode(), mkexpr( xB ) ) ) ), mkU32( 0 ) ), binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop( Iop_RoundF64toF32, get_IR_roundingmode(), mkexpr( xB2 ) ) ) ), mkU32( 0 ) ) ) ); break; case 0x390: // xvcvdpuxds (VSX Vector truncate Double-Precision to integer // and Convert to Unsigned Integer Doubleword format // with Saturate) DIP("xvcvdpuxds v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_F64toI64U, mkU32( Irrm_ZERO ), mkexpr( xB ) ), binop( Iop_F64toI64U, mkU32( Irrm_ZERO ), mkexpr( xB2 ) ) ) ); break; case 0x190: // xvcvdpuxws (VSX Vector truncate Double-Precision to integer and // Convert to Unsigned Integer Word format with Saturate) DIP("xvcvdpuxws v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, binop( Iop_F64toI32U, mkU32( Irrm_ZERO ), mkexpr( xB ) ), mkU32( 0 ) ), binop( Iop_32HLto64, binop( Iop_F64toI32U, mkU32( Irrm_ZERO ), mkexpr( xB2 ) ), mkU32( 0 ) ) ) ); break; case 0x392: // xvcvspdp (VSX Vector Convert Single-Precision to Double-Precision format) DIP("xvcvspdp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, unop( Iop_F32toF64, unop( Iop_ReinterpI32asF32, handle_SNaN_to_QNaN_32( mkexpr( b3 ) ) ) ) ), unop( Iop_ReinterpF64asI64, unop( Iop_F32toF64, unop( Iop_ReinterpI32asF32, handle_SNaN_to_QNaN_32( mkexpr( b1 ) ) ) ) ) ) ); break; case 0x330: // xvcvspsxds (VSX Vector truncate Single-Precision to integer and // Convert to Signed Integer Doubleword format with Saturate) DIP("xvcvspsxds v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_F64toI64S, mkU32( Irrm_ZERO ), unop( Iop_F32toF64, unop( Iop_ReinterpI32asF32, mkexpr( b3 ) ) ) ), binop( Iop_F64toI64S, mkU32( Irrm_ZERO ), unop( Iop_F32toF64, unop( Iop_ReinterpI32asF32, mkexpr( b1 ) ) ) ) ) ); break; case 0x310: // xvcvspuxds (VSX Vector truncate Single-Precision to integer and // Convert to Unsigned Integer Doubleword format with Saturate) DIP("xvcvspuxds v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_F64toI64U, mkU32( Irrm_ZERO ), unop( Iop_F32toF64, unop( Iop_ReinterpI32asF32, mkexpr( b3 ) ) ) ), binop( Iop_F64toI64U, mkU32( Irrm_ZERO ), unop( Iop_F32toF64, unop( Iop_ReinterpI32asF32, mkexpr( b1 ) ) ) ) ) ); break; case 0x3B0: // xvcvdpsxds (VSX Vector truncate Double-Precision to integer and // Convert to Signed Integer Doubleword format with Saturate) DIP("xvcvdpsxds v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_F64toI64S, mkU32( Irrm_ZERO ), mkexpr( xB ) ), binop( Iop_F64toI64S, mkU32( Irrm_ZERO ), mkexpr( xB2 ) ) ) ); break; case 0x3f0: // xvcvsxddp (VSX Vector Convert and round Signed Integer Doubleword // to Double-Precision format) DIP("xvcvsxddp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_I64StoF64, get_IR_roundingmode(), mkexpr( xB ) ) ), unop( Iop_ReinterpF64asI64, binop( Iop_I64StoF64, get_IR_roundingmode(), mkexpr( xB2 ) ) ) ) ); break; case 0x3d0: // xvcvuxddp (VSX Vector Convert and round Unsigned Integer Doubleword // to Double-Precision format) DIP("xvcvuxddp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_I64UtoF64, get_IR_roundingmode(), mkexpr( xB ) ) ), unop( Iop_ReinterpF64asI64, binop( Iop_I64UtoF64, get_IR_roundingmode(), mkexpr( xB2 ) ) ) ) ); break; case 0x370: // xvcvsxdsp (VSX Vector Convert and round Signed Integer Doubleword // to Single-Precision format) DIP("xvcvsxddp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop( Iop_RoundF64toF32, get_IR_roundingmode(), binop( Iop_I64StoF64, get_IR_roundingmode(), mkexpr( xB ) ) ) ) ), mkU32( 0 ) ), binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop( Iop_RoundF64toF32, get_IR_roundingmode(), binop( Iop_I64StoF64, get_IR_roundingmode(), mkexpr( xB2 ) ) ) ) ), mkU32( 0 ) ) ) ); break; case 0x350: // xvcvuxdsp (VSX Vector Convert and round Unsigned Integer Doubleword // to Single-Precision format) DIP("xvcvuxddp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop( Iop_RoundF64toF32, get_IR_roundingmode(), binop( Iop_I64UtoF64, get_IR_roundingmode(), mkexpr( xB ) ) ) ) ), mkU32( 0 ) ), binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop( Iop_RoundF64toF32, get_IR_roundingmode(), binop( Iop_I64UtoF64, get_IR_roundingmode(), mkexpr( xB2 ) ) ) ) ), mkU32( 0 ) ) ) ); break; case 0x1f0: // xvcvsxwdp (VSX Vector Convert Signed Integer Word to Double-Precision format) DIP("xvcvsxwdp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_I64StoF64, get_IR_roundingmode(), unop( Iop_32Sto64, mkexpr( b3 ) ) ) ), unop( Iop_ReinterpF64asI64, binop( Iop_I64StoF64, get_IR_roundingmode(), unop( Iop_32Sto64, mkexpr( b1 ) ) ) ) ) ); break; case 0x1d0: // xvcvuxwdp (VSX Vector Convert Unsigned Integer Word to Double-Precision format) DIP("xvcvuxwdp v%u,v%u\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_I64UtoF64, get_IR_roundingmode(), unop( Iop_32Uto64, mkexpr( b3 ) ) ) ), unop( Iop_ReinterpF64asI64, binop( Iop_I64UtoF64, get_IR_roundingmode(), unop( Iop_32Uto64, mkexpr( b1 ) ) ) ) ) ); break; case 0x170: // xvcvsxwsp (VSX Vector Convert Signed Integer Word to Single-Precision format) DIP("xvcvsxwsp v%u,v%u\n", XT, XB); putVSReg( XT, unop( Iop_I32StoFx4, getVSReg( XB ) ) ); break; case 0x150: // xvcvuxwsp (VSX Vector Convert Unsigned Integer Word to Single-Precision format) DIP("xvcvuxwsp v%u,v%u\n", XT, XB); putVSReg( XT, unop( Iop_I32UtoFx4, getVSReg( XB ) ) ); break; default: vex_printf( "dis_vx_conv(ppc)(opc2)\n" ); return False; } return True; } /* * VSX vector Double Precision Floating Point Arithmetic Instructions */ static Bool dis_vxv_dp_arith ( UInt theInstr, UInt opc2 ) { /* XX3-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT( theInstr ); UChar XA = ifieldRegXA( theInstr ); UChar XB = ifieldRegXB( theInstr ); IRExpr* rm = get_IR_roundingmode(); IRTemp frA = newTemp(Ity_F64); IRTemp frB = newTemp(Ity_F64); IRTemp frA2 = newTemp(Ity_F64); IRTemp frB2 = newTemp(Ity_F64); if (opc1 != 0x3C) { vex_printf( "dis_vxv_dp_arith(ppc)(instr)\n" ); return False; } assign(frA, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, getVSReg( XA )))); assign(frB, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, getVSReg( XB )))); assign(frA2, unop(Iop_ReinterpI64asF64, unop(Iop_V128to64, getVSReg( XA )))); assign(frB2, unop(Iop_ReinterpI64asF64, unop(Iop_V128to64, getVSReg( XB )))); switch (opc2) { case 0x1E0: // xvdivdp (VSX Vector Divide Double-Precision) case 0x1C0: // xvmuldp (VSX Vector Multiply Double-Precision) case 0x180: // xvadddp (VSX Vector Add Double-Precision) case 0x1A0: // xvsubdp (VSX Vector Subtract Double-Precision) { IROp mOp; const HChar * oper_name; switch (opc2) { case 0x1E0: mOp = Iop_DivF64; oper_name = "div"; break; case 0x1C0: mOp = Iop_MulF64; oper_name = "mul"; break; case 0x180: mOp = Iop_AddF64; oper_name = "add"; break; case 0x1A0: mOp = Iop_SubF64; oper_name = "sub"; break; default: vpanic("The impossible happened: dis_vxv_dp_arith(ppc)"); } IRTemp hiResult = newTemp(Ity_I64); IRTemp loResult = newTemp(Ity_I64); DIP("xv%sdp v%d,v%d,v%d\n", oper_name, XT, XA, XB); assign( hiResult, unop( Iop_ReinterpF64asI64, triop( mOp, rm, mkexpr( frA ), mkexpr( frB ) ) ) ); assign( loResult, unop( Iop_ReinterpF64asI64, triop( mOp, rm, mkexpr( frA2 ), mkexpr( frB2 ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( hiResult ), mkexpr( loResult ) ) ); break; } case 0x196: // xvsqrtdp { IRTemp hiResult = newTemp(Ity_I64); IRTemp loResult = newTemp(Ity_I64); DIP("xvsqrtdp v%d,v%d\n", XT, XB); assign( hiResult, unop( Iop_ReinterpF64asI64, binop( Iop_SqrtF64, rm, mkexpr( frB ) ) ) ); assign( loResult, unop( Iop_ReinterpF64asI64, binop( Iop_SqrtF64, rm, mkexpr( frB2 ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( hiResult ), mkexpr( loResult ) ) ); break; } case 0x184: case 0x1A4: // xvmaddadp, xvmaddmdp (VSX Vector Multiply-Add Double-Precision) case 0x1C4: case 0x1E4: // xvmsubadp, xvmsubmdp (VSX Vector Multiply-Subtract Double-Precision) case 0x384: case 0x3A4: // xvnmaddadp, xvnmaddmdp (VSX Vector Negate Multiply-Add Double-Precision) case 0x3C4: case 0x3E4: // xvnmsubadp, xvnmsubmdp (VSX Vector Negate Multiply-Subtract Double-Precision) { /* xvm{add|sub}mdp XT,XA,XB is element-wise equivalent to fm{add|sub} FRT,FRA,FRC,FRB with . . . * XT == FRC * XA == FRA * XB == FRB * * and for xvm{add|sub}adp . . . * XT == FRB * XA == FRA * XB == FRC */ Bool negate; IROp mOp = Iop_INVALID; const HChar * oper_name = NULL; Bool mdp = False; switch (opc2) { case 0x184: case 0x1A4: case 0x384: case 0x3A4: mOp = Iop_MAddF64; oper_name = "add"; mdp = (opc2 & 0x0FF) == 0x0A4; break; case 0x1C4: case 0x1E4: case 0x3C4: case 0x3E4: mOp = Iop_MSubF64; oper_name = "sub"; mdp = (opc2 & 0x0FF) == 0x0E4; break; default: vpanic("The impossible happened: dis_vxv_sp_arith(ppc)"); } switch (opc2) { case 0x384: case 0x3A4: case 0x3C4: case 0x3E4: negate = True; break; default: negate = False; } IRTemp hiResult = newTemp(Ity_I64); IRTemp loResult = newTemp(Ity_I64); IRTemp frT = newTemp(Ity_F64); IRTemp frT2 = newTemp(Ity_F64); DIP("xv%sm%s%s v%d,v%d,v%d\n", negate ? "n" : "", oper_name, mdp ? "mdp" : "adp", XT, XA, XB); assign(frT, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, getVSReg( XT ) ) ) ); assign(frT2, unop(Iop_ReinterpI64asF64, unop(Iop_V128to64, getVSReg( XT ) ) ) ); assign( hiResult, unop( Iop_ReinterpF64asI64, qop( mOp, rm, mkexpr( frA ), mkexpr( mdp ? frT : frB ), mkexpr( mdp ? frB : frT ) ) ) ); assign( loResult, unop( Iop_ReinterpF64asI64, qop( mOp, rm, mkexpr( frA2 ), mkexpr( mdp ? frT2 : frB2 ), mkexpr( mdp ? frB2 : frT2 ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( negate ? getNegatedResult( hiResult ) : hiResult ), mkexpr( negate ? getNegatedResult( loResult ) : loResult ) ) ); break; } case 0x1D4: // xvtsqrtdp (VSX Vector Test for software Square Root Double-Precision) { IRTemp frBHi_I64 = newTemp(Ity_I64); IRTemp frBLo_I64 = newTemp(Ity_I64); IRTemp flagsHi = newTemp(Ity_I32); IRTemp flagsLo = newTemp(Ity_I32); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); IRTemp fe_flagHi, fg_flagHi, fe_flagLo, fg_flagLo; fe_flagHi = fg_flagHi = fe_flagLo = fg_flagLo = IRTemp_INVALID; DIP("xvtsqrtdp cr%d,v%d\n", crfD, XB); assign( frBHi_I64, unop(Iop_V128HIto64, getVSReg( XB )) ); assign( frBLo_I64, unop(Iop_V128to64, getVSReg( XB )) ); do_fp_tsqrt(frBHi_I64, False /*not single precision*/, &fe_flagHi, &fg_flagHi); do_fp_tsqrt(frBLo_I64, False /*not single precision*/, &fe_flagLo, &fg_flagLo); /* The CR field consists of fl_flag || fg_flag || fe_flag || 0b0 * where fl_flag == 1 on ppc64. */ assign( flagsHi, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flagHi), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flagHi), mkU8( 1 ) ) ) ); assign( flagsLo, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flagLo), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flagLo), mkU8( 1 ) ) ) ); putGST_field( PPC_GST_CR, binop( Iop_Or32, mkexpr( flagsHi ), mkexpr( flagsLo ) ), crfD ); break; } case 0x1F4: // xvtdivdp (VSX Vector Test for software Divide Double-Precision) { IRTemp frBHi_I64 = newTemp(Ity_I64); IRTemp frBLo_I64 = newTemp(Ity_I64); IRTemp frAHi_I64 = newTemp(Ity_I64); IRTemp frALo_I64 = newTemp(Ity_I64); IRTemp flagsHi = newTemp(Ity_I32); IRTemp flagsLo = newTemp(Ity_I32); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); IRTemp fe_flagHi, fg_flagHi, fe_flagLo, fg_flagLo; fe_flagHi = fg_flagHi = fe_flagLo = fg_flagLo = IRTemp_INVALID; DIP("xvtdivdp cr%d,v%d,v%d\n", crfD, XA, XB); assign( frAHi_I64, unop(Iop_V128HIto64, getVSReg( XA )) ); assign( frALo_I64, unop(Iop_V128to64, getVSReg( XA )) ); assign( frBHi_I64, unop(Iop_V128HIto64, getVSReg( XB )) ); assign( frBLo_I64, unop(Iop_V128to64, getVSReg( XB )) ); _do_fp_tdiv(frAHi_I64, frBHi_I64, False/*dp*/, &fe_flagHi, &fg_flagHi); _do_fp_tdiv(frALo_I64, frBLo_I64, False/*dp*/, &fe_flagLo, &fg_flagLo); /* The CR field consists of fl_flag || fg_flag || fe_flag || 0b0 * where fl_flag == 1 on ppc64. */ assign( flagsHi, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flagHi), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flagHi), mkU8( 1 ) ) ) ); assign( flagsLo, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flagLo), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flagLo), mkU8( 1 ) ) ) ); putGST_field( PPC_GST_CR, binop( Iop_Or32, mkexpr( flagsHi ), mkexpr( flagsLo ) ), crfD ); break; } default: vex_printf( "dis_vxv_dp_arith(ppc)(opc2)\n" ); return False; } return True; } /* * VSX vector Single Precision Floating Point Arithmetic Instructions */ static Bool dis_vxv_sp_arith ( UInt theInstr, UInt opc2 ) { /* XX3-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT( theInstr ); UChar XA = ifieldRegXA( theInstr ); UChar XB = ifieldRegXB( theInstr ); IRExpr* rm = get_IR_roundingmode(); IRTemp a3, a2, a1, a0; IRTemp b3, b2, b1, b0; IRTemp res0 = newTemp(Ity_I32); IRTemp res1 = newTemp(Ity_I32); IRTemp res2 = newTemp(Ity_I32); IRTemp res3 = newTemp(Ity_I32); a3 = a2 = a1 = a0 = IRTemp_INVALID; b3 = b2 = b1 = b0 = IRTemp_INVALID; if (opc1 != 0x3C) { vex_printf( "dis_vxv_sp_arith(ppc)(instr)\n" ); return False; } switch (opc2) { case 0x100: // xvaddsp (VSX Vector Add Single-Precision) DIP("xvaddsp v%d,v%d,v%d\n", XT, XA, XB); // WARNING: BOGUS! The backend ignores rm on Iop_Add32Fx4 putVSReg( XT, triop(Iop_Add32Fx4, rm, getVSReg( XA ), getVSReg( XB )) ); break; case 0x140: // xvmulsp (VSX Vector Multiply Single-Precision) DIP("xvmulsp v%d,v%d,v%d\n", XT, XA, XB); // WARNING: BOGUS! The backend ignores rm on Iop_Mul32Fx4 putVSReg( XT, triop(Iop_Mul32Fx4, rm, getVSReg( XA ), getVSReg( XB )) ); break; case 0x120: // xvsubsp (VSX Vector Subtract Single-Precision) DIP("xvsubsp v%d,v%d,v%d\n", XT, XA, XB); // WARNING: BOGUS! The backend ignores rm on Iop_Sub32Fx4 putVSReg( XT, triop(Iop_Sub32Fx4, rm, getVSReg( XA ), getVSReg( XB )) ); break; case 0x160: // xvdivsp (VSX Vector Divide Single-Precision) { /* Iop_Div32Fx4 is not implemented for ppc64 (in host_ppc_{isel|defs}.c. * So there are two choices: * 1. Implement the xvdivsp with a native insn; or * 2. Extract the 4 single precision floats from each vector * register inputs and perform fdivs on each pair * I will do the latter, due to the general philosophy of * reusing existing implementations when practical. */ DIP("xvdivsp v%d,v%d,v%d\n", XT, XA, XB); breakV128to4xF64( getVSReg( XA ), &a3, &a2, &a1, &a0 ); breakV128to4xF64( getVSReg( XB ), &b3, &b2, &b1, &b0 ); assign( res0, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, triop( Iop_DivF64r32, rm, mkexpr( a0 ), mkexpr( b0 ) ) ) ) ); assign( res1, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, triop( Iop_DivF64r32, rm, mkexpr( a1 ), mkexpr( b1 ) ) ) ) ); assign( res2, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, triop( Iop_DivF64r32, rm, mkexpr( a2 ), mkexpr( b2 ) ) ) ) ); assign( res3, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, triop( Iop_DivF64r32, rm, mkexpr( a3 ), mkexpr( b3 ) ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkexpr( res3 ), mkexpr( res2 ) ), binop( Iop_32HLto64, mkexpr( res1 ), mkexpr( res0 ) ) ) ); break; } case 0x116: // xvsqrtsp (VSX Vector Square Root Single-Precision) { DIP("xvsqrtsp v%d,v%d\n", XT, XB); breakV128to4xF64( getVSReg( XB ), &b3, &b2, &b1, &b0 ); /* Note: The native xvsqrtsp insruction does not always give the same precision * as what we get with Iop_SqrtF64. But it doesn't seem worthwhile to implement * an Iop_SqrtF32 that would give us a lower precision result, albeit more true * to the actual instruction. */ assign( res0, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop(Iop_SqrtF64, rm, mkexpr( b0 ) ) ) ) ); assign( res1, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop(Iop_SqrtF64, rm, mkexpr( b1 ) ) ) ) ); assign( res2, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop(Iop_SqrtF64, rm, mkexpr( b2) ) ) ) ); assign( res3, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, binop(Iop_SqrtF64, rm, mkexpr( b3 ) ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkexpr( res3 ), mkexpr( res2 ) ), binop( Iop_32HLto64, mkexpr( res1 ), mkexpr( res0 ) ) ) ); break; } case 0x104: case 0x124: // xvmaddasp, xvmaddmsp (VSX Vector Multiply-Add Single-Precision) case 0x144: case 0x164: // xvmsubasp, xvmsubmsp (VSX Vector Multiply-Subtract Single-Precision) case 0x304: case 0x324: // xvnmaddasp, xvnmaddmsp (VSX Vector Negate Multiply-Add Single-Precision) case 0x344: case 0x364: // xvnmsubasp, xvnmsubmsp (VSX Vector Negate Multiply-Subtract Single-Precision) { IRTemp t3, t2, t1, t0; Bool msp = False; Bool negate; const HChar * oper_name = NULL; IROp mOp = Iop_INVALID; switch (opc2) { case 0x104: case 0x124: case 0x304: case 0x324: msp = (opc2 & 0x0FF) == 0x024; mOp = Iop_MAddF64r32; oper_name = "madd"; break; case 0x144: case 0x164: case 0x344: case 0x364: msp = (opc2 & 0x0FF) == 0x064; mOp = Iop_MSubF64r32; oper_name = "sub"; break; default: vpanic("The impossible happened: dis_vxv_sp_arith(ppc)"); } switch (opc2) { case 0x304: case 0x324: case 0x344: case 0x364: negate = True; break; default: negate = False; } DIP("xv%sm%s%s v%d,v%d,v%d\n", negate ? "n" : "", oper_name, msp ? "msp" : "asp", XT, XA, XB); t3 = t2 = t1 = t0 = IRTemp_INVALID; breakV128to4xF64( getVSReg( XA ), &a3, &a2, &a1, &a0 ); breakV128to4xF64( getVSReg( XB ), &b3, &b2, &b1, &b0 ); breakV128to4xF64( getVSReg( XT ), &t3, &t2, &t1, &t0 ); assign( res0, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, qop( mOp, rm, mkexpr( a0 ), mkexpr( msp ? t0 : b0 ), mkexpr( msp ? b0 : t0 ) ) ) ) ); assign( res1, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, qop( mOp, rm, mkexpr( a1 ), mkexpr( msp ? t1 : b1 ), mkexpr( msp ? b1 : t1 ) ) ) ) ); assign( res2, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, qop( mOp, rm, mkexpr( a2 ), mkexpr( msp ? t2 : b2 ), mkexpr( msp ? b2 : t2 ) ) ) ) ); assign( res3, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, qop( mOp, rm, mkexpr( a3 ), mkexpr( msp ? t3 : b3 ), mkexpr( msp ? b3 : t3 ) ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkexpr( negate ? getNegatedResult_32( res3 ) : res3 ), mkexpr( negate ? getNegatedResult_32( res2 ) : res2 ) ), binop( Iop_32HLto64, mkexpr( negate ? getNegatedResult_32( res1 ) : res1 ), mkexpr( negate ? getNegatedResult_32( res0 ) : res0 ) ) ) ); break; } case 0x154: // xvtsqrtsp (VSX Vector Test for software Square Root Single-Precision) { IRTemp flags0 = newTemp(Ity_I32); IRTemp flags1 = newTemp(Ity_I32); IRTemp flags2 = newTemp(Ity_I32); IRTemp flags3 = newTemp(Ity_I32); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); IRTemp fe_flag0, fg_flag0, fe_flag1, fg_flag1; IRTemp fe_flag2, fg_flag2, fe_flag3, fg_flag3; fe_flag0 = fg_flag0 = fe_flag1 = fg_flag1 = IRTemp_INVALID; fe_flag2 = fg_flag2 = fe_flag3 = fg_flag3 = IRTemp_INVALID; DIP("xvtsqrtsp cr%d,v%d\n", crfD, XB); breakV128to4x32( getVSReg( XB ), &b3, &b2, &b1, &b0 ); do_fp_tsqrt(b0, True /* single precision*/, &fe_flag0, &fg_flag0); do_fp_tsqrt(b1, True /* single precision*/, &fe_flag1, &fg_flag1); do_fp_tsqrt(b2, True /* single precision*/, &fe_flag2, &fg_flag2); do_fp_tsqrt(b3, True /* single precision*/, &fe_flag3, &fg_flag3); /* The CR field consists of fl_flag || fg_flag || fe_flag || 0b0 * where fl_flag == 1 on ppc64. */ assign( flags0, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag0), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag0), mkU8( 1 ) ) ) ); assign( flags1, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag1), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag1), mkU8( 1 ) ) ) ); assign( flags2, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag2), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag2), mkU8( 1 ) ) ) ); assign( flags3, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag3), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag3), mkU8( 1 ) ) ) ); putGST_field( PPC_GST_CR, binop( Iop_Or32, mkexpr( flags0 ), binop( Iop_Or32, mkexpr( flags1 ), binop( Iop_Or32, mkexpr( flags2 ), mkexpr( flags3 ) ) ) ), crfD ); break; } case 0x174: // xvtdivsp (VSX Vector Test for software Divide Single-Precision) { IRTemp flags0 = newTemp(Ity_I32); IRTemp flags1 = newTemp(Ity_I32); IRTemp flags2 = newTemp(Ity_I32); IRTemp flags3 = newTemp(Ity_I32); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); IRTemp fe_flag0, fg_flag0, fe_flag1, fg_flag1; IRTemp fe_flag2, fg_flag2, fe_flag3, fg_flag3; fe_flag0 = fg_flag0 = fe_flag1 = fg_flag1 = IRTemp_INVALID; fe_flag2 = fg_flag2 = fe_flag3 = fg_flag3 = IRTemp_INVALID; DIP("xvtdivsp cr%d,v%d,v%d\n", crfD, XA, XB); breakV128to4x32( getVSReg( XA ), &a3, &a2, &a1, &a0 ); breakV128to4x32( getVSReg( XB ), &b3, &b2, &b1, &b0 ); _do_fp_tdiv(a0, b0, True /* single precision*/, &fe_flag0, &fg_flag0); _do_fp_tdiv(a1, b1, True /* single precision*/, &fe_flag1, &fg_flag1); _do_fp_tdiv(a2, b2, True /* single precision*/, &fe_flag2, &fg_flag2); _do_fp_tdiv(a3, b3, True /* single precision*/, &fe_flag3, &fg_flag3); /* The CR field consists of fl_flag || fg_flag || fe_flag || 0b0 * where fl_flag == 1 on ppc64. */ assign( flags0, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag0), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag0), mkU8( 1 ) ) ) ); assign( flags1, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag1), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag1), mkU8( 1 ) ) ) ); assign( flags2, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag2), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag2), mkU8( 1 ) ) ) ); assign( flags3, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag3), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag3), mkU8( 1 ) ) ) ); putGST_field( PPC_GST_CR, binop( Iop_Or32, mkexpr( flags0 ), binop( Iop_Or32, mkexpr( flags1 ), binop( Iop_Or32, mkexpr( flags2 ), mkexpr( flags3 ) ) ) ), crfD ); break; } default: vex_printf( "dis_vxv_sp_arith(ppc)(opc2)\n" ); return False; } return True; } /* * Vector Population Count/bit matrix transpose */ static Bool dis_av_count_bitTranspose ( UInt theInstr, UInt opc2 ) { UChar vRB_addr = ifieldRegB(theInstr); UChar vRT_addr = ifieldRegDS(theInstr); UChar opc1 = ifieldOPC( theInstr ); IRTemp vB = newTemp(Ity_V128); assign( vB, getVReg(vRB_addr)); if (opc1 != 0x4) { vex_printf( "dis_av_count_bitTranspose(ppc)(instr)\n" ); return False; } switch (opc2) { case 0x702: // vclzb DIP("vclzb v%d,v%d\n", vRT_addr, vRB_addr); putVReg( vRT_addr, unop(Iop_Clz8x16, mkexpr( vB ) ) ); break; case 0x742: // vclzh DIP("vclzh v%d,v%d\n", vRT_addr, vRB_addr); putVReg( vRT_addr, unop(Iop_Clz16x8, mkexpr( vB ) ) ); break; case 0x782: // vclzw DIP("vclzw v%d,v%d\n", vRT_addr, vRB_addr); putVReg( vRT_addr, unop(Iop_Clz32x4, mkexpr( vB ) ) ); break; case 0x7C2: // vclzd DIP("vclzd v%d,v%d\n", vRT_addr, vRB_addr); putVReg( vRT_addr, unop(Iop_Clz64x2, mkexpr( vB ) ) ); break; case 0x703: // vpopcntb { /* Break vector into 32-bit words and do the population count * on byte in the words */ IRType ty = Ity_I32; IRTemp bits0_31, bits32_63, bits64_95, bits96_127; bits0_31 = bits32_63 = bits64_95 = bits96_127 = IRTemp_INVALID; IRTemp cnt_bits0_31, cnt_bits32_63, cnt_bits64_95, cnt_bits96_127; cnt_bits0_31 = cnt_bits32_63 = cnt_bits64_95 = cnt_bits96_127 = IRTemp_INVALID; DIP("vpopcntb v%d,v%d\n", vRT_addr, vRB_addr); breakV128to4x32(mkexpr( vB), &bits96_127, &bits64_95, &bits32_63, &bits0_31 ); cnt_bits0_31 = gen_POPCOUNT(ty, bits0_31, BYTE); cnt_bits32_63 = gen_POPCOUNT(ty, bits32_63, BYTE); cnt_bits64_95 = gen_POPCOUNT(ty, bits64_95, BYTE); cnt_bits96_127 = gen_POPCOUNT(ty, bits96_127, BYTE); putVReg( vRT_addr, mkV128from32(cnt_bits96_127, cnt_bits64_95, cnt_bits32_63, cnt_bits0_31) ); break; } case 0x743: // vpopcnth { /* Break vector into 32-bit words and do the population count * for each half word */ IRType ty = Ity_I32; IRTemp bits0_31, bits32_63, bits64_95, bits96_127; bits0_31 = bits32_63 = bits64_95 = bits96_127 = IRTemp_INVALID; IRTemp cnt_bits0_31, cnt_bits32_63, cnt_bits64_95, cnt_bits96_127; cnt_bits0_31 = cnt_bits32_63 = cnt_bits64_95 = cnt_bits96_127 = IRTemp_INVALID; DIP("vpopcnth v%d,v%d\n", vRT_addr, vRB_addr); breakV128to4x32(mkexpr( vB), &bits96_127, &bits64_95, &bits32_63, &bits0_31 ); cnt_bits0_31 = gen_POPCOUNT(ty, bits0_31, HWORD); cnt_bits32_63 = gen_POPCOUNT(ty, bits32_63, HWORD); cnt_bits64_95 = gen_POPCOUNT(ty, bits64_95, HWORD); cnt_bits96_127 = gen_POPCOUNT(ty, bits96_127, HWORD); putVReg( vRT_addr, mkV128from32(cnt_bits96_127, cnt_bits64_95, cnt_bits32_63, cnt_bits0_31) ); break; } case 0x783: // vpopcntw { /* Break vector into 32-bit words and do the population count * on each word. */ IRType ty = Ity_I32; IRTemp bits0_31, bits32_63, bits64_95, bits96_127; bits0_31 = bits32_63 = bits64_95 = bits96_127 = IRTemp_INVALID; IRTemp cnt_bits0_31, cnt_bits32_63, cnt_bits64_95, cnt_bits96_127; cnt_bits0_31 = cnt_bits32_63 = cnt_bits64_95 = cnt_bits96_127 = IRTemp_INVALID; DIP("vpopcntw v%d,v%d\n", vRT_addr, vRB_addr); breakV128to4x32(mkexpr( vB), &bits96_127, &bits64_95, &bits32_63, &bits0_31 ); cnt_bits0_31 = gen_POPCOUNT(ty, bits0_31, WORD); cnt_bits32_63 = gen_POPCOUNT(ty, bits32_63, WORD); cnt_bits64_95 = gen_POPCOUNT(ty, bits64_95, WORD); cnt_bits96_127 = gen_POPCOUNT(ty, bits96_127, WORD); putVReg( vRT_addr, mkV128from32(cnt_bits96_127, cnt_bits64_95, cnt_bits32_63, cnt_bits0_31) ); break; } case 0x7C3: // vpopcntd { if (mode64) { /* Break vector into 64-bit double words and do the population count * on each double word. */ IRType ty = Ity_I64; IRTemp bits0_63 = newTemp(Ity_I64); IRTemp bits64_127 = newTemp(Ity_I64); IRTemp cnt_bits0_63 = newTemp(Ity_I64); IRTemp cnt_bits64_127 = newTemp(Ity_I64); DIP("vpopcntd v%d,v%d\n", vRT_addr, vRB_addr); assign(bits0_63, unop( Iop_V128to64, mkexpr( vB ) ) ); assign(bits64_127, unop( Iop_V128HIto64, mkexpr( vB ) ) ); cnt_bits0_63 = gen_POPCOUNT(ty, bits0_63, DWORD); cnt_bits64_127 = gen_POPCOUNT(ty, bits64_127, DWORD); putVReg( vRT_addr, binop( Iop_64HLtoV128, mkexpr( cnt_bits64_127 ), mkexpr( cnt_bits0_63 ) ) ); } else { /* Break vector into 32-bit words and do the population count * on each doubleword. */ IRTemp bits0_31, bits32_63, bits64_95, bits96_127; bits0_31 = bits32_63 = bits64_95 = bits96_127 = IRTemp_INVALID; IRTemp cnt_bits0_63 = newTemp(Ity_I64); IRTemp cnt_bits64_127 = newTemp(Ity_I64); DIP("vpopcntd v%d,v%d\n", vRT_addr, vRB_addr); breakV128to4x32(mkexpr( vB), &bits96_127, &bits64_95, &bits32_63, &bits0_31 ); cnt_bits0_63 = gen_vpopcntd_mode32(bits0_31, bits32_63); cnt_bits64_127 = gen_vpopcntd_mode32(bits64_95, bits96_127); putVReg( vRT_addr, binop( Iop_64HLtoV128, mkexpr( cnt_bits64_127 ), mkexpr( cnt_bits0_63 ) ) ); } break; } case 0x50C: // vgbbd Vector Gather Bits by Bytes by Doubleword DIP("vgbbd v%d,v%d\n", vRT_addr, vRB_addr); putVReg( vRT_addr, unop( Iop_PwBitMtxXpose64x2, mkexpr( vB ) ) ); break; default: vex_printf("dis_av_count_bitTranspose(ppc)(opc2)\n"); return False; break; } return True; } typedef enum { PPC_CMP_EQ = 2, PPC_CMP_GT = 4, PPC_CMP_GE = 6, PPC_CMP_LT = 8 } ppc_cmp_t; /* This helper function takes as input the IRExpr returned from a binop( Iop_CmpF64, fpA, fpB), whose result is returned in IR form. This helper function converts it to PPC form. Map compare result from IR to PPC FP cmp result | PPC | IR -------------------------- UN | 0x1 | 0x45 EQ | 0x2 | 0x40 GT | 0x4 | 0x00 LT | 0x8 | 0x01 condcode = Shl(1, (~(ccIR>>5) & 2) | ((ccIR ^ (ccIR>>6)) & 1) */ static IRTemp get_fp_cmp_CR_val (IRExpr * ccIR_expr) { IRTemp condcode = newTemp( Ity_I32 ); IRTemp ccIR = newTemp( Ity_I32 ); assign(ccIR, ccIR_expr); assign( condcode, binop( Iop_Shl32, mkU32( 1 ), unop( Iop_32to8, binop( Iop_Or32, binop( Iop_And32, unop( Iop_Not32, binop( Iop_Shr32, mkexpr( ccIR ), mkU8( 5 ) ) ), mkU32( 2 ) ), binop( Iop_And32, binop( Iop_Xor32, mkexpr( ccIR ), binop( Iop_Shr32, mkexpr( ccIR ), mkU8( 6 ) ) ), mkU32( 1 ) ) ) ) ) ); return condcode; } /* * Helper function for get_max_min_fp for ascertaining the max or min between two doubles * following these special rules: * - The max/min of a QNaN and any value is that value * (When two QNaNs are being compared, the frA QNaN is the return value.) * - The max/min of any value and an SNaN is that SNaN converted to a QNaN * (When two SNaNs are being compared, the frA SNaN is converted to a QNaN.) */ static IRExpr * _get_maxmin_fp_NaN(IRTemp frA_I64, IRTemp frB_I64) { IRTemp frA_isNaN = newTemp(Ity_I1); IRTemp frB_isNaN = newTemp(Ity_I1); IRTemp frA_isSNaN = newTemp(Ity_I1); IRTemp frB_isSNaN = newTemp(Ity_I1); IRTemp frA_isQNaN = newTemp(Ity_I1); IRTemp frB_isQNaN = newTemp(Ity_I1); assign( frA_isNaN, is_NaN( frA_I64 ) ); assign( frB_isNaN, is_NaN( frB_I64 ) ); // If operand is a NAN and bit 12 is '0', then it's an SNaN assign( frA_isSNaN, mkAND1( mkexpr(frA_isNaN), binop( Iop_CmpEQ32, binop( Iop_And32, unop( Iop_64HIto32, mkexpr( frA_I64 ) ), mkU32( 0x00080000 ) ), mkU32( 0 ) ) ) ); assign( frB_isSNaN, mkAND1( mkexpr(frB_isNaN), binop( Iop_CmpEQ32, binop( Iop_And32, unop( Iop_64HIto32, mkexpr( frB_I64 ) ), mkU32( 0x00080000 ) ), mkU32( 0 ) ) ) ); assign( frA_isQNaN, mkAND1( mkexpr( frA_isNaN ), unop( Iop_Not1, mkexpr( frA_isSNaN ) ) ) ); assign( frB_isQNaN, mkAND1( mkexpr( frB_isNaN ), unop( Iop_Not1, mkexpr( frB_isSNaN ) ) ) ); /* Based on the rules specified in the function prologue, the algorithm is as follows: * <<<<<<<<<>>>>>>>>>>>>>>>>>> * if frA is a SNaN * result = frA converted to QNaN * else if frB is a SNaN * result = frB converted to QNaN * else if frB is a QNaN * result = frA * // One of frA or frB was a NaN in order for this function to be called, so * // if we get to this point, we KNOW that frA must be a QNaN. * else // frA is a QNaN * result = frB * <<<<<<<<<>>>>>>>>>>>>>>>>>> */ #define SNAN_MASK 0x0008000000000000ULL return IRExpr_ITE(mkexpr(frA_isSNaN), /* then: result = frA converted to QNaN */ binop(Iop_Or64, mkexpr(frA_I64), mkU64(SNAN_MASK)), /* else: if frB is a SNaN */ IRExpr_ITE(mkexpr(frB_isSNaN), /* then: result = frB converted to QNaN */ binop(Iop_Or64, mkexpr(frB_I64), mkU64(SNAN_MASK)), /* else: if frB is a QNaN */ IRExpr_ITE(mkexpr(frB_isQNaN), /* then: result = frA */ mkexpr(frA_I64), /* else: frA is a QNaN, so result = frB */ mkexpr(frB_I64)))); } /* * Helper function for get_max_min_fp. */ static IRExpr * _get_maxmin_fp_cmp(IRTemp src1, IRTemp src2, Bool isMin) { IRTemp src1cmpsrc2 = get_fp_cmp_CR_val( binop( Iop_CmpF64, unop( Iop_ReinterpI64asF64, mkexpr( src1 ) ), unop( Iop_ReinterpI64asF64, mkexpr( src2 ) ) ) ); return IRExpr_ITE( binop( Iop_CmpEQ32, mkexpr( src1cmpsrc2 ), mkU32( isMin ? PPC_CMP_LT : PPC_CMP_GT ) ), /* then: use src1 */ mkexpr( src1 ), /* else: use src2 */ mkexpr( src2 ) ); } /* * Helper function for "Maximum/Minimum Double Precision" operations. * Arguments: frA and frb are Ity_I64 * Returns Ity_I64 IRExpr that answers the "which is Maxiumum/Minimum" question */ static IRExpr * get_max_min_fp(IRTemp frA_I64, IRTemp frB_I64, Bool isMin) { /* There are three special cases where get_fp_cmp_CR_val is not helpful * for ascertaining the maximum between two doubles: * 1. The max/min of +0 and -0 is +0. * 2. The max/min of a QNaN and any value is that value. * 3. The max/min of any value and an SNaN is that SNaN converted to a QNaN. * We perform the check for [+/-]0 here in this function and use the * _get_maxmin_fp_NaN helper for the two NaN cases; otherwise we call _get_maxmin_fp_cmp * to do the standard comparison function. */ IRTemp anyNaN = newTemp(Ity_I1); IRTemp frA_isZero = newTemp(Ity_I1); IRTemp frB_isZero = newTemp(Ity_I1); assign(frA_isZero, is_Zero(frA_I64, False /*not single precision*/ )); assign(frB_isZero, is_Zero(frB_I64, False /*not single precision*/ )); assign(anyNaN, mkOR1(is_NaN(frA_I64), is_NaN(frB_I64))); #define MINUS_ZERO 0x8000000000000000ULL return IRExpr_ITE( /* If both arguments are zero . . . */ mkAND1( mkexpr( frA_isZero ), mkexpr( frB_isZero ) ), /* then: if frA is -0 and isMin==True, return -0; * else if frA is +0 and isMin==False; return +0; * otherwise, simply return frB. */ IRExpr_ITE( binop( Iop_CmpEQ32, unop( Iop_64HIto32, mkexpr( frA_I64 ) ), mkU32( isMin ? 0x80000000 : 0 ) ), mkU64( isMin ? MINUS_ZERO : 0ULL ), mkexpr( frB_I64 ) ), /* else: check if either input is a NaN*/ IRExpr_ITE( mkexpr( anyNaN ), /* then: use "NaN helper" */ _get_maxmin_fp_NaN( frA_I64, frB_I64 ), /* else: use "comparison helper" */ _get_maxmin_fp_cmp( frB_I64, frA_I64, isMin ) )); } static const HChar * _get_vsx_rdpi_suffix(UInt opc2) { switch (opc2 & 0x7F) { case 0x72: return "m"; case 0x52: return "p"; case 0x56: return "c"; case 0x32: return "z"; case 0x12: return ""; default: // Impossible to get here vex_printf("Unrecognized opcode %x\n", opc2); vpanic("_get_vsx_rdpi_suffix(ppc)(opc2)"); } } /* * Helper function for vector/scalar double precision fp round to integer instructions. */ static IRExpr * _do_vsx_fp_roundToInt(IRTemp frB_I64, UInt opc2) { /* The same rules apply for x{s|v}rdpi{m|p|c|z} as for floating point round operations (fri{m|n|p|z}). */ IRTemp frB = newTemp(Ity_F64); IRTemp frD = newTemp(Ity_F64); IRTemp intermediateResult = newTemp(Ity_I64); IRTemp is_SNAN = newTemp(Ity_I1); IRExpr * hi32; IRExpr * rxpi_rm; switch (opc2 & 0x7F) { case 0x72: rxpi_rm = mkU32(Irrm_NegINF); break; case 0x52: rxpi_rm = mkU32(Irrm_PosINF); break; case 0x56: rxpi_rm = get_IR_roundingmode(); break; case 0x32: rxpi_rm = mkU32(Irrm_ZERO); break; case 0x12: rxpi_rm = mkU32(Irrm_NEAREST); break; default: // Impossible to get here vex_printf("Unrecognized opcode %x\n", opc2); vpanic("_do_vsx_fp_roundToInt(ppc)(opc2)"); } assign(frB, unop(Iop_ReinterpI64asF64, mkexpr(frB_I64))); assign( intermediateResult, binop( Iop_F64toI64S, rxpi_rm, mkexpr( frB ) ) ); /* don't use the rounded integer if frB is outside -9e18..9e18 */ /* F64 has only log10(2**52) significant digits anyway */ /* need to preserve sign of zero */ /* frD = (fabs(frB) > 9e18) ? frB : (sign(frB)) ? -fabs((double)intermediateResult) : (double)intermediateResult */ assign( frD, IRExpr_ITE( binop( Iop_CmpNE8, unop( Iop_32to8, binop( Iop_CmpF64, IRExpr_Const( IRConst_F64( 9e18 ) ), unop( Iop_AbsF64, mkexpr( frB ) ) ) ), mkU8(0) ), mkexpr( frB ), IRExpr_ITE( binop( Iop_CmpNE32, binop( Iop_Shr32, unop( Iop_64HIto32, mkexpr( frB_I64 ) ), mkU8( 31 ) ), mkU32(0) ), unop( Iop_NegF64, unop( Iop_AbsF64, binop( Iop_I64StoF64, mkU32( 0 ), mkexpr( intermediateResult ) ) ) ), binop( Iop_I64StoF64, mkU32( 0 ), mkexpr( intermediateResult ) ) ) ) ); /* See Appendix "Floating-Point Round to Integer Model" in ISA doc. * If frB is a SNAN, then frD <- frB, with bit 12 set to '1'. */ #define SNAN_MASK 0x0008000000000000ULL hi32 = unop( Iop_64HIto32, mkexpr(frB_I64) ); assign( is_SNAN, mkAND1( is_NaN( frB_I64 ), binop( Iop_CmpEQ32, binop( Iop_And32, hi32, mkU32( 0x00080000 ) ), mkU32( 0 ) ) ) ); return IRExpr_ITE( mkexpr( is_SNAN ), unop( Iop_ReinterpI64asF64, binop( Iop_Xor64, mkU64( SNAN_MASK ), mkexpr( frB_I64 ) ) ), mkexpr( frD )); } /* * Miscellaneous VSX vector instructions */ static Bool dis_vxv_misc ( UInt theInstr, UInt opc2 ) { /* XX3-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT( theInstr ); UChar XB = ifieldRegXB( theInstr ); if (opc1 != 0x3C) { vex_printf( "dis_vxv_misc(ppc)(instr)\n" ); return False; } switch (opc2) { case 0x1B4: // xvredp (VSX Vector Reciprocal Estimate Double-Precision) case 0x194: // xvrsqrtedp (VSX Vector Reciprocal Square Root Estimate // Double-Precision) { IRExpr* ieee_one = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL)); IRExpr* rm = get_IR_roundingmode(); IRTemp frB = newTemp(Ity_I64); IRTemp frB2 = newTemp(Ity_I64); Bool redp = opc2 == 0x1B4; IRTemp sqrtHi = newTemp(Ity_F64); IRTemp sqrtLo = newTemp(Ity_F64); assign(frB, unop(Iop_V128HIto64, getVSReg( XB ))); assign(frB2, unop(Iop_V128to64, getVSReg( XB ))); DIP("%s v%d,v%d\n", redp ? "xvredp" : "xvrsqrtedp", XT, XB); if (!redp) { assign( sqrtHi, binop( Iop_SqrtF64, rm, unop( Iop_ReinterpI64asF64, mkexpr( frB ) ) ) ); assign( sqrtLo, binop( Iop_SqrtF64, rm, unop( Iop_ReinterpI64asF64, mkexpr( frB2 ) ) ) ); } putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, triop( Iop_DivF64, rm, ieee_one, redp ? unop( Iop_ReinterpI64asF64, mkexpr( frB ) ) : mkexpr( sqrtHi ) ) ), unop( Iop_ReinterpF64asI64, triop( Iop_DivF64, rm, ieee_one, redp ? unop( Iop_ReinterpI64asF64, mkexpr( frB2 ) ) : mkexpr( sqrtLo ) ) ) ) ); break; } case 0x134: // xvresp (VSX Vector Reciprocal Estimate Single-Precision) case 0x114: // xvrsqrtesp (VSX Vector Reciprocal Square Root Estimate Single-Precision) { IRTemp b3, b2, b1, b0; IRTemp res0 = newTemp(Ity_I32); IRTemp res1 = newTemp(Ity_I32); IRTemp res2 = newTemp(Ity_I32); IRTemp res3 = newTemp(Ity_I32); IRTemp sqrt3 = newTemp(Ity_F64); IRTemp sqrt2 = newTemp(Ity_F64); IRTemp sqrt1 = newTemp(Ity_F64); IRTemp sqrt0 = newTemp(Ity_F64); IRExpr* rm = get_IR_roundingmode(); Bool resp = opc2 == 0x134; IRExpr* ieee_one = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL)); b3 = b2 = b1 = b0 = IRTemp_INVALID; DIP("%s v%d,v%d\n", resp ? "xvresp" : "xvrsqrtesp", XT, XB); breakV128to4xF64( getVSReg( XB ), &b3, &b2, &b1, &b0 ); if (!resp) { assign( sqrt3, binop( Iop_SqrtF64, rm, mkexpr( b3 ) ) ); assign( sqrt2, binop( Iop_SqrtF64, rm, mkexpr( b2 ) ) ); assign( sqrt1, binop( Iop_SqrtF64, rm, mkexpr( b1 ) ) ); assign( sqrt0, binop( Iop_SqrtF64, rm, mkexpr( b0 ) ) ); } assign( res0, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, triop( Iop_DivF64r32, rm, ieee_one, resp ? mkexpr( b0 ) : mkexpr( sqrt0 ) ) ) ) ); assign( res1, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, triop( Iop_DivF64r32, rm, ieee_one, resp ? mkexpr( b1 ) : mkexpr( sqrt1 ) ) ) ) ); assign( res2, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, triop( Iop_DivF64r32, rm, ieee_one, resp ? mkexpr( b2 ) : mkexpr( sqrt2 ) ) ) ) ); assign( res3, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, triop( Iop_DivF64r32, rm, ieee_one, resp ? mkexpr( b3 ) : mkexpr( sqrt3 ) ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkexpr( res3 ), mkexpr( res2 ) ), binop( Iop_32HLto64, mkexpr( res1 ), mkexpr( res0 ) ) ) ); break; } case 0x300: // xvmaxsp (VSX Vector Maximum Single-Precision) case 0x320: // xvminsp (VSX Vector Minimum Single-Precision) { UChar XA = ifieldRegXA( theInstr ); IRTemp a3, a2, a1, a0; IRTemp b3, b2, b1, b0; IRTemp res0 = newTemp( Ity_I32 ); IRTemp res1 = newTemp( Ity_I32 ); IRTemp res2 = newTemp( Ity_I32 ); IRTemp res3 = newTemp( Ity_I32 ); IRTemp a0_I64 = newTemp( Ity_I64 ); IRTemp a1_I64 = newTemp( Ity_I64 ); IRTemp a2_I64 = newTemp( Ity_I64 ); IRTemp a3_I64 = newTemp( Ity_I64 ); IRTemp b0_I64 = newTemp( Ity_I64 ); IRTemp b1_I64 = newTemp( Ity_I64 ); IRTemp b2_I64 = newTemp( Ity_I64 ); IRTemp b3_I64 = newTemp( Ity_I64 ); Bool isMin = opc2 == 0x320 ? True : False; a3 = a2 = a1 = a0 = IRTemp_INVALID; b3 = b2 = b1 = b0 = IRTemp_INVALID; DIP("%s v%d,v%d v%d\n", isMin ? "xvminsp" : "xvmaxsp", XT, XA, XB); breakV128to4xF64( getVSReg( XA ), &a3, &a2, &a1, &a0 ); breakV128to4xF64( getVSReg( XB ), &b3, &b2, &b1, &b0 ); assign( a0_I64, unop( Iop_ReinterpF64asI64, mkexpr( a0 ) ) ); assign( b0_I64, unop( Iop_ReinterpF64asI64, mkexpr( b0 ) ) ); assign( a1_I64, unop( Iop_ReinterpF64asI64, mkexpr( a1 ) ) ); assign( b1_I64, unop( Iop_ReinterpF64asI64, mkexpr( b1 ) ) ); assign( a2_I64, unop( Iop_ReinterpF64asI64, mkexpr( a2 ) ) ); assign( b2_I64, unop( Iop_ReinterpF64asI64, mkexpr( b2 ) ) ); assign( a3_I64, unop( Iop_ReinterpF64asI64, mkexpr( a3 ) ) ); assign( b3_I64, unop( Iop_ReinterpF64asI64, mkexpr( b3 ) ) ); assign( res0, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, unop( Iop_ReinterpI64asF64, get_max_min_fp( a0_I64, b0_I64, isMin ) ) ) ) ); assign( res1, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, unop( Iop_ReinterpI64asF64, get_max_min_fp( a1_I64, b1_I64, isMin ) ) ) ) ); assign( res2, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, unop( Iop_ReinterpI64asF64, get_max_min_fp( a2_I64, b2_I64, isMin ) ) ) ) ); assign( res3, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, unop( Iop_ReinterpI64asF64, get_max_min_fp( a3_I64, b3_I64, isMin ) ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkexpr( res3 ), mkexpr( res2 ) ), binop( Iop_32HLto64, mkexpr( res1 ), mkexpr( res0 ) ) ) ); break; } case 0x380: // xvmaxdp (VSX Vector Maximum Double-Precision) case 0x3A0: // xvmindp (VSX Vector Minimum Double-Precision) { UChar XA = ifieldRegXA( theInstr ); IRTemp frA = newTemp(Ity_I64); IRTemp frB = newTemp(Ity_I64); IRTemp frA2 = newTemp(Ity_I64); IRTemp frB2 = newTemp(Ity_I64); Bool isMin = opc2 == 0x3A0 ? True : False; assign(frA, unop(Iop_V128HIto64, getVSReg( XA ))); assign(frB, unop(Iop_V128HIto64, getVSReg( XB ))); assign(frA2, unop(Iop_V128to64, getVSReg( XA ))); assign(frB2, unop(Iop_V128to64, getVSReg( XB ))); DIP("%s v%d,v%d v%d\n", isMin ? "xvmindp" : "xvmaxdp", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, get_max_min_fp(frA, frB, isMin), get_max_min_fp(frA2, frB2, isMin) ) ); break; } case 0x3c0: // xvcpsgndp (VSX Vector Copy Sign Double-Precision) { UChar XA = ifieldRegXA( theInstr ); IRTemp frA = newTemp(Ity_I64); IRTemp frB = newTemp(Ity_I64); IRTemp frA2 = newTemp(Ity_I64); IRTemp frB2 = newTemp(Ity_I64); assign(frA, unop(Iop_V128HIto64, getVSReg( XA ))); assign(frB, unop(Iop_V128HIto64, getVSReg( XB ))); assign(frA2, unop(Iop_V128to64, getVSReg( XA ))); assign(frB2, unop(Iop_V128to64, getVSReg( XB ))); DIP("xvcpsgndp v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_Or64, binop( Iop_And64, mkexpr( frA ), mkU64( SIGN_BIT ) ), binop( Iop_And64, mkexpr( frB ), mkU64( SIGN_MASK ) ) ), binop( Iop_Or64, binop( Iop_And64, mkexpr( frA2 ), mkU64( SIGN_BIT ) ), binop( Iop_And64, mkexpr( frB2 ), mkU64( SIGN_MASK ) ) ) ) ); break; } case 0x340: // xvcpsgnsp { UChar XA = ifieldRegXA( theInstr ); IRTemp a3_I64, a2_I64, a1_I64, a0_I64; IRTemp b3_I64, b2_I64, b1_I64, b0_I64; IRTemp resHi = newTemp(Ity_I64); IRTemp resLo = newTemp(Ity_I64); a3_I64 = a2_I64 = a1_I64 = a0_I64 = IRTemp_INVALID; b3_I64 = b2_I64 = b1_I64 = b0_I64 = IRTemp_INVALID; DIP("xvcpsgnsp v%d,v%d v%d\n",XT, XA, XB); breakV128to4x64U( getVSReg( XA ), &a3_I64, &a2_I64, &a1_I64, &a0_I64 ); breakV128to4x64U( getVSReg( XB ), &b3_I64, &b2_I64, &b1_I64, &b0_I64 ); assign( resHi, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_And32, unop(Iop_64to32, mkexpr( a3_I64 ) ), mkU32( SIGN_BIT32 ) ), binop( Iop_And32, unop(Iop_64to32, mkexpr( b3_I64 ) ), mkU32( SIGN_MASK32) ) ), binop( Iop_Or32, binop( Iop_And32, unop(Iop_64to32, mkexpr( a2_I64 ) ), mkU32( SIGN_BIT32 ) ), binop( Iop_And32, unop(Iop_64to32, mkexpr( b2_I64 ) ), mkU32( SIGN_MASK32 ) ) ) ) ); assign( resLo, binop( Iop_32HLto64, binop( Iop_Or32, binop( Iop_And32, unop(Iop_64to32, mkexpr( a1_I64 ) ), mkU32( SIGN_BIT32 ) ), binop( Iop_And32, unop(Iop_64to32, mkexpr( b1_I64 ) ), mkU32( SIGN_MASK32 ) ) ), binop( Iop_Or32, binop( Iop_And32, unop(Iop_64to32, mkexpr( a0_I64 ) ), mkU32( SIGN_BIT32 ) ), binop( Iop_And32, unop(Iop_64to32, mkexpr( b0_I64 ) ), mkU32( SIGN_MASK32 ) ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( resHi ), mkexpr( resLo ) ) ); break; } case 0x3B2: // xvabsdp (VSX Vector Absolute Value Double-Precision) case 0x3D2: // xvnabsdp VSX Vector Negative Absolute Value Double-Precision) { IRTemp frB = newTemp(Ity_F64); IRTemp frB2 = newTemp(Ity_F64); IRTemp abs_resultHi = newTemp(Ity_F64); IRTemp abs_resultLo = newTemp(Ity_F64); Bool make_negative = (opc2 == 0x3D2) ? True : False; assign(frB, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, getVSReg( XB )))); assign(frB2, unop(Iop_ReinterpI64asF64, unop(Iop_V128to64, getVSReg(XB)))); DIP("xv%sabsdp v%d,v%d\n", make_negative ? "n" : "", XT, XB); if (make_negative) { assign(abs_resultHi, unop( Iop_NegF64, unop( Iop_AbsF64, mkexpr( frB ) ) ) ); assign(abs_resultLo, unop( Iop_NegF64, unop( Iop_AbsF64, mkexpr( frB2 ) ) ) ); } else { assign(abs_resultHi, unop( Iop_AbsF64, mkexpr( frB ) ) ); assign(abs_resultLo, unop( Iop_AbsF64, mkexpr( frB2 ) ) ); } putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, mkexpr( abs_resultHi ) ), unop( Iop_ReinterpF64asI64, mkexpr( abs_resultLo ) ) ) ); break; } case 0x332: // xvabssp (VSX Vector Absolute Value Single-Precision) case 0x352: // xvnabssp (VSX Vector Negative Absolute Value Single-Precision) { /* * The Iop_AbsF32 IRop is not implemented for ppc64 since, up until introduction * of xvabssp, there has not been an abs(sp) type of instruction. But since emulation * of this function is so easy using shifts, I choose to emulate this instruction that * way versus a native instruction method of implementation. */ Bool make_negative = (opc2 == 0x352) ? True : False; IRTemp shiftVector = newTemp(Ity_V128); IRTemp absVal_vector = newTemp(Ity_V128); assign( shiftVector, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkU32( 1 ), mkU32( 1 ) ), binop( Iop_32HLto64, mkU32( 1 ), mkU32( 1 ) ) ) ); assign( absVal_vector, binop( Iop_Shr32x4, binop( Iop_Shl32x4, getVSReg( XB ), mkexpr( shiftVector ) ), mkexpr( shiftVector ) ) ); if (make_negative) { IRTemp signBit_vector = newTemp(Ity_V128); assign( signBit_vector, binop( Iop_64HLtoV128, binop( Iop_32HLto64, mkU32( 0x80000000 ), mkU32( 0x80000000 ) ), binop( Iop_32HLto64, mkU32( 0x80000000 ), mkU32( 0x80000000 ) ) ) ); putVSReg( XT, binop( Iop_OrV128, mkexpr( absVal_vector ), mkexpr( signBit_vector ) ) ); } else { putVSReg( XT, mkexpr( absVal_vector ) ); } break; } case 0x3F2: // xvnegdp (VSX Vector Negate Double-Precision) { IRTemp frB = newTemp(Ity_F64); IRTemp frB2 = newTemp(Ity_F64); assign(frB, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, getVSReg( XB )))); assign(frB2, unop(Iop_ReinterpI64asF64, unop(Iop_V128to64, getVSReg(XB)))); DIP("xvnegdp v%d,v%d\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, unop( Iop_NegF64, mkexpr( frB ) ) ), unop( Iop_ReinterpF64asI64, unop( Iop_NegF64, mkexpr( frB2 ) ) ) ) ); break; } case 0x192: // xvrdpi (VSX Vector Round to Double-Precision Integer using round toward Nearest Away) case 0x1D6: // xvrdpic (VSX Vector Round to Double-Precision Integer using Current rounding mode) case 0x1F2: // xvrdpim (VSX Vector Round to Double-Precision Integer using round toward -Infinity) case 0x1D2: // xvrdpip (VSX Vector Round to Double-Precision Integer using round toward +Infinity) case 0x1B2: // xvrdpiz (VSX Vector Round to Double-Precision Integer using round toward Zero) { IRTemp frBHi_I64 = newTemp(Ity_I64); IRTemp frBLo_I64 = newTemp(Ity_I64); IRExpr * frD_fp_roundHi = NULL; IRExpr * frD_fp_roundLo = NULL; assign( frBHi_I64, unop( Iop_V128HIto64, getVSReg( XB ) ) ); frD_fp_roundHi = _do_vsx_fp_roundToInt(frBHi_I64, opc2); assign( frBLo_I64, unop( Iop_V128to64, getVSReg( XB ) ) ); frD_fp_roundLo = _do_vsx_fp_roundToInt(frBLo_I64, opc2); DIP("xvrdpi%s v%d,v%d\n", _get_vsx_rdpi_suffix(opc2), XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, frD_fp_roundHi ), unop( Iop_ReinterpF64asI64, frD_fp_roundLo ) ) ); break; } case 0x112: // xvrspi (VSX Vector Round to Single-Precision Integer using round toward Nearest Away) case 0x156: // xvrspic (VSX Vector Round to SinglePrecision Integer using Current rounding mode) case 0x172: // xvrspim (VSX Vector Round to SinglePrecision Integer using round toward -Infinity) case 0x152: // xvrspip (VSX Vector Round to SinglePrecision Integer using round toward +Infinity) case 0x132: // xvrspiz (VSX Vector Round to SinglePrecision Integer using round toward Zero) { const HChar * insn_suffix = NULL; IROp op; if (opc2 != 0x156) { // Use pre-defined IRop's for vrfi{m|n|p|z} switch (opc2) { case 0x112: insn_suffix = ""; op = Iop_RoundF32x4_RN; break; case 0x172: insn_suffix = "m"; op = Iop_RoundF32x4_RM; break; case 0x152: insn_suffix = "p"; op = Iop_RoundF32x4_RP; break; case 0x132: insn_suffix = "z"; op = Iop_RoundF32x4_RZ; break; default: vex_printf("Unrecognized opcode %x\n", opc2); vpanic("dis_vxv_misc(ppc)(vrspi<x>)(opc2)\n"); } DIP("xvrspi%s v%d,v%d\n", insn_suffix, XT, XB); putVSReg( XT, unop( op, getVSReg(XB) ) ); } else { // Handle xvrspic. Unfortunately there is no corresponding "vfric" instruction. IRExpr * frD_fp_roundb3, * frD_fp_roundb2, * frD_fp_roundb1, * frD_fp_roundb0; IRTemp b3_F64, b2_F64, b1_F64, b0_F64; IRTemp b3_I64 = newTemp(Ity_I64); IRTemp b2_I64 = newTemp(Ity_I64); IRTemp b1_I64 = newTemp(Ity_I64); IRTemp b0_I64 = newTemp(Ity_I64); b3_F64 = b2_F64 = b1_F64 = b0_F64 = IRTemp_INVALID; frD_fp_roundb3 = frD_fp_roundb2 = frD_fp_roundb1 = frD_fp_roundb0 = NULL; breakV128to4xF64( getVSReg(XB), &b3_F64, &b2_F64, &b1_F64, &b0_F64); assign(b3_I64, unop(Iop_ReinterpF64asI64, mkexpr(b3_F64))); assign(b2_I64, unop(Iop_ReinterpF64asI64, mkexpr(b2_F64))); assign(b1_I64, unop(Iop_ReinterpF64asI64, mkexpr(b1_F64))); assign(b0_I64, unop(Iop_ReinterpF64asI64, mkexpr(b0_F64))); frD_fp_roundb3 = unop(Iop_TruncF64asF32, _do_vsx_fp_roundToInt(b3_I64, opc2)); frD_fp_roundb2 = unop(Iop_TruncF64asF32, _do_vsx_fp_roundToInt(b2_I64, opc2)); frD_fp_roundb1 = unop(Iop_TruncF64asF32, _do_vsx_fp_roundToInt(b1_I64, opc2)); frD_fp_roundb0 = unop(Iop_TruncF64asF32, _do_vsx_fp_roundToInt(b0_I64, opc2)); DIP("xvrspic v%d,v%d\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, frD_fp_roundb3 ), unop( Iop_ReinterpF32asI32, frD_fp_roundb2 ) ), binop( Iop_32HLto64, unop( Iop_ReinterpF32asI32, frD_fp_roundb1 ), unop( Iop_ReinterpF32asI32, frD_fp_roundb0 ) ) ) ); } break; } default: vex_printf( "dis_vxv_misc(ppc)(opc2)\n" ); return False; } return True; } /* * VSX Scalar Floating Point Arithmetic Instructions */ static Bool dis_vxs_arith ( UInt theInstr, UInt opc2 ) { /* XX3-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT( theInstr ); UChar XA = ifieldRegXA( theInstr ); UChar XB = ifieldRegXB( theInstr ); IRExpr* rm = get_IR_roundingmode(); IRTemp frA = newTemp(Ity_F64); IRTemp frB = newTemp(Ity_F64); if (opc1 != 0x3C) { vex_printf( "dis_vxs_arith(ppc)(instr)\n" ); return False; } assign(frA, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, getVSReg( XA )))); assign(frB, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, getVSReg( XB )))); /* For all the VSX sclar arithmetic instructions, the contents of doubleword element 1 * of VSX[XT] are undefined after the operation; therefore, we can simply set * element to zero where it makes sense to do so. */ switch (opc2) { case 0x000: // xsaddsp (VSX Scalar Add Single-Precision) DIP("xsaddsp v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, triop( Iop_AddF64, rm, mkexpr( frA ), mkexpr( frB ) ) ) ), mkU64( 0 ) ) ); break; case 0x020: // xssubsp (VSX Scalar Subtract Single-Precision) DIP("xssubsp v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, triop( Iop_SubF64, rm, mkexpr( frA ), mkexpr( frB ) ) ) ), mkU64( 0 ) ) ); break; case 0x080: // xsadddp (VSX scalar add double-precision) DIP("xsadddp v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, triop( Iop_AddF64, rm, mkexpr( frA ), mkexpr( frB ) ) ), mkU64( 0 ) ) ); break; case 0x060: // xsdivsp (VSX scalar divide single-precision) DIP("xsdivsp v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, triop( Iop_DivF64, rm, mkexpr( frA ), mkexpr( frB ) ) ) ), mkU64( 0 ) ) ); break; case 0x0E0: // xsdivdp (VSX scalar divide double-precision) DIP("xsdivdp v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, triop( Iop_DivF64, rm, mkexpr( frA ), mkexpr( frB ) ) ), mkU64( 0 ) ) ); break; case 0x004: case 0x024: /* xsmaddasp, xsmaddmsp (VSX scalar multiply-add * single-precision) */ { IRTemp frT = newTemp(Ity_F64); Bool mdp = opc2 == 0x024; DIP("xsmadd%ssp v%d,v%d,v%d\n", mdp ? "m" : "a", XT, XA, XB); assign( frT, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XT ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, qop( Iop_MAddF64, rm, mkexpr( frA ), mkexpr( mdp ? frT : frB ), mkexpr( mdp ? frB : frT ) ) ) ), mkU64( 0 ) ) ); break; } case 0x084: case 0x0A4: // xsmaddadp, xsmaddmdp (VSX scalar multiply-add double-precision) { IRTemp frT = newTemp(Ity_F64); Bool mdp = opc2 == 0x0A4; DIP("xsmadd%sdp v%d,v%d,v%d\n", mdp ? "m" : "a", XT, XA, XB); assign( frT, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XT ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, qop( Iop_MAddF64, rm, mkexpr( frA ), mkexpr( mdp ? frT : frB ), mkexpr( mdp ? frB : frT ) ) ), mkU64( 0 ) ) ); break; } case 0x044: case 0x064: /* xsmsubasp, xsmsubmsp (VSX scalar * multiply-subtract single-precision) */ { IRTemp frT = newTemp(Ity_F64); Bool mdp = opc2 == 0x064; DIP("xsmsub%ssp v%d,v%d,v%d\n", mdp ? "m" : "a", XT, XA, XB); assign( frT, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XT ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, qop( Iop_MSubF64, rm, mkexpr( frA ), mkexpr( mdp ? frT : frB ), mkexpr( mdp ? frB : frT ) ) ) ), mkU64( 0 ) ) ); break; } case 0x0C4: case 0x0E4: // xsmsubadp, xsmsubmdp (VSX scalar multiply-subtract double-precision) { IRTemp frT = newTemp(Ity_F64); Bool mdp = opc2 == 0x0E4; DIP("xsmsub%sdp v%d,v%d,v%d\n", mdp ? "m" : "a", XT, XA, XB); assign( frT, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XT ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, qop( Iop_MSubF64, rm, mkexpr( frA ), mkexpr( mdp ? frT : frB ), mkexpr( mdp ? frB : frT ) ) ), mkU64( 0 ) ) ); break; } case 0x284: case 0x2A4: // xsnmaddadp, xsnmaddmdp (VSX scalar multiply-add double-precision) { /* TODO: mpj -- Naturally, I expected to be able to leverage the implementation * of fnmadd and use pretty much the same code. However, that code has a bug in the * way it blindly negates the signbit, even if the floating point result is a NaN. * So, the TODO is to fix fnmadd (which I'll do in a different patch). * FIXED 7/1/2012: carll fnmadd and fnmsubs fixed to not negate sign * bit for NaN result. */ Bool mdp = opc2 == 0x2A4; IRTemp frT = newTemp(Ity_F64); IRTemp maddResult = newTemp(Ity_I64); DIP("xsnmadd%sdp v%d,v%d,v%d\n", mdp ? "m" : "a", XT, XA, XB); assign( frT, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XT ) ) ) ); assign( maddResult, unop( Iop_ReinterpF64asI64, qop( Iop_MAddF64, rm, mkexpr( frA ), mkexpr( mdp ? frT : frB ), mkexpr( mdp ? frB : frT ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( getNegatedResult(maddResult) ), mkU64( 0 ) ) ); break; } case 0x204: case 0x224: /* xsnmaddasp, xsnmaddmsp (VSX scalar * multiply-add single-precision) */ { Bool mdp = opc2 == 0x224; IRTemp frT = newTemp(Ity_F64); IRTemp maddResult = newTemp(Ity_I64); DIP("xsnmadd%ssp v%d,v%d,v%d\n", mdp ? "m" : "a", XT, XA, XB); assign( frT, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XT ) ) ) ); assign( maddResult, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, qop( Iop_MAddF64, rm, mkexpr( frA ), mkexpr( mdp ? frT : frB ), mkexpr( mdp ? frB : frT ) ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( getNegatedResult(maddResult) ), mkU64( 0 ) ) ); break; } case 0x244: case 0x264: /* xsnmsubasp, xsnmsubmsp (VSX Scalar Negative * Multiply-Subtract Single-Precision) */ { IRTemp frT = newTemp(Ity_F64); Bool mdp = opc2 == 0x264; IRTemp msubResult = newTemp(Ity_I64); DIP("xsnmsub%ssp v%d,v%d,v%d\n", mdp ? "m" : "a", XT, XA, XB); assign( frT, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XT ) ) ) ); assign( msubResult, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, qop( Iop_MSubF64, rm, mkexpr( frA ), mkexpr( mdp ? frT : frB ), mkexpr( mdp ? frB : frT ) ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( getNegatedResult(msubResult) ), mkU64( 0 ) ) ); break; } case 0x2C4: case 0x2E4: // xsnmsubadp, xsnmsubmdp (VSX Scalar Negative Multiply-Subtract Double-Precision) { IRTemp frT = newTemp(Ity_F64); Bool mdp = opc2 == 0x2E4; IRTemp msubResult = newTemp(Ity_I64); DIP("xsnmsub%sdp v%d,v%d,v%d\n", mdp ? "m" : "a", XT, XA, XB); assign( frT, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, getVSReg( XT ) ) ) ); assign(msubResult, unop( Iop_ReinterpF64asI64, qop( Iop_MSubF64, rm, mkexpr( frA ), mkexpr( mdp ? frT : frB ), mkexpr( mdp ? frB : frT ) ) )); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( getNegatedResult(msubResult) ), mkU64( 0 ) ) ); break; } case 0x040: // xsmulsp (VSX Scalar Multiply Single-Precision) DIP("xsmulsp v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, triop( Iop_MulF64, rm, mkexpr( frA ), mkexpr( frB ) ) ) ), mkU64( 0 ) ) ); break; case 0x0C0: // xsmuldp (VSX Scalar Multiply Double-Precision) DIP("xsmuldp v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, triop( Iop_MulF64, rm, mkexpr( frA ), mkexpr( frB ) ) ), mkU64( 0 ) ) ); break; case 0x0A0: // xssubdp (VSX Scalar Subtract Double-Precision) DIP("xssubdp v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, triop( Iop_SubF64, rm, mkexpr( frA ), mkexpr( frB ) ) ), mkU64( 0 ) ) ); break; case 0x016: // xssqrtsp (VSX Scalar Square Root Single-Precision) DIP("xssqrtsp v%d,v%d\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, binop( Iop_SqrtF64, rm, mkexpr( frB ) ) ) ), mkU64( 0 ) ) ); break; case 0x096: // xssqrtdp (VSX Scalar Square Root Double-Precision) DIP("xssqrtdp v%d,v%d\n", XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_SqrtF64, rm, mkexpr( frB ) ) ), mkU64( 0 ) ) ); break; case 0x0F4: // xstdivdp (VSX Scalar Test for software Divide Double-Precision) { UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); IRTemp frA_I64 = newTemp(Ity_I64); IRTemp frB_I64 = newTemp(Ity_I64); DIP("xstdivdp crf%d,v%d,v%d\n", crfD, XA, XB); assign( frA_I64, unop( Iop_ReinterpF64asI64, mkexpr( frA ) ) ); assign( frB_I64, unop( Iop_ReinterpF64asI64, mkexpr( frB ) ) ); putGST_field( PPC_GST_CR, do_fp_tdiv(frA_I64, frB_I64), crfD ); break; } case 0x0D4: // xstsqrtdp (VSX Vector Test for software Square Root Double-Precision) { IRTemp frB_I64 = newTemp(Ity_I64); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); IRTemp flags = newTemp(Ity_I32); IRTemp fe_flag, fg_flag; fe_flag = fg_flag = IRTemp_INVALID; DIP("xstsqrtdp v%d,v%d\n", XT, XB); assign( frB_I64, unop(Iop_V128HIto64, getVSReg( XB )) ); do_fp_tsqrt(frB_I64, False /*not single precision*/, &fe_flag, &fg_flag); /* The CR field consists of fl_flag || fg_flag || fe_flag || 0b0 * where fl_flag == 1 on ppc64. */ assign( flags, binop( Iop_Or32, binop( Iop_Or32, mkU32( 8 ), // fl_flag binop( Iop_Shl32, mkexpr(fg_flag), mkU8( 2 ) ) ), binop( Iop_Shl32, mkexpr(fe_flag), mkU8( 1 ) ) ) ); putGST_field( PPC_GST_CR, mkexpr(flags), crfD ); break; } default: vex_printf( "dis_vxs_arith(ppc)(opc2)\n" ); return False; } return True; } /* * VSX Floating Point Compare Instructions */ static Bool dis_vx_cmp( UInt theInstr, UInt opc2 ) { /* XX3-Form and XX2-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) ); IRTemp ccPPC32; UChar XA = ifieldRegXA ( theInstr ); UChar XB = ifieldRegXB ( theInstr ); IRTemp frA = newTemp(Ity_F64); IRTemp frB = newTemp(Ity_F64); if (opc1 != 0x3C) { vex_printf( "dis_vx_cmp(ppc)(instr)\n" ); return False; } assign(frA, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, getVSReg( XA )))); assign(frB, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, getVSReg( XB )))); switch (opc2) { case 0x08C: case 0x0AC: // xscmpudp, xscmpodp /* Note: Differences between xscmpudp and xscmpodp are only in * exception flag settings, which aren't supported anyway. */ DIP("xscmp%sdp crf%d,fr%u,fr%u\n", opc2 == 0x08c ? "u" : "o", crfD, XA, XB); ccPPC32 = get_fp_cmp_CR_val( binop(Iop_CmpF64, mkexpr(frA), mkexpr(frB))); putGST_field( PPC_GST_CR, mkexpr(ccPPC32), crfD ); break; default: vex_printf( "dis_vx_cmp(ppc)(opc2)\n" ); return False; } return True; } static void do_vvec_fp_cmp ( IRTemp vA, IRTemp vB, UChar XT, UChar flag_rC, ppc_cmp_t cmp_type ) { IRTemp frA_hi = newTemp(Ity_F64); IRTemp frB_hi = newTemp(Ity_F64); IRTemp frA_lo = newTemp(Ity_F64); IRTemp frB_lo = newTemp(Ity_F64); IRTemp ccPPC32 = newTemp(Ity_I32); IRTemp ccIR_hi; IRTemp ccIR_lo; IRTemp hiResult = newTemp(Ity_I64); IRTemp loResult = newTemp(Ity_I64); IRTemp hiEQlo = newTemp(Ity_I1); IRTemp all_elem_true = newTemp(Ity_I32); IRTemp all_elem_false = newTemp(Ity_I32); assign(frA_hi, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, mkexpr( vA )))); assign(frB_hi, unop(Iop_ReinterpI64asF64, unop(Iop_V128HIto64, mkexpr( vB )))); assign(frA_lo, unop(Iop_ReinterpI64asF64, unop(Iop_V128to64, mkexpr( vA )))); assign(frB_lo, unop(Iop_ReinterpI64asF64, unop(Iop_V128to64, mkexpr( vB )))); ccIR_hi = get_fp_cmp_CR_val( binop( Iop_CmpF64, mkexpr( frA_hi ), mkexpr( frB_hi ) ) ); ccIR_lo = get_fp_cmp_CR_val( binop( Iop_CmpF64, mkexpr( frA_lo ), mkexpr( frB_lo ) ) ); if (cmp_type != PPC_CMP_GE) { assign( hiResult, unop( Iop_1Sto64, binop( Iop_CmpEQ32, mkexpr( ccIR_hi ), mkU32( cmp_type ) ) ) ); assign( loResult, unop( Iop_1Sto64, binop( Iop_CmpEQ32, mkexpr( ccIR_lo ), mkU32( cmp_type ) ) ) ); } else { // For PPC_CMP_GE, one element compare may return "4" (for "greater than") and // the other element compare may return "2" (for "equal to"). IRTemp lo_GE = newTemp(Ity_I1); IRTemp hi_GE = newTemp(Ity_I1); assign(hi_GE, mkOR1( binop( Iop_CmpEQ32, mkexpr( ccIR_hi ), mkU32( 2 ) ), binop( Iop_CmpEQ32, mkexpr( ccIR_hi ), mkU32( 4 ) ) ) ); assign( hiResult,unop( Iop_1Sto64, mkexpr( hi_GE ) ) ); assign(lo_GE, mkOR1( binop( Iop_CmpEQ32, mkexpr( ccIR_lo ), mkU32( 2 ) ), binop( Iop_CmpEQ32, mkexpr( ccIR_lo ), mkU32( 4 ) ) ) ); assign( loResult, unop( Iop_1Sto64, mkexpr( lo_GE ) ) ); } // The [hi/lo]Result will be all 1's or all 0's. We just look at the lower word. assign( hiEQlo, binop( Iop_CmpEQ32, unop( Iop_64to32, mkexpr( hiResult ) ), unop( Iop_64to32, mkexpr( loResult ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( hiResult ), mkexpr( loResult ) ) ); assign( all_elem_true, unop( Iop_1Uto32, mkAND1( mkexpr( hiEQlo ), binop( Iop_CmpEQ32, mkU32( 0xffffffff ), unop( Iop_64to32, mkexpr( hiResult ) ) ) ) ) ); assign( all_elem_false, unop( Iop_1Uto32, mkAND1( mkexpr( hiEQlo ), binop( Iop_CmpEQ32, mkU32( 0 ), unop( Iop_64to32, mkexpr( hiResult ) ) ) ) ) ); assign( ccPPC32, binop( Iop_Or32, binop( Iop_Shl32, mkexpr( all_elem_false ), mkU8( 1 ) ), binop( Iop_Shl32, mkexpr( all_elem_true ), mkU8( 3 ) ) ) ); if (flag_rC) { putGST_field( PPC_GST_CR, mkexpr(ccPPC32), 6 ); } } /* * VSX Vector Compare Instructions */ static Bool dis_vvec_cmp( UInt theInstr, UInt opc2 ) { /* XX3-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT ( theInstr ); UChar XA = ifieldRegXA ( theInstr ); UChar XB = ifieldRegXB ( theInstr ); UChar flag_rC = ifieldBIT10(theInstr); IRTemp vA = newTemp( Ity_V128 ); IRTemp vB = newTemp( Ity_V128 ); if (opc1 != 0x3C) { vex_printf( "dis_vvec_cmp(ppc)(instr)\n" ); return False; } assign( vA, getVSReg( XA ) ); assign( vB, getVSReg( XB ) ); switch (opc2) { case 0x18C: case 0x38C: // xvcmpeqdp[.] (VSX Vector Compare Equal To Double-Precision [ & Record ]) { DIP("xvcmpeqdp%s crf%d,fr%u,fr%u\n", (flag_rC ? ".":""), XT, XA, XB); do_vvec_fp_cmp(vA, vB, XT, flag_rC, PPC_CMP_EQ); break; } case 0x1CC: case 0x3CC: // xvcmpgedp[.] (VSX Vector Compare Greater Than or Equal To Double-Precision [ & Record ]) { DIP("xvcmpgedp%s crf%d,fr%u,fr%u\n", (flag_rC ? ".":""), XT, XA, XB); do_vvec_fp_cmp(vA, vB, XT, flag_rC, PPC_CMP_GE); break; } case 0x1AC: case 0x3AC: // xvcmpgtdp[.] (VSX Vector Compare Greater Than Double-Precision [ & Record ]) { DIP("xvcmpgtdp%s crf%d,fr%u,fr%u\n", (flag_rC ? ".":""), XT, XA, XB); do_vvec_fp_cmp(vA, vB, XT, flag_rC, PPC_CMP_GT); break; } case 0x10C: case 0x30C: // xvcmpeqsp[.] (VSX Vector Compare Equal To Single-Precision [ & Record ]) { IRTemp vD = newTemp(Ity_V128); DIP("xvcmpeqsp%s crf%d,fr%u,fr%u\n", (flag_rC ? ".":""), XT, XA, XB); assign( vD, binop(Iop_CmpEQ32Fx4, mkexpr(vA), mkexpr(vB)) ); putVSReg( XT, mkexpr(vD) ); if (flag_rC) { set_AV_CR6( mkexpr(vD), True ); } break; } case 0x14C: case 0x34C: // xvcmpgesp[.] (VSX Vector Compare Greater Than or Equal To Single-Precision [ & Record ]) { IRTemp vD = newTemp(Ity_V128); DIP("xvcmpgesp%s crf%d,fr%u,fr%u\n", (flag_rC ? ".":""), XT, XA, XB); assign( vD, binop(Iop_CmpGE32Fx4, mkexpr(vA), mkexpr(vB)) ); putVSReg( XT, mkexpr(vD) ); if (flag_rC) { set_AV_CR6( mkexpr(vD), True ); } break; } case 0x12C: case 0x32C: //xvcmpgtsp[.] (VSX Vector Compare Greater Than Single-Precision [ & Record ]) { IRTemp vD = newTemp(Ity_V128); DIP("xvcmpgtsp%s crf%d,fr%u,fr%u\n", (flag_rC ? ".":""), XT, XA, XB); assign( vD, binop(Iop_CmpGT32Fx4, mkexpr(vA), mkexpr(vB)) ); putVSReg( XT, mkexpr(vD) ); if (flag_rC) { set_AV_CR6( mkexpr(vD), True ); } break; } default: vex_printf( "dis_vvec_cmp(ppc)(opc2)\n" ); return False; } return True; } /* * Miscellaneous VSX Scalar Instructions */ static Bool dis_vxs_misc( UInt theInstr, UInt opc2 ) { #define VG_PPC_SIGN_MASK 0x7fffffffffffffffULL /* XX3-Form and XX2-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT ( theInstr ); UChar XA = ifieldRegXA ( theInstr ); UChar XB = ifieldRegXB ( theInstr ); IRTemp vA = newTemp( Ity_V128 ); IRTemp vB = newTemp( Ity_V128 ); if (opc1 != 0x3C) { vex_printf( "dis_vxs_misc(ppc)(instr)\n" ); return False; } assign( vA, getVSReg( XA ) ); assign( vB, getVSReg( XB ) ); /* For all the VSX move instructions, the contents of doubleword element 1 * of VSX[XT] are undefined after the operation; therefore, we can simply * move the entire array element where it makes sense to do so. */ switch (opc2) { case 0x2B2: // xsabsdp (VSX scalar absolute value double-precision { /* Move abs val of dw 0 of VSX[XB] to dw 0 of VSX[XT]. */ IRTemp absVal = newTemp(Ity_V128); if (host_endness == VexEndnessLE) { IRTemp hi64 = newTemp(Ity_I64); IRTemp lo64 = newTemp(Ity_I64); assign( hi64, unop( Iop_V128HIto64, mkexpr(vB) ) ); assign( lo64, unop( Iop_V128to64, mkexpr(vB) ) ); assign( absVal, binop( Iop_64HLtoV128, binop( Iop_And64, mkexpr(hi64), mkU64(VG_PPC_SIGN_MASK) ), mkexpr(lo64) ) ); } else { assign(absVal, binop(Iop_ShrV128, binop(Iop_ShlV128, mkexpr(vB), mkU8(1)), mkU8(1))); } DIP("xsabsdp v%d,v%d\n", XT, XB); putVSReg(XT, mkexpr(absVal)); break; } case 0x2C0: // xscpsgndp { /* Scalar copy sign double-precision */ IRTemp vecA_signed = newTemp(Ity_I64); IRTemp vecB_unsigned = newTemp(Ity_I64); IRTemp vec_result = newTemp(Ity_V128); DIP("xscpsgndp v%d,v%d v%d\n", XT, XA, XB); assign( vecA_signed, binop( Iop_And64, unop( Iop_V128HIto64, mkexpr(vA)), mkU64(~VG_PPC_SIGN_MASK) ) ); assign( vecB_unsigned, binop( Iop_And64, unop( Iop_V128HIto64, mkexpr(vB) ), mkU64(VG_PPC_SIGN_MASK) ) ); assign( vec_result, binop( Iop_64HLtoV128, binop( Iop_Or64, mkexpr(vecA_signed), mkexpr(vecB_unsigned) ), mkU64(0x0ULL))); putVSReg(XT, mkexpr(vec_result)); break; } case 0x2D2: // xsnabsdp { /* Scalar negative absolute value double-precision */ IRTemp BHi_signed = newTemp(Ity_I64); DIP("xsnabsdp v%d,v%d\n", XT, XB); assign( BHi_signed, binop( Iop_Or64, unop( Iop_V128HIto64, mkexpr(vB) ), mkU64(~VG_PPC_SIGN_MASK) ) ); putVSReg(XT, binop( Iop_64HLtoV128, mkexpr(BHi_signed), mkU64(0x0ULL) ) ); break; } case 0x2F2: // xsnegdp { /* Scalar negate double-precision */ IRTemp BHi_signed = newTemp(Ity_I64); IRTemp BHi_unsigned = newTemp(Ity_I64); IRTemp BHi_negated = newTemp(Ity_I64); IRTemp BHi_negated_signbit = newTemp(Ity_I1); IRTemp vec_result = newTemp(Ity_V128); DIP("xsnabsdp v%d,v%d\n", XT, XB); assign( BHi_signed, unop( Iop_V128HIto64, mkexpr(vB) ) ); assign( BHi_unsigned, binop( Iop_And64, mkexpr(BHi_signed), mkU64(VG_PPC_SIGN_MASK) ) ); assign( BHi_negated_signbit, unop( Iop_Not1, unop( Iop_32to1, binop( Iop_Shr32, unop( Iop_64HIto32, binop( Iop_And64, mkexpr(BHi_signed), mkU64(~VG_PPC_SIGN_MASK) ) ), mkU8(31) ) ) ) ); assign( BHi_negated, binop( Iop_Or64, binop( Iop_32HLto64, binop( Iop_Shl32, unop( Iop_1Uto32, mkexpr(BHi_negated_signbit) ), mkU8(31) ), mkU32(0) ), mkexpr(BHi_unsigned) ) ); assign( vec_result, binop( Iop_64HLtoV128, mkexpr(BHi_negated), mkU64(0x0ULL))); putVSReg( XT, mkexpr(vec_result)); break; } case 0x280: // xsmaxdp (VSX Scalar Maximum Double-Precision) case 0x2A0: // xsmindp (VSX Scalar Minimum Double-Precision) { IRTemp frA = newTemp(Ity_I64); IRTemp frB = newTemp(Ity_I64); Bool isMin = opc2 == 0x2A0 ? True : False; DIP("%s v%d,v%d v%d\n", isMin ? "xsmaxdp" : "xsmindp", XT, XA, XB); assign(frA, unop(Iop_V128HIto64, mkexpr( vA ))); assign(frB, unop(Iop_V128HIto64, mkexpr( vB ))); putVSReg( XT, binop( Iop_64HLtoV128, get_max_min_fp(frA, frB, isMin), mkU64( 0 ) ) ); break; } case 0x0F2: // xsrdpim (VSX Scalar Round to Double-Precision Integer using round toward -Infinity) case 0x0D2: // xsrdpip (VSX Scalar Round to Double-Precision Integer using round toward +Infinity) case 0x0D6: // xsrdpic (VSX Scalar Round to Double-Precision Integer using Current rounding mode) case 0x0B2: // xsrdpiz (VSX Scalar Round to Double-Precision Integer using round toward Zero) case 0x092: // xsrdpi (VSX Scalar Round to Double-Precision Integer using round toward Nearest Away) { IRTemp frB_I64 = newTemp(Ity_I64); IRExpr * frD_fp_round = NULL; assign(frB_I64, unop(Iop_V128HIto64, mkexpr( vB ))); frD_fp_round = _do_vsx_fp_roundToInt(frB_I64, opc2); DIP("xsrdpi%s v%d,v%d\n", _get_vsx_rdpi_suffix(opc2), XT, XB); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, frD_fp_round), mkU64( 0 ) ) ); break; } case 0x034: // xsresp (VSX Scalar Reciprocal Estimate single-Precision) case 0x014: /* xsrsqrtesp (VSX Scalar Reciprocal Square Root Estimate * single-Precision) */ { IRTemp frB = newTemp(Ity_F64); IRTemp sqrt = newTemp(Ity_F64); IRExpr* ieee_one = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL)); IRExpr* rm = get_IR_roundingmode(); Bool redp = opc2 == 0x034; DIP("%s v%d,v%d\n", redp ? "xsresp" : "xsrsqrtesp", XT, XB); assign( frB, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, mkexpr( vB ) ) ) ); if (!redp) assign( sqrt, binop( Iop_SqrtF64, rm, mkexpr(frB) ) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, triop( Iop_DivF64, rm, ieee_one, redp ? mkexpr( frB ) : mkexpr( sqrt ) ) ) ), mkU64( 0 ) ) ); break; } case 0x0B4: // xsredp (VSX Scalar Reciprocal Estimate Double-Precision) case 0x094: // xsrsqrtedp (VSX Scalar Reciprocal Square Root Estimate Double-Precision) { IRTemp frB = newTemp(Ity_F64); IRTemp sqrt = newTemp(Ity_F64); IRExpr* ieee_one = IRExpr_Const(IRConst_F64i(0x3ff0000000000000ULL)); IRExpr* rm = get_IR_roundingmode(); Bool redp = opc2 == 0x0B4; DIP("%s v%d,v%d\n", redp ? "xsredp" : "xsrsqrtedp", XT, XB); assign( frB, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, mkexpr( vB ) ) ) ); if (!redp) assign( sqrt, binop( Iop_SqrtF64, rm, mkexpr(frB) ) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, triop( Iop_DivF64, rm, ieee_one, redp ? mkexpr( frB ) : mkexpr( sqrt ) ) ), mkU64( 0 ) ) ); break; } case 0x232: // xsrsp (VSX Scalar Round to Single-Precision) { IRTemp frB = newTemp(Ity_F64); IRExpr* rm = get_IR_roundingmode(); DIP("xsrsp v%d, v%d\n", XT, XB); assign( frB, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, mkexpr( vB ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_ReinterpF64asI64, binop( Iop_RoundF64toF32, rm, mkexpr( frB ) ) ), mkU64( 0 ) ) ); break; } default: vex_printf( "dis_vxs_misc(ppc)(opc2)\n" ); return False; } return True; } /* * VSX Logical Instructions */ static Bool dis_vx_logic ( UInt theInstr, UInt opc2 ) { /* XX3-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT ( theInstr ); UChar XA = ifieldRegXA ( theInstr ); UChar XB = ifieldRegXB ( theInstr ); IRTemp vA = newTemp( Ity_V128 ); IRTemp vB = newTemp( Ity_V128 ); if (opc1 != 0x3C) { vex_printf( "dis_vx_logic(ppc)(instr)\n" ); return False; } assign( vA, getVSReg( XA ) ); assign( vB, getVSReg( XB ) ); switch (opc2) { case 0x268: // xxlxor DIP("xxlxor v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_XorV128, mkexpr( vA ), mkexpr( vB ) ) ); break; case 0x248: // xxlor DIP("xxlor v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_OrV128, mkexpr( vA ), mkexpr( vB ) ) ); break; case 0x288: // xxlnor DIP("xxlnor v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, unop( Iop_NotV128, binop( Iop_OrV128, mkexpr( vA ), mkexpr( vB ) ) ) ); break; case 0x208: // xxland DIP("xxland v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_AndV128, mkexpr( vA ), mkexpr( vB ) ) ); break; case 0x228: //xxlandc DIP("xxlandc v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_AndV128, mkexpr( vA ), unop( Iop_NotV128, mkexpr( vB ) ) ) ); break; case 0x2A8: // xxlorc (VSX Logical OR with complement) DIP("xxlorc v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, binop( Iop_OrV128, mkexpr( vA ), unop( Iop_NotV128, mkexpr( vB ) ) ) ); break; case 0x2C8: // xxlnand (VSX Logical NAND) DIP("xxlnand v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, unop( Iop_NotV128, binop( Iop_AndV128, mkexpr( vA ), mkexpr( vB ) ) ) ); break; case 0x2E8: // xxleqv (VSX Logical Equivalence) DIP("xxleqv v%d,v%d,v%d\n", XT, XA, XB); putVSReg( XT, unop( Iop_NotV128, binop( Iop_XorV128, mkexpr( vA ), mkexpr( vB ) ) ) ); break; default: vex_printf( "dis_vx_logic(ppc)(opc2)\n" ); return False; } return True; } /* * VSX Load Instructions * NOTE: VSX supports word-aligned storage access. */ static Bool dis_vx_load ( UInt theInstr ) { /* XX1-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT ( theInstr ); UChar rA_addr = ifieldRegA( theInstr ); UChar rB_addr = ifieldRegB( theInstr ); UInt opc2 = ifieldOPClo10( theInstr ); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA = newTemp( ty ); if (opc1 != 0x1F) { vex_printf( "dis_vx_load(ppc)(instr)\n" ); return False; } assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) ); switch (opc2) { case 0x00C: // lxsiwzx (Load VSX Scalar as Integer Word and Zero Indexed) { IRExpr * exp; DIP("lxsiwzx %d,r%u,r%u\n", XT, rA_addr, rB_addr); if (host_endness == VexEndnessLE) exp = unop( Iop_64to32, load( Ity_I64, mkexpr( EA ) ) ); else exp = unop( Iop_64HIto32, load( Ity_I64, mkexpr( EA ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_32Uto64, exp), mkU64(0) ) ); break; } case 0x04C: // lxsiwax (Load VSX Scalar as Integer Word Algebraic Indexed) { IRExpr * exp; DIP("lxsiwax %d,r%u,r%u\n", XT, rA_addr, rB_addr); if (host_endness == VexEndnessLE) exp = unop( Iop_64to32, load( Ity_I64, mkexpr( EA ) ) ); else exp = unop( Iop_64HIto32, load( Ity_I64, mkexpr( EA ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, unop( Iop_32Sto64, exp), mkU64(0) ) ); break; } case 0x20C: // lxsspx (Load VSX Scalar Single-Precision Indexed) { IRExpr * exp; DIP("lxsspx %d,r%u,r%u\n", XT, rA_addr, rB_addr); /* Take 32-bit floating point value in the upper half of the fetched * 64-bit value, convert to 64-bit floating point value and load into * top word of V128. */ exp = unop( Iop_ReinterpF64asI64, unop( Iop_F32toF64, unop( Iop_ReinterpI32asF32, load( Ity_I32, mkexpr( EA ) ) ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, exp, mkU64( 0 ) ) ); break; } case 0x24C: // lxsdx { IRExpr * exp; DIP("lxsdx %d,r%u,r%u\n", XT, rA_addr, rB_addr); exp = load( Ity_I64, mkexpr( EA ) ); // We need to pass an expression of type Ity_V128 with putVSReg, but the load // we just performed is only a DW. But since the contents of VSR[XT] element 1 // are undefined after this operation, we can just do a splat op. putVSReg( XT, binop( Iop_64HLtoV128, exp, exp ) ); break; } case 0x34C: // lxvd2x { IROp addOp = ty == Ity_I64 ? Iop_Add64 : Iop_Add32; IRExpr * high, *low; ULong ea_off = 8; IRExpr* high_addr; DIP("lxvd2x %d,r%u,r%u\n", XT, rA_addr, rB_addr); high = load( Ity_I64, mkexpr( EA ) ); high_addr = binop( addOp, mkexpr( EA ), ty == Ity_I64 ? mkU64( ea_off ) : mkU32( ea_off ) ); low = load( Ity_I64, high_addr ); putVSReg( XT, binop( Iop_64HLtoV128, high, low ) ); break; } case 0x14C: // lxvdsx { IRTemp data = newTemp(Ity_I64); DIP("lxvdsx %d,r%u,r%u\n", XT, rA_addr, rB_addr); assign( data, load( Ity_I64, mkexpr( EA ) ) ); putVSReg( XT, binop( Iop_64HLtoV128, mkexpr( data ), mkexpr( data ) ) ); break; } case 0x30C: { IRExpr *t0; DIP("lxvw4x %d,r%u,r%u\n", XT, rA_addr, rB_addr); /* The load will result in the data being in BE order. */ if (host_endness == VexEndnessLE) { IRExpr *t0_BE; IRTemp perm_LE = newTemp(Ity_V128); t0_BE = load( Ity_V128, mkexpr( EA ) ); /* Permute the data to LE format */ assign( perm_LE, binop( Iop_64HLtoV128, mkU64(0x0c0d0e0f08090a0bULL), mkU64(0x0405060700010203ULL))); t0 = binop( Iop_Perm8x16, t0_BE, mkexpr(perm_LE) ); } else { t0 = load( Ity_V128, mkexpr( EA ) ); } putVSReg( XT, t0 ); break; } default: vex_printf( "dis_vx_load(ppc)(opc2)\n" ); return False; } return True; } /* * VSX Store Instructions * NOTE: VSX supports word-aligned storage access. */ static Bool dis_vx_store ( UInt theInstr ) { /* XX1-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XS = ifieldRegXS( theInstr ); UChar rA_addr = ifieldRegA( theInstr ); UChar rB_addr = ifieldRegB( theInstr ); IRTemp vS = newTemp( Ity_V128 ); UInt opc2 = ifieldOPClo10( theInstr ); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA = newTemp( ty ); if (opc1 != 0x1F) { vex_printf( "dis_vx_store(ppc)(instr)\n" ); return False; } assign( EA, ea_rAor0_idxd( rA_addr, rB_addr ) ); assign( vS, getVSReg( XS ) ); switch (opc2) { case 0x08C: { /* Need the next to the most significant 32-bit word from * the 128-bit vector. */ IRExpr * high64, * low32; DIP("stxsiwx %d,r%u,r%u\n", XS, rA_addr, rB_addr); high64 = unop( Iop_V128HIto64, mkexpr( vS ) ); low32 = unop( Iop_64to32, high64 ); store( mkexpr( EA ), low32 ); break; } case 0x28C: { IRTemp high64 = newTemp(Ity_F64); IRTemp val32 = newTemp(Ity_I32); DIP("stxsspx %d,r%u,r%u\n", XS, rA_addr, rB_addr); assign(high64, unop( Iop_ReinterpI64asF64, unop( Iop_V128HIto64, mkexpr( vS ) ) ) ); assign(val32, unop( Iop_ReinterpF32asI32, unop( Iop_TruncF64asF32, mkexpr(high64) ) ) ); store( mkexpr( EA ), mkexpr( val32 ) ); break; } case 0x2CC: { IRExpr * high64; DIP("stxsdx %d,r%u,r%u\n", XS, rA_addr, rB_addr); high64 = unop( Iop_V128HIto64, mkexpr( vS ) ); store( mkexpr( EA ), high64 ); break; } case 0x3CC: { IRExpr * high64, *low64; DIP("stxvd2x %d,r%u,r%u\n", XS, rA_addr, rB_addr); high64 = unop( Iop_V128HIto64, mkexpr( vS ) ); low64 = unop( Iop_V128to64, mkexpr( vS ) ); store( mkexpr( EA ), high64 ); store( binop( mkSzOp( ty, Iop_Add8 ), mkexpr( EA ), ty == Ity_I64 ? mkU64( 8 ) : mkU32( 8 ) ), low64 ); break; } case 0x38C: { UInt ea_off = 0; IRExpr* irx_addr; IRTemp hi64 = newTemp( Ity_I64 ); IRTemp lo64 = newTemp( Ity_I64 ); DIP("stxvw4x %d,r%u,r%u\n", XS, rA_addr, rB_addr); // This instruction supports word-aligned stores, so EA may not be // quad-word aligned. Therefore, do 4 individual word-size stores. assign( hi64, unop( Iop_V128HIto64, mkexpr( vS ) ) ); assign( lo64, unop( Iop_V128to64, mkexpr( vS ) ) ); store( mkexpr( EA ), unop( Iop_64HIto32, mkexpr( hi64 ) ) ); ea_off += 4; irx_addr = binop( mkSzOp( ty, Iop_Add8 ), mkexpr( EA ), ty == Ity_I64 ? mkU64( ea_off ) : mkU32( ea_off ) ); store( irx_addr, unop( Iop_64to32, mkexpr( hi64 ) ) ); ea_off += 4; irx_addr = binop( mkSzOp( ty, Iop_Add8 ), mkexpr( EA ), ty == Ity_I64 ? mkU64( ea_off ) : mkU32( ea_off ) ); store( irx_addr, unop( Iop_64HIto32, mkexpr( lo64 ) ) ); ea_off += 4; irx_addr = binop( mkSzOp( ty, Iop_Add8 ), mkexpr( EA ), ty == Ity_I64 ? mkU64( ea_off ) : mkU32( ea_off ) ); store( irx_addr, unop( Iop_64to32, mkexpr( lo64 ) ) ); break; } default: vex_printf( "dis_vx_store(ppc)(opc2)\n" ); return False; } return True; } /* * VSX permute and other miscealleous instructions */ static Bool dis_vx_permute_misc( UInt theInstr, UInt opc2 ) { /* XX3-Form */ UChar opc1 = ifieldOPC( theInstr ); UChar XT = ifieldRegXT ( theInstr ); UChar XA = ifieldRegXA ( theInstr ); UChar XB = ifieldRegXB ( theInstr ); IRTemp vT = newTemp( Ity_V128 ); IRTemp vA = newTemp( Ity_V128 ); IRTemp vB = newTemp( Ity_V128 ); if (opc1 != 0x3C) { vex_printf( "dis_vx_permute_misc(ppc)(instr)\n" ); return False; } assign( vA, getVSReg( XA ) ); assign( vB, getVSReg( XB ) ); switch (opc2) { case 0x8: // xxsldwi (VSX Shift Left Double by Word Immediate) { UChar SHW = ifieldSHW ( theInstr ); IRTemp result = newTemp(Ity_V128); if ( SHW != 0 ) { IRTemp hi = newTemp(Ity_V128); IRTemp lo = newTemp(Ity_V128); assign( hi, binop(Iop_ShlV128, mkexpr(vA), mkU8(SHW*32)) ); assign( lo, binop(Iop_ShrV128, mkexpr(vB), mkU8(128-SHW*32)) ); assign ( result, binop(Iop_OrV128, mkexpr(hi), mkexpr(lo)) ); } else assign ( result, mkexpr(vA) ); DIP("xxsldwi v%d,v%d,v%d,%d\n", XT, XA, XB, SHW); putVSReg( XT, mkexpr(result) ); break; } case 0x28: // xpermdi (VSX Permute Doubleword Immediate) { UChar DM = ifieldDM ( theInstr ); IRTemp hi = newTemp(Ity_I64); IRTemp lo = newTemp(Ity_I64); if (DM & 0x2) assign( hi, unop(Iop_V128to64, mkexpr(vA)) ); else assign( hi, unop(Iop_V128HIto64, mkexpr(vA)) ); if (DM & 0x1) assign( lo, unop(Iop_V128to64, mkexpr(vB)) ); else assign( lo, unop(Iop_V128HIto64, mkexpr(vB)) ); assign( vT, binop(Iop_64HLtoV128, mkexpr(hi), mkexpr(lo)) ); DIP("xxpermdi v%d,v%d,v%d,0x%x\n", XT, XA, XB, DM); putVSReg( XT, mkexpr( vT ) ); break; } case 0x48: // xxmrghw (VSX Merge High Word) case 0xc8: // xxmrglw (VSX Merge Low Word) { const HChar type = (opc2 == 0x48) ? 'h' : 'l'; IROp word_op = (opc2 == 0x48) ? Iop_V128HIto64 : Iop_V128to64; IRTemp a64 = newTemp(Ity_I64); IRTemp ahi32 = newTemp(Ity_I32); IRTemp alo32 = newTemp(Ity_I32); IRTemp b64 = newTemp(Ity_I64); IRTemp bhi32 = newTemp(Ity_I32); IRTemp blo32 = newTemp(Ity_I32); assign( a64, unop(word_op, mkexpr(vA)) ); assign( ahi32, unop(Iop_64HIto32, mkexpr(a64)) ); assign( alo32, unop(Iop_64to32, mkexpr(a64)) ); assign( b64, unop(word_op, mkexpr(vB)) ); assign( bhi32, unop(Iop_64HIto32, mkexpr(b64)) ); assign( blo32, unop(Iop_64to32, mkexpr(b64)) ); assign( vT, binop(Iop_64HLtoV128, binop(Iop_32HLto64, mkexpr(ahi32), mkexpr(bhi32)), binop(Iop_32HLto64, mkexpr(alo32), mkexpr(blo32))) ); DIP("xxmrg%cw v%d,v%d,v%d\n", type, XT, XA, XB); putVSReg( XT, mkexpr( vT ) ); break; } case 0x018: // xxsel (VSX Select) { UChar XC = ifieldRegXC(theInstr); IRTemp vC = newTemp( Ity_V128 ); assign( vC, getVSReg( XC ) ); DIP("xxsel v%d,v%d,v%d,v%d\n", XT, XA, XB, XC); /* vD = (vA & ~vC) | (vB & vC) */ putVSReg( XT, binop(Iop_OrV128, binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, mkexpr(vC))), binop(Iop_AndV128, mkexpr(vB), mkexpr(vC))) ); break; } case 0x148: // xxspltw (VSX Splat Word) { UChar UIM = ifieldRegA(theInstr) & 3; UChar sh_uim = (3 - (UIM)) * 32; DIP("xxspltw v%d,v%d,%d\n", XT, XB, UIM); putVSReg( XT, unop( Iop_Dup32x4, unop( Iop_V128to32, binop( Iop_ShrV128, mkexpr( vB ), mkU8( sh_uim ) ) ) ) ); break; } default: vex_printf( "dis_vx_permute_misc(ppc)(opc2)\n" ); return False; } return True; } /* AltiVec Load Instructions */ static Bool dis_av_load ( const VexAbiInfo* vbi, UInt theInstr ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA = newTemp(ty); IRTemp EA_align16 = newTemp(ty); if (opc1 != 0x1F || b0 != 0) { vex_printf("dis_av_load(ppc)(instr)\n"); return False; } assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) ); assign( EA_align16, addr_align( mkexpr(EA), 16 ) ); switch (opc2) { case 0x006: { // lvsl (Load Vector for Shift Left, AV p123) IRDirty* d; UInt vD_off = vectorGuestRegOffset(vD_addr); IRExpr** args_be = mkIRExprVec_5( IRExpr_BBPTR(), mkU32(vD_off), binop(Iop_And32, mkNarrowTo32(ty, mkexpr(EA)), mkU32(0xF)), mkU32(0)/*left*/, mkU32(1)/*Big Endian*/); IRExpr** args_le = mkIRExprVec_5( IRExpr_BBPTR(), mkU32(vD_off), binop(Iop_And32, mkNarrowTo32(ty, mkexpr(EA)), mkU32(0xF)), mkU32(0)/*left*/, mkU32(0)/*Little Endian*/); if (!mode64) { d = unsafeIRDirty_0_N ( 0/*regparms*/, "ppc32g_dirtyhelper_LVS", fnptr_to_fnentry(vbi, &ppc32g_dirtyhelper_LVS), args_be ); } else { if (host_endness == VexEndnessBE) d = unsafeIRDirty_0_N ( 0/*regparms*/, "ppc64g_dirtyhelper_LVS", fnptr_to_fnentry(vbi, &ppc64g_dirtyhelper_LVS), args_be ); else d = unsafeIRDirty_0_N ( 0/*regparms*/, "ppc64g_dirtyhelper_LVS", &ppc64g_dirtyhelper_LVS, args_le ); } DIP("lvsl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr); /* declare guest state effects */ d->nFxState = 1; vex_bzero(&d->fxState, sizeof(d->fxState)); d->fxState[0].fx = Ifx_Write; d->fxState[0].offset = vD_off; d->fxState[0].size = sizeof(U128); /* execute the dirty call, side-effecting guest state */ stmt( IRStmt_Dirty(d) ); break; } case 0x026: { // lvsr (Load Vector for Shift Right, AV p125) IRDirty* d; UInt vD_off = vectorGuestRegOffset(vD_addr); IRExpr** args_be = mkIRExprVec_5( IRExpr_BBPTR(), mkU32(vD_off), binop(Iop_And32, mkNarrowTo32(ty, mkexpr(EA)), mkU32(0xF)), mkU32(1)/*right*/, mkU32(1)/*Big Endian*/); IRExpr** args_le = mkIRExprVec_5( IRExpr_BBPTR(), mkU32(vD_off), binop(Iop_And32, mkNarrowTo32(ty, mkexpr(EA)), mkU32(0xF)), mkU32(1)/*right*/, mkU32(0)/*Little Endian*/); if (!mode64) { d = unsafeIRDirty_0_N ( 0/*regparms*/, "ppc32g_dirtyhelper_LVS", fnptr_to_fnentry(vbi, &ppc32g_dirtyhelper_LVS), args_be ); } else { if (host_endness == VexEndnessBE) d = unsafeIRDirty_0_N ( 0/*regparms*/, "ppc64g_dirtyhelper_LVS", fnptr_to_fnentry(vbi, &ppc64g_dirtyhelper_LVS), args_be ); else d = unsafeIRDirty_0_N ( 0/*regparms*/, "ppc64g_dirtyhelper_LVS", &ppc64g_dirtyhelper_LVS, args_le ); } DIP("lvsr v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr); /* declare guest state effects */ d->nFxState = 1; vex_bzero(&d->fxState, sizeof(d->fxState)); d->fxState[0].fx = Ifx_Write; d->fxState[0].offset = vD_off; d->fxState[0].size = sizeof(U128); /* execute the dirty call, side-effecting guest state */ stmt( IRStmt_Dirty(d) ); break; } case 0x007: // lvebx (Load Vector Element Byte Indexed, AV p119) DIP("lvebx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr); /* loads addressed byte into vector[EA[0:3] since all other destination bytes are undefined, can simply load entire vector from 16-aligned EA */ putVReg( vD_addr, load(Ity_V128, mkexpr(EA_align16)) ); break; case 0x027: // lvehx (Load Vector Element Half Word Indexed, AV p121) DIP("lvehx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr); /* see note for lvebx */ putVReg( vD_addr, load(Ity_V128, mkexpr(EA_align16)) ); break; case 0x047: // lvewx (Load Vector Element Word Indexed, AV p122) DIP("lvewx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr); /* see note for lvebx */ putVReg( vD_addr, load(Ity_V128, mkexpr(EA_align16)) ); break; case 0x067: // lvx (Load Vector Indexed, AV p127) DIP("lvx v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr); putVReg( vD_addr, load(Ity_V128, mkexpr(EA_align16)) ); break; case 0x167: // lvxl (Load Vector Indexed LRU, AV p128) DIP("lvxl v%d,r%u,r%u\n", vD_addr, rA_addr, rB_addr); putVReg( vD_addr, load(Ity_V128, mkexpr(EA_align16)) ); break; default: vex_printf("dis_av_load(ppc)(opc2)\n"); return False; } return True; } /* AltiVec Store Instructions */ static Bool dis_av_store ( UInt theInstr ) { /* X-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vS_addr = ifieldRegDS(theInstr); UChar rA_addr = ifieldRegA(theInstr); UChar rB_addr = ifieldRegB(theInstr); UInt opc2 = ifieldOPClo10(theInstr); UChar b0 = ifieldBIT0(theInstr); IRType ty = mode64 ? Ity_I64 : Ity_I32; IRTemp EA = newTemp(ty); IRTemp addr_aligned = newTemp(ty); IRTemp vS = newTemp(Ity_V128); IRTemp eb = newTemp(Ity_I8); IRTemp idx = newTemp(Ity_I8); if (opc1 != 0x1F || b0 != 0) { vex_printf("dis_av_store(ppc)(instr)\n"); return False; } assign( vS, getVReg(vS_addr)); assign( EA, ea_rAor0_idxd(rA_addr, rB_addr) ); switch (opc2) { case 0x087: { // stvebx (Store Vector Byte Indexed, AV p131) DIP("stvebx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr); assign( eb, binop(Iop_And8, mkU8(0xF), unop(Iop_32to8, mkNarrowTo32(ty, mkexpr(EA)) )) ); if (host_endness == VexEndnessLE) { assign( idx, binop(Iop_Shl8, mkexpr(eb), mkU8(3)) ); } else { assign( idx, binop(Iop_Shl8, binop(Iop_Sub8, mkU8(15), mkexpr(eb)), mkU8(3)) ); } store( mkexpr(EA), unop( Iop_32to8, unop(Iop_V128to32, binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) ); break; } case 0x0A7: { // stvehx (Store Vector Half Word Indexed, AV p132) DIP("stvehx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr); assign( addr_aligned, addr_align(mkexpr(EA), 2) ); assign( eb, binop(Iop_And8, mkU8(0xF), mkNarrowTo8(ty, mkexpr(addr_aligned) )) ); if (host_endness == VexEndnessLE) { assign( idx, binop(Iop_Shl8, mkexpr(eb), mkU8(3)) ); } else { assign( idx, binop(Iop_Shl8, binop(Iop_Sub8, mkU8(14), mkexpr(eb)), mkU8(3)) ); } store( mkexpr(addr_aligned), unop( Iop_32to16, unop(Iop_V128to32, binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx)))) ); break; } case 0x0C7: { // stvewx (Store Vector Word Indexed, AV p133) DIP("stvewx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr); assign( addr_aligned, addr_align(mkexpr(EA), 4) ); assign( eb, binop(Iop_And8, mkU8(0xF), mkNarrowTo8(ty, mkexpr(addr_aligned) )) ); if (host_endness == VexEndnessLE) { assign( idx, binop(Iop_Shl8, mkexpr(eb), mkU8(3)) ); } else { assign( idx, binop(Iop_Shl8, binop(Iop_Sub8, mkU8(12), mkexpr(eb)), mkU8(3)) ); } store( mkexpr( addr_aligned), unop( Iop_V128to32, binop(Iop_ShrV128, mkexpr(vS), mkexpr(idx))) ); break; } case 0x0E7: // stvx (Store Vector Indexed, AV p134) DIP("stvx v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr); store( addr_align( mkexpr(EA), 16 ), mkexpr(vS) ); break; case 0x1E7: // stvxl (Store Vector Indexed LRU, AV p135) DIP("stvxl v%d,r%u,r%u\n", vS_addr, rA_addr, rB_addr); store( addr_align( mkexpr(EA), 16 ), mkexpr(vS) ); break; default: vex_printf("dis_av_store(ppc)(opc2)\n"); return False; } return True; } /* AltiVec Arithmetic Instructions */ static Bool dis_av_arith ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UInt opc2 = IFIELD( theInstr, 0, 11 ); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); IRTemp z3 = newTemp(Ity_I64); IRTemp z2 = newTemp(Ity_I64); IRTemp z1 = newTemp(Ity_I64); IRTemp z0 = newTemp(Ity_I64); IRTemp aEvn, aOdd; IRTemp a15, a14, a13, a12, a11, a10, a9, a8; IRTemp a7, a6, a5, a4, a3, a2, a1, a0; IRTemp b3, b2, b1, b0; aEvn = aOdd = IRTemp_INVALID; a15 = a14 = a13 = a12 = a11 = a10 = a9 = a8 = IRTemp_INVALID; a7 = a6 = a5 = a4 = a3 = a2 = a1 = a0 = IRTemp_INVALID; b3 = b2 = b1 = b0 = IRTemp_INVALID; assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); if (opc1 != 0x4) { vex_printf("dis_av_arith(ppc)(opc1 != 0x4)\n"); return False; } switch (opc2) { /* Add */ case 0x180: { // vaddcuw (Add Carryout Unsigned Word, AV p136) DIP("vaddcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); /* unsigned_ov(x+y) = (y >u not(x)) */ putVReg( vD_addr, binop(Iop_ShrN32x4, binop(Iop_CmpGT32Ux4, mkexpr(vB), unop(Iop_NotV128, mkexpr(vA))), mkU8(31)) ); break; } case 0x000: // vaddubm (Add Unsigned Byte Modulo, AV p141) DIP("vaddubm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Add8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x040: // vadduhm (Add Unsigned Half Word Modulo, AV p143) DIP("vadduhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Add16x8, mkexpr(vA), mkexpr(vB)) ); break; case 0x080: // vadduwm (Add Unsigned Word Modulo, AV p145) DIP("vadduwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vA), mkexpr(vB)) ); break; case 0x0C0: // vaddudm (Add Unsigned Double Word Modulo) DIP("vaddudm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Add64x2, mkexpr(vA), mkexpr(vB)) ); break; case 0x200: // vaddubs (Add Unsigned Byte Saturate, AV p142) DIP("vaddubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QAdd8Ux16, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT], perhaps via new primop: Iop_SatOfQAdd8Ux16 break; case 0x240: // vadduhs (Add Unsigned Half Word Saturate, AV p144) DIP("vadduhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QAdd16Ux8, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; case 0x280: // vadduws (Add Unsigned Word Saturate, AV p146) DIP("vadduws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QAdd32Ux4, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; case 0x300: // vaddsbs (Add Signed Byte Saturate, AV p138) DIP("vaddsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QAdd8Sx16, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; case 0x340: // vaddshs (Add Signed Half Word Saturate, AV p139) DIP("vaddshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QAdd16Sx8, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; case 0x380: // vaddsws (Add Signed Word Saturate, AV p140) DIP("vaddsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QAdd32Sx4, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; /* Subtract */ case 0x580: { // vsubcuw (Subtract Carryout Unsigned Word, AV p260) DIP("vsubcuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); /* unsigned_ov(x-y) = (y >u x) */ putVReg( vD_addr, binop(Iop_ShrN32x4, unop(Iop_NotV128, binop(Iop_CmpGT32Ux4, mkexpr(vB), mkexpr(vA))), mkU8(31)) ); break; } case 0x400: // vsububm (Subtract Unsigned Byte Modulo, AV p265) DIP("vsububm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Sub8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x440: // vsubuhm (Subtract Unsigned Half Word Modulo, AV p267) DIP("vsubuhm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Sub16x8, mkexpr(vA), mkexpr(vB)) ); break; case 0x480: // vsubuwm (Subtract Unsigned Word Modulo, AV p269) DIP("vsubuwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Sub32x4, mkexpr(vA), mkexpr(vB)) ); break; case 0x4C0: // vsubudm (Subtract Unsigned Double Word Modulo) DIP("vsubudm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Sub64x2, mkexpr(vA), mkexpr(vB)) ); break; case 0x600: // vsububs (Subtract Unsigned Byte Saturate, AV p266) DIP("vsububs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QSub8Ux16, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; case 0x640: // vsubuhs (Subtract Unsigned HWord Saturate, AV p268) DIP("vsubuhs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QSub16Ux8, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; case 0x680: // vsubuws (Subtract Unsigned Word Saturate, AV p270) DIP("vsubuws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QSub32Ux4, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; case 0x700: // vsubsbs (Subtract Signed Byte Saturate, AV p262) DIP("vsubsbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QSub8Sx16, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; case 0x740: // vsubshs (Subtract Signed Half Word Saturate, AV p263) DIP("vsubshs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QSub16Sx8, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; case 0x780: // vsubsws (Subtract Signed Word Saturate, AV p264) DIP("vsubsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QSub32Sx4, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] break; /* Maximum */ case 0x002: // vmaxub (Maximum Unsigned Byte, AV p182) DIP("vmaxub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Max8Ux16, mkexpr(vA), mkexpr(vB)) ); break; case 0x042: // vmaxuh (Maximum Unsigned Half Word, AV p183) DIP("vmaxuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Max16Ux8, mkexpr(vA), mkexpr(vB)) ); break; case 0x082: // vmaxuw (Maximum Unsigned Word, AV p184) DIP("vmaxuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Max32Ux4, mkexpr(vA), mkexpr(vB)) ); break; case 0x0C2: // vmaxud (Maximum Unsigned Double word) DIP("vmaxud v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Max64Ux2, mkexpr(vA), mkexpr(vB)) ); break; case 0x102: // vmaxsb (Maximum Signed Byte, AV p179) DIP("vmaxsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Max8Sx16, mkexpr(vA), mkexpr(vB)) ); break; case 0x142: // vmaxsh (Maximum Signed Half Word, AV p180) DIP("vmaxsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Max16Sx8, mkexpr(vA), mkexpr(vB)) ); break; case 0x182: // vmaxsw (Maximum Signed Word, AV p181) DIP("vmaxsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Max32Sx4, mkexpr(vA), mkexpr(vB)) ); break; case 0x1C2: // vmaxsd (Maximum Signed Double word) DIP("vmaxsd v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Max64Sx2, mkexpr(vA), mkexpr(vB)) ); break; /* Minimum */ case 0x202: // vminub (Minimum Unsigned Byte, AV p191) DIP("vminub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Min8Ux16, mkexpr(vA), mkexpr(vB)) ); break; case 0x242: // vminuh (Minimum Unsigned Half Word, AV p192) DIP("vminuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Min16Ux8, mkexpr(vA), mkexpr(vB)) ); break; case 0x282: // vminuw (Minimum Unsigned Word, AV p193) DIP("vminuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Min32Ux4, mkexpr(vA), mkexpr(vB)) ); break; case 0x2C2: // vminud (Minimum Unsigned Double Word) DIP("vminud v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Min64Ux2, mkexpr(vA), mkexpr(vB)) ); break; case 0x302: // vminsb (Minimum Signed Byte, AV p188) DIP("vminsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Min8Sx16, mkexpr(vA), mkexpr(vB)) ); break; case 0x342: // vminsh (Minimum Signed Half Word, AV p189) DIP("vminsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Min16Sx8, mkexpr(vA), mkexpr(vB)) ); break; case 0x382: // vminsw (Minimum Signed Word, AV p190) DIP("vminsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Min32Sx4, mkexpr(vA), mkexpr(vB)) ); break; case 0x3C2: // vminsd (Minimum Signed Double Word) DIP("vminsd v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Min64Sx2, mkexpr(vA), mkexpr(vB)) ); break; /* Average */ case 0x402: // vavgub (Average Unsigned Byte, AV p152) DIP("vavgub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Avg8Ux16, mkexpr(vA), mkexpr(vB)) ); break; case 0x442: // vavguh (Average Unsigned Half Word, AV p153) DIP("vavguh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Avg16Ux8, mkexpr(vA), mkexpr(vB)) ); break; case 0x482: // vavguw (Average Unsigned Word, AV p154) DIP("vavguw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Avg32Ux4, mkexpr(vA), mkexpr(vB)) ); break; case 0x502: // vavgsb (Average Signed Byte, AV p149) DIP("vavgsb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Avg8Sx16, mkexpr(vA), mkexpr(vB)) ); break; case 0x542: // vavgsh (Average Signed Half Word, AV p150) DIP("vavgsh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Avg16Sx8, mkexpr(vA), mkexpr(vB)) ); break; case 0x582: // vavgsw (Average Signed Word, AV p151) DIP("vavgsw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Avg32Sx4, mkexpr(vA), mkexpr(vB)) ); break; /* Multiply */ case 0x008: // vmuloub (Multiply Odd Unsigned Byte, AV p213) DIP("vmuloub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB))); break; case 0x048: // vmulouh (Multiply Odd Unsigned Half Word, AV p214) DIP("vmulouh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB))); break; case 0x088: // vmulouw (Multiply Odd Unsigned Word) DIP("vmulouw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop( Iop_MullEven32Ux4, mkexpr(vA), mkexpr(vB) ) ); break; case 0x089: // vmuluwm (Multiply Unsigned Word Modulo) DIP("vmuluwm v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop( Iop_Mul32x4, mkexpr(vA), mkexpr(vB) ) ); break; case 0x108: // vmulosb (Multiply Odd Signed Byte, AV p211) DIP("vmulosb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_MullEven8Sx16, mkexpr(vA), mkexpr(vB))); break; case 0x148: // vmulosh (Multiply Odd Signed Half Word, AV p212) DIP("vmulosh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB))); break; case 0x188: // vmulosw (Multiply Odd Signed Word) DIP("vmulosw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop( Iop_MullEven32Sx4, mkexpr(vA), mkexpr(vB) ) ); break; case 0x208: // vmuleub (Multiply Even Unsigned Byte, AV p209) DIP("vmuleub v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) )); break; case 0x248: // vmuleuh (Multiply Even Unsigned Half Word, AV p210) DIP("vmuleuh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) )); break; case 0x288: // vmuleuw (Multiply Even Unsigned Word) DIP("vmuleuw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, MK_Iop_MullOdd32Ux4( mkexpr(vA), mkexpr(vB) ) ); break; case 0x308: // vmulesb (Multiply Even Signed Byte, AV p207) DIP("vmulesb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, MK_Iop_MullOdd8Sx16( mkexpr(vA), mkexpr(vB) )); break; case 0x348: // vmulesh (Multiply Even Signed Half Word, AV p208) DIP("vmulesh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) )); break; case 0x388: // vmulesw (Multiply Even Signed Word) DIP("vmulesw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, MK_Iop_MullOdd32Sx4( mkexpr(vA), mkexpr(vB) ) ); break; /* Sum Across Partial */ case 0x608: { // vsum4ubs (Sum Partial (1/4) UB Saturate, AV p275) IRTemp aEE, aEO, aOE, aOO; aEE = aEO = aOE = aOO = IRTemp_INVALID; DIP("vsum4ubs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); /* vA: V128_8Ux16 -> 4 x V128_32Ux4, sign-extended */ expand8Ux16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...) expand16Ux8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...) expand16Ux8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...) /* break V128 to 4xI32's, zero-extending to I64's */ breakV128to4x64U( mkexpr(aEE), &a15, &a11, &a7, &a3 ); breakV128to4x64U( mkexpr(aOE), &a14, &a10, &a6, &a2 ); breakV128to4x64U( mkexpr(aEO), &a13, &a9, &a5, &a1 ); breakV128to4x64U( mkexpr(aOO), &a12, &a8, &a4, &a0 ); breakV128to4x64U( mkexpr(vB), &b3, &b2, &b1, &b0 ); /* add lanes */ assign( z3, binop(Iop_Add64, mkexpr(b3), binop(Iop_Add64, binop(Iop_Add64, mkexpr(a15), mkexpr(a14)), binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) ); assign( z2, binop(Iop_Add64, mkexpr(b2), binop(Iop_Add64, binop(Iop_Add64, mkexpr(a11), mkexpr(a10)), binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) ); assign( z1, binop(Iop_Add64, mkexpr(b1), binop(Iop_Add64, binop(Iop_Add64, mkexpr(a7), mkexpr(a6)), binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) ); assign( z0, binop(Iop_Add64, mkexpr(b0), binop(Iop_Add64, binop(Iop_Add64, mkexpr(a3), mkexpr(a2)), binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) ); /* saturate-narrow to 32bit, and combine to V128 */ putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2), mkexpr(z1), mkexpr(z0)) ); break; } case 0x708: { // vsum4sbs (Sum Partial (1/4) SB Saturate, AV p273) IRTemp aEE, aEO, aOE, aOO; aEE = aEO = aOE = aOO = IRTemp_INVALID; DIP("vsum4sbs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); /* vA: V128_8Sx16 -> 4 x V128_32Sx4, sign-extended */ expand8Sx16( mkexpr(vA), &aEvn, &aOdd ); // (15,13...),(14,12...) expand16Sx8( mkexpr(aEvn), &aEE, &aEO ); // (15,11...),(13, 9...) expand16Sx8( mkexpr(aOdd), &aOE, &aOO ); // (14,10...),(12, 8...) /* break V128 to 4xI32's, sign-extending to I64's */ breakV128to4x64S( mkexpr(aEE), &a15, &a11, &a7, &a3 ); breakV128to4x64S( mkexpr(aOE), &a14, &a10, &a6, &a2 ); breakV128to4x64S( mkexpr(aEO), &a13, &a9, &a5, &a1 ); breakV128to4x64S( mkexpr(aOO), &a12, &a8, &a4, &a0 ); breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 ); /* add lanes */ assign( z3, binop(Iop_Add64, mkexpr(b3), binop(Iop_Add64, binop(Iop_Add64, mkexpr(a15), mkexpr(a14)), binop(Iop_Add64, mkexpr(a13), mkexpr(a12)))) ); assign( z2, binop(Iop_Add64, mkexpr(b2), binop(Iop_Add64, binop(Iop_Add64, mkexpr(a11), mkexpr(a10)), binop(Iop_Add64, mkexpr(a9), mkexpr(a8)))) ); assign( z1, binop(Iop_Add64, mkexpr(b1), binop(Iop_Add64, binop(Iop_Add64, mkexpr(a7), mkexpr(a6)), binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))) ); assign( z0, binop(Iop_Add64, mkexpr(b0), binop(Iop_Add64, binop(Iop_Add64, mkexpr(a3), mkexpr(a2)), binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) ); /* saturate-narrow to 32bit, and combine to V128 */ putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2), mkexpr(z1), mkexpr(z0)) ); break; } case 0x648: { // vsum4shs (Sum Partial (1/4) SHW Saturate, AV p274) DIP("vsum4shs v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); /* vA: V128_16Sx8 -> 2 x V128_32Sx4, sign-extended */ expand16Sx8( mkexpr(vA), &aEvn, &aOdd ); // (7,5...),(6,4...) /* break V128 to 4xI32's, sign-extending to I64's */ breakV128to4x64S( mkexpr(aEvn), &a7, &a5, &a3, &a1 ); breakV128to4x64S( mkexpr(aOdd), &a6, &a4, &a2, &a0 ); breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 ); /* add lanes */ assign( z3, binop(Iop_Add64, mkexpr(b3), binop(Iop_Add64, mkexpr(a7), mkexpr(a6)))); assign( z2, binop(Iop_Add64, mkexpr(b2), binop(Iop_Add64, mkexpr(a5), mkexpr(a4)))); assign( z1, binop(Iop_Add64, mkexpr(b1), binop(Iop_Add64, mkexpr(a3), mkexpr(a2)))); assign( z0, binop(Iop_Add64, mkexpr(b0), binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))); /* saturate-narrow to 32bit, and combine to V128 */ putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2), mkexpr(z1), mkexpr(z0)) ); break; } case 0x688: { // vsum2sws (Sum Partial (1/2) SW Saturate, AV p272) DIP("vsum2sws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); /* break V128 to 4xI32's, sign-extending to I64's */ breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 ); breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 ); /* add lanes */ assign( z2, binop(Iop_Add64, mkexpr(b2), binop(Iop_Add64, mkexpr(a3), mkexpr(a2))) ); assign( z0, binop(Iop_Add64, mkexpr(b0), binop(Iop_Add64, mkexpr(a1), mkexpr(a0))) ); /* saturate-narrow to 32bit, and combine to V128 */ putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkexpr(z2), mkU64(0), mkexpr(z0)) ); break; } case 0x788: { // vsumsws (Sum SW Saturate, AV p271) DIP("vsumsws v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); /* break V128 to 4xI32's, sign-extending to I64's */ breakV128to4x64S( mkexpr(vA), &a3, &a2, &a1, &a0 ); breakV128to4x64S( mkexpr(vB), &b3, &b2, &b1, &b0 ); /* add lanes */ assign( z0, binop(Iop_Add64, mkexpr(b0), binop(Iop_Add64, binop(Iop_Add64, mkexpr(a3), mkexpr(a2)), binop(Iop_Add64, mkexpr(a1), mkexpr(a0)))) ); /* saturate-narrow to 32bit, and combine to V128 */ putVReg( vD_addr, mkV128from4x64S( mkU64(0), mkU64(0), mkU64(0), mkexpr(z0)) ); break; } default: vex_printf("dis_av_arith(ppc)(opc2=0x%x)\n", opc2); return False; } return True; } /* AltiVec Logic Instructions */ static Bool dis_av_logic ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UInt opc2 = IFIELD( theInstr, 0, 11 ); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); if (opc1 != 0x4) { vex_printf("dis_av_logic(ppc)(opc1 != 0x4)\n"); return False; } switch (opc2) { case 0x404: // vand (And, AV p147) DIP("vand v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA), mkexpr(vB)) ); break; case 0x444: // vandc (And, AV p148) DIP("vandc v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, mkexpr(vB))) ); break; case 0x484: // vor (Or, AV p217) DIP("vor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB)) ); break; case 0x4C4: // vxor (Xor, AV p282) DIP("vxor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_XorV128, mkexpr(vA), mkexpr(vB)) ); break; case 0x504: // vnor (Nor, AV p216) DIP("vnor v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, unop(Iop_NotV128, binop(Iop_OrV128, mkexpr(vA), mkexpr(vB))) ); break; case 0x544: // vorc (vA Or'd with complement of vb) DIP("vorc v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop( Iop_OrV128, mkexpr( vA ), unop( Iop_NotV128, mkexpr( vB ) ) ) ); break; case 0x584: // vnand (Nand) DIP("vnand v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, unop( Iop_NotV128, binop(Iop_AndV128, mkexpr( vA ), mkexpr( vB ) ) ) ); break; case 0x684: // veqv (complemented XOr) DIP("veqv v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, unop( Iop_NotV128, binop( Iop_XorV128, mkexpr( vA ), mkexpr( vB ) ) ) ); break; default: vex_printf("dis_av_logic(ppc)(opc2=0x%x)\n", opc2); return False; } return True; } /* AltiVec Compare Instructions */ static Bool dis_av_cmp ( UInt theInstr ) { /* VXR-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UChar flag_rC = ifieldBIT10(theInstr); UInt opc2 = IFIELD( theInstr, 0, 10 ); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); IRTemp vD = newTemp(Ity_V128); assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); if (opc1 != 0x4) { vex_printf("dis_av_cmp(ppc)(instr)\n"); return False; } switch (opc2) { case 0x006: // vcmpequb (Compare Equal-to Unsigned B, AV p160) DIP("vcmpequb%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpEQ8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x046: // vcmpequh (Compare Equal-to Unsigned HW, AV p161) DIP("vcmpequh%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpEQ16x8, mkexpr(vA), mkexpr(vB)) ); break; case 0x086: // vcmpequw (Compare Equal-to Unsigned W, AV p162) DIP("vcmpequw%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpEQ32x4, mkexpr(vA), mkexpr(vB)) ); break; case 0x0C7: // vcmpequd (Compare Equal-to Unsigned Doubleword) DIP("vcmpequd%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpEQ64x2, mkexpr(vA), mkexpr(vB)) ); break; case 0x206: // vcmpgtub (Compare Greater-than Unsigned B, AV p168) DIP("vcmpgtub%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGT8Ux16, mkexpr(vA), mkexpr(vB)) ); break; case 0x246: // vcmpgtuh (Compare Greater-than Unsigned HW, AV p169) DIP("vcmpgtuh%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGT16Ux8, mkexpr(vA), mkexpr(vB)) ); break; case 0x286: // vcmpgtuw (Compare Greater-than Unsigned W, AV p170) DIP("vcmpgtuw%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGT32Ux4, mkexpr(vA), mkexpr(vB)) ); break; case 0x2C7: // vcmpgtud (Compare Greater-than Unsigned double) DIP("vcmpgtud%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGT64Ux2, mkexpr(vA), mkexpr(vB)) ); break; case 0x306: // vcmpgtsb (Compare Greater-than Signed B, AV p165) DIP("vcmpgtsb%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGT8Sx16, mkexpr(vA), mkexpr(vB)) ); break; case 0x346: // vcmpgtsh (Compare Greater-than Signed HW, AV p166) DIP("vcmpgtsh%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGT16Sx8, mkexpr(vA), mkexpr(vB)) ); break; case 0x386: // vcmpgtsw (Compare Greater-than Signed W, AV p167) DIP("vcmpgtsw%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGT32Sx4, mkexpr(vA), mkexpr(vB)) ); break; case 0x3C7: // vcmpgtsd (Compare Greater-than Signed double) DIP("vcmpgtsd%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGT64Sx2, mkexpr(vA), mkexpr(vB)) ); break; default: vex_printf("dis_av_cmp(ppc)(opc2)\n"); return False; } putVReg( vD_addr, mkexpr(vD) ); if (flag_rC) { set_AV_CR6( mkexpr(vD), True ); } return True; } /* AltiVec Multiply-Sum Instructions */ static Bool dis_av_multarith ( UInt theInstr ) { /* VA-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UChar vC_addr = ifieldRegC(theInstr); UChar opc2 = toUChar( IFIELD( theInstr, 0, 6 ) ); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); IRTemp vC = newTemp(Ity_V128); IRTemp zeros = newTemp(Ity_V128); IRTemp aLo = newTemp(Ity_V128); IRTemp bLo = newTemp(Ity_V128); IRTemp cLo = newTemp(Ity_V128); IRTemp zLo = newTemp(Ity_V128); IRTemp aHi = newTemp(Ity_V128); IRTemp bHi = newTemp(Ity_V128); IRTemp cHi = newTemp(Ity_V128); IRTemp zHi = newTemp(Ity_V128); IRTemp abEvn = newTemp(Ity_V128); IRTemp abOdd = newTemp(Ity_V128); IRTemp z3 = newTemp(Ity_I64); IRTemp z2 = newTemp(Ity_I64); IRTemp z1 = newTemp(Ity_I64); IRTemp z0 = newTemp(Ity_I64); IRTemp ab7, ab6, ab5, ab4, ab3, ab2, ab1, ab0; IRTemp c3, c2, c1, c0; ab7 = ab6 = ab5 = ab4 = ab3 = ab2 = ab1 = ab0 = IRTemp_INVALID; c3 = c2 = c1 = c0 = IRTemp_INVALID; assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); assign( vC, getVReg(vC_addr)); assign( zeros, unop(Iop_Dup32x4, mkU32(0)) ); if (opc1 != 0x4) { vex_printf("dis_av_multarith(ppc)(instr)\n"); return False; } switch (opc2) { /* Multiply-Add */ case 0x20: { // vmhaddshs (Mult Hi, Add Signed HW Saturate, AV p185) IRTemp cSigns = newTemp(Ity_V128); DIP("vmhaddshs v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC))); assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA))); assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB))); assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC))); assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA))); assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB))); assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC))); assign( zLo, binop(Iop_Add32x4, mkexpr(cLo), binop(Iop_SarN32x4, binop(Iop_MullEven16Sx8, mkexpr(aLo), mkexpr(bLo)), mkU8(15))) ); assign( zHi, binop(Iop_Add32x4, mkexpr(cHi), binop(Iop_SarN32x4, binop(Iop_MullEven16Sx8, mkexpr(aHi), mkexpr(bHi)), mkU8(15))) ); putVReg( vD_addr, binop(Iop_QNarrowBin32Sto16Sx8, mkexpr(zHi), mkexpr(zLo)) ); break; } case 0x21: { // vmhraddshs (Mult High Round, Add Signed HW Saturate, AV p186) IRTemp zKonst = newTemp(Ity_V128); IRTemp cSigns = newTemp(Ity_V128); DIP("vmhraddshs v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); assign(cSigns, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vC)) ); assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA))); assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB))); assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(cSigns),mkexpr(vC))); assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA))); assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB))); assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(cSigns),mkexpr(vC))); /* shifting our const avoids store/load version of Dup */ assign( zKonst, binop(Iop_ShlN32x4, unop(Iop_Dup32x4, mkU32(0x1)), mkU8(14)) ); assign( zLo, binop(Iop_Add32x4, mkexpr(cLo), binop(Iop_SarN32x4, binop(Iop_Add32x4, mkexpr(zKonst), binop(Iop_MullEven16Sx8, mkexpr(aLo), mkexpr(bLo))), mkU8(15))) ); assign( zHi, binop(Iop_Add32x4, mkexpr(cHi), binop(Iop_SarN32x4, binop(Iop_Add32x4, mkexpr(zKonst), binop(Iop_MullEven16Sx8, mkexpr(aHi), mkexpr(bHi))), mkU8(15))) ); putVReg( vD_addr, binop(Iop_QNarrowBin32Sto16Sx8, mkexpr(zHi), mkexpr(zLo)) ); break; } case 0x22: { // vmladduhm (Mult Low, Add Unsigned HW Modulo, AV p194) DIP("vmladduhm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); assign(aLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vA))); assign(bLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vB))); assign(cLo, binop(Iop_InterleaveLO16x8, mkexpr(zeros), mkexpr(vC))); assign(aHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vA))); assign(bHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vB))); assign(cHi, binop(Iop_InterleaveHI16x8, mkexpr(zeros), mkexpr(vC))); assign(zLo, binop(Iop_Add32x4, binop(Iop_MullEven16Ux8, mkexpr(aLo), mkexpr(bLo)), mkexpr(cLo)) ); assign(zHi, binop(Iop_Add32x4, binop(Iop_MullEven16Ux8, mkexpr(aHi), mkexpr(bHi)), mkexpr(cHi))); putVReg( vD_addr, binop(Iop_NarrowBin32to16x8, mkexpr(zHi), mkexpr(zLo)) ); break; } /* Multiply-Sum */ case 0x24: { // vmsumubm (Multiply Sum Unsigned B Modulo, AV p204) IRTemp abEE, abEO, abOE, abOO; abEE = abEO = abOE = abOO = IRTemp_INVALID; DIP("vmsumubm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); /* multiply vA,vB (unsigned, widening) */ assign( abEvn, MK_Iop_MullOdd8Ux16( mkexpr(vA), mkexpr(vB) )); assign( abOdd, binop(Iop_MullEven8Ux16, mkexpr(vA), mkexpr(vB)) ); /* evn,odd: V128_16Ux8 -> 2 x V128_32Ux4, zero-extended */ expand16Ux8( mkexpr(abEvn), &abEE, &abEO ); expand16Ux8( mkexpr(abOdd), &abOE, &abOO ); putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vC), binop(Iop_Add32x4, binop(Iop_Add32x4, mkexpr(abEE), mkexpr(abEO)), binop(Iop_Add32x4, mkexpr(abOE), mkexpr(abOO)))) ); break; } case 0x25: { // vmsummbm (Multiply Sum Mixed-Sign B Modulo, AV p201) IRTemp aEvn, aOdd, bEvn, bOdd; IRTemp abEE = newTemp(Ity_V128); IRTemp abEO = newTemp(Ity_V128); IRTemp abOE = newTemp(Ity_V128); IRTemp abOO = newTemp(Ity_V128); aEvn = aOdd = bEvn = bOdd = IRTemp_INVALID; DIP("vmsummbm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); /* sign-extend vA, zero-extend vB, for mixed-sign multiply (separating out adjacent lanes to different vectors) */ expand8Sx16( mkexpr(vA), &aEvn, &aOdd ); expand8Ux16( mkexpr(vB), &bEvn, &bOdd ); /* multiply vA, vB, again separating adjacent lanes */ assign( abEE, MK_Iop_MullOdd16Sx8( mkexpr(aEvn), mkexpr(bEvn) )); assign( abEO, binop(Iop_MullEven16Sx8, mkexpr(aEvn), mkexpr(bEvn)) ); assign( abOE, MK_Iop_MullOdd16Sx8( mkexpr(aOdd), mkexpr(bOdd) )); assign( abOO, binop(Iop_MullEven16Sx8, mkexpr(aOdd), mkexpr(bOdd)) ); /* add results together, + vC */ putVReg( vD_addr, binop(Iop_QAdd32Sx4, mkexpr(vC), binop(Iop_QAdd32Sx4, binop(Iop_QAdd32Sx4, mkexpr(abEE), mkexpr(abEO)), binop(Iop_QAdd32Sx4, mkexpr(abOE), mkexpr(abOO)))) ); break; } case 0x26: { // vmsumuhm (Multiply Sum Unsigned HW Modulo, AV p205) DIP("vmsumuhm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); assign( abEvn, MK_Iop_MullOdd16Ux8( mkexpr(vA), mkexpr(vB) )); assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) ); putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vC), binop(Iop_Add32x4, mkexpr(abEvn), mkexpr(abOdd))) ); break; } case 0x27: { // vmsumuhs (Multiply Sum Unsigned HW Saturate, AV p206) DIP("vmsumuhs v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); /* widening multiply, separating lanes */ assign( abEvn, MK_Iop_MullOdd16Ux8(mkexpr(vA), mkexpr(vB) )); assign( abOdd, binop(Iop_MullEven16Ux8, mkexpr(vA), mkexpr(vB)) ); /* break V128 to 4xI32's, zero-extending to I64's */ breakV128to4x64U( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 ); breakV128to4x64U( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 ); breakV128to4x64U( mkexpr(vC), &c3, &c2, &c1, &c0 ); /* add lanes */ assign( z3, binop(Iop_Add64, mkexpr(c3), binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6)))); assign( z2, binop(Iop_Add64, mkexpr(c2), binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4)))); assign( z1, binop(Iop_Add64, mkexpr(c1), binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2)))); assign( z0, binop(Iop_Add64, mkexpr(c0), binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0)))); /* saturate-narrow to 32bit, and combine to V128 */ putVReg( vD_addr, mkV128from4x64U( mkexpr(z3), mkexpr(z2), mkexpr(z1), mkexpr(z0)) ); break; } case 0x28: { // vmsumshm (Multiply Sum Signed HW Modulo, AV p202) DIP("vmsumshm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) )); assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) ); putVReg( vD_addr, binop(Iop_Add32x4, mkexpr(vC), binop(Iop_Add32x4, mkexpr(abOdd), mkexpr(abEvn))) ); break; } case 0x29: { // vmsumshs (Multiply Sum Signed HW Saturate, AV p203) DIP("vmsumshs v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); /* widening multiply, separating lanes */ assign( abEvn, MK_Iop_MullOdd16Sx8( mkexpr(vA), mkexpr(vB) )); assign( abOdd, binop(Iop_MullEven16Sx8, mkexpr(vA), mkexpr(vB)) ); /* break V128 to 4xI32's, sign-extending to I64's */ breakV128to4x64S( mkexpr(abEvn), &ab7, &ab5, &ab3, &ab1 ); breakV128to4x64S( mkexpr(abOdd), &ab6, &ab4, &ab2, &ab0 ); breakV128to4x64S( mkexpr(vC), &c3, &c2, &c1, &c0 ); /* add lanes */ assign( z3, binop(Iop_Add64, mkexpr(c3), binop(Iop_Add64, mkexpr(ab7), mkexpr(ab6)))); assign( z2, binop(Iop_Add64, mkexpr(c2), binop(Iop_Add64, mkexpr(ab5), mkexpr(ab4)))); assign( z1, binop(Iop_Add64, mkexpr(c1), binop(Iop_Add64, mkexpr(ab3), mkexpr(ab2)))); assign( z0, binop(Iop_Add64, mkexpr(c0), binop(Iop_Add64, mkexpr(ab1), mkexpr(ab0)))); /* saturate-narrow to 32bit, and combine to V128 */ putVReg( vD_addr, mkV128from4x64S( mkexpr(z3), mkexpr(z2), mkexpr(z1), mkexpr(z0)) ); break; } default: vex_printf("dis_av_multarith(ppc)(opc2)\n"); return False; } return True; } /* AltiVec Polynomial Multiply-Sum Instructions */ static Bool dis_av_polymultarith ( UInt theInstr ) { /* VA-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UChar vC_addr = ifieldRegC(theInstr); UInt opc2 = IFIELD(theInstr, 0, 11); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); IRTemp vC = newTemp(Ity_V128); assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); assign( vC, getVReg(vC_addr)); if (opc1 != 0x4) { vex_printf("dis_av_polymultarith(ppc)(instr)\n"); return False; } switch (opc2) { /* Polynomial Multiply-Add */ case 0x408: // vpmsumb Vector Polynomial Multipy-sum Byte DIP("vpmsumb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_PolynomialMulAdd8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x448: // vpmsumd Vector Polynomial Multipy-sum Double Word DIP("vpmsumd v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_PolynomialMulAdd64x2, mkexpr(vA), mkexpr(vB)) ); break; case 0x488: // vpmsumw Vector Polynomial Multipy-sum Word DIP("vpmsumw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_PolynomialMulAdd32x4, mkexpr(vA), mkexpr(vB)) ); break; case 0x4C8: // vpmsumh Vector Polynomial Multipy-sum Half Word DIP("vpmsumh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_PolynomialMulAdd16x8, mkexpr(vA), mkexpr(vB)) ); break; default: vex_printf("dis_av_polymultarith(ppc)(opc2=0x%x)\n", opc2); return False; } return True; } /* AltiVec Shift/Rotate Instructions */ static Bool dis_av_shift ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UInt opc2 = IFIELD( theInstr, 0, 11 ); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); if (opc1 != 0x4){ vex_printf("dis_av_shift(ppc)(instr)\n"); return False; } switch (opc2) { /* Rotate */ case 0x004: // vrlb (Rotate Left Integer B, AV p234) DIP("vrlb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Rol8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x044: // vrlh (Rotate Left Integer HW, AV p235) DIP("vrlh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Rol16x8, mkexpr(vA), mkexpr(vB)) ); break; case 0x084: // vrlw (Rotate Left Integer W, AV p236) DIP("vrlw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Rol32x4, mkexpr(vA), mkexpr(vB)) ); break; case 0x0C4: // vrld (Rotate Left Integer Double Word) DIP("vrld v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Rol64x2, mkexpr(vA), mkexpr(vB)) ); break; /* Shift Left */ case 0x104: // vslb (Shift Left Integer B, AV p240) DIP("vslb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Shl8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x144: // vslh (Shift Left Integer HW, AV p242) DIP("vslh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Shl16x8, mkexpr(vA), mkexpr(vB)) ); break; case 0x184: // vslw (Shift Left Integer W, AV p244) DIP("vslw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Shl32x4, mkexpr(vA), mkexpr(vB)) ); break; case 0x5C4: // vsld (Shift Left Integer Double Word) DIP("vsld v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Shl64x2, mkexpr(vA), mkexpr(vB)) ); break; case 0x1C4: { // vsl (Shift Left, AV p239) IRTemp sh = newTemp(Ity_I8); DIP("vsl v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); assign( sh, binop(Iop_And8, mkU8(0x7), unop(Iop_32to8, unop(Iop_V128to32, mkexpr(vB)))) ); putVReg( vD_addr, binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) ); break; } case 0x40C: { // vslo (Shift Left by Octet, AV p243) IRTemp sh = newTemp(Ity_I8); DIP("vslo v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); assign( sh, binop(Iop_And8, mkU8(0x78), unop(Iop_32to8, unop(Iop_V128to32, mkexpr(vB)))) ); putVReg( vD_addr, binop(Iop_ShlV128, mkexpr(vA), mkexpr(sh)) ); break; } /* Shift Right */ case 0x204: // vsrb (Shift Right B, AV p256) DIP("vsrb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Shr8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x244: // vsrh (Shift Right HW, AV p257) DIP("vsrh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Shr16x8, mkexpr(vA), mkexpr(vB)) ); break; case 0x284: // vsrw (Shift Right W, AV p259) DIP("vsrw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Shr32x4, mkexpr(vA), mkexpr(vB)) ); break; case 0x2C4: { // vsr (Shift Right, AV p251) IRTemp sh = newTemp(Ity_I8); DIP("vsr v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); assign( sh, binop(Iop_And8, mkU8(0x7), unop(Iop_32to8, unop(Iop_V128to32, mkexpr(vB)))) ); putVReg( vD_addr, binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) ); break; } case 0x304: // vsrab (Shift Right Alg B, AV p253) DIP("vsrab v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Sar8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x344: // vsrah (Shift Right Alg HW, AV p254) DIP("vsrah v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Sar16x8, mkexpr(vA), mkexpr(vB)) ); break; case 0x384: // vsraw (Shift Right Alg W, AV p255) DIP("vsraw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Sar32x4, mkexpr(vA), mkexpr(vB)) ); break; case 0x3C4: // vsrad (Shift Right Alg Double Word) DIP("vsrad v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Sar64x2, mkexpr(vA), mkexpr(vB)) ); break; case 0x44C: { // vsro (Shift Right by Octet, AV p258) IRTemp sh = newTemp(Ity_I8); DIP("vsro v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); assign( sh, binop(Iop_And8, mkU8(0x78), unop(Iop_32to8, unop(Iop_V128to32, mkexpr(vB)))) ); putVReg( vD_addr, binop(Iop_ShrV128, mkexpr(vA), mkexpr(sh)) ); break; } case 0x6C4: // vsrd (Shift Right Double Word) DIP("vsrd v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Shr64x2, mkexpr(vA), mkexpr(vB)) ); break; default: vex_printf("dis_av_shift(ppc)(opc2)\n"); return False; } return True; } /* AltiVec Permute Instructions */ static Bool dis_av_permute ( UInt theInstr ) { /* VA-Form, VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar UIMM_5 = vA_addr; UChar vB_addr = ifieldRegB(theInstr); UChar vC_addr = ifieldRegC(theInstr); UChar b10 = ifieldBIT10(theInstr); UChar SHB_uimm4 = toUChar( IFIELD( theInstr, 6, 4 ) ); UInt opc2 = toUChar( IFIELD( theInstr, 0, 6 ) ); UChar SIMM_8 = extend_s_5to8(UIMM_5); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); IRTemp vC = newTemp(Ity_V128); assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); assign( vC, getVReg(vC_addr)); if (opc1 != 0x4) { vex_printf("dis_av_permute(ppc)(instr)\n"); return False; } switch (opc2) { case 0x2A: // vsel (Conditional Select, AV p238) DIP("vsel v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); /* vD = (vA & ~vC) | (vB & vC) */ putVReg( vD_addr, binop(Iop_OrV128, binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, mkexpr(vC))), binop(Iop_AndV128, mkexpr(vB), mkexpr(vC))) ); return True; case 0x2B: { // vperm (Permute, AV p218) /* limited to two args for IR, so have to play games... */ IRTemp a_perm = newTemp(Ity_V128); IRTemp b_perm = newTemp(Ity_V128); IRTemp mask = newTemp(Ity_V128); IRTemp vC_andF = newTemp(Ity_V128); DIP("vperm v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr, vC_addr); /* Limit the Perm8x16 steering values to 0 .. 15 as that is what IR specifies, and also to hide irrelevant bits from memcheck */ assign( vC_andF, binop(Iop_AndV128, mkexpr(vC), unop(Iop_Dup8x16, mkU8(0xF))) ); assign( a_perm, binop(Iop_Perm8x16, mkexpr(vA), mkexpr(vC_andF)) ); assign( b_perm, binop(Iop_Perm8x16, mkexpr(vB), mkexpr(vC_andF)) ); // mask[i8] = (vC[i8]_4 == 1) ? 0xFF : 0x0 assign( mask, binop(Iop_SarN8x16, binop(Iop_ShlN8x16, mkexpr(vC), mkU8(3)), mkU8(7)) ); // dst = (a & ~mask) | (b & mask) putVReg( vD_addr, binop(Iop_OrV128, binop(Iop_AndV128, mkexpr(a_perm), unop(Iop_NotV128, mkexpr(mask))), binop(Iop_AndV128, mkexpr(b_perm), mkexpr(mask))) ); return True; } case 0x2C: // vsldoi (Shift Left Double by Octet Imm, AV p241) if (b10 != 0) { vex_printf("dis_av_permute(ppc)(vsldoi)\n"); return False; } DIP("vsldoi v%d,v%d,v%d,%d\n", vD_addr, vA_addr, vB_addr, SHB_uimm4); if (SHB_uimm4 == 0) putVReg( vD_addr, mkexpr(vA) ); else putVReg( vD_addr, binop(Iop_OrV128, binop(Iop_ShlV128, mkexpr(vA), mkU8(SHB_uimm4*8)), binop(Iop_ShrV128, mkexpr(vB), mkU8((16-SHB_uimm4)*8))) ); return True; case 0x2D: { // vpermxor (Vector Permute and Exclusive-OR) IRTemp a_perm = newTemp(Ity_V128); IRTemp b_perm = newTemp(Ity_V128); IRTemp vrc_a = newTemp(Ity_V128); IRTemp vrc_b = newTemp(Ity_V128); /* IBM index is 0:7, Change index value to index 7:0 */ assign( vrc_b, binop( Iop_AndV128, mkexpr( vC ), unop( Iop_Dup8x16, mkU8( 0xF ) ) ) ); assign( vrc_a, binop( Iop_ShrV128, binop( Iop_AndV128, mkexpr( vC ), unop( Iop_Dup8x16, mkU8( 0xF0 ) ) ), mkU8 ( 4 ) ) ); assign( a_perm, binop( Iop_Perm8x16, mkexpr( vA ), mkexpr( vrc_a ) ) ); assign( b_perm, binop( Iop_Perm8x16, mkexpr( vB ), mkexpr( vrc_b ) ) ); putVReg( vD_addr, binop( Iop_XorV128, mkexpr( a_perm ), mkexpr( b_perm) ) ); return True; } default: break; // Fall through... } opc2 = IFIELD( theInstr, 0, 11 ); switch (opc2) { /* Merge */ case 0x00C: // vmrghb (Merge High B, AV p195) DIP("vmrghb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_InterleaveHI8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x04C: // vmrghh (Merge High HW, AV p196) DIP("vmrghh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_InterleaveHI16x8, mkexpr(vA), mkexpr(vB)) ); break; case 0x08C: // vmrghw (Merge High W, AV p197) DIP("vmrghw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_InterleaveHI32x4, mkexpr(vA), mkexpr(vB)) ); break; case 0x10C: // vmrglb (Merge Low B, AV p198) DIP("vmrglb v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_InterleaveLO8x16, mkexpr(vA), mkexpr(vB)) ); break; case 0x14C: // vmrglh (Merge Low HW, AV p199) DIP("vmrglh v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_InterleaveLO16x8, mkexpr(vA), mkexpr(vB)) ); break; case 0x18C: // vmrglw (Merge Low W, AV p200) DIP("vmrglw v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_InterleaveLO32x4, mkexpr(vA), mkexpr(vB)) ); break; /* Splat */ case 0x20C: { // vspltb (Splat Byte, AV p245) /* vD = Dup8x16( vB[UIMM_5] ) */ UChar sh_uimm = (15 - (UIMM_5 & 15)) * 8; DIP("vspltb v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5); putVReg( vD_addr, unop(Iop_Dup8x16, unop(Iop_32to8, unop(Iop_V128to32, binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) ); break; } case 0x24C: { // vsplth (Splat Half Word, AV p246) UChar sh_uimm = (7 - (UIMM_5 & 7)) * 16; DIP("vsplth v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5); putVReg( vD_addr, unop(Iop_Dup16x8, unop(Iop_32to16, unop(Iop_V128to32, binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm))))) ); break; } case 0x28C: { // vspltw (Splat Word, AV p250) /* vD = Dup32x4( vB[UIMM_5] ) */ UChar sh_uimm = (3 - (UIMM_5 & 3)) * 32; DIP("vspltw v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5); putVReg( vD_addr, unop(Iop_Dup32x4, unop(Iop_V128to32, binop(Iop_ShrV128, mkexpr(vB), mkU8(sh_uimm)))) ); break; } case 0x30C: // vspltisb (Splat Immediate Signed B, AV p247) DIP("vspltisb v%d,%d\n", vD_addr, (Char)SIMM_8); putVReg( vD_addr, unop(Iop_Dup8x16, mkU8(SIMM_8)) ); break; case 0x34C: // vspltish (Splat Immediate Signed HW, AV p248) DIP("vspltish v%d,%d\n", vD_addr, (Char)SIMM_8); putVReg( vD_addr, unop(Iop_Dup16x8, mkU16(extend_s_8to32(SIMM_8))) ); break; case 0x38C: // vspltisw (Splat Immediate Signed W, AV p249) DIP("vspltisw v%d,%d\n", vD_addr, (Char)SIMM_8); putVReg( vD_addr, unop(Iop_Dup32x4, mkU32(extend_s_8to32(SIMM_8))) ); break; case 0x68C: // vmrgow (Merge Odd Word) DIP("vmrgow v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); /* VD[0] <- VA[1] VD[1] <- VB[1] VD[2] <- VA[3] VD[3] <- VB[3] */ putVReg( vD_addr, binop(Iop_CatOddLanes32x4, mkexpr(vA), mkexpr(vB) ) ); break; case 0x78C: // vmrgew (Merge Even Word) DIP("vmrgew v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); /* VD[0] <- VA[0] VD[1] <- VB[0] VD[2] <- VA[2] VD[3] <- VB[2] */ putVReg( vD_addr, binop(Iop_CatEvenLanes32x4, mkexpr(vA), mkexpr(vB) ) ); break; default: vex_printf("dis_av_permute(ppc)(opc2)\n"); return False; } return True; } /* AltiVec Pack/Unpack Instructions */ static Bool dis_av_pack ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UInt opc2 = IFIELD( theInstr, 0, 11 ); IRTemp signs = IRTemp_INVALID; IRTemp zeros = IRTemp_INVALID; IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); if (opc1 != 0x4) { vex_printf("dis_av_pack(ppc)(instr)\n"); return False; } switch (opc2) { /* Packing */ case 0x00E: // vpkuhum (Pack Unsigned HW Unsigned Modulo, AV p224) DIP("vpkuhum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_NarrowBin16to8x16, mkexpr(vA), mkexpr(vB)) ); return True; case 0x04E: // vpkuwum (Pack Unsigned W Unsigned Modulo, AV p226) DIP("vpkuwum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_NarrowBin32to16x8, mkexpr(vA), mkexpr(vB)) ); return True; case 0x08E: // vpkuhus (Pack Unsigned HW Unsigned Saturate, AV p225) DIP("vpkuhus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QNarrowBin16Uto8Ux16, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] return True; case 0x0CE: // vpkuwus (Pack Unsigned W Unsigned Saturate, AV p227) DIP("vpkuwus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QNarrowBin32Uto16Ux8, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] return True; case 0x10E: { // vpkshus (Pack Signed HW Unsigned Saturate, AV p221) // This insn does a signed->unsigned saturating conversion. // Conversion done here, then uses unsigned->unsigned vpk insn: // => UnsignedSaturatingNarrow( x & ~ (x >>s 15) ) IRTemp vA_tmp = newTemp(Ity_V128); IRTemp vB_tmp = newTemp(Ity_V128); DIP("vpkshus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, binop(Iop_SarN16x8, mkexpr(vA), mkU8(15)))) ); assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB), unop(Iop_NotV128, binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)))) ); putVReg( vD_addr, binop(Iop_QNarrowBin16Uto8Ux16, mkexpr(vA_tmp), mkexpr(vB_tmp)) ); // TODO: set VSCR[SAT] return True; } case 0x14E: { // vpkswus (Pack Signed W Unsigned Saturate, AV p223) // This insn does a signed->unsigned saturating conversion. // Conversion done here, then uses unsigned->unsigned vpk insn: // => UnsignedSaturatingNarrow( x & ~ (x >>s 31) ) IRTemp vA_tmp = newTemp(Ity_V128); IRTemp vB_tmp = newTemp(Ity_V128); DIP("vpkswus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, binop(Iop_SarN32x4, mkexpr(vA), mkU8(31)))) ); assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB), unop(Iop_NotV128, binop(Iop_SarN32x4, mkexpr(vB), mkU8(31)))) ); putVReg( vD_addr, binop(Iop_QNarrowBin32Uto16Ux8, mkexpr(vA_tmp), mkexpr(vB_tmp)) ); // TODO: set VSCR[SAT] return True; } case 0x18E: // vpkshss (Pack Signed HW Signed Saturate, AV p220) DIP("vpkshss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QNarrowBin16Sto8Sx16, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] return True; case 0x1CE: // vpkswss (Pack Signed W Signed Saturate, AV p222) DIP("vpkswss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QNarrowBin32Sto16Sx8, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] return True; case 0x30E: { // vpkpx (Pack Pixel, AV p219) /* CAB: Worth a new primop? */ /* Using shifts to compact pixel elements, then packing them */ IRTemp a1 = newTemp(Ity_V128); IRTemp a2 = newTemp(Ity_V128); IRTemp a3 = newTemp(Ity_V128); IRTemp a_tmp = newTemp(Ity_V128); IRTemp b1 = newTemp(Ity_V128); IRTemp b2 = newTemp(Ity_V128); IRTemp b3 = newTemp(Ity_V128); IRTemp b_tmp = newTemp(Ity_V128); DIP("vpkpx v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); assign( a1, binop(Iop_ShlN16x8, binop(Iop_ShrN32x4, mkexpr(vA), mkU8(19)), mkU8(10)) ); assign( a2, binop(Iop_ShlN16x8, binop(Iop_ShrN16x8, mkexpr(vA), mkU8(11)), mkU8(5)) ); assign( a3, binop(Iop_ShrN16x8, binop(Iop_ShlN16x8, mkexpr(vA), mkU8(8)), mkU8(11)) ); assign( a_tmp, binop(Iop_OrV128, mkexpr(a1), binop(Iop_OrV128, mkexpr(a2), mkexpr(a3))) ); assign( b1, binop(Iop_ShlN16x8, binop(Iop_ShrN32x4, mkexpr(vB), mkU8(19)), mkU8(10)) ); assign( b2, binop(Iop_ShlN16x8, binop(Iop_ShrN16x8, mkexpr(vB), mkU8(11)), mkU8(5)) ); assign( b3, binop(Iop_ShrN16x8, binop(Iop_ShlN16x8, mkexpr(vB), mkU8(8)), mkU8(11)) ); assign( b_tmp, binop(Iop_OrV128, mkexpr(b1), binop(Iop_OrV128, mkexpr(b2), mkexpr(b3))) ); putVReg( vD_addr, binop(Iop_NarrowBin32to16x8, mkexpr(a_tmp), mkexpr(b_tmp)) ); return True; } case 0x44E: // vpkudum (Pack Unsigned Double Word Unsigned Modulo) DIP("vpkudum v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_NarrowBin64to32x4, mkexpr(vA), mkexpr(vB)) ); return True; case 0x4CE: // vpkudus (Pack Unsigned Double Word Unsigned Saturate) DIP("vpkudus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QNarrowBin64Uto32Ux4, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] return True; case 0x54E: { // vpksdus (Pack Signed Double Word Unsigned Saturate) // This insn does a doubled signed->double unsigned saturating conversion // Conversion done here, then uses unsigned->unsigned vpk insn: // => UnsignedSaturatingNarrow( x & ~ (x >>s 31) ) // This is similar to the technique used for vpkswus, except done // with double word integers versus word integers. IRTemp vA_tmp = newTemp(Ity_V128); IRTemp vB_tmp = newTemp(Ity_V128); DIP("vpksdus v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); assign( vA_tmp, binop(Iop_AndV128, mkexpr(vA), unop(Iop_NotV128, binop(Iop_SarN64x2, mkexpr(vA), mkU8(63)))) ); assign( vB_tmp, binop(Iop_AndV128, mkexpr(vB), unop(Iop_NotV128, binop(Iop_SarN64x2, mkexpr(vB), mkU8(63)))) ); putVReg( vD_addr, binop(Iop_QNarrowBin64Uto32Ux4, mkexpr(vA_tmp), mkexpr(vB_tmp)) ); // TODO: set VSCR[SAT] return True; } case 0x5CE: // vpksdss (Pack Signed double word Signed Saturate) DIP("vpksdss v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_QNarrowBin64Sto32Sx4, mkexpr(vA), mkexpr(vB)) ); // TODO: set VSCR[SAT] return True; default: break; // Fall through... } if (vA_addr != 0) { vex_printf("dis_av_pack(ppc)(vA_addr)\n"); return False; } signs = newTemp(Ity_V128); zeros = newTemp(Ity_V128); assign( zeros, unop(Iop_Dup32x4, mkU32(0)) ); switch (opc2) { /* Unpacking */ case 0x20E: { // vupkhsb (Unpack High Signed B, AV p277) DIP("vupkhsb v%d,v%d\n", vD_addr, vB_addr); assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) ); putVReg( vD_addr, binop(Iop_InterleaveHI8x16, mkexpr(signs), mkexpr(vB)) ); break; } case 0x24E: { // vupkhsh (Unpack High Signed HW, AV p278) DIP("vupkhsh v%d,v%d\n", vD_addr, vB_addr); assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) ); putVReg( vD_addr, binop(Iop_InterleaveHI16x8, mkexpr(signs), mkexpr(vB)) ); break; } case 0x28E: { // vupklsb (Unpack Low Signed B, AV p280) DIP("vupklsb v%d,v%d\n", vD_addr, vB_addr); assign( signs, binop(Iop_CmpGT8Sx16, mkexpr(zeros), mkexpr(vB)) ); putVReg( vD_addr, binop(Iop_InterleaveLO8x16, mkexpr(signs), mkexpr(vB)) ); break; } case 0x2CE: { // vupklsh (Unpack Low Signed HW, AV p281) DIP("vupklsh v%d,v%d\n", vD_addr, vB_addr); assign( signs, binop(Iop_CmpGT16Sx8, mkexpr(zeros), mkexpr(vB)) ); putVReg( vD_addr, binop(Iop_InterleaveLO16x8, mkexpr(signs), mkexpr(vB)) ); break; } case 0x34E: { // vupkhpx (Unpack High Pixel16, AV p276) /* CAB: Worth a new primop? */ /* Using shifts to isolate pixel elements, then expanding them */ IRTemp z0 = newTemp(Ity_V128); IRTemp z1 = newTemp(Ity_V128); IRTemp z01 = newTemp(Ity_V128); IRTemp z2 = newTemp(Ity_V128); IRTemp z3 = newTemp(Ity_V128); IRTemp z23 = newTemp(Ity_V128); DIP("vupkhpx v%d,v%d\n", vD_addr, vB_addr); assign( z0, binop(Iop_ShlN16x8, binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)), mkU8(8)) ); assign( z1, binop(Iop_ShrN16x8, binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)), mkU8(11)) ); assign( z01, binop(Iop_InterleaveHI16x8, mkexpr(zeros), binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) ); assign( z2, binop(Iop_ShrN16x8, binop(Iop_ShlN16x8, binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)), mkU8(11)), mkU8(3)) ); assign( z3, binop(Iop_ShrN16x8, binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)), mkU8(11)) ); assign( z23, binop(Iop_InterleaveHI16x8, mkexpr(zeros), binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) ); putVReg( vD_addr, binop(Iop_OrV128, binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)), mkexpr(z23)) ); break; } case 0x3CE: { // vupklpx (Unpack Low Pixel16, AV p279) /* identical to vupkhpx, except interleaving LO */ IRTemp z0 = newTemp(Ity_V128); IRTemp z1 = newTemp(Ity_V128); IRTemp z01 = newTemp(Ity_V128); IRTemp z2 = newTemp(Ity_V128); IRTemp z3 = newTemp(Ity_V128); IRTemp z23 = newTemp(Ity_V128); DIP("vupklpx v%d,v%d\n", vD_addr, vB_addr); assign( z0, binop(Iop_ShlN16x8, binop(Iop_SarN16x8, mkexpr(vB), mkU8(15)), mkU8(8)) ); assign( z1, binop(Iop_ShrN16x8, binop(Iop_ShlN16x8, mkexpr(vB), mkU8(1)), mkU8(11)) ); assign( z01, binop(Iop_InterleaveLO16x8, mkexpr(zeros), binop(Iop_OrV128, mkexpr(z0), mkexpr(z1))) ); assign( z2, binop(Iop_ShrN16x8, binop(Iop_ShlN16x8, binop(Iop_ShrN16x8, mkexpr(vB), mkU8(5)), mkU8(11)), mkU8(3)) ); assign( z3, binop(Iop_ShrN16x8, binop(Iop_ShlN16x8, mkexpr(vB), mkU8(11)), mkU8(11)) ); assign( z23, binop(Iop_InterleaveLO16x8, mkexpr(zeros), binop(Iop_OrV128, mkexpr(z2), mkexpr(z3))) ); putVReg( vD_addr, binop(Iop_OrV128, binop(Iop_ShlN32x4, mkexpr(z01), mkU8(16)), mkexpr(z23)) ); break; } case 0x64E: { // vupkhsw (Unpack High Signed Word) DIP("vupkhsw v%d,v%d\n", vD_addr, vB_addr); assign( signs, binop(Iop_CmpGT32Sx4, mkexpr(zeros), mkexpr(vB)) ); putVReg( vD_addr, binop(Iop_InterleaveHI32x4, mkexpr(signs), mkexpr(vB)) ); break; } case 0x6CE: { // vupklsw (Unpack Low Signed Word) DIP("vupklsw v%d,v%d\n", vD_addr, vB_addr); assign( signs, binop(Iop_CmpGT32Sx4, mkexpr(zeros), mkexpr(vB)) ); putVReg( vD_addr, binop(Iop_InterleaveLO32x4, mkexpr(signs), mkexpr(vB)) ); break; } default: vex_printf("dis_av_pack(ppc)(opc2)\n"); return False; } return True; } /* AltiVec Cipher Instructions */ static Bool dis_av_cipher ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UInt opc2 = IFIELD( theInstr, 0, 11 ); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); if (opc1 != 0x4) { vex_printf("dis_av_cipher(ppc)(instr)\n"); return False; } switch (opc2) { case 0x508: // vcipher (Vector Inverser Cipher) DIP("vcipher v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_CipherV128, mkexpr(vA), mkexpr(vB)) ); return True; case 0x509: // vcipherlast (Vector Inverser Cipher Last) DIP("vcipherlast v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_CipherLV128, mkexpr(vA), mkexpr(vB)) ); return True; case 0x548: // vncipher (Vector Inverser Cipher) DIP("vncipher v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_NCipherV128, mkexpr(vA), mkexpr(vB)) ); return True; case 0x549: // vncipherlast (Vector Inverser Cipher Last) DIP("vncipherlast v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_NCipherLV128, mkexpr(vA), mkexpr(vB)) ); return True; case 0x5C8: /* vsbox (Vector SubBytes, this does the cipher * subBytes transform) */ DIP("vsbox v%d,v%d\n", vD_addr, vA_addr); putVReg( vD_addr, unop(Iop_CipherSV128, mkexpr(vA) ) ); return True; default: vex_printf("dis_av_cipher(ppc)(opc2)\n"); return False; } return True; } /* AltiVec Secure Hash Instructions */ static Bool dis_av_hash ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vRT_addr = ifieldRegDS(theInstr); UChar vRA_addr = ifieldRegA(theInstr); UChar s_field = IFIELD( theInstr, 11, 5 ); // st and six field UChar st = IFIELD( theInstr, 15, 1 ); // st UChar six = IFIELD( theInstr, 11, 4 ); // six field UInt opc2 = IFIELD( theInstr, 0, 11 ); IRTemp vA = newTemp(Ity_V128); IRTemp dst = newTemp(Ity_V128); assign( vA, getVReg(vRA_addr)); if (opc1 != 0x4) { vex_printf("dis_av_hash(ppc)(instr)\n"); return False; } switch (opc2) { case 0x682: // vshasigmaw DIP("vshasigmaw v%d,v%d,%u,%u\n", vRT_addr, vRA_addr, st, six); assign( dst, binop( Iop_SHA256, mkexpr( vA ), mkU8( s_field) ) ); putVReg( vRT_addr, mkexpr(dst)); return True; case 0x6C2: // vshasigmad, DIP("vshasigmad v%d,v%d,%u,%u\n", vRT_addr, vRA_addr, st, six); putVReg( vRT_addr, binop( Iop_SHA512, mkexpr( vA ), mkU8( s_field) ) ); return True; default: vex_printf("dis_av_hash(ppc)(opc2)\n"); return False; } return True; } /* * This function is used by the Vector add/subtract [extended] modulo/carry * instructions. * - For the non-extended add instructions, the cin arg is set to zero. * - For the extended add instructions, cin is the integer value of * src3.bit[127]. * - For the non-extended subtract instructions, src1 is added to the one's * complement of src2 + 1. We re-use the cin argument to hold the '1' * value for this operation. * - For the extended subtract instructions, cin is the integer value of src3.bit[127]. */ static IRTemp _get_quad_modulo_or_carry(IRExpr * vecA, IRExpr * vecB, IRExpr * cin, Bool modulo) { IRTemp _vecA_32 = IRTemp_INVALID; IRTemp _vecB_32 = IRTemp_INVALID; IRTemp res_32 = IRTemp_INVALID; IRTemp result = IRTemp_INVALID; IRTemp tmp_result = IRTemp_INVALID; IRTemp carry = IRTemp_INVALID; Int i; IRExpr * _vecA_low64 = unop( Iop_V128to64, vecA ); IRExpr * _vecB_low64 = unop( Iop_V128to64, vecB ); IRExpr * _vecA_high64 = unop( Iop_V128HIto64, vecA ); IRExpr * _vecB_high64 = unop( Iop_V128HIto64, vecB ); for (i = 0; i < 4; i++) { _vecA_32 = newTemp(Ity_I32); _vecB_32 = newTemp(Ity_I32); res_32 = newTemp(Ity_I32); switch (i) { case 0: assign(_vecA_32, unop( Iop_64to32, _vecA_low64 ) ); assign(_vecB_32, unop( Iop_64to32, _vecB_low64 ) ); break; case 1: assign(_vecA_32, unop( Iop_64HIto32, _vecA_low64 ) ); assign(_vecB_32, unop( Iop_64HIto32, _vecB_low64 ) ); break; case 2: assign(_vecA_32, unop( Iop_64to32, _vecA_high64 ) ); assign(_vecB_32, unop( Iop_64to32, _vecB_high64 ) ); break; case 3: assign(_vecA_32, unop( Iop_64HIto32, _vecA_high64 ) ); assign(_vecB_32, unop( Iop_64HIto32, _vecB_high64 ) ); break; } assign(res_32, binop( Iop_Add32, binop( Iop_Add32, binop ( Iop_Add32, mkexpr(_vecA_32), mkexpr(_vecB_32) ), (i == 0) ? mkU32(0) : mkexpr(carry) ), (i == 0) ? cin : mkU32(0) ) ); if (modulo) { result = newTemp(Ity_V128); assign(result, binop( Iop_OrV128, (i == 0) ? binop( Iop_64HLtoV128, mkU64(0), mkU64(0) ) : mkexpr(tmp_result), binop( Iop_ShlV128, binop( Iop_64HLtoV128, mkU64(0), binop( Iop_32HLto64, mkU32(0), mkexpr(res_32) ) ), mkU8(i * 32) ) ) ); tmp_result = newTemp(Ity_V128); assign(tmp_result, mkexpr(result)); } carry = newTemp(Ity_I32); assign(carry, unop(Iop_1Uto32, binop( Iop_CmpLT32U, mkexpr(res_32), mkexpr(_vecA_32 ) ) ) ); } if (modulo) return result; else return carry; } static Bool dis_av_quad ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vRT_addr = ifieldRegDS(theInstr); UChar vRA_addr = ifieldRegA(theInstr); UChar vRB_addr = ifieldRegB(theInstr); UChar vRC_addr; UInt opc2 = IFIELD( theInstr, 0, 11 ); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); IRTemp vC = IRTemp_INVALID; IRTemp cin = IRTemp_INVALID; assign( vA, getVReg(vRA_addr)); assign( vB, getVReg(vRB_addr)); if (opc1 != 0x4) { vex_printf("dis_av_quad(ppc)(instr)\n"); return False; } switch (opc2) { case 0x140: // vaddcuq DIP("vaddcuq v%d,v%d,v%d\n", vRT_addr, vRA_addr, vRB_addr); putVReg( vRT_addr, unop( Iop_32UtoV128, mkexpr(_get_quad_modulo_or_carry(mkexpr(vA), mkexpr(vB), mkU32(0), False) ) ) ); return True; case 0x100: // vadduqm DIP("vadduqm v%d,v%d,v%d\n", vRT_addr, vRA_addr, vRB_addr); putVReg( vRT_addr, mkexpr(_get_quad_modulo_or_carry(mkexpr(vA), mkexpr(vB), mkU32(0), True) ) ); return True; case 0x540: // vsubcuq DIP("vsubcuq v%d,v%d,v%d\n", vRT_addr, vRA_addr, vRB_addr); putVReg( vRT_addr, unop( Iop_32UtoV128, mkexpr(_get_quad_modulo_or_carry(mkexpr(vA), unop( Iop_NotV128, mkexpr(vB) ), mkU32(1), False) ) ) ); return True; case 0x500: // vsubuqm DIP("vsubuqm v%d,v%d,v%d\n", vRT_addr, vRA_addr, vRB_addr); putVReg( vRT_addr, mkexpr(_get_quad_modulo_or_carry(mkexpr(vA), unop( Iop_NotV128, mkexpr(vB) ), mkU32(1), True) ) ); return True; case 0x054C: // vbpermq { #define BPERMD_IDX_MASK 0x00000000000000FFULL #define BPERMD_BIT_MASK 0x8000000000000000ULL int i; IRExpr * vB_expr = mkexpr(vB); IRExpr * res = binop(Iop_AndV128, mkV128(0), mkV128(0)); DIP("vbpermq v%d,v%d,v%d\n", vRT_addr, vRA_addr, vRB_addr); for (i = 0; i < 16; i++) { IRTemp idx_tmp = newTemp( Ity_V128 ); IRTemp perm_bit = newTemp( Ity_V128 ); IRTemp idx = newTemp( Ity_I8 ); IRTemp idx_LT127 = newTemp( Ity_I1 ); IRTemp idx_LT127_ity128 = newTemp( Ity_V128 ); assign( idx_tmp, binop( Iop_AndV128, binop( Iop_64HLtoV128, mkU64(0), mkU64(BPERMD_IDX_MASK) ), vB_expr ) ); assign( idx_LT127, binop( Iop_CmpEQ32, unop ( Iop_64to32, unop( Iop_V128to64, binop( Iop_ShrV128, mkexpr(idx_tmp), mkU8(7) ) ) ), mkU32(0) ) ); /* Below, we set idx to determine which bit of vA to use for the * perm bit. If idx_LT127 is 0, the perm bit is forced to '0'. */ assign( idx, binop( Iop_And8, unop( Iop_1Sto8, mkexpr(idx_LT127) ), unop( Iop_32to8, unop( Iop_V128to32, mkexpr( idx_tmp ) ) ) ) ); assign( idx_LT127_ity128, binop( Iop_64HLtoV128, mkU64(0), unop( Iop_32Uto64, unop( Iop_1Uto32, mkexpr(idx_LT127 ) ) ) ) ); assign( perm_bit, binop( Iop_AndV128, mkexpr( idx_LT127_ity128 ), binop( Iop_ShrV128, binop( Iop_AndV128, binop (Iop_64HLtoV128, mkU64( BPERMD_BIT_MASK ), mkU64(0)), binop( Iop_ShlV128, mkexpr( vA ), mkexpr( idx ) ) ), mkU8( 127 ) ) ) ); res = binop( Iop_OrV128, res, binop( Iop_ShlV128, mkexpr( perm_bit ), mkU8( i + 64 ) ) ); vB_expr = binop( Iop_ShrV128, vB_expr, mkU8( 8 ) ); } putVReg( vRT_addr, res); return True; #undef BPERMD_IDX_MASK #undef BPERMD_BIT_MASK } default: break; // fall through } opc2 = IFIELD( theInstr, 0, 6 ); vRC_addr = ifieldRegC(theInstr); vC = newTemp(Ity_V128); cin = newTemp(Ity_I32); switch (opc2) { case 0x3D: // vaddecuq assign( vC, getVReg(vRC_addr)); DIP("vaddecuq v%d,v%d,v%d,v%d\n", vRT_addr, vRA_addr, vRB_addr, vRC_addr); assign(cin, binop( Iop_And32, unop( Iop_64to32, unop( Iop_V128to64, mkexpr(vC) ) ), mkU32(1) ) ); putVReg( vRT_addr, unop( Iop_32UtoV128, mkexpr(_get_quad_modulo_or_carry(mkexpr(vA), mkexpr(vB), mkexpr(cin), False) ) ) ); return True; case 0x3C: // vaddeuqm assign( vC, getVReg(vRC_addr)); DIP("vaddeuqm v%d,v%d,v%d,v%d\n", vRT_addr, vRA_addr, vRB_addr, vRC_addr); assign(cin, binop( Iop_And32, unop( Iop_64to32, unop( Iop_V128to64, mkexpr(vC) ) ), mkU32(1) ) ); putVReg( vRT_addr, mkexpr(_get_quad_modulo_or_carry(mkexpr(vA), mkexpr(vB), mkexpr(cin), True) ) ); return True; case 0x3F: // vsubecuq assign( vC, getVReg(vRC_addr)); DIP("vsubecuq v%d,v%d,v%d,v%d\n", vRT_addr, vRA_addr, vRB_addr, vRC_addr); assign(cin, binop( Iop_And32, unop( Iop_64to32, unop( Iop_V128to64, mkexpr(vC) ) ), mkU32(1) ) ); putVReg( vRT_addr, unop( Iop_32UtoV128, mkexpr(_get_quad_modulo_or_carry(mkexpr(vA), unop( Iop_NotV128, mkexpr(vB) ), mkexpr(cin), False) ) ) ); return True; case 0x3E: // vsubeuqm assign( vC, getVReg(vRC_addr)); DIP("vsubeuqm v%d,v%d,v%d,v%d\n", vRT_addr, vRA_addr, vRB_addr, vRC_addr); assign(cin, binop( Iop_And32, unop( Iop_64to32, unop( Iop_V128to64, mkexpr(vC) ) ), mkU32(1) ) ); putVReg( vRT_addr, mkexpr(_get_quad_modulo_or_carry(mkexpr(vA), unop( Iop_NotV128, mkexpr(vB) ), mkexpr(cin), True) ) ); return True; default: vex_printf("dis_av_quad(ppc)(opc2.2)\n"); return False; } return True; } /* AltiVec BCD Arithmetic instructions. These instructions modify CR6 for various conditions in the result, including when an overflow occurs. We could easily detect all conditions except when an overflow occurs. But since we can't be 100% accurate in our emulation of CR6, it seems best to just not support it all. */ static Bool dis_av_bcd ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vRT_addr = ifieldRegDS(theInstr); UChar vRA_addr = ifieldRegA(theInstr); UChar vRB_addr = ifieldRegB(theInstr); UChar ps = IFIELD( theInstr, 9, 1 ); UInt opc2 = IFIELD( theInstr, 0, 9 ); IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); IRTemp dst = newTemp(Ity_V128); assign( vA, getVReg(vRA_addr)); assign( vB, getVReg(vRB_addr)); if (opc1 != 0x4) { vex_printf("dis_av_bcd(ppc)(instr)\n"); return False; } switch (opc2) { case 0x1: // bcdadd DIP("bcdadd. v%d,v%d,v%d,%u\n", vRT_addr, vRA_addr, vRB_addr, ps); assign( dst, triop( Iop_BCDAdd, mkexpr( vA ), mkexpr( vB ), mkU8( ps ) ) ); putVReg( vRT_addr, mkexpr(dst)); return True; case 0x41: // bcdsub DIP("bcdsub. v%d,v%d,v%d,%u\n", vRT_addr, vRA_addr, vRB_addr, ps); assign( dst, triop( Iop_BCDSub, mkexpr( vA ), mkexpr( vB ), mkU8( ps ) ) ); putVReg( vRT_addr, mkexpr(dst)); return True; default: vex_printf("dis_av_bcd(ppc)(opc2)\n"); return False; } return True; } /* AltiVec Floating Point Arithmetic Instructions */ static Bool dis_av_fp_arith ( UInt theInstr ) { /* VA-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UChar vC_addr = ifieldRegC(theInstr); UInt opc2=0; IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); IRTemp vC = newTemp(Ity_V128); assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); assign( vC, getVReg(vC_addr)); if (opc1 != 0x4) { vex_printf("dis_av_fp_arith(ppc)(instr)\n"); return False; } IRTemp rm = newTemp(Ity_I32); assign(rm, get_IR_roundingmode()); opc2 = IFIELD( theInstr, 0, 6 ); switch (opc2) { case 0x2E: // vmaddfp (Multiply Add FP, AV p177) DIP("vmaddfp v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vC_addr, vB_addr); putVReg( vD_addr, triop(Iop_Add32Fx4, mkU32(Irrm_NEAREST), mkexpr(vB), triop(Iop_Mul32Fx4, mkU32(Irrm_NEAREST), mkexpr(vA), mkexpr(vC))) ); return True; case 0x2F: { // vnmsubfp (Negative Multiply-Subtract FP, AV p215) DIP("vnmsubfp v%d,v%d,v%d,v%d\n", vD_addr, vA_addr, vC_addr, vB_addr); putVReg( vD_addr, triop(Iop_Sub32Fx4, mkU32(Irrm_NEAREST), mkexpr(vB), triop(Iop_Mul32Fx4, mkU32(Irrm_NEAREST), mkexpr(vA), mkexpr(vC))) ); return True; } default: break; // Fall through... } opc2 = IFIELD( theInstr, 0, 11 ); switch (opc2) { case 0x00A: // vaddfp (Add FP, AV p137) DIP("vaddfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, triop(Iop_Add32Fx4, mkU32(Irrm_NEAREST), mkexpr(vA), mkexpr(vB)) ); return True; case 0x04A: // vsubfp (Subtract FP, AV p261) DIP("vsubfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, triop(Iop_Sub32Fx4, mkU32(Irrm_NEAREST), mkexpr(vA), mkexpr(vB)) ); return True; case 0x40A: // vmaxfp (Maximum FP, AV p178) DIP("vmaxfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Max32Fx4, mkexpr(vA), mkexpr(vB)) ); return True; case 0x44A: // vminfp (Minimum FP, AV p187) DIP("vminfp v%d,v%d,v%d\n", vD_addr, vA_addr, vB_addr); putVReg( vD_addr, binop(Iop_Min32Fx4, mkexpr(vA), mkexpr(vB)) ); return True; default: break; // Fall through... } if (vA_addr != 0) { vex_printf("dis_av_fp_arith(ppc)(vA_addr)\n"); return False; } switch (opc2) { case 0x10A: // vrefp (Reciprocal Esimate FP, AV p228) DIP("vrefp v%d,v%d\n", vD_addr, vB_addr); putVReg( vD_addr, unop(Iop_RecipEst32Fx4, mkexpr(vB)) ); return True; case 0x14A: // vrsqrtefp (Reciprocal Sqrt Estimate FP, AV p237) DIP("vrsqrtefp v%d,v%d\n", vD_addr, vB_addr); putVReg( vD_addr, unop(Iop_RSqrtEst32Fx4, mkexpr(vB)) ); return True; case 0x18A: // vexptefp (2 Raised to the Exp Est FP, AV p173) DIP("vexptefp v%d,v%d\n", vD_addr, vB_addr); DIP(" => not implemented\n"); return False; case 0x1CA: // vlogefp (Log2 Estimate FP, AV p175) DIP("vlogefp v%d,v%d\n", vD_addr, vB_addr); DIP(" => not implemented\n"); return False; default: vex_printf("dis_av_fp_arith(ppc)(opc2=0x%x)\n",opc2); return False; } return True; } /* AltiVec Floating Point Compare Instructions */ static Bool dis_av_fp_cmp ( UInt theInstr ) { /* VXR-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar vA_addr = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UChar flag_rC = ifieldBIT10(theInstr); UInt opc2 = IFIELD( theInstr, 0, 10 ); Bool cmp_bounds = False; IRTemp vA = newTemp(Ity_V128); IRTemp vB = newTemp(Ity_V128); IRTemp vD = newTemp(Ity_V128); assign( vA, getVReg(vA_addr)); assign( vB, getVReg(vB_addr)); if (opc1 != 0x4) { vex_printf("dis_av_fp_cmp(ppc)(instr)\n"); return False; } switch (opc2) { case 0x0C6: // vcmpeqfp (Compare Equal-to FP, AV p159) DIP("vcmpeqfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpEQ32Fx4, mkexpr(vA), mkexpr(vB)) ); break; case 0x1C6: // vcmpgefp (Compare Greater-than-or-Equal-to, AV p163) DIP("vcmpgefp%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGE32Fx4, mkexpr(vA), mkexpr(vB)) ); break; case 0x2C6: // vcmpgtfp (Compare Greater-than FP, AV p164) DIP("vcmpgtfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); assign( vD, binop(Iop_CmpGT32Fx4, mkexpr(vA), mkexpr(vB)) ); break; case 0x3C6: { // vcmpbfp (Compare Bounds FP, AV p157) IRTemp gt = newTemp(Ity_V128); IRTemp lt = newTemp(Ity_V128); IRTemp zeros = newTemp(Ity_V128); DIP("vcmpbfp%s v%d,v%d,v%d\n", (flag_rC ? ".":""), vD_addr, vA_addr, vB_addr); cmp_bounds = True; assign( zeros, unop(Iop_Dup32x4, mkU32(0)) ); /* Note: making use of fact that the ppc backend for compare insns return zero'd lanes if either of the corresponding arg lanes is a nan. Perhaps better to have an irop Iop_isNan32Fx4, but then we'd need this for the other compares too (vcmpeqfp etc)... Better still, tighten down the spec for compare irops. */ assign( gt, unop(Iop_NotV128, binop(Iop_CmpLE32Fx4, mkexpr(vA), mkexpr(vB))) ); assign( lt, unop(Iop_NotV128, binop(Iop_CmpGE32Fx4, mkexpr(vA), triop(Iop_Sub32Fx4, mkU32(Irrm_NEAREST), mkexpr(zeros), mkexpr(vB)))) ); // finally, just shift gt,lt to correct position assign( vD, binop(Iop_ShlN32x4, binop(Iop_OrV128, binop(Iop_AndV128, mkexpr(gt), unop(Iop_Dup32x4, mkU32(0x2))), binop(Iop_AndV128, mkexpr(lt), unop(Iop_Dup32x4, mkU32(0x1)))), mkU8(30)) ); break; } default: vex_printf("dis_av_fp_cmp(ppc)(opc2)\n"); return False; } putVReg( vD_addr, mkexpr(vD) ); if (flag_rC) { set_AV_CR6( mkexpr(vD), !cmp_bounds ); } return True; } /* AltiVec Floating Point Convert/Round Instructions */ static Bool dis_av_fp_convert ( UInt theInstr ) { /* VX-Form */ UChar opc1 = ifieldOPC(theInstr); UChar vD_addr = ifieldRegDS(theInstr); UChar UIMM_5 = ifieldRegA(theInstr); UChar vB_addr = ifieldRegB(theInstr); UInt opc2 = IFIELD( theInstr, 0, 11 ); IRTemp vB = newTemp(Ity_V128); IRTemp vScale = newTemp(Ity_V128); IRTemp vInvScale = newTemp(Ity_V128); float scale, inv_scale; assign( vB, getVReg(vB_addr)); /* scale = 2^UIMM, cast to float, reinterpreted as uint */ scale = (float)( (unsigned int) 1<<UIMM_5 ); assign( vScale, unop(Iop_Dup32x4, mkU32( float_to_bits(scale) )) ); inv_scale = 1/scale; assign( vInvScale, unop(Iop_Dup32x4, mkU32( float_to_bits(inv_scale) )) ); if (opc1 != 0x4) { vex_printf("dis_av_fp_convert(ppc)(instr)\n"); return False; } switch (opc2) { case 0x30A: // vcfux (Convert from Unsigned Fixed-Point W, AV p156) DIP("vcfux v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5); putVReg( vD_addr, triop(Iop_Mul32Fx4, mkU32(Irrm_NEAREST), unop(Iop_I32UtoFx4, mkexpr(vB)), mkexpr(vInvScale)) ); return True; case 0x34A: // vcfsx (Convert from Signed Fixed-Point W, AV p155) DIP("vcfsx v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5); putVReg( vD_addr, triop(Iop_Mul32Fx4, mkU32(Irrm_NEAREST), unop(Iop_I32StoFx4, mkexpr(vB)), mkexpr(vInvScale)) ); return True; case 0x38A: // vctuxs (Convert to Unsigned Fixed-Point W Saturate, AV p172) DIP("vctuxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5); putVReg( vD_addr, unop(Iop_QFtoI32Ux4_RZ, triop(Iop_Mul32Fx4, mkU32(Irrm_NEAREST), mkexpr(vB), mkexpr(vScale))) ); return True; case 0x3CA: // vctsxs (Convert to Signed Fixed-Point W Saturate, AV p171) DIP("vctsxs v%d,v%d,%d\n", vD_addr, vB_addr, UIMM_5); putVReg( vD_addr, unop(Iop_QFtoI32Sx4_RZ, triop(Iop_Mul32Fx4, mkU32(Irrm_NEAREST), mkexpr(vB), mkexpr(vScale))) ); return True; default: break; // Fall through... } if (UIMM_5 != 0) { vex_printf("dis_av_fp_convert(ppc)(UIMM_5)\n"); return False; } switch (opc2) { case 0x20A: // vrfin (Round to FP Integer Nearest, AV p231) DIP("vrfin v%d,v%d\n", vD_addr, vB_addr); putVReg( vD_addr, unop(Iop_RoundF32x4_RN, mkexpr(vB)) ); break; case 0x24A: // vrfiz (Round to FP Integer toward zero, AV p233) DIP("vrfiz v%d,v%d\n", vD_addr, vB_addr); putVReg( vD_addr, unop(Iop_RoundF32x4_RZ, mkexpr(vB)) ); break; case 0x28A: // vrfip (Round to FP Integer toward +inf, AV p232) DIP("vrfip v%d,v%d\n", vD_addr, vB_addr); putVReg( vD_addr, unop(Iop_RoundF32x4_RP, mkexpr(vB)) ); break; case 0x2CA: // vrfim (Round to FP Integer toward -inf, AV p230) DIP("vrfim v%d,v%d\n", vD_addr, vB_addr); putVReg( vD_addr, unop(Iop_RoundF32x4_RM, mkexpr(vB)) ); break; default: vex_printf("dis_av_fp_convert(ppc)(opc2)\n"); return False; } return True; } static Bool dis_transactional_memory ( UInt theInstr, UInt nextInstr, const VexAbiInfo* vbi, /*OUT*/DisResult* dres, Bool (*resteerOkFn)(void*,Addr), void* callback_opaque ) { UInt opc2 = IFIELD( theInstr, 1, 10 ); switch (opc2) { case 0x28E: { //tbegin. /* The current implementation is to just fail the tbegin and execute * the failure path. The failure path is assumed to be functionaly * equivalent to the transactional path with the needed data locking * to ensure correctness. The tend is just a noop and shouldn't * actually get executed. * 1) set cr0 to 0x2 * 2) Initialize TFHAR to CIA+4 * 3) Initialize TEXASR * 4) Initialize TFIAR (probably to CIA, ie, the address of tbegin.) * 5) Continue executing at the next instruction. */ UInt R = IFIELD( theInstr, 21, 1 ); ULong tm_reason; UInt failure_code = 0; /* Forcing failure, will not be due to tabort * or treclaim. */ UInt persistant = 1; /* set persistant since we are always failing * the tbegin. */ UInt nest_overflow = 1; /* Alowed nesting depth overflow, we use this as the reason for failing the trasaction */ UInt tm_exact = 1; /* have exact address for failure */ DIP("tbegin. %u\n", R); /* Set the CR0 field to indicate the tbegin failed. Then let * the code do the branch to the failure path. * * 000 || 0 Transaction initiation successful, * unnested (Transaction state of * Non-transactional prior to tbegin.) * 010 || 0 Transaction initiation successful, nested * (Transaction state of Transactional * prior to tbegin.) * 001 || 0 Transaction initiation unsuccessful, * (Transaction state of Suspended prior * to tbegin.) */ putCR321( 0, mkU8( 0x2 ) ); tm_reason = generate_TMreason( failure_code, persistant, nest_overflow, tm_exact ); storeTMfailure( guest_CIA_curr_instr, tm_reason, guest_CIA_curr_instr+4 ); return True; break; } case 0x2AE: { //tend. /* The tend. is just a noop. Do nothing */ UInt A = IFIELD( theInstr, 25, 1 ); DIP("tend. %u\n", A); break; } case 0x2EE: { //tsr. /* The tsr. is just a noop. Do nothing */ UInt L = IFIELD( theInstr, 21, 1 ); DIP("tsr. %u\n", L); break; } case 0x2CE: { //tcheck. /* The tcheck. is just a noop. Do nothing */ UInt BF = IFIELD( theInstr, 25, 1 ); DIP("tcheck. %u\n", BF); break; } case 0x30E: { //tbortwc. /* The tabortwc. is just a noop. Do nothing */ UInt TO = IFIELD( theInstr, 25, 1 ); UInt RA = IFIELD( theInstr, 16, 5 ); UInt RB = IFIELD( theInstr, 11, 5 ); DIP("tabortwc. %u,%u,%u\n", TO, RA, RB); break; } case 0x32E: { //tbortdc. /* The tabortdc. is just a noop. Do nothing */ UInt TO = IFIELD( theInstr, 25, 1 ); UInt RA = IFIELD( theInstr, 16, 5 ); UInt RB = IFIELD( theInstr, 11, 5 ); DIP("tabortdc. %u,%u,%u\n", TO, RA, RB); break; } case 0x34E: { //tbortwci. /* The tabortwci. is just a noop. Do nothing */ UInt TO = IFIELD( theInstr, 25, 1 ); UInt RA = IFIELD( theInstr, 16, 5 ); UInt SI = IFIELD( theInstr, 11, 5 ); DIP("tabortwci. %u,%u,%u\n", TO, RA, SI); break; } case 0x36E: { //tbortdci. /* The tabortdci. is just a noop. Do nothing */ UInt TO = IFIELD( theInstr, 25, 1 ); UInt RA = IFIELD( theInstr, 16, 5 ); UInt SI = IFIELD( theInstr, 11, 5 ); DIP("tabortdci. %u,%u,%u\n", TO, RA, SI); break; } case 0x38E: { //tbort. /* The tabort. is just a noop. Do nothing */ UInt RA = IFIELD( theInstr, 16, 5 ); DIP("tabort. %u\n", RA); break; } case 0x3AE: { //treclaim. /* The treclaim. is just a noop. Do nothing */ UInt RA = IFIELD( theInstr, 16, 5 ); DIP("treclaim. %u\n", RA); break; } case 0x3EE: { //trechkpt. /* The trechkpt. is just a noop. Do nothing */ DIP("trechkpt.\n"); break; } default: vex_printf("dis_transactional_memory(ppc): unrecognized instruction\n"); return False; } return True; } /* The 0x3C primary opcode (VSX category) uses several different forms of * extended opcodes: * o XX2-form: * - [10:2] (IBM notation [21:29]) * o XX3-form variants: * - variant 1: [10:3] (IBM notation [21:28]) * - variant 2: [9:3] (IBM notation [22:28]) * - variant 3: [7:3] (IBM notation [24:28]) * o XX-4 form: * - [10:6] (IBM notation [21:25]) * * The XX2-form needs bit 0 masked from the standard extended opcode * as returned by ifieldOPClo10; the XX3-form needs bits 0 and 1 masked; * and the XX4-form needs bits 0, 1, and 2 masked. Additionally, the * XX4 and XX3 (variants 2 and 3) forms need certain bits masked on the * front end since their encoding does not begin at bit 21 like the standard * format. * * The get_VSX60_opc2() function uses the vsx_insn array below to obtain the * secondary opcode for such VSX instructions. * */ struct vsx_insn { UInt opcode; const HChar * name; }; // ATTENTION: Keep this array sorted on the opcocde!!! static struct vsx_insn vsx_all[] = { { 0x0, "xsaddsp" }, { 0x4, "xsmaddasp" }, { 0x8, "xxsldwi" }, { 0x14, "xsrsqrtesp" }, { 0x16, "xssqrtsp" }, { 0x18, "xxsel" }, { 0x20, "xssubsp" }, { 0x24, "xsmaddmsp" }, { 0x28, "xxpermdi" }, { 0x34, "xsresp" }, { 0x40, "xsmulsp" }, { 0x44, "xsmsubasp" }, { 0x48, "xxmrghw" }, { 0x60, "xsdivsp" }, { 0x64, "xsmsubmsp" }, { 0x80, "xsadddp" }, { 0x84, "xsmaddadp" }, { 0x8c, "xscmpudp" }, { 0x90, "xscvdpuxws" }, { 0x92, "xsrdpi" }, { 0x94, "xsrsqrtedp" }, { 0x96, "xssqrtdp" }, { 0xa0, "xssubdp" }, { 0xa4, "xsmaddmdp" }, { 0xac, "xscmpodp" }, { 0xb0, "xscvdpsxws" }, { 0xb2, "xsrdpiz" }, { 0xb4, "xsredp" }, { 0xc0, "xsmuldp" }, { 0xc4, "xsmsubadp" }, { 0xc8, "xxmrglw" }, { 0xd2, "xsrdpip" }, { 0xd4, "xstsqrtdp" }, { 0xd6, "xsrdpic" }, { 0xe0, "xsdivdp" }, { 0xe4, "xsmsubmdp" }, { 0xf2, "xsrdpim" }, { 0xf4, "xstdivdp" }, { 0x100, "xvaddsp" }, { 0x104, "xvmaddasp" }, { 0x10c, "xvcmpeqsp" }, { 0x110, "xvcvspuxws" }, { 0x112, "xvrspi" }, { 0x114, "xvrsqrtesp" }, { 0x116, "xvsqrtsp" }, { 0x120, "xvsubsp" }, { 0x124, "xvmaddmsp" }, { 0x12c, "xvcmpgtsp" }, { 0x130, "xvcvspsxws" }, { 0x132, "xvrspiz" }, { 0x134, "xvresp" }, { 0x140, "xvmulsp" }, { 0x144, "xvmsubasp" }, { 0x148, "xxspltw" }, { 0x14c, "xvcmpgesp" }, { 0x150, "xvcvuxwsp" }, { 0x152, "xvrspip" }, { 0x154, "xvtsqrtsp" }, { 0x156, "xvrspic" }, { 0x160, "xvdivsp" }, { 0x164, "xvmsubmsp" }, { 0x170, "xvcvsxwsp" }, { 0x172, "xvrspim" }, { 0x174, "xvtdivsp" }, { 0x180, "xvadddp" }, { 0x184, "xvmaddadp" }, { 0x18c, "xvcmpeqdp" }, { 0x190, "xvcvdpuxws" }, { 0x192, "xvrdpi" }, { 0x194, "xvrsqrtedp" }, { 0x196, "xvsqrtdp" }, { 0x1a0, "xvsubdp" }, { 0x1a4, "xvmaddmdp" }, { 0x1ac, "xvcmpgtdp" }, { 0x1b0, "xvcvdpsxws" }, { 0x1b2, "xvrdpiz" }, { 0x1b4, "xvredp" }, { 0x1c0, "xvmuldp" }, { 0x1c4, "xvmsubadp" }, { 0x1cc, "xvcmpgedp" }, { 0x1d0, "xvcvuxwdp" }, { 0x1d2, "xvrdpip" }, { 0x1d4, "xvtsqrtdp" }, { 0x1d6, "xvrdpic" }, { 0x1e0, "xvdivdp" }, { 0x1e4, "xvmsubmdp" }, { 0x1f0, "xvcvsxwdp" }, { 0x1f2, "xvrdpim" }, { 0x1f4, "xvtdivdp" }, { 0x204, "xsnmaddasp" }, { 0x208, "xxland" }, { 0x212, "xscvdpsp" }, { 0x216, "xscvdpspn" }, { 0x224, "xsnmaddmsp" }, { 0x228, "xxlandc" }, { 0x232, "xxrsp" }, { 0x244, "xsnmsubasp" }, { 0x248, "xxlor" }, { 0x250, "xscvuxdsp" }, { 0x264, "xsnmsubmsp" }, { 0x268, "xxlxor" }, { 0x270, "xscvsxdsp" }, { 0x280, "xsmaxdp" }, { 0x284, "xsnmaddadp" }, { 0x288, "xxlnor" }, { 0x290, "xscvdpuxds" }, { 0x292, "xscvspdp" }, { 0x296, "xscvspdpn" }, { 0x2a0, "xsmindp" }, { 0x2a4, "xsnmaddmdp" }, { 0x2a8, "xxlorc" }, { 0x2b0, "xscvdpsxds" }, { 0x2b2, "xsabsdp" }, { 0x2c0, "xscpsgndp" }, { 0x2c4, "xsnmsubadp" }, { 0x2c8, "xxlnand" }, { 0x2d0, "xscvuxddp" }, { 0x2d2, "xsnabsdp" }, { 0x2e4, "xsnmsubmdp" }, { 0x2e8, "xxleqv" }, { 0x2f0, "xscvsxddp" }, { 0x2f2, "xsnegdp" }, { 0x300, "xvmaxsp" }, { 0x304, "xvnmaddasp" }, { 0x30c, "xvcmpeqsp." }, { 0x310, "xvcvspuxds" }, { 0x312, "xvcvdpsp" }, { 0x320, "xvminsp" }, { 0x324, "xvnmaddmsp" }, { 0x32c, "xvcmpgtsp." }, { 0x330, "xvcvspsxds" }, { 0x332, "xvabssp" }, { 0x340, "xvcpsgnsp" }, { 0x344, "xvnmsubasp" }, { 0x34c, "xvcmpgesp." }, { 0x350, "xvcvuxdsp" }, { 0x352, "xvnabssp" }, { 0x364, "xvnmsubmsp" }, { 0x370, "xvcvsxdsp" }, { 0x372, "xvnegsp" }, { 0x380, "xvmaxdp" }, { 0x384, "xvnmaddadp" }, { 0x38c, "xvcmpeqdp." }, { 0x390, "xvcvdpuxds" }, { 0x392, "xvcvspdp" }, { 0x3a0, "xvmindp" }, { 0x3a4, "xvnmaddmdp" }, { 0x3ac, "xvcmpgtdp." }, { 0x3b0, "xvcvdpsxds" }, { 0x3b2, "xvabsdp" }, { 0x3c0, "xvcpsgndp" }, { 0x3c4, "xvnmsubadp" }, { 0x3cc, "xvcmpgedp." }, { 0x3d0, "xvcvuxddp" }, { 0x3d2, "xvnabsdp" }, { 0x3e4, "xvnmsubmdp" }, { 0x3f0, "xvcvsxddp" }, { 0x3f2, "xvnegdp" } }; #define VSX_ALL_LEN (sizeof vsx_all / sizeof *vsx_all) // ATTENTION: This search function assumes vsx_all array is sorted. static Int findVSXextOpCode(UInt opcode) { Int low, mid, high; low = 0; high = VSX_ALL_LEN - 1; while (low <= high) { mid = (low + high)/2; if (opcode < vsx_all[mid].opcode) high = mid - 1; else if (opcode > vsx_all[mid].opcode) low = mid + 1; else return mid; } return -1; } /* The full 10-bit extended opcode retrieved via ifieldOPClo10 is * passed, and we then try to match it up with one of the VSX forms * below. */ static UInt get_VSX60_opc2(UInt opc2_full) { #define XX2_MASK 0x000003FE #define XX3_1_MASK 0x000003FC #define XX3_2_MASK 0x000001FC #define XX3_3_MASK 0x0000007C #define XX4_MASK 0x00000018 Int ret; UInt vsxExtOpcode = 0; if (( ret = findVSXextOpCode(opc2_full & XX2_MASK)) >= 0) vsxExtOpcode = vsx_all[ret].opcode; else if (( ret = findVSXextOpCode(opc2_full & XX3_1_MASK)) >= 0) vsxExtOpcode = vsx_all[ret].opcode; else if (( ret = findVSXextOpCode(opc2_full & XX3_2_MASK)) >= 0) vsxExtOpcode = vsx_all[ret].opcode; else if (( ret = findVSXextOpCode(opc2_full & XX3_3_MASK)) >= 0) vsxExtOpcode = vsx_all[ret].opcode; else if (( ret = findVSXextOpCode(opc2_full & XX4_MASK)) >= 0) vsxExtOpcode = vsx_all[ret].opcode; return vsxExtOpcode; } /*------------------------------------------------------------*/ /*--- Disassemble a single instruction ---*/ /*------------------------------------------------------------*/ /* Disassemble a single instruction into IR. The instruction is located in host memory at &guest_code[delta]. */ static DisResult disInstr_PPC_WRK ( Bool (*resteerOkFn) ( /*opaque*/void*, Addr ), Bool resteerCisOk, void* callback_opaque, Long delta64, const VexArchInfo* archinfo, const VexAbiInfo* abiinfo, Bool sigill_diag ) { UChar opc1; UInt opc2; DisResult dres; UInt theInstr; IRType ty = mode64 ? Ity_I64 : Ity_I32; Bool allow_F = False; Bool allow_V = False; Bool allow_FX = False; Bool allow_GX = False; Bool allow_VX = False; // Equates to "supports Power ISA 2.06 Bool allow_DFP = False; Bool allow_isa_2_07 = False; UInt hwcaps = archinfo->hwcaps; Long delta; /* What insn variants are we supporting today? */ if (mode64) { allow_F = True; allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC64_V)); allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC64_FX)); allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC64_GX)); allow_VX = (0 != (hwcaps & VEX_HWCAPS_PPC64_VX)); allow_DFP = (0 != (hwcaps & VEX_HWCAPS_PPC64_DFP)); allow_isa_2_07 = (0 != (hwcaps & VEX_HWCAPS_PPC64_ISA2_07)); } else { allow_F = (0 != (hwcaps & VEX_HWCAPS_PPC32_F)); allow_V = (0 != (hwcaps & VEX_HWCAPS_PPC32_V)); allow_FX = (0 != (hwcaps & VEX_HWCAPS_PPC32_FX)); allow_GX = (0 != (hwcaps & VEX_HWCAPS_PPC32_GX)); allow_VX = (0 != (hwcaps & VEX_HWCAPS_PPC32_VX)); allow_DFP = (0 != (hwcaps & VEX_HWCAPS_PPC32_DFP)); allow_isa_2_07 = (0 != (hwcaps & VEX_HWCAPS_PPC32_ISA2_07)); } /* The running delta */ delta = (Long)mkSzAddr(ty, (ULong)delta64); /* Set result defaults. */ dres.whatNext = Dis_Continue; dres.len = 0; dres.continueAt = 0; dres.jk_StopHere = Ijk_INVALID; /* At least this is simple on PPC32: insns are all 4 bytes long, and 4-aligned. So just fish the whole thing out of memory right now and have done. */ theInstr = getUIntPPCendianly( &guest_code[delta] ); if (0) vex_printf("insn: 0x%x\n", theInstr); DIP("\t0x%llx: ", (ULong)guest_CIA_curr_instr); /* Spot "Special" instructions (see comment at top of file). */ { const UChar* code = guest_code + delta; /* Spot the 16-byte preamble: 32-bit mode: 5400183E rlwinm 0,0,3,0,31 5400683E rlwinm 0,0,13,0,31 5400E83E rlwinm 0,0,29,0,31 5400983E rlwinm 0,0,19,0,31 64-bit mode: 78001800 rotldi 0,0,3 78006800 rotldi 0,0,13 7800E802 rotldi 0,0,61 78009802 rotldi 0,0,51 */ UInt word1 = mode64 ? 0x78001800 : 0x5400183E; UInt word2 = mode64 ? 0x78006800 : 0x5400683E; UInt word3 = mode64 ? 0x7800E802 : 0x5400E83E; UInt word4 = mode64 ? 0x78009802 : 0x5400983E; Bool is_special_preamble = False; if (getUIntPPCendianly(code+ 0) == word1 && getUIntPPCendianly(code+ 4) == word2 && getUIntPPCendianly(code+ 8) == word3 && getUIntPPCendianly(code+12) == word4) { is_special_preamble = True; } else if (! mode64 && getUIntPPCendianly(code+ 0) == 0x54001800 && getUIntPPCendianly(code+ 4) == 0x54006800 && getUIntPPCendianly(code+ 8) == 0x5400E800 && getUIntPPCendianly(code+12) == 0x54009800) { static Bool reported = False; if (!reported) { vex_printf("disInstr(ppc): old ppc32 instruction magic detected. Code might clobber r0.\n"); vex_printf("disInstr(ppc): source needs to be recompiled against latest valgrind.h.\n"); reported = True; } is_special_preamble = True; } if (is_special_preamble) { /* Got a "Special" instruction preamble. Which one is it? */ if (getUIntPPCendianly(code+16) == 0x7C210B78 /* or 1,1,1 */) { /* %R3 = client_request ( %R4 ) */ DIP("r3 = client_request ( %%r4 )\n"); delta += 20; putGST( PPC_GST_CIA, mkSzImm( ty, guest_CIA_bbstart + delta )); dres.jk_StopHere = Ijk_ClientReq; dres.whatNext = Dis_StopHere; goto decode_success; } else if (getUIntPPCendianly(code+16) == 0x7C421378 /* or 2,2,2 */) { /* %R3 = guest_NRADDR */ DIP("r3 = guest_NRADDR\n"); delta += 20; dres.len = 20; putIReg(3, IRExpr_Get( OFFB_NRADDR, ty )); goto decode_success; } else if (getUIntPPCendianly(code+16) == 0x7C631B78 /* or 3,3,3 */) { delta += 20; if (host_endness == VexEndnessLE) { /* branch-and-link-to-noredir %R12 */ DIP("branch-and-link-to-noredir r12\n"); putGST( PPC_GST_LR, mkSzImm(ty, guest_CIA_bbstart + (Long)delta) ); putGST( PPC_GST_CIA, getIReg(12)); } else { /* branch-and-link-to-noredir %R11 */ DIP("branch-and-link-to-noredir r11\n"); putGST( PPC_GST_LR, mkSzImm(ty, guest_CIA_bbstart + (Long)delta) ); putGST( PPC_GST_CIA, getIReg(11)); } dres.jk_StopHere = Ijk_NoRedir; dres.whatNext = Dis_StopHere; goto decode_success; } else if (getUIntPPCendianly(code+16) == 0x7C842378 /* or 4,4,4 */) { /* %R3 = guest_NRADDR_GPR2 */ DIP("r3 = guest_NRADDR_GPR2\n"); delta += 20; dres.len = 20; putIReg(3, IRExpr_Get( OFFB_NRADDR_GPR2, ty )); goto decode_success; } else if (getUIntPPCendianly(code+16) == 0x7CA52B78 /* or 5,5,5 */) { DIP("IR injection\n"); if (host_endness == VexEndnessBE) vex_inject_ir(irsb, Iend_BE); else vex_inject_ir(irsb, Iend_LE); delta += 20; dres.len = 20; // Invalidate the current insn. The reason is that the IRop we're // injecting here can change. In which case the translation has to // be redone. For ease of handling, we simply invalidate all the // time. stmt(IRStmt_Put(OFFB_CMSTART, mkSzImm(ty, guest_CIA_curr_instr))); stmt(IRStmt_Put(OFFB_CMLEN, mkSzImm(ty, 20))); putGST( PPC_GST_CIA, mkSzImm( ty, guest_CIA_bbstart + delta )); dres.whatNext = Dis_StopHere; dres.jk_StopHere = Ijk_InvalICache; goto decode_success; } /* We don't know what it is. Set opc1/opc2 so decode_failure can print the insn following the Special-insn preamble. */ theInstr = getUIntPPCendianly(code+16); opc1 = ifieldOPC(theInstr); opc2 = ifieldOPClo10(theInstr); goto decode_failure; /*NOTREACHED*/ } } opc1 = ifieldOPC(theInstr); opc2 = ifieldOPClo10(theInstr); // Note: all 'reserved' bits must be cleared, else invalid switch (opc1) { /* Integer Arithmetic Instructions */ case 0x0C: case 0x0D: case 0x0E: // addic, addic., addi case 0x0F: case 0x07: case 0x08: // addis, mulli, subfic if (dis_int_arith( theInstr )) goto decode_success; goto decode_failure; /* Integer Compare Instructions */ case 0x0B: case 0x0A: // cmpi, cmpli if (dis_int_cmp( theInstr )) goto decode_success; goto decode_failure; /* Integer Logical Instructions */ case 0x1C: case 0x1D: case 0x18: // andi., andis., ori case 0x19: case 0x1A: case 0x1B: // oris, xori, xoris if (dis_int_logic( theInstr )) goto decode_success; goto decode_failure; /* Integer Rotate Instructions */ case 0x14: case 0x15: case 0x17: // rlwimi, rlwinm, rlwnm if (dis_int_rot( theInstr )) goto decode_success; goto decode_failure; /* 64bit Integer Rotate Instructions */ case 0x1E: // rldcl, rldcr, rldic, rldicl, rldicr, rldimi if (!mode64) goto decode_failure; if (dis_int_rot( theInstr )) goto decode_success; goto decode_failure; /* Integer Load Instructions */ case 0x22: case 0x23: case 0x2A: // lbz, lbzu, lha case 0x2B: case 0x28: case 0x29: // lhau, lhz, lhzu case 0x20: case 0x21: // lwz, lwzu if (dis_int_load( theInstr )) goto decode_success; goto decode_failure; /* Integer Store Instructions */ case 0x26: case 0x27: case 0x2C: // stb, stbu, sth case 0x2D: case 0x24: case 0x25: // sthu, stw, stwu if (dis_int_store( theInstr, abiinfo )) goto decode_success; goto decode_failure; /* Integer Load and Store Multiple Instructions */ case 0x2E: case 0x2F: // lmw, stmw if (dis_int_ldst_mult( theInstr )) goto decode_success; goto decode_failure; /* Branch Instructions */ case 0x12: case 0x10: // b, bc if (dis_branch(theInstr, abiinfo, &dres, resteerOkFn, callback_opaque)) goto decode_success; goto decode_failure; /* System Linkage Instructions */ case 0x11: // sc if (dis_syslink(theInstr, abiinfo, &dres)) goto decode_success; goto decode_failure; /* Trap Instructions */ case 0x02: // tdi if (!mode64) goto decode_failure; if (dis_trapi(theInstr, &dres)) goto decode_success; goto decode_failure; case 0x03: // twi if (dis_trapi(theInstr, &dres)) goto decode_success; goto decode_failure; /* Floating Point Load Instructions */ case 0x30: case 0x31: case 0x32: // lfs, lfsu, lfd case 0x33: // lfdu if (!allow_F) goto decode_noF; if (dis_fp_load( theInstr )) goto decode_success; goto decode_failure; /* Floating Point Store Instructions */ case 0x34: case 0x35: case 0x36: // stfsx, stfsux, stfdx case 0x37: // stfdux if (!allow_F) goto decode_noF; if (dis_fp_store( theInstr )) goto decode_success; goto decode_failure; /* Floating Point Load Double Pair Instructions */ case 0x39: case 0x3D: if (!allow_F) goto decode_noF; if (dis_fp_pair( theInstr )) goto decode_success; goto decode_failure; /* 128-bit Integer Load */ case 0x38: // lq if (dis_int_load( theInstr )) goto decode_success; goto decode_failure; /* 64bit Integer Loads */ case 0x3A: // ld, ldu, lwa if (!mode64) goto decode_failure; if (dis_int_load( theInstr )) goto decode_success; goto decode_failure; case 0x3B: if (!allow_F) goto decode_noF; opc2 = ifieldOPClo10(theInstr); switch (opc2) { case 0x2: // dadd - DFP Add case 0x202: // dsub - DFP Subtract case 0x22: // dmul - DFP Mult case 0x222: // ddiv - DFP Divide if (!allow_DFP) goto decode_noDFP; if (dis_dfp_arith( theInstr )) goto decode_success; case 0x82: // dcmpo, DFP comparison ordered instruction case 0x282: // dcmpu, DFP comparison unordered instruction if (!allow_DFP) goto decode_noDFP; if (dis_dfp_compare( theInstr ) ) goto decode_success; goto decode_failure; case 0x102: // dctdp - DFP convert to DFP long case 0x302: // drsp - DFP round to dfp short case 0x122: // dctfix - DFP convert to fixed if (!allow_DFP) goto decode_noDFP; if (dis_dfp_fmt_conv( theInstr )) goto decode_success; goto decode_failure; case 0x322: // POWER 7 inst, dcffix - DFP convert from fixed if (!allow_VX) goto decode_failure; if (!allow_DFP) goto decode_noDFP; if (dis_dfp_fmt_conv( theInstr )) goto decode_success; goto decode_failure; case 0x2A2: // dtstsf - DFP number of significant digits if (!allow_DFP) goto decode_noDFP; if (dis_dfp_significant_digits(theInstr)) goto decode_success; goto decode_failure; case 0x142: // ddedpd DFP Decode DPD to BCD case 0x342: // denbcd DFP Encode BCD to DPD if (!allow_DFP) goto decode_noDFP; if (dis_dfp_bcd(theInstr)) goto decode_success; goto decode_failure; case 0x162: // dxex - Extract exponent case 0x362: // diex - Insert exponent if (!allow_DFP) goto decode_noDFP; if (dis_dfp_extract_insert( theInstr ) ) goto decode_success; goto decode_failure; case 0x3CE: // fcfidus (implemented as native insn) if (!allow_VX) goto decode_noVX; if (dis_fp_round( theInstr )) goto decode_success; goto decode_failure; case 0x34E: // fcfids if (dis_fp_round( theInstr )) goto decode_success; goto decode_failure; } opc2 = ifieldOPClo9( theInstr ); switch (opc2) { case 0x42: // dscli, DFP shift left case 0x62: // dscri, DFP shift right if (!allow_DFP) goto decode_noDFP; if (dis_dfp_shift( theInstr )) goto decode_success; goto decode_failure; case 0xc2: // dtstdc, DFP test data class case 0xe2: // dtstdg, DFP test data group if (!allow_DFP) goto decode_noDFP; if (dis_dfp_class_test( theInstr )) goto decode_success; goto decode_failure; } opc2 = ifieldOPClo8( theInstr ); switch (opc2) { case 0x3: // dqua - DFP Quantize case 0x23: // drrnd - DFP Reround case 0x43: // dquai - DFP Quantize immediate if (!allow_DFP) goto decode_noDFP; if (dis_dfp_quantize_sig_rrnd( theInstr ) ) goto decode_success; goto decode_failure; case 0xA2: // dtstex - DFP Test exponent if (!allow_DFP) goto decode_noDFP; if (dis_dfp_exponent_test( theInstr ) ) goto decode_success; goto decode_failure; case 0x63: // drintx - Round to an integer value case 0xE3: // drintn - Round to an integer value if (!allow_DFP) goto decode_noDFP; if (dis_dfp_round( theInstr ) ) { goto decode_success; } goto decode_failure; default: break; /* fall through to next opc2 check */ } opc2 = IFIELD(theInstr, 1, 5); switch (opc2) { /* Floating Point Arith Instructions */ case 0x12: case 0x14: case 0x15: // fdivs, fsubs, fadds case 0x19: // fmuls if (dis_fp_arith(theInstr)) goto decode_success; goto decode_failure; case 0x16: // fsqrts if (!allow_FX) goto decode_noFX; if (dis_fp_arith(theInstr)) goto decode_success; goto decode_failure; case 0x18: // fres if (!allow_GX) goto decode_noGX; if (dis_fp_arith(theInstr)) goto decode_success; goto decode_failure; /* Floating Point Mult-Add Instructions */ case 0x1C: case 0x1D: case 0x1E: // fmsubs, fmadds, fnmsubs case 0x1F: // fnmadds if (dis_fp_multadd(theInstr)) goto decode_success; goto decode_failure; case 0x1A: // frsqrtes if (!allow_GX) goto decode_noGX; if (dis_fp_arith(theInstr)) goto decode_success; goto decode_failure; default: goto decode_failure; } break; case 0x3C: // VSX instructions (except load/store) { // All of these VSX instructions use some VMX facilities, so // if allow_V is not set, we'll skip trying to decode. if (!allow_V) goto decode_noVX; UInt vsxOpc2 = get_VSX60_opc2(opc2); /* The vsxOpc2 returned is the "normalized" value, representing the * instructions secondary opcode as taken from the standard secondary * opcode field [21:30] (IBM notatition), even if the actual field * is non-standard. These normalized values are given in the opcode * appendices of the ISA 2.06 document. */ switch (vsxOpc2) { case 0x8: case 0x28: case 0x48: case 0xc8: // xxsldwi, xxpermdi, xxmrghw, xxmrglw case 0x018: case 0x148: // xxsel, xxspltw if (dis_vx_permute_misc(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x268: case 0x248: case 0x288: // xxlxor, xxlor, xxlnor, case 0x208: case 0x228: case 0x2A8: // xxland, xxlandc, xxlorc case 0x2C8: case 0x2E8: // xxlnand, xxleqv if (dis_vx_logic(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x2B2: case 0x2C0: // xsabsdp, xscpsgndp case 0x2D2: case 0x2F2: // xsnabsdp, xsnegdp case 0x280: case 0x2A0: // xsmaxdp, xsmindp case 0x0F2: case 0x0D2: // xsrdpim, xsrdpip case 0x034: case 0x014: // xsresp, xsrsqrtesp case 0x0B4: case 0x094: // xsredp, xsrsqrtedp case 0x0D6: case 0x0B2: // xsrdpic, xsrdpiz case 0x092: case 0x232: // xsrdpi, xsrsp if (dis_vxs_misc(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x08C: case 0x0AC: // xscmpudp, xscmpodp if (dis_vx_cmp(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x0: case 0x020: // xsaddsp, xssubsp case 0x080: // xsadddp case 0x060: case 0x0E0: // xsdivsp, xsdivdp case 0x004: case 0x024: // xsmaddasp, xsmaddmsp case 0x084: case 0x0A4: // xsmaddadp, xsmaddmdp case 0x044: case 0x064: // xsmsubasp, xsmsubmsp case 0x0C4: case 0x0E4: // xsmsubadp, xsmsubmdp case 0x204: case 0x224: // xsnmaddasp, xsnmaddmsp case 0x284: case 0x2A4: // xsnmaddadp, xsnmaddmdp case 0x244: case 0x264: // xsnmsubasp, xsnmsubmsp case 0x2C4: case 0x2E4: // xsnmsubadp, xsnmsubmdp case 0x040: case 0x0C0: // xsmulsp, xsmuldp case 0x0A0: // xssubdp case 0x016: case 0x096: // xssqrtsp,xssqrtdp case 0x0F4: case 0x0D4: // xstdivdp, xstsqrtdp if (dis_vxs_arith(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x180: // xvadddp case 0x1E0: // xvdivdp case 0x1C0: // xvmuldp case 0x1A0: // xvsubdp case 0x184: case 0x1A4: // xvmaddadp, xvmaddmdp case 0x1C4: case 0x1E4: // xvmsubadp, xvmsubmdp case 0x384: case 0x3A4: // xvnmaddadp, xvnmaddmdp case 0x3C4: case 0x3E4: // xvnmsubadp, xvnmsubmdp case 0x1D4: case 0x1F4: // xvtsqrtdp, xvtdivdp case 0x196: // xvsqrtdp if (dis_vxv_dp_arith(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x100: // xvaddsp case 0x160: // xvdivsp case 0x140: // xvmulsp case 0x120: // xvsubsp case 0x104: case 0x124: // xvmaddasp, xvmaddmsp case 0x144: case 0x164: // xvmsubasp, xvmsubmsp case 0x304: case 0x324: // xvnmaddasp, xvnmaddmsp case 0x344: case 0x364: // xvnmsubasp, xvnmsubmsp case 0x154: case 0x174: // xvtsqrtsp, xvtdivsp case 0x116: // xvsqrtsp if (dis_vxv_sp_arith(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x250: // xscvuxdsp case 0x2D0: case 0x3d0: // xscvuxddp, xvcvuxddp case 0x350: case 0x1d0: // xvcvuxdsp, xvcvuxwdp case 0x090: // xscvdpuxws // The above VSX conversion instructions employ some ISA 2.06 // floating point conversion instructions under the covers, // so if allow_VX (which means "supports ISA 2.06") is not set, // we'll skip the decode. if (!allow_VX) goto decode_noVX; if (dis_vx_conv(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x2B0: // xscvdpsxds case 0x270: case 0x2F0: // xscvsxdsp, xscvsxddp case 0x1b0: case 0x130: // xvcvdpsxws, xvcvspsxws case 0x0b0: case 0x290: // xscvdpsxws, xscvdpuxds case 0x212: case 0x216: // xscvdpsp, xscvdpspn case 0x292: case 0x296: // xscvspdp, xscvspdpn case 0x312: // xvcvdpsp case 0x390: case 0x190: // xvcvdpuxds, xvcvdpuxws case 0x3B0: case 0x310: // xvcvdpsxds, xvcvspuxds case 0x392: case 0x330: // xvcvspdp, xvcvspsxds case 0x110: case 0x3f0: // xvcvspuxws, xvcvsxddp case 0x370: case 0x1f0: // xvcvsxdsp, xvcvsxwdp case 0x170: case 0x150: // xvcvsxwsp, xvcvuxwsp if (dis_vx_conv(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x18C: case 0x38C: // xvcmpeqdp[.] case 0x10C: case 0x30C: // xvcmpeqsp[.] case 0x14C: case 0x34C: // xvcmpgesp[.] case 0x12C: case 0x32C: // xvcmpgtsp[.] case 0x1CC: case 0x3CC: // xvcmpgedp[.] case 0x1AC: case 0x3AC: // xvcmpgtdp[.] if (dis_vvec_cmp(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; case 0x134: // xvresp case 0x1B4: // xvredp case 0x194: case 0x114: // xvrsqrtedp, xvrsqrtesp case 0x380: case 0x3A0: // xvmaxdp, xvmindp case 0x300: case 0x320: // xvmaxsp, xvminsp case 0x3C0: case 0x340: // xvcpsgndp, xvcpsgnsp case 0x3B2: case 0x332: // xvabsdp, xvabssp case 0x3D2: case 0x352: // xvnabsdp, xvnabssp case 0x192: case 0x1D6: // xvrdpi, xvrdpic case 0x1F2: case 0x1D2: // xvrdpim, xvrdpip case 0x1B2: case 0x3F2: // xvrdpiz, xvnegdp case 0x112: case 0x156: // xvrspi, xvrspic case 0x172: case 0x152: // xvrspim, xvrspip case 0x132: // xvrspiz if (dis_vxv_misc(theInstr, vsxOpc2)) goto decode_success; goto decode_failure; default: goto decode_failure; } break; } /* 64bit Integer Stores */ case 0x3E: // std, stdu, stq if (dis_int_store( theInstr, abiinfo )) goto decode_success; goto decode_failure; case 0x3F: if (!allow_F) goto decode_noF; /* Instrs using opc[1:5] never overlap instrs using opc[1:10], so we can simply fall through the first switch statement */ opc2 = IFIELD(theInstr, 1, 5); switch (opc2) { /* Floating Point Arith Instructions */ case 0x12: case 0x14: case 0x15: // fdiv, fsub, fadd case 0x19: // fmul if (dis_fp_arith(theInstr)) goto decode_success; goto decode_failure; case 0x16: // fsqrt if (!allow_FX) goto decode_noFX; if (dis_fp_arith(theInstr)) goto decode_success; goto decode_failure; case 0x17: case 0x1A: // fsel, frsqrte if (!allow_GX) goto decode_noGX; if (dis_fp_arith(theInstr)) goto decode_success; goto decode_failure; /* Floating Point Mult-Add Instructions */ case 0x1C: case 0x1D: case 0x1E: // fmsub, fmadd, fnmsub case 0x1F: // fnmadd if (dis_fp_multadd(theInstr)) goto decode_success; goto decode_failure; case 0x18: // fre if (!allow_GX) goto decode_noGX; if (dis_fp_arith(theInstr)) goto decode_success; goto decode_failure; default: break; // Fall through } opc2 = IFIELD(theInstr, 1, 10); switch (opc2) { /* 128-bit DFP instructions */ case 0x2: // daddq - DFP Add case 0x202: // dsubq - DFP Subtract case 0x22: // dmulq - DFP Mult case 0x222: // ddivq - DFP Divide if (!allow_DFP) goto decode_noDFP; if (dis_dfp_arithq( theInstr )) goto decode_success; goto decode_failure; case 0x162: // dxexq - DFP Extract exponent case 0x362: // diexq - DFP Insert exponent if (!allow_DFP) goto decode_noDFP; if (dis_dfp_extract_insertq( theInstr )) goto decode_success; goto decode_failure; case 0x82: // dcmpoq, DFP comparison ordered instruction case 0x282: // dcmpuq, DFP comparison unordered instruction if (!allow_DFP) goto decode_noDFP; if (dis_dfp_compare( theInstr ) ) goto decode_success; goto decode_failure; case 0x102: // dctqpq - DFP convert to DFP extended case 0x302: // drdpq - DFP round to dfp Long case 0x122: // dctfixq - DFP convert to fixed quad case 0x322: // dcffixq - DFP convert from fixed quad if (!allow_DFP) goto decode_noDFP; if (dis_dfp_fmt_convq( theInstr )) goto decode_success; goto decode_failure; case 0x2A2: // dtstsfq - DFP number of significant digits if (!allow_DFP) goto decode_noDFP; if (dis_dfp_significant_digits(theInstr)) goto decode_success; goto decode_failure; case 0x142: // ddedpdq DFP Decode DPD to BCD case 0x342: // denbcdq DFP Encode BCD to DPD if (!allow_DFP) goto decode_noDFP; if (dis_dfp_bcdq(theInstr)) goto decode_success; goto decode_failure; /* Floating Point Compare Instructions */ case 0x000: // fcmpu case 0x020: // fcmpo if (dis_fp_cmp(theInstr)) goto decode_success; goto decode_failure; case 0x080: // ftdiv case 0x0A0: // ftsqrt if (dis_fp_tests(theInstr)) goto decode_success; goto decode_failure; /* Floating Point Rounding/Conversion Instructions */ case 0x00C: // frsp case 0x00E: // fctiw case 0x00F: // fctiwz case 0x32E: // fctid case 0x32F: // fctidz case 0x34E: // fcfid if (dis_fp_round(theInstr)) goto decode_success; goto decode_failure; case 0x3CE: case 0x3AE: case 0x3AF: // fcfidu, fctidu[z] (implemented as native insns) case 0x08F: case 0x08E: // fctiwu[z] (implemented as native insns) if (!allow_VX) goto decode_noVX; if (dis_fp_round(theInstr)) goto decode_success; goto decode_failure; /* Power6 rounding stuff */ case 0x1E8: // frim case 0x1C8: // frip case 0x188: // frin case 0x1A8: // friz /* A hack to check for P6 capability . . . */ if ((allow_F && allow_V && allow_FX && allow_GX) && (dis_fp_round(theInstr))) goto decode_success; goto decode_failure; /* Floating Point Move Instructions */ case 0x008: // fcpsgn case 0x028: // fneg case 0x048: // fmr case 0x088: // fnabs case 0x108: // fabs if (dis_fp_move( theInstr )) goto decode_success; goto decode_failure; case 0x3c6: case 0x346: // fmrgew, fmrgow if (dis_fp_merge( theInstr )) goto decode_success; goto decode_failure; /* Floating Point Status/Control Register Instructions */ case 0x026: // mtfsb1 case 0x040: // mcrfs case 0x046: // mtfsb0 case 0x086: // mtfsfi case 0x247: // mffs case 0x2C7: // mtfsf // Some of the above instructions need to know more about the // ISA level supported by the host. if (dis_fp_scr( theInstr, allow_GX )) goto decode_success; goto decode_failure; default: break; // Fall through... } opc2 = ifieldOPClo9( theInstr ); switch (opc2) { case 0x42: // dscli, DFP shift left case 0x62: // dscri, DFP shift right if (!allow_DFP) goto decode_noDFP; if (dis_dfp_shiftq( theInstr )) goto decode_success; goto decode_failure; case 0xc2: // dtstdc, DFP test data class case 0xe2: // dtstdg, DFP test data group if (!allow_DFP) goto decode_noDFP; if (dis_dfp_class_test( theInstr )) goto decode_success; goto decode_failure; default: break; } opc2 = ifieldOPClo8( theInstr ); switch (opc2) { case 0x3: // dquaq - DFP Quantize Quad case 0x23: // drrndq - DFP Reround Quad case 0x43: // dquaiq - DFP Quantize immediate Quad if (!allow_DFP) goto decode_noDFP; if (dis_dfp_quantize_sig_rrndq( theInstr )) goto decode_success; goto decode_failure; case 0xA2: // dtstexq - DFP Test exponent Quad if (!allow_DFP) goto decode_noDFP; if (dis_dfp_exponent_test( theInstr ) ) goto decode_success; goto decode_failure; case 0x63: // drintxq - DFP Round to an integer value case 0xE3: // drintnq - DFP Round to an integer value if (!allow_DFP) goto decode_noDFP; if (dis_dfp_roundq( theInstr )) goto decode_success; goto decode_failure; default: goto decode_failure; } break; case 0x13: switch (opc2) { /* Condition Register Logical Instructions */ case 0x101: case 0x081: case 0x121: // crand, crandc, creqv case 0x0E1: case 0x021: case 0x1C1: // crnand, crnor, cror case 0x1A1: case 0x0C1: case 0x000: // crorc, crxor, mcrf if (dis_cond_logic( theInstr )) goto decode_success; goto decode_failure; /* Branch Instructions */ case 0x210: case 0x010: // bcctr, bclr if (dis_branch(theInstr, abiinfo, &dres, resteerOkFn, callback_opaque)) goto decode_success; goto decode_failure; /* Memory Synchronization Instructions */ case 0x096: // isync if (dis_memsync( theInstr )) goto decode_success; goto decode_failure; default: goto decode_failure; } break; case 0x1F: /* For arith instns, bit10 is the OE flag (overflow enable) */ opc2 = IFIELD(theInstr, 1, 9); switch (opc2) { /* Integer Arithmetic Instructions */ case 0x10A: case 0x00A: case 0x08A: // add, addc, adde case 0x0EA: case 0x0CA: case 0x1EB: // addme, addze, divw case 0x1CB: case 0x04B: case 0x00B: // divwu, mulhw, mulhwu case 0x0EB: case 0x068: case 0x028: // mullw, neg, subf case 0x008: case 0x088: case 0x0E8: // subfc, subfe, subfme case 0x0C8: // subfze if (dis_int_arith( theInstr )) goto decode_success; goto decode_failure; case 0x18B: // divweu (implemented as native insn) case 0x1AB: // divwe (implemented as native insn) if (!allow_VX) goto decode_noVX; if (dis_int_arith( theInstr )) goto decode_success; goto decode_failure; /* 64bit Integer Arithmetic */ case 0x009: case 0x049: case 0x0E9: // mulhdu, mulhd, mulld case 0x1C9: case 0x1E9: // divdu, divd if (!mode64) goto decode_failure; if (dis_int_arith( theInstr )) goto decode_success; goto decode_failure; case 0x1A9: // divde (implemented as native insn) case 0x189: // divdeuo (implemented as native insn) if (!allow_VX) goto decode_noVX; if (!mode64) goto decode_failure; if (dis_int_arith( theInstr )) goto decode_success; goto decode_failure; case 0x1FC: // cmpb if (dis_int_logic( theInstr )) goto decode_success; goto decode_failure; default: break; // Fall through... } /* All remaining opcodes use full 10 bits. */ opc2 = IFIELD(theInstr, 1, 10); switch (opc2) { /* Integer Compare Instructions */ case 0x000: case 0x020: // cmp, cmpl if (dis_int_cmp( theInstr )) goto decode_success; goto decode_failure; /* Integer Logical Instructions */ case 0x01C: case 0x03C: case 0x01A: // and, andc, cntlzw case 0x11C: case 0x3BA: case 0x39A: // eqv, extsb, extsh case 0x1DC: case 0x07C: case 0x1BC: // nand, nor, or case 0x19C: case 0x13C: // orc, xor case 0x2DF: case 0x25F: // mftgpr, mffgpr if (dis_int_logic( theInstr )) goto decode_success; goto decode_failure; case 0x28E: case 0x2AE: // tbegin., tend. case 0x2EE: case 0x2CE: case 0x30E: // tsr., tcheck., tabortwc. case 0x32E: case 0x34E: case 0x36E: // tabortdc., tabortwci., tabortdci. case 0x38E: case 0x3AE: case 0x3EE: // tabort., treclaim., trechkpt. if (dis_transactional_memory( theInstr, getUIntPPCendianly( &guest_code[delta + 4]), abiinfo, &dres, resteerOkFn, callback_opaque)) goto decode_success; goto decode_failure; /* 64bit Integer Logical Instructions */ case 0x3DA: case 0x03A: // extsw, cntlzd if (!mode64) goto decode_failure; if (dis_int_logic( theInstr )) goto decode_success; goto decode_failure; /* 64bit Integer Parity Instructions */ case 0xba: // prtyd if (!mode64) goto decode_failure; if (dis_int_parity( theInstr )) goto decode_success; goto decode_failure; case 0x9a: // prtyw if (dis_int_parity( theInstr )) goto decode_success; goto decode_failure; /* Integer Shift Instructions */ case 0x018: case 0x318: case 0x338: // slw, sraw, srawi case 0x218: // srw if (dis_int_shift( theInstr )) goto decode_success; goto decode_failure; /* 64bit Integer Shift Instructions */ case 0x01B: case 0x31A: // sld, srad case 0x33A: case 0x33B: // sradi case 0x21B: // srd if (!mode64) goto decode_failure; if (dis_int_shift( theInstr )) goto decode_success; goto decode_failure; /* Integer Load Instructions */ case 0x057: case 0x077: case 0x157: // lbzx, lbzux, lhax case 0x177: case 0x117: case 0x137: // lhaux, lhzx, lhzux case 0x017: case 0x037: // lwzx, lwzux if (dis_int_load( theInstr )) goto decode_success; goto decode_failure; /* 64bit Integer Load Instructions */ case 0x035: case 0x015: // ldux, ldx case 0x175: case 0x155: // lwaux, lwax if (!mode64) goto decode_failure; if (dis_int_load( theInstr )) goto decode_success; goto decode_failure; /* Integer Store Instructions */ case 0x0F7: case 0x0D7: case 0x1B7: // stbux, stbx, sthux case 0x197: case 0x0B7: case 0x097: // sthx, stwux, stwx if (dis_int_store( theInstr, abiinfo )) goto decode_success; goto decode_failure; /* 64bit Integer Store Instructions */ case 0x0B5: case 0x095: // stdux, stdx if (!mode64) goto decode_failure; if (dis_int_store( theInstr, abiinfo )) goto decode_success; goto decode_failure; /* Integer Load and Store with Byte Reverse Instructions */ case 0x214: case 0x294: // ldbrx, stdbrx if (!mode64) goto decode_failure; if (dis_int_ldst_rev( theInstr )) goto decode_success; goto decode_failure; case 0x216: case 0x316: case 0x296: // lwbrx, lhbrx, stwbrx case 0x396: // sthbrx if (dis_int_ldst_rev( theInstr )) goto decode_success; goto decode_failure; /* Integer Load and Store String Instructions */ case 0x255: case 0x215: case 0x2D5: // lswi, lswx, stswi case 0x295: { // stswx Bool stopHere = False; Bool ok = dis_int_ldst_str( theInstr, &stopHere ); if (!ok) goto decode_failure; if (stopHere) { putGST( PPC_GST_CIA, mkSzImm(ty, nextInsnAddr()) ); dres.jk_StopHere = Ijk_Boring; dres.whatNext = Dis_StopHere; } goto decode_success; } /* Memory Synchronization Instructions */ case 0x034: case 0x074: // lbarx, lharx case 0x2B6: case 0x2D6: // stbcx, sthcx if (!allow_isa_2_07) goto decode_noP8; if (dis_memsync( theInstr )) goto decode_success; goto decode_failure; case 0x356: case 0x014: case 0x096: // eieio, lwarx, stwcx. case 0x256: // sync if (dis_memsync( theInstr )) goto decode_success; goto decode_failure; /* 64bit Memory Synchronization Instructions */ case 0x054: case 0x0D6: // ldarx, stdcx. if (!mode64) goto decode_failure; if (dis_memsync( theInstr )) goto decode_success; goto decode_failure; case 0x114: case 0x0B6: // lqarx, stqcx. if (dis_memsync( theInstr )) goto decode_success; goto decode_failure; /* Processor Control Instructions */ case 0x33: case 0x73: // mfvsrd, mfvsrwz case 0xB3: case 0xD3: case 0xF3: // mtvsrd, mtvsrwa, mtvsrwz case 0x200: case 0x013: case 0x153: // mcrxr, mfcr, mfspr case 0x173: case 0x090: case 0x1D3: // mftb, mtcrf, mtspr case 0x220: // mcrxrt if (dis_proc_ctl( abiinfo, theInstr )) goto decode_success; goto decode_failure; /* Cache Management Instructions */ case 0x2F6: case 0x056: case 0x036: // dcba, dcbf, dcbst case 0x116: case 0x0F6: case 0x3F6: // dcbt, dcbtst, dcbz case 0x3D6: // icbi if (dis_cache_manage( theInstr, &dres, archinfo )) goto decode_success; goto decode_failure; //zz /* External Control Instructions */ //zz case 0x136: case 0x1B6: // eciwx, ecowx //zz DIP("external control op => not implemented\n"); //zz goto decode_failure; /* Trap Instructions */ case 0x004: // tw if (dis_trap(theInstr, &dres)) goto decode_success; goto decode_failure; case 0x044: // td if (!mode64) goto decode_failure; if (dis_trap(theInstr, &dres)) goto decode_success; goto decode_failure; /* Floating Point Load Instructions */ case 0x217: case 0x237: case 0x257: // lfsx, lfsux, lfdx case 0x277: // lfdux if (!allow_F) goto decode_noF; if (dis_fp_load( theInstr )) goto decode_success; goto decode_failure; /* Floating Point Store Instructions */ case 0x297: case 0x2B7: case 0x2D7: // stfs, stfsu, stfd case 0x2F7: // stfdu, stfiwx if (!allow_F) goto decode_noF; if (dis_fp_store( theInstr )) goto decode_success; goto decode_failure; case 0x3D7: // stfiwx if (!allow_F) goto decode_noF; if (!allow_GX) goto decode_noGX; if (dis_fp_store( theInstr )) goto decode_success; goto decode_failure; /* Floating Point Double Pair Indexed Instructions */ case 0x317: // lfdpx (Power6) case 0x397: // stfdpx (Power6) if (!allow_F) goto decode_noF; if (dis_fp_pair(theInstr)) goto decode_success; goto decode_failure; case 0x357: // lfiwax if (!allow_F) goto decode_noF; if (dis_fp_load( theInstr )) goto decode_success; goto decode_failure; case 0x377: // lfiwzx if (!allow_F) goto decode_noF; if (dis_fp_load( theInstr )) goto decode_success; goto decode_failure; /* AltiVec instructions */ /* AV Cache Control - Data streams */ case 0x156: case 0x176: case 0x336: // dst, dstst, dss if (!allow_V) goto decode_noV; if (dis_av_datastream( theInstr )) goto decode_success; goto decode_failure; /* AV Load */ case 0x006: case 0x026: // lvsl, lvsr case 0x007: case 0x027: case 0x047: // lvebx, lvehx, lvewx case 0x067: case 0x167: // lvx, lvxl if (!allow_V) goto decode_noV; if (dis_av_load( abiinfo, theInstr )) goto decode_success; goto decode_failure; /* AV Store */ case 0x087: case 0x0A7: case 0x0C7: // stvebx, stvehx, stvewx case 0x0E7: case 0x1E7: // stvx, stvxl if (!allow_V) goto decode_noV; if (dis_av_store( theInstr )) goto decode_success; goto decode_failure; /* VSX Load */ case 0x00C: // lxsiwzx case 0x04C: // lxsiwax case 0x20C: // lxsspx case 0x24C: // lxsdx case 0x34C: // lxvd2x case 0x14C: // lxvdsx case 0x30C: // lxvw4x // All of these VSX load instructions use some VMX facilities, so // if allow_V is not set, we'll skip trying to decode. if (!allow_V) goto decode_noV; if (dis_vx_load( theInstr )) goto decode_success; goto decode_failure; /* VSX Store */ case 0x08C: // stxsiwx case 0x28C: // stxsspx case 0x2CC: // stxsdx case 0x3CC: // stxvd2x case 0x38C: // stxvw4x // All of these VSX store instructions use some VMX facilities, so // if allow_V is not set, we'll skip trying to decode. if (!allow_V) goto decode_noV; if (dis_vx_store( theInstr )) goto decode_success; goto decode_failure; /* Miscellaneous ISA 2.06 instructions */ case 0x1FA: // popcntd case 0x17A: // popcntw case 0x7A: // popcntb if (dis_int_logic( theInstr )) goto decode_success; goto decode_failure; case 0x0FC: // bpermd if (!mode64) goto decode_failure; if (dis_int_logic( theInstr )) goto decode_success; goto decode_failure; default: /* Deal with some other cases that we would otherwise have punted on. */ /* --- ISEL (PowerISA_V2.05.pdf, p74) --- */ /* only decode this insn when reserved bit 0 (31 in IBM's notation) is zero */ if (IFIELD(theInstr, 0, 6) == (15<<1)) { UInt rT = ifieldRegDS( theInstr ); UInt rA = ifieldRegA( theInstr ); UInt rB = ifieldRegB( theInstr ); UInt bi = ifieldRegC( theInstr ); putIReg( rT, IRExpr_ITE( binop(Iop_CmpNE32, getCRbit( bi ), mkU32(0)), rA == 0 ? (mode64 ? mkU64(0) : mkU32(0)) : getIReg(rA), getIReg(rB)) ); DIP("isel r%u,r%u,r%u,crb%u\n", rT,rA,rB,bi); goto decode_success; } goto decode_failure; } break; case 0x04: /* AltiVec instructions */ opc2 = IFIELD(theInstr, 0, 6); switch (opc2) { /* AV Mult-Add, Mult-Sum */ case 0x20: case 0x21: case 0x22: // vmhaddshs, vmhraddshs, vmladduhm case 0x24: case 0x25: case 0x26: // vmsumubm, vmsummbm, vmsumuhm case 0x27: case 0x28: case 0x29: // vmsumuhs, vmsumshm, vmsumshs if (!allow_V) goto decode_noV; if (dis_av_multarith( theInstr )) goto decode_success; goto decode_failure; /* AV Permutations */ case 0x2A: // vsel case 0x2B: // vperm case 0x2C: // vsldoi if (!allow_V) goto decode_noV; if (dis_av_permute( theInstr )) goto decode_success; goto decode_failure; case 0x2D: // vpermxor if (!allow_isa_2_07) goto decode_noP8; if (dis_av_permute( theInstr )) goto decode_success; goto decode_failure; /* AV Floating Point Mult-Add/Sub */ case 0x2E: case 0x2F: // vmaddfp, vnmsubfp if (!allow_V) goto decode_noV; if (dis_av_fp_arith( theInstr )) goto decode_success; goto decode_failure; case 0x3D: case 0x3C: // vaddecuq, vaddeuqm case 0x3F: case 0x3E: // vsubecuq, vsubeuqm if (!allow_V) goto decode_noV; if (dis_av_quad( theInstr)) goto decode_success; goto decode_failure; default: break; // Fall through... } opc2 = IFIELD(theInstr, 0, 9); switch (opc2) { /* BCD arithmetic */ case 0x1: case 0x41: // bcdadd, bcdsub if (!allow_isa_2_07) goto decode_noP8; if (dis_av_bcd( theInstr )) goto decode_success; goto decode_failure; default: break; // Fall through... } opc2 = IFIELD(theInstr, 0, 11); switch (opc2) { /* AV Arithmetic */ case 0x180: // vaddcuw case 0x000: case 0x040: case 0x080: // vaddubm, vadduhm, vadduwm case 0x200: case 0x240: case 0x280: // vaddubs, vadduhs, vadduws case 0x300: case 0x340: case 0x380: // vaddsbs, vaddshs, vaddsws case 0x580: // vsubcuw case 0x400: case 0x440: case 0x480: // vsububm, vsubuhm, vsubuwm case 0x600: case 0x640: case 0x680: // vsububs, vsubuhs, vsubuws case 0x700: case 0x740: case 0x780: // vsubsbs, vsubshs, vsubsws case 0x402: case 0x442: case 0x482: // vavgub, vavguh, vavguw case 0x502: case 0x542: case 0x582: // vavgsb, vavgsh, vavgsw case 0x002: case 0x042: case 0x082: // vmaxub, vmaxuh, vmaxuw case 0x102: case 0x142: case 0x182: // vmaxsb, vmaxsh, vmaxsw case 0x202: case 0x242: case 0x282: // vminub, vminuh, vminuw case 0x302: case 0x342: case 0x382: // vminsb, vminsh, vminsw case 0x008: case 0x048: // vmuloub, vmulouh case 0x108: case 0x148: // vmulosb, vmulosh case 0x208: case 0x248: // vmuleub, vmuleuh case 0x308: case 0x348: // vmulesb, vmulesh case 0x608: case 0x708: case 0x648: // vsum4ubs, vsum4sbs, vsum4shs case 0x688: case 0x788: // vsum2sws, vsumsws if (!allow_V) goto decode_noV; if (dis_av_arith( theInstr )) goto decode_success; goto decode_failure; case 0x088: case 0x089: // vmulouw, vmuluwm case 0x0C0: case 0x0C2: // vaddudm, vmaxud case 0x1C2: case 0x2C2: case 0x3C2: // vnaxsd, vminud, vminsd case 0x188: case 0x288: case 0x388: // vmulosw, vmuleuw, vmulesw case 0x4C0: // vsubudm if (!allow_isa_2_07) goto decode_noP8; if (dis_av_arith( theInstr )) goto decode_success; goto decode_failure; /* AV Polynomial Vector Multiply Add */ case 0x408: case 0x448: // vpmsumb, vpmsumd case 0x488: case 0x4C8: // vpmsumw, vpmsumh if (!allow_isa_2_07) goto decode_noP8; if (dis_av_polymultarith( theInstr )) goto decode_success; goto decode_failure; /* AV Rotate, Shift */ case 0x004: case 0x044: case 0x084: // vrlb, vrlh, vrlw case 0x104: case 0x144: case 0x184: // vslb, vslh, vslw case 0x204: case 0x244: case 0x284: // vsrb, vsrh, vsrw case 0x304: case 0x344: case 0x384: // vsrab, vsrah, vsraw case 0x1C4: case 0x2C4: // vsl, vsr case 0x40C: case 0x44C: // vslo, vsro if (!allow_V) goto decode_noV; if (dis_av_shift( theInstr )) goto decode_success; goto decode_failure; case 0x0C4: // vrld case 0x3C4: case 0x5C4: case 0x6C4: // vsrad, vsld, vsrd if (!allow_isa_2_07) goto decode_noP8; if (dis_av_shift( theInstr )) goto decode_success; goto decode_failure; /* AV Logic */ case 0x404: case 0x444: case 0x484: // vand, vandc, vor case 0x4C4: case 0x504: // vxor, vnor if (!allow_V) goto decode_noV; if (dis_av_logic( theInstr )) goto decode_success; goto decode_failure; case 0x544: // vorc case 0x584: case 0x684: // vnand, veqv if (!allow_isa_2_07) goto decode_noP8; if (dis_av_logic( theInstr )) goto decode_success; goto decode_failure; /* AV Processor Control */ case 0x604: case 0x644: // mfvscr, mtvscr if (!allow_V) goto decode_noV; if (dis_av_procctl( theInstr )) goto decode_success; goto decode_failure; /* AV Floating Point Arithmetic */ case 0x00A: case 0x04A: // vaddfp, vsubfp case 0x10A: case 0x14A: case 0x18A: // vrefp, vrsqrtefp, vexptefp case 0x1CA: // vlogefp case 0x40A: case 0x44A: // vmaxfp, vminfp if (!allow_V) goto decode_noV; if (dis_av_fp_arith( theInstr )) goto decode_success; goto decode_failure; /* AV Floating Point Round/Convert */ case 0x20A: case 0x24A: case 0x28A: // vrfin, vrfiz, vrfip case 0x2CA: // vrfim case 0x30A: case 0x34A: case 0x38A: // vcfux, vcfsx, vctuxs case 0x3CA: // vctsxs if (!allow_V) goto decode_noV; if (dis_av_fp_convert( theInstr )) goto decode_success; goto decode_failure; /* AV Merge, Splat */ case 0x00C: case 0x04C: case 0x08C: // vmrghb, vmrghh, vmrghw case 0x10C: case 0x14C: case 0x18C: // vmrglb, vmrglh, vmrglw case 0x20C: case 0x24C: case 0x28C: // vspltb, vsplth, vspltw case 0x30C: case 0x34C: case 0x38C: // vspltisb, vspltish, vspltisw if (!allow_V) goto decode_noV; if (dis_av_permute( theInstr )) goto decode_success; goto decode_failure; case 0x68C: case 0x78C: // vmrgow, vmrgew if (!allow_isa_2_07) goto decode_noP8; if (dis_av_permute( theInstr )) goto decode_success; goto decode_failure; /* AV Pack, Unpack */ case 0x00E: case 0x04E: case 0x08E: // vpkuhum, vpkuwum, vpkuhus case 0x0CE: // vpkuwus case 0x10E: case 0x14E: case 0x18E: // vpkshus, vpkswus, vpkshss case 0x1CE: // vpkswss case 0x20E: case 0x24E: case 0x28E: // vupkhsb, vupkhsh, vupklsb case 0x2CE: // vupklsh case 0x30E: case 0x34E: case 0x3CE: // vpkpx, vupkhpx, vupklpx if (!allow_V) goto decode_noV; if (dis_av_pack( theInstr )) goto decode_success; goto decode_failure; case 0x44E: case 0x4CE: case 0x54E: // vpkudum, vpkudus, vpksdus case 0x5CE: case 0x64E: case 0x6cE: // vpksdss, vupkhsw, vupklsw if (!allow_isa_2_07) goto decode_noP8; if (dis_av_pack( theInstr )) goto decode_success; goto decode_failure; case 0x508: case 0x509: // vcipher, vcipherlast case 0x548: case 0x549: // vncipher, vncipherlast case 0x5C8: // vsbox if (!allow_isa_2_07) goto decode_noP8; if (dis_av_cipher( theInstr )) goto decode_success; goto decode_failure; case 0x6C2: case 0x682: // vshasigmaw, vshasigmad if (!allow_isa_2_07) goto decode_noP8; if (dis_av_hash( theInstr )) goto decode_success; goto decode_failure; case 0x702: case 0x742: // vclzb, vclzh case 0x782: case 0x7c2: // vclzw, vclzd if (!allow_isa_2_07) goto decode_noP8; if (dis_av_count_bitTranspose( theInstr, opc2 )) goto decode_success; goto decode_failure; case 0x703: case 0x743: // vpopcntb, vpopcnth case 0x783: case 0x7c3: // vpopcntw, vpopcntd if (!allow_isa_2_07) goto decode_noP8; if (dis_av_count_bitTranspose( theInstr, opc2 )) goto decode_success; goto decode_failure; case 0x50c: // vgbbd if (!allow_isa_2_07) goto decode_noP8; if (dis_av_count_bitTranspose( theInstr, opc2 )) goto decode_success; goto decode_failure; case 0x140: case 0x100: // vaddcuq, vadduqm case 0x540: case 0x500: // vsubcuq, vsubuqm case 0x54C: // vbpermq if (!allow_V) goto decode_noV; if (dis_av_quad( theInstr)) goto decode_success; goto decode_failure; default: break; // Fall through... } opc2 = IFIELD(theInstr, 0, 10); switch (opc2) { /* AV Compare */ case 0x006: case 0x046: case 0x086: // vcmpequb, vcmpequh, vcmpequw case 0x206: case 0x246: case 0x286: // vcmpgtub, vcmpgtuh, vcmpgtuw case 0x306: case 0x346: case 0x386: // vcmpgtsb, vcmpgtsh, vcmpgtsw if (!allow_V) goto decode_noV; if (dis_av_cmp( theInstr )) goto decode_success; goto decode_failure; case 0x0C7: // vcmpequd case 0x2C7: // vcmpgtud case 0x3C7: // vcmpgtsd if (!allow_isa_2_07) goto decode_noP8; if (dis_av_cmp( theInstr )) goto decode_success; goto decode_failure; /* AV Floating Point Compare */ case 0x0C6: case 0x1C6: case 0x2C6: // vcmpeqfp, vcmpgefp, vcmpgtfp case 0x3C6: // vcmpbfp if (!allow_V) goto decode_noV; if (dis_av_fp_cmp( theInstr )) goto decode_success; goto decode_failure; default: goto decode_failure; } break; default: goto decode_failure; decode_noF: vassert(!allow_F); vex_printf("disInstr(ppc): found the Floating Point instruction 0x%x that\n" "can't be handled by Valgrind on this host. This instruction\n" "requires a host that supports Floating Point instructions.\n", theInstr); goto not_supported; decode_noV: vassert(!allow_V); vex_printf("disInstr(ppc): found an AltiVec or an e500 instruction 0x%x\n" "that can't be handled by Valgrind. If this instruction is an\n" "Altivec instruction, Valgrind must be run on a host that supports" "AltiVec instructions. If the application was compiled for e500, then\n" "unfortunately Valgrind does not yet support e500 instructions.\n", theInstr); goto not_supported; decode_noVX: vassert(!allow_VX); vex_printf("disInstr(ppc): found the instruction 0x%x that is defined in the\n" "Power ISA 2.06 ABI but can't be handled by Valgrind on this host.\n" "This instruction \nrequires a host that supports the ISA 2.06 ABI.\n", theInstr); goto not_supported; decode_noFX: vassert(!allow_FX); vex_printf("disInstr(ppc): found the General Purpose-Optional instruction 0x%x\n" "that can't be handled by Valgrind on this host. This instruction\n" "requires a host that supports the General Purpose-Optional instructions.\n", theInstr); goto not_supported; decode_noGX: vassert(!allow_GX); vex_printf("disInstr(ppc): found the Graphics-Optional instruction 0x%x\n" "that can't be handled by Valgrind on this host. This instruction\n" "requires a host that supports the Graphic-Optional instructions.\n", theInstr); goto not_supported; decode_noDFP: vassert(!allow_DFP); vex_printf("disInstr(ppc): found the decimal floating point (DFP) instruction 0x%x\n" "that can't be handled by Valgrind on this host. This instruction\n" "requires a host that supports DFP instructions.\n", theInstr); goto not_supported; decode_noP8: vassert(!allow_isa_2_07); vex_printf("disInstr(ppc): found the Power 8 instruction 0x%x that can't be handled\n" "by Valgrind on this host. This instruction requires a host that\n" "supports Power 8 instructions.\n", theInstr); goto not_supported; decode_failure: /* All decode failures end up here. */ opc2 = (theInstr) & 0x7FF; if (sigill_diag) { vex_printf("disInstr(ppc): unhandled instruction: " "0x%x\n", theInstr); vex_printf(" primary %d(0x%x), secondary %u(0x%x)\n", opc1, opc1, opc2, opc2); } not_supported: /* Tell the dispatcher that this insn cannot be decoded, and so has not been executed, and (is currently) the next to be executed. CIA should be up-to-date since it made so at the start of each insn, but nevertheless be paranoid and update it again right now. */ putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr) ); dres.len = 0; dres.whatNext = Dis_StopHere; dres.jk_StopHere = Ijk_NoDecode; dres.continueAt = 0; return dres; } /* switch (opc) for the main (primary) opcode switch. */ decode_success: /* All decode successes end up here. */ switch (dres.whatNext) { case Dis_Continue: putGST( PPC_GST_CIA, mkSzImm(ty, guest_CIA_curr_instr + 4)); break; case Dis_ResteerU: case Dis_ResteerC: putGST( PPC_GST_CIA, mkSzImm(ty, dres.continueAt)); break; case Dis_StopHere: break; default: vassert(0); } DIP("\n"); if (dres.len == 0) { dres.len = 4; } else { vassert(dres.len == 20); } return dres; } #undef DIP #undef DIS /*------------------------------------------------------------*/ /*--- Top-level fn ---*/ /*------------------------------------------------------------*/ /* Disassemble a single instruction into IR. The instruction is located in host memory at &guest_code[delta]. */ DisResult disInstr_PPC ( IRSB* irsb_IN, Bool (*resteerOkFn) ( void*, Addr ), Bool resteerCisOk, void* callback_opaque, const UChar* guest_code_IN, Long delta, Addr guest_IP, VexArch guest_arch, const VexArchInfo* archinfo, const VexAbiInfo* abiinfo, VexEndness host_endness_IN, Bool sigill_diag_IN ) { IRType ty; DisResult dres; UInt mask32, mask64; UInt hwcaps_guest = archinfo->hwcaps; vassert(guest_arch == VexArchPPC32 || guest_arch == VexArchPPC64); /* global -- ick */ mode64 = guest_arch == VexArchPPC64; ty = mode64 ? Ity_I64 : Ity_I32; if (!mode64 && (host_endness_IN == VexEndnessLE)) { vex_printf("disInstr(ppc): Little Endian 32-bit mode is not supported\n"); dres.len = 0; dres.whatNext = Dis_StopHere; dres.jk_StopHere = Ijk_NoDecode; dres.continueAt = 0; return dres; } /* do some sanity checks */ mask32 = VEX_HWCAPS_PPC32_F | VEX_HWCAPS_PPC32_V | VEX_HWCAPS_PPC32_FX | VEX_HWCAPS_PPC32_GX | VEX_HWCAPS_PPC32_VX | VEX_HWCAPS_PPC32_DFP | VEX_HWCAPS_PPC32_ISA2_07; mask64 = VEX_HWCAPS_PPC64_V | VEX_HWCAPS_PPC64_FX | VEX_HWCAPS_PPC64_GX | VEX_HWCAPS_PPC64_VX | VEX_HWCAPS_PPC64_DFP | VEX_HWCAPS_PPC64_ISA2_07; if (mode64) { vassert((hwcaps_guest & mask32) == 0); } else { vassert((hwcaps_guest & mask64) == 0); } /* Set globals (see top of this file) */ guest_code = guest_code_IN; irsb = irsb_IN; host_endness = host_endness_IN; guest_CIA_curr_instr = mkSzAddr(ty, guest_IP); guest_CIA_bbstart = mkSzAddr(ty, guest_IP - delta); dres = disInstr_PPC_WRK ( resteerOkFn, resteerCisOk, callback_opaque, delta, archinfo, abiinfo, sigill_diag_IN); return dres; } /*------------------------------------------------------------*/ /*--- Unused stuff ---*/ /*------------------------------------------------------------*/ ///* A potentially more memcheck-friendly implementation of Clz32, with // the boundary case Clz32(0) = 32, which is what ppc requires. */ // //static IRExpr* /* :: Ity_I32 */ verbose_Clz32 ( IRTemp arg ) //{ // /* Welcome ... to SSA R Us. */ // IRTemp n1 = newTemp(Ity_I32); // IRTemp n2 = newTemp(Ity_I32); // IRTemp n3 = newTemp(Ity_I32); // IRTemp n4 = newTemp(Ity_I32); // IRTemp n5 = newTemp(Ity_I32); // IRTemp n6 = newTemp(Ity_I32); // IRTemp n7 = newTemp(Ity_I32); // IRTemp n8 = newTemp(Ity_I32); // IRTemp n9 = newTemp(Ity_I32); // IRTemp n10 = newTemp(Ity_I32); // IRTemp n11 = newTemp(Ity_I32); // IRTemp n12 = newTemp(Ity_I32); // // /* First, propagate the most significant 1-bit into all lower // positions in the word. */ // /* unsigned int clz ( unsigned int n ) // { // n |= (n >> 1); // n |= (n >> 2); // n |= (n >> 4); // n |= (n >> 8); // n |= (n >> 16); // return bitcount(~n); // } // */ // assign(n1, mkexpr(arg)); // assign(n2, binop(Iop_Or32, mkexpr(n1), binop(Iop_Shr32, mkexpr(n1), mkU8(1)))); // assign(n3, binop(Iop_Or32, mkexpr(n2), binop(Iop_Shr32, mkexpr(n2), mkU8(2)))); // assign(n4, binop(Iop_Or32, mkexpr(n3), binop(Iop_Shr32, mkexpr(n3), mkU8(4)))); // assign(n5, binop(Iop_Or32, mkexpr(n4), binop(Iop_Shr32, mkexpr(n4), mkU8(8)))); // assign(n6, binop(Iop_Or32, mkexpr(n5), binop(Iop_Shr32, mkexpr(n5), mkU8(16)))); // /* This gives a word of the form 0---01---1. Now invert it, giving // a word of the form 1---10---0, then do a population-count idiom // (to count the 1s, which is the number of leading zeroes, or 32 // if the original word was 0. */ // assign(n7, unop(Iop_Not32, mkexpr(n6))); // // /* unsigned int bitcount ( unsigned int n ) // { // n = n - ((n >> 1) & 0x55555555); // n = (n & 0x33333333) + ((n >> 2) & 0x33333333); // n = (n + (n >> 4)) & 0x0F0F0F0F; // n = n + (n >> 8); // n = (n + (n >> 16)) & 0x3F; // return n; // } // */ // assign(n8, // binop(Iop_Sub32, // mkexpr(n7), // binop(Iop_And32, // binop(Iop_Shr32, mkexpr(n7), mkU8(1)), // mkU32(0x55555555)))); // assign(n9, // binop(Iop_Add32, // binop(Iop_And32, mkexpr(n8), mkU32(0x33333333)), // binop(Iop_And32, // binop(Iop_Shr32, mkexpr(n8), mkU8(2)), // mkU32(0x33333333)))); // assign(n10, // binop(Iop_And32, // binop(Iop_Add32, // mkexpr(n9), // binop(Iop_Shr32, mkexpr(n9), mkU8(4))), // mkU32(0x0F0F0F0F))); // assign(n11, // binop(Iop_Add32, // mkexpr(n10), // binop(Iop_Shr32, mkexpr(n10), mkU8(8)))); // assign(n12, // binop(Iop_Add32, // mkexpr(n11), // binop(Iop_Shr32, mkexpr(n11), mkU8(16)))); // return // binop(Iop_And32, mkexpr(n12), mkU32(0x3F)); //} /*--------------------------------------------------------------------*/ /*--- end guest_ppc_toIR.c ---*/ /*--------------------------------------------------------------------*/