Title: [168635] branches/ftlopt
Revision
168635
Author
fpi...@apple.com
Date
2014-05-12 12:23:47 -0700 (Mon, 12 May 2014)

Log Message

jsSubstring() should be lazy
https://bugs.webkit.org/show_bug.cgi?id=132556

Reviewed by Andreas Kling.
        

Source/_javascript_Core: 
jsSubstring() is now lazy by using a special rope that is a substring instead of a
concatenation. To make this patch super simple, we require that a substring's base is
never a rope. Hence, when resolving a rope, we either go down a non-recursive substring
path, or we go down a concatenation path which may see exactly one level of substrings in
its fibers.
        
This is up to a 50% speed-up on microbenchmarks and a 10% speed-up on Octane/regexp.
        
Relanding this with assertion fixes.

* heap/MarkedBlock.cpp:
(JSC::MarkedBlock::specializedSweep):
* runtime/JSString.cpp:
(JSC::JSRopeString::visitFibers):
(JSC::JSRopeString::resolveRopeInternal8):
(JSC::JSRopeString::resolveRopeInternal16):
(JSC::JSRopeString::clearFibers):
(JSC::JSRopeString::resolveRope):
(JSC::JSRopeString::resolveRopeSlowCase8):
(JSC::JSRopeString::resolveRopeSlowCase):
* runtime/JSString.h:
(JSC::JSRopeString::finishCreation):
(JSC::JSRopeString::append):
(JSC::JSRopeString::create):
(JSC::JSRopeString::offsetOfFibers):
(JSC::JSRopeString::fiber):
(JSC::JSRopeString::substringBase):
(JSC::JSRopeString::substringOffset):
(JSC::JSRopeString::notSubstringSentinel):
(JSC::JSRopeString::substringSentinel):
(JSC::JSRopeString::isSubstring):
(JSC::JSRopeString::setIsSubstring):
(JSC::jsSubstring):
* runtime/RegExpMatchesArray.cpp:
(JSC::RegExpMatchesArray::reifyAllProperties):
* runtime/StringPrototype.cpp:
(JSC::stringProtoFuncSubstring):

LayoutTests: 
These tests get 35-50% faster.

* js/regress/script-tests/substring-concat-weird.js: Added.
(foo):
* js/regress/script-tests/substring-concat.js: Added.
(foo):
* js/regress/script-tests/substring.js: Added.
(foo):
* js/regress/substring-concat-expected.txt: Added.
* js/regress/substring-concat-weird-expected.txt: Added.
* js/regress/substring-concat-weird.html: Added.
* js/regress/substring-concat.html: Added.
* js/regress/substring-expected.txt: Added.
* js/regress/substring.html: Added.

Modified Paths

Added Paths

Diff

Modified: branches/ftlopt/LayoutTests/ChangeLog (168634 => 168635)


--- branches/ftlopt/LayoutTests/ChangeLog	2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/LayoutTests/ChangeLog	2014-05-12 19:23:47 UTC (rev 168635)
@@ -1,3 +1,25 @@
+2014-05-08  Filip Pizlo  <fpi...@apple.com>
+
+        jsSubstring() should be lazy
+        https://bugs.webkit.org/show_bug.cgi?id=132556
+
+        Reviewed by Andreas Kling.
+        
+        These tests get 35-50% faster.
+
+        * js/regress/script-tests/substring-concat-weird.js: Added.
+        (foo):
+        * js/regress/script-tests/substring-concat.js: Added.
+        (foo):
+        * js/regress/script-tests/substring.js: Added.
+        (foo):
+        * js/regress/substring-concat-expected.txt: Added.
+        * js/regress/substring-concat-weird-expected.txt: Added.
+        * js/regress/substring-concat-weird.html: Added.
+        * js/regress/substring-concat.html: Added.
+        * js/regress/substring-expected.txt: Added.
+        * js/regress/substring.html: Added.
+
 2014-05-08  Commit Queue  <commit-qu...@webkit.org>
 
         Unreviewed, rolling out r168498.

Added: branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat-weird.js (0 => 168635)


--- branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat-weird.js	                        (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat-weird.js	2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,18 @@
+function foo() {
+    return a + b;
+}
+
+var array = Array(10007);
+
+var string = foo.toString();
+
+for (var i = 0; i < 500000; ++i) {
+    array[i % array.length] = "foo " + string.substring(i % string.length, (i / string.length) % string.length) + " bar";
+    array[i % array.length] = "this " + array[i % array.length] + " that";
+}
+
+for (var i = 0; i < array.length; ++i) {
+    var thing = array[i].substring(9, array[i].length - 9);
+    if (string.indexOf(thing) < 0)
+        throw "Error: bad substring: " + thing;
+}

Added: branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat.js (0 => 168635)


--- branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat.js	                        (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/script-tests/substring-concat.js	2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,17 @@
+function foo() {
+    return a + b;
+}
+
+var array = Array(10007);
+
+var string = foo.toString();
+
+for (var i = 0; i < 700000; ++i) {
+    array[i % array.length] = "foo " + string.substring(i % string.length, (i / string.length) % string.length) + " bar";
+}
+
+for (var i = 0; i < array.length; ++i) {
+    var thing = array[i].substring(4, array[i].length - 4);
+    if (string.indexOf(thing) < 0)
+        throw "Error: bad substring: \"" + thing + "\"";
+}

Added: branches/ftlopt/LayoutTests/js/regress/script-tests/substring.js (0 => 168635)


--- branches/ftlopt/LayoutTests/js/regress/script-tests/substring.js	                        (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/script-tests/substring.js	2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,16 @@
+function foo() {
+    return a + b;
+}
+
+var array = Array(10007);
+
+var string = foo.toString();
+
+for (var i = 0; i < 1000000; ++i) {
+    array[i % array.length] = string.substring(i % string.length, (i / string.length) % string.length);
+}
+
+for (var i = 0; i < array.length; ++i) {
+    if (string.indexOf(array[i]) < 0)
+        throw "Error: bad substring: " + array[i];
+}

Added: branches/ftlopt/LayoutTests/js/regress/substring-concat-expected.txt (0 => 168635)


--- branches/ftlopt/LayoutTests/js/regress/substring-concat-expected.txt	                        (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-concat-expected.txt	2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,10 @@
+JSRegress/substring-concat
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: branches/ftlopt/LayoutTests/js/regress/substring-concat-weird-expected.txt (0 => 168635)


--- branches/ftlopt/LayoutTests/js/regress/substring-concat-weird-expected.txt	                        (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-concat-weird-expected.txt	2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,10 @@
+JSRegress/substring-concat-weird
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: branches/ftlopt/LayoutTests/js/regress/substring-concat-weird.html (0 => 168635)


--- branches/ftlopt/LayoutTests/js/regress/substring-concat-weird.html	                        (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-concat-weird.html	2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: branches/ftlopt/LayoutTests/js/regress/substring-concat.html (0 => 168635)


--- branches/ftlopt/LayoutTests/js/regress/substring-concat.html	                        (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-concat.html	2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Added: branches/ftlopt/LayoutTests/js/regress/substring-expected.txt (0 => 168635)


--- branches/ftlopt/LayoutTests/js/regress/substring-expected.txt	                        (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring-expected.txt	2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,10 @@
+JSRegress/substring
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: branches/ftlopt/LayoutTests/js/regress/substring.html (0 => 168635)


--- branches/ftlopt/LayoutTests/js/regress/substring.html	                        (rev 0)
+++ branches/ftlopt/LayoutTests/js/regress/substring.html	2014-05-12 19:23:47 UTC (rev 168635)
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+</body>
+</html>

Modified: branches/ftlopt/Source/_javascript_Core/ChangeLog (168634 => 168635)


--- branches/ftlopt/Source/_javascript_Core/ChangeLog	2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/ChangeLog	2014-05-12 19:23:47 UTC (rev 168635)
@@ -1,3 +1,48 @@
+2014-05-08  Filip Pizlo  <fpi...@apple.com>
+
+        jsSubstring() should be lazy
+        https://bugs.webkit.org/show_bug.cgi?id=132556
+
+        Reviewed by Andreas Kling.
+        
+        jsSubstring() is now lazy by using a special rope that is a substring instead of a
+        concatenation. To make this patch super simple, we require that a substring's base is
+        never a rope. Hence, when resolving a rope, we either go down a non-recursive substring
+        path, or we go down a concatenation path which may see exactly one level of substrings in
+        its fibers.
+        
+        This is up to a 50% speed-up on microbenchmarks and a 10% speed-up on Octane/regexp.
+        
+        Relanding this with assertion fixes.
+
+        * heap/MarkedBlock.cpp:
+        (JSC::MarkedBlock::specializedSweep):
+        * runtime/JSString.cpp:
+        (JSC::JSRopeString::visitFibers):
+        (JSC::JSRopeString::resolveRopeInternal8):
+        (JSC::JSRopeString::resolveRopeInternal16):
+        (JSC::JSRopeString::clearFibers):
+        (JSC::JSRopeString::resolveRope):
+        (JSC::JSRopeString::resolveRopeSlowCase8):
+        (JSC::JSRopeString::resolveRopeSlowCase):
+        * runtime/JSString.h:
+        (JSC::JSRopeString::finishCreation):
+        (JSC::JSRopeString::append):
+        (JSC::JSRopeString::create):
+        (JSC::JSRopeString::offsetOfFibers):
+        (JSC::JSRopeString::fiber):
+        (JSC::JSRopeString::substringBase):
+        (JSC::JSRopeString::substringOffset):
+        (JSC::JSRopeString::notSubstringSentinel):
+        (JSC::JSRopeString::substringSentinel):
+        (JSC::JSRopeString::isSubstring):
+        (JSC::JSRopeString::setIsSubstring):
+        (JSC::jsSubstring):
+        * runtime/RegExpMatchesArray.cpp:
+        (JSC::RegExpMatchesArray::reifyAllProperties):
+        * runtime/StringPrototype.cpp:
+        (JSC::stringProtoFuncSubstring):
+
 2014-05-08  Mark Hahnenberg  <mhahnenb...@apple.com>
 
         Base case for get-by-id inline cache doesn't check for HasImpureGetOwnPropertySlot

Modified: branches/ftlopt/Source/_javascript_Core/heap/MarkedBlock.cpp (168634 => 168635)


--- branches/ftlopt/Source/_javascript_Core/heap/MarkedBlock.cpp	2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/heap/MarkedBlock.cpp	2014-05-12 19:23:47 UTC (rev 168635)
@@ -70,6 +70,8 @@
     ASSERT(blockState != Allocated && blockState != FreeListed);
     ASSERT(!(dtorType == MarkedBlock::None && sweepMode == SweepOnly));
 
+    SamplingRegion samplingRegion((dtorType != MarkedBlock::None && blockState != New) ? "Calling destructors" : "sweeping");
+    
     // This produces a free list that is ordered in reverse through the block.
     // This is fine, since the allocation code makes no assumptions about the
     // order of the free list.

Modified: branches/ftlopt/Source/_javascript_Core/runtime/JSString.cpp (168634 => 168635)


--- branches/ftlopt/Source/_javascript_Core/runtime/JSString.cpp	2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/runtime/JSString.cpp	2014-05-12 19:23:47 UTC (rev 168635)
@@ -82,24 +82,39 @@
 
 void JSRopeString::visitFibers(SlotVisitor& visitor)
 {
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
-        visitor.append(&m_fibers[i]);
+    if (isSubstring()) {
+        visitor.append(&substringBase());
+        return;
+    }
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
+        visitor.append(&fiber(i));
 }
 
 static const unsigned maxLengthForOnStackResolve = 2048;
 
 void JSRopeString::resolveRopeInternal8(LChar* buffer) const
 {
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
-        if (m_fibers[i]->isRope()) {
+    if (isSubstring()) {
+        StringImpl::copyChars(
+            buffer, substringBase()->m_value.characters8() + substringOffset(), m_length);
+        return;
+    }
+    
+    resolveRopeInternal8NoSubstring(buffer);
+}
+
+void JSRopeString::resolveRopeInternal8NoSubstring(LChar* buffer) const
+{
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        if (fiber(i)->isRope()) {
             resolveRopeSlowCase8(buffer);
             return;
         }
     }
 
     LChar* position = buffer;
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
-        const StringImpl& fiberString = *m_fibers[i]->m_value.impl();
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        const StringImpl& fiberString = *fiber(i)->m_value.impl();
         unsigned length = fiberString.length();
         StringImpl::copyChars(position, fiberString.characters8(), length);
         position += length;
@@ -109,16 +124,27 @@
 
 void JSRopeString::resolveRopeInternal16(UChar* buffer) const
 {
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
-        if (m_fibers[i]->isRope()) {
+    if (isSubstring()) {
+        StringImpl::copyChars(
+            buffer, substringBase()->m_value.characters16() + substringOffset(), m_length);
+        return;
+    }
+    
+    resolveRopeInternal16NoSubstring(buffer);
+}
+
+void JSRopeString::resolveRopeInternal16NoSubstring(UChar* buffer) const
+{
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        if (fiber(i)->isRope()) {
             resolveRopeSlowCase(buffer);
             return;
         }
     }
 
     UChar* position = buffer;
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i) {
-        const StringImpl& fiberString = *m_fibers[i]->m_value.impl();
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i) {
+        const StringImpl& fiberString = *fiber(i)->m_value.impl();
         unsigned length = fiberString.length();
         if (fiberString.is8Bit())
             StringImpl::copyChars(position, fiberString.characters8(), length);
@@ -156,8 +182,8 @@
 
 void JSRopeString::clearFibers() const
 {
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
-        m_fibers[i].clear();
+    for (size_t i = 0; i < s_maxInternalRopeLength; ++i)
+        u[i].number = 0;
 }
 
 AtomicStringImpl* JSRopeString::resolveRopeToExistingAtomicString(ExecState* exec) const
@@ -171,7 +197,7 @@
         }
         return nullptr;
     }
-
+    
     if (is8Bit()) {
         LChar buffer[maxLengthForOnStackResolve];
         resolveRopeInternal8(buffer);
@@ -196,7 +222,14 @@
 void JSRopeString::resolveRope(ExecState* exec) const
 {
     ASSERT(isRope());
-
+    
+    if (isSubstring()) {
+        ASSERT(!substringBase()->isRope());
+        m_value = substringBase()->m_value.substring(substringOffset(), m_length);
+        substringBase().clear();
+        return;
+    }
+    
     if (is8Bit()) {
         LChar* buffer;
         if (RefPtr<StringImpl> newImpl = StringImpl::tryCreateUninitialized(m_length, buffer)) {
@@ -206,7 +239,7 @@
             outOfMemory(exec);
             return;
         }
-        resolveRopeInternal8(buffer);
+        resolveRopeInternal8NoSubstring(buffer);
         clearFibers();
         ASSERT(!isRope());
         return;
@@ -221,7 +254,7 @@
         return;
     }
 
-    resolveRopeInternal16(buffer);
+    resolveRopeInternal16NoSubstring(buffer);
     clearFibers();
     ASSERT(!isRope());
 }
@@ -241,24 +274,32 @@
     LChar* position = buffer + m_length; // We will be working backwards over the rope.
     Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // Putting strings into a Vector is only OK because there are no GC points in this method.
     
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
-        workQueue.append(m_fibers[i].get());
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
+        workQueue.append(fiber(i).get());
 
     while (!workQueue.isEmpty()) {
         JSString* currentFiber = workQueue.last();
         workQueue.removeLast();
 
+        const LChar* characters;
+        
         if (currentFiber->isRope()) {
             JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber);
-            for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->m_fibers[i]; ++i)
-                workQueue.append(currentFiberAsRope->m_fibers[i].get());
-            continue;
-        }
-
-        StringImpl* string = static_cast<StringImpl*>(currentFiber->m_value.impl());
-        unsigned length = string->length();
+            if (!currentFiberAsRope->isSubstring()) {
+                for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i)
+                    workQueue.append(currentFiberAsRope->fiber(i).get());
+                continue;
+            }
+            ASSERT(!currentFiberAsRope->substringBase()->isRope());
+            characters =
+                currentFiberAsRope->substringBase()->m_value.characters8() +
+                currentFiberAsRope->substringOffset();
+        } else
+            characters = currentFiber->m_value.characters8();
+        
+        unsigned length = currentFiber->length();
         position -= length;
-        StringImpl::copyChars(position, string->characters8(), length);
+        StringImpl::copyChars(position, characters, length);
     }
 
     ASSERT(buffer == position);
@@ -269,8 +310,8 @@
     UChar* position = buffer + m_length; // We will be working backwards over the rope.
     Vector<JSString*, 32, UnsafeVectorOverflow> workQueue; // These strings are kept alive by the parent rope, so using a Vector is OK.
 
-    for (size_t i = 0; i < s_maxInternalRopeLength && m_fibers[i]; ++i)
-        workQueue.append(m_fibers[i].get());
+    for (size_t i = 0; i < s_maxInternalRopeLength && fiber(i); ++i)
+        workQueue.append(fiber(i).get());
 
     while (!workQueue.isEmpty()) {
         JSString* currentFiber = workQueue.last();
@@ -278,8 +319,21 @@
 
         if (currentFiber->isRope()) {
             JSRopeString* currentFiberAsRope = static_cast<JSRopeString*>(currentFiber);
-            for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->m_fibers[i]; ++i)
-                workQueue.append(currentFiberAsRope->m_fibers[i].get());
+            if (currentFiberAsRope->isSubstring()) {
+                ASSERT(!currentFiberAsRope->substringBase()->isRope());
+                StringImpl* string = static_cast<StringImpl*>(
+                    currentFiberAsRope->substringBase()->m_value.impl());
+                unsigned offset = currentFiberAsRope->substringOffset();
+                unsigned length = currentFiberAsRope->length();
+                position -= length;
+                if (string->is8Bit())
+                    StringImpl::copyChars(position, string->characters8() + offset, length);
+                else
+                    StringImpl::copyChars(position, string->characters16() + offset, length);
+                continue;
+            }
+            for (size_t i = 0; i < s_maxInternalRopeLength && currentFiberAsRope->fiber(i); ++i)
+                workQueue.append(currentFiberAsRope->fiber(i).get());
             continue;
         }
 

Modified: branches/ftlopt/Source/_javascript_Core/runtime/JSString.h (168634 => 168635)


--- branches/ftlopt/Source/_javascript_Core/runtime/JSString.h	2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/runtime/JSString.h	2014-05-12 19:23:47 UTC (rev 168635)
@@ -273,8 +273,10 @@
             Base::finishCreation(vm);
             m_length = s1->length() + s2->length();
             setIs8Bit(s1->is8Bit() && s2->is8Bit());
-            m_fibers[0].set(vm, this, s1);
-            m_fibers[1].set(vm, this, s2);
+            setIsSubstring(false);
+            fiber(0).set(vm, this, s1);
+            fiber(1).set(vm, this, s2);
+            fiber(2).clear();
         }
             
         void finishCreation(VM& vm, JSString* s1, JSString* s2, JSString* s3)
@@ -282,19 +284,37 @@
             Base::finishCreation(vm);
             m_length = s1->length() + s2->length() + s3->length();
             setIs8Bit(s1->is8Bit() && s2->is8Bit() &&  s3->is8Bit());
-            m_fibers[0].set(vm, this, s1);
-            m_fibers[1].set(vm, this, s2);
-            m_fibers[2].set(vm, this, s3);
+            setIsSubstring(false);
+            fiber(0).set(vm, this, s1);
+            fiber(1).set(vm, this, s2);
+            fiber(2).set(vm, this, s3);
         }
+        
+        void finishCreation(VM& vm, JSString* base, unsigned offset, unsigned length)
+        {
+            Base::finishCreation(vm);
+            ASSERT(!base->isRope());
+            ASSERT(!sumOverflows<int32_t>(offset, length));
+            ASSERT(offset + length <= base->length());
+            m_length = length;
+            setIs8Bit(base->is8Bit());
+            setIsSubstring(true);
+            substringBase().set(vm, this, base);
+            substringOffset() = offset;
+        }
 
         void finishCreation(VM& vm)
         {
             JSString::finishCreation(vm);
+            setIsSubstring(false);
+            fiber(0).clear();
+            fiber(1).clear();
+            fiber(2).clear();
         }
 
         void append(VM& vm, size_t index, JSString* jsString)
         {
-            m_fibers[index].set(vm, this, jsString);
+            fiber(index).set(vm, this, jsString);
             m_length += jsString->m_length;
             RELEASE_ASSERT(static_cast<int32_t>(m_length) >= 0);
             setIs8Bit(is8Bit() && jsString->is8Bit());
@@ -320,10 +340,17 @@
             newString->finishCreation(vm, s1, s2, s3);
             return newString;
         }
+        
+        static JSString* create(VM& vm, JSString* base, unsigned offset, unsigned length)
+        {
+            JSRopeString* newString = new (NotNull, allocateCell<JSRopeString>(vm.heap)) JSRopeString(vm);
+            newString->finishCreation(vm, base, offset, length);
+            return newString;
+        }
 
         void visitFibers(SlotVisitor&);
             
-        static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, m_fibers); }
+        static ptrdiff_t offsetOfFibers() { return OBJECT_OFFSETOF(JSRopeString, u); }
 
         static const unsigned s_maxInternalRopeLength = 3;
             
@@ -338,12 +365,54 @@
         void resolveRopeSlowCase(UChar*) const;
         void outOfMemory(ExecState*) const;
         void resolveRopeInternal8(LChar*) const;
+        void resolveRopeInternal8NoSubstring(LChar*) const;
         void resolveRopeInternal16(UChar*) const;
+        void resolveRopeInternal16NoSubstring(UChar*) const;
         void clearFibers() const;
             
         JS_EXPORT_PRIVATE JSString* getIndexSlowCase(ExecState*, unsigned);
+        
+        WriteBarrierBase<JSString>& fiber(unsigned i) const
+        {
+            ASSERT(!isSubstring());
+            ASSERT(i < s_maxInternalRopeLength);
+            return u[i].string;
+        }
+        
+        WriteBarrierBase<JSString>& substringBase() const
+        {
+            return u[1].string;
+        }
+        
+        uintptr_t& substringOffset() const
+        {
+            return u[2].number;
+        }
+        
+        static uintptr_t notSubstringSentinel()
+        {
+            return 0;
+        }
+        
+        static uintptr_t substringSentinel()
+        {
+            return 1;
+        }
+        
+        bool isSubstring() const
+        {
+            return u[0].number == substringSentinel();
+        }
+        
+        void setIsSubstring(bool isSubstring)
+        {
+            u[0].number = isSubstring ? substringSentinel() : notSubstringSentinel();
+        }
 
-        mutable std::array<WriteBarrier<JSString>, s_maxInternalRopeLength> m_fibers;
+        mutable union {
+            uintptr_t number;
+            WriteBarrierBase<JSString> string;
+        } u[s_maxInternalRopeLength];
     };
 
 
