support -- VEX side changes. See #295221.
This patch adds the DFP 64-bit and 128-bit support, support for the
new IEEE rounding modes and the Add, Subtract, Multiply and Divide
instructions for both 64-bit and 128-bit instructions to Valgrind.
Carl Love (carll@us.ibm.com) and Maynard Johnson (maynardj@us.ibm.com)
git-svn-id: svn://svn.valgrind.org/vex/trunk@2270
vex_state->guest_CR7_321 = 0;
vex_state->guest_CR7_0 = 0;
- vex_state->guest_FPROUND = (UInt)PPCrm_NEAREST;
+ vex_state->guest_FPROUND = PPCrm_NEAREST;
+ vex_state->guest_DFPROUND = PPCrm_NEAREST;
vex_state->guest_VRSAVE = 0;
vex_state->guest_CR7_321 = 0;
vex_state->guest_CR7_0 = 0;
- vex_state->guest_FPROUND = (UInt)PPCrm_NEAREST;
+ vex_state->guest_FPROUND = PPCrm_NEAREST;
+ vex_state->guest_DFPROUND = PPCrm_NEAREST;
vex_state->guest_VRSAVE = 0;
#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_EMWARN offsetofPPCGuestState(guest_EMWARN)
PPC_GST_MAX
} PPC_GST;
-#define MASK_FPSCR_RN 0x3
-#define MASK_FPSCR_FPRF 0x1F000
+#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
return mask;
}
-/* ditto for 64bit mask */
static ULong MASK64( UInt begin, UInt end )
{
ULong m1, m2, mask;
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;
+}
+
+/* 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 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);
all exceptions masked, round-to-nearest.
This corresponds to a FPSCR value of 0x0. */
- /* We're only keeping track of the rounding mode,
- so if the mask isn't asking for this, just return 0x0 */
- if (mask & (MASK_FPSCR_RN|MASK_FPSCR_FPRF)) {
- assign( val, IRExpr_Get( OFFB_FPROUND, Ity_I32 ) );
+ /* 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) );
}
}
}
+/* 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. */
}
/* Write masked src to the given reg */
-static void putGST_masked ( PPC_GST reg, IRExpr* src, UInt mask )
+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_I32 );
-
+ vassert( typeOfIRExpr( irsb->tyenv,src ) == Ity_I64 );
+
switch (reg) {
case PPC_GST_FPSCR: {
- /* Allow writes to Rounding Mode */
- if (mask & (MASK_FPSCR_RN|MASK_FPSCR_FPRF)) {
- /* construct new fpround from new and old values as per mask:
- new fpround = (src & (3 & mask)) | (fpround & (3 & ~mask)) */
- stmt(
- IRStmt_Put(
- OFFB_FPROUND,
- binop(
- Iop_Or32,
- binop(Iop_And32, src, mkU32((MASK_FPSCR_RN|MASK_FPSCR_FPRF) & mask)),
- binop(
- Iop_And32,
- IRExpr_Get(OFFB_FPROUND,Ity_I32),
- mkU32((MASK_FPSCR_RN|MASK_FPSCR_FPRF) & ~mask)
- )
- )
- )
- );
+ /* Allow writes to either binary or decimal floating point
+ * Rounding Mode
+ */
+ if (mask & MASK_FPSCR_RN) {
+ stmt( IRStmt_Put( OFFB_FPROUND,
+ unop( Iop_32to8,
+ binop( Iop_And32,
+ unop( Iop_64to32, src ),
+ mkU32( MASK_FPSCR_RN & mask ) ) ) ) );
+ } else if (mask & MASK_FPSCR_DRN) {
+ stmt( IRStmt_Put( OFFB_DFPROUND,
+ unop( Iop_32to8,
+ binop( Iop_And32,
+ unop( Iop_64HIto32, src ),
+ mkU32( ( MASK_FPSCR_DRN & mask )
+ >> 32 ) ) ) ) );
}
/* Give EmWarn for attempted writes to:
REG[FLD] (as per IBM/hardware notation). */
static void putGST_field ( PPC_GST reg, IRExpr* src, UInt fld )
{
- UInt shft, mask;
+ UInt shft;
+ ULong mask;
vassert( typeOfIRExpr(irsb->tyenv,src ) == Ity_I32 );
- vassert( fld < 8 );
+ vassert( fld < 16 );
vassert( reg < PPC_GST_MAX );
- shft = 4*(7-fld);
+ if (fld < 8)
+ shft = 4*(7-fld);
+ else
+ shft = 4*(15-fld);
mask = 0xF<<shft;
switch (reg) {
break;
default:
- if (shft == 0) {
- putGST_masked( reg, src, mask );
- } else {
- putGST_masked( reg,
- binop(Iop_Shl32, src, mkU8(toUChar(shft))),
- mask );
+ {
+ 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 );
+ }
}
}
}
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 ) ) );
+}
/*------------------------------------------------------------*/
/*--- Floating Point Instruction Translation ---*/
/*
Floating Point Status/Control Register Instructions
*/
-static Bool dis_fp_scr ( UInt theInstr )
+static Bool dis_fp_scr ( UInt theInstr, Bool GX_level )
{
/* Many forms - see each switch case */
UChar opc1 = ifieldOPC(theInstr);
return False;
}
DIP("mtfsb1%s crb%d \n", flag_rC ? ".":"", crbD);
- putGST_masked( PPC_GST_FPSCR, mkU32(1<<(31-crbD)), 1<<(31-crbD) );
+ putGST_masked( PPC_GST_FPSCR, mkU64( 1 <<( 31 - crbD ) ),
+ 1ULL << ( 31 - crbD ) );
break;
}
return False;
}
DIP("mtfsb0%s crb%d\n", flag_rC ? ".":"", crbD);
- putGST_masked( PPC_GST_FPSCR, mkU32(0), 1<<(31-crbD) );
+ putGST_masked( PPC_GST_FPSCR, mkU64( 0 ), 1ULL << ( 31 - crbD ) );
break;
}
case 0x086: { // mtfsfi (Move to FPSCR Field Immediate, PPC32 p481)
- UChar crfD = toUChar( IFIELD( theInstr, 23, 3 ) );
+ 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%d,%d\n", flag_rC ? ".":"", crfD, IMM);
- putGST_field( PPC_GST_FPSCR, mkU32(IMM), crfD );
+ 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_all = getGST_masked( PPC_GST_FPSCR, MASK_FPSCR_RN );
+ 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");
DIP("mffs%s fr%u\n", flag_rC ? ".":"", frD_addr);
putFReg( frD_addr,
unop( Iop_ReinterpI64asF64,
- unop( Iop_32Uto64, fpscr_all )));
+ binop( Iop_32HLto64, fpscr_upper, fpscr_lower ) ) );
break;
}
UChar FM = toUChar( IFIELD(theInstr, 17, 8) );
UChar frB_addr = ifieldRegB(theInstr);
IRTemp frB = newTemp(Ity_F64);
- IRTemp rB_32 = newTemp(Ity_I32);
- Int i, mask;
+ 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
mask = 0;
for (i=0; i<8; i++) {
if ((FM & (1<<(7-i))) == 1) {
- mask |= 0xF << (7-i);
+ /* 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_32, unop( Iop_64to32,
- unop( Iop_ReinterpF64asI64, mkexpr(frB) )));
- putGST_masked( PPC_GST_FPSCR, mkexpr(rB_32), mask );
+ assign( rB_64, unop( Iop_ReinterpF64asI64, mkexpr( frB ) ) );
+ putGST_masked( PPC_GST_FPSCR, mkexpr( rB_64 ), mask );
break;
}
return True;
}
+/*------------------------------------------------------------*/
+/*--- 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;
+}
/*------------------------------------------------------------*/
/*--- AltiVec Instruction Translation ---*/
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_GX) goto decode_failure;
+ if (dis_dfp_arith( theInstr ))
+ goto decode_success;
case 0x3CE: // fcfidus (implemented as native insn)
if (!allow_VX)
goto decode_noVX;
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_GX) goto decode_failure;
+ if (dis_dfp_arithq( theInstr ))
+ goto decode_success;
+ goto decode_failure;
+
/* Floating Point Compare Instructions */
case 0x000: // fcmpu
case 0x020: // fcmpo
case 0x086: // mtfsfi
case 0x247: // mffs
case 0x2C7: // mtfsf
- if (dis_fp_scr( theInstr )) goto decode_success;
+ // 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:
case Pfp_FRIN: return "frin";
case Pfp_FRIP: return "frip";
case Pfp_FRIZ: return "friz";
+ case Pfp_DFPADD: return "dadd";
+ case Pfp_DFPADDQ: return "daddq";
+ case Pfp_DFPSUB: return "dsub";
+ case Pfp_DFPSUBQ: return "dsubq";
+ case Pfp_DFPMUL: return "dmul";
+ case Pfp_DFPMULQ: return "dmulq";
+ case Pfp_DFPDIV: return "ddivd";
+ case Pfp_DFPDIVQ: return "ddivq";
default: vpanic("showPPCFpOp");
}
}
i->Pin.FpRSP.src = src;
return i;
}
+PPCInstr* PPCInstr_Dfp64Unary(PPCFpOp op, HReg dst, HReg src) {
+ PPCInstr* i = LibVEX_Alloc( sizeof(PPCInstr) );
+ i->tag = Pin_Dfp64Unary;
+ i->Pin.Dfp64Unary.op = op;
+ i->Pin.Dfp64Unary.dst = dst;
+ i->Pin.Dfp64Unary.src = src;
+ return i;
+}
+PPCInstr* PPCInstr_Dfp64Binary(PPCFpOp op, HReg dst, HReg srcL, HReg srcR) {
+ PPCInstr* i = LibVEX_Alloc( sizeof(PPCInstr) );
+ i->tag = Pin_Dfp64Binary;
+ i->Pin.Dfp64Binary.op = op;
+ i->Pin.Dfp64Binary.dst = dst;
+ i->Pin.Dfp64Binary.srcL = srcL;
+ i->Pin.Dfp64Binary.srcR = srcR;
+ return i;
+}
+PPCInstr* PPCInstr_Dfp128Binary(PPCFpOp op, HReg dst_hi, HReg dst_lo,
+ HReg srcR_hi, HReg srcR_lo) {
+ /* dst is used to pass the srcL argument and return the result */
+ PPCInstr* i = LibVEX_Alloc( sizeof(PPCInstr) );
+ i->tag = Pin_Dfp128Binary;
+ i->Pin.Dfp128Binary.op = op;
+ i->Pin.Dfp128Binary.dst_hi = dst_hi;
+ i->Pin.Dfp128Binary.dst_lo = dst_lo;
+ i->Pin.Dfp128Binary.srcR_hi = srcR_hi;
+ i->Pin.Dfp128Binary.srcR_lo = srcR_lo;
+ return i;
+}
/*
Valid combo | fromI | int32 | syned | flt64 |
vassert(cond.test != Pct_ALWAYS);
return i;
}
-PPCInstr* PPCInstr_FpLdFPSCR ( HReg src ) {
+PPCInstr* PPCInstr_FpLdFPSCR ( HReg src, Bool dfp_rm ) {
PPCInstr* i = LibVEX_Alloc(sizeof(PPCInstr));
i->tag = Pin_FpLdFPSCR;
i->Pin.FpLdFPSCR.src = src;
+ i->Pin.FpLdFPSCR.dfp_rm = dfp_rm ? 1 : 0;
return i;
}
PPCInstr* PPCInstr_FpCmp ( HReg dst, HReg srcL, HReg srcR ) {
case Pin_FpLdFPSCR:
vex_printf("mtfsf 0xFF,");
ppHRegPPC(i->Pin.FpLdFPSCR.src);
+ vex_printf(",0, %s", i->Pin.FpLdFPSCR.dfp_rm ? "1" : "0");
return;
case Pin_FpCmp:
vex_printf("fcmpo %%cr1,");
ppHRegPPC(i->Pin.AvLdVSCR.src);
return;
+ case Pin_Dfp64Unary:
+ vex_printf("%s ", showPPCFpOp(i->Pin.Dfp64Unary.op));
+ ppHRegPPC(i->Pin.Dfp64Unary.dst);
+ vex_printf(",");
+ ppHRegPPC(i->Pin.Dfp64Unary.src);
+ return;
+
+ case Pin_Dfp64Binary:
+ vex_printf("%s ", showPPCFpOp(i->Pin.Dfp64Binary.op));
+ ppHRegPPC(i->Pin.Dfp64Binary.dst);
+ vex_printf(",");
+ ppHRegPPC(i->Pin.Dfp64Binary.srcL);
+ vex_printf(",");
+ ppHRegPPC(i->Pin.Dfp64Binary.srcR);
+ return;
+
+ case Pin_Dfp128Binary:
+ vex_printf("%s ", showPPCFpOp(i->Pin.Dfp128Binary.op));
+ ppHRegPPC(i->Pin.Dfp128Binary.dst_hi);
+ vex_printf(",");
+ ppHRegPPC(i->Pin.Dfp128Binary.srcR_hi);
+ return;
+
default:
vex_printf("\nppPPCInstr: No such tag(%d)\n", (Int)i->tag);
vpanic("ppPPCInstr");
case Pin_AvLdVSCR:
addHRegUse(u, HRmRead, i->Pin.AvLdVSCR.src);
return;
+ case Pin_Dfp64Unary:
+ addHRegUse(u, HRmWrite, i->Pin.Dfp64Unary.dst);
+ addHRegUse(u, HRmRead, i->Pin.Dfp64Unary.src);
+ return;
+ case Pin_Dfp64Binary:
+ addHRegUse(u, HRmWrite, i->Pin.Dfp64Binary.dst);
+ addHRegUse(u, HRmRead, i->Pin.Dfp64Binary.srcL);
+ addHRegUse(u, HRmRead, i->Pin.Dfp64Binary.srcR);
+ return;
+ case Pin_Dfp128Binary:
+ addHRegUse(u, HRmWrite, i->Pin.Dfp128Binary.dst_hi);
+ addHRegUse(u, HRmWrite, i->Pin.Dfp128Binary.dst_lo);
+ addHRegUse(u, HRmRead, i->Pin.Dfp128Binary.srcR_hi);
+ addHRegUse(u, HRmRead, i->Pin.Dfp128Binary.srcR_lo);
+ return;
default:
ppPPCInstr(i, mode64);
case Pin_AvLdVSCR:
mapReg(m, &i->Pin.AvLdVSCR.src);
return;
+ case Pin_Dfp64Unary:
+ mapReg(m, &i->Pin.Dfp64Unary.dst);
+ mapReg(m, &i->Pin.Dfp64Unary.src);
+ return;
+ case Pin_Dfp64Binary:
+ mapReg(m, &i->Pin.Dfp64Binary.dst);
+ mapReg(m, &i->Pin.Dfp64Binary.srcL);
+ mapReg(m, &i->Pin.Dfp64Binary.srcR);
+ return;
+ case Pin_Dfp128Binary:
+ mapReg(m, &i->Pin.Dfp128Binary.dst_hi);
+ mapReg(m, &i->Pin.Dfp128Binary.dst_lo);
+ mapReg(m, &i->Pin.Dfp128Binary.srcR_hi);
+ mapReg(m, &i->Pin.Dfp128Binary.srcR_lo);
+ return;
default:
ppPPCInstr(i, mode64);
}
// Only used by mtfsf
-static UChar* mkFormXFL ( UChar* p, UInt FM, UInt freg )
+static UChar* mkFormXFL ( UChar* p, UInt FM, UInt freg, UInt dfp_rm )
{
UInt theInstr;
vassert(FM < 0x100);
vassert(freg < 0x20);
- theInstr = ((63<<26) | (FM<<17) | (freg<<11) | (711<<1));
+ theInstr = ((63<<26) | (FM<<17) | (dfp_rm<<16) | (freg<<11) | (711<<1));
return emit32(p, theInstr);
}
case Pin_FpLdFPSCR: {
UInt fr_src = fregNo(i->Pin.FpLdFPSCR.src);
- p = mkFormXFL(p, 0xFF, fr_src); // mtfsf, PPC32 p480
+ p = mkFormXFL(p, 0xFF, fr_src, i->Pin.FpLdFPSCR.dfp_rm); // mtfsf, PPC32 p480
goto done;
}
goto done;
}
+ case Pin_Dfp64Unary: {
+ UInt fr_dst = fregNo( i->Pin.FpUnary.dst );
+ UInt fr_src = fregNo( i->Pin.FpUnary.src );
+
+ switch (i->Pin.Dfp64Unary.op) {
+ case Pfp_MOV: // fmr, PPC32 p410
+ p = mkFormX( p, 63, fr_dst, 0, fr_src, 72, 0 );
+ break;
+ default:
+ goto bad;
+ }
+ goto done;
+ }
+
+ case Pin_Dfp64Binary: {
+ UInt fr_dst = fregNo( i->Pin.Dfp64Binary.dst );
+ UInt fr_srcL = fregNo( i->Pin.Dfp64Binary.srcL );
+ UInt fr_srcR = fregNo( i->Pin.Dfp64Binary.srcR );
+ switch (i->Pin.Dfp64Binary.op) {
+ case Pfp_DFPADD: /* dadd, dfp add, use default RM from reg ignore mode
+ * from the Iop instruction. */
+ p = mkFormX( p, 59, fr_dst, fr_srcL, fr_srcR, 2, 0 );
+ break;
+ case Pfp_DFPSUB: /* dsub, dfp subtract, use default RM from reg ignore
+ * mode from the Iop instruction. */
+ p = mkFormX( p, 59, fr_dst, fr_srcL, fr_srcR, 514, 0 );
+ break;
+ case Pfp_DFPMUL: /* dmul, dfp multipy, use default RM from reg ignore
+ * mode from the Iop instruction. */
+ p = mkFormX( p, 59, fr_dst, fr_srcL, fr_srcR, 34, 0 );
+ break;
+ case Pfp_DFPDIV: /* ddiv, dfp divide, use default RM from reg ignore
+ * mode from the Iop instruction. */
+ p = mkFormX( p, 59, fr_dst, fr_srcL, fr_srcR, 546, 0 );
+ break;
+ default:
+ goto bad;
+ }
+ goto done;
+ }
+
+ case Pin_Dfp128Binary: {
+ /* dst is used to supply the left source operand and return
+ * the result.
+ */
+ UInt fr_dstHi = fregNo( i->Pin.Dfp128Binary.dst_hi );
+ UInt fr_dstLo = fregNo( i->Pin.Dfp128Binary.dst_lo );
+ UInt fr_srcRHi = fregNo( i->Pin.Dfp128Binary.srcR_hi );
+ UInt fr_srcRLo = fregNo( i->Pin.Dfp128Binary.srcR_lo );
+
+ /* Setup the upper and lower registers of the source operand
+ * register pair.
+ */
+ p = mkFormX( p, 63, 10, 0, fr_dstHi, 72, 0 );
+ p = mkFormX( p, 63, 11, 0, fr_dstLo, 72, 0 );
+ p = mkFormX( p, 63, 12, 0, fr_srcRHi, 72, 0 );
+ p = mkFormX( p, 63, 13, 0, fr_srcRLo, 72, 0 );
+
+ /* Do instruction with 128-bit source operands in registers (10,11)
+ * and (12,13).
+ */
+ switch (i->Pin.Dfp128Binary.op) {
+ case Pfp_DFPADDQ:
+ p = mkFormX( p, 63, 10, 10, 12, 2, 0 );
+ break;
+ case Pfp_DFPSUBQ:
+ p = mkFormX( p, 63, 10, 10, 12, 514, 0 );
+ break;
+ case Pfp_DFPMULQ:
+ p = mkFormX( p, 63, 10, 10, 12, 34, 0 );
+ break;
+ case Pfp_DFPDIVQ:
+ p = mkFormX( p, 63, 10, 10, 12, 546, 0 );
+ break;
+ default:
+ goto bad;
+ }
+
+ /* The instruction will put the 128-bit result in
+ * registers (10,11). Note, the operand in the instruction only
+ * reference the first of the two registers in the pair.
+ */
+ p = mkFormX(p, 63, fr_dstHi, 0, 10, 72, 0);
+ p = mkFormX(p, 63, fr_dstLo, 0, 11, 72, 0);
+ goto done;
+ }
+
default:
goto bad;
}
Pfp_INVALID,
/* Ternary */
- Pfp_MADDD, Pfp_MSUBD,
- Pfp_MADDS, Pfp_MSUBS,
+ Pfp_MADDD, Pfp_MSUBD,
+ Pfp_MADDS, Pfp_MSUBS,
+ Pfp_DFPADD, Pfp_DFPADDQ,
+ Pfp_DFPSUB, Pfp_DFPSUBQ,
+ Pfp_DFPMUL, Pfp_DFPMULQ,
+ Pfp_DFPDIV, Pfp_DFPDIVQ,
+ Pfp_DQUAQ, Pfp_DRRNDQ,
/* Binary */
Pfp_ADDD, Pfp_SUBD, Pfp_MULD, Pfp_DIVD,
Pin_AvShlDbl, /* AV shift-left double by imm */
Pin_AvSplat, /* One elem repeated throughout dst */
Pin_AvLdVSCR, /* mtvscr */
- Pin_AvCMov /* AV conditional move */
+ Pin_AvCMov, /* AV conditional move */
+ Pin_Dfp64Unary, /* DFP64 unary op */
+ Pin_Dfp128nary, /* DFP128 unary op */
+ Pin_Dfp64Binary, /* DFP64 binary op */
+ Pin_Dfp128Binary /* DFP128 binary op */
}
PPCInstrTag;
/* Load FP Status & Control Register */
struct {
HReg src;
+ UInt dfp_rm;
} FpLdFPSCR;
/* Do a compare, generating result into an int register. */
struct {
struct {
HReg src;
} AvLdVSCR;
- } Pin;
+ struct {
+ PPCFpOp op;
+ HReg dst;
+ HReg src;
+ } Dfp64Unary;
+ struct {
+ PPCFpOp op;
+ HReg dst;
+ HReg srcL;
+ HReg srcR;
+ } Dfp64Binary;
+ struct {
+ PPCFpOp op;
+ HReg dst_hi;
+ HReg dst_lo;
+ HReg src_hi;
+ HReg src_lo;
+ } Dfp128Unary;
+ struct {
+ /* The dst is used to pass the left source operand in and return
+ * the result.
+ */
+ PPCFpOp op;
+ HReg dst_hi;
+ HReg dst_lo;
+ HReg srcR_hi;
+ HReg srcR_lo;
+ } Dfp128Binary;
+ } Pin;
}
PPCInstr;
extern PPCInstr* PPCInstr_FpCftI ( Bool fromI, Bool int32, Bool syned,
Bool dst64, HReg dst, HReg src );
extern PPCInstr* PPCInstr_FpCMov ( PPCCondCode, HReg dst, HReg src );
-extern PPCInstr* PPCInstr_FpLdFPSCR ( HReg src );
+extern PPCInstr* PPCInstr_FpLdFPSCR ( HReg src, Bool dfp_rm );
extern PPCInstr* PPCInstr_FpCmp ( HReg dst, HReg srcL, HReg srcR );
extern PPCInstr* PPCInstr_RdWrLR ( Bool wrLR, HReg gpr );
extern PPCInstr* PPCInstr_AvCMov ( PPCCondCode, HReg dst, HReg src );
extern PPCInstr* PPCInstr_AvLdVSCR ( HReg src );
-extern void ppPPCInstr ( PPCInstr*, Bool mode64 );
+extern PPCInstr* PPCInstr_Dfp64Unary ( PPCFpOp op, HReg dst, HReg src );
+extern PPCInstr* PPCInstr_Dfp64Binary ( PPCFpOp op, HReg dst, HReg srcL,
+ HReg srcR );
+extern PPCInstr* PPCInstr_Dfp128Binary( PPCFpOp op, HReg dst_hi, HReg dst_lo,
+ HReg srcR_hi, HReg srcR_lo );
+
+extern void ppPPCInstr(PPCInstr*, Bool mode64);
+
/* Some functions that insulate the register allocator from details
of the underlying instruction set. */
typedef
struct {
- IRTypeEnv* type_env;
-
- HReg* vregmap;
- HReg* vregmapHI;
- Int n_vregmap;
-
- HReg savedLR;
+ IRTypeEnv* type_env;
+ // 64-bit mode 32-bit mode
+ HReg* vregmapLo; // Low 64-bits [63:0] Low 32-bits [31:0]
+ HReg* vregmapMedLo; // high 64-bits[127:64] Next 32-bits [63:32]
+ HReg* vregmapMedHi; // unused Next 32-bits [95:64]
+ HReg* vregmapHi; // unused highest 32-bits [127:96]
+ Int n_vregmap;
+
+ HReg savedLR;
HInstrArray* code;
{
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
- return env->vregmap[tmp];
+ return env->vregmapLo[tmp];
}
static void lookupIRTempPair ( HReg* vrHI, HReg* vrLO,
ISelEnv* env, IRTemp tmp )
+{
+ vassert(tmp >= 0);
+ vassert(tmp < env->n_vregmap);
+ vassert(env->vregmapMedLo[tmp] != INVALID_HREG);
+ *vrLO = env->vregmapLo[tmp];
+ *vrHI = env->vregmapMedLo[tmp];
+}
+
+/* Only for used in 32-bit mode */
+static void lookupIRTempQuad ( HReg* vrHi, HReg* vrMedHi, HReg* vrMedLo,
+ HReg* vrLo, ISelEnv* env, IRTemp tmp )
{
vassert(!env->mode64);
vassert(tmp >= 0);
vassert(tmp < env->n_vregmap);
- vassert(env->vregmapHI[tmp] != INVALID_HREG);
- *vrLO = env->vregmap[tmp];
- *vrHI = env->vregmapHI[tmp];
+ vassert(env->vregmapMedLo[tmp] != INVALID_HREG);
+ *vrHi = env->vregmapHi[tmp];
+ *vrMedHi = env->vregmapMedHi[tmp];
+ *vrMedLo = env->vregmapMedLo[tmp];
+ *vrLo = env->vregmapLo[tmp];
}
static void addInstr ( ISelEnv* env, PPCInstr* instr )
static PPCAMode* iselWordExpr_AMode_wrk ( ISelEnv* env, IRExpr* e, IRType xferTy );
static PPCAMode* iselWordExpr_AMode ( ISelEnv* env, IRExpr* e, IRType xferTy );
+static void iselInt128Expr_to_32x4_wrk ( HReg* rHi, HReg* rMedHi,
+ HReg* rMedLo, HReg* rLo,
+ ISelEnv* env, IRExpr* e );
+static void iselInt128Expr_to_32x4 ( HReg* rHi, HReg* rMedHi,
+ HReg* rMedLo, HReg* rLo,
+ ISelEnv* env, IRExpr* e );
+
+
/* 32-bit mode ONLY: compute an I64 into a GPR pair. */
static void iselInt64Expr_wrk ( HReg* rHi, HReg* rLo,
ISelEnv* env, IRExpr* e );
static HReg iselVecExpr_wrk ( ISelEnv* env, IRExpr* e );
static HReg iselVecExpr ( ISelEnv* env, IRExpr* e );
+/* 64-bit mode ONLY. */
+static HReg iselDfp64Expr_wrk ( ISelEnv* env, IRExpr* e );
+static HReg iselDfp64Expr ( ISelEnv* env, IRExpr* e );
+
+/* 64-bit mode ONLY: compute an D128 into a GPR64 pair. */
+static void iselDfp128Expr_wrk ( HReg* rHi, HReg* rLo, ISelEnv* env,
+ IRExpr* e );
+static void iselDfp128Expr ( HReg* rHi, HReg* rLo, ISelEnv* env,
+ IRExpr* e );
/*---------------------------------------------------------*/
/*--- ISEL: Misc helpers ---*/
static HReg roundModeIRtoPPC ( ISelEnv* env, HReg r_rmIR )
{
/*
- rounding mode | PPC | IR
- ------------------------
- to nearest | 00 | 00
- to zero | 01 | 11
- to +infinity | 10 | 10
- to -infinity | 11 | 01
+ rounding mode | PPC | IR
+ -----------------------------------------------
+ to nearest, ties to even | 000 | 000
+ to zero | 001 | 011
+ to +infinity | 010 | 010
+ to -infinity | 011 | 001
+ +++++ Below are the extended rounding modes for decimal floating point +++++
+ to nearest, ties away from 0 | 100 | 100
+ to nearest, ties toward 0 | 101 | 111
+ to away from 0 | 110 | 110
+ to prepare for shorter precision | 111 | 101
*/
HReg r_rmPPC = newVRegI(env);
HReg r_tmp1 = newVRegI(env);
+ HReg r_tmp2 = newVRegI(env);
vassert(hregClass(r_rmIR) == HRcGPR(env->mode64));
addInstr(env, PPCInstr_Shft(Pshft_SHL, True/*32bit shift*/,
r_tmp1, r_rmIR, PPCRH_Imm(False,1)));
- addInstr(env, PPCInstr_Alu( Palu_XOR, r_tmp1, r_rmIR,
- PPCRH_Reg(r_tmp1) ));
+ addInstr( env, PPCInstr_Alu( Palu_AND,
+ r_tmp2, r_tmp1, PPCRH_Imm( False, 3 ) ) );
- addInstr(env, PPCInstr_Alu( Palu_AND, r_rmPPC, r_tmp1,
- PPCRH_Imm(False,3) ));
+ addInstr( env, PPCInstr_Alu( Palu_XOR,
+ r_rmPPC, r_rmIR, PPCRH_Reg( r_tmp2 ) ) );
return r_rmPPC;
}
/* Set the FPU's rounding mode: 'mode' is an I32-typed expression
- denoting a value in the range 0 .. 3, indicating a round mode
+ denoting a value in the range 0 .. 7, indicating a round mode
encoded as per type IRRoundingMode. Set the PPC FPSCR to have the
- same rounding.
+ same rounding. When the dfp_rm arg is True, set the decimal
+ floating point rounding mode bits (29:31); otherwise, set the
+ binary floating point rounding mode bits (62:63).
For speed & simplicity, we're setting the *entire* FPSCR here.
on any block with any sign of floating point activity.
*/
static
-void set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode )
+void _set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode, Bool dfp_rm )
{
HReg fr_src = newVRegF(env);
HReg r_src;
// Resolve rounding mode and convert to PPC representation
r_src = roundModeIRtoPPC( env, iselWordExpr_R(env, mode) );
+
// gpr -> fpr
if (env->mode64) {
- fr_src = mk_LoadR64toFPR( env, r_src ); // 1*I64 -> F64
+ if (dfp_rm) {
+ HReg r_tmp1 = newVRegI( env );
+ addInstr( env,
+ PPCInstr_Shft( Pshft_SHL, False/*64bit shift*/,
+ r_tmp1, r_src, PPCRH_Imm( False, 32 ) ) );
+ fr_src = mk_LoadR64toFPR( env, r_tmp1 );
+ } else {
+ fr_src = mk_LoadR64toFPR( env, r_src ); // 1*I64 -> F64
+ }
} else {
- fr_src = mk_LoadRR32toFPR( env, r_src, r_src ); // 2*I32 -> F64
+ if (dfp_rm) {
+ HReg r_zero = newVRegI( env );
+ addInstr( env, PPCInstr_LI( r_zero, 0, env->mode64 ) );
+ fr_src = mk_LoadRR32toFPR( env, r_src, r_zero );
+ } else {
+ fr_src = mk_LoadRR32toFPR( env, r_src, r_src ); // 2*I32 -> F64
+ }
}
// Move to FPSCR
- addInstr(env, PPCInstr_FpLdFPSCR( fr_src ));
+ addInstr(env, PPCInstr_FpLdFPSCR( fr_src, dfp_rm ));
+}
+
+static void set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode )
+{
+ _set_FPU_rounding_mode(env, mode, False);
+}
+
+static void set_FPU_DFP_rounding_mode ( ISelEnv* env, IRExpr* mode )
+{
+ _set_FPU_rounding_mode(env, mode, True);
}
*rHi = iselWordExpr_R(env, e->Iex.Binop.arg1);
*rLo = iselWordExpr_R(env, e->Iex.Binop.arg2);
return;
-
default:
break;
}
/*--- ISEL: Integer expressions (64 bit) ---*/
/*---------------------------------------------------------*/
+/* 32-bit mode ONLY: compute a 128-bit value into a register quad */
+static void iselInt128Expr_to_32x4 ( HReg* rHi, HReg* rMedHi, HReg* rMedLo,
+ HReg* rLo, ISelEnv* env, IRExpr* e )
+{
+ vassert(!env->mode64);
+ iselInt128Expr_to_32x4_wrk(rHi, rMedHi, rMedLo, rLo, env, e);
+# if 0
+ vex_printf("\n"); ppIRExpr(e); vex_printf("\n");
+# endif
+ vassert(hregClass(*rHi) == HRcInt32);
+ vassert(hregIsVirtual(*rHi));
+ vassert(hregClass(*rMedHi) == HRcInt32);
+ vassert(hregIsVirtual(*rMedHi));
+ vassert(hregClass(*rMedLo) == HRcInt32);
+ vassert(hregIsVirtual(*rMedLo));
+ vassert(hregClass(*rLo) == HRcInt32);
+ vassert(hregIsVirtual(*rLo));
+}
+
+static void iselInt128Expr_to_32x4_wrk ( HReg* rHi, HReg* rMedHi,
+ HReg* rMedLo, HReg* rLo,
+ ISelEnv* env, IRExpr* e )
+{
+ vassert(e);
+ vassert(typeOfIRExpr(env->type_env,e) == Ity_I128);
+
+ /* read 128-bit IRTemp */
+ if (e->tag == Iex_RdTmp) {
+ lookupIRTempQuad( rHi, rMedHi, rMedLo, rLo, env, e->Iex.RdTmp.tmp);
+ return;
+ }
+
+ if (e->tag == Iex_Binop) {
+
+ IROp op_binop = e->Iex.Binop.op;
+ switch (op_binop) {
+ case Iop_64HLto128:
+ iselInt64Expr(rHi, rMedHi, env, e->Iex.Binop.arg1);
+ iselInt64Expr(rMedLo, rLo, env, e->Iex.Binop.arg2);
+ return;
+ default:
+ vex_printf("iselInt128Expr_to_32x4_wrk: Binop case 0x%x not found\n",
+ op_binop);
+ break;
+ }
+ }
+
+ vex_printf("iselInt128Expr_to_32x4_wrk: e->tag 0x%x not found\n", e->tag);
+ return;
+}
+
/* 32-bit mode ONLY: compute a 64-bit value into a register pair,
which is returned as the first two parameters. As with
iselIntExpr_R, these may be either real or virtual regs; in any
return;
}
+ case Iop_128to64: {
+ /* Narrow, return the low 64-bit half as a 32-bit
+ * register pair */
+ HReg r_Hi = INVALID_HREG;
+ HReg r_MedHi = INVALID_HREG;
+ HReg r_MedLo = INVALID_HREG;
+ HReg r_Lo = INVALID_HREG;
+
+ iselInt128Expr_to_32x4(&r_Hi, &r_MedHi, &r_MedLo, &r_Lo,
+ env, e->Iex.Unop.arg);
+ *rHi = r_MedLo;
+ *rLo = r_Lo;
+ return;
+ }
+
+ case Iop_128HIto64: {
+ /* Narrow, return the high 64-bit half as a 32-bit
+ * register pair */
+ HReg r_Hi = INVALID_HREG;
+ HReg r_MedHi = INVALID_HREG;
+ HReg r_MedLo = INVALID_HREG;
+ HReg r_Lo = INVALID_HREG;
+
+ iselInt128Expr_to_32x4(&r_Hi, &r_MedHi, &r_MedLo, &r_Lo,
+ env, e->Iex.Unop.arg);
+ *rHi = r_Hi;
+ *rLo = r_MedHi;
+ return;
+ }
+
/* V128{HI}to64 */
case Iop_V128HIto64:
case Iop_V128to64: {
vpanic("iselDblExpr_wrk(ppc)");
}
+static HReg iselDfp64Expr(ISelEnv* env, IRExpr* e)
+{
+ HReg r = iselDfp64Expr_wrk( env, e );
+ vassert(hregClass(r) == HRcFlt64);
+ vassert( hregIsVirtual(r) );
+ return r;
+}
+
+/* DO NOT CALL THIS DIRECTLY */
+static HReg iselDfp64Expr_wrk(ISelEnv* env, IRExpr* e)
+{
+ Bool mode64 = env->mode64;
+ IRType ty = typeOfIRExpr( env->type_env, e );
+ HReg r_dstHi, r_dstLo;
+
+ vassert( e );
+ vassert( ty == Ity_D64 );
+
+ if (e->tag == Iex_RdTmp) {
+ return lookupIRTemp( env, e->Iex.RdTmp.tmp );
+ }
+
+ /* --------- GET --------- */
+ if (e->tag == Iex_Get) {
+ HReg r_dst = newVRegF( env );
+ PPCAMode* am_addr = PPCAMode_IR( e->Iex.Get.offset,
+ GuestStatePtr(mode64) );
+ addInstr( env, PPCInstr_FpLdSt( True/*load*/, 8, r_dst, am_addr ) );
+ return r_dst;
+ }
+
+ /* --------- OPS --------- */
+ if (e->tag == Iex_Qop) {
+ HReg r_dst = newVRegF( env );
+ return r_dst;
+ }
+
+ if (e->tag == Iex_Unop) {
+ switch (e->Iex.Unop.op) {
+ case Iop_D128HItoD64:
+ iselDfp128Expr( &r_dstHi, &r_dstLo, env, e->Iex.Unop.arg );
+ return r_dstHi;
+ case Iop_D128LOtoD64:
+ iselDfp128Expr( &r_dstHi, &r_dstLo, env, e->Iex.Unop.arg );
+ return r_dstLo;
+ default:
+ vex_printf( "ERROR: iselDfp64Expr_wrk, UNKNOWN unop case %d\n",
+ e->Iex.Unop.op );
+ }
+ }
+
+ if (e->tag == Iex_Triop) {
+ PPCFpOp fpop = Pfp_INVALID;
+
+ switch (e->Iex.Triop.op) {
+ case Iop_AddD64:
+ fpop = Pfp_DFPADD;
+ break;
+ case Iop_SubD64:
+ fpop = Pfp_DFPSUB;
+ break;
+ case Iop_MulD64:
+ fpop = Pfp_DFPMUL;
+ break;
+ case Iop_DivD64:
+ fpop = Pfp_DFPDIV;
+ break;
+ default:
+ break;
+ }
+ if (fpop != Pfp_INVALID) {
+ HReg r_dst = newVRegF( env );
+ HReg r_srcL = iselDfp64Expr( env, e->Iex.Triop.arg2 );
+ HReg r_srcR = iselDfp64Expr( env, e->Iex.Triop.arg3 );
+
+ set_FPU_DFP_rounding_mode( env, e->Iex.Triop.arg1 );
+ addInstr( env, PPCInstr_Dfp64Binary( fpop, r_dst, r_srcL, r_srcR ) );
+ return r_dst;
+ }
+ }
+
+ ppIRExpr( e );
+ vpanic( "iselDfp64Expr_wrk(ppc)" );
+}
+
+static void iselDfp128Expr(HReg* rHi, HReg* rLo, ISelEnv* env, IRExpr* e)
+{
+ iselDfp128Expr_wrk( rHi, rLo, env, e );
+ vassert( hregIsVirtual(*rHi) );
+ vassert( hregIsVirtual(*rLo) );
+}
+
+/* DO NOT CALL THIS DIRECTLY */
+static void iselDfp128Expr_wrk(HReg* rHi, HReg *rLo, ISelEnv* env, IRExpr* e)
+{
+ vassert( e );
+ vassert( typeOfIRExpr(env->type_env,e) == Ity_D128 );
+
+ /* read 128-bit IRTemp */
+ if (e->tag == Iex_RdTmp) {
+ // lookupDfp128IRTempPair( rHi, rLo, env, e->Iex.RdTmp.tmp );
+ lookupIRTempPair( rHi, rLo, env, e->Iex.RdTmp.tmp );
+ return;
+ }
+
+ /* --------- OPS --------- */
+ if (e->tag == Iex_Binop) {
+ HReg r_srcHi;
+ HReg r_srcLo;
+
+ switch (e->Iex.Binop.op) {
+ case Iop_D64HLtoD128:
+ r_srcHi = iselDfp64Expr( env, e->Iex.Binop.arg1 );
+ r_srcLo = iselDfp64Expr( env, e->Iex.Binop.arg2 );
+ *rHi = r_srcHi;
+ *rLo = r_srcLo;
+ return;
+ break;
+ default:
+ vex_printf( "ERROR: iselD128Expr_wrk, UNKNOWN binop case %d\n",
+ e->Iex.Binop.op );
+ break;
+ }
+ }
+
+ if (e->tag == Iex_Triop) {
+ PPCFpOp fpop = Pfp_INVALID;
+ switch (e->Iex.Triop.op) {
+ case Iop_AddD128:
+ fpop = Pfp_DFPADDQ;
+ break;
+ case Iop_SubD128:
+ fpop = Pfp_DFPSUBQ;
+ break;
+ case Iop_MulD128:
+ fpop = Pfp_DFPMULQ;
+ break;
+ case Iop_DivD128:
+ fpop = Pfp_DFPDIVQ;
+ break;
+ default:
+ break;
+ }
+
+ if (fpop != Pfp_INVALID) {
+ HReg r_dstHi = newVRegV( env );
+ HReg r_dstLo = newVRegV( env );
+ HReg r_srcRHi = newVRegV( env );
+ HReg r_srcRLo = newVRegV( env );
+
+ /* dst will be used to pass in the left operand and get the result. */
+ iselDfp128Expr( &r_dstHi, &r_dstLo, env, e->Iex.Triop.arg2 );
+ iselDfp128Expr( &r_srcRHi, &r_srcRLo, env, e->Iex.Triop.arg3 );
+ set_FPU_rounding_mode( env, e->Iex.Triop.arg1 );
+ addInstr( env,
+ PPCInstr_Dfp128Binary( fpop, r_dstHi, r_dstLo,
+ r_srcRHi, r_srcRLo ) );
+ *rHi = r_dstHi;
+ *rLo = r_dstLo;
+ return;
+ }
+ }
+
+ ppIRExpr( e );
+ vpanic( "iselD128Expr(ppc64)" );
+}
+
/*---------------------------------------------------------*/
/*--- ISEL: SIMD (Vector) expressions, 128 bit. ---*/
fr_src, am_addr ));
return;
}
+ if (ty == Ity_D64) {
+ HReg fr_src = iselDfp64Expr( env, stmt->Ist.Put.data );
+ PPCAMode* am_addr = PPCAMode_IR( stmt->Ist.Put.offset,
+ GuestStatePtr(mode64) );
+ addInstr( env, PPCInstr_FpLdSt( False/*store*/, 8, fr_src, am_addr ) );
+ return;
+ }
break;
}
}
if (!mode64 && ty == Ity_I64) {
HReg r_srcHi, r_srcLo, r_dstHi, r_dstLo;
+
iselInt64Expr(&r_srcHi,&r_srcLo, env, stmt->Ist.WrTmp.data);
lookupIRTempPair( &r_dstHi, &r_dstLo, env, tmp);
addInstr(env, mk_iMOVds_RR(r_dstHi, r_srcHi) );
addInstr(env, mk_iMOVds_RR(r_dstLo, r_srcLo) );
return;
}
+ if (!mode64 && ty == Ity_I128) {
+ HReg r_srcHi, r_srcMedHi, r_srcMedLo, r_srcLo;
+ HReg r_dstHi, r_dstMedHi, r_dstMedLo, r_dstLo;
+
+ iselInt128Expr_to_32x4(&r_srcHi, &r_srcMedHi,
+ &r_srcMedLo, &r_srcLo,
+ env, stmt->Ist.WrTmp.data);
+
+ lookupIRTempQuad( &r_dstHi, &r_dstMedHi, &r_dstMedLo,
+ &r_dstLo, env, tmp);
+
+ addInstr(env, mk_iMOVds_RR(r_dstHi, r_srcHi) );
+ addInstr(env, mk_iMOVds_RR(r_dstMedHi, r_srcMedHi) );
+ addInstr(env, mk_iMOVds_RR(r_dstMedLo, r_srcMedLo) );
+ addInstr(env, mk_iMOVds_RR(r_dstLo, r_srcLo) );
+ return;
+ }
if (ty == Ity_I1) {
PPCCondCode cond = iselCondCode(env, stmt->Ist.WrTmp.data);
HReg r_dst = lookupIRTemp(env, tmp);
addInstr(env, PPCInstr_AvUnary(Pav_MOV, v_dst, v_src));
return;
}
+ if (ty == Ity_D64) {
+ HReg fr_dst = lookupIRTemp( env, tmp );
+ HReg fr_src = iselDfp64Expr( env, stmt->Ist.WrTmp.data );
+ addInstr( env, PPCInstr_Dfp64Unary( Pfp_MOV, fr_dst, fr_src ) );
+ return;
+ }
+ if (ty == Ity_D128) {
+ HReg fr_srcHi, fr_srcLo, fr_dstHi, fr_dstLo;
+ // lookupDfp128IRTempPair( &fr_dstHi, &fr_dstLo, env, tmp );
+ lookupIRTempPair( &fr_dstHi, &fr_dstLo, env, tmp );
+ iselDfp128Expr( &fr_srcHi, &fr_srcLo, env, stmt->Ist.WrTmp.data );
+ addInstr( env, PPCInstr_Dfp64Unary( Pfp_MOV, fr_dstHi, fr_srcHi ) );
+ addInstr( env, PPCInstr_Dfp64Unary( Pfp_MOV, fr_dstLo, fr_srcLo ) );
+ return;
+ }
break;
}
/*---------------------------------------------------------*/
/* Translate an entire BS to ppc code. */
-
HInstrArray* iselSB_PPC ( IRSB* bb, VexArch arch_host,
VexArchInfo* archinfo_host,
VexAbiInfo* vbi )
{
Int i, j;
- HReg hreg, hregHI;
+ HReg hregLo, hregMedLo, hregMedHi, hregHi;
ISelEnv* env;
UInt hwcaps_host = archinfo_host->hwcaps;
Bool mode64 = False;
env->type_env = bb->tyenv;
/* Make up an IRTemp -> virtual HReg mapping. This doesn't
- change as we go along. */
+ * change as we go along.
+ *
+ * vregmap2 and vregmap3 are only used in 32 bit mode
+ * for supporting I128 in 32-bit mode
+ */
env->n_vregmap = bb->tyenv->types_used;
- env->vregmap = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
- env->vregmapHI = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
+ env->vregmapLo = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
+ env->vregmapMedLo = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
+ if (!mode64) {
+ env->vregmapMedHi = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
+ env->vregmapHi = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
+ }
/* and finally ... */
env->hwcaps = hwcaps_host;
register. */
j = 0;
for (i = 0; i < env->n_vregmap; i++) {
- hregHI = hreg = INVALID_HREG;
+ hregLo = hregMedLo = hregMedHi = hregHi = INVALID_HREG;
switch (bb->tyenv->types[i]) {
case Ity_I1:
case Ity_I8:
case Ity_I16:
case Ity_I32:
- if (mode64) { hreg = mkHReg(j++, HRcInt64, True); break;
- } else { hreg = mkHReg(j++, HRcInt32, True); break;
+ if (mode64) { hregLo = mkHReg(j++, HRcInt64, True); break;
+ } else { hregLo = mkHReg(j++, HRcInt32, True); break;
}
case Ity_I64:
- if (mode64) { hreg = mkHReg(j++, HRcInt64, True); break;
- } else { hreg = mkHReg(j++, HRcInt32, True);
- hregHI = mkHReg(j++, HRcInt32, True); break;
+ if (mode64) { hregLo = mkHReg(j++, HRcInt64, True); break;
+ } else { hregLo = mkHReg(j++, HRcInt32, True);
+ hregMedLo = mkHReg(j++, HRcInt32, True); break;
+ }
+ case Ity_I128:
+ if (mode64) { hregLo = mkHReg(j++, HRcInt64, True);
+ hregMedLo = mkHReg(j++, HRcInt64, True); break;
+ } else { hregLo = mkHReg(j++, HRcInt32, True);
+ hregMedLo = mkHReg(j++, HRcInt32, True);
+ hregMedHi = mkHReg(j++, HRcInt32, True);
+ hregHi = mkHReg(j++, HRcInt32, True); break;
}
- case Ity_I128: vassert(mode64);
- hreg = mkHReg(j++, HRcInt64, True);
- hregHI = mkHReg(j++, HRcInt64, True); break;
case Ity_F32:
- case Ity_F64: hreg = mkHReg(j++, HRcFlt64, True); break;
- case Ity_V128: hreg = mkHReg(j++, HRcVec128, True); break;
+ case Ity_F64: hregLo = mkHReg(j++, HRcFlt64, True); break;
+ case Ity_V128: hregLo = mkHReg(j++, HRcVec128, True); break;
+ case Ity_D64: hregLo = mkHReg(j++, HRcFlt64, True); break;
+ case Ity_D128: hregLo = mkHReg(j++, HRcFlt64, True);
+ hregMedLo = mkHReg(j++, HRcFlt64, True); break;
default:
ppIRType(bb->tyenv->types[i]);
vpanic("iselBB(ppc): IRTemp type");
}
- env->vregmap[i] = hreg;
- env->vregmapHI[i] = hregHI;
+ env->vregmapLo[i] = hregLo;
+ env->vregmapMedLo[i] = hregMedLo;
+ if (!mode64) {
+ env->vregmapMedHi[i] = hregMedHi;
+ env->vregmapHi[i] = hregHi;
+ }
}
env->vreg_ctr = j;
case Ity_F64: vex_printf( "F64"); break;
case Ity_F128: vex_printf( "F128"); break;
case Ity_V128: vex_printf( "V128"); break;
+ case Ity_D32: vex_printf( "D32"); break;
+ case Ity_D64: vex_printf( "D64"); break;
+ case Ity_D128: vex_printf( "D128"); break;
default: vex_printf("ty = 0x%x\n", (Int)ty);
vpanic("ppIRType");
}
case Iop_Fixed32UToF32x2_RN: vex_printf("Fixed32UToF32x2_RN"); return;
case Iop_Fixed32SToF32x2_RN: vex_printf("Fixed32SToF32x2_RN"); return;
+ case Iop_AddD64: vex_printf("AddD64"); return;
+ case Iop_SubD64: vex_printf("SubD64"); return;
+ case Iop_MulD64: vex_printf("MulD64"); return;
+ case Iop_DivD64: vex_printf("DivD64"); return;
+ case Iop_AddD128: vex_printf("AddD128"); return;
+ case Iop_SubD128: vex_printf("SubD128"); return;
+ case Iop_MulD128: vex_printf("MulD128"); return;
+ case Iop_DivD128: vex_printf("DivD128"); return;
+ case Iop_D64HLtoD128: vex_printf("D64HLtoD128"); return;
+ case Iop_D128HItoD64: vex_printf("D128HItoD64"); return;
+ case Iop_D128LOtoD64: vex_printf("D128LOtoD64"); return;
+
default: vpanic("ppIROp(1)");
}
case Iop_F128toF32: BINARY(ity_RMode,Ity_F128, Ity_F32);
case Iop_F128toF64: BINARY(ity_RMode,Ity_F128, Ity_F64);
+ case Iop_D128HItoD64:
+ case Iop_D128LOtoD64:
+ UNARY(Ity_D128, Ity_D64);
+
+ case Iop_D64HLtoD128:
+ BINARY(Ity_D64,Ity_D64, Ity_D128);
+
+ case Iop_AddD64:
+ case Iop_SubD64:
+ case Iop_MulD64:
+ case Iop_DivD64:
+ TERNARY( ity_RMode, Ity_D64, Ity_D64, Ity_D64 );
+
+ case Iop_AddD128:
+ case Iop_SubD128:
+ case Iop_MulD128:
+ case Iop_DivD128:
+ TERNARY(ity_RMode,Ity_D128,Ity_D128, Ity_D128);
+
default:
ppIROp(op);
vpanic("typeOfPrimop");
case Ity_I8: case Ity_I16: case Ity_I32:
case Ity_I64: case Ity_I128:
case Ity_F32: case Ity_F64: case Ity_F128:
+ case Ity_D32: case Ity_D64: case Ity_D128:
case Ity_V128:
return True;
default:
case Ity_F64: return 8;
case Ity_F128: return 16;
case Ity_V128: return 16;
+ case Ity_D32: return 4;
+ case Ity_D64: return 8;
+ case Ity_D128: return 16;
default: vex_printf("\n"); ppIRType(ty); vex_printf("\n");
vpanic("sizeofIRType");
}
case Ity_F32: case Ity_F64: case Ity_F128: case Ity_V128:
*hasVorFtemps = True;
break;
+ case Ity_D32: case Ity_D64: case Ity_D128:
+ *hasVorFtemps = True;
+ break;
default:
goto bad;
}
/* 1182 */ UChar guest_CR7_321; /* in [3:1] */
/* 1183 */ UChar guest_CR7_0; /* in lsb */
- /* FP Status & Control Register fields */
- /* 1184 */ UInt guest_FPROUND; // FP Rounding Mode
+ /* FP Status & Control Register fields. Only rounding mode fields are supported. */
+ /* 1184 */ UChar guest_FPROUND; // Binary Floating Point Rounding Mode
+ /* 1185 */ UChar guest_DFPROUND; // Decimal Floating Point Rounding Mode
+ /* 1186 */ UChar pad1;
+ /* 1187 */ UChar pad2;
/* Vector Save/Restore Register */
/* 1188 */ UInt guest_VRSAVE;
/* 1322 */ UChar guest_CR7_321; /* in [3:1] */
/* 1323 */ UChar guest_CR7_0; /* in lsb */
- /* FP Status & Control Register fields */
- /* 1324 */ UInt guest_FPROUND; // FP Rounding Mode
+ /* FP Status and Control Register fields. Only rounding mode fields
+ are supported. */
+ /* 1324 */ UChar guest_FPROUND; // Binary Floating Point Rounding Mode
+ /* 1325 */ UChar guest_DFPROUND; // Decimal Floating Point Rounding Mode
+ /* 1326 */ UChar pad1;
+ /* 1327 */ UChar pad2;
/* Vector Save/Restore Register */
/* 1328 */ UInt guest_VRSAVE;
Ity_I128, /* 128-bit scalar */
Ity_F32, /* IEEE 754 float */
Ity_F64, /* IEEE 754 double */
+ Ity_D32, /* 32-bit Decimal floating point */
+ Ity_D64, /* 64-bit Decimal floating point */
+ Ity_D128, /* 128-bit Decimal floating point */
Ity_F128, /* 128-bit floating point; implementation defined */
Ity_V128 /* 128-bit SIMD */
}
See floating-point equiwalents for details. */
Iop_Recip32x2, Iop_Rsqrte32x2,
+ /* ------------------ Decimal Floating Point ------------------ */
+
+ /* ARITHMETIC INSTRUCTIONS 64-bit
+ ----------------------------------
+ IRRoundingModeDFP(I32) X D64 X D64 -> D64
+ Iop_AddD64, Iop_SubD64, Iop_MulD64, Iop_DivD64
+ */
+ Iop_AddD64, Iop_SubD64, Iop_MulD64, Iop_DivD64,
+
+ /* ARITHMETIC INSTRUCTIONS 128-bit
+ ----------------------------------
+ IRRoundingModeDFP(I32) X D128 X D128 -> D128
+ Iop_AddD128, Iop_SubD128, Iop_MulD128, Iop_DivD128
+ */
+ Iop_AddD128, Iop_SubD128, Iop_MulD128, Iop_DivD128,
+
+ /* Support for 128-bit DFP type */
+ Iop_D64HLtoD128, Iop_D128HItoD64, Iop_D128LOtoD64,
+
/* ------------------ 128-bit SIMD FP. ------------------ */
/* --- 32x4 vector FP --- */
}
IRRoundingMode;
+/* DFP encoding of IEEE754 2008 specified rounding modes extends the two bit
+ * binary floating point rounding mode (IRRoundingMode) to three bits. The
+ * DFP rounding modes are a super set of the binary rounding modes. The
+ * encoding was chosen such that the mapping of the least significant two bits
+ * of the IR to POWER encodings is same. The upper IR encoding bit is just
+ * a logical OR of the upper rounding mode bit from the POWER encoding.
+ */
+typedef
+ enum {
+ Irrm_DFP_NEAREST = 0, // Round to nearest, ties to even
+ Irrm_DFP_NegINF = 1, // Round to negative infinity
+ Irrm_DFP_PosINF = 2, // Round to posative infinity
+ Irrm_DFP_ZERO = 3, // Round toward zero
+ Irrm_DFP_NEAREST_TIE_AWAY_0 = 4, // Round to nearest, ties away from 0
+ Irrm_DFP_PREPARE_SHORTER = 5, // Round to prepare for storter
+ // precision
+ Irrm_DFP_AWAY_FROM_ZERO = 6, // Round to away from 0
+ Irrm_DFP_NEAREST_TIE_TOWARD_0 = 7 // Round to nearest, ties towards 0
+ }
+ IRRoundingModeDFP;
+
/* Floating point comparison result values, as created by Iop_CmpF64.
This is also derived from what IA32 does. */
typedef