[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