]> git.ipfire.org Git - thirdparty/LuaJIT.git/commitdiff
MIPS: Switch to dual-number mode. Fix soft-float interpreter.
authorMike Pall <mike>
Fri, 29 Jan 2016 06:03:36 +0000 (07:03 +0100)
committerMike Pall <mike>
Fri, 29 Jan 2016 06:03:36 +0000 (07:03 +0100)
src/Makefile
src/lj_arch.h
src/lj_dispatch.h
src/lj_frame.h
src/lj_vm.h
src/lj_vmmath.c
src/vm_mips.dasc

index 6d9a1053ec1a4e8274319c5469b3db20c361dda5..1df39dc160edb9364233162f929c91a87debea96 100644 (file)
@@ -388,6 +388,11 @@ DASM_XFLAGS=
 DASM_AFLAGS=
 DASM_ARCH= $(TARGET_LJARCH)
 
+ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH)))
+  DASM_AFLAGS+= -D ENDIAN_LE
+else
+  DASM_AFLAGS+= -D ENDIAN_BE
+endif
 ifneq (,$(findstring LJ_ARCH_BITS 64,$(TARGET_TESTARCH)))
   DASM_AFLAGS+= -D P64
 endif
index a114bdda53fee44118922eb61c1ba75dd0c6737d..7096ad5e4abcfd28a1578b860322e0501957a441 100644 (file)
 #define LJ_ARCH_NAME           "mips"
 #define LJ_ARCH_ENDIAN         LUAJIT_BE
 #endif
+
+#if !defined(LJ_ARCH_HASFPU)
+#ifdef __mips_soft_float
+#define LJ_ARCH_HASFPU         0
+#else
+#define LJ_ARCH_HASFPU         1
+#endif
+#endif
+
+/* Temporarily disable features until the code has been merged. */
+#if !LJ_ARCH_HASFPU
+#define LJ_ARCH_NOJIT          1
+#endif
+#if !defined(LUAJIT_NO_UNWIND) && __GNU_COMPACT_EH__
+#define LUAJIT_NO_UNWIND       1
+#endif
+
+#if !defined(LJ_ABI_SOFTFP)
+#ifdef __mips_soft_float
+#define LJ_ABI_SOFTFP          1
+#else
+#define LJ_ABI_SOFTFP          0
+#endif
+#endif
+
 #define LJ_ARCH_BITS           32
 #define LJ_TARGET_MIPS         1
 #define LJ_TARGET_EHRETREG     4
 #define LJ_TARGET_MASKSHIFT    1
 #define LJ_TARGET_MASKROT      1
 #define LJ_TARGET_UNIFYROT     2       /* Want only IR_BROR. */
-#define LJ_ARCH_NUMMODE                LJ_NUMMODE_SINGLE
-
-#if !defined(LJ_ARCH_HASFPU) && defined(__mips_soft_float)
-#define LJ_ARCH_HASFPU         0
-#endif
-#if !defined(LJ_ABI_SOFTFP) && defined(__mips_soft_float)
-#define LJ_ABI_SOFTFP          1
-#endif
+#define LJ_ARCH_NUMMODE                LJ_NUMMODE_DUAL
 
 #if _MIPS_ARCH_MIPS32R2
 #define LJ_ARCH_VERSION                20
index 73d00ec00cce845cdd63c2993fc1c6e52d686f49..5844115b1fe9a6c0c411f900041d130488c38112 100644 (file)
 
 #if LJ_TARGET_MIPS
 /* Need our own global offset table for the dreaded MIPS calling conventions. */
+
+#ifndef _LJ_VM_H
+LJ_ASMF int32_t LJ_FASTCALL lj_vm_modi(int32_t a, int32_t b);
+#endif
+
 #if LJ_SOFTFP
+#ifndef _LJ_IRCALL_H
 extern double __adddf3(double a, double b);
 extern double __subdf3(double a, double b);
 extern double __muldf3(double a, double b);
 extern double __divdf3(double a, double b);
-extern void __ledf2(double a, double b);
-extern double __floatsidf(int32_t a);
-extern int32_t __fixdfsi(double a);
-
-#define SFGOTDEF(_) \
-  _(lj_num2bit) _(sqrt) _(__adddf3) _(__subdf3) _(__muldf3) _(__divdf3) _(__ledf2) \
-  _(__floatsidf) _(__fixdfsi)
+#endif
+#define SFGOTDEF(_)    _(sqrt) _(__adddf3) _(__subdf3) _(__muldf3) _(__divdf3)
 #else
 #define SFGOTDEF(_)
 #endif
@@ -43,14 +44,14 @@ extern int32_t __fixdfsi(double a);
 #define GOTDEF(_) \
   _(floor) _(ceil) _(trunc) _(log) _(log10) _(exp) _(sin) _(cos) _(tan) \
   _(asin) _(acos) _(atan) _(sinh) _(cosh) _(tanh) _(frexp) _(modf) _(atan2) \
-  _(pow) _(fmod) _(ldexp) \
+  _(pow) _(fmod) _(ldexp) _(lj_vm_modi) \
   _(lj_dispatch_call) _(lj_dispatch_ins) _(lj_dispatch_stitch) \
   _(lj_dispatch_profile) _(lj_err_throw) \
   _(lj_ffh_coroutine_wrap_err) _(lj_func_closeuv) _(lj_func_newL_gc) \
   _(lj_gc_barrieruv) _(lj_gc_step) _(lj_gc_step_fixtop) _(lj_meta_arith) \
   _(lj_meta_call) _(lj_meta_cat) _(lj_meta_comp) _(lj_meta_equal) \
   _(lj_meta_for) _(lj_meta_istype) _(lj_meta_len) _(lj_meta_tget) \
-  _(lj_meta_tset) _(lj_state_growstack) _(lj_strfmt_num) \
+  _(lj_meta_tset) _(lj_state_growstack) _(lj_strfmt_number) \
   _(lj_str_new) _(lj_tab_dup) _(lj_tab_get) _(lj_tab_getinth) _(lj_tab_len) \
   _(lj_tab_new) _(lj_tab_newkey) _(lj_tab_next) _(lj_tab_reasize) \
   _(lj_tab_setinth) _(lj_buf_putstr_reverse) _(lj_buf_putstr_lower) \
index aa3ab20bbf157c62b3c78ff4d6fee98a0ef96015..07b36cdf282347e19ded6dac6f5bf1df8eade2f8 100644 (file)
@@ -228,13 +228,13 @@ enum { LJ_CONT_TAILCALL, LJ_CONT_FFI_CALLBACK };  /* Special continuations. */
 #define CFRAME_SIZE            112
 #define CFRAME_SHIFT_MULTRES   3
 #else
-#define CFRAME_OFS_ERRF                100
-#define CFRAME_OFS_NRES                96
-#define CFRAME_OFS_PREV                92
-#define CFRAME_OFS_L           88
-#define CFRAME_OFS_PC          44
+#define CFRAME_OFS_ERRF                76
+#define CFRAME_OFS_NRES                72
+#define CFRAME_OFS_PREV                68
+#define CFRAME_OFS_L           64
+#define CFRAME_OFS_PC          20
 #define CFRAME_OFS_MULTRES     16
-#define CFRAME_SIZE            88
+#define CFRAME_SIZE            64
 #define CFRAME_SHIFT_MULTRES   3
 #endif
 #else
index cb76d7a7002f3dbbcbc3ac25590ee39821828d62..9afb53abe36533849d99f271d3fea46a003e9880 100644 (file)
@@ -66,6 +66,9 @@ LJ_ASMF double lj_vm_log2(double);
 #else
 #define lj_vm_log2     log2
 #endif
+#if !(defined(_LJ_DISPATCH_H) && LJ_TARGET_MIPS)
+LJ_ASMF int32_t LJ_FASTCALL lj_vm_modi(int32_t, int32_t);
+#endif
 
 #if LJ_HASJIT
 #if LJ_TARGET_X86ORX64
@@ -90,7 +93,6 @@ LJ_ASMF double lj_vm_exp2(double);
 #else
 #define lj_vm_exp2     exp2
 #endif
-LJ_ASMF int32_t LJ_FASTCALL lj_vm_modi(int32_t, int32_t);
 #if LJ_HASFFI
 LJ_ASMF int lj_vm_errno(void);
 #endif
index ecad2950d93f52aa6e433ae52afc0678ea951e4c..90d8a78ef6536bbf7a38609990a1ad3cfb359108 100644 (file)
@@ -57,6 +57,20 @@ double lj_vm_foldarith(double x, double y, int op)
   }
 }
 
+#if (LJ_HASJIT && !(LJ_TARGET_ARM || LJ_TARGET_ARM64 || LJ_TARGET_PPC)) || LJ_TARGET_MIPS
+int32_t LJ_FASTCALL lj_vm_modi(int32_t a, int32_t b)
+{
+  uint32_t y, ua, ub;
+  lua_assert(b != 0);  /* This must be checked before using this function. */
+  ua = a < 0 ? (uint32_t)-a : (uint32_t)a;
+  ub = b < 0 ? (uint32_t)-b : (uint32_t)b;
+  y = ua % ub;
+  if (y != 0 && (a^b) < 0) y = y - ub;
+  if (((int32_t)y^b) < 0) y = (uint32_t)-(int32_t)y;
+  return (int32_t)y;
+}
+#endif
+
 #if LJ_HASJIT
 
 #ifdef LUAJIT_NO_LOG2
@@ -73,20 +87,6 @@ double lj_vm_exp2(double a)
 }
 #endif
 
-#if !(LJ_TARGET_ARM || LJ_TARGET_ARM64 || LJ_TARGET_PPC)
-int32_t LJ_FASTCALL lj_vm_modi(int32_t a, int32_t b)
-{
-  uint32_t y, ua, ub;
-  lua_assert(b != 0);  /* This must be checked before using this function. */
-  ua = a < 0 ? (uint32_t)-a : (uint32_t)a;
-  ub = b < 0 ? (uint32_t)-b : (uint32_t)b;
-  y = ua % ub;
-  if (y != 0 && (a^b) < 0) y = y - ub;
-  if (((int32_t)y^b) < 0) y = (uint32_t)-(int32_t)y;
-  return (int32_t)y;
-}
-#endif
-
 #if !LJ_TARGET_X86ORX64
 /* Unsigned x^k. */
 static double lj_vm_powui(double x, uint32_t k)
index 0dba12931641903621a8eccfe890e39bf5b54199..8c307d8ddc00a12de99433a006f840801d2ce097 100644 (file)
 |.define DISPATCH,     r19     // Opcode dispatch table.
 |.define LREG,         r20     // Register holding lua_State (also in SAVE_L).
 |.define MULTRES,      r21     // Size of multi-result: (nresults+1)*8.
-|// NYI: r22 currently unused.
 |
 |.define JGL,          r30     // On-trace: global_State + 32768.
 |
 |// Constants for type-comparisons, stores and conversions. C callee-save.
+|.define TISNUM,       r22
 |.define TISNIL,       r30
 |.if FPU
 |.define TOBIT,                f30     // 2^52 + 2^51.
 |.define TMP2,         r14
 |.define TMP3,         r15
 |
-|.if not FPU
-|.define SFT1,         r2
-|.define SFT2,         r3
-|.define SFT3,         r4
-|.define SFT4,         r5
-|.endif
-|
 |// Calling conventions.
 |.define CFUNCADDR,    r25
 |.define CARG1,                r4
 |.define CRET1,                r2
 |.define CRET2,                r3
 |
+|.if ENDIAN_LE
+|.define SFRETLO,      CRET1
+|.define SFRETHI,      CRET2
+|.define SFARG1LO,     CARG1
+|.define SFARG1HI,     CARG2
+|.define SFARG2LO,     CARG3
+|.define SFARG2HI,     CARG4
+|.else
+|.define SFRETLO,      CRET2
+|.define SFRETHI,      CRET1
+|.define SFARG1LO,     CARG2
+|.define SFARG1HI,     CARG1
+|.define SFARG2LO,     CARG4
+|.define SFARG2HI,     CARG3
+|.endif
+|
 |.if FPU
 |.define FARG1,                f12
 |.define FARG2,                f14
@@ -84,6 +93,7 @@
 |
 |// Stack layout while in interpreter. Must match with lj_frame.h.
 |.if FPU               // MIPS32 hard-float.
+|
 |.define CFRAME_SPACE, 112     // Delta for sp.
 |
 |.define SAVE_ERRF,    124(sp) // 32 bit C frame info.
 |//----- 8 byte aligned, ^^^^ 16 byte register save area, owned by interpreter.
 |.define SAVE_GPR_,    72      // .. 72+10*4: 32 bit GPR saves.
 |.define SAVE_FPR_,    24      // .. 24+6*8: 64 bit FPR saves.
-|.define SAVE_PC,      20(sp)
-|.define ARG5,         16(sp)
-|.define CSAVE_4,      12(sp)
-|.define CSAVE_3,      8(sp)
-|.define CSAVE_2,      4(sp)
-|.define CSAVE_1,      0(sp)
-|//----- 8 byte aligned, ^^^^ 16 byte register save area, owned by callee.
-|
-|.define ARG5_OFS,     16
-|.define SAVE_MULTRES, ARG5
 |
-|//-----------------------------------------------------------------------
-|.else                         // MIPS32 soft-float.
+|.else                 // MIPS32 soft-float
 |
-|.define CFRAME_SPACE, 88      // Delta for sp.
+|.define CFRAME_SPACE, 64      // Delta for sp.
 |
-|.define SAVE_ERRF,    100(sp) // 32 bit C frame info.
-|.define SAVE_NRES,    96(sp)
-|.define SAVE_CFRAME,  92(sp)
-|.define SAVE_L,       88(sp)
+|.define SAVE_ERRF,    76(sp)  // 32 bit C frame info.
+|.define SAVE_NRES,    72(sp)
+|.define SAVE_CFRAME,  68(sp)
+|.define SAVE_L,       64(sp)
 |//----- 8 byte aligned, ^^^^ 16 byte register save area, owned by interpreter.
-|.define SAVE_GPR_,    48      // .. 48+10*4: 32 bit GPR saves.
-|.define SAVE_PC,      44(sp)
-|.define TEMP_SAVE_6,  40(sp)
-|.define TEMP_SAVE_5,  36(sp)
-|.define TEMP_SAVE_4,  32(sp)
-|.define TEMP_SAVE_3,  28(sp)
-|.define TEMP_SAVE_2,  24(sp)
-|.define TEMP_SAVE_1,  20(sp)
-|//----- 8 byte aligned, ^^^^ 24 byte register save area, owned by caller.
+|.define SAVE_GPR_,    24      // .. 24+10*4: 32 bit GPR saves.
+|
+|.endif
+|
+|.define SAVE_PC,      20(sp)
 |.define ARG5,         16(sp)
 |.define CSAVE_4,      12(sp)
 |.define CSAVE_3,      8(sp)
 |.define ARG5_OFS,     16
 |.define SAVE_MULTRES, ARG5
 |
-|.endif
-|
 |//-----------------------------------------------------------------------
 |
 |.macro saveregs
 |//-----------------------------------------------------------------------
 |
 |// Endian-specific defines.
-|.define FRAME_PC,     LJ_ENDIAN_SELECT(-4,-8)
-|.define FRAME_FUNC,   LJ_ENDIAN_SELECT(-8,-4)
-|.define HI,           LJ_ENDIAN_SELECT(4,0)
-|.define LO,           LJ_ENDIAN_SELECT(0,4)
-|.define OFS_RD,       LJ_ENDIAN_SELECT(2,0)
-|.define OFS_RA,       LJ_ENDIAN_SELECT(1,2)
-|.define OFS_OP,       LJ_ENDIAN_SELECT(0,3)
+|.if ENDIAN_LE
+|.define FRAME_PC,     -4
+|.define FRAME_FUNC,   -8
+|.define HI,           4
+|.define LO,           0
+|.define OFS_RD,       2
+|.define OFS_RA,       1
+|.define OFS_OP,       0
+|.else
+|.define FRAME_PC,     -8
+|.define FRAME_FUNC,   -4
+|.define HI,           0
+|.define LO,           4
+|.define OFS_RD,       0
+|.define OFS_RA,       2
+|.define OFS_OP,       3
+|.endif
 |
 |// Instruction decode.
 |.macro decode_OP1, dst, ins; andi dst, ins, 0xff; .endmacro
 |.macro call_extern; jalr CFUNCADDR; .endmacro
 |.macro jmp_extern; jr CFUNCADDR; .endmacro
 |
-|// Converts int from given reg to double, result in CRET1 and CRET2 regs.
-|.if not FPU
-|.macro cvti2d, arg
-|   load_got __floatsidf
-|   call_extern
-|.  move CARG1, arg
-|.endmacro
-|.endif
-|
-|// Loads a double-word floating-point value.
-|.macro load_double, fpr, gpr1, gpr2, src
-|.if FPU
-|  ldc1 fpr, src
-|.else
-|  lw gpr1, src
-|  lw gpr2, 4+src
-|.endif
-|.endmacro
-|
-|// Stores a double-word floating-point value.
-|.macro store_double, fpr, gpr1, gpr2, dst
-|.if FPU
-|  sdc1 fpr, dst
-|.else
-|  sw gpr1, dst
-|  sw gpr2, 4+dst
-|.endif
-|.endmacro
-|
-|// Loads the first double-word floating-point argument.
-|.macro load_farg1, src
-|  load_double FARG1, CARG1, CARG2, src
-|.endmacro
-|
-|// Loads the second double-word floating-point argument.
-|.macro load_farg2, src
-|  load_double FARG2, CARG3, CARG4, src
-|.endmacro
-|
-|.macro load_double1, src
-|  load_double f0, SFT1, SFT2, src
-|.endmacro
-|
-|.macro store_double1, dst
-|  store_double f0, SFT1, SFT2, dst
-|.endmacro
-|
-|.macro load_double2, src
-|  load_double f2, SFT3, SFT4, src
-|.endmacro
-|
-|.macro store_double2, dst
-|  store_double f2, SFT3, SFT4, dst
-|.endmacro
-|
 |.macro hotcheck, delta, target
 |  srl TMP1, PC, 1
 |  andi TMP1, TMP1, 126
@@ -463,9 +411,11 @@ static void build_subroutines(BuildCtx *ctx)
   |.   sll TMP2, TMP2, 3
   |1:
   |  addiu TMP1, TMP1, -8
-  |   load_double1 0(RA)
+  |   lw SFRETHI, HI(RA)
+  |    lw SFRETLO, LO(RA)
   |    addiu RA, RA, 8
-  |   store_double1 0(BASE)
+  |   sw SFRETHI, HI(BASE)
+  |    sw SFRETLO, LO(BASE)
   |  bnez TMP1, <1
   |.  addiu BASE, BASE, 8
   |
@@ -535,6 +485,7 @@ static void build_subroutines(BuildCtx *ctx)
   |->vm_unwind_ff_eh:                  // Landing pad for external unwinder.
   |  lw L, SAVE_L
   |     .FPU lui TMP3, 0x59c0          // TOBIT = 2^52 + 2^51 (float).
+  |     li TISNUM, LJ_TISNUM           // Setup type comparison constants.
   |     li TISNIL, LJ_TNIL
   |  lw BASE, L->base
   |   lw DISPATCH, L->glref            // Setup pointer to dispatch table.
@@ -605,6 +556,7 @@ static void build_subroutines(BuildCtx *ctx)
   |  sw L, DISPATCH_GL(cur_L)(DISPATCH)
   |  move RA, BASE
   |   lw BASE, L->base
+  |     li TISNUM, LJ_TISNUM           // Setup type comparison constants.
   |   lw TMP1, L->top
   |  lw PC, FRAME_PC(BASE)
   |     .FPU  lui TMP3, 0x59c0         // TOBIT = 2^52 + 2^51 (float).
@@ -649,6 +601,7 @@ static void build_subroutines(BuildCtx *ctx)
   |3:  // Entry point for vm_cpcall/vm_resume (BASE = base, PC = ftype).
   |  sw L, DISPATCH_GL(cur_L)(DISPATCH)
   |  lw TMP2, L->base                  // TMP2 = old base (used in vmeta_call).
+  |     li TISNUM, LJ_TISNUM           // Setup type comparison constants.
   |     .FPU lui TMP3, 0x59c0          // TOBIT = 2^52 + 2^51 (float).
   |   lw TMP1, L->top
   |     .FPU mtc1 TMP3, TOBIT
@@ -737,7 +690,8 @@ static void build_subroutines(BuildCtx *ctx)
   |->cont_cat:                         // RA = resultptr, RB = meta base
   |  lw INS, -4(PC)
   |   addiu CARG2, RB, -16
-  |   load_double1 0(RA)
+  |  lw SFRETHI, HI(RA)
+  |    lw SFRETLO, LO(RA)
   |  decode_RB8a MULTRES, INS
   |   decode_RA8a RA, INS
   |  decode_RB8b MULTRES
@@ -745,21 +699,13 @@ static void build_subroutines(BuildCtx *ctx)
   |  addu TMP1, BASE, MULTRES
   |   sw BASE, L->base
   |   subu CARG3, CARG2, TMP1
-  |.if FPU
-  |  bne TMP1, CARG2, ->BC_CAT_Z
-  |.  sdc1 f0, 0(CARG2)
-  |  addu RA, BASE, RA
-  |  b ->cont_nop
-  |.  sdc1 f0, 0(RA)
-  |.else
-  |  sw SFT1, 0(CARG2)
+  |  sw SFRETHI, HI(CARG2)
   |  bne TMP1, CARG2, ->BC_CAT_Z
-  |.  sw SFT2, 4(CARG2)
+  |.  sw SFRETLO, LO(CARG2)
   |  addu RA, BASE, RA
-  |  sw SFT1, 0(RA)
+  |  sw SFRETHI, HI(RA)
   |  b ->cont_nop
-  |.  sw SFT2, 4(RA)
-  |.endif
+  |.  sw SFRETLO, LO(RA)
   |
   |//-- Table indexing metamethods -----------------------------------------
   |
@@ -782,19 +728,9 @@ static void build_subroutines(BuildCtx *ctx)
   |.  sw TMP1, HI(CARG3)
   |
   |->vmeta_tgetb:                      // TMP0 = index
