]> git.ipfire.org Git - thirdparty/LuaJIT.git/commitdiff
Merge branch 'master' into v2.1
authorMike Pall <mike>
Wed, 21 Dec 2022 23:52:04 +0000 (00:52 +0100)
committerMike Pall <mike>
Wed, 21 Dec 2022 23:52:04 +0000 (00:52 +0100)
15 files changed:
1  2 
src/lib_base.c
src/lib_bit.c
src/lj_asm_mips.h
src/lj_carith.c
src/lj_cparse.c
src/lj_crecord.c
src/lj_ctype.c
src/lj_emit_arm.h
src/lj_emit_arm64.h
src/lj_obj.h
src/lj_opt_fold.c
src/lj_parse.c
src/lj_strfmt.c
src/lj_strscan.c
src/lj_vmmath.c

diff --cc src/lib_base.c
index 98ec67c7c656140d456afd3b46916c48052c25ec,6c96e8d51084658ae4673f5ba0ea3adcb1de0336..c59d54a2e130d57b56b8ac2e079a5498d45a6cc7
@@@ -295,23 -287,15 +295,23 @@@ LJLIB_ASM(tonumber)             LJLIB_REC(.
      unsigned long ul;
      if (base < 2 || base > 36)
        lj_err_arg(L, 2, LJ_ERR_BASERNG);
 -    ul = strtoul(p, &ep, base);
 -    if (p != ep) {
 -      while (lj_char_isspace((unsigned char)(*ep))) ep++;
 -      if (*ep == '\0') {
 -      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);
 +    while (lj_char_isspace((unsigned char)(*p))) p++;
 +    if (*p == '-') { p++; neg = 1; } else if (*p == '+') { p++; }
 +    if (lj_char_isalnum((unsigned char)(*p))) {
 +      ul = strtoul(p, &ep, base);
 +      if (p != ep) {
 +      while (lj_char_isspace((unsigned char)(*ep))) ep++;
 +      if (*ep == '\0') {
 +        if (LJ_DUALNUM && LJ_LIKELY(ul < 0x80000000u+neg)) {
-           if (neg) ul = (unsigned long)-(long)ul;
++          if (neg) ul = ~ul+1u;
 +          setintV(L->base-1-LJ_FR2, (int32_t)ul);
 +        } else {
 +          lua_Number n = (lua_Number)ul;
 +          if (neg) n = -n;
 +          setnumV(L->base-1-LJ_FR2, n);
 +        }
 +        return FFH_RES(1);
 +      }
        }
      }
    }
diff --cc src/lib_bit.c
index 38c0f578a5bb242853c5e20237b0ebdf61992b6e,b7988d701308275b34b2673c81bc90502d7dc568..e08cd883c5f7c7caca2b7286fde8d90bbd6b10ea
@@@ -142,29 -49,16 +142,30 @@@ LJLIB_ASM_(bit_bxor)             LJLIB_REC(bit_nar
  
  /* ------------------------------------------------------------------------ */
  
 -LJLIB_CF(bit_tohex)
 +LJLIB_CF(bit_tohex)           LJLIB_REC(.)
  {
 -  uint32_t b = (uint32_t)lj_lib_checkbit(L, 1);
 -  int32_t i, n = L->base+1 >= L->top ? 8 : lj_lib_checkbit(L, 2);
 -  const char *hexdigits = "0123456789abcdef";
 -  char buf[8];
 -  if (n < 0) { n = (int32_t)(~(uint32_t)n+1u); hexdigits = "0123456789ABCDEF"; }
 -  if ((uint32_t)n > 8) n = 8;
 -  for (i = n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
 -  lua_pushlstring(L, buf, (size_t)n);
 +#if LJ_HASFFI
 +  CTypeID id = 0, id2 = 0;
 +  uint64_t b = lj_carith_check64(L, 1, &id);
 +  int32_t n = L->base+1>=L->top ? (id ? 16 : 8) :
 +                                (int32_t)lj_carith_check64(L, 2, &id2);
 +#else
 +  uint32_t b = (uint32_t)bit_checkbit(L, 1);
 +  int32_t n = L->base+1>=L->top ? 8 : bit_checkbit(L, 2);
 +#endif
 +  SBuf *sb = lj_buf_tmp_(L);
 +  SFormat sf = (STRFMT_UINT|STRFMT_T_HEX);
-   if (n < 0) { n = -n; sf |= STRFMT_F_UPPER; }
++  if (n < 0) { n = (int32_t)(~(uint32_t)n+1u); sf |= STRFMT_F_UPPER; }
++  if ((uint32_t)n > 254) n = 254;
 +  sf |= ((SFormat)((n+1)&255) << STRFMT_SH_PREC);
 +#if LJ_HASFFI
 +  if (n < 16) b &= ((uint64_t)1 << 4*n)-1;
 +#else
 +  if (n < 8) b &= (1u << 4*n)-1;
 +#endif
 +  sb = lj_strfmt_putfxint(sb, sf, b);
 +  setstrV(L, L->top-1, lj_buf_str(L, sb));
 +  lj_gc_check(L);
    return 1;
  }
  
index 1686b40f699721ca5ac8658e82a6f1f25538bbbd,1d4c8a25a8af6affe0fe3d99c6490d9862432300..ac2d2662eb22d42341023ed6a63caa95d24dc36d
@@@ -1876,25 -1222,12 +1876,25 @@@ static void asm_neg(ASMState *as, IRIn
    }
  }
  
 +#if !LJ_SOFTFP
 +#define asm_abs(as, ir)               asm_fpunary(as, ir, MIPSI_ABS_D)
 +#elif LJ_64   /* && LJ_SOFTFP */
 +static void asm_abs(ASMState *as, IRIns *ir)
 +{
 +  Reg dest = ra_dest(as, ir, RSET_GPR);
 +  Reg left = ra_alloc1(as, ir->op1, RSET_GPR);
 +  emit_tsml(as, MIPSI_DEXTM, dest, left, 30, 0);
 +}
 +#endif
 +
  static void asm_arithov(ASMState *as, IRIns *ir)
  {
 +  /* TODO MIPSR6: bovc/bnvc. Caveat: no delay slot to load RID_TMP. */
    Reg right, left, tmp, dest = ra_dest(as, ir, RSET_GPR);
 +  lj_assertA(!irt_is64(ir->t), "bad usage");
    if (irref_isk(ir->op2)) {
      int k = IR(ir->op2)->i;
-     if (ir->o == IR_SUBOV) k = -k;
+     if (ir->o == IR_SUBOV) k = (int)(~(unsigned int)k+1u);
      if (checki16(k)) {  /* (dest < left) == (k >= 0 ? 1 : 0) */
        left = ra_alloc1(as, ir->op1, RSET_GPR);
        asm_guard(as, k >= 0 ? MIPSI_BNE : MIPSI_BEQ, RID_TMP, RID_ZERO);
diff --cc src/lj_carith.c
index 1a2a058f3cfef5280d67785bb0fee0bc50467601,231d7a8ad7f749cd2c9559fc5921ef1729dc8cd6..2e8504a9484fe7bc320725aba538400a0500b37d
@@@ -207,10 -205,8 +207,10 @@@ static int carith_int64(lua_State *L, C
        else
        *up = lj_carith_powu64(u0, u1);
        break;
-     case MM_unm: *up = (uint64_t)-(int64_t)u0; break;
+     case MM_unm: *up = ~u0+1u; break;
 -    default: lua_assert(0); break;
 +    default:
 +      lj_assertL(0, "bad metamethod %d", mm);
 +      break;
      }
      lj_gc_check(L);
      return 1;
diff --cc src/lj_cparse.c
Simple merge
index e0f581cadc2a50c8acca111357f2ade9db181328,2fcc6d1c9b1396d2d36c311c6ef14a2b3d9bb8c5..9c9b6ccb147ee7581044e93a7a43ce510fc0c336
@@@ -1761,139 -1654,7 +1761,140 @@@ void LJ_FASTCALL recff_ffi_xof(jit_Stat
  void LJ_FASTCALL recff_ffi_gc(jit_State *J, RecordFFData *rd)
  {
    argv2cdata(J, J->base[0], &rd->argv[0]);
 -  crec_finalizer(J, J->base[0], &rd->argv[1]);
 +  if (!J->base[1])
 +    lj_trace_err(J, LJ_TRERR_BADTYPE);
 +  crec_finalizer(J, J->base[0], J->base[1], &rd->argv[1]);
 +}
 +
 +/* -- 64 bit bit.* library functions -------------------------------------- */
 +
 +/* Determine bit operation type from argument type. */
 +static CTypeID crec_bit64_type(CTState *cts, cTValue *tv)
 +{
 +  if (tviscdata(tv)) {
 +    CType *ct = lj_ctype_rawref(cts, cdataV(tv)->ctypeid);
 +    if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
 +    if ((ct->info & (CTMASK_NUM|CTF_BOOL|CTF_FP|CTF_UNSIGNED)) ==
 +      CTINFO(CT_NUM, CTF_UNSIGNED) && ct->size == 8)
 +      return CTID_UINT64;  /* Use uint64_t, since it has the highest rank. */
 +    return CTID_INT64;  /* Otherwise use int64_t. */
 +  }
 +  return 0;  /* Use regular 32 bit ops. */
 +}
 +
 +void LJ_FASTCALL recff_bit64_tobit(jit_State *J, RecordFFData *rd)
 +{
 +  CTState *cts = ctype_ctsG(J2G(J));
 +  TRef tr = crec_ct_tv(J, ctype_get(cts, CTID_INT64), 0,
 +                     J->base[0], &rd->argv[0]);
 +  if (!tref_isinteger(tr))
 +    tr = emitconv(tr, IRT_INT, tref_type(tr), 0);
 +  J->base[0] = tr;
 +}
 +
 +int LJ_FASTCALL recff_bit64_unary(jit_State *J, RecordFFData *rd)
 +{
 +  CTState *cts = ctype_ctsG(J2G(J));
 +  CTypeID id = crec_bit64_type(cts, &rd->argv[0]);
 +  if (id) {
 +    TRef tr = crec_ct_tv(J, ctype_get(cts, id), 0, J->base[0], &rd->argv[0]);
 +    tr = emitir(IRT(rd->data, id-CTID_INT64+IRT_I64), tr, 0);
 +    J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr);
 +    return 1;
 +  }
 +  return 0;
 +}
 +
 +int LJ_FASTCALL recff_bit64_nary(jit_State *J, RecordFFData *rd)
 +{
 +  CTState *cts = ctype_ctsG(J2G(J));
 +  CTypeID id = 0;
 +  MSize i;
 +  for (i = 0; J->base[i] != 0; i++) {
 +    CTypeID aid = crec_bit64_type(cts, &rd->argv[i]);
 +    if (id < aid) id = aid;  /* Determine highest type rank of all arguments. */
 +  }
 +  if (id) {
 +    CType *ct = ctype_get(cts, id);
 +    uint32_t ot = IRT(rd->data, id-CTID_INT64+IRT_I64);
 +    TRef tr = crec_ct_tv(J, ct, 0, J->base[0], &rd->argv[0]);
 +    for (i = 1; J->base[i] != 0; i++) {
 +      TRef tr2 = crec_ct_tv(J, ct, 0, J->base[i], &rd->argv[i]);
 +      tr = emitir(ot, tr, tr2);
 +    }
 +    J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr);
 +    return 1;
 +  }
 +  return 0;
 +}
 +
 +int LJ_FASTCALL recff_bit64_shift(jit_State *J, RecordFFData *rd)
 +{
 +  CTState *cts = ctype_ctsG(J2G(J));
 +  CTypeID id;
 +  TRef tsh = 0;
 +  if (J->base[0] && tref_iscdata(J->base[1])) {
 +    tsh = crec_ct_tv(J, ctype_get(cts, CTID_INT64), 0,
 +                   J->base[1], &rd->argv[1]);
 +    if (!tref_isinteger(tsh))
 +      tsh = emitconv(tsh, IRT_INT, tref_type(tsh), 0);
 +    J->base[1] = tsh;
 +  }
 +  id = crec_bit64_type(cts, &rd->argv[0]);
 +  if (id) {
 +    TRef tr = crec_ct_tv(J, ctype_get(cts, id), 0, J->base[0], &rd->argv[0]);
 +    uint32_t op = rd->data;
 +    if (!tsh) tsh = lj_opt_narrow_tobit(J, J->base[1]);
 +    if (!(op < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) &&
 +      !tref_isk(tsh))
 +      tsh = emitir(IRTI(IR_BAND), tsh, lj_ir_kint(J, 63));
 +#ifdef LJ_TARGET_UNIFYROT
 +      if (op == (LJ_TARGET_UNIFYROT == 1 ? IR_BROR : IR_BROL)) {
 +      op = LJ_TARGET_UNIFYROT == 1 ? IR_BROL : IR_BROR;
 +      tsh = emitir(IRTI(IR_NEG), tsh, tsh);
 +      }
 +#endif
 +    tr = emitir(IRT(op, id-CTID_INT64+IRT_I64), tr, tsh);
 +    J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr);
 +    return 1;
 +  }
 +  return 0;
 +}
 +
 +TRef recff_bit64_tohex(jit_State *J, RecordFFData *rd, TRef hdr)
 +{
 +  CTState *cts = ctype_ctsG(J2G(J));
 +  CTypeID id = crec_bit64_type(cts, &rd->argv[0]);
 +  TRef tr, trsf = J->base[1];
 +  SFormat sf = (STRFMT_UINT|STRFMT_T_HEX);
 +  int32_t n;
 +  if (trsf) {
 +    CTypeID id2 = 0;
 +    n = (int32_t)lj_carith_check64(J->L, 2, &id2);
 +    if (id2)
 +      trsf = crec_ct_tv(J, ctype_get(cts, CTID_INT32), 0, trsf, &rd->argv[1]);
 +    else
 +      trsf = lj_opt_narrow_tobit(J, trsf);
 +    emitir(IRTGI(IR_EQ), trsf, lj_ir_kint(J, n));  /* Specialize to n. */
 +  } else {
 +    n = id ? 16 : 8;
 +  }
-   if (n < 0) { n = -n; sf |= STRFMT_F_UPPER; }
++  if (n < 0) { n = (int32_t)(~n+1u); sf |= STRFMT_F_UPPER; }
++  if ((uint32_t)n > 254) n = 254;
 +  sf |= ((SFormat)((n+1)&255) << STRFMT_SH_PREC);
 +  if (id) {
 +    tr = crec_ct_tv(J, ctype_get(cts, id), 0, J->base[0], &rd->argv[0]);
 +    if (n < 16)
 +      tr = emitir(IRT(IR_BAND, IRT_U64), tr,
 +                lj_ir_kint64(J, ((uint64_t)1 << 4*n)-1));
 +  } else {
 +    tr = lj_opt_narrow_tobit(J, J->base[0]);
 +    if (n < 8)
 +      tr = emitir(IRTI(IR_BAND), tr, lj_ir_kint(J, (int32_t)((1u << 4*n)-1)));
 +    tr = emitconv(tr, IRT_U64, IRT_INT, 0);  /* No sign-extension. */
 +    lj_needsplit(J);
 +  }
 +  return lj_ir_call(J, IRCALL_lj_strfmt_putfxint, hdr, lj_ir_kint(J, sf), tr);
  }
  
  /* -- Miscellaneous library functions ------------------------------------- */
diff --cc src/lj_ctype.c
Simple merge
Simple merge
index c4b4c147dc96203feecf9dda541fbbdfe8ee95ee,0000000000000000000000000000000000000000..0ddba4a3d62ca6567cffb21018601a12fc868872
mode 100644,000000..100644
--- /dev/null
@@@ -1,424 -1,0 +1,425 @@@
-   uint64_t k = (n < 0) ? -n : n;
-   uint32_t m = (n < 0) ? 0x40000000 : 0;
 +/*
 +** ARM64 instruction emitter.
 +** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h
 +**
 +** Contributed by Djordje Kovacevic and Stefan Pejic from RT-RK.com.
 +** Sponsored by Cisco Systems, Inc.
 +*/
 +
 +/* -- Constant encoding --------------------------------------------------- */
 +
 +static uint64_t get_k64val(ASMState *as, IRRef ref)
 +{
 +  IRIns *ir = IR(ref);
 +  if (ir->o == IR_KINT64) {
 +    return ir_kint64(ir)->u64;
 +  } else if (ir->o == IR_KGC) {
 +    return (uint64_t)ir_kgc(ir);
 +  } else if (ir->o == IR_KPTR || ir->o == IR_KKPTR) {
 +    return (uint64_t)ir_kptr(ir);
 +  } else {
 +    lj_assertA(ir->o == IR_KINT || ir->o == IR_KNULL,
 +             "bad 64 bit const IR op %d", ir->o);
 +    return ir->i;  /* Sign-extended. */
 +  }
 +}
 +
 +/* Encode constant in K12 format for data processing instructions. */
 +static uint32_t emit_isk12(int64_t n)
 +{
-       uint32_t k12 = emit_isk12(delta < 0 ? -delta : delta);
++  uint64_t k = n < 0 ? ~(uint64_t)n+1u : (uint64_t)n;
++  uint32_t m = n < 0 ? 0x40000000 : 0;
 +  if (k < 0x1000) {
 +    return A64I_K12|m|A64F_U12(k);
 +  } else if ((k & 0xfff000) == k) {
 +    return A64I_K12|m|0x400000|A64F_U12(k>>12);
 +  }
 +  return 0;
 +}
 +
 +#define emit_clz64(n) __builtin_clzll(n)
 +#define emit_ctz64(n) __builtin_ctzll(n)
 +
 +/* Encode constant in K13 format for logical data processing instructions. */
 +static uint32_t emit_isk13(uint64_t n, int is64)
 +{
 +  int inv = 0, w = 128, lz, tz;
 +  if (n & 1) { n = ~n; w = 64; inv = 1; }  /* Avoid wrap-around of ones. */
 +  if (!n) return 0;  /* Neither all-zero nor all-ones are allowed. */
 +  do {  /* Find the repeat width. */
 +    if (is64 && (uint32_t)(n^(n>>32))) break;
 +    n = (uint32_t)n;
 +    if (!n) return 0;  /* Ditto when passing n=0xffffffff and is64=0. */
 +    w = 32; if ((n^(n>>16)) & 0xffff) break;
 +    n = n & 0xffff; w = 16; if ((n^(n>>8)) & 0xff) break;
 +    n = n & 0xff; w = 8; if ((n^(n>>4)) & 0xf) break;
 +    n = n & 0xf; w = 4; if ((n^(n>>2)) & 0x3) break;
 +    n = n & 0x3; w = 2;
 +  } while (0);
 +  lz = emit_clz64(n);
 +  tz = emit_ctz64(n);
 +  if ((int64_t)(n << lz) >> (lz+tz) != -1ll) return 0; /* Non-contiguous? */
 +  if (inv)
 +    return A64I_K13 | (((lz-w) & 127) << 16) | (((lz+tz-w-1) & 63) << 10);
 +  else
 +    return A64I_K13 | ((w-tz) << 16) | (((63-lz-tz-w-w) & 63) << 10);
 +}
 +
 +static uint32_t emit_isfpk64(uint64_t n)
 +{
 +  uint64_t etop9 = ((n >> 54) & 0x1ff);
 +  if ((n << 16) == 0 && (etop9 == 0x100 || etop9 == 0x0ff)) {
 +    return (uint32_t)(((n >> 48) & 0x7f) | ((n >> 56) & 0x80));
 +  }
 +  return ~0u;
 +}
 +
 +/* -- Emit basic instructions --------------------------------------------- */
 +
 +static void emit_dnma(ASMState *as, A64Ins ai, Reg rd, Reg rn, Reg rm, Reg ra)
 +{
 +  *--as->mcp = ai | A64F_D(rd) | A64F_N(rn) | A64F_M(rm) | A64F_A(ra);
 +}
 +
 +static void emit_dnm(ASMState *as, A64Ins ai, Reg rd, Reg rn, Reg rm)
 +{
 +  *--as->mcp = ai | A64F_D(rd) | A64F_N(rn) | A64F_M(rm);
 +}
 +
 +static void emit_dm(ASMState *as, A64Ins ai, Reg rd, Reg rm)
 +{
 +  *--as->mcp = ai | A64F_D(rd) | A64F_M(rm);
 +}
 +
 +static void emit_dn(ASMState *as, A64Ins ai, Reg rd, Reg rn)
 +{
 +  *--as->mcp = ai | A64F_D(rd) | A64F_N(rn);
 +}
 +
 +static void emit_nm(ASMState *as, A64Ins ai, Reg rn, Reg rm)
 +{
 +  *--as->mcp = ai | A64F_N(rn) | A64F_M(rm);
 +}
 +
 +static void emit_d(ASMState *as, A64Ins ai, Reg rd)
 +{
 +  *--as->mcp = ai | A64F_D(rd);
 +}
 +
 +static void emit_n(ASMState *as, A64Ins ai, Reg rn)
 +{
 +  *--as->mcp = ai | A64F_N(rn);
 +}
 +
 +static int emit_checkofs(A64Ins ai, int64_t ofs)
 +{
 +  int scale = (ai >> 30) & 3;
 +  if (ofs < 0 || (ofs & ((1<<scale)-1))) {
 +    return (ofs >= -256 && ofs <= 255) ? -1 : 0;
 +  } else {
 +    return (ofs < (4096<<scale)) ? 1 : 0;
 +  }
 +}
 +
 +static void emit_lso(ASMState *as, A64Ins ai, Reg rd, Reg rn, int64_t ofs)
 +{
 +  int ot = emit_checkofs(ai, ofs), sc = (ai >> 30) & 3;
 +  lj_assertA(ot, "load/store offset %d out of range", ofs);
 +  /* Combine LDR/STR pairs to LDP/STP. */
 +  if ((sc == 2 || sc == 3) &&
 +      (!(ai & 0x400000) || rd != rn) &&
 +      as->mcp != as->mcloop) {
 +    uint32_t prev = *as->mcp & ~A64F_D(31);
 +    int ofsm = ofs - (1<<sc), ofsp = ofs + (1<<sc);
 +    A64Ins aip;
 +    if (prev == (ai | A64F_N(rn) | A64F_U12(ofsm>>sc)) ||
 +      prev == ((ai^A64I_LS_U) | A64F_N(rn) | A64F_S9(ofsm&0x1ff))) {
 +      aip = (A64F_A(rd) | A64F_D(*as->mcp & 31));
 +    } else if (prev == (ai | A64F_N(rn) | A64F_U12(ofsp>>sc)) ||
 +             prev == ((ai^A64I_LS_U) | A64F_N(rn) | A64F_S9(ofsp&0x1ff))) {
 +      aip = (A64F_D(rd) | A64F_A(*as->mcp & 31));
 +      ofsm = ofs;
 +    } else {
 +      goto nopair;
 +    }
 +    if (ofsm >= (int)((unsigned int)-64<<sc) && ofsm <= (63<<sc)) {
 +      *as->mcp = aip | A64F_N(rn) | ((ofsm >> sc) << 15) |
 +      (ai ^ ((ai == A64I_LDRx || ai == A64I_STRx) ? 0x50000000 : 0x90000000));
 +      return;
 +    }
 +  }
 +nopair:
 +  if (ot == 1)
 +    *--as->mcp = ai | A64F_D(rd) | A64F_N(rn) | A64F_U12(ofs >> sc);
 +  else
 +    *--as->mcp = (ai^A64I_LS_U) | A64F_D(rd) | A64F_N(rn) | A64F_S9(ofs & 0x1ff);
 +}
 +
 +/* -- Emit loads/stores --------------------------------------------------- */
 +
 +/* Prefer rematerialization of BASE/L from global_State over spills. */
 +#define emit_canremat(ref)    ((ref) <= ASMREF_L)
 +
 +/* Try to find an N-step delta relative to other consts with N < lim. */
 +static int emit_kdelta(ASMState *as, Reg rd, uint64_t k, int lim)
 +{
 +  RegSet work = (~as->freeset & RSET_GPR) | RID2RSET(RID_GL);
 +  if (lim <= 1) return 0;  /* Can't beat that. */
 +  while (work) {
 +    Reg r = rset_picktop(work);
 +    IRRef ref = regcost_ref(as->cost[r]);
 +    lj_assertA(r != rd, "dest reg %d not free", rd);
 +    if (ref < REF_TRUE) {
 +      uint64_t kx = ra_iskref(ref) ? (uint64_t)ra_krefk(as, ref) :
 +                                   get_k64val(as, ref);
 +      int64_t delta = (int64_t)(k - kx);
 +      if (delta == 0) {
 +      emit_dm(as, A64I_MOVx, rd, r);
 +      return 1;
 +      } else {
-                ofs < 0 ? -ofs : ofs, rset_exclude(RSET_GPR, r));
++      uint32_t k12 = emit_isk12(delta < 0 ? (int64_t)(~(uint64_t)delta+1u) : delta);
 +      if (k12) {
 +        emit_dn(as, (delta < 0 ? A64I_SUBx : A64I_ADDx)^k12, rd, r);
 +        return 1;
 +      }
 +      /* Do other ops or multi-step deltas pay off? Probably not.
 +      ** E.g. XOR rarely helps with pointer consts.
 +      */
 +      }
 +    }
 +    rset_clear(work, r);
 +  }
 +  return 0;  /* Failed. */
 +}
 +
 +static void emit_loadk(ASMState *as, Reg rd, uint64_t u64, int is64)
 +{
 +  int i, zeros = 0, ones = 0, neg;
 +  if (!is64) u64 = (int64_t)(int32_t)u64;  /* Sign-extend. */
 +  /* Count homogeneous 16 bit fragments. */
 +  for (i = 0; i < 4; i++) {
 +    uint64_t frag = (u64 >> i*16) & 0xffff;
 +    zeros += (frag == 0);
 +    ones += (frag == 0xffff);
 +  }
 +  neg = ones > zeros;  /* Use MOVN if it pays off. */
 +  if ((neg ? ones : zeros) < 3) {  /* Need 2+ ins. Try shorter K13 encoding. */
 +    uint32_t k13 = emit_isk13(u64, is64);
 +    if (k13) {
 +      emit_dn(as, (is64|A64I_ORRw)^k13, rd, RID_ZERO);
 +      return;
 +    }
 +  }
 +  if (!emit_kdelta(as, rd, u64, 4 - (neg ? ones : zeros))) {
 +    int shift = 0, lshift = 0;
 +    uint64_t n64 = neg ? ~u64 : u64;
 +    if (n64 != 0) {
 +      /* Find first/last fragment to be filled. */
 +      shift = (63-emit_clz64(n64)) & ~15;
 +      lshift = emit_ctz64(n64) & ~15;
 +    }
 +    /* MOVK requires the original value (u64). */
 +    while (shift > lshift) {
 +      uint32_t u16 = (u64 >> shift) & 0xffff;
 +      /* Skip fragments that are correctly filled by MOVN/MOVZ. */
 +      if (u16 != (neg ? 0xffff : 0))
 +      emit_d(as, is64 | A64I_MOVKw | A64F_U16(u16) | A64F_LSL16(shift), rd);
 +      shift -= 16;
 +    }
 +    /* But MOVN needs an inverted value (n64). */
 +    emit_d(as, (neg ? A64I_MOVNx : A64I_MOVZx) |
 +             A64F_U16((n64 >> lshift) & 0xffff) | A64F_LSL16(lshift), rd);
 +  }
 +}
 +
 +/* Load a 32 bit constant into a GPR. */
 +#define emit_loadi(as, rd, i) emit_loadk(as, rd, i, 0)
 +
 +/* Load a 64 bit constant into a GPR. */
 +#define emit_loadu64(as, rd, i)       emit_loadk(as, rd, i, A64I_X)
 +
 +#define emit_loada(as, r, addr)       emit_loadu64(as, (r), (uintptr_t)(addr))
 +
 +#define glofs(as, k) \
 +  ((intptr_t)((uintptr_t)(k) - (uintptr_t)&J2GG(as->J)->g))
 +#define mcpofs(as, k) \
 +  ((intptr_t)((uintptr_t)(k) - (uintptr_t)(as->mcp - 1)))
 +#define checkmcpofs(as, k) \
 +  (A64F_S_OK(mcpofs(as, k)>>2, 19))
 +
 +static Reg ra_allock(ASMState *as, intptr_t k, RegSet allow);
 +
 +/* Get/set from constant pointer. */
 +static void emit_lsptr(ASMState *as, A64Ins ai, Reg r, void *p)
 +{
 +  /* First, check if ip + offset is in range. */
 +  if ((ai & 0x00400000) && checkmcpofs(as, p)) {
 +    emit_d(as, A64I_LDRLx | A64F_S19(mcpofs(as, p)>>2), r);
 +  } else {
 +    Reg base = RID_GL;  /* Next, try GL + offset. */
 +    int64_t ofs = glofs(as, p);
 +    if (!emit_checkofs(ai, ofs)) {  /* Else split up into base reg + offset. */
 +      int64_t i64 = i64ptr(p);
 +      base = ra_allock(as, (i64 & ~0x7fffull), rset_exclude(RSET_GPR, r));
 +      ofs = i64 & 0x7fffull;
 +    }
 +    emit_lso(as, ai, r, base, ofs);
 +  }
 +}
 +
 +/* Load 64 bit IR constant into register. */
 +static void emit_loadk64(ASMState *as, Reg r, IRIns *ir)
 +{
 +  const uint64_t *k = &ir_k64(ir)->u64;
 +  int64_t ofs;
 +  if (r >= RID_MAX_GPR) {
 +    uint32_t fpk = emit_isfpk64(*k);
 +    if (fpk != ~0u) {
 +      emit_d(as, A64I_FMOV_DI | A64F_FP8(fpk), (r & 31));
 +      return;
 +    }
 +  }
 +  ofs = glofs(as, k);
 +  if (emit_checkofs(A64I_LDRx, ofs)) {
 +    emit_lso(as, r >= RID_MAX_GPR ? A64I_LDRd : A64I_LDRx,
 +           (r & 31), RID_GL, ofs);
 +  } else {
 +    if (r >= RID_MAX_GPR) {
 +      emit_dn(as, A64I_FMOV_D_R, (r & 31), RID_TMP);
 +      r = RID_TMP;
 +    }
 +    if (checkmcpofs(as, k))
 +      emit_d(as, A64I_LDRLx | A64F_S19(mcpofs(as, k)>>2), r);
 +    else
 +      emit_loadu64(as, r, *k);
 +  }
 +}
 +
 +/* Get/set global_State fields. */
 +#define emit_getgl(as, r, field) \
 +  emit_lsptr(as, A64I_LDRx, (r), (void *)&J2G(as->J)->field)
 +#define emit_setgl(as, r, field) \
 +  emit_lsptr(as, A64I_STRx, (r), (void *)&J2G(as->J)->field)
 +
 +/* Trace number is determined from pc of exit instruction. */
 +#define emit_setvmstate(as, i)        UNUSED(i)
 +
 +/* -- Emit control-flow instructions -------------------------------------- */
 +
 +/* Label for internal jumps. */
 +typedef MCode *MCLabel;
 +
 +/* Return label pointing to current PC. */
 +#define emit_label(as)                ((as)->mcp)
 +
 +static void emit_cond_branch(ASMState *as, A64CC cond, MCode *target)
 +{
 +  MCode *p = --as->mcp;
 +  ptrdiff_t delta = target - p;
 +  lj_assertA(A64F_S_OK(delta, 19), "branch target out of range");
 +  *p = A64I_BCC | A64F_S19(delta) | cond;
 +}
 +
 +static void emit_branch(ASMState *as, A64Ins ai, MCode *target)
 +{
 +  MCode *p = --as->mcp;
 +  ptrdiff_t delta = target - p;
 +  lj_assertA(A64F_S_OK(delta, 26), "branch target out of range");
 +  *p = ai | A64F_S26(delta);
 +}
 +
 +static void emit_tnb(ASMState *as, A64Ins ai, Reg r, uint32_t bit, MCode *target)
 +{
 +  MCode *p = --as->mcp;
 +  ptrdiff_t delta = target - p;
 +  lj_assertA(bit < 63, "bit number out of range");
 +  lj_assertA(A64F_S_OK(delta, 14), "branch target out of range");
 +  if (bit > 31) ai |= A64I_X;
 +  *p = ai | A64F_BIT(bit & 31) | A64F_S14(delta) | r;
 +}
 +
 +static void emit_cnb(ASMState *as, A64Ins ai, Reg r, MCode *target)
 +{
 +  MCode *p = --as->mcp;
 +  ptrdiff_t delta = target - p;
 +  lj_assertA(A64F_S_OK(delta, 19), "branch target out of range");
 +  *p = ai | A64F_S19(delta) | r;
 +}
 +
 +#define emit_jmp(as, target)  emit_branch(as, A64I_B, (target))
 +
 +static void emit_call(ASMState *as, void *target)
 +{
 +  MCode *p = --as->mcp;
 +  ptrdiff_t delta = (char *)target - (char *)p;
 +  if (A64F_S_OK(delta>>2, 26)) {
 +    *p = A64I_BL | A64F_S26(delta>>2);
 +  } else {  /* Target out of range: need indirect call. But don't use R0-R7. */
 +    Reg r = ra_allock(as, i64ptr(target),
 +                    RSET_RANGE(RID_X8, RID_MAX_GPR)-RSET_FIXED);
 +    *p = A64I_BLR | A64F_N(r);
 +  }
 +}
 +
 +/* -- Emit generic operations --------------------------------------------- */
 +
 +/* Generic move between two regs. */
 +static void emit_movrr(ASMState *as, IRIns *ir, Reg dst, Reg src)
 +{
 +  if (dst >= RID_MAX_GPR) {
 +    emit_dn(as, irt_isnum(ir->t) ? A64I_FMOV_D : A64I_FMOV_S,
 +          (dst & 31), (src & 31));
 +    return;
 +  }
 +  if (as->mcp != as->mcloop) {  /* Swap early registers for loads/stores. */
 +    MCode ins = *as->mcp, swp = (src^dst);
 +    if ((ins & 0xbf800000) == 0xb9000000) {
 +      if (!((ins ^ (dst << 5)) & 0x000003e0))
 +      *as->mcp = ins ^ (swp << 5);  /* Swap N in load/store. */
 +      if (!(ins & 0x00400000) && !((ins ^ dst) & 0x0000001f))
 +      *as->mcp = ins ^ swp;  /* Swap D in store. */
 +    }
 +  }
 +  emit_dm(as, A64I_MOVx, dst, src);
 +}
 +
 +/* Generic load of register with base and (small) offset address. */
 +static void emit_loadofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs)
 +{
 +  if (r >= RID_MAX_GPR)
 +    emit_lso(as, irt_isnum(ir->t) ? A64I_LDRd : A64I_LDRs, (r & 31), base, ofs);
 +  else
 +    emit_lso(as, irt_is64(ir->t) ? A64I_LDRx : A64I_LDRw, r, base, ofs);
 +}
 +
 +/* Generic store of register with base and (small) offset address. */
 +static void emit_storeofs(ASMState *as, IRIns *ir, Reg r, Reg base, int32_t ofs)
 +{
 +  if (r >= RID_MAX_GPR)
 +    emit_lso(as, irt_isnum(ir->t) ? A64I_STRd : A64I_STRs, (r & 31), base, ofs);
 +  else
 +    emit_lso(as, irt_is64(ir->t) ? A64I_STRx : A64I_STRw, r, base, ofs);
 +}
 +
 +/* Emit an arithmetic operation with a constant operand. */
 +static void emit_opk(ASMState *as, A64Ins ai, Reg dest, Reg src,
 +                   int32_t i, RegSet allow)
 +{
 +  uint32_t k = emit_isk12(i);
 +  if (k)
 +    emit_dn(as, ai^k, dest, src);
 +  else
 +    emit_dnm(as, ai, dest, src, ra_allock(as, i, allow));
 +}
 +
 +/* Add offset to pointer. */
 +static void emit_addptr(ASMState *as, Reg r, int32_t ofs)
 +{
 +  if (ofs)
 +    emit_opk(as, ofs < 0 ? A64I_SUBx : A64I_ADDx, r, r,
++               ofs < 0 ? (int32_t)(~(uint32_t)ofs+1u) : ofs,
++               rset_exclude(RSET_GPR, r));
 +}
 +
 +#define emit_spsub(as, ofs)   emit_addptr(as, RID_SP, -(ofs))
 +
diff --cc src/lj_obj.h
Simple merge
Simple merge
diff --cc src/lj_parse.c
Simple merge
diff --cc src/lj_strfmt.c
index 5c8082903d55c70f2dbfaab566d7659bb2904b2f,0000000000000000000000000000000000000000..71ee9f6281f9e2e362255c55632c72caa24b4076
mode 100644,000000..100644
--- /dev/null
@@@ -1,606 -1,0 +1,606 @@@
-   if (k < 0) { u = (uint32_t)-k; *p++ = '-'; }
 +/*
 +** String formatting.
 +** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h
 +*/
 +
 +#include <stdio.h>
 +
 +#define lj_strfmt_c
 +#define LUA_CORE
 +
 +#include "lj_obj.h"
 +#include "lj_err.h"
 +#include "lj_buf.h"
 +#include "lj_str.h"
 +#include "lj_meta.h"
 +#include "lj_state.h"
 +#include "lj_char.h"
 +#include "lj_strfmt.h"
 +#if LJ_HASFFI
 +#include "lj_ctype.h"
 +#endif
 +#include "lj_lib.h"
 +
 +/* -- Format parser ------------------------------------------------------- */
 +
 +static const uint8_t strfmt_map[('x'-'A')+1] = {
 +  STRFMT_A,0,0,0,STRFMT_E,STRFMT_F,STRFMT_G,0,0,0,0,0,0,
 +  0,0,0,0,0,0,0,0,0,0,STRFMT_X,0,0,
 +  0,0,0,0,0,0,
 +  STRFMT_A,0,STRFMT_C,STRFMT_D,STRFMT_E,STRFMT_F,STRFMT_G,0,STRFMT_I,0,0,0,0,
 +  0,STRFMT_O,STRFMT_P,STRFMT_Q,0,STRFMT_S,0,STRFMT_U,0,0,STRFMT_X
 +};
 +
 +SFormat LJ_FASTCALL lj_strfmt_parse(FormatState *fs)
 +{
 +  const uint8_t *p = fs->p, *e = fs->e;
 +  fs->str = (const char *)p;
 +  for (; p < e; p++) {
 +    if (*p == '%') {  /* Escape char? */
 +      if (p[1] == '%') {  /* '%%'? */
 +      fs->p = ++p+1;
 +      goto retlit;
 +      } else {
 +      SFormat sf = 0;
 +      uint32_t c;
 +      if (p != (const uint8_t *)fs->str)
 +        break;
 +      for (p++; (uint32_t)*p - ' ' <= (uint32_t)('0' - ' '); p++) {
 +        /* Parse flags. */
 +        if (*p == '-') sf |= STRFMT_F_LEFT;
 +        else if (*p == '+') sf |= STRFMT_F_PLUS;
 +        else if (*p == '0') sf |= STRFMT_F_ZERO;
 +        else if (*p == ' ') sf |= STRFMT_F_SPACE;
 +        else if (*p == '#') sf |= STRFMT_F_ALT;
 +        else break;
 +      }
 +      if ((uint32_t)*p - '0' < 10) {  /* Parse width. */
 +        uint32_t width = (uint32_t)*p++ - '0';
 +        if ((uint32_t)*p - '0' < 10)
 +          width = (uint32_t)*p++ - '0' + width*10;
 +        sf |= (width << STRFMT_SH_WIDTH);
 +      }
 +      if (*p == '.') {  /* Parse precision. */
 +        uint32_t prec = 0;
 +        p++;
 +        if ((uint32_t)*p - '0' < 10) {
 +          prec = (uint32_t)*p++ - '0';
 +          if ((uint32_t)*p - '0' < 10)
 +            prec = (uint32_t)*p++ - '0' + prec*10;
 +        }
 +        sf |= ((prec+1) << STRFMT_SH_PREC);
 +      }
 +      /* Parse conversion. */
 +      c = (uint32_t)*p - 'A';
 +      if (LJ_LIKELY(c <= (uint32_t)('x' - 'A'))) {
 +        uint32_t sx = strfmt_map[c];
 +        if (sx) {
 +          fs->p = p+1;
 +          return (sf | sx | ((c & 0x20) ? 0 : STRFMT_F_UPPER));
 +        }
 +      }
 +      /* Return error location. */
 +      if (*p >= 32) p++;
 +      fs->len = (MSize)(p - (const uint8_t *)fs->str);
 +      fs->p = fs->e;
 +      return STRFMT_ERR;
 +      }
 +    }
 +  }
 +  fs->p = p;
 +retlit:
 +  fs->len = (MSize)(p - (const uint8_t *)fs->str);
 +  return fs->len ? STRFMT_LIT : STRFMT_EOF;
 +}
 +
 +/* -- Raw conversions ----------------------------------------------------- */
 +
 +#define WINT_R(x, sh, sc) \
 +  { uint32_t d = (x*(((1<<sh)+sc-1)/sc))>>sh; x -= d*sc; *p++ = (char)('0'+d); }
 +
 +/* Write integer to buffer. */
 +char * LJ_FASTCALL lj_strfmt_wint(char *p, int32_t k)
 +{
 +  uint32_t u = (uint32_t)k;
-       k = (uint64_t)-(int64_t)k;
++  if (k < 0) { u = ~u+1u; *p++ = '-'; }
 +  if (u < 10000) {
 +    if (u < 10) goto dig1;
 +    if (u < 100) goto dig2;
 +    if (u < 1000) goto dig3;
 +  } else {
 +    uint32_t v = u / 10000; u -= v * 10000;
 +    if (v < 10000) {
 +      if (v < 10) goto dig5;
 +      if (v < 100) goto dig6;
 +      if (v < 1000) goto dig7;
 +    } else {
 +      uint32_t w = v / 10000; v -= w * 10000;
 +      if (w >= 10) WINT_R(w, 10, 10)
 +      *p++ = (char)('0'+w);
 +    }
 +    WINT_R(v, 23, 1000)
 +    dig7: WINT_R(v, 12, 100)
 +    dig6: WINT_R(v, 10, 10)
 +    dig5: *p++ = (char)('0'+v);
 +  }
 +  WINT_R(u, 23, 1000)
 +  dig3: WINT_R(u, 12, 100)
 +  dig2: WINT_R(u, 10, 10)
 +  dig1: *p++ = (char)('0'+u);
 +  return p;
 +}
 +#undef WINT_R
 +
 +/* Write pointer to buffer. */
 +char * LJ_FASTCALL lj_strfmt_wptr(char *p, const void *v)
 +{
 +  ptrdiff_t x = (ptrdiff_t)v;
 +  MSize i, n = STRFMT_MAXBUF_PTR;
 +  if (x == 0) {
 +    *p++ = 'N'; *p++ = 'U'; *p++ = 'L'; *p++ = 'L';
 +    return p;
 +  }
 +#if LJ_64
 +  /* Shorten output for 64 bit pointers. */
 +  n = 2+2*4+((x >> 32) ? 2+2*(lj_fls((uint32_t)(x >> 32))>>3) : 0);
 +#endif
 +  p[0] = '0';
 +  p[1] = 'x';
 +  for (i = n-1; i >= 2; i--, x >>= 4)
 +    p[i] = "0123456789abcdef"[(x & 15)];
 +  return p+n;
 +}
 +
 +/* Write ULEB128 to buffer. */
 +char * LJ_FASTCALL lj_strfmt_wuleb128(char *p, uint32_t v)
 +{
 +  for (; v >= 0x80; v >>= 7)
 +    *p++ = (char)((v & 0x7f) | 0x80);
 +  *p++ = (char)v;
 +  return p;
 +}
 +
 +/* Return string or write number to tmp buffer and return pointer to start. */
 +const char *lj_strfmt_wstrnum(lua_State *L, cTValue *o, MSize *lenp)
 +{
 +  SBuf *sb;
 +  if (tvisstr(o)) {
 +    *lenp = strV(o)->len;
 +    return strVdata(o);
 +  } else if (tvisbuf(o)) {
 +    SBufExt *sbx = bufV(o);
 +    *lenp = sbufxlen(sbx);
 +    return sbx->r;
 +  } else if (tvisint(o)) {
 +    sb = lj_strfmt_putint(lj_buf_tmp_(L), intV(o));
 +  } else if (tvisnum(o)) {
 +    sb = lj_strfmt_putfnum(lj_buf_tmp_(L), STRFMT_G14, o->n);
 +  } else {
 +    return NULL;
 +  }
 +  *lenp = sbuflen(sb);
 +  return sb->b;
 +}
 +
 +/* -- Unformatted conversions to buffer ----------------------------------- */
 +
 +/* Add integer to buffer. */
 +SBuf * LJ_FASTCALL lj_strfmt_putint(SBuf *sb, int32_t k)
 +{
 +  sb->w = lj_strfmt_wint(lj_buf_more(sb, STRFMT_MAXBUF_INT), k);
 +  return sb;
 +}
 +
 +#if LJ_HASJIT
 +/* Add number to buffer. */
 +SBuf * LJ_FASTCALL lj_strfmt_putnum(SBuf *sb, cTValue *o)
 +{
 +  return lj_strfmt_putfnum(sb, STRFMT_G14, o->n);
 +}
 +#endif
 +
 +SBuf * LJ_FASTCALL lj_strfmt_putptr(SBuf *sb, const void *v)
 +{
 +  sb->w = lj_strfmt_wptr(lj_buf_more(sb, STRFMT_MAXBUF_PTR), v);
 +  return sb;
 +}
 +
 +/* Add quoted string to buffer. */
 +static SBuf *strfmt_putquotedlen(SBuf *sb, const char *s, MSize len)
 +{
 +  lj_buf_putb(sb, '"');
 +  while (len--) {
 +    uint32_t c = (uint32_t)(uint8_t)*s++;
 +    char *w = lj_buf_more(sb, 4);
 +    if (c == '"' || c == '\\' || c == '\n') {
 +      *w++ = '\\';
 +    } else if (lj_char_iscntrl(c)) {  /* This can only be 0-31 or 127. */
 +      uint32_t d;
 +      *w++ = '\\';
 +      if (c >= 100 || lj_char_isdigit((uint8_t)*s)) {
 +      *w++ = (char)('0'+(c >= 100)); if (c >= 100) c -= 100;
 +      goto tens;
 +      } else if (c >= 10) {
 +      tens:
 +      d = (c * 205) >> 11; c -= d * 10; *w++ = (char)('0'+d);
 +      }
 +      c += '0';
 +    }
 +    *w++ = (char)c;
 +    sb->w = w;
 +  }
 +  lj_buf_putb(sb, '"');
 +  return sb;
 +}
 +
 +#if LJ_HASJIT
 +SBuf * LJ_FASTCALL lj_strfmt_putquoted(SBuf *sb, GCstr *str)
 +{
 +  return strfmt_putquotedlen(sb, strdata(str), str->len);
 +}
 +#endif
 +
 +/* -- Formatted conversions to buffer ------------------------------------- */
 +
 +/* Add formatted char to buffer. */
 +SBuf *lj_strfmt_putfchar(SBuf *sb, SFormat sf, int32_t c)
 +{
 +  MSize width = STRFMT_WIDTH(sf);
 +  char *w = lj_buf_more(sb, width > 1 ? width : 1);
 +  if ((sf & STRFMT_F_LEFT)) *w++ = (char)c;
 +  while (width-- > 1) *w++ = ' ';
 +  if (!(sf & STRFMT_F_LEFT)) *w++ = (char)c;
 +  sb->w = w;
 +  return sb;
 +}
 +
 +/* Add formatted string to buffer. */
 +static SBuf *strfmt_putfstrlen(SBuf *sb, SFormat sf, const char *s, MSize len)
 +{
 +  MSize width = STRFMT_WIDTH(sf);
 +  char *w;
 +  if (len > STRFMT_PREC(sf)) len = STRFMT_PREC(sf);
 +  w = lj_buf_more(sb, width > len ? width : len);
 +  if ((sf & STRFMT_F_LEFT)) w = lj_buf_wmem(w, s, len);
 +  while (width-- > len) *w++ = ' ';
 +  if (!(sf & STRFMT_F_LEFT)) w = lj_buf_wmem(w, s, len);
 +  sb->w = w;
 +  return sb;
 +}
 +
 +#if LJ_HASJIT
 +SBuf *lj_strfmt_putfstr(SBuf *sb, SFormat sf, GCstr *str)
 +{
 +  return strfmt_putfstrlen(sb, sf, strdata(str), str->len);
 +}
 +#endif
 +
 +/* Add formatted signed/unsigned integer to buffer. */
 +SBuf *lj_strfmt_putfxint(SBuf *sb, SFormat sf, uint64_t k)
 +{
 +  char buf[STRFMT_MAXBUF_XINT], *q = buf + sizeof(buf), *w;
 +#ifdef LUA_USE_ASSERT
 +  char *ws;
 +#endif
 +  MSize prefix = 0, len, prec, pprec, width, need;
 +
 +  /* Figure out signed prefixes. */
 +  if (STRFMT_TYPE(sf) == STRFMT_INT) {
 +    if ((int64_t)k < 0) {
++      k = ~k+1u;
 +      prefix = 256 + '-';
 +    } else if ((sf & STRFMT_F_PLUS)) {
 +      prefix = 256 + '+';
 +    } else if ((sf & STRFMT_F_SPACE)) {
 +      prefix = 256 + ' ';
 +    }
 +  }
 +
 +  /* Convert number and store to fixed-size buffer in reverse order. */
 +  prec = STRFMT_PREC(sf);
 +  if ((int32_t)prec >= 0) sf &= ~STRFMT_F_ZERO;
 +  if (k == 0) {  /* Special-case zero argument. */
 +    if (prec != 0 ||
 +      (sf & (STRFMT_T_OCT|STRFMT_F_ALT)) == (STRFMT_T_OCT|STRFMT_F_ALT))
 +      *--q = '0';
 +  } else if (!(sf & (STRFMT_T_HEX|STRFMT_T_OCT))) {  /* Decimal. */
 +    uint32_t k2;
 +    while ((k >> 32)) { *--q = (char)('0' + k % 10); k /= 10; }
 +    k2 = (uint32_t)k;
 +    do { *--q = (char)('0' + k2 % 10); k2 /= 10; } while (k2);
 +  } else if ((sf & STRFMT_T_HEX)) {  /* Hex. */
 +    const char *hexdig = (sf & STRFMT_F_UPPER) ? "0123456789ABCDEF" :
 +                                               "0123456789abcdef";
 +    do { *--q = hexdig[(k & 15)]; k >>= 4; } while (k);
 +    if ((sf & STRFMT_F_ALT)) prefix = 512 + ((sf & STRFMT_F_UPPER) ? 'X' : 'x');
 +  } else {  /* Octal. */
 +    do { *--q = (char)('0' + (uint32_t)(k & 7)); k >>= 3; } while (k);
 +    if ((sf & STRFMT_F_ALT)) *--q = '0';
 +  }
 +
 +  /* Calculate sizes. */
 +  len = (MSize)(buf + sizeof(buf) - q);
 +  if ((int32_t)len >= (int32_t)prec) prec = len;
 +  width = STRFMT_WIDTH(sf);
 +  pprec = prec + (prefix >> 8);
 +  need = width > pprec ? width : pprec;
 +  w = lj_buf_more(sb, need);
 +#ifdef LUA_USE_ASSERT
 +  ws = w;
 +#endif
 +
 +  /* Format number with leading/trailing whitespace and zeros. */
 +  if ((sf & (STRFMT_F_LEFT|STRFMT_F_ZERO)) == 0)
 +    while (width-- > pprec) *w++ = ' ';
 +  if (prefix) {
 +    if ((char)prefix >= 'X') *w++ = '0';
 +    *w++ = (char)prefix;
 +  }
 +  if ((sf & (STRFMT_F_LEFT|STRFMT_F_ZERO)) == STRFMT_F_ZERO)
 +    while (width-- > pprec) *w++ = '0';
 +  while (prec-- > len) *w++ = '0';
 +  while (q < buf + sizeof(buf)) *w++ = *q++;  /* Add number itself. */
 +  if ((sf & STRFMT_F_LEFT))
 +    while (width-- > pprec) *w++ = ' ';
 +
 +  lj_assertX(need == (MSize)(w - ws), "miscalculated format size");
 +  sb->w = w;
 +  return sb;
 +}
 +
 +/* Add number formatted as signed integer to buffer. */
 +SBuf *lj_strfmt_putfnum_int(SBuf *sb, SFormat sf, lua_Number n)
 +{
 +  int64_t k = (int64_t)n;
 +  if (checki32(k) && sf == STRFMT_INT)
 +    return lj_strfmt_putint(sb, (int32_t)k);  /* Shortcut for plain %d. */
 +  else
 +    return lj_strfmt_putfxint(sb, sf, (uint64_t)k);
 +}
 +
 +/* Add number formatted as unsigned integer to buffer. */
 +SBuf *lj_strfmt_putfnum_uint(SBuf *sb, SFormat sf, lua_Number n)
 +{
 +  int64_t k;
 +  if (n >= 9223372036854775808.0)
 +    k = (int64_t)(n - 18446744073709551616.0);
 +  else
 +    k = (int64_t)n;
 +  return lj_strfmt_putfxint(sb, sf, (uint64_t)k);
 +}
 +
 +/* Format stack arguments to buffer. */
 +int lj_strfmt_putarg(lua_State *L, SBuf *sb, int arg, int retry)
 +{
 +  int narg = (int)(L->top - L->base);
 +  GCstr *fmt = lj_lib_checkstr(L, arg);
 +  FormatState fs;
 +  SFormat sf;
 +  lj_strfmt_init(&fs, strdata(fmt), fmt->len);
 +  while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) {
 +    if (sf == STRFMT_LIT) {
 +      lj_buf_putmem(sb, fs.str, fs.len);
 +    } else if (sf == STRFMT_ERR) {
 +      lj_err_callerv(L, LJ_ERR_STRFMT,
 +                   strdata(lj_str_new(L, fs.str, fs.len)));
 +    } else {
 +      TValue *o = &L->base[arg++];
 +      if (arg > narg)
 +      lj_err_arg(L, arg, LJ_ERR_NOVAL);
 +      switch (STRFMT_TYPE(sf)) {
 +      case STRFMT_INT:
 +      if (tvisint(o)) {
 +        int32_t k = intV(o);
 +        if (sf == STRFMT_INT)
 +          lj_strfmt_putint(sb, k);  /* Shortcut for plain %d. */
 +        else
 +          lj_strfmt_putfxint(sb, sf, k);
 +        break;
 +      }
 +#if LJ_HASFFI
 +      if (tviscdata(o)) {
 +        GCcdata *cd = cdataV(o);
 +        if (cd->ctypeid == CTID_INT64 || cd->ctypeid == CTID_UINT64) {
 +          lj_strfmt_putfxint(sb, sf, *(uint64_t *)cdataptr(cd));
 +          break;
 +        }
 +      }
 +#endif
 +      lj_strfmt_putfnum_int(sb, sf, lj_lib_checknum(L, arg));
 +      break;
 +      case STRFMT_UINT:
 +      if (tvisint(o)) {
 +        lj_strfmt_putfxint(sb, sf, intV(o));
 +        break;
 +      }
 +#if LJ_HASFFI
 +      if (tviscdata(o)) {
 +        GCcdata *cd = cdataV(o);
 +        if (cd->ctypeid == CTID_INT64 || cd->ctypeid == CTID_UINT64) {
 +          lj_strfmt_putfxint(sb, sf, *(uint64_t *)cdataptr(cd));
 +          break;
 +        }
 +      }
 +#endif
 +      lj_strfmt_putfnum_uint(sb, sf, lj_lib_checknum(L, arg));
 +      break;
 +      case STRFMT_NUM:
 +      lj_strfmt_putfnum(sb, sf, lj_lib_checknum(L, arg));
 +      break;
 +      case STRFMT_STR: {
 +      MSize len;
 +      const char *s;
 +      cTValue *mo;
 +      if (LJ_UNLIKELY(!tvisstr(o) && !tvisbuf(o)) && retry >= 0 &&
 +          !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
 +        /* Call __tostring metamethod once. */
 +        copyTV(L, L->top++, mo);
 +        copyTV(L, L->top++, o);
 +        lua_call(L, 1, 1);
 +        o = &L->base[arg-1];  /* Stack may have been reallocated. */
 +        copyTV(L, o, --L->top);  /* Replace inline for retry. */
 +        if (retry < 2) {  /* Global buffer may have been overwritten. */
 +          retry = 1;
 +          break;
 +        }
 +      }
 +      if (LJ_LIKELY(tvisstr(o))) {
 +        len = strV(o)->len;
 +        s = strVdata(o);
 +#if LJ_HASBUFFER
 +      } else if (tvisbuf(o)) {
 +        SBufExt *sbx = bufV(o);
 +        if (sbx == (SBufExt *)sb) lj_err_arg(L, arg+1, LJ_ERR_BUFFER_SELF);
 +        len = sbufxlen(sbx);
 +        s = sbx->r;
 +#endif
 +      } else {
 +        GCstr *str = lj_strfmt_obj(L, o);
 +        len = str->len;
 +        s = strdata(str);
 +      }
 +      if ((sf & STRFMT_T_QUOTED))
 +        strfmt_putquotedlen(sb, s, len);  /* No formatting. */
 +      else
 +        strfmt_putfstrlen(sb, sf, s, len);
 +      break;
 +      }
 +      case STRFMT_CHAR:
 +      lj_strfmt_putfchar(sb, sf, lj_lib_checkint(L, arg));
 +      break;
 +      case STRFMT_PTR:  /* No formatting. */
 +      lj_strfmt_putptr(sb, lj_obj_ptr(G(L), o));
 +      break;
 +      default:
 +      lj_assertL(0, "bad string format type");
 +      break;
 +      }
 +    }
 +  }
 +  return retry;
 +}
 +
 +/* -- Conversions to strings ---------------------------------------------- */
 +
 +/* Convert integer to string. */
 +GCstr * LJ_FASTCALL lj_strfmt_int(lua_State *L, int32_t k)
 +{
 +  char buf[STRFMT_MAXBUF_INT];
 +  MSize len = (MSize)(lj_strfmt_wint(buf, k) - buf);
 +  return lj_str_new(L, buf, len);
 +}
 +
 +/* Convert integer or number to string. */
 +GCstr * LJ_FASTCALL lj_strfmt_number(lua_State *L, cTValue *o)
 +{
 +  return tvisint(o) ? lj_strfmt_int(L, intV(o)) : lj_strfmt_num(L, o);
 +}
 +
 +#if LJ_HASJIT
 +/* Convert char value to string. */
 +GCstr * LJ_FASTCALL lj_strfmt_char(lua_State *L, int c)
 +{
 +  char buf[1];
 +  buf[0] = c;
 +  return lj_str_new(L, buf, 1);
 +}
 +#endif
 +
 +/* Raw conversion of object to string. */
 +GCstr * LJ_FASTCALL lj_strfmt_obj(lua_State *L, cTValue *o)
 +{
 +  if (tvisstr(o)) {
 +    return strV(o);
 +  } else if (tvisnumber(o)) {
 +    return lj_strfmt_number(L, o);
 +  } else if (tvisnil(o)) {
 +    return lj_str_newlit(L, "nil");
 +  } else if (tvisfalse(o)) {
 +    return lj_str_newlit(L, "false");
 +  } else if (tvistrue(o)) {
 +    return lj_str_newlit(L, "true");
 +  } else {
 +    char buf[8+2+2+16], *p = buf;
 +    p = lj_buf_wmem(p, lj_typename(o), (MSize)strlen(lj_typename(o)));
 +    *p++ = ':'; *p++ = ' ';
 +    if (tvisfunc(o) && isffunc(funcV(o))) {
 +      p = lj_buf_wmem(p, "builtin#", 8);
 +      p = lj_strfmt_wint(p, funcV(o)->c.ffid);
 +    } else {
 +      p = lj_strfmt_wptr(p, lj_obj_ptr(G(L), o));
 +    }
 +    return lj_str_new(L, buf, (size_t)(p - buf));
 +  }
 +}
 +
 +/* -- Internal string formatting ------------------------------------------ */
 +
 +/*
 +** These functions are only used for lua_pushfstring(), lua_pushvfstring()
 +** and for internal string formatting (e.g. error messages). Caveat: unlike
 +** string.format(), only a limited subset of formats and flags are supported!
 +**
 +** LuaJIT has support for a couple more formats than Lua 5.1/5.2:
 +** - %d %u %o %x with full formatting, 32 bit integers only.
 +** - %f and other FP formats are really %.14g.
 +** - %s %c %p without formatting.
 +*/
 +
 +/* Push formatted message as a string object to Lua stack. va_list variant. */
 +const char *lj_strfmt_pushvf(lua_State *L, const char *fmt, va_list argp)
 +{
 +  SBuf *sb = lj_buf_tmp_(L);
 +  FormatState fs;
 +  SFormat sf;
 +  GCstr *str;
 +  lj_strfmt_init(&fs, fmt, (MSize)strlen(fmt));
 +  while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) {
 +    switch (STRFMT_TYPE(sf)) {
 +    case STRFMT_LIT:
 +      lj_buf_putmem(sb, fs.str, fs.len);
 +      break;
 +    case STRFMT_INT:
 +      lj_strfmt_putfxint(sb, sf, va_arg(argp, int32_t));
 +      break;
 +    case STRFMT_UINT:
 +      lj_strfmt_putfxint(sb, sf, va_arg(argp, uint32_t));
 +      break;
 +    case STRFMT_NUM:
 +      lj_strfmt_putfnum(sb, STRFMT_G14, va_arg(argp, lua_Number));
 +      break;
 +    case STRFMT_STR: {
 +      const char *s = va_arg(argp, char *);
 +      if (s == NULL) s = "(null)";
 +      lj_buf_putmem(sb, s, (MSize)strlen(s));
 +      break;
 +      }
 +    case STRFMT_CHAR:
 +      lj_buf_putb(sb, va_arg(argp, int));
 +      break;
 +    case STRFMT_PTR:
 +      lj_strfmt_putptr(sb, va_arg(argp, void *));
 +      break;
 +    case STRFMT_ERR:
 +    default:
 +      lj_buf_putb(sb, '?');
 +      lj_assertL(0, "bad string format near offset %d", fs.len);
 +      break;
 +    }
 +  }
 +  str = lj_buf_str(L, sb);
 +  setstrV(L, L->top, str);
 +  incr_top(L);
 +  return strdata(str);
 +}
 +
 +/* Push formatted message as a string object to Lua stack. Vararg variant. */
 +const char *lj_strfmt_pushf(lua_State *L, const char *fmt, ...)
 +{
 +  const char *msg;
 +  va_list argp;
 +  va_start(argp, fmt);
 +  msg = lj_strfmt_pushvf(L, fmt, argp);
 +  va_end(argp);
 +  return msg;
 +}
 +
