Bug 895465 - IonMonkey: Optimize codegen for test(and(x, y)). r=mrosenberg
authorDan Gohman <sunfish@google.com>
Wed, 24 Jul 2013 09:36:14 -0700
changeset 139857 89a4ff6bdc937068222da3f0cc1a0cb3a8a8c797
parent 139856 8c6b16333028cb0e62fecea31160b9a565ab4a13
child 139858 711c00277568c51fd8cc98265dd89578cc41c93e
push idunknown
push userunknown
push dateunknown
reviewersmrosenberg
bugs895465
milestone25.0a1
Bug 895465 - IonMonkey: Optimize codegen for test(and(x, y)). r=mrosenberg
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/arm/CodeGenerator-arm.cpp
js/src/ion/arm/CodeGenerator-arm.h
js/src/ion/arm/Lowering-arm.cpp
js/src/ion/arm/Lowering-arm.h
js/src/ion/shared/CodeGenerator-x86-shared.cpp
js/src/ion/shared/CodeGenerator-x86-shared.h
js/src/ion/shared/Lowering-x86-shared.cpp
js/src/ion/shared/Lowering-x86-shared.h
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -1661,16 +1661,43 @@ class LCompareVM : public LCallInstructi
     static const size_t LhsInput = 0;
     static const size_t RhsInput = BOX_PIECES;
 
     MCompare *mir() const {
         return mir_->toCompare();
     }
 };
 
+class LBitAndAndBranch : public LControlInstructionHelper<2, 2, 0>
+{
+  public:
+    LIR_HEADER(BitAndAndBranch)
+    LBitAndAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse)
+    {
+        setSuccessor(0, ifTrue);
+        setSuccessor(1, ifFalse);
+    }
+
+    MBasicBlock *ifTrue() const {
+        return getSuccessor(0);
+    }
+    MBasicBlock *ifFalse() const {
+        return getSuccessor(1);
+    }
+    const LAllocation *left() {
+        return getOperand(0);
+    }
+    const LAllocation *right() {
+        return getOperand(1);
+    }
+    MCompare *mir() {
+        return mir_->toCompare();
+    }
+};
+
 class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2>
 {
   public:
     LIR_HEADER(IsNullOrLikeUndefined)
 
     LIsNullOrLikeUndefined(const LDefinition &temp, const LDefinition &tempToUnbox)
     {
         setTemp(0, temp);
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -76,16 +76,17 @@
     _(CompareDAndBranch)            \
     _(CompareS)                     \
     _(CompareStrictS)               \
     _(CompareB)                     \
     _(CompareBAndBranch)            \
     _(CompareV)                     \
     _(CompareVAndBranch)            \
     _(CompareVM)                    \
+    _(BitAndAndBranch)              \
     _(IsNullOrLikeUndefined)        \
     _(IsNullOrLikeUndefinedAndBranch)\
     _(EmulatesUndefined)            \
     _(EmulatesUndefinedAndBranch)   \
     _(MinMaxI)                      \
     _(MinMaxD)                      \
     _(NegI)                         \
     _(NegD)                         \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -513,16 +513,37 @@ ReorderComparison(JSOp op, MDefinition *
     if (lhs->isConstant()) {
         *rhsp = lhs;
         *lhsp = rhs;
         return js::analyze::ReverseCompareOp(op);
     }
     return op;
 }
 
+static void
+ReorderCommutative(MDefinition **lhsp, MDefinition **rhsp)
+{
+    MDefinition *lhs = *lhsp;
+    MDefinition *rhs = *rhsp;
+
+    // Ensure that if there is a constant, then it is in rhs.
+    // In addition, since clobbering binary operations clobber the left
+    // operand, prefer a non-constant lhs operand with no further uses.
+
+    if (rhs->isConstant())
+        return;
+
+    if (lhs->isConstant() ||
+        (rhs->defUseCount() == 1 && lhs->defUseCount() > 1))
+    {
+        *rhsp = lhs;
+        *lhsp = rhs;
+    }
+}
+
 bool
 LIRGenerator::visitTest(MTest *test)
 {
     MDefinition *opd = test->getOperand(0);
     MBasicBlock *ifTrue = test->ifTrue();
     MBasicBlock *ifFalse = test->ifFalse();
 
     // String is converted to length of string in the type analysis phase (see
@@ -667,16 +688,27 @@ LIRGenerator::visitTest(MTest *test)
             if (!useBoxAtStart(lir, LCompareVAndBranch::LhsInput, left))
                 return false;
             if (!useBoxAtStart(lir, LCompareVAndBranch::RhsInput, right))
                 return false;
             return add(lir, comp);
         }
     }
 
+    // Check if the operand for this test is a bitand operation. If it is, we want
+    // to emit an LBitAndAndBranch rather than an LTest*AndBranch.
+    if (opd->isBitAnd() && opd->isEmittedAtUses()) {
+        MDefinition *lhs = opd->getOperand(0);
+        MDefinition *rhs = opd->getOperand(1);
+        if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
+            ReorderCommutative(&lhs, &rhs);
+            return lowerForBitAndAndBranch(new LBitAndAndBranch(ifTrue, ifFalse), test, lhs, rhs);
+        }
+    }
+
     if (opd->type() == MIRType_Double)
         return add(new LTestDAndBranch(useRegister(opd), ifTrue, ifFalse));
 
     JS_ASSERT(opd->type() == MIRType_Int32 || opd->type() == MIRType_Boolean);
     return add(new LTestIAndBranch(useRegister(opd), ifTrue, ifFalse));
 }
 
 bool
@@ -841,37 +873,16 @@ LIRGenerator::visitCompare(MCompare *com
         if (!useBoxAtStart(lir, LCompareV::RhsInput, right))
             return false;
         return define(lir, comp);
     }
 
     MOZ_ASSUME_UNREACHABLE("Unrecognized compare type.");
 }
 
-static void
-ReorderCommutative(MDefinition **lhsp, MDefinition **rhsp)
-{
-    MDefinition *lhs = *lhsp;
-    MDefinition *rhs = *rhsp;
-
-    // Ensure that if there is a constant, then it is in rhs.
-    // In addition, since clobbering binary operations clobber the left
-    // operand, prefer a non-constant lhs operand with no further uses.
-
-    if (rhs->isConstant())
-        return;
-
-    if (lhs->isConstant() ||
-        (rhs->defUseCount() == 1 && lhs->defUseCount() > 1))
-    {
-        *rhsp = lhs;
-        *lhsp = rhs;
-    }
-}
-
 bool
 LIRGenerator::lowerBitOp(JSOp op, MInstruction *ins)
 {
     MDefinition *lhs = ins->getOperand(0);
     MDefinition *rhs = ins->getOperand(1);
 
     if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) {
         ReorderCommutative(&lhs, &rhs);
@@ -921,19 +932,46 @@ LIRGenerator::visitBitNot(MBitNot *ins)
     LBitNotV *lir = new LBitNotV;
     if (!useBoxAtStart(lir, LBitNotV::Input, input))
         return false;
     if (!defineReturn(lir, ins))
         return false;
     return assignSafepoint(lir, ins);
 }
 
+static bool
+CanEmitBitAndAtUses(MInstruction *ins)
+{
+    if (!ins->canEmitAtUses())
+        return false;
+
+    if (ins->getOperand(0)->type() != MIRType_Int32 || ins->getOperand(1)->type() != MIRType_Int32)
+        return false;
+
+    MUseDefIterator iter(ins);
+    if (!iter)
+        return false;
+
+    if (!iter.def()->isTest())
+        return false;
+
+    iter++;
+    return !iter;
+}
+
 bool
 LIRGenerator::visitBitAnd(MBitAnd *ins)
 {
+    // Sniff out if the output of this bitand is used only for a branching.
+    // If it is, then we will emit an LBitAndAndBranch instruction in place
+    // of this bitand and any test that uses this bitand. Thus, we can
+    // ignore this BitAnd.
+    if (CanEmitBitAndAtUses(ins))
+        return emitAtUses(ins);
+
     return lowerBitOp(JSOP_BITAND, ins);
 }
 
 bool
 LIRGenerator::visitBitOr(MBitOr *ins)
 {
     return lowerBitOp(JSOP_BITOR, ins);
 }
--- a/js/src/ion/arm/CodeGenerator-arm.cpp
+++ b/js/src/ion/arm/CodeGenerator-arm.cpp
@@ -1445,16 +1445,27 @@ CodeGeneratorARM::visitCompareVAndBranch
     masm.j(Assembler::NotEqual, notEqual);
     masm.cmp32(lhs.payloadReg(), rhs.payloadReg());
     emitBranch(cond, lir->ifTrue(), lir->ifFalse());
 
     return true;
 }
 
 bool
