ReviewOS

zig-utils/zig-js

JIT: replace owner-wide invalidation with precise artifact assumptions

#471
Closed chris opened this 9 days ago ·
9 days ago

Parent: #459 Depends on: #461 Blocks: #468

Goal

Replace owner-wide Class-A JIT invalidation with precise per-artifact/per-assumption invalidation, and broaden property inline caches without losing no-GIL soundness.

Measured problem

cve/mc-val-fire-vs-link.js records publications=271244 invalidations=271244 under no-GIL versus publications=40 invalidations=0 serialized. The current shape filter can avoid unrelated mutations, but once a stop fires Owner.clear retires every tier and one owner-wide generation closes all native entry.

Scope

  • Give artifacts or assumption groups independent invalidation generations and retirement sets.
  • Index artifacts by exact shape/prototype/callee/global assumptions so a mutation retires only affected code.
  • Distinguish shape transition/add (normally caught by live shape guards) from in-place deletion, attribute/accessor redefinition, prototype replacement, and other assumption-breaking mutation.
  • Preserve atomic publication, epoch lifetime, call links, GC/JIT conductor ordering, and cooperative safepoints.
  • Expand ICs from four-shape own data slots to bounded polymorphic prototype, accessor, transition, deletion, array named-property, and megamorphic strategies only where profile evidence justifies them.
  • Add invalidation reason, scope, survivor, republish, and executable-byte counters.

No-workaround rules

  • Do not simply raise compilation thresholds, disable JIT in shared mode, ignore mutations, poll less often, or retain stale code behind a guard that does not cover every assumption.
  • Shape hash filters may over-approximate but never false-negative.

Acceptance

  • The measured witness converges near its necessary compile count without owner-wide storms; exact target comes from a pre-change attribution artifact.
  • Surviving artifacts remain enterable after unrelated invalidation and affected artifacts close before unsafe execution.
  • Races cover shape transition, delete/redefine, prototype change, call-link relink, GC movement, compilation publication, and context teardown under TSan.
  • Forced optimizer-off/on semantic and step differentials pass for own/inherited/accessor/proxy/megamorphic cases.
  • #461 evidence reports materially lower invalidation/retirement/compile work and executable churn with no #44 regression.

Foundation

Property snapshots and direct reads/writes: 7a26276c, a35ee764, 85e072eb. Artifact retirement: #433.

9 days ago

The shared Class-A/JIT-GC conductor witness is now deterministic on main in a4a9cd67.

JitGcConductor publishes a live waiting_threads gauge when a thread has actually observed the conductor held. The invalidation-ordering test now holds the conductor until the invalidator is a published contender, rather than guessing with 256 scheduler yields, and verifies the gauge drains to zero after the owner-wide stop completes.

Validation: 5 consecutive focused Debug runs, focused TSan, and the full 1,477-test suite across 4 shards with 0 failures/skips/leaks. This strengthens the existing owner-wide ordering foundation; it does not advance the issue acceptance boxes for precise per-artifact invalidation.

9 days ago

048a4b7e closes a no-GIL race in inherited property cache observation found by the sharded TSan corpus.

The canonical property read already held property_lock, but the advisory cache record reread the prototype shape and receiver shape afterward as plain fields. gc-stress/watchpoint-storm.js therefore raced a main-thread shape transition against a worker VM read. The engine now returns value + immutable holder shape/slot from one locked snapshot and snapshots the receiver shape through the same publication lock.

Verification: exact storm case under TSan passes 1/1 with 7 optimizer publications and 4 invalidations; the complete unit suite passes 1,477/1,477 across 10 shards with zero failures, skips, or leaks. Test262 flips: 0.

9 days ago

9a6dba73 lands the mutation-classification slice of #471.

Existing named data-property stores now preserve published artifacts: own/inherited optimizer caches guard the live shape/slot pair and reload the value, so a value-only store breaks no heap assumption. Shape-changing additions still retain the Class-A compile/link boundary.

Measured on the exact cve/mc-val-fire-vs-link.js witness in ReleaseSafe no-GIL:

  • checked-in before: 294,094 publications / 294,094 invalidations / 50,382 ms
  • after: 40 publications / 39 invalidations / 5,235 ms, with 39 reclaimed and 0 retired
  • suppression-free TSan: 42 publications / 41 invalidations / 76,194 ms, no race reported

The dedicated checktraps-invalidation.js Class-A witness remains active at 52 publications / 51 invalidations, with all 51 mappings reclaimed. Full unit suite: 1,479/1,479 across 10 shards, zero failures/skips/leaks.

This removes the measured owner-wide storm and converges at the required 40 fresh function generations. Exact per-artifact retirement, survivor counters, and broader IC strategies remain open; no broader acceptance box is being claimed by this slice.

9 days ago

092f34c7 lands exact shape-scoped artifact retirement.

Each published mapping now has its own invalidation cell. A Class-A mutation scans immutable property-cache assumptions under the owner lock, closes only matching baseline/optimizer tiers, rebuilds the watched-shape filter from survivors, and retires affected mappings behind a cross-epoch older-reader frontier. The owner-wide generation remains an attribution counter, not the native-entry guard.

Measured evidence:

  • mc-val-fire-vs-link.js ReleaseSafe no-GIL: checked pre-change 294,094 publications / 294,094 invalidations / 50,382 ms; now 40 / 0 / 4,000 ms.
  • Same exact witness under suppression-free TSan: 42 publications / 2 precise shape retirements / 81,957 ms; 44 survivor observations; both retired mappings reclaimed; no sanitizer report.
  • checktraps-invalidation.js: 51 publications / 50 invalidations, all 50 exact shape retirements and all 50 mappings reclaimed (819,200 bytes), with no full/unknown fallback.
  • Full unit suite: 1,480/1,480 across 10 shards, zero skips/failures/leaks.

The targeted owner test holds an older execution lease across two successive shape retirements, proving the survivor is not reclaimed beneath an old reader. The parallel context test proves the affected tier closes while an unrelated artifact remains the identical published, enterable mapping. This satisfies the first two acceptance boxes; the broader assumption index/IC strategies, race matrix, semantic differentials, and #461 publication evidence remain open.

9 days ago

d8277a49 extends exact Class-A retirement to ordinary delete and property-descriptor mutations.

The mutation classifier now:

  • retires artifacts guarding the exact pre-delete shape only when an ordinary property is present and configurable;
  • retires exact artifacts before shape addition, data/accessor conversion, or attribute-sidecar publication;
  • preserves published artifacts for an existing data property’s value-only defineProperty, an empty descriptor, and a failed non-configurable delete;
  • avoids materializing an attributes sidecar for those representation-preserving definitions;
  • never waits on the Class-A conductor while holding the indexed-name exclusion.

The focused parallel_js test warms five distinct artifacts. A real delete and data→accessor redefine retire only their two mappings; value-only/empty definitions, the failed delete, and the unrelated survivor retain identical published pointers. Validation: focused test Debug + suppression-free TSan, delete 8/8, defineProperty 5/5, and full suite 1,481/1,481 across 10 shards with zero skips/failures/leaks. The broader concurrent race matrix acceptance item remains open.

9 days ago

9c61d811 adds an actual shared-realm race for three #471 assumption surfaces: delete/re-add shape transitions, data↔accessor redefinition, and same-shape prototype replacement.

A warmed reader runs concurrently with 64 mutation rounds and rejects every value outside the documented program-race states: delete is undefined|1, redefine is undefined|2|6 (the accessor→data conversion may expose its ordinary-object absence window), and prototype replacement is exactly 3|4. The same-shape prototype pair specifically proves optimized inherited reads follow the live atomic prototype rather than retaining old prototype identity.

Evidence: 5/5 focused Debug runs, focused suppression-free TSan 1/1 with zero failure/leak, and full suite 1,482/1,482 across 10 shards with zero skips/failures/leaks. This advances the race-matrix acceptance item, but it remains open for call-link relink, GC movement, compilation publication, and teardown coverage.

9 days ago

12451f08 closes the remaining owner-teardown gap in the #471 TSan race matrix. Teardown now has a deterministic test holding both an active compiler claim and native execution lease: Owner.deinit must remain blocked, then invalidate the published tier and complete only after both holders release.

Suppression-free TSan evidence for the complete acceptance row is now explicit:

  • shape transition + delete/redefine + same-shape prototype replacement: parallel_js: optimized reads race delete redefine and prototype replacement;
  • call-link relink/reset: optimizer call-link writer writer and reader reset stay coherent;
  • compilation publication/invalidation: optimizer publication and invalidation races converge and Owner rejects a compiler publishing across an epoch rotation;
  • GC movement: parallel_js automatic compaction relocates at a shared moving stop;
  • teardown: Owner teardown waits for active compilation and execution.

All focused TSan runs passed with zero failures/leaks. The integrated full suite is 1,483/1,483 across 10 shards with zero skips/failures/leaks. The race-matrix acceptance box is checked; optimizer-off/on semantic/step differentials and #461 causal publication evidence remain open.

9 days ago

576cb8ed completes the forced optimizer-off/on property differential acceptance item.

The test builds independent bytecode-only and optimizer-enabled realms for five minimal warmed functions: own data, inherited data, accessor, Proxy, and six-shape megamorphic named reads. Every optimizer-enabled function must publish an optimizer artifact; the six distinct receiver shapes exceed the four-entry property cache. The test then requires exact equality for result bits, accessor/Proxy effect counts, and per-call bytecode step totals.

Focused differential: 1/1. Full suite: 1,484/1,484 across 10 shards, zero skips/failures/leaks. The optimizer differential box is checked. The remaining acceptance item is #461 causal evidence for lower invalidation/retirement/compile/executable churn with no #44 regression.

9 days ago

f9a92096 replaces the stale owner-wide invalidation design note with the landed #471 contract: artifact-local invalidation cells, exact pre-mutation shape retirement, survivor filter rebuilding, cross-epoch older-reader lifetime, fail-closed fallback, and mutation classification.

The same commit refreshes the deterministic whole-tree BunPress manifest after rebasing all current committed docs changes. Validation: zig build docs-link-check resolves 89/89 files and bun run docs:build verifies all 49 rendered pages against the regenerated manifest. This is documentation-only (flips 0 test262 cases) and does not claim the final #461/#44 evidence box.

9 days ago

The first clean-host #44 guard attempt exposed infrastructure failures before it could collect samples: Performance run 30781756957 never installed Zig after a Pantry socket reset, and inspection found the performance workflow also lacked the now-required owned Home producer chain. b1d3a31c fixes the deterministic bootstrap/pin/workflow defects and validates the complete harness locally. I am rerunning the existing full 7-sample, 2/4/8-lane workflow; the final acceptance box remains unchecked until raw matrix evidence exists.

9 days ago

The second clean-host guard attempt (run 30782424899) confirmed Pantry was swallowing a required Zig install failure and returning success. The owner fix landed in pantry@9b105eaf4, and zig-js pins it in 2a3f65c3. Full matrix run 30782817738 is now queued from that strict setup contract; the acceptance box remains unchecked pending raw evidence.

9 days ago

The clean-host #44 guard for the precise-invalidation series is now running from 66b3173c, with the owned Pantry bootstrap fixed and the compiler locked to the same Zig 0.17.0-dev.1441+d5181a9c9 snapshot as the retained reference evidence: https://github.com/zig-utils/zig-js/actions/runs/30783602925

I will only check the final acceptance box after the full 1,540-sample artifact validates its checksum and #44 mode/lane contracts; hosted wall times remain diagnostic, not publication evidence.

9 days ago

Run 30783602925 confirmed the repaired Pantry/bootstrap/Home producer chain, then correctly stopped before sampling because the newly migrated exact-parent self-test assumed HEAD^1 existed in GitHub’s depth-1 checkout. 186083b8 fixes the test contract itself: it validates exact-parent logic in a purpose-built three-commit fixture while production revision checks remain unchanged. The self-test passes in a one-commit repository and the complete local benchmark-comparison-test harness passes.

Replacement clean-host run: https://github.com/zig-utils/zig-js/actions/runs/30784074991

9 days ago

Completed and published in 5e0c4023.

The exact-parent artifact records seven alternating fresh-process pairs (14/14 witness passes): optimizer publications remain near the necessary count (288 parent vs 284 candidate total), while owner invalidations and reclaimed executable mappings fall from 281 to 4 (-98.6%). The candidate records 4 exact-shape retirements, 67 survivor observations, 65,536 precisely retired executable bytes, and zero post-quiescent retired mappings.

The independent hosted performance guard then passed the Home-powered harness, all 1,540 raw samples, publication validation, and artifact upload. It preserved the #44 contract: 10/10 direct wins, 10/10 eight-lane steady wins, 10/10 eight-lane cold wins, and every shared workload above 1.0x scaling at every multi-lane point. Hosted timings remain diagnostic because this was a three-CPU virtual M1 rather than the quiet M3 reference host.

Evidence: report · raw counters. The final acceptance box is now checked.

9 days ago

Follow-up regression fix landed in 76fd2a06.

The descriptor-invalidation work exposed an existing recursive-lock path: converting a configurable canonical-index accessor to a data property entered defineOneResult under the indexed-property exclusion, then called [[Delete]], which attempted to acquire the same non-reentrant lock again. The fix removes the accessor representation under the existing exclusion and keeps the prior invalidation plus replacement write ordering.

Evidence:

  • focused Debug regression: 1/1
  • focused TSan regression: 1/1
  • test/built-ins/Array: 3,074/3,081 → 3,081/3,081
  • test/built-ins/Object: 3,379/3,411 → 3,411/3,411

#471 remains closed; this is a narrow correctness follow-up, not an acceptance-boundary change.

9 days ago

A second lifecycle follow-up landed in 4db56b5d: the last Execution.release() could observe its epoch become idle, queue opportunistic reclamation behind Owner.deinit()s lock, then resume after teardown reset the Owner storage. That produced an index-out-of-bounds panic in reclaimRetiredEpochs().

Release now skips opportunistic reclamation while invalidation/teardown is active; clear/deinit already own reclamation after their idle wait. Evidence: exact teardown lifecycle 50/50 Debug stress repetitions and 1/1 TSan. This is a narrow artifact-lifetime correctness follow-up; #471 remains closed.

9 days ago

#530 landed collision-free logarithmic lookup for deep hidden classes without changing Shape pointer identity or inline-cache guard semantics. Exact-parent 1K/2K/4K computed traversal reaches 0.288x/0.190x/0.092x instructions, while shallow classes stay neutral at 1.011x instructions. Evidence: 21a33f13.

This removes one deep miss-path bottleneck; #471 still owns broader property IC coverage and artifact invalidation.

Sign in to comment on this issue.