Fuse strict-equality guards: x === undefined/null and typeof x === "literal"#2658
Merged
Merged
Conversation
…iteral" QuickJS dedicates opcodes (is_undefined, is_null, typeof_is_*) to these guard idioms; the AST equivalent is a build-time specialized node. When one side of ===/!== is the undefined identifier, the null literal, or the other side of a typeof expression is a string literal, the fused node evaluates only the interesting operand and answers with a single internal-type test - or, for typeof, a reference compare against the interned singleton the literal maps to at build time (typeof only ever produces those singletons; an unmapped literal can never match but the operand still evaluates for its side effects). The undefined form keys off the same compile-time folding the identifier itself evaluates through (BindingName.CalculatedValue), the typeof side still evaluates through JintTypeOfExpression (unresolvable identifiers and host-object classification untouched), and literal-vs-literal keeps the existing constant fold. New GuardComparisonBenchmark (LCG-mixed inputs), default job: | Lane | Before | After | Δ | |--------------------- |----------:|---------:|---------:| | IsUndefinedGuard | 13.54 ms | 10.36 ms | -23.5% | | IsNullGuard | 12.42 ms | 10.29 ms | -17.1% | | TypeofStringGuard | 17.06 ms | 14.24 ms | -16.5% | | TypeofUndefinedGuard | 11.87 ms | 10.73 ms | -9.6% | | TypeofSwitchMixed | 22.13 ms | 18.65 ms | -15.7% | | LooseNullGuard | 10.13 ms | 10.26 ms | untouched| linq-js within run-to-run noise (its Execute lane varies +-5% between identical binaries). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lahma
force-pushed
the
perf-quickjs-ws2-strict-eq-fusion
branch
from
July 13, 2026 08:38
c195636 to
0fde311
Compare
lahma
enabled auto-merge (squash)
July 13, 2026 08:40
This was referenced Jul 15, 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.
Third PR of the quickjs-ng learnings campaign (#2654 lanes, #2656 for-in array fast path). QuickJS dedicates opcodes (
is_undefined,is_null,typeof_is_undefined,typeof_is_function) to these guard idioms, produced by its peephole pass; this is the AST-interpreter equivalent — a build-time specialized node.What
When one side of
===/!==is statically known, the generic strict-equality node (evaluate both operands, generic dispatch) is replaced at build time:x === undefined/x === null(either orientation): evaluates only the interesting operand; the answer is a single internal-type test. Theundefinedform keys off the same compile-time folding the identifier itself already evaluates through (BindingName.CalculatedValue), so semantics match the unfused path exactly. Strict equality against these singletons is precisely a type-identity check (document.all-style IsHTMLDDA values are only loosely equal to them, so the strict path needs no carve-out).typeof x === "literal": the typeof side still evaluates throughJintTypeOfExpression— unresolvable identifiers and host-object classification untouched — and since typeof only ever produces the internedJsStringsingletons, the comparison is a reference test against the singleton the literal maps to at build time. An unmapped literal ("bogus") can never match, but the operand still evaluates for its side effects.Literal-vs-literal expressions keep the existing constant fold; loose equality (
== null) is untouched.Numbers
New
GuardComparisonBenchmark(LCG-mixed inputs the branch predictor cannot memorize), default job:linq-js is within run-to-run noise (its
Executelane varies ±5% between identical binaries).Tests
New
GuardFusionTestspin: both orientations, every typeof result string, undeclared-identifier typeof (no throw), impossible literals evaluating side effects exactly once, CLR interop callables classifying as"function", fused guards acrossawaitsuspension, and the if-statement boolean fast path.Full gate green: Jint.Tests, PublicInterface, CommonScripts, and a clean 99,429-test Test262 run.
🤖 Generated with Claude Code