Bug 690974 - Add inline paths for strict equality on strings/objects (r=bhackett)
authorBill McCloskey <wmccloskey@mozilla.com>
Thu, 06 Oct 2011 10:39:19 -0700
changeset 78898 70433779ffa7ccfcc82571d9e0051edc7a40023b
parent 78897 312306f72fa31fd373863c560e53de44271b3a5f
child 78899 ef08991a3411a1974f26329d58442bda353d3137
push id506
push userclegnitto@mozilla.com
push dateWed, 09 Nov 2011 02:03:18 +0000
treeherdermozilla-aurora@63587fc7bb93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs690974
milestone10.0a1
Bug 690974 - Add inline paths for strict equality on strings/objects (r=bhackett)
js/src/methodjit/Compiler.h
js/src/methodjit/FastOps.cpp
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -718,34 +718,37 @@ class Compiler : public BaseCompiler
     void jsop_getelem_args();
 #ifdef JS_METHODJIT_TYPED_ARRAY
     bool jsop_getelem_typed(int atype);
 #endif
     void jsop_toid();
     bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id);
     void jsop_stricteq(JSOp op);
     bool jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
+    CompileStatus jsop_equality_obj_obj(JSOp op, jsbytecode *target, JSOp fused);
     bool jsop_equality_int_string(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused);
     void jsop_pos();
 
     static inline Assembler::Condition
     GetCompareCondition(JSOp op, JSOp fused)
     {
         bool ifeq = fused == JSOP_IFEQ;
         switch (op) {
           case JSOP_GT:
             return ifeq ? Assembler::LessThanOrEqual : Assembler::GreaterThan;
           case JSOP_GE:
             return ifeq ? Assembler::LessThan : Assembler::GreaterThanOrEqual;
           case JSOP_LT:
             return ifeq ? Assembler::GreaterThanOrEqual : Assembler::LessThan;
           case JSOP_LE:
             return ifeq ? Assembler::GreaterThan : Assembler::LessThanOrEqual;
+          case JSOP_STRICTEQ:
           case JSOP_EQ:
             return ifeq ? Assembler::NotEqual : Assembler::Equal;
+          case JSOP_STRICTNE:
           case JSOP_NE:
             return ifeq ? Assembler::Equal : Assembler::NotEqual;
           default:
             JS_NOT_REACHED("unrecognized op");
             return Assembler::Equal;
         }
     }
 
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -398,16 +398,68 @@ static inline bool
 CheckNullOrUndefined(FrameEntry *fe)
 {
     if (!fe->isTypeKnown())
         return false;
     JSValueType type = fe->getKnownType();
     return type == JSVAL_TYPE_NULL || type == JSVAL_TYPE_UNDEFINED;
 }
 
