Bug 951439 - Use CallVM for RegExp.exec(). r=h4writer
authorSean Stangl <sstangl@mozilla.com>
Mon, 06 Jan 2014 14:51:34 -0800
changeset 162289 72af8e9c325cdd9aaa05b8c7d53a933e86c18173
parent 162288 2c5fcba9480cbfcac8841d239596df9aed0b2a45
child 162290 039ef62a57c030038641c8a3863d101061c8a6a9
push idunknown
push userunknown
push dateunknown
reviewersh4writer
bugs951439
milestone29.0a1
Bug 951439 - Use CallVM for RegExp.exec(). r=h4writer
js/src/builtin/RegExp.cpp
js/src/builtin/RegExp.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.h
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.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/ParallelSafetyAnalysis.cpp
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -606,66 +606,74 @@ ExecuteRegExp(JSContext *cx, CallArgs ar
     if (!string)
         return RegExpRunStatus_Error;
 
     return ExecuteRegExp(cx, regexp, string, matches, UpdateRegExpStatics);
 }
 
 /* ES5 15.10.6.2. */
 static bool
-regexp_exec_impl(JSContext *cx, CallArgs args, HandleObject regexp, HandleString string,
-                 RegExpStaticsUpdate staticsUpdate)
+regexp_exec_impl(JSContext *cx, HandleObject regexp, HandleString string,
+                 RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
 {
     /* Execute regular expression and gather matches. */
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     MatchConduit conduit(&matches);
 
     RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, conduit, staticsUpdate);
 
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
-        args.rval().setNull();
+        rval.setNull();
         return true;
     }
 
-    return CreateRegExpMatchResult(cx, string, matches, args.rval());
+    return CreateRegExpMatchResult(cx, string, matches, rval);
 }
 
 static bool
 regexp_exec_impl(JSContext *cx, CallArgs args)
 {
     RootedObject regexp(cx, &args.thisv().toObject());
     RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
     if (!string)
         return false;
 
-    return regexp_exec_impl(cx, args, regexp, string, UpdateRegExpStatics);
+    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, IsRegExp, regexp_exec_impl, args);
 }
 
+/* Separate interface for use by IonMonkey. */
+bool
+js::regexp_exec_raw(JSContext *cx, HandleObject regexp, HandleString input, Value *vp)
+{
+    MutableHandleValue vpHandle = MutableHandleValue::fromMarkedLocation(vp);
+    return regexp_exec_impl(cx, regexp, input, UpdateRegExpStatics, vpHandle);
+}
+
 bool
 js::regexp_exec_no_statics(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ASSERT(args.length() == 2);
     JS_ASSERT(IsRegExp(args[0]));
     JS_ASSERT(args[1].isString());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
 
-    return regexp_exec_impl(cx, args, regexp, string, DontUpdateRegExpStatics);
+    return regexp_exec_impl(cx, regexp, string, DontUpdateRegExpStatics, args.rval());
 }
 
 /* ES5 15.10.6.3. */
 static bool
 regexp_test_impl(JSContext *cx, CallArgs args)
 {
     MatchPair match;
     MatchConduit conduit(&match);
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -44,16 +44,19 @@ bool
 CreateRegExpMatchResult(JSContext *cx, HandleString string, MatchPairs &matches,
                         MutableHandleValue rval);
 
 bool
 CreateRegExpMatchResult(JSContext *cx, HandleString input, const jschar *chars, size_t length,
                         MatchPairs &matches, MutableHandleValue rval);
 
 extern bool
+regexp_exec_raw(JSContext *cx, HandleObject regexp, HandleString input, Value *vp);
+
+extern bool
 regexp_exec(JSContext *cx, unsigned argc, Value *vp);
 
 bool
 regexp_test_raw(JSContext *cx, HandleObject regexp, HandleString input, bool *result);
 
 extern bool
 regexp_test(JSContext *cx, unsigned argc, Value *vp);
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -759,16 +759,28 @@ static const VMFunction CloneRegExpObjec
 
 bool
 CodeGenerator::visitRegExp(LRegExp *lir)
 {
     pushArg(ImmGCPtr(lir->mir()->source()));
     return callVM(CloneRegExpObjectInfo, lir);
 }
 
+typedef bool (*RegExpExecRawFn)(JSContext *cx, HandleObject regexp,
+                                HandleString input, Value *vp);
+static const VMFunction RegExpExecRawInfo = FunctionInfo<RegExpExecRawFn>(regexp_exec_raw);
+
+bool
+CodeGenerator::visitRegExpExec(LRegExpExec *lir)
+{
+    pushArg(ToRegister(lir->string()));
+    pushArg(ToRegister(lir->regexp()));
+    return callVM(RegExpExecRawInfo, lir);
+}
+
 typedef bool (*RegExpTestRawFn)(JSContext *cx, HandleObject regexp,
                                 HandleString input, bool *result);
 static const VMFunction RegExpTestRawInfo = FunctionInfo<RegExpTestRawFn>(regexp_test_raw);
 
 bool
 CodeGenerator::visitRegExpTest(LRegExpTest *lir)
 {
     pushArg(ToRegister(lir->string()));
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -85,16 +85,17 @@ class CodeGenerator : public CodeGenerat
     bool visitTestOAndBranch(LTestOAndBranch *lir);
     bool visitTestVAndBranch(LTestVAndBranch *lir);
     bool visitFunctionDispatch(LFunctionDispatch *lir);
     bool visitTypeObjectDispatch(LTypeObjectDispatch *lir);
     bool visitIntToString(LIntToString *lir);
     bool visitDoubleToString(LDoubleToString *lir);
     bool visitInteger(LInteger *lir);
     bool visitRegExp(LRegExp *lir);
+    bool visitRegExpExec(LRegExpExec *lir);
     bool visitRegExpTest(LRegExpTest *lir);
     bool visitLambda(LLambda *lir);
     bool visitLambdaForSingleton(LLambdaForSingleton *lir);
     bool visitLambdaPar(LLambdaPar *lir);
     bool visitPointer(LPointer *lir);
     bool visitSlots(LSlots *lir);
     bool visitStoreSlotV(LStoreSlotV *store);
     bool visitElements(LElements *lir);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -626,16 +626,17 @@ class IonBuilder : public MIRGenerator
     // String natives.
     InliningStatus inlineStringObject(CallInfo &callInfo);
     InliningStatus inlineStringSplit(CallInfo &callInfo);
     InliningStatus inlineStrCharCodeAt(CallInfo &callInfo);
     InliningStatus inlineStrFromCharCode(CallInfo &callInfo);
     InliningStatus inlineStrCharAt(CallInfo &callInfo);
 
     // RegExp natives.
+    InliningStatus inlineRegExpExec(CallInfo &callInfo);
     InliningStatus inlineRegExpTest(CallInfo &callInfo);
 
     // Array intrinsics.
     InliningStatus inlineUnsafePutElements(CallInfo &callInfo);
     bool inlineUnsafeSetDenseArrayElement(CallInfo &callInfo, uint32_t base);
     bool inlineUnsafeSetTypedArrayElement(CallInfo &callInfo, uint32_t base,
                                           ScalarTypeRepresentation::Type arrayType);
     InliningStatus inlineNewDenseArray(CallInfo &callInfo);
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3211,16 +3211,39 @@ 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)
+    {
+        setOperand(0, regexp);
+        setOperand(1, string);
+    }
+
+    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)
     {
         setOperand(0, regexp);
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -144,16 +144,17 @@
     _(DoubleToString)               \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(OsrReturnValue)               \
     _(OsrArgumentsObject)           \
     _(RegExp)                       \
+    _(RegExpExec)                   \
     _(RegExpTest)                   \
     _(Lambda)                       \
     _(LambdaForSingleton)           \
     _(LambdaPar)                    \
     _(ImplicitThis)                 \
     _(Slots)                        \
     _(Elements)                     \
     _(ConvertElementsToDoubles)     \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1912,16 +1912,27 @@ LIRGenerator::visitRegExp(MRegExp *ins)
         return define(new(alloc()) LPointer(source), ins);
     }
 
     LRegExp *lir = new(alloc()) LRegExp();
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitRegExpExec(MRegExpExec *ins)
+{
+    JS_ASSERT(ins->regexp()->type() == MIRType_Object);
+    JS_ASSERT(ins->string()->type() == MIRType_String);
+
+    LRegExpExec *lir = new(alloc()) LRegExpExec(useRegisterAtStart(ins->regexp()),
+                                                useRegisterAtStart(ins->string()));
+    return defineReturn(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitRegExpTest(MRegExpTest *ins)
 {
     JS_ASSERT(ins->regexp()->type() == MIRType_Object);
     JS_ASSERT(ins->string()->type() == MIRType_String);
 
     LRegExpTest *lir = new(alloc()) LRegExpTest(useRegisterAtStart(ins->regexp()),
                                                 useRegisterAtStart(ins->string()));
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -140,16 +140,17 @@ class LIRGenerator : public LIRGenerator
     bool visitOsrReturnValue(MOsrReturnValue *value);
     bool visitOsrArgumentsObject(MOsrArgumentsObject *object);
     bool visitToDouble(MToDouble *convert);
     bool visitToFloat32(MToFloat32 *convert);
     bool visitToInt32(MToInt32 *convert);
     bool visitTruncateToInt32(MTruncateToInt32 *truncate);
     bool visitToString(MToString *convert);
     bool visitRegExp(MRegExp *ins);
+    bool visitRegExpExec(MRegExpExec *ins);
     bool visitRegExpTest(MRegExpTest *ins);
     bool visitLambda(MLambda *ins);
     bool visitLambdaPar(MLambdaPar *ins);
     bool visitImplicitThis(MImplicitThis *ins);
     bool visitSlots(MSlots *ins);
     bool visitElements(MElements *ins);
     bool visitConstantElements(MConstantElements *ins);
     bool visitConvertElementsToDoubles(MConvertElementsToDoubles *ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -116,16 +116,18 @@ IonBuilder::inlineNativeCall(CallInfo &c
     if (native == js_str_charCodeAt)
         return inlineStrCharCodeAt(callInfo);
     if (native == js::str_fromCharCode)
         return inlineStrFromCharCode(callInfo);
     if (native == js_str_charAt)
         return inlineStrCharAt(callInfo);
 
     // RegExp natives.
+    if (native == regexp_exec && CallResultEscapes(pc))
+        return inlineRegExpExec(callInfo);
     if (native == regexp_exec && !CallResultEscapes(pc))
         return inlineRegExpTest(callInfo);
     if (native == regexp_test)
         return inlineRegExpTest(callInfo);
 
     // Array intrinsics.
     if (native == intrinsic_UnsafePutElements)
         return inlineUnsafePutElements(callInfo);
@@ -1104,16 +1106,44 @@ 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)
+{
+    if (callInfo.argc() != 1 || callInfo.constructing())
+        return InliningStatus_NotInlined;
+
+    if (callInfo.thisArg()->type() != MIRType_Object)
+        return InliningStatus_NotInlined;
+
+    types::TemporaryTypeSet *thisTypes = callInfo.thisArg()->resultTypeSet();
+    const Class *clasp = thisTypes ? thisTypes->getKnownClass() : nullptr;
+    if (clasp != &RegExpObject::class_)
+        return InliningStatus_NotInlined;
+
+    if (callInfo.getArg(0)->type() != MIRType_String)
+        return InliningStatus_NotInlined;
+
+    callInfo.setFoldedUnchecked();
+
+    MInstruction *exec = MRegExpExec::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
+    current->add(exec);
+    current->push(exec);
+    if (!resumeAfter(exec))
+        return InliningStatus_Error;
+
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineRegExpTest(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     // TI can infer a nullptr return type of regexp_test with eager compilation.
     if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean)
         return InliningStatus_NotInlined;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4802,16 +4802,53 @@ class MRegExp : public MNullaryInstructi
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
     bool possiblyCalls() const {
         return true;
     }
 };
 
+class MRegExpExec
+  : public MBinaryInstruction,
+    public MixPolicy<ObjectPolicy<1>, StringPolicy<0> >
+{
+  private:
+
+    MRegExpExec(MDefinition *regexp, MDefinition *string)
+      : MBinaryInstruction(string, regexp)
+    {
+        // 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);
+    }
+
+    MDefinition *regexp() const {
+        return getOperand(1);
+    }
+
+    TypePolicy *typePolicy() {
+        return this;
+    }
+
+    bool possiblyCalls() const {
+        return true;
+    }
+};
+
 class MRegExpTest
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<1>, StringPolicy<0> >
 {
   private:
 
     MRegExpTest(MDefinition *regexp, MDefinition *string)
       : MBinaryInstruction(string, regexp)
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -93,16 +93,17 @@ namespace jit {
     _(InitElem)                                                             \
     _(InitElemGetterSetter)                                                 \
     _(InitProp)                                                             \
     _(InitPropGetterSetter)                                                 \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
     _(Nop)                                                                  \
     _(RegExp)                                                               \
+    _(RegExpExec)                                                           \
     _(RegExpTest)                                                           \
     _(Lambda)                                                               \
     _(ImplicitThis)                                                         \
     _(Slots)                                                                \
     _(Elements)                                                             \
     _(ConstantElements)                                                     \
     _(ConvertElementsToDoubles)                                             \
     _(MaybeToDoubleElement)                                                 \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -274,16 +274,17 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(GetDOMProperty)
     UNSAFE_OP(GetDOMMember)
     UNSAFE_OP(SetDOMProperty)
     UNSAFE_OP(NewStringObject)
     UNSAFE_OP(Random)
     UNSAFE_OP(Pow)
     UNSAFE_OP(PowHalf)
     UNSAFE_OP(RegExpTest)
+    UNSAFE_OP(RegExpExec)
     UNSAFE_OP(CallInstanceOf)
     UNSAFE_OP(FunctionBoundary)
     UNSAFE_OP(GuardString)
     UNSAFE_OP(NewDeclEnvObject)
     UNSAFE_OP(In)
     UNSAFE_OP(InArray)
     SAFE_OP(GuardThreadLocalObject)
     SAFE_OP(CheckInterruptPar)