[pypy-commit] pypy default: hg merge array-propagate-len

arigo noreply at buildbot.pypy.org
Sat Feb 15 18:21:29 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r69165:754284731a51
Date: 2014-02-15 18:20 +0100
http://bitbucket.org/pypy/pypy/changeset/754284731a51/

Log:	hg merge array-propagate-len

	Kill some guards and operations in JIT traces by adding integer
	bounds propagation for getfield_(raw|gc) and getarrayitem_(raw|gc).

diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -70,3 +70,7 @@
 
 .. branch: NonConstant
 Simplify implementation of NonConstant.
+
+.. branch: array-propagate-len
+Kill some guards and operations in JIT traces by adding integer bounds
+propagation for getfield_(raw|gc) and getarrayitem_(raw|gc).
diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -1,10 +1,12 @@
 import py, weakref
 from rpython.jit.backend import model
 from rpython.jit.backend.llgraph import support
+from rpython.jit.backend.llsupport import symbolic
 from rpython.jit.metainterp.history import AbstractDescr
 from rpython.jit.metainterp.history import Const, getkind
 from rpython.jit.metainterp.history import INT, REF, FLOAT, VOID
 from rpython.jit.metainterp.resoperation import rop
+from rpython.jit.metainterp.optimizeopt import intbounds
 from rpython.jit.codewriter import longlong, heaptracker
 from rpython.jit.codewriter.effectinfo import EffectInfo
 
@@ -119,6 +121,24 @@
     def is_field_signed(self):
         return _is_signed_kind(self.FIELD)
 
+    def is_integer_bounded(self):
+        return getkind(self.FIELD) == 'int' \
+            and rffi.sizeof(self.FIELD) < symbolic.WORD
+
+    def get_integer_min(self):
+        if getkind(self.FIELD) != 'int':
+            assert False
+
+        return intbounds.get_integer_min(
+            not _is_signed_kind(self.FIELD), rffi.sizeof(self.FIELD))
+
+    def get_integer_max(self):
+        if getkind(self.FIELD) != 'int':
+            assert False
+
+        return intbounds.get_integer_max(
+            not _is_signed_kind(self.FIELD), rffi.sizeof(self.FIELD))
+
 def _is_signed_kind(TYPE):
     return (TYPE is not lltype.Bool and isinstance(TYPE, lltype.Number) and
             rffi.cast(TYPE, -1) == -1)
@@ -144,6 +164,25 @@
     def is_array_of_structs(self):
         return isinstance(self.A.OF, lltype.Struct)
 
+    def is_item_integer_bounded(self):
+        return getkind(self.A.OF) == 'int' \
+            and rffi.sizeof(self.A.OF) < symbolic.WORD
+
+    def get_item_integer_min(self):
+        if getkind(self.A.OF) != 'int':
+            assert False
+
+        return intbounds.get_integer_min(
+            not _is_signed_kind(self.A.OF), rffi.sizeof(self.A.OF))
+
+    def get_item_integer_max(self):
+        if getkind(self.A.OF) != 'int':
+            assert False
+
+        return intbounds.get_integer_max(
+            not _is_signed_kind(self.A.OF), rffi.sizeof(self.A.OF))
+
+
 class InteriorFieldDescr(AbstractDescr):
     def __init__(self, A, fieldname):
         self.A = A
@@ -162,6 +201,24 @@
     def is_float_field(self):
         return getkind(self.FIELD) == 'float'
 
+    def is_integer_bounded(self):
+        return getkind(self.FIELD) == 'int' \
+            and rffi.sizeof(self.FIELD) < symbolic.WORD
+
+    def get_integer_min(self):
+        if getkind(self.FIELD) != 'int':
+            assert False
+
+        return intbounds.get_integer_min(
+            not _is_signed_kind(self.FIELD), rffi.sizeof(self.FIELD))
+
+    def get_integer_max(self):
+        if getkind(self.FIELD) != 'int':
+            assert False
+
+        return intbounds.get_integer_max(
+            not _is_signed_kind(self.FIELD), rffi.sizeof(self.FIELD))
+
 _example_res = {'v': None,
                 'r': lltype.nullptr(llmemory.GCREF.TO),
                 'i': 0,
diff --git a/rpython/jit/backend/llsupport/descr.py b/rpython/jit/backend/llsupport/descr.py
--- a/rpython/jit/backend/llsupport/descr.py
+++ b/rpython/jit/backend/llsupport/descr.py
@@ -6,6 +6,7 @@
 from rpython.jit.metainterp import history
 from rpython.jit.codewriter import heaptracker, longlong
 from rpython.jit.codewriter.longlong import is_longlong
+from rpython.jit.metainterp.optimizeopt import intbounds
 
 
 class GcCache(object):
@@ -103,6 +104,26 @@
     def is_field_signed(self):
         return self.flag == FLAG_SIGNED
 
+    def is_integer_bounded(self):
+        return self.flag in (FLAG_SIGNED, FLAG_UNSIGNED) \
+            and self.field_size < symbolic.WORD
+
+    def get_integer_min(self):
+        if self.flag == FLAG_UNSIGNED:
+            return intbounds.get_integer_min(True, self.field_size)
+        elif self.flag == FLAG_SIGNED:
+            return intbounds.get_integer_min(False, self.field_size)
+
+        assert False
+
+    def get_integer_max(self):
+        if self.flag == FLAG_UNSIGNED:
+            return intbounds.get_integer_max(True, self.field_size)
+        elif self.flag == FLAG_SIGNED:
+            return intbounds.get_integer_max(False, self.field_size)
+
+        assert False
+
     def sort_key(self):
         return self.offset
 
@@ -182,6 +203,28 @@
     def is_array_of_structs(self):
         return self.flag == FLAG_STRUCT
 
+    def is_item_integer_bounded(self):
+        return self.flag in (FLAG_SIGNED, FLAG_UNSIGNED) \
+            and self.itemsize < symbolic.WORD
+
+    def get_item_integer_min(self):
+        if self.flag == FLAG_UNSIGNED:
+            return intbounds.get_integer_min(True, self.itemsize)
+        elif self.flag == FLAG_SIGNED:
+            return intbounds.get_integer_min(False, self.itemsize)
+
+        assert False
+
+    def get_item_integer_max(self):
+        if self.flag == FLAG_UNSIGNED:
+            return intbounds.get_integer_max(True, self.itemsize)
+        elif self.flag == FLAG_SIGNED:
+            return intbounds.get_integer_max(False, self.itemsize)
+
+        assert False
+
+
+
     def repr_of_descr(self):
         return '<Array%s %s>' % (self.flag, self.itemsize)
 
@@ -230,6 +273,15 @@
     def is_float_field(self):
         return self.fielddescr.is_float_field()
 
+    def is_integer_bounded(self):
+        return self.fielddescr.is_integer_bounded()
+
+    def get_integer_min(self):
+        return self.fielddescr.get_integer_min()
+
+    def get_integer_max(self):
+        return self.fielddescr.get_integer_max()
+
     def repr_of_descr(self):
         return '<InteriorFieldDescr %s>' % self.fielddescr.repr_of_descr()
 
diff --git a/rpython/jit/backend/llsupport/test/test_descr.py b/rpython/jit/backend/llsupport/test/test_descr.py
--- a/rpython/jit/backend/llsupport/test/test_descr.py
+++ b/rpython/jit/backend/llsupport/test/test_descr.py
@@ -432,3 +432,24 @@
     assert descr.basesize == struct.calcsize("PP")         # hash, length
     assert descr.lendescr.offset == struct.calcsize("P")   # hash
     assert not descr.is_array_of_pointers()
+
+
+def test_descr_integer_bounded():
+    descr = FieldDescr('descr', 0, symbolic.SIZEOF_CHAR, FLAG_SIGNED)
+    assert descr.is_integer_bounded()
+
+    descr = FieldDescr('descr', 0, symbolic.WORD, FLAG_UNSIGNED)
+    assert not descr.is_integer_bounded()
+
+    descr = FieldDescr('descr', 0, symbolic.SIZEOF_FLOAT, FLAG_FLOAT)
+    assert not descr.is_integer_bounded()
+
+
+def test_descr_get_integer_bounds():
+    descr = FieldDescr('decr', 0, 1, FLAG_UNSIGNED)
+    assert descr.get_integer_min() == 0
+    assert descr.get_integer_max() == 255
+
+    descr = FieldDescr('descr', 0, 1, FLAG_SIGNED)
+    assert descr.get_integer_min() == -128
+    assert descr.get_integer_max() == 127
diff --git a/rpython/jit/metainterp/optimizeopt/__init__.py b/rpython/jit/metainterp/optimizeopt/__init__.py
--- a/rpython/jit/metainterp/optimizeopt/__init__.py
+++ b/rpython/jit/metainterp/optimizeopt/__init__.py
@@ -63,7 +63,7 @@
             optimizer.propagate_all_forward()
     finally:
         debug_stop("jit-optimize")
-        
+
 if __name__ == '__main__':
     print ALL_OPTS_NAMES
 
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
@@ -9,6 +9,20 @@
 from rpython.jit.metainterp.resoperation import rop
 
 
+def get_integer_min(is_unsigned, byte_size):
+    if is_unsigned:
+        return 0
+    else:
+        return -(1 << ((byte_size << 3) - 1))
+
+
+def get_integer_max(is_unsigned, byte_size):
+    if is_unsigned:
+        return (1 << (byte_size << 3)) - 1
+    else:
+        return (1 << ((byte_size << 3) - 1)) - 1
+
+
 class OptIntBounds(Optimization):
     """Keeps track of the bounds placed on integers by guards and remove
        redundant guards"""
@@ -322,6 +336,29 @@
         v1.intbound.make_ge(IntLowerBound(0))
         v1.intbound.make_lt(IntUpperBound(256))
 
+    def optimize_GETFIELD_RAW(self, op):
+        self.emit_operation(op)
+        descr = op.getdescr()
+        if descr.is_integer_bounded():
+            v1 = self.getvalue(op.result)
+            v1.intbound.make_ge(IntLowerBound(descr.get_integer_min()))
+            v1.intbound.make_lt(IntUpperBound(descr.get_integer_max() + 1))
+
+    optimize_GETFIELD_GC = optimize_GETFIELD_RAW
+
+    optimize_GETINTERIORFIELD_GC = optimize_GETFIELD_RAW
+
+    def optimize_GETARRAYITEM_RAW(self, op):
+        self.emit_operation(op)
+        descr = op.getdescr()
+        if descr and descr.is_item_integer_bounded():
+            v1 = self.getvalue(op.result)
+            v1.intbound.make_ge(IntLowerBound(descr.get_item_integer_min()))
+            v1.intbound.make_lt(
+                IntUpperBound(descr.get_item_integer_max() + 1))
+
+    optimize_GETARRAYITEM_GC = optimize_GETARRAYITEM_RAW
+
     def optimize_UNICODEGETITEM(self, op):
         self.emit_operation(op)
         v1 = self.getvalue(op.result)
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
@@ -5200,6 +5200,104 @@
         """
         self.optimize_loop(ops, ops)
 
+    def test_getfield_cmp_above_bounds(self):
+        ops = """
+        [p0]
+        i0 = getfield_gc(p0, descr=chardescr)
+        i1 = int_lt(i0, 256)
+        guard_true(i1) []
+        """
+
+        expected = """
+        [p0]
+        i0 = getfield_gc(p0, descr=chardescr)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_getfield_cmp_below_bounds(self):
+        ops = """
+        [p0]
+        i0 = getfield_gc(p0, descr=chardescr)
+        i1 = int_gt(i0, -1)
+        guard_true(i1) []
+        """
+
+        expected = """
+        [p0]
+        i0 = getfield_gc(p0, descr=chardescr)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_getfield_cmp_in_bounds(self):
+        ops = """
+        [p0]
+        i0 = getfield_gc(p0, descr=chardescr)
+        i1 = int_gt(i0, 0)
+        guard_true(i1) []
+        i2 = int_lt(i0, 255)
+        guard_true(i2) []
+        """
+        self.optimize_loop(ops, ops)
+
+    def test_getfieldraw_cmp_outside_bounds(self):
+        ops = """
+        [p0]
+        i0 = getfield_raw(p0, descr=chardescr)
+        i1 = int_gt(i0, -1)
+        guard_true(i1) []
+        """
+
+        expected = """
+        [p0]
+        i0 = getfield_raw(p0, descr=chardescr)
+        """
+        self.optimize_loop(ops, expected)
+
+
+    def test_rawarray_cmp_outside_intbounds(self):
+        ops = """
+        [i0]
+        i1 = getarrayitem_raw(i0, 0, descr=rawarraydescr_char)
+        i2 = int_lt(i1, 256)
+        guard_true(i2) []
+        """
+
+        expected = """
+        [i0]
+        i1 = getarrayitem_raw(i0, 0, descr=rawarraydescr_char)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_gcarray_outside_intbounds(self):
+        ops = """
+        [p0]
+        i0 = getarrayitem_gc(p0, 0, descr=chararraydescr)
+        i1 = int_lt(i0, 256)
+        guard_true(i1) []
+        """
+
+        expected = """
+        [p0]
+        i0 = getarrayitem_gc(p0, 0, descr=chararraydescr)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_getinterior_outside_intbounds(self):
+        ops = """
+        [p0]
+        f0 = getinteriorfield_gc(p0, 0, descr=fc_array_floatdescr)
+        i0 = getinteriorfield_gc(p0, 0, descr=fc_array_chardescr)
+        i1 = int_lt(i0, 256)
+        guard_true(i1) []
+        """
+
+        expected = """
+        [p0]
+        f0 = getinteriorfield_gc(p0, 0, descr=fc_array_floatdescr)
+        i0 = getinteriorfield_gc(p0, 0, descr=fc_array_chardescr)
+        """
+        self.optimize_loop(ops, expected)
+
 
 class TestLLtype(BaseTestOptimizeBasic, LLtypeMixin):
     pass
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -5563,6 +5563,8 @@
                 self.name = name
             def sort_key(self):
                 return id(self)
+            def is_integer_bounded(self):
+                return False
 
 
         for n in ('inst_w_seq', 'inst_index', 'inst_w_list', 'inst_length',
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -92,6 +92,7 @@
     NODE.become(lltype.GcStruct('NODE', ('parent', OBJECT),
                                         ('value', lltype.Signed),
                                         ('floatval', lltype.Float),
+                                        ('charval', lltype.Char),
                                         ('next', lltype.Ptr(NODE))))
     NODE2 = lltype.GcStruct('NODE2', ('parent', NODE),
                                      ('other', lltype.Ptr(NODE)))
@@ -108,6 +109,7 @@
     nodesize2 = cpu.sizeof(NODE2)
     valuedescr = cpu.fielddescrof(NODE, 'value')
     floatdescr = cpu.fielddescrof(NODE, 'floatval')
+    chardescr = cpu.fielddescrof(NODE, 'charval')
     nextdescr = cpu.fielddescrof(NODE, 'next')
     otherdescr = cpu.fielddescrof(NODE2, 'other')
 
@@ -204,6 +206,8 @@
                         EffectInfo.EF_CANNOT_RAISE,
                         oopspecindex=EffectInfo.OS_RAW_FREE))
 
+    chararray = lltype.GcArray(lltype.Char)
+    chararraydescr = cpu.arraydescrof(chararray)
 
     # array of structs (complex data)
     complexarray = lltype.GcArray(
@@ -221,6 +225,12 @@
     rawarraydescr_char = cpu.arraydescrof(lltype.Array(lltype.Char,
                                                        hints={'nolength': True}))
 
+    fc_array = lltype.GcArray(
+        lltype.Struct(
+            "floatchar", ("float", lltype.Float), ("char", lltype.Char)))
+    fc_array_descr = cpu.arraydescrof(fc_array)
+    fc_array_floatdescr = cpu.interiorfielddescrof(fc_array, "float")
+    fc_array_chardescr = cpu.interiorfielddescrof(fc_array, "char")
 
     for _name, _os in [
         ('strconcatdescr',               'OS_STR_CONCAT'),
diff --git a/rpython/jit/metainterp/optimizeopt/util.py b/rpython/jit/metainterp/optimizeopt/util.py
--- a/rpython/jit/metainterp/optimizeopt/util.py
+++ b/rpython/jit/metainterp/optimizeopt/util.py
@@ -1,3 +1,5 @@
+import itertools
+
 import py
 from rpython.rlib.objectmodel import r_dict, compute_identity_hash
 from rpython.rlib.rarithmetic import intmask
@@ -136,13 +138,16 @@
     print ' Comparing lists '.center(totwidth, '-')
     text_right = text_right or 'expected'
     print '%s| %s' % ('optimized'.center(width), text_right.center(width))
-    for op1, op2 in zip(oplist1, oplist2):
+    for op1, op2 in itertools.izip_longest(oplist1, oplist2, fillvalue=''):
         txt1 = str(op1)
         txt2 = str(op2)
         while txt1 or txt2:
             print '%s| %s' % (txt1[:width].ljust(width), txt2[:width])
             txt1 = txt1[width:]
             txt2 = txt2[width:]
+    print '-' * totwidth
+
+    for op1, op2 in zip(oplist1, oplist2):
         assert op1.getopnum() == op2.getopnum()
         assert op1.numargs() == op2.numargs()
         for i in range(op1.numargs()):
@@ -177,6 +182,5 @@
                     else:
                         assert False
     assert len(oplist1) == len(oplist2)
-    print '-'*totwidth
     return True
 


More information about the pypy-commit mailing list