Author: Armin Rigo <[email protected]>
Branch: conditional_call_value_4
Changeset: r88580:be014558712e
Date: 2016-11-23 15:54 +0100
http://bitbucket.org/pypy/pypy/changeset/be014558712e/

Log:    More tests, more fixes

diff --git a/rpython/jit/metainterp/optimizeopt/pure.py 
b/rpython/jit/metainterp/optimizeopt/pure.py
--- a/rpython/jit/metainterp/optimizeopt/pure.py
+++ b/rpython/jit/metainterp/optimizeopt/pure.py
@@ -197,13 +197,17 @@
     optimize_COND_CALL_VALUE_R = optimize_COND_CALL_VALUE_I
 
     def optimize_call_pure_old(self, op, old_op, start_index):
-        if (op.numargs() != old_op.numargs() or
-            op.getdescr() is not old_op.getdescr()):
+        if op.getdescr() is not old_op.getdescr():
             return False
-        for i in range(start_index, old_op.numargs()):
+        # this will match a call_pure and a cond_call_value with
+        # the same function and arguments
+        j = start_index
+        old_start_index = OpHelpers.is_cond_call_value(old_op.opnum)
+        for i in range(old_start_index, old_op.numargs()):
             box = old_op.getarg(i)
-            if not self.get_box_replacement(op.getarg(i)).same_box(box):
+            if not self.get_box_replacement(op.getarg(j)).same_box(box):
                 break
+            j += 1
         else:
             # all identical
             # this removes a CALL_PURE that has the same (non-constant)
@@ -260,10 +264,17 @@
             # don't move call_pure_with_exception in the short preamble...
             # issue #2015
 
+            # Also, don't move cond_call_value in the short preamble.
+            # The issue there is that it's usually pointless to try to
+            # because the 'value' argument is typically not a loop
+            # invariant, and would really need to be in order to end up
+            # in the short preamble.  Maybe the code works anyway in the
+            # other rare case, but better safe than sorry and don't try.
             effectinfo = op.getdescr().get_extra_info()
             if not effectinfo.check_can_raise(ignore_memoryerror=True):
                 assert rop.is_call(op.opnum)
-                sb.add_pure_op(op)
+                if not OpHelpers.is_cond_call_value(op.opnum):
+                    sb.add_pure_op(op)
 
 dispatch_opt = make_dispatcher_method(OptPure, 'optimize_',
                                       default=OptPure.optimize_default)
diff --git a/rpython/jit/metainterp/test/test_call.py 
b/rpython/jit/metainterp/test/test_call.py
--- a/rpython/jit/metainterp/test/test_call.py
+++ b/rpython/jit/metainterp/test/test_call.py
@@ -115,6 +115,29 @@
         self.check_resops({'int_sub': 2, 'int_gt': 2, 'guard_true': 2,
                            'jump': 1})
 
+    def test_cond_call_constant_in_optimizer_1(self):
+        # same as test_cond_call_constant_in_optimizer, but the 'value'
+        # argument changes
+        myjitdriver = jit.JitDriver(greens = ['m'], reds = ['n', 'p'])
+        def externfn(x):
+            return x - 3
+        class V:
+            def __init__(self, value):
+                self.value = value
+        def f(n, m, p):
+            while n > 0:
+                myjitdriver.can_enter_jit(n=n, p=p, m=m)
+                myjitdriver.jit_merge_point(n=n, p=p, m=m)
+                m1 = noConst(m)
+                n -= jit.conditional_call_elidable(p, externfn, m1)
+            return n
+        assert f(21, 5, 0) == -1
+        res = self.meta_interp(f, [21, 5, 0])
+        assert res == -1
+        # the COND_CALL_VALUE is constant-folded away by optimizeopt.py
+        self.check_resops({'int_sub': 2, 'int_gt': 2, 'guard_true': 2,
+                           'jump': 1})
+
     def test_cond_call_constant_in_optimizer_2(self):
         myjitdriver = jit.JitDriver(greens = ['m'], reds = ['n', 'p'])
         def externfn(x):
