ReviewOS

zig-utils/zig-js

Compiler: scan repeated loop-body captures once

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

Parents: #465, #493, #496 Related: #616

Problem

Repeated loop-body admission still multiplies lexical declaration count by body AST size. loopBodyCapturesLexical, repeatedBodyCapturesSupported, predeclareRepeatedBodyNode, emitDeclareRepeatedBodyNode, and repeatedBodyListNeedsEnvironment each answer per-binding capture questions through another exhaustive nameRefInClosure(body, name, false) walk. A wide block-scoped body with a large closure-free tail therefore performs declaration-count × body-size compiler-classification work before bytecode emission.

This is distinct from #616, which owns lexical names in the for/for-in/of head. This child owns block-scoped declarations inside bodies repeated by while, do-while, for, and for-in/of, including the shared CaseBlock handling for switches nested in such bodies.

Scope

  • Freeze production parse + plain-function admission/compile growth witnesses before changing the classifier.
  • Collect repeated-body lexical binding names once across the exact existing block/if/try/switch/labeled boundaries while leaving nested loops/functions to their own roots.
  • Traverse the repeated-body AST once to classify exact names referenced from nested closures or deferred class bodies.
  • Reuse one compile-owned capture result for support checks, lexical predeclaration, Environment Record declaration, and environment-need decisions.
  • Preserve conservative shadowing, catch/destructuring behavior, nested-loop ownership, bytecode fallback, structural checksum, and allocation accounting.
  • Keep zero- and one-binding common cases allocation-minimal.

No-workaround rules

  • No syntax refusal, heuristic cutoff, skipped body region, runtime TDZ broadening, tree-walker fallback presented as optimization, source rewriting, or weakened per-iteration Environment semantics.
  • No process-global or cross-compilation mutable cache.
  • A new AST node kind must not silently bypass capture classification.

Acceptance

  • 1K/2K/4K wide-body growth and unrelated/first/last/destructuring/catch/switch/nested-loop controls preserve exact admission and structural checksums.
  • Parent → candidate exact evidence demonstrates bounded classifier scaling with allocation metrics and names the complete scored boundary.
  • Captured declarations retain fresh per-iteration bindings; uncaptured declarations remain slot-backed in forced tree-walker and required-bytecode execution.
  • Focused compiler/frontend, full units, relevant Test262 loop/block/lexical subtrees, and applicable TSan gates pass with exact before → after accounting.
  • Evidence and parent issues are cross-linked without converting diagnostic wall noise into a public claim.
8 days ago

Benchmark contract pushed in 3fcd7f02 (bench(frontend): add repeated-body capture witness (#618)).

The production frontend runner now freezes ten parse + plain-function admission/compile rows: clear 1,024/2,048/4,096-binding growth, 4,096 unrelated nested-closure references, first/last captures, wide destructuring capture, catch capture, switch CaseBlock capture, and nested-loop ownership. Every row validates Environment Record versus frame-slot policy, complete bytecode chunk structure, frozen checksum, and an untimed identical-work allocation replay.

All 10/10 contracts pass. The existing #615 TDZ and #616 loop-head 1K controls remain exact, and dependency audit stays at 15 classified edges / 0 migrations / 60 tools. This is benchmark infrastructure only, flips 0 Test262 cases, and makes no timing claim from the occupied host. The single-analysis implementation starts from this exact first parent.

8 days ago

Implementation pushed in 14ca5cd4 (perf(compiler): classify repeated body captures once (#618)), whose exact first parent is the benchmark contract 3fcd7f02.

The compiler now collects declarations owned by one repeated-body root once, then marks exact captured names during one exhaustive traversal. Block/switch environment decisions, support checks, predeclaration, and Environment Record emission reuse that result. A one-binding body remains allocation-free; wide bodies use one name→captured table. Catch patterns are classified once against only their catch block and cached by AST identity. Nested loops replace and restore their own analysis.

Validation so far:

  • all 10 parent-frozen admission/environment/chunk checksums remain exact, with allocation replay passing;
  • direct repeated-body classifier matrix passes 12 scope/capture controls;
  • compiler-filtered units: 70/70;
  • repeated-body/GC units: 3/3;
  • moving-GC catch environment: 1/1;
  • focused frontend: 12/12;
  • dependency audit: 15 edges / 0 migrations / 60 tools.

A non-evidence one-job probe shows the 4,096 clear row moving from about 939M to 31M retired instructions, but no performance claim is published from the occupied host. Full units, exact Test262 accounting, and guarded exact-parent evidence remain pending.

8 days ago

Full integration unit gate at candidate main 8bbbf402: 1,733 passed, 1 platform skip, 0 failed, 0 leaked across 10 shards in 238.1 seconds. This includes the focused #618 classifier tests and moving-GC repeated-body coverage already recorded above. Relevant exact Test262 loop/block/lexical parent→candidate accounting remains pending and will run from the frozen binaries without overlapping other corpus or host jobs.

8 days ago

Invalidated — do not use these counts as acceptance evidence.

After this comparison completed, process inspection found an accidental long-lived candidate619-out/bin/test262 --help driver from an earlier CLI probe. The runner interpreted --help as its default corpus mode, so that separate #619 corpus process overlapped the #618 parent/candidate slices. I terminated only that accidental process tree and retained no result from the contaminated run.

The observed denominators happened to match, but #617’s no-overlap policy requires discarding them. #618 Test262 accounting remains pending a fresh serial rerun after all user-owned Home build/test processes are also absent.

8 days ago

Exact parent→candidate Test262 accounting is complete for #618.

Frozen identities:

  • parent benchmark contract: 3fcd7f027954b4c9c1ed5d89f2e7a503b4a5ae91
  • candidate implementation: 14ca5cd4588b8f40baea15eb931e3d696d3c66bc
  • parent Test262 binary SHA-256: 2fe64edb3917e99b6672eb7fb50d9910ff988edc5e070577389a4897b99f9e41
  • candidate Test262 binary SHA-256: e91f11dcf698f3e29555480ac1d5f342a2c23cd8d4b44db362dccf036c159037

Every subtree ran as an independently guarded serial parent→candidate pair. Process snapshots were clear before, between, and after the two invocations; interrupted/overlapped attempts were discarded. Each retained parent/candidate diagnostic transcript was byte-identical:

  • test/language/block-scope: 0 fail / 145 pass → 0 / 145
  • test/language/statements/while: 0 / 38 → 0 / 38
  • test/language/statements/do-while: 0 / 36 → 0 / 36
  • test/language/statements/for: 0 / 385 → 0 / 385
  • test/language/statements/for-in: 0 / 115 → 0 / 115
  • test/language/statements/for-of: 0 / 751 → 0 / 751
  • test/language/statements/for-await-of: 0 / 1,234 → 0 / 1,234
  • test/language/statements/switch: 0 / 111 → 0 / 111
  • test/language/statements/try: 0 / 201 → 0 / 201
  • test/language/statements/let: 0 / 145 → 0 / 145
  • test/language/statements/const: 0 / 136 → 0 / 136

Total scored invocations: 0 fail / 3,297 pass → 0 / 3,297, with nonzero denominators and zero flips.

Together with the existing focused compiler/frontend/GC checks and full 1,733 pass / 1 platform skip / 0 fail / 0 leak suite, the ordinary correctness accounting is complete. #618 remains open only for applicable sanitizer coverage and guarded exact-parent scaling/allocation evidence; no wall-time claim is made here.

8 days ago

Applicable compiler-focused TSan coverage is now complete on the current compiler state: 71/71 passed, including the repeated-body capture matrix, with zero skips, failures, or leaks. Combined with the previously recorded focused frontend/compiler gates, full 1,733-pass suite, and exact 3,297-case Test262 comparison, the correctness/sanitizer acceptance row is complete. #618 remains open only for guarded exact-parent performance evidence and final cross-linking.

8 days ago

Completed and published the remaining exact-parent bounded-scaling evidence.

The seven-pair quiet-reference comparison scores only normalized retired instructions and exact allocation replay across the frozen 1,024/2,048/4,096 closure-free repeated-body rows. Parent and candidate binaries use byte-identical one-commit measurement overlays, and the candidate remains the exact first child of the benchmark parent.

Results:

  • parent 1K→4K instruction growth: 14.6568x, exponent 1.937
  • candidate 1K→4K instruction growth: 4.0318x, exponent 1.006
  • candidate/parent instructions: 0.1188x, 0.0635x, 0.0327x
  • instruction RSD: below 0.10% for every variant/width
  • allocations/job P/C: 3,224/3,233, 6,314/6,324, 12,474/12,485
  • bytes/job P/C: 2,530,094/2,603,894, 5,644,262/5,791,814, 9,689,038/9,984,070
  • checksums: exact at all widths

The bounded deterministic allocation increase is published explicitly. Wall, throughput, latency, cycle, energy, RSS, thermal, and full-efficiency observations remain diagnostic and are not promoted. The 1K/2K ordinary rows retain their blocked full-efficiency decisions rather than weakening that gate.

All three performance-attribution-v3 inputs, the algorithmic-growth-v3 aggregate, all 324 documentation links, the 50-page BunPress build, and git diff --check pass. Correctness remains the previously measured 3,297 -> 3,297 targeted Test262 passes with zero flips, plus the focused/full/TSan gates recorded above. This satisfies the remaining evidence and cross-link acceptance items and closes #618; parent relationships #465, #493, and #496 and related loop-head boundary #616 remain recorded in the issue.

Sign in to comment on this issue.