Bug 887016 - Part 6: Add RegExpPrototypeOptimizable. r=nbp
☠☠ backed out by 2081fb1b83a1 ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Sun, 22 Nov 2015 03:07:36 +0900
changeset 290574 56e25768c99addacea51258b2392926749da2df5
parent 290573 b77cb94666ca383ff7586997aa820b692794f557
child 290575 dee348be668513677d339437e9fcf4d6b5b6ce01
push id18399
push userkwierso@gmail.com
push dateMon, 28 Mar 2016 20:08:57 +0000
treeherderfx-team@f40c52c4b800 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs887016
milestone48.0a1
Bug 887016 - Part 6: Add RegExpPrototypeOptimizable. r=nbp
js/src/builtin/RegExp.cpp
js/src/builtin/RegExp.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/InlinableNatives.h
js/src/jit/IonBuilder.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/shared/LIR-shared.h
js/src/jit/shared/LOpcodes-shared.h
js/src/jscompartment.h
js/src/vm/CommonPropertyNames.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1018,8 +1018,89 @@ js::regexp_test_no_statics(JSContext* cx
     RootedString string(cx, args[1].toString());
 
     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;
 }
+
+bool
+js::RegExpPrototypeOptimizable(JSContext* cx, unsigned argc, Value* vp)
+{
+    // This can only be called from self-hosted code.
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 1);
+
+    uint8_t result = false;
+    if (!RegExpPrototypeOptimizableRaw(cx, &args[0].toObject(), &result))
+        return false;
+
+    args.rval().setBoolean(result);
+    return true;
+}
+
+bool
+js::RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto, uint8_t* result)
+{
+    JS::AutoCheckCannotGC nogc;
+    if (!proto->isNative()) {
+        *result = false;
+        return true;
+    }
+
+    NativeObject* nproto = static_cast<NativeObject*>(proto);
+
+    Shape* shape = cx->compartment()->regExps.getOptimizableRegExpPrototypeShape();
+    if (shape == nproto->lastProperty()) {
+        *result = true;
+        return true;
+    }
+
+    JSNative globalGetter;
+    if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().global), &globalGetter))
+        return false;
+
+    if (globalGetter != regexp_global) {
+        *result = false;
+        return true;
+    }
+
+    JSNative stickyGetter;
+    if (!GetOwnNativeGetterPure(cx, proto, NameToId(cx->names().sticky), &stickyGetter))
+        return false;
+
+    if (stickyGetter != regexp_sticky) {
+        *result = false;
+        return true;
+    }
+
+    // Check if @@match, @@search, and exec are own data properties,
+    // those values should be tested in selfhosted JS.
+    bool has = false;
+    if (!HasOwnDataPropertyPure(cx, proto, SYMBOL_TO_JSID(cx->wellKnownSymbols().match), &has))
+        return false;
+    if (!has) {
+        *result = false;
+        return true;
+    }
+
+    /*
+    if (!HasOwnDataPropertyPure(cx, proto, SYMBOL_TO_JSID(cx->wellKnownSymbols().search), &has))
+        return false;
+    if (!has) {
+        *result = false;
+        return true;
+    }
+    */
+
+    if (!HasOwnDataPropertyPure(cx, proto, NameToId(cx->names().exec), &has))
+        return false;
+    if (!has) {
+        *result = false;
+        return true;
+    }
+
+    cx->compartment()->regExps.setOptimizableRegExpPrototypeShape(nproto->lastProperty());
+    *result = true;
+    return true;
+}
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -86,16 +86,22 @@ extern bool
 regexp_construct_self_hosting(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 IsRegExp(JSContext* cx, HandleValue value, bool* result);
 
 extern bool
 RegExpCreate(JSContext* cx, HandleValue pattern, HandleValue flags, MutableHandleValue rval);
 
+extern bool
+RegExpPrototypeOptimizable(JSContext* cx, unsigned argc, Value* vp);
+
+extern bool
+RegExpPrototypeOptimizableRaw(JSContext* cx, JSObject* proto, uint8_t* result);
+
 // RegExp ClassSpec members used in RegExpObject.cpp.
 extern bool
 regexp_construct(JSContext* cx, unsigned argc, Value* vp);
 extern const JSPropertySpec regexp_static_props[];
 extern const JSPropertySpec regexp_properties[];
 extern const JSFunctionSpec regexp_methods[];
 
 // Used in RegExpObject::isOriginalFlagGetter.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/SizePrintfMacros.h"
 
 #include "jslibmath.h"
 #include "jsmath.h"
 #include "jsnum.h"
 #include "jsprf.h"
+#include "jsstr.h"
 
 #include "builtin/Eval.h"
 #include "builtin/TypedObject.h"
 #include "gc/Nursery.h"
 #include "irregexp/NativeRegExpMacroAssembler.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineCompiler.h"
 #include "jit/IonBuilder.h"
@@ -33,16 +34,17 @@
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #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/RegExpObject.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"
@@ -1959,16 +1961,85 @@ CodeGenerator::visitRegExpTester(LRegExp
 
     JitCode* regExpTesterStub = gen->compartment->jitCompartment()->regExpTesterStubNoBarrier();
     masm.call(regExpTesterStub);
 
     masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpTesterResultFailed), ool->entry());
     masm.bind(ool->rejoin());
 }
 