@@ -454,10 +523,11 @@
         ASSERT(offset <= static_cast<unsigned>(s->length()));
         ASSERT(length <= static_cast<unsigned>(s->length()));
         ASSERT(offset + length <= static_cast<unsigned>(s->length()));
-        VM* vm = &exec->vm();
+        VM& vm = exec->vm();
         if (!length)
-            return vm->smallStrings.emptyString();
-        return jsSubstring(vm, s->value(exec), offset, length);
+            return vm.smallStrings.emptyString();
+        s->value(exec); // For effect. We need to ensure that any string that is used as a substring base is not a rope.
+        return JSRopeString::create(vm, s, offset, length);
     }
 
     inline JSString* jsSubstring8(VM* vm, const String& s, unsigned offset, unsigned length)

Modified: branches/ftlopt/Source/_javascript_Core/runtime/RegExpMatchesArray.cpp (168634 => 168635)


--- branches/ftlopt/Source/_javascript_Core/runtime/RegExpMatchesArray.cpp	2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/runtime/RegExpMatchesArray.cpp	2014-05-12 19:23:47 UTC (rev 168635)
@@ -74,6 +74,8 @@
     ASSERT(m_state != ReifiedAll);
     ASSERT(m_result);
  
+    SamplingRegion samplingRegion("Reifying substring properties");
+    
     reifyMatchPropertyIfNecessary(exec);
 
     if (unsigned numSubpatterns = m_regExp->numSubpatterns()) {

Modified: branches/ftlopt/Source/_javascript_Core/runtime/StringPrototype.cpp (168634 => 168635)


--- branches/ftlopt/Source/_javascript_Core/runtime/StringPrototype.cpp	2014-05-12 19:20:21 UTC (rev 168634)
+++ branches/ftlopt/Source/_javascript_Core/runtime/StringPrototype.cpp	2014-05-12 19:23:47 UTC (rev 168635)
@@ -1218,6 +1218,7 @@
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec)
 {
+    SamplingRegion samplingRegion("Doing substringing");
     JSValue thisValue = exec->thisValue();
     if (!checkObjectCoercible(thisValue))
         return throwVMTypeError(exec);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to