-  |.if FPU
-  |  mtc1 TMP0, f0
-  |  cvt.d.w f0, f0
-  |  addiu CARG3, DISPATCH, DISPATCH_GL(tmptv)
-  |  sdc1 f0, 0(CARG3)
-  |.else
-  |  sw CARG2, TEMP_SAVE_1 //needed to be saved because it's used later in lj_meta_tget
-  |  cvti2d TMP0
   |  addiu CARG3, DISPATCH, DISPATCH_GL(tmptv)
-  |  sw CRET1, 0(CARG3)
-  |  sw CRET2, 4(CARG3)
-  |  lw CARG2, TEMP_SAVE_1
-  |.endif
+  |  sw TMP0, LO(CARG3)
+  |  sw TISNUM, HI(CARG3)
   |
   |->vmeta_tgetv:
   |1:
@@ -806,9 +742,11 @@ static void build_subroutines(BuildCtx *ctx)
   |  // Returns TValue * (finished) or NULL (metamethod).
   |  beqz CRET1, >3
   |.  addiu TMP1, BASE, -FRAME_CONT
-  |  load_double2 0(CRET1)
+  |  lw SFARG1HI, HI(CRET1)
+  |   lw SFARG2HI, LO(CRET1)
   |  ins_next1
-  |   store_double2 0(RA)
+  |  sw SFARG1HI, HI(RA)
+  |   sw SFARG2HI, LO(RA)
   |  ins_next2
   |
   |3:  // Call __index metamethod.
@@ -825,16 +763,11 @@ static void build_subroutines(BuildCtx *ctx)
   |  call_intern lj_tab_getinth                // (GCtab *t, int32_t key)
   |.  nop
   |  // Returns cTValue * or NULL.
-  |  beqz CRET1, >1
-  |.  nop
-  |.if FPU
-  |  b ->BC_TGETR_Z
-  |.  ldc1 f0, 0(CRET1)
-  |.else
-  |  lw SFT1, 0(CRET1)
+  |  beqz CRET1, ->BC_TGETR_Z
+  |.  move SFARG2HI, TISNIL
+  |  lw SFARG2HI, HI(CRET1)
   |  b ->BC_TGETR_Z
-  |.  lw SFT2, 4(CRET1)
-  |.endif
+  |.  lw SFARG2LO, LO(CRET1)
   |
   |//-----------------------------------------------------------------------
   |
@@ -857,19 +790,9 @@ static void build_subroutines(BuildCtx *ctx)
   |.  sw TMP1, HI(CARG3)
   |
   |->vmeta_tsetb:                      // TMP0 = index
-  |.if  FPU
-  |  mtc1 TMP0, f0
-  |  cvt.d.w f0, f0
-  |  addiu CARG3, DISPATCH, DISPATCH_GL(tmptv)
-  |  sdc1 f0, 0(CARG3)
-  |.else
-  |  sw CARG2, TEMP_SAVE_1
-  |  cvti2d TMP0
   |  addiu CARG3, DISPATCH, DISPATCH_GL(tmptv)
-  |  sw CRET1, 0(CARG3)
-  |  sw CRET2, 4(CARG3)
-  |  lw CARG2, TEMP_SAVE_1
-  |.endif
+  |  sw TMP0, LO(CARG3)
+  |  sw TISNUM, HI(CARG3)
   |
   |->vmeta_tsetv:
   |1:
@@ -879,17 +802,13 @@ static void build_subroutines(BuildCtx *ctx)
   |  call_intern lj_meta_tset          // (lua_State *L, TValue *o, TValue *k)
   |.  move CARG1, L
   |  // Returns TValue * (finished) or NULL (metamethod).
-  |.if FPU
-  |  beqz CRET1, >3
-  |.  ldc1 f2, 0(RA)
-  |.else
-  |  lw SFT3, 0(RA)
+  |  lw SFARG1HI, HI(RA)
   |  beqz CRET1, >3
-  |.  lw SFT4, 4(RA)
-  |.endif
+  |.  lw SFARG1LO, LO(RA)
   |  // NOBARRIER: lj_meta_tset ensures the table is not black.
   |  ins_next1
-  |   store_double2 0(CRET1)
+  |  sw SFARG1HI, HI(CRET1)
+  |   sw SFARG1LO, LO(CRET1)
   |  ins_next2
   |
   |3:  // Call __newindex metamethod.
@@ -899,7 +818,8 @@ static void build_subroutines(BuildCtx *ctx)
   |  sw PC, -16+HI(BASE)               // [cont|PC]
   |   subu PC, BASE, TMP1
   |  lw LFUNC:RB, FRAME_FUNC(BASE)     // Guaranteed to be a function here.
-  |  store_double2 16(BASE)                    // Copy value to third argument.
+  |  sw SFARG1HI, 16+HI(BASE)          // Copy value to third argument.
+  |   sw SFARG1LO, 16+LO(BASE)
   |  b ->vm_call_dispatch_f
   |.  li NARGS8:RC, 24                 // 3 args for func(t, k, v)
   |
@@ -916,7 +836,9 @@ static void build_subroutines(BuildCtx *ctx)
   |//-- Comparison metamethods ---------------------------------------------
   |
   |->vmeta_comp:
-  |  // CARG2, CARG3 are already set by BC_ISLT/BC_ISGE/BC_ISLE/BC_ISGT.
+  |  // RA/RD point to o1/o2.
+  |  move CARG2, RA
+  |  move CARG3, RD
   |  load_got lj_meta_comp
   |  addiu PC, PC, -4
   |  sw BASE, L->base
@@ -942,17 +864,13 @@ static void build_subroutines(BuildCtx *ctx)
   |
   |->cont_ra:                          // RA = resultptr
   |  lbu TMP1, -4+OFS_RA(PC)
-  |   load_double1 0(RA)
+  |   lw SFRETHI, HI(RA)
+  |    lw SFRETLO, LO(RA)
   |  sll TMP1, TMP1, 3
   |  addu TMP1, BASE, TMP1
-  |.if FPU
-  |  b ->cont_nop
-  |.  sdc1 f0, 0(TMP1)
-  |.else
-  |   sw SFT1, 0(TMP1)
+  |   sw SFRETHI, HI(TMP1)
   |  b ->cont_nop
-  |.  sw SFT2, 4(TMP1)
-  |.endif
+  |.   sw SFRETLO, LO(TMP1)
   |
   |->cont_condt:                       // RA = resultptr
   |  lw TMP0, HI(RA)
@@ -967,8 +885,11 @@ static void build_subroutines(BuildCtx *ctx)
   |.  addiu TMP2, AT, -1               // Branch if result is false.
   |
   |->vmeta_equal:
-  |  // CARG2, CARG3, CARG4 are already set by BC_ISEQV/BC_ISNEV.
+  |  // SFARG1LO/SFARG2LO point to o1/o2. TMP0 is set to 0/1.
   |  load_got lj_meta_equal
+  |   move CARG2, SFARG1LO
+  |   move CARG3, SFARG2LO
+  |   move CARG4, TMP0
   |  addiu PC, PC, -4
   |   sw BASE, L->base
   |   sw PC, SAVE_PC
@@ -1007,29 +928,16 @@ static void build_subroutines(BuildCtx *ctx)
   |//-- Arithmetic metamethods ---------------------------------------------
   |
   |->vmeta_unm:
-  |  b ->vmeta_arith
-  |.  move CARG4, CARG3
-  |
-  |->vmeta_arith_vn:
-  |  addu CARG3, BASE, RB
-  |  b ->vmeta_arith
-  |.  addu CARG4, KBASE, RC
-  |
-  |->vmeta_arith_nv:
-  |  addu CARG4, BASE, RB
-  |  b ->vmeta_arith
-  |.  addu CARG3, KBASE, RC
-  |
-  |->vmeta_arith_vv:
-  |  addu CARG3, BASE, RB
-  |   addu CARG4, BASE, RC
+  |  move RC, RB
   |
   |->vmeta_arith:
   |  load_got lj_meta_arith
   |  decode_OP1 TMP0, INS
   |   sw BASE, L->base
-  |   sw PC, SAVE_PC
   |  move CARG2, RA
+  |   sw PC, SAVE_PC
+  |  move CARG3, RB
+  |  move CARG4, RC
   |  sw TMP0, ARG5
   |  call_intern lj_meta_arith  // (lua_State *L, TValue *ra,*rb,*rc, BCReg op)
   |.  move CARG1, L
@@ -1137,40 +1045,52 @@ static void build_subroutines(BuildCtx *ctx)
   |
   |.macro .ffunc_1, name
   |->ff_ .. name:
+  |  lw SFARG1HI, HI(BASE)
   |  beqz NARGS8:RC, ->fff_fallback
-  |.  lw CARG3, HI(BASE)
-  |    lw CARG1, LO(BASE)
+  |.  lw SFARG1LO, LO(BASE)
   |.endmacro
   |
   |.macro .ffunc_2, name
   |->ff_ .. name:
   |  sltiu AT, NARGS8:RC, 16
-  |   lw CARG3, HI(BASE)
+  |   lw SFARG1HI, HI(BASE)
   |  bnez AT, ->fff_fallback
-  |.   lw CARG4, 8+HI(BASE)
-  |   lw CARG1, LO(BASE)
-  |    lw CARG2, 8+LO(BASE)
+  |.   lw SFARG2HI, 8+HI(BASE)
+  |   lw SFARG1LO, LO(BASE)
+  |    lw SFARG2LO, 8+LO(BASE)
   |.endmacro
   |
   |.macro .ffunc_n, name       // Caveat: has delay slot!
   |->ff_ .. name:
-  |  lw CARG3, HI(BASE)
-  |   load_farg1 0(BASE)
+  |  lw SFARG1HI, HI(BASE)
+  |.if FPU
+  |   ldc1 FARG1, 0(BASE)
+  |.else
+  |   lw SFARG1LO, LO(BASE)
+  |.endif
   |  beqz NARGS8:RC, ->fff_fallback
-  |.  sltiu AT, CARG3, LJ_TISNUM
+  |.  sltiu AT, SFARG1HI, LJ_TISNUM
   |  beqz AT, ->fff_fallback
   |.endmacro
   |
   |.macro .ffunc_nn, name      // Caveat: has delay slot!
   |->ff_ .. name:
   |  sltiu AT, NARGS8:RC, 16
-  |   lw CARG3, HI(BASE)
+  |   lw SFARG1HI, HI(BASE)
   |  bnez AT, ->fff_fallback
-  |.  lw CARG4, 8+HI(BASE)
-  |  sltiu TMP0, CARG3, LJ_TISNUM
-  |  sltiu TMP1, CARG4, LJ_TISNUM
-  |  load_farg1 0(BASE)
-  |  load_farg2 8(BASE)
+  |.  lw SFARG2HI, 8+HI(BASE)
+  |  sltiu TMP0, SFARG1HI, LJ_TISNUM
+  |.if FPU
+  |   ldc1 FARG1, 0(BASE)
+  |.else
+  |   lw SFARG1LO, LO(BASE)
+  |.endif
+  |  sltiu TMP1, SFARG2HI, LJ_TISNUM
+  |.if FPU
+  |   ldc1 FARG2, 8(BASE)
+  |.else
+  |   lw SFARG2LO, 8+LO(BASE)
+  |.endif
   |  and TMP0, TMP0, TMP1
   |  beqz TMP0, ->fff_fallback
   |.endmacro
@@ -1186,58 +1106,54 @@ static void build_subroutines(BuildCtx *ctx)
   |//-- Base library: checks -----------------------------------------------
   |
   |.ffunc_1 assert
-  |  sltiu AT, CARG3, LJ_TISTRUECOND
+  |  sltiu AT, SFARG1HI, LJ_TISTRUECOND
   |  beqz AT, ->fff_fallback
   |.  addiu RA, BASE, -8
   |  lw PC, FRAME_PC(BASE)
   |  addiu RD, NARGS8:RC, 8            // Compute (nresults+1)*8.
   |  addu TMP2, RA, NARGS8:RC
-  |   sw CARG3, HI(RA)
+  |   sw SFARG1HI, HI(RA)
   |  addiu TMP1, BASE, 8
   |  beq BASE, TMP2, ->fff_res         // Done if exactly 1 argument.
-  |.  sw CARG1, LO(RA)
+  |.  sw SFARG1LO, LO(RA)
   |1:
-  |  load_double1 0(TMP1)
-  |  store_double1 -8(TMP1)
+  |  lw SFRETHI, HI(TMP1)
+  |   lw SFRETLO, LO(TMP1)
+  |  sw SFRETHI, -8+HI(TMP1)
+  |   sw SFRETLO, -8+LO(TMP1)
   |  bne TMP1, TMP2, <1
   |.  addiu TMP1, TMP1, 8
   |  b ->fff_res
   |.  nop
   |
   |.ffunc type
-  |  lw CARG3, HI(BASE)
-  |  li TMP1, LJ_TISNUM
+  |  lw SFARG1HI, HI(BASE)
   |  beqz NARGS8:RC, ->fff_fallback
-  |.  sltiu TMP0, CARG3, LJ_TISNUM
-  |  movz TMP1, CARG3, TMP0
-  |  not TMP1, TMP1
+  |.  sltiu TMP0, SFARG1HI, LJ_TISNUM
+  |  movn SFARG1HI, TISNUM, TMP0
+  |  not TMP1, SFARG1HI
   |  sll TMP1, TMP1, 3
   |  addu TMP1, CFUNC:RB, TMP1
-  |.if HFABI
-  |  b ->fff_resn
-  |.  ldc1 FRET1, CFUNC:TMP1->upvalue
-  |.else
-  |  lw CRET1, CFUNC:TMP1->upvalue[0].u32.hi
-  |  b ->fff_resn
-  |.  lw CRET2, CFUNC:TMP1->upvalue[0].u32.lo
-  |.endif
+  |  lw SFARG1HI, CFUNC:TMP1->upvalue[0].u32.hi
+  |  b ->fff_restv
+  |.  lw SFARG1LO, CFUNC:TMP1->upvalue[0].u32.lo
   |
   |//-- Base library: getters and setters ---------------------------------
   |
   |.ffunc_1 getmetatable
   |  li AT, LJ_TTAB
-  |  bne CARG3, AT, >6
+  |  bne SFARG1HI, AT, >6
   |.  li AT, LJ_TUDATA
   |1:  // Field metatable must be at same offset for GCtab and GCudata!
-  |  lw TAB:CARG1, TAB:CARG1->metatable
+  |  lw TAB:SFARG1LO, TAB:SFARG1LO->metatable
   |2:
   |  lw STR:RC, DISPATCH_GL(gcroot[GCROOT_MMNAME+MM_metatable])(DISPATCH)
-  |  beqz TAB:CARG1, ->fff_restv
-  |.  li CARG3, LJ_TNIL
-  |  lw TMP0, TAB:CARG1->hmask
-  |   li CARG3, LJ_TTAB                        // Use metatable as default result.
+  |  beqz TAB:SFARG1LO, ->fff_restv
+  |.  li SFARG1HI, LJ_TNIL
+  |  lw TMP0, TAB:SFARG1LO->hmask
+  |   li SFARG1HI, LJ_TTAB             // Use metatable as default result.
   |  lw TMP1, STR:RC->hash
-  |  lw NODE:TMP2, TAB:CARG1->node
+  |  lw NODE:TMP2, TAB:SFARG1LO->node
   |  and TMP1, TMP1, TMP0              // idx = str->hash & tab->hmask
   |  sll TMP0, TMP1, 5
   |  sll TMP1, TMP1, 3
@@ -1249,7 +1165,7 @@ static void build_subroutines(BuildCtx *ctx)
   |   lw TMP0, offsetof(Node, key)+LO(NODE:TMP2)
   |    lw NODE:TMP3, NODE:TMP2->next
   |  bne CARG4, AT, >4
-  |.    lw CARG2, offsetof(Node, val)+HI(NODE:TMP2)
+  |.    lw CARG3, offsetof(Node, val)+HI(NODE:TMP2)
   |  beq TMP0, STR:RC, >5
   |.    lw TMP1, offsetof(Node, val)+LO(NODE:TMP2)
   |4:
@@ -1258,36 +1174,35 @@ static void build_subroutines(BuildCtx *ctx)
   |  b <3
   |.  nop
   |5:
-  |  beq CARG2, TISNIL, ->fff_restv    // Ditto for nil value.
+  |  beq CARG3, TISNIL, ->fff_restv    // Ditto for nil value.
   |.  nop
-  |  move CARG3, CARG2                 // Return value of mt.__metatable.
+  |  move SFARG1HI, CARG3              // Return value of mt.__metatable.
   |  b ->fff_restv
-  |.  move CARG1, TMP1
+  |.  move SFARG1LO, TMP1
   |
   |6:
-  |  beq CARG3, AT, <1
-  |.  sltiu TMP0, CARG3, LJ_TISNUM
-  |  li TMP1, LJ_TISNUM
-  |  movz TMP1, CARG3, TMP0
-  |  not TMP1, TMP1
+  |  beq SFARG1HI, AT, <1
+  |.  sltu AT, TISNUM, SFARG1HI
+  |  movz SFARG1HI, TISNUM, AT
+  |  not TMP1, SFARG1HI
   |  sll TMP1, TMP1, 2
   |  addu TMP1, DISPATCH, TMP1
   |  b <2
-  |.  lw TAB:CARG1, DISPATCH_GL(gcroot[GCROOT_BASEMT])(TMP1)
+  |.  lw TAB:SFARG1LO, DISPATCH_GL(gcroot[GCROOT_BASEMT])(TMP1)
   |
   |.ffunc_2 setmetatable
   |  // Fast path: no mt for table yet and not clearing the mt.
   |  li AT, LJ_TTAB
-  |  bne CARG3, AT, ->fff_fallback
-  |.  addiu CARG4, CARG4, -LJ_TTAB
-  |  lw TAB:TMP1, TAB:CARG1->metatable
-  |   lbu TMP3, TAB:CARG1->marked
-  |  or AT, CARG4, TAB:TMP1
+  |  bne SFARG1HI, AT, ->fff_fallback
+  |.  addiu SFARG2HI, SFARG2HI, -LJ_TTAB
+  |  lw TAB:TMP1, TAB:SFARG1LO->metatable
+  |   lbu TMP3, TAB:SFARG1LO->marked
+  |  or AT, SFARG2HI, TAB:TMP1
   |  bnez AT, ->fff_fallback
   |.  andi AT, TMP3, LJ_GC_BLACK       // isblack(table)
   |  beqz AT, ->fff_restv
-  |.  sw TAB:CARG2, TAB:CARG1->metatable
-  |  barrierback TAB:CARG1, TMP3, TMP0, ->fff_restv
+  |.  sw TAB:SFARG2LO, TAB:SFARG1LO->metatable
+  |  barrierback TAB:SFARG1LO, TMP3, TMP0, ->fff_restv
   |
   |.ffunc rawget
   |  lw CARG4, HI(BASE)
@@ -1301,56 +1216,44 @@ static void build_subroutines(BuildCtx *ctx)
   |  call_intern lj_tab_get    // (lua_State *L, GCtab *t, cTValue *key)
   |.  move CARG1, L
   |  // Returns cTValue *.
-  |.if HFABI
-  |  b ->fff_resn
-  |.  ldc1 FRET1, 0(CRET1)
-  |.else
-  |  lw CRET2, 4(CRET1)
-  |  b ->fff_resn
-  |.  lw CRET1, 0(CRET1)
-  |.endif
+  |  lw SFARG1HI, HI(CRET1)
+  |  b ->fff_restv
+  |.  lw SFARG1LO, LO(CRET1)
   |
   |//-- Base library: conversions ------------------------------------------
   |
   |.ffunc tonumber
   |  // Only handles the number case inline (without a base argument).
   |  lw CARG1, HI(BASE)
-  |  xori AT, NARGS8:RC, 8
-  |  sltiu CARG1, CARG1, LJ_TISNUM
-  |  movn CARG1, r0, AT
-  |.if HFABI
-  |  beqz CARG1, ->fff_fallback                // Exactly one number argument.
-  |.  ldc1 FRET1, 0(BASE)
-  |.else
-  |  lw CRET1, 0(BASE)
-  |  beqz CARG1, ->fff_fallback                // Exactly one number argument.
-  |.  lw CRET2, 4(BASE)
-  |.endif
-  |  b ->fff_resn
-  |.  nop
+  |  xori AT, NARGS8:RC, 8             // Exactly one number argument.
+  |  sltu TMP0, TISNUM, CARG1
+  |  or AT, AT, TMP0
+  |  bnez AT, ->fff_fallback
+  |.  lw SFARG1HI, HI(BASE)
+  |  b ->fff_restv
+  |.  lw SFARG1LO, LO(BASE)
   |
   |.ffunc_1 tostring
   |  // Only handles the string or number case inline.
   |  li AT, LJ_TSTR
   |  // A __tostring method in the string base metatable is ignored.
-  |  beq CARG3, AT, ->fff_restv                        // String key?
+  |  beq SFARG1HI, AT, ->fff_restv     // String key?
   |  // Handle numbers inline, unless a number base metatable is present.
   |.  lw TMP1, DISPATCH_GL(gcroot[GCROOT_BASEMT_NUM])(DISPATCH)
-  |  sltiu TMP0, CARG3, LJ_TISNUM
-  |  sltiu TMP1, TMP1, 1
-  |  and TMP0, TMP0, TMP1
-  |  beqz TMP0, ->fff_fallback
+  |  sltu TMP0, TISNUM, SFARG1HI
+  |  or TMP0, TMP0, TMP1
+  |  bnez TMP0, ->fff_fallback
   |.  sw BASE, L->base                 // Add frame since C call can throw.
   |  ffgccheck
   |.  sw PC, SAVE_PC                   // Redundant (but a defined value).
-  |  load_got lj_strfmt_num
+  |  load_got lj_strfmt_number
   |  move CARG1, L
-  |  call_intern lj_strfmt_num         // (lua_State *L, lua_Number *np)
+  |  call_intern lj_strfmt_number      // (lua_State *L, cTValue *o)
   |.  move CARG2, BASE
   |  // Returns GCstr *.
-  |  li CARG3, LJ_TSTR
+  |  li SFARG1HI, LJ_TSTR
   |  b ->fff_restv
-  |.  move CARG1, CRET1
+  |.  move SFARG1LO, CRET1
   |
   |//-- Base library: iterators -------------------------------------------
   |
@@ -1372,47 +1275,38 @@ static void build_subroutines(BuildCtx *ctx)
   |.  move CARG1, L
   |  // Returns 0 at end of traversal.
   |  beqz CRET1, ->fff_restv           // End of traversal: return nil.
