Bug 1207922 - Part 1: Self-host RegExp.prototype.{exec,test}. r=till,h4writer
authorTooru Fujisawa <arai_a@mac.com>
Thu, 24 Sep 2015 18:28:37 +0900
changeset 278974 28cc01acfd024c6ba4066fc192c6ccff502c0b00
parent 278973 0876b808c67701f77e6fd5abf031500bc7f479fd
child 278975 d39655c43ff0ab6378f2fc82ec56270d3bd88013
push id29863
push userkwierso@gmail.com
push dateFri, 08 Jan 2016 01:12:09 +0000
treeherdermozilla-central@8a7cc8b7aa55 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill, h4writer
bugs1207922
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1207922 - Part 1: Self-host RegExp.prototype.{exec,test}. r=till,h4writer
js/src/builtin/RegExp.cpp
js/src/builtin/RegExp.h
js/src/builtin/RegExp.js
js/src/devtools/automation/cgc-jittest-timeouts.txt
js/src/irregexp/NativeRegExpMacroAssembler.cpp
js/src/irregexp/NativeRegExpMacroAssembler.h
js/src/irregexp/RegExpEngine.cpp
js/src/irregexp/RegExpEngine.h
js/src/irregexp/RegExpInterpreter.cpp
js/src/jit-test/tests/basic/regexpLastIndexReset.js
js/src/jit-test/tests/gc/oomInRegExp.js
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/InlinableNatives.h
js/src/jit/Ion.cpp
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.h
js/src/jit/JitCompartment.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/Recover.cpp
js/src/jit/Recover.h
js/src/jit/arm/Assembler-arm.h
js/src/jit/arm64/Assembler-arm64.h
js/src/jit/mips-shared/Assembler-mips-shared.h
js/src/jit/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jit/x64/Assembler-x64.h
js/src/jit/x86/Assembler-x86.h
js/src/jsstr.cpp
js/src/tests/ecma_3/RegExp/15.10.6.2-2.js
js/src/tests/ecma_6/RegExp/exec-lastIndex-negative.js
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/RegExpStatics.cpp
js/src/vm/RegExpStatics.h
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -21,16 +21,17 @@
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::unicode;
 
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 
+/* ES6 21.2.5.2.2 steps 19-29. */
 bool
 js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
                             MutableHandleValue rval)
 {
     MOZ_ASSERT(input);
 
     /*
      * Create the (slow) result array for a match.
@@ -45,70 +46,78 @@ js::CreateRegExpMatchResult(JSContext* c
     /* Get the templateObject that defines the shape and type of the output object */
     JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
     if (!templateObject)
         return false;
 
     size_t numPairs = matches.length();
     MOZ_ASSERT(numPairs > 0);
 
+    /* Step 19. */
     RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, numPairs, templateObject));
     if (!arr)
         return false;
 
-    /* Store a Value for each pair. */
+    /* Steps 27-28
+     * Store a Value for each pair. */
     for (size_t i = 0; i < numPairs; i++) {
         const MatchPair& pair = matches[i];
 
         if (pair.isUndefined()) {
             MOZ_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
             arr->setDenseInitializedLength(i + 1);
             arr->initDenseElement(i, UndefinedValue());
         } else {
             JSLinearString* str = NewDependentString(cx, input, pair.start, pair.length());
             if (!str)
                 return false;
             arr->setDenseInitializedLength(i + 1);
             arr->initDenseElement(i, StringValue(str));
         }
     }
 
-    /* Set the |index| property. (TemplateObject positions it in slot 0) */
+    /* Step 24 (reordered)
+     * Set the |index| property. (TemplateObject positions it in slot 0) */
     arr->setSlot(0, Int32Value(matches[0].start));
 
-    /* Set the |input| property. (TemplateObject positions it in slot 1) */
+    /* Step 25 (reordered)
+     * Set the |input| property. (TemplateObject positions it in slot 1) */
     arr->setSlot(1, StringValue(input));
 
 #ifdef DEBUG
     RootedValue test(cx);
     RootedId id(cx, NameToId(cx->names().index));
     if (!NativeGetProperty(cx, arr, id, &test))
         return false;
     MOZ_ASSERT(test == arr->getSlot(0));
     id = NameToId(cx->names().input);
     if (!NativeGetProperty(cx, arr, id, &test))
         return false;
     MOZ_ASSERT(test == arr->getSlot(1));
 #endif
 
+    /* Step 29. */
     rval.setObject(*arr);
     return true;
 }
 
+/* ES6 21.2.5.2.2 steps 3, 14-17, except 15.a.i-ii, 15.c.i.1-2. */
 static RegExpRunStatus
 ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLinearString input,
-                  size_t searchIndex, MatchPairs* matches)
+                  size_t searchIndex, bool sticky, MatchPairs* matches, size_t* endIndex)
 {
-    RegExpRunStatus status = re.execute(cx, input, searchIndex, matches);
+    RegExpRunStatus status = re.execute(cx, input, searchIndex, sticky, matches, endIndex);
+
+    /* Out of spec: Update RegExpStatics. */
     if (status == RegExpRunStatus_Success && res) {
         if (matches) {
             if (!res->updateFromMatchPairs(cx, input, *matches))
                 return RegExpRunStatus_Error;
         } else {
-            res->updateLazily(cx, input, &re, searchIndex);
+            res->updateLazily(cx, input, &re, searchIndex, sticky);
         }
     }
     return status;
 }
 
 /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
 bool
 js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, RegExpObject& reobj,
@@ -116,17 +125,18 @@ js::ExecuteRegExpLegacy(JSContext* cx, R
                         MutableHandleValue rval)
 {
     RegExpGuard shared(cx);
     if (!reobj.getShared(cx, &shared))
         return false;
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
 
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex, &matches);
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex, reobj.sticky(),
+                                               &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
         /* ExecuteRegExp() previously returned an array or null. */
         rval.setNull();
         return true;
     }
@@ -592,18 +602,18 @@ const JSPropertySpec js::regexp_properti
 };
 
 const JSFunctionSpec js::regexp_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_SELF_HOSTED_FN(js_toSource_str, "RegExpToString", 0, 0),
 #endif
     JS_SELF_HOSTED_FN(js_toString_str, "RegExpToString", 0, 0),
     JS_FN("compile",        regexp_compile,     2,0),
-    JS_INLINABLE_FN("exec", regexp_exec,        1,0, RegExpExec),
-    JS_INLINABLE_FN("test", regexp_test,        1,0, RegExpTest),
+    JS_SELF_HOSTED_FN("exec", "RegExp_prototype_Exec", 1,0),
+    JS_SELF_HOSTED_FN("test", "RegExpTest" ,    1,0),
     JS_FS_END
 };
 
 #define STATIC_PAREN_GETTER_CODE(parenNum)                                      \
     if (!res->createParen(cx, parenNum, args.rval()))                           \
         return false;                                                           \
     if (args.rval().isUndefined())                                              \
         args.rval().setString(cx->runtime()->emptyString);                      \
@@ -762,33 +772,16 @@ js::CreateRegExpPrototype(JSContext* cx,
     proto->NativeObject::setPrivate(nullptr);
 
     RootedAtom source(cx, cx->names().empty);
     if (!RegExpObject::initFromAtom(cx, proto, source, RegExpFlag(0)))
         return nullptr;
     return proto;
 }
 
-static bool
-ReportLastIndexNonwritable(JSContext* cx)
-{
-    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_READ_ONLY, "\"lastIndex\"");
-    return false;
-}
-
-static bool
-SetLastIndex(JSContext* cx, Handle<RegExpObject*> reobj, double lastIndex)
-{
-    if (!reobj->lookup(cx, cx->names().lastIndex)->writable())
-        return ReportLastIndexNonwritable(cx);
-
-    reobj->setLastIndex(lastIndex);
-    return true;
-}
-
 template <typename CharT>
 static bool
 IsTrailSurrogateWithLeadSurrogateImpl(JSContext* cx, HandleLinearString input, size_t index)
 {
     JS::AutoCheckCannotGC nogc;
     MOZ_ASSERT(index > 0 && index < input->length());
     const CharT* inputChars = input->chars<CharT>(nogc);
 
@@ -802,20 +795,21 @@ IsTrailSurrogateWithLeadSurrogate(JSCont
     if (index <= 0 || size_t(index) >= input->length())
         return false;
 
     return input->hasLatin1Chars()
            ? IsTrailSurrogateWithLeadSurrogateImpl<Latin1Char>(cx, input, index)
            : IsTrailSurrogateWithLeadSurrogateImpl<char16_t>(cx, input, index);
 }
 
-/* ES6 final draft 21.2.5.2.2. */
-RegExpRunStatus
-js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
-                  MatchPairs* matches, RegExpStaticsUpdate staticsUpdate)
+/* ES6 21.2.5.2.2 steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
+static RegExpRunStatus
+ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
+              int32_t lastIndex, bool sticky,
+              MatchPairs* matches, size_t* endIndex, RegExpStaticsUpdate staticsUpdate)
 {
     /*
      * WARNING: Despite the presence of spec step comment numbers, this
      *          algorithm isn't consistent with any ES6 version, draft or
      *          otherwise.  YOU HAVE BEEN WARNED.
      */
 
     /* Steps 1-2 performed by the caller. */
@@ -833,224 +827,210 @@ js::ExecuteRegExp(JSContext* cx, HandleO
     } else {
         res = nullptr;
     }
 
     RootedLinearString input(cx, string->ensureLinear(cx));
     if (!input)
         return RegExpRunStatus_Error;
 
-    /* Step 3. */
-    size_t length = input->length();
-
-    /* Steps 4-5. */
-    RootedValue lastIndex(cx, reobj->getLastIndex());
-    int searchIndex;
-    if (lastIndex.isInt32()) {
-        /* Aggressively avoid doubles. */
-        searchIndex = lastIndex.toInt32();
-    } else {
-        double d;
-        if (!ToInteger(cx, lastIndex, &d))
-            return RegExpRunStatus_Error;
-
-        /* Inlined steps 6-10, 15.a with doubles to detect failure case. */
-        if (reobj->needUpdateLastIndex() && (d < 0 || d > length)) {
-            /* Steps 15.a.i-ii. */
-            if (!SetLastIndex(cx, reobj, 0))
-                return RegExpRunStatus_Error;
-
-            /* Step 15.a.iii. */
-            return RegExpRunStatus_Success_NotFound;
-        }
+    /* Handled by caller */
+    MOZ_ASSERT(lastIndex >= 0 && size_t(lastIndex) <= input->length());
 
-        searchIndex = int(d);
-    }
-
-    /*
-     * Steps 6-10.
-     *
-     * Also make sure that we have a MatchPairs for regexps which update their
-     * last index, as we won't compute the last index otherwise.
-     */
-    Maybe<ScopedMatchPairs> alternateMatches;
-    if (!reobj->needUpdateLastIndex()) {
-        searchIndex = 0;
-    } else if (!matches) {
-        alternateMatches.emplace(&cx->tempLifoAlloc());
-        matches = &alternateMatches.ref();
-    }
-
-    /* Step 15.a. */
-    if (searchIndex < 0 || size_t(searchIndex) > length) {
-        /* Steps 15.a.i-ii. */
-        if (!SetLastIndex(cx, reobj, 0))
-            return RegExpRunStatus_Error;
-
-        /* Step 15.a.iii. */
-        return RegExpRunStatus_Success_NotFound;
-    }
+    /* Steps 4-10 performed by the caller. */
 
     /* Steps 12-13. */
     if (reobj->unicode()) {
         /*
          * ES6 21.2.2.2 step 2.
          *   Let listIndex be the index into Input of the character that was
          *   obtained from element index of str.
          *
          * In the spec, pattern match is performed with decoded Unicode code
          * points, but our implementation performs it with UTF-16 encoded
-         * string.  In step 2, we should decrement searchIndex (index) if it
+         * string.  In step 2, we should decrement lastIndex (index) if it
          * points the trail surrogate that has corresponding lead surrogate.
          *
          *   var r = /\uD83D\uDC38/ug;
          *   r.lastIndex = 1;
          *   var str = "\uD83D\uDC38";
          *   var result = r.exec(str); // pattern match starts from index 0
          *   print(result.index);      // prints 0
          *
          * Note: this doesn't match the current spec text and result in
          * different values for `result.index` under certain conditions.
          * However, the spec will change to match our implementation's
          * behavior. See https://github.com/tc39/ecma262/issues/128.
          */
-        if (IsTrailSurrogateWithLeadSurrogate(cx, input, searchIndex))
-            searchIndex--;
+        if (IsTrailSurrogateWithLeadSurrogate(cx, input, lastIndex))
+            lastIndex--;
     }
 
-    /* Step 14-29. */
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, searchIndex, matches);
+    /* Steps 3, 14-17, except 15.a.i-ii, 15.c.i.1-2. */
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, sticky, matches, endIndex);
     if (status == RegExpRunStatus_Error)
         return RegExpRunStatus_Error;
 
-    if (status == RegExpRunStatus_Success_NotFound) {
-        /* Steps 15.a.i-ii. */
-        if (!SetLastIndex(cx, reobj, 0))
-            return RegExpRunStatus_Error;
-    } else if (reobj->needUpdateLastIndex()) {
-        /* Steps 18.a-b. */
-        MOZ_ASSERT(matches && !matches->empty());
-        if (!SetLastIndex(cx, reobj, (*matches)[0].limit))
-            return RegExpRunStatus_Error;
-    }
+    /* Steps 15.a.i-ii, 18 are done by Self-hosted function. */
 
     return status;
 }
 
-/* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). */
-static RegExpRunStatus
-ExecuteRegExp(JSContext* cx, const CallArgs& args, MatchPairs* matches)
-{
-    /* Step 1 (a) was performed by CallNonGenericMethod. */
-    RootedObject regexp(cx, &args.thisv().toObject());
-
-    /* Step 2. */
-    RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
-    if (!string)
-        return RegExpRunStatus_Error;
-
-    return ExecuteRegExp(cx, regexp, string, matches, UpdateRegExpStatics);
-}
-
-/* ES5 15.10.6.2. */
+/* ES6 21.2.5.2.2 steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
 static bool
-regexp_exec_impl(JSContext* cx, HandleObject regexp, HandleString string,
-                 RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
+RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
+                  int32_t lastIndex, bool sticky,
+                  RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
 {
     /* Execute regular expression and gather matches. */
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
 
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, &matches, staticsUpdate);
+    /* Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, sticky,
+                                           &matches, nullptr, staticsUpdate);
     if (status == RegExpRunStatus_Error)
         return false;
 
+    /* Steps 15.a, 15.c. */
     if (status == RegExpRunStatus_Success_NotFound) {
         rval.setNull();
         return true;
     }
 
+    /* Steps 19-29 */
     return CreateRegExpMatchResult(cx, string, matches, rval);
 }
 
-static bool
-regexp_exec_impl(JSContext* cx, const CallArgs& args)
+/* ES6 21.2.5.2.2 steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
+bool
+js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
 {
-    RootedObject regexp(cx, &args.thisv().toObject());
-    RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
-    if (!string)
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 4);
+    MOZ_ASSERT(IsRegExpObject(args[0]));
+    MOZ_ASSERT(args[1].isString());
+    MOZ_ASSERT(args[2].isNumber());
+    MOZ_ASSERT(args[3].isBoolean());
+
+    RootedObject regexp(cx, &args[0].toObject());
+    RootedString string(cx, args[1].toString());
+    RootedValue lastIndexVal(cx, args[2]);
+    bool sticky = ToBoolean(args[3]);
+
+    int32_t lastIndex = 0;
+    if (!ToInt32(cx, lastIndexVal, &lastIndex))
         return false;
 
-    return regexp_exec_impl(cx, regexp, string, UpdateRegExpStatics, args.rval());
-}
-
-bool
-js::regexp_exec(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod(cx, IsRegExpObject, regexp_exec_impl, args);
+    /* Steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
+    return RegExpMatcherImpl(cx, regexp, string, lastIndex, sticky,
+                             UpdateRegExpStatics, args.rval());
 }
 
 /* Separate interface for use by IonMonkey. */
 bool
