[pypy-commit] pypy default: Support elidable functions that can raise.
arigo
noreply at buildbot.pypy.org
Thu Jul 28 18:00:23 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r46046:7d31a6bafd88
Date: 2011-07-28 11:30 +0200
http://bitbucket.org/pypy/pypy/changeset/7d31a6bafd88/
Log: Support elidable functions that can raise.
diff --git a/pypy/jit/codewriter/call.py b/pypy/jit/codewriter/call.py
--- a/pypy/jit/codewriter/call.py
+++ b/pypy/jit/codewriter/call.py
@@ -228,8 +228,10 @@
elif loopinvariant:
extraeffect = EffectInfo.EF_LOOPINVARIANT
elif elidable:
- # XXX check what to do about exceptions (also MemoryError?)
- extraeffect = EffectInfo.EF_ELIDABLE
+ if self._canraise(op):
+ extraeffect = EffectInfo.EF_ELIDABLE_CAN_RAISE
+ else:
+ extraeffect = EffectInfo.EF_ELIDABLE_CANNOT_RAISE
elif self._canraise(op):
extraeffect = EffectInfo.EF_CAN_RAISE
else:
@@ -263,7 +265,7 @@
def calldescr_canraise(self, calldescr):
effectinfo = calldescr.get_extra_info()
return (effectinfo is None or
- effectinfo.extraeffect >= EffectInfo.EF_CAN_RAISE)
+ effectinfo.extraeffect > EffectInfo.EF_CANNOT_RAISE)
def jitdriver_sd_from_portal_graph(self, graph):
for jd in self.jitdrivers_sd:
diff --git a/pypy/jit/codewriter/effectinfo.py b/pypy/jit/codewriter/effectinfo.py
--- a/pypy/jit/codewriter/effectinfo.py
+++ b/pypy/jit/codewriter/effectinfo.py
@@ -9,10 +9,11 @@
_cache = {}
# the 'extraeffect' field is one of the following values:
- EF_ELIDABLE = 0 #elidable function (and cannot raise)
+ 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_CAN_RAISE = 3 #normal function (can 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
# the 'oopspecindex' field is one of the following values:
@@ -94,7 +95,8 @@
result.readonly_descrs_fields = readonly_descrs_fields
result.readonly_descrs_arrays = readonly_descrs_arrays
if extraeffect == EffectInfo.EF_LOOPINVARIANT or \
- extraeffect == EffectInfo.EF_ELIDABLE:
+ extraeffect == EffectInfo.EF_ELIDABLE_CANNOT_RAISE or \
+ extraeffect == EffectInfo.EF_ELIDABLE_CAN_RAISE:
result.write_descrs_fields = []
result.write_descrs_arrays = []
else:
diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -902,7 +902,7 @@
op1 = self.prepare_builtin_call(op, "llong_%s", args)
op2 = self._handle_oopspec_call(op1, args,
EffectInfo.OS_LLONG_%s,
- EffectInfo.EF_ELIDABLE)
+ EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
if %r == "TO_INT":
assert op2.result.concretetype == lltype.Signed
return op2
@@ -1363,15 +1363,15 @@
otherindex += EffectInfo._OS_offset_uni
self._register_extra_helper(otherindex, othername,
argtypes, resulttype,
- EffectInfo.EF_ELIDABLE)
+ EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
#
return self._handle_oopspec_call(op, args, dict[oopspec_name],
- EffectInfo.EF_ELIDABLE)
+ EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
def _handle_str2unicode_call(self, op, oopspec_name, args):
- # ll_str2unicode is not EF_ELIDABLE, because it can raise
- # UnicodeDecodeError...
- return self._handle_oopspec_call(op, args, EffectInfo.OS_STR2UNICODE)
+ # ll_str2unicode can raise UnicodeDecodeError
+ return self._handle_oopspec_call(op, args, EffectInfo.OS_STR2UNICODE,
+ EffectInfo.EF_ELIDABLE_CAN_RAISE)
# ----------
# VirtualRefs.
@@ -1415,7 +1415,7 @@
def _handle_math_sqrt_call(self, op, oopspec_name, args):
return self._handle_oopspec_call(op, args, EffectInfo.OS_MATH_SQRT,
- EffectInfo.EF_ELIDABLE)
+ EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
def rewrite_op_jit_force_quasi_immutable(self, op):
v_inst, c_fieldname = op.args
diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py
--- a/pypy/jit/codewriter/test/test_jtransform.py
+++ b/pypy/jit/codewriter/test/test_jtransform.py
@@ -120,9 +120,9 @@
assert argtypes[0] == [v.concretetype for v in op.args[1:]]
assert argtypes[1] == op.result.concretetype
if oopspecindex == EI.OS_STR2UNICODE:
- assert extraeffect == None # not pure, can raise!
+ assert extraeffect == EI.EF_ELIDABLE_CAN_RAISE
else:
- assert extraeffect == EI.EF_ELIDABLE
+ assert extraeffect == EI.EF_ELIDABLE_CANNOT_RAISE
return 'calldescr-%d' % oopspecindex
def calldescr_canraise(self, calldescr):
return False
diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py
--- a/pypy/jit/metainterp/optimizeopt/unroll.py
+++ b/pypy/jit/metainterp/optimizeopt/unroll.py
@@ -543,8 +543,10 @@
elif opnum == rop.CALL:
effectinfo = descr.get_extra_info()
if effectinfo is not None:
- if effectinfo.extraeffect == EffectInfo.EF_LOOPINVARIANT or \
- effectinfo.extraeffect == EffectInfo.EF_ELIDABLE:
+ ef = effectinfo.extraeffect
+ if ef == EffectInfo.EF_LOOPINVARIANT or \
+ ef == EffectInfo.EF_ELIDABLE_CANNOT_RAISE or \
+ ef == EffectInfo.EF_ELIDABLE_CAN_RAISE:
return True
return False
diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -1199,7 +1199,7 @@
return self.metainterp.execute_and_record(opnum, descr, *argboxes)
@specialize.arg(1)
- def execute_varargs(self, opnum, argboxes, descr, exc):
+ def execute_varargs(self, opnum, argboxes, descr, exc, pure):
self.metainterp.clear_exception()
resbox = self.metainterp.execute_and_record_varargs(opnum, argboxes,
descr=descr)
@@ -1207,6 +1207,9 @@
self.make_result_of_lastop(resbox)
# ^^^ this is done before handle_possible_exception() because we
# need the box to show up in get_list_of_active_boxes()
+ if pure and self.metainterp.last_exc_value_box is None:
+ resbox = self.metainterp.record_result_of_call_pure(resbox)
+ exc = exc and not isinstance(resbox, Const)
if exc:
self.metainterp.handle_possible_exception()
else:
@@ -1269,16 +1272,14 @@
return resbox
else:
effect = effectinfo.extraeffect
- if effect == effectinfo.EF_CANNOT_RAISE:
- return self.execute_varargs(rop.CALL, allboxes, descr, False)
- elif effect == effectinfo.EF_ELIDABLE:
- return self.metainterp.record_result_of_call_pure(
- self.execute_varargs(rop.CALL, allboxes, descr, False))
- elif effect == effectinfo.EF_LOOPINVARIANT:
+ if effect == effectinfo.EF_LOOPINVARIANT:
return self.execute_varargs(rop.CALL_LOOPINVARIANT, allboxes,
- descr, False)
- else:
- return self.execute_varargs(rop.CALL, allboxes, descr, True)
+ descr, False, False)
+ exc = (effect != effectinfo.EF_CANNOT_RAISE and
+ effect != effectinfo.EF_ELIDABLE_CANNOT_RAISE)
+ pure = (effect == effectinfo.EF_ELIDABLE_CAN_RAISE or
+ effect == effectinfo.EF_ELIDABLE_CANNOT_RAISE)
+ return self.execute_varargs(rop.CALL, allboxes, descr, exc, pure)
def do_residual_or_indirect_call(self, funcbox, calldescr, argboxes):
"""The 'residual_call' operation is emitted in two cases:
@@ -1686,8 +1687,12 @@
return
if opnum == rop.CALL:
effectinfo = descr.get_extra_info()
- if effectinfo.extraeffect == effectinfo.EF_ELIDABLE:
- return
+ if effectinfo is not None:
+ ef = effectinfo.extraeffect
+ if ef == effectinfo.EF_LOOPINVARIANT or \
+ ef == effectinfo.EF_ELIDABLE_CANNOT_RAISE or \
+ ef == effectinfo.EF_ELIDABLE_CAN_RAISE:
+ return
if self.heap_cache:
self.heap_cache.clear()
if self.heap_array_cache:
@@ -2375,6 +2380,7 @@
tobox = newbox
if change:
self.heap_cache[descr] = frombox, tobox
+ # XXX what about self.heap_array_cache?
def find_biggest_function(self):
start_stack = []
diff --git a/pypy/jit/metainterp/test/support.py b/pypy/jit/metainterp/test/support.py
--- a/pypy/jit/metainterp/test/support.py
+++ b/pypy/jit/metainterp/test/support.py
@@ -277,3 +277,15 @@
NODE._add_fields({'value': ootype.Signed,
'next': NODE})
return NODE
+
+# ____________________________________________________________
+
+class _Foo:
+ pass
+
+def noConst(x):
+ """Helper function for tests, returning 'x' as a BoxInt/BoxPtr
+ even if it is a ConstInt/ConstPtr."""
+ f1 = _Foo(); f2 = _Foo()
+ f1.x = x; f2.x = 0
+ return f1.x
diff --git a/pypy/jit/metainterp/test/test_ajit.py b/pypy/jit/metainterp/test/test_ajit.py
--- a/pypy/jit/metainterp/test/test_ajit.py
+++ b/pypy/jit/metainterp/test/test_ajit.py
@@ -14,7 +14,7 @@
from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.rpython.ootypesystem import ootype
from pypy.jit.metainterp.optimizeopt import ALL_OPTS_DICT
-from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin
+from pypy.jit.metainterp.test.support import LLJitMixin, OOJitMixin, noConst
class BasicTests:
@@ -425,6 +425,32 @@
return n
res = self.meta_interp(f, [22, 6])
assert res == -3
+ # the CALL_PURE is constant-folded away during tracing
+ self.check_loops(int_sub=1, call=0, call_pure=0)
+ #
+ res = self.meta_interp(f, [22, -5])
+ assert res == 0
+ # raises: becomes CALL and is not constant-folded away
+ self.check_loops(int_sub=1, call=1, call_pure=0)
+
+ def test_elidable_raising_2(self):
+ myjitdriver = JitDriver(greens = ['m'], reds = ['n'])
+ @elidable
+ def externfn(x):
+ if x <= 0:
+ raise ValueError
+ return x - 1
+ def f(n, m):
+ while n > 0:
+ myjitdriver.can_enter_jit(n=n, m=m)
+ myjitdriver.jit_merge_point(n=n, m=m)
+ try:
+ n -= externfn(noConst(m))
+ except ValueError:
+ n -= 1
+ return n
+ res = self.meta_interp(f, [22, 6])
+ assert res == -3
# the CALL_PURE is constant-folded away by optimizeopt.py
self.check_loops(int_sub=1, call=0, call_pure=0)
#
More information about the pypy-commit
mailing list