aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <rth@twiddle.net>2016-12-15 15:04:19 -0800
committerRichard Henderson <rth@twiddle.net>2017-01-23 09:52:40 -0800
commitebe9383caefd56d519e965a5d87bca29f0aeffe3 (patch)
tree8a1448c2b70a4f16091ab3b7162fd77635001cfb
parent98a9cb792c8c177d5d81c2c4a08e740deeb207fd (diff)
target-hppa: Implement floating-point insns
Signed-off-by: Richard Henderson <rth@twiddle.net>
-rw-r--r--target/hppa/helper.h55
-rw-r--r--target/hppa/op_helper.c394
-rw-r--r--target/hppa/translate.c728
3 files changed, 1177 insertions, 0 deletions
diff --git a/target/hppa/helper.h b/target/hppa/helper.h
index d51cf6d33f..789f07fc0a 100644
--- a/target/hppa/helper.h
+++ b/target/hppa/helper.h
@@ -9,3 +9,58 @@ DEF_HELPER_FLAGS_1(probe_r, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_FLAGS_1(probe_w, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_FLAGS_1(loaded_fr0, TCG_CALL_NO_RWG, void, env)
+
+DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, f32, env, f32)
+DEF_HELPER_FLAGS_2(frnd_s, TCG_CALL_NO_RWG, f32, env, f32)
+DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_RWG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fmpy_s, TCG_CALL_NO_RWG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, f32, env, f32, f32)
+
+DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, f64, env, f64)
+DEF_HELPER_FLAGS_2(frnd_d, TCG_CALL_NO_RWG, f64, env, f64)
+DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, f64, env, f64, f64)
+DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, f64, env, f64, f64)
+DEF_HELPER_FLAGS_3(fmpy_d, TCG_CALL_NO_RWG, f64, env, f64, f64)
+DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, f64, env, f64, f64)
+
+DEF_HELPER_FLAGS_2(fcnv_s_d, TCG_CALL_NO_RWG, f64, env, f32)
+DEF_HELPER_FLAGS_2(fcnv_d_s, TCG_CALL_NO_RWG, f32, env, f64)
+
+DEF_HELPER_FLAGS_2(fcnv_w_s, TCG_CALL_NO_RWG, f32, env, s32)
+DEF_HELPER_FLAGS_2(fcnv_dw_s, TCG_CALL_NO_RWG, f32, env, s64)
+DEF_HELPER_FLAGS_2(fcnv_w_d, TCG_CALL_NO_RWG, f64, env, s32)
+DEF_HELPER_FLAGS_2(fcnv_dw_d, TCG_CALL_NO_RWG, f64, env, s64)
+
+DEF_HELPER_FLAGS_2(fcnv_s_w, TCG_CALL_NO_RWG, s32, env, f32)
+DEF_HELPER_FLAGS_2(fcnv_d_w, TCG_CALL_NO_RWG, s32, env, f64)
+DEF_HELPER_FLAGS_2(fcnv_s_dw, TCG_CALL_NO_RWG, s64, env, f32)
+DEF_HELPER_FLAGS_2(fcnv_d_dw, TCG_CALL_NO_RWG, s64, env, f64)
+
+DEF_HELPER_FLAGS_2(fcnv_t_s_w, TCG_CALL_NO_RWG, s32, env, f32)
+DEF_HELPER_FLAGS_2(fcnv_t_d_w, TCG_CALL_NO_RWG, s32, env, f64)
+DEF_HELPER_FLAGS_2(fcnv_t_s_dw, TCG_CALL_NO_RWG, s64, env, f32)
+DEF_HELPER_FLAGS_2(fcnv_t_d_dw, TCG_CALL_NO_RWG, s64, env, f64)
+
+DEF_HELPER_FLAGS_2(fcnv_uw_s, TCG_CALL_NO_RWG, f32, env, i32)
+DEF_HELPER_FLAGS_2(fcnv_udw_s, TCG_CALL_NO_RWG, f32, env, i64)
+DEF_HELPER_FLAGS_2(fcnv_uw_d, TCG_CALL_NO_RWG, f64, env, i32)
+DEF_HELPER_FLAGS_2(fcnv_udw_d, TCG_CALL_NO_RWG, f64, env, i64)
+
+DEF_HELPER_FLAGS_2(fcnv_s_uw, TCG_CALL_NO_RWG, i32, env, f32)
+DEF_HELPER_FLAGS_2(fcnv_d_uw, TCG_CALL_NO_RWG, i32, env, f64)
+DEF_HELPER_FLAGS_2(fcnv_s_udw, TCG_CALL_NO_RWG, i64, env, f32)
+DEF_HELPER_FLAGS_2(fcnv_d_udw, TCG_CALL_NO_RWG, i64, env, f64)
+
+DEF_HELPER_FLAGS_2(fcnv_t_s_uw, TCG_CALL_NO_RWG, i32, env, f32)
+DEF_HELPER_FLAGS_2(fcnv_t_d_uw, TCG_CALL_NO_RWG, i32, env, f64)
+DEF_HELPER_FLAGS_2(fcnv_t_s_udw, TCG_CALL_NO_RWG, i64, env, f32)
+DEF_HELPER_FLAGS_2(fcnv_t_d_udw, TCG_CALL_NO_RWG, i64, env, f64)
+
+DEF_HELPER_FLAGS_5(fcmp_s, TCG_CALL_NO_RWG, void, env, f32, f32, i32, i32)
+DEF_HELPER_FLAGS_5(fcmp_d, TCG_CALL_NO_RWG, void, env, f64, f64, i32, i32)
+
+DEF_HELPER_FLAGS_4(fmpyfadd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32)
+DEF_HELPER_FLAGS_4(fmpynfadd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32)
+DEF_HELPER_FLAGS_4(fmpyfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fmpynfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c
index 670e600bcd..c05c0d5572 100644
--- a/target/hppa/op_helper.c
+++ b/target/hppa/op_helper.c
@@ -174,3 +174,397 @@ void cpu_hppa_loaded_fr0(CPUHPPAState *env)
{
helper_loaded_fr0(env);
}
+
+#define CONVERT_BIT(X, SRC, DST) \
+ ((SRC) > (DST) \
+ ? (X) / ((SRC) / (DST)) & (DST) \
+ : ((X) & (SRC)) * ((DST) / (SRC)))
+
+static void update_fr0_op(CPUHPPAState *env, uintptr_t ra)
+{
+ uint32_t soft_exp = get_float_exception_flags(&env->fp_status);
+ uint32_t hard_exp = 0;
+ uint32_t shadow = env->fr0_shadow;
+
+ if (likely(soft_exp == 0)) {
+ env->fr[0] = (uint64_t)shadow << 32;
+ return;
+ }
+ set_float_exception_flags(0, &env->fp_status);
+
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0);
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1);
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2);
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3);
+ hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4);
+ shadow |= hard_exp << (32 - 5);
+ env->fr0_shadow = shadow;
+ env->fr[0] = (uint64_t)shadow << 32;
+
+ if (hard_exp & shadow) {
+ dynexcp(env, EXCP_SIGFPE, ra);
+ }
+}
+
+float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg)
+{
+ float32 ret = float32_sqrt(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg)
+{
+ float32 ret = float32_round_to_int(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b)
+{
+ float32 ret = float32_add(a, b, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b)
+{
+ float32 ret = float32_sub(a, b, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b)
+{
+ float32 ret = float32_mul(a, b, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b)
+{
+ float32 ret = float32_div(a, b, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg)
+{
+ float64 ret = float64_sqrt(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg)
+{
+ float64 ret = float64_round_to_int(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b)
+{
+ float64 ret = float64_add(a, b, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b)
+{
+ float64 ret = float64_sub(a, b, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b)
+{
+ float64 ret = float64_mul(a, b, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b)
+{
+ float64 ret = float64_div(a, b, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg)
+{
+ float64 ret = float32_to_float64(arg, &env->fp_status);
+ ret = float64_maybe_silence_nan(ret, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg)
+{
+ float32 ret = float64_to_float32(arg, &env->fp_status);
+ ret = float32_maybe_silence_nan(ret, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg)
+{
+ float32 ret = int32_to_float32(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg)
+{
+ float32 ret = int64_to_float32(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg)
+{
+ float64 ret = int32_to_float64(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg)
+{
+ float64 ret = int64_to_float64(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg)
+{
+ int32_t ret = float32_to_int32(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg)
+{
+ int32_t ret = float64_to_int32(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg)
+{
+ int64_t ret = float32_to_int64(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg)
+{
+ int64_t ret = float64_to_int64(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg)
+{
+ int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg)
+{
+ int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg)
+{
+ int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg)
+{
+ int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg)
+{
+ float32 ret = uint32_to_float32(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg)
+{
+ float32 ret = uint64_to_float32(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg)
+{
+ float64 ret = uint32_to_float64(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg)
+{
+ float64 ret = uint64_to_float64(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg)
+{
+ uint32_t ret = float32_to_uint32(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg)
+{
+ uint32_t ret = float64_to_uint32(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg)
+{
+ uint64_t ret = float32_to_uint64(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg)
+{
+ uint64_t ret = float64_to_uint64(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg)
+{
+ uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg)
+{
+ uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg)
+{
+ uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg)
+{
+ uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, uint32_t c, int r)
+{
+ uint32_t shadow = env->fr0_shadow;
+
+ switch (r) {
+ case float_relation_greater:
+ c = extract32(c, 4, 1);
+ break;
+ case float_relation_less:
+ c = extract32(c, 3, 1);
+ break;
+ case float_relation_equal:
+ c = extract32(c, 2, 1);
+ break;
+ case float_relation_unordered:
+ c = extract32(c, 1, 1);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (y) {
+ /* targeted comparison */
+ /* set fpsr[ca[y - 1]] to current compare */
+ shadow = deposit32(shadow, 21 - (y - 1), 1, c);
+ } else {
+ /* queued comparison */
+ /* shift cq right by one place */
+ shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10));
+ /* move fpsr[c] to fpsr[cq[0]] */
+ shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1));
+ /* set fpsr[c] to current compare */
+ shadow = deposit32(shadow, 26, 1, c);
+ }
+
+ env->fr0_shadow = shadow;
+ env->fr[0] = (uint64_t)shadow << 32;
+}
+
+void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b,
+ uint32_t y, uint32_t c)
+{
+ int r;
+ if (c & 1) {
+ r = float32_compare(a, b, &env->fp_status);
+ } else {
+ r = float32_compare_quiet(a, b, &env->fp_status);
+ }
+ update_fr0_op(env, GETPC());
+ update_fr0_cmp(env, y, c, r);
+}
+
+void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b,
+ uint32_t y, uint32_t c)
+{
+ int r;
+ if (c & 1) {
+ r = float64_compare(a, b, &env->fp_status);
+ } else {
+ r = float64_compare_quiet(a, b, &env->fp_status);
+ }
+ update_fr0_op(env, GETPC());
+ update_fr0_cmp(env, y, c, r);
+}
+
+float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c)
+{
+ float32 ret = float32_muladd(a, b, c, 0, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c)
+{
+ float32 ret = float32_muladd(a, b, c, float_muladd_negate_product,
+ &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c)
+{
+ float64 ret = float64_muladd(a, b, c, 0, &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
+
+float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c)
+{
+ float64 ret = float64_muladd(a, b, c, float_muladd_negate_product,
+ &env->fp_status);
+ update_fr0_op(env, GETPC());
+ return ret;
+}
diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index cfdb9ee761..4d243f7d3d 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -85,6 +85,12 @@ typedef struct DisasInsn {
const struct DisasInsn *f);
union {
void (*f_ttt)(TCGv, TCGv, TCGv);
+ void (*f_weww)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32);
+ void (*f_dedd)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64);
+ void (*f_wew)(TCGv_i32, TCGv_env, TCGv_i32);
+ void (*f_ded)(TCGv_i64, TCGv_env, TCGv_i64);
+ void (*f_wed)(TCGv_i32, TCGv_env, TCGv_i64);
+ void (*f_dew)(TCGv_i64, TCGv_env, TCGv_i32);
};
} DisasInsn;
@@ -295,6 +301,28 @@ static TCGv_i32 load_frw_i32(unsigned rt)
return ret;
}
+static TCGv_i32 load_frw0_i32(unsigned rt)
+{
+ if (rt == 0) {
+ return tcg_const_i32(0);
+ } else {
+ return load_frw_i32(rt);
+ }
+}
+
+static TCGv_i64 load_frw0_i64(unsigned rt)
+{
+ if (rt == 0) {
+ return tcg_const_i64(0);
+ } else {
+ TCGv_i64 ret = tcg_temp_new_i64();
+ tcg_gen_ld32u_i64(ret, cpu_env,
+ offsetof(CPUHPPAState, fr[rt & 31])
+ + (rt & 32 ? LO_OFS : HI_OFS));
+ return ret;
+ }
+}
+
static void save_frw_i32(unsigned rt, TCGv_i32 val)
{
tcg_gen_st_i32(val, cpu_env,
@@ -312,6 +340,15 @@ static TCGv_i64 load_frd(unsigned rt)
return ret;
}
+static TCGv_i64 load_frd0(unsigned rt)
+{
+ if (rt == 0) {
+ return tcg_const_i64(0);
+ } else {
+ return load_frd(rt);
+ }
+}
+
static void save_frd(unsigned rt, TCGv_i64 val)
{
tcg_gen_st_i64(val, cpu_env, offsetof(CPUHPPAState, fr[rt]));
@@ -494,6 +531,35 @@ static target_long low_sextract(uint32_t val, int pos, int len)
return x;
}
+static unsigned assemble_rt64(uint32_t insn)
+{
+ unsigned r1 = extract32(insn, 6, 1);
+ unsigned r0 = extract32(insn, 0, 5);
+ return r1 * 32 + r0;
+}
+
+static unsigned assemble_ra64(uint32_t insn)
+{
+ unsigned r1 = extract32(insn, 7, 1);
+ unsigned r0 = extract32(insn, 21, 5);
+ return r1 * 32 + r0;
+}
+
+static unsigned assemble_rb64(uint32_t insn)
+{
+ unsigned r1 = extract32(insn, 12, 1);
+ unsigned r0 = extract32(insn, 16, 5);
+ return r1 * 32 + r0;
+}
+
+static unsigned assemble_rc64(uint32_t insn)
+{
+ unsigned r2 = extract32(insn, 8, 1);
+ unsigned r1 = extract32(insn, 13, 3);
+ unsigned r0 = extract32(insn, 9, 2);
+ return r2 * 32 + r1 * 4 + r0;
+}
+
static target_long assemble_12(uint32_t insn)
{
target_ulong x = -(target_ulong)(insn & 1);
@@ -1218,6 +1284,110 @@ static ExitStatus do_fstored(DisasContext *ctx, unsigned rt, unsigned rb,
return nullify_end(ctx, NO_EXIT);
}
+static ExitStatus do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra,
+ void (*func)(TCGv_i32, TCGv_env, TCGv_i32))
+{
+ TCGv_i32 tmp;
+
+ nullify_over(ctx);
+ tmp = load_frw0_i32(ra);
+
+ func(tmp, cpu_env, tmp);
+
+ save_frw_i32(rt, tmp);
+ tcg_temp_free_i32(tmp);
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra,
+ void (*func)(TCGv_i32, TCGv_env, TCGv_i64))
+{
+ TCGv_i32 dst;
+ TCGv_i64 src;
+
+ nullify_over(ctx);
+ src = load_frd(ra);
+ dst = tcg_temp_new_i32();
+
+ func(dst, cpu_env, src);
+
+ tcg_temp_free_i64(src);
+ save_frw_i32(rt, dst);
+ tcg_temp_free_i32(dst);
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra,
+ void (*func)(TCGv_i64, TCGv_env, TCGv_i64))
+{
+ TCGv_i64 tmp;
+
+ nullify_over(ctx);
+ tmp = load_frd0(ra);
+
+ func(tmp, cpu_env, tmp);
+
+ save_frd(rt, tmp);
+ tcg_temp_free_i64(tmp);
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra,
+ void (*func)(TCGv_i64, TCGv_env, TCGv_i32))
+{
+ TCGv_i32 src;
+ TCGv_i64 dst;
+
+ nullify_over(ctx);
+ src = load_frw0_i32(ra);
+ dst = tcg_temp_new_i64();
+
+ func(dst, cpu_env, src);
+
+ tcg_temp_free_i32(src);
+ save_frd(rt, dst);
+ tcg_temp_free_i64(dst);
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus do_fop_weww(DisasContext *ctx, unsigned rt,
+ unsigned ra, unsigned rb,
+ void (*func)(TCGv_i32, TCGv_env,
+ TCGv_i32, TCGv_i32))
+{
+ TCGv_i32 a, b;
+
+ nullify_over(ctx);
+ a = load_frw0_i32(ra);
+ b = load_frw0_i32(rb);
+
+ func(a, cpu_env, a, b);
+
+ tcg_temp_free_i32(b);
+ save_frw_i32(rt, a);
+ tcg_temp_free_i32(a);
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus do_fop_dedd(DisasContext *ctx, unsigned rt,
+ unsigned ra, unsigned rb,
+ void (*func)(TCGv_i64, TCGv_env,
+ TCGv_i64, TCGv_i64))
+{
+ TCGv_i64 a, b;
+
+ nullify_over(ctx);
+ a = load_frd0(ra);
+ b = load_frd0(rb);
+
+ func(a, cpu_env, a, b);
+
+ tcg_temp_free_i64(b);
+ save_frd(rt, a);
+ tcg_temp_free_i64(a);
+ return nullify_end(ctx, NO_EXIT);
+}
+
/* Emit an unconditional branch to a direct target, which may or may not
have already had nullification handled. */
static ExitStatus do_dbranch(DisasContext *ctx, target_ulong dest,
@@ -2893,6 +3063,554 @@ static const DisasInsn table_branch[] = {
{ 0xe800d000u, 0xfc00dffcu, trans_bve },
};
+static ExitStatus trans_fop_wew_0c(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ unsigned ra = extract32(insn, 21, 5);
+ return do_fop_wew(ctx, rt, ra, di->f_wew);
+}
+
+static ExitStatus trans_fop_wew_0e(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = assemble_rt64(insn);
+ unsigned ra = assemble_ra64(insn);
+ return do_fop_wew(ctx, rt, ra, di->f_wew);
+}
+
+static ExitStatus trans_fop_ded(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ unsigned ra = extract32(insn, 21, 5);
+ return do_fop_ded(ctx, rt, ra, di->f_ded);
+}
+
+static ExitStatus trans_fop_wed_0c(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ unsigned ra = extract32(insn, 21, 5);
+ return do_fop_wed(ctx, rt, ra, di->f_wed);
+}
+
+static ExitStatus trans_fop_wed_0e(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = assemble_rt64(insn);
+ unsigned ra = extract32(insn, 21, 5);
+ return do_fop_wed(ctx, rt, ra, di->f_wed);
+}
+
+static ExitStatus trans_fop_dew_0c(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ unsigned ra = extract32(insn, 21, 5);
+ return do_fop_dew(ctx, rt, ra, di->f_dew);
+}
+
+static ExitStatus trans_fop_dew_0e(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ unsigned ra = assemble_ra64(insn);
+ return do_fop_dew(ctx, rt, ra, di->f_dew);
+}
+
+static ExitStatus trans_fop_weww_0c(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ unsigned rb = extract32(insn, 16, 5);
+ unsigned ra = extract32(insn, 21, 5);
+ return do_fop_weww(ctx, rt, ra, rb, di->f_weww);
+}
+
+static ExitStatus trans_fop_weww_0e(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = assemble_rt64(insn);
+ unsigned rb = assemble_rb64(insn);
+ unsigned ra = assemble_ra64(insn);
+ return do_fop_weww(ctx, rt, ra, rb, di->f_weww);
+}
+
+static ExitStatus trans_fop_dedd(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ unsigned rb = extract32(insn, 16, 5);
+ unsigned ra = extract32(insn, 21, 5);
+ return do_fop_dedd(ctx, rt, ra, rb, di->f_dedd);
+}
+
+static void gen_fcpy_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
+{
+ tcg_gen_mov_i32(dst, src);
+}
+
+static void gen_fcpy_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
+{
+ tcg_gen_mov_i64(dst, src);
+}
+
+static void gen_fabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
+{
+ tcg_gen_andi_i32(dst, src, INT32_MAX);
+}
+
+static void gen_fabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
+{
+ tcg_gen_andi_i64(dst, src, INT64_MAX);
+}
+
+static void gen_fneg_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
+{
+ tcg_gen_xori_i32(dst, src, INT32_MIN);
+}
+
+static void gen_fneg_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
+{
+ tcg_gen_xori_i64(dst, src, INT64_MIN);
+}
+
+static void gen_fnegabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
+{
+ tcg_gen_ori_i32(dst, src, INT32_MIN);
+}
+
+static void gen_fnegabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
+{
+ tcg_gen_ori_i64(dst, src, INT64_MIN);
+}
+
+static ExitStatus do_fcmp_s(DisasContext *ctx, unsigned ra, unsigned rb,
+ unsigned y, unsigned c)
+{
+ TCGv_i32 ta, tb, tc, ty;
+
+ nullify_over(ctx);
+
+ ta = load_frw0_i32(ra);
+ tb = load_frw0_i32(rb);
+ ty = tcg_const_i32(y);
+ tc = tcg_const_i32(c);
+
+ gen_helper_fcmp_s(cpu_env, ta, tb, ty, tc);
+
+ tcg_temp_free_i32(ta);
+ tcg_temp_free_i32(tb);
+ tcg_temp_free_i32(ty);
+ tcg_temp_free_i32(tc);
+
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus trans_fcmp_s_0c(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned c = extract32(insn, 0, 5);
+ unsigned y = extract32(insn, 13, 3);
+ unsigned rb = extract32(insn, 16, 5);
+ unsigned ra = extract32(insn, 21, 5);
+ return do_fcmp_s(ctx, ra, rb, y, c);
+}
+
+static ExitStatus trans_fcmp_s_0e(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned c = extract32(insn, 0, 5);
+ unsigned y = extract32(insn, 13, 3);
+ unsigned rb = assemble_rb64(insn);
+ unsigned ra = assemble_ra64(insn);
+ return do_fcmp_s(ctx, ra, rb, y, c);
+}
+
+static ExitStatus trans_fcmp_d(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned c = extract32(insn, 0, 5);
+ unsigned y = extract32(insn, 13, 3);
+ unsigned rb = extract32(insn, 16, 5);
+ unsigned ra = extract32(insn, 21, 5);
+ TCGv_i64 ta, tb;
+ TCGv_i32 tc, ty;
+
+ nullify_over(ctx);
+
+ ta = load_frd0(ra);
+ tb = load_frd0(rb);
+ ty = tcg_const_i32(y);
+ tc = tcg_const_i32(c);
+
+ gen_helper_fcmp_d(cpu_env, ta, tb, ty, tc);
+
+ tcg_temp_free_i64(ta);
+ tcg_temp_free_i64(tb);
+ tcg_temp_free_i32(ty);
+ tcg_temp_free_i32(tc);
+
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus trans_ftest_t(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned y = extract32(insn, 13, 3);
+ unsigned cbit = (y ^ 1) - 1;
+ TCGv t;
+
+ nullify_over(ctx);
+
+ t = tcg_temp_new();
+ tcg_gen_ld32u_tl(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow));
+ tcg_gen_extract_tl(t, t, 21 - cbit, 1);
+ ctx->null_cond = cond_make_0(TCG_COND_NE, t);
+ tcg_temp_free(t);
+
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus trans_ftest_q(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned c = extract32(insn, 0, 5);
+ int mask;
+ bool inv = false;
+ TCGv t;
+
+ nullify_over(ctx);
+
+ t = tcg_temp_new();
+ tcg_gen_ld32u_tl(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow));
+
+ switch (c) {
+ case 0: /* simple */
+ tcg_gen_andi_tl(t, t, 0x4000000);
+ ctx->null_cond = cond_make_0(TCG_COND_NE, t);
+ goto done;
+ case 2: /* rej */
+ inv = true;
+ /* fallthru */
+ case 1: /* acc */
+ mask = 0x43ff800;
+ break;
+ case 6: /* rej8 */
+ inv = true;
+ /* fallthru */
+ case 5: /* acc8 */
+ mask = 0x43f8000;
+ break;
+ case 9: /* acc6 */
+ mask = 0x43e0000;
+ break;
+ case 13: /* acc4 */
+ mask = 0x4380000;
+ break;
+ case 17: /* acc2 */
+ mask = 0x4200000;
+ break;
+ default:
+ return gen_illegal(ctx);
+ }
+ if (inv) {
+ TCGv c = load_const(ctx, mask);
+ tcg_gen_or_tl(t, t, c);
+ ctx->null_cond = cond_make(TCG_COND_EQ, t, c);
+ } else {
+ tcg_gen_andi_tl(t, t, mask);
+ ctx->null_cond = cond_make_0(TCG_COND_EQ, t);
+ }
+ done:
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus trans_xmpyu(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ unsigned rb = assemble_rb64(insn);
+ unsigned ra = assemble_ra64(insn);
+ TCGv_i64 a, b;
+
+ nullify_over(ctx);
+
+ a = load_frw0_i64(ra);
+ b = load_frw0_i64(rb);
+ tcg_gen_mul_i64(a, a, b);
+ save_frd(rt, a);
+ tcg_temp_free_i64(a);
+ tcg_temp_free_i64(b);
+
+ return nullify_end(ctx, NO_EXIT);
+}
+
+#define FOP_DED trans_fop_ded, .f_ded
+#define FOP_DEDD trans_fop_dedd, .f_dedd
+
+#define FOP_WEW trans_fop_wew_0c, .f_wew
+#define FOP_DEW trans_fop_dew_0c, .f_dew
+#define FOP_WED trans_fop_wed_0c, .f_wed
+#define FOP_WEWW trans_fop_weww_0c, .f_weww
+
+static const DisasInsn table_float_0c[] = {
+ /* floating point class zero */
+ { 0x30004000, 0xfc1fffe0, FOP_WEW = gen_fcpy_s },
+ { 0x30006000, 0xfc1fffe0, FOP_WEW = gen_fabs_s },
+ { 0x30008000, 0xfc1fffe0, FOP_WEW = gen_helper_fsqrt_s },
+ { 0x3000a000, 0xfc1fffe0, FOP_WEW = gen_helper_frnd_s },
+ { 0x3000c000, 0xfc1fffe0, FOP_WEW = gen_fneg_s },
+ { 0x3000e000, 0xfc1fffe0, FOP_WEW = gen_fnegabs_s },
+
+ { 0x30004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d },
+ { 0x30006800, 0xfc1fffe0, FOP_DED = gen_fabs_d },
+ { 0x30008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d },
+ { 0x3000a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d },
+ { 0x3000c800, 0xfc1fffe0, FOP_DED = gen_fneg_d },
+ { 0x3000e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d },
+
+ /* floating point class three */
+ { 0x30000600, 0xfc00ffe0, FOP_WEWW = gen_helper_fadd_s },
+ { 0x30002600, 0xfc00ffe0, FOP_WEWW = gen_helper_fsub_s },
+ { 0x30004600, 0xfc00ffe0, FOP_WEWW = gen_helper_fmpy_s },
+ { 0x30006600, 0xfc00ffe0, FOP_WEWW = gen_helper_fdiv_s },
+
+ { 0x30000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d },
+ { 0x30002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d },
+ { 0x30004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d },
+ { 0x30006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d },
+
+ /* floating point class one */
+ /* float/float */
+ { 0x30000a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_s },
+ { 0x30002200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_d },
+ /* int/float */
+ { 0x30008200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_w_s },
+ { 0x30008a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_dw_s },
+ { 0x3000a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_w_d },
+ { 0x3000aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d },
+ /* float/int */
+ { 0x30010200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_w },
+ { 0x30010a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_w },
+ { 0x30012200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_dw },
+ { 0x30012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw },
+ /* float/int truncate */
+ { 0x30018200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_w },
+ { 0x30018a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_w },
+ { 0x3001a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_dw },
+ { 0x3001aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw },
+ /* uint/float */
+ { 0x30028200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_uw_s },
+ { 0x30028a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_udw_s },
+ { 0x3002a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_uw_d },
+ { 0x3002aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d },
+ /* float/uint */
+ { 0x30030200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_uw },
+ { 0x30030a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_uw },
+ { 0x30032200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_udw },
+ { 0x30032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw },
+ /* float/uint truncate */
+ { 0x30038200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_uw },
+ { 0x30038a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_uw },
+ { 0x3003a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_udw },
+ { 0x3003aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw },
+
+ /* floating point class two */
+ { 0x30000400, 0xfc001fe0, trans_fcmp_s_0c },
+ { 0x30000c00, 0xfc001fe0, trans_fcmp_d },
+ { 0x30002420, 0xffffffe0, trans_ftest_q },
+ { 0x30000420, 0xffff1fff, trans_ftest_t },
+
+ /* FID. Note that ra == rt == 0, which via fcpy puts 0 into fr0.
+ This is machine/revision == 0, which is reserved for simulator. */
+ { 0x30000000, 0xffffffff, FOP_WEW = gen_fcpy_s },
+};
+
+#undef FOP_WEW
+#undef FOP_DEW
+#undef FOP_WED
+#undef FOP_WEWW
+#define FOP_WEW trans_fop_wew_0e, .f_wew
+#define FOP_DEW trans_fop_dew_0e, .f_dew
+#define FOP_WED trans_fop_wed_0e, .f_wed
+#define FOP_WEWW trans_fop_weww_0e, .f_weww
+
+static const DisasInsn table_float_0e[] = {
+ /* floating point class zero */
+ { 0x38004000, 0xfc1fff20, FOP_WEW = gen_fcpy_s },
+ { 0x38006000, 0xfc1fff20, FOP_WEW = gen_fabs_s },
+ { 0x38008000, 0xfc1fff20, FOP_WEW = gen_helper_fsqrt_s },
+ { 0x3800a000, 0xfc1fff20, FOP_WEW = gen_helper_frnd_s },
+ { 0x3800c000, 0xfc1fff20, FOP_WEW = gen_fneg_s },
+ { 0x3800e000, 0xfc1fff20, FOP_WEW = gen_fnegabs_s },
+
+ { 0x38004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d },
+ { 0x38006800, 0xfc1fffe0, FOP_DED = gen_fabs_d },
+ { 0x38008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d },
+ { 0x3800a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d },
+ { 0x3800c800, 0xfc1fffe0, FOP_DED = gen_fneg_d },
+ { 0x3800e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d },
+
+ /* floating point class three */
+ { 0x38000600, 0xfc00ef20, FOP_WEWW = gen_helper_fadd_s },
+ { 0x38002600, 0xfc00ef20, FOP_WEWW = gen_helper_fsub_s },
+ { 0x38004600, 0xfc00ef20, FOP_WEWW = gen_helper_fmpy_s },
+ { 0x38006600, 0xfc00ef20, FOP_WEWW = gen_helper_fdiv_s },
+
+ { 0x38000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d },
+ { 0x38002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d },
+ { 0x38004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d },
+ { 0x38006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d },
+
+ { 0x38004700, 0xfc00ef60, trans_xmpyu },
+
+ /* floating point class one */
+ /* float/float */
+ { 0x38000a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_s },
+ { 0x38002200, 0xfc1fffc0, FOP_DEW = gen_helper_fcnv_s_d },
+ /* int/float */
+ { 0x38008200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_w_s },
+ { 0x38008a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_dw_s },
+ { 0x3800a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_w_d },
+ { 0x3800aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d },
+ /* float/int */
+ { 0x38010200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_s_w },
+ { 0x38010a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_w },
+ { 0x38012200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_dw },
+ { 0x38012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw },
+ /* float/int truncate */
+ { 0x38018200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_t_s_w },
+ { 0x38018a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_w },
+ { 0x3801a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_dw },
+ { 0x3801aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw },
+ /* uint/float */
+ { 0x38028200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_uw_s },
+ { 0x38028a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_udw_s },
+ { 0x3802a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_uw_d },
+ { 0x3802aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d },
+ /* float/uint */
+ { 0x38030200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_s_uw },
+ { 0x38030a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_uw },
+ { 0x38032200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_udw },
+ { 0x38032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw },
+ /* float/uint truncate */
+ { 0x38038200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_t_s_uw },
+ { 0x38038a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_uw },
+ { 0x3803a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_udw },
+ { 0x3803aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw },
+
+ /* floating point class two */
+ { 0x38000400, 0xfc000f60, trans_fcmp_s_0e },
+ { 0x38000c00, 0xfc001fe0, trans_fcmp_d },
+};
+
+#undef FOP_WEW
+#undef FOP_DEW
+#undef FOP_WED
+#undef FOP_WEWW
+#undef FOP_DED
+#undef FOP_DEDD
+
+/* Convert the fmpyadd single-precision register encodings to standard. */
+static inline int fmpyadd_s_reg(unsigned r)
+{
+ return (r & 16) * 2 + 16 + (r & 15);
+}
+
+static ExitStatus trans_fmpyadd(DisasContext *ctx, uint32_t insn, bool is_sub)
+{
+ unsigned tm = extract32(insn, 0, 5);
+ unsigned f = extract32(insn, 5, 1);
+ unsigned ra = extract32(insn, 6, 5);
+ unsigned ta = extract32(insn, 11, 5);
+ unsigned rm2 = extract32(insn, 16, 5);
+ unsigned rm1 = extract32(insn, 21, 5);
+
+ nullify_over(ctx);
+
+ /* Independent multiply & add/sub, with undefined behaviour
+ if outputs overlap inputs. */
+ if (f == 0) {
+ tm = fmpyadd_s_reg(tm);
+ ra = fmpyadd_s_reg(ra);
+ ta = fmpyadd_s_reg(ta);
+ rm2 = fmpyadd_s_reg(rm2);
+ rm1 = fmpyadd_s_reg(rm1);
+ do_fop_weww(ctx, tm, rm1, rm2, gen_helper_fmpy_s);
+ do_fop_weww(ctx, ta, ta, ra,
+ is_sub ? gen_helper_fsub_s : gen_helper_fadd_s);
+ } else {
+ do_fop_dedd(ctx, tm, rm1, rm2, gen_helper_fmpy_d);
+ do_fop_dedd(ctx, ta, ta, ra,
+ is_sub ? gen_helper_fsub_d : gen_helper_fadd_d);
+ }
+
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus trans_fmpyfadd_s(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = assemble_rt64(insn);
+ unsigned neg = extract32(insn, 5, 1);
+ unsigned rm1 = assemble_ra64(insn);
+ unsigned rm2 = assemble_rb64(insn);
+ unsigned ra3 = assemble_rc64(insn);
+ TCGv_i32 a, b, c;
+
+ nullify_over(ctx);
+ a = load_frw0_i32(rm1);
+ b = load_frw0_i32(rm2);
+ c = load_frw0_i32(ra3);
+
+ if (neg) {
+ gen_helper_fmpynfadd_s(a, cpu_env, a, b, c);
+ } else {
+ gen_helper_fmpyfadd_s(a, cpu_env, a, b, c);
+ }
+
+ tcg_temp_free_i32(b);
+ tcg_temp_free_i32(c);
+ save_frw_i32(rt, a);
+ tcg_temp_free_i32(a);
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static ExitStatus trans_fmpyfadd_d(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ unsigned neg = extract32(insn, 5, 1);
+ unsigned rm1 = extract32(insn, 21, 5);
+ unsigned rm2 = extract32(insn, 16, 5);
+ unsigned ra3 = assemble_rc64(insn);
+ TCGv_i64 a, b, c;
+
+ nullify_over(ctx);
+ a = load_frd0(rm1);
+ b = load_frd0(rm2);
+ c = load_frd0(ra3);
+
+ if (neg) {
+ gen_helper_fmpynfadd_d(a, cpu_env, a, b, c);
+ } else {
+ gen_helper_fmpyfadd_d(a, cpu_env, a, b, c);
+ }
+
+ tcg_temp_free_i64(b);
+ tcg_temp_free_i64(c);
+ save_frd(rt, a);
+ tcg_temp_free_i64(a);
+ return nullify_end(ctx, NO_EXIT);
+}
+
+static const DisasInsn table_fp_fused[] = {
+ { 0xb8000000u, 0xfc000800u, trans_fmpyfadd_s },
+ { 0xb8000800u, 0xfc0019c0u, trans_fmpyfadd_d }
+};
+
static ExitStatus translate_table_int(DisasContext *ctx, uint32_t insn,
const DisasInsn table[], size_t n)
{
@@ -2921,6 +3639,8 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
return translate_table(ctx, insn, table_arith_log);
case 0x03:
return translate_table(ctx, insn, table_index_mem);
+ case 0x06:
+ return trans_fmpyadd(ctx, insn, false);
case 0x08:
return trans_ldil(ctx, insn);
case 0x09:
@@ -2929,8 +3649,12 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
return trans_addil(ctx, insn);
case 0x0B:
return trans_copr_dw(ctx, insn);
+ case 0x0C:
+ return translate_table(ctx, insn, table_float_0c);
case 0x0D:
return trans_ldo(ctx, insn);
+ case 0x0E:
+ return translate_table(ctx, insn, table_float_0e);
case 0x10:
return trans_load(ctx, insn, false, MO_UB);
@@ -2969,6 +3693,8 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
return trans_cmpiclr(ctx, insn);
case 0x25:
return trans_subi(ctx, insn);
+ case 0x26:
+ return trans_fmpyadd(ctx, insn, true);
case 0x27:
return trans_cmpb(ctx, insn, true, false, true);
case 0x28:
@@ -2982,6 +3708,8 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn)
case 0x2C:
case 0x2D:
return trans_addi(ctx, insn);
+ case 0x2E:
+ return translate_table(ctx, insn, table_fp_fused);
case 0x2F:
return trans_cmpb(ctx, insn, false, false, true);