arrays: element facts inside loop regions — one preheader guard, bare in-region reads (S3) - #11671
Conversation
A loop region (#11650) now takes an array binding the loop cannot reassign as a receiver of its own when the body reads it as `xs[e & c]` (or `xs[c]`). The preheader checks the S1 guard word, the prototype facts a hole read needs and `c <u capacity` once, and derives the element base from the same header into a slot. In F-body the read is `load base; load [base + 8 idx]; hole ? undefined` and runs no JS, so it no longer stales the region's other facts. The base is an address: the loop poll's arm re-derives it from the binding's root, and so does every re-check. The verifier judges the emitted element reads for JS like every bare access, and separately requires that nothing that may collect lies before a read, and that an F-body path that collects leaves through a re-check. When the loop body also has a per-iteration receiver read from the array (`const o = objs[k & 7]; o.d = k; ...`), that body region is split inside the loop region's F-body; its G-tail and fact trees' generic arms set the loop's dirty flag. A plan that would re-check every iteration is not formed. The packed-f64-range tier's slow copy is lowered through the same region entry, and a `let` redeclared through `LocalSet` keeps the region plan for its cloned initializer.
Holes, Array.prototype and own-prototype changes, an accessor element, length shrink and growth, growth past capacity, stores, allocation in the body, a bound past capacity, a non-array receiver and a module const, each in the middle of a region loop, compared with node.
The earlier cases changed a fact in the middle of a loop through calls, so no array plan formed there and sabotaging the preheader guard left the test green. These cases keep the body call-free (one element read, an `undefined` count), so the loop region forms (PERRY_REGION_DIAG=4 prints a plan for each), and break the fact before the loop: an Array.prototype index over a holey array, a replaced own prototype, a read past the capacity (JSON.parse allocates exactly 8; a literal gets the minimum 16, read with `& 31`), a hole written by `delete`, with and without a prototype element at that index, a length shrink and pops, and receivers that are not arrays. An index property on a prototype turns the array fast paths off for the rest of the process, so the cases that set one run last; before, they ran first and every later region guard refused.
A loop region with array facts can carry a body region split inside its F-body (`const o = objs[k & 7]; o.d = k; ...`). Its G-tail set the loop's dirty flag, so where the body guard fails every iteration (the matrix addkey and inherited `varying` rows: a spilled key, an inherited key) the array guard re-ran every iteration, +24 instructions over main's straight-line read. The G-tail now clears the loop's valid flag instead, and the loop runs G-body, which keeps the nested body region (main's loop). This is the judgement that already refuses a plan which would re-check every iteration, made where the failure is seen. A receiver that failed only while its layout was warming up comes back: when the body guard passes inside G-body, it sets the dirty flag, and the loop's top tests that flag first and re-checks. Fact trees' generic arms still only set the dirty flag. The array verifier accepts an exit that left the region after a collection.
…ay-region-facts
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (11)
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 5 remain after this review. 📝 WalkthroughWalkthroughThe code generator now plans statically bounded array-element reads as loop-region facts, emits guarded base-relative loads with hole handling, and coordinates validity across collection and nested-region fallback. A TypeScript regression test covers array mutations, prototypes, holes, capacity bounds, and allocation. ChangesArray Region Facts
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~45 minutes Change: Refactor Suggested reviewers: Merge Risk: ⚪ Minimal · up to This change speeds up array element reads inside loops by checking array facts once before the loop. The concern that array bases could become stale after garbage collection was checked, and the refreshed base matches the one computed by the original guard. No outstanding defects remain, so the change appears ready to merge after normal checks. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 71.19% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 59 functions across 10 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches📝 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 |
Array slice S3: array element facts inside loop regions (#11650).
What
xs[e & c]orxs[c], one guard is checked in the region preheader. It covers the header guard word, the prototype facts andc < capacity.const o = objs[k & 7]) gets its body region inside the loop region.lead_lit_ctl's packed-f64 tier slow copy goes through the region entry, and a redeclaredletno longer loses the plan.Results (instructions per iteration, outputs identical to node)
Verification
test_gap_region_array_facts.ts(11 loop regions) matches node--check, wasm abi, sso, file size and fmt passSummary by CodeRabbit