[pypy-commit] pypy default: hg merge remove-raisingops (again**2)

arigo pypy.commits at gmail.com
Fri May 27 18:15:04 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r84746:c8536785e17f
Date: 2016-05-27 23:21 +0100
http://bitbucket.org/pypy/pypy/changeset/c8536785e17f/

Log:	hg merge remove-raisingops (again**2)

	Modulo-by-constant

diff --git a/pypy/module/pypyjit/test_pypy_c/test_shift.py b/pypy/module/pypyjit/test_pypy_c/test_shift.py
--- a/pypy/module/pypyjit/test_pypy_c/test_shift.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_shift.py
@@ -78,6 +78,44 @@
             i6 = int_xor(i5, i2)
         """ % args)
 
+    def test_modulo_optimization(self):
+        def main(b):
+            res = 0
+            a = 0
+            while a < 300:
+                res1 = a%b     # ID: mod
+                res2 = a%2     # ID: and
+                res3 = a%11    # ID: mul
+                res += res1 + res2 + res3
+                a += 1
+            return res
+        #
+        log = self.run(main, [3])
+        assert log.result == main(3)
+        loop, = log.loops_by_filename(self.filepath)
+        assert loop.match_by_id('mod', """
+            i56 = int_eq(i48, %d)
+            i57 = int_and(i56, i37)
+            guard_false(i57, descr=...)
+            i1 = call_i(_, i48, i3, descr=...)
+        """ % (-sys.maxint-1,))
+        assert loop.match_by_id('and', """
+            i1 = int_and(i2, 1)
+        """)
+        if sys.maxint > 2**32:
+            args = (63, -5030930201920786804, 3)
+        else:
+            args = (31, -1171354717, 3)
+        assert loop.match_by_id('mul', """
+            i2 = int_rshift(i1, %d)
+            i3 = int_xor(i1, i2)
+            i4 = uint_mul_high(i3, %d)
+            i5 = uint_rshift(i4, %d)
+            i6 = int_xor(i5, i2)
+            i7 = int_mul(i6, 11)
+            i8 = int_sub(i1, i7)
+        """ % args)
+
     def test_division_to_rshift_allcases(self):
         """
         This test only checks that we get the expected result, not that any
diff --git a/pypy/module/pypyjit/test_pypy_c/test_string.py b/pypy/module/pypyjit/test_pypy_c/test_string.py
--- a/pypy/module/pypyjit/test_pypy_c/test_string.py
+++ b/pypy/module/pypyjit/test_pypy_c/test_string.py
@@ -54,12 +54,25 @@
         log = self.run(main, [1100], import_site=True)
         assert log.result == main(1100)
         loop, = log.loops_by_filename(self.filepath)
