Skip to content

jit: drop the dead exception-bridge prologue and the unread walk-sym exception getters - #1240

Merged
youknowone merged 1 commit into
mainfrom
perf-bridge
Aug 15, 2026
Merged

jit: drop the dead exception-bridge prologue and the unread walk-sym exception getters#1240
youknowone merged 1 commit into
mainfrom
perf-bridge

Conversation

@youknowone

Copy link
Copy Markdown
Owner

Follow-up to #1232. This round re-read the bridge/retrace surface against the
vendored RPython sources looking for more of what #1222 and #1232 fixed. It
found no wrong-code defect.
What it did find is dead code that had drifted
away from the live path it documents, so that is what this PR removes.

emit_exception_bridge_prologue was dead, and no longer matched the live path

MetaInterp::emit_exception_bridge_prologue had no callers. The live
exception-edge bridge builds the sequence itself in bridge_subwalk, via
trace_ctx.save_exc_class() / save_exception() / restore_exception(), which
record0 the two SAVEs exactly as pyjitpl.py:3148-3149 does. The dead copy
passed the class and value as operands instead — a shape the backends'
genop_save_exc_class (result-only, no arglocs) would not accept — and cited
optimizeopt/rewrite.rs for remove_bridge_exception, which lives in
majit-gc/src/rewrite.rs (rewrite.py:988-1001).

It did carry one thing worth keeping: that RPython splits the sequence across
_prepare_exception_resumption and prepare_resume_from_failure because
resume-data replay runs between the SAVEs and the RESTORE, and that collapsing
them into one block is sound only while pyre emits nothing in that gap. That
note moves onto the live emitter, where the next reader will be standing.

Two WalkSym accessors had no callers

current_exc_value() / current_exc_box() are gone. The setters and the fields
stay — walk_active_sym_exc_roots reads sym.current_exc_value directly as a
GC carrier, so the state is live even though these two getters never were.

A per-typetag match with four identical arms

must_compile_with_values decoded the GUARD_VALUE typetag and then returned the
same expression for TY_INT, TY_REF, TY_FLOAT and the fallthrough.
compile.py:761-771 really does convert — cast_ptr_to_int for TY_REF,
longlong.gethash_fast for TY_FLOAT — but gethash_fast is
longlong2float.float2longlong on a 64-bit host (codewriter/longlong.py:28),
i.e. the double's raw bit pattern, which is what fail_values already holds for
every slot. So no arm needs a conversion. Replaced with that reasoning plus
compile.py:772-773's assert 0, typetag, so the next reader does not have to
re-derive it from the RPython side.

What was checked and came back faithful

Recording this so the next pass starts further along rather than re-walking it:
bridgeopt.py serialize/deserialize on both sides of the class bitfield and the
heap/loopinvariant sections; serialize_optheap's three skip conditions
(get_descr_index() == -1, _lazy_set, parent_descr.is_object()) and the
array side's index >= 2**15; the ST_BUSY_FLAG / ST_TYPE_MASK status machine
and make_a_counter_per_value across all four backends; store_hash (pyre
assigns per compiled fail-descr layout rather than at optimizer-emit time, and
both bridge paths are covered); _copy_resume_data_from and the
ResumeGuardCopiedDescr prev chase, including the blackhole resume;
propagate_original_jitcell_token against the retrace path, where
combined_ops = partial.ops + body_ops puts the prior front's LABEL inside the
trace so the token set and the LABEL set coincide; all seven optimize_bridge
exits, the vs-survives-InvalidLoop fallthrough on the retrace-limit retry,
and the retraced_count read and write both landing on cell_token_key; and
record_loop_or_bridge's quasi-immutable dependency publication on the bridge
path.

Verification

pyre/check.py, darwin arm64, all three backends:

ALL PASSED: dynasm 435/435
ALL PASSED: cranelift 435/435
ALL PASSED: wasm 428/428
ALL PASSED: 3/3 backend run(s)

Neither touched crate is in the LLBC extraction set, and the change is dead-code
removal plus comments, so no jitstats row can move — the green run is a
regression check, not a measurement.

authored by Claude

…exception getters

`MetaInterp::emit_exception_bridge_prologue` had no callers. The live
exception-edge bridge emits the sequence itself in `bridge_subwalk`, through
`trace_ctx.save_exc_class()` / `save_exception()` / `restore_exception()`,
which record0 the two SAVEs as `pyjitpl.py:3148-3149` does. The dead copy
passed the class and value as operands instead, and pointed at
`optimizeopt/rewrite.rs` for `remove_bridge_exception`, which lives in
`majit-gc/src/rewrite.rs` (rewrite.py:988-1001).

Its one piece of live information — that RPython splits the sequence across
`_prepare_exception_resumption` and `prepare_resume_from_failure` because
resume-data replay runs between the SAVEs and the RESTORE, and that pyre may
collapse them only while it emits nothing in that gap — moves onto the live
emitter.

`WalkSym::current_exc_value` / `current_exc_box` had no callers either. The
setters and the fields stay: `walk_active_sym_exc_roots` reads
`sym.current_exc_value` directly as a GC carrier.

In `must_compile_with_values`, the per-typetag match had the same body in all
four arms. compile.py:761-771 does convert — `cast_ptr_to_int` for TY_REF and
`longlong.gethash_fast` for TY_FLOAT — but `gethash_fast` is
`longlong2float.float2longlong` on a 64-bit host (codewriter/longlong.py:28),
so all three tags hash the raw word `fail_values` already holds. Replaced with
that note plus compile.py:772-773's `assert 0, typetag`.

Assisted-by: Claude
@coderabbitai

coderabbitai Bot commented Aug 15, 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: 21 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

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 Plus

Run ID: 7eed8d12-2294-40b3-993b-92eefe0a0c69

📥 Commits

Reviewing files that changed from the base of the PR and between f391d36 and 8803872.

📒 Files selected for processing (3)
  • majit/majit-metainterp/src/pyjitpl.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/bridge_subwalk.rs
  • pyre/pyre-jit-trace/src/state.rs

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

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit 8803872).
Updated: 2026-08-15T10:07:31.053Z

Files in the reviewed diff
majit/majit-metainterp/src/pyjitpl.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/bridge_subwalk.rs
pyre/pyre-jit-trace/src/state.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)

  • majit/majit-metainterp/src/pyjitpl.rs:11517 ↔ rpython/jit/metainterp/compile.py:772-773 — PyPy unconditionally executes assert 0, typetag for an invalid per-value guard tag. Pyre uses debug_assert!, which is removed from release builds; release then hashes fail_values[index] and ticks the counter. This was already the effective release behavior on main (_ => raw); the patch improves debug builds but does not restore release parity.

  • pyre/pyre-jit-trace/src/jitcode_dispatch/bridge_subwalk.rs:460-462 ↔ rpython/jit/metainterp/pyjitpl.py:3148-3165 — PyPy records SAVE_EXC_CLASS/SAVE_EXCEPTION at an empty trace start, reconstructs resume state, then records RESTORE_EXCEPTION and handles the exception. Pyre emits all three operations consecutively and has no bridge-side resume-data replay between them. The sequence is therefore structurally non-equivalent; it predates this patch, which only documents it.

4. Structural adaptations

None.

@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: 8803872e0d

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

// host (codewriter/longlong.py:28) — the double's raw bit pattern.
// `fail_values` already holds every slot as that raw word, so all
// three tags hash the stored value unchanged.
let intval: i64 = fail_values.get(index as usize).copied().unwrap_or(0);

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 Preserve the 32-bit float guard hash

On wasm32 and other 32-bit targets, RPython's rpython/jit/codewriter/longlong.py:44-45 defines gethash_fast as intmask(xll - (xll >> 32)), and the Rust port already implements that target-specific behavior in majit-translate/src/codewriter/longlong.rs:64-76. Treating the raw word as the hash ignores its upper 32 bits because JitCounter::_get_index truncates to u32; distinct values such as 1.0 and 2.0 therefore feed the same per-value counter and can spuriously reach trace eagerness from their combined failures. Keep the raw-word shortcut only on 64-bit targets and apply the folded gethash_fast calculation for TY_FLOAT on 32-bit targets.

AGENTS.md reference: AGENTS.md:L288-L290

Useful? React with 👍 / 👎.

@youknowone
youknowone merged commit ce472a5 into main Aug 15, 2026
14 of 17 checks passed
@youknowone
youknowone deleted the perf-bridge branch August 15, 2026 12:08
youknowone added a commit that referenced this pull request Aug 15, 2026
`arith_int_bool` reads `bridges_compiled` 11 and `guard_failures` 2307 where
the file recorded 10 and 2211; `short_circuit_value_kept_stack` reads 11 and
2201 where it recorded 12 and 2510.

Both are main's, not this branch's: the CI run on `3b403691725` reports the
same two fixtures with the same numbers, and they do not move across
`#1238`/`#1240` or with the environment byte length varied from 0 to 512.

The ratio row on `short_circuit_value_kept_stack` is a separate inherited
red and is not addressed here.

Assisted-by: Claude
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