@@ -148,12 +171,12 @@
                 n -= jit.conditional_call_elidable(p, externfn, n0)
                 n -= jit.conditional_call_elidable(p, externfn, n0)
             return n
-        res = self.meta_interp(f, [21, 5, 15])
+        res = self.meta_interp(f, [21, 5, 0])
         assert res == -1
         # same as test_cond_call_constant_in_optimizer_2, but the two
         # intermediate CALL_PUREs are replaced with only one, because
         # they are called with the same arguments
-        self.check_resops(call_pure_i=0, cond_call_pure_i=0, call_i=2,
+        self.check_resops(call_pure_i=0, cond_call_value_i=0, call_i=2,
                           int_sub=4)
 
     def test_cond_call_constant_in_optimizer_4(self):
@@ -167,13 +190,71 @@
             def get_triple(self):
                 return jit.conditional_call_elidable(self.triple,
                                                   X._compute_triple, self)
+
+        myjitdriver = jit.JitDriver(greens = [], reds = 'auto')
         def main(n):
-            x = X(n)
-            return x.get_triple() + x.get_triple()
+            total = 0
+            while n > 1:
+                myjitdriver.jit_merge_point()
+                x = X(n)
+                total += x.get_triple() + x.get_triple() + x.get_triple()
+                n -= 10
+            return total
 
-        assert self.interp_operations(main, [100]) == 600
-        XXX
-        self.check_operations_history(finish=1)   # empty history
+        res = self.meta_interp(main, [100])
+        assert res == main(100)
+        # remaining: only the first call to get_triple(), as a call_i
+        # because we know that x.triple == 0 here.  The remaining calls
+        # are removed because equal to the first one.
+        self.check_resops(call_i=2, cond_call_value_i=0)
+
+    def test_cond_call_multiple_in_optimizer_1(self):
+        # test called several times with the same arguments, but
+        # the condition is not available to the short preamble.
+        # This means that the second cond_call_value after unrolling
+        # can't be removed.
+        myjitdriver = jit.JitDriver(greens = [], reds = ['n', 'p', 'm'])
+        def externfn(x):
+            return 2000      # never actually called
+        @jit.dont_look_inside
+        def randomish(p):
+            return p + 1
+        def f(n, m, p):
+            while n > 0:
+                myjitdriver.can_enter_jit(n=n, p=p, m=m)
+                myjitdriver.jit_merge_point(n=n, p=p, m=m)
+                n -= jit.conditional_call_elidable(randomish(p), externfn, m)
+            return n
+        assert f(21, 5, 1) == -1
+        res = self.meta_interp(f, [21, 5, 1])
+        assert res == -1
+        self.check_resops(call_pure_i=0, cond_call_value_i=2,
+                          call_i=2,    # randomish()
+                          int_sub=2)
+
+    def test_cond_call_multiple_in_optimizer_2(self):
+        # test called several times with the same arguments.  Ideally
+        # we would like them to be consolidated into one call even if
+        # the 'value' are different but available from the short
+        # preamble.  We don't do it so far---it's a mess, because the
+        # short preamble is supposed to depend only on loop-invariant
+        # things, and 'value' is (most of the time) not loop-invariant.
+        myjitdriver = jit.JitDriver(greens = [], reds = ['n', 'p', 'm'])
+        def externfn(x):
+            return 2      # called only the first time
+        def f(n, m, p):
+            while n > 0:
+                myjitdriver.can_enter_jit(n=n, p=p, m=m)
+                myjitdriver.jit_merge_point(n=n, p=p, m=m)
+                p = jit.conditional_call_elidable(p, externfn, m)
+                n -= p
+            return n
+        assert f(219, 5, 0) == -1
+        res = self.meta_interp(f, [219, 5, 0])
+        assert res == -1
+        self.check_resops(call_pure_i=0,
+                          cond_call_value_i=2,   # ideally 1, but see above
+                          int_sub=2)
 
 
 class TestCall(LLJitMixin, CallTest):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to