From: Florian Krohm Date: Fri, 21 Dec 2012 17:32:12 +0000 (+0000) Subject: Add support for these DFP insns: X-Git-Tag: svn/VALGRIND_3_9_0^2~181 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0d8fe4df8fac2b6b93053fbc8932462794ca6205;p=thirdparty%2Fvalgrind.git Add support for these DFP insns: AXTRA, CDTR, CXTR, DXTRA, LDETR, LXDTR, LDXTR, LEDTR, LTXTR, MXTRA, SXTRA This is part of fixing BZ #307113. Patch by Maran Pakkirisamy (maranp@linux.vnet.ibm.com) with some minor mods. git-svn-id: svn://svn.valgrind.org/vex/trunk@2605 --- diff --git a/VEX/priv/guest_s390_defs.h b/VEX/priv/guest_s390_defs.h index e73190772e..51eaaeea61 100644 --- a/VEX/priv/guest_s390_defs.h +++ b/VEX/priv/guest_s390_defs.h @@ -137,7 +137,8 @@ enum { S390_CC_OP_BFP_32_TO_UINT_64 = 39, S390_CC_OP_BFP_64_TO_UINT_64 = 40, S390_CC_OP_BFP_128_TO_UINT_64 = 41, - S390_CC_OP_DFP_RESULT_64 = 42 + S390_CC_OP_DFP_RESULT_64 = 42, + S390_CC_OP_DFP_RESULT_128 = 43 }; /*------------------------------------------------------------*/ @@ -196,6 +197,7 @@ enum { | S390_CC_OP_BFP_64_TO_UINT_64 | F source | Z rounding mode | | | S390_CC_OP_BFP_128_TO_UINT_64 | F source hi 64 bits | F source low 64 bits | Z rounding mode | | S390_CC_OP_DFP_RESULT_64 | D result | | | + | S390_CC_OP_DFP_RESULT_128 | D result hi 64 bits | D result low 64 bits | | +--------------------------------+-----------------------+----------------------+-----------------+ */ diff --git a/VEX/priv/guest_s390_helpers.c b/VEX/priv/guest_s390_helpers.c index 891ee88c1e..930636c773 100644 --- a/VEX/priv/guest_s390_helpers.c +++ b/VEX/priv/guest_s390_helpers.c @@ -1165,6 +1165,18 @@ decode_bfp_rounding_mode(UInt irrm) psw >> 28; /* cc */ \ }) +#define S390_CC_FOR_DFP128_RESULT(hi,lo) \ +({ \ + __asm__ volatile ( \ + "ldr 4,%[high]\n\t" \ + "ldr 6,%[low]\n\t" \ + ".insn rre, 0xb3de0000,0,4\n\t" /* LTXTR */ \ + "ipm %[psw]\n\t" : [psw] "=d"(psw) \ + : [high] "f"(hi), [low] "f"(lo) \ + : "cc", "f0", "f2", "f4", "f6"); \ + psw >> 28; /* cc */ \ +}) + /* Return the value of the condition code from the supplied thunk parameters. This is not the value of the PSW. It is the value of the 2 CC bits within @@ -1384,6 +1396,9 @@ s390_calculate_cc(ULong cc_op, ULong cc_dep1, ULong cc_dep2, ULong cc_ndep) case S390_CC_OP_DFP_RESULT_64: return S390_CC_FOR_DFP_RESULT(cc_dep1); + case S390_CC_OP_DFP_RESULT_128: + return S390_CC_FOR_DFP128_RESULT(cc_dep1, cc_dep2); + default: break; } diff --git a/VEX/priv/guest_s390_toIR.c b/VEX/priv/guest_s390_toIR.c index 7663920a35..e77efc2aea 100644 --- a/VEX/priv/guest_s390_toIR.c +++ b/VEX/priv/guest_s390_toIR.c @@ -50,6 +50,7 @@ static UInt s390_decode_and_irgen(UChar *, UInt, DisResult *); static void s390_irgen_xonc(IROp, IRTemp, IRTemp, IRTemp); static void s390_irgen_CLC_EX(IRTemp, IRTemp, IRTemp); +static IRExpr *convert_vex_fpcc_to_s390(IRTemp); /*------------------------------------------------------------*/ @@ -455,6 +456,29 @@ put_fpr_pair(UInt archreg, IRExpr *expr) put_fpr_dw0(archreg + 2, low); } +/* Read a floating point register pair cointaining DFP value + and combine their contents into a 128-bit value */ + +static IRExpr * +get_dpr_pair(UInt archreg) +{ + IRExpr *high = get_dpr_dw0(archreg); + IRExpr *low = get_dpr_dw0(archreg + 2); + + return binop(Iop_D64HLtoD128, high, low); +} + +/* Write a 128-bit decimal floating point value into a register pair. */ +static void +put_dpr_pair(UInt archreg, IRExpr *expr) +{ + IRExpr *high = unop(Iop_D128HItoD64, expr); + IRExpr *low = unop(Iop_D128LOtoD64, expr); + + put_dpr_dw0(archreg, high); + put_dpr_dw0(archreg + 2, low); +} + /* Terminate the current IRSB with an emulation failure. */ static void emulation_failure(VexEmNote fail_kind) @@ -667,6 +691,22 @@ s390_cc_thunk_put1f128Z(UInt opc, IRTemp d1, IRTemp nd) } +/* Write a 128-bit decimal floating point value into the flags thunk. + This is done by splitting the value into two 64-bits values. */ +static void +s390_cc_thunk_put1d128(UInt opc, IRTemp d1) +{ + IRExpr *op, *hi, *lo, *ndep; + + op = mkU64(opc); + hi = unop(Iop_D128HItoD64, mkexpr(d1)); + lo = unop(Iop_D128LOtoD64, mkexpr(d1)); + ndep = mkU64(0); + + s390_cc_thunk_fill(op, hi, lo, ndep); +} + + static void s390_cc_set(UInt val) { @@ -912,6 +952,22 @@ get_fpr_dw0(UInt archreg) return IRExpr_Get(fpr_dw0_offset(archreg), Ity_F64); } +/* Write word #0 of a dpr to the guest state. */ +static __inline__ void +put_dpr_w0(UInt archreg, IRExpr *expr) +{ + vassert(typeOfIRExpr(irsb->tyenv, expr) == Ity_D32); + + stmt(IRStmt_Put(fpr_w0_offset(archreg), expr)); +} + +/* Read word #0 of a dpr register. */ +static __inline__ IRExpr * +get_dpr_w0(UInt archreg) +{ + return IRExpr_Get(fpr_w0_offset(archreg), Ity_D32); +} + /* Write double word #0 of a fpr containg DFP value to the guest state. */ static __inline__ void put_dpr_dw0(UInt archreg, IRExpr *expr) @@ -1888,6 +1944,16 @@ s390_format_RRF_UUFF(const HChar *(*irgen)(UChar m3, UChar m4, UChar r1, s390_disasm(ENC5(MNM, FPR, UINT, FPR, UINT), mnm, r1, m3, r2, m4); } +static void +s390_format_RRF_0UFF(const HChar *(*irgen)(UChar m4, UChar r1, UChar r2), + UChar m4, UChar r1, UChar r2) +{ + const HChar *mnm = irgen(m4, r1, r2); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_FE)) + s390_disasm(ENC4(MNM, FPR, FPR, UINT), mnm, r1, r2, m4); +} + static void s390_format_RRF_UUFR(const HChar *(*irgen)(UChar m3, UChar m4, UChar r1, UChar r2), @@ -8928,18 +8994,6 @@ s390_irgen_LEDBR(UChar m3, UChar m4 __attribute__((unused)), return "ledbr"; } -static const HChar * -s390_irgen_LTDTR(UChar r1, UChar r2) -{ - IRTemp result = newTemp(Ity_D64); - - assign(result, get_dpr_dw0(r2)); - put_dpr_dw0(r1, mkexpr(result)); - s390_cc_thunk_putF(S390_CC_OP_DFP_RESULT_64, result); - - return "ltdtr"; -} - static const HChar * s390_irgen_MEEBR(UChar r1, UChar r2) { @@ -9104,6 +9158,66 @@ s390_irgen_ADTRA(UChar r3, UChar m4, UChar r1, UChar r2) return (m4 == 0) ? "adtr" : "adtra"; } +static const HChar * +s390_irgen_AXTRA(UChar r3, UChar m4, UChar r1, UChar r2) +{ + IRTemp op1 = newTemp(Ity_D128); + IRTemp op2 = newTemp(Ity_D128); + IRTemp result = newTemp(Ity_D128); + IRTemp rounding_mode; + + vassert(s390_host_has_dfp); + vassert(m4 == 0 || s390_host_has_fpext); + /* when m4 = 0, S390_DFP_ROUND_PER_FPC_0 should be set. + since S390_DFP_ROUND_PER_FPC_0 is also 0, passing m4 is sufficient */ + rounding_mode = encode_dfp_rounding_mode(m4); + assign(op1, get_dpr_pair(r2)); + assign(op2, get_dpr_pair(r3)); + assign(result, triop(Iop_AddD128, mkexpr(rounding_mode), mkexpr(op1), + mkexpr(op2))); + put_dpr_pair(r1, mkexpr(result)); + + s390_cc_thunk_put1d128(S390_CC_OP_DFP_RESULT_128, result); + + return (m4 == 0) ? "axtr" : "axtra"; +} + +static const HChar * +s390_irgen_CDTR(UChar r1, UChar r2) +{ + IRTemp op1 = newTemp(Ity_D64); + IRTemp op2 = newTemp(Ity_D64); + IRTemp cc_vex = newTemp(Ity_I32); + IRTemp cc_s390 = newTemp(Ity_I32); + + assign(op1, get_dpr_dw0(r1)); + assign(op2, get_dpr_dw0(r2)); + assign(cc_vex, binop(Iop_CmpD64, mkexpr(op1), mkexpr(op2))); + + assign(cc_s390, convert_vex_fpcc_to_s390(cc_vex)); + s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False); + + return "cdtr"; +} + +static const HChar * +s390_irgen_CXTR(UChar r1, UChar r2) +{ + IRTemp op1 = newTemp(Ity_D128); + IRTemp op2 = newTemp(Ity_D128); + IRTemp cc_vex = newTemp(Ity_I32); + IRTemp cc_s390 = newTemp(Ity_I32); + + assign(op1, get_dpr_pair(r1)); + assign(op2, get_dpr_pair(r2)); + assign(cc_vex, binop(Iop_CmpD128, mkexpr(op1), mkexpr(op2))); + + assign(cc_s390, convert_vex_fpcc_to_s390(cc_vex)); + s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False); + + return "cxtr"; +} + static const HChar * s390_irgen_DDTRA(UChar r3, UChar m4, UChar r1, UChar r2) { @@ -9126,6 +9240,112 @@ s390_irgen_DDTRA(UChar r3, UChar m4, UChar r1, UChar r2) return (m4 == 0) ? "ddtr" : "ddtra"; } +static const HChar * +s390_irgen_DXTRA(UChar r3, UChar m4, UChar r1, UChar r2) +{ + IRTemp op1 = newTemp(Ity_D128); + IRTemp op2 = newTemp(Ity_D128); + IRTemp result = newTemp(Ity_D128); + IRTemp rounding_mode; + + vassert(s390_host_has_dfp); + vassert(m4 == 0 || s390_host_has_fpext); + /* when m4 = 0, S390_DFP_ROUND_PER_FPC_0 should be set. + since S390_DFP_ROUND_PER_FPC_0 is also 0, passing m4 is sufficient */ + rounding_mode = encode_dfp_rounding_mode(m4); + assign(op1, get_dpr_pair(r2)); + assign(op2, get_dpr_pair(r3)); + assign(result, triop(Iop_DivD128, mkexpr(rounding_mode), mkexpr(op1), + mkexpr(op2))); + put_dpr_pair(r1, mkexpr(result)); + + return (m4 == 0) ? "dxtr" : "dxtra"; +} + +static const HChar * +s390_irgen_LDETR(UChar m4 __attribute__((unused)), UChar r1, UChar r2) +{ + IRTemp op = newTemp(Ity_D32); + + vassert(s390_host_has_dfp); + + assign(op, get_dpr_w0(r2)); + put_dpr_dw0(r1, unop(Iop_D32toD64, mkexpr(op))); + + return "ldetr"; +} + +static const HChar * +s390_irgen_LXDTR(UChar m4 __attribute__((unused)), UChar r1, UChar r2) +{ + IRTemp op = newTemp(Ity_D64); + + assign(op, get_dpr_dw0(r2)); + put_dpr_pair(r1, unop(Iop_D64toD128, mkexpr(op))); + + return "lxdtr"; +} + +static const HChar * +s390_irgen_LDXTR(UChar m3, UChar m4 __attribute__((unused)), + UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + if (! s390_host_has_fpext && m3 != S390_DFP_ROUND_PER_FPC_0) { + emulation_warning(EmWarn_S390X_fpext_rounding); + m3 = S390_DFP_ROUND_PER_FPC_0; + } + IRTemp result = newTemp(Ity_D64); + + assign(result, binop(Iop_D128toD64, mkexpr(encode_dfp_rounding_mode(m3)), + get_dpr_pair(r2))); + put_dpr_dw0(r1, mkexpr(result)); + + return "ldxtr"; +} + +static const HChar * +s390_irgen_LEDTR(UChar m3, UChar m4 __attribute__((unused)), + UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + if (! s390_host_has_fpext && m3 != S390_DFP_ROUND_PER_FPC_0) { + emulation_warning(EmWarn_S390X_fpext_rounding); + m3 = S390_DFP_ROUND_PER_FPC_0; + } + IRTemp op = newTemp(Ity_D64); + + assign(op, get_dpr_dw0(r2)); + put_dpr_w0(r1, binop(Iop_D64toD32, mkexpr(encode_dfp_rounding_mode(m3)), + mkexpr(op))); + + return "ledtr"; +} + +static const HChar * +s390_irgen_LTDTR(UChar r1, UChar r2) +{ + IRTemp result = newTemp(Ity_D64); + + assign(result, get_dpr_dw0(r2)); + put_dpr_dw0(r1, mkexpr(result)); + s390_cc_thunk_putF(S390_CC_OP_DFP_RESULT_64, result); + + return "ltdtr"; +} + +static const HChar * +s390_irgen_LTXTR(UChar r1, UChar r2) +{ + IRTemp result = newTemp(Ity_D128); + + assign(result, get_dpr_pair(r2)); + put_dpr_pair(r1, mkexpr(result)); + s390_cc_thunk_put1d128(S390_CC_OP_DFP_RESULT_128, result); + + return "ltxtr"; +} + static const HChar * s390_irgen_MDTRA(UChar r3, UChar m4, UChar r1, UChar r2) { @@ -9148,6 +9368,28 @@ s390_irgen_MDTRA(UChar r3, UChar m4, UChar r1, UChar r2) return (m4 == 0) ? "mdtr" : "mdtra"; } +static const HChar * +s390_irgen_MXTRA(UChar r3, UChar m4, UChar r1, UChar r2) +{ + IRTemp op1 = newTemp(Ity_D128); + IRTemp op2 = newTemp(Ity_D128); + IRTemp result = newTemp(Ity_D128); + IRTemp rounding_mode; + + vassert(s390_host_has_dfp); + vassert(m4 == 0 || s390_host_has_fpext); + /* when m4 = 0, S390_DFP_ROUND_PER_FPC_0 should be set. + since S390_DFP_ROUND_PER_FPC_0 is also 0, passing m4 is sufficient */ + rounding_mode = encode_dfp_rounding_mode(m4); + assign(op1, get_dpr_pair(r2)); + assign(op2, get_dpr_pair(r3)); + assign(result, triop(Iop_MulD128, mkexpr(rounding_mode), mkexpr(op1), + mkexpr(op2))); + put_dpr_pair(r1, mkexpr(result)); + + return (m4 == 0) ? "mxtr" : "mxtra"; +} + static const HChar * s390_irgen_SDTRA(UChar r3, UChar m4, UChar r1, UChar r2) { @@ -9171,6 +9413,29 @@ s390_irgen_SDTRA(UChar r3, UChar m4, UChar r1, UChar r2) return (m4 == 0) ? "sdtr" : "sdtra"; } +static const HChar * +s390_irgen_SXTRA(UChar r3, UChar m4, UChar r1, UChar r2) +{ + IRTemp op1 = newTemp(Ity_D128); + IRTemp op2 = newTemp(Ity_D128); + IRTemp result = newTemp(Ity_D128); + IRTemp rounding_mode; + + vassert(s390_host_has_dfp); + vassert(m4 == 0 || s390_host_has_fpext); + /* when m4 = 0, S390_DFP_ROUND_PER_FPC_0 should be set. + since S390_DFP_ROUND_PER_FPC_0 is also 0, passing m4 is sufficient */ + rounding_mode = encode_dfp_rounding_mode(m4); + assign(op1, get_dpr_pair(r2)); + assign(op2, get_dpr_pair(r3)); + assign(result, triop(Iop_SubD128, mkexpr(rounding_mode), mkexpr(op1), + mkexpr(op2))); + put_dpr_pair(r1, mkexpr(result)); + + s390_cc_thunk_put1d128(S390_CC_OP_DFP_RESULT_128, result); + + return (m4 == 0) ? "sxtr" : "sxtra"; +} static const HChar * s390_irgen_CLC(UChar length, IRTemp start1, IRTemp start2) @@ -12720,6 +12985,13 @@ s390_decode_4byte_and_irgen(UChar *bytes) unsigned int r1 : 4; unsigned int r2 : 4; } RRF4; + struct { + unsigned int op : 16; + unsigned int : 4; + unsigned int m4 : 4; + unsigned int r1 : 4; + unsigned int r2 : 4; + } RRF5; struct { unsigned int op : 8; unsigned int r1 : 4; @@ -13196,31 +13468,48 @@ s390_decode_4byte_and_irgen(UChar *bytes) case 0xb3d3: s390_format_RRF_FUFF2(s390_irgen_SDTRA, ovl.fmt.RRF4.r3, ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1, ovl.fmt.RRF4.r2); goto ok; - case 0xb3d4: /* LDETR */ goto unimplemented; - case 0xb3d5: /* LEDTR */ goto unimplemented; + case 0xb3d4: s390_format_RRF_0UFF(s390_irgen_LDETR, ovl.fmt.RRF5.m4, + ovl.fmt.RRF5.r1, ovl.fmt.RRF5.r2); goto ok; + case 0xb3d5: s390_format_RRF_UUFF(s390_irgen_LEDTR, ovl.fmt.RRF2.m3, + ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1, + ovl.fmt.RRF2.r2); goto ok; case 0xb3d6: s390_format_RRE_FF(s390_irgen_LTDTR, ovl.fmt.RRE.r1, ovl.fmt.RRE.r2); goto ok; case 0xb3d7: /* FIDTR */ goto unimplemented; - case 0xb3d8: /* MXTR */ goto unimplemented; - case 0xb3d9: /* DXTR */ goto unimplemented; - case 0xb3da: /* AXTR */ goto unimplemented; - case 0xb3db: /* SXTR */ goto unimplemented; - case 0xb3dc: /* LXDTR */ goto unimplemented; - case 0xb3dd: /* LDXTR */ goto unimplemented; - case 0xb3de: /* LTXTR */ goto unimplemented; + case 0xb3d8: s390_format_RRF_FUFF2(s390_irgen_MXTRA, ovl.fmt.RRF4.r3, + ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1, + ovl.fmt.RRF4.r2); goto ok; + case 0xb3d9: s390_format_RRF_FUFF2(s390_irgen_DXTRA, ovl.fmt.RRF4.r3, + ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1, + ovl.fmt.RRF4.r2); goto ok; + case 0xb3da: s390_format_RRF_FUFF2(s390_irgen_AXTRA, ovl.fmt.RRF4.r3, + ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1, + ovl.fmt.RRF4.r2); goto ok; + case 0xb3db: s390_format_RRF_FUFF2(s390_irgen_SXTRA, ovl.fmt.RRF4.r3, + ovl.fmt.RRF4.m4, ovl.fmt.RRF4.r1, + ovl.fmt.RRF4.r2); goto ok; + case 0xb3dc: s390_format_RRF_0UFF(s390_irgen_LXDTR, ovl.fmt.RRF5.m4, + ovl.fmt.RRF5.r1, ovl.fmt.RRF5.r2); goto ok; + case 0xb3dd: s390_format_RRF_UUFF(s390_irgen_LDXTR, ovl.fmt.RRF2.m3, + ovl.fmt.RRF2.m4, ovl.fmt.RRF2.r1, + ovl.fmt.RRF2.r2); goto ok; + case 0xb3de: s390_format_RRE_FF(s390_irgen_LTXTR, ovl.fmt.RRE.r1, + ovl.fmt.RRE.r2); goto ok; case 0xb3df: /* FIXTR */ goto unimplemented; case 0xb3e0: /* KDTR */ goto unimplemented; case 0xb3e1: /* CGDTR */ goto unimplemented; case 0xb3e2: /* CUDTR */ goto unimplemented; case 0xb3e3: /* CSDTR */ goto unimplemented; - case 0xb3e4: /* CDTR */ goto unimplemented; + case 0xb3e4: s390_format_RRE_FF(s390_irgen_CDTR, ovl.fmt.RRE.r1, + ovl.fmt.RRE.r2); goto ok; case 0xb3e5: /* EEDTR */ goto unimplemented; case 0xb3e7: /* ESDTR */ goto unimplemented; case 0xb3e8: /* KXTR */ goto unimplemented; case 0xb3e9: /* CGXTR */ goto unimplemented; case 0xb3ea: /* CUXTR */ goto unimplemented; case 0xb3eb: /* CSXTR */ goto unimplemented; - case 0xb3ec: /* CXTR */ goto unimplemented; + case 0xb3ec: s390_format_RRE_FF(s390_irgen_CXTR, ovl.fmt.RRE.r1, + ovl.fmt.RRE.r2); goto ok; case 0xb3ed: /* EEXTR */ goto unimplemented; case 0xb3ef: /* ESXTR */ goto unimplemented; case 0xb3f1: /* CDGTR */ goto unimplemented; diff --git a/VEX/priv/host_s390_defs.c b/VEX/priv/host_s390_defs.c index e5f6a842a2..8057ba4ffc 100644 --- a/VEX/priv/host_s390_defs.c +++ b/VEX/priv/host_s390_defs.c @@ -714,6 +714,25 @@ s390_insn_get_reg_usage(HRegUsage *u, const s390_insn *insn) } break; + case S390_INSN_DFP_COMPARE: + addHRegUse(u, HRmWrite, insn->variant.dfp_compare.dst); + addHRegUse(u, HRmRead, insn->variant.dfp_compare.op1_hi); /* left */ + addHRegUse(u, HRmRead, insn->variant.dfp_compare.op2_hi); /* right */ + if (insn->size == 16) { + addHRegUse(u, HRmRead, insn->variant.dfp_compare.op1_lo); /* left */ + addHRegUse(u, HRmRead, insn->variant.dfp_compare.op2_lo); /* right */ + } + break; + + case S390_INSN_DFP_CONVERT: + addHRegUse(u, HRmWrite, insn->variant.dfp_convert.dst_hi); + if (insn->variant.dfp_convert.dst_lo != INVALID_HREG) + addHRegUse(u, HRmWrite, insn->variant.dfp_convert.dst_lo); + addHRegUse(u, HRmRead, insn->variant.dfp_convert.op_hi); /* operand */ + if (insn->variant.dfp_convert.op_lo != INVALID_HREG) + addHRegUse(u, HRmRead, insn->variant.dfp_convert.op_lo); /* operand */ + break; + case S390_INSN_MZERO: s390_amode_get_reg_usage(u, insn->variant.mzero.dst); break; @@ -966,6 +985,34 @@ s390_insn_map_regs(HRegRemap *m, s390_insn *insn) } break; + case S390_INSN_DFP_COMPARE: + insn->variant.dfp_compare.dst = + lookupHRegRemap(m, insn->variant.dfp_compare.dst); + insn->variant.dfp_compare.op1_hi = + lookupHRegRemap(m, insn->variant.dfp_compare.op1_hi); + insn->variant.dfp_compare.op2_hi = + lookupHRegRemap(m, insn->variant.dfp_compare.op2_hi); + if (insn->size == 16) { + insn->variant.dfp_compare.op1_lo = + lookupHRegRemap(m, insn->variant.dfp_compare.op1_lo); + insn->variant.dfp_compare.op2_lo = + lookupHRegRemap(m, insn->variant.dfp_compare.op2_lo); + } + break; + + case S390_INSN_DFP_CONVERT: + insn->variant.dfp_convert.dst_hi = + lookupHRegRemap(m, insn->variant.dfp_convert.dst_hi); + if (insn->variant.dfp_convert.dst_lo != INVALID_HREG) + insn->variant.dfp_convert.dst_lo = + lookupHRegRemap(m, insn->variant.dfp_convert.dst_lo); + insn->variant.dfp_convert.op_hi = + lookupHRegRemap(m, insn->variant.dfp_convert.op_hi); + if (insn->variant.dfp_convert.op_lo != INVALID_HREG) + insn->variant.dfp_convert.op_lo = + lookupHRegRemap(m, insn->variant.dfp_convert.op_lo); + break; + case S390_INSN_MZERO: s390_amode_map_regs(m, insn->variant.mzero.dst); break; @@ -1163,6 +1210,19 @@ emit_RRF4(UChar *p, UInt op, UChar r3, UChar m4, UChar r1, UChar r2) } +static UChar * +emit_RRF5(UChar *p, UInt op, UChar m4, UChar r1, UChar r2) +{ + ULong the_insn = op; + + the_insn |= ((ULong)m4) << 8; + the_insn |= ((ULong)r1) << 4; + the_insn |= ((ULong)r2) << 0; + + return emit_4bytes(p, the_insn); +} + + static UChar * emit_RS(UChar *p, UInt op, UChar r1, UChar r3, UChar b2, UShort d2) { @@ -3894,6 +3954,44 @@ s390_emit_ADTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) } +static UChar * +s390_emit_AXTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + vassert(m4 == 0 || s390_host_has_fpext); + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) { + if (m4 == 0) + s390_disasm(ENC4(MNM, FPR, FPR, FPR), "axtr", r1, r2, r3); + else + s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), "axtra", r1, r2, r3, m4); + } + + return emit_RRF4(p, 0xb3da0000, r3, m4, r1, r2); +} + + +static UChar * +s390_emit_CDTR(UChar *p, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC3(MNM, FPR, FPR), "cdtr", r1, r2); + + return emit_RRE(p, 0xb3e40000, r1, r2); +} + + +static UChar * +s390_emit_CXTR(UChar *p, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC3(MNM, FPR, FPR), "cxtr", r1, r2); + + return emit_RRE(p, 0xb3ec0000, r1, r2); +} + + static UChar * s390_emit_DDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) { @@ -3910,6 +4008,76 @@ s390_emit_DDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) } +static UChar * +s390_emit_DXTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + vassert(m4 == 0 || s390_host_has_fpext); + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) { + if (m4 == 0) + s390_disasm(ENC4(MNM, FPR, FPR, FPR), "dxtr", r1, r2, r3); + else + s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), "dxtra", r1, r2, r3, m4); + } + + return emit_RRF4(p, 0xb3d90000, r3, m4, r1, r2); +} + + +static UChar * +s390_emit_LDETR(UChar *p, UChar m4, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC4(MNM, FPR, FPR, UINT), "ldetr", r1, r2, m4); + + return emit_RRF5(p, 0xb3d40000, m4, r1, r2); +} + + +static UChar * +s390_emit_LXDTR(UChar *p, UChar m4, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC4(MNM, FPR, FPR, UINT), "lxdtr", r1, r2, m4); + + return emit_RRF5(p, 0xb3dc0000, m4, r1, r2); +} + + +static UChar * +s390_emit_LEDTR(UChar *p, UChar m3, UChar m4, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + vassert(m4 == 0); + vassert(m3 == 0 || s390_host_has_fpext); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) { + s390_disasm(ENC5(MNM, FPR, UINT, FPR, UINT), + "ledtr", r1, m3, r2, m4); + } + + return emit_RRF2(p, 0xb3d50000, m3, m4, r1, r2); +} + + +static UChar * +s390_emit_LDXTR(UChar *p, UChar m3, UChar m4, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + vassert(m4 == 0); + vassert(m3 == 0 || s390_host_has_fpext); + + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) { + s390_disasm(ENC5(MNM, FPR, UINT, FPR, UINT), + "ldxtr", r1, m3, r2, m4); + } + + return emit_RRF2(p, 0xb3dd0000, m3, m4, r1, r2); +} + + static UChar * s390_emit_MDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) { @@ -3926,6 +4094,22 @@ s390_emit_MDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) } +static UChar * +s390_emit_MXTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + vassert(m4 == 0 || s390_host_has_fpext); + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) { + if (m4 == 0) + s390_disasm(ENC4(MNM, FPR, FPR, FPR), "mxtr", r1, r2, r3); + else + s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), "mxtra", r1, r2, r3, m4); + } + + return emit_RRF4(p, 0xb3d80000, r3, m4, r1, r2); +} + + static UChar * s390_emit_SDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) { @@ -3942,6 +4126,22 @@ s390_emit_SDTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) } +static UChar * +s390_emit_SXTRA(UChar *p, UChar r3, UChar m4, UChar r1, UChar r2) +{ + vassert(s390_host_has_dfp); + vassert(m4 == 0 || s390_host_has_fpext); + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) { + if (m4 == 0) + s390_disasm(ENC4(MNM, FPR, FPR, FPR), "sxtr", r1, r2, r3); + else + s390_disasm(ENC5(MNM, FPR, FPR, FPR, UINT), "sxtra", r1, r2, r3, m4); + } + + return emit_RRF4(p, 0xb3db0000, r3, m4, r1, r2); +} + + static UChar * s390_emit_LOCGR(UChar *p, UChar m3, UChar r1, UChar r2) { @@ -4914,11 +5114,11 @@ s390_insn_bfp_convert(UChar size, s390_conv_t tag, HReg dst, HReg op, } -/* Check validity of a register pair for 128-bit BFP. Valid register +/* Check validity of a register pair for 128-bit FP. Valid register pairs are (0,2), (1,3), (4, 6), (5, 7), (8, 10), (9, 11), (12, 14), and (13, 15). */ static Bool -is_valid_bfp128_regpair(HReg hi, HReg lo) +is_valid_fp128_regpair(HReg hi, HReg lo) { UInt hi_regno = hregNumber(hi); UInt lo_regno = hregNumber(lo); @@ -4936,8 +5136,8 @@ s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t tag, HReg dst_hi, s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn)); vassert(size == 16); - vassert(is_valid_bfp128_regpair(dst_hi, dst_lo)); - vassert(is_valid_bfp128_regpair(op2_hi, op2_lo)); + vassert(is_valid_fp128_regpair(dst_hi, dst_lo)); + vassert(is_valid_fp128_regpair(op2_hi, op2_lo)); insn->tag = S390_INSN_BFP_BINOP; insn->size = size; @@ -4958,8 +5158,8 @@ s390_insn_bfp128_unop(UChar size, s390_bfp_unop_t tag, HReg dst_hi, s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn)); vassert(size == 16); - vassert(is_valid_bfp128_regpair(dst_hi, dst_lo)); - vassert(is_valid_bfp128_regpair(op_hi, op_lo)); + vassert(is_valid_fp128_regpair(dst_hi, dst_lo)); + vassert(is_valid_fp128_regpair(op_hi, op_lo)); insn->tag = S390_INSN_BFP_UNOP; insn->size = size; @@ -4980,8 +5180,8 @@ s390_insn_bfp128_compare(UChar size, HReg dst, HReg op1_hi, HReg op1_lo, s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn)); vassert(size == 16); - vassert(is_valid_bfp128_regpair(op1_hi, op1_lo)); - vassert(is_valid_bfp128_regpair(op2_hi, op2_lo)); + vassert(is_valid_fp128_regpair(op1_hi, op1_lo)); + vassert(is_valid_fp128_regpair(op2_hi, op2_lo)); insn->tag = S390_INSN_BFP_COMPARE; insn->size = size; @@ -5004,11 +5204,11 @@ s390_insn_bfp128_convert(UChar size, s390_conv_t tag, HReg dst_hi, if (size == 16) { /* From smaller size to 16 bytes */ - vassert(is_valid_bfp128_regpair(dst_hi, dst_lo)); + vassert(is_valid_fp128_regpair(dst_hi, dst_lo)); vassert(op_lo == INVALID_HREG); } else { /* From 16 bytes to smaller size */ - vassert(is_valid_bfp128_regpair(op_hi, op_lo)); + vassert(is_valid_fp128_regpair(op_hi, op_lo)); vassert(dst_lo == INVALID_HREG); } @@ -5071,6 +5271,149 @@ s390_insn_dfp_binop(UChar size, s390_dfp_binop_t tag, HReg dst, HReg op2, } +s390_insn * +s390_insn_dfp_compare(UChar size, HReg dst, HReg op1, HReg op2) +{ + s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn)); + + vassert(size == 8); + + insn->tag = S390_INSN_DFP_COMPARE; + insn->size = size; + insn->variant.dfp_compare.dst = dst; + insn->variant.dfp_compare.op1_hi = op1; + insn->variant.dfp_compare.op2_hi = op2; + insn->variant.dfp_compare.op1_lo = INVALID_HREG; + insn->variant.dfp_compare.op2_lo = INVALID_HREG; + + return insn; +} + + +s390_insn * +s390_insn_dfp_convert(UChar size, s390_dfp_conv_t tag, HReg dst, HReg op, + s390_dfp_round_t rounding_mode) +{ + s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn)); + + vassert(size == 8); + + insn->tag = S390_INSN_DFP_CONVERT; + insn->size = size; + insn->variant.dfp_convert.tag = tag; + insn->variant.dfp_convert.dst_hi = dst; + insn->variant.dfp_convert.op_hi = op; + insn->variant.dfp_convert.dst_lo = INVALID_HREG; + insn->variant.dfp_convert.op_lo = INVALID_HREG; + insn->variant.dfp_convert.rounding_mode = rounding_mode; + + return insn; +} + + +s390_insn * +s390_insn_dfp128_binop(UChar size, s390_dfp_binop_t tag, HReg dst_hi, + HReg dst_lo, HReg op2_hi, HReg op2_lo, HReg op3_hi, + HReg op3_lo, s390_dfp_round_t rounding_mode) +{ + s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn)); + + vassert(size == 16); + vassert(is_valid_fp128_regpair(dst_hi, dst_lo)); + vassert(is_valid_fp128_regpair(op2_hi, op2_lo)); + vassert(is_valid_fp128_regpair(op3_hi, op3_lo)); + + + insn->tag = S390_INSN_DFP_BINOP; + insn->size = size; + insn->variant.dfp_binop.tag = tag; + insn->variant.dfp_binop.dst_hi = dst_hi; + insn->variant.dfp_binop.dst_lo = dst_lo; + insn->variant.dfp_binop.op2_hi = op2_hi; + insn->variant.dfp_binop.op2_lo = op2_lo; + insn->variant.dfp_binop.op3_hi = op3_hi; + insn->variant.dfp_binop.op3_lo = op3_lo; + insn->variant.dfp_binop.rounding_mode = rounding_mode; + + return insn; +} + + +s390_insn * +s390_insn_dfp128_compare(UChar size, HReg dst, HReg op1_hi, HReg op1_lo, + HReg op2_hi, HReg op2_lo) +{ + s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn)); + + vassert(size == 16); + vassert(is_valid_fp128_regpair(op1_hi, op1_lo)); + vassert(is_valid_fp128_regpair(op2_hi, op2_lo)); + + insn->tag = S390_INSN_DFP_COMPARE; + insn->size = size; + insn->variant.dfp_compare.dst = dst; + insn->variant.dfp_compare.op1_hi = op1_hi; + insn->variant.dfp_compare.op1_lo = op1_lo; + insn->variant.dfp_compare.op2_hi = op2_hi; + insn->variant.dfp_compare.op2_lo = op2_lo; + + return insn; +} + + +static s390_insn * +s390_insn_dfp128_convert(UChar size, s390_dfp_conv_t tag, HReg dst_hi, + HReg dst_lo, HReg op_hi, HReg op_lo, + s390_dfp_round_t rounding_mode) +{ + s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn)); + + if (size == 16) { + /* From smaller size to 16 bytes */ + vassert(is_valid_fp128_regpair(dst_hi, dst_lo)); + vassert(op_lo == INVALID_HREG); + } else { + /* From 16 bytes to smaller size */ + vassert(is_valid_fp128_regpair(op_hi, op_lo)); + vassert(dst_lo == INVALID_HREG); + } + + insn->tag = S390_INSN_DFP_CONVERT; + insn->size = size; + insn->variant.dfp_convert.tag = tag; + insn->variant.dfp_convert.dst_hi = dst_hi; + insn->variant.dfp_convert.dst_lo = dst_lo; + insn->variant.dfp_convert.op_hi = op_hi; + insn->variant.dfp_convert.op_lo = op_lo; + insn->variant.dfp_convert.rounding_mode = rounding_mode; + + return insn; +} + + +s390_insn * +s390_insn_dfp128_convert_to(UChar size, s390_dfp_conv_t tag, HReg dst_hi, + HReg dst_lo, HReg op) +{ + /* Conversion to dfp128 never requires a rounding mode. Provide default + rounding mode. It will not be used when emitting insns. */ + s390_dfp_round_t rounding_mode = S390_DFP_ROUND_NEAREST_EVEN_4; + + return s390_insn_dfp128_convert(size, tag, dst_hi, dst_lo, op, + INVALID_HREG, rounding_mode); +} + + +s390_insn * +s390_insn_dfp128_convert_from(UChar size, s390_dfp_conv_t tag, HReg dst, + HReg op_hi, HReg op_lo, + s390_dfp_round_t rounding_mode) +{ + return s390_insn_dfp128_convert(size, tag, dst, INVALID_HREG, op_hi, op_lo, + rounding_mode); +} + + s390_insn * s390_insn_mfence(void) { @@ -5649,6 +5992,24 @@ s390_insn_as_string(const s390_insn *insn) insn->variant.dfp_binop.op3_hi); break; + case S390_INSN_DFP_COMPARE: + s390_sprintf(buf, "%M %R,%R,%R", "v-dcmp", insn->variant.dfp_compare.dst, + insn->variant.dfp_compare.op1_hi, + insn->variant.dfp_compare.op2_hi); + break; + + case S390_INSN_DFP_CONVERT: + switch (insn->variant.dfp_convert.tag) { + case S390_DFP_D32_TO_D64: + case S390_DFP_D64_TO_D32: + case S390_DFP_D64_TO_D128: + case S390_DFP_D128_TO_D64: op = "v-d2d"; break; + default: goto fail; + } + s390_sprintf(buf, "%M %R,%R", op, insn->variant.dfp_convert.dst_hi, + insn->variant.dfp_convert.op_hi); + break; + case S390_INSN_MFENCE: s390_sprintf(buf, "%M", "v-mfence"); return buf; /* avoid printing "size = ..." which is meaningless */ @@ -5778,6 +6139,16 @@ s390_insn_as_string(const s390_insn *insn) goto common; } + case S390_INSN_DFP_CONVERT: + switch (insn->variant.dfp_convert.tag) { + case S390_DFP_D32_TO_D64: p += vex_sprintf(p, "4 -> "); goto common; + case S390_DFP_D64_TO_D32: + case S390_DFP_D64_TO_D128:p += vex_sprintf(p, "8 -> "); goto common; + case S390_DFP_D128_TO_D64:p += vex_sprintf(p, "16 -> "); goto common; + default: + goto common; + } + default: goto common; } @@ -7782,6 +8153,17 @@ s390_insn_dfp_binop_emit(UChar *buf, const s390_insn *insn) default: goto fail; } break; + + case 16: + switch (insn->variant.dfp_binop.tag) { + case S390_DFP_ADD: return s390_emit_AXTRA(buf, r3, m4, r1, r2); + case S390_DFP_SUB: return s390_emit_SXTRA(buf, r3, m4, r1, r2); + case S390_DFP_MUL: return s390_emit_MXTRA(buf, r3, m4, r1, r2); + case S390_DFP_DIV: return s390_emit_DXTRA(buf, r3, m4, r1, r2); + default: goto fail; + } + break; + default: goto fail; } @@ -7790,6 +8172,54 @@ s390_insn_dfp_binop_emit(UChar *buf, const s390_insn *insn) } +static UChar * +s390_insn_dfp_compare_emit(UChar *buf, const s390_insn *insn) +{ + UInt dst = hregNumber(insn->variant.dfp_compare.dst); + UInt r1 = hregNumber(insn->variant.dfp_compare.op1_hi); + UInt r2 = hregNumber(insn->variant.dfp_compare.op2_hi); + + switch (insn->size) { + case 8: buf = s390_emit_CDTR(buf, r1, r2); break; + case 16: buf = s390_emit_CXTR(buf, r1, r2); break; + default: goto fail; + } + + return s390_emit_load_cc(buf, dst); /* Load condition code into DST */ + + fail: + vpanic("s390_insn_dfp_compare_emit"); +} + + +static UChar * +s390_insn_dfp_convert_emit(UChar *buf, const s390_insn *insn) +{ + UInt r1 = hregNumber(insn->variant.dfp_convert.dst_hi); + UInt r2 = hregNumber(insn->variant.dfp_convert.op_hi); + s390_dfp_round_t m3 = insn->variant.dfp_convert.rounding_mode; + /* The IEEE-inexact-exception control is not modelled. So the + m4 field is 0 (which is what GCC does, too) */ + const UInt m4 = 0; + + switch (insn->variant.dfp_convert.tag) { + + /* Load lengthened */ + case S390_DFP_D32_TO_D64: return s390_emit_LDETR(buf, m4, r1, r2); + case S390_DFP_D64_TO_D128: return s390_emit_LXDTR(buf, m4, r1, r2); + + /* Load rounded */ + case S390_DFP_D64_TO_D32: return s390_emit_LEDTR(buf, m3, m4, r1, r2); + case S390_DFP_D128_TO_D64: return s390_emit_LDXTR(buf, m3, m4, r1, r2); + + default: goto fail; + } + + fail: + vpanic("s390_insn_dfp_convert_emit"); +} + + static UChar * s390_insn_mfence_emit(UChar *buf, const s390_insn *insn) { @@ -8383,6 +8813,14 @@ emit_S390Instr(Bool *is_profinc, UChar *buf, Int nbuf, s390_insn *insn, end = s390_insn_dfp_binop_emit(buf, insn); break; + case S390_INSN_DFP_COMPARE: + end = s390_insn_dfp_compare_emit(buf, insn); + break; + + case S390_INSN_DFP_CONVERT: + end = s390_insn_dfp_convert_emit(buf, insn); + break; + case S390_INSN_MFENCE: end = s390_insn_mfence_emit(buf, insn); break; diff --git a/VEX/priv/host_s390_defs.h b/VEX/priv/host_s390_defs.h index 2e93b2fd6a..75e91c77dd 100644 --- a/VEX/priv/host_s390_defs.h +++ b/VEX/priv/host_s390_defs.h @@ -138,6 +138,8 @@ typedef enum { S390_INSN_BFP_COMPARE, S390_INSN_BFP_CONVERT, S390_INSN_DFP_BINOP, /* Decimal floating point */ + S390_INSN_DFP_COMPARE, + S390_INSN_DFP_CONVERT, S390_INSN_MFENCE, S390_INSN_MZERO, /* Assign zero to a memory location */ S390_INSN_GADD, /* Add a value to a guest register */ @@ -199,7 +201,7 @@ typedef enum { S390_BFP_SQRT } s390_bfp_unop_t; -/* Type conversion operations: to and/or from floating point */ +/* Type conversion operations: to and/or from binary floating point */ typedef enum { S390_BFP_I32_TO_F32, S390_BFP_I32_TO_F64, @@ -233,6 +235,13 @@ typedef enum { S390_BFP_F128_TO_F64 } s390_conv_t; +/* Type conversion operations: to and/or from decimal floating point */ +typedef enum { + S390_DFP_D32_TO_D64, + S390_DFP_D64_TO_D32, + S390_DFP_D64_TO_D128, + S390_DFP_D128_TO_D64 +} s390_dfp_conv_t; /* The kind of binary DFP operations */ typedef enum { @@ -507,6 +516,21 @@ typedef struct { HReg op3_lo; /* 128-bit operand low part */ s390_dfp_round_t rounding_mode; } dfp_binop; + struct { + s390_dfp_conv_t tag; + s390_dfp_round_t rounding_mode; + HReg dst_hi; /* 128-bit result high part; 64-bit result */ + HReg dst_lo; /* 128-bit result low part */ + HReg op_hi; /* 128-bit operand high part; 64-bit opnd */ + HReg op_lo; /* 128-bit operand low part */ + } dfp_convert; + struct { + HReg dst; /* condition code in s390 encoding */ + HReg op1_hi; /* 128-bit operand high part; 64-bit opnd 1 */ + HReg op1_lo; /* 128-bit operand low part */ + HReg op2_hi; /* 128-bit operand high part; 64-bit opnd 2 */ + HReg op2_lo; /* 128-bit operand low part */ + } dfp_compare; /* Miscellaneous */ struct { @@ -615,6 +639,20 @@ s390_insn *s390_insn_bfp128_convert_from(UChar size, s390_conv_t, s390_insn *s390_insn_dfp_binop(UChar size, s390_dfp_binop_t, HReg dst, HReg op2, HReg op3, s390_dfp_round_t rounding_mode); +s390_insn *s390_insn_dfp_compare(UChar size, HReg dst, HReg op1, HReg op2); +s390_insn *s390_insn_dfp_convert(UChar size, s390_dfp_conv_t tag, HReg dst, + HReg op, s390_dfp_round_t); +s390_insn *s390_insn_dfp128_binop(UChar size, s390_dfp_binop_t, HReg dst_hi, + HReg dst_lo, HReg op2_hi, HReg op2_lo, + HReg op3_hi, HReg op3_lo, + s390_dfp_round_t rounding_mode); +s390_insn *s390_insn_dfp128_compare(UChar size, HReg dst, HReg op1_hi, + HReg op1_lo, HReg op2_hi, HReg op2_lo); +s390_insn *s390_insn_dfp128_convert_to(UChar size, s390_dfp_conv_t, + HReg dst_hi, HReg dst_lo, HReg op); +s390_insn *s390_insn_dfp128_convert_from(UChar size, s390_dfp_conv_t, + HReg dst, HReg op_hi, HReg op_lo, + s390_dfp_round_t); s390_insn *s390_insn_mfence(void); s390_insn *s390_insn_mzero(UChar size, s390_amode *dst); s390_insn *s390_insn_gadd(UChar size, UInt offset, UChar delta, ULong value); diff --git a/VEX/priv/host_s390_isel.c b/VEX/priv/host_s390_isel.c index 0b1da75f28..6916731bc3 100644 --- a/VEX/priv/host_s390_isel.c +++ b/VEX/priv/host_s390_isel.c @@ -136,6 +136,7 @@ static void s390_isel_int128_expr(HReg *, HReg *, ISelEnv *, IRExpr *); static HReg s390_isel_float_expr(ISelEnv *, IRExpr *); static void s390_isel_float128_expr(HReg *, HReg *, ISelEnv *, IRExpr *); static HReg s390_isel_dfp_expr(ISelEnv *, IRExpr *); +static void s390_isel_dfp128_expr(HReg *, HReg *, ISelEnv *, IRExpr *); static Int @@ -1206,6 +1207,46 @@ s390_isel_int_expr_wrk(ISelEnv *env, IRExpr *expr) return convert_s390_fpcc_to_vex(env, cc_s390); } + case Iop_CmpD64: { + HReg cc_s390, h2; + + h1 = s390_isel_dfp_expr(env, arg1); + h2 = s390_isel_dfp_expr(env, arg2); + cc_s390 = newVRegI(env); + size = 8; + + addInstr(env, s390_insn_dfp_compare(size, cc_s390, h1, h2)); + + return convert_s390_fpcc_to_vex(env, cc_s390); + } + + case Iop_CmpD128: { + HReg op1_hi, op1_lo, op2_hi, op2_lo, f12, f13, f14, f15, cc_s390; + + s390_isel_dfp128_expr(&op1_hi, &op1_lo, env, arg1); /* 1st operand */ + s390_isel_dfp128_expr(&op2_hi, &op2_lo, env, arg2); /* 2nd operand */ + cc_s390 = newVRegI(env); + + /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */ + f12 = make_fpr(12); + f13 = make_fpr(13); + f14 = make_fpr(14); + f15 = make_fpr(15); + + /* 1st operand --> (f12, f14) */ + addInstr(env, s390_insn_move(8, f12, op1_hi)); + addInstr(env, s390_insn_move(8, f14, op1_lo)); + + /* 2nd operand --> (f13, f15) */ + addInstr(env, s390_insn_move(8, f13, op2_hi)); + addInstr(env, s390_insn_move(8, f15, op2_lo)); + + res = newVRegI(env); + addInstr(env, s390_insn_dfp128_compare(16, cc_s390, f12, f14, f13, f15)); + + return convert_s390_fpcc_to_vex(env, cc_s390); + } + case Iop_Add8: case Iop_Add16: case Iop_Add32: @@ -1357,6 +1398,14 @@ s390_isel_int_expr_wrk(ISelEnv *env, IRExpr *expr) return dst; } + if (unop == Iop_ReinterpD64asI64) { + dst = newVRegI(env); + h1 = s390_isel_dfp_expr(env, arg); /* Process the operand */ + addInstr(env, s390_insn_move(size, dst, h1)); + + return dst; + } + /* Expressions whose argument is 1-bit wide */ if (typeOfIRExpr(env->type_env, arg) == Ity_I1) { s390_cc_t cond = s390_isel_cc(env, arg); @@ -2215,6 +2264,189 @@ s390_isel_float_expr(ISelEnv *env, IRExpr *expr) } +/*---------------------------------------------------------*/ +/*--- ISEL: Decimal point expressions (128 bit) ---*/ +/*---------------------------------------------------------*/ +static void +s390_isel_dfp128_expr_wrk(HReg *dst_hi, HReg *dst_lo, ISelEnv *env, + IRExpr *expr) +{ + IRType ty = typeOfIRExpr(env->type_env, expr); + + vassert(ty == Ity_D128); + + switch (expr->tag) { + case Iex_RdTmp: + /* Return the virtual registers that hold the temporary. */ + lookupIRTemp128(dst_hi, dst_lo, env, expr->Iex.RdTmp.tmp); + return; + + /* --------- LOAD --------- */ + case Iex_Load: { + IRExpr *addr_hi, *addr_lo; + s390_amode *am_hi, *am_lo; + + if (expr->Iex.Load.end != Iend_BE) + goto irreducible; + + addr_hi = expr->Iex.Load.addr; + addr_lo = IRExpr_Binop(Iop_Add64, addr_hi, mkU64(8)); + + am_hi = s390_isel_amode(env, addr_hi); + am_lo = s390_isel_amode(env, addr_lo); + + *dst_hi = newVRegF(env); + *dst_lo = newVRegF(env); + addInstr(env, s390_insn_load(8, *dst_hi, am_hi)); + addInstr(env, s390_insn_load(8, *dst_hi, am_lo)); + return; + } + + /* --------- GET --------- */ + case Iex_Get: + /* This is not supported because loading 128-bit from the guest + state is almost certainly wrong. Use get_dpr_pair instead. */ + vpanic("Iex_Get with D128 data"); + + /* --------- 4-ary OP --------- */ + case Iex_Qop: + vpanic("Iex_Qop with D128 data"); + + /* --------- TERNARY OP --------- */ + case Iex_Triop: { + IRTriop *triop = expr->Iex.Triop.details; + IROp op = triop->op; + IRExpr *irrm = triop->arg1; + IRExpr *left = triop->arg2; + IRExpr *right = triop->arg3; + s390_dfp_round_t rounding_mode; + s390_dfp_binop_t dfpop; + HReg op1_hi, op1_lo, op2_hi, op2_lo, f9, f11, f12, f13, f14, f15; + + s390_isel_dfp128_expr(&op1_hi, &op1_lo, env, left); /* 1st operand */ + s390_isel_dfp128_expr(&op2_hi, &op2_lo, env, right); /* 2nd operand */ + + /* We use non-virtual registers as pairs with (f9, f11) as op1, + (f12, f14) as op2 and (f13, f15) as destination) */ + f9 = make_fpr(9); + f11 = make_fpr(11); + f12 = make_fpr(12); + f13 = make_fpr(13); + f14 = make_fpr(14); + f15 = make_fpr(15); + + /* 1st operand --> (f9, f11) */ + addInstr(env, s390_insn_move(8, f9, op1_hi)); + addInstr(env, s390_insn_move(8, f11, op1_lo)); + + /* 2nd operand --> (f12, f14) */ + addInstr(env, s390_insn_move(8, f12, op2_hi)); + addInstr(env, s390_insn_move(8, f14, op2_lo)); + + switch (op) { + case Iop_AddD128: dfpop = S390_DFP_ADD; break; + case Iop_SubD128: dfpop = S390_DFP_SUB; break; + case Iop_MulD128: dfpop = S390_DFP_MUL; break; + case Iop_DivD128: dfpop = S390_DFP_DIV; break; + default: + goto irreducible; + } + + /* DFP binary ops have insns with rounding mode field + when the floating point extension facility is installed. */ + if (s390_host_has_fpext) { + rounding_mode = get_dfp_rounding_mode(env, irrm); + } else { + set_dfp_rounding_mode_in_fpc(env, irrm); + rounding_mode = S390_DFP_ROUND_PER_FPC_0; + } + + addInstr(env, s390_insn_dfp128_binop(16, dfpop, f13, f15, f9, f11, + f12, f14, rounding_mode)); + + /* Move result to virtual destination register */ + *dst_hi = newVRegF(env); + *dst_lo = newVRegF(env); + addInstr(env, s390_insn_move(8, *dst_hi, f13)); + addInstr(env, s390_insn_move(8, *dst_lo, f15)); + + return; + } + + /* --------- BINARY OP --------- */ + case Iex_Binop: { + switch (expr->Iex.Binop.op) { + case Iop_D64HLtoD128: + *dst_hi = s390_isel_dfp_expr(env, expr->Iex.Binop.arg1); + *dst_lo = s390_isel_dfp_expr(env, expr->Iex.Binop.arg2); + return; + + default: + goto irreducible; + } + } + + /* --------- UNARY OP --------- */ + case Iex_Unop: { + IRExpr *left = expr->Iex.Unop.arg; + s390_dfp_conv_t conv; + // HReg op, f12, f13, f14, f15; + HReg op, f12, f14; + + /* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */ + f12 = make_fpr(12); + // f13 = make_fpr(13); + f14 = make_fpr(14); + // f15 = make_fpr(15); + + switch (expr->Iex.Unop.op) { + case Iop_D64toD128: conv = S390_DFP_D64_TO_D128; goto convert_dfp; + default: + goto irreducible; + } + + convert_dfp: + op = s390_isel_dfp_expr(env, left); + addInstr(env, s390_insn_dfp128_convert_to(16, conv, f12, f14, op)); + goto move_dst; + + move_dst: + /* Move result to virtual destination registers */ + *dst_hi = newVRegF(env); + *dst_lo = newVRegF(env); + addInstr(env, s390_insn_move(8, *dst_hi, f12)); + addInstr(env, s390_insn_move(8, *dst_lo, f14)); + return; + } + + default: + goto irreducible; + } + + /* We get here if no pattern matched. */ + irreducible: + ppIRExpr(expr); + vpanic("s390_isel_dfp128_expr_wrk: cannot reduce tree"); + +} + + +/* Compute a 128-bit value into two 64-bit registers. These may be either + real or virtual regs; in any case they must not be changed by subsequent + code emitted by the caller. */ +static void +s390_isel_dfp128_expr(HReg *dst_hi, HReg *dst_lo, ISelEnv *env, IRExpr *expr) +{ + s390_isel_dfp128_expr_wrk(dst_hi, dst_lo, env, expr); + + /* Sanity checks ... */ + vassert(hregIsVirtual(*dst_hi)); + vassert(hregIsVirtual(*dst_lo)); + vassert(hregClass(*dst_hi) == HRcFlt64); + vassert(hregClass(*dst_lo) == HRcFlt64); +} + + /*---------------------------------------------------------*/ /*--- ISEL: Decimal point expressions (64 bit) ---*/ /*---------------------------------------------------------*/ @@ -2225,7 +2457,7 @@ s390_isel_dfp_expr_wrk(ISelEnv *env, IRExpr *expr) IRType ty = typeOfIRExpr(env->type_env, expr); UChar size; - vassert(ty == Ity_D64); + vassert(ty == Ity_D64 || ty == Ity_D32); size = sizeofIRType(ty); @@ -2257,6 +2489,114 @@ s390_isel_dfp_expr_wrk(ISelEnv *env, IRExpr *expr) return dst; } + /* --------- BINARY OP --------- */ + case Iex_Binop: { + IROp op = expr->Iex.Binop.op; + IRExpr *irrm = expr->Iex.Binop.arg1; + IRExpr *left = expr->Iex.Binop.arg2; + HReg h1, dst; + s390_dfp_conv_t conv; + + switch (op) { + case Iop_D64toD32: conv = S390_DFP_D64_TO_D32; goto convert_dfp; + + convert_dfp: + h1 = s390_isel_dfp_expr(env, left); + goto convert; + + convert: { + s390_dfp_round_t rounding_mode; + /* convert-from-fixed and load-rounded have a rounding mode field + when the floating point extension facility is installed. */ + dst = newVRegF(env); + if (s390_host_has_fpext) { + rounding_mode = get_dfp_rounding_mode(env, irrm); + } else { + set_dfp_rounding_mode_in_fpc(env, irrm); + rounding_mode = S390_DFP_ROUND_PER_FPC_0; + } + addInstr(env, s390_insn_dfp_convert(size, conv, dst, h1, + rounding_mode)); + return dst; + } + default: + goto irreducible; + + case Iop_D128toD64: { + HReg op_hi, op_lo, f13, f15; + s390_dfp_round_t rounding_mode; + + conv = S390_DFP_D128_TO_D64; + + s390_isel_dfp128_expr(&op_hi, &op_lo, env, left); + + /* We use non-virtual registers as pairs (f13, f15) */ + f13 = make_fpr(13); + f15 = make_fpr(15); + + /* operand --> (f13, f15) */ + addInstr(env, s390_insn_move(8, f13, op_hi)); + addInstr(env, s390_insn_move(8, f15, op_lo)); + + dst = newVRegF(env); + /* load-rounded has a rounding mode field when the floating point + extension facility is installed. */ + if (s390_host_has_fpext) { + rounding_mode = get_dfp_rounding_mode(env, irrm); + } else { + set_dfp_rounding_mode_in_fpc(env, irrm); + rounding_mode = S390_DFP_ROUND_PER_FPC_0; + } + addInstr(env, s390_insn_dfp128_convert_from(size, conv, dst, f13, f15, + rounding_mode)); + return dst; + } + + } + } + + /* --------- UNARY OP --------- */ + case Iex_Unop: { + IROp op = expr->Iex.Unop.op; + IRExpr *left = expr->Iex.Unop.arg; + s390_dfp_conv_t conv; + HReg h1, dst; + + if (op == Iop_D128HItoD64 || op == Iop_D128LOtoD64) { + HReg dst_hi, dst_lo; + + s390_isel_dfp128_expr(&dst_hi, &dst_lo, env, left); + return op == Iop_D128LOtoD64 ? dst_lo : dst_hi; + } + + if (op == Iop_ReinterpI64asD64) { + dst = newVRegF(env); + h1 = s390_isel_int_expr(env, left); /* Process the operand */ + addInstr(env, s390_insn_move(size, dst, h1)); + + return dst; + } + + switch (op) { + case Iop_D32toD64: conv = S390_DFP_D32_TO_D64; goto convert_dfp1; + + convert_dfp1: + h1 = s390_isel_dfp_expr(env, left); + goto convert1; + + convert1: + dst = newVRegF(env); + /* No rounding mode is needed for these conversions. Just stick + one in. It won't be used later on. */ + addInstr(env, s390_insn_dfp_convert(size, conv, dst, h1, + S390_DFP_ROUND_NEAREST_EVEN_4)); + return dst; + + default: + goto irreducible; + } + } + /* --------- TERNARY OP --------- */ case Iex_Triop: { IRTriop *triop = expr->Iex.Triop.details; @@ -2572,8 +2912,9 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt) break; case Ity_F128: + case Ity_D128: /* Cannot occur. No such instruction */ - vpanic("Ist_Store with F128 data"); + vpanic("Ist_Store with 128-bit floating point data"); default: goto stmt_fail; @@ -2703,9 +3044,11 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt) break; case Ity_F128: - /* Does not occur. See function put_fpr_pair. */ - vpanic("Ist_Put with F128 data"); + case Ity_D128: + /* Does not occur. See function put_(f|d)pr_pair. */ + vpanic("Ist_Put with 128-bit floating point data"); + case Ity_D32: case Ity_D64: src = s390_isel_dfp_expr(env, stmt->Ist.Put.data); break; @@ -2768,11 +3111,23 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt) return; } + case Ity_D32: case Ity_D64: src = s390_isel_dfp_expr(env, stmt->Ist.WrTmp.data); dst = lookupIRTemp(env, tmp); break; + case Ity_D128: { + HReg dst_hi, dst_lo, res_hi, res_lo; + + s390_isel_dfp128_expr(&res_hi, &res_lo, env, stmt->Ist.WrTmp.data); + lookupIRTemp128(&dst_hi, &dst_lo, env, tmp); + + addInstr(env, s390_insn_move(8, dst_hi, res_hi)); + addInstr(env, s390_insn_move(8, dst_lo, res_lo)); + return; + } + default: goto stmt_fail; } @@ -3138,11 +3493,13 @@ iselSB_S390(IRSB *bb, VexArch arch_host, VexArchInfo *archinfo_host, case Ity_F32: case Ity_F64: + case Ity_D32: case Ity_D64: hreg = mkHReg(j++, HRcFlt64, True); break; case Ity_F128: + case Ity_D128: hreg = mkHReg(j++, HRcFlt64, True); hregHI = mkHReg(j++, HRcFlt64, True); break;