Patchwork [RFC] target-mips: add loongson 2E & 2F integer instructions

login
register
mail settings
Submitter Aurelien Jarno
Date July 1, 2010, 10:13 p.m.
Message ID <1278022422-6786-1-git-send-email-aurelien@aurel32.net>
Download mbox | patch
Permalink /patch/57601/
State New
Headers show

Comments

Aurelien Jarno - July 1, 2010, 10:13 p.m.
This patch adds support for loongson 2E & 2F instructions. They are the
same instructions, but differ by the opcode encoding.

This patch has still a few problems (hence the RFC), but is enough to
boot a fulong 2E kernel built with the -mloongson2e flag:
- I am unable to understand the difference between DMULT.G and DMULTU.G
  instructions. As they are both a 64x64 => 64 multiplication, the
  signedness should not make any difference. They are implemented the
  same way in this patch.
- Division by 0 is not supposed to trigger any arithmetic exception,
  however the manual doesn't explain which result is return in such
  condition. With this patch, QEMU will crash with a SIGFPE in sich
  a situation.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 target-mips/translate.c |  143 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 143 insertions(+), 0 deletions(-)

Patch

diff --git a/target-mips/translate.c b/target-mips/translate.c
index 0ab23d3..91882b7 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -267,6 +267,19 @@  enum {
     OPC_MUL      = 0x02 | OPC_SPECIAL2,
     OPC_MSUB     = 0x04 | OPC_SPECIAL2,
     OPC_MSUBU    = 0x05 | OPC_SPECIAL2,
+    /* Loongson 2F */
+    OPC_MULT_G_2F   = 0x10 | OPC_SPECIAL2,
+    OPC_DMULT_G_2F  = 0x11 | OPC_SPECIAL2,
+    OPC_MULTU_G_2F  = 0x12 | OPC_SPECIAL2,
+    OPC_DMULTU_G_2F = 0x13 | OPC_SPECIAL2,
+    OPC_DIV_G_2F    = 0x14 | OPC_SPECIAL2,
+    OPC_DDIV_G_2F   = 0x15 | OPC_SPECIAL2,
+    OPC_DIVU_G_2F   = 0x16 | OPC_SPECIAL2,
+    OPC_DDIVU_G_2F  = 0x17 | OPC_SPECIAL2,
+    OPC_MOD_G_2F    = 0x1c | OPC_SPECIAL2,
+    OPC_DMOD_G_2F   = 0x1d | OPC_SPECIAL2,
+    OPC_MODU_G_2F   = 0x1e | OPC_SPECIAL2,
+    OPC_DMODU_G_2F  = 0x1f | OPC_SPECIAL2,
     /* Misc */
     OPC_CLZ      = 0x20 | OPC_SPECIAL2,
     OPC_CLO      = 0x21 | OPC_SPECIAL2,
@@ -293,6 +306,20 @@  enum {
     OPC_BSHFL    = 0x20 | OPC_SPECIAL3,
     OPC_DBSHFL   = 0x24 | OPC_SPECIAL3,
     OPC_RDHWR    = 0x3B | OPC_SPECIAL3,
+
+    /* Loongson 2E */
+    OPC_MULT_G_2E   = 0x18 | OPC_SPECIAL3,
+    OPC_MULTU_G_2E  = 0x19 | OPC_SPECIAL3,
+    OPC_DIV_G_2E    = 0x1A | OPC_SPECIAL3,
+    OPC_DIVU_G_2E   = 0x1B | OPC_SPECIAL3,
+    OPC_DMULT_G_2E  = 0x1C | OPC_SPECIAL3,
+    OPC_DMULTU_G_2E = 0x1D | OPC_SPECIAL3,
+    OPC_DDIV_G_2E   = 0x1E | OPC_SPECIAL3,
+    OPC_DDIVU_G_2E  = 0x1F | OPC_SPECIAL3,
+    OPC_MOD_G_2E    = 0x22 | OPC_SPECIAL3,
+    OPC_MODU_G_2E   = 0x23 | OPC_SPECIAL3,
+    OPC_DMOD_G_2E   = 0x26 | OPC_SPECIAL3,
+    OPC_DMODU_G_2E  = 0x27 | OPC_SPECIAL3,
 };
 
 /* BSHFL opcodes */
@@ -2325,6 +2352,110 @@  static void gen_cl (DisasContext *ctx, uint32_t opc,
     tcg_temp_free(t0);
 }
 
+#if defined(TARGET_MIPS64)
+/* Godson integer instructions */
+static void gen_loongson_integer (DisasContext *ctx, uint32_t opc,
+                                int rd, int rs, int rt)
+{
+    const char *opn = "loongson";
+    TCGv t0, t1;
+
+    if (rd == 0) {
+        /* Treat as NOP. */
+        MIPS_DEBUG("NOP");
+        return;
+    }
+
+    t0 = tcg_temp_new();
+    t1 = tcg_temp_new();
+
+    gen_load_gpr(t0, rs);
+    gen_load_gpr(t1, rt);
+    switch (opc) {
+    case OPC_MULT_G_2E:
+    case OPC_MULT_G_2F:
+        tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
+        tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+        opn = "mult.g";
+        break;
+    case OPC_MULTU_G_2E:
+    case OPC_MULTU_G_2F:
+        tcg_gen_ext32u_tl(t0, t0);
+        tcg_gen_ext32u_tl(t1, t1);
+        tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
+        tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+        opn = "multu.g";
+        break;
+    case OPC_DMULT_G_2E:
+    case OPC_DMULT_G_2F:
+        tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
+        opn = "dmult.g";
+        break;
+    case OPC_DMULTU_G_2E:
+    case OPC_DMULTU_G_2F:
+        tcg_gen_mul_tl(cpu_gpr[rd], t0, t1);
+        opn = "dmultu.g";
+        break;
+    case OPC_DIV_G_2E:
+    case OPC_DIV_G_2F:
+        tcg_gen_ext32s_tl(t0, t0);
+        tcg_gen_ext32s_tl(t1, t1);
+        tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
+        tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+        opn = "div.g";
+        break;
+    case OPC_DIVU_G_2E:
+    case OPC_DIVU_G_2F:
+        tcg_gen_ext32u_tl(t0, t0);
+        tcg_gen_ext32u_tl(t1, t1);
+        tcg_gen_divu_tl(cpu_gpr[rd], t0, t1);
+        tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+        opn = "divu.g";
+        break;
+    case OPC_DDIV_G_2E:
+    case OPC_DDIV_G_2F:
+        tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
+        opn = "ddiv.g";
+        break;
+    case OPC_DDIVU_G_2E:
+    case OPC_DDIVU_G_2F:
+        tcg_gen_div_tl(cpu_gpr[rd], t0, t1);
+        opn = "ddivu.g";
+        break;
+    case OPC_MOD_G_2E:
+    case OPC_MOD_G_2F:
+        tcg_gen_ext32s_tl(t0, t0);
+        tcg_gen_ext32s_tl(t1, t1);
+        tcg_gen_rem_tl(cpu_gpr[rd], t0, t1);
+        tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+        opn = "mod.g";
+        break;
+    case OPC_MODU_G_2E:
+    case OPC_MODU_G_2F:
+        tcg_gen_ext32u_tl(t0, t0);
+        tcg_gen_ext32u_tl(t1, t1);
+        tcg_gen_remu_tl(cpu_gpr[rd], t0, t1);
+        tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]);
+        opn = "modu.g";
+        break;
+    case OPC_DMOD_G_2E:
+    case OPC_DMOD_G_2F:
+        tcg_gen_rem_tl(cpu_gpr[rd], t0, t1);
+        opn = "dmod.g";
+        break;
+    case OPC_DMODU_G_2E:
+    case OPC_DMODU_G_2F:
+        tcg_gen_remu_tl(cpu_gpr[rd], t0, t1);
+        opn = "dmodu.g";
+        break;
+    }
+
+    MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]);
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+}
+#endif
+
 /* Traps */
 static void gen_trap (DisasContext *ctx, uint32_t opc,
                       int rs, int rt, int16_t imm)
@@ -11603,6 +11734,12 @@  static void decode_opc (CPUState *env, DisasContext *ctx, int *is_branch)
             check_mips_64(ctx);
             gen_cl(ctx, op1, rd, rs);
             break;
+        case OPC_MULT_G_2F ... OPC_DDIVU_G_2F:
+        case OPC_MOD_G_2F ... OPC_DMOD_G_2F:
+        case OPC_MODU_G_2F ... OPC_DMODU_G_2F:
+            check_insn(env, ctx, INSN_LOONGSON2F);
+            gen_loongson_integer(ctx, op1, rd, rs, rt);
+            break;
 #endif
         default:            /* Invalid */
             MIPS_INVAL("special2");
@@ -11664,6 +11801,12 @@  static void decode_opc (CPUState *env, DisasContext *ctx, int *is_branch)
             op2 = MASK_DBSHFL(ctx->opcode);
             gen_bshfl(ctx, op2, rt, rd);
             break;
+        case OPC_MULT_G_2E ... OPC_DDIVU_G_2E:
+        case OPC_MOD_G_2E ... OPC_MODU_G_2E:
+        case OPC_DMOD_G_2E ... OPC_DMODU_G_2E:
+            check_insn(env, ctx, INSN_LOONGSON2E);
+            gen_loongson_integer(ctx, op1, rd, rs, rt);
+            break;
 #endif
         default:            /* Invalid */
             MIPS_INVAL("special3");