framework.
Copyright IBM Corp. 2010-2012
+ Copyright (C) 2012-2012 Florian Krohm (britzel@acm.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
case S390_INSN_GADD:
break;
+ case S390_INSN_SET_FPCRM:
+ addHRegUse(u, HRmRead, insn->variant.set_fpcrm.mode);
+ break;
+
case S390_INSN_EVCHECK:
s390_amode_get_reg_usage(u, insn->variant.evcheck.counter);
s390_amode_get_reg_usage(u, insn->variant.evcheck.fail_addr);
case S390_INSN_GADD:
break;
+ case S390_INSN_SET_FPCRM:
+ insn->variant.set_fpcrm.mode =
+ lookupHRegRemap(m, insn->variant.set_fpcrm.mode);
+ break;
+
case S390_INSN_EVCHECK:
s390_amode_map_regs(m, insn->variant.evcheck.counter);
s390_amode_map_regs(m, insn->variant.evcheck.fail_addr);
s390_insn *
s390_insn_bfp_triop(UChar size, s390_bfp_triop_t tag, HReg dst, HReg op2,
- HReg op3, s390_round_t rounding_mode)
+ HReg op3)
{
s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
insn->variant.bfp_triop.dst = dst;
insn->variant.bfp_triop.op2 = op2;
insn->variant.bfp_triop.op3 = op3;
- insn->variant.bfp_triop.rounding_mode = rounding_mode;
return insn;
}
s390_insn *
-s390_insn_bfp_binop(UChar size, s390_bfp_binop_t tag, HReg dst, HReg op2,
- s390_round_t rounding_mode)
+s390_insn_bfp_binop(UChar size, s390_bfp_binop_t tag, HReg dst, HReg op2)
{
s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
insn->variant.bfp_binop.op2_hi = op2;
insn->variant.bfp_binop.dst_lo = INVALID_HREG;
insn->variant.bfp_binop.op2_lo = INVALID_HREG;
- insn->variant.bfp_binop.rounding_mode = rounding_mode;
return insn;
}
s390_insn *
-s390_insn_bfp_unop(UChar size, s390_bfp_unop_t tag, HReg dst, HReg op,
- s390_round_t rounding_mode)
+s390_insn_bfp_unop(UChar size, s390_bfp_unop_t tag, HReg dst, HReg op)
{
s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
insn->variant.bfp_unop.op_hi = op;
insn->variant.bfp_unop.dst_lo = INVALID_HREG;
insn->variant.bfp_unop.op_lo = INVALID_HREG;
- insn->variant.bfp_unop.rounding_mode = rounding_mode;
return insn;
}
s390_insn *
s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t tag, HReg dst_hi,
- HReg dst_lo, HReg op2_hi, HReg op2_lo,
- s390_round_t rounding_mode)
+ HReg dst_lo, HReg op2_hi, HReg op2_lo)
{
s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
insn->variant.bfp_binop.dst_lo = dst_lo;
insn->variant.bfp_binop.op2_hi = op2_hi;
insn->variant.bfp_binop.op2_lo = op2_lo;
- insn->variant.bfp_binop.rounding_mode = rounding_mode;
return insn;
}
s390_insn *
s390_insn_bfp128_unop(UChar size, s390_bfp_unop_t tag, HReg dst_hi,
- HReg dst_lo, HReg op_hi, HReg op_lo,
- s390_round_t rounding_mode)
+ HReg dst_lo, HReg op_hi, HReg op_lo)
{
s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
insn->variant.bfp_unop.dst_lo = dst_lo;
insn->variant.bfp_unop.op_hi = op_hi;
insn->variant.bfp_unop.op_lo = op_lo;
- insn->variant.bfp_unop.rounding_mode = rounding_mode;
return insn;
}
}
+s390_insn *
+s390_insn_set_fpcrm(UChar size, HReg mode)
+{
+ vassert(size == 4);
+
+ s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+ insn->tag = S390_INSN_SET_FPCRM;
+ insn->size = size;
+ insn->variant.set_fpcrm.mode = mode;
+
+ return insn;
+}
+
+
s390_insn *
s390_insn_xdirect(s390_cc_t cond, Addr64 dst, s390_amode *guest_IA,
Bool to_fast_entry)
insn->variant.gadd.value);
break;
+ case S390_INSN_SET_FPCRM:
+ s390_sprintf(buf, "%M %R", "v-set-fpcrm", insn->variant.set_fpcrm.mode);
+ break;
+
case S390_INSN_EVCHECK:
s390_sprintf(buf, "%M counter = %A, fail-addr = %A", "v-evcheck",
insn->variant.evcheck.counter,
}
-/* Little helper function to the rounding mode in the real FPC
- register */
-static UChar *
-s390_set_fpc_rounding_mode(UChar *buf, s390_round_t rounding_mode)
-{
- UChar bits;
-
- /* Determine BFP rounding bits */
- switch (rounding_mode) {
- case S390_ROUND_NEAREST_EVEN: bits = 0; break;
- case S390_ROUND_ZERO: bits = 1; break;
- case S390_ROUND_POSINF: bits = 2; break;
- case S390_ROUND_NEGINF: bits = 3; break;
- default: vpanic("invalid rounding mode\n");
- }
-
- /* Copy FPC from guest state to R0 and OR in the new rounding mode */
- buf = s390_emit_L(buf, R0, 0, S390_REGNO_GUEST_STATE_POINTER,
- S390X_GUEST_OFFSET(guest_fpc)); // r0 = guest_fpc
-
- buf = s390_emit_NILL(buf, R0, 0xFFFC); /* Clear out right-most 2 bits */
- buf = s390_emit_OILL(buf, R0, bits); /* OR in the new rounding mode */
- buf = s390_emit_SFPC(buf, R0, 0); /* Load FPC register from R0 */
-
- return buf;
-}
-
-
static UChar *
s390_insn_bfp_triop_emit(UChar *buf, const s390_insn *insn)
{
UInt r1 = hregNumber(insn->variant.bfp_triop.dst);
UInt r2 = hregNumber(insn->variant.bfp_triop.op2);
UInt r3 = hregNumber(insn->variant.bfp_triop.op3);
- s390_round_t rounding_mode = insn->variant.bfp_triop.rounding_mode;
-
- if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
- buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
- }
switch (insn->size) {
case 4:
switch (insn->variant.bfp_triop.tag) {
- case S390_BFP_MADD: buf = s390_emit_MAEBR(buf, r1, r3, r2); break;
- case S390_BFP_MSUB: buf = s390_emit_MSEBR(buf, r1, r3, r2); break;
+ case S390_BFP_MADD: return s390_emit_MAEBR(buf, r1, r3, r2);
+ case S390_BFP_MSUB: return s390_emit_MSEBR(buf, r1, r3, r2);
default: goto fail;
}
break;
case 8:
switch (insn->variant.bfp_triop.tag) {
- case S390_BFP_MADD: buf = s390_emit_MADBR(buf, r1, r3, r2); break;
- case S390_BFP_MSUB: buf = s390_emit_MSDBR(buf, r1, r3, r2); break;
+ case S390_BFP_MADD: return s390_emit_MADBR(buf, r1, r3, r2);
+ case S390_BFP_MSUB: return s390_emit_MSDBR(buf, r1, r3, r2);
default: goto fail;
}
break;
default: goto fail;
}
- if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
- /* Restore FPC register from guest state */
- buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
- S390X_GUEST_OFFSET(guest_fpc)); // fpc = guest_fpc
- }
- return buf;
-
fail:
vpanic("s390_insn_bfp_triop_emit");
}
{
UInt r1 = hregNumber(insn->variant.bfp_binop.dst_hi);
UInt r2 = hregNumber(insn->variant.bfp_binop.op2_hi);
- s390_round_t rounding_mode = insn->variant.bfp_binop.rounding_mode;
-
- if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
- buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
- }
switch (insn->size) {
case 4:
switch (insn->variant.bfp_binop.tag) {
- case S390_BFP_ADD: buf = s390_emit_AEBR(buf, r1, r2); break;
- case S390_BFP_SUB: buf = s390_emit_SEBR(buf, r1, r2); break;
- case S390_BFP_MUL: buf = s390_emit_MEEBR(buf, r1, r2); break;
- case S390_BFP_DIV: buf = s390_emit_DEBR(buf, r1, r2); break;
+ case S390_BFP_ADD: return s390_emit_AEBR(buf, r1, r2);
+ case S390_BFP_SUB: return s390_emit_SEBR(buf, r1, r2);
+ case S390_BFP_MUL: return s390_emit_MEEBR(buf, r1, r2);
+ case S390_BFP_DIV: return s390_emit_DEBR(buf, r1, r2);
default: goto fail;
}
break;
case 8:
switch (insn->variant.bfp_binop.tag) {
- case S390_BFP_ADD: buf = s390_emit_ADBR(buf, r1, r2); break;
- case S390_BFP_SUB: buf = s390_emit_SDBR(buf, r1, r2); break;
- case S390_BFP_MUL: buf = s390_emit_MDBR(buf, r1, r2); break;
- case S390_BFP_DIV: buf = s390_emit_DDBR(buf, r1, r2); break;
+ case S390_BFP_ADD: return s390_emit_ADBR(buf, r1, r2);
+ case S390_BFP_SUB: return s390_emit_SDBR(buf, r1, r2);
+ case S390_BFP_MUL: return s390_emit_MDBR(buf, r1, r2);
+ case S390_BFP_DIV: return s390_emit_DDBR(buf, r1, r2);
default: goto fail;
}
break;
case 16:
switch (insn->variant.bfp_binop.tag) {
- case S390_BFP_ADD: buf = s390_emit_AXBR(buf, r1, r2); break;
- case S390_BFP_SUB: buf = s390_emit_SXBR(buf, r1, r2); break;
- case S390_BFP_MUL: buf = s390_emit_MXBR(buf, r1, r2); break;
- case S390_BFP_DIV: buf = s390_emit_DXBR(buf, r1, r2); break;
+ case S390_BFP_ADD: return s390_emit_AXBR(buf, r1, r2);
+ case S390_BFP_SUB: return s390_emit_SXBR(buf, r1, r2);
+ case S390_BFP_MUL: return s390_emit_MXBR(buf, r1, r2);
+ case S390_BFP_DIV: return s390_emit_DXBR(buf, r1, r2);
default: goto fail;
}
break;
default: goto fail;
}
- if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
- /* Restore FPC register from guest state */
- buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
- S390X_GUEST_OFFSET(guest_fpc));
- }
- return buf;
-
fail:
vpanic("s390_insn_bfp_binop_emit");
}
{
UInt r1 = hregNumber(insn->variant.bfp_unop.dst_hi);
UInt r2 = hregNumber(insn->variant.bfp_unop.op_hi);
- s390_round_t rounding_mode = insn->variant.bfp_unop.rounding_mode;
-
- /* For all other insns if a special rounding mode is requested,
- we need to set the FPC first and restore it later. */
- if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
- buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
- }
switch (insn->variant.bfp_unop.tag) {
case S390_BFP_ABS:
switch (insn->size) {
- case 4: buf = s390_emit_LPEBR(buf, r1, r2); break;
- case 8: buf = s390_emit_LPDBR(buf, r1, r2); break;
- case 16: buf = s390_emit_LPXBR(buf, r1, r2); break;
+ case 4: return s390_emit_LPEBR(buf, r1, r2);
+ case 8: return s390_emit_LPDBR(buf, r1, r2);
+ case 16: return s390_emit_LPXBR(buf, r1, r2);
default: goto fail;
}
break;
case S390_BFP_NABS:
switch (insn->size) {
- case 4: buf = s390_emit_LNEBR(buf, r1, r2); break;
- case 8: buf = s390_emit_LNDBR(buf, r1, r2); break;
- case 16: buf = s390_emit_LNXBR(buf, r1, r2); break;
+ case 4: return s390_emit_LNEBR(buf, r1, r2);
+ case 8: return s390_emit_LNDBR(buf, r1, r2);
+ case 16: return s390_emit_LNXBR(buf, r1, r2);
default: goto fail;
}
break;
case S390_BFP_NEG:
switch (insn->size) {
- case 4: buf = s390_emit_LCEBR(buf, r1, r2); break;
- case 8: buf = s390_emit_LCDBR(buf, r1, r2); break;
- case 16: buf = s390_emit_LCXBR(buf, r1, r2); break;
+ case 4: return s390_emit_LCEBR(buf, r1, r2);
+ case 8: return s390_emit_LCDBR(buf, r1, r2);
+ case 16: return s390_emit_LCXBR(buf, r1, r2);
default: goto fail;
}
break;
case S390_BFP_SQRT:
switch (insn->size) {
- case 4: buf = s390_emit_SQEBR(buf, r1, r2); break;
- case 8: buf = s390_emit_SQDBR(buf, r1, r2); break;
- case 16: buf = s390_emit_SQXBR(buf, r1, r2); break;
+ case 4: return s390_emit_SQEBR(buf, r1, r2);
+ case 8: return s390_emit_SQDBR(buf, r1, r2);
+ case 16: return s390_emit_SQXBR(buf, r1, r2);
default: goto fail;
}
break;
default: goto fail;
}
- if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
- /* Restore FPC register from guest state */
- buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
- S390X_GUEST_OFFSET(guest_fpc)); // fpc = guest_fpc
- }
- return buf;
-
fail:
vpanic("s390_insn_bfp_unop_emit");
}
{
UInt r1 = hregNumber(insn->variant.bfp_convert.dst_hi);
UInt r2 = hregNumber(insn->variant.bfp_convert.op_hi);
- s390_round_t rounding_mode = insn->variant.bfp_convert.rounding_mode;
- s390_round_t m3 = rounding_mode;
+ s390_round_t m3 = insn->variant.bfp_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.bfp_convert.tag) {
/* Convert to fixed */
case S390_BFP_F128_TO_I64: return s390_emit_CGXBR(buf, m3, r1, r2);
/* Convert to logical */
- /* We leave m4 as 0 - as gcc */
- case S390_BFP_F32_TO_U32: return s390_emit_CLFEBR(buf, m3, 0, r1, r2);
- case S390_BFP_F64_TO_U32: return s390_emit_CLFDBR(buf, m3, 0, r1, r2);
- case S390_BFP_F128_TO_U32: return s390_emit_CLFXBR(buf, m3, 0, r1, r2);
- case S390_BFP_F32_TO_U64: return s390_emit_CLGEBR(buf, m3, 0, r1, r2);
- case S390_BFP_F64_TO_U64: return s390_emit_CLGDBR(buf, m3, 0, r1, r2);
- case S390_BFP_F128_TO_U64: return s390_emit_CLGXBR(buf, m3, 0, r1, r2);
-
- /* Conversion to 128-bit never requires a rounding mode */
- case S390_BFP_I32_TO_F128: return s390_emit_CXFBRA(buf, 0, 0, r1, r2);
- case S390_BFP_I64_TO_F128: return s390_emit_CXGBRA(buf, 0, 0, r1, r2);
- case S390_BFP_U32_TO_F128: return s390_emit_CXLFBR(buf, 0, 0, r1, r2);
- case S390_BFP_U64_TO_F128: return s390_emit_CXLGBR(buf, 0, 0, r1, r2);
+ case S390_BFP_F32_TO_U32: return s390_emit_CLFEBR(buf, m3, m4, r1, r2);
+ case S390_BFP_F64_TO_U32: return s390_emit_CLFDBR(buf, m3, m4, r1, r2);
+ case S390_BFP_F128_TO_U32: return s390_emit_CLFXBR(buf, m3, m4, r1, r2);
+ case S390_BFP_F32_TO_U64: return s390_emit_CLGEBR(buf, m3, m4, r1, r2);
+ case S390_BFP_F64_TO_U64: return s390_emit_CLGDBR(buf, m3, m4, r1, r2);
+ case S390_BFP_F128_TO_U64: return s390_emit_CLGXBR(buf, m3, m4, r1, r2);
+
+ /* Convert from fixed */
+ case S390_BFP_I32_TO_F32: return s390_emit_CEFBRA(buf, m3, m4, r1, r2);
+ case S390_BFP_I32_TO_F64: return s390_emit_CDFBRA(buf, 0, m4, r1, r2);
+ case S390_BFP_I32_TO_F128: return s390_emit_CXFBRA(buf, 0, m4, r1, r2);
+ case S390_BFP_I64_TO_F32: return s390_emit_CEGBRA(buf, m3, m4, r1, r2);
+ case S390_BFP_I64_TO_F64: return s390_emit_CDGBRA(buf, m3, m4, r1, r2);
+ case S390_BFP_I64_TO_F128: return s390_emit_CXGBRA(buf, 0, m4, r1, r2);
+
+ /* Convert from logical */
+ case S390_BFP_U32_TO_F32: return s390_emit_CELFBR(buf, m3, m4, r1, r2);
+ case S390_BFP_U32_TO_F64: return s390_emit_CDLFBR(buf, m3, m4, r1, r2);
+ case S390_BFP_U32_TO_F128: return s390_emit_CXLFBR(buf, m3, m4, r1, r2);
+ case S390_BFP_U64_TO_F32: return s390_emit_CELGBR(buf, m3, m4, r1, r2);
+ case S390_BFP_U64_TO_F64: return s390_emit_CDLGBR(buf, m3, m4, r1, r2);
+ case S390_BFP_U64_TO_F128: return s390_emit_CXLGBR(buf, m3, m4, r1, r2);
+
+ /* Load lengthened */
+ case S390_BFP_F32_TO_F64: return s390_emit_LDEBR(buf, r1, r2);
case S390_BFP_F32_TO_F128: return s390_emit_LXEBR(buf, r1, r2);
case S390_BFP_F64_TO_F128: return s390_emit_LXDBR(buf, r1, r2);
- default: break;
- }
- /* For all other insns if a special rounding mode is requested,
- we need to set the FPC first and restore it later. */
- if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
- buf = s390_set_fpc_rounding_mode(buf, rounding_mode);
- }
-
- switch (insn->variant.bfp_convert.tag) {
- case S390_BFP_I32_TO_F32: buf = s390_emit_CEFBRA(buf, 0, 0, r1, r2); break;
- case S390_BFP_I32_TO_F64: buf = s390_emit_CDFBRA(buf, 0, 0, r1, r2); break;
- case S390_BFP_I32_TO_F128: buf = s390_emit_CXFBRA(buf, 0, 0, r1, r2); break;
- case S390_BFP_I64_TO_F32: buf = s390_emit_CEGBRA(buf, 0, 0, r1, r2); break;
- case S390_BFP_I64_TO_F64: buf = s390_emit_CDGBRA(buf, 0, 0, r1, r2); break;
- case S390_BFP_I64_TO_F128: buf = s390_emit_CXGBRA(buf, 0, 0, r1, r2); break;
-
- /* We leave m4 as 0 - as gcc */
- case S390_BFP_U32_TO_F32: buf = s390_emit_CELFBR(buf, m3, 0, r1, r2); break;
- case S390_BFP_U32_TO_F64: buf = s390_emit_CDLFBR(buf, m3, 0, r1, r2); break;
- case S390_BFP_U32_TO_F128: buf = s390_emit_CXLFBR(buf, m3, 0, r1, r2); break;
- case S390_BFP_U64_TO_F32: buf = s390_emit_CELGBR(buf, m3, 0, r1, r2); break;
- case S390_BFP_U64_TO_F64: buf = s390_emit_CDLGBR(buf, m3, 0, r1, r2); break;
- case S390_BFP_U64_TO_F128: buf = s390_emit_CXLGBR(buf, m3, 0, r1, r2); break;
-
- case S390_BFP_F32_TO_F64: buf = s390_emit_LDEBR(buf, r1, r2); break;
- case S390_BFP_F32_TO_F128: buf = s390_emit_LXEBR(buf, r1, r2); break;
- case S390_BFP_F64_TO_F32: buf = s390_emit_LEDBRA(buf, 0, 0, r1, r2); break;
- case S390_BFP_F64_TO_F128: buf = s390_emit_LXDBR(buf, r1, r2); break;
-
- case S390_BFP_F128_TO_F32: buf = s390_emit_LEXBRA(buf, 0, 0, r1, r2); break;
- case S390_BFP_F128_TO_F64: buf = s390_emit_LDXBRA(buf, 0, 0, r1, r2); break;
+ /* Load rounded */
+ case S390_BFP_F64_TO_F32: return s390_emit_LEDBRA(buf, m3, m4, r1, r2);
+ case S390_BFP_F128_TO_F32: return s390_emit_LEXBRA(buf, m3, m4, r1, r2);
+ case S390_BFP_F128_TO_F64: return s390_emit_LDXBRA(buf, m3, m4, r1, r2);
default: goto fail;
}
- if (rounding_mode != S390_ROUND_NEAREST_EVEN) {
- /* Restore FPC register from guest state */
- buf = s390_emit_LFPC(buf, S390_REGNO_GUEST_STATE_POINTER,
- S390X_GUEST_OFFSET(guest_fpc)); // fpc = guest_fpc
- }
- return buf;
-
fail:
vpanic("s390_insn_bfp_convert_emit");
}
}
+static UChar *
+s390_insn_set_fpcrm_emit(UChar *buf, const s390_insn *insn)
+{
+ UInt mode = hregNumber(insn->variant.set_fpcrm.mode);
+
+ /* Copy FPC from guest state to R0 and OR in the new rounding mode */
+ buf = s390_emit_L(buf, R0, 0, S390_REGNO_GUEST_STATE_POINTER,
+ S390X_GUEST_OFFSET(guest_fpc)); // r0 = guest_fpc
+
+ buf = s390_emit_NILL(buf, R0, 0xFFF8); /* Clear out right-most 3 bits */
+ buf = s390_emit_OR(buf, R0, mode); /* OR in the new rounding mode */
+ buf = s390_emit_SFPC(buf, R0, 0); /* Load FPC register from R0 */
+
+ return buf;
+}
+
+
/* Define convenience functions needed for translation chaining.
Any changes need to be applied to the functions in concert. */
end = s390_insn_gadd_emit(buf, insn);
break;
+ case S390_INSN_SET_FPCRM:
+ end = s390_insn_set_fpcrm_emit(buf, insn);
+ break;
+
case S390_INSN_PROFINC:
end = s390_insn_profinc_emit(buf, insn);
/* Tell the caller .. */
S390_INSN_MFENCE,
S390_INSN_GZERO, /* Assign zero to a guest register */
S390_INSN_GADD, /* Add a value to a guest register */
+ S390_INSN_SET_FPCRM, /* Set the rounding mode in the FPC */
/* The following 5 insns are mandated by translation chaining */
S390_INSN_XDIRECT, /* direct transfer to guest address */
S390_INSN_XINDIR, /* indirect transfer to guest address */
} s390_round_t;
+/* Rounding mode as it is encoded in bits [29:31] of the FPC register.
+ Only rounding modes 0..3 are universally supported. Others require
+ additional hardware facilities. */
+typedef enum {
+ S390_FPC_ROUND_NEAREST_EVEN = 0,
+ S390_FPC_ROUND_ZERO = 1,
+ S390_FPC_ROUND_POSINF = 2,
+ S390_FPC_ROUND_NEGINF = 3,
+ /* 4,5,6 are not allowed */
+ S390_FPC_ROUND_PREPARE_SHORT = 7
+} s390_fpc_round_t;
+
+
/* Invert the condition code */
static __inline__ s390_cc_t
s390_cc_invert(s390_cc_t cond)
to store only one register of the pair in order to represent it.
We chose not to do that as being explicit about all registers
helps with debugging and does not require special handling in
- e.g. s390_insn_get_reg_usage, It'd be all to easy to forget about
+ e.g. s390_insn_get_reg_usage, It'd be all too easy to forget about
the "other" register in a pair if it is implicit.
The convention for all fp s390_insn is that the _hi register will
/* There are currently no ternary 128-bit BFP operations. */
struct {
s390_bfp_triop_t tag;
- s390_round_t rounding_mode;
HReg dst;
HReg op2;
HReg op3;
} bfp_triop;
struct {
s390_bfp_binop_t tag;
- s390_round_t rounding_mode;
HReg dst_hi; /* 128-bit result high part; 32/64-bit result */
HReg dst_lo; /* 128-bit result low part */
HReg op2_hi; /* 128-bit operand high part; 32/64-bit opnd */
} bfp_binop;
struct {
s390_bfp_unop_t tag;
- s390_round_t rounding_mode;
HReg dst_hi; /* 128-bit result high part; 32/64-bit result */
HReg dst_lo; /* 128-bit result low part */
HReg op_hi; /* 128-bit operand high part; 32/64-bit opnd */
UChar delta;
ULong value; /* for debugging only */
} gadd;
+ struct {
+ HReg mode;
+ } set_fpcrm;
/* The next 5 entries are generic to support translation chaining */
Bool signed_comparison);
s390_insn *s390_insn_helper_call(s390_cc_t cond, Addr64 target, UInt num_args,
HChar *name, HReg dst);
-s390_insn *s390_insn_bfp_triop(UChar size, s390_bfp_triop_t, HReg dst, HReg op2,
- HReg op3, s390_round_t);
-s390_insn *s390_insn_bfp_binop(UChar size, s390_bfp_binop_t, HReg dst, HReg op2,
- s390_round_t);
+s390_insn *s390_insn_bfp_triop(UChar size, s390_bfp_triop_t, HReg dst,
+ HReg op2, HReg op3);
+s390_insn *s390_insn_bfp_binop(UChar size, s390_bfp_binop_t, HReg dst,
+ HReg op2);
s390_insn *s390_insn_bfp_unop(UChar size, s390_bfp_unop_t tag, HReg dst,
- HReg op, s390_round_t);
+ HReg op);
s390_insn *s390_insn_bfp_compare(UChar size, HReg dst, HReg op1, HReg op2);
s390_insn *s390_insn_bfp_convert(UChar size, s390_conv_t tag, HReg dst,
HReg op, s390_round_t);
s390_insn *s390_insn_bfp128_binop(UChar size, s390_bfp_binop_t, HReg dst_hi,
- HReg dst_lo, HReg op2_hi, HReg op2_lo,
- s390_round_t);
+ HReg dst_lo, HReg op2_hi, HReg op2_lo);
s390_insn *s390_insn_bfp128_unop(UChar size, s390_bfp_unop_t, HReg dst_hi,
- HReg dst_lo, HReg op_hi, HReg op_lo,
- s390_round_t);
+ HReg dst_lo, HReg op_hi, HReg op_lo);
s390_insn *s390_insn_bfp128_compare(UChar size, HReg dst, HReg op1_hi,
HReg op1_lo, HReg op2_hi, HReg op2_lo);
s390_insn *s390_insn_bfp128_convert_to(UChar size, s390_conv_t,
s390_insn *s390_insn_mfence(void);
s390_insn *s390_insn_gzero(UChar size, UInt offset);
s390_insn *s390_insn_gadd(UChar size, UInt offset, UChar delta, ULong value);
+s390_insn *s390_insn_set_fpcrm(UChar size, HReg mode);
/* Five for translation chaining */
s390_insn *s390_insn_xdirect(s390_cc_t cond, Addr64 dst, s390_amode *guest_IA,
framework.
Copyright IBM Corp. 2010-2012
+ Copyright (C) 2012-2012 Florian Krohm (britzel@acm.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
UInt vreg_ctr;
UInt hwcaps;
+ IRExpr *previous_bfp_rounding_mode;
+
ULong old_value[NUM_TRACKED_REGS];
/* The next two are for translation chaining */
}
-/* Given an expression representing a rounding mode using IRRoundingMode
- encoding convert it to an s390_round_t value. */
+/*---------------------------------------------------------*/
+/*--- BFP helper functions ---*/
+/*---------------------------------------------------------*/
+
+/* Set the BFP rounding mode in the FPC. This function is called for
+ all non-conversion BFP instructions as those will always get the
+ rounding mode from the FPC. */
+static void
+set_bfp_rounding_mode_in_fpc(ISelEnv *env, IRExpr *irrm)
+{
+ vassert(typeOfIRExpr(env->type_env, irrm) == Ity_I32);
+
+ /* Do we need to do anything? */
+ if (env->previous_bfp_rounding_mode &&
+ env->previous_bfp_rounding_mode->tag == Iex_RdTmp &&
+ irrm->tag == Iex_RdTmp &&
+ env->previous_bfp_rounding_mode->Iex.RdTmp.tmp == irrm->Iex.RdTmp.tmp) {
+ /* No - new mode is identical to previous mode. */
+ return;
+ }
+
+ /* No luck - we better set it, and remember what we set it to. */
+ env->previous_bfp_rounding_mode = irrm;
+
+ /* The incoming rounding mode is in VEX IR encoding. Need to change
+ to s390.
+
+ rounding mode | s390 | IR
+ -------------------------
+ to nearest | 00 | 00
+ to zero | 01 | 11
+ to +infinity | 10 | 10
+ to -infinity | 11 | 01
+
+ So: s390 = (4 - IR) & 3
+ */
+ HReg ir = s390_isel_int_expr(env, irrm);
+
+ HReg mode = newVRegI(env);
+
+ addInstr(env, s390_insn_load_immediate(4, mode, 4));
+ addInstr(env, s390_insn_alu(4, S390_ALU_SUB, mode, s390_opnd_reg(ir)));
+ addInstr(env, s390_insn_alu(4, S390_ALU_AND, mode, s390_opnd_imm(3)));
+
+ addInstr(env, s390_insn_set_fpcrm(4, mode));
+}
+
+
+/* This function is invoked for insns that support a specification of
+ a rounding mode in the insn itself. In that case there is no need to
+ stick the rounding mode into the FPC -- a good thing. However, the
+ rounding mode must be known. */
static s390_round_t
-decode_rounding_mode(IRExpr *rounding_expr)
+get_bfp_rounding_mode(ISelEnv *env, IRExpr *irrm)
{
- if (rounding_expr->tag == Iex_Const &&
- rounding_expr->Iex.Const.con->tag == Ico_U32) {
- IRRoundingMode mode = rounding_expr->Iex.Const.con->Ico.U32;
+ if (irrm->tag == Iex_Const) { /* rounding mode is known */
+ vassert(irrm->Iex.Const.con->tag == Ico_U32);
+ IRRoundingMode mode = irrm->Iex.Const.con->Ico.U32;
switch (mode) {
- case Irrm_NEAREST: return S390_ROUND_NEAREST_EVEN;
- case Irrm_ZERO: return S390_ROUND_ZERO;
- case Irrm_PosINF: return S390_ROUND_POSINF;
- case Irrm_NegINF: return S390_ROUND_NEGINF;
+ case Irrm_NEAREST: return S390_ROUND_NEAREST_EVEN;
+ case Irrm_ZERO: return S390_ROUND_ZERO;
+ case Irrm_PosINF: return S390_ROUND_POSINF;
+ case Irrm_NegINF: return S390_ROUND_NEGINF;
+ default:
+ vpanic("get_bfp_rounding_mode");
}
}
- vpanic("decode_rounding_mode");
+ set_bfp_rounding_mode_in_fpc(env, irrm);
+ return S390_ROUND_PER_FPC;
}
res = newVRegI(env);
h1 = s390_isel_float_expr(env, arg2); /* Process operand */
- rounding_mode = decode_rounding_mode(arg1);
- addInstr(env, s390_insn_bfp_convert(size, conv, res, h1, rounding_mode));
+ rounding_mode = get_bfp_rounding_mode(env, arg1);
+ addInstr(env, s390_insn_bfp_convert(size, conv, res, h1,
+ rounding_mode));
return res;
}
addInstr(env, s390_insn_move(8, f13, op_hi));
addInstr(env, s390_insn_move(8, f15, op_lo));
- rounding_mode = decode_rounding_mode(arg1);
+ rounding_mode = get_bfp_rounding_mode(env, arg1);
addInstr(env, s390_insn_bfp128_convert_from(size, conv, res, f13, f15,
rounding_mode));
return res;
IRExpr *left = triop->arg2;
IRExpr *right = triop->arg3;
s390_bfp_binop_t bfpop;
- s390_round_t rounding_mode;
HReg op1_hi, op1_lo, op2_hi, op2_lo, f12, f13, f14, f15;
s390_isel_float128_expr(&op1_hi, &op1_lo, env, left); /* 1st operand */
goto irreducible;
}
- rounding_mode = decode_rounding_mode(triop->arg1);
- addInstr(env, s390_insn_bfp128_binop(16, bfpop, f12, f14, f13,
- f15, rounding_mode));
+ set_bfp_rounding_mode_in_fpc(env, triop->arg1);
+ addInstr(env, s390_insn_bfp128_binop(16, bfpop, f12, f14, f13, f15));
/* Move result to virtual destination register */
*dst_hi = newVRegF(env);
/* --------- BINARY OP --------- */
case Iex_Binop: {
HReg op_hi, op_lo, f12, f13, f14, f15;
- s390_bfp_unop_t bfpop;
- s390_round_t rounding_mode;
/* We use non-virtual registers as pairs (f13, f15) and (f12, f14)) */
f12 = make_fpr(12);
addInstr(env, s390_insn_move(8, f13, op_hi));
addInstr(env, s390_insn_move(8, f15, op_lo));
- bfpop = S390_BFP_SQRT;
- rounding_mode = decode_rounding_mode(expr->Iex.Binop.arg1);
-
- addInstr(env, s390_insn_bfp128_unop(16, bfpop, f12, f14, f13, f15,
- rounding_mode));
+ set_bfp_rounding_mode_in_fpc(env, expr->Iex.Binop.arg1);
+ addInstr(env, s390_insn_bfp128_unop(16, S390_BFP_SQRT, f12, f14,
+ f13, f15));
/* Move result to virtual destination registers */
*dst_hi = newVRegF(env);
case Iex_Unop: {
IRExpr *left = expr->Iex.Unop.arg;
s390_bfp_unop_t bfpop;
- s390_round_t rounding_mode;
s390_conv_t conv;
HReg op_hi, op_lo, op, f12, f13, f14, f15;
addInstr(env, s390_insn_move(8, f13, op_hi));
addInstr(env, s390_insn_move(8, f15, op_lo));
- rounding_mode = S390_ROUND_NEAREST_EVEN; /* will not be used later on */
- addInstr(env, s390_insn_bfp128_unop(16, bfpop, f12, f14, f13, f15,
- rounding_mode));
+ addInstr(env, s390_insn_bfp128_unop(16, bfpop, f12, f14, f13, f15));
goto move_dst;
convert_float:
case Iex_Qop: {
HReg op1, op2, op3, dst;
s390_bfp_triop_t bfpop;
- s390_round_t rounding_mode;
op1 = s390_isel_float_expr(env, expr->Iex.Qop.details->arg2);
op2 = s390_isel_float_expr(env, expr->Iex.Qop.details->arg3);
goto irreducible;
}
- rounding_mode = decode_rounding_mode(expr->Iex.Qop.details->arg1);
- addInstr(env, s390_insn_bfp_triop(size, bfpop, dst, op2, op3,
- rounding_mode));
+ set_bfp_rounding_mode_in_fpc(env, expr->Iex.Qop.details->arg1);
+ addInstr(env, s390_insn_bfp_triop(size, bfpop, dst, op2, op3));
return dst;
}
IRExpr *left = triop->arg2;
IRExpr *right = triop->arg3;
s390_bfp_binop_t bfpop;
- s390_round_t rounding_mode;
HReg h1, op2, dst;
h1 = s390_isel_float_expr(env, left); /* Process 1st operand */
goto irreducible;
}
- rounding_mode = decode_rounding_mode(triop->arg1);
- addInstr(env, s390_insn_bfp_binop(size, bfpop, dst, op2, rounding_mode));
+ set_bfp_rounding_mode_in_fpc(env, triop->arg1);
+ addInstr(env, s390_insn_bfp_binop(size, bfpop, dst, op2));
return dst;
}
IRExpr *irrm = expr->Iex.Binop.arg1;
IRExpr *left = expr->Iex.Binop.arg2;
HReg h1, dst;
- s390_round_t rounding_mode;
s390_conv_t conv;
switch (op) {
case Iop_SqrtF64:
h1 = s390_isel_float_expr(env, left);
dst = newVRegF(env);
- rounding_mode = decode_rounding_mode(irrm);
- addInstr(env, s390_insn_bfp_unop(size, S390_BFP_SQRT, dst, h1,
- rounding_mode));
+ set_bfp_rounding_mode_in_fpc(env, irrm);
+ addInstr(env, s390_insn_bfp_unop(size, S390_BFP_SQRT, dst, h1));
return dst;
case Iop_F64toF32: conv = S390_BFP_F64_TO_F32; goto convert_float;
h1 = s390_isel_int_expr(env, left);
goto convert;
- convert:
+ convert: {
+ s390_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);
- rounding_mode = decode_rounding_mode(irrm);
+ if (s390_host_has_fpext) {
+ rounding_mode = get_bfp_rounding_mode(env, irrm);
+ } else {
+ set_bfp_rounding_mode_in_fpc(env, irrm);
+ rounding_mode = S390_ROUND_PER_FPC;
+ }
addInstr(env, s390_insn_bfp_convert(size, conv, dst, h1,
rounding_mode));
return dst;
+ }
default:
goto irreducible;
case Iop_F128toF64:
case Iop_F128toF32: {
HReg op_hi, op_lo, f13, f15;
+ s390_round_t rounding_mode;
conv = op == Iop_F128toF32 ? S390_BFP_F128_TO_F32
: S390_BFP_F128_TO_F64;
- rounding_mode = decode_rounding_mode(irrm);
-
s390_isel_float128_expr(&op_hi, &op_lo, env, left);
/* We use non-virtual registers as pairs (f13, f15) */
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_bfp_rounding_mode(env, irrm);
+ } else {
+ set_bfp_rounding_mode_in_fpc(env, irrm);
+ rounding_mode = S390_ROUND_PER_FPC;
+ }
addInstr(env, s390_insn_bfp128_convert_from(size, conv, dst, f13, f15,
rounding_mode));
return dst;
IROp op = expr->Iex.Unop.op;
IRExpr *left = expr->Iex.Unop.arg;
s390_bfp_unop_t bfpop;
- /* No rounding mode is needed for these conversions. Provide the
- default rounding mode. It will not be used. */
- s390_round_t rounding_mode = S390_ROUND_NEAREST_EVEN;
s390_conv_t conv;
HReg h1, dst;
convert1:
dst = newVRegF(env);
- addInstr(env, s390_insn_bfp_convert(size, conv, dst, h1, rounding_mode));
+ /* No rounding mode is needed for these conversions. Just stick
+ one in. It won't be used later on. */
+ addInstr(env, s390_insn_bfp_convert(size, conv, dst, h1,
+ S390_ROUND_NEAREST_EVEN));
return dst;
default:
/* Process operand */
h1 = s390_isel_float_expr(env, left);
dst = newVRegF(env);
- addInstr(env, s390_insn_bfp_unop(size, bfpop, dst, h1, rounding_mode));
+ addInstr(env, s390_insn_bfp_unop(size, bfpop, dst, h1));
return dst;
}
env->vregmap = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
env->vregmapHI = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
+ env->previous_bfp_rounding_mode = NULL;
+
/* and finally ... */
env->hwcaps = hwcaps_host;