<!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>[192841] 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/192841">192841</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2015-11-30 16:04:57 -0800 (Mon, 30 Nov 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>B3 stackmaps should support early clobber
https://bugs.webkit.org/show_bug.cgi?id=151668

Reviewed by Geoffrey Garen.

While starting work on FTL lazy slow paths, I realized that we needed some way to say that <a href="http://trac.webkit.org/projects/webkit/changeset/11">r11</a> is
off limits. Not just that it's clobbered, but that it cannot be used for any input values to a
stackmap.

In LLVM we do this by having the AnyRegCC forbid <a href="http://trac.webkit.org/projects/webkit/changeset/11">r11</a>.

In B3, we want something more flexible. In this and other cases, what we really want is an early
clobber set. B3 already supported a late clobber set for every stackmap value. Late clobber means
that the act of performing the operation will cause garbage to be written into those registers.
But here we want: assume that garbage magically appears in those registers in the moment before
the operation executes. Any registers in that set will be off-limits to the inputs to the
stackmap. This should be great for other things, like the way the we handle exceptions.

For the simple <a href="http://trac.webkit.org/projects/webkit/changeset/11">r11</a> issue, what we want is to call the StackmapValue::clobber() method, which now
means both early and late clobber. It's the weapon of choice whenever you're unsure.

This adds the early clobber feature, does some minor Inst refactoring to make this less scary,
and adds a test. The test is simple but it's very comprehensive - for example it tests the
early-clobber-after-Move special case.

* b3/B3StackmapSpecial.cpp:
(JSC::B3::StackmapSpecial::extraClobberedRegs):
(JSC::B3::StackmapSpecial::extraEarlyClobberedRegs):
(JSC::B3::StackmapSpecial::forEachArgImpl):
* b3/B3StackmapSpecial.h:
* b3/B3StackmapValue.cpp:
(JSC::B3::StackmapValue::dumpMeta):
(JSC::B3::StackmapValue::StackmapValue):
* b3/B3StackmapValue.h:
* b3/air/AirCCallSpecial.cpp:
(JSC::B3::Air::CCallSpecial::extraClobberedRegs):
(JSC::B3::Air::CCallSpecial::extraEarlyClobberedRegs):
(JSC::B3::Air::CCallSpecial::dumpImpl):
* b3/air/AirCCallSpecial.h:
* b3/air/AirInst.h:
* b3/air/AirInstInlines.h:
(JSC::B3::Air::Inst::extraClobberedRegs):
(JSC::B3::Air::Inst::extraEarlyClobberedRegs):
(JSC::B3::Air::Inst::forEachTmpWithExtraClobberedRegs):
(JSC::B3::Air::Inst::reportUsedRegisters):
(JSC::B3::Air::Inst::forEachDefAndExtraClobberedTmp): Deleted.
* b3/air/AirIteratedRegisterCoalescing.cpp:
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::IteratedRegisterCoalescingAllocator):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::build):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::allocate):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::initializeDegrees):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::addEdges):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::addEdge):
(JSC::B3::Air::iteratedRegisterCoalescingOnType):
(JSC::B3::Air::iteratedRegisterCoalescing):
* b3/air/AirSpecial.h:
* b3/air/AirSpillEverything.cpp:
(JSC::B3::Air::spillEverything):
* b3/testb3.cpp:
(JSC::B3::testSimplePatchpointWithoutOuputClobbersGPArgs):
(JSC::B3::testSimplePatchpointWithOuputClobbersGPArgs):
(JSC::B3::testSimplePatchpointWithoutOuputClobbersFPArgs):
(JSC::B3::testSimplePatchpointWithOuputClobbersFPArgs):
(JSC::B3::testPatchpointWithEarlyClobber):
(JSC::B3::testPatchpointCallArg):
(JSC::B3::run):
* dfg/DFGCommon.h:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3StackmapSpecialcpp">trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3StackmapSpecialh">trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3StackmapValuecpp">trunk/Source/JavaScriptCore/b3/B3StackmapValue.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3StackmapValueh">trunk/Source/JavaScriptCore/b3/B3StackmapValue.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirCCallSpecialcpp">trunk/Source/JavaScriptCore/b3/air/AirCCallSpecial.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirCCallSpecialh">trunk/Source/JavaScriptCore/b3/air/AirCCallSpecial.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirInsth">trunk/Source/JavaScriptCore/b3/air/AirInst.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirInstInlinesh">trunk/Source/JavaScriptCore/b3/air/AirInstInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirIteratedRegisterCoalescingcpp">trunk/Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirSpecialh">trunk/Source/JavaScriptCore/b3/air/AirSpecial.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirSpillEverythingcpp">trunk/Source/JavaScriptCore/b3/air/AirSpillEverything.cpp</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 (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -1,3 +1,73 @@
</span><ins>+2015-11-30  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        B3 stackmaps should support early clobber
+        https://bugs.webkit.org/show_bug.cgi?id=151668
+
+        Reviewed by Geoffrey Garen.
+
+        While starting work on FTL lazy slow paths, I realized that we needed some way to say that r11 is
+        off limits. Not just that it's clobbered, but that it cannot be used for any input values to a
+        stackmap.
+
+        In LLVM we do this by having the AnyRegCC forbid r11.
+
+        In B3, we want something more flexible. In this and other cases, what we really want is an early
+        clobber set. B3 already supported a late clobber set for every stackmap value. Late clobber means
+        that the act of performing the operation will cause garbage to be written into those registers.
+        But here we want: assume that garbage magically appears in those registers in the moment before
+        the operation executes. Any registers in that set will be off-limits to the inputs to the
+        stackmap. This should be great for other things, like the way the we handle exceptions.
+
+        For the simple r11 issue, what we want is to call the StackmapValue::clobber() method, which now
+        means both early and late clobber. It's the weapon of choice whenever you're unsure.
+
+        This adds the early clobber feature, does some minor Inst refactoring to make this less scary,
+        and adds a test. The test is simple but it's very comprehensive - for example it tests the
+        early-clobber-after-Move special case.
+
+        * b3/B3StackmapSpecial.cpp:
+        (JSC::B3::StackmapSpecial::extraClobberedRegs):
+        (JSC::B3::StackmapSpecial::extraEarlyClobberedRegs):
+        (JSC::B3::StackmapSpecial::forEachArgImpl):
+        * b3/B3StackmapSpecial.h:
+        * b3/B3StackmapValue.cpp:
+        (JSC::B3::StackmapValue::dumpMeta):
+        (JSC::B3::StackmapValue::StackmapValue):
+        * b3/B3StackmapValue.h:
+        * b3/air/AirCCallSpecial.cpp:
+        (JSC::B3::Air::CCallSpecial::extraClobberedRegs):
+        (JSC::B3::Air::CCallSpecial::extraEarlyClobberedRegs):
+        (JSC::B3::Air::CCallSpecial::dumpImpl):
+        * b3/air/AirCCallSpecial.h:
+        * b3/air/AirInst.h:
+        * b3/air/AirInstInlines.h:
+        (JSC::B3::Air::Inst::extraClobberedRegs):
+        (JSC::B3::Air::Inst::extraEarlyClobberedRegs):
+        (JSC::B3::Air::Inst::forEachTmpWithExtraClobberedRegs):
+        (JSC::B3::Air::Inst::reportUsedRegisters):
+        (JSC::B3::Air::Inst::forEachDefAndExtraClobberedTmp): Deleted.
+        * b3/air/AirIteratedRegisterCoalescing.cpp:
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::IteratedRegisterCoalescingAllocator):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::build):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::allocate):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::initializeDegrees):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::addEdges):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::addEdge):
+        (JSC::B3::Air::iteratedRegisterCoalescingOnType):
+        (JSC::B3::Air::iteratedRegisterCoalescing):
+        * b3/air/AirSpecial.h:
+        * b3/air/AirSpillEverything.cpp:
+        (JSC::B3::Air::spillEverything):
+        * b3/testb3.cpp:
+        (JSC::B3::testSimplePatchpointWithoutOuputClobbersGPArgs):
+        (JSC::B3::testSimplePatchpointWithOuputClobbersGPArgs):
+        (JSC::B3::testSimplePatchpointWithoutOuputClobbersFPArgs):
+        (JSC::B3::testSimplePatchpointWithOuputClobbersFPArgs):
+        (JSC::B3::testPatchpointWithEarlyClobber):
+        (JSC::B3::testPatchpointCallArg):
+        (JSC::B3::run):
+        * dfg/DFGCommon.h:
+
</ins><span class="cx"> 2015-11-30  Mark Lam  &lt;mark.lam@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Snippefy op_div for the baseline JIT.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3StackmapSpecialcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.cpp (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.cpp        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.cpp        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -60,9 +60,17 @@
</span><span class="cx">     StackmapValue* value = inst.origin-&gt;as&lt;StackmapValue&gt;();
</span><span class="cx">     ASSERT(value);
</span><span class="cx"> 
</span><del>-    return value-&gt;clobbered();
</del><ins>+    return value-&gt;lateClobbered();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+const RegisterSet&amp; StackmapSpecial::extraEarlyClobberedRegs(Inst&amp; inst)
+{
+    StackmapValue* value = inst.origin-&gt;as&lt;StackmapValue&gt;();
+    ASSERT(value);
+
+    return value-&gt;earlyClobbered();
+}
+
</ins><span class="cx"> void StackmapSpecial::forEachArgImpl(
</span><span class="cx">     unsigned numIgnoredB3Args, unsigned numIgnoredAirArgs,
</span><span class="cx">     Inst&amp; inst, Arg::Role role, const ScopedLambda&lt;Inst::EachArgCallback&gt;&amp; callback)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3StackmapSpecialh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.h (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.h        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/B3StackmapSpecial.h        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -47,6 +47,7 @@
</span><span class="cx"> 
</span><span class="cx"> protected:
</span><span class="cx">     void reportUsedRegisters(Air::Inst&amp;, const RegisterSet&amp;) override;
</span><ins>+    const RegisterSet&amp; extraEarlyClobberedRegs(Air::Inst&amp;) override;
</ins><span class="cx">     const RegisterSet&amp; extraClobberedRegs(Air::Inst&amp;) override;
</span><span class="cx"> 
</span><span class="cx">     // Note that this does not override generate() or dumpImpl()/deepDumpImpl(). We have many some
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3StackmapValuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3StackmapValue.cpp (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3StackmapValue.cpp        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/B3StackmapValue.cpp        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -79,8 +79,8 @@
</span><span class="cx"> void StackmapValue::dumpMeta(CommaPrinter&amp; comma, PrintStream&amp; out) const
</span><span class="cx"> {
</span><span class="cx">     out.print(
</span><del>-        comma, &quot;generator = &quot;, RawPointer(m_generator.get()), &quot;, clobbered = &quot;, m_clobbered,
-        &quot;, usedRegisters = &quot;, m_usedRegisters);
</del><ins>+        comma, &quot;generator = &quot;, RawPointer(m_generator.get()), &quot;, earlyClobbered = &quot;, m_earlyClobbered,
+        &quot;, lateClobbered = &quot;, m_lateClobbered, &quot;, usedRegisters = &quot;, m_usedRegisters);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> StackmapValue::StackmapValue(unsigned index, CheckedOpcodeTag, Opcode opcode, Type type, Origin origin)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3StackmapValueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3StackmapValue.h (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3StackmapValue.h        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/B3StackmapValue.h        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -83,12 +83,24 @@
</span><span class="cx"> 
</span><span class="cx">     const Vector&lt;ValueRep&gt;&amp; reps() const { return m_reps; }
</span><span class="cx"> 
</span><ins>+    void clobberEarly(const RegisterSet&amp; set)
+    {
+        m_earlyClobbered.merge(set);
+    }
+
+    void clobberLate(const RegisterSet&amp; set)
+    {
+        m_lateClobbered.merge(set);
+    }
+
</ins><span class="cx">     void clobber(const RegisterSet&amp; set)
</span><span class="cx">     {
</span><del>-        m_clobbered.merge(set);
</del><ins>+        clobberEarly(set);
+        clobberLate(set);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    const RegisterSet&amp; clobbered() const { return m_clobbered; }
</del><ins>+    const RegisterSet&amp; earlyClobbered() const { return m_earlyClobbered; }
+    const RegisterSet&amp; lateClobbered() const { return m_lateClobbered; }
</ins><span class="cx"> 
</span><span class="cx">     void setGenerator(RefPtr&lt;StackmapGenerator&gt; generator)
</span><span class="cx">     {
</span><span class="lines">@@ -189,7 +201,8 @@
</span><span class="cx">     
</span><span class="cx">     Vector&lt;ValueRep&gt; m_reps;
</span><span class="cx">     RefPtr&lt;StackmapGenerator&gt; m_generator;
</span><del>-    RegisterSet m_clobbered;
</del><ins>+    RegisterSet m_earlyClobbered;
+    RegisterSet m_lateClobbered;
</ins><span class="cx">     RegisterSet m_usedRegisters; // Stackmaps could be further duplicated by Air, but that's unlikely, so we just merge the used registers sets if that were to happen.
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirCCallSpecialcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirCCallSpecial.cpp (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirCCallSpecial.cpp        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/air/AirCCallSpecial.cpp        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -138,6 +138,11 @@
</span><span class="cx">     return CCallHelpers::Jump();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+const RegisterSet&amp; CCallSpecial::extraEarlyClobberedRegs(Inst&amp;)
+{
+    return m_emptyRegs;
+}
+
</ins><span class="cx"> const RegisterSet&amp; CCallSpecial::extraClobberedRegs(Inst&amp;)
</span><span class="cx"> {
</span><span class="cx">     return m_clobberedRegs;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirCCallSpecialh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirCCallSpecial.h (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirCCallSpecial.h        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/air/AirCCallSpecial.h        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -58,6 +58,7 @@
</span><span class="cx">     bool admitsStack(Inst&amp;, unsigned argIndex) override;
</span><span class="cx">     void reportUsedRegisters(Inst&amp;, const RegisterSet&amp;) override;
</span><span class="cx">     CCallHelpers::Jump generate(Inst&amp;, CCallHelpers&amp;, GenerationContext&amp;) override;
</span><ins>+    const RegisterSet&amp; extraEarlyClobberedRegs(Inst&amp;) override;
</ins><span class="cx">     const RegisterSet&amp; extraClobberedRegs(Inst&amp;) override;
</span><span class="cx"> 
</span><span class="cx">     void dumpImpl(PrintStream&amp;) const override;
</span><span class="lines">@@ -76,6 +77,7 @@
</span><span class="cx">         numSpecialArgs + numCalleeArgs + numReturnGPArgs + numReturnFPArgs;
</span><span class="cx">     
</span><span class="cx">     RegisterSet m_clobberedRegs;
</span><ins>+    RegisterSet m_emptyRegs;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } } } // namespace JSC::B3::Air
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirInsth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirInst.h (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirInst.h        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/air/AirInst.h        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -125,10 +125,12 @@
</span><span class="cx">     // Reports any additional registers clobbered by this operation. Note that for efficiency,
</span><span class="cx">     // extraClobberedRegs() only works if hasSpecial() returns true.
</span><span class="cx">     const RegisterSet&amp; extraClobberedRegs();
</span><ins>+    const RegisterSet&amp; extraEarlyClobberedRegs();
</ins><span class="cx"> 
</span><del>-    // Iterate over the Defs and the extra clobbered registers.
</del><ins>+    // Iterate over the Defs and the extra clobbered registers. You must supply the next instruction if
+    // there is one.
</ins><span class="cx">     template&lt;typename Functor&gt;
</span><del>-    void forEachDefAndExtraClobberedTmp(Arg::Type, const Functor&amp; functor);
</del><ins>+    void forEachTmpWithExtraClobberedRegs(Inst* nextInst, const Functor&amp; functor);
</ins><span class="cx"> 
</span><span class="cx">     // Use this to report which registers are live. This should be done just before codegen. Note
</span><span class="cx">     // that for efficiency, reportUsedRegisters() only works if hasSpecial() returns true.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirInstInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirInstInlines.h (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirInstInlines.h        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/air/AirInstInlines.h        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -89,24 +89,29 @@
</span><span class="cx">     return args[0].special()-&gt;extraClobberedRegs(*this);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+inline const RegisterSet&amp; Inst::extraEarlyClobberedRegs()
+{
+    ASSERT(hasSpecial());
+    return args[0].special()-&gt;extraEarlyClobberedRegs(*this);
+}
+
</ins><span class="cx"> template&lt;typename Functor&gt;
</span><del>-inline void Inst::forEachDefAndExtraClobberedTmp(Arg::Type type, const Functor&amp; functor)
</del><ins>+inline void Inst::forEachTmpWithExtraClobberedRegs(Inst* nextInst, const Functor&amp; functor)
</ins><span class="cx"> {
</span><del>-    forEachTmp([&amp;] (Tmp&amp; tmpArg, Arg::Role role, Arg::Type argType) {
-        if (argType == type &amp;&amp; Arg::isDef(role))
-            functor(tmpArg);
-    });
</del><ins>+    forEachTmp(
+        [&amp;] (Tmp&amp; tmpArg, Arg::Role role, Arg::Type argType) {
+            functor(tmpArg, role, argType);
+        });
</ins><span class="cx"> 
</span><del>-    if (!hasSpecial())
-        return;
</del><ins>+    auto reportReg = [&amp;] (Reg reg) {
+        functor(Tmp(reg), Arg::Def, reg.isGPR() ? Arg::GP : Arg::FP);
+    };
</ins><span class="cx"> 
</span><del>-    const RegisterSet&amp; clobberedRegisters = extraClobberedRegs();
-    clobberedRegisters.forEach([functor, type] (Reg reg) {
-        if (reg.isGPR() == (type == Arg::GP)) {
-            Tmp registerTmp(reg);
-            functor(registerTmp);
-        }
-    });
</del><ins>+    if (hasSpecial())
+        extraClobberedRegs().forEach(reportReg);
+
+    if (nextInst &amp;&amp; nextInst-&gt;hasSpecial())
+        nextInst-&gt;extraEarlyClobberedRegs().forEach(reportReg);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> inline void Inst::reportUsedRegisters(const RegisterSet&amp; usedRegisters)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirIteratedRegisterCoalescingcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/air/AirIteratedRegisterCoalescing.cpp        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -145,20 +145,24 @@
</span><span class="cx">         m_isOnSelectStack.ensureSize(tmpArraySize);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void build(Inst&amp; inst, const Liveness&lt;Tmp&gt;::LocalCalc&amp; localCalc)
</del><ins>+    void build(Inst&amp; inst, Inst* nextInst, const Liveness&lt;Tmp&gt;::LocalCalc&amp; localCalc)
</ins><span class="cx">     {
</span><del>-        inst.forEachDefAndExtraClobberedTmp(type, [&amp;] (Tmp&amp; arg) {
-            // All the Def()s interfere with each other and with all the extra clobbered Tmps.
-            // We should not use forEachDefAndExtraClobberedTmp() here since colored Tmps
-            // do not need interference edges in our implementation.
-            inst.forEachTmp([&amp;] (Tmp&amp; otherArg, Arg::Role role, Arg::Type otherArgType) {
-                if (otherArgType != type)
</del><ins>+        inst.forEachTmpWithExtraClobberedRegs(
+            nextInst,
+            [&amp;] (const Tmp&amp; arg, Arg::Role role, Arg::Type argType) {
+                if (!Arg::isDef(role) || argType != type)
</ins><span class="cx">                     return;
</span><del>-
-                if (Arg::isDef(role))
-                    addEdge(arg, otherArg);
</del><ins>+                
+                // All the Def()s interfere with each other and with all the extra clobbered Tmps.
+                // We should not use forEachDefAndExtraClobberedTmp() here since colored Tmps
+                // do not need interference edges in our implementation.
+                inst.forEachTmp([&amp;] (Tmp&amp; otherArg, Arg::Role role, Arg::Type argType) {
+                        if (!Arg::isDef(role) || argType != type)
+                            return;
+                        
+                        addEdge(arg, otherArg);
+                    });
</ins><span class="cx">             });
</span><del>-        });
</del><span class="cx"> 
</span><span class="cx">         if (MoveInstHelper&lt;type&gt;::mayBeCoalescable(inst)) {
</span><span class="cx">             // We do not want the Use() of this move to interfere with the Def(), even if it is live
</span><span class="lines">@@ -195,8 +199,18 @@
</span><span class="cx">                 if (liveTmp != useTmp &amp;&amp; liveTmp.isGP() == (type == Arg::GP))
</span><span class="cx">                     addEdge(defTmp, liveTmp);
</span><span class="cx">             }
</span><ins>+
+            // The next instruction could have early clobbers. We need to consider those now.
+            if (nextInst &amp;&amp; nextInst-&gt;hasSpecial()) {
+                nextInst-&gt;extraEarlyClobberedRegs().forEach(
+                    [&amp;] (Reg reg) {
+                        for (const Tmp&amp; liveTmp : localCalc.live()) {
+                            addEdge(Tmp(reg), liveTmp);
+                        }
+                    });
+            }
</ins><span class="cx">         } else
</span><del>-            addEdges(inst, localCalc.live());
</del><ins>+            addEdges(inst, nextInst, localCalc.live());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void allocate()
</span><span class="lines">@@ -296,15 +310,20 @@
</span><span class="cx">         bzero(m_degrees.data() + firstNonRegIndex, (tmpArraySize - firstNonRegIndex) * sizeof(unsigned));
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void addEdges(Inst&amp; inst, const HashSet&lt;Tmp&gt;&amp; liveTmp)
</del><ins>+    void addEdges(Inst&amp; inst, Inst* nextInst, const HashSet&lt;Tmp&gt;&amp; liveTmp)
</ins><span class="cx">     {
</span><span class="cx">         // All the Def()s interfere with everthing live.
</span><del>-        inst.forEachDefAndExtraClobberedTmp(type, [&amp;] (Tmp&amp; arg) {
-            for (const Tmp&amp; liveTmp : liveTmp) {
-                if (liveTmp.isGP() == (type == Arg::GP))
-                    addEdge(arg, liveTmp);
-            }
-        });
</del><ins>+        inst.forEachTmpWithExtraClobberedRegs(
+            nextInst,
+            [&amp;] (const Tmp&amp; arg, Arg::Role role, Arg::Type argType) {
+                if (!Arg::isDef(role) || argType != type)
+                    return;
+                
+                for (const Tmp&amp; liveTmp : liveTmp) {
+                    if (liveTmp.isGP() == (type == Arg::GP))
+                        addEdge(arg, liveTmp);
+                }
+            });
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void addEdge(const Tmp&amp; a, const Tmp&amp; b)
</span><span class="lines">@@ -1030,7 +1049,8 @@
</span><span class="cx">             Liveness&lt;Tmp&gt;::LocalCalc localCalc(liveness, block);
</span><span class="cx">             for (unsigned instIndex = block-&gt;size(); instIndex--;) {
</span><span class="cx">                 Inst&amp; inst = block-&gt;at(instIndex);
</span><del>-                allocator.build(inst, localCalc);
</del><ins>+                Inst* nextInst = instIndex + 1 &lt; block-&gt;size() ? &amp;block-&gt;at(instIndex + 1) : nullptr;
+                allocator.build(inst, nextInst, localCalc);
</ins><span class="cx">                 localCalc.execute(instIndex);
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="lines">@@ -1068,9 +1088,10 @@
</span><span class="cx">             Liveness&lt;Tmp&gt;::LocalCalc localCalc(liveness, block);
</span><span class="cx">             for (unsigned instIndex = block-&gt;size(); instIndex--;) {
</span><span class="cx">                 Inst&amp; inst = block-&gt;at(instIndex);
</span><ins>+                Inst* nextInst = instIndex + 1 &lt; block-&gt;size() ? &amp;block-&gt;at(instIndex + 1) : nullptr;
</ins><span class="cx"> 
</span><del>-                gpAllocator.build(inst, localCalc);
-                fpAllocator.build(inst, localCalc);
</del><ins>+                gpAllocator.build(inst, nextInst, localCalc);
+                fpAllocator.build(inst, nextInst, localCalc);
</ins><span class="cx"> 
</span><span class="cx">                 localCalc.execute(instIndex);
</span><span class="cx">             }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirSpecialh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirSpecial.h (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirSpecial.h        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/air/AirSpecial.h        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -83,6 +83,7 @@
</span><span class="cx">     
</span><span class="cx">     virtual CCallHelpers::Jump generate(Inst&amp;, CCallHelpers&amp;, GenerationContext&amp;) = 0;
</span><span class="cx"> 
</span><ins>+    virtual const RegisterSet&amp; extraEarlyClobberedRegs(Inst&amp;) = 0;
</ins><span class="cx">     virtual const RegisterSet&amp; extraClobberedRegs(Inst&amp;) = 0;
</span><span class="cx"> 
</span><span class="cx">     // By default, this returns true.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirSpillEverythingcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirSpillEverything.cpp (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirSpillEverything.cpp        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/air/AirSpillEverything.cpp        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -58,12 +58,12 @@
</span><span class="cx"> 
</span><span class="cx">             // Gotta account for dead assignments to registers. These may happen because the input
</span><span class="cx">             // code is suboptimal.
</span><del>-            auto updateRegisterSet = [&amp;registerSet] (const Tmp&amp; tmp) {
-                if (tmp.isReg())
-                    registerSet.set(tmp.reg());
-            };
-            inst.forEachDefAndExtraClobberedTmp(Arg::GP, updateRegisterSet);
-            inst.forEachDefAndExtraClobberedTmp(Arg::FP, updateRegisterSet);
</del><ins>+            inst.forEachTmpWithExtraClobberedRegs(
+                index &lt; block-&gt;size() ? &amp;block-&gt;at(index) : nullptr,
+                [&amp;registerSet] (const Tmp&amp; tmp, Arg::Role role, Arg::Type) {
+                    if (tmp.isReg() &amp;&amp; Arg::isDef(role))
+                        registerSet.set(tmp.reg());
+                });
</ins><span class="cx">         };
</span><span class="cx"> 
</span><span class="cx">         for (unsigned instIndex = block-&gt;size(); instIndex--;) {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3testb3cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/testb3.cpp (192840 => 192841)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/testb3.cpp        2015-12-01 00:03:51 UTC (rev 192840)
+++ trunk/Source/JavaScriptCore/b3/testb3.cpp        2015-12-01 00:04:57 UTC (rev 192841)
</span><span class="lines">@@ -3206,7 +3206,7 @@
</span><span class="cx">     Value* const2 = root-&gt;appendNew&lt;Const64Value&gt;(proc, Origin(), 13);
</span><span class="cx"> 
</span><span class="cx">     PatchpointValue* patchpoint = root-&gt;appendNew&lt;PatchpointValue&gt;(proc, Void, Origin());
</span><del>-    patchpoint-&gt;clobber(RegisterSet(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1));
</del><ins>+    patchpoint-&gt;clobberLate(RegisterSet(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1));
</ins><span class="cx">     patchpoint-&gt;append(ConstrainedValue(const1, ValueRep::SomeRegister));
</span><span class="cx">     patchpoint-&gt;append(ConstrainedValue(const2, ValueRep::SomeRegister));
</span><span class="cx">     patchpoint-&gt;setGenerator(
</span><span class="lines">@@ -3249,7 +3249,7 @@
</span><span class="cx">     clobberAll.exclude(RegisterSet::stackRegisters());
</span><span class="cx">     clobberAll.exclude(RegisterSet::reservedHardwareRegisters());
</span><span class="cx">     clobberAll.clear(GPRInfo::argumentGPR2);
</span><del>-    patchpoint-&gt;clobber(clobberAll);
</del><ins>+    patchpoint-&gt;clobberLate(clobberAll);
</ins><span class="cx"> 
</span><span class="cx">     patchpoint-&gt;append(ConstrainedValue(const1, ValueRep::SomeRegister));
</span><span class="cx">     patchpoint-&gt;append(ConstrainedValue(const2, ValueRep::SomeRegister));
</span><span class="lines">@@ -3286,7 +3286,7 @@
</span><span class="cx">     Value* const2 = root-&gt;appendNew&lt;ConstDoubleValue&gt;(proc, Origin(), 13.1);
</span><span class="cx"> 
</span><span class="cx">     PatchpointValue* patchpoint = root-&gt;appendNew&lt;PatchpointValue&gt;(proc, Void, Origin());
</span><del>-    patchpoint-&gt;clobber(RegisterSet(FPRInfo::argumentFPR0, FPRInfo::argumentFPR1));
</del><ins>+    patchpoint-&gt;clobberLate(RegisterSet(FPRInfo::argumentFPR0, FPRInfo::argumentFPR1));
</ins><span class="cx">     patchpoint-&gt;append(ConstrainedValue(const1, ValueRep::SomeRegister));
</span><span class="cx">     patchpoint-&gt;append(ConstrainedValue(const2, ValueRep::SomeRegister));
</span><span class="cx">     patchpoint-&gt;setGenerator(
</span><span class="lines">@@ -3322,7 +3322,7 @@
</span><span class="cx">     clobberAll.exclude(RegisterSet::stackRegisters());
</span><span class="cx">     clobberAll.exclude(RegisterSet::reservedHardwareRegisters());
</span><span class="cx">     clobberAll.clear(FPRInfo::argumentFPR2);
</span><del>-    patchpoint-&gt;clobber(clobberAll);
</del><ins>+    patchpoint-&gt;clobberLate(clobberAll);
</ins><span class="cx"> 
</span><span class="cx">     patchpoint-&gt;append(ConstrainedValue(const1, ValueRep::SomeRegister));
</span><span class="cx">     patchpoint-&gt;append(ConstrainedValue(const2, ValueRep::SomeRegister));
</span><span class="lines">@@ -3348,6 +3348,42 @@
</span><span class="cx">     CHECK(compileAndRun&lt;double&gt;(proc, 1.5, 2.5) == 59.6);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void testPatchpointWithEarlyClobber()
+{
+    auto test = [] (GPRReg registerToClobber, bool arg1InArgGPR, bool arg2InArgGPR) {
+        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);
+        
+        PatchpointValue* patchpoint = root-&gt;appendNew&lt;PatchpointValue&gt;(proc, Int32, Origin());
+        patchpoint-&gt;append(ConstrainedValue(arg1, ValueRep::SomeRegister));
+        patchpoint-&gt;append(ConstrainedValue(arg2, ValueRep::SomeRegister));
+        patchpoint-&gt;clobberEarly(RegisterSet(registerToClobber));
+        patchpoint-&gt;setGenerator(
+            [&amp;] (CCallHelpers&amp; jit, const StackmapGenerationParams&amp; params) {
+                CHECK((params.reps[1].gpr() == GPRInfo::argumentGPR0) == arg1InArgGPR);
+                CHECK((params.reps[2].gpr() == GPRInfo::argumentGPR1) == arg2InArgGPR);
+
+                if (params.reps[0].gpr() == params.reps[2].gpr()) {
+                    jit.move(params.reps[2].gpr(), params.reps[0].gpr());
+                    jit.add32(params.reps[1].gpr(), params.reps[0].gpr());
+                } else {
+                    jit.move(params.reps[1].gpr(), params.reps[0].gpr());
+                    jit.add32(params.reps[2].gpr(), params.reps[0].gpr());
+                }
+            });
+
+        root-&gt;appendNew&lt;ControlValue&gt;(proc, Return, Origin(), patchpoint);
+
+        CHECK(compileAndRun&lt;int&gt;(proc, 1, 2) == 3);
+    };
+
+    test(GPRInfo::nonArgGPR0, true, true);
+    test(GPRInfo::argumentGPR0, false, true);
+    test(GPRInfo::argumentGPR1, true, false);
+}
+
</ins><span class="cx"> void testPatchpointCallArg()
</span><span class="cx"> {
</span><span class="cx">     Procedure proc;
</span><span class="lines">@@ -5717,6 +5753,7 @@
</span><span class="cx">     RUN(testSimplePatchpointWithOuputClobbersGPArgs());
</span><span class="cx">     RUN(testSimplePatchpointWithoutOuputClobbersFPArgs());
</span><span class="cx">     RUN(testSimplePatchpointWithOuputClobbersFPArgs());
</span><ins>+    RUN(testPatchpointWithEarlyClobber());
</ins><span class="cx">     RUN(testPatchpointCallArg());
</span><span class="cx">     RUN(testPatchpointFixedRegister());
</span><span class="cx">     RUN(testPatchpointAny());
</span></span></pre>
</div>
</div>

</body>
</html>