Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 41ec81351b41fd60b018e9f6f399a602a4a1b496
https://github.com/WebKit/WebKit/commit/41ec81351b41fd60b018e9f6f399a602a4a1b496
Author: Yijia Huang <[email protected]>
Date: 2026-09-10 (Thu, 10 Sep 2026)
Changed paths:
A JSTests/microbenchmarks/mul-negated-operand-chain.js
A JSTests/microbenchmarks/mul-negated-operand-integrate.js
A JSTests/stress/multiply-negated-operand.js
M Source/JavaScriptCore/b3/B3LowerToAir.cpp
M Source/JavaScriptCore/b3/testb3.h
M Source/JavaScriptCore/b3/testb3_3.cpp
Log Message:
-----------
[JSC] Lower Mul(Neg(n), m) to MNEG/FNMUL
https://bugs.webkit.org/show_bug.cgi?id=323150
rdar://186407016
Reviewed by Yusuke Suzuki.
B3LowerToAir folds Neg(Mul(n, m)) in case Neg, but nothing matches the other
shape, Mul(Neg(n), m), which is what "-n * m" parses to. B3ReduceStrength
rewrites that shape into Neg(Mul(n, m)) only under isInteger(), so the floating
point form reaches lowering and emits fneg plus fmul. Match it in case Mul.
(-n) * m and -(n * m) are the same value: negation only flips the sign bit, and
the product's magnitude and rounding do not depend on the sign of either input.
Extending the B3ReduceStrength rewrite to floating point instead was
implemented and measured. It performs the same, but the profitability test is
much harder to state there. Consider:
a = -x * y; // a is still live at a later OSR exit
b = a * z;
which reaches B3 as:
n = Neg(x)
a = Mul(n, y)
b = Mul(a, z)
Check(..., a, ...)
Canonicalizing a is a win. n has one use, so it goes away:
p = Mul(x, y) // 1 use: a
a = Neg(p) // 2 uses: Check, b
b = Mul(a, z)
The rule now wants to do the same to b, since its first child is a Neg. That
second rewrite loses:
p = Mul(x, y) // 2 uses: a, q <- no longer absorbable
a = Neg(p) // 1 use: Check <- did not go away
q = Mul(p, z) // 1 use: b
b = Neg(q)
The Check still needs a itself, so the negation survives, and p has picked up a
second reader. canBeInternal will not absorb a multiply with two readers, so
fnmul a, x, y
fmul b, a, z
becomes
fmul p, x, y
fneg a, p
fnmul b, p, z
Declining that rewrite requires knowing the use count of the Neg in order to
protect the use count of the multiply one level up. B3ReduceStrength has no use
counts, and adding them to a pass that rewrites the graph to a fixpoint means
keeping them correct across insertions. In B3LowerToAir the same decision is a
single local question about a single value, canBeInternal on the Neg, asked
once the use counts are final.
Canonicalizing would additionally let Neg(Neg(x)) -> x cancel negations out of
a chain of negated multiplies, which this patch does not get. That is a
separate optimization and is left for follow-up.
ToT Patched
mul-negated-operand-chain 89.7970+-0.1399 ^ 60.4523+-0.3202
^ definitely 1.4854x faster
mul-negated-operand-integrate 90.6986+-0.1278 ^ 84.9924+-0.1883
^ definitely 1.0671x faster
Tests: JSTests/microbenchmarks/mul-negated-operand-chain.js
JSTests/microbenchmarks/mul-negated-operand-integrate.js
JSTests/stress/multiply-negated-operand.js
Source/JavaScriptCore/b3/testb3_3.cpp
Canonical link: https://commits.webkit.org/320861@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications