target-mips: add MSA branch instructions

add MSA branch instructions

Reviewed-by: James Hogan <james.hogan@imgtec.com>
Signed-off-by: Yongbok Kim <yongbok.kim@imgtec.com>
Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
This commit is contained in:
Yongbok Kim 2014-11-01 05:28:42 +00:00 committed by Leon Alrae
parent 42daa9bed4
commit 5692c6e1f8
1 changed files with 220 additions and 114 deletions

View File

@ -17247,6 +17247,93 @@ static inline int check_msa_access(DisasContext *ctx)
return 1; return 1;
} }
static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt)
{
/* generates tcg ops to check if any element is 0 */
/* Note this function only works with MSA_WRLEN = 128 */
uint64_t eval_zero_or_big = 0;
uint64_t eval_big = 0;
TCGv_i64 t0 = tcg_temp_new_i64();
TCGv_i64 t1 = tcg_temp_new_i64();
switch (df) {
case DF_BYTE:
eval_zero_or_big = 0x0101010101010101ULL;
eval_big = 0x8080808080808080ULL;
break;
case DF_HALF:
eval_zero_or_big = 0x0001000100010001ULL;
eval_big = 0x8000800080008000ULL;
break;
case DF_WORD:
eval_zero_or_big = 0x0000000100000001ULL;
eval_big = 0x8000000080000000ULL;
break;
case DF_DOUBLE:
eval_zero_or_big = 0x0000000000000001ULL;
eval_big = 0x8000000000000000ULL;
break;
}
tcg_gen_subi_i64(t0, msa_wr_d[wt<<1], eval_zero_or_big);
tcg_gen_andc_i64(t0, t0, msa_wr_d[wt<<1]);
tcg_gen_andi_i64(t0, t0, eval_big);
tcg_gen_subi_i64(t1, msa_wr_d[(wt<<1)+1], eval_zero_or_big);
tcg_gen_andc_i64(t1, t1, msa_wr_d[(wt<<1)+1]);
tcg_gen_andi_i64(t1, t1, eval_big);
tcg_gen_or_i64(t0, t0, t1);
/* if all bits are zero then all elements are not zero */
/* if some bit is non-zero then some element is zero */
tcg_gen_setcondi_i64(TCG_COND_NE, t0, t0, 0);
tcg_gen_trunc_i64_tl(tresult, t0);
tcg_temp_free_i64(t0);
tcg_temp_free_i64(t1);
}
static void gen_msa_branch(CPUMIPSState *env, DisasContext *ctx, uint32_t op1)
{
uint8_t df = (ctx->opcode >> 21) & 0x3;
uint8_t wt = (ctx->opcode >> 16) & 0x1f;
int64_t s16 = (int16_t)ctx->opcode;
check_msa_access(ctx);
if (ctx->insn_flags & ISA_MIPS32R6 && ctx->hflags & MIPS_HFLAG_BMASK) {
MIPS_DEBUG("CTI in delay / forbidden slot");
generate_exception(ctx, EXCP_RI);
return;
}
switch (op1) {
case OPC_BZ_V:
case OPC_BNZ_V:
{
TCGv_i64 t0 = tcg_temp_new_i64();
tcg_gen_or_i64(t0, msa_wr_d[wt<<1], msa_wr_d[(wt<<1)+1]);
tcg_gen_setcondi_i64((op1 == OPC_BZ_V) ?
TCG_COND_EQ : TCG_COND_NE, t0, t0, 0);
tcg_gen_trunc_i64_tl(bcond, t0);
tcg_temp_free_i64(t0);
}
break;
case OPC_BZ_B:
case OPC_BZ_H:
case OPC_BZ_W:
case OPC_BZ_D:
gen_check_zero_element(bcond, df, wt);
break;
case OPC_BNZ_B:
case OPC_BNZ_H:
case OPC_BNZ_W:
case OPC_BNZ_D:
gen_check_zero_element(bcond, df, wt);
tcg_gen_setcondi_tl(TCG_COND_EQ, bcond, bcond, 0);
break;
}
ctx->btarget = ctx->pc + (s16 << 2) + 4;
ctx->hflags |= MIPS_HFLAG_BC;
ctx->hflags |= MIPS_HFLAG_BDS32;
}
static void decode_opc (CPUMIPSState *env, DisasContext *ctx) static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
{ {
int32_t offset; int32_t offset;
@ -17568,133 +17655,152 @@ static void decode_opc (CPUMIPSState *env, DisasContext *ctx)
break; break;
case OPC_CP1: case OPC_CP1:
if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { op1 = MASK_CP1(ctx->opcode);
switch (op1) {
case OPC_MFHC1:
case OPC_MTHC1:
check_cp1_enabled(ctx); check_cp1_enabled(ctx);
op1 = MASK_CP1(ctx->opcode); check_insn(ctx, ISA_MIPS32R2);
switch (op1) { case OPC_MFC1:
case OPC_MFHC1: case OPC_CFC1:
case OPC_MTHC1: case OPC_MTC1:
check_insn(ctx, ISA_MIPS32R2); case OPC_CTC1:
case OPC_MFC1: check_cp1_enabled(ctx);
case OPC_CFC1: gen_cp1(ctx, op1, rt, rd);
case OPC_MTC1: break;
case OPC_CTC1:
gen_cp1(ctx, op1, rt, rd);
break;
#if defined(TARGET_MIPS64) #if defined(TARGET_MIPS64)
case OPC_DMFC1: case OPC_DMFC1:
case OPC_DMTC1: case OPC_DMTC1:
check_insn(ctx, ISA_MIPS3); check_cp1_enabled(ctx);
gen_cp1(ctx, op1, rt, rd); check_insn(ctx, ISA_MIPS3);
break; gen_cp1(ctx, op1, rt, rd);
break;
#endif #endif
case OPC_BC1EQZ: /* OPC_BC1ANY2 */ case OPC_BC1EQZ: /* OPC_BC1ANY2 */
if (ctx->insn_flags & ISA_MIPS32R6) { check_cp1_enabled(ctx);
/* OPC_BC1EQZ */ if (ctx->insn_flags & ISA_MIPS32R6) {
gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), /* OPC_BC1EQZ */
rt, imm << 2);
} else {
/* OPC_BC1ANY2 */
check_cop1x(ctx);
check_insn(ctx, ASE_MIPS3D);
gen_compute_branch1(ctx, MASK_BC1(ctx->opcode),
(rt >> 2) & 0x7, imm << 2);
}
break;
case OPC_BC1NEZ:
check_insn(ctx, ISA_MIPS32R6);
gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode),
rt, imm << 2); rt, imm << 2);
break; } else {
case OPC_BC1ANY4: /* OPC_BC1ANY2 */
check_insn_opc_removed(ctx, ISA_MIPS32R6);
check_cop1x(ctx); check_cop1x(ctx);
check_insn(ctx, ASE_MIPS3D); check_insn(ctx, ASE_MIPS3D);
/* fall through */
case OPC_BC1:
check_insn_opc_removed(ctx, ISA_MIPS32R6);
gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), gen_compute_branch1(ctx, MASK_BC1(ctx->opcode),
(rt >> 2) & 0x7, imm << 2); (rt >> 2) & 0x7, imm << 2);
break; }
case OPC_PS_FMT: break;
check_insn_opc_removed(ctx, ISA_MIPS32R6); case OPC_BC1NEZ:
case OPC_S_FMT: check_cp1_enabled(ctx);
case OPC_D_FMT: check_insn(ctx, ISA_MIPS32R6);
gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode),
(imm >> 8) & 0x7); rt, imm << 2);
break; break;
case OPC_W_FMT: case OPC_BC1ANY4:
case OPC_L_FMT: check_cp1_enabled(ctx);
{ check_insn_opc_removed(ctx, ISA_MIPS32R6);
int r6_op = ctx->opcode & FOP(0x3f, 0x1f); check_cop1x(ctx);
if (ctx->insn_flags & ISA_MIPS32R6) { check_insn(ctx, ASE_MIPS3D);
switch (r6_op) { /* fall through */
case R6_OPC_CMP_AF_S: case OPC_BC1:
case R6_OPC_CMP_UN_S: check_cp1_enabled(ctx);
case R6_OPC_CMP_EQ_S: check_insn_opc_removed(ctx, ISA_MIPS32R6);
case R6_OPC_CMP_UEQ_S: gen_compute_branch1(ctx, MASK_BC1(ctx->opcode),
case R6_OPC_CMP_LT_S: (rt >> 2) & 0x7, imm << 2);
case R6_OPC_CMP_ULT_S: break;
case R6_OPC_CMP_LE_S: case OPC_PS_FMT:
case R6_OPC_CMP_ULE_S: check_cp1_enabled(ctx);
case R6_OPC_CMP_SAF_S: check_insn_opc_removed(ctx, ISA_MIPS32R6);
case R6_OPC_CMP_SUN_S: case OPC_S_FMT:
case R6_OPC_CMP_SEQ_S: case OPC_D_FMT:
case R6_OPC_CMP_SEUQ_S: check_cp1_enabled(ctx);
case R6_OPC_CMP_SLT_S: gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa,
case R6_OPC_CMP_SULT_S: (imm >> 8) & 0x7);
case R6_OPC_CMP_SLE_S: break;
case R6_OPC_CMP_SULE_S: case OPC_W_FMT:
case R6_OPC_CMP_OR_S: case OPC_L_FMT:
case R6_OPC_CMP_UNE_S: {
case R6_OPC_CMP_NE_S: int r6_op = ctx->opcode & FOP(0x3f, 0x1f);
case R6_OPC_CMP_SOR_S: check_cp1_enabled(ctx);
case R6_OPC_CMP_SUNE_S: if (ctx->insn_flags & ISA_MIPS32R6) {
case R6_OPC_CMP_SNE_S: switch (r6_op) {
gen_r6_cmp_s(ctx, ctx->opcode & 0x1f, rt, rd, sa); case R6_OPC_CMP_AF_S:
break; case R6_OPC_CMP_UN_S:
case R6_OPC_CMP_AF_D: case R6_OPC_CMP_EQ_S:
case R6_OPC_CMP_UN_D: case R6_OPC_CMP_UEQ_S:
case R6_OPC_CMP_EQ_D: case R6_OPC_CMP_LT_S:
case R6_OPC_CMP_UEQ_D: case R6_OPC_CMP_ULT_S:
case R6_OPC_CMP_LT_D: case R6_OPC_CMP_LE_S:
case R6_OPC_CMP_ULT_D: case R6_OPC_CMP_ULE_S:
case R6_OPC_CMP_LE_D: case R6_OPC_CMP_SAF_S:
case R6_OPC_CMP_ULE_D: case R6_OPC_CMP_SUN_S:
case R6_OPC_CMP_SAF_D: case R6_OPC_CMP_SEQ_S:
case R6_OPC_CMP_SUN_D: case R6_OPC_CMP_SEUQ_S:
case R6_OPC_CMP_SEQ_D: case R6_OPC_CMP_SLT_S:
case R6_OPC_CMP_SEUQ_D: case R6_OPC_CMP_SULT_S:
case R6_OPC_CMP_SLT_D: case R6_OPC_CMP_SLE_S:
case R6_OPC_CMP_SULT_D: case R6_OPC_CMP_SULE_S:
case R6_OPC_CMP_SLE_D: case R6_OPC_CMP_OR_S:
case R6_OPC_CMP_SULE_D: case R6_OPC_CMP_UNE_S:
case R6_OPC_CMP_OR_D: case R6_OPC_CMP_NE_S:
case R6_OPC_CMP_UNE_D: case R6_OPC_CMP_SOR_S:
case R6_OPC_CMP_NE_D: case R6_OPC_CMP_SUNE_S:
case R6_OPC_CMP_SOR_D: case R6_OPC_CMP_SNE_S:
case R6_OPC_CMP_SUNE_D: gen_r6_cmp_s(ctx, ctx->opcode & 0x1f, rt, rd, sa);
case R6_OPC_CMP_SNE_D: break;
gen_r6_cmp_d(ctx, ctx->opcode & 0x1f, rt, rd, sa); case R6_OPC_CMP_AF_D:
break; case R6_OPC_CMP_UN_D:
default: case R6_OPC_CMP_EQ_D:
gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, case R6_OPC_CMP_UEQ_D:
(imm >> 8) & 0x7); case R6_OPC_CMP_LT_D:
break; case R6_OPC_CMP_ULT_D:
} case R6_OPC_CMP_LE_D:
} else { case R6_OPC_CMP_ULE_D:
case R6_OPC_CMP_SAF_D:
case R6_OPC_CMP_SUN_D:
case R6_OPC_CMP_SEQ_D:
case R6_OPC_CMP_SEUQ_D:
case R6_OPC_CMP_SLT_D:
case R6_OPC_CMP_SULT_D:
case R6_OPC_CMP_SLE_D:
case R6_OPC_CMP_SULE_D:
case R6_OPC_CMP_OR_D:
case R6_OPC_CMP_UNE_D:
case R6_OPC_CMP_NE_D:
case R6_OPC_CMP_SOR_D:
case R6_OPC_CMP_SUNE_D:
case R6_OPC_CMP_SNE_D:
gen_r6_cmp_d(ctx, ctx->opcode & 0x1f, rt, rd, sa);
break;
default:
gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa,
(imm >> 8) & 0x7); (imm >> 8) & 0x7);
break;
} }
break; } else {
gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa,
(imm >> 8) & 0x7);
} }
default: break;
MIPS_INVAL("cp1"); }
generate_exception (ctx, EXCP_RI); case OPC_BZ_V:
break; case OPC_BNZ_V:
} case OPC_BZ_B:
} else { case OPC_BZ_H:
generate_exception_err(ctx, EXCP_CpU, 1); case OPC_BZ_W:
case OPC_BZ_D:
case OPC_BNZ_B:
case OPC_BNZ_H:
case OPC_BNZ_W:
case OPC_BNZ_D:
check_insn(ctx, ASE_MSA);
gen_msa_branch(env, ctx, op1);
break;
default:
MIPS_INVAL("cp1");
generate_exception(ctx, EXCP_RI);
break;
} }
break; break;