Transform RegExp.exec to RegExp.test in native call IC, bug 645889. r=dmandelin
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 30 Mar 2011 17:43:36 -0700
changeset 64556 ba8cc57e0848818df689149c4eed99c4c0d7b5e9
parent 64555 76d04b5e5e75345190149c1cfb015b668ccaae6f
child 64557 be3045fcd6fd1b8d003f75928bb3e405e65a2e4b
push idunknown
push userunknown
push dateunknown
reviewersdmandelin
bugs645889
milestone2.2a1pre
Transform RegExp.exec to RegExp.test in native call IC, bug 645889. r=dmandelin
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jstracer.cpp
js/src/methodjit/MonoIC.cpp
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -5548,8 +5548,44 @@ ReconstructPCStack(JSContext *cx, JSScri
 
     }
     LOCAL_ASSERT(pc == target);
     return pcdepth;
 }
 
 #undef LOCAL_ASSERT
 #undef LOCAL_ASSERT_RV
+
+namespace js {
+
+bool
+CallResultEscapes(jsbytecode *pc)
+{
+    /*
+     * If we see any of these sequences, the result is unused:
+     * - call / pop
+     * - call / trace / pop
+     *
+     * If we see any of these sequences, the result is only tested for nullness:
+     * - call / ifeq
+     * - call / trace / ifeq
+     * - call / not / ifeq
+     * - call / trace / not / ifeq
+     */
+
+    if (*pc != JSOP_CALL)
+        return true;
+
+    pc += JSOP_CALL_LENGTH;
+
+    if (*pc == JSOP_TRACE)
+        pc += JSOP_TRACE_LENGTH;
+
+    if (*pc == JSOP_POP)
+        return false;
+
+    if (*pc == JSOP_NOT)
+        pc += JSOP_NOT_LENGTH;
+
+    return (*pc != JSOP_IFEQ);
+}
+
+} // namespace js
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -511,16 +511,19 @@ extern ptrdiff_t
 SprintCString(Sprinter *sp, const char *s);
 
 extern ptrdiff_t
 SprintString(Sprinter *sp, JSString *str);
 
 extern ptrdiff_t
 Sprint(Sprinter *sp, const char *format, ...);
 
+extern bool
+CallResultEscapes(jsbytecode *pc);
+
 }
 #endif
 
 #ifdef DEBUG
 #ifdef __cplusplus
 /*
  * Disassemblers, for debugging only.
  */
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -11435,57 +11435,34 @@ TraceRecorder::callNative(uintN argc, JS
                     CHECK_STATUS(getCharCodeAt(str, str_ins, idx_ins, &charCode_ins));
                     set(&vp[0], charCode_ins);
                     pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
                     return RECORD_CONTINUE;
                 }
             }
         } else if (vp[2].isString() && mode == JSOP_CALL) {
             if (native == js_regexp_exec) {
-                jsbytecode *pc = cx->regs->pc;
                 /*
-                 * If we see any of these sequences, the result is unused:
-                 * - call / pop
-                 * - call / trace / pop
-                 *
-                 * If we see any of these sequences, the result is only tested for nullness:
-                 * - call / ifeq
-                 * - call / trace / ifeq
-                 * - call / not / ifeq
-                 * - call / trace / not / ifeq
-                 *
-                 * In either case, we replace the call to RegExp.exec() on the
+                 * If the result of the call will be unused or only tested against
+                 * nullness, we replace the call to RegExp.exec() on the
                  * stack with a call to RegExp.test() because "r.exec(s) !=
                  * null" is equivalent to "r.test(s)".  This avoids building
                  * the result array, which can be expensive.  This requires
                  * that RegExp.prototype.test() hasn't been changed;  we check this.
                  */
-                if (pc[0] == JSOP_CALL) {
-                    if ((pc[JSOP_CALL_LENGTH] == JSOP_POP) ||
-                        (pc[JSOP_CALL_LENGTH] == JSOP_TRACE &&
-                         pc[JSOP_CALL_LENGTH + JSOP_TRACE_LENGTH] == JSOP_POP) ||
-                        (pc[JSOP_CALL_LENGTH] == JSOP_IFEQ) ||
-                        (pc[JSOP_CALL_LENGTH] == JSOP_TRACE &&
-                         pc[JSOP_CALL_LENGTH + JSOP_TRACE_LENGTH] == JSOP_IFEQ) ||
-                        (pc[JSOP_CALL_LENGTH] == JSOP_NOT &&
-                         pc[JSOP_CALL_LENGTH + JSOP_NOT_LENGTH] == JSOP_IFEQ) ||
-                        (pc[JSOP_CALL_LENGTH] == JSOP_TRACE &&
-                         pc[JSOP_CALL_LENGTH + JSOP_TRACE_LENGTH] == JSOP_NOT &&
-                         pc[JSOP_CALL_LENGTH + JSOP_TRACE_LENGTH + JSOP_NOT_LENGTH] == JSOP_IFEQ))
-                    {
-                        JSObject* proto;
-                        jsid id = ATOM_TO_JSID(cx->runtime->atomState.testAtom);
-                        /* Get RegExp.prototype.test() and check it hasn't been changed. */
-                        if (js_GetClassPrototype(cx, NULL, JSProto_RegExp, &proto)) {
-                            if (JSObject *tmp = HasNativeMethod(proto, id, js_regexp_test)) {
-                                vp[0] = ObjectValue(*tmp);
-                                funobj = tmp;
-                                fun = tmp->getFunctionPrivate();
-                                native = js_regexp_test;
-                            }
+                if (!CallResultEscapes(cx->regs->pc)) {
+                    JSObject* proto;
+                    jsid id = ATOM_TO_JSID(cx->runtime->atomState.testAtom);
+                    /* Get RegExp.prototype.test() and check it hasn't been changed. */
+                    if (js_GetClassPrototype(cx, NULL, JSProto_RegExp, &proto)) {
+                        if (JSObject *tmp = HasNativeMethod(proto, id, js_regexp_test)) {
+                            vp[0] = ObjectValue(*tmp);
+                            funobj = tmp;
+                            fun = tmp->getFunctionPrivate();
+                            native = js_regexp_test;
                         }
                     }
                 }
             }
         }
         break;
 
       case 2:
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -895,17 +895,27 @@ class CallCompiler : public BaseCompiler
 
         masm.setupABICall(Registers::NormalCall, 3);
         masm.storeArg(2, vpReg);
         if (ic.frameSize.isStatic())
             masm.storeArg(1, Imm32(ic.frameSize.staticArgc()));
         else
             masm.storeArg(1, argcReg.reg());
         masm.storeArg(0, cxReg);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, fun->u.n.native), false);
+
+        js::Native native = fun->u.n.native;
+
+        /*
+         * Call RegExp.test instead of exec if the result will not be used or
+         * will only be used to test for existence.
+         */
+        if (native == js_regexp_exec && !CallResultEscapes(f.regs.pc))
+            native = js_regexp_test;
+
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, native), false);
 
         Jump hasException = masm.branchTest32(Assembler::Zero, Registers::ReturnReg,
                                               Registers::ReturnReg);
         
 
         Jump done = masm.jump();
 
         /* Move JaegerThrowpoline into register for very far jump on x64. */