[pypy-svn] r76717 - in pypy/branch/jit-bounds/pypy: jit/metainterp jit/metainterp/test module/pypyjit/test

hakanardo at codespeak.net hakanardo at codespeak.net
Tue Aug 24 17:52:16 CEST 2010


Author: hakanardo
Date: Tue Aug 24 17:52:13 2010
New Revision: 76717

Added:
   pypy/branch/jit-bounds/pypy/module/pypyjit/test/randomized.py
Modified:
   pypy/branch/jit-bounds/pypy/jit/metainterp/optimizeopt.py
   pypy/branch/jit-bounds/pypy/jit/metainterp/test/test_intbound.py
   pypy/branch/jit-bounds/pypy/jit/metainterp/test/test_optimizeopt.py
   pypy/branch/jit-bounds/pypy/module/pypyjit/test/test_pypy_c.py
Log:
int_mul and int_ne suppert, randomized tests, more traces

Modified: pypy/branch/jit-bounds/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/branch/jit-bounds/pypy/jit/metainterp/optimizeopt.py	(original)
+++ pypy/branch/jit-bounds/pypy/jit/metainterp/optimizeopt.py	Tue Aug 24 17:52:13 2010
@@ -118,6 +118,9 @@
         except OverflowError:
             res.has_upper = False
         return res
+
+    def mul(self, value):
+        return self.mul_bound(IntBound(value, value))
     
     def add_bound(self, other):
         res = self.copy()
@@ -155,6 +158,35 @@
             res.has_lower = False
         return res
 
+    def mul_bound(self, other):
+        if self.has_upper and self.has_lower and \
+           other.has_upper and other.has_lower:
+            try:
+                vals = (ovfcheck(self.upper * other.upper),
+                        ovfcheck(self.upper * other.lower),
+                        ovfcheck(self.lower * other.upper),
+                        ovfcheck(self.lower * other.lower))
+                return IntBound(min4(vals), max4(vals))
+            except OverflowError:
+                return IntUnbounded()
+        else:
+            return IntUnbounded()
+
+    def div_bound(self, other):
+        if self.has_upper and self.has_lower and \
+           other.has_upper and other.has_lower and \
+           not other.contains(0):
+            try:
+                vals = (ovfcheck(self.upper / other.upper),
+                        ovfcheck(self.upper / other.lower),
+                        ovfcheck(self.lower / other.upper),
+                        ovfcheck(self.lower / other.lower))
+                return IntBound(min4(vals), max4(vals))
+            except OverflowError:
+                return IntUnbounded()
+        else:
+            return IntUnbounded()
+
     def contains(self, val):
         if self.has_lower and val < self.lower:
             return False
@@ -711,6 +743,9 @@
         self.resumedata_memo.update_counters(self.metainterp_sd.profiler)
 
     def propagate_bounds_backward(self, box):
+        # FIXME: This takes care of the instruction where box is the reuslt
+        #        but the bounds produced by all instructions where box is
+        #        an argument might also be tighten
         v = self.getvalue(box)
         b = v.intbound
         if b.has_lower and b.has_upper and b.lower == b.upper:
@@ -817,7 +852,6 @@
         oldop = self.pure_operations.get(targs, None)
         if oldop is not None and oldop.descr is op.descr:
             value = self.getvalue(oldop.result)
-            print value
             if value.is_constant():
                 if value.box.same_constant(CONST_1):
                     self.make_constant(op.result, CONST_0)
@@ -1280,13 +1314,29 @@
         self.pure(rop.INT_SUB, [op.result, op.args[1]], op.args[0])
         self.pure(rop.INT_SUB, [op.result, op.args[0]], op.args[1])
 
