target-sparc: Inline some generation of carry for ADDX/SUBX.
Computing carry is trivial for some inputs. By avoiding an external function call, we generate near-optimal code for the common cases of add+addx (double-word arithmetic) and cmp+addx (a setcc pattern). Signed-off-by: Richard Henderson <rth@twiddle.net> Acked-by: Artyom Tarasenko <atar4qemu@gmail.com> Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
parent
da441cffde
commit
70c482852a
@ -158,6 +158,6 @@ VIS_CMPHELPER(cmpne);
|
|||||||
#undef VIS_HELPER
|
#undef VIS_HELPER
|
||||||
#undef VIS_CMPHELPER
|
#undef VIS_CMPHELPER
|
||||||
DEF_HELPER_0(compute_psr, void);
|
DEF_HELPER_0(compute_psr, void);
|
||||||
DEF_HELPER_0(compute_C_icc, tl);
|
DEF_HELPER_0(compute_C_icc, i32);
|
||||||
|
|
||||||
#include "def-helper.h"
|
#include "def-helper.h"
|
||||||
|
@ -1347,7 +1347,7 @@ void helper_compute_psr(void)
|
|||||||
CC_OP = CC_OP_FLAGS;
|
CC_OP = CC_OP_FLAGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
target_ulong helper_compute_C_icc(void)
|
uint32_t helper_compute_C_icc(void)
|
||||||
{
|
{
|
||||||
uint32_t ret;
|
uint32_t ret;
|
||||||
|
|
||||||
|
@ -332,24 +332,132 @@ static inline void gen_op_add_cc(TCGv dst, TCGv src1, TCGv src2)
|
|||||||
tcg_gen_mov_tl(dst, cpu_cc_dst);
|
tcg_gen_mov_tl(dst, cpu_cc_dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_op_addxi_cc(TCGv dst, TCGv src1, target_long src2)
|
static TCGv_i32 gen_add32_carry32(void)
|
||||||
{
|
{
|
||||||
gen_helper_compute_C_icc(cpu_tmp0);
|
TCGv_i32 carry_32, cc_src1_32, cc_src2_32;
|
||||||
tcg_gen_mov_tl(cpu_cc_src, src1);
|
|
||||||
tcg_gen_movi_tl(cpu_cc_src2, src2);
|
/* Carry is computed from a previous add: (dst < src) */
|
||||||
tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_tmp0);
|
#if TARGET_LONG_BITS == 64
|
||||||
tcg_gen_addi_tl(cpu_cc_dst, cpu_cc_dst, src2);
|
cc_src1_32 = tcg_temp_new_i32();
|
||||||
tcg_gen_mov_tl(dst, cpu_cc_dst);
|
cc_src2_32 = tcg_temp_new_i32();
|
||||||
|
tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_dst);
|
||||||
|
tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src);
|
||||||
|
#else
|
||||||
|
cc_src1_32 = cpu_cc_dst;
|
||||||
|
cc_src2_32 = cpu_cc_src;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
carry_32 = tcg_temp_new_i32();
|
||||||
|
tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32);
|
||||||
|
|
||||||
|
#if TARGET_LONG_BITS == 64
|
||||||
|
tcg_temp_free_i32(cc_src1_32);
|
||||||
|
tcg_temp_free_i32(cc_src2_32);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return carry_32;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_op_addx_cc(TCGv dst, TCGv src1, TCGv src2)
|
static TCGv_i32 gen_sub32_carry32(void)
|
||||||
{
|
{
|
||||||
gen_helper_compute_C_icc(cpu_tmp0);
|
TCGv_i32 carry_32, cc_src1_32, cc_src2_32;
|
||||||
tcg_gen_mov_tl(cpu_cc_src, src1);
|
|
||||||
tcg_gen_mov_tl(cpu_cc_src2, src2);
|
/* Carry is computed from a previous borrow: (src1 < src2) */
|
||||||
tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_tmp0);
|
#if TARGET_LONG_BITS == 64
|
||||||
tcg_gen_add_tl(cpu_cc_dst, cpu_cc_dst, cpu_cc_src2);
|
cc_src1_32 = tcg_temp_new_i32();
|
||||||
tcg_gen_mov_tl(dst, cpu_cc_dst);
|
cc_src2_32 = tcg_temp_new_i32();
|
||||||
|
tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_src);
|
||||||
|
tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src2);
|
||||||
|
#else
|
||||||
|
cc_src1_32 = cpu_cc_src;
|
||||||
|
cc_src2_32 = cpu_cc_src2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
carry_32 = tcg_temp_new_i32();
|
||||||
|
tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32);
|
||||||
|
|
||||||
|
#if TARGET_LONG_BITS == 64
|
||||||
|
tcg_temp_free_i32(cc_src1_32);
|
||||||
|
tcg_temp_free_i32(cc_src2_32);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return carry_32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1,
|
||||||
|
TCGv src2, int update_cc)
|
||||||
|
{
|
||||||
|
TCGv_i32 carry_32;
|
||||||
|
TCGv carry;
|
||||||
|
|
||||||
|
switch (dc->cc_op) {
|
||||||
|
case CC_OP_DIV:
|
||||||
|
case CC_OP_LOGIC:
|
||||||
|
/* Carry is known to be zero. Fall back to plain ADD. */
|
||||||
|
if (update_cc) {
|
||||||
|
gen_op_add_cc(dst, src1, src2);
|
||||||
|
} else {
|
||||||
|
tcg_gen_add_tl(dst, src1, src2);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case CC_OP_ADD:
|
||||||
|
case CC_OP_TADD:
|
||||||
|
case CC_OP_TADDTV:
|
||||||
|
#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32
|
||||||
|
{
|
||||||
|
/* For 32-bit hosts, we can re-use the host's hardware carry
|
||||||
|
generation by using an ADD2 opcode. We discard the low
|
||||||
|
part of the output. Ideally we'd combine this operation
|
||||||
|
with the add that generated the carry in the first place. */
|
||||||
|
TCGv dst_low = tcg_temp_new();
|
||||||
|
tcg_gen_op6_i32(INDEX_op_add2_i32, dst_low, dst,
|
||||||
|
cpu_cc_src, src1, cpu_cc_src2, src2);
|
||||||
|
tcg_temp_free(dst_low);
|
||||||
|
goto add_done;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
carry_32 = gen_add32_carry32();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CC_OP_SUB:
|
||||||
|
case CC_OP_TSUB:
|
||||||
|
case CC_OP_TSUBTV:
|
||||||
|
carry_32 = gen_sub32_carry32();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* We need external help to produce the carry. */
|
||||||
|
carry_32 = tcg_temp_new_i32();
|
||||||
|
gen_helper_compute_C_icc(carry_32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TARGET_LONG_BITS == 64
|
||||||
|
carry = tcg_temp_new();
|
||||||
|
tcg_gen_extu_i32_i64(carry, carry_32);
|
||||||
|
#else
|
||||||
|
carry = carry_32;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tcg_gen_add_tl(dst, src1, src2);
|
||||||
|
tcg_gen_add_tl(dst, dst, carry);
|
||||||
|
|
||||||
|
tcg_temp_free_i32(carry_32);
|
||||||
|
#if TARGET_LONG_BITS == 64
|
||||||
|
tcg_temp_free(carry);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32
|
||||||
|
add_done:
|
||||||
|
#endif
|
||||||
|
if (update_cc) {
|
||||||
|
tcg_gen_mov_tl(cpu_cc_src, src1);
|
||||||
|
tcg_gen_mov_tl(cpu_cc_src2, src2);
|
||||||
|
tcg_gen_mov_tl(cpu_cc_dst, dst);
|
||||||
|
tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX);
|
||||||
|
dc->cc_op = CC_OP_ADDX;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_op_tadd_cc(TCGv dst, TCGv src1, TCGv src2)
|
static inline void gen_op_tadd_cc(TCGv dst, TCGv src1, TCGv src2)
|
||||||
@ -415,24 +523,80 @@ static inline void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2)
|
|||||||
tcg_gen_mov_tl(dst, cpu_cc_dst);
|
tcg_gen_mov_tl(dst, cpu_cc_dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_op_subxi_cc(TCGv dst, TCGv src1, target_long src2)
|
static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1,
|
||||||
|
TCGv src2, int update_cc)
|
||||||
{
|
{
|
||||||
gen_helper_compute_C_icc(cpu_tmp0);
|
TCGv_i32 carry_32;
|
||||||
tcg_gen_mov_tl(cpu_cc_src, src1);
|
TCGv carry;
|
||||||
tcg_gen_movi_tl(cpu_cc_src2, src2);
|
|
||||||
tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_tmp0);
|
|
||||||
tcg_gen_subi_tl(cpu_cc_dst, cpu_cc_dst, src2);
|
|
||||||
tcg_gen_mov_tl(dst, cpu_cc_dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void gen_op_subx_cc(TCGv dst, TCGv src1, TCGv src2)
|
switch (dc->cc_op) {
|
||||||
{
|
case CC_OP_DIV:
|
||||||
gen_helper_compute_C_icc(cpu_tmp0);
|
case CC_OP_LOGIC:
|
||||||
tcg_gen_mov_tl(cpu_cc_src, src1);
|
/* Carry is known to be zero. Fall back to plain SUB. */
|
||||||
tcg_gen_mov_tl(cpu_cc_src2, src2);
|
if (update_cc) {
|
||||||
tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_tmp0);
|
gen_op_sub_cc(dst, src1, src2);
|
||||||
tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_dst, cpu_cc_src2);
|
} else {
|
||||||
tcg_gen_mov_tl(dst, cpu_cc_dst);
|
tcg_gen_sub_tl(dst, src1, src2);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case CC_OP_ADD:
|
||||||
|
case CC_OP_TADD:
|
||||||
|
case CC_OP_TADDTV:
|
||||||
|
carry_32 = gen_add32_carry32();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CC_OP_SUB:
|
||||||
|
case CC_OP_TSUB:
|
||||||
|
case CC_OP_TSUBTV:
|
||||||
|
#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32
|
||||||
|
{
|
||||||
|
/* For 32-bit hosts, we can re-use the host's hardware carry
|
||||||
|
generation by using a SUB2 opcode. We discard the low
|
||||||
|
part of the output. Ideally we'd combine this operation
|
||||||
|
with the add that generated the carry in the first place. */
|
||||||
|
TCGv dst_low = tcg_temp_new();
|
||||||
|
tcg_gen_op6_i32(INDEX_op_sub2_i32, dst_low, dst,
|
||||||
|
cpu_cc_src, src1, cpu_cc_src2, src2);
|
||||||
|
tcg_temp_free(dst_low);
|
||||||
|
goto sub_done;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
carry_32 = gen_sub32_carry32();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* We need external help to produce the carry. */
|
||||||
|
carry_32 = tcg_temp_new_i32();
|
||||||
|
gen_helper_compute_C_icc(carry_32);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TARGET_LONG_BITS == 64
|
||||||
|
carry = tcg_temp_new();
|
||||||
|
tcg_gen_extu_i32_i64(carry, carry_32);
|
||||||
|
#else
|
||||||
|
carry = carry_32;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tcg_gen_sub_tl(dst, src1, src2);
|
||||||
|
tcg_gen_sub_tl(dst, dst, carry);
|
||||||
|
|
||||||
|
tcg_temp_free_i32(carry_32);
|
||||||
|
#if TARGET_LONG_BITS == 64
|
||||||
|
tcg_temp_free(carry);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32
|
||||||
|
sub_done:
|
||||||
|
#endif
|
||||||
|
if (update_cc) {
|
||||||
|
tcg_gen_mov_tl(cpu_cc_src, src1);
|
||||||
|
tcg_gen_mov_tl(cpu_cc_src2, src2);
|
||||||
|
tcg_gen_mov_tl(cpu_cc_dst, dst);
|
||||||
|
tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX);
|
||||||
|
dc->cc_op = CC_OP_SUBX;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gen_op_tsub_cc(TCGv dst, TCGv src1, TCGv src2)
|
static inline void gen_op_tsub_cc(TCGv dst, TCGv src1, TCGv src2)
|
||||||
@ -2950,28 +3114,8 @@ static void disas_sparc_insn(DisasContext * dc)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x8: /* addx, V9 addc */
|
case 0x8: /* addx, V9 addc */
|
||||||
if (IS_IMM) {
|
gen_op_addx_int(dc, cpu_dst, cpu_src1, cpu_src2,
|
||||||
simm = GET_FIELDs(insn, 19, 31);
|
(xop & 0x10));
|
||||||
if (xop & 0x10) {
|
|
||||||
gen_op_addxi_cc(cpu_dst, cpu_src1, simm);
|
|
||||||
tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX);
|
|
||||||
dc->cc_op = CC_OP_ADDX;
|
|
||||||
} else {
|
|
||||||
gen_helper_compute_C_icc(cpu_tmp0);
|
|
||||||
tcg_gen_addi_tl(cpu_tmp0, cpu_tmp0, simm);
|
|
||||||
tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_tmp0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (xop & 0x10) {
|
|
||||||
gen_op_addx_cc(cpu_dst, cpu_src1, cpu_src2);
|
|
||||||
tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX);
|
|
||||||
dc->cc_op = CC_OP_ADDX;
|
|
||||||
} else {
|
|
||||||
gen_helper_compute_C_icc(cpu_tmp0);
|
|
||||||
tcg_gen_add_tl(cpu_tmp0, cpu_src2, cpu_tmp0);
|
|
||||||
tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_tmp0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
#ifdef TARGET_SPARC64
|
#ifdef TARGET_SPARC64
|
||||||
case 0x9: /* V9 mulx */
|
case 0x9: /* V9 mulx */
|
||||||
@ -3002,28 +3146,8 @@ static void disas_sparc_insn(DisasContext * dc)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0xc: /* subx, V9 subc */
|
case 0xc: /* subx, V9 subc */
|
||||||
if (IS_IMM) {
|
gen_op_subx_int(dc, cpu_dst, cpu_src1, cpu_src2,
|
||||||
simm = GET_FIELDs(insn, 19, 31);
|
(xop & 0x10));
|
||||||
if (xop & 0x10) {
|
|
||||||
gen_op_subxi_cc(cpu_dst, cpu_src1, simm);
|
|
||||||
tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX);
|
|
||||||
dc->cc_op = CC_OP_SUBX;
|
|
||||||
} else {
|
|
||||||
gen_helper_compute_C_icc(cpu_tmp0);
|
|
||||||
tcg_gen_addi_tl(cpu_tmp0, cpu_tmp0, simm);
|
|
||||||
tcg_gen_sub_tl(cpu_dst, cpu_src1, cpu_tmp0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (xop & 0x10) {
|
|
||||||
gen_op_subx_cc(cpu_dst, cpu_src1, cpu_src2);
|
|
||||||
tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX);
|
|
||||||
dc->cc_op = CC_OP_SUBX;
|
|
||||||
} else {
|
|
||||||
gen_helper_compute_C_icc(cpu_tmp0);
|
|
||||||
tcg_gen_add_tl(cpu_tmp0, cpu_src2, cpu_tmp0);
|
|
||||||
tcg_gen_sub_tl(cpu_dst, cpu_src1, cpu_tmp0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
#ifdef TARGET_SPARC64
|
#ifdef TARGET_SPARC64
|
||||||
case 0xd: /* V9 udivx */
|
case 0xd: /* V9 udivx */
|
||||||
|
Loading…
Reference in New Issue
Block a user