Skip to content

Commit db43382

Browse files
committed
fix: trace active-mt local table traversal
1 parent cb43489 commit db43382

4 files changed

Lines changed: 51 additions & 15 deletions

File tree

‎notes/m6.md‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,3 +894,26 @@ M6 active-MT recorder table boundary:
894894
secondary-TG table allocation, shared table-read/write, and `__index`
895895
fallback trace coverage in `t-jit-secondary.lua`;
896896
- `LJ_TEST_DISABLE_BUILD_CACHE=1 tools/ci/lua_test.sh m6_jit_table_store_helper`.
897+
898+
M6 trace-local `next()` recording:
899+
- Narrowed the active-MT `next()` recorder fence to non-trace-local tables.
900+
A table allocated by the same trace through `TNEW`/`TDUP` has no published
901+
pre-existing array/hash generation for another TG to race while the recorder
902+
predicts the key/value result types, so stock trace-local `next(t, nil)`
903+
recording can stay enabled. Non-trace-local shared traversal still raises
904+
NYI under active MT and uses the interpreter path until the traversal-index
905+
contract is helper-backed and generation-safe.
906+
- Added secondary-TG coverage in `tests/t-jit-secondary.lua` for a hot loop
907+
that allocates a fresh table, records `next(t, nil)`, and verifies both the
908+
stock result and an emitted trace while active MT is latched.
909+
- Fixed x64 `asm_tvptr()` temp-slot routing so future IR calls that mark
910+
`IRTMPREF_OUT2` address `tmptv2` instead of aliasing the first output temp.
911+
The current trace-local `next()` path does not need the second output helper,
912+
but this keeps the tempref contract correct for the pending shared-traversal
913+
helper work.
914+
- Verification:
915+
- `LJ_TEST_DISABLE_BUILD_CACHE=1 tools/ci/lua_test.sh m5_tab_resize_stress`;
916+
- `tools/ci/lua_test.sh m5_tab_next_snapshot`;
917+
- `LJ_TEST_DISABLE_BUILD_CACHE=1 tools/ci/lua_test.sh m6_jit_token`;
918+
- `tools/ci/lua_test.sh m6_jit_table_store_helper`;
919+
- `git diff --check`.

‎src/lj_asm_x86.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1239,7 +1239,7 @@ static void asm_tvptr(ASMState *as, Reg dest, IRRef ref, MSize mode)
12391239
#endif
12401240
}
12411241
}
1242-
if (mode & IRTMPREF_IN2)
1242+
if (mode & (IRTMPREF_IN2|IRTMPREF_OUT2))
12431243
emit_leatg(as, dest, tmptv2);
12441244
else
12451245
emit_leatg(as, dest, tmptv);

‎src/lj_record.c‎

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2093,16 +2093,8 @@ int lj_record_next(jit_State *J, RecordIndex *ix)
20932093
{
20942094
IRType t, tkey, tval;
20952095
TRef trvk;
2096-
/*
2097-
** lj_vm_next returns a key/value pair plus the next traversal index, while
2098-
** the recorder predicts result types from the current table generation.
2099-
** Another TG can resize the table between those two observations. Keep
2100-
** stock single-threaded traversal recording, but after MT activation leave
2101-
** traversal to the interpreter until the index/type contract is
2102-
** generation-safe.
2103-
*/
2104-
if (mt_active_acq(J2G(J)))
2105-
lj_trace_err(J, LJ_TRERR_NYIBC);
2096+
if (mt_active_acq(J2G(J)) && !rec_idx_tab_trace_local(J, ix->tab))
2097+
lj_trace_err_info(J, LJ_TRERR_NYIBC);
21062098
t = rec_next_types(tabV(&ix->tabv), ix->keyv.u32.lo);
21072099
tkey = (t & 0xff); tval = (t >> 8);
21082100
trvk = lj_ir_call(J, IRCALL_lj_vm_next, ix->tab, ix->key);

‎tests/t-jit-secondary.lua‎

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,19 +106,40 @@ local worker = th.spawn(function()
106106
local write_traces = trace_count(32)
107107
assert(write_traces > 0)
108108

109+
jit.flush()
110+
jit.opt.start("hotloop=1", "hotexit=1", "-sink")
111+
112+
local function table_next(n)
113+
local s = 0
114+
for i = 1, n do
115+
local t = { only = i }
116+
local key, value = next(t, nil)
117+
assert(key == "only")
118+
s = s + value
119+
end
120+
return s
121+
end
122+
123+
for _ = 1, 20 do
124+
assert(table_next(80) == 3240)
125+
end
126+
local next_traces = trace_count(32)
127+
assert(next_traces > 0)
128+
109129
return root_traces, side_traces, table_traces, read_traces, index_traces,
110-
write_traces, th.current():id()
130+
write_traces, next_traces, th.current():id()
111131
end)
112132

113133
local ok, root_traces, side_traces, table_traces, read_traces, index_traces,
114-
write_traces, tid = worker:join()
115-
assert(ok == true)
134+
write_traces, next_traces, tid = worker:join()
135+
assert(ok == true, tostring(root_traces))
116136
assert(type(root_traces) == "number" and root_traces > 0)
117137
assert(type(side_traces) == "number" and side_traces > root_traces)
118138
assert(type(table_traces) == "number" and table_traces > 0)
119139
assert(type(read_traces) == "number" and read_traces > 0)
120140
assert(type(index_traces) == "number" and index_traces > 0)
121141
assert(type(write_traces) == "number" and write_traces > 0)
142+
assert(type(next_traces) == "number" and next_traces > 0)
122143
assert(tid == worker:id())
123144

124-
print("t-jit-secondary OK: secondary TG records, enters, side-traces, allocates tables, reads/writes shared tables, and preserves __index reads in x64 mcode")
145+
print("t-jit-secondary OK: secondary TG records, enters, side-traces, allocates tables, reads/writes shared tables, records trace-local next(), and preserves __index reads in x64 mcode")

0 commit comments

Comments
 (0)