-  |.  li CARG3, LJ_TNIL
-  |  load_double1 8(BASE)
+  |.  li SFARG1HI, LJ_TNIL
+  |  lw TMP0, 8+HI(BASE)
+  |   lw TMP1, 8+LO(BASE)
   |    addiu RA, BASE, -8
-  |   load_double2 16(BASE)
-  |  store_double1 0(RA)
-  |   store_double2 8(RA)
+  |  lw TMP2, 16+HI(BASE)
+  |   lw TMP3, 16+LO(BASE)
+  |  sw TMP0, HI(RA)
+  |   sw TMP1, LO(RA)
+  |  sw TMP2, 8+HI(RA)
+  |   sw TMP3, 8+LO(RA)
   |  b ->fff_res
   |.  li RD, (2+1)*8
   |
   |.ffunc_1 pairs
   |  li AT, LJ_TTAB
-  |  bne CARG3, AT, ->fff_fallback
+  |  bne SFARG1HI, AT, ->fff_fallback
   |.  lw PC, FRAME_PC(BASE)
 #if LJ_52
-  |  lw TAB:TMP2, TAB:CARG1->metatable
-  |.if FPU
-  |   ldc1 f0, CFUNC:RB->upvalue[0]
-  |.else
-  |  lw SFT1, CFUNC:RB->upvalue[0].u32.hi
-  |   lw SFT2, CFUNC:RB->upvalue[0].u32.lo
-  |.endif
+  |  lw TAB:TMP2, TAB:SFARG1LO->metatable
+  |  lw TMP0, CFUNC:RB->upvalue[0].u32.hi
+  |   lw TMP1, CFUNC:RB->upvalue[0].u32.lo
   |  bnez TAB:TMP2, ->fff_fallback
 #else
-  |.if FPU
-  |  ldc1 f0, CFUNC:RB->upvalue[0]
-  |.else
-  |  lw SFT1, CFUNC:RB->upvalue[0].u32.hi
-  |   lw SFT2, CFUNC:RB->upvalue[0].u32.lo
-  |.endif
+  |  lw TMP0, CFUNC:RB->upvalue[0].u32.hi
+  |   lw TMP1, CFUNC:RB->upvalue[0].u32.lo
 #endif
   |.  addiu RA, BASE, -8
   |   sw TISNIL, 8+HI(BASE)
-  |  li RD, (3+1)*8
-  |.if FPU
+  |  sw TMP0, HI(RA)
+  |   sw TMP1, LO(RA)
   |  b ->fff_res
-  |.  sdc1 f0, 0(RA)
-  |.else
-  |  sw SFT1, 0(RA)
-  |  b ->fff_res
-  |.  sw SFT2, 4(RA)
-  |.endif
+  |.  li RD, (3+1)*8
   |
   |.ffunc ipairs_aux
   |  sltiu AT, NARGS8:RC, 16
@@ -1421,54 +1315,31 @@ static void build_subroutines(BuildCtx *ctx)
   |   lw CARG4, 8+HI(BASE)
   |  bnez AT, ->fff_fallback
   |.  addiu CARG3, CARG3, -LJ_TTAB
-  |  sltiu AT, CARG4, LJ_TISNUM
-  |   li TMP0, 1
-  |  movn AT, r0, CARG3
-  |  beqz AT, ->fff_fallback
+  |  xor CARG4, CARG4, TISNUM
+  |  and AT, CARG3, CARG4
+  |  bnez AT, ->fff_fallback
   |.  lw PC, FRAME_PC(BASE)
-  |.if FPU
-  |   ldc1 FARG2, 8(BASE)
-  |   mtc1 TMP0, FARG1
-  |   trunc.w.d FRET1, FARG2
-  |  cvt.d.w FARG1, FARG1
-  |  mfc1 TMP2, FRET1
-  |  add.d FARG2, FARG2, FARG1
-  |.else
-  |  sw CARG1, TEMP_SAVE_1
-  |  cvti2d TMP0
-  |  sw CRET1, TEMP_SAVE_2     // Store result CRET1/CRET2=1 (double).
-  |  sw CRET2, TEMP_SAVE_3
-  |  lw CARG2, 8+4(BASE)
-  |  load_got __fixdfsi
-  |  call_extern
-  |.  lw CARG1, 8(BASE)
-  |  sw CRET1, TEMP_SAVE_4
-  |  load_got __adddf3
-  |  lw CARG2, TEMP_SAVE_3
-  |  lw CARG3, 8(BASE)
-  |  lw CARG4, 8+4(BASE)
-  |  call_extern
-  |.  lw CARG1, TEMP_SAVE_2
-  |  lw TMP2, TEMP_SAVE_4
-  |  lw CARG1, TEMP_SAVE_1
-  |.endif
+  |  lw TMP2, 8+LO(BASE)
   |   lw TMP0, TAB:CARG1->asize
   |   lw TMP1, TAB:CARG1->array
   |  addiu TMP2, TMP2, 1
+  |  sw TISNUM, -8+HI(BASE)
   |  sltu AT, TMP2, TMP0
+  |   sw TMP2, -8+LO(BASE)
   |  beqz AT, >2                       // Not in array part?
   |.  addiu RA, BASE, -8
-  |  store_double FARG2, CRET1, CRET2, 0(RA)
   |   sll TMP3, TMP2, 3
   |   addu TMP3, TMP1, TMP3
-  |  lw TMP2, HI(TMP3)
-  |  load_double1 0(TMP3)
+  |  lw TMP1, HI(TMP3)
+  |   lw TMP2, LO(TMP3)
   |1:
-  |  beq TMP2, TISNIL, ->fff_res       // End of iteration, return 0 results.
+  |  beq TMP1, TISNIL, ->fff_res       // End of iteration, return 0 results.
   |.  li RD, (0+1)*8
-  |   store_double1 8(RA)
+  |  sw TMP1, 8+HI(RA)
+  |   sw TMP2, 8+LO(RA)
   |  b ->fff_res
   |.  li RD, (2+1)*8
+  |
   |2:  // Check for empty hash part first. Otherwise call C function.
   |  lw TMP0, TAB:CARG1->hmask
   |  load_got lj_tab_getinth
@@ -1479,49 +1350,30 @@ static void build_subroutines(BuildCtx *ctx)
   |  // Returns cTValue * or NULL.
   |  beqz CRET1, ->fff_res
   |.  li RD, (0+1)*8
-  |  lw TMP2, HI(CRET1)
-  |.if FPU
-  |  b <1
-  |.  ldc1 f0, 0(CRET1)
-  |.else
-  |  lw SFT2, 4(CRET1)
+  |  lw TMP1, HI(CRET1)
   |  b <1
-  |.  lw SFT1, 0(CRET1)
-  |.endif
+  |.  lw TMP2, LO(CRET1)
   |
   |.ffunc_1 ipairs
   |  li AT, LJ_TTAB
-  |  bne CARG3, AT, ->fff_fallback
+  |  bne SFARG1HI, AT, ->fff_fallback
   |.  lw PC, FRAME_PC(BASE)
 #if LJ_52
-  |  lw TAB:TMP2, TAB:CARG1->metatable
-  |.if FPU
-  |   ldc1 f0, CFUNC:RB->upvalue[0]
-  |.else
-  |  lw SFT1, CFUNC:RB->upvalue[0].u32.hi
-  |   lw SFT2, CFUNC:RB->upvalue[0].u32.lo
-  |.endif
+  |  lw TAB:TMP2, TAB:SFARG1LO->metatable
+  |  lw TMP0, CFUNC:RB->upvalue[0].u32.hi
+  |   lw TMP1, CFUNC:RB->upvalue[0].u32.lo
   |  bnez TAB:TMP2, ->fff_fallback
 #else
-  |.if FPU
-  |  ldc1 f0, CFUNC:RB->upvalue[0]
-  |.else
-  |  lw SFT1, CFUNC:RB->upvalue[0].u32.hi
-  |   lw SFT2, CFUNC:RB->upvalue[0].u32.lo
-  |.endif
+  |  lw TMP0, CFUNC:RB->upvalue[0].u32.hi
+  |   lw TMP1, CFUNC:RB->upvalue[0].u32.lo
 #endif
   |.  addiu RA, BASE, -8
-  |   sw r0, 8+HI(BASE)
+  |   sw TISNUM, 8+HI(BASE)
   |   sw r0, 8+LO(BASE)
-  |  li RD, (3+1)*8
-  |.if FPU
+  |  sw TMP0, HI(RA)
+  |   sw TMP1, LO(RA)
   |  b ->fff_res
-  |.  sdc1 f0, 0(RA)
-  |.else
-  |  sw SFT1, 0(RA)
-  |  b ->fff_res
-  |.  sw SFT2, 4(RA)
-  |.endif
+  |.  li RD, (3+1)*8
   |
   |//-- Base library: catch errors ----------------------------------------
   |
@@ -1541,12 +1393,9 @@ static void build_subroutines(BuildCtx *ctx)
   |    sltiu AT, NARGS8:RC, 16
   |  lw CARG4, 8+HI(BASE)
   |    bnez AT, ->fff_fallback
-  |.if FPU
-  |.  ldc1 FARG2, 8(BASE)
-  |.else
   |.  lw CARG3, 8+LO(BASE)
-  |.endif
-  |   load_double FARG1, CARG1, CARG2, 0(BASE)
+  |   lw CARG1, LO(BASE)
+  |    lw CARG2, HI(BASE)
   |    lbu TMP1, DISPATCH_GL(hookmask)(DISPATCH)
   |  li AT, LJ_TFUNC
   |   move TMP2, BASE
@@ -1554,14 +1403,11 @@ static void build_subroutines(BuildCtx *ctx)
   |   addiu BASE, BASE, 16
   |  // Remember active hook before pcall.
   |  srl TMP3, TMP3, HOOK_ACTIVE_SHIFT
-  |.if FPU
-  |   sdc1 FARG2, 0(TMP2)              // Swap function and traceback.
-  |.else
-  |   sw CARG3, LO(TMP2)
+  |   sw CARG3, LO(TMP2)       // Swap function and traceback.
   |   sw CARG4, HI(TMP2)
-  |.endif
   |  andi TMP3, TMP3, 1
-  |   store_double FARG1, CARG1, CARG2, 8(TMP2)
+  |   sw CARG1, 8+LO(TMP2)
+  |    sw CARG2, 8+HI(TMP2)
   |  addiu PC, TMP3, 16+FRAME_PCALL
   |  b ->vm_call_dispatch
   |.  addiu NARGS8:RC, NARGS8:RC, -16
@@ -1570,7 +1416,10 @@ static void build_subroutines(BuildCtx *ctx)
   |
   |.macro coroutine_resume_wrap, resume
   |.if resume
-  |.ffunc_1 coroutine_resume
+  |.ffunc coroutine_resume
+  |  lw CARG3, HI(BASE)
+  |  beqz NARGS8:RC, ->fff_fallback
+  |.  lw CARG1, LO(BASE)
   |  li AT, LJ_TTHREAD
   |  bne CARG3, AT, ->fff_fallback
   |.else
@@ -1605,11 +1454,13 @@ static void build_subroutines(BuildCtx *ctx)
   |  move CARG3, CARG2
   |  sw BASE, L->top
   |2:  // Move args to coroutine.
-  |   load_double1 0(BASE)
+  |   lw SFRETHI, HI(BASE)
+  |    lw SFRETLO, LO(BASE)
   |  sltu AT, BASE, TMP1
   |  beqz AT, >3
   |.  addiu BASE, BASE, 8
-  |   store_double1 0(CARG3)
+  |   sw SFRETHI, HI(CARG3)
+  |    sw SFRETLO, LO(CARG3)
   |  b <2
   |.  addiu CARG3, CARG3, 8
   |3:
@@ -1635,10 +1486,12 @@ static void build_subroutines(BuildCtx *ctx)
   |  sw TMP2, L:RA->top                        // Clear coroutine stack.
   |  move TMP1, BASE
   |5:  // Move results from coroutine.
-  |   load_double1 0(TMP2)
+  |   lw SFRETHI, HI(TMP2)
+  |    lw SFRETLO, LO(TMP2)
   |  addiu TMP2, TMP2, 8
   |  sltu AT, TMP2, TMP3
-  |   store_double1 0(TMP1)
+  |   sw SFRETHI, HI(TMP1)
+  |    sw SFRETLO, LO(TMP1)
   |  bnez AT, <5
   |.  addiu TMP1, TMP1, 8
   |6:
@@ -1663,12 +1516,14 @@ static void build_subroutines(BuildCtx *ctx)
   |.if resume
   |  addiu TMP3, TMP3, -8
   |   li TMP1, LJ_TFALSE
-  |  load_double1 0(TMP3)
+  |  lw SFRETHI, HI(TMP3)
+  |   lw SFRETLO, LO(TMP3)
   |   sw TMP3, L:RA->top               // Remove error from coroutine stack.
   |    li RD, (2+1)*8
   |   sw TMP1, -8+HI(BASE)             // Prepend false to results.
   |    addiu RA, BASE, -8
-  |   store_double1 0(BASE)                    // Copy error message.
+  |  sw SFRETHI, HI(BASE)              // Copy error message.
+  |   sw SFRETLO, LO(BASE)
   |  b <7
   |.  andi TMP0, PC, FRAME_TYPE
   |.else
@@ -1705,39 +1560,28 @@ static void build_subroutines(BuildCtx *ctx)
   |//-- Math library -------------------------------------------------------
   |
   |.ffunc_1 math_abs
-  |   load_farg1 0(BASE)
-  |   sltiu AT, CARG3, LJ_TISNUM
-  |  beqz AT, ->fff_fallback
+  |  bne SFARG1HI, TISNUM, >1
+  |.  sra TMP0, SFARG1LO, 31
+  |  xor TMP1, SFARG1LO, TMP0
+  |  subu SFARG1LO, TMP1, TMP0
+  |  bgez SFARG1LO, ->fff_restv
   |.  nop
-  |.if FPU
-  |.  abs.d FRET1, FARG1
-  |.else
-  |.  lui TMP1, 0x8000
-  |   and AT, CARG1, TMP1
-  |   move CRET2, CARG2
-  |   beqz AT, ->fff_resn
-  |.  move CRET1, CARG1
-  |   xor CRET1, CARG1, TMP1
-  |.endif
-  |
-  |->fff_resn:
-  |  lw PC, FRAME_PC(BASE)
-  |  addiu RA, BASE, -8
-  |.if HFABI
-  |  b ->fff_res1
-  |.  sdc1 FRET1, -8(BASE)
-  |.else
-  |  sw CRET1, -8(BASE)
-  |  b ->fff_res1
-  |.  sw CRET2, -8+4(BASE)
-  |.endif
+  |  lui SFARG1HI, 0x41e0              // 2^31 as a double.
+  |  b ->fff_restv
+  |.  li SFARG1LO, 0
+  |1:
+  |  sltiu AT, SFARG1HI, LJ_TISNUM
+  |  beqz AT, ->fff_fallback
+  |.  sll SFARG1HI, SFARG1HI, 1
+  |  srl SFARG1HI, SFARG1HI, 1
+  |// fallthrough
   |
   |->fff_restv:
-  |  // CARG3/CARG1 = TValue result.
+  |  // SFARG1LO/SFARG1HI = TValue result.
   |  lw PC, FRAME_PC(BASE)
-  |   sw CARG3, -8+HI(BASE)
+  |   sw SFARG1HI, -8+HI(BASE)
   |  addiu RA, BASE, -8
-  |   sw CARG1, -8+LO(BASE)
+  |   sw SFARG1LO, -8+LO(BASE)
   |->fff_res1:
   |  // RA = results, PC = return.
   |  li RD, (1+1)*8
@@ -1766,21 +1610,19 @@ static void build_subroutines(BuildCtx *ctx)
   |.  sw TISNIL, -8+HI(TMP1)
   |
   |.macro math_extern, func
-  |->ff_math_ .. func:
-  |  lw CARG3, HI(BASE)
+  |  .ffunc math_ .. func
+  |  lw SFARG1HI, HI(BASE)
   |  beqz NARGS8:RC, ->fff_fallback
   |.  load_got func
-  |  sltiu AT, CARG3, LJ_TISNUM
+  |  sltiu AT, SFARG1HI, LJ_TISNUM
   |  beqz AT, ->fff_fallback
-  |.  nop
-  |.if HFABI
-  |  call_extern
+  |.if FPU
   |.  ldc1 FARG1, 0(BASE)
   |.else
-  |  lw CARG1, 0(BASE)
-  |  call_extern
-  |.  lw CARG2, 4(BASE)
+  |.  lw SFARG1LO, LO(BASE)
   |.endif
+  |  call_extern
+  |.  nop
   |  b ->fff_resn
   |.  nop
   |.endmacro
@@ -1794,10 +1636,22 @@ static void build_subroutines(BuildCtx *ctx)
   |.  nop
   |.endmacro
   |
+  |// TODO: Return integer type if result is integer (own sf implementation).
   |.macro math_round, func
-  |  .ffunc_n math_ .. func
-  |.  nop
+  |->ff_math_ .. func:
+  |  lw SFARG1HI, HI(BASE)
+  |  beqz NARGS8:RC, ->fff_fallback
+  |.  lw SFARG1LO, LO(BASE)
+  |  beq SFARG1HI, TISNUM, ->fff_restv
+  |.  sltu AT, SFARG1HI, TISNUM
+  |  beqz AT, ->fff_fallback
+  |.if FPU
+  |.  ldc1 FARG1, 0(BASE)
   |  bal ->vm_ .. func
+  |.else
+  |.  load_got func
+  |  call_extern
+  |.endif
   |.  nop
   |  b ->fff_resn
   |.  nop
@@ -1809,17 +1663,16 @@ static void build_subroutines(BuildCtx *ctx)
   |.ffunc math_log
   |  li AT, 8
   |  bne NARGS8:RC, AT, ->fff_fallback // Exactly 1 argument.
-  |.  lw CARG3, HI(BASE)
-  |  sltiu AT, CARG3, LJ_TISNUM
+  |.  lw SFARG1HI, HI(BASE)
+  |  sltiu AT, SFARG1HI, LJ_TISNUM
   |  beqz AT, ->fff_fallback
   |.  load_got log
-  |.if HFABI
+  |.if FPU
   |  call_extern
   |.  ldc1 FARG1, 0(BASE)
   |.else
-  |  lw CARG1, 0(BASE)
   |  call_extern
-  |.  lw CARG2, 4(BASE)
+  |.  lw SFARG1LO, LO(BASE)
   |.endif
   |  b ->fff_resn
   |.  nop
@@ -1842,37 +1695,40 @@ static void build_subroutines(BuildCtx *ctx)
   |.if FPU
   |.ffunc_n math_sqrt
   |.  sqrt.d FRET1, FARG1
-  |  b ->fff_resn
-  |.  nop
+  |// fallthrough to ->fff_resn
   |.else
   |  math_extern sqrt
   |.endif
   |
-  |.ffunc_2 math_ldexp
-  |  sltiu TMP0, CARG3, LJ_TISNUM
-  |  sltiu TMP1, CARG4, LJ_TISNUM
-  |   load_farg1 0(BASE)
-  |   load_farg2 8(BASE)
-  |  and TMP0, TMP0, TMP1
-  |  beqz TMP0, ->fff_fallback
+  |->fff_resn:
+  |  lw PC, FRAME_PC(BASE)
+  |  addiu RA, BASE, -8
   |.if FPU
-  |  load_got ldexp
-  |  trunc.w.d FARG2, FARG2
-  |  call_extern
-  |.  mfc1 CARG3, FARG2
+  |  b ->fff_res1
+  |.  sdc1 FRET1, -8(BASE)
   |.else
-  |  sw CARG1, TEMP_SAVE_1
-  |  sw CARG2, TEMP_SAVE_2
-  |  load_got __fixdfsi
-  |   move CARG1, CARG3
-  |  call_extern
-  |.  move CARG2, CARG4
-  |  lw CARG1, TEMP_SAVE_1
+  |  sw SFRETHI, -8+HI(BASE)
+  |  b ->fff_res1
+  |.  sw SFRETLO, -8+LO(BASE)
+  |.endif
+  |
+  |
+  |.ffunc math_ldexp
+  |  sltiu AT, NARGS8:RC, 16
+  |   lw SFARG1HI, HI(BASE)
+  |  bnez AT, ->fff_fallback
+  |.   lw CARG4, 8+HI(BASE)
+  |  bne CARG4, TISNUM, ->fff_fallback
   |  load_got ldexp
-  |  lw CARG2, TEMP_SAVE_2
-  |  call_extern
-  |.  move CARG3, CRET1
+  |.  sltu AT, SFARG1HI, TISNUM
+  |  beqz AT, ->fff_fallback
+  |.if FPU
+  |.  ldc1 FARG1, 0(BASE)
+  |.else
+  |.  lw SFARG1LO, LO(BASE)
   |.endif
+  |  call_extern
+  |.  lw CARG3, 8+LO(BASE)
   |  b ->fff_resn
   |.  nop
   |
@@ -1883,14 +1739,17 @@ static void build_subroutines(BuildCtx *ctx)
   |.  addiu CARG3, DISPATCH, DISPATCH_GL(tmptv)
   |   lw TMP1, DISPATCH_GL(tmptv)(DISPATCH)
   |  addiu RA, BASE, -8
-  |  store_double FRET1, CRET1, CRET2, 0(RA)
   |.if FPU
   |   mtc1 TMP1, FARG2
+  |  sdc1 FRET1, 0(RA)
   |   cvt.d.w FARG2, FARG2
+  |   sdc1 FARG2, 8(RA)
   |.else
-  |   cvti2d TMP1
+  |  sw SFRETLO, LO(RA)
+  |  sw SFRETHI, HI(RA)
+  |  sw TMP1, 8+LO(RA)
+  |  sw TISNUM, 8+HI(RA)
   |.endif
-  |  store_double FARG2, CRET1, CRET2, 8(RA)
   |  b ->fff_res
   |.  li RD, (2+1)*8
   |
@@ -1900,92 +1759,98 @@ static void build_subroutines(BuildCtx *ctx)
   |  call_extern
   |.  addiu CARG3, BASE, -8
   |  addiu RA, BASE, -8
-  |.if HFABI
+  |.if FPU
   |  sdc1 FRET1, 0(BASE)
   |.else
