diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 6c73f56f4850..d96242cef338 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -5553,3 +5553,39 @@ ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target, #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 diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 023bb05d0362..49535b32660b 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -516,6 +516,9 @@ SprintString(Sprinter *sp, JSString *str); extern ptrdiff_t Sprint(Sprinter *sp, const char *format, ...); +extern bool +CallResultEscapes(jsbytecode *pc); + } #endif diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index e687946b6b63..3241d10b34dc 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -11440,47 +11440,24 @@ TraceRecorder::callNative(uintN argc, JSOp mode) } } 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; } } } diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 15e504330336..a26a6e0566aa 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -900,7 +900,17 @@ class CallCompiler : public BaseCompiler 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);