+        if sys.maxint > 2**32:
+            args = (63, -3689348814741910323, 3)
+        else:
+            args = (31, -858993459, 3)
         assert loop.match("""
             i11 = int_lt(i6, i7)
             guard_true(i11, descr=...)
             guard_not_invalidated(descr=...)
             i13 = int_eq(i6, %d)         # value provided below
-            i19 = call_i(ConstClass(ll_int_mod__Signed_Signed), i6, 10, descr=<Calli . ii EF=0 OS=14>)
+
+            # "mod 10" block:
+            i79 = int_rshift(i6, %d)
+            i80 = int_xor(i6, i79)
+            i82 = uint_mul_high(i80, %d)
+            i84 = uint_rshift(i82, %d)
+            i85 = int_xor(i84, i79)
+            i87 = int_mul(i85, 10)
+            i19 = int_sub(i6, i87)
+
             i23 = strgetitem(p10, i19)
             p25 = newstr(1)
             strsetitem(p25, 0, i23)
@@ -74,7 +87,7 @@
             guard_no_overflow(descr=...)
             --TICK--
             jump(..., descr=...)
-        """ % (-sys.maxint-1,))
+        """ % ((-sys.maxint-1,)+args))
 
     def test_str_mod(self):
         def main(n):
diff --git a/rpython/jit/metainterp/optimizeopt/intbounds.py b/rpython/jit/metainterp/optimizeopt/intbounds.py
--- a/rpython/jit/metainterp/optimizeopt/intbounds.py
+++ b/rpython/jit/metainterp/optimizeopt/intbounds.py
@@ -196,17 +196,6 @@
     def opt_call_INT_PY_MOD(self, op):
         b1 = self.getintbound(op.getarg(1))
         b2 = self.getintbound(op.getarg(2))
-        if b2.is_constant():
-            val = b2.getint()
-            if val > 0 and (val & (val-1)) == 0:
-                # x % power-of-two ==> x & (power-of-two - 1)
-                # with Python's modulo, this is valid even if 'x' is negative.
-                from rpython.jit.metainterp.history import DONT_CHANGE
-                arg1 = op.getarg(1)
-                arg2 = ConstInt(val-1)
-                op = self.replace_op_with(op, rop.INT_AND,
-                                          args=[arg1, arg2],
-                                          descr=DONT_CHANGE)  # <- xxx rename?
         self.emit_operation(op)
         if b2.is_constant():
             val = b2.getint()
diff --git a/rpython/jit/metainterp/optimizeopt/intdiv.py b/rpython/jit/metainterp/optimizeopt/intdiv.py
--- a/rpython/jit/metainterp/optimizeopt/intdiv.py
+++ b/rpython/jit/metainterp/optimizeopt/intdiv.py
@@ -90,6 +90,14 @@
         return [mul_box, sh_box]
 
 
+def modulo_operations(n_box, m, known_nonneg=False):
+    operations = division_operations(n_box, m, known_nonneg)
+
+    mul_box = ResOperation(rop.INT_MUL, [operations[-1], ConstInt(m)])
+    diff_box = ResOperation(rop.INT_SUB, [n_box, mul_box])
+    return operations + [mul_box, diff_box]
+
+
 def unsigned_mul_high(a, b):
     DIGIT = LONG_BIT / 2
     MASK = (1 << DIGIT) - 1
diff --git a/rpython/jit/metainterp/optimizeopt/rewrite.py b/rpython/jit/metainterp/optimizeopt/rewrite.py
--- a/rpython/jit/metainterp/optimizeopt/rewrite.py
+++ b/rpython/jit/metainterp/optimizeopt/rewrite.py
@@ -673,6 +673,9 @@
         elif oopspecindex == EffectInfo.OS_INT_PY_DIV:
             if self._optimize_CALL_INT_PY_DIV(op):
                 return
+        elif oopspecindex == EffectInfo.OS_INT_PY_MOD:
+            if self._optimize_CALL_INT_PY_MOD(op):
+                return
         self.emit_operation(op)
     optimize_CALL_PURE_R = optimize_CALL_PURE_I
     optimize_CALL_PURE_F = optimize_CALL_PURE_I
@@ -726,6 +729,46 @@
             self.make_equal_to(op, newop)
             return True
 
+    def _optimize_CALL_INT_PY_MOD(self, op):
+        arg1 = op.getarg(1)
+        b1 = self.getintbound(arg1)
+        arg2 = op.getarg(2)
+        b2 = self.getintbound(arg2)
+
+        if b1.is_constant() and b1.getint() == 0:
+            self.make_constant_int(op, 0)
+            self.last_emitted_operation = REMOVED
+            return True
+        # This is Python's integer division: 'x // (2**shift)' can always
+        # be replaced with 'x >> shift', even for negative values of x
+        if not b2.is_constant():
+            return False
+        val = b2.getint()
+        if val <= 0:
+            return False
+        if val == 1:
+            self.make_constant_int(op, 0)
+            self.last_emitted_operation = REMOVED
+            return True
+        elif val & (val - 1) == 0:   # val == 2**shift
+            from rpython.jit.metainterp.history import DONT_CHANGE
+            # x % power-of-two ==> x & (power-of-two - 1)
+            # with Python's modulo, this is valid even if 'x' is negative.
+            op = self.replace_op_with(op, rop.INT_AND,
+                        args=[arg1, ConstInt(val - 1)],
+                        descr=DONT_CHANGE)  # <- xxx rename? means "kill"
+            self.optimizer.send_extra_operation(op)
+            return True
+        else:
+            from rpython.jit.metainterp.optimizeopt import intdiv
+            known_nonneg = b1.known_ge(IntBound(0, 0))
+            operations = intdiv.modulo_operations(arg1, val, known_nonneg)
+            newop = None
+            for newop in operations:
+                self.optimizer.send_extra_operation(newop)
+            self.make_equal_to(op, newop)
+            return True
+
     def optimize_CAST_PTR_TO_INT(self, op):
         self.optimizer.pure_reverse(op)
         self.emit_operation(op)
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_intdiv.py b/rpython/jit/metainterp/optimizeopt/test/test_intdiv.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_intdiv.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_intdiv.py
@@ -4,6 +4,7 @@
 
 from rpython.jit.metainterp.optimizeopt.intdiv import magic_numbers, LONG_BIT
 from rpython.jit.metainterp.optimizeopt.intdiv import division_operations
+from rpython.jit.metainterp.optimizeopt.intdiv import modulo_operations
 from rpython.jit.metainterp.optimizeopt.intdiv import unsigned_mul_high
 from rpython.jit.metainterp.history import ConstInt
 from rpython.jit.metainterp.resoperation import InputArgInt
@@ -46,3 +47,22 @@
         constants[op] = ConstInt(res)
 
     assert constants[op].getint() == n // m
+
+
+ at given(strategies.integers(min_value=-sys.maxint-1, max_value=sys.maxint),
+       not_power_of_two,
+       strategies.booleans())
+def test_modulo_operations(n, m, known_nonneg):
+    if n < 0:
+        known_nonneg = False
+    n_box = InputArgInt()
+    ops = modulo_operations(n_box, m, known_nonneg)
+
+    constants = {n_box: ConstInt(n)}
+    for op in ops:
+        argboxes = op.getarglist()
+        constantboxes = [constants.get(box, box) for box in argboxes]
+        res = execute(None, None, op.getopnum(), None, *constantboxes)
+        constants[op] = ConstInt(res)
+
+    assert constants[op].getint() == n % m
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -1,5 +1,6 @@
-import py
+import py, sys
 from rpython.rlib.objectmodel import instantiate
+from rpython.rlib.rarithmetic import intmask
 from rpython.jit.metainterp.optimizeopt.test.test_util import (
     LLtypeMixin, BaseTest, FakeMetaInterpStaticData, convert_old_style_to_targets)
 from rpython.jit.metainterp.history import TargetToken, JitCellToken
@@ -4659,6 +4660,7 @@
         self.optimize_loop(ops, expected)
 
     def test_intmod_bounds(self):
+        from rpython.jit.metainterp.optimizeopt.intdiv import magic_numbers
         ops = """
         [i0, i1]
         i2 = call_pure_i(321, i0, 12, descr=int_py_mod_descr)
@@ -4673,31 +4675,32 @@
         guard_false(i7) []
         jump(i2, i5)
         """
+        kk, ii = magic_numbers(12)
         expected = """
         [i0, i1]
-        i2 = call_i(321, i0, 12, descr=int_py_mod_descr)
+        i4 = int_rshift(i0, %d)
+        i6 = int_xor(i0, i4)
+        i8 = uint_mul_high(i6, %d)
+        i9 = uint_rshift(i8, %d)
+        i10 = int_xor(i9, i4)
+        i11 = int_mul(i10, 12)
+        i2 = int_sub(i0, i11)
         i5 = call_i(321, i1, -12, descr=int_py_mod_descr)
         jump(i2, i5)
-        """
-        self.optimize_loop(ops, expected)
-
-        # same as above, but all guards are shifted by one so that they
-        # must stay
-        ops = """
-        [i8, i9]
-        i0 = escape_i()
-        i2 = call_pure_i(321, i0, 12, descr=int_py_mod_descr)
-        i3 = int_ge(i2, 11)
-        guard_false(i3) []
-        i4 = int_lt(i2, 1)
-        guard_false(i4) []
+        """ % (63 if sys.maxint > 2**32 else 31, intmask(kk), ii)
+        self.optimize_loop(ops, expected)
+
+        # same as above (2nd case), but all guards are shifted by one so
+        # that they must stay
+        ops = """
+        [i9]
         i1 = escape_i()
         i5 = call_pure_i(321, i1, -12, descr=int_py_mod_descr)
         i6 = int_le(i5, -11)
         guard_false(i6) []
         i7 = int_gt(i5, -1)
         guard_false(i7) []
-        jump(i2, i5)
+        jump(i5)
         """
         self.optimize_loop(ops, ops.replace('call_pure_i', 'call_i'))
 


More information about the pypy-commit mailing list