+CompileStatus
+mjit::Compiler::jsop_equality_obj_obj(JSOp op, jsbytecode *target, JSOp fused)
+{
+    FrameEntry *rhs = frame.peek(-1);
+    FrameEntry *lhs = frame.peek(-2);
+
+    JS_ASSERT(cx->typeInferenceEnabled() &&
+              lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT));
+
+    /*
+     * Handle equality between two objects. We have to ensure there is no
+     * special equality operator on either object, if that passes then
+     * this is a pointer comparison.
+     */
+    types::TypeSet *lhsTypes = analysis->poppedTypes(PC, 1);
+    types::TypeSet *rhsTypes = analysis->poppedTypes(PC, 0);
+    if (!lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) &&
+        !rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY)) {
+        /* :TODO: Merge with jsop_relational_int? */
+        JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
+        frame.forgetMismatchedObject(lhs);
+        frame.forgetMismatchedObject(rhs);
+        Assembler::Condition cond = GetCompareCondition(op, fused);
+        if (target) {
+            Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
+                                               Registers::ReturnReg, Registers::ReturnReg);
+            if (!frame.syncForBranch(target, Uses(2)))
+                return Compile_Error;
+            RegisterID lreg = frame.tempRegForData(lhs);
+            frame.pinReg(lreg);
+            RegisterID rreg = frame.tempRegForData(rhs);
+            frame.unpinReg(lreg);
+            Jump fast = masm.branchPtr(cond, lreg, rreg);
+            frame.popn(2);
+            return jumpAndTrace(fast, target, &sj) ? Compile_Okay : Compile_Error;
+        } else {
+            RegisterID result = frame.allocReg();
+            RegisterID lreg = frame.tempRegForData(lhs);
+            frame.pinReg(lreg);
+            RegisterID rreg = frame.tempRegForData(rhs);
+            frame.unpinReg(lreg);
+            masm.branchValue(cond, lreg, rreg, result);
+
+            frame.popn(2);
+            frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
+            return Compile_Okay;
+        }
+    }
+
+    return Compile_Skipped;
+}
+
 bool
 mjit::Compiler::jsop_equality(JSOp op, BoolStub stub, jsbytecode *target, JSOp fused)
 {
     FrameEntry *rhs = frame.peek(-1);
     FrameEntry *lhs = frame.peek(-2);
 
     /* The compiler should have handled constant folding. */
     JS_ASSERT(!(rhs->isConstant() && lhs->isConstant()));
@@ -482,56 +534,21 @@ mjit::Compiler::jsop_equality(JSOp op, B
             masm.move(Imm32(op == JSOP_EQ), reg);
             j3.linkTo(masm.label(), &masm);
             frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, reg);
         }
         return true;
     }
 
     if (cx->typeInferenceEnabled() &&
-        lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT)) {
-        /*
-         * Handle equality between two objects. We have to ensure there is no
-         * special equality operator on either object, if that passes then
-         * this is a pointer comparison.
-         */
-        types::TypeSet *lhsTypes = analysis->poppedTypes(PC, 1);
-        types::TypeSet *rhsTypes = analysis->poppedTypes(PC, 0);
-        if (!lhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY) &&
-            !rhsTypes->hasObjectFlags(cx, types::OBJECT_FLAG_SPECIAL_EQUALITY)) {
-            /* :TODO: Merge with jsop_relational_int? */
-            JS_ASSERT_IF(!target, fused != JSOP_IFEQ);
-            frame.forgetMismatchedObject(lhs);
-            frame.forgetMismatchedObject(rhs);
-            Assembler::Condition cond = GetCompareCondition(op, fused);
-            if (target) {
-                Jump sj = stubcc.masm.branchTest32(GetStubCompareCondition(fused),
-                                                   Registers::ReturnReg, Registers::ReturnReg);
-                if (!frame.syncForBranch(target, Uses(2)))
-                    return false;
-                RegisterID lreg = frame.tempRegForData(lhs);
-                frame.pinReg(lreg);
-                RegisterID rreg = frame.tempRegForData(rhs);
-                frame.unpinReg(lreg);
-                Jump fast = masm.branchPtr(cond, lreg, rreg);
-                frame.popn(2);
-                return jumpAndTrace(fast, target, &sj);
-            } else {
-                RegisterID result = frame.allocReg();
-                RegisterID lreg = frame.tempRegForData(lhs);
-                frame.pinReg(lreg);
-                RegisterID rreg = frame.tempRegForData(rhs);
-                frame.unpinReg(lreg);
-                masm.branchValue(cond, lreg, rreg, result);
-
-                frame.popn(2);
-                frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
-                return true;
-            }
-        }
+        lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT))
+    {
+        CompileStatus status = jsop_equality_obj_obj(op, target, fused);
+        if (status == Compile_Okay) return true;
+        else if (status == Compile_Error) return false;
     }
 
     return emitStubCmpOp(stub, target, fused);
 }
 
 bool
 mjit::Compiler::jsop_relational(JSOp op, BoolStub stub,
                                 jsbytecode *target, JSOp fused)
@@ -2344,16 +2361,102 @@ mjit::Compiler::jsop_stricteq(JSOp op)
         if (data != result)
             frame.freeReg(data);
 
         frame.popn(2);
         frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
         return;
     }
 
