<!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>[214410] trunk/Source</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/214410">214410</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2017-03-26 21:17:52 -0700 (Sun, 26 Mar 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>B3::fixSSA should do liveness pruning
https://bugs.webkit.org/show_bug.cgi?id=170111

Reviewed by Saam Barati.
        
Source/JavaScriptCore:

This moves all of the logic of Air::Liveness&lt;&gt; to WTF::Liveness&lt;&gt; and then uses that to
create B3::VariableLiveness. Then this uses VariableLiveness::LiveAtHead to prune Phi
construction.
        
This makes B3::fixSSA run twice as fast. This is a 13% progression on WasmBench compile
times.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* b3/B3BasicBlock.h:
(JSC::B3::BasicBlock::get):
* b3/B3FixSSA.cpp:
(JSC::B3::fixSSA):
* b3/B3VariableLiveness.cpp: Added.
(JSC::B3::VariableLiveness::VariableLiveness):
(JSC::B3::VariableLiveness::~VariableLiveness):
* b3/B3VariableLiveness.h: Added.
(JSC::B3::VariableLivenessAdapter::VariableLivenessAdapter):
(JSC::B3::VariableLivenessAdapter::numIndices):
(JSC::B3::VariableLivenessAdapter::valueToIndex):
(JSC::B3::VariableLivenessAdapter::indexToValue):
(JSC::B3::VariableLivenessAdapter::blockSize):
(JSC::B3::VariableLivenessAdapter::forEachEarlyUse):
(JSC::B3::VariableLivenessAdapter::forEachLateUse):
(JSC::B3::VariableLivenessAdapter::forEachEarlyDef):
(JSC::B3::VariableLivenessAdapter::forEachLateDef):
* b3/air/AirCFG.h: Added.
(JSC::B3::Air::CFG::CFG):
(JSC::B3::Air::CFG::root):
(JSC::B3::Air::CFG::newMap):
(JSC::B3::Air::CFG::successors):
(JSC::B3::Air::CFG::predecessors):
(JSC::B3::Air::CFG::index):
(JSC::B3::Air::CFG::node):
(JSC::B3::Air::CFG::numNodes):
(JSC::B3::Air::CFG::dump):
* b3/air/AirCode.cpp:
(JSC::B3::Air::Code::Code):
* b3/air/AirCode.h:
(JSC::B3::Air::Code::cfg):
* b3/air/AirLiveness.h:
(JSC::B3::Air::LivenessAdapter::LivenessAdapter):
(JSC::B3::Air::LivenessAdapter::blockSize):
(JSC::B3::Air::LivenessAdapter::forEachEarlyUse):
(JSC::B3::Air::LivenessAdapter::forEachLateUse):
(JSC::B3::Air::LivenessAdapter::forEachEarlyDef):
(JSC::B3::Air::LivenessAdapter::forEachLateDef):
(JSC::B3::Air::TmpLivenessAdapter::TmpLivenessAdapter):
(JSC::B3::Air::TmpLivenessAdapter::numIndices):
(JSC::B3::Air::StackSlotLivenessAdapter::StackSlotLivenessAdapter):
(JSC::B3::Air::StackSlotLivenessAdapter::numIndices):
(JSC::B3::Air::StackSlotLivenessAdapter::indexToValue):
(JSC::B3::Air::Liveness::Liveness):
(JSC::B3::Air::Liveness::LocalCalc::LocalCalc): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::Iterable::Iterable): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::iterator): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::operator++): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::operator*): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::operator==): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::operator!=): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::Iterable::begin): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::Iterable::end): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::Iterable::contains): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::live): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::isLive): Deleted.
(JSC::B3::Air::Liveness::LocalCalc::execute): Deleted.
(JSC::B3::Air::Liveness::rawLiveAtHead): Deleted.
(JSC::B3::Air::Liveness::Iterable::Iterable): Deleted.
(JSC::B3::Air::Liveness::Iterable::iterator::iterator): Deleted.
(JSC::B3::Air::Liveness::Iterable::iterator::operator*): Deleted.
(JSC::B3::Air::Liveness::Iterable::iterator::operator++): Deleted.
(JSC::B3::Air::Liveness::Iterable::iterator::operator==): Deleted.
(JSC::B3::Air::Liveness::Iterable::iterator::operator!=): Deleted.
(JSC::B3::Air::Liveness::Iterable::begin): Deleted.
(JSC::B3::Air::Liveness::Iterable::end): Deleted.
(JSC::B3::Air::Liveness::Iterable::contains): Deleted.
(JSC::B3::Air::Liveness::liveAtHead): Deleted.
(JSC::B3::Air::Liveness::liveAtTail): Deleted.
(JSC::B3::Air::Liveness::workset): Deleted.

Source/WTF:

Move Air::Liveness&lt;&gt; to WTF::Liveness&lt;&gt;. This is pretty easy since Air::Liveness&lt;&gt; was
already fairly generic. It leverages the CFG concept so that it can understand many
different kinds of basic blocks. It doesn't try to understand the contents of basic
blocks; it just asks the adaptor for the block size and asks for the early/late
uses/defs of each thing in the block.
        
This makes it easy to create a B3::VariableLiveness, which fixSSA then uses for
pruning. One of the new features is the Liveness::LiveAtHead nested class, which you
instantiate if you want to perform liveAtHead queries, which SSA construction wants to
do.
        
This also fixes https://bugs.webkit.org/show_bug.cgi?id=170102#c12

* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
* wtf/Liveness.h: Added.
(WTF::Liveness::Liveness):
(WTF::Liveness::LocalCalc::LocalCalc):
(WTF::Liveness::LocalCalc::Iterable::Iterable):
(WTF::Liveness::LocalCalc::Iterable::iterator::iterator):
(WTF::Liveness::LocalCalc::Iterable::iterator::operator++):
(WTF::Liveness::LocalCalc::Iterable::iterator::operator*):
(WTF::Liveness::LocalCalc::Iterable::iterator::operator==):
(WTF::Liveness::LocalCalc::Iterable::iterator::operator!=):
(WTF::Liveness::LocalCalc::Iterable::begin):
(WTF::Liveness::LocalCalc::Iterable::end):
(WTF::Liveness::LocalCalc::Iterable::contains):
(WTF::Liveness::LocalCalc::live):
(WTF::Liveness::LocalCalc::isLive):
(WTF::Liveness::LocalCalc::execute):
(WTF::Liveness::rawLiveAtHead):
(WTF::Liveness::Iterable::Iterable):
(WTF::Liveness::Iterable::iterator::iterator):
(WTF::Liveness::Iterable::iterator::operator*):
(WTF::Liveness::Iterable::iterator::operator++):
(WTF::Liveness::Iterable::iterator::operator==):
(WTF::Liveness::Iterable::iterator::operator!=):
(WTF::Liveness::Iterable::begin):
(WTF::Liveness::Iterable::end):
(WTF::Liveness::Iterable::contains):
(WTF::Liveness::liveAtHead):
(WTF::Liveness::liveAtTail):
(WTF::Liveness::workset):
(WTF::Liveness::LiveAtHead::LiveAtHead):
(WTF::Liveness::LiveAtHead::isLiveAtHead):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreCMakeListstxt">trunk/Source/JavaScriptCore/CMakeLists.txt</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj">trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3BasicBlockh">trunk/Source/JavaScriptCore/b3/B3BasicBlock.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3FixSSAcpp">trunk/Source/JavaScriptCore/b3/B3FixSSA.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirCodecpp">trunk/Source/JavaScriptCore/b3/air/AirCode.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirCodeh">trunk/Source/JavaScriptCore/b3/air/AirCode.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirLivenessh">trunk/Source/JavaScriptCore/b3/air/AirLiveness.h</a></li>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFWTFxcodeprojprojectpbxproj">trunk/Source/WTF/WTF.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWTFwtfCMakeListstxt">trunk/Source/WTF/wtf/CMakeLists.txt</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreb3B3VariableLivenesscpp">trunk/Source/JavaScriptCore/b3/B3VariableLiveness.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3VariableLivenessh">trunk/Source/JavaScriptCore/b3/B3VariableLiveness.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirCFGh">trunk/Source/JavaScriptCore/b3/air/AirCFG.h</a></li>
<li><a href="#trunkSourceWTFwtfLivenessh">trunk/Source/WTF/wtf/Liveness.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/CMakeLists.txt (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/CMakeLists.txt        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/JavaScriptCore/CMakeLists.txt        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -180,6 +180,7 @@
</span><span class="cx">     b3/B3ValueKey.cpp
</span><span class="cx">     b3/B3ValueRep.cpp
</span><span class="cx">     b3/B3Variable.cpp
</span><ins>+    b3/B3VariableLiveness.cpp
</ins><span class="cx">     b3/B3VariableValue.cpp
</span><span class="cx">     b3/B3WasmAddressValue.cpp
</span><span class="cx">     b3/B3WasmBoundsCheckValue.cpp
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/JavaScriptCore/ChangeLog        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -1,3 +1,90 @@
</span><ins>+2017-03-26  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        B3::fixSSA should do liveness pruning
+        https://bugs.webkit.org/show_bug.cgi?id=170111
+
+        Reviewed by Saam Barati.
+        
+        This moves all of the logic of Air::Liveness&lt;&gt; to WTF::Liveness&lt;&gt; and then uses that to
+        create B3::VariableLiveness. Then this uses VariableLiveness::LiveAtHead to prune Phi
+        construction.
+        
+        This makes B3::fixSSA run twice as fast. This is a 13% progression on WasmBench compile
+        times.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * b3/B3BasicBlock.h:
+        (JSC::B3::BasicBlock::get):
+        * b3/B3FixSSA.cpp:
+        (JSC::B3::fixSSA):
+        * b3/B3VariableLiveness.cpp: Added.
+        (JSC::B3::VariableLiveness::VariableLiveness):
+        (JSC::B3::VariableLiveness::~VariableLiveness):
+        * b3/B3VariableLiveness.h: Added.
+        (JSC::B3::VariableLivenessAdapter::VariableLivenessAdapter):
+        (JSC::B3::VariableLivenessAdapter::numIndices):
+        (JSC::B3::VariableLivenessAdapter::valueToIndex):
+        (JSC::B3::VariableLivenessAdapter::indexToValue):
+        (JSC::B3::VariableLivenessAdapter::blockSize):
+        (JSC::B3::VariableLivenessAdapter::forEachEarlyUse):
+        (JSC::B3::VariableLivenessAdapter::forEachLateUse):
+        (JSC::B3::VariableLivenessAdapter::forEachEarlyDef):
+        (JSC::B3::VariableLivenessAdapter::forEachLateDef):
+        * b3/air/AirCFG.h: Added.
+        (JSC::B3::Air::CFG::CFG):
+        (JSC::B3::Air::CFG::root):
+        (JSC::B3::Air::CFG::newMap):
+        (JSC::B3::Air::CFG::successors):
+        (JSC::B3::Air::CFG::predecessors):
+        (JSC::B3::Air::CFG::index):
+        (JSC::B3::Air::CFG::node):
+        (JSC::B3::Air::CFG::numNodes):
+        (JSC::B3::Air::CFG::dump):
+        * b3/air/AirCode.cpp:
+        (JSC::B3::Air::Code::Code):
+        * b3/air/AirCode.h:
+        (JSC::B3::Air::Code::cfg):
+        * b3/air/AirLiveness.h:
+        (JSC::B3::Air::LivenessAdapter::LivenessAdapter):
+        (JSC::B3::Air::LivenessAdapter::blockSize):
+        (JSC::B3::Air::LivenessAdapter::forEachEarlyUse):
+        (JSC::B3::Air::LivenessAdapter::forEachLateUse):
+        (JSC::B3::Air::LivenessAdapter::forEachEarlyDef):
+        (JSC::B3::Air::LivenessAdapter::forEachLateDef):
+        (JSC::B3::Air::TmpLivenessAdapter::TmpLivenessAdapter):
+        (JSC::B3::Air::TmpLivenessAdapter::numIndices):
+        (JSC::B3::Air::StackSlotLivenessAdapter::StackSlotLivenessAdapter):
+        (JSC::B3::Air::StackSlotLivenessAdapter::numIndices):
+        (JSC::B3::Air::StackSlotLivenessAdapter::indexToValue):
+        (JSC::B3::Air::Liveness::Liveness):
+        (JSC::B3::Air::Liveness::LocalCalc::LocalCalc): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::Iterable::Iterable): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::iterator): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::operator++): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::operator*): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::operator==): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::Iterable::iterator::operator!=): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::Iterable::begin): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::Iterable::end): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::Iterable::contains): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::live): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::isLive): Deleted.
+        (JSC::B3::Air::Liveness::LocalCalc::execute): Deleted.
+        (JSC::B3::Air::Liveness::rawLiveAtHead): Deleted.
+        (JSC::B3::Air::Liveness::Iterable::Iterable): Deleted.
+        (JSC::B3::Air::Liveness::Iterable::iterator::iterator): Deleted.
+        (JSC::B3::Air::Liveness::Iterable::iterator::operator*): Deleted.
+        (JSC::B3::Air::Liveness::Iterable::iterator::operator++): Deleted.
+        (JSC::B3::Air::Liveness::Iterable::iterator::operator==): Deleted.
+        (JSC::B3::Air::Liveness::Iterable::iterator::operator!=): Deleted.
+        (JSC::B3::Air::Liveness::Iterable::begin): Deleted.
+        (JSC::B3::Air::Liveness::Iterable::end): Deleted.
+        (JSC::B3::Air::Liveness::Iterable::contains): Deleted.
+        (JSC::B3::Air::Liveness::liveAtHead): Deleted.
+        (JSC::B3::Air::Liveness::liveAtTail): Deleted.
+        (JSC::B3::Air::Liveness::workset): Deleted.
+
</ins><span class="cx"> 2017-03-25  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Air::Liveness shouldn't need HashSets
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -1007,6 +1007,9 @@
</span><span class="cx">                 0FF427651591A1CE004CB9FF /* DFGDisassembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF427621591A1C9004CB9FF /* DFGDisassembler.h */; };
</span><span class="cx">                 0FF4B4BC1E88449700DBBE86 /* AirRegLiveness.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF4B4BA1E88449500DBBE86 /* AirRegLiveness.cpp */; };
</span><span class="cx">                 0FF4B4BD1E88449A00DBBE86 /* AirRegLiveness.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF4B4BB1E88449500DBBE86 /* AirRegLiveness.h */; };
</span><ins>+                0FF4B4C71E8893C500DBBE86 /* AirCFG.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF4B4C61E8893BF00DBBE86 /* AirCFG.h */; };
+                0FF4B4CA1E889D7B00DBBE86 /* B3VariableLiveness.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF4B4C81E889D7800DBBE86 /* B3VariableLiveness.cpp */; };
+                0FF4B4CB1E889D7E00DBBE86 /* B3VariableLiveness.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF4B4C91E889D7800DBBE86 /* B3VariableLiveness.h */; };
</ins><span class="cx">                 0FF60AC216740F8300029779 /* ReduceWhitespace.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF60AC016740F8100029779 /* ReduceWhitespace.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 0FF60AC316740F8800029779 /* ReduceWhitespace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF60ABF16740F8100029779 /* ReduceWhitespace.cpp */; };
</span><span class="cx">                 0FF7168C15A3B235008F5DAA /* PropertyOffset.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF7168A15A3B231008F5DAA /* PropertyOffset.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -3524,6 +3527,9 @@
</span><span class="cx">                 0FF427621591A1C9004CB9FF /* DFGDisassembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDisassembler.h; path = dfg/DFGDisassembler.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 0FF4B4BA1E88449500DBBE86 /* AirRegLiveness.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirRegLiveness.cpp; path = b3/air/AirRegLiveness.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 0FF4B4BB1E88449500DBBE86 /* AirRegLiveness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirRegLiveness.h; path = b3/air/AirRegLiveness.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                0FF4B4C61E8893BF00DBBE86 /* AirCFG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirCFG.h; path = b3/air/AirCFG.h; sourceTree = &quot;&lt;group&gt;&quot;; };
+                0FF4B4C81E889D7800DBBE86 /* B3VariableLiveness.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3VariableLiveness.cpp; path = b3/B3VariableLiveness.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
+                0FF4B4C91E889D7800DBBE86 /* B3VariableLiveness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3VariableLiveness.h; path = b3/B3VariableLiveness.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 0FF60ABF16740F8100029779 /* ReduceWhitespace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReduceWhitespace.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 0FF60AC016740F8100029779 /* ReduceWhitespace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReduceWhitespace.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 0FF7168A15A3B231008F5DAA /* PropertyOffset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PropertyOffset.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -5504,6 +5510,8 @@
</span><span class="cx">                                 0FEC84FD1BDACDAC0080FF74 /* B3ValueRep.h */,
</span><span class="cx">                                 0F2BBD921C5FF3F50023EF23 /* B3Variable.cpp */,
</span><span class="cx">                                 0F2BBD931C5FF3F50023EF23 /* B3Variable.h */,
</span><ins>+                                0FF4B4C81E889D7800DBBE86 /* B3VariableLiveness.cpp */,
+                                0FF4B4C91E889D7800DBBE86 /* B3VariableLiveness.h */,
</ins><span class="cx">                                 0F2BBD941C5FF3F50023EF23 /* B3VariableValue.cpp */,
</span><span class="cx">                                 0F2BBD951C5FF3F50023EF23 /* B3VariableValue.h */,
</span><span class="cx">                                 53D444DD1DAF09A000B92784 /* B3WasmAddressValue.cpp */,
</span><span class="lines">@@ -5538,6 +5546,7 @@
</span><span class="cx">                                 0F6183211C45BF070072450B /* AirCCallingConvention.h */,
</span><span class="cx">                                 0FEC854E1BDACDC70080FF74 /* AirCCallSpecial.cpp */,
</span><span class="cx">                                 0FEC854F1BDACDC70080FF74 /* AirCCallSpecial.h */,
</span><ins>+                                0FF4B4C61E8893BF00DBBE86 /* AirCFG.h */,
</ins><span class="cx">                                 0FEC85501BDACDC70080FF74 /* AirCode.cpp */,
</span><span class="cx">                                 0FEC85511BDACDC70080FF74 /* AirCode.h */,
</span><span class="cx">                                 0F6183221C45BF070072450B /* AirCustom.cpp */,
</span><span class="lines">@@ -8547,6 +8556,7 @@
</span><span class="cx">                                 BC02E90F0E1839DB000F9297 /* ErrorPrototype.h in Headers */,
</span><span class="cx">                                 996B731B1BDA08D100331B84 /* ErrorPrototype.lut.h in Headers */,
</span><span class="cx">                                 14AD910C1DCA92940014F9FE /* EvalCodeBlock.h in Headers */,
</span><ins>+                                0FF4B4CB1E889D7E00DBBE86 /* B3VariableLiveness.h in Headers */,
</ins><span class="cx">                                 147341D21DC02E2E00AA29BA /* EvalExecutable.h in Headers */,
</span><span class="cx">                                 A54982041891D0B00081E5B8 /* EventLoop.h in Headers */,
</span><span class="cx">                                 FE1C0FFD1B193E9800B53FCA /* Exception.h in Headers */,
</span><span class="lines">@@ -8840,6 +8850,7 @@
</span><span class="cx">                                 BC18C41E0E16F5CD00B34460 /* JSContextRef.h in Headers */,
</span><span class="cx">                                 A5EA70EE19F5B5C40098F5EC /* JSContextRefInspectorSupport.h in Headers */,
</span><span class="cx">                                 A5D2E665195E174000A518E7 /* JSContextRefInternal.h in Headers */,
</span><ins>+                                0FF4B4C71E8893C500DBBE86 /* AirCFG.h in Headers */,
</ins><span class="cx">                                 148CD1D8108CF902008163C6 /* JSContextRefPrivate.h in Headers */,
</span><span class="cx">                                 A72028B81797601E0098028C /* JSCTestRunnerUtils.h in Headers */,
</span><span class="cx">                                 72AAF7CE1D0D31B3005E60BE /* JSCustomGetterSetterFunction.h in Headers */,
</span><span class="lines">@@ -9964,6 +9975,7 @@
</span><span class="cx">                                 795F099D1E03600500BBE37F /* B3Compile.cpp in Sources */,
</span><span class="cx">                                 0FEC850D1BDACDAC0080FF74 /* B3Const32Value.cpp in Sources */,
</span><span class="cx">                                 0FEC850F1BDACDAC0080FF74 /* B3Const64Value.cpp in Sources */,
</span><ins>+                                0FF4B4CA1E889D7B00DBBE86 /* B3VariableLiveness.cpp in Sources */,
</ins><span class="cx">                                 0FEC85111BDACDAC0080FF74 /* B3ConstDoubleValue.cpp in Sources */,
</span><span class="cx">                                 43422A621C158E6A00E2EB98 /* B3ConstFloatValue.cpp in Sources */,
</span><span class="cx">                                 0F338DF51BE93D550013C88F /* B3ConstrainedValue.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3BasicBlockh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3BasicBlock.h (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3BasicBlock.h        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/JavaScriptCore/b3/B3BasicBlock.h        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -64,6 +64,13 @@
</span><span class="cx">     size_t size() const { return m_values.size(); }
</span><span class="cx">     Value* at(size_t index) const { return m_values[index]; }
</span><span class="cx">     Value*&amp; at(size_t index) { return m_values[index]; }
</span><ins>+    
+    Value* get(size_t index) const
+    {
+        if (index &gt;= size())
+            return nullptr;
+        return at(index);
+    }
</ins><span class="cx"> 
</span><span class="cx">     Value* last() const { return m_values.last(); }
</span><span class="cx">     Value*&amp; last() { return m_values.last(); }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3FixSSAcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3FixSSA.cpp (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3FixSSA.cpp        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/JavaScriptCore/b3/B3FixSSA.cpp        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2016 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -38,6 +38,7 @@
</span><span class="cx"> #include &quot;B3UpsilonValue.h&quot;
</span><span class="cx"> #include &quot;B3ValueInlines.h&quot;
</span><span class="cx"> #include &quot;B3Variable.h&quot;
</span><ins>+#include &quot;B3VariableLiveness.h&quot;
</ins><span class="cx"> #include &quot;B3VariableValue.h&quot;
</span><span class="cx"> #include &lt;wtf/CommaPrinter.h&gt;
</span><span class="cx"> #include &lt;wtf/IndexSet.h&gt;
</span><span class="lines">@@ -136,7 +137,10 @@
</span><span class="cx"> 
</span><span class="cx">     // We know that we have variables to optimize, so do that now.
</span><span class="cx">     breakCriticalEdges(proc);
</span><del>-
</del><ins>+    
+    VariableLiveness liveness(proc);
+    VariableLiveness::LiveAtHead liveAtHead = liveness.liveAtHead();
+    
</ins><span class="cx">     SSACalculator ssa(proc);
</span><span class="cx"> 
</span><span class="cx">     // Create a SSACalculator::Variable (&quot;calcVar&quot;) for every variable.
</span><span class="lines">@@ -167,6 +171,9 @@
</span><span class="cx">     ssa.computePhis(
</span><span class="cx">         [&amp;] (SSACalculator::Variable* calcVar, BasicBlock* block) -&gt; Value* {
</span><span class="cx">             Variable* variable = calcVarToVariable[calcVar-&gt;index()];
</span><ins>+            if (!liveAtHead.isLiveAtHead(block, variable))
+                return nullptr;
+            
</ins><span class="cx">             Value* phi = proc.add&lt;Value&gt;(Phi, variable-&gt;type(), block-&gt;at(0)-&gt;origin());
</span><span class="cx">             if (verbose) {
</span><span class="cx">                 dataLog(
</span><span class="lines">@@ -182,10 +189,8 @@
</span><span class="cx">     for (BasicBlock* block : proc.blocksInPreOrder()) {
</span><span class="cx">         mapping.clear();
</span><span class="cx"> 
</span><del>-        for (unsigned index = calcVarToVariable.size(); index--;) {
-            Variable* variable = calcVarToVariable[index];
-            SSACalculator::Variable* calcVar = ssa.variable(index);
-
</del><ins>+        for (Variable* variable : liveness.liveAtHead(block)) {
+            SSACalculator::Variable* calcVar = variableToCalcVar[variable];
</ins><span class="cx">             SSACalculator::Def* def = ssa.reachingDefAtHead(block, calcVar);
</span><span class="cx">             if (def)
</span><span class="cx">                 mapping[variable] = def-&gt;value();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3VariableLivenesscpp"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/b3/B3VariableLiveness.cpp (0 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3VariableLiveness.cpp                                (rev 0)
+++ trunk/Source/JavaScriptCore/b3/B3VariableLiveness.cpp        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -0,0 +1,45 @@
</span><ins>+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include &quot;config.h&quot;
+#include &quot;B3VariableLiveness.h&quot;
+
+#if ENABLE(B3_JIT)
+
+namespace JSC { namespace B3 {
+
+VariableLiveness::VariableLiveness(Procedure&amp; proc)
+    : WTF::Liveness&lt;VariableLivenessAdapter&gt;(proc.cfg(), proc)
+{
+}
+
+VariableLiveness::~VariableLiveness()
+{
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3VariableLivenessh"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/b3/B3VariableLiveness.h (0 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3VariableLiveness.h                                (rev 0)
+++ trunk/Source/JavaScriptCore/b3/B3VariableLiveness.h        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -0,0 +1,107 @@
</span><ins>+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(B3_JIT)
+
+#include &quot;B3BasicBlock.h&quot;
+#include &quot;B3CFG.h&quot;
+#include &quot;B3Procedure.h&quot;
+#include &quot;B3ValueInlines.h&quot;
+#include &quot;B3Variable.h&quot;
+#include &quot;B3VariableValue.h&quot;
+#include &lt;wtf/Liveness.h&gt;
+
+namespace JSC { namespace B3 {
+
+struct VariableLivenessAdapter {
+    static constexpr const char* name = &quot;VariableLiveness&quot;;
+    typedef B3::CFG CFG;
+    typedef Variable* Thing;
+    
+    VariableLivenessAdapter(Procedure&amp; proc)
+        : proc(proc)
+    {
+    }
+    
+    unsigned numIndices()
+    {
+        return proc.variables().size();
+    }
+    
+    static unsigned valueToIndex(Variable* var) { return var-&gt;index(); }
+    Variable* indexToValue(unsigned index) { return proc.variables()[index]; }
+    
+    unsigned blockSize(BasicBlock* block)
+    {
+        return block-&gt;size();
+    }
+    
+    template&lt;typename Func&gt;
+    void forEachEarlyUse(BasicBlock* block, unsigned valueIndex, const Func&amp; func)
+    {
+        Value* value = block-&gt;get(valueIndex);
+        if (!value)
+            return;
+        if (value-&gt;opcode() != Get)
+            return;
+        func(value-&gt;as&lt;VariableValue&gt;()-&gt;variable()-&gt;index());
+    }
+    
+    template&lt;typename Func&gt;
+    void forEachLateUse(BasicBlock*, unsigned, const Func&amp;)
+    {
+    }
+    
+    template&lt;typename Func&gt;
+    void forEachEarlyDef(BasicBlock*, unsigned, const Func&amp;)
+    {
+    }
+    
+    template&lt;typename Func&gt;
+    void forEachLateDef(BasicBlock* block, unsigned valueIndex, const Func&amp; func)
+    {
+        Value* value = block-&gt;get(valueIndex);
+        if (!value)
+            return;
+        if (value-&gt;opcode() != Set)
+            return;
+        func(value-&gt;as&lt;VariableValue&gt;()-&gt;variable()-&gt;index());
+    }
+    
+    Procedure&amp; proc;
+};
+
+class VariableLiveness : public WTF::Liveness&lt;VariableLivenessAdapter&gt; {
+public:
+    VariableLiveness(Procedure&amp;);
+    ~VariableLiveness();
+};
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirCFGh"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/b3/air/AirCFG.h (0 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirCFG.h                                (rev 0)
+++ trunk/Source/JavaScriptCore/b3/air/AirCFG.h        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -0,0 +1,76 @@
</span><ins>+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#pragma once
+
+#if ENABLE(B3_JIT)
+
+#include &quot;AirBasicBlock.h&quot;
+#include &quot;AirCode.h&quot;
+#include &lt;wtf/IndexMap.h&gt;
+#include &lt;wtf/IndexSet.h&gt;
+
+namespace JSC { namespace B3 { namespace Air {
+
+class CFG {
+    WTF_MAKE_NONCOPYABLE(CFG);
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    typedef BasicBlock* Node;
+    typedef IndexSet&lt;BasicBlock&gt; Set;
+    template&lt;typename T&gt; using Map = IndexMap&lt;BasicBlock, T&gt;;
+    typedef Vector&lt;BasicBlock*, 4&gt; List;
+
+    CFG(Code&amp; code)
+        : m_code(code)
+    {
+    }
+
+    Node root() { return m_code[0]; }
+
+    template&lt;typename T&gt;
+    Map&lt;T&gt; newMap() { return IndexMap&lt;JSC::B3::Air::BasicBlock, T&gt;(m_code.size()); }
+
+    SuccessorCollection&lt;BasicBlock, BasicBlock::SuccessorList&gt; successors(Node node) { return node-&gt;successorBlocks(); }
+    BasicBlock::PredecessorList&amp; predecessors(Node node) { return node-&gt;predecessors(); }
+
+    unsigned index(Node node) const { return node-&gt;index(); }
+    Node node(unsigned index) const { return m_code[index]; }
+    unsigned numNodes() const { return m_code.size(); }
+
+    PointerDump&lt;BasicBlock&gt; dump(Node node) const { return pointerDump(node); }
+
+    void dump(PrintStream&amp; out) const
+    {
+        m_code.dump(out);
+    }
+
+private:
+    Code&amp; m_code;
+};
+
+} } } // namespace JSC::B3::Air
+
+#endif // ENABLE(B3_JIT)
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirCodecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirCode.cpp (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirCode.cpp        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/JavaScriptCore/b3/air/AirCode.cpp        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #if ENABLE(B3_JIT)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;AirCCallSpecial.h&quot;
</span><ins>+#include &quot;AirCFG.h&quot;
</ins><span class="cx"> #include &quot;B3BasicBlockUtils.h&quot;
</span><span class="cx"> #include &quot;B3Procedure.h&quot;
</span><span class="cx"> #include &quot;B3StackSlot.h&quot;
</span><span class="lines">@@ -38,6 +39,7 @@
</span><span class="cx"> 
</span><span class="cx"> Code::Code(Procedure&amp; proc)
</span><span class="cx">     : m_proc(proc)
</span><ins>+    , m_cfg(new CFG(*this))
</ins><span class="cx">     , m_lastPhaseName(&quot;initial&quot;)
</span><span class="cx"> {
</span><span class="cx">     // Come up with initial orderings of registers. The user may replace this with something else.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirCodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirCode.h (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirCode.h        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/JavaScriptCore/b3/air/AirCode.h        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -52,6 +52,7 @@
</span><span class="cx"> 
</span><span class="cx"> class BlockInsertionSet;
</span><span class="cx"> class CCallSpecial;
</span><ins>+class CFG;
</ins><span class="cx"> class Disassembler;
</span><span class="cx"> 
</span><span class="cx"> typedef void WasmBoundsCheckGeneratorFunction(CCallHelpers&amp;, GPRReg, unsigned);
</span><span class="lines">@@ -176,7 +177,7 @@
</span><span class="cx"> 
</span><span class="cx">     // Recomputes predecessors and deletes unreachable blocks.
</span><span class="cx">     void resetReachability();
</span><del>-
</del><ins>+    
</ins><span class="cx">     JS_EXPORT_PRIVATE void dump(PrintStream&amp;) const;
</span><span class="cx"> 
</span><span class="cx">     unsigned size() const { return m_blocks.size(); }
</span><span class="lines">@@ -256,6 +257,8 @@
</span><span class="cx">     void addFastTmp(Tmp);
</span><span class="cx">     bool isFastTmp(Tmp tmp) const { return m_fastTmps.contains(tmp); }
</span><span class="cx">     
</span><ins>+    CFG&amp; cfg() const { return *m_cfg; }
+    
</ins><span class="cx">     void* addDataSection(size_t);
</span><span class="cx">     
</span><span class="cx">     // The name has to be a string literal, since we don't do any memory management for the string.
</span><span class="lines">@@ -304,6 +307,7 @@
</span><span class="cx">     SparseCollection&lt;StackSlot&gt; m_stackSlots;
</span><span class="cx">     Vector&lt;std::unique_ptr&lt;BasicBlock&gt;&gt; m_blocks;
</span><span class="cx">     SparseCollection&lt;Special&gt; m_specials;
</span><ins>+    std::unique_ptr&lt;CFG&gt; m_cfg;
</ins><span class="cx">     HashSet&lt;Tmp&gt; m_fastTmps;
</span><span class="cx">     CCallSpecial* m_cCallSpecial { nullptr };
</span><span class="cx">     unsigned m_numGPTmps { 0 };
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirLivenessh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirLiveness.h (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirLiveness.h        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/JavaScriptCore/b3/air/AirLiveness.h        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -28,27 +28,107 @@
</span><span class="cx"> #if ENABLE(B3_JIT)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;AirBasicBlock.h&quot;
</span><ins>+#include &quot;AirCFG.h&quot;
</ins><span class="cx"> #include &quot;AirCode.h&quot;
</span><span class="cx"> #include &quot;AirInstInlines.h&quot;
</span><span class="cx"> #include &quot;AirStackSlot.h&quot;
</span><span class="cx"> #include &quot;AirTmpInlines.h&quot;
</span><del>-#include &quot;B3TimingScope.h&quot;
-#include &lt;wtf/IndexMap.h&gt;
-#include &lt;wtf/IndexSparseSet.h&gt;
-#include &lt;wtf/ListDump.h&gt;
</del><ins>+#include &lt;wtf/Liveness.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace JSC { namespace B3 { namespace Air {
</span><span class="cx"> 
</span><ins>+template&lt;typename Adapter&gt;
+struct LivenessAdapter {
+    typedef Air::CFG CFG;
+    
+    LivenessAdapter(Code&amp; code)
+        : code(code)
+    {
+    }
+    
+    unsigned blockSize(BasicBlock* block)
+    {
+        return block-&gt;size();
+    }
+
+    template&lt;typename Func&gt;
+    void forEachEarlyUse(BasicBlock* block, unsigned instIndex, const Func&amp; func)
+    {
+        Inst* inst = block-&gt;get(instIndex);
+        if (!inst)
+            return;
+        inst-&gt;forEach&lt;typename Adapter::Thing&gt;(
+            [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
+                if (Arg::isEarlyUse(role)
+                    &amp;&amp; Adapter::acceptsBank(bank)
+                    &amp;&amp; Adapter::acceptsRole(role))
+                    func(Adapter::valueToIndex(thing));
+            });
+    }
+    
+    template&lt;typename Func&gt;
+    void forEachLateUse(BasicBlock* block, unsigned instIndex, const Func&amp; func)
+    {
+        Inst* inst = block-&gt;get(instIndex);
+        if (!inst)
+            return;
+        inst-&gt;forEach&lt;typename Adapter::Thing&gt;(
+            [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
+                if (Arg::isLateUse(role)
+                    &amp;&amp; Adapter::acceptsBank(bank)
+                    &amp;&amp; Adapter::acceptsRole(role))
+                    func(Adapter::valueToIndex(thing));
+            });
+    }
+    
+    template&lt;typename Func&gt;
+    void forEachEarlyDef(BasicBlock* block, unsigned instIndex, const Func&amp; func)
+    {
+        Inst* inst = block-&gt;get(instIndex);
+        if (!inst)
+            return;
+        inst-&gt;forEach&lt;typename Adapter::Thing&gt;(
+            [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
+                if (Arg::isEarlyDef(role)
+                    &amp;&amp; Adapter::acceptsBank(bank)
+                    &amp;&amp; Adapter::acceptsRole(role))
+                    func(Adapter::valueToIndex(thing));
+            });
+    }
+    
+    template&lt;typename Func&gt;
+    void forEachLateDef(BasicBlock* block, unsigned instIndex, const Func&amp; func)
+    {
+        Inst* inst = block-&gt;get(instIndex);
+        if (!inst)
+            return;
+        inst-&gt;forEach&lt;typename Adapter::Thing&gt;(
+            [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
+                if (Arg::isLateDef(role)
+                    &amp;&amp; Adapter::acceptsBank(bank)
+                    &amp;&amp; Adapter::acceptsRole(role))
+                    func(Adapter::valueToIndex(thing));
+            });
+    }
+    
+    Code&amp; code;
+};
+
</ins><span class="cx"> template&lt;Bank adapterBank, Arg::Temperature minimumTemperature = Arg::Cold&gt;
</span><del>-struct TmpLivenessAdapter {
</del><ins>+struct TmpLivenessAdapter : LivenessAdapter&lt;TmpLivenessAdapter&lt;adapterBank, minimumTemperature&gt;&gt; {
+    typedef LivenessAdapter&lt;TmpLivenessAdapter&lt;adapterBank, minimumTemperature&gt;&gt; Base;
+    
</ins><span class="cx">     static constexpr const char* name = &quot;TmpLiveness&quot;;
</span><span class="cx">     typedef Tmp Thing;
</span><span class="cx"> 
</span><del>-    TmpLivenessAdapter(Code&amp;) { }
</del><ins>+    TmpLivenessAdapter(Code&amp; code)
+        : Base(code)
+    {
+    }
</ins><span class="cx"> 
</span><del>-    static unsigned numIndices(Code&amp; code)
</del><ins>+    unsigned numIndices()
</ins><span class="cx">     {
</span><del>-        unsigned numTmps = code.numTmps(adapterBank);
</del><ins>+        unsigned numTmps = Base::code.numTmps(adapterBank);
</ins><span class="cx">         return AbsoluteTmpMapper&lt;adapterBank&gt;::absoluteIndex(numTmps);
</span><span class="cx">     }
</span><span class="cx">     static bool acceptsBank(Bank bank) { return bank == adapterBank; }
</span><span class="lines">@@ -57,16 +137,16 @@
</span><span class="cx">     static Tmp indexToValue(unsigned index) { return AbsoluteTmpMapper&lt;adapterBank&gt;::tmpFromAbsoluteIndex(index); }
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-struct StackSlotLivenessAdapter {
</del><ins>+struct StackSlotLivenessAdapter : LivenessAdapter&lt;StackSlotLivenessAdapter&gt; {
</ins><span class="cx">     static constexpr const char* name = &quot;StackSlotLiveness&quot;;
</span><span class="cx">     typedef StackSlot* Thing;
</span><span class="cx"> 
</span><span class="cx">     StackSlotLivenessAdapter(Code&amp; code)
</span><del>-        : m_code(code)
</del><ins>+        : LivenessAdapter(code)
</ins><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    static unsigned numIndices(Code&amp; code)
</del><ins>+    unsigned numIndices()
</ins><span class="cx">     {
</span><span class="cx">         return code.stackSlots().size();
</span><span class="cx">     }
</span><span class="lines">@@ -73,336 +153,16 @@
</span><span class="cx">     static bool acceptsBank(Bank) { return true; }
</span><span class="cx">     static bool acceptsRole(Arg::Role) { return true; }
</span><span class="cx">     static unsigned valueToIndex(StackSlot* stackSlot) { return stackSlot-&gt;index(); }
</span><del>-    StackSlot* indexToValue(unsigned index) { return m_code.stackSlots()[index]; }
-
-private:
-    Code&amp; m_code;
</del><ins>+    StackSlot* indexToValue(unsigned index) { return code.stackSlots()[index]; }
</ins><span class="cx"> };
</span><span class="cx"> 
</span><del>-// HEADS UP: The algorithm here is duplicated in AirRegLiveness.h.
</del><span class="cx"> template&lt;typename Adapter&gt;
</span><del>-class Liveness : public Adapter {
-    struct Workset;
</del><ins>+class Liveness : public WTF::Liveness&lt;Adapter&gt; {
</ins><span class="cx"> public:
</span><del>-    typedef typename Adapter::Thing Thing;
-    typedef Vector&lt;unsigned, 4, UnsafeVectorOverflow&gt; IndexVector;
-    
</del><span class="cx">     Liveness(Code&amp; code)
</span><del>-        : Adapter(code)
-        , m_workset(Adapter::numIndices(code))
-        , m_liveAtHead(code.size())
-        , m_liveAtTail(code.size())
</del><ins>+        : WTF::Liveness&lt;Adapter&gt;(code.cfg(), code)
</ins><span class="cx">     {
</span><del>-        TimingScope timingScope(Adapter::name);
-        
-        // The liveAtTail of each block automatically contains the LateUse's of the terminal.
-        for (BasicBlock* block : code) {
-            IndexVector&amp; liveAtTail = m_liveAtTail[block];
-
-            block-&gt;last().forEach&lt;typename Adapter::Thing&gt;(
-                [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
-                    if (Arg::isLateUse(role)
-                        &amp;&amp; Adapter::acceptsBank(bank)
-                        &amp;&amp; Adapter::acceptsRole(role))
-                        liveAtTail.append(Adapter::valueToIndex(thing));
-                });
-            
-            std::sort(liveAtTail.begin(), liveAtTail.end());
-            removeRepeatedElements(liveAtTail);
-        }
-
-        // Blocks with new live values at tail.
-        BitVector dirtyBlocks;
-        for (size_t blockIndex = code.size(); blockIndex--;)
-            dirtyBlocks.set(blockIndex);
-        
-        IndexVector mergeBuffer;
-        
-        bool changed;
-        do {
-            changed = false;
-
-            for (size_t blockIndex = code.size(); blockIndex--;) {
-                BasicBlock* block = code[blockIndex];
-                if (!block)
-                    continue;
-
-                if (!dirtyBlocks.quickClear(blockIndex))
-                    continue;
-
-                LocalCalc localCalc(*this, block);
-                for (size_t instIndex = block-&gt;size(); instIndex--;)
-                    localCalc.execute(instIndex);
-
-                // Handle the early def's of the first instruction.
-                block-&gt;at(0).forEach&lt;typename Adapter::Thing&gt;(
-                    [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
-                        if (Arg::isEarlyDef(role)
-                            &amp;&amp; Adapter::acceptsBank(bank)
-                            &amp;&amp; Adapter::acceptsRole(role))
-                            m_workset.remove(Adapter::valueToIndex(thing));
-                    });
-
-                IndexVector&amp; liveAtHead = m_liveAtHead[block];
-
-                // We only care about Tmps that were discovered in this iteration. It is impossible
-                // to remove a live value from the head.
-                // We remove all the values we already knew about so that we only have to deal with
-                // what is new in LiveAtHead.
-                if (m_workset.size() == liveAtHead.size())
-                    m_workset.clear();
-                else {
-                    for (unsigned liveIndexAtHead : liveAtHead)
-                        m_workset.remove(liveIndexAtHead);
-                }
-
-                if (m_workset.isEmpty())
-                    continue;
-
-                liveAtHead.reserveCapacity(liveAtHead.size() + m_workset.size());
-                for (unsigned newValue : m_workset)
-                    liveAtHead.uncheckedAppend(newValue);
-                
-                m_workset.sort();
-                
-                for (BasicBlock* predecessor : block-&gt;predecessors()) {
-                    IndexVector&amp; liveAtTail = m_liveAtTail[predecessor];
-                    
-                    if (liveAtTail.isEmpty())
-                        liveAtTail = m_workset.values();
-                    else {
-                        mergeBuffer.resize(0);
-                        mergeBuffer.reserveCapacity(liveAtTail.size() + m_workset.size());
-                        auto iter = mergeDeduplicatedSorted(
-                            liveAtTail.begin(), liveAtTail.end(),
-                            m_workset.begin(), m_workset.end(),
-                            mergeBuffer.begin());
-                        mergeBuffer.resize(iter - mergeBuffer.begin());
-                        
-                        if (mergeBuffer.size() == liveAtTail.size())
-                            continue;
-                    
-                        RELEASE_ASSERT(mergeBuffer.size() &gt; liveAtTail.size());
-                        liveAtTail = mergeBuffer;
-                    }
-                    
-                    dirtyBlocks.quickSet(predecessor-&gt;index());
-                    changed = true;
-                }
-            }
-        } while (changed);
</del><span class="cx">     }
</span><del>-
-    // This calculator has to be run in reverse.
-    class LocalCalc {
-    public:
-        LocalCalc(Liveness&amp; liveness, BasicBlock* block)
-            : m_liveness(liveness)
-            , m_block(block)
-        {
-            auto&amp; workset = liveness.m_workset;
-            workset.clear();
-            IndexVector&amp; liveAtTail = liveness.m_liveAtTail[block];
-            for (unsigned index : liveAtTail)
-                workset.add(index);
-        }
-
-        class Iterable {
-        public:
-            Iterable(Liveness&amp; liveness)
-                : m_liveness(liveness)
-            {
-            }
-
-            class iterator {
-            public:
-                iterator(Adapter&amp; adapter, IndexSparseSet&lt;UnsafeVectorOverflow&gt;::const_iterator sparceSetIterator)
-                    : m_adapter(adapter)
-                    , m_sparceSetIterator(sparceSetIterator)
-                {
-                }
-
-                iterator&amp; operator++()
-                {
-                    ++m_sparceSetIterator;
-                    return *this;
-                }
-
-                typename Adapter::Thing operator*() const
-                {
-                    return m_adapter.indexToValue(*m_sparceSetIterator);
-                }
-
-                bool operator==(const iterator&amp; other) { return m_sparceSetIterator == other.m_sparceSetIterator; }
-                bool operator!=(const iterator&amp; other) { return m_sparceSetIterator != other.m_sparceSetIterator; }
-
-            private:
-                Adapter&amp; m_adapter;
-                IndexSparseSet&lt;UnsafeVectorOverflow&gt;::const_iterator m_sparceSetIterator;
-            };
-
-            iterator begin() const { return iterator(m_liveness, m_liveness.m_workset.begin()); }
-            iterator end() const { return iterator(m_liveness, m_liveness.m_workset.end()); }
-            
-            bool contains(const typename Adapter::Thing&amp; thing) const
-            {
-                return m_liveness.m_workset.contains(Adapter::valueToIndex(thing));
-            }
-
-        private:
-            Liveness&amp; m_liveness;
-        };
-
-        Iterable live() const
-        {
-            return Iterable(m_liveness);
-        }
-
-        bool isLive(const typename Adapter::Thing&amp; thing) const
-        {
-            return live().contains(thing);
-        }
-
-        void execute(unsigned instIndex)
-        {
-            Inst&amp; inst = m_block-&gt;at(instIndex);
-            auto&amp; workset = m_liveness.m_workset;
-
-            // First handle the early def's of the next instruction.
-            if (instIndex + 1 &lt; m_block-&gt;size()) {
-                Inst&amp; nextInst = m_block-&gt;at(instIndex + 1);
-                nextInst.forEach&lt;typename Adapter::Thing&gt;(
-                    [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
-                        if (Arg::isEarlyDef(role)
-                            &amp;&amp; Adapter::acceptsBank(bank)
-                            &amp;&amp; Adapter::acceptsRole(role))
-                            workset.remove(Adapter::valueToIndex(thing));
-                    });
-            }
-            
-            // Then handle def's.
-            inst.forEach&lt;typename Adapter::Thing&gt;(
-                [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
-                    if (Arg::isLateDef(role)
-                        &amp;&amp; Adapter::acceptsBank(bank)
-                        &amp;&amp; Adapter::acceptsRole(role))
-                        workset.remove(Adapter::valueToIndex(thing));
-                });
-
-            // Then handle use's.
-            inst.forEach&lt;typename Adapter::Thing&gt;(
-                [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
-                    if (Arg::isEarlyUse(role)
-                        &amp;&amp; Adapter::acceptsBank(bank)
-                        &amp;&amp; Adapter::acceptsRole(role))
-                        workset.add(Adapter::valueToIndex(thing));
-                });
-
-            // And finally, handle the late use's of the previous instruction.
-            if (instIndex) {
-                Inst&amp; prevInst = m_block-&gt;at(instIndex - 1);
-                prevInst.forEach&lt;typename Adapter::Thing&gt;(
-                    [&amp;] (typename Adapter::Thing&amp; thing, Arg::Role role, Bank bank, Width) {
-                        if (Arg::isLateUse(role)
-                            &amp;&amp; Adapter::acceptsBank(bank)
-                            &amp;&amp; Adapter::acceptsRole(role))
-                            workset.add(Adapter::valueToIndex(thing));
-                    });
-            }
-        }
-        
-    private:
-        Liveness&amp; m_liveness;
-        BasicBlock* m_block;
-    };
-
-    const IndexVector&amp; rawLiveAtHead(BasicBlock* block)
-    {
-        return m_liveAtHead[block];
-    }
-
-    template&lt;typename UnderlyingIterable&gt;
-    class Iterable {
-    public:
-        Iterable(Liveness&amp; liveness, const UnderlyingIterable&amp; iterable)
-            : m_liveness(liveness)
-            , m_iterable(iterable)
-        {
-        }
-
-        class iterator {
-        public:
-            iterator()
-                : m_liveness(nullptr)
-                , m_iter()
-            {
-            }
-            
-            iterator(Liveness&amp; liveness, typename UnderlyingIterable::const_iterator iter)
-                : m_liveness(&amp;liveness)
-                , m_iter(iter)
-            {
-            }
-
-            typename Adapter::Thing operator*()
-            {
-                return m_liveness-&gt;indexToValue(*m_iter);
-            }
-
-            iterator&amp; operator++()
-            {
-                ++m_iter;
-                return *this;
-            }
-
-            bool operator==(const iterator&amp; other) const
-            {
-                ASSERT(m_liveness == other.m_liveness);
-                return m_iter == other.m_iter;
-            }
-
-            bool operator!=(const iterator&amp; other) const
-            {
-                return !(*this == other);
-            }
-
-        private:
-            Liveness* m_liveness;
-            typename UnderlyingIterable::const_iterator m_iter;
-        };
-
-        iterator begin() const { return iterator(m_liveness, m_iterable.begin()); }
-        iterator end() const { return iterator(m_liveness, m_iterable.end()); }
-
-        bool contains(const typename Adapter::Thing&amp; thing) const
-        {
-            return m_liveness.m_workset.contains(Adapter::valueToIndex(thing));
-        }
-
-    private:
-        Liveness&amp; m_liveness;
-        const UnderlyingIterable&amp; m_iterable;
-    };
-
-    Iterable&lt;IndexVector&gt; liveAtHead(BasicBlock* block)
-    {
-        return Iterable&lt;IndexVector&gt;(*this, m_liveAtHead[block]);
-    }
-
-    Iterable&lt;IndexVector&gt; liveAtTail(BasicBlock* block)
-    {
-        return Iterable&lt;IndexVector&gt;(*this, m_liveAtTail[block]);
-    }
-
-    IndexSparseSet&lt;UnsafeVectorOverflow&gt;&amp; workset() { return m_workset; }
-
-private:
-    friend class LocalCalc;
-    friend class LocalCalc::Iterable;
-
-    IndexSparseSet&lt;UnsafeVectorOverflow&gt; m_workset;
-    IndexMap&lt;BasicBlock, IndexVector&gt; m_liveAtHead;
-    IndexMap&lt;BasicBlock, IndexVector&gt; m_liveAtTail;
</del><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> template&lt;Bank bank, Arg::Temperature minimumTemperature = Arg::Cold&gt;
</span></span></pre></div>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/WTF/ChangeLog        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -1,3 +1,56 @@
</span><ins>+2017-03-26  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        B3::fixSSA should do liveness pruning
+        https://bugs.webkit.org/show_bug.cgi?id=170111
+
+        Reviewed by Saam Barati.
+        
+        Move Air::Liveness&lt;&gt; to WTF::Liveness&lt;&gt;. This is pretty easy since Air::Liveness&lt;&gt; was
+        already fairly generic. It leverages the CFG concept so that it can understand many
+        different kinds of basic blocks. It doesn't try to understand the contents of basic
+        blocks; it just asks the adaptor for the block size and asks for the early/late
+        uses/defs of each thing in the block.
+        
+        This makes it easy to create a B3::VariableLiveness, which fixSSA then uses for
+        pruning. One of the new features is the Liveness::LiveAtHead nested class, which you
+        instantiate if you want to perform liveAtHead queries, which SSA construction wants to
+        do.
+        
+        This also fixes https://bugs.webkit.org/show_bug.cgi?id=170102#c12
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/CMakeLists.txt:
+        * wtf/Liveness.h: Added.
+        (WTF::Liveness::Liveness):
+        (WTF::Liveness::LocalCalc::LocalCalc):
+        (WTF::Liveness::LocalCalc::Iterable::Iterable):
+        (WTF::Liveness::LocalCalc::Iterable::iterator::iterator):
+        (WTF::Liveness::LocalCalc::Iterable::iterator::operator++):
+        (WTF::Liveness::LocalCalc::Iterable::iterator::operator*):
+        (WTF::Liveness::LocalCalc::Iterable::iterator::operator==):
+        (WTF::Liveness::LocalCalc::Iterable::iterator::operator!=):
+        (WTF::Liveness::LocalCalc::Iterable::begin):
+        (WTF::Liveness::LocalCalc::Iterable::end):
+        (WTF::Liveness::LocalCalc::Iterable::contains):
+        (WTF::Liveness::LocalCalc::live):
+        (WTF::Liveness::LocalCalc::isLive):
+        (WTF::Liveness::LocalCalc::execute):
+        (WTF::Liveness::rawLiveAtHead):
+        (WTF::Liveness::Iterable::Iterable):
+        (WTF::Liveness::Iterable::iterator::iterator):
+        (WTF::Liveness::Iterable::iterator::operator*):
+        (WTF::Liveness::Iterable::iterator::operator++):
+        (WTF::Liveness::Iterable::iterator::operator==):
+        (WTF::Liveness::Iterable::iterator::operator!=):
+        (WTF::Liveness::Iterable::begin):
+        (WTF::Liveness::Iterable::end):
+        (WTF::Liveness::Iterable::contains):
+        (WTF::Liveness::liveAtHead):
+        (WTF::Liveness::liveAtTail):
+        (WTF::Liveness::workset):
+        (WTF::Liveness::LiveAtHead::LiveAtHead):
+        (WTF::Liveness::LiveAtHead::isLiveAtHead):
+
</ins><span class="cx"> 2017-03-25  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Air::Liveness shouldn't need HashSets
</span></span></pre></div>
<a id="trunkSourceWTFWTFxcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -82,6 +82,7 @@
</span><span class="cx">                 0FEC84AF1BD825310080FF74 /* GraphNodeWorklist.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC84AE1BD825310080FF74 /* GraphNodeWorklist.h */; };
</span><span class="cx">                 0FEC84B11BDACD390080FF74 /* ScopedLambda.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC84B01BDACD390080FF74 /* ScopedLambda.h */; };
</span><span class="cx">                 0FED67B61B22D4D80066CE15 /* TinyPtrSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FED67B51B22D4D80066CE15 /* TinyPtrSet.h */; };
</span><ins>+                0FF4B4C51E88939C00DBBE86 /* Liveness.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF4B4C41E88939C00DBBE86 /* Liveness.h */; };
</ins><span class="cx">                 0FF860951BCCBD740045127F /* PointerComparison.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF860941BCCBD740045127F /* PointerComparison.h */; };
</span><span class="cx">                 0FFF19DC1BB334EB00886D91 /* ParallelHelperPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFF19DA1BB334EB00886D91 /* ParallelHelperPool.cpp */; };
</span><span class="cx">                 0FFF19DD1BB334EB00886D91 /* ParallelHelperPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFF19DB1BB334EB00886D91 /* ParallelHelperPool.h */; };
</span><span class="lines">@@ -467,6 +468,7 @@
</span><span class="cx">                 0FEC84AE1BD825310080FF74 /* GraphNodeWorklist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GraphNodeWorklist.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 0FEC84B01BDACD390080FF74 /* ScopedLambda.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScopedLambda.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 0FED67B51B22D4D80066CE15 /* TinyPtrSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TinyPtrSet.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                0FF4B4C41E88939C00DBBE86 /* Liveness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Liveness.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 0FF860941BCCBD740045127F /* PointerComparison.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PointerComparison.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 0FFF19DA1BB334EB00886D91 /* ParallelHelperPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParallelHelperPool.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 0FFF19DB1BB334EB00886D91 /* ParallelHelperPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParallelHelperPool.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -1027,6 +1029,7 @@
</span><span class="cx">                                 539EB0621D55284200C82EF7 /* LEBDecoder.h */,
</span><span class="cx">                                 A70DA0831799F04D00529A9B /* ListDump.h */,
</span><span class="cx">                                 A8A472C1151A825A004123FF /* ListHashSet.h */,
</span><ins>+                                0FF4B4C41E88939C00DBBE86 /* Liveness.h */,
</ins><span class="cx">                                 0FE164681B6FFC9600400E7C /* Lock.cpp */,
</span><span class="cx">                                 0FE164691B6FFC9600400E7C /* Lock.h */,
</span><span class="cx">                                 0F0FCDDD1DD167F900CCAB53 /* LockAlgorithm.h */,
</span><span class="lines">@@ -1390,6 +1393,7 @@
</span><span class="cx">                                 9BD8F40B176C2B470002D865 /* AtomicStringTable.h in Headers */,
</span><span class="cx">                                 1469419C16EAB10A0024E146 /* AutodrainedPool.h in Headers */,
</span><span class="cx">                                 0F43D8F21DB5ADDC00108FB6 /* AutomaticThread.h in Headers */,
</span><ins>+                                0FF4B4C51E88939C00DBBE86 /* Liveness.h in Headers */,
</ins><span class="cx">                                 DCEE22051CEB9869000C2396 /* BackwardsGraph.h in Headers */,
</span><span class="cx">                                 0FB14E19180FA218009B6B4D /* Bag.h in Headers */,
</span><span class="cx">                                 0FB14E1B1810E1DC009B6B4D /* BagToHashMap.h in Headers */,
</span></span></pre></div>
<a id="trunkSourceWTFwtfCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/CMakeLists.txt (214409 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/CMakeLists.txt        2017-03-26 22:11:13 UTC (rev 214409)
+++ trunk/Source/WTF/wtf/CMakeLists.txt        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -57,6 +57,7 @@
</span><span class="cx">     IteratorAdaptors.h
</span><span class="cx">     IteratorRange.h
</span><span class="cx">     ListHashSet.h
</span><ins>+    Liveness.h
</ins><span class="cx">     Lock.h
</span><span class="cx">     LockAlgorithm.h
</span><span class="cx">     LockedPrintStream.h
</span></span></pre></div>
<a id="trunkSourceWTFwtfLivenessh"></a>
<div class="addfile"><h4>Added: trunk/Source/WTF/wtf/Liveness.h (0 => 214410)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/Liveness.h                                (rev 0)
+++ trunk/Source/WTF/wtf/Liveness.h        2017-03-27 04:17:52 UTC (rev 214410)
</span><span class="lines">@@ -0,0 +1,373 @@
</span><ins>+/*
+ * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include &lt;wtf/BitVector.h&gt;
+#include &lt;wtf/IndexSparseSet.h&gt;
+#include &lt;wtf/ListDump.h&gt;
+#include &lt;wtf/StdLibExtras.h&gt;
+
+namespace WTF {
+
+// HEADS UP: The algorithm here is duplicated in AirRegLiveness.h. That one uses sets rather
+// than fancy vectors, because that's better for register liveness analysis.
+template&lt;typename Adapter&gt;
+class Liveness : public Adapter {
+public:
+    typedef typename Adapter::CFG CFG;
+    typedef typename Adapter::Thing Thing;
+    typedef Vector&lt;unsigned, 4, UnsafeVectorOverflow&gt; IndexVector;
+    
+    template&lt;typename... Arguments&gt;
+    Liveness(CFG&amp; cfg, Arguments&amp;&amp;... arguments)
+        : Adapter(std::forward&lt;Arguments&gt;(arguments)...)
+        , m_cfg(cfg)
+        , m_workset(Adapter::numIndices())
+        , m_liveAtHead(cfg.template newMap&lt;IndexVector&gt;())
+        , m_liveAtTail(cfg.template newMap&lt;IndexVector&gt;())
+    {
+        // The liveAtTail of each block automatically contains the LateUse's of the terminal.
+        for (unsigned blockIndex = m_cfg.numNodes(); blockIndex--;) {
+            typename CFG::Node block = m_cfg.node(blockIndex);
+            if (!block)
+                continue;
+            
+            IndexVector&amp; liveAtTail = m_liveAtTail[block];
+
+            Adapter::forEachLateUse(
+                block, Adapter::blockSize(block) - 1,
+                [&amp;] (unsigned index) {
+                    liveAtTail.append(index);
+                });
+            
+            std::sort(liveAtTail.begin(), liveAtTail.end());
+            removeRepeatedElements(liveAtTail);
+        }
+
+        // Blocks with new live values at tail.
+        BitVector dirtyBlocks;
+        for (size_t blockIndex = m_cfg.numNodes(); blockIndex--;)
+            dirtyBlocks.set(blockIndex);
+        
+        IndexVector mergeBuffer;
+        
+        bool changed;
+        do {
+            changed = false;
+
+            for (size_t blockIndex = m_cfg.numNodes(); blockIndex--;) {
+                typename CFG::Node block = m_cfg.node(blockIndex);
+                if (!block)
+                    continue;
+
+                if (!dirtyBlocks.quickClear(blockIndex))
+                    continue;
+
+                LocalCalc localCalc(*this, block);
+                for (size_t instIndex = Adapter::blockSize(block); instIndex--;)
+                    localCalc.execute(instIndex);
+
+                // Handle the early def's of the first instruction.
+                Adapter::forEachEarlyDef(
+                    block, 0,
+                    [&amp;] (unsigned index) {
+                        m_workset.remove(index);
+                    });
+
+                IndexVector&amp; liveAtHead = m_liveAtHead[block];
+
+                // We only care about Tmps that were discovered in this iteration. It is impossible
+                // to remove a live value from the head.
+                // We remove all the values we already knew about so that we only have to deal with
+                // what is new in LiveAtHead.
+                if (m_workset.size() == liveAtHead.size())
+                    m_workset.clear();
+                else {
+                    for (unsigned liveIndexAtHead : liveAtHead)
+                        m_workset.remove(liveIndexAtHead);
+                }
+
+                if (m_workset.isEmpty())
+                    continue;
+
+                liveAtHead.reserveCapacity(liveAtHead.size() + m_workset.size());
+                for (unsigned newValue : m_workset)
+                    liveAtHead.uncheckedAppend(newValue);
+                
+                m_workset.sort();
+                
+                for (typename CFG::Node predecessor : m_cfg.predecessors(block)) {
+                    IndexVector&amp; liveAtTail = m_liveAtTail[predecessor];
+                    
+                    if (liveAtTail.isEmpty())
+                        liveAtTail = m_workset.values();
+                    else {
+                        mergeBuffer.resize(liveAtTail.size() + m_workset.size());
+                        auto iter = mergeDeduplicatedSorted(
+                            liveAtTail.begin(), liveAtTail.end(),
+                            m_workset.begin(), m_workset.end(),
+                            mergeBuffer.begin());
+                        mergeBuffer.resize(iter - mergeBuffer.begin());
+                        
+                        if (mergeBuffer.size() == liveAtTail.size())
+                            continue;
+                    
+                        RELEASE_ASSERT(mergeBuffer.size() &gt; liveAtTail.size());
+                        liveAtTail = mergeBuffer;
+                    }
+                    
+                    dirtyBlocks.quickSet(predecessor-&gt;index());
+                    changed = true;
+                }
+            }
+        } while (changed);
+    }
+
+    // This calculator has to be run in reverse.
+    class LocalCalc {
+    public:
+        LocalCalc(Liveness&amp; liveness, typename CFG::Node block)
+            : m_liveness(liveness)
+            , m_block(block)
+        {
+            auto&amp; workset = liveness.m_workset;
+            workset.clear();
+            IndexVector&amp; liveAtTail = liveness.m_liveAtTail[block];
+            for (unsigned index : liveAtTail)
+                workset.add(index);
+        }
+
+        class Iterable {
+        public:
+            Iterable(Liveness&amp; liveness)
+                : m_liveness(liveness)
+            {
+            }
+
+            class iterator {
+            public:
+                iterator(Adapter&amp; adapter, IndexSparseSet&lt;UnsafeVectorOverflow&gt;::const_iterator sparceSetIterator)
+                    : m_adapter(adapter)
+                    , m_sparceSetIterator(sparceSetIterator)
+                {
+                }
+
+                iterator&amp; operator++()
+                {
+                    ++m_sparceSetIterator;
+                    return *this;
+                }
+
+                typename Adapter::Thing operator*() const
+                {
+                    return m_adapter.indexToValue(*m_sparceSetIterator);
+                }
+
+                bool operator==(const iterator&amp; other) { return m_sparceSetIterator == other.m_sparceSetIterator; }
+                bool operator!=(const iterator&amp; other) { return m_sparceSetIterator != other.m_sparceSetIterator; }
+
+            private:
+                Adapter&amp; m_adapter;
+                IndexSparseSet&lt;UnsafeVectorOverflow&gt;::const_iterator m_sparceSetIterator;
+            };
+
+            iterator begin() const { return iterator(m_liveness, m_liveness.m_workset.begin()); }
+            iterator end() const { return iterator(m_liveness, m_liveness.m_workset.end()); }
+            
+            bool contains(const typename Adapter::Thing&amp; thing) const
+            {
+                return m_liveness.m_workset.contains(Adapter::valueToIndex(thing));
+            }
+
+        private:
+            Liveness&amp; m_liveness;
+        };
+
+        Iterable live() const
+        {
+            return Iterable(m_liveness);
+        }
+
+        bool isLive(const typename Adapter::Thing&amp; thing) const
+        {
+            return live().contains(thing);
+        }
+
+        void execute(unsigned instIndex)
+        {
+            auto&amp; workset = m_liveness.m_workset;
+
+            // First handle the early def's of the next instruction.
+            m_liveness.forEachEarlyDef(
+                m_block, instIndex + 1,
+                [&amp;] (unsigned index) {
+                    workset.remove(index);
+                });
+            
+            // Then handle def's.
+            m_liveness.forEachLateDef(
+                m_block, instIndex,
+                [&amp;] (unsigned index) {
+                    workset.remove(index);
+                });
+
+            // Then handle use's.
+            m_liveness.forEachEarlyUse(
+                m_block, instIndex,
+                [&amp;] (unsigned index) {
+                    workset.add(index);
+                });
+
+            // And finally, handle the late use's of the previous instruction.
+            m_liveness.forEachLateUse(
+                m_block, instIndex - 1,
+                [&amp;] (unsigned index) {
+                    workset.add(index);
+                });
+        }
+        
+    private:
+        Liveness&amp; m_liveness;
+        typename CFG::Node m_block;
+    };
+
+    const IndexVector&amp; rawLiveAtHead(typename CFG::Node block)
+    {
+        return m_liveAtHead[block];
+    }
+
+    template&lt;typename UnderlyingIterable&gt;
+    class Iterable {
+    public:
+        Iterable(Liveness&amp; liveness, const UnderlyingIterable&amp; iterable)
+            : m_liveness(liveness)
+            , m_iterable(iterable)
+        {
+        }
+
+        class iterator {
+        public:
+            iterator()
+                : m_liveness(nullptr)
+                , m_iter()
+            {
+            }
+            
+            iterator(Liveness&amp; liveness, typename UnderlyingIterable::const_iterator iter)
+                : m_liveness(&amp;liveness)
+                , m_iter(iter)
+            {
+            }
+
+            typename Adapter::Thing operator*()
+            {
+                return m_liveness-&gt;indexToValue(*m_iter);
+            }
+
+            iterator&amp; operator++()
+            {
+                ++m_iter;
+                return *this;
+            }
+
+            bool operator==(const iterator&amp; other) const
+            {
+                ASSERT(m_liveness == other.m_liveness);
+                return m_iter == other.m_iter;
+            }
+
+            bool operator!=(const iterator&amp; other) const
+            {
+                return !(*this == other);
+            }
+
+        private:
+            Liveness* m_liveness;
+            typename UnderlyingIterable::const_iterator m_iter;
+        };
+
+        iterator begin() const { return iterator(m_liveness, m_iterable.begin()); }
+        iterator end() const { return iterator(m_liveness, m_iterable.end()); }
+
+        bool contains(const typename Adapter::Thing&amp; thing) const
+        {
+            return m_liveness.m_workset.contains(Adapter::valueToIndex(thing));
+        }
+
+    private:
+        Liveness&amp; m_liveness;
+        const UnderlyingIterable&amp; m_iterable;
+    };
+
+    Iterable&lt;IndexVector&gt; liveAtHead(typename CFG::Node block)
+    {
+        return Iterable&lt;IndexVector&gt;(*this, m_liveAtHead[block]);
+    }
+
+    Iterable&lt;IndexVector&gt; liveAtTail(typename CFG::Node block)
+    {
+        return Iterable&lt;IndexVector&gt;(*this, m_liveAtTail[block]);
+    }
+
+    IndexSparseSet&lt;UnsafeVectorOverflow&gt;&amp; workset() { return m_workset; }
+    
+    class LiveAtHead {
+    public:
+        LiveAtHead(Liveness&amp; liveness)
+            : m_liveness(liveness)
+        {
+            for (unsigned blockIndex = m_liveness.m_cfg.numNodes(); blockIndex--;) {
+                typename CFG::Node block = m_liveness.m_cfg.node(blockIndex);
+                if (!block)
+                    continue;
+                
+                std::sort(m_liveness.m_liveAtHead[block].begin(), m_liveness.m_liveAtHead[block].end());
+            }
+        }
+        
+        bool isLiveAtHead(typename CFG::Node block, const typename Adapter::Thing&amp; thing)
+        {
+            return !!tryBinarySearch&lt;unsigned&gt;(m_liveness.m_liveAtHead[block], m_liveness.m_liveAtHead[block].size(), m_liveness.valueToIndex(thing), [] (unsigned* value) { return *value; });
+        }
+        
+    private:
+        Liveness&amp; m_liveness;
+    };
+    
+    LiveAtHead liveAtHead() { return LiveAtHead(*this); }
+
+private:
+    friend class LocalCalc;
+    friend class LocalCalc::Iterable;
+
+    CFG&amp; m_cfg;
+    IndexSparseSet&lt;UnsafeVectorOverflow&gt; m_workset;
+    typename CFG::template Map&lt;IndexVector&gt; m_liveAtHead;
+    typename CFG::template Map&lt;IndexVector&gt; m_liveAtTail;
+};
+
+} // namespace WTF
+
</ins></span></pre>
</div>
</div>

</body>
</html>