Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r76743:3c8a7c3382b5
Date: 2015-04-07 20:55 +0200
http://bitbucket.org/pypy/pypy/changeset/3c8a7c3382b5/

Log:    merge heads

diff --git a/rpython/jit/codewriter/call.py b/rpython/jit/codewriter/call.py
--- a/rpython/jit/codewriter/call.py
+++ b/rpython/jit/codewriter/call.py
@@ -31,6 +31,8 @@
             self.rtyper = cpu.rtyper
             translator = self.rtyper.annotator.translator
             self.raise_analyzer = RaiseAnalyzer(translator)
+            self.raise_analyzer_ignore_memoryerror = RaiseAnalyzer(translator)
+            self.raise_analyzer_ignore_memoryerror.do_ignore_memory_error()
             self.readwrite_analyzer = ReadWriteAnalyzer(translator)
             self.virtualizable_analyzer = VirtualizableAnalyzer(translator)
             self.quasiimmut_analyzer = QuasiImmutAnalyzer(translator)
@@ -260,11 +262,14 @@
             elif loopinvariant:
                 extraeffect = EffectInfo.EF_LOOPINVARIANT
             elif elidable:
-                if self._canraise(op):
+                cr = self._canraise(op)
+                if cr == "mem":
+                    extraeffect = EffectInfo.EF_ELIDABLE_OR_MEMORYERROR
+                elif cr:
                     extraeffect = EffectInfo.EF_ELIDABLE_CAN_RAISE
                 else:
                     extraeffect = EffectInfo.EF_ELIDABLE_CANNOT_RAISE
-            elif self._canraise(op):
+            elif self._canraise(op):   # True or "mem"
                 extraeffect = EffectInfo.EF_CAN_RAISE
             else:
                 extraeffect = EffectInfo.EF_CANNOT_RAISE
@@ -278,6 +283,7 @@
                 " effects): EF=%s" % (op, extraeffect))
         if elidable:
             if extraeffect not in (EffectInfo.EF_ELIDABLE_CANNOT_RAISE,
+                                   EffectInfo.EF_ELIDABLE_OR_MEMORYERROR,
                                    EffectInfo.EF_ELIDABLE_CAN_RAISE):
                 raise Exception(
                 "in operation %r: this calls an _elidable_function_,"
@@ -301,10 +307,17 @@
                                     effectinfo)
 
     def _canraise(self, op):
+        """Returns True, False, or "mem" to mean 'only MemoryError'."""
         if op.opname == 'pseudo_call_cannot_raise':
             return False
         try:
-            return self.raise_analyzer.can_raise(op)
+            if self.raise_analyzer.can_raise(op):
+                if self.raise_analyzer_ignore_memoryerror.can_raise(op):
+                    return True
+                else:
+                    return "mem"
+            else:
+                return False
         except lltype.DelayedPointer:
             return True  # if we need to look into the delayed ptr that is
                          # the portal, then it's certainly going to raise
diff --git a/rpython/jit/codewriter/effectinfo.py 
b/rpython/jit/codewriter/effectinfo.py
--- a/rpython/jit/codewriter/effectinfo.py
+++ b/rpython/jit/codewriter/effectinfo.py
@@ -11,10 +11,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 #elidable, can only raise 
MemoryError
+    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
 
     # the 'oopspecindex' field is one of the following values:
     OS_NONE                     = 0    # normal case, no oopspec
@@ -143,6 +144,7 @@
         result.readonly_descrs_interiorfields = readonly_descrs_interiorfields
         if extraeffect == EffectInfo.EF_LOOPINVARIANT or \
            extraeffect == EffectInfo.EF_ELIDABLE_CANNOT_RAISE or \
+           extraeffect == EffectInfo.EF_ELIDABLE_OR_MEMORYERROR or \
            extraeffect == EffectInfo.EF_ELIDABLE_CAN_RAISE:
             # Ignore the writes.  Note that this ignores also writes with
             # no corresponding reads (rarely the case, but possible).
@@ -158,19 +160,23 @@
         result.oopspecindex = oopspecindex
         result.extradescrs = extradescrs
         result.call_release_gil_target = call_release_gil_target
-        if result.check_can_raise():
+        if result.check_can_raise(ignore_memoryerror=True):
             assert oopspecindex in cls._OS_CANRAISE
         cls._cache[key] = result
         return result
 
