target-mips: optimize gen_arith_imm()

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7092 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
aurel32 2009-04-11 18:42:17 +00:00
parent b2ee0ce237
commit 324d9e3204
1 changed files with 161 additions and 106 deletions

View File

@ -57,10 +57,12 @@ enum {
OPC_ADDIU = (0x09 << 26),
OPC_SLTI = (0x0A << 26),
OPC_SLTIU = (0x0B << 26),
/* logic with immediate */
OPC_ANDI = (0x0C << 26),
OPC_ORI = (0x0D << 26),
OPC_XORI = (0x0E << 26),
OPC_LUI = (0x0F << 26),
/* arithmetic with immediate */
OPC_DADDI = (0x18 << 26),
OPC_DADDIU = (0x19 << 26),
/* Jump and branches */
@ -1197,140 +1199,184 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft,
static void gen_arith_imm (CPUState *env, DisasContext *ctx, uint32_t opc,
int rt, int rs, int16_t imm)
{
target_ulong uimm;
target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */
const char *opn = "imm arith";
TCGv t0 = tcg_temp_local_new();
if (rt == 0 && opc != OPC_ADDI && opc != OPC_DADDI) {
/* If no destination, treat it as a NOP.
For addi, we must generate the overflow exception when needed. */
MIPS_DEBUG("NOP");
goto out;
}
uimm = (uint16_t)imm;
switch (opc) {
case OPC_ADDI:
case OPC_ADDIU:
#if defined(TARGET_MIPS64)
case OPC_DADDI:
case OPC_DADDIU:
#endif
case OPC_SLTI:
case OPC_SLTIU:
uimm = (target_long)imm; /* Sign extend to 32/64 bits */
/* Fall through. */
case OPC_ANDI:
case OPC_ORI:
case OPC_XORI:
gen_load_gpr(t0, rs);
break;
case OPC_LUI:
tcg_gen_movi_tl(t0, imm << 16);
break;
case OPC_SLL:
case OPC_SRA:
case OPC_SRL:
#if defined(TARGET_MIPS64)
case OPC_DSLL:
case OPC_DSRA:
case OPC_DSRL:
case OPC_DSLL32:
case OPC_DSRA32:
case OPC_DSRL32:
#endif
uimm &= 0x1f;
gen_load_gpr(t0, rs);
break;
return;
}
switch (opc) {
case OPC_ADDI:
{
TCGv r_tmp1 = tcg_temp_new();
TCGv r_tmp2 = tcg_temp_new();
TCGv t0 = tcg_temp_local_new();
TCGv t1 = tcg_temp_new();
TCGv t2 = tcg_temp_new();
int l1 = gen_new_label();
save_cpu_state(ctx, 1);
tcg_gen_ext32s_tl(r_tmp1, t0);
tcg_gen_addi_tl(t0, r_tmp1, uimm);
gen_load_gpr(t1, rs);
tcg_gen_addi_tl(t0, t1, uimm);
tcg_gen_ext32s_tl(t0, t0);
tcg_gen_xori_tl(r_tmp1, r_tmp1, ~uimm);
tcg_gen_xori_tl(r_tmp2, t0, uimm);
tcg_gen_and_tl(r_tmp1, r_tmp1, r_tmp2);
tcg_temp_free(r_tmp2);
tcg_gen_brcondi_tl(TCG_COND_GE, r_tmp1, 0, l1);
tcg_gen_xori_tl(t1, t1, ~uimm);
tcg_gen_xori_tl(t2, t0, uimm);
tcg_gen_and_tl(t1, t1, t2);
tcg_temp_free(t2);
tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
tcg_temp_free(t1);
/* operands of same sign, result different sign */
generate_exception(ctx, EXCP_OVERFLOW);
gen_set_label(l1);
tcg_temp_free(r_tmp1);
tcg_gen_ext32s_tl(t0, t0);
gen_store_gpr(t0, rt);
tcg_temp_free(t0);
}
opn = "addi";
break;
case OPC_ADDIU:
tcg_gen_addi_tl(t0, t0, uimm);
tcg_gen_ext32s_tl(t0, t0);
if (rs != 0) {
tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]);
} else {
tcg_gen_movi_tl(cpu_gpr[rt], uimm);
}
opn = "addiu";
break;
#if defined(TARGET_MIPS64)
case OPC_DADDI:
{
TCGv r_tmp1 = tcg_temp_new();
TCGv r_tmp2 = tcg_temp_new();
TCGv t0 = tcg_temp_local_new();
TCGv t1 = tcg_temp_new();
TCGv t2 = tcg_temp_new();
int l1 = gen_new_label();
save_cpu_state(ctx, 1);
tcg_gen_mov_tl(r_tmp1, t0);
tcg_gen_addi_tl(t0, t0, uimm);
gen_load_gpr(t1, rs);
tcg_gen_addi_tl(t0, t1, uimm);
tcg_gen_xori_tl(r_tmp1, r_tmp1, ~uimm);
tcg_gen_xori_tl(r_tmp2, t0, uimm);
tcg_gen_and_tl(r_tmp1, r_tmp1, r_tmp2);
tcg_temp_free(r_tmp2);
tcg_gen_brcondi_tl(TCG_COND_GE, r_tmp1, 0, l1);
tcg_gen_xori_tl(t1, t1, ~uimm);
tcg_gen_xori_tl(t2, t0, uimm);
tcg_gen_and_tl(t1, t1, t2);
tcg_temp_free(t2);
tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1);
tcg_temp_free(t1);
/* operands of same sign, result different sign */
generate_exception(ctx, EXCP_OVERFLOW);
gen_set_label(l1);
tcg_temp_free(r_tmp1);
gen_store_gpr(t0, rt);
tcg_temp_free(t0);
}
opn = "daddi";
break;
case OPC_DADDIU:
tcg_gen_addi_tl(t0, t0, uimm);
if (rs != 0) {
tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
} else {
tcg_gen_movi_tl(cpu_gpr[rt], uimm);
}
opn = "daddiu";
break;
#endif
case OPC_SLTI:
gen_op_lti(t0, t0, uimm);
opn = "slti";
break;
case OPC_SLTIU:
gen_op_ltiu(t0, t0, uimm);
opn = "sltiu";
break;
}
MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
}
/* Logic with immediate operand */
static void gen_logic_imm (CPUState *env, uint32_t opc, int rt, int rs, int16_t imm)
{
target_ulong uimm;
const char *opn = "imm logic";
if (rt == 0) {
/* If no destination, treat it as a NOP. */
MIPS_DEBUG("NOP");
return;
}
uimm = (uint16_t)imm;
switch (opc) {
case OPC_ANDI:
tcg_gen_andi_tl(t0, t0, uimm);
if (likely(rs != 0))
tcg_gen_andi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
else
tcg_gen_movi_tl(cpu_gpr[rt], 0);
opn = "andi";
break;
case OPC_ORI:
tcg_gen_ori_tl(t0, t0, uimm);
if (rs != 0)
tcg_gen_ori_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
else
tcg_gen_movi_tl(cpu_gpr[rt], uimm);
opn = "ori";
break;
case OPC_XORI:
tcg_gen_xori_tl(t0, t0, uimm);
if (likely(rs != 0))
tcg_gen_xori_tl(cpu_gpr[rt], cpu_gpr[rs], uimm);
else
tcg_gen_movi_tl(cpu_gpr[rt], uimm);
opn = "xori";
break;
case OPC_LUI:
tcg_gen_movi_tl(cpu_gpr[rt], imm << 16);
opn = "lui";
break;
}
MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
}
/* Set on less than with immediate operand */
static void gen_slt_imm (CPUState *env, uint32_t opc, int rt, int rs, int16_t imm)
{
target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */
const char *opn = "imm arith";
TCGv t0;
if (rt == 0) {
/* If no destination, treat it as a NOP. */
MIPS_DEBUG("NOP");
return;
}
t0 = tcg_temp_new();
gen_load_gpr(t0, rs);
switch (opc) {
case OPC_SLTI:
gen_op_lti(cpu_gpr[rt], t0, uimm);
opn = "slti";
break;
case OPC_SLTIU:
gen_op_ltiu(cpu_gpr[rt], t0, uimm);
opn = "sltiu";
break;
}
MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
tcg_temp_free(t0);
}
/* Shifts with immediate operand */
static void gen_shift_imm(CPUState *env, DisasContext *ctx, uint32_t opc,
int rt, int rs, int16_t imm)
{
target_ulong uimm = ((uint16_t)imm) & 0x1f;
const char *opn = "imm shift";
TCGv t0;
if (rt == 0) {
/* If no destination, treat it as a NOP. */
MIPS_DEBUG("NOP");
return;
}
t0 = tcg_temp_new();
gen_load_gpr(t0, rs);
switch (opc) {
case OPC_SLL:
tcg_gen_shli_tl(t0, t0, uimm);
tcg_gen_ext32s_tl(t0, t0);
tcg_gen_ext32s_tl(cpu_gpr[rt], t0);
opn = "sll";
break;
case OPC_SRA:
tcg_gen_ext32s_tl(t0, t0);
tcg_gen_sari_tl(t0, t0, uimm);
tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm);
opn = "sra";
break;
case OPC_SRL:
@ -1338,9 +1384,9 @@ static void gen_arith_imm (CPUState *env, DisasContext *ctx, uint32_t opc,
case 0:
if (uimm != 0) {
tcg_gen_ext32u_tl(t0, t0);
tcg_gen_shri_tl(t0, t0, uimm);
tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm);
} else {
tcg_gen_ext32s_tl(t0, t0);
tcg_gen_ext32s_tl(cpu_gpr[rt], t0);
}
opn = "srl";
break;
@ -1352,16 +1398,16 @@ static void gen_arith_imm (CPUState *env, DisasContext *ctx, uint32_t opc,
tcg_gen_trunc_tl_i32(r_tmp1, t0);
tcg_gen_rotri_i32(r_tmp1, r_tmp1, uimm);
tcg_gen_ext_i32_tl(t0, r_tmp1);
tcg_gen_ext_i32_tl(cpu_gpr[rt], r_tmp1);
tcg_temp_free_i32(r_tmp1);
}
opn = "rotr";
} else {
if (uimm != 0) {
tcg_gen_ext32u_tl(t0, t0);
tcg_gen_shri_tl(t0, t0, uimm);
tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm);
} else {
tcg_gen_ext32s_tl(t0, t0);
tcg_gen_ext32s_tl(cpu_gpr[rt], t0);
}
opn = "srl";
}
@ -1374,28 +1420,28 @@ static void gen_arith_imm (CPUState *env, DisasContext *ctx, uint32_t opc,
break;
#if defined(TARGET_MIPS64)
case OPC_DSLL:
tcg_gen_shli_tl(t0, t0, uimm);
tcg_gen_shli_tl(cpu_gpr[rt], t0, uimm);
opn = "dsll";
break;
case OPC_DSRA:
tcg_gen_sari_tl(t0, t0, uimm);
tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm);
opn = "dsra";
break;
case OPC_DSRL:
switch ((ctx->opcode >> 21) & 0x1f) {
case 0:
tcg_gen_shri_tl(t0, t0, uimm);
tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm);
opn = "dsrl";
break;
case 1:
/* drotr is decoded as dsrl on non-R2 CPUs */
if (env->insn_flags & ISA_MIPS32R2) {
if (uimm != 0) {
tcg_gen_rotri_tl(t0, t0, uimm);
tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm);
}
opn = "drotr";
} else {
tcg_gen_shri_tl(t0, t0, uimm);
tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm);
opn = "dsrl";
}
break;
@ -1406,26 +1452,26 @@ static void gen_arith_imm (CPUState *env, DisasContext *ctx, uint32_t opc,
}
break;
case OPC_DSLL32:
tcg_gen_shli_tl(t0, t0, uimm + 32);
tcg_gen_shli_tl(cpu_gpr[rt], t0, uimm + 32);
opn = "dsll32";
break;
case OPC_DSRA32:
tcg_gen_sari_tl(t0, t0, uimm + 32);
tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm + 32);
opn = "dsra32";
break;
case OPC_DSRL32:
switch ((ctx->opcode >> 21) & 0x1f) {
case 0:
tcg_gen_shri_tl(t0, t0, uimm + 32);
tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm + 32);
opn = "dsrl32";
break;
case 1:
/* drotr32 is decoded as dsrl32 on non-R2 CPUs */
if (env->insn_flags & ISA_MIPS32R2) {
tcg_gen_rotri_tl(t0, t0, uimm + 32);
tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm + 32);
opn = "drotr32";
} else {
tcg_gen_shri_tl(t0, t0, uimm + 32);
tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm + 32);
opn = "dsrl32";
}
break;
@ -1436,14 +1482,8 @@ static void gen_arith_imm (CPUState *env, DisasContext *ctx, uint32_t opc,
}
break;
#endif
default:
MIPS_INVAL(opn);
generate_exception(ctx, EXCP_RI);
goto out;
}
gen_store_gpr(t0, rt);
MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm);
out:
tcg_temp_free(t0);
}
@ -7556,9 +7596,10 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
case OPC_SPECIAL:
op1 = MASK_SPECIAL(ctx->opcode);
switch (op1) {
case OPC_SLL: /* Arithmetic with immediate */
case OPC_SRL ... OPC_SRA:
gen_arith_imm(env, ctx, op1, rd, rt, sa);
case OPC_SLL: /* Shift with immediate */
case OPC_SRA:
case OPC_SRL:
gen_shift_imm(env, ctx, op1, rd, rt, sa);
break;
case OPC_MOVN: /* Conditional move */
case OPC_MOVZ:
@ -7648,12 +7689,14 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
#if defined(TARGET_MIPS64)
/* MIPS64 specific opcodes */
case OPC_DSLL:
case OPC_DSRL ... OPC_DSRA:
case OPC_DSRA:
case OPC_DSRL:
case OPC_DSLL32:
case OPC_DSRL32 ... OPC_DSRA32:
case OPC_DSRA32:
case OPC_DSRL32:
check_insn(env, ctx, ISA_MIPS3);
check_mips_64(ctx);
gen_arith_imm(env, ctx, op1, rd, rt, sa);
gen_shift_imm(env, ctx, op1, rd, rt, sa);
break;
case OPC_DADD ... OPC_DSUBU:
check_insn(env, ctx, ISA_MIPS3);
@ -7928,9 +7971,20 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
break;
}
break;
case OPC_ADDI ... OPC_LUI: /* Arithmetic with immediate opcode */
case OPC_ADDI: /* Arithmetic with immediate opcode */
case OPC_ADDIU:
gen_arith_imm(env, ctx, op, rt, rs, imm);
break;
case OPC_SLTI: /* Set on less than with immediate opcode */
case OPC_SLTIU:
gen_slt_imm(env, op, rt, rs, imm);
break;
case OPC_ANDI: /* Arithmetic with immediate opcode */
case OPC_LUI:
case OPC_ORI:
case OPC_XORI:
gen_logic_imm(env, op, rt, rs, imm);
break;
case OPC_J ... OPC_JAL: /* Jump */
offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2;
gen_compute_branch(ctx, op, rs, rt, offset);
@ -8080,7 +8134,8 @@ static void decode_opc (CPUState *env, DisasContext *ctx)
check_mips_64(ctx);
gen_ldst(ctx, op, rt, rs, imm);
break;
case OPC_DADDI ... OPC_DADDIU:
case OPC_DADDI:
case OPC_DADDIU:
check_insn(env, ctx, ISA_MIPS3);
check_mips_64(ctx);
gen_arith_imm(env, ctx, op, rt, rs, imm);