Bug 1302367 - Use unsigned integer modulo instead of ModD opcode. r=nbp, r=jandem
authorSander Mathijs van Veen <smvv@kompiler.org>
Mon, 03 Oct 2016 02:36:00 -0400
changeset 359180 6e75141df030eb78478ddefef51f06ef34f6ab2d
parent 359179 dce9c50dbf7f7e7be845a55c117d2ec3287a4704
child 359181 30cc764b67a44be9a6fce652314e84e86cb04b1d
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp, jandem
bugs1302367
milestone52.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 1302367 - Use unsigned integer modulo instead of ModD opcode. r=nbp, r=jandem
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
@@ -2731,16 +2731,30 @@ MBinaryBitwiseInstruction::foldsTo(TempA
         return folded;
 
     return this;
 }
 
 MDefinition*
 MBinaryBitwiseInstruction::foldUnnecessaryBitop()
 {
+    // Fold unsigned shift right operator when the second operand is zero and
+    // the only use is an unsigned modulo. Thus, the expression
+    // |(x >>> 0) % y| becomes |x % y|.
+    if (isUrsh() && hasOneDefUse() && getOperand(1)->isConstant()) {
+        MConstant* constant = getOperand(1)->toConstant();
+        if (constant->type() == MIRType::Int32 && constant->toInt32() == 0) {
+            for (MUseDefIterator use(this); use; use++) {
+                if (use.def()->isMod() && use.def()->toMod()->isUnsigned())
+                    return getOperand(0);
+                break;
+            }
+        }
+    }
+
     if (specialization_ != MIRType::Int32)
         return this;
 
     // Eliminate bitwise operations that are no-ops when used on integer
     // inputs, such as (x | 0).
 
     MDefinition* lhs = getOperand(0);
     MDefinition* rhs = getOperand(1);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7134,33 +7134,61 @@ class MDiv : public MBinaryArithInstruct
         return unsigned_ == other->isUnsigned();
     }
 
     ALLOW_CLONE(MDiv)
 };
 
 class MMod : public MBinaryArithInstruction
 {
+  public:
+    enum class PossiblyUnsigned {
+        NotPossible,
+        LHSPossible,
+        RHSPossible,
+        BothPossible,
+    };
+
+  protected:
     bool unsigned_;
     bool canBeNegativeDividend_;
     bool canBePowerOfTwoDivisor_;
     bool canBeDivideByZero_;
     bool trapOnError_;
 
+    PossiblyUnsigned possiblyUnsigned_;
+
     MMod(MDefinition* left, MDefinition* right, MIRType type)
       : MBinaryArithInstruction(left, right),
         unsigned_(false),
         canBeNegativeDividend_(true),
         canBePowerOfTwoDivisor_(true),
         canBeDivideByZero_(true),
-        trapOnError_(false)
+        trapOnError_(false),
+        possiblyUnsigned_(PossiblyUnsigned::NotPossible)
     {
         if (type != MIRType::Value)
             specialization_ = type;
         setResultType(type);
+
+        if (left->isUrsh() && left->getOperand(1)->isConstant()) {
+            MConstant* constant = left->getOperand(1)->toConstant();
+            if (constant->type() == MIRType::Int32 && constant->toInt32() == 0)
+                possiblyUnsigned_ = PossiblyUnsigned::LHSPossible;
+        }
+
+        if (right->isUrsh() && right->getOperand(1)->isConstant()) {
+            MConstant* constant = right->getOperand(1)->toConstant();
+            if (constant->type() == MIRType::Int32 && constant->toInt32() == 0) {
+                if (possiblyUnsigned_ == PossiblyUnsigned::NotPossible)
+                    possiblyUnsigned_ = PossiblyUnsigned::RHSPossible;
+                else
+                    possiblyUnsigned_ = PossiblyUnsigned::BothPossible;
+            }
+        }
     }
 
   public:
     INSTRUCTION_HEADER(Mod)
     static MMod* New(TempAllocator& alloc, MDefinition* left, MDefinition* right) {
         return new(alloc) MMod(left, right, MIRType::Value);
     }
     static MMod* NewAsmJS(TempAllocator& alloc, MDefinition* left, MDefinition* right,
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1556,23 +1556,50 @@ MMod::computeRange(TempAllocator& alloc)
 {
     if (specialization() != MIRType::Int32 && specialization() != MIRType::Double)
         return;
     Range lhs(getOperand(0));
     Range rhs(getOperand(1));
 
     // If either operand is a NaN, the result is NaN. This also conservatively
     // handles Infinity cases.
-    if (!lhs.hasInt32Bounds() || !rhs.hasInt32Bounds())
+    if ((possiblyUnsigned_ == PossiblyUnsigned::NotPossible &&
+         !lhs.hasInt32Bounds()) || !rhs.hasInt32Bounds()) 
+    {
         return;
+    }
 
     // If RHS can be zero, the result can be NaN.
     if (rhs.lower() <= 0 && rhs.upper() >= 0)
         return;
 
+    // (x >>> 0) % y is an unsigned modulo operation but the lhs' range is not
+    // always >= 0. The lhs range assumes a signed integer 32 bit while the
+    // value is unsigned 32 bit. That breaks the assumption that range >= 0.
+    if (specialization() == MIRType::Int32) {
+        switch (possiblyUnsigned_) {
+          case PossiblyUnsigned::NotPossible:
+            break;
+          case PossiblyUnsigned::LHSPossible:
+            if (rhs.lower() > 0 && !rhs.canHaveFractionalPart())
+                unsigned_ = true;
+            break;
+          case PossiblyUnsigned::RHSPossible:
+            if (lhs.lower() >= 0 && !lhs.canHaveFractionalPart())
+                unsigned_ = true;
+            break;
+          case PossiblyUnsigned::BothPossible:
+            if (lhs.lower() >= 0 && !lhs.canHaveFractionalPart())
+                unsigned_ = true;
+            else if (rhs.lower() > 0 && !rhs.canHaveFractionalPart())
+                unsigned_ = true;
+            break;
+        }
+    }
+
     // If both operands are non-negative integers, we can optimize this to an
     // unsigned mod.
     if (specialization() == MIRType::Int32 && lhs.lower() >= 0 && rhs.lower() > 0 &&
         !lhs.canHaveFractionalPart() && !rhs.canHaveFractionalPart())
     {
         unsigned_ = true;
     }