<!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>[192400] 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/192400">192400</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2015-11-12 17:30:21 -0800 (Thu, 12 Nov 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>B3 should be able to compile programs with CheckAdd, CheckSub, and CheckMul
https://bugs.webkit.org/show_bug.cgi?id=151213

Reviewed by Benjamin Poulain.

This adds lowering code for CheckAdd, CheckSub, and CheckMul along with support for CheckNeg
(i.e. CheckSub with the left child being zero).

This adds tests for these things, by simulating what JS would do: if there is overflow, revert
to double math. To write the test, I needed support for IToD so I added that support.

This also fixes a memory corruption bug in the register allocator.

Also fixes a bug in opcode_generator.rb where we were emitting stray &quot;return&quot; statements inside
switch statements. I think this was benign, but I'm not sure, so I fixed it.

Note that I had to convert CheckMul handling to always use a three-operand form. That's because
there is no other way to tell Air that we need the original value alive. This means that on X86
we'll emit an extra Move just before the checked mul. That's probably fine since that's
necessary anyway.

* assembler/MacroAssemblerX86_64.h:
(JSC::MacroAssemblerX86_64::branchMul64):
(JSC::MacroAssemblerX86_64::branchSub64):
* b3/B3CheckSpecial.cpp:
(JSC::B3::CheckSpecial::generate):
* b3/B3Const32Value.cpp:
(JSC::B3::Const32Value::mulConstant):
(JSC::B3::Const32Value::checkAddConstant):
(JSC::B3::Const32Value::checkSubConstant):
(JSC::B3::Const32Value::checkMulConstant):
(JSC::B3::Const32Value::divConstant):
* b3/B3Const32Value.h:
* b3/B3Const64Value.cpp:
(JSC::B3::Const64Value::mulConstant):
(JSC::B3::Const64Value::checkAddConstant):
(JSC::B3::Const64Value::checkSubConstant):
(JSC::B3::Const64Value::checkMulConstant):
(JSC::B3::Const64Value::divConstant):
* b3/B3Const64Value.h:
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::appendUnOp):
(JSC::B3::Air::LowerToAir::ensureSpecial):
(JSC::B3::Air::LowerToAir::ensureCheckSpecial):
(JSC::B3::Air::LowerToAir::fillStackmap):
(JSC::B3::Air::LowerToAir::lower):
* b3/B3ReduceStrength.cpp:
* b3/B3Value.cpp:
(JSC::B3::Value::mulConstant):
(JSC::B3::Value::checkAddConstant):
(JSC::B3::Value::checkSubConstant):
(JSC::B3::Value::checkMulConstant):
(JSC::B3::Value::divConstant):
* b3/B3Value.h:
* b3/air/AirIteratedRegisterCoalescing.cpp:
(JSC::B3::Air::iteratedRegisterCoalescing):
* b3/air/AirOpcode.opcodes:
* b3/air/opcode_generator.rb:
* b3/testb3.cpp:
(JSC::B3::testCheckMegaCombo):
(JSC::B3::testCheckAddImm):
(JSC::B3::testCheckAdd):
(JSC::B3::testCheckAdd64):
(JSC::B3::testCheckAddFold):
(JSC::B3::testCheckAddFoldFail):
(JSC::B3::testCheckSubImm):
(JSC::B3::testCheckSub):
(JSC::B3::doubleSub):
(JSC::B3::testCheckSub64):
(JSC::B3::testCheckSubFold):
(JSC::B3::testCheckSubFoldFail):
(JSC::B3::testCheckNeg):
(JSC::B3::testCheckNeg64):
(JSC::B3::testCheckMul):
(JSC::B3::testCheckMul64):
(JSC::B3::testCheckMulFold):
(JSC::B3::testCheckMulFoldFail):
(JSC::B3::genericTestCompare):
(JSC::B3::run):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreassemblerMacroAssemblerX86_64h">trunk/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3CheckSpecialcpp">trunk/Source/JavaScriptCore/b3/B3CheckSpecial.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3Const32Valuecpp">trunk/Source/JavaScriptCore/b3/B3Const32Value.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3Const32Valueh">trunk/Source/JavaScriptCore/b3/B3Const32Value.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3Const64Valuecpp">trunk/Source/JavaScriptCore/b3/B3Const64Value.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3Const64Valueh">trunk/Source/JavaScriptCore/b3/B3Const64Value.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3LowerToAircpp">trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3ReduceStrengthcpp">trunk/Source/JavaScriptCore/b3/B3ReduceStrength.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3Valuecpp">trunk/Source/JavaScriptCore/b3/B3Value.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3Valueh">trunk/Source/JavaScriptCore/b3/B3Value.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirIteratedRegisterCoalescingcpp">trunk/Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirOpcodeopcodes">trunk/Source/JavaScriptCore/b3/air/AirOpcode.opcodes</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airopcode_generatorrb">trunk/Source/JavaScriptCore/b3/air/opcode_generator.rb</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3testb3cpp">trunk/Source/JavaScriptCore/b3/testb3.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -1,3 +1,85 @@
</span><ins>+2015-11-12  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        B3 should be able to compile programs with CheckAdd, CheckSub, and CheckMul
+        https://bugs.webkit.org/show_bug.cgi?id=151213
+
+        Reviewed by Benjamin Poulain.
+
+        This adds lowering code for CheckAdd, CheckSub, and CheckMul along with support for CheckNeg
+        (i.e. CheckSub with the left child being zero).
+
+        This adds tests for these things, by simulating what JS would do: if there is overflow, revert
+        to double math. To write the test, I needed support for IToD so I added that support.
+
+        This also fixes a memory corruption bug in the register allocator.
+
+        Also fixes a bug in opcode_generator.rb where we were emitting stray &quot;return&quot; statements inside
+        switch statements. I think this was benign, but I'm not sure, so I fixed it.
+
+        Note that I had to convert CheckMul handling to always use a three-operand form. That's because
+        there is no other way to tell Air that we need the original value alive. This means that on X86
+        we'll emit an extra Move just before the checked mul. That's probably fine since that's
+        necessary anyway.
+
+        * assembler/MacroAssemblerX86_64.h:
+        (JSC::MacroAssemblerX86_64::branchMul64):
+        (JSC::MacroAssemblerX86_64::branchSub64):
+        * b3/B3CheckSpecial.cpp:
+        (JSC::B3::CheckSpecial::generate):
+        * b3/B3Const32Value.cpp:
+        (JSC::B3::Const32Value::mulConstant):
+        (JSC::B3::Const32Value::checkAddConstant):
+        (JSC::B3::Const32Value::checkSubConstant):
+        (JSC::B3::Const32Value::checkMulConstant):
+        (JSC::B3::Const32Value::divConstant):
+        * b3/B3Const32Value.h:
+        * b3/B3Const64Value.cpp:
+        (JSC::B3::Const64Value::mulConstant):
+        (JSC::B3::Const64Value::checkAddConstant):
+        (JSC::B3::Const64Value::checkSubConstant):
+        (JSC::B3::Const64Value::checkMulConstant):
+        (JSC::B3::Const64Value::divConstant):
+        * b3/B3Const64Value.h:
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::appendUnOp):
+        (JSC::B3::Air::LowerToAir::ensureSpecial):
+        (JSC::B3::Air::LowerToAir::ensureCheckSpecial):
+        (JSC::B3::Air::LowerToAir::fillStackmap):
+        (JSC::B3::Air::LowerToAir::lower):
+        * b3/B3ReduceStrength.cpp:
+        * b3/B3Value.cpp:
+        (JSC::B3::Value::mulConstant):
+        (JSC::B3::Value::checkAddConstant):
+        (JSC::B3::Value::checkSubConstant):
+        (JSC::B3::Value::checkMulConstant):
+        (JSC::B3::Value::divConstant):
+        * b3/B3Value.h:
+        * b3/air/AirIteratedRegisterCoalescing.cpp:
+        (JSC::B3::Air::iteratedRegisterCoalescing):
+        * b3/air/AirOpcode.opcodes:
+        * b3/air/opcode_generator.rb:
+        * b3/testb3.cpp:
+        (JSC::B3::testCheckMegaCombo):
+        (JSC::B3::testCheckAddImm):
+        (JSC::B3::testCheckAdd):
+        (JSC::B3::testCheckAdd64):
+        (JSC::B3::testCheckAddFold):
+        (JSC::B3::testCheckAddFoldFail):
+        (JSC::B3::testCheckSubImm):
+        (JSC::B3::testCheckSub):
+        (JSC::B3::doubleSub):
+        (JSC::B3::testCheckSub64):
+        (JSC::B3::testCheckSubFold):
+        (JSC::B3::testCheckSubFoldFail):
+        (JSC::B3::testCheckNeg):
+        (JSC::B3::testCheckNeg64):
+        (JSC::B3::testCheckMul):
+        (JSC::B3::testCheckMul64):
+        (JSC::B3::testCheckMulFold):
+        (JSC::B3::testCheckMulFoldFail):
+        (JSC::B3::genericTestCompare):
+        (JSC::B3::run):
+
</ins><span class="cx"> 2015-11-12  Joseph Pecoraro  &lt;pecoraro@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Reduce list of saved console messages
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreassemblerMacroAssemblerX86_64h"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/assembler/MacroAssemblerX86_64.h        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -785,6 +785,14 @@
</span><span class="cx">         return Jump(m_assembler.jCC(x86Condition(cond)));
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    Jump branchMul64(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest)
+    {
+        if (src1 == dest)
+            return branchMul64(cond, src2, dest);
+        move(src2, dest);
+        return branchMul64(cond, src1, dest);
+    }
+
</ins><span class="cx">     Jump branchSub64(ResultCondition cond, TrustedImm32 imm, RegisterID dest)
</span><span class="cx">     {
</span><span class="cx">         sub64(imm, dest);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3CheckSpecialcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3CheckSpecial.cpp (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3CheckSpecial.cpp        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/B3CheckSpecial.cpp        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -131,13 +131,16 @@
</span><span class="cx"> 
</span><span class="cx">     Vector&lt;ValueRep&gt; reps;
</span><span class="cx">     if (isCheckMath(value-&gt;opcode())) {
</span><del>-        if (value-&gt;opcode() == CheckMul)
-            reps.append(ValueRep());
-        else if (value-&gt;opcode() == CheckSub &amp;&amp; value-&gt;child(0)-&gt;isInt(0))
-            reps.append(ValueRep::constant(0));
-        else
</del><ins>+        if (value-&gt;opcode() == CheckMul) {
+            reps.append(repForArg(*context.code, inst.args[2]));
</ins><span class="cx">             reps.append(repForArg(*context.code, inst.args[3]));
</span><del>-        reps.append(repForArg(*context.code, inst.args[2]));
</del><ins>+        } else {
+            if (value-&gt;opcode() == CheckSub &amp;&amp; value-&gt;child(0)-&gt;isInt(0))
+                reps.append(ValueRep::constant(0));
+            else
+                reps.append(repForArg(*context.code, inst.args[3]));
+            reps.append(repForArg(*context.code, inst.args[2]));
+        }
</ins><span class="cx">     } else {
</span><span class="cx">         ASSERT(value-&gt;opcode() == Check);
</span><span class="cx">         reps.append(ValueRep::constant(1));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3Const32Valuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3Const32Value.cpp (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3Const32Value.cpp        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/B3Const32Value.cpp        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -68,6 +68,36 @@
</span><span class="cx">     return proc.add&lt;Const32Value&gt;(origin(), m_value * other-&gt;asInt32());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+Value* Const32Value::checkAddConstant(Procedure&amp; proc, const Value* other) const
+{
+    if (!other-&gt;hasInt32())
+        return nullptr;
+    CheckedInt32 result = CheckedInt32(m_value) + CheckedInt32(other-&gt;asInt32());
+    if (result.hasOverflowed())
+        return nullptr;
+    return proc.add&lt;Const32Value&gt;(origin(), result.unsafeGet());
+}
+
+Value* Const32Value::checkSubConstant(Procedure&amp; proc, const Value* other) const
+{
+    if (!other-&gt;hasInt32())
+        return nullptr;
+    CheckedInt32 result = CheckedInt32(m_value) - CheckedInt32(other-&gt;asInt32());
+    if (result.hasOverflowed())
+        return nullptr;
+    return proc.add&lt;Const32Value&gt;(origin(), result.unsafeGet());
+}
+
+Value* Const32Value::checkMulConstant(Procedure&amp; proc, const Value* other) const
+{
+    if (!other-&gt;hasInt32())
+        return nullptr;
+    CheckedInt32 result = CheckedInt32(m_value) * CheckedInt32(other-&gt;asInt32());
+    if (result.hasOverflowed())
+        return nullptr;
+    return proc.add&lt;Const32Value&gt;(origin(), result.unsafeGet());
+}
+
</ins><span class="cx"> Value* Const32Value::divConstant(Procedure&amp; proc, const Value* other) const
</span><span class="cx"> {
</span><span class="cx">     if (!other-&gt;hasInt32())
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3Const32Valueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3Const32Value.h (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3Const32Value.h        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/B3Const32Value.h        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -45,6 +45,9 @@
</span><span class="cx">     Value* addConstant(Procedure&amp;, const Value* other) const override;
</span><span class="cx">     Value* subConstant(Procedure&amp;, const Value* other) const override;
</span><span class="cx">     Value* mulConstant(Procedure&amp;, const Value* other) const override;
</span><ins>+    Value* checkAddConstant(Procedure&amp;, const Value* other) const override;
+    Value* checkSubConstant(Procedure&amp;, const Value* other) const override;
+    Value* checkMulConstant(Procedure&amp;, const Value* other) const override;
</ins><span class="cx">     Value* divConstant(Procedure&amp;, const Value* other) const override;
</span><span class="cx">     Value* bitAndConstant(Procedure&amp;, const Value* other) const override;
</span><span class="cx">     Value* bitOrConstant(Procedure&amp;, const Value* other) const override;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3Const64Valuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3Const64Value.cpp (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3Const64Value.cpp        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/B3Const64Value.cpp        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -68,6 +68,36 @@
</span><span class="cx">     return proc.add&lt;Const64Value&gt;(origin(), m_value * other-&gt;asInt64());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+Value* Const64Value::checkAddConstant(Procedure&amp; proc, const Value* other) const
+{
+    if (!other-&gt;hasInt64())
+        return nullptr;
+    CheckedInt64 result = CheckedInt64(m_value) + CheckedInt64(other-&gt;asInt64());
+    if (result.hasOverflowed())
+        return nullptr;
+    return proc.add&lt;Const64Value&gt;(origin(), result.unsafeGet());
+}
+
+Value* Const64Value::checkSubConstant(Procedure&amp; proc, const Value* other) const
+{
+    if (!other-&gt;hasInt64())
+        return nullptr;
+    CheckedInt64 result = CheckedInt64(m_value) - CheckedInt64(other-&gt;asInt64());
+    if (result.hasOverflowed())
+        return nullptr;
+    return proc.add&lt;Const64Value&gt;(origin(), result.unsafeGet());
+}
+
+Value* Const64Value::checkMulConstant(Procedure&amp; proc, const Value* other) const
+{
+    if (!other-&gt;hasInt64())
+        return nullptr;
+    CheckedInt64 result = CheckedInt64(m_value) * CheckedInt64(other-&gt;asInt64());
+    if (result.hasOverflowed())
+        return nullptr;
+    return proc.add&lt;Const64Value&gt;(origin(), result.unsafeGet());
+}
+
</ins><span class="cx"> Value* Const64Value::divConstant(Procedure&amp; proc, const Value* other) const
</span><span class="cx"> {
</span><span class="cx">     if (!other-&gt;hasInt64())
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3Const64Valueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3Const64Value.h (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3Const64Value.h        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/B3Const64Value.h        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -45,6 +45,9 @@
</span><span class="cx">     Value* addConstant(Procedure&amp;, const Value* other) const override;
</span><span class="cx">     Value* subConstant(Procedure&amp;, const Value* other) const override;
</span><span class="cx">     Value* mulConstant(Procedure&amp;, const Value* other) const override;
</span><ins>+    Value* checkAddConstant(Procedure&amp;, const Value* other) const override;
+    Value* checkSubConstant(Procedure&amp;, const Value* other) const override;
+    Value* checkMulConstant(Procedure&amp;, const Value* other) const override;
</ins><span class="cx">     Value* divConstant(Procedure&amp;, const Value* other) const override;
</span><span class="cx">     Value* bitAndConstant(Procedure&amp;, const Value* other) const override;
</span><span class="cx">     Value* bitOrConstant(Procedure&amp;, const Value* other) const override;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3LowerToAircpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -497,11 +497,18 @@
</span><span class="cx">         // mean something like:
</span><span class="cx">         //     b = Op a
</span><span class="cx"> 
</span><ins>+        ArgPromise addr = loadPromise(value);
+        if (isValidForm(opcode, addr.kind(), Arg::Tmp)) {
+            append(opcode, addr.consume(*this), result);
+            return;
+        }
+
</ins><span class="cx">         if (isValidForm(opcode, Arg::Tmp, Arg::Tmp)) {
</span><span class="cx">             append(opcode, tmp(value), result);
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        ASSERT(value-&gt;type() == m_value-&gt;type());
</ins><span class="cx">         append(relaxedMoveForType(m_value-&gt;type()), tmp(value), result);
</span><span class="cx">         append(opcode, result);
</span><span class="cx">     }
</span><span class="lines">@@ -720,6 +727,14 @@
</span><span class="cx">         return field;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    template&lt;typename... Arguments&gt;
+    CheckSpecial* ensureCheckSpecial(Arguments&amp;&amp;... arguments)
+    {
+        CheckSpecial::Key key(std::forward&lt;Arguments&gt;(arguments)...);
+        auto result = m_checkSpecials.add(key, nullptr);
+        return ensureSpecial(result.iterator-&gt;value, key);
+    }
+
</ins><span class="cx">     void fillStackmap(Inst&amp; inst, StackmapValue* stackmap, unsigned numSkipped)
</span><span class="cx">     {
</span><span class="cx">         for (unsigned i = numSkipped; i &lt; stackmap-&gt;numChildren(); ++i) {
</span><span class="lines">@@ -1418,6 +1433,11 @@
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        case IToD: {
+            appendUnOp&lt;ConvertInt32ToDouble, ConvertInt64ToDouble, Air::Oops&gt;(m_value-&gt;child(0));
+            return;
+        }
+
</ins><span class="cx">         case CCall: {
</span><span class="cx">             CCallValue* cCall = m_value-&gt;as&lt;CCallValue&gt;();
</span><span class="cx">             Inst inst(Patch, cCall, Arg::special(m_code.cCallSpecial()));
</span><span class="lines">@@ -1497,13 +1517,112 @@
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        case CheckAdd:
+        case CheckSub: {
+            // FIXME: Make this support commutativity. That will let us leverage more instruction forms
+            // and it let us commute to maximize coalescing.
+            // https://bugs.webkit.org/show_bug.cgi?id=151214
+
+            CheckValue* checkValue = m_value-&gt;as&lt;CheckValue&gt;();
+
+            Value* left = checkValue-&gt;child(0);
+            Value* right = checkValue-&gt;child(1);
+
+            Tmp result = tmp(m_value);
+
+            // Handle checked negation.
+            if (checkValue-&gt;opcode() == CheckSub &amp;&amp; left-&gt;isInt(0)) {
+                append(relaxedMoveForType(checkValue-&gt;type()), tmp(right), result);
+
+                Air::Opcode opcode =
+                    opcodeForType(BranchNeg32, BranchNeg64, Air::Oops, checkValue-&gt;type());
+                CheckSpecial* special = ensureCheckSpecial(opcode, 2);
+
+                Inst inst(Patch, checkValue, Arg::special(special));
+                inst.args.append(Arg::resCond(MacroAssembler::Overflow));
+                inst.args.append(result);
+
+                fillStackmap(inst, checkValue, 2);
+
+                m_insts.last().append(WTF::move(inst));
+                return;
+            }
+
+            append(relaxedMoveForType(m_value-&gt;type()), tmp(left), result);
+            
+            Air::Opcode opcode = Air::Oops;
+            CheckSpecial* special = nullptr;
+            switch (m_value-&gt;opcode()) {
+            case CheckAdd:
+                opcode = opcodeForType(BranchAdd32, BranchAdd64, Air::Oops, m_value-&gt;type());
+                special = ensureCheckSpecial(opcode, 3);
+                break;
+            case CheckSub:
+                opcode = opcodeForType(BranchSub32, BranchSub64, Air::Oops, m_value-&gt;type());
+                special = ensureCheckSpecial(opcode, 3);
+                break;
+            default:
+                RELEASE_ASSERT_NOT_REACHED();
+                break;
+            }
+
+            Inst inst(Patch, checkValue, Arg::special(special));
+
+            inst.args.append(Arg::resCond(MacroAssembler::Overflow));
+
+            // FIXME: It would be great to fuse Loads into these. We currently don't do it because the
+            // rule for stackmaps is that all addresses are just stack addresses. Maybe we could relax
+            // this rule here.
+            // https://bugs.webkit.org/show_bug.cgi?id=151228
+            
+            if (imm(right) &amp;&amp; isValidForm(opcode, Arg::ResCond, Arg::Imm, Arg::Tmp))
+                inst.args.append(imm(right));
+            else
+                inst.args.append(tmp(right));
+            inst.args.append(result);
+            
+            fillStackmap(inst, checkValue, 2);
+
+            m_insts.last().append(WTF::move(inst));
+            return;
+        }
+
+        case CheckMul: {
+            // Handle multiplication separately. Multiplication is hard because we have to preserve
+            // both inputs. This requires using three-operand multiplication, even on platforms where
+            // this requires an additional Move.
+
+            CheckValue* checkValue = m_value-&gt;as&lt;CheckValue&gt;();
+
+            Value* left = checkValue-&gt;child(0);
+            Value* right = checkValue-&gt;child(1);
+
+            Tmp result = tmp(m_value);
+
+            Air::Opcode opcode =
+                opcodeForType(BranchMul32, BranchMul64, Air::Oops, checkValue-&gt;type());
+            CheckSpecial* special = ensureCheckSpecial(opcode, 4);
+
+            // FIXME: Handle immediates.
+            // https://bugs.webkit.org/show_bug.cgi?id=151230
+
+            Inst inst(Patch, checkValue, Arg::special(special));
+            inst.args.append(Arg::resCond(MacroAssembler::Overflow));
+            inst.args.append(tmp(left));
+            inst.args.append(tmp(right));
+            inst.args.append(result);
+
+            fillStackmap(inst, checkValue, 2);
+            
+            m_insts.last().append(WTF::move(inst));
+            return;
+        }
+
</ins><span class="cx">         case Check: {
</span><span class="cx">             Inst branch = createBranch(m_value-&gt;child(0));
</span><ins>+
+            CheckSpecial* special = ensureCheckSpecial(branch);
</ins><span class="cx">             
</span><del>-            CheckSpecial::Key key(branch);
-            auto result = m_checkSpecials.add(key, nullptr);
-            Special* special = ensureSpecial(result.iterator-&gt;value, key);
-            
</del><span class="cx">             CheckValue* checkValue = m_value-&gt;as&lt;CheckValue&gt;();
</span><span class="cx">             
</span><span class="cx">             Inst inst(Patch, checkValue, Arg::special(special));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3ReduceStrengthcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3ReduceStrength.cpp (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3ReduceStrength.cpp        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/B3ReduceStrength.cpp        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -632,6 +632,70 @@
</span><span class="cx">                     m_value-&gt;child(0)-&gt;belowEqualConstant(m_value-&gt;child(1))));
</span><span class="cx">             break;
</span><span class="cx"> 
</span><ins>+        case CheckAdd:
+            // FIXME: Handle commutativity.
+            // https://bugs.webkit.org/show_bug.cgi?id=151214
+            
+            if (replaceWithNewValue(m_value-&gt;child(0)-&gt;checkAddConstant(m_proc, m_value-&gt;child(1))))
+                break;
+            
+            if (m_value-&gt;child(0)-&gt;isInt(0)) {
+                m_value-&gt;replaceWithIdentity(m_value-&gt;child(1));
+                m_changed = true;
+                break;
+            }
+            
+            if (m_value-&gt;child(1)-&gt;isInt(0)) {
+                m_value-&gt;replaceWithIdentity(m_value-&gt;child(0));
+                m_changed = true;
+                break;
+            }
+            break;
+
+        case CheckSub:
+            if (replaceWithNewValue(m_value-&gt;child(0)-&gt;checkSubConstant(m_proc, m_value-&gt;child(1))))
+                break;
+
+            if (m_value-&gt;child(1)-&gt;isInt(0)) {
+                m_value-&gt;replaceWithIdentity(m_value-&gt;child(0));
+                m_changed = true;
+                break;
+            }
+            break;
+
+        case CheckMul:
+            // FIXME: Handle commutativity.
+            // https://bugs.webkit.org/show_bug.cgi?id=151214
+            
+            if (replaceWithNewValue(m_value-&gt;child(0)-&gt;checkMulConstant(m_proc, m_value-&gt;child(1))))
+                break;
+
+            if (m_value-&gt;child(0)-&gt;isInt(1)) {
+                m_value-&gt;replaceWithIdentity(m_value-&gt;child(1));
+                m_changed = true;
+                break;
+            }
+            
+            if (m_value-&gt;child(1)-&gt;isInt(1)) {
+                m_value-&gt;replaceWithIdentity(m_value-&gt;child(0));
+                m_changed = true;
+                break;
+            }
+
+            if (m_value-&gt;child(0)-&gt;isInt(0) || m_value-&gt;child(1)-&gt;isInt(0)) {
+                replaceWithNewValue(m_proc.addIntConstant(m_value, 0));
+                break;
+            }
+            break;
+
+        case Check:
+            if (m_value-&gt;child(0)-&gt;isLikeZero()) {
+                m_value-&gt;replaceWithNop();
+                m_changed = true;
+                break;
+            }
+            break;
+
</ins><span class="cx">         case Branch: {
</span><span class="cx">             ControlValue* branch = m_value-&gt;as&lt;ControlValue&gt;();
</span><span class="cx"> 
</span><span class="lines">@@ -722,13 +786,14 @@
</span><span class="cx">         replaceWithNewValue(m_proc.add&lt;ValueType&gt;(arguments...));
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void replaceWithNewValue(Value* newValue)
</del><ins>+    bool replaceWithNewValue(Value* newValue)
</ins><span class="cx">     {
</span><span class="cx">         if (!newValue)
</span><del>-            return;
</del><ins>+            return false;
</ins><span class="cx">         m_insertionSet.insertValue(m_index, newValue);
</span><span class="cx">         m_value-&gt;replaceWithIdentity(newValue);
</span><span class="cx">         m_changed = true;
</span><ins>+        return true;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     bool handleShiftByZero()
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3Valuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3Value.cpp (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3Value.cpp        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/B3Value.cpp        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -139,6 +139,21 @@
</span><span class="cx">     return nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+Value* Value::checkAddConstant(Procedure&amp;, const Value*) const
+{
+    return nullptr;
+}
+
+Value* Value::checkSubConstant(Procedure&amp;, const Value*) const
+{
+    return nullptr;
+}
+
+Value* Value::checkMulConstant(Procedure&amp;, const Value*) const
+{
+    return nullptr;
+}
+
</ins><span class="cx"> Value* Value::divConstant(Procedure&amp;, const Value*) const
</span><span class="cx"> {
</span><span class="cx">     return nullptr;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3Valueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3Value.h (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3Value.h        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/B3Value.h        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -115,6 +115,9 @@
</span><span class="cx">     virtual Value* addConstant(Procedure&amp;, const Value* other) const;
</span><span class="cx">     virtual Value* subConstant(Procedure&amp;, const Value* other) const;
</span><span class="cx">     virtual Value* mulConstant(Procedure&amp;, const Value* other) const;
</span><ins>+    virtual Value* checkAddConstant(Procedure&amp;, const Value* other) const;
+    virtual Value* checkSubConstant(Procedure&amp;, const Value* other) const;
+    virtual Value* checkMulConstant(Procedure&amp;, const Value* other) const;
</ins><span class="cx">     virtual Value* divConstant(Procedure&amp;, const Value* other) const; // This chooses ChillDiv semantics for integers.
</span><span class="cx">     virtual Value* bitAndConstant(Procedure&amp;, const Value* other) const;
</span><span class="cx">     virtual Value* bitOrConstant(Procedure&amp;, const Value* other) const;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirIteratedRegisterCoalescingcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -920,13 +920,14 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         gpAllocator.allocate();
</span><ins>+        fpAllocator.allocate();
+
</ins><span class="cx">         if (gpAllocator.spilledTmp().isEmpty()) {
</span><span class="cx">             assignRegisterToTmpInProgram(code, gpAllocator);
</span><span class="cx">             gpIsColored = true;
</span><span class="cx">         } else
</span><span class="cx">             addSpillAndFillToProgram&lt;Arg::GP&gt;(code, gpAllocator.spilledTmp());
</span><span class="cx"> 
</span><del>-        fpAllocator.allocate();
</del><span class="cx">         if (fpAllocator.spilledTmp().isEmpty()) {
</span><span class="cx">             assignRegisterToTmpInProgram(code, fpAllocator);
</span><span class="cx">             fpIsColored = true;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirOpcodeopcodes"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirOpcode.opcodes (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirOpcode.opcodes        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/air/AirOpcode.opcodes        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -183,6 +183,13 @@
</span><span class="cx">     Tmp, Addr
</span><span class="cx">     Imm, Tmp
</span><span class="cx"> 
</span><ins>+ConvertInt32ToDouble U:G, D:F
+    Tmp, Tmp
+    Addr, Tmp
+
+ConvertInt64ToDouble U:G, D:F
+    Tmp, Tmp
+
</ins><span class="cx"> # Note that Move operates over the full register size, which is either 32-bit or 64-bit depending on
</span><span class="cx"> # the platform. I'm not entirely sure that this is a good thing; it might be better to just have a
</span><span class="cx"> # Move64 instruction. OTOH, our MacroAssemblers already have this notion of &quot;move()&quot; that basically
</span><span class="lines">@@ -301,10 +308,23 @@
</span><span class="cx">     ResCond, Tmp, Addr
</span><span class="cx">     ResCond, Addr, Tmp
</span><span class="cx"> 
</span><ins>+BranchAdd64 U:G, U:G, UD:G /branch
+    ResCond, Imm, Tmp
+    ResCond, Tmp, Tmp
+
</ins><span class="cx"> BranchMul32 U:G, U:G, UD:G /branch
</span><span class="cx">     ResCond, Tmp, Tmp
</span><span class="cx">     ResCond, Addr, Tmp
</span><span class="cx"> 
</span><ins>+BranchMul32 U:G, U:G, U:G, D:G /branch
+    ResCond, Tmp, Tmp, Tmp
+
+BranchMul64 U:G, U:G, UD:G /branch
+    ResCond, Tmp, Tmp
+
+BranchMul64 U:G, U:G, U:G, D:G /branch
+    ResCond, Tmp, Tmp, Tmp
+
</ins><span class="cx"> BranchSub32 U:G, U:G, UD:G /branch
</span><span class="cx">     ResCond, Tmp, Tmp
</span><span class="cx">     ResCond, Imm, Tmp
</span><span class="lines">@@ -312,9 +332,16 @@
</span><span class="cx">     ResCond, Tmp, Addr
</span><span class="cx">     ResCond, Addr, Tmp
</span><span class="cx"> 
</span><ins>+BranchSub64 U:G, U:G, UD:G /branch
+    ResCond, Imm, Tmp
+    ResCond, Tmp, Tmp
+
</ins><span class="cx"> BranchNeg32 U:G, UD:G /branch
</span><span class="cx">     ResCond, Tmp
</span><span class="cx"> 
</span><ins>+BranchNeg64 U:G, UD:G /branch
+    ResCond, Tmp
+
</ins><span class="cx"> Jump /branch
</span><span class="cx"> 
</span><span class="cx"> Ret /terminal
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airopcode_generatorrb"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/opcode_generator.rb (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/opcode_generator.rb        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/air/opcode_generator.rb        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -791,20 +791,28 @@
</span><span class="cx">     outp.puts &quot;bool Inst::hasNonArgNonControlEffects()&quot;
</span><span class="cx">     outp.puts &quot;{&quot;
</span><span class="cx">     outp.puts &quot;switch (opcode) {&quot;
</span><ins>+    foundTrue = false
</ins><span class="cx">     $opcodes.values.each {
</span><span class="cx">         | opcode |
</span><span class="cx">         if opcode.attributes[:effects]
</span><span class="cx">             outp.puts &quot;case #{opcode.name}:&quot;
</span><ins>+            foundTrue = true
</ins><span class="cx">         end
</span><span class="cx">     }
</span><del>-    outp.puts &quot;return true;&quot;
</del><ins>+    if foundTrue
+        outp.puts &quot;return true;&quot;
+    end
+    foundTrue = false
</ins><span class="cx">     $opcodes.values.each {
</span><span class="cx">         | opcode |
</span><span class="cx">         if opcode.special
</span><span class="cx">             outp.puts &quot;case #{opcode.name}:&quot;
</span><ins>+            foundTrue = true
</ins><span class="cx">         end
</span><span class="cx">     }
</span><del>-    outp.puts &quot;return args[0].special()-&gt;hasNonArgNonControlEffects();&quot;
</del><ins>+    if foundTrue
+        outp.puts &quot;return args[0].special()-&gt;hasNonArgNonControlEffects();&quot;
+    end
</ins><span class="cx">     outp.puts &quot;default:&quot;
</span><span class="cx">     outp.puts &quot;return false;&quot;
</span><span class="cx">     outp.puts &quot;}&quot;
</span><span class="lines">@@ -813,20 +821,28 @@
</span><span class="cx">     outp.puts &quot;bool Inst::hasNonArgEffects()&quot;
</span><span class="cx">     outp.puts &quot;{&quot;
</span><span class="cx">     outp.puts &quot;switch (opcode) {&quot;
</span><ins>+    foundTrue = false
</ins><span class="cx">     $opcodes.values.each {
</span><span class="cx">         | opcode |
</span><span class="cx">         if opcode.attributes[:branch] or opcode.attributes[:terminal] or opcode.attributes[:effects]
</span><span class="cx">             outp.puts &quot;case #{opcode.name}:&quot;
</span><ins>+            foundTrue = true
</ins><span class="cx">         end
</span><span class="cx">     }
</span><del>-    outp.puts &quot;return true;&quot;
</del><ins>+    if foundTrue
+        outp.puts &quot;return true;&quot;
+    end
+    foundTrue = false
</ins><span class="cx">     $opcodes.values.each {
</span><span class="cx">         | opcode |
</span><span class="cx">         if opcode.special
</span><span class="cx">             outp.puts &quot;case #{opcode.name}:&quot;
</span><ins>+            foundTrue = true
</ins><span class="cx">         end
</span><span class="cx">     }
</span><del>-    outp.puts &quot;return args[0].special()-&gt;hasNonArgNonControlEffects();&quot;
</del><ins>+    if foundTrue
+        outp.puts &quot;return args[0].special()-&gt;hasNonArgNonControlEffects();&quot;
+    end
</ins><span class="cx">     outp.puts &quot;default:&quot;
</span><span class="cx">     outp.puts &quot;return false;&quot;
</span><span class="cx">     outp.puts &quot;}&quot;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3testb3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/testb3.cpp (192399 => 192400)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/testb3.cpp        2015-11-13 01:00:09 UTC (rev 192399)
+++ trunk/Source/JavaScriptCore/b3/testb3.cpp        2015-11-13 01:30:21 UTC (rev 192400)
</span><span class="lines">@@ -2918,6 +2918,451 @@
</span><span class="cx">     CHECK(invoke&lt;int&gt;(*code, &amp;value - 2, 1) == 42);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void testCheckAddImm()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(
+        proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg2 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), 42);
+    CheckValue* checkAdd = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckAdd, Origin(), arg1, arg2);
+    checkAdd-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0].isGPR());
+            CHECK(params.reps[1].isConstant());
+            CHECK(params.reps[1].value() == 42);
+            jit.sub32(CCallHelpers::TrustedImm32(42), params.reps[0].gpr());
+            jit.convertInt32ToDouble(params.reps[0].gpr(), FPRInfo::fpRegT0);
+            jit.convertInt32ToDouble(CCallHelpers::TrustedImm32(42), FPRInfo::fpRegT1);
+            jit.addDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkAdd));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0) == 42.0);
+    CHECK(invoke&lt;double&gt;(*code, 1) == 43.0);
+    CHECK(invoke&lt;double&gt;(*code, 42) == 84.0);
+    CHECK(invoke&lt;double&gt;(*code, 2147483647) == 2147483689.0);
+}
+
+void testCheckAdd()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(
+        proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg2 = root-&gt;appendNew&lt;Value&gt;(
+        proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1));
+    CheckValue* checkAdd = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckAdd, Origin(), arg1, arg2);
+    checkAdd-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0].isGPR());
+            CHECK(params.reps[1].isGPR());
+            jit.sub32(params.reps[1].gpr(), params.reps[0].gpr());
+            jit.convertInt32ToDouble(params.reps[0].gpr(), FPRInfo::fpRegT0);
+            jit.convertInt32ToDouble(params.reps[1].gpr(), FPRInfo::fpRegT1);
+            jit.addDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkAdd));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0, 42) == 42.0);
+    CHECK(invoke&lt;double&gt;(*code, 1, 42) == 43.0);
+    CHECK(invoke&lt;double&gt;(*code, 42, 42) == 84.0);
+    CHECK(invoke&lt;double&gt;(*code, 2147483647, 42) == 2147483689.0);
+}
+
+void testCheckAdd64()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg2 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1);
+    CheckValue* checkAdd = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckAdd, Origin(), arg1, arg2);
+    checkAdd-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0].isGPR());
+            CHECK(params.reps[1].isGPR());
+            jit.sub64(params.reps[1].gpr(), params.reps[0].gpr());
+            jit.convertInt64ToDouble(params.reps[0].gpr(), FPRInfo::fpRegT0);
+            jit.convertInt64ToDouble(params.reps[1].gpr(), FPRInfo::fpRegT1);
+            jit.addDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkAdd));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0ll, 42ll) == 42.0);
+    CHECK(invoke&lt;double&gt;(*code, 1ll, 42ll) == 43.0);
+    CHECK(invoke&lt;double&gt;(*code, 42ll, 42ll) == 84.0);
+    CHECK(invoke&lt;double&gt;(*code, 9223372036854775807ll, 42ll) == static_cast&lt;double&gt;(9223372036854775807ll) + 42.0);
+}
+
+void testCheckAddFold(int a, int b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), a);
+    Value* arg2 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), b);
+    CheckValue* checkAdd = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckAdd, Origin(), arg1, arg2);
+    checkAdd-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp;, const StackmapGenerationParams&amp;) {
+            CHECK(!&quot;Should have been folded&quot;);
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), checkAdd);
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;int&gt;(*code) == a + b);
+}
+
+void testCheckAddFoldFail(int a, int b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), a);
+    Value* arg2 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), b);
+    CheckValue* checkAdd = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckAdd, Origin(), arg1, arg2);
+    checkAdd-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp;) {
+            jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), checkAdd);
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;int&gt;(*code) == 42);
+}
+
+void testCheckSubImm()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(
+        proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg2 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), 42);
+    CheckValue* checkSub = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckSub, Origin(), arg1, arg2);
+    checkSub-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0].isGPR());
+            CHECK(params.reps[1].isConstant());
+            CHECK(params.reps[1].value() == 42);
+            jit.add32(CCallHelpers::TrustedImm32(42), params.reps[0].gpr());
+            jit.convertInt32ToDouble(params.reps[0].gpr(), FPRInfo::fpRegT0);
+            jit.convertInt32ToDouble(CCallHelpers::TrustedImm32(42), FPRInfo::fpRegT1);
+            jit.subDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkSub));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0) == -42.0);
+    CHECK(invoke&lt;double&gt;(*code, 1) == -41.0);
+    CHECK(invoke&lt;double&gt;(*code, 42) == 0.0);
+    CHECK(invoke&lt;double&gt;(*code, -2147483647) == -2147483689.0);
+}
+
+void testCheckSub()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(
+        proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg2 = root-&gt;appendNew&lt;Value&gt;(
+        proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1));
+    CheckValue* checkSub = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckSub, Origin(), arg1, arg2);
+    checkSub-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0].isGPR());
+            CHECK(params.reps[1].isGPR());
+            jit.add32(params.reps[1].gpr(), params.reps[0].gpr());
+            jit.convertInt32ToDouble(params.reps[0].gpr(), FPRInfo::fpRegT0);
+            jit.convertInt32ToDouble(params.reps[1].gpr(), FPRInfo::fpRegT1);
+            jit.subDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkSub));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0, 42) == -42.0);
+    CHECK(invoke&lt;double&gt;(*code, 1, 42) == -41.0);
+    CHECK(invoke&lt;double&gt;(*code, 42, 42) == 0.0);
+    CHECK(invoke&lt;double&gt;(*code, -2147483647, 42) == -2147483689.0);
+}
+
+NEVER_INLINE double doubleSub(double a, double b)
+{
+    return a - b;
+}
+
+void testCheckSub64()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg2 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1);
+    CheckValue* checkSub = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckSub, Origin(), arg1, arg2);
+    checkSub-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0].isGPR());
+            CHECK(params.reps[1].isGPR());
+            jit.add64(params.reps[1].gpr(), params.reps[0].gpr());
+            jit.convertInt64ToDouble(params.reps[0].gpr(), FPRInfo::fpRegT0);
+            jit.convertInt64ToDouble(params.reps[1].gpr(), FPRInfo::fpRegT1);
+            jit.subDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkSub));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0ll, 42ll) == -42.0);
+    CHECK(invoke&lt;double&gt;(*code, 1ll, 42ll) == -41.0);
+    CHECK(invoke&lt;double&gt;(*code, 42ll, 42ll) == 0.0);
+    CHECK(invoke&lt;double&gt;(*code, -9223372036854775807ll, 42ll) == doubleSub(static_cast&lt;double&gt;(-9223372036854775807ll), 42.0));
+}
+
+void testCheckSubFold(int a, int b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), a);
+    Value* arg2 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), b);
+    CheckValue* checkSub = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckSub, Origin(), arg1, arg2);
+    checkSub-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp;, const StackmapGenerationParams&amp;) {
+            CHECK(!&quot;Should have been folded&quot;);
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), checkSub);
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;int&gt;(*code) == a - b);
+}
+
+void testCheckSubFoldFail(int a, int b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), a);
+    Value* arg2 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), b);
+    CheckValue* checkSub = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckSub, Origin(), arg1, arg2);
+    checkSub-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp;) {
+            jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), checkSub);
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;int&gt;(*code) == 42);
+}
+
+void testCheckNeg()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), 0);
+    Value* arg2 = root-&gt;appendNew&lt;Value&gt;(
+        proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    CheckValue* checkNeg = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckSub, Origin(), arg1, arg2);
+    checkNeg-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0] == ValueRep::constant(0));
+            CHECK(params.reps[1].isGPR());
+            jit.neg32(params.reps[1].gpr());
+            jit.convertInt32ToDouble(params.reps[1].gpr(), FPRInfo::fpRegT1);
+            jit.negateDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkNeg));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0) == 0.0);
+    CHECK(invoke&lt;double&gt;(*code, 1) == -1.0);
+    CHECK(invoke&lt;double&gt;(*code, 42) == -42.0);
+    CHECK(invoke&lt;double&gt;(*code, -2147483647 - 1) == 2147483648.0);
+}
+
+void testCheckNeg64()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Const64Value&gt;(proc, Origin(), 0);
+    Value* arg2 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    CheckValue* checkNeg = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckSub, Origin(), arg1, arg2);
+    checkNeg-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0] == ValueRep::constant(0));
+            CHECK(params.reps[1].isGPR());
+            jit.neg64(params.reps[1].gpr());
+            jit.convertInt64ToDouble(params.reps[1].gpr(), FPRInfo::fpRegT1);
+            jit.negateDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkNeg));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0ll) == 0.0);
+    CHECK(invoke&lt;double&gt;(*code, 1ll) == -1.0);
+    CHECK(invoke&lt;double&gt;(*code, 42ll) == -42.0);
+    CHECK(invoke&lt;double&gt;(*code, -9223372036854775807ll - 1) == 9223372036854775808.0);
+}
+
+void testCheckMul()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(
+        proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg2 = root-&gt;appendNew&lt;Value&gt;(
+        proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1));
+    CheckValue* checkMul = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckMul, Origin(), arg1, arg2);
+    checkMul-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0].isGPR());
+            CHECK(params.reps[1].isGPR());
+            jit.convertInt32ToDouble(params.reps[0].gpr(), FPRInfo::fpRegT0);
+            jit.convertInt32ToDouble(params.reps[1].gpr(), FPRInfo::fpRegT1);
+            jit.mulDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkMul));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0, 42) == 0.0);
+    CHECK(invoke&lt;double&gt;(*code, 1, 42) == 42.0);
+    CHECK(invoke&lt;double&gt;(*code, 42, 42) == 42.0 * 42.0);
+    CHECK(invoke&lt;double&gt;(*code, 2147483647, 42) == 2147483647.0 * 42.0);
+}
+
+void testCheckMul64()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg2 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1);
+    CheckValue* checkMul = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckMul, Origin(), arg1, arg2);
+    checkMul-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+            CHECK(params.reps.size() == 2);
+            CHECK(params.reps[0].isGPR());
+            CHECK(params.reps[1].isGPR());
+            jit.convertInt64ToDouble(params.reps[0].gpr(), FPRInfo::fpRegT0);
+            jit.convertInt64ToDouble(params.reps[1].gpr(), FPRInfo::fpRegT1);
+            jit.mulDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(
+        proc, Return, Origin(),
+        root-&gt;appendNew&lt;Value&gt;(proc, IToD, Origin(), checkMul));
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;double&gt;(*code, 0, 42) == 0.0);
+    CHECK(invoke&lt;double&gt;(*code, 1, 42) == 42.0);
+    CHECK(invoke&lt;double&gt;(*code, 42, 42) == 42.0 * 42.0);
+    CHECK(invoke&lt;double&gt;(*code, 9223372036854775807ll, 42) == static_cast&lt;double&gt;(9223372036854775807ll) * 42.0);
+}
+
+void testCheckMulFold(int a, int b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), a);
+    Value* arg2 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), b);
+    CheckValue* checkMul = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckMul, Origin(), arg1, arg2);
+    checkMul-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp;, const StackmapGenerationParams&amp;) {
+            CHECK(!&quot;Should have been folded&quot;);
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), checkMul);
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;int&gt;(*code) == a * b);
+}
+
+void testCheckMulFoldFail(int a, int b)
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+    Value* arg1 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), a);
+    Value* arg2 = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), b);
+    CheckValue* checkMul = root-&gt;appendNew&lt;CheckValue&gt;(proc, CheckMul, Origin(), arg1, arg2);
+    checkMul-&gt;setGenerator(
+        [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp;) {
+            jit.move(CCallHelpers::TrustedImm32(42), GPRInfo::returnValueGPR);
+            jit.emitFunctionEpilogue();
+            jit.ret();
+        });
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), checkMul);
+
+    auto code = compile(proc);
+
+    CHECK(invoke&lt;int&gt;(*code) == 42);
+}
+
</ins><span class="cx"> template&lt;typename LeftFunctor, typename RightFunctor&gt;
</span><span class="cx"> void genericTestCompare(
</span><span class="cx">     B3::Opcode opcode, const LeftFunctor&amp; leftFunctor, const RightFunctor&amp; rightFunctor,
</span><span class="lines">@@ -3957,6 +4402,22 @@
</span><span class="cx">     RUN(testSimpleCheck());
</span><span class="cx">     RUN(testCheckLessThan());
</span><span class="cx">     RUN(testCheckMegaCombo());
</span><ins>+    RUN(testCheckAddImm());
+    RUN(testCheckAdd());
+    RUN(testCheckAdd64());
+    RUN(testCheckAddFold(100, 200));
+    RUN(testCheckAddFoldFail(2147483647, 100));
+    RUN(testCheckSubImm());
+    RUN(testCheckSub());
+    RUN(testCheckSub64());
+    RUN(testCheckSubFold(100, 200));
+    RUN(testCheckSubFoldFail(-2147483647, 100));
+    RUN(testCheckNeg());
+    RUN(testCheckNeg64());
+    RUN(testCheckMul());
+    RUN(testCheckMul64());
+    RUN(testCheckMulFold(100, 200));
+    RUN(testCheckMulFoldFail(2147483647, 100));
</ins><span class="cx"> 
</span><span class="cx">     RUN(testCompare(Equal, 42, 42));
</span><span class="cx">     RUN(testCompare(NotEqual, 42, 42));
</span></span></pre>
</div>
</div>

</body>
</html>