[pypy-commit] pypy unroll-if-alt: Allow elidable function call results to be reused within a loop.
alex_gaynor
noreply at buildbot.pypy.org
Sat Jul 30 13:08:31 CEST 2011
Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch: unroll-if-alt
Changeset: r46110:170db2bb70ac
Date: 2011-07-30 04:08 -0700
http://bitbucket.org/pypy/pypy/changeset/170db2bb70ac/
Log: Allow elidable function call results to be reused within a loop.
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
@@ -484,8 +484,12 @@
def make_args_key(self, op):
n = op.numargs()
- args = [None] * (n + 2)
- for i in range(n):
+ if op.getopnum() == rop.CALL_PURE:
+ start = 1
+ else:
+ start = 0
+ args = [None] * (n + 2 - start)
+ for i in range(start, n):
arg = op.getarg(i)
try:
value = self.values[arg]
@@ -493,9 +497,9 @@
pass
else:
arg = value.get_key_box()
- args[i] = arg
- args[n] = ConstInt(op.getopnum())
- args[n+1] = op.getdescr()
+ args[i - start] = arg
+ args[n - start] = ConstInt(op.getopnum())
+ args[n + 1 - start] = op.getdescr()
return args
def optimize_default(self, op):
diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -224,6 +224,16 @@
else:
self.make_constant(op.result, result)
return
+
+ args = self.optimizer.make_args_key(op)
+ oldop = self.optimizer.pure_operations.get(args, None)
+ if oldop is not None and oldop.getdescr() is op.getdescr():
+ assert oldop.getopnum() == op.getopnum()
+ self.make_equal_to(op.result, self.getvalue(oldop.result))
+ return
+ else:
+ self.optimizer.pure_operations[args] = op
+
# replace CALL_PURE with just CALL
args = op.getarglist()
self.emit_operation(ResOperation(rop.CALL, args, op.result,
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
@@ -2932,13 +2932,17 @@
jump(p1, i4, i3)
'''
expected = '''
+ [p1, i4, i3]
+ jump(p1, i3, i3)
+ '''
+ preamble = '''
[p1, i1, i4]
setfield_gc(p1, i1, descr=valuedescr)
i3 = call(p1, descr=plaincalldescr)
setfield_gc(p1, i3, descr=valuedescr)
jump(p1, i4, i3)
'''
- self.optimize_loop(ops, expected, expected)
+ 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
@@ -2950,21 +2954,20 @@
jump(p1, i4, i3)
'''
expected = '''
+ [p1, i4, i3]
+ setfield_gc(p1, i4, descr=valuedescr)
+ jump(p1, i3, i3)
+ '''
+ preamble = '''
[p1, i1, i4]
setfield_gc(p1, i1, descr=valuedescr)
i3 = call(p1, descr=plaincalldescr)
setfield_gc(p1, i1, descr=valuedescr)
jump(p1, i4, i3)
'''
- self.optimize_loop(ops, expected, expected)
+ self.optimize_loop(ops, expected, preamble)
def test_call_pure_constant_folding(self):
- # CALL_PURE is not marked as is_always_pure(), because it is wrong
- # to call the function arbitrary many times at arbitrary points in
- # time. Check that it is either constant-folded (and replaced by
- # the result of the call, recorded as the first arg), or turned into
- # a regular CALL.
- # XXX can this test be improved with unrolling?
arg_consts = [ConstInt(i) for i in (123456, 4, 5, 6)]
call_pure_results = {tuple(arg_consts): ConstInt(42)}
ops = '''
@@ -2983,10 +2986,9 @@
jump(i0, i4)
'''
expected = '''
- [i0, i2]
+ [i0, i4]
escape(42)
- escape(i2)
- i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
+ escape(i4)
jump(i0, i4)
'''
self.optimize_loop(ops, expected, preamble, call_pure_results)
@@ -3017,9 +3019,7 @@
[i0, i2]
escape(42)
escape(i2)
- i4 = call(123456, 4, i0, 6, descr=plaincalldescr)
- guard_no_exception() []
- jump(i0, i4)
+ jump(i0, i2)
'''
self.optimize_loop(ops, expected, preamble, call_pure_results)
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
@@ -2712,6 +2712,22 @@
assert res == main(1, 10, 2)
self.check_loops(call=0)
+ def test_reuse_elidable_result(self):
+ driver = JitDriver(reds=['n', 's'], greens = [])
+ def main(n):
+ s = 0
+ while n > 0:
+ driver.jit_merge_point(s=s, n=n)
+ s += len(str(n)) + len(str(n))
+ n -= 1
+ return s
+ res = self.meta_interp(main, [10])
+ assert res == main(10)
+ self.check_loops({
+ 'call': 1, 'guard_no_exception': 1, 'guard_true': 1, 'int_add': 2,
+ 'int_gt': 1, 'int_sub': 1, 'strlen': 1, 'jump': 1,
+ })
+
class TestLLtype(BaseLLtypeTests, LLJitMixin):
pass
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
@@ -153,11 +153,7 @@
res = self.meta_interp(f, [100], listops=True)
assert res == f(50)
- # XXX: ideally there would be 7 calls here, but repeated CALL_PURE with
- # 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": 6,
+ self.check_loops({"call": 7, "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
@@ -26,7 +26,7 @@
return i
res = self.meta_interp(f, [10, True, _str('h')], listops=True)
assert res == 5
- self.check_loops(**{self.CALL: 1, self.CALL_PURE: 0})
+ self.check_loops(**{self.CALL: 1, self.CALL_PURE: 0, 'everywhere': True})
def test_eq_folded(self):
_str = self._str
@@ -355,7 +355,7 @@
m -= 1
return 42
self.meta_interp(f, [6, 7])
- self.check_loops(call=3, # str(), _str(), escape()
+ self.check_loops(call=1, # escape()
newunicode=1, unicodegetitem=0,
unicodesetitem=1, copyunicodecontent=1)
More information about the pypy-commit
mailing list