<!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>[183787] trunk</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/183787">183787</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2015-05-04 19:40:28 -0700 (Mon, 04 May 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Large array shouldn't be slow
https://bugs.webkit.org/show_bug.cgi?id=144617

Reviewed by Geoffrey Garen.
        
PerformanceTests:

Add the hash-map benchmark to LongSpider. LongSpider was already not a perfect match of
SunSpider. It's not an official benchmark. It contains benchmarks that are relatively
long-running. So, hash-map sort of belongs here.

* LongSpider/hash-map.js: Added.
(HashMap):
(HashMap.):
(.get var):

Source/JavaScriptCore:

Decouple MIN_SPARSE_ARRAY_INDEX, which is the threshold for storing to the sparse map when
you're already using ArrayStorage mode, from the minimul array length required to use
ArrayStorage in a new Array(length) allocation.
        
Lift the array allocation length threshold to something very high. If this works, we'll
probably remove that threshold entirely.
        
This is a 27% speed-up on JetStream/hash-map. Because run-jsc-benchmarks still can't run
JetStream as a discrete suite, this adds hash-map to LongSpider so that we run it somewhere
for now.

* dfg/DFGCallArrayAllocatorSlowPathGenerator.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::LowerDFGToLLVM::compileNewArrayWithSize):
* runtime/ArrayConventions.h:
* runtime/JSArray.h:
(JSC::JSArray::create):
* runtime/JSGlobalObject.h:
(JSC::constructEmptyArray):
* tests/stress/new-array-storage-array-with-size.js: Skip this test until we fix https://bugs.webkit.org/show_bug.cgi?id=144609.

Tools:

Add the hash-map benchmark to LongSpider. LongSpider was already not a perfect match of
SunSpider. It's not an official benchmark. It contains benchmarks that are relatively
long-running. So, hash-map sort of belongs here.

* Scripts/run-jsc-benchmarks:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkPerformanceTestsChangeLog">trunk/PerformanceTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGCallArrayAllocatorSlowPathGeneratorh">trunk/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJIT32_64cpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGSpeculativeJIT64cpp">trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp">trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeArrayConventionsh">trunk/Source/JavaScriptCore/runtime/ArrayConventions.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSArrayh">trunk/Source/JavaScriptCore/runtime/JSArray.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSGlobalObjecth">trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressnewarraystoragearraywithsizejs">trunk/Source/JavaScriptCore/tests/stress/new-array-storage-array-with-size.js</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsScriptsrunjscbenchmarks">trunk/Tools/Scripts/run-jsc-benchmarks</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkPerformanceTestsLongSpiderhashmapjs">trunk/PerformanceTests/LongSpider/hash-map.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkPerformanceTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/PerformanceTests/ChangeLog (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/ChangeLog        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/PerformanceTests/ChangeLog        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2015-05-04  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        Large array shouldn't be slow
+        https://bugs.webkit.org/show_bug.cgi?id=144617
+
+        Reviewed by Geoffrey Garen.
+        
+        Add the hash-map benchmark to LongSpider. LongSpider was already not a perfect match of
+        SunSpider. It's not an official benchmark. It contains benchmarks that are relatively
+        long-running. So, hash-map sort of belongs here.
+
+        * LongSpider/hash-map.js: Added.
+        (HashMap):
+        (HashMap.):
+        (.get var):
+
</ins><span class="cx"> 2015-05-01  Dewei Zhu  &lt;dewei_zhu@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix typo bug in Speedometer/resources/main.js
</span></span></pre></div>
<a id="trunkPerformanceTestsLongSpiderhashmapjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/LongSpider/hash-map.js (0 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/LongSpider/hash-map.js                                (rev 0)
+++ trunk/PerformanceTests/LongSpider/hash-map.js        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -0,0 +1,602 @@
</span><ins>+//@ runDefault
+
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE below for additional
+ *  information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the &quot;License&quot;); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+/******* NOTICE *********
+
+Apache Harmony
+Copyright 2006, 2010 The Apache Software Foundation.
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Portions of Apache Harmony were originally developed by
+Intel Corporation and are licensed to the Apache Software
+Foundation under the &quot;Software Grant and Corporate Contribution
+License Agreement&quot; and for which the following copyright notices
+apply
+         (C) Copyright 2005 Intel Corporation
+         (C) Copyright 2005-2006 Intel Corporation
+         (C) Copyright 2006 Intel Corporation
+
+
+The following copyright notice(s) were affixed to portions of the code
+with which this file is now or was at one time distributed
+and are placed here unaltered.
+
+(C) Copyright 1997,2004 International Business Machines Corporation.
+All rights reserved.
+
+(C) Copyright IBM Corp. 2003. 
+
+
+This software contains code derived from UNIX V7, Copyright(C)
+Caldera International Inc.
+
+************************/
+
+// This code is a manual translation of Apache Harmony's HashMap class to
+// JavaScript.
+
+var HashMap = (function() {
+    var DEFAULT_SIZE = 16;
+    
+    function calculateCapacity(x)
+    {
+        if (x &gt;= 1 &lt;&lt; 30)
+            return 1 &lt;&lt; 30;
+        if (x == 0)
+            return 16;
+        x = x - 1;
+        x |= x &gt;&gt; 1;
+        x |= x &gt;&gt; 2;
+        x |= x &gt;&gt; 4;
+        x |= x &gt;&gt; 8;
+        x |= x &gt;&gt; 16;
+        return x + 1;
+    }
+    
+    function computeHashCode(key)
+    {
+        switch (typeof key) {
+        case &quot;undefined&quot;:
+            return 0;
+        case &quot;object&quot;:
+            if (!key)
+                return 0;
+        case &quot;function&quot;:
+            return key.hashCode();
+        case &quot;boolean&quot;:
+            return key | 0;
+        case &quot;number&quot;:
+            if ((key | 0) == key)
+                return key;
+            key = &quot;&quot; + key; // Compute string hash of the double. It's the best we can do.
+        case &quot;string&quot;:
+            // source: http://en.wikipedia.org/wiki/Java_hashCode()#Sample_implementations_of_the_java.lang.String_algorithm
+            var h = 0;
+            var len = key.length;
+            for (var index = 0; index &lt; len; index++) {
+                h = (((31 * h) | 0) + key.charCodeAt(index)) | 0;
+            }
+            return h;
+        default:
+            throw new Error(&quot;Internal error: Bad JavaScript value type&quot;);
+        }
+    }
+    
+    function equals(a, b)
+    {
+        if (typeof a != typeof b)
+            return false;
+        switch (typeof a) {
+        case &quot;object&quot;:
+            if (!a)
+                return !b;
+        case &quot;function&quot;:
+            switch (typeof b) {
+            case &quot;object&quot;:
+            case &quot;function&quot;:
+                return a.equals(b);
+            default:
+                return false;
+            }
+        default:
+            return a == b;
+        }
+    }
+    
+    function Entry(key, hash, value)
+    {
+        this._key = key;
+        this._value = value;
+        this._origKeyHash = hash;
+        this._next = null;
+    }
+
+    Entry.prototype = {
+        clone: function()
+        {
+            var result = new Entry(this._key, this._hash, this._value);
+            if (this._next)
+                result._next = this._next.clone();
+            return result;
+        },
+        
+        toString: function()
+        {
+            return this._key + &quot;=&quot; + this._value;
+        },
+        
+        get key()
+        {
+            return this._key;
+        },
+        
+        get value()
+        {
+            return this._value;
+        }
+    };
+    
+    function AbstractMapIterator(map)
+    {
+        this._associatedMap = map;
+        this._expectedModCount = map._modCount;
+        this._futureEntry = null;
+        this._currentEntry = null;
+        this._prevEntry = null;
+        this._position = 0;
+    }
+    
+    AbstractMapIterator.prototype = {
+        hasNext: function()
+        {
+            if (this._futureEntry)
+                return true;
+            while (this._position &lt; this._associatedMap._elementData.length) {
+                if (!this._associatedMap._elementData[this._position])
+                    this._position++;
+                else
+                    return true;
+            }
+            return false;
+        },
+        
+        _checkConcurrentMod: function()
+        {
+            if (this._expectedModCount != this._associatedMap._modCount)
+                throw new Error(&quot;Concurrent HashMap modification detected&quot;);
+        },
+        
+        _makeNext: function()
+        {
+            this._checkConcurrentMod();
+            if (!this.hasNext())
+                throw new Error(&quot;No such element&quot;);
+            if (!this._futureEntry) {
+                this._currentEntry = this._associatedMap._elementData[this._position++];
+                this._futureEntry = this._currentEntry._next;
+                this._prevEntry = null;
+                return;
+            }
+            if (this._currentEntry)
+                this._prevEntry = this._currentEntry;
+            this._currentEntry = this._futureEntry;
+            this._futureEntry = this._futureEntry._next;
+        },
+        
+        remove: function()
+        {
+            this._checkConcurrentMod();
+            if (!this._currentEntry)
+                throw new Error(&quot;Illegal state&quot;);
+            if (!this._prevEntry) {
+                var index = this._currentEntry._origKeyHash &amp; (this._associatedMap._elementData.length - 1);
+                this._associatedMap._elementData[index] = this._associatedMap._elementData[index]._next;
+            } else
+                this._prevEntry._next = this._currentEntry._next;
+            this._currentEntry = null;
+            this._expectedModCount++;
+            this._associatedMap._modCount++;
+            this._associatedMap._elementCount--;
+        }
+    };
+    
+    function EntryIterator(map)
+    {
+        AbstractMapIterator.call(this, map);
+    }
+    
+    EntryIterator.prototype = {
+        next: function()
+        {
+            this._makeNext();
+            return this._currentEntry;
+        }
+    };
+    EntryIterator.prototype.__proto__ = AbstractMapIterator.prototype;
+    
+    function KeyIterator(map)
+    {
+        AbstractMapIterator.call(this, map);
+    }
+    
+    KeyIterator.prototype = {
+        next: function()
+        {
+            this.makeNext();
+            return this._currentEntry._key;
+        }
+    };
+    KeyIterator.prototype.__proto__ = AbstractMapIterator.prototype;
+    
+    function ValueIterator(map)
+    {
+        AbstractMapIterator.call(this, map);
+    }
+    
+    ValueIterator.prototype = {
+        next: function()
+        {
+            this.makeNext();
+            return this._currentEntry._value;
+        }
+    };
+    ValueIterator.prototype.__proto__ = AbstractMapIterator.prototype;
+    
+    function EntrySet(map)
+    {
+        this._associatedMap = map;
+    }
+    
+    EntrySet.prototype = {
+        size: function()
+        {
+            return this._associatedMap._elementCount;
+        },
+        
+        clear: function()
+        {
+            this._associatedMap.clear();
+        },
+        
+        remove: function(object)
+        {
+            var entry = this._associatedMap._getEntry(object.key);
+            if (!entry)
+                return false;
+            if (!equals(entry._value, object.value))
+                return false;
+            this._associatedMap._removeEntry(entry);
+            return true;
+        },
+        
+        contains: function(object)
+        {
+            var entry = this._associatedMap._getEntry(object.key);
+            if (!entry)
+                return false;
+            return equals(entry._value, object.value);
+        },
+        
+        iterator: function()
+        {
+            return new EntryIterator(this._associatedMap);
+        }
+    };
+    
+    function KeySet(map)
+    {
+        this._associatedMap = map;
+    }
+    
+    KeySet.prototype = {
+        contains: function(object)
+        {
+            return this._associatedMap.contains(object);
+        },
+        
+        size: function()
+        {
+            return this._associatedMap._elementCount;
+        },
+        
+        clear: function()
+        {
+            this._associatedMap.clear();
+        },
+        
+        remove: function(key)
+        {
+            return !!this._associatedMap.remove(key);
+        },
+        
+        iterator: function()
+        {
+            return new KeyIterator(this._associatedMap);
+        }
+    };
+    
+    function ValueCollection(map)
+    {
+        this._associatedMap = map;
+    }
+    
+    ValueCollection.prototype = {
+        contains: function(object)
+        {
+            return this._associatedMap.containsValue(object);
+        },
+        
+        size: function()
+        {
+            return this._associatedMap._elementCount;
+        },
+        
+        clear: function()
+        {
+            this._associatedMap.clear();
+        },
+        
+        iterator: function()
+        {
+            return new ValueIterator(this._associatedMap);
+        }
+    };
+    
+    function HashMap(capacity, loadFactor)
+    {
+        if (capacity == null)
+            capacity = DEFAULT_SIZE;
+        if (loadFactor == null)
+            loadFactor = 0.75;
+        
+        if (capacity &lt; 0)
+            throw new Error(&quot;Invalid argument to HashMap constructor: capacity is negative&quot;);
+        if (loadFactor &lt;= 0)
+            throw new Error(&quot;Invalid argument to HashMap constructor: loadFactor is not positive&quot;);
+        
+        this._capacity = calculateCapacity(capacity);
+        this._elementCount = 0;
+        this._elementData = new Array(this.capacity);
+        this._loadFactor = loadFactor;
+        this._modCount = 0;
+        this._computeThreshold();
+    }
+    
+    HashMap.prototype = {
+        _computeThreshold: function()
+        {
+            this._threshold = (this._elementData.length * this._loadFactor) | 0;
+        },
+        
+        clear: function()
+        {
+            if (!this._elementCount)
+                return;
+            
+            this._elementCount = 0;
+            for (var i = this._elementData.length; i--;)
+                this._elementData[i] = null;
+            this._modCount++;
+        },
+        
+        clone: function()
+        {
+            var result = new HashMap(this._elementData.length, this._loadFactor);
+            result.putAll(this);
+            return result;
+        },
+        
+        containsKey: function(key)
+        {
+            return !!this._getEntry(key);
+        },
+        
+        containsValue: function(value)
+        {
+            for (var i = this._elementData.length; i--;) {
+                for (var entry = this._elementData[i]; entry; entry = entry._next) {
+                    if (equals(value, entry._value))
+                        return true;
+                }
+            }
+            return false;
+        },
+        
+        entrySet: function()
+        {
+            return new EntrySet(this);
+        },
+        
+        get: function(key)
+        {
+            var entry = this._getEntry(key);
+            if (entry)
+                return entry._value;
+            return null;
+        },
+        
+        _getEntry: function(key)
+        {
+            var hash = computeHashCode(key);
+            var index = hash &amp; (this._elementData.length - 1);
+            return this._findKeyEntry(key, index, hash);
+        },
+        
+        _findKeyEntry: function(key, index, keyHash)
+        {
+            var entry = this._elementData[index];
+            while (entry &amp;&amp; (entry._origKeyHash != keyHash || !equals(key, entry._key)))
+                entry = entry._next;
+            return entry;
+        },
+        
+        isEmpty: function()
+        {
+            return !this._elementCount;
+        },
+        
+        keySet: function()
+        {
+            return new KeySet(this);
+        },
+        
+        put: function(key, value)
+        {
+            var hash = computeHashCode(key);
+            var index = hash &amp; (this._elementData.length - 1);
+            var entry = this._findKeyEntry(key, index, hash);
+            if (!entry) {
+                this._modCount++;
+                entry = this._createHashedEntry(key, index, hash);
+                if (++this._elementCount &gt; this._threshold)
+                    this._rehash();
+            }
+            
+            var result = entry._value;
+            entry._value = value;
+            return result;
+        },
+        
+        _createHashedEntry: function(key, index, hash)
+        {
+            var entry = new Entry(key, hash, null);
+            entry._next = this._elementData[index];
+            this._elementData[index] = entry;
+            return entry;
+        },
+        
+        putAll: function(map)
+        {
+            if (map.isEmpty())
+                return;
+            for (var iter = map.entrySet().iterator(); iter.hasNext();) {
+                var entry = iter.next();
+                put(entry.key, entry.value);
+            }
+        },
+        
+        _rehash: function(capacity)
+        {
+            if (capacity == null)
+                capacity = this._elementData.length;
+            
+            var length = calculateCapacity(!capacity ? 1 : capacity &lt;&lt; 1);
+            var newData = new Array(length);
+            for (var i = 0; i &lt; this._elementData.length; ++i) {
+                var entry = this._elementData[i];
+                this._elementData[i] = null;
+                while (entry) {
+                    var index = entry._origKeyHash &amp; (length - 1);
+                    var next = entry._next;
+                    entry._next = newData[index];
+                    newData[index] = entry;
+                    entry = next;
+                }
+            }
+            this._elementData = newData;
+            this._computeThreshold();
+        },
+        
+        remove: function(key)
+        {
+            var entry = this._removeEntryForKey(key);
+            if (!entry)
+                return null;
+            return entry._value;
+        },
+        
+        _removeEntry: function(entry)
+        {
+            var index = entry._origKeyHash &amp; (this._elementData.length - 1);
+            var current = this._elementData[index];
+            if (current == entry)
+                this._elementData[index] = entry._next;
+            else {
+                while (current._next != entry)
+                    current = current._next;
+                current._next = entry._next;
+            }
+            this._modCount++;
+            this._elementCount--;
+        },
+        
+        _removeEntryForKey: function(key)
+        {
+            var hash = computeHashCode(key);
+            var index = hash &amp; (this._elementData.length - 1);
+            var entry = this._elementData[index];
+            var last = null;
+            while (entry != null &amp;&amp; !(entry._origKeyHash == hash &amp;&amp; equals(key, entry._key))) {
+                last = entry;
+                entry = entry._next;
+            }
+            if (!entry)
+                return null;
+            if (!last)
+                this._elementData[index] = entry._next;
+            else
+                last._next = entry._next;
+            this._modCount++;
+            this._elementCount--;
+            return entry;
+        },
+        
+        size: function()
+        {
+            return this._elementCount;
+        },
+        
+        values: function()
+        {
+            return new ValueCollection(this);
+        }
+    };
+    
+    return HashMap;
+})();
+
+var map = new HashMap();
+var COUNT = 500000;
+
+for (var i = 0; i &lt; COUNT; ++i)
+    map.put(i, 42);
+
+var result = 0;
+for (var j = 0; j &lt; 5; ++j) {
+    for (var i = 0; i &lt; COUNT; ++i)
+        result += map.get(i);
+}
+
+var keySum = 0;
+var valueSum = 0;
+for (var iterator = map.entrySet().iterator(); iterator.hasNext();) {
+    var entry = iterator.next();
+    keySum += entry.key;
+    valueSum += entry.value;
+}
+
+if (result != 105000000)
+    throw &quot;Error: result = &quot; + result;
+if (keySum != 124999750000)
+    throw &quot;Error: keySum = &quot; + keySum;
+if (valueSum != 21000000)
+    throw &quot;Error: valueSum = &quot; + valueSum;
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -1,3 +1,35 @@
</span><ins>+2015-05-04  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        Large array shouldn't be slow
+        https://bugs.webkit.org/show_bug.cgi?id=144617
+
+        Reviewed by Geoffrey Garen.
+        
+        Decouple MIN_SPARSE_ARRAY_INDEX, which is the threshold for storing to the sparse map when
+        you're already using ArrayStorage mode, from the minimul array length required to use
+        ArrayStorage in a new Array(length) allocation.
+        
+        Lift the array allocation length threshold to something very high. If this works, we'll
+        probably remove that threshold entirely.
+        
+        This is a 27% speed-up on JetStream/hash-map. Because run-jsc-benchmarks still can't run
+        JetStream as a discrete suite, this adds hash-map to LongSpider so that we run it somewhere
+        for now.
+
+        * dfg/DFGCallArrayAllocatorSlowPathGenerator.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::LowerDFGToLLVM::compileNewArrayWithSize):
+        * runtime/ArrayConventions.h:
+        * runtime/JSArray.h:
+        (JSC::JSArray::create):
+        * runtime/JSGlobalObject.h:
+        (JSC::constructEmptyArray):
+        * tests/stress/new-array-storage-array-with-size.js: Skip this test until we fix https://bugs.webkit.org/show_bug.cgi?id=144609.
+
</ins><span class="cx"> 2015-05-03  Yusuke Suzuki  &lt;utatane.tea@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Add backed intrinsics to private functions exposed with private symbols in global object
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGCallArrayAllocatorSlowPathGeneratorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -96,7 +96,7 @@
</span><span class="cx">         for (unsigned i = 0; i &lt; m_plans.size(); ++i)
</span><span class="cx">             jit-&gt;silentSpill(m_plans[i]);
</span><span class="cx">         GPRReg scratchGPR = AssemblyHelpers::selectScratchGPR(m_sizeGPR);
</span><del>-        MacroAssembler::Jump bigLength = jit-&gt;m_jit.branch32(MacroAssembler::AboveOrEqual, m_sizeGPR, MacroAssembler::TrustedImm32(MIN_SPARSE_ARRAY_INDEX));
</del><ins>+        MacroAssembler::Jump bigLength = jit-&gt;m_jit.branch32(MacroAssembler::AboveOrEqual, m_sizeGPR, MacroAssembler::TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH));
</ins><span class="cx">         jit-&gt;m_jit.move(MacroAssembler::TrustedImmPtr(m_contiguousStructure), scratchGPR);
</span><span class="cx">         MacroAssembler::Jump done = jit-&gt;m_jit.jump();
</span><span class="cx">         bigLength.link(&amp;jit-&gt;m_jit);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJIT32_64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -3317,7 +3317,7 @@
</span><span class="cx">             GPRReg scratch2GPR = scratch2.gpr();
</span><span class="cx">             
</span><span class="cx">             MacroAssembler::JumpList slowCases;
</span><del>-            slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX)));
</del><ins>+            slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)));
</ins><span class="cx">             
</span><span class="cx">             ASSERT((1 &lt;&lt; 3) == sizeof(JSValue));
</span><span class="cx">             m_jit.move(sizeGPR, scratchGPR);
</span><span class="lines">@@ -3361,7 +3361,7 @@
</span><span class="cx">         GPRFlushedCallResult result(this);
</span><span class="cx">         GPRReg resultGPR = result.gpr();
</span><span class="cx">         GPRReg structureGPR = selectScratchGPR(sizeGPR);
</span><del>-        MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX));
</del><ins>+        MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH));
</ins><span class="cx">         m_jit.move(TrustedImmPtr(globalObject-&gt;arrayStructureForIndexingTypeDuringAllocation(node-&gt;indexingType())), structureGPR);
</span><span class="cx">         MacroAssembler::Jump done = m_jit.jump();
</span><span class="cx">         bigLength.link(&amp;m_jit);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGSpeculativeJIT64cpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -3408,7 +3408,7 @@
</span><span class="cx">             GPRReg scratch2GPR = scratch2.gpr();
</span><span class="cx">             
</span><span class="cx">             MacroAssembler::JumpList slowCases;
</span><del>-            slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX)));
</del><ins>+            slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)));
</ins><span class="cx">             
</span><span class="cx">             ASSERT((1 &lt;&lt; 3) == sizeof(JSValue));
</span><span class="cx">             m_jit.move(sizeGPR, scratchGPR);
</span><span class="lines">@@ -3450,7 +3450,7 @@
</span><span class="cx">         GPRFlushedCallResult result(this);
</span><span class="cx">         GPRReg resultGPR = result.gpr();
</span><span class="cx">         GPRReg structureGPR = selectScratchGPR(sizeGPR);
</span><del>-        MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX));
</del><ins>+        MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH));
</ins><span class="cx">         m_jit.move(TrustedImmPtr(globalObject-&gt;arrayStructureForIndexingTypeDuringAllocation(node-&gt;indexingType())), structureGPR);
</span><span class="cx">         MacroAssembler::Jump done = m_jit.jump();
</span><span class="cx">         bigLength.link(&amp;m_jit);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreftlFTLLowerDFGToLLVMcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -3249,7 +3249,7 @@
</span><span class="cx">             LBasicBlock continuation = FTL_NEW_BLOCK(m_out, (&quot;NewArrayWithSize continuation&quot;));
</span><span class="cx">             
</span><span class="cx">             m_out.branch(
</span><del>-                m_out.aboveOrEqual(publicLength, m_out.constInt32(MIN_SPARSE_ARRAY_INDEX)),
</del><ins>+                m_out.aboveOrEqual(publicLength, m_out.constInt32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)),
</ins><span class="cx">                 rarely(largeCase), usually(fastCase));
</span><span class="cx"> 
</span><span class="cx">             LBasicBlock lastNext = m_out.appendTo(fastCase, largeCase);
</span><span class="lines">@@ -3325,7 +3325,7 @@
</span><span class="cx">         }
</span><span class="cx">         
</span><span class="cx">         LValue structureValue = m_out.select(
</span><del>-            m_out.aboveOrEqual(publicLength, m_out.constInt32(MIN_SPARSE_ARRAY_INDEX)),
</del><ins>+            m_out.aboveOrEqual(publicLength, m_out.constInt32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)),
</ins><span class="cx">             m_out.constIntPtr(
</span><span class="cx">                 globalObject-&gt;arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage)),
</span><span class="cx">             m_out.constIntPtr(structure));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeArrayConventionsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/ArrayConventions.h (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/ArrayConventions.h        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Source/JavaScriptCore/runtime/ArrayConventions.h        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -58,7 +58,14 @@
</span><span class="cx"> 
</span><span class="cx"> // These values have to be macros to be used in max() and min() without introducing
</span><span class="cx"> // a PIC branch in Mach-O binaries, see &lt;rdar://problem/5971391&gt;.
</span><ins>+
+// If you grow an ArrayStorage array by more than this, then the array will go sparse. Note that we
+// could probably make this smaller (it's large because it used to be conflated with
+// MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH).
</ins><span class="cx"> #define MIN_SPARSE_ARRAY_INDEX 100000U
</span><ins>+// If you try to allocate a contiguous array larger than this, then we will allocate an ArrayStorage
+// array instead. We allow for an array that occupies 1GB of VM.
+#define MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH 1024 * 1024 * 1024 / 8
</ins><span class="cx"> #define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
</span><span class="cx"> // 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
</span><span class="cx"> #define MAX_ARRAY_INDEX 0xFFFFFFFEU
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSArrayh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSArray.h (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSArray.h        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Source/JavaScriptCore/runtime/JSArray.h        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -200,7 +200,7 @@
</span><span class="cx">             || hasContiguous(structure-&gt;indexingType()));
</span><span class="cx">         unsigned vectorLength;
</span><span class="cx">         butterfly = createContiguousArrayButterfly(vm, 0, initialLength, vectorLength);
</span><del>-        ASSERT(initialLength &lt; MIN_SPARSE_ARRAY_INDEX);
</del><ins>+        ASSERT(initialLength &lt; MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH);
</ins><span class="cx">         if (hasDouble(structure-&gt;indexingType())) {
</span><span class="cx">             for (unsigned i = 0; i &lt; vectorLength; ++i)
</span><span class="cx">                 butterfly-&gt;contiguousDouble()[i] = PNaN;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSGlobalObjecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -672,7 +672,7 @@
</span><span class="cx"> 
</span><span class="cx"> inline JSArray* constructEmptyArray(ExecState* exec, ArrayAllocationProfile* profile, JSGlobalObject* globalObject, unsigned initialLength = 0)
</span><span class="cx"> {
</span><del>-    return ArrayAllocationProfile::updateLastAllocationFor(profile, JSArray::create(exec-&gt;vm(), initialLength &gt;= MIN_SPARSE_ARRAY_INDEX ? globalObject-&gt;arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage) : globalObject-&gt;arrayStructureForProfileDuringAllocation(profile), initialLength));
</del><ins>+    return ArrayAllocationProfile::updateLastAllocationFor(profile, JSArray::create(exec-&gt;vm(), initialLength &gt;= MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH ? globalObject-&gt;arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage) : globalObject-&gt;arrayStructureForProfileDuringAllocation(profile), initialLength));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> inline JSArray* constructEmptyArray(ExecState* exec, ArrayAllocationProfile* profile, unsigned initialLength = 0)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressnewarraystoragearraywithsizejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tests/stress/new-array-storage-array-with-size.js (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/new-array-storage-array-with-size.js        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Source/JavaScriptCore/tests/stress/new-array-storage-array-with-size.js        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -1,3 +1,6 @@
</span><ins>+// https://bugs.webkit.org/show_bug.cgi?id=144609
+//@ skip
+
</ins><span class="cx"> function foo(x) {
</span><span class="cx">     return new Array(x);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Tools/ChangeLog        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2015-05-04  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        Large array shouldn't be slow
+        https://bugs.webkit.org/show_bug.cgi?id=144617
+
+        Reviewed by Geoffrey Garen.
+        
+        Add the hash-map benchmark to LongSpider. LongSpider was already not a perfect match of
+        SunSpider. It's not an official benchmark. It contains benchmarks that are relatively
+        long-running. So, hash-map sort of belongs here.
+
+        * Scripts/run-jsc-benchmarks:
+
</ins><span class="cx"> 2015-05-04  Doug Russell  &lt;d_russell@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         AX: setting focus via accessibility object needs to set isSynchronizing in resulting selection intent
</span></span></pre></div>
<a id="trunkToolsScriptsrunjscbenchmarks"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/run-jsc-benchmarks (183786 => 183787)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/run-jsc-benchmarks        2015-05-05 01:00:49 UTC (rev 183786)
+++ trunk/Tools/Scripts/run-jsc-benchmarks        2015-05-05 02:40:28 UTC (rev 183787)
</span><span class="lines">@@ -2897,7 +2897,7 @@
</span><span class="cx">    &quot;access-fannkuch&quot;, &quot;access-nbody&quot;, &quot;access-nsieve&quot;,
</span><span class="cx">    &quot;bitops-3bit-bits-in-byte&quot;, &quot;bitops-bits-in-byte&quot;, &quot;bitops-nsieve-bits&quot;,
</span><span class="cx">    &quot;controlflow-recursive&quot;, &quot;crypto-aes&quot;, &quot;crypto-md5&quot;, &quot;crypto-sha1&quot;,
</span><del>-   &quot;date-format-tofte&quot;, &quot;date-format-xparb&quot;, &quot;math-cordic&quot;,
</del><ins>+   &quot;date-format-tofte&quot;, &quot;date-format-xparb&quot;, &quot;hash-map&quot;, &quot;math-cordic&quot;,
</ins><span class="cx">    &quot;math-partial-sums&quot;, &quot;math-spectral-norm&quot;, &quot;string-base64&quot;,
</span><span class="cx">    &quot;string-fasta&quot;, &quot;string-tagcloud&quot;].each {
</span><span class="cx">     | name |
</span></span></pre>
</div>
</div>

</body>
</html>