Update to the latest version of the LLVM mono branch.
[mono.git] / mono / mini / mini-mips.c
index 8afd14b2ba2dcda1335d54ba9a5bf2ca5ae9f11b..75908bc255457f1a5443dd1c7e9c44f66c0354a3 100644 (file)
@@ -28,7 +28,6 @@
 #define SAVE_FP_REGS           0
 #define SAVE_ALL_REGS          0
 #define EXTRA_STACK_SPACE      0       /* suppresses some s-reg corruption issues */
-#define LONG_BRANCH            1       /* needed for yyparse in mcs */
 
 #define SAVE_LMF               1
 #define ALWAYS_USE_FP          1
 #define PROMOTE_R4_TO_R8       1       /* promote single values in registers to doubles */
 #define USE_MUL                        1       /* use mul instead of mult/mflo for multiply */
 
+/* Emit a call sequence to 'v', using 'D' as a scratch register if necessary */
+#define mips_call(c,D,v) do {  \
+               guint32 _target = (guint32)(v); \
+               if (1 || !(v) || ((_target & 0xfc000000) != (((guint32)(c)) & 0xfc000000))) { \
+                       mips_load_const (c, D, _target); \
+                       mips_jalr (c, D, mips_ra); \
+               } \
+               else { \
+                       mips_jumpl (c, _target >> 2); \
+               } \
+               mips_nop (c); \
+       } while (0)
+
 enum {
        TLS_MODE_DETECT,
        TLS_MODE_FAILED,
@@ -193,10 +205,7 @@ mips_emit_exc_by_name(guint8 *code, const char *name)
 
        mips_load_const (code, mips_a0, exc_class->type_token);
        addr = (guint32) mono_get_throw_corlib_exception ();
-       mips_load_const (code, mips_t9, addr);
-       mips_jalr (code, mips_t9, mips_ra);
-       mips_nop (code);
-
+       mips_call (code, mips_t9, addr);
        return code;
 }
 
