target/arm: Implement FCVT (scalar, integer) for fp16

Cc: qemu-stable@nongnu.org
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Tested-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 20180512003217.9105-4-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Richard Henderson 2018-05-15 14:58:43 +01:00 committed by Peter Maydell
parent 8c738d4307
commit 564a063250
3 changed files with 122 additions and 18 deletions

View File

@ -11427,8 +11427,12 @@ VFP_CONV_FIX_A64(uq, s, 32, 64, uint64)
#undef VFP_CONV_FIX_A64 #undef VFP_CONV_FIX_A64
/* Conversion to/from f16 can overflow to infinity before/after scaling. /* Conversion to/from f16 can overflow to infinity before/after scaling.
* Therefore we convert to f64 (which does not round), scale, * Therefore we convert to f64, scale, and then convert f64 to f16; or
* and then convert f64 to f16 (which may round). * vice versa for conversion to integer.
*
* For 16- and 32-bit integers, the conversion to f64 never rounds.
* For 64-bit integers, any integer that would cause rounding will also
* overflow to f16 infinity, so there is no double rounding problem.
*/ */
static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst) static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst)
@ -11446,6 +11450,16 @@ float16 HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst)
return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst); return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst);
} }
float16 HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst)
{
return do_postscale_fp16(int64_to_float64(x, fpst), shift, fpst);
}
float16 HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst)
{
return do_postscale_fp16(uint64_to_float64(x, fpst), shift, fpst);
}
static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst) static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst)
{ {
if (unlikely(float16_is_any_nan(f))) { if (unlikely(float16_is_any_nan(f))) {
@ -11475,6 +11489,26 @@ uint32_t HELPER(vfp_touhh)(float16 x, uint32_t shift, void *fpst)
return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst); return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst);
} }
uint32_t HELPER(vfp_toslh)(float16 x, uint32_t shift, void *fpst)
{
return float64_to_int32(do_prescale_fp16(x, shift, fpst), fpst);
}
uint32_t HELPER(vfp_toulh)(float16 x, uint32_t shift, void *fpst)
{
return float64_to_uint32(do_prescale_fp16(x, shift, fpst), fpst);
}
uint64_t HELPER(vfp_tosqh)(float16 x, uint32_t shift, void *fpst)
{
return float64_to_int64(do_prescale_fp16(x, shift, fpst), fpst);
}
uint64_t HELPER(vfp_touqh)(float16 x, uint32_t shift, void *fpst)
{
return float64_to_uint64(do_prescale_fp16(x, shift, fpst), fpst);
}
/* Set the current fp rounding mode and return the old one. /* Set the current fp rounding mode and return the old one.
* The argument is a softfloat float_round_ value. * The argument is a softfloat float_round_ value.
*/ */

View File

@ -151,6 +151,10 @@ DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr)
DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr)
DEF_HELPER_3(vfp_touhh, i32, f16, i32, ptr) DEF_HELPER_3(vfp_touhh, i32, f16, i32, ptr)
DEF_HELPER_3(vfp_toshh, i32, f16, i32, ptr) DEF_HELPER_3(vfp_toshh, i32, f16, i32, ptr)
DEF_HELPER_3(vfp_toulh, i32, f16, i32, ptr)
DEF_HELPER_3(vfp_toslh, i32, f16, i32, ptr)
DEF_HELPER_3(vfp_touqh, i64, f16, i32, ptr)
DEF_HELPER_3(vfp_tosqh, i64, f16, i32, ptr)
DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr) DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr)
DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr)
DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr) DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr)
@ -177,6 +181,8 @@ DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr)
DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr) DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr)
DEF_HELPER_3(vfp_sltoh, f16, i32, i32, ptr) DEF_HELPER_3(vfp_sltoh, f16, i32, i32, ptr)
DEF_HELPER_3(vfp_ultoh, f16, i32, i32, ptr) DEF_HELPER_3(vfp_ultoh, f16, i32, i32, ptr)
DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, ptr)
DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, ptr)
DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, ptr) DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, ptr)
DEF_HELPER_FLAGS_2(set_neon_rmode, TCG_CALL_NO_RWG, i32, i32, env) DEF_HELPER_FLAGS_2(set_neon_rmode, TCG_CALL_NO_RWG, i32, i32, env)

View File