-  |  sw CRET1, 0(BASE)
-  |  sw CRET2, 4(BASE)
+  |  sw SFRETLO, LO(BASE)
+  |  sw SFRETHI, HI(BASE)
   |.endif
   |  b ->fff_res
   |.  li RD, (2+1)*8
   |
-  |.macro math_minmax, name, ismax
-  |->ff_ .. name:
-  |  lw CARG3, HI(BASE)
-  |  beqz NARGS8:RC, ->fff_fallback
-  |.  sltiu AT, CARG3, LJ_TISNUM
+  |.macro math_minmax, name, intins, fpins
+  |  .ffunc_1 name
+  |  addu TMP3, BASE, NARGS8:RC
+  |  bne SFARG1HI, TISNUM, >5
+  |.  addiu TMP2, BASE, 8
+  |1:  // Handle integers.
+  |.  lw SFARG2HI, HI(TMP2)
+  |  beq TMP2, TMP3, ->fff_restv
+  |.  lw SFARG2LO, LO(TMP2)
+  |  bne SFARG2HI, TISNUM, >3
+  |.  slt AT, SFARG1LO, SFARG2LO
+  |  intins SFARG1LO, SFARG2LO, AT
+  |  b <1
+  |.  addiu TMP2, TMP2, 8
+  |
+  |3:  // Convert intermediate result to number and continue with number loop.
+  |  sltiu AT, SFARG2HI, LJ_TISNUM
   |  beqz AT, ->fff_fallback
-  |.  addu TMP2, BASE, NARGS8:RC
-  |  addiu TMP1, BASE, 8
-  |.if HFABI
-  |  ldc1 FRET1, 0(BASE)
-  |  beq TMP1, TMP2, ->fff_resn
-  |.else
-  |  lw CRET1, 0(BASE)
-  |  lw CRET2, 4(BASE)
-  |  beq TMP1, TMP2, ->fff_resn
-  |.endif
-  |1:
-  |.  lw CARG3, HI(TMP1)
-  |.if HFABI
-  |  ldc1 FARG1, 0(TMP1)
+  |.if FPU
+  |.  mtc1 SFARG1LO, FRET1
+  |  cvt.d.w FRET1, FRET1
+  |  b >7
+  |.  ldc1 FARG1, 0(TMP2)
   |.else
-  |  lw CARG1, 0(TMP1)
-  |  lw CARG2, 4(TMP1)
+  |.  nop
+  |  bal ->vm_sfi2d_1
+  |.  nop
+  |  b >7
+  |.  nop
   |.endif
-  |  sltiu AT, CARG3, LJ_TISNUM
+  |
+  |5:
+  |.  sltiu AT, SFARG1HI, LJ_TISNUM
   |  beqz AT, ->fff_fallback
-  |. addiu TMP1, TMP1, 8
   |.if FPU
-  |.if ismax
-  |  c.olt.d FARG1, FRET1
+  |.  ldc1 FRET1, 0(BASE)
+  |.endif
+  |
+  |6:  // Handle numbers.
+  |.  lw SFARG2HI, HI(TMP2)
+  |.if FPU
+  |  beq TMP2, TMP3, ->fff_resn
   |.else
-  |  c.olt.d FRET1, FARG1
+  |  beq TMP2, TMP3, ->fff_restv
   |.endif
-  |  bne TMP1, TMP2, <1
-  |.  movf.d FRET1, FARG1
+  |.  sltiu AT, SFARG2HI, LJ_TISNUM
+  |  beqz AT, >8
+  |.if FPU
+  |.  ldc1 FARG1, 0(TMP2)
   |.else
-  |  load_got __ledf2
-  |  sw TMP1, TEMP_SAVE_1
-  |  sw TMP2, TEMP_SAVE_2
-  |  sw CARG1, TEMP_SAVE_3
-  |  sw CARG2, TEMP_SAVE_4
-  |  sw CRET1, TEMP_SAVE_5
-  |  sw CRET2, TEMP_SAVE_6
-  |  move CARG3, CRET1
-  |  call_extern
-  |.  move CARG4, CRET2
-  |  lw CARG4, TEMP_SAVE_6
-  |  lw CARG3, TEMP_SAVE_5
-  |  lw CARG2, TEMP_SAVE_4
-  |  lw CARG1, TEMP_SAVE_3
-  |  lw TMP2, TEMP_SAVE_2
-  |  lw TMP1, TEMP_SAVE_1
-  |.if ismax
-  |  beqz CRET1, >2            // farg1==fret1
-  |.  li TMP3, 1
-  |  beq CRET1, TMP3, >2       // farg1>fret1
-  |.  nop
+  |.  lw SFARG2LO, LO(TMP2)
+  |.endif
+  |7:
+  |.if FPU
+  |  c.olt.d FRET1, FARG1
+  |  fpins FRET1, FARG1
   |.else
-  |  blez CRET1, >2
+  |  bal ->vm_sfcmpolt
   |.  nop
+  |  intins SFARG1LO, SFARG2LO, CRET1
+  |  intins SFARG1HI, SFARG2HI, CRET1
   |.endif
-  |  move CRET1, CARG3         // Keep the value.
-  |  b >3
-  |.  move CRET2, CARG4
-  |2:
-  |  move CRET1, CARG1         // Set new value.
-  |  move CRET2, CARG2
-  |3:
-  |  bne TMP1, TMP2, <1
+  |  b <6
+  |.  addiu TMP2, TMP2, 8
+  |
+  |8:  // Convert integer to number and continue with number loop.
+  |  bne SFARG2HI, TISNUM, ->fff_fallback
+  |.if FPU
+  |.  lwc1 FARG1, LO(TMP2)
+  |  b <7
+  |.  cvt.d.w FARG1, FARG1
+  |.else
   |.  nop
-  |.endif
-  |  b ->fff_resn
+  |  bal ->vm_sfi2d_2
   |.  nop
+  |  b <7
+  |.  nop
+  |.endif
+  |
   |.endmacro
   |
-  |  math_minmax math_min, 0
-  |  math_minmax math_max, 1
+  |  math_minmax math_min, movz, movf.d
+  |  math_minmax math_max, movn, movt.d
   |
   |//-- String library -----------------------------------------------------
   |
@@ -1999,51 +1864,29 @@ static void build_subroutines(BuildCtx *ctx)
   |.  nop
   |  lw TMP0, STR:CARG1->len
   |    addiu RA, BASE, -8
+  |    lw PC, FRAME_PC(BASE)
   |  sltu RD, r0, TMP0
-  |  lw PC, FRAME_PC(BASE)
-  |  addiu RD, RD, 1
   |   lbu TMP1, STR:CARG1[1]           // Access is always ok (NUL at end).
-  |.if FPU
-  |   mtc1 TMP1, f0
-  |   cvt.d.w f0, f0
-  |   sdc1 f0, 0(RA)
-  |.else
-  |   sw RD, TEMP_SAVE_1
-  |   cvti2d TMP1
-  |  sw CRET1, 0(RA)
-  |  sw CRET2, 4(RA)
-  |   lw RD, TEMP_SAVE_1
-  |.endif
+  |  addiu RD, RD, 1
+  |  sll RD, RD, 3                     // RD = ((str->len != 0)+1)*8
+  |  sw TISNUM, HI(RA)
   |  b ->fff_res
-  |.  sll RD, RD, 3            // RD = ((str->len != 0)+1)*8
+  |.  sw TMP1, LO(RA)
   |
   |.ffunc string_char                  // Only handle the 1-arg case here.
   |  ffgccheck
-  |  lw CARG3, HI(BASE)
-  |  li AT, 8
-  |  bne NARGS8:RC, AT, ->fff_fallback // Exactly 1 argument.
-  |.  sltiu AT, CARG3, LJ_TISNUM
-  |  beqz AT, ->fff_fallback
+  |.  lw CARG3, HI(BASE)
+  |   lw CARG1, LO(BASE)
+  |  li TMP1, 255
+  |  xori AT, NARGS8:RC, 8             // Exactly 1 argument.
+  |  xor TMP0, CARG3, TISNUM           // Integer.
+  |   sltu TMP1, TMP1, CARG1           // !(255 < n).
+  |  or AT, AT, TMP0
+  |   or AT, AT, TMP1
+  |  bnez AT, ->fff_fallback
   |.  li CARG3, 1
-  |  sltiu AT, TMP0, 256
-  |  beqz AT, ->fff_fallback
-  |  load_farg1 0(BASE)
-  |.if FPU
-  |   trunc.w.d FARG1, FARG1
-  |   mfc1 TMP0, FARG1
-  |.else
-  |   load_got __fixdfsi
-  |   sw RB, TEMP_SAVE_1
-  |   sw RC, TEMP_SAVE_2
-  |   call_extern
-  |.   sw CARG3, TEMP_SAVE_3
-  |   lw CARG3, TEMP_SAVE_3
-  |   lw RC, TEMP_SAVE_2
-  |   lw RB, TEMP_SAVE_1
-  |   move TMP0, CRET1
-  |.endif
   |  addiu CARG2, sp, ARG5_OFS
-  |  sw TMP0, ARG5
+  |  sb CARG1, ARG5
   |->fff_newstr:
   |  load_got lj_str_new
   |   sw BASE, L->base
@@ -2053,24 +1896,13 @@ static void build_subroutines(BuildCtx *ctx)
   |  // Returns GCstr *.
   |  lw BASE, L->base
   |->fff_resstr:
-  |  move CARG1, CRET1
+  |  move SFARG1LO, CRET1
   |  b ->fff_restv
-  |.  li CARG3, LJ_TSTR
+  |.  li SFARG1HI, LJ_TSTR
   |
   |.ffunc string_sub
   |  ffgccheck
-  |  addiu AT, NARGS8:RC, -16
-  |.if FPU
-  |   ldc1 f0, 16(BASE)
-  |   trunc.w.d f0, f0
-  |.else
-  |   lw CARG1, 16(BASE)
-  |   load_got __fixdfsi
-  |   sw AT, TEMP_SAVE_1
-  |   call_extern
-  |.   lw CARG2, 16+4(BASE)
-  |   lw AT, TEMP_SAVE_1
-  |.endif
+  |.  addiu AT, NARGS8:RC, -16
   |   lw CARG3, 16+HI(BASE)
   |   lw TMP0, HI(BASE)
   |    lw STR:CARG1, LO(BASE)
@@ -2078,33 +1910,13 @@ static void build_subroutines(BuildCtx *ctx)
   |.  lw CARG2, 8+HI(BASE)
   |  beqz AT, >1
   |.  li CARG4, -1
-  |  sltiu AT, CARG3, LJ_TISNUM
-  |  beqz AT, ->fff_fallback
-  |.if FPU
-  |.  mfc1 CARG4, f0
-  |.else
-  |.  move CARG4, CRET1
-  |.endif
+  |  bne CARG3, TISNUM, ->fff_fallback
+  |.  lw CARG4, 16+LO(BASE)
   |1:
-  |  sltiu AT, CARG2, LJ_TISNUM
-  |  beqz AT, ->fff_fallback
+  |  bne CARG2, TISNUM, ->fff_fallback
   |.  li AT, LJ_TSTR
   |  bne TMP0, AT, ->fff_fallback
-  |.if FPU
-  |.  ldc1 f2, 8(BASE)
-  |  trunc.w.d f2, f2
-  |  mfc1 CARG3, f2
-  |.else
-  |.   sw CARG1, TEMP_SAVE_1
-  |   sw CARG4, TEMP_SAVE_2
-  |   lw CARG2, 8+4(BASE)
-  |   load_got __fixdfsi
-  |   call_extern
-  |.   lw CARG1, 8(BASE)
-  |   lw CARG1, TEMP_SAVE_1
-  |   lw CARG4, TEMP_SAVE_2
-  |   move CARG3, CRET1
-  |.endif
+  |.  lw CARG3, 8+LO(BASE)
   |  lw CARG2, STR:CARG1->len
   |  // STR:CARG1 = str, CARG2 = str->len, CARG3 = start, CARG4 = end
   |  slt AT, CARG4, r0
@@ -2127,14 +1939,14 @@ static void build_subroutines(BuildCtx *ctx)
   |  bgez CARG3, ->fff_newstr
   |.  addiu CARG3, CARG3, 1            // len++
   |->fff_emptystr:  // Return empty string.
-  |  addiu STR:CARG1, DISPATCH, DISPATCH_GL(strempty)
+  |  addiu STR:SFARG1LO, DISPATCH, DISPATCH_GL(strempty)
   |  b ->fff_restv
-  |.  li CARG3, LJ_TSTR
+  |.  li SFARG1HI, LJ_TSTR
   |
   |.macro ffstring_op, name
   |  .ffunc string_ .. name
   |  ffgccheck
-  |  lw CARG3, HI(BASE)
+  |.  lw CARG3, HI(BASE)
   |   lw STR:CARG2, LO(BASE)
   |  beqz NARGS8:RC, ->fff_fallback
   |.  li AT, LJ_TSTR
@@ -2160,88 +1972,96 @@ static void build_subroutines(BuildCtx *ctx)
   |
   |//-- Bit library --------------------------------------------------------
   |
-  |.if not FPU
+  |->vm_tobit_fb:
+  |  beqz TMP1, ->fff_fallback
+  |.if FPU
+  |.  ldc1 FARG1, 0(BASE)
+  |  add.d FARG1, FARG1, TOBIT
+  |  jr ra
+  |.  mfc1 CRET1, FARG1
+  |.else
   |// FP number to bit conversion for soft-float.
   |->vm_tobit:
-  |  sll TMP0, CARG1, 1
-  |  lui TMP3, 0x0020
-  |  addu TMP0, TMP0, TMP3
-  |  slt TMP3, TMP0, r0
-  |  movz CARG2, r0, TMP3
-  |  beqz TMP3, >2
-  |.  li CARG4, 0x3e0
-  |  not CARG4, CARG4
+  |  sll TMP0, SFARG1HI, 1
+  |  lui AT, 0x0020
+  |  addu TMP0, TMP0, AT
+  |  slt AT, TMP0, r0
+  |  movz SFARG1LO, r0, AT
+  |  beqz AT, >2
+  |.  li TMP1, 0x3e0
+  |  not TMP1, TMP1
   |  sra TMP0, TMP0, 21
-  |  subu TMP0, CARG4, TMP0
-  |  slt TMP3, TMP0, r0
-  |  bnez TMP3, >1
-  |.  sll CARG4, CARG1, 11
-  |  lui TMP3, 0x8000
-  |  or CARG4, CARG4, TMP3
-  |  srl TMP3, CARG2, 21
-  |  or CARG4, CARG4, TMP3
-  |  slt TMP3, CARG1, r0
-  |  beqz TMP3, >2
-  |.  srlv CARG2, CARG4, TMP0
-  |  subu CARG2, r0, CARG2
+  |  subu TMP0, TMP1, TMP0
+  |  slt AT, TMP0, r0
+  |  bnez AT, >1
+  |.  sll TMP1, SFARG1HI, 11
+  |  lui AT, 0x8000
+  |  or TMP1, TMP1, AT
+  |  srl AT, SFARG1LO, 21
+  |  or TMP1, TMP1, AT
+  |  slt AT, SFARG1HI, r0
+  |  beqz AT, >2
+  |.  srlv SFARG1LO, TMP1, TMP0
+  |  subu SFARG1LO, r0, SFARG1LO
   |2:
   |  jr ra
-  |.  move CRET1, CARG2
+  |.  move CRET1, SFARG1LO
   |1:
   |  addiu TMP0, TMP0, 21
-  |  srlv CARG4, CARG2, TMP0
-  |  li TMP3, 20
-  |  subu TMP0, TMP3, TMP0
-  |  sll CARG2, CARG1, 12
-  |  sllv TMP3, CARG2, TMP0
-  |  or CARG2, CARG4, TMP3
-  |  slt TMP3, CARG1, r0
-  |  beqz TMP3, <2
+  |  srlv TMP1, SFARG1LO, TMP0
+  |  li AT, 20
+  |  subu TMP0, AT, TMP0
+  |  sll SFARG1LO, SFARG1HI, 12
+  |  sllv AT, SFARG1LO, TMP0
+  |  or SFARG1LO, TMP1, AT
+  |  slt AT, SFARG1HI, r0
+  |  beqz AT, <2
   |.  nop
   |  jr ra
-  |.  subu CRET1, r0, CARG2
+  |.  subu CRET1, r0, SFARG1LO
   |.endif
   |
   |.macro .ffunc_bit, name
-  |  .ffunc_n bit_..name
-  |.if FPU
-  |.  add.d FARG1, FARG1, TOBIT
-  |  mfc1 CRET1, FARG1
-  |.else
-  |.  nop
-  |  bal ->vm_tobit
-  |.  nop
-  |.endif
+  |  .ffunc_1 bit_..name
+  |  beq SFARG1HI, TISNUM, >6
+  |.  move CRET1, SFARG1LO
+  |  bal ->vm_tobit_fb
+  |.  sltu TMP1, SFARG1HI, TISNUM
+  |6:
   |.endmacro
   |
   |.macro .ffunc_bit_op, name, ins
   |  .ffunc_bit name
-  |  addiu TMP1, BASE, 8
-  |  addu TMP2, BASE, NARGS8:RC
+  |  addiu TMP2, BASE, 8
+  |  addu TMP3, BASE, NARGS8:RC
   |1:
-  |  move CRET2, CRET1
-  |  lw CARG4, HI(TMP1)
-  |.if FPU
-  |  beq TMP1, TMP2, ->fff_resi
-  |.  ldc1 FARG1, 0(TMP1)
-  |.else
-  |  lw CARG1, 0(TMP1)
-  |  beq TMP1, TMP2, ->fff_resi
-  |.  lw CARG2, 4(TMP1)
-  |.endif
-  |  sltiu AT, CARG4, LJ_TISNUM
-  |  beqz AT, ->fff_fallback
+  |  lw SFARG1HI, HI(TMP2)
+  |  beq TMP2, TMP3, ->fff_resi
+  |.  lw SFARG1LO, LO(TMP2)
   |.if FPU
+  |  bne SFARG1HI, TISNUM, >2
+  |.  addiu TMP2, TMP2, 8
+  |  b <1
+  |.  ins CRET1, CRET1, SFARG1LO
+  |2:
+  |   ldc1 FARG1, -8(TMP2)
+  |  sltu TMP1, SFARG1HI, TISNUM
+  |  beqz TMP1, ->fff_fallback
   |.  add.d FARG1, FARG1, TOBIT
-  |  mfc1 CRET1, FARG1
+  |  mfc1 SFARG1LO, FARG1
+  |  b <1
+  |.  ins CRET1, CRET1, SFARG1LO
   |.else
-  |.  nop
-  |  bal ->vm_tobit
-  |.  nop
-  |.endif
-  |  ins CRET1, CRET2, CRET1
+  |  beq SFARG1HI, TISNUM, >2
+  |.  move CRET2, CRET1
+  |  bal ->vm_tobit_fb
+  |.  sltu TMP1, SFARG1HI, TISNUM
+  |  move SFARG1LO, CRET2
+  |2:
+  |  ins CRET1, CRET1, SFARG1LO
   |  b <1
-  |.  addiu TMP1, TMP1, 8
+  |.  addiu TMP2, TMP2, 8
+  |.endif
   |.endmacro
   |
   |.ffunc_bit_op band, and
@@ -2265,36 +2085,28 @@ static void build_subroutines(BuildCtx *ctx)
   |.  not CRET1, CRET1
   |
   |.macro .ffunc_bit_sh, name, ins, shmod
-  |  .ffunc_nn bit_..name
-  |.if FPU
-  |.  add.d FARG1, FARG1, TOBIT
-  |  add.d FARG2, FARG2, TOBIT
-  |  mfc1 CARG1, FARG1
-  |  mfc1 CARG2, FARG2
-  |.else
-  |.  sw CARG4, TEMP_SAVE_1
-  |  bal ->vm_tobit
+  |  .ffunc_2 bit_..name
+  |  beq SFARG1HI, TISNUM, >1
+  |.  nop
+  |  bal ->vm_tobit_fb
+  |.  sltu TMP1, SFARG1HI, TISNUM
+  |  move SFARG1LO, CRET1
+  |1:
+  |  bne SFARG2HI, TISNUM, ->fff_fallback
   |.  nop
-  |  move CRET2, CRET1
-  |  lw CARG2, TEMP_SAVE_1
-  |  bal ->vm_tobit
-  |.  move CARG1, CARG3
-  |  move CARG2, CRET1
-  |  move CARG1, CRET2
-  |.endif
   |.if shmod == 1
   |  li AT, 32
-  |  subu TMP0, AT, CARG2
-  |  sllv CARG2, CARG1, CARG2
-  |  srlv CARG1, CARG1, TMP0
+  |  subu TMP0, AT, SFARG2LO
+  |  sllv SFARG2LO, SFARG1LO, SFARG2LO
+  |  srlv SFARG1LO, SFARG1LO, TMP0
   |.elif shmod == 2
   |  li AT, 32
-  |  subu TMP0, AT, CARG2
-  |  srlv CARG2, CARG1, CARG2
-  |  sllv CARG1, CARG1, TMP0
+  |  subu TMP0, AT, SFARG2LO
+  |  srlv SFARG2LO, SFARG1LO, SFARG2LO
+  |  sllv SFARG1LO, SFARG1LO, TMP0
   |.endif
   |  b ->fff_resi
-  |.  ins CRET1, CARG1, CARG2
+  |.  ins CRET1, SFARG1LO, SFARG2LO
   |.endmacro
   |
   |.ffunc_bit_sh lshift, sllv, 0
@@ -2308,17 +2120,9 @@ static void build_subroutines(BuildCtx *ctx)
   |->fff_resi:
   |  lw PC, FRAME_PC(BASE)
   |  addiu RA, BASE, -8
-  |.if HFABI
-  |  mtc1 CRET1, FRET1
-  |  cvt.d.w FRET1, FRET1
+  |  sw TISNUM, -8+HI(BASE)
   |  b ->fff_res1
-  |.  sdc1 FRET1, -8(BASE)
-  |.else               // Result already in CRET1.
-  |  cvti2d CRET1
-  |  sw CRET1, -8(BASE)
-  |  b ->fff_res1
-  |.  sw CRET2, -8+4(BASE)
-  |.endif
+  |.  sw CRET1, -8+LO(BASE)
   |
   |//-----------------------------------------------------------------------
   |
@@ -2516,10 +2320,12 @@ static void build_subroutines(BuildCtx *ctx)
   |   beqz AT, >2
   |. addu RC, BASE, RC                 // Call base.
   |1:  // Move results down.
