commit 600a0099a1eb2335a3f9563534c112e11817002b Author: Vadim Barkov Date: Fri Oct 5 13:51:49 2018 +0300 Bug 385411 s390x: Add z13 vector floating point support This adds support for the z/Architecture vector FP instructions that were introduced with z13. The patch was contributed by Vadim Barkov, with some clean-up and minor adjustments by Andreas Arnez. diff --git a/VEX/priv/guest_s390_defs.h b/VEX/priv/guest_s390_defs.h index 3bfecbe..d72cc9f 100644 --- a/VEX/priv/guest_s390_defs.h +++ b/VEX/priv/guest_s390_defs.h @@ -281,7 +281,11 @@ enum { S390_VEC_OP_VMALH = 13, S390_VEC_OP_VCH = 14, S390_VEC_OP_VCHL = 15, - S390_VEC_OP_LAST = 16 // supposed to be the last element in enum + S390_VEC_OP_VFCE = 16, + S390_VEC_OP_VFCH = 17, + S390_VEC_OP_VFCHE = 18, + S390_VEC_OP_VFTCI = 19, + S390_VEC_OP_LAST = 20 // supposed to be the last element in enum } s390x_vec_op_t; /* Arguments of s390x_dirtyhelper_vec_op(...) which are packed into one @@ -300,8 +304,10 @@ typedef union { unsigned int m4 : 4; // field m4 of insn or zero if it's missing unsigned int m5 : 4; // field m5 of insn or zero if it's missing + unsigned int m6 : 4; // field m6 of insn or zero if it's missing + unsigned int i3 : 12; // field i3 of insn or zero if it's missing unsigned int read_only: 1; // don't write result to Guest State - unsigned int reserved : 27; // reserved for future + unsigned int reserved : 11; // reserved for future }; ULong serialized; } s390x_vec_op_details_t; diff --git a/VEX/priv/guest_s390_helpers.c b/VEX/priv/guest_s390_helpers.c index d9773e7..5877743 100644 --- a/VEX/priv/guest_s390_helpers.c +++ b/VEX/priv/guest_s390_helpers.c @@ -2498,6 +2498,10 @@ s390x_dirtyhelper_vec_op(VexGuestS390XState *guest_state, {0xe7, 0xa9}, /* VMALH */ {0xe7, 0xfb}, /* VCH */ {0xe7, 0xf9}, /* VCHL */ + {0xe7, 0xe8}, /* VFCE */ + {0xe7, 0xeb}, /* VFCH */ + {0xe7, 0xea}, /* VFCHE */ + {0xe7, 0x4a} /* VFTCI */ }; union { @@ -2525,6 +2529,28 @@ s390x_dirtyhelper_vec_op(VexGuestS390XState *guest_state, unsigned int rxb : 4; unsigned int op2 : 8; } VRRd; + struct { + UInt op1 : 8; + UInt v1 : 4; + UInt v2 : 4; + UInt v3 : 4; + UInt : 4; + UInt m6 : 4; + UInt m5 : 4; + UInt m4 : 4; + UInt rxb : 4; + UInt op2 : 8; + } VRRc; + struct { + UInt op1 : 8; + UInt v1 : 4; + UInt v2 : 4; + UInt i3 : 12; + UInt m5 : 4; + UInt m4 : 4; + UInt rxb : 4; + UInt op2 : 8; + } VRIe; UChar bytes[6]; } the_insn; @@ -2578,6 +2604,27 @@ s390x_dirtyhelper_vec_op(VexGuestS390XState *guest_state, the_insn.VRRd.m6 = d->m5; break; + case S390_VEC_OP_VFCE: + case S390_VEC_OP_VFCH: + case S390_VEC_OP_VFCHE: + the_insn.VRRc.v1 = 1; + the_insn.VRRc.v2 = 2; + the_insn.VRRc.v3 = 3; + the_insn.VRRc.rxb = 0b1110; + the_insn.VRRc.m4 = d->m4; + the_insn.VRRc.m5 = d->m5; + the_insn.VRRc.m6 = d->m6; + break; + + case S390_VEC_OP_VFTCI: + the_insn.VRIe.v1 = 1; + the_insn.VRIe.v2 = 2; + the_insn.VRIe.rxb = 0b1100; + the_insn.VRIe.i3 = d->i3; + the_insn.VRIe.m4 = d->m4; + the_insn.VRIe.m5 = d->m5; + break; + default: vex_printf("operation = %d\n", d->op); vpanic("s390x_dirtyhelper_vec_op: unknown operation"); diff --git a/VEX/priv/guest_s390_toIR.c b/VEX/priv/guest_s390_toIR.c index 50a5a41..1c4ac39 100644 --- a/VEX/priv/guest_s390_toIR.c +++ b/VEX/priv/guest_s390_toIR.c @@ -86,6 +86,7 @@ typedef enum { S390_DECODE_UNKNOWN_INSN, S390_DECODE_UNIMPLEMENTED_INSN, S390_DECODE_UNKNOWN_SPECIAL_INSN, + S390_DECODE_SPECIFICATION_EXCEPTION, S390_DECODE_ERROR } s390_decode_t; @@ -421,6 +422,26 @@ yield_if(IRExpr *condition) S390X_GUEST_OFFSET(guest_IA))); } +/* Convenience macro to yield a specification exception if the given condition + is not met. Used to pass this type of decoding error up through the call + chain. */ +#define s390_insn_assert(mnm, cond) \ + do { \ + if (!(cond)) { \ + dis_res->whatNext = Dis_StopHere; \ + dis_res->jk_StopHere = Ijk_NoDecode; \ + return (mnm); \ + } \ + } while (0) + +/* Convenience function to check for a specification exception. */ +static Bool +is_specification_exception(void) +{ + return (dis_res->whatNext == Dis_StopHere && + dis_res->jk_StopHere == Ijk_NoDecode); +} + static __inline__ IRExpr *get_fpr_dw0(UInt); static __inline__ void put_fpr_dw0(UInt, IRExpr *); static __inline__ IRExpr *get_dpr_dw0(UInt); @@ -1770,6 +1791,11 @@ s390_vr_get_type(const UChar m) /* Determine if Zero Search (ZS) flag is set in m field */ #define s390_vr_is_zs_set(m) (((m) & 0b0010) != 0) +/* Check if the "Single-Element-Control" bit is set. + Used in vector FP instructions. + */ +#define s390_vr_is_single_element_control_set(m) (((m) & 0x8) != 0) + /* Generates arg1 < arg2 (or arg1 <= arg2 if allow_equal == True) expression. Arguments must have V128 type and are treated as unsigned 128-bit numbers. */ @@ -2001,12 +2027,14 @@ s390_vr_offset_by_index(UInt archreg,IRType type, UChar index) return vr_offset(archreg) + sizeof(UShort) * index; case Ity_I32: + case Ity_F32: if(index > 3) { goto invalidIndex; } return vr_offset(archreg) + sizeof(UInt) * index; case Ity_I64: + case Ity_F64: if(index > 1) { goto invalidIndex; } @@ -2237,8 +2265,8 @@ encode_bfp_rounding_mode(UChar mode) case S390_BFP_ROUND_PER_FPC: rm = get_bfp_rounding_mode_from_fpc(); break; - case S390_BFP_ROUND_NEAREST_AWAY: /* not supported */ - case S390_BFP_ROUND_PREPARE_SHORT: /* not supported */ + case S390_BFP_ROUND_NEAREST_AWAY: rm = mkU32(Irrm_NEAREST_TIE_AWAY_0); break; + case S390_BFP_ROUND_PREPARE_SHORT: rm = mkU32(Irrm_PREPARE_SHORTER); break; case S390_BFP_ROUND_NEAREST_EVEN: rm = mkU32(Irrm_NEAREST); break; case S390_BFP_ROUND_ZERO: rm = mkU32(Irrm_ZERO); break; case S390_BFP_ROUND_POSINF: rm = mkU32(Irrm_PosINF); break; @@ -3524,6 +3552,26 @@ s390_format_VRI_VVIM(const HChar *(*irgen)(UChar v1, UChar v3, UShort i2, UChar s390_disasm(ENC5(MNM, VR, VR, UINT, UINT), mnm, v1, v3, i2, m4); } +static void +s390_format_VRI_VVIMM(const HChar *(*irgen)(UChar v1, UChar v2, UShort i3, + UChar m4, UChar m5), + UChar v1, UChar v2, UShort i3, UChar m4, UChar m5, + UChar rxb) +{ + const HChar *mnm; + + if (!s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + mnm = irgen(v1, v2, i3, m4, m5); + + if (vex_traceflags & VEX_TRACE_FE) + s390_disasm(ENC6(MNM, VR, VR, UINT, UINT, UINT), mnm, v1, v2, i3, m4, m5); +} static void s390_format_VRS_RRDVM(const HChar *(*irgen)(UChar r1, IRTemp op2addr, UChar v3, @@ -3680,7 +3728,7 @@ s390_format_VRV_VVRDMT(const HChar *(*irgen)(UChar v1, IRTemp op2addr, UChar m3) static void -s390_format_VRRd_VVVVMM(const HChar *(*irgen)(UChar v1, UChar v2, UChar v3, +s390_format_VRR_VVVVMM(const HChar *(*irgen)(UChar v1, UChar v2, UChar v3, UChar v4, UChar m5, UChar m6), UChar v1, UChar v2, UChar v3, UChar v4, UChar m5, UChar m6, UChar rxb) @@ -3794,6 +3842,92 @@ s390_format_VRRd_VVVVM(const HChar *(*irgen)(UChar v1, UChar v2, UChar v3, } +static void +s390_format_VRRa_VVMMM(const HChar *(*irgen)(UChar v1, UChar v2, UChar m3, + UChar m4, UChar m5), + UChar v1, UChar v2, UChar m3, UChar m4, UChar m5, + UChar rxb) +{ + const HChar *mnm; + + if (!s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + mnm = irgen(v1, v2, m3, m4, m5); + + if (vex_traceflags & VEX_TRACE_FE) + s390_disasm(ENC6(MNM, VR, VR, UINT, UINT, UINT), mnm, v1, v2, m3, m4, m5); +} + +static void +s390_format_VRRa_VVVMM(const HChar *(*irgen)(UChar v1, UChar v2, UChar v3, + UChar m4, UChar m5), + UChar v1, UChar v2, UChar v3, UChar m4, UChar m5, + UChar rxb) +{ + const HChar *mnm; + + if (!s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + v3 = s390_vr_getVRindex(v3, 3, rxb); + mnm = irgen(v1, v2, v3, m4, m5); + + if (vex_traceflags & VEX_TRACE_FE) + s390_disasm(ENC6(MNM, VR, VR, VR, UINT, UINT), mnm, v1, v2, v3, m4, m5); +} + +static void +s390_format_VRRa_VVMM(const HChar *(*irgen)(UChar v1, UChar v2, UChar m3, + UChar m4), + UChar v1, UChar v2, UChar m3, UChar m4, UChar rxb) +{ + const HChar *mnm; + + if (!s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + mnm = irgen(v1, v2, m3, m4); + + if (vex_traceflags & VEX_TRACE_FE) + s390_disasm(ENC5(MNM, VR, VR, UINT, UINT), mnm, v1, v2, m3, m4); +} + +static void +s390_format_VRRa_VVVMMM(const HChar *(*irgen)(UChar v1, UChar v2, UChar v3, + UChar m4, UChar m5, UChar m6), + UChar v1, UChar v2, UChar v3, UChar m4, UChar m5, + UChar m6, UChar rxb) +{ + const HChar *mnm; + + if (!s390_host_has_vx) { + emulation_failure(EmFail_S390X_vx); + return; + } + + v1 = s390_vr_getVRindex(v1, 1, rxb); + v2 = s390_vr_getVRindex(v2, 2, rxb); + v3 = s390_vr_getVRindex(v3, 3, rxb); + mnm = irgen(v1, v2, v3, m4, m5, m6); + + if (vex_traceflags & VEX_TRACE_FE) + s390_disasm(ENC6(MNM, VR, VR, VR, UINT, UINT), + mnm, v1, v2, v3, m4, m5, m6); +} + /*------------------------------------------------------------*/ /*--- Build IR for opcodes ---*/ /*------------------------------------------------------------*/ @@ -17895,6 +18029,575 @@ s390_irgen_VMALH(UChar v1, UChar v2, UChar v3, UChar v4, UChar m5) return "vmalh"; } +static void +s390_vector_fp_convert(IROp op, IRType fromType, IRType toType, + UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + Bool isSingleElementOp = s390_vr_is_single_element_control_set(m4); + UChar maxIndex = isSingleElementOp ? 0 : 1; + + /* For Iop_F32toF64 we do this: + f32[0] -> f64[0] + f32[2] -> f64[1] + + For Iop_F64toF32 we do this: + f64[0] -> f32[0] + f64[1] -> f32[2] + + The magic below with scaling factors is used to achieve the logic + described above. + */ + const UChar sourceIndexScaleFactor = (op == Iop_F32toF64) ? 2 : 1; + const UChar destinationIndexScaleFactor = (op == Iop_F64toF32) ? 2 : 1; + + const Bool isUnary = (op == Iop_F32toF64); + for (UChar i = 0; i <= maxIndex; i++) { + IRExpr* argument = get_vr(v2, fromType, i * sourceIndexScaleFactor); + IRExpr* result; + if (!isUnary) { + result = binop(op, + mkexpr(encode_bfp_rounding_mode(m5)), + argument); + } else { + result = unop(op, argument); + } + put_vr(v1, toType, i * destinationIndexScaleFactor, result); + } + + if (isSingleElementOp) { + put_vr_dw1(v1, mkU64(0)); + } +} + +static const HChar * +s390_irgen_VCDG(UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + s390_insn_assert("vcdg", m3 == 3); + + if (!s390_host_has_fpext && m5 != S390_BFP_ROUND_PER_FPC) { + emulation_warning(EmWarn_S390X_fpext_rounding); + m5 = S390_BFP_ROUND_PER_FPC; + } + + s390_vector_fp_convert(Iop_I64StoF64, Ity_I64, Ity_F64, v1, v2, m3, m4, m5); + + return "vcdg"; +} + +static const HChar * +s390_irgen_VCDLG(UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + s390_insn_assert("vcdlg", m3 == 3); + + if (!s390_host_has_fpext && m5 != S390_BFP_ROUND_PER_FPC) { + emulation_warning(EmWarn_S390X_fpext_rounding); + m5 = S390_BFP_ROUND_PER_FPC; + } + + s390_vector_fp_convert(Iop_I64UtoF64, Ity_I64, Ity_F64, v1, v2, m3, m4, m5); + + return "vcdlg"; +} + +static const HChar * +s390_irgen_VCGD(UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + s390_insn_assert("vcgd", m3 == 3); + + if (!s390_host_has_fpext && m5 != S390_BFP_ROUND_PER_FPC) { + emulation_warning(EmWarn_S390X_fpext_rounding); + m5 = S390_BFP_ROUND_PER_FPC; + } + + s390_vector_fp_convert(Iop_F64toI64S, Ity_F64, Ity_I64, v1, v2, m3, m4, m5); + + return "vcgd"; +} + +static const HChar * +s390_irgen_VCLGD(UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + s390_insn_assert("vclgd", m3 == 3); + + if (!s390_host_has_fpext && m5 != S390_BFP_ROUND_PER_FPC) { + emulation_warning(EmWarn_S390X_fpext_rounding); + m5 = S390_BFP_ROUND_PER_FPC; + } + + s390_vector_fp_convert(Iop_F64toI64U, Ity_F64, Ity_I64, v1, v2, m3, m4, m5); + + return "vclgd"; +} + +static const HChar * +s390_irgen_VFI(UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + s390_insn_assert("vfi", m3 == 3); + + if (!s390_host_has_fpext && m5 != S390_BFP_ROUND_PER_FPC) { + emulation_warning(EmWarn_S390X_fpext_rounding); + m5 = S390_BFP_ROUND_PER_FPC; + } + + s390_vector_fp_convert(Iop_RoundF64toInt, Ity_F64, Ity_F64, + v1, v2, m3, m4, m5); + + return "vcgld"; +} + +static const HChar * +s390_irgen_VLDE(UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + s390_insn_assert("vlde", m3 == 2); + + s390_vector_fp_convert(Iop_F32toF64, Ity_F32, Ity_F64, v1, v2, m3, m4, m5); + + return "vlde"; +} + +static const HChar * +s390_irgen_VLED(UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + s390_insn_assert("vled", m3 == 3); + + if (!s390_host_has_fpext && m5 != S390_BFP_ROUND_PER_FPC) { + m5 = S390_BFP_ROUND_PER_FPC; + } + + s390_vector_fp_convert(Iop_F64toF32, Ity_F64, Ity_F32, v1, v2, m3, m4, m5); + + return "vled"; +} + +static const HChar * +s390_irgen_VFPSO(UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + s390_insn_assert("vfpso", m3 == 3); + + IRExpr* result; + switch (m5) { + case 0: { + /* Invert sign */ + if (!s390_vr_is_single_element_control_set(m4)) { + result = unop(Iop_Neg64Fx2, get_vr_qw(v2)); + } + else { + result = binop(Iop_64HLtoV128, + unop(Iop_ReinterpF64asI64, + unop(Iop_NegF64, get_vr(v2, Ity_F64, 0))), + mkU64(0)); + } + break; + } + + case 1: { + /* Set sign to negative */ + IRExpr* highHalf = mkU64(0x8000000000000000ULL); + if (!s390_vr_is_single_element_control_set(m4)) { + IRExpr* lowHalf = highHalf; + IRExpr* mask = binop(Iop_64HLtoV128, highHalf, lowHalf); + result = binop(Iop_OrV128, get_vr_qw(v2), mask); + } + else { + result = binop(Iop_64HLtoV128, + binop(Iop_Or64, get_vr_dw0(v2), highHalf), + mkU64(0ULL)); + } + + break; + } + + case 2: { + /* Set sign to positive */ + if (!s390_vr_is_single_element_control_set(m4)) { + result = unop(Iop_Abs64Fx2, get_vr_qw(v2)); + } + else { + result = binop(Iop_64HLtoV128, + unop(Iop_ReinterpF64asI64, + unop(Iop_AbsF64, get_vr(v2, Ity_F64, 0))), + mkU64(0)); + } + + break; + } + + default: + vpanic("s390_irgen_VFPSO: Invalid m5 value"); + } + + put_vr_qw(v1, result); + if (s390_vr_is_single_element_control_set(m4)) { + put_vr_dw1(v1, mkU64(0ULL)); + } + + return "vfpso"; +} + +static void s390x_vec_fp_binary_op(IROp generalOp, IROp singleElementOp, + UChar v1, UChar v2, UChar v3, UChar m4, + UChar m5) +{ + IRExpr* result; + if (!s390_vr_is_single_element_control_set(m5)) { + result = triop(generalOp, get_bfp_rounding_mode_from_fpc(), + get_vr_qw(v2), get_vr_qw(v3)); + } else { + IRExpr* highHalf = triop(singleElementOp, + get_bfp_rounding_mode_from_fpc(), + get_vr(v2, Ity_F64, 0), + get_vr(v3, Ity_F64, 0)); + result = binop(Iop_64HLtoV128, unop(Iop_ReinterpF64asI64, highHalf), + mkU64(0ULL)); + } + + put_vr_qw(v1, result); +} + +static void s390x_vec_fp_unary_op(IROp generalOp, IROp singleElementOp, + UChar v1, UChar v2, UChar m3, UChar m4) +{ + IRExpr* result; + if (!s390_vr_is_single_element_control_set(m4)) { + result = binop(generalOp, get_bfp_rounding_mode_from_fpc(), + get_vr_qw(v2)); + } + else { + IRExpr* highHalf = binop(singleElementOp, + get_bfp_rounding_mode_from_fpc(), + get_vr(v2, Ity_F64, 0)); + result = binop(Iop_64HLtoV128, unop(Iop_ReinterpF64asI64, highHalf), + mkU64(0ULL)); + } + + put_vr_qw(v1, result); +} + + +static void +s390_vector_fp_mulAddOrSub(IROp singleElementOp, + UChar v1, UChar v2, UChar v3, UChar v4, + UChar m5, UChar m6) +{ + Bool isSingleElementOp = s390_vr_is_single_element_control_set(m5); + IRTemp irrm_temp = newTemp(Ity_I32); + assign(irrm_temp, get_bfp_rounding_mode_from_fpc()); + IRExpr* irrm = mkexpr(irrm_temp); + IRExpr* result; + IRExpr* highHalf = qop(singleElementOp, + irrm, + get_vr(v2, Ity_F64, 0), + get_vr(v3, Ity_F64, 0), + get_vr(v4, Ity_F64, 0)); + + if (isSingleElementOp) { + result = binop(Iop_64HLtoV128, unop(Iop_ReinterpF64asI64, highHalf), + mkU64(0ULL)); + } else { + IRExpr* lowHalf = qop(singleElementOp, + irrm, + get_vr(v2, Ity_F64, 1), + get_vr(v3, Ity_F64, 1), + get_vr(v4, Ity_F64, 1)); + result = binop(Iop_64HLtoV128, unop(Iop_ReinterpF64asI64, highHalf), + unop(Iop_ReinterpF64asI64, lowHalf)); + } + + put_vr_qw(v1, result); +} + +static const HChar * +s390_irgen_VFA(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + s390_insn_assert("vfa", m4 == 3); + s390x_vec_fp_binary_op(Iop_Add64Fx2, Iop_AddF64, v1, v2, v3, m4, m5); + return "vfa"; +} + +static const HChar * +s390_irgen_VFS(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + s390_insn_assert("vfs", m4 == 3); + s390x_vec_fp_binary_op(Iop_Sub64Fx2, Iop_SubF64, v1, v2, v3, m4, m5); + return "vfs"; +} + +static const HChar * +s390_irgen_VFM(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + s390_insn_assert("vfm", m4 == 3); + s390x_vec_fp_binary_op(Iop_Mul64Fx2, Iop_MulF64, v1, v2, v3, m4, m5); + return "vfm"; +} + +static const HChar * +s390_irgen_VFD(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + s390_insn_assert("vfd", m4 == 3); + s390x_vec_fp_binary_op(Iop_Div64Fx2, Iop_DivF64, v1, v2, v3, m4, m5); + return "vfd"; +} + +static const HChar * +s390_irgen_VFSQ(UChar v1, UChar v2, UChar m3, UChar m4) +{ + s390_insn_assert("vfsq", m3 == 3); + s390x_vec_fp_unary_op(Iop_Sqrt64Fx2, Iop_SqrtF64, v1, v2, m3, m4); + + return "vfsq"; +} + +static const HChar * +s390_irgen_VFMA(UChar v1, UChar v2, UChar v3, UChar v4, UChar m5, UChar m6) +{ + s390_insn_assert("vfma", m6 == 3); + s390_vector_fp_mulAddOrSub(Iop_MAddF64, v1, v2, v3, v4, m5, m6); + return "vfma"; +} + +static const HChar * +s390_irgen_VFMS(UChar v1, UChar v2, UChar v3, UChar v4, UChar m5, UChar m6) +{ + s390_insn_assert("vfms", m6 == 3); + s390_vector_fp_mulAddOrSub(Iop_MSubF64, v1, v2, v3, v4, m5, m6); + return "vfms"; +} + +static const HChar * +s390_irgen_WFC(UChar v1, UChar v2, UChar m3, UChar m4) +{ + s390_insn_assert("wfc", m3 == 3); + s390_insn_assert("wfc", m4 == 0); + + IRTemp cc_vex = newTemp(Ity_I32); + assign(cc_vex, binop(Iop_CmpF64, + get_vr(v1, Ity_F64, 0), get_vr(v2, Ity_F64, 0))); + + IRTemp cc_s390 = newTemp(Ity_I32); + assign(cc_s390, convert_vex_bfpcc_to_s390(cc_vex)); + s390_cc_thunk_put1(S390_CC_OP_SET, cc_s390, False); + + return "wfc"; +} + +static const HChar * +s390_irgen_WFK(UChar v1, UChar v2, UChar m3, UChar m4) +{ + s390_irgen_WFC(v1, v2, m3, m4); + + return "wfk"; +} + +static const HChar * +s390_irgen_VFCE(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5, UChar m6) +{ + s390_insn_assert("vfce", m4 == 3); + + Bool isSingleElementOp = s390_vr_is_single_element_control_set(m5); + if (!s390_vr_is_cs_set(m6)) { + if (!isSingleElementOp) { + put_vr_qw(v1, binop(Iop_CmpEQ64Fx2, get_vr_qw(v2), get_vr_qw(v3))); + } else { + IRExpr* comparisonResult = binop(Iop_CmpF64, get_vr(v2, Ity_F64, 0), + get_vr(v3, Ity_F64, 0)); + IRExpr* result = mkite(binop(Iop_CmpEQ32, comparisonResult, + mkU32(Ircr_EQ)), + mkU64(0xffffffffffffffffULL), + mkU64(0ULL)); + put_vr_qw(v1, binop(Iop_64HLtoV128, result, mkU64(0ULL))); + } + } else { + IRDirty* d; + IRTemp cc = newTemp(Ity_I64); + + s390x_vec_op_details_t details = { .serialized = 0ULL }; + details.op = S390_VEC_OP_VFCE; + details.v1 = v1; + details.v2 = v2; + details.v3 = v3; + details.m4 = m4; + details.m5 = m5; + details.m6 = m6; + + d = unsafeIRDirty_1_N(cc, 0, "s390x_dirtyhelper_vec_op", + &s390x_dirtyhelper_vec_op, + mkIRExprVec_2(IRExpr_GSPTR(), + mkU64(details.serialized))); + + const UChar elementSize = isSingleElementOp ? sizeof(ULong) : sizeof(V128); + d->nFxState = 3; + vex_bzero(&d->fxState, sizeof(d->fxState)); + d->fxState[0].fx = Ifx_Read; + d->fxState[0].offset = S390X_GUEST_OFFSET(guest_v0) + v2 * sizeof(V128); + d->fxState[0].size = elementSize; + d->fxState[1].fx = Ifx_Read; + d->fxState[1].offset = S390X_GUEST_OFFSET(guest_v0) + v3 * sizeof(V128); + d->fxState[1].size = elementSize; + d->fxState[2].fx = Ifx_Write; + d->fxState[2].offset = S390X_GUEST_OFFSET(guest_v0) + v1 * sizeof(V128); + d->fxState[2].size = sizeof(V128); + + stmt(IRStmt_Dirty(d)); + s390_cc_set(cc); + } + + return "vfce"; +} + +static const HChar * +s390_irgen_VFCH(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5, UChar m6) +{ + vassert(m4 == 3); + + Bool isSingleElementOp = s390_vr_is_single_element_control_set(m5); + if (!s390_vr_is_cs_set(m6)) { + if (!isSingleElementOp) { + put_vr_qw(v1, binop(Iop_CmpLE64Fx2, get_vr_qw(v3), get_vr_qw(v2))); + } else { + IRExpr* comparisonResult = binop(Iop_CmpF64, get_vr(v2, Ity_F64, 0), + get_vr(v3, Ity_F64, 0)); + IRExpr* result = mkite(binop(Iop_CmpEQ32, comparisonResult, + mkU32(Ircr_GT)), + mkU64(0xffffffffffffffffULL), + mkU64(0ULL)); + put_vr_qw(v1, binop(Iop_64HLtoV128, result, mkU64(0ULL))); + } + } + else { + IRDirty* d; + IRTemp cc = newTemp(Ity_I64); + + s390x_vec_op_details_t details = { .serialized = 0ULL }; + details.op = S390_VEC_OP_VFCH; + details.v1 = v1; + details.v2 = v2; + details.v3 = v3; + details.m4 = m4; + details.m5 = m5; + details.m6 = m6; + + d = unsafeIRDirty_1_N(cc, 0, "s390x_dirtyhelper_vec_op", + &s390x_dirtyhelper_vec_op, + mkIRExprVec_2(IRExpr_GSPTR(), + mkU64(details.serialized))); + + const UChar elementSize = isSingleElementOp ? sizeof(ULong) : sizeof(V128); + d->nFxState = 3; + vex_bzero(&d->fxState, sizeof(d->fxState)); + d->fxState[0].fx = Ifx_Read; + d->fxState[0].offset = S390X_GUEST_OFFSET(guest_v0) + v2 * sizeof(V128); + d->fxState[0].size = elementSize; + d->fxState[1].fx = Ifx_Read; + d->fxState[1].offset = S390X_GUEST_OFFSET(guest_v0) + v3 * sizeof(V128); + d->fxState[1].size = elementSize; + d->fxState[2].fx = Ifx_Write; + d->fxState[2].offset = S390X_GUEST_OFFSET(guest_v0) + v1 * sizeof(V128); + d->fxState[2].size = sizeof(V128); + + stmt(IRStmt_Dirty(d)); + s390_cc_set(cc); + } + + return "vfch"; +} + +static const HChar * +s390_irgen_VFCHE(UChar v1, UChar v2, UChar v3, UChar m4, UChar m5, UChar m6) +{ + s390_insn_assert("vfche", m4 == 3); + + Bool isSingleElementOp = s390_vr_is_single_element_control_set(m5); + if (!s390_vr_is_cs_set(m6)) { + if (!isSingleElementOp) { + put_vr_qw(v1, binop(Iop_CmpLT64Fx2, get_vr_qw(v3), get_vr_qw(v2))); + } + else { + IRExpr* comparisonResult = binop(Iop_CmpF64, get_vr(v3, Ity_F64, 0), + get_vr(v2, Ity_F64, 0)); + IRExpr* result = mkite(binop(Iop_CmpEQ32, comparisonResult, + mkU32(Ircr_LT)), + mkU64(0xffffffffffffffffULL), + mkU64(0ULL)); + put_vr_qw(v1, binop(Iop_64HLtoV128, result, mkU64(0ULL))); + } + } + else { + IRDirty* d; + IRTemp cc = newTemp(Ity_I64); + + s390x_vec_op_details_t details = { .serialized = 0ULL }; + details.op = S390_VEC_OP_VFCHE; + details.v1 = v1; + details.v2 = v2; + details.v3 = v3; + details.m4 = m4; + details.m5 = m5; + details.m6 = m6; + + d = unsafeIRDirty_1_N(cc, 0, "s390x_dirtyhelper_vec_op", + &s390x_dirtyhelper_vec_op, + mkIRExprVec_2(IRExpr_GSPTR(), + mkU64(details.serialized))); + + const UChar elementSize = isSingleElementOp ? sizeof(ULong) : sizeof(V128); + d->nFxState = 3; + vex_bzero(&d->fxState, sizeof(d->fxState)); + d->fxState[0].fx = Ifx_Read; + d->fxState[0].offset = S390X_GUEST_OFFSET(guest_v0) + v2 * sizeof(V128); + d->fxState[0].size = elementSize; + d->fxState[1].fx = Ifx_Read; + d->fxState[1].offset = S390X_GUEST_OFFSET(guest_v0) + v3 * sizeof(V128); + d->fxState[1].size = elementSize; + d->fxState[2].fx = Ifx_Write; + d->fxState[2].offset = S390X_GUEST_OFFSET(guest_v0) + v1 * sizeof(V128); + d->fxState[2].size = sizeof(V128); + + stmt(IRStmt_Dirty(d)); + s390_cc_set(cc); + } + + return "vfche"; +} + +static const HChar * +s390_irgen_VFTCI(UChar v1, UChar v2, UShort i3, UChar m4, UChar m5) +{ + s390_insn_assert("vftci", m4 == 3); + + Bool isSingleElementOp = s390_vr_is_single_element_control_set(m5); + + IRDirty* d; + IRTemp cc = newTemp(Ity_I64); + + s390x_vec_op_details_t details = { .serialized = 0ULL }; + details.op = S390_VEC_OP_VFTCI; + details.v1 = v1; + details.v2 = v2; + details.i3 = i3; + details.m4 = m4; + details.m5 = m5; + + d = unsafeIRDirty_1_N(cc, 0, "s390x_dirtyhelper_vec_op", + &s390x_dirtyhelper_vec_op, + mkIRExprVec_2(IRExpr_GSPTR(), + mkU64(details.serialized))); + + const UChar elementSize = isSingleElementOp ? sizeof(ULong) : sizeof(V128); + d->nFxState = 2; + vex_bzero(&d->fxState, sizeof(d->fxState)); + d->fxState[0].fx = Ifx_Read; + d->fxState[0].offset = S390X_GUEST_OFFSET(guest_v0) + v2 * sizeof(V128); + d->fxState[0].size = elementSize; + d->fxState[1].fx = Ifx_Write; + d->fxState[1].offset = S390X_GUEST_OFFSET(guest_v0) + v1 * sizeof(V128); + d->fxState[1].size = sizeof(V128); + + stmt(IRStmt_Dirty(d)); + s390_cc_set(cc); + + return "vftci"; +} + /* New insns are added here. If an insn is contingent on a facility being installed also check whether the list of supported facilities in function @@ -19358,6 +20061,18 @@ s390_decode_6byte_and_irgen(const UChar *bytes) unsigned int op2 : 8; } VRR; struct { + UInt op1 : 8; + UInt v1 : 4; + UInt v2 : 4; + UInt v3 : 4; + UInt : 4; + UInt m5 : 4; + UInt m4 : 4; + UInt m3 : 4; + UInt rxb : 4; + UInt op2 : 8; + } VRRa; + struct { unsigned int op1 : 8; unsigned int v1 : 4; unsigned int v2 : 4; @@ -19370,6 +20085,18 @@ s390_decode_6byte_and_irgen(const UChar *bytes) unsigned int op2 : 8; } VRRd; struct { + unsigned int op1 : 8; + unsigned int v1 : 4; + unsigned int v2 : 4; + unsigned int v3 : 4; + unsigned int m6 : 4; + unsigned int : 4; + unsigned int m5 : 4; + unsigned int v4 : 4; + unsigned int rxb : 4; + unsigned int op2 : 8; + } VRRe; + struct { unsigned int op1 : 8; unsigned int v1 : 4; unsigned int v3 : 4; @@ -19390,6 +20117,16 @@ s390_decode_6byte_and_irgen(const UChar *bytes) unsigned int op2 : 8; } VRId; struct { + UInt op1 : 8; + UInt v1 : 4; + UInt v2 : 4; + UInt i3 : 12; + UInt m5 : 4; + UInt m4 : 4; + UInt rxb : 4; + UInt op2 : 8; + } VRIe; + struct { unsigned int op1 : 8; unsigned int v1 : 4; unsigned int v3 : 4; @@ -19974,7 +20711,10 @@ s390_decode_6byte_and_irgen(const UChar *bytes) case 0xe70000000046ULL: s390_format_VRI_VIM(s390_irgen_VGM, ovl.fmt.VRI.v1, ovl.fmt.VRI.i2, ovl.fmt.VRI.m3, ovl.fmt.VRI.rxb); goto ok; - case 0xe7000000004aULL: /* VFTCI */ goto unimplemented; + case 0xe7000000004aULL: s390_format_VRI_VVIMM(s390_irgen_VFTCI, ovl.fmt.VRIe.v1, + ovl.fmt.VRIe.v2, ovl.fmt.VRIe.i3, + ovl.fmt.VRIe.m4, ovl.fmt.VRIe.m5, + ovl.fmt.VRIe.rxb); goto ok; case 0xe7000000004dULL: s390_format_VRI_VVIM(s390_irgen_VREP, ovl.fmt.VRI.v1, ovl.fmt.VRI.v3, ovl.fmt.VRI.i2, ovl.fmt.VRI.m3, ovl.fmt.VRI.rxb); goto ok; @@ -20087,19 +20827,27 @@ s390_decode_6byte_and_irgen(const UChar *bytes) ovl.fmt.VRR.v2, ovl.fmt.VRR.r3, ovl.fmt.VRR.m4, ovl.fmt.VRR.rxb); goto ok; case 0xe70000000085ULL: /* VBPERM */ goto unimplemented; - case 0xe7000000008aULL: s390_format_VRRd_VVVVMM(s390_irgen_VSTRC, ovl.fmt.VRRd.v1, - ovl.fmt.VRRd.v2, ovl.fmt.VRRd.v3, - ovl.fmt.VRRd.v4, ovl.fmt.VRRd.m5, - ovl.fmt.VRRd.m6, - ovl.fmt.VRRd.rxb); goto ok; + case 0xe7000000008aULL: s390_format_VRR_VVVVMM(s390_irgen_VSTRC, ovl.fmt.VRRd.v1, + ovl.fmt.VRRd.v2, ovl.fmt.VRRd.v3, + ovl.fmt.VRRd.v4, ovl.fmt.VRRd.m5, + ovl.fmt.VRRd.m6, + ovl.fmt.VRRd.rxb); goto ok; case 0xe7000000008cULL: s390_format_VRR_VVVV(s390_irgen_VPERM, ovl.fmt.VRR.v1, ovl.fmt.VRR.v2, ovl.fmt.VRR.r3, ovl.fmt.VRR.m4, ovl.fmt.VRR.rxb); goto ok; case 0xe7000000008dULL: s390_format_VRR_VVVV(s390_irgen_VSEL, ovl.fmt.VRR.v1, ovl.fmt.VRR.v2, ovl.fmt.VRR.r3, ovl.fmt.VRR.m4, ovl.fmt.VRR.rxb); goto ok; - case 0xe7000000008eULL: /* VFMS */ goto unimplemented; - case 0xe7000000008fULL: /* VFMA */ goto unimplemented; + case 0xe7000000008eULL: s390_format_VRR_VVVVMM(s390_irgen_VFMS, ovl.fmt.VRRe.v1, + ovl.fmt.VRRe.v2, ovl.fmt.VRRe.v3, + ovl.fmt.VRRe.v4, ovl.fmt.VRRe.m5, + ovl.fmt.VRRe.m6, + ovl.fmt.VRRe.rxb); goto ok; + case 0xe7000000008fULL: s390_format_VRR_VVVVMM(s390_irgen_VFMA, ovl.fmt.VRRe.v1, + ovl.fmt.VRRe.v2, ovl.fmt.VRRe.v3, + ovl.fmt.VRRe.v4, ovl.fmt.VRRe.m5, + ovl.fmt.VRRe.m6, + ovl.fmt.VRRe.rxb); goto ok; case 0xe70000000094ULL: s390_format_VRR_VVVM(s390_irgen_VPK, ovl.fmt.VRR.v1, ovl.fmt.VRR.v2, ovl.fmt.VRR.r3, ovl.fmt.VRR.m4, ovl.fmt.VRR.rxb); goto ok; @@ -20184,17 +20932,50 @@ s390_decode_6byte_and_irgen(const UChar *bytes) ovl.fmt.VRRd.v2, ovl.fmt.VRRd.v3, ovl.fmt.VRRd.v4, ovl.fmt.VRRd.m5, ovl.fmt.VRRd.rxb); goto ok; - case 0xe700000000c0ULL: /* VCLGD */ goto unimplemented; - case 0xe700000000c1ULL: /* VCDLG */ goto unimplemented; - case 0xe700000000c2ULL: /* VCGD */ goto unimplemented; - case 0xe700000000c3ULL: /* VCDG */ goto unimplemented; - case 0xe700000000c4ULL: /* VLDE */ goto unimplemented; - case 0xe700000000c5ULL: /* VLED */ goto unimplemented; - case 0xe700000000c7ULL: /* VFI */ goto unimplemented; - case 0xe700000000caULL: /* WFK */ goto unimplemented; - case 0xe700000000cbULL: /* WFC */ goto unimplemented; - case 0xe700000000ccULL: /* VFPSO */ goto unimplemented; - case 0xe700000000ceULL: /* VFSQ */ goto unimplemented; + case 0xe700000000c0ULL: s390_format_VRRa_VVMMM(s390_irgen_VCLGD, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000c1ULL: s390_format_VRRa_VVMMM(s390_irgen_VCDLG, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000c2ULL: s390_format_VRRa_VVMMM(s390_irgen_VCGD, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000c3ULL: s390_format_VRRa_VVMMM(s390_irgen_VCDG, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000c4ULL: s390_format_VRRa_VVMMM(s390_irgen_VLDE, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000c5ULL: s390_format_VRRa_VVMMM(s390_irgen_VLED, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000c7ULL: s390_format_VRRa_VVMMM(s390_irgen_VFI, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000caULL: s390_format_VRRa_VVMM(s390_irgen_WFK, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000cbULL: s390_format_VRRa_VVMM(s390_irgen_WFC, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000ccULL: s390_format_VRRa_VVMMM(s390_irgen_VFPSO, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000ceULL: s390_format_VRRa_VVMM(s390_irgen_VFSQ, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.m3, + ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.rxb); goto ok; case 0xe700000000d4ULL: s390_format_VRR_VVM(s390_irgen_VUPLL, ovl.fmt.VRR.v1, ovl.fmt.VRR.v2, ovl.fmt.VRR.m4, ovl.fmt.VRR.rxb); goto ok; @@ -20221,13 +21002,37 @@ s390_decode_6byte_and_irgen(const UChar *bytes) case 0xe700000000dfULL: s390_format_VRR_VVM(s390_irgen_VLP, ovl.fmt.VRR.v1, ovl.fmt.VRR.v2, ovl.fmt.VRR.m4, ovl.fmt.VRR.rxb); goto ok; - case 0xe700000000e2ULL: /* VFS */ goto unimplemented; - case 0xe700000000e3ULL: /* VFA */ goto unimplemented; - case 0xe700000000e5ULL: /* VFD */ goto unimplemented; - case 0xe700000000e7ULL: /* VFM */ goto unimplemented; - case 0xe700000000e8ULL: /* VFCE */ goto unimplemented; - case 0xe700000000eaULL: /* VFCHE */ goto unimplemented; - case 0xe700000000ebULL: /* VFCH */ goto unimplemented; + case 0xe700000000e2ULL: s390_format_VRRa_VVVMM(s390_irgen_VFS, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.v3, + ovl.fmt.VRRa.m3, ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000e3ULL: s390_format_VRRa_VVVMM(s390_irgen_VFA, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.v3, + ovl.fmt.VRRa.m3, ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000e5ULL: s390_format_VRRa_VVVMM(s390_irgen_VFD, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.v3, + ovl.fmt.VRRa.m3, ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000e7ULL: s390_format_VRRa_VVVMM(s390_irgen_VFM, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.v3, + ovl.fmt.VRRa.m3, ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000e8ULL: s390_format_VRRa_VVVMMM(s390_irgen_VFCE, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.v3, + ovl.fmt.VRRa.m3, ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000eaULL: s390_format_VRRa_VVVMMM(s390_irgen_VFCHE, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.v3, + ovl.fmt.VRRa.m3, ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; + case 0xe700000000ebULL: s390_format_VRRa_VVVMMM(s390_irgen_VFCH, ovl.fmt.VRRa.v1, + ovl.fmt.VRRa.v2, ovl.fmt.VRRa.v3, + ovl.fmt.VRRa.m3, ovl.fmt.VRRa.m4, + ovl.fmt.VRRa.m5, + ovl.fmt.VRRa.rxb); goto ok; case 0xe700000000eeULL: /* VFMIN */ goto unimplemented; case 0xe700000000efULL: /* VFMAX */ goto unimplemented; case 0xe700000000f0ULL: s390_format_VRR_VVVM(s390_irgen_VAVGL, ovl.fmt.VRR.v1, @@ -21148,7 +21953,13 @@ s390_decode_and_irgen(const UChar *bytes, UInt insn_length, DisResult *dres) dis_res->jk_StopHere = Ijk_Boring; } - if (status == S390_DECODE_OK) return insn_length; /* OK */ + if (status == S390_DECODE_OK) { + /* Adjust status if a specification exception was indicated. */ + if (is_specification_exception()) + status = S390_DECODE_SPECIFICATION_EXCEPTION; + else + return insn_length; /* OK */ + } /* Decoding failed somehow */ if (sigill_diag) { @@ -21166,6 +21977,10 @@ s390_decode_and_irgen(const UChar *bytes, UInt insn_length, DisResult *dres) vex_printf("unimplemented special insn: "); break; + case S390_DECODE_SPECIFICATION_EXCEPTION: + vex_printf("specification exception: "); + break; + case S390_DECODE_ERROR: vex_printf("decoding error: "); break; diff --git a/VEX/priv/host_s390_defs.c b/VEX/priv/host_s390_defs.c index 98ac938..22cdd04 100644 --- a/VEX/priv/host_s390_defs.c +++ b/VEX/priv/host_s390_defs.c @@ -1711,6 +1711,23 @@ emit_VRR_VVM(UChar *p, ULong op, UChar v1, UChar v2, UChar m4) return emit_6bytes(p, the_insn); } +static UChar * +emit_VRR_VVMMM(UChar *p, ULong op, UChar v1, UChar v2, UChar m3, UChar m4, + UChar m5) +{ + ULong the_insn = op; + ULong rxb = s390_update_rxb(0, 1, &v1); + rxb = s390_update_rxb(rxb, 2, &v2); + + the_insn |= ((ULong)v1) << 36; + the_insn |= ((ULong)v2) << 32; + the_insn |= ((ULong)m5) << 20; + the_insn |= ((ULong)m4) << 16; + the_insn |= ((ULong)m3) << 12; + the_insn |= ((ULong)rxb) << 8; + + return emit_6bytes(p, the_insn); +} static UChar * emit_VRR_VVVM(UChar *p, ULong op, UChar v1, UChar v2, UChar v3, UChar m4) @@ -1762,6 +1779,26 @@ emit_VRR_VVVV(UChar *p, ULong op, UChar v1, UChar v2, UChar v3, UChar v4) return emit_6bytes(p, the_insn); } +static UChar * +emit_VRRe_VVVVMM(UChar *p, ULong op, UChar v1, UChar v2, UChar v3, UChar v4, + UChar m5, UChar m6) +{ + ULong the_insn = op; + ULong rxb = s390_update_rxb(0, 1, &v1); + rxb = s390_update_rxb(rxb, 2, &v2); + rxb = s390_update_rxb(rxb, 3, &v3); + rxb = s390_update_rxb(rxb, 4, &v4); + + the_insn |= ((ULong)v1) << 36; + the_insn |= ((ULong)v2) << 32; + the_insn |= ((ULong)v3) << 28; + the_insn |= ((ULong)m6) << 24; + the_insn |= ((ULong)m5) << 16; + the_insn |= ((ULong)v4) << 12; + the_insn |= ((ULong)rxb) << 8; + + return emit_6bytes(p, the_insn); +} static UChar * emit_VRR_VRR(UChar *p, ULong op, UChar v1, UChar r2, UChar r3) @@ -1777,6 +1814,33 @@ emit_VRR_VRR(UChar *p, ULong op, UChar v1, UChar r2, UChar r3) return emit_6bytes(p, the_insn); } +static UChar * +emit_VRR_VVVMMM(UChar *p, ULong op, UChar v1, UChar v2, UChar v3, UChar m4, + UChar m5, UChar m6) +{ + ULong the_insn = op; + ULong rxb = s390_update_rxb(0, 1, &v1); + rxb = s390_update_rxb(rxb, 2, &v2); + rxb = s390_update_rxb(rxb, 3, &v3); + + the_insn |= ((ULong)v1) << 36; + the_insn |= ((ULong)v2) << 32; + the_insn |= ((ULong)v3) << 28; + the_insn |= ((ULong)m6) << 20; + the_insn |= ((ULong)m5) << 16; + the_insn |= ((ULong)m4) << 12; + the_insn |= ((ULong)rxb) << 8; + + return emit_6bytes(p, the_insn); +} + +static UChar* +emit_VRR_VVVMM(UChar *p, ULong op, UChar v1, UChar v2, UChar v3, UChar m4, + UChar m5) +{ + return emit_VRR_VVVMMM(p, op, v1, v2, v3, m4, m5, 0); +} + /*------------------------------------------------------------*/ /*--- Functions to emit particular instructions ---*/ /*------------------------------------------------------------*/ @@ -6057,6 +6121,116 @@ s390_emit_VLVGP(UChar *p, UChar v1, UChar r2, UChar r3) return emit_VRR_VRR(p, 0xE70000000062ULL, v1, r2, r3); } +static UChar * +s390_emit_VFPSO(UChar *p, UChar v1, UChar v2, UChar m3, UChar m4, UChar m5) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC6(MNM, VR, VR, UINT, UINT, UINT), "vfpso", v1, v2, m3, m4, + m5); + + return emit_VRR_VVMMM(p, 0xE700000000CCULL, v1, v2, m3, m4, m5); +} + +static UChar * +s390_emit_VFA(UChar *p, UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC6(MNM, VR, VR, VR, UINT, UINT), "vfa", v1, v2, v3, m4, m5); + + return emit_VRR_VVVMM(p, 0xE700000000e3ULL, v1, v2, v3, m4, m5); +} + +static UChar * +s390_emit_VFS(UChar *p, UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC6(MNM, VR, VR, VR, UINT, UINT), "vfs", v1, v2, v3, m4, m5); + + return emit_VRR_VVVMM(p, 0xE700000000e2ULL, v1, v2, v3, m4, m5); +} + +static UChar * +s390_emit_VFM(UChar *p, UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC6(MNM, VR, VR, VR, UINT, UINT), "vfm", v1, v2, v3, m4, m5); + + return emit_VRR_VVVMM(p, 0xE700000000e7ULL, v1, v2, v3, m4, m5); +} + +static UChar * +s390_emit_VFD(UChar *p, UChar v1, UChar v2, UChar v3, UChar m4, UChar m5) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC6(MNM, VR, VR, VR, UINT, UINT), "vfd", v1, v2, v3, m4, m5); + + return emit_VRR_VVVMM(p, 0xE700000000e5ULL, v1, v2, v3, m4, m5); +} + +static UChar * +s390_emit_VFSQ(UChar *p, UChar v1, UChar v2, UChar m3, UChar m4) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC5(MNM, VR, VR, UINT, UINT), "vfsq", v1, v2, m3, m4); + + return emit_VRR_VVMMM(p, 0xE700000000CEULL, v1, v2, m3, m4, 0); +} + +static UChar * +s390_emit_VFMA(UChar *p, UChar v1, UChar v2, UChar v3, UChar v4, UChar m5, + UChar m6) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC7(MNM, VR, VR, VR, VR, UINT, UINT), "vfma", + v1, v2, v3, v4, m5, m6); + + return emit_VRRe_VVVVMM(p, 0xE7000000008fULL, v1, v2, v3, v4, m5, m6); +} + +static UChar * +s390_emit_VFMS(UChar *p, UChar v1, UChar v2, UChar v3, UChar v4, UChar m5, + UChar m6) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC7(MNM, VR, VR, VR, VR, UINT, UINT), "vfms", + v1, v2, v3, v4, m5, m6); + + return emit_VRRe_VVVVMM(p, 0xE7000000008eULL, v1, v2, v3, v4, m5, m6); +} + +static UChar * +s390_emit_VFCE(UChar *p, UChar v1, UChar v2, UChar v3, UChar m4, UChar m5, + UChar m6) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC7(MNM, VR, VR, VR, UINT, UINT, UINT), "vfce", + v1, v2, v3, m4, m5, m6); + + return emit_VRR_VVVMMM(p, 0xE700000000e8ULL, v1, v2, v3, m4, m5, m6); +} + +static UChar * +s390_emit_VFCH(UChar *p, UChar v1, UChar v2, UChar v3, UChar m4, UChar m5, + UChar m6) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC7(MNM, VR, VR, VR, UINT, UINT, UINT), "vfch", + v1, v2, v3, m4, m5, m6); + + return emit_VRR_VVVMMM(p, 0xE700000000ebULL, v1, v2, v3, m4, m5, m6); +} + +static UChar * +s390_emit_VFCHE(UChar *p, UChar v1, UChar v2, UChar v3, UChar m4, UChar m5, + UChar m6) +{ + if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM)) + s390_disasm(ENC7(MNM, VR, VR, VR, UINT, UINT, UINT), "vfche", + v1, v2, v3, m4, m5, m6); + + return emit_VRR_VVVMMM(p, 0xE700000000eaULL, v1, v2, v3, m4, m5, m6); +} + /*---------------------------------------------------------------*/ /*--- Constructors for the various s390_insn kinds ---*/ /*---------------------------------------------------------------*/ @@ -7201,7 +7375,6 @@ s390_insn *s390_insn_vec_triop(UChar size, s390_vec_triop_t tag, HReg dst, { s390_insn *insn = LibVEX_Alloc_inline(sizeof(s390_insn)); - vassert(size == 16); insn->tag = S390_INSN_VEC_TRIOP; insn->size = size; @@ -7508,6 +7681,18 @@ s390_insn_as_string(const s390_insn *insn) op = "v-vunpacku"; break; + case S390_VEC_FLOAT_NEG: + op = "v-vfloatneg"; + break; + + case S390_VEC_FLOAT_SQRT: + op = "v-vfloatsqrt"; + break; + + case S390_VEC_FLOAT_ABS: + op = "v-vfloatabs"; + break; + default: goto fail; } @@ -7880,6 +8065,13 @@ s390_insn_as_string(const s390_insn *insn) case S390_VEC_PWSUM_DW: op = "v-vpwsumdw"; break; case S390_VEC_PWSUM_QW: op = "v-vpwsumqw"; break; case S390_VEC_INIT_FROM_GPRS: op = "v-vinitfromgprs"; break; + case S390_VEC_FLOAT_ADD: op = "v-vfloatadd"; break; + case S390_VEC_FLOAT_SUB: op = "v-vfloatsub"; break; + case S390_VEC_FLOAT_MUL: op = "v-vfloatmul"; break; + case S390_VEC_FLOAT_DIV: op = "v-vfloatdiv"; break; + case S390_VEC_FLOAT_COMPARE_EQUAL: op = "v-vfloatcmpeq"; break; + case S390_VEC_FLOAT_COMPARE_LESS_OR_EQUAL: op = "v-vfloatcmple"; break; + case S390_VEC_FLOAT_COMPARE_LESS: op = "v-vfloatcmpl"; break; default: goto fail; } s390_sprintf(buf, "%M %R, %R, %R", op, insn->variant.vec_binop.dst, @@ -7889,6 +8081,8 @@ s390_insn_as_string(const s390_insn *insn) case S390_INSN_VEC_TRIOP: switch (insn->variant.vec_triop.tag) { case S390_VEC_PERM: op = "v-vperm"; break; + case S390_VEC_FLOAT_MADD: op = "v-vfloatmadd"; break; + case S390_VEC_FLOAT_MSUB: op = "v-vfloatmsub"; break; default: goto fail; } s390_sprintf(buf, "%M %R, %R, %R, %R", op, insn->variant.vec_triop.dst, @@ -9036,6 +9230,27 @@ s390_insn_unop_emit(UChar *buf, const s390_insn *insn) return s390_emit_VPOPCT(buf, v1, v2, s390_getM_from_size(insn->size)); } + case S390_VEC_FLOAT_NEG: { + vassert(insn->variant.unop.src.tag == S390_OPND_REG); + vassert(insn->size == 8); + UChar v1 = hregNumber(insn->variant.unop.dst); + UChar v2 = hregNumber(insn->variant.unop.src.variant.reg); + return s390_emit_VFPSO(buf, v1, v2, s390_getM_from_size(insn->size), 0, 0); + } + case S390_VEC_FLOAT_ABS: { + vassert(insn->variant.unop.src.tag == S390_OPND_REG); + vassert(insn->size == 8); + UChar v1 = hregNumber(insn->variant.unop.dst); + UChar v2 = hregNumber(insn->variant.unop.src.variant.reg); + return s390_emit_VFPSO(buf, v1, v2, s390_getM_from_size(insn->size), 0, 2); + } + case S390_VEC_FLOAT_SQRT: { + vassert(insn->variant.unop.src.tag == S390_OPND_REG); + vassert(insn->size == 8); + UChar v1 = hregNumber(insn->variant.unop.dst); + UChar v2 = hregNumber(insn->variant.unop.src.variant.reg); + return s390_emit_VFSQ(buf, v1, v2, s390_getM_from_size(insn->size), 0); + } default: vpanic("s390_insn_unop_emit"); } @@ -11049,6 +11264,21 @@ s390_insn_vec_binop_emit(UChar *buf, const s390_insn *insn) return s390_emit_VSUMQ(buf, v1, v2, v3, s390_getM_from_size(size)); case S390_VEC_INIT_FROM_GPRS: return s390_emit_VLVGP(buf, v1, v2, v3); + case S390_VEC_FLOAT_ADD: + return s390_emit_VFA(buf, v1, v2, v3, s390_getM_from_size(size), 0); + case S390_VEC_FLOAT_SUB: + return s390_emit_VFS(buf, v1, v2, v3, s390_getM_from_size(size), 0); + case S390_VEC_FLOAT_MUL: + return s390_emit_VFM(buf, v1, v2, v3, s390_getM_from_size(size), 0); + case S390_VEC_FLOAT_DIV: + return s390_emit_VFD(buf, v1, v2, v3, s390_getM_from_size(size), 0); + case S390_VEC_FLOAT_COMPARE_EQUAL: + return s390_emit_VFCE(buf, v1, v2, v3, s390_getM_from_size(size), 0, 0); + case S390_VEC_FLOAT_COMPARE_LESS_OR_EQUAL: + return s390_emit_VFCH(buf, v1, v3, v2, s390_getM_from_size(size), 0, 0); + case S390_VEC_FLOAT_COMPARE_LESS: + return s390_emit_VFCHE(buf, v1, v3, v2, s390_getM_from_size(size), 0, 0); + default: goto fail; } @@ -11070,8 +11300,14 @@ s390_insn_vec_triop_emit(UChar *buf, const s390_insn *insn) UChar v4 = hregNumber(insn->variant.vec_triop.op3); switch (tag) { - case S390_VEC_PERM: + case S390_VEC_PERM: { + vassert(insn->size == 16); return s390_emit_VPERM(buf, v1, v2, v3, v4); + } + case S390_VEC_FLOAT_MADD: + return s390_emit_VFMA(buf, v1, v2, v3, v4, 0, 3); + case S390_VEC_FLOAT_MSUB: + return s390_emit_VFMS(buf, v1, v2, v3, v4, 0, 3); default: goto fail; } diff --git a/VEX/priv/host_s390_defs.h b/VEX/priv/host_s390_defs.h index 7ea0101..40f0472 100644 --- a/VEX/priv/host_s390_defs.h +++ b/VEX/priv/host_s390_defs.h @@ -202,7 +202,10 @@ typedef enum { S390_VEC_ABS, S390_VEC_COUNT_LEADING_ZEROES, S390_VEC_COUNT_TRAILING_ZEROES, - S390_VEC_COUNT_ONES + S390_VEC_COUNT_ONES, + S390_VEC_FLOAT_NEG, + S390_VEC_FLOAT_ABS, + S390_VEC_FLOAT_SQRT } s390_unop_t; /* The kind of ternary BFP operations */ @@ -394,11 +397,20 @@ typedef enum { S390_VEC_PWSUM_QW, S390_VEC_INIT_FROM_GPRS, + S390_VEC_FLOAT_ADD, + S390_VEC_FLOAT_SUB, + S390_VEC_FLOAT_MUL, + S390_VEC_FLOAT_DIV, + S390_VEC_FLOAT_COMPARE_EQUAL, + S390_VEC_FLOAT_COMPARE_LESS_OR_EQUAL, + S390_VEC_FLOAT_COMPARE_LESS } s390_vec_binop_t; /* The vector operations with three operands */ typedef enum { - S390_VEC_PERM + S390_VEC_PERM, + S390_VEC_FLOAT_MADD, + S390_VEC_FLOAT_MSUB } s390_vec_triop_t; /* The details of a CDAS insn. Carved out to keep the size of diff --git a/VEX/priv/host_s390_isel.c b/VEX/priv/host_s390_isel.c index bc34f90..79581ff 100644 --- a/VEX/priv/host_s390_isel.c +++ b/VEX/priv/host_s390_isel.c @@ -787,10 +787,12 @@ get_bfp_rounding_mode(ISelEnv *env, IRExpr *irrm) IRRoundingMode mode = irrm->Iex.Const.con->Ico.U32; switch (mode) { - case Irrm_NEAREST: return S390_BFP_ROUND_NEAREST_EVEN; - case Irrm_ZERO: return S390_BFP_ROUND_ZERO; - case Irrm_PosINF: return S390_BFP_ROUND_POSINF; - case Irrm_NegINF: return S390_BFP_ROUND_NEGINF; + case Irrm_NEAREST_TIE_AWAY_0: return S390_BFP_ROUND_NEAREST_AWAY; + case Irrm_PREPARE_SHORTER: return S390_BFP_ROUND_PREPARE_SHORT; + case Irrm_NEAREST: return S390_BFP_ROUND_NEAREST_EVEN; + case Irrm_ZERO: return S390_BFP_ROUND_ZERO; + case Irrm_PosINF: return S390_BFP_ROUND_POSINF; + case Irrm_NegINF: return S390_BFP_ROUND_NEGINF; default: vpanic("get_bfp_rounding_mode"); } @@ -3871,6 +3873,17 @@ s390_isel_vec_expr_wrk(ISelEnv *env, IRExpr *expr) vec_op = S390_VEC_COUNT_ONES; goto Iop_V_wrk; + case Iop_Neg64Fx2: + size = 8; + vec_op = S390_VEC_FLOAT_NEG; + goto Iop_V_wrk; + + case Iop_Abs64Fx2: + size = 8; + vec_op = S390_VEC_FLOAT_ABS; + goto Iop_V_wrk; + + Iop_V_wrk: { dst = newVRegV(env); reg1 = s390_isel_vec_expr(env, arg); @@ -4388,6 +4401,28 @@ s390_isel_vec_expr_wrk(ISelEnv *env, IRExpr *expr) vec_op = S390_VEC_ELEM_ROLL_V; goto Iop_VV_wrk; + case Iop_CmpEQ64Fx2: + size = 8; + vec_op = S390_VEC_FLOAT_COMPARE_EQUAL; + goto Iop_VV_wrk; + + case Iop_CmpLE64Fx2: { + size = 8; + vec_op = S390_VEC_FLOAT_COMPARE_LESS_OR_EQUAL; + goto Iop_VV_wrk; + } + + case Iop_CmpLT64Fx2: { + size = 8; + vec_op = S390_VEC_FLOAT_COMPARE_LESS; + goto Iop_VV_wrk; + } + + case Iop_Sqrt64Fx2: + size = 8; + vec_op = S390_VEC_FLOAT_SQRT; + goto Iop_irrm_V_wrk; + case Iop_ShlN8x16: size = 1; shift_op = S390_VEC_ELEM_SHL_INT; @@ -4493,6 +4528,14 @@ s390_isel_vec_expr_wrk(ISelEnv *env, IRExpr *expr) return dst; } + Iop_irrm_V_wrk: { + set_bfp_rounding_mode_in_fpc(env, arg1); + reg1 = s390_isel_vec_expr(env, arg2); + + addInstr(env, s390_insn_unop(size, vec_op, dst, s390_opnd_reg(reg1))); + return dst; + } + case Iop_64HLtoV128: reg1 = s390_isel_int_expr(env, arg1); reg2 = s390_isel_int_expr(env, arg2); @@ -4516,6 +4559,7 @@ s390_isel_vec_expr_wrk(ISelEnv *env, IRExpr *expr) IRExpr* arg1 = expr->Iex.Triop.details->arg1; IRExpr* arg2 = expr->Iex.Triop.details->arg2; IRExpr* arg3 = expr->Iex.Triop.details->arg3; + IROp vec_op; switch (op) { case Iop_SetElem8x16: size = 1; @@ -4551,6 +4595,36 @@ s390_isel_vec_expr_wrk(ISelEnv *env, IRExpr *expr) dst, reg1, reg2, reg3)); return dst; + case Iop_Add64Fx2: + size = 8; + vec_op = S390_VEC_FLOAT_ADD; + goto Iop_irrm_VV_wrk; + + case Iop_Sub64Fx2: + size = 8; + vec_op = S390_VEC_FLOAT_SUB; + goto Iop_irrm_VV_wrk; + + case Iop_Mul64Fx2: + size = 8; + vec_op = S390_VEC_FLOAT_MUL; + goto Iop_irrm_VV_wrk; + case Iop_Div64Fx2: + size = 8; + vec_op = S390_VEC_FLOAT_DIV; + goto Iop_irrm_VV_wrk; + + Iop_irrm_VV_wrk: { + set_bfp_rounding_mode_in_fpc(env, arg1); + reg1 = s390_isel_vec_expr(env, arg2); + reg2 = s390_isel_vec_expr(env, arg3); + + addInstr(env, s390_insn_vec_binop(size, vec_op, + dst, reg1, reg2)); + + return dst; + } + default: goto irreducible; }