Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit