codewriter, jit-trace: re-land the goto_if_not fusion with the four walker sites it needs - #1679
Conversation
… arms it needs Reapplies `0dacccbfe2d`, which was backed out because it broke the JIT, and adds the four walker sites that were the reason. The fusion turns 9284 `goto_if_not/iL` into `goto_if_not_int_is_true/iL`. That key is a distinct byte (`BC_GOTO_IF_NOT_INT_IS_TRUE` = 15, against `BC_GOTO_IF_NOT` = 18), and pyre's walker matches on the key string, not the byte, so every site that named only the unfused spelling stopped recognizing the branch: * `jitcode_dispatch/mod.rs` dispatch -- no arm at all, so the walk answered `DispatchError::UnsupportedOpname` and aborted mid-body. Same shape as the `int_is_zero` gap fixed earlier on this branch, and the same signature the revert recorded: `loops_aborted 0 -> N`, `loops_compiled` down, `fbw_blackhole_adopted_single_frame 0 -> N`, and a `TypeError: 'int' object is not an iterator` out of `for j in range(n)`. * `jitcode_dispatch/mod.rs` reachability scan -- the taken arm never reached the worklist, which the walk reads as an unreachable block. * `branch.rs decode_side_other_target` -- compares the bare opname, so a guard's not-taken arm could not be reconstructed from `orgpc`. * `branch.rs branch_arm_reads_unrestorable_ref` -- the same bare-opname compare, so the scan ran past a guard into a block belonging to another resume coordinate. The three non-dispatch sites now recognize the whole family by prefix and read the label as the final operand, which every member spells the same way (`iL`, `iiL`, `rL`, `rrL`, `ffL`). The dispatch arm is not an alias of the plain one. `pyjitpl.py` `opimpl_goto_if_not_int_is_true` executes `rop.INT_IS_TRUE` and passes the fresh condbox to `opimpl_goto_if_not(..., replace=False)`; only `blackhole.py` aliases the two. Routing it to the plain arm would record no `int_is_true` and let `replace` rewrite the value box -- which for the fused form holds `x`, not the boolean. Assisted-by: Claude
|
Warning Review limit reachedNext included review available in 19 minutes. View limit detailsLimit details: You’ve used all 2 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Team Run ID: 📒 Files selected for processing (4)
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. Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
🤖 Codex parity reviewStatic analysis of this diff vs the local RPython/PyPy sources (commit 97a2753). Files in the reviewed diffCodex did not produce a report (exit 1). Last log lines: |
Merging this PR will not alter performance
Comparing Footnotes
|
#1679 re-landed the goto_if_not fusion together with the walker arms it needs, so pyre's walker now answers `goto_if_not_int_is_true/iL` and the common set is 150 of 204 rather than 149. Assisted-by: Claude
#1679 re-landed the goto_if_not fusion together with the walker arms it needs, so pyre's walker now answers `goto_if_not_int_is_true/iL` and the common set is 150 of 204 rather than 149. Assisted-by: Claude
#1679 re-landed the goto_if_not fusion together with the walker arms it needs, so pyre's walker now answers `goto_if_not_int_is_true/iL` and the common set is 150 of 204 rather than 149. Assisted-by: Claude
Re-lands
0dacccbfe2d, theoptimize_goto_if_notfusion that #1664 measured,found green in the shipped jitcode and then backed out because it broke the
JIT. The revert was right about the symptom and wrong about the cause: the
fusion is fine, and four walker sites did not know the opname it produces.
Why the revert was necessary, and what it actually was
The revert recorded
pyre/check.py --backend dynasmat 478 passed / 60failed with the fusion against 535 / 3 without, with this signature across
the 57 it added:
loops_aborted 0 -> N,loops_compileddown,fbw_blackhole_adopted_single_frame 0 -> Npyre/bench/synth/listcomp_hot.pyraisingTypeError: 'int' object is not an iteratorfromfor j in range(n)That is the same signature as the
int_is_zerowalker gap fixed in #1664, andthe same cause.
goto_if_not_int_is_true/iLis its own byte(
BC_GOTO_IF_NOT_INT_IS_TRUE= 15, againstBC_GOTO_IF_NOT= 18), and pyre'swalker matches on the key string, not the byte. The fusion put 9284 of that
key into the shipped jitcodes; every site naming only the unfused spelling
stopped recognizing the branch.
Four sites — and two of them compare the bare opname
Searching for the full key
"goto_if_not/iL"finds two. Two more compareop.opnamewithout argcodes, which is why the revert's own investigation("
body_branch_targetsdecodes anyLargcode") cleared the scans wrongly:jitcode_dispatch/mod.rsdispatchUnsupportedOpname, walk aborts mid-bodyjitcode_dispatch/mod.rsreachability scan (op.key)branch.rs decode_side_other_target(op.opname)orgpcErr("notgoto")branch.rs branch_arm_reads_unrestorable_ref(op.opname)The three non-dispatch sites now recognize the family by prefix and read the
label as the final operand, which every member spells the same way (
iL,iiL,rL,rrL,ffL), so the index isargcodes.len() - 1.The dispatch arm is not an alias of the plain one
blackhole.pyaliases them —bhimpl_goto_if_not_int_is_true = bhimpl_goto_if_not— and the walker's old comment cited exactly that alias asthe reason it needed no arm. But the walker is the metainterp side, and
pyjitpl.py opimpl_goto_if_not_int_is_trueexecutesrop.INT_IS_TRUEandhands the fresh condbox to
opimpl_goto_if_not(..., replace=False). Routingthe fused key to the plain arm would record no
int_is_trueand letreplacerewrite the value box to a constant — and for the fused form that boxholds
x, not the boolean the branch tested. So it usesfused_goto_if_not_int_unary(.., OpCode::IntIsTrue), the exact sibling of theexisting
int_is_zeroarm.Measured
Counting
body.code[pos]for everypos in body.startpointsacross theshipped
jit_metadata.json:goto_if_not/iLgoto_if_not_int_is_true/iLint_is_true/i>ilive/ptr_ne/rr>i→goto_if_not_ptr_ne/rrL9,268 standalone
int_is_trueops disappear along with the 1,674-live-markers that preceded them, and each frees its destination register.
jit_metadata.jsonis 726 KB smaller.Gates
pyre/check.py --backend dynasm— 541 / 541 ALL PASSEDpyre/check.py --backend cranelift— 541 / 541 ALL PASSEDBoth runs report no
jit-stats changeat all: the counters the.jitstatssidecars record did not move, so nothing needed
--snapshotdespite the 7%instruction cut. The one
within band (not gated)line(
synth/foriter_load_special_with) is present on the base too.Runtime is not claimed here. The static reduction above is deterministic;
the local bench ratios were taken under a loaded machine on the "before" side
and are not a measurement. The CI
pyre/check.pyjobs on this PR againstmain's own run at
de7e1a70159are the apples-to-apples comparison.— authored by Claude