Fused modulo-equality lane; admit const bindings to comparison-lane reads#2604
Merged
Conversation
lahma
force-pushed
the
perf-fused-modulo-test
branch
from
July 9, 2026 07:27
5adb9f8 to
c631b77
Compare
lahma
enabled auto-merge (squash)
July 9, 2026 07:59
lahma
force-pushed
the
perf-fused-modulo-test
branch
from
July 9, 2026 08:11
c631b77 to
6134981
Compare
`identifier % numericConstant ==/===/!=/!== numericConstant` - the stopwatch.js if-chain shape, executed ~820k times per benchmark op - is now evaluated as one fused lane on the equality expressions: the identifier is read through the same slot/global arms as the comparison lane and the whole test runs on raw doubles, skipping the modulo node's dispatch, operand materialization and result boxing. C# double % implements Number::remainder exactly, and the only integer-vs-double remainder divergence (-0 vs +0 results) is erased by the equality consumer (IEEE == treats them equal), so the fused result is spec-exact for any proven-Number operand: NaN dividends, % 0 and NaN constants compare false (true negated), v % ±Infinity == v. Non-number operands decline to the generic coercing path pre-effect. A/B (default job, adjacent runs, vs the global-arm base): - LoopDispatchBenchmarks.ModuloEqualTest 8.05 -> 5.56 ms (-31%), allocation 2,808 KB -> 3.3 KB (Gen0 eliminated) - stopwatch driver 167.9 -> 152.6 ms/iter (-9.1%); StopwatchBenchmark classic Execute -9.0%, classic prepared -10.5%, allocations flat Tests pin sign/zero behavior across negative dividends, NaN / % 0 / Infinity specials, the coercing decline, and the global-binding shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TryGetNumberSlot requires binding.Mutable so that its read-modify-write callers may pair it with SetNumberSlot unchecked - but the comparison, equality and fused-modulo lanes reused it for pure reads, inheriting the write-side constraint. Consequence: no lane ever fired on a `const` operand, and stopwatch-modern's entire `const z` if-chain (plus every `const` bound/limit in loop tests anywhere) declined per evaluation - turning the fused-modulo commit into a small regression on that row (driver +2.0%). The lanes now read through TryGetNumberSlotForRead, a read-only sibling without the Mutable gate. TDZ stays intact: an uninitialized binding has neither an unboxed number nor a reference value, so it declines and the generic path raises the ReferenceError. Writers keep the Mutable-gated form (compound assignment on a const must still throw through the generic path). A/B vs the global-arm base, both fused-modulo commits applied (default job, adjacent runs): | StopwatchBenchmark | base | fused+const | |----------------------|----------|-------------| | classic Execute | 158.6 ms | 141.6 ms (-10.7%) | | classic Prepared | 152.9 ms | 137.5 ms (-10.1%) | | modern Execute | 173.7 ms | 153.0 ms (-11.9%) | | modern Prepared | 169.7 ms | 149.8 ms (-11.7%) | Drivers: stopwatch-modern 186.3 -> 167.7 ms/iter (-10.0%, was +2.0% before this commit), classic 167.1 -> 153.0 (-8.4%). Allocations flat. Tests pin the const shapes through all three lane forms and the TDZ ReferenceError. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lahma
force-pushed
the
perf-fused-modulo-test
branch
from
July 9, 2026 08:49
b3b9425 to
ea41e99
Compare
This was referenced Jul 13, 2026
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.
What
Two commits, each independently measured:
identifier % numericConstant ==/===/!=/!== numericConstant—the stopwatch.js if-chain shape (
z % 2 == 0, executed ~820k times per benchmark op) — isevaluated as one fused lane on the equality expressions: the identifier is read through the same
slot/global arms as the comparison lane and the whole test runs on raw doubles, skipping the
modulo node's dispatch, operand materialization and result boxing.
TryGetNumberSlotrequiresbinding.Mutableso itsread-modify-write callers can pair it with
SetNumberSlotunchecked — but the comparison lanesreused it for pure reads, inheriting the write-side constraint. Consequence: no lane ever fired
on a
constoperand (stopwatch-modern's entireconst zchain declined per evaluation). Thelanes now read through
TryGetNumberSlotForRead, a read-only sibling without the gate.Semantics
C#
double %implementsNumber::remainderexactly; the only integer-vs-double remainderdivergence (−0 vs +0 results) is erased by the equality consumer (IEEE
==treats them equal) — sothe fused result is spec-exact for any proven-Number operand: NaN dividends,
% 0and NaN constantscompare false,
v % ±Infinity == v. TDZ stays intact for the const admission: an uninitializedbinding has neither an unboxed number nor a reference value, so it declines and the generic path
raises the ReferenceError. Writers keep the Mutable-gated form (compound assignment on a const still
throws through the generic path).
Results
Commit 1 (fused lane), A/B vs the global-arm base (default job, adjacent runs):
Commit 2 (const admission), cumulative A/B vs the same base:
Allocations flat on every row.
Tests
Fused-lane sign/zero behavior across negative dividends, NaN /
% 0/ Infinity specials, thecoercing decline, the global-binding shape, const bindings through all three lane forms, and the TDZ
ReferenceError.
Gating
All-TFM build ✅ · Jint.Tests ×2 ✅ · PublicInterface ×2 ✅ · Test262 99,260/0 ✅
🤖 Generated with Claude Code