[pypy-commit] pypy default: merged upstream.
alex_gaynor
noreply at buildbot.pypy.org
Thu Jul 28 18:07:11 CEST 2011
Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch:
Changeset: r46053:25e36db72459
Date: 2011-07-28 09:07 -0700
http://bitbucket.org/pypy/pypy/changeset/25e36db72459/
Log: merged upstream.
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/support.py b/pypy/jit/codewriter/support.py
--- a/pypy/jit/codewriter/support.py
+++ b/pypy/jit/codewriter/support.py
@@ -20,6 +20,7 @@
from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
from pypy.jit.metainterp.typesystem import deref
from pypy.rlib import rgc
+from pypy.rlib.jit import elidable
from pypy.rlib.rarithmetic import r_longlong, r_ulonglong, r_uint, intmask
def getargtypes(annotator, values):
@@ -167,9 +168,14 @@
_ll_5_list_ll_arraycopy = rgc.ll_arraycopy
+ at elidable
def _ll_1_gc_identityhash(x):
return lltype.identityhash(x)
+# the following function should not be "@elidable": I can think of
+# a corner case in which id(const) is constant-folded, and then 'const'
+# disappears and is collected too early (possibly causing another object
+# with the same id() to appear).
def _ll_1_gc_id(ptr):
return llop.gc_id(lltype.Signed, ptr)
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/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py
--- a/pypy/jit/metainterp/optimizeopt/__init__.py
+++ b/pypy/jit/metainterp/optimizeopt/__init__.py
@@ -55,7 +55,7 @@
def optimize_loop_1(metainterp_sd, loop, enable_opts,
- inline_short_preamble=True, retraced=False):
+ inline_short_preamble=True, retraced=False, bridge=False):
"""Optimize loop.operations to remove internal overheadish operations.
"""
@@ -64,7 +64,7 @@
if unroll:
optimize_unroll(metainterp_sd, loop, optimizations)
else:
- optimizer = Optimizer(metainterp_sd, loop, optimizations)
+ optimizer = Optimizer(metainterp_sd, loop, optimizations, bridge)
optimizer.propagate_all_forward()
def optimize_bridge_1(metainterp_sd, bridge, enable_opts,
@@ -76,7 +76,7 @@
except KeyError:
pass
optimize_loop_1(metainterp_sd, bridge, enable_opts,
- inline_short_preamble, retraced)
+ inline_short_preamble, retraced, bridge=True)
if __name__ == '__main__':
print ALL_OPTS_NAMES
diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -248,10 +248,11 @@
class Optimizer(Optimization):
- def __init__(self, metainterp_sd, loop, optimizations=None):
+ def __init__(self, metainterp_sd, loop, optimizations=None, bridge=False):
self.metainterp_sd = metainterp_sd
self.cpu = metainterp_sd.cpu
self.loop = loop
+ self.bridge = bridge
self.values = {}
self.interned_refs = self.cpu.ts.new_ref_dict()
self.resumedata_memo = resume.ResumeDataLoopMemo(metainterp_sd)
@@ -407,9 +408,7 @@
return CVAL_ZERO
def propagate_all_forward(self):
- self.exception_might_have_happened = True
- # ^^^ at least at the start of bridges. For loops, we could set
- # it to False, but we probably don't care
+ self.exception_might_have_happened = self.bridge
self.newoperations = []
self.first_optimization.propagate_begin_forward()
self.i = 0
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -693,7 +693,6 @@
"""
expected = """
[i]
- guard_no_exception() []
i1 = int_add(i, 3)
i2 = call(i1, descr=nonwritedescr)
guard_no_exception() [i1, i2]
diff --git a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -889,12 +889,10 @@
i3 = call(i2, descr=nonwritedescr)
jump(i1) # the exception is considered lost when we loop back
"""
- # note that 'guard_no_exception' at the very start must be kept
- # around: bridges may start with one. (In case of loops we could
- # remove it, but we probably don't care.)
+ # note that 'guard_no_exception' at the very start is kept around
+ # for bridges, but not for loops
preamble = """
[i]
- guard_no_exception() []
i1 = int_add(i, 3)
i2 = call(i1, descr=nonwritedescr)
guard_no_exception() [i1, i2]
@@ -2993,6 +2991,38 @@
'''
self.optimize_loop(ops, expected, preamble, call_pure_results)
+ def test_call_pure_constant_folding_exc(self):
+ # CALL_PURE may be followed by GUARD_NO_EXCEPTION
+ arg_consts = [ConstInt(i) for i in (123456, 4, 5, 6)]
+ call_pure_results = {tuple(arg_consts): ConstInt(42)}
+ ops = '''
+ [i0, i1, i2]
+ escape(i1)
+ escape(i2)
+ i3 = call_pure(123456, 4, 5, 6, descr=plaincalldescr)
+ guard_no_exception() []
+ i4 = call_pure(123456, 4, i0, 6, descr=plaincalldescr)
+ guard_no_exception() []
+ jump(i0, i3, i4)
+ '''
+ preamble = '''
+ [i0, i1, i2]
+ escape(i1)
+ escape(i2)
+ i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
+ guard_no_exception() []
+ jump(i0, i4)
+ '''
+ expected = '''
+ [i0, i2]
+ escape(42)
+ escape(i2)
+ i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
+ guard_no_exception() []
+ jump(i0, i4)
+ '''
+ self.optimize_loop(ops, expected, preamble, call_pure_results)
+
# ----------
def test_vref_nonvirtual_nonescape(self):
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:
@@ -407,6 +407,58 @@
# the CALL_PURE is constant-folded away by optimizeopt.py
self.check_loops(int_sub=1, call=0, call_pure=0, getfield_gc=0)
+ def test_elidable_raising(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(m)
+ except ValueError:
+ n -= 1
+ 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)
+ #
+ 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_constant_across_mp(self):
myjitdriver = JitDriver(greens = [], reds = ['n'])
class X(object):
diff --git a/pypy/jit/metainterp/test/test_dict.py b/pypy/jit/metainterp/test/test_dict.py
--- a/pypy/jit/metainterp/test/test_dict.py
+++ b/pypy/jit/metainterp/test/test_dict.py
@@ -157,7 +157,7 @@
# the same arguments are not folded, because we have conflicting
# definitions of pure, once strhash can be appropriately folded
# this should be decreased to seven.
- self.check_loops({"call": 8, "guard_false": 1, "guard_no_exception": 5,
+ self.check_loops({"call": 8, "guard_false": 1, "guard_no_exception": 6,
"guard_true": 1, "int_and": 1, "int_gt": 1,
"int_is_true": 1, "int_sub": 1, "jump": 1,
"new_with_vtable": 1, "setfield_gc": 1})
diff --git a/pypy/jit/metainterp/test/test_string.py b/pypy/jit/metainterp/test/test_string.py
--- a/pypy/jit/metainterp/test/test_string.py
+++ b/pypy/jit/metainterp/test/test_string.py
@@ -358,3 +358,22 @@
self.check_loops(call=3, # str(), _str(), escape()
newunicode=1, unicodegetitem=0,
unicodesetitem=1, copyunicodecontent=1)
+
+ def test_str2unicode_fold(self):
+ _str = self._str
+ jitdriver = JitDriver(greens = ['g'], reds = ['m'])
+ @dont_look_inside
+ def escape(x):
+ print str(x)
+ def f(g, m):
+ g = str(g)
+ while m >= 0:
+ jitdriver.can_enter_jit(g=g, m=m)
+ jitdriver.jit_merge_point(g=g, m=m)
+ escape(_str(g))
+ m -= 1
+ return 42
+ self.meta_interp(f, [6, 7])
+ self.check_loops(call_pure=0, call=1,
+ newunicode=0, unicodegetitem=0,
+ unicodesetitem=0, copyunicodecontent=0)
diff --git a/pypy/jit/metainterp/warmspot.py b/pypy/jit/metainterp/warmspot.py
--- a/pypy/jit/metainterp/warmspot.py
+++ b/pypy/jit/metainterp/warmspot.py
@@ -409,6 +409,7 @@
jd.warmstate = state
def crash_in_jit(e):
+ tb = not we_are_translated() and sys.exc_info()[2]
try:
raise e
except JitException:
@@ -422,8 +423,8 @@
print "~~~ Crash in JIT!"
print '~~~ %s: %s' % (e.__class__, e)
if sys.stdout == sys.__stdout__:
- import pdb; pdb.post_mortem(sys.exc_info()[2])
- raise
+ import pdb; pdb.post_mortem(tb)
+ raise e.__class__, e, tb
fatalerror('~~~ Crash in JIT! %s' % (e,), traceback=True)
crash_in_jit._dont_inline_ = True
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -20,7 +20,8 @@
Most importantly it doesn't mean that an elidable function has no observable
side effect, but those side effects are idempotent (ie caching).
- For now, such a function should never raise an exception.
+ The function can raise an exception, in which case this decorator is
+ ignored.
"""
func._elidable_function_ = True
return func
More information about the pypy-commit
mailing list