From f53d406c967bee97f5576f577f40fae546f1ba56 Mon Sep 17 00:00:00 2001 From: Florian Krohm Date: Sat, 27 Oct 2012 16:19:31 +0000 Subject: [PATCH] Add machinery to support DFP rounding modes. Part of fixing BZ 307113. Patch by Maran git-svn-id: svn://svn.valgrind.org/vex/trunk@2557 --- VEX/priv/guest_s390_toIR.c | 78 ++++++++++++++++++++++++ VEX/priv/host_s390_defs.c | 52 ++++++++++++++++ VEX/priv/host_s390_defs.h | 44 +++++++++++++- VEX/priv/host_s390_isel.c | 121 +++++++++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+), 2 deletions(-) diff --git a/VEX/priv/guest_s390_toIR.c b/VEX/priv/guest_s390_toIR.c index bde121ba50..07486853c8 100644 --- a/VEX/priv/guest_s390_toIR.c +++ b/VEX/priv/guest_s390_toIR.c @@ -1465,6 +1465,84 @@ encode_bfp_rounding_mode(UChar mode) 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/VEX/priv/host_s390_defs.c b/VEX/priv/host_s390_defs.c index 279d83d7ab..6fb6d0bbd1 100644 --- a/VEX/priv/host_s390_defs.c +++ b/VEX/priv/host_s390_defs.c @@ -712,6 +712,10 @@ s390_insn_get_reg_usage(HRegUsage *u, const s390_insn *insn) 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 @@ s390_insn_map_regs(HRegRemap *m, s390_insn *insn) 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); @@ -4946,6 +4955,21 @@ s390_insn_set_fpc_bfprm(UChar size, HReg mode) } +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 @@ s390_insn_as_string(const s390_insn *insn) 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 @@ s390_insn_set_fpc_bfprm_emit(UChar *buf, const s390_insn *insn) } +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 @@ emit_S390Instr(Bool *is_profinc, UChar *buf, Int nbuf, s390_insn *insn, 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/VEX/priv/host_s390_defs.h b/VEX/priv/host_s390_defs.h index 37a7dc9bf7..d8f7dd6619 100644 --- a/VEX/priv/host_s390_defs.h +++ b/VEX/priv/host_s390_defs.h @@ -141,6 +141,7 @@ typedef enum { 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 @@ typedef enum { } 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 @@ typedef enum { } 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 @@ typedef enum { } 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 @@ typedef struct { 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_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_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/VEX/priv/host_s390_isel.c b/VEX/priv/host_s390_isel.c index d42a3ab55c..6a958d1431 100644 --- a/VEX/priv/host_s390_isel.c +++ b/VEX/priv/host_s390_isel.c @@ -114,6 +114,7 @@ typedef struct { UInt hwcaps; IRExpr *previous_bfp_rounding_mode; + IRExpr *previous_dfp_rounding_mode; ULong old_value[NUM_TRACKED_REGS]; @@ -607,6 +608,125 @@ get_bfp_rounding_mode(ISelEnv *env, IRExpr *irrm) } +/*---------------------------------------------------------*/ +/*--- 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 @@ iselSB_S390(IRSB *bb, VexArch arch_host, VexArchInfo *archinfo_host, 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; -- 2.47.2