target/arm: Implement MVE VCVT with specified rounding mode

Implement the MVE VCVT which converts from floating-point to integer
using a rounding mode specified by the instruction.  We implement
this similarly to the Neon equivalents, by passing the required
rounding mode as an extra integer parameter to the helper functions.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Peter Maydell 2021-09-01 09:02:38 +01:00
parent 2ec0dcf034
commit 53fc5f6139
4 changed files with 105 additions and 0 deletions

View File

@ -177,6 +177,11 @@ DEF_HELPER_FLAGS_3(mve_vminab, TCG_CALL_NO_WG, void, env, ptr, ptr)
DEF_HELPER_FLAGS_3(mve_vminah, TCG_CALL_NO_WG, void, env, ptr, ptr)
DEF_HELPER_FLAGS_3(mve_vminaw, TCG_CALL_NO_WG, void, env, ptr, ptr)
DEF_HELPER_FLAGS_4(mve_vcvt_rm_sh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(mve_vcvt_rm_uh, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(mve_vcvt_rm_ss, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(mve_vcvt_rm_us, TCG_CALL_NO_WG, void, env, ptr, ptr, i32)
DEF_HELPER_FLAGS_3(mve_vmovnbb, TCG_CALL_NO_WG, void, env, ptr, ptr)
DEF_HELPER_FLAGS_3(mve_vmovnbh, TCG_CALL_NO_WG, void, env, ptr, ptr)
DEF_HELPER_FLAGS_3(mve_vmovntb, TCG_CALL_NO_WG, void, env, ptr, ptr)

View File

@ -805,3 +805,13 @@ VCVT_SF 1111 1111 1 . 11 .. 11 ... 0 011 00 1 . 0 ... 0 @1op
VCVT_UF 1111 1111 1 . 11 .. 11 ... 0 011 01 1 . 0 ... 0 @1op
VCVT_FS 1111 1111 1 . 11 .. 11 ... 0 011 10 1 . 0 ... 0 @1op
VCVT_FU 1111 1111 1 . 11 .. 11 ... 0 011 11 1 . 0 ... 0 @1op
# VCVT from floating point to integer with specified rounding mode
VCVTAS 1111 1111 1 . 11 .. 11 ... 000 00 0 1 . 0 ... 0 @1op
VCVTAU 1111 1111 1 . 11 .. 11 ... 000 00 1 1 . 0 ... 0 @1op
VCVTNS 1111 1111 1 . 11 .. 11 ... 000 01 0 1 . 0 ... 0 @1op
VCVTNU 1111 1111 1 . 11 .. 11 ... 000 01 1 1 . 0 ... 0 @1op
VCVTPS 1111 1111 1 . 11 .. 11 ... 000 10 0 1 . 0 ... 0 @1op
VCVTPU 1111 1111 1 . 11 .. 11 ... 000 10 1 1 . 0 ... 0 @1op
VCVTMS 1111 1111 1 . 11 .. 11 ... 000 11 0 1 . 0 ... 0 @1op
VCVTMU 1111 1111 1 . 11 .. 11 ... 000 11 1 1 . 0 ... 0 @1op

View File

@ -3294,3 +3294,41 @@ DO_VCVT_FIXED(vcvt_sf, 4, int32_t, helper_vfp_sltos)
DO_VCVT_FIXED(vcvt_uf, 4, uint32_t, helper_vfp_ultos)
DO_VCVT_FIXED(vcvt_fs, 4, int32_t, helper_vfp_tosls_round_to_zero)
DO_VCVT_FIXED(vcvt_fu, 4, uint32_t, helper_vfp_touls_round_to_zero)
/* VCVT with specified rmode */
#define DO_VCVT_RMODE(OP, ESIZE, TYPE, FN) \
void HELPER(glue(mve_, OP))(CPUARMState *env, \
void *vd, void *vm, uint32_t rmode) \
{ \
TYPE *d = vd, *m = vm; \
TYPE r; \
uint16_t mask = mve_element_mask(env); \
unsigned e; \
float_status *fpst; \
float_status scratch_fpst; \
float_status *base_fpst = (ESIZE == 2) ? \
&env->vfp.standard_fp_status_f16 : \
&env->vfp.standard_fp_status; \
uint32_t prev_rmode = get_float_rounding_mode(base_fpst); \
set_float_rounding_mode(rmode, base_fpst); \
for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE) { \
if ((mask & MAKE_64BIT_MASK(0, ESIZE)) == 0) { \
continue; \
} \
fpst = base_fpst; \
if (!(mask & 1)) { \
/* We need the result but without updating flags */ \
scratch_fpst = *fpst; \
fpst = &scratch_fpst; \
} \
r = FN(m[H##ESIZE(e)], 0, fpst); \
mergemask(&d[H##ESIZE(e)], r, mask); \
} \
set_float_rounding_mode(prev_rmode, base_fpst); \
mve_advance_vpt(env); \
}
DO_VCVT_RMODE(vcvt_rm_sh, 2, uint16_t, helper_vfp_toshh)
DO_VCVT_RMODE(vcvt_rm_uh, 2, uint16_t, helper_vfp_touhh)
DO_VCVT_RMODE(vcvt_rm_ss, 4, uint32_t, helper_vfp_tosls)
DO_VCVT_RMODE(vcvt_rm_us, 4, uint32_t, helper_vfp_touls)

View File

@ -49,6 +49,7 @@ typedef void MVEGenCmpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr);
typedef void MVEGenScalarCmpFn(TCGv_ptr, TCGv_ptr, TCGv_i32);
typedef void MVEGenVABAVFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
typedef void MVEGenDualAccOpFn(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
typedef void MVEGenVCVTRmodeFn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32);
/* Return the offset of a Qn register (same semantics as aa32_vfp_qreg()) */
static inline long mve_qreg_offset(unsigned reg)
@ -575,6 +576,57 @@ DO_VCVT(VCVT_UF, vcvt_uh, vcvt_uf)
DO_VCVT(VCVT_FS, vcvt_hs, vcvt_fs)
DO_VCVT(VCVT_FU, vcvt_hu, vcvt_fu)
static bool do_vcvt_rmode(DisasContext *s, arg_1op *a,
enum arm_fprounding rmode, bool u)
{
/*
* Handle VCVT fp to int with specified rounding mode.
* This is a 1op fn but we must pass the rounding mode as
* an immediate to the helper.
*/
TCGv_ptr qd, qm;
static MVEGenVCVTRmodeFn * const fns[4][2] = {
{ NULL, NULL },
{ gen_helper_mve_vcvt_rm_sh, gen_helper_mve_vcvt_rm_uh },
{ gen_helper_mve_vcvt_rm_ss, gen_helper_mve_vcvt_rm_us },
{ NULL, NULL },
};
MVEGenVCVTRmodeFn *fn = fns[a->size][u];
if (!dc_isar_feature(aa32_mve_fp, s) ||
!mve_check_qreg_bank(s, a->qd | a->qm) ||
!fn) {
return false;
}
if (!mve_eci_check(s) || !vfp_access_check(s)) {
return true;
}
qd = mve_qreg_ptr(a->qd);
qm = mve_qreg_ptr(a->qm);
fn(cpu_env, qd, qm, tcg_constant_i32(arm_rmode_to_sf(rmode)));
tcg_temp_free_ptr(qd);
tcg_temp_free_ptr(qm);
mve_update_eci(s);
return true;
}
#define DO_VCVT_RMODE(INSN, RMODE, U) \
static bool trans_##INSN(DisasContext *s, arg_1op *a) \
{ \
return do_vcvt_rmode(s, a, RMODE, U); \
} \
DO_VCVT_RMODE(VCVTAS, FPROUNDING_TIEAWAY, false)
DO_VCVT_RMODE(VCVTAU, FPROUNDING_TIEAWAY, true)
DO_VCVT_RMODE(VCVTNS, FPROUNDING_TIEEVEN, false)
DO_VCVT_RMODE(VCVTNU, FPROUNDING_TIEEVEN, true)
DO_VCVT_RMODE(VCVTPS, FPROUNDING_POSINF, false)
DO_VCVT_RMODE(VCVTPU, FPROUNDING_POSINF, true)
DO_VCVT_RMODE(VCVTMS, FPROUNDING_NEGINF, false)
DO_VCVT_RMODE(VCVTMU, FPROUNDING_NEGINF, true)
/* Narrowing moves: only size 0 and 1 are valid */
#define DO_VMOVN(INSN, FN) \
static bool trans_##INSN(DisasContext *s, arg_1op *a) \