Add machinery to support DFP rounding modes.
Part of fixing BZ 307113.
Patch by Maran <maranp@linux.vnet.ibm.com>
git-svn-id: svn://svn.valgrind.org/vex/trunk@2557 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_s390_toIR.c b/priv/guest_s390_toIR.c
index bde121b..0748685 100644
--- a/priv/guest_s390_toIR.c
+++ b/priv/guest_s390_toIR.c
@@ -1465,6 +1465,84 @@
return mktemp(Ity_I32, rm);
}
+/* Extract the DFP rounding mode from the guest FPC reg and encode it as an
+ IRRoundingMode:
+
+ rounding mode | s390 | IR
+ ------------------------------------------------
+ to nearest, ties to even | 000 | 000
+ to zero | 001 | 011
+ to +infinity | 010 | 010
+ to -infinity | 011 | 001
+ to nearest, ties away from 0 | 100 | 100
+ to nearest, ties toward 0 | 101 | 111
+ to away from 0 | 110 | 110
+ to prepare for shorter precision | 111 | 101
+
+ So: IR = (s390 ^ ((s390 << 1) & 2))
+*/
+#if 0 // fixs390: avoid compiler warnings about unused function
+static IRExpr *
+get_dfp_rounding_mode_from_fpc(void)
+{
+ IRTemp fpc_bits = newTemp(Ity_I32);
+
+ /* The dfp rounding mode is stored in bits [25:27].
+ extract the bits at 25:27 and right shift 4 times. */
+ assign(fpc_bits, binop(Iop_Shr32,
+ binop(Iop_And32, get_fpc_w0(), mkU32(0x70)),
+ mkU8(4)));
+
+ IRExpr *rm_s390 = mkexpr(fpc_bits);
+ // rm_IR = (rm_s390 ^ ((rm_s390 << 1) & 2));
+
+ return binop(Iop_Xor32, rm_s390,
+ binop( Iop_And32,
+ binop(Iop_Shl32, rm_s390, mkU8(1)),
+ mkU32(2)));
+}
+
+/* Encode the s390 rounding mode as it appears in the m3 field of certain
+ instructions to VEX's IRRoundingMode. */
+static IRTemp
+encode_dfp_rounding_mode(UChar mode)
+{
+ IRExpr *rm;
+
+ switch (mode) {
+ case S390_DFP_ROUND_PER_FPC_0:
+ case S390_DFP_ROUND_PER_FPC_2:
+ rm = get_dfp_rounding_mode_from_fpc(); break;
+ case S390_DFP_ROUND_NEAREST_EVEN_4:
+ case S390_DFP_ROUND_NEAREST_EVEN_8:
+ rm = mkU32(Irrm_DFP_NEAREST); break;
+ case S390_DFP_ROUND_NEAREST_TIE_AWAY_0_1:
+ case S390_DFP_ROUND_NEAREST_TIE_AWAY_0_12:
+ rm = mkU32(Irrm_DFP_NEAREST_TIE_AWAY_0); break;
+ case S390_DFP_ROUND_PREPARE_SHORT_3:
+ case S390_DFP_ROUND_PREPARE_SHORT_15:
+ rm = mkU32(Irrm_DFP_PREPARE_SHORTER); break;
+ case S390_DFP_ROUND_ZERO_5:
+ case S390_DFP_ROUND_ZERO_9:
+ rm = mkU32(Irrm_DFP_ZERO ); break;
+ case S390_DFP_ROUND_POSINF_6:
+ case S390_DFP_ROUND_POSINF_10:
+ rm = mkU32(Irrm_DFP_PosINF); break;
+ case S390_DFP_ROUND_NEGINF_7:
+ case S390_DFP_ROUND_NEGINF_11:
+ rm = mkU32(Irrm_DFP_NegINF); break;
+ case S390_DFP_ROUND_NEAREST_TIE_TOWARD_0:
+ rm = mkU32(Irrm_DFP_NEAREST_TIE_TOWARD_0); break;
+ case S390_DFP_ROUND_AWAY_0:
+ rm = mkU32(Irrm_DFP_AWAY_FROM_ZERO); break;
+ default:
+ vpanic("encode_dfp_rounding_mode");
+ }
+
+ return mktemp(Ity_I32, rm);
+}
+#endif
+
/*------------------------------------------------------------*/
/*--- Build IR for formats ---*/
/*------------------------------------------------------------*/
diff --git a/priv/host_s390_defs.c b/priv/host_s390_defs.c
index 279d83d..6fb6d0b 100644
--- a/priv/host_s390_defs.c
+++ b/priv/host_s390_defs.c
@@ -712,6 +712,10 @@
addHRegUse(u, HRmRead, insn->variant.set_fpc_bfprm.mode);
break;
+ case S390_INSN_SET_FPC_DFPRM:
+ addHRegUse(u, HRmRead, insn->variant.set_fpc_dfprm.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);
@@ -941,6 +945,11 @@
lookupHRegRemap(m, insn->variant.set_fpc_bfprm.mode);
break;
+ case S390_INSN_SET_FPC_DFPRM:
+ insn->variant.set_fpc_dfprm.mode =
+ lookupHRegRemap(m, insn->variant.set_fpc_dfprm.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);
@@ -4947,6 +4956,21 @@
s390_insn *
+s390_insn_set_fpc_dfprm(UChar size, HReg mode)
+{
+ vassert(size == 4);
+
+ s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+ insn->tag = S390_INSN_SET_FPC_DFPRM;
+ insn->size = size;
+ insn->variant.set_fpc_dfprm.mode = mode;
+
+ return insn;
+}
+
+
+s390_insn *
s390_insn_xdirect(s390_cc_t cond, Addr64 dst, s390_amode *guest_IA,
Bool to_fast_entry)
{
@@ -5456,6 +5480,11 @@
insn->variant.set_fpc_bfprm.mode);
break;
+ case S390_INSN_SET_FPC_DFPRM:
+ s390_sprintf(buf, "%M %R", "v-set-fpc-dfprm",
+ insn->variant.set_fpc_dfprm.mode);
+ break;
+
case S390_INSN_EVCHECK:
s390_sprintf(buf, "%M counter = %A, fail-addr = %A", "v-evcheck",
insn->variant.evcheck.counter,
@@ -7535,6 +7564,25 @@
}
+static UChar *
+s390_insn_set_fpc_dfprm_emit(UChar *buf, const s390_insn *insn)
+{
+ UInt mode = hregNumber(insn->variant.set_fpc_dfprm.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
+
+ /* DFP rounding mode is set at bit position 25:27 in FPC register */
+ buf = s390_emit_NILL(buf, R0, 0xFF8F); /* Clear out 25:27 bits */
+ buf = s390_emit_SLL(buf, mode, 0, 4); /* bring mode to 25:27 bits */
+ buf = s390_emit_OR(buf, R0, mode); /* OR in the new rounding mode */
+ buf = s390_emit_SFPC(buf, R0); /* 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. */
@@ -8079,6 +8127,10 @@
end = s390_insn_set_fpc_bfprm_emit(buf, insn);
break;
+ case S390_INSN_SET_FPC_DFPRM:
+ end = s390_insn_set_fpc_dfprm_emit(buf, insn);
+ break;
+
case S390_INSN_PROFINC:
end = s390_insn_profinc_emit(buf, insn);
/* Tell the caller .. */
diff --git a/priv/host_s390_defs.h b/priv/host_s390_defs.h
index 37a7dc9..d8f7dd6 100644
--- a/priv/host_s390_defs.h
+++ b/priv/host_s390_defs.h
@@ -141,6 +141,7 @@
S390_INSN_GZERO, /* Assign zero to a guest register */
S390_INSN_GADD, /* Add a value to a guest register */
S390_INSN_SET_FPC_BFPRM, /* Set the bfp rounding mode in the FPC */
+ S390_INSN_SET_FPC_DFPRM, /* Set the dfp 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 */
@@ -254,7 +255,7 @@
} s390_cc_t;
-/* Rounding mode as it is encoded in the m3 field of certain
+/* BFP Rounding mode as it is encoded in the m3 field of certain
instructions (e.g. CFEBR) */
typedef enum {
S390_BFP_ROUND_PER_FPC = 0,
@@ -268,7 +269,7 @@
} s390_bfp_round_t;
-/* Rounding mode as it is encoded in bits [29:31] of the FPC register.
+/* BFP 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 {
@@ -281,6 +282,41 @@
} s390_fpc_bfp_round_t;
+/* DFP Rounding mode as it is encoded in the m3 field of certain
+ instructions (e.g. CGDTR) */
+typedef enum {
+ S390_DFP_ROUND_PER_FPC_0 = 0,
+ S390_DFP_ROUND_NEAREST_TIE_AWAY_0_1 = 1,
+ S390_DFP_ROUND_PER_FPC_2 = 2,
+ S390_DFP_ROUND_PREPARE_SHORT_3 = 3,
+ S390_DFP_ROUND_NEAREST_EVEN_4 = 4,
+ S390_DFP_ROUND_ZERO_5 = 5,
+ S390_DFP_ROUND_POSINF_6 = 6,
+ S390_DFP_ROUND_NEGINF_7 = 7,
+ S390_DFP_ROUND_NEAREST_EVEN_8 = 8,
+ S390_DFP_ROUND_ZERO_9 = 9,
+ S390_DFP_ROUND_POSINF_10 = 10,
+ S390_DFP_ROUND_NEGINF_11 = 11,
+ S390_DFP_ROUND_NEAREST_TIE_AWAY_0_12 = 12,
+ S390_DFP_ROUND_NEAREST_TIE_TOWARD_0 = 13,
+ S390_DFP_ROUND_AWAY_0 = 14,
+ S390_DFP_ROUND_PREPARE_SHORT_15 = 15
+} s390_dfp_round_t;
+
+
+/* DFP Rounding mode as it is encoded in bits [25:27] of the FPC register. */
+typedef enum {
+ S390_FPC_DFP_ROUND_NEAREST_EVEN = 0,
+ S390_FPC_DFP_ROUND_ZERO = 1,
+ S390_FPC_DFP_ROUND_POSINF = 2,
+ S390_FPC_DFP_ROUND_NEGINF = 3,
+ S390_FPC_DFP_ROUND_NEAREST_AWAY_0 = 4,
+ S390_FPC_DFP_ROUND_NEAREST_TOWARD_0 = 5,
+ S390_FPC_DFP_ROUND_AWAY_ZERO = 6,
+ S390_FPC_DFP_ROUND_PREPARE_SHORT = 7
+} s390_fpc_dfp_round_t;
+
+
/* Invert the condition code */
static __inline__ s390_cc_t
s390_cc_invert(s390_cc_t cond)
@@ -464,6 +500,9 @@
struct {
HReg mode;
} set_fpc_bfprm;
+ struct {
+ HReg mode;
+ } set_fpc_dfprm;
/* The next 5 entries are generic to support translation chaining */
@@ -557,6 +596,7 @@
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_fpc_bfprm(UChar size, HReg mode);
+s390_insn *s390_insn_set_fpc_dfprm(UChar size, HReg mode);
/* Five for translation chaining */
s390_insn *s390_insn_xdirect(s390_cc_t cond, Addr64 dst, s390_amode *guest_IA,
diff --git a/priv/host_s390_isel.c b/priv/host_s390_isel.c
index d42a3ab..6a958d1 100644
--- a/priv/host_s390_isel.c
+++ b/priv/host_s390_isel.c
@@ -114,6 +114,7 @@
UInt hwcaps;
IRExpr *previous_bfp_rounding_mode;
+ IRExpr *previous_dfp_rounding_mode;
ULong old_value[NUM_TRACKED_REGS];
@@ -607,6 +608,125 @@
}
+/*---------------------------------------------------------*/
+/*--- DFP helper functions ---*/
+/*---------------------------------------------------------*/
+
+/* Set the DFP rounding mode in the FPC. This function is called for
+ all non-conversion DFP instructions as those will always get the
+ rounding mode from the FPC. */
+#if 0 // fixs390: avoid compiler warnings about unused function
+static void
+set_dfp_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_dfp_rounding_mode &&
+ env->previous_dfp_rounding_mode->tag == Iex_RdTmp &&
+ irrm->tag == Iex_RdTmp &&
+ env->previous_dfp_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_dfp_rounding_mode = irrm;
+
+ /* The incoming rounding mode is in VEX IR encoding. Need to change
+ to s390.
+
+ rounding mode | S390 | IR
+ -----------------------------------------------
+ to nearest, ties to even | 000 | 000
+ to zero | 001 | 011
+ to +infinity | 010 | 010
+ to -infinity | 011 | 001
+ to nearest, ties away from 0 | 100 | 100
+ to nearest, ties toward 0 | 101 | 111
+ to away from 0 | 110 | 110
+ to prepare for shorter precision | 111 | 101
+
+ So: s390 = (IR ^ ((IR << 1) & 2))
+ */
+ HReg ir = s390_isel_int_expr(env, irrm);
+
+ HReg mode = newVRegI(env);
+
+ addInstr(env, s390_insn_move(4, mode, ir));
+ addInstr(env, s390_insn_alu(4, S390_ALU_LSH, mode, s390_opnd_imm(1)));
+ addInstr(env, s390_insn_alu(4, S390_ALU_AND, mode, s390_opnd_imm(2)));
+ addInstr(env, s390_insn_alu(4, S390_ALU_XOR, mode, s390_opnd_reg(ir)));
+
+ addInstr(env, s390_insn_set_fpc_dfprm(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.
+ The IR to s390 encoding is chosen in the range 0:7 except
+ S390_DFP_ROUND_NEAREST_TIE_TOWARD_0 and
+ S390_DFP_ROUND_AWAY_0 which have no choice within the range.
+ Since the s390 dfp rounding mode encoding in 8:15 is not used, the
+ quantum excpetion is not suppressed and this is fine as valgrind does
+ not model this exception.
+
+ Translation table of
+ s390 DFP rounding mode to IRRoundingMode to s390 DFP rounding mode
+
+ s390(S390_DFP_ROUND_) | IR(Irrm_DFP_) | s390(S390_DFP_ROUND_)
+ --------------------------------------------------------------------
+ NEAREST_TIE_AWAY_0_1 | NEAREST_TIE_AWAY_0 | NEAREST_TIE_AWAY_0_1
+ NEAREST_TIE_AWAY_0_12 | " | "
+ PREPARE_SHORT_3 | PREPARE_SHORTER | PREPARE_SHORT_3
+ PREPARE_SHORT_15 | " | "
+ NEAREST_EVEN_4 | NEAREST | NEAREST_EVEN_4
+ NEAREST_EVEN_8 | " | "
+ ZERO_5 | ZERO | ZERO_5
+ ZERO_9 | " | "
+ POSINF_6 | PosINF | POSINF_6
+ POSINF_10 | " | "
+ NEGINF_7 | NegINF | NEGINF_7
+ NEGINF_11 | " | "
+ NEAREST_TIE_TOWARD_0 | NEAREST_TIE_TOWARD_0| NEAREST_TIE_TOWARD_0
+ AWAY_0 | AWAY_FROM_ZERO | AWAY_0
+*/
+static s390_dfp_round_t
+get_dfp_rounding_mode(ISelEnv *env, IRExpr *irrm)
+{
+ 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_DFP_NEAREST:
+ return S390_DFP_ROUND_NEAREST_EVEN_4;
+ case Irrm_DFP_NegINF:
+ return S390_DFP_ROUND_NEGINF_7;
+ case Irrm_DFP_PosINF:
+ return S390_DFP_ROUND_POSINF_6;
+ case Irrm_DFP_ZERO:
+ return S390_DFP_ROUND_ZERO_5;
+ case Irrm_DFP_NEAREST_TIE_AWAY_0:
+ return S390_DFP_ROUND_NEAREST_TIE_AWAY_0_1;
+ case Irrm_DFP_PREPARE_SHORTER:
+ return S390_DFP_ROUND_PREPARE_SHORT_3;
+ case Irrm_DFP_AWAY_FROM_ZERO:
+ return S390_DFP_ROUND_AWAY_0;
+ case Irrm_DFP_NEAREST_TIE_TOWARD_0:
+ return S390_DFP_ROUND_NEAREST_TIE_TOWARD_0;
+ default:
+ vpanic("get_dfp_rounding_mode");
+ }
+ }
+
+ set_dfp_rounding_mode_in_fpc(env, irrm);
+ return S390_DFP_ROUND_PER_FPC_0;
+}
+#endif
+
/* CC_S390 holds the condition code in s390 encoding. Convert it to
VEX encoding
@@ -2872,6 +2992,7 @@
env->vregmapHI = LibVEX_Alloc(env->n_vregmap * sizeof(HReg));
env->previous_bfp_rounding_mode = NULL;
+ env->previous_dfp_rounding_mode = NULL;
/* and finally ... */
env->hwcaps = hwcaps_host;