Repository navigation
Conversation
Changelog💅 Polish
Semantic Diff2 commits · Full page Replace local mutable blocks in one pass over the function52c1aa6 · cknitt + Claude · Nov 07, 2026, 10:04 UTC
A value written to, or initializing, a never-read field is held back until its block is decided, as the old innermost-first order dropped such values before analyzing the blocks around them. The generated JavaScript is the same for the test suites and 1,600 generated functions. ✨ Analyze blocks without allocating a closure per nodeba4c395 · cknitt + Claude · Nov 07, 2026, 19:02 UTC · new The benchmarks showed the single pass allocating up to 0.9% more for functions without local blocks: each node passed a new closure to the traversal. The innermost slot is now a reference, so the traversal reuses one closure, and other inputs allocate as much as before. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #9258 +/- ##
=======================================
Coverage 81.74% 81.74%
=======================================
Files 457 457
Lines 65713 65751 +38
=======================================
+ Hits 53715 53748 +33
- Misses 11998 12003 +5
🚀 New features to boost your workflow:
|
rescript
@rescript/belt
@rescript/darwin-arm64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
|
Developer playground preview: https://rescript-lang.github.io/rescript/dev-playground/?version=pr-9258 |
Why:
The pass analyzed and rewrote a block's whole scope once per block, so
n refs used in one function cost O(n²): 1,000 refs updated in one loop
allocated 151M words. One traversal now decides every block and one
rewrite replaces them, so the same input allocates 17M.
A value written to, or initializing, a never-read field is held back until
its block is decided, as the old innermost-first order dropped such values
before analyzing the blocks around them. The generated JavaScript is the
same for the test suites and 1,600 generated functions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kbf3gK1dcjbe97yY1wRMgs
Signed-off-by: Christoph Knittel <ck@cca.io>
9862905 to
52c1aa6
Compare
🎉 Compiler benchmarksAllocated wordsIn total: 456,989,425 → 444,961,995 (-2.63%). 🎉 Improvement: 1 benchmark allocates more than 1% less.
InstructionsIn total: 10,226,961,997 → 10,032,277,336 (-1.90%). 🎉 Improvement: 1 benchmark executes more than 1% fewer instructions.
Pull request ba4c395 compared with base bf35764 in this run. |
The benchmarks showed the single pass allocating up to 0.9% more for functions without local blocks: each node passed a new closure to the traversal. The innermost slot is now a reference, so the traversal reuses one closure, and other inputs allocate as much as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kbf3gK1dcjbe97yY1wRMgs Signed-off-by: Christoph Knittel <ck@cca.io>
Part of #9256 (many refs updated in one loop).
The cause was scalar replacement (
Lam_pass_sroa): it analyzed and rewrote a block's whole scope once per block, so n refs used in one function cost O(n²). Now one traversal decides every block and one rewrite replaces them all.The old order replaced blocks from the innermost out, so when an inner block had a never-read field, the pure values written to or initializing that field were already gone when the blocks around it were analyzed. To give the same result, an occurrence inside such a value is held in a pending slot until the inner block is decided, and dropped if the field turns out never to be read. The rewrite then drops exactly those values without rewriting them.
Measured with
bscon linux-arm64, allocated words and wall time, n refs each updated once in oneforloop:The generated JavaScript is unchanged for the test suites and about 1,600 generated functions with refs and mutable records stored in, read from and written to each other. A first version without the pending slots differed on 3 of them.
New tests in
sroa_test.res: a ref stored only in a write-only ref, a ref read only by a write-only ref (both still become scalars), and several refs updated in one loop.🤖 Generated with Claude Code
https://claude.ai/code/session_01Kbf3gK1dcjbe97yY1wRMgs