From 72d2061ae2fa9a5fd45237943f9982baf59435ec Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Sat, 13 Jun 2026 10:59:29 +0200 Subject: [PATCH 01/34] Fix corner case of os.time(). Thanks to Temir Galeev. #1470 --- src/lib_os.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib_os.c b/src/lib_os.c index 60b7b82d61..4715e64cd9 100644 --- a/src/lib_os.c +++ b/src/lib_os.c @@ -237,9 +237,10 @@ 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)) + if (t == (time_t)(-1) && errno != 0) lua_pushnil(L); else lua_pushnumber(L, (lua_Number)t); From d2c1327f57dc96f6ed8b11474f3bc5e09a9d227a Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Tue, 16 Jun 2026 11:12:21 +0200 Subject: [PATCH 02/34] Fix stack overflow relimit handling. Thanks to Sergey Kaplun. #1471 --- src/lj_state.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/lj_state.c b/src/lj_state.c index 302c721c19..d1eff61744 100644 --- a/src/lj_state.c +++ b/src/lj_state.c @@ -34,8 +34,9 @@ #define LJ_STACK_MAX LUAI_MAXSTACK /* Max. stack size. */ #define LJ_STACK_START (2*LJ_STACK_MIN) /* Starting stack size. */ #define LJ_STACK_MAXEX (LJ_STACK_MAX + 1 + LJ_STACK_EXTRA) +#define LJ_STACK_ERREX (1 + 2*LJ_STACK_MIN) /* Extra for error handling. */ -/* Explanation of LJ_STACK_EXTRA: +/* Explanation for LJ_STACK_EXTRA: ** ** Calls to metamethods store their arguments beyond the current top ** without checking for the stack limit. This avoids stack resizes which @@ -47,6 +48,11 @@ ** one extra slot if mobj is not a function. Only lj_meta_tset needs 5 ** slots above top, but then mobj is always a function. So we can get by ** with 5 extra slots. +** +** Explanation for LJ_STACK_ERREX: +** +** The 1 is space for the error message, and 2 * LJ_STACK_MIN is for +** the lj_state_checkstack() call in lj_err_run(). */ /* Resize stack slots and adjust pointers in state. */ @@ -78,7 +84,8 @@ static void resizestack(lua_State *L, MSize n) /* Relimit stack after error, in case the limit was overdrawn. */ void lj_state_relimitstack(lua_State *L) { - if (L->stacksize > LJ_STACK_MAXEX && L->top-tvref(L->stack) < LJ_STACK_MAX-1) + if (L->stacksize > LJ_STACK_MAXEX && + L->top-tvref(L->stack) < LJ_STACK_MAX - 1 - LJ_STACK_ERREX) resizestack(L, LJ_STACK_MAX); } @@ -119,11 +126,9 @@ void LJ_FASTCALL lj_state_growstack(lua_State *L, MSize need) /* An error handler might want to inspect the stack overflow error, but ** will need some stack space to run in. We give it a stack size beyond ** the normal limit in order to do so, then rely on lj_state_relimitstack - ** calls during unwinding to bring us back to a convential stack size. - ** The + 1 is space for the error message, and 2 * LUA_MINSTACK is for - ** the lj_state_checkstack() call in lj_err_run(). + ** calls during unwinding to bring us back to a conventional stack size. */ - resizestack(L, LJ_STACK_MAX + 1 + 2 * LUA_MINSTACK); + resizestack(L, LJ_STACK_MAX + LJ_STACK_ERREX); lj_err_stkov(L); /* May invoke an error handler. */ } else { /* If we're here, then the stack overflow error handler is requesting From 8e6520a7aecd0517e792b359afbbfd7274791f5f Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Tue, 16 Jun 2026 11:38:02 +0200 Subject: [PATCH 03/34] Optionally return PC position in jit.util.tracesnap(). Suggested by Sergey Bronnikov. #1472 --- src/lib_jit.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib_jit.c b/src/lib_jit.c index af3e0a6ffd..e6c5271f8e 100644 --- a/src/lib_jit.c +++ b/src/lib_jit.c @@ -340,11 +340,12 @@ LJLIB_CF(jit_util_tracek) return 0; } -/* local snap = jit.util.tracesnap(tr, sn) */ +/* local snap = jit.util.tracesnap(tr, sn[, getpos]) */ LJLIB_CF(jit_util_tracesnap) { GCtrace *T = jit_checktrace(L); SnapNo sn = (SnapNo)lj_lib_checkint(L, 2); + int getpos = (L->base+2 < L->top && tvistruecond(L->base+2)); if (T && sn < T->nsnap) { SnapShot *snap = &T->snap[sn]; SnapEntry *map = &T->snapmap[snap->mapofs]; @@ -357,6 +358,12 @@ LJLIB_CF(jit_util_tracesnap) for (n = 0; n < nent; n++) setintV(lj_tab_setint(L, t, (int32_t)(n+2)), (int32_t)map[n]); setintV(lj_tab_setint(L, t, (int32_t)(nent+2)), (int32_t)SNAP(255, 0, 0)); + if (getpos) { + const BCIns *pc = snap_pc(&map[nent]), *startpc = pc; + while (bc_op(*startpc) < BC_FUNCF) startpc--; + setintV(L->top++, (int)(pc - startpc)); + return 2; + } return 1; } return 0; From 7ff85518540617c0f97c4720558bb21c245994a8 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Wed, 24 Jun 2026 16:20:30 +0200 Subject: [PATCH 04/34] iOS: Avoid macro name collision. Reported by Andrey Filipenkov. #1477 --- src/Makefile | 2 +- src/lj_arch.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index bac0341e8d..0b8c04f710 100644 --- a/src/Makefile +++ b/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/src/lj_arch.h b/src/lj_arch.h index 3c4e3f9b0b..7b7907ef58 100644 --- a/src/lj_arch.h +++ b/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 From 295d45fb26de56498782c94594f31b97aef744ef Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 25 Jun 2026 11:59:34 +0200 Subject: [PATCH 05/34] Make check in os.time() consistent. Thanks to Temir Galeev. #1470 --- src/lib_os.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib_os.c b/src/lib_os.c index 4715e64cd9..dac232ad2b 100644 --- a/src/lib_os.c +++ b/src/lib_os.c @@ -224,6 +224,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 { @@ -237,7 +238,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) From a2bde60819d83e6f75130ac2c93ee4b3c7615800 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 29 Jun 2026 11:40:02 +0200 Subject: [PATCH 06/34] FFI/MacOS: Fix calling convention for on-stack varargs. Thanks to Sergey Kaplun. #1455 --- src/lj_ccall.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lj_ccall.c b/src/lj_ccall.c index c89d97605b..1aae0d40c8 100644 --- a/src/lj_ccall.c +++ b/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; From acb223497d84c65139a7eaaac395b42f112249ac Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Tue, 7 Jul 2026 11:07:19 +0200 Subject: [PATCH 07/34] x64/ARM64/MIPS64: Fix constant bit shift code generation. Reported by Sergey Kaplun. #1480 --- src/lj_asm_arm64.h | 3 ++- src/lj_asm_mips.h | 3 ++- src/lj_asm_x86.h | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lj_asm_arm64.h b/src/lj_asm_arm64.h index dfc5490fa9..d068b153a3 100644 --- a/src/lj_asm_arm64.h +++ b/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/src/lj_asm_mips.h b/src/lj_asm_mips.h index a54af233f2..7565f442eb 100644 --- a/src/lj_asm_mips.h +++ b/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/src/lj_asm_x86.h b/src/lj_asm_x86.h index 3d68baefdc..943cf2b77c 100644 --- a/src/lj_asm_x86.h +++ b/src/lj_asm_x86.h @@ -2318,9 +2318,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. */ From fed6d4782a3939d0b260402e2df5706ca036a290 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Thu, 9 Jul 2026 10:24:06 +0200 Subject: [PATCH 08/34] Don't use destroyed lock when profiler has been stopped. Reported by Miku AuahDark. #1482 #1460 --- src/lj_profile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lj_profile.c b/src/lj_profile.c index 5b2a2b6c72..ea65fcb8f2 100644 --- a/src/lj_profile.c +++ b/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; } } From 5dd996ece5aa20087e37cb8c471aec1f88ca5ca2 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Fri, 10 Jul 2026 10:38:55 +0200 Subject: [PATCH 09/34] DynASM/x86: Fix insertps instruction encoding. Thanks to Dmitry Stogov. #1483 --- dynasm/dasm_x86.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dynasm/dasm_x86.lua b/dynasm/dasm_x86.lua index 0794e1804f..a24570e0ad 100644 --- a/dynasm/dasm_x86.lua +++ b/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", From 859ad934bc3b92125043c3e599492aecd3586992 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Sat, 11 Jul 2026 14:37:49 +0200 Subject: [PATCH 10/34] x64: Fix callback result handling. Reported by Matt Gerassimoff. --- src/lj_ccallback.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lj_ccallback.c b/src/lj_ccallback.c index 3ddb7ca5c4..2fa8e7ce0c 100644 --- a/src/lj_ccallback.c +++ b/src/lj_ccallback.c @@ -519,6 +519,10 @@ static void callback_conv_result(CTState *cts, lua_State *L, TValue *o) #if LJ_TARGET_X86 if (ctype_isfp(ctr->info)) cts->cb.gpr[2] = ctr->size == sizeof(float) ? 1 : 2; +#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 } } From 14d8a7a27dc8c626ab9e7c7e9e50b6df6def4f03 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Sat, 18 Jul 2026 09:48:48 +0200 Subject: [PATCH 11/34] x64/LJ_GC64: Avoid store-to-load forwarding stall after stack restore. Thanks to Sergey Kaplun. #1485 --- src/lj_asm_x86.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lj_asm_x86.h b/src/lj_asm_x86.h index 943cf2b77c..3c024092ca 100644 --- a/src/lj_asm_x86.h +++ b/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)); @@ -2787,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)) { From a2ce8114f107464473070427e69e84f4923bd08a Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 20 Jul 2026 10:20:49 +0200 Subject: [PATCH 12/34] Backport some v3.0 syntax extensions. Documentation in #1475. Backport discussion in #1476. Thanks to Appla, slashOwO, Sergey Kaplun, CppCXY. --- doc/extensions.html | 25 ++ src/Makefile.dep | 9 +- src/lj_bc.h | 11 +- src/lj_bcdump.h | 3 +- src/lj_bcwrite.c | 5 +- src/lj_carith.c | 31 ++ src/lj_carith.h | 1 + src/lj_crecord.c | 35 +- src/lj_crecord.h | 5 +- src/lj_dispatch.h | 1 + src/lj_errmsg.h | 4 + src/lj_ffrecord.c | 2 +- src/lj_ir.h | 2 +- src/lj_lex.c | 49 ++- src/lj_lex.h | 12 +- src/lj_meta.c | 75 +++++ src/lj_meta.h | 2 + src/lj_obj.h | 1 + src/lj_opt_fold.c | 2 +- src/lj_parse.c | 781 ++++++++++++++++++++++++++++++++------------ src/lj_record.c | 40 +++ src/vm_arm.dasc | 75 +++++ src/vm_arm64.dasc | 64 ++++ src/vm_mips.dasc | 75 +++++ src/vm_mips64.dasc | 77 +++++ src/vm_ppc.dasc | 100 ++++++ src/vm_x64.dasc | 125 ++++++- src/vm_x86.dasc | 132 +++++++- 28 files changed, 1510 insertions(+), 234 deletions(-) diff --git a/doc/extensions.html b/doc/extensions.html index 16e88505b4..4668e192e2 100644 --- a/doc/extensions.html +++ b/doc/extensions.html @@ -411,6 +411,31 @@

Extensions from Lua 5.3

+

Backported Syntax Extensions from LuaJIT 3.0

+

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

+
    +
  • Bit Operators: unary ~, binary & | ~ << >> ~>>
  • +
  • Customary Operators: ! && || !=
  • +
  • Ternary ?: conditional operator
  • +
  • Safe Navigation Operator ?.
  • +
  • nil-Coalescing Operator ??
  • +
  • Compound Assignment Operators: += -= *= /= %= &= |= ~= <<= >>= ~>>= ..=
  • +
  • continue Statement
  • +
  • const Declaration
  • +
  • Short Function Expression
  • +
  • Underscores in Number Literals
  • +
+

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

+

C++ Exception Interoperability

LuaJIT has built-in support for interoperating with C++ exceptions. diff --git a/src/Makefile.dep b/src/Makefile.dep index e10a6b29b4..709107c2ec 100644 --- a/src/Makefile.dep +++ b/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/src/lj_bc.h b/src/lj_bc.h index 54d529e340..cbadc9abe3 100644 --- a/src/lj_bc.h +++ b/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/src/lj_bcdump.h b/src/lj_bcdump.h index 074ac0fdac..3d2c95aca5 100644 --- a/src/lj_bcdump.h +++ b/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/src/lj_bcwrite.c b/src/lj_bcwrite.c index a0230eff58..fb557e5882 100644 --- a/src/lj_bcwrite.c +++ b/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/src/lj_carith.c b/src/lj_carith.c index cb408fb808..10f461580a 100644 --- a/src/lj_carith.c +++ b/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/src/lj_carith.h b/src/lj_carith.h index 93fa41f2b7..700e48791b 100644 --- a/src/lj_carith.h +++ b/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/src/lj_crecord.c b/src/lj_crecord.c index 4be36eca6d..a8ea419a81 100644 --- a/src/lj_crecord.c +++ b/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/src/lj_crecord.h b/src/lj_crecord.h index ad99b72542..ad2d3e2526 100644 --- a/src/lj_crecord.h +++ b/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/src/lj_dispatch.h b/src/lj_dispatch.h index 9baf762b20..755590e679 100644 --- a/src/lj_dispatch.h +++ b/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/src/lj_errmsg.h b/src/lj_errmsg.h index daf7fb6bfe..ad8581c999 100644 --- a/src/lj_errmsg.h +++ b/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/src/lj_ffrecord.c b/src/lj_ffrecord.c index edbf13cc24..4349f748f4 100644 --- a/src/lj_ffrecord.c +++ b/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/src/lj_ir.h b/src/lj_ir.h index b9f5e2c13e..879196a524 100644 --- a/src/lj_ir.h +++ b/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/src/lj_lex.c b/src/lj_lex.c index a585cb585a..4bbe183597 100644 --- a/src/lj_lex.c +++ b/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/src/lj_lex.h b/src/lj_lex.h index 8d5c9a3365..e5b78337cd 100644 --- a/src/lj_lex.h +++ b/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/src/lj_meta.c b/src/lj_meta.c index 1e7262a437..ddb37a1a3e 100644 --- a/src/lj_meta.c +++ b/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/src/lj_meta.h b/src/lj_meta.h index 3d6a71c056..28e6c8bb1c 100644 --- a/src/lj_meta.h +++ b/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/src/lj_obj.h b/src/lj_obj.h index f380c78fc7..96dc1e0d07 100644 --- a/src/lj_obj.h +++ b/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/src/lj_opt_fold.c b/src/lj_opt_fold.c index 7fc838373f..cc2b9098cf 100644 --- a/src/lj_opt_fold.c +++ b/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/src/lj_parse.c b/src/lj_parse.c index 66e5a0341a..00a64382c7 100644 --- a/src/lj_parse.c +++ b/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) -{ - 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; +/* Lookup variable name. */ +static MSize var_lookup(LexState *ls, ExpDesc *e, GCstr *name) +{ + 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; - } - } - if ((bl->flags & FSCOPE_GOLA)) { + 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|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/src/lj_record.c b/src/lj_record.c index 8a123a61f6..6afe2035b1 100644 --- a/src/lj_record.c +++ b/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/src/vm_arm.dasc b/src/vm_arm.dasc index d67dbffcc1..d9b2bf6633 100644 --- a/src/vm_arm.dasc +++ b/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/src/vm_arm64.dasc b/src/vm_arm64.dasc index be8b76a7a5..60452fe565 100644 --- a/src/vm_arm64.dasc +++ b/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/src/vm_mips.dasc b/src/vm_mips.dasc index 9a39edd69e..40e15e8885 100644 --- a/src/vm_mips.dasc +++ b/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/src/vm_mips64.dasc b/src/vm_mips64.dasc index 41587e14d2..e4a710b4a1 100644 --- a/src/vm_mips64.dasc +++ b/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/src/vm_ppc.dasc b/src/vm_ppc.dasc index 440bf1c4b0..8d257fdf55 100644 --- a/src/vm_ppc.dasc +++ b/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/src/vm_x64.dasc b/src/vm_x64.dasc index 16af1f2996..5769d1cf5b 100644 --- a/src/vm_x64.dasc +++ b/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/src/vm_x86.dasc b/src/vm_x86.dasc index 2c9386562d..c930168aea 100644 --- a/src/vm_x86.dasc +++ b/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: From 5f627e4114d9b2247cff1e530766312e979d7bdd Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 20 Jul 2026 10:23:09 +0200 Subject: [PATCH 13/34] Document bit operator bytecode compatibility. --- doc/extensions.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/extensions.html b/doc/extensions.html index 4668e192e2..099fd67607 100644 --- a/doc/extensions.html +++ b/doc/extensions.html @@ -435,6 +435,9 @@

Backported Syntax Extensions from LuaJIT 3.0

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

From 2460b3ff93a1c955de3d62cfc825de7d68dc272e Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 20 Jul 2026 22:55:05 +0200 Subject: [PATCH 14/34] Fix documentation about Lua 5.2 extensions/compatibility. Reported by goodusername123. #1487 --- doc/extensions.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/extensions.html b/doc/extensions.html index 099fd67607..73edf11feb 100644 --- a/doc/extensions.html +++ b/doc/extensions.html @@ -377,6 +377,7 @@

Extensions from Lua 5.2

  • 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 @@

    Extensions from Lua 5.2

    exit status.
  • debug.setmetatable() returns object.
  • debug.getuservalue() and debug.setuservalue().
  • -
  • Remove math.mod(), string.gfind().
  • package.searchers.
  • module() returns the module table.
  • From 346ab587cb235b4ef0b5777b4cd29009808d0cc0 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Fri, 24 Jul 2026 16:14:33 +0200 Subject: [PATCH 15/34] DynASM/x86: Fix movd/movq and vmovd/vmovq operand sizes. Thanks to Dmitry Stogov. #1489 --- dynasm/dasm_x86.lua | 8 ++++---- src/vm_x64.dasc | 30 +++++++++++++++--------------- src/vm_x86.dasc | 6 +++--- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/dynasm/dasm_x86.lua b/dynasm/dasm_x86.lua index a24570e0ad..e339b31616 100644 --- a/dynasm/dasm_x86.lua +++ b/dynasm/dasm_x86.lua @@ -1310,7 +1310,7 @@ local map_op = { mfence_0 = "0FAEF0", movapd_2 = "rmo:660F28rM|mro:660F29Rm", movaps_2 = "rmo:0F28rM|mro:0F29Rm", - movd_2 = "rm/od:660F6ErM|rm/oq:660F6ErXM|mr/do:660F7ERm|mr/qo:", + movd_2 = "rm/od:660F6ErM|mr/do:660F7ERm", movdqa_2 = "rmo:660F6FrM|mro:660F7FRm", movdqu_2 = "rmo:F30F6FrM|mro:F30F7FRm", movhlps_2 = "rro:0F12rM", @@ -1325,7 +1325,7 @@ local map_op = { movnti_2 = "xrqd:0FC3Rm", movntpd_2 = "xro:660F2BRm", movntps_2 = "xro:0F2BRm", - movq_2 = "rro:F30F7ErM|rx/oq:|xr/qo:n660FD6Rm", + movq_2 = x64 and "rro:F30F7ErM|rx/oq:|xr/qo:n660FD6Rm|rm/oq:660F6ErXM|mr/qo:660F7ERm" or "rro:F30F7ErM|rx/oq:|xr/qo:n660FD6Rm", movsd_2 = "rro:F20F10rM|rx/oq:|xr/qo:nF20F11Rm", movss_2 = "rro:F30F10rM|rx/od:|xr/do:F30F11Rm", movupd_2 = "rmo:660F10rM|mro:660F11Rm", @@ -1529,8 +1529,8 @@ local map_op = { vmaskmovpd_3 = "rrxoy:660F38V2DrM|xrroy:660F38V2FRm", vmovapd_2 = "rmoy:660Fu28rM|mroy:660Fu29Rm", vmovaps_2 = "rmoy:0Fu28rM|mroy:0Fu29Rm", - vmovd_2 = "rm/od:660Fu6ErM|rm/oq:660FuX6ErM|mr/do:660Fu7ERm|mr/qo:", - vmovq_2 = "rro:F30Fu7ErM|rx/oq:|xr/qo:660FuD6Rm", + vmovd_2 = "rm/od:660Fu6ErM|mr/do:660Fu7ERm", + vmovq_2 = x64 and "rro:F30Fu7ErM|rx/oq:|xr/qo:660FuD6Rm|rm/oq:660FuX6ErM|mr/qo:660Fu7ERm" or "rro:F30Fu7ErM|rx/oq:|xr/qo:660FuD6Rm", vmovddup_2 = "rmy:F20Fu12rM|rro:|rx/oq:", vmovhlps_3 = "rrro:0FV12rM", vmovhpd_2 = "xr/qo:660Fu17Rm", diff --git a/src/vm_x64.dasc b/src/vm_x64.dasc index 5769d1cf5b..2c18acd67f 100644 --- a/src/vm_x64.dasc +++ b/src/vm_x64.dasc @@ -354,11 +354,11 @@ | |// Synthesize SSE FP constants. |.macro sseconst_abs, reg, tmp // Synthesize abs mask. -| mov64 tmp, U64x(7fffffff,ffffffff); movd reg, tmp +| mov64 tmp, U64x(7fffffff,ffffffff); movq reg, tmp |.endmacro | |.macro sseconst_hi, reg, tmp, val // Synthesize hi-32 bit const. -| mov64 tmp, U64x(val,00000000); movd reg, tmp +| mov64 tmp, U64x(val,00000000); movq reg, tmp |.endmacro | |.macro sseconst_sign, reg, tmp // Synthesize sign mask. @@ -2087,7 +2087,7 @@ static void build_subroutines(BuildCtx *ctx) |.endif |1: | ja ->fff_fallback - | movd xmm0, RB + | movq xmm0, RB |.else | checknumtp [BASE], ->fff_fallback | movsd xmm0, qword [BASE] @@ -2122,7 +2122,7 @@ static void build_subroutines(BuildCtx *ctx) | jmp <1 |2: | ja ->fff_fallback_bit_op - | movd xmm0, RA + | movq xmm0, RA |.else | checknumtp [RD], ->fff_fallback_bit_op | movsd xmm0, qword [RD] @@ -2693,7 +2693,7 @@ static void build_subroutines(BuildCtx *ctx) | ret |1: | mov64 rdx, U64x(c3f00000,00000000) // -0x1p64 (double). - | movd xmm1, rdx + | movq xmm1, rdx | addsd xmm0, xmm1 | cvttsd2si rax, xmm0 // Convert [2^63..2^64+2^63) range. | // Note that -0x1p63 converts to -0x8000000000000000LL either way. @@ -3005,16 +3005,16 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | ja ->vmeta_comp | // RA is an integer, RD is a number. | cvtsi2sd xmm1, RAd - | movd xmm0, RD + | movq xmm0, RD | jmp >3 |.else | cmp ITYPEd, LJ_TISNUM; jae ->vmeta_comp | cmp RBd, LJ_TISNUM; jae ->vmeta_comp |.endif |1: - | movd xmm0, RD + | movq xmm0, RD |2: - | movd xmm1, RA + | movq xmm1, RA |3: | add PC, 4 | ucomisd xmm0, xmm1 @@ -3059,7 +3059,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) |7: // RD is not an integer. | ja >5 | // RD is a number. - | movd xmm1, RD + | movq xmm1, RD | cmp ITYPEd, LJ_TISNUM; jb >1; jne >5 | // RD is a number, RA is an integer. | cvtsi2sd xmm0, RAd @@ -3074,10 +3074,10 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) |.else | cmp RBd, LJ_TISNUM; jae >5 | cmp ITYPEd, LJ_TISNUM; jae >5 - | movd xmm1, RD + | movq xmm1, RD |.endif |1: - | movd xmm0, RA + | movq xmm0, RA |2: | ucomisd xmm0, xmm1 |4: @@ -3199,11 +3199,11 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) | |8: // RA is an integer, RD is a number. | cvtsi2sd xmm0, RBd - | movd xmm1, RD + | movq xmm1, RD | ucomisd xmm0, xmm1 | jmp >4 |1: - | movd xmm0, RD + | movq xmm0, RD |.else | checknum RB, >3 |1: @@ -3881,7 +3881,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) |.else | // Convert number to int and back and compare. | checknum RC, >5 - | movd xmm0, RC + | movq xmm0, RC | cvttsd2si RCd, xmm0 | cvtsi2sd xmm1, RCd | ucomisd xmm0, xmm1 @@ -4007,7 +4007,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop) |.else | // Convert number to int and back and compare. | checknum RC, >5 - | movd xmm0, RC + | movq xmm0, RC | cvttsd2si RCd, xmm0 | cvtsi2sd xmm1, RCd | ucomisd xmm0, xmm1 diff --git a/src/vm_x86.dasc b/src/vm_x86.dasc index c930168aea..c201c3343b 100644 --- a/src/vm_x86.dasc +++ b/src/vm_x86.dasc @@ -444,7 +444,7 @@ |// Synthesize SSE FP constants. |.macro sseconst_abs, reg, tmp // Synthesize abs mask. |.if X64 -| mov64 tmp, U64x(7fffffff,ffffffff); movd reg, tmp +| mov64 tmp, U64x(7fffffff,ffffffff); movq reg, tmp |.else | pxor reg, reg; pcmpeqd reg, reg; psrlq reg, 1 |.endif @@ -452,7 +452,7 @@ | |.macro sseconst_hi, reg, tmp, val // Synthesize hi-32 bit const. |.if X64 -| mov64 tmp, U64x(val,00000000); movd reg, tmp +| mov64 tmp, U64x(val,00000000); movq reg, tmp |.else | mov tmp, 0x .. val; movd reg, tmp; pshufd reg, reg, 0x51 |.endif @@ -3156,7 +3156,7 @@ static void build_subroutines(BuildCtx *ctx) | ret |1: | mov64 rdx, U64x(c3f00000,00000000) // -0x1p64 (double). - | movd xmm1, rdx + | movq xmm1, rdx | addsd xmm0, xmm1 | cvttsd2si rax, xmm0 // Convert [2^63..2^64+2^63) range. | // Note that -0x1p63 converts to -0x8000000000000000LL either way. From e4d805163f0942693ae15fee3e3a8bdd9a08554b Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Sat, 25 Jul 2026 20:53:11 +0200 Subject: [PATCH 16/34] FFI: Fix widening semantics for 64 bit arithmetic. Thanks to Frityet. #1492 --- src/lj_crecord.c | 2 +- src/lj_opt_fold.c | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/lj_crecord.c b/src/lj_crecord.c index 4adfefe632..59420184ac 100644 --- a/src/lj_crecord.c +++ b/src/lj_crecord.c @@ -1249,7 +1249,7 @@ static TRef crec_arith_int64(jit_State *J, TRef *sp, CType **s, MMS mm) sp[i] = emitconv(sp[i], dt, st, IRCONV_TRUNC|IRCONV_ANY); else if (!(st == IRT_I64 || st == IRT_U64)) sp[i] = emitconv(sp[i], dt, IRT_INT, - (s[i]->info & CTF_UNSIGNED) ? 0 : IRCONV_SEXT); + ((st - IRT_I8) & 1) ? 0 : IRCONV_SEXT); } if (mm < MM_add) { comp: diff --git a/src/lj_opt_fold.c b/src/lj_opt_fold.c index b8676a577d..03d869d005 100644 --- a/src/lj_opt_fold.c +++ b/src/lj_opt_fold.c @@ -956,14 +956,10 @@ LJFOLDF(simplify_conv_i64_num) fins->op2 = ((IRT_I64<<5)|IRT_INT|IRCONV_SEXT); return RETRYFOLD; } else if ((fleft->op2 & IRCONV_SRCMASK) == IRT_U32) { -#if LJ_TARGET_X64 - return fleft->op1; -#else /* Reduce to a zero-extension. */ fins->op1 = fleft->op1; fins->op2 = (IRT_I64<<5)|IRT_U32; return RETRYFOLD; -#endif } return NEXTFOLD; } From faaf663340347a78b22ed94c63c24fe090bd9784 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Tue, 28 Jul 2026 00:44:24 +0200 Subject: [PATCH 17/34] x86: Conditionally use SSE3 for 64 bit conversions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported by Gero Schwäricke. #1496 #1411 --- src/lib_jit.c | 6 ++++++ src/lj_meta.c | 4 ++-- src/lj_obj.h | 21 ++++++++++++++++++++- src/lj_vmmath.c | 5 +++++ src/vm_x86.dasc | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/lib_jit.c b/src/lib_jit.c index e6c5271f8e..6e01e374fe 100644 --- a/src/lib_jit.c +++ b/src/lib_jit.c @@ -657,6 +657,12 @@ static uint32_t jit_cpudetect(void) uint32_t features[4]; if (lj_vm_cpuid(0, vendor) && lj_vm_cpuid(1, features)) { flags |= ((features[2] >> 0)&1) * JIT_F_SSE3; +#if LJ_TARGET_X86 + if (flags) { + lj_vm_num2i64_ptr = lj_vm_num2i64_sse3; + lj_vm_num2u64_ptr = lj_vm_num2u64_sse3; + } +#endif flags |= ((features[2] >> 19)&1) * JIT_F_SSE4_1; if (vendor[0] >= 7) { uint32_t xfeatures[4]; diff --git a/src/lj_meta.c b/src/lj_meta.c index ddb37a1a3e..5a08cf9471 100644 --- a/src/lj_meta.c +++ b/src/lj_meta.c @@ -248,10 +248,10 @@ void lj_meta_bitop(lua_State *L, TValue *ra, cTValue *rb, cTValue *rc, BCReg op) 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)); + b = id == CTID_UINT64 ? lj_num2u64(numV(rb)) : (uint64_t)lj_num2i64(numV(rb)); } if (tvisnum(rc)) { - c = id == CTID_UINT64 ? lj_num2u64(numV(rc)) : lj_num2i64(numV(rc)); + c = id == CTID_UINT64 ? lj_num2u64(numV(rc)) : (uint64_t)lj_num2i64(numV(rc)); } } switch (op) { diff --git a/src/lj_obj.h b/src/lj_obj.h index 96dc1e0d07..30991a7e9b 100644 --- a/src/lj_obj.h +++ b/src/lj_obj.h @@ -1035,11 +1035,30 @@ LJ_ASMF LJ_CONSTF int64_t lj_vm_num2int_check(double x); ** The uint64_t conversion accepts the union of the unsigned + signed range. */ LJ_ASMF LJ_CONSTF int64_t lj_vm_num2i64(double x); -LJ_ASMF LJ_CONSTF int64_t lj_vm_num2u64(double x); +LJ_ASMF LJ_CONSTF uint64_t lj_vm_num2u64(double x); + +#if LJ_TARGET_X86 + +LJ_ASMF LJ_CONSTF int64_t lj_vm_num2i64_sse3(double x); +LJ_ASMF LJ_CONSTF uint64_t lj_vm_num2u64_sse3(double x); +LJ_ASMF int64_t (*lj_vm_num2i64_ptr)(double x); +LJ_ASMF uint64_t (*lj_vm_num2u64_ptr)(double x); +static LJ_AINLINE int64_t lj_num2i64(double x) +{ + return (*lj_vm_num2i64_ptr)(x); +} +static LJ_AINLINE uint64_t lj_num2u64(double x) +{ + return (*lj_vm_num2u64_ptr)(x); +} + +#else #define lj_num2i64(x) (lj_vm_num2i64((x))) #define lj_num2u64(x) (lj_vm_num2u64((x))) +#endif + /* Lua BitOp conversion semantics use the 2^52 + 2^51 trick. */ LJ_ASMF LJ_CONSTF int32_t lj_vm_tobit(double x); diff --git a/src/lj_vmmath.c b/src/lj_vmmath.c index 5aca720713..603b913e67 100644 --- a/src/lj_vmmath.c +++ b/src/lj_vmmath.c @@ -13,6 +13,11 @@ #include "lj_ir.h" #include "lj_vm.h" +#if LJ_TARGET_X86 +int64_t (*lj_vm_num2i64_ptr)(double x) = lj_vm_num2i64; +uint64_t (*lj_vm_num2u64_ptr)(double x) = lj_vm_num2u64; +#endif + /* -- Wrapper functions --------------------------------------------------- */ #if LJ_TARGET_X86 && __ELF__ && __PIC__ diff --git a/src/vm_x86.dasc b/src/vm_x86.dasc index c201c3343b..50becd4609 100644 --- a/src/vm_x86.dasc +++ b/src/vm_x86.dasc @@ -3140,6 +3140,21 @@ static void build_subroutines(BuildCtx *ctx) |.else | sub esp, 12 | fld qword [esp+16] + | fnstcw word [esp+8] + | mov eax, 0x0c00 + | or ax, word [esp+8] + | mov word [esp+10], ax + | fldcw word [esp+10] + | fistp qword [esp] + | fldcw word [esp+8] + | mov eax, dword [esp] + | mov edx, dword [esp+4] + | add esp, 12 + | ret + | + |->vm_num2i64_sse3: + | sub esp, 12 + | fld qword [esp+16] | fisttp qword [esp] | mov eax, dword [esp] | mov edx, dword [esp+4] @@ -3164,6 +3179,37 @@ static void build_subroutines(BuildCtx *ctx) |.else | sub esp, 12 | fld qword [esp+16] + | fnstcw word [esp+8] + | mov eax, 0x0c00 + | or ax, word [esp+8] + | mov word [esp+10], ax + | fldcw word [esp+10] + | fld st0 + | fistp qword [esp] + | mov edx, dword [esp+4] + | mov eax, dword [esp] + | cmp edx, 1 + | jo >2 + |1: + | fpop + | fldcw word [esp+8] + | add esp, 12 + | ret + |2: + | cmp eax, 0 + | jne <1 + | mov dword [esp], 0xdf800000 // -0x1p64 (float). + | fadd dword [esp] + | fistp qword [esp] + | fldcw word [esp+8] + | mov eax, dword [esp] + | mov edx, dword [esp+4] + | add esp, 12 + | ret + | + |->vm_num2u64_sse3: + | sub esp, 12 + | fld qword [esp+16] | fld st0 | fisttp qword [esp] | mov edx, dword [esp+4] From afa81a57382ce7e747b493220e79573f23205ab0 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Sat, 1 Aug 2026 11:36:13 +0200 Subject: [PATCH 18/34] x64/LJ_GC64: Enable XLOAD/STRREF fusion. --- src/lj_asm_x86.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lj_asm_x86.h b/src/lj_asm_x86.h index 3c024092ca..66914834e5 100644 --- a/src/lj_asm_x86.h +++ b/src/lj_asm_x86.h @@ -344,13 +344,12 @@ static void asm_fusexref(ASMState *as, IRRef ref, RegSet allow) as->mrm.base = RID_DISPATCH; return; } - } if (0) { #else as->mrm.ofs = ir->i; as->mrm.base = RID_NONE; +#endif } else if (ir->o == IR_STRREF) { asm_fusestrref(as, ir, allow); -#endif } else { as->mrm.ofs = 0; if (canfuse(as, ir) && ir->o == IR_ADD && ra_noreg(ir->r)) { From 594472579a9ea43fd2fc65be125570900b8bba12 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Sat, 1 Aug 2026 11:37:20 +0200 Subject: [PATCH 19/34] Optimize common 1-char case of string.sub/string.byte. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported by Kacper MichajÅ‚ow. #1497 --- src/lj_ffrecord.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c index 4349f748f4..ef8e554bd5 100644 --- a/src/lj_ffrecord.c +++ b/src/lj_ffrecord.c @@ -846,6 +846,17 @@ static void LJ_FASTCALL recff_string_range(jit_State *J, RecordFFData *rd) lj_ir_kint(J, 1)); end = end+(int32_t)str->len+1; } else if ((MSize)end <= str->len) { + if (trstart == trend && start != 0) { /* Common 1-char case. */ + TRef trptr, tr = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, -1)); + emitir(IRTGI(IR_ULT), tr, trlen); + trptr = emitir(IRT(IR_STRREF, IRT_PGC), trstr, tr); + if (rd->data) { /* Return string.sub result. */ + J->base[0] = emitir(IRT(IR_SNEW, IRT_STR), trptr, lj_ir_kint(J, 1)); + } else { /* Return string.byte result. */ + J->base[0] = emitir(IRT(IR_XLOAD, IRT_U8), trptr, IRXLOAD_READONLY); + } + return; + } emitir(IRTGI(IR_ULE), trend, trlen); } else { emitir(IRTGI(IR_GT), trend, trlen); From 4886b676a698acc4bbdf54adfabb3e33a8c020e8 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Sat, 1 Aug 2026 11:38:57 +0200 Subject: [PATCH 20/34] FFI: Set cur_L in FFI callback. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to David Komárek. #1498 --- src/lj_ccallback.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lj_ccallback.c b/src/lj_ccallback.c index 2ed84507a1..a7979a5dcd 100644 --- a/src/lj_ccallback.c +++ b/src/lj_ccallback.c @@ -728,6 +728,7 @@ lua_State * LJ_FASTCALL lj_ccallback_enter(CTState *cts, void *cf) exit(EXIT_FAILURE); } lj_trace_abort(g); /* Never record across callback. */ + setgcref(g->cur_L, obj2gco(L)); /* Setup C frame. */ cframe_prev(cf) = L->cframe; setcframe_L(cf, L); From 5ed524c09fec64bed46b4bf74fa03be9083b0963 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Sat, 1 Aug 2026 19:39:35 +0200 Subject: [PATCH 21/34] Don't fold -a / -b for unsigned operands. Thanks to Peter Marreck and Peter Cawley. #1499 --- src/lj_opt_fold.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lj_opt_fold.c b/src/lj_opt_fold.c index 03d869d005..3738f35702 100644 --- a/src/lj_opt_fold.c +++ b/src/lj_opt_fold.c @@ -867,6 +867,8 @@ LJFOLD(MUL NEG NEG) LJFOLD(DIV NEG NEG) LJFOLDF(simplify_nummuldiv_negneg) { + if (fins->o == IR_DIV && (irt_isu32(fins->t) || irt_isu64(fins->t))) + return NEXTFOLD; PHIBARRIER(fleft); PHIBARRIER(fright); fins->op1 = fleft->op1; /* (-a) o (-b) ==> a o b */ From f30aabe82f61dbd6901f6a75dadde0a64dc626d2 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Mon, 3 Aug 2026 10:44:17 +0200 Subject: [PATCH 22/34] Modernize jit.* Lua modules. --- src/jit/bc.lua | 43 ++++---- src/jit/bcsave.lua | 47 ++++----- src/jit/dis_arm.lua | 137 +++++++++++++------------ src/jit/dis_arm64.lua | 232 ++++++++++++++++++++---------------------- src/jit/dis_mips.lua | 83 ++++++++------- src/jit/dis_ppc.lua | 117 +++++++++++---------- src/jit/dis_x86.lua | 152 ++++++++++++++------------- src/jit/dump.lua | 132 ++++++++++++------------ src/jit/p.lua | 14 +-- src/jit/v.lua | 4 +- 10 files changed, 472 insertions(+), 489 deletions(-) diff --git a/src/jit/bc.lua b/src/jit/bc.lua index 8014d6029c..59bc35b83d 100644 --- a/src/jit/bc.lua +++ b/src/jit/bc.lua @@ -43,9 +43,8 @@ local jit = require("jit") local jutil = require("jit.util") local vmdef = require("jit.vmdef") -local bit = require("bit") local sub, gsub, format = string.sub, string.gsub, string.format -local byte, band, shr = string.byte, bit.band, bit.rshift +local byte = string.byte local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck local funcuvname = jutil.funcuvname local bcnames = vmdef.bcnames @@ -65,49 +64,49 @@ end local function bcline(func, pc, prefix) local ins, m = funcbc(func, pc) if not ins then return end - local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128) - local a = band(shr(ins, 8), 0xff) - local oidx = 6*band(ins, 0xff) + local ma, mb, mc = m & 7, (m >> 3) & 15, (m >> 7) & 15 + local a = (ins >> 8) & 0xff + local oidx = 6 * (ins & 0xff) local op = sub(bcnames, oidx+1, oidx+6) local s = format("%04d %s %-6s %3s ", - pc, prefix or " ", op, ma == 0 and "" or a) - local d = shr(ins, 16) - if mc == 13*128 then -- BCMjump + pc, prefix or " ", op, ma == 0 ? "" : a) + local d = ins >> 16 + if mc == 13 then -- BCMjump return format("%s=> %04d\n", s, pc+d-0x7fff) end - if mb ~= 0 then - d = band(d, 0xff) + if mb != 0 then + d &= 0xff elseif mc == 0 then return s.."\n" end local kc - if mc == 10*128 then -- BCMstr + if mc == 10 then -- BCMstr kc = funck(func, -d-1) - kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub)) - elseif mc == 9*128 then -- BCMnum + kc = format(#kc > 40 ? '"%.40s"~' : '"%s"', gsub(kc, "%c", ctlsub)) + elseif mc == 9 then -- BCMnum kc = funck(func, d) - if op == "TSETM " then kc = kc - 2^52 end - elseif mc == 12*128 then -- BCMfunc + if op == "TSETM " then kc -= 2^52 end + elseif mc == 12 then -- BCMfunc local fi = funcinfo(funck(func, -d-1)) if fi.ffid then kc = vmdef.ffnames[fi.ffid] else kc = fi.loc end - elseif mc == 5*128 then -- BCMuv + elseif mc == 5 then -- BCMuv kc = funcuvname(func, d) end if ma == 5 then -- BCMuv local ka = funcuvname(func, a) if kc then kc = ka.." ; "..kc else kc = ka end end - if mb ~= 0 then - local b = shr(ins, 24) + if mb != 0 then + local b = ins >> 24 if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end return format("%s%3d %3d\n", s, b, d) end if kc then return format("%s%3d ; %s\n", s, d, kc) end - if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits + if mc == 7 and d > 32767 then d -= 65536 end -- BCMlits return format("%s%3d\n", s, d) end @@ -117,7 +116,7 @@ local function bctargets(func) for pc=1,1000000000 do local ins, m = funcbc(func, pc) if not ins then break end - if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end + if m & (15 << 7) == (13 << 7) then target[pc+(ins >> 16)-0x7fff] = true end end return target end @@ -159,7 +158,7 @@ local function bclistoff() if active then active = false jit.attach(h_list) - if out and out ~= stdout and out ~= stderr then out:close() end + if out and out != stdout and out != stderr then out:close() end out = nil end end @@ -169,7 +168,7 @@ local function bcliston(outfile) if active then bclistoff() end if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end if outfile then - out = outfile == "-" and stdout or assert(io.open(outfile, "w")) + out = outfile == "-" ? stdout : assert(io.open(outfile, "w")) else out = stderr end diff --git a/src/jit/bcsave.lua b/src/jit/bcsave.lua index 7d19cb0649..544b506c10 100644 --- a/src/jit/bcsave.lua +++ b/src/jit/bcsave.lua @@ -20,6 +20,7 @@ local LJBC_PREFIX = "luaJIT_BC_" local type, assert = type, assert local format = string.format local tremove, tconcat = table.remove, table.concat +local bswap = bit.bswap ------------------------------------------------------------------------------ @@ -111,7 +112,7 @@ local map_os = { local function checkarg(str, map, err) str = str:lower() local s = check(map[str], "unknown ", err) - return type(s) == "string" and s or str + return type(s) == "string" ? s : str end local function detecttype(str) @@ -142,7 +143,7 @@ end local function bcsave_tail(fp, output, s) local ok, err = fp:write(s) - if ok and output ~= "-" then ok, err = fp:close() end + if ok and output != "-" then ok, err = fp:close() end check(ok, "cannot write ", output, ": ", err) end @@ -179,12 +180,12 @@ static const unsigned char %s%s[] = { local t, n, m = {}, 0, 0 for i=1,#s do local b = tostring(string.byte(s, i)) - m = m + #b + 1 + m += #b + 1 if m > 78 then fp:write(tconcat(t, ",", 1, n), ",\n") n, m = 0, #b + 1 end - n = n + 1 + n += 1 t[n] = b end bcsave_tail(fp, output, tconcat(t, ",", 1, n).."\n};\n") @@ -248,19 +249,19 @@ typedef struct { -- Handle different host/target endianess. local function f32(x) return x end local f16, fofs = f32, f32 - if ffi.abi("be") ~= isbe then - f32 = bit.bswap - function f16(x) return bit.rshift(bit.bswap(x), 16) end + if ffi.abi("be") != isbe then + f32 = bswap + function f16(x) return bswap(x) >> 16 end if is64 then local two32 = ffi.cast("int64_t", 2^32) - function fofs(x) return bit.bswap(x)*two32 end + function fofs(x) return bswap(x)*two32 end else fofs = f32 end end -- Create ELF object and fill in header. - local o = ffi.new(is64 and "ELF64obj" or "ELF32obj") + local o = ffi.new(is64 ? "ELF64obj" : "ELF32obj") local hdr = o.hdr if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi. local bf = assert(io.open("/bin/ls", "rb")) @@ -272,8 +273,8 @@ typedef struct { hdr.emagic = "\127ELF" hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0 end - hdr.eclass = is64 and 2 or 1 - hdr.eendian = isbe and 2 or 1 + hdr.eclass = is64 ? 2 : 1 + hdr.eendian = isbe ? 2 : 1 hdr.eversion = 1 hdr.type = f16(1) hdr.machine = f16(ai.m) @@ -294,7 +295,7 @@ typedef struct { sect.align = fofs(1) sect.name = f32(ofs) ffi.copy(o.space+ofs, name) - ofs = ofs + #name+1 + ofs += #name+1 end o.sect[1].type = f32(2) -- .symtab o.sect[1].link = f32(3) @@ -314,7 +315,7 @@ typedef struct { o.sect[3].ofs = fofs(sofs + ofs) o.sect[3].size = fofs(#symname+2) ffi.copy(o.space+ofs+1, symname) - ofs = ofs + #symname + 2 + ofs += #symname + 2 o.sect[4].type = f32(1) -- .rodata o.sect[4].flags = fofs(2) o.sect[4].ofs = fofs(sofs + ofs) @@ -381,8 +382,8 @@ typedef struct { local function f32(x) return x end local f16 = f32 if ffi.abi("be") then - f32 = bit.bswap - function f16(x) return bit.rshift(bit.bswap(x), 16) end + f32 = bswap + function f16(x) return bswap(x) >> 16 end end -- Create PE object and fill in header. @@ -422,7 +423,7 @@ typedef struct { o.strtabsize = f32(ofs + 4) o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs) ffi.copy(o.space + ofs, symexport) - ofs = ofs + #symexport + ofs += #symexport o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs) -- Write PE object file. @@ -475,11 +476,11 @@ typedef struct { ]] local symname = '_'..LJBC_PREFIX..ctx.modname local cputype, cpusubtype = 0x01000007, 3 - if ctx.arch ~= "x64" then + if ctx.arch != "x64" then check(ctx.arch == "arm64", "unsupported architecture for OSX") cputype, cpusubtype = 0x0100000c, 0 end - local function aligned(v, a) return bit.band(v+a-1, -a) end + local function aligned(v, a) return v+a-1 & -a end -- Create Mach-O object and fill in header. local o = ffi.new("mach_obj_64") @@ -579,7 +580,7 @@ local function docmd(...) local gc64 = "" while n <= #arg do local a = arg[n] - if type(a) == "string" and a:sub(1, 1) == "-" and a ~= "-" then + if type(a) == "string" and a:sub(1, 1) == "-" and a != "-" then tremove(arg, n) if a == "--" then break end for m=2,#a do @@ -595,9 +596,9 @@ local function docmd(...) elseif opt == "d" then ctx.mode = ctx.mode .. opt else - if arg[n] == nil or m ~= #a then usage() end + if arg[n] == nil or m != #a then usage() end if opt == "e" then - if n ~= 1 then usage() end + if n != 1 then usage() end ctx.string = true elseif opt == "n" then ctx.modname = checkmodname(tremove(arg, n)) @@ -615,7 +616,7 @@ local function docmd(...) end end else - n = n + 1 + n += 1 end end ctx.mode = ctx.mode .. strip .. gc64 @@ -623,7 +624,7 @@ local function docmd(...) if #arg == 0 or #arg > 2 then usage() end bclist(ctx, arg[1], arg[2] or "-") else - if #arg ~= 2 then usage() end + if #arg != 2 then usage() end bcsave(ctx, arg[1], arg[2]) end end diff --git a/src/jit/dis_arm.lua b/src/jit/dis_arm.lua index 0adc799de7..e2334ebbcb 100644 --- a/src/jit/dis_arm.lua +++ b/src/jit/dis_arm.lua @@ -15,8 +15,7 @@ local sub, byte, format = string.sub, string.byte, string.format local match, gmatch = string.match, string.gmatch local concat = table.concat local bit = require("bit") -local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex -local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift +local ror, tohex = bit.ror, bit.tohex ------------------------------------------------------------------------------ -- Opcode maps @@ -373,7 +372,7 @@ local map_datar = { [16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, }, _ = { shift = 0, mask = 0xffffffff, - [bor(0xe1a00000)] = "nop", + [0xe1a00000|0] = "nop", _ = map_data, } }, @@ -427,7 +426,7 @@ local function putop(ctx, text, operands) local sym = ctx.symtab[ctx.rel] if sym then extra = "\t->"..sym - elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then + elseif ctx.op & 0x0e000000 != 0x0a000000 then extra = "\t; 0x"..tohex(ctx.rel) end end @@ -448,47 +447,47 @@ end -- Format operand 2 of load/store opcodes. local function fmtload(ctx, op, pos) - local base = map_gpr[band(rshift(op, 16), 15)] + local base = map_gpr[(op >> 16) & 15] local x, ofs - local ext = (band(op, 0x04000000) == 0) - if not ext and band(op, 0x02000000) == 0 then - ofs = band(op, 4095) - if band(op, 0x00800000) == 0 then ofs = -ofs end + local ext = (op & 0x04000000 == 0) + if not ext and op & 0x02000000 == 0 then + ofs = op & 4095 + if op & 0x00800000 == 0 then ofs = -ofs end if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end ofs = "#"..ofs - elseif ext and band(op, 0x00400000) ~= 0 then - ofs = band(op, 15) + band(rshift(op, 4), 0xf0) - if band(op, 0x00800000) == 0 then ofs = -ofs end + elseif ext and op & 0x00400000 != 0 then + ofs = (op & 0x0f) | ((op >> 4) & 0xf0) + if op & 0x00800000 == 0 then ofs = -ofs end if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end ofs = "#"..ofs else - ofs = map_gpr[band(op, 15)] - if ext or band(op, 0xfe0) == 0 then - elseif band(op, 0xfe0) == 0x60 then + ofs = map_gpr[op & 15] + if ext or op & 0xfe0 == 0 then + elseif op & 0xfe0 == 0x60 then ofs = format("%s, rrx", ofs) else - local sh = band(rshift(op, 7), 31) + local sh = (op >> 7) & 31 if sh == 0 then sh = 32 end - ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh) + ofs = format("%s, %s #%d", ofs, map_shift[(op >> 5) & 3], sh) end - if band(op, 0x00800000) == 0 then ofs = "-"..ofs end + if op & 0x00800000 == 0 then ofs = "-"..ofs end end if ofs == "#0" then x = format("[%s]", base) - elseif band(op, 0x01000000) == 0 then + elseif op & 0x01000000 == 0 then x = format("[%s], %s", base, ofs) else x = format("[%s, %s]", base, ofs) end - if band(op, 0x01200000) == 0x01200000 then x = x.."!" end + if op & 0x01200000 == 0x01200000 then x ..= "!" end return x end -- Format operand 2 of vector load/store opcodes. local function fmtvload(ctx, op, pos) - local base = map_gpr[band(rshift(op, 16), 15)] - local ofs = band(op, 255)*4 - if band(op, 0x00800000) == 0 then ofs = -ofs end + local base = map_gpr[(op >> 16) & 15] + local ofs = (op & 255) << 2 + if op & 0x00800000 == 0 then ofs = -ofs end if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end if ofs == 0 then return format("[%s]", base) @@ -499,9 +498,9 @@ end local function fmtvr(op, vr, sh0, sh1) if vr == "s" then - return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1)) + return format("s%d", ((op >> sh0-1) & 0x1e) | ((op >> sh1) & 1)) else - return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16)) + return format("d%d", ((op >> sh0) & 15) | ((op >> sh1-4) & 16)) end end @@ -509,7 +508,7 @@ end local function disass_ins(ctx) local pos = ctx.pos local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) - local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) + local op = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 local operands = {} local suffix = "" local last, name, pat @@ -517,35 +516,35 @@ local function disass_ins(ctx) ctx.op = op ctx.rel = nil - local cond = rshift(op, 28) + local cond = op >> 28 local opat if cond == 15 then - opat = map_uncondins[band(rshift(op, 25), 7)] + opat = map_uncondins[(op >> 25) & 7] else - if cond ~= 14 then suffix = map_cond[cond] end - opat = map_condins[band(rshift(op, 25), 7)] + if cond != 14 then suffix = map_cond[cond] end + opat = map_condins[(op >> 25) & 7] end - while type(opat) ~= "string" do + while type(opat) != "string" do if not opat then return unknown(ctx) end - opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ + opat = opat[(op >> opat.shift) & opat.mask] or opat._ end name, pat = match(opat, "^([a-z0-9]*)(.*)") if sub(pat, 1, 1) == "." then local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)") - suffix = suffix..s2 + suffix ..= s2 pat = p2 end for p in gmatch(pat, ".") do local x = nil if p == "D" then - x = map_gpr[band(rshift(op, 12), 15)] + x = map_gpr[(op >> 12) & 15] elseif p == "N" then - x = map_gpr[band(rshift(op, 16), 15)] + x = map_gpr[(op >> 16) & 15] elseif p == "S" then - x = map_gpr[band(rshift(op, 8), 15)] + x = map_gpr[(op >> 8) & 15] elseif p == "M" then - x = map_gpr[band(op, 15)] + x = map_gpr[op & 15] elseif p == "d" then x = fmtvr(op, vr, 12, 22) elseif p == "n" then @@ -553,20 +552,20 @@ local function disass_ins(ctx) elseif p == "m" then x = fmtvr(op, vr, 0, 5) elseif p == "P" then - if band(op, 0x02000000) ~= 0 then - x = ror(band(op, 255), 2*band(rshift(op, 8), 15)) + if op & 0x02000000 != 0 then + x = ror(op & 0xff, (op >> 7) & 0x1e) else - x = map_gpr[band(op, 15)] - if band(op, 0xff0) ~= 0 then + x = map_gpr[op & 15] + if op & 0xff0 != 0 then operands[#operands+1] = x - local s = map_shift[band(rshift(op, 5), 3)] + local s = map_shift[(op >> 5) & 3] local r = nil - if band(op, 0xf90) == 0 then + if op & 0xf90 == 0 then if s == "ror" then s = "rrx" else r = "#32" end - elseif band(op, 0x10) == 0 then - r = "#"..band(rshift(op, 7), 31) + elseif op & 0x10 == 0 then + r = "#"..((op >> 7) & 31) else - r = map_gpr[band(rshift(op, 8), 15)] + r = map_gpr[(op >> 8) & 15] end if name == "mov" then name = s; x = r elseif r then x = format("%s %s", s, r) @@ -578,8 +577,8 @@ local function disass_ins(ctx) elseif p == "l" then x = fmtvload(ctx, op, pos) elseif p == "B" then - local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6) - if cond == 15 then addr = addr + band(rshift(op, 23), 2) end + local addr = ctx.addr + pos + 8 + ((op << 8) ~>> 6) + if cond == 15 then addr += (op >> 23) & 2 end ctx.rel = addr x = "0x"..tohex(addr) elseif p == "F" then @@ -587,52 +586,52 @@ local function disass_ins(ctx) elseif p == "G" then vr = "d" elseif p == "." then - suffix = suffix..(vr == "s" and ".f32" or ".f64") + suffix ..= vr == "s" ? ".f32" : ".f64" elseif p == "R" then - if band(op, 0x00200000) ~= 0 and #operands == 1 then + if op & 0x00200000 != 0 and #operands == 1 then operands[1] = operands[1].."!" end local t = {} for i=0,15 do - if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end + if (op >> i) & 1 == 1 then t[#t+1] = map_gpr[i] end end x = "{"..concat(t, ", ").."}" elseif p == "r" then - if band(op, 0x00200000) ~= 0 and #operands == 2 then + if op & 0x00200000 != 0 and #operands == 2 then operands[1] = operands[1].."!" end local s = tonumber(sub(last, 2)) - local n = band(op, 255) - if vr == "d" then n = rshift(n, 1) end + local n = op & 0xff + if vr == "d" then n >>= 1 end operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1) elseif p == "W" then - x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000) + x = (op & 0x0fff) | ((op >> 4) & 0xf000) elseif p == "T" then - x = "#0x"..tohex(band(op, 0x00ffffff), 6) + x = "#0x"..tohex(op & 0x00ffffff, 6) elseif p == "U" then - x = band(rshift(op, 7), 31) + x = (op >> 7) & 31 if x == 0 then x = nil end elseif p == "u" then - x = band(rshift(op, 7), 31) - if band(op, 0x40) == 0 then - if x == 0 then x = nil else x = "lsl #"..x end + x = (op >> 7) & 31 + if op & 0x40 == 0 then + x = x == 0 ? nil : "lsl #"..x else - if x == 0 then x = "asr #32" else x = "asr #"..x end + x = x == 0 ? "asr #32" : "asr #"..x end elseif p == "v" then - x = band(rshift(op, 7), 31) + x = (op >> 7) & 31 elseif p == "w" then - x = band(rshift(op, 16), 31) + x = (op >> 16) & 31 elseif p == "x" then - x = band(rshift(op, 16), 31) + 1 + x = ((op >> 16) & 31) + 1 elseif p == "X" then - x = band(rshift(op, 16), 31) - last + 1 + x = ((op >> 16) & 31) - last + 1 elseif p == "Y" then - x = band(rshift(op, 12), 0xf0) + band(op, 0x0f) + x = ((op >> 12) & 0xf0) | (op & 0x0f) elseif p == "K" then - x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4) + x = "#0x"..tohex(((op >> 4) & 0xfff0) | (op & 0x000f), 4) elseif p == "s" then - if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end + if op & 0x00100000 != 0 then suffix = "s"..suffix end else assert(false) end @@ -651,7 +650,7 @@ end -- Disassemble a block of code. local function disass_block(ctx, ofs, len) if not ofs then ofs = 0 end - local stop = len and ofs+len or #ctx.code + local stop = len ? ofs+len : #ctx.code ctx.pos = ofs ctx.rel = nil while ctx.pos < stop do disass_ins(ctx) end diff --git a/src/jit/dis_arm64.lua b/src/jit/dis_arm64.lua index 896fab791e..7464421e47 100644 --- a/src/jit/dis_arm64.lua +++ b/src/jit/dis_arm64.lua @@ -18,9 +18,7 @@ local sub, byte, format = string.sub, string.byte, string.format local match, gmatch, gsub = string.match, string.gmatch, string.gsub local concat = table.concat local bit = require("bit") -local band, bor, bxor, tohex = bit.band, bit.bor, bit.bxor, bit.tohex -local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift -local ror = bit.ror +local ror, tohex = bit.ror, bit.tohex ------------------------------------------------------------------------------ -- Opcode maps @@ -782,35 +780,35 @@ end local imm13_rep = { 0x55555555, 0x11111111, 0x01010101, 0x00010001, 0x00000001 } local function decode_imm13(op) - local imms = band(rshift(op, 10), 63) - local immr = band(rshift(op, 16), 63) - if band(op, 0x00400000) == 0 then + local imms = (op >> 10) & 63 + local immr = (op >> 16) & 63 + if op & 0x00400000 == 0 then local len = 5 if imms >= 56 then if imms >= 60 then len = 1 else len = 2 end elseif imms >= 48 then len = 3 elseif imms >= 32 then len = 4 end - local l = lshift(1, len)-1 - local s = band(imms, l) - local r = band(immr, l) - local imm = ror(rshift(-1, 31-s), r) - if len ~= 5 then imm = band(imm, lshift(1, l)-1) + rshift(imm, 31-l) end - imm = imm * imm13_rep[len] + local l = (1 << len) - 1 + local s = imms & l + local r = immr & l + local imm = ror(-1 >> 31-s, r) + if len != 5 then imm = (imm & ((1 << l) - 1)) | (imm >> 31-l) end + imm *= imm13_rep[len] local ix = fmt_hex32(imm) - if rshift(op, 31) ~= 0 then + if op >> 31 != 0 then return ix..tohex(imm) else return ix end else local lo, hi = -1, 0 - if imms < 32 then lo = rshift(-1, 31-imms) else hi = rshift(-1, 63-imms) end - if immr ~= 0 then + if imms < 32 then lo = -1 >> 31-imms else hi = -1 >> 63-imms end + if immr != 0 then lo, hi = ror(lo, immr), ror(hi, immr) - local x = immr == 32 and 0 or band(bxor(lo, hi), lshift(-1, 32-immr)) - lo, hi = bxor(lo, x), bxor(hi, x) + local x = immr == 32 ? 0 : (lo ~ hi) & (-1 << 32-immr) + lo, hi = lo ~ x, hi ~ x if immr >= 32 then lo, hi = hi, lo end end - if hi ~= 0 then + if hi != 0 then return fmt_hex32(hi)..tohex(lo) else return fmt_hex32(lo) @@ -820,33 +818,31 @@ end local function parse_immpc(op, name) if name == "b" or name == "bl" then - return arshift(lshift(op, 6), 4) + return (op << 6) ~>> 4 elseif name == "adr" or name == "adrp" then - local immlo = band(rshift(op, 29), 3) - local immhi = lshift(arshift(lshift(op, 8), 13), 2) - return bor(immhi, immlo) + return (((op << 8) ~>> 13) << 2) | ((op >> 29) & 3) elseif name == "tbz" or name == "tbnz" then - return lshift(arshift(lshift(op, 13), 18), 2) + return ((op << 13) ~>> 18) << 2 else - return lshift(arshift(lshift(op, 8), 13), 2) + return ((op << 8) ~>> 13) << 2 end end local function parse_fpimm8(op) - local sign = band(op, 0x100000) == 0 and 1 or -1 - local exp = bxor(rshift(arshift(lshift(op, 12), 5), 24), 0x80) - 131 - local frac = 16+band(rshift(op, 13), 15) + local sign = op & 0x100000 == 0 ? 1 : -1 + local exp = ((((op << 12) ~>> 5) >> 24) ~ 0x80) - 131 + local frac = 16 + ((op >> 13) & 15) return sign * frac * 2^exp end local function decode_fpmovi(op) - local lo = rshift(op, 5) - local hi = rshift(op, 9) - lo = bor(band(lo, 1) * 0xff, band(lo, 2) * 0x7f80, band(lo, 4) * 0x3fc000, - band(lo, 8) * 0x1fe00000) - hi = bor(band(hi, 1) * 0xff, band(hi, 0x80) * 0x1fe, - band(hi, 0x100) * 0xff00, band(hi, 0x200) * 0x7f8000) - if hi ~= 0 then + local lo = op >> 5 + local hi = op >> 9 + lo = ((lo & 1) * 0xff) | ((lo & 2) * 0x7f80) | + ((lo & 4) * 0x3fc000) | ((lo & 8) * 0x1fe00000) + hi = ((hi & 1) * 0xff) | ((hi & 0x80) * 0x1fe) | + ((hi & 0x100) * 0xff00) | ((hi & 0x200) * 0x7f8000) + if hi != 0 then return fmt_hex32(hi)..tohex(lo) else return fmt_hex32(lo) @@ -861,7 +857,7 @@ local function prefer_bfx(sf, uns, imms, immr) if sf == 0 and (imms == 7 or imms == 15) then return false end - if sf ~= 0 and uns == 0 and (imms == 7 or imms == 15 or imms == 31) then + if sf != 0 and uns == 0 and (imms == 7 or imms == 15 or imms == 31) then return false end end @@ -872,7 +868,7 @@ end local function disass_ins(ctx) local pos = ctx.pos local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) - local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) + local op = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 local operands = {} local suffix = "" local last, name, pat @@ -881,26 +877,26 @@ local function disass_ins(ctx) ctx.rel = nil last = nil local opat - opat = map_init[band(rshift(op, 25), 15)] - while type(opat) ~= "string" do + opat = map_init[(op >> 25) & 15] + while type(opat) != "string" do if not opat then return unknown(ctx) end - opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ + opat = opat[(op >> opat.shift) & opat.mask] or opat._ end name, pat = match(opat, "^([a-z0-9]*)(.*)") local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)") if altname then pat = pat2 end if sub(pat, 1, 1) == "." then local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)") - suffix = suffix..s2 + suffix ..= s2 pat = p2 end local rt = match(pat, "[gf]") if rt then if rt == "g" then - map_reg = band(op, 0x80000000) ~= 0 and map_regs.x or map_regs.w + map_reg = map_regs[op & 0x80000000 != 0 ? "x" : "w"] else - map_reg = band(op, 0x400000) ~= 0 and map_regs.d or map_regs.s + map_reg = map_regs[op & 0x400000 != 0 ? "d" : "s"] end end @@ -909,41 +905,41 @@ local function disass_ins(ctx) for p in gmatch(pat, ".") do local x = nil if p == "D" then - local regnum = band(op, 31) - x = rt and map_reg[regnum] or match_reg(p, pat, regnum) + local regnum = op & 31 + x = rt ? map_reg[regnum] : match_reg(p, pat, regnum) elseif p == "N" then - local regnum = band(rshift(op, 5), 31) - x = rt and map_reg[regnum] or match_reg(p, pat, regnum) + local regnum = (op >> 5) & 31 + x = rt ? map_reg[regnum] : match_reg(p, pat, regnum) elseif p == "M" then - local regnum = band(rshift(op, 16), 31) - x = rt and map_reg[regnum] or match_reg(p, pat, regnum) + local regnum = (op >> 16) & 31 + x = rt ? map_reg[regnum] : match_reg(p, pat, regnum) elseif p == "A" then - local regnum = band(rshift(op, 10), 31) - x = rt and map_reg[regnum] or match_reg(p, pat, regnum) + local regnum = (op >> 10) & 31 + x = rt ? map_reg[regnum] : match_reg(p, pat, regnum) elseif p == "B" then local addr = ctx.addr + pos + parse_immpc(op, name) ctx.rel = addr x = format("0x%08x", addr) elseif p == "T" then - x = bor(band(rshift(op, 26), 32), band(rshift(op, 19), 31)) + x = ((op >> 26) & 32) | ((op >> 19) & 31) elseif p == "V" then - x = band(op, 15) + x = op & 15 elseif p == "C" then - x = map_cond[band(rshift(op, 12), 15)] + x = map_cond[(op >> 12) & 15] elseif p == "c" then - local rn = band(rshift(op, 5), 31) - local rm = band(rshift(op, 16), 31) - local cond = band(rshift(op, 12), 15) - local invc = bxor(cond, 1) + local rn = (op >> 5) & 31 + local rm = (op >> 16) & 31 + local cond = (op >> 12) & 15 + local invc = cond ~ 1 x = map_cond[cond] - if altname and cond ~= 14 and cond ~= 15 then + if altname and cond != 14 and cond != 15 then local a1, a2 = match(altname, "([^|]*)|(.*)") if rn == rm then local n = #operands operands[n] = nil x = map_cond[invc] - if rn ~= 31 then - if a1 then name = a1 else name = altname end + if rn != 31 then + name = a1 ?? altname else operands[n-1] = nil name = a2 @@ -951,65 +947,59 @@ local function disass_ins(ctx) end end elseif p == "W" then - x = band(rshift(op, 5), 0xffff) + x = (op >> 5) & 0xffff elseif p == "Y" then - x = band(rshift(op, 5), 0xffff) - local hw = band(rshift(op, 21), 3) - if altname and (hw == 0 or x ~= 0) then + x = (op >> 5) & 0xffff + local hw = (op >> 21) & 3 + if altname and (hw == 0 or x != 0) then name = altname end elseif p == "L" then - local rn = map_regs.x[band(rshift(op, 5), 31)] - local imm9 = arshift(lshift(op, 11), 23) - if band(op, 0x800) ~= 0 then + local rn = map_regs.x[(op >> 5) & 31] + local imm9 = (op << 11) ~>> 23 + if op & 0x800 != 0 then x = "["..rn..", #"..imm9.."]!" else x = "["..rn.."], #"..imm9 end elseif p == "U" then - local rn = map_regs.x[band(rshift(op, 5), 31)] - local sz = band(rshift(op, 30), 3) - local imm12 = lshift(rshift(lshift(op, 10), 20), sz) - if imm12 ~= 0 then + local rn = map_regs.x[(op >> 5) & 31] + local sz = (op >> 30) & 3 + local imm12 = ((op << 10) >> 20) << sz + if imm12 != 0 then x = "["..rn..", #"..imm12.."]" else x = "["..rn.."]" end elseif p == "K" then - local rn = map_regs.x[band(rshift(op, 5), 31)] - local imm9 = arshift(lshift(op, 11), 23) - if imm9 ~= 0 then + local rn = map_regs.x[(op >> 5) & 31] + local imm9 = (op << 11) ~>> 23 + if imm9 != 0 then x = "["..rn..", #"..imm9.."]" else x = "["..rn.."]" end elseif p == "O" then - local rn, rm = map_regs.x[band(rshift(op, 5), 31)] - local m = band(rshift(op, 13), 1) - if m == 0 then - rm = map_regs.w[band(rshift(op, 16), 31)] - else - rm = map_regs.x[band(rshift(op, 16), 31)] - end + local rn = map_regs.x[(op >> 5) & 31] + local rm = map_regs[op & (1 << 13) == 0 ? "w" : "x"][(op >> 16) & 31] x = "["..rn..", "..rm - local opt = band(rshift(op, 13), 7) - local s = band(rshift(op, 12), 1) - local sz = band(rshift(op, 30), 3) - -- extension to be applied + local opt = (op >> 13) & 7 + local s = (op >> 12) & 1 + local sz = (op >> 30) & 3 if opt == 3 then - if s == 0 then x = x.."]" + if s == 0 then x ..= "]" else x = x..", lsl #"..sz.."]" end elseif opt == 2 or opt == 6 or opt == 7 then if s == 0 then x = x..", "..map_extend[opt].."]" else x = x..", "..map_extend[opt].." #"..sz.."]" end else - x = x.."]" + x ..= "]" end elseif p == "P" then - local sh = 2 + rshift(op, 31 - band(rshift(op, 26), 1)) - local imm7 = lshift(arshift(lshift(op, 10), 25), sh) - local rn = map_regs.x[band(rshift(op, 5), 31)] - local ind = band(rshift(op, 23), 3) + local sh = 2 + (op >> (31 - ((op >> 26) & 1))) + local imm7 = ((op << 10) ~>> 25) << sh + local rn = map_regs.x[(op >> 5) & 31] + local ind = (op >> 23) & 3 if ind == 1 then x = "["..rn.."], #"..imm7 elseif ind == 2 then @@ -1022,9 +1012,9 @@ local function disass_ins(ctx) x = "["..rn..", #"..imm7.."]!" end elseif p == "I" then - local shf = band(rshift(op, 22), 3) - local imm12 = band(rshift(op, 10), 0x0fff) - local rn, rd = band(rshift(op, 5), 31), band(op, 31) + local shf = (op >> 22) & 3 + local imm12 = (op >> 10) & 0x0fff + local rn, rd = (op >> 5) & 31, op & 31 if altname == "mov" and shf == 0 and imm12 == 0 and (rn == 31 or rd == 31) then name = altname x = nil @@ -1036,22 +1026,22 @@ local function disass_ins(ctx) elseif p == "i" then x = "#0x"..decode_imm13(op) elseif p == "1" then - immr = band(rshift(op, 16), 63) + immr = (op >> 16) & 63 x = immr elseif p == "2" then - x = band(rshift(op, 10), 63) + x = (op >> 10) & 63 if altname then local a1, a2, a3, a4, a5, a6 = match(altname, "([^|]*)|([^|]*)|([^|]*)|([^|]*)|([^|]*)|(.*)") - local sf = band(rshift(op, 26), 32) - local uns = band(rshift(op, 30), 1) + local sf = (op >> 26) & 32 + local uns = (op >> 30) & 1 if prefer_bfx(sf, uns, x, immr) then name = a2 x = x - immr + 1 elseif immr == 0 and x == 7 then local n = #operands operands[n] = nil - if sf ~= 0 then + if sf != 0 then operands[n-1] = gsub(operands[n-1], "x", "w") end last = operands[n-1] @@ -1060,7 +1050,7 @@ local function disass_ins(ctx) elseif immr == 0 and x == 15 then local n = #operands operands[n] = nil - if sf ~= 0 then + if sf != 0 then operands[n-1] = gsub(operands[n-1], "x", "w") end last = operands[n-1] @@ -1071,7 +1061,7 @@ local function disass_ins(ctx) name = a4 local n = #operands operands[n] = nil - if sf ~= 0 then + if sf != 0 then operands[n-1] = gsub(operands[n-1], "x", "w") end last = operands[n-1] @@ -1079,7 +1069,7 @@ local function disass_ins(ctx) name = a3 end x = nil - elseif band(x, 31) ~= 31 and immr == x+1 and name == "ubfm" then + elseif x & 31 != 31 and immr == x+1 and name == "ubfm" then name = a4 last = "#"..(sf+32 - immr) operands[#operands] = last @@ -1088,28 +1078,28 @@ local function disass_ins(ctx) name = a1 last = "#"..(sf+32 - immr) operands[#operands] = last - x = x + 1 + x += 1 end end elseif p == "3" then - x = band(rshift(op, 10), 63) + x = (op >> 10) & 63 if altname then local a1, a2 = match(altname, "([^|]*)|(.*)") if x < immr then name = a1 - local sf = band(rshift(op, 26), 32) + local sf = (op >> 26) & 32 last = "#"..(sf+32 - immr) operands[#operands] = last - x = x + 1 + x += 1 else name = a2 x = x - immr + 1 end end elseif p == "4" then - x = band(rshift(op, 10), 63) - local rn = band(rshift(op, 5), 31) - local rm = band(rshift(op, 16), 31) + x = (op >> 10) & 63 + local rn = (op >> 5) & 31 + local rm = (op >> 16) & 31 if altname and rn == rm then local n = #operands operands[n] = nil @@ -1117,30 +1107,30 @@ local function disass_ins(ctx) name = altname end elseif p == "5" then - x = band(rshift(op, 16), 31) + x = (op >> 16) & 31 elseif p == "S" then - x = band(rshift(op, 10), 63) + x = (op >> 10) & 63 if x == 0 then x = nil - else x = map_shift[band(rshift(op, 22), 3)].." #"..x end + else x = map_shift[(op >> 22) & 3].." #"..x end elseif p == "X" then - local opt = band(rshift(op, 13), 7) + local opt = (op >> 13) & 7 -- Width specifier . - if opt ~= 3 and opt ~= 7 then - last = map_regs.w[band(rshift(op, 16), 31)] + if opt != 3 and opt != 7 then + last = map_regs.w[(op >> 16) & 31] operands[#operands] = last end - x = band(rshift(op, 10), 7) + x = (op >> 10) & 7 -- Extension. - if opt == 2 + band(rshift(op, 31), 1) and - band(rshift(op, second0 and 5 or 0), 31) == 31 then + if opt == 2 + ((op >> 31) & 1) and + (op >> (second0 ? 5 : 0)) & 31 == 31 then if x == 0 then x = nil else x = "lsl #"..x end else - if x == 0 then x = map_extend[band(rshift(op, 13), 7)] - else x = map_extend[band(rshift(op, 13), 7)].." #"..x end + if x == 0 then x = map_extend[(op >> 13) & 7] + else x = map_extend[(op >> 13) & 7].." #"..x end end elseif p == "R" then - x = band(rshift(op,21), 3) + x = (op >> 21) & 3 if x == 0 then x = nil else x = "lsl #"..x*16 end elseif p == "z" then diff --git a/src/jit/dis_mips.lua b/src/jit/dis_mips.lua index fece89370d..2ff68f6be4 100644 --- a/src/jit/dis_mips.lua +++ b/src/jit/dis_mips.lua @@ -15,8 +15,7 @@ local byte, format = string.byte, string.format local match, gmatch = string.match, string.gmatch local concat = table.concat local bit = require("bit") -local band, bor, tohex = bit.band, bit.bor, bit.tohex -local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift +local tohex = bit.tohex ------------------------------------------------------------------------------ -- Extended opcode maps common to all MIPS releases @@ -477,13 +476,13 @@ end local function get_be(ctx) local pos = ctx.pos local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) - return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3) + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 end local function get_le(ctx) local pos = ctx.pos local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) - return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) + return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0 end -- Disassemble a single instruction. @@ -494,13 +493,13 @@ local function disass_ins(ctx) ctx.op = op ctx.rel = nil - local opat = ctx.map_pri[rshift(op, 26)] - while type(opat) ~= "string" do + local opat = ctx.map_pri[op >> 26] + while type(opat) != "string" do if not opat then return unknown(ctx) end if opat.maprs then - opat = opat[opat.maprs(band(rshift(op,21),31), band(rshift(op,16),31))] + opat = opat[opat.maprs((op >> 21) & 31, (op >> 16) & 31)] else - opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ + opat = opat[(op >> opat.shift) & opat.mask] or opat._ end end local name, pat = match(opat, "^([a-z0-9_.]*)(.*)") @@ -510,82 +509,82 @@ local function disass_ins(ctx) for p in gmatch(pat, ".") do local x = nil if p == "S" then - x = map_gpr[band(rshift(op, 21), 31)] + x = map_gpr[(op >> 21) & 31] elseif p == "T" then - x = map_gpr[band(rshift(op, 16), 31)] + x = map_gpr[(op >> 16) & 31] elseif p == "D" then - x = map_gpr[band(rshift(op, 11), 31)] + x = map_gpr[(op >> 11) & 31] elseif p == "F" then - x = "f"..band(rshift(op, 6), 31) + x = "f"..((op >> 6) & 31) elseif p == "G" then - x = "f"..band(rshift(op, 11), 31) + x = "f"..((op >> 11) & 31) elseif p == "H" then - x = "f"..band(rshift(op, 16), 31) + x = "f"..((op >> 16) & 31) elseif p == "R" then - x = "f"..band(rshift(op, 21), 31) + x = "f"..((op >> 21) & 31) elseif p == "A" then - x = band(rshift(op, 6), 31) + x = (op >> 6) & 31 elseif p == "a" then - x = band(rshift(op, 6), 7) + x = (op >> 6) & 7 elseif p == "E" then - x = band(rshift(op, 6), 31) + 32 + x = ((op >> 6) & 31) + 32 elseif p == "M" then - x = band(rshift(op, 11), 31) + x = (op >> 11) & 31 elseif p == "N" then - x = band(rshift(op, 16), 31) + x = (op >> 16) & 31 elseif p == "C" then - x = band(rshift(op, 18), 7) + x = (op >> 18) & 7 if x == 0 then x = nil end elseif p == "K" then - x = band(rshift(op, 11), 31) + 1 + x = ((op >> 11) & 31) + 1 elseif p == "P" then - x = band(rshift(op, 11), 31) + 33 + x = ((op >> 11) & 31) + 33 elseif p == "L" then - x = band(rshift(op, 11), 31) - last + 1 + x = ((op >> 11) & 31) - last + 1 elseif p == "Q" then - x = band(rshift(op, 11), 31) - last + 33 + x = ((op >> 11) & 31) - last + 33 elseif p == "I" then - x = arshift(lshift(op, 16), 16) + x = (op << 16) ~>> 16 elseif p == "2" then - x = arshift(lshift(op, 13), 11) + x = (op << 13) ~>> 11 elseif p == "3" then - x = arshift(lshift(op, 14), 11) + x = (op << 14) ~>> 11 elseif p == "U" then - x = band(op, 0xffff) + x = op & 0xffff elseif p == "O" then - local disp = arshift(lshift(op, 16), 16) + local disp = (op << 16) ~>> 16 operands[#operands] = format("%d(%s)", disp, last) elseif p == "X" then - local index = map_gpr[band(rshift(op, 16), 31)] + local index = map_gpr[(op >> 16) & 31] operands[#operands] = format("%s(%s)", index, last) elseif p == "B" then - x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 14) + 4 + x = ctx.addr + ctx.pos + ((op << 16) ~>> 14) + 4 ctx.rel = x x = format("0x%08x", x) elseif p == "b" then - x = ctx.addr + ctx.pos + arshift(lshift(op, 11), 9) + 4 + x = ctx.addr + ctx.pos + ((op << 11) ~>> 9) + 4 ctx.rel = x x = format("0x%08x", x) elseif p == "#" then - x = ctx.addr + ctx.pos + arshift(lshift(op, 6), 4) + 4 + x = ctx.addr + ctx.pos + ((op << 6) ~>> 4) + 4 ctx.rel = x x = format("0x%08x", x) elseif p == "J" then local a = ctx.addr + ctx.pos - x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4 + x = a - (a & 0x0fffffff) + ((op & 0x03ffffff) << 2) ctx.rel = x x = format("0x%08x", x) elseif p == "V" then - x = band(rshift(op, 8), 7) + x = (op >> 8) & 7 if x == 0 then x = nil end elseif p == "W" then - x = band(op, 7) + x = op & 7 if x == 0 then x = nil end elseif p == "Y" then - x = band(rshift(op, 6), 0x000fffff) + x = (op >> 6) & 0x000fffff if x == 0 then x = nil end elseif p == "Z" then - x = band(rshift(op, 6), 1023) + x = (op >> 6) & 1023 if x == 0 then x = nil end elseif p == "0" then if last == "r0" or last == 0 then @@ -616,9 +615,9 @@ end -- Disassemble a block of code. local function disass_block(ctx, ofs, len) if not ofs then ofs = 0 end - local stop = len and ofs+len or #ctx.code - stop = stop - stop % 4 - ctx.pos = ofs - ofs % 4 + local stop = len ? ofs+len : #ctx.code + stop &= -4 + ctx.pos = ofs & -4 ctx.rel = nil while ctx.pos < stop do disass_ins(ctx) end end diff --git a/src/jit/dis_ppc.lua b/src/jit/dis_ppc.lua index d8f4cfb783..90eef9408b 100644 --- a/src/jit/dis_ppc.lua +++ b/src/jit/dis_ppc.lua @@ -17,8 +17,7 @@ local byte, format = string.byte, string.format local match, gmatch, gsub = string.match, string.gmatch, string.gsub local concat = table.concat local bit = require("bit") -local band, bor, tohex = bit.band, bit.bor, bit.tohex -local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift +local tohex = bit.tohex ------------------------------------------------------------------------------ -- Primary and extended opcode maps @@ -39,9 +38,9 @@ local map_rlwinm = setmetatable({ shift = 0, mask = -1, }, { __index = function(t, x) - local rot = band(rshift(x, 11), 31) - local mb = band(rshift(x, 6), 31) - local me = band(rshift(x, 1), 31) + local rot = (x >> 11) & 31 + local mb = (x >> 6) & 31 + local me = (x >> 1) & 31 if mb == 0 and me == 31-rot then return "slwiRR~A." elseif me == 31 and mb == 32-rot then @@ -167,7 +166,7 @@ local map_ext = setmetatable({ [539] = "srdRR~R.", }, { __index = function(t, x) - if band(x, 31) == 15 then return "iselRRRC" end + if x & 31 == 15 then return "iselRRRC" end end }) @@ -386,9 +385,9 @@ local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", } -- Format a condition bit. local function condfmt(cond) if cond <= 3 then - return map_cond[band(cond, 3)] + return map_cond[cond & 3] else - return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)]) + return format("4*cr%d+%s", cond >> 2, map_cond[cond & 3]) end end @@ -421,17 +420,17 @@ end local function disass_ins(ctx) local pos = ctx.pos local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) - local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3) + local op = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 local operands = {} local last = nil local rs = 21 ctx.op = op ctx.rel = nil - local opat = map_pri[rshift(b0, 2)] - while type(opat) ~= "string" do + local opat = map_pri[b0 >> 2] + while type(opat) != "string" do if not opat then return unknown(ctx) end - opat = opat[band(rshift(op, opat.shift), opat.mask)] + opat = opat[(op >> opat.shift) & opat.mask] end local name, pat = match(opat, "^([a-z0-9_.]*)(.*)") local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)") @@ -440,84 +439,84 @@ local function disass_ins(ctx) for p in gmatch(pat, ".") do local x = nil if p == "R" then - x = map_gpr[band(rshift(op, rs), 31)] - rs = rs - 5 + x = map_gpr[(op >> rs) & 31] + rs -= 5 elseif p == "F" then - x = "f"..band(rshift(op, rs), 31) - rs = rs - 5 + x = "f"..((op >> rs) & 31) + rs -= 5 elseif p == "A" then - x = band(rshift(op, rs), 31) - rs = rs - 5 + x = (op >> rs) & 31 + rs -= 5 elseif p == "S" then - x = arshift(lshift(op, 27-rs), 27) - rs = rs - 5 + x = (op << 27-rs) ~>> 27 + rs -= 5 elseif p == "I" then - x = arshift(lshift(op, 16), 16) + x = (op << 16) ~>> 16 elseif p == "U" then - x = band(op, 0xffff) + x = op & 0xffff elseif p == "D" or p == "E" then - local disp = arshift(lshift(op, 16), 16) - if p == "E" then disp = band(disp, -4) end + local disp = (op << 16) ~>> 16 + if p == "E" then disp &= -4 end if last == "r0" then last = "0" end operands[#operands] = format("%d(%s)", disp, last) elseif p >= "2" and p <= "8" then - local disp = band(rshift(op, rs), 31) * p + local disp = ((op >> rs) & 31) * (byte(p) - 0x30) if last == "r0" then last = "0" end operands[#operands] = format("%d(%s)", disp, last) elseif p == "H" then - x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4) - rs = rs - 5 + x = ((op >> rs) & 31) | ((op & 2) << 4) + rs -= 5 elseif p == "M" then - x = band(rshift(op, rs), 31) + band(op, 0x20) + x = ((op >> rs) & 31) | (op & 0x20) elseif p == "C" then - x = condfmt(band(rshift(op, rs), 31)) - rs = rs - 5 + x = condfmt((op >> rs) & 31) + rs -= 5 elseif p == "B" then - local bo = rshift(op, 21) - local cond = band(rshift(op, 16), 31) + local bo = op >> 21 + local cond = (op >> 16) & 31 local cn = "" - rs = rs - 10 - if band(bo, 4) == 0 then - cn = band(bo, 2) == 0 and "dnz" or "dz" - if band(bo, 0x10) == 0 then - cn = cn..(band(bo, 8) == 0 and "f" or "t") + rs -= 10 + if bo & 4 == 0 then + cn = bo & 2 == 0 ? "dnz" : "dz" + if bo & 0x10 == 0 then + cn ..= bo & 8 == 0 ? "f" : "t" + x = condfmt(cond) end - if band(bo, 0x10) == 0 then x = condfmt(cond) end - name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+") - elseif band(bo, 0x10) == 0 then - cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)] - if cond > 3 then x = "cr"..rshift(cond, 2) end - name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+") + name ..= bo & 1 == (op >> 15) & 1 ? "-" : "+" + elseif bo & 0x10 == 0 then + cn = map_cond[(cond & 3) | ((bo >> 1) & 4)] + if cond > 3 then x = "cr"..(cond >> 2) end + name ..= bo & 1 == (op >> 15) & 1 ? "-" : "+" end name = gsub(name, "_", cn) elseif p == "J" then - x = arshift(lshift(op, 27-rs), 29-rs)*4 - if band(op, 2) == 0 then x = ctx.addr + pos + x end + x = ((op << 27-rs) ~>> 29-rs) << 2 + if op & 2 == 0 then x = ctx.addr + pos + x end ctx.rel = x x = "0x"..tohex(x) elseif p == "K" then - if band(op, 1) ~= 0 then name = name.."l" end - if band(op, 2) ~= 0 then name = name.."a" end + if op & 1 != 0 then name ..= "l" end + if op & 2 != 0 then name ..= "a" end elseif p == "X" or p == "Y" then - x = band(rshift(op, rs+2), 7) - if x == 0 and p == "Y" then x = nil else x = "cr"..x end - rs = rs - 5 + x = (op >> rs+2) & 7 + x = x == 0 and p == "Y" ? nil : "cr"..x + rs -= 5 elseif p == "W" then - x = "cr"..band(op, 7) + x = "cr"..(op & 7) elseif p == "Z" then - x = band(rshift(op, rs-4), 255) - rs = rs - 10 + x = (op >> rs-4) & 0xff + rs -= 10 elseif p == ">" then - operands[#operands] = rshift(operands[#operands], 1) + operands[#operands] >>= 1 elseif p == "0" then if last == "r0" then operands[#operands] = nil if altname then name = altname end end elseif p == "L" then - name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w") + name = gsub(name, "_", op & 0x00200000 != 0 ? "d" : "w") elseif p == "." then - if band(op, 1) == 1 then name = name.."." end + if op & 1 == 1 then name ..= "." end elseif p == "N" then if op == 0x60000000 then name = "nop"; break end elseif p == "~" then @@ -537,7 +536,7 @@ local function disass_ins(ctx) name = altname end elseif p == "-" then - rs = rs - 5 + rs -= 5 else assert(false) end @@ -553,8 +552,8 @@ end local function disass_block(ctx, ofs, len) if not ofs then ofs = 0 end local stop = len and ofs+len or #ctx.code - stop = stop - stop % 4 - ctx.pos = ofs - ofs % 4 + stop &= -4 + ctx.pos = ofs & -4 ctx.rel = nil while ctx.pos < stop do disass_ins(ctx) end end diff --git a/src/jit/dis_x86.lua b/src/jit/dis_x86.lua index 80bf721b1a..b850612927 100644 --- a/src/jit/dis_x86.lua +++ b/src/jit/dis_x86.lua @@ -421,21 +421,21 @@ local function putop(ctx, text, operands) local hmax = ctx.hexdump if hmax > 0 then for i=ctx.start,pos-1 do - hex = hex..format("%02X", byte(code, i, i)) + hex ..= format("%02X", byte(code, i)) end if #hex > hmax then hex = sub(hex, 1, hmax)..". " - else hex = hex..rep(" ", hmax-#hex+2) end + else hex ..= rep(" ", hmax-#hex+2) end end if operands then text = text.." "..operands end if ctx.o16 then text = "o16 "..text; ctx.o16 = false end if ctx.a32 then text = "a32 "..text; ctx.a32 = false end if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end if ctx.rex then - local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "").. - (ctx.rexx and "x" or "")..(ctx.rexb and "b" or "").. - (ctx.vexl and "l" or "") - if ctx.vexv and ctx.vexv ~= 0 then t = t.."v"..ctx.vexv end - if t ~= "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "") + local t = (ctx.rexw ? "w" : "")..(ctx.rexr ? "r" : "").. + (ctx.rexx ? "x" : "")..(ctx.rexb ? "b" : "").. + (ctx.vexl ? "l" : "") + if ctx.vexv and ctx.vexv != 0 then t = t.."v"..ctx.vexv end + if t != "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "") elseif ctx.rex == "vex" then text = gsub("v"..text, "^v ", "") end ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false ctx.rex = false; ctx.vexl = false; ctx.vexv = false @@ -483,7 +483,7 @@ local function getimm(ctx, pos, n) if pos+n-1 > ctx.stop then return incomplete(ctx) end local code = ctx.code if n == 1 then - local b1 = byte(code, pos, pos) + local b1 = byte(code, pos) return b1 elseif n == 2 then local b1, b2 = byte(code, pos, pos+1) @@ -521,41 +521,41 @@ local function putpat(ctx, name, pat) if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end regs = map_regs[sz] elseif p == "P" then - sz = ctx.o16 and "X" or "M"; ctx.o16 = false + sz = ctx.o16 ? "X" : "M"; ctx.o16 = false if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end regs = map_regs[sz] elseif p == "H" then - name = name..(ctx.rexw and "d" or "s") + name ..= ctx.rexw ? "d" : "s" ctx.rexw = false elseif p == "S" then - name = name..lower(sz) + name ..= lower(sz) elseif p == "s" then local imm = getimm(ctx, pos, 1); if not imm then return end - x = imm <= 127 and format("+0x%02x", imm) - or format("-0x%02x", 256-imm) - pos = pos+1 + x = imm <= 127 ? format("+0x%02x", imm) + : format("-0x%02x", 256-imm) + pos += 1 elseif p == "u" then local imm = getimm(ctx, pos, 1); if not imm then return end x = format("0x%02x", imm) - pos = pos+1 + pos += 1 elseif p == "b" then local imm = getimm(ctx, pos, 1); if not imm then return end x = regs[imm/16+1] - pos = pos+1 + pos += 1 elseif p == "w" then local imm = getimm(ctx, pos, 2); if not imm then return end x = format("0x%x", imm) - pos = pos+2 + pos += 2 elseif p == "o" then -- [offset] if ctx.x64 then local imm1 = getimm(ctx, pos, 4); if not imm1 then return end local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end x = format("[0x%08x%08x]", imm2, imm1) - pos = pos+8 + pos += 8 else local imm = getimm(ctx, pos, 4); if not imm then return end x = format("[0x%08x]", imm) - pos = pos+4 + pos += 4 end elseif p == "i" or p == "I" then local n = map_sz2n[sz] @@ -568,21 +568,21 @@ local function putpat(ctx, name, pat) local imm = getimm(ctx, pos, n); if not imm then return end if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then imm = (0xffffffff+1)-imm - x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm) + x = format(imm > 65535 ? "-0x%08x" : "-0x%x", imm) else - x = format(imm > 65535 and "0x%08x" or "0x%x", imm) + x = format(imm > 65535 ? "0x%08x" : "0x%x", imm) end end - pos = pos+n + pos += n elseif p == "j" then local n = map_sz2n[sz] if n == 8 then n = 4 end local imm = getimm(ctx, pos, n); if not imm then return end - if sz == "B" and imm > 127 then imm = imm-256 - elseif imm > 2147483647 then imm = imm-4294967296 end - pos = pos+n + if sz == "B" and imm > 127 then imm -= 256 + elseif imm > 2147483647 then imm -= 4294967296 end + pos += n imm = imm + pos + ctx.addr - if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end + if imm > 4294967295 and not ctx.x64 then imm -= 4294967296 end ctx.imm = imm if sz == "W" then x = format("word 0x%04x", imm%65536) @@ -593,8 +593,8 @@ local function putpat(ctx, name, pat) x = "0x"..tohex(imm) end elseif p == "R" then - local r = byte(code, pos-1, pos-1)%8 - if ctx.rexb then r = r + 8; ctx.rexb = false end + local r = byte(code, pos-1) & 7 + if ctx.rexb then r += 8; ctx.rexb = false end x = regs[r+1] elseif p == "a" then x = regs[1] elseif p == "c" then x = "cl" @@ -605,25 +605,25 @@ local function putpat(ctx, name, pat) mode = ctx.mrm if not mode then if pos > stop then return incomplete(ctx) end - mode = byte(code, pos, pos) - pos = pos+1 + mode = byte(code, pos) + pos += 1 end - rm = mode%8; mode = (mode-rm)/8 - sp = mode%8; mode = (mode-sp)/8 + rm = mode & 7; mode >>= 3 + sp = mode & 7; mode >>= 3 sdisp = "" if mode < 3 then if rm == 4 then if pos > stop then return incomplete(ctx) end - sc = byte(code, pos, pos) - pos = pos+1 - rm = sc%8; sc = (sc-rm)/8 - rx = sc%8; sc = (sc-rx)/8 - if ctx.rexx then rx = rx + 8; ctx.rexx = false end + sc = byte(code, pos) + pos += 1 + rm = sc & 7; sc >>= 3 + rx = sc & 7; sc >>= 3 + if ctx.rexx then rx += 8; ctx.rexx = false end if rx == 4 then rx = nil end end if mode > 0 or rm == 5 then local dsz = mode - if dsz ~= 1 then dsz = 4 end + if dsz != 1 then dsz = 4 end local disp = getimm(ctx, pos, dsz); if not disp then return end if mode == 0 then rm = nil end if rm or rx or (not sc and ctx.x64 and not ctx.a32) then @@ -637,26 +637,26 @@ local function putpat(ctx, name, pat) else sdisp = format(ctx.x64 and not ctx.a32 and not (disp >= 0 and disp <= 0x7fffffff) - and "0xffffffff%08x" or "0x%08x", disp) + ? "0xffffffff%08x" : "0x%08x", disp) end - pos = pos+dsz + pos += dsz end end - if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end - if ctx.rexr then sp = sp + 8; ctx.rexr = false end + if rm and ctx.rexb then rm += 8; ctx.rexb = false end + if ctx.rexr then sp += 8; ctx.rexr = false end end if p == "m" then if mode == 3 then x = regs[rm+1] else - local aregs = ctx.a32 and map_regs.D or ctx.aregs + local aregs = ctx.a32 ? map_regs.D : ctx.aregs local srm, srx = "", "" if rm then srm = aregs[rm+1] elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end ctx.a32 = false if rx then - if rm then srm = srm.."+" end + if rm then srm ..= "+" end srx = aregs[rx+1] - if sc > 0 then srx = srx.."*"..(2^sc) end + if sc > 0 then srx = srx.."*"..(1 << sc) end end x = format("[%s%s%s]", srm, srx, sdisp) end @@ -686,7 +686,7 @@ local function putpat(ctx, name, pat) error("bad pattern `"..pat.."'") end end - if x then operands = operands and operands..", "..x or x end + if x then operands = operands ? operands..", "..x : x end end ctx.pos = pos return putop(ctx, name, operands) @@ -701,8 +701,8 @@ local function getmrm(ctx) if not mrm then local pos = ctx.pos if pos > ctx.stop then return nil end - mrm = byte(ctx.code, pos, pos) - ctx.pos = pos+1 + mrm = byte(ctx.code, pos) + ctx.pos = pos + 1 ctx.mrm = mrm end return mrm @@ -714,7 +714,7 @@ local function dispatch(ctx, opat, patgrp) if match(opat, "%|") then -- MMX/SSE variants depending on prefix. local p if ctx.rep then - p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)" + p = ctx.rep == "rep" ? "%|([^%|]*)" : "%|[^%|]*%|[^%|]*%|([^%|]*)" ctx.rep = false elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false else p = "^[^%|]*" end @@ -726,7 +726,7 @@ local function dispatch(ctx, opat, patgrp) end if match(opat, "%$") then -- reg$mem variants. local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end - opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)") + opat = match(opat, mrm >= 192 ? "^[^%$]*" : "%$(.*)") if opat == "" then return unknown(ctx) end end if opat == "" then return unknown(ctx) end @@ -738,9 +738,8 @@ end -- Get a pattern from an opcode map and dispatch to handler. local function dispatchmap(ctx, opcmap) local pos = ctx.pos - local opat = opcmap[byte(ctx.code, pos, pos)] - pos = pos + 1 - ctx.pos = pos + local opat = opcmap[byte(ctx.code, pos)] + ctx.pos = pos + 1 return dispatch(ctx, opat) end @@ -760,7 +759,7 @@ map_act = { -- Collect prefixes. [":"] = function(ctx, name, pat) - ctx[pat == ":" and name or sub(pat, 2)] = name + ctx[pat == ":" ? name : sub(pat, 2)] = name if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes. end, @@ -772,7 +771,7 @@ map_act = { -- Use named subtable for opcode group. ["!"] = function(ctx, name, pat) local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end - return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2)) + return dispatch(ctx, map_opcgroup[name][((mrm >> 3) & 7)+1], sub(pat, 2)) end, -- o16,o32[,o64] variants. @@ -825,9 +824,9 @@ map_act = { -- Floating point opcode dispatch. fp = function(ctx, name, pat) local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end - local rm = mrm%8 - local idx = pat*8 + ((mrm-rm)/8)%8 - if mrm >= 192 then idx = idx + 64 end + local rm = mrm & 7 + local idx = ((byte(pat) - 0x30) << 3) | ((mrm >> 3) & 7) + if mrm >= 192 then idx += 64 end local opat = map_opcfp[idx] if type(opat) == "table" then opat = opat[rm+1] end return dispatch(ctx, opat) @@ -847,23 +846,21 @@ map_act = { local pos = ctx.pos if ctx.mrm then ctx.mrm = nil - pos = pos-1 + pos -= 1 end - local b = byte(ctx.code, pos, pos) + local b = byte(ctx.code, pos) if not b then return incomplete(ctx) end - pos = pos+1 + pos += 1 if b < 128 then ctx.rexr = true end local m = 1 if pat == "3" then - m = b%32; b = (b-m)/32 - local nb = b%2; b = (b-nb)/2 - if nb == 0 then ctx.rexb = true end - local nx = b%2 - if nx == 0 then ctx.rexx = true end - b = byte(ctx.code, pos, pos) + m = b & 0x1f + if b & 0x20 == 0 then ctx.rexb = true end + if b & 0x40 == 0 then ctx.rexx = true end + b = byte(ctx.code, pos) if not b then return incomplete(ctx) end - pos = pos+1 - if b >= 128 then ctx.rexw = true end + pos += 1 + if b & 0x80 then ctx.rexw = true end end ctx.pos = pos local map @@ -871,24 +868,23 @@ map_act = { elseif m == 2 then map = map_opc3["38"] elseif m == 3 then map = map_opc3["3a"] else return unknown(ctx) end - local p = b%4; b = (b-p)/4 + local p = b & 3 if p == 1 then ctx.o16 = "o16" elseif p == 2 then ctx.rep = "rep" elseif p == 3 then ctx.rep = "repne" end - local l = b%2; b = (b-l)/2 - if l ~= 0 then ctx.vexl = true end - ctx.vexv = (-1-b)%16 + if b & 4 != 0 then ctx.vexl = true end + ctx.vexv = ~(b >> 3) & 15 return dispatchmap(ctx, map) end, -- Special case for nop with REX prefix. nop = function(ctx, name, pat) - return dispatch(ctx, ctx.rex and pat or "nop") + return dispatch(ctx, ctx.rex ? pat : "nop") end, -- Special case for 0F 77. emms = function(ctx, name, pat) - if ctx.rex ~= "vex" then + if ctx.rex != "vex" then return putop(ctx, "emms") elseif ctx.vexl then ctx.vexl = false @@ -904,8 +900,8 @@ map_act = { -- Disassemble a block of code. local function disass_block(ctx, ofs, len) if not ofs then ofs = 0 end - local stop = len and ofs+len or #ctx.code - ofs = ofs + 1 + local stop = len ? ofs+len : #ctx.code + ofs += 1 ctx.start = ofs ctx.pos = ofs ctx.stop = stop @@ -913,7 +909,7 @@ local function disass_block(ctx, ofs, len) ctx.mrm = false clearprefixes(ctx) while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end - if ctx.pos ~= ctx.start then incomplete(ctx) end + if ctx.pos != ctx.start then incomplete(ctx) end end -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). diff --git a/src/jit/dump.lua b/src/jit/dump.lua index 6a700bbe44..3532e3c4e6 100644 --- a/src/jit/dump.lua +++ b/src/jit/dump.lua @@ -62,7 +62,7 @@ local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr local bit = require("bit") -local band, shr, tohex = bit.band, bit.rshift, bit.tohex +local tohex = bit.tohex local sub, gsub, format = string.sub, string.gsub, string.format local byte, rep = string.byte, string.rep local type, tostring = type, tostring @@ -90,7 +90,7 @@ local function fillsymtab_tr(tr, nexit) end for i=0,nexit-1 do local addr = traceexitstub(tr, i) - if addr < 0 then addr = addr + 2^32 end + if addr < 0 then addr += 2^32 end t[addr] = tostring(i) end local addr = traceexitstub(tr, nexit) @@ -105,9 +105,9 @@ local function fillsymtab(tr, nexit) local ircall = vmdef.ircall for i=0,#ircall do local addr = ircalladdr(i) - if addr ~= 0 then - if maskaddr then addr = band(addr, maskaddr) end - if addr < 0 then addr = addr + 2^32 end + if addr != 0 then + if maskaddr then addr &= maskaddr end + if addr < 0 then addr += 2^32 end t[addr] = ircall[i] end end @@ -123,7 +123,7 @@ local function fillsymtab(tr, nexit) nexit = 1000000 break end - if addr < 0 then addr = addr + 2^32 end + if addr < 0 then addr += 2^32 end t[addr] = tostring(i) end nexitsym = nexit @@ -142,12 +142,12 @@ local function dump_mcode(tr) local mcode, addr, loop = tracemc(tr) if not mcode then return end if not disass then disass = require("jit.dis_"..jit.arch) end - if addr < 0 then addr = addr + 2^32 end + if addr < 0 then addr += 2^32 end out:write("---- TRACE ", tr, " mcode ", #mcode, "\n") local ctx = disass.create(mcode, addr, dumpwrite) ctx.hexdump = 0 ctx.symtab = fillsymtab(tr, info.nexit) - if loop ~= 0 then + if loop != 0 then symtab[addr+loop] = "LOOP" ctx:disass(0, loop) out:write("->LOOP:\n") @@ -233,7 +233,7 @@ local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", } local function colorize_html(s, t, extra) s = gsub(s, "[<>&]", html_escape) return format('%s', - irtype_text[t], extra and " irt_extra" or "", s) + irtype_text[t], extra ? " irt_extra" : "", s) end local irtype_html = setmetatable({}, @@ -268,25 +268,25 @@ local colorize, irtype local litname = { ["SLOAD "] = setmetatable({}, { __index = function(t, mode) local s = "" - if band(mode, 1) ~= 0 then s = s.."P" end - if band(mode, 2) ~= 0 then s = s.."F" end - if band(mode, 4) ~= 0 then s = s.."T" end - if band(mode, 8) ~= 0 then s = s.."C" end - if band(mode, 16) ~= 0 then s = s.."R" end - if band(mode, 32) ~= 0 then s = s.."I" end - if band(mode, 64) ~= 0 then s = s.."K" end + if mode & 1 != 0 then s ..= "P" end + if mode & 2 != 0 then s ..= "F" end + if mode & 4 != 0 then s ..= "T" end + if mode & 8 != 0 then s ..= "C" end + if mode & 16 != 0 then s ..= "R" end + if mode & 32 != 0 then s ..= "I" end + if mode & 64 != 0 then s ..= "K" end t[mode] = s return s end}), ["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", }, ["CONV "] = setmetatable({}, { __index = function(t, mode) - local s = irtype[band(mode, 31)] - s = irtype[band(shr(mode, 5), 31)].."."..s - if band(mode, 0x800) ~= 0 then s = s.." sext" end - local c = shr(mode, 12) - if c == 1 then s = s.." none" - elseif c == 2 then s = s.." index" - elseif c == 3 then s = s.." check" end + local s = irtype[mode & 31] + s = irtype[(mode >> 5) & 31].."."..s + if mode & 0x800 != 0 then s ..= " sext" end + local c = mode >> 12 + if c == 1 then s ..= " none" + elseif c == 2 then s ..= " index" + elseif c == 3 then s ..= " check" end t[mode] = s return s end}), @@ -325,16 +325,16 @@ local function formatk(tr, idx, sn) local s if tn == "number" then if t < 12 then - s = k == 0 and "NULL" or format("[0x%08x]", k) - elseif band(sn or 0, 0x30000) ~= 0 then - s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz" + s = k == 0 ? "NULL" : format("[0x%08x]", k) + elseif (sn or 0) & 0x30000 != 0 then + s = sn & 0x20000 != 0 ? "contpc" : "ftsz" elseif k == 2^52+2^51 then s = "bias" else - s = format(0 < k and k < 0x1p-1026 and "%+a" or "%+.14g", k) + s = format(0 < k and k < 0x1p-1026 ? "%+a" : "%+.14g", k) end elseif tn == "string" then - s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub)) + s = format(#k > 20 ? '"%.20s"~' : '"%s"', gsub(k, "%c", ctlsub)) elseif tn == "function" then s = fmtfunc(k) elseif tn == "table" then @@ -348,13 +348,13 @@ local function formatk(tr, idx, sn) end elseif t == 21 then -- int64_t s = sub(tostring(k), 1, -3) - if sub(s, 1, 1) ~= "-" then s = "+"..s end + if sub(s, 1, 1) != "-" then s = "+"..s end elseif sn == 0x1057fff then -- SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL) return "----" -- Special case for LJ_FR2 slot 1. else s = tostring(k) -- For primitives. end - s = colorize(format("%-4s", s), t, band(sn or 0, 0x100000) ~= 0) + s = colorize(format("%-4s", s), t, (sn or 0) & 0x100000 != 0) if slot then s = format("%s @%d", s, slot) end @@ -365,18 +365,18 @@ local function printsnap(tr, snap) local n = 2 for s=0,snap[1]-1 do local sn = snap[n] - if shr(sn, 24) == s then - n = n + 1 - local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS + if sn >> 24 == s then + n += 1 + local ref = (sn & 0xffff) - 0x8000 -- REF_BIAS if ref < 0 then out:write(formatk(tr, ref, sn)) - elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM + elseif sn & 0x80000 != 0 then -- SNAP_SOFTFPNUM out:write(colorize(format("%04d/%04d", ref, ref+1), 14)) else local m, ot, op1, op2 = traceir(tr, ref) - out:write(colorize(format("%04d", ref), band(ot, 31), band(sn, 0x100000) ~= 0)) + out:write(colorize(format("%04d", ref), ot & 31, sn & 0x100000 != 0)) end - out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME + out:write(sn & 0x10000 == 0 ? " " : "|") -- SNAP_FRAME else out:write("---- ") end @@ -398,9 +398,9 @@ end -- Return a register name or stack slot for a rid/sp location. local function ridsp_name(ridsp, ins) if not disass then disass = require("jit.dis_"..jit.arch) end - local rid, slot = band(ridsp, 0xff), shr(ridsp, 8) + local rid, slot = ridsp & 0xff, ridsp >> 8 if rid == 253 or rid == 254 then - return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot) + return (slot == 0 or slot == 255) ? " {sink" : format(" {%04d", ins-slot) end if ridsp > 255 then return format("[%x]", slot*4) end if rid < 128 then return disass.regname(rid) end @@ -412,7 +412,7 @@ local function dumpcallfunc(tr, ins) local ctype if ins > 0 then local m, ot, op1, op2 = traceir(tr, ins) - if band(ot, 31) == 0 then -- nil type means CARG(func, ctype). + if ot & 31 == 0 then -- nil type means CARG(func, ctype). ins = op1 ctype = formatk(tr, op2) end @@ -431,7 +431,7 @@ local function dumpcallargs(tr, ins) out:write(formatk(tr, ins)) else local m, ot, op1, op2 = traceir(tr, ins) - local oidx = 6*shr(ot, 8) + local oidx = 6 * (ot >> 8) local op = sub(vmdef.irnames, oidx+1, oidx+6) if op == "CARG " then dumpcallargs(tr, op1) @@ -468,12 +468,12 @@ local function dump_ir(tr, dumpsnap, dumpreg) out:write(format(".... SNAP #%-3d [ ", snapno)) end printsnap(tr, snap) - snapno = snapno + 1 + snapno += 1 snap = tracesnap(tr, snapno) - snapref = snap and snap[0] or 65536 + snapref = snap ? snap[0] : 65536 end local m, ot, op1, op2, ridsp = traceir(tr, ins) - local oidx, t = 6*shr(ot, 8), band(ot, 31) + local oidx, t = 6 * (ot >> 8), ot & 31 local op = sub(irnames, oidx+1, oidx+6) if op == "LOOP " then if dumpreg then @@ -481,45 +481,45 @@ local function dump_ir(tr, dumpsnap, dumpreg) else out:write(format("%04d ------ LOOP ------------\n", ins)) end - elseif op ~= "NOP " and op ~= "CARG " and - (dumpreg or op ~= "RENAME") then - local rid = band(ridsp, 255) + elseif op != "NOP " and op != "CARG " and + (dumpreg or op != "RENAME") then + local rid = ridsp & 255 if dumpreg then out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins))) else out:write(format("%04d ", ins)) end out:write(format("%s%s %s %s ", - (rid == 254 or rid == 253) and "}" or - (band(ot, 128) == 0 and " " or ">"), - band(ot, 64) == 0 and " " or "+", + rid == 254 or rid == 253 ? "}" : + ot & 128 == 0 ? " " : ">", + ot & 64 == 0 ? " " : "+", irtype[t], op)) - local m1, m2 = band(m, 3), band(m, 3*4) + local m1, m2 = m & 3, m & (3 << 2) if sub(op, 1, 4) == "CALL" then local ctype - if m2 == 1*4 then -- op2 == IRMlit + if m2 == 1 << 2 then -- op2 == IRMlit out:write(format("%-10s (", vmdef.ircall[op2])) else ctype = dumpcallfunc(tr, op2) end - if op1 ~= -1 then dumpcallargs(tr, op1) end + if op1 != -1 then dumpcallargs(tr, op1) end out:write(")") if ctype then out:write(" ctype ", ctype) end elseif op == "CNEW " and op2 == -1 then out:write(formatk(tr, op1)) - elseif m1 ~= 3 then -- op1 != IRMnone + elseif m1 != 3 then -- op1 != IRMnone if op1 < 0 then out:write(formatk(tr, op1)) else - out:write(format(m1 == 0 and "%04d" or "#%-3d", op1)) + out:write(format(m1 == 0 ? "%04d" : "#%-3d", op1)) end - if m2 ~= 3*4 then -- op2 != IRMnone - if m2 == 1*4 then -- op2 == IRMlit + if m2 != 3 << 2 then -- op2 != IRMnone + if m2 == 1 << 2 then -- op2 == IRMlit local litn = litname[op] if litn and litn[op2] then out:write(" ", litn[op2]) elseif op == "UREFO " or op == "UREFC " then - out:write(format(" #%-3d", shr(op2, 8))) + out:write(format(" #%-3d", op2 >> 8)) else out:write(format(" #%-3d", op2)) end @@ -572,7 +572,7 @@ local function dump_trace(what, tr, func, pc, otr, oex) if what == "start" then if dumpmode.H then out:write('
    \n') end
         out:write("---- TRACE ", tr, " ", what)
    -    if otr then out:write(" ", otr, "/", oex == -1 and "stitch" or oex) end
    +    if otr then out:write(" ", otr, "/", oex == -1 ? "stitch" : oex) end
         out:write(" ", fmtfunc(func, pc), "\n")
       elseif what == "stop" or what == "abort" then
         out:write("---- TRACE ", tr, " ", what)
    @@ -599,7 +599,7 @@ end
     
     -- Dump recorded bytecode.
     local function dump_record(tr, func, pc, depth)
    -  if depth ~= recdepth then
    +  if depth != recdepth then
         recdepth = depth
         recprefix = rep(" .", depth)
       end
    @@ -615,7 +615,7 @@ local function dump_record(tr, func, pc, depth)
       else
         out:write(line)
       end
    -  if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
    +  if pc >= 0 and funcbc(func, pc) & 0xff < 16 then -- ORDER BC
         out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
       end
     end
    @@ -664,7 +664,7 @@ local function dumpoff()
         jit.attach(dump_texit)
         jit.attach(dump_record)
         jit.attach(dump_trace)
    -    if out and out ~= stdout and out ~= stderr then out:close() end
    +    if out and out != stdout and out != stderr then out:close() end
         out = nil
       end
     end
    @@ -674,16 +674,16 @@ local function dumpon(opt, outfile)
       if active then dumpoff() end
     
       local term = os.getenv("TERM")
    -  local colormode = (term and term:match("color") or os.getenv("COLORTERM")) and "A" or "T"
    +  local colormode = (term ? (term:match("color")) : os.getenv("COLORTERM")) ? "A" : "T"
       if opt then
         opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
       end
     
       local m = { t=true, b=true, i=true, m=true, }
    -  if opt and opt ~= "" then
    +  if opt and opt != "" then
         local o = sub(opt, 1, 1)
    -    if o ~= "+" and o ~= "-" then m = {} end
    -    for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
    +    if o != "+" and o != "-" then m = {} end
    +    for i=1,#opt do m[sub(opt, i, i)] = (o != "-") end
       end
       dumpmode = m
     
    @@ -700,7 +700,7 @@ local function dumpon(opt, outfile)
     
       if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
       if outfile then
    -    out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
    +    out = outfile == "-" ? stdout : assert(io.open(outfile, "w"))
       else
         out = stdout
       end
    diff --git a/src/jit/p.lua b/src/jit/p.lua
    index 9d938ce568..bb04fbd21e 100644
    --- a/src/jit/p.lua
    +++ b/src/jit/p.lua
    @@ -120,7 +120,7 @@ end
     local function prof_top(count1, count2, samples, indent)
       local t, n = {}, 0
       for k in pairs(count1) do
    -    n = n + 1
    +    n += 1
         t[n] = k
       end
       sort(t, function(a, b) return count1[a] > count1[b] end)
    @@ -184,7 +184,7 @@ local function prof_annotate(count1, samples)
         out:write(format("\n====== %s ======\n", file))
         local fl = files[file]
         local n, show = 1, false
    -    if ann ~= 0 then
    +    if ann != 0 then
           for i=1,ann do
     	if fl[i] then show = true; out:write("@@ 1 @@\n"); break end
           end
    @@ -195,7 +195,7 @@ local function prof_annotate(count1, samples)
     	break
           end
           local v = fl[n]
    -      if ann ~= 0 then
    +      if ann != 0 then
     	local v2 = fl[n+ann]
     	if show then
     	  if v2 then show = n+ann elseif v then show = n
    @@ -212,7 +212,7 @@ local function prof_annotate(count1, samples)
     	out:write(format(fmtn, line))
           end
         ::next::
    -      n = n + 1
    +      n += 1
         end
         fp:close()
       end
    @@ -226,7 +226,7 @@ local function prof_finish()
         profile.stop()
         local samples = prof_samples
         if samples == 0 then
    -      if prof_raw ~= true then out:write("[No samples collected]\n") end
    +      if prof_raw != true then out:write("[No samples collected]\n") end
         elseif prof_ann then
           prof_annotate(prof_count1, samples)
         else
    @@ -235,7 +235,7 @@ local function prof_finish()
         prof_count1 = nil
         prof_count2 = nil
         prof_ud = nil
    -    if out ~= stdout then out:close() end
    +    if out != stdout then out:close() end
       end
     end
     
    @@ -270,7 +270,7 @@ local function prof_start(mode)
         prof_fmt = "pl"
         prof_split = 0
         prof_depth = 1
    -  elseif m.G and scope ~= "" then
    +  elseif m.G and scope != "" then
         prof_fmt = flags..scope.."Z;"
         prof_depth = -100
         prof_raw = true
    diff --git a/src/jit/v.lua b/src/jit/v.lua
    index 69443d3169..849915ab7f 100644
    --- a/src/jit/v.lua
    +++ b/src/jit/v.lua
    @@ -107,7 +107,7 @@ local function dump_trace(what, tr, func, pc, otr, oex)
       else
         if what == "abort" then
           local loc = fmtfunc(func, pc)
    -      if loc ~= startloc then
    +      if loc != startloc then
     	out:write(format("[TRACE --- %s%s -- %s at %s]\n",
     	  startex, startloc, fmterr(otr, oex), loc))
           else
    @@ -147,7 +147,7 @@ local function dumpoff()
       if active then
         active = false
         jit.attach(dump_trace)
    -    if out and out ~= stdout and out ~= stderr then out:close() end
    +    if out and out != stdout and out != stderr then out:close() end
         out = nil
       end
     end
    
    From 1edc3e52b67eaf6ce5f809be8e17d6862594b8bc Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Mon, 3 Aug 2026 15:24:25 +0200
    Subject: [PATCH 23/34] x64/LJ_GC64: Fix XLOAD fusion.
    
    Reported by FMiS. #1500
    ---
     src/lj_asm_x86.h | 4 +++-
     1 file changed, 3 insertions(+), 1 deletion(-)
    
    diff --git a/src/lj_asm_x86.h b/src/lj_asm_x86.h
    index 66914834e5..a2cf8fe9bd 100644
    --- a/src/lj_asm_x86.h
    +++ b/src/lj_asm_x86.h
    @@ -347,8 +347,10 @@ static void asm_fusexref(ASMState *as, IRRef ref, RegSet allow)
     #else
         as->mrm.ofs = ir->i;
         as->mrm.base = RID_NONE;
    +    return;
     #endif
    -  } else if (ir->o == IR_STRREF) {
    +  }
    +  if (ir->o == IR_STRREF) {
         asm_fusestrref(as, ir, allow);
       } else {
         as->mrm.ofs = 0;
    
    From 52cdb12eb4b13d5608a00c3cbb669429b9164671 Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Tue, 18 Aug 2026 14:59:40 +0200
    Subject: [PATCH 24/34] Compile unpack().
    
    Thanks to Sander Bos.
    ---
     src/lib_base.c    |  2 +-
     src/lj_ffrecord.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++
     2 files changed, 52 insertions(+), 1 deletion(-)
    
    diff --git a/src/lib_base.c b/src/lib_base.c
    index bf98f2842c..f120b2dece 100644
    --- a/src/lib_base.c
    +++ b/src/lib_base.c
    @@ -224,7 +224,7 @@ LJLIB_CF(rawlen)		LJLIB_REC(.)
     }
     #endif
     
    -LJLIB_CF(unpack)
    +LJLIB_CF(unpack)		LJLIB_REC(.)
     {
       GCtab *t = lj_lib_checktab(L, 1);
       int32_t n, i = lj_lib_optint(L, 2, 1);
    diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c
    index ef8e554bd5..ff65f1966e 100644
    --- a/src/lj_ffrecord.c
    +++ b/src/lj_ffrecord.c
    @@ -314,6 +314,57 @@ static void LJ_FASTCALL recff_rawlen(jit_State *J, RecordFFData *rd)
     }
     #endif
     
    +static void LJ_FASTCALL recff_unpack(jit_State *J, RecordFFData *rd)
    +{
    +  TRef trtab, trstart, trend;
    +  int32_t start, end;
    +  GCtab *t;
    +  /* Check for table. */
    +  trtab = J->base[0];
    +  if (!tref_istab(trtab)) return;  /* Interpreter will throw. */
    +  t = tabV(&rd->argv[0]);
    +  /* Starting index. */
    +  trstart = J->base[1];
    +  if (tref_isnil(trstart)) {  /* Default start = 1. */
    +    start = 1;
    +    trstart = lj_ir_kint(J, 1);
    +  } else {
    +    start = argv2int(J, &rd->argv[1]);
    +    trstart = lj_opt_narrow_toint(J, trstart);
    +    if (!tref_isk(trstart))
    +      emitir(IRTGI(IR_EQ), trstart, lj_ir_kint(J, start));
    +  }
    +  /* Ending index. */
    +  trend = J->base[2];
    +  if (tref_isnil(trend)) {  /* Default end = #t. */
    +    end = (int32_t)lj_tab_len(t);
    +    trend = emitir(IRTI(IR_ALEN), trtab, TREF_NIL);
    +  } else {
    +    end = argv2int(J, &rd->argv[2]);
    +    trend = lj_opt_narrow_toint(J, trend);
    +  }
    +  if (!tref_isk(trend))
    +    emitir(IRTGI(IR_EQ), trend, lj_ir_kint(J, end));
    +  if (start <= end) {
    +    RecordIndex ix;
    +    int32_t i;
    +    uint32_t len = (uint32_t)end - (uint32_t)start;
    +    if (len > LJ_MAX_JSLOTS || (uint32_t)J->baseslot + len > LJ_MAX_JSLOTS)
    +      lj_trace_err_info(J, LJ_TRERR_STACKOV);
    +    rd->nres = (ptrdiff_t)(len + 1);
    +    ix.tab = trtab; ix.val = 0; ix.idxchain = 0;
    +    settabV(J->L, &ix.tabv, t);
    +    for (i = start; i <= end; i++) {
    +      ix.key = lj_ir_kint(J, i);
    +      setintV(&ix.keyv, i);
    +      J->base[i - start] = lj_record_idx(J, &ix);
    +    }
    +  } else {  /* Empty result. */
    +    emitir(IRTGI(IR_LT), trend, trstart);
    +    rd->nres = 0;
    +  }
    +}
    +
     /* Determine mode of select() call. */
     int32_t lj_ffrecord_select_mode(jit_State *J, TRef tr, TValue *tv)
     {
    
    From 71797fa75ffa1ddbd9b41bbd55d6c2d4236164b0 Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Tue, 18 Aug 2026 15:01:31 +0200
    Subject: [PATCH 25/34] FFI: Mark cts->L in atomic phase.
    
    Reported by zyxwvu Shi. #1506
    ---
     src/lj_ccallback.c | 1 +
     src/lj_gc.c        | 4 ++++
     2 files changed, 5 insertions(+)
    
    diff --git a/src/lj_ccallback.c b/src/lj_ccallback.c
    index 2fa8e7ce0c..802aa7c4b6 100644
    --- a/src/lj_ccallback.c
    +++ b/src/lj_ccallback.c
    @@ -533,6 +533,7 @@ lua_State * LJ_FASTCALL lj_ccallback_enter(CTState *cts, void *cf)
       lua_State *L = cts->L;
       global_State *g = cts->g;
       lua_assert(L != NULL);
    +  lua_assert(!isdead(g, obj2gco(L)));
       if (gcref(g->jit_L)) {
         setstrV(L, L->top++, lj_err_str(L, LJ_ERR_FFI_BADCBACK));
         if (g->panic) g->panic(L);
    diff --git a/src/lj_gc.c b/src/lj_gc.c
    index c79dbdee65..944916df16 100644
    --- a/src/lj_gc.c
    +++ b/src/lj_gc.c
    @@ -586,6 +586,10 @@ static void atomic(global_State *g, lua_State *L)
       setgcrefnull(g->gc.weak);
       lua_assert(!iswhite(obj2gco(mainthread(g))));
       gc_markobj(g, L);  /* Mark running thread. */
    +#if LJ_HASFFI
    +  if (ctype_ctsG(g) && ctype_ctsG(g)->L)  /* Mark cts->L thread. */
    +    gc_markobj(g, ctype_ctsG(g)->L);
    +#endif
       gc_traverse_curtrace(g);  /* Traverse current trace. */
       gc_mark_gcroot(g);  /* Mark GC roots (again). */
       gc_propagate_gray(g);  /* Propagate all of the above. */
    
    From c55691087e743c1750c454b9da9918ff85697a55 Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Tue, 18 Aug 2026 15:05:15 +0200
    Subject: [PATCH 26/34] Handle OOM error during trace stitching.
    
    Thanks to Sergey Kaplun. #1502
    ---
     src/lj_ffrecord.c | 20 ++++++++++----------
     1 file changed, 10 insertions(+), 10 deletions(-)
    
    diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c
    index ff65f1966e..00315abc29 100644
    --- a/src/lj_ffrecord.c
    +++ b/src/lj_ffrecord.c
    @@ -118,16 +118,7 @@ static void recff_stitch(jit_State *J)
       TValue *pframe = frame_prevl(base-1);
       int errcode;
     
    -  /* Move func + args up in Lua stack and insert continuation. */
    -  memmove(&base[1], &base[-1-LJ_FR2], sizeof(TValue)*nslot);
    -  setframe_ftsz(nframe, ((char *)nframe - (char *)pframe) + FRAME_CONT);
    -  setcont(base-LJ_FR2, cont);
    -  setframe_pc(base, pc);
    -  setnilV(base-1-LJ_FR2);  /* Incorrect, but rec_check_slots() won't run anymore. */
    -  L->base += 2 + LJ_FR2;
    -  L->top += 2 + LJ_FR2;
    -
    -  /* Ditto for the IR. */
    +  /* Move func + args up in IR slots and insert continuation. */
       memmove(&J->base[1], &J->base[-1-LJ_FR2], sizeof(TRef)*nslot);
     #if LJ_FR2
       J->base[2] = TREF_FRAME;
    @@ -141,6 +132,15 @@ static void recff_stitch(jit_State *J)
       J->baseslot += 2 + LJ_FR2;
       J->framedepth++;
     
    +  /* Ditto for the Lua stack. */
    +  memmove(&base[1], &base[-1-LJ_FR2], sizeof(TValue)*nslot);
    +  setframe_ftsz(nframe, ((char *)nframe - (char *)pframe) + FRAME_CONT);
    +  setcont(base-LJ_FR2, cont);
    +  setframe_pc(base, pc);
    +  setnilV(base-1-LJ_FR2);  /* Incorrect, but rec_check_slots() won't run anymore. */
    +  L->base += 2 + LJ_FR2;
    +  L->top += 2 + LJ_FR2;
    +
       errcode = lj_vm_cpcall(L, NULL, J, rec_stop_stitch_cp);
     
       /* Undo Lua stack changes. */
    
    From 27f169c6c64175896d86e14f0d23c8d85c119c2c Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Tue, 18 Aug 2026 15:08:34 +0200
    Subject: [PATCH 27/34] Fix next() recording immediately after ITERN
     despecialization.
    
    Thanks to rvanschoren. #1510
    ---
     src/lj_ffrecord.c | 3 +++
     1 file changed, 3 insertions(+)
    
    diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c
    index 00315abc29..697dcde03a 100644
    --- a/src/lj_ffrecord.c
    +++ b/src/lj_ffrecord.c
    @@ -601,6 +601,9 @@ static void LJ_FASTCALL recff_next(jit_State *J, RecordFFData *rd)
         if (tref_isnil(J->base[1])) {  /* Shortcut for start of traversal. */
           ix.key = lj_ir_kint(J, 0);
           keyv = niltvg(J2G(J));
    +    } else if ((J->base[1] & TREF_KEYINDEX)) {
    +      ix.key = J->base[1] & ~TREF_KEYINDEX;
    +      keyv = &rd->argv[1];
         } else {
           TRef tmp = recff_tmpref(J, J->base[1], IRTMPREF_IN1);
           ix.key = lj_ir_call(J, IRCALL_lj_tab_keyindex, tab, tmp);
    
    From 21ecb3610e0ef5f9f6badf5119b2098d351db773 Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Wed, 19 Aug 2026 18:21:10 +0200
    Subject: [PATCH 28/34] Fix unpack() recording.
    
    Thanks to Appla. #1512
    ---
     src/lj_ffrecord.c | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c
    index 697dcde03a..20e0455f7e 100644
    --- a/src/lj_ffrecord.c
    +++ b/src/lj_ffrecord.c
    @@ -354,10 +354,11 @@ static void LJ_FASTCALL recff_unpack(jit_State *J, RecordFFData *rd)
         rd->nres = (ptrdiff_t)(len + 1);
         ix.tab = trtab; ix.val = 0; ix.idxchain = 0;
         settabV(J->L, &ix.tabv, t);
    -    for (i = start; i <= end; i++) {
    +    for (i = start; ; i++) {
           ix.key = lj_ir_kint(J, i);
           setintV(&ix.keyv, i);
           J->base[i - start] = lj_record_idx(J, &ix);
    +      if (i == end) break;
         }
       } else {  /* Empty result. */
         emitir(IRTGI(IR_LT), trend, trstart);
    
    From 8c23d6aad7f39d58c4ca949426a3f18e900a9485 Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Wed, 19 Aug 2026 20:56:02 +0200
    Subject: [PATCH 29/34] Fix unpack() recording.
    
    Thanks to Christian Clason and Justin M. Keyes. #1513
    ---
     src/lj_ffrecord.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c
    index 20e0455f7e..41dee1caf6 100644
    --- a/src/lj_ffrecord.c
    +++ b/src/lj_ffrecord.c
    @@ -336,7 +336,7 @@ static void LJ_FASTCALL recff_unpack(jit_State *J, RecordFFData *rd)
       }
       /* Ending index. */
       trend = J->base[2];
    -  if (tref_isnil(trend)) {  /* Default end = #t. */
    +  if (J->maxslot < 3 || tref_isnil(trend)) {  /* Default end = #t. */
         end = (int32_t)lj_tab_len(t);
         trend = emitir(IRTI(IR_ALEN), trtab, TREF_NIL);
       } else {
    
    From 1ee778a4e37122d8ca7d5733c590a47dafd6b15c Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Wed, 19 Aug 2026 20:57:39 +0200
    Subject: [PATCH 30/34] Add FOLD rule for ALEN of TNEW/TDUP.
    
    ---
     src/lj_opt_mem.c | 23 +++++++++++++++++++++++
     1 file changed, 23 insertions(+)
    
    diff --git a/src/lj_opt_mem.c b/src/lj_opt_mem.c
    index c7098777b5..764cdd84cd 100644
    --- a/src/lj_opt_mem.c
    +++ b/src/lj_opt_mem.c
    @@ -397,6 +397,7 @@ TRef LJ_FASTCALL lj_opt_fwd_alen(jit_State *J)
       IRRef tab = fins->op1;  /* Table reference. */
       IRRef lim = tab;  /* Search limit. */
       IRRef ref;
    +  IROp op;
     
       /* Search for conflicting HSTORE with numeric key. */
       ref = J->chain[IR_HSTORE];
    @@ -448,6 +449,28 @@ TRef LJ_FASTCALL lj_opt_fwd_alen(jit_State *J)
         }
         ref = IR(ref)->prev;
       }
    +
    +  /* Try to const-fold length. */
    +  op = IR(tab)->o;
    +  if (lim == tab &&
    +      (op == IR_TNEW || op == IR_TDUP) &&
    +      fwd_aa_tab_clear(J, tab, tab)) {
    +    /* Search for conflicting store. */
    +    int32_t len = 0;
    +    IRRef sref = J->chain[IR_ASTORE];
    +    while (sref > ref) {
    +      IRIns *store = IR(sref);
    +      IRIns *aref = IR(store->op1);
    +      IRIns *fref = IR(aref->op1);
    +      if (tab == fref->op1 || aa_table(J, tab, fref->op1) != ALIAS_NO) {
    +	goto doemit;  /* Conflicting store. */
    +      }
    +      sref = store->prev;
    +    }
    +    if (op == IR_TDUP) len = (int32_t)lj_tab_len(ir_ktab(IR(IR(tab)->op1)));
    +    return lj_ir_kint(J, len);
    +  }
    +
     doemit:
       return EMITFOLD;
     }
    
    From 3000f7cdb1123f2a4d8b690d4292c541359c670c Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Thu, 3 Sep 2026 20:25:44 +0200
    Subject: [PATCH 31/34] FFI: Adjust J->maxslot after ffi.offsetof.
    
    Reported by Elias Hogstvedt. #1514
    ---
     src/lj_record.c | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/src/lj_record.c b/src/lj_record.c
    index a1039d1531..437735b6b6 100644
    --- a/src/lj_record.c
    +++ b/src/lj_record.c
    @@ -1712,6 +1712,8 @@ void lj_record_ins(jit_State *J)
           {
     	BCReg s;
     	TValue *tv = J->L->base;
    +	ptrdiff_t delta = J->L->top - tv;
    +	if (J->maxslot > (BCReg)delta) J->maxslot = (BCReg)delta;
     	for (s = 0; s < J->maxslot; s++)  /* Constify stack slots (if any). */
     	  if (J->base[s] == TREF_NIL && !tvisnil(&tv[s]))
     	    J->base[s] = lj_record_constify(J, &tv[s]);
    
    From c6ffc141a8762b41703f9287d63d93622a13dd8f Mon Sep 17 00:00:00 2001
    From: Mike Pall 
    Date: Tue, 8 Sep 2026 10:43:01 +0200
    Subject: [PATCH 32/34] PPC: Fix string.* fast functions with P64 set (testing
     only).
    
    Thanks to Minsoo Choo.
    ---
     src/vm_ppc.dasc | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/src/vm_ppc.dasc b/src/vm_ppc.dasc
    index 8d257fdf55..723f797bbb 100644
    --- a/src/vm_ppc.dasc
    +++ b/src/vm_ppc.dasc
    @@ -2521,11 +2521,11 @@ static void build_subroutines(BuildCtx *ctx)
       |  checkstr CARG3
       |   la SBUF:CARG1, DISPATCH_GL(tmpbuf)(DISPATCH)
       |  bne ->fff_fallback
    -  |   lwz TMP0, SBUF:CARG1->b
    +  |   lp TMP0, SBUF:CARG1->b
       |  stw L, SBUF:CARG1->L
       |  stp BASE, L->base
       |  stw PC, SAVE_PC
    -  |   stw TMP0, SBUF:CARG1->w
    +  |   stp TMP0, SBUF:CARG1->w
       |  bl extern lj_buf_putstr_ .. name
       |  bl extern lj_buf_tostr
       |  b ->fff_resstr
    
    From c55a6ddec0210295f864733c6f7847261c5df594 Mon Sep 17 00:00:00 2001
    From: Charles Grunwald 
    Date: Mon, 14 Sep 2026 00:31:26 +0100
    Subject: [PATCH 33/34] Add additional MSVC fixes.
    
    ---
     src/lj_arena.c    |   2 +-
     src/lj_atomic.h   | 172 +++++++++++++++++++++++++++++++++++++++++++++-
     src/lj_gc2.c      |   2 +-
     src/lj_gc2token.h |   2 +-
     src/lj_markword.h |   2 +-
     src/lj_tgslot.h   |   2 +-
     src/lj_universe.h |   2 +-
     7 files changed, 177 insertions(+), 7 deletions(-)
    
    diff --git a/src/lj_arena.c b/src/lj_arena.c
    index c4a4e91379..4bebbcabe8 100644
    --- a/src/lj_arena.c
    +++ b/src/lj_arena.c
    @@ -1681,7 +1681,7 @@ void lj_arena_scan_free_runs(const GCArena *a, LJArenaRunCB cb, void *ud)
           i = (i | 63u) + 1u;
           continue;
         }
    -    i += (uint32_t)__builtin_ctzll(starts);
    +    i += lj_ffs64(starts);
         if (i >= LJ_ARENA_CELLS)
           break;
         st = (!arena_side_owners_none(a, i) ||
    diff --git a/src/lj_atomic.h b/src/lj_atomic.h
    index 45c5f33323..8a1b27de33 100644
    --- a/src/lj_atomic.h
    +++ b/src/lj_atomic.h
    @@ -17,8 +17,176 @@
     #include 
     #include 
     
    +#if defined(_MSC_VER) && !defined(__clang__)
    +
    +#if !defined(_M_X64) && !defined(_M_IX86)
    +#error "lj_atomic.h MSVC backend assumes x86/x86-64 TSO for its compiler-only \
    +  acquire/release barriers (_ReadBarrier/_WriteBarrier); this target's memory \
    +  model needs real hardware fences (e.g. via MemoryBarrier()) before use."
    +#endif
    +
    +#include 
    +#include 
    +
    +#define LA_INLINE static __forceinline
    +#ifndef __alignof__
    +#define __alignof__(type) __alignof(type)
    +#endif
    +
    +/* MSVC does not expose the C11 atomic API for plain storage. The Interlocked
    +** intrinsics are full barriers, which is stronger than the order requested by
    +** the API below. Plain x86-64 loads/stores are atomic; compiler barriers keep
    +** acquire/release accesses from being reordered by the optimizer. */
    +enum {
    +  LA_RLX = 0,
    +  LA_ACQ = 1,
    +  LA_REL = 2,
    +  LA_ACQ_REL = 3,
    +  LA_SEQ = 4
    +};
    +
    +LA_INLINE uint8_t la_load8_rlx(const uint8_t *p)
    +{ return *(const volatile uint8_t *)p; }
    +LA_INLINE uint32_t la_load32_rlx(const uint32_t *p)
    +{ return *(const volatile uint32_t *)p; }
    +LA_INLINE uint64_t la_load64_rlx(const uint64_t *p)
    +{ return *(const volatile uint64_t *)p; }
    +LA_INLINE uintptr_t la_loaduptr_rlx(const uintptr_t *p)
    +{ return *(const volatile uintptr_t *)p; }
    +LA_INLINE uint8_t la_load8_acq(const uint8_t *p)
    +{ uint8_t v = *(const volatile uint8_t *)p; _ReadBarrier(); return v; }
    +LA_INLINE uint16_t la_load16_acq(const uint16_t *p)
    +{ uint16_t v = *(const volatile uint16_t *)p; _ReadBarrier(); return v; }
    +LA_INLINE uint32_t la_load32_acq(const uint32_t *p)
    +{ uint32_t v = *(const volatile uint32_t *)p; _ReadBarrier(); return v; }
    +LA_INLINE uint64_t la_load64_acq(const uint64_t *p)
    +{ uint64_t v = *(const volatile uint64_t *)p; _ReadBarrier(); return v; }
    +LA_INLINE uintptr_t la_loaduptr_acq(const uintptr_t *p)
    +{ uintptr_t v = *(const volatile uintptr_t *)p; _ReadBarrier(); return v; }
    +LA_INLINE void *la_loadptr_rlx(void *const *p)
    +{ return *(void *volatile const *)p; }
    +LA_INLINE void *la_loadptr_acq(void *const *p)
    +{ void *v = *(void *volatile const *)p; _ReadBarrier(); return v; }
    +#define la_loadfunc_acq(p) \
    +  _InterlockedCompareExchangePointer((void *volatile *)(p), NULL, NULL)
    +
    +LA_INLINE void la_store8_rlx(uint8_t *p, uint8_t v)
    +{ *(volatile uint8_t *)p = v; }
    +LA_INLINE void la_store8_rel(uint8_t *p, uint8_t v)
    +{ _WriteBarrier(); *(volatile uint8_t *)p = v; }
    +LA_INLINE void la_store32_rlx(uint32_t *p, uint32_t v)
    +{ *(volatile uint32_t *)p = v; }
    +LA_INLINE void la_store32_rel(uint32_t *p, uint32_t v)
    +{ _WriteBarrier(); *(volatile uint32_t *)p = v; }
    +LA_INLINE void la_store64_rlx(uint64_t *p, uint64_t v)
    +{ *(volatile uint64_t *)p = v; }
    +LA_INLINE void la_storeuptr_rlx(uintptr_t *p, uintptr_t v)
    +{ *(volatile uintptr_t *)p = v; }
    +LA_INLINE void la_store64_rel(uint64_t *p, uint64_t v)
    +{ _WriteBarrier(); *(volatile uint64_t *)p = v; }
    +LA_INLINE void la_storeuptr_rel(uintptr_t *p, uintptr_t v)
    +{ _WriteBarrier(); *(volatile uintptr_t *)p = v; }
    +LA_INLINE void la_store16_rel(uint16_t *p, uint16_t v)
    +{ _WriteBarrier(); *(volatile uint16_t *)p = v; }
    +LA_INLINE void la_storeptr_rlx(void **p, void *v)
    +{ *(void *volatile *)p = v; }
    +LA_INLINE void la_storeptr_rel(void **p, void *v)
    +{ _WriteBarrier(); *(void *volatile *)p = v; }
    +#define la_storefunc_rel(p, v) \
    +  ((void)_InterlockedExchangePointer((void *volatile *)(p), (void *)(v)))
    +
    +#define LA_MSVC_CAS(namebits, intrinsicbits, type, itype) \
    +  LA_INLINE int la_cas##namebits(type *p, type *exp, type des, int mo_s, int mo_f) \
    +  { \
    +    itype old; \
    +    (void)mo_s; (void)mo_f; \
    +    old = _InterlockedCompareExchange##intrinsicbits((volatile itype *)p, \
    +                                                     (itype)des, (itype)*exp); \
    +    if ((type)old == *exp) return 1; \
    +    *exp = (type)old; \
    +    return 0; \
    +  }
    +LA_MSVC_CAS(8, 8, uint8_t, char)
    +LA_MSVC_CAS(16, 16, uint16_t, short)
    +LA_MSVC_CAS(32, , uint32_t, long)
    +LA_MSVC_CAS(64, 64, uint64_t, __int64)
    +#undef LA_MSVC_CAS
    +
    +LA_INLINE int la_casuptr(uintptr_t *p, uintptr_t *exp, uintptr_t des,
    +                         int mo_s, int mo_f)
    +{ return la_cas64((uint64_t *)p, (uint64_t *)exp, (uint64_t)des, mo_s, mo_f); }
    +LA_INLINE int la_casptr(void **p, void **exp, void *des, int mo_s, int mo_f)
    +{
    +  void *old;
    +  (void)mo_s; (void)mo_f;
    +  old = _InterlockedCompareExchangePointer((void *volatile *)p, des, *exp);
    +  if (old == *exp) return 1;
    +  *exp = old;
    +  return 0;
    +}
    +
    +typedef __declspec(align(16)) struct la_u128 {
    +  uint64_t lo, hi;
    +} la_u128;
    +
    +LA_INLINE int la_cas128(la_u128 *p, la_u128 *exp, la_u128 des)
    +{
    +  return (int)_InterlockedCompareExchange128((volatile __int64 *)p,
    +                                              (__int64)des.hi,
    +                                              (__int64)des.lo,
    +                                              (__int64 *)exp);
    +}
    +
    +LA_INLINE uint32_t la_add32_rlx(uint32_t *p, uint32_t v)
    +{ return (uint32_t)_InterlockedExchangeAdd((volatile long *)p, (long)v); }
    +LA_INLINE uint32_t la_add32_acqrel(uint32_t *p, uint32_t v)
    +{ return la_add32_rlx(p, v); }
    +LA_INLINE uint64_t la_add64_rlx(uint64_t *p, uint64_t v)
    +{ return (uint64_t)_InterlockedExchangeAdd64((volatile __int64 *)p, (__int64)v); }
    +LA_INLINE uint32_t la_sub32_rlx(uint32_t *p, uint32_t v)
    +{ return la_add32_rlx(p, 0u-v); }
    +LA_INLINE uint64_t la_sub64_rlx(uint64_t *p, uint64_t v)
    +{ return la_add64_rlx(p, 0u-v); }
    +LA_INLINE uint32_t la_sub32_acqrel(uint32_t *p, uint32_t v)
    +{ return la_sub32_rlx(p, v); }
    +LA_INLINE uint64_t la_sub64_acqrel(uint64_t *p, uint64_t v)
    +{ return la_sub64_rlx(p, v); }
    +LA_INLINE uint8_t la_or8_rlx(uint8_t *p, uint8_t v)
    +{ return (uint8_t)_InterlockedOr8((volatile char *)p, (char)v); }
    +LA_INLINE uint8_t la_and8_rlx(uint8_t *p, uint8_t v)
    +{ return (uint8_t)_InterlockedAnd8((volatile char *)p, (char)v); }
    +LA_INLINE uint8_t la_or8_acqrel(uint8_t *p, uint8_t v)
    +{ return la_or8_rlx(p, v); }
    +LA_INLINE uint8_t la_and8_acqrel(uint8_t *p, uint8_t v)
    +{ return la_and8_rlx(p, v); }
    +LA_INLINE uint64_t la_or64_rlx(uint64_t *p, uint64_t v)
    +{ return (uint64_t)_InterlockedOr64((volatile __int64 *)p, (__int64)v); }
    +LA_INLINE uint64_t la_and64_rlx(uint64_t *p, uint64_t v)
    +{ return (uint64_t)_InterlockedAnd64((volatile __int64 *)p, (__int64)v); }
    +LA_INLINE uint32_t la_xchg32_acqrel(uint32_t *p, uint32_t v)
    +{ return (uint32_t)_InterlockedExchange((volatile long *)p, (long)v); }
    +LA_INLINE uint64_t la_xchg64_acqrel(uint64_t *p, uint64_t v)
    +{ return (uint64_t)_InterlockedExchange64((volatile __int64 *)p, (__int64)v); }
    +LA_INLINE void *la_xchgptr_acqrel(void **p, void *v)
    +{ return _InterlockedExchangePointer((void *volatile *)p, v); }
    +#define la_xchgfunc_acqrel(p, v) \
    +  _InterlockedExchangePointer((void *volatile *)(p), (void *)(v))
    +
    +LA_INLINE int la_bit_test_and_set64(uint64_t *word, unsigned bit)
    +{
    +  uint64_t mask = (uint64_t)1 << (bit & 63);
    +  return (la_or64_rlx(word, mask) & mask) != 0;
    +}
    +
    +LA_INLINE void la_fence_acq(void) { MemoryBarrier(); }
    +LA_INLINE void la_fence_rel(void) { MemoryBarrier(); }
    +LA_INLINE void la_fence_seq(void) { MemoryBarrier(); }
    +LA_INLINE void la_cpu_pause(void) { YieldProcessor(); }
    +
    +#else
    +
     #if !defined(__GNUC__) && !defined(__clang__)
    -#error "lj_atomic.h requires GCC or Clang __atomic builtins"
    +#error "lj_atomic.h requires GCC, Clang, or MSVC atomics"
     #endif
     
     #define LA_INLINE static inline __attribute__((always_inline))
    @@ -145,6 +313,8 @@ LA_INLINE void la_cpu_pause(void)
     #endif
     }
     
    +#endif /* MSVC atomic backend. */
    +
     /* ---- futex + membarrier (Linux) ------------------------------------- */
     #if defined(__linux__)
     #define LA_HAS_FUTEX 1
    diff --git a/src/lj_gc2.c b/src/lj_gc2.c
    index fb44efa7e3..2bcd599f4e 100644
    --- a/src/lj_gc2.c
    +++ b/src/lj_gc2.c
    @@ -23248,7 +23248,7 @@ static uint32_t gc2_paranoia_scan_arena(global_State *g, GCArena *a)
         uint64_t b = la_load64_acq(&a->block[w]);
         uint64_t m = b & la_load64_acq(&a->mark[w]);
         while (m) {
    -      uint32_t bit = (uint32_t)__builtin_ctzll(m);
    +      uint32_t bit = lj_ffs64(m);
           uint32_t cell = (w << 6) + bit;
           uint32_t dtor_kind;
           m &= m - 1u;
    diff --git a/src/lj_gc2token.h b/src/lj_gc2token.h
    index 85e5de661d..cc9e5d0f96 100644
    --- a/src/lj_gc2token.h
    +++ b/src/lj_gc2token.h
    @@ -10,7 +10,7 @@
     
     #include "lj_atomic.h"
     
    -#if !defined(__x86_64__)
    +#if !defined(__x86_64__) && !defined(_M_X64) && !defined(_M_AMD64)
     #error "GC2 activation tokens currently require the x86-64 CX16 contract"
     #endif
     
    diff --git a/src/lj_markword.h b/src/lj_markword.h
    index fcf2976318..09d3221a4e 100644
    --- a/src/lj_markword.h
    +++ b/src/lj_markword.h
    @@ -11,7 +11,7 @@
     
     #include "lj_atomic.h"
     
    -#if !defined(__x86_64__)
    +#if !defined(__x86_64__) && !defined(_M_X64) && !defined(_M_AMD64)
     #error "GC2 epoch markwords currently require the x86-64 CX16 contract"
     #endif
     
    diff --git a/src/lj_tgslot.h b/src/lj_tgslot.h
    index 7404d700be..1ffbe11a2a 100644
    --- a/src/lj_tgslot.h
    +++ b/src/lj_tgslot.h
    @@ -13,7 +13,7 @@
     
     #include "lj_atomic.h"
     
    -#if !defined(__x86_64__)
    +#if !defined(__x86_64__) && !defined(_M_X64) && !defined(_M_AMD64)
     #error "TG-slot lifecycle tokens currently require the x86-64 CX16 contract"
     #endif
     
    diff --git a/src/lj_universe.h b/src/lj_universe.h
    index ea72f3b30b..1307b23e4d 100644
    --- a/src/lj_universe.h
    +++ b/src/lj_universe.h
    @@ -17,7 +17,7 @@
     
     #include "lj_atomic.h"
     
    -#if !defined(__x86_64__)
    +#if !defined(__x86_64__) && !defined(_M_X64) && !defined(_M_AMD64)
     #error "Universe admission tokens currently require the x86-64 CX16 contract"
     #endif
     
    
    From db500403529020cdd0983be4fe28c6d950f7046e Mon Sep 17 00:00:00 2001
    From: Charles Grunwald 
    Date: Mon, 14 Sep 2026 00:41:57 +0100
    Subject: [PATCH 34/34] Add one more MSVC fix
    
    ---
     src/lj_gc2token.h | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/src/lj_gc2token.h b/src/lj_gc2token.h
    index cc9e5d0f96..3528ca0e88 100644
    --- a/src/lj_gc2token.h
    +++ b/src/lj_gc2token.h
    @@ -971,7 +971,7 @@ typedef struct LJGC2RootRange {
       void *hi;
     } LJGC2RootRange;
     
    -typedef struct LJGC2RootDesc {
    +typedef struct  LJ_ALIGN(16) LJGC2RootDesc {
       uint64_t control;  /* generation << 2 | LJGC2RootDescState. */
       uint32_t flags;
       uint32_t reserved;
    @@ -988,7 +988,7 @@ typedef struct LJGC2RootDesc {
       ** coverage after owner-written payload to reduce control-word false sharing.
       */
       la_u128 coverage;
    -} __attribute__((aligned(16))) LJGC2RootDesc;
    +} LJGC2RootDesc;
     
     typedef struct LJGC2RootDescSpec {
       uint32_t flags;