wasm: dead frame, guard cells and residual-call signatures on their upstream owners - #1960
Conversation
|
Important Review skippedToo many files! This PR contains 104 files, which is 4 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (104)
You can disable this status message by setting the 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 5ca7b9f). Files in the reviewed diffCodex did not produce a report (exit 1). Last log lines: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb3c20f8fd
ℹ️ 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".
| pyre_interpreter::shutdown::finalize_runtime( | ||
| canonical, | ||
| pyre_interpreter::call::getexecutioncontext(), | ||
| ); |
There was a problem hiding this comment.
Keep the web wasm runtime alive between runs
run_python_impl is also the #[wasm_bindgen] run_python entry point, and the bundled web UI invokes it again on every button click. Calling finalize_runtime here permanently marks the interpreter finalizing and detaches/clears its module state, but the next click reuses the same wasm instance rather than creating a fresh interpreter; subsequent browser runs therefore execute against torn-down sys/import/thread state. Finalize only the one-shot wasm-host path, or reset the runtime before allowing another web invocation.
Useful? React with 👍 / 👎.
| let cache = MODULE_CACHE.get_or_init(|| Mutex::new(indexmap::IndexMap::new())); | ||
| cache.lock().retain(|_, handle| *handle != func_id); |
There was a problem hiding this comment.
Reference-count cached wasm table handles before freeing
compile_module_cached intentionally returns one handle for byte-identical modules, while each compilation records that handle in its own LoopAsmResources and now frees it when its token is released. Removing the cache entry and calling the host free on the first release invalidates the same slot for any still-live cached owner; when that slot is reused, its next JIT dispatch can reach the wrong trace (or a null table entry). Track owners/refcounts per cached handle, or avoid sharing handles across independently released resources.
Useful? React with 👍 / 👎.
| let out = Command::new("/usr/bin/time") | ||
| .args(["-l", "-o"]) | ||
| .arg(&rss_path) |
There was a problem hiding this comment.
Use a portable RSS collector in the integration test
This invokes BSD /usr/bin/time -l, but Linux uses GNU time (with -v, not -l), and the review Linux image does not even provide /usr/bin/time; on Linux the command either cannot spawn or exits on the unsupported flag before pyre-dynasm runs. Since this dynasm-gated integration test is part of the required workspace cargo-test suite, the Ubuntu test job will fail instead of validating the memory ceiling. Use platform-specific collection or avoid an external time dependency.
AGENTS.md reference: AGENTS.md:L277-L283
Useful? React with 👍 / 👎.
| if self.ceiling != 0 && desired > self.ceiling { | ||
| emit_once(); | ||
| return Ok(false); | ||
| } | ||
| Ok(true) |
There was a problem hiding this comment.
Charge guest linear-memory growth against the process cap
The wasm guest limiter compares the guest memory's desired size only with ceiling; it never adds the growth delta to the host allocator's CHARGED total. Thus a runner with, for example, 200 MiB of host allocations can still grow guest memory to a 512 MiB PYRE_MAX_MEMORY limit, exceeding the advertised process ceiling by the host allocation (and the reverse is also possible). Reserve/release the guest-memory delta through the same accounting before accepting growth.
Useful? React with 👍 / 👎.
cb3c20f to
f56b244
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f56b244432
ℹ️ 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".
| let mut cmd = match time_flag { | ||
| Some(flag) => { | ||
| let mut cmd = Command::new("/usr/bin/time"); | ||
| cmd.args([flag, "-o"]).arg(&rss_path).arg(PYRE); |
There was a problem hiding this comment.
Avoid GNU-only
-o when invoking macOS time
Beyond the already-reported Linux flag issue, the macOS branch still passes GNU -o to BSD /usr/bin/time. macOS time(1) documents the synopsis as time [-l] [-p] utility ..., so this exits on the unrecognized option before pyre-dynasm runs; both dynasm-gated integration tests then fail their required stderr assertion on the macOS cargo-test job. Capture BSD time's stderr separately or use a macOS-specific collector. macOS time(1)
AGENTS.md reference: AGENTS.md:L277-L283
Useful? React with 👍 / 👎.
| let file = OpenFile { | ||
| data: Vec::new(), | ||
| pos: 0, | ||
| is_dir: false, | ||
| stdio: true, | ||
| }; |
There was a problem hiding this comment.
Model stdout and stderr as write-only descriptors
In wasm runs, this initializes fds 0, 1, and 2 identically as empty non-directory files. Consequently os.read(1, 1) and os.read(2, 1) fall through fd_take and incorrectly return b'', whereas stdout and stderr are write-only streams and must raise OSError with EBADF; only stdin should have the synthetic empty-read behavior. Store per-fd direction or reject reads for 1 and 2.
AGENTS.md reference: AGENTS.md:L219-L223
Useful? React with 👍 / 👎.
f56b244 to
2bc03a7
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2bc03a7693
ℹ️ 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".
| *owners -= 1; | ||
| drop(cache); | ||
| return compile_module(wasm_bytes); |
There was a problem hiding this comment.
Retain the replaced cache ownership until release
When two byte-identical loops share handle H and one later re-emits, this branch decrements H's cache count even though that loop's original LoopAsmResources still owns H and will call free(H) when it is released. If that token is freed first, its later free consumes the remaining owner's cache ref and clears H while the other loop can still dispatch through it (and a subsequent table reuse can misroute it). The new replace_module shared-handle branch is fresh evidence that the earlier cache-lifetime concern remains: keep the old ownership attached to the old resource until its drop, rather than decrementing it here.
Useful? React with 👍 / 👎.
2bc03a7 to
a6ec0b5
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
- `build_callee_gcmap` had no caller; `build_home_gcmap` builds the per-frame `jf_gcmap`. Its test now covers `build_home_gcmap`. - `WasmBackend::gc_rewriter` forwarded to the free `gc_rewriter()`; the doc moves there and the test calls that function. - `retarget_slots_to_module` and `retract_bridge_label_targets_for_slots` had no callers: re-emission leaves retired slots on the old module. - `reemit_loop_retiring`'s retire list was unused; folded back into `reemit_loop`. - `remember_and_drop_execution_frame` is reached only from its test; `run_compiled` runs the barrier and the pop around `WasmFrameData::boxed`. - Drop the file-scope `InputArg` import, the doc comment on a `thread_local!`, an unnecessary `unsafe` around the wasm-bindgen `jit_func_sig`, and an unused test binding. Assisted-by: Claude Opus 5
`float_subclass_binop_dispatch` drops its 5.6x allowance: darwin-arm64 reads 2.4x and 2.8x, ubuntu-24.04 2.0x and 2.1x. `pickle_terminal_raise_resume` goes from 6.3x to 4.1x. Four darwin-arm64 readings give 3.2x, 3.3x, 3.3x and 3.5x; 4.1x is the highest plus WASM_RATIO_FIT_HEADROOM. ubuntu-24.04 reads 2.4x and 2.7x. Local gate on darwin-arm64: dynasm 563/563, wasm 553/553, with `pickle_terminal_raise_resume` measured at 3.4x under the new allowance. Assisted-by: Claude Opus 5
`W_SeqIterObject`, `W_ListIterObject`, `W_ListReverseIterObject` and `W_TupleIterObject` store `index` as `isize` (`iterobject.py` `W_AbstractSeqIterObject.index` is a `Signed`). Conversions to the `i64` Python-int payload go through `seq_index_to_i64` / `seq_index_from_i64`; a `__setstate__` cursor wider than a word raises OverflowError. The iterator descr field sizes are `size_of::<isize>()`. `intbounds.rs` narrows a GETFIELD / GETARRAYITEM result only when the field or item is narrower than the machine word (`descr.py` `FieldDescr.is_integer_bounded` / `ArrayDescr.is_integer_bounded`, `< symbolic.WORD`), instead of narrower than 8 bytes. On wasm32 the old shape recorded `IntAnd(index, 0xFFFFFFFF)` and a 4-byte bound on the index read; the short preamble re-established it as `IntGe(index, 1)`, which failed on every bridge entry with a fresh iterator. Five wasm baselines now equal their dynasm baselines and are re-recorded: attr_cache_invalidation (bridges 4->3, guard_failures 804->604), gc_iterator_source_drop (10->8, 2018->1618), nested_for_varying_trip (3->1, 617->396), sre_pattern_methods (guard_failures 1209->1018), subscr_user_getitem_stack_index (2->1, 401->201). Fold census unchanged: spec_folds rows 112, try_walker_specialize_ 96. Assisted-by: Claude Opus 5
`publication_is_serialised_across_threads` ran its two recorder threads until the mutators finished; under machine load the mutators were starved and one instance's list grew to 75 GB. The recorders now stop after `4 * ROUNDS` registrations or at `stop`, whichever comes first. Assisted-by: Claude Opus 5.5
`CompiledLoopToken.free_loop_and_bridges` (`llmodel.py` `AbstractLLCPU.free_loop_and_bridges`) now releases what a wasm loop and its bridges hold. Each emission pushes a `LoopAsmResources` (new `release.rs`) into the token's `asmmemmgr_blocks`; its `Drop` frees the host table slots (`glue::free`, which also drops the `MODULE_CACHE` entry), removes the label rows the token still owns (`LabelTarget.owner_token`), sets its fail indices back to `Reserved`, and frees the home gcmaps and bridge cells it owns. Before, these were leaked (`Box::leak`) or never released. The host's `TraceState` keeps a `free_slots` list; `publish` reuses a freed pair before growing the table. A token that another token jumps into stays alive through `JitCellToken.record_jump_to`, so its slot is not freed under a caller. Runtime gcmaps from `wasm_jit_union_gcmap` stay `Box::into_raw`, as dynasm's `allocate_gcmap` does. Assisted-by: Claude Opus 5
The pyre binaries (`pyre`, `pyre-dynasm`, `pyre-cranelift`) and `pyre-wasm-runner` install `ProcessAllocator` as the global allocator. It charges every allocation against a process ceiling: 8 GiB by default, `PYRE_MAX_MEMORY` (bytes or K/M/G; 0 = unbounded) overrides it. An allocation over the ceiling returns null after one stderr line `pyre: process memory limit of N bytes exceeded (PYRE_MAX_MEMORY; 0 = unbounded)`: fallible paths (`try_reserve`, bigint) raise MemoryError, infallible ones abort in `handle_alloc_error`. The counter lives in `majit_gc::process_ceiling`, so the GC's `std::alloc` arenas are charged too, and `AsmLargeBlock::map` charges JIT code `mmap`s (uncharged on unmap). The wasm runner keeps its own counter and refuses guest linear-memory growth past the ceiling through its store limiter. `uncharge` saturates at zero so a block allocated before the ceiling was armed does not wrap the counter. `max_heap_size` (`incminimark.py`) is not set from the ceiling, so collection thresholds below it are unchanged. With `PYRE_MAX_MEMORY=512M`, a program appending 1 MiB bytearrays forever and one appending 64 MiB bytes forever each stop with that line at a peak RSS of 559 MB and 524 MB. `PYRE_MAX_MEMORY` is listed in `pyre/gate-triage.md`. Assisted-by: Claude Opus 5
The wasm backend now publishes `JitFrameDescrs` and `call_assembler_callee_locs` to its GC rewriter, so every `CALL_ASSEMBLER*` goes through `rewrite.py` `handle_call_assembler` → `gen_malloc_frame` → `gen_malloc_nursery_varsize_frame` and reaches codegen as a one-argument `CALL_ASSEMBLER(frame)`. A compiled loop's CLT carries `_ll_initial_locs` (`8 + i*8`, the slots a wasm trace entry reads its inputs from) and its `frame_info`; the lookup mirrors dynasm `lookup_call_assembler_callee_locs`. The rewriter's `jitframe_info.is_none()` arm is deleted, as are the wasm-only caller-side argument marshal and the host frame allocator `wasm_jit_ca_alloc_frame`. Kept, because wasm code is immutable and the GC cannot scan wasm locals: dispatch through the `ca_dispatch_*` table cell, the shadow-stack push/pop around the `call_indirect`, the callee's home gcmap, and the deopt path. Nursery bytes are not zeroed (`incminimark.py` `arena_reset(..., 0)`), and the callee's home gcmap marks its whole reserved Ref-home region, so the CA arm clears that region before pushing the frame (`emit_clear_reserved_homes`); without it `recursive_call_frame_relocation` traced a stale pointer. x86 marks live slots per call (`assembler.py` `push_gcmap`) and needs no such clear. Interleaved wasm A/B against the previous commit (user-CPU, mean): fib_recursive 0.968s -> 0.990s, loop_callee_shared_mutation 0.355s -> 0.365s, recursion_past_unroll_bound_from_loop 0.470s -> 0.475s. Assisted-by: Claude Opus 5
jfi_frame_depth is an AtomicIsize; the CALL_ASSEMBLER callee-locs lookup cast the field directly, which no longer compiles. Assisted-by: Claude Opus 5.5
Move the native launcher's shutdown (threading._shutdown, atexit _run_exitfuncs, sys.finalizing, signal-handler reset, stream flush, module teardown) from pyrex into pyre_interpreter::shutdown:: finalize_runtime and call it from both pyrex and the wasm guest's run_python_impl. The wasm guest previously ran no atexit callbacks. Uncaught non-SystemExit errors in the guest now go through sys.excepthook before finalization, like the native launcher. Re-record nine wasm jitstats whose loops_compiled/loops_aborted (and for inline_chain_depth_typeflip, guard_failures) now equal the native baselines. guard_failures of exception_metadata_jitstress and pickle_terminal_raise_resume stay at their wasm values (1073, 133; native 1074, 132). Assisted-by: Claude Opus 5.5
BUILTIN_WRAPPER_DESCRIPTORS is a linkme slice, which has no wasm32 arm, so the wasm guest's jit_trace_fnaddrs held only six hand-listed __majit_wrap_* rows and every other builtin wrapper was an opaque call there while the native JIT descended it. Add WASM_BUILTIN_WRAPPER_DESCRIPTORS (constructor-registered, the same shape as WASM_CLASS_DESCRIPTORS / WASM_TYPE_OBJECT_FNADDRS / WASM_HELPER_FNADDRS) and for_each_builtin_wrapper_descriptor. The emit both arms; every hand-written linkme static uses the macro. The six hand-listed wasm32 rows in jit_fnaddr.rs are removed. The host path set is unchanged (391 entries). exception_metadata_jitstress, exception_vable_frame_virtual_local and exception_reused_object_tb_not_doubled now record the native guard_failures on wasm; their wasm baselines are re-recorded to it. Assisted-by: Claude Opus 5.5
…table A residual call is emitted as a direct `call_indirect` when the CallDescr and the function table's declared type for the target agree (`jit_func_sig`); otherwise it goes through `JIT_CALL_AREA` and `jit_call`. Removed the address-keyed side tables and their producers: FAITHFUL_RESIDUAL_CALL_ADDRS, WORD_RESULT_RESIDUAL_CALL_ADDRS, RESIDUAL_TARGET_SIG_CACHE, RESIDUAL_CALL_ABI / ResidualCallAbi, the vouch APIs and the eval.rs vouch block, and the helper-address lists that fed them. ConstPtr fail args are resolved through the GcTable compile-key list carried in ModuleBuildInputs / InlinedBridge instead of the thread-local FAILARG_CONST_TABLE, which is removed. Host unit tests keep TEST_RESIDUAL_TARGET_SIGS, since they have no function table. Fold census unchanged: 112 spec_folds! rows, 96 try_walker_specialize_ fns. Assisted-by: Claude Opus 5.5
fds 0, 1 and 2 start open as stdio. Writes to 1 and 2 go through the print and stderr hooks, then the host descriptor. Other open descriptors stay read-only. Assisted-by: Grok 4.7
The wasm run matches the shared baseline: fbw_blackhole_adopted multi 3, single 13, loops_compiled 52, loops_aborted 16. Assisted-by: Grok 4.7
Production modules always carry the residual call type family (`body_reload_fn_ptr` is non-zero on wasm32, so `build_wasm_module` sets `residual_max_arity`), and CondCallGcWb / CondCallGcWbArray already require it through `direct_helper_i64_arity`. The `JIT_CALL_AREA` write-barrier arms in `emit_write_barrier` and `emit_jitframe_write_barrier`, which skipped the flag test and card marking, were reached only from host unit tests; they are removed and a module without the family reports `Unsupported`. A COND_CALL whose signature the CallDescr does not establish is now `Unsupported` instead of a host-trampoline call. pickle_terminal_raise_resume: wasm observes guard_failures=132, the native value, so the separate wasm baseline is removed. Assisted-by: Claude Opus 5.5
wasm now records fbw_blackhole_adopted_single_frame=40, the native value, so the separate wasm baseline is removed. Assisted-by: Claude Opus 5.5
…patch window The dead frame is the jitframe the trace exited on. A guard exit stores its descr cell in `jf_descr` and, for an exception exit, the value in `jf_guard_exc` (`_store_and_reset_exception`); `get_latest_descr` and `grab_exc_value` read those fields (`llmodel.py`). The frame is rooted once through an OwnerRootGuard. Removed: the copied `WasmFrameData` box, `FAIL_DESCR_REGISTRY` / `global_fail_descr`, `jit_exc_take` / `jit_exc_clear`. Descr cells and gcmaps are owned by the loop's `LoopAsmResources`. A propagate-exception exit (`compile_tmp_callback`'s GUARD_NO_EXCEPTION) is staged on the live frame: the exception moves from `jf_guard_exc` to fail-arg slot 0 and `jf_descr` / `jf_gcmap` are retargeted at the exit-with-exception finish descr (`_build_propagate_exception_path`). The withdrawn-dispatch window is removed (`wasm_dispatch_withdrawn`, the `wasm_inline_merge_exits` counter and its jitstats key). A merged owner is installed at the bridge trip without a blackhole exit: the running loop's back-edge reads a resume cell and tail-calls the replaced module slot with the same jitframe; the replacement's own back-edge is a plain `br` (`patch_jump_for_descr`). Fold census unchanged: 105 spec_folds! rows, 89 try_walker_specialize_ fns. Assisted-by: Claude Opus 5.5
…scrs A guard's bridge cell address is carried on the fail descr (`AbstractFailDescr.adr_jump_offset`, with a stable copy in `WasmFailDescr.bridge_cell`). `compile_bridge` writes the cell and then clears `adr_jump_offset`. A guard inside an inlined bridge gets its cell when the owner is re-emitted, and the re-emitted module reads each guard's cell from `ModuleBuildInputs.guard_cell_addrs`, collected from the descrs in emission order. Removed `bridge_slots`, `chained_bridge_slots`, `chained_trace_meta`, and the decline for a bridge whose parent is not chained. Each looptoken owns one `WasmCaDispatchEntry`, owned by `LoopAsmResources`, whose address is `JitCellToken._ll_function_addr`. `redirect_call_assembler` publishes after `update_frame_info`. Removed `CALL_ASSEMBLER_TARGETS`, `WASM_CA_DISPATCH` and `CA_CALLEE_REGS`. Removed the `bridge_decline_is_terminal` override. Assisted-by: Claude Opus 5.5
`exception_metadata_jitstress` on wasm now records guard_failures=1214, the native value; its wasm overlay is removed. Assisted-by: Claude Opus 5.5
a6ec0b5 to
5ca7b9f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5ca7b9fbf0
ℹ️ 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".
| unsafe fn alloc(&self, layout: Layout) -> *mut u8 { | ||
| let charge = layout.size(); | ||
| if !majit_gc::try_charge(charge) { | ||
| return refuse(); | ||
| } |
There was a problem hiding this comment.
Route dynasm raw allocations through the memory cap
With PYRE_MAX_MEMORY set, this allocator only charges Rust GlobalAlloc calls, but the shipped dynasm path leaves NEW_VIA_GC false and x86::Assembler::new_alloc_fn_addr emits direct libc::malloc calls. Thus a hot allocation-heavy loop that reaches compiled NEW operations can grow the C heap without changing CHARGED, bypassing the advertised process ceiling (and the new integration test only exercises interpreter-side bytearray/bytes growth). Account for or replace those raw allocator entry points before claiming the cap applies to pyre-dynasm processes.
Useful? React with 👍 / 👎.
…frontend's wasm overlay (#1971) #1960 landed with two wasm snapshot diffs on its ubuntu check.py job. pickle_terminal_raise_resume on wasm reads loops_compiled=24, loops_aborted=4, guard_failures=139 on ubuntu-24.04 and darwin-arm64, which is the native baseline, so the wasm overlay is deleted. gc_pypy_frontend's guard_failures counts GuardClass failures over the gc.get_rpy_roots() list, which the fixture header documents as a per-runner root-population figure under a band of 8. After #1960 the wasm run reads 375 on ubuntu-24.04 and 377 on darwin-arm64 (331 before); loops_compiled=2 and bridges_compiled=1 are unchanged. The overlay takes the ubuntu reading. Assisted-by: Claude Opus 5.5
…resume jitstats Both wasm baselines were last recorded in #1932, before #1960 made the sequence iterator index Signed-width. On wasm32 that removes the 4-byte index bound whose short-preamble `IntGe(index, 1)` failed on every bridge entry with a fresh iterator. main 17c8884 CI (ubuntu-24.04) fails both fixtures on wasm; native counters are unchanged. - pickle_terminal_raise_resume: wasm now reads loops_compiled 24, loops_aborted 4, guard_failures 139, bridges 0, equal to the shared native baseline, so the wasm file is removed. - gc_pypy_frontend: wasm guard_failures 331 -> 373 on ubuntu-24.04 and 375 on darwin-arm64; recorded as 374, inside the existing band of 8. The native baseline reads 402. The fixture comment no longer attributes the root-count gap to eager prebuilt_root_objects: only about 16k of the 22.5k roots are distinct objects. Assisted-by: Claude
…aseline wasm records the same counters as the shared `.jitstats` for these fixtures (check.py --synthetic-only, wasm backend, on the previous head): - trace_too_long_inline_multiframe: loops_compiled 51, loops_aborted 19, fbw_blackhole_adopted_single_frame 14, fbw_blackhole_adopted_multi_frame 4 (overlay 50 / 16 / 13 / 3; upstream #1960 had already removed it) - exception_reused_object_tb_not_doubled: guard_failures 29872 (overlay 29871) - sre_pattern_methods: bridges_compiled 2, guard_failures 404 (overlay 3 / 604) Assisted-by: Claude Claude-Session: https://claude.ai/code/session_01Aei5CsUsBqUwyiYkkV4Bjm
…aseline wasm records the same counters as the shared `.jitstats` for these fixtures (check.py --synthetic-only, wasm backend, on the previous head): - trace_too_long_inline_multiframe: loops_compiled 51, loops_aborted 19, fbw_blackhole_adopted_single_frame 14, fbw_blackhole_adopted_multi_frame 4 (overlay 50 / 16 / 13 / 3; upstream #1960 had already removed it) - exception_reused_object_tb_not_doubled: guard_failures 29872 (overlay 29871) - sre_pattern_methods: bridges_compiled 2, guard_failures 404 (overlay 3 / 604) Assisted-by: Claude Claude-Session: https://claude.ai/code/session_01Aei5CsUsBqUwyiYkkV4Bjm
Moves the wasm backend's remaining wasm-only structures onto the upstream owners.
get_latest_descr/grab_exc_valuereadjf_descr/jf_guard_excon the jitframe the trace exited on; the copied dead-frame box and the withdrawn-dispatch window are removed.adr_jump_offset, copy inWasmFailDescr.bridge_cell);bridge_slots,chained_bridge_slots,chained_trace_metaare removed. Each looptoken owns one CALL_ASSEMBLER dispatch entry at_ll_function_addr;CALL_ASSEMBLER_TARGETS,WASM_CA_DISPATCH,CA_CALLEE_REGSare removed.math_*_residual_call_addrshooks are removed.handle_call_assembler; write barriers are emitted only on the typed residual-call arm;jfi_frame_depthis read throughJitFrameInfo::depth.ObjSpace.finishsequence; stdio fds 0-2 are table entries written through the embedder hooks;os.writeon a read-only fd answersEBADF.run_pythonkeeps its runtime alive; only the one-shotwasm-hostguest runsfinish. wasmtime guest-memory growth is charged to thePYRE_MAX_MEMORYtotal.Commits:
Local verification:
python3 pyre/check.pydynasm 567/567 and wasm 556/557 on the previous tip; the remaining wasm diff isload_method_self_bindingon macOS only (a JitCounter bucket collision from upstream-identical hashes, address-layout dependent; Linux CI records the shared value).cargo test --all --no-default-features --features dynasmpassed.Not included: the per-site
push_gcmapport of the wasm rooting (failsexception_traceback_frame_linenoatPYPY_GC_NURSERY=64K).🤖 Generated with Claude Code