Skip to content

perf(interp): faster named calls and looped tail calls - #55

Merged
esrrhs merged 1 commit into
masterfrom
perf/interp-call-speed
Nov 11, 2026
Merged

esrrhs merged 1 commit into
masterfrom
perf/interp-call-speed

Conversation

@esrrhs

@esrrhs esrrhs commented Nov 10, 2026

Copy link
Copy Markdown
Owner

Summary

This PR speeds up interpreter calls in two related ways, landed together:

  1. Direct INTERP call fast paths. Exact-arity INTERP-to-INTERP named and closure calls go through InterpreterExecute / InterpExecuteInContext without copying VmFunction or entering GuardJitEntry / an extra LuaLocScope. Vm::FindFunction(std::string_view) returns a pointer into the function table. CALLNAME / CallByNameArgs take string_view and no longer heap-allocate std::string on the success path (allocation remains on error paths).

  2. Looped tail calls. return f(...) (not parenthesized return (f(...))) emits TAIL / TAILNAME. The common exact-arity, non-vararg, no-box self-tail reuses the current frame in place (inst.c = kTailSelfExact, no name lookup). Mutual recursion, vararg, Multi, JIT/native callees, and no-box→boxed or root-frame growth use a generic fallback and a TLS trampoline so C-stack depth stays one. Normal CALL / CALLNAME do not pay a per-tail setup cost.

Semantics / tests

New fixtures and cases cover self, mutual, vararg, upvalue, multi-return, deep INTERP tails (n=100000), and tail error locations (TailErr.boom line 6, call_missing line 13, including deep INTERP n=20000). File-level functions cannot be reassigned, which is what allows self-TAILNAME to skip the name lookup.

Locally, 562 related unit tests (test_basic, closure, runtime, vm_cvar_call, test_native, algo, jitter, exception) passed under an addr2line bwrap stub used only to keep Boost.Stacktrace throw-heavy tests from hanging on symbolization. This is not a claim that the full suite or CI is green.

Measurements

Comparable Release, same harness, debug_mode=false, min_time=0.3s, 3 sequential reps, median Time ns/call. Three libraries (master at 49c63c7, call-opt alone, and this combined change) via LD_LIBRARY_PATH. No concurrent compile during the run. INTERP TailRec at large n ran in a forked child so C-stack overflow is recorded as a crash rather than killing the driver.

master → this PR (combined call-opt + tail):

  • EmptyCall n=10000 INTERP: 829704 → 559517 ns (1.48×)
  • non-tail Recursion (fib) n=20 INTERP: 1888952 → 1298006 ns (1.45×)

Tradeoff versus call-opt alone (not an unqualified all-path speedup): the combined change is about 4% slower on EmptyCall and about 11% slower on non-tail Recursion than the call-opt-only intermediate (EmptyCall 538246 → 559517; Recursion 1165972 → 1298006). The residual is likely interpreter loop code layout. There is no new public opt-in flag; tail is on by default because of the tail-call wins and stack safety below.

TailRec INTERP (self tail):

  • n=200: call-opt 11457 → 4777 ns (2.4×)
  • n=1000: call-opt 64729 → 23477 ns (2.76×)
  • n=100000: 2449587 ns (~2.45 ms) on this change; master and call-opt overflow the C stack at n=5000.

Notes

Default CompileConfig / debug_mode and unrelated caches are unchanged.

Exact-arity INTERP-to-INTERP named and closure calls go through
InterpreterExecute without copying VmFunction or entering
GuardJitEntry. CALLNAME uses string_view on the success path.

return f(...) emits TAIL/TAILNAME. Common exact-arity no-box self
tails reuse the current frame in place; mutual, vararg, Multi, and
cross-template cases use a generic fallback and a TLS trampoline
so C-stack depth stays one. Tests cover self, mutual, vararg,
upvalue, multi-return, deep tails, and error locations.
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 87.83270% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.73%. Comparing base (49c63c7) to head (ab7a7e8).

Files with missing lines Patch % Lines
src/interp/interpreter.cpp 85.90% 31 Missing ⚠️
src/interp/codegen.cpp 96.00% 1 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #55      +/-   ##
==========================================
- Coverage   89.77%   89.73%   -0.04%     
==========================================
  Files         125      125              
  Lines       27317    27554     +237     
==========================================
+ Hits        24523    24725     +202     
- Misses       2794     2829      +35     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@esrrhs
esrrhs merged commit cc0e0c8 into master Nov 11, 2026
9 checks passed
@esrrhs
esrrhs deleted the perf/interp-call-speed branch November 11, 2026 02:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants