ReviewOS

zig-utils/zig-js

Optimizer: execute captured binding reads and writes natively

#1095
Closed chris opened this 8 days ago ·
8 days ago

Parent: #467; related #465, #466 and #491. Follows #1092.

Executed against 2fc1383: warmed closures over an outer ordinary binding retain correct results, but the captured reader has no optimizer artifact and the writer has an artifact with interpreter-owned side exits. The focused admission fixture reports artifact=false, fully_native=false for the reader and artifact=true, fully_native=false for the writer.

A regular/mapped/lexical closure mutation control was executed against Node, direct system JavaScriptCore, zig-js tree and required-VM modes. All return [[3,7,5],[11,12,13,12],[11,12,13]], so the task is exact native admission rather than changing those semantics.

Scope: all six load/store upvalue opcodes, including mapped arguments cells, lexical TDZ and readonly writes, arbitrary defining-frame depth, precise effect/exception ordering, activation-safe entry, and native region recovery. Reuse canonical frame synchronization and retain original caller/arguments state across callbacks. No inferred purity, raw shared-slot access, frozen capture values or name-based specialization.

Region qualification also exposed the shared-mode numeric property-read guard: a captured-object loop repeatedly deoptimized before reaching a native moving checkpoint. The implementation therefore needs a canonical locked own-data snapshot for numeric property reads, with accessor/type misses leaving before observable work, while preserving the existing serial assembly fast path. The region witness must verify actual movement and identical step/termination outcomes in serialized and no-GIL modes.

Definition of done:

  • SSA and native metadata model all six opcodes with exact coordinates, effects, recovery states and managed roots; verifier/OOM qualification passes.
  • Real entry/region artifacts execute captured loads/stores; normal/TDZ/readonly/throw/coercion/step/termination outcomes agree with optimizer disabled in serialized and no-GIL modes.
  • Escaped/shared closures, mapped arguments and mutation during runtime re-entry survive moving GC and unsuppressed TSan, with actual native execution proved.
  • Exact-parent corpus, production and appropriate threading gates pass; implementation and evidence land in a small conventional commit.
8 days ago

Landed in 2eeae7a5.

All six captured-binding load/store opcodes now produce SSA/runtime-operation nodes with original depth/slot/readonly coordinates, exact effect and exception states, and managed recovery roots. The published VM execution supplies the owning frame; direct entry cannot borrow a caller's frame. Bytecode and native callbacks share canonical access for mapped argument cells, escaped-frame synchronization, TDZ and readonly-write precedence.

Numeric regions access canonical cells with pre-effect Number guards. Shared numeric own-data property reads use a locked descriptor/value snapshot; accessor/type/exotic misses leave before the opcode's step delta or mutation. Existing serial assembly specializations remain first in the guard chain.

Evidence:

  • The pre-change admission fixture had no reader artifact and a writer with side exits. Both now have entry artifacts without side exits, and the fixture proves actual native hits. All six opcodes retain depth-two coordinates; direct native execution verifies results, TDZ, readonly precedence and exact steps.
  • Eight deterministic controls were executed against Node and direct system JavaScriptCore. Their optimizer-off/on variants agree in serialized/no-GIL configurations across regular, mapped and lexical mutations, object coercion that mutates a capture and collects, string results and BigInt/Number errors. Both production tree and VM binaries agree with the reference outputs for every seed.
  • Four no-GIL workers mutate one shared escaped frame with mapped and lexical cells. Relocation fixtures verify changed object pointers through the captured frame. A native OSR loop consumes a real moving request in serialized and no-GIL modes; type changes and accessor conversion resume without repeating getter work. Native and bytecode budget/termination outcomes retain exact 1024/1025-step boundaries. Allocation-failure sweeps cover lowering/recovery ownership for all six opcodes.
  • 202 optimizer module tests passed in ReleaseSafe and unsuppressed TSan; linked instrumentation was verified. Debug optimizer coverage passed, plus the focused JIT gate (147 passed, one platform skip), frontend 79, VM/concurrency four each, default threadfuzz 400 and broad 300 with zero failures. Production library and runner builds, GC relocation inventory, all 378 documentation links, and the checked 51-page docs render passed.
  • Exact parent 2fc1383: 3,767 cases across 20 subtrees, default failures 0 → 0 in each, 0 flips, no new failing or diverging paths. The six existing tree-fail/VM-pass witnesses are the three TCO call cases and three TCO catch/finally cases. Scope: arguments 263, function expressions 264/statements 451, Function 509, calls 92, rest 11, Reflect.ownKeys 13, keys 59, names 45, eval-code 347/eval builtin 10, arrows 343, assignment 485, compound 454, logical assignment 78, prefix/postfix increment 33/38, prefix/postfix decrement 34/37, try 201.

Before binary SHA-256: 862aa2fc444bd404b895ceb9ab0917ca4c606d51ef85283accd578c7b3f33615; after: 061e7c21a56777a4ae3848e04709018e6ef5508081fca90a0bc7927127553174.

The full combined local unit link remains blocked by #933's memory cap. Integration CI 38020942537 is queued. #465, #466, #467 and #491 remain open.

Sign in to comment on this issue.