Skip to content

jit: cut JIT merge-point to the upstream marker path; delete the direct hook (#205 C1) - #597

Merged
youknowone merged 5 commits into
mainfrom
gc-decouple
Jul 17, 2026
Merged

youknowone merged 5 commits into
mainfrom
gc-decouple

Conversation

@youknowone

@youknowone youknowone commented Jul 16, 2026 •

Copy link
Copy Markdown
Collaborator

Cut the JIT merge-point to the upstream marker path and delete the legacy direct hook (#205 C1).

Commits

  • jit: wire marker merge entry behind hook default — add source-level jit_merge_point/can_enter_jit markers (translator-lowered to JitMergePoint/LoopHeader), a synchronous marker-entry trace helper (compile_and_run_once), and marker-aware main-trace reconstruction. Gated behind a temporary PYRE_JIT_MERGE_PATH=hook(default)|marker switch so the marker path was proven identical to the direct hook before flipping.
  • jit: remove legacy merge-point hook — flip to marker-only: delete jit_merge_point_hook and both feed sites, the PYRE_JIT_MERGE_PATH/JitMergePath switch, eval_loop_jit_bridge (zero callers), and the hook-only call-depth / trace-continuation-suspension dispatch. Main traces now always enter before the static marker; runtime marker-coordinate scanning remains only for bridge resumes.

Net: 434 insertions(+), 660 deletions(-). The direct runtime merge-point discovery hook is gone; the merge point is driven entirely by the marker OP the way the upstream warmspot/codewriter protocol does.

Verification

pyre/check.py green on all three backends on the rebased tree:

  • dynasm 194/194
  • cranelift 194/194
  • wasm 193/193

Marker-only reproduces the pre-flip marker reference JIT-stats exactly across the 11 named benchmarks on both native backends (loops/bridges/aborts/guard_failures/panics), and gc_stress is 6/0.

Rebased onto current main; integrates the WalkJournals walk-journal handoff (#592) and the env-gate cleanup (#594) — full_body_walk_trace now carries both the journals parameter and the marker entry it used to gate.

Unblocks #205 C2 (multi-portal driver model): no jdindex==0 hardcode; per-driver JitCode/descriptor identity and portal red-register metadata are preserved.

— commented by Claude

Summary by CodeRabbit

  • Performance & Reliability
    • Improved JIT tracing with more accurate bridge-resume execution-state recovery.
    • Refined handling for declined/aborted traces to consistently fall back to interpreter execution.
    • Centralized JIT compile-and-run flow across loop and function-entry paths for more consistent behavior.
  • Documentation
    • Updated comments and diagnostics around trace dispatch, resume handling, and exit/abort behavior.
  • Tests
    • Added coverage to ensure JIT driver marker calls are lowered correctly during translation.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 44 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: a3cab9a5-8496-4ff2-88b4-c5c21f9ebdac

📥 Commits

Reviewing files that changed from the base of the PR and between 850bcb3 and 2333808.

📒 Files selected for processing (10)
  • majit/majit-metainterp/src/pyjitpl.rs
  • majit/majit-metainterp/src/trace_ctx.rs
  • majit/majit-metainterp/src/warmstate.rs
  • majit/majit-translate/src/codewriter/jtransform.rs
  • pyre/pyre-interpreter/src/pycode.rs
  • pyre/pyre-interpreter/src/typedef.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs
  • pyre/pyre-jit-trace/src/trace.rs
  • pyre/pyre-jit/src/call_jit.rs
  • pyre/pyre-jit/src/eval.rs

Walkthrough

The PR refactors interpreter JIT marker feeding and synchronous trace compilation, adds bridge-specific register reconstruction for full-body walks, removes obsolete tracing-depth state, and updates tracing comments and marker-lowering tests.

Changes

JIT tracing pipeline

Layer / File(s) Summary
Marker lowering and synchronous tracing flow
majit/majit-translate/src/codewriter/jtransform.rs, pyre/pyre-jit/src/eval.rs, majit/majit-metainterp/src/pyjitpl.rs
JIT marker lowering is tested, eval_loop_jit uses direct marker calls, call depth reads interpreter TLS, and back-edge and function-entry tracing share compile_and_run_once.
Bridge resume register reconstruction and walk seeding
pyre/pyre-jit-trace/src/trace.rs, pyre/pyre-jit/src/call_jit.rs
Bridge walks reconstruct governing merge-point registers and seed red inputs through bridge-specific logic; bridge resume snapshot behavior is documented.
Interpreter fallback and tracing-state documentation
pyre/pyre-jit-trace/src/jitcode_dispatch.rs, pyre/pyre-jit-trace/src/trace.rs, majit/majit-metainterp/src/trace_ctx.rs, majit/majit-metainterp/src/warmstate.rs
Comments describe interpreter decline paths, synchronous marker-walk outcomes, cleanup behavior, and updated tracing entry terminology.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant eval_loop_jit
  participant PyPyJitDriver
  participant compile_and_run_once
  participant trace_bytecode
  eval_loop_jit->>PyPyJitDriver: jit_merge_point and can_enter_jit markers
  PyPyJitDriver->>compile_and_run_once: start back-edge or function-entry tracing
  compile_and_run_once->>trace_bytecode: drive synchronous trace walk
  trace_bytecode-->>compile_and_run_once: return trace outcome
  compile_and_run_once-->>eval_loop_jit: dispatch LoopResult
Loading

Possibly related PRs

Suggested reviewers: lifthrasiir

Poem

I’m a rabbit tracing loops in the sun,
Markers hop where bridges run.
Red registers neatly line,
Entry paths now share one sign.
Interpreter fallback waits below—
Hop, hop, compile, and go!

🚥 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 clearly summarizes the main change: moving JIT merge-point handling to the marker path and removing the direct hook.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch gc-decouple

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 16, 2026 •

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit 2333808).
Updated: 2026-07-17T00:22:50.282Z

Files in the reviewed diff
majit/majit-metainterp/src/pyjitpl.rs
majit/majit-metainterp/src/trace_ctx.rs
majit/majit-metainterp/src/warmstate.rs
majit/majit-translate/src/codewriter/jtransform.rs
pyre/pyre-interpreter/src/pycode.rs
pyre/pyre-interpreter/src/typedef.rs
pyre/pyre-jit-trace/src/jitcode_dispatch.rs
pyre/pyre-jit-trace/src/trace.rs
pyre/pyre-jit/src/call_jit.rs
pyre/pyre-jit/src/eval.rs

1. Regressions to PyPy parity introduced by this patch

  • Assessment blocked: the workspace command sandbox failed before any repository file or diff could be read (bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted).

2. Other mismatches introduced by this patch

  • Assessment blocked: no static comparison could be performed.

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

  • Assessment blocked: no baseline source inspection was possible.

4. Structural adaptations

  • Assessment blocked: no source inspection was possible.

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

ℹ️ 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 thread pyre/pyre-jit/src/eval.rs
Comment on lines +4598 to +4602
frame_root.frame(),
marker_ec,
pc,
marker_pycode,
marker_profiled,

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 Pass greens before reds to jit_merge_point

The source-marker lowering slices positional arguments as greens first and reds second in try_handle_jit_marker (the new test uses [next_instr, profiled, pycode, frame, ec]), but this call passes [frame, ec, pc, pycode, profiled]. As a result every translated jit_merge_point treats the frame as gr[0] (the pycode green) and treats pycode/profiled as reds, so the walker builds green keys from the frame address and closes loops with the wrong live red arguments instead of (pycode, next_instr) and [frame, ec]. Hot loops traced through the new source-marker path will fail to attach under the runtime key or compile with a malformed entry contract.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pyre/pyre-jit-trace/src/trace.rs (1)

590-619: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not bind a bridge resume to a future merge point.

When entry precedes the first marker, the or_else selects a marker that has not executed. run_perfn_walk then seeds and reserves its register colors instead of using the portal-metadata fallback, potentially corrupting guard-resume operands. Return None when no preceding marker exists.

Proposed fix
     let mp_pc = merge_point_pcs()
         .filter(|&pc| pc <= entry)
-        .max()
-        .or_else(|| merge_point_pcs().filter(|&pc| pc >= entry).min())?;
+        .max()?;

Also applies to: 1555-1628

🤖 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/trace.rs` around lines 590 - 619, Update
bridge_resume_merge_point_regs so it only selects the latest jit_merge_point at
or before entry; remove the fallback that chooses the earliest future marker.
Return None when no preceding marker exists, allowing run_perfn_walk to use
portal metadata instead.
🤖 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 `@majit/majit-translate/src/codewriter/jtransform.rs`:
- Around line 7772-7780: Update the test around try_handle_jit_marker to
construct real OpKind::Call operations using the production argument order
[receiver, frame, ec, next_instr, pycode, profiled], including the can_enter_jit
arguments, and run the complete transform path. Preserve the upstream call
structure and strict structural parity, then assert the exact marker register
bindings and that no residual calls remain.

---

Outside diff comments:
In `@pyre/pyre-jit-trace/src/trace.rs`:
- Around line 590-619: Update bridge_resume_merge_point_regs so it only selects
the latest jit_merge_point at or before entry; remove the fallback that chooses
the earliest future marker. Return None when no preceding marker exists,
allowing run_perfn_walk to use portal metadata instead.
🪄 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: fd44fb3c-6971-4e09-babb-e2d7182a046c

📥 Commits

Reviewing files that changed from the base of the PR and between aa3a125 and c999c65.

📒 Files selected for processing (8)
  • majit/majit-metainterp/src/pyjitpl.rs
  • majit/majit-metainterp/src/trace_ctx.rs
  • majit/majit-metainterp/src/warmstate.rs
  • majit/majit-translate/src/codewriter/jtransform.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs
  • pyre/pyre-jit-trace/src/trace.rs
  • pyre/pyre-jit/src/call_jit.rs
  • pyre/pyre-jit/src/eval.rs
💤 Files with no reviewable changes (1)
  • majit/majit-metainterp/src/pyjitpl.rs

Comment on lines +7772 to +7780
let merge_target = CallTarget::method("jit_merge_point", Some("PyPyJitDriver".into()));
let merge_key = jit_marker_key_from_target(&merge_target)
.expect("source jit_merge_point method must be recognized");
let merge_ops = transformer
.try_handle_jit_marker(
merge_key,
&[receiver, next_instr, profiled, pycode, frame, ec],
&graph,
)

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 | 🟠 Major | ⚡ Quick win

Exercise the actual source-call argument order.

The production calls use [receiver, frame, ec, next_instr, pycode, profiled], but this test supplies canonical [receiver, next_instr, profiled, pycode, frame, ec] and no arguments for can_enter_jit. It can therefore pass while source lowering misbinds greens/reds. Build real OpKind::Call operations and run the full transform, then assert the exact marker registers and absence of residual calls.

As per coding guidelines, preserve the upstream call structure and strict structural parity. <coding_guidelines>

Also applies to: 7794-7799

🤖 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 `@majit/majit-translate/src/codewriter/jtransform.rs` around lines 7772 - 7780,
Update the test around try_handle_jit_marker to construct real OpKind::Call
operations using the production argument order [receiver, frame, ec, next_instr,
pycode, profiled], including the can_enter_jit arguments, and run the complete
transform path. Preserve the upstream call structure and strict structural
parity, then assert the exact marker register bindings and that no residual
calls remain.

Source: Coding guidelines

@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/eval.rs`:
- Around line 4628-4634: The source call in pyre/pyre-jit/src/eval.rs lines
4628-4634 must retain its existing RPython argument order; update the lowering
logic so driver declarations reorder the post-receiver payload into the correct
green/red bindings. In majit/majit-translate/src/codewriter/jtransform.rs lines
7772-7806, construct real OpKind::Call markers in production order, run the
complete transform, and assert the exact green/red bindings for both methods
while preserving strict structural parity.
🪄 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: 828df438-7aa3-48df-8350-a6ad209eed30

📥 Commits

Reviewing files that changed from the base of the PR and between c999c65 and 850bcb3.

📒 Files selected for processing (8)
  • majit/majit-metainterp/src/pyjitpl.rs
  • majit/majit-metainterp/src/trace_ctx.rs
  • majit/majit-metainterp/src/warmstate.rs
  • majit/majit-translate/src/codewriter/jtransform.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch.rs
  • pyre/pyre-jit-trace/src/trace.rs
  • pyre/pyre-jit/src/call_jit.rs
  • pyre/pyre-jit/src/eval.rs
💤 Files with no reviewable changes (1)
  • majit/majit-metainterp/src/pyjitpl.rs

Comment thread pyre/pyre-jit/src/eval.rs
Comment on lines +4628 to +4634
pypyjitdriver.jit_merge_point(
frame_root.frame(),
marker_ec,
pc,
marker_pycode,
marker_profiled,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Map source-method arguments into the driver’s declared green/red order.

The lowerer splits the post-receiver payload as the first three greens, but production emits [frame, ec, pc, pycode, profiled]. This binds frame/ec as greens and pycode/profiled as reds, corrupting merge-point register metadata.

  • pyre/pyre-jit/src/eval.rs#L4628-L4634: preserve the source call structure; make lowering reorder arguments by the driver declarations.
  • majit/majit-translate/src/codewriter/jtransform.rs#L7772-L7806: build real OpKind::Call markers in production order, run the full transform, and assert exact green/red bindings for both methods.

As per coding guidelines, preserve the RPython call structure and strict line-by-line structural parity.

📍 Affects 2 files
  • pyre/pyre-jit/src/eval.rs#L4628-L4634 (this comment)
  • majit/majit-translate/src/codewriter/jtransform.rs#L7772-L7806
🤖 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/eval.rs` around lines 4628 - 4634, The source call in
pyre/pyre-jit/src/eval.rs lines 4628-4634 must retain its existing RPython
argument order; update the lowering logic so driver declarations reorder the
post-receiver payload into the correct green/red bindings. In
majit/majit-translate/src/codewriter/jtransform.rs lines 7772-7806, construct
real OpKind::Call markers in production order, run the complete transform, and
assert the exact green/red bindings for both methods while preserving strict
structural parity.

Source: Coding guidelines

Add source-level merge markers, a synchronous marker-entry trace helper, and marker-aware main-trace reconstruction while retaining the legacy direct hook as the default path.

Assisted-by: Claude
Assisted-by: Codex
Make static JitCode markers the sole trace-entry path and retain runtime merge-point reconstruction only for bridge resumes.

Assisted-by: Claude
Assisted-by: Codex
…t per field

w_code_new_with_hidden_applevel checked `!code_ptr.is_null() &&
(code_ptr as i64) & align_mask == 0` separately at each of its five field
initializers. Hoist the check into a single `code_ptr_aligned` binding and
reference it from every initializer.

Assisted-by: Claude
init_typeobjects ran patch_object_class_descriptor and its four sibling
patch_* passes after the TYPEOBJECT_CACHE get_or_init closure returned.
get_or_init serializes only the type construction: once it is
initialized, the concurrent ExecutionContext::new callers it had blocked
reach the patch passes together, so the first-time type_dict_store races
a sibling thread's type_dict_contains read on the shared object-type dict
and panics inside indexmap on a torn index table. Guard the patch
sequence with a dedicated std::sync::Once.

Assisted-by: Claude
build_gc_global gated build+publish+store on a plain is_initialized()
check-then-act, so on a fresh process every thread that reached it before
the first store_singleton observed the flag unset and each ran build_gc().
build_gc calls freeze_types() and the subclass-range writeback, which
mutate the shared global PyType GC-tid table and subclassrange_{min,max}
atomics; concurrent writebacks raced a sibling thread reading those
ranges. Wrap the build in std::sync::Once.

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