Add unboxed slot-number lane to equality operators#2602
Merged
Conversation
===, !==, == and != now take the same per-node numeric lane the relational operators gained in sebastienros#2574/sebastienros#2578: when both operands resolve to slot-stored numbers (or a numeric constant), the comparison runs on raw doubles without materializing either side. Both operands are runtime-proven Numbers, so the IEEE compare is spec-exact for all four operators (IsLooselyEqual defers to IsStrictlyEqual for same-type operands; NaN == NaN is false, +0 == -0 is true); any non-number operand declines to the generic coercing path before any side effect. The dominant win is allocation: a loop test like `i === 50000` was re-boxing the counter into a JsNumber on every iteration once it exceeded the small-int cache. LoopDispatchBenchmarks A/B (default job, adjacent runs): | row | base | lanes | |-----------------|---------------------|---------------------| | StrictEqualTest | 5.967 ms / 2,808 KB | 3.710 ms / 3.26 KB | | LooseEqualTest | 5.523 ms / 2,808 KB | 3.777 ms / 3.27 KB | -38% / -32% time, -99.9% allocation (Gen0 eliminated). Untouched rows byte-identical alloc; time deltas were sign-mixed and traced to window drift via an A/B/A re-run on identical bits. New LoopDispatch rows (StrictEqualTest/LooseEqualTest/ModuloEqualTest) pin the shapes; ModuloEqualTest (the stopwatch.js if-chain shape) is deliberately not covered yet - its left side is a modulo expression, not an identifier. EqualityLaneTests pin NaN/signed-zero semantics, mid-loop type-change decline, BigInt/mixed-type bail and value positions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lahma
enabled auto-merge (squash)
July 9, 2026 05:55
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
===,!==,==and!=now take the same per-node unboxed numeric lane the relational operatorsgained in #2574/#2578: when both operands resolve to slot-stored numbers (or a numeric constant), the
comparison runs on raw doubles without materializing either side.
Why
A loop test like
i === 50000re-boxed the counter into aJsNumberon every iteration once itexceeded the small-int cache — the equality operators were the only comparison family without the lane.
Semantics
Both operands are runtime-proven Numbers (
TryGetNumberSlotonly succeeds for number-holdingbindings), so the IEEE compare is spec-exact for all four operators:
IsLooselyEqualdefers toIsStrictlyEqualfor same-type operands,NaN == NaNis false,+0 == -0is true. BigInt isexcluded by the JsNumber gates; any non-number operand declines to the generic coercing path before
any side effect.
Results
LoopDispatchBenchmarks A/B (default job, adjacent runs; new rows added by this PR):
Gen0 eliminated on both rows; untouched rows byte-identical allocation, time deltas sign-mixed and
resolved as window drift via an A/B/A on identical bits.
ModuloEqualTest(the stopwatch.js if-chain shape) is added as a baseline row but deliberately notcovered here — its left side is a modulo expression, not an identifier (see the follow-up PR).
Tests
EqualityLaneTestspin NaN/signed-zero semantics, mid-loop type-change decline, BigInt/mixed-typebail, and value positions.
Gating
All-TFM build ✅ · Jint.Tests net10+net472 ✅ · PublicInterface ×2 ✅ · Test262 99,260/0 ✅
🤖 Generated with Claude Code