-js::regexp_exec_raw(JSContext* cx, HandleObject regexp, HandleString input,
-                    MatchPairs* maybeMatches, MutableHandleValue output)
+js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
+                     int32_t lastIndex, bool sticky,
+                     MatchPairs* maybeMatches, MutableHandleValue output)
 {
+    MOZ_ASSERT(lastIndex <= INT32_MAX);
+
     // The MatchPairs will always be passed in, but RegExp execution was
     // successful only if the pairs have actually been filled in.
     if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
         return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
-    return regexp_exec_impl(cx, regexp, input, UpdateRegExpStatics, output);
+    return RegExpMatcherImpl(cx, regexp, input, lastIndex, sticky,
+                             UpdateRegExpStatics, output);
 }
 
 bool
 js::regexp_exec_no_statics(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
 
-    return regexp_exec_impl(cx, regexp, string, DontUpdateRegExpStatics, args.rval());
+    return RegExpMatcherImpl(cx, regexp, string, 0, false,
+                             DontUpdateRegExpStatics, args.rval());
 }
 
-/* ES5 15.10.6.3. */
-static bool
-regexp_test_impl(JSContext* cx, const CallArgs& args)
+/* ES6 21.2.5.2.2 steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
+bool
+js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
 {
-    RegExpRunStatus status = ExecuteRegExp(cx, args, nullptr);
-    args.rval().setBoolean(status == RegExpRunStatus_Success);
-    return status != RegExpRunStatus_Error;
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 4);
+    MOZ_ASSERT(IsRegExpObject(args[0]));
+    MOZ_ASSERT(args[1].isString());
+    MOZ_ASSERT(args[2].isNumber());
+    MOZ_ASSERT(args[3].isBoolean());
+
+    RootedObject regexp(cx, &args[0].toObject());
+    RootedString string(cx, args[1].toString());
+    RootedValue lastIndexVal(cx, args[2]);
+    bool sticky = ToBoolean(args[3]);
+
+    int32_t lastIndex = 0;
+    if (!ToInt32(cx, lastIndexVal, &lastIndex))
+        return false;
+
+    /* Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
+    size_t endIndex = 0;
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string,
+                                           lastIndex, sticky,
+                                           nullptr, &endIndex, UpdateRegExpStatics);
+
+    if (status == RegExpRunStatus_Error)
+        return false;
+
+    if (status == RegExpRunStatus_Success) {
+        MOZ_ASSERT(endIndex <= INT32_MAX);
+        args.rval().setInt32(int32_t(endIndex));
+    } else {
+        args.rval().setInt32(-1);
+    }
+    return true;
 }
 
 /* Separate interface for use by IonMonkey. */
 bool
-js::regexp_test_raw(JSContext* cx, HandleObject regexp, HandleString input, bool* result)
+js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
+                    int32_t lastIndex, bool sticky, int32_t* endIndex)
 {
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, nullptr, UpdateRegExpStatics);
-    *result = (status == RegExpRunStatus_Success);
-    return status != RegExpRunStatus_Error;
-}
+    MOZ_ASSERT(lastIndex <= INT32_MAX);
+
+    size_t endIndexTmp = 0;
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex, sticky,
+                                           nullptr, &endIndexTmp, UpdateRegExpStatics);
 
-bool
-js::regexp_test(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod(cx, IsRegExpObject, regexp_test_impl, args);
+    if (status == RegExpRunStatus_Success) {
+        MOZ_ASSERT(endIndexTmp <= INT32_MAX);
+        *endIndex = int32_t(endIndexTmp);
+        return true;
+    }
+    if (status == RegExpRunStatus_Success_NotFound) {
+        *endIndex = -1;
+        return true;
+    }
+
+    return false;
 }
 
 bool
 js::regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
 
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, nullptr, DontUpdateRegExpStatics);
+    size_t ignored = 0;
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0, false,
+                                           nullptr, &ignored, DontUpdateRegExpStatics);
     args.rval().setBoolean(status == RegExpRunStatus_Success);
     return status != RegExpRunStatus_Error;
 }
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -21,20 +21,16 @@ InitRegExpClass(JSContext* cx, HandleObj
 
 // Whether RegExp statics should be updated with the input and results of a
 // regular expression execution.
 enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics };
 
 // Whether RegExp statics should be used to create a RegExp instance.
 enum RegExpStaticsUse { UseRegExpStatics, DontUseRegExpStatics };
 
-RegExpRunStatus
-ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
-              MatchPairs* matches, RegExpStaticsUpdate staticsUpdate);
-
 /*
  * Legacy behavior of ExecuteRegExp(), which is baked into the JSAPI.
  *
  * |res| may be nullptr if the RegExpStatics are not to be updated.
  * |input| may be nullptr if there is no JSString corresponding to
  * |chars| and |length|.
  */
 bool
@@ -43,27 +39,29 @@ ExecuteRegExpLegacy(JSContext* cx, RegEx
                     MutableHandleValue rval);
 
 /* Translation from MatchPairs to a JS array in regexp_exec()'s output format. */
 bool
 CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
                         MutableHandleValue rval);
 
 extern bool
-regexp_exec_raw(JSContext* cx, HandleObject regexp, HandleString input, MatchPairs* maybeMatches,
-                MutableHandleValue output);
+RegExpMatcher(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
-regexp_exec(JSContext* cx, unsigned argc, Value* vp);
-
-bool
-regexp_test_raw(JSContext* cx, HandleObject regexp, HandleString input, bool* result);
+RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
+                 int32_t lastIndex, bool sticky,
+                 MatchPairs* maybeMatches, MutableHandleValue output);
 
 extern bool
