Strict Equality on objects should only check that one of the two sides is an object.
https://bugs.webkit.org/show_bug.cgi?id=145992
Source/JavaScriptCore:
This patch adds a new optimization for checking strict equality on objects.
If we speculate that a strict equality comparison has an object on one side
we only need to type check that side. Equality is then determined by a pointer
comparison between the two values (although in the 32-bit case we must also check
that the other side is a cell). Once LICM hoists type checks out of a loop we
can be cleverer about how we choose the operand we type check if both are
speculated to be objects.
For testing I added the addressOf function, which returns the address
of a Cell to the runtime.
Patch by Keith Miller <keith_miller@apple.com> on 2015-06-24
Reviewed by Mark Lam.
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileStrictEq):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compileObjectStrictEquality):
(JSC::DFG::SpeculativeJIT::compilePeepHoleObjectStrictEquality):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compileObjectStrictEquality):
(JSC::DFG::SpeculativeJIT::compilePeepHoleObjectStrictEquality):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileCompareStrictEq):
* jsc.cpp:
(GlobalObject::finishCreation):
(functionAddressOf):
* tests/stress/equality-type-checking.js: Added.
(Foo):
(checkStrictEq):
(checkStrictEqOther):
LayoutTests:
Patch by Keith Miller <keith_miller@apple.com> on 2015-06-24
Reviewed by Mark Lam.
Adds a test that checks if strict equality checks with objects properly exit out of DFG code when
dealing with document.all, which is an object that masquerades as undefined.
* js/dom/document-all-strict-eq-expected.txt: Added.
* js/dom/document-all-strict-eq.html: Added.
* js/dom/script-tests/document-all-strict-eq.js: Added.
(f):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@185920 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 872275d..1affa1a 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -471,7 +471,21 @@
fixEdge<StringUse>(node->child2());
break;
}
- if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
+ WatchpointSet* masqueradesAsUndefinedWatchpoint = m_graph.globalObjectFor(node->origin.semantic)->masqueradesAsUndefinedWatchpoint();
+ if (masqueradesAsUndefinedWatchpoint->isStillValid()) {
+
+ if (node->child1()->shouldSpeculateObject()) {
+ m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
+ fixEdge<ObjectUse>(node->child1());
+ break;
+ }
+ if (node->child2()->shouldSpeculateObject()) {
+ m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
+ fixEdge<ObjectUse>(node->child2());
+ break;
+ }
+
+ } else if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
fixEdge<ObjectUse>(node->child1());
fixEdge<ObjectUse>(node->child2());
break;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index de54e73..98db3e0 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -3915,6 +3915,36 @@
return false;
}
+ if (node->isBinaryUseKind(ObjectUse, UntypedUse)) {
+ unsigned branchIndexInBlock = detectPeepHoleBranch();
+ if (branchIndexInBlock != UINT_MAX) {
+ Node* branchNode = m_block->at(branchIndexInBlock);
+ compilePeepHoleObjectStrictEquality(node->child1(), node->child2(), branchNode);
+ use(node->child1());
+ use(node->child2());
+ m_indexInBlock = branchIndexInBlock;
+ m_currentNode = branchNode;
+ return true;
+ }
+ compileObjectStrictEquality(node->child1(), node->child2());
+ return false;
+ }
+
+ if (node->isBinaryUseKind(UntypedUse, ObjectUse)) {
+ unsigned branchIndexInBlock = detectPeepHoleBranch();
+ if (branchIndexInBlock != UINT_MAX) {
+ Node* branchNode = m_block->at(branchIndexInBlock);
+ compilePeepHoleObjectStrictEquality(node->child2(), node->child1(), branchNode);
+ use(node->child1());
+ use(node->child2());
+ m_indexInBlock = branchIndexInBlock;
+ m_currentNode = branchNode;
+ return true;
+ }
+ compileObjectStrictEquality(node->child2(), node->child1());
+ return false;
+ }
+
if (node->isBinaryUseKind(ObjectUse)) {
unsigned branchIndexInBlock = detectPeepHoleBranch();
if (branchIndexInBlock != UINT_MAX) {
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 1ff980c..5d219da 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -2088,8 +2088,10 @@
void compilePeepHoleBooleanBranch(Node*, Node* branchNode, JITCompiler::RelationalCondition);
void compilePeepHoleDoubleBranch(Node*, Node* branchNode, JITCompiler::DoubleCondition);
void compilePeepHoleObjectEquality(Node*, Node* branchNode);
+ void compilePeepHoleObjectStrictEquality(Edge objectChild, Edge otherChild, Node* branchNode);
void compilePeepHoleObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild, Node* branchNode);
void compileObjectEquality(Node*);
+ void compileObjectStrictEquality(Edge objectChild, Edge otherChild);
void compileObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild);
void compileObjectOrOtherLogicalNot(Edge value);
void compileLogicalNot(Node*);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 28c5caa..c876151 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -1184,6 +1184,57 @@
booleanResult(resultPayloadGPR, node);
}
+void SpeculativeJIT::compileObjectStrictEquality(Edge objectChild, Edge otherChild)
+{
+ SpeculateCellOperand op1(this, objectChild);
+ JSValueOperand op2(this, otherChild);
+
+ GPRReg op1GPR = op1.gpr();
+ GPRReg op2GPR = op2.payloadGPR();
+
+ DFG_TYPE_CHECK(JSValueSource::unboxedCell(op1GPR), objectChild, SpecObject, m_jit.branchIfNotObject(op1GPR));
+
+ GPRTemporary resultPayload(this, Reuse, op1);
+ GPRReg resultPayloadGPR = resultPayload.gpr();
+
+ MacroAssembler::Jump op2CellJump = m_jit.branchIfCell(op2.jsValueRegs());
+
+ m_jit.move(TrustedImm32(0), resultPayloadGPR);
+ MacroAssembler::Jump op2NotCellJump = m_jit.jump();
+
+ // At this point we know that we can perform a straight-forward equality comparison on pointer
+ // values because we are doing strict equality.
+ op2CellJump.link(&m_jit);
+ m_jit.compare32(MacroAssembler::Equal, op1GPR, op2GPR, resultPayloadGPR);
+
+ op2NotCellJump.link(&m_jit);
+ booleanResult(resultPayloadGPR, m_currentNode);
+}
+
+void SpeculativeJIT::compilePeepHoleObjectStrictEquality(Edge objectChild, Edge otherChild, Node* branchNode)
+{
+ BasicBlock* taken = branchNode->branchData()->taken.block;
+ BasicBlock* notTaken = branchNode->branchData()->notTaken.block;
+
+ SpeculateCellOperand op1(this, objectChild);
+ JSValueOperand op2(this, otherChild);
+
+ GPRReg op1GPR = op1.gpr();
+ GPRReg op2GPR = op2.payloadGPR();
+
+ DFG_TYPE_CHECK(JSValueSource::unboxedCell(op1GPR), objectChild, SpecObject, m_jit.branchIfNotObject(op1GPR));
+
+ branch32(MacroAssembler::NotEqual, op2.tagGPR(), TrustedImm32(JSValue::CellTag), notTaken);
+
+ if (taken == nextBlock()) {
+ branch32(MacroAssembler::NotEqual, op1GPR, op2GPR, notTaken);
+ jump(taken);
+ } else {
+ branch32(MacroAssembler::Equal, op1GPR, op2GPR, taken);
+ jump(notTaken);
+ }
+}
+
void SpeculativeJIT::compileObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild)
{
SpeculateCellOperand op1(this, leftChild);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 27b77df7..a67b2f3 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -1296,6 +1296,47 @@
jsValueResult(resultGPR, m_currentNode, DataFormatJSBoolean);
}
+void SpeculativeJIT::compileObjectStrictEquality(Edge objectChild, Edge otherChild)
+{
+ SpeculateCellOperand op1(this, objectChild);
+ JSValueOperand op2(this, otherChild);
+ GPRTemporary result(this);
+
+ GPRReg op1GPR = op1.gpr();
+ GPRReg op2GPR = op2.gpr();
+ GPRReg resultGPR = result.gpr();
+
+ DFG_TYPE_CHECK(JSValueSource::unboxedCell(op1GPR), objectChild, SpecObject, m_jit.branchIfNotObject(op1GPR));
+
+ // At this point we know that we can perform a straight-forward equality comparison on pointer
+ // values because we are doing strict equality.
+ m_jit.compare64(MacroAssembler::Equal, op1GPR, op2GPR, resultGPR);
+ m_jit.or32(TrustedImm32(ValueFalse), resultGPR);
+ jsValueResult(resultGPR, m_currentNode, DataFormatJSBoolean);
+}
+
+void SpeculativeJIT::compilePeepHoleObjectStrictEquality(Edge objectChild, Edge otherChild, Node* branchNode)
+{
+ BasicBlock* taken = branchNode->branchData()->taken.block;
+ BasicBlock* notTaken = branchNode->branchData()->notTaken.block;
+
+ SpeculateCellOperand op1(this, objectChild);
+ JSValueOperand op2(this, otherChild);
+
+ GPRReg op1GPR = op1.gpr();
+ GPRReg op2GPR = op2.gpr();
+
+ DFG_TYPE_CHECK(JSValueSource::unboxedCell(op1GPR), objectChild, SpecObject, m_jit.branchIfNotObject(op1GPR));
+
+ if (taken == nextBlock()) {
+ branchPtr(MacroAssembler::NotEqual, op1GPR, op2GPR, notTaken);
+ jump(taken);
+ } else {
+ branchPtr(MacroAssembler::Equal, op1GPR, op2GPR, taken);
+ jump(notTaken);
+ }
+}
+
void SpeculativeJIT::compileObjectToObjectOrOtherEquality(Edge leftChild, Edge rightChild)
{
SpeculateCellOperand op1(this, leftChild);