From 03946ac978d9a1a3230619e3da048002e5fda2d1 Mon Sep 17 00:00:00 2001
From: Mike Pall <mike>
Date: Thu, 17 Feb 2011 00:44:14 +0100
Subject: DUALNUM: Add integer type to core VM.

---
 src/lib_base.c    |  28 +++++++-----
 src/lib_io.c      |  42 +++++++++++-------
 src/lib_jit.c     |  25 ++++++-----
 src/lib_math.c    |   2 +-
 src/lib_string.c  |   6 +--
 src/lib_table.c   |   8 ++--
 src/lj_api.c      | 103 +++++++++++++++++++++++++------------------
 src/lj_ctype.c    |   2 +-
 src/lj_def.h      |   1 -
 src/lj_dispatch.c |   2 +-
 src/lj_err.c      |  12 ++---
 src/lj_func.c     |   4 +-
 src/lj_lex.c      |   9 +++-
 src/lj_lib.c      |  14 +++---
 src/lj_lib.h      |   4 --
 src/lj_meta.c     |  12 ++---
 src/lj_obj.c      |   4 +-
 src/lj_obj.h      |  63 +++++++++++++++++++++++---
 src/lj_parse.c    | 129 ++++++++++++++++++++++++++++++++++++++++--------------
 src/lj_snap.c     |   2 +-
 src/lj_str.c      |  79 +++++++++++++++++++++++----------
 src/lj_str.h      |   8 +++-
 src/lj_tab.c      |  33 ++++++++++----
 23 files changed, 403 insertions(+), 189 deletions(-)

