Skip to content

Commit a1e9846

Browse files
committed
Trace uint32 FFI ccall returns
1 parent d591d86 commit a1e9846

10 files changed

Lines changed: 112 additions & 19 deletions

notes/ffi-callxs-record-gate.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ The sibling `lj_ccall_jit_i64_gpr()` helper traces exact zero-argument signed
1919
64-bit integer returns, preserving stock boxed int64 cdata results. Signed
2020
64-bit argument conversion remains interpreted until the recorder can produce
2121
the exact ABI value without widening the semantics.
22+
`lj_ccall_jit_u32_0()` traces exact zero-argument unsigned 32-bit returns as
23+
Lua numbers, preserving high-bit values without signed truncation. Unsigned
24+
integer argument conversion remains interpreted.
2225
The separate `lj_ccall_jit_{num,flt}_fpr()` helpers trace exact double or float
2326
returns with 0, 1, or 2 same-kind exact FP arguments through the same
2427
native-state bridge.

notes/ffi-ccall-jit-trampoline.md

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
# FFI C-Call JIT Trampoline
22

33
The first traced ordinary FFI C-call slice now records exact void, signed
4-
32-bit integer, pointer-returning, zero-argument signed 64-bit integer, and
5-
FP-returning function cdata on x64. The narrow GPR subset accepts 0, 1, or 2
6-
signed 32-bit integer/pointer arguments, plus exact zero-argument signed 64-bit
7-
integer returns. The FPR subset accepts 0, 1, or 2 same-kind exact float or
8-
double arguments. The recorder emits `lj_ccall_jit_void_gpr()`,
9-
`lj_ccall_jit_i32_gpr()`, `lj_ccall_jit_i64_gpr()`, or
10-
`lj_ccall_jit_ptr_gpr()` plus a tiny signature code for the GPR argument shape,
11-
or `lj_ccall_jit_num_fpr()` / `lj_ccall_jit_flt_fpr()` for the FP-only FPR
12-
shapes.
4+
32-bit integer, pointer-returning, zero-argument unsigned 32-bit/signed 64-bit
5+
integer, and FP-returning function cdata on x64. The narrow GPR subset accepts
6+
0, 1, or 2 signed 32-bit integer/pointer arguments, plus exact zero-argument
7+
unsigned 32-bit and signed 64-bit integer returns. The FPR subset accepts 0, 1,
8+
or 2 same-kind exact float or double arguments. The recorder emits
9+
`lj_ccall_jit_void_gpr()`, `lj_ccall_jit_i32_gpr()`,
10+
`lj_ccall_jit_i64_gpr()`, or `lj_ccall_jit_ptr_gpr()` plus a tiny signature
11+
code for the GPR argument shape, `lj_ccall_jit_u32_0()` for high-bit-safe
12+
unsigned 32-bit results, or `lj_ccall_jit_num_fpr()` /
13+
`lj_ccall_jit_flt_fpr()` for the FP-only FPR shapes.
1314

1415
This does not enable the old direct `IR_CALLXS` path. Each helper is emitted as
1516
a side-effecting `IRCALL` with an implicit `lua_State *`; the recorder converts
@@ -23,6 +24,7 @@ The scope is deliberately narrow:
2324
- exactly 0, 1, or 2 Lua arguments;
2425
- exact signed 32-bit integer or pointer argument types;
2526
- void, exact signed 32-bit integer, or pointer return types;
27+
- zero-argument exact unsigned 32-bit integer returns as Lua numbers;
2628
- zero-argument exact signed 64-bit integer returns;
2729
- same-kind exact float/double arguments and exact float/double returns;
2830
- x64 only;
@@ -32,11 +34,12 @@ The scope is deliberately narrow:
3234

3335
This gives hot `ffi.C.getpid()`, `ffi.C.abs(i)`, small
3436
`int add(int,int)`-style loops, simple pointer-return/pointer-argument loops,
35-
side-effecting `void f(...)` loops, zero-argument signed int64 cdata-result
36-
loops, and FP-only numeric call loops a traced, nonblocking native-state path
37-
without risking the direct backend `IR_CALLXS` register/result ordering. The
38-
full direct bridge still needs x64 lowering that brackets the foreign ABI call
39-
without clobbering argument or result registers.
37+
side-effecting `void f(...)` loops, zero-argument high-bit uint32 result loops,
38+
zero-argument signed int64 cdata-result loops, and FP-only numeric call loops a
39+
traced, nonblocking native-state path without risking the direct backend
40+
`IR_CALLXS` register/result ordering. The full direct bridge still needs x64
41+
lowering that brackets the foreign ABI call without clobbering argument or
42+
result registers.
4043

