target/sparc: Remove CC_OP_DIV
Return both result and overflow from helper_[us]div. Compute all flags explicitly in gen_op_[us]divcc. Marginally improve the INT64_MIN special case in helper_sdiv. Tested-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Acked-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
parent
2a45b73658
commit
1326010322
@ -47,30 +47,6 @@ static inline uint32_t get_NZ_xcc(target_long dst)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline uint32_t get_V_div_icc(target_ulong src2)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
|
||||
if (src2 != 0) {
|
||||
ret = PSR_OVF;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t compute_all_div(CPUSPARCState *env)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
ret = get_NZ_icc(CC_DST);
|
||||
ret |= get_V_div_icc(CC_SRC2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t compute_C_div(CPUSPARCState *env)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline uint32_t get_C_add_icc(uint32_t dst, uint32_t src1)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
@ -378,13 +354,6 @@ static uint32_t compute_all_tsubtv(CPUSPARCState *env)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef TARGET_SPARC64
|
||||
static uint32_t compute_all_logic_xcc(CPUSPARCState *env)
|
||||
{
|
||||
return get_NZ_xcc(CC_DST);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct CCTable {
|
||||
uint32_t (*compute_all)(CPUSPARCState *env); /* return all the flags */
|
||||
uint32_t (*compute_c)(CPUSPARCState *env); /* return the C flag */
|
||||
@ -392,7 +361,6 @@ typedef struct CCTable {
|
||||
|
||||
static const CCTable icc_table[CC_OP_NB] = {
|
||||
/* CC_OP_DYNAMIC should never happen */
|
||||
[CC_OP_DIV] = { compute_all_div, compute_C_div },
|
||||
[CC_OP_ADD] = { compute_all_add, compute_C_add },
|
||||
[CC_OP_ADDX] = { compute_all_addx, compute_C_addx },
|
||||
[CC_OP_TADD] = { compute_all_tadd, compute_C_add },
|
||||
@ -406,7 +374,6 @@ static const CCTable icc_table[CC_OP_NB] = {
|
||||
#ifdef TARGET_SPARC64
|
||||
static const CCTable xcc_table[CC_OP_NB] = {
|
||||
/* CC_OP_DYNAMIC should never happen */
|
||||
[CC_OP_DIV] = { compute_all_logic_xcc, compute_C_div },
|
||||
[CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc },
|
||||
[CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc },
|
||||
[CC_OP_TADD] = { compute_all_add_xcc, compute_C_add_xcc },
|
||||
|
@ -150,7 +150,6 @@ enum {
|
||||
enum {
|
||||
CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
|
||||
CC_OP_FLAGS, /* all cc are back in cc_*_[NZCV] registers */
|
||||
CC_OP_DIV, /* modify N, Z and V, C = 0*/
|
||||
CC_OP_ADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_ADDX, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
CC_OP_TADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */
|
||||
|
@ -81,79 +81,52 @@ void helper_tick_set_limit(void *opaque, uint64_t limit)
|
||||
}
|
||||
#endif
|
||||
|
||||
static target_ulong do_udiv(CPUSPARCState *env, target_ulong a,
|
||||
target_ulong b, int cc, uintptr_t ra)
|
||||
uint64_t helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b)
|
||||
{
|
||||
int overflow = 0;
|
||||
uint64_t x0;
|
||||
uint32_t x1;
|
||||
uint64_t a64 = (uint32_t)a | ((uint64_t)env->y << 32);
|
||||
uint32_t b32 = b;
|
||||
uint32_t r;
|
||||
|
||||
x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32);
|
||||
x1 = (b & 0xffffffff);
|
||||
|
||||
if (x1 == 0) {
|
||||
cpu_raise_exception_ra(env, TT_DIV_ZERO, ra);
|
||||
if (b32 == 0) {
|
||||
cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC());
|
||||
}
|
||||
|
||||
x0 = x0 / x1;
|
||||
if (x0 > UINT32_MAX) {
|
||||
x0 = UINT32_MAX;
|
||||
overflow = 1;
|
||||
a64 /= b32;
|
||||
r = a64;
|
||||
if (unlikely(a64 > UINT32_MAX)) {
|
||||
return -1; /* r = UINT32_MAX, v = 1 */
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
uint64_t helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b)
|
||||
{
|
||||
int64_t a64 = (uint32_t)a | ((uint64_t)env->y << 32);
|
||||
int32_t b32 = b;
|
||||
int32_t r;
|
||||
|
||||
if (b32 == 0) {
|
||||
cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC());
|
||||
}
|
||||
|
||||
if (cc) {
|
||||
env->cc_src2 = overflow;
|
||||
}
|
||||
return x0;
|
||||
}
|
||||
|
||||
target_ulong helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b)
|
||||
{
|
||||
return do_udiv(env, a, b, 0, GETPC());
|
||||
}
|
||||
|
||||
target_ulong helper_udiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b)
|
||||
{
|
||||
return do_udiv(env, a, b, 1, GETPC());
|
||||
}
|
||||
|
||||
static target_ulong do_sdiv(CPUSPARCState *env, target_ulong a,
|
||||
target_ulong b, int cc, uintptr_t ra)
|
||||
{
|
||||
int overflow = 0;
|
||||
int64_t x0;
|
||||
int32_t x1;
|
||||
|
||||
x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32);
|
||||
x1 = (b & 0xffffffff);
|
||||
|
||||
if (x1 == 0) {
|
||||
cpu_raise_exception_ra(env, TT_DIV_ZERO, ra);
|
||||
} else if (x1 == -1 && x0 == INT64_MIN) {
|
||||
x0 = INT32_MAX;
|
||||
overflow = 1;
|
||||
} else {
|
||||
x0 = x0 / x1;
|
||||
if ((int32_t) x0 != x0) {
|
||||
x0 = x0 < 0 ? INT32_MIN : INT32_MAX;
|
||||
overflow = 1;
|
||||
}
|
||||
if (unlikely(a64 == INT64_MIN)) {
|
||||
/*
|
||||
* Special case INT64_MIN / -1 is required to avoid trap on x86 host.
|
||||
* However, with a dividend of INT64_MIN, there is no 32-bit divisor
|
||||
* which can yield a 32-bit result:
|
||||
* INT64_MIN / INT32_MIN = 0x1_0000_0000
|
||||
* INT64_MIN / INT32_MAX = -0x1_0000_0002
|
||||
* Therefore we know we must overflow and saturate.
|
||||
*/
|
||||
return (uint32_t)(b32 < 0 ? INT32_MAX : INT32_MIN) | (-1ull << 32);
|
||||
}
|
||||
|
||||
if (cc) {
|
||||
env->cc_src2 = overflow;
|
||||
a64 /= b;
|
||||
r = a64;
|
||||
if (unlikely(r != a64)) {
|
||||
return (uint32_t)(a64 < 0 ? INT32_MIN : INT32_MAX) | (-1ull << 32);
|
||||
}
|
||||
return x0;
|
||||
}
|
||||
|
||||
target_ulong helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b)
|
||||
{
|
||||
return do_sdiv(env, a, b, 0, GETPC());
|
||||
}
|
||||
|
||||
target_ulong helper_sdiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b)
|
||||
{
|
||||
return do_sdiv(env, a, b, 1, GETPC());
|
||||
return (uint32_t)r;
|
||||
}
|
||||
|
||||
#ifdef TARGET_SPARC64
|
||||
|
@ -27,10 +27,8 @@ DEF_HELPER_FLAGS_2(tick_set_limit, TCG_CALL_NO_RWG, void, ptr, i64)
|
||||
DEF_HELPER_1(debug, void, env)
|
||||
DEF_HELPER_1(save, void, env)
|
||||
DEF_HELPER_1(restore, void, env)
|
||||
DEF_HELPER_3(udiv, tl, env, tl, tl)
|
||||
DEF_HELPER_3(udiv_cc, tl, env, tl, tl)
|
||||
DEF_HELPER_3(sdiv, tl, env, tl, tl)
|
||||
DEF_HELPER_3(sdiv_cc, tl, env, tl, tl)
|
||||
DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_WG, i64, env, tl, tl)
|
||||
DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_WG, i64, env, tl, tl)
|
||||
DEF_HELPER_3(taddcctv, tl, env, tl, tl)
|
||||
DEF_HELPER_3(tsubcctv, tl, env, tl, tl)
|
||||
#ifdef TARGET_SPARC64
|
||||
|
@ -693,22 +693,76 @@ static void gen_op_sdivx(TCGv dst, TCGv src1, TCGv src2)
|
||||
|
||||
static void gen_op_udiv(TCGv dst, TCGv src1, TCGv src2)
|
||||
{
|
||||
#ifdef TARGET_SPARC64
|
||||
gen_helper_udiv(dst, tcg_env, src1, src2);
|
||||
tcg_gen_ext32u_tl(dst, dst);
|
||||
#else
|
||||
TCGv_i64 t64 = tcg_temp_new_i64();
|
||||
gen_helper_udiv(t64, tcg_env, src1, src2);
|
||||
tcg_gen_trunc_i64_tl(dst, t64);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gen_op_sdiv(TCGv dst, TCGv src1, TCGv src2)
|
||||
{
|
||||
#ifdef TARGET_SPARC64
|
||||
gen_helper_sdiv(dst, tcg_env, src1, src2);
|
||||
tcg_gen_ext32s_tl(dst, dst);
|
||||
#else
|
||||
TCGv_i64 t64 = tcg_temp_new_i64();
|
||||
gen_helper_sdiv(t64, tcg_env, src1, src2);
|
||||
tcg_gen_trunc_i64_tl(dst, t64);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gen_op_udivcc(TCGv dst, TCGv src1, TCGv src2)
|
||||
{
|
||||
gen_helper_udiv_cc(dst, tcg_env, src1, src2);
|
||||
TCGv_i64 t64;
|
||||
|
||||
#ifdef TARGET_SPARC64
|
||||
t64 = cpu_cc_V;
|
||||
#else
|
||||
t64 = tcg_temp_new_i64();
|
||||
#endif
|
||||
|
||||
gen_helper_udiv(t64, tcg_env, src1, src2);
|
||||
|
||||
#ifdef TARGET_SPARC64
|
||||
tcg_gen_ext32u_tl(cpu_cc_N, t64);
|
||||
tcg_gen_shri_tl(cpu_cc_V, t64, 32);
|
||||
tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N);
|
||||
tcg_gen_movi_tl(cpu_icc_C, 0);
|
||||
#else
|
||||
tcg_gen_extr_i64_tl(cpu_cc_N, cpu_cc_V, t64);
|
||||
#endif
|
||||
tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N);
|
||||
tcg_gen_movi_tl(cpu_cc_C, 0);
|
||||
tcg_gen_mov_tl(dst, cpu_cc_N);
|
||||
}
|
||||
|
||||
static void gen_op_sdivcc(TCGv dst, TCGv src1, TCGv src2)
|
||||
{
|
||||
gen_helper_sdiv_cc(dst, tcg_env, src1, src2);
|
||||
TCGv_i64 t64;
|
||||
|
||||
#ifdef TARGET_SPARC64
|
||||
t64 = cpu_cc_V;
|
||||
#else
|
||||
t64 = tcg_temp_new_i64();
|
||||
#endif
|
||||
|
||||
gen_helper_sdiv(t64, tcg_env, src1, src2);
|
||||
|
||||
#ifdef TARGET_SPARC64
|
||||
tcg_gen_ext32s_tl(cpu_cc_N, t64);
|
||||
tcg_gen_shri_tl(cpu_cc_V, t64, 32);
|
||||
tcg_gen_mov_tl(cpu_icc_Z, cpu_cc_N);
|
||||
tcg_gen_movi_tl(cpu_icc_C, 0);
|
||||
#else
|
||||
tcg_gen_extr_i64_tl(cpu_cc_N, cpu_cc_V, t64);
|
||||
#endif
|
||||
tcg_gen_mov_tl(cpu_cc_Z, cpu_cc_N);
|
||||
tcg_gen_movi_tl(cpu_cc_C, 0);
|
||||
tcg_gen_mov_tl(dst, cpu_cc_N);
|
||||
}
|
||||
|
||||
static void gen_op_taddcctv(TCGv dst, TCGv src1, TCGv src2)
|
||||
@ -3717,8 +3771,8 @@ TRANS(SMUL, MUL, do_logic, a, gen_op_smul, NULL)
|
||||
|
||||
TRANS(UDIVX, 64, do_arith, a, -1, gen_op_udivx, NULL, NULL)
|
||||
TRANS(SDIVX, 64, do_arith, a, -1, gen_op_sdivx, NULL, NULL)
|
||||
TRANS(UDIV, DIV, do_arith, a, CC_OP_DIV, gen_op_udiv, NULL, gen_op_udivcc)
|
||||
TRANS(SDIV, DIV, do_arith, a, CC_OP_DIV, gen_op_sdiv, NULL, gen_op_sdivcc)
|
||||
TRANS(UDIV, DIV, do_arith, a, CC_OP_FLAGS, gen_op_udiv, NULL, gen_op_udivcc)
|
||||
TRANS(SDIV, DIV, do_arith, a, CC_OP_FLAGS, gen_op_sdiv, NULL, gen_op_sdivcc)
|
||||
|
||||
/* TODO: Should have feature bit -- comes in with UltraSparc T2. */
|
||||
TRANS(POPC, 64, do_arith, a, -1, gen_op_popc, NULL, NULL)
|
||||
@ -3743,10 +3797,6 @@ static bool trans_OR(DisasContext *dc, arg_r_r_ri_cc *a)
|
||||
static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a)
|
||||
{
|
||||
switch (dc->cc_op) {
|
||||
case CC_OP_DIV:
|
||||
/* Carry is known to be zero. Fall back to plain ADD. */
|
||||
return do_arith(dc, a, CC_OP_ADD,
|
||||
tcg_gen_add_tl, tcg_gen_addi_tl, gen_op_add_cc);
|
||||
case CC_OP_ADD:
|
||||
case CC_OP_TADD:
|
||||
case CC_OP_TADDTV:
|
||||
@ -3766,10 +3816,6 @@ static bool trans_ADDC(DisasContext *dc, arg_r_r_ri_cc *a)
|
||||
static bool trans_SUBC(DisasContext *dc, arg_r_r_ri_cc *a)
|
||||
{
|
||||
switch (dc->cc_op) {
|
||||
case CC_OP_DIV:
|
||||
/* Carry is known to be zero. Fall back to plain SUB. */
|
||||
return do_arith(dc, a, CC_OP_SUB,
|
||||
tcg_gen_sub_tl, tcg_gen_subi_tl, gen_op_sub_cc);
|
||||
case CC_OP_ADD:
|
||||
case CC_OP_TADD:
|
||||
case CC_OP_TADDTV:
|
||||
|
Loading…
x
Reference in New Issue
Block a user