A while loop whose remainder reads number locals that enter it without
range facts, such as copies of module globals, parameters or fields
(`const base = column;` before `(count + base - breaks) % 60`), now runs
as two copies. On entry, a test checks that each such local holds an
integer in [-2^48, 2^48] other than -0. The fast copy uses the facts the
integer range analysis proves when the function is analyzed again with
those entry ranges assumed: the loop's counters become bounded, and the
sums and remainders run as i64 arithmetic. When the test fails, the loop
runs as it did before.
- ir/loop-entry-ranges.ts picks innermost while loops with a remainder
that the assumed ranges prove and the function's own analysis does
not. The test covers only the locals the remainder depends on, directly,
through the loop's writes or through its condition.
- ir/integer-ranges.ts takes the assumed entry ranges per loop, and keeps
no fact about the assumed locals or the loop's writes after it.
- backend/llvm/loop-entry-versions.ts emits the test and both copies.
The test is marked likely. A function called only from the loop gets
`alwaysinline` on the fast copy's call, because the second copy would
otherwise cost it the inlining of a single call site.
reverse-complement (25M bases): 2.08 s -> 1.19 s; Node 3.97 s.