Commits

Alex Gaynor committed 76034ab

Turn INT_MUL with a constant argument that is a power of 2 into an lshift.

  • Participants
  • Parent commits bb4e36f

Comments (0)

Files changed (4)

pypy/jit/metainterp/optimizeopt/rewrite.py

 from pypy.jit.metainterp.resoperation import rop, ResOperation
 from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.metainterp.optimizeopt.intutils import IntBound
+from pypy.rlib.rarithmetic import highest_bit
+
 
 class OptRewrite(Optimization):
     """Rewrite operations into equivalent, cheaper operations.
              (v2.is_constant() and v2.box.getint() == 0):
             self.make_constant_int(op.result, 0)
         else:
+            for lhs, rhs in [(v1, v2), (v2, v1)]:
+                # x & (x -1) == 0 is a quick test for power of 2
+                if (lhs.is_constant() and
+                    (lhs.box.getint() & (lhs.box.getint() - 1)) == 0):
+                    new_rhs = ConstInt(highest_bit(lhs.box.getint()))
+                    op = op.copy_and_change(rop.INT_LSHIFT, args=[rhs.box, new_rhs])
+                    break
+
             self.emit_operation(op)
 
     def optimize_CALL_PURE(self, op):
         if v1.intbound.known_ge(IntBound(0, 0)) and v2.is_constant():
             val = v2.box.getint()
             if val & (val - 1) == 0 and val > 0: # val == 2**shift
-                shift = 0
-                while (1 << shift) < val:
-                    shift += 1
                 op = op.copy_and_change(rop.INT_RSHIFT,
-                                        args = [op.getarg(0), ConstInt(shift)])
+                                        args = [op.getarg(0), ConstInt(highest_bit(val))])
         self.emit_operation(op)
 
 

pypy/jit/metainterp/test/test_optimizeopt.py

         """
         self.optimize_loop(ops, expected)
 
+    def test_mul_to_lshift(self):
+        ops = """
+        [i1, i2]
+        i3 = int_mul(i1, 2)
+        i4 = int_mul(2, i2)
+        i5 = int_mul(i1, 32)
+        i6 = int_mul(i1, i2)
+        jump(i5, i6)
+        """
+        expected = """
+        [i1, i2]
+        i3 = int_lshift(i1, 1)
+        i4 = int_lshift(i2, 1)
+        i5 = int_lshift(i1, 5)
+        i6 = int_mul(i1, i2)
+        jump(i5, i6)
+        """
+        self.optimize_loop(ops, expected)
+
     def test_lshift_rshift(self):
         ops = """
         [i1, i2, i2b, i1b]

pypy/rlib/rarithmetic.py

         assert t.BITS <= r_longlong.BITS
         return build_int(None, t.SIGNED, r_longlong.BITS)
 
+def highest_bit(n):
+    """
+    Calculates the highest set bit in n.  This function assumes that n is a
+    power of 2 (and thus only has a single set bit).
+    """
+    assert n and (n & (n - 1)) == 0
+    i = -1
+    while n:
+        i += 1
+        n >>= 1
+    return i
+
 class base_int(long):
     """ fake unsigned integer implementation """
 

pypy/rlib/test/test_rarithmetic.py

 def test_int_real_union():
     from pypy.rpython.lltypesystem.rffi import r_int_real
     assert compute_restype(r_int_real, r_int_real) is r_int_real
+
+def test_highest_bit():
+    py.test.raises(AssertionError, highest_bit, 0)
+    py.test.raises(AssertionError, highest_bit, 14)
+
+    for i in xrange(31):
+        assert highest_bit(2**i) == i