Skip to content

jit: #73 jitcode-blackhole — backxlat consumer-family twin cutover + midbody py forward-carry (P1) - #745

Merged
youknowone merged 5 commits into
mainfrom
rewrite-tracer
Jul 23, 2026
Merged

youknowone merged 5 commits into
mainfrom
rewrite-tracer

Conversation

@youknowone

@youknowone youknowone commented Jul 23, 2026 •

Copy link
Copy Markdown
Collaborator

Continues the jitcode-blackhole epic (#73) on top of #727 (Slice 3'), retiring
runtime consumers of the py_pc ↔ jitcode_pc translation tables by reading the
codewriter-built forward py_pc twin instead of inverting jitcode_pc → py_pc.

Commits

  1. d977ab8 — cut the Backxlat parent-resume coordinate to the forward
    py_pc twin.
    resolve_parent_resume_py_pc's Backxlat arm reads the
    codewriter-built forward_py_pc_for_jitcode_pc twin, keeping the inversion
    only for the empty-twin (skeleton / fixture) class.

  2. cabe2c8 — PYRE_M73_LASTINSTR_AUDIT blackhole-traceback divergence
    census.
    A gated census over the blackhole traceback-delivery sites,
    comparing the jitcode_pc → py_pc inversion against frame.last_instr + 1.
    Measured large, varied divergence (not ±1) across the synth corpus, proving
    last_instr is many opcodes stale at the raising op — the inversion is
    load-bearing for tb_lasti / tb_lineno, so a naive forward-carry here is a
    NO-GO. Census retained as a tripwire.

  3. 2003cdf + e032ddd — route the backxlat_py_pc resume-word consumer
    family through state::forward_py_pc_or_backxlat
    (two phases: audit, then
    cutover). One helper with a negative-guard (NO_JITCODE_PC / tagged
    branch-orgpc words delegate to backxlat_py_pc), a forward_py_pc twin read
    for non-negative words, and an empty-twin fallback. All backxlat_py_pc
    resume-word call sites converted; the inversion is now reached only via the
    helper and the diagnostic fallback. A 3-lens adversarial pass proved
    twin == backxlat by construction (marker tier iterates the same
    block_head_py_by_jit_pc + skip_python_trivia_forward; pred tier mirrors
    floor resolution). PYRE_M73_BACKXLAT_TWIN_AUDIT sweep over 280 synth
    tests × both backends: 0 divergence.

  4. 972f566 — Eliminate pc_map and per-PC labels by switching the tracer to interpret JitCode bytecode #73 P1: forward-carry the midbody callee resume py on
    MidBodyPayload.
    Adds MidBodyPayload.callee_py_pc, stamped at capture
    from the value already computed for the abort anchor, so the midbody flush
    reads a scalar instead of re-inverting abort_jitcode_pc through
    block_head_py_by_jit_pc + py_floor_by_jit_pc. Removes one runtime reader
    of both tables. Byte-parity rests on encode/resume input identity;
    PYRE_M73_MIDBODY_CARRY_AUDIT is a permanent tripwire (the midbody
    callee-rebuild commit path is not exercised by the synth corpus, so the
    certificate here is by-construction).

Scoping note (recorded for follow-up)

A map + decompose + adversary scoping of the walker-as-tracer epic found the
byte-preserving forward-carry lever is exhausted after P1: the walk itself is
py-free (op.pc is a jitcode offset; InlineParentFrame carries jitcode coords,
no py scalar), so a forward-carry reduces table readers only where the encode
already converts py for another reason — P1 is the unique such site. The
remaining warm consumers (Entry / outer abort carriers) are pure relocations,
and the true remaining progress is the structural reentry-in-jitcode-coords
change (byte-changing, design-bearing). No pc_map / table deletion lands here.

Verification

  • python3 pyre/check.py --backend dynasm,cranelift = 294/294 each, byte-identical.
  • PYRE_M73_BACKXLAT_TWIN_AUDIT sweep: 280 synth × 2 backends, 0 divergence.

— authored by Claude

Summary by CodeRabbit

  • Bug Fixes

    • Improved accuracy when translating JIT execution positions back to Python source positions.
    • Improved handling of inline calls and mid-execution interruptions, preserving the correct callee location.
    • Improved diagnostic and traceback reporting across JIT execution paths.
  • Diagnostics

    • Added optional runtime checks to detect inconsistencies in position tracking and report detailed information for troubleshooting.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown

Review Change Stack

Walkthrough

JIT trace resume paths now prefer forward Python-PC twins, carry callee Python PCs through midbody abort payloads, and provide environment-gated audits for coordinate consistency and last-instruction inversion.

Changes

Python-PC Resume Mapping

Layer / File(s) Summary
Forward translation and resume consumers
pyre/pyre-jit-trace/src/state.rs, pyre/pyre-jit-trace/src/jitcode_dispatch/*, pyre/pyre-jit-trace/src/trace.rs
Forward Python-PC twins are preferred for inline reconstruction, bridge assembly, walk entry, tracing diagnostics, and parent resume resolution, with back-translation fallback where needed.
Midbody callee coordinate carry
pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs, pyre/pyre-jit-trace/src/jitcode_dispatch/inline_call.rs, pyre/pyre-jit-trace/src/trace.rs
MidBodyPayload carries callee_py_pc, and midbody flushes use that value with optional consistency auditing.
Last-instruction telemetry
pyre/pyre-jit/src/call_jit.rs
Optional audits compare inverted Python PCs with frame.last_instr + 1 on caught and exception-exit paths.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • youknowone/pyre#602: Modifies coordinate handling at inline abort and midbody flush boundaries.
  • youknowone/pyre#623: Instruments JIT-to-Python coordinate resolution and carried-coordinate paths.
  • youknowone/pyre#727: Updates resume and trace handling to use forward-carried Python PCs.

Poem

A bunny hops where twin PCs gleam,
Forward paths now guide the stream.
Midbody carries what flushes need,
Audits count each hopping deed.
JIT and Python share one tune—
Thump-thump beneath the debugging moon.

🚥 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 main JIT backxlat-to-forward-twin cutover and midbody py forward-carry changes.
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 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 23, 2026 •

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit 1bee8cf).
Updated: 2026-07-23T16:31:34.389Z

Files in the reviewed diff
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/state.rs
pyre/pyre-jit-trace/src/trace.rs
pyre/pyre-jit/src/call_jit.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)

None.

4. Structural adaptations

  • pyre/pyre-jit-trace/src/state.rs:859 ↔ rpython/jit/metainterp/resume.py:1042: Pyre carries/precomputes a CPython Python-instruction PC beside JitCode PCs, whereas PyPy resumes solely from (jitcode_pos, pc). The new forward table preserves the prior reverse-translation result, including trivia skipping and fallback.

  • pyre/pyre-jit-trace/src/jitcode_dispatch/inline_call.rs:2431 ↔ rpython/jit/metainterp/resume.py:1051: the Rust mid-body-abort payload carries callee_py_pc explicitly rather than reconstructing a fresh MIFrame and calling setup_resume_at_op(pc). It is stamped from the same conversion previously performed at flush, so the resumed coordinate is unchanged.

  • pyre/pyre-jit/src/call_jit.rs:628 ↔ pypy/interpreter/pytraceback.py:104: PYRE_M73_LASTINSTR_AUDIT is Rust-specific opt-in telemetry comparing two coordinate representations. It does not alter the traceback PC passed to record_application_traceback; PyPy has no equivalent diagnostic gate.

resolve_parent_resume_py_pc's Backxlat arm inverted the parent jitcode_pc via
backxlat_py_pc. Read the forward_py_pc twin (a codewriter-built, trivia-
normalized twin of backxlat_py_pc's result, proven equal corpus-wide in Slice
3') instead; the inversion survives only for the empty-twin class. Byte-identical;
dynasm/cranelift 293/293.

Assisted-by: Claude
Gated telemetry at the three blackhole exception-delivery sites comparing the
production inversion python_pc_for_jitcode_pc_public(last_opcode_position)
against the forward candidate frame.last_instr + 1. Off by default (zero
production effect). Measured over the synth corpus: ~99.97% divergence
(75689/75712 hits), deltas up to ~100 bytecodes — the vable last_instr is stale
by many opcodes at the raising op, so the inversion is load-bearing for
traceback fidelity and cannot be replaced without reconciling the last_instr
convention.

Assisted-by: Claude
…_or_backxlat (audit phase)

Introduce state::forward_py_pc_or_backxlat(jitcode_index, pc_word) beside
backxlat_py_pc. It returns the backxlat inversion and, under the
PYRE_M73_BACKXLAT_TWIN_AUDIT gate, asserts the codewriter-built forward_py_pc
twin equals that inversion for every non-negative resume word. Negative sentinel
words (NO_JITCODE_PC -1 / tagged branch-orgpc <= -2) and the empty-twin class
(skeleton/fixture jitcodes) never consult the twin, so the negative word is
delegated straight to backxlat_py_pc.

Convert the 12 backxlat_py_pc consumer call sites (state.rs x2, trace.rs x9,
jitcode_dispatch/mod.rs x1) to the helper. The helper still returns backxlat, so
behaviour is unchanged and byte-identical; only the gated audit branch is added.
backxlat_py_pc now has two remaining callers: this helper and the diag.rs
Backxlat fallback.

check.py dynasm 293/293 + cranelift 293/293 byte-identical (audit off).
PYRE_M73_BACKXLAT_TWIN_AUDIT sweep over 280 synth tests x both backends: 0
divergence; the walker entry_py_pc arm alone exercises the audit on nearly every
JIT trace entry.

Assisted-by: Claude
…r phase)

Flip forward_py_pc_or_backxlat to return the codewriter-built forward_py_pc twin
for non-negative resume words, keeping backxlat_py_pc only for the empty-twin
class (skeleton/fixture jitcodes) and negative sentinel words. The
PYRE_M73_BACKXLAT_TWIN_AUDIT gate now asserts the twin equals backxlat before
returning it.

The twin is a by-construction mirror of backxlat's block-head/floor resolution
plus its skip_python_trivia_forward normalization; the preceding audit phase
certified it equal across 280 synth tests x both backends with zero divergence.

check.py dynasm 293/293 + cranelift 293/293 byte-identical.

Assisted-by: Claude
…P1)

Add MidBodyPayload.callee_py_pc and stamp it at capture (inline_call.rs) from
the callee_py_pc already computed for the abort anchor validation, so the
midbody flush reads the resume py as a scalar (trace.rs resolve_midbody_flush_words)
instead of re-inverting abort_jitcode_pc through the block_head_py_by_jit_pc +
py_floor_by_jit_pc tables. This removes one runtime reader of those tables on the
midbody callee-rebuild resume path.

Byte-parity rests on encode/resume input identity: both sides compute
python_pc_for_jitcode_pc over the same append-only-immutable jitcode metadata
(round-tripped via callee_jitcode_index) at the same pc (abort_jitcode_pc ==
abort_pc); the function reads only metadata, never code_ptr. The
PYRE_M73_MIDBODY_CARRY_AUDIT gate asserts carry == inversion at the flush and
stays as a permanent tripwire. The midbody callee-rebuild commit path is not
exercised by the synth corpus (0/280 both backends), so the audit is a
by-construction certificate here rather than an exercised one.

check.py dynasm 294/294 + cranelift 294/294 byte-identical.

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/src/call_jit.rs`:
- Around line 650-663: Track first-hit heartbeats independently for each audit
site instead of using the shared HITS counter alone. Update the instrumentation
around the site identifier and the caught/exit_guard_exc/exit_got_exc paths to
maintain separate atomic counters and emit each site’s heartbeat on its own
first execution, while preserving HITS for aggregate counting if needed.
🪄 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 Plus

Run ID: a4bf8142-0b9e-43bd-b1bc-42521b90ff02

📥 Commits

Reviewing files that changed from the base of the PR and between 7172684 and 1bee8cf.

📒 Files selected for processing (6)
  • 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/state.rs
  • pyre/pyre-jit-trace/src/trace.rs
  • pyre/pyre-jit/src/call_jit.rs

Comment on lines +650 to +663
use std::sync::atomic::{AtomicU64, Ordering};
static HITS: AtomicU64 = AtomicU64::new(0);
static DIVERGE: AtomicU64 = AtomicU64::new(0);
static NONE: AtomicU64 = AtomicU64::new(0);
let inv = jitcode_index.and_then(|index| {
pyre_jit_trace::state::python_pc_for_jitcode_pc_public(index, opcode_position)
});
let fwd = unsafe { (*frame_ptr).last_instr as i64 } + 1;
let hits = HITS.fetch_add(1, Ordering::Relaxed) + 1;
if hits == 1 {
// Per-process heartbeat: confirms the site was exercised even when no
// divergence prints (each check.py test is a fresh process).
eprintln!("[M73-LASTINSTR] first-hit site={site}");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Track first hits per audit site.

HITS is shared by "caught", "exit_guard_exc", and "exit_got_exc", so only whichever site runs first emits a heartbeat. Later sites can be exercised without any confirmation, weakening the audit’s gated-path verification. Keep the aggregate counter if useful, but add per-site first-hit counters.

As per coding guidelines, gated trace/debug paths must be verified as actually reached.

🤖 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/src/call_jit.rs` around lines 650 - 663, Track first-hit
heartbeats independently for each audit site instead of using the shared HITS
counter alone. Update the instrumentation around the site identifier and the
caught/exit_guard_exc/exit_got_exc paths to maintain separate atomic counters
and emit each site’s heartbeat on its own first execution, while preserving HITS
for aggregate counting if needed.

Source: Coding guidelines

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