+CodeGeneratorARM::visitBitAndAndBranch(LBitAndAndBranch *baab)
+{
+    if (baab->right()->isConstant())
+        masm.ma_tst(ToRegister(baab->left()), Imm32(ToInt32(baab->right())));
+    else
+        masm.ma_tst(ToRegister(baab->left()), ToRegister(baab->right()));
+    emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse());
+    return true;
+}
+
+bool
 CodeGeneratorARM::visitUInt32ToDouble(LUInt32ToDouble *lir)
 {
     masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
     return true;
 }
 
 bool
 CodeGeneratorARM::visitNotI(LNotI *ins)
--- a/js/src/ion/arm/CodeGenerator-arm.h
+++ b/js/src/ion/arm/CodeGenerator-arm.h
@@ -90,16 +90,17 @@ class CodeGeneratorARM : public CodeGene
     virtual bool visitCompareAndBranch(LCompareAndBranch *comp);
     virtual bool visitTestDAndBranch(LTestDAndBranch *test);
     virtual bool visitCompareD(LCompareD *comp);
     virtual bool visitCompareDAndBranch(LCompareDAndBranch *comp);
     virtual bool visitCompareB(LCompareB *lir);
     virtual bool visitCompareBAndBranch(LCompareBAndBranch *lir);
     virtual bool visitCompareV(LCompareV *lir);
     virtual bool visitCompareVAndBranch(LCompareVAndBranch *lir);
+    virtual bool visitBitAndAndBranch(LBitAndAndBranch *baab);
     virtual bool visitUInt32ToDouble(LUInt32ToDouble *lir);
     virtual bool visitNotI(LNotI *ins);
     virtual bool visitNotD(LNotD *ins);
 
     virtual bool visitMathD(LMathD *math);
     virtual bool visitFloor(LFloor *lir);
     virtual bool visitRound(LRound *lir);
     virtual bool visitTruncateDToInt32(LTruncateDToInt32 *ins);
--- a/js/src/ion/arm/Lowering-arm.cpp
+++ b/js/src/ion/arm/Lowering-arm.cpp
@@ -181,16 +181,25 @@ LIRGeneratorARM::lowerForFPU(LInstructio
 {
     ins->setOperand(0, useRegister(lhs));
     ins->setOperand(1, useRegister(rhs));
     return define(ins, mir,
                   LDefinition(LDefinition::TypeFrom(mir->type()), LDefinition::DEFAULT));
 }
 
 bool
+LIRGeneratorARM::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
+                                         MDefinition *lhs, MDefinition *rhs)
+{
+    baab->setOperand(0, useRegister(lhs));
+    baab->setOperand(1, useRegisterOrConstant(rhs));
+    return add(baab, mir);
+}
+
+bool
 LIRGeneratorARM::defineUntypedPhi(MPhi *phi, size_t lirIndex)
 {
     LPhi *type = current->getPhi(lirIndex + VREG_TYPE_OFFSET);
     LPhi *payload = current->getPhi(lirIndex + VREG_DATA_OFFSET);
 
     uint32_t typeVreg = getVirtualRegister();
     if (typeVreg >= MAX_VIRTUAL_REGISTERS)
         return false;
--- a/js/src/ion/arm/Lowering-arm.h
+++ b/js/src/ion/arm/Lowering-arm.h
@@ -40,16 +40,18 @@ class LIRGeneratorARM : public LIRGenera
                      MDefinition *input);
     bool lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
                      MDefinition *lhs, MDefinition *rhs);
 
     bool lowerForFPU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
                      MDefinition *src);
     bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir,
                      MDefinition *lhs, MDefinition *rhs);
+    bool lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
+                                 MDefinition *lhs, MDefinition *rhs);
 
     bool lowerTruncateDToInt32(MTruncateToInt32 *ins);
 
     bool lowerConstantDouble(double d, MInstruction *ins);
     bool lowerDivI(MDiv *div);
     bool lowerModI(MMod *mod);
     bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs);
     bool lowerUDiv(MInstruction *div);
--- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp
@@ -123,16 +123,27 @@ CodeGeneratorX86Shared::visitTestDAndBra
     // NaN is falsey, so comparing against 0 and then using the Z flag is
     // enough to determine which branch to take.
     masm.xorpd(ScratchFloatReg, ScratchFloatReg);
     masm.ucomisd(ToFloatRegister(opd), ScratchFloatReg);
     emitBranch(Assembler::NotEqual, test->ifTrue(), test->ifFalse());
     return true;
 }
 
+bool
+CodeGeneratorX86Shared::visitBitAndAndBranch(LBitAndAndBranch *baab)
+{
+    if (baab->right()->isConstant())
+        masm.testl(ToRegister(baab->left()), Imm32(ToInt32(baab->right())));
+    else
+        masm.testl(ToRegister(baab->left()), ToRegister(baab->right()));
+    emitBranch(Assembler::NonZero, baab->ifTrue(), baab->ifFalse());
+    return true;
+}
+
 void
 CodeGeneratorX86Shared::emitCompare(MCompare::CompareType type, const LAllocation *left, const LAllocation *right)
 {
 #ifdef JS_CPU_X64
     if (type == MCompare::Compare_Object) {
         masm.cmpq(ToRegister(left), ToOperand(right));
         return;
     }
--- a/js/src/ion/shared/CodeGenerator-x86-shared.h
+++ b/js/src/ion/shared/CodeGenerator-x86-shared.h
@@ -93,16 +93,17 @@ class CodeGeneratorX86Shared : public Co
     virtual bool visitShiftI(LShiftI *ins);
     virtual bool visitUrshD(LUrshD *ins);
     virtual bool visitTestIAndBranch(LTestIAndBranch *test);
     virtual bool visitTestDAndBranch(LTestDAndBranch *test);
     virtual bool visitCompare(LCompare *comp);
     virtual bool visitCompareAndBranch(LCompareAndBranch *comp);
     virtual bool visitCompareD(LCompareD *comp);
     virtual bool visitCompareDAndBranch(LCompareDAndBranch *comp);
+    virtual bool visitBitAndAndBranch(LBitAndAndBranch *baab);
     virtual bool visitNotI(LNotI *comp);
     virtual bool visitNotD(LNotD *comp);
     virtual bool visitMathD(LMathD *math);
     virtual bool visitFloor(LFloor *lir);
     virtual bool visitRound(LRound *lir);
     virtual bool visitGuardShape(LGuardShape *guard);
     virtual bool visitGuardObjectType(LGuardObjectType *guard);
     virtual bool visitGuardClass(LGuardClass *guard);
--- a/js/src/ion/shared/Lowering-x86-shared.cpp
+++ b/js/src/ion/shared/Lowering-x86-shared.cpp
@@ -112,16 +112,25 @@ bool
 LIRGeneratorX86Shared::lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs, MDefinition *rhs)
 {
     ins->setOperand(0, useRegisterAtStart(lhs));
     ins->setOperand(1, use(rhs));
     return defineReuseInput(ins, mir, 0);
 }
 
 bool
+LIRGeneratorX86Shared::lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
+                                               MDefinition *lhs, MDefinition *rhs)
+{
+    baab->setOperand(0, useRegister(lhs));
+    baab->setOperand(1, useRegisterOrConstant(rhs));
+    return add(baab, mir);
+}
+
+bool
 LIRGeneratorX86Shared::lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs)
 {
     // Note: lhs is used twice, so that we can restore the original value for the
     // negative zero check.
     LMulI *lir = new LMulI(useRegisterAtStart(lhs), useOrConstant(rhs), use(lhs));
     if (mul->fallible() && !assignSnapshot(lir))
         return false;
     return defineReuseInput(lir, mul, 0);
--- a/js/src/ion/shared/Lowering-x86-shared.h
+++ b/js/src/ion/shared/Lowering-x86-shared.h
@@ -29,16 +29,18 @@ class LIRGeneratorX86Shared : public LIR
     bool visitPowHalf(MPowHalf *ins);
     bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
                        MDefinition *rhs);
     bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir, MDefinition *input);
     bool lowerForALU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
                      MDefinition *rhs);
     bool lowerForFPU(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
                      MDefinition *rhs);
+    bool lowerForBitAndAndBranch(LBitAndAndBranch *baab, MInstruction *mir,
+                                 MDefinition *lhs, MDefinition *rhs);
     bool visitConstant(MConstant *ins);
     bool visitAsmJSNeg(MAsmJSNeg *ins);
     bool visitAsmJSUDiv(MAsmJSUDiv *ins);
     bool visitAsmJSUMod(MAsmJSUMod *ins);
     bool lowerMulI(MMul *mul, MDefinition *lhs, MDefinition *rhs);
     bool lowerDivI(MDiv *div);
     bool lowerModI(MMod *mod);
     bool lowerUDiv(MInstruction *div);