jit-trace: exactness gates on the int/float payload folds; dynasm cond-call predicate from arglocs - #1433
Conversation
|
Warning Review limit reached
Next review available in: 58 minutes Limit 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. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
Included review availability: Your plan provides up to 2 included reviews per hour; 0 remain after this review. WalkthroughConditional-call emitters now use register-allocated predicate locations on x86 and AArch64. JIT specialization now rejects subclass values for selected unboxed operations. Benchmarks cover these subclass cases and update recorded statistics. ChangesConditional-call emitters
Exact-value specialization
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk: ⚪ Minimal · up to This PR tightens subclass handling and corrects conditional-call predicate sourcing across supported backends, with targeted checks reported as passing; no actionable merge-blocking risk remains beyond normal review and checks. Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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 parity reviewStatic analysis of this diff vs the local RPython/PyPy sources (commit c5b894c). Files in the reviewed diff1. Regressions to PyPy parity introduced by this patchNone. 2. Other mismatches introduced by this patchNone. 3. Pre-existing mismatches (already present before this patch)
4. Structural adaptations
|
`genop_discard_cond_call` (x86) and `genop_cond_call_value` (x86 and aarch64) resolved the predicate with `load_arg_to_rax(op.arg(0))`. `resolve_opref` maps only constants and frame slots, so a predicate the regalloc left register-resident has no mapping there. All three now read `arglocs[0]`. On x86 `genop_discard_cond_call` the test moves from rax to R11 (`X86_64_SCRATCH_REG`, outside `ALL_CORE_REGS`): `consider_discard_nargs_j2` emits no `before_call`, so the same op's other arglocs can be caller-saved registers including rax. Adds `emit_load_loc_to_scratch`, the counterpart of the aarch64 `emit_load_loc_to_ip0`. `genop_cond_call_value` keeps rax, which carries the predicate to `store_rax_to_result` on the not-taken path. Assisted-by: Claude
`walker_numeric_builtin_class` answers with the canonical `int`/`float` class and documents `is_exact_builtin_instance` in the operand gate as its precondition. Three callers never established it, admitting a subclass through `is_int`/`is_float`, which read `ob_type`: - `try_walker_specialize_truth_int` folded the truth off the raw payload instead of running the subclass's `__bool__`, then pinned a `w_class` the recorded operand does not carry. - `try_walker_specialize_store_attr` spelled mapdict's `type(w_value) is space.IntObjectCls` as `is_bool || !is_int`, and the float arm as `is_float`. - `try_walker_specialize_store_subscr` gated the unboxed list strategies with `is_int && !is_bool` / `is_float`. Object storage keeps the value boxed and stays ungated. The `w_class` pin rejects a subclass that arrives after recording; it does not cover one present at recording time. Adds cold-start cases to `float_subclass_binop_dispatch` for the four paths and re-records its baselines. Assisted-by: Claude
…ad_as_f64 `try_get_double` carried its own bool/int/long-to-f64 ladder, including a second copy of the "int too large to convert to float" message. It now calls `builtins::int_payload_as_f64` behind the same `is_exact_builtin_instance` gate. The two orderings differ only in which predicate answers a bool, and both read the same `intval` payload. Assisted-by: Claude
Follow-ups to #1414, opened separately because that PR was squash-merged while
these were still local. Three independent fixes; the first two are defects the
#1414 review surfaced, the third is a duplication the same review flagged.
majit dynasm: read the cond-call predicate from its regalloc locationresolve_oprefmaps only constants and frame slots, so a predicate theregalloc left register-resident has no mapping there and the emitter panics
compiling an otherwise valid trace. Three sites read it that way and now read
arglocs[0]:x86::genop_discard_cond_call,x86::genop_cond_call_value, andaarch64::genop_cond_call_value.aarch64::genop_discard_cond_callwasalready correct — the x86 twin's comment claims to mirror it but mirrored it
only for the callee and arguments.
On x86 the discard variant's test moves from rax to R11
(
X86_64_SCRATCH_REG, outsideALL_CORE_REGS):consider_discard_nargs_j2emits no
before_call, so the op's other arglocs may be caller-savedregisters including rax.
genop_cond_call_valuekeeps rax, which carries thepredicate to
store_rax_to_resulton the not-taken path.Scope note:
genop_cond_call_valuestill resolves its callee and argumentsthrough
emit_call. That is the same exposure, but pre-existing rather thanintroduced here, and the two helpers differ in their
arg_typesfallback(
emit_callinfers per-argument types;emit_call_from_arglocsassumesall-Int), so swapping it carries its own ABI risk and is left for a follow-up.
jit-trace: require an exact builtin before the int/float payload foldswalker_numeric_builtin_classanswers with the canonicalint/floatclassand documents
is_exact_builtin_instancein the operand gate as itsprecondition. Three callers never established it, admitting a subclass through
is_int/is_float, which readob_type:try_walker_specialize_truth_intfolded the truth off the raw payloadinstead of running the subclass's
__bool__, then pinned aw_classtherecorded operand does not carry — a guard that fails on the very value that
recorded it.
try_walker_specialize_store_attrspelled mapdict'stype(w_value) is space.IntObjectClsasis_bool || !is_int. Both arms nowcall the interpreter's own
is_unboxable_int/is_unboxable_floatratherthan re-deriving the rule.
try_walker_specialize_store_subscrgated the unboxed list strategies withis_int && !is_bool/is_float. Object storage keeps the value boxed andstays ungated.
The
w_classpin rejects a subclass arriving after recording; it does notcover one present at recording time.
The other
walker_numeric_builtin_classcallers are clean, and a single-filecensus gets this wrong:
compare_op_int/binary_op_int/binary_op_float/
compare_op_floatgate throughwalker_int_specialization_input_operandsand its float twin in
mod.rs;newlistgates withis_plain_int1.bench/synth/float_subclass_binop_dispatch.pyonly had warm-then-swap cases,which pass either way — they meet the subclass after recording. Adds
cold-start cases that record on the subclass from the first iteration
(
truth_cold_subclassdiffers 20000 vs 0 between the two answers) andre-records the baselines:
loops_compiled17 → 24 across all three backends,exactly the seven added functions.
interp: fold the math float coercion's int payload arm intoint_payload_as_f64try_get_doublecarried its own bool/int/long-to-f64 ladder including a secondcopy of the "int too large to convert to float" message. The two orderings
differ only in which predicate answers a bool, and both read the same
intval.Verified:
cargo checkonpyre-interpreter,pyre-jit-trace,majit-translate,majit-backend-dynasm(native andx86_64-apple-darwin);cargo test -p pyre-interpreter407 passed;cargo test -p majit-backend-dynasm72 passed;check.py --synthetic-pattern float_subclass_binop_dispatchPASS on dynasm/cranelift/wasm.Cold-start A/B, measured: same tree with only
specialize.rsreverted to0aa9ada1eb1^, two full release builds.store_subscr_cold_subclassstore_attr_cold_subclassstore_attr_cold_subclass_floattruth_cold_subclassSo only
truth_cold_subclassobserves the defect:LiarBool(0)is falsy bypayload and true by
__bool__, and 48 of 20000 iterations — the compiledregion's share — took the payload answer.
The other three are already correct before the fix, so they verify nothing.
The
truth_intgate is confirmed by observation; thestore_attrandstore_subscrgates rest on reading the gates, not on an observed failure.🤖 Generated with Claude Code
https://claude.ai/code/session_019gCeUzbGWCK8S6vqnXh416
Summary by CodeRabbit
Bug Fixes
Tests