target/mips: Add loongson-ext lsdc2 group of instructions

LDC2/SDC2 opcodes have been rewritten as "load & store with offset"
group of instructions by loongson-ext ASE.

This patch add implementation of these instructions:

  gslbx: load 1 bytes to GPR
  gslhx: load 2 bytes to GPR
  gslwx: load 4 bytes to GPR
  gsldx: load 8 bytes to GPR
  gslwxc1: load 4 bytes to FPR
  gsldxc1: load 8 bytes to FPR
  gssbx: store 1 bytes from GPR
  gsshx: store 2 bytes from GPR
  gsswx: store 4 bytes from GPR
  gssdx: store 8 bytes from GPR
  gsswxc1: store 4 bytes from FPR
  gssdxc1: store 8 bytes from FPR

Details of Loongson-EXT is here:
https://github.com/FlyGoat/loongson-insn/blob/master/loongson-ext.md

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Signed-off-by: Huacai Chen <chenhc@lemote.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Message-Id: <1602831120-3377-5-git-send-email-chenhc@lemote.com>
This commit is contained in:
Jiaxun Yang 2020-10-16 14:51:56 +08:00 committed by Philippe Mathieu-Daudé
parent fd723105c1
commit 90e22a57af

View file

@ -484,6 +484,24 @@ enum {
OPC_GSSDRC1 = 0x7 | OPC_GSSHFS,
};
/* Loongson EXT LDC2/SDC2 opcodes */
#define MASK_LOONGSON_LSDC2(op) (MASK_OP_MAJOR(op) | (op & 0x7))
enum {
OPC_GSLBX = 0x0 | OPC_LDC2,
OPC_GSLHX = 0x1 | OPC_LDC2,
OPC_GSLWX = 0x2 | OPC_LDC2,
OPC_GSLDX = 0x3 | OPC_LDC2,
OPC_GSLWXC1 = 0x6 | OPC_LDC2,
OPC_GSLDXC1 = 0x7 | OPC_LDC2,
OPC_GSSBX = 0x0 | OPC_SDC2,
OPC_GSSHX = 0x1 | OPC_SDC2,
OPC_GSSWX = 0x2 | OPC_SDC2,
OPC_GSSDX = 0x3 | OPC_SDC2,
OPC_GSSWXC1 = 0x6 | OPC_SDC2,
OPC_GSSDXC1 = 0x7 | OPC_SDC2,
};
/* BSHFL opcodes */
#define MASK_BSHFL(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6)))
@ -6172,6 +6190,165 @@ static void gen_loongson_lswc2(DisasContext *ctx, int rt,
tcg_temp_free(t0);
}
/* Loongson EXT LDC2/SDC2 */
static void gen_loongson_lsdc2(DisasContext *ctx, int rt,
int rs, int rd)
{
int offset = sextract32(ctx->opcode, 3, 8);
uint32_t opc = MASK_LOONGSON_LSDC2(ctx->opcode);
TCGv t0, t1;
TCGv_i32 fp0;
/* Pre-conditions */
switch (opc) {
case OPC_GSLBX:
case OPC_GSLHX:
case OPC_GSLWX:
case OPC_GSLDX:
/* prefetch, implement as NOP */
if (rt == 0) {
return;
}
break;
case OPC_GSSBX:
case OPC_GSSHX:
case OPC_GSSWX:
case OPC_GSSDX:
break;
case OPC_GSLWXC1:
#if defined(TARGET_MIPS64)
case OPC_GSLDXC1:
#endif
check_cp1_enabled(ctx);
/* prefetch, implement as NOP */
if (rt == 0) {
return;
}
break;
case OPC_GSSWXC1:
#if defined(TARGET_MIPS64)
case OPC_GSSDXC1:
#endif
check_cp1_enabled(ctx);
break;
default:
MIPS_INVAL("loongson_lsdc2");
generate_exception_end(ctx, EXCP_RI);
return;
break;
}
t0 = tcg_temp_new();
gen_base_offset_addr(ctx, t0, rs, offset);
gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
switch (opc) {
case OPC_GSLBX:
tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_SB);
gen_store_gpr(t0, rt);
break;
case OPC_GSLHX:
tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW |
ctx->default_tcg_memop_mask);
gen_store_gpr(t0, rt);
break;
case OPC_GSLWX:
gen_base_offset_addr(ctx, t0, rs, offset);
if (rd) {
gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
}
tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL |
ctx->default_tcg_memop_mask);
gen_store_gpr(t0, rt);
break;
#if defined(TARGET_MIPS64)
case OPC_GSLDX:
gen_base_offset_addr(ctx, t0, rs, offset);
if (rd) {
gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
}
tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ |
ctx->default_tcg_memop_mask);
gen_store_gpr(t0, rt);
break;
#endif
case OPC_GSLWXC1:
check_cp1_enabled(ctx);
gen_base_offset_addr(ctx, t0, rs, offset);
if (rd) {
gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
}
fp0 = tcg_temp_new_i32();
tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL |
ctx->default_tcg_memop_mask);
gen_store_fpr32(ctx, fp0, rt);
tcg_temp_free_i32(fp0);
break;
#if defined(TARGET_MIPS64)
case OPC_GSLDXC1:
check_cp1_enabled(ctx);
gen_base_offset_addr(ctx, t0, rs, offset);
if (rd) {
gen_op_addr_add(ctx, t0, cpu_gpr[rd], t0);
}
tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ |
ctx->default_tcg_memop_mask);
gen_store_fpr64(ctx, t0, rt);
break;
#endif
case OPC_GSSBX:
t1 = tcg_temp_new();
gen_load_gpr(t1, rt);
tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_SB);
tcg_temp_free(t1);
break;
case OPC_GSSHX:
t1 = tcg_temp_new();
gen_load_gpr(t1, rt);
tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW |
ctx->default_tcg_memop_mask);
tcg_temp_free(t1);
break;
case OPC_GSSWX:
t1 = tcg_temp_new();
gen_load_gpr(t1, rt);
tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL |
ctx->default_tcg_memop_mask);
tcg_temp_free(t1);
break;
#if defined(TARGET_MIPS64)
case OPC_GSSDX:
t1 = tcg_temp_new();
gen_load_gpr(t1, rt);
tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ |
ctx->default_tcg_memop_mask);
tcg_temp_free(t1);
break;
#endif
case OPC_GSSWXC1:
fp0 = tcg_temp_new_i32();
gen_load_fpr32(ctx, fp0, rt);
tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL |
ctx->default_tcg_memop_mask);
tcg_temp_free_i32(fp0);
break;
#if defined(TARGET_MIPS64)
case OPC_GSSDXC1:
t1 = tcg_temp_new();
gen_load_fpr64(ctx, t1, rt);
tcg_gen_qemu_st_i64(t1, t0, ctx->mem_idx, MO_TEQ |
ctx->default_tcg_memop_mask);
tcg_temp_free(t1);
break;
#endif
default:
break;
}
tcg_temp_free(t0);
}
/* Traps */
static void gen_trap(DisasContext *ctx, uint32_t opc,
int rs, int rt, int16_t imm)
@ -31055,6 +31232,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx)
/* OPC_JIC, OPC_JIALC */
gen_compute_compact_branch(ctx, op, 0, rt, imm);
}
} else if (ctx->insn_flags & ASE_LEXT) {
gen_loongson_lsdc2(ctx, rt, rs, rd);
} else {
/* OPC_LWC2, OPC_SWC2 */
/* COP2: Not implemented. */