Skip to content

jit: close the FBW inline sub-walk double-apply class (gh#495) + branch-resume local binding fix (gh#498)#513

Merged
youknowone merged 16 commits into
mainfrom
single-walker
Jul 13, 2026
Merged

jit: close the FBW inline sub-walk double-apply class (gh#495) + branch-resume local binding fix (gh#498)#513
youknowone merged 16 commits into
mainfrom
single-walker

Conversation

@youknowone

@youknowone youknowone commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Closes the gh#495 double-apply epic and gh#498, plus regression guards and gated Slice-A infra. 16 commits on main.

gh#495 — FBW inline sub-walk concrete-residual double-apply class

  • Phase 0 floor: decline any non-provably-pure residual executed inside an inline sub-walk (fbw_abort_nested_unjournaled_residual), so concrete heap effects outside the FBW journals can never be re-applied by entry replay.
  • B1 forward-commit chain: FBW_EXECUTED_NONPURE_RESIDUAL latch; const-int/float finish stashes; exception forward-delivery (exit_frame_with_exception, pyjitpl.py:2557 parity); abort-flush of executed-effect nested aborts with caller-CALL-pc + concrete stack overrides (rebuild_from_resumedata resume.py:1042 parity); GC-rooting for the stashes.
  • Parity fixes: clear residual-call exception at ENTRY (execute_varargs pyjitpl.py:1942), drop the decline-mode raise finish stash.
  • 10 synth regression guards (pyre/bench/synth/inline_subwalk_* et al., each # gh#495 guard:).

gh#498 — inlined-closure freevar + branch miscompile

  • Root cause: branch-guard snapshots sourced local Ref slots from the virtualizable shadow first, picking up a stale loop-entry binding for a local overwritten just before the guard (get_list_of_active_boxes reads registers_r[index] — the live color). Fix: prefer the live walk register when the snapshot carries a guard pc.
  • Soundness floor kept: decline bridge reconstruction when a pcdep-declared color is missing from decoded resume liveness (never fill a pcdep-live slot from the vable image).
  • 3 synth guards (closure_freevar_branch_{readonly,nonlocal,list_cell}).

Task #1 (codex-review §3) — ForIterNext exemption audit

  • Disposition: latent, masked — 11 shapes swept with zero default-flag divergence; the exemption's double-advance was reachable only under PYRE_FBW_NESTED_RESID_ABORT=0 on older bases. 2 synth guards (foriter_exempt_*) pin the masking machinery.

Slice A0 (gated OFF)

  • Callee operand-stack mirror for inline sub-walks behind PYRE_FBW_CALLEE_VSTACK (default OFF). A corpus-wide decline census found no reachable target for the flip on the current base, so it stays inert re-evaluation infra (177/177 in both modes).

Verification

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved correctness and recovery for optimized and full-body walk execution across nested calls, mutations, exceptions, generators/iterators, closures, and branch scenarios.
    • Enhanced end-of-walk state resumption for inline-decline paths, including more reliable operand-stack flushing with targeted slot overrides.
    • Strengthened completion handling for mixed integer/float results and added tracking to prevent unsafe non-pure residual execution.
  • Tests

    • Added new benchmark/regression modules covering closure branching, nonlocal behavior, mutation/raise flows, and additional inline sub-walk edge cases.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4e0d3fbe-df68-40db-b0da-0c904d81d7ec

📥 Commits

Reviewing files that changed from the base of the PR and between c268598 and f35ca5f.

📒 Files selected for processing (2)
  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs
  • pyre/pyre-jit-trace/src/trace.rs

Walkthrough

The PR adds regression benchmarks for closure, mutation, iterator, exception, allocation, and nested-callee behavior. It also updates full-body-walk dispatch, vstack reconstruction, inline abort recovery, stack flushing, exception cleanup, GC rooting, and trace commitment logic.

Changes

FBW regression coverage

Layer / File(s) Summary
Closure and return-side-effect benchmarks
pyre/bench/synth/closure_freevar_*.py, pyre/bench/synth/*return_side_effect.py
Adds closure free-variable, list-cell, nonlocal, readonly, integer-return, and float-return benchmarks.
Generator and iterator benchmarks
pyre/bench/synth/foriter_exempt_*.py
Adds shared-generator and nested-for benchmarks that track iterator advancement and mutable counters.
Inline-subwalk mutation and exception benchmarks
pyre/bench/synth/inline_subwalk_*.py, pyre/bench/synth/mutate_*.py, pyre/bench/synth/nested_callee_chain_mutation_abort.py, pyre/bench/synth/or_chain_fresh_alloc_arg.py
Adds benchmarks covering residual mutation, properties, user iterators, reflected addition, exceptions, nested callees, and fresh allocation.
FBW dispatch and concrete resume state
pyre/pyre-jit-trace/src/jitcode_dispatch.rs
Adds centralized exception clearing, non-pure residual tracking, stack-liveness selection, callee vstack mirroring, inline abort metadata, GC rooting, and concrete return-value stashing.
Trace abort and flush commitment
pyre/pyre-jit-trace/src/state.rs, pyre/pyre-jit-trace/src/trace.rs
Adds stack-override-aware state flushing and revises nested-inline and kept-stack abort commitment decisions.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

  • youknowone/pyre#495 — Adds regression benchmarks for the closure free-variable and branch-resume cases covered by this PR.

Possibly related PRs

  • youknowone/pyre#238 — Addresses related multi-frame inline-callee guard-resume state and operand-stack reconstruction.
  • youknowone/pyre#387 — Touches related FOR_ITER and inline-subwalk decline recovery logic.
  • youknowone/pyre#512 — Covers the same closure branch-resume benchmark family.

Poem

I’m a rabbit with registers tucked neat,
Watching resumes hop back on their feet.
Closures and iterators race,
While stack slots return to their place.
Exceptions clear, fresh values appear—
A tidy JIT burrow to cheer!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main fixes in the PR: FBW inline sub-walk double-apply handling and the branch-resume local binding fix.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch single-walker

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.44.1)
pyre/pyre-jit-trace/src/jitcode_dispatch.rs

ast-grep timed out on this file


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit 896a463).

Files in the reviewed diff
pyre/bench/synth/closure_freevar_branch_list_cell.py
pyre/bench/synth/closure_freevar_branch_nonlocal.py
pyre/bench/synth/closure_freevar_branch_readonly.py
pyre/bench/synth/const_int_return_side_effect.py
pyre/bench/synth/float_return_side_effect.py
pyre/bench/synth/foriter_exempt_nested_foriter.py
pyre/bench/synth/foriter_exempt_shared_generator.py
pyre/bench/synth/inline_subwalk_mutating_residual_abort.py
pyre/bench/synth/inline_subwalk_mutating_residual_noexc.py
pyre/bench/synth/inline_subwalk_property_mutates.py
pyre/bench/synth/inline_subwalk_radd_consumed.py
pyre/bench/synth/inline_subwalk_user_iterator.py
pyre/bench/synth/mutate_then_raise_caught.py
pyre/bench/synth/mutate_uncaught_raise_delivery.py
pyre/bench/synth/nested_callee_chain_mutation_abort.py
pyre/bench/synth/or_chain_fresh_alloc_arg.py
pyre/pyre-jit-trace/src/jitcode_dispatch.rs
pyre/pyre-jit-trace/src/state.rs
pyre/pyre-jit-trace/src/trace.rs

1. Regressions to PyPy parity introduced by this patch

  • pyre/pyre-jit-trace/src/trace.rs:2180 ↔ rpython/jit/metainterp/resume.py:1049 — the new BranchGuardKeptStackUnsupported path flushes the walked state unconditionally. The prior code required fbw_foriter_inflight_completed_at_resume(), which verified a completed FOR_ITER header and no uncommitted effects. PyPy resumes only from fully encoded resumedata frames; this change can adopt a partial/non-FOR_ITER state.

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:5915 ↔ rpython/jit/metainterp/pyjitpl.py:2004clear_walk_exception(ctx) now runs before every attempted residual execution, including paths that subsequently decline or are symbolic. PyPy clears at do_residual_call only in the forces/virtualizable branch (pyjitpl.py:2010), after the OS_NOT_IN_TRACE decision; ordinary residual calls do not have this unconditional entry clear.

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:15732 ↔ rpython/jit/metainterp/pyjitpl.py:2004 — the iRd dispatcher also clears before inline-call handling, folds, OS_NOT_IN_TRACE, and force-virtual handling. This broadens the exception-state reset beyond PyPy’s do_residual_call ordering.

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:20962 ↔ rpython/jit/metainterp/pyjitpl.py:2004 — same unconditional pre-dispatch exception clear in the iIRd form.

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:21545 ↔ rpython/jit/metainterp/pyjitpl.py:2004 — same unconditional pre-dispatch exception clear in the iIRFd form.

2. Other mismatches introduced by this patch

None.

3. Pre-existing mismatches (already present before this patch)

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:5942 ↔ rpython/jit/metainterp/pyjitpl.py:2019 — Pyre can decline residual execution when the walk is not the “authoritative executor”; PyPy concretely executes the residual call while tracing. This gate predates the patch.

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:6017 ↔ rpython/jit/metainterp/pyjitpl.py:2025 — Pyre declines calls based on a 47-bit function-address heuristic, whereas PyPy executes through its CPU executor. This is an existing Rust runtime-address workaround.

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:23508 ↔ rpython/jit/metainterp/pyjitpl.py:2503 — normal int_return does not clear last_exc_value; PyPy’s finishframe() clears it before popping the frame. The same pre-existing omission applies to the other normal-return arms.

4. Structural adaptations

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:7510 ↔ rpython/jit/metainterp/resume.py:1049 — thread-local abort PC/stack-override storage plus explicit GC rooting replaces PyPy’s GC-managed MIFrame/resumedata frame state. This is Rust/GC-lifetime machinery, not a direct RPython container equivalent.

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:9792 ↔ rpython/jit/metainterp/pyjitpl.py:177 — the optional callee operand-stack mirror reconstructs stack state from CPython bytecode metadata and liveness tables. PyPy directly snapshots each MIFrame.registers_*; the adaptation is driven by CPython-compatible bytecode and generated JitCode layout.

  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs:6321 ↔ rpython/jit/metainterp/pyjitpl.py:2019 — nested inline walks conservatively abort before non-provably-pure residual calls, while PyPy executes them inside its ordinary per-frame meta-interpreter. This preserves interpreter fallback semantics but is not a 1:1 tracing behavior.

  • pyre/pyre-jit-trace/src/state.rs:3776 ↔ rpython/jit/metainterp/pyjitpl.py:3453 — explicit flushing of virtualizable locals/operand-stack overrides into a CPython PyFrame substitutes for PyPy’s synchronize_virtualizable() after resumedata reconstruction.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 844c82ee87

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// kill-switch OFF the stash is ignored and Terminate
// aborts; the direct record preserves the legacy
// decline shape.
fbw_finish_concrete_exc_set(exc, ctx.last_exc_value_concrete);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve bubbled exception concrete for no-replay exits

When an uncaught SubRaise bubbles out of an inlined callee, the caller only receives the concrete exception in the exc_concrete payload; the caller ctx.last_exc_value_concrete is not seeded unless the exception is caught in this frame. With PYRE_FBW_RAISE_NO_REPLAY on, using the caller context here leaves the exception stash empty or stale, so a valid uncaught callee exception maps to Terminate::NoFinishPayload/legacy replay instead of compiling and returning the exception no-replay path.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 199f49f36c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +8689 to +8692
let stack_overrides = FBW_ABORT_OUTER_STACK_OVERRIDES.with(|c| {
let mut slots = c.borrow_mut();
std::mem::take(&mut *slots)
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep stack overrides rooted until after the flush

In the nested-inline abort path, this take removes call_stack_overrides from FBW_ABORT_OUTER_STACK_OVERRIDES, which is the only place the new root walker visits them, and then run_perfn_walk passes the returned Vec into flush_walk_end_state_to_frame_with_stack_overrides. During that flush, boxing any Int/Float local before the operand-stack slots can allocate and trigger GC; because the override refs are now only in an untracked Rust Vec, nursery objects in the CALL stack can move without the overrides being updated, so the flush may write stale PyObjectRef pointers into the live frame. Keep the overrides in a rooted cell until after the flush (or add a scoped root for the local vector).

Useful? React with 👍 / 👎.

Generalize the pre-call residual decline from write-tagged residuals to every residual that is not provably side-effect-free. The existing helper limits the gate to inline sub-walks, while writes_live_heap remains available for the FOR_ITER body-effect candidate path.

Assisted-by: Claude
Forward-commit nested inline residual-decline aborts after the walk has already executed a non-pure residual. Stash the top-level caller CALL pc and concrete call stack at inline-capture time, then use that outer coordinate for the abort flush instead of mapping the callee error pc through the outer jitcode tables.

Assisted-by: Claude
Move the walk exception clear to residual-call entry before any dispatcher fold, symbolic decline, or concrete helper execution. This matches execute_varargs semantics and prevents a handled exception from surviving into a later linear catch_exception/L.

Remove the catch_exception/L clear-and-continue workaround so an active exception there is again a strict invariant error. The success-arm clear was also removed because residual-call entry now performs the same WalkContext reset before every residual-call path; the existing success arm did not reset BH_LAST_EXC_VALUE, so there was no BH state to mirror.

Assisted-by: Claude
Add synth parity oracles for gh#495 covering inline subwalk mutation with abort/no-exception paths, property mutation, reflected-add consumption, user-iterator mutation, nested callee mutation, caught and uncaught mutating raises, and return-value side effects for float and constant-int callees.

Assisted-by: Claude
Add gh#498 synthetic parity cases for a closure freevar read, a nonlocal mutation, and a list-cell mutation feeding the same branch-resume local binding shape.

Assisted-by: Claude
The preferred source is get_list_of_active_boxes's registers_r[index]
(pyjitpl.py:222); the pcdep ownership proof bounds it to where the pyre
register read means the same thing. PYRE_FBW_STACK_LIVEREG=0 restores
the shadow-first order.

Assisted-by: Claude
A top-level FBW portal walk that concretely ran a FOR_ITER body's
residual effects before aborting with BranchGuardKeptStackUnsupported
had no forward-flush leg in run_perfn_walk: committed stayed false and
legacy snapshot-replay re-ran the walked iteration, double-applying the
executed __next__ effects.

Extend the PYRE_FBW_BRANCH_FLUSH or-pattern to also match
BranchGuardKeptStackUnsupported. That variant aborts after the consumed
item's body ran, so the walk's symbolic shadow end state already counted
the iteration; flush it with flush_walk_end_state_to_frame without
re-delivering the in-flight item. BranchGuardUnrestorableKeptStackPermanent
keeps its Shape-A in-flight item delivery.

Assisted-by: Claude
Prefer a real color-indexed Ref-bank value when a force-alived portal red
is present in liveness with no semantic frame slot. Fall back to the named
red field, then CONST_NULL, so top-level finish snapshots do not panic on
dead-or-unseeded red fields.

Add a synth regression covering a three-term heap-loaded or chain with a
freshly allocated loop argument.

Re-applies the collect_outer_active_boxes portion of the pre-rebase change
without its exception-stash match arm reflow (that stash was retired in
favour of the upstream FinishConcrete::Raise disposition).

Assisted-by: Claude

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1967c060ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// false, and rollback would miss that concrete mutation. The helper no-ops
// on an empty session framestack, so top-level depth-1 behavior is unchanged.
if !provably_side_effect_free {
fbw_abort_nested_unjournaled_residual(ctx, op_pc)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move nested abort before arming the vable token

When this new inline-subwalk decline fires for a may-force residual, the code has already run tracing_before_residual_call above and set the live virtualizable's token to TOKEN_TRACING_RESCALL; returning here skips the matching tracing_after_residual_call cleanup. In an inlined callee that hits a non-pure CallMayForce* residual, the walk aborts with the frame token still armed, so the next residual/force path can trip the non-NONE token invariant or mis-handle the virtualizable. Move this abort check before the token pre-call setup, or explicitly clear the token on this error path.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyre/pyre-jit-trace/src/jitcode_dispatch.rs`:
- Around line 6322-6329: Before the early return in the
!provably_side_effect_free branch, restore the saved virtualizable heap pointer
and run tracing_after_residual_call to clear TOKEN_TRACING_RESCALL when
vable_obj_ptr is set. Then invoke fbw_abort_nested_unjournaled_residual(ctx,
op_pc)?, preserving the existing abort behavior while ensuring live-frame state
is restored first.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 11a7f1e6-0cd0-4038-8c2b-bd18508be395

📥 Commits

Reviewing files that changed from the base of the PR and between 199f49f and 1967c06.

📒 Files selected for processing (19)
  • pyre/bench/synth/closure_freevar_branch_list_cell.py
  • pyre/bench/synth/closure_freevar_branch_nonlocal.py
  • pyre/bench/synth/closure_freevar_branch_readonly.py
  • pyre/bench/synth/const_int_return_side_effect.py
  • pyre/bench/synth/float_return_side_effect.py
  • pyre/bench/synth/foriter_exempt_nested_foriter.py
  • pyre/bench/synth/foriter_exempt_shared_generator.py
  • pyre/bench/synth/inline_subwalk_mutating_residual_abort.py
  • pyre/bench/synth/inline_subwalk_mutating_residual_noexc.py
  • pyre/bench/synth/inline_subwalk_property_mutates.py
  • pyre/bench/synth/inline_subwalk_radd_consumed.py
  • pyre/bench/synth/inline_subwalk_user_iterator.py
  • pyre/bench/synth/mutate_then_raise_caught.py
  • pyre/bench/synth/mutate_uncaught_raise_delivery.py
  • pyre/bench/synth/nested_callee_chain_mutation_abort.py
  • pyre/bench/synth/or_chain_fresh_alloc_arg.py
  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs
  • pyre/pyre-jit-trace/src/state.rs
  • pyre/pyre-jit-trace/src/trace.rs

Comment on lines +6322 to +6329
// Inside an inline sub-walk, decline before any residual that is not
// provably side-effect-free. Ref-result getters/dunders/user `__next__`
// can mutate live heap through user frames while `writes_live_heap` is
// false, and rollback would miss that concrete mutation. The helper no-ops
// on an empty session framestack, so top-level depth-1 behavior is unchanged.
if !provably_side_effect_free {
fbw_abort_nested_unjournaled_residual(ctx, op_pc)?;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="pyre/pyre-jit-trace/src/jitcode_dispatch.rs"

echo "== Relevant line ranges =="
sed -n '6200,6425p' "$FILE" | cat -n

echo
echo "== Search for token/restore helpers =="
rg -n "tracing_before_residual_call|tracing_after_residual_call|saved_vable_heap_ptr|set_virtualizable_heap_ptr|fbw_abort_nested_unjournaled_residual|LoopBearingCalleeInlineUnsupported|VableEscapedDuringResidualCall|provably_side_effect_free" "$FILE"

echo
echo "== Nearby control-flow contexts =="
sed -n '6228,6410p' "$FILE" | cat -n

echo
echo "== Definitions/usages of residual-call helpers in repo =="
rg -n "fn .*tracing_before_residual_call|fn .*tracing_after_residual_call|struct .*Virtualizable|saved_vable_heap_ptr|fbw_abort_nested_unjournaled_residual" pyre/pyre-jit-trace/src

Repository: youknowone/pyre

Length of output: 38531


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="pyre/pyre-jit-trace/src/jitcode_dispatch.rs"

echo "== fbw_abort_nested_unjournaled_residual definition =="
sed -n '8912,8965p' "$FILE" | cat -n

echo
echo "== Other nested-abort call sites for comparison =="
sed -n '13295,13350p' "$FILE" | cat -n
echo
sed -n '16190,16240p' "$FILE" | cat -n
echo
sed -n '21380,21630p' "$FILE" | cat -n

echo
echo "== Search for virtualizable cleanup in abort paths =="
rg -n "set_virtualizable_heap_ptr\\(|tracing_after_residual_call\\(|TOKEN_TRACING_RESCALL|TOKEN_NONE|virtualizable_heap_ptr\\(" pyre/pyre-jit-trace/src/jitcode_dispatch.rs pyre/pyre-jit-trace/src/trace_opcode.rs pyre/pyre-jit-trace/src/*.rs

Repository: youknowone/pyre

Length of output: 28666


Restore the virtualizable state before aborting nested inline residuals
When vable_obj_ptr is set, this early ? skips both set_virtualizable_heap_ptr(saved_vable_heap_ptr...) and tracing_after_residual_call, leaving TOKEN_TRACING_RESCALL stranded on the live frame. That can trip the next residual-call token assert. Clear the token and restore the saved heap pointer before aborting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyre/pyre-jit-trace/src/jitcode_dispatch.rs` around lines 6322 - 6329, Before
the early return in the !provably_side_effect_free branch, restore the saved
virtualizable heap pointer and run tracing_after_residual_call to clear
TOKEN_TRACING_RESCALL when vable_obj_ptr is set. Then invoke
fbw_abort_nested_unjournaled_residual(ctx, op_pc)?, preserving the existing
abort behavior while ensuring live-frame state is restored first.

Forward the nested-inline abort outer-frame stash
(FBW_ABORT_OUTER_STACK_OVERRIDES) and the active walk session's
InlineParentFrame.call_stack_overrides slots as GC roots through the
captured FbwStoreJournalRootArea, matching the quiesced-area model. Both
stashes hold nursery-resident PyObjectRefs across residual-call
allocations and are populated by default-on paths
(PYRE_FBW_NESTED_RESID_ABORT, PYRE_FBW_INLINE_MULTIFRAME).

Assisted-by: Claude

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pyre/pyre-jit-trace/src/jitcode_dispatch.rs`:
- Around line 8958-8964: The shared borrow created by
fbw_abort_outer_stack_overrides_with must not remain active during GC-capable
work. In pyre/pyre-jit-trace/src/jitcode_dispatch.rs:8958-8964, copy or
otherwise materialize the override values within a short borrow, release it,
then invoke the callback; apply the same borrow-lifetime separation at the
sibling site pyre/pyre-jit-trace/src/jitcode_dispatch.rs:9042-9047 and
pyre/pyre-jit-trace/src/trace.rs:2100-2110, ensuring
flush_walk_end_state_to_frame_with_stack_overrides runs only after the override
collection is no longer borrowed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3df0d584-bbfc-4265-8333-50d20bfb8615

📥 Commits

Reviewing files that changed from the base of the PR and between 1967c06 and c268598.

📒 Files selected for processing (2)
  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs
  • pyre/pyre-jit-trace/src/trace.rs

Comment thread pyre/pyre-jit-trace/src/jitcode_dispatch.rs
…eclines

Abort override rooting: fbw_abort_outer_resume_take now returns only the
resume pc. The outer-caller stack overrides stay in
FBW_ABORT_OUTER_STACK_OVERRIDES (rooted by the #447 area walker) and are
read in place via fbw_abort_outer_stack_overrides_with during the
walk-end flush, then cleared with fbw_abort_outer_stack_overrides_clear.
The flush boxes Int/Float locals, an allocation that can move the
nursery-resident override refs; reading from the rooted cell lets a minor
collection forward the very refs the flush writes.

Residual vable token: move the tracing_before_residual_call arm from
before the decline gates to after fbw_abort_nested_unjournaled_residual,
so a residual declined by the inline-subwalk gate no longer strands
TOKEN_TRACING_RESCALL on the active virtualizable. tracing_before now
pairs with tracing_after only for residuals that proceed.

Assisted-by: Claude
@youknowone youknowone merged commit dd2f964 into main Jul 13, 2026
9 checks passed
@youknowone youknowone deleted the single-walker branch July 13, 2026 13:53

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f35ca5f966

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

opref: OpRef,
) -> Option<pyre_object::PyObjectRef> {
match ctx.trace_ctx.concrete_of_opref(opref) {
Some(Value::Ref(r)) => Some(r.as_usize() as pyre_object::PyObjectRef),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject NO_CONCRETE stack override refs

When the caller's CALL stack contains a ref whose concrete value is only the GcRef::NO_CONCRETE sentinel (for example, a freshly allocated argument), this helper converts that sentinel to a PyObjectRef. The nested-inline abort flush then treats the override as complete and writes the sentinel into locals_cells_stack_w instead of declining, so the interpreter resumes with an invalid object pointer. Filter GcRef::NO_CONCRETE here (and in the color helper) the same way other concrete_of_opref callers do.

Useful? React with 👍 / 👎.

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.

1 participant