From: Julian Seward Date: Mon, 2 Apr 2012 10:20:48 +0000 (+0000) Subject: Initial support for POWER Processor decimal floating point instruction X-Git-Tag: svn/VALGRIND_3_8_1^2~189 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e7775ac8fdcb7a9e80a3231b1faaadb5fb48243c;p=thirdparty%2Fvalgrind.git Initial support for POWER Processor decimal floating point instruction 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 --- diff --git a/VEX/priv/guest_ppc_helpers.c b/VEX/priv/guest_ppc_helpers.c index b8a3cd0780..26ec86f419 100644 --- a/VEX/priv/guest_ppc_helpers.c +++ b/VEX/priv/guest_ppc_helpers.c @@ -482,7 +482,8 @@ void LibVEX_GuestPPC32_initialise ( /*OUT*/VexGuestPPC32State* vex_state ) 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; @@ -638,7 +639,8 @@ void LibVEX_GuestPPC64_initialise ( /*OUT*/VexGuestPPC64State* vex_state ) 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; diff --git a/VEX/priv/guest_ppc_toIR.c b/VEX/priv/guest_ppc_toIR.c index 06e512a6ff..85d3b0c311 100644 --- a/VEX/priv/guest_ppc_toIR.c +++ b/VEX/priv/guest_ppc_toIR.c @@ -222,6 +222,7 @@ static void* fnptr_to_fnentry( VexAbiInfo* vbi, void* f ) #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) @@ -373,8 +374,8 @@ typedef enum { 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 @@ -415,7 +416,6 @@ static UInt MASK32( UInt begin, UInt end ) return mask; } -/* ditto for 64bit mask */ static ULong MASK64( UInt begin, UInt end ) { ULong m1, m2, mask; @@ -1114,6 +1114,45 @@ static void putFReg ( UInt archreg, IRExpr* e ) 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); @@ -2464,10 +2503,12 @@ static IRExpr* /* ::Ity_I32 */ getGST_masked ( PPC_GST reg, UInt mask ) 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) ); } @@ -2486,6 +2527,36 @@ static IRExpr* /* ::Ity_I32 */ getGST_masked ( PPC_GST reg, UInt mask ) } } +/* 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. */ @@ -2581,32 +2652,30 @@ static void putGST ( PPC_GST reg, IRExpr* src ) } /* 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: @@ -2641,13 +2710,17 @@ static void putGST_masked ( PPC_GST reg, IRExpr* src, UInt mask ) 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<> ( 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; } @@ -8360,7 +8500,124 @@ static Bool dis_fp_scr ( UInt theInstr ) 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 ---*/ @@ -13542,7 +13799,15 @@ DisResult disInstr_PPC_WRK ( 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; @@ -13759,6 +14024,16 @@ DisResult disInstr_PPC_WRK ( 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 @@ -13812,7 +14087,9 @@ DisResult disInstr_PPC_WRK ( 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: diff --git a/VEX/priv/host_ppc_defs.c b/VEX/priv/host_ppc_defs.c index cac5a5c543..9229e11cd8 100644 --- a/VEX/priv/host_ppc_defs.c +++ b/VEX/priv/host_ppc_defs.c @@ -630,6 +630,14 @@ HChar* showPPCFpOp ( PPCFpOp op ) { 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"); } } @@ -970,6 +978,35 @@ PPCInstr* PPCInstr_FpRSP ( HReg dst, HReg src ) { 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 | @@ -1040,10 +1077,11 @@ PPCInstr* PPCInstr_FpCMov ( PPCCondCode cond, HReg dst, HReg src ) { 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 ) { @@ -1547,6 +1585,7 @@ void ppPPCInstr ( PPCInstr* i, Bool mode64 ) 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,"); @@ -1711,6 +1750,29 @@ void ppPPCInstr ( PPCInstr* i, Bool mode64 ) 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"); @@ -1978,6 +2040,21 @@ void getRegUsage_PPCInstr ( HRegUsage* u, PPCInstr* i, Bool mode64 ) 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); @@ -2174,6 +2251,21 @@ void mapRegs_PPCInstr ( HRegRemap* m, PPCInstr* i, Bool 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); @@ -2423,12 +2515,12 @@ static UChar* mkFormXFX ( UChar* p, UInt r1, UInt f2, UInt opc2 ) } // 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); } @@ -3533,7 +3625,7 @@ Int emit_PPCInstr ( UChar* buf, Int nbuf, PPCInstr* i, 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; } @@ -3950,6 +4042,93 @@ Int emit_PPCInstr ( UChar* buf, Int nbuf, PPCInstr* i, 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; } diff --git a/VEX/priv/host_ppc_defs.h b/VEX/priv/host_ppc_defs.h index 58ddb4327a..9f6797bd63 100644 --- a/VEX/priv/host_ppc_defs.h +++ b/VEX/priv/host_ppc_defs.h @@ -359,8 +359,13 @@ typedef 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, @@ -485,7 +490,11 @@ typedef 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; @@ -685,6 +694,7 @@ typedef /* Load FP Status & Control Register */ struct { HReg src; + UInt dfp_rm; } FpLdFPSCR; /* Do a compare, generating result into an int register. */ struct { @@ -782,7 +792,35 @@ typedef 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; @@ -820,7 +858,7 @@ extern PPCInstr* PPCInstr_FpRSP ( HReg dst, HReg src ); 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 ); @@ -840,7 +878,14 @@ extern PPCInstr* PPCInstr_AvSplat ( UChar sz, HReg dst, PPCVI5s* src ); 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. */ diff --git a/VEX/priv/host_ppc_isel.c b/VEX/priv/host_ppc_isel.c index 07b5854c10..78b2a08b58 100644 --- a/VEX/priv/host_ppc_isel.c +++ b/VEX/priv/host_ppc_isel.c @@ -252,13 +252,15 @@ static IRExpr* bind ( Int binder ) 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; @@ -280,18 +282,31 @@ static HReg lookupIRTemp ( ISelEnv* env, IRTemp tmp ) { 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 ) @@ -391,6 +406,14 @@ static PPCRH* iselWordExpr_RH6u ( ISelEnv* env, IRExpr* e ); 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 ); @@ -415,6 +438,15 @@ static HReg iselFltExpr ( ISelEnv* env, IRExpr* e ); static HReg iselVecExpr_wrk ( ISelEnv* env, IRExpr* e ); static HReg iselVecExpr ( ISelEnv* env, IRExpr* e ); +/* 64-bit mode ONLY. */ +static HReg 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 ---*/ @@ -888,15 +920,21 @@ void doHelperCall ( ISelEnv* env, 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)); @@ -909,20 +947,22 @@ static HReg roundModeIRtoPPC ( ISelEnv* env, HReg r_rmIR ) 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. @@ -947,7 +987,7 @@ static HReg roundModeIRtoPPC ( ISelEnv* env, HReg r_rmIR ) 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; @@ -972,15 +1012,40 @@ void set_FPU_rounding_mode ( ISelEnv* env, IRExpr* mode ) // 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); } @@ -2569,7 +2634,6 @@ static void iselInt128Expr_wrk ( HReg* rHi, HReg* rLo, *rHi = iselWordExpr_R(env, e->Iex.Binop.arg1); *rLo = iselWordExpr_R(env, e->Iex.Binop.arg2); return; - default: break; } @@ -2594,6 +2658,57 @@ static void iselInt128Expr_wrk ( HReg* rHi, HReg* rLo, /*--- 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 @@ -2864,6 +2979,36 @@ static void iselInt64Expr_wrk ( HReg* rHi, HReg* rLo, 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: { @@ -3436,6 +3581,173 @@ static HReg iselDblExpr_wrk ( ISelEnv* env, IRExpr* e ) 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. ---*/ @@ -3985,6 +4297,13 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt ) 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; } @@ -4023,6 +4342,7 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt ) } 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) ); @@ -4037,6 +4357,23 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt ) 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); @@ -4061,6 +4398,21 @@ static void iselStmt ( ISelEnv* env, IRStmt* stmt ) 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; } @@ -4242,13 +4594,12 @@ static void iselNext ( ISelEnv* env, IRExpr* next, IRJumpKind jk ) /*---------------------------------------------------------*/ /* 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; @@ -4284,10 +4635,18 @@ HInstrArray* iselSB_PPC ( IRSB* bb, VexArch arch_host, 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; @@ -4298,32 +4657,44 @@ HInstrArray* iselSB_PPC ( IRSB* bb, VexArch arch_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; diff --git a/VEX/priv/ir_defs.c b/VEX/priv/ir_defs.c index 06708a2d52..545df8e3ff 100644 --- a/VEX/priv/ir_defs.c +++ b/VEX/priv/ir_defs.c @@ -58,6 +58,9 @@ void ppIRType ( IRType ty ) 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"); } @@ -934,6 +937,18 @@ void ppIROp ( IROp op ) 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)"); } @@ -2591,6 +2606,25 @@ void typeOfPrimop ( IROp op, 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"); @@ -2735,6 +2769,7 @@ Bool isPlausibleIRType ( IRType ty ) 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: @@ -3602,6 +3637,9 @@ Int sizeofIRType ( IRType ty ) 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"); } diff --git a/VEX/priv/ir_opt.c b/VEX/priv/ir_opt.c index 849648d24c..a97ce58917 100644 --- a/VEX/priv/ir_opt.c +++ b/VEX/priv/ir_opt.c @@ -4818,6 +4818,9 @@ static void considerExpensives ( /*OUT*/Bool* hasGetIorPutI, 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; } diff --git a/VEX/pub/libvex_guest_ppc32.h b/VEX/pub/libvex_guest_ppc32.h index 00b76490e1..d848029d09 100644 --- a/VEX/pub/libvex_guest_ppc32.h +++ b/VEX/pub/libvex_guest_ppc32.h @@ -189,8 +189,11 @@ typedef /* 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; diff --git a/VEX/pub/libvex_guest_ppc64.h b/VEX/pub/libvex_guest_ppc64.h index 631791b3f0..e086c02cfd 100644 --- a/VEX/pub/libvex_guest_ppc64.h +++ b/VEX/pub/libvex_guest_ppc64.h @@ -227,8 +227,12 @@ typedef /* 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; diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index 04c9fa906d..3b4c7f006d 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -227,6 +227,9 @@ typedef 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 */ } @@ -983,6 +986,25 @@ typedef 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 --- */ @@ -1301,6 +1323,27 @@ typedef } 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