[pypy-commit] pypy default: The EF_ELIDABLE_OR_MEMORYERROR functions can also be moved out of loops.
arigo
noreply at buildbot.pypy.org
Tue Apr 7 22:17:42 CEST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r76744:cdbda6639c3b
Date: 2015-04-07 22:17 +0200
http://bitbucket.org/pypy/pypy/changeset/cdbda6639c3b/
Log: The EF_ELIDABLE_OR_MEMORYERROR functions can also be moved out of
loops. It differs from the EF_ELIDABLE_CANNOT_RAISE functions
because they still need the guard_no_exception afterward to check
for MemoryError; and it differs from the EF_ELIDABLE_CAN_RAISE
functions because these ones should still not be removed from loops,
as 56eebe9dd813.
diff --git a/rpython/jit/metainterp/heapcache.py b/rpython/jit/metainterp/heapcache.py
--- a/rpython/jit/metainterp/heapcache.py
+++ b/rpython/jit/metainterp/heapcache.py
@@ -191,6 +191,7 @@
ef = effectinfo.extraeffect
if (ef == effectinfo.EF_LOOPINVARIANT or
ef == effectinfo.EF_ELIDABLE_CANNOT_RAISE or
+ ef == effectinfo.EF_ELIDABLE_OR_MEMORYERROR or
ef == effectinfo.EF_ELIDABLE_CAN_RAISE):
return
# A special case for ll_arraycopy, because it is so common, and its
diff --git a/rpython/jit/metainterp/optimizeopt/pure.py b/rpython/jit/metainterp/optimizeopt/pure.py
--- a/rpython/jit/metainterp/optimizeopt/pure.py
+++ b/rpython/jit/metainterp/optimizeopt/pure.py
@@ -84,7 +84,7 @@
# don't move call_pure_with_exception in the short preamble...
# issue #2015
effectinfo = op.getdescr().get_extra_info()
- if not effectinfo.check_can_raise():
+ if not effectinfo.check_can_raise(ignore_memoryerror=True):
self.call_pure_positions.append(
len(self.optimizer._newoperations) - 1)
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -3625,6 +3625,63 @@
'''
self.optimize_loop(ops, expected, preamble)
+ def test_call_pure_invalidates_caches_2(self):
+ # same as test_call_pure_invalidates_caches, but with
+ # an EF_ELIDABLE_OR_MEMORYERROR, which can still be moved
+ # out of the main loop potentially into the short preamble
+ ops = '''
+ [p1, i1, i4]
+ setfield_gc(p1, i1, descr=valuedescr)
+ i3 = call_pure(p1, descr=elidable2calldescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ jump(p1, i4, i3)
+ '''
+ expected = '''
+ [p1, i4, i3, i5]
+ setfield_gc(p1, i5, descr=valuedescr)
+ jump(p1, i3, i5, i5)
+ '''
+ preamble = '''
+ [p1, i1, i4]
+ setfield_gc(p1, i1, descr=valuedescr)
+ i3 = call(p1, descr=elidable2calldescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ i148 = same_as(i3)
+ i147 = same_as(i3)
+ jump(p1, i4, i3, i148)
+ '''
+ self.optimize_loop(ops, expected, preamble)
+
+ def test_call_pure_can_raise(self):
+ # same as test_call_pure_invalidates_caches, but this time
+ # with an EF_ELIDABLE_CAN_RAISE, which *cannot* be moved
+ # out of the main loop: the problem is that it cannot be
+ # present in the short preamble, because nobody would catch
+ # the potential exception
+ ops = '''
+ [p1, i1, i4]
+ setfield_gc(p1, i1, descr=valuedescr)
+ i3 = call_pure(p1, descr=elidable3calldescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ jump(p1, i4, i3)
+ '''
+ expected = '''
+ [p1, i1, i4]
+ setfield_gc(p1, i1, descr=valuedescr)
+ i3 = call(p1, descr=elidable3calldescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ jump(p1, i4, i3)
+ '''
+ preamble = '''
+ [p1, i1, i4]
+ setfield_gc(p1, i1, descr=valuedescr)
+ i3 = call(p1, descr=elidable3calldescr)
+ setfield_gc(p1, i3, descr=valuedescr)
+ i167 = same_as(i3)
+ jump(p1, i4, i3)
+ '''
+ self.optimize_loop(ops, expected, preamble)
+
def test_call_pure_invalidates_heap_knowledge(self):
# CALL_PURE should still force the setfield_gc() to occur before it
ops = '''
@@ -3677,25 +3734,31 @@
def test_call_pure_constant_folding_exc(self):
# CALL_PURE may be followed by GUARD_NO_EXCEPTION
- # XXX maybe temporary, but we can't remove such call_pures from
- # the loop, because the short preamble can't call them safely.
+ # we can't remove such call_pures from the loop,
+ # because the short preamble can't call them safely.
+ # We can still check that an all-constant call_pure is removed,
+ # and that duplicate call_pures are folded.
arg_consts = [ConstInt(i) for i in (123456, 4, 5, 6)]
call_pure_results = {tuple(arg_consts): ConstInt(42)}
ops = '''
- [i0, i1, i2]
+ [i0, i1, i2, i9]
escape(i1)
escape(i2)
- i3 = call_pure(123456, 4, 5, 6, descr=plaincalldescr)
+ escape(i9)
+ i3 = call_pure(123456, 4, 5, 6, descr=elidable3calldescr)
guard_no_exception() []
- i4 = call_pure(123456, 4, i0, 6, descr=plaincalldescr)
+ i4 = call_pure(123456, 4, i0, 6, descr=elidable3calldescr)
guard_no_exception() []
- jump(i0, i3, i4)
+ i5 = call_pure(123456, 4, i0, 6, descr=elidable3calldescr)
+ guard_no_exception() []
+ jump(i0, i3, i4, i5)
'''
preamble = '''
- [i0, i1, i2]
+ [i0, i1, i2, i9]
escape(i1)
escape(i2)
- i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
+ escape(i9)
+ i4 = call(123456, 4, i0, 6, descr=elidable3calldescr)
guard_no_exception() []
jump(i0, i4)
'''
@@ -3703,7 +3766,8 @@
[i0, i2]
escape(42)
escape(i2)
- i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
+ escape(i2)
+ i4 = call(123456, 4, i0, 6, descr=elidable3calldescr)
guard_no_exception() []
jump(i0, i4)
'''
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -186,6 +186,14 @@
EffectInfo([valuedescr], [], [],
[valuedescr], [], [],
EffectInfo.EF_ELIDABLE_CANNOT_RAISE))
+ elidable2calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([valuedescr], [], [],
+ [valuedescr], [], [],
+ EffectInfo.EF_ELIDABLE_OR_MEMORYERROR))
+ elidable3calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([valuedescr], [], [],
+ [valuedescr], [], [],
+ EffectInfo.EF_ELIDABLE_CAN_RAISE))
nonwritedescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
EffectInfo([], [], [], [], [], []))
writeadescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
@@ -259,20 +267,24 @@
('streq_checknull_char_descr', 'OS_STREQ_CHECKNULL_CHAR'),
('streq_lengthok_descr', 'OS_STREQ_LENGTHOK'),
]:
+ if _name in ('strconcatdescr', 'strslicedescr'):
+ _extra = EffectInfo.EF_ELIDABLE_OR_MEMORYERROR
+ else:
+ _extra = EffectInfo.EF_ELIDABLE_CANNOT_RAISE
_oopspecindex = getattr(EffectInfo, _os)
locals()[_name] = \
cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+ EffectInfo([], [], [], [], [], [], _extra,
oopspecindex=_oopspecindex))
#
_oopspecindex = getattr(EffectInfo, _os.replace('STR', 'UNI'))
locals()[_name.replace('str', 'unicode')] = \
cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
+ EffectInfo([], [], [], [], [], [], _extra,
oopspecindex=_oopspecindex))
s2u_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
- EffectInfo([], [], [], [], [], [],
+ EffectInfo([], [], [], [], [], [], EffectInfo.EF_ELIDABLE_CAN_RAISE,
oopspecindex=EffectInfo.OS_STR2UNICODE))
#
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -438,7 +438,21 @@
if op.is_ovf():
guard = ResOperation(rop.GUARD_NO_OVERFLOW, [], None)
optimizer.send_extra_operation(guard)
- assert not op.is_call_pure_with_exception()
+ if self.is_call_pure_with_exception(op): # only for MemoryError
+ guard = ResOperation(rop.GUARD_NO_EXCEPTION, [], None)
+ optimizer.send_extra_operation(guard)
+
+ def is_call_pure_with_exception(self, op):
+ if op.getopnum() == rop.CALL_PURE:
+ effectinfo = op.getdescr().get_extra_info()
+ # Assert that only EF_ELIDABLE_CANNOT_RAISE or
+ # EF_ELIDABLE_OR_MEMORYERROR end up here, not
+ # for example EF_ELIDABLE_CAN_RAISE.
+ assert effectinfo.extraeffect in (
+ effectinfo.EF_ELIDABLE_CANNOT_RAISE,
+ effectinfo.EF_ELIDABLE_OR_MEMORYERROR)
+ return effectinfo.extraeffect != effectinfo.EF_ELIDABLE_CANNOT_RAISE
+ return False
def add_op_to_short(self, op, emit=True, guards_needed=False):
if op is None:
@@ -472,7 +486,9 @@
# FIXME: ensure that GUARD_OVERFLOW:ed ops not end up here
guard = ResOperation(rop.GUARD_NO_OVERFLOW, [], None)
self.add_op_to_short(guard, emit, guards_needed)
- assert not op.is_call_pure_with_exception()
+ if self.is_call_pure_with_exception(op): # only for MemoryError
+ guard = ResOperation(rop.GUARD_NO_EXCEPTION, [], None)
+ self.add_op_to_short(guard, emit, guards_needed)
for guard in value_guards:
self.add_op_to_short(guard, emit, guards_needed)
diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -176,12 +176,6 @@
def returns_bool_result(self):
return self._cls_has_bool_result
- def is_call_pure_with_exception(self):
- if self.getopnum() == rop.CALL_PURE:
- effectinfo = self.getdescr().get_extra_info()
- return effectinfo.check_can_raise()
- return False
-
# ===================
# Top of the hierachy
diff --git a/rpython/jit/metainterp/test/test_heapcache.py b/rpython/jit/metainterp/test/test_heapcache.py
--- a/rpython/jit/metainterp/test/test_heapcache.py
+++ b/rpython/jit/metainterp/test/test_heapcache.py
@@ -22,10 +22,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
+ 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
OS_ARRAYCOPY = 0
@@ -600,7 +601,8 @@
extraeffect = 5
EF_LOOPINVARIANT = 1
EF_ELIDABLE_CANNOT_RAISE = 2
- EF_ELIDABLE_CAN_RAISE = 3
+ EF_ELIDABLE_OR_MEMORYERROR = 3
+ EF_ELIDABLE_CAN_RAISE = 4
descr.get_extra_info = XTra
h.invalidate_caches(rop.CALL, descr, [])
assert h.is_unescaped(box1)
diff --git a/rpython/jit/metainterp/test/test_string.py b/rpython/jit/metainterp/test/test_string.py
--- a/rpython/jit/metainterp/test/test_string.py
+++ b/rpython/jit/metainterp/test/test_string.py
@@ -897,9 +897,11 @@
m -= 1
return 42
self.meta_interp(f, [6, 7])
- # xxx used to be 'call=4', but the two extra calls in the loop
- # are not safe to remove; see 56eebe9dd813
- self.check_resops(unicodesetitem=2, newunicode=2, call=6,
+ # used to be 'call=4', but the call to ll_str2unicode in the
+ # loop is not safe to remove; see 56eebe9dd813. We can still
+ # remove the call to ll_str, because that cannot raise anything
+ # else than MemoryError.
+ self.check_resops(unicodesetitem=2, newunicode=2, call=5,
copyunicodecontent=2, unicodegetitem=0)
def test_str2unicode_fold(self):
More information about the pypy-commit
mailing list