Use the JITAddGenerator snippet in the DFG.
https://bugs.webkit.org/show_bug.cgi?id=151266
Reviewed by Geoffrey Garen.
No tests added because the op_add.js stress test already tests for correctness
(using the LLINT as a reference).
Performance-wise, the difference from the pre-existing DFG implementation is
insignificant (at least as measured on x86 and x86_64). We're moving forward
with adopting this implementation because it unifies the 32-bit and 64-bit
implementations, as well as lays ground work for a repatching inline cache
implementation of op_add later.
* dfg/DFGAbstractValue.cpp:
(JSC::DFG::AbstractValue::resultType):
- Made an assertion less restrictive. For ValueAdd operands, the DFG thinks that
the operand can also be empty (though we should never see this in practice).
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
- Add fallback to unused type operands.
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileValueAdd):
- Introduce a common function to compile the ValueAdd node.
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::JSValueOperand::JSValueOperand):
- Add the forwarding constructor so that we can use Optional<JSValueOperand>.
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
- Changed to use the common compileValueAdd().
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::moveValue):
- Similar to moveTrustedValue() but used for untrusted constants.
* jit/JITAddGenerator.cpp:
(JSC::JITAddGenerator::generateFastPath):
* jit/JITAddGenerator.h:
(JSC::JITAddGenerator::JITAddGenerator):
- Updated to take the left or right operand as a constant. This is necessary
because the client should not be making assumptions about whether the snippet
will determine the operation to be commutative or not. Instead, the client
should just pass in the operands and let the snippet do any operand order
swapping if necessary.
* jit/JITArithmetic.cpp:
(JSC::JIT::emit_op_add):
- Updated to use the new JITAddGenerator interface.
* tests/stress/op_add.js:
(stringifyIfNeeded):
(runTest):
- Made test output more clear about when string results are expected.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@192531 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp b/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
index 7181c6a..b6fcf89 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
@@ -499,7 +499,7 @@
ResultType AbstractValue::resultType() const
{
- ASSERT(isType(SpecHeapTop));
+ ASSERT(isType(SpecBytecodeTop));
if (isType(SpecBoolean))
return ResultType::booleanType();
if (isType(SpecInt32))
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index c4f36f9..e0a89ea 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -155,8 +155,9 @@
if (attemptToMakeFastStringAdd(node))
break;
- // We could attempt to turn this into a StrCat here. But for now, that wouldn't
- // significantly reduce the number of branches required.
+ fixEdge<UntypedUse>(node->child1());
+ fixEdge<UntypedUse>(node->child2());
+ node->setResult(NodeResultJS);
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index e2f99c4..b4a6438 100755
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -38,6 +38,7 @@
#include "DFGSaneStringGetByValSlowPathGenerator.h"
#include "DFGSlowPathGenerator.h"
#include "DirectArguments.h"
+#include "JITAddGenerator.h"
#include "JITSubGenerator.h"
#include "JSArrowFunction.h"
#include "JSCInlines.h"
@@ -2782,6 +2783,127 @@
blessedBooleanResult(scratchReg, node);
}
+void SpeculativeJIT::compileValueAdd(Node* node)
+{
+ if (isKnownNotNumber(node->child1().node()) || isKnownNotNumber(node->child2().node())) {
+ JSValueOperand left(this, node->child1());
+ JSValueOperand right(this, node->child2());
+ JSValueRegs leftRegs = left.jsValueRegs();
+ JSValueRegs rightRegs = right.jsValueRegs();
+#if USE(JSVALUE64)
+ GPRTemporary result(this);
+ JSValueRegs resultRegs = JSValueRegs(result.gpr());
+#else
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+ JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr());
+#endif
+ flushRegisters();
+ callOperation(operationValueAddNotNumber, resultRegs, leftRegs, rightRegs);
+ m_jit.exceptionCheck();
+
+ jsValueResult(resultRegs, node);
+ return;
+ }
+
+ bool leftIsConstInt32 = node->child1()->isInt32Constant();
+ bool rightIsConstInt32 = node->child2()->isInt32Constant();
+
+ // The DFG does not always fold the sum of 2 constant int operands together.
+ if (leftIsConstInt32 && rightIsConstInt32) {
+#if USE(JSVALUE64)
+ GPRTemporary result(this);
+ JSValueRegs resultRegs = JSValueRegs(result.gpr());
+#else
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+ JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr());
+#endif
+ int64_t leftConst = node->child1()->asInt32();
+ int64_t rightConst = node->child2()->asInt32();
+ int64_t resultConst = leftConst + rightConst;
+ m_jit.moveValue(JSValue(resultConst), resultRegs);
+ jsValueResult(resultRegs, node);
+ return;
+ }
+
+ Optional<JSValueOperand> left;
+ Optional<JSValueOperand> right;
+
+ JSValueRegs leftRegs;
+ JSValueRegs rightRegs;
+
+ FPRTemporary leftNumber(this);
+ FPRTemporary rightNumber(this);
+ FPRReg leftFPR = leftNumber.fpr();
+ FPRReg rightFPR = rightNumber.fpr();
+
+#if USE(JSVALUE64)
+ GPRTemporary result(this);
+ JSValueRegs resultRegs = JSValueRegs(result.gpr());
+ GPRTemporary scratch(this);
+ GPRReg scratchGPR = scratch.gpr();
+ FPRReg scratchFPR = InvalidFPRReg;
+#else
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+ JSValueRegs resultRegs = JSValueRegs(resultPayload.gpr(), resultTag.gpr());
+ GPRReg scratchGPR = resultTag.gpr();
+ FPRTemporary fprScratch(this);
+ FPRReg scratchFPR = fprScratch.fpr();
+#endif
+
+ ResultType leftType = m_state.forNode(node->child1()).resultType();
+ ResultType rightType = m_state.forNode(node->child2()).resultType();
+ int32_t leftConstInt32 = 0;
+ int32_t rightConstInt32 = 0;
+
+ ASSERT(!leftIsConstInt32 || !rightIsConstInt32);
+
+ if (leftIsConstInt32) {
+ leftConstInt32 = node->child1()->asInt32();
+ right = JSValueOperand(this, node->child2());
+ rightRegs = right->jsValueRegs();
+ } else if (rightIsConstInt32) {
+ left = JSValueOperand(this, node->child1());
+ leftRegs = left->jsValueRegs();
+ rightConstInt32 = node->child2()->asInt32();
+ } else {
+ left = JSValueOperand(this, node->child1());
+ leftRegs = left->jsValueRegs();
+ right = JSValueOperand(this, node->child2());
+ rightRegs = right->jsValueRegs();
+ }
+
+ JITAddGenerator gen(resultRegs, leftRegs, rightRegs, leftType, rightType,
+ leftIsConstInt32, rightIsConstInt32, leftConstInt32, rightConstInt32,
+ leftFPR, rightFPR, scratchGPR, scratchFPR);
+ gen.generateFastPath(m_jit);
+
+ gen.slowPathJumpList().link(&m_jit);
+
+ silentSpillAllRegisters(resultRegs);
+
+ if (leftIsConstInt32) {
+ leftRegs = resultRegs;
+ int64_t leftConst = node->child1()->asInt32();
+ m_jit.moveValue(JSValue(leftConst), leftRegs);
+ } else if (rightIsConstInt32) {
+ rightRegs = resultRegs;
+ int64_t rightConst = node->child2()->asInt32();
+ m_jit.moveValue(JSValue(rightConst), rightRegs);
+ }
+
+ callOperation(operationValueAdd, resultRegs, leftRegs, rightRegs);
+
+ silentFillAllRegisters(resultRegs);
+ m_jit.exceptionCheck();
+
+ gen.endJumpList().link(&m_jit);
+ jsValueResult(resultRegs, node);
+ return;
+}
+
void SpeculativeJIT::compileArithAdd(Node* node)
{
switch (node->binaryUseKind()) {
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 4b835ef..ba7270c 100755
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -2207,6 +2207,7 @@
void compileValueToInt32(Node*);
void compileUInt32ToNumber(Node*);
void compileDoubleAsInt32(Node*);
+ void compileValueAdd(Node*);
void compileArithAdd(Node*);
void compileMakeRope(Node*);
void compileArithClz32(Node*);
@@ -2552,6 +2553,32 @@
#endif
}
+ explicit JSValueOperand(JSValueOperand&& other)
+ : m_jit(other.m_jit)
+ , m_edge(other.m_edge)
+ {
+#if USE(JSVALUE64)
+ m_gprOrInvalid = other.m_gprOrInvalid;
+#elif USE(JSVALUE32_64)
+ m_register.pair.tagGPR = InvalidGPRReg;
+ m_register.pair.payloadGPR = InvalidGPRReg;
+ m_isDouble = other.m_isDouble;
+
+ if (m_edge) {
+ if (m_isDouble)
+ m_register.fpr = other.m_register.fpr;
+ else
+ m_register.pair = other.m_register.pair;
+ }
+#endif
+ other.m_edge = Edge();
+#if USE(JSVALUE64)
+ other.m_gprOrInvalid = InvalidGPRReg;
+#elif USE(JSVALUE32_64)
+ other.m_isDouble = false;
+#endif
+ }
+
~JSValueOperand()
{
if (!m_edge)
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index e62a9cb..5bd6ebb 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -2134,29 +2134,10 @@
compileValueRep(node);
break;
}
-
- case ValueAdd: {
- JSValueOperand op1(this, node->child1());
- JSValueOperand op2(this, node->child2());
-
- GPRReg op1TagGPR = op1.tagGPR();
- GPRReg op1PayloadGPR = op1.payloadGPR();
- GPRReg op2TagGPR = op2.tagGPR();
- GPRReg op2PayloadGPR = op2.payloadGPR();
-
- flushRegisters();
-
- GPRFlushedCallResult2 resultTag(this);
- GPRFlushedCallResult resultPayload(this);
- if (isKnownNotNumber(node->child1().node()) || isKnownNotNumber(node->child2().node()))
- callOperation(operationValueAddNotNumber, resultTag.gpr(), resultPayload.gpr(), op1TagGPR, op1PayloadGPR, op2TagGPR, op2PayloadGPR);
- else
- callOperation(operationValueAdd, resultTag.gpr(), resultPayload.gpr(), op1TagGPR, op1PayloadGPR, op2TagGPR, op2PayloadGPR);
- m_jit.exceptionCheck();
-
- jsValueResult(resultTag.gpr(), resultPayload.gpr(), node);
+
+ case ValueAdd:
+ compileValueAdd(node);
break;
- }
case StrCat: {
JSValueOperand op1(this, node->child1(), ManualOperandSpeculation);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 4f5b129..67b1cf8 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -2268,27 +2268,11 @@
}
break;
}
-
- case ValueAdd: {
- JSValueOperand op1(this, node->child1());
- JSValueOperand op2(this, node->child2());
-
- GPRReg op1GPR = op1.gpr();
- GPRReg op2GPR = op2.gpr();
-
- flushRegisters();
-
- GPRFlushedCallResult result(this);
- if (isKnownNotNumber(node->child1().node()) || isKnownNotNumber(node->child2().node()))
- callOperation(operationValueAddNotNumber, result.gpr(), op1GPR, op2GPR);
- else
- callOperation(operationValueAdd, result.gpr(), op1GPR, op2GPR);
- m_jit.exceptionCheck();
-
- jsValueResult(result.gpr(), node);
+
+ case ValueAdd:
+ compileValueAdd(node);
break;
- }
-
+
case StrCat: {
JSValueOperand op1(this, node->child1(), ManualOperandSpeculation);
JSValueOperand op2(this, node->child2(), ManualOperandSpeculation);