Author: Armin Rigo <[email protected]>
Branch:
Changeset: r92345:f70ed7c5cc49
Date: 2017-09-07 22:54 +0200
http://bitbucket.org/pypy/pypy/changeset/f70ed7c5cc49/
Log: merge heads
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1629,12 +1629,15 @@
# return text_w(w_obj) or None
return None if self.is_none(w_obj) else self.text_w(w_obj)
+ @specialize.argtype(1)
def bytes_w(self, w_obj):
""" Takes an application level :py:class:`bytes`
(on PyPy2 this equals `str`) and returns a rpython byte string.
"""
+ assert w_obj is not None
return w_obj.str_w(self)
+ @specialize.argtype(1)
def text_w(self, w_obj):
""" PyPy2 takes either a :py:class:`str` and returns a
rpython byte string, or it takes an :py:class:`unicode`
@@ -1644,6 +1647,7 @@
On PyPy3 it takes a :py:class:`str` and it will return
an utf-8 encoded rpython string.
"""
+ assert w_obj is not None
return w_obj.str_w(self)
@not_rpython # tests only; should be replaced with bytes_w or text_w
@@ -1692,6 +1696,7 @@
raise oefmt(self.w_ValueError, "byte must be in range(0, 256)")
return chr(value)
+ @specialize.argtype(1)
def int_w(self, w_obj, allow_conversion=True):
"""
Unwrap an app-level int object into an interpret-level int.
@@ -1704,26 +1709,35 @@
If allow_conversion=False, w_obj needs to be an app-level int or a
subclass.
"""
+ assert w_obj is not None
return w_obj.int_w(self, allow_conversion)
+ @specialize.argtype(1)
def int(self, w_obj):
+ assert w_obj is not None
return w_obj.int(self)
+ @specialize.argtype(1)
def uint_w(self, w_obj):
+ assert w_obj is not None
return w_obj.uint_w(self)
+ @specialize.argtype(1)
def bigint_w(self, w_obj, allow_conversion=True):
"""
Like int_w, but return a rlib.rbigint object and call __long__ if
allow_conversion is True.
"""
+ assert w_obj is not None
return w_obj.bigint_w(self, allow_conversion)
+ @specialize.argtype(1)
def float_w(self, w_obj, allow_conversion=True):
"""
Like int_w, but return an interp-level float and call __float__ if
allow_conversion is True.
"""
+ assert w_obj is not None
return w_obj.float_w(self, allow_conversion)
def realtext_w(self, w_obj):
@@ -1733,7 +1747,9 @@
raise oefmt(self.w_TypeError, "argument must be a string")
return self.bytes_w(w_obj)
+ @specialize.argtype(1)
def unicode_w(self, w_obj):
+ assert w_obj is not None
return w_obj.unicode_w(self)
def unicode0_w(self, w_obj):
@@ -1758,7 +1774,9 @@
# This is here mostly just for gateway.int_unwrapping_space_method().
return bool(self.int_w(w_obj))
+ @specialize.argtype(1)
def ord(self, w_obj):
+ assert w_obj is not None
return w_obj.ord(self)
# This is all interface for gateway.py.
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -40,6 +40,7 @@
from rpython.rlib import rawrefcount
from rpython.rlib import rthread
from rpython.rlib.debug import fatalerror_notb
+from rpython.rlib import rstackovf
from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
from pypy.module.cpyext.cparser import CTypeSpace
@@ -940,6 +941,11 @@
message = str(e)
state.set_exception(OperationError(space.w_SystemError,
space.newtext(message)))
+ except rstackovf.StackOverflow as e:
+ rstackovf.check_stack_overflow()
+ failed = True
+ state.set_exception(OperationError(space.w_RuntimeError,
+ space.newtext("maximum recursion depth exceeded")))
else:
failed = False
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
@@ -185,6 +185,27 @@
FUNC.RESULT, EffectInfo.MOST_GENERAL)
return (fnaddr, calldescr)
+ def _raise_effect_error(self, op, extraeffect, functype, calling_graph):
+ explanation = []
+ if extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
+ explanation =
self.randomeffects_analyzer.explain_analyze_slowly(op)
+ elif extraeffect == EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE:
+ explanation =
self.virtualizable_analyzer.explain_analyze_slowly(op)
+ msg = []
+ if explanation:
+ msg = [
+ "_______ ERROR AT BOTTOM ______",
+ "RPython callstack leading to problem:",
+ ]
+ msg.extend(explanation)
+ msg.append("_______ ERROR: ______")
+ msg.append("operation %r" % op)
+ msg.append("in graph %s" % (calling_graph or "<unknown>"))
+ msg.append("this calls a %s function," % (functype, ))
+ msg.append(" but this contradicts other sources (e.g. it can have
random"
+ " effects): EF=%s" % (extraeffect, ))
+ raise Exception("\n".join(msg))
+
def getcalldescr(self, op, oopspecindex=EffectInfo.OS_NONE,
extraeffect=None, extradescr=None,
calling_graph=None):
@@ -278,18 +299,13 @@
# check that the result is really as expected
if loopinvariant:
if extraeffect != EffectInfo.EF_LOOPINVARIANT:
- raise Exception(
- "operation %r in %s: this calls a _jit_loop_invariant_
function,"
- " but this contradicts other sources (e.g. it can have random"
- " effects): EF=%s" % (op, calling_graph, extraeffect))
+ self._raise_effect_error(op, extraeffect,
"_jit_loop_invariant_", calling_graph)
if elidable:
if extraeffect not in (EffectInfo.EF_ELIDABLE_CANNOT_RAISE,
EffectInfo.EF_ELIDABLE_OR_MEMORYERROR,
EffectInfo.EF_ELIDABLE_CAN_RAISE):
- raise Exception(
- "operation %r in %s: this calls an elidable function,"
- " but this contradicts other sources (e.g. it can have random"
- " effects): EF=%s" % (op, calling_graph, extraeffect))
+
+ self._raise_effect_error(op, extraeffect, "elidable",
calling_graph)
elif RESULT is lltype.Void:
raise Exception(
"operation %r in %s: this calls an elidable function "
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
@@ -299,11 +299,23 @@
def f4(n, m):
return compute_hash(str(n) + str(m))
+ T = rffi.CArrayPtr(rffi.TIME_T)
+ external = rffi.llexternal("time", [T], rffi.TIME_T, releasegil=True)
+
+ def effect():
+ return external(lltype.nullptr(T.TO))
+
+ @jit.elidable
+ def f5(n, m):
+ effect()
+ return 1
+
def f(n, m):
a = f1(n, m)
b = f2(n, m)
c = f3(n, m)
d = f4(n, m)
+ f5(n, m)
enable_siphash24()
return a + len(b) + c + d
@@ -323,6 +335,14 @@
call_descr = cc.getcalldescr(call_op)
assert call_descr.extrainfo.extraeffect == expected
+ call_op = f_graph.startblock.operations[4]
+ assert call_op.opname == 'direct_call'
+ excinfo = py.test.raises(Exception, cc.getcalldescr, call_op)
+ lines = excinfo.value.args[0].splitlines()
+ assert "f5" in lines[2]
+ assert "effect" in lines[3]
+ assert "random effects" in lines[-1]
+
def test_raise_elidable_no_result():
from rpython.jit.backend.llgraph.runner import LLGraphCPU
l = []
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
@@ -4,6 +4,7 @@
class GraphAnalyzer(object):
verbose = False
+ explanation = None
def __init__(self, translator):
self.translator = translator
@@ -73,6 +74,20 @@
def compute_graph_info(self, graph):
return None
+ def explain_analyze_slowly(self, op):
+ # this is a hack! usually done before a crash
+ self.__init__(self.translator)
+ self.explanation = explanation = []
+ oldverbose = self.verbose
+ self.verbose = True
+ try:
+ self.analyze(op)
+ finally:
+ del self.explanation
+ self.verbose = oldverbose
+ explanation.reverse()
+ return explanation
+
def analyze(self, op, seen=None, graphinfo=None):
if op.opname == "direct_call":
try:
@@ -113,7 +128,11 @@
return x
def dump_info(self, info):
- print '[%s] %s' % (self.__class__.__name__, info)
+ st = '[%s] %s' % (self.__class__.__name__, info)
+ if self.explanation is not None:
+ self.explanation.append(st)
+ else:
+ print st
def analyze_direct_call(self, graph, seen=None):
if seen is None:
diff --git a/rpython/translator/backendopt/test/test_writeanalyze.py
b/rpython/translator/backendopt/test/test_writeanalyze.py
--- a/rpython/translator/backendopt/test/test_writeanalyze.py
+++ b/rpython/translator/backendopt/test/test_writeanalyze.py
@@ -531,3 +531,37 @@
typed_effects = self._analyze_graph(t, wa, typed_write)
typed_effects = self._filter_reads(typed_effects)
assert typed_effects == direct_effects
+
+ def test_explanation(self):
+ class A(object):
+ def methodname(self):
+ self.x = 1
+ return 1
+ def m(self):
+ raise ValueError
+ class B(A):
+ def methodname(self):
+ return 2
+ def m(self):
+ return 3
+ def fancyname(a):
+ return a.methodname()
+ def m(a):
+ return a.m()
+ def h(flag):
+ if flag:
+ obj = A()
+ else:
+ obj = B()
+ fancyname(obj)
+ m(obj)
+
+ t, wa = self.translate(h, [int])
+ hgraph = graphof(t, h)
+ # fiiiish :-(
+ block = hgraph.startblock.exits[0].target.exits[0].target
+ op_call_fancyname = block.operations[0]
+
+ explanation = wa.explain_analyze_slowly(op_call_fancyname)
+ assert "fancyname" in explanation[0]
+ assert "methodname" in explanation[1]
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit