emitter: one counting header for every range loop - #308
Merged
Merged
Conversation
A range loop now lowers to the same counting header in every form:
block statement, block in tail or value position, postfix, and
parenthesized comprehension. The postfix and comprehension forms, and a
block loop in tail position, used to materialize the range with
Array.from and iterate it with for-of; only the component render path
still builds the range, because __reconcile consumes the array.
Direction: a literal step's sign decides, and a zero literal rejects; a
variable step binds once and the header tests its sign; with no step,
two literal bounds decide statically, so [3..1] descends in every form,
and any variable bound ascends, with `by -1` to descend. Negative
literal bounds are literals, so [2..-2] needs no temp. A second loop
variable is the iteration index, which the block paths never bound.
Behavior that changes: [1..n] with n = 0 ran twice in the materialized
forms and now runs zero times, which fixes the zero-tests and zero-jobs
cases in the sites and swarm packages; a variable range with start
above end ran downward in those forms and is now empty; a fractional
bound compares directly instead of flooring through Array length, and
the one site that relied on the floor, the pdf name-record reader, now
spells it with >> 1.
Corpus: compose4.js line 16 changes from the Array.from helper to
`for (let n = 1; n <= 3; n++) {`, with its map; 184 artifacts unchanged.
The parser is untouched. The browser bundle is regenerated under the
pinned Bun.
Measured on the loop alone with a trivial body: 4.8x faster at 8
iterations, 9.0x at 1024, 6.9x at 65536. The barcodes package emits no
materialized range anywhere, its parity oracles report zero mismatches,
and its bench is unchanged because its hot loops were already block
form.
Battery: 36 rows across loops.rip and comprehensions.rip pin every form
and direction, the two-variable form, evaluation order, per-iteration
capture, fractional and negative bounds, and the emitted headers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every range loop now lowers to the same counting header. The postfix and comprehension forms, and a block loop in tail position, used to build the range with
Array.fromand iterate it withfor...of; they now emit the loop the block form always emitted. The component render path keeps the materialized range because__reconcileconsumes it.Direction rule. A literal step's sign decides. A variable step binds once and the header tests its sign. With no step, two literal bounds decide statically, so
[3..1]descends in every form, and any variable bound ascends, withby -1to descend.Behavior that changes
[1..n]withn = 0ran twice in the materialized forms and now runs zero times. This fixes the sites test runner spawning two workers for zero tests and swarm spawning two for zero jobs.Arraylength. The pdf name-record reader relied on the floor and now spells it with>> 1.for v, i in [1..3]never boundiin block form; it does now, in every form.Measured on the loop alone: 4.8x faster at 8 iterations, 9.0x at 1024, 6.9x at 65536.
Artifacts.
compose4.jschanges one line, from the helper tofor (let n = 1; n <= 3; n++) {, with its map; 184 corpus artifacts unchanged; parser untouched; browser bundle regenerated under Bun 1.4.0.Test
36 new battery rows in
loops.ripandcomprehensions.rip. Language battery 3250 passing, toolchain gates 661 passing, barcodes parity oracles at zero mismatches with no materialized range left in the package.