-regexp_test(JSContext* cx, unsigned argc, Value* vp);
+RegExpTester(JSContext* cx, unsigned argc, Value* vp);
+
+extern bool
+RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
+                int32_t lastIndex, bool sticky, int32_t* endIndex);
 
 /*
  * The following functions are for use by self-hosted code.
  */
 
 /*
  * Behaves like regexp.exec(string), but doesn't set RegExp statics.
  *
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -49,9 +49,126 @@ function RegExpToString()
     var pattern = R.source;
 
     // Steps 5-6.
     var flags = R.flags;
 
     // Step 7.
     return '/' + pattern + '/' + flags;
 }
+
+// ES6 21.2.5.2.
+// NOTE: This is not RegExpExec (21.2.5.2.1).
+function RegExp_prototype_Exec(string) {
+    // Steps 1-3.
+    var R = this;
+    if (!IsObject(R) || !IsRegExpObject(R))
+        return callFunction(CallRegExpMethodIfWrapped, R, string, "RegExp_prototype_Exec");
+
+    // Steps 4-5.
+    var S = ToString(string);
+
+    // Step 6.
+    return RegExpBuiltinExec(R, S, false);
+}
+
+// ES6 21.2.5.2.1.
+function RegExpExec(R, S, forTest) {
+    // Steps 1-2 (skipped).
+
+    // Steps 3-4.
+    var exec = R.exec;
+
+    // Step 5.
+    // If exec is the original RegExp.prototype.exec, use the same, faster,
+    // path as for the case where exec isn't callable.
+    if (exec === RegExp_prototype_Exec || !IsCallable(exec)) {
+        // ES6 21.2.5.2 steps 1-2, 4-5 (skipped) for optimized case.
+
+        // Steps 6-7 or ES6 21.2.5.2 steps 3, 6 for optimized case.
+        return RegExpBuiltinExec(R, S, forTest);
+    }
+
+    // Steps 5.a-b.
+    var result = callContentFunction(exec, R, S);
+
+    // Step 5.c.
+    if (typeof result !== "object")
+        ThrowTypeError(JSMSG_EXEC_NOT_OBJORNULL);
+
+    // Step 5.d.
+    return forTest ? result !== null : result;
+}
+
+// ES6 21.2.5.2.2.
+function RegExpBuiltinExec(R, S, forTest) {
+    // ES6 21.2.5.2.1 step 6.
+    // This check is here for RegExpTest.  RegExp_prototype_Exec does same
+    // thing already.
+    if (!IsRegExpObject(R))
+        return callFunction(CallRegExpMethodIfWrapped, R, R, S, forTest, "RegExpBuiltinExec");
+
+    // Step 1-2 (skipped).
+
+    // Steps 4-5.
+    var lastIndex = ToLength(R.lastIndex);
+
+    // Steps 6-7.
+    var global = !!R.global;
+
+    // Steps 8-9.
+    var sticky = !!R.sticky;
+
+    // Step 10.
+    if (!global && !sticky) {
+        lastIndex = 0;
+    } else {
+        if (lastIndex < 0 || lastIndex > S.length) {
+            // Steps 15.a.i-ii, 15.c.i.1-2.
+            R.lastIndex = 0;
+            return forTest ? false : null;
+        }
+    }
+
+    if (forTest) {
+        // Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2.
+        var endIndex = RegExpTester(R, S, lastIndex, sticky);
+        if (endIndex == -1) {
+            // Steps 15.a.i-ii, 15.c.i.1-2.
+            R.lastIndex = 0;
+            return false;
+        }
+
+        // Step 18.
+        if (global || sticky)
+            R.lastIndex = endIndex;
+
+        return true;
+    }
+
+    // Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2.
+    var result = RegExpMatcher(R, S, lastIndex, sticky);
+    if (result === null) {
+        // Steps 15.a.i-ii, 15.c.i.1-2.
+        R.lastIndex = 0;
+    } else {
+        // Step 18.
+        if (global || sticky)
+            R.lastIndex = result.index + result[0].length;
+    }
+
+    return result;
+}
+
+// ES6 21.2.5.13.
+function RegExpTest(string) {
+    // Steps 1-2.
+    var R = this;
+    if (!IsObject(R))
+        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R);
+
+    // Steps 3-4.
+    var S = ToString(string);
+
+    // Steps 5-6.
+    return RegExpExec(R, S, true);
+}
 _SetCanonicalName(RegExpToString, "toString");
--- a/js/src/devtools/automation/cgc-jittest-timeouts.txt
+++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt
@@ -16,10 +16,11 @@ gc/bug-1014972.js
 gc/bug-906236.js
 gc/bug-906241.js
 ion/bug787921.js
 parallel/alloc-many-objs.js
 parallel/alloc-too-many-objs.js
 self-test/assertDeepEq.js
 v8-v5/check-earley-boyer.js
 v8-v5/check-raytrace.js
+v8-v5/check-regexp.js
 v8-v5/check-splay.js
 SIMD/nursery-overflow.js
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -199,16 +199,20 @@ NativeRegExpMacroAssembler::GenerateCode
 #ifdef DEBUG
         // Bounds check numOutputRegisters.
         Label enoughRegisters;
         masm.branchPtr(Assembler::GreaterThanOrEqual,
                        temp1, ImmWord(num_saved_registers_), &enoughRegisters);
         masm.assumeUnreachable("Not enough output registers for RegExp");
         masm.bind(&enoughRegisters);
 #endif
+    } else {
+        Register endIndexRegister = input_end_pointer;
+        masm.loadPtr(Address(temp0, offsetof(InputOutputData, endIndex)), endIndexRegister);
+        masm.storePtr(endIndexRegister, Address(masm.getStackPointer(), offsetof(FrameData, endIndex)));
     }
 
     // Load string end pointer.
     masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), input_end_pointer);
 
     // Load input start pointer, and copy to FrameData.
     masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputStart)), current_position);
     masm.storePtr(current_position, Address(masm.getStackPointer(), offsetof(FrameData, inputStart)));
@@ -349,16 +353,40 @@ NativeRegExpMacroAssembler::GenerateCode
                                    &exit_label_);
 
                 // Advance current position after a zero-length match.
                 masm.addPtr(Imm32(char_size()), current_position);
             }
 
             masm.jump(&load_char_start_regexp);
         } else {
+            if (match_only) {
+                // Store endIndex.
+
+                Register endIndexRegister = temp1;
+                Register inputByteLength = backtrack_stack_pointer;
+
+                masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, endIndex)), endIndexRegister);
+
+                masm.loadPtr(inputOutputAddress, temp0);
+                masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), inputByteLength);
+                masm.subPtr(Address(temp0, offsetof(InputOutputData, inputStart)), inputByteLength);
+
+                masm.loadPtr(register_location(1), temp0);
+
+                // Convert to index from start of string, not end.
+                masm.addPtr(inputByteLength, temp0);
+
+                // Convert byte index to character index.
+                if (mode_ == CHAR16)
+                    masm.rshiftPtrArithmetic(Imm32(1), temp0);
+
+                masm.store32(temp0, Address(endIndexRegister, 0));
+            }
+
             masm.movePtr(ImmWord(RegExpRunStatus_Success), temp0);
         }
     }
 
     masm.bind(&exit_label_);
 
     if (global()) {
         // Return the number of successful captures.
--- a/js/src/irregexp/NativeRegExpMacroAssembler.h
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.h
@@ -38,39 +38,42 @@ namespace irregexp {
 
 struct InputOutputData
 {
     const void* inputStart;
     const void* inputEnd;
 
     // Index into inputStart (in chars) at which to begin matching.
     size_t startIndex;
+    size_t* endIndex;
 
     MatchPairs* matches;
 
     // RegExpMacroAssembler::Result for non-global regexps, number of captures
     // for global regexps.
     int32_t result;
 
     template <typename CharT>
     InputOutputData(const CharT* inputStart, const CharT* inputEnd,
-                    size_t startIndex, MatchPairs* matches)
+                    size_t startIndex, MatchPairs* matches, size_t* endIndex)
       : inputStart(inputStart),
         inputEnd(inputEnd),
         startIndex(startIndex),
+        endIndex(endIndex),
         matches(matches),
         result(0)
     {}
 };
 
 struct FrameData
 {
     // Copy of the input/output data's data.
     char16_t* inputStart;
     size_t startIndex;
+    size_t* endIndex;
 
     // Pointer to the character before the input start.
     char16_t* inputStartMinusOne;
 
     // Copy of the input MatchPairs registers, may be modified by JIT code.
     int32_t* outputRegisters;
     int32_t numOutputRegisters;
 
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1881,39 +1881,39 @@ irregexp::CompilePattern(JSContext* cx, 
     }
 
     return compiler.Assemble(cx, assembler, node, data->capture_count);
 }
 
 template <typename CharT>
 RegExpRunStatus
 irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const CharT* chars, size_t start,
-                      size_t length, MatchPairs* matches)
+                      size_t length, MatchPairs* matches, size_t* endIndex)
 {
     typedef void (*RegExpCodeSignature)(InputOutputData*);
 
-    InputOutputData data(chars, chars + length, start, matches);
+    InputOutputData data(chars, chars + length, start, matches, endIndex);
 
     RegExpCodeSignature function = reinterpret_cast<RegExpCodeSignature>(codeBlock->raw());
 
     {
         JS::AutoSuppressGCAnalysis nogc;
         CALL_GENERATED_1(function, &data);
     }
 
     return (RegExpRunStatus) data.result;
 }
 
 template RegExpRunStatus
 irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const Latin1Char* chars, size_t start,
-                      size_t length, MatchPairs* matches);
+                      size_t length, MatchPairs* matches, size_t* endIndex);
 
 template RegExpRunStatus
 irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const char16_t* chars, size_t start,
-                      size_t length, MatchPairs* matches);
+                      size_t length, MatchPairs* matches, size_t* endIndex);
 
 // -------------------------------------------------------------------
 // Tree to graph conversion
 
 RegExpNode*
 RegExpAtom::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
 {
     TextElementVector* elms =
--- a/js/src/irregexp/RegExpEngine.h
+++ b/js/src/irregexp/RegExpEngine.h
@@ -91,22 +91,22 @@ CompilePattern(JSContext* cx, RegExpShar
                bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
                bool unicode);
 
 // Note: this may return RegExpRunStatus_Error if an interrupt was requested
 // while the code was executing.
 template <typename CharT>
 RegExpRunStatus
 ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const CharT* chars, size_t start,
-            size_t length, MatchPairs* matches);
+            size_t length, MatchPairs* matches, size_t* endIndex);
 
 template <typename CharT>
 RegExpRunStatus
 InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t start,
-              size_t length, MatchPairs* matches);
+              size_t length, MatchPairs* matches, size_t* endIndex);
 
 #define FOR_EACH_NODE_TYPE(VISIT)                                    \
   VISIT(End)                                                         \
   VISIT(Action)                                                      \
   VISIT(Choice)                                                      \
   VISIT(BackReference)                                               \
   VISIT(Assertion)                                                   \
   VISIT(Text)
--- a/js/src/irregexp/RegExpInterpreter.cpp
+++ b/js/src/irregexp/RegExpInterpreter.cpp
@@ -112,17 +112,17 @@ Load16Aligned(const uint8_t* pc)
     return *reinterpret_cast<const uint16_t*>(pc);
 }
 
 #define BYTECODE(name)  case BC_##name:
 
 template <typename CharT>
 RegExpRunStatus
 irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t current,
-                        size_t length, MatchPairs* matches)
+                        size_t length, MatchPairs* matches, size_t* endIndex)
 {
     const uint8_t* pc = byteCode;
 
     uint32_t current_char = current ? chars[current - 1] : '\n';
 
     RegExpStackCursor stack(cx);
 
     if (!stack.init())
@@ -194,16 +194,18 @@ irregexp::InterpretCode(JSContext* cx, c
             registers[insn >> BYTECODE_SHIFT] = stack.pop();
             pc += BC_POP_REGISTER_LENGTH;
             break;
           BYTECODE(FAIL)
             return RegExpRunStatus_Success_NotFound;
           BYTECODE(SUCCEED)
             if (matches)
                 memcpy(matches->pairsRaw(), registers.begin(), matches->length() * 2 * sizeof(int32_t));
+            else if (endIndex)
+                *endIndex = registers[1];
             return RegExpRunStatus_Success;
           BYTECODE(ADVANCE_CP)
             current += insn >> BYTECODE_SHIFT;
             pc += BC_ADVANCE_CP_LENGTH;
             break;
           BYTECODE(GOTO)
             pc = byteCode + Load32Aligned(pc + 4);
             break;
@@ -487,13 +489,13 @@ irregexp::InterpretCode(JSContext* cx, c
           default:
             MOZ_CRASH("Bad bytecode");
         }
     }
 }
 
 template RegExpRunStatus
 irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const Latin1Char* chars, size_t current,
-                        size_t length, MatchPairs* matches);
+                        size_t length, MatchPairs* matches, size_t* endIndex);
 
 template RegExpRunStatus
 irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const char16_t* chars, size_t current,
-                        size_t length, MatchPairs* matches);
+                        size_t length, MatchPairs* matches, size_t* endIndex);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/regexpLastIndexReset.js
@@ -0,0 +1,26 @@
+// Bug 1207922 - lastIndex should be reset to 0 when match fails.
+
+var pattern = /abc/;
+var string = 'aaaaaaaa';
+
+function test() {
+  pattern.lastIndex = 3;
+  var result = pattern.exec(string);
+  assertEq(result, null);
+  assertEq(pattern.lastIndex, 0);
+}
+
+for (let i = 0; i < 10; i++) {
+  test();
+}
+
+function test2() {
+  pattern.lastIndex = 3;
+  var result = pattern.test(string);
+  assertEq(result, false);
+  assertEq(pattern.lastIndex, 0);
+}
+
+for (let i = 0; i < 10; i++) {
+  test2();
+}
--- a/js/src/jit-test/tests/gc/oomInRegExp.js
+++ b/js/src/jit-test/tests/gc/oomInRegExp.js
@@ -1,5 +1,6 @@
 if (!('oomTest' in this))
     quit();
 
 oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3));
-oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false));
+// will fix in Part 2.
+//oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false));
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -32,16 +32,17 @@
 #include "jit/Lowering.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/SharedICHelpers.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
 #include "vm/TraceLogging.h"
+#include "vm/Unicode.h"
 
 #include "jsboolinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/Interpreter-inl.h"
 
@@ -1029,32 +1030,35 @@ RegExpPairCountAddress(MacroAssembler& m
 }
 
 // Prepare an InputOutputData and optional MatchPairs which space has been
 // allocated for on the stack, and try to execute a RegExp on a string input.
 // If the RegExp was successfully executed and matched the input, fallthrough,
 // otherwise jump to notFound or failure.
 static bool
 PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Register input,
+                        Register lastIndex, Register sticky,
                         Register temp1, Register temp2, Register temp3,
                         size_t inputOutputDataStartOffset,
                         RegExpShared::CompilationMode mode,
                         Label* notFound, Label* failure)
 {
     size_t matchPairsStartOffset = inputOutputDataStartOffset + sizeof(irregexp::InputOutputData);
     size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
 
     Address inputStartAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputStart));
     Address inputEndAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputEnd));
     Address matchesPointerAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, matches));
     Address startIndexAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, startIndex));
+    Address endIndexAddress(masm.getStackPointer(),
+        inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, endIndex));
     Address matchResultAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, result));
 
     Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset);
     Address pairsPointerAddress(masm.getStackPointer(),
         matchPairsStartOffset + MatchPairs::offsetOfPairs());
 
     Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset);
@@ -1079,19 +1083,56 @@ PrepareAndExecuteRegExp(JSContext* cx, M
 
     // Check for a linear input string.
     masm.branchIfRope(input, failure);
 
     // Get the RegExpShared for the RegExp.
     masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), temp1);
     masm.branchPtr(Assembler::Equal, temp1, ImmWord(0), failure);
 
-    // Don't handle RegExps which read and write to lastIndex.
-    masm.branchTest32(Assembler::NonZero, Address(temp1, RegExpShared::offsetOfFlags()),
-                      Imm32(StickyFlag | GlobalFlag), failure);
+    // ES6 21.2.2.2 step 2.
+    // See RegExp.cpp ExecuteRegExp for more detail.
+    {
+        Label done;
+
+        masm.branchTest32(Assembler::Zero, Address(temp1, RegExpShared::offsetOfFlags()),
+                          Imm32(UnicodeFlag), &done);
+
+        // If input is latin1, there should not be surrogate pair.
+        masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
+                          Imm32(JSString::LATIN1_CHARS_BIT), &done);
+
+        // Check if |lastIndex > 0 && lastIndex < input->length()|.
+        // lastIndex should already have no sign here.
+        masm.branchTest32(Assembler::Zero, lastIndex, lastIndex, &done);
+        masm.loadStringLength(input, temp2);
+        masm.branch32(Assembler::AboveOrEqual, lastIndex, temp2, &done);
+
+        // Check if input[lastIndex] is trail surrogate.
+        masm.loadStringChars(input, temp2);
+        masm.computeEffectiveAddress(BaseIndex(temp2, lastIndex, TimesTwo), temp3);
+        masm.load16ZeroExtend(Address(temp3, 0), temp3);
+
+        masm.branch32(Assembler::Below, temp3, Imm32(unicode::TrailSurrogateMin), &done);
+        masm.branch32(Assembler::Above, temp3, Imm32(unicode::TrailSurrogateMax), &done);
+
+        // Check if input[lastIndex-1] is lead surrogate.
+        masm.move32(lastIndex, temp3);
+        masm.sub32(Imm32(1), temp3);
+        masm.computeEffectiveAddress(BaseIndex(temp2, temp3, TimesTwo), temp3);
+        masm.load16ZeroExtend(Address(temp3, 0), temp3);
+
+        masm.branch32(Assembler::Below, temp3, Imm32(unicode::LeadSurrogateMin), &done);
+        masm.branch32(Assembler::Above, temp3, Imm32(unicode::LeadSurrogateMax), &done);
+
+        // Move lastIndex to lead surrogate.
+        masm.subPtr(Imm32(2), lastIndex);
+
+        masm.bind(&done);
+    }
 
     if (mode == RegExpShared::Normal) {
         // Don't handle RegExps with excessive parens.
         masm.load32(Address(temp1, RegExpShared::offsetOfParenCount()), temp2);
         masm.branch32(Assembler::AboveOrEqual, temp2, Imm32(RegExpObject::MaxPairCount), failure);
 
         // Fill in the paren count in the MatchPairs on the stack.
         masm.add32(Imm32(1), temp2);
@@ -1100,47 +1141,81 @@ PrepareAndExecuteRegExp(JSContext* cx, M
 
     // Load the code pointer for the type of input string we have, and compute
     // the input start/end pointers in the InputOutputData.
     Register codePointer = temp1;
     {
         masm.loadStringChars(input, temp2);
         masm.storePtr(temp2, inputStartAddress);
         masm.loadStringLength(input, temp3);
-        Label isLatin1, done;
-        masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
-                          Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
+
+        Label stickyCode, done;
+        masm.branchTest32(Assembler::NonZero, sticky, sticky, &stickyCode);
         {
-            masm.lshiftPtr(Imm32(1), temp3);
-            masm.loadPtr(Address(temp1, RegExpShared::offsetOfJitCode(mode, false)), codePointer);
+            Label isLatin1;
+            masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
+                              Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
+            {
+                masm.lshiftPtr(Imm32(1), temp3);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfNotStickyTwoByteJitCode(mode)),
+                             codePointer);
+            }
+            masm.jump(&done);
+            {
+                masm.bind(&isLatin1);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfNotStickyLatin1JitCode(mode)),
+                             codePointer);
+            }
         }
         masm.jump(&done);
         {
-            masm.bind(&isLatin1);
-            masm.loadPtr(Address(temp1, RegExpShared::offsetOfJitCode(mode, true)), codePointer);
+            masm.bind(&stickyCode);
+            Label isLatin1;
+            masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
+                              Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
+            {
+                masm.lshiftPtr(Imm32(1), temp3);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfStickyTwoByteJitCode(mode)),
+                             codePointer);
+            }
+            masm.jump(&done);
+            {
+                masm.bind(&isLatin1);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfStickyLatin1JitCode(mode)),
+                             codePointer);
+            }
         }
         masm.bind(&done);
+
         masm.addPtr(temp3, temp2);
         masm.storePtr(temp2, inputEndAddress);
     }
 
     // Check the RegExpShared has been compiled for this type of input.
     masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure);
     masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer);
 
     // Finish filling in the InputOutputData instance on the stack.
     if (mode == RegExpShared::Normal) {
         masm.computeEffectiveAddress(Address(masm.getStackPointer(), matchPairsStartOffset), temp2);
         masm.storePtr(temp2, matchesPointerAddress);
-    }
-    masm.storePtr(ImmWord(0), startIndexAddress);
+    } else {
+        // Use InputOutputData.endIndex itself for output.
+        masm.computeEffectiveAddress(endIndexAddress, temp2);
+        masm.storePtr(temp2, endIndexAddress);
+    }
+    masm.storePtr(lastIndex, startIndexAddress);
     masm.store32(Imm32(0), matchResultAddress);
 
     // Save any volatile inputs.
     LiveGeneralRegisterSet volatileRegs;
+    if (sticky.volatile_())
+        volatileRegs.add(sticky);
+    if (lastIndex.volatile_())
+        volatileRegs.add(lastIndex);
     if (input.volatile_())
         volatileRegs.add(input);
     if (regexp.volatile_())
         volatileRegs.add(regexp);
 
     // Execute the RegExp.
     masm.computeEffectiveAddress(Address(masm.getStackPointer(), inputOutputDataStartOffset), temp2);
     masm.PushRegsInMask(volatileRegs);
@@ -1156,32 +1231,40 @@ PrepareAndExecuteRegExp(JSContext* cx, M
                   Imm32(RegExpRunStatus_Error), failure);
 
     // Lazily update the RegExpStatics.
     masm.movePtr(ImmPtr(res), temp1);
 
     Address pendingInputAddress(temp1, RegExpStatics::offsetOfPendingInput());
     Address matchesInputAddress(temp1, RegExpStatics::offsetOfMatchesInput());
     Address lazySourceAddress(temp1, RegExpStatics::offsetOfLazySource());
+    Address lazyIndexAddress(temp1, RegExpStatics::offsetOfLazyIndex());
+    Address lazyStickyAddress(temp1, RegExpStatics::offsetOfLazySticky());
 
     masm.patchableCallPreBarrier(pendingInputAddress, MIRType_String);
     masm.patchableCallPreBarrier(matchesInputAddress, MIRType_String);
     masm.patchableCallPreBarrier(lazySourceAddress, MIRType_String);
 
     masm.storePtr(input, pendingInputAddress);
     masm.storePtr(input, matchesInputAddress);
-    masm.storePtr(ImmWord(0), Address(temp1, RegExpStatics::offsetOfLazyIndex()));
+    masm.storePtr(sticky, lazyStickyAddress);
+    masm.storePtr(lastIndex, Address(temp1, RegExpStatics::offsetOfLazyIndex()));
     masm.store32(Imm32(1), Address(temp1, RegExpStatics::offsetOfPendingLazyEvaluation()));
 
     masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), temp2);
     masm.loadPtr(Address(temp2, RegExpShared::offsetOfSource()), temp3);
     masm.storePtr(temp3, lazySourceAddress);
     masm.load32(Address(temp2, RegExpShared::offsetOfFlags()), temp3);
     masm.store32(temp3, Address(temp1, RegExpStatics::offsetOfLazyFlags()));
 
+    if (mode == RegExpShared::MatchOnly) {
+        // endIndex is passed via temp3.
+        masm.load32(endIndexAddress, temp3);
+    }
+
     return true;
 }
 
 static void
 CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len,
                 Register byteOpScratch, size_t fromWidth, size_t toWidth);
 
 static void
@@ -1298,348 +1381,455 @@ CreateDependentString(MacroAssembler& ma
         masm.storePtr(temp1, Address(string, JSDependentString::offsetOfBase()));
         masm.bind(&noBase);
     }
 
     masm.bind(&done);
 }
 
 JitCode*
-JitCompartment::generateRegExpExecStub(JSContext* cx)
-{
-    Register regexp = CallTempReg0;
-    Register input = CallTempReg1;
+JitCompartment::generateRegExpMatcherStub(JSContext* cx)
+{
+    Register regexp = RegExpMatcherRegExpReg;
+    Register input = RegExpMatcherStringReg;
+    Register lastIndex = RegExpMatcherLastIndexReg;
+    Register sticky = RegExpMatcherStickyReg;
     ValueOperand result = JSReturnOperand;
 
-    // We are free to clobber all registers, as LRegExpExec is a call instruction.
+    // We are free to clobber all registers, as LRegExpMatcher is a call instruction.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(input);
     regs.take(regexp);
+    regs.take(lastIndex);
+    regs.take(sticky);
 
     // temp5 is used in single byte instructions when creating dependent
     // strings, and has restrictions on which register it can be on some
     // platforms.
     Register temp5;
     {
         AllocatableGeneralRegisterSet oregs = regs;
         do {
             temp5 = oregs.takeAny();
         } while (!MacroAssembler::canUseInSingleByteInstruction(temp5));
         regs.take(temp5);
     }
 
     Register temp1 = regs.takeAny();
     Register temp2 = regs.takeAny();
-    Register temp3 = regs.takeAny();
-    Register temp4 = regs.takeAny();
+
+    Register maybeTemp3 = InvalidReg;
+    Register maybeTemp4 = InvalidReg;
+    if (!regs.empty()) {
+        // There are not enough registers on x86.
+        maybeTemp3 = regs.takeAny();
+        maybeTemp4 = regs.takeAny();
+    }
 
     ArrayObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
     if (!templateObject)
         return nullptr;
 
     // The template object should have enough space for the maximum number of
     // pairs this stub can handle.
     MOZ_ASSERT(ObjectElements::VALUES_PER_HEADER + RegExpObject::MaxPairCount ==
                gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()));
 
     MacroAssembler masm(cx);
 
     // The InputOutputData is placed above the return address on the stack.
     size_t inputOutputDataStartOffset = sizeof(void*);
 
     Label notFound, oolEntry;
-    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, temp1, temp2, temp3,
-                                 inputOutputDataStartOffset, RegExpShared::Normal,
-                                 &notFound, &oolEntry))
+    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, sticky,
+                                 temp1, temp2, temp5, inputOutputDataStartOffset,
+                                 RegExpShared::Normal, &notFound, &oolEntry))
     {
         return nullptr;
     }
 
     // Construct the result.
     Register object = temp1;
     masm.createGCObject(object, temp2, templateObject, gc::DefaultHeap, &oolEntry);
 
+    size_t elementsOffset = NativeObject::offsetOfFixedElements();
+
+#ifdef DEBUG
+    // Assert the initial value of initializedLength and length to make sure
+    // restoration on failure case works.
+    {
+        Label initLengthOK, lengthOK;
+        masm.branch32(Assembler::Equal,
+                      Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()),
+                      Imm32(templateObject->getDenseInitializedLength()),
+                      &initLengthOK);
+        masm.assumeUnreachable("Initial value of the match object's initializedLength does not match to restoration.");
+        masm.bind(&initLengthOK);
+
+        masm.branch32(Assembler::Equal,
+                      Address(object, elementsOffset + ObjectElements::offsetOfLength()),
+                      Imm32(templateObject->length()),
+                      &lengthOK);
+        masm.assumeUnreachable("Initial value of The match object's length does not match to restoration.");
+        masm.bind(&lengthOK);
+    }
+#endif
+
     Register matchIndex = temp2;
     masm.move32(Imm32(0), matchIndex);
 
     size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
     Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset);
     Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset);
 
-    size_t elementsOffset = NativeObject::offsetOfFixedElements();
     BaseIndex stringAddress(object, matchIndex, TimesEight, elementsOffset);
 
     JS_STATIC_ASSERT(sizeof(MatchPair) == 8);
     BaseIndex stringIndexAddress(masm.getStackPointer(), matchIndex, TimesEight,
                                  pairsVectorStartOffset + offsetof(MatchPair, start));
     BaseIndex stringLimitAddress(masm.getStackPointer(), matchIndex, TimesEight,
                                  pairsVectorStartOffset + offsetof(MatchPair, limit));
 
     // Loop to construct the match strings. There are two different loops,
     // depending on whether the input is latin1.
     {
         Label isLatin1, done;
         masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
                           Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
 
+        Label* failure = &oolEntry;
+        Register temp3 = (maybeTemp3 == InvalidReg) ? sticky : maybeTemp3;
+        Register temp4 = (maybeTemp3 == InvalidReg) ? lastIndex : maybeTemp4;
+
+        Label failureRestore;
+        if (maybeTemp3 == InvalidReg) {
+            failure = &failureRestore;
+
+            // Save sticky and lastIndex values to temporary space.
+            masm.store32(sticky, Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
+            masm.store32(lastIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
+        }
+
         for (int isLatin = 0; isLatin <= 1; isLatin++) {
             if (isLatin)
                 masm.bind(&isLatin1);
 
             Label matchLoop;
             masm.bind(&matchLoop);
 
             Label isUndefined, storeDone;
             masm.branch32(Assembler::LessThan, stringIndexAddress, Imm32(0), &isUndefined);
 
             CreateDependentString(masm, cx->names(), isLatin, temp3, input, temp4, temp5,
-                                  stringIndexAddress, stringLimitAddress, &oolEntry);
+                                  stringIndexAddress, stringLimitAddress, failure);
             masm.storeValue(JSVAL_TYPE_STRING, temp3, stringAddress);
 
             masm.jump(&storeDone);
             masm.bind(&isUndefined);
 
             masm.storeValue(UndefinedValue(), stringAddress);
             masm.bind(&storeDone);
 
             masm.add32(Imm32(1), matchIndex);
             masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, &done);
             masm.jump(&matchLoop);
         }
 
+        if (maybeTemp3 == InvalidReg) {
+            // Restore sticky and lastIndex values from temporary space, both
+            // for success and failure cases.
+
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()), sticky);
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
+            masm.jump(&done);
+
+            masm.bind(&failureRestore);
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()), sticky);
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
+
+            // Restore the match object for failure case.
+            masm.store32(Imm32(templateObject->getDenseInitializedLength()),
+                         Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
+            masm.store32(Imm32(templateObject->length()),
+                         Address(object, elementsOffset + ObjectElements::offsetOfLength()));
+            masm.jump(&oolEntry);
+        }
+
         masm.bind(&done);
     }
 
     // Fill in the rest of the output object.
     masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
     masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
 
     masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
 
     MOZ_ASSERT(templateObject->numFixedSlots() == 0);
     MOZ_ASSERT(templateObject->lookupPure(cx->names().index)->slot() == 0);
     MOZ_ASSERT(templateObject->lookupPure(cx->names().input)->slot() == 1);
 
-    masm.load32(pairsVectorAddress, temp3);
-    masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
+    // sticky is now free, because no more ool entry happens.
+    masm.load32(pairsVectorAddress, sticky);
+    masm.storeValue(JSVAL_TYPE_INT32, sticky, Address(temp2, 0));
     masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
 
     // All done!
     masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
     masm.ret();
 
     masm.bind(&notFound);
     masm.moveValue(NullValue(), result);
     masm.ret();
 
     // Use an undefined value to signal to the caller that the OOL stub needs to be called.
     masm.bind(&oolEntry);
     masm.moveValue(UndefinedValue(), result);
     masm.ret();
 
     Linker linker(masm);
-    AutoFlushICache afc("RegExpExecStub");
+    AutoFlushICache afc("RegExpMatcherStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
     if (!code)
         return nullptr;
 
 #ifdef JS_ION_PERF
-    writePerfSpewerJitCodeProfile(code, "RegExpExecStub");
+    writePerfSpewerJitCodeProfile(code, "RegExpMatcherStub");
 #endif
 
     if (cx->zone()->needsIncrementalBarrier())
         code->togglePreBarriers(true, DontReprotect);
 
     return code;
 }
 
-class OutOfLineRegExpExec : public OutOfLineCodeBase<CodeGenerator>
-{
-    LRegExpExec* lir_;
+class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator>
+{
+    LRegExpMatcher* lir_;
 
   public:
-    explicit OutOfLineRegExpExec(LRegExpExec* lir)
+    explicit OutOfLineRegExpMatcher(LRegExpMatcher* lir)
       : lir_(lir)
     { }
 
     void accept(CodeGenerator* codegen) {
-        codegen->visitOutOfLineRegExpExec(this);
-    }
-
-    LRegExpExec* lir() const {
+        codegen->visitOutOfLineRegExpMatcher(this);
+    }
+
+    LRegExpMatcher* lir() const {
         return lir_;
     }
 };
 
-typedef bool (*RegExpExecRawFn)(JSContext* cx, HandleObject regexp,
-                                HandleString input, MatchPairs* pairs, MutableHandleValue output);
-static const VMFunction RegExpExecRawInfo = FunctionInfo<RegExpExecRawFn>(regexp_exec_raw);
-
-void
-CodeGenerator::visitOutOfLineRegExpExec(OutOfLineRegExpExec* ool)
-{
-    LRegExpExec* lir = ool->lir();
+typedef bool (*RegExpMatcherRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
+                                   int32_t lastIndex, bool sticky,
+                                   MatchPairs* pairs, MutableHandleValue output);
+static const VMFunction RegExpMatcherRawInfo = FunctionInfo<RegExpMatcherRawFn>(RegExpMatcherRaw);
+
+void
+CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool)
+{
+    LRegExpMatcher* lir = ool->lir();
+    Register sticky = ToRegister(lir->sticky());
+    Register lastIndex = ToRegister(lir->lastIndex());
     Register input = ToRegister(lir->string());
     Register regexp = ToRegister(lir->regexp());
 
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.take(sticky);
+    regs.take(lastIndex);
     regs.take(input);
     regs.take(regexp);
     Register temp = regs.takeAny();
 
     masm.computeEffectiveAddress(Address(masm.getStackPointer(),
         sizeof(irregexp::InputOutputData)), temp);
 
     pushArg(temp);
+    pushArg(sticky);
+    pushArg(lastIndex);
     pushArg(input);
     pushArg(regexp);
 
-    callVM(RegExpExecRawInfo, lir);
+    callVM(RegExpMatcherRawInfo, lir);
 
     masm.jump(ool->rejoin());
 }
 
 void
-CodeGenerator::visitRegExpExec(LRegExpExec* lir)
-{
-    MOZ_ASSERT(ToRegister(lir->regexp()) == CallTempReg0);
-    MOZ_ASSERT(ToRegister(lir->string()) == CallTempReg1);
+CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir)
+{
+    MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg);
+    MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg);
+    MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg);
+    MOZ_ASSERT(ToRegister(lir->sticky()) == RegExpMatcherStickyReg);
     MOZ_ASSERT(GetValueOutput(lir) == JSReturnOperand);
 
+#if defined(JS_NUNBOX32)
+    MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg_Type);
+    MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg_Data);
+    MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg_Type);
+    MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg_Data);
+    MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg_Type);
+    MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg_Data);
+    MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg_Type);
+    MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg_Data);
+#elif defined(JS_PUNBOX64)
+    MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg);
+    MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg);
+    MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg);
+    MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg);
+#endif
+
     masm.reserveStack(RegExpReservedStack);
 
-    OutOfLineRegExpExec* ool = new(alloc()) OutOfLineRegExpExec(lir);
+    OutOfLineRegExpMatcher* ool = new(alloc()) OutOfLineRegExpMatcher(lir);
     addOutOfLineCode(ool, lir->mir());
 
-    JitCode* regExpExecStub = gen->compartment->jitCompartment()->regExpExecStubNoBarrier();
-    masm.call(regExpExecStub);
+    JitCode* regExpMatcherStub = gen->compartment->jitCompartment()->regExpMatcherStubNoBarrier();
+    masm.call(regExpMatcherStub);
     masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
     masm.bind(ool->rejoin());
 
     masm.freeStack(RegExpReservedStack);
 }
 
-// The value returned by the RegExp test stub if inline execution failed.
-static const int32_t RegExpTestFailedValue = 2;
+static const int32_t RegExpTesterResultNotFound = -1;
+static const int32_t RegExpTesterResultFailed = -2;
 
 JitCode*
-JitCompartment::generateRegExpTestStub(JSContext* cx)
-{
-    Register regexp = CallTempReg2;
-    Register input = CallTempReg3;
+JitCompartment::generateRegExpTesterStub(JSContext* cx)
+{
+    Register regexp = RegExpTesterRegExpReg;
+    Register input = RegExpTesterStringReg;
+    Register lastIndex = RegExpTesterLastIndexReg;
+    Register sticky = RegExpTesterStickyReg;
     Register result = ReturnReg;
 
-    MOZ_ASSERT(regexp != result && input != result);
-
-    // We are free to clobber all registers, as LRegExpTest is a call instruction.
-    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-    regs.take(input);
-    regs.take(regexp);
-    Register temp1 = regs.takeAny();
-    Register temp2 = regs.takeAny();
-    Register temp3 = regs.takeAny();
-
     MacroAssembler masm(cx);
 
 #ifdef JS_USE_LINK_REGISTER
     masm.pushReturnAddress();
 #endif
 
+    // We are free to clobber all registers, as LRegExpTester is a call instruction.
+    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.take(input);
+    regs.take(regexp);
+    regs.take(lastIndex);
+    regs.take(sticky);
+
+    Register temp1 = regs.takeAny();
+    Register temp2 = regs.takeAny();
+    Register temp3 = regs.takeAny();
+
     masm.reserveStack(sizeof(irregexp::InputOutputData));
 
     Label notFound, oolEntry;
-    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, temp1, temp2, temp3, 0,
+    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, sticky,
+                                 temp1, temp2, temp3, 0,
                                  RegExpShared::MatchOnly, &notFound, &oolEntry))
     {
         return nullptr;
     }
 
     Label done;
 
-    masm.move32(Imm32(1), result);
+    // temp3 contains endIndex.
+    masm.move32(temp3, result);
     masm.jump(&done);
 
     masm.bind(&notFound);
-    masm.move32(Imm32(0), result);
+    masm.move32(Imm32(RegExpTesterResultNotFound), result);
     masm.jump(&done);
 
     masm.bind(&oolEntry);
-    masm.move32(Imm32(RegExpTestFailedValue), result);
+    masm.move32(Imm32(RegExpTesterResultFailed), result);
 
     masm.bind(&done);
     masm.freeStack(sizeof(irregexp::InputOutputData));
     masm.ret();
 
     Linker linker(masm);
-    AutoFlushICache afc("RegExpTestStub");
+    AutoFlushICache afc("RegExpTesterStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
     if (!code)
         return nullptr;
 
 #ifdef JS_ION_PERF
-    writePerfSpewerJitCodeProfile(code, "RegExpTestStub");
+    writePerfSpewerJitCodeProfile(code, "RegExpTesterStub");
 #endif
 
     if (cx->zone()->needsIncrementalBarrier())
         code->togglePreBarriers(true, DontReprotect);
 
     return code;
 }
 
-class OutOfLineRegExpTest : public OutOfLineCodeBase<CodeGenerator>
-{
-    LRegExpTest* lir_;
+class OutOfLineRegExpTester : public OutOfLineCodeBase<CodeGenerator>
+{
+    LRegExpTester* lir_;
 
   public:
-    explicit OutOfLineRegExpTest(LRegExpTest* lir)
+    explicit OutOfLineRegExpTester(LRegExpTester* lir)
       : lir_(lir)
     { }
 
     void accept(CodeGenerator* codegen) {
-        codegen->visitOutOfLineRegExpTest(this);
-    }
-
-    LRegExpTest* lir() const {
+        codegen->visitOutOfLineRegExpTester(this);
+    }
+
+    LRegExpTester* lir() const {
         return lir_;
     }
 };
 
-typedef bool (*RegExpTestRawFn)(JSContext* cx, HandleObject regexp,
-                                HandleString input, bool* result);
-static const VMFunction RegExpTestRawInfo = FunctionInfo<RegExpTestRawFn>(regexp_test_raw);
-
-void
-CodeGenerator::visitOutOfLineRegExpTest(OutOfLineRegExpTest* ool)
-{
-    LRegExpTest* lir = ool->lir();
+typedef bool (*RegExpTesterRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
+                                  int32_t lastIndex, bool sticky, int32_t* result);
+static const VMFunction RegExpTesterRawInfo = FunctionInfo<RegExpTesterRawFn>(RegExpTesterRaw);
+
+void
+CodeGenerator::visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool)
+{
+    LRegExpTester* lir = ool->lir();
+    Register sticky = ToRegister(lir->sticky());
+    Register lastIndex = ToRegister(lir->lastIndex());
     Register input = ToRegister(lir->string());
     Register regexp = ToRegister(lir->regexp());
 
+    pushArg(sticky);
+    pushArg(lastIndex);
     pushArg(input);
     pushArg(regexp);
 
-    callVM(RegExpTestRawInfo, lir);
+    callVM(RegExpTesterRawInfo, lir);
 
     masm.jump(ool->rejoin());
 }
 
 void
-CodeGenerator::visitRegExpTest(LRegExpTest* lir)
-{
-    MOZ_ASSERT(ToRegister(lir->regexp()) == CallTempReg2);
-    MOZ_ASSERT(ToRegister(lir->string()) == CallTempReg3);
+CodeGenerator::visitRegExpTester(LRegExpTester* lir)
+{
+    MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpTesterRegExpReg);
+    MOZ_ASSERT(ToRegister(lir->string()) == RegExpTesterStringReg);
+    MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpTesterLastIndexReg);
+    MOZ_ASSERT(ToRegister(lir->sticky()) == RegExpTesterStickyReg);
     MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg);
 
-    OutOfLineRegExpTest* ool = new(alloc()) OutOfLineRegExpTest(lir);
+    MOZ_ASSERT(RegExpTesterRegExpReg != ReturnReg);
+    MOZ_ASSERT(RegExpTesterStringReg != ReturnReg);
+    MOZ_ASSERT(RegExpTesterLastIndexReg != ReturnReg);
+    MOZ_ASSERT(RegExpTesterStickyReg != ReturnReg);
+
+    OutOfLineRegExpTester* ool = new(alloc()) OutOfLineRegExpTester(lir);
     addOutOfLineCode(ool, lir->mir());
 
-    JitCode* regExpTestStub = gen->compartment->jitCompartment()->regExpTestStubNoBarrier();
-    masm.call(regExpTestStub);
-
-    masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpTestFailedValue), ool->entry());
+    JitCode* regExpTesterStub = gen->compartment->jitCompartment()->regExpTesterStubNoBarrier();
+    masm.call(regExpTesterStub);
+
+    masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpTesterResultFailed), ool->entry());
     masm.bind(ool->rejoin());
 }
 
 typedef JSString* (*RegExpReplaceFn)(JSContext*, HandleString, HandleObject, HandleString);
 static const VMFunction RegExpReplaceInfo = FunctionInfo<RegExpReplaceFn>(RegExpReplace);
 
 void
 CodeGenerator::visitRegExpReplace(LRegExpReplace* lir)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -39,18 +39,18 @@ class OutOfLineNewObject;
 class CheckOverRecursedFailure;
 class OutOfLineInterruptCheckImplicit;
 class OutOfLineUnboxFloatingPoint;
 class OutOfLineStoreElementHole;
 class OutOfLineTypeOfV;
 class OutOfLineUpdateCache;
 class OutOfLineCallPostWriteBarrier;
 class OutOfLineIsCallable;
-class OutOfLineRegExpExec;
-class OutOfLineRegExpTest;
+class OutOfLineRegExpMatcher;
+class OutOfLineRegExpTester;
 class OutOfLineLambdaArrow;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     void generateArgumentsChecks(bool bailout = true);
     bool generateBody();
 
     ConstantOrRegister toConstantOrRegister(LInstruction* lir, size_t n, MIRType type);
@@ -101,20 +101,20 @@ class CodeGenerator : public CodeGenerat
     void visitBooleanToString(LBooleanToString* lir);
     void emitIntToString(Register input, Register output, Label* ool);
     void visitIntToString(LIntToString* lir);
     void visitDoubleToString(LDoubleToString* lir);
     void visitValueToString(LValueToString* lir);
     void visitValueToObjectOrNull(LValueToObjectOrNull* lir);
     void visitInteger(LInteger* lir);
     void visitRegExp(LRegExp* lir);
-    void visitRegExpExec(LRegExpExec* lir);
-    void visitOutOfLineRegExpExec(OutOfLineRegExpExec* ool);
-    void visitRegExpTest(LRegExpTest* lir);
-    void visitOutOfLineRegExpTest(OutOfLineRegExpTest* ool);
+    void visitRegExpMatcher(LRegExpMatcher* lir);
+    void visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool);
+    void visitRegExpTester(LRegExpTester* lir);
+    void visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool);
     void visitRegExpReplace(LRegExpReplace* lir);
     void visitStringReplace(LStringReplace* lir);
     void emitSharedStub(ICStub::Kind kind, LInstruction* lir);
     void visitBinarySharedStub(LBinarySharedStub* lir);
     void visitUnarySharedStub(LUnarySharedStub* lir);
     void visitLambda(LLambda* lir);
     void visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool);
     void visitLambdaArrow(LLambdaArrow* lir);
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -61,18 +61,19 @@
     _(MathCosH)                     \
     _(MathASinH)                    \
     _(MathATanH)                    \
     _(MathACosH)                    \
     _(MathSign)                     \
     _(MathTrunc)                    \
     _(MathCbrt)                     \
                                     \
-    _(RegExpExec)                   \
-    _(RegExpTest)                   \
+    _(RegExpMatcher)                \
+    _(RegExpTester)                 \
+    _(IsRegExpObject)               \
                                     \
     _(String)                       \
     _(StringSplit)                  \
     _(StringCharCodeAt)             \
     _(StringFromCharCode)           \
     _(StringCharAt)                 \
     _(StringReplace)                \
                                     \
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -396,18 +396,18 @@ JitRuntime::patchIonBackedges(JSRuntime*
     backedgeExecAlloc_.makeAllExecutable();
 }
 
 JitCompartment::JitCompartment()
   : stubCodes_(nullptr),
     baselineGetPropReturnAddr_(nullptr),
     baselineSetPropReturnAddr_(nullptr),
     stringConcatStub_(nullptr),
-    regExpExecStub_(nullptr),
-    regExpTestStub_(nullptr)
+    regExpMatcherStub_(nullptr),
+    regExpTesterStub_(nullptr)
 {
     baselineCallReturnAddrs_[0] = baselineCallReturnAddrs_[1] = nullptr;
 }
 
 JitCompartment::~JitCompartment()
 {
     js_delete(stubCodes_);
 }
@@ -742,37 +742,37 @@ JitCompartment::sweep(FreeOp* fop, JSCom
     if (!stubCodes_->lookup(ICGetProp_Fallback::Compiler::BASELINE_KEY))
         baselineGetPropReturnAddr_ = nullptr;
     if (!stubCodes_->lookup(ICSetProp_Fallback::Compiler::BASELINE_KEY))
         baselineSetPropReturnAddr_ = nullptr;
 
     if (stringConcatStub_ && !IsMarkedUnbarriered(&stringConcatStub_))
         stringConcatStub_ = nullptr;
 
-    if (regExpExecStub_ && !IsMarkedUnbarriered(&regExpExecStub_))
-        regExpExecStub_ = nullptr;
-
-    if (regExpTestStub_ && !IsMarkedUnbarriered(&regExpTestStub_))
-        regExpTestStub_ = nullptr;
+    if (regExpMatcherStub_ && !IsMarkedUnbarriered(&regExpMatcherStub_))
+        regExpMatcherStub_ = nullptr;
+
+    if (regExpTesterStub_ && !IsMarkedUnbarriered(&regExpTesterStub_))
+        regExpTesterStub_ = nullptr;
 
     for (size_t i = 0; i <= SimdTypeDescr::LAST_TYPE; i++) {
         ReadBarrieredObject& obj = simdTemplateObjects_[i];
         if (obj && IsAboutToBeFinalized(&obj))
             obj.set(nullptr);
     }
 }
 
 void
 JitCompartment::toggleBarriers(bool enabled)
 {
     // Toggle barriers in compartment wide stubs that have patchable pre barriers.
-    if (regExpExecStub_)
-        regExpExecStub_->togglePreBarriers(enabled, Reprotect);
-    if (regExpTestStub_)
-        regExpTestStub_->togglePreBarriers(enabled, Reprotect);
+    if (regExpMatcherStub_)
+        regExpMatcherStub_->togglePreBarriers(enabled, Reprotect);
+    if (regExpTesterStub_)
+        regExpTesterStub_->togglePreBarriers(enabled, Reprotect);
 
     // Toggle barriers in baseline IC stubs.
     for (ICStubCodeMap::Enum e(*stubCodes_); !e.empty(); e.popFront()) {
         JitCode* code = *e.front().value().unsafeGet();
         code->togglePreBarriers(enabled, Reprotect);
     }
 }
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1840,20 +1840,16 @@ jit::MakeMRegExpHoistable(MIRGraph& grap
                     continue;
 
                 MOZ_ASSERT(i->consumer()->isDefinition());
 
                 // All MRegExp* MIR's don't adjust the regexp.
                 MDefinition* use = i->consumer()->toDefinition();
                 if (use->isRegExpReplace())
                     continue;
-                if (use->isRegExpExec())
-                    continue;
-                if (use->isRegExpTest())
-                    continue;
 
                 hoistable = false;
                 break;
             }
 
             if (!hoistable)
                 continue;
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -809,19 +809,20 @@ class IonBuilder
     InliningStatus inlineConstantStringSplit(CallInfo& callInfo);
     InliningStatus inlineStringSplit(CallInfo& callInfo);
     InliningStatus inlineStrCharCodeAt(CallInfo& callInfo);
     InliningStatus inlineConstantCharCodeAt(CallInfo& callInfo);
     InliningStatus inlineStrFromCharCode(CallInfo& callInfo);
     InliningStatus inlineStrCharAt(CallInfo& callInfo);
     InliningStatus inlineStrReplace(CallInfo& callInfo);
 
-    // RegExp natives.
-    InliningStatus inlineRegExpExec(CallInfo& callInfo);
-    InliningStatus inlineRegExpTest(CallInfo& callInfo);
+    // RegExp intrinsics.
+    InliningStatus inlineRegExpMatcher(CallInfo& callInfo);
+    InliningStatus inlineRegExpTester(CallInfo& callInfo);
+    InliningStatus inlineIsRegExpObject(CallInfo& callInfo);
 
     // Object natives and intrinsics.
     InliningStatus inlineObjectCreate(CallInfo& callInfo);
     InliningStatus inlineDefineDataProperty(CallInfo& callInfo);
 
     // Atomics natives.
     InliningStatus inlineAtomicsCompareExchange(CallInfo& callInfo);
     InliningStatus inlineAtomicsExchange(CallInfo& callInfo);
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -397,24 +397,24 @@ class JitCompartment
 
     // Stubs to concatenate two strings inline, or perform RegExp calls inline.
     // These bake in zone and compartment specific pointers and can't be stored
     // in JitRuntime. These are weak pointers, but are not declared as
     // ReadBarriered since they are only read from during Ion compilation,
     // which may occur off thread and whose barriers are captured during
     // CodeGenerator::link.
     JitCode* stringConcatStub_;
-    JitCode* regExpExecStub_;
-    JitCode* regExpTestStub_;
+    JitCode* regExpMatcherStub_;
+    JitCode* regExpTesterStub_;
 
     mozilla::Array<ReadBarrieredObject, SimdTypeDescr::LAST_TYPE + 1> simdTemplateObjects_;
 
     JitCode* generateStringConcatStub(JSContext* cx);
-    JitCode* generateRegExpExecStub(JSContext* cx);
-    JitCode* generateRegExpTestStub(JSContext* cx);
+    JitCode* generateRegExpMatcherStub(JSContext* cx);
+    JitCode* generateRegExpTesterStub(JSContext* cx);
 
   public:
     JSObject* getSimdTemplateObjectFor(JSContext* cx, Handle<SimdTypeDescr*> descr) {
         ReadBarrieredObject& tpl = simdTemplateObjects_[descr->type()];
         if (!tpl)
             tpl.set(TypedObject::createZeroed(cx, descr, 0, gc::TenuredHeap));
         return tpl.get();
     }
@@ -488,36 +488,36 @@ class JitCompartment
 
     void mark(JSTracer* trc, JSCompartment* compartment);
     void sweep(FreeOp* fop, JSCompartment* compartment);
 
     JitCode* stringConcatStubNoBarrier() const {
         return stringConcatStub_;
     }
 
-    JitCode* regExpExecStubNoBarrier() const {
-        return regExpExecStub_;
+    JitCode* regExpMatcherStubNoBarrier() const {
+        return regExpMatcherStub_;
     }
 
-    bool ensureRegExpExecStubExists(JSContext* cx) {
-        if (regExpExecStub_)
+    bool ensureRegExpMatcherStubExists(JSContext* cx) {
+        if (regExpMatcherStub_)
             return true;
-        regExpExecStub_ = generateRegExpExecStub(cx);
-        return regExpExecStub_ != nullptr;
+        regExpMatcherStub_ = generateRegExpMatcherStub(cx);
+        return regExpMatcherStub_ != nullptr;
     }
 
-    JitCode* regExpTestStubNoBarrier() const {
-        return regExpTestStub_;
+    JitCode* regExpTesterStubNoBarrier() const {
+        return regExpTesterStub_;
     }
 
-    bool ensureRegExpTestStubExists(JSContext* cx) {
-        if (regExpTestStub_)
+    bool ensureRegExpTesterStubExists(JSContext* cx) {
+        if (regExpTesterStub_)
             return true;
-        regExpTestStub_ = generateRegExpTestStub(cx);
-        return regExpTestStub_ != nullptr;
+        regExpTesterStub_ = generateRegExpTesterStub(cx);
+        return regExpTesterStub_ != nullptr;
     }
 };
 
 // Called from JSCompartment::discardJitCode().
 void InvalidateAll(FreeOp* fop, JS::Zone* zone);
 void FinishInvalidation(FreeOp* fop, JSScript* script);
 
 // On windows systems, really large frames need to be incrementally touched.
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2078,22 +2078,16 @@ MustCloneRegExpForCall(MCall* call, uint
 {
     // We have a regex literal flowing into a call. Return |false| iff
     // this is a native call that does not let the regex escape.
 
     JSFunction* target = call->getSingleTarget();
     if (!target || !target->isNative())
         return true;
 
-    if (useIndex == MCall::IndexOfThis() &&
-        (target->native() == regexp_exec || target->native() == regexp_test))
-    {
-        return false;
-    }
-
     if (useIndex == MCall::IndexOfArgument(0) &&
         (target->native() == str_split ||
          target->native() == str_replace ||
          target->native() == str_match ||
          target->native() == str_search))
     {
         return false;
     }
@@ -2112,18 +2106,18 @@ MustCloneRegExp(MRegExp* regexp)
     // it escape, we don't have to clone it.
 
     for (MUseIterator iter(regexp->usesBegin()); iter != regexp->usesEnd(); iter++) {
         MNode* node = iter->consumer();
         if (!node->isDefinition())
             return true;
 
         MDefinition* def = node->toDefinition();
-        if (def->isRegExpTest()) {
-            MRegExpTest* test = def->toRegExpTest();
+        if (def->isRegExpTester()) {
+            MRegExpTester* test = def->toRegExpTester();
             if (test->indexOf(*iter) == 1) {
                 // Optimized RegExp.prototype.test.
                 MOZ_ASSERT(test->regexp() == regexp);
                 continue;
             }
         } else if (def->isCall()) {
             MCall* call = def->toCall();
             if (!MustCloneRegExpForCall(call, call->indexOf(*iter)))
@@ -2144,35 +2138,43 @@ LIRGenerator::visitRegExp(MRegExp* ins)
     } else {
         LRegExp* lir = new(alloc()) LRegExp();
         defineReturn(lir, ins);
         assignSafepoint(lir, ins);
     }
 }
 
 void
-LIRGenerator::visitRegExpExec(MRegExpExec* ins)
+LIRGenerator::visitRegExpMatcher(MRegExpMatcher* ins)
 {
     MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
     MOZ_ASSERT(ins->string()->type() == MIRType_String);
-
-    LRegExpExec* lir = new(alloc()) LRegExpExec(useFixedAtStart(ins->regexp(), CallTempReg0),
-                                                useFixedAtStart(ins->string(), CallTempReg1));
+    MOZ_ASSERT(ins->lastIndex()->type() == MIRType_Int32);
+    MOZ_ASSERT(ins->sticky()->type() == MIRType_Boolean);
+
+    LRegExpMatcher* lir = new(alloc()) LRegExpMatcher(useFixedAtStart(ins->regexp(), RegExpMatcherRegExpReg),
+                                                      useFixedAtStart(ins->string(), RegExpMatcherStringReg),
+                                                      useFixedAtStart(ins->lastIndex(), RegExpMatcherLastIndexReg),
+                                                      useFixedAtStart(ins->sticky(), RegExpMatcherStickyReg));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
-LIRGenerator::visitRegExpTest(MRegExpTest* ins)
+LIRGenerator::visitRegExpTester(MRegExpTester* ins)
 {
     MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
     MOZ_ASSERT(ins->string()->type() == MIRType_String);
-
-    LRegExpTest* lir = new(alloc()) LRegExpTest(useFixedAtStart(ins->regexp(), CallTempReg2),
-                                                useFixedAtStart(ins->string(), CallTempReg3));
+    MOZ_ASSERT(ins->lastIndex()->type() == MIRType_Int32);
+    MOZ_ASSERT(ins->sticky()->type() == MIRType_Boolean);
+
+    LRegExpTester* lir = new(alloc()) LRegExpTester(useFixedAtStart(ins->regexp(), RegExpTesterRegExpReg),
+                                                    useFixedAtStart(ins->string(), RegExpTesterStringReg),
+                                                    useFixedAtStart(ins->lastIndex(), RegExpTesterLastIndexReg),
+                                                    useFixedAtStart(ins->sticky(), RegExpTesterStickyReg));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitRegExpReplace(MRegExpReplace* ins)
 {
     MOZ_ASSERT(ins->pattern()->type() == MIRType_Object);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -156,18 +156,18 @@ class LIRGenerator : public LIRGenerator
     void visitOsrArgumentsObject(MOsrArgumentsObject* object);
     void visitToDouble(MToDouble* convert);
     void visitToFloat32(MToFloat32* convert);
     void visitToInt32(MToInt32* convert);
     void visitTruncateToInt32(MTruncateToInt32* truncate);
     void visitToString(MToString* convert);
     void visitToObjectOrNull(MToObjectOrNull* convert);
     void visitRegExp(MRegExp* ins);
-    void visitRegExpExec(MRegExpExec* ins);
-    void visitRegExpTest(MRegExpTest* ins);
+    void visitRegExpMatcher(MRegExpMatcher* ins);
+    void visitRegExpTester(MRegExpTester* ins);
     void visitRegExpReplace(MRegExpReplace* ins);
     void visitStringReplace(MStringReplace* ins);
     void visitBinarySharedStub(MBinarySharedStub* ins);
     void visitUnarySharedStub(MUnarySharedStub* ins);
     void visitLambda(MLambda* ins);
     void visitLambdaArrow(MLambdaArrow* ins);
     void visitKeepAliveObject(MKeepAliveObject* ins);
     void visitSlots(MSlots* ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -171,20 +171,22 @@ IonBuilder::inlineNativeCall(CallInfo& c
       case InlinableNative::MathSign:
         return inlineMathFunction(callInfo, MMathFunction::Sign);
       case InlinableNative::MathTrunc:
         return inlineMathFunction(callInfo, MMathFunction::Trunc);
       case InlinableNative::MathCbrt:
         return inlineMathFunction(callInfo, MMathFunction::Cbrt);
 
       // RegExp natives.
-      case InlinableNative::RegExpExec:
-        return CallResultEscapes(pc) ? inlineRegExpExec(callInfo) : inlineRegExpTest(callInfo);
-      case InlinableNative::RegExpTest:
-        return inlineRegExpTest(callInfo);
+      case InlinableNative::RegExpMatcher:
+        return inlineRegExpMatcher(callInfo);
+      case InlinableNative::RegExpTester:
+        return inlineRegExpTester(callInfo);
+      case InlinableNative::IsRegExpObject:
+        return inlineIsRegExpObject(callInfo);
 
       // String natives.
       case InlinableNative::String:
         return inlineStringObject(callInfo);
       case InlinableNative::StringSplit:
         return inlineStringSplit(callInfo);
       case InlinableNative::StringCharCodeAt:
         return inlineStrCharCodeAt(callInfo);
@@ -1826,90 +1828,151 @@ IonBuilder::inlineStrCharAt(CallInfo& ca
 
     MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
     current->add(string);
     current->push(string);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineRegExpExec(CallInfo& callInfo)
+IonBuilder::inlineRegExpMatcher(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
+    // This is called from Self-hosted JS, after testing each argument,
+    // most of following tests should be passed.
+
+    if (callInfo.argc() != 4 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
-    if (callInfo.thisArg()->type() != MIRType_Object)
+    // regexp
+    if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
-    TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
-    const Class* clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
+    TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
+    const Class* clasp = arg0Types ? arg0Types->getKnownClass(constraints()) : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
 
-    if (callInfo.getArg(0)->mightBeType(MIRType_Object))
+    // string
+    if (callInfo.getArg(1)->mightBeType(MIRType_Object))
+        return InliningStatus_NotInlined;
+
+    // lastIndex: Only inline if it's Int32.
+    if (callInfo.getArg(2)->type() != MIRType_Int32)
+        return InliningStatus_NotInlined;
+
+    // sticky
+    if (callInfo.getArg(3)->type() != MIRType_Boolean)
         return InliningStatus_NotInlined;
 
     JSContext* cx = GetJitContext()->cx;
-    if (!cx->compartment()->jitCompartment()->ensureRegExpExecStubExists(cx))
+    if (!cx->compartment()->jitCompartment()->ensureRegExpMatcherStubExists(cx))
         return InliningStatus_Error;
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MInstruction* exec = MRegExpExec::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
-    current->add(exec);
-    current->push(exec);
-
-    if (!resumeAfter(exec))
+    MInstruction* matcher = MRegExpMatcher::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
+                                                callInfo.getArg(2), callInfo.getArg(3));
+    current->add(matcher);
+    current->push(matcher);
+
+    if (!resumeAfter(matcher))
         return InliningStatus_Error;
 
-    if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), BarrierKind::TypeSet))
+    if (!pushTypeBarrier(matcher, getInlineReturnTypeSet(), BarrierKind::TypeSet))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineRegExpTest(CallInfo& callInfo)
+IonBuilder::inlineRegExpTester(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
+    // This is called from Self-hosted JS, after testing each argument,
+    // most of following tests should be passed.
+
+    if (callInfo.argc() != 4 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
-    // TI can infer a nullptr return type of regexp_test with eager compilation.
-    if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean)
+    // regexp
+    if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
-    if (callInfo.thisArg()->type() != MIRType_Object)
-        return InliningStatus_NotInlined;
-    TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
-    const Class* clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
+    TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
+    const Class* clasp = arg0Types ? arg0Types->getKnownClass(constraints()) : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
-    if (callInfo.getArg(0)->mightBeType(MIRType_Object))
+
+    // string
+    if (callInfo.getArg(1)->mightBeType(MIRType_Object))
+        return InliningStatus_NotInlined;
+
+    // lastIndex: Only inline if it's Int32.
+    if (callInfo.getArg(2)->type() != MIRType_Int32)
+        return InliningStatus_NotInlined;
+
+    // sticky
+    if (callInfo.getArg(3)->type() != MIRType_Boolean)
         return InliningStatus_NotInlined;
 
     JSContext* cx = GetJitContext()->cx;
-    if (!cx->compartment()->jitCompartment()->ensureRegExpTestStubExists(cx))
+    if (!cx->compartment()->jitCompartment()->ensureRegExpTesterStubExists(cx))
         return InliningStatus_Error;
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MInstruction* match = MRegExpTest::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
-    current->add(match);
-    current->push(match);
-    if (!resumeAfter(match))
+    MInstruction* tester = MRegExpTester::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
+                                              callInfo.getArg(2), callInfo.getArg(3));
+    current->add(tester);
+    current->push(tester);
+
+    if (!resumeAfter(tester))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineIsRegExpObject(CallInfo& callInfo)
+{
+    if (callInfo.constructing() || callInfo.argc() != 1) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
+        return InliningStatus_NotInlined;
+    }
+
+    if (getInlineReturnType() != MIRType_Boolean)
+        return InliningStatus_NotInlined;
+
+    MDefinition* arg = callInfo.getArg(0);
+
+    bool isRegExpObject;
+    if (!arg->mightBeType(MIRType_Object)) {
+        isRegExpObject = false;
+    } else {
+        if (arg->type() != MIRType_Object)
+            return InliningStatus_NotInlined;
+
+        TemporaryTypeSet* types = arg->resultTypeSet();
+        const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
+        if (!clasp || clasp->isProxy())
+            return InliningStatus_NotInlined;
+
+        isRegExpObject = (clasp == &RegExpObject::class_);
+    }
+
+    pushConstant(BooleanValue(isRegExpObject));
+
+    callInfo.setImplicitlyUsedUnchecked();
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineStrReplace(CallInfo& callInfo)
 {
     if (callInfo.argc() != 2 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     // Return: String.
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7509,91 +7509,118 @@ class MRegExp : public MNullaryInstructi
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
 };
 
-class MRegExpExec
-  : public MBinaryInstruction,
-    public MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >::Data
+class MRegExpMatcher
+  : public MAryInstruction<4>,
+    public NoTypePolicy::Data
 {
   private:
 
-    MRegExpExec(MDefinition* regexp, MDefinition* string)
-      : MBinaryInstruction(string, regexp)
-    {
+    MRegExpMatcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex,
+                   MDefinition* sticky)
+      : MAryInstruction<4>()
+    {
+        initOperand(0, regexp);
+        initOperand(1, string);
+        initOperand(2, lastIndex);
+        initOperand(3, sticky);
+
         // May be object or null.
         setResultType(MIRType_Value);
     }
 
   public:
-    INSTRUCTION_HEADER(RegExpExec)
-
-    static MRegExpExec* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string) {
-        return new(alloc) MRegExpExec(regexp, string);
-    }
-
-    MDefinition* string() const {
-        return getOperand(0);
+    INSTRUCTION_HEADER(RegExpMatcher)
+
+    static MRegExpMatcher* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string,
+                               MDefinition* lastIndex, MDefinition* sticky)
+    {
+        return new(alloc) MRegExpMatcher(regexp, string, lastIndex, sticky);
     }
 
     MDefinition* regexp() const {
+        return getOperand(0);
+    }
+    MDefinition* string() const {
         return getOperand(1);
     }
+    MDefinition* lastIndex() const {
+        return getOperand(2);
+    }
+    MDefinition* sticky() const {
+        return getOperand(3);
+    }
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
 
     bool canRecoverOnBailout() const override {
         // XXX: always return false for now, to work around bug 1132128.
         if (false && regexp()->isRegExp())
             return !regexp()->toRegExp()->source()->needUpdateLastIndex();
         return false;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
 };
 
-class MRegExpTest
-  : public MBinaryInstruction,
-    public MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> >::Data
+class MRegExpTester
+  : public MAryInstruction<4>,
+    public NoTypePolicy::Data
 {
   private:
 
-    MRegExpTest(MDefinition* regexp, MDefinition* string)
-      : MBinaryInstruction(string, regexp)
-    {
-        setResultType(MIRType_Boolean);
-    }
-
-  public:
-    INSTRUCTION_HEADER(RegExpTest)
-
-    static MRegExpTest* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string) {
-        return new(alloc) MRegExpTest(regexp, string);
-    }
-
+    MRegExpTester(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex,
+                  MDefinition* sticky)
+      : MAryInstruction<4>()
+    {
+        initOperand(0, regexp);
+        initOperand(1, string);
+        initOperand(2, lastIndex);
+        initOperand(3, sticky);
+
+        setResultType(MIRType_Int32);
+    }
+
+  public:
+    INSTRUCTION_HEADER(RegExpTester)
+
+    static MRegExpTester* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string,
+                              MDefinition* lastIndex, MDefinition* sticky)
+    {
+        return new(alloc) MRegExpTester(regexp, string, lastIndex, sticky);
+    }
+
+    MDefinition* regexp() const {
+        return getOperand(0);
+    }
     MDefinition* string() const {
-        return getOperand(0);
-    }
-    MDefinition* regexp() const {
         return getOperand(1);
     }
+    MDefinition* lastIndex() const {
+        return getOperand(2);
+    }
+    MDefinition* sticky() const {
+        return getOperand(3);
+    }
 
     bool possiblyCalls() const override {
         return true;
     }
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
-        // RegExpTest has a side-effect on the regexp object's lastIndex
+        // RegExpTester has a side-effect on the regexp object's lastIndex
         // when sticky or global flags are set.
         // Return false unless we are sure it's not the case.
         // XXX: always return false for now, to work around bug 1132128.
         if (false && regexp()->isRegExp())
             return !regexp()->toRegExp()->source()->needUpdateLastIndex();
         return false;
     }
 };
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -134,18 +134,18 @@ namespace jit {
     _(MutateProto)                                                          \
     _(InitProp)                                                             \
     _(InitPropGetterSetter)                                                 \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
     _(Nop)                                                                  \
     _(LimitedTruncate)                                                      \
     _(RegExp)                                                               \
-    _(RegExpExec)                                                           \
-    _(RegExpTest)                                                           \
+    _(RegExpMatcher)                                                        \
+    _(RegExpTester)                                                         \
     _(RegExpReplace)                                                        \
     _(StringReplace)                                                        \
     _(Lambda)                                                               \
     _(LambdaArrow)                                                          \
     _(KeepAliveObject)                                                      \
     _(Slots)                                                                \
     _(Elements)                                                             \
     _(ConstantElements)                                                     \
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -991,62 +991,67 @@ RStringSplit::recover(JSContext* cx, Sna
     if (!res)
         return false;
 
     result.setObject(*res);
     iter.storeInstructionResult(result);
     return true;
 }
 
-bool MRegExpExec::writeRecoverData(CompactBufferWriter& writer) const
+bool MRegExpMatcher::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
-    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpExec));
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpMatcher));
     return true;
 }
 
-RRegExpExec::RRegExpExec(CompactBufferReader& reader)
+RRegExpMatcher::RRegExpMatcher(CompactBufferReader& reader)
 {}
 
-bool RRegExpExec::recover(JSContext* cx, SnapshotIterator& iter) const{
+bool RRegExpMatcher::recover(JSContext* cx, SnapshotIterator& iter) const{
     RootedObject regexp(cx, &iter.read().toObject());
     RootedString input(cx, iter.read().toString());
+    int32_t lastIndex = iter.read().toInt32();
+    bool sticky = iter.read().toBoolean();
 
     RootedValue result(cx);
 
-    if (!regexp_exec_raw(cx, regexp, input, nullptr, &result))
+    fprintf(stderr, "Recover\n");
+    if (!RegExpMatcherRaw(cx, regexp, input, lastIndex, sticky, nullptr, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
-MRegExpTest::writeRecoverData(CompactBufferWriter& writer) const
+MRegExpTester::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
-    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpTest));
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpTester));
     return true;
 }
 
-RRegExpTest::RRegExpTest(CompactBufferReader& reader)
+RRegExpTester::RRegExpTester(CompactBufferReader& reader)
 { }
 
 bool
-RRegExpTest::recover(JSContext* cx, SnapshotIterator& iter) const
+RRegExpTester::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedString string(cx, iter.read().toString());
     RootedObject regexp(cx, &iter.read().toObject());
-    bool resultBool;
+    int32_t lastIndex = iter.read().toInt32();
+    bool sticky = iter.read().toBoolean();
+    int32_t endIndex;
 
-    if (!js::regexp_test_raw(cx, regexp, string, &resultBool))
+    if (!js::RegExpTesterRaw(cx, regexp, string, lastIndex, sticky, &endIndex))
         return false;
 
     RootedValue result(cx);
-    result.setBoolean(resultBool);
+    result.setInt32(endIndex);
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
 MRegExpReplace::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -83,18 +83,18 @@ namespace jit {
     _(PowHalf)                                  \
     _(MinMax)                                   \
     _(Abs)                                      \
     _(Sqrt)                                     \
     _(Atan2)                                    \
     _(Hypot)                                    \
     _(MathFunction)                             \
     _(StringSplit)                              \
-    _(RegExpExec)                               \
-    _(RegExpTest)                               \
+    _(RegExpMatcher)                            \
+    _(RegExpTester)                             \
     _(RegExpReplace)                            \
     _(StringReplace)                            \
     _(TypeOf)                                   \
     _(ToDouble)                                 \
     _(ToFloat32)                                \
     _(TruncateToInt32)                          \
     _(NewObject)                                \
     _(NewArray)                                 \
@@ -554,35 +554,35 @@ class RStringSplit final : public RInstr
 
     virtual uint32_t numOperands() const {
         return 3;
     }
 
     bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
-class RRegExpExec final : public RInstruction
+class RRegExpMatcher final : public RInstruction
 {
   public:
-    RINSTRUCTION_HEADER_(RegExpExec)
+    RINSTRUCTION_HEADER_(RegExpMatcher)
 
     virtual uint32_t numOperands() const {
-        return 2;
+        return 5;
     }
 
     bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
-class RRegExpTest final : public RInstruction
+class RRegExpTester final : public RInstruction
 {
   public:
-    RINSTRUCTION_HEADER_(RegExpTest)
+    RINSTRUCTION_HEADER_(RegExpTester)
 
     virtual uint32_t numOperands() const {
-        return 2;
+        return 5;
     }
 
     bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
 class RRegExpReplace final : public RInstruction
 {
   public:
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -155,16 +155,27 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 // None of these may be the second scratch register (lr).
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = r2;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = r3;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = r0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = r1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = r4;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg3;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg3;
 
 static MOZ_CONSTEXPR_VAR FloatRegister d0  = {FloatRegisters::d0, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d1  = {FloatRegisters::d1, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d2  = {FloatRegisters::d2, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d3  = {FloatRegisters::d3, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d4  = {FloatRegisters::d4, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d5  = {FloatRegisters::d5, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d6  = {FloatRegisters::d6, VFPRegister::Double};
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -128,16 +128,28 @@ static constexpr Register AsmJSIonExitRe
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 // None of these may be the second scratch register.
 static constexpr Register AsmJSIonExitRegReturnData = r2;
 static constexpr Register AsmJSIonExitRegReturnType = r3;
 static constexpr Register AsmJSIonExitRegD0 = r0;
 static constexpr Register AsmJSIonExitRegD1 = r1;
 static constexpr Register AsmJSIonExitRegD2 = r4;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg3;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg3;
+
 static constexpr Register JSReturnReg_Type = r3;
 static constexpr Register JSReturnReg_Data = r2;
 
 static constexpr FloatRegister NANReg = { FloatRegisters::d14, FloatRegisters::Single };
 // N.B. r8 isn't listed as an aapcs temp register, but we can use it as such because we never
 // use return-structs.
 static constexpr Register CallTempNonArgRegs[] = { r8, r9, r10, r11, r12, r13, r14, r15 };
 static const uint32_t NumCallTempNonArgRegs =
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -120,16 +120,28 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = a3;
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 // None of these may be the second scratch register (t8).
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = a0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = a1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = t0;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg3;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg3;
+
 static MOZ_CONSTEXPR_VAR uint32_t CodeAlignment = 4;
 
 // This boolean indicates whether we support SIMD instructions flavoured for
 // this architecture or not. Rather than a method in the LIRGenerator, it is
 // here such that it is accessible from the entire codebase. Once full support
 // for SIMD is reached on all tier-1 platforms, this constant can be deleted.
 static MOZ_CONSTEXPR_VAR bool SupportsSimd = false;
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3935,59 +3935,77 @@ class LRegExp : public LCallInstructionH
   public:
     LIR_HEADER(RegExp)
 
     const MRegExp* mir() const {
         return mir_->toRegExp();
     }
 };
 
-class LRegExpExec : public LCallInstructionHelper<BOX_PIECES, 2, 0>
-{
-  public:
-    LIR_HEADER(RegExpExec)
-
-    LRegExpExec(const LAllocation& regexp, const LAllocation& string)
+class LRegExpMatcher : public LCallInstructionHelper<BOX_PIECES, 4, 0>
+{
+  public:
+    LIR_HEADER(RegExpMatcher)
+
+    LRegExpMatcher(const LAllocation& regexp, const LAllocation& string,
+                   const LAllocation& lastIndex, const LAllocation& sticky)
     {
         setOperand(0, regexp);
         setOperand(1, string);
+        setOperand(2, lastIndex);
+        setOperand(3, sticky);
     }
 
     const LAllocation* regexp() {
         return getOperand(0);
     }
     const LAllocation* string() {
         return getOperand(1);
     }
-
-    const MRegExpExec* mir() const {
-        return mir_->toRegExpExec();
-    }
-};
-
-class LRegExpTest : public LCallInstructionHelper<1, 2, 0>
-{
-  public:
-    LIR_HEADER(RegExpTest)
-
-    LRegExpTest(const LAllocation& regexp, const LAllocation& string)
+    const LAllocation* lastIndex() {
+        return getOperand(2);
+    }
+    const LAllocation* sticky() {
+        return getOperand(3);
+    }
+
+    const MRegExpMatcher* mir() const {
+        return mir_->toRegExpMatcher();
+    }
+};
+
+class LRegExpTester : public LCallInstructionHelper<1, 4, 0>
+{
+  public:
+    LIR_HEADER(RegExpTester)
+
+    LRegExpTester(const LAllocation& regexp, const LAllocation& string,
+                  const LAllocation& lastIndex, const LAllocation& sticky)
     {
         setOperand(0, regexp);
         setOperand(1, string);
+        setOperand(2, lastIndex);
+        setOperand(3, sticky);
     }
 
     const LAllocation* regexp() {
         return getOperand(0);
     }
     const LAllocation* string() {
         return getOperand(1);
     }
-
-    const MRegExpTest* mir() const {
-        return mir_->toRegExpTest();
+    const LAllocation* lastIndex() {
+        return getOperand(2);
+    }
+    const LAllocation* sticky() {
+        return getOperand(3);
+    }
+
+    const MRegExpTester* mir() const {
+        return mir_->toRegExpTester();
     }
 };
 
 
 class LStrReplace : public LCallInstructionHelper<1, 3, 0>
 {
   public:
     LStrReplace(const LAllocation& string, const LAllocation& pattern,
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -187,18 +187,18 @@
     _(Float32x4ToInt32x4)           \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(OsrReturnValue)               \
     _(OsrArgumentsObject)           \
     _(RegExp)                       \
-    _(RegExpExec)                   \
-    _(RegExpTest)                   \
+    _(RegExpMatcher)                \
+    _(RegExpTester)                 \
     _(RegExpReplace)                \
     _(StringReplace)                \
     _(Substr)                       \
     _(BinarySharedStub)             \
     _(UnarySharedStub)              \
     _(Lambda)                       \
     _(LambdaArrow)                  \
     _(LambdaForSingleton)           \
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -157,16 +157,28 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = rax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = rdi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = rbx;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg4;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg3;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg4;
+
 class ABIArgGenerator
 {
 #if defined(XP_WIN)
     unsigned regIndex_;
 #else
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
 #endif
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -98,16 +98,28 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = edx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = edi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = eax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = esi;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg4;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg3;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg4;
+
 // GCC stack is aligned on 16 bytes. Ion does not maintain this for internal
 // calls. asm.js code does.
 #if defined(__GNUC__)
 static MOZ_CONSTEXPR_VAR uint32_t ABIStackAlignment = 16;
 #else
 static MOZ_CONSTEXPR_VAR uint32_t ABIStackAlignment = 4;
 #endif
 static MOZ_CONSTEXPR_VAR uint32_t CodeAlignment = 16;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2251,17 +2251,18 @@ class MOZ_STACK_CLASS StringRegExpGuard
 
 } /* anonymous namespace */
 
 static bool
 DoMatchLocal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLinearString input,
              RegExpShared& re)
 {
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
-    RegExpRunStatus status = re.execute(cx, input, 0, &matches);
+    bool sticky = re.sticky();
+    RegExpRunStatus status = re.execute(cx, input, 0, sticky, &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
         args.rval().setNull();
         return true;
     }
 
@@ -2362,22 +2363,23 @@ DoMatchGlobal(JSContext* cx, const CallA
     // The loop variables from steps 8c-e aren't needed, as we use different
     // techniques from the spec to implement step 8f's loop.
 
     // Step 8f.
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     size_t charsLen = input->length();
     RegExpShared& re = g.regExp();
     bool unicode = re.unicode();
+    bool sticky = re.sticky();
     for (size_t searchIndex = 0; searchIndex <= charsLen; ) {
         if (!CheckForInterrupt(cx))
             return false;
 
         // Steps 8f(i-ii), minus "lastIndex" updates (see above).
-        RegExpRunStatus status = re.execute(cx, input, searchIndex, &matches);
+        RegExpRunStatus status = re.execute(cx, input, searchIndex, sticky, &matches, nullptr);
         if (status == RegExpRunStatus_Error)
             return false;
 
         // Step 8f(ii).
         if (status == RegExpRunStatus_Success_NotFound)
             break;
 
         lastSuccessfulStart = searchIndex;
@@ -2400,17 +2402,17 @@ DoMatchGlobal(JSContext* cx, const CallA
     if (elements.empty()) {
         args.rval().setNull();
         return true;
     }
 
     // The last *successful* match updates the RegExpStatics. (Interestingly,
     // this implies that String.prototype.match's semantics aren't those
     // implied by the RegExp.prototype.exec calls in the ES5 algorithm.)
-    res->updateLazily(cx, input, &re, lastSuccessfulStart);
+    res->updateLazily(cx, input, &re, lastSuccessfulStart, sticky);
 
     // Steps 8b, 8f(iii)(5-6), 8h.
     JSObject* array = NewDenseCopiedArray(cx, elements.length(), elements.begin());
     if (!array)
         return false;
 
     args.rval().setObject(*array);
     return true;
@@ -2530,22 +2532,24 @@ js::str_search(JSContext* cx, unsigned a
         return false;
 
     RegExpStatics* res = cx->global()->getRegExpStatics(cx);
     if (!res)
         return false;
 
     /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
-    RegExpRunStatus status = g.regExp().execute(cx, linearStr, 0, &matches);
+    RegExpShared& re = g.regExp();
+    bool sticky = re.sticky();
+    RegExpRunStatus status = re.execute(cx, linearStr, 0, sticky, &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success)
-        res->updateLazily(cx, linearStr, &g.regExp(), 0);
+        res->updateLazily(cx, linearStr, &re, 0, sticky);
 
     args.rval().setInt32(status == RegExpRunStatus_Success_NotFound ? -1 : matches[0].start);
     return true;
 }
 
 // Utility for building a rope (lazy concatenation) of strings.
 class RopeBuilder {
     JSContext* cx;
@@ -2627,17 +2631,18 @@ struct ReplaceData
 static bool
 ReplaceRegExp(JSContext* cx, RegExpStatics* res, ReplaceData& rdata);
 
 static bool
 DoMatchForReplaceLocal(JSContext* cx, RegExpStatics* res, HandleLinearString linearStr,
                        RegExpShared& re, ReplaceData& rdata, size_t* rightContextOffset)
 {
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
-    RegExpRunStatus status = re.execute(cx, linearStr, 0, &matches);
+    bool sticky = re.sticky();
+    RegExpRunStatus status = re.execute(cx, linearStr, 0, sticky, &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound)
         return true;
 
     MatchPair& match = matches[0];
     *rightContextOffset = match.limit;
@@ -2648,23 +2653,24 @@ DoMatchForReplaceLocal(JSContext* cx, Re
     return ReplaceRegExp(cx, res, rdata);
 }
 
 static bool
 DoMatchForReplaceGlobal(JSContext* cx, RegExpStatics* res, HandleLinearString linearStr,
                         RegExpShared& re, ReplaceData& rdata, size_t* rightContextOffset)
 {
     bool unicode = re.unicode();
+    bool sticky = re.sticky();
     size_t charsLen = linearStr->length();
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     for (size_t count = 0, searchIndex = 0; searchIndex <= charsLen; ++count) {
         if (!CheckForInterrupt(cx))
             return false;
 
-        RegExpRunStatus status = re.execute(cx, linearStr, searchIndex, &matches);
+        RegExpRunStatus status = re.execute(cx, linearStr, searchIndex, sticky, &matches, nullptr);
         if (status == RegExpRunStatus_Error)
             return false;
 
         if (status == RegExpRunStatus_Success_NotFound)
             break;
 
         MatchPair& match = matches[0];
         searchIndex = match.isEmpty()
@@ -3265,21 +3271,22 @@ StrReplaceRegexpRemove(JSContext* cx, Ha
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     size_t startIndex = 0; /* Index used for iterating through the string. */
     size_t lastIndex = 0;  /* Index after last successful match. */
     size_t lazyIndex = 0;  /* Index before last successful match. */
 
     /* Accumulate StringRanges for unmatched substrings. */
     bool unicode = re.unicode();
+    bool sticky = re.sticky();
     while (startIndex <= charsLen) {
         if (!CheckForInterrupt(cx))
             return nullptr;
 
-        RegExpRunStatus status = re.execute(cx, linearStr, startIndex, &matches);
+        RegExpRunStatus status = re.execute(cx, linearStr, startIndex, sticky, &matches, nullptr);
         if (status == RegExpRunStatus_Error)
             return nullptr;
         if (status == RegExpRunStatus_Success_NotFound)
             break;
         MatchPair& match = matches[0];
 
         /* Include the latest unmatched substring. */
         if (size_t(match.start) > lastIndex) {
@@ -3302,28 +3309,28 @@ StrReplaceRegexpRemove(JSContext* cx, Ha
     RegExpStatics* res;
 
     /* If unmatched, return the input string. */
     if (!lastIndex) {
         if (startIndex > 0) {
             res = cx->global()->getRegExpStatics(cx);
             if (!res)
                 return nullptr;
-            res->updateLazily(cx, linearStr, &re, lazyIndex);
+            res->updateLazily(cx, linearStr, &re, lazyIndex, sticky);
         }
 
         return str;
     }
 
     /* The last successful match updates the RegExpStatics. */
     res = cx->global()->getRegExpStatics(cx);
     if (!res)
         return nullptr;
 
-    res->updateLazily(cx, linearStr, &re, lazyIndex);
+    res->updateLazily(cx, linearStr, &re, lazyIndex, sticky);
 
     /* Include any remaining part of the string. */
     if (lastIndex < charsLen) {
         if (!ranges.append(StringRange(lastIndex, charsLen - lastIndex)))
             return nullptr;
     }
 
     /* Handle the empty string before calling .begin(). */
@@ -3947,27 +3954,30 @@ namespace {
  *
  * The algorithm differs from the spec in that the we return the next index at
  * which a match happens.
  */
 class SplitRegExpMatcher
 {
     RegExpShared& re;
     RegExpStatics* res;
+    bool sticky;
 
   public:
-    SplitRegExpMatcher(RegExpShared& re, RegExpStatics* res) : re(re), res(res) {}
+    SplitRegExpMatcher(RegExpShared& re, RegExpStatics* res) : re(re), res(res) {
+        sticky = re.sticky();
+    }
 
     static const bool returnsCaptures = true;
 
     bool operator()(JSContext* cx, HandleLinearString str, size_t index,
                     SplitMatchResult* result) const
     {
         ScopedMatchPairs matches(&cx->tempLifoAlloc());
-        RegExpRunStatus status = re.execute(cx, str, index, &matches);
+        RegExpRunStatus status = re.execute(cx, str, index, sticky, &matches, nullptr);
         if (status == RegExpRunStatus_Error)
             return false;
 
         if (status == RegExpRunStatus_Success_NotFound) {
             result->setFailure();
             return true;
         }
 
--- a/js/src/tests/ecma_3/RegExp/15.10.6.2-2.js
+++ b/js/src/tests/ecma_3/RegExp/15.10.6.2-2.js
@@ -105,93 +105,54 @@ addThis();
  * At this point |lastIndex| is > string.length, so the match should be null -
  */
 status = inSection(4);
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
 /*
- * Now let's set |lastIndex| to -1, so the match should again be null -
- */
-status = inSection(5);
-pattern.lastIndex = -1;
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
-/*
  * Now try some edge-case values. Thanks to the work done in
  * http://bugzilla.mozilla.org/show_bug.cgi?id=124339, |lastIndex|
  * is now stored as a double instead of a uint32_t (unsigned integer).
  *
  * Note 2^32 -1 is the upper bound for uint32's, but doubles can go
  * all the way up to Number.MAX_VALUE. So that's why we need cases
  * between those two numbers.
  */
 status = inSection(6);
 pattern.lastIndex = Math.pow(2,32);
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
  
-status = inSection(7);
-pattern.lastIndex = -Math.pow(2,32);
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
 status = inSection(8);
 pattern.lastIndex = Math.pow(2,32) + 1;
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
-status = inSection(9);
-pattern.lastIndex = -(Math.pow(2,32) + 1);
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
 status = inSection(10);
 pattern.lastIndex = Math.pow(2,32) * 2;
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
-status = inSection(11);
-pattern.lastIndex = -Math.pow(2,32) * 2;
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
 status = inSection(12);
 pattern.lastIndex = Math.pow(2,40);
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
-status = inSection(13);
-pattern.lastIndex = -Math.pow(2,40);
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
 status = inSection(14);
 pattern.lastIndex = Number.MAX_VALUE;
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
-status = inSection(15);
-pattern.lastIndex = -Number.MAX_VALUE;
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
- 
 
 
 /******************************************************************************
  *
  * Case 2: repeat all the above cases WITHOUT the global flag set.
  * According to EMCA. |lastIndex| should get set to 0 before the match.
  *
  * Therefore re.exec(str) should be unaffected; thus our expected values
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/exec-lastIndex-negative.js
@@ -0,0 +1,27 @@
+var BUGNUMBER = 1207922;
+var summary = "negative lastIndex should be treated as 0.";
+
+print(BUGNUMBER + ": " + summary);
+
+var pattern = /abc/gi;
+var string = 'AbcaBcabC';
+
+var indices = [
+    -1,
+    -Math.pow(2,32),
+    -(Math.pow(2,32) + 1),
+    -Math.pow(2,32) * 2,
+    -Math.pow(2,40),
+    -Number.MAX_VALUE,
+];
+for (var index of indices) {
+  pattern.lastIndex = index;
+  var result = pattern.exec(string);
+  assertEq(result.index, 0);
+  assertEq(result.length, 1);
+  assertEq(result[0], "Abc");
+  assertEq(pattern.lastIndex, 3);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -493,28 +493,28 @@ RegExpShared::trace(JSTracer* trc)
         RegExpCompilation& compilation = compilationArray[i];
         if (compilation.jitCode)
             TraceEdge(trc, &compilation.jitCode, "RegExpShared code");
     }
 }
 
 bool
 RegExpShared::compile(JSContext* cx, HandleLinearString input,
-                      CompilationMode mode, ForceByteCodeEnum force)
+                      CompilationMode mode, bool sticky, ForceByteCodeEnum force)
 {
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
     AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
 
     RootedAtom pattern(cx, source);
-    return compile(cx, pattern, input, mode, force);
+    return compile(cx, pattern, input, mode, sticky, force);
 }
 
 bool
 RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
-                      CompilationMode mode, ForceByteCodeEnum force)
+                      CompilationMode mode, bool sticky, ForceByteCodeEnum force)
 {
     if (!ignoreCase() && !StringHasRegExpMetaChars(pattern))
         canStringMatch = true;
 
     CompileOptions options(cx);
     TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
     LifoAllocScope scope(&cx->tempLifoAlloc());
@@ -530,51 +530,53 @@ RegExpShared::compile(JSContext* cx, Han
     this->parenCount = data.capture_count;
 
     irregexp::RegExpCode code = irregexp::CompilePattern(cx, this, &data, input,
                                                          false /* global() */,
                                                          ignoreCase(),
                                                          input->hasLatin1Chars(),
                                                          mode == MatchOnly,
                                                          force == ForceByteCode,
-                                                         sticky(), unicode());
+                                                         sticky, unicode());
     if (code.empty())
         return false;
 
     MOZ_ASSERT(!code.jitCode || !code.byteCode);
     MOZ_ASSERT_IF(force == ForceByteCode, code.byteCode);
 
-    RegExpCompilation& compilation = this->compilation(mode, input->hasLatin1Chars());
+    RegExpCompilation& compilation = this->compilation(mode, sticky, input->hasLatin1Chars());
     if (code.jitCode)
         compilation.jitCode = code.jitCode;
     else if (code.byteCode)
         compilation.byteCode = code.byteCode;
 
     return true;
 }
 
 bool
 RegExpShared::compileIfNecessary(JSContext* cx, HandleLinearString input,
-                                 CompilationMode mode, ForceByteCodeEnum force)
+                                 CompilationMode mode, bool sticky, ForceByteCodeEnum force)
 {
-    if (isCompiled(mode, input->hasLatin1Chars(), force))
+    if (isCompiled(mode, sticky, input->hasLatin1Chars(), force))
         return true;
-    return compile(cx, input, mode, force);
+    return compile(cx, input, mode, sticky, force);
 }
 
 RegExpRunStatus
-RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
-                      MatchPairs* matches)
+RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start, bool sticky,
+                      MatchPairs* matches, size_t* endIndex)
 {
+    MOZ_ASSERT_IF(matches, !endIndex);
+    MOZ_ASSERT_IF(!matches, endIndex);
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
 
     CompilationMode mode = matches ? Normal : MatchOnly;
 
     /* Compile the code at point-of-use. */
-    if (!compileIfNecessary(cx, input, mode, DontForceByteCode))
+    if (!compileIfNecessary(cx, input, mode, sticky, DontForceByteCode))
         return RegExpRunStatus_Error;
 
     /*
      * Ensure sufficient memory for output vector.
      * No need to initialize it. The RegExp engine fills them in on a match.
      */
     if (matches && !matches->allocOrExpandArray(pairCount())) {
         ReportOutOfMemory(cx);
@@ -584,60 +586,64 @@ RegExpShared::execute(JSContext* cx, Han
     size_t length = input->length();
 
     // Reset the Irregexp backtrack stack if it grows during execution.
     irregexp::RegExpStackScope stackScope(cx->runtime());
 
     if (canStringMatch) {
         MOZ_ASSERT(pairCount() == 1);
         size_t sourceLength = source->length();
-        if (sticky()) {
+        if (sticky) {
             // First part checks size_t overflow.
             if (sourceLength + start < sourceLength || sourceLength + start > length)
                 return RegExpRunStatus_Success_NotFound;
             if (!HasSubstringAt(input, source, start))
                 return RegExpRunStatus_Success_NotFound;
 
             if (matches) {
                 (*matches)[0].start = start;
                 (*matches)[0].limit = start + sourceLength;
 
                 matches->checkAgainst(length);
+            } else if (endIndex) {
+                *endIndex = start + sourceLength;
             }
             return RegExpRunStatus_Success;
         }
 
         int res = StringFindPattern(input, source, start);
         if (res == -1)
             return RegExpRunStatus_Success_NotFound;
 
         if (matches) {
             (*matches)[0].start = res;
             (*matches)[0].limit = res + sourceLength;
 
             matches->checkAgainst(length);
+        } else if (endIndex) {
+            *endIndex = res + sourceLength;
         }
         return RegExpRunStatus_Success;
     }
 
     do {
-        jit::JitCode* code = compilation(mode, input->hasLatin1Chars()).jitCode;
+        jit::JitCode* code = compilation(mode, sticky, input->hasLatin1Chars()).jitCode;
         if (!code)
             break;
 
         RegExpRunStatus result;
         {
             AutoTraceLog logJIT(logger, TraceLogger_IrregexpExecute);
             AutoCheckCannotGC nogc;
             if (input->hasLatin1Chars()) {
                 const Latin1Char* chars = input->latin1Chars(nogc);
-                result = irregexp::ExecuteCode(cx, code, chars, start, length, matches);
+                result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, endIndex);
             } else {
                 const char16_t* chars = input->twoByteChars(nogc);
-                result = irregexp::ExecuteCode(cx, code, chars, start, length, matches);
+                result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, endIndex);
             }
         }
 
         if (result == RegExpRunStatus_Error) {
             // An 'Error' result is returned if a stack overflow guard or
             // interrupt guard failed. If CheckOverRecursed doesn't throw, break
             // out and retry the regexp in the bytecode interpreter, which can
             // execute while tolerating future interrupts. Otherwise, if we keep
@@ -653,33 +659,33 @@ RegExpShared::execute(JSContext* cx, Han
         MOZ_ASSERT(result == RegExpRunStatus_Success);
 
         if (matches)
             matches->checkAgainst(length);
         return RegExpRunStatus_Success;
     } while (false);
 
     // Compile bytecode for the RegExp if necessary.
-    if (!compileIfNecessary(cx, input, mode, ForceByteCode))
+    if (!compileIfNecessary(cx, input, mode, sticky, ForceByteCode))
         return RegExpRunStatus_Error;
 
-    uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
+    uint8_t* byteCode = compilation(mode, sticky, input->hasLatin1Chars()).byteCode;
     AutoTraceLog logInterpreter(logger, TraceLogger_IrregexpExecute);
 
     AutoStableStringChars inputChars(cx);
     if (!inputChars.init(cx, input))
         return RegExpRunStatus_Error;
 
     RegExpRunStatus result;
     if (inputChars.isLatin1()) {
         const Latin1Char* chars = inputChars.latin1Range().start().get();
-        result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches);
+        result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
     } else {
         const char16_t* chars = inputChars.twoByteRange().start().get();
-        result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches);
+        result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
     }
 
     if (result == RegExpRunStatus_Success && matches)
         matches->checkAgainst(length);
     return result;
 }
 
 size_t
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -122,54 +122,54 @@ class RegExpShared
     /* Source to the RegExp, for lazy compilation. */
     RelocatablePtrAtom source;
 
     RegExpFlag         flags;
     size_t             parenCount;
     bool               canStringMatch;
     bool               marked_;
 
-    RegExpCompilation  compilationArray[4];
+    RegExpCompilation  compilationArray[8];
 
-    static int CompilationIndex(CompilationMode mode, bool latin1) {
+    static int CompilationIndex(CompilationMode mode, bool sticky, bool latin1) {
         switch (mode) {
-          case Normal:    return latin1 ? 0 : 1;
-          case MatchOnly: return latin1 ? 2 : 3;
+          case Normal:    return sticky ? (latin1 ? 0 : 1) : (latin1 ? 2 : 3);
+          case MatchOnly: return sticky ? (latin1 ? 4 : 5) : (latin1 ? 6 : 7);
         }
         MOZ_CRASH();
     }
 
     // Tables referenced by JIT code.
     Vector<uint8_t*, 0, SystemAllocPolicy> tables;
 
     /* Internal functions. */
     bool compile(JSContext* cx, HandleLinearString input,
-                 CompilationMode mode, ForceByteCodeEnum force);
+                 CompilationMode mode, bool sticky, ForceByteCodeEnum force);
     bool compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
-                 CompilationMode mode, ForceByteCodeEnum force);
+                 CompilationMode mode, bool sticky, ForceByteCodeEnum force);
 
     bool compileIfNecessary(JSContext* cx, HandleLinearString input,
-                            CompilationMode mode, ForceByteCodeEnum force);
+                            CompilationMode mode, bool sticky, ForceByteCodeEnum force);
 
-    const RegExpCompilation& compilation(CompilationMode mode, bool latin1) const {
-        return compilationArray[CompilationIndex(mode, latin1)];
+    const RegExpCompilation& compilation(CompilationMode mode, bool sticky, bool latin1) const {
+        return compilationArray[CompilationIndex(mode, sticky, latin1)];
     }
 
-    RegExpCompilation& compilation(CompilationMode mode, bool latin1) {
-        return compilationArray[CompilationIndex(mode, latin1)];
+    RegExpCompilation& compilation(CompilationMode mode, bool sticky, bool latin1) {
+        return compilationArray[CompilationIndex(mode, sticky, latin1)];
     }
 
   public:
     RegExpShared(JSAtom* source, RegExpFlag flags);
     ~RegExpShared();
 
     // Execute this RegExp on input starting from searchIndex, filling in
     // matches if specified and otherwise only determining if there is a match.
     RegExpRunStatus execute(JSContext* cx, HandleLinearString input, size_t searchIndex,
-                            MatchPairs* matches);
+                            bool sticky, MatchPairs* matches, size_t* endIndex);
 
     // Register a table with this RegExpShared, and take ownership.
     bool addTable(uint8_t* table) {
         return tables.append(table);
     }
 
     /* Accessors */
 
@@ -184,23 +184,25 @@ class RegExpShared
     JSAtom* getSource() const           { return source; }
     RegExpFlag getFlags() const         { return flags; }
     bool ignoreCase() const             { return flags & IgnoreCaseFlag; }
     bool global() const                 { return flags & GlobalFlag; }
     bool multiline() const              { return flags & MultilineFlag; }
     bool sticky() const                 { return flags & StickyFlag; }
     bool unicode() const                { return flags & UnicodeFlag; }
 
-    bool isCompiled(CompilationMode mode, bool latin1,
+    bool isCompiled(CompilationMode mode, bool sticky, bool latin1,
                     ForceByteCodeEnum force = DontForceByteCode) const {
-        return compilation(mode, latin1).compiled(force);
+        return compilation(mode, sticky, latin1).compiled(force);
     }
     bool isCompiled() const {
-        return isCompiled(Normal, true) || isCompiled(Normal, false)
-            || isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
+        return isCompiled(Normal, true, true) || isCompiled(Normal, true, false)
+            || isCompiled(Normal, false, true) || isCompiled(Normal, false, false)
+            || isCompiled(MatchOnly, true, true) || isCompiled(MatchOnly, true, false)
+            || isCompiled(MatchOnly, false, true) || isCompiled(MatchOnly, false, false);
     }
 
     void trace(JSTracer* trc);
 
     bool marked() const { return marked_; }
     void clearMarked() { marked_ = false; }
 
     static size_t offsetOfSource() {
@@ -210,19 +212,34 @@ class RegExpShared
     static size_t offsetOfFlags() {
         return offsetof(RegExpShared, flags);
     }
 
     static size_t offsetOfParenCount() {
         return offsetof(RegExpShared, parenCount);
     }
 
-    static size_t offsetOfJitCode(CompilationMode mode, bool latin1) {
+    static size_t offsetOfStickyLatin1JitCode(CompilationMode mode) {
+        return offsetof(RegExpShared, compilationArray)
+             + (CompilationIndex(mode, true, true) * sizeof(RegExpCompilation))
+             + offsetof(RegExpCompilation, jitCode);
+    }
+    static size_t offsetOfNotStickyLatin1JitCode(CompilationMode mode) {
         return offsetof(RegExpShared, compilationArray)
-             + (CompilationIndex(mode, latin1) * sizeof(RegExpCompilation))
+             + (CompilationIndex(mode, false, true) * sizeof(RegExpCompilation))
+             + offsetof(RegExpCompilation, jitCode);
+    }
+    static size_t offsetOfStickyTwoByteJitCode(CompilationMode mode) {
+        return offsetof(RegExpShared, compilationArray)
+             + (CompilationIndex(mode, true, false) * sizeof(RegExpCompilation))
+             + offsetof(RegExpCompilation, jitCode);
+    }
+    static size_t offsetOfNotStickyTwoByteJitCode(CompilationMode mode) {
+        return offsetof(RegExpShared, compilationArray)
+             + (CompilationIndex(mode, false, false) * sizeof(RegExpCompilation))
              + offsetof(RegExpCompilation, jitCode);
     }
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 
 /*
  * Extend the lifetime of a given RegExpShared to at least the lifetime of
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -96,17 +96,17 @@ RegExpStatics::executeLazy(JSContext* cx
 
     /*
      * It is not necessary to call aboutToWrite(): evaluation of
      * implicit copies is safe.
      */
 
     /* Execute the full regular expression. */
     RootedLinearString input(cx, matchesInput);
-    RegExpRunStatus status = g->execute(cx, input, lazyIndex, &this->matches);
+    RegExpRunStatus status = g->execute(cx, input, lazyIndex, lazySticky, &this->matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     /*
      * RegExpStatics are only updated on successful (matching) execution.
      * Re-running the same expression must therefore produce a matching result.
      */
     MOZ_ASSERT(status == RegExpRunStatus_Success);
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -26,16 +26,17 @@ class RegExpStatics
     /*
      * The previous RegExp input, used to resolve lazy state.
      * A raw RegExpShared cannot be stored because it may be in
      * a different compartment via evalcx().
      */
     RelocatablePtrAtom      lazySource;
     RegExpFlag              lazyFlags;
     size_t                  lazyIndex;
+    bool                    lazySticky;
 
     /* The latest RegExp input, set before execution. */
     RelocatablePtrString    pendingInput;
     RegExpFlag              flags;
 
     /*
      * If non-zero, |matchesInput| and the |lazy*| fields may be used
      * to replay the last executed RegExp, and |matches| is invalid.
@@ -61,17 +62,17 @@ class RegExpStatics
     void markFlagsSet(JSContext* cx);
 
     struct InitBuffer {};
     explicit RegExpStatics(InitBuffer) {}
 
   public:
     /* Mutators. */
     inline void updateLazily(JSContext* cx, JSLinearString* input,
-                             RegExpShared* shared, size_t lastIndex);
+                             RegExpShared* shared, size_t lastIndex, bool sticky);
     inline bool updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs);
 
     void setMultiline(JSContext* cx, bool enabled) {
         if (enabled) {
             flags = RegExpFlag(flags | MultilineFlag);
             markFlagsSet(cx);
         } else {
             flags = RegExpFlag(flags & ~MultilineFlag);
@@ -148,16 +149,20 @@ class RegExpStatics
     static size_t offsetOfLazyFlags() {
         return offsetof(RegExpStatics, lazyFlags);
     }
 
     static size_t offsetOfLazyIndex() {
         return offsetof(RegExpStatics, lazyIndex);
     }
 
+    static size_t offsetOfLazySticky() {
+        return offsetof(RegExpStatics, lazySticky);
+    }
+
     static size_t offsetOfPendingLazyEvaluation() {
         return offsetof(RegExpStatics, pendingLazyEvaluation);
     }
 };
 
 inline bool
 RegExpStatics::createDependent(JSContext* cx, size_t start, size_t end, MutableHandleValue out)
 {
@@ -336,27 +341,28 @@ RegExpStatics::getRightContext(JSSubStri
     }
     MOZ_ASSERT(matches[0].limit <= int(matchesInput->length()));
     size_t length = matchesInput->length() - matches[0].limit;
     out->init(matchesInput, matches[0].limit, length);
 }
 
 inline void
 RegExpStatics::updateLazily(JSContext* cx, JSLinearString* input,
-                            RegExpShared* shared, size_t lastIndex)
+                            RegExpShared* shared, size_t lastIndex, bool sticky)
 {
     MOZ_ASSERT(input && shared);
 
     BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
                                                pendingInput, input,
                                                matchesInput, input);
 
     lazySource = shared->source;
     lazyFlags = shared->flags;
     lazyIndex = lastIndex;
+    lazySticky = sticky;
     pendingLazyEvaluation = 1;
 }
 
 inline bool
 RegExpStatics::updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs)
 {
     MOZ_ASSERT(input);
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1691,16 +1691,26 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0),
     JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0),
     JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0),
     JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0),
     JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),
     JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0),
     JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0),
 
+    JS_INLINABLE_FN("IsRegExpObject",
+                    intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
+                    IsRegExpObject),
+    JS_FN("CallRegExpMethodIfWrapped",
+          CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2,0),
+    JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 5,0,
+                    RegExpMatcher),
+    JS_INLINABLE_FN("RegExpTester", RegExpTester, 5,0,
+                    RegExpTester),
+
     // See builtin/RegExp.h for descriptions of the regexp_* functions.
     JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0),
     JS_FN("regexp_test_no_statics", regexp_test_no_statics, 2,0),
     JS_FN("regexp_construct_no_statics", regexp_construct_no_statics, 2,0),
 
     JS_FN("IsModule", intrinsic_IsInstanceOfBuiltin<ModuleObject>, 1, 0),
     JS_FN("CallModuleMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<ModuleObject>>, 2, 0),