[pypy-commit] pypy default: The EF_ELIDABLE_OR_MEMORYERROR functions can also be moved out of loops.

arigo noreply at buildbot.pypy.org
Tue Apr 7 22:17:42 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r76744:cdbda6639c3b
Date: 2015-04-07 22:17 +0200
http://bitbucket.org/pypy/pypy/changeset/cdbda6639c3b/

Log:	The EF_ELIDABLE_OR_MEMORYERROR functions can also be moved out of
	loops. It differs from the EF_ELIDABLE_CANNOT_RAISE functions
	because they still need the guard_no_exception afterward to check
	for MemoryError; and it differs from the EF_ELIDABLE_CAN_RAISE
	functions because these ones should still not be removed from loops,
	as 56eebe9dd813.

diff --git a/rpython/jit/metainterp/heapcache.py b/rpython/jit/metainterp/heapcache.py
--- a/rpython/jit/metainterp/heapcache.py
+++ b/rpython/jit/metainterp/heapcache.py
@@ -191,6 +191,7 @@
             ef = effectinfo.extraeffect
             if (ef == effectinfo.EF_LOOPINVARIANT or
                 ef == effectinfo.EF_ELIDABLE_CANNOT_RAISE or
+                ef == effectinfo.EF_ELIDABLE_OR_MEMORYERROR or
                 ef == effectinfo.EF_ELIDABLE_CAN_RAISE):
                 return
             # A special case for ll_arraycopy, because it is so common, and its
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
@@ -84,7 +84,7 @@
         # don't move call_pure_with_exception in the short preamble...
         # issue #2015
         effectinfo = op.getdescr().get_extra_info()
-        if not effectinfo.check_can_raise():
+        if not effectinfo.check_can_raise(ignore_memoryerror=True):
             self.call_pure_positions.append(
                 len(self.optimizer._newoperations) - 1)
 
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
@@ -3625,6 +3625,63 @@
         '''
         self.optimize_loop(ops, expected, preamble)
 
+    def test_call_pure_invalidates_caches_2(self):
+        # same as test_call_pure_invalidates_caches, but with
+        # an EF_ELIDABLE_OR_MEMORYERROR, which can still be moved
+        # out of the main loop potentially into the short preamble
+        ops = '''
+        [p1, i1, i4]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i3 = call_pure(p1, descr=elidable2calldescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        jump(p1, i4, i3)
+        '''
+        expected = '''
+        [p1, i4, i3, i5]
+        setfield_gc(p1, i5, descr=valuedescr)
+        jump(p1, i3, i5, i5)
+        '''
+        preamble = '''
+        [p1, i1, i4]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i3 = call(p1, descr=elidable2calldescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        i148 = same_as(i3)
+        i147 = same_as(i3)
+        jump(p1, i4, i3, i148)
+        '''
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_call_pure_can_raise(self):
+        # same as test_call_pure_invalidates_caches, but this time
+        # with an EF_ELIDABLE_CAN_RAISE, which *cannot* be moved
+        # out of the main loop: the problem is that it cannot be
+        # present in the short preamble, because nobody would catch
+        # the potential exception
+        ops = '''
+        [p1, i1, i4]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i3 = call_pure(p1, descr=elidable3calldescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        jump(p1, i4, i3)
+        '''
+        expected = '''
+        [p1, i1, i4]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i3 = call(p1, descr=elidable3calldescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        jump(p1, i4, i3)
+        '''
+        preamble = '''
+        [p1, i1, i4]
+        setfield_gc(p1, i1, descr=valuedescr)
+        i3 = call(p1, descr=elidable3calldescr)
+        setfield_gc(p1, i3, descr=valuedescr)
+        i167 = same_as(i3)
+        jump(p1, i4, i3)
+        '''
+        self.optimize_loop(ops, expected, preamble)
+
     def test_call_pure_invalidates_heap_knowledge(self):
         # CALL_PURE should still force the setfield_gc() to occur before it
         ops = '''
@@ -3677,25 +3734,31 @@
 
     def test_call_pure_constant_folding_exc(self):
         # CALL_PURE may be followed by GUARD_NO_EXCEPTION
-        # XXX maybe temporary, but we can't remove such call_pures from
-        # the loop, because the short preamble can't call them safely.
+        # we can't remove such call_pures from the loop,
+        # because the short preamble can't call them safely.
+        # We can still check that an all-constant call_pure is removed,
+        # and that duplicate call_pures are folded.
         arg_consts = [ConstInt(i) for i in (123456, 4, 5, 6)]
         call_pure_results = {tuple(arg_consts): ConstInt(42)}
         ops = '''
-        [i0, i1, i2]
+        [i0, i1, i2, i9]
         escape(i1)
         escape(i2)
-        i3 = call_pure(123456, 4, 5, 6, descr=plaincalldescr)
+        escape(i9)
+        i3 = call_pure(123456, 4, 5, 6, descr=elidable3calldescr)
         guard_no_exception() []
-        i4 = call_pure(123456, 4, i0, 6, descr=plaincalldescr)
+        i4 = call_pure(123456, 4, i0, 6, descr=elidable3calldescr)
         guard_no_exception() []
-        jump(i0, i3, i4)
+        i5 = call_pure(123456, 4, i0, 6, descr=elidable3calldescr)
+        guard_no_exception() []
+        jump(i0, i3, i4, i5)
         '''
         preamble = '''
-        [i0, i1, i2]
+        [i0, i1, i2, i9]
         escape(i1)
         escape(i2)
-        i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
+        escape(i9)
+        i4 = call(123456, 4, i0, 6, descr=elidable3calldescr)
         guard_no_exception() []
         jump(i0, i4)
         '''
@@ -3703,7 +3766,8 @@
         [i0, i2]
         escape(42)
         escape(i2)
-        i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
+        escape(i2)
+        i4 = call(123456, 4, i0, 6, descr=elidable3calldescr)
         guard_no_exception() []
         jump(i0, i4)
         '''
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
@@ -186,6 +186,14 @@
                                     EffectInfo([valuedescr], [], [],
                                                [valuedescr], [], [],
                                          EffectInfo.EF_ELIDABLE_CANNOT_RAISE))
+    elidable2calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                                    EffectInfo([valuedescr], [], [],
+                                               [valuedescr], [], [],
+                                         EffectInfo.EF_ELIDABLE_OR_MEMORYERROR))
+    elidable3calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                                    EffectInfo([valuedescr], [], [],
+                                               [valuedescr], [], [],
+                                         EffectInfo.EF_ELIDABLE_CAN_RAISE))
     nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
                                     EffectInfo([], [], [], [], [], []))
     writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
@@ -259,20 +267,24 @@
         ('streq_checknull_char_descr',   'OS_STREQ_CHECKNULL_CHAR'),
         ('streq_lengthok_descr',         'OS_STREQ_LENGTHOK'),
         ]:
+        if _name in ('strconcatdescr', 'strslicedescr'):
+            _extra = EffectInfo.EF_ELIDABLE_OR_MEMORYERROR
+        else:
+            _extra = EffectInfo.EF_ELIDABLE_CANNOT_RAISE
         _oopspecindex = getattr(EffectInfo, _os)
         locals()[_name] = \
             cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+                EffectInfo([], [], [], [], [], [], _extra,
                            oopspecindex=_oopspecindex))
         #
         _oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
         locals()[_name.replace('str', 'unicode')] = \
             cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-                EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+                EffectInfo([], [], [], [], [], [], _extra,
                            oopspecindex=_oopspecindex))
 
     s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
-            EffectInfo([], [], [], [], [], [],
+            EffectInfo([], [], [], [], [], [], EffectInfo.EF_ELIDABLE_CAN_RAISE,
                        oopspecindex=EffectInfo.OS_STR2UNICODE))
     #
 
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -438,7 +438,21 @@
         if op.is_ovf():
             guard = ResOperation(rop.GUARD_NO_OVERFLOW, [], None)
             optimizer.send_extra_operation(guard)
-        assert not op.is_call_pure_with_exception()
+        if self.is_call_pure_with_exception(op):    # only for MemoryError
+            guard = ResOperation(rop.GUARD_NO_EXCEPTION, [], None)
+            optimizer.send_extra_operation(guard)
+
+    def is_call_pure_with_exception(self, op):
+        if op.getopnum() == rop.CALL_PURE:
+            effectinfo = op.getdescr().get_extra_info()
+            # Assert that only EF_ELIDABLE_CANNOT_RAISE or
+            # EF_ELIDABLE_OR_MEMORYERROR end up here, not
+            # for example EF_ELIDABLE_CAN_RAISE.
+            assert effectinfo.extraeffect in (
+                effectinfo.EF_ELIDABLE_CANNOT_RAISE,
+                effectinfo.EF_ELIDABLE_OR_MEMORYERROR)
+            return effectinfo.extraeffect != effectinfo.EF_ELIDABLE_CANNOT_RAISE
+        return False
 
     def add_op_to_short(self, op, emit=True, guards_needed=False):
         if op is None:
@@ -472,7 +486,9 @@
             # FIXME: ensure that GUARD_OVERFLOW:ed ops not end up here
             guard = ResOperation(rop.GUARD_NO_OVERFLOW, [], None)
             self.add_op_to_short(guard, emit, guards_needed)
-        assert not op.is_call_pure_with_exception()
+        if self.is_call_pure_with_exception(op):    # only for MemoryError
+            guard = ResOperation(rop.GUARD_NO_EXCEPTION, [], None)
+            self.add_op_to_short(guard, emit, guards_needed)
         for guard in value_guards:
             self.add_op_to_short(guard, emit, guards_needed)
 
diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -176,12 +176,6 @@
     def returns_bool_result(self):
         return self._cls_has_bool_result
 
-    def is_call_pure_with_exception(self):
-        if self.getopnum() == rop.CALL_PURE:
-            effectinfo = self.getdescr().get_extra_info()
-            return effectinfo.check_can_raise()
-        return False
-
 
 # ===================
 # Top of the hierachy
diff --git a/rpython/jit/metainterp/test/test_heapcache.py b/rpython/jit/metainterp/test/test_heapcache.py
--- a/rpython/jit/metainterp/test/test_heapcache.py
+++ b/rpython/jit/metainterp/test/test_heapcache.py
@@ -22,10 +22,11 @@
     EF_ELIDABLE_CANNOT_RAISE           = 0 #elidable function (and cannot raise)
     EF_LOOPINVARIANT                   = 1 #special: call it only once per loop
     EF_CANNOT_RAISE                    = 2 #a function which cannot raise
-    EF_ELIDABLE_CAN_RAISE              = 3 #elidable function (but can raise)
-    EF_CAN_RAISE                       = 4 #normal function (can raise)
-    EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE = 5 #can raise and force virtualizables
-    EF_RANDOM_EFFECTS                  = 6 #can do whatever
+    EF_ELIDABLE_OR_MEMORYERROR         = 3
+    EF_ELIDABLE_CAN_RAISE              = 4 #elidable function (but can raise)
+    EF_CAN_RAISE                       = 5 #normal function (can raise)
+    EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE = 6 #can raise and force virtualizables
+    EF_RANDOM_EFFECTS                  = 7 #can do whatever
 
     OS_ARRAYCOPY = 0
 
@@ -600,7 +601,8 @@
             extraeffect = 5
             EF_LOOPINVARIANT = 1
             EF_ELIDABLE_CANNOT_RAISE = 2
-            EF_ELIDABLE_CAN_RAISE = 3
+            EF_ELIDABLE_OR_MEMORYERROR = 3
+            EF_ELIDABLE_CAN_RAISE = 4
         descr.get_extra_info = XTra
         h.invalidate_caches(rop.CALL, descr, [])
         assert h.is_unescaped(box1)
diff --git a/rpython/jit/metainterp/test/test_string.py b/rpython/jit/metainterp/test/test_string.py
--- a/rpython/jit/metainterp/test/test_string.py
+++ b/rpython/jit/metainterp/test/test_string.py
@@ -897,9 +897,11 @@
                 m -= 1
             return 42
         self.meta_interp(f, [6, 7])
-        # xxx used to be 'call=4', but the two extra calls in the loop
-        # are not safe to remove; see 56eebe9dd813
-        self.check_resops(unicodesetitem=2, newunicode=2, call=6,
+        # used to be 'call=4', but the call to ll_str2unicode in the
+        # loop is not safe to remove; see 56eebe9dd813.  We can still
+        # remove the call to ll_str, because that cannot raise anything
+        # else than MemoryError.
+        self.check_resops(unicodesetitem=2, newunicode=2, call=5,
                           copyunicodecontent=2, unicodegetitem=0)
 
     def test_str2unicode_fold(self):


More information about the pypy-commit mailing list