Skip to content

wasm: stamp w_class on NEW_WITH_VTABLE; drop the wasm-only LIST_APPEND residual decline - #770

Merged
youknowone merged 4 commits into
mainfrom
wasm-jit
Jul 25, 2026
Merged

wasm: stamp w_class on NEW_WITH_VTABLE; drop the wasm-only LIST_APPEND residual decline#770
youknowone merged 4 commits into
mainfrom
wasm-jit

Conversation

@youknowone

@youknowone youknowone commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Four commits on top of main, all found by chasing the largest wasm-vs-dynasm
runtime gaps in pyre/check.py --synthetic-only.

wasm: stamp w_class on NEW_WITH_VTABLE

fuse_boxing_alloc lowers malloc_typed(W_IntObject { .. }) to
NewWithVtable + a payload store, dropping the boxing ctor's ob_header
subtree on the contract that the backend stamps both ob_type (the vtable)
and w_class (get_instantiate(vtable_type)) from the size descr. dynasm's
genop_new_with_vtable does this; the wasm backend wrote only the vtable.

wasm also zero-fills the nursery on reset, so the missing store left
w_class == 0, and the promoted GuardValue(w_class, int_typeobj) that
OptVirtualize folds those header reads to failed on every iteration of any
loop that escapes a freshly boxed builtin.

synth/list_reverse on wasm: executes=233267, decl_shortcircuit=233065
executes=13, decl_shortcircuit=0; 52.8x → 4.7x vs CPython.

cranelift's NewWithVtable has the same omission. It is latent there only
because that nursery is not zeroed on reset, so recycled memory usually still
holds a plausible w_class; not fixed here.

jit: drop the wasm-only LIST_APPEND residual-fallthrough safeguard

The top three wasm-vs-dynasm gaps were all one arm in
jitcode_dispatch/residual_call.rs. #749 let a declined LIST_APPEND fold fall
through to the generic jit_list_append residual, but kept
if cfg!(target_arch = "wasm32") { return Err(UnfoldableListAppendResidualUnsupported) }
because wasm miscompiled the resulting resize/append bridge. That aborted the
bridge on every guard failure, forever: FIRED=4984, sbt_entered=4984,
cb_entered=0, bridge_diag entered=0 — nothing ever compiled.

Removing it (9 deleted lines, no replacement — the backend split disappears):

bench before after wasm/dynasm
synth/nested_list_comprehension_hot 19.70s 1.42s 59.8x → 2.4x
synth/comprehension_object_append_hot 7.78s 0.36s 34.0x → 2.3x
synth/const_arg_call_resume 3.16s 0.29s 21.1x → 1.9x

⚠️ The element drop that safeguard guarded against is the wasm offset-0
silent-null class: wasm32 linear-memory offset 0 is valid memory, so a null
Ref is a layout-dependent wrong answer rather than a trap, and it does not
reproduce on macOS aarch64 in principle. An ablation (safeguard off and the
w_class stamp above reverted) still passed locally, so the w_class fix is
not proven to be what cured it — #737's bridge-iter journal root is the likelier
fixer. Linux CI is the real gate for this commit. If it reddens, hunt the
null Ref in the append-residual bridge rather than restoring the arm.

bench: commit jit-stats baselines for the three append-residual synth benches