+    if (lhs->isType(JSVAL_TYPE_STRING) || rhs->isType(JSVAL_TYPE_STRING)) {
+        FrameEntry *maybeNotStr = lhs->isType(JSVAL_TYPE_STRING) ? rhs : lhs;
+
+        if (maybeNotStr->isNotType(JSVAL_TYPE_STRING)) {
+            frame.popn(2);
+            frame.push(BooleanValue(false));
+            return;
+        }
+
+        if (!maybeNotStr->isTypeKnown()) {
+            JS_ASSERT(!maybeNotStr->isConstant());
+            Jump j = frame.testString(Assembler::NotEqual, maybeNotStr);
+            stubcc.linkExit(j, Uses(2));
+        }
+
+        FrameEntry *op1 = lhs->isConstant() ? rhs : lhs;
+        FrameEntry *op2 = lhs->isConstant() ? lhs : rhs;
+        JS_ASSERT(!op1->isConstant());
+
+        /* ReturnReg is safely usable with set32, since %ah can be accessed. */
+        RegisterID resultReg = Registers::ReturnReg;
+        frame.takeReg(resultReg);
+        RegisterID tmpReg = frame.allocReg();
+        RegisterID reg1 = frame.tempRegForData(op1);
+        frame.pinReg(reg1);
+
+        RegisterID reg2;
+        if (op2->isConstant()) {
+            reg2 = frame.allocReg();
+            JSString *str = op2->getValue().toString();
+            JS_ASSERT(str->isAtom());
+            masm.move(ImmPtr(str), reg2);
+        } else {
+            reg2 = frame.tempRegForData(op2);
+            frame.pinReg(reg2);
+        }
+
+        JS_ASSERT(reg1 != resultReg);
+        JS_ASSERT(reg1 != tmpReg);
+        JS_ASSERT(reg2 != resultReg);
+        JS_ASSERT(reg2 != tmpReg);
+
+        /* JSString::isAtom === (lengthAndFlags & ATOM_MASK == 0) */
+        JS_STATIC_ASSERT(JSString::ATOM_FLAGS == 0);
+        Imm32 atomMask(JSString::ATOM_MASK);
+
+        masm.load32(Address(reg1, JSString::offsetOfLengthAndFlags()), tmpReg);
+        Jump op1NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask);
+        stubcc.linkExit(op1NotAtomized, Uses(2));
+
+        if (!op2->isConstant()) {
+            masm.load32(Address(reg2, JSString::offsetOfLengthAndFlags()), tmpReg);
+            Jump op2NotAtomized = masm.branchTest32(Assembler::NonZero, tmpReg, atomMask);
+            stubcc.linkExit(op2NotAtomized, Uses(2));
+        }
+
+        masm.set32(cond, reg1, reg2, resultReg);
+
+        frame.unpinReg(reg1);
+        if (op2->isConstant())
+            frame.freeReg(reg2);
+        else
+            frame.unpinReg(reg2);
+        frame.freeReg(tmpReg);
+
+        stubcc.leave();
+        if (op == JSOP_STRICTEQ)
+            OOL_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2));
+        else
+            OOL_STUBCALL_USES(stubs::StrictNe, REJOIN_NONE, Uses(2));
+
+        frame.popn(2);
+        frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, resultReg);
+
+        stubcc.rejoin(Changes(1));
+        return;
+    }
+
+    if (cx->typeInferenceEnabled() &&
+        lhs->isType(JSVAL_TYPE_OBJECT) && rhs->isType(JSVAL_TYPE_OBJECT))
+    {
+        CompileStatus status = jsop_equality_obj_obj(op, NULL, JSOP_NOP);
+        if (status == Compile_Okay) return;
+        JS_ASSERT(status == Compile_Skipped);
+    }
+
     /* Is it impossible that both Values are ints? */
     if ((lhs->isTypeKnown() && lhs->isNotType(JSVAL_TYPE_INT32)) ||
         (rhs->isTypeKnown() && rhs->isNotType(JSVAL_TYPE_INT32))) {
         prepareStubCall(Uses(2));
 
         if (op == JSOP_STRICTEQ)
             INLINE_STUBCALL_USES(stubs::StrictEq, REJOIN_NONE, Uses(2));
         else