diff --git a/src/lib_base.c b/src/lib_base.c
index 1a9a6df2..746a4fd6 100644
--- a/src/lib_base.c
+++ b/src/lib_base.c
@@ -190,8 +190,8 @@ LJLIB_ASM(tonumber)		LJLIB_REC(.)
   int32_t base = lj_lib_optint(L, 2, 10);
   if (base == 10) {
     TValue *o = lj_lib_checkany(L, 1);
-    if (tvisnum(o) || (tvisstr(o) && lj_str_tonum(strV(o), o))) {
-      setnumV(L->base-1, numV(o));
+    if (tvisnumber(o) || (tvisstr(o) && lj_str_tonumber(strV(o), o))) {
+      copyTV(L, L->base-1, o);
       return FFH_RES(1);
     }
 #if LJ_HASFFI
@@ -212,7 +212,10 @@ LJLIB_ASM(tonumber)		LJLIB_REC(.)
     if (p != ep) {
       while (lj_char_isspace((unsigned char)(*ep))) ep++;
       if (*ep == '\0') {
-	setnumV(L->base-1, cast_num(ul));
+	if (LJ_DUALNUM && LJ_LIKELY(ul < 0x80000000u))
+	  setintV(L->base-1, (int32_t)ul);
+	else
+	  setnumV(L->base-1, (lua_Number)ul);
 	return FFH_RES(1);
       }
     }
@@ -234,8 +237,8 @@ LJLIB_ASM(tostring)		LJLIB_REC(.)
     return FFH_TAILCALL;
   } else {
     GCstr *s;
-    if (tvisnum(o)) {
-      s = lj_str_fromnum(L, &o->n);
+    if (tvisnumber(o)) {
+      s = lj_str_fromnumber(L, o);
     } else if (tvispri(o)) {
       s = strV(lj_lib_upvalue(L, -(int32_t)itype(o)));
     } else {
@@ -359,7 +362,7 @@ static const char *reader_func(lua_State *L, void *ud, size_t *size)
   if (tvisnil(L->top)) {
     *size = 0;
     return NULL;
-  } else if (tvisstr(L->top) || tvisnum(L->top)) {
+  } else if (tvisstr(L->top) || tvisnumber(L->top)) {
     copyTV(L, L->base+2, L->top);  /* Anchor string in reserved stack slot. */
     return lua_tolstring(L, 3, size);
   } else {
@@ -385,7 +388,7 @@ LJLIB_CF(dofile)
   if (luaL_loadfile(L, fname ? strdata(fname) : NULL) != 0)
     lua_error(L);
   lua_call(L, 0, LUA_MULTRET);
-  return cast_int(L->top - L->base) - 1;
+  return (int)(L->top - L->base) - 1;
 }
 
 /* -- Base library: GC control -------------------------------------------- */
@@ -402,7 +405,7 @@ LJLIB_CF(collectgarbage)
     "\4stop\7restart\7collect\5count\1\377\4step\10setpause\12setstepmul");
   int32_t data = lj_lib_optint(L, 2, 0);
   if (opt == LUA_GCCOUNT) {
-    setnumV(L->top, cast_num(G(L)->gc.total)/1024.0);
+    setnumV(L->top, (lua_Number)G(L)->gc.total/1024.0);
   } else {
     int res = lua_gc(L, opt, data);
     if (opt == LUA_GCSTEP)
@@ -464,8 +467,13 @@ LJLIB_CF(print)
     if (shortcut && tvisstr(o)) {
       str = strVdata(o);
       size = strV(o)->len;
+    } else if (shortcut && tvisint(o)) {
+      char buf[LJ_STR_INTBUF];
+      char *p = lj_str_bufint(buf, intV(o));
+      size = (size_t)(buf+LJ_STR_INTBUF-p);
+      str = p;
     } else if (shortcut && tvisnum(o)) {
-      char buf[LUAI_MAXNUMBER2STR];
+      char buf[LJ_STR_NUMBUF];
       size = lj_str_bufnum(buf, o);
       str = buf;
     } else {
@@ -604,7 +612,7 @@ static void newproxy_weaktable(lua_State *L)
   setgcref(t->metatable, obj2gco(t));
   setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")),
 	    lj_str_newlit(L, "kv"));
-  t->nomm = cast_byte(~(1u<<MM_mode));
+  t->nomm = (uint8_t)(~(1u<<MM_mode));
 }
 
 LUALIB_API int luaopen_base(lua_State *L)
diff --git a/src/lib_io.c b/src/lib_io.c
index 307d005c..66b174f8 100644
--- a/src/lib_io.c
+++ b/src/lib_io.c
@@ -195,7 +195,7 @@ static int io_file_readchars(lua_State *L, FILE *fp, size_t n)
 
 static int io_file_read(lua_State *L, FILE *fp, int start)
 {
-  int ok, n, nargs = cast_int(L->top - L->base) - start;
+  int ok, n, nargs = (int)(L->top - L->base) - start;
   clearerr(fp);
   if (nargs == 0) {
     ok = io_file_readline(L, fp);
@@ -240,10 +240,15 @@ static int io_file_write(lua_State *L, FILE *fp, int start)
     if (tvisstr(tv)) {
       MSize len = strV(tv)->len;
       status = status && (fwrite(strVdata(tv), 1, len, fp) == len);
+    } else if (tvisint(tv)) {
+      char buf[LJ_STR_INTBUF];
+      char *p = lj_str_bufint(buf, intV(tv));
+      size_t len = (size_t)(buf+LJ_STR_INTBUF-p);
+      status = status && (fwrite(p, 1, len, fp) == len);
     } else if (tvisnum(tv)) {
       status = status && (fprintf(fp, LUA_NUMBER_FMT, numV(tv)) > 0);
     } else {
-      lj_err_argt(L, cast_int(tv - L->base) + 1, LUA_TSTRING);
+      lj_err_argt(L, (int)(tv - L->base) + 1, LUA_TSTRING);
     }
   }
   return io_pushresult(L, status, NULL);
@@ -279,37 +284,42 @@ LJLIB_CF(io_method_seek)
 {
   FILE *fp = io_tofile(L)->fp;
   int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end");
-  lua_Number ofs;
+  int64_t ofs = 0;
+  cTValue *o;
   int res;
   if (opt == 0) opt = SEEK_SET;
   else if (opt == 1) opt = SEEK_CUR;
   else if (opt == 2) opt = SEEK_END;
-  lj_lib_opt(L, 3,
-    ofs = lj_lib_checknum(L, 3);
-    ,
-    ofs = 0;
-  )
+  o = L->base+2;
+  if (o < L->top) {
+    if (tvisint(o))
+      ofs = (int64_t)intV(o);
+    else if (tvisnum(o))
+      ofs = (int64_t)numV(o);
+    else if (!tvisnil(o))
+      lj_err_argt(L, 3, LUA_TNUMBER);
+  }
 #if LJ_TARGET_POSIX
-  res = fseeko(fp, (int64_t)ofs, opt);
+  res = fseeko(fp, ofs, opt);
 #elif _MSC_VER >= 1400
-  res = _fseeki64(fp, (int64_t)ofs, opt);
+  res = _fseeki64(fp, ofs, opt);
 #elif defined(__MINGW32__)
-  res = fseeko64(fp, (int64_t)ofs, opt);
+  res = fseeko64(fp, ofs, opt);
 #else
   res = fseek(fp, (long)ofs, opt);
 #endif
   if (res)
     return io_pushresult(L, 0, NULL);
 #if LJ_TARGET_POSIX
-  ofs = cast_num(ftello(fp));
+  ofs = ftello(fp);
 #elif _MSC_VER >= 1400
-  ofs = cast_num(_ftelli64(fp));
+  ofs = _ftelli64(fp);
 #elif defined(__MINGW32__)
-  ofs = cast_num(ftello64(fp));
+  ofs = ftello64(fp);
 #else
-  ofs = cast_num(ftell(fp));
+  ofs = (int64_t)ftell(fp);
 #endif
-  setnumV(L->top-1, ofs);
+  setint64V(L->top-1, ofs);
   return 1;
 }
 
diff --git a/src/lib_jit.c b/src/lib_jit.c
index 66a00523..8f809875 100644
--- a/src/lib_jit.c
+++ b/src/lib_jit.c
@@ -70,7 +70,7 @@ LJLIB_CF(jit_off)
 LJLIB_CF(jit_flush)
 {
 #if LJ_HASJIT
-  if (L->base < L->top && (tvisnum(L->base) || tvisstr(L->base))) {
+  if (L->base < L->top && !tvisnil(L->base)) {
     int traceno = lj_lib_checkint(L, 1);
     luaJIT_setmode(L, traceno, LUAJIT_MODE_FLUSH|LUAJIT_MODE_TRACE);
     return 0;
@@ -202,8 +202,8 @@ LJLIB_CF(jit_util_funcinfo)
     t = tabV(L->top-1);
     if (!iscfunc(fn))
       setintfield(L, t, "ffid", fn->c.ffid);
-    setnumV(lj_tab_setstr(L, t, lj_str_newlit(L, "addr")),
-	    cast_num((intptr_t)fn->c.f));
+    setintptrV(lj_tab_setstr(L, t, lj_str_newlit(L, "addr")),
+	       (intptr_t)(void *)fn->c.f);
     setintfield(L, t, "upvalues", fn->c.nupvalues);
   }
   return 1;
@@ -233,7 +233,7 @@ LJLIB_CF(jit_util_funck)
   ptrdiff_t idx = (ptrdiff_t)lj_lib_checkint(L, 2);
   if (idx >= 0) {
     if (idx < (ptrdiff_t)pt->sizekn) {
-      setnumV(L->top-1, proto_knum(pt, idx));
+      copyTV(L, L->top-1, proto_knumtv(pt, idx));
       return 1;
     }
   } else {
@@ -358,7 +358,7 @@ LJLIB_CF(jit_util_tracemc)
   GCtrace *T = jit_checktrace(L);
   if (T && T->mcode != NULL) {
     setstrV(L, L->top-1, lj_str_new(L, (const char *)T->mcode, T->szmcode));
-    setnumV(L->top++, cast_num((intptr_t)T->mcode));
+    setintptrV(L->top++, (intptr_t)(void *)T->mcode);
     setintV(L->top++, T->mcloop);
     return 3;
   }
@@ -371,7 +371,7 @@ LJLIB_CF(jit_util_traceexitstub)
   ExitNo exitno = (ExitNo)lj_lib_checkint(L, 1);
   jit_State *J = L2J(L);
   if (exitno < EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) {
-    setnumV(L->top-1, cast_num((intptr_t)exitstub_addr(J, exitno)));
+    setintptrV(L->top-1, (intptr_t)(void *)exitstub_addr(J, exitno));
     return 1;
   }
   return 0;
@@ -382,7 +382,7 @@ LJLIB_CF(jit_util_ircalladdr)
 {
   uint32_t idx = (uint32_t)lj_lib_checkint(L, 1);
   if (idx < IRCALL__MAX) {
-    setnumV(L->top-1, cast_num((uintptr_t)(void *)lj_ir_callinfo[idx].func));
+    setintptrV(L->top-1, (intptr_t)(void *)lj_ir_callinfo[idx].func);
     return 1;
   }
   return 0;
@@ -462,11 +462,14 @@ static int jitopt_param(jit_State *J, const char *str)
   int i;
   for (i = 0; i < JIT_P__MAX; i++) {
     size_t len = *(const uint8_t *)lst;
-    TValue tv;
     lua_assert(len != 0);
-    if (strncmp(str, lst+1, len) == 0 && str[len] == '=' &&
-	lj_str_numconv(&str[len+1], &tv)) {
-      J->param[i] = lj_num2int(tv.n);
+    if (strncmp(str, lst+1, len) == 0 && str[len] == '=') {
+      int32_t n = 0;
+      const char *p = &str[len+1];
+      while (*p >= '0' && *p <= '9')
+	n = n*10 + (*p++ - '0');
+      if (*p) return 0;  /* Malformed number. */
+      J->param[i] = n;
       if (i == JIT_P_hotloop)
 	lj_dispatch_init_hotcount(J2G(J));
       return 1;  /* Ok. */
diff --git a/src/lib_math.c b/src/lib_math.c
index 79d91e73..46841e08 100644
--- a/src/lib_math.c
+++ b/src/lib_math.c
@@ -129,7 +129,7 @@ static void random_init(RandomState *rs, double d)
 LJLIB_PUSH(top-2)  /* Upvalue holds userdata with RandomState. */
 LJLIB_CF(math_random)		LJLIB_REC(.)
 {
-  int n = cast_int(L->top - L->base);
+  int n = (int)(L->top - L->base);
   RandomState *rs = (RandomState *)(uddata(udataV(lj_lib_upvalue(L, 1))));
   U64double u;
   double d;
diff --git a/src/lib_string.c b/src/lib_string.c
index 0b1c98a9..61f73060 100644
--- a/src/lib_string.c
+++ b/src/lib_string.c
@@ -61,7 +61,7 @@ LJLIB_ASM(string_byte)		LJLIB_REC(string_range 0)
 
 LJLIB_ASM(string_char)
 {
-  int i, nargs = cast_int(L->top - L->base);
+  int i, nargs = (int)(L->top - L->base);
   char *buf = lj_str_needbuf(L, &G(L)->tmpbuf, (size_t)nargs);
   for (i = 1; i <= nargs; i++) {
     int32_t k = lj_lib_checkint(L, i);
@@ -737,7 +737,7 @@ LJLIB_CF(string_format)
 	tv.n = lj_lib_checknum(L, arg);
 	if (LJ_UNLIKELY((tv.u32.hi << 1) >= 0xffe00000)) {
 	  /* Canonicalize output of non-finite values. */
-	  char *p, nbuf[LUAI_MAXNUMBER2STR];
+	  char *p, nbuf[LJ_STR_NUMBUF];
 	  size_t len = lj_str_bufnum(nbuf, &tv);
 	  if (strfrmt[-1] == 'E' || strfrmt[-1] == 'G') {
 	    nbuf[len-3] = nbuf[len-3] - 0x20;
@@ -801,7 +801,7 @@ LUALIB_API int luaopen_string(lua_State *L)
   g = G(L);
   setgcref(basemt_it(g, LJ_TSTR), obj2gco(mt));
   settabV(L, lj_tab_setstr(L, mt, mmname_str(g, MM_index)), tabV(L->top-1));
-  mt->nomm = cast_byte(~(1u<<MM_index));
+  mt->nomm = (uint8_t)(~(1u<<MM_index));
   return 1;
 }
 
diff --git a/src/lib_table.c b/src/lib_table.c
index 8ae54167..fab6b1f4 100644
--- a/src/lib_table.c
+++ b/src/lib_table.c
@@ -82,8 +82,10 @@ LJLIB_CF(table_maxn)
     }
   node = noderef(t->node);
   for (i = (ptrdiff_t)t->hmask; i >= 0; i--)
-    if (tvisnum(&node[i].key) && numV(&node[i].key) > m)
-      m = numV(&node[i].key);
+    if (tvisnumber(&node[i].key)) {
+      lua_Number n = numberVnum(&node[i].key);
+      if (n > m) m = n;
+    }
   setnumV(L->top-1, m);
   return 1;
 }
@@ -154,7 +156,7 @@ LJLIB_CF(table_concat)
       cTValue *o;
       lua_rawgeti(L, 1, i);
       o = L->top-1;
-      if (!(tvisstr(o) || tvisnum(o)))
+      if (!(tvisstr(o) || tvisnumber(o)))
 	lj_err_callerv(L, LJ_ERR_TABCAT, typename(o), i);
       luaL_addvalue(&b);
       if (i++ == e) break;
diff --git a/src/lj_api.c b/src/lj_api.c
index 92699078..f33748ce 100644
--- a/src/lj_api.c
+++ b/src/lj_api.c
@@ -115,7 +115,7 @@ LUA_API void lua_xmove(lua_State *from, lua_State *to, int n)
 
 LUA_API int lua_gettop(lua_State *L)
 {
-  return cast_int(L->top - L->base);
+  return (int)(L->top - L->base);
 }
 
 LUA_API void lua_settop(lua_State *L, int idx)
@@ -186,7 +186,7 @@ LUA_API void lua_pushvalue(lua_State *L, int idx)
 LUA_API int lua_type(lua_State *L, int idx)
 {
   cTValue *o = index2adr(L, idx);
-  if (tvisnum(o)) {
+  if (tvisnumber(o)) {
     return LUA_TNUMBER;
 #if LJ_64
   } else if (tvislightud(o)) {
@@ -234,13 +234,13 @@ LUA_API int lua_isnumber(lua_State *L, int idx)
 {
   cTValue *o = index2adr(L, idx);
   TValue tmp;
-  return (tvisnum(o) || (tvisstr(o) && lj_str_tonum(strV(o), &tmp)));
+  return (tvisnumber(o) || (tvisstr(o) && lj_str_tonumber(strV(o), &tmp)));
 }
 
 LUA_API int lua_isstring(lua_State *L, int idx)
 {
   cTValue *o = index2adr(L, idx);
-  return (tvisstr(o) || tvisnum(o));
+  return (tvisstr(o) || tvisnumber(o));
 }
 
 LUA_API int lua_isuserdata(lua_State *L, int idx)
@@ -260,8 +260,10 @@ LUA_API int lua_equal(lua_State *L, int idx1, int idx2)
 {
   cTValue *o1 = index2adr(L, idx1);
   cTValue *o2 = index2adr(L, idx2);
-  if (tvisnum(o1) && tvisnum(o2)) {
-    return numV(o1) == numV(o2);
+  if (tvisint(o1) && tvisint(o2)) {
+    return intV(o1) == intV(o2);
+  } else if (tvisnumber(o1) && tvisnumber(o2)) {
+    return numberVnum(o1) == numberVnum(o2);
   } else if (itype(o1) != itype(o2)) {
     return 0;
   } else if (tvispri(o1)) {
@@ -293,8 +295,10 @@ LUA_API int lua_lessthan(lua_State *L, int idx1, int idx2)
   cTValue *o2 = index2adr(L, idx2);
   if (o1 == niltv(L) || o2 == niltv(L)) {
     return 0;
-  } else if (tvisnum(o1) && tvisnum(o2)) {
-    return numV(o1) < numV(o2);
+  } else if (tvisint(o1) && tvisint(o2)) {
+    return intV(o1) < intV(o2);
+  } else if (tvisnumber(o1) && tvisnumber(o2)) {
+    return numberVnum(o1) < numberVnum(o2);
   } else {
     TValue *base = lj_meta_comp(L, o1, o2, 0);
     if ((uintptr_t)base <= 1) {
@@ -312,8 +316,8 @@ LUA_API lua_Number lua_tonumber(lua_State *L, int idx)
 {
   cTValue *o = index2adr(L, idx);
   TValue tmp;
-  if (LJ_LIKELY(tvisnum(o)))
-    return numV(o);
+  if (LJ_LIKELY(tvisnumber(o)))
+    return numberVnum(o);
   else if (tvisstr(o) && lj_str_tonum(strV(o), &tmp))
     return numV(&tmp);
   else
@@ -324,8 +328,8 @@ LUALIB_API lua_Number luaL_checknumber(lua_State *L, int idx)
 {
   cTValue *o = index2adr(L, idx);
   TValue tmp;
-  if (tvisnum(o))
-    return numV(o);
+  if (LJ_LIKELY(tvisnumber(o)))
+    return numberVnum(o);
   else if (!(tvisstr(o) && lj_str_tonum(strV(o), &tmp)))
     lj_err_argt(L, idx, LUA_TNUMBER);
   return numV(&tmp);
@@ -335,8 +339,8 @@ LUALIB_API lua_Number luaL_optnumber(lua_State *L, int idx, lua_Number def)
 {
   cTValue *o = index2adr(L, idx);
   TValue tmp;
-  if (tvisnum(o))
-    return numV(o);
+  if (LJ_LIKELY(tvisnumber(o)))
+    return numberVnum(o);
   else if (tvisnil(o))
     return def;
   else if (!(tvisstr(o) && lj_str_tonum(strV(o), &tmp)))
@@ -349,12 +353,17 @@ LUA_API lua_Integer lua_tointeger(lua_State *L, int idx)
   cTValue *o = index2adr(L, idx);
   TValue tmp;
   lua_Number n;
-  if (LJ_LIKELY(tvisnum(o)))
+  if (LJ_LIKELY(tvisint(o))) {
+    return intV(o);
+  } else if (LJ_LIKELY(tvisnum(o))) {
     n = numV(o);
-  else if (tvisstr(o) && lj_str_tonum(strV(o), &tmp))
+  } else {
+    if (!(tvisstr(o) && lj_str_tonumber(strV(o), &tmp)))
+      return 0;
+    if (tvisint(&tmp))
+      return (lua_Integer)intV(&tmp);
     n = numV(&tmp);
-  else
-    return 0;
+  }
 #if LJ_64
   return (lua_Integer)n;
 #else
@@ -367,12 +376,17 @@ LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int idx)
   cTValue *o = index2adr(L, idx);
   TValue tmp;
   lua_Number n;
-  if (LJ_LIKELY(tvisnum(o)))
+  if (LJ_LIKELY(tvisint(o))) {
+    return intV(o);
+  } else if (LJ_LIKELY(tvisnum(o))) {
     n = numV(o);
-  else if (tvisstr(o) && lj_str_tonum(strV(o), &tmp))
+  } else {
+    if (!(tvisstr(o) && lj_str_tonumber(strV(o), &tmp)))
+      lj_err_argt(L, idx, LUA_TNUMBER);
+    if (tvisint(&tmp))
+      return (lua_Integer)intV(&tmp);
     n = numV(&tmp);
-  else
-    lj_err_argt(L, idx, LUA_TNUMBER);
+  }
 #if LJ_64
   return (lua_Integer)n;
 #else
@@ -385,14 +399,19 @@ LUALIB_API lua_Integer luaL_optinteger(lua_State *L, int idx, lua_Integer def)
   cTValue *o = index2adr(L, idx);
   TValue tmp;
   lua_Number n;
-  if (LJ_LIKELY(tvisnum(o)))
+  if (LJ_LIKELY(tvisint(o))) {
+    return intV(o);
+  } else if (LJ_LIKELY(tvisnum(o))) {
     n = numV(o);
-  else if (tvisnil(o))
+  } else if (tvisnil(o)) {
     return def;
-  else if (tvisstr(o) && lj_str_tonum(strV(o), &tmp))
+  } else {
+    if (!(tvisstr(o) && lj_str_tonumber(strV(o), &tmp)))
+      lj_err_argt(L, idx, LUA_TNUMBER);
+    if (tvisint(&tmp))
+      return (lua_Integer)intV(&tmp);
     n = numV(&tmp);
-  else
-    lj_err_argt(L, idx, LUA_TNUMBER);
+  }
 #if LJ_64
   return (lua_Integer)n;
 #else
@@ -412,10 +431,10 @@ LUA_API const char *lua_tolstring(lua_State *L, int idx, size_t *len)
   GCstr *s;
   if (LJ_LIKELY(tvisstr(o))) {
     s = strV(o);
-  } else if (tvisnum(o)) {
+  } else if (tvisnumber(o)) {
     lj_gc_check(L);
     o = index2adr(L, idx);  /* GC may move the stack. */
-    s = lj_str_fromnum(L, &o->n);
+    s = lj_str_fromnumber(L, o);
   } else {
     if (len != NULL) *len = 0;
     return NULL;
@@ -430,10 +449,10 @@ LUALIB_API const char *luaL_checklstring(lua_State *L, int idx, size_t *len)
   GCstr *s;
   if (LJ_LIKELY(tvisstr(o))) {
     s = strV(o);
-  } else if (tvisnum(o)) {
+  } else if (tvisnumber(o)) {
     lj_gc_check(L);
     o = index2adr(L, idx);  /* GC may move the stack. */
-    s = lj_str_fromnum(L, &o->n);
+    s = lj_str_fromnumber(L, o);
   } else {
     lj_err_argt(L, idx, LUA_TSTRING);
   }
@@ -451,10 +470,10 @@ LUALIB_API const char *luaL_optlstring(lua_State *L, int idx,
   } else if (tvisnil(o)) {
     if (len != NULL) *len = def ? strlen(def) : 0;
     return def;
-  } else if (tvisnum(o)) {
+  } else if (tvisnumber(o)) {
     lj_gc_check(L);
     o = index2adr(L, idx);  /* GC may move the stack. */
-    s = lj_str_fromnum(L, &o->n);
+    s = lj_str_fromnumber(L, o);
   } else {
     lj_err_argt(L, idx, LUA_TSTRING);
   }
@@ -484,8 +503,8 @@ LUA_API size_t lua_objlen(lua_State *L, int idx)
     return cast(size_t, lj_tab_len(tabV(o)));
   else if (tvisudata(o))
     return udataV(o)->len;
-  else if (tvisnum(o))
-    return lj_str_fromnum(L, &o->n)->len;
+  else if (tvisnumber(o))
+    return lj_str_fromnumber(L, o)->len;
   else
     return 0;
 }
@@ -551,7 +570,7 @@ LUA_API void lua_pushnumber(lua_State *L, lua_Number n)
 
 LUA_API void lua_pushinteger(lua_State *L, lua_Integer n)
 {
-  setnumV(L->top, cast_num(n));
+  setintptrV(L->top, n);
   incr_top(L);
 }
 
@@ -687,7 +706,7 @@ LUA_API void lua_concat(lua_State *L, int n)
 	L->top -= n;
 	break;
       }
-      n -= cast_int(L->top - top);
+      n -= (int)(L->top - top);
       L->top = top+2;
       lj_vm_call(L, top, 1+1);
       L->top--;
@@ -1085,7 +1104,7 @@ LUA_API int lua_yield(lua_State *L, int nresults)
       setcont(top+1, lj_cont_hook);
       setframe_pc(top+1, cframe_pc(cf)-1);
       setframe_gc(top+2, obj2gco(L));
-      top[2].fr.tp.ftsz = cast_int((char *)(top+3)-(char *)L->base)+FRAME_CONT;
+      top[2].fr.tp.ftsz = (int)((char *)(top+3)-(char *)L->base)+FRAME_CONT;
       L->top = L->base = top+3;
     }
     L->cframe = NULL;
@@ -1160,10 +1179,10 @@ LUA_API int lua_gc(lua_State *L, int what, int data)
     lj_gc_fullgc(L);
     break;
   case LUA_GCCOUNT:
-    res = cast_int(g->gc.total >> 10);
+    res = (int)(g->gc.total >> 10);
     break;
   case LUA_GCCOUNTB:
-    res = cast_int(g->gc.total & 0x3ff);
+    res = (int)(g->gc.total & 0x3ff);
     break;
   case LUA_GCSTEP: {
     MSize a = (MSize)data << 10;
@@ -1176,11 +1195,11 @@ LUA_API int lua_gc(lua_State *L, int what, int data)
     break;
   }
   case LUA_GCSETPAUSE:
-    res = cast_int(g->gc.pause);
+    res = (int)(g->gc.pause);
     g->gc.pause = (MSize)data;
     break;
   case LUA_GCSETSTEPMUL:
-    res = cast_int(g->gc.stepmul);
+    res = (int)(g->gc.stepmul);
     g->gc.stepmul = (MSize)data;
     break;
   default:
diff --git a/src/lj_ctype.c b/src/lj_ctype.c
index 585039ef..956f0a09 100644
--- a/src/lj_ctype.c
+++ b/src/lj_ctype.c
@@ -522,7 +522,7 @@ GCstr *lj_ctype_repr_int64(lua_State *L, uint64_t n, int isunsigned)
 /* Convert complex to string with 'i' or 'I' suffix. */
 GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size)
 {
-  char buf[2*LUAI_MAXNUMBER2STR+2+1];
+  char buf[2*LJ_STR_NUMBUF+2+1];
   TValue re, im;
   size_t len;
   if (size == 2*sizeof(double)) {
diff --git a/src/lj_def.h b/src/lj_def.h
index ac457cf5..f6f03318 100644
--- a/src/lj_def.h
+++ b/src/lj_def.h
@@ -81,7 +81,6 @@ typedef unsigned __int32 uintptr_t;
 #define U64x(hi, lo)	(((uint64_t)0x##hi << 32) + (uint64_t)0x##lo)
 #define cast_byte(i)	cast(uint8_t, (i))
 #define cast_num(i)	cast(lua_Number, (i))
-#define cast_int(i)	cast(int, (i))
 #define i32ptr(p)	((int32_t)(intptr_t)(void *)(p))
 #define u32ptr(p)	((uint32_t)(intptr_t)(void *)(p))
 
diff --git a/src/lj_dispatch.c b/src/lj_dispatch.c
index a8aa0731..0b14e21c 100644
--- a/src/lj_dispatch.c
+++ b/src/lj_dispatch.c
@@ -320,7 +320,7 @@ static void callhook(lua_State *L, int event, BCLine line)
     ar.event = event;
     ar.currentline = line;
     /* Top frame, nextframe = NULL. */
-    ar.i_ci = cast_int((L->base-1) - tvref(L->stack));
+    ar.i_ci = (int)((L->base-1) - tvref(L->stack));
     lj_state_checkstack(L, 1+LUA_MINSTACK);
     hook_enter(g);
     hookf(L, &ar);
diff --git a/src/lj_err.c b/src/lj_err.c
index 12f32af2..2503c218 100644
--- a/src/lj_err.c
+++ b/src/lj_err.c
@@ -397,7 +397,7 @@ cTValue *lj_err_getframe(lua_State *L, int level, int *size)
     if (frame_gc(frame) == obj2gco(L))
       level++;  /* Skip dummy frames. See lj_meta_call(). */
     if (level-- == 0) {
-      *size = cast_int(nextframe - frame);
+      *size = (int)(nextframe - frame);
       return frame;  /* Level found. */
     }
     nextframe = frame;
@@ -418,7 +418,7 @@ LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar)
   int size;
   cTValue *frame = lj_err_getframe(L, level, &size);
   if (frame) {
-    ar->i_ci = (size << 16) + cast_int(frame - tvref(L->stack));
+    ar->i_ci = (size << 16) + (int)(frame - tvref(L->stack));
     return 1;
   } else {
     ar->i_ci = level - size;
@@ -486,7 +486,7 @@ static void *err_unwind(lua_State *L, void *stopcf, int errcode)
       if (cframe_canyield(cf)) {  /* Resume? */
 	if (errcode) {
 	  L->cframe = NULL;
-	  L->status = cast_byte(errcode);
+	  L->status = (uint8_t)errcode;
 	}
 	return cf;
       }
@@ -534,7 +534,7 @@ static void *err_unwind(lua_State *L, void *stopcf, int errcode)
 #define LJ_UEXCLASS		0x4c55414a49543200ULL	/* LUAJIT2\0 */
 #define LJ_UEXCLASS_MAKE(c)	(LJ_UEXCLASS | (_Unwind_Exception_Class)(c))
 #define LJ_UEXCLASS_CHECK(cl)	(((cl) ^ LJ_UEXCLASS) <= 0xff)
-#define LJ_UEXCLASS_ERRCODE(cl)	(cast_int((cl) & 0xff))
+#define LJ_UEXCLASS_ERRCODE(cl)	((int)((cl) & 0xff))
 
 /* DWARF2 personality handler referenced from interpreter .eh_frame. */
 LJ_FUNCA int lj_err_unwind_dwarf(int version, _Unwind_Action actions,
@@ -642,7 +642,7 @@ extern __DestructExceptionObject(EXCEPTION_RECORD *rec, int nothrow);
 #define LJ_EXCODE		((DWORD)0xe24c4a00)
 #define LJ_EXCODE_MAKE(c)	(LJ_EXCODE | (DWORD)(c))
 #define LJ_EXCODE_CHECK(cl)	(((cl) ^ LJ_EXCODE) <= 0xff)
-#define LJ_EXCODE_ERRCODE(cl)	(cast_int((cl) & 0xff))
+#define LJ_EXCODE_ERRCODE(cl)	((int)((cl) & 0xff))
 
 /* Win64 exception handler for interpreter frame. */
 LJ_FUNCA EXCEPTION_DISPOSITION lj_err_unwind_win64(EXCEPTION_RECORD *rec,
@@ -945,7 +945,7 @@ LJ_NORET LJ_NOINLINE static void err_argmsg(lua_State *L, int narg,
   const char *fname = "?";
   const char *ftype = getfuncname(L, L->base - 1, &fname);
   if (narg < 0 && narg > LUA_REGISTRYINDEX)
-    narg = cast_int(L->top - L->base) + narg + 1;
+    narg = (int)(L->top - L->base) + narg + 1;
   if (ftype && ftype[3] == 'h' && --narg == 0)  /* Check for "method". */
     msg = lj_str_pushf(L, err2msg(LJ_ERR_BADSELF), fname, msg);
   else
diff --git a/src/lj_func.c b/src/lj_func.c
index 83dab3b1..b3564091 100644
--- a/src/lj_func.c
+++ b/src/lj_func.c
@@ -98,7 +98,7 @@ GCfunc *lj_func_newC(lua_State *L, MSize nelems, GCtab *env)
   GCfunc *fn = cast(GCfunc *, lj_mem_newgco(L, sizeCfunc(nelems)));
   fn->c.gct = ~LJ_TFUNC;
   fn->c.ffid = FF_C;
-  fn->c.nupvalues = cast_byte(nelems);
+  fn->c.nupvalues = (uint8_t)nelems;
   /* NOBARRIER: The GCfunc is new (marked white). */
   setmref(fn->c.pc, &G(L)->bc_cfunc_ext);
   setgcref(fn->c.env, obj2gco(env));
@@ -110,7 +110,7 @@ GCfunc *lj_func_newL(lua_State *L, GCproto *pt, GCtab *env)
   GCfunc *fn = cast(GCfunc *, lj_mem_newgco(L, sizeLfunc((MSize)pt->sizeuv)));
   fn->l.gct = ~LJ_TFUNC;
   fn->l.ffid = FF_LUA;
-  fn->l.nupvalues = cast_byte(pt->sizeuv);
+  fn->l.nupvalues = (uint8_t)pt->sizeuv;
   /* NOBARRIER: Really a setgcref. But the GCfunc is new (marked white). */
   setmref(fn->l.pc, proto_bc(pt));
   setgcref(fn->l.env, obj2gco(env));
diff --git a/src/lj_lex.c b/src/lj_lex.c
index ac0d1e95..78458572 100644
--- a/src/lj_lex.c
+++ b/src/lj_lex.c
@@ -170,10 +170,15 @@ static void lex_number(LexState *ls, TValue *tv)
     if (c == 'I') {  /* Return cdata holding a complex number. */
       GCcdata *cd = lj_cdata_new_(ls->L, CTID_COMPLEX_DOUBLE, 2*sizeof(double));
       ((double *)cdataptr(cd))[0] = 0;
-      ((double *)cdataptr(cd))[1] = tv->n;
+      ((double *)cdataptr(cd))[1] = numberVnum(tv);
       lj_parse_keepcdata(ls, tv, cd);
     }
 #endif
+    if (LJ_DUALNUM && tvisnum(tv)) {
+      int32_t k = lj_num2int(numV(tv));
+      if ((lua_Number)k == numV(tv))  /* -0 cannot end up here. */
+	setintV(tv, k);
+    }
     return;
   }
   lj_lex_error(ls, TK_number, LJ_ERR_XNUMBER);
@@ -506,7 +511,7 @@ void lj_lex_init(lua_State *L)
   for (i = 0; i < TK_RESERVED; i++) {
     GCstr *s = lj_str_newz(L, tokennames[i]);
     fixstring(s);  /* Reserved words are never collected. */
-    s->reserved = cast_byte(i+1);
+    s->reserved = (uint8_t)(i+1);
   }
 }
 
diff --git a/src/lj_lib.c b/src/lj_lib.c
index a6681f2b..930e59a0 100644
--- a/src/lj_lib.c
+++ b/src/lj_lib.c
@@ -135,8 +135,8 @@ GCstr *lj_lib_checkstr(lua_State *L, int narg)
   if (o < L->top) {
     if (LJ_LIKELY(tvisstr(o))) {
       return strV(o);
-    } else if (tvisnum(o)) {
-      GCstr *s = lj_str_fromnum(L, &o->n);
+    } else if (tvisnumber(o)) {
+      GCstr *s = lj_str_fromnumber(L, o);
       setstrV(L, o, s);
       return s;
     }
@@ -155,14 +155,18 @@ lua_Number lj_lib_checknum(lua_State *L, int narg)
 {
   TValue *o = L->base + narg-1;
   if (!(o < L->top &&
-	(tvisnum(o) || (tvisstr(o) && lj_str_tonum(strV(o), o)))))
+	(tvisnumber(o) || (tvisstr(o) && lj_str_tonumber(strV(o), o)))))
     lj_err_argt(L, narg, LUA_TNUMBER);
-  return numV(o);
+  return numberVnum(o);
 }
 
 int32_t lj_lib_checkint(lua_State *L, int narg)
 {
-  return lj_num2int(lj_lib_checknum(L, narg));
+  TValue *o = L->base + narg-1;
+  if (!(o < L->top &&
+	(tvisnumber(o) || (tvisstr(o) && lj_str_tonumber(strV(o), o)))))
+    lj_err_argt(L, narg, LUA_TNUMBER);
+  return numberVint(o);
 }
 
 int32_t lj_lib_optint(lua_State *L, int narg, int32_t def)
diff --git a/src/lj_lib.h b/src/lj_lib.h
index b9bd4c2d..ae4be384 100644
--- a/src/lj_lib.h
+++ b/src/lj_lib.h
@@ -41,10 +41,6 @@ LJ_FUNC GCtab *lj_lib_checktab(lua_State *L, int narg);
 LJ_FUNC GCtab *lj_lib_checktabornil(lua_State *L, int narg);
 LJ_FUNC int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst);
 
-#define lj_lib_opt(L, narg, gotarg, noarg) \
-  { TValue *_o = L->base + (narg)-1; \
-    if (_o < L->top && !tvisnil(_o)) { gotarg } else { noarg } }
-
 /* Avoid including lj_frame.h. */
 #define lj_lib_upvalue(L, n) \
   (&gcref((L->base-1)->fr.func)->fn.c.upvalue[(n)-1])
diff --git a/src/lj_meta.c b/src/lj_meta.c
index 0df1de08..32024e85 100644
--- a/src/lj_meta.c
+++ b/src/lj_meta.c
@@ -44,7 +44,7 @@ cTValue *lj_meta_cache(GCtab *mt, MMS mm, GCstr *name)
   cTValue *mo = lj_tab_getstr(mt, name);
   lua_assert(mm <= MM_FAST);
   if (!mo || tvisnil(mo)) {  /* No metamethod? */
-    mt->nomm |= cast_byte(1u<<mm);  /* Set negative cache flag. */
+    mt->nomm |= (uint8_t)(1u<<mm);  /* Set negative cache flag. */
     return NULL;
   }
   return mo;
@@ -156,6 +156,8 @@ static cTValue *str2num(cTValue *o, TValue *n)
 {
   if (tvisnum(o))
     return o;
+  else if (tvisint(o))
+    return (setnumV(n, (lua_Number)intV(o)), n);
   else if (tvisstr(o) && lj_str_tonum(strV(o), n))
     return n;
   else
@@ -192,8 +194,8 @@ static LJ_AINLINE int tostring(lua_State *L, TValue *o)
 {
   if (tvisstr(o)) {
     return 1;
-  } else if (tvisnum(o)) {
-    setstrV(L, o, lj_str_fromnum(L, &o->n));
+  } else if (tvisnumber(o)) {
+    setstrV(L, o, lj_str_fromnumber(L, o));
     return 1;
   } else {
     return 0;
@@ -205,12 +207,12 @@ TValue *lj_meta_cat(lua_State *L, TValue *top, int left)
 {
   do {
     int n = 1;
-    if (!(tvisstr(top-1) || tvisnum(top-1)) || !tostring(L, top)) {
+    if (!(tvisstr(top-1) || tvisnumber(top-1)) || !tostring(L, top)) {
       cTValue *mo = lj_meta_lookup(L, top-1, MM_concat);
       if (tvisnil(mo)) {
 	mo = lj_meta_lookup(L, top, MM_concat);
 	if (tvisnil(mo)) {
-	  if (tvisstr(top-1) || tvisnum(top-1)) top++;
+	  if (tvisstr(top-1) || tvisnumber(top-1)) top++;
 	  lj_err_optype(L, top-1, LJ_ERR_OPCAT);
 	  return NULL;  /* unreachable */
 	}
diff --git a/src/lj_obj.c b/src/lj_obj.c
index 60f5dd3a..1476f0b2 100644
--- a/src/lj_obj.c
+++ b/src/lj_obj.c
@@ -27,9 +27,9 @@ int lj_obj_equal(cTValue *o1, cTValue *o2)
       return 1;
     if (!tvisnum(o1))
       return gcrefeq(o1->gcr, o2->gcr);
-  } else if (!tvisnum(o1) || !tvisnum(o2)) {
+  } else if (!tvisnumber(o1) || !tvisnumber(o2)) {
     return 0;
   }
-  return numV(o1) == numV(o2);
+  return numberVnum(o1) == numberVnum(o2);
 }
 
diff --git a/src/lj_obj.h b/src/lj_obj.h
index 1bd50809..88289f3e 100644
--- a/src/lj_obj.h
+++ b/src/lj_obj.h
@@ -140,7 +140,10 @@ typedef LJ_ALIGN(8) union TValue {
   lua_Number n;		/* Number object overlaps split tag/value object. */
   struct {
     LJ_ENDIAN_LOHI(
-      GCRef gcr;	/* GCobj reference (if any). */
+      union {
+	GCRef gcr;	/* GCobj reference (if any). */
+	int32_t i;	/* Integer value. */
+      };
     , uint32_t it;	/* Internal object tag. Must overlap MSW of number. */
     )
   };
@@ -180,6 +183,7 @@ typedef const TValue cTValue;
 ** lightuserdata   |  itype  |  void * |  (32 bit platforms)
 ** lightuserdata   |ffff|    void *    |  (64 bit platforms, 47 bit pointers)
 ** GC objects      |  itype  |  GCRef  |
+** int (LJ_DUALNUM)|  itype  |   int   |
 ** number           -------double------
 **
 ** ORDER LJ_T
@@ -203,6 +207,7 @@ typedef const TValue cTValue;
 /* This is just the canonical number type used in some places. */
 #define LJ_TNUMX		(~13u)
 
+/* Integers have itype == LJ_TISNUM doubles have itype < LJ_TISNUM */
 #if LJ_64
 #define LJ_TISNUM		0xfffeffffu
 #else
@@ -322,6 +327,8 @@ typedef struct GCproto {
 	    gcref(mref((pt)->k, GCRef)[(idx)]))
 #define proto_knum(pt, idx) \
   check_exp((uintptr_t)(idx) < (pt)->sizekn, mref((pt)->k, lua_Number)[(idx)])
+#define proto_knumtv(pt, idx) \
+  check_exp((uintptr_t)(idx) < (pt)->sizekn, &mref((pt)->k, TValue)[(idx)])
 #define proto_bc(pt)		((BCIns *)((char *)(pt) + sizeof(GCproto)))
 #define proto_bcpos(pt, pc)	((BCPos)((pc) - proto_bc(pt)))
 #define proto_uv(pt)		(mref((pt)->uv, uint16_t))
@@ -650,7 +657,9 @@ typedef union GCobj {
 #define tviscdata(o)	(itype(o) == LJ_TCDATA)
 #define tvistab(o)	(itype(o) == LJ_TTAB)
 #define tvisudata(o)	(itype(o) == LJ_TUDATA)
-#define tvisnum(o)	(itype(o) <= LJ_TISNUM)
+#define tvisnumber(o)	(itype(o) <= LJ_TISNUM)
+#define tvisint(o)	(LJ_DUALNUM && itype(o) == LJ_TISNUM)
+#define tvisnum(o)	(itype(o) < LJ_TISNUM)
 
 #define tvistruecond(o)	(itype(o) < LJ_TISTRUECOND)
 #define tvispri(o)	(itype(o) >= LJ_TISPRI)
@@ -659,6 +668,11 @@ typedef union GCobj {
 
 /* Special macros to test numbers for NaN, +0, -0, +1 and raw equality. */
 #define tvisnan(o)	((o)->n != (o)->n)
+#if LJ_64
+#define tviszero(o)	(((o)->u64 << 1) == 0)
+#else
+#define tviszero(o)	(((o)->u32.lo | ((o)->u32.hi << 1)) == 0)
+#endif
 #define tvispzero(o)	((o)->u64 == 0)
 #define tvismzero(o)	((o)->u64 == U64x(80000000,00000000))
 #define tvispone(o)	((o)->u64 == U64x(3ff00000,00000000))
@@ -667,9 +681,9 @@ typedef union GCobj {
 /* Macros to convert type ids. */
 #if LJ_64
 #define itypemap(o) \
-  (tvisnum(o) ? ~LJ_TNUMX : tvislightud(o) ? ~LJ_TLIGHTUD : ~itype(o))
+  (tvisnumber(o) ? ~LJ_TNUMX : tvislightud(o) ? ~LJ_TLIGHTUD : ~itype(o))
 #else
-#define itypemap(o)	(tvisnum(o) ? ~LJ_TNUMX : ~itype(o))
+#define itypemap(o)	(tvisnumber(o) ? ~LJ_TNUMX : ~itype(o))
 #endif
 
 /* Macros to get tagged values. */
@@ -690,6 +704,7 @@ typedef union GCobj {
 #define tabV(o)		check_exp(tvistab(o), &gcval(o)->tab)
 #define udataV(o)	check_exp(tvisudata(o), &gcval(o)->ud)
 #define numV(o)		check_exp(tvisnum(o), (o)->n)
+#define intV(o)		check_exp(tvisint(o), (int32_t)(o)->i)
 
 /* Macros to set tagged values. */
 #define setitype(o, i)		((o)->it = (i))
@@ -741,7 +756,29 @@ define_setV(setudataV, GCudata, LJ_TUDATA)
 #define setnanV(o)		((o)->u64 = U64x(fff80000,00000000))
 #define setpinfV(o)		((o)->u64 = U64x(7ff00000,00000000))
 #define setminfV(o)		((o)->u64 = U64x(fff00000,00000000))
-#define setintV(o, i)		((o)->n = cast_num((int32_t)(i)))
+
+static LJ_AINLINE void setintV(TValue *o, int32_t i)
+{
+#if LJ_DUALNUM
+  o->i = (uint32_t)i; setitype(o, LJ_TISNUM);
+#else
+  o->n = (lua_Number)i;
+#endif
+}
+
+static LJ_AINLINE void setint64V(TValue *o, int64_t i)
+{
+  if (LJ_DUALNUM && LJ_LIKELY(i == (int64_t)(int32_t)i))
+    setintV(o, (int32_t)i);
+  else
+    setnumV(o, (lua_Number)i);
+}
+
+#if LJ_64
+#define setintptrV(o, i)	setint64V((o), (i))
+#else
+#define setintptrV(o, i)	setintV((o), (i))
+#endif
 
 /* Copy tagged values. */
 static LJ_AINLINE void copyTV(lua_State *L, TValue *o1, const TValue *o2)
@@ -774,6 +811,22 @@ static LJ_AINLINE uint64_t lj_num2u64(lua_Number n)
     return (uint64_t)n;
 }
 
+static LJ_AINLINE int32_t numberVint(cTValue *o)
+{
+  if (LJ_LIKELY(tvisint(o)))
+    return intV(o);
+  else
+    return lj_num2int(numV(o));
+}
+
+static LJ_AINLINE lua_Number numberVnum(cTValue *o)
+{
+  if (LJ_UNLIKELY(tvisint(o)))
+    return (lua_Number)intV(o);
+  else
+    return numV(o);
+}
+
 /* -- Miscellaneous object handling --------------------------------------- */
 
 /* Names and maps for internal and external object tags. */
diff --git a/src/lj_parse.c b/src/lj_parse.c
index 0f5577e1..858891d2 100644
--- a/src/lj_parse.c
+++ b/src/lj_parse.c
@@ -73,7 +73,8 @@ typedef struct ExpDesc {
 #define expr_isnumk_nojump(e)	(expr_isnumk(e) && !expr_hasjump(e))
 #define expr_isstrk(e)		((e)->k == VKSTR)
 
-#define expr_numV(e)		check_exp(expr_isnumk((e)), numV(&(e)->u.nval))
+#define expr_numtv(e)		check_exp(expr_isnumk((e)), &(e)->u.nval)
+#define expr_numberV(e)		numberVnum(expr_numtv((e)))
 
 /* Initialize expression. */
 static LJ_AINLINE void expr_init(ExpDesc *e, ExpKind k, uint32_t info)
@@ -83,6 +84,13 @@ static LJ_AINLINE void expr_init(ExpDesc *e, ExpKind k, uint32_t info)
   e->f = e->t = NO_JMP;
 }
 
+/* Check number constant for +-0. */
+static int expr_numiszero(ExpDesc *e)
+{
+  TValue *o = expr_numtv(e);
+  return tvisint(o) ? (intV(o) == 0) : tviszero(o);
+}
+
 /* Per-function linked list of scope blocks. */
 typedef struct FuncScope {
   struct FuncScope *prev;	/* Link to outer scope. */
@@ -174,16 +182,19 @@ LJ_NORET static void err_limit(FuncState *fs, uint32_t limit, const char *what)
 /* Return bytecode encoding for primitive constant. */
 #define const_pri(e)		check_exp((e)->k <= VKTRUE, (e)->k)
 
+#define tvhaskslot(o)	((o)->u32.hi == 0)
+#define tvkslot(o)	((o)->u32.lo)
+
 /* Add a number constant. */
 static BCReg const_num(FuncState *fs, ExpDesc *e)
 {
   lua_State *L = fs->L;
-  TValue *val;
+  TValue *o;
   lua_assert(expr_isnumk(e));
-  val = lj_tab_set(L, fs->kt, &e->u.nval);
-  if (tvisnum(val))
-    return val->u32.lo;
-  val->u64 = fs->nkn;
+  o = lj_tab_set(L, fs->kt, &e->u.nval);
+  if (tvhaskslot(o))
+    return tvkslot(o);
+  o->u64 = fs->nkn;
   return fs->nkn++;
 }
 
@@ -191,13 +202,13 @@ static BCReg const_num(FuncState *fs, ExpDesc *e)
 static BCReg const_gc(FuncState *fs, GCobj *gc, uint32_t itype)
 {
   lua_State *L = fs->L;
-  TValue o, *val;
-  setgcV(L, &o, gc, itype);
+  TValue key, *o;
+  setgcV(L, &key, gc, itype);
   /* NOBARRIER: the key is new or kept alive. */
-  val = lj_tab_set(L, fs->kt, &o);
-  if (tvisnum(val))
-    return val->u32.lo;
-  val->u64 = fs->nkgc;
+  o = lj_tab_set(L, fs->kt, &key);
+  if (tvhaskslot(o))
+    return tvkslot(o);
+  o->u64 = fs->nkgc;
   return fs->nkgc++;
 }
 
@@ -344,7 +355,7 @@ static void bcreg_bump(FuncState *fs, BCReg n)
   if (sz > fs->framesize) {
     if (sz >= LJ_MAX_SLOTS)
       err_syntax(fs->ls, LJ_ERR_XSLOTS);
-    fs->framesize = cast_byte(sz);
+    fs->framesize = (uint8_t)sz;
   }
 }
 
@@ -478,11 +489,18 @@ static void expr_toreg_nobranch(FuncState *fs, ExpDesc *e, BCReg reg)
   if (e->k == VKSTR) {
     ins = BCINS_AD(BC_KSTR, reg, const_str(fs, e));
   } else if (e->k == VKNUM) {
-    lua_Number n = expr_numV(e);
+#if LJ_DUALNUM
+    cTValue *tv = expr_numtv(e);
+    if (tvisint(tv) && checki16(intV(tv)))
+      ins = BCINS_AD(BC_KSHORT, reg, (BCReg)(uint16_t)intV(tv));
+    else
+#else
+    lua_Number n = expr_numberV(e);
     int32_t k = lj_num2int(n);
-    if (checki16(k) && n == cast_num(k))
+    if (checki16(k) && n == (lua_Number)k)
       ins = BCINS_AD(BC_KSHORT, reg, (BCReg)(uint16_t)k);
     else
+#endif
       ins = BCINS_AD(BC_KNUM, reg, const_num(fs, e));
 #if LJ_HASFFI
   } else if (e->k == VKCDATA) {
@@ -720,10 +738,19 @@ static void bcemit_branch_f(FuncState *fs, ExpDesc *e)
 static int foldarith(BinOpr opr, ExpDesc *e1, ExpDesc *e2)
 {
   TValue o;
+  lua_Number n;
   if (!expr_isnumk_nojump(e1) || !expr_isnumk_nojump(e2)) return 0;
-  setnumV(&o, lj_vm_foldarith(expr_numV(e1), expr_numV(e2), (int)opr-OPR_ADD));
+  n = lj_vm_foldarith(expr_numberV(e1), expr_numberV(e2), (int)opr-OPR_ADD);
+  setnumV(&o, n);
   if (tvisnan(&o) || tvismzero(&o)) return 0;  /* Avoid NaN and -0 as consts. */
-  setnumV(&e1->u.nval, numV(&o));
+  if (LJ_DUALNUM) {
+    int32_t k = lj_num2int(n);
+    if ((lua_Number)k == n) {
+      setintV(&e1->u.nval, k);
+      return 1;
+    }
+  }
+  setnumV(&e1->u.nval, n);
   return 1;
 }
 
@@ -900,9 +927,18 @@ static void bcemit_unop(FuncState *fs, BCOp op, ExpDesc *e)
 	return;
       } else
 #endif
-      if (expr_isnumk(e) && expr_numV(e) != 0) {  /* Avoid folding to -0. */
-	e->u.nval.u64 ^= U64x(80000000,00000000);
-	return;
+      if (expr_isnumk(e) && !expr_numiszero(e)) {  /* Avoid folding to -0. */
+	TValue *o = expr_numtv(e);
+	if (tvisint(o)) {
+	  int32_t k = intV(o);
+	  if (k == -k)
+	    setnumV(o, -(lua_Number)k);
+	  else
+	    setintV(o, -k);
+	} else {
+	  o->u64 ^= U64x(80000000,00000000);
+	  return;
+	}
       }
     }
     expr_toanyreg(fs, e);
@@ -986,7 +1022,7 @@ static void var_new(LexState *ls, BCReg n, GCstr *name)
 static void var_add(LexState *ls, BCReg nvars)
 {
   FuncState *fs = ls->fs;
-  fs->nactvar = cast_byte(fs->nactvar + nvars);
+  fs->nactvar = (uint8_t)(fs->nactvar + nvars);
   for (; nvars; nvars--)
     var_get(ls, fs, fs->nactvar - nvars).startpc = fs->pc;
 }
@@ -1094,16 +1130,33 @@ static void fs_fixup_k(FuncState *fs, GCproto *pt, void *kptr)
   kt = fs->kt;
   array = tvref(kt->array);
   for (i = 0; i < kt->asize; i++)
-    if (tvisnum(&array[i]))
-      ((lua_Number *)kptr)[array[i].u32.lo] = cast_num(i);
+    if (tvhaskslot(&array[i])) {
+      TValue *tv = &((TValue *)kptr)[tvkslot(&array[i])];
+      if (LJ_DUALNUM)
+	setintV(tv, (int32_t)i);
+      else
+	setnumV(tv, (lua_Number)i);
+    }
   node = noderef(kt->node);
   hmask = kt->hmask;
   for (i = 0; i <= hmask; i++) {
     Node *n = &node[i];
-    if (tvisnum(&n->val)) {
-      ptrdiff_t kidx = (ptrdiff_t)n->val.u32.lo;
+    if (tvhaskslot(&n->val)) {
+      ptrdiff_t kidx = (ptrdiff_t)tvkslot(&n->val);
+      lua_assert(!tvisint(&n->key));
       if (tvisnum(&n->key)) {
-	((lua_Number *)kptr)[kidx] = numV(&n->key);
+	TValue *tv = &((TValue *)kptr)[kidx];
+	if (LJ_DUALNUM) {
+	  lua_Number nn = numV(&n->key);
+	  int32_t k = lj_num2int(nn);
+	  lua_assert(!tvismzero(&n->key));
+	  if ((lua_Number)k == nn)
+	    setintV(tv, k);
+	  else
+	    *tv = n->key;
+	} else {
+	  *tv = n->key;
+	}
       } else {
 	GCobj *o = gcV(&n->key);
 	setgcref(((GCRef *)kptr)[~kidx], o);
@@ -1286,12 +1339,22 @@ static void expr_index(FuncState *fs, ExpDesc *t, ExpDesc *e)
   /* Already called: expr_toval(fs, e). */
   t->k = VINDEXED;
   if (expr_isnumk(e)) {
-    lua_Number n = expr_numV(e);
+#if LJ_DUALNUM
+    if (tvisint(expr_numtv(e))) {
+      int32_t k = intV(expr_numtv(e));
+      if (checku8(k)) {
+	t->u.s.aux = BCMAX_C+1+(uint32_t)k;  /* 256..511: const byte key */
+	return;
+      }
+    }
+#else
+    lua_Number n = expr_numberV(e);
     int32_t k = lj_num2int(n);
-    if (checku8(k) && n == cast_num(k)) {
+    if (checku8(k) && n == (lua_Number)k) {
       t->u.s.aux = BCMAX_C+1+(uint32_t)k;  /* 256..511: const byte key */
       return;
     }
+#endif
   } else if (expr_isstrk(e)) {
     BCReg idx = const_str(fs, e);
     if (idx <= BCMAX_C) {
@@ -1331,8 +1394,8 @@ static void expr_kvalue(TValue *v, ExpDesc *e)
     setgcref(v->gcr, obj2gco(e->u.sval));
     setitype(v, LJ_TSTR);
   } else {
-    lua_assert(e->k == VKNUM);
-    setnumV(v, expr_numV(e));
+    lua_assert(tvisnumber(expr_numtv(e)));
+    *v = *expr_numtv(e);
   }
 }
 
@@ -1357,7 +1420,7 @@ static void expr_table(LexState *ls, ExpDesc *e)
     if (ls->token == '[') {
       expr_bracket(ls, &key);  /* Already calls expr_toval. */
       if (!expr_isk(&key)) expr_index(fs, e, &key);
-      if (expr_isnumk(&key) && expr_numV(&key) == 0) needarr = 1; else nhash++;
+      if (expr_isnumk(&key) && expr_numiszero(&key)) needarr = 1; else nhash++;
       lex_check(ls, '=');
     } else if (ls->token == TK_name && lj_lex_lookahead(ls) == '=') {
       expr_str(ls, &key);
@@ -2119,10 +2182,10 @@ static int predict_next(LexState *ls, FuncState *fs, BCPos pc)
   case BC_GGET:
     /* There's no inverse index (yet), so lookup the strings. */
     o = lj_tab_getstr(fs->kt, lj_str_newlit(ls->L, "pairs"));
-    if (o && tvisnum(o) && o->u32.lo == bc_d(ins))
+    if (o && tvhaskslot(o) && tvkslot(o) == bc_d(ins))
       return 1;
     o = lj_tab_getstr(fs->kt, lj_str_newlit(ls->L, "next"));
-    if (o && tvisnum(o) && o->u32.lo == bc_d(ins))
+    if (o && tvhaskslot(o) && tvkslot(o) == bc_d(ins))
       return 1;
     return 0;
   default:
diff --git a/src/lj_snap.c b/src/lj_snap.c
index fa2a20e0..e04da81f 100644
--- a/src/lj_snap.c
+++ b/src/lj_snap.c
@@ -289,7 +289,7 @@ const BCIns *lj_snap_restore(jit_State *J, void *exptr)
 	Reg r = regsp_reg(rs);
 	lua_assert(ra_hasreg(r));
 	if (irt_isinteger(t)) {
-	  setintV(o, ex->gpr[r-RID_MIN_GPR]);
+	  setintV(o, (int32_t)ex->gpr[r-RID_MIN_GPR]);
 	} else if (irt_isnum(t)) {
 	  setnumV(o, ex->fpr[r-RID_MIN_FPR]);
 #if LJ_64
diff --git a/src/lj_str.c b/src/lj_str.c
index 20049ab3..dea02858 100644
--- a/src/lj_str.c
+++ b/src/lj_str.c
@@ -171,6 +171,14 @@ void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s)
 
 /* Convert string object to number. */
 int LJ_FASTCALL lj_str_tonum(GCstr *str, TValue *n)
+{
+  int ok = lj_str_numconv(strdata(str), n);
+  if (ok && tvisint(n))
+    setnumV(n, (lua_Number)intV(n));
+  return ok;
+}
+
+int LJ_FASTCALL lj_str_tonumber(GCstr *str, TValue *n)
 {
   return lj_str_numconv(strdata(str), n);
 }
@@ -178,7 +186,11 @@ int LJ_FASTCALL lj_str_tonum(GCstr *str, TValue *n)
 /* Convert string to number. */
 int LJ_FASTCALL lj_str_numconv(const char *s, TValue *n)
 {
+#if LJ_DUALNUM
+  int sign = 0;
+#else
   lua_Number sign = 1;
+#endif
   const uint8_t *p = (const uint8_t *)s;
   while (lj_char_isspace(*p)) p++;
   if (*p == '-') { p++; sign = -1; } else if (*p == '+') { p++; }
@@ -189,21 +201,34 @@ int LJ_FASTCALL lj_str_numconv(const char *s, TValue *n)
       if (!lj_char_isxdigit(*p))
 	return 0;  /* Don't accept '0x' without hex digits. */
       do {
-	if (k >= 0x10000000) goto parsedbl;
+	if (k >= 0x10000000u) goto parsedbl;
 	k = (k << 4) + (*p & 15u);
 	if (!lj_char_isdigit(*p)) k += 9;
 	p++;
       } while (lj_char_isxdigit(*p));
     } else {
       while ((uint32_t)(*p - '0') < 10) {
-	if (k >= 0x19999999) goto parsedbl;
+	if (LJ_UNLIKELY(k >= 429496729) && (k != 429496729 || *p > '5'))
+	  goto parsedbl;
 	k = k * 10u + (uint32_t)(*p++ - '0');
       }
     }
     while (LJ_UNLIKELY(lj_char_isspace(*p))) p++;
     if (LJ_LIKELY(*p == '\0')) {
-      setnumV(n, sign * cast_num(k));
+#if LJ_DUALNUM
+      if (!sign) {
+	if (k < 0x80000000u) {
+	  setintV(n, (int32_t)k);
+	  return 1;
+	}
+      } else if (k <= 0x80000000u) {
+	setintV(n, -(int32_t)k);
+	return 1;
+      }
+#else
+      setnumV(n, sign * (lua_Number)k);
       return 1;
+#endif
     }
   }
 parsedbl:
@@ -239,26 +264,36 @@ size_t LJ_FASTCALL lj_str_bufnum(char *s, cTValue *o)
   }
 }
 
+/* Print integer to buffer. Returns pointer to start. */
+char * LJ_FASTCALL lj_str_bufint(char *p, int32_t k)
+{
+  uint32_t u = (uint32_t)(k < 0 ? -k : k);
+  p += 1+10;
+  do { *--p = (char)('0' + u % 10); } while (u /= 10);
+  if (k < 0) *--p = '-';
+  return p;
+}
+
 /* Convert number to string. */
 GCstr * LJ_FASTCALL lj_str_fromnum(lua_State *L, const lua_Number *np)
 {
-  char buf[LUAI_MAXNUMBER2STR];
+  char buf[LJ_STR_NUMBUF];
   size_t len = lj_str_bufnum(buf, (TValue *)np);
   return lj_str_new(L, buf, len);
 }
 
-#if LJ_HASJIT
 /* Convert integer to string. */
 GCstr * LJ_FASTCALL lj_str_fromint(lua_State *L, int32_t k)
 {
   char s[1+10];
-  char *p = s+sizeof(s);
-  uint32_t i = (uint32_t)(k < 0 ? -k : k);
-  do { *--p = (char)('0' + i % 10); } while (i /= 10);
-  if (k < 0) *--p = '-';
+  char *p = lj_str_bufint(s, k);
   return lj_str_new(L, p, (size_t)(s+sizeof(s)-p));
 }
-#endif
+
+GCstr * LJ_FASTCALL lj_str_fromnumber(lua_State *L, cTValue *o)
+{
+  return tvisint(o) ? lj_str_fromint(L, intV(o)) : lj_str_fromnum(L, &o->n);
+}
 
 /* -- String formatting --------------------------------------------------- */
 
@@ -307,38 +342,34 @@ const char *lj_str_pushvf(lua_State *L, const char *fmt, va_list argp)
       addchar(L, sb, va_arg(argp, int));
       break;
     case 'd': {
-      char buff[1+10];
-      char *p = buff+sizeof(buff);
-      int32_t k = va_arg(argp, int32_t);
-      uint32_t i = (uint32_t)(k < 0 ? -k : k);
-      do { *--p = (char)('0' + i % 10); } while (i /= 10);
-      if (k < 0) *--p = '-';
-      addstr(L, sb, p, (MSize)(buff+sizeof(buff)-p));
+      char buf[LJ_STR_INTBUF];
+      char *p = lj_str_bufint(buf, va_arg(argp, int32_t));
+      addstr(L, sb, p, (MSize)(buf+LJ_STR_INTBUF-p));
       break;
       }
     case 'f': {
-      char buf[LUAI_MAXNUMBER2STR];
+      char buf[LJ_STR_NUMBUF];
       TValue tv;
       MSize len;
-      tv.n = cast_num(va_arg(argp, LUAI_UACNUMBER));
+      tv.n = (lua_Number)(va_arg(argp, LUAI_UACNUMBER));
       len = (MSize)lj_str_bufnum(buf, &tv);
       addstr(L, sb, buf, len);
       break;
       }
     case 'p': {
 #define FMTP_CHARS	(2*sizeof(ptrdiff_t))
-      char buff[2+FMTP_CHARS];
+      char buf[2+FMTP_CHARS];
       ptrdiff_t p = (ptrdiff_t)(va_arg(argp, void *));
       ptrdiff_t i, lasti = 2+FMTP_CHARS;
 #if LJ_64
       if ((p >> 32) == 0)  /* Shorten output for true 32 bit pointers. */
 	lasti = 2+2*4;
 #endif
-      buff[0] = '0';
-      buff[1] = 'x';
+      buf[0] = '0';
+      buf[1] = 'x';
       for (i = lasti-1; i >= 2; i--, p >>= 4)
-	buff[i] = "0123456789abcdef"[(p & 15)];
-      addstr(L, sb, buff, (MSize)lasti);
+	buf[i] = "0123456789abcdef"[(p & 15)];
+      addstr(L, sb, buf, (MSize)lasti);
       break;
       }
     case '%':
diff --git a/src/lj_str.h b/src/lj_str.h
index 26b932e0..406e2522 100644
--- a/src/lj_str.h
+++ b/src/lj_str.h
@@ -22,11 +22,15 @@ LJ_FUNC void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s);
 /* Type conversions. */
 LJ_FUNC int LJ_FASTCALL lj_str_numconv(const char *s, TValue *n);
 LJ_FUNC int LJ_FASTCALL lj_str_tonum(GCstr *str, TValue *n);
+LJ_FUNC int LJ_FASTCALL lj_str_tonumber(GCstr *str, TValue *n);
 LJ_FUNC size_t LJ_FASTCALL lj_str_bufnum(char *s, cTValue *o);
+LJ_FUNC char * LJ_FASTCALL lj_str_bufint(char *p, int32_t k);
 LJ_FUNCA GCstr * LJ_FASTCALL lj_str_fromnum(lua_State *L, const lua_Number *np);
-#if LJ_HASJIT
 LJ_FUNC GCstr * LJ_FASTCALL lj_str_fromint(lua_State *L, int32_t k);
-#endif
+LJ_FUNC GCstr * LJ_FASTCALL lj_str_fromnumber(lua_State *L, cTValue *o);
+
+#define LJ_STR_INTBUF		(1+10)
+#define LJ_STR_NUMBUF		LUAI_MAXNUMBER2STR
 
 /* String formatting. */
 LJ_FUNC const char *lj_str_pushvf(lua_State *L, const char *fmt, va_list argp);
diff --git a/src/lj_tab.c b/src/lj_tab.c
index d64a0426..c8e4efe1 100644
--- a/src/lj_tab.c
+++ b/src/lj_tab.c
@@ -34,6 +34,7 @@ static LJ_AINLINE Node *hashmask(const GCtab *t, uint32_t hash)
 /* Hash an arbitrary key and return its anchor position in the hash table. */
 static Node *hashkey(const GCtab *t, cTValue *key)
 {
+  lua_assert(!tvisint(key));
   if (tvisstr(key))
     return hashstr(t, strV(key));
   else if (tvisnum(key))
@@ -100,7 +101,7 @@ static GCtab *newtab(lua_State *L, uint32_t asize, uint32_t hbits)
     lua_assert((sizeof(GCtab) & 7) == 0);
     t = (GCtab *)lj_mem_newgco(L, sizetabcolo(asize));
     t->gct = ~LJ_TTAB;
-    t->nomm = cast_byte(~0);
+    t->nomm = (uint8_t)~0;
     t->colo = (int8_t)asize;
     setmref(t->array, (TValue *)((char *)t + sizeof(GCtab)));
     setgcrefnull(t->metatable);
@@ -110,7 +111,7 @@ static GCtab *newtab(lua_State *L, uint32_t asize, uint32_t hbits)
   } else {  /* Otherwise separately allocate the array part. */
     t = lj_mem_newobj(L, GCtab);
     t->gct = ~LJ_TTAB;
-    t->nomm = cast_byte(~0);
+    t->nomm = (uint8_t)~0;
     t->colo = 0;
     setmref(t->array, NULL);
     setgcrefnull(t->metatable);
@@ -275,10 +276,11 @@ static void resizetab(lua_State *L, GCtab *t, uint32_t asize, uint32_t hbits)
 
 static uint32_t countint(cTValue *key, uint32_t *bins)
 {
+  lua_assert(!tvisint(key));
   if (tvisnum(key)) {
     lua_Number nk = numV(key);
     int32_t k = lj_num2int(nk);
-    if ((uint32_t)k < LJ_MAX_ASIZE && nk == cast_num(k)) {
+    if ((uint32_t)k < LJ_MAX_ASIZE && nk == (lua_Number)k) {
       bins[(k > 2 ? lj_fls((uint32_t)(k-1)) : 0)]++;
       return 1;
     }
@@ -360,7 +362,7 @@ cTValue * LJ_FASTCALL lj_tab_getinth(GCtab *t, int32_t key)
 {
   TValue k;
   Node *n;
-  k.n = cast_num(key);
+  k.n = (lua_Number)key;
   n = hashnum(t, &k);
   do {
     if (tvisnum(&n->key) && n->key.n == k.n)
@@ -385,10 +387,14 @@ cTValue *lj_tab_get(lua_State *L, GCtab *t, cTValue *key)
     cTValue *tv = lj_tab_getstr(t, strV(key));
     if (tv)
       return tv;
+  } else if (tvisint(key)) {
+    cTValue *tv = lj_tab_getint(t, intV(key));
+    if (tv)
+      return tv;
   } else if (tvisnum(key)) {
     lua_Number nk = numV(key);
     int32_t k = lj_num2int(nk);
-    if (nk == cast_num(k)) {
+    if (nk == (lua_Number)k) {
       cTValue *tv = lj_tab_getint(t, k);
       if (tv)
 	return tv;
@@ -466,7 +472,7 @@ TValue *lj_tab_setinth(lua_State *L, GCtab *t, int32_t key)
 {
   TValue k;
   Node *n;
-  k.n = cast_num(key);
+  k.n = (lua_Number)key;
   n = hashnum(t, &k);
   do {
     if (tvisnum(&n->key) && n->key.n == k.n)
@@ -493,10 +499,12 @@ TValue *lj_tab_set(lua_State *L, GCtab *t, cTValue *key)
   t->nomm = 0;  /* Invalidate negative metamethod cache. */
   if (tvisstr(key)) {
     return lj_tab_setstr(L, t, strV(key));
+  } else if (tvisint(key)) {
+    return lj_tab_setint(L, t, intV(key));
   } else if (tvisnum(key)) {
     lua_Number nk = numV(key);
     int32_t k = lj_num2int(nk);
-    if (nk == cast_num(k))
+    if (nk == (lua_Number)k)
       return lj_tab_setint(L, t, k);
     if (tvisnan(key))
       lj_err_msg(L, LJ_ERR_NANIDX);
@@ -517,10 +525,17 @@ TValue *lj_tab_set(lua_State *L, GCtab *t, cTValue *key)
 /* Get the traversal index of a key. */
 static uint32_t keyindex(lua_State *L, GCtab *t, cTValue *key)
 {
-  if (tvisnum(key)) {
+  TValue tmp;
+  if (tvisint(key)) {
+    int32_t k = intV(key);
+    if ((uint32_t)k < t->asize)
+      return (uint32_t)k;  /* Array key indexes: [0..t->asize-1] */
+    setnumV(&tmp, (lua_Number)k);
+    key = &tmp;
+  } else if (tvisnum(key)) {
     lua_Number nk = numV(key);
     int32_t k = lj_num2int(nk);
-    if ((uint32_t)k < t->asize && nk == cast_num(k))
+    if ((uint32_t)k < t->asize && nk == (lua_Number)k)
       return (uint32_t)k;  /* Array key indexes: [0..t->asize-1] */
   }
   if (!tvisnil(key)) {
-- 
cgit v1.2.3-55-g6feb