Skip to content

jit: pc_map read-cutover B–D + vestigial py_pc cleanup; begin backxlat reentry-inverse deletion (jitcode-blackhole Slice 1) - #709

Merged
youknowone merged 7 commits into
mainfrom
rewrite-tracer
Jul 22, 2026
Merged

jit: pc_map read-cutover B–D + vestigial py_pc cleanup; begin backxlat reentry-inverse deletion (jitcode-blackhole Slice 1)#709
youknowone merged 7 commits into
mainfrom
rewrite-tracer

Conversation

@youknowone

@youknowone youknowone commented Jul 21, 2026

Copy link
Copy Markdown
Owner

pc_map read-cutover — slices B–D + vestigial py_pc cleanup

Continues the #366 layer-3 / #73 work of retiring the surviving py_pc-keyed
depth_at_py_pc() reads in the JIT tracer, cutting each seam over to the
jitcode-pc-keyed "twin" tables built at finalize_jitcode. With these slices,
every depth_at_py_pc read in the tracer is retired and the remaining
vestigial py_pc plumbing is removed.

Depth-read cutover

Each twin reproduces the py_pc-derived depth by construction (same static
depth_at_py_pc source, same keys), verified byte-identical per slice via a
PYRE_PCMAP_*_AUDIT assert-equality gate + audit-OFF byte-identity before this
rebase, and re-verified green on the current base.

Vestigial py_pc plumbing removal

  • 1502d8a: drop collect_outer_active_boxes's dead entry_py_pc
    (panic-string only) and guard_py_pc: Option<u32>guard_present: bool
    params, plus two now-unused backxlat_py_pc inversions.
  • 98334a0: drop the last two python_pc_for_jitcode_pc inversions —
    CalleeAbortPermanentHit.marker_py_pc (debug-log only) and guard_py_pc
    has_branch_guard in the guard-snapshot capture.

After these, no byte-identical-removable py_pc plumbing remains: every surviving
py_pc use is load-bearing or the structural backxlat_py_pc interpreter-reentry
inverse (pyre's resume interp runs Python bytecode, so it needs the jitcode→py
inverse at re-entry — a difference from PyPy's blackhole, which runs jitcode
directly). Deleting that inverse is a separate "jitcode-blackhole" epic (make the
resume/reentry interp itself run jitcode), out of scope here.

jitcode-blackhole epic — Slice 1

With the read-cutover done, the only remaining py_pc use is the structural
backxlat_py_pc jitcode→py reentry inverse. pyre already has a working,
PyPy-orthodox jitcode-blackhole (majit-metainterp/src/blackhole.rs +
resume::blackhole_from_resumedata, jitcode-coordinate, no backxlat); the
inverse survives only via the bridge-trace resume path. There are exactly two
backxlat_py_pc reentry consumers, and the first is dead:

  • afdf9e4: delete the write-only PyreJitState.resume_pc field.
    rebuild_typed_from_rd_numb derived an outer-frame py pc via backxlat_py_pc
    and stored it into resume_pc, which had zero readers repo-wide (its doc
    comment's restore_guard_failure_values consumer was never wired). Deletes the
    field, both writers, all initializers, and the dead computation — dropping the
    Option<usize> from the function's return tuple. Removes one of the two
    backxlat_py_pc call sites. (PyPy stores no such field — the resume pc is a
    local threaded forward, never persisted or computed backward.)

Verification

  • cargo check -p pyre-jit-trace --features dynasm: clean (only the pre-existing
    vstack_mirror.rs:168 unreachable-pattern warning).
  • python3 pyre/check.py --backend dynasm,cranelift: dynasm 246/246 +
    cranelift 246/246.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@youknowone, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 17 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 437af191-f286-4bf9-a7f5-362236438590

📥 Commits

Reviewing files that changed from the base of the PR and between 98ca568 and dbb2fb0.

📒 Files selected for processing (13)
  • pyre/pyre-jit-trace/src/jitcode_dispatch/bridge_subwalk.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/diag.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/inline_call.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/resume_snapshot.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/specialize.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/vstack_mirror.rs
  • pyre/pyre-jit-trace/src/pyjitcode.rs
  • pyre/pyre-jit-trace/src/state.rs
  • pyre/pyre-jit-trace/src/trace.rs
  • pyre/pyre-jit-trace/tests/multi_frame_restore_supported.rs
  • pyre/pyre-jit/src/eval.rs
  • pyre/pyre-jit/src/jit/codewriter.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rewrite-tracer

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 21, 2026

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit dbb2fb0).
Updated: 2026-07-22T08:05:34.699Z

Files in the reviewed diff
pyre/pyre-jit-trace/src/jitcode_dispatch/bridge_subwalk.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/diag.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/inline_call.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/resume_snapshot.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/specialize.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/vstack_mirror.rs
pyre/pyre-jit-trace/src/pyjitcode.rs
pyre/pyre-jit-trace/src/state.rs
pyre/pyre-jit-trace/src/trace.rs
pyre/pyre-jit-trace/tests/multi_frame_restore_supported.rs
pyre/pyre-jit/src/eval.rs
pyre/pyre-jit/src/jit/codewriter.rs

1. Regressions to PyPy parity introduced by this patch

None.

2. Other mismatches introduced by this patch

None.

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

  • pyre/pyre-jit-trace/src/state.rs:9701 ↔ rpython/jit/metainterp/resume.py:1049: Pyre’s push_caller_frame unconditionally returns true while discarding the caller’s frame values, PC, and JitCode index. PyPy rebuilds a distinct frame for every encoded resume-data header (newframe(jitcode), setup_resume_at_op(pc), then consume_boxes). Thus multi-frame guard failure cannot preserve caller-frame identity in Pyre; this was already present on upstream/main.

4. Structural adaptations

  • pyre/pyre-jit/src/jit/codewriter.rs:13502 ↔ rpython/jit/codewriter/jitcode.py:66: Pyre adds JitCode-PC-indexed liveness-depth side tables, whereas PyPy resolves liveness from emitted -live- bytecode using JitCode.get_live_vars_info. This is a CPython-bytecode/compiler-coordinate adaptation, not a semantic divergence; the new tables preserve the former Python-PC liveness query.

  • pyre/pyre-jit-trace/src/pyjitcode.rs:579 ↔ rpython/jit/codewriter/jitcode.py:82: depth_containing_for_jitcode_pc uses Rust partition_point over generated metadata rather than PyPy’s bytecode-offset/liveness decoder. The patch retains floor-coordinate semantics and is required because Pyre’s JitCode offsets are not RPython bytecode PCs.

  • pyre/pyre-jit/src/eval.rs:8795 ↔ rpython/jit/metainterp/blackhole.py:312: the removed PyreJitState.resume_pc cache had no reads; Pyre now relies on the decoded per-frame resume-data coordinates instead of retaining a mutable blackhole position field. This removal does not itself change behavior.

@youknowone youknowone changed the title jit: pc_map read-cutover B–D — retire remaining depth_at_py_pc reads onto jitcode-pc twins + vestigial py_pc cleanup jit: pc_map read-cutover B–D + vestigial py_pc cleanup; begin backxlat reentry-inverse deletion (jitcode-blackhole Slice 1) Jul 21, 2026

@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: afdf9e452d

ℹ️ 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".

