Diff
Modified: trunk/JSTests/ChangeLog (282199 => 282200)
--- trunk/JSTests/ChangeLog 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/JSTests/ChangeLog 2021-09-09 08:10:20 UTC (rev 282200)
@@ -1,3 +1,17 @@
+2021-09-09 Robin Morisset <[email protected]>
+
+ Optimize compareStrictEq when neither side is a double and at least one is not a BigInt
+ https://bugs.webkit.org/show_bug.cgi?id=226755
+ <rdar://problem/79321542>
+
+ Reviewed by Yusuke Suzuki.
+
+ Made the error messages in stress/reflect-set a bit more informative in the process of debugging an issue with the patch.
+
+ * stress/reflect-set.js:
+ (shouldBe):
+ (shouldThrow):
+
2021-09-07 Yusuke Suzuki <[email protected]>
[JSC] Implement Temporal.PlainTime
Modified: trunk/JSTests/stress/reflect-set.js (282199 => 282200)
--- trunk/JSTests/stress/reflect-set.js 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/JSTests/stress/reflect-set.js 2021-09-09 08:10:20 UTC (rev 282200)
@@ -1,6 +1,6 @@
function shouldBe(actual, expected) {
if (actual !== expected)
- throw new Error('bad value: ' + actual);
+ throw new Error('bad value: ' + describe(actual) + ', expected: ' + describe(expected));
}
function shouldThrow(func, message) {
@@ -11,9 +11,9 @@
error = e;
}
if (!error)
- throw new Error('not thrown.');
+ throw new Error('not thrown. Expected: ' + message);
if (String(error) !== message)
- throw new Error('bad error: ' + String(error));
+ throw new Error('bad error: ' + String(error) + ', expected: ' + message);
}
function unreachable()
Modified: trunk/Source/_javascript_Core/ChangeLog (282199 => 282200)
--- trunk/Source/_javascript_Core/ChangeLog 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-09-09 08:10:20 UTC (rev 282200)
@@ -1,3 +1,74 @@
+2021-09-09 Robin Morisset <[email protected]>
+
+ Optimize compareStrictEq when neither side is a double and at least one is not a BigInt
+ https://bugs.webkit.org/show_bug.cgi?id=226755
+ <rdar://problem/79321542>
+
+ Reviewed by Yusuke Suzuki.
+
+ This is a very similar patch to https://bugs.webkit.org/show_bug.cgi?id=226676.
+ The difference is that here we allow Strings on both side of the comparison, so we must add code to handle equality among strings.
+
+ Like for that other patch, the optimization is disabled for BigInt32.
+ Enabling it in that case would either need modifying the speculation (from banning HeapBigInt to banning all BigInts), or ensuring that we can never have a HeapBigInt so small it compares equal to a BigInt32.
+
+ I only implemented this optimization on 64-bits: it is just painful to write code that handles registers at such a low-level without a 32-bit machine to test things locally.
+ If anyone wants to make this optimization work on 32-bit, I don't foretell any major difficulty.
+
+ Finally, like quite a few other useKinds already, this case does not make the CompareStrictEq merge with an adjacent Branch.
+ The reason is simply that this patch relies on compileStringEquality, which currently does not support that feature.
+ I intend to fix this (for all useKinds at once) in a separate patch.
+
+ Effect on microbenchmarks:
+ poly-stricteq-not-double 46.8000+-0.4110 ^ 23.5872+-0.3061 ^ definitely 1.9841x faster
+ poly-stricteq-not-double-nor-string 16.6880+-0.2317 16.3627+-0.3729 might be 1.0199x faster
+ poly-stricteq 49.2175+-0.6047 48.9532+-0.6758
+
+ I looked at how many cases of Untyped/Untyped compareStrictEq have been fixed by this patch and two other recent patches.
+ On JetStream2:
+ - https://bugs.webkit.org/show_bug.cgi?id=226621: 731 instances
+ - https://bugs.webkit.org/show_bug.cgi?id=226676: 944 instances
+ - This patch: only 26 instances
+ This leaves 20 instances of Untyped/Untyped.
+
+ On Speedometer2.0:
+ - https://bugs.webkit.org/show_bug.cgi?id=226621: 1587 instances
+ - https://bugs.webkit.org/show_bug.cgi?id=226676: 2784 instances
+ - This patch: 891 instances
+ This leaves 75 instances of Untyped/Untyped.
+
+ * bytecode/SpeculatedType.h:
+ (JSC::isNeitherDoubleNorHeapBigIntSpeculation):
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC):
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupCompareStrictEqAndSameValue):
+ * dfg/DFGNode.h:
+ (JSC::DFG::Node::shouldSpeculateNeitherDoubleNorHeapBigInt):
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::SafeToExecuteEdge::operator()):
+ * dfg/DFGSpeculativeJIT.cpp:
+ (JSC::DFG::SpeculativeJIT::compileStrictEq):
+ (JSC::DFG::SpeculativeJIT::emitBitwiseJSValueEquality):
+ (JSC::DFG::SpeculativeJIT::emitBranchOnBitwiseJSValueEquality):
+ (JSC::DFG::SpeculativeJIT::compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality):
+ (JSC::DFG::SpeculativeJIT::compilePeepHoleNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality):
+ (JSC::DFG::SpeculativeJIT::speculateNeitherDoubleNorHeapBigInt):
+ (JSC::DFG::SpeculativeJIT::speculateNeitherDoubleNorHeapBigIntNorString):
+ (JSC::DFG::SpeculativeJIT::speculate):
+ * dfg/DFGSpeculativeJIT.h:
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compileNeitherDoubleNorHeapBigIntToNotDoubleStrictEquality):
+ * dfg/DFGUseKind.cpp:
+ (WTF::printInternal):
+ * dfg/DFGUseKind.h:
+ (JSC::DFG::typeFilterFor):
+ (JSC::DFG::checkMayCrashIfInputIsEmpty):
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile):
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
+
2021-09-07 Yusuke Suzuki <[email protected]>
Unreviewed, add files to xcodeproj
Modified: trunk/Source/_javascript_Core/bytecode/SpeculatedType.h (282199 => 282200)
--- trunk/Source/_javascript_Core/bytecode/SpeculatedType.h 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/bytecode/SpeculatedType.h 2021-09-09 08:10:20 UTC (rev 282200)
@@ -449,6 +449,11 @@
return !(type & (SpecFullDouble | SpecHeapBigInt | SpecString));
}
+inline bool isNeitherDoubleNorHeapBigIntSpeculation(SpeculatedType type)
+{
+ return !(type & (SpecFullDouble | SpecHeapBigInt));
+}
+
inline bool isOtherSpeculation(SpeculatedType value)
{
return value == SpecOther;
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (282199 => 282200)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2021-09-09 08:10:20 UTC (rev 282200)
@@ -492,7 +492,7 @@
|| node->isBinaryUseKind(ObjectUse)
|| node->isBinaryUseKind(MiscUse, UntypedUse) || node->isBinaryUseKind(UntypedUse, MiscUse)
|| node->isBinaryUseKind(StringIdentUse, NotStringVarUse) || node->isBinaryUseKind(NotStringVarUse, StringIdentUse)
- || node->isBinaryUseKind(NotDoubleUse, NeitherDoubleNorHeapBigIntNorStringUse) || node->isBinaryUseKind(NotDoubleUse, NeitherDoubleNorHeapBigIntNorStringUse))
+ || node->isBinaryUseKind(NotDoubleUse, NeitherDoubleNorHeapBigIntNorStringUse) || node->isBinaryUseKind(NeitherDoubleNorHeapBigIntNorStringUse, NotDoubleUse))
return false;
return true;
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (282199 => 282200)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2021-09-09 08:10:20 UTC (rev 282200)
@@ -4477,7 +4477,23 @@
node->setOpAndDefaultFlags(CompareStrictEq);
return;
}
-#endif
+#if USE(JSVALUE64)
+ if (node->child1()->shouldSpeculateNeitherDoubleNorHeapBigInt()
+ && node->child2()->shouldSpeculateNotDouble()) {
+ fixEdge<NeitherDoubleNorHeapBigIntUse>(node->child1());
+ fixEdge<NotDoubleUse>(node->child2());
+ node->setOpAndDefaultFlags(CompareStrictEq);
+ return;
+ }
+ if (node->child1()->shouldSpeculateNotDouble()
+ && node->child2()->shouldSpeculateNeitherDoubleNorHeapBigInt()) {
+ fixEdge<NotDoubleUse>(node->child1());
+ fixEdge<NeitherDoubleNorHeapBigIntUse>(node->child2());
+ node->setOpAndDefaultFlags(CompareStrictEq);
+ return;
+ }
+#endif // USE(JSVALUE64)
+#endif // !USE(BIGINT32)
}
void fixupChecksInBlock(BasicBlock* block)
Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (282199 => 282200)
--- trunk/Source/_javascript_Core/dfg/DFGNode.h 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h 2021-09-09 08:10:20 UTC (rev 282200)
@@ -2887,6 +2887,11 @@
{
return isNeitherDoubleNorHeapBigIntNorStringSpeculation(prediction());
}
+
+ bool shouldSpeculateNeitherDoubleNorHeapBigInt()
+ {
+ return isNeitherDoubleNorHeapBigIntSpeculation(prediction());
+ }
bool shouldSpeculateUntypedForArithmetic()
{
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (282199 => 282200)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2021-09-09 08:10:20 UTC (rev 282200)
@@ -94,6 +94,7 @@
case DoubleRepAnyIntUse:
case NotDoubleUse:
case NeitherDoubleNorHeapBigIntNorStringUse:
+ case NeitherDoubleNorHeapBigIntUse:
return;
case KnownInt32Use:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (282199 => 282200)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp 2021-09-09 08:10:20 UTC (rev 282200)
@@ -7226,7 +7226,21 @@
compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(node, notDoubleChild, neitherDoubleNorHeapBigIntNorStringChild);
return false;
}
-#endif
+#if USE(JSVALUE64)
+ if (node->isBinaryUseKind(NeitherDoubleNorHeapBigIntUse, NotDoubleUse)) {
+ Edge neitherDoubleNorHeapBigIntChild = node->child1();
+ Edge notDoubleChild = node->child2();
+ compileNeitherDoubleNorHeapBigIntToNotDoubleStrictEquality(node, neitherDoubleNorHeapBigIntChild, notDoubleChild);
+ return false;
+ }
+ if (node->isBinaryUseKind(NotDoubleUse, NeitherDoubleNorHeapBigIntUse)) {
+ Edge notDoubleChild = node->child1();
+ Edge neitherDoubleNorHeapBigIntChild = node->child2();
+ compileNeitherDoubleNorHeapBigIntToNotDoubleStrictEquality(node, neitherDoubleNorHeapBigIntChild, notDoubleChild);
+ return false;
+ }
+#endif // USE(JSVALUE64)
+#endif // !USE(BIGINT32)
if (node->isBinaryUseKind(HeapBigIntUse)) {
compileHeapBigIntEquality(node);
@@ -7457,6 +7471,40 @@
}
}
+void SpeculativeJIT::emitBitwiseJSValueEquality(JSValueRegs& left, JSValueRegs& right, GPRReg& result)
+{
+#if USE(JSVALUE64)
+ m_jit.compare64(JITCompiler::Equal, left.gpr(), right.gpr(), result);
+#else
+ m_jit.move(TrustedImm32(0), result);
+ JITCompiler::Jump notEqual = m_jit.branch32(JITCompiler::NotEqual, left.tagGPR(), right.tagGPR());
+ m_jit.compare32(JITCompiler::Equal, left.payloadGPR(), right.payloadGPR(), result);
+ notEqual.link(&m_jit);
+#endif
+}
+
+void SpeculativeJIT::emitBranchOnBitwiseJSValueEquality(JSValueRegs& left, JSValueRegs& right, BasicBlock* taken, BasicBlock* notTaken)
+{
+#if USE(JSVALUE64)
+ if (taken == nextBlock()) {
+ branch64(JITCompiler::NotEqual, left.gpr(), right.gpr(), notTaken);
+ jump(taken);
+ } else {
+ branch64(JITCompiler::Equal, left.gpr(), right.gpr(), taken);
+ jump(notTaken);
+ }
+#else
+ branch32(JITCompiler::NotEqual, left.tagGPR(), right.tagGPR(), notTaken);
+ if (taken == nextBlock()) {
+ branch32(JITCompiler::NotEqual, left.payloadGPR(), right.payloadGPR(), notTaken);
+ jump(taken);
+ } else {
+ branch32(JITCompiler::Equal, left.payloadGPR(), right.payloadGPR(), taken);
+ jump(notTaken);
+ }
+#endif
+}
+
void SpeculativeJIT::compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(Node* node, Edge notDoubleChild, Edge neitherDoubleNorHeapBigIntNorStringChild)
{
JSValueOperand left(this, notDoubleChild, ManualOperandSpeculation);
@@ -7476,14 +7524,7 @@
speculateNotDouble(notDoubleChild, leftRegs, tempGPR);
speculateNeitherDoubleNorHeapBigIntNorString(neitherDoubleNorHeapBigIntNorStringChild, rightRegs, tempGPR);
-#if USE(JSVALUE64)
- m_jit.compare64(JITCompiler::Equal, left.gpr(), right.gpr(), result.gpr());
-#else
- m_jit.move(TrustedImm32(0), result.gpr());
- JITCompiler::Jump notEqual = m_jit.branch32(JITCompiler::NotEqual, left.tagGPR(), right.tagGPR());
- m_jit.compare32(JITCompiler::Equal, left.payloadGPR(), right.payloadGPR(), result.gpr());
- notEqual.link(&m_jit);
-#endif
+ emitBitwiseJSValueEquality(leftRegs, rightRegs, resultGPR);
unblessedBooleanResult(resultGPR, node);
}
@@ -7503,24 +7544,7 @@
BasicBlock* taken = branchNode->branchData()->taken.block;
BasicBlock* notTaken = branchNode->branchData()->notTaken.block;
-#if USE(JSVALUE64)
- if (taken == nextBlock()) {
- branch64(JITCompiler::NotEqual, left.gpr(), right.gpr(), notTaken);
- jump(taken);
- } else {
- branch64(JITCompiler::Equal, left.gpr(), right.gpr(), taken);
- jump(notTaken);
- }
-#else
- branch32(JITCompiler::NotEqual, left.tagGPR(), right.tagGPR(), notTaken);
- if (taken == nextBlock()) {
- branch32(JITCompiler::NotEqual, left.payloadGPR(), right.payloadGPR(), notTaken);
- jump(taken);
- } else {
- branch32(JITCompiler::Equal, left.payloadGPR(), right.payloadGPR(), taken);
- jump(notTaken);
- }
-#endif
+ emitBranchOnBitwiseJSValueEquality(leftRegs, rightRegs, taken, notTaken);
}
void SpeculativeJIT::compileStringEquality(
@@ -11781,6 +11805,42 @@
speculateNotDouble(edge, regs, tempGPR);
}
+void SpeculativeJIT::speculateNeitherDoubleNorHeapBigInt(Edge edge, JSValueRegs regs, GPRReg tempGPR)
+{
+ if (!needsTypeCheck(edge, ~(SpecFullDouble | SpecHeapBigInt)))
+ return;
+
+ JITCompiler::JumpList done;
+
+ bool mayBeInt32 = needsTypeCheck(edge, ~SpecInt32Only);
+ if (mayBeInt32)
+ done.append(m_jit.branchIfInt32(regs));
+
+ DFG_TYPE_CHECK(regs, edge, ~SpecFullDouble, m_jit.branchIfNumber(regs, tempGPR));
+
+ bool mayBeNotCell = needsTypeCheck(edge, SpecCell);
+ if (mayBeNotCell)
+ done.append(m_jit.branchIfNotCell(regs));
+
+ DFG_TYPE_CHECK(regs, edge, ~SpecHeapBigInt, m_jit.branchIfHeapBigInt(regs.payloadGPR()));
+
+ if (mayBeInt32 || mayBeNotCell)
+ done.link(&m_jit);
+}
+
+void SpeculativeJIT::speculateNeitherDoubleNorHeapBigInt(Edge edge)
+{
+ if (!needsTypeCheck(edge, ~(SpecFullDouble | SpecHeapBigInt)))
+ return;
+
+ JSValueOperand operand(this, edge, ManualOperandSpeculation);
+ GPRTemporary temp(this);
+ JSValueRegs regs = operand.jsValueRegs();
+ GPRReg tempGPR = temp.gpr();
+
+ speculateNeitherDoubleNorHeapBigInt(edge, regs, tempGPR);
+}
+
void SpeculativeJIT::speculateNeitherDoubleNorHeapBigIntNorString(Edge edge, JSValueRegs regs, GPRReg tempGPR)
{
if (!needsTypeCheck(edge, ~(SpecFullDouble | SpecString | SpecHeapBigInt)))
@@ -11794,14 +11854,14 @@
DFG_TYPE_CHECK(regs, edge, ~SpecFullDouble, m_jit.branchIfNumber(regs, tempGPR));
- bool mayNotBeCell = needsTypeCheck(edge, SpecCell);
- if (mayNotBeCell)
+ bool mayBeNotCell = needsTypeCheck(edge, SpecCell);
+ if (mayBeNotCell)
done.append(m_jit.branchIfNotCell(regs));
static_assert(StringType + 1 == HeapBigIntType);
DFG_TYPE_CHECK(regs, edge, ~(SpecString | SpecHeapBigInt), m_jit.branchIfType(regs.payloadGPR(), JSTypeRange { StringType, HeapBigIntType }));
- if (mayBeInt32 || mayNotBeCell)
+ if (mayBeInt32 || mayBeNotCell)
done.link(&m_jit);
}
@@ -12003,6 +12063,9 @@
case NotDoubleUse:
speculateNotDouble(edge);
break;
+ case NeitherDoubleNorHeapBigIntUse:
+ speculateNeitherDoubleNorHeapBigInt(edge);
+ break;
case NeitherDoubleNorHeapBigIntNorStringUse:
speculateNeitherDoubleNorHeapBigIntNorString(edge);
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (282199 => 282200)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h 2021-09-09 08:10:20 UTC (rev 282200)
@@ -1248,6 +1248,11 @@
void compileSymbolEquality(Node*);
void compileHeapBigIntEquality(Node*);
void compilePeepHoleSymbolEquality(Node*, Node* branchNode);
+#if USE(JSVALUE64)
+ void compileNeitherDoubleNorHeapBigIntToNotDoubleStrictEquality(Node*, Edge neitherDoubleNorHeapBigInt, Edge notDouble);
+#endif
+ void emitBitwiseJSValueEquality(JSValueRegs&, JSValueRegs&, GPRReg& result);
+ void emitBranchOnBitwiseJSValueEquality(JSValueRegs&, JSValueRegs&, BasicBlock* taken, BasicBlock* notTaken);
void compileNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(Node*, Edge notDoubleEdge, Edge neitherDoubleNorHeapBigIntNorStringEdge);
void compilePeepHoleNotDoubleNeitherDoubleNorHeapBigIntNorStringStrictEquality(Node*, Node* branchNode, Edge notDoubleEdge, Edge neitherDoubleNorHeapBigIntNorStringEdge);
void compileSymbolUntypedEquality(Node*, Edge symbolEdge, Edge untypedEdge);
@@ -1740,6 +1745,8 @@
void speculateNotCellNorBigInt(Edge);
void speculateNotDouble(Edge, JSValueRegs, GPRReg temp);
void speculateNotDouble(Edge);
+ void speculateNeitherDoubleNorHeapBigInt(Edge, JSValueRegs, GPRReg temp);
+ void speculateNeitherDoubleNorHeapBigInt(Edge);
void speculateNeitherDoubleNorHeapBigIntNorString(Edge, JSValueRegs, GPRReg temp);
void speculateNeitherDoubleNorHeapBigIntNorString(Edge);
void speculateOther(Edge, JSValueRegs, GPRReg temp);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (282199 => 282200)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2021-09-09 08:10:20 UTC (rev 282200)
@@ -336,6 +336,95 @@
}
}
+void SpeculativeJIT::compileNeitherDoubleNorHeapBigIntToNotDoubleStrictEquality(Node* node, Edge neitherDoubleNorHeapBigIntChild, Edge notDoubleChild)
+{
+ ASSERT(neitherDoubleNorHeapBigIntChild.useKind() == NeitherDoubleNorHeapBigIntUse);
+ ASSERT(notDoubleChild.useKind() == NotDoubleUse);
+
+ JSValueOperand left(this, neitherDoubleNorHeapBigIntChild, ManualOperandSpeculation);
+ JSValueOperand right(this, notDoubleChild, ManualOperandSpeculation);
+
+ GPRTemporary temp(this);
+ GPRTemporary leftTemp(this);
+ GPRTemporary rightTemp(this);
+ GPRTemporary leftTemp2(this, Reuse, left);
+ GPRTemporary rightTemp2(this, Reuse, right);
+ JSValueRegs leftRegs = left.jsValueRegs();
+ JSValueRegs rightRegs = right.jsValueRegs();
+
+ GPRReg leftGPR = leftRegs.payloadGPR();
+ GPRReg rightGPR = rightRegs.payloadGPR();
+ GPRReg tempGPR = temp.gpr();
+ GPRReg leftTempGPR = leftTemp.gpr();
+ GPRReg rightTempGPR = rightTemp.gpr();
+ GPRReg leftTemp2GPR = leftTemp2.gpr();
+ GPRReg rightTemp2GPR = rightTemp2.gpr();
+
+ // We try to avoid repeated and redundant checks here, which leads to the following pseudo-code:
+ /*
+ if (left == true) {
+ speculateNeitherDoubleNorHeapBigInt(left);
+ return true;
+ }
+ speculateNotDouble(right);
+ speculateNotDouble(left);
+ if (left is not Cell)
+ return false;
+ Check that left is not HeapBigInt;
+ if (left is not String)
+ return false;
+ if (right is not Cell)
+ return false;
+ if (right is not String)
+ return false;
+ return stringEquality(left, right)
+ }
+ */
+ // Mercifully we can often eliminate much of this soup of conditionals with the help of the abstract interpreter.
+
+ JITCompiler::JumpList trueCase;
+ JITCompiler::JumpList falseCase;
+
+ JITCompiler::Jump notEqual = m_jit.branch64(JITCompiler::NotEqual, leftGPR, rightGPR);
+ // We cannot use speculateNeitherDoubleNorHeapBigInt here, because it updates the interpreter state, and we can skip over it.
+ // So we would always skip the speculateNotDouble that follows.
+ if (needsTypeCheck(neitherDoubleNorHeapBigIntChild, ~SpecFullDouble)) {
+ if (needsTypeCheck(neitherDoubleNorHeapBigIntChild, ~SpecInt32Only))
+ trueCase.append(m_jit.branchIfInt32(leftRegs));
+ speculationCheck(BadType, leftRegs, neitherDoubleNorHeapBigIntChild.node(), m_jit.branchIfNumber(leftRegs, tempGPR));
+ }
+ if (needsTypeCheck(neitherDoubleNorHeapBigIntChild, ~SpecHeapBigInt)) {
+ if (needsTypeCheck(neitherDoubleNorHeapBigIntChild, SpecCell))
+ trueCase.append(m_jit.branchIfNotCell(leftRegs));
+ speculationCheck(BadType, leftRegs, neitherDoubleNorHeapBigIntChild.node(), m_jit.branchIfHeapBigInt(leftGPR));
+ }
+ trueCase.append(m_jit.jump());
+ notEqual.link(&m_jit);
+
+ speculateNotDouble(notDoubleChild, rightRegs, tempGPR);
+ speculateNotDouble(neitherDoubleNorHeapBigIntChild, leftRegs, tempGPR);
+
+ bool leftMayBeNotCell = needsTypeCheck(neitherDoubleNorHeapBigIntChild, SpecCellCheck);
+ if (leftMayBeNotCell)
+ falseCase.append(m_jit.branchIfNotCell(leftRegs));
+
+ DFG_TYPE_CHECK(leftRegs, neitherDoubleNorHeapBigIntChild, ~SpecHeapBigInt, m_jit.branchIfHeapBigInt(leftGPR));
+
+ bool leftMayBeNotStringKnowingCell = needsTypeCheck(neitherDoubleNorHeapBigIntChild, (~SpecString) & SpecCellCheck);
+ if (leftMayBeNotStringKnowingCell)
+ falseCase.append(m_jit.branchIfNotString(leftGPR));
+
+ bool rightMayBeNotCell = needsTypeCheck(notDoubleChild, SpecCellCheck);
+ if (rightMayBeNotCell)
+ falseCase.append(m_jit.branchIfNotCell(rightRegs));
+
+ bool rightMayBeNotStringKnowingCell = needsTypeCheck(notDoubleChild, (~SpecString) & SpecCellCheck);
+ if (rightMayBeNotStringKnowingCell)
+ falseCase.append(m_jit.branchIfNotString(rightGPR));
+
+ compileStringEquality(node, leftGPR, rightGPR, tempGPR, leftTempGPR, rightTempGPR, leftTemp2GPR, rightTemp2GPR, trueCase, falseCase);
+}
+
void SpeculativeJIT::nonSpeculativePeepholeStrictEq(Node* node, Node* branchNode, bool invert)
{
BasicBlock* taken = branchNode->branchData()->taken.block;
Modified: trunk/Source/_javascript_Core/dfg/DFGUseKind.cpp (282199 => 282200)
--- trunk/Source/_javascript_Core/dfg/DFGUseKind.cpp 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/dfg/DFGUseKind.cpp 2021-09-09 08:10:20 UTC (rev 282200)
@@ -173,6 +173,9 @@
case NotDoubleUse:
out.print("NotDouble");
return;
+ case NeitherDoubleNorHeapBigIntUse:
+ out.print("NeitherDoubleNorHeapBigInt");
+ return;
case NeitherDoubleNorHeapBigIntNorStringUse:
out.print("NeitherDoubleNorHeapBigIntNorString");
return;
Modified: trunk/Source/_javascript_Core/dfg/DFGUseKind.h (282199 => 282200)
--- trunk/Source/_javascript_Core/dfg/DFGUseKind.h 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/dfg/DFGUseKind.h 2021-09-09 08:10:20 UTC (rev 282200)
@@ -82,6 +82,7 @@
NotCellUse,
NotCellNorBigIntUse,
NotDoubleUse,
+ NeitherDoubleNorHeapBigIntUse,
NeitherDoubleNorHeapBigIntNorStringUse,
KnownOtherUse,
OtherUse,
@@ -191,6 +192,8 @@
return ~SpecCellCheck & ~SpecBigInt;
case NotDoubleUse:
return ~SpecFullDouble;
+ case NeitherDoubleNorHeapBigIntUse:
+ return ~SpecFullDouble & ~SpecHeapBigInt;
case NeitherDoubleNorHeapBigIntNorStringUse:
return ~(SpecFullDouble | SpecHeapBigInt | SpecString);
case KnownOtherUse:
@@ -312,6 +315,7 @@
case NotCellUse:
case NotCellNorBigIntUse:
case NotDoubleUse:
+ case NeitherDoubleNorHeapBigIntUse:
case NeitherDoubleNorHeapBigIntNorStringUse:
return false;
default:
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (282199 => 282200)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2021-09-09 08:10:20 UTC (rev 282200)
@@ -527,6 +527,7 @@
case AnyIntUse:
case DoubleRepAnyIntUse:
case NotDoubleUse:
+ case NeitherDoubleNorHeapBigIntUse:
case NeitherDoubleNorHeapBigIntNorStringUse:
// These are OK.
break;
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (282199 => 282200)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2021-09-09 07:37:01 UTC (rev 282199)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2021-09-09 08:10:20 UTC (rev 282200)
@@ -9821,6 +9821,15 @@
return;
}
+ if (m_node->isBinaryUseKind(NeitherDoubleNorHeapBigIntUse, NotDoubleUse)) {
+ compileNeitherDoubleNorHeapBigIntToNotDoubleStrictEquality(m_node->child1(), m_node->child2());
+ return;
+ }
+ if (m_node->isBinaryUseKind(NotDoubleUse, NeitherDoubleNorHeapBigIntUse)) {
+ compileNeitherDoubleNorHeapBigIntToNotDoubleStrictEquality(m_node->child2(), m_node->child1());
+ return;
+ }
+
// FIXME: we can do something much smarter here, see the DFGSpeculativeJIT approach in e.g. SpeculativeJIT::nonSpeculativePeepholeStrictEq
DFG_ASSERT(m_graph, m_node, m_node->isBinaryUseKind(UntypedUse), m_node->child1().useKind(), m_node->child2().useKind());
genericJSValueCompare(
@@ -9869,6 +9878,91 @@
setBoolean(m_out.phi(Int32, fastTrue, fastFalse, slowResult));
}
+ void compileNeitherDoubleNorHeapBigIntToNotDoubleStrictEquality(Edge neitherDoubleNorHeapBigIntEdge, Edge notDoubleEdge)
+ {
+ ASSERT(neitherDoubleNorHeapBigIntEdge.useKind() == NeitherDoubleNorHeapBigIntUse);
+ ASSERT(notDoubleEdge.useKind() == NotDoubleUse);
+
+ LValue leftValue = lowJSValue(neitherDoubleNorHeapBigIntEdge, ManualOperandSpeculation);
+ LValue rightValue = lowJSValue(notDoubleEdge, ManualOperandSpeculation);
+ SpeculatedType leftValueType = provenType(neitherDoubleNorHeapBigIntEdge);
+ SpeculatedType rightValueType = provenType(notDoubleEdge);
+
+ LBasicBlock triviallyEqualCase = m_out.newBlock();
+ LBasicBlock leftIsNotInt32EqualCase = m_out.newBlock();
+ LBasicBlock leftIsCellEqualCase = m_out.newBlock();
+ LBasicBlock returnTrueBlock = m_out.newBlock();
+ LBasicBlock notTriviallyEqualCase = m_out.newBlock();
+ LBasicBlock leftIsCell = m_out.newBlock();
+ LBasicBlock leftIsString = m_out.newBlock();
+ LBasicBlock rightIsCell = m_out.newBlock();
+ LBasicBlock rightIsString = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+
+ // We try to avoid repeated and redundant checks here, which leads to the following pseudo-code:
+ /*
+ if (left == right) {
+ speculateNeitherDoubleNorHeapBigInt(left);
+ return true;
+ }
+ speculateNotDouble(right);
+ speculateNotDouble(left);
+ if (left is not Cell)
+ return false;
+ Check that left is not HeapBigInt;
+ if (left is not String)
+ return false;
+ if (right is not Cell)
+ return false;
+ if (right is not String)
+ return false;
+ return stringEquality(left, right)
+ }
+ */
+ m_out.branch(m_out.equal(leftValue, rightValue), unsure(triviallyEqualCase), unsure(notTriviallyEqualCase));
+
+ LBasicBlock lastNext = m_out.appendTo(triviallyEqualCase, leftIsNotInt32EqualCase);
+ // Logically we want speculateNeitherDoubleNorHeapBigInt, but we cannot use it here as it changes the state of the abstract interpreter.
+ // So if we used it here, we would skip the later checks.
+ // Instead we reimplement it in this and the next few blocks, using typeCheckWithoutUpdatingInterpreter.
+ m_out.branch(isInt32(leftValue, leftValueType), unsure(returnTrueBlock), unsure(leftIsNotInt32EqualCase));
+
+ m_out.appendTo(leftIsNotInt32EqualCase, leftIsCellEqualCase);
+ typeCheckWithoutUpdatingInterpreter(jsValueValue(leftValue), neitherDoubleNorHeapBigIntEdge, ~SpecFullDouble, isNumber(leftValue));
+ m_out.branch(isCell(leftValue, leftValueType & ~SpecFullNumber), unsure(leftIsCellEqualCase), unsure(returnTrueBlock));
+
+ m_out.appendTo(leftIsCellEqualCase, returnTrueBlock);
+ typeCheckWithoutUpdatingInterpreter(jsValueValue(leftValue), neitherDoubleNorHeapBigIntEdge, ~SpecHeapBigInt, isHeapBigInt(leftValue));
+ m_out.jump(returnTrueBlock);
+
+ m_out.appendTo(returnTrueBlock, notTriviallyEqualCase);
+ ValueFromBlock fastTrue = m_out.anchor(m_out.booleanTrue);
+ m_out.jump(continuation);
+
+ m_out.appendTo(notTriviallyEqualCase, leftIsCell);
+ speculateNotDouble(neitherDoubleNorHeapBigIntEdge);
+ speculateNotDouble(notDoubleEdge);
+ ValueFromBlock fastFalse = m_out.anchor(m_out.booleanFalse);
+ m_out.branch(isNotCell(leftValue, leftValueType & ~SpecFullDouble), unsure(continuation), unsure(leftIsCell));
+
+ m_out.appendTo(leftIsCell, leftIsString);
+ FTL_TYPE_CHECK(jsValueValue(leftValue), neitherDoubleNorHeapBigIntEdge, ~SpecHeapBigInt, isHeapBigInt(leftValue));
+ m_out.branch(isNotString(leftValue, leftValueType & SpecCell & ~SpecHeapBigInt), unsure(continuation), unsure(leftIsString));
+
+ m_out.appendTo(leftIsString, rightIsCell);
+ m_out.branch(isNotCell(rightValue, rightValueType & ~SpecFullDouble), unsure(continuation), unsure(rightIsCell));
+
+ m_out.appendTo(rightIsCell, rightIsString);
+ m_out.branch(isNotString(rightValue, rightValueType & SpecCell & ~SpecFullDouble), unsure(continuation), unsure(rightIsString));
+
+ m_out.appendTo(rightIsString, continuation);
+ ValueFromBlock slowResult = m_out.anchor(stringsEqual(leftValue, rightValue, neitherDoubleNorHeapBigIntEdge, notDoubleEdge));
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ setBoolean(m_out.phi(Int32, fastTrue, fastFalse, slowResult));
+ }
+
void compileCompareEqPtr()
{
setBoolean(
@@ -18047,25 +18141,25 @@
{
m_interpreter.filter(highValue, typesPassedThrough);
}
-
- void typeCheck(
+
+ void typeCheckWithoutUpdatingInterpreter(
FormattedValue lowValue, Edge highValue, SpeculatedType typesPassedThrough,
LValue failCondition, ExitKind exitKind = BadType)
{
- appendTypeCheck(lowValue, highValue, typesPassedThrough, failCondition, exitKind);
- }
-
- void appendTypeCheck(
- FormattedValue lowValue, Edge highValue, SpeculatedType typesPassedThrough,
- LValue failCondition, ExitKind exitKind)
- {
if (!m_interpreter.needsTypeCheck(highValue, typesPassedThrough))
return;
ASSERT(mayHaveTypeCheck(highValue.useKind()));
appendOSRExit(exitKind, lowValue, highValue.node(), failCondition, m_origin);
+ }
+
+ void typeCheck(
+ FormattedValue lowValue, Edge highValue, SpeculatedType typesPassedThrough,
+ LValue failCondition, ExitKind exitKind = BadType)
+ {
+ typeCheckWithoutUpdatingInterpreter(lowValue, highValue, typesPassedThrough, failCondition, exitKind);
m_interpreter.filter(highValue, typesPassedThrough);
}
-
+
LValue lowInt32(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation)
{
ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || (edge.useKind() == Int32Use || edge.useKind() == KnownInt32Use));
@@ -18921,6 +19015,9 @@
case NotDoubleUse:
speculateNotDouble(edge);
break;
+ case NeitherDoubleNorHeapBigIntUse:
+ speculateNeitherDoubleNorHeapBigInt(edge);
+ break;
case NeitherDoubleNorHeapBigIntNorStringUse:
speculateNeitherDoubleNorHeapBigIntNorString(edge);
break;
@@ -18988,6 +19085,30 @@
m_out.appendTo(continuation, lastNext);
}
+ void speculateNeitherDoubleNorHeapBigInt(Edge edge)
+ {
+ if (!m_interpreter.needsTypeCheck(edge))
+ return;
+
+ LValue value = lowJSValue(edge, ManualOperandSpeculation);
+
+ LBasicBlock isNotInt32 = m_out.newBlock();
+ LBasicBlock isCellBlock = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+
+ m_out.branch(isInt32(value, provenType(edge)), unsure(continuation), unsure(isNotInt32));
+
+ LBasicBlock lastNext = m_out.appendTo(isNotInt32, isCellBlock);
+ FTL_TYPE_CHECK(jsValueValue(value), edge, ~SpecFullDouble, isNumber(value));
+ m_out.branch(isCell(value, provenType(edge) & ~SpecFullNumber), unsure(isCellBlock), unsure(continuation));
+
+ m_out.appendTo(isCellBlock, continuation);
+ FTL_TYPE_CHECK(jsValueValue(value), edge, ~SpecHeapBigInt, isHeapBigInt(value));
+ m_out.jump(continuation);
+
+ m_out.appendTo(continuation, lastNext);
+ }
+
void speculateNeitherDoubleNorHeapBigIntNorString(Edge edge)
{
if (!m_interpreter.needsTypeCheck(edge))