4144
`tests/t-ffi-ccall-stopreq.c` also heats the shared `sleep_i32` trampoline until
4245
a trace exists, starts the STOPREQ publisher only after that warmup, and catches

notes/ffi-ccall-native-helpers.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ This remains infrastructure for a future direct `IR_CALLXS` bridge. The narrow
3030
signed 32-bit integer, and pointer-returning calls with 0, 1, or 2
3131
integer/pointer arguments through `IRCALL` helpers using this same native-state
3232
protocol. `lj_ccall_jit_i64_gpr()` handles exact zero-argument signed 64-bit
33-
integer returns and preserves boxed int64 cdata results. The sibling
33+
integer returns and preserves boxed int64 cdata results.
34+
`lj_ccall_jit_u32_0()` handles exact zero-argument unsigned 32-bit integer
35+
returns as Lua numbers without high-bit truncation. The sibling
3436
`lj_ccall_jit_{num,flt}_fpr()` helpers trace exact double or float returns with
3537
0, 1, or 2 same-kind exact FP arguments. Broad traced ordinary FFI C calls
3638
remain disabled by `LJ_FFI_RECORD_CALLS=0` because x64 `IR_CALLXS` lowering

notes/m7.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,8 @@ FFI C-call JIT trampoline:
15571557
integer/pointer arguments now records through the
15581558
`lj_ccall_jit_{void,i32,ptr}_gpr()` side-effecting `IRCALL` helpers with an
15591559
implicit `lua_State *` and a small signature code for the GPR argument shape.
1560+
Exact zero-argument unsigned 32-bit integer returns record through
1561+
`lj_ccall_jit_u32_0()` and return Lua numbers, including high-bit values.
15601562
Exact zero-argument signed 64-bit integer returns record through
15611563
`lj_ccall_jit_i64_gpr()` and keep the normal boxed int64 cdata result.
15621564
Exact FP-returning function cdata with 0, 1, or 2 same-kind exact float or
@@ -1565,20 +1567,21 @@ FFI C-call JIT trampoline:
15651567
The helpers enter native state around the foreign call, leave through
15661568
`lj_ccall_native_leave()`, and check fresh STOPREQ before returning zero Lua
15671569
results, a preserved 32-bit integer, a preserved pointer, or a preserved FP
1568-
result, and box zero-argument signed 64-bit integer results as regular int64
1569-
cdata.
1570+
result, return zero-argument unsigned 32-bit integer results as Lua numbers,
1571+
and box zero-argument signed 64-bit integer results as regular int64 cdata.
15701572
- The dormant direct `LJ_FFI_RECORD_CALLS`/`IR_CALLXS` path remains hard-gated;
15711573
varargs, functions with more than two arguments, non-GPR argument/return
15721574
shapes outside the exact signed 32-bit integer/pointer subset, and
15731575
callback-blacklisted functions still fall back to the interpreted ccall path.
15741576
- `tests/t-ffi-ccall-native.lua` now requires `ffi.C.getpid()`, `ffi.C.abs()`,
15751577
shared-library `int add2(int,int)`, shared-library `int sleep_ms(int)`,
1576-
shared-library zero-argument signed-int64 probes, shared-library FP-returning probes,
1578+
shared-library zero-argument unsigned-int32/signed-int64 probes,
1579+
shared-library FP-returning probes,
15771580
shared-library void-returning side-effect probes, and shared-library
15781581
pointer-return/pointer-argument probes to trace, verifies GC does not block
15791582
while the traced sleep helper is native, and keeps a negative assertion that
15801583
unsupported pointer/size multi-argument `poll()` and unsigned 32-bit C-call
1581-
signatures remain off trace.
1584+
signatures with arguments remain off trace.
15821585
- `tests/t-ffi-ccall-stopreq.c` now reuses the shared sleep helper to verify a
15831586
fresh STOPREQ published during a traced trampoline call is delivered through
15841587
the trace snapshot and caught by Lua `pcall()`.

src/lj_ccall.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,19 @@ int32_t lj_ccall_jit_i32_gpr(lua_State *L, void *func, uintptr_t a,
13921392
return ret;
13931393
}
13941394

1395+
double lj_ccall_jit_u32_0(lua_State *L, void *func)
1396+
{
1397+
CTState *cts = ctype_cts(L);
1398+
CCallNativeState native;
1399+
uint32_t actions, ret;
1400+
lj_ccall_native_save(L, &native);
1401+
lj_ccall_native_enter(L, &native, func);
1402+
ret = ((uint32_t (*)(void))(uintptr_t)func)();
1403+
actions = lj_ccall_native_leave(L, cts, &native, func);
1404+
lj_ccall_native_checkstop(L, actions, &native);
1405+
return (double)ret;
1406+
}
1407+
13951408
int64_t lj_ccall_jit_i64_gpr(lua_State *L, void *func, int64_t a,
13961409
int64_t b, uint32_t sig)
13971410
{

src/lj_ccall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ LJ_FUNC void lj_ccall_jit_void_gpr(lua_State *L, void *func,
227227
uintptr_t a, uintptr_t b, uint32_t sig);
228228
LJ_FUNC int32_t lj_ccall_jit_i32_gpr(lua_State *L, void *func,
229229
uintptr_t a, uintptr_t b, uint32_t sig);
230+
LJ_FUNC double lj_ccall_jit_u32_0(lua_State *L, void *func);
230231
LJ_FUNC int64_t lj_ccall_jit_i64_gpr(lua_State *L, void *func,
231232
int64_t a, int64_t b, uint32_t sig);
232233
LJ_FUNC void *lj_ccall_jit_ptr_gpr(lua_State *L, void *func,

src/lj_crecord.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2437,6 +2437,52 @@ static int crec_call_jit_i64_kind(CTInfo info, CTSize size)
24372437
return ctype_isinteger(info) && size == 8 && !(info & CTF_UNSIGNED);
24382438
}
24392439

2440+
static int crec_call_jit_u32_0(jit_State *J, RecordFFData *rd, CTState *cts,
2441+
CType *ct, CTInfo info, GCcdata *cd,
2442+
IRType tp, CTSize fsz)
2443+
{
2444+
CType ctrsnap, ctfcopy;
2445+
CType *ctr, *ctf;
2446+
CTypeID fid;
2447+
CTInfo ctr_info, ctfinfo;
2448+
TRef func;
2449+
MSize narg = 0;
2450+
2451+
if ((info & CTF_VARARG))
2452+
return 0;
2453+
while (J->base[1+narg]) {
2454+
if (narg >= 1)
2455+
return 0;
2456+
narg++;
2457+
}
2458+
if (narg != 0)
2459+
return 0;
2460+
2461+
ctr = crec_ctype_rawchild(J, cts, ct, &ctrsnap);
2462+
ctr_info = ctype_info_acq(ctr);
2463+
if (!ctype_isinteger(ctr_info) || ctype_size_acq(ctr) != 4 ||
2464+
!(ctr_info & CTF_UNSIGNED))
2465+
return 0;
2466+
2467+
fid = ctype_sib_acq(ct);
2468+
while (fid) {
2469+
ctf = crec_ctype_snapshot(J, cts, fid, &ctfcopy);
2470+
ctfinfo = ctype_info_acq(ctf);
2471+
if (!ctype_isattrib(ctfinfo)) break;
2472+
fid = ctype_sib_acq(ctf);
2473+
}
2474+
if (fid)
2475+
return 0;
2476+
2477+
if (lj_ctype_cb_isblacklisted(cts, cdata_getptr(cdataptr(cd), fsz)))
2478+
lj_trace_err(J, LJ_TRERR_BLACKL);
2479+
2480+
func = emitir(IRT(IR_FLOAD, tp), J->base[0], IRFL_CDATA_PTR);
2481+
J->base[0] = lj_ir_call(J, IRCALL_lj_ccall_jit_u32_0, func);
2482+
J->needsnap = 1;
2483+
return 1;
2484+
}
2485+
24402486
static int crec_call_jit_i64_gpr(jit_State *J, RecordFFData *rd, CTState *cts,
24412487
CType *ct, CTInfo info, GCcdata *cd,
24422488
IRType tp, CTSize fsz)
@@ -2611,6 +2657,8 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd)
26112657
return 1;
26122658
if (crec_call_jit_flt_fpr(J, rd, cts, ct, info, cd, tp, fsz))
26132659
return 1;
2660+
if (crec_call_jit_u32_0(J, rd, cts, ct, info, cd, tp, fsz))
2661+
return 1;
26142662
if (crec_call_jit_i64_gpr(J, rd, cts, ct, info, cd, tp, fsz))
26152663
return 1;
26162664
if (crec_call_jit_gpr(J, rd, cts, ct, info, cd, tp, fsz))

