aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pall <mike>2020-06-23 03:06:45 +0200
committerMike Pall <mike>2020-06-23 03:06:45 +0200
commitff34b48ddd6f2b3bdd26d6088662a214ba6b0288 (patch)
tree5585ab1933d148b046061a1e061686aa09e63789
parenta44f53acf53603e7d9b88352de035b1804be4e88 (diff)
downloadluajit-ff34b48ddd6f2b3bdd26d6088662a214ba6b0288.tar.gz
luajit-ff34b48ddd6f2b3bdd26d6088662a214ba6b0288.tar.bz2
luajit-ff34b48ddd6f2b3bdd26d6088662a214ba6b0288.zip
Redesign and harden string interning.
Up to 40% faster on hash-intensive benchmarks. With some ideas from Sokolov Yura.
Diffstat (limited to '')
-rw-r--r--src/Makefile1
-rw-r--r--src/lj.supp41
-rw-r--r--src/lj_arch.h17
-rw-r--r--src/lj_asm.c2
-rw-r--r--src/lj_asm_arm.h4
-rw-r--r--src/lj_asm_arm64.h4
-rw-r--r--src/lj_asm_mips.h2
-rw-r--r--src/lj_asm_ppc.h2
-rw-r--r--src/lj_asm_x86.h2
-rw-r--r--src/lj_gc.c38
-rw-r--r--src/lj_obj.h37
-rw-r--r--src/lj_state.c8
-rw-r--r--src/lj_str.c358
-rw-r--r--src/lj_str.h4
-rw-r--r--src/lj_tab.c4
-rw-r--r--src/vm_arm.dasc12
-rw-r--r--src/vm_arm64.dasc12
-rw-r--r--src/vm_mips.dasc12
-rw-r--r--src/vm_mips64.dasc12
-rw-r--r--src/vm_ppc.dasc12
-rw-r--r--src/vm_x64.dasc6
-rw-r--r--src/vm_x86.dasc6
22 files changed, 394 insertions, 202 deletions
diff --git a/src/Makefile b/src/Makefile
index 6a9de5db..178a5acd 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -132,7 +132,6 @@ XCFLAGS=
132# 132#
133# This define is required to run LuaJIT under Valgrind. The Valgrind 133# This define is required to run LuaJIT under Valgrind. The Valgrind
134# header files must be installed. You should enable debug information, too. 134# header files must be installed. You should enable debug information, too.
135# Use --suppressions=lj.supp to avoid some false positives.
136#XCFLAGS+= -DLUAJIT_USE_VALGRIND 135#XCFLAGS+= -DLUAJIT_USE_VALGRIND
137# 136#
138# This is the client for the GDB JIT API. GDB 7.0 or higher is required 137# This is the client for the GDB JIT API. GDB 7.0 or higher is required
diff --git a/src/lj.supp b/src/lj.supp
deleted file mode 100644
index 217f7c89..00000000
--- a/src/lj.supp
+++ /dev/null
@@ -1,41 +0,0 @@
1# Valgrind suppression file for LuaJIT 2.0.
2{
3 Optimized string compare
4 Memcheck:Addr4
5 fun:lj_str_cmp
6}
7{
8 Optimized string compare
9 Memcheck:Addr1
10 fun:lj_str_cmp
11}
12{
13 Optimized string compare
14 Memcheck:Addr4
15 fun:lj_str_new
16}
17{
18 Optimized string compare
19 Memcheck:Addr1
20 fun:lj_str_new
21}
22{
23 Optimized string compare
24 Memcheck:Cond
25 fun:lj_str_new
26}
27{
28 Optimized string compare
29 Memcheck:Addr4
30 fun:str_fastcmp
31}
32{
33 Optimized string compare
34 Memcheck:Addr1
35 fun:str_fastcmp
36}
37{
38 Optimized string compare
39 Memcheck:Cond
40 fun:str_fastcmp
41}
diff --git a/src/lj_arch.h b/src/lj_arch.h
index 626f6c13..f148b3f7 100644
--- a/src/lj_arch.h
+++ b/src/lj_arch.h
@@ -637,14 +637,31 @@ extern void *LJ_WIN_LOADLIBA(const char *path);
637 637
638/* Don't make any changes here. Instead build with: 638/* Don't make any changes here. Instead build with:
639** make "XCFLAGS=-DLUAJIT_SECURITY_flag=value" 639** make "XCFLAGS=-DLUAJIT_SECURITY_flag=value"
640**
641** Important note to distro maintainers: DO NOT change the defaults for a
642** regular distro build -- neither upwards, nor downwards!
643** These build-time configurable security flags are intended for embedders
644** who may have specific needs wrt. security vs. performance.
640*/ 645*/
641 646
642/* Security defaults. */ 647/* Security defaults. */
643#ifndef LUAJIT_SECURITY_PRNG 648#ifndef LUAJIT_SECURITY_PRNG
649/* PRNG init: 0 = fixed/insecure, 1 = secure from OS. */
644#define LUAJIT_SECURITY_PRNG 1 650#define LUAJIT_SECURITY_PRNG 1
645#endif 651#endif
646 652
653#ifndef LUAJIT_SECURITY_STRHASH
654/* String hash: 0 = sparse only, 1 = sparse + dense. */
655#define LUAJIT_SECURITY_STRHASH 1
656#endif
657
658#ifndef LUAJIT_SECURITY_STRID
659/* String IDs: 0 = linear, 1 = reseed < 255, 2 = reseed < 15, 3 = random. */
660#define LUAJIT_SECURITY_STRID 1
661#endif
662
647#ifndef LUAJIT_SECURITY_MCODE 663#ifndef LUAJIT_SECURITY_MCODE
664/* Machine code page protection: 0 = insecure RWX, 1 = secure RW^X. */
648#define LUAJIT_SECURITY_MCODE 1 665#define LUAJIT_SECURITY_MCODE 1
649#endif 666#endif
650 667
diff --git a/src/lj_asm.c b/src/lj_asm.c
index 2659c8a2..cc7841c0 100644
--- a/src/lj_asm.c
+++ b/src/lj_asm.c
@@ -1029,7 +1029,7 @@ static uint32_t ir_khash(ASMState *as, IRIns *ir)
1029 uint32_t lo, hi; 1029 uint32_t lo, hi;
1030 UNUSED(as); 1030 UNUSED(as);
1031 if (irt_isstr(ir->t)) { 1031 if (irt_isstr(ir->t)) {
1032 return ir_kstr(ir)->hash; 1032 return ir_kstr(ir)->sid;
1033 } else if (irt_isnum(ir->t)) { 1033 } else if (irt_isnum(ir->t)) {
1034 lo = ir_knum(ir)->u32.lo; 1034 lo = ir_knum(ir)->u32.lo;
1035 hi = ir_knum(ir)->u32.hi << 1; 1035 hi = ir_knum(ir)->u32.hi << 1;
diff --git a/src/lj_asm_arm.h b/src/lj_asm_arm.h
index d2fad141..e7d2bf17 100644
--- a/src/lj_asm_arm.h
+++ b/src/lj_asm_arm.h
@@ -825,10 +825,10 @@ static void asm_href(ASMState *as, IRIns *ir, IROp merge)
825 } else { 825 } else {
826 emit_dnm(as, ARMI_ADD|ARMF_SH(ARMSH_LSL, 3), dest, dest, tmp); 826 emit_dnm(as, ARMI_ADD|ARMF_SH(ARMSH_LSL, 3), dest, dest, tmp);
827 emit_dnm(as, ARMI_ADD|ARMF_SH(ARMSH_LSL, 1), tmp, tmp, tmp); 827 emit_dnm(as, ARMI_ADD|ARMF_SH(ARMSH_LSL, 1), tmp, tmp, tmp);
828 if (irt_isstr(kt)) { /* Fetch of str->hash is cheaper than ra_allock. */ 828 if (irt_isstr(kt)) { /* Fetch of str->sid is cheaper than ra_allock. */
829 emit_dnm(as, ARMI_AND, tmp, tmp+1, RID_TMP); 829 emit_dnm(as, ARMI_AND, tmp, tmp+1, RID_TMP);
830 emit_lso(as, ARMI_LDR, dest, tab, (int32_t)offsetof(GCtab, node)); 830 emit_lso(as, ARMI_LDR, dest, tab, (int32_t)offsetof(GCtab, node));
831 emit_lso(as, ARMI_LDR, tmp+1, key, (int32_t)offsetof(GCstr, hash)); 831 emit_lso(as, ARMI_LDR, tmp+1, key, (int32_t)offsetof(GCstr, sid));
832 emit_lso(as, ARMI_LDR, RID_TMP, tab, (int32_t)offsetof(GCtab, hmask)); 832 emit_lso(as, ARMI_LDR, RID_TMP, tab, (int32_t)offsetof(GCtab, hmask));
833 } else if (irref_isk(refkey)) { 833 } else if (irref_isk(refkey)) {
834 emit_opk(as, ARMI_AND, tmp, RID_TMP, (int32_t)khash, 834 emit_opk(as, ARMI_AND, tmp, RID_TMP, (int32_t)khash,
diff --git a/src/lj_asm_arm64.h b/src/lj_asm_arm64.h
index 0729a3a5..b1fd3acc 100644
--- a/src/lj_asm_arm64.h
+++ b/src/lj_asm_arm64.h
@@ -847,9 +847,9 @@ static void asm_href(ASMState *as, IRIns *ir, IROp merge)
847 emit_dnm(as, A64I_ANDw, dest, dest, tmphash); 847 emit_dnm(as, A64I_ANDw, dest, dest, tmphash);
848 emit_lso(as, A64I_LDRw, dest, tab, offsetof(GCtab, hmask)); 848 emit_lso(as, A64I_LDRw, dest, tab, offsetof(GCtab, hmask));
849 } else if (irt_isstr(kt)) { 849 } else if (irt_isstr(kt)) {
850 /* Fetch of str->hash is cheaper than ra_allock. */ 850 /* Fetch of str->sid is cheaper than ra_allock. */
851 emit_dnm(as, A64I_ANDw, dest, dest, tmp); 851 emit_dnm(as, A64I_ANDw, dest, dest, tmp);
852 emit_lso(as, A64I_LDRw, tmp, key, offsetof(GCstr, hash)); 852 emit_lso(as, A64I_LDRw, tmp, key, offsetof(GCstr, sid));
853 emit_lso(as, A64I_LDRw, dest, tab, offsetof(GCtab, hmask)); 853 emit_lso(as, A64I_LDRw, dest, tab, offsetof(GCtab, hmask));
854 } else { /* Must match with hash*() in lj_tab.c. */ 854 } else { /* Must match with hash*() in lj_tab.c. */
855 emit_dnm(as, A64I_ANDw, dest, dest, tmp); 855 emit_dnm(as, A64I_ANDw, dest, dest, tmp);
diff --git a/src/lj_asm_mips.h b/src/lj_asm_mips.h
index a2b8d8e0..513bd5ca 100644
--- a/src/lj_asm_mips.h
+++ b/src/lj_asm_mips.h
@@ -1041,7 +1041,7 @@ static void asm_href(ASMState *as, IRIns *ir, IROp merge)
1041 if (isk) { 1041 if (isk) {
1042 /* Nothing to do. */ 1042 /* Nothing to do. */
1043 } else if (irt_isstr(kt)) { 1043 } else if (irt_isstr(kt)) {
1044 emit_tsi(as, MIPSI_LW, tmp1, key, (int32_t)offsetof(GCstr, hash)); 1044 emit_tsi(as, MIPSI_LW, tmp1, key, (int32_t)offsetof(GCstr, sid));
1045 } else { /* Must match with hash*() in lj_tab.c. */ 1045 } else { /* Must match with hash*() in lj_tab.c. */
1046 emit_dst(as, MIPSI_SUBU, tmp1, tmp1, tmp2); 1046 emit_dst(as, MIPSI_SUBU, tmp1, tmp1, tmp2);
1047 emit_rotr(as, tmp2, tmp2, dest, (-HASH_ROT3)&31); 1047 emit_rotr(as, tmp2, tmp2, dest, (-HASH_ROT3)&31);
diff --git a/src/lj_asm_ppc.h b/src/lj_asm_ppc.h
index 498fdac3..77ab09d6 100644
--- a/src/lj_asm_ppc.h
+++ b/src/lj_asm_ppc.h
@@ -721,7 +721,7 @@ static void asm_href(ASMState *as, IRIns *ir, IROp merge)
721 if (isk) { 721 if (isk) {
722 /* Nothing to do. */ 722 /* Nothing to do. */
723 } else if (irt_isstr(kt)) { 723 } else if (irt_isstr(kt)) {
724 emit_tai(as, PPCI_LWZ, tmp1, key, (int32_t)offsetof(GCstr, hash)); 724 emit_tai(as, PPCI_LWZ, tmp1, key, (int32_t)offsetof(GCstr, sid));
725 } else { /* Must match with hash*() in lj_tab.c. */ 725 } else { /* Must match with hash*() in lj_tab.c. */
726 emit_tab(as, PPCI_SUBF, tmp1, tmp2, tmp1); 726 emit_tab(as, PPCI_SUBF, tmp1, tmp2, tmp1);
727 emit_rotlwi(as, tmp2, tmp2, HASH_ROT3); 727 emit_rotlwi(as, tmp2, tmp2, HASH_ROT3);
diff --git a/src/lj_asm_x86.h b/src/lj_asm_x86.h
index a3adee14..e40b5e54 100644
--- a/src/lj_asm_x86.h
+++ b/src/lj_asm_x86.h
@@ -1228,7 +1228,7 @@ static void asm_href(ASMState *as, IRIns *ir, IROp merge)
1228 emit_gri(as, XG_ARITHi(XOg_AND), dest, (int32_t)khash); 1228 emit_gri(as, XG_ARITHi(XOg_AND), dest, (int32_t)khash);
1229 emit_rmro(as, XO_MOV, dest, tab, offsetof(GCtab, hmask)); 1229 emit_rmro(as, XO_MOV, dest, tab, offsetof(GCtab, hmask));
1230 } else if (irt_isstr(kt)) { 1230 } else if (irt_isstr(kt)) {
1231 emit_rmro(as, XO_ARITH(XOg_AND), dest, key, offsetof(GCstr, hash)); 1231 emit_rmro(as, XO_ARITH(XOg_AND), dest, key, offsetof(GCstr, sid));
1232 emit_rmro(as, XO_MOV, dest, tab, offsetof(GCtab, hmask)); 1232 emit_rmro(as, XO_MOV, dest, tab, offsetof(GCtab, hmask));
1233 } else { /* Must match with hashrot() in lj_tab.c. */ 1233 } else { /* Must match with hashrot() in lj_tab.c. */
1234 emit_rmro(as, XO_ARITH(XOg_AND), dest, tab, offsetof(GCtab, hmask)); 1234 emit_rmro(as, XO_ARITH(XOg_AND), dest, tab, offsetof(GCtab, hmask));
diff --git a/src/lj_gc.c b/src/lj_gc.c
index 671b5983..cc4232a6 100644
--- a/src/lj_gc.c
+++ b/src/lj_gc.c
@@ -417,6 +417,32 @@ static GCRef *gc_sweep(global_State *g, GCRef *p, uint32_t lim)
417 return p; 417 return p;
418} 418}
419 419
420/* Sweep one string interning table chain. Preserves hashalg bit. */
421static void gc_sweepstr(global_State *g, GCRef *chain)
422{
423 /* Mask with other white and LJ_GC_FIXED. Or LJ_GC_SFIXED on shutdown. */
424 int ow = otherwhite(g);
425 uintptr_t u = gcrefu(*chain);
426 GCRef q;
427 GCRef *p = &q;
428 GCobj *o;
429 setgcrefp(q, (u & ~(uintptr_t)1));
430 while ((o = gcref(*p)) != NULL) {
431 if (((o->gch.marked ^ LJ_GC_WHITES) & ow)) { /* Black or current white? */
432 lj_assertG(!isdead(g, o) || (o->gch.marked & LJ_GC_FIXED),
433 "sweep of undead string");
434 makewhite(g, o); /* String is alive, change to the current white. */
435 p = &o->gch.nextgc;
436 } else { /* Otherwise string is dead, free it. */
437 lj_assertG(isdead(g, o) || ow == LJ_GC_SFIXED,
438 "sweep of unlive string");
439 setgcrefr(*p, o->gch.nextgc);
440 lj_str_free(g, gco2str(o));
441 }
442 }
443 setgcrefp(*chain, (gcrefu(q) | (u & 1)));
444}
445
420/* Check whether we can clear a key or a value slot from a table. */ 446/* Check whether we can clear a key or a value slot from a table. */
421static int gc_mayclear(cTValue *o, int val) 447static int gc_mayclear(cTValue *o, int val)
422{ 448{
@@ -571,9 +597,9 @@ void lj_gc_freeall(global_State *g)
571 /* Free everything, except super-fixed objects (the main thread). */ 597 /* Free everything, except super-fixed objects (the main thread). */
572 g->gc.currentwhite = LJ_GC_WHITES | LJ_GC_SFIXED; 598 g->gc.currentwhite = LJ_GC_WHITES | LJ_GC_SFIXED;
573 gc_fullsweep(g, &g->gc.root); 599 gc_fullsweep(g, &g->gc.root);
574 strmask = g->strmask; 600 strmask = g->str.mask;
575 for (i = 0; i <= strmask; i++) /* Free all string hash chains. */ 601 for (i = 0; i <= strmask; i++) /* Free all string hash chains. */
576 gc_fullsweep(g, &g->strhash[i]); 602 gc_sweepstr(g, &g->str.tab[i]);
577} 603}
578 604
579/* -- Collector ----------------------------------------------------------- */ 605/* -- Collector ----------------------------------------------------------- */
@@ -636,8 +662,8 @@ static size_t gc_onestep(lua_State *L)
636 return 0; 662 return 0;
637 case GCSsweepstring: { 663 case GCSsweepstring: {
638 GCSize old = g->gc.total; 664 GCSize old = g->gc.total;
639 gc_fullsweep(g, &g->strhash[g->gc.sweepstr++]); /* Sweep one chain. */ 665 gc_sweepstr(g, &g->str.tab[g->gc.sweepstr++]); /* Sweep one chain. */
640 if (g->gc.sweepstr > g->strmask) 666 if (g->gc.sweepstr > g->str.mask)
641 g->gc.state = GCSsweep; /* All string hash chains sweeped. */ 667 g->gc.state = GCSsweep; /* All string hash chains sweeped. */
642 lj_assertG(old >= g->gc.total, "sweep increased memory"); 668 lj_assertG(old >= g->gc.total, "sweep increased memory");
643 g->gc.estimate -= old - g->gc.total; 669 g->gc.estimate -= old - g->gc.total;
@@ -649,8 +675,8 @@ static size_t gc_onestep(lua_State *L)
649 lj_assertG(old >= g->gc.total, "sweep increased memory"); 675 lj_assertG(old >= g->gc.total, "sweep increased memory");
650 g->gc.estimate -= old - g->gc.total; 676 g->gc.estimate -= old - g->gc.total;
651 if (gcref(*mref(g->gc.sweep, GCRef)) == NULL) { 677 if (gcref(*mref(g->gc.sweep, GCRef)) == NULL) {
652 if (g->strnum <= (g->strmask >> 2) && g->strmask > LJ_MIN_STRTAB*2-1) 678 if (g->str.num <= (g->str.mask >> 2) && g->str.mask > LJ_MIN_STRTAB*2-1)
653 lj_str_resize(L, g->strmask >> 1); /* Shrink string table. */ 679 lj_str_resize(L, g->str.mask >> 1); /* Shrink string table. */
654 if (gcref(g->gc.mmudata)) { /* Need any finalizations? */ 680 if (gcref(g->gc.mmudata)) { /* Need any finalizations? */
655 g->gc.state = GCSfinalize; 681 g->gc.state = GCSfinalize;
656#if LJ_HASFFI 682#if LJ_HASFFI
diff --git a/src/lj_obj.h b/src/lj_obj.h
index 6c974812..9d4bec08 100644
--- a/src/lj_obj.h
+++ b/src/lj_obj.h
@@ -13,7 +13,7 @@
13#include "lj_def.h" 13#include "lj_def.h"
14#include "lj_arch.h" 14#include "lj_arch.h"
15 15
16/* -- Memory references (32 bit address space) ---------------------------- */ 16/* -- Memory references --------------------------------------------------- */
17 17
18/* Memory and GC object sizes. */ 18/* Memory and GC object sizes. */
19typedef uint32_t MSize; 19typedef uint32_t MSize;
@@ -44,7 +44,7 @@ typedef struct MRef {
44#define setmrefr(r, v) ((r).ptr32 = (v).ptr32) 44#define setmrefr(r, v) ((r).ptr32 = (v).ptr32)
45#endif 45#endif
46 46
47/* -- GC object references (32 bit address space) ------------------------- */ 47/* -- GC object references ------------------------------------------------ */
48 48
49/* GCobj reference */ 49/* GCobj reference */
50typedef struct GCRef { 50typedef struct GCRef {
@@ -287,12 +287,16 @@ typedef const TValue cTValue;
287 287
288/* -- String object ------------------------------------------------------- */ 288/* -- String object ------------------------------------------------------- */
289 289
290typedef uint32_t StrHash; /* String hash value. */
291typedef uint32_t StrID; /* String ID. */
292
290/* String object header. String payload follows. */ 293/* String object header. String payload follows. */
291typedef struct GCstr { 294typedef struct GCstr {
292 GCHeader; 295 GCHeader;
293 uint8_t reserved; /* Used by lexer for fast lookup of reserved words. */ 296 uint8_t reserved; /* Used by lexer for fast lookup of reserved words. */
294 uint8_t unused; 297 uint8_t hashalg; /* Hash algorithm. */
295 MSize hash; /* Hash of string. */ 298 StrID sid; /* Interned string ID. */
299 StrHash hash; /* Hash of string. */
296 MSize len; /* Size of string. */ 300 MSize len; /* Size of string. */
297} GCstr; 301} GCstr;
298 302
@@ -300,7 +304,6 @@ typedef struct GCstr {
300#define strdata(s) ((const char *)((s)+1)) 304#define strdata(s) ((const char *)((s)+1))
301#define strdatawr(s) ((char *)((s)+1)) 305#define strdatawr(s) ((char *)((s)+1))
302#define strVdata(o) strdata(strV(o)) 306#define strVdata(o) strdata(strV(o))
303#define sizestring(s) (sizeof(struct GCstr)+(s)->len+1)
304 307
305/* -- Userdata object ----------------------------------------------------- */ 308/* -- Userdata object ----------------------------------------------------- */
306 309
@@ -570,6 +573,7 @@ typedef enum {
570#define basemt_obj(g, o) ((g)->gcroot[GCROOT_BASEMT+itypemap(o)]) 573#define basemt_obj(g, o) ((g)->gcroot[GCROOT_BASEMT+itypemap(o)])
571#define mmname_str(g, mm) (strref((g)->gcroot[GCROOT_MMNAME+(mm)])) 574#define mmname_str(g, mm) (strref((g)->gcroot[GCROOT_MMNAME+(mm)]))
572 575
576/* Garbage collector state. */
573typedef struct GCState { 577typedef struct GCState {
574 GCSize total; /* Memory currently allocated. */ 578 GCSize total; /* Memory currently allocated. */
575 GCSize threshold; /* Memory threshold. */ 579 GCSize threshold; /* Memory threshold. */
@@ -590,25 +594,36 @@ typedef struct GCState {
590 MSize pause; /* Pause between successive GC cycles. */ 594 MSize pause; /* Pause between successive GC cycles. */
591} GCState; 595} GCState;
592 596
597/* String interning state. */
598typedef struct StrInternState {
599 GCRef *tab; /* String hash table anchors. */
600 MSize mask; /* String hash mask (size of hash table - 1). */
601 MSize num; /* Number of strings in hash table. */
602 StrID id; /* Next string ID. */
603 uint8_t idreseed; /* String ID reseed counter. */
604 uint8_t second; /* String interning table uses secondary hashing. */
605 uint8_t unused1;
606 uint8_t unused2;
607 LJ_ALIGN(8) uint64_t seed; /* Random string seed. */
608} StrInternState;
609
593/* Global state, shared by all threads of a Lua universe. */ 610/* Global state, shared by all threads of a Lua universe. */
594typedef struct global_State { 611typedef struct global_State {
595 GCRef *strhash; /* String hash table (hash chain anchors). */
596 MSize strmask; /* String hash mask (size of hash table - 1). */
597 MSize strnum; /* Number of strings in hash table. */
598 lua_Alloc allocf; /* Memory allocator. */ 612 lua_Alloc allocf; /* Memory allocator. */
599 void *allocd; /* Memory allocator data. */ 613 void *allocd; /* Memory allocator data. */
600 GCState gc; /* Garbage collector. */ 614 GCState gc; /* Garbage collector. */
601 volatile int32_t vmstate; /* VM state or current JIT code trace number. */
602 SBuf tmpbuf; /* Temporary string buffer. */
603 GCstr strempty; /* Empty string. */ 615 GCstr strempty; /* Empty string. */
604 uint8_t stremptyz; /* Zero terminator of empty string. */ 616 uint8_t stremptyz; /* Zero terminator of empty string. */
605 uint8_t hookmask; /* Hook mask. */ 617 uint8_t hookmask; /* Hook mask. */
606 uint8_t dispatchmode; /* Dispatch mode. */ 618 uint8_t dispatchmode; /* Dispatch mode. */
607 uint8_t vmevmask; /* VM event mask. */ 619 uint8_t vmevmask; /* VM event mask. */
620 StrInternState str; /* String interning. */
621 volatile int32_t vmstate; /* VM state or current JIT code trace number. */
608 GCRef mainthref; /* Link to main thread. */ 622 GCRef mainthref; /* Link to main thread. */
609 TValue registrytv; /* Anchor for registry. */ 623 SBuf tmpbuf; /* Temporary string buffer. */
610 TValue tmptv, tmptv2; /* Temporary TValues. */ 624 TValue tmptv, tmptv2; /* Temporary TValues. */
611 Node nilnode; /* Fallback 1-element hash part (nil key and value). */ 625 Node nilnode; /* Fallback 1-element hash part (nil key and value). */
626 TValue registrytv; /* Anchor for registry. */
612 GCupval uvhead; /* Head of double-linked list of all open upvalues. */ 627 GCupval uvhead; /* Head of double-linked list of all open upvalues. */
613 int32_t hookcount; /* Instruction hook countdown. */ 628 int32_t hookcount; /* Instruction hook countdown. */
614 int32_t hookcstart; /* Start count for instruction hook counter. */ 629 int32_t hookcstart; /* Start count for instruction hook counter. */
diff --git a/src/lj_state.c b/src/lj_state.c
index a4d072be..4f77e71f 100644
--- a/src/lj_state.c
+++ b/src/lj_state.c
@@ -150,7 +150,7 @@ static TValue *cpluaopen(lua_State *L, lua_CFunction dummy, void *ud)
150 /* NOBARRIER: State initialization, all objects are white. */ 150 /* NOBARRIER: State initialization, all objects are white. */
151 setgcref(L->env, obj2gco(lj_tab_new(L, 0, LJ_MIN_GLOBAL))); 151 setgcref(L->env, obj2gco(lj_tab_new(L, 0, LJ_MIN_GLOBAL)));
152 settabV(L, registry(L), lj_tab_new(L, 0, LJ_MIN_REGISTRY)); 152 settabV(L, registry(L), lj_tab_new(L, 0, LJ_MIN_REGISTRY));
153 lj_str_resize(L, LJ_MIN_STRTAB-1); 153 lj_str_init(L);
154 lj_meta_init(L); 154 lj_meta_init(L);
155 lj_lex_init(L); 155 lj_lex_init(L);
156 fixstring(lj_err_str(L, LJ_ERR_ERRMEM)); /* Preallocate memory error msg. */ 156 fixstring(lj_err_str(L, LJ_ERR_ERRMEM)); /* Preallocate memory error msg. */
@@ -166,12 +166,12 @@ static void close_state(lua_State *L)
166 lj_gc_freeall(g); 166 lj_gc_freeall(g);
167 lj_assertG(gcref(g->gc.root) == obj2gco(L), 167 lj_assertG(gcref(g->gc.root) == obj2gco(L),
168 "main thread is not first GC object"); 168 "main thread is not first GC object");
169 lj_assertG(g->strnum == 0, "leaked %d strings", g->strnum); 169 lj_assertG(g->str.num == 0, "leaked %d strings", g->str.num);
170 lj_trace_freestate(g); 170 lj_trace_freestate(g);
171#if LJ_HASFFI 171#if LJ_HASFFI
172 lj_ctype_freestate(g); 172 lj_ctype_freestate(g);
173#endif 173#endif
174 lj_mem_freevec(g, g->strhash, g->strmask+1, GCRef); 174 lj_str_freetab(g);
175 lj_buf_free(g, &g->tmpbuf); 175 lj_buf_free(g, &g->tmpbuf);
176 lj_mem_freevec(g, tvref(L->stack), L->stacksize, TValue); 176 lj_mem_freevec(g, tvref(L->stack), L->stacksize, TValue);
177 lj_assertG(g->gc.total == sizeof(GG_State), 177 lj_assertG(g->gc.total == sizeof(GG_State),
@@ -231,7 +231,7 @@ LUA_API lua_State *lua_newstate(lua_Alloc allocf, void *allocd)
231 setgcref(g->mainthref, obj2gco(L)); 231 setgcref(g->mainthref, obj2gco(L));
232 setgcref(g->uvhead.prev, obj2gco(&g->uvhead)); 232 setgcref(g->uvhead.prev, obj2gco(&g->uvhead));
233 setgcref(g->uvhead.next, obj2gco(&g->uvhead)); 233 setgcref(g->uvhead.next, obj2gco(&g->uvhead));
234 g->strmask = ~(MSize)0; 234 g->str.mask = ~(MSize)0;
235 setnilV(registry(L)); 235 setnilV(registry(L));
236 setnilV(&g->nilnode.val); 236 setnilV(&g->nilnode.val);
237 setnilV(&g->nilnode.key); 237 setnilV(&g->nilnode.key);
diff --git a/src/lj_str.c b/src/lj_str.c
index 0253c15e..5bf8426c 100644
--- a/src/lj_str.c
+++ b/src/lj_str.c
@@ -11,6 +11,7 @@
11#include "lj_err.h" 11#include "lj_err.h"
12#include "lj_str.h" 12#include "lj_str.h"
13#include "lj_char.h" 13#include "lj_char.h"
14#include "lj_prng.h"
14 15
15/* -- String helpers ------------------------------------------------------ */ 16/* -- String helpers ------------------------------------------------------ */
16 17
@@ -37,28 +38,6 @@ int32_t LJ_FASTCALL lj_str_cmp(GCstr *a, GCstr *b)
37 return (int32_t)(a->len - b->len); 38 return (int32_t)(a->len - b->len);
38} 39}
39 40
40/* Fast string data comparison. Caveat: unaligned access to 1st string! */
41static LJ_AINLINE int str_fastcmp(const char *a, const char *b, MSize len)
42{
43 MSize i = 0;
44 lj_assertX(len > 0, "fast string compare with zero length");
45 lj_assertX((((uintptr_t)a+len-1) & (LJ_PAGESIZE-1)) <= LJ_PAGESIZE-4,
46 "fast string compare crossing page boundary");
47 do { /* Note: innocuous access up to end of string + 3. */
48 uint32_t v = lj_getu32(a+i) ^ *(const uint32_t *)(b+i);
49 if (v) {
50 i -= len;
51#if LJ_LE
52 return (int32_t)i >= -3 ? (v << (32+(i<<3))) : 1;
53#else
54 return (int32_t)i >= -3 ? (v >> (32+(i<<3))) : 1;
55#endif
56 }
57 i += 4;
58 } while (i < len);
59 return 0;
60}
61
62/* Find fixed string p inside string s. */ 41/* Find fixed string p inside string s. */
63const char *lj_str_find(const char *s, const char *p, MSize slen, MSize plen) 42const char *lj_str_find(const char *s, const char *p, MSize slen, MSize plen)
64{ 43{
@@ -91,108 +70,301 @@ int lj_str_haspattern(GCstr *s)
91 return 0; /* No pattern matching chars found. */ 70 return 0; /* No pattern matching chars found. */
92} 71}
93 72
94/* -- String interning ---------------------------------------------------- */ 73/* -- String hashing ------------------------------------------------------ */
95
96/* Resize the string hash table (grow and shrink). */
97void lj_str_resize(lua_State *L, MSize newmask)
98{
99 global_State *g = G(L);
100 GCRef *newhash;
101 MSize i;
102 if (g->gc.state == GCSsweepstring || newmask >= LJ_MAX_STRTAB-1)
103 return; /* No resizing during GC traversal or if already too big. */
104 newhash = lj_mem_newvec(L, newmask+1, GCRef);
105 memset(newhash, 0, (newmask+1)*sizeof(GCRef));
106 for (i = g->strmask; i != ~(MSize)0; i--) { /* Rehash old table. */
107 GCobj *p = gcref(g->strhash[i]);
108 while (p) { /* Follow each hash chain and reinsert all strings. */
109 MSize h = gco2str(p)->hash & newmask;
110 GCobj *next = gcnext(p);
111 /* NOBARRIER: The string table is a GC root. */
112 setgcrefr(p->gch.nextgc, newhash[h]);
113 setgcref(newhash[h], p);
114 p = next;
115 }
116 }
117 lj_mem_freevec(g, g->strhash, g->strmask+1, GCRef);
118 g->strmask = newmask;
119 g->strhash = newhash;
120}
121 74
122/* Intern a string and return string object. */ 75/* Keyed sparse ARX string hash. Constant time. */
123GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx) 76static StrHash hash_sparse(uint64_t seed, const char *str, MSize len)
124{ 77{
125 global_State *g; 78 /* Constants taken from lookup3 hash by Bob Jenkins. */
126 GCstr *s; 79 StrHash a, b, h = len ^ (StrHash)seed;
127 GCobj *o;
128 MSize len = (MSize)lenx;
129 MSize a, b, h = len;
130 if (lenx >= LJ_MAX_STR)
131 lj_err_msg(L, LJ_ERR_STROV);
132 g = G(L);
133 /* Compute string hash. Constants taken from lookup3 hash by Bob Jenkins. */
134 if (len >= 4) { /* Caveat: unaligned access! */ 80 if (len >= 4) { /* Caveat: unaligned access! */
135 a = lj_getu32(str); 81 a = lj_getu32(str);
136 h ^= lj_getu32(str+len-4); 82 h ^= lj_getu32(str+len-4);
137 b = lj_getu32(str+(len>>1)-2); 83 b = lj_getu32(str+(len>>1)-2);
138 h ^= b; h -= lj_rol(b, 14); 84 h ^= b; h -= lj_rol(b, 14);
139 b += lj_getu32(str+(len>>2)-1); 85 b += lj_getu32(str+(len>>2)-1);
140 } else if (len > 0) { 86 } else {
141 a = *(const uint8_t *)str; 87 a = *(const uint8_t *)str;
142 h ^= *(const uint8_t *)(str+len-1); 88 h ^= *(const uint8_t *)(str+len-1);
143 b = *(const uint8_t *)(str+(len>>1)); 89 b = *(const uint8_t *)(str+(len>>1));
144 h ^= b; h -= lj_rol(b, 14); 90 h ^= b; h -= lj_rol(b, 14);
145 } else {
146 return &g->strempty;
147 } 91 }
148 a ^= h; a -= lj_rol(h, 11); 92 a ^= h; a -= lj_rol(h, 11);
149 b ^= a; b -= lj_rol(a, 25); 93 b ^= a; b -= lj_rol(a, 25);
150 h ^= b; h -= lj_rol(b, 16); 94 h ^= b; h -= lj_rol(b, 16);
151 /* Check if the string has already been interned. */ 95 return h;
152 o = gcref(g->strhash[h & g->strmask]); 96}
153 if (LJ_LIKELY((((uintptr_t)str+len-1) & (LJ_PAGESIZE-1)) <= LJ_PAGESIZE-4)) { 97
154 while (o != NULL) { 98#if LUAJIT_SECURITY_STRHASH
155 GCstr *sx = gco2str(o); 99/* Keyed dense ARX string hash. Linear time. */
156 if (sx->len == len && str_fastcmp(str, strdata(sx), len) == 0) { 100static LJ_NOINLINE StrHash hash_dense(uint64_t seed, StrHash h,
157 /* Resurrect if dead. Can only happen with fixstring() (keywords). */ 101 const char *str, MSize len)
158 if (isdead(g, o)) flipwhite(o); 102{
159 return sx; /* Return existing string. */ 103 StrHash b = lj_bswap(lj_rol(h ^ (StrHash)(seed >> 32), 4));
104 if (len > 12) {
105 StrHash a = (StrHash)seed;
106 const char *pe = str+len-12, *p = pe, *q = str;
107 do {
108 a += lj_getu32(p);
109 b += lj_getu32(p+4);
110 h += lj_getu32(p+8);
111 p = q; q += 12;
112 h ^= b; h -= lj_rol(b, 14);
113 a ^= h; a -= lj_rol(h, 11);
114 b ^= a; b -= lj_rol(a, 25);
115 } while (p < pe);
116 h ^= b; h -= lj_rol(b, 16);
117 a ^= h; a -= lj_rol(h, 4);
118 b ^= a; b -= lj_rol(a, 14);
119 }
120 return b;
121}
122#endif
123
124/* -- String interning ---------------------------------------------------- */
125
126#define LJ_STR_MAXCOLL 32
127
128/* Resize the string interning hash table (grow and shrink). */
129void lj_str_resize(lua_State *L, MSize newmask)
130{
131 global_State *g = G(L);
132 GCRef *newtab, *oldtab = g->str.tab;
133 MSize i;
134
135 /* No resizing during GC traversal or if already too big. */
136 if (g->gc.state == GCSsweepstring || newmask >= LJ_MAX_STRTAB-1)
137 return;
138
139 newtab = lj_mem_newvec(L, newmask+1, GCRef);
140 memset(newtab, 0, (newmask+1)*sizeof(GCRef));
141
142#if LUAJIT_SECURITY_STRHASH
143 /* Check which chains need secondary hashes. */
144 if (g->str.second) {
145 int newsecond = 0;
146 /* Compute primary chain lengths. */
147 for (i = g->str.mask; i != ~(MSize)0; i--) {
148 GCobj *o = (GCobj *)(gcrefu(oldtab[i]) & ~(uintptr_t)1);
149 while (o) {
150 GCstr *s = gco2str(o);
151 MSize hash = s->hashalg ? hash_sparse(g->str.seed, strdata(s), s->len) :
152 s->hash;
153 hash &= newmask;
154 setgcrefp(newtab[hash], gcrefu(newtab[hash]) + 1);
155 o = gcnext(o);
160 } 156 }
161 o = gcnext(o);
162 } 157 }
163 } else { /* Slow path: end of string is too close to a page boundary. */ 158 /* Mark secondary chains. */
164 while (o != NULL) { 159 for (i = newmask; i != ~(MSize)0; i--) {
165 GCstr *sx = gco2str(o); 160 int secondary = gcrefu(newtab[i]) > LJ_STR_MAXCOLL;
166 if (sx->len == len && memcmp(str, strdata(sx), len) == 0) { 161 newsecond |= secondary;
167 /* Resurrect if dead. Can only happen with fixstring() (keywords). */ 162 setgcrefp(newtab[i], secondary);
168 if (isdead(g, o)) flipwhite(o); 163 }
169 return sx; /* Return existing string. */ 164 g->str.second = newsecond;
165 }
166#endif
167
168 /* Reinsert all strings from the old table into the new table. */
169 for (i = g->str.mask; i != ~(MSize)0; i--) {
170 GCobj *o = (GCobj *)(gcrefu(oldtab[i]) & ~(uintptr_t)1);
171 while (o) {
172 GCobj *next = gcnext(o);
173 GCstr *s = gco2str(o);
174 MSize hash = s->hash;
175#if LUAJIT_SECURITY_STRHASH
176 uintptr_t u;
177 if (LJ_LIKELY(!s->hashalg)) { /* String hashed with primary hash. */
178 hash &= newmask;
179 u = gcrefu(newtab[hash]);
180 if (LJ_UNLIKELY(u & 1)) { /* Switch string to secondary hash. */
181 s->hash = hash = hash_dense(g->str.seed, s->hash, strdata(s), s->len);
182 s->hashalg = 1;
183 hash &= newmask;
184 u = gcrefu(newtab[hash]);
185 }
186 } else { /* String hashed with secondary hash. */
187 MSize shash = hash_sparse(g->str.seed, strdata(s), s->len);
188 u = gcrefu(newtab[shash & newmask]);
189 if (u & 1) {
190 hash &= newmask;
191 u = gcrefu(newtab[hash]);
192 } else { /* Revert string back to primary hash. */
193 s->hash = shash;
194 s->hashalg = 0;
195 hash = (shash & newmask);
196 }
197 }
198 /* NOBARRIER: The string table is a GC root. */
199 setgcrefp(o->gch.nextgc, (u & ~(uintptr_t)1));
200 setgcrefp(newtab[hash], ((uintptr_t)o | (u & 1)));
201#else
202 hash &= newmask;
203 /* NOBARRIER: The string table is a GC root. */
204 setgcrefr(o->gch.nextgc, newtab[hash]);
205 setgcref(newtab[hash], o);
206#endif
207 o = next;
208 }
209 }
210
211 /* Free old table and replace with new table. */
212 lj_str_freetab(g);
213 g->str.tab = newtab;
214 g->str.mask = newmask;
215}
216
217#if LUAJIT_SECURITY_STRHASH
218/* Rehash and rechain all strings in a chain. */
219static LJ_NOINLINE GCstr *lj_str_rehash_chain(lua_State *L, StrHash hashc,
220 const char *str, MSize len)
221{
222 global_State *g = G(L);
223 int ow = g->gc.state == GCSsweepstring ? otherwhite(g) : 0; /* Sweeping? */
224 GCRef *strtab = g->str.tab;
225 MSize strmask = g->str.mask;
226 GCobj *o = gcref(strtab[hashc & strmask]);
227 setgcrefp(strtab[hashc & strmask], (void *)((uintptr_t)1));
228 g->str.second = 1;
229 while (o) {
230 uintptr_t u;
231 GCobj *next = gcnext(o);
232 GCstr *s = gco2str(o);
233 StrHash hash;
234 if (ow) { /* Must sweep while rechaining. */
235 if (((o->gch.marked ^ LJ_GC_WHITES) & ow)) { /* String alive? */
236 lj_assertG(!isdead(g, o) || (o->gch.marked & LJ_GC_FIXED),
237 "sweep of undead string");
238 makewhite(g, o);
239 } else { /* Free dead string. */
240 lj_assertG(isdead(g, o) || ow == LJ_GC_SFIXED,
241 "sweep of unlive string");
242 lj_str_free(g, s);
243 o = next;
244 continue;
170 } 245 }
171 o = gcnext(o);
172 } 246 }
247 hash = s->hash;
248 if (!s->hashalg) { /* Rehash with secondary hash. */
249 hash = hash_dense(g->str.seed, hash, strdata(s), s->len);
250 s->hash = hash;
251 s->hashalg = 1;
252 }
253 /* Rechain. */
254 hash &= strmask;
255 u = gcrefu(strtab[hash]);
256 setgcrefp(o->gch.nextgc, (u & ~(uintptr_t)1));
257 setgcrefp(strtab[hash], ((uintptr_t)o | (u & 1)));
258 o = next;
173 } 259 }
174 /* Nope, create a new string. */ 260 /* Try to insert the pending string again. */
175 s = lj_mem_newt(L, sizeof(GCstr)+len+1, GCstr); 261 return lj_str_new(L, str, len);
262}
263#endif
264
265/* Reseed String ID from PRNG after random interval < 2^bits. */
266#if LUAJIT_SECURITY_STRID == 1
267#define STRID_RESEED_INTERVAL 8
268#elif LUAJIT_SECURITY_STRID == 2
269#define STRID_RESEED_INTERVAL 4
270#elif LUAJIT_SECURITY_STRID >= 3
271#define STRID_RESEED_INTERVAL 0
272#endif
273
274/* Allocate a new string and add to string interning table. */
275static GCstr *lj_str_alloc(lua_State *L, const char *str, MSize len,
276 StrHash hash, int hashalg)
277{
278 GCstr *s = lj_mem_newt(L, lj_str_size(len), GCstr);
279 global_State *g = G(L);
280 uintptr_t u;
176 newwhite(g, s); 281 newwhite(g, s);
177 s->gct = ~LJ_TSTR; 282 s->gct = ~LJ_TSTR;
178 s->len = len; 283 s->len = len;
179 s->hash = h; 284 s->hash = hash;
285#ifndef STRID_RESEED_INTERVAL
286 s->sid = g->str.id++;
287#elif STRID_RESEED_INTERVAL
288 if (!g->str.idreseed--) {
289 uint64_t r = lj_prng_u64(&g->prng);
290 g->str.id = (StrID)r;
291 g->str.idreseed = (uint8_t)(r >> (64 - STRID_RESEED_INTERVAL));
292 }
293 s->sid = g->str.id++;
294#else
295 s->sid = (StrID)lj_prng_u64(&g->prng);
296#endif
180 s->reserved = 0; 297 s->reserved = 0;
298 s->hashalg = (uint8_t)hashalg;
299 /* Clear last 4 bytes of allocated memory. Implies zero-termination, too. */
300 *(uint32_t *)(strdatawr(s)+(len & ~(MSize)3)) = 0;
181 memcpy(strdatawr(s), str, len); 301 memcpy(strdatawr(s), str, len);
182 strdatawr(s)[len] = '\0'; /* Zero-terminate string. */ 302 /* Add to string hash table. */
183 /* Add it to string hash table. */ 303 hash &= g->str.mask;
184 h &= g->strmask; 304 u = gcrefu(g->str.tab[hash]);
185 s->nextgc = g->strhash[h]; 305 setgcrefp(s->nextgc, (u & ~(uintptr_t)1));
186 /* NOBARRIER: The string table is a GC root. */ 306 /* NOBARRIER: The string table is a GC root. */
187 setgcref(g->strhash[h], obj2gco(s)); 307 setgcrefp(g->str.tab[hash], ((uintptr_t)s | (u & 1)));
188 if (g->strnum++ > g->strmask) /* Allow a 100% load factor. */ 308 if (g->str.num++ > g->str.mask) /* Allow a 100% load factor. */
189 lj_str_resize(L, (g->strmask<<1)+1); /* Grow string table. */ 309 lj_str_resize(L, (g->str.mask<<1)+1); /* Grow string table. */
190 return s; /* Return newly interned string. */ 310 return s; /* Return newly interned string. */
191} 311}
192 312
313/* Intern a string and return string object. */
314GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx)
315{
316 global_State *g = G(L);
317 if (lenx-1 < LJ_MAX_STR-1) {
318 MSize len = (MSize)lenx;
319 StrHash hash = hash_sparse(g->str.seed, str, len);
320 MSize coll = 0;
321 int hashalg = 0;
322 /* Check if the string has already been interned. */
323 GCobj *o = gcref(g->str.tab[hash & g->str.mask]);
324#if LUAJIT_SECURITY_STRHASH
325 if (LJ_UNLIKELY((uintptr_t)o & 1)) { /* Secondary hash for this chain? */
326 hashalg = 1;
327 hash = hash_dense(g->str.seed, hash, str, len);
328 o = (GCobj *)(gcrefu(g->str.tab[hash & g->str.mask]) & ~(uintptr_t)1);
329 }
330#endif
331 while (o != NULL) {
332 GCstr *sx = gco2str(o);
333 if (sx->hash == hash && sx->len == len) {
334 if (memcmp(str, strdata(sx), len) == 0) {
335 if (isdead(g, o)) flipwhite(o); /* Resurrect if dead. */
336 return sx; /* Return existing string. */
337 }
338 coll++;
339 }
340 coll++;
341 o = gcnext(o);
342 }
343#if LUAJIT_SECURITY_STRHASH
344 /* Rehash chain if there are too many collisions. */
345 if (LJ_UNLIKELY(coll > LJ_STR_MAXCOLL) && !hashalg) {
346 return lj_str_rehash_chain(L, hash, str, len);
347 }
348#endif
349 /* Otherwise allocate a new string. */
350 return lj_str_alloc(L, str, len, hash, hashalg);
351 } else {
352 if (lenx)
353 lj_err_msg(L, LJ_ERR_STROV);
354 return &g->strempty;
355 }
356}
357
193void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s) 358void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s)
194{ 359{
195 g->strnum--; 360 g->str.num--;
196 lj_mem_free(g, s, sizestring(s)); 361 lj_mem_free(g, s, lj_str_size(s->len));
362}
363
364void LJ_FASTCALL lj_str_init(lua_State *L)
365{
366 global_State *g = G(L);
367 g->str.seed = lj_prng_u64(&g->prng);
368 lj_str_resize(L, LJ_MIN_STRTAB-1);
197} 369}
198 370
diff --git a/src/lj_str.h b/src/lj_str.h
index 2e9bfc1d..01c6ba6b 100644
--- a/src/lj_str.h
+++ b/src/lj_str.h
@@ -20,8 +20,12 @@ LJ_FUNC int lj_str_haspattern(GCstr *s);
20LJ_FUNC void lj_str_resize(lua_State *L, MSize newmask); 20LJ_FUNC void lj_str_resize(lua_State *L, MSize newmask);
21LJ_FUNCA GCstr *lj_str_new(lua_State *L, const char *str, size_t len); 21LJ_FUNCA GCstr *lj_str_new(lua_State *L, const char *str, size_t len);
22LJ_FUNC void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s); 22LJ_FUNC void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s);
23LJ_FUNC void LJ_FASTCALL lj_str_init(lua_State *L);
24#define lj_str_freetab(g) \
25 (lj_mem_freevec(g, g->str.tab, g->str.mask+1, GCRef))
23 26
24#define lj_str_newz(L, s) (lj_str_new(L, s, strlen(s))) 27#define lj_str_newz(L, s) (lj_str_new(L, s, strlen(s)))
25#define lj_str_newlit(L, s) (lj_str_new(L, "" s, sizeof(s)-1)) 28#define lj_str_newlit(L, s) (lj_str_new(L, "" s, sizeof(s)-1))
29#define lj_str_size(len) (sizeof(GCstr) + (((len)+4) & ~(MSize)3))
26 30
27#endif 31#endif
diff --git a/src/lj_tab.c b/src/lj_tab.c
index efc423cb..982b0763 100644
--- a/src/lj_tab.c
+++ b/src/lj_tab.c
@@ -23,8 +23,8 @@ static LJ_AINLINE Node *hashmask(const GCtab *t, uint32_t hash)
23 return &n[hash & t->hmask]; 23 return &n[hash & t->hmask];
24} 24}
25 25
26/* String hashes are precomputed when they are interned. */ 26/* String IDs are generated when a string is interned. */
27#define hashstr(t, s) hashmask(t, (s)->hash) 27#define hashstr(t, s) hashmask(t, (s)->sid)
28 28
29#define hashlohi(t, lo, hi) hashmask((t), hashrot((lo), (hi))) 29#define hashlohi(t, lo, hi) hashmask((t), hashrot((lo), (hi)))
30#define hashnum(t, o) hashlohi((t), (o)->u32.lo, ((o)->u32.hi << 1)) 30#define hashnum(t, o) hashlohi((t), (o)->u32.lo, ((o)->u32.hi << 1))
diff --git a/src/vm_arm.dasc b/src/vm_arm.dasc
index 013688fb..770a8e21 100644
--- a/src/vm_arm.dasc
+++ b/src/vm_arm.dasc
@@ -1012,9 +1012,9 @@ static void build_subroutines(BuildCtx *ctx)
1012 | cmp TAB:RB, #0 1012 | cmp TAB:RB, #0
1013 | beq ->fff_restv 1013 | beq ->fff_restv
1014 | ldr CARG3, TAB:RB->hmask 1014 | ldr CARG3, TAB:RB->hmask
1015 | ldr CARG4, STR:RC->hash 1015 | ldr CARG4, STR:RC->sid
1016 | ldr NODE:INS, TAB:RB->node 1016 | ldr NODE:INS, TAB:RB->node
1017 | and CARG3, CARG3, CARG4 // idx = str->hash & tab->hmask 1017 | and CARG3, CARG3, CARG4 // idx = str->sid & tab->hmask
1018 | add CARG3, CARG3, CARG3, lsl #1 1018 | add CARG3, CARG3, CARG3, lsl #1
1019 | add NODE:INS, NODE:INS, CARG3, lsl #3 // node = tab->node + idx*3*8 1019 | add NODE:INS, NODE:INS, CARG3, lsl #3 // node = tab->node + idx*3*8
1020 |3: // Rearranged logic, because we expect _not_ to find the key. 1020 |3: // Rearranged logic, because we expect _not_ to find the key.
@@ -3500,10 +3500,10 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
3500 |->BC_TGETS_Z: 3500 |->BC_TGETS_Z:
3501 | // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8 3501 | // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8
3502 | ldr CARG3, TAB:CARG1->hmask 3502 | ldr CARG3, TAB:CARG1->hmask
3503 | ldr CARG4, STR:RC->hash 3503 | ldr CARG4, STR:RC->sid
3504 | ldr NODE:INS, TAB:CARG1->node 3504 | ldr NODE:INS, TAB:CARG1->node
3505 | mov TAB:RB, TAB:CARG1 3505 | mov TAB:RB, TAB:CARG1
3506 | and CARG3, CARG3, CARG4 // idx = str->hash & tab->hmask 3506 | and CARG3, CARG3, CARG4 // idx = str->sid & tab->hmask
3507 | add CARG3, CARG3, CARG3, lsl #1 3507 | add CARG3, CARG3, CARG3, lsl #1
3508 | add NODE:INS, NODE:INS, CARG3, lsl #3 // node = tab->node + idx*3*8 3508 | add NODE:INS, NODE:INS, CARG3, lsl #3 // node = tab->node + idx*3*8
3509 |1: 3509 |1:
@@ -3647,10 +3647,10 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
3647 |->BC_TSETS_Z: 3647 |->BC_TSETS_Z:
3648 | // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8 3648 | // (TAB:RB =) TAB:CARG1 = GCtab *, STR:RC = GCstr *, RA = dst*8
3649 | ldr CARG3, TAB:CARG1->hmask 3649 | ldr CARG3, TAB:CARG1->hmask
3650 | ldr CARG4, STR:RC->hash 3650 | ldr CARG4, STR:RC->sid
3651 | ldr NODE:INS, TAB:CARG1->node 3651 | ldr NODE:INS, TAB:CARG1->node
3652 | mov TAB:RB, TAB:CARG1 3652 | mov TAB:RB, TAB:CARG1
3653 | and CARG3, CARG3, CARG4 // idx = str->hash & tab->hmask 3653 | and CARG3, CARG3, CARG4 // idx = str->sid & tab->hmask
3654 | add CARG3, CARG3, CARG3, lsl #1 3654 | add CARG3, CARG3, CARG3, lsl #1
3655 | mov CARG4, #0 3655 | mov CARG4, #0
3656 | add NODE:INS, NODE:INS, CARG3, lsl #3 // node = tab->node + idx*3*8 3656 | add NODE:INS, NODE:INS, CARG3, lsl #3 // node = tab->node + idx*3*8
diff --git a/src/vm_arm64.dasc b/src/vm_arm64.dasc
index c157696c..4a729f65 100644
--- a/src/vm_arm64.dasc
+++ b/src/vm_arm64.dasc
@@ -993,9 +993,9 @@ static void build_subroutines(BuildCtx *ctx)
993 | ldr STR:RC, GL->gcroot[GCROOT_MMNAME+MM_metatable] 993 | ldr STR:RC, GL->gcroot[GCROOT_MMNAME+MM_metatable]
994 | cbz TAB:RB, ->fff_restv 994 | cbz TAB:RB, ->fff_restv
995 | ldr TMP1w, TAB:RB->hmask 995 | ldr TMP1w, TAB:RB->hmask
996 | ldr TMP2w, STR:RC->hash 996 | ldr TMP2w, STR:RC->sid
997 | ldr NODE:CARG3, TAB:RB->node 997 | ldr NODE:CARG3, TAB:RB->node
998 | and TMP1w, TMP1w, TMP2w // idx = str->hash & tab->hmask 998 | and TMP1w, TMP1w, TMP2w // idx = str->sid & tab->hmask
999 | add TMP1, TMP1, TMP1, lsl #1 999 | add TMP1, TMP1, TMP1, lsl #1
1000 | movn CARG4, #~LJ_TSTR 1000 | movn CARG4, #~LJ_TSTR
1001 | add NODE:CARG3, NODE:CARG3, TMP1, lsl #3 // node = tab->node + idx*3*8 1001 | add NODE:CARG3, NODE:CARG3, TMP1, lsl #3 // node = tab->node + idx*3*8
@@ -2943,9 +2943,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
2943 |->BC_TGETS_Z: 2943 |->BC_TGETS_Z:
2944 | // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = dst 2944 | // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = dst
2945 | ldr TMP1w, TAB:CARG2->hmask 2945 | ldr TMP1w, TAB:CARG2->hmask
2946 | ldr TMP2w, STR:RC->hash 2946 | ldr TMP2w, STR:RC->sid
2947 | ldr NODE:CARG3, TAB:CARG2->node 2947 | ldr NODE:CARG3, TAB:CARG2->node
2948 | and TMP1w, TMP1w, TMP2w // idx = str->hash & tab->hmask 2948 | and TMP1w, TMP1w, TMP2w // idx = str->sid & tab->hmask
2949 | add TMP1, TMP1, TMP1, lsl #1 2949 | add TMP1, TMP1, TMP1, lsl #1
2950 | movn CARG4, #~LJ_TSTR 2950 | movn CARG4, #~LJ_TSTR
2951 | add NODE:CARG3, NODE:CARG3, TMP1, lsl #3 // node = tab->node + idx*3*8 2951 | add NODE:CARG3, NODE:CARG3, TMP1, lsl #3 // node = tab->node + idx*3*8
@@ -3069,9 +3069,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
3069 |->BC_TSETS_Z: 3069 |->BC_TSETS_Z:
3070 | // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = src 3070 | // TAB:CARG2 = GCtab *, STR:RC = GCstr *, RA = src
3071 | ldr TMP1w, TAB:CARG2->hmask 3071 | ldr TMP1w, TAB:CARG2->hmask
3072 | ldr TMP2w, STR:RC->hash 3072 | ldr TMP2w, STR:RC->sid
3073 | ldr NODE:CARG3, TAB:CARG2->node 3073 | ldr NODE:CARG3, TAB:CARG2->node
3074 | and TMP1w, TMP1w, TMP2w // idx = str->hash & tab->hmask 3074 | and TMP1w, TMP1w, TMP2w // idx = str->sid & tab->hmask
3075 | add TMP1, TMP1, TMP1, lsl #1 3075 | add TMP1, TMP1, TMP1, lsl #1
3076 | movn CARG4, #~LJ_TSTR 3076 | movn CARG4, #~LJ_TSTR
3077 | add NODE:CARG3, NODE:CARG3, TMP1, lsl #3 // node = tab->node + idx*3*8 3077 | add NODE:CARG3, NODE:CARG3, TMP1, lsl #3 // node = tab->node + idx*3*8
diff --git a/src/vm_mips.dasc b/src/vm_mips.dasc
index 0c84c13b..91de4b5c 100644
--- a/src/vm_mips.dasc
+++ b/src/vm_mips.dasc
@@ -1152,9 +1152,9 @@ static void build_subroutines(BuildCtx *ctx)
1152 |. li SFARG1HI, LJ_TNIL 1152 |. li SFARG1HI, LJ_TNIL
1153 | lw TMP0, TAB:SFARG1LO->hmask 1153 | lw TMP0, TAB:SFARG1LO->hmask
1154 | li SFARG1HI, LJ_TTAB // Use metatable as default result. 1154 | li SFARG1HI, LJ_TTAB // Use metatable as default result.
1155 | lw TMP1, STR:RC->hash 1155 | lw TMP1, STR:RC->sid
1156 | lw NODE:TMP2, TAB:SFARG1LO->node 1156 | lw NODE:TMP2, TAB:SFARG1LO->node
1157 | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask 1157 | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask
1158 | sll TMP0, TMP1, 5 1158 | sll TMP0, TMP1, 5
1159 | sll TMP1, TMP1, 3 1159 | sll TMP1, TMP1, 3
1160 | subu TMP1, TMP0, TMP1 1160 | subu TMP1, TMP0, TMP1
@@ -4029,9 +4029,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
4029 |->BC_TGETS_Z: 4029 |->BC_TGETS_Z:
4030 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8 4030 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8
4031 | lw TMP0, TAB:RB->hmask 4031 | lw TMP0, TAB:RB->hmask
4032 | lw TMP1, STR:RC->hash 4032 | lw TMP1, STR:RC->sid
4033 | lw NODE:TMP2, TAB:RB->node 4033 | lw NODE:TMP2, TAB:RB->node
4034 | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask 4034 | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask
4035 | sll TMP0, TMP1, 5 4035 | sll TMP0, TMP1, 5
4036 | sll TMP1, TMP1, 3 4036 | sll TMP1, TMP1, 3
4037 | subu TMP1, TMP0, TMP1 4037 | subu TMP1, TMP0, TMP1
@@ -4203,10 +4203,10 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
4203 |->BC_TSETS_Z: 4203 |->BC_TSETS_Z:
4204 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = BASE+src*8 4204 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = BASE+src*8
4205 | lw TMP0, TAB:RB->hmask 4205 | lw TMP0, TAB:RB->hmask
4206 | lw TMP1, STR:RC->hash 4206 | lw TMP1, STR:RC->sid
4207 | lw NODE:TMP2, TAB:RB->node 4207 | lw NODE:TMP2, TAB:RB->node
4208 | sb r0, TAB:RB->nomm // Clear metamethod cache. 4208 | sb r0, TAB:RB->nomm // Clear metamethod cache.
4209 | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask 4209 | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask
4210 | sll TMP0, TMP1, 5 4210 | sll TMP0, TMP1, 5
4211 | sll TMP1, TMP1, 3 4211 | sll TMP1, TMP1, 3
4212 | subu TMP1, TMP0, TMP1 4212 | subu TMP1, TMP0, TMP1
diff --git a/src/vm_mips64.dasc b/src/vm_mips64.dasc
index dac143a4..71acf9ed 100644
--- a/src/vm_mips64.dasc
+++ b/src/vm_mips64.dasc
@@ -1201,9 +1201,9 @@ static void build_subroutines(BuildCtx *ctx)
1201 | beqz TAB:RB, ->fff_restv 1201 | beqz TAB:RB, ->fff_restv
1202 |. li CARG1, LJ_TNIL 1202 |. li CARG1, LJ_TNIL
1203 | lw TMP0, TAB:RB->hmask 1203 | lw TMP0, TAB:RB->hmask
1204 | lw TMP1, STR:RC->hash 1204 | lw TMP1, STR:RC->sid
1205 | ld NODE:TMP2, TAB:RB->node 1205 | ld NODE:TMP2, TAB:RB->node
1206 | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask 1206 | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask
1207 | dsll TMP0, TMP1, 5 1207 | dsll TMP0, TMP1, 5
1208 | dsll TMP1, TMP1, 3 1208 | dsll TMP1, TMP1, 3
1209 | dsubu TMP1, TMP0, TMP1 1209 | dsubu TMP1, TMP0, TMP1
@@ -4239,9 +4239,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
4239 |->BC_TGETS_Z: 4239 |->BC_TGETS_Z:
4240 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8 4240 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8
4241 | lw TMP0, TAB:RB->hmask 4241 | lw TMP0, TAB:RB->hmask
4242 | lw TMP1, STR:RC->hash 4242 | lw TMP1, STR:RC->sid
4243 | ld NODE:TMP2, TAB:RB->node 4243 | ld NODE:TMP2, TAB:RB->node
4244 | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask 4244 | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask
4245 | sll TMP0, TMP1, 5 4245 | sll TMP0, TMP1, 5
4246 | sll TMP1, TMP1, 3 4246 | sll TMP1, TMP1, 3
4247 | subu TMP1, TMP0, TMP1 4247 | subu TMP1, TMP0, TMP1
@@ -4402,10 +4402,10 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
4402 |->BC_TSETS_Z: 4402 |->BC_TSETS_Z:
4403 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = BASE+src*8 4403 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = BASE+src*8
4404 | lw TMP0, TAB:RB->hmask 4404 | lw TMP0, TAB:RB->hmask
4405 | lw TMP1, STR:RC->hash 4405 | lw TMP1, STR:RC->sid
4406 | ld NODE:TMP2, TAB:RB->node 4406 | ld NODE:TMP2, TAB:RB->node
4407 | sb r0, TAB:RB->nomm // Clear metamethod cache. 4407 | sb r0, TAB:RB->nomm // Clear metamethod cache.
4408 | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask 4408 | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask
4409 | sll TMP0, TMP1, 5 4409 | sll TMP0, TMP1, 5
4410 | sll TMP1, TMP1, 3 4410 | sll TMP1, TMP1, 3
4411 | subu TMP1, TMP0, TMP1 4411 | subu TMP1, TMP0, TMP1
diff --git a/src/vm_ppc.dasc b/src/vm_ppc.dasc
index 7a2d321e..18fc6f93 100644
--- a/src/vm_ppc.dasc
+++ b/src/vm_ppc.dasc
@@ -1447,9 +1447,9 @@ static void build_subroutines(BuildCtx *ctx)
1447 | beq ->fff_restv 1447 | beq ->fff_restv
1448 | lwz TMP0, TAB:CARG1->hmask 1448 | lwz TMP0, TAB:CARG1->hmask
1449 | li CARG3, LJ_TTAB // Use metatable as default result. 1449 | li CARG3, LJ_TTAB // Use metatable as default result.
1450 | lwz TMP1, STR:RC->hash 1450 | lwz TMP1, STR:RC->sid
1451 | lwz NODE:TMP2, TAB:CARG1->node 1451 | lwz NODE:TMP2, TAB:CARG1->node
1452 | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask 1452 | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask
1453 | slwi TMP0, TMP1, 5 1453 | slwi TMP0, TMP1, 5
1454 | slwi TMP1, TMP1, 3 1454 | slwi TMP1, TMP1, 3
1455 | sub TMP1, TMP0, TMP1 1455 | sub TMP1, TMP0, TMP1
@@ -4588,9 +4588,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
4588 |->BC_TGETS_Z: 4588 |->BC_TGETS_Z:
4589 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8 4589 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = dst*8
4590 | lwz TMP0, TAB:RB->hmask 4590 | lwz TMP0, TAB:RB->hmask
4591 | lwz TMP1, STR:RC->hash 4591 | lwz TMP1, STR:RC->sid
4592 | lwz NODE:TMP2, TAB:RB->node 4592 | lwz NODE:TMP2, TAB:RB->node
4593 | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask 4593 | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask
4594 | slwi TMP0, TMP1, 5 4594 | slwi TMP0, TMP1, 5
4595 | slwi TMP1, TMP1, 3 4595 | slwi TMP1, TMP1, 3
4596 | sub TMP1, TMP0, TMP1 4596 | sub TMP1, TMP0, TMP1
@@ -4784,10 +4784,10 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
4784 |->BC_TSETS_Z: 4784 |->BC_TSETS_Z:
4785 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = src*8 4785 | // TAB:RB = GCtab *, STR:RC = GCstr *, RA = src*8
4786 | lwz TMP0, TAB:RB->hmask 4786 | lwz TMP0, TAB:RB->hmask
4787 | lwz TMP1, STR:RC->hash 4787 | lwz TMP1, STR:RC->sid
4788 | lwz NODE:TMP2, TAB:RB->node 4788 | lwz NODE:TMP2, TAB:RB->node
4789 | stb ZERO, TAB:RB->nomm // Clear metamethod cache. 4789 | stb ZERO, TAB:RB->nomm // Clear metamethod cache.
4790 | and TMP1, TMP1, TMP0 // idx = str->hash & tab->hmask 4790 | and TMP1, TMP1, TMP0 // idx = str->sid & tab->hmask
4791 |.if FPU 4791 |.if FPU
4792 | lfdx f14, BASE, RA 4792 | lfdx f14, BASE, RA
4793 |.else 4793 |.else
diff --git a/src/vm_x64.dasc b/src/vm_x64.dasc
index 77a579d5..14a54e34 100644
--- a/src/vm_x64.dasc
+++ b/src/vm_x64.dasc
@@ -1230,7 +1230,7 @@ static void build_subroutines(BuildCtx *ctx)
1230 | mov [BASE-16], TAB:RC // Store metatable as default result. 1230 | mov [BASE-16], TAB:RC // Store metatable as default result.
1231 | mov STR:RC, [DISPATCH+DISPATCH_GL(gcroot)+8*(GCROOT_MMNAME+MM_metatable)] 1231 | mov STR:RC, [DISPATCH+DISPATCH_GL(gcroot)+8*(GCROOT_MMNAME+MM_metatable)]
1232 | mov RAd, TAB:RB->hmask 1232 | mov RAd, TAB:RB->hmask
1233 | and RAd, STR:RC->hash 1233 | and RAd, STR:RC->sid
1234 | settp STR:RC, LJ_TSTR 1234 | settp STR:RC, LJ_TSTR
1235 | imul RAd, #NODE 1235 | imul RAd, #NODE
1236 | add NODE:RA, TAB:RB->node 1236 | add NODE:RA, TAB:RB->node
@@ -3674,7 +3674,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
3674 | checktab TAB:RB, ->vmeta_tgets 3674 | checktab TAB:RB, ->vmeta_tgets
3675 |->BC_TGETS_Z: // RB = GCtab *, RC = GCstr * 3675 |->BC_TGETS_Z: // RB = GCtab *, RC = GCstr *
3676 | mov TMPRd, TAB:RB->hmask 3676 | mov TMPRd, TAB:RB->hmask
3677 | and TMPRd, STR:RC->hash 3677 | and TMPRd, STR:RC->sid
3678 | imul TMPRd, #NODE 3678 | imul TMPRd, #NODE
3679 | add NODE:TMPR, TAB:RB->node 3679 | add NODE:TMPR, TAB:RB->node
3680 | settp ITYPE, STR:RC, LJ_TSTR 3680 | settp ITYPE, STR:RC, LJ_TSTR
@@ -3806,7 +3806,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
3806 | checktab TAB:RB, ->vmeta_tsets 3806 | checktab TAB:RB, ->vmeta_tsets
3807 |->BC_TSETS_Z: // RB = GCtab *, RC = GCstr * 3807 |->BC_TSETS_Z: // RB = GCtab *, RC = GCstr *
3808 | mov TMPRd, TAB:RB->hmask 3808 | mov TMPRd, TAB:RB->hmask
3809 | and TMPRd, STR:RC->hash 3809 | and TMPRd, STR:RC->sid
3810 | imul TMPRd, #NODE 3810 | imul TMPRd, #NODE
3811 | mov byte TAB:RB->nomm, 0 // Clear metamethod cache. 3811 | mov byte TAB:RB->nomm, 0 // Clear metamethod cache.
3812 | add NODE:TMPR, TAB:RB->node 3812 | add NODE:TMPR, TAB:RB->node
diff --git a/src/vm_x86.dasc b/src/vm_x86.dasc
index 57c8e4fc..f9bea426 100644
--- a/src/vm_x86.dasc
+++ b/src/vm_x86.dasc
@@ -1522,7 +1522,7 @@ static void build_subroutines(BuildCtx *ctx)
1522 | mov dword [BASE-4], LJ_TTAB // Store metatable as default result. 1522 | mov dword [BASE-4], LJ_TTAB // Store metatable as default result.
1523 | mov [BASE-8], TAB:RB 1523 | mov [BASE-8], TAB:RB
1524 | mov RA, TAB:RB->hmask 1524 | mov RA, TAB:RB->hmask
1525 | and RA, STR:RC->hash 1525 | and RA, STR:RC->sid
1526 | imul RA, #NODE 1526 | imul RA, #NODE
1527 | add NODE:RA, TAB:RB->node 1527 | add NODE:RA, TAB:RB->node
1528 |3: // Rearranged logic, because we expect _not_ to find the key. 1528 |3: // Rearranged logic, because we expect _not_ to find the key.
@@ -4286,7 +4286,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
4286 | mov TAB:RB, [BASE+RB*8] 4286 | mov TAB:RB, [BASE+RB*8]
4287 |->BC_TGETS_Z: // RB = GCtab *, RC = GCstr *, refetches PC_RA. 4287 |->BC_TGETS_Z: // RB = GCtab *, RC = GCstr *, refetches PC_RA.
4288 | mov RA, TAB:RB->hmask 4288 | mov RA, TAB:RB->hmask
4289 | and RA, STR:RC->hash 4289 | and RA, STR:RC->sid
4290 | imul RA, #NODE 4290 | imul RA, #NODE
4291 | add NODE:RA, TAB:RB->node 4291 | add NODE:RA, TAB:RB->node
4292 |1: 4292 |1:
@@ -4457,7 +4457,7 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
4457 | mov TAB:RB, [BASE+RB*8] 4457 | mov TAB:RB, [BASE+RB*8]
4458 |->BC_TSETS_Z: // RB = GCtab *, RC = GCstr *, refetches PC_RA. 4458 |->BC_TSETS_Z: // RB = GCtab *, RC = GCstr *, refetches PC_RA.
4459 | mov RA, TAB:RB->hmask 4459 | mov RA, TAB:RB->hmask
4460 | and RA, STR:RC->hash 4460 | and RA, STR:RC->sid
4461 | imul RA, #NODE 4461 | imul RA, #NODE
4462 | mov byte TAB:RB->nomm, 0 // Clear metamethod cache. 4462 | mov byte TAB:RB->nomm, 0 // Clear metamethod cache.
4463 | add NODE:RA, TAB:RB->node 4463 | add NODE:RA, TAB:RB->node