-  |  ldc1 f0, 0(RA)
+  |  lw SFRETHI, HI(RA)
+  |   lw SFRETLO, LO(RA)
   |   addiu AT, AT, -8
   |    addiu RA, RA, 8
-  |  sdc1 f0, 0(RC)
+  |  sw SFRETHI, HI(RC)
+  |   sw SFRETLO, LO(RC)
   |   bnez AT, <1
   |.   addiu RC, RC, 8
   |2:
@@ -2658,6 +2464,7 @@ static void build_subroutines(BuildCtx *ctx)
   |    .FPU lui TMP3, 0x59c0                   // TOBIT = 2^52 + 2^51 (float).
   |  sll MULTRES, CRET1, 3
   |    li TISNIL, LJ_TNIL
+  |     li TISNUM, LJ_TISNUM           // Setup type comparison constants.
   |  sw MULTRES, SAVE_MULTRES
   |    .FPU mtc1 TMP3, TOBIT
   |  lw TMP1, LFUNC:RB->pc
@@ -2712,6 +2519,7 @@ static void build_subroutines(BuildCtx *ctx)
   |//-- Math helper functions ----------------------------------------------
   |//-----------------------------------------------------------------------
   |
+  |// Hard-float round to integer.
   |// Modifies AT, TMP0, FRET1, FRET2, f4. Keeps all others incl. FARG1.
   |.macro vm_round_hf, func
   |  lui TMP0, 0x4330                  // Hiword of 2^52 (double).
@@ -2755,22 +2563,9 @@ static void build_subroutines(BuildCtx *ctx)
   |.  mov.d FRET1, FARG1
   |.endmacro
   |
-  |.macro vm_round_sf, func
-  |  addiu sp, sp, -8
-  |  load_got func
-  |  sw ra, 0(sp)
-  |  call_extern
-  |.   nop
-  |  lw ra, 0(sp)
-  |  jr ra
-  |.  addiu sp, sp, 8
-  |.endmacro
-  |
   |.macro vm_round, func
   |.if FPU
   |  vm_round_hf, func
-  |.else
-  |  vm_round_sf, func
   |.endif
   |.endmacro
   |
@@ -2783,6 +2578,159 @@ static void build_subroutines(BuildCtx *ctx)
   |  vm_round trunc
   |.endif
   |
+  |// Soft-float integer to number conversion.
+  |.macro sfi2d, AHI, ALO
+  |.if not FPU
+  |  beqz ALO, >9                      // Handle zero first.
+  |.  sra TMP0, ALO, 31
+  |  xor TMP1, ALO, TMP0
+  |  subu TMP1, TMP1, TMP0             // Absolute value in TMP1.
+  |  clz AHI, TMP1
+  |    andi TMP0, TMP0, 0x800          // Mask sign bit.
+  |  li AT, 0x3ff+31-1
+  |   sllv TMP1, TMP1, AHI             // Align mantissa left with leading 1.
+  |  subu AHI, AT, AHI                 // Exponent - 1 in AHI.
+  |   sll ALO, TMP1, 21
+  |  or AHI, AHI, TMP0                 // Sign | Exponent.
+  |   srl TMP1, TMP1, 11
+  |  sll AHI, AHI, 20                  // Align left.
+  |  jr ra
+  |.  addu AHI, AHI, TMP1              // Add mantissa, increment exponent.
+  |9:
+  |  jr ra
+  |.  li AHI, 0
+  |.endif
+  |.endmacro
+  |
+  |// Input SFARG1LO. Output: SFARG1*. Temporaries: AT, TMP0, TMP1.
+  |->vm_sfi2d_1:
+  |  sfi2d SFARG1HI, SFARG1LO
+  |
+  |// Input SFARG2LO. Output: SFARG2*. Temporaries: AT, TMP0, TMP1.
+  |->vm_sfi2d_2:
+  |  sfi2d SFARG2HI, SFARG2LO
+  |
+  |// Soft-float comparison. Equivalent to c.eq.d.
+  |// Input: SFARG*. Output: CRET1. Temporaries: AT, TMP0, TMP1.
+  |->vm_sfcmpeq:
+  |.if not FPU
+  |  sll AT, SFARG1HI, 1
+  |  sll TMP0, SFARG2HI, 1
+  |  or CRET1, SFARG1LO, SFARG2LO
+  |  or TMP1, AT, TMP0
+  |  or TMP1, TMP1, CRET1
+  |  beqz TMP1, >8                     // Both args +-0: return 1.
+  |.  sltu CRET1, r0, SFARG1LO
+  |  lui TMP1, 0xffe0
+  |  addu AT, AT, CRET1
+  |   sltu CRET1, r0, SFARG2LO
+  |  sltu AT, TMP1, AT
+  |   addu TMP0, TMP0, CRET1
+  |   sltu TMP0, TMP1, TMP0
+  |  or TMP1, AT, TMP0
+  |  bnez TMP1, >9                     // Either arg is NaN: return 0;
+  |.  xor TMP0, SFARG1HI, SFARG2HI
+  |  xor TMP1, SFARG1LO, SFARG2LO
+  |  or AT, TMP0, TMP1
+  |  jr ra
+  |.  sltiu CRET1, AT, 1               // Same values: return 1.
+  |8:
+  |  jr ra
+  |.  li CRET1, 1
+  |9:
+  |  jr ra
+  |.  li CRET1, 0
+  |.endif
+  |
+  |// Soft-float comparison. Equivalent to c.ult.d and c.olt.d.
+  |// Input: SFARG*. Output: CRET1. Temporaries: AT, TMP0, TMP1, CRET2.
+  |->vm_sfcmpult:
+  |.if not FPU
+  |  b >1
+  |.  li CRET2, 1
+  |.endif
+  |
+  |->vm_sfcmpolt:
+  |.if not FPU
+  |  li CRET2, 0
+  |1:
+  |  sll AT, SFARG1HI, 1
+  |  sll TMP0, SFARG2HI, 1
+  |  or CRET1, SFARG1LO, SFARG2LO
+  |  or TMP1, AT, TMP0
+  |  or TMP1, TMP1, CRET1
+  |  beqz TMP1, >8                     // Both args +-0: return 0.
+  |.  sltu CRET1, r0, SFARG1LO
+  |  lui TMP1, 0xffe0
+  |  addu AT, AT, CRET1
+  |   sltu CRET1, r0, SFARG2LO
+  |  sltu AT, TMP1, AT
+  |   addu TMP0, TMP0, CRET1
+  |   sltu TMP0, TMP1, TMP0
+  |  or TMP1, AT, TMP0
+  |  bnez TMP1, >9                     // Either arg is NaN: return 0 or 1;
+  |.  and AT, SFARG1HI, SFARG2HI
+  |  bltz AT, >5                       // Both args negative?
+  |.  nop
+  |  beq SFARG1HI, SFARG2HI, >8
+  |.  sltu CRET1, SFARG1LO, SFARG2LO
+  |  jr ra
+  |.  slt CRET1, SFARG1HI, SFARG2HI
+  |5:  // Swap conditions if both operands are negative.
+  |  beq SFARG1HI, SFARG2HI, >8
+  |.  sltu CRET1, SFARG2LO, SFARG1LO
+  |  jr ra
+  |.  slt CRET1, SFARG2HI, SFARG1HI
+  |8:
+  |  jr ra
+  |.  nop
+  |9:
+  |  jr ra
+  |.  move CRET1, CRET2
+  |.endif
+  |
+  |// Soft-float comparison. Equivalent to c.ole.d a, b or c.ole.d b, a.
+  |// Input: SFARG*, TMP3. Output: CRET1. Temporaries: AT, TMP0, TMP1.
+  |->vm_sfcmpolex:
+  |.if not FPU
+  |  sll AT, SFARG1HI, 1
+  |  sll TMP0, SFARG2HI, 1
+  |  or CRET1, SFARG1LO, SFARG2LO
+  |  or TMP1, AT, TMP0
+  |  or TMP1, TMP1, CRET1
+  |  beqz TMP1, >8                     // Both args +-0: return 1.
+  |.  sltu CRET1, r0, SFARG1LO
+  |  lui TMP1, 0xffe0
+  |  addu AT, AT, CRET1
+  |   sltu CRET1, r0, SFARG2LO
+  |  sltu AT, TMP1, AT
+  |   addu TMP0, TMP0, CRET1
+  |   sltu TMP0, TMP1, TMP0
+  |  or TMP1, AT, TMP0
+  |  bnez TMP1, >9                     // Either arg is NaN: return 0;
+  |.  and AT, SFARG1HI, SFARG2HI
+  |  xor AT, AT, TMP3
+  |  bltz AT, >5                       // Both args negative?
+  |.  nop
+  |  beq SFARG1HI, SFARG2HI, >6
+  |.  sltu CRET1, SFARG2LO, SFARG1LO
+  |  jr ra
+  |.  slt CRET1, SFARG2HI, SFARG1HI
+  |5:  // Swap conditions if both operands are negative.
+  |  beq SFARG1HI, SFARG2HI, >6
+  |.  sltu CRET1, SFARG1LO, SFARG2LO
+  |  slt CRET1, SFARG1HI, SFARG2HI
+  |6:
+  |  jr ra
+  |.  nop
+  |8:
+  |  jr ra
+  |.  li CRET1, 1
+  |9:
+  |  jr ra
+  |.  li CRET1, 0
+  |.endif
+  |
   |//-----------------------------------------------------------------------
   |//-- Miscellaneous functions --------------------------------------------
   |//-----------------------------------------------------------------------
@@ -2815,6 +2763,7 @@ static void build_subroutines(BuildCtx *ctx)
   |  // Returns lua_State *.
   |  lw BASE, L:CRET1->base
   |  lw RC, L:CRET1->top
+  |     li TISNUM, LJ_TISNUM           // Setup type comparison constants.
   |   move L, CRET1
   |     .FPU lui TMP3, 0x59c0          // TOBIT = 2^52 + 2^51 (float).
   |  lw LFUNC:RB, FRAME_FUNC(BASE)
@@ -2882,10 +2831,13 @@ static void build_subroutines(BuildCtx *ctx)
   |  lw ra, -4(r16)
   |  sw CRET1, CCSTATE:TMP1->gpr[0]
   |  sw CRET2, CCSTATE:TMP1->gpr[1]
-  |  .FPU sdc1 FRET1, CCSTATE:TMP1->fpr[0]
-  |  .FPU sdc1 FRET2, CCSTATE:TMP1->fpr[1]
-  |  sw CARG1, CCSTATE:TMP1->gpr[2]    // MIPS32 soft-float.
-  |  sw CARG2, CCSTATE:TMP1->gpr[3]    // Complex doubles are returned in v0, v1, a0, a1.
+  |.if FPU
+  |  sdc1 FRET1, CCSTATE:TMP1->fpr[0]
+  |  sdc1 FRET2, CCSTATE:TMP1->fpr[1]
+  |.else
+  |  sw CARG1, CCSTATE:TMP1->gpr[2]    // Soft-float: complex double .im part.
+  |  sw CARG2, CCSTATE:TMP1->gpr[3]
+  |.endif
   |  move sp, r16
   |  jr ra
   |.  move r16, TMP2
@@ -2909,127 +2861,143 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
 
   case BC_ISLT: case BC_ISGE: case BC_ISLE: case BC_ISGT:
     |  // RA = src1*8, RD = src2*8, JMP with RD = target
-    |  addu CARG2, BASE, RA
-    |   addu CARG3, BASE, RD
-    |  lw TMP0, HI(CARG2)
-    |   lw TMP1, HI(CARG3)
-    |  sltiu TMP0, TMP0, LJ_TISNUM
-    |   sltiu TMP1, TMP1, LJ_TISNUM
+    |.macro bc_comp, FRA, FRD, RAHI, RALO, RDHI, RDLO, movop, fmovop, fcomp, sfcomp
+    |  addu RA, BASE, RA
+    |   addu RD, BASE, RD
+    |  lw RAHI, HI(RA)
+    |   lw RDHI, HI(RD)
     |    lhu TMP2, OFS_RD(PC)
-    |  and TMP0, TMP0, TMP1
     |    addiu PC, PC, 4
-    |  beqz TMP0, ->vmeta_comp
-    |.   lui TMP1, (-(BCBIAS_J*4 >> 16) & 65535)
-    |   load_double f0, CARG1, CARG2, 0(CARG2)
+    |  bne RAHI, TISNUM, >2
+    |.  lw RALO, LO(RA)
+    |    lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535)
+    |  lw RDLO, LO(RD)
+    |  bne RDHI, TISNUM, >5
+    |.   decode_RD4b TMP2
+    |  slt AT, SFARG1LO, SFARG2LO
+    |    addu TMP2, TMP2, TMP3
+    |  movop TMP2, r0, AT
+    |1:
+    |  addu PC, PC, TMP2
+    |  ins_next
+    |
+    |2:  // RA is not an integer.
+    |  sltiu AT, RAHI, LJ_TISNUM
+    |  beqz AT, ->vmeta_comp
+    |.   lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535)
+    |  sltiu AT, RDHI, LJ_TISNUM
     |.if FPU
-    |   ldc1 f2, 0(CARG3)
+    |  ldc1 FRA, 0(RA)
+    |   ldc1 FRD, 0(RD)
     |.else
-    |   lw CARG4, 4(CARG3)
-    |   lw CARG3, 0(CARG3)
+    |   lw RDLO, LO(RD)
     |.endif
-    |    decode_RD4b TMP2
-    |    addu TMP2, TMP2, TMP1
+    |  beqz AT, >4
+    |.   decode_RD4b TMP2
+    |3:  // RA and RD are both numbers.
     |.if FPU
-    if (op == BC_ISLT || op == BC_ISGE) {
-      |  c.olt.d f0, f2
-    } else {
-      |  c.ole.d f0, f2
-    }
-    if (op == BC_ISLT || op == BC_ISLE) {
-      |  movf TMP2, r0
-    } else {
-      |  movt TMP2, r0
-    }
+    |  fcomp f20, f22
+    |   addu TMP2, TMP2, TMP3
+    |  b <1
+    |.  fmovop TMP2, r0
     |.else
-    |  load_got __ledf2
-    |  sw RD, TEMP_SAVE_1
-    |  sw TMP1, TEMP_SAVE_2
-    |  call_extern              //CRET1 = f0<=f2
-    |.  sw TMP2, TEMP_SAVE_3
-    |  lw TMP2, TEMP_SAVE_3
-    |  lw TMP1, TEMP_SAVE_2
+    |  bal sfcomp
+    |.   addu TMP2, TMP2, TMP3
+    |  b <1
+    |.  movop TMP2, r0, CRET1
+    |.endif
+    |
+    |4:  // RA is a number, RD is not a number.
+    |  bne RDHI, TISNUM, ->vmeta_comp
+    |  // RA is a number, RD is an integer. Convert RD to a number.
+    |.if FPU
+    |.  lwc1 FRD, LO(RD)
+    |  b <3
+    |.  cvt.d.w FRD, FRD
+    |.else
+    |.  nop
+    |.if "RDHI" == "SFARG1HI"
+    |  bal ->vm_sfi2d_1
+    |.else
+    |  bal ->vm_sfi2d_2
+    |.endif
+    |.  nop
+    |  b <3
+    |.  nop
+    |.endif
+    |
+    |5:  // RA is an integer, RD is not an integer
+    |  sltiu AT, RDHI, LJ_TISNUM
+    |  beqz AT, ->vmeta_comp
+    |  // RA is an integer, RD is a number. Convert RA to a number.
+    |.if FPU
+    |.  mtc1 RALO, FRA
+    |   ldc1 FRD, 0(RD)
+    |  b <3
+    |   cvt.d.w FRA, FRA
+    |.else
+    |.  nop
+    |.if "RAHI" == "SFARG1HI"
+    |  bal ->vm_sfi2d_1
+    |.else
+    |  bal ->vm_sfi2d_2
+    |.endif
+    |.  nop
+    |  b <3
+    |.  nop
+    |.endif
+    |.endmacro
+    |
     if (op == BC_ISLT) {
-      |  bltz CRET1, >1
+      |  bc_comp f20, f22, SFARG1HI, SFARG1LO, SFARG2HI, SFARG2LO, movz, movf, c.olt.d, ->vm_sfcmpolt
+    } else if (op == BC_ISGE) {
+      |  bc_comp f20, f22, SFARG1HI, SFARG1LO, SFARG2HI, SFARG2LO, movn, movt, c.olt.d, ->vm_sfcmpolt
     } else if (op == BC_ISLE) {
-      |  blez CRET1, >1
-    }  else if (op == BC_ISGT) {
-      |  bgtz CRET1, >1
-    }  else {
-      |  bgez CRET1, >1
+      |  bc_comp f22, f20, SFARG2HI, SFARG2LO, SFARG1HI, SFARG1LO, movn, movt, c.ult.d, ->vm_sfcmpult
+    } else {
+      |  bc_comp f22, f20, SFARG2HI, SFARG2LO, SFARG1HI, SFARG1LO, movz, movf, c.ult.d, ->vm_sfcmpult
     }
-    |.  lw RD, TEMP_SAVE_1
-    |  move TMP2, r0
-    |1:
-    |.endif
-    |  addu PC, PC, TMP2
-    |  ins_next
     break;
 
   case BC_ISEQV: case BC_ISNEV:
     vk = op == BC_ISEQV;
     |  // RA = src1*8, RD = src2*8, JMP with RD = target
     |  addu RA, BASE, RA
-    |   addiu PC, PC, 4
-    |  lw TMP0, HI(RA)
+    |    addiu PC, PC, 4
     |  addu RD, BASE, RD
+    |  lw SFARG1HI, HI(RA)
     |    lhu TMP2, -4+OFS_RD(PC)
+    |  lw SFARG2HI, HI(RD)
     |    lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535)
-    |   lw TMP1, HI(RD)
-    |    decode_RD4b TMP2
-    |  sltiu AT, TMP0, LJ_TISNUM
-    |  sltiu CARG1, TMP1, LJ_TISNUM
-    |   load_double f2, CARG3, CARG4, 0(RD)
-    |    lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535)
-    |  and AT, AT, CARG1
-    |   load_double f0, CARG1, CARG2, 0(RA)
-    |  beqz AT, >5
-    |.   addu TMP2, TMP2, TMP3
-    |.if FPU
-    |  c.eq.d f0, f2
+    |  sltu AT, TISNUM, SFARG1HI
+    |  sltu TMP0, TISNUM, SFARG2HI
+    |  or AT, AT, TMP0
     if (vk) {
-      |  movf TMP2, r0
+      |  beqz AT, ->BC_ISEQN_Z
     } else {
-      |  movt TMP2, r0
+      |  beqz AT, ->BC_ISNEN_Z
     }
-    |.else
-    |  load_got __ledf2
-    |  sw RD, TEMP_SAVE_1
-    |  call_extern
-    |.  sw TMP2, TEMP_SAVE_2
-    |  lw RD, TEMP_SAVE_1
-    |  lw TMP2, TEMP_SAVE_2
-    if (vk) {
-      |  beqz CRET1, >4
-      |.  nop
-    } else {
-      |  bnez CRET1, >4
-      |.  nop
-    }
-    |  move TMP2, r0
-    |4:
-    |.endif
-    |1:
-    |  addu PC, PC, TMP2
-    |  ins_next
-    |5:  // Either or both types are not numbers.
-    |  lw CARG2, LO(RA)
-    |  lw CARG3, LO(RD)
+    |.   decode_RD4b TMP2
+    |  // Either or both types are not numbers.
+    |  lw SFARG1LO, LO(RA)
+    |  lw SFARG2LO, LO(RD)
+    |  addu TMP2, TMP2, TMP3
     |.if FFI
     |  li TMP3, LJ_TCDATA
-    |  beq TMP0, TMP3, ->vmeta_equal_cd
+    |  beq SFARG1HI, TMP3, ->vmeta_equal_cd
     |.endif
-    |.  sltiu AT, TMP0, LJ_TISPRI              // Not a primitive?
+    |.  sltiu AT, SFARG1HI, LJ_TISPRI          // Not a primitive?
     |.if FFI
-    |  beq TMP1, TMP3, ->vmeta_equal_cd
+    |  beq SFARG2HI, TMP3, ->vmeta_equal_cd
     |.endif
-    |.  xor TMP3, CARG2, CARG3                 // Same tv?
-    |  xor TMP1, TMP1, TMP0                    // Same type?
-    |  sltiu CARG1, TMP0, LJ_TISTABUD+1                // Table or userdata?
+    |.  xor TMP3, SFARG1LO, SFARG2LO           // Same tv?
+    |  xor SFARG2HI, SFARG2HI, SFARG1HI                // Same type?
+    |  sltiu TMP0, SFARG1HI, LJ_TISTABUD+1     // Table or userdata?
     |  movz TMP3, r0, AT                       // Ignore tv if primitive.
-    |  movn CARG1, r0, TMP1                    // Tab/ud and same type?
-    |  or AT, TMP1, TMP3                       // Same type && (pri||same tv).
-    |  movz CARG1, r0, AT
-    |  beqz CARG1, <1  // Done if not tab/ud or not same type or same tv.
+    |  movn TMP0, r0, SFARG2HI                 // Tab/ud and same type?
+    |  or AT, SFARG2HI, TMP3                   // Same type && (pri||same tv).
+    |  movz TMP0, r0, AT
+    |  beqz TMP0, >1   // Done if not tab/ud or not same type or same tv.
     if (vk) {
       |.  movn TMP2, r0, AT
     } else {
@@ -3037,15 +3005,18 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     }
     |  // Different tables or userdatas. Need to check __eq metamethod.
     |  // Field metatable must be at same offset for GCtab and GCudata!
-    |  lw TAB:TMP1, TAB:CARG2->metatable
-    |  beqz TAB:TMP1, <1               // No metatable?
+    |  lw TAB:TMP1, TAB:SFARG1LO->metatable
+    |  beqz TAB:TMP1, >1               // No metatable?
     |.  nop
     |  lbu TMP1, TAB:TMP1->nomm
     |  andi TMP1, TMP1, 1<<MM_eq
-    |  bnez TMP1, <1                   // Or 'no __eq' flag set?
+    |  bnez TMP1, >1                   // Or 'no __eq' flag set?
     |.  nop
     |  b ->vmeta_equal                 // Handle __eq metamethod.
