Use JITSubGenerator to support UntypedUse operands for op_sub in the DFG.
https://bugs.webkit.org/show_bug.cgi?id=150038

Reviewed by Geoffrey Garen.

* bytecode/SpeculatedType.h:
(JSC::isUntypedSpeculationForArithmetic): Added
- Also fixed some comments.
        
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):

* dfg/DFGAbstractValue.cpp:
(JSC::DFG::AbstractValue::resultType):
* dfg/DFGAbstractValue.h:
- Added function to compute the ResultType of an operand from its SpeculatedType.

* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
- Fix up ArithSub to speculate its operands to be numbers.  But if an OSR exit
  due to a BadType was seen at this node, we'll fix it up to expect UntypedUse
  operands.  This gives the generated code a change to run fast if it only
  receives numeric operands.

* dfg/DFGNode.h:
(JSC::DFG::Node::shouldSpeculateUntypedForArithmetic):

* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
- Add the C++ runtime function to implement op_sub when we really encounter the
  hard types in the operands.

* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileArithSub):
- Added support for UntypedUse operands using the JITSubGenerator.

* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::silentSpillAllRegisters):
(JSC::DFG::SpeculativeJIT::pickCanTrample):
(JSC::DFG::SpeculativeJIT::callOperation):

* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
- Just refuse to FTL compile functions with UntypedUse op_sub operands for now.

* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::boxDouble):
(JSC::AssemblyHelpers::unboxDoubleNonDestructive):
(JSC::AssemblyHelpers::unboxDouble):
(JSC::AssemblyHelpers::boxBooleanPayload):
* jit/JITArithmetic.cpp:
(JSC::JIT::emit_op_sub):

* jit/JITSubGenerator.h:
(JSC::JITSubGenerator::generateFastPath):
(JSC::JITSubGenerator::endJumpList):
- Added some asserts to document the contract that this generator expects in
  terms of its incoming registers.

  Also fixed the generated code to not be destructive with regards to incoming
  registers.  The DFG expects this.

  Also added an endJumpList so that we don't have to jump twice for the fast
  path where both operands are ints.

* parser/ResultType.h:
(JSC::ResultType::ResultType):
- Make the internal Type bits and the constructor private.  Clients should only
  create ResultType values using one of the provided factory methods.

* tests/stress/op_sub.js: Added.
(o1.valueOf):
(stringify):
(generateScenarios):
(printScenarios):
(testCases.func):
(func):
(initializeTestCases):
(runTest):
- test op_sub results by comparing one LLINT result against the output of
  multiple LLINT, and JIT runs.  This test assume that we'll at least get the
  right result some of the time (if not all the time), and confirms that the
  various engines produce consistent results for all the various value pairs
  being tested.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@191224 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index f111605..92ad9fd 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,90 @@
+2015-10-16  Mark Lam  <mark.lam@apple.com>
+
+        Use JITSubGenerator to support UntypedUse operands for op_sub in the DFG.
+        https://bugs.webkit.org/show_bug.cgi?id=150038
+
+        Reviewed by Geoffrey Garen.
+
+        * bytecode/SpeculatedType.h:
+        (JSC::isUntypedSpeculationForArithmetic): Added
+        - Also fixed some comments.
+        
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+
+        * dfg/DFGAbstractValue.cpp:
+        (JSC::DFG::AbstractValue::resultType):
+        * dfg/DFGAbstractValue.h:
+        - Added function to compute the ResultType of an operand from its SpeculatedType.
+
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        - Fix up ArithSub to speculate its operands to be numbers.  But if an OSR exit
+          due to a BadType was seen at this node, we'll fix it up to expect UntypedUse
+          operands.  This gives the generated code a change to run fast if it only
+          receives numeric operands.
+
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::shouldSpeculateUntypedForArithmetic):
+
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        - Add the C++ runtime function to implement op_sub when we really encounter the
+          hard types in the operands.
+
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileArithSub):
+        - Added support for UntypedUse operands using the JITSubGenerator.
+
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::silentSpillAllRegisters):
+        (JSC::DFG::SpeculativeJIT::pickCanTrample):
+        (JSC::DFG::SpeculativeJIT::callOperation):
+
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        - Just refuse to FTL compile functions with UntypedUse op_sub operands for now.
+
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::boxDouble):
+        (JSC::AssemblyHelpers::unboxDoubleNonDestructive):
+        (JSC::AssemblyHelpers::unboxDouble):
+        (JSC::AssemblyHelpers::boxBooleanPayload):
+        * jit/JITArithmetic.cpp:
+        (JSC::JIT::emit_op_sub):
+
+        * jit/JITSubGenerator.h:
+        (JSC::JITSubGenerator::generateFastPath):
+        (JSC::JITSubGenerator::endJumpList):
+        - Added some asserts to document the contract that this generator expects in
+          terms of its incoming registers.
+
+          Also fixed the generated code to not be destructive with regards to incoming
+          registers.  The DFG expects this.
+
+          Also added an endJumpList so that we don't have to jump twice for the fast
+          path where both operands are ints.
+
+        * parser/ResultType.h:
+        (JSC::ResultType::ResultType):
+        - Make the internal Type bits and the constructor private.  Clients should only
+          create ResultType values using one of the provided factory methods.
+
+        * tests/stress/op_sub.js: Added.
+        (o1.valueOf):
+        (stringify):
+        (generateScenarios):
+        (printScenarios):
+        (testCases.func):
+        (func):
+        (initializeTestCases):
+        (runTest):
+        - test op_sub results by comparing one LLINT result against the output of
+          multiple LLINT, and JIT runs.  This test assume that we'll at least get the
+          right result some of the time (if not all the time), and confirms that the
+          various engines produce consistent results for all the various value pairs
+          being tested.
+
 2015-10-15  Filip Pizlo  <fpizlo@apple.com>
 
         CopyBarrier must be avoided for slow TypedArrays
diff --git a/Source/JavaScriptCore/bytecode/SpeculatedType.h b/Source/JavaScriptCore/bytecode/SpeculatedType.h
index e695d79..dac8bcc 100644
--- a/Source/JavaScriptCore/bytecode/SpeculatedType.h
+++ b/Source/JavaScriptCore/bytecode/SpeculatedType.h
@@ -84,9 +84,9 @@
 static const SpeculatedType SpecBoolean            = 0x10000000; // It's definitely a Boolean.
 static const SpeculatedType SpecOther              = 0x20000000; // It's definitely either Null or Undefined.
 static const SpeculatedType SpecMisc               = 0x30000000; // It's definitely either a boolean, Null, or Undefined.
-static const SpeculatedType SpecHeapTop            = 0x3bbfffff; // It can be any of the above, except for SpecInt52.
+static const SpeculatedType SpecHeapTop            = 0x3bbfffff; // It can be any of the above, except for SpecInt52 and SpecDoubleImpureNaN.
 static const SpeculatedType SpecEmpty              = 0x40000000; // It's definitely an empty value marker.
-static const SpeculatedType SpecBytecodeTop        = 0x7bbfffff; // It can be any of the above, except for SpecInt52. This is (SpecHeapTop | SpecEmpty).
+static const SpeculatedType SpecBytecodeTop        = 0x7bbfffff; // It can be any of the above, except for SpecInt52 and SpecDoubleImpureNaN. This is (SpecHeapTop | SpecEmpty).
 static const SpeculatedType SpecFullTop            = 0x7fffffff; // It can be any of the above plus anything the DFG chooses.
 
 typedef bool (*SpeculatedTypeChecker)(SpeculatedType);
@@ -384,6 +384,11 @@
     return value == SpecEmpty;
 }
 
+inline bool isUntypedSpeculationForArithmetic(SpeculatedType value)
+{
+    return !(value & (SpecFullNumber | SpecBoolean));
+}
+
 void dumpSpeculation(PrintStream&, SpeculatedType);
 void dumpSpeculationAbbreviated(PrintStream&, SpeculatedType);
 
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index d7539ee..f28cc7a 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -527,6 +527,9 @@
                 typeOfDoubleDifference(
                     forNode(node->child1()).m_type, forNode(node->child2()).m_type));
             break;
+        case UntypedUse:
+            forNode(node).setType(m_graph, SpecHeapTop);
+            break;
         default:
             RELEASE_ASSERT_NOT_REACHED();
             break;
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp b/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
index fd2652c..7181c6a 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
@@ -497,6 +497,22 @@
 }
 #endif
 
+ResultType AbstractValue::resultType() const
+{
+    ASSERT(isType(SpecHeapTop));
+    if (isType(SpecBoolean))
+        return ResultType::booleanType();
+    if (isType(SpecInt32))
+        return ResultType::numberTypeIsInt32();
+    if (isType(SpecBytecodeNumber))
+        return ResultType::numberType();
+    if (isType(SpecString))
+        return ResultType::stringType();
+    if (isType(SpecString | SpecBytecodeNumber))
+        return ResultType::stringOrNumberType();
+    return ResultType::unknownType();
+}
+
 void AbstractValue::dump(PrintStream& out) const
 {
     dumpInContext(out, 0);
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
index c3d3223..4808428 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
@@ -36,6 +36,7 @@
 #include "DFGStructureClobberState.h"
 #include "InferredType.h"
 #include "JSCell.h"
+#include "ResultType.h"
 #include "SpeculatedType.h"
 #include "DumpContext.h"
 #include "StructureSet.h"
@@ -344,7 +345,9 @@
     void checkConsistency() const;
     void assertIsRegistered(Graph&) const;
 #endif
-    
+
+    ResultType resultType() const;
+
     void dumpInContext(PrintStream&, DumpContext*) const;
     void dump(PrintStream&) const;
     
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 72cb7f9..eb6415b 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -183,6 +183,15 @@
             
         case ArithAdd:
         case ArithSub: {
+            if (op == ArithSub
+                && (Node::shouldSpeculateUntypedForArithmetic(node->child1().node(), node->child2().node())
+                    || m_graph.hasExitSite(node->origin.semantic, BadType))) {
+
+                fixEdge<UntypedUse>(node->child1());
+                fixEdge<UntypedUse>(node->child2());
+                node->setResult(NodeResultJS);
+                break;
+            }
             if (attemptToMakeIntegerAdd(node))
                 break;
             fixDoubleOrBooleanEdge(node->child1());
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index f1a0ed1..ac1a757 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -1977,6 +1977,16 @@
         return isNotCellSpeculation(prediction());
     }
     
+    bool shouldSpeculateUntypedForArithmetic()
+    {
+        return isUntypedSpeculationForArithmetic(prediction());
+    }
+
+    static bool shouldSpeculateUntypedForArithmetic(Node* op1, Node* op2)
+    {
+        return op1->shouldSpeculateUntypedForArithmetic() || op2->shouldSpeculateUntypedForArithmetic();
+    }
+    
     static bool shouldSpeculateBoolean(Node* op1, Node* op2)
     {
         return op1->shouldSpeculateBoolean() && op2->shouldSpeculateBoolean();
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index f210728..716c1ea 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -200,6 +200,19 @@
     return JSValue::encode(jsAddSlowCase(exec, op1, op2));
 }
 
+EncodedJSValue JIT_OPERATION operationValueSub(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+    VM* vm = &exec->vm();
+    NativeCallFrameTracer tracer(vm, exec);
+    
+    JSValue op1 = JSValue::decode(encodedOp1);
+    JSValue op2 = JSValue::decode(encodedOp2);
+
+    double a = op1.toNumber(exec);
+    double b = op2.toNumber(exec);
+    return JSValue::encode(jsNumber(a - b));
+}
+
 static ALWAYS_INLINE EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint32_t index)
 {
     VM& vm = exec->vm();
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h
index d1bc778..bbafc0b 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.h
+++ b/Source/JavaScriptCore/dfg/DFGOperations.h
@@ -45,6 +45,7 @@
 EncodedJSValue JIT_OPERATION operationToThisStrict(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueAdd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationValueSub(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByVal(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState*, JSCell*, EncodedJSValue encodedProperty) WTF_INTERNAL;
 EncodedJSValue JIT_OPERATION operationGetByValArrayInt(ExecState*, JSArray*, int32_t) WTF_INTERNAL;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index e3ccb7f..231ab97 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 "JITSubGenerator.h"
 #include "JSArrowFunction.h"
 #include "JSCInlines.h"
 #include "JSEnvironmentRecord.h"
@@ -3079,7 +3080,52 @@
         doubleResult(result.fpr(), node);
         return;
     }
-        
+
+    case UntypedUse: {
+        JSValueOperand left(this, node->child1());
+        JSValueOperand right(this, node->child2());
+
+        JSValueRegs leftRegs = left.jsValueRegs();
+        JSValueRegs rightRegs = right.jsValueRegs();
+
+        ResultType leftType = m_state.forNode(node->child1()).resultType();
+        ResultType rightType = m_state.forNode(node->child2()).resultType();
+
+        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
+
+        JITSubGenerator gen(resultRegs, leftRegs, rightRegs, leftType, rightType,
+            leftFPR, rightFPR, scratchGPR, scratchFPR);
+        gen.generateFastPath(m_jit);
+
+        gen.slowPathJumpList().link(&m_jit);
+        silentSpillAllRegisters(resultRegs);
+        callOperation(operationValueSub, resultRegs, leftRegs, rightRegs);
+        silentFillAllRegisters(resultRegs);
+        m_jit.exceptionCheck();
+
+        gen.endJumpList().link(&m_jit);
+        jsValueResult(resultRegs, node);
+        return;
+    }
+
     default:
         RELEASE_ASSERT_NOT_REACHED();
         return;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index ce3c596..4588b5c 100755
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -386,6 +386,14 @@
     {
         silentSpillAllRegisters(InvalidGPRReg, InvalidGPRReg, exclude);
     }
+    void silentSpillAllRegisters(JSValueRegs exclude)
+    {
+#if USE(JSVALUE64)
+        silentSpillAllRegisters(exclude.payloadGPR());
+#else
+        silentSpillAllRegisters(exclude.payloadGPR(), exclude.tagGPR());
+#endif
+    }
     
     static GPRReg pickCanTrample(GPRReg exclude)
     {
@@ -403,7 +411,12 @@
         return GPRInfo::regT0;
     }
 
-#if USE(JSVALUE32_64)
+#if USE(JSVALUE64)
+    static GPRReg pickCanTrample(JSValueRegs exclude)
+    {
+        return pickCanTrample(exclude.payloadGPR());
+    }
+#else
     static GPRReg pickCanTrample(JSValueRegs exclude)
     {
         GPRReg result = GPRInfo::regT0;
@@ -1439,6 +1452,10 @@
         m_jit.setupArgumentsWithExecState(MacroAssembler::TrustedImm64(JSValue::encode(jsNumber(imm.m_value))), arg2);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(J_JITOperation_EJJ operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2)
+    {
+        return callOperation(operation, result.payloadGPR(), arg1.payloadGPR(), arg2.payloadGPR());
+    }
     JITCompiler::Call callOperation(J_JITOperation_ECC operation, GPRReg result, GPRReg arg1, GPRReg arg2)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -1747,6 +1764,10 @@
         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG imm, TrustedImm32(JSValue::Int32Tag), SH4_32BIT_DUMMY_ARG arg2Payload, arg2Tag);
         return appendCallSetResult(operation, resultPayload, resultTag);
     }
+    JITCompiler::Call callOperation(J_JITOperation_EJJ operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2)
+    {
+        return callOperation(operation, result.tagGPR(), result.payloadGPR(), arg1.tagGPR(), arg1.payloadGPR(), arg2.tagGPR(), arg2.payloadGPR());
+    }
 
     JITCompiler::Call callOperation(J_JITOperation_ECJ operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1, GPRReg arg2Tag, GPRReg arg2Payload)
     {
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index 5d88bb1..537f8f6 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -84,7 +84,6 @@
     case StrCat:
     case ArithAdd:
     case ArithClz32:
-    case ArithSub:
     case ArithMul:
     case ArithDiv:
     case ArithMod:
@@ -206,6 +205,11 @@
     case TypeOf:
         // These are OK.
         break;
+    case ArithSub:
+        if (node->result() == NodeResultJS)
+            return CannotCompile;
+        break;
+
     case Identity:
         // No backend handles this because it will be optimized out. But we may check
         // for capabilities before optimization. It would be a deep error to remove this
diff --git a/Source/JavaScriptCore/jit/AssemblyHelpers.h b/Source/JavaScriptCore/jit/AssemblyHelpers.h
index c20535b..4e9ab0b 100644
--- a/Source/JavaScriptCore/jit/AssemblyHelpers.h
+++ b/Source/JavaScriptCore/jit/AssemblyHelpers.h
@@ -987,9 +987,11 @@
     {
         boxDouble(fpr, regs.gpr());
     }
-    void unboxDouble(JSValueRegs regs, FPRReg destFPR, FPRReg)
+
+    void unboxDoubleNonDestructive(JSValueRegs regs, FPRReg destFPR, GPRReg scratchGPR, FPRReg)
     {
-        unboxDouble(regs.payloadGPR(), destFPR);
+        move(regs.payloadGPR(), scratchGPR);
+        unboxDouble(scratchGPR, destFPR);
     }
 
     // Here are possible arrangements of source, target, scratch:
@@ -1033,6 +1035,11 @@
     {
         unboxDouble(regs.tagGPR(), regs.payloadGPR(), fpr, scratchFPR);
     }
+
+    void unboxDoubleNonDestructive(const JSValueRegs regs, FPRReg destFPR, GPRReg, FPRReg scratchFPR)
+    {
+        unboxDouble(regs, destFPR, scratchFPR);
+    }
 #endif
     
     void boxBooleanPayload(GPRReg boolGPR, GPRReg payloadGPR)
diff --git a/Source/JavaScriptCore/jit/JITArithmetic.cpp b/Source/JavaScriptCore/jit/JITArithmetic.cpp
index d97965b..b639b11 100644
--- a/Source/JavaScriptCore/jit/JITArithmetic.cpp
+++ b/Source/JavaScriptCore/jit/JITArithmetic.cpp
@@ -970,7 +970,7 @@
     JSValueRegs leftRegs = JSValueRegs(regT0);
     JSValueRegs rightRegs = JSValueRegs(regT1);
     JSValueRegs resultRegs = leftRegs;
-    GPRReg scratchGPR = InvalidGPRReg;
+    GPRReg scratchGPR = regT2;
     FPRReg scratchFPR = InvalidFPRReg;
 #else
     JSValueRegs leftRegs = JSValueRegs(regT1, regT0);
@@ -987,6 +987,7 @@
         fpRegT0, fpRegT1, scratchGPR, scratchFPR);
 
     gen.generateFastPath(*this);
+    gen.endJumpList().link(this);
     emitPutVirtualRegister(result, resultRegs);
 
     addSlowCase(gen.slowPathJumpList());
diff --git a/Source/JavaScriptCore/jit/JITSubGenerator.h b/Source/JavaScriptCore/jit/JITSubGenerator.h
index c3a4d3f..524337d 100644
--- a/Source/JavaScriptCore/jit/JITSubGenerator.h
+++ b/Source/JavaScriptCore/jit/JITSubGenerator.h
@@ -27,6 +27,7 @@
 #define JITSubGenerator_h
 
 #include "CCallHelpers.h"
+#include "ResultType.h"
 
 namespace JSC {
     
@@ -49,15 +50,24 @@
 
     void generateFastPath(CCallHelpers& jit)
     {
-        CCallHelpers::JumpList slowPath;
-
+        ASSERT(m_scratchGPR != InvalidGPRReg);
+        ASSERT(m_scratchGPR != m_left.payloadGPR());
+        ASSERT(m_scratchGPR != m_right.payloadGPR());
+#if ENABLE(JSVALUE32_64)
+        ASSERT(m_scratchGPR != m_left.tagGPR());x
+        ASSERT(m_scratchGPR != m_right.tagGPR());
+        ASSERT(m_scratchFPR != InvalidFPRReg);
+#endif
         CCallHelpers::Jump leftNotInt = jit.branchIfNotInt32(m_left);
         CCallHelpers::Jump rightNotInt = jit.branchIfNotInt32(m_right);
 
+        jit.move(m_left.payloadGPR(), m_result.payloadGPR());
         m_slowPathJumpList.append(
-            jit.branchSub32(CCallHelpers::Overflow, m_right.payloadGPR(), m_left.payloadGPR()));
+            jit.branchSub32(CCallHelpers::Overflow, m_right.payloadGPR(), m_result.payloadGPR()));
 
-        jit.boxInt32(m_left.payloadGPR(), m_result);
+        jit.boxInt32(m_result.payloadGPR(), m_result);
+
+        m_endJumpList.append(jit.jump());
 
         if (!jit.supportsFloatingPoint()) {
             m_slowPathJumpList.append(leftNotInt);
@@ -65,15 +75,13 @@
             return;
         }
         
-        CCallHelpers::Jump end = jit.jump();
-
         leftNotInt.link(&jit);
         if (!m_leftType.definitelyIsNumber())
             m_slowPathJumpList.append(jit.branchIfNotNumber(m_left, m_scratchGPR));
         if (!m_rightType.definitelyIsNumber())
             m_slowPathJumpList.append(jit.branchIfNotNumber(m_right, m_scratchGPR));
 
-        jit.unboxDouble(m_left, m_leftFPR, m_scratchFPR);
+        jit.unboxDoubleNonDestructive(m_left, m_leftFPR, m_scratchGPR, m_scratchFPR);
         CCallHelpers::Jump rightIsDouble = jit.branchIfNotInt32(m_right);
 
         jit.convertInt32ToDouble(m_right.payloadGPR(), m_rightFPR);
@@ -86,16 +94,17 @@
         jit.convertInt32ToDouble(m_left.payloadGPR(), m_leftFPR);
 
         rightIsDouble.link(&jit);
-        jit.unboxDouble(m_right, m_rightFPR, m_scratchFPR);
+        jit.unboxDoubleNonDestructive(m_right, m_rightFPR, m_scratchGPR, m_scratchFPR);
 
         rightWasInteger.link(&jit);
 
         jit.subDouble(m_rightFPR, m_leftFPR);
         jit.boxDouble(m_leftFPR, m_result);
 
-        end.link(&jit);
+        m_endJumpList.append(jit.jump());
     }
 
+    CCallHelpers::JumpList endJumpList() { return m_endJumpList; }
     CCallHelpers::JumpList slowPathJumpList() { return m_slowPathJumpList; }
 
 private:
@@ -109,6 +118,7 @@
     GPRReg m_scratchGPR;
     FPRReg m_scratchFPR;
 
+    CCallHelpers::JumpList m_endJumpList;
     CCallHelpers::JumpList m_slowPathJumpList;
 };
 
diff --git a/Source/JavaScriptCore/parser/ResultType.h b/Source/JavaScriptCore/parser/ResultType.h
index ad86c98..a55627d 100644
--- a/Source/JavaScriptCore/parser/ResultType.h
+++ b/Source/JavaScriptCore/parser/ResultType.h
@@ -29,6 +29,7 @@
 namespace JSC {
 
     struct ResultType {
+    private:
         friend struct OperandTypes;
 
         typedef char Type;
@@ -46,7 +47,8 @@
             : m_type(type)
         {
         }
-        
+
+    public:
         bool isInt32()
         {
             return m_type & TypeInt32;
diff --git a/Source/JavaScriptCore/tests/stress/op_sub.js b/Source/JavaScriptCore/tests/stress/op_sub.js
new file mode 100644
index 0000000..eb8a9ea
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/op_sub.js
@@ -0,0 +1,293 @@
+//@ runFTLNoCJIT
+
+// This test module aims to test the subtraction operator by comparing its runtime
+// behavior (using the different tiers) with expected values computed at initialization
+// time using the LLINT / bytecode generator.
+//
+// It works by generating test scenarios from permutations of value pairs to exercise
+// the subtraction operator. It computes the expected results by evaluating an expression
+// to subtract the values in an initialization pass. The scenarios are later applied to
+// a set of test functions of the forms:
+//
+//     variable - variable
+//     constant - variable
+//     variable - constant
+//
+// See generateScenarios() and initializeTestCases() for details on how the test
+// cases are generated.
+//
+// If all goes well, this test module will terminate silently. If not, it will print
+// errors.
+
+var o1 = {
+    valueOf: function() { return 10; }
+};
+
+var set1 = [
+    o1,
+    null,
+    undefined,
+    NaN,
+    "abc",
+];
+
+var set2 = [
+    10, -10,
+    2147483647, -2147483647,
+    4294967296, -4294967296,
+    100.2, -100.2,
+    true, false
+];
+
+// Assemble the values that we'll be testing with:
+var values = [];
+for (var i = 0; i < set1.length; i++)
+    values.push(set1[i]);
+for (var i = 0; i < set2.length; i++)
+    values.push(set2[i]);
+for (var i = 0; i < set2.length; i++)
+    values.push("" + set2[i]);
+
+function stringify(value) {
+    if (typeof value == "string")
+        return '"' + value + '"';
+    return "" + value;
+}
+
+function generateScenarios(xvalues, yvalues) {
+    var scenarios = [];
+    for (var i = 0; i < xvalues.length; i++) {
+        for (var j = 0; j < yvalues.length; j++) {
+            var name = "(" + xvalues[i] + " - " + yvalues[j] + ")";
+            var x = xvalues[i];
+            var y = yvalues[j];
+            var expected = eval(stringify(x) + " - " + stringify(y));
+            var scenario = { name: name, x: x, y: y, expected: expected };
+
+            scenarios.push(scenario);
+        }
+    }
+    return scenarios;
+}
+
+function printScenarios(scenarios) {
+    for (var i = 0; i < scenarios.length; i++) {
+        var scenario = scenarios[i];
+        print("scenario[" + i + "]: { name: " + scenario.name + ", x: " + scenario.x, ", y: " + scenario.y + ", expected: " + scenario.expected + " }");
+    }
+}
+
+var testCases = [
+    {
+        name: "sub",
+        func: function(x, y) { return x - y; },
+        xvalues: values,
+        yvalues: values
+    },
+    {
+        name: "subI32V",
+        func: function(x, y) { return 10 - y; },
+        xvalues: [ 10 ],
+        yvalues: values
+    },
+    {
+        name: "subVI32",
+        func: function(x, y) { return x - 10; },
+        xvalues: values,
+        yvalues: [ 10 ]
+    },
+    {
+        name: "subI32oV",
+        func: function(x, y) { return -2147483647 - y; },
+        xvalues: [ -2147483647 ],
+        yvalues: values
+    },
+    {
+        name: "subVI32o",
+        func: function(x, y) { return x - 2147483647; },
+        xvalues: values,
+        yvalues: [ 2147483647 ]
+    },
+    {
+        name: "subI52V",
+        func: function(x, y) { return 4294967296 - y; },
+        xvalues: [ 4294967296 ],
+        yvalues: values
+    },
+    {
+        name: "subVI52",
+        func: function(x, y) { return x - 4294967296; },
+        xvalues: values,
+        yvalues: [ 4294967296 ]
+    },
+    {
+        name: "subDV",
+        func: function(x, y) { return 100.2 - y; },
+        xvalues: [ 100.2 ],
+        yvalues: values
+    },
+    {
+        name: "subVD",
+        func: function(x, y) { return x - 100.2; },
+        xvalues: values,
+        yvalues: [ 100.2 ]
+    },
+    {
+        name: "subBV",
+        func: function(x, y) { return true - y; },
+        xvalues: [ true ],
+        yvalues: values
+    },
+    {
+        name: "subVB",
+        func: function(x, y) { return x - true; },
+        xvalues: values,
+        yvalues: [ true ]
+    },
+    {
+        name: "subSi32V",
+        func: function(x, y) { return "10" - y; },
+        xvalues: [ "10" ],
+        yvalues: values
+    },
+    {
+        name: "subVSi32",
+        func: function(x, y) { return x - "10"; },
+        xvalues: values,
+        yvalues: [ "10" ]
+    },
+
+    {
+        name: "subSi32oV",
+        func: function(x, y) { return "-2147483647" - y; },
+        xvalues: [ "-2147483647" ],
+        yvalues: values
+    },
+    {
+        name: "subVSi32o",
+        func: function(x, y) { return x - "2147483647"; },
+        xvalues: values,
+        yvalues: [ "2147483647" ]
+    },
+    {
+        name: "subSi52V",
+        func: function(x, y) { return "4294967296" - y; },
+        xvalues: [ "4294967296" ],
+        yvalues: values
+    },
+    {
+        name: "subVSi52",
+        func: function(x, y) { return x - "4294967296"; },
+        xvalues: values,
+        yvalues: [ "4294967296" ]
+    },
+    {
+        name: "subSdV",
+        func: function(x, y) { return "100.2" - y; },
+        xvalues: [ "100.2" ],
+        yvalues: values
+    },
+    {
+        name: "subVSd",
+        func: function(x, y) { return x - "100.2"; },
+        xvalues: values,
+        yvalues: [ "100.2" ]
+    },
+    {
+        name: "subSbV",
+        func: function(x, y) { return "true" - y; },
+        xvalues: [ "true" ],
+        yvalues: values
+    },
+    {
+        name: "subVSb",
+        func: function(x, y) { return x - "true"; },
+        xvalues: values,
+        yvalues: [ "true" ]
+    },
+
+    {
+        name: "subSV",
+        func: function(x, y) { return "abc" - y; },
+        xvalues: [ "abc" ],
+        yvalues: values
+    },
+    {
+        name: "subVS",
+        func: function(x, y) { return x - "abc"; },
+        xvalues: values,
+        yvalues: [ "abc" ]
+    },
+    {
+        name: "subNV",
+        func: function(x, y) { return null - y; },
+        xvalues: [ null ],
+        yvalues: values
+    },
+    {
+        name: "subVN",
+        func: function(x, y) { return x - null; },
+        xvalues: values,
+        yvalues: [ null ]
+    },
+    {
+        name: "subOV",
+        func: function(x, y) { return o1 - y; },
+        xvalues: [ o1 ],
+        yvalues: values
+    },
+    {
+        name: "subVO",
+        func: function(x, y) { return x - o1; },
+        xvalues: values,
+        yvalues: [ o1 ]
+    },
+    {
+        name: "subNaNV",
+        func: function(x, y) { return NaN - y; },
+        xvalues: [ NaN ],
+        yvalues: values
+    },
+    {
+        name: "subVNaN",
+        func: function(x, y) { return x - NaN; },
+        xvalues: values,
+        yvalues: [ NaN ]
+    },
+];
+
+function initializeTestCases() {
+    for (var test of testCases) {
+        noInline(test.func);
+        test.scenarios = generateScenarios(test.xvalues, test.yvalues);
+    }
+}
+initializeTestCases();
+
+function runTest(test) {
+    var failedScenario = [];
+    var scenarios = test.scenarios;
+    var testFunc = test.func;
+    try {
+        for (var i = 0; i < 10000; i++) {
+            for (var scenarioID = 0; scenarioID < scenarios.length; scenarioID++) {
+                var scenario = scenarios[scenarioID];
+                var result = testFunc(scenario.x, scenario.y);
+                if (result == scenario.expected)
+                    continue;
+                if (Number.isNaN(result) && Number.isNaN(scenario.expected))
+                    continue;
+                if (!failedScenario[scenarioID]) {
+                    print("FAIL: " + test.name + ":" + scenario.name + " started failing on iteration " + i + ": expected " + scenario.expected + ", actual " + result);
+                    failedScenario[scenarioID] = scenario;
+                }
+            }
+        }
+    } catch(e) {
+        print("Unexpected exception: " + e);
+    }
+}
+
+for (var test of testCases)
+    runTest(test);
+