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