diff options
author | Mike Pall <mike> | 2010-04-21 01:45:58 +0200 |
---|---|---|
committer | Mike Pall <mike> | 2010-04-21 01:45:58 +0200 |
commit | ab45481199e9c9dd3efec922647bcec122504bcb (patch) | |
tree | 0484341edff50a0afe98133ad66fb6a59915996a /src | |
parent | d8cb69ed076c3444258f63314662451c9d117cae (diff) | |
download | luajit-ab45481199e9c9dd3efec922647bcec122504bcb.tar.gz luajit-ab45481199e9c9dd3efec922647bcec122504bcb.tar.bz2 luajit-ab45481199e9c9dd3efec922647bcec122504bcb.zip |
No longer let the GC replace dead keys with the LJ_TDEADKEY tag.
Important: this changes the semantics of the write barrier!
Carefully read the big comment block in lj_obj.h
This helps HREFK key slot specialization and allows safely hoisting
HREF/HREFK across GC steps, too (fix for a barely reproducible bug).
Dead keys are only removed during a table resize (as before).
Diffstat (limited to 'src')
-rw-r--r-- | src/lib_base.c | 2 | ||||
-rw-r--r-- | src/lj_api.c | 7 | ||||
-rw-r--r-- | src/lj_gc.c | 8 | ||||
-rw-r--r-- | src/lj_gc.h | 2 | ||||
-rw-r--r-- | src/lj_ir.h | 2 | ||||
-rw-r--r-- | src/lj_lib.c | 2 | ||||
-rw-r--r-- | src/lj_meta.c | 2 | ||||
-rw-r--r-- | src/lj_obj.c | 14 | ||||
-rw-r--r-- | src/lj_obj.h | 41 | ||||
-rw-r--r-- | src/lj_opt_fold.c | 5 | ||||
-rw-r--r-- | src/lj_parse.c | 6 | ||||
-rw-r--r-- | src/lj_record.c | 9 | ||||
-rw-r--r-- | src/lj_tab.c | 8 |
13 files changed, 65 insertions, 43 deletions
diff --git a/src/lib_base.c b/src/lib_base.c index e85b7264..cbfd818a 100644 --- a/src/lib_base.c +++ b/src/lib_base.c | |||
@@ -54,7 +54,7 @@ LJLIB_PUSH("upval") | |||
54 | LJLIB_PUSH("thread") | 54 | LJLIB_PUSH("thread") |
55 | LJLIB_PUSH("proto") | 55 | LJLIB_PUSH("proto") |
56 | LJLIB_PUSH("function") | 56 | LJLIB_PUSH("function") |
57 | LJLIB_PUSH("deadkey") | 57 | LJLIB_PUSH("") /* Unused. */ |
58 | LJLIB_PUSH("table") | 58 | LJLIB_PUSH("table") |
59 | LJLIB_PUSH(top-8) /* userdata */ | 59 | LJLIB_PUSH(top-8) /* userdata */ |
60 | LJLIB_PUSH("number") | 60 | LJLIB_PUSH("number") |
diff --git a/src/lj_api.c b/src/lj_api.c index a19f0b33..aac3b4c9 100644 --- a/src/lj_api.c +++ b/src/lj_api.c | |||
@@ -196,7 +196,8 @@ LUA_API int lua_type(lua_State *L, int idx) | |||
196 | return LUA_TNONE; | 196 | return LUA_TNONE; |
197 | } else { /* Magic internal/external tag conversion. ORDER LJ_T */ | 197 | } else { /* Magic internal/external tag conversion. ORDER LJ_T */ |
198 | int t = ~itype(o); | 198 | int t = ~itype(o); |
199 | return (int)(((t < 8 ? 0x98a42110 : 0x75b6) >> 4*(t&7)) & 15u); | 199 | lua_assert(itype(o) != LJ_TUPVAL); |
200 | return (int)(((t < 8 ? 0x98042110 : 0x7506) >> 4*(t&7)) & 15u); | ||
200 | } | 201 | } |
201 | } | 202 | } |
202 | 203 | ||
@@ -631,7 +632,7 @@ LUALIB_API int luaL_newmetatable(lua_State *L, const char *tname) | |||
631 | GCtab *mt = lj_tab_new(L, 0, 1); | 632 | GCtab *mt = lj_tab_new(L, 0, 1); |
632 | settabV(L, tv, mt); | 633 | settabV(L, tv, mt); |
633 | settabV(L, L->top++, mt); | 634 | settabV(L, L->top++, mt); |
634 | lj_gc_objbarriert(L, regt, mt); | 635 | lj_gc_anybarriert(L, regt); |
635 | return 1; | 636 | return 1; |
636 | } else { | 637 | } else { |
637 | copyTV(L, L->top++, tv); | 638 | copyTV(L, L->top++, tv); |
@@ -899,7 +900,7 @@ LUA_API void lua_rawset(lua_State *L, int idx) | |||
899 | key = L->top-2; | 900 | key = L->top-2; |
900 | dst = lj_tab_set(L, t, key); | 901 | dst = lj_tab_set(L, t, key); |
901 | copyTV(L, dst, key+1); | 902 | copyTV(L, dst, key+1); |
902 | lj_gc_barriert(L, t, dst); | 903 | lj_gc_anybarriert(L, t); |
903 | L->top = key; | 904 | L->top = key; |
904 | } | 905 | } |
905 | 906 | ||
diff --git a/src/lj_gc.c b/src/lj_gc.c index b97fb955..b457c424 100644 --- a/src/lj_gc.c +++ b/src/lj_gc.c | |||
@@ -186,13 +186,10 @@ static int gc_traverse_tab(global_State *g, GCtab *t) | |||
186 | MSize i, hmask = t->hmask; | 186 | MSize i, hmask = t->hmask; |
187 | for (i = 0; i <= hmask; i++) { | 187 | for (i = 0; i <= hmask; i++) { |
188 | Node *n = &node[i]; | 188 | Node *n = &node[i]; |
189 | lua_assert(itype(&n->key) != LJ_TDEADKEY || tvisnil(&n->val)); | ||
190 | if (!tvisnil(&n->val)) { /* Mark non-empty slot. */ | 189 | if (!tvisnil(&n->val)) { /* Mark non-empty slot. */ |
191 | lua_assert(!tvisnil(&n->key)); | 190 | lua_assert(!tvisnil(&n->key)); |
192 | if (!(weak & LJ_GC_WEAKKEY)) gc_marktv(g, &n->key); | 191 | if (!(weak & LJ_GC_WEAKKEY)) gc_marktv(g, &n->key); |
193 | if (!(weak & LJ_GC_WEAKVAL)) gc_marktv(g, &n->val); | 192 | if (!(weak & LJ_GC_WEAKVAL)) gc_marktv(g, &n->val); |
194 | } else if (tvisgcv(&n->key)) { /* Leave GC key in, but mark as dead. */ | ||
195 | setitype(&n->key, LJ_TDEADKEY); | ||
196 | } | 193 | } |
197 | } | 194 | } |
198 | } | 195 | } |
@@ -424,11 +421,8 @@ static void gc_clearweak(GCobj *o) | |||
424 | Node *n = &node[i]; | 421 | Node *n = &node[i]; |
425 | /* Clear hash slot when key or value is about to be collected. */ | 422 | /* Clear hash slot when key or value is about to be collected. */ |
426 | if (!tvisnil(&n->val) && (gc_mayclear(&n->key, 0) || | 423 | if (!tvisnil(&n->val) && (gc_mayclear(&n->key, 0) || |
427 | gc_mayclear(&n->val, 1))) { | 424 | gc_mayclear(&n->val, 1))) |
428 | setnilV(&n->val); | 425 | setnilV(&n->val); |
429 | if (tvisgcv(&n->key)) /* Leave GC key in, but mark as dead. */ | ||
430 | setitype(&n->key, LJ_TDEADKEY); | ||
431 | } | ||
432 | } | 426 | } |
433 | } | 427 | } |
434 | o = gcref(t->gclist); | 428 | o = gcref(t->gclist); |
diff --git a/src/lj_gc.h b/src/lj_gc.h index 7279b93c..3d213eb3 100644 --- a/src/lj_gc.h +++ b/src/lj_gc.h | |||
@@ -69,6 +69,8 @@ LJ_FUNC void lj_gc_barriertrace(global_State *g, void *T); | |||
69 | #endif | 69 | #endif |
70 | 70 | ||
71 | /* Barrier for stores to table objects. TValue and GCobj variant. */ | 71 | /* Barrier for stores to table objects. TValue and GCobj variant. */ |
72 | #define lj_gc_anybarriert(L, t) \ | ||
73 | { if (isblack(obj2gco(t))) lj_gc_barrierback(G(L), (t)); } | ||
72 | #define lj_gc_barriert(L, t, tv) \ | 74 | #define lj_gc_barriert(L, t, tv) \ |
73 | { if (tviswhite(tv) && isblack(obj2gco(t))) \ | 75 | { if (tviswhite(tv) && isblack(obj2gco(t))) \ |
74 | lj_gc_barrierback(G(L), (t)); } | 76 | lj_gc_barrierback(G(L), (t)); } |
diff --git a/src/lj_ir.h b/src/lj_ir.h index ca871238..14f80ac5 100644 --- a/src/lj_ir.h +++ b/src/lj_ir.h | |||
@@ -317,7 +317,7 @@ typedef enum { | |||
317 | IRT_THREAD, | 317 | IRT_THREAD, |
318 | IRT_PROTO, | 318 | IRT_PROTO, |
319 | IRT_FUNC, | 319 | IRT_FUNC, |
320 | IRT_9, /* LJ_TDEADKEY is never used in the IR. */ | 320 | IRT_9, /* Never used in the IR. */ |
321 | IRT_TAB, | 321 | IRT_TAB, |
322 | IRT_UDATA, | 322 | IRT_UDATA, |
323 | /* ... until here. */ | 323 | /* ... until here. */ |
diff --git a/src/lj_lib.c b/src/lj_lib.c index 0ba0ecb1..9540772e 100644 --- a/src/lj_lib.c +++ b/src/lj_lib.c | |||
@@ -53,7 +53,7 @@ void lj_lib_register(lua_State *L, const char *libname, | |||
53 | ptrdiff_t tpos = L->top - L->base; | 53 | ptrdiff_t tpos = L->top - L->base; |
54 | 54 | ||
55 | /* Avoid barriers further down. */ | 55 | /* Avoid barriers further down. */ |
56 | if (isblack(obj2gco(tab))) lj_gc_barrierback(G(L), tab); | 56 | lj_gc_anybarriert(L, tab); |
57 | tab->nomm = 0; | 57 | tab->nomm = 0; |
58 | 58 | ||
59 | for (;;) { | 59 | for (;;) { |
diff --git a/src/lj_meta.c b/src/lj_meta.c index c8ac18d6..41124fdc 100644 --- a/src/lj_meta.c +++ b/src/lj_meta.c | |||
@@ -134,7 +134,7 @@ TValue *lj_meta_tset(lua_State *L, cTValue *o, cTValue *k) | |||
134 | TValue *tv = lj_tab_set(L, t, k); | 134 | TValue *tv = lj_tab_set(L, t, k); |
135 | if (!tvisnil(tv) || | 135 | if (!tvisnil(tv) || |
136 | !(mo = lj_meta_fast(L, tabref(t->metatable), MM_newindex))) { | 136 | !(mo = lj_meta_fast(L, tabref(t->metatable), MM_newindex))) { |
137 | if (isblack(obj2gco(t))) lj_gc_barrierback(G(L), t); | 137 | lj_gc_anybarriert(L, t); |
138 | return tv; | 138 | return tv; |
139 | } | 139 | } |
140 | } else if (tvisnil(mo = lj_meta_lookup(L, o, MM_newindex))) { | 140 | } else if (tvisnil(mo = lj_meta_lookup(L, o, MM_newindex))) { |
diff --git a/src/lj_obj.c b/src/lj_obj.c index 4363e790..0df51bc4 100644 --- a/src/lj_obj.c +++ b/src/lj_obj.c | |||
@@ -11,12 +11,12 @@ | |||
11 | /* Object type names. */ | 11 | /* Object type names. */ |
12 | LJ_DATADEF const char *const lj_obj_typename[] = { /* ORDER LUA_T */ | 12 | LJ_DATADEF const char *const lj_obj_typename[] = { /* ORDER LUA_T */ |
13 | "no value", "nil", "boolean", "userdata", "number", "string", | 13 | "no value", "nil", "boolean", "userdata", "number", "string", |
14 | "table", "function", "userdata", "thread", "proto", "upval" | 14 | "table", "function", "userdata", "thread", "proto" |
15 | }; | 15 | }; |
16 | 16 | ||
17 | LJ_DATADEF const char *const lj_obj_itypename[] = { /* ORDER LJ_T */ | 17 | LJ_DATADEF const char *const lj_obj_itypename[] = { /* ORDER LJ_T */ |
18 | "nil", "boolean", "boolean", "userdata", "string", "upval", "thread", | 18 | "nil", "boolean", "boolean", "userdata", "string", "upval", "thread", |
19 | "proto", "function", "deadkey", "table", "userdata", "number" | 19 | "proto", "function", "" /* Unused */, "table", "userdata", "number" |
20 | }; | 20 | }; |
21 | 21 | ||
22 | /* Compare two objects without calling metamethods. */ | 22 | /* Compare two objects without calling metamethods. */ |
@@ -25,14 +25,8 @@ int lj_obj_equal(cTValue *o1, cTValue *o2) | |||
25 | if (itype(o1) == itype(o2)) { | 25 | if (itype(o1) == itype(o2)) { |
26 | if (tvispri(o1)) | 26 | if (tvispri(o1)) |
27 | return 1; | 27 | return 1; |
28 | if (!tvisnum(o1)) { | 28 | if (!tvisnum(o1)) |
29 | #if LJ_64 | 29 | return gcrefeq(o1->gcr, o2->gcr); |
30 | if (tvislightud(o1)) | ||
31 | return o1->u64 == o2->u64; | ||
32 | else | ||
33 | #endif | ||
34 | return gcrefeq(o1->gcr, o2->gcr); | ||
35 | } | ||
36 | } else if (!tvisnum(o1) || !tvisnum(o2)) { | 30 | } else if (!tvisnum(o1) || !tvisnum(o2)) { |
37 | return 0; | 31 | return 0; |
38 | } | 32 | } |
diff --git a/src/lj_obj.h b/src/lj_obj.h index 048a74f9..71146dfc 100644 --- a/src/lj_obj.h +++ b/src/lj_obj.h | |||
@@ -75,9 +75,37 @@ typedef struct GCRef { | |||
75 | ** a barrier has been omitted are annotated with a NOBARRIER comment. | 75 | ** a barrier has been omitted are annotated with a NOBARRIER comment. |
76 | ** | 76 | ** |
77 | ** The same logic applies for stores to table slots (array part or hash | 77 | ** The same logic applies for stores to table slots (array part or hash |
78 | ** part). ALL uses of lj_tab_set* require a barrier for the stored *value* | 78 | ** part). ALL uses of lj_tab_set* require a barrier for the stored value |
79 | ** (if it's a GC object). The barrier for the *key* is already handled | 79 | ** *and* the stored key, based on the above rules. In practice this means |
80 | ** internally by lj_tab_newkey. | 80 | ** a barrier is needed if *either* of the key or value are a GC object. |
81 | ** | ||
82 | ** It's ok to LEAVE OUT the write barrier in the following special cases: | ||
83 | ** - The stored value is nil. The key doesn't matter because it's either | ||
84 | ** not resurrected or lj_tab_newkey() will take care of the key barrier. | ||
85 | ** - The key doesn't matter if the *previously* stored value is guaranteed | ||
86 | ** to be non-nil (because the key is kept alive in the table). | ||
87 | ** - The key doesn't matter if it's guaranteed not to be part of the table, | ||
88 | ** since lj_tab_newkey() takes care of the key barrier. This applies | ||
89 | ** trivially to new tables, but watch out for resurrected keys. Storing | ||
90 | ** a nil value leaves the key in the table! | ||
91 | ** | ||
92 | ** In case of doubt use lj_gc_anybarriert() as it's rather cheap. It's used | ||
93 | ** by the interpreter for all table stores. | ||
94 | ** | ||
95 | ** Note: In contrast to Lua's GC, LuaJIT's GC does *not* specially mark | ||
96 | ** dead keys in tables. The reference is left in, but it's guaranteed to | ||
97 | ** be never dereferenced as long as the value is nil. It's ok if the key is | ||
98 | ** freed or if any object subsequently gets the same address. | ||
99 | ** | ||
100 | ** Not destroying dead keys helps to keep key hash slots stable. This avoids | ||
101 | ** specialization back-off for HREFK when a value flips between nil and | ||
102 | ** non-nil and the GC gets in the way. It also allows safely hoisting | ||
103 | ** HREF/HREFK across GC steps. Dead keys are only removed if a table is | ||
104 | ** resized (i.e. by NEWREF) and xREF must not be CSEd across a resize. | ||
105 | ** | ||
106 | ** The trade-off is that a write barrier for tables must take the key into | ||
107 | ** account, too. Implicitly resurrecting the key by storing a non-nil value | ||
108 | ** may invalidate the incremental GC invariant. | ||
81 | */ | 109 | */ |
82 | 110 | ||
83 | /* -- Common type definitions --------------------------------------------- */ | 111 | /* -- Common type definitions --------------------------------------------- */ |
@@ -136,10 +164,7 @@ typedef const TValue cTValue; | |||
136 | 164 | ||
137 | /* More external and GCobj tags for internal objects. */ | 165 | /* More external and GCobj tags for internal objects. */ |
138 | #define LAST_TT LUA_TTHREAD | 166 | #define LAST_TT LUA_TTHREAD |
139 | |||
140 | #define LUA_TPROTO (LAST_TT+1) | 167 | #define LUA_TPROTO (LAST_TT+1) |
141 | #define LUA_TUPVAL (LAST_TT+2) | ||
142 | #define LUA_TDEADKEY (LAST_TT+3) | ||
143 | 168 | ||
144 | /* Internal object tags. | 169 | /* Internal object tags. |
145 | ** | 170 | ** |
@@ -170,7 +195,7 @@ typedef const TValue cTValue; | |||
170 | #define LJ_TTHREAD (-7) | 195 | #define LJ_TTHREAD (-7) |
171 | #define LJ_TPROTO (-8) | 196 | #define LJ_TPROTO (-8) |
172 | #define LJ_TFUNC (-9) | 197 | #define LJ_TFUNC (-9) |
173 | #define LJ_TDEADKEY (-10) | 198 | /* Unused (-10) */ |
174 | #define LJ_TTAB (-11) | 199 | #define LJ_TTAB (-11) |
175 | #define LJ_TUDATA (-12) | 200 | #define LJ_TUDATA (-12) |
176 | /* This is just the canonical number type used in some places. */ | 201 | /* This is just the canonical number type used in some places. */ |
@@ -689,7 +714,7 @@ static LJ_AINLINE int32_t lj_num2bit(lua_Number n) | |||
689 | /* -- Miscellaneous object handling --------------------------------------- */ | 714 | /* -- Miscellaneous object handling --------------------------------------- */ |
690 | 715 | ||
691 | /* Names and maps for internal and external object tags. */ | 716 | /* Names and maps for internal and external object tags. */ |
692 | LJ_DATA const char *const lj_obj_typename[1+LUA_TUPVAL+1]; | 717 | LJ_DATA const char *const lj_obj_typename[1+LUA_TPROTO+1]; |
693 | LJ_DATA const char *const lj_obj_itypename[~LJ_TNUMX+1]; | 718 | LJ_DATA const char *const lj_obj_itypename[~LJ_TNUMX+1]; |
694 | 719 | ||
695 | #define typename(o) (lj_obj_itypename[itypemap(o)]) | 720 | #define typename(o) (lj_obj_itypename[itypemap(o)]) |
diff --git a/src/lj_opt_fold.c b/src/lj_opt_fold.c index c91f3382..69ade882 100644 --- a/src/lj_opt_fold.c +++ b/src/lj_opt_fold.c | |||
@@ -1351,8 +1351,9 @@ LJFOLDF(fwd_xload) | |||
1351 | /* Write barriers are amenable to CSE, but not across any incremental | 1351 | /* Write barriers are amenable to CSE, but not across any incremental |
1352 | ** GC steps. | 1352 | ** GC steps. |
1353 | ** | 1353 | ** |
1354 | ** The same logic applies to open upvalue references, because the stack | 1354 | ** The same logic applies to open upvalue references, because a stack |
1355 | ** may be resized during a GC step. | 1355 | ** may be resized during a GC step (not the current stack, but maybe that |
1356 | ** of a coroutine). | ||
1356 | */ | 1357 | */ |
1357 | LJFOLD(TBAR any) | 1358 | LJFOLD(TBAR any) |
1358 | LJFOLD(OBAR any any) | 1359 | LJFOLD(OBAR any any) |
diff --git a/src/lj_parse.c b/src/lj_parse.c index 31a70d38..b5bd7baf 100644 --- a/src/lj_parse.c +++ b/src/lj_parse.c | |||
@@ -189,6 +189,7 @@ static BCReg const_gc(FuncState *fs, GCobj *gc, int itype) | |||
189 | lua_State *L = fs->L; | 189 | lua_State *L = fs->L; |
190 | TValue o, *val; | 190 | TValue o, *val; |
191 | setgcV(L, &o, &gc->gch, itype); | 191 | setgcV(L, &o, &gc->gch, itype); |
192 | /* NOBARRIER: the key is new or kept alive. */ | ||
192 | val = lj_tab_set(L, fs->kt, &o); | 193 | val = lj_tab_set(L, fs->kt, &o); |
193 | if (tvisnum(val)) | 194 | if (tvisnum(val)) |
194 | return val->u32.lo; | 195 | return val->u32.lo; |
@@ -206,6 +207,7 @@ static BCReg const_str(FuncState *fs, ExpDesc *e) | |||
206 | /* Anchor string constant to avoid GC. */ | 207 | /* Anchor string constant to avoid GC. */ |
207 | GCstr *lj_parse_keepstr(LexState *ls, const char *str, size_t len) | 208 | GCstr *lj_parse_keepstr(LexState *ls, const char *str, size_t len) |
208 | { | 209 | { |
210 | /* NOBARRIER: the key is new or kept alive. */ | ||
209 | lua_State *L = ls->L; | 211 | lua_State *L = ls->L; |
210 | GCstr *s = lj_str_new(L, str, len); | 212 | GCstr *s = lj_str_new(L, str, len); |
211 | TValue *tv = lj_tab_setstr(L, ls->fs->kt, s); | 213 | TValue *tv = lj_tab_setstr(L, ls->fs->kt, s); |
@@ -1202,6 +1204,7 @@ static GCproto *fs_finish(LexState *ls, BCLine line) | |||
1202 | lua_assert(ls->fs != NULL || ls->token == TK_eof); | 1204 | lua_assert(ls->fs != NULL || ls->token == TK_eof); |
1203 | /* Re-anchor last string token to avoid GC. */ | 1205 | /* Re-anchor last string token to avoid GC. */ |
1204 | if (ls->token == TK_name || ls->token == TK_string) { | 1206 | if (ls->token == TK_name || ls->token == TK_string) { |
1207 | /* NOBARRIER: the key is new or kept alive. */ | ||
1205 | TValue *tv = lj_tab_setstr(ls->L, ls->fs->kt, strV(&ls->tokenval)); | 1208 | TValue *tv = lj_tab_setstr(ls->L, ls->fs->kt, strV(&ls->tokenval)); |
1206 | if (tvisnil(tv)) setboolV(tv, 1); | 1209 | if (tvisnil(tv)) setboolV(tv, 1); |
1207 | } | 1210 | } |
@@ -1346,8 +1349,7 @@ static void expr_table(LexState *ls, ExpDesc *e) | |||
1346 | vcall = 0; | 1349 | vcall = 0; |
1347 | expr_kvalue(&k, &key); | 1350 | expr_kvalue(&k, &key); |
1348 | expr_kvalue(lj_tab_set(fs->L, t, &k), &val); | 1351 | expr_kvalue(lj_tab_set(fs->L, t, &k), &val); |
1349 | if (val.k == VKSTR) | 1352 | lj_gc_anybarriert(fs->L, t); |
1350 | lj_gc_objbarriert(fs->L, t, val.u.sval); | ||
1351 | } else { | 1353 | } else { |
1352 | if (val.k != VCALL) { expr_toanyreg(fs, &val); vcall = 0; } | 1354 | if (val.k != VCALL) { expr_toanyreg(fs, &val); vcall = 0; } |
1353 | if (expr_isk(&key)) expr_index(fs, e, &key); | 1355 | if (expr_isk(&key)) expr_index(fs, e, &key); |
diff --git a/src/lj_record.c b/src/lj_record.c index 206eedca..c13c67fd 100644 --- a/src/lj_record.c +++ b/src/lj_record.c | |||
@@ -994,6 +994,7 @@ static TRef rec_idx(jit_State *J, RecordIndex *ix) | |||
994 | return res; | 994 | return res; |
995 | } else { /* Indexed store. */ | 995 | } else { /* Indexed store. */ |
996 | GCtab *mt = tabref(tabV(&ix->tabv)->metatable); | 996 | GCtab *mt = tabref(tabV(&ix->tabv)->metatable); |
997 | int keybarrier = tref_isgcv(ix->key) && !tref_isnil(ix->val); | ||
997 | if (tvisnil(oldv)) { /* Previous value was nil? */ | 998 | if (tvisnil(oldv)) { /* Previous value was nil? */ |
998 | /* Need to duplicate the hasmm check for the early guards. */ | 999 | /* Need to duplicate the hasmm check for the early guards. */ |
999 | int hasmm = 0; | 1000 | int hasmm = 0; |
@@ -1004,7 +1005,8 @@ static TRef rec_idx(jit_State *J, RecordIndex *ix) | |||
1004 | if (hasmm) | 1005 | if (hasmm) |
1005 | emitir(IRTG(loadop, IRT_NIL), xref, 0); /* Guard for nil value. */ | 1006 | emitir(IRTG(loadop, IRT_NIL), xref, 0); /* Guard for nil value. */ |
1006 | else if (xrefop == IR_HREF) | 1007 | else if (xrefop == IR_HREF) |
1007 | emitir(IRTG(oldv == niltvg(J2G(J)) ? IR_EQ : IR_NE, IRT_PTR), xref, lj_ir_kptr(J, niltvg(J2G(J)))); | 1008 | emitir(IRTG(oldv == niltvg(J2G(J)) ? IR_EQ : IR_NE, IRT_PTR), |
1009 | xref, lj_ir_kptr(J, niltvg(J2G(J)))); | ||
1008 | if (ix->idxchain && rec_mm_lookup(J, ix, MM_newindex)) { /* Metamethod? */ | 1010 | if (ix->idxchain && rec_mm_lookup(J, ix, MM_newindex)) { /* Metamethod? */ |
1009 | lua_assert(hasmm); | 1011 | lua_assert(hasmm); |
1010 | goto handlemm; | 1012 | goto handlemm; |
@@ -1015,6 +1017,7 @@ static TRef rec_idx(jit_State *J, RecordIndex *ix) | |||
1015 | if (tref_isinteger(key)) /* NEWREF needs a TValue as a key. */ | 1017 | if (tref_isinteger(key)) /* NEWREF needs a TValue as a key. */ |
1016 | key = emitir(IRTN(IR_TONUM), key, 0); | 1018 | key = emitir(IRTN(IR_TONUM), key, 0); |
1017 | xref = emitir(IRT(IR_NEWREF, IRT_PTR), ix->tab, key); | 1019 | xref = emitir(IRT(IR_NEWREF, IRT_PTR), ix->tab, key); |
1020 | keybarrier = 0; /* NEWREF already takes care of the key barrier. */ | ||
1018 | } | 1021 | } |
1019 | } else if (!lj_opt_fwd_wasnonnil(J, loadop, tref_ref(xref))) { | 1022 | } else if (!lj_opt_fwd_wasnonnil(J, loadop, tref_ref(xref))) { |
1020 | /* Cannot derive that the previous value was non-nil, must do checks. */ | 1023 | /* Cannot derive that the previous value was non-nil, must do checks. */ |
@@ -1030,11 +1033,13 @@ static TRef rec_idx(jit_State *J, RecordIndex *ix) | |||
1030 | emitir(IRTG(loadop, t), xref, 0); /* Guard for non-nil value. */ | 1033 | emitir(IRTG(loadop, t), xref, 0); /* Guard for non-nil value. */ |
1031 | } | 1034 | } |
1032 | } | 1035 | } |
1036 | } else { | ||
1037 | keybarrier = 0; /* Previous non-nil value kept the key alive. */ | ||
1033 | } | 1038 | } |
1034 | if (tref_isinteger(ix->val)) /* Convert int to number before storing. */ | 1039 | if (tref_isinteger(ix->val)) /* Convert int to number before storing. */ |
1035 | ix->val = emitir(IRTN(IR_TONUM), ix->val, 0); | 1040 | ix->val = emitir(IRTN(IR_TONUM), ix->val, 0); |
1036 | emitir(IRT(loadop+IRDELTA_L2S, tref_type(ix->val)), xref, ix->val); | 1041 | emitir(IRT(loadop+IRDELTA_L2S, tref_type(ix->val)), xref, ix->val); |
1037 | if (tref_isgcv(ix->val)) | 1042 | if (keybarrier || tref_isgcv(ix->val)) |
1038 | emitir(IRT(IR_TBAR, IRT_NIL), ix->tab, 0); | 1043 | emitir(IRT(IR_TBAR, IRT_NIL), ix->tab, 0); |
1039 | /* Invalidate neg. metamethod cache for stores with certain string keys. */ | 1044 | /* Invalidate neg. metamethod cache for stores with certain string keys. */ |
1040 | if (!nommstr(J, ix->key)) { | 1045 | if (!nommstr(J, ix->key)) { |
diff --git a/src/lj_tab.c b/src/lj_tab.c index d77aa05a..5d68cc54 100644 --- a/src/lj_tab.c +++ b/src/lj_tab.c | |||
@@ -192,7 +192,7 @@ GCtab * LJ_FASTCALL lj_tab_dup(lua_State *L, const GCtab *kt) | |||
192 | Node *kn = &knode[i]; | 192 | Node *kn = &knode[i]; |
193 | Node *n = &node[i]; | 193 | Node *n = &node[i]; |
194 | Node *next = nextnode(kn); | 194 | Node *next = nextnode(kn); |
195 | /* Don't use copyTV here, since it asserts on a copy of a DEADKEY. */ | 195 | /* Don't use copyTV here, since it asserts on a copy of a dead key. */ |
196 | n->val = kn->val; n->key = kn->key; | 196 | n->val = kn->val; n->key = kn->key; |
197 | setmref(n->next, next == NULL? next : (Node *)((char *)next + d)); | 197 | setmref(n->next, next == NULL? next : (Node *)((char *)next + d)); |
198 | } | 198 | } |
@@ -448,7 +448,7 @@ TValue *lj_tab_newkey(lua_State *L, GCtab *t, cTValue *key) | |||
448 | n->key.u64 = key->u64; | 448 | n->key.u64 = key->u64; |
449 | if (LJ_UNLIKELY(tvismzero(&n->key))) | 449 | if (LJ_UNLIKELY(tvismzero(&n->key))) |
450 | n->key.u64 = 0; | 450 | n->key.u64 = 0; |
451 | lj_gc_barriert(L, t, key); | 451 | lj_gc_anybarriert(L, t); |
452 | lua_assert(tvisnil(&n->val)); | 452 | lua_assert(tvisnil(&n->val)); |
453 | return &n->val; | 453 | return &n->val; |
454 | } | 454 | } |
@@ -517,9 +517,7 @@ static uint32_t keyindex(lua_State *L, GCtab *t, cTValue *key) | |||
517 | if (!tvisnil(key)) { | 517 | if (!tvisnil(key)) { |
518 | Node *n = hashkey(t, key); | 518 | Node *n = hashkey(t, key); |
519 | do { | 519 | do { |
520 | if (lj_obj_equal(&n->key, key) || | 520 | if (lj_obj_equal(&n->key, key)) |
521 | (itype(&n->key) == LJ_TDEADKEY && tvisgcv(key) && | ||
522 | gcV(&n->key) == gcV(key))) | ||
523 | return t->asize + (uint32_t)(n - noderef(t->node)); | 521 | return t->asize + (uint32_t)(n - noderef(t->node)); |
524 | /* Hash key indexes: [t->asize..t->asize+t->nmask] */ | 522 | /* Hash key indexes: [t->asize..t->asize+t->nmask] */ |
525 | } while ((n = nextnode(n))); | 523 | } while ((n = nextnode(n))); |