Bug 848319: IonMonkey: Enable hoisting of MRegExp, r=sstangl
authorHannes Verschore <hv1989@gmail.com>
Wed, 13 Mar 2013 19:15:36 +0100
changeset 125071 738733ab166d0f48f15691191cdbd5536c3f659e
parent 125070 ba0ef31a1726be61921ffb4fe34b76093ab67743
child 125072 d0ad849d2bd0722205fc6cabdf5a70ffee535274
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerssstangl
bugs848319
milestone22.0a1
Bug 848319: IonMonkey: Enable hoisting of MRegExp, r=sstangl
js/src/ion/IonBuilder.cpp
js/src/ion/MIR.h
js/src/jit-test/tests/ion/bug848319.js
js/src/vm/RegExpObject.h
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -6884,19 +6884,33 @@ IonBuilder::jsop_delprop(HandlePropertyN
 
 bool
 IonBuilder::jsop_regexp(RegExpObject *reobj)
 {
     JSObject *prototype = script()->global().getOrCreateRegExpPrototype(cx);
     if (!prototype)
         return false;
 
-    MRegExp *ins = MRegExp::New(reobj, prototype, MRegExp::MustClone);
-    current->add(ins);
-    current->push(ins);
+    MRegExp *regexp = MRegExp::New(reobj, prototype);
+    current->add(regexp);
+    current->push(regexp);
+
+    regexp->setMovable();
+
+    // The MRegExp is set to be movable.
+    // That would be incorrect for global/sticky, because lastIndex could be wrong.
+    // Therefore setting the lastIndex to 0. That is faster than removing the movable flag.
+    if (reobj->sticky() || reobj->global()) {
+        MConstant *zero = MConstant::New(Int32Value(0));
+        current->add(zero);
+
+        MStoreFixedSlot *lastIndex =
+            MStoreFixedSlot::New(regexp, RegExpObject::lastIndexSlot(), zero);
+        current->add(lastIndex);
+    }
 
     return true;
 }
 
 bool
 IonBuilder::jsop_object(JSObject *obj)
 {
     MConstant *ins = MConstant::New(ObjectValue(*obj));
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -3439,60 +3439,39 @@ class MDefFun : public MUnaryInstruction
     }
     MDefinition *scopeChain() const {
         return getOperand(0);
     }
 };
 
 class MRegExp : public MNullaryInstruction
 {
-  public:
-    // In the future we can optimize MRegExp to reuse the source object
-    // instead of cloning in the case of some
-    // single-use-is-a-known-native-that-can't-observe-the-object
-    // operations (like test).
-    enum CloneBehavior {
-        UseSource,
-        MustClone
-    };
-
-  private:
     CompilerRoot<RegExpObject *> source_;
     CompilerRootObject prototype_;
-    CloneBehavior shouldClone_;
-
-    MRegExp(RegExpObject *source, JSObject *prototype, CloneBehavior shouldClone)
+
+    MRegExp(RegExpObject *source, JSObject *prototype)
       : source_(source),
-        prototype_(prototype),
-        shouldClone_(shouldClone)
+        prototype_(prototype)
     {
         setResultType(MIRType_Object);
-
-        // Can't move if we're cloning, because cloning takes into
-        // account the RegExpStatics flags.
-        if (shouldClone == UseSource)
-            setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(RegExp)
 
-    static MRegExp *New(RegExpObject *source, JSObject *prototype, CloneBehavior shouldClone) {
-        return new MRegExp(source, prototype, shouldClone);
+    static MRegExp *New(RegExpObject *source, JSObject *prototype) {
+        return new MRegExp(source, prototype);
     }
 
     RegExpObject *source() const {
         return source_;
     }
     JSObject *getRegExpPrototype() const {
         return prototype_;
     }
-    CloneBehavior shouldClone() const {
-        return shouldClone_;
-    }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
 class MRegExpTest
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<1>, StringPolicy<0> >
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug848319.js
@@ -0,0 +1,25 @@
+function test() {
+  for(var i=0; i<2; i++) {
+    var a = /a/;
+    assertEq(a.lastIndex, 0);
+    a.exec("aaa");
+    assertEq(a.lastIndex, 0);
+  }
+
+  for(var i=0; i<2; i++) {
+    var a = /a/g;
+    assertEq(a.lastIndex, 0);
+    a.exec("aaa");
+    assertEq(a.lastIndex, 1);
+  }
+
+  for(var i=0; i<2; i++) {
+    var a = /a/y;
+    assertEq(a.lastIndex, 0);
+    a.exec("aaa");
+    assertEq(a.lastIndex, 1);
+  }
+}
+
+test();
+test();
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -308,16 +308,18 @@ class RegExpObject : public JSObject
     createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
                     frontend::TokenStream *ts);
 
     static RegExpObject *
     createNoStatics(JSContext *cx, HandleAtom atom, RegExpFlag flags, frontend::TokenStream *ts);
 
     /* Accessors. */
 
+    static unsigned lastIndexSlot() { return LAST_INDEX_SLOT; }
+
     const Value &getLastIndex() const { return getSlot(LAST_INDEX_SLOT); }
     inline void setLastIndex(double d);
     inline void zeroLastIndex();
 
     JSFlatString *toString(JSContext *cx) const;
 
     JSAtom *getSource() const { return &getSlot(SOURCE_SLOT).toString()->asAtom(); }
     inline void setSource(JSAtom *source);