Merge train 188: runtime, JSON, GC and compiler fixes (v0.5.1566) - #10261
Conversation
…aversal evidence Traversal feedback (#10150) learns that a program scans its parsed arrays from element reads in lazy_get_rooted. The element-shape loop clone (#10171) materializes a lazy array whole in its preheader through js_array_refresh_local_head before any element is read lazily, so a scan loop served by the clone never produced evidence: every parse built the tape and then materialized every record anyway. On the quiet bench mini records_array_16k:scan and records_array_1m:scan read 1.11x and 1.18x the better of Node and Bun, with a third of the parse samples in build_tape_into. js_array_refresh_local_head now notes one flip's worth of evidence the first time it materializes a lazy array. Its emitters are all cold arms that run about once per receiver, so the added tracked-header probe is not on a hot path.
…es from instances
Refs #10110. Adapt the reusable index and population/churn witnesses from proggeramlug#1 for main. Scale all three hash slices with the per-owner width and cover concurrent refreshes and address reuse. The canonical-wrapper owner is still on the campaign branch; document its required integration without closing the issue.
…d trigger checks Runtime safepoint polls (regex quanta, the microtask pump, the event loop), gc_malloc and the other gc_check_trigger callers spent hundreds to thousands of instructions answering "is a collection due?" when nothing was. Three changes, each leaving every collection decision unchanged: - Runtime polls no longer build a JsGcStepResult. The budgeted step returns a debt-free GcStepReport; the FFI and test entry points attach the GcDebtSnapshot after the step returns, which reads the same values. The cycle start/step machinery moved out of line so a no-trigger poll does not pay its multi-kilobyte frame. - copying_from_space_in_use_bytes() is O(1) between layout changes: the bytes outside Eden's current block are cached, keyed on the heap generation (every reset, detach, evacuation and survivor flip runs inside a HeapChange scope), and every move of an arena's current block goes through Arena::set_current, which invalidates the cache. Debug builds compare every cached answer with the block walk. - gc_check_trigger evaluates the due trigger once instead of up to three times, reusing the answer unless it came from the #10169 one-shot leaf priority; the young cap reuses the old-gen pressure the due trigger already read (debug cross-checked) and tests the census-seeded flag first.
… reaches its cap The tiny-parse guard prices forced collections on total arena in-use (#9831), which a collection cannot lower below the live set. It never asked the generational question of whether the young generation is at its scavenge cap, and a loop of tiny JSON.parse calls allocates only inside the parser's suppression window and at bounded inline-object births, so nothing else arms the nursery safepoint for it. The young generation grew to the guard's 48 MB floor before its first minor: small_record:parse peaked at 80 MiB against Node's 59 with 0-9 permille of each collection surviving, and object_1k:parse at 65 against 61. The boundary now also collects when the young scavenge cap is due. A minor lowers that quantity to the survivors, so unlike the absolute in-use guard it cannot re-fire until the cap has been refilled.
…ary touched gc/policy.rs
… in native domains The fast clone now redirects the accumulator into a promotable f64 alloca and defers the counter's double storage to an i32 slot the Update lowering advances alone, publishing both at a side-exit trampoline and on the fall-through exit.
…ounter IR census for the f64 accumulator redirect, the i32 counter, the side-exit trampoline and the fall-through write-back; the carried-commit and side-exit helpers count the trampoline spelling of an exit.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: ⛔ Files ignored due to path filters (22)
📒 Files selected for processing (118)
📝 WalkthroughWalkthroughThe pull request adds native property access, runtime handle-stack storage, GC and compiler fast paths, inferred re-export purity, declared-prototype symbol lookup, registry address indexing, benchmark evidence, documentation, tests, and a project version update. ChangesNative property Get
Runtime handle scopes
Element-shape native domains
GC and traversal fast paths
Re-export purity analysis
Symbol lookup and registry indexing
Release metadata
Estimated code review effort: 5 (Critical) | ~90 minutes Change: Bug fix · Severity of issue fixed: Medium ✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Land ordinary property Get, JSON traversal feedback, declared-class symbol properties, pure re-export pruning, registry-index preparation, handle-stack and GC polling changes, nursery-cap checks, and native numeric loop state as Perry 0.5.1566. This train contains #10248, #10249, #10250, #10251, #10252, #10253, #10254, #10255, and the remaining two follow-ups from #10245.
Integration fixes preserve symbol-property presence without invoking getters, honor prototype overrides, and validate tracked allocation ownership before reading class metadata. Android and OHOS retain scoped access to OS-owned TLS so teardown cannot leave a cached dangling stack reference; native handle buffers keep the prior four-slot initial capacity. GC holder records are merged structurally with pins recomputed from the merged source. The source-graph test helper clears an earlier compiled output before asserting that collect-only mode creates none.
Validation uses the approved fast protocol. Each original PR's full gap, GC-stress, and core cargo-test CI was attributed against main; the current main run has exactly the same three gated gap failures as the earlier baseline. All original PR GC-stress jobs passed. The core cargo-test failures are the known custom-thread-stack test; #10245's final run passed it.
Measured tradeoffs: four runtime workloads use 8.57–15.50% less CPU with flat or lower RSS; record-array scans use 25.8–28.6% less CPU, and two small-object parse cases use about half the peak RSS. Longer repeated measurements also show empty-object parse CPU +2.49%, empty-object stringify CPU +3.51%, and 1 MiB record-array round-trip peak RSS increasing from 63.59 to 80.61 MiB (+17.02 MiB, +26.76%) over 970 calls. Escaped-string stringify measured +1.66% CPU in the longer follow-up; its instruction count was flat. The round-trip retention change has not been attributed to a single PR.
The final test-harness fix produces byte-identical JSON benchmark machine code to the measured candidate (11,808,956-byte __text section, SHA-256 f63e00306116c2795f58f81648f88c3d0091321e52a12644ab0ee26c909247e0). The owner approved these measured compute/RSS tradeoffs. Local validation is complete.
Outside the three approved CI tiers, #10252 has a Linux debug-runtime property-read abort that has not been reproduced or attributed locally. The same probes pass on the landed macOS baseline in release and paired debug configurations, and on this train in release. Other auxiliary failures are runner/build/oracle issues or have explicit baseline reproduction; they are not being presented as green tests.
CI baseline: https://github.com/PerryTS/perry/actions/runs/34813821843
Closes #10180
Summary by CodeRabbit
Performance
Reflect.get, and inheritedtoJSONlookups.Bug Fixes
Documentation
Chores