Skip to content

jit: exempt list_write_barrier from the FOR_ITER body-effect gate (#389 item b) + wasm codegen fixes - #661

Merged
youknowone merged 7 commits into
mainfrom
no-token
Jul 21, 2026
Merged

youknowone merged 7 commits into
mainfrom
no-token

Conversation

@youknowone

@youknowone youknowone commented Jul 19, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes the Object-strategy inlined-comprehension LIST_APPEND fold (#389 item b): a hot
comprehension whose appended element goes to the list's Object strategy
([(i,i) for …], [None …], ["s" …], [{i:i} …], [f"{i}" …]) was yielding one
element short under the JIT. Also fixes two wasm-backend codegen gaps the same trace
exposed.

Root cause (barrier body-effect misclassification)

The #171 orthodox LIST_APPEND fold descends w_list_append. Its Object-strategy arm
stores a GC ref and runs list_write_barrier, a #[dont_look_inside] Void residual.
The FBW body-effect gate treated that Void residual as a live-heap write, so the in-flight
FOR_ITER item was refuse-dropped — one iteration lost. The Int/Float arms store unboxed
scalars with no barrier, which is why only Object-strategy appends dropped.

This mirrors RPython, where the write barrier is COND_CALL_GC_WB: pyjitpl never executes
it (executor.py:446), the optimizer never treats it as a side effect
(resoperation.py:1124), and it is inserted only in the backend rewrite pass
(rewrite.py:948). It therefore cannot be a trace-level body-effect.

Changes

jit: exempt list_write_barrier from FOR_ITER body-effect gate

  • is_list_write_barrier(addr) in jit_fnaddr.rs matches the registered barrier fnaddr.
  • Exempt it from body_effect_candidate, fbw_mark_executed_nonpure_residual, and the
    executed-effect odometer in try_execute_residual_call_via_executor. The barrier still
    executes concretely and is still recorded in IR — only the FBW body-effect bookkeeping
    is suppressed.
  • Widen for_iter_bodies_all_jit_safe to admit a call-free LIST_APPEND body. A
    non-empty nested BUILD_LIST element ([[i] for …]) is deliberately declined — the
    fold virtualizes the inner list but does not thread its backing block into the append
    commit sub-walk's guard-exit resume data. Tracked as a follow-up in JIT: hot loops inside a with-block / comprehension never receive a JIT token (no-token cliff behind #373) #389 (see comment).
  • Add synth/comprehension_object_append_hot.py.

majit-backend-wasm: normalize op positions and resolve NONE fail-args

  • Add normalize_ops_for_codegen (mirroring dynasm prepare_ops_for_compile /
    cranelift normalize_ops_for_codegen_simple) and call it in compile_loop /
    compile_bridge, so every non-Void-result op left unpositioned by the force path gets a
    stamped OpRef before codegen.
  • emit_resolve spilled value_types[opref.raw()] for a NONE operand
    (raw() == u32::MAX), panicking OOB when a Finish op carried a NONE fail-arg (a dead
    deopt slot). Spill a zero placeholder instead; the blackhole never reads that slot back.

Verification

  • check.py: dynasm 226/226, cranelift 226/226, wasm 225/225.
  • cargo test -p pyre-jit for_iter 17/17; cargo test -p pyre-interpreter is_list_write_barrier.
  • 3-way bit-exact parity on the synth (total = 3000000).

Assisted-by: Claude

Summary by CodeRabbit

  • New Features

    • Improved JIT support for call-free list comprehensions, including tuples, strings, dictionaries, formatted strings, and empty nested lists.
    • Added optional support for optimizing nested list-comprehension patterns.
  • Bug Fixes

    • Improved deoptimization and resume-data handling for complex optimized loops.
    • Prevented crashes caused by invalid optimization references.
    • Improved list write-barrier handling and reduced unnecessary effect tracking.
    • Disabled problematic unboxed list-append optimizations on WebAssembly by default.

@coderabbitai

coderabbitai Bot commented Jul 19, 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: 20 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: d51a1b64-a0a4-41dc-9e02-1a337507ed56

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2b54b and de53244.

📒 Files selected for processing (3)
  • majit/majit-metainterp/src/optimizeopt/mod.rs
  • majit/majit-metainterp/src/optimizeopt/unroll.rs
  • pyre/pyre-jit/src/eval.rs

Walkthrough

Adds nested-list comprehension JIT admission rules, extra virtual-root snapshot propagation through optimization and resume-data generation, idempotent list-barrier effect handling, and wasm safeguards for missing operand references.

Changes

Nested list virtualization and deoptimization

Layer / File(s) Summary
List-append eligibility and effect accounting
pyre/pyre-jit/src/eval.rs, pyre/pyre-jit-trace/src/jitcode_dispatch/*, pyre/pyre-interpreter/src/jit_fnaddr.rs, pyre/bench/synth/*
Call-free value-producing list appends are admitted, non-empty nested lists are gated, wasm unboxed folds can be declined, and list write barriers are excluded from irreversible effect accounting.
Extra virtual-root snapshot capture
majit/majit-metainterp/src/recorder.rs, majit/majit-metainterp/src/history.rs, pyre/pyre-jit-trace/src/jitcode_dispatch/*, pyre/pyre-jit-trace/src/trace_opcode.rs
Snapshots define extra_virtual_roots and capture or remap them across guard, trace, dispatch, and opcode paths.
Snapshot propagation through optimization
majit/majit-metainterp/src/pyjitpl.rs, majit/majit-metainterp/src/optimizeopt/*
Extra-root snapshot maps are translated and carried through bridge preparation, retries, retracing, unrolling, and optimizer state.
Resume-data and wasm resolution
majit/majit-metainterp/src/resume.rs, majit/majit-backend-wasm/src/*
Resume-data virtual discovery seeds its worklist from extra roots, while wasm codegen normalizes missing result positions and emits zero for OpRef::NONE.

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

Possibly related issues

  • youknowone/pyre#389 — Addresses nested-list comprehension deoptimization and OpRef::NONE handling.
  • youknowone/pyre#570 — Shares the list-comprehension deoptimization and virtual-root snapshot paths.

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant TraceCapture
  participant SnapshotMaps
  participant Optimizer
  participant ResumeDataVirtualAdder
  TraceCapture->>SnapshotMaps: capture extra_virtual_roots
  SnapshotMaps->>Optimizer: translate and store snapshot roots
  Optimizer->>ResumeDataVirtualAdder: pass extra_virtual_roots
  ResumeDataVirtualAdder->>ResumeDataVirtualAdder: seed virtual worklist and discover fields
Loading

Poem

A rabbit hops through lists so neat,
With roots tucked safely by each beat.
Deopts bloom, and wasm knows
A missing ref means zero toes.
Nested leaves now pass the gate—
Hoppy code can celebrate!

🚥 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 is specific and matches the main changes: exempting list_write_barrier from the FOR_ITER gate and fixing wasm codegen.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch no-token

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.

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

ℹ️ 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 on lines +1059 to +1064
let normalized = (**op).clone();
let rt = normalized.result_type();
if rt != majit_ir::Type::Void && normalized.pos.get().is_none() {
normalized
.pos
.set(majit_ir::OpRef::op_typed(num_inputs + op_idx as u32, rt));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stamp producer identities before wasm codegen

When the forced-materialization path leaves a producer unpositioned and a later op consumes it, this only stamps the fresh cloned Op. The cloned consumer still holds Operand::Op(Rc<original>), and Operand::to_opref() reads the original producer's pos, so it remains OpRef::NONE; wasm codegen can then resolve that normal operand as the new zero placeholder and use a null/zero value instead of the materialized result. Please mutate/rebind the producer identities (or rebuild operands to point at the normalized ops) before codegen.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Jul 19, 2026 •

Copy link
Copy Markdown

🤖 Codex parity review

Static analysis of this diff vs the local RPython/PyPy sources (commit de53244).
Updated: 2026-07-21T14:35:44.539Z

Files in the reviewed diff
majit/majit-backend-wasm/src/codegen.rs
majit/majit-backend-wasm/src/lib.rs
majit/majit-metainterp/src/history.rs
majit/majit-metainterp/src/optimizeopt/mod.rs
majit/majit-metainterp/src/optimizeopt/optimizer.rs
majit/majit-metainterp/src/optimizeopt/unroll.rs
majit/majit-metainterp/src/pyjitpl.rs
majit/majit-metainterp/src/pyjitpl/dispatch.rs
majit/majit-metainterp/src/recorder.rs
majit/majit-metainterp/src/resume.rs
majit/majit-metainterp/tests/jit_driver_runtime_parity.rs
pyre/bench/synth/comprehension_object_append_hot.py
pyre/bench/synth/nested_list_comprehension_hot.py
pyre/pyre-interpreter/src/jit_fnaddr.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/resume_snapshot.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/specialize.rs
pyre/pyre-jit-trace/src/trace_opcode.rs
pyre/pyre-jit/src/eval.rs

1. Regressions to PyPy parity introduced by this patch

  • pyre/pyre-jit/src/eval.rs:5002 ↔ rpython/jit/metainterp/resume.py:414 — The patch enables non-empty nested-list comprehensions by default, but the required virtual-root channel is never populated: every GuardCaptureScope construction uses its default empty slice (pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs:7084), and the only use merely copies that slice (pyre/pyre-jit-trace/src/jitcode_dispatch/resume_snapshot.rs:944). Thus [[i] for i in range(n)] is newly admitted without the claimed root for its virtual backing block. RPython recursively reaches nested virtuals from an actually-live virtual via visitor_walk_recursive; it does not enable the shape with an empty auxiliary-root path (rpython/jit/metainterp/optimizeopt/info.py:243).

2. Other mismatches introduced by this patch

  • majit/majit-metainterp/src/recorder.rs:61 ↔ rpython/jit/metainterp/resume.py:389 — Snapshot::extra_virtual_roots introduces a fourth, out-of-band resume-data root section. RPython’s ResumeDataVirtualAdder.finish() derives virtual traversal only from numbered vable/vref/frame boxes and pending fields; it has no separate snapshot root channel. The new channel is also inconsistent with its own documentation: pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs:3737 says roots must be appended to frame boxes, while resume_snapshot.rs:939 explicitly keeps them out of frame liveness and serializes them separately.

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

None.

4. Structural adaptations

  • pyre/pyre-jit/src/eval.rs:4961 ↔ pypy/interpreter/pyopcode.py:1492 — The FOR_ITER/LIST_APPEND admission scan is a CPython-3.14-bytecode safety gate. PyPy’s compiler/interpreter opcode pipeline differs, so this gate has no 1:1 RPython metainterpreter counterpart.

  • pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs:669 ↔ rpython/jit/metainterp/executor.py:446 — Treating list_write_barrier as an executable but replay-idempotent residual is a Rust-runtime adaptation. RPython does not execute COND_CALL_GC_WB in pyjitpl; its backend GC rewrite inserts it after optimization (rpython/jit/backend/llsupport/rewrite.py:948).

  • majit/majit-backend-wasm/src/codegen.rs:4506 ↔ rpython/jit/metainterp/compile.py:396 — Lowering an absent fail-argument to a wasm zero placeholder adapts RPython’s None failarg representation to wasm locals. RPython preserves None through guard failargs rather than materializing a machine value.

  • pyre/pyre-jit-trace/src/jitcode_dispatch/specialize.rs:3110 ↔ rpython/jit/metainterp/optimizeopt/info.py:587 — Disabling unboxed append folding only on wasm is a backend-specific correctness fallback for a wasm deopt/resume defect, not an RPython optimization rule.

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

ℹ️ 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 +4341 to +4343
|| (!body_has_call
&& (!body_has_nonempty_list_build || nested_list_fold_ok)
&& matches!(body_instr, I::ListAppend { .. }));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Block implicit user-code ops before LIST_APPEND

When a comprehension element uses implicit dispatch such as x.__add__, a descriptor/property __get__, or x.__format__, the bytecode contains BinaryOp, LoadAttr, or FormatSimple rather than an explicit Call, so body_has_call remains false and the following LIST_APPEND is admitted. The residual-call body-effect logic already treats these as user-frame entries (pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs:4088-4091), so a later mid-body abort can still refuse the in-flight FOR_ITER delivery and drop the trace-attempt item—the same failure mode this gate avoids for explicit calls. Please include these MayForce implicit-call opcodes in the decline condition, or otherwise prove the operation is builtin-only before admitting LIST_APPEND.

Useful? React with 👍 / 👎.

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

ℹ️ 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 +4471 to +4474
I::Call { .. }
| I::CallKw { .. }
| I::CallFunctionEx
| I::CallIntrinsic1 { .. } => has_call = true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Treat set/dict builds as call-bearing before LIST_APPEND

When a comprehension appends a set or dict whose element/key type defines __hash__ or __eq__, the bytecode contains BUILD_SET/BUILD_MAP followed by LIST_APPEND but no explicit CALL, so this scan leaves body_has_call false and admits the append. Those builders route through checked dict/set construction (build_map_from_refs / build_set_from_refs), whose own comments note that hashing may run user __hash__ / __eq__; if a later walk aborts after that user frame, the in-flight FOR_ITER item can still take the existing refuse/drop path. Please classify non-empty BuildSet/BuildMap as call-bearing here unless the keys/elements are proven builtin-only.

Useful? React with 👍 / 👎.

@youknowone
youknowone force-pushed the no-token branch 3 times, most recently from bb17bd4 to de2f808 Compare July 21, 2026 08:50

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

ℹ️ 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 +4853 to +4855
|| (!body_has_call
&& (!body_has_nonempty_list_build || nested_list_fold_ok)
&& matches!(body_instr, I::ListAppend { .. }));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep nested list folds declined until roots are wired

When PYRE_NESTED_LIST_FOLD_VIRT=1, this condition admits non-empty nested-list elements like [[i] for ...], but the current tree still never populates GuardCaptureScope::sub_walk_extra_virtual_roots (the only references are the field/default and the snapshot reader). That means the append sub-walk's guard-fail resume still lacks the inner list backing block the comments describe, so enabling the gate reintroduces the null-OpRef deopt crash instead of making the shape safe; keep declining until the commit path actually supplies those roots.

Useful? React with 👍 / 👎.

Comment on lines +2487 to +2489
// non-reentrant Mutex. A parallel double-miss yields two equivalent holders
// and the later insert wins; each caller returns its own local holder, so
// the only cost is a leaked holder node.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allocate mapdict transition holders under one lock

In the parallel miss case this assumption is only true for the exact same key. If two threads add different attributes from the same self_node, both can observe the same cache_attrs().len() in new_cached_attribute_holder, creating sibling holders with duplicate order; find_branch_to_move_into uses order to decide when to stop reordering, so duplicate sibling orders can make later objects that need both attributes bounce between the two branches instead of reaching a canonical map. Reserve the order and insert while holding one critical section (or pass the reserved order into holder creation) so concurrent different-key misses cannot share an order.

Useful? React with 👍 / 👎.

…call-free Object-append comprehensions

The #171 orthodox LIST_APPEND fold descends w_list_append; its Object-strategy
arm stores a GC ref and runs list_write_barrier, a #[dont_look_inside] Void
residual. The FBW body-effect gate classified that Void residual as a live-heap
write, so an in-flight FOR_ITER item was refuse-dropped and an Object-strategy
comprehension append ([(i,i) for ...], [None ...], ["s" ...], [{i:i} ...],
[f"{i}" ...]) yielded one element short.

- Add is_list_write_barrier(addr) in jit_fnaddr.rs, matching the registered
  barrier fnaddr, and exempt it from body_effect_candidate,
  fbw_mark_executed_nonpure_residual, and the executed-effect odometer in
  try_execute_residual_call_via_executor. The barrier still executes concretely.
  This mirrors RPython's COND_CALL_GC_WB, which pyjitpl never executes
  (executor.py:446) and the optimizer never treats as a side effect.

- Widen for_iter_bodies_all_jit_safe to admit a call-free LIST_APPEND body
  (body_has_call replaces the bare-accumulator whitelist), but decline a
  non-empty nested BUILD_LIST element ([[i] for ...]): the fold virtualizes the
  inner list yet does not thread its backing block into the append commit
  sub-walk's guard-exit resume data. Empty [] and non-list containers are safe.

- Add synth/comprehension_object_append_hot.py covering the five Object-strategy
  element kinds plus int.

Assisted-by: Claude
… before codegen

An inlined comprehension trace exposed two wasm-backend gaps that the dynasm and
cranelift backends do not have (both compile the same trace):

- The wasm backend handed the optimizer's ops straight to codegen without the
  position-normalization pass the native backends run
  (dynasm prepare_ops_for_compile, cranelift normalize_ops_for_codegen_simple).
  Add normalize_ops_for_codegen and call it in compile_loop / compile_bridge so
  every non-Void-result op left unpositioned by the force path gets a stamped
  OpRef before codegen.

- emit_resolve indexed value_types with opref.raw() for a NONE operand
  (raw() == u32::MAX), panicking out of bounds when a Finish op carried a NONE
  fail-arg (a dead deopt slot). Spill a zero placeholder for a NONE operand;
  the blackhole never reads that slot back.

Assisted-by: Claude
A non-empty nested BUILD_LIST element flowing into the #171 inlined-comprehension
LIST_APPEND fold ([[i] for i in range(n)]) declines to the interpreter: the fold
virtualizes the inner list, but its separately allocated backing block (NewArray /
NewArrayClear) has no jitcode-liveness color, so collect_outer_active_boxes cannot
root it in the append commit sub-walk's guard-exit resume data and a deopt resolves
it to a null OpRef. Closing it needs the inner wrapper threaded into the guard
snapshot as an extra virtual root, so the ported resume recursion
(_visitor_walk_recursive / VArrayInfo) registers the nested virtual.

Land the opt-in scaffold for that work, default-off so it is a strict no-op:

- Add nested_list_fold_virt_enabled() (PYRE_NESTED_LIST_FOLD_VIRT, default off).
- Add GuardCaptureScope::sub_walk_extra_virtual_roots, the dormant channel for
  walker-minted virtual roots that carry no outer-frame liveness color. Empty on
  every path today; the two struct-literal sites use ..default() so they are
  unaffected.
- Gate the LIST_APPEND admit in for_iter_bodies_all_jit_safe on the flag: with it
  off the nested shape still declines via body_has_nonempty_list_build.
- Add synth/nested_list_comprehension_hot.py, the acceptance repro. With the gate
  off it runs in the interpreter and prints the correct total; with the gate on it
  reaches the fold and aborts cleanly at UnfoldableListAppendResidualUnsupported
  (try_walker_orthodox_list_append_opcode declines the nested value) — the next
  slice's target.

dynasm 227/227, cranelift 227/227, wasm 226/226.

Assisted-by: Claude
…c-deopt miscompile

The #171 orthodox LIST_APPEND fold records the Integer/Float unboxed spare-capacity
fast path guarded by GuardTrue(length < arraylen(items)). When a comprehension
result escapes the enclosing frame and a later append crosses the backing block's
realloc boundary, the wasm backend mis-resumes that capacity-guard deopt: a
frame-local list loses the reallocating append's iteration (one element short), and
a kept operand-stack list resolves to NULL (TypeError: call failed). dynasm and
cranelift resume the identical guard IR correctly, so this is a wasm-backend
deopt/resume defect.

Add wasm_unboxed_append_fold_declined() (PYRE_WASM_UNBOXED_APPEND_FOLD, default-on
for wasm32, strict no-op on native, =1 forces the fold back on for root-fix
bisecting) and gate the Integer/Float arms of orthodox_list_append_recognize on it —
both the empty->typed first-append promotion and the steady spare-capacity branch.
On wasm the unboxed append then runs as the residual jit_list_append (interpreter
fallback, correct). The Object-storage arm is unaffected and stays folded.

dynasm 227/227, cranelift 227/227, wasm 226/226.

Assisted-by: Claude
…old, inert)

Add a per-guard `extra_virtual_roots` channel that seeds the resume-data
virtual worklist with virtual op-results reachable only as a field of another
walker-minted virtual (no frame-liveness slot of their own).

- `recorder::Snapshot` gains `extra_virtual_roots: Vec<OpRef>`;
  `capture_snapshot_for_last_guard_with_vable_vref` and the walker capture
  path write it. All other snapshot construction sites default empty.
- `snapshot_map_from_trace_snapshots` flattens each recorder Snapshot's roots
  into a new position-keyed optimizer map `snapshot_extra_virtual_roots`
  (untyped SnapshotBox, 6-tuple return). New field added to OptContext,
  Optimizer, UnrollOptimizer and threaded through every snapshot_vref_boxes
  data-flow site: the max_snapshot next_pos chain, phase-move takes, phase1
  clone, phase2 remap, bridge remap_snapshot_boxes, the five
  snapshot_map_from_trace_snapshots call sites, PreparedBridgeTrace,
  prepare_bridge_trace_for_optimizer, compile_bridge/compile_entry_bridge.
  Not added to collect_snapshot_const_ptr_slots (roots carry no inline
  ConstPtr gcrefs).
- `ResumeDataVirtualAdder::finish` gains `extra_virtual_roots: &[OpRef]`; a
  seed loop after the liveboxes worklist seed registers each via
  register_virtual_box (UNASSIGNEDVIRTUAL) and pushes it into the virtual
  worklist, so the existing get_virtual_fields drain descends into it.
  store_final_boxes_in_guard reads the position map and passes the roots.

Empty on every path until PYRE_NESTED_LIST_FOLD_VIRT populates the roots, so
this is a no-op: dynasm 243/243, cranelift 243/243.

Assisted-by: Claude
`nested_list_fold_virt_enabled` flips DEFAULT-OFF -> DEFAULT-ON, admitting a
non-empty nested `BUILD_LIST` element (`[[i] for i in range(n)]`) into the
`w_list_append` fold on all backends. With the trace-time single-executor forks
retired, the inner list's separately allocated backing block is bound at every
guard-exit deopt without an extra resume-data root, so the shape compiles
bit-exact on dynasm / cranelift / wasm.

check.py: dynasm 243/243, cranelift 243/243, wasm 242/242. The acceptance repro
`bench/synth/nested_list_comprehension_hot.py` and a 5-shape stress match CPython
3.14 across all three backends; cranelift is ~27% faster gate-on. The wasm guest
cannot read the env var, so the source default is the only way to exercise it
there.

Assisted-by: Claude

@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: 1c2b54b5cb

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

/// decline (native only — the wasm guest cannot read the env var).
pub fn nested_list_fold_virt_enabled() -> bool {
static ENABLED: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
std::env::var("PYRE_NESTED_LIST_FOLD_VIRT").map_or(true, |v| v != "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.

P2 Badge Keep nested-list folding disabled by default

When PYRE_NESTED_LIST_FOLD_VIRT is unset, this map_or(true, ...) enables the nested-list append fold, so for_iter_bodies_all_jit_safe admits non-empty BUILD_LIST elements by default. Fresh evidence in the current tree is that sub_walk_extra_virtual_roots still has no writer (rg sub_walk_extra_virtual_roots only finds the field and snapshot reader), and the newly added nested-list test/benchmark still describe this as DEFAULT-OFF and expect the shape to decline; with the default on, normal runs can hit the unrooted inner-list backing-block deopt/null-OpRef path instead of staying in the interpreter.

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: 4

🤖 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-backend-wasm/src/lib.rs`:
- Around line 1064-1073: Update normalize_ops_for_codegen so each original Op is
assigned its generated position before cloning, rather than setting
normalized.pos on the clone. Preserve the existing non-Void and missing-position
conditions, and ensure downstream Operand::Op references observe the updated
original Op position.

In `@pyre/bench/synth/nested_list_comprehension_hot.py`:
- Around line 1-13: Update the benchmark rationale around
nested_list_fold_virt_enabled() to match its actual default behavior, rather
than claiming PYRE_NESTED_LIST_FOLD_VIRT is default-off or that
for_iter_bodies_all_jit_safe declines the shape by default. Preserve the
benchmark’s requirement that it prints the same total across all backends when
the gate is enabled.

In `@pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs`:
- Around line 3727-3741: Update the documentation for
sub_walk_extra_virtual_roots to remove the incorrect “DEFAULT-OFF” and “dormant”
claims, and describe its availability consistently with
nested_list_fold_virt_enabled(), whose default is enabled.
- Around line 6005-6049: Keep nested_list_fold_virt_enabled() default-on and
update all affected references: in
pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs:6005-6049 retain the new
default; update pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs:3727-3741 so
for_iter_nonempty_nested_list_comprehension_body_declines expects folding by
default or explicitly forces PYRE_NESTED_LIST_FOLD_VIRT=0; revise the
sub_walk_extra_virtual_roots documentation in
pyre/pyre-jit/src/eval.rs:10094-10114 and the benchmark header in
pyre/bench/synth/nested_list_comprehension_hot.py:1-13 to describe the
default-on behavior and opt-out.
🪄 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: 49f3edf6-3dda-4478-abfa-da07ab8b56e1

📥 Commits

Reviewing files that changed from the base of the PR and between 56644af and 1c2b54b.

📒 Files selected for processing (20)
  • majit/majit-backend-wasm/src/codegen.rs
  • majit/majit-backend-wasm/src/lib.rs
  • majit/majit-metainterp/src/history.rs
  • majit/majit-metainterp/src/optimizeopt/mod.rs
  • majit/majit-metainterp/src/optimizeopt/optimizer.rs
  • majit/majit-metainterp/src/optimizeopt/unroll.rs
  • majit/majit-metainterp/src/pyjitpl.rs
  • majit/majit-metainterp/src/pyjitpl/dispatch.rs
  • majit/majit-metainterp/src/recorder.rs
  • majit/majit-metainterp/src/resume.rs
  • majit/majit-metainterp/tests/jit_driver_runtime_parity.rs
  • pyre/bench/synth/comprehension_object_append_hot.py
  • pyre/bench/synth/nested_list_comprehension_hot.py
  • pyre/pyre-interpreter/src/jit_fnaddr.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/resume_snapshot.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/specialize.rs
  • pyre/pyre-jit-trace/src/trace_opcode.rs
  • pyre/pyre-jit/src/eval.rs

Comment on lines +1064 to +1073
.map(|(op_idx, op)| {
let normalized = (**op).clone();
let rt = normalized.result_type();
if rt != majit_ir::Type::Void && normalized.pos.get().is_none() {
normalized
.pos
.set(majit_ir::OpRef::op_typed(num_inputs + op_idx as u32, rt));
}
normalized
})

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 | 🔴 Critical | ⚡ Quick win

Mutate the original Op's position before cloning it.

Because normalize_ops_for_codegen mutates the clone (normalized.pos.set(...)), the original Op's position remains NONE.
Later operations in the trace hold Operand::Op(OpRc) references that point to the original producer ops, not the clones in the returned Vec. When codegen resolves those operands, it will read pos from the original OpRc, yielding NONE and causing the exact out-of-bounds indexing crash this function was intended to prevent.

Mutate the original Op instead so that downstream consumers see the updated position.

🐛 Proposed fix
-            let normalized = (**op).clone();
-            let rt = normalized.result_type();
-            if rt != majit_ir::Type::Void && normalized.pos.get().is_none() {
-                normalized
-                    .pos
-                    .set(majit_ir::OpRef::op_typed(num_inputs + op_idx as u32, rt));
+            let rt = op.result_type();
+            if rt != majit_ir::Type::Void && op.pos.get().is_none() {
+                op.pos.set(majit_ir::OpRef::op_typed(num_inputs + op_idx as u32, rt));
             }
-            normalized
+            (**op).clone()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.map(|(op_idx, op)| {
let normalized = (**op).clone();
let rt = normalized.result_type();
if rt != majit_ir::Type::Void && normalized.pos.get().is_none() {
normalized
.pos
.set(majit_ir::OpRef::op_typed(num_inputs + op_idx as u32, rt));
}
normalized
})
.map(|(op_idx, op)| {
let rt = op.result_type();
if rt != majit_ir::Type::Void && op.pos.get().is_none() {
op.pos.set(majit_ir::OpRef::op_typed(num_inputs + op_idx as u32, rt));
}
(**op).clone()
})
🤖 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-backend-wasm/src/lib.rs` around lines 1064 - 1073, Update
normalize_ops_for_codegen so each original Op is assigned its generated position
before cloning, rather than setting normalized.pos on the clone. Preserve the
existing non-Void and missing-position conditions, and ensure downstream
Operand::Op references observe the updated original Op position.

Comment on lines +1 to +13
# An inlined list comprehension whose LIST_APPEND element is a non-empty nested
# list (`[[i] …]` / `[[i, i + 1] …]`). The #171 fold virtualizes the inner list,
# but its separately allocated backing block (NewArray / NewArrayClear) carries
# no jitcode-liveness color, so it is not rooted in the append commit sub-walk's
# guard-exit resume data and a deopt resolves it to a null OpRef.
#
# Held back behind the DEFAULT-OFF PYRE_NESTED_LIST_FOLD_VIRT gate: while the
# gate is off `for_iter_bodies_all_jit_safe` declines the shape, so this runs in
# the interpreter and prints the correct total. It is the acceptance repro for
# the recursive virtual-list-forcing rooting work — with the gate on it must
# still print the same total on all three backends (dynasm / cranelift / wasm)
# once the rooting lands.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Comment assumes the gate defaults off — see consolidated cross-file comment.

This benchmark's rationale ("Held back behind the DEFAULT-OFF PYRE_NESTED_LIST_FOLD_VIRT gate") assumes for_iter_bodies_all_jit_safe currently declines this shape by default, which contradicts nested_list_fold_virt_enabled()'s actual default. See the consolidated comment for details.

🤖 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/bench/synth/nested_list_comprehension_hot.py` around lines 1 - 13,
Update the benchmark rationale around nested_list_fold_virt_enabled() to match
its actual default behavior, rather than claiming PYRE_NESTED_LIST_FOLD_VIRT is
default-off or that for_iter_bodies_all_jit_safe declines the shape by default.
Preserve the benchmark’s requirement that it prints the same total across all
backends when the gate is enabled.

Comment on lines +3727 to +3741

/// Extra virtual roots to seed into the guard snapshot beyond the outer
/// Python frame's jitcode-liveness boxes — walker-minted virtual op-results
/// that carry no liveness color yet must survive a deopt as fail-arg roots.
/// The motivating case is the `w_list_append` commit sub-walk over a
/// non-empty nested list element (`[[i] for i in range(n)]`): the appended
/// inner-list wrapper is passed to the sub-walk as a register arg but is not
/// named by any live outer color, so without an explicit root the resume
/// recursion never descends into it and its backing block resolves to a null
/// `OpRef`. Each entry is the `(OpRef, Type)` of a root the snapshot builder
/// must append to the top frame's boxes so the ported
/// `_visitor_walk_recursive` registers the nested virtual (wrapper + backing
/// block) in resume data. Empty on every path today: only populated behind
/// the DEFAULT-OFF `PYRE_NESTED_LIST_FOLD_VIRT` gate, so this is dormant.
pub sub_walk_extra_virtual_roots: &'a [(OpRef, majit_ir::Type)],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stale "DEFAULT-OFF" claim contradicts the gate's actual default.

The doc says extra virtual roots are "only populated behind the DEFAULT-OFF PYRE_NESTED_LIST_FOLD_VIRT gate, so this is dormant." However nested_list_fold_virt_enabled() (below, same file) is default-ON (map_or(true, ...)). This is part of a broader cross-file inconsistency — see the consolidated comment.

🤖 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/jitcode_dispatch/mod.rs` around lines 3727 - 3741,
Update the documentation for sub_walk_extra_virtual_roots to remove the
incorrect “DEFAULT-OFF” and “dormant” claims, and describe its availability
consistently with nested_list_fold_virt_enabled(), whose default is enabled.

Comment on lines +6005 to +6049
/// `PYRE_NESTED_LIST_FOLD_VIRT` gate (read once) — admits a non-empty nested
/// `BUILD_LIST` element (`[[i] for i in range(n)]`) into the orthodox
/// `w_list_append` fold. The appended value is a virtualized inner list whose
/// separately allocated backing block (`NewArray` / `NewArrayClear`) carries no
/// jitcode-liveness slot; once the trace-time single-executor forks were retired
/// the append body no longer runs under a speculative-replay sub-walk, so the
/// backing block is bound at every guard-exit deopt and the shape compiles
/// bit-exact on dynasm / cranelift / wasm (comprehension-hot acceptance repro
/// `bench/synth/nested_list_comprehension_hot.py`). Default-on; set
/// `PYRE_NESTED_LIST_FOLD_VIRT=0` to fall back to the `for_iter_bodies_all_jit_safe`
/// decline (native only — the wasm guest cannot read the env var).
pub fn nested_list_fold_virt_enabled() -> bool {
static ENABLED: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
std::env::var("PYRE_NESTED_LIST_FOLD_VIRT").map_or(true, |v| v != "0")
});
*ENABLED
}

/// `PYRE_WASM_UNBOXED_APPEND_FOLD` gate (read once) — admits the unboxed
/// (Integer/Float storage) `w_list_append` fold on the wasm backend. The fold
/// records the spare-capacity fast path guarded by `GuardTrue(length <
/// arraylen(items))`; when the comprehension result escapes the enclosing frame
/// and a later append crosses the backing block's realloc boundary, the wasm
/// backend mis-resumes that capacity-guard deopt — the reallocating append's
/// iteration is lost (list one element short) or, when the partially built list
/// is a kept operand-stack temp, the whole list resolves to NULL ("call
/// failed"). dynasm / cranelift resume the identical guard IR correctly, so this
/// is a wasm-backend deopt/resume defect. Until it is root-fixed, decline the
/// unboxed arm on wasm so the append runs as the plain residual `jit_list_append`
/// (interpreter fallback, correct if unaccelerated). DEFAULT-ON for wasm32 (the
/// backend that mis-resumes); a strict no-op on native (the fold always folds
/// there). Set `PYRE_WASM_UNBOXED_APPEND_FOLD=1` to force the fold back on for
/// bisecting the root fix. The Object-storage arm is unaffected — it resumes
/// correctly and stays folded.
fn wasm_unboxed_append_fold_declined() -> bool {
static DECLINED: std::sync::LazyLock<bool> = std::sync::LazyLock::new(|| {
if cfg!(target_arch = "wasm32") {
std::env::var("PYRE_WASM_UNBOXED_APPEND_FOLD").map_or(true, |v| v != "1")
} else {
false
}
});
*DECLINED
}

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate every reference to the gate and its dependent field to determine
# the intended default and whether tests/CI override it.
rg -n "PYRE_NESTED_LIST_FOLD_VIRT" -C3
rg -n "sub_walk_extra_virtual_roots" -C3
rg -n "nested_list_fold_virt_enabled" -C3

Repository: youknowone/pyre

Length of output: 153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== gate definition ==\n'
sed -n '5998,6055p' pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs

printf '\n== extra roots doc ==\n'
sed -n '3718,3750p' pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs

printf '\n== eval test ==\n'
sed -n '10086,10120p' pyre/pyre-jit/src/eval.rs

printf '\n== benchmark header ==\n'
sed -n '1,20p' pyre/bench/synth/nested_list_comprehension_hot.py

printf '\n== all references ==\n'
rg -n "PYRE_NESTED_LIST_FOLD_VIRT|nested_list_fold_virt_enabled|sub_walk_extra_virtual_roots|for_iter_nonempty_nested_list_comprehension_body_declines|nested_list_comprehension_hot" pyre -C 2

Repository: youknowone/pyre

Length of output: 12864


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== env override references ==\n'
rg -n "PYRE_NESTED_LIST_FOLD_VIRT=0|PYRE_NESTED_LIST_FOLD_VIRT=1|PYRE_NESTED_LIST_FOLD_VIRT" . -g '!target' -g '!node_modules' -C 2

printf '\n== gate call sites ==\n'
rg -n "nested_list_fold_virt_enabled\(" pyre -C 2

printf '\n== eval test neighbors ==\n'
sed -n '10060,10140p' pyre/pyre-jit/src/eval.rs

Repository: youknowone/pyre

Length of output: 12178


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== for_iter_bodies_all_jit_safe definition and callers ==\n'
rg -n "fn for_iter_bodies_all_jit_safe|for_iter_bodies_all_jit_safe\(" pyre/pyre-jit/src -C 3

printf '\n== nested_list_fold_virt_enabled callers ==\n'
rg -n "nested_list_fold_virt_enabled\(" pyre/pyre-jit-trace/src pyre/pyre-jit/src -C 3

printf '\n== gate-related shape classification ==\n'
rg -n "UnsupportedJitShape::CurrentFrameOnly|sub_walk_extra_virtual_roots|nested list" pyre/pyre-jit/src/eval.rs pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs -C 3

Repository: youknowone/pyre

Length of output: 18264


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '4961,5040p' pyre/pyre-jit/src/eval.rs

Repository: youknowone/pyre

Length of output: 4838


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '5040,5125p' pyre/pyre-jit/src/eval.rs

Repository: youknowone/pyre

Length of output: 4413


PYRE_NESTED_LIST_FOLD_VIRT's default-on flip leaves the nested-list decline test and docs stale.

nested_list_fold_virt_enabled() now returns true by default, so for_iter_nonempty_nested_list_comprehension_body_declines will assert the opposite behavior unless the test environment forces PYRE_NESTED_LIST_FOLD_VIRT=0. Update that test, the sub_walk_extra_virtual_roots doc, and the benchmark header to the new default, or restore the old default if the decline is still intended.

📍 Affects 3 files
  • pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs#L6005-L6049 (this comment)
  • pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs#L3727-L3741
  • pyre/pyre-jit/src/eval.rs#L10094-L10114
  • pyre/bench/synth/nested_list_comprehension_hot.py#L1-L13
🤖 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/jitcode_dispatch/mod.rs` around lines 6005 - 6049,
Keep nested_list_fold_virt_enabled() default-on and update all affected
references: in pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs:6005-6049 retain
the new default; update
pyre/pyre-jit-trace/src/jitcode_dispatch/mod.rs:3727-3741 so
for_iter_nonempty_nested_list_comprehension_body_declines expects folding by
default or explicitly forces PYRE_NESTED_LIST_FOLD_VIRT=0; revise the
sub_walk_extra_virtual_roots documentation in
pyre/pyre-jit/src/eval.rs:10094-10114 and the benchmark header in
pyre/bench/synth/nested_list_comprehension_hot.py:1-13 to describe the
default-on behavior and opt-out.

- rustfmt (mod.rs:6346, unroll.rs:956): reflow the extra_virtual_roots
  snapshot_get / std::mem::take to satisfy the CI rustfmt.
- eval.rs test: with the nested-list fold gate default-on, a non-empty nested
  `BUILD_LIST` element is admitted, so the standalone
  `for_iter_nonempty_nested_list_comprehension_body_declines` assertion no
  longer holds. Fold its two shapes into
  `for_iter_value_producing_list_append_comprehension_body_is_jit_safe`
  (asserts admit + UnsupportedJitShape::None) and drop the decline test.

Assisted-by: Claude
@youknowone
youknowone merged commit 0364c46 into main Jul 21, 2026
29 of 31 checks passed
@youknowone
youknowone deleted the no-token branch July 21, 2026 19:50
youknowone added a commit that referenced this pull request Jul 21, 2026
…tra_virtual_roots build break (#708)

* comments: remove internal task/slice/gap tracking tags

Session-invented tracking labels prefixed comments across 50 files:
`task#50 phase-1`, `task #157`, `#73 S3.5`, `gh#73 S3.2`,
`Parity #14 Slice C.4`, `Slice 7b`, `Slice C`, `sub-slice 4`,
`micro-slice 3`, `gap-10`, `#203 gap-7`, `51d.1`, `M4`, `Phase G slice 2`.
Each occurrence is replaced by the technical statement it prefixed; where
the tag carried the sentence's subject the sentence is reworded.

Dropped in the same lines: two claude memory-file paths cited as
references (`item3_abstractstringrepr_epic_plan.md`,
`project_issue73_architecture_walker_as_tracer_2026_05_28`), and the line
numbers on file citations those comments carried.

The `residual_call.rs` "Priority order for sub-slice 2 (widen)" paragraph
is removed rather than reworded: it ordered work the same doc records as
landed.

Comment-only — no changed line is code.
`cargo check --workspace --all-targets --features dynasm` passes; the
pre-existing `vstack_mirror.rs` unreachable_patterns is the only warning.

Assisted-by: Claude

* majit-metainterp: initialize Snapshot::extra_virtual_roots at the guard-op multi-frame capture

`capture_snapshot_for_last_guard_op_multi_frame_with_vable_vref` built a
`recorder::Snapshot` without the `extra_virtual_roots` field, so
majit-metainterp failed to compile with E0063 at `origin/main`
`57b01d0e8bd`. The field was added by #661; this call site was not
updated.

Value and comment copied from the sibling
`capture_snapshot_for_last_guard_multi_frame_with_vable_vref`, the same
multi-frame path: the nested-list append fold resumes through the
single-frame collapse, so no extra virtual roots reach here.

Assisted-by: Claude

* comments: remove AI-review labels, session plan/memory references, and remaining stage tags

Second pass over the categories the first sweep did not cover.

- `Codex P1` / `Codex P2` (+ `(PR #89)`, `(round 7/8/10)`) AI-review
  priority labels, 21 sites. The sentence each prefixed is kept.
- References to files that exist only in a private session directory:
  `.claude/plans/*.md` (5), `memory/*.md` (6), and one
  `project_issue73_...` memory name.
- Plan-stage identifiers: `S2.1`/`S2.2`/`S2.3`/`S2.4` "(wiggly-barto
  plan)", `S1.3`, `S0 spike`, `S1-S3`, `orth-9 step 4`,
  `Path B (B.6.7*)`, `[SPIKE-S0/FR]`, `A0-era`, `off-GC storage epic
  S2..S5`, `Task #85`/`#197`/`#333`, `#73 Slice-1`.

Three changed lines are not comments, all string literals: the `S1.3`
mentions in the `AnnotatorError` text and in an `expect_err` message in
`rlib/jit.rs` / `extregistry.rs`, and a `memory/*.md` citation inside an
`#[ignore = "..."]` reason string in `pyre-jit/src/eval.rs`. Every other
changed line is a comment.

`Finding #1`/`#2`/`#3` and `Option C` are kept: they are defined by the
`#57` GitHub issue cited alongside them, so a reader can resolve them.

`cargo check --workspace --all-targets --features dynasm` passes.

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