qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 5/5] tcg/mips: use direct jumps
  2009-10-22 20:17 [Qemu-devel] [PATCH 0/5] MIPS(EL) host support Aurelien Jarno
@ 2009-10-22 19:31 ` Aurelien Jarno
  2009-10-22 20:17 ` [Qemu-devel] [PATCH 2/5] cpu-all.h: fix cpu_get_real_ticks on mips host Aurelien Jarno
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Aurelien Jarno @ 2009-10-22 19:31 UTC (permalink / raw)
  To: qemu-devel; +Cc: Arnaud Patard

Use direct jumps on MIPS hosts. The jump address has to be written
atomically, so a R_MIPS_26 needs to be used. It means the code
generation buffer should not be bigger than 256MB, and has to be
aligned on a 256MB boundary.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 exec-all.h            |   13 ++++++++++++-
 exec.c                |    7 +++++++
 tcg/mips/tcg-target.c |    3 ++-
 tcg/mips/tcg-target.h |    5 -----
 4 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/exec-all.h b/exec-all.h
index 820b59e..21d6c91 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -114,7 +114,7 @@ static inline int tlb_set_page(CPUState *env1, target_ulong vaddr,
 #define CODE_GEN_AVG_BLOCK_SIZE 64
 #endif
 
-#if defined(_ARCH_PPC) || defined(__x86_64__) || defined(__arm__) || defined(__i386__)
+#if defined(_ARCH_PPC) || defined(__x86_64__) || defined(__arm__) || defined(__i386__) || defined(__mips__)
 #define USE_DIRECT_JUMP
 #endif
 
@@ -222,6 +222,17 @@ static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr
     __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
 #endif
 }
+#elif defined(__mips__)
+static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
+{
+    *(uint32_t *)jmp_addr = 0x08000000 | ((addr >> 2) & 0x03ffffff);
+#if QEMU_GNUC_PREREQ(4, 1)
+    void __clear_cache(char *beg, char *end);
+    __clear_cache((char *) jmp_addr, (char *) jmp_addr + 4);
+#else
+# error __clear_cache not available
+#endif
+}
 #endif
 
 static inline void tb_set_jmp_target(TranslationBlock *tb,
diff --git a/exec.c b/exec.c
index 076d26b..1d3f48c 100644
--- a/exec.c
+++ b/exec.c
@@ -454,6 +454,13 @@ static void code_gen_alloc(unsigned long tb_size)
         start = (void *) 0x01000000UL;
         if (code_gen_buffer_size > 16 * 1024 * 1024)
             code_gen_buffer_size = 16 * 1024 * 1024;
+#elif defined(__mips__)
+        /* Map the buffer aligned on a 256M boundary, so we can use direct
+           calls and branches (R_MIPS_26 relocation) */
+        flags |= MAP_FIXED;
+        start = (void *) 0x30000000UL;
+        if (code_gen_buffer_size > 256 * 1024 * 1024)
+            code_gen_buffer_size = 256 * 1024 * 1024;
 #endif
         code_gen_buffer = mmap(start, code_gen_buffer_size,
                                PROT_WRITE | PROT_READ | PROT_EXEC,
diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c
index 03fdcbf..d445169 100644
--- a/tcg/mips/tcg-target.c
+++ b/tcg/mips/tcg-target.c
@@ -1042,7 +1042,8 @@ static inline void tcg_out_op(TCGContext *s, int opc,
     case INDEX_op_goto_tb:
         if (s->tb_jmp_offset) {
             /* direct jump method */
-            tcg_abort();
+            s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+            s->code_ptr += 4;
         } else {
             /* indirect jump method */
             tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, (tcg_target_long)(s->tb_next + args[0]));
diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h
index ba91623..3d36100 100644
--- a/tcg/mips/tcg-target.h
+++ b/tcg/mips/tcg-target.h
@@ -98,10 +98,5 @@ enum {
 
 static inline void flush_icache_range(unsigned long start, unsigned long stop)
 {
-#if QEMU_GNUC_PREREQ(4, 1)
-    void __clear_cache(char *beg, char *end);
     __clear_cache((char *) start, (char *) stop);
-#else
-# error __clear_cache not available
-#endif
 }
-- 
1.6.1.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 4/5] tcg: increase TCG_MAX_OP_SIZE to 192
  2009-10-22 20:17 [Qemu-devel] [PATCH 0/5] MIPS(EL) host support Aurelien Jarno
                   ` (3 preceding siblings ...)
  2009-10-22 20:17 ` [Qemu-devel] [PATCH 3/5] tcg: initial mips support Aurelien Jarno
@ 2009-10-22 20:17 ` Aurelien Jarno
  4 siblings, 0 replies; 8+ messages in thread
From: Aurelien Jarno @ 2009-10-22 20:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: Arnaud Patard

This is needed on a MIPS host and a 64-bit cross-endian target.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 exec-all.h |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/exec-all.h b/exec-all.h
index dd134a9..820b59e 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -41,10 +41,10 @@ typedef struct TranslationBlock TranslationBlock;
 #define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR)
 
 /* Maximum size a TCG op can expand to.  This is complicated because a
-   single op may require several host instructions and regirster reloads.
-   For now take a wild guess at 128 bytes, which should allow at least
+   single op may require several host instructions and register reloads.
+   For now take a wild guess at 192 bytes, which should allow at least
    a couple of fixup instructions per argument.  */
-#define TCG_MAX_OP_SIZE 128
+#define TCG_MAX_OP_SIZE 192
 
 #define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * MAX_OPC_PARAM)
 
-- 
1.6.1.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 0/5] MIPS(EL) host support
@ 2009-10-22 20:17 Aurelien Jarno
  2009-10-22 19:31 ` [Qemu-devel] [PATCH 5/5] tcg/mips: use direct jumps Aurelien Jarno
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Aurelien Jarno @ 2009-10-22 20:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: Arnaud Patard

This patch series add host MIPS(EL) support to QEMU. It is based on the
excellent work from Arnaud Patard [1].

It supports both little and big-endian hosts, and is currently limited
to the o32 abi. It has been mainly developped and tested in system mode 
on a MIPSEL host, but I have verified that both MIPS and MIPSEL hosts 
can boot Debian Installer amd64, arm, i386, mips, mipsel, ppc and sparc,
and can also boot an SH4 kernel. On the user side, I have verified that
basic support (aka starting libc.so.6) works, but there is probably some
more work to do on this side.

I have mainly focused on getting host support fully working, and haven't
written "aggressively" optimised code. There is still space for speed 
improvement, for example by filling the delay slots or using MIPS R2 
instructions (if available) for byte swapping, rotations or bit field 
insertion/extraction (useful for the memory load/store).

[1] http://git.rtp-net.org/?p=qemu.git;a=summary

Arnaud Patard (2):
  linux-user: remove hardcoded value of _NSIG in signal.c
  cpu-all.h: fix cpu_get_real_ticks on mips host

Aurelien Jarno (3):
  tcg: initial mips support
  tcg: increase TCG_MAX_OP_SIZE to 192
  tcg/mips: use direct jumps

 configure             |    7 +-
 cpu-all.h             |   27 +-
 exec-all.h            |   19 +-
 exec.c                |    7 +
 linux-user/signal.c   |   12 +-
 tcg/mips/tcg-target.c | 1464 +++++++++++++++++++++++++++++++++++++++++++++++++
 tcg/mips/tcg-target.h |  102 ++++
 7 files changed, 1618 insertions(+), 20 deletions(-)
 create mode 100644 tcg/mips/tcg-target.c
 create mode 100644 tcg/mips/tcg-target.h

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 3/5] tcg: initial mips support
  2009-10-22 20:17 [Qemu-devel] [PATCH 0/5] MIPS(EL) host support Aurelien Jarno
                   ` (2 preceding siblings ...)
  2009-10-22 20:17 ` [Qemu-devel] [PATCH 1/5] linux-user: remove hardcoded value of _NSIG in signal.c Aurelien Jarno
@ 2009-10-22 20:17 ` Aurelien Jarno
  2009-10-22 20:17 ` [Qemu-devel] [PATCH 4/5] tcg: increase TCG_MAX_OP_SIZE to 192 Aurelien Jarno
  4 siblings, 0 replies; 8+ messages in thread
From: Aurelien Jarno @ 2009-10-22 20:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: Arnaud Patard

Based on a patch from Arnaud Patard (Rtp) <arnaud.patard@rtp-net.org>

A few words about design choices:
* Two registers, at and t0, are reserved for TCG internal use. They are
  useful for bswap and 64-bit ops.
* Most ops supports a constant argument with value 0, which is actually
  mapped to the zero register.
* While the at register is available for constant loading, ops only
  support a limited range of constants. TCG does a better job doing the
  register allocation and constant loading by itself. There are plenty of
  registers available anyway.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 configure             |    7 +-
 tcg/mips/tcg-target.c | 1463 +++++++++++++++++++++++++++++++++++++++++++++++++
 tcg/mips/tcg-target.h |  107 ++++
 3 files changed, 1576 insertions(+), 1 deletions(-)
 create mode 100644 tcg/mips/tcg-target.c
 create mode 100644 tcg/mips/tcg-target.h

diff --git a/configure b/configure
index 43d87c5..e70b7ce 100755
--- a/configure
+++ b/configure
@@ -133,13 +133,15 @@ elif check_define _ARCH_PPC ; then
   else
     cpu="ppc"
   fi
+elif check_define __mips__ ; then
+  cpu="mips"
 else
   cpu=`uname -m`
 fi
 
 target_list=""
 case "$cpu" in
-  alpha|cris|ia64|m68k|microblaze|mips|mips64|ppc|ppc64|sparc64)
+  alpha|cris|ia64|m68k|microblaze|ppc|ppc64|sparc64)
     cpu="$cpu"
   ;;
   i386|i486|i586|i686|i86pc|BePC)
@@ -157,6 +159,9 @@ case "$cpu" in
   parisc|parisc64)
     cpu="hppa"
   ;;
+  mips*)
+    cpu="mips"
+  ;;
   s390*)
     cpu="s390"
   ;;
diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c
new file mode 100644
index 0000000..03fdcbf
--- /dev/null
+++ b/tcg/mips/tcg-target.c
@@ -0,0 +1,1463 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008-2009 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright (c) 2009 Aurelien Jarno <aurelien@aurel32.net>
+ * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#if defined(TCG_TARGET_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+# define TCG_NEED_BSWAP 0
+#else
+# define TCG_NEED_BSWAP 1
+#endif
+
+#ifndef NDEBUG
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+    "zero",
+    "at",
+    "v0",
+    "v1",
+    "a0",
+    "a1",
+    "a2",
+    "a3",
+    "t0",
+    "t1",
+    "t2",
+    "t3",
+    "t4",
+    "t5",
+    "t6",
+    "t7",
+    "s0",
+    "s1",
+    "s2",
+    "s3",
+    "s4",
+    "s5",
+    "s6",
+    "s7",
+    "t8",
+    "t9",
+    "k0",
+    "k1",
+    "gp",
+    "sp",
+    "fp",
+    "ra",
+};
+#endif
+
+/* check if we really need so many registers :P */
+static const int tcg_target_reg_alloc_order[] = {
+    TCG_REG_S0,
+    TCG_REG_S1,
+    TCG_REG_S2,
+    TCG_REG_S3,
+    TCG_REG_S4,
+    TCG_REG_S5,
+    TCG_REG_S6,
+    TCG_REG_S7,
+    TCG_REG_T1,
+    TCG_REG_T2,
+    TCG_REG_T3,
+    TCG_REG_T4,
+    TCG_REG_T5,
+    TCG_REG_T6,
+    TCG_REG_T7,
+    TCG_REG_T8,
+    TCG_REG_T9,
+    TCG_REG_A0,
+    TCG_REG_A1,
+    TCG_REG_A2,
+    TCG_REG_A3,
+    TCG_REG_V0,
+    TCG_REG_V1
+};
+
+static const int tcg_target_call_iarg_regs[4] = {
+    TCG_REG_A0,
+    TCG_REG_A1,
+    TCG_REG_A2,
+    TCG_REG_A3
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+    TCG_REG_V0,
+    TCG_REG_V1
+};
+
+static uint8_t *tb_ret_addr;
+
+static inline uint32_t reloc_lo16_val (void *pc, tcg_target_long target)
+{
+    return target & 0xffff;
+}
+
+static inline void reloc_lo16 (void *pc, tcg_target_long target)
+{
+    *(uint32_t *) pc = (*(uint32_t *) pc & ~0xffff)
+                       | reloc_lo16_val(pc, target);
+}
+
+static inline uint32_t reloc_hi16_val (void *pc, tcg_target_long target)
+{
+    return (target >> 16) & 0xffff;
+}
+
+static inline void reloc_hi16 (void *pc, tcg_target_long target)
+{
+    *(uint32_t *) pc = (*(uint32_t *) pc & ~0xffff)
+                       | reloc_hi16_val(pc, target);
+}
+
+static inline uint32_t reloc_pc16_val (void *pc, tcg_target_long target)
+{
+    int32_t disp;
+
+    disp = target - (tcg_target_long) pc - 4;
+    if (disp != (disp << 14) >> 14) {
+        tcg_abort ();
+    }
+
+    return (disp >> 2) & 0xffff;
+}
+
+static inline void reloc_pc16 (void *pc, tcg_target_long target)
+{
+    *(uint32_t *) pc = (*(uint32_t *) pc & ~0xffff)
+                       | reloc_pc16_val(pc, target);
+}
+
+static inline uint32_t reloc_26_val (void *pc, tcg_target_long target)
+{
+    if ((((tcg_target_long)pc + 4) & 0xf0000000) != (target & 0xf0000000)) {
+        tcg_abort ();
+    }
+
+    return (target >> 2) & 0x3ffffff;
+}
+
+static inline void reloc_pc26 (void *pc, tcg_target_long target)
+{
+    *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3ffffff)
+                       | reloc_26_val(pc, target);
+}
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+                        tcg_target_long value, tcg_target_long addend)
+{
+    value += addend;
+    switch(type) {
+    case R_MIPS_LO16:
+        reloc_lo16(code_ptr, value);
+        break;
+    case R_MIPS_HI16:
+        reloc_hi16(code_ptr, value);
+        break;
+    case R_MIPS_PC16:
+        reloc_pc16(code_ptr, value);
+        break;
+    case R_MIPS_26:
+        reloc_pc26(code_ptr, value);
+        break;
+    default:
+        tcg_abort();
+    }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+    return 4;
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+    const char *ct_str;
+
+    ct_str = *pct_str;
+    switch(ct_str[0]) {
+    case 'r':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        break;
+    case 'L': /* qemu_ld output arg constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_V0);
+        break;
+    case 'l': /* qemu_ld input arg constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+#if defined(CONFIG_SOFTMMU)
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0);
+#endif
+        break;
+    case 'S': /* qemu_st constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+#if defined(CONFIG_SOFTMMU)
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0);
+# if TARGET_LONG_BITS == 64
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_A1);
+# endif
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_A2);
+#endif
+        break;
+    case 'I':
+        ct->ct |= TCG_CT_CONST_U16;
+        break;
+    case 'J':
+        ct->ct |= TCG_CT_CONST_S16;
+        break;
+    case 'Z':
+        /* We are cheating a bit here, using the fact that the register
+           ZERO is also the register number 0. Hence there is no need
+           to check for const_args in each instruction. */
+        ct->ct |= TCG_CT_CONST_ZERO;
+        break;
+    default:
+        return -1;
+    }
+    ct_str++;
+    *pct_str = ct_str;
+    return 0;
+}
+
+/* test if a constant matches the constraint */
+static inline int tcg_target_const_match(tcg_target_long val,
+                                         const TCGArgConstraint *arg_ct)
+{
+    int ct;
+    ct = arg_ct->ct;
+    if (ct & TCG_CT_CONST)
+        return 1;
+    else if ((ct & TCG_CT_CONST_ZERO) && val == 0)
+        return 1;
+    else if ((ct & TCG_CT_CONST_U16) && val == (uint16_t)val)
+        return 1;
+    else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val)
+        return 1;
+    else
+        return 0;
+}
+
+/*
+ * Type reg
+ */
+static inline void tcg_out_opc_reg(TCGContext *s, int opc, int sa, int funct, int rd, int rs, int rt)
+{
+    int32_t inst;
+
+    inst = (opc & 0x3F) << 26;
+    inst |= (rs & 0x1F) << 21;
+    inst |= (rt & 0x1F) << 16;
+    inst |= (rd & 0x1F) << 11;
+    inst |= (sa & 0x1F) <<  6;
+    inst |= (funct & 0x3F);
+    tcg_out32(s, inst);
+}
+
+/*
+ * Type immediate
+ */
+static inline void tcg_out_opc_imm(TCGContext *s, int opc, int rt, int rs, int imm)
+{
+    int32_t inst;
+
+    inst = (opc & 0x3F) << 26;
+    inst |= (rs & 0x1F) << 21;
+    inst |= (rt & 0x1F) << 16;
+    inst |= (imm & 0xffff);
+    tcg_out32(s, inst);
+}
+
+/*
+ * Type shift immediate
+ */
+static inline void tcg_out_shift_imm(TCGContext *s, int funct, int rd, int rt, int sa)
+{
+    tcg_out_opc_reg(s, 0, sa, funct, rd, 0, rt);
+}
+
+static inline void tcg_out_nop(TCGContext *s)
+{
+    tcg_out32(s, 0);
+}
+
+static inline void tcg_out_mov(TCGContext *s, int ret, int arg)
+{
+    /* addu ret, arg, 0 */
+    tcg_out_opc_reg(s, 0, 0, 33, ret, arg, TCG_REG_ZERO);
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type,
+                                int reg, int32_t arg)
+{
+    if (arg == (int16_t)arg) {
+        /* addiu reg, zero, arg */
+        tcg_out_opc_imm(s, 9, reg, TCG_REG_ZERO, arg);
+    } else if (arg == (uint16_t)arg) {
+        /* ori reg, zero, arg */
+        tcg_out_opc_imm(s, 13, reg, TCG_REG_ZERO, arg);
+    } else {
+        /* lui reg, arg >> 16 */
+        tcg_out_opc_imm(s, 15, reg, 0, arg >> 16);
+        /* ori reg, reg, arg & 0xffff */
+        tcg_out_opc_imm(s, 13, reg, reg, arg & 0xffff);
+    }
+}
+
+static inline void tcg_out_bswap16(TCGContext *s, int ret, int arg)
+{
+    /* ret and arg can't be register at */
+    if (ret == TCG_REG_AT || arg == TCG_REG_AT) {
+        tcg_abort();
+    }
+
+    /* shr at, arg, 8 */
+    tcg_out_shift_imm(s, 2, TCG_REG_AT, arg, 8);
+    /* andi at, at, 0x00ff */
+    tcg_out_opc_imm(s, 12, TCG_REG_AT, TCG_REG_AT, 0x00ff);
+    /* shl ret, arg, 8 */
+    tcg_out_shift_imm(s, 0, ret, arg, 8);
+    /* andi ret, ret, 0xff00 */
+    tcg_out_opc_imm(s, 12, ret, ret, 0xff00);
+    /* or ret, ret, at */
+    tcg_out_opc_reg(s, 0, 0, 37, ret, ret, TCG_REG_AT);
+}
+
+static inline void tcg_out_bswap16s(TCGContext *s, int ret, int arg)
+{
+    /* ret and arg can't be register at */
+    if (ret == TCG_REG_AT || arg == TCG_REG_AT) {
+        tcg_abort();
+    }
+
+    /* srl at, arg, 8 */
+    tcg_out_shift_imm(s, 2, TCG_REG_AT, arg, 8);
+    /* andi at, at, 0xff */
+    tcg_out_opc_imm(s, 12, TCG_REG_AT, TCG_REG_AT, 0xff);
+
+    /* sll ret, arg, 24 */
+    tcg_out_shift_imm(s, 0, ret, arg, 24);
+    /* sra ret, ret, 16 */
+    tcg_out_shift_imm(s, 3, ret, ret, 16);
+    /* or ret, ret, at */
+    tcg_out_opc_reg(s, 0, 0, 37, ret, ret, TCG_REG_AT);
+}
+
+static inline void tcg_out_bswap32(TCGContext *s, int ret, int arg)
+{
+    /* ret and arg must be different and can't be register at */
+    if (ret == arg || ret == TCG_REG_AT || arg == TCG_REG_AT) {
+        tcg_abort();
+    }
+
+    /* sll ret, arg, 24 */
+    tcg_out_opc_reg(s, 0, 24, 0, ret, 0, arg);
+
+    /* srl at, arg, 8 */
+    tcg_out_shift_imm(s, 2, TCG_REG_AT, arg, 24);
+    /* or ret, ret, at */
+    tcg_out_opc_reg(s, 0, 0, 37, ret, ret, TCG_REG_AT);
+
+    /* andi at, arg, 0xff00 */
+    tcg_out_opc_imm(s, 12, TCG_REG_AT, arg, 0xff00);
+    /* sll at, at, 8 */
+    tcg_out_shift_imm(s, 0, TCG_REG_AT, TCG_REG_AT, 8);
+    /* or ret, ret, at */
+    tcg_out_opc_reg(s, 0, 0, 37, ret, ret, TCG_REG_AT);
+
+    /* srl at, arg, 8 */
+    tcg_out_shift_imm(s, 2, TCG_REG_AT, arg, 8);
+    /* andi at, at, 0xff00 */
+    tcg_out_opc_imm(s, 12, TCG_REG_AT, TCG_REG_AT, 0xff00);
+    /* or ret, ret, at */
+    tcg_out_opc_reg(s, 0, 0, 37, ret, ret, TCG_REG_AT);
+}
+
+static inline void tcg_out_ldst(TCGContext *s, int opc, int arg,
+                              int arg1, tcg_target_long arg2)
+{
+    if (arg2 == (int16_t) arg2) {
+        /* ldst arg, arg2(arg1) */
+        tcg_out_opc_imm(s, opc, arg, arg1, arg2);
+    } else {
+        /* movi at, arg2 */
+        tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, arg2);
+        /* addu at, at, arg1 */
+        tcg_out_opc_reg(s, 0, 0, 33, TCG_REG_AT, TCG_REG_AT, arg1);
+        /* ldst arg, arg2(arg1) */
+        tcg_out_opc_imm(s, opc, arg, TCG_REG_AT, 0);
+    }
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int arg,
+                              int arg1, tcg_target_long arg2)
+{
+    tcg_out_ldst(s, 35, arg, arg1, arg2);
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
+                              int arg1, tcg_target_long arg2)
+{
+    tcg_out_ldst(s, 43, arg, arg1, arg2);
+}
+
+static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+    if (val == (int16_t)val) {
+        /* addiu reg, reg, val */
+        tcg_out_opc_imm(s, 9, reg, reg, val);
+    } else {
+        tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, val);
+        /* addu reg, reg, at */
+        tcg_out_opc_reg(s, 0, 0, 33, reg, reg, TCG_REG_AT);
+    }
+}
+
+static void tcg_out_brcond(TCGContext *s, int cond, int arg1,
+                           int arg2, int label_index)
+{
+    TCGLabel *l = &s->labels[label_index];
+
+    switch (cond) {
+    case TCG_COND_EQ:
+        /* beq arg1, arg2, 0 */
+        tcg_out_opc_imm(s, 4, arg2, arg1, 0);
+        break;
+    case TCG_COND_NE:
+        /* bne arg1, arg2, 0 */
+        tcg_out_opc_imm(s, 5, arg2, arg1, 0);
+        break;
+    case TCG_COND_LT:
+        /* slt at, arg1, arg2 */
+        tcg_out_opc_reg(s, 0, 0, 42, TCG_REG_AT, arg1, arg2);
+        /* bne at, zero, 0 */
+        tcg_out_opc_imm(s, 5, TCG_REG_ZERO, TCG_REG_AT, 0);
+        break;
+    case TCG_COND_LTU:
+        /* sltu at, arg1, arg2 */
+        tcg_out_opc_reg(s, 0, 0, 43, TCG_REG_AT, arg1, arg2);
+        /* bne at, zero, 0 */
+        tcg_out_opc_imm(s, 5, TCG_REG_ZERO, TCG_REG_AT, 0);
+        break;
+    case TCG_COND_GE:
+        /* slt at, arg1, arg2 */
+        tcg_out_opc_reg(s, 0, 0, 42, TCG_REG_AT, arg1, arg2);
+        /* beq at, zero, 0 */
+        tcg_out_opc_imm(s, 4, TCG_REG_ZERO, TCG_REG_AT, 0);
+        break;
+    case TCG_COND_GEU:
+        /* sltu at, arg1, arg2 */
+        tcg_out_opc_reg(s, 0, 0, 43, TCG_REG_AT, arg1, arg2);
+        /* beq at, zero, 0 */
+        tcg_out_opc_imm(s, 4, TCG_REG_ZERO, TCG_REG_AT, 0);
+        break;
+    case TCG_COND_LE:
+        /* slt at, arg2, arg1 */
+        tcg_out_opc_reg(s, 0, 0, 42, TCG_REG_AT, arg2, arg1);
+        /* beq at, zero, 0 */
+        tcg_out_opc_imm(s, 4, TCG_REG_ZERO, TCG_REG_AT, 0);
+        break;
+    case TCG_COND_LEU:
+        /* sltu at, arg2, arg1 */
+        tcg_out_opc_reg(s, 0, 0, 43, TCG_REG_AT, arg2, arg1);
+        /* beq at, zero, 0 */
+        tcg_out_opc_imm(s, 4, TCG_REG_ZERO, TCG_REG_AT, 0);
+        break;
+    case TCG_COND_GT:
+        /* slt at, arg2, arg1 */
+        tcg_out_opc_reg(s, 0, 0, 42, TCG_REG_AT, arg2, arg1);
+        /* bne at, zero, 0 */
+        tcg_out_opc_imm(s, 5, TCG_REG_ZERO, TCG_REG_AT, 0);
+        break;
+    case TCG_COND_GTU:
+        /* sltu at, arg2, arg1 */
+        tcg_out_opc_reg(s, 0, 0, 43, TCG_REG_AT, arg2, arg1);
+        /* bne at, zero, 0 */
+        tcg_out_opc_imm(s, 5, TCG_REG_ZERO, TCG_REG_AT, 0);
+        break;
+    default:
+        tcg_abort();
+        break;
+    }
+    if (l->has_value) {
+        reloc_pc16(s->code_ptr - 4, l->u.value);
+    } else {
+        tcg_out_reloc(s, s->code_ptr - 4, R_MIPS_PC16, label_index, 0);
+    }
+    tcg_out_nop(s);
+}
+
+/* XXX: we implement it at the target level to avoid having to
+   handle cross basic blocks temporaries */
+static void tcg_out_brcond2(TCGContext *s, int cond, int arg1,
+                            int arg2, int arg3, int arg4, int label_index)
+{
+    void *label_ptr;
+
+    switch(cond) {
+    case TCG_COND_NE:
+        tcg_out_brcond(s, TCG_COND_NE, arg2, arg4, label_index);
+        tcg_out_brcond(s, TCG_COND_NE, arg1, arg3, label_index);
+        return;
+    case TCG_COND_EQ:
+        break;
+    case TCG_COND_LT:
+    case TCG_COND_LE:
+        tcg_out_brcond(s, TCG_COND_LT, arg2, arg4, label_index);
+        break;
+    case TCG_COND_GT:
+    case TCG_COND_GE:
+        tcg_out_brcond(s, TCG_COND_GT, arg2, arg4, label_index);
+        break;
+    case TCG_COND_LTU:
+    case TCG_COND_LEU:
+        tcg_out_brcond(s, TCG_COND_LTU, arg2, arg4, label_index);
+        break;
+    case TCG_COND_GTU:
+    case TCG_COND_GEU:
+        tcg_out_brcond(s, TCG_COND_GTU, arg2, arg4, label_index);
+        break;
+    default:
+        tcg_abort();
+    }
+
+    /* bne arg2, arg4, 0 */
+    label_ptr = s->code_ptr;
+    tcg_out_opc_imm(s, 5, arg2, arg4, 0);
+    tcg_out_nop(s);
+
+    switch(cond) {
+    case TCG_COND_EQ:
+        tcg_out_brcond(s, TCG_COND_EQ, arg1, arg3, label_index);
+        break;
+    case TCG_COND_LT:
+    case TCG_COND_LTU:
+        tcg_out_brcond(s, TCG_COND_LTU, arg1, arg3, label_index);
+        break;
+    case TCG_COND_LE:
+    case TCG_COND_LEU:
+        tcg_out_brcond(s, TCG_COND_LEU, arg1, arg3, label_index);
+        break;
+    case TCG_COND_GT:
+    case TCG_COND_GTU:
+        tcg_out_brcond(s, TCG_COND_GTU, arg1, arg3, label_index);
+        break;
+    case TCG_COND_GE:
+    case TCG_COND_GEU:
+        tcg_out_brcond(s, TCG_COND_GEU, arg1, arg3, label_index);
+        break;
+    default:
+        tcg_abort();
+    }
+
+    reloc_pc16(label_ptr, (tcg_target_long) s->code_ptr);
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+    __ldb_mmu,
+    __ldw_mmu,
+    __ldl_mmu,
+    __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+    __stb_mmu,
+    __stw_mmu,
+    __stl_mmu,
+    __stq_mmu,
+};
+#endif
+
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
+                            int opc)
+{
+    int addr_regl, addr_reg1, addr_meml;
+    int data_regl, data_regh, data_reg1, data_reg2;
+    int mem_index, s_bits;
+#if defined(CONFIG_SOFTMMU)
+    void *label1_ptr, *label2_ptr;
+    int sp_args;
+#endif
+#if TARGET_LONG_BITS == 64
+# if defined(CONFIG_SOFTMMU)
+    uint8_t *label3_ptr;
+# endif
+    int addr_regh, addr_reg2, addr_memh;
+#endif
+    data_regl = *args++;
+    if (opc == 3)
+        data_regh = *args++;
+    else
+        data_regh = 0;
+    addr_regl = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_regh = *args++;
+#endif
+    mem_index = *args;
+    s_bits = opc & 3;
+
+    if (opc == 3) {
+#if defined(TCG_TARGET_WORDS_BIGENDIAN)
+        data_reg1 = data_regh;
+        data_reg2 = data_regl;
+#else
+        data_reg1 = data_regl;
+        data_reg2 = data_regh;
+#endif
+    } else {
+        data_reg1 = data_regl;
+        data_reg2 = 0;
+    }
+#if TARGET_LONG_BITS == 64
+# if defined(TCG_TARGET_WORDS_BIGENDIAN)
+    addr_reg1 = addr_regh;
+    addr_reg2 = addr_regl;
+    addr_memh = 0;
+    addr_meml = 4;
+# else
+    addr_reg1 = addr_regl;
+    addr_reg2 = addr_regh;
+    addr_memh = 4;
+    addr_meml = 0;
+# endif
+#else
+    addr_reg1 = addr_regl;
+    addr_meml = 0;
+#endif
+
+#if defined(CONFIG_SOFTMMU)
+    /* srl a0, addr_regl, $x */
+    tcg_out_shift_imm(s, 2, TCG_REG_A0, addr_regl, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
+    /* andi a0, a0, 0xff */
+    tcg_out_opc_imm(s, 12, TCG_REG_A0, TCG_REG_A0, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+    /* addu a0, a0, fp */
+    tcg_out_opc_reg(s, 0, 0, 33, TCG_REG_A0, TCG_REG_A0, TCG_AREG0);
+    /* lw at, $x(a0) */
+    tcg_out_opc_imm(s, 35, TCG_REG_AT, TCG_REG_A0,
+                    offsetof(CPUState, tlb_table[mem_index][0].addr_read) + addr_meml);
+    /* andi a1, addr_regl, $x */
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T0, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+    tcg_out_opc_reg(s, 0, 0, 36, TCG_REG_T0, TCG_REG_T0, addr_regl);
+
+# if TARGET_LONG_BITS == 64
+    /* bne a1, at, label3 */
+    label3_ptr = s->code_ptr;
+    tcg_out_opc_imm(s, 5, TCG_REG_T0, TCG_REG_AT, 0);
+    tcg_out_nop(s);
+
+    /* lw at, $x(a0) */
+    tcg_out_opc_imm(s, 35, TCG_REG_AT, TCG_REG_A0,
+                    offsetof(CPUState, tlb_table[mem_index][0].addr_read) + addr_memh);
+
+    /* beq addr_regh, at, label1 */
+    label1_ptr = s->code_ptr;
+    tcg_out_opc_imm(s, 4, addr_regh, TCG_REG_AT, 0);
+    tcg_out_nop(s);
+
+    reloc_pc16(label3_ptr, (tcg_target_long) s->code_ptr);
+# else
+    /* beq a1, at, label1 */
+    label1_ptr = s->code_ptr;
+    tcg_out_opc_imm(s, 4, TCG_REG_T0, TCG_REG_AT, 0);
+    tcg_out_nop(s);
+# endif
+
+    /* slow path */
+    sp_args = TCG_REG_A0;
+    tcg_out_mov(s, sp_args++, addr_reg1);
+# if TARGET_LONG_BITS == 64
+    tcg_out_mov(s, sp_args++, addr_reg2);
+# endif
+    tcg_out_movi(s, TCG_TYPE_I32, sp_args++, mem_index);
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_AT, (tcg_target_long)qemu_ld_helpers[s_bits]);
+    tcg_out_opc_reg(s, 0, 0, 9, TCG_REG_RA, TCG_REG_AT, 0);
+    tcg_out_nop(s);
+
+    switch(opc) {
+    case 0:
+        /* andi data_reg1, v0, 0xff */
+        tcg_out_opc_imm(s, 12, data_reg1, TCG_REG_V0, 0xff);
+        break;
+    case 0 | 4:
+        /* sll v0, v0, 24 */
+        tcg_out_shift_imm(s, 0, TCG_REG_V0, TCG_REG_V0, 24);
+        /* sra data_reg1, v0, 24 */
+        tcg_out_shift_imm(s, 3, data_reg1, TCG_REG_V0, 24);
+        break;
+    case 1:
+        /* andi data_reg1, v0, 0xffff */
+        tcg_out_opc_imm(s, 12, data_reg1, TCG_REG_V0, 0xffff);
+        break;
+    case 1 | 4:
+        /* sll v0, v0, 16 */
+        tcg_out_shift_imm(s, 0, TCG_REG_V0, TCG_REG_V0, 16);
+        /* sra data_reg1, v0, 16 */
+        tcg_out_shift_imm(s, 3, data_reg1, TCG_REG_V0, 16);
+        break;
+    case 2:
+        /* move data_reg1, v0 */
+        tcg_out_mov(s, data_reg1, TCG_REG_V0);
+        break;
+    case 3:
+        /* move data_reg2, v1 */
+        tcg_out_mov(s, data_reg2, TCG_REG_V1);
+        /* move data_reg1, v0 */
+        tcg_out_mov(s, data_reg1, TCG_REG_V0);
+        break;
+    default:
+        tcg_abort();
+    }
+
+    /* beq zero, zero, label2 */
+    label2_ptr = s->code_ptr;
+    tcg_out_opc_imm(s, 4, TCG_REG_ZERO, TCG_REG_ZERO, 0);
+    tcg_out_nop(s);
+
+    /* label1: fast path */
+    reloc_pc16(label1_ptr, (tcg_target_long) s->code_ptr);
+
+    /* lw v0, $x(a0) */
+    tcg_out_opc_imm(s, 35, TCG_REG_V0, TCG_REG_A0,
+                    offsetof(CPUState, tlb_table[mem_index][0].addend) + addr_meml);
+    /* addu v0, v0, addr_regl */
+    tcg_out_opc_reg(s, 0, 0, 33, TCG_REG_V0, TCG_REG_V0, addr_regl);
+
+    addr_reg1 = TCG_REG_V0;
+#endif
+
+    switch(opc) {
+    case 0:
+        /* lbu data_reg1, 0(addr_reg1) */
+        tcg_out_opc_imm(s, 36, data_reg1, addr_reg1, 0);
+        break;
+    case 0 | 4:
+        /* lb data_reg1, 0(addr_reg1) */
+        tcg_out_opc_imm(s, 32, data_reg1, addr_reg1, 0);
+        break;
+    case 1:
+        if (TCG_NEED_BSWAP) {
+            /* lhu t0, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 37, TCG_REG_T0, addr_reg1, 0);
+            /* bswap16 data_reg1, t0 */
+            tcg_out_bswap16(s, data_reg1, TCG_REG_T0);
+        } else {
+            /* lhu data_reg1, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 37, data_reg1, addr_reg1, 0);
+        }
+        break;
+    case 1 | 4:
+        if (TCG_NEED_BSWAP) {
+            /* lhu t0, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 37, TCG_REG_T0, addr_reg1, 0);
+            /* bswap16s data_reg1, t0 */
+            tcg_out_bswap16s(s, data_reg1, TCG_REG_T0);
+        } else {
+            /* lh data_reg1, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 33, data_reg1, addr_reg1, 0);
+        }
+        break;
+    case 2:
+        if (TCG_NEED_BSWAP) {
+            /* lw t0, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 35, TCG_REG_T0, addr_reg1, 0);
+            /* bswap32 data_reg1, t0 */
+            tcg_out_bswap32(s, data_reg1, TCG_REG_T0);
+        } else {
+            /* lw data_reg1, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 35, data_reg1, addr_reg1, 0);
+        }
+        break;
+    case 3:
+#if !defined(CONFIG_SOFTMMU)
+        tcg_out_mov(s, TCG_REG_V0, addr_reg1);
+        addr_reg1 = TCG_REG_V0;
+#endif
+        if (TCG_NEED_BSWAP) {
+            /* lw data_reg1, 4(a0) */
+            tcg_out_opc_imm(s, 35, TCG_REG_T0, addr_reg1, 4);
+            /* bswap32 data_reg1, a0 */
+            tcg_out_bswap32(s, data_reg1, TCG_REG_T0);
+            /* lw data_reg1, 0(a0) */
+            tcg_out_opc_imm(s, 35, TCG_REG_T0, addr_reg1, 0);
+            /* bswap32 data_reg2, a0 */
+            tcg_out_bswap32(s, data_reg2, TCG_REG_T0);
+        } else {
+            /* lw data_reg1, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 35, data_reg1, addr_reg1, 0);
+            /* lw data_reg2, 4(addr_reg1) */
+            tcg_out_opc_imm(s, 35, data_reg2, addr_reg1, 4);
+        }
+        break;
+    default:
+        tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    reloc_pc16(label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
+                            int opc)
+{
+    int addr_regl, addr_reg1, addr_meml;
+    int data_regl, data_regh, data_reg1, data_reg2;
+    int mem_index, s_bits;
+#if defined(CONFIG_SOFTMMU)
+    uint8_t *label1_ptr, *label2_ptr;
+    int sp_args;
+#endif
+#if TARGET_LONG_BITS == 64
+# if defined(CONFIG_SOFTMMU)
+    uint8_t *label3_ptr;
+# endif
+    int addr_regh, addr_reg2, addr_memh;
+#endif
+
+    data_regl = *args++;
+    if (opc == 3) {
+        data_regh = *args++;
+#if defined(TCG_TARGET_WORDS_BIGENDIAN)
+        data_reg1 = data_regh;
+        data_reg2 = data_regl;
+#else
+        data_reg1 = data_regl;
+        data_reg2 = data_regh;
+#endif
+    } else {
+        data_reg1 = data_regl;
+        data_reg2 = 0;
+        data_regh = 0;
+    }
+    addr_regl = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_regh = *args++;
+# if defined(TCG_TARGET_WORDS_BIGENDIAN)
+    addr_reg1 = addr_regh;
+    addr_reg2 = addr_regl;
+    addr_memh = 0;
+    addr_meml = 4;
+# else
+    addr_reg1 = addr_regl;
+    addr_reg2 = addr_regh;
+    addr_memh = 4;
+    addr_meml = 0;
+# endif
+#else
+    addr_reg1 = addr_regl;
+    addr_meml = 0;
+#endif
+    mem_index = *args;
+    s_bits = opc;
+
+#if defined(CONFIG_SOFTMMU)
+    /* srl a0, addr_regl, $x */
+    tcg_out_shift_imm(s, 2, TCG_REG_A0, addr_regl, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS);
+    /* andi a0, a0, 0xff */
+    tcg_out_opc_imm(s, 12, TCG_REG_A0, TCG_REG_A0, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+    /* addu a0, a0, fp */
+    tcg_out_opc_reg(s, 0, 0, 33, TCG_REG_A0, TCG_REG_A0, TCG_AREG0);
+    /* lw at, $x(a0) */
+    tcg_out_opc_imm(s, 35, TCG_REG_AT, TCG_REG_A0,
+                    offsetof(CPUState, tlb_table[mem_index][0].addr_write) + addr_meml);
+    /* andi t0, addr_regl, $x */
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T0, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+    tcg_out_opc_reg(s, 0, 0, 36, TCG_REG_T0, TCG_REG_T0, addr_regl);
+
+# if TARGET_LONG_BITS == 64
+    /* bne t0, at, label3 */
+    label3_ptr = s->code_ptr;
+    tcg_out_opc_imm(s, 5, TCG_REG_T0, TCG_REG_AT, 0);
+    tcg_out_nop(s);
+
+    /* lw at, $x(a0) */
+    tcg_out_opc_imm(s, 35, TCG_REG_AT, TCG_REG_A0,
+                    offsetof(CPUState, tlb_table[mem_index][0].addr_write) + addr_memh);
+
+    /* beq addr_regh, at, label1 */
+    label1_ptr = s->code_ptr;
+    tcg_out_opc_imm(s, 4, addr_regh, TCG_REG_AT, 0);
+    tcg_out_nop(s);
+
+    reloc_pc16(label3_ptr, (tcg_target_long) s->code_ptr);
+# else
+    /* beq t0, at, label1 */
+    label1_ptr = s->code_ptr;
+    tcg_out_opc_imm(s, 4, TCG_REG_T0, TCG_REG_AT, 0);
+    tcg_out_nop(s);
+# endif
+
+    /* slow path */
+    sp_args = TCG_REG_A0;
+    tcg_out_mov(s, sp_args++, addr_reg1);
+# if TARGET_LONG_BITS == 64
+    tcg_out_mov(s, sp_args++, addr_reg2);
+# endif
+    switch(opc) {
+    case 0:
+        /* andi aX, data_reg1, 0xff */
+        tcg_out_opc_imm(s, 12, sp_args++, data_reg1, 0xff);
+        break;
+    case 1:
+        /* andi aX, data_reg1, 0xffff */
+        tcg_out_opc_imm(s, 12, sp_args++, data_reg1, 0xffff);
+        break;
+    case 2:
+        /* move aX, data_reg1 */
+        tcg_out_mov(s, sp_args++, data_reg1);
+        break;
+    case 3:
+        sp_args = (sp_args + 1) & ~1;
+        /* move aX, data_reg1 */
+        tcg_out_mov(s, sp_args++, data_reg1);
+        /* move aX, data_reg2 */
+        tcg_out_mov(s, sp_args++, data_reg2);
+        break;
+    default:
+        tcg_abort();
+    }
+    if (sp_args > TCG_REG_A3) {
+        /* Push mem_index on the stack */
+        tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_AT, mem_index);
+        tcg_out_st(s, TCG_TYPE_I32, TCG_REG_AT, TCG_REG_SP, 16);
+    } else {
+        tcg_out_movi(s, TCG_TYPE_I32, sp_args, mem_index);
+    }
+
+    /* call qemu_st_helpers[s_bits] */
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_AT, (tcg_target_long)qemu_st_helpers[s_bits]);
+    tcg_out_opc_reg(s, 0, 0, 9, TCG_REG_RA, TCG_REG_AT, 0);
+    tcg_out_nop(s);
+
+    /* beq zero, zero, label2 */
+    label2_ptr = s->code_ptr;
+    tcg_out_opc_imm(s, 4, TCG_REG_ZERO, TCG_REG_ZERO, 0);
+    tcg_out_nop(s);
+
+    /* label1: fast path */
+    reloc_pc16(label1_ptr, (tcg_target_long) s->code_ptr);
+
+    /* lw a0, $x(a0) */
+    tcg_out_opc_imm(s, 35, TCG_REG_A0, TCG_REG_A0,
+                    offsetof(CPUState, tlb_table[mem_index][0].addend) + addr_meml);
+    /* addu a0, a0, addr_regl */
+    tcg_out_opc_reg(s, 0, 0, 33, TCG_REG_A0, TCG_REG_A0, addr_regl);
+
+    addr_reg1 = TCG_REG_A0;
+#endif
+
+    switch(opc) {
+    case 0:
+        /* sb data_reg1, 0(addr_reg1) */
+        tcg_out_opc_imm(s, 40, data_reg1, addr_reg1, 0);
+        break;
+    case 1:
+        if (TCG_NEED_BSWAP) {
+            /* bswap16 t0, data_reg1 */
+            tcg_out_bswap16(s, TCG_REG_T0, data_reg1);
+            /* sh t0, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 41, TCG_REG_T0, addr_reg1, 0);
+        } else {
+            /* sh data_reg1, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 41, data_reg1, addr_reg1, 0);
+        }
+        break;
+    case 2:
+        if (TCG_NEED_BSWAP) {
+            /* bswap32 t0, data_reg1 */
+            tcg_out_bswap32(s, TCG_REG_T0, data_reg1);
+            /* sw t0, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 43, TCG_REG_T0, addr_reg1, 0);
+        } else {
+            /* sw data_reg1, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 43, data_reg1, addr_reg1, 0);
+        }
+        break;
+    case 3:
+        if (TCG_NEED_BSWAP) {
+            /* bswap32 t0, data_reg2 */
+            tcg_out_bswap32(s, TCG_REG_T0, data_reg2);
+            /* sw t0, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 43, TCG_REG_T0, addr_reg1, 0);
+            /* bswap32 t0, data_reg1 */
+            tcg_out_bswap32(s, TCG_REG_T0, data_reg1);
+            /* sw t0, 4(addr_reg1) */
+            tcg_out_opc_imm(s, 43, TCG_REG_T0, addr_reg1, 4);
+        } else {
+            /* sw data_reg1, 0(addr_reg1) */
+            tcg_out_opc_imm(s, 43, data_reg1, addr_reg1, 0);
+            /* sw data_reg2, 4(addr_reg1) */
+            tcg_out_opc_imm(s, 43, data_reg2, addr_reg1, 4);
+        }
+        break;
+    default:
+        tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    reloc_pc16(label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static inline void tcg_out_op(TCGContext *s, int opc,
+                              const TCGArg *args, const int *const_args)
+{
+    switch(opc) {
+    case INDEX_op_exit_tb:
+        tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_V0, args[0]);
+        tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_AT, (tcg_target_long)tb_ret_addr);
+        /* jr at */
+        tcg_out_opc_reg(s, 0, 0, 8, 0, TCG_REG_AT, 0);
+        tcg_out_nop(s);
+        break;
+    case INDEX_op_goto_tb:
+        if (s->tb_jmp_offset) {
+            /* direct jump method */
+            tcg_abort();
+        } else {
+            /* indirect jump method */
+            tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_AT, (tcg_target_long)(s->tb_next + args[0]));
+            tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_AT, TCG_REG_AT, 0);
+            /* jr at */
+            tcg_out_opc_reg(s, 0, 0, 8, 0, TCG_REG_AT, 0);
+        }
+        tcg_out_nop(s);
+        s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+        break;
+    case INDEX_op_call:
+        /* jalr args[0] */
+        tcg_out_opc_reg(s, 0, 0, 9, TCG_REG_RA, args[0], 0);
+        tcg_out_nop(s);
+        break;
+    case INDEX_op_jmp:
+        /* jr args[0] */
+        tcg_out_opc_reg(s, 0, 0, 8, 0, args[0], 0);
+        tcg_out_nop(s);
+        break;
+    case INDEX_op_br:
+        tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, args[0]);
+        break;
+
+    case INDEX_op_mov_i32:
+        tcg_out_mov(s, args[0], args[1]);
+        break;
+    case INDEX_op_movi_i32:
+        tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]);
+        break;
+
+    case INDEX_op_ld8u_i32:
+        /* lbu args[0], args[2](args[1]) */
+	tcg_out_ldst(s, 36, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld8s_i32:
+        /* lb args[0], args[2](args[1]) */
+        tcg_out_ldst(s, 32, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16u_i32:
+        /* lhu args[0], args[2](args[1]) */
+        tcg_out_ldst(s, 37, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16s_i32:
+        /* lh args[0], args[2](args[1]) */
+        tcg_out_ldst(s, 33, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld_i32:
+        /* lw args[0], args[2](args[1]) */
+        tcg_out_ldst(s, 35, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st8_i32:
+        /* sb args[0], args[2](args[1]) */
+        tcg_out_ldst(s, 40, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st16_i32:
+        /* sh args[0], args[2](args[1]) */
+        tcg_out_ldst(s, 41, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st_i32:
+        /* sw args[0], args[2](args[1]) */
+        tcg_out_ldst(s, 43, args[0], args[1], args[2]);
+        break;
+
+    case INDEX_op_add_i32:
+        if (const_args[2]) {
+            /* addiu args[0], args[1], args[2] */
+            tcg_out_opc_imm(s, 9, args[0], args[1], args[2]);
+        } else {
+            /* addu args[0], args[1], args[2] */
+            tcg_out_opc_reg(s, 0, 0, 33, args[0], args[1], args[2]);
+        }
+        break;
+    case INDEX_op_add2_i32:
+        if (const_args[4]) {
+            /* addiu at, args[2], args[4] */
+            tcg_out_opc_imm(s, 9, TCG_REG_AT, args[2], args[4]);
+        } else {
+            /* addu at, args[2], args[4] */
+            tcg_out_opc_reg(s, 0, 0, 33, TCG_REG_AT, args[2], args[4]);
+        }
+        /* sltu t0, at, args[2] */
+        tcg_out_opc_reg(s, 0, 0, 43, TCG_REG_T0, TCG_REG_AT, args[2]);
+        if (const_args[5]) {
+            /* addiu args[1], args[3], args[5] */
+            tcg_out_opc_imm(s, 9, args[1], args[3], args[5]);
+        } else {
+             /* addu args[1], args[3], args[5] */
+             tcg_out_opc_reg(s, 0, 0, 33, args[1], args[3], args[5]);
+        }
+        /* addu args[1], args[1], t0 */
+        tcg_out_opc_reg(s, 0, 0, 33, args[1], args[1], TCG_REG_T0);
+        /* mov args[0], at */
+        tcg_out_mov(s, args[0], TCG_REG_AT);
+        break;
+    case INDEX_op_sub_i32:
+        if (const_args[2]) {
+            /* addiu args[0], arg [1], -args[2] */
+            tcg_out_opc_imm(s, 9, args[0], args[1], -args[2]);
+        } else {
+            /* subu args[0], args[1], args[2] */
+            tcg_out_opc_reg(s, 0, 0, 35, args[0], args[1], args[2]);
+        }
+        break;
+    case INDEX_op_sub2_i32:
+        if (const_args[4]) {
+            /* addiu at, args[2], -args[4] */
+            tcg_out_opc_imm(s, 9, TCG_REG_AT, args[2], -args[4]);
+        } else {
+            /* addu at, args[2], args[4] */
+            tcg_out_opc_reg(s, 0, 0, 35, TCG_REG_AT, args[2], args[4]);
+        }
+        /* sltu t0, args[2], at */
+        tcg_out_opc_reg(s, 0, 0, 43, TCG_REG_T0, args[2], TCG_REG_AT);
+        if (const_args[5]) {
+            /* addiu args[1], args[3], -args[5] */
+            tcg_out_opc_imm(s, 9, args[1], args[3], -args[5]);
+        } else {
+             /* subu args[1], args[3], args[5] */
+             tcg_out_opc_reg(s, 0, 0, 35, args[1], args[3], args[5]);
+        }
+        /* subu args[1], args[1], t0 */
+        tcg_out_opc_reg(s, 0, 0, 35, args[1], args[1], TCG_REG_T0);
+        /* mov args[0], at */
+        tcg_out_mov(s, args[0], TCG_REG_AT);
+        break;
+    case INDEX_op_mul_i32:
+        /* mult args[1], args[2] */
+        tcg_out_opc_reg(s, 0, 0, 24, 0, args[1], args[2]);
+        /* mflo args[0] */
+        tcg_out_opc_reg(s, 0, 0, 18, args[0], 0, 0);
+        break;
+    case INDEX_op_mulu2_i32:
+        /* multu args[2], args[3] */
+        tcg_out_opc_reg(s, 0, 0, 25, 0, args[2], args[3]);
+        /* mflo args[0] */
+        tcg_out_opc_reg(s, 0, 0, 18, args[0], 0, 0);
+        /* mfhi args[1] */
+        tcg_out_opc_reg(s, 0, 0, 16, args[1], 0, 0);
+        break;
+    case INDEX_op_div_i32:
+        /* div args[1], args[2] */
+        tcg_out_opc_reg(s, 0, 0, 26, 0, args[1], args[2]);
+        /* mflo args[0] */
+        tcg_out_opc_reg(s, 0, 0, 18, args[0], 0, 0);
+        break;
+    case INDEX_op_divu_i32:
+        /* divu args[1], args[2] */
+        tcg_out_opc_reg(s, 0, 0, 27, 0, args[1], args[2]);
+        /* mflo args[0] */
+        tcg_out_opc_reg(s, 0, 0, 18, args[0], 0, 0);
+        break;
+    case INDEX_op_rem_i32:
+        /* div args[1], args[2] */
+        tcg_out_opc_reg(s, 0, 0, 26, 0, args[1], args[2]);
+        /* mfhi args[0] */
+        tcg_out_opc_reg(s, 0, 0, 16, args[0], 0, 0);
+        break;
+    case INDEX_op_remu_i32:
+        /* divu args[1], args[2] */
+        tcg_out_opc_reg(s, 0, 0, 27, 0, args[1], args[2]);
+        /* mfhi args[0] */
+        tcg_out_opc_reg(s, 0, 0, 16, args[0], 0, 0);
+        break;
+
+    case INDEX_op_and_i32:
+        if (const_args[2]) {
+            /* andi args[0], args [1], args[2] */
+            tcg_out_opc_imm(s, 12, args[0], args[1], args[2]);
+        } else {
+            /* and args[0], args [1], args[2] */
+            tcg_out_opc_reg(s, 0, 0, 36, args[0], args[1], args[2]);
+        }
+        break;
+    case INDEX_op_or_i32:
+        if (const_args[2]) {
+            /* ori args[0], args [1], args[2] */
+            tcg_out_opc_imm(s, 13, args[0], args[1], args[2]);
+        } else {
+            /* or args[0], args [1], args[2] */
+            tcg_out_opc_reg(s, 0, 0, 37, args[0], args[1], args[2]);
+        }
+        break;
+    case INDEX_op_not_i32:
+        /* nor args[0], args [1], zero */
+        tcg_out_opc_reg(s, 0, 0, 39, args[0], args[1], args[1]);
+        break;
+    case INDEX_op_xor_i32:
+        if (const_args[2]) {
+            /* xori args[0], args [1], args[2] */
+            tcg_out_opc_imm(s, 14, args[0], args[1], args[2]);
+        } else {
+            /* xor args[0], args [1], args[2] */
+            tcg_out_opc_reg(s, 0, 0, 38, args[0], args[1], args[2]);
+        }
+        break;
+
+    case INDEX_op_sar_i32:
+        if (const_args[2]) {
+            /* sra args[0], args [1], args[2] */
+            tcg_out_shift_imm(s, 3, args[0], args[1], args[2]);
+        } else {
+            /* srav args[0], args [1], args[2] */
+            tcg_out_opc_reg(s, 0, 0, 7, args[0], args[2], args[1]);
+        }
+        break;
+    case INDEX_op_shl_i32:
+        if (const_args[2]) {
+            /* sll args[0], args [1], args[2] */
+            tcg_out_shift_imm(s, 0, args[0], args[1], args[2]);
+        } else {
+            /* sllv args[0], args [1], args[2] */
+            tcg_out_opc_reg(s, 0, 0, 4, args[0], args[2], args[1]);
+        }
+        break;
+    case INDEX_op_shr_i32:
+        if (const_args[2]) {
+            /* srl args[0], args [1], args[2] */
+            tcg_out_shift_imm(s, 2, args[0], args[1], args[2]);
+        } else {
+            /* srlv args[0], args [1], args[2] */
+            tcg_out_opc_reg(s, 0, 0, 6, args[0], args[2], args[1]);
+        }
+        break;
+
+    case INDEX_op_brcond_i32:
+        tcg_out_brcond(s, args[2], args[0], args[1], args[3]);
+        break;
+    case INDEX_op_brcond2_i32:
+        tcg_out_brcond2(s, args[4], args[0], args[1], args[2], args[3], args[5]);
+        break;
+
+    case INDEX_op_qemu_ld8u:
+        tcg_out_qemu_ld(s, args, 0);
+        break;
+    case INDEX_op_qemu_ld8s:
+        tcg_out_qemu_ld(s, args, 0 | 4);
+        break;
+    case INDEX_op_qemu_ld16u:
+        tcg_out_qemu_ld(s, args, 1);
+        break;
+    case INDEX_op_qemu_ld16s:
+        tcg_out_qemu_ld(s, args, 1 | 4);
+        break;
+    case INDEX_op_qemu_ld32u:
+        tcg_out_qemu_ld(s, args, 2);
+        break;
+    case INDEX_op_qemu_ld64:
+        tcg_out_qemu_ld(s, args, 3);
+        break;
+    case INDEX_op_qemu_st8:
+        tcg_out_qemu_st(s, args, 0);
+        break;
+    case INDEX_op_qemu_st16:
+        tcg_out_qemu_st(s, args, 1);
+        break;
+    case INDEX_op_qemu_st32:
+        tcg_out_qemu_st(s, args, 2);
+        break;
+    case INDEX_op_qemu_st64:
+        tcg_out_qemu_st(s, args, 3);
+        break;
+
+    default:
+        tcg_abort();
+    }
+}
+
+static const TCGTargetOpDef mips_op_defs[] = {
+    { INDEX_op_exit_tb, { } },
+    { INDEX_op_goto_tb, { } },
+    { INDEX_op_call, { "r" } },
+    { INDEX_op_jmp, { "r" } },
+    { INDEX_op_br, { } },
+
+    { INDEX_op_mov_i32, { "r", "r" } },
+    { INDEX_op_movi_i32, { "r" } },
+    { INDEX_op_ld8u_i32, { "r", "r" } },
+    { INDEX_op_ld8s_i32, { "r", "r" } },
+    { INDEX_op_ld16u_i32, { "r", "r" } },
+    { INDEX_op_ld16s_i32, { "r", "r" } },
+    { INDEX_op_ld_i32, { "r", "r" } },
+    { INDEX_op_st8_i32, { "rZ", "r" } },
+    { INDEX_op_st16_i32, { "rZ", "r" } },
+    { INDEX_op_st_i32, { "rZ", "r" } },
+
+    { INDEX_op_add_i32, { "r", "rZ", "rJZ" } },
+    { INDEX_op_mul_i32, { "r", "rZ", "rZ" } },
+    { INDEX_op_mulu2_i32, { "r", "r", "rZ", "rZ" } },
+    { INDEX_op_div_i32, { "r", "rZ", "rZ" } },
+    { INDEX_op_divu_i32, { "r", "rZ", "rZ" } },
+    { INDEX_op_rem_i32, { "r", "rZ", "rZ" } },
+    { INDEX_op_remu_i32, { "r", "rZ", "rZ" } },
+    { INDEX_op_sub_i32, { "r", "rZ", "rJZ" } },
+
+    { INDEX_op_and_i32, { "r", "rZ", "rIZ" } },
+    { INDEX_op_not_i32, { "r", "rZ" } },
+    { INDEX_op_or_i32, { "r", "rZ", "rIZ" } },
+    { INDEX_op_xor_i32, { "r", "rZ", "rIZ" } },
+
+    { INDEX_op_shl_i32, { "r", "rZ", "riZ" } },
+    { INDEX_op_shr_i32, { "r", "rZ", "riZ" } },
+    { INDEX_op_sar_i32, { "r", "rZ", "riZ" } },
+
+    { INDEX_op_brcond_i32, { "rZ", "rZ" } },
+
+    { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rJZ", "rJZ" } },
+    { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rJZ", "rJZ" } },
+    { INDEX_op_brcond2_i32, { "rZ", "rZ", "rZ", "rZ" } },
+
+#if TARGET_LONG_BITS == 32
+    { INDEX_op_qemu_ld8u, { "L", "lZ" } },
+    { INDEX_op_qemu_ld8s, { "L", "lZ" } },
+    { INDEX_op_qemu_ld16u, { "L", "lZ" } },
+    { INDEX_op_qemu_ld16s, { "L", "lZ" } },
+    { INDEX_op_qemu_ld32u, { "L", "lZ" } },
+    { INDEX_op_qemu_ld64, { "L", "L", "lZ" } },
+
+    { INDEX_op_qemu_st8, { "SZ", "SZ" } },
+    { INDEX_op_qemu_st16, { "SZ", "SZ" } },
+    { INDEX_op_qemu_st32, { "SZ", "SZ" } },
+    { INDEX_op_qemu_st64, { "SZ", "SZ", "SZ" } },
+#else
+    { INDEX_op_qemu_ld8u, { "L", "lZ", "lZ" } },
+    { INDEX_op_qemu_ld8s, { "L", "lZ", "lZ" } },
+    { INDEX_op_qemu_ld16u, { "L", "lZ", "lZ" } },
+    { INDEX_op_qemu_ld16s, { "L", "lZ", "lZ" } },
+    { INDEX_op_qemu_ld32u, { "L", "lZ", "lZ" } },
+    { INDEX_op_qemu_ld64, { "L", "L", "lZ", "lZ" } },
+
+    { INDEX_op_qemu_st8, { "SZ", "SZ", "SZ" } },
+    { INDEX_op_qemu_st16, { "SZ", "SZ", "SZ" } },
+    { INDEX_op_qemu_st32, { "SZ", "SZ", "SZ" } },
+    { INDEX_op_qemu_st64, { "SZ", "SZ", "SZ", "SZ" } },
+#endif
+    { -1 },
+};
+
+static int tcg_target_callee_save_regs[] = {
+    TCG_REG_S0,
+    TCG_REG_S1,
+    TCG_REG_S2,
+    TCG_REG_S3,
+    TCG_REG_S4,
+    TCG_REG_S5,
+    TCG_REG_S6,
+    TCG_REG_S7,
+    TCG_REG_GP,
+    /* TCG_REG_FP, */ /* currently used for the global env, so np
+                         need to save */
+    TCG_REG_RA,       /* should be last for ABI compliance */
+};
+
+/* Generate global QEMU prologue and epilogue code */
+void tcg_target_qemu_prologue(TCGContext *s)
+{
+    int i, frame_size;
+
+    /* reserve some stack space */
+    frame_size = ARRAY_SIZE(tcg_target_callee_save_regs) * 4
+                 + TCG_STATIC_CALL_ARGS_SIZE;
+    frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) &
+                 ~(TCG_TARGET_STACK_ALIGN - 1);
+
+    /* TB prologue */
+    tcg_out_addi(s, TCG_REG_SP, -frame_size);
+    for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) {
+        tcg_out_st(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i],
+                   TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4);
+    }
+
+    /* Call generated code */
+    tcg_out_opc_reg(s, 0, 0, 8, 0, TCG_REG_A0, 0);
+    tcg_out_nop(s);
+    tb_ret_addr = s->code_ptr;
+
+    /* TB epilogue */
+    for(i = 0 ; i < ARRAY_SIZE(tcg_target_callee_save_regs) ; i++) {
+        tcg_out_ld(s, TCG_TYPE_I32, tcg_target_callee_save_regs[i],
+                   TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE + i * 4);
+    }
+
+    /* jr ra */
+    tcg_out_opc_reg(s, 0, 0, 8, 0, TCG_REG_RA, 0);
+    tcg_out_addi(s, TCG_REG_SP, frame_size);
+}
+
+void tcg_target_init(TCGContext *s)
+{
+    tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
+    tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+                     (1 << TCG_REG_V0) |
+                     (1 << TCG_REG_V1) |
+                     (1 << TCG_REG_A0) |
+                     (1 << TCG_REG_A1) |
+                     (1 << TCG_REG_A2) |
+                     (1 << TCG_REG_A3) |
+                     (1 << TCG_REG_T1) |
+                     (1 << TCG_REG_T2) |
+                     (1 << TCG_REG_T3) |
+                     (1 << TCG_REG_T4) |
+                     (1 << TCG_REG_T5) |
+                     (1 << TCG_REG_T6) |
+                     (1 << TCG_REG_T7) |
+                     (1 << TCG_REG_T8) |
+                     (1 << TCG_REG_T9));
+
+    tcg_regset_clear(s->reserved_regs);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_ZERO); /* zero register */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_K0);   /* kernel use only */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_K1);   /* kernel use only */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_AT);   /* internal use */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_T0);   /* internal use */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_RA);   /* return address */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP);   /* stack pointer */
+
+    tcg_add_target_add_op_defs(mips_op_defs);
+}
diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h
new file mode 100644
index 0000000..ba91623
--- /dev/null
+++ b/tcg/mips/tcg-target.h
@@ -0,0 +1,107 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008-2009 Arnaud Patard <arnaud.patard@rtp-net.org>
+ * Copyright (c) 2009 Aurelien Jarno <aurelien@aurel32.net>
+ * Based on i386/tcg-target.c - Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_MIPS 1
+
+#define TCG_TARGET_REG_BITS 32
+#ifdef __MIPSEB__
+# define TCG_TARGET_WORDS_BIGENDIAN
+#endif
+
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+    TCG_REG_ZERO = 0,
+    TCG_REG_AT,
+    TCG_REG_V0,
+    TCG_REG_V1,
+    TCG_REG_A0,
+    TCG_REG_A1,
+    TCG_REG_A2,
+    TCG_REG_A3,
+    TCG_REG_T0,
+    TCG_REG_T1,
+    TCG_REG_T2,
+    TCG_REG_T3,
+    TCG_REG_T4,
+    TCG_REG_T5,
+    TCG_REG_T6,
+    TCG_REG_T7,
+    TCG_REG_S0,
+    TCG_REG_S1,
+    TCG_REG_S2,
+    TCG_REG_S3,
+    TCG_REG_S4,
+    TCG_REG_S5,
+    TCG_REG_S6,
+    TCG_REG_S7,
+    TCG_REG_T8,
+    TCG_REG_T9,
+    TCG_REG_K0,
+    TCG_REG_K1,
+    TCG_REG_GP,
+    TCG_REG_SP,
+    TCG_REG_FP,
+    TCG_REG_RA,
+};
+
+#define TCG_CT_CONST_ZERO 0x100
+#define TCG_CT_CONST_U16  0x200
+#define TCG_CT_CONST_S16  0x400
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_SP
+#define TCG_TARGET_STACK_ALIGN 8
+#define TCG_TARGET_CALL_STACK_OFFSET 16
+#define TCG_TARGET_CALL_ALIGN_ARGS 1
+
+/* optional instructions */
+#define TCG_TARGET_HAS_div_i32
+#define TCG_TARGET_HAS_not_i32
+#undef TCG_TARGET_HAS_ext8s_i32
+#undef TCG_TARGET_HAS_ext16s_i32
+#undef TCG_TARGET_HAS_bswap32_i32
+#undef TCG_TARGET_HAS_bswap16_i32
+#undef TCG_TARGET_HAS_rot_i32
+
+/* optional instructions automatically implemented */
+#undef TCG_TARGET_HAS_neg_i32      /* sub  rd, zero, rt   */
+#undef TCG_TARGET_HAS_ext8u_i32    /* andi rt, rs, 0xff   */
+#undef TCG_TARGET_HAS_ext16u_i32   /* andi rt, rs, 0xffff */
+
+/* Note: must be synced with dyngen-exec.h */
+#define TCG_AREG0 TCG_REG_FP
+#define TCG_AREG1 TCG_REG_S0
+#define TCG_AREG2 TCG_REG_S1
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+#if QEMU_GNUC_PREREQ(4, 1)
+    void __clear_cache(char *beg, char *end);
+    __clear_cache((char *) start, (char *) stop);
+#else
+# error __clear_cache not available
+#endif
+}
-- 
1.6.1.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 1/5] linux-user: remove hardcoded value of _NSIG in signal.c
  2009-10-22 20:17 [Qemu-devel] [PATCH 0/5] MIPS(EL) host support Aurelien Jarno
  2009-10-22 19:31 ` [Qemu-devel] [PATCH 5/5] tcg/mips: use direct jumps Aurelien Jarno
  2009-10-22 20:17 ` [Qemu-devel] [PATCH 2/5] cpu-all.h: fix cpu_get_real_ticks on mips host Aurelien Jarno
@ 2009-10-22 20:17 ` Aurelien Jarno
  2009-10-23 15:12   ` Stefan Weil
  2009-10-22 20:17 ` [Qemu-devel] [PATCH 3/5] tcg: initial mips support Aurelien Jarno
  2009-10-22 20:17 ` [Qemu-devel] [PATCH 4/5] tcg: increase TCG_MAX_OP_SIZE to 192 Aurelien Jarno
  4 siblings, 1 reply; 8+ messages in thread
From: Aurelien Jarno @ 2009-10-22 20:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: Riku Voipio, Arnaud Patard

From: Arnaud Patard <arnaud.patard@rtp-net.org>

In a bunch of places, 64 is used as value of _NSIG but it's wrong
at least on MIPS were _NSIG is 128.

Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 linux-user/signal.c |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/linux-user/signal.c b/linux-user/signal.c
index 2df17aa..6620ce3 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -44,7 +44,7 @@ static struct target_sigaction sigact_table[TARGET_NSIG];
 static void host_signal_handler(int host_signum, siginfo_t *info,
                                 void *puc);
 
-static uint8_t host_to_target_signal_table[65] = {
+static uint8_t host_to_target_signal_table[_NSIG+1] = {
     [SIGHUP] = TARGET_SIGHUP,
     [SIGINT] = TARGET_SIGINT,
     [SIGQUIT] = TARGET_SIGQUIT,
@@ -87,7 +87,7 @@ static uint8_t host_to_target_signal_table[65] = {
     [__SIGRTMIN] = __SIGRTMAX,
     [__SIGRTMAX] = __SIGRTMIN,
 };
-static uint8_t target_to_host_signal_table[65];
+static uint8_t target_to_host_signal_table[_NSIG+1];
 
 static inline int on_sig_stack(unsigned long sp)
 {
@@ -103,14 +103,14 @@ static inline int sas_ss_flags(unsigned long sp)
 
 int host_to_target_signal(int sig)
 {
-    if (sig > 64)
+    if (sig > _NSIG)
         return sig;
     return host_to_target_signal_table[sig];
 }
 
 int target_to_host_signal(int sig)
 {
-    if (sig > 64)
+    if (sig > _NSIG)
         return sig;
     return target_to_host_signal_table[sig];
 }
@@ -311,11 +311,11 @@ void signal_init(void)
     int host_sig;
 
     /* generate signal conversion tables */
-    for(i = 1; i <= 64; i++) {
+    for(i = 1; i <= _NSIG; i++) {
         if (host_to_target_signal_table[i] == 0)
             host_to_target_signal_table[i] = i;
     }
-    for(i = 1; i <= 64; i++) {
+    for(i = 1; i <= _NSIG; i++) {
         j = host_to_target_signal_table[i];
         target_to_host_signal_table[j] = i;
     }
-- 
1.6.1.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 2/5] cpu-all.h: fix cpu_get_real_ticks on mips host
  2009-10-22 20:17 [Qemu-devel] [PATCH 0/5] MIPS(EL) host support Aurelien Jarno
  2009-10-22 19:31 ` [Qemu-devel] [PATCH 5/5] tcg/mips: use direct jumps Aurelien Jarno
@ 2009-10-22 20:17 ` Aurelien Jarno
  2009-10-22 20:17 ` [Qemu-devel] [PATCH 1/5] linux-user: remove hardcoded value of _NSIG in signal.c Aurelien Jarno
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Aurelien Jarno @ 2009-10-22 20:17 UTC (permalink / raw)
  To: qemu-devel; +Cc: Arnaud Patard

From: Arnaud Patard <arnaud.patard@rtp-net.org>

Fix cpu_get_real_ticks:
- check should be done on __mips and not __mips_isa_rev
- linux kernels >= 2.6.25 are emulating the 2 needed rdhwr functions
  so it's safe to use rdhwr.

This is better than what's currently in but it doesn't mean it works nicely
Some tests needs to be done imho

Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 cpu-all.h |   27 ++++++++++++++++++---------
 1 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/cpu-all.h b/cpu-all.h
index ebe8bfb..e214374 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -1017,24 +1017,33 @@ static inline int64_t cpu_get_real_ticks (void)
 #endif
 }
 
-#elif defined(__mips__)
+#elif (defined(__mips_isa_rev) && __mips_isa_rev >= 2) || defined(__linux__)
+/*
+ * binutils wants to use rdhwr only on mips32r2
+ * but as linux kernel emulate it, it's fine
+ * to use it.
+ *
+ */
+#define MIPS_RDHWR(rd, value) {                 \
+    __asm__ __volatile__ (                      \
+                          ".set   push\n\t"     \
+                          ".set mips32r2\n\t"   \
+                          "rdhwr  %0, "rd"\n\t" \
+                          ".set   pop"          \
+                          : "=r" (value));      \
+}
 
 static inline int64_t cpu_get_real_ticks(void)
 {
-#if defined(__mips_isa_rev) && __mips_isa_rev >= 2
+/* On kernels >= 2.6.25 rdhwr <reg>, $2 and $3 are emulated */
     uint32_t count;
     static uint32_t cyc_per_count = 0;
 
     if (!cyc_per_count)
-        __asm__ __volatile__("rdhwr %0, $3" : "=r" (cyc_per_count));
+        MIPS_RDHWR("$3", cyc_per_count);
 
-    __asm__ __volatile__("rdhwr %1, $2" : "=r" (count));
+    MIPS_RDHWR("$2", count);
     return (int64_t)(count * cyc_per_count);
-#else
-    /* FIXME */
-    static int64_t ticks = 0;
-    return ticks++;
-#endif
 }
 
 #else
-- 
1.6.1.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 1/5] linux-user: remove hardcoded value of _NSIG in signal.c
  2009-10-22 20:17 ` [Qemu-devel] [PATCH 1/5] linux-user: remove hardcoded value of _NSIG in signal.c Aurelien Jarno
@ 2009-10-23 15:12   ` Stefan Weil
  2009-10-23 15:21     ` Aurelien Jarno
  0 siblings, 1 reply; 8+ messages in thread
From: Stefan Weil @ 2009-10-23 15:12 UTC (permalink / raw)
  To: Aurelien Jarno; +Cc: Riku Voipio, qemu-devel, Arnaud Patard

Aurelien Jarno schrieb:
> From: Arnaud Patard <arnaud.patard@rtp-net.org>
>
> In a bunch of places, 64 is used as value of _NSIG but it's wrong
> at least on MIPS were _NSIG is 128.
>
> Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>
> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
> ---
>  linux-user/signal.c |   12 ++++++------
>  1 files changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/linux-user/signal.c b/linux-user/signal.c
> index 2df17aa..6620ce3 100644
> --- a/linux-user/signal.c
> +++ b/linux-user/signal.c
> @@ -44,7 +44,7 @@ static struct target_sigaction sigact_table[TARGET_NSIG];
>  static void host_signal_handler(int host_signum, siginfo_t *info,
>                                  void *puc);
>  
> -static uint8_t host_to_target_signal_table[65] = {
> +static uint8_t host_to_target_signal_table[_NSIG+1] = {
>      [SIGHUP] = TARGET_SIGHUP,
>      [SIGINT] = TARGET_SIGINT,
>      [SIGQUIT] = TARGET_SIGQUIT,
> @@ -87,7 +87,7 @@ static uint8_t host_to_target_signal_table[65] = {
>      [__SIGRTMIN] = __SIGRTMAX,
>      [__SIGRTMAX] = __SIGRTMIN,
>  };
> -static uint8_t target_to_host_signal_table[65];
> +static uint8_t target_to_host_signal_table[_NSIG+1];
>  
>  static inline int on_sig_stack(unsigned long sp)
>  {
> @@ -103,14 +103,14 @@ static inline int sas_ss_flags(unsigned long sp)
>  
>  int host_to_target_signal(int sig)
>  {
> -    if (sig > 64)
> +    if (sig > _NSIG)
>          return sig;
>      return host_to_target_signal_table[sig];
>  }
>  
>  int target_to_host_signal(int sig)
>  {
> -    if (sig > 64)
> +    if (sig > _NSIG)
>          return sig;
>      return target_to_host_signal_table[sig];
>  }
> @@ -311,11 +311,11 @@ void signal_init(void)
>      int host_sig;
>  
>      /* generate signal conversion tables */
> -    for(i = 1; i <= 64; i++) {
> +    for(i = 1; i <= _NSIG; i++) {
>          if (host_to_target_signal_table[i] == 0)
>              host_to_target_signal_table[i] = i;
>      }
> -    for(i = 1; i <= 64; i++) {
> +    for(i = 1; i <= _NSIG; i++) {
>          j = host_to_target_signal_table[i];
>          target_to_host_signal_table[j] = i;
>      }
>   

At least in my linux installation, _NSIG is 65 (x86) or 128 (mips),
so the patch above should replace 64 by (_NSIG - 1) and 65 by _NSIG.

But perhaps it does no harm if the code allocates an unused signal
at the end.

For my tests with mips host and tci, I used
static uint8_t target_to_host_signal_table[_NSIG]

Regards,
Stefan

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 1/5] linux-user: remove hardcoded value of _NSIG in signal.c
  2009-10-23 15:12   ` Stefan Weil
@ 2009-10-23 15:21     ` Aurelien Jarno
  0 siblings, 0 replies; 8+ messages in thread
From: Aurelien Jarno @ 2009-10-23 15:21 UTC (permalink / raw)
  To: Stefan Weil; +Cc: Riku Voipio, qemu-devel, Arnaud Patard

Stefan Weil a écrit :
> Aurelien Jarno schrieb:
>> From: Arnaud Patard <arnaud.patard@rtp-net.org>
>>
>> In a bunch of places, 64 is used as value of _NSIG but it's wrong
>> at least on MIPS were _NSIG is 128.
>>
>> Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>
>> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
>> ---
>>  linux-user/signal.c |   12 ++++++------
>>  1 files changed, 6 insertions(+), 6 deletions(-)
>>
>> diff --git a/linux-user/signal.c b/linux-user/signal.c
>> index 2df17aa..6620ce3 100644
>> --- a/linux-user/signal.c
>> +++ b/linux-user/signal.c
>> @@ -44,7 +44,7 @@ static struct target_sigaction sigact_table[TARGET_NSIG];
>>  static void host_signal_handler(int host_signum, siginfo_t *info,
>>                                  void *puc);
>>  
>> -static uint8_t host_to_target_signal_table[65] = {
>> +static uint8_t host_to_target_signal_table[_NSIG+1] = {
>>      [SIGHUP] = TARGET_SIGHUP,
>>      [SIGINT] = TARGET_SIGINT,
>>      [SIGQUIT] = TARGET_SIGQUIT,
>> @@ -87,7 +87,7 @@ static uint8_t host_to_target_signal_table[65] = {
>>      [__SIGRTMIN] = __SIGRTMAX,
>>      [__SIGRTMAX] = __SIGRTMIN,
>>  };
>> -static uint8_t target_to_host_signal_table[65];
>> +static uint8_t target_to_host_signal_table[_NSIG+1];
>>  
>>  static inline int on_sig_stack(unsigned long sp)
>>  {
>> @@ -103,14 +103,14 @@ static inline int sas_ss_flags(unsigned long sp)
>>  
>>  int host_to_target_signal(int sig)
>>  {
>> -    if (sig > 64)
>> +    if (sig > _NSIG)
>>          return sig;
>>      return host_to_target_signal_table[sig];
>>  }
>>  
>>  int target_to_host_signal(int sig)
>>  {
>> -    if (sig > 64)
>> +    if (sig > _NSIG)
>>          return sig;
>>      return target_to_host_signal_table[sig];
>>  }
>> @@ -311,11 +311,11 @@ void signal_init(void)
>>      int host_sig;
>>  
>>      /* generate signal conversion tables */
>> -    for(i = 1; i <= 64; i++) {
>> +    for(i = 1; i <= _NSIG; i++) {
>>          if (host_to_target_signal_table[i] == 0)
>>              host_to_target_signal_table[i] = i;
>>      }
>> -    for(i = 1; i <= 64; i++) {
>> +    for(i = 1; i <= _NSIG; i++) {
>>          j = host_to_target_signal_table[i];
>>          target_to_host_signal_table[j] = i;
>>      }
>>   
> 
> At least in my linux installation, _NSIG is 65 (x86) or 128 (mips),
> so the patch above should replace 64 by (_NSIG - 1) and 65 by _NSIG.
> 
> But perhaps it does no harm if the code allocates an unused signal
> at the end.
> 
> For my tests with mips host and tci, I used
> static uint8_t target_to_host_signal_table[_NSIG]
> 

>From /usr/include/bits/signum.h:
#define   _NSIG           65      /* Biggest signal number + 1

I confirm that all use of _NSIG of them should be decremented by one.

-- 
Aurelien Jarno                          GPG: 1024D/F1BCDB73
aurelien@aurel32.net                 http://www.aurel32.net

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2009-10-23 15:21 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-10-22 20:17 [Qemu-devel] [PATCH 0/5] MIPS(EL) host support Aurelien Jarno
2009-10-22 19:31 ` [Qemu-devel] [PATCH 5/5] tcg/mips: use direct jumps Aurelien Jarno
2009-10-22 20:17 ` [Qemu-devel] [PATCH 2/5] cpu-all.h: fix cpu_get_real_ticks on mips host Aurelien Jarno
2009-10-22 20:17 ` [Qemu-devel] [PATCH 1/5] linux-user: remove hardcoded value of _NSIG in signal.c Aurelien Jarno
2009-10-23 15:12   ` Stefan Weil
2009-10-23 15:21     ` Aurelien Jarno
2009-10-22 20:17 ` [Qemu-devel] [PATCH 3/5] tcg: initial mips support Aurelien Jarno
2009-10-22 20:17 ` [Qemu-devel] [PATCH 4/5] tcg: increase TCG_MAX_OP_SIZE to 192 Aurelien Jarno

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).