-    def check_can_raise(self):
-        return self.extraeffect > self.EF_CANNOT_RAISE
+    def check_can_raise(self, ignore_memoryerror=False):
+        if ignore_memoryerror:
+            return self.extraeffect > self.EF_ELIDABLE_OR_MEMORYERROR
+        else:
+            return self.extraeffect > self.EF_CANNOT_RAISE
 
     def check_can_invalidate(self):
         return self.can_invalidate
 
     def check_is_elidable(self):
         return (self.extraeffect == self.EF_ELIDABLE_CAN_RAISE or
+                self.extraeffect == self.EF_ELIDABLE_OR_MEMORYERROR or
                 self.extraeffect == self.EF_ELIDABLE_CANNOT_RAISE)
 
     def check_forces_virtual_or_virtualizable(self):
diff --git a/rpython/jit/codewriter/jtransform.py 
b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -1847,6 +1847,13 @@
 
     def _handle_stroruni_call(self, op, oopspec_name, args):
         SoU = args[0].concretetype     # Ptr(STR) or Ptr(UNICODE)
+        can_raise_memoryerror = {
+                    "stroruni.concat": True,
+                    "stroruni.slice":  True,
+                    "stroruni.equal":  False,
+                    "stroruni.cmp":    False,
+                    "stroruni.copy_string_to_raw": False,
+                    }
         if SoU.TO == rstr.STR:
             dict = {"stroruni.concat": EffectInfo.OS_STR_CONCAT,
                     "stroruni.slice":  EffectInfo.OS_STR_SLICE,
@@ -1913,8 +1920,11 @@
                                             argtypes, resulttype,
                                            EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
         #
-        return self._handle_oopspec_call(op, args, dict[oopspec_name],
-                                         EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
+        if can_raise_memoryerror[oopspec_name]:
+            extra = EffectInfo.EF_ELIDABLE_OR_MEMORYERROR
+        else:
+            extra = EffectInfo.EF_ELIDABLE_CANNOT_RAISE
+        return self._handle_oopspec_call(op, args, dict[oopspec_name], extra)
 
     def _handle_str2unicode_call(self, op, oopspec_name, args):
         # ll_str2unicode can raise UnicodeDecodeError
diff --git a/rpython/jit/codewriter/test/test_call.py 
b/rpython/jit/codewriter/test/test_call.py
--- a/rpython/jit/codewriter/test/test_call.py
+++ b/rpython/jit/codewriter/test/test_call.py
@@ -6,6 +6,7 @@
 from rpython.rlib import jit
 from rpython.jit.codewriter import support, call
 from rpython.jit.codewriter.call import CallControl
+from rpython.jit.codewriter.effectinfo import EffectInfo
 
 
 class FakePolicy:
@@ -279,3 +280,39 @@
     call_descr = cc.getcalldescr(op)
     assert not call_descr.extrainfo.has_random_effects()
     assert call_descr.extrainfo.check_is_elidable()
+
+def test_elidable_kinds():
+    from rpython.jit.backend.llgraph.runner import LLGraphCPU
+
+    @jit.elidable
+    def f1(n, m):
+        return n + m
+    @jit.elidable
+    def f2(n, m):
+        return [n, m]    # may raise MemoryError
+    @jit.elidable
+    def f3(n, m):
+        if n > m:
+            raise ValueError
+        return n + m
+
+    def f(n, m):
+        a = f1(n, m)
+        b = f2(n, m)
+        c = f3(n, m)
+        return a + len(b) + c
+
+    rtyper = support.annotate(f, [7, 9])
+    jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
+    cc = CallControl(LLGraphCPU(rtyper), jitdrivers_sd=[jitdriver_sd])
+    res = cc.find_all_graphs(FakePolicy())
+    [f_graph] = [x for x in res if x.func is f]
+
+    for index, expected in [
+            (0, EffectInfo.EF_ELIDABLE_CANNOT_RAISE),
+            (1, EffectInfo.EF_ELIDABLE_OR_MEMORYERROR),
+            (2, EffectInfo.EF_ELIDABLE_CAN_RAISE)]:
+        call_op = f_graph.startblock.operations[index]
+        assert call_op.opname == 'direct_call'
+        call_descr = cc.getcalldescr(call_op)
+        assert call_descr.extrainfo.extraeffect == expected
diff --git a/rpython/jit/codewriter/test/test_jtransform.py 
b/rpython/jit/codewriter/test/test_jtransform.py
--- a/rpython/jit/codewriter/test/test_jtransform.py
+++ b/rpython/jit/codewriter/test/test_jtransform.py
@@ -161,6 +161,9 @@
                 assert extraeffect == EI.EF_CANNOT_RAISE
             elif oopspecindex == EI.OS_THREADLOCALREF_GET:
                 assert extraeffect == 
self.expected_effect_of_threadlocalref_get
+            elif oopspecindex in (EI.OS_STR_CONCAT, EI.OS_UNI_CONCAT,
+                                  EI.OS_STR_SLICE, EI.OS_UNI_SLICE):
+                assert extraeffect == EI.EF_ELIDABLE_OR_MEMORYERROR
             else:
                 assert extraeffect == EI.EF_ELIDABLE_CANNOT_RAISE
         return 'calldescr-%d' % oopspecindex
diff --git a/rpython/translator/backendopt/canraise.py 
b/rpython/translator/backendopt/canraise.py
--- a/rpython/translator/backendopt/canraise.py
+++ b/rpython/translator/backendopt/canraise.py
@@ -9,9 +9,15 @@
 
 
 class RaiseAnalyzer(graphanalyze.BoolGraphAnalyzer):
+    ignore_exact_class = None
+
+    def do_ignore_memory_error(self):
+        self.ignore_exact_class = MemoryError
+
     def analyze_simple_operation(self, op, graphinfo):
         try:
-            return bool(LL_OPERATIONS[op.opname].canraise)
+            canraise = LL_OPERATIONS[op.opname].canraise
+            return bool(canraise) and canraise != (self.ignore_exact_class,)
         except KeyError:
             log.WARNING("Unknown operation: %s" % op.opname)
             return True
@@ -20,7 +26,22 @@
         fnobj = op.args[0].value._obj
         return getattr(fnobj, 'canraise', True)
 
-    def analyze_exceptblock(self, block, seen=None):
+    analyze_exceptblock = None    # don't call this
+
+    def analyze_exceptblock_in_graph(self, graph, block, seen=None):
+        if self.ignore_exact_class is not None:
+            from rpython.translator.backendopt.ssa import DataFlowFamilyBuilder
+            dff = DataFlowFamilyBuilder(graph)
+            variable_families = dff.get_variable_families()
+            v_exc_instance = variable_families.find_rep(block.inputargs[1])
+            for link1 in graph.iterlinks():
+                v = link1.last_exc_value
+                if v is not None:
+                    if variable_families.find_rep(v) is v_exc_instance:
+                        # this is a case of re-raise the exception caught;
+                        # it doesn't count.  We'll see the place that really
+                        # raises the exception in the first place.
+                        return False
         return True
 
     # backward compatible interface
diff --git a/rpython/translator/backendopt/graphanalyze.py 
b/rpython/translator/backendopt/graphanalyze.py
--- a/rpython/translator/backendopt/graphanalyze.py
+++ b/rpython/translator/backendopt/graphanalyze.py
@@ -49,6 +49,9 @@
     def analyze_exceptblock(self, block, seen=None):
         return self.bottom_result()
 
+    def analyze_exceptblock_in_graph(self, graph, block, seen=None):
+        return self.analyze_exceptblock(block, seen)
+
     def analyze_startblock(self, block, seen=None):
         return self.bottom_result()
 
@@ -121,7 +124,7 @@
             elif block is graph.exceptblock:
                 result = self.add_to_result(
                     result,
-                    self.analyze_exceptblock(block, seen)
+                    self.analyze_exceptblock_in_graph(graph, block, seen)
                 )
             if not self.is_top_result(result):
                 for op in block.operations:
diff --git a/rpython/translator/backendopt/test/test_canraise.py 
b/rpython/translator/backendopt/test/test_canraise.py
--- a/rpython/translator/backendopt/test/test_canraise.py
+++ b/rpython/translator/backendopt/test/test_canraise.py
@@ -225,3 +225,32 @@
         fgraph = graphof(t, f)
         result = ra.can_raise(fgraph.startblock.operations[0])
         assert not result
+
+    def test_memoryerror(self):
+        def f(x):
+            return [x, 42]
+        t, ra = self.translate(f, [int])
+        result = ra.analyze_direct_call(graphof(t, f))
+        assert result
+        #
+        ra = RaiseAnalyzer(t)
+        ra.do_ignore_memory_error()
+        result = ra.analyze_direct_call(graphof(t, f))
+        assert not result
+        #
+        def g(x):
+            try:
+                return f(x)
+            except:
+                raise
+        t, ra = self.translate(g, [int])
+        ra.do_ignore_memory_error()
+        result = ra.analyze_direct_call(graphof(t, g))
+        assert not result
+        #
+        def h(x):
+            return {5:6}[x]
+        t, ra = self.translate(h, [int])
+        ra.do_ignore_memory_error()     # but it's potentially a KeyError
+        result = ra.analyze_direct_call(graphof(t, h))
+        assert result
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to