Bug 784765 - Add fastpath for string equality with different length. r=jandem
authorTom Schuster <evilpies@gmail.com>
Sat, 06 Oct 2012 21:59:14 +0200
changeset 109533 5a3283cb92b8a84e9dbf6813191add0fcb87512d
parent 109532 7fc54528af644c058526ec8665eaad70d53e9ab7
child 109534 416a9b587ba84ed872022a7c87335e1c4b9cb9d9
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjandem
bugs784765
milestone18.0a1
Bug 784765 - Add fastpath for string equality with different length. r=jandem
js/src/ion/CodeGenerator.cpp
js/src/ion/LIR-Common.h
js/src/ion/Lowering.cpp
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1915,16 +1915,17 @@ CodeGenerator::visitBinaryV(LBinaryV *li
 
 bool
 CodeGenerator::visitCompareS(LCompareS *lir)
 {
     JSOp op = lir->mir()->jsop();
     Register left = ToRegister(lir->left());
     Register right = ToRegister(lir->right());
     Register output = ToRegister(lir->output());
+    Register temp = ToRegister(lir->temp());
 
     typedef bool (*pf)(JSContext *, HandleString, HandleString, JSBool *);
     static const VMFunction stringsEqualInfo = FunctionInfo<pf>(ion::StringsEqual<true>);
     static const VMFunction stringsNotEqualInfo = FunctionInfo<pf>(ion::StringsEqual<false>);
 
     OutOfLineCode *ool = NULL;
     if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
         ool = oolCallVM(stringsEqualInfo, lir, (ArgList(), left, right),  StoreRegisterTo(output));
@@ -1938,30 +1939,36 @@ CodeGenerator::visitCompareS(LCompareS *
 
     Label notPointerEqual;
     // Fast path for identical strings
     masm.branchPtr(Assembler::NotEqual, left, right, &notPointerEqual);
     masm.move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), output);
     masm.jump(ool->rejoin());
 
     masm.bind(&notPointerEqual);
-
-    // JSString::isAtom === !(lengthAndFlags & ATOM_MASK)
-    Imm32 atomMask(JSString::ATOM_BIT);
-
-    // This optimization is only correct for atomized strings,
-    // so we need to jump to the ool path.
-    masm.branchTest32(Assembler::Zero, Address(left, JSString::offsetOfLengthAndFlags()), 
-                      atomMask, ool->entry());
-
-    masm.branchTest32(Assembler::Zero, Address(right, JSString::offsetOfLengthAndFlags()), 
-                      atomMask, ool->entry());
+    masm.loadPtr(Address(left, JSString::offsetOfLengthAndFlags()), output);
+    masm.loadPtr(Address(right, JSString::offsetOfLengthAndFlags()), temp);
+
+    Label notAtom;
+    // We can optimize the equality operation to a pointer compare for
+    // two atoms.
+    Imm32 atomBit(JSString::ATOM_BIT);
+    masm.branchTest32(Assembler::Zero, output, atomBit, &notAtom);
+    masm.branchTest32(Assembler::Zero, temp, atomBit, &notAtom);
 
     masm.cmpPtr(left, right);
     emitSet(JSOpToCondition(op), output);
+    masm.jump(ool->rejoin());
+
+    masm.bind(&notAtom);
+    // Strings of different length can never be equal.
+    masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), output);
+    masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp);
+    masm.branchPtr(Assembler::Equal, output, temp, ool->entry());
+    masm.move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), output);
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
 CodeGenerator::visitCompareV(LCompareV *lir)
 {
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -925,31 +925,36 @@ class LCompareD : public LInstructionHel
     const LAllocation *right() {
         return getOperand(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
-class LCompareS : public LInstructionHelper<1, 2, 0>
+class LCompareS : public LInstructionHelper<1, 2, 1>
 {
   public:
     LIR_HEADER(CompareS);
-    LCompareS(const LAllocation &left, const LAllocation &right) {
+    LCompareS(const LAllocation &left, const LAllocation &right,
+              const LDefinition &temp) {
         setOperand(0, left);
         setOperand(1, right);
+        setTemp(0, temp);
     }
 
     const LAllocation *left() {
         return getOperand(0);
     }
     const LAllocation *right() {
         return getOperand(1);
     }
+    const LDefinition *temp() {
+        return getTemp(0);
+    }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
 class LCompareV : public LCallInstructionHelper<1, 2 * BOX_PIECES, 0>
 {
   public:
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -449,17 +449,17 @@ LIRGenerator::visitCompare(MCompare *com
         bool result;
         if (comp->tryFold(&result))
             return define(new LInteger(result), comp);
 
         // Move below the emitAtUses call if we ever implement
         // LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't
         // make sense and avoids confusion.
         if (comp->specialization() == MIRType_String) {
-            LCompareS *lir = new LCompareS(useRegister(left), useRegister(right));
+            LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
             if (!define(lir, comp))
                 return false;
             return assignSafepoint(lir, comp);
         }
 
         // Sniff out if the output of this compare is used only for a branching.
         // If it is, then we willl emit an LCompare*AndBranch instruction in place
         // of this compare and any test that uses this compare. Thus, we can