src/lj_ircall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ typedef struct CCallInfo {
269269
_(FFI, lj_vm_errno, 0, S, INT, CCI_NOFPRCLOBBER) \
270270
_(FFI, lj_ccall_jit_void_gpr, 5, S, NIL, CCI_L|CCI_T) \
271271
_(FFI, lj_ccall_jit_i32_gpr, 5, S, INT, CCI_L|CCI_T) \
272+
_(FFI, lj_ccall_jit_u32_0, 2, S, NUM, CCI_L|CCI_T) \
272273
_(FFI, lj_ccall_jit_i64_gpr, 5, S, I64, CCI_L|CCI_T) \
273274
_(FFI, lj_ccall_jit_ptr_gpr, 5, S, PTR, CCI_L|CCI_T) \
274275
_(FFI, lj_ccall_jit_num_fpr, 5, S, NUM, CCI_L|CCI_T) \

tests/t-ffi-ccall-jit-lib.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ unsigned int lj_m7_ccall_jit_u32(unsigned int a)
8585
return a + 1u;
8686
}
8787

88+
uint32_t lj_m7_ccall_jit_u32_0(void)
89+
{
90+
return 0xf0000001u;
91+
}
92+
8893
int *lj_m7_ccall_jit_ptr0(void)
8994
{
9095
return lj_m7_ccall_jit_values;

tests/t-ffi-ccall-native.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ int lj_m7_ccall_jit_void_count_i32(void);
1919
void lj_m7_ccall_jit_void0(void);
2020
void lj_m7_ccall_jit_store_i32(int *, int);
2121
unsigned int lj_m7_ccall_jit_u32(unsigned int);
22+
uint32_t lj_m7_ccall_jit_u32_0(void);
2223
int *lj_m7_ccall_jit_ptr0(void);
2324
int lj_m7_ccall_jit_ptr_read_i32(int *);
2425
int lj_m7_ccall_jit_ptr_sum_i32(int *, int *);
@@ -151,6 +152,7 @@ do
151152
local void0 = lib.lj_m7_ccall_jit_void0
152153
local store_i32 = lib.lj_m7_ccall_jit_store_i32
153154
local u32 = lib.lj_m7_ccall_jit_u32
155+
local u32_0 = lib.lj_m7_ccall_jit_u32_0
154156
local ptr0 = lib.lj_m7_ccall_jit_ptr0
155157
local ptr_read_i32 = lib.lj_m7_ccall_jit_ptr_read_i32
156158
local ptr_sum_i32 = lib.lj_m7_ccall_jit_ptr_sum_i32
@@ -235,6 +237,13 @@ do
235237
end
236238
return r
237239
end
240+
local function run_u32_0(n)
241+
local r = 0
242+
for _ = 1, n do
243+
r = r + u32_0()
244+
end
245+
return r
246+
end
238247
local function run_ptr0(n)
239248
local p
240249
for _ = 1, n do
@@ -337,6 +346,11 @@ do
337346
assert(run_u32(80) == 80 * 8)
338347
assert(trace_count() == 0, "unsigned int FFI calls must keep stock semantics off trace")
339348

349+
jit.flush()
350+
jit.opt.start("hotloop=1", "hotexit=1")
351+
assert(run_u32_0(80) == 80 * 0xf0000001)
352+
assert(trace_count() > 0, "shared void->uint32_t FFI call loop should trace")
353+
340354
jit.flush()
341355
jit.opt.start("hotloop=1", "hotexit=1")
342356
assert(run_ptr0(80) == 33)

0 commit comments

Comments
 (0)