Author: Antonio Cuni <[email protected]>
Branch: autoreds
Changeset: r58954:c953de9d810f
Date: 2012-11-16 15:41 +0100
http://bitbucket.org/pypy/pypy/changeset/c953de9d810f/
Log: completely change the strategy for inlining jit_merge_points in the
caller. The old one did not work in complex cases involving raising
graphs. In particular, this case did not work:
def bar(): "something which cannot be inlined and raises"
@inline_in_portal def foo(): driver.jit_merge_point() return
bar()
def fn(): try: foo(): except StopIteration:
pass
that's because that the backendopt inliner is not able to inline
calls to raising graphs inside a try/except block.
To work around the issue, we put the actual driver.jit_merge_point
in a separate function, which needs to be called as soon as we enter
foo(). Then, we move *only* this call from foo() to fn(), and
finally inline the jit_merge_point in fn().
Next step is to provide a nice decorator to do everythin
automatically
diff --git a/pypy/jit/metainterp/test/test_warmspot.py
b/pypy/jit/metainterp/test/test_warmspot.py
--- a/pypy/jit/metainterp/test/test_warmspot.py
+++ b/pypy/jit/metainterp/test/test_warmspot.py
@@ -383,7 +383,38 @@
assert res == expected
self.check_resops(int_sub=2, int_mul=0, int_add=2)
+ def test_inline_jit_merge_point(self):
+ # test that the machinery to inline jit_merge_points in callers
+ # works. The final user does not need to mess manually with the
+ # _inline_jit_merge_point_ attribute and similar, it is all nicely
+ # handled by @JitDriver.inline()
+ myjitdriver = JitDriver(greens = ['a'], reds = 'auto')
+
+ def jit_merge_point(a, b):
+ myjitdriver.jit_merge_point(a=a)
+
+ def add(a, b):
+ jit_merge_point(a, b)
+ return a+b
+ add._inline_jit_merge_point_ = jit_merge_point
+ myjitdriver.inline_jit_merge_point = True
+
+ def calc(n):
+ res = 0
+ while res < 1000:
+ res = add(n, res)
+ return res
+
+ def f():
+ return calc(1) + calc(3)
+
+ res = self.meta_interp(f, [])
+ assert res == 1000 + 1002
+ self.check_resops(int_add=4)
+
+
def test_inline_in_portal(self):
+ py.test.skip('in-progress')
myjitdriver = JitDriver(greens = [], reds = 'auto')
class MyRange(object):
def __init__(self, n):
@@ -423,30 +454,6 @@
self.check_resops(int_eq=4, int_add=8)
self.check_trace_count(2)
- def test_inline_in_portal_exception(self):
- myjitdriver = JitDriver(greens = [], reds = 'auto')
- def inc(n):
- if n == 1000:
- raise OverflowError
- return n+1
-
- @myjitdriver.inline_in_portal
- def jitted_inc(n):
- myjitdriver.jit_merge_point()
- return inc(n)
-
- def f():
- res = 0
- while True:
- try:
- res = jitted_inc(res)
- except OverflowError:
- break
- return res
- res = self.meta_interp(f, [])
- assert res == 1000
- self.check_resops(int_add=2)
-
class TestLLWarmspot(WarmspotTests, LLJitMixin):
CPUClass = runner.LLtypeCPU
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
@@ -244,28 +244,53 @@
def inline_inlineable_portals(self):
"""
- Find all the graphs which have been decorated with
- @jitdriver.inline_in_portal and inline them in the callers, making
- them JIT portals. Then, create a fresh copy of the jitdriver for each
- of those new portals, because they cannot share the same one. See
- test_ajit::test_inline_in_portal.
+ Find all the graphs which have been decorated with @jitdriver.inline
+ and inline them in the callers, making them JIT portals. Then, create
+ a fresh copy of the jitdriver for each of those new portals, because
+ they cannot share the same one. See
+ test_ajit::test_inline_jit_merge_point
"""
- from pypy.translator.backendopt import inline
+ from pypy.translator.backendopt.inline import (
+ get_funcobj, inlinable_static_callers, auto_inlining)
# find all the graphs which call an @inline_in_portal function
- callgraph = inline.inlinable_static_callers(self.translator.graphs)
+ callgraph = inlinable_static_callers(self.translator.graphs,
store_calls=True)
new_callgraph = []
new_portals = set()
- for caller, callee in callgraph:
+ for caller, block, op_call, callee in callgraph:
func = getattr(callee, 'func', None)
- _inline_in_portal_ = getattr(func, '_inline_in_portal_', False)
- if _inline_in_portal_:
- new_callgraph.append((caller, callee))
+ _inline_jit_merge_point_ = getattr(func,
'_inline_jit_merge_point_', None)
+ if _inline_jit_merge_point_:
+ # we are calling a function which has been decorated with
+ # @jitdriver.inline: the very first op of the callee graph is
+ # a call to the function which contains the actual
+ # jit_merge_point: fish it!
+ jmp_block, op_jmp_call = next(callee.iterblockops())
+ msg = ("The first operation of an _inline_jit_merge_point_
graph must be "
+ "a direct_call to the function passed to
@jitdriver.inline()")
+ assert op_jmp_call.opname == 'direct_call', msg
+ jmp_funcobj = get_funcobj(op_jmp_call.args[0].value)
+ assert jmp_funcobj._callable is _inline_jit_merge_point_, msg
+ #
+ # now we move the op_jmp_call from callee to caller, just
+ # before op_call. We assume that the args passed to
+ # op_jmp_call are the very same which are received by callee
+ # (i.e., the one passed to op_call)
+ assert len(op_call.args) == len(op_jmp_call.args)
+ jmp_block.operations.remove(op_jmp_call)
+ op_jmp_call.args[1:] = op_call.args[1:]
+ idx = block.operations.index(op_call)
+ block.operations.insert(idx, op_jmp_call)
+ #
+ # finally, we signal that we want to inline op_jmp_call into
+ # caller, so that finally the actuall call to
+ # driver.jit_merge_point will be seen there
+ new_callgraph.append((caller, jmp_funcobj.graph))
new_portals.add(caller)
# inline them!
inline_threshold =
self.translator.config.translation.backendopt.inline_threshold
- inline.auto_inlining(self.translator, inline_threshold, callgraph)
+ auto_inlining(self.translator, inline_threshold, new_callgraph)
# make a fresh copy of the JitDriver in all newly created
# jit_merge_points
@@ -282,7 +307,7 @@
op = block.operations[pos]
v_driver = op.args[1]
driver = v_driver.value
- if not driver.inlined_in_portal:
+ if not driver.inline_jit_merge_point:
continue
new_driver = driver.clone()
c_new_driver = Constant(new_driver, v_driver.concretetype)
@@ -327,6 +352,7 @@
alive_v.add(op1.result)
greens_v = op.args[2:]
reds_v = alive_v - set(greens_v)
+ reds_v = [v for v in reds_v if v.concretetype is not
lltype.Void]
reds_v = support.sort_vars(reds_v)
op.args.extend(reds_v)
if jitdriver.numreds is None:
diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py
--- a/pypy/rlib/jit.py
+++ b/pypy/rlib/jit.py
@@ -559,7 +559,7 @@
return func
def clone(self):
- assert self.inlined_in_portal, 'JitDriver.clone works only after
@inline_in_portal'
+ assert self.inline_jit_merge_point, 'JitDriver.clone works only after
@inline'
newdriver = object.__new__(self.__class__)
newdriver.__dict__ = self.__dict__.copy()
return newdriver
diff --git a/pypy/translator/backendopt/inline.py
b/pypy/translator/backendopt/inline.py
--- a/pypy/translator/backendopt/inline.py
+++ b/pypy/translator/backendopt/inline.py
@@ -614,9 +614,15 @@
return (0.9999 * measure_median_execution_cost(graph) +
count), True
-def inlinable_static_callers(graphs):
+def inlinable_static_callers(graphs, store_calls=False):
ok_to_call = set(graphs)
result = []
+ def add(parentgraph, block, op, graph):
+ if store_calls:
+ result.append((parentgraph, block, op, graph))
+ else:
+ result.append((parentgraph, graph))
+ #
for parentgraph in graphs:
for block in parentgraph.iterblocks():
for op in block.operations:
@@ -627,12 +633,12 @@
if getattr(getattr(funcobj, '_callable', None),
'_dont_inline_', False):
continue
- result.append((parentgraph, graph))
+ add(parentgraph, block, op, graph)
if op.opname == "oosend":
meth = get_meth_from_oosend(op)
graph = getattr(meth, 'graph', None)
if graph is not None and graph in ok_to_call:
- result.append((parentgraph, graph))
+ add(parentgraph, block, op, graph)
return result
def instrument_inline_candidates(graphs, threshold):
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit