<!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>[197546] 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/197546">197546</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2016-03-03 20:40:17 -0800 (Thu, 03 Mar 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>[JSC] Add support for MADD, MSUB and MNEG to Air
https://bugs.webkit.org/show_bug.cgi?id=154997

Patch by Benjamin Poulain &lt;bpoulain@apple.com&gt; on 2016-03-03
Reviewed by Filip Pizlo.

ARM64 can do an Add/Sub in the Multiply units.
LLVM was doing so but we lost that when switching to B3.

This patch adds those instructions in Air.

There are more ALUs than multiply units, thus we are more
likely to successfully schedule a Multiply+Add than 2 Multiply.
I am conservative and only emit a multiply-add if the value
can be interned. As far as I can tell from what is generated
by LLVM, that backend had the same rule.

* assembler/MacroAssemblerARM64.h:
(JSC::MacroAssemblerARM64::multiplyAdd32):
(JSC::MacroAssemblerARM64::multiplySub32):
(JSC::MacroAssemblerARM64::multiplyNeg32):
(JSC::MacroAssemblerARM64::multiplyAdd64):
(JSC::MacroAssemblerARM64::multiplySub64):
(JSC::MacroAssemblerARM64::multiplyNeg64):
* b3/B3LowerToAir.cpp:
(JSC::B3::Air::LowerToAir::lower):
* b3/air/AirOpcode.opcodes:
* b3/testb3.cpp:
(JSC::B3::populateWithInterestingValues):
(JSC::B3::floatingPointOperands):
(JSC::B3::int64Operands):
(JSC::B3::int32Operands):
(JSC::B3::testMulAddArgsLeft):
(JSC::B3::testMulAddArgsRight):
(JSC::B3::testMulAddArgsLeft32):
(JSC::B3::testMulAddArgsRight32):
(JSC::B3::testMulSubArgsLeft):
(JSC::B3::testMulSubArgsRight):
(JSC::B3::testMulSubArgsLeft32):
(JSC::B3::testMulSubArgsRight32):
(JSC::B3::testMulNegArgs):
(JSC::B3::testMulNegArgs32):
(JSC::B3::run):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreassemblerMacroAssemblerARM64h">trunk/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3LowerToAircpp">trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirOpcodeopcodes">trunk/Source/JavaScriptCore/b3/air/AirOpcode.opcodes</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 (197545 => 197546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-03-04 04:02:25 UTC (rev 197545)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-03-04 04:40:17 UTC (rev 197546)
</span><span class="lines">@@ -1,3 +1,48 @@
</span><ins>+2016-03-03  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
+
+        [JSC] Add support for MADD, MSUB and MNEG to Air
+        https://bugs.webkit.org/show_bug.cgi?id=154997
+
+        Reviewed by Filip Pizlo.
+
+        ARM64 can do an Add/Sub in the Multiply units.
+        LLVM was doing so but we lost that when switching to B3.
+
+        This patch adds those instructions in Air.
+
+        There are more ALUs than multiply units, thus we are more
+        likely to successfully schedule a Multiply+Add than 2 Multiply.
+        I am conservative and only emit a multiply-add if the value
+        can be interned. As far as I can tell from what is generated
+        by LLVM, that backend had the same rule.
+
+        * assembler/MacroAssemblerARM64.h:
+        (JSC::MacroAssemblerARM64::multiplyAdd32):
+        (JSC::MacroAssemblerARM64::multiplySub32):
+        (JSC::MacroAssemblerARM64::multiplyNeg32):
+        (JSC::MacroAssemblerARM64::multiplyAdd64):
+        (JSC::MacroAssemblerARM64::multiplySub64):
+        (JSC::MacroAssemblerARM64::multiplyNeg64):
+        * b3/B3LowerToAir.cpp:
+        (JSC::B3::Air::LowerToAir::lower):
+        * b3/air/AirOpcode.opcodes:
+        * b3/testb3.cpp:
+        (JSC::B3::populateWithInterestingValues):
+        (JSC::B3::floatingPointOperands):
+        (JSC::B3::int64Operands):
+        (JSC::B3::int32Operands):
+        (JSC::B3::testMulAddArgsLeft):
+        (JSC::B3::testMulAddArgsRight):
+        (JSC::B3::testMulAddArgsLeft32):
+        (JSC::B3::testMulAddArgsRight32):
+        (JSC::B3::testMulSubArgsLeft):
+        (JSC::B3::testMulSubArgsRight):
+        (JSC::B3::testMulSubArgsLeft32):
+        (JSC::B3::testMulSubArgsRight32):
+        (JSC::B3::testMulNegArgs):
+        (JSC::B3::testMulNegArgs32):
+        (JSC::B3::run):
+
</ins><span class="cx"> 2016-03-03  Saam Barati  &lt;sbarati@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [ES6] Implement Proxy.[[SetPrototypeOf]]
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreassemblerMacroAssemblerARM64h"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h (197545 => 197546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h        2016-03-04 04:02:25 UTC (rev 197545)
+++ trunk/Source/JavaScriptCore/assembler/MacroAssemblerARM64.h        2016-03-04 04:40:17 UTC (rev 197546)
</span><span class="lines">@@ -487,6 +487,36 @@
</span><span class="cx">         m_assembler.mul&lt;64&gt;(dest, left, right);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    void multiplyAdd32(RegisterID mulLeft, RegisterID mulRight, RegisterID summand, RegisterID dest)
+    {
+        m_assembler.madd&lt;32&gt;(dest, mulLeft, mulRight, summand);
+    }
+
+    void multiplySub32(RegisterID mulLeft, RegisterID mulRight, RegisterID minuend, RegisterID dest)
+    {
+        m_assembler.msub&lt;32&gt;(dest, mulLeft, mulRight, minuend);
+    }
+
+    void multiplyNeg32(RegisterID mulLeft, RegisterID mulRight, RegisterID dest)
+    {
+        m_assembler.msub&lt;32&gt;(dest, mulLeft, mulRight, ARM64Registers::zr);
+    }
+
+    void multiplyAdd64(RegisterID mulLeft, RegisterID mulRight, RegisterID summand, RegisterID dest)
+    {
+        m_assembler.madd&lt;64&gt;(dest, mulLeft, mulRight, summand);
+    }
+
+    void multiplySub64(RegisterID mulLeft, RegisterID mulRight, RegisterID minuend, RegisterID dest)
+    {
+        m_assembler.msub&lt;64&gt;(dest, mulLeft, mulRight, minuend);
+    }
+
+    void multiplyNeg64(RegisterID mulLeft, RegisterID mulRight, RegisterID dest)
+    {
+        m_assembler.msub&lt;64&gt;(dest, mulLeft, mulRight, ARM64Registers::zr);
+    }
+
</ins><span class="cx">     void div32(RegisterID dividend, RegisterID divisor, RegisterID dest)
</span><span class="cx">     {
</span><span class="cx">         m_assembler.sdiv&lt;32&gt;(dest, dividend, divisor);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3LowerToAircpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp (197545 => 197546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2016-03-04 04:02:25 UTC (rev 197545)
+++ trunk/Source/JavaScriptCore/b3/B3LowerToAir.cpp        2016-03-04 04:40:17 UTC (rev 197546)
</span><span class="lines">@@ -1704,17 +1704,86 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         case Add: {
</span><ins>+            Air::Opcode multiplyAddOpcode = tryOpcodeForType(MultiplyAdd32, MultiplyAdd64, m_value-&gt;type());
+            if (multiplyAddOpcode != Air::Oops
+                &amp;&amp; isValidForm(multiplyAddOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
+                Value* left = m_value-&gt;child(0);
+                Value* right = m_value-&gt;child(1);
+                if (!imm(right) || m_valueToTmp[right]) {
+                    auto tryAppendMultiplyAdd = [&amp;] (Value* left, Value* right) -&gt; bool {
+                        if (left-&gt;opcode() != Mul || !canBeInternal(left))
+                            return false;
+
+                        Value* multiplyLeft = left-&gt;child(0);
+                        Value* multiplyRight = left-&gt;child(1);
+                        if (m_locked.contains(multiplyLeft) || m_locked.contains(multiplyRight))
+                            return false;
+
+                        append(multiplyAddOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(right), tmp(m_value));
+                        commitInternal(left);
+
+                        return true;
+                    };
+
+                    if (tryAppendMultiplyAdd(left, right))
+                        return;
+                    if (tryAppendMultiplyAdd(right, left))
+                        return;
+                }
+            }
+
</ins><span class="cx">             appendBinOp&lt;Add32, Add64, AddDouble, AddFloat, Commutative&gt;(
</span><span class="cx">                 m_value-&gt;child(0), m_value-&gt;child(1));
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         case Sub: {
</span><ins>+            Air::Opcode multiplySubOpcode = tryOpcodeForType(MultiplySub32, MultiplySub64, m_value-&gt;type());
+            if (multiplySubOpcode != Air::Oops
+                &amp;&amp; isValidForm(multiplySubOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp, Arg::Tmp)) {
+                Value* left = m_value-&gt;child(0);
+                Value* right = m_value-&gt;child(1);
+                if (!imm(right) || m_valueToTmp[right]) {
+                    auto tryAppendMultiplySub = [&amp;] () -&gt; bool {
+                        if (right-&gt;opcode() != Mul || !canBeInternal(right))
+                            return false;
+
+                        Value* multiplyLeft = right-&gt;child(0);
+                        Value* multiplyRight = right-&gt;child(1);
+                        if (m_locked.contains(multiplyLeft) || m_locked.contains(multiplyRight))
+                            return false;
+
+                        append(multiplySubOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(left), tmp(m_value));
+                        commitInternal(right);
+
+                        return true;
+                    };
+
+                    if (tryAppendMultiplySub())
+                        return;
+                }
+            }
+
</ins><span class="cx">             appendBinOp&lt;Sub32, Sub64, SubDouble, SubFloat&gt;(m_value-&gt;child(0), m_value-&gt;child(1));
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         case Neg: {
</span><ins>+            Air::Opcode multiplyNegOpcode = tryOpcodeForType(MultiplyNeg32, MultiplyNeg64, m_value-&gt;type());
+            if (multiplyNegOpcode != Air::Oops
+                &amp;&amp; isValidForm(multiplyNegOpcode, Arg::Tmp, Arg::Tmp, Arg::Tmp)
+                &amp;&amp; m_value-&gt;child(0)-&gt;opcode() == Mul
+                &amp;&amp; canBeInternal(m_value-&gt;child(0))) {
+                Value* multiplyOperation = m_value-&gt;child(0);
+                Value* multiplyLeft = multiplyOperation-&gt;child(0);
+                Value* multiplyRight = multiplyOperation-&gt;child(1);
+                if (!m_locked.contains(multiplyLeft) &amp;&amp; !m_locked.contains(multiplyRight)) {
+                    append(multiplyNegOpcode, tmp(multiplyLeft), tmp(multiplyRight), tmp(m_value));
+                    commitInternal(multiplyOperation);
+                    return;
+                }
+            }
+
</ins><span class="cx">             appendUnOp&lt;Neg32, Neg64, NegateDouble, Air::Oops&gt;(m_value-&gt;child(0));
</span><span class="cx">             return;
</span><span class="cx">         }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirOpcodeopcodes"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirOpcode.opcodes (197545 => 197546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirOpcode.opcodes        2016-03-04 04:02:25 UTC (rev 197545)
+++ trunk/Source/JavaScriptCore/b3/air/AirOpcode.opcodes        2016-03-04 04:40:17 UTC (rev 197546)
</span><span class="lines">@@ -215,6 +215,24 @@
</span><span class="cx"> Mul64 U:G:64, U:G:64, D:G:64
</span><span class="cx">     Tmp, Tmp, Tmp
</span><span class="cx"> 
</span><ins>+arm64: MultiplyAdd32 U:G:32, U:G:32, U:G:32, ZD:G:32
+    Tmp, Tmp, Tmp, Tmp
+
+arm64: MultiplyAdd64 U:G:64, U:G:64, U:G:64, D:G:64
+    Tmp, Tmp, Tmp, Tmp
+
+arm64: MultiplySub32 U:G:32, U:G:32, U:G:32, ZD:G:32
+    Tmp, Tmp, Tmp, Tmp
+
+arm64: MultiplySub64 U:G:64, U:G:64, U:G:64, D:G:64
+    Tmp, Tmp, Tmp, Tmp
+
+arm64: MultiplyNeg32 U:G:32, U:G:32, ZD:G:32
+    Tmp, Tmp, Tmp
+
+arm64: MultiplyNeg64 U:G:64, U:G:64, ZD:G:64
+    Tmp, Tmp, Tmp
+
</ins><span class="cx"> arm64: Div32 U:G:32, U:G:32, ZD:G:32
</span><span class="cx">     Tmp, Tmp, Tmp
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3testb3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/testb3.cpp (197545 => 197546)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/testb3.cpp        2016-03-04 04:02:25 UTC (rev 197545)
+++ trunk/Source/JavaScriptCore/b3/testb3.cpp        2016-03-04 04:40:17 UTC (rev 197546)
</span><span class="lines">@@ -862,6 +862,256 @@
</span><span class="cx">     test(1);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void testMulAddArgsLeft()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg1 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1);
+    Value* arg2 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR2);
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg0, arg1);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Add, Origin(), multiplied, arg2);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int64Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            for (auto c : testValues) {
+                CHECK(invoke&lt;int64_t&gt;(*code, a.value, b.value, c.value) == a.value * b.value + c.value);
+            }
+        }
+    }
+}
+
+void testMulAddArgsRight()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg1 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1);
+    Value* arg2 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR2);
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg1, arg2);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Add, Origin(), arg0, multiplied);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int64Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            for (auto c : testValues) {
+                CHECK(invoke&lt;int64_t&gt;(*code, a.value, b.value, c.value) == a.value + b.value * c.value);
+            }
+        }
+    }
+}
+
+void testMulAddArgsLeft32()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1));
+    Value* arg2 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR2));
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg0, arg1);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Add, Origin(), multiplied, arg2);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int32Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            for (auto c : testValues) {
+                CHECK(invoke&lt;int32_t&gt;(*code, a.value, b.value, c.value) == a.value * b.value + c.value);
+            }
+        }
+    }
+}
+
+void testMulAddArgsRight32()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1));
+    Value* arg2 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR2));
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg1, arg2);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Add, Origin(), arg0, multiplied);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int32Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            for (auto c : testValues) {
+                CHECK(invoke&lt;int32_t&gt;(*code, a.value, b.value, c.value) == a.value + b.value * c.value);
+            }
+        }
+    }
+}
+
+void testMulSubArgsLeft()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg1 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1);
+    Value* arg2 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR2);
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg0, arg1);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Sub, Origin(), multiplied, arg2);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int64Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            for (auto c : testValues) {
+                CHECK(invoke&lt;int64_t&gt;(*code, a.value, b.value, c.value) == a.value * b.value - c.value);
+            }
+        }
+    }
+}
+
+void testMulSubArgsRight()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg1 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1);
+    Value* arg2 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR2);
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg1, arg2);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Sub, Origin(), arg0, multiplied);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int64Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            for (auto c : testValues) {
+                CHECK(invoke&lt;int64_t&gt;(*code, a.value, b.value, c.value) == a.value - b.value * c.value);
+            }
+        }
+    }
+}
+
+void testMulSubArgsLeft32()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1));
+    Value* arg2 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR2));
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg0, arg1);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Sub, Origin(), multiplied, arg2);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int32Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            for (auto c : testValues) {
+                CHECK(invoke&lt;int32_t&gt;(*code, a.value, b.value, c.value) == a.value * b.value - c.value);
+            }
+        }
+    }
+}
+
+void testMulSubArgsRight32()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1));
+    Value* arg2 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR2));
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg1, arg2);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Sub, Origin(), arg0, multiplied);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int32Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            for (auto c : testValues) {
+                CHECK(invoke&lt;int32_t&gt;(*code, a.value, b.value, c.value) == a.value - b.value * c.value);
+            }
+        }
+    }
+}
+
+void testMulNegArgs()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0);
+    Value* arg1 = root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1);
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg0, arg1);
+    Value* zero = root-&gt;appendNew&lt;Const64Value&gt;(proc, Origin(), 0);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Sub, Origin(), zero, multiplied);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int64Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            CHECK(invoke&lt;int64_t&gt;(*code, a.value, b.value) == -(a.value * b.value));
+        }
+    }
+}
+
+void testMulNegArgs32()
+{
+    Procedure proc;
+    BasicBlock* root = proc.addBlock();
+
+    Value* arg0 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR0));
+    Value* arg1 = root-&gt;appendNew&lt;Value&gt;(proc, Trunc, Origin(),
+        root-&gt;appendNew&lt;ArgumentRegValue&gt;(proc, Origin(), GPRInfo::argumentGPR1));
+    Value* multiplied = root-&gt;appendNew&lt;Value&gt;(proc, Mul, Origin(), arg0, arg1);
+    Value* zero = root-&gt;appendNew&lt;Const32Value&gt;(proc, Origin(), 0);
+    Value* added = root-&gt;appendNew&lt;Value&gt;(proc, Sub, Origin(), zero, multiplied);
+    root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), added);
+
+    auto code = compile(proc);
+
+    auto testValues = int32Operands();
+    for (auto a : testValues) {
+        for (auto b : testValues) {
+            CHECK(invoke&lt;int32_t&gt;(*code, a.value, b.value) == -(a.value * b.value));
+        }
+    }
+}
+
</ins><span class="cx"> void testMulArgDouble(double a)
</span><span class="cx"> {
</span><span class="cx">     Procedure proc;
</span><span class="lines">@@ -11333,6 +11583,16 @@
</span><span class="cx">     RUN(testMulArgs32(1, 1));
</span><span class="cx">     RUN(testMulArgs32(1, 2));
</span><span class="cx">     RUN(testMulLoadTwice());
</span><ins>+    RUN(testMulAddArgsLeft());
+    RUN(testMulAddArgsRight());
+    RUN(testMulAddArgsLeft32());
+    RUN(testMulAddArgsRight32());
+    RUN(testMulSubArgsLeft());
+    RUN(testMulSubArgsRight());
+    RUN(testMulSubArgsLeft32());
+    RUN(testMulSubArgsRight32());
+    RUN(testMulNegArgs());
+    RUN(testMulNegArgs32());
</ins><span class="cx"> 
</span><span class="cx">     RUN_UNARY(testMulArgDouble, floatingPointOperands&lt;double&gt;());
</span><span class="cx">     RUN_BINARY(testMulArgsDouble, floatingPointOperands&lt;double&gt;(), floatingPointOperands&lt;double&gt;());
</span></span></pre>
</div>
</div>

</body>
</html>