Backed out changesets 00edef0582f1 and 7796f1b42487 (bug 965712) for making OSX mochitest-dt more timeout-prone in the debugger tests.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 30 Apr 2014 15:56:36 -0400
changeset 181451 44e189f0fd67e5eb4d184c5bba0a85018dc0bd3a
parent 181450 34887cdb561622ec80c4d80e249a87224be9b553
child 181502 34736c996ff76a7379b7a4f98b29567cc39d53f5
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
bugs965712
milestone32.0a1
backs out00edef0582f1fc689226c6bb9181ad4e43332fa6
7796f1b424872b11f26be040da31570ea1967b7f
Backed out changesets 00edef0582f1 and 7796f1b42487 (bug 965712) for making OSX mochitest-dt more timeout-prone in the debugger tests. CLOSED TREE
js/src/jit-test/tests/ion/bug965712.js
js/src/jsstr.cpp
js/src/jsstr.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
deleted file mode 100644
--- a/js/src/jit-test/tests/ion/bug965712.js
+++ /dev/null
@@ -1,2 +0,0 @@
-var result = "D1D1D1D1D1D1D1D1D1D1".replace(/d1/ig,1);
-assertEq(result, "1111111111");
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1058,63 +1058,56 @@ struct ManualCmp {
                 return false;
         }
         return true;
     }
 };
 
 template <class InnerMatch>
 static int
-MemChrMatch(const jschar *text, uint32_t textlen, const jschar *pat, uint32_t patlen)
+UnrolledMatch(const jschar *text, uint32_t textlen, const jschar *pat, uint32_t patlen)
 {
-    /*
-     * Use the very efficient memchr to find the character that matches the
-     * first pattern character. The only caveat is that memchr uses 8 bit chars,
-     * while spidermonkey uses 16 bit chars.
-     */
-
+    JS_ASSERT(patlen > 0 && textlen > 0);
+    const jschar *textend = text + textlen - (patlen - 1);
+    const jschar p0 = *pat;
+    const jschar *const patNext = pat + 1;
     const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
-
-    /* Treat input as 8 bit strings */
-    const char *text8 = (const char *)text;
-    const char *pat8 = (const char *)pat;
-
-    /*
-     * Indexing on 8 bits. So the indexes get twice as big.
-     * Currently this isn't a problem since max string size is 28bit.
-     */
-    JS_ASSERT(textlen < UINT32_MAX/2);
-
-    uint32_t i = 0;
-    uint32_t n = (textlen - patlen)*2;
-    while (i <= n) {
-        /* Find the first 8 bits of the pattern in the text. */
-        const char* pos = reinterpret_cast<const char *>(memchr(text8 + i, pat8[0], n - i + 1));
-        if (pos == nullptr)
-            return -1;
-
-        i = static_cast<uint32_t>(pos - text8);
-
-        /* Ignore when it matched the upper 8 bits of the 16bits text. */
-        if (i%2 != 0) {
-            i++;
-            continue;
-        }
-
-        /*
-         * Test the upper 8 bits of the first character of the pattern.
-         * On success test the full text using normal 16bit character compares,
-         * starting at the second 16 bit character.
-         */
-        if (pat8[1] == text8[i+1]) {
-            if (InnerMatch::match(pat + 1, text + i/2 + 1, extent))
-                return i/2;
-        }
-
-        i += 2;
+    uint8_t fixup;
+
+    const jschar *t = text;
+    switch ((textend - t) & 7) {
+      case 0: if (*t++ == p0) { fixup = 8; goto match; }
+      case 7: if (*t++ == p0) { fixup = 7; goto match; }
+      case 6: if (*t++ == p0) { fixup = 6; goto match; }
+      case 5: if (*t++ == p0) { fixup = 5; goto match; }
+      case 4: if (*t++ == p0) { fixup = 4; goto match; }
+      case 3: if (*t++ == p0) { fixup = 3; goto match; }
+      case 2: if (*t++ == p0) { fixup = 2; goto match; }
+      case 1: if (*t++ == p0) { fixup = 1; goto match; }
+    }
+    while (t != textend) {
+      if (t[0] == p0) { t += 1; fixup = 8; goto match; }
+      if (t[1] == p0) { t += 2; fixup = 7; goto match; }
+      if (t[2] == p0) { t += 3; fixup = 6; goto match; }
+      if (t[3] == p0) { t += 4; fixup = 5; goto match; }
+      if (t[4] == p0) { t += 5; fixup = 4; goto match; }
+      if (t[5] == p0) { t += 6; fixup = 3; goto match; }
+      if (t[6] == p0) { t += 7; fixup = 2; goto match; }
+      if (t[7] == p0) { t += 8; fixup = 1; goto match; }
+        t += 8;
+        continue;
+        do {
+            if (*t++ == p0) {
+              match:
+                if (!InnerMatch::match(patNext, t, extent))
+                    goto failed_match;
+                return t - text - 1;
+            }
+          failed_match:;
+        } while (--fixup > 0);
     }
     return -1;
 }
 
 static MOZ_ALWAYS_INLINE int
 StringMatch(const jschar *text, uint32_t textlen,
             const jschar *pat, uint32_t patlen)
 {
@@ -1160,38 +1153,31 @@ StringMatch(const jschar *text, uint32_t
     /*
      * For big patterns with large potential overlap we want the SIMD-optimized
      * speed of memcmp. For small patterns, a simple loop is faster.
      *
      * FIXME: Linux memcmp performance is sad and the manual loop is faster.
      */
     return
 #if !defined(__linux__)
-           patlen > 128 ? MemChrMatch<MemCmp>(text, textlen, pat, patlen)
+           patlen > 128 ? UnrolledMatch<MemCmp>(text, textlen, pat, patlen)
                         :
 #endif
-                          MemChrMatch<ManualCmp>(text, textlen, pat, patlen);
+                          UnrolledMatch<ManualCmp>(text, textlen, pat, patlen);
 }
 
 static const size_t sRopeMatchThresholdRatioLog2 = 5;
 
 bool
 js::StringHasPattern(const jschar *text, uint32_t textlen,
                      const jschar *pat, uint32_t patlen)
 {
     return StringMatch(text, textlen, pat, patlen) != -1;
 }
 
-int
-js::StringFindPattern(const jschar *text, uint32_t textlen,
-                      const jschar *pat, uint32_t patlen)
-{
-    return StringMatch(text, textlen, pat, patlen);
-}
-
 // When an algorithm does not need a string represented as a single linear
 // array of characters, this range utility may be used to traverse the string a
 // sequence of linear arrays of characters. This avoids flattening ropes.
 class StringSegmentRange
 {
     // If malloc() shows up in any profiles from this vector, we can add a new
     // StackAllocPolicy which stashes a reusable freed-at-gc buffer in the cx.
     AutoStringVector stack;
@@ -1746,22 +1732,16 @@ HasRegExpMetaChars(const jschar *chars, 
 {
     for (size_t i = 0; i < length; ++i) {
         if (IsRegExpMetaChar(chars[i]))
             return true;
     }
     return false;
 }
 
-bool
-js::StringHasRegExpMetaChars(const jschar *chars, size_t length)
-{
-    return HasRegExpMetaChars(chars, length);
-}
-
 namespace {
 
 /*
  * StringRegExpGuard factors logic out of String regexp operations.
  *
  * |optarg| indicates in which argument position RegExp flags will be found, if
  * present. This is a Mozilla extension and not part of any ECMA spec.
  */
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -208,23 +208,16 @@ CompareAtoms(JSAtom *atom1, JSAtom *atom
 extern bool
 StringEqualsAscii(JSLinearString *str, const char *asciiBytes);
 
 /* Return true if the string contains a pattern anywhere inside it. */
 extern bool
 StringHasPattern(const jschar *text, uint32_t textlen,
                  const jschar *pat, uint32_t patlen);
 
-extern int
-StringFindPattern(const jschar *text, uint32_t textlen,
-                  const jschar *pat, uint32_t patlen);
-
-extern bool
-StringHasRegExpMetaChars(const jschar *chars, size_t length);
-
 } /* namespace js */
 
 extern size_t
 js_strlen(const jschar *s);
 
 extern int32_t
 js_strcmp(const jschar *lhs, const jschar *rhs);
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -3,18 +3,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/RegExpObject.h"
 
 #include "mozilla/MemoryReporting.h"
 
-#include "jsstr.h"
-
 #include "frontend/TokenStream.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringBuffer.h"
 #include "vm/TraceLogging.h"
 #include "vm/Xdr.h"
 #include "yarr/YarrSyntaxChecker.h"
 
@@ -374,17 +372,17 @@ RegExpObject::toString(JSContext *cx) co
         return nullptr;
 
     return sb.finishString();
 }
 
 /* RegExpShared */
 
 RegExpShared::RegExpShared(JSAtom *source, RegExpFlag flags, uint64_t gcNumber)
-  : source(source), flags(flags), parenCount(0), canStringMatch(false),
+  : source(source), flags(flags), parenCount(0),
 #if ENABLE_YARR_JIT
     codeBlock(),
 #endif
     bytecode(nullptr), activeUseCount(0), gcNumberWhenUsed(gcNumber)
 {}
 
 RegExpShared::~RegExpShared()
 {
@@ -435,19 +433,16 @@ RegExpShared::checkSyntax(ExclusiveConte
 
     reportYarrError(cx, tokenStream, error);
     return false;
 }
 
 bool
 RegExpShared::compile(JSContext *cx, bool matchOnly)
 {
-    TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
-    AutoTraceLog logCompile(logger, TraceLogger::YarrCompile);
-
     if (!sticky())
         return compile(cx, *source, matchOnly);
 
     /*
      * The sticky case we implement hackily by prepending a caret onto the front
      * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp.
      */
     static const jschar prefix[] = {'^', '(', '?', ':'};
@@ -466,22 +461,16 @@ RegExpShared::compile(JSContext *cx, boo
         return false;
 
     return compile(cx, *fakeySource, matchOnly);
 }
 
 bool
 RegExpShared::compile(JSContext *cx, JSLinearString &pattern, bool matchOnly)
 {
-    if (!ignoreCase() && !StringHasRegExpMetaChars(pattern.chars(), pattern.length())) {
-        canStringMatch = true;
-        parenCount = 0;
-        return true;
-    }
-
     /* Parse the pattern. */
     ErrorCode yarrError;
     YarrPattern yarrPattern(pattern, ignoreCase(), multiline(), &yarrError);
     if (yarrError) {
         reportYarrError(cx, nullptr, yarrError);
         return false;
     }
     this->parenCount = yarrPattern.m_numSubpatterns;
@@ -513,38 +502,41 @@ RegExpShared::compile(JSContext *cx, JSL
 
     bytecode = byteCompile(yarrPattern, bumpAlloc).get();
     return true;
 }
 
 bool
 RegExpShared::compileIfNecessary(JSContext *cx)
 {
-    if (hasCode() || hasBytecode() || canStringMatch)
+    if (hasCode() || hasBytecode())
         return true;
     return compile(cx, false);
 }
 
 bool
 RegExpShared::compileMatchOnlyIfNecessary(JSContext *cx)
 {
-    if (hasMatchOnlyCode() || hasBytecode() || canStringMatch)
+    if (hasMatchOnlyCode() || hasBytecode())
         return true;
     return compile(cx, true);
 }
 
 RegExpRunStatus
 RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length,
                       size_t *lastIndex, MatchPairs &matches)
 {
     TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
 
-    /* Compile the code at point-of-use. */
-    if (!compileIfNecessary(cx))
-        return RegExpRunStatus_Error;
+    {
+        /* Compile the code at point-of-use. */
+        AutoTraceLog logCompile(logger, TraceLogger::YarrCompile);
+        if (!compileIfNecessary(cx))
+            return RegExpRunStatus_Error;
+    }
 
     /* Ensure sufficient memory for output vector. */
     if (!matches.initArray(pairCount()))
         return RegExpRunStatus_Error;
 
     /*
      * |displacement| emulates sticky mode by matching from this offset
      * into the char buffer and subtracting the delta off at the end.
@@ -558,30 +550,16 @@ RegExpShared::execute(JSContext *cx, con
         chars += displacement;
         length -= displacement;
         start = 0;
     }
 
     unsigned *outputBuf = matches.rawBuf();
     unsigned result;
 
-    if (canStringMatch) {
-        int res = StringFindPattern(chars+start, length-start, source->chars(), source->length());
-        if (res == -1)
-            return RegExpRunStatus_Success_NotFound;
-
-        outputBuf[0] = res + start;
-        outputBuf[1] = outputBuf[0] + source->length();
-
-        matches.displace(displacement);
-        matches.checkAgainst(origLength);
-        *lastIndex = matches[0].limit;
-        return RegExpRunStatus_Success;
-    }
-
 #if ENABLE_YARR_JIT
     if (codeBlock.isFallBack()) {
         AutoTraceLog logInterpret(logger, TraceLogger::YarrInterpret);
         result = JSC::Yarr::interpret(cx, bytecode, chars, length, start, outputBuf);
     } else {
         AutoTraceLog logJIT(logger, TraceLogger::YarrJIT);
         result = codeBlock.execute(chars, start, length, (int *)outputBuf).start;
     }
@@ -607,44 +585,36 @@ RegExpShared::execute(JSContext *cx, con
 }
 
 RegExpRunStatus
 RegExpShared::executeMatchOnly(JSContext *cx, const jschar *chars, size_t length,
                                size_t *lastIndex, MatchPair &match)
 {
     TraceLogger *logger = js::TraceLoggerForMainThread(cx->runtime());
 
-    /* Compile the code at point-of-use. */
-    if (!compileMatchOnlyIfNecessary(cx))
-        return RegExpRunStatus_Error;
+    {
+        /* Compile the code at point-of-use. */
+        AutoTraceLog logCompile(logger, TraceLogger::YarrCompile);
+        if (!compileMatchOnlyIfNecessary(cx))
+            return RegExpRunStatus_Error;
+    }
 
 #ifdef DEBUG
     const size_t origLength = length;
 #endif
     size_t start = *lastIndex;
     size_t displacement = 0;
 
     if (sticky()) {
         displacement = start;
         chars += displacement;
         length -= displacement;
         start = 0;
     }
 
-    if (canStringMatch) {
-        int res = StringFindPattern(chars+start, length-start, source->chars(), source->length());
-        if (res == -1)
-            return RegExpRunStatus_Success_NotFound;
-
-        match = MatchPair(res + start, res + start + source->length());
-        match.displace(displacement);
-        *lastIndex = match.limit;
-        return RegExpRunStatus_Success;
-    }
-
 #if ENABLE_YARR_JIT
     if (!codeBlock.isFallBack()) {
         AutoTraceLog logJIT(logger, TraceLogger::YarrJIT);
         MatchResult result = codeBlock.execute(chars, start, length);
         if (!result)
             return RegExpRunStatus_Success_NotFound;
 
         match = MatchPair(result.start, result.end);
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -140,17 +140,16 @@ class RegExpShared
      * Source to the RegExp, for lazy compilation.
      * The source must be rooted while activeUseCount is non-zero
      * via RegExpGuard or explicit calls to trace().
      */
     JSAtom *           source;
 
     RegExpFlag         flags;
     unsigned           parenCount;
-    bool               canStringMatch;
 
 #if ENABLE_YARR_JIT
     /* Note: Native code is valid only if |codeBlock.isFallBack() == false|. */
     YarrCodeBlock   codeBlock;
 #endif
     BytecodePattern *bytecode;
 
     /* Lifetime-preserving variables: see class-level comment above. */
@@ -200,21 +199,17 @@ class RegExpShared
                             size_t *lastIndex, MatchPairs &matches);
 
     /* Run the regular expression without collecting matches, for test(). */
     RegExpRunStatus executeMatchOnly(JSContext *cx, const jschar *chars, size_t length,
                                      size_t *lastIndex, MatchPair &match);
 
     /* Accessors */
 
-    size_t getParenCount() const {
-        JS_ASSERT(isCompiled() || canStringMatch);
-        return parenCount;
-    }
-
+    size_t getParenCount() const        { JS_ASSERT(isCompiled()); return parenCount; }
     void incRef()                       { activeUseCount++; }
     void decRef()                       { JS_ASSERT(activeUseCount > 0); activeUseCount--; }
 
     /* Accounts for the "0" (whole match) pair. */
     size_t pairCount() const            { return getParenCount() + 1; }
 
     RegExpFlag getFlags() const         { return flags; }
     bool ignoreCase() const             { return flags & IgnoreCaseFlag; }