<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[191224] trunk/Source/JavaScriptCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/191224">191224</a></dd>
<dt>Author</dt> <dd>mark.lam@apple.com</dd>
<dt>Date</dt> <dd>2015-10-16 16:26:14 -0700 (Fri, 16 Oct 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>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&lt;AbstractStateType&gt;::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.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecodeSpeculatedTypeh">trunk/Source/JavaScriptCore/bytecode/SpeculatedType.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh">trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGAbstractValuecpp">trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGAbstractValueh">trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGFixupPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGNodeh">trunk/Source/JavaScriptCore/dfg/DFGNode.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOperationscpp">trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGOperationsh">trunk/Source/JavaScriptCore/dfg/DFGOperations.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJITh">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLCapabilitiescpp">trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitAssemblyHelpersh">trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h</a></li>
<li><a href="#trunkSourceJavaScriptCorejitJITArithmeticcpp">trunk/Source/JavaScriptCore/jit/JITArithmetic.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorejitJITSubGeneratorh">trunk/Source/JavaScriptCore/jit/JITSubGenerator.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreparserResultTypeh">trunk/Source/JavaScriptCore/parser/ResultType.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressop_subjs">trunk/Source/JavaScriptCore/tests/stress/op_sub.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -1,3 +1,90 @@
</span><ins>+2015-10-16  Mark Lam  &lt;mark.lam@apple.com&gt;
+
+        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&lt;AbstractStateType&gt;::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.
+
</ins><span class="cx"> 2015-10-15  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         CopyBarrier must be avoided for slow TypedArrays
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecodeSpeculatedTypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecode/SpeculatedType.h (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecode/SpeculatedType.h        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/bytecode/SpeculatedType.h        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -84,9 +84,9 @@
</span><span class="cx"> static const SpeculatedType SpecBoolean            = 0x10000000; // It's definitely a Boolean.
</span><span class="cx"> static const SpeculatedType SpecOther              = 0x20000000; // It's definitely either Null or Undefined.
</span><span class="cx"> static const SpeculatedType SpecMisc               = 0x30000000; // It's definitely either a boolean, Null, or Undefined.
</span><del>-static const SpeculatedType SpecHeapTop            = 0x3bbfffff; // It can be any of the above, except for SpecInt52.
</del><ins>+static const SpeculatedType SpecHeapTop            = 0x3bbfffff; // It can be any of the above, except for SpecInt52 and SpecDoubleImpureNaN.
</ins><span class="cx"> static const SpeculatedType SpecEmpty              = 0x40000000; // It's definitely an empty value marker.
</span><del>-static const SpeculatedType SpecBytecodeTop        = 0x7bbfffff; // It can be any of the above, except for SpecInt52. This is (SpecHeapTop | SpecEmpty).
</del><ins>+static const SpeculatedType SpecBytecodeTop        = 0x7bbfffff; // It can be any of the above, except for SpecInt52 and SpecDoubleImpureNaN. This is (SpecHeapTop | SpecEmpty).
</ins><span class="cx"> static const SpeculatedType SpecFullTop            = 0x7fffffff; // It can be any of the above plus anything the DFG chooses.
</span><span class="cx"> 
</span><span class="cx"> typedef bool (*SpeculatedTypeChecker)(SpeculatedType);
</span><span class="lines">@@ -384,6 +384,11 @@
</span><span class="cx">     return value == SpecEmpty;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+inline bool isUntypedSpeculationForArithmetic(SpeculatedType value)
+{
+    return !(value &amp; (SpecFullNumber | SpecBoolean));
+}
+
</ins><span class="cx"> void dumpSpeculation(PrintStream&amp;, SpeculatedType);
</span><span class="cx"> void dumpSpeculationAbbreviated(PrintStream&amp;, SpeculatedType);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGAbstractInterpreterInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -527,6 +527,9 @@
</span><span class="cx">                 typeOfDoubleDifference(
</span><span class="cx">                     forNode(node-&gt;child1()).m_type, forNode(node-&gt;child2()).m_type));
</span><span class="cx">             break;
</span><ins>+        case UntypedUse:
+            forNode(node).setType(m_graph, SpecHeapTop);
+            break;
</ins><span class="cx">         default:
</span><span class="cx">             RELEASE_ASSERT_NOT_REACHED();
</span><span class="cx">             break;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGAbstractValuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -497,6 +497,22 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+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();
+}
+
</ins><span class="cx"> void AbstractValue::dump(PrintStream&amp; out) const
</span><span class="cx"> {
</span><span class="cx">     dumpInContext(out, 0);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGAbstractValueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.h (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.h        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.h        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx"> #include &quot;DFGStructureClobberState.h&quot;
</span><span class="cx"> #include &quot;InferredType.h&quot;
</span><span class="cx"> #include &quot;JSCell.h&quot;
</span><ins>+#include &quot;ResultType.h&quot;
</ins><span class="cx"> #include &quot;SpeculatedType.h&quot;
</span><span class="cx"> #include &quot;DumpContext.h&quot;
</span><span class="cx"> #include &quot;StructureSet.h&quot;
</span><span class="lines">@@ -344,7 +345,9 @@
</span><span class="cx">     void checkConsistency() const;
</span><span class="cx">     void assertIsRegistered(Graph&amp;) const;
</span><span class="cx"> #endif
</span><del>-    
</del><ins>+
+    ResultType resultType() const;
+
</ins><span class="cx">     void dumpInContext(PrintStream&amp;, DumpContext*) const;
</span><span class="cx">     void dump(PrintStream&amp;) const;
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGFixupPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -183,6 +183,15 @@
</span><span class="cx">             
</span><span class="cx">         case ArithAdd:
</span><span class="cx">         case ArithSub: {
</span><ins>+            if (op == ArithSub
+                &amp;&amp; (Node::shouldSpeculateUntypedForArithmetic(node-&gt;child1().node(), node-&gt;child2().node())
+                    || m_graph.hasExitSite(node-&gt;origin.semantic, BadType))) {
+
+                fixEdge&lt;UntypedUse&gt;(node-&gt;child1());
+                fixEdge&lt;UntypedUse&gt;(node-&gt;child2());
+                node-&gt;setResult(NodeResultJS);
+                break;
+            }
</ins><span class="cx">             if (attemptToMakeIntegerAdd(node))
</span><span class="cx">                 break;
</span><span class="cx">             fixDoubleOrBooleanEdge(node-&gt;child1());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGNodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGNode.h (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGNode.h        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/dfg/DFGNode.h        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -1977,6 +1977,16 @@
</span><span class="cx">         return isNotCellSpeculation(prediction());
</span><span class="cx">     }
</span><span class="cx">     
</span><ins>+    bool shouldSpeculateUntypedForArithmetic()
+    {
+        return isUntypedSpeculationForArithmetic(prediction());
+    }
+
+    static bool shouldSpeculateUntypedForArithmetic(Node* op1, Node* op2)
+    {
+        return op1-&gt;shouldSpeculateUntypedForArithmetic() || op2-&gt;shouldSpeculateUntypedForArithmetic();
+    }
+    
</ins><span class="cx">     static bool shouldSpeculateBoolean(Node* op1, Node* op2)
</span><span class="cx">     {
</span><span class="cx">         return op1-&gt;shouldSpeculateBoolean() &amp;&amp; op2-&gt;shouldSpeculateBoolean();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOperationscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/dfg/DFGOperations.cpp        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -200,6 +200,19 @@
</span><span class="cx">     return JSValue::encode(jsAddSlowCase(exec, op1, op2));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+EncodedJSValue JIT_OPERATION operationValueSub(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+    VM* vm = &amp;exec-&gt;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));
+}
+
</ins><span class="cx"> static ALWAYS_INLINE EncodedJSValue getByVal(ExecState* exec, JSCell* base, uint32_t index)
</span><span class="cx"> {
</span><span class="cx">     VM&amp; vm = exec-&gt;vm();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGOperationsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGOperations.h (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGOperations.h        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/dfg/DFGOperations.h        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -45,6 +45,7 @@
</span><span class="cx"> EncodedJSValue JIT_OPERATION operationToThisStrict(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
</span><span class="cx"> EncodedJSValue JIT_OPERATION operationValueAdd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
</span><span class="cx"> EncodedJSValue JIT_OPERATION operationValueAddNotNumber(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
</span><ins>+EncodedJSValue JIT_OPERATION operationValueSub(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
</ins><span class="cx"> EncodedJSValue JIT_OPERATION operationGetByVal(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedProperty) WTF_INTERNAL;
</span><span class="cx"> EncodedJSValue JIT_OPERATION operationGetByValCell(ExecState*, JSCell*, EncodedJSValue encodedProperty) WTF_INTERNAL;
</span><span class="cx"> EncodedJSValue JIT_OPERATION operationGetByValArrayInt(ExecState*, JSArray*, int32_t) WTF_INTERNAL;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -38,6 +38,7 @@
</span><span class="cx"> #include &quot;DFGSaneStringGetByValSlowPathGenerator.h&quot;
</span><span class="cx"> #include &quot;DFGSlowPathGenerator.h&quot;
</span><span class="cx"> #include &quot;DirectArguments.h&quot;
</span><ins>+#include &quot;JITSubGenerator.h&quot;
</ins><span class="cx"> #include &quot;JSArrowFunction.h&quot;
</span><span class="cx"> #include &quot;JSCInlines.h&quot;
</span><span class="cx"> #include &quot;JSEnvironmentRecord.h&quot;
</span><span class="lines">@@ -3079,7 +3080,52 @@
</span><span class="cx">         doubleResult(result.fpr(), node);
</span><span class="cx">         return;
</span><span class="cx">     }
</span><del>-        
</del><ins>+
+    case UntypedUse: {
+        JSValueOperand left(this, node-&gt;child1());
+        JSValueOperand right(this, node-&gt;child2());
+
+        JSValueRegs leftRegs = left.jsValueRegs();
+        JSValueRegs rightRegs = right.jsValueRegs();
+
+        ResultType leftType = m_state.forNode(node-&gt;child1()).resultType();
+        ResultType rightType = m_state.forNode(node-&gt;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(&amp;m_jit);
+        silentSpillAllRegisters(resultRegs);
+        callOperation(operationValueSub, resultRegs, leftRegs, rightRegs);
+        silentFillAllRegisters(resultRegs);
+        m_jit.exceptionCheck();
+
+        gen.endJumpList().link(&amp;m_jit);
+        jsValueResult(resultRegs, node);
+        return;
+    }
+
</ins><span class="cx">     default:
</span><span class="cx">         RELEASE_ASSERT_NOT_REACHED();
</span><span class="cx">         return;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJITh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -386,6 +386,14 @@
</span><span class="cx">     {
</span><span class="cx">         silentSpillAllRegisters(InvalidGPRReg, InvalidGPRReg, exclude);
</span><span class="cx">     }
</span><ins>+    void silentSpillAllRegisters(JSValueRegs exclude)
+    {
+#if USE(JSVALUE64)
+        silentSpillAllRegisters(exclude.payloadGPR());
+#else
+        silentSpillAllRegisters(exclude.payloadGPR(), exclude.tagGPR());
+#endif
+    }
</ins><span class="cx">     
</span><span class="cx">     static GPRReg pickCanTrample(GPRReg exclude)
</span><span class="cx">     {
</span><span class="lines">@@ -403,9 +411,14 @@
</span><span class="cx">         return GPRInfo::regT0;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-#if USE(JSVALUE32_64)
</del><ins>+#if USE(JSVALUE64)
</ins><span class="cx">     static GPRReg pickCanTrample(JSValueRegs exclude)
</span><span class="cx">     {
</span><ins>+        return pickCanTrample(exclude.payloadGPR());
+    }
+#else
+    static GPRReg pickCanTrample(JSValueRegs exclude)
+    {
</ins><span class="cx">         GPRReg result = GPRInfo::regT0;
</span><span class="cx">         if (result == exclude.tagGPR()) {
</span><span class="cx">             result = GPRInfo::regT1;
</span><span class="lines">@@ -1439,6 +1452,10 @@
</span><span class="cx">         m_jit.setupArgumentsWithExecState(MacroAssembler::TrustedImm64(JSValue::encode(jsNumber(imm.m_value))), arg2);
</span><span class="cx">         return appendCallSetResult(operation, result);
</span><span class="cx">     }
</span><ins>+    JITCompiler::Call callOperation(J_JITOperation_EJJ operation, JSValueRegs result, JSValueRegs arg1, JSValueRegs arg2)
+    {
+        return callOperation(operation, result.payloadGPR(), arg1.payloadGPR(), arg2.payloadGPR());
+    }
</ins><span class="cx">     JITCompiler::Call callOperation(J_JITOperation_ECC operation, GPRReg result, GPRReg arg1, GPRReg arg2)
</span><span class="cx">     {
</span><span class="cx">         m_jit.setupArgumentsWithExecState(arg1, arg2);
</span><span class="lines">@@ -1747,6 +1764,10 @@
</span><span class="cx">         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG imm, TrustedImm32(JSValue::Int32Tag), SH4_32BIT_DUMMY_ARG arg2Payload, arg2Tag);
</span><span class="cx">         return appendCallSetResult(operation, resultPayload, resultTag);
</span><span class="cx">     }
</span><ins>+    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());
+    }
</ins><span class="cx"> 
</span><span class="cx">     JITCompiler::Call callOperation(J_JITOperation_ECJ operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1, GPRReg arg2Tag, GPRReg arg2Payload)
</span><span class="cx">     {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLCapabilitiescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -84,7 +84,6 @@
</span><span class="cx">     case StrCat:
</span><span class="cx">     case ArithAdd:
</span><span class="cx">     case ArithClz32:
</span><del>-    case ArithSub:
</del><span class="cx">     case ArithMul:
</span><span class="cx">     case ArithDiv:
</span><span class="cx">     case ArithMod:
</span><span class="lines">@@ -206,6 +205,11 @@
</span><span class="cx">     case TypeOf:
</span><span class="cx">         // These are OK.
</span><span class="cx">         break;
</span><ins>+    case ArithSub:
+        if (node-&gt;result() == NodeResultJS)
+            return CannotCompile;
+        break;
+
</ins><span class="cx">     case Identity:
</span><span class="cx">         // No backend handles this because it will be optimized out. But we may check
</span><span class="cx">         // for capabilities before optimization. It would be a deep error to remove this
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitAssemblyHelpersh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -987,9 +987,11 @@
</span><span class="cx">     {
</span><span class="cx">         boxDouble(fpr, regs.gpr());
</span><span class="cx">     }
</span><del>-    void unboxDouble(JSValueRegs regs, FPRReg destFPR, FPRReg)
</del><ins>+
+    void unboxDoubleNonDestructive(JSValueRegs regs, FPRReg destFPR, GPRReg scratchGPR, FPRReg)
</ins><span class="cx">     {
</span><del>-        unboxDouble(regs.payloadGPR(), destFPR);
</del><ins>+        move(regs.payloadGPR(), scratchGPR);
+        unboxDouble(scratchGPR, destFPR);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Here are possible arrangements of source, target, scratch:
</span><span class="lines">@@ -1033,6 +1035,11 @@
</span><span class="cx">     {
</span><span class="cx">         unboxDouble(regs.tagGPR(), regs.payloadGPR(), fpr, scratchFPR);
</span><span class="cx">     }
</span><ins>+
+    void unboxDoubleNonDestructive(const JSValueRegs regs, FPRReg destFPR, GPRReg, FPRReg scratchFPR)
+    {
+        unboxDouble(regs, destFPR, scratchFPR);
+    }
</ins><span class="cx"> #endif
</span><span class="cx">     
</span><span class="cx">     void boxBooleanPayload(GPRReg boolGPR, GPRReg payloadGPR)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitJITArithmeticcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/JITArithmetic.cpp (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/JITArithmetic.cpp        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/jit/JITArithmetic.cpp        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -970,7 +970,7 @@
</span><span class="cx">     JSValueRegs leftRegs = JSValueRegs(regT0);
</span><span class="cx">     JSValueRegs rightRegs = JSValueRegs(regT1);
</span><span class="cx">     JSValueRegs resultRegs = leftRegs;
</span><del>-    GPRReg scratchGPR = InvalidGPRReg;
</del><ins>+    GPRReg scratchGPR = regT2;
</ins><span class="cx">     FPRReg scratchFPR = InvalidFPRReg;
</span><span class="cx"> #else
</span><span class="cx">     JSValueRegs leftRegs = JSValueRegs(regT1, regT0);
</span><span class="lines">@@ -987,6 +987,7 @@
</span><span class="cx">         fpRegT0, fpRegT1, scratchGPR, scratchFPR);
</span><span class="cx"> 
</span><span class="cx">     gen.generateFastPath(*this);
</span><ins>+    gen.endJumpList().link(this);
</ins><span class="cx">     emitPutVirtualRegister(result, resultRegs);
</span><span class="cx"> 
</span><span class="cx">     addSlowCase(gen.slowPathJumpList());
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejitJITSubGeneratorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jit/JITSubGenerator.h (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jit/JITSubGenerator.h        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/jit/JITSubGenerator.h        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> #define JITSubGenerator_h
</span><span class="cx"> 
</span><span class="cx"> #include &quot;CCallHelpers.h&quot;
</span><ins>+#include &quot;ResultType.h&quot;
</ins><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx">     
</span><span class="lines">@@ -49,31 +50,38 @@
</span><span class="cx"> 
</span><span class="cx">     void generateFastPath(CCallHelpers&amp; jit)
</span><span class="cx">     {
</span><del>-        CCallHelpers::JumpList slowPath;
-
</del><ins>+        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
</ins><span class="cx">         CCallHelpers::Jump leftNotInt = jit.branchIfNotInt32(m_left);
</span><span class="cx">         CCallHelpers::Jump rightNotInt = jit.branchIfNotInt32(m_right);
</span><span class="cx"> 
</span><ins>+        jit.move(m_left.payloadGPR(), m_result.payloadGPR());
</ins><span class="cx">         m_slowPathJumpList.append(
</span><del>-            jit.branchSub32(CCallHelpers::Overflow, m_right.payloadGPR(), m_left.payloadGPR()));
</del><ins>+            jit.branchSub32(CCallHelpers::Overflow, m_right.payloadGPR(), m_result.payloadGPR()));
</ins><span class="cx"> 
</span><del>-        jit.boxInt32(m_left.payloadGPR(), m_result);
</del><ins>+        jit.boxInt32(m_result.payloadGPR(), m_result);
</ins><span class="cx"> 
</span><ins>+        m_endJumpList.append(jit.jump());
+
</ins><span class="cx">         if (!jit.supportsFloatingPoint()) {
</span><span class="cx">             m_slowPathJumpList.append(leftNotInt);
</span><span class="cx">             m_slowPathJumpList.append(rightNotInt);
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx">         
</span><del>-        CCallHelpers::Jump end = jit.jump();
-
</del><span class="cx">         leftNotInt.link(&amp;jit);
</span><span class="cx">         if (!m_leftType.definitelyIsNumber())
</span><span class="cx">             m_slowPathJumpList.append(jit.branchIfNotNumber(m_left, m_scratchGPR));
</span><span class="cx">         if (!m_rightType.definitelyIsNumber())
</span><span class="cx">             m_slowPathJumpList.append(jit.branchIfNotNumber(m_right, m_scratchGPR));
</span><span class="cx"> 
</span><del>-        jit.unboxDouble(m_left, m_leftFPR, m_scratchFPR);
</del><ins>+        jit.unboxDoubleNonDestructive(m_left, m_leftFPR, m_scratchGPR, m_scratchFPR);
</ins><span class="cx">         CCallHelpers::Jump rightIsDouble = jit.branchIfNotInt32(m_right);
</span><span class="cx"> 
</span><span class="cx">         jit.convertInt32ToDouble(m_right.payloadGPR(), m_rightFPR);
</span><span class="lines">@@ -86,16 +94,17 @@
</span><span class="cx">         jit.convertInt32ToDouble(m_left.payloadGPR(), m_leftFPR);
</span><span class="cx"> 
</span><span class="cx">         rightIsDouble.link(&amp;jit);
</span><del>-        jit.unboxDouble(m_right, m_rightFPR, m_scratchFPR);
</del><ins>+        jit.unboxDoubleNonDestructive(m_right, m_rightFPR, m_scratchGPR, m_scratchFPR);
</ins><span class="cx"> 
</span><span class="cx">         rightWasInteger.link(&amp;jit);
</span><span class="cx"> 
</span><span class="cx">         jit.subDouble(m_rightFPR, m_leftFPR);
</span><span class="cx">         jit.boxDouble(m_leftFPR, m_result);
</span><span class="cx"> 
</span><del>-        end.link(&amp;jit);
</del><ins>+        m_endJumpList.append(jit.jump());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    CCallHelpers::JumpList endJumpList() { return m_endJumpList; }
</ins><span class="cx">     CCallHelpers::JumpList slowPathJumpList() { return m_slowPathJumpList; }
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="lines">@@ -109,6 +118,7 @@
</span><span class="cx">     GPRReg m_scratchGPR;
</span><span class="cx">     FPRReg m_scratchFPR;
</span><span class="cx"> 
</span><ins>+    CCallHelpers::JumpList m_endJumpList;
</ins><span class="cx">     CCallHelpers::JumpList m_slowPathJumpList;
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreparserResultTypeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/parser/ResultType.h (191223 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/parser/ResultType.h        2015-10-16 23:21:14 UTC (rev 191223)
+++ trunk/Source/JavaScriptCore/parser/ResultType.h        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><span class="cx">     struct ResultType {
</span><ins>+    private:
</ins><span class="cx">         friend struct OperandTypes;
</span><span class="cx"> 
</span><span class="cx">         typedef char Type;
</span><span class="lines">@@ -46,7 +47,8 @@
</span><span class="cx">             : m_type(type)
</span><span class="cx">         {
</span><span class="cx">         }
</span><del>-        
</del><ins>+
+    public:
</ins><span class="cx">         bool isInt32()
</span><span class="cx">         {
</span><span class="cx">             return m_type &amp; TypeInt32;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressop_subjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/op_sub.js (0 => 191224)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/op_sub.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/op_sub.js        2015-10-16 23:26:14 UTC (rev 191224)
</span><span class="lines">@@ -0,0 +1,293 @@
</span><ins>+//@ 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,
+    &quot;abc&quot;,
+];
+
+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 &lt; set1.length; i++)
+    values.push(set1[i]);
+for (var i = 0; i &lt; set2.length; i++)
+    values.push(set2[i]);
+for (var i = 0; i &lt; set2.length; i++)
+    values.push(&quot;&quot; + set2[i]);
+
+function stringify(value) {
+    if (typeof value == &quot;string&quot;)
+        return '&quot;' + value + '&quot;';
+    return &quot;&quot; + value;
+}
+
+function generateScenarios(xvalues, yvalues) {
+    var scenarios = [];
+    for (var i = 0; i &lt; xvalues.length; i++) {
+        for (var j = 0; j &lt; yvalues.length; j++) {
+            var name = &quot;(&quot; + xvalues[i] + &quot; - &quot; + yvalues[j] + &quot;)&quot;;
+            var x = xvalues[i];
+            var y = yvalues[j];
+            var expected = eval(stringify(x) + &quot; - &quot; + 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 &lt; scenarios.length; i++) {
+        var scenario = scenarios[i];
+        print(&quot;scenario[&quot; + i + &quot;]: { name: &quot; + scenario.name + &quot;, x: &quot; + scenario.x, &quot;, y: &quot; + scenario.y + &quot;, expected: &quot; + scenario.expected + &quot; }&quot;);
+    }
+}
+
+var testCases = [
+    {
+        name: &quot;sub&quot;,
+        func: function(x, y) { return x - y; },
+        xvalues: values,
+        yvalues: values
+    },
+    {
+        name: &quot;subI32V&quot;,
+        func: function(x, y) { return 10 - y; },
+        xvalues: [ 10 ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVI32&quot;,
+        func: function(x, y) { return x - 10; },
+        xvalues: values,
+        yvalues: [ 10 ]
+    },
+    {
+        name: &quot;subI32oV&quot;,
+        func: function(x, y) { return -2147483647 - y; },
+        xvalues: [ -2147483647 ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVI32o&quot;,
+        func: function(x, y) { return x - 2147483647; },
+        xvalues: values,
+        yvalues: [ 2147483647 ]
+    },
+    {
+        name: &quot;subI52V&quot;,
+        func: function(x, y) { return 4294967296 - y; },
+        xvalues: [ 4294967296 ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVI52&quot;,
+        func: function(x, y) { return x - 4294967296; },
+        xvalues: values,
+        yvalues: [ 4294967296 ]
+    },
+    {
+        name: &quot;subDV&quot;,
+        func: function(x, y) { return 100.2 - y; },
+        xvalues: [ 100.2 ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVD&quot;,
+        func: function(x, y) { return x - 100.2; },
+        xvalues: values,
+        yvalues: [ 100.2 ]
+    },
+    {
+        name: &quot;subBV&quot;,
+        func: function(x, y) { return true - y; },
+        xvalues: [ true ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVB&quot;,
+        func: function(x, y) { return x - true; },
+        xvalues: values,
+        yvalues: [ true ]
+    },
+    {
+        name: &quot;subSi32V&quot;,
+        func: function(x, y) { return &quot;10&quot; - y; },
+        xvalues: [ &quot;10&quot; ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVSi32&quot;,
+        func: function(x, y) { return x - &quot;10&quot;; },
+        xvalues: values,
+        yvalues: [ &quot;10&quot; ]
+    },
+
+    {
+        name: &quot;subSi32oV&quot;,
+        func: function(x, y) { return &quot;-2147483647&quot; - y; },
+        xvalues: [ &quot;-2147483647&quot; ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVSi32o&quot;,
+        func: function(x, y) { return x - &quot;2147483647&quot;; },
+        xvalues: values,
+        yvalues: [ &quot;2147483647&quot; ]
+    },
+    {
+        name: &quot;subSi52V&quot;,
+        func: function(x, y) { return &quot;4294967296&quot; - y; },
+        xvalues: [ &quot;4294967296&quot; ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVSi52&quot;,
+        func: function(x, y) { return x - &quot;4294967296&quot;; },
+        xvalues: values,
+        yvalues: [ &quot;4294967296&quot; ]
+    },
+    {
+        name: &quot;subSdV&quot;,
+        func: function(x, y) { return &quot;100.2&quot; - y; },
+        xvalues: [ &quot;100.2&quot; ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVSd&quot;,
+        func: function(x, y) { return x - &quot;100.2&quot;; },
+        xvalues: values,
+        yvalues: [ &quot;100.2&quot; ]
+    },
+    {
+        name: &quot;subSbV&quot;,
+        func: function(x, y) { return &quot;true&quot; - y; },
+        xvalues: [ &quot;true&quot; ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVSb&quot;,
+        func: function(x, y) { return x - &quot;true&quot;; },
+        xvalues: values,
+        yvalues: [ &quot;true&quot; ]
+    },
+
+    {
+        name: &quot;subSV&quot;,
+        func: function(x, y) { return &quot;abc&quot; - y; },
+        xvalues: [ &quot;abc&quot; ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVS&quot;,
+        func: function(x, y) { return x - &quot;abc&quot;; },
+        xvalues: values,
+        yvalues: [ &quot;abc&quot; ]
+    },
+    {
+        name: &quot;subNV&quot;,
+        func: function(x, y) { return null - y; },
+        xvalues: [ null ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVN&quot;,
+        func: function(x, y) { return x - null; },
+        xvalues: values,
+        yvalues: [ null ]
+    },
+    {
+        name: &quot;subOV&quot;,
+        func: function(x, y) { return o1 - y; },
+        xvalues: [ o1 ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVO&quot;,
+        func: function(x, y) { return x - o1; },
+        xvalues: values,
+        yvalues: [ o1 ]
+    },
+    {
+        name: &quot;subNaNV&quot;,
+        func: function(x, y) { return NaN - y; },
+        xvalues: [ NaN ],
+        yvalues: values
+    },
+    {
+        name: &quot;subVNaN&quot;,
+        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 &lt; 10000; i++) {
+            for (var scenarioID = 0; scenarioID &lt; scenarios.length; scenarioID++) {
+                var scenario = scenarios[scenarioID];
+                var result = testFunc(scenario.x, scenario.y);
+                if (result == scenario.expected)
+                    continue;
+                if (Number.isNaN(result) &amp;&amp; Number.isNaN(scenario.expected))
+                    continue;
+                if (!failedScenario[scenarioID]) {
+                    print(&quot;FAIL: &quot; + test.name + &quot;:&quot; + scenario.name + &quot; started failing on iteration &quot; + i + &quot;: expected &quot; + scenario.expected + &quot;, actual &quot; + result);
+                    failedScenario[scenarioID] = scenario;
+                }
+            }
+        }
+    } catch(e) {
+        print(&quot;Unexpected exception: &quot; + e);
+    }
+}
+
+for (var test of testCases)
+    runTest(test);
+
</ins></span></pre>
</div>
</div>

</body>
</html>