+    def optimize_INT_MUL(self, op):
+        v1 = self.getvalue(op.args[0])
+        v2 = self.getvalue(op.args[1])
+        # If one side of the op is 0 the result is the other side.
+        if v1.is_constant() and v1.box.getint() == 1:
+            self.make_equal_to(op.result, v2)
+        elif v2.is_constant() and v2.box.getint() == 1:
+            self.make_equal_to(op.result, v1)
+        elif (v1.is_constant() and v1.box.getint() == 0) or \
+             (v2.is_constant() and v2.box.getint() == 0):
+            self.make_constant_int(op.result, 0)
+        else:
+            self.optimize_default(op)
+        r = self.getvalue(op.result)
+        r.intbound.intersect(v1.intbound.mul_bound(v2.intbound))
+
     def optimize_INT_ADD_OVF(self, op):
         v1 = self.getvalue(op.args[0])
         v2 = self.getvalue(op.args[1])
         resbound = v1.intbound.add_bound(v2.intbound)
         if resbound.has_lower and resbound.has_upper and \
                self.loop.operations[self.i+1].opnum == rop.GUARD_NO_OVERFLOW:
-            # Transform into INT_SUB and remove guard
+            # Transform into INT_ADD and remove guard
             op.opnum = rop.INT_ADD
             self.i += 1
             self.optimize_INT_ADD(op)
@@ -1310,6 +1360,21 @@
             r = self.getvalue(op.result)
             r.intbound.intersect(resbound)
 
+    def optimize_INT_MUL_OVF(self, op):
+        v1 = self.getvalue(op.args[0])
+        v2 = self.getvalue(op.args[1])
+        resbound = v1.intbound.mul_bound(v2.intbound)
+        if resbound.has_lower and resbound.has_upper and \
+               self.loop.operations[self.i+1].opnum == rop.GUARD_NO_OVERFLOW:
+            # Transform into INT_MUL and remove guard
+            op.opnum = rop.INT_MUL
+            self.i += 1
+            self.optimize_INT_MUL(op)
+        else:
+            self.optimize_default(op)
+            r = self.getvalue(op.result)
+            r.intbound.intersect(resbound)
+        
     def optimize_INT_LT(self, op):
         v1 = self.getvalue(op.args[0])
         v2 = self.getvalue(op.args[1])
@@ -1360,6 +1425,16 @@
         else: 
             self.optimize_default(op)
             
+    def optimize_INT_NE(self, op):
+        v1 = self.getvalue(op.args[0])
+        v2 = self.getvalue(op.args[1])
+        if v1.intbound.known_gt(v2.intbound):
+            self.make_constant_int(op.result, 1)
+        elif v1.intbound.known_lt(v2.intbound):
+            self.make_constant_int(op.result, 1)
+        else: 
+            self.optimize_default(op)
+            
     def make_int_lt(self, args):
         v1 = self.getvalue(args[0])
         v2 = self.getvalue(args[1])
@@ -1426,6 +1501,17 @@
                 if v2.intbound.intersect(v1.intbound):
                     self.propagate_bounds_backward(op.args[1])
 
+    def propagate_bounds_INT_NE(self, op):
+        r = self.getvalue(op.result)
+        if r.is_constant():
+            if r.box.same_constant(CONST_0):
+                v1 = self.getvalue(op.args[0])
+                v2 = self.getvalue(op.args[1])
+                if v1.intbound.intersect(v2.intbound):
+                    self.propagate_bounds_backward(op.args[0])
+                if v2.intbound.intersect(v1.intbound):
+                    self.propagate_bounds_backward(op.args[1])
+
     def propagate_bounds_INT_ADD(self, op):
         v1 = self.getvalue(op.args[0])
         v2 = self.getvalue(op.args[1])
@@ -1444,12 +1530,24 @@
         b = r.intbound.add_bound(v2.intbound)
         if v1.intbound.intersect(b):
             self.propagate_bounds_backward(op.args[0])        
-        # FIXME:
-        # b = r.intbound.sub_bound(v1.intbound).mul(-1)
-        # if v2.intbound.intersect(b):
+        b = r.intbound.sub_bound(v1.intbound).mul(-1)
+        if v2.intbound.intersect(b):
+            self.propagate_bounds_backward(op.args[1])            
+
+    def propagate_bounds_INT_MUL(self, op):
+        v1 = self.getvalue(op.args[0])
+        v2 = self.getvalue(op.args[1])
+        r = self.getvalue(op.result)
+        b = r.intbound.div_bound(v2.intbound)
+        if v1.intbound.intersect(b):
+            self.propagate_bounds_backward(op.args[0])    
+        b = r.intbound.div_bound(v1.intbound)
+        if v2.intbound.intersect(b):
+            self.propagate_bounds_backward(op.args[1])
 
     propagate_bounds_INT_ADD_OVF  = propagate_bounds_INT_ADD
     propagate_bounds_INT_SUB_OVF  = propagate_bounds_INT_SUB
+    propagate_bounds_INT_MUL_OVF  = propagate_bounds_INT_MUL
 
 optimize_ops = _findall(Optimizer, 'optimize_')
 propagate_bounds_ops = _findall(Optimizer, 'propagate_bounds_')
@@ -1691,3 +1789,8 @@
                                    write=True)
 
 
+def min4(t):
+    return min(min(t[0], t[1]), min(t[2], t[3]))
+
+def max4(t):
+    return max(max(t[0], t[1]), max(t[2], t[3]))

Modified: pypy/branch/jit-bounds/pypy/jit/metainterp/test/test_intbound.py
==============================================================================
--- pypy/branch/jit-bounds/pypy/jit/metainterp/test/test_intbound.py	(original)
+++ pypy/branch/jit-bounds/pypy/jit/metainterp/test/test_intbound.py	Tue Aug 24 17:52:13 2010
@@ -185,6 +185,53 @@
     assert not a.contains(2)
     assert not a.contains(7)
 
+def test_mul_bound():
+    for _, _, b1 in some_bounds():
+        for _, _, b2 in some_bounds():
+            b3 = b1.mul_bound(b2)
+            for n1 in nbr:
+                for n2 in nbr:
+                    if b1.contains(n1) and b2.contains(n2):
+                        assert b3.contains(n1 * n2)
+
+    a=bound(2, 4).mul_bound(bound(1, 2))
+    assert not a.contains(1)
+    assert not a.contains(9)
+
+    a=bound(-3, 2).mul_bound(bound(1, 2))
+    assert not a.contains(-7)
+    assert not a.contains(5)
+    assert a.contains(-6)
+    assert a.contains(4)
+
+    a=bound(-3, 2).mul(-1)
+    for i in range(-2,4):
+        assert a.contains(i)
+    assert not a.contains(4)
+    assert not a.contains(-3)
+
+def test_div_bound():
+    for _, _, b1 in some_bounds():
+        for _, _, b2 in some_bounds():
+            b3 = b1.div_bound(b2)
+            for n1 in nbr:
+                for n2 in nbr:
+                    if b1.contains(n1) and b2.contains(n2):
+                        if n2 != 0:
+                            assert b3.contains(n1 / n2)
+
+    a=bound(2, 4).div_bound(bound(1, 2))
+    assert not a.contains(0)
+    assert not a.contains(5)
+
+    a=bound(-3, 2).div_bound(bound(1, 2))
+    assert not a.contains(-4)
+    assert not a.contains(3)
+    assert a.contains(-3)
+    assert a.contains(0)
+
+
+
 def test_sub_bound():
     for _, _, b1 in some_bounds():
         for _, _, b2 in some_bounds():

Modified: pypy/branch/jit-bounds/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/branch/jit-bounds/pypy/jit/metainterp/test/test_optimizeopt.py	(original)
+++ pypy/branch/jit-bounds/pypy/jit/metainterp/test/test_optimizeopt.py	Tue Aug 24 17:52:13 2010
@@ -3548,10 +3548,7 @@
         guard_no_overflow() []
         i2 = int_gt(i1, 1)
         guard_true(i2) []
-        i3 = int_sub_ovf(1, i0)
-        guard_no_overflow() []
-        i4 = int_gt(i3, 1)
-        guard_true(i4) []
+        i3 = int_sub(1, i0)
         jump(i0)
         """
         self.optimize_loop(ops, 'Not', expected)
@@ -3594,6 +3591,75 @@
         """
         self.optimize_loop(ops, 'Not', expected)
 
+    def test_bound_eq_const_not(self):
+        ops = """
+        [i0]
+        i1 = int_eq(i0, 7)
+        guard_false(i1) []
+        i2 = int_add(i0, 3)
+        jump(i2)
+        """
+        expected = """
+        [i0]
+        i1 = int_eq(i0, 7)
+        guard_false(i1) []
+        i2 = int_add(i0, 3)
+        jump(i2)
+
+        """
+        self.optimize_loop(ops, 'Not', expected)
+
+    def test_bound_ne_const(self):
+        ops = """
+        [i0]
+        i1 = int_ne(i0, 7)
+        guard_false(i1) []
+        i2 = int_add(i0, 3)
+        jump(i2)
+        """
+        expected = """
+        [i0]
+        i1 = int_ne(i0, 7)
+        guard_false(i1) []
+        jump(10)
+
+        """
+        self.optimize_loop(ops, 'Not', expected)
+
+    def test_bound_ne_const_not(self):
+        ops = """
+        [i0]
+        i1 = int_ne(i0, 7)
+        guard_true(i1) []
+        i2 = int_add(i0, 3)
+        jump(i2)
+        """
+        expected = """
+        [i0]
+        i1 = int_ne(i0, 7)
+        guard_true(i1) []
+        i2 = int_add(i0, 3)
+        jump(i2)
+        """
+        self.optimize_loop(ops, 'Not', expected)
+
+    def test_bound_ltne(self):
+        ops = """
+        [i0, i1]
+        i2 = int_lt(i0, 7)
+        guard_true(i2) []
+        i3 = int_ne(i0, 10)
+        guard_true(i2) []
+        jump(i0, i1)
+        """
+        expected = """
+        [i0, i1]
+        i2 = int_lt(i0, 7)
+        guard_true(i2) []
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+
     def test_bound_lege_const(self):
         ops = """
         [i0]
