Repository navigation
Commit 1c7759c
authored
Flatten Python eval loop with trampoline for reduced call overhead (#8431)
* Factor with_iframe into enter_iframe/exit_iframe helpers
Extract the frame entry (recursion check, TLS link, exception save)
and exit (materialization sync, TLS restore, GC tracking) logic
from with_iframe into standalone enter_iframe/exit_iframe methods.
with_iframe now calls them, with no behavioral change.
This prepares for the trampoline loop where enter/exit are called
individually rather than wrapped around a closure.
Assisted-by: Claude
* Allocate InterpreterFrame and LocalsPlus together on the datastack
Add InterpreterFrame::new_on_datastack() that bump-allocates both the
InterpreterFrame struct and its LocalsPlus data array in a single
datastack push, eliminating one allocation per function call.
Update datastack_frame_size_bytes_for_code() to include InterpreterFrame
size. Convert invoke_prepared_exact_args() and the invoke() fast path to
use the combined allocation.
Add release_datastack_frame() method on InterpreterFrame that drops all
localsplus values, runs field destructors (trace, temporary_refs,
retained_back, etc.), and returns the datastack base pointer for pop.
Assisted-by: Claude
* Implement trampoline loop for Python-to-Python calls
Add ExecutionResult::TailCall variant and a trampoline in
run_frame_fast that flattens Python-to-Python calls into a single
Rust stack frame instead of recursing through the eval loop.
CallPyExactArgs now prepares the callee frame on the datastack and
returns TailCall when tailcall_enabled is set (run_iframe path only).
The trampoline dispatches via a state machine (EnterCallee / ReturnValue
/ Unwind) in a single loop, avoiding mutual recursion between helper
functions that would exhaust the C stack.
Exception propagation through suspended frames uses
trampoline_handle_exception which adds traceback entries and calls
unwind_blocks on each caller.
Assisted-by: Claude
* Optimize trampoline: skip recursion check, avoid temporary_refs mutex, add bound method TailCall
- Add enter_iframe_unchecked for trampoline callee entry (recursion
already checked by specialization_call_recursion_guard)
- Move callable ownership from per-frame temporary_refs mutex to
trampoline-local SuspendedFrame.owned_refs via VM side channel
- Add TailCall support for CallBoundMethodExactArgs
- Move args directly from caller stack to callee fastlocals
- Read materialized pointer once in exit_iframe
Incremental call overhead: ~55 ns -> ~35 ns
Assisted-by: Claude
* Use UnsafeCell for pending_tailcall_refs
The VM is per-thread so RefCell's runtime borrow checking is
unnecessary overhead. Replace with UnsafeCell for direct access.
Assisted-by: Claude
* Remove .claude/settings.json from tracking
Assisted-by: Claude
* Replace SendPtr with Option<SendNonNull> for type-safe pending tailcall
Use NonNull + Option instead of raw *mut T with manual null checks.
The compiler enforces non-null via the type system, and Option<NonNull>
has the same size as a raw pointer thanks to niche optimization.
Also extract take_pending_tailcall helper to deduplicate the pattern.
Assisted-by: Claude
* Restrict PendingFrame to private, expose only set/take methods
Rename SendNonNull to PendingFrame and make it fully private: the
struct, its field, and the pending_tailcall_frame Cell are all
non-pub. External code accesses the side channel only through
set_pending_tailcall (pub(crate)) and take_pending_tailcall (private).
This ensures the unsafe Send+Sync impl cannot be reused elsewhere
without justifying a new safety argument.
Assisted-by: Claude
* Restore C-stack overflow check in trampoline enter_iframe_unchecked
enter_iframe_unchecked was skipping C-stack checks under the
assumption that the trampoline stays in one Rust stack frame. But
each run_iframe call still consumes Rust stack, so deep Python
recursion through the trampoline can exhaust the C stack (observed
as STATUS_STACK_OVERFLOW on Windows CI).
Keep the C-stack check (every 8th call) while still skipping the
Python recursion depth check (already done by
specialization_call_recursion_guard).
Assisted-by: Claude
* Address code review: entry frame double-free, panic safety, cleanup
- Fix double-free: mark entry frame with is_entry flag in
SuspendedFrame so the trampoline skips its datastack release
(the caller owns that cleanup)
- Clear iframe.previous in exit_iframe before unlinking the chain,
matching with_frame and resume_gen_frame behavior
- Add scopeguard in with_iframe for panic safety
- Use saturating_sub(1) for lasti in trampoline_handle_exception
- Extract datastack_iframe_localsplus_offset helper to avoid
duplicated alignment computation
Assisted-by: Claude1 parent 1fa76a3 commit 1c7759c
6 files changed
Lines changed: 783 additions & 79 deletions
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
67 | | - | |
68 | | - | |
69 | | - | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
| |||
617 | 617 | | |
618 | 618 | | |
619 | 619 | | |
620 | | - | |
621 | | - | |
622 | | - | |
623 | | - | |
624 | | - | |
625 | 620 | | |
626 | 621 | | |
627 | 622 | | |
| |||
634 | 629 | | |
635 | 630 | | |
636 | 631 | | |
637 | | - | |
| 632 | + | |
638 | 633 | | |
639 | 634 | | |
640 | 635 | | |
641 | 636 | | |
642 | | - | |
643 | 637 | | |
644 | 638 | | |
645 | | - | |
| 639 | + | |
646 | 640 | | |
647 | 641 | | |
648 | | - | |
649 | | - | |
| 642 | + | |
| 643 | + | |
650 | 644 | | |
651 | 645 | | |
652 | | - | |
| 646 | + | |
653 | 647 | | |
654 | 648 | | |
655 | 649 | | |
| |||
797 | 791 | | |
798 | 792 | | |
799 | 793 | | |
800 | | - | |
801 | | - | |
802 | | - | |
803 | | - | |
804 | 794 | | |
805 | 795 | | |
806 | 796 | | |
| |||
810 | 800 | | |
811 | 801 | | |
812 | 802 | | |
813 | | - | |
| 803 | + | |
814 | 804 | | |
815 | 805 | | |
816 | 806 | | |
817 | 807 | | |
818 | | - | |
819 | 808 | | |
820 | 809 | | |
821 | | - | |
| 810 | + | |
822 | 811 | | |
823 | 812 | | |
824 | 813 | | |
| |||
829 | 818 | | |
830 | 819 | | |
831 | 820 | | |
832 | | - | |
| 821 | + | |
833 | 822 | | |
834 | | - | |
| 823 | + | |
835 | 824 | | |
836 | 825 | | |
837 | 826 | | |
| |||
887 | 876 | | |
888 | 877 | | |
889 | 878 | | |
890 | | - | |
891 | | - | |
| 879 | + | |
| 880 | + | |
| 881 | + | |
| 882 | + | |
892 | 883 | | |
893 | 884 | | |
894 | 885 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| |||
104 | 105 | | |
105 | 106 | | |
106 | 107 | | |
| 108 | + | |
107 | 109 | | |
108 | 110 | | |
109 | 111 | | |
| |||
0 commit comments