From 2af9ab7737c050e063dc939973a8d669a3ab3743 Mon Sep 17 00:00:00 2001 From: Johan Bengtsson Date: Tue, 7 Dec 2010 12:01:44 +0000 Subject: [PATCH 01/14] target-arm: Add support for PKHxx in thumb2 The PKHxx instructions were not recognized by the thumb2 decoder. The solution provided in this changeset is identical to the arm-mode implementation. Signed-off-by: Johan Bengtsson Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/translate.c | 63 ++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 99464ab730..183928bdda 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7601,27 +7601,54 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1) } } break; - case 5: /* Data processing register constant shift. */ - if (rn == 15) { - tmp = new_tmp(); - tcg_gen_movi_i32(tmp, 0); - } else { - tmp = load_reg(s, rn); - } - tmp2 = load_reg(s, rm); + case 5: + op = (insn >> 21) & 0xf; - shiftop = (insn >> 4) & 3; - shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c); - conds = (insn & (1 << 20)) != 0; - logic_cc = (conds && thumb2_logic_op(op)); - gen_arm_shift_im(tmp2, shiftop, shift, logic_cc); - if (gen_thumb2_data_op(s, op, conds, 0, tmp, tmp2)) - goto illegal_op; - dead_tmp(tmp2); - if (rd != 15) { + if (op == 6) { + /* Halfword pack. */ + tmp = load_reg(s, rn); + tmp2 = load_reg(s, rm); + shift = ((insn >> 10) & 0x1c) | ((insn >> 6) & 0x3); + if (insn & (1 << 5)) { + /* pkhtb */ + if (shift == 0) + shift = 31; + tcg_gen_sari_i32(tmp2, tmp2, shift); + tcg_gen_andi_i32(tmp, tmp, 0xffff0000); + tcg_gen_ext16u_i32(tmp2, tmp2); + } else { + /* pkhbt */ + if (shift) + tcg_gen_shli_i32(tmp2, tmp2, shift); + tcg_gen_ext16u_i32(tmp, tmp); + tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000); + } + tcg_gen_or_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); store_reg(s, rd, tmp); } else { - dead_tmp(tmp); + /* Data processing register constant shift. */ + if (rn == 15) { + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, 0); + } else { + tmp = load_reg(s, rn); + } + tmp2 = load_reg(s, rm); + + shiftop = (insn >> 4) & 3; + shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c); + conds = (insn & (1 << 20)) != 0; + logic_cc = (conds && thumb2_logic_op(op)); + gen_arm_shift_im(tmp2, shiftop, shift, logic_cc); + if (gen_thumb2_data_op(s, op, conds, 0, tmp, tmp2)) + goto illegal_op; + dead_tmp(tmp2); + if (rd != 15) { + store_reg(s, rd, tmp); + } else { + dead_tmp(tmp); + } } break; case 13: /* Misc data processing. */ From 4809c612bc5eabf158d7a4656a98f5601c6bf104 Mon Sep 17 00:00:00 2001 From: Johan Bengtsson Date: Tue, 7 Dec 2010 12:01:44 +0000 Subject: [PATCH 02/14] target-arm: Fix mixup in decoding of saturating add and sub The thumb2 decoder contained a mixup between the bit controlling doubling and the bit controlling if the operation was an add or a sub. Signed-off-by: Johan Bengtsson Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 183928bdda..947de6d18a 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7713,9 +7713,9 @@ static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1) /* Saturating add/subtract. */ tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); - if (op & 2) - gen_helper_double_saturate(tmp, tmp); if (op & 1) + gen_helper_double_saturate(tmp, tmp); + if (op & 2) gen_helper_sub_saturate(tmp, tmp2, tmp); else gen_helper_add_saturate(tmp, tmp, tmp2); From 49e14940adeea797abd2be4ccda97d0349b22aae Mon Sep 17 00:00:00 2001 From: Adam Lackorzynski Date: Tue, 7 Dec 2010 12:01:44 +0000 Subject: [PATCH 03/14] target-arm: Handle 'smc' as an undefined instruction Refine check on bkpt so that smc and undefined instruction encodings are handled as an undefined instruction and trap. Signed-off-by: Adam Lackorzynski Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/translate.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 947de6d18a..bf1e643b53 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -6346,7 +6346,14 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) dead_tmp(tmp2); store_reg(s, rd, tmp); break; - case 7: /* bkpt */ + case 7: + /* SMC instruction (op1 == 3) + and undefined instructions (op1 == 0 || op1 == 2) + will trap */ + if (op1 != 1) { + goto illegal_op; + } + /* bkpt */ gen_set_condexec(s); gen_set_pc_im(s->pc - 4); gen_exception(EXCP_BKPT); From 2c9adbda721c9996ec6b371cac4a00c1164b818e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 04/14] ARM: fix ldrexd/strexd Correct ldrexd and strexd code to always read and write the high word of the 64-bit value from addr+4. Also make ldrexd and strexd agree that for a 64 bit value the address in env->exclusive_addr is that of the low word. This fixes the issues reported in https://bugs.launchpad.net/qemu/+bug/670883 Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- linux-user/main.c | 2 +- target-arm/translate.c | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 7d41d4ab88..0d627d68dd 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -589,7 +589,7 @@ static int do_strex(CPUARMState *env) } if (size == 3) { val = env->regs[(env->exclusive_info >> 12) & 0xf]; - segv = put_user_u32(val, addr); + segv = put_user_u32(val, addr + 4); if (segv) { env->cp15.c6_data = addr + 4; goto done; diff --git a/target-arm/translate.c b/target-arm/translate.c index bf1e643b53..7ee5375373 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -5926,8 +5926,10 @@ static void gen_load_exclusive(DisasContext *s, int rt, int rt2, tcg_gen_mov_i32(cpu_exclusive_val, tmp); store_reg(s, rt, tmp); if (size == 3) { - tcg_gen_addi_i32(addr, addr, 4); - tmp = gen_ld32(addr, IS_USER(s)); + TCGv tmp2 = new_tmp(); + tcg_gen_addi_i32(tmp2, addr, 4); + tmp = gen_ld32(tmp2, IS_USER(s)); + dead_tmp(tmp2); tcg_gen_mov_i32(cpu_exclusive_high, tmp); store_reg(s, rt2, tmp); } @@ -5987,7 +5989,7 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, if (size == 3) { TCGv tmp2 = new_tmp(); tcg_gen_addi_i32(tmp2, addr, 4); - tmp = gen_ld32(addr, IS_USER(s)); + tmp = gen_ld32(tmp2, IS_USER(s)); dead_tmp(tmp2); tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_high, fail_label); dead_tmp(tmp); From 04595bf66f15cb205cb7f64eaa3a38804bf0893a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 05/14] ARM: Fix decoding of VFP forms of VCVT between float and int/fixed Correct the decoding of source and destination registers for the VFP forms of the VCVT instructions which convert between floating point and integer or fixed-point. Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/translate.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 7ee5375373..69a424aa50 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -2870,16 +2870,18 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) VFP_DREG_N(rn, insn); } - if (op == 15 && (rn == 15 || rn > 17)) { + if (op == 15 && (rn == 15 || ((rn & 0x1c) == 0x18))) { /* Integer or single precision destination. */ rd = VFP_SREG_D(insn); } else { VFP_DREG_D(rd, insn); } - - if (op == 15 && (rn == 16 || rn == 17)) { - /* Integer source. */ - rm = ((insn << 1) & 0x1e) | ((insn >> 5) & 1); + if (op == 15 && + (((rn & 0x1c) == 0x10) || ((rn & 0x14) == 0x14))) { + /* VCVT from int is always from S reg regardless of dp bit. + * VCVT with immediate frac_bits has same format as SREG_M + */ + rm = VFP_SREG_M(insn); } else { VFP_DREG_M(rm, insn); } @@ -2891,6 +2893,9 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) } else { rd = VFP_SREG_D(insn); } + /* NB that we implicitly rely on the encoding for the frac_bits + * in VCVT of fixed to float being the same as that of an SREG_M + */ rm = VFP_SREG_M(insn); } @@ -3179,8 +3184,8 @@ static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn) /* Write back the result. */ if (op == 15 && (rn >= 8 && rn <= 11)) ; /* Comparison, do nothing. */ - else if (op == 15 && rn > 17) - /* Integer result. */ + else if (op == 15 && dp && ((rn & 0x1c) == 0x18)) + /* VCVT double to int: always integer result. */ gen_mov_vreg_F0(0, rd); else if (op == 15 && rn == 15) /* conversion */ From f73534a56e21f0fa37c10941a2a887b3576c2f69 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 06/14] ARM: Fix decoding of Neon forms of VCVT between float and fixed point Fix errors in the decoding of the Neon forms of fixed-point VCVT: * fixed-point VCVT is op 14 and 15, not 15 and 16 * the fbits immediate field was being misinterpreted * the sense of the to_fixed bit was inverted Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/translate.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 69a424aa50..0b3e4e6c6a 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -4850,11 +4850,15 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) } neon_store_reg64(cpu_V0, rd + pass); } - } else if (op == 15 || op == 16) { + } else if (op >= 14) { /* VCVT fixed-point. */ + /* We have already masked out the must-be-1 top bit of imm6, + * hence this 32-shift where the ARM ARM has 64-imm6. + */ + shift = 32 - shift; for (pass = 0; pass < (q ? 4 : 2); pass++) { tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, pass)); - if (op & 1) { + if (!(op & 1)) { if (u) gen_vfp_ulto(0, shift); else From d3587ef81db34885189178d53f99831aea95c702 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 07/14] ARM: Fix sense of to_integer bit in Neon VCVT float/int conversion Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/translate.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target-arm/translate.c b/target-arm/translate.c index 0b3e4e6c6a..d4a0666be5 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -5664,17 +5664,17 @@ static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn) gen_helper_rsqrte_f32(cpu_F0s, cpu_F0s, cpu_env); break; case 60: /* VCVT.F32.S32 */ - gen_vfp_tosiz(0); - break; - case 61: /* VCVT.F32.U32 */ - gen_vfp_touiz(0); - break; - case 62: /* VCVT.S32.F32 */ gen_vfp_sito(0); break; - case 63: /* VCVT.U32.F32 */ + case 61: /* VCVT.F32.U32 */ gen_vfp_uito(0); break; + case 62: /* VCVT.S32.F32 */ + gen_vfp_tosiz(0); + break; + case 63: /* VCVT.U32.F32 */ + gen_vfp_touiz(0); + break; default: /* Reserved: 21, 29, 39-56 */ return 1; From 21d6ebde76e9e7a89f5d2100816aac920cae109c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 08/14] softfloat: Add float*_is_any_nan() functions Add float*_is_any_nan() functions which return true if the argument is a NaN of any kind (quiet or signalling). Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- fpu/softfloat.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fpu/softfloat.h b/fpu/softfloat.h index 9528825522..9bece80d4d 100644 --- a/fpu/softfloat.h +++ b/fpu/softfloat.h @@ -314,6 +314,11 @@ INLINE int float32_is_zero(float32 a) return (float32_val(a) & 0x7fffffff) == 0; } +INLINE int float32_is_any_nan(float32 a) +{ + return ((float32_val(a) & ~(1 << 31)) > 0x7f800000UL); +} + #define float32_zero make_float32(0) #define float32_one make_float32(0x3f800000) #define float32_ln2 make_float32(0x3f317218) @@ -386,6 +391,11 @@ INLINE int float64_is_zero(float64 a) return (float64_val(a) & 0x7fffffffffffffffLL) == 0; } +INLINE int float64_is_any_nan(float64 a) +{ + return ((float64_val(a) & ~(1ULL << 63)) > 0x7ff0000000000000ULL); +} + #define float64_zero make_float64(0) #define float64_one make_float64(0x3ff0000000000000LL) #define float64_ln2 make_float64(0x3fe62e42fefa39efLL) From 09d9487fbb5c17226762e73c2b7d84170f5bd092 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 09/14] ARM: Return correct result for float-to-integer conversion of NaN The ARM architecture mandates that converting a NaN value to integer gives zero (if Invalid Operation FP exceptions are not being trapped). This isn't the behaviour of the SoftFloat library, so NaNs must be special-cased. Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/helper.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/target-arm/helper.c b/target-arm/helper.c index 2a1f44860b..6d2a8f2ad9 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2463,41 +2463,65 @@ float64 VFP_HELPER(sito, d)(float32 x, CPUState *env) /* Float to integer conversion. */ float32 VFP_HELPER(toui, s)(float32 x, CPUState *env) { + if (float32_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float32_to_uint32(x, &env->vfp.fp_status)); } float32 VFP_HELPER(toui, d)(float64 x, CPUState *env) { + if (float64_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float64_to_uint32(x, &env->vfp.fp_status)); } float32 VFP_HELPER(tosi, s)(float32 x, CPUState *env) { + if (float32_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float32_to_int32(x, &env->vfp.fp_status)); } float32 VFP_HELPER(tosi, d)(float64 x, CPUState *env) { + if (float64_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float64_to_int32(x, &env->vfp.fp_status)); } float32 VFP_HELPER(touiz, s)(float32 x, CPUState *env) { + if (float32_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float32_to_uint32_round_to_zero(x, &env->vfp.fp_status)); } float32 VFP_HELPER(touiz, d)(float64 x, CPUState *env) { + if (float64_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float64_to_uint32_round_to_zero(x, &env->vfp.fp_status)); } float32 VFP_HELPER(tosiz, s)(float32 x, CPUState *env) { + if (float32_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float32_to_int32_round_to_zero(x, &env->vfp.fp_status)); } float32 VFP_HELPER(tosiz, d)(float64 x, CPUState *env) { + if (float64_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float64_to_int32_round_to_zero(x, &env->vfp.fp_status)); } @@ -2524,6 +2548,9 @@ ftype VFP_HELPER(name##to, p)(ftype x, uint32_t shift, CPUState *env) \ ftype VFP_HELPER(to##name, p)(ftype x, uint32_t shift, CPUState *env) \ { \ ftype tmp; \ + if (ftype##_is_any_nan(x)) { \ + return ftype##_zero; \ + } \ tmp = ftype##_scalbn(x, shift, &env->vfp.fp_status); \ return vfp_ito##p((itype)ftype##_to_##sign##int32_round_to_zero(tmp, \ &env->vfp.fp_status)); \ From b408dbdec3d3220b7e2da2b0fd768048f43a2e39 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 10/14] softfloat: Add float*_maybe_silence_nan() functions Add functions float*_maybe_silence_nan() which ensure that a value is not a signaling NaN by turning it into a quiet NaN. Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- fpu/softfloat-specialize.h | 38 ++++++++++++++++++++++++++++++++++++++ fpu/softfloat.h | 2 ++ 2 files changed, 40 insertions(+) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 8e6aceb552..07468786f9 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -101,6 +101,25 @@ int float32_is_signaling_nan( float32 a_ ) #endif } +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the single-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float32 float32_maybe_silence_nan( float32 a_ ) +{ + if (float32_is_signaling_nan(a_)) { + uint32_t a = float32_val(a_); +#if SNAN_BIT_IS_ONE + a &= ~(1 << 22); +#else + a |= (1 << 22); +#endif + return make_float32(a); + } + return a_; +} + /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid @@ -233,6 +252,25 @@ int float64_is_signaling_nan( float64 a_ ) #endif } +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the double-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float64 float64_maybe_silence_nan( float64 a_ ) +{ + if (float64_is_signaling_nan(a_)) { + bits64 a = float64_val(a_); +#if SNAN_BIT_IS_ONE + a &= ~LIT64( 0x0008000000000000 ); +#else + a |= LIT64( 0x0008000000000000 ); +#endif + return make_float64(a); + } + return a_; +} + /*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid diff --git a/fpu/softfloat.h b/fpu/softfloat.h index 9bece80d4d..2e651e2605 100644 --- a/fpu/softfloat.h +++ b/fpu/softfloat.h @@ -287,6 +287,7 @@ int float32_compare( float32, float32 STATUS_PARAM ); int float32_compare_quiet( float32, float32 STATUS_PARAM ); int float32_is_nan( float32 ); int float32_is_signaling_nan( float32 ); +float32 float32_maybe_silence_nan( float32 ); float32 float32_scalbn( float32, int STATUS_PARAM ); INLINE float32 float32_abs(float32 a) @@ -364,6 +365,7 @@ int float64_compare( float64, float64 STATUS_PARAM ); int float64_compare_quiet( float64, float64 STATUS_PARAM ); int float64_is_nan( float64 a ); int float64_is_signaling_nan( float64 ); +float64 float64_maybe_silence_nan( float64 ); float64 float64_scalbn( float64, int STATUS_PARAM ); INLINE float64 float64_abs(float64 a) From 2d6277373d3fc18f11504cd05ea82f0fe6d67962 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 11/14] ARM: Return correct result for single<->double conversion of NaN The ARM ARM defines that if the input to a single<->double conversion is a NaN then the output is always forced to be a quiet NaN by setting the most significant bit of the fraction part. Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/helper.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 6d2a8f2ad9..4bd1cd4fa4 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2528,12 +2528,20 @@ float32 VFP_HELPER(tosiz, d)(float64 x, CPUState *env) /* floating point conversion */ float64 VFP_HELPER(fcvtd, s)(float32 x, CPUState *env) { - return float32_to_float64(x, &env->vfp.fp_status); + float64 r = float32_to_float64(x, &env->vfp.fp_status); + /* ARM requires that S<->D conversion of any kind of NaN generates + * a quiet NaN by forcing the most significant frac bit to 1. + */ + return float64_maybe_silence_nan(r); } float32 VFP_HELPER(fcvts, d)(float64 x, CPUState *env) { - return float64_to_float32(x, &env->vfp.fp_status); + float32 r = float64_to_float32(x, &env->vfp.fp_status); + /* ARM requires that S<->D conversion of any kind of NaN generates + * a quiet NaN by forcing the most significant frac bit to 1. + */ + return float32_maybe_silence_nan(r); } /* VFP3 fixed point conversion. */ From 26a5e69aafd0434922316e4185c45c9bddd4b9ba Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 12/14] ARM: Ignore top 16 bits when doing VCVT from 16 bit fixed point VCVT of 16 bit fixed point to float should ignore the top 16 bits of the source register. Cast to int16_t and friends rather than int16 -- the former is guaranteed exactly 16 bits wide where the latter is merely at least 16 bits wide (and so is usually 32 bits). Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 4bd1cd4fa4..292578235b 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2549,7 +2549,7 @@ float32 VFP_HELPER(fcvts, d)(float64 x, CPUState *env) ftype VFP_HELPER(name##to, p)(ftype x, uint32_t shift, CPUState *env) \ { \ ftype tmp; \ - tmp = sign##int32_to_##ftype ((itype)vfp_##p##toi(x), \ + tmp = sign##int32_to_##ftype ((itype##_t)vfp_##p##toi(x), \ &env->vfp.fp_status); \ return ftype##_scalbn(tmp, -(int)shift, &env->vfp.fp_status); \ } \ From cbcef455a2d29cdd19f4f0a21990cd76c0d03102 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 13/14] softfloat: Add float/double to 16 bit integer conversion functions The ARM architecture needs float/double to 16 bit integer conversions. (The 32 bit versions aren't sufficient because of the requirement to saturate at 16 bit MAXINT/MININT and to get the exception bits right.) Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- fpu/softfloat.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ fpu/softfloat.h | 4 ++ 2 files changed, 140 insertions(+) diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 0b8279798c..6f5b05d5fe 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1353,6 +1353,55 @@ int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM ) } +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the 16-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int16 float32_to_int16_round_to_zero( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + bits32 aSig; + int32 z; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + shiftCount = aExp - 0x8E; + if ( 0 <= shiftCount ) { + if ( float32_val(a) != 0xC7000000 ) { + float_raise( float_flag_invalid STATUS_VAR); + if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { + return 0x7FFF; + } + } + return (sbits32) 0xffff8000; + } + else if ( aExp <= 0x7E ) { + if ( aExp | aSig ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + return 0; + } + shiftCount -= 0x10; + aSig = ( aSig | 0x00800000 )<<8; + z = aSig>>( - shiftCount ); + if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + if ( aSign ) { + z = - z; + } + return z; + +} + /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point value | `a' to the 64-bit two's complement integer format. The conversion is @@ -2410,6 +2459,57 @@ int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM ) } +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the 16-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int16 float64_to_int16_round_to_zero( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + bits64 aSig, savedASig; + int32 z; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( 0x40E < aExp ) { + if ( ( aExp == 0x7FF ) && aSig ) { + aSign = 0; + } + goto invalid; + } + else if ( aExp < 0x3FF ) { + if ( aExp || aSig ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + return 0; + } + aSig |= LIT64( 0x0010000000000000 ); + shiftCount = 0x433 - aExp; + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if ( aSign ) { + z = - z; + } + if ( ( (int16_t)z < 0 ) ^ aSign ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + return aSign ? (sbits32) 0xffff8000 : 0x7FFF; + } + if ( ( aSig< 0xffff) { + res = 0xffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + unsigned int float64_to_uint32( float64 a STATUS_PARAM ) { int64_t v; @@ -5668,6 +5786,24 @@ unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM ) return res; } +unsigned int float64_to_uint16_round_to_zero( float64 a STATUS_PARAM ) +{ + int64_t v; + unsigned int res; + + v = float64_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise( float_flag_invalid STATUS_VAR); + } else if (v > 0xffff) { + res = 0xffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + /* FIXME: This looks broken. */ uint64_t float64_to_uint64 (float64 a STATUS_PARAM) { diff --git a/fpu/softfloat.h b/fpu/softfloat.h index 2e651e2605..1c1004de97 100644 --- a/fpu/softfloat.h +++ b/fpu/softfloat.h @@ -251,6 +251,8 @@ float32 float16_to_float32( bits16, flag STATUS_PARAM ); /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ +int float32_to_int16_round_to_zero( float32 STATUS_PARAM ); +unsigned int float32_to_uint16_round_to_zero( float32 STATUS_PARAM ); int float32_to_int32( float32 STATUS_PARAM ); int float32_to_int32_round_to_zero( float32 STATUS_PARAM ); unsigned int float32_to_uint32( float32 STATUS_PARAM ); @@ -327,6 +329,8 @@ INLINE int float32_is_any_nan(float32 a) /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ +int float64_to_int16_round_to_zero( float64 STATUS_PARAM ); +unsigned int float64_to_uint16_round_to_zero( float64 STATUS_PARAM ); int float64_to_int32( float64 STATUS_PARAM ); int float64_to_int32_round_to_zero( float64 STATUS_PARAM ); unsigned int float64_to_uint32( float64 STATUS_PARAM ); From 72f24d155ceffa0eb888fd277fa09584bcd1b04c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 7 Dec 2010 15:37:34 +0000 Subject: [PATCH 14/14] ARM: Implement VCVT to 16 bit integer using new softfloat routines Use the softfloat conversion routines for conversion to 16 bit integers, because just casting to a 16 bit type truncates the value rather than saturating it at 16-bit MAXINT/MININT. Signed-off-by: Peter Maydell Reviewed-by: Nathan Froyd --- target-arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index 292578235b..9ba2f4fe15 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2560,7 +2560,7 @@ ftype VFP_HELPER(to##name, p)(ftype x, uint32_t shift, CPUState *env) \ return ftype##_zero; \ } \ tmp = ftype##_scalbn(x, shift, &env->vfp.fp_status); \ - return vfp_ito##p((itype)ftype##_to_##sign##int32_round_to_zero(tmp, \ + return vfp_ito##p(ftype##_to_##itype##_round_to_zero(tmp, \ &env->vfp.fp_status)); \ }