Bug 764743 - Port RegExp.exec -> RegExp.test optimization to IonMonkey. r=sstangl
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 14 Sep 2012 18:39:53 +0200
changeset 107210 790c0298e246c916677a9b00a26b6064ac7f832c
parent 107209 2307541f08fb750abca9db1662a0d7d9776dbb3d
child 107211 4de50fce24ea2d7423012555035c28d9d45269bb
push id74
push usershu@rfrn.org
push dateTue, 18 Sep 2012 19:23:47 +0000
reviewerssstangl
bugs764743
milestone18.0a1
Bug 764743 - Port RegExp.exec -> RegExp.test optimization to IonMonkey. r=sstangl
js/src/ion/IonBuilder.cpp
js/src/jit-test/tests/ion/regexp-exec.js
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -3656,16 +3656,51 @@ IonBuilder::jsop_funapply(uint32 argc)
     if (!resumeAfter(apply))
         return false;
 
     types::StackTypeSet *barrier;
     types::StackTypeSet *types = oracle->returnTypeSet(script, pc, &barrier);
     return pushTypeBarrier(apply, types, barrier);
 }
 
+// Get the builtin RegExp.prototype.test function.
+static bool
+GetBuiltinRegExpTest(JSContext *cx, JSScript *script, JSFunction **result)
+{
+    JS_ASSERT(*result == NULL);
+
+    // Get the builtin RegExp.prototype object.
+    RootedObject proto(cx, script->global().getOrCreateRegExpPrototype(cx));
+    if (!proto)
+        return false;
+
+    // Get the |test| property. Note that we use lookupProperty, not getProperty,
+    // to avoid calling a getter.
+    RootedShape shape(cx);
+    RootedObject holder(cx);
+    if (!JSObject::lookupProperty(cx, proto, cx->runtime->atomState.testAtom, &holder, &shape))
+        return false;
+
+    if (proto != holder || !shape || !shape->hasDefaultGetter() || !shape->hasSlot())
+        return true;
+
+    // The RegExp.prototype.test property is writable, so we have to ensure
+    // we got the builtin function.
+    Value val = holder->getSlot(shape->slot());
+    if (!val.isObject())
+        return true;
+
+    JSObject *obj = &val.toObject();
+    if (!obj->isFunction() || obj->toFunction()->maybeNative() != regexp_test)
+        return true;
+
+    *result = obj->toFunction();
+    return true;
+}
+
 bool
 IonBuilder::jsop_call(uint32 argc, bool constructing)
 {
     // Acquire known call target if existent.
     AutoObjectVector targets(cx);
     uint32_t numTargets = getPolyCallTargets(argc, pc, targets, 4);
     types::StackTypeSet *barrier;
     types::StackTypeSet *types = oracle->returnTypeSet(script, pc, &barrier);
@@ -3684,17 +3719,31 @@ IonBuilder::jsop_call(uint32 argc, bool 
                 break;
             }
         }
 
         if (numTargets > 0 && makeInliningDecision(targets))
             return inlineScriptedCall(targets, argc, constructing, types, barrier);
     }
 
-    RootedFunction target(cx, numTargets == 1 ? targets[0]->toFunction() : NULL);
+    RootedFunction target(cx, NULL);
+    if (numTargets == 1) {
+        target = targets[0]->toFunction();
+
+        // Call RegExp.test instead of RegExp.exec if the result will not be used
+        // or will only be used to test for existence.
+        if (target->maybeNative() == regexp_exec && !CallResultEscapes(pc)) {
+            JSFunction *newTarget = NULL;
+            if (!GetBuiltinRegExpTest(cx, script, &newTarget))
+                return false;
+            if (newTarget)
+                target = newTarget;
+        }
+    }
+
     return makeCallBarrier(target, argc, constructing, types, barrier);
 }
 
 MCall *
 IonBuilder::makeCallHelper(HandleFunction target, uint32 argc, bool constructing)
 {
     // This function may be called with mutated stack.
     // Querying TI for popped types is invalid.
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/regexp-exec.js
@@ -0,0 +1,20 @@
+// RegExp.exec -> RegExp.test optimization should use the builtin test method.
+function f() {
+    var res = 0;
+    for (var i=0; i<100; i++) {
+        if (/a/.exec("a"))
+            res++;
+    }
+    assertEq(res, 100);
+}
+delete RegExp.prototype.test;
+gc();
+f();
+
+RegExp.prototype.test = function() { assertEq(0, 1); }
+gc();
+f();
+
+Object.defineProperty(RegExp.prototype, "test", {get: function() { assertEq(0, 1); }});
+gc();
+f();