index 1d1c1c7402482c0a9468eab047c261a3aa1c90ae,9e8023b5ac142490c5dc651e4127b12c0f74e05b..4ca848ec3501e2db7facbfd806a95fd46e2ef1a3
@@@ -328,55 -328,10 +328,55 @@@ static StrScanFmt strscan_dec(const uin
    return fmt;
  }
  
-       o->i = neg ? -(int32_t)x : (int32_t)x;
 +/* Parse binary number. */
 +static StrScanFmt strscan_bin(const uint8_t *p, TValue *o,
 +                            StrScanFmt fmt, uint32_t opt,
 +                            int32_t ex2, int32_t neg, uint32_t dig)
 +{
 +  uint64_t x = 0;
 +  uint32_t i;
 +
 +  if (ex2 || dig > 64) return STRSCAN_ERROR;
 +
 +  /* Scan binary digits. */
 +  for (i = dig; i; i--, p++) {
 +    if ((*p & ~1) != '0') return STRSCAN_ERROR;
 +    x = (x << 1) | (*p & 1);
 +  }
 +
 +  /* Format-specific handling. */
 +  switch (fmt) {
 +  case STRSCAN_INT:
 +    if (!(opt & STRSCAN_OPT_TONUM) && x < 0x80000000u+neg) {
-     o->i = neg ? -(int32_t)x : (int32_t)x;
++      o->i = neg ? (int32_t)(~x+1u) : (int32_t)x;
 +      return STRSCAN_INT;  /* Fast path for 32 bit integers. */
 +    }
 +    if (!(opt & STRSCAN_OPT_C)) { fmt = STRSCAN_NUM; break; }
 +    /* fallthrough */
 +  case STRSCAN_U32:
 +    if (dig > 32) return STRSCAN_ERROR;
-     o->u64 = neg ? (uint64_t)-(int64_t)x : x;
++    o->i = neg ? (int32_t)(~x+1u) : (int32_t)x;
 +    return STRSCAN_U32;
 +  case STRSCAN_I64:
 +  case STRSCAN_U64:
++    o->u64 = neg ? ~x+1u : x;
 +    return fmt;
 +  default:
 +    break;
 +  }
 +
 +  /* Reduce range, then convert to double. */
 +  if ((x & U64x(c0000000,0000000))) { x = (x >> 2) | (x & 3); ex2 += 2; }
 +  strscan_double(x, o, ex2, neg);
 +  return fmt;
 +}
 +
  /* Scan string containing a number. Returns format. Returns value in o. */
 -StrScanFmt lj_strscan_scan(const uint8_t *p, TValue *o, uint32_t opt)
 +StrScanFmt lj_strscan_scan(const uint8_t *p, MSize len, TValue *o,
 +                         uint32_t opt)
  {
    int32_t neg = 0;
 +  const uint8_t *pe = p + len;
  
    /* Remove leading space, parse sign and non-numbers. */
    if (LJ_UNLIKELY(!lj_char_isdigit(*p))) {
diff --cc src/lj_vmmath.c
index d0febd81dc33467b126b95f851f672c0d5938cec,6369bc6be38a8b0827e56aefe50b9ccfd08af57e..4fa79ae4f6845288137c6097a84ec9c05d8e90e4
@@@ -73,13 -65,12 +73,13 @@@ double lj_vm_foldarith(double x, doubl
  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. */
 +  /* This must be checked before using this function. */
 +  lj_assertX(b != 0, "modulo with zero divisor");
-   ua = a < 0 ? (uint32_t)-a : (uint32_t)a;
-   ub = b < 0 ? (uint32_t)-b : (uint32_t)b;
+   ua = a < 0 ? ~(uint32_t)a+1u : (uint32_t)a;
+   ub = b < 0 ? ~(uint32_t)b+1u : (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;
+   if (((int32_t)y^b) < 0) y = ~y+1u;
    return (int32_t)y;
  }
  #endif