@ -5511,11 +5511,11 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
bool itof, int rmode, int scale, int sf, int type) bool itof, int rmode, int scale, int sf, int type)
{ {
bool is_signed = !(opcode & 1); bool is_signed = !(opcode & 1);
bool is_double = type;
TCGv_ptr tcg_fpstatus; TCGv_ptr tcg_fpstatus;
TCGv_i32 tcg_shift; TCGv_i32 tcg_shift, tcg_single;
TCGv_i64 tcg_double;
tcg_fpstatus = get_fpstatus_ptr(false); tcg_fpstatus = get_fpstatus_ptr(type == 3);
tcg_shift = tcg_const_i32(64 - scale); tcg_shift = tcg_const_i32(64 - scale);
@ -5533,8 +5533,9 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
tcg_int = tcg_extend; tcg_int = tcg_extend;
} }
if (is_double) { switch (type) {
TCGv_i64 tcg_double = tcg_temp_new_i64(); case 1: /* float64 */
tcg_double = tcg_temp_new_i64();
if (is_signed) { if (is_signed) {
gen_helper_vfp_sqtod(tcg_double, tcg_int, gen_helper_vfp_sqtod(tcg_double, tcg_int,
tcg_shift, tcg_fpstatus); tcg_shift, tcg_fpstatus);
@ -5544,8 +5545,10 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
} }
write_fp_dreg(s, rd, tcg_double); write_fp_dreg(s, rd, tcg_double);
tcg_temp_free_i64(tcg_double); tcg_temp_free_i64(tcg_double);
} else { break;
TCGv_i32 tcg_single = tcg_temp_new_i32();
case 0: /* float32 */
tcg_single = tcg_temp_new_i32();
if (is_signed) { if (is_signed) {
gen_helper_vfp_sqtos(tcg_single, tcg_int, gen_helper_vfp_sqtos(tcg_single, tcg_int,
tcg_shift, tcg_fpstatus); tcg_shift, tcg_fpstatus);
@ -5555,6 +5558,23 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
} }
write_fp_sreg(s, rd, tcg_single); write_fp_sreg(s, rd, tcg_single);
tcg_temp_free_i32(tcg_single); tcg_temp_free_i32(tcg_single);
break;
case 3: /* float16 */
tcg_single = tcg_temp_new_i32();
if (is_signed) {
gen_helper_vfp_sqtoh(tcg_single, tcg_int,
tcg_shift, tcg_fpstatus);
} else {
gen_helper_vfp_uqtoh(tcg_single, tcg_int,
tcg_shift, tcg_fpstatus);
}
write_fp_sreg(s, rd, tcg_single);
tcg_temp_free_i32(tcg_single);
break;
default:
g_assert_not_reached();
} }
} else { } else {
TCGv_i64 tcg_int = cpu_reg(s, rd); TCGv_i64 tcg_int = cpu_reg(s, rd);
@ -5571,8 +5591,9 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus);
if (is_double) { switch (type) {
TCGv_i64 tcg_double = read_fp_dreg(s, rn); case 1: /* float64 */
tcg_double = read_fp_dreg(s, rn);
if (is_signed) { if (is_signed) {
if (!sf) { if (!sf) {
gen_helper_vfp_tosld(tcg_int, tcg_double, gen_helper_vfp_tosld(tcg_int, tcg_double,
@ -5590,9 +5611,14 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
tcg_shift, tcg_fpstatus); tcg_shift, tcg_fpstatus);
} }
} }
if (!sf) {
tcg_gen_ext32u_i64(tcg_int, tcg_int);
}
tcg_temp_free_i64(tcg_double); tcg_temp_free_i64(tcg_double);
} else { break;
TCGv_i32 tcg_single = read_fp_sreg(s, rn);
case 0: /* float32 */
tcg_single = read_fp_sreg(s, rn);
if (sf) { if (sf) {
if (is_signed) { if (is_signed) {
gen_helper_vfp_tosqs(tcg_int, tcg_single, gen_helper_vfp_tosqs(tcg_int, tcg_single,
@ -5614,14 +5640,39 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode,
tcg_temp_free_i32(tcg_dest); tcg_temp_free_i32(tcg_dest);
} }
tcg_temp_free_i32(tcg_single); tcg_temp_free_i32(tcg_single);
break;
case 3: /* float16 */
tcg_single = read_fp_sreg(s, rn);
if (sf) {
if (is_signed) {
gen_helper_vfp_tosqh(tcg_int, tcg_single,
tcg_shift, tcg_fpstatus);
} else {
gen_helper_vfp_touqh(tcg_int, tcg_single,
tcg_shift, tcg_fpstatus);
}
} else {
TCGv_i32 tcg_dest = tcg_temp_new_i32();
if (is_signed) {
gen_helper_vfp_toslh(tcg_dest, tcg_single,
tcg_shift, tcg_fpstatus);
} else {
gen_helper_vfp_toulh(tcg_dest, tcg_single,
tcg_shift, tcg_fpstatus);
}
tcg_gen_extu_i32_i64(tcg_int, tcg_dest);
tcg_temp_free_i32(tcg_dest);
}
tcg_temp_free_i32(tcg_single);
break;
default:
g_assert_not_reached();
} }
gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus);
tcg_temp_free_i32(tcg_rmode); tcg_temp_free_i32(tcg_rmode);
if (!sf) {
tcg_gen_ext32u_i64(tcg_int, tcg_int);
}
} }
tcg_temp_free_ptr(tcg_fpstatus); tcg_temp_free_ptr(tcg_fpstatus);
@ -5791,7 +5842,20 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn)
/* actual FP conversions */ /* actual FP conversions */
bool itof = extract32(opcode, 1, 1); bool itof = extract32(opcode, 1, 1);
if (type > 1 || (rmode != 0 && opcode > 1)) { if (rmode != 0 && opcode > 1) {
unallocated_encoding(s);
return;
}
switch (type) {
case 0: /* float32 */
case 1: /* float64 */
break;
case 3: /* float16 */
if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) {
break;
}
/* fallthru */
default:
unallocated_encoding(s); unallocated_encoding(s);
return; return;
} }