Bug 1232977 - Remove redundant bitand with uint{8,16} and an all-bits-set mask. r=nbp, r=sunfish
authorSander Mathijs van Veen <smvv@kompiler.org>
Mon, 04 Jan 2016 07:21:00 -0500
changeset 313751 4867a5f744003eb6e622be8e6d5663d05e904d5a
parent 313750 7e27b120d5cfa2a6dec6f8d93165cb8c842e9763
child 313752 03c12499f78d1f995b7fc854df6d97aa70337a6c
push id5703
push userraliiev@mozilla.com
push dateMon, 07 Mar 2016 14:18:41 +0000
treeherdermozilla-beta@31e373ad5b5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp, sunfish
bugs1232977
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1232977 - Remove redundant bitand with uint{8,16} and an all-bits-set mask. r=nbp, r=sunfish
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/RangeAnalysis.cpp
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2157,16 +2157,26 @@ MBinaryBitwiseInstruction::foldUnnecessa
         return foldIfNegOne(0);
 
     if (IsConstant(rhs, -1))
         return foldIfNegOne(1);
 
     if (lhs == rhs)
         return foldIfEqual();
 
+    if (maskMatchesRightRange) {
+        MOZ_ASSERT(lhs->isConstantValue() && lhs->type() == MIRType_Int32);
+        return foldIfAllBitsSet(0);
+    }
+
+    if (maskMatchesLeftRange) {
+        MOZ_ASSERT(rhs->isConstantValue() && rhs->type() == MIRType_Int32);
+        return foldIfAllBitsSet(1);
+    }
+
     return this;
 }
 
 void
 MBinaryBitwiseInstruction::infer(BaselineInspector*, jsbytecode*)
 {
     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(0)->mightBeType(MIRType_Symbol) ||
         getOperand(1)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Symbol))
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5428,31 +5428,36 @@ class MToId
 };
 
 class MBinaryBitwiseInstruction
   : public MBinaryInstruction,
     public BitwisePolicy::Data
 {
   protected:
     MBinaryBitwiseInstruction(MDefinition* left, MDefinition* right)
-      : MBinaryInstruction(left, right)
+      : MBinaryInstruction(left, right), maskMatchesLeftRange(false),
+        maskMatchesRightRange(false)
     {
         setResultType(MIRType_Int32);
         setMovable();
     }
 
     void specializeAsInt32();
+    bool maskMatchesLeftRange;
+    bool maskMatchesRightRange;
 
   public:
     MDefinition* foldsTo(TempAllocator& alloc) override;
     MDefinition* foldUnnecessaryBitop();
     virtual MDefinition* foldIfZero(size_t operand) = 0;
     virtual MDefinition* foldIfNegOne(size_t operand) = 0;
     virtual MDefinition* foldIfEqual()  = 0;
+    virtual MDefinition* foldIfAllBitsSet(size_t operand)  = 0;
     virtual void infer(BaselineInspector* inspector, jsbytecode* pc);
+    void collectRangeInfoPreTrunc() override;
 
     bool congruentTo(const MDefinition* ins) const override {
         return binaryCongruentTo(ins);
     }
     AliasSet getAliasSet() const override {
         if (specialization_ >= MIRType_Object)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
@@ -5476,16 +5481,20 @@ class MBitAnd : public MBinaryBitwiseIns
         return getOperand(operand); // 0 & x => 0;
     }
     MDefinition* foldIfNegOne(size_t operand) override {
         return getOperand(1 - operand); // x & -1 => x
     }
     MDefinition* foldIfEqual() override {
         return getOperand(0); // x & x => x;
     }
+    MDefinition* foldIfAllBitsSet(size_t operand) override {
+        // e.g. for uint16: x & 0xffff => x;
+        return getOperand(1 - operand);
+    }
     void computeRange(TempAllocator& alloc) override;
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return specialization_ != MIRType_None;
     }
 
     ALLOW_CLONE(MBitAnd)