-    |.  li CARG4, 1-vk                 // ne = 0 or 1.
+    |.  li TMP0, 1-vk                  // ne = 0 or 1.
+    |1:
+    |  addu PC, PC, TMP2
+    |  ins_next
     break;
 
   case BC_ISEQS: case BC_ISNES:
@@ -3082,61 +3053,124 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     vk = op == BC_ISEQN;
     |  // RA = src*8, RD = num_const*8, JMP with RD = target
     |  addu RA, BASE, RA
-    |   addiu PC, PC, 4
-    |  lw TMP0, HI(RA)
-    |   load_double f0, CARG1, CARG2, 0(RA)
-    |  addu RD, KBASE, RD
-    |    lhu TMP2, -4+OFS_RD(PC)
-    |   load_double f2, CARG3, CARG4, 0(RD)
+    |   addu RD, KBASE, RD
+    |  lw SFARG1HI, HI(RA)
+    |   lw SFARG2HI, HI(RD)
+    |    lhu TMP2, OFS_RD(PC)
+    |    addiu PC, PC, 4
     |    lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535)
-    |  sltiu AT, TMP0, LJ_TISNUM
     |    decode_RD4b TMP2
+    if (vk) {
+      |->BC_ISEQN_Z:
+    } else {
+      |->BC_ISNEN_Z:
+    }
+    |  bne SFARG1HI, TISNUM, >3
+    |.  lw SFARG1LO, LO(RA)
+    |  lw SFARG2LO, LO(RD)
+    |    addu TMP2, TMP2, TMP3
+    |  bne SFARG2HI, TISNUM, >6
+    |.  xor AT, SFARG1LO, SFARG2LO
+    if (vk) {
+      |  movn TMP2, r0, AT
+      |1:
+      |  addu PC, PC, TMP2
+      |2:
+    } else {
+      |  movz TMP2, r0, AT
+      |1:
+      |2:
+      |  addu PC, PC, TMP2
+    }
+    |  ins_next
+    |
+    |3:  // RA is not an integer.
+    |  sltiu AT, SFARG1HI, LJ_TISNUM
     |.if FFI
-    |  beqz AT, >5
+    |  beqz AT, >8
     |.else
-    |  beqz AT, >1
+    |  beqz AT, <2
     |.endif
     |.   addu TMP2, TMP2, TMP3
+    |  sltiu AT, SFARG2HI, LJ_TISNUM
+    |.if FPU
+    |  ldc1 f20, 0(RA)
+    |   ldc1 f22, 0(RD)
+    |.endif
+    |  beqz AT, >5
+    |.  lw SFARG2LO, LO(RD)
+    |4:  // RA and RD are both numbers.
     |.if FPU
-    |   c.eq.d f0, f2
+    |  c.eq.d f20, f22
+    |  b <1
     if (vk) {
-      |  movf TMP2, r0
-      |  addu PC, PC, TMP2
-      |1:
+      |.  movf TMP2, r0
     } else {
-      |  movt TMP2, r0
-      |1:
-      |  addu PC, PC, TMP2
+      |.  movt TMP2, r0
     }
     |.else
-    |  load_got __ledf2
-    |   sw RD, TEMP_SAVE_1
-    |  call_extern
-    |.  sw TMP2, TEMP_SAVE_2
-    |  lw RD, TEMP_SAVE_1
-    |  lw TMP2, TEMP_SAVE_2
+    |  bal ->vm_sfcmpeq
+    |.  nop
+    |  b <1
     if (vk) {
-      |  beqz CRET1, >4
-      |.  nop
-      |  move TMP2, r0
-      |4:
-      |  addu PC, PC, TMP2
-      |1:
+      |.  movz TMP2, r0, CRET1
     } else {
-      |  bnez CRET1, >1
-      |.  nop
-      |  move TMP2, r0
-      |1:
-      |  addu PC, PC, TMP2
+      |.  movn TMP2, r0, CRET1
     }
     |.endif
-    |  ins_next
+    |
+    |5:  // RA is a number, RD is not a number.
     |.if FFI
-    |5:
+    |  bne SFARG2HI, TISNUM, >9
+    |.else
+    |  bne SFARG2HI, TISNUM, <2
+    |.endif
+    |  // RA is a number, RD is an integer. Convert RD to a number.
+    |.if FPU
+    |.  lwc1 f22, LO(RD)
+    |  b <4
+    |.  cvt.d.w f22, f22
+    |.else
+    |.  nop
+    |  bal ->vm_sfi2d_2
+    |.  nop
+    |  b <4
+    |.  nop
+    |.endif
+    |
+    |6:  // RA is an integer, RD is not an integer
+    |  sltiu AT, SFARG2HI, LJ_TISNUM
+    |.if FFI
+    |  beqz AT, >9
+    |.else
+    |  beqz AT, <2
+    |.endif
+    |  // RA is an integer, RD is a number. Convert RA to a number.
+    |.if FPU
+    |.  mtc1 SFARG1LO, f20
+    |   ldc1 f22, 0(RD)
+    |  b <4
+    |   cvt.d.w f20, f20
+    |.else
+    |.  nop
+    |  bal ->vm_sfi2d_1
+    |.  nop
+    |  b <4
+    |.  nop
+    |.endif
+    |
+    |.if FFI
+    |8:
     |  li AT, LJ_TCDATA
-    |  beq TMP0, AT, ->vmeta_equal_cd
+    |  bne SFARG1HI, AT, <2
     |.  nop
-    |  b <1
+    |  b ->vmeta_equal_cd
+    |.  nop
+    |9:
+    |  li AT, LJ_TCDATA
+    |  bne SFARG2HI, AT, <2
+    |.  nop
+    |  b ->vmeta_equal_cd
     |.  nop
     |.endif
     break;
@@ -3188,7 +3222,8 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
       |  addu PC, PC, TMP2
     } else {
       |  sltiu TMP0, TMP0, LJ_TISTRUECOND
-      |  load_double1 0(RD)
+      |  lw SFRETHI, HI(RD)
+      |   lw SFRETLO, LO(RD)
       if (op == BC_ISTC) {
        |  beqz TMP0, >1
       } else {
@@ -3198,7 +3233,8 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
       |   decode_RD4b TMP2
       |   lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535)
       |   addu TMP2, TMP2, TMP3
-      |  store_double1 0(RA)
+      |  sw SFRETHI, HI(RA)
+      |   sw SFRETLO, LO(RA)
       |   addu PC, PC, TMP2
       |1:
     }
@@ -3230,10 +3266,12 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
   case BC_MOV:
     |  // RA = dst*8, RD = src*8
     |  addu RD, BASE, RD
-    |  addu RA, BASE, RA
-    |  load_double1 0(RD)
+    |   addu RA, BASE, RA
+    |  lw SFRETHI, HI(RD)
+    |   lw SFRETLO, LO(RD)
     |  ins_next1
-    |  store_double1 0(RA)
+    |  sw SFRETHI, HI(RA)
+    |   sw SFRETLO, LO(RA)
     |  ins_next2
     break;
   case BC_NOT:
@@ -3250,23 +3288,25 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     break;
   case BC_UNM:
     |  // RA = dst*8, RD = src*8
-    |  addu CARG3, BASE, RD
+    |  addu RB, BASE, RD
+    |  lw SFARG1HI, HI(RB)
     |   addu RA, BASE, RA
-    |  lw TMP0, HI(CARG3)
-    |  sltiu AT, TMP0, LJ_TISNUM
-    |   load_double f0, CARG1, CARG2, 0(CARG3)
-    |.if FPU
-    |  beqz AT, ->vmeta_unm
-    |.  neg.d f0, f0
-    |.else
-    |   lui TMP1, 0x8000
-    |   xor CRET1, TMP1, CARG1
-    |  beqz AT, ->vmeta_unm
-    |.   move CRET2, CARG2
-    |.endif
+    |  bne SFARG1HI, TISNUM, >2
+    |.  lw SFARG1LO, LO(RB)
+    |  lui TMP1, 0x8000
+    |  beq SFARG1LO, TMP1, ->vmeta_unm // Meta handler deals with -2^31.
+    |.  negu SFARG1LO, SFARG1LO
+    |1:
     |  ins_next1
-    |   store_double f0, CRET1, CRET2, 0(RA)
+    |  sw SFARG1HI, HI(RA)
+    |   sw SFARG1LO, LO(RA)
     |  ins_next2
+    |2:
+    |  sltiu AT, SFARG1HI, LJ_TISNUM
+    |  beqz AT, ->vmeta_unm
+    |.  lui TMP1, 0x8000
+    |  b <1
+    |.  xor SFARG1HI, SFARG1HI, TMP1
     break;
   case BC_LEN:
     |  // RA = dst*8, RD = src*8
@@ -3277,16 +3317,11 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  li AT, LJ_TSTR
     |  bne TMP0, AT, >2
     |.  li AT, LJ_TTAB
-    |  lw CRET1, STR:CARG1->len
+    |   lw CRET1, STR:CARG1->len
     |1:
-    |.if FPU
-    |  mtc1 CRET1, f0
-    |  cvt.d.w f0, f0
-    |.else
-    |  cvti2d CRET1
-    |.endif
     |  ins_next1
-    |   store_double f0, CRET1, CRET2, 0(RA)
+    |  sw TISNUM, HI(RA)
+    |   sw CRET1, LO(RA)
     |  ins_next2
     |2:
     |  bne TMP0, AT, ->vmeta_len
@@ -3317,178 +3352,231 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
 
   /* -- Binary ops -------------------------------------------------------- */
 
-    |.macro ins_arithpre
+    |.macro fpmod, a, b, c
+    |  bal ->vm_floor     // floor(b/c)
+    |.  div.d FARG1, b, c
+    |  mul.d a, FRET1, c
+    |  sub.d a, b, a      // b - floor(b/c)*c
+    |.endmacro
+
+    |.macro sfpmod
+    |  addiu sp, sp, -16
+    |
+    |  load_got __divdf3
+    |  sw SFARG1HI, HI(sp)
+    |   sw SFARG1LO, LO(sp)
+    |  sw SFARG2HI, 8+HI(sp)
+    |  call_extern
+    |.  sw SFARG2LO, 8+LO(sp)
+    |
+    |  load_got floor
+    |  move SFARG1HI, SFRETHI
+    |  call_extern
+    |.  move SFARG1LO, SFRETLO
+    |
+    |  load_got __muldf3
+    |  move SFARG1HI, SFRETHI
+    |   move SFARG1LO, SFRETLO
+    |  lw SFARG2HI, 8+HI(sp)
+    |  call_extern
+    |.  lw SFARG2LO, 8+LO(sp)
+    |
+    |  load_got __subdf3
+    |  lw SFARG1HI, HI(sp)
+    |   lw SFARG1LO, LO(sp)
+    |  move SFARG2HI, SFRETHI
+    |  call_extern
+    |.  move SFARG2LO, SFRETLO
+    |
+    |  addiu sp, sp, 16
+    |.endmacro
+
+    |.macro ins_arithpre, label
     ||vk = ((int)op - BC_ADDVN) / (BC_ADDNV-BC_ADDVN);
-    |  decode_RB8a RB, INS
-    |  decode_RB8b RB
-    |   decode_RDtoRC8 RC, RD
     |  // RA = dst*8, RB = src1*8, RC = src2*8 | num_const*8
     ||switch (vk) {
     ||case 0:
-    |   addu CARG3, BASE, RB
-    |    addu CARG4, KBASE, RC
-    |   lw TMP1, HI(CARG3)
-    |  sltiu AT, TMP1, LJ_TISNUM
-    |   load_double f20, CARG1, CARG2, 0(CARG3)
-    |   load_double f22, CARG3, CARG4, 0(CARG4)
-    |.if FPU
-    |   beqz AT, ->vmeta_arith
-    |.else
-    |   beqz AT, ->vmeta_arith_vn
+    |   decode_RB8a RB, INS
+    |   decode_RB8b RB
+    |    decode_RDtoRC8 RC, RD
+    |   // RA = dst*8, RB = src1*8, RC = num_const*8
+    |   addu RB, BASE, RB
+    |.if "label" ~= "none"
+    |   b label
     |.endif
-    |.   addu RA, BASE, RA
+    |.   addu RC, KBASE, RC
     ||  break;
     ||case 1:
-    |   addu CARG4, BASE, RB
-    |    addu CARG3, KBASE, RC
-    |   lw TMP1, HI(CARG4)
-    |  sltiu AT, TMP1, LJ_TISNUM
-    |  load_double f20, CARG1, CARG2, 0(CARG3)
-    |   load_double f22, CARG3, CARG4, 0(CARG4)
-    |.if FPU
-    |   beqz AT, ->vmeta_arith
-    |.else
-    |   beqz AT, ->vmeta_arith_nv
+    |   decode_RB8a RC, INS
+    |   decode_RB8b RC
+    |    decode_RDtoRC8 RB, RD
+    |   // RA = dst*8, RB = num_const*8, RC = src1*8
+    |   addu RC, BASE, RC
+    |.if "label" ~= "none"
+    |   b label
     |.endif
-    |.   addu RA, BASE, RA
+    |.   addu RB, KBASE, RB
     ||  break;
     ||default:
-    |   addu CARG3, BASE, RB
-    |    addu CARG4, BASE, RC
-    |   lw TMP1, HI(CARG3)
-    |    lw TMP2, HI(CARG4)
-    |  sltiu AT, TMP1, LJ_TISNUM
-    |  sltiu TMP0, TMP2, LJ_TISNUM
-    |  and AT, AT, TMP0
-    |  load_double f20, CARG1, CARG2, 0(CARG3)
-    |   load_double f22, CARG3, CARG4, 0(CARG4)
-    |.if FPU
-    |   beqz AT, ->vmeta_arith
-    |.else
-    |   beqz AT, ->vmeta_arith_vv
+    |   decode_RB8a RB, INS
+    |   decode_RB8b RB
+    |    decode_RDtoRC8 RC, RD
+    |   // RA = dst*8, RB = src1*8, RC = src2*8
+    |   addu RB, BASE, RB
+    |.if "label" ~= "none"
+    |   b label
     |.endif
-    |.   addu RA, BASE, RA
+    |.   addu RC, BASE, RC
     ||  break;
     ||}
     |.endmacro
     |
-    |.macro ins_arithfallback
-    ||switch (vk) {
-    ||case 0:
-    |   b ->vmeta_arith_vn
-    |.  nop
-    ||  break;
-    ||case 1:
-    |   b ->vmeta_arith_nv
-    |.  nop
-    ||  break;
-    ||default:
-    |   b ->vmeta_arith_vv
-    |.  nop
-    ||  break;
-    ||}
-    |.endmacro
+    |.macro ins_arith, intins, fpins, fpcall, label
+    |  ins_arithpre none
     |
-    |.if FPU
-    |.macro fpmod, a, b, c
-    |->BC_MODVN_Z:
-    |  bal ->vm_floor     // floor(b/c)
-    |.  div.d FARG1, b, c
-    |  mul.d a, FRET1, c
-    |  sub.d a, b, a      // b - floor(b/c)*c
-    |.endmacro
-    |.else
+    |.if "label" ~= "none"
+    |label:
+    |.endif
     |
-    |.macro sfpmod
-    |->BC_MODVN_Z:
-    |  load_got __divdf3
-    |  sw CARG1, TEMP_SAVE_1
-    |  sw CARG2, TEMP_SAVE_2
-    |  sw CARG3, TEMP_SAVE_3
-    |  call_extern
-    |.  sw CARG4, TEMP_SAVE_4
-    |  move CARG1, CRET1
-    |  bal ->vm_floor
-    |.  move CARG2, CRET2
-    |  load_got __muldf3
-    |  move CARG1, CRET1
-    |  move CARG2, CRET2
-    |  lw CARG3, TEMP_SAVE_3
-    |  call_extern
-    |.  lw CARG4, TEMP_SAVE_4
-    |  load_got __subdf3
-    |  lw CARG1, TEMP_SAVE_1
-    |  lw CARG2, TEMP_SAVE_2
-    |  move CARG3, CRET1
+    |  lw SFARG1HI, HI(RB)
+    |   lw SFARG2HI, HI(RC)
+    |
+    |.if "intins" ~= "div"
+    |
+    |  // Check for two integers.
+    |  lw SFARG1LO, LO(RB)
+    |  bne SFARG1HI, TISNUM, >5
+    |.  lw SFARG2LO, LO(RC)
+    |  bne SFARG2HI, TISNUM, >5
+    |
+    |.if "intins" == "addu"
+    |.  intins CRET1, SFARG1LO, SFARG2LO
+    |  xor TMP1, CRET1, SFARG1LO       // ((y^a) & (y^b)) < 0: overflow.
+    |  xor TMP2, CRET1, SFARG2LO
+    |  and TMP1, TMP1, TMP2
+    |  bltz TMP1, ->vmeta_arith
+    |.  addu RA, BASE, RA
+    |.elif "intins" == "subu"
+    |.  intins CRET1, SFARG1LO, SFARG2LO
+    |  xor TMP1, CRET1, SFARG1LO       // ((y^a) & (a^b)) < 0: overflow.
+    |  xor TMP2, SFARG1LO, SFARG2LO
+    |  and TMP1, TMP1, TMP2
+    |  bltz TMP1, ->vmeta_arith
+    |.  addu RA, BASE, RA
+    |.elif "intins" == "mult"
+    |.  intins SFARG1LO, SFARG2LO
+    |  mflo CRET1
+    |  mfhi TMP2
+    |  sra TMP1, CRET1, 31
+    |  bne TMP1, TMP2, ->vmeta_arith
+    |.  addu RA, BASE, RA
+    |.else
+    |.  load_got lj_vm_modi
+    |  beqz SFARG2LO, ->vmeta_arith
+    |.  addu RA, BASE, RA
+    |.if ENDIAN_BE
+    |  move CARG1, SFARG1LO
+    |.endif
     |  call_extern
-    |.  move CARG4, CRET2
-    |.endmacro
+    |.  move CARG2, SFARG2LO
     |.endif
     |
-    |.macro ins_arith, intins, fpins, fpcall
-    |  ins_arithpre
-    |.if "fpins" == "fpmod_"
-    |  b ->BC_MODVN_Z     // Avoid 3 copies. It's slow anyway.
-    |.  nop
-    |.else
+    |  ins_next1
+    |  sw TISNUM, HI(RA)
+    |   sw CRET1, LO(RA)
+    |3:
+    |  ins_next2
+    |
+    |.elif not FPU
+    |
+    |  lw SFARG1LO, LO(RB)
+    |   lw SFARG2LO, LO(RC)
+    |
+    |.endif
+    |
+    |5:  // Check for two numbers.
+    |  .FPU ldc1 f20, 0(RB)
+    |  sltiu AT, SFARG1HI, LJ_TISNUM
+    |   sltiu TMP0, SFARG2HI, LJ_TISNUM
+    |  .FPU ldc1 f22, 0(RC)
+    |   and AT, AT, TMP0
+    |   beqz AT, ->vmeta_arith
+    |.   addu RA, BASE, RA
+    |
     |.if FPU
-    |  fpins f0, f20, f22
-    |.else
-    |.if "fpcall" == "sfpmod"
+    |  fpins FRET1, f20, f22
+    |.elif "fpcall" == "sfpmod"
     |  sfpmod
     |.else
     |  load_got fpcall
     |  call_extern
     |.  nop
     |.endif
-    |.endif
+    |
     |  ins_next1
-    |  store_double1 0(RA)
+    |.if not FPU
+    |  sw SFRETHI, HI(RA)
+    |.endif
+    |.if "intins" ~= "div"
+    |  b <3
+    |.endif
+    |.if FPU
+    |.  sdc1 FRET1, 0(RA)
+    |.else
+    |.  sw SFRETLO, LO(RA)
+    |.endif
+    |.if "intins" == "div"
     |  ins_next2
     |.endif
+    |
     |.endmacro
 
   case BC_ADDVN: case BC_ADDNV: case BC_ADDVV:
-    |  ins_arith addu, add.d, __adddf3
+    |  ins_arith addu, add.d, __adddf3, none
     break;
   case BC_SUBVN: case BC_SUBNV: case BC_SUBVV:
-    |  ins_arith subu, sub.d, __subdf3
+    |  ins_arith subu, sub.d, __subdf3, none
     break;
   case BC_MULVN: case BC_MULNV: case BC_MULVV:
-    |  ins_arith mult, mul.d, __muldf3
+    |  ins_arith mult, mul.d, __muldf3, none
+    break;
+  case BC_DIVVN:
+    |  ins_arith div, div.d, __divdf3, ->BC_DIVVN_Z
     break;
-  case BC_DIVVN: case BC_DIVNV: case BC_DIVVV:
-    |  ins_arith div, div.d, __divdf3
+  case BC_DIVNV: case BC_DIVVV:
+    |  ins_arithpre ->BC_DIVVN_Z
     break;
   case BC_MODVN:
-    |  ins_arith modi, fpmod, sfpmod
+    |  ins_arith modi, fpmod, sfpmod, ->BC_MODVN_Z
+    break;
   case BC_MODNV: case BC_MODVV:
-    |  ins_arith modi, fpmod_, sfpmod
+    |  ins_arithpre ->BC_MODVN_Z
     break;
   case BC_POW:
-    |  decode_RB8a RB, INS
-    |  decode_RB8b RB
-    |   decode_RDtoRC8 RC, RD
-    |  addu CARG3, BASE, RB
-    |   addu CARG4, BASE, RC
-    |  lw TMP1, HI(CARG3)
-    |   lw TMP2, HI(CARG4)
-    |  sltiu AT, TMP1, LJ_TISNUM
-    |  sltiu TMP0, TMP2, LJ_TISNUM
+    |  ins_arithpre none
+    |  lw SFARG1HI, HI(RB)
+    |   lw SFARG2HI, HI(RC)
+    |  sltiu AT, SFARG1HI, LJ_TISNUM
+    |  sltiu TMP0, SFARG2HI, LJ_TISNUM
     |  and AT, AT, TMP0
     |  load_got pow
     |  beqz AT, ->vmeta_arith
     |.  addu RA, BASE, RA
-    |  load_farg1 0(CARG3)
-    |   load_farg2 0(CARG4)
+    |.if FPU
+    |  ldc1 FARG1, 0(RB)
+    |  ldc1 FARG2, 0(RC)
+    |.else
+    |  lw SFARG1LO, LO(RB)
+    |   lw SFARG2LO, LO(RC)
+    |.endif
     |  call_extern
     |.  nop
     |  ins_next1
-    |.if HFABI
+    |.if FPU
     |  sdc1 FRET1, 0(RA)
     |.else
-    |  sw CRET1, 0(RA)
-    |  sw CRET2, 4(RA)
+    |  sw SFRETHI, HI(RA)
+    |   sw SFRETLO, LO(RA)
     |.endif
     |  ins_next2
     break;
@@ -3512,10 +3600,12 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  bnez CRET1, ->vmeta_binop
     |.  lw BASE, L->base
     |  addu RB, BASE, MULTRES
-    |  load_double1 0(RB)
+    |  lw SFRETHI, HI(RB)
+    |   lw SFRETLO, LO(RB)
     |   addu RA, BASE, RA
     |  ins_next1
-    |   store_double1 0(RA)
+    |  sw SFRETHI, HI(RA)
+    |   sw SFRETLO, LO(RA)
     |  ins_next2
     break;
 
@@ -3551,23 +3641,20 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  // RA = dst*8, RD = int16_literal*8
     |  sra RD, INS, 16
     |  addu RA, BASE, RA
-    |.if FPU
-    |  mtc1 RD, f0
-    |  cvt.d.w f0, f0
-    |.else
-    |  cvti2d RD
-    |.endif
     |  ins_next1
-    |   store_double f0, CRET1, CRET2, 0(RA)
+    |  sw TISNUM, HI(RA)
+    |   sw RD, LO(RA)
     |  ins_next2
     break;
   case BC_KNUM:
     |  // RA = dst*8, RD = num_const*8
     |  addu RD, KBASE, RD
     |   addu RA, BASE, RA
-    |  load_double1 0(RD)
+    |  lw SFRETHI, HI(RD)
+    |   lw SFRETLO, LO(RD)
     |  ins_next1
-    |   store_double1 0(RA)
+    |  sw SFRETHI, HI(RA)
+    |   sw SFRETLO, LO(RA)
     |  ins_next2
     break;
   case BC_KPRI:
@@ -3603,9 +3690,11 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  lw UPVAL:RB, LFUNC:RD->uvptr
     |  ins_next1
     |  lw TMP1, UPVAL:RB->v
-    |  load_double1 0(TMP1)
+    |  lw SFRETHI, HI(TMP1)
+    |   lw SFRETLO, LO(TMP1)
     |  addu RA, BASE, RA
-    |  store_double1 0(RA)
+    |  sw SFRETHI, HI(RA)
+    |   sw SFRETLO, LO(RA)
     |  ins_next2
     break;
   case BC_USETV:
@@ -3614,26 +3703,27 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |    srl RA, RA, 1
     |   addu RD, BASE, RD
     |    addu RA, RA, LFUNC:RB
-    |   load_double1 0(RD)
     |  lw UPVAL:RB, LFUNC:RA->uvptr
+    |   lw SFRETHI, HI(RD)
+    |    lw SFRETLO, LO(RD)
     |  lbu TMP3, UPVAL:RB->marked
     |   lw CARG2, UPVAL:RB->v
     |  andi TMP3, TMP3, LJ_GC_BLACK    // isblack(uv)
     |  lbu TMP0, UPVAL:RB->closed
-    |   lw TMP2, HI(RD)
-    |   store_double1 0(CARG2)
+    |   sw SFRETHI, HI(CARG2)
+    |    sw SFRETLO, LO(CARG2)
     |  li AT, LJ_GC_BLACK|1
     |  or TMP3, TMP3, TMP0
     |  beq TMP3, AT, >2                        // Upvalue is closed and black?
-    |.  addiu TMP2, TMP2, -(LJ_TNUMX+1)
+    |.  addiu TMP2, SFRETHI, -(LJ_TNUMX+1)
     |1:
     |  ins_next
     |
     |2:  // Check if new value is collectable.
     |  sltiu AT, TMP2, LJ_TISGCV - (LJ_TNUMX+1)
     |  beqz AT, <1                     // tvisgcv(v)
-    |.  lw TMP1, LO(RD)
-    |  lbu TMP3, GCOBJ:TMP1->gch.marked
+    |.  nop
+    |  lbu TMP3, GCOBJ:SFRETLO->gch.marked
     |  andi TMP3, TMP3, LJ_GC_WHITES   // iswhite(v)
     |  beqz TMP3, <1
     |.  load_got lj_gc_barrieruv
@@ -3681,11 +3771,13 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |   srl RA, RA, 1
     |    addu RD, KBASE, RD
     |   addu RA, RA, LFUNC:RB
-    |    load_double1 0(RD)
-    |  lw UPVAL:RB, LFUNC:RA->uvptr
+    |   lw UPVAL:RB, LFUNC:RA->uvptr
+    |    lw SFRETHI, HI(RD)
+    |     lw SFRETLO, LO(RD)
+    |   lw TMP1, UPVAL:RB->v
     |  ins_next1
-    |  lw TMP1, UPVAL:RB->v
-    |  store_double1 0(TMP1)
+    |    sw SFRETHI, HI(TMP1)
+    |     sw SFRETLO, LO(TMP1)
     |  ins_next2
     break;
   case BC_USETP:
@@ -3695,10 +3787,10 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |    srl TMP0, RD, 3
     |   addu RA, RA, LFUNC:RB
     |    not TMP0, TMP0
-    |  lw UPVAL:RB, LFUNC:RA->uvptr
+    |   lw UPVAL:RB, LFUNC:RA->uvptr
     |  ins_next1
-    |  lw TMP1, UPVAL:RB->v
-    |  sw TMP0, HI(TMP1)
+    |   lw TMP1, UPVAL:RB->v
+    |   sw TMP0, HI(TMP1)
     |  ins_next2
     break;
 
@@ -3734,8 +3826,8 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |   li TMP0, LJ_TFUNC
     |  ins_next1
     |  addu RA, BASE, RA
-    |  sw TMP0, HI(RA)
     |  sw LFUNC:CRET1, LO(RA)
+    |   sw TMP0, HI(RA)
     |  ins_next2
     break;
 
@@ -3818,71 +3910,21 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  li AT, LJ_TTAB
     |  bne TMP1, AT, ->vmeta_tgetv
     |.  addu RA, BASE, RA
-    |  sltiu AT, TMP2, LJ_TISNUM
-    |  beqz AT, >5
-    |.  li AT, LJ_TSTR
-    |.if FPU
-    |   ldc1 f0, 0(CARG3)
-    |  // Convert number key to integer, check for integerness and range.
-    |  cvt.w.d f2, f0
-    |   lw TMP0, TAB:RB->asize
-    |  mfc1 TMP2, f2
-    |  cvt.d.w f4, f2
-    |   lw TMP1, TAB:RB->array
-    |  c.eq.d f0, f4
-    |  sltu AT, TMP2, TMP0
-    |  movf AT, r0
-    |   sll TMP2, TMP2, 3
-    |  beqz AT, ->vmeta_tgetv          // Integer key and in array part?
-    |.  addu TMP2, TMP1, TMP2
-    |  lw TMP0, HI(TMP2)
-    |  beq TMP0, TISNIL, >2
-    |.  ldc1 f0, 0(TMP2)
-    |.else
-    |   sw RB, TEMP_SAVE_1
-    |   sw CARG2, TEMP_SAVE_3
-    |  load_got __fixdfsi
-    |   lw CARG1, 0(CARG3)
-    |   lw CARG2, 4(CARG3)
-    |  call_extern                     // cvt.w.d f2, f0
-    |.  sw RC, TEMP_SAVE_2
-    |  sw CRET1, TEMP_SAVE_4
-    |  cvti2d CRET1                    // cvt.d.w f4, f2
-    |  load_got __ledf2
-    |  lw RC, TEMP_SAVE_2
-    |   addu CARG3, BASE, RC
-    |  lw CARG1, 0(CARG3)
-    |  lw CARG2, 4(CARG3)
-    |   move CARG3, CRET1
-    |   move CARG4, CRET2
-    |  call_extern                     // c.eq.d f0, f4
-    |.  nop
-    |  lw CARG3, TEMP_SAVE_3
-    |  lw RC, TEMP_SAVE_2
-    |  lw RB, TEMP_SAVE_1
-    |   lw TMP0, TAB:RB->asize
+    |  bne TMP2, TISNUM, >5
+    |.  lw RC, LO(CARG3)
+    |  lw TMP0, TAB:RB->asize
     |   lw TMP1, TAB:RB->array
-    |  lw TMP2, TEMP_SAVE_4
-    |  lw CARG2, TEMP_SAVE_3           // Restore old CARG2 and CARG3.
-    |   addu CARG3, BASE, RC
-    |  bnez CRET1, >3
-    |.  sltu AT, TMP2, TMP0
-    |  b >4
-    |.  nop
-    |3:
-    |  move AT, r0
-    |4:
-    |   sll TMP2, TMP2, 3
+    |  sltu AT, RC, TMP0
+    |   sll TMP2, RC, 3
     |  beqz AT, ->vmeta_tgetv          // Integer key and in array part?
     |.  addu TMP2, TMP1, TMP2
-    |  lw TMP0, HI(TMP2)
-    |  lw SFT2, 4(TMP2)
-    |  beq TMP0, TISNIL, >2
-    |.  lw SFT1, 0(TMP2)
-    |.endif
+    |  lw SFRETHI, HI(TMP2)
+    |  beq SFRETHI, TISNIL, >2
+    |.  lw SFRETLO, LO(TMP2)
     |1:
     |  ins_next1
-    |   store_double1 0(RA)
+    |  sw SFRETHI, HI(RA)
+    |   sw SFRETLO, LO(RA)
     |  ins_next2
     |
     |2:  // Check for __index if table value is nil.
@@ -3897,8 +3939,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |.  nop
     |
     |5:
+    |  li AT, LJ_TSTR
     |  bne TMP2, AT, ->vmeta_tgetv
-    |.  lw STR:RC, LO(CARG3)
+    |.  nop
     |  b ->BC_TGETS_Z                  // String key?
     |.  nop
     break;
@@ -3930,18 +3973,18 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  lw CARG1, offsetof(Node, key)+HI(NODE:TMP2)
     |   lw TMP0, offsetof(Node, key)+LO(NODE:TMP2)
     |    lw NODE:TMP1, NODE:TMP2->next
-    |    lw CARG2, offsetof(Node, val)+HI(NODE:TMP2)
+    |    lw SFRETHI, offsetof(Node, val)+HI(NODE:TMP2)
     |  addiu CARG1, CARG1, -LJ_TSTR
     |   xor TMP0, TMP0, STR:RC
     |  or AT, CARG1, TMP0
     |  bnez AT, >4
     |.  lw TAB:TMP3, TAB:RB->metatable
-    |    beq CARG2, TISNIL, >5         // Key found, but nil value?
-    |.    lw CARG1, offsetof(Node, val)+LO(NODE:TMP2)
+    |    beq SFRETHI, TISNIL, >5       // Key found, but nil value?
+    |.    lw SFRETLO, offsetof(Node, val)+LO(NODE:TMP2)
     |3:
     |  ins_next1
-    |    sw CARG2, HI(RA)
-    |     sw CARG1, LO(RA)
+    |    sw SFRETHI, HI(RA)
+    |     sw SFRETLO, LO(RA)
     |  ins_next2
     |
     |4:  // Follow hash chain.
@@ -3951,7 +3994,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |
     |5:  // Check for __index if table value is nil.
     |  beqz TAB:TMP3, <3               // No metatable: done.
-    |.  li CARG2, LJ_TNIL
+    |.  li SFRETHI, LJ_TNIL
     |  lbu TMP0, TAB:TMP3->nomm
     |  andi TMP0, TMP0, 1<<MM_index
     |  bnez TMP0, <3                   // 'no __index' flag set: done.
@@ -3976,13 +4019,13 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  sltu AT, TMP0, TMP1
     |  beqz AT, ->vmeta_tgetb
     |.  addu RC, TMP2, RC
-    |  lw TMP1, HI(RC)
-    |  beq TMP1, TISNIL, >5
-    |.  nop
+    |  lw SFRETHI, HI(RC)
+    |  beq SFRETHI, TISNIL, >5
+    |.  lw SFRETLO, LO(RC)
     |1:
-    |  load_double1 0(RC)
     |  ins_next1
-    |   store_double1 0(RA)
+    |  sw SFRETHI, HI(RA)
+    |   sw SFRETLO, LO(RA)
     |  ins_next2
     |
     |5:  // Check for __index if table value is nil.
@@ -3993,7 +4036,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  andi TMP1, TMP1, 1<<MM_index
     |  bnez TMP1, <1                   // 'no __index' flag set: done.
     |.  nop
-    |  b ->vmeta_tgetb                 // Caveat: preserve TMP0!
+    |  b ->vmeta_tgetb                 // Caveat: preserve TMP0 and CARG2!
     |.  nop
     break;
   case BC_TGETR:
@@ -4001,31 +4044,23 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  decode_RB8a RB, INS
     |  decode_RB8b RB
     |   decode_RDtoRC8 RC, RD
-    |  addu CARG2, BASE, RB
-    |   addu CARG3, BASE, RC
-    |    lw TAB:CARG1, LO(CARG2)
-    |   lw TMP0, TAB:CARG1->asize
+    |  addu RB, BASE, RB
+    |   addu RC, BASE, RC
+    |  lw TAB:CARG1, LO(RB)
+    |   lw CARG2, LO(RC)
+    |    addu RA, BASE, RA
+    |  lw TMP0, TAB:CARG1->asize
     |   lw TMP1, TAB:CARG1->array
-    |.if FPU
-    |   ldc1 f0, 0(CARG3)
-    |  trunc.w.d f2, f0
-    |  mfc1 CARG2, f2
-    |.else
-    |  load_got __fixdfsi
-    |  lw CARG1, 0(CARG3)
-    |  call_extern
-    |.  lw CARG2, 4(CARG3)
-    |  move CARG2, CRET1
-    |.endif
     |  sltu AT, CARG2, TMP0
     |   sll TMP2, CARG2, 3
     |  beqz AT, ->vmeta_tgetr          // In array part?
-    |.  addu TMP2, TMP1, TMP2
-    |   load_double1 0(TMP2)
+    |.  addu CRET1, TMP1, TMP2
+    |  lw SFARG2HI, HI(CRET1)
+    |   lw SFARG2LO, LO(CRET1)
     |->BC_TGETR_Z:
-    |   addu RA, BASE, RA
     |  ins_next1
-    |   store_double1 0(RA)
+    |  sw SFARG2HI, HI(RA)
+    |   sw SFARG2LO, LO(RA)
     |  ins_next2
     break;
 
@@ -4042,77 +4077,24 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  li AT, LJ_TTAB
     |  bne TMP1, AT, ->vmeta_tsetv
     |.  addu RA, BASE, RA
-    |  sltiu AT, TMP2, LJ_TISNUM
-    |  beqz AT, >5
-    |.  li AT, LJ_TSTR
-    |.if FPU
-    |   ldc1 f0, 0(CARG3)
-    |  // Convert number key to integer, check for integerness and range.
-    |  cvt.w.d f2, f0
-    |   lw TMP0, TAB:RB->asize
-    |  mfc1 TMP2, f2
-    |  cvt.d.w f4, f2
+    |  bne TMP2, TISNUM, >5
+    |.  lw RC, LO(CARG3)
+    |  lw TMP0, TAB:RB->asize
     |   lw TMP1, TAB:RB->array
-    |  c.eq.d f0, f4
-    |  sltu AT, TMP2, TMP0
-    |  movf AT, r0
-    |   sll TMP2, TMP2, 3
+    |  sltu AT, RC, TMP0
+    |   sll TMP2, RC, 3
     |  beqz AT, ->vmeta_tsetv          // Integer key and in array part?
     |.  addu TMP1, TMP1, TMP2
-    |   lbu TMP3, TAB:RB->marked
     |  lw TMP0, HI(TMP1)
-    |  beq TMP0, TISNIL, >3
-    |.  ldc1 f0, 0(RA)
-    |1:
-    |   andi AT, TMP3, LJ_GC_BLACK     // isblack(table)
-    |   bnez AT, >7
-    |.  sdc1 f0, 0(TMP1)
-    |.else
-    |  sw RB, TEMP_SAVE_1
-    |  sw RC, TEMP_SAVE_2
-    |  sw CARG2, TEMP_SAVE_3
-    |  load_got __fixdfsi
-    |  lw CARG1, 0(CARG3)
-    |  call_extern                     // cvt.w.d f2, f0
-    |.  lw CARG2, 4(CARG3)
-    |  sw CRET1, TEMP_SAVE_4
-    |  cvti2d CRET1                    // cvt.d.w f4, f2
-    |  load_got __ledf2
-    |  lw RC, TEMP_SAVE_2
-    |   addu CARG3, BASE, RC
-    |  lw CARG1, 0(CARG3)
-    |  lw CARG2, 4(CARG3)
-    |  move CARG3, CRET1
-    |  call_extern                     // c.eq.d f0, f4
-    |.  move CARG4, CRET2
-    |  lw RC, TEMP_SAVE_2
-    |  lw RB, TEMP_SAVE_1
-    |   lw TMP0, TAB:RB->asize
-    |   lw TMP1, TAB:RB->array
-    |  lw TMP2, TEMP_SAVE_4
-    |  lw CARG2, TEMP_SAVE_3           // Restore old CARG2 and CARG3.
-    |   addu CARG3, BASE, RC
-    |  bnez CRET1, >4                  // NaN?
-    |.  sltu AT, TMP2, TMP0
-    |  b >6
-    |.  nop
-    |4:
-    |  move AT, r0
-    |6:
-    |   sll TMP2, TMP2, 3
-    |  beqz AT, ->vmeta_tsetv          // Integer key and in array part?
-    |.  addu TMP1, TMP1, TMP2
     |   lbu TMP3, TAB:RB->marked
-    |  lw TMP0, HI(TMP1)
-    |   lw SFT1, 0(RA)
+    |  lw SFRETHI, HI(RA)
     |  beq TMP0, TISNIL, >3
-    |.  lw SFT2, 4(RA)
+    |.  lw SFRETLO, LO(RA)
     |1:
-    |   andi AT, TMP3, LJ_GC_BLACK     // isblack(table)
-    |   sw SFT1, 0(TMP1)
-    |   bnez AT, >7
-    |.  sw SFT2, 4(TMP1)
-    |.endif
+    |   andi AT, TMP3, LJ_GC_BLACK  // isblack(table)
+    |  sw SFRETHI, HI(TMP1)
+    |  bnez AT, >7
+    |.  sw SFRETLO, LO(TMP1)
     |2:
     |  ins_next
     |
@@ -4128,8 +4110,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |.  nop
     |
     |5:
+    |  li AT, LJ_TSTR
     |  bne TMP2, AT, ->vmeta_tsetv
-    |.  lw STR:RC, LO(CARG3)
+    |.  nop
     |  b ->BC_TSETS_Z                  // String key?
     |.  nop
     |
@@ -4161,7 +4144,12 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  sll TMP1, TMP1, 3
     |  subu TMP1, TMP0, TMP1
     |  addu NODE:TMP2, NODE:TMP2, TMP1 // node = tab->node + (idx*32-idx*8)
-    |   load_double f20, SFT1, SFT2, 0(RA)
+    |.if FPU
+    |   ldc1 f20, 0(RA)
+    |.else
+    |   lw SFRETHI, HI(RA)
+    |    lw SFRETLO, LO(RA)
+    |.endif
     |1:
     |  lw CARG1, offsetof(Node, key)+HI(NODE:TMP2)
     |   lw TMP0, offsetof(Node, key)+LO(NODE:TMP2)
@@ -4179,9 +4167,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  bnez AT, >7
     |.  sdc1 f20, NODE:TMP2->val
     |.else
-    |  sw SFT1, NODE:TMP2->val.u32.hi
+    |   sw SFRETHI, NODE:TMP2->val.u32.hi
     |  bnez AT, >7
-    |.  sw SFT2, NODE:TMP2->val.u32.lo
+    |.   sw SFRETLO, NODE:TMP2->val.u32.lo
     |.endif
     |3:
     |  ins_next
@@ -4210,10 +4198,6 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  beqz TMP0, ->vmeta_tsets                // 'no __newindex' flag NOT set: check.
     |.  li AT, LJ_TSTR
     |6:
-    |.if not FPU
-    |  sw SFT1, TEMP_SAVE_1
-    |  sw SFT2, TEMP_SAVE_2
-    |.endif
     |  load_got lj_tab_newkey
     |  sw STR:RC, LO(CARG3)
     |  sw AT, HI(CARG3)
@@ -4228,11 +4212,11 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  b <3                            // No 2nd write barrier needed.
     |.  sdc1 f20, 0(CRET1)
     |.else
-    |  lw SFT2, TEMP_SAVE_1
-    |  lw SFT3, TEMP_SAVE_2
-    |  sw SFT2, 0(CRET1)
-    |  b <3
-    |.  sw SFT3, 4(CRET1)
+    |  lw SFARG1HI, HI(RA)
+    |   lw SFARG1LO, LO(RA)
+    |  sw SFARG1HI, HI(CRET1)
+    |  b <3                            // No 2nd write barrier needed.
+    |.  sw SFARG1LO, LO(CRET1)
     |.endif
     |
     |7:  // Possible table write barrier for the value. Skip valiswhite check.
@@ -4259,16 +4243,12 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |   lbu TMP3, TAB:RB->marked
     |  beq TMP1, TISNIL, >5
     |1:
-    |.  andi AT, TMP3, LJ_GC_BLACK     // isblack(table)
-    |  load_double1 0(RA)
-    |.if FPU
-    |  bnez AT, >7
-    |.  sdc1 f0, 0(RC)
-    |.else
-    |  sw SFT1, 0(RC)
+    |.  lw SFRETHI, HI(RA)
+    |    lw SFRETLO, LO(RA)
+    |  andi AT, TMP3, LJ_GC_BLACK      // isblack(table)
+    |   sw SFRETHI, HI(RC)
     |  bnez AT, >7
-    |.  sw SFT2, 4(RC)
-    |.endif
+    |.   sw SFRETLO, LO(RC)
     |2:
     |  ins_next
     |
@@ -4280,7 +4260,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  andi TMP1, TMP1, 1<<MM_newindex
     |  bnez TMP1, <1                   // 'no __newindex' flag set: done.
     |.  nop
-    |  b ->vmeta_tsetb                 // Caveat: preserve TMP0!
+    |  b ->vmeta_tsetb                 // Caveat: preserve TMP0 and CARG2!
     |.  nop
     |
     |7:  // Possible table write barrier for the value. Skip valiswhite check.
@@ -4293,54 +4273,31 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |   decode_RDtoRC8 RC, RD
     |  addu CARG1, BASE, RB
     |   addu CARG3, BASE, RC
-    |.if FPU
-    |   ldc1 f0, 0(CARG3)
-    |  trunc.w.d f2, f0
-    |  mfc1 CARG3, f2
-    |.else
-    |  load_got __fixdfsi
-    |  sw CARG1, TEMP_SAVE_1
-    |   lw CARG1, 0(CARG3)
-    |  call_extern
-    |.   lw CARG2, 4(CARG3)
-    |  lw CARG1, TEMP_SAVE_1
-    |  move CARG3, CRET1
-    |.endif
-    |    lw TAB:CARG2, LO(CARG1)
-    |    lbu TMP3, TAB:CARG2->marked
+    |  lw TAB:CARG2, LO(CARG1)
+    |   lw CARG3, LO(CARG3)
+    |  lbu TMP3, TAB:CARG2->marked
     |   lw TMP0, TAB:CARG2->asize
-    |   lw TMP1, TAB:CARG2->array
+    |    lw TMP1, TAB:CARG2->array
     |  andi AT, TMP3, LJ_GC_BLACK      // isblack(table)
     |  bnez AT, >7
     |.  addu RA, BASE, RA
     |2:
     |  sltu AT, CARG3, TMP0
     |   sll TMP2, CARG3, 3
-    |.if FPU
-    |  beqz AT, ->vmeta_tsetr          // In array part?
-    |.  ldc1 f20, 0(RA)
-    |   addu CRET1, TMP1, TMP2
-    |->BC_TSETR_Z:
-    |.else
-    |   lw TMP0, 0(RA)
-    |   lw TMP3, 4(RA)
-    |   sw TMP0, TEMP_SAVE_1
     |  beqz AT, ->vmeta_tsetr          // In array part?
-    |.   sw TMP3, TEMP_SAVE_2
-    |  addu CRET1, TMP1, TMP2
+    |.  addu CRET1, TMP1, TMP2
     |->BC_TSETR_Z:
-    |   lw TMP0, TEMP_SAVE_1
-    |   lw TMP3, TEMP_SAVE_2
-    |.endif
+    |  lw SFARG1HI, HI(RA)
+    |   lw SFARG1LO, LO(RA)
     |  ins_next1
-    |   store_double f20, TMP0, TMP3, 0(CRET1)
+    |  sw SFARG1HI, HI(CRET1)
+    |   sw SFARG1LO, LO(CRET1)
     |  ins_next2
     |
     |7:  // Possible table write barrier for the value. Skip valiswhite check.
     |  barrierback TAB:RB, TMP3, TMP0, <2
     break;
 
-
   case BC_TSETM:
     |  // RA = base*8 (table at base-1), RD = num_const*8 (start index)
     |  addu RA, BASE, RA
@@ -4362,10 +4319,12 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |   addu TMP1, TMP1, CARG1
     |  andi TMP0, TMP3, LJ_GC_BLACK    // isblack(table)
     |3:  // Copy result slots to table.
-    |   load_double1 0(RA)
+    |   lw SFRETHI, HI(RA)
+    |    lw SFRETLO, LO(RA)
     |    addiu RA, RA, 8
     |  sltu AT, RA, TMP2
-    |   store_double1 0(TMP1)
+    |   sw SFRETHI, HI(TMP1)
+    |    sw SFRETLO, LO(TMP1)
     |  bnez AT, <3
     |.   addiu TMP1, TMP1, 8
     |  bnez TMP0, >7
@@ -4440,10 +4399,12 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  beqz NARGS8:RC, >3
     |.  move TMP3, NARGS8:RC
     |2:
-    |   load_double1 0(RA)
+    |   lw SFRETHI, HI(RA)
+    |    lw SFRETLO, LO(RA)
     |    addiu RA, RA, 8
     |  addiu TMP3, TMP3, -8
-    |   store_double1 0(TMP2)
+    |   sw SFRETHI, HI(TMP2)
+    |    sw SFRETLO, LO(TMP2)
     |  bnez TMP3, <2
     |.   addiu TMP2, TMP2, 8
     |3:
@@ -4480,12 +4441,16 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |   li AT, LJ_TFUNC
     |  lw TMP1, -24+HI(BASE)
     |   lw LFUNC:RB, -24+LO(BASE)
-    |    load_double1 -8(BASE)
-    |    load_double2 -16(BASE)
+    |    lw SFARG1HI, -16+HI(BASE)
+    |     lw SFARG1LO, -16+LO(BASE)
+    |    lw SFARG2HI, -8+HI(BASE)
+    |     lw SFARG2LO, -8+LO(BASE)
     |  sw TMP1, HI(BASE)               // Copy callable.
     |   sw LFUNC:RB, LO(BASE)
-    |    store_double1 16(BASE)                // Copy control var.
-    |    store_double2 8(BASE)         // Copy state.
+    |    sw SFARG1HI, 8+HI(BASE)       // Copy state.
+    |     sw SFARG1LO, 8+LO(BASE)
+    |    sw SFARG2HI, 16+HI(BASE)      // Copy control var.
+    |     sw SFARG2LO, 16+LO(BASE)
     |   addiu BASE, BASE, 8
     |  bne TMP1, AT, ->vmeta_call
     |.  li NARGS8:RC, 16               // Iterators get 2 arguments.
@@ -4508,26 +4473,16 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  beqz AT, >5                     // Index points after array part?
     |.  sll TMP3, RC, 3
     |  addu TMP3, TMP1, TMP3
-    |  lw TMP2, HI(TMP3)
-    |   load_double1 0(TMP3)
-    |.if FPU
-    |    mtc1 RC, f2
-    |.else
-    |    move CARG1, RC
-    |.endif
+    |  lw SFARG1HI, HI(TMP3)
+    |   lw SFARG1LO, LO(TMP3)
     |     lhu RD, -4+OFS_RD(PC)
-    |  beq TMP2, TISNIL, <1            // Skip holes in array part.
+    |  sw TISNUM, HI(RA)
+    |   sw RC, LO(RA)
+    |  beq SFARG1HI, TISNIL, <1                // Skip holes in array part.
     |.  addiu RC, RC, 1
-    |   store_double1 8(RA)
-    |.if FPU
-    |    cvt.d.w f2, f2
-    |.else
-    |   load_got __floatsidf
-    |   call_extern
-    |.   nop
-    |.endif
+    |  sw SFARG1HI, 8+HI(RA)
+    |   sw SFARG1LO, 8+LO(RA)
     |     lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535)
-    |   store_double f2, CRET1, CRET2, 0(RA)
     |     decode_RD4b RD
     |     addu RD, RD, TMP3
     |   sw RC, -8+LO(RA)               // Update control var.
@@ -4546,23 +4501,21 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |   sll RB, RC, 3
     |   subu TMP3, TMP3, RB
     |  addu NODE:TMP3, TMP3, TMP2
-    |  lw RB, HI(NODE:TMP3)
-    |  load_double1 0(NODE:TMP3)
+    |  lw SFARG1HI, NODE:TMP3->val.u32.hi
+    |   lw SFARG1LO, NODE:TMP3->val.u32.lo
     |     lhu RD, -4+OFS_RD(PC)
-    |  beq RB, TISNIL, <6              // Skip holes in hash part.
+    |  beq SFARG1HI, TISNIL, <6                // Skip holes in hash part.
     |.  addiu RC, RC, 1
-    |.if FPU
-    |   ldc1 f2, NODE:TMP3->key
-    |.else
-    |   lw SFT3, NODE:TMP3->key.u32.hi
-    |   lw SFT4, NODE:TMP3->key.u32.lo
-    |.endif
+    |  lw SFARG2HI, NODE:TMP3->key.u32.hi
+    |   lw SFARG2LO, NODE:TMP3->key.u32.lo
     |     lui TMP3, (-(BCBIAS_J*4 >> 16) & 65535)
-    |  store_double1 8(RA)
+    |  sw SFARG1HI, 8+HI(RA)
+    |   sw SFARG1LO, 8+LO(RA)
     |    addu RC, RC, TMP0
     |     decode_RD4b RD
     |     addu RD, RD, TMP3
-    |   store_double2 0(RA)
+    |  sw SFARG2HI, HI(RA)
+    |   sw SFARG2LO, LO(RA)
     |     addu PC, PC, RD
     |  b <3
     |.  sw RC, -8+LO(RA)               // Update control var.
@@ -4642,9 +4595,11 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  bnez AT, >7
     |.  addiu MULTRES, TMP1, 8
     |6:
-    |  load_double1 0(RC)
+    |  lw SFRETHI, HI(RC)
+    |   lw SFRETLO, LO(RC)
     |   addiu RC, RC, 8
-    |  store_double1 0(RA)
+    |  sw SFRETHI, HI(RA)
+    |   sw SFRETLO, LO(RA)
     |  sltu AT, RC, TMP3
     |  bnez AT, <6                     // More vararg slots?
     |.  addiu RA, RA, 8
@@ -4700,10 +4655,12 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  beqz RC, >3
     |.  subu BASE, TMP2, TMP0
     |2:
-    |   load_double1 0(RA)
+    |   lw SFRETHI, HI(RA)
+    |    lw SFRETLO, LO(RA)
     |    addiu RA, RA, 8
     |  addiu RC, RC, -8
-    |   store_double1 0(TMP2)
+    |   sw SFRETHI, HI(TMP2)
+    |    sw SFRETLO, LO(TMP2)
     |  bnez RC, <2
     |.   addiu TMP2, TMP2, 8
     |3:
@@ -4744,14 +4701,16 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  lw INS, -4(PC)
     |   addiu TMP2, BASE, -8
     if (op == BC_RET1) {
-      |  load_double1 0(RA)
+      |  lw SFRETHI, HI(RA)
+      |   lw SFRETLO, LO(RA)
     }
     |  decode_RB8a RB, INS
     |   decode_RA8a RA, INS
     |  decode_RB8b RB
     |   decode_RA8b RA
     if (op == BC_RET1) {
-      |  store_double1 0(TMP2)
+      |  sw SFRETHI, HI(TMP2)
+      |   sw SFRETLO, LO(TMP2)
     }
     |   subu BASE, TMP2, RA
     |5:
@@ -4776,45 +4735,6 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
 
   /* -- Loops and branches ------------------------------------------------ */
 
-    |.macro cmp_res, gt
-    |.if gt == 1
-    |.if FPU
-    |  movf TMP1, r0, 0                // f0>f2: TMP1=0
-    |  movf TMP2, r0, 1                // f2>f0: TMP2=0
-    |.else
-    |  li SFT2, 1
-    |  bne CRET1, SFT2, >1
-    |.  nop
-    |  b >2
-    |.  move TMP1, r0
-    |1:
-    |  li SFT2, -1
-    |  bne CRET1, SFT2, >2
-    |.  nop
-    |  move TMP2, r0
-    |2:
-    |.endif
-    |.else
-    |.if FPU
-    |  movt TMP1, r0, 0                // f0<=f2: TMP1=0
-    |  movt TMP2, r0, 1                // f2<=f0: TMP2=0
-    |.else
-    |  bltz CRET1, >3          // f0<f2: TMP1=0
-    |.  nop
-    |  beqz CRET1, >2          // f0==f2: TMP1=TMP2=0
-    |.  li SFT2, 1
-    |  bne SFT2, CRET1, >4     // f0>f2: TMP2=0
-    |.  nop
-    |  b >4
-    |2:
-    |.  move TMP2, r0
-    |3:
-    |  move TMP1, r0
-    |4:
-    |.endif
-    |.endif
-    |.endmacro
-
   case BC_FORL:
     |.if JIT
     |  hotloop
@@ -4832,96 +4752,140 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
     |  // RA = base*8, RD = target (after end of loop or start of loop)
     vk = (op == BC_IFORL || op == BC_JFORL);
     |  addu RA, BASE, RA
-    if (vk) {
-      |.if FPU
-      |  ldc1 f0, FORL_IDX*8(RA)
-      |  ldc1 f4, FORL_STEP*8(RA)
-      |  ldc1 f2, FORL_STOP*8(RA)
-      |   lw TMP3, FORL_STEP*8+HI(RA)
-      |  add.d f0, f0, f4
-      |  sdc1 f0, FORL_IDX*8(RA)
-      |.else
-      |  load_got __adddf3
-      |  load_farg1 FORL_IDX*8(RA)
-      |  load_farg2 FORL_STEP*8(RA)
-      |  call_extern
-      |.  sw RD, TEMP_SAVE_1  //save RD
-      |  sw CRET1, FORL_IDX*8(RA)
-      |  sw CRET2, FORL_IDX*8+4(RA)
-      |  load_farg1 FORL_IDX*8(RA)
-      |  load_farg2 FORL_STOP*8(RA)            // f0 and f2
-      |  lw TMP3, FORL_STEP*8+HI(RA)
-      |  lw RD, TEMP_SAVE_1
-      |.endif
-    } else {
-      |  lw TMP1, FORL_IDX*8+HI(RA)
-      |  lw TMP3, FORL_STEP*8+HI(RA)
-      |  lw TMP2, FORL_STOP*8+HI(RA)
-      |  sltiu TMP1, TMP1, LJ_TISNUM
-      |  sltiu TMP0, TMP3, LJ_TISNUM
-      |  sltiu TMP2, TMP2, LJ_TISNUM
-      |  and TMP1, TMP1, TMP0
-      |  and TMP1, TMP1, TMP2
-      |.if FPU
-      |   ldc1 f0, FORL_IDX*8(RA)
-      |  beqz TMP1, ->vmeta_for
-      |.  ldc1 f2, FORL_STOP*8(RA)
-      |.else
-      |  beqz TMP1, ->vmeta_for
-      |  load_farg1 FORL_IDX*8(RA)
-      |  load_farg2 FORL_STOP*8(RA)
-      |.endif
-    }
+    |  lw SFARG1HI, FORL_IDX*8+HI(RA)
+    |   lw SFARG1LO, FORL_IDX*8+LO(RA)
     if (op != BC_JFORL) {
       |  srl RD, RD, 1
-      |  lui TMP0, (-(BCBIAS_J*4 >> 16) & 65535)
+      |  lui TMP2, (-(BCBIAS_J*4 >> 16) & 65535)
+      |  addu TMP2, RD, TMP2
     }
-    |  store_double f0, CARG1, CARG2, FORL_EXT*8(RA)
-    |.if FPU
-    |  c.le.d 0, f0, f2
-    |  c.le.d 1, f2, f0
-    |.else
-    |  sw RD, TEMP_SAVE_1
-    |  load_got __ledf2                                // f0<=f2
-    |  call_extern
-    |.  sw TMP0, TEMP_SAVE_2
-    |  lw TMP0, TEMP_SAVE_2
-    |  lw RD, TEMP_SAVE_1
-    |  lw TMP3, FORL_STEP*8+HI(RA)             // Restored step.
-    |.endif
-    |
+    if (!vk) {
+      |  lw SFARG2HI, FORL_STOP*8+HI(RA)
+      |   lw SFARG2LO, FORL_STOP*8+LO(RA)
+      |  bne SFARG1HI, TISNUM, >5
+      |.  lw SFRETHI, FORL_STEP*8+HI(RA)
+      |  xor AT, SFARG2HI, TISNUM
+      |   lw SFRETLO, FORL_STEP*8+LO(RA)
+      |  xor TMP0, SFRETHI, TISNUM
+      |  or AT, AT, TMP0
+      |  bnez AT, ->vmeta_for
+      |.  slt AT, SFRETLO, r0
+      |  slt CRET1, SFARG2LO, SFARG1LO
+      |  slt TMP1, SFARG1LO, SFARG2LO
+      |  movn CRET1, TMP1, AT
+    } else {
+      |  bne SFARG1HI, TISNUM, >5
+      |.  lw SFARG2LO, FORL_STEP*8+LO(RA)
+      |  lw SFRETLO, FORL_STOP*8+LO(RA)
+      |  move TMP3, SFARG1LO
+      |  addu SFARG1LO, SFARG1LO, SFARG2LO
+      |  xor TMP0, SFARG1LO, TMP3
+      |  xor TMP1, SFARG1LO, SFARG2LO
+      |  and TMP0, TMP0, TMP1
+      |  slt TMP1, SFARG1LO, SFRETLO
+      |  slt CRET1, SFRETLO, SFARG1LO
+      |  slt AT, SFARG2LO, r0
+      |   slt TMP0, TMP0, r0           // ((y^a) & (y^b)) < 0: overflow.
+      |  movn CRET1, TMP1, AT
+      |   or CRET1, CRET1, TMP0
+    }
+    |1:
+    if (op == BC_FORI) {
+      |  movz TMP2, r0, CRET1
+      |  addu PC, PC, TMP2
+    } else if (op == BC_JFORI) {
+      |  addu PC, PC, TMP2
+      |  lhu RD, -4+OFS_RD(PC)
+    } else if (op == BC_IFORL) {
+      |  movn TMP2, r0, CRET1
+      |  addu PC, PC, TMP2
+    }
+    if (vk) {
+      |  sw SFARG1HI, FORL_IDX*8+HI(RA)
+      |   sw SFARG1LO, FORL_IDX*8+LO(RA)
+    }
+    |  ins_next1
+    |  sw SFARG1HI, FORL_EXT*8+HI(RA)
+    |   sw SFARG1LO, FORL_EXT*8+LO(RA)
+    |2:
     if (op == BC_JFORI) {
-      |  li TMP1, 1
-      |  li TMP2, 1
-      |   addu TMP0, RD, TMP0
-      |  slt TMP3, TMP3, r0
-      |  cmp_res 1
-      |   addu PC, PC, TMP0
-      |   lhu RD, -4+OFS_RD(PC)
-      |  movn TMP1, TMP2, TMP3
-      |  bnez TMP1, =>BC_JLOOP
+      |  beqz CRET1, =>BC_JLOOP
       |.  decode_RD8b RD
     } else if (op == BC_JFORL) {
-      |  li TMP1, 1
-      |  li TMP2, 1
-      |  slt TMP3, TMP3, r0
-      |  cmp_res 1
-      |  movn TMP1, TMP2, TMP3
-      |  bnez TMP1, =>BC_JLOOP
-      |.  nop
+      |  beqz CRET1, =>BC_JLOOP
+    }
+    |  ins_next2
+    |
+    |5:  // FP loop.
+    |.if FPU
+    if (!vk) {
+      |  ldc1 f0, FORL_IDX*8(RA)
+      |   ldc1 f2, FORL_STOP*8(RA)
+      |  sltiu TMP0, SFARG1HI, LJ_TISNUM
+      |  sltiu TMP1, SFARG2HI, LJ_TISNUM
+      |  sltiu AT, SFRETHI, LJ_TISNUM
+      |  and TMP0, TMP0, TMP1
+      |  and AT, AT, TMP0
+      |  beqz AT, ->vmeta_for
+      |.  slt TMP3, SFRETHI, r0
+      |  c.ole.d 0, f0, f2
+      |  c.ole.d 1, f2, f0
+      |  li CRET1, 1
+      |  movt CRET1, r0, 0
+      |  movt AT, r0, 1
+      |  b <1
+      |.  movn CRET1, AT, TMP3
     } else {
-      |  addu TMP1, RD, TMP0
-      |  slt TMP3, TMP3, r0
-      |  move TMP2, TMP1
-      if (op == BC_FORI) {
-       | cmp_res 0
-      } else {
-       | cmp_res 1
+      |  ldc1 f0, FORL_IDX*8(RA)
+      |   ldc1 f4, FORL_STEP*8(RA)
+      |    ldc1 f2, FORL_STOP*8(RA)
+      |   lw SFARG2HI, FORL_STEP*8+HI(RA)
+      |  add.d f0, f0, f4
+      |  c.ole.d 0, f0, f2
+      |  c.ole.d 1, f2, f0
+      |   slt TMP3, SFARG2HI, r0
+      |  li CRET1, 1
+      |  li AT, 1
+      |  movt CRET1, r0, 0
+      |  movt AT, r0, 1
+      |  movn CRET1, AT, TMP3
+      if (op == BC_IFORL) {
+       |  movn TMP2, r0, CRET1
+       |  addu PC, PC, TMP2
       }
-      |  movn TMP1, TMP2, TMP3
-      |  addu PC, PC, TMP1
+      |  sdc1 f0, FORL_IDX*8(RA)
+      |  ins_next1
+      |  b <2
+      |.  sdc1 f0, FORL_EXT*8(RA)
     }
-    |  ins_next
+    |.else
+    if (!vk) {
+      |  sltiu TMP0, SFARG1HI, LJ_TISNUM
+      |  sltiu TMP1, SFARG2HI, LJ_TISNUM
+      |  sltiu AT, SFRETHI, LJ_TISNUM
+      |  and TMP0, TMP0, TMP1
+      |  and AT, AT, TMP0
+      |  beqz AT, ->vmeta_for
+      |.  nop
+      |  bal ->vm_sfcmpolex
+      |.  move TMP3, SFRETHI
+      |  b <1
+      |.  nop
+    } else {
+      |   lw SFARG2HI, FORL_STEP*8+HI(RA)
+      |  load_got __adddf3
+      |  call_extern
+      |.  sw TMP2, ARG5
+      |  lw SFARG2HI, FORL_STOP*8+HI(RA)
+      |   lw SFARG2LO, FORL_STOP*8+LO(RA)
+      |  move SFARG1HI, SFRETHI
+      |   move SFARG1LO, SFRETLO
+      |  bal ->vm_sfcmpolex
+      |.  lw TMP3, FORL_STEP*8+HI(RA)
+      |  b <1
+      |.  lw TMP2, ARG5
+    }
+    |.endif
     break;
 
   case BC_ITERL:
@@ -5225,8 +5189,10 @@ static void emit_asm_debug(BuildCtx *ctx)
        fcofs, CFRAME_SIZE);
     for (i = 23; i >= 16; i--)
       fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+i, 26-i);
+#if !LJ_SOFTFP
     for (i = 30; i >= 20; i -= 2)
       fprintf(ctx->fp, "\t.byte %d\n\t.uleb128 %d\n", 0x80+32+i, 42-i);
+#endif
     fprintf(ctx->fp,
        "\t.align 2\n"
        ".LEFDE2:\n\n");