@@ -234,79 +243,78 @@ mips_emit_load_const(guint8 *code, int dreg, mgreg_t v)
 guint8 *
 mips_emit_cond_branch (MonoCompile *cfg, guint8 *code, int op, MonoInst *ins)
 {
-#if LONG_BRANCH
-       int br_offset = 5;
-#endif
-
        g_assert (ins);
-#if LONG_BRANCH
-       /* Invert test and emit branch around jump */
-       switch (op) {
-       case OP_MIPS_BEQ:
-               mips_bne (code, ins->sreg1, ins->sreg2, br_offset);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BNE:
-               mips_beq (code, ins->sreg1, ins->sreg2, br_offset);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BGEZ:
-               mips_bltz (code, ins->sreg1, br_offset);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BGTZ:
-               mips_blez (code, ins->sreg1, br_offset);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BLEZ:
-               mips_bgtz (code, ins->sreg1, br_offset);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BLTZ:
-               mips_bgez (code, ins->sreg1, br_offset);
+       if (cfg->arch.long_branch) {
+               int br_offset = 5;
+
+               /* Invert test and emit branch around jump */
+               switch (op) {
+               case OP_MIPS_BEQ:
+                       mips_bne (code, ins->sreg1, ins->sreg2, br_offset);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BNE:
+                       mips_beq (code, ins->sreg1, ins->sreg2, br_offset);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BGEZ:
+                       mips_bltz (code, ins->sreg1, br_offset);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BGTZ:
+                       mips_blez (code, ins->sreg1, br_offset);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BLEZ:
+                       mips_bgtz (code, ins->sreg1, br_offset);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BLTZ:
+                       mips_bgez (code, ins->sreg1, br_offset);
+                       mips_nop (code);
+                       break;
+               default:
+                       g_assert_not_reached ();
+               }
+               mono_add_patch_info (cfg, code - cfg->native_code,
+                                    MONO_PATCH_INFO_BB, ins->inst_true_bb);
+               mips_lui (code, mips_at, mips_zero, 0);
+               mips_addiu (code, mips_at, mips_at, 0);
+               mips_jr (code, mips_at);
                mips_nop (code);
-               break;
-       default:
-               g_assert_not_reached ();
        }
-       mono_add_patch_info (cfg, code - cfg->native_code,
-                            MONO_PATCH_INFO_BB, ins->inst_true_bb);
-       mips_lui (code, mips_at, mips_zero, 0);
-       mips_addiu (code, mips_at, mips_at, 0);
-       mips_jr (code, mips_at);
-       mips_nop (code);
-#else
-       mono_add_patch_info (cfg, code - cfg->native_code,
-                            MONO_PATCH_INFO_BB, ins->inst_true_bb);
-       switch (op) {
-       case OP_MIPS_BEQ:
-               mips_beq (code, ins->sreg1, ins->sreg2, 0);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BNE:
-               mips_bne (code, ins->sreg1, ins->sreg2, 0);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BGEZ:
-               mips_bgez (code, ins->sreg1, 0);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BGTZ:
-               mips_bgtz (code, ins->sreg1, 0);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BLEZ:
-               mips_blez (code, ins->sreg1, 0);
-               mips_nop (code);
-               break;
-       case OP_MIPS_BLTZ:
-               mips_bltz (code, ins->sreg1, 0);
-               mips_nop (code);
-               break;
-       default:
-               g_assert_not_reached ();
+       else {
+               mono_add_patch_info (cfg, code - cfg->native_code,
+                                    MONO_PATCH_INFO_BB, ins->inst_true_bb);
+               switch (op) {
+               case OP_MIPS_BEQ:
+                       mips_beq (code, ins->sreg1, ins->sreg2, 0);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BNE:
+                       mips_bne (code, ins->sreg1, ins->sreg2, 0);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BGEZ:
+                       mips_bgez (code, ins->sreg1, 0);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BGTZ:
+                       mips_bgtz (code, ins->sreg1, 0);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BLEZ:
+                       mips_blez (code, ins->sreg1, 0);
+                       mips_nop (code);
+                       break;
+               case OP_MIPS_BLTZ:
+                       mips_bltz (code, ins->sreg1, 0);
+                       mips_nop (code);
+                       break;
+               default:
+                       g_assert_not_reached ();
+               }
        }
-#endif
        return (code);
 }
 
@@ -509,12 +517,9 @@ mono_arch_get_argument_info (MonoMethodSignature *csig, int param_count, MonoJit
 
 
 gpointer
-mono_arch_get_this_arg_from_call (MonoGenericSharingContext *gsctx, MonoMethodSignature *sig, mgreg_t *regs, guint8 *code)
+mono_arch_get_this_arg_from_call (mgreg_t *regs, guint8 *code)
 {
-       /* FIXME: handle returning a struct */
        g_assert(regs);
-       if (sig && MONO_TYPE_ISSTRUCT (sig->ret))
-               return (gpointer)regs [mips_a1];
        return (gpointer)regs [mips_a0];
 }
 
@@ -3288,6 +3293,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_LOAD_MEMBASE:
                case OP_LOADI4_MEMBASE:
                case OP_LOADU4_MEMBASE:
+                       g_assert (ins->dreg != -1);
                        if (mips_is_imm16 (ins->inst_offset)) {
                                mips_lw (code, ins->dreg, ins->inst_basereg, ins->inst_offset);
                        } else {
@@ -3416,18 +3422,22 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_IDIV:
                case OP_IREM: {
                        guint32 *divisor_is_m1;
+                       guint32 *dividend_is_minvalue;
                        guint32 *divisor_is_zero;
 
-                       /* */
-                       mips_addiu (code, mips_at, mips_zero, 0xffff);
+                       mips_load_const (code, mips_at, -1);
                        divisor_is_m1 = (guint32 *)(void *)code;
                        mips_bne (code, ins->sreg2, mips_at, 0);
+                       mips_lui (code, mips_at, mips_zero, 0x8000);
+                       dividend_is_minvalue = (guint32 *)(void *)code;
+                       mips_bne (code, ins->sreg1, mips_at, 0);
                        mips_nop (code);
 
-                       /* Divide by -1 -- throw exception */
-                       EMIT_SYSTEM_EXCEPTION_NAME("ArithmeticException");
+                       /* Divide Int32.MinValue by -1 -- throw exception */
+                       EMIT_SYSTEM_EXCEPTION_NAME("OverflowException");
 
                        mips_patch (divisor_is_m1, (guint32)code);
+                       mips_patch (dividend_is_minvalue, (guint32)code);
 
                        /* Put divide in branch delay slot (NOT YET) */
                        divisor_is_zero = (guint32 *)(void *)code;
@@ -3681,15 +3691,16 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 
                        mono_add_patch_info (cfg, (guint8*) code - cfg->native_code,
                                             MONO_PATCH_INFO_METHOD_JUMP, ins->inst_p0);
-#if LONG_BRANCH
-                       mips_lui (code, mips_t9, mips_zero, 0);
-                       mips_addiu (code, mips_t9, mips_t9, 0);
-                       mips_jr (code, mips_t9);
-                       mips_nop (code);
-#else
-                       mips_beq (code, mips_zero, mips_zero, 0);
-                       mips_nop (code);
-#endif
+                       if (cfg->arch.long_branch) {
+                               mips_lui (code, mips_t9, mips_zero, 0);
+                               mips_addiu (code, mips_t9, mips_t9, 0);
+                               mips_jr (code, mips_t9);
+                               mips_nop (code);
+                       }
+                       else {
+                               mips_beq (code, mips_zero, mips_zero, 0);
+                               mips_nop (code);
+                       }
                        break;
                case OP_CHECK_THIS:
                        /* ensure ins->sreg1 is not NULL */
@@ -3731,12 +3742,14 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        case OP_VCALL2:
                        case OP_VOIDCALL:
                        case OP_CALL:
-                               if (ins->flags & MONO_INST_HAS_METHOD)
+                               if (ins->flags & MONO_INST_HAS_METHOD) {
                                        mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_METHOD, call->method);
-                               else
+                                       mips_call (code, mips_t9, call->method);
+                               }
+                               else {
                                        mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_ABS, call->fptr);
-                               mips_lui (code, mips_t9, mips_zero, 0);
-                               mips_addiu (code, mips_t9, mips_t9, 0);
+                                       mips_call (code, mips_t9, call->fptr);
+                               }
                                break;
                        case OP_FCALL_REG:
                        case OP_LCALL_REG:
@@ -3745,6 +3758,8 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        case OP_VOIDCALL_REG:
                        case OP_CALL_REG:
                                MIPS_MOVE (code, mips_t9, ins->sreg1);
+                               mips_jalr (code, mips_t9, mips_ra);
+                               mips_nop (code);
                                break;
                        case OP_FCALL_MEMBASE:
                        case OP_LCALL_MEMBASE:
@@ -3753,10 +3768,10 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        case OP_VOIDCALL_MEMBASE:
                        case OP_CALL_MEMBASE:
                                mips_lw (code, mips_t9, ins->sreg1, ins->inst_offset);
+                               mips_jalr (code, mips_t9, mips_ra);
+                               mips_nop (code);
                                break;
                        }
-                       mips_jalr (code, mips_t9, mips_ra);
-                       mips_nop (code);
 #if PROMOTE_R4_TO_R8
                        /* returned an FP R4 (single), promote to R8 (double) in place */
                        if ((ins->opcode == OP_FCALL ||
@@ -3790,18 +3805,14 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_THROW: {
                        gpointer addr = mono_arch_get_throw_exception(NULL, FALSE);
                        mips_move (code, mips_a0, ins->sreg1);
-                       mips_load_const (code, mips_t9, addr);
-                       mips_jalr (code, mips_t9, mips_ra);
-                       mips_nop (code);
+                       mips_call (code, mips_t9, addr);
                        mips_break (code, 0xfc);
                        break;
                }
                case OP_RETHROW: {
                        gpointer addr = mono_arch_get_rethrow_exception(NULL, FALSE);
                        mips_move (code, mips_a0, ins->sreg1);
-                       mips_load_const (code, mips_t9, addr);
-                       mips_jalr (code, mips_t9, mips_ra);
-                       mips_nop (code);
+                       mips_call (code, mips_t9, addr);
                        mips_break (code, 0xfb);
                        break;
                }
@@ -3869,15 +3880,16 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                        break;
                case OP_BR:
                        mono_add_patch_info (cfg, offset, MONO_PATCH_INFO_BB, ins->inst_target_bb);
-#if LONG_BRANCH
-                       mips_lui (code, mips_at, mips_zero, 0);
-                       mips_addiu (code, mips_at, mips_at, 0);
-                       mips_jr (code, mips_at);
-                       mips_nop (code);
-#else
-                       mips_beq (code, mips_zero, mips_zero, 0);
-                       mips_nop (code);
-#endif
+                       if (cfg->arch.long_branch) {
+                               mips_lui (code, mips_at, mips_zero, 0);
+                               mips_addiu (code, mips_at, mips_at, 0);
+                               mips_jr (code, mips_at);
+                               mips_nop (code);
+                       }
+                       else {
+                               mips_beq (code, mips_zero, mips_zero, 0);
+                               mips_nop (code);
+                       }
                        break;
                case OP_BR_REG:
                        mips_jr (code, ins->sreg1);
@@ -3995,9 +4007,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                                break;
 
                        case OP_MIPS_COND_EXC_LE_UN:
-                               mips_subu (code, mips_at, ins->sreg1, ins->sreg2);
+                               mips_sltu (code, mips_at, ins->sreg2, ins->sreg1);
                                throw = (guint32 *)(void *)code;
-                               mips_blez (code, mips_at, 0);
+                               mips_beq (code, mips_at, mips_zero, 0);
                                mips_nop (code);
                                break;
 
@@ -4129,11 +4141,11 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 #endif
                        break;
                case OP_LOADR4_MEMINDEX:
-                       mips_addiu (code, mips_at, ins->inst_basereg, ins->sreg2);
+                       mips_addu (code, mips_at, ins->inst_basereg, ins->sreg2);
                        mips_lwc1 (code, ins->dreg, mips_at, 0);
                        break;
                case OP_LOADR8_MEMINDEX:
-                       mips_addiu (code, mips_at, ins->inst_basereg, ins->sreg2);
+                       mips_addu (code, mips_at, ins->inst_basereg, ins->sreg2);
 #if _MIPS_SIM == _ABIO32
                        mips_lwc1 (code, ins->dreg, mips_at, 0);
                        mips_lwc1 (code, ins->dreg+1, mips_at, 4);
@@ -4142,7 +4154,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 #endif
                        break;
                case OP_STORER4_MEMINDEX:
-                       mips_addiu (code, mips_at, ins->inst_basereg, ins->sreg2);
+                       mips_addu (code, mips_at, ins->inst_destbasereg, ins->sreg2);
 #if PROMOTE_R4_TO_R8
                        /* Need to convert ins->sreg1 to single-precision first */
                        mips_cvtsd (code, mips_ftemp, ins->sreg1);
@@ -4152,7 +4164,7 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
 #endif
                        break;
                case OP_STORER8_MEMINDEX:
-                       mips_addiu (code, mips_at, ins->inst_basereg, ins->sreg2);
+                       mips_addu (code, mips_at, ins->inst_destbasereg, ins->sreg2);
 #if _MIPS_SIM == _ABIO32
                        mips_swc1 (code, ins->sreg1, mips_at, 0);
                        mips_swc1 (code, ins->sreg1+1, mips_at, 4);
@@ -4377,7 +4389,7 @@ mono_arch_register_lowlevel_calls (void)
 }
 
 void
-mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gboolean run_cctors)
+mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, MonoCodeManager *dyn_code_mp, gboolean run_cctors)
 {
        MonoJumpInfo *patch_info;
 
@@ -4481,9 +4493,7 @@ mono_arch_instrument_prolog (MonoCompile *cfg, void *func, void *p, gboolean ena
 
        mips_load_const (code, mips_a0, cfg->method);
        mips_addiu (code, mips_a1, mips_sp, offset);
-       mips_load_const (code, mips_t9, func);
-       mips_jalr (code, mips_t9, mips_ra);
-       mips_nop (code);
+       mips_call (code, mips_t9, func);
 
        MIPS_LW (code, mips_a0, mips_sp, offset + 0*SIZEOF_REGISTER);
        MIPS_LW (code, mips_a1, mips_sp, offset + 1*SIZEOF_REGISTER);
@@ -4644,6 +4654,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        MonoMethodSignature *sig;
        MonoInst *inst;
        int alloc_size, pos, i;
+       int alloc2_size = 0;
        guint8 *code;
        CallInfo *cinfo;
        int tracing = 0;
@@ -4696,26 +4707,37 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        fregs_to_save |= (fregs_to_save << 1);
 #endif
 #endif
+       /* If the stack size is too big, save 1024 bytes to start with
+        * so the prologue can use imm16(reg) addressing, then allocate
+        * the rest of the frame.
+        */
+       if (alloc_size > ((1 << 15) - 1024)) {
+               alloc2_size = alloc_size - 1024;
+               alloc_size = 1024;
+       }
        if (alloc_size) {
-               if (mips_is_imm16 (-alloc_size)) {
-                       mips_addiu (code, mips_sp, mips_sp, -alloc_size);
-               } else {
-                       mips_load_const (code, mips_at, -alloc_size);
-                       mips_addu (code, mips_sp, mips_sp, mips_at);
-               }
+               g_assert (mips_is_imm16 (-alloc_size));
+               mips_addiu (code, mips_sp, mips_sp, -alloc_size);
        }
 
-       if ((cfg->flags & MONO_CFG_HAS_CALLS) || ALWAYS_SAVE_RA)
-               mips_sw (code, mips_ra, mips_sp, alloc_size + MIPS_RET_ADDR_OFFSET);
+       if ((cfg->flags & MONO_CFG_HAS_CALLS) || ALWAYS_SAVE_RA) {
+               int offset = alloc_size + MIPS_RET_ADDR_OFFSET;
+               if (mips_is_imm16(offset))
+                       mips_sw (code, mips_ra, mips_sp, offset);
+               else {
+                       g_assert_not_reached ();
+               }
+       }
 
        /* XXX - optimize this later to not save all regs if LMF constructed */
+       pos = cfg->arch.iregs_offset - alloc2_size;
 
        if (iregs_to_save) {
                /* save used registers in own stack frame (at pos) */
-               pos = cfg->arch.iregs_offset;
                for (i = MONO_MAX_IREGS-1; i >= 0; --i) {
                        if (iregs_to_save & (1 << i)) {
                                g_assert (pos < cfg->stack_usage - sizeof(gpointer));
+                               g_assert (mips_is_imm16(pos));
                                MIPS_SW (code, i, mips_sp, pos);
                                pos += SIZEOF_REGISTER;
                        }
@@ -4724,7 +4746,9 @@ mono_arch_emit_prolog (MonoCompile *cfg)
 #if SAVE_LMF
        if (method->save_lmf) {
                for (i = MONO_MAX_IREGS-1; i >= 0; --i) {
-                       MIPS_SW (code, i, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, iregs[i]));
+                       int offset = lmf_offset + G_STRUCT_OFFSET(MonoLMF, iregs[i]);
+                       g_assert (mips_is_imm16(offset));
+                       MIPS_SW (code, i, mips_sp, offset);
                }
        }
 #endif
@@ -4735,6 +4759,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                for (i = MONO_MAX_FREGS-1; i >= 0; --i) {
                        if (fregs_to_save & (1 << i)) {
                                g_assert (pos < cfg->stack_usage - MIPS_STACK_ALIGNMENT);
+                               g_assert (mips_is_imm16(pos));
                                mips_swc1 (code, i, mips_sp, pos);
                                pos += sizeof (gulong);
                        }
@@ -4743,7 +4768,9 @@ mono_arch_emit_prolog (MonoCompile *cfg)
 #if SAVE_LMF
        if (method->save_lmf) {
                for (i = MONO_MAX_FREGS-1; i >= 0; --i) {
-                       mips_swc1 (code, i, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, fregs[i]));
+                       int offset = lmf_offset + G_STRUCT_OFFSET(MonoLMF, fregs[i]);
+                       g_assert (mips_is_imm16(offset));
+                       mips_swc1 (code, i, mips_sp, offset);
                }
        }
 #endif
@@ -4751,9 +4778,11 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        if (cfg->frame_reg != mips_sp) {
                MIPS_MOVE (code, cfg->frame_reg, mips_sp);
 #if SAVE_LMF
-               if (method->save_lmf)
-                       MIPS_SW (code, cfg->frame_reg, mips_sp,
-                                lmf_offset + G_STRUCT_OFFSET(MonoLMF, iregs[cfg->frame_reg]));
+               if (method->save_lmf) {
+                       int offset = lmf_offset + G_STRUCT_OFFSET(MonoLMF, iregs[cfg->frame_reg]);
+                       g_assert (mips_is_imm16(offset));
+                       MIPS_SW (code, cfg->frame_reg, mips_sp, offset);
+               }
 #endif
        }
 
@@ -4801,7 +4830,9 @@ mono_arch_emit_prolog (MonoCompile *cfg)
 #endif
                        }
                        else if (ainfo->regtype == RegTypeBase) {
-                               mips_lw (code, inst->dreg, mips_sp, cfg->stack_usage + ainfo->offset);
+                               int offset = cfg->stack_usage + ainfo->offset;
+                               g_assert (mips_is_imm16(offset));
+                               mips_lw (code, inst->dreg, mips_sp, offset);
                        } else
                                g_assert_not_reached ();
 
@@ -4863,6 +4894,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                }
                        } else if (ainfo->regtype == RegTypeFP) {
                                g_assert (mips_is_imm16 (inst->inst_offset));
+                               g_assert (mips_is_imm16 (inst->inst_offset+4));
                                if (ainfo->size == 8) {
 #if _MIPS_SIM == _ABIO32
                                        mips_swc1 (code, ainfo->reg, inst->inst_basereg, inst->inst_offset+4);
@@ -4883,6 +4915,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                g_assert (mips_is_imm16 (inst->inst_offset + ainfo->size * sizeof (gpointer)));
                                /* Push the argument registers into their stack slots */
                                for (i = 0; i < ainfo->size; ++i) {
+                                       g_assert (mips_is_imm16(doffset));
                                        MIPS_SW (code, ainfo->reg + i, inst->inst_basereg, doffset);
                                        doffset += SIZEOF_REGISTER;
                                }
@@ -4898,9 +4931,7 @@ mono_arch_emit_prolog (MonoCompile *cfg)
 
        if (method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) {
                mips_load_const (code, mips_a0, cfg->domain);
-               mips_load_const (code, mips_t9, (gpointer)mono_jit_thread_attach);
-               mips_jalr (code, mips_t9, mips_ra);
-               mips_nop (code);
+               mips_call (code, mips_t9, (gpointer)mono_jit_thread_attach);
        }
 
 #if SAVE_LMF
@@ -4913,33 +4944,33 @@ mono_arch_emit_prolog (MonoCompile *cfg)
 #if 0
                        emit_tls_access (code, mips_temp, lmf_pthread_key);
 #endif
-                       if (G_STRUCT_OFFSET (MonoJitTlsData, lmf))
-                               mips_addiu (code, mips_a0, mips_temp, G_STRUCT_OFFSET (MonoJitTlsData, lmf));
+                       if (G_STRUCT_OFFSET (MonoJitTlsData, lmf)) {
+                               int offset = G_STRUCT_OFFSET (MonoJitTlsData, lmf);
+                               g_assert (mips_is_imm16(offset));
+                               mips_addiu (code, mips_a0, mips_temp, offset);
+                       }
                } else {
-#if 0
-                       mips_addiu (code, mips_a0, mips_sp, lmf_offset);
-                       mips_load_const (code, mips_t9, (gpointer)mono_trace_lmf_prolog);
-                       mips_jalr (code, mips_t9, mips_ra);
-                       mips_nop (code);
-#endif
                        /* This can/will clobber the a0-a3 registers */
-                       mips_load_const (code, mips_t9, (gpointer)mono_get_lmf_addr);
-                       mips_jalr (code, mips_t9, mips_ra);
-                       mips_nop (code);
+                       mips_call (code, mips_t9, (gpointer)mono_get_lmf_addr);
                }
 
                /* mips_v0 is the result from mono_get_lmf_addr () (MonoLMF **) */
+               g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, lmf_addr)));
                mips_sw (code, mips_v0, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
                /* new_lmf->previous_lmf = *lmf_addr */
                mips_lw (code, mips_at, mips_v0, 0);
+               g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, previous_lmf)));
                mips_sw (code, mips_at, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
                /* *(lmf_addr) = sp + lmf_offset */
+               g_assert (mips_is_imm16(lmf_offset));
                mips_addiu (code, mips_at, mips_sp, lmf_offset);
                mips_sw (code, mips_at, mips_v0, 0);
 
                /* save method info */
                mips_load_const (code, mips_at, method);
+               g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, method)));
                mips_sw (code, mips_at, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, method));
+               g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, ebp)));
                MIPS_SW (code, mips_sp, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, ebp));
 
                /* save the current IP */
@@ -4948,6 +4979,18 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                mips_sw (code, mips_at, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, eip));
        }
 #endif
+       if (alloc2_size) {
+               if (mips_is_imm16 (-alloc2_size)) {
+                       mips_addu (code, mips_sp, mips_sp, -alloc2_size);
+               }
+               else {
+                       mips_load_const (code, mips_at, -alloc2_size);
+                       mips_addu (code, mips_sp, mips_sp, mips_at);
+               }
+               if (cfg->frame_reg != mips_sp)
+                       MIPS_MOVE (code, cfg->frame_reg, mips_sp);
+               alloc_size += alloc2_size;
+       }
 
        cfg->code_len = code - cfg->native_code;
        g_assert (cfg->code_len < cfg->code_size);
@@ -5014,9 +5057,11 @@ mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolea
        }
 
        mips_addiu (code, mips_sp, mips_sp, -32);
+       g_assert (mips_is_imm16(save_offset));
        switch (save_mode) {
        case SAVE_TWO:
                mips_sw (code, mips_v0, mips_sp, save_offset);
+               g_assert (mips_is_imm16(save_offset + SIZEOF_REGISTER));
                mips_sw (code, mips_v1, mips_sp, save_offset + SIZEOF_REGISTER);
                if (enable_arguments) {
                        MIPS_MOVE (code, mips_a1, mips_v0);
@@ -5033,6 +5078,7 @@ mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolea
                mips_sdc1 (code, mips_f0, mips_sp, save_offset);
                mips_ldc1 (code, mips_f12, mips_sp, save_offset);
                mips_lw (code, mips_a0, mips_sp, save_offset);
+               g_assert (mips_is_imm16(save_offset + SIZEOF_REGISTER));
                mips_lw (code, mips_a1, mips_sp, save_offset + SIZEOF_REGISTER);
                break;
        case SAVE_STRUCT:
@@ -5041,13 +5087,12 @@ mono_arch_instrument_epilog_full (MonoCompile *cfg, void *func, void *p, gboolea
                break;
        }
        mips_load_const (code, mips_a0, cfg->method);
-       mips_load_const (code, mips_t9, func);
-       mips_jalr (code, mips_t9, mips_ra);
-       mips_nop (code);
+       mips_call (code, mips_t9, func);
 
        switch (save_mode) {
        case SAVE_TWO:
                mips_lw (code, mips_v0, mips_sp, save_offset);
+               g_assert (mips_is_imm16(save_offset + SIZEOF_REGISTER));
                mips_lw (code, mips_v1, mips_sp, save_offset + SIZEOF_REGISTER);
                break;
        case SAVE_ONE:
@@ -5073,6 +5118,7 @@ mono_arch_emit_epilog_sub (MonoCompile *cfg, guint8 *code)
        MonoMethod *method = cfg->method;
        int pos = 0, i;
        int max_epilog_size = 16 + 20*4;
+       int alloc2_size = 0;
        guint32 iregs_to_restore;
 #if SAVE_FP_REGS
        guint32 fregs_to_restore;
@@ -5108,10 +5154,17 @@ mono_arch_emit_epilog_sub (MonoCompile *cfg, guint8 *code)
        if (mono_jit_trace_calls != NULL && mono_trace_eval (method)) {
                code = mono_arch_instrument_epilog (cfg, mono_trace_leave_method, code, TRUE);
        }
-       pos = cfg->arch.iregs_offset;
        if (cfg->frame_reg != mips_sp) {
                MIPS_MOVE (code, mips_sp, cfg->frame_reg);
        }
+       /* If the stack frame is really large, deconstruct it in two steps */
+       if (cfg->stack_usage > ((1 << 15) - 1024)) {
+               alloc2_size = cfg->stack_usage - 1024;
+               /* partially deconstruct the stack */
+               mips_load_const (code, mips_at, alloc2_size);
+               mips_addu (code, mips_sp, mips_sp, mips_at);
+       }
+       pos = cfg->arch.iregs_offset - alloc2_size;
 #if SAVE_ALL_REGS
        iregs_to_restore = MONO_ARCH_CALLEE_SAVED_REGS;
 #else
@@ -5120,6 +5173,7 @@ mono_arch_emit_epilog_sub (MonoCompile *cfg, guint8 *code)
        if (iregs_to_restore) {
                for (i = MONO_MAX_IREGS-1; i >= 0; --i) {
                        if (iregs_to_restore & (1 << i)) {
+                               g_assert (mips_is_imm16(pos));
                                MIPS_LW (code, i, mips_sp, pos);
                                pos += SIZEOF_REGISTER;
                        }
@@ -5137,6 +5191,7 @@ mono_arch_emit_epilog_sub (MonoCompile *cfg, guint8 *code)
                for (i = MONO_MAX_FREGS-1; i >= 0; --i) {
                        if (fregs_to_restore & (1 << i)) {
                                g_assert (pos < cfg->stack_usage - MIPS_STACK_ALIGNMENT);
+                               g_assert (mips_is_imm16(pos));
                                mips_lwc1 (code, i, mips_sp, pos);
                                pos += FREG_SIZE
                        }
@@ -5149,8 +5204,10 @@ mono_arch_emit_epilog_sub (MonoCompile *cfg, guint8 *code)
                int lmf_offset = cfg->arch.lmf_offset;
 
                /* t0 = current_lmf->previous_lmf */
+               g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, previous_lmf)));
                mips_lw (code, mips_temp, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, previous_lmf));
                /* t1 = lmf_addr */
+               g_assert (mips_is_imm16(lmf_offset + G_STRUCT_OFFSET(MonoLMF, lmf_addr)));
                mips_lw (code, mips_t1, mips_sp, lmf_offset + G_STRUCT_OFFSET(MonoLMF, lmf_addr));
                /* (*lmf_addr) = previous_lmf */
                mips_sw (code, mips_temp, mips_t1, 0);
@@ -5160,10 +5217,14 @@ mono_arch_emit_epilog_sub (MonoCompile *cfg, guint8 *code)
        /* Restore the fp */
        mips_lw (code, mips_fp, mips_sp, cfg->stack_usage + MIPS_FP_ADDR_OFFSET);
 #endif
-       /* Correct the stack pointer */
-       if ((cfg->flags & MONO_CFG_HAS_CALLS) || ALWAYS_SAVE_RA)
-               mips_lw (code, mips_ra, mips_sp, cfg->stack_usage + MIPS_RET_ADDR_OFFSET);
-       mips_addiu (code, mips_sp, mips_sp, cfg->stack_usage);
+       /* Restore ra */
+       if ((cfg->flags & MONO_CFG_HAS_CALLS) || ALWAYS_SAVE_RA) {
+               g_assert (mips_is_imm16(cfg->stack_usage - alloc2_size + MIPS_RET_ADDR_OFFSET));
+               mips_lw (code, mips_ra, mips_sp, cfg->stack_usage - alloc2_size + MIPS_RET_ADDR_OFFSET);
+       }
+       /* Restore the stack pointer */
+       g_assert (mips_is_imm16(cfg->stack_usage - alloc2_size));
+       mips_addiu (code, mips_sp, mips_sp, cfg->stack_usage - alloc2_size);
 
        /* Caller will emit either return or tail-call sequence */
 
@@ -5207,6 +5268,8 @@ exception_id_by_name (const char *name)
                return MONO_EXC_NULL_REF;
        if (strcmp (name, "ArrayTypeMismatchException") == 0)
                return MONO_EXC_ARRAY_TYPE_MISMATCH;
+       if (strcmp (name, "ArgumentException") == 0)
+               return MONO_EXC_ARGUMENT;
        g_error ("Unknown intrinsic exception %s\n", name);
        return 0;
 }
@@ -5580,9 +5643,9 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
                MonoIMTCheckItem *item = imt_entries [i];
 
                item->code_target = code;
-               mips_load_const (code, mips_temp, (gsize)item->key);
                if (item->is_equals) {
                        if (item->check_target_idx) {
+                               mips_load_const (code, mips_temp, (gsize)item->key);
                                item->jmp_code = code;
                                mips_bne (code, mips_temp, MONO_ARCH_IMT_REG, 0);
                                mips_nop (code);
@@ -5598,6 +5661,7 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
                                mips_nop (code);
                        } else {
                                if (fail_tramp) {
+                                       mips_load_const (code, mips_temp, (gsize)item->key);
                                        patch = code;
                                        mips_bne (code, mips_temp, MONO_ARCH_IMT_REG, 0);
                                        mips_nop (code);
@@ -5638,10 +5702,10 @@ mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckI
                        }
                } else {
                        mips_load_const (code, mips_temp, (gulong)item->key);
-                       mips_slt (code, mips_temp, mips_temp, MONO_ARCH_IMT_REG);
+                       mips_slt (code, mips_temp, MONO_ARCH_IMT_REG, mips_temp);
 
                        item->jmp_code = code;
-                       mips_bne (code, mips_temp, mips_zero, 0);
+                       mips_beq (code, mips_temp, mips_zero, 0);
                        mips_nop (code);
                }
        }