@@ -1907,12 +1907,6 @@ pub use crate::liveness::{LiveVars, liveness_for};
pub struct PyreJitState {
#[vable(frame)]
pub frame: usize,

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 Update PyreJitState test instantiations

Removing resume_pc here leaves pyre/pyre-jit-trace/tests/multi_frame_restore_supported.rs still constructing PyreJitState { frame: 0, resume_pc: None }, so the required cargo test --features dynasm path will fail to compile that integration test with an unknown-field error before any tests run. Update the integration test (or keep a compatibility field) in the same change so the required test suite can build.

AGENTS.md reference: AGENTS.md:L200-L200

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: 493e626e32

ℹ️ 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 +13748 to +13749
depth_after_residual_pred_by_jit_pc
.push((pos, static_depth.get(ft_rc).copied()));

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 Deduplicate after-residual depth keys

When two Python PCs share the same first_jit_pc_by_py_pc offset, this new depth twin keeps both duplicate keys and later sorts them unstably, unlike the floor table just above that collapses duplicate offsets with the later py winning. depth_after_residual_for_jitcode_pc(op.pc) exact-searches by that offset, so an after-residual guard whose call starts at a shared offset can read the earlier py's fallthrough depth instead of the same py that python_pc_for_jitcode_pc would resolve, sizing the vstack/snapshot to the wrong stack depth; publish this table with the same last-wins deduplication before binary search.

AGENTS.md reference: AGENTS.md:L14-L18

Useful? React with 👍 / 👎.

The operand-stack mirror's exception-handler entry
(`vstack_enter_exception_handler`) and the resume-frame coordinate
(`vstack_coordinate_for_jitcode_pc`) read the value-stack depth by
inverting a jitcode pc through `vstack_containing_py_pc` and indexing
the py_pc-keyed static-liveness `depth_at_py_pc`.

`vstack_containing_py_pc` is a pure floor lookup over
`py_floor_by_jit_pc` — no block-head marker precedence, no trivia — so
the existing marker-baking depth twins cannot reproduce it. Build a new
floor-only twin `depth_containing_by_jit_pc` at `finalize_jitcode`,
sharing `py_floor_by_jit_pc`'s keys exactly with each value the
static-liveness depth at that segment's py, and read it through
`PyJitCode::depth_containing_for_jitcode_pc`. A `partition_point` floor
pivot identical to `floor_segment_for_jitcode_pc` makes the twin equal
`depth_at_py_pc[vstack_containing_py_pc(jit_pc)]` (including the
out-of-range -> 0 collapse) for every jit_pc.

Both seams gate on `depth_containing_populated()`; skeleton / fixture
installs (empty twin) keep the raw py_pc-keyed read, so the result is
byte-identical. `PYRE_PCMAP_CONTAINING_AUDIT` asserts twin == raw at
both seams; off in production.

Assisted-by: Claude
The inline-caller-frame resume (`compute_inline_caller_frame`,
`compute_nested_inline_caller_frame`) and the self-recursive
call-assembler vstack rebuild (`try_walker_call_assembler_self_recursive`)
read the value-stack depth at the after-residual-call fallthrough pc by
inverting a jitcode pc to a python pc and indexing the py_pc-keyed
static-liveness `depth_at_py_pc`.

Build a jitcode-pc-keyed twin `depth_after_residual_by_jit_pc` at
`finalize_jitcode`, extending the existing `result_color_after_residual`
construction loops: for each block-head / op-start offset, bake the
static-liveness depth at `semantic_fallthrough_pc(py)` for the raw
resolving py (no trivia skip). Read it through
`PyJitCode::depth_after_residual_for_jitcode_pc`, resolved with the same
exact-marker / predecessor-op-start tiers as
`after_residual_marker_for_jitcode_pc`, so it equals
`depth_at_py_pc[semantic_fallthrough_pc(python_pc_for_jitcode_pc(jit_pc))]`
by construction.

The three seams gate on `depth_after_residual_populated()` and keep the
raw py_pc-keyed read as the skeleton / fixture fallback, so the result is
byte-identical. `PYRE_PCMAP_AFTERRESIDUAL_AUDIT` asserts twin == raw at
each seam; off in production. The branched multi-frame vsd read
(`walker_capture_multi_frame_inline_snapshot`) is left for a follow-up.

Assisted-by: Claude
`walker_capture_multi_frame_inline_snapshot` published the outermost
caller's `valuestackdepth` scalar by inverting the paused-parent resume
coordinate to a python pc (`resolve_parent_resume_py_pc`) and indexing
the py_pc-keyed static-liveness `depth_at_py_pc`.

Read the depth off the jitcode-pc twins instead, branching on the parent
resume coordinate: `Backxlat` reads `depth_trivia_for_jitcode_pc`,
`CallFallthrough` reads `depth_after_residual_for_jitcode_pc`. The raw
read resolves the py against `outer.jitcode_index` but indexes the
caller frame's own liveness, so the twin cut is gated on
`outer.jitcode_index == jc.index`; a cross-jitcode parent keeps the raw
read. The `None` depth arm stays `valuestackdepth`, so the twin's
`Option` is pattern-matched rather than defaulted. Byte-identical:
`PYRE_PCMAP_AFTERRESIDUAL_AUDIT` asserts twin == raw at the seam.

Completes the after-residual / multiframe depth read-cutover; only the
py-only params and deliberate `None`-arm fallbacks remain py_pc-keyed.

Assisted-by: Claude
`step_vstack_mirror` and `seed_vstack_mirror` published the operand-stack
mirror depth by inverting a jitcode pc to a python pc
(`vstack_step_py_pc` / `vstack_initial_py_pc`) and indexing the
py_pc-keyed static-liveness `depth_at_py_pc`.

Read the depth off the jitcode-pc twins instead. `step_vstack_mirror`
reads the floor-only `depth_containing_for_jitcode_pc`: its depth is
consumed only when the walk crosses an opcode boundary, where
`vstack_step_py_pc` returns the floor segment py, so the floor twin
reproduces the raw read (the block-head-marker branch returns the
current py and the value is dead). `seed_vstack_mirror` mirrors
`vstack_initial_py_pc`'s branch: the floor twin for a normal entry, and
a new exact-match block-head depth twin `depth_block_head_by_jit_pc`
(same keys as `block_head_py_by_jit_pc`, same static-liveness source) for
a permuted FOR_ITER entry, falling back to the floor when the start pc is
not a marker offset. Both seams keep the raw read when the twin is
unpopulated (skeleton / fixture). Byte-identical:
`PYRE_PCMAP_CONTAINING_AUDIT` asserts twin == raw at each seam.

Retires the last py_pc-keyed `depth_at_py_pc` reads in the vstack mirror.

Assisted-by: Claude
`collect_outer_active_boxes` carried two Python-pc parameters that no
longer key any table: `entry_py_pc` fed only a diagnostic panic string,
and `guard_py_pc: Option<u32>` was read only as a presence flag (its
`Some(gpc)` binding was never used — the body resolves everything through
`carried_jitcode_pc`). Every live table read is already keyed by
`carried_jitcode_pc` / `entry_jitcode_pc`.

Drop `entry_py_pc` (the panic string now reports `entry_jitcode_pc`) and
replace `guard_py_pc` with `guard_present: bool`. The four entry-capture
call sites pass `false`; the guard-capture site passes
`guard_py_pc.is_some()`. Two now-unused `backxlat_py_pc` inversions that
existed only to compute the dropped argument — `root_py_pc` in
`compute_bridge_root_parent_frame` and `call_site_py_pc` in
`try_walker_inline_resolved_user_call` — are removed. Byte-identical: no
table index changes.

Assisted-by: Claude
Two `python_pc_for_jitcode_pc` inversions remained whose result no longer
keyed any table — each only fed a diagnostic string or a presence flag.

- `CalleeAbortPermanentHit.marker_py_pc` (trace.rs) was computed in
  `loop_inlines_abort_permanent_callee` solely to print `marker_py=` in the
  `fbw_debug_abort_enabled()`-gated decline log. Drop the field and the
  inversion; the log now reports only `abort_permanent_jit_pc`
  (`marker_jit_pc`).

- `guard_py_pc: Option<u32>` in `walker_capture_snapshot_for_last_guard_impl`
  (resume_snapshot.rs) was read only as `.is_some()` (presence) and as
  `liveness_py_pc = guard_py_pc.unwrap_or(py_pc)`. Its former consumers now
  key on the guard's own jitcode pc: the #124 kept-stack overlay resolves
  through `scope.branch_guard_jitcode_pc`, and the entry window through
  `entry_jitcode_pc`. Branch guards and the `after_residual_call` family (the
  only other `liveness_py_pc` consumer) never coincide, so `liveness_py_pc`
  collapses to the merge `py_pc`. Replace the inversion with
  `has_branch_guard: bool`.

Byte-identical: no table index or resume-coordinate changes.

Assisted-by: Claude
`rebuild_typed_from_rd_numb` derived an outer-frame Python pc via the
`backxlat_py_pc` jitcode→py inverse and stored it into
`PyreJitState.resume_pc`. The field had no readers repo-wide — its doc
comment's `restore_guard_failure_values` consumer was never wired.

Delete the field, both writers, its initializers, and the now-dead
backxlat computation; `rebuild_typed_from_rd_numb` drops the
`Option<usize>` element from its return tuple. This removes one of the
two `backxlat_py_pc` call sites; the surviving one is the multi-frame
branch of `build_resumed_frames`.

Assisted-by: Claude
@youknowone
youknowone merged commit 825e4cc into main Jul 22, 2026
30 of 31 checks passed
@youknowone
youknowone deleted the rewrite-tracer branch July 22, 2026 10:30
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