From f253a9ac782bea8779ef2f27cf3fa4308b4899a5 Mon Sep 17 00:00:00 2001 From: Sasha Szpakowski Date: Thu, 23 Jul 2026 21:14:56 -0300 Subject: [PATCH] update luajit to LuaJIT/LuaJIT@2460b3f --- libs/LuaJIT/.relver | 2 +- libs/LuaJIT/doc/extensions.html | 30 +- libs/LuaJIT/dynasm/dasm_x86.lua | 2 +- libs/LuaJIT/src/Makefile | 2 +- libs/LuaJIT/src/Makefile.dep | 9 +- libs/LuaJIT/src/lib_os.c | 2 +- libs/LuaJIT/src/lj_arch.h | 2 +- libs/LuaJIT/src/lj_asm_arm64.h | 3 +- libs/LuaJIT/src/lj_asm_mips.h | 3 +- libs/LuaJIT/src/lj_asm_x86.h | 16 +- libs/LuaJIT/src/lj_bc.h | 11 +- libs/LuaJIT/src/lj_bcdump.h | 3 +- libs/LuaJIT/src/lj_bcwrite.c | 5 +- libs/LuaJIT/src/lj_carith.c | 31 ++ libs/LuaJIT/src/lj_carith.h | 1 + libs/LuaJIT/src/lj_ccall.c | 2 +- libs/LuaJIT/src/lj_ccallback.c | 4 + libs/LuaJIT/src/lj_crecord.c | 35 +- libs/LuaJIT/src/lj_crecord.h | 5 +- libs/LuaJIT/src/lj_dispatch.h | 1 + libs/LuaJIT/src/lj_errmsg.h | 4 + libs/LuaJIT/src/lj_ffrecord.c | 2 +- libs/LuaJIT/src/lj_ir.h | 2 +- libs/LuaJIT/src/lj_lex.c | 49 +- libs/LuaJIT/src/lj_lex.h | 12 +- libs/LuaJIT/src/lj_meta.c | 75 +++ libs/LuaJIT/src/lj_meta.h | 2 + libs/LuaJIT/src/lj_obj.h | 1 + libs/LuaJIT/src/lj_opt_fold.c | 2 +- libs/LuaJIT/src/lj_parse.c | 777 ++++++++++++++++++++++++-------- libs/LuaJIT/src/lj_profile.c | 2 +- libs/LuaJIT/src/lj_record.c | 40 ++ libs/LuaJIT/src/vm_arm.dasc | 75 +++ libs/LuaJIT/src/vm_arm64.dasc | 64 +++ libs/LuaJIT/src/vm_mips.dasc | 75 +++ libs/LuaJIT/src/vm_mips64.dasc | 77 ++++ libs/LuaJIT/src/vm_ppc.dasc | 100 ++++ libs/LuaJIT/src/vm_x64.dasc | 125 ++++- libs/LuaJIT/src/vm_x86.dasc | 132 +++++- 39 files changed, 1537 insertions(+), 248 deletions(-) diff --git a/libs/LuaJIT/.relver b/libs/LuaJIT/.relver index b5c311d4..2b51f040 100644 --- a/libs/LuaJIT/.relver +++ b/libs/LuaJIT/.relver @@ -1 +1 @@ -1781602682 +1784580905 diff --git a/libs/LuaJIT/doc/extensions.html b/libs/LuaJIT/doc/extensions.html index 16e88505..73edf11f 100644 --- a/libs/LuaJIT/doc/extensions.html +++ b/libs/LuaJIT/doc/extensions.html @@ -377,6 +377,7 @@ are allowed.
  • pairs() and ipairs() check for __pairs and __ipairs.
  • coroutine.running() returns two results.
  • +
  • string.find() returns nil for out-of-range position.
  • table.pack() and table.unpack() (same as unpack()).
  • io.write() and file:write() return file handle @@ -385,7 +386,6 @@ instead of true.
  • exit status.
  • debug.setmetatable() returns object.
  • debug.getuservalue() and debug.setuservalue().
  • -
  • Remove math.mod(), string.gfind().
  • package.searchers.
  • module() returns the module table.
  • @@ -411,6 +411,34 @@ LuaJIT supports some extensions from Lua 5.3: +

    Backported Syntax Extensions from LuaJIT 3.0

    +

    +LuaJIT 2.1 supports some +» syntax extensions backported from LuaJIT 3.0: +

    + +

    +Not backported are: +bit operator metamethods, +floor division operator //, +compound assignment metamethods, +named vararg parameter ...name. +

    +

    +Bytecode that uses a bit operator can only be loaded by LuaJIT 2.1.1784535649 or higher. +

    +

    C++ Exception Interoperability

    LuaJIT has built-in support for interoperating with C++ exceptions. diff --git a/libs/LuaJIT/dynasm/dasm_x86.lua b/libs/LuaJIT/dynasm/dasm_x86.lua index 0794e180..a24570e0 100644 --- a/libs/LuaJIT/dynasm/dasm_x86.lua +++ b/libs/LuaJIT/dynasm/dasm_x86.lua @@ -1409,7 +1409,7 @@ local map_op = { dppd_3 = "rmio:660F3A41rMU", dpps_3 = "rmio:660F3A40rMU", extractps_3 = "mri/do:660F3A17RmU|rri/qo:660F3A17RXmU", - insertps_3 = "rrio:660F3A41rMU|rxi/od:", + insertps_3 = "rrio:660F3A21rMU|rxi/od:", movntdqa_2 = "rxo:660F382ArM", mpsadbw_3 = "rmio:660F3A42rMU", packusdw_2 = "rmo:660F382BrM", diff --git a/libs/LuaJIT/src/Makefile b/libs/LuaJIT/src/Makefile index bac0341e..0b8c04f7 100644 --- a/libs/LuaJIT/src/Makefile +++ b/libs/LuaJIT/src/Makefile @@ -372,7 +372,7 @@ ifneq ($(HOST_SYS),$(TARGET_SYS)) HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX else ifeq (iOS,$(TARGET_SYS)) - HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX -DTARGET_OS_IPHONE=1 + HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX -DLUAJIT_TARGET_IPHONE=1 else HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER endif diff --git a/libs/LuaJIT/src/Makefile.dep b/libs/LuaJIT/src/Makefile.dep index e10a6b29..709107c2 100644 --- a/libs/LuaJIT/src/Makefile.dep +++ b/libs/LuaJIT/src/Makefile.dep @@ -149,7 +149,8 @@ lj_mcode.o: lj_mcode.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_dispatch.h lj_bc.h lj_traceerr.h lj_prng.h lj_vm.h lj_meta.o: lj_meta.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_meta.h lj_frame.h \ - lj_bc.h lj_vm.h lj_strscan.h lj_strfmt.h lj_lib.h + lj_bc.h lj_vm.h lj_strscan.h lj_strfmt.h lj_lib.h lj_ctype.h lj_cdata.h \ + lj_carith.h lj_obj.o: lj_obj.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_opt_dce.o: lj_opt_dce.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_ir.h lj_jit.h lj_iropt.h @@ -181,9 +182,9 @@ lj_profile.o: lj_profile.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_jit.h lj_ir.h lj_trace.h lj_traceerr.h lj_profile.h luajit.h lj_record.o: lj_record.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \ - lj_ctype.h lj_gc.h lj_ff.h lj_ffdef.h lj_debug.h lj_ir.h lj_jit.h \ - lj_ircall.h lj_iropt.h lj_trace.h lj_dispatch.h lj_traceerr.h \ - lj_record.h lj_ffrecord.h lj_snap.h lj_vm.h lj_prng.h + lj_ctype.h lj_gc.h lj_crecord.h lj_jit.h lj_ir.h lj_ffrecord.h lj_ff.h \ + lj_ffdef.h lj_debug.h lj_ircall.h lj_iropt.h lj_trace.h lj_dispatch.h \ + lj_traceerr.h lj_record.h lj_snap.h lj_vm.h lj_prng.h lj_serialize.o: lj_serialize.c lj_obj.h lua.h luaconf.h lj_def.h \ lj_arch.h lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_tab.h \ lj_udata.h lj_ctype.h lj_cdata.h lj_ir.h lj_serialize.h diff --git a/libs/LuaJIT/src/lib_os.c b/libs/LuaJIT/src/lib_os.c index bf174915..d91cb976 100644 --- a/libs/LuaJIT/src/lib_os.c +++ b/libs/LuaJIT/src/lib_os.c @@ -230,6 +230,7 @@ LJLIB_CF(os_date) LJLIB_CF(os_time) { time_t t; + errno = 0; if (lua_isnoneornil(L, 1)) { /* called without args? */ t = time(NULL); /* get current time */ } else { @@ -243,7 +244,6 @@ LJLIB_CF(os_time) ts.tm_mon = (int)((unsigned int)getfield(L, "month", -1) - 1u); ts.tm_year = (int)((unsigned int)getfield(L, "year", -1) - 1900u); ts.tm_isdst = getboolfield(L, "isdst"); - errno = 0; t = mktime(&ts); } if (t == (time_t)(-1) && errno != 0) diff --git a/libs/LuaJIT/src/lj_arch.h b/libs/LuaJIT/src/lj_arch.h index 3c4e3f9b..7b7907ef 100644 --- a/libs/LuaJIT/src/lj_arch.h +++ b/libs/LuaJIT/src/lj_arch.h @@ -127,7 +127,7 @@ #define LJ_TARGET_POSIX (LUAJIT_OS > LUAJIT_OS_WINDOWS) #define LJ_TARGET_DLOPEN LJ_TARGET_POSIX -#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || LUAJIT_TARGET_IPHONE #define LJ_TARGET_IOS 1 #else #define LJ_TARGET_IOS 0 diff --git a/libs/LuaJIT/src/lj_asm_arm64.h b/libs/LuaJIT/src/lj_asm_arm64.h index dfc5490f..d068b153 100644 --- a/libs/LuaJIT/src/lj_asm_arm64.h +++ b/libs/LuaJIT/src/lj_asm_arm64.h @@ -1549,7 +1549,8 @@ static void asm_bitshift(ASMState *as, IRIns *ir, A64Ins ai, A64Shift sh) int32_t shmask = irt_is64(ir->t) ? 63 : 31; if (irref_isk(ir->op2)) { /* Constant shifts. */ Reg left, dest = ra_dest(as, ir, RSET_GPR); - int32_t shift = (IR(ir->op2)->i & shmask); + IRIns *irr = IR(ir->op2); + int32_t shift = ((irr->o == IR_KINT ? irr->i : (int32_t)ir_kint64(irr)->u64) & shmask); IRIns *irl = IR(ir->op1); if (shmask == 63) ai += A64I_UBFMx - A64I_UBFMw; diff --git a/libs/LuaJIT/src/lj_asm_mips.h b/libs/LuaJIT/src/lj_asm_mips.h index a54af233..7565f442 100644 --- a/libs/LuaJIT/src/lj_asm_mips.h +++ b/libs/LuaJIT/src/lj_asm_mips.h @@ -2089,7 +2089,8 @@ static void asm_bitshift(ASMState *as, IRIns *ir, MIPSIns mi, MIPSIns mik) { Reg dest = ra_dest(as, ir, RSET_GPR); if (irref_isk(ir->op2)) { /* Constant shifts. */ - uint32_t shift = (uint32_t)IR(ir->op2)->i; + IRIns *irr = IR(ir->op2); + uint32_t shift = (uint32_t)(LJ_32 || irr->o == IR_KINT) ? (uint32_t)irr->i : (uint32_t)ir_kint64(irr)->u64; if (LJ_64 && irt_is64(ir->t)) mik |= (shift & 32) ? MIPSI_D32 : MIPSI_D; emit_dta(as, mik, dest, ra_hintalloc(as, ir->op1, dest, RSET_GPR), (shift & 31)); diff --git a/libs/LuaJIT/src/lj_asm_x86.h b/libs/LuaJIT/src/lj_asm_x86.h index 3d68baef..3c024092 100644 --- a/libs/LuaJIT/src/lj_asm_x86.h +++ b/libs/LuaJIT/src/lj_asm_x86.h @@ -1112,10 +1112,12 @@ static void asm_tvptr(ASMState *as, Reg dest, IRRef ref, MSize mode) } else { #if LJ_GC64 if (irref_isk(ref)) { + Reg tmp; TValue k; lj_ir_kvalue(as->J->L, &k, ir); - emit_movmroi(as, dest, 4, k.u32.hi); - emit_movmroi(as, dest, 0, k.u32.lo); + tmp = ra_scratch(as, rset_exclude(RSET_GPR, dest)); + emit_rmro(as, XO_MOVto, tmp|REX_64, dest, 0); + emit_loadu64(as, tmp, k.u64); } else { /* TODO: 64 bit store + 32 bit load-modify-store is suboptimal. */ Reg src = ra_alloc1(as, ref, rset_exclude(RSET_GPR, dest)); @@ -2318,9 +2320,10 @@ static void asm_bitshift(ASMState *as, IRIns *ir, x86Shift xs, x86Op xv) IRIns *irr = IR(rref); Reg dest; if (irref_isk(rref)) { /* Constant shifts. */ - int shift; + int32_t shift; dest = ra_dest(as, ir, RSET_GPR); - shift = irr->i & (irt_is64(ir->t) ? 63 : 31); + shift = (LJ_32 || irr->o == IR_KINT) ? irr->i : (int32_t)ir_kint64(irr)->u64; + shift &= (irt_is64(ir->t) ? 63 : 31); if (!xv && shift && (as->flags & JIT_F_BMI2)) { Reg left = asm_fuseloadm(as, ir->op1, RSET_GPR, irt_is64(ir->t)); if (left != dest) { /* BMI2 rotate right by constant. */ @@ -2786,8 +2789,9 @@ static void asm_stack_restore(ASMState *as, SnapShot *snap) emit_i32(as, -1); emit_rmro(as, XO_MOVmi, REX_64, RID_BASE, ofs); } else { - emit_movmroi(as, RID_BASE, ofs+4, k.u32.hi); - emit_movmroi(as, RID_BASE, ofs, k.u32.lo); + Reg tmp = ra_scratch(as, rset_exclude(RSET_GPR, RID_BASE)); + emit_rmro(as, XO_MOVto, tmp|REX_64, RID_BASE, ofs); + emit_loadu64(as, tmp, k.u64); } #else } else if (!irt_ispri(ir->t)) { diff --git a/libs/LuaJIT/src/lj_bc.h b/libs/LuaJIT/src/lj_bc.h index 54d529e3..cbadc9ab 100644 --- a/libs/LuaJIT/src/lj_bc.h +++ b/libs/LuaJIT/src/lj_bc.h @@ -98,7 +98,7 @@ _(UNM, dst, ___, var, unm) \ _(LEN, dst, ___, var, len) \ \ - /* Binary ops. ORDER OPR. VV last, POW must be next. */ \ + /* Binary ops. ORDER OPR. ORDER ARITH. VV last, POW must be next. */ \ _(ADDVN, dst, var, num, add) \ _(SUBVN, dst, var, num, sub) \ _(MULVN, dst, var, num, mul) \ @@ -186,6 +186,15 @@ \ _(JMP, rbase, ___, jump, ___) \ \ + /* Bit operators. ORDER OPR. ORDER BIT. */ \ + _(BNOT, dst, ___, var, ___) \ + _(BAND, dst, var, var, ___) \ + _(BOR, dst, var, var, ___) \ + _(BXOR, dst, var, var, ___) \ + _(BSHL, dst, var, var, ___) \ + _(BSHR, dst, var, var, ___) \ + _(BSAR, dst, var, var, ___) \ + \ /* Function headers. I/J = interp/JIT, F/V/C = fixarg/vararg/C func. */ \ _(FUNCF, rbase, ___, ___, ___) \ _(IFUNCF, rbase, ___, ___, ___) \ diff --git a/libs/LuaJIT/src/lj_bcdump.h b/libs/LuaJIT/src/lj_bcdump.h index 074ac0fd..3d2c95ac 100644 --- a/libs/LuaJIT/src/lj_bcdump.h +++ b/libs/LuaJIT/src/lj_bcdump.h @@ -43,8 +43,9 @@ #define BCDUMP_F_STRIP 0x02 #define BCDUMP_F_FFI 0x04 #define BCDUMP_F_FR2 0x08 +#define BCDUMP_F_BITOP 0x10 -#define BCDUMP_F_KNOWN (BCDUMP_F_FR2*2-1) +#define BCDUMP_F_KNOWN (BCDUMP_F_BITOP*2-1) #define BCDUMP_F_DETERMINISTIC 0x80000000 diff --git a/libs/LuaJIT/src/lj_bcwrite.c b/libs/LuaJIT/src/lj_bcwrite.c index a0230eff..fb557e58 100644 --- a/libs/LuaJIT/src/lj_bcwrite.c +++ b/libs/LuaJIT/src/lj_bcwrite.c @@ -340,7 +340,7 @@ static void bcwrite_proto(BCWriteCtx *ctx, GCproto *pt) p += 5; /* Leave room for final size. */ /* Write prototype header. */ - *p++ = (pt->flags & (PROTO_CHILD|PROTO_VARARG|PROTO_FFI)); + *p++ = (pt->flags & (PROTO_CHILD|PROTO_VARARG|PROTO_FFI|PROTO_BITOP)); *p++ = pt->numparams; *p++ = pt->framesize; *p++ = pt->sizeuv; @@ -397,7 +397,8 @@ static void bcwrite_header(BCWriteCtx *ctx) *p++ = BCDUMP_VERSION; *p++ = (ctx->flags & (BCDUMP_F_STRIP | BCDUMP_F_FR2)) + LJ_BE*BCDUMP_F_BE + - ((ctx->pt->flags & PROTO_FFI) ? BCDUMP_F_FFI : 0); + ((ctx->pt->flags & PROTO_FFI) ? BCDUMP_F_FFI : 0) + + ((ctx->pt->flags & PROTO_BITOP) ? BCDUMP_F_BITOP : 0); if (!(ctx->flags & BCDUMP_F_STRIP)) { p = lj_strfmt_wuleb128(p, len); p = lj_buf_wmem(p, name, len); diff --git a/libs/LuaJIT/src/lj_carith.c b/libs/LuaJIT/src/lj_carith.c index cb408fb8..10f46158 100644 --- a/libs/LuaJIT/src/lj_carith.c +++ b/libs/LuaJIT/src/lj_carith.c @@ -353,6 +353,37 @@ uint64_t lj_carith_check64(lua_State *L, int narg, CTypeID *id) } } +/* Check bit operator arguments. No coercion from strings. */ +uint64_t lj_carith_checkbit64(lua_State *L, cTValue *o, CTypeID *id) +{ + if (tviscdata(o)) { + CTState *cts = ctype_cts(L); + uint8_t *sp = (uint8_t *)cdataptr(cdataV(o)); + CTypeID sid = cdataV(o)->ctypeid; + CType *s = ctype_get(cts, sid); + uint64_t x; + if (ctype_isref(s->info)) { + sp = *(void **)sp; + sid = ctype_cid(s->info); + } + s = ctype_raw(cts, sid); + if (ctype_isenum(s->info)) s = ctype_child(cts, s); + if ((s->info & (CTMASK_NUM|CTF_BOOL|CTF_FP|CTF_UNSIGNED)) == + CTINFO(CT_NUM, CTF_UNSIGNED) && s->size == 8) + *id = CTID_UINT64; /* Use uint64_t, since it has the highest rank. */ + else if (!*id) + *id = CTID_INT64; /* Use int64_t, unless already set. */ + lj_cconv_ct_ct(cts, ctype_get(cts, *id), s, + (uint8_t *)&x, sp, 0); + return x; + } else if (LJ_LIKELY(tvisint(o))) { + return (uint64_t)intV(o); /* Sign-extended. */ + } else { + if (!tvisnum(o)) lj_err_optype(L, o, LJ_ERR_OPARITH); + return (uint64_t)lj_num2bit(numV(o)); /* Sign-extended. */ + } +} + /* -- 64 bit integer arithmetic helpers ----------------------------------- */ #if LJ_32 && LJ_HASJIT diff --git a/libs/LuaJIT/src/lj_carith.h b/libs/LuaJIT/src/lj_carith.h index 93fa41f2..700e4879 100644 --- a/libs/LuaJIT/src/lj_carith.h +++ b/libs/LuaJIT/src/lj_carith.h @@ -21,6 +21,7 @@ LJ_FUNC uint64_t lj_carith_ror64(uint64_t x, int32_t sh); #endif LJ_FUNC uint64_t lj_carith_shift64(uint64_t x, int32_t sh, int op); LJ_FUNC uint64_t lj_carith_check64(lua_State *L, int narg, CTypeID *id); +LJ_FUNC uint64_t lj_carith_checkbit64(lua_State *L, cTValue *o, CTypeID *id); #if LJ_32 && LJ_HASJIT LJ_FUNC int64_t lj_carith_mul64(int64_t x, int64_t k); diff --git a/libs/LuaJIT/src/lj_ccall.c b/libs/LuaJIT/src/lj_ccall.c index c89d9760..1aae0d40 100644 --- a/libs/LuaJIT/src/lj_ccall.c +++ b/libs/LuaJIT/src/lj_ccall.c @@ -1091,7 +1091,7 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, if (CCALL_ALIGN_STACKARG) { /* Align argument on stack. */ MSize align = (1u << ctype_align(ccall_struct_align(cts, d))) - 1; #if LJ_TARGET_ARM64 && LJ_TARGET_OSX - isva = ctype_isstruct(d->info); + isva |= ctype_isstruct(d->info); #endif if (rp || (CCALL_PACK_STACKARG && isva && align < CTSIZE_PTR-1)) align = CTSIZE_PTR-1; diff --git a/libs/LuaJIT/src/lj_ccallback.c b/libs/LuaJIT/src/lj_ccallback.c index 724dea60..2ed84507 100644 --- a/libs/LuaJIT/src/lj_ccallback.c +++ b/libs/LuaJIT/src/lj_ccallback.c @@ -704,6 +704,10 @@ static void callback_conv_result(CTState *cts, lua_State *L, TValue *o) if (ctr->size <= 4 && (LJ_ABI_SOFTFP || ctype_isinteger_or_bool(ctr->info))) *(int64_t *)dp = (int64_t)*(int32_t *)dp; +#elif LJ_TARGET_X64 + /* Always zero-extend results to 64 bits. */ + if (ctr->size <= 4 && ctype_isinteger_or_bool(ctr->info)) + *(uint64_t *)dp = (uint64_t)*(uint32_t *)dp; #endif #if LJ_TARGET_X86 if (ctype_isfp(ctr->info)) diff --git a/libs/LuaJIT/src/lj_crecord.c b/libs/LuaJIT/src/lj_crecord.c index 4be36eca..a8ea419a 100644 --- a/libs/LuaJIT/src/lj_crecord.c +++ b/libs/LuaJIT/src/lj_crecord.c @@ -1884,24 +1884,23 @@ int LJ_FASTCALL recff_bit64_nary(jit_State *J, RecordFFData *rd) return 0; } -int LJ_FASTCALL recff_bit64_shift(jit_State *J, RecordFFData *rd) +int recff_bit64_shift(jit_State *J, TRef *rb, TRef *rc, + TValue *rbv, TValue *rcv, IROp op) { CTState *cts = ctype_ctsG(J2G(J)); CTypeID id; TRef tsh = 0; - if (J->base[0] && tref_iscdata(J->base[1])) { - tsh = crec_bit64_arg(J, ctype_get(cts, CTID_INT64), - J->base[1], &rd->argv[1]); + if (*rb && tref_iscdata(*rc)) { + tsh = crec_bit64_arg(J, ctype_get(cts, CTID_INT64), *rc, rcv); if (LJ_32 && !tref_isinteger(tsh)) tsh = emitconv(tsh, IRT_INT, tref_type(tsh), 0); - J->base[1] = tsh; + *rc = tsh; } - id = crec_bit64_type(cts, &rd->argv[0]); + id = crec_bit64_type(cts, rbv); if (id) { - TRef tr = crec_bit64_arg(J, ctype_get(cts, id), J->base[0], &rd->argv[0]); - uint32_t op = rd->data; + TRef tr = crec_bit64_arg(J, ctype_get(cts, id), *rb, rbv); IRType t; - if (!tsh) tsh = lj_opt_narrow_tobit(J, J->base[1]); + if (!tsh) tsh = lj_opt_narrow_tobit(J, *rc); t = tref_isinteger(tsh) ? IRT_INT : tref_type(tsh); if (!(op < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) && !tref_isk(tsh)) @@ -1913,7 +1912,7 @@ int LJ_FASTCALL recff_bit64_shift(jit_State *J, RecordFFData *rd) } #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); + *rb = emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr); return 1; } return 0; @@ -1955,6 +1954,22 @@ TRef recff_bit64_tohex(jit_State *J, RecordFFData *rd, TRef hdr) return lj_ir_call(J, IRCALL_lj_strfmt_putfxint, hdr, lj_ir_kint(J, sf), tr); } +TRef recff_bit64_bitop(jit_State *J, TRef rb, TRef rc, + TValue *rbv, TValue *rcv, IROp op) +{ + CTState *cts = ctype_ctsG(J2G(J)); + CTypeID id = crec_bit64_type(cts, rbv); + CTypeID id2 = rcv ? crec_bit64_type(cts, rcv) : 0; + CType *ct; + TRef tr, tr2; + if (id < id2) id = id2; + ct = ctype_get(cts, id); + tr = crec_bit64_arg(J, ct, rb, rbv); + tr2 = rcv ? crec_bit64_arg(J, ct, rc, rcv) : 0; + tr = emitir(IRT(op, id-CTID_INT64+IRT_I64), tr, tr2); + return emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, id), tr); +} + /* -- Miscellaneous library functions ------------------------------------- */ void LJ_FASTCALL lj_crecord_tonumber(jit_State *J, RecordFFData *rd) diff --git a/libs/LuaJIT/src/lj_crecord.h b/libs/LuaJIT/src/lj_crecord.h index ad99b725..ad2d3e25 100644 --- a/libs/LuaJIT/src/lj_crecord.h +++ b/libs/LuaJIT/src/lj_crecord.h @@ -29,8 +29,11 @@ LJ_FUNC void LJ_FASTCALL recff_ffi_gc(jit_State *J, RecordFFData *rd); LJ_FUNC void LJ_FASTCALL recff_bit64_tobit(jit_State *J, RecordFFData *rd); LJ_FUNC int LJ_FASTCALL recff_bit64_unary(jit_State *J, RecordFFData *rd); LJ_FUNC int LJ_FASTCALL recff_bit64_nary(jit_State *J, RecordFFData *rd); -LJ_FUNC int LJ_FASTCALL recff_bit64_shift(jit_State *J, RecordFFData *rd); +LJ_FUNC int recff_bit64_shift(jit_State *J, TRef *rb, TRef *rc, + TValue *rbv, TValue *rcv, IROp op); LJ_FUNC TRef recff_bit64_tohex(jit_State *J, RecordFFData *rd, TRef hdr); +LJ_FUNC TRef recff_bit64_bitop(jit_State *J, TRef rb, TRef rc, + TValue *rbv, TValue *rcv, IROp op); LJ_FUNC void LJ_FASTCALL lj_crecord_tonumber(jit_State *J, RecordFFData *rd); LJ_FUNC TRef lj_crecord_loadiu64(jit_State *J, TRef tr, cTValue *o); diff --git a/libs/LuaJIT/src/lj_dispatch.h b/libs/LuaJIT/src/lj_dispatch.h index 9baf762b..755590e6 100644 --- a/libs/LuaJIT/src/lj_dispatch.h +++ b/libs/LuaJIT/src/lj_dispatch.h @@ -49,6 +49,7 @@ extern double __divdf3(double a, double b); _(lj_dispatch_profile) _(lj_err_throw) \ _(lj_ffh_coroutine_wrap_err) _(lj_func_closeuv) _(lj_func_newL_gc) \ _(lj_gc_barrieruv) _(lj_gc_step) _(lj_gc_step_fixtop) _(lj_meta_arith) \ + _(lj_meta_bitop) \ _(lj_meta_call) _(lj_meta_cat) _(lj_meta_comp) _(lj_meta_equal) \ _(lj_meta_for) _(lj_meta_istype) _(lj_meta_len) _(lj_meta_tget) \ _(lj_meta_tset) _(lj_state_growstack) _(lj_strfmt_number) \ diff --git a/libs/LuaJIT/src/lj_errmsg.h b/libs/LuaJIT/src/lj_errmsg.h index daf7fb6b..ad8581c9 100644 --- a/libs/LuaJIT/src/lj_errmsg.h +++ b/libs/LuaJIT/src/lj_errmsg.h @@ -140,9 +140,13 @@ ERRDEF(XDOTS, "cannot use " LUA_QL("...") " outside a vararg function") ERRDEF(XSYNTAX, "syntax error") ERRDEF(XFOR, LUA_QL("=") " or " LUA_QL("in") " expected") ERRDEF(XBREAK, "no loop to break") +ERRDEF(XCONT, "no loop to continue") ERRDEF(XLUNDEF, "undefined label " LUA_QS) ERRDEF(XLDUP, "duplicate label " LUA_QS) ERRDEF(XGSCOPE, " jumps into the scope of local " LUA_QS) +ERRDEF(XCSCOPE, " jumps into the scope of local " LUA_QS) +ERRDEF(XCONSTA, "attempt to assign to const variable " LUA_QS) +ERRDEF(XCONSTR, "attempt to re-declare const variable " LUA_QS) /* Bytecode reader errors. */ ERRDEF(BCFMT, "cannot load incompatible bytecode") diff --git a/libs/LuaJIT/src/lj_ffrecord.c b/libs/LuaJIT/src/lj_ffrecord.c index edbf13cc..4349f748 100644 --- a/libs/LuaJIT/src/lj_ffrecord.c +++ b/libs/LuaJIT/src/lj_ffrecord.c @@ -745,7 +745,7 @@ static void LJ_FASTCALL recff_bit_nary(jit_State *J, RecordFFData *rd) static void LJ_FASTCALL recff_bit_shift(jit_State *J, RecordFFData *rd) { #if LJ_HASFFI - if (recff_bit64_shift(J, rd)) + if (recff_bit64_shift(J, &J->base[0], &J->base[1], &rd->argv[0], &rd->argv[1], rd->data)) return; #endif { diff --git a/libs/LuaJIT/src/lj_ir.h b/libs/LuaJIT/src/lj_ir.h index b9f5e2c1..879196a5 100644 --- a/libs/LuaJIT/src/lj_ir.h +++ b/libs/LuaJIT/src/lj_ir.h @@ -53,7 +53,7 @@ _(KINT64, N , cst, ___) \ _(KSLOT, N , ref, lit) \ \ - /* Bit ops. */ \ + /* Bit ops. ORDER BIT */ \ _(BNOT, N , ref, ___) \ _(BSWAP, N , ref, ___) \ _(BAND, C , ref, ref) \ diff --git a/libs/LuaJIT/src/lj_lex.c b/libs/LuaJIT/src/lj_lex.c index a585cb58..4bbe1835 100644 --- a/libs/LuaJIT/src/lj_lex.c +++ b/libs/LuaJIT/src/lj_lex.c @@ -97,12 +97,18 @@ static void lex_number(LexState *ls, TValue *tv) StrScanFmt fmt; LexChar c, xp = 'e'; lj_assertLS(lj_char_isdigit(ls->c), "bad usage"); - if ((c = ls->c) == '0' && (lex_savenext(ls) | 0x20) == 'x') - xp = 'p'; + if ((c = ls->c) == '0') { + lex_save(ls, c); + do { c = lex_next(ls); } while (c == '_'); + if ((c | 0x20) == 'x') xp = 'p'; + } while (lj_char_isident(ls->c) || ls->c == '.' || ((ls->c == '-' || ls->c == '+') && (c | 0x20) == xp)) { - c = ls->c; - lex_savenext(ls); + if (LJ_LIKELY(ls->c != '_')) { + c = ls->c; + lex_save(ls, ls->c); + } + lex_next(ls); } lex_save(ls, '\0'); fmt = lj_strscan_scan((const uint8_t *)ls->sb.b, sbuflen(&ls->sb)-1, tv, @@ -321,7 +327,9 @@ static LexToken lex_scan(LexState *ls, TValue *tv) continue; case '-': lex_next(ls); - if (ls->c != '-') return '-'; + if (ls->c != '-') { + if (ls->c != '>') return '-'; else { lex_next(ls); return TK_arrow; } + } lex_next(ls); if (ls->c == '[') { /* Long comment "--[=*[...]=*]". */ int sep = lex_skipeq(ls); @@ -353,16 +361,41 @@ static LexToken lex_scan(LexState *ls, TValue *tv) if (ls->c != '=') return '='; else { lex_next(ls); return TK_eq; } case '<': lex_next(ls); - if (ls->c != '=') return '<'; else { lex_next(ls); return TK_le; } + if (ls->c == '=') { lex_next(ls); return TK_le; } + if (ls->c == '<') { lex_next(ls); return TK_shl; } + return '<'; case '>': lex_next(ls); - if (ls->c != '=') return '>'; else { lex_next(ls); return TK_ge; } + if (ls->c == '=') { lex_next(ls); return TK_ge; } + if (ls->c == '>') { lex_next(ls); return TK_shr; } + return '>'; case '~': lex_next(ls); - if (ls->c != '=') return '~'; else { lex_next(ls); return TK_ne; } + if (ls->c == '=') { lex_next(ls); return TK_ne; } + if (ls->c == '>') { + lex_next(ls); + if (ls->c != '>') lj_lex_error(ls, '~', LJ_ERR_XSYMBOL); + lex_next(ls); + return TK_sar; + } + return '~'; + case '!': + lex_next(ls); + if (ls->c != '=') return '!'; else { lex_next(ls); return TK_ne_; } case ':': lex_next(ls); if (ls->c != ':') return ':'; else { lex_next(ls); return TK_label; } + case '?': + lex_next(ls); + if (ls->c == '.') { lex_next(ls); return TK_nav; } + if (ls->c == '?') { lex_next(ls); return TK_coal; } + return '?'; + case '&': + lex_next(ls); + if (ls->c != '&') return '&'; else { lex_next(ls); return TK_and_; } + case '|': + lex_next(ls); + if (ls->c != '|') return '|'; else { lex_next(ls); return TK_or_; } case '"': case '\'': lex_string(ls, tv); diff --git a/libs/LuaJIT/src/lj_lex.h b/libs/LuaJIT/src/lj_lex.h index 8d5c9a33..e5b78337 100644 --- a/libs/LuaJIT/src/lj_lex.h +++ b/libs/LuaJIT/src/lj_lex.h @@ -13,10 +13,12 @@ /* Lua lexer tokens. */ #define TKDEF(_, __) \ - _(and) _(break) _(do) _(else) _(elseif) _(end) _(false) \ + _(and) _(break) _(const) _(continue) _(do) _(else) _(elseif) _(end) _(false) \ _(for) _(function) _(goto) _(if) _(in) _(local) _(nil) _(not) _(or) \ _(repeat) _(return) _(then) _(true) _(until) _(while) \ __(concat, ..) __(dots, ...) __(eq, ==) __(ge, >=) __(le, <=) __(ne, ~=) \ + __(nav, ?.) __(coal, \?\?) __(shl, <<) __(shr, >>) __(sar, ~>>) \ + __(and_, &&) __(or_, ||) __(ne_, !=) __(arrow, ->) \ __(label, ::) __(number, ) __(name, ) __(string, ) \ __(eof, ) @@ -39,6 +41,12 @@ typedef struct BCInsLine { BCLine line; /* Line number for this bytecode. */ } BCInsLine; +/* Index into variable stack. */ +typedef uint16_t VarIndex; + +#define LJ_VINDEX_HSIZE 32 /* Hash table size. Must be a power of 2. */ +#define LJ_VINDEX_MASK (LJ_VINDEX_HSIZE-1) + /* Info for local variables. Only used during bytecode generation. */ typedef struct VarInfo { GCRef name; /* Local variable name or goto/label name. */ @@ -46,6 +54,7 @@ typedef struct VarInfo { BCPos endpc; /* First point where the local variable is dead. */ uint8_t slot; /* Variable slot. */ uint8_t info; /* Variable/goto/label info. */ + VarIndex prev; /* Previous entry in variable hash chain. */ } VarInfo; /* Lua lexer state. */ @@ -75,6 +84,7 @@ typedef struct LexState { uint32_t level; /* Syntactical nesting level. */ int endmark; /* Trust bytecode end marker, even if not at EOF. */ int fr2; /* Generate bytecode for LJ_FR2 mode. */ + VarIndex vhash[LJ_VINDEX_HSIZE]; /* Variable hash chain anchors. */ } LexState; LJ_FUNC int lj_lex_setup(lua_State *L, LexState *ls); diff --git a/libs/LuaJIT/src/lj_meta.c b/libs/LuaJIT/src/lj_meta.c index 1e7262a4..ddb37a1a 100644 --- a/libs/LuaJIT/src/lj_meta.c +++ b/libs/LuaJIT/src/lj_meta.c @@ -22,6 +22,11 @@ #include "lj_strscan.h" #include "lj_strfmt.h" #include "lj_lib.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_cdata.h" +#include "lj_carith.h" +#endif /* -- Metamethod handling ------------------------------------------------- */ @@ -234,6 +239,76 @@ TValue *lj_meta_arith(lua_State *L, TValue *ra, cTValue *rb, cTValue *rc, } } +/* Helper for bit operators. No bitop metamethods in v2.1. */ +void lj_meta_bitop(lua_State *L, TValue *ra, cTValue *rb, cTValue *rc, BCReg op) +{ +#if LJ_HASFFI + CTypeID id = 0, id_ignore = 0; + uint64_t b = lj_carith_checkbit64(L, rb, &id); + uint64_t c = lj_carith_checkbit64(L, rc, op >= BC_BSHL ? &id_ignore : &id); + if (id) { + if (tvisnum(rb)) { + b = id == CTID_UINT64 ? lj_num2u64(numV(rb)) : lj_num2i64(numV(rb)); + } + if (tvisnum(rc)) { + c = id == CTID_UINT64 ? lj_num2u64(numV(rc)) : lj_num2i64(numV(rc)); + } + } + switch (op) { + case BC_BNOT: b = ~b; break; + case BC_BAND: b &= c; break; + case BC_BOR: b |= c; break; + case BC_BXOR: b ^= c; break; + default: + if (id) { + b = lj_carith_shift64(b, (int32_t)c, op-BC_BSHL); + } else if (op == BC_BSHL) { + b = (uint64_t)((uint32_t)b << ((uint32_t)c & 31)); + } else if (op == BC_BSHR) { + b = (uint64_t)((uint32_t)b >> ((uint32_t)c & 31)); + } else { + lj_assertL(op == BC_BSAR, "bad bytecode op %d", op); + b = (uint64_t)(uint32_t)((int32_t)b >> ((uint32_t)c & 31)); + } + break; + } + if (id) { + GCcdata *cd = lj_cdata_new_(L, id, 8); + *(uint64_t *)cdataptr(cd) = b; + setcdataV(L, ra, cd); + } else { + setintV(ra, (int32_t)b); + } +#else +#if LJ_DUALNUM + uint32_t b = 0, c = 0; + if (tvisint(rb)) b = (uint32_t)intV(rb); + else if (tvisnum(rb)) b = (uint32_t)lj_num2bit(numV(rb)); + else goto err; + if (tvisint(rc)) c = (uint32_t)intV(rc); + else if (tvisnum(rc)) c = (uint32_t)lj_num2bit(numV(rc)); + else goto err; + switch (op) { + case BC_BNOT: b = ~b; break; + case BC_BAND: b &= c; break; + case BC_BOR: b |= c; break; + case BC_BXOR: b ^= c; break; + case BC_BSHL: b <<= (c & 31); break; + case BC_BSHR: b >>= (c & 31); break; + case BC_BSAR: b = (uint32_t)((int32_t)b >> (c & 31)); break; + default: + lj_assertL(0, "bad bytecode op %d", op); + break; + } + setintV(ra, (int32_t)b); + return; +err: +#endif + UNUSED(ra); UNUSED(op); + lj_err_optype(L, tvisnumber(rb) ? rc : rb, LJ_ERR_OPARITH); +#endif +} + /* Helper for CAT. Coercion, iterative concat, __concat metamethod. */ TValue *lj_meta_cat(lua_State *L, TValue *top, int left) { diff --git a/libs/LuaJIT/src/lj_meta.h b/libs/LuaJIT/src/lj_meta.h index 3d6a71c0..28e6c8bb 100644 --- a/libs/LuaJIT/src/lj_meta.h +++ b/libs/LuaJIT/src/lj_meta.h @@ -26,6 +26,8 @@ LJ_FUNCA cTValue *lj_meta_tget(lua_State *L, cTValue *o, cTValue *k); LJ_FUNCA TValue *lj_meta_tset(lua_State *L, cTValue *o, cTValue *k); LJ_FUNCA TValue *lj_meta_arith(lua_State *L, TValue *ra, cTValue *rb, cTValue *rc, BCReg op); +LJ_FUNCA void lj_meta_bitop(lua_State *L, TValue *ra, cTValue *rb, + cTValue *rc, BCReg op); LJ_FUNCA TValue *lj_meta_cat(lua_State *L, TValue *top, int left); LJ_FUNCA TValue * LJ_FASTCALL lj_meta_len(lua_State *L, cTValue *o); LJ_FUNCA TValue *lj_meta_equal(lua_State *L, GCobj *o1, GCobj *o2, int ne); diff --git a/libs/LuaJIT/src/lj_obj.h b/libs/LuaJIT/src/lj_obj.h index f380c78f..96dc1e0d 100644 --- a/libs/LuaJIT/src/lj_obj.h +++ b/libs/LuaJIT/src/lj_obj.h @@ -401,6 +401,7 @@ typedef struct GCproto { #define PROTO_FFI 0x04 /* Uses BC_KCDATA for FFI datatypes. */ #define PROTO_NOJIT 0x08 /* JIT disabled for this function. */ #define PROTO_ILOOP 0x10 /* Patched bytecode with ILOOP etc. */ +#define PROTO_BITOP 0x80 /* Uses bit operator bytecodes. */ /* Only used during parsing. */ #define PROTO_HAS_RETURN 0x20 /* Already emitted a return. */ #define PROTO_FIXUP_RETURN 0x40 /* Need to fixup emitted returns. */ diff --git a/libs/LuaJIT/src/lj_opt_fold.c b/libs/LuaJIT/src/lj_opt_fold.c index 7fc83837..cc2b9098 100644 --- a/libs/LuaJIT/src/lj_opt_fold.c +++ b/libs/LuaJIT/src/lj_opt_fold.c @@ -382,8 +382,8 @@ static uint64_t kfold_int64arith(jit_State *J, uint64_t k1, uint64_t k2, case IR_BSAR: k1 = (uint64_t)((int64_t)k1 >> (k2 & 63)); break; case IR_BROL: k1 = lj_rol(k1, (k2 & 63)); break; case IR_BROR: k1 = lj_ror(k1, (k2 & 63)); break; - default: lj_assertJ(0, "bad IR op %d", op); break; #endif + default: lj_assertJ(0, "bad IR op %d", op); break; } return k1; } diff --git a/libs/LuaJIT/src/lj_parse.c b/libs/LuaJIT/src/lj_parse.c index 66e5a034..00a64382 100644 --- a/libs/LuaJIT/src/lj_parse.c +++ b/libs/LuaJIT/src/lj_parse.c @@ -49,6 +49,7 @@ typedef enum { VRELOCABLE, /* info = instruction PC */ VNONRELOC, /* info = result register */ VCALL, /* info = instruction PC, aux = base */ + VCALLNAV, /* info = instruction PC, aux = base */ VVOID } ExpKind; @@ -79,6 +80,18 @@ typedef struct ExpDesc { #define expr_numtv(e) check_exp(expr_isnumk((e)), &(e)->u.nval) #define expr_numberV(e) numberVnum(expr_numtv((e))) +/* Expression flags. */ +#define EXPR_F_NORES 0x01 /* Result will not be used. */ +#define EXPR_F_NOCOLON 0x02 /* Disallow colon for method call.*/ +#define EXPR_F_NONAV 0x04 /* Disallow safe navigation. */ +#define EXPR_F_RET1 0x08 /* Return a single expr. */ + +static LJ_AINLINE int32_t expr_bitV(ExpDesc *e) +{ + TValue *o = expr_numtv(e); + return tvisint(o) ? intV(o) : lj_num2bit(numV(o)); +} + /* Initialize expression. */ static LJ_AINLINE void expr_init(ExpDesc *e, ExpKind k, uint32_t info) { @@ -107,17 +120,22 @@ typedef struct FuncScope { #define FSCOPE_GOLA 0x04 /* Goto or label used in scope. */ #define FSCOPE_UPVAL 0x08 /* Upvalue in scope. */ #define FSCOPE_NOCLOSE 0x10 /* Do not close upvalues. */ +#define FSCOPE_CONT 0x20 /* Continue used in scope. */ #define NAME_BREAK ((GCstr *)(uintptr_t)1) +#define NAME_CONT ((GCstr *)(uintptr_t)2) -/* Index into variable stack. */ -typedef uint16_t VarIndex; +/* Index into variable stack. See VarIndex in lj_lex.h. */ +#define VINDEX_NONE 0xffff #define LJ_MAX_VSTACK (65536 - LJ_MAX_UPVAL) +#define LJ_HASH_VSTACK 0x20 /* Must be a power of 2. */ + /* Variable/goto/label info. */ #define VSTACK_VAR_RW 0x01 /* R/W variable. */ #define VSTACK_GOTO 0x02 /* Pending goto. */ #define VSTACK_LABEL 0x04 /* Label. */ +#define VSTACK_CONST 0x08 /* Constant variable. */ /* Per-function state. */ typedef struct FuncState { @@ -148,10 +166,11 @@ typedef struct FuncState { /* Binary and unary operators. ORDER OPR */ typedef enum BinOpr { OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, /* ORDER ARITH */ + OPR_BAND, OPR_BOR, OPR_BXOR, OPR_BSHL, OPR_BSHR, OPR_BSAR, /* ORDER BIT */ OPR_CONCAT, OPR_NE, OPR_EQ, OPR_LT, OPR_GE, OPR_LE, OPR_GT, - OPR_AND, OPR_OR, + OPR_AND, OPR_OR, OPR_COAL, OPR_NOBINOPR } BinOpr; @@ -458,7 +477,7 @@ static void expr_discharge(FuncState *fs, ExpDesc *e) ins = BCINS_ABC(BC_TGETV, 0, e->u.s.info, rc); } bcreg_free(fs, e->u.s.info); - } else if (e->k == VCALL) { + } else if (e->k == VCALL || e->k == VCALLNAV) { e->u.s.info = e->u.s.aux; e->k = VNONRELOC; return; @@ -623,11 +642,13 @@ static void bcemit_store(FuncState *fs, ExpDesc *var, ExpDesc *e) { BCIns ins; if (var->k == VLOCAL) { + lj_assertFS(!(fs->ls->vstack[var->u.s.aux].info & VSTACK_CONST), "unchecked const assignment"); fs->ls->vstack[var->u.s.aux].info |= VSTACK_VAR_RW; expr_free(fs, e); expr_toreg(fs, e, var->u.s.info); return; } else if (var->k == VUPVAL) { + lj_assertFS(!(fs->ls->vstack[var->u.s.aux].info & VSTACK_CONST), "unchecked const assignment"); fs->ls->vstack[var->u.s.aux].info |= VSTACK_VAR_RW; expr_toval(fs, e); if (e->k <= VKTRUE) @@ -793,17 +814,39 @@ static int foldarith(BinOpr opr, ExpDesc *e1, ExpDesc *e2) return 1; } +/* Try constant-folding of bit operators. */ +static int foldbitop(BinOpr opr, ExpDesc *e1, ExpDesc *e2) +{ + if (expr_isnumk_nojump(e1) && expr_isnumk_nojump(e2)) { + int32_t k1 = expr_bitV(e1), k2 = expr_bitV(e2); + switch (opr) { + case OPR_BAND: k1 &= k2; break; + case OPR_BOR: k1 |= k2; break; + case OPR_BXOR: k1 ^= k2; break; + case OPR_BSHL: k1 <<= (k2 & 31); break; + case OPR_BSHR: k1 = (int32_t)((uint32_t)k1 >> (k2 & 31)); break; + case OPR_BSAR: k1 >>= (k2 & 31); break; + default: lj_assertX(0, "bad OPR %d", opr); break; + } + setintV(&e1->u.nval, k1); + return 1; + } + return 0; +} + /* Emit arithmetic operator. */ static void bcemit_arith(FuncState *fs, BinOpr opr, ExpDesc *e1, ExpDesc *e2) { BCReg rb, rc, t; uint32_t op; - if (foldarith(opr, e1, e2)) - return; if (opr == OPR_POW) { op = BC_POW; rc = expr_toanyreg(fs, e2); rb = expr_toanyreg(fs, e1); + } else if (opr >= OPR_BAND) { + op = opr-OPR_BAND+BC_BAND; + rc = expr_toanyreg(fs, e2); + rb = expr_toanyreg(fs, e1); } else { op = opr-OPR_ADD+BC_ADDVV; /* Must discharge 2nd operand first since VINDEXED might free regs. */ @@ -887,6 +930,13 @@ static void bcemit_binop_left(FuncState *fs, BinOpr op, ExpDesc *e) bcemit_branch_t(fs, e); } else if (op == OPR_OR) { bcemit_branch_f(fs, e); + } else if (op == OPR_COAL) { + BCReg reg; + expr_tonextreg(fs, e); + reg = e->u.s.info; + bcemit_INS(fs, BCINS_AD(BC_ISNEP, reg, VKNIL)); + e->u.s.aux = bcemit_jmp(fs); + bcreg_free(fs, reg); } else if (op == OPR_CONCAT) { expr_tonextreg(fs, e); } else if (op == OPR_EQ || op == OPR_NE) { @@ -900,7 +950,12 @@ static void bcemit_binop_left(FuncState *fs, BinOpr op, ExpDesc *e) static void bcemit_binop(FuncState *fs, BinOpr op, ExpDesc *e1, ExpDesc *e2) { if (op <= OPR_POW) { - bcemit_arith(fs, op, e1, e2); + if (!foldarith(op, e1, e2)) bcemit_arith(fs, op, e1, e2); + } else if (op <= OPR_BSAR) { + if (!foldbitop(op, e1, e2)) { + fs->flags |= PROTO_BITOP; + bcemit_arith(fs, op, e1, e2); + } } else if (op == OPR_AND) { lj_assertFS(e1->t == NO_JMP, "jump list not closed"); expr_discharge(fs, e2); @@ -911,6 +966,9 @@ static void bcemit_binop(FuncState *fs, BinOpr op, ExpDesc *e1, ExpDesc *e2) expr_discharge(fs, e2); jmp_append(fs, &e2->t, e1->t); *e1 = *e2; + } else if (op == OPR_COAL) { + expr_tonextreg(fs, e2); + jmp_tohere(fs, e1->u.s.aux); } else if (op == OPR_CONCAT) { expr_toval(fs, e2); if (e2->k == VRELOCABLE && bc_op(*bcptr(fs, e2)) == BC_CAT) { @@ -961,34 +1019,41 @@ static void bcemit_unop(FuncState *fs, BCOp op, ExpDesc *e) lj_assertFS(e->k == VNONRELOC, "bad expr type %d", e->k); } } else { - lj_assertFS(op == BC_UNM || op == BC_LEN, "bad unop %d", op); - if (op == BC_UNM && !expr_hasjump(e)) { /* Constant-fold negations. */ + lj_assertFS(op == BC_UNM || op == BC_LEN || op == BC_BNOT, "bad unop %d", op); + if (!expr_hasjump(e)) { + if (op == BC_UNM) { /* Constant-fold negations. */ #if LJ_HASFFI - if (e->k == VKCDATA) { /* Fold in-place since cdata is not interned. */ - GCcdata *cd = cdataV(&e->u.nval); - uint64_t *p = (uint64_t *)cdataptr(cd); - if (cd->ctypeid == CTID_COMPLEX_DOUBLE) - p[1] ^= U64x(80000000,00000000); - else - *p = ~*p+1u; - return; - } else -#endif - if (expr_isnumk(e) && !expr_numiszero(e)) { /* Avoid folding to -0. */ - TValue *o = expr_numtv(e); - if (tvisint(o)) { - int32_t k = intV(o), negk = (int32_t)(~(uint32_t)k+1u); - if (k == negk) - setnumV(o, -(lua_Number)k); + if (e->k == VKCDATA) { /* Fold in-place since cdata is not interned. */ + GCcdata *cd = cdataV(&e->u.nval); + uint64_t *p = (uint64_t *)cdataptr(cd); + if (cd->ctypeid == CTID_COMPLEX_DOUBLE) + p[1] ^= U64x(80000000,00000000); else - setintV(o, negk); - return; - } else { - o->u64 ^= U64x(80000000,00000000); + *p = ~*p+1u; return; + } else +#endif + if (expr_isnumk(e) && !expr_numiszero(e)) { /* Avoid folding to -0. */ + TValue *o = expr_numtv(e); + if (tvisint(o)) { + int32_t k = intV(o), negk = (int32_t)(~(uint32_t)k+1u); + if (k == negk) + setnumV(o, -(lua_Number)k); + else + setintV(o, negk); + return; + } else { + o->u64 ^= U64x(80000000,00000000); + return; + } } + } else if (op == BC_BNOT && expr_isnumk(e)) { + /* Constant-fold bitwise not. */ + setintV(&e->u.nval, (int32_t)~(uint32_t)expr_bitV(e)); + return; } } + if (op == BC_BNOT) fs->flags |= PROTO_BITOP; expr_toanyreg(fs, e); } expr_free(fs, e); @@ -1030,11 +1095,20 @@ static void lex_match(LexState *ls, LexToken what, LexToken who, BCLine line) } } +/* Check for a name, including soft keywords. */ +static LJ_AINLINE int lex_isname(LexToken tok) +{ + return (tok == TK_name || + (!LJ_52 && tok == TK_goto) || + tok == TK_continue || + tok == TK_const); +} + /* Check for string token. */ static GCstr *lex_str(LexState *ls) { GCstr *s; - if (ls->tok != TK_name && (LJ_52 || ls->tok != TK_goto)) + if (!lex_isname(ls->tok)) err_token(ls, TK_name); s = strV(&ls->tokval); lj_lex_next(ls); @@ -1045,11 +1119,31 @@ static GCstr *lex_str(LexState *ls) #define var_get(ls, fs, i) ((ls)->vstack[(fs)->varmap[(i)]]) +typedef intptr_t VarHash; /* For performance reasons. */ + +/* Hash of a variable name. */ +static LJ_AINLINE VarHash var_hash(GCstr *name) +{ + if ((uintptr_t)name < VARNAME__MAX) + return -1; + else + return (name->sid & LJ_VINDEX_MASK); /* Immutable id, not name->hash! */ +} + /* Define a new local variable. */ -static void var_new(LexState *ls, BCReg n, GCstr *name) +static MSize var_new(LexState *ls, BCReg n, GCstr *name) { FuncState *fs = ls->fs; MSize vtop = ls->vtop; + if ((uintptr_t)name >= VARNAME__MAX) { /* Check for const re-declaration. */ + MSize vidx = ls->vhash[var_hash(name)]; + while (vidx != VINDEX_NONE) { + VarInfo *v = &ls->vstack[vidx]; + if (strref(v->name) == name && (v->info & VSTACK_CONST)) + lj_lex_error(ls, 0, LJ_ERR_XCONSTR, strdata(name)); + vidx = v->prev; + } + } checklimit(fs, fs->nactvar+n, LJ_MAX_LOCVAR, "local variables"); if (LJ_UNLIKELY(vtop >= ls->sizevstack)) { if (ls->sizevstack >= LJ_MAX_VSTACK) @@ -1061,8 +1155,11 @@ static void var_new(LexState *ls, BCReg n, GCstr *name) "unanchored variable name"); /* NOBARRIER: name is anchored in fs->kt and ls->vstack is not a GCobj. */ setgcref(ls->vstack[vtop].name, obj2gco(name)); + ls->vstack[vtop].info = 0; + /* The other VarInfo fields are filled in by var_add and var_remove. */ fs->varmap[fs->nactvar+n] = (uint16_t)vtop; ls->vtop = vtop+1; + return vtop; } #define var_new_lit(ls, n, v) \ @@ -1077,10 +1174,15 @@ static void var_add(LexState *ls, BCReg nvars) FuncState *fs = ls->fs; BCReg nactvar = fs->nactvar; while (nvars--) { - VarInfo *v = &var_get(ls, fs, nactvar); + intptr_t vidx = fs->varmap[nactvar]; + VarInfo *v = &ls->vstack[vidx]; + VarHash hash = var_hash(strref(v->name)); v->startpc = fs->pc; v->slot = nactvar++; - v->info = 0; + if (hash != -1) { + v->prev = ls->vhash[hash]; + ls->vhash[hash] = vidx; + } } fs->nactvar = nactvar; } @@ -1089,70 +1191,86 @@ static void var_add(LexState *ls, BCReg nvars) static void var_remove(LexState *ls, BCReg tolevel) { FuncState *fs = ls->fs; - while (fs->nactvar > tolevel) - var_get(ls, fs, --fs->nactvar).endpc = fs->pc; -} - -/* Lookup local variable name. */ -static BCReg var_lookup_local(FuncState *fs, GCstr *n) -{ - int i; - for (i = fs->nactvar-1; i >= 0; i--) { - if (n == strref(var_get(fs->ls, fs, i).name)) - return (BCReg)i; + while (fs->nactvar > tolevel) { + VarInfo *v = &var_get(ls, fs, --fs->nactvar); + VarHash hash = var_hash(strref(v->name)); + v->endpc = fs->pc; + if (hash != -1) { + ls->vhash[hash] = v->prev; + } } - return (BCReg)-1; /* Not found. */ -} - -/* Lookup or add upvalue index. */ -static MSize var_lookup_uv(FuncState *fs, MSize vidx, ExpDesc *e) -{ - MSize i, n = fs->nuv; - for (i = 0; i < n; i++) - if (fs->uvmap[i] == vidx) - return i; /* Already exists. */ - /* Otherwise create a new one. */ - checklimit(fs, fs->nuv, LJ_MAX_UPVAL, "upvalues"); - lj_assertFS(e->k == VLOCAL || e->k == VUPVAL, "bad expr type %d", e->k); - fs->uvmap[n] = (uint16_t)vidx; - fs->uvtmp[n] = (uint16_t)(e->k == VLOCAL ? vidx : LJ_MAX_VSTACK+e->u.s.info); - fs->nuv = n+1; - return n; } /* Forward declaration. */ static void fscope_uvmark(FuncState *fs, BCReg level); -/* Recursively lookup variables in enclosing functions. */ -static MSize var_lookup_(FuncState *fs, GCstr *name, ExpDesc *e, int first) +/* Lookup variable name. */ +static MSize var_lookup(LexState *ls, ExpDesc *e, GCstr *name) { - if (fs) { - BCReg reg = var_lookup_local(fs, name); - if ((int32_t)reg >= 0) { /* Local in this function? */ - expr_init(e, VLOCAL, reg); - if (!first) - fscope_uvmark(fs, reg); /* Scope now has an upvalue. */ - return (MSize)(e->u.s.aux = (uint32_t)fs->varmap[reg]); - } else { - MSize vidx = var_lookup_(fs->prev, name, e, 0); /* Var in outer func? */ - if ((int32_t)vidx >= 0) { /* Yes, make it an upvalue here. */ - e->u.s.info = (uint8_t)var_lookup_uv(fs, vidx, e); - e->k = VUPVAL; - return vidx; + MSize vidx = ls->vhash[var_hash(name)]; + while (vidx != VINDEX_NONE) { + VarInfo *v = &ls->vstack[vidx]; + if (strref(v->name) == name) { + FuncState *fs = ls->fs; + if (vidx >= fs->vbase) { + expr_init(e, VLOCAL, v->slot); + e->u.s.aux = vidx; + } else { + MSize uvidx, nuv = fs->nuv; + e->u.s.aux = vidx; + for (uvidx = 0; uvidx < nuv; uvidx++) { + if (fs->uvmap[uvidx] == vidx) { /* Upvalue already exists. */ + expr_init(e, VUPVAL, uvidx); + return vidx; + } + } + expr_init(e, VUPVAL, nuv); + for (;;) { + /* Create a new upvalue. */ + VarIndex *puvtmp; + checklimit(fs, nuv, LJ_MAX_UPVAL, "upvalues"); + fs->uvmap[nuv] = (uint16_t)vidx; + fs->nuv = nuv + 1; + puvtmp = &fs->uvtmp[nuv]; /* Set below. */ + fs = fs->prev; /* Continue in parent. */ + lj_assertLS(fs != NULL, "variable hash chain broken"); + if (vidx >= fs->vbase) { /* Local in that function. */ + *puvtmp = vidx; + fscope_uvmark(fs, v->slot); + return vidx; + } + /* Not a local in that function. Find or create upvalue. */ + nuv = fs->nuv; + for (uvidx = 0; uvidx < nuv; uvidx++) { + if (fs->uvmap[uvidx] == vidx) { /* Upvalue already exists. */ + *puvtmp = LJ_MAX_VSTACK + uvidx; + return vidx; + } + } + /* Not yet an upvalue. Create it and continue. */ + *puvtmp = LJ_MAX_VSTACK + nuv; + } } + return vidx; } - } else { /* Not found in any function, must be a global. */ - expr_init(e, VGLOBAL, 0); - e->u.sval = name; + vidx = v->prev; } - return (MSize)-1; /* Global. */ + expr_init(e, VGLOBAL, 0); + e->u.sval = name; + return vidx; } -/* Lookup variable name. */ -#define var_lookup(ls, e) \ - var_lookup_((ls)->fs, lex_str(ls), (e), 1) +/* Check for const variable assignment. */ +static void var_assign(LexState *ls, ExpDesc *e) +{ + if (e->k == VLOCAL || e->k == VUPVAL) { + VarInfo *v = &ls->vstack[e->u.s.aux]; + if ((v->info & VSTACK_CONST)) + lj_lex_error(ls, 0, LJ_ERR_XCONSTA, strdata(strref(v->name))); + } +} -/* -- Goto an label handling ---------------------------------------------- */ +/* -- Goto and label handling --------------------------------------------- */ /* Add a new goto or label. */ static MSize gola_new(LexState *ls, GCstr *name, uint8_t info, BCPos pc) @@ -1164,7 +1282,8 @@ static MSize gola_new(LexState *ls, GCstr *name, uint8_t info, BCPos pc) lj_lex_error(ls, 0, LJ_ERR_XLIMC, LJ_MAX_VSTACK); lj_mem_growvec(ls->L, ls->vstack, ls->sizevstack, LJ_MAX_VSTACK, VarInfo); } - lj_assertFS(name == NAME_BREAK || lj_tab_getstr(fs->kt, name) != NULL, + lj_assertFS(name == NAME_BREAK || name == NAME_CONT || + lj_tab_getstr(fs->kt, name) != NULL, "unanchored label name"); /* NOBARRIER: name is anchored in fs->kt and ls->vstack is not a GCobj. */ setgcref(ls->vstack[vtop].name, obj2gco(name)); @@ -1219,8 +1338,12 @@ static void gola_resolve(LexState *ls, FuncScope *bl, MSize idx) lj_assertLS((uintptr_t)name >= VARNAME__MAX, "expected goto name"); ls->linenumber = ls->fs->bcbase[vg->startpc].line; lj_assertLS(strref(vg->name) != NAME_BREAK, "unexpected break"); - lj_lex_error(ls, 0, LJ_ERR_XGSCOPE, - strdata(strref(vg->name)), strdata(name)); + if (strref(vg->name) == NAME_CONT) { + lj_lex_error(ls, 0, LJ_ERR_XCSCOPE, strdata(name)); + } else { + lj_lex_error(ls, 0, LJ_ERR_XGSCOPE, + strdata(strref(vg->name)), strdata(name)); + } } gola_patch(ls, vg, vl); } @@ -1244,8 +1367,10 @@ static void gola_fixup(LexState *ls, FuncScope *bl) gola_patch(ls, vg, v); } } else if (gola_isgoto(v)) { - if (bl->prev) { /* Propagate goto or break to outer scope. */ - bl->prev->flags |= name == NAME_BREAK ? FSCOPE_BREAK : FSCOPE_GOLA; + if (bl->prev) { /* Propagate goto, break or continue to outer scope. */ + bl->prev->flags |= name == NAME_BREAK ? FSCOPE_BREAK : + name == NAME_CONT ? FSCOPE_CONT : + FSCOPE_GOLA; v->slot = bl->nactvar; if ((bl->flags & FSCOPE_UPVAL)) gola_close(ls, v); @@ -1253,6 +1378,8 @@ static void gola_fixup(LexState *ls, FuncScope *bl) ls->linenumber = ls->fs->bcbase[v->startpc].line; if (name == NAME_BREAK) lj_lex_error(ls, 0, LJ_ERR_XBREAK); + else if (name == NAME_CONT) + lj_lex_error(ls, 0, LJ_ERR_XCONT); else lj_lex_error(ls, 0, LJ_ERR_XLUNDEF, strdata(name)); } @@ -1296,21 +1423,33 @@ static void fscope_end(FuncState *fs) lj_assertFS(bl->nactvar == fs->nactvar, "bad regalloc"); if ((bl->flags & (FSCOPE_UPVAL|FSCOPE_NOCLOSE)) == FSCOPE_UPVAL) bcemit_AJ(fs, BC_UCLO, bl->nactvar, 0); - if ((bl->flags & FSCOPE_BREAK)) { - if ((bl->flags & FSCOPE_LOOP)) { - MSize idx = gola_new(ls, NAME_BREAK, VSTACK_LABEL, fs->pc); - ls->vtop = idx; /* Drop break label immediately. */ - gola_resolve(ls, bl, idx); - } else { /* Need the fixup step to propagate the breaks. */ - gola_fixup(ls, bl); - return; - } + lj_assertFS((bl->flags & (FSCOPE_LOOP|FSCOPE_CONT)) != (FSCOPE_LOOP|FSCOPE_CONT), "dangling continue"); + if ((bl->flags & (FSCOPE_LOOP|FSCOPE_BREAK)) == (FSCOPE_LOOP|FSCOPE_BREAK)) { + MSize idx; + bl->flags &= ~FSCOPE_BREAK; + idx = gola_new(ls, NAME_BREAK, VSTACK_LABEL, fs->pc); + ls->vtop = idx; /* Drop break label immediately. */ + gola_resolve(ls, bl, idx); } - if ((bl->flags & FSCOPE_GOLA)) { + if ((bl->flags & (FSCOPE_GOLA|FSCOPE_BREAK|FSCOPE_CONT))) { gola_fixup(ls, bl); } } +/* Add continue label. */ +static void fscope_continue(FuncState *fs, BCPos cont) +{ + FuncScope *bl = fs->bl; + if ((bl->flags & FSCOPE_CONT)) { + LexState *ls = fs->ls; + MSize idx; + bl->flags &= ~FSCOPE_CONT; + idx = gola_new(ls, NAME_CONT, VSTACK_LABEL, cont); + ls->vtop = idx; /* Drop continue label immediately. */ + gola_resolve(ls, bl, idx); + } +} + /* Mark scope as having an upvalue. */ static void fscope_uvmark(FuncState *fs, BCReg level) { @@ -1460,7 +1599,7 @@ static void fs_fixup_line(FuncState *fs, GCproto *pt, /* Prepare variable info for prototype. */ static size_t fs_prep_var(LexState *ls, FuncState *fs, size_t *ofsvar) { - VarInfo *vs =ls->vstack, *ve; + VarInfo *vs = ls->vstack, *ve; MSize i, n; BCPos lastpc; lj_buf_reset(&ls->sb); /* Copy to temp. string buffer. */ @@ -1633,7 +1772,7 @@ static void fs_init(LexState *ls, FuncState *fs) /* -- Expressions --------------------------------------------------------- */ /* Forward declaration. */ -static void expr(LexState *ls, ExpDesc *v); +static void expr(LexState *ls, ExpDesc *v, int nocolon); /* Return string expression. */ static void expr_str(LexState *ls, ExpDesc *e) @@ -1680,7 +1819,6 @@ static void expr_field(LexState *ls, ExpDesc *v) FuncState *fs = ls->fs; ExpDesc key; expr_toanyreg(fs, v); - lj_lex_next(ls); /* Skip dot or colon. */ expr_str(ls, &key); expr_index(fs, v, &key); } @@ -1689,7 +1827,7 @@ static void expr_field(LexState *ls, ExpDesc *v) static void expr_bracket(LexState *ls, ExpDesc *v) { lj_lex_next(ls); /* Skip '['. */ - expr(ls, v); + expr(ls, v, 0); expr_toval(ls->fs, v); lex_check(ls, ']'); } @@ -1731,8 +1869,7 @@ static void expr_table(LexState *ls, ExpDesc *e) if (!expr_isk(&key)) expr_index(fs, e, &key); if (expr_isnumk(&key) && expr_numiszero(&key)) needarr = 1; else nhash++; lex_check(ls, '='); - } else if ((ls->tok == TK_name || (!LJ_52 && ls->tok == TK_goto)) && - lj_lex_lookahead(ls) == '=') { + } else if (lex_isname(ls->tok) && lj_lex_lookahead(ls) == '=') { expr_str(ls, &key); lex_check(ls, '='); nhash++; @@ -1742,7 +1879,7 @@ static void expr_table(LexState *ls, ExpDesc *e) narr++; needarr = vcall = 1; } - expr(ls, &val); + expr(ls, &val, 0); if (expr_isk(&key) && key.k != VKNIL && (key.k == VKSTR || expr_isk_nojump(&val))) { TValue k, *v; @@ -1766,7 +1903,10 @@ static void expr_table(LexState *ls, ExpDesc *e) } } else { nonconst: - if (val.k != VCALL) { expr_toanyreg(fs, &val); vcall = 0; } + if (val.k != VCALL) { + expr_toanyreg(fs, &val); + vcall = 0; + } if (expr_isk(&key)) expr_index(fs, e, &key); bcemit_store(fs, e, &val); } @@ -1808,16 +1948,17 @@ static void expr_table(LexState *ls, ExpDesc *e) } /* Parse function parameters. */ -static BCReg parse_params(LexState *ls, int needself) +static BCReg parse_params(LexState *ls, int needself, + LexToken before, LexToken after) { FuncState *fs = ls->fs; BCReg nparams = 0; - lex_check(ls, '('); + lex_check(ls, before); if (needself) var_new_lit(ls, nparams++, "self"); - if (ls->tok != ')') { + if (ls->tok != after) { do { - if (ls->tok == TK_name || (!LJ_52 && ls->tok == TK_goto)) { + if (lex_isname(ls->tok)) { var_new(ls, nparams++, lex_str(ls)); } else if (ls->tok == TK_dots) { lj_lex_next(ls); @@ -1831,54 +1972,96 @@ static BCReg parse_params(LexState *ls, int needself) var_add(ls, nparams); lj_assertFS(fs->nactvar == nparams, "bad regalloc"); bcreg_reserve(fs, nparams); - lex_check(ls, ')'); + lex_check(ls, after); return nparams; } -/* Forward declaration. */ +/* Forward declarations. */ static void parse_chunk(LexState *ls); +static void parse_return(LexState *ls, int eflags); -/* Parse body of a function. */ -static void parse_body(LexState *ls, ExpDesc *e, int needself, BCLine line) +/* Begin a new function prototype. */ +static void proto_begin(FuncState *fs, BCLine line, BCReg nparams) { - FuncState fs, *pfs = ls->fs; - FuncScope bl; - GCproto *pt; - ptrdiff_t oldbase = pfs->bcbase - ls->bcstack; - fs_init(ls, &fs); - fscope_begin(&fs, &bl, 0); - fs.linedefined = line; - fs.numparams = (uint8_t)parse_params(ls, needself); - fs.bcbase = pfs->bcbase + pfs->pc; - fs.bclim = pfs->bclim - pfs->pc; - bcemit_AD(&fs, BC_FUNCF, 0, 0); /* Placeholder. */ - parse_chunk(ls); - if (ls->tok != TK_end) lex_match(ls, TK_end, TK_function, line); - pt = fs_finish(ls, (ls->lastline = ls->linenumber)); + FuncState *pfs = fs->prev; + fs->linedefined = line; + fs->numparams = (uint8_t)nparams; + fs->bcbase = pfs->bcbase + pfs->pc; + fs->bclim = pfs->bclim - pfs->pc; + bcemit_AD(fs, BC_FUNCF, 0, 0); /* Placeholder. */ +} + +/* Finish a function prototype. */ +static void proto_finish(LexState *ls, ExpDesc *e, ptrdiff_t oldbase) +{ + MSize flags = (ls->fs->flags & (PROTO_FFI|PROTO_BITOP)); + GCproto *pt = fs_finish(ls, (ls->lastline = ls->linenumber)); + FuncState *pfs = ls->fs; pfs->bcbase = ls->bcstack + oldbase; /* May have been reallocated. */ pfs->bclim = (BCPos)(ls->sizebcstack - oldbase); /* Store new prototype in the constant array of the parent. */ expr_init(e, VRELOCABLE, bcemit_AD(pfs, BC_FNEW, 0, const_gc(pfs, obj2gco(pt), LJ_TPROTO))); -#if LJ_HASFFI - pfs->flags |= (fs.flags & PROTO_FFI); -#endif + pfs->flags |= (uint8_t)flags; /* Inherited flags. */ if (!(pfs->flags & PROTO_CHILD)) { if (pfs->flags & PROTO_HAS_RETURN) pfs->flags |= PROTO_FIXUP_RETURN; pfs->flags |= PROTO_CHILD; } +} + +/* Parse body of a function. */ +static void parse_body(LexState *ls, ExpDesc *e, int needself, BCLine line) +{ + ptrdiff_t oldbase = ls->fs->bcbase - ls->bcstack; + FuncState fs; + FuncScope bl; + fs_init(ls, &fs); + fscope_begin(&fs, &bl, 0); + proto_begin(&fs, line, parse_params(ls, needself, '(', ')')); + parse_chunk(ls); + if (ls->tok != TK_end) lex_match(ls, TK_end, TK_function, line); + proto_finish(ls, e, oldbase); lj_lex_next(ls); } +/* Parse short function. */ +static void parse_shortfunc(LexState *ls, ExpDesc *e, GCstr *name, + int eflags, BCLine line) +{ + ptrdiff_t oldbase = ls->fs->bcbase - ls->bcstack; + FuncState fs; + FuncScope bl; + BCReg nparams = 0; + fs_init(ls, &fs); + fscope_begin(&fs, &bl, 0); + if (name != NULL) { + setboolV(lj_tab_setstr(ls->L, fs.kt, name), 1); /* Anchor in new proto. */ + var_new(ls, nparams++, name); + var_add(ls, nparams); + bcreg_reserve(&fs, 1); + } else if (!lex_opt(ls, TK_or_)) { + nparams = parse_params(ls, 0, '|', '|'); + } + lex_check(ls, TK_arrow); + proto_begin(&fs, line, nparams); + if (lex_opt(ls, TK_do)) { + parse_chunk(ls); + if (!lex_opt(ls, TK_end)) lex_match(ls, TK_end, TK_do, line); + } else { + parse_return(ls, (eflags | EXPR_F_RET1)); + } + proto_finish(ls, e, oldbase); +} + /* Parse expression list. Last expression is left open. */ static BCReg expr_list(LexState *ls, ExpDesc *v) { BCReg n = 1; - expr(ls, v); + expr(ls, v, 0); while (lex_opt(ls, ',')) { expr_tonextreg(ls->fs, v); - expr(ls, v); + expr(ls, v, 0); n++; } return n; @@ -1931,48 +2114,102 @@ static void parse_args(LexState *ls, ExpDesc *e) fs->freereg = base+1; /* Leave one result by default. */ } -/* Parse primary expression. */ -static void expr_primary(LexState *ls, ExpDesc *v) +/* Parse primary expression with safe navigation. */ +static BCPos expr_primary_nav(LexState *ls, ExpDesc *v, int eflags) { FuncState *fs = ls->fs; + BCPos xpc = NO_JMP; /* Parse prefix expression. */ if (ls->tok == '(') { BCLine line = ls->linenumber; lj_lex_next(ls); - expr(ls, v); + expr(ls, v, 0); /* Don't propagate eflags. */ lex_match(ls, ')', '(', line); expr_discharge(ls->fs, v); - } else if (ls->tok == TK_name || (!LJ_52 && ls->tok == TK_goto)) { - var_lookup(ls, v); + } else if (lex_isname(ls->tok)) { + BCLine line = ls->linenumber; + GCstr *name = lex_str(ls); + if (!(eflags & EXPR_F_NORES) && ls->tok == TK_arrow) { + parse_shortfunc(ls, v, name, eflags, line); + return xpc; + } + var_lookup(ls, v, name); } else { + err: err_syntax(ls, LJ_ERR_XSYMBOL); } for (;;) { /* Parse multiple expression suffixes. */ - if (ls->tok == '.') { - expr_field(ls, v); - } else if (ls->tok == '[') { + int nav = 0; + if (!(eflags & EXPR_F_NONAV) && lex_opt(ls, TK_nav)) { + nav = 1; + expr_toanyreg(fs, v); + bcemit_INS(fs, BCINS_AD(BC_ISEQP, v->u.s.info, VKNIL)); + jmp_append(fs, &xpc, bcemit_jmp(fs)); + } + if (ls->tok == '[') { ExpDesc key; expr_toanyreg(fs, v); expr_bracket(ls, &key); expr_index(fs, v, &key); } else if (ls->tok == ':') { ExpDesc key; - lj_lex_next(ls); + if ((eflags & EXPR_F_NOCOLON)) { + if (nav) goto err; + break; + } + lj_lex_next(ls); /* Skip ':'. */ expr_str(ls, &key); bcemit_method(fs, v, &key); - parse_args(ls, v); + nav = 0; + if (lex_opt(ls, TK_nav)) { + nav = 1; + bcemit_INS(fs, BCINS_AD(BC_ISEQP, v->u.s.info, VKNIL)); + jmp_append(fs, &xpc, bcemit_jmp(fs)); + } + goto call; } else if (ls->tok == '(' || ls->tok == TK_string || ls->tok == '{') { expr_tonextreg(fs, v); if (ls->fr2) bcreg_reserve(fs, 1); + call: parse_args(ls, v); + /* Keep nav VCALL if no suffix follows. */ + if (nav && !(eflags & EXPR_F_NORES) && + !(ls->tok == TK_nav || ls->tok == '[' || ls->tok == ':' || + ls->tok == '(' || ls->tok == TK_string || ls->tok == '{' || + ls->tok == '.')) break; + } else if (nav || lex_opt(ls, '.')) { + expr_field(ls, v); } else { break; } + if (nav && !(eflags & EXPR_F_NORES)) { + expr_tonextreg(fs, v); + } + } + return xpc; +} + +/* Parse primary expression. */ +static void expr_primary(LexState *ls, ExpDesc *v, int eflags) +{ + BCPos xpc = expr_primary_nav(ls, v, eflags); + if (xpc != NO_JMP) { + FuncState *fs = ls->fs; + BCPos around; + around = bcemit_jmp(fs); + jmp_tohere(fs, xpc); + if (v->k == VCALL) { /* Change to VCALLNAV. Still points to CALL/CALLM. */ + v->k = VCALLNAV; + bcemit_AD(fs, BC_KPRI, v->u.s.aux, VKNIL); + } else { + bcemit_AD(fs, BC_KPRI, v->u.s.info, VKNIL); + } + jmp_tohere(fs, around); } } /* Parse simple expression. */ -static void expr_simple(LexState *ls, ExpDesc *v) +static void expr_simple(LexState *ls, ExpDesc *v, int eflags) { switch (ls->tok) { case TK_number: @@ -2009,8 +2246,11 @@ static void expr_simple(LexState *ls, ExpDesc *v) lj_lex_next(ls); parse_body(ls, v, 0, ls->linenumber); return; + case '|': case TK_or_: + parse_shortfunc(ls, v, NULL, eflags, ls->linenumber); + return; default: - expr_primary(ls, v); + expr_primary(ls, v, eflags); return; } lj_lex_next(ls); @@ -2035,15 +2275,22 @@ static BinOpr token2binop(LexToken tok) case '/': return OPR_DIV; case '%': return OPR_MOD; case '^': return OPR_POW; + case '&': return OPR_BAND; + case '|': return OPR_BOR; + case '~': return OPR_BXOR; + case TK_shl: return OPR_BSHL; + case TK_shr: return OPR_BSHR; + case TK_sar: return OPR_BSAR; case TK_concat: return OPR_CONCAT; - case TK_ne: return OPR_NE; + case TK_ne: case TK_ne_: return OPR_NE; case TK_eq: return OPR_EQ; case '<': return OPR_LT; case TK_le: return OPR_LE; case '>': return OPR_GT; case TK_ge: return OPR_GE; - case TK_and: return OPR_AND; - case TK_or: return OPR_OR; + case TK_and: case TK_and_: return OPR_AND; + case TK_or: case TK_or_: return OPR_OR; + case TK_coal: return OPR_COAL; default: return OPR_NOBINOPR; } } @@ -2053,69 +2300,91 @@ static const struct { uint8_t left; /* Left priority. */ uint8_t right; /* Right priority. */ } priority[] = { - {6,6}, {6,6}, {7,7}, {7,7}, {7,7}, /* ADD SUB MUL DIV MOD */ - {10,9}, {5,4}, /* POW CONCAT (right associative) */ + {10,10}, {10,10}, {11,11}, {11,11}, {11,11}, /* ADD SUB MUL DIV MOD */ + {14,13}, /* POW (right associative) */ + {6,6}, {4,4}, {5,5}, /* BAND BOR BXOR */ + {7,7}, {7,7}, {7,7}, /* BSHL BSHR BSAR */ + {9,8}, /* CONCAT (right associative) */ {3,3}, {3,3}, /* EQ NE */ {3,3}, {3,3}, {3,3}, {3,3}, /* LT GE GT LE */ - {2,2}, {1,1} /* AND OR */ + {2,2}, {1,1}, {1,1} /* AND OR COAL */ }; -#define UNARY_PRIORITY 8 /* Priority for unary operators. */ +#define UNARY_PRIORITY 12 /* Priority for unary operators. */ /* Forward declaration. */ -static BinOpr expr_binop(LexState *ls, ExpDesc *v, uint32_t limit); +static BinOpr expr_binop(LexState *ls, ExpDesc *v, uint32_t limit, int eflags); /* Parse unary expression. */ -static void expr_unop(LexState *ls, ExpDesc *v) +static void expr_unop(LexState *ls, ExpDesc *v, int eflags) { BCOp op; - if (ls->tok == TK_not) { + if (ls->tok == TK_not || ls->tok == '!') { op = BC_NOT; } else if (ls->tok == '-') { op = BC_UNM; } else if (ls->tok == '#') { op = BC_LEN; + } else if (ls->tok == '~') { + op = BC_BNOT; } else { - expr_simple(ls, v); + expr_simple(ls, v, eflags); return; } lj_lex_next(ls); - expr_binop(ls, v, UNARY_PRIORITY); + expr_binop(ls, v, UNARY_PRIORITY, eflags); bcemit_unop(ls->fs, op, v); } /* Parse binary expressions with priority higher than the limit. */ -static BinOpr expr_binop(LexState *ls, ExpDesc *v, uint32_t limit) +static BinOpr expr_binop(LexState *ls, ExpDesc *v, uint32_t limit, int eflags) { - BinOpr op; + BinOpr opr; synlevel_begin(ls); - expr_unop(ls, v); - op = token2binop(ls->tok); - while (op != OPR_NOBINOPR && priority[op].left > limit) { + expr_unop(ls, v, eflags); + opr = token2binop(ls->tok); + while (opr != OPR_NOBINOPR && priority[opr].left > limit) { ExpDesc v2; BinOpr nextop; lj_lex_next(ls); - bcemit_binop_left(ls->fs, op, v); + bcemit_binop_left(ls->fs, opr, v); /* Parse binary expression with higher priority. */ - nextop = expr_binop(ls, &v2, priority[op].right); - bcemit_binop(ls->fs, op, v, &v2); - op = nextop; + nextop = expr_binop(ls, &v2, priority[opr].right, eflags); + bcemit_binop(ls->fs, opr, v, &v2); + opr = nextop; } synlevel_end(ls); - return op; /* Return unconsumed binary operator (if any). */ + return opr; /* Return unconsumed binary operator (if any). */ } /* Parse expression. */ -static void expr(LexState *ls, ExpDesc *v) +static void expr(LexState *ls, ExpDesc *v, int eflags) { - expr_binop(ls, v, 0); /* Priority 0: parse whole expression. */ + expr_binop(ls, v, 0, eflags); /* Priority 0: parse whole expression. */ + if (lex_opt(ls, '?')) { /* Ternary ?: conditional operator. Right-assoc. */ + FuncState *fs = ls->fs; + BCPos escapelist = NO_JMP, cond; + BCReg reg; + bcemit_branch_t(fs, v); + cond = v->f; + expr(ls, v, EXPR_F_NOCOLON); /* Prevent method parsing. Use parentheses. */ + expr_tonextreg(fs, v); + reg = v->u.s.info; + jmp_append(fs, &escapelist, bcemit_jmp(fs)); + jmp_tohere(fs, cond); + lex_check(ls, ':'); + bcreg_free(fs, reg); + expr(ls, v, 0); + expr_tonextreg(fs, v); + jmp_tohere(fs, escapelist); + } } /* Assign expression to the next register. */ static void expr_next(LexState *ls) { ExpDesc e; - expr(ls, &e); + expr(ls, &e, 0); expr_tonextreg(ls->fs, &e); } @@ -2123,7 +2392,7 @@ static void expr_next(LexState *ls) static BCPos expr_cond(LexState *ls) { ExpDesc v; - expr(ls, &v); + expr(ls, &v, 0); if (v.k == VKNIL) v.k = VKFALSE; bcemit_branch_t(ls->fs, &v); return v.f; @@ -2137,6 +2406,44 @@ typedef struct LHSVarList { struct LHSVarList *prev; /* Link to previous LHS variable. */ } LHSVarList; +/* Parse compound assignment. */ +static int parse_compound(LexState *ls, ExpDesc *e) +{ + FuncState *fs; + ExpDesc estore, v; + BinOpr opr; + if (!(e->k >= VLOCAL && e->k <= VINDEXED)) return 0; + opr = token2binop(ls->tok); + /* '^=' aka exponentiation assignment is deliberately omitted to avoid + ** confusion with xor assignment in other computer languages. + ** Use 'a ~= b' for xor assignment. The unequal operator is only valid + ** in expression contexts and assignments are statements. + */ + if (opr > OPR_NE || opr == OPR_POW) return 0; /* ORDER OPR */ + var_assign(ls, e); + if (opr == OPR_NE) { + if (ls->tok != TK_ne) lj_lex_error(ls, '!', LJ_ERR_XTOKEN, "="); + opr = OPR_BXOR; + } else { /* Can't use lex_check() here. Only allow '+=', not '+ ='. */ + if (ls->c != '=') err_token(ls, '='); + lj_lex_next(ls); /* Skip operator. */ + } + lj_lex_next(ls); /* Skip '=' or '~=' aka TOK_ne. */ + fs = ls->fs; + estore = *e; + if (e->k == VINDEXED) { /* Preserve the base and key for the store. */ + BCReg freg = fs->freereg; + expr_discharge(fs, e); + fs->freereg = freg; /* Undo bcreg_free of info and/or aux. */ + } + if (opr == OPR_CONCAT) expr_tonextreg(fs, e); else expr_toanyreg(fs, e); + expr(ls, &v, 0); + bcemit_binop(fs, opr, e, &v); + bcemit_store(fs, &estore, e); + /* Don't bother to free VINDEXED info+aux. Done by parse_chunk(). */ + return 1; +} + /* Eliminate write-after-read hazards for local variable assignment. */ static void assign_hazard(LexState *ls, LHSVarList *lh, const ExpDesc *v) { @@ -2167,11 +2474,23 @@ static void assign_adjust(LexState *ls, BCReg nvars, BCReg nexps, ExpDesc *e) { FuncState *fs = ls->fs; int32_t extra = (int32_t)nvars - (int32_t)nexps; - if (e->k == VCALL) { + if (e->k == VCALL || e->k == VCALLNAV) { + BCInsLine *ilp = &fs->bcbase[e->u.s.info]; extra++; /* Compensate for the VCALL itself. */ if (extra < 0) extra = 0; - setbc_b(bcptr(fs, e), extra+1); /* Fixup call results. */ + setbc_b(&ilp->ins, extra+1); /* Fixup call results. */ if (extra > 1) bcreg_reserve(fs, (BCReg)extra-1); + if (e->k == VCALLNAV) { /* Safe navigation result. */ + BCPos base = e->u.s.aux; + lj_assertFS((bc_op(ilp[0].ins) == BC_CALL || + bc_op(ilp[0].ins) == BC_CALLM) && + bc_op(ilp[1].ins) == BC_JMP && + bc_op(ilp[2].ins) == BC_KPRI, + "expected CALL|CALLM, JMP, KPRI inside safe navigation"); + setbc_a(&ilp[1].ins, base + extra); /* Fixup JMP nactvar. */ + if (extra > 1) /* Need more nils. Case extra == 0 is harmless. */ + ilp[2].ins = BCINS_AD(BC_KNIL, base, base + extra-1); + } } else { if (e->k != VVOID) expr_tonextreg(fs, e); /* Close last expression. */ @@ -2190,10 +2509,11 @@ static void parse_assignment(LexState *ls, LHSVarList *lh, BCReg nvars) { ExpDesc e; checkcond(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, LJ_ERR_XSYNTAX); + var_assign(ls, &lh->v); if (lex_opt(ls, ',')) { /* Collect LHS list and recurse upwards. */ LHSVarList vl; vl.prev = lh; - expr_primary(ls, &vl.v); + expr_primary(ls, &vl.v, EXPR_F_NONAV); if (vl.v.k == VLOCAL) assign_hazard(ls, lh, &vl.v); checklimit(ls->fs, ls->level + nvars, LJ_MAX_XLEVEL, "variable names"); @@ -2208,6 +2528,11 @@ static void parse_assignment(LexState *ls, LHSVarList *lh, BCReg nvars) ls->fs->freereg--; e.k = VRELOCABLE; } else { /* Multiple call results. */ + lj_assertLS(bc_op(*bcptr(ls->fs, &e)) == BC_CALL || + bc_op(*bcptr(ls->fs, &e)) == BC_CALLM || + bc_op(*bcptr(ls->fs, &e)) == BC_KPRI, + "unexpected call expression bytecode %d in assignment", + bc_op(*bcptr(ls->fs, &e))); e.u.s.info = e.u.s.aux; /* Base of call is not relocatable. */ e.k = VNONRELOC; } @@ -2227,22 +2552,30 @@ static void parse_call_assign(LexState *ls) { FuncState *fs = ls->fs; LHSVarList vl; - expr_primary(ls, &vl.v); + BCReg xpc = expr_primary_nav(ls, &vl.v, EXPR_F_NORES); if (vl.v.k == VCALL) { /* Function call statement. */ setbc_b(bcptr(fs, &vl.v), 1); /* No results. */ } else { /* Start of an assignment. */ - vl.prev = NULL; - parse_assignment(ls, &vl, 1); + lj_assertFS(vl.v.k != VCALLNAV, "unexpected VCALLNAV in statement"); + /* Safe navigation is incompatible with parallel assignment. */ + checkcond(ls, xpc == NO_JMP || ls->tok != ',', LJ_ERR_XSYNTAX); + if (!parse_compound(ls, &vl.v)) { + vl.prev = NULL; + parse_assignment(ls, &vl, 1); + } } + if (xpc != NO_JMP) jmp_tohere(fs, xpc); } -/* Parse 'local' statement. */ -static void parse_local(LexState *ls) +/* Parse 'local' or 'const' statement. */ +static void parse_local(LexState *ls, int vinfo) { + lj_lex_next(ls); /* Skip local or const. */ if (lex_opt(ls, TK_function)) { /* Local function declaration. */ ExpDesc v, b; FuncState *fs = ls->fs; - var_new(ls, 0, lex_str(ls)); + MSize vidx = var_new(ls, 0, lex_str(ls)); + ls->vstack[vidx].info = (uint8_t)vinfo; expr_init(&v, VLOCAL, fs->freereg); v.u.s.aux = fs->varmap[fs->freereg]; bcreg_reserve(fs, 1); @@ -2256,9 +2589,23 @@ static void parse_local(LexState *ls) } else { /* Local variable declaration. */ ExpDesc e; BCReg nexps, nvars = 0; - do { /* Collect LHS. */ - var_new(ls, nvars++, lex_str(ls)); - } while (lex_opt(ls, ',')); + if (vinfo) { /* Multiple consts need to be checked against each other. */ + VarIndex vhsave[LJ_VINDEX_HSIZE]; + memcpy(vhsave, ls->vhash, sizeof(vhsave)); + do { /* Collect LHS. */ + MSize vidx = var_new(ls, nvars++, lex_str(ls)); + VarInfo *v = &ls->vstack[vidx]; + VarHash hash = var_hash(strref(v->name)); + v->prev = ls->vhash[hash]; /* Temporarily add to hash. */ + ls->vhash[hash] = vidx; + v->info = (uint8_t)vinfo; + } while (lex_opt(ls, ',')); + memcpy(ls->vhash, vhsave, sizeof(vhsave)); /* Restore hash anchors. */ + } else { + do { /* Collect LHS. */ + var_new(ls, nvars++, lex_str(ls)); + } while (lex_opt(ls, ',')); + } if (lex_opt(ls, '=')) { /* Optional RHS. */ nexps = expr_list(ls, &e); } else { /* Or implicitly set to nil. */ @@ -2277,14 +2624,14 @@ static void parse_func(LexState *ls, BCLine line) ExpDesc v, b; int needself = 0; lj_lex_next(ls); /* Skip 'function'. */ - /* Parse function name. */ - var_lookup(ls, &v); - while (ls->tok == '.') /* Multiple dot-separated fields. */ + var_lookup(ls, &v, lex_str(ls)); /* Parse function name. */ + while (lex_opt(ls, '.')) /* Multiple dot-separated fields. */ expr_field(ls, &v); - if (ls->tok == ':') { /* Optional colon to signify method call. */ + if (lex_opt(ls, ':')) { /* Optional colon to signify method call. */ needself = 1; expr_field(ls, &v); } + var_assign(ls, &v); parse_body(ls, &b, needself, line); fs = ls->fs; bcemit_store(fs, &v, &b); @@ -2305,19 +2652,25 @@ static int parse_isend(LexToken tok) } /* Parse 'return' statement. */ -static void parse_return(LexState *ls) +static void parse_return(LexState *ls, int eflags) { BCIns ins; FuncState *fs = ls->fs; - lj_lex_next(ls); /* Skip 'return'. */ fs->flags |= PROTO_HAS_RETURN; - if (parse_isend(ls->tok) || ls->tok == ';') { /* Bare return. */ - ins = BCINS_AD(BC_RET0, 0, 1); + if (!(eflags & EXPR_F_RET1) && (parse_isend(ls->tok) || ls->tok == ';')) { + ins = BCINS_AD(BC_RET0, 0, 1); /* Bare return. */ } else { /* Return with one or more values. */ ExpDesc e; /* Receives the _last_ expression in the list. */ - BCReg nret = expr_list(ls, &e); + BCReg nret; + if ((eflags & EXPR_F_RET1)) { + expr(ls, &e, eflags); + nret = 1; + } else { + nret = expr_list(ls, &e); + } if (nret == 1) { /* Return one result. */ - if (e.k == VCALL) { /* Check for tail call. */ + /* Check for tail call. */ + if (e.k == VCALL) { #ifdef LUAJIT_DISABLE_TAILCALL goto notailcall; #else @@ -2331,7 +2684,8 @@ static void parse_return(LexState *ls) ins = BCINS_AD(BC_RET1, expr_toanyreg(fs, &e), 2); } } else { - if (e.k == VCALL) { /* Append all results from a call. */ + if (e.k == VCALL) { + /* Append all results from a call. */ notailcall: setbc_b(bcptr(fs, &e), 0); ins = BCINS_AD(BC_RETM, fs->nactvar, e.u.s.aux - fs->nactvar); @@ -2353,6 +2707,13 @@ static void parse_break(LexState *ls) gola_new(ls, NAME_BREAK, VSTACK_GOTO, bcemit_jmp(ls->fs)); } +/* Parse 'continue' statement. */ +static void parse_continue(LexState *ls) +{ + ls->fs->bl->flags |= FSCOPE_CONT; + gola_new(ls, NAME_CONT, VSTACK_GOTO, bcemit_jmp(ls->fs)); +} + /* Parse 'goto' statement. */ static void parse_goto(LexState *ls) { @@ -2424,6 +2785,7 @@ static void parse_while(LexState *ls, BCLine line) parse_block(ls); jmp_patch(fs, bcemit_jmp(fs), start); lex_match(ls, TK_end, TK_while, line); + fscope_continue(fs, start); fscope_end(fs); jmp_tohere(fs, condexit); jmp_patchins(fs, loop, fs->pc); @@ -2442,6 +2804,7 @@ static void parse_repeat(LexState *ls, BCLine line) bcemit_AD(fs, BC_LOOP, fs->nactvar, 0); parse_chunk(ls); lex_match(ls, TK_until, TK_repeat, line); + fscope_continue(fs, fs->pc); condexit = expr_cond(ls); /* Parse condition (still inside inner scope). */ if (!(bl2.flags & FSCOPE_UPVAL)) { /* No upvalues? Just end inner scope. */ fscope_end(fs); @@ -2487,6 +2850,7 @@ static void parse_for_num(LexState *ls, GCstr *varname, BCLine line) bcreg_reserve(fs, 1); parse_block(ls); fscope_end(fs); + fscope_continue(fs, fs->pc); /* Perform loop inversion. Loop control instructions are at the end. */ loopend = bcemit_AJ(fs, BC_FORL, base, NO_JMP); fs->bcbase[loopend].line = line; /* Fix line for control ins. */ @@ -2562,6 +2926,7 @@ static void parse_for_iter(LexState *ls, GCstr *indexname) fscope_end(fs); /* Perform loop inversion. Loop control instructions are at the end. */ jmp_patchins(fs, loop, fs->pc); + fscope_continue(fs, fs->pc); bcemit_ABC(fs, isnext ? BC_ITERN : BC_ITERC, base, nvars-3+1, 2+1); loopend = bcemit_AJ(fs, BC_ITERL, base, NO_JMP); fs->bcbase[loopend-1].line = line; /* Fix line for control ins. */ @@ -2651,16 +3016,28 @@ static int parse_stmt(LexState *ls) parse_func(ls, line); break; case TK_local: - lj_lex_next(ls); - parse_local(ls); + parse_local(ls, 0); break; + case TK_const: { + LexToken tokx = lj_lex_lookahead(ls); + if (!(lex_isname(tokx) || tokx == TK_function)) + goto assign; /* Soft keyword. */ + parse_local(ls, VSTACK_CONST); + break; + } case TK_return: - parse_return(ls); + lj_lex_next(ls); + parse_return(ls, 0); return 1; /* Must be last. */ case TK_break: lj_lex_next(ls); parse_break(ls); return !LJ_52; /* Must be last in Lua 5.1. */ + case TK_continue: + if (!parse_isend(lj_lex_lookahead(ls))) goto assign; /* Soft keyword. */ + lj_lex_next(ls); + parse_continue(ls); + return 1; /* Must be last. */ #if LJ_52 case ';': lj_lex_next(ls); @@ -2670,13 +3047,14 @@ static int parse_stmt(LexState *ls) parse_label(ls); break; case TK_goto: - if (LJ_52 || lj_lex_lookahead(ls) == TK_name) { + if (LJ_52 || lex_isname(lj_lex_lookahead(ls))) { /* 5.1 soft keyword. */ lj_lex_next(ls); parse_goto(ls); break; } /* fallthrough */ default: + assign: parse_call_assign(ls); break; } @@ -2714,6 +3092,7 @@ GCproto *lj_parse(LexState *ls) setstrV(L, L->top, ls->chunkname); /* Anchor chunkname string. */ incr_top(L); ls->level = 0; + memset(ls->vhash, 0xff, sizeof(ls->vhash)); fs_init(ls, &fs); fs.linedefined = 0; fs.numparams = 0; diff --git a/libs/LuaJIT/src/lj_profile.c b/libs/LuaJIT/src/lj_profile.c index 5b2a2b6c..ea65fcb8 100644 --- a/libs/LuaJIT/src/lj_profile.c +++ b/libs/LuaJIT/src/lj_profile.c @@ -359,6 +359,7 @@ LUA_API void luaJIT_profile_stop(lua_State *L) ProfileState *ps = &profile_state; global_State *g = ps->g; if (G(L) == g) { /* Only stop profiler if started by this VM. */ + ps->g = NULL; profile_timer_stop(ps); g->hookmask &= ~HOOK_PROFILE; lj_dispatch_update(g, 0); @@ -368,7 +369,6 @@ LUA_API void luaJIT_profile_stop(lua_State *L) #endif lj_buf_free(g, &ps->sb); ps->sb.w = ps->sb.e = NULL; - ps->g = NULL; } } diff --git a/libs/LuaJIT/src/lj_record.c b/libs/LuaJIT/src/lj_record.c index 8a123a61..6afe2035 100644 --- a/libs/LuaJIT/src/lj_record.c +++ b/libs/LuaJIT/src/lj_record.c @@ -17,6 +17,7 @@ #include "lj_frame.h" #if LJ_HASFFI #include "lj_ctype.h" +#include "lj_crecord.h" #endif #include "lj_bc.h" #include "lj_ff.h" @@ -2505,6 +2506,45 @@ void lj_record_ins(jit_State *J) rc = rec_mm_arith(J, &ix, MM_pow); break; + /* -- Bit operators ----------------------------------------------------- */ + + case BC_BNOT: +#if LJ_HASFFI + if (tref_iscdata(rc)) { + rc = recff_bit64_bitop(J, rc, 0, rcv, NULL, IR_BNOT); + break; + } +#endif + rc = lj_opt_narrow_tobit(J, rc); + rc = emitir(IRTI(IR_BNOT), rc, 0); + break; + + case BC_BAND: case BC_BOR: case BC_BXOR: +#if LJ_HASFFI + if (tref_iscdata(rb) || tref_iscdata(rc)) { + rc = recff_bit64_bitop(J, rb, rc, rbv, rcv, (int)op - (int)BC_BAND + (int)IR_BAND); + break; + } +#endif + recbit: + rb = lj_opt_narrow_tobit(J, rb); + rc = lj_opt_narrow_tobit(J, rc); + rc = emitir(IRTI((int)op - (int)BC_BAND + (int)IR_BAND), rb, rc); + break; + + case BC_BSHL: case BC_BSHR: case BC_BSAR: +#if LJ_HASFFI + { + TRef xrb = rb, xrc = rc; + if (recff_bit64_shift(J, &xrb, &xrc, rbv, rcv, (int)op - (int)BC_BSHL + (int)IR_BSHL)) { + rc = xrb; + break; + } + rc = xrc; /* Shift amount may have been converted. */ + } +#endif + goto recbit; + /* -- Miscellaneous ops ------------------------------------------------- */ case BC_CAT: diff --git a/libs/LuaJIT/src/vm_arm.dasc b/libs/LuaJIT/src/vm_arm.dasc index d67dbffc..d9b2bf66 100644 --- a/libs/LuaJIT/src/vm_arm.dasc +++ b/libs/LuaJIT/src/vm_arm.dasc @@ -848,6 +848,26 @@ static void build_subroutines(BuildCtx *ctx) #else | b ->vmeta_binop // Binop call for compatibility. #endif + | + |//-- Bit operator metamethods ------------------------------------------- + | + |->vmeta_bnot: + | add CARG3, BASE, RC + | mov CARG4, CARG3 + | b >1 + | + |->vmeta_bitop: + | add CARG3, BASE, RB + | add CARG4, BASE, RC + |1: + | decode_OP OP, INS + | add CARG2, BASE, RA + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | str OP, ARG5 + | bl extern lj_meta_bitop // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | b ->cont_nop | |//-- Call metamethod ---------------------------------------------------- | @@ -3376,6 +3396,61 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | ins_next3 break; + /* -- Bit ops ----------------------------------------------------------- */ + + case BC_BNOT: + | // RA = dst, RC = src + | lsl RC, RC, #3 + | ldrd CARG12, [BASE, RC] + | checktp CARG1, LJ_TISNUM + | bne ->vmeta_bnot + | mvn CARG1, CARG1 + | mvn CARG2, #~LJ_TISNUM + | ins_next1 + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + break; + + |.macro ins_bitop, ins, shop + | decode_RB8 RB, INS + | decode_RC8 RC, INS + | // RA = dst*8, RB = src1*8, RC = src2*8 + | ldrd CARG12, [BASE, RB] + | ldrd CARG34, [BASE, RC] + | checktp CARG2, LJ_TISNUM + | checktpeq CARG4, LJ_TISNUM + | bne ->vmeta_bitop + |.if shop == 1 + | and CARG3, CARG3, #31 + |.endif + | ins CARG1, CARG1, CARG3 + | mvn CARG2, #~LJ_TISNUM + | ins_next1 + | ins_next2 + | strd CARG12, [BASE, RA] + | ins_next3 + |.endmacro + + case BC_BAND: + | ins_bitop and, 0 + break; + case BC_BOR: + | ins_bitop orr, 0 + break; + case BC_BXOR: + | ins_bitop eor, 0 + break; + case BC_BSHL: + | ins_bitop lsl, 1 + break; + case BC_BSHR: + | ins_bitop lsr, 1 + break; + case BC_BSAR: + | ins_bitop asr, 1 + break; + /* -- Constant ops ------------------------------------------------------ */ case BC_KSTR: diff --git a/libs/LuaJIT/src/vm_arm64.dasc b/libs/LuaJIT/src/vm_arm64.dasc index be8b76a7..60452fe5 100644 --- a/libs/LuaJIT/src/vm_arm64.dasc +++ b/libs/LuaJIT/src/vm_arm64.dasc @@ -920,6 +920,25 @@ static void build_subroutines(BuildCtx *ctx) #else | b ->vmeta_binop // Binop call for compatibility. #endif + | + |//-- Bit operator metamethods ------------------------------------------- + | + |->vmeta_bnot: + | add CARG3, BASE, RC, lsl #3 + | mov CARG4, CARG3 + | b >1 + | + |->vmeta_bitop: + | add CARG3, BASE, RB, lsl #3 + | add CARG4, BASE, RC, lsl #3 + |1: + | uxtb CARG5w, INSw + | add CARG2, BASE, RA, lsl #3 + | str BASE, L->base + | mov CARG1, L + | str PC, SAVE_PC + | bl extern lj_meta_bitop // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | b ->cont_nop | |//-- Call metamethod ---------------------------------------------------- | @@ -2878,6 +2897,51 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | ins_next break; + /* -- Bit ops ----------------------------------------------------------- */ + + case BC_BNOT: + | // RA = dst, RC = src + | ldr CARG1, [BASE, RC, lsl #3] + | checkint CARG1, ->vmeta_bnot + | mvn CARG1w, CARG1w + | add_TISNUM CARG1, CARG1 + | str CARG1, [BASE, RA, lsl #3] + | ins_next + break; + + |.macro ins_bitop, ins + | decode_RB RB, INS + | and RC, RC, #255 + | // RA = dst, RB = src1, RC = src2 + | ldr CARG1, [BASE, RB, lsl #3] + | ldr CARG2, [BASE, RC, lsl #3] + | checkint CARG1, ->vmeta_bitop + | checkint CARG2, ->vmeta_bitop + | ins CARG1w, CARG1w, CARG2w + | add_TISNUM CARG1, CARG1 + | str CARG1, [BASE, RA, lsl #3] + | ins_next + |.endmacro + + case BC_BAND: + | ins_bitop and + break; + case BC_BOR: + | ins_bitop orr + break; + case BC_BXOR: + | ins_bitop eor + break; + case BC_BSHL: + | ins_bitop lsl + break; + case BC_BSHR: + | ins_bitop lsr + break; + case BC_BSAR: + | ins_bitop asr + break; + /* -- Constant ops ------------------------------------------------------ */ case BC_KSTR: diff --git a/libs/LuaJIT/src/vm_mips.dasc b/libs/LuaJIT/src/vm_mips.dasc index 9a39edd6..40e15e88 100644 --- a/libs/LuaJIT/src/vm_mips.dasc +++ b/libs/LuaJIT/src/vm_mips.dasc @@ -981,6 +981,25 @@ static void build_subroutines(BuildCtx *ctx) | b ->vmeta_binop // Binop call for compatibility. |. nop #endif + | + |//-- Bit operator metamethods ------------------------------------------- + | + |->vmeta_bnot: + | move RC, RB + | + |->vmeta_bitop: + | load_got lj_meta_bitop + | decode_OP1 TMP0, INS + | sw BASE, L->base + | move CARG2, RA + | sw PC, SAVE_PC + | move CARG3, RB + | move CARG4, RC + | sw TMP0, ARG5 + | call_intern lj_meta_bitop // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + |. move CARG1, L + | b ->cont_nop + |. nop | |//-- Call metamethod ---------------------------------------------------- | @@ -3886,6 +3905,62 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | ins_next2 break; + /* -- Bit ops ----------------------------------------------------------- */ + + case BC_BNOT: + | // RA = dst*8, RD = src*8 + | addu RB, BASE, RD + | lw TMP0, HI(RB) + | lw CRET1, LO(RB) + | bne TMP0, TISNUM, ->vmeta_bnot + |. addu RA, BASE, RA + | not CRET1, CRET1 + | ins_next1 + | sw TISNUM, HI(RA) + | sw CRET1, LO(RA) + | ins_next2 + break; + + |.macro ins_bitop, ins + | // RA = dst*8, RB = src1*8, RC = src2*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | addu RB, BASE, RB + | addu RC, BASE, RC + | lw TMP0, HI(RB) + | lw TMP1, HI(RC) + | lw CRET1, LO(RB) + | bne TMP0, TISNUM, ->vmeta_bitop + |. addu RA, BASE, RA + | lw CRET2, LO(RC) + | bne TMP1, TISNUM, ->vmeta_bitop + |. ins CRET1, CRET1, CRET2 + | ins_next1 + | sw TISNUM, HI(RA) + | sw CRET1, LO(RA) + | ins_next2 + |.endmacro + + case BC_BAND: + | ins_bitop and + break; + case BC_BOR: + | ins_bitop or + break; + case BC_BXOR: + | ins_bitop xor + break; + case BC_BSHL: + | ins_bitop sllv + break; + case BC_BSHR: + | ins_bitop srlv + break; + case BC_BSAR: + | ins_bitop srav + break; + /* -- Constant ops ------------------------------------------------------ */ case BC_KSTR: diff --git a/libs/LuaJIT/src/vm_mips64.dasc b/libs/LuaJIT/src/vm_mips64.dasc index 41587e14..e4a710b4 100644 --- a/libs/LuaJIT/src/vm_mips64.dasc +++ b/libs/LuaJIT/src/vm_mips64.dasc @@ -1027,6 +1027,24 @@ static void build_subroutines(BuildCtx *ctx) | b ->vmeta_binop // Binop call for compatibility. |. nop #endif + | + |//-- Bit operator metamethods ------------------------------------------- + | + |->vmeta_bnot: + | move RC, RB + | + |->vmeta_bitop: + | load_got lj_meta_bitop + | sd BASE, L->base + | move CARG2, RA + | sd PC, SAVE_PC + | move CARG3, RB + | move CARG4, RC + | decode_OP1 CARG5, INS // CARG5 == RB. + | call_intern lj_meta_bitop // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + |. move CARG1, L + | b ->cont_nop + |. nop | |//-- Call metamethod ---------------------------------------------------- | @@ -4113,6 +4131,65 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | ins_next2 break; + /* -- Bit ops ----------------------------------------------------------- */ + + case BC_BNOT: + | // RA = dst*8, RD = src*8 + | daddu RB, BASE, RD + | ld CRET1, 0(RB) + | gettp TMP0, CRET1 + | bne TMP0, TISNUM, ->vmeta_bnot + |. daddu RA, BASE, RA + | not CRET1, CRET1 + | zextw CRET1, CRET1 + | settp CRET1, TISNUM + | ins_next1 + | sd CRET1, 0(RA) + | ins_next2 + break; + + |.macro ins_bitop, ins + | // RA = dst*8, RB = table*8, RC = key*8 + | decode_RB8a RB, INS + | decode_RB8b RB + | decode_RDtoRC8 RC, RD + | daddu RB, BASE, RB + | daddu RC, BASE, RC + | ld CRET1, 0(RB) + | ld CRET2, 0(RC) + | gettp TMP0, CRET1 + | daddu RA, BASE, RA + | bne TMP0, TISNUM, ->vmeta_bitop + |. gettp TMP1, CRET2 + | sextw CRET1, CRET1 + | bne TMP1, TISNUM, ->vmeta_bitop + |. ins CRET1, CRET1, CRET2 + | zextw CRET1, CRET1 + | settp CRET1, TISNUM + | ins_next1 + | sd CRET1, 0(RA) + | ins_next2 + |.endmacro + + case BC_BAND: + | ins_bitop and + break; + case BC_BOR: + | ins_bitop or + break; + case BC_BXOR: + | ins_bitop xor + break; + case BC_BSHL: + | ins_bitop sllv + break; + case BC_BSHR: + | ins_bitop srlv + break; + case BC_BSAR: + | ins_bitop srav + break; + /* -- Constant ops ------------------------------------------------------ */ case BC_KSTR: diff --git a/libs/LuaJIT/src/vm_ppc.dasc b/libs/LuaJIT/src/vm_ppc.dasc index 440bf1c4..8d257fdf 100644 --- a/libs/LuaJIT/src/vm_ppc.dasc +++ b/libs/LuaJIT/src/vm_ppc.dasc @@ -1268,6 +1268,25 @@ static void build_subroutines(BuildCtx *ctx) #else | b ->vmeta_binop // Binop call for compatibility. #endif + | + |//-- Bit operator metamethods ------------------------------------------- + | + |->vmeta_bnot: + | mr CARG3, RD + | mr CARG4, RD + | b >1 + | + |->vmeta_bitop: + | mr CARG3, RB + | mr CARG4, RC + |1: + | add CARG2, BASE, RA + | stp BASE, L->base + | mr CARG1, L + | stw PC, SAVE_PC + | decode_OP1 CARG5, INS // Caveat: CARG5 overlaps INS. + | bl extern lj_meta_bitop // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | b ->cont_nop | |//-- Call metamethod ---------------------------------------------------- | @@ -4357,6 +4376,87 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | ins_next2 break; + /* -- Bit ops ----------------------------------------------------------- */ + + case BC_BNOT: + | lwzux CARG1, RD, BASE + |.if DUALNUM + | lwz CARG2, 4(RD) + | checknum cr0, CARG1 + | bne ->vmeta_bnot + |.else + | lfd FARG1, 0(RD) + | checknum cr0, CARG1 + | bge ->vmeta_bnot + | fadd FARG1, FARG1, TOBIT + | stfd FARG1, TMPD + | lwz CARG2, TMPD_LO + |.endif + | not CARG2, CARG2 + |7: + |.if DUALNUM + | ins_next1 + | stwux TISNUM, RA, BASE + | stw CARG2, 4(RA) + |.else + | tonum_u FARG1, CARG2 + | ins_next1 + | stfdx FARG1, BASE, RA + |.endif + | ins_next2 + break; + + |.macro ins_bitop, ins, shmod + | // RA = dst*8, RB = src1*8, RC = src2*8 + | lwzux CARG1, RB, BASE + | lwzux CARG3, RC, BASE + |.if DUALNUM + | lwz CARG2, 4(RB) + | lwz CARG4, 4(RC) + | checknum cr0, CARG1 + | checknum cr1, CARG3 + | bne ->vmeta_bitop + | bne cr1, ->vmeta_bitop + |.else + | lfd FARG1, 0(RB) + | lfd FARG2, 0(RC) + | checknum cr0, CARG1 + | checknum cr1, CARG3 + | bge ->vmeta_bitop + | bge cr1, ->vmeta_bitop + | fadd FARG1, FARG1, TOBIT + | fadd FARG2, FARG2, TOBIT + | stfd FARG1, TMPD + | lwz CARG2, TMPD_LO + | stfd FARG2, TMPD + | lwz CARG4, TMPD_LO + |.endif + |.if shmod == 1 + | rlwinm CARG4, CARG4, 0, 27, 31 + |.endif + | ins CARG2, CARG2, CARG4 + | b <7 + |.endmacro + + case BC_BAND: + | ins_bitop and, 0 + break; + case BC_BOR: + | ins_bitop or, 0 + break; + case BC_BXOR: + | ins_bitop xor, 0 + break; + case BC_BSHL: + | ins_bitop slw, 1 + break; + case BC_BSHR: + | ins_bitop srw, 1 + break; + case BC_BSAR: + | ins_bitop sraw, 1 + break; + /* -- Constant ops ------------------------------------------------------ */ case BC_KSTR: diff --git a/libs/LuaJIT/src/vm_x64.dasc b/libs/LuaJIT/src/vm_x64.dasc index 16af1f29..5769d1cf 100644 --- a/libs/LuaJIT/src/vm_x64.dasc +++ b/libs/LuaJIT/src/vm_x64.dasc @@ -1092,6 +1092,47 @@ static void build_subroutines(BuildCtx *ctx) #else | jmp ->vmeta_binop // Binop call for compatibility. #endif + | + |//-- Bit operator metamethods ------------------------------------------- + | + |->vmeta_bnot: + | mov RB, RA + | lea RC, [BASE+RD*8] + | mov RA, RC + | jmp >2 + | + |// Caveat: ra=RB rb=RC rc=RA. + |->vmeta_bitop: + |.if DUALNUM + | movzx RCd, PC_RB + | movzx RAd, PC_RC + |.endif + | lea RC, [BASE+RC*8] + | lea RA, [BASE+RA*8] + |2: + |.if X64WIN + | mov CARG3, RC + | mov CARG4, RA + | lea RA, [BASE+RB*8] + | movzx RC, PC_OP + | mov ARG5, RC + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2d == BASE. + | mov CARG2, RA + | mov CARG1, L:RB // Caveat: CARG1d == RA. + |.else + | lea CARG2, [BASE+RB*8] + | // CARG4 == RA. + | movzx CARG5, PC_OP + | mov L:CARG1, SAVE_L + | mov L:CARG1->base, BASE // Caveat: CARG3d == BASE. + | mov CARG3, RC + | mov L:RB, L:CARG1 + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_bitop // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | mov BASE, L:RB->base + | jmp ->cont_nop | |//-- Call metamethod ---------------------------------------------------- | @@ -3416,7 +3457,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) |.if DUALNUM | ins_arithdn intins |.else - | ins_arith, sseins + | ins_arith sseins |.endif |.endmacro @@ -3476,6 +3517,88 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | ins_next break; + /* -- Bit ops ----------------------------------------------------------- */ + + case BC_BNOT: + | ins_AD // RA = dst, RD = src + |.if DUALNUM + | mov RB, [BASE+RD*8] + | checkint RB, ->vmeta_bnot + | not RBd + | setint RB + | mov [BASE+RA*8], RB + | ins_next + |.else + | checknumtp [BASE+RD*8], ->vmeta_bnot + | movsd xmm0, qword [BASE+RD*8] + | sseconst_tobit xmm1, RB + | addsd xmm0, xmm1 + | movd RBd, xmm0 + | not RBd + | cvtsi2sd xmm0, RBd + | movsd qword [BASE+RA*8], xmm0 + | ins_next + |.endif + break; + + |.macro ins_bitop, ins, shift + | ins_A // Really ins_ABC. RB = dst, RC = src1, RA = src2 + | // Swap registers around to avoid reloading RA. + | mov RB, RA + | movzx RAd, RCL // Really src2 (C). + | movzx RCd, RCH // Really src1 (B). + |.if DUALNUM + | mov RA, [BASE+RA*8] + | mov RC, [BASE+RC*8] + | checkint RA, ->vmeta_bitop + | checkint RC, ->vmeta_bitop + |.if shift == 1 + | ins RCd, cl // Assumes RA is ecx. + |.else + | ins RCd, RAd + |.endif + | setint RC + | mov [BASE+RB*8], RC + |.else + | checknumtp [BASE+RA*8], ->vmeta_bitop + | checknumtp [BASE+RC*8], ->vmeta_bitop + | movsd xmm0, qword [BASE+RA*8] + | movsd xmm1, qword [BASE+RC*8] + | sseconst_tobit xmm2, RC + | addsd xmm0, xmm2 + | addsd xmm1, xmm2 + | movd RAd, xmm0 + | movd RCd, xmm1 + |.if shift == 1 + | ins RCd, cl // Assumes RA is ecx. + |.else + | ins RCd, RAd + |.endif + | cvtsi2sd xmm0, RCd + | movsd qword [BASE+RB*8], xmm0 + |.endif + | ins_next + |.endmacro + + case BC_BAND: + | ins_bitop and, 0 + break; + case BC_BOR: + | ins_bitop or, 0 + break; + case BC_BXOR: + | ins_bitop xor, 0 + break; + case BC_BSHL: + | ins_bitop shl, 1 + break; + case BC_BSHR: + | ins_bitop shr, 1 + break; + case BC_BSAR: + | ins_bitop sar, 1 + break; + /* -- Constant ops ------------------------------------------------------ */ case BC_KSTR: diff --git a/libs/LuaJIT/src/vm_x86.dasc b/libs/LuaJIT/src/vm_x86.dasc index 2c938656..c930168a 100644 --- a/libs/LuaJIT/src/vm_x86.dasc +++ b/libs/LuaJIT/src/vm_x86.dasc @@ -1337,6 +1337,53 @@ static void build_subroutines(BuildCtx *ctx) #else | jmp ->vmeta_binop // Binop call for compatibility. #endif + | + |//-- Bit operator metamethods ------------------------------------------- + | + |->vmeta_bnot: + | mov RB, RA + | lea RC, [BASE+RD*8] + | mov RA, RC + | jmp >2 + | + |// Caveat: ra=RB rb=RC rc=RA. + |->vmeta_bitop: + | lea RC, [BASE+RC*8] + | lea RA, [BASE+RA*8] + |2: + |.if X64WIN + | mov CARG3d, RC + | mov CARG4d, RA + | lea RA, [BASE+RB*8] + | movzx RC, PC_OP + | mov ARG5d, RC + | mov L:RB, SAVE_L + | mov L:RB->base, BASE // Caveat: CARG2d == BASE. + | mov CARG2d, RA + | mov CARG1d, L:RB // Caveat: CARG1d == RA. + |.elif X64 + | lea CARG2, [BASE+RB*8] + | // CARG4d == RA. + | movzx CARG5d, PC_OP + | mov L:CARG1d, SAVE_L + | mov L:CARG1d->base, BASE // Caveat: CARG3d == BASE. + | mov CARG3d, RC + | mov L:RB, L:CARG1d + |.else + | lea RB, [BASE+RB*8] + | mov ARG3, RC + | movzx RC, PC_OP + | mov ARG2, RB + | mov L:RB, SAVE_L + | mov ARG4, RA + | mov ARG5, RC + | mov ARG1, L:RB + | mov L:RB->base, BASE + |.endif + | mov SAVE_PC, PC + | call extern lj_meta_bitop // (lua_State *L, TValue *ra,*rb,*rc, BCReg op) + | mov BASE, L:RB->base + | jmp ->cont_nop | |//-- Call metamethod ---------------------------------------------------- | @@ -4006,7 +4053,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) |.if DUALNUM | ins_arithdn intins |.else - | ins_arith, sseins + | ins_arith sseins |.endif |.endmacro @@ -4092,6 +4139,89 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | ins_next break; + /* -- Bit ops ----------------------------------------------------------- */ + + case BC_BNOT: + | ins_AD // RA = dst, RD = src + |.if DUALNUM + | checkint RD, ->vmeta_bnot + | mov RB, [BASE+RD*8] + | not RB + | mov dword [BASE+RA*8+4], LJ_TISNUM + | mov dword [BASE+RA*8], RB + | ins_next + |.else + | checknum RD, ->vmeta_bnot + | movsd xmm0, qword [BASE+RD*8] + | sseconst_tobit xmm1, RBa + | addsd xmm0, xmm1 + | movd RB, xmm0 + | not RB + | cvtsi2sd xmm0, RB + | movsd qword [BASE+RA*8], xmm0 + | ins_next + |.endif + break; + + |.macro ins_bitop, ins, shift + | ins_A // Really ins_ABC. RB = dst, RC = src1, RA = src2 + | // Swap registers around to avoid reloading RA. + | mov RB, RA + | movzx RA, RCL // Really src2 (C). + | movzx RC, RCH // Really src1 (B). + |.if DUALNUM + | checkint RA, ->vmeta_bitop + | checkint RC, ->vmeta_bitop + |.if shift == 1 + | mov RA, [BASE+RA*8] + | mov RC, [BASE+RC*8] + | ins RC, cl // Assumes RA is ecx. + |.else + | mov RC, [BASE+RC*8] + | ins RC, [BASE+RA*8] + |.endif + | mov dword [BASE+RB*8+4], LJ_TISNUM + | mov dword [BASE+RB*8], RC + |.else + | checknum RA, ->vmeta_bitop + | checknum RC, ->vmeta_bitop + | movsd xmm0, qword [BASE+RA*8] + | movsd xmm1, qword [BASE+RC*8] + | sseconst_tobit xmm2, RCa + | addsd xmm0, xmm2 + | addsd xmm1, xmm2 + | movd RA, xmm0 + | movd RC, xmm1 + |.if shift == 1 + | ins RC, cl // Assumes RA is ecx. + |.else + | ins RC, RA + |.endif + | cvtsi2sd xmm0, RC + | movsd qword [BASE+RB*8], xmm0 + |.endif + | ins_next + |.endmacro + + case BC_BAND: + | ins_bitop and, 0 + break; + case BC_BOR: + | ins_bitop or, 0 + break; + case BC_BXOR: + | ins_bitop xor, 0 + break; + case BC_BSHL: + | ins_bitop shl, 1 + break; + case BC_BSHR: + | ins_bitop shr, 1 + break; + case BC_BSAR: + | ins_bitop sar, 1 + break; + /* -- Constant ops ------------------------------------------------------ */ case BC_KSTR: