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
};
/*------------------------------------------------------------*/
| 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 | |
+--------------------------------+-----------------------+----------------------+-----------------+
*/
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
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;
}
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);
/*------------------------------------------------------------*/
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)
}
+/* 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)
{
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)
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),
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)
{
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)
{
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)
{
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)
{
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)
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;
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;
}
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;
}
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;
}
+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)
{
}
+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)
{
}
+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)
{
}
+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)
{
}
+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)
{
}
-/* 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);
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;
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;
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;
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);
}
}
+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)
{
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 */
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;
}
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;
}
}
+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)
{
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;
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 */
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,
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 {
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 {
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);
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
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:
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);
}
+/*---------------------------------------------------------*/
+/*--- 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) ---*/
/*---------------------------------------------------------*/
IRType ty = typeOfIRExpr(env->type_env, expr);
UChar size;
- vassert(ty == Ity_D64);
+ vassert(ty == Ity_D64 || ty == Ity_D32);
size = sizeofIRType(ty);
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;
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;
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;
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;
}
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;