@@ -5506,16 +5515,19 @@ class MBitOr : public MBinaryBitwiseInst
         return getOperand(1 - operand); // 0 | x => x, so if ith is 0, return (1-i)th
     }
     MDefinition* foldIfNegOne(size_t operand) override {
         return getOperand(operand); // x | -1 => -1
     }
     MDefinition* foldIfEqual() override {
         return getOperand(0); // x | x => x
     }
+    MDefinition* foldIfAllBitsSet(size_t operand) override {
+        return this;
+    }
     void computeRange(TempAllocator& alloc) override;
     bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return specialization_ != MIRType_None;
     }
 
     ALLOW_CLONE(MBitOr)
 };
@@ -5535,16 +5547,19 @@ class MBitXor : public MBinaryBitwiseIns
         return getOperand(1 - operand); // 0 ^ x => x
     }
     MDefinition* foldIfNegOne(size_t operand) override {
         return this;
     }
     MDefinition* foldIfEqual() override {
         return this;
     }
+    MDefinition* foldIfAllBitsSet(size_t operand) override {
+        return this;
+    }
     void computeRange(TempAllocator& alloc) override;
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return specialization_ < MIRType_Object;
     }
 
     ALLOW_CLONE(MBitXor)
@@ -5560,16 +5575,19 @@ class MShiftInstruction
 
   public:
     MDefinition* foldIfNegOne(size_t operand) override {
         return this;
     }
     MDefinition* foldIfEqual() override {
         return this;
     }
+    MDefinition* foldIfAllBitsSet(size_t operand) override {
+        return this;
+    }
     virtual void infer(BaselineInspector* inspector, jsbytecode* pc) override;
 };
 
 class MLsh : public MShiftInstruction
 {
     MLsh(MDefinition* left, MDefinition* right)
       : MShiftInstruction(left, right)
     { }
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -3288,16 +3288,54 @@ MUrsh::collectRangeInfoPreTrunc()
     rhsRange.wrapAroundToShiftCount();
 
     // If the most significant bit of our result is always going to be zero,
     // we can optimize by disabling bailout checks for enforcing an int32 range.
     if (lhsRange.lower() >= 0 || rhsRange.lower() >= 1)
         bailoutsDisabled_ = true;
 }
 
+static bool
+DoesMaskMatchRange(int32_t mask, Range& range)
+{
+    // Check if range is positive, because the bitand operator in `(-3) & 0xff` can't be
+    // eliminated.
+    if (range.lower() >= 0) {
+        MOZ_ASSERT(range.isInt32());
+        // Check that the mask value has all bits set given the range upper bound. Note that the
+        // upper bound does not have to be exactly the mask value. For example, consider `x &
+        // 0xfff` where `x` is a uint8. That expression can still be optimized to `x`.
+        int bits = 1 + FloorLog2(range.upper());
+        uint32_t maskNeeded = (bits == 32) ? 0xffffffff : (uint32_t(1) << bits) - 1;
+        if ((mask & maskNeeded) == maskNeeded)
+            return true;
+    }
+
+    return false;
+}
+
+void
+MBinaryBitwiseInstruction::collectRangeInfoPreTrunc()
+{
+    Range lhsRange(lhs());
+    Range rhsRange(rhs());
+
+    if (lhs()->isConstantValue() && lhs()->type() == MIRType_Int32 &&
+         DoesMaskMatchRange(lhs()->constantValue().toInt32(), rhsRange))
+    {
+        maskMatchesRightRange = true;
+    }
+
+    if (rhs()->isConstantValue() && rhs()->type() == MIRType_Int32 &&
+         DoesMaskMatchRange(rhs()->constantValue().toInt32(), lhsRange))
+    {
+        maskMatchesLeftRange = true;
+    }
+}
+
 bool
 RangeAnalysis::prepareForUCE(bool* shouldRemoveDeadCode)
 {
     *shouldRemoveDeadCode = false;
 
     for (ReversePostorderIterator iter(graph_.rpoBegin()); iter != graph_.rpoEnd(); iter++) {
         MBasicBlock* block = *iter;