+class OutOfLineRegExpPrototypeOptimizable : public OutOfLineCodeBase<CodeGenerator>
+{
+    LRegExpPrototypeOptimizable* ins_;
+
+  public:
+    explicit OutOfLineRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
+      : ins_(ins)
+    { }
+
+    void accept(CodeGenerator* codegen) {
+        codegen->visitOutOfLineRegExpPrototypeOptimizable(this);
+    }
+    LRegExpPrototypeOptimizable* ins() const {
+        return ins_;
+    }
+};
+
+void
+CodeGenerator::visitRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
+{
+    Register object = ToRegister(ins->object());
+    Register output = ToRegister(ins->output());
+    Register temp = ToRegister(ins->temp());
+
+    OutOfLineRegExpPrototypeOptimizable* ool = new(alloc()) OutOfLineRegExpPrototypeOptimizable(ins);
+    addOutOfLineCode(ool, ins->mir());
+
+    masm.loadJSContext(temp);
+    masm.loadPtr(Address(temp, JSContext::offsetOfCompartment()), temp);
+    masm.loadPtr(Address(temp, JSCompartment::offsetOfRegExps()), temp);
+    masm.loadPtr(Address(temp, RegExpCompartment::offsetOfOptimizableRegExpPrototypeShape()),
+                 temp);
+
+    masm.loadPtr(Address(object, JSObject::offsetOfShape()), output);
+    masm.branchPtr(Assembler::NotEqual, output, temp, ool->entry());
+    masm.move32(Imm32(0x1), output);
+
+    masm.bind(ool->rejoin());
+}
+
+void
+CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable(OutOfLineRegExpPrototypeOptimizable* ool)
+{
+    LRegExpPrototypeOptimizable* ins = ool->ins();
+    Register object = ToRegister(ins->object());
+    Register output = ToRegister(ins->output());
+    Register temp = ToRegister(ins->temp());
+
+    saveVolatile(output);
+
+    masm.reserveStack(sizeof(void*));
+    masm.moveStackPtrTo(temp);
+
+    masm.setupUnalignedABICall(output);
+    masm.loadJSContext(output);
+    masm.passABIArg(output);
+    masm.passABIArg(object);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, RegExpPrototypeOptimizableRaw));
+    masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
+
+    masm.load8ZeroExtend(Address(masm.getStackPointer(), 0), output);
+    masm.freeStack(sizeof(void*));
+
+    restoreVolatile(output);
+
+    masm.jump(ool->rejoin());
+}
+
 typedef JSString* (*RegExpReplaceFn)(JSContext*, HandleString, HandleObject, HandleString);
 static const VMFunction RegExpReplaceInfo = FunctionInfo<RegExpReplaceFn>(RegExpReplace);
 
 void
 CodeGenerator::visitRegExpReplace(LRegExpReplace* lir)
 {
     if (lir->replacement()->isConstant())
         pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -43,16 +43,17 @@ class OutOfLineStoreElementHole;
 class OutOfLineTypeOfV;
 class OutOfLineUpdateCache;
 class OutOfLineCallPostWriteBarrier;
 class OutOfLineCallPostWriteElementBarrier;
 class OutOfLineIsCallable;
 class OutOfLineIsConstructor;
 class OutOfLineRegExpMatcher;
 class OutOfLineRegExpTester;
+class OutOfLineRegExpPrototypeOptimizable;
 class OutOfLineLambdaArrow;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     void generateArgumentsChecks(bool bailout = true);
     bool generateBody();
 
     ConstantOrRegister toConstantOrRegister(LInstruction* lir, size_t n, MIRType type);
@@ -108,16 +109,18 @@ class CodeGenerator : public CodeGenerat
     void visitValueToObjectOrNull(LValueToObjectOrNull* lir);
     void visitInteger(LInteger* lir);
     void visitInteger64(LInteger64* lir);
     void visitRegExp(LRegExp* lir);
     void visitRegExpMatcher(LRegExpMatcher* lir);
     void visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool);
     void visitRegExpTester(LRegExpTester* lir);
     void visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool);
+    void visitRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* lir);
+    void visitOutOfLineRegExpPrototypeOptimizable(OutOfLineRegExpPrototypeOptimizable* 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
@@ -63,16 +63,17 @@
     _(MathACosH)                    \
     _(MathSign)                     \
     _(MathTrunc)                    \
     _(MathCbrt)                     \
                                     \
     _(RegExpMatcher)                \
     _(RegExpTester)                 \
     _(IsRegExpObject)               \
+    _(RegExpPrototypeOptimizable)   \
                                     \
     _(String)                       \
     _(StringSplit)                  \
     _(StringCharCodeAt)             \
     _(StringFromCharCode)           \
     _(StringCharAt)                 \
     _(StringReplace)                \
                                     \
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -818,16 +818,17 @@ class IonBuilder
     InliningStatus inlineStrFromCharCode(CallInfo& callInfo);
     InliningStatus inlineStrCharAt(CallInfo& callInfo);
     InliningStatus inlineStrReplace(CallInfo& callInfo);
 
     // RegExp intrinsics.
     InliningStatus inlineRegExpMatcher(CallInfo& callInfo);
     InliningStatus inlineRegExpTester(CallInfo& callInfo);
     InliningStatus inlineIsRegExpObject(CallInfo& callInfo);
+    InliningStatus inlineRegExpPrototypeOptimizable(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/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2281,16 +2281,26 @@ LIRGenerator::visitRegExpTester(MRegExpT
                                                     useFixedAtStart(ins->string(), RegExpTesterStringReg),
                                                     useFixedAtStart(ins->lastIndex(), RegExpTesterLastIndexReg),
                                                     useFixedAtStart(ins->sticky(), RegExpTesterStickyReg));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
+LIRGenerator::visitRegExpPrototypeOptimizable(MRegExpPrototypeOptimizable* ins)
+{
+    MOZ_ASSERT(ins->object()->type() == MIRType_Object);
+    MOZ_ASSERT(ins->type() == MIRType_Boolean);
+    LRegExpPrototypeOptimizable* lir = new(alloc()) LRegExpPrototypeOptimizable(useRegister(ins->object()),
+                                                                                temp());
+    define(lir, ins);
+}
+
+void
 LIRGenerator::visitRegExpReplace(MRegExpReplace* ins)
 {
     MOZ_ASSERT(ins->pattern()->type() == MIRType_Object);
     MOZ_ASSERT(ins->string()->type() == MIRType_String);
     MOZ_ASSERT(ins->replacement()->type() == MIRType_String);
 
     LRegExpReplace* lir = new(alloc()) LRegExpReplace(useRegisterOrConstantAtStart(ins->string()),
                                                       useRegisterAtStart(ins->pattern()),
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -160,16 +160,17 @@ class LIRGenerator : public LIRGenerator
     void visitTruncateToInt32(MTruncateToInt32* truncate);
     void visitWrapInt64ToInt32(MWrapInt64ToInt32* ins);
     void visitExtendInt32ToInt64(MExtendInt32ToInt64* ins);
     void visitToString(MToString* convert);
     void visitToObjectOrNull(MToObjectOrNull* convert);
     void visitRegExp(MRegExp* ins);
     void visitRegExpMatcher(MRegExpMatcher* ins);
     void visitRegExpTester(MRegExpTester* ins);
+    void visitRegExpPrototypeOptimizable(MRegExpPrototypeOptimizable* 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
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsmath.h"
+#include "jsstr.h"
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/SIMD.h"
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/InlinableNatives.h"
 #include "jit/IonBuilder.h"
@@ -176,16 +177,18 @@ IonBuilder::inlineNativeCall(CallInfo& c
 
       // RegExp natives.
       case InlinableNative::RegExpMatcher:
         return inlineRegExpMatcher(callInfo);
       case InlinableNative::RegExpTester:
         return inlineRegExpTester(callInfo);
       case InlinableNative::IsRegExpObject:
         return inlineIsRegExpObject(callInfo);
+      case InlinableNative::RegExpPrototypeOptimizable:
+        return inlineRegExpPrototypeOptimizable(callInfo);
 
       // String natives.
       case InlinableNative::String:
         return inlineStringObject(callInfo);
       case InlinableNative::StringSplit:
         return inlineStringSplit(callInfo);
       case InlinableNative::StringCharCodeAt:
         return inlineStrCharCodeAt(callInfo);
@@ -1877,16 +1880,41 @@ IonBuilder::inlineIsRegExpObject(CallInf
 
     pushConstant(BooleanValue(isRegExpObject));
 
     callInfo.setImplicitlyUsedUnchecked();
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineRegExpPrototypeOptimizable(CallInfo& callInfo)
+{
+    if (callInfo.argc() != 1 || callInfo.constructing()) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
+        return InliningStatus_NotInlined;
+    }
+
+    MDefinition* protoArg = callInfo.getArg(0);
+
+    if (protoArg->type() != MIRType_Object)
+        return InliningStatus_NotInlined;
+
+    if (getInlineReturnType() != MIRType_Boolean)
+        return InliningStatus_NotInlined;
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    MInstruction* opt = MRegExpPrototypeOptimizable::New(alloc(), protoArg);
+    current->add(opt);
+    current->push(opt);
+
+    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
@@ -7987,16 +7987,41 @@ class MRegExpTester
     }
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
 };
 
+class MRegExpPrototypeOptimizable
+  : public MUnaryInstruction,
+    public SingleObjectPolicy::Data
+{
+    explicit MRegExpPrototypeOptimizable(MDefinition* object)
+      : MUnaryInstruction(object)
+    {
+        setResultType(MIRType_Boolean);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(RegExpPrototypeOptimizable)
+
+    static MRegExpPrototypeOptimizable* New(TempAllocator& alloc, MDefinition* obj) {
+        return new(alloc) MRegExpPrototypeOptimizable(obj);
+    }
+    MDefinition* object() const {
+        return getOperand(0);
+    }
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+};
+
 template <class Policy1>
 class MStrReplace
   : public MTernaryInstruction,
     public Mix3Policy<StringPolicy<0>, Policy1, StringPolicy<2> >::Data
 {
   protected:
 
     MStrReplace(MDefinition* string, MDefinition* pattern, MDefinition* replacement)
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -142,16 +142,17 @@ namespace jit {
     _(InitPropGetterSetter)                                                 \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
     _(Nop)                                                                  \
     _(LimitedTruncate)                                                      \
     _(RegExp)                                                               \
     _(RegExpMatcher)                                                        \
     _(RegExpTester)                                                         \
+    _(RegExpPrototypeOptimizable)                                           \
     _(RegExpReplace)                                                        \
     _(StringReplace)                                                        \
     _(Lambda)                                                               \
     _(LambdaArrow)                                                          \
     _(KeepAliveObject)                                                      \
     _(Slots)                                                                \
     _(Elements)                                                             \
     _(ConstantElements)                                                     \
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -4328,16 +4328,35 @@ class LRegExpTester : public LCallInstru
         return getOperand(3);
     }
 
     const MRegExpTester* mir() const {
         return mir_->toRegExpTester();
     }
 };
 
+class LRegExpPrototypeOptimizable : public LInstructionHelper<1, 1, 1>
+{
+  public:
+    LIR_HEADER(RegExpPrototypeOptimizable);
+    explicit LRegExpPrototypeOptimizable(const LAllocation& object, const LDefinition& temp) {
+        setOperand(0, object);
+        setTemp(0, temp);
+    }
+
+    const LAllocation* object() {
+        return getOperand(0);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    MRegExpPrototypeOptimizable* mir() const {
+        return mir_->toRegExpPrototypeOptimizable();
+    }
+};
 
 class LStrReplace : public LCallInstructionHelper<1, 3, 0>
 {
   public:
     LStrReplace(const LAllocation& string, const LAllocation& pattern,
                    const LAllocation& replacement)
     {
         setOperand(0, string);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -203,16 +203,17 @@
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(OsrReturnValue)               \
     _(OsrArgumentsObject)           \
     _(RegExp)                       \
     _(RegExpMatcher)                \
     _(RegExpTester)                 \
+    _(RegExpPrototypeOptimizable)   \
     _(RegExpReplace)                \
     _(StringReplace)                \
     _(Substr)                       \
     _(BinarySharedStub)             \
     _(UnarySharedStub)              \
     _(Lambda)                       \
     _(LambdaArrow)                  \
     _(LambdaForSingleton)           \
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -620,16 +620,20 @@ struct JSCompartment
     js::DtoaCache dtoaCache;
 
     // Random number generator for Math.random().
     mozilla::Maybe<mozilla::non_crypto::XorShift128PlusRNG> randomNumberGenerator;
 
     // Initialize randomNumberGenerator if needed.
     void ensureRandomNumberGenerator();
 
+    static size_t offsetOfRegExps() {
+        return offsetof(JSCompartment, regExps);
+    }
+
   private:
     JSCompartment* thisForCtor() { return this; }
 
   public:
     //
     // The Debugger observes execution on a frame-by-frame basis. The
     // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee,
     // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -91,16 +91,17 @@
     macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \
     macro(endTimestamp, endTimestamp, "endTimestamp") \
     macro(entries, entries, "entries") \
     macro(enumerable, enumerable, "enumerable") \
     macro(enumerate, enumerate, "enumerate") \
     macro(era, era, "era") \
     macro(escape, escape, "escape") \
     macro(eval, eval, "eval") \
+    macro(exec, exec, "exec") \
     macro(false, false_, "false") \
     macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
     macro(fieldTypes, fieldTypes, "fieldTypes") \
     macro(fileName, fileName, "fileName") \
     macro(fill, fill, "fill") \
     macro(find, find, "find") \
     macro(findIndex, findIndex, "findIndex") \
     macro(fix, fix, "fix") \
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -714,17 +714,19 @@ RegExpShared::sizeOfIncludingThis(mozill
         n += mallocSizeOf(tables[i]);
 
     return n;
 }
 
 /* RegExpCompartment */
 
 RegExpCompartment::RegExpCompartment(JSRuntime* rt)
-  : set_(rt), matchResultTemplateObject_(nullptr)
+  : set_(rt),
+    matchResultTemplateObject_(nullptr),
+    optimizableRegExpPrototypeShape_(nullptr)
 {}
 
 RegExpCompartment::~RegExpCompartment()
 {
     // Because of stray mark bits being set (see RegExpCompartment::sweep)
     // there might still be RegExpShared instances which haven't been deleted.
     if (set_.initialized()) {
         for (Set::Enum e(set_); !e.empty(); e.popFront()) {
@@ -839,16 +841,22 @@ RegExpCompartment::sweep(JSRuntime* rt)
         }
     }
 
     if (matchResultTemplateObject_ &&
         IsAboutToBeFinalized(&matchResultTemplateObject_))
     {
         matchResultTemplateObject_.set(nullptr);
     }
+
+    if (optimizableRegExpPrototypeShape_ &&
+        IsAboutToBeFinalized(&optimizableRegExpPrototypeShape_))
+    {
+        optimizableRegExpPrototypeShape_.set(nullptr);
+    }
 }
 
 bool
 RegExpCompartment::get(JSContext* cx, JSAtom* source, RegExpFlag flags, RegExpGuard* g)
 {
     Key key(source, flags);
     Set::AddPtr p = set_.lookupForAdd(key);
     if (p) {
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -321,16 +321,26 @@ class RegExpCompartment
 
     /*
      * This is the template object where the result of re.exec() is based on,
      * if there is a result. This is used in CreateRegExpMatchResult to set
      * the input/index properties faster.
      */
     ReadBarriered<ArrayObject*> matchResultTemplateObject_;
 
+    /*
+     * The shape of RegExp.prototype object that satisfies following:
+     *   * RegExp.prototype.global getter is not modified
+     *   * RegExp.prototype.sticky getter is not modified
+     *   * RegExp.prototype.exec is an own data property
+     *   * RegExp.prototype[@@match] is an own data property
+     *   * RegExp.prototype[@@search] is an own data property
+     */
+    ReadBarriered<Shape*> optimizableRegExpPrototypeShape_;
+
     ArrayObject* createMatchResultTemplateObject(JSContext* cx);
 
   public:
     explicit RegExpCompartment(JSRuntime* rt);
     ~RegExpCompartment();
 
     bool init(JSContext* cx);
     void sweep(JSRuntime* rt);
@@ -344,16 +354,27 @@ class RegExpCompartment
 
     /* Get or create template object used to base the result of .exec() on. */
     ArrayObject* getOrCreateMatchResultTemplateObject(JSContext* cx) {
         if (matchResultTemplateObject_)
             return matchResultTemplateObject_;
         return createMatchResultTemplateObject(cx);
     }
 
+    Shape* getOptimizableRegExpPrototypeShape() {
+        return optimizableRegExpPrototypeShape_;
+    }
+    void setOptimizableRegExpPrototypeShape(Shape* shape) {
+        optimizableRegExpPrototypeShape_ = shape;
+    }
+
+    static size_t offsetOfOptimizableRegExpPrototypeShape() {
+        return offsetof(RegExpCompartment, optimizableRegExpPrototypeShape_);
+    }
+
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 
 class RegExpObject : public NativeObject
 {
     static const unsigned LAST_INDEX_SLOT          = 0;
     static const unsigned SOURCE_SLOT              = 1;
     static const unsigned FLAGS_SLOT               = 2;
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2366,16 +2366,18 @@ static const JSFunctionSpec intrinsic_fu
                     IsRegExpObject),
     JS_FN("CallRegExpMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2,0),
     JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 4,0,
                     RegExpMatcher),
     JS_INLINABLE_FN("RegExpTester", RegExpTester, 4,0,
                     RegExpTester),
     JS_FN("RegExpCreate", intrinsic_RegExpCreate, 2,0),
+    JS_INLINABLE_FN("RegExpPrototypeOptimizable", RegExpPrototypeOptimizable, 1,0,
+                    RegExpPrototypeOptimizable),
 
     // 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", regexp_construct_self_hosting, 2,0),
 
     JS_FN("IsModule", intrinsic_IsInstanceOfBuiltin<ModuleObject>, 1, 0),
     JS_FN("CallModuleMethodIfWrapped",