@@ -3615,6 +3681,96 @@
         """
         self.optimize_loop(ops, 'Not', expected)
 
+    def test_mul_ovf(self):
+        ops = """
+        [i0, i1]
+        i2 = int_and(i0, 255)
+        i3 = int_lt(i1, 5)
+        guard_true(i3) []
+        i4 = int_gt(i1, -10)
+        guard_true(i4) []
+        i5 = int_mul_ovf(i2, i1)
+        guard_no_overflow() []
+        i6 = int_lt(i5, -2550)
+        guard_false(i6) []
+        i7 = int_ge(i5, 1276)
+        guard_false(i7) []
+        i8 = int_gt(i5, 126)
+        guard_true(i8) []
+        jump(i0, i1)
+        """
+        expected = """
+        [i0, i1]
+        i2 = int_and(i0, 255)
+        i3 = int_lt(i1, 5)
+        guard_true(i3) []
+        i4 = int_gt(i1, -10)
+        guard_true(i4) []
+        i5 = int_mul(i2, i1)
+        i8 = int_gt(i5, 126)
+        guard_true(i8) []
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+
+    def test_mul_ovf_before(self):
+        ops = """
+        [i0, i1]
+        i2 = int_and(i0, 255)
+        i22 = int_add(i2, 1)
+        i3 = int_mul_ovf(i22, i1)
+        guard_no_overflow() []
+        i4 = int_lt(i3, 10)
+        guard_true(i4) []
+        i5 = int_gt(i3, 2)
+        guard_true(i5) []
+        i6 = int_lt(i1, 0)
+        guard_false(i6) []
+        jump(i0, i1)
+        """
+        expected = """
+        [i0, i1]
+        i2 = int_and(i0, 255)
+        i22 = int_add(i2, 1)
+        i3 = int_mul_ovf(i22, i1)
+        guard_no_overflow() []
+        i4 = int_lt(i3, 10)
+        guard_true(i4) []
+        i5 = int_gt(i3, 2)
+        guard_true(i5) []
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+
+    def test_sub_ovf_before(self):
+        ops = """
+        [i0, i1]
+        i2 = int_and(i0, 255)
+        i3 = int_sub_ovf(i2, i1)
+        guard_no_overflow() []
+        i4 = int_le(i3, 10)
+        guard_true(i4) []
+        i5 = int_ge(i3, 2)
+        guard_true(i5) []
+        i6 = int_lt(i1, -10)
+        guard_false(i6) []
+        i7 = int_gt(i1, 253)
+        guard_false(i7) []
+        jump(i0, i1)
+        """
+        expected = """
+        [i0, i1]
+        i2 = int_and(i0, 255)
+        i3 = int_sub_ovf(i2, i1)
+        guard_no_overflow() []
+        i4 = int_le(i3, 10)
+        guard_true(i4) []
+        i5 = int_ge(i3, 2)
+        guard_true(i5) []
+        jump(i0, i1)
+        """
+        self.optimize_loop(ops, 'Not, Not', expected)
+
         
 
 

Added: pypy/branch/jit-bounds/pypy/module/pypyjit/test/randomized.py
==============================================================================
--- (empty file)
+++ pypy/branch/jit-bounds/pypy/module/pypyjit/test/randomized.py	Tue Aug 24 17:52:13 2010
@@ -0,0 +1,124 @@
+#!/usr/bin/python
+
+import random, inspect, os
+
+class RandomCode(object):
+
+    maxifdepth = 10
+    maxopdepth = 20
+
+    def __init__(self):
+        self.vars = set()
+
+    def sample(self, population):
+        return random.sample(population, 1)[0]
+
+    def chose(self, *args):
+        return self.sample(args)()
+
+    def expression(self):
+        if len(self.vars) == 0:
+            return self.constant()
+        elif self.depth() > self.maxopdepth:
+            return self.chose(self.variable, self.constant)
+        else:
+            return self.chose(self.variable, self.opperation, self.constant)
+        
+    def variable(self):
+        return self.sample(self.vars)
+
+    def opperation(self):
+        return self.expression() + ' ' + self.sample(self.opperators) + \
+               ' ' + self.expression()
+
+    def test(self):
+        return self.expression() + ' ' + self.sample(self.tests) + \
+               ' ' + self.expression()
+
+    def constant(self):
+        return str(self.sample(self.constants))
+
+    def depth(self):
+        return len(inspect.getouterframes(inspect.currentframe()))
+        
+    def statement(self):
+        if self.depth() > self.maxifdepth:
+            return self.assignment()
+        else:
+            return self.chose(self.assignment, self.ifstatement)
+
+    def assignment(self):
+        v = self.sample(self.varnames)
+        s = v + ' = ' + self.expression() + '\n'
+        self.vars.add(v)
+        return s
+
+    def indent(self, s):
+        lines = s.split('\n')
+        lines = ['    ' + l for l in lines[:-1]]
+        return '\n'.join(lines) + '\n'
+    
+    def ifstatement(self):
+        return 'if ' + self.test() + ':\n' + self.indent(self.block(5))
+
+    def block(self, n):
+        s = ''
+        for i in range(random.randrange(1,n)):
+            s += self.statement()
+        return s
+
+    def whileloop(self):
+        self.vars.add('i')
+        return 'i = 0\nwhile i < 10:\n' + \
+               self.indent(self.block(5) + 'i += 1\n')
+
+    def setupvars(self):
+        return ', '.join(self.vars) + ' = ' + \
+               ', '.join('0' * len(self.vars)) + '\n'
+
+    def return_statement(self):
+        return 'return (' + ', '.join(self.vars) + ')\n'
+        
+
+class IntBounds(RandomCode):
+    opperators = ('+', '-', '*')
+    tests = ('<', '>', '<=', '>=', '==', '!=')
+    constants = range(-3,4)
+    varnames = 'abcd'
+
+    def function(self, name='f'):
+        body = self.block(3) + self.whileloop() + self.return_statement()
+        body = self.setupvars() + body
+        return 'def %s():\n' % name + self.indent(body)
+
+
+def run(python, code):
+    (s,r,e) = os.popen3(python)
+    s.write(code)
+    s.close()
+    res = r.read()
+    err = e.read()
+    r.close()
+    return res, err
+
+while True:
+    code = '''
+try: # make the file runnable by CPython
+    import pypyjit
+    pypyjit.set_param(threshold=3)
+except ImportError:
+    pass
+
+%s
+print f()
+''' % IntBounds().function('f')
+
+    r1,e1 = run('/usr/bin/python', code)
+    r2,e2 = run('../../../translator/goal/pypy-c', code)
+    if r1 != r2:
+        print
+        print '******************** FAILED ******************'
+        print code
+        print 'cpython: ', r1, e1
+        print 'pypy: ', r2, e2
+

Modified: pypy/branch/jit-bounds/pypy/module/pypyjit/test/test_pypy_c.py
==============================================================================
--- pypy/branch/jit-bounds/pypy/module/pypyjit/test/test_pypy_c.py	(original)
+++ pypy/branch/jit-bounds/pypy/module/pypyjit/test/test_pypy_c.py	Tue Aug 24 17:52:13 2010
@@ -849,7 +849,7 @@
             ''', maxops, ([tc], res))
 
     def test_intbound_simple(self):
-        ops = ('<', '>', '<=', '>=')
+        ops = ('<', '>', '<=', '>=', '==', '!=')
         nbr = (3, 7)
         for o1 in ops:
             for o2 in ops:
@@ -870,7 +870,10 @@
 
                         def main():
                             res = [0] * 4
-                            for i in range(15) * 1500:
+                            idx = []
+                            for i in range(15):
+                                idx.extend([i] * 1500)
+                            for i in idx:
                                 res[f(i)] += 1
                             return res
 
@@ -880,12 +883,12 @@
                         res = [0] * 4
                         for i in range(15):
                             res[f(i)] += 1500
-                        self.run_source(src, 220, ([], res))
+                        self.run_source(src, 268, ([], res))
 
     def test_intbound_addsub_mix(self):
         tests = ('i > 4', 'i > 2', 'i + 1 > 2', '1 + i > 4',
                  'i - 1 > 1', '1 - i > 1', '1 - i < -3',
-                 'i == 1', 'i == 5')
+                 'i == 1', 'i == 5', 'i != 1', '-2 * i < -4')
         for t1 in tests:
             for t2 in tests:
                 print t1, t2
@@ -904,7 +907,10 @@
 
                 def main():
                     res = [0] * 4
-                    for i in range(15) * 1500:
+                    idx = []
+                    for i in range(15):
+                        idx.extend([i] * 1500)
+                    for i in idx:
                         res[f(i)] += 1
                     return res
 
@@ -914,7 +920,7 @@
                 res = [0] * 4
                 for i in range(15):
                     res[f(i)] += 1500
-                self.run_source(src, 232, ([], res))
+                self.run_source(src, 280, ([], res))
 
     def test_intbound_gt(self):
         self.run_source('''
@@ -953,6 +959,19 @@
             return (a, b)
         ''', 56, ([], (2000, 2000)))
 
+    def test_intbound_addmul_ge(self):
+        self.run_source('''
+        def main():
+            i, a, b = 0, 0, 0
+            while i < 2000:
+                if i + 5 >= 5:
+                    a += 1
+                if 2 * i >= 0:
+                    b += 1
+                i += 1
+            return (a, b)
+        ''', 53, ([], (2000, 2000)))
+
     def test_intbound_eq(self):
         self.run_source('''
         def main(a):
@@ -968,6 +987,20 @@
             return s
         ''', 69, ([7], 12000), ([42], 1509), ([10], 1509))
         
+    def test_intbound_mul(self):
+        self.run_source('''
+        def main(a):
+            i, s = 0, 0
+            while i < 1500:
+                assert i >= 0
+                if 2 * i < 30000:
+                    s += 1
+                else:
+                    s += a
+                i += 1
+            return s
+        ''', 43, ([7], 1500))
+        
     def test_assert(self):
         self.run_source('''
         def main(a):
@@ -1012,6 +1045,7 @@
                 self = array.__new__(cls, 'd', range(256))
                 return self
             def __getitem__(self, i):
+                # assert self.__len__() == 256 (FIXME: does not improve)
                 return array.__getitem__(self, i & 255)
 
         def main():



More information about the Pypy-commit mailing list