Nothing caught the above: run_synthetic_bench passes vs_pypy=None for wasm,
so the max-pypy-ratio header is not read there and the per-bench timeout alone
let the pre-fix 8.58s run pass. check.py's loops_aborted /
internal_compile_panics regression floor would have caught it, but
pyre/.gitignore treated every bench/synth/*.jitstats as local scratch.

Records and commits the baselines for these three benches (loops_aborted =
2 / 2 / 1, byte-identical on dynasm and cranelift; the residual aborts are
LoopBearingCalleeInlineUnsupported { pc: 116 } on the module-level trace) with
matching ! exceptions in pyre/.gitignore. Verified by writing
loops_aborted=0 into the wasm baseline: SNAPDIFF jit-stats regression: loops_aborted 0 -> 2.

jit: exempt the FOR_ITER loop-variable store from the in-flight body-effect guard

Pre-existing commit carried on this branch; a module-level loop-variable
STORE_NAME was classified as a body effect and aborted the trace with
VableEscaped.

Verification

pyre/check.py locally (macOS aarch64): dynasm 309/309, cranelift 309/309,
wasm 306/306.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved WebAssembly object creation so runtime type information is initialized correctly.
    • Fixed loop tracing behavior to preserve accurate loop-variable handling and exception state observation.
    • Improved WebAssembly list-append tracing behavior, reducing unnecessary tracing aborts.
  • Tests

    • Added a benchmark covering sys.exc_info() behavior inside and after repeated exception handlers.
    • Added and updated JIT benchmark statistics for WebAssembly and native execution paths.

…effect guard

A module/global-scope `for i in …` lowers the loop variable to a STORE_NAME /
STORE_GLOBAL residual (not a STORE_FAST frame local). Executed on the
authoritative walk while the FOR_ITER item is in flight, that store set
`body_effect_since_consume` (residual_call.rs body_effect_candidate), so a later
`VableEscapedDuringResidualCall` abort in the loop body made
`fbw_foriter_inflight_take` refuse re-delivery and drop the iteration — resuming
at the FOR_ITER header re-consumed the iterator and skipped the item, losing the
body's effects (e.g. the `exc_info_inside` / `exc_info_after` counters in
exception_metadata_hot).

Re-delivery re-runs the body from the FOR_ITER continue-arm fallthrough, which
re-binds the SAME re-delivered item to the SAME name — an idempotent write, not
an accumulating double. Exempt it from the R1 body-effect accounting when the
store's py_pc is the in-flight entry's body_pc, matching the existing
`is_idempotent_gc_barrier` treatment. Add `fbw_foriter_inflight_top_body_pc` and
a `exc_info_module_loop_hot` regression bench.

Assisted-by: Claude
`fuse_boxing_alloc` (majit-translate model.rs) lowers a boxing constructor such
as `malloc_typed(W_IntObject { … })` to `NewWithVtable` + the payload store,
dropping the `ob_header` subtree on the contract that the backend stamps both
`ob_type` and `w_class` from the size descr — `w_class_obj()` (majit-ir
descr.rs) documents that a `new_with_vtable` inherits
`get_instantiate(vtable_type)` and that OptVirtualize folds `w_class` header
reads to that constant. The wasm backend wrote only the vtable, so a
materialized box carried whatever `w_class` bytes the allocation left behind.

On wasm those bytes are zero (`Nursery::reset` memsets on this target), so a
JIT-allocated `W_IntObject` had `w_class == null` while an interpreter-created
one carries the `int` type object. In a loop whose induction variable escapes
into the heap (`while i < N: lst.append(i); i = i + 1`) the promoted
`GuardValue(w_class, int_typeobj)` then failed on every trace entry; the
recovery bridge declined at setup and `declined_bridge_guards` short-circuited
every later failure to the interpreter.

Stamp `w_class` after the vtable write, mirroring dynasm
`genop_new_with_vtable`. On `append_only` (700k appends) trace entries drop
from 233267 to 13 and the declined-bridge short-circuit from 233065 to 0;
`synth/list_reverse` goes 52.8x -> 4.7x against CPython. wasm synthetic suite
289/289.

Assisted-by: Claude
#749 routed a LIST_APPEND fold decline (dominantly the realloc boundary) into
the generic `jit_list_append` residual instead of aborting the trace, but kept
a `cfg!(target_arch = "wasm32")` arm that still returned
`UnfoldableListAppendResidualUnsupported`, because the wasm backend then
miscompiled the resize/append bridge into an element drop.

That arm aborts the bridge on every guard failure. On
`nested_list_comprehension_hot` the wasm run enters `must_compile` 997002 times
against 998000 trace entries, starts bridge tracing 4984 times, and reaches
`compile_bridge` zero times; the abort census is
`UnfoldableListAppendResidualUnsupported` 4992 (dynasm: 0, and it compiles six
bridges). The trace records 25-35 body ops before aborting, so the
zero-body-op structural-decline path never latches it and every attempt is
retried.

The drop no longer reproduces: with the arm removed, `check.py` is
dynasm 309/309, cranelift 309/309, wasm 306/306, and the wasm times drop from
19.70s to 1.42s (`comprehension_object_append_hot`), 7.78s to 0.36s
(`nested_list_comprehension_hot`), and 3.16s to 0.29s
(`const_arg_call_resume`) — 34.0x/59.8x/21.1x against dynasm down to
2.3x/2.4x/1.9x. Five apparent regressions in the same table re-measure at
their pre-change times.

The element drop belongs to the wasm offset-0 silent-null class, whose effect
is layout-dependent and benign on macOS aarch64, so this local run does not
cover the Linux x86_64 manifestation; CI does.

Assisted-by: Claude
… benches

check.py's regression floor (`_jit_stats_regression_floor`) fails a run when
`loops_aborted` or `internal_compile_panics` rises above the committed
`<bench>.<backend>.jitstats` baseline. Only `pyre/bench/*.py` carried such
baselines; `pyre/.gitignore` treated every `bench/synth/*.jitstats` as local
scratch, so no synthetic bench was gated on those counters.

Record and commit the baselines for the three benches the removed wasm-only
`ListAppendValue` residual decline covered, and add matching `!` exceptions to
`pyre/.gitignore`. Recorded values:

  nested_list_comprehension_hot    loops_aborted=2
  comprehension_object_append_hot  loops_aborted=2
  const_arg_call_resume            loops_aborted=1

dynasm and cranelift record byte-identical counters, and the residual aborts
are `LoopBearingCalleeInlineUnsupported { pc: 116 }` on the module-level trace
in all three benches, so the values come from the backend-independent tracer.

This is the only gate that covers wasm on these benches: `run_synthetic_bench`
passes `vs_pypy=None` for wasm, so its `max-pypy-ratio` header is not read, and
the per-bench timeout alone let the pre-fix 8.58s run pass. With the decline
arm in place the bridge aborted on every guard failure (4984 aborts recorded in
`PYRE_WASM_JIT_STATS`), which the floor now reports as
`jit-stats regression: loops_aborted 2 -> N`.

Verified by writing `loops_aborted=0` into the wasm baseline and running
`check.py --backend wasm`: `SNAPDIFF jit-stats regression: loops_aborted 0 -> 2`.

Assisted-by: Claude
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Wasm allocation now materializes w_class; FOR_ITER residual handling refines loop-variable store detection and wasm LIST_APPEND fallback behavior. Synthetic exception-loop benchmarks and JIT statistics fixtures are added.

Changes

Wasm and JIT behavior

Layer / File(s) Summary
Wasm class metadata stamping
majit/majit-backend-wasm/src/codegen.rs
NewWithVtable allocations derive the w_class descriptor field and conditionally store its pointer-width value.
FOR_ITER residual handling
pyre/pyre-jit-trace/src/jitcode_dispatch/fbw_state.rs, pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs
Top in-flight FOR_ITER body coordinates are exposed, loop-variable binding stores are excluded from body-effect detection, and the wasm LIST_APPEND fallback abort is removed.
Synthetic benchmark coverage
pyre/bench/synth/*, pyre/.gitignore
Adds an exception-loop benchmark, records Cranelift, Dynasm, and wasm JIT statistics, and permits selected statistics files to be committed.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Poem

A rabbit bounds through wasm’s field,
While loops keep their traces sealed.
Exceptions hop, stats softly glow,
Class pointers find where they should go.
“Hop!” says Bun, “the tests now grow!”

🚥 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 accurately describes two of the main wasm changes in the pull request.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wasm-jit

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 c657d63).
Updated: 2026-07-25T02:50:17.892Z

Files in the reviewed diff
majit/majit-backend-wasm/src/codegen.rs
pyre/.gitignore
pyre/bench/synth/comprehension_object_append_hot.cranelift.jitstats
pyre/bench/synth/comprehension_object_append_hot.dynasm.jitstats
pyre/bench/synth/comprehension_object_append_hot.wasm.jitstats
pyre/bench/synth/const_arg_call_resume.cranelift.jitstats
pyre/bench/synth/const_arg_call_resume.dynasm.jitstats
pyre/bench/synth/const_arg_call_resume.wasm.jitstats
pyre/bench/synth/exc_info_module_loop_hot.py
pyre/bench/synth/nested_list_comprehension_hot.cranelift.jitstats
pyre/bench/synth/nested_list_comprehension_hot.dynasm.jitstats
pyre/bench/synth/nested_list_comprehension_hot.wasm.jitstats
pyre/pyre-jit-trace/src/jitcode_dispatch/fbw_state.rs
pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs

1. Regressions to PyPy parity introduced by this patch

  • pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs:1520 ↔ pypy/interpreter/pyopcode.py:872: The new exemption treats every loop-header STORE_NAME as idempotent. PyPy implements it through space.setitem_str(...), which can invoke a custom locals mapping’s write behavior; replaying it can therefore perform an observable second mutation. Main conservatively recorded it as a body effect. STORE_GLOBAL has the same generic setitem_str route when jitted (pypy/objspace/std/celldict.py:328).

2. Other mismatches introduced by this patch

None.

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

None.

4. Structural adaptations

  • majit/majit-backend-wasm/src/codegen.rs:3829 ↔ rpython/jit/backend/llsupport/llmodel.py:778: Initializing Pyre’s additional PyObject.w_class header field has no literal RPython equivalent; RPython’s NEW_WITH_VTABLE writes only its type pointer. This is a necessary Rust/Pyre object-layout adaptation, consistent with RPython’s instance-class initialization at rpython/rtyper/rclass.py:742.
  • majit/majit-backend-wasm/src/codegen.rs:3840 ↔ rpython/jit/backend/llsupport/llmodel.py:781: The i32 pointer-width store is wasm32-specific, whereas upstream uses target-width WORD; this is an implementation-target adaptation, not a parity issue.
  • pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs:2668 ↔ pypy/objspace/std/frame.py:17: Enabling the fallback jit_list_append residual preserves the specialized PyPy LIST_APPEND behavior for an exact internal list, but Pyre reaches it through CPython-compatible compiler/JitCode lowering rather than PyPy’s bytecode dispatch shape.

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

🤖 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/bench/synth/comprehension_object_append_hot.cranelift.jitstats`:
- Line 4: Resolve or explicitly reclassify the known loops_aborted cases before
updating baselines, preserving pyre/check.py’s healthy zero expectation: adjust
pyre/bench/synth/comprehension_object_append_hot.cranelift.jitstats:4-4 and
comprehension_object_append_hot.dynasm.jitstats:4-4 after fixing their aborts,
keep pyre/bench/synth/comprehension_object_append_hot.wasm.jitstats:2-2 from
weakening wasm’s sole JIT regression gate, and remove the aborts from
pyre/bench/synth/const_arg_call_resume.cranelift.jitstats:4-4 and
const_arg_call_resume.dynasm.jitstats:4-4.

In `@pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs`:
- Around line 1505-1528: Regenerate the Charon .ullbc extraction after updating
residual_call.rs, including the related fbw_state.rs change, then run all eight
required JIT benchmarks and verify there is no regression; include the
regenerated artifacts and benchmark evidence with the change.
🪄 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 Plus

Run ID: 7e564bba-d2e4-46ff-aaaa-7b36bee02fde

📥 Commits

Reviewing files that changed from the base of the PR and between c38f0f2 and c657d63.

📒 Files selected for processing (14)
  • majit/majit-backend-wasm/src/codegen.rs
  • pyre/.gitignore
  • pyre/bench/synth/comprehension_object_append_hot.cranelift.jitstats
  • pyre/bench/synth/comprehension_object_append_hot.dynasm.jitstats
  • pyre/bench/synth/comprehension_object_append_hot.wasm.jitstats
  • pyre/bench/synth/const_arg_call_resume.cranelift.jitstats
  • pyre/bench/synth/const_arg_call_resume.dynasm.jitstats
  • pyre/bench/synth/const_arg_call_resume.wasm.jitstats
  • pyre/bench/synth/exc_info_module_loop_hot.py
  • pyre/bench/synth/nested_list_comprehension_hot.cranelift.jitstats
  • pyre/bench/synth/nested_list_comprehension_hot.dynasm.jitstats
  • pyre/bench/synth/nested_list_comprehension_hot.wasm.jitstats
  • pyre/pyre-jit-trace/src/jitcode_dispatch/fbw_state.rs
  • pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs

bridges_compiled=18
guard_failures=3700
internal_compile_panics=0
loops_aborted=2

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 | 🏗️ Heavy lift

The committed baselines weaken the loops_aborted regression gate.

pyre/check.py documents healthy baselines as zero, but these fixtures commit existing aborts. Fix or explicitly reclassify the underlying aborts before recording baselines:

  • pyre/bench/synth/comprehension_object_append_hot.cranelift.jitstats#L4-L4: change the nonzero loops_aborted baseline after resolving the abort.
  • pyre/bench/synth/comprehension_object_append_hot.dynasm.jitstats#L4-L4: change the nonzero loops_aborted baseline after resolving the abort.
  • pyre/bench/synth/comprehension_object_append_hot.wasm.jitstats#L2-L2: avoid weakening wasm’s only stated JIT regression gate.
  • pyre/bench/synth/const_arg_call_resume.cranelift.jitstats#L4-L4: remove the known abort from the healthy baseline.
  • pyre/bench/synth/const_arg_call_resume.dynasm.jitstats#L4-L4: remove the known abort from the healthy baseline.
📍 Affects 5 files
  • pyre/bench/synth/comprehension_object_append_hot.cranelift.jitstats#L4-L4 (this comment)
  • pyre/bench/synth/comprehension_object_append_hot.dynasm.jitstats#L4-L4
  • pyre/bench/synth/comprehension_object_append_hot.wasm.jitstats#L2-L2
  • pyre/bench/synth/const_arg_call_resume.cranelift.jitstats#L4-L4
  • pyre/bench/synth/const_arg_call_resume.dynasm.jitstats#L4-L4
🤖 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/comprehension_object_append_hot.cranelift.jitstats` at line
4, Resolve or explicitly reclassify the known loops_aborted cases before
updating baselines, preserving pyre/check.py’s healthy zero expectation: adjust
pyre/bench/synth/comprehension_object_append_hot.cranelift.jitstats:4-4 and
comprehension_object_append_hot.dynasm.jitstats:4-4 after fixing their aborts,
keep pyre/bench/synth/comprehension_object_append_hot.wasm.jitstats:2-2 from
weakening wasm’s sole JIT regression gate, and remove the aborts from
pyre/bench/synth/const_arg_call_resume.cranelift.jitstats:4-4 and
const_arg_call_resume.dynasm.jitstats:4-4.

Comment on lines +1505 to 1528
// The loop-variable binding store is the op at the in-flight FOR_ITER's
// `body_pc` (the FOR_ITER continue-arm fallthrough), a STORE_NAME/
// STORE_GLOBAL that writes the just-consumed item to the loop target (a
// module/global-scope `for i in …`; a function-scope loop var is a
// STORE_FAST frame local that never becomes a residual). Re-delivery
// re-runs the body from `body_pc`, re-storing the SAME re-delivered item to
// the SAME name — an idempotent write, never an accumulating double. Like
// the `is_idempotent_gc_barrier` write barrier it still EXECUTES concretely
// (the module dict must hold the binding for the walk's remaining reads) but
// it is not a body effect: keep it out of the R1 in-flight-FOR_ITER
// accounting so an escaping residual later in the same body does not
// refuse-drop the whole iteration.
// `vstack_cur_pypc` points one past the executing op (next-instr
// convention), while `body_pc` is the store's own py_pc, so the loop-var
// store satisfies `vstack_cur_pypc == body_pc + 1`.
let is_loop_var_binding_store = matches!(
helper,
majit_ir::PyreHelperKind::StoreName | majit_ir::PyreHelperKind::StoreGlobal
) && fbw_foriter_inflight_top_body_pc()
.is_some_and(|body_pc| body_pc + 1 == ctx.vstack_cur_pypc as usize);
let body_effect_candidate = !provably_side_effect_free
&& !is_idempotent_gc_barrier
&& !is_loop_var_binding_store
&& writes_live_heap

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)pyre/pyre-jit-trace/src/jitcode_dispatch/(residual_call|fbw_state)\.rs$|extract-llbc\.py|README|benchmark' || true

echo "== tracked changes/stat =="
git diff --stat || true

echo "== residual_call helper call context =="
if [ -f pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs ]; then
  wc -l pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs
  sed -n '1470,1545p' pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs | cat -n
fi

echo "== fbw_state helper context =="
if [ -f pyre/pyre-jit-trace/src/jitcode_dispatch/fbw_state.rs ]; then
  wc -l pyre/pyre/jit-trace/src/jitcode_dispatch/fbw_state.rs
  sed -n '660,710p' pyre/pyre-jit-trace/src/jitcode_dispatch/fbw_state.rs | cat -n
fi

echo "== git diff around JIT files =="
git diff -- pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs pyre/pyre-jit-trace/src/jitcode_dispatch/fbw_state.rs | sed -n '1,220p' || true

echo "== search benchmark run / ullbc evidence =="
git diff -- pyre/pyre-jit-trace | rg -n '(benchmark|eight|ULLBC|ullbc|Charon|Charon|scripts/extract-llbc\.py|all eight|no-regression|regression)' || true

echo "== possible committed artifact changes in diff =="
git diff --name-only -- pyre | rg '\.ullbc$|pyre/pyre-jit-trace' || true

Repository: youknowone/pyre

Length of output: 7742


🌐 Web query:

RPython Charon ULLBC extraction scripts extract-llbc.py Pyre JIT changes Charon .ullbc

💡 Result:

Charon is a tool designed to interface between the Rust compiler (rustc) and program verification projects by converting Rust's Mid-level Intermediate Representation (MIR) into more stable, simplified formats [1][2]. These formats include ULLBC (Unstructured Low-Level Borrow Calculus) and LLBC (Low-Level Borrow Calculus), which are serialized as JSON files with .ullbc and .llbc extensions [1][3]. ULLBC represents a cleaned-up, slightly simplified version of MIR, while LLBC further restructures the control-flow to replace gotos with higher-level constructs like loops and if-then-else blocks [3]. In the context of the pyre project—a fast, compliant Rust-based Python interpreter that utilizes the MaJIT meta-tracing JIT framework—extract-llbc.py is a Python utility script used to automate the extraction of these artifacts [4][5]. Key aspects of this process include: - Deterministic Extraction: The script computes source fingerprints over Git-tracked inputs to manage extraction and caching [4]. It skips unnecessary re-extractions unless the inputs have changed or a --force flag is used [4]. - Environment Setup: It helps maintain stamp files per crate and handles environment configuration, such as MSVC link detection on Windows, replacing older, less robust shell scripts [4]. - Integration: The script is used within pyre's development workflow (e.g., python3 scripts/extract-llbc.py <target>) to ensure that the required .ullbc and .llbc artifacts are present for the JIT analysis and translation pipeline [4][6]. - JIT Context: By using Charon to extract MIR-derived artifacts, pyre can perform static analysis on the Rust source (via majit-analyze) to facilitate JIT compilation, serving a role similar to RPython's translation process in PyPy but tailored for a Rust-based architecture [5]. Documentation for the ULLBC structure itself can be found in the Charon library's internal definitions, specifically in modules like ullbc_ast.rs, which define the AST and serialization formats used [7][8].

Citations:


Run the required artifact regeneration and benchmark evidence for this JIT change.

pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs now changes JIT source, so the corresponding Charon .ullbc extraction must be regenerated and all eight benchmarks must run with no regression before this change can be accepted.

📍 Affects 2 files
  • pyre/pyre-jit-trace/src/jitcode_dispatch/residual_call.rs#L1505-L1528 (this comment)
  • pyre/pyre-jit-trace/src/jitcode_dispatch/fbw_state.rs#L685-L695
🤖 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/residual_call.rs` around lines 1505
- 1528, Regenerate the Charon .ullbc extraction after updating residual_call.rs,
including the related fbw_state.rs change, then run all eight required JIT
benchmarks and verify there is no regression; include the regenerated artifacts
and benchmark evidence with the change.

Source: Coding guidelines

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