[pypy-svn] r57935 - in pypy/dist/pypy: interpreter interpreter/test rpython/memory/gctransform rpython/memory/gctransform/test translator/test
pedronis at codespeak.net
pedronis at codespeak.net
Sun Sep 7 12:31:57 CEST 2008
Author: pedronis
Date: Sun Sep 7 12:31:55 2008
New Revision: 57935
Modified:
pypy/dist/pypy/interpreter/eval.py
pypy/dist/pypy/interpreter/function.py
pypy/dist/pypy/interpreter/pycode.py
pypy/dist/pypy/interpreter/test/test_function.py
pypy/dist/pypy/rpython/memory/gctransform/framework.py
pypy/dist/pypy/rpython/memory/gctransform/test/test_framework.py
pypy/dist/pypy/translator/test/test_stackcheck.py
Log:
merging garden-call-code-2
- don't use fastcall* shortcuts for PyCode, simply have uniform code for the flat f(a,b,c,d) case. Speeds up calls to python functions with more than 4 arguments
- avoid unneeded root reloading around stack_check in non-stackless builds: speeds up quite a bit of things except for the odd one
because of our usual translation performance instabilities
Modified: pypy/dist/pypy/interpreter/eval.py
==============================================================================
--- pypy/dist/pypy/interpreter/eval.py (original)
+++ pypy/dist/pypy/interpreter/eval.py Sun Sep 7 12:31:55 2008
@@ -62,18 +62,6 @@
def funcrun_obj(self, func, w_obj, args):
return self.funcrun(func, args.prepend(w_obj))
-
- # a performance hack (see gateway.BuiltinCode1/2/3 and pycode.PyCode)
- def fastcall_0(self, space, func):
- raise NotImplementedError
- def fastcall_1(self, space, func, w1):
- raise NotImplementedError
- def fastcall_2(self, space, func, w1, w2):
- raise NotImplementedError
- def fastcall_3(self, space, func, w1, w2, w3):
- raise NotImplementedError
- def fastcall_4(self, space, func, w1, w2, w3, w4):
- raise NotImplementedError
class Frame(Wrappable):
"""A frame is an environment supporting the execution of a code object.
Modified: pypy/dist/pypy/interpreter/function.py
==============================================================================
--- pypy/dist/pypy/interpreter/function.py (original)
+++ pypy/dist/pypy/interpreter/function.py Sun Sep 7 12:31:55 2008
@@ -6,11 +6,14 @@
attribute.
"""
+from pypy.rlib.unroll import unrolling_iterable
from pypy.interpreter.error import OperationError
from pypy.interpreter.baseobjspace import Wrappable
from pypy.interpreter.eval import Code
from pypy.interpreter.argument import Arguments, ArgumentsFromValuestack
+funccallunrolling = unrolling_iterable(range(4))
+
class Function(Wrappable):
"""A function is a code object captured with some environment:
an object space, a dictionary of globals, default arguments,
@@ -44,24 +47,40 @@
return self.code
def funccall(self, *args_w): # speed hack
+ from pypy.interpreter import gateway
+ from pypy.interpreter.pycode import PyCode
+
code = self.getcode() # hook for the jit
nargs = len(args_w)
fast_natural_arity = code.fast_natural_arity
if nargs == fast_natural_arity:
if nargs == 0:
+ assert isinstance(code, gateway.BuiltinCode0)
return code.fastcall_0(self.space, self)
elif nargs == 1:
+ assert isinstance(code, gateway.BuiltinCode1)
return code.fastcall_1(self.space, self, args_w[0])
elif nargs == 2:
+ assert isinstance(code, gateway.BuiltinCode2)
return code.fastcall_2(self.space, self, args_w[0], args_w[1])
elif nargs == 3:
+ assert isinstance(code, gateway.BuiltinCode3)
return code.fastcall_3(self.space, self, args_w[0],
args_w[1], args_w[2])
elif nargs == 4:
+ assert isinstance(code, gateway.BuiltinCode4)
return code.fastcall_4(self.space, self, args_w[0],
args_w[1], args_w[2], args_w[3])
+ elif (nargs|PyCode.FLATPYCALL) == fast_natural_arity:
+ assert isinstance(code, PyCode)
+ if nargs < 5:
+ new_frame = self.space.createframe(code, self.w_func_globals,
+ self.closure)
+ for i in funccallunrolling:
+ if i < nargs:
+ new_frame.fastlocals_w[i] = args_w[i]
+ return new_frame.run()
elif nargs >= 1 and fast_natural_arity == -1:
- from pypy.interpreter import gateway
assert isinstance(code, gateway.BuiltinCodePassThroughArguments1)
return code.funcrun_obj(self, args_w[0],
Arguments(self.space,
@@ -69,25 +88,35 @@
return self.call_args(Arguments(self.space, list(args_w)))
def funccall_valuestack(self, nargs, frame): # speed hack
+ from pypy.interpreter import gateway
+ from pypy.interpreter.pycode import PyCode
+
code = self.getcode() # hook for the jit
fast_natural_arity = code.fast_natural_arity
- if nargs == fast_natural_arity:
+ if nargs == fast_natural_arity:
if nargs == 0:
+ assert isinstance(code, gateway.BuiltinCode0)
return code.fastcall_0(self.space, self)
elif nargs == 1:
+ assert isinstance(code, gateway.BuiltinCode1)
return code.fastcall_1(self.space, self, frame.peekvalue(0))
elif nargs == 2:
+ assert isinstance(code, gateway.BuiltinCode2)
return code.fastcall_2(self.space, self, frame.peekvalue(1),
frame.peekvalue(0))
elif nargs == 3:
+ assert isinstance(code, gateway.BuiltinCode3)
return code.fastcall_3(self.space, self, frame.peekvalue(2),
frame.peekvalue(1), frame.peekvalue(0))
elif nargs == 4:
+ assert isinstance(code, gateway.BuiltinCode4)
return code.fastcall_4(self.space, self, frame.peekvalue(3),
frame.peekvalue(2), frame.peekvalue(1),
frame.peekvalue(0))
+ elif (nargs|PyCode.FLATPYCALL) == fast_natural_arity:
+ assert isinstance(code, PyCode)
+ return self._flat_pycall(code, nargs, frame)
elif fast_natural_arity == -1 and nargs >= 1:
- from pypy.interpreter import gateway
assert isinstance(code, gateway.BuiltinCodePassThroughArguments1)
w_obj = frame.peekvalue(nargs-1)
args = frame.make_arguments(nargs-1)
@@ -104,6 +133,15 @@
if isinstance(args, ArgumentsFromValuestack):
args.frame = None
+ def _flat_pycall(self, code, nargs, frame):
+ # code is a PyCode
+ new_frame = self.space.createframe(code, self.w_func_globals,
+ self.closure)
+ for i in xrange(nargs):
+ w_arg = frame.peekvalue(nargs-1-i)
+ new_frame.fastlocals_w[i] = w_arg
+ return new_frame.run()
+
def getdict(self):
if self.w_func_dict is None:
self.w_func_dict = self.space.newdict()
Modified: pypy/dist/pypy/interpreter/pycode.py
==============================================================================
--- pypy/dist/pypy/interpreter/pycode.py (original)
+++ pypy/dist/pypy/interpreter/pycode.py Sun Sep 7 12:31:55 2008
@@ -108,7 +108,7 @@
self._args_as_cellvars.append(-1) # pad
self._args_as_cellvars[i] = j
- self._compute_fastcall()
+ self._compute_flatcall()
co_names = property(lambda self: [self.space.unwrap(w_name) for w_name in self.co_names_w]) # for trace
@@ -159,53 +159,20 @@
freevars, cellvars, hidden_applevel)
_code_new_w = staticmethod(_code_new_w)
+
+ FLATPYCALL = 0x100
- def _compute_fastcall(self):
+ def _compute_flatcall(self):
# Speed hack!
self.fast_natural_arity = -99
- if not (0 <= self.co_argcount <= 4):
- return
if self.co_flags & (CO_VARARGS | CO_VARKEYWORDS):
return
if len(self._args_as_cellvars) > 0:
return
-
- self.fast_natural_arity = self.co_argcount
-
- def fastcall_0(self, space, w_func):
- frame = space.createframe(self, w_func.w_func_globals,
- w_func.closure)
- return frame.run()
-
- def fastcall_1(self, space, w_func, w_arg):
- frame = space.createframe(self, w_func.w_func_globals,
- w_func.closure)
- frame.fastlocals_w[0] = w_arg # frame.setfastscope([w_arg])
- return frame.run()
-
- def fastcall_2(self, space, w_func, w_arg1, w_arg2):
- frame = space.createframe(self, w_func.w_func_globals,
- w_func.closure)
- frame.fastlocals_w[0] = w_arg1 # frame.setfastscope([w_arg])
- frame.fastlocals_w[1] = w_arg2
- return frame.run()
-
- def fastcall_3(self, space, w_func, w_arg1, w_arg2, w_arg3):
- frame = space.createframe(self, w_func.w_func_globals,
- w_func.closure)
- frame.fastlocals_w[0] = w_arg1 # frame.setfastscope([w_arg])
- frame.fastlocals_w[1] = w_arg2
- frame.fastlocals_w[2] = w_arg3
- return frame.run()
-
- def fastcall_4(self, space, w_func, w_arg1, w_arg2, w_arg3, w_arg4):
- frame = space.createframe(self, w_func.w_func_globals,
- w_func.closure)
- frame.fastlocals_w[0] = w_arg1 # frame.setfastscope([w_arg])
- frame.fastlocals_w[1] = w_arg2
- frame.fastlocals_w[2] = w_arg3
- frame.fastlocals_w[3] = w_arg4
- return frame.run()
+ if self.co_argcount > 0xff:
+ return
+
+ self.fast_natural_arity = PyCode.FLATPYCALL | self.co_argcount
def funcrun(self, func, args):
frame = self.space.createframe(self, func.w_func_globals,
Modified: pypy/dist/pypy/interpreter/test/test_function.py
==============================================================================
--- pypy/dist/pypy/interpreter/test/test_function.py (original)
+++ pypy/dist/pypy/interpreter/test/test_function.py Sun Sep 7 12:31:55 2008
@@ -457,9 +457,38 @@
w_meth5 = meth3.descr_method_get(space.wrap('hello'), space.w_str)
assert space.is_w(w_meth5, w_meth3)
-class TestShortcuts(object):
+class TestShortcuts(object):
- def test_fastcall(self):
+ def test_call_function(self):
+ space = self.space
+
+ d = {}
+ for i in range(10):
+ args = "(" + ''.join(["a%d," % a for a in range(i)]) + ")"
+ exec """
+def f%s:
+ return %s
+""" % (args, args) in d
+ f = d['f']
+ res = f(*range(i))
+ code = PyCode._from_code(self.space, f.func_code)
+ fn = Function(self.space, code, self.space.newdict())
+
+ assert fn.code.fast_natural_arity == i|PyCode.FLATPYCALL
+ if i < 5:
+
+ def bomb(*args):
+ assert False, "shortcutting should have avoided this"
+
+ code.funcrun = bomb
+ code.funcrun_obj = bomb
+
+ args_w = map(space.wrap, range(i))
+ w_res = space.call_function(fn, *args_w)
+ check = space.is_true(space.eq(w_res, space.wrap(res)))
+ assert check
+
+ def test_flatcall(self):
space = self.space
def f(a):
@@ -467,32 +496,26 @@
code = PyCode._from_code(self.space, f.func_code)
fn = Function(self.space, code, self.space.newdict())
- assert fn.code.fast_natural_arity == 1
+ assert fn.code.fast_natural_arity == 1|PyCode.FLATPYCALL
- called = []
- fastcall_1 = fn.code.fastcall_1
- def witness_fastcall_1(space, w_func, w_arg):
- called.append(w_func)
- return fastcall_1(space, w_func, w_arg)
+ def bomb(*args):
+ assert False, "shortcutting should have avoided this"
- fn.code.fastcall_1 = witness_fastcall_1
+ code.funcrun = bomb
+ code.funcrun_obj = bomb
w_3 = space.newint(3)
w_res = space.call_function(fn, w_3)
assert w_res is w_3
- assert called == [fn]
-
- called = []
w_res = space.appexec([fn, w_3], """(f, x):
return f(x)
""")
assert w_res is w_3
- assert called == [fn]
- def test_fastcall_method(self):
+ def test_flatcall_method(self):
space = self.space
def f(self, a):
@@ -500,15 +523,13 @@
code = PyCode._from_code(self.space, f.func_code)
fn = Function(self.space, code, self.space.newdict())
- assert fn.code.fast_natural_arity == 2
+ assert fn.code.fast_natural_arity == 2|PyCode.FLATPYCALL
- called = []
- fastcall_2 = fn.code.fastcall_2
- def witness_fastcall_2(space, w_func, w_arg1, w_arg2):
- called.append(w_func)
- return fastcall_2(space, w_func, w_arg1, w_arg2)
+ def bomb(*args):
+ assert False, "shortcutting should have avoided this"
- fn.code.fastcall_2 = witness_fastcall_2
+ code.funcrun = bomb
+ code.funcrun_obj = bomb
w_3 = space.newint(3)
w_res = space.appexec([fn, w_3], """(f, x):
@@ -521,7 +542,6 @@
""")
assert space.is_true(w_res)
- assert called == [fn, fn]
Modified: pypy/dist/pypy/rpython/memory/gctransform/framework.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform/framework.py (original)
+++ pypy/dist/pypy/rpython/memory/gctransform/framework.py Sun Sep 7 12:31:55 2008
@@ -7,6 +7,7 @@
from pypy.rpython.memory.gc import marksweep
from pypy.rpython.memory.gcheader import GCHeaderBuilder
from pypy.rlib.rarithmetic import ovfcheck
+from pypy.rlib import rstack
from pypy.rlib.debug import ll_assert
from pypy.translator.backendopt import graphanalyze
from pypy.translator.backendopt.support import var_needsgc
@@ -25,7 +26,10 @@
def analyze_direct_call(self, graph, seen=None):
try:
- if graph.func._gctransformer_hint_cannot_collect_:
+ func = graph.func
+ if func is rstack.stack_check:
+ return self.translator.config.translation.stackless
+ if func._gctransformer_hint_cannot_collect_:
return False
except AttributeError:
pass
Modified: pypy/dist/pypy/rpython/memory/gctransform/test/test_framework.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform/test/test_framework.py (original)
+++ pypy/dist/pypy/rpython/memory/gctransform/test/test_framework.py Sun Sep 7 12:31:55 2008
@@ -63,6 +63,28 @@
gg = graphof(t, g)
assert CollectAnalyzer(t).analyze_direct_call(gg)
+ def g(x):
+ return -x
+ t = rtype(g, [int])
+ gg = graphof(t, g)
+ assert not CollectAnalyzer(t).analyze_direct_call(gg)
+
+def test_cancollect_stack_check():
+ from pypy.rlib import rstack
+
+ def with_check():
+ rstack.stack_check()
+
+ t = rtype(with_check, [])
+ with_check_graph = graphof(t, with_check)
+
+ assert not t.config.translation.stackless
+ can_collect = CollectAnalyzer(t).analyze_direct_call(with_check_graph)
+ assert not can_collect
+
+ t.config.translation.stackless = True
+ can_collect = CollectAnalyzer(t).analyze_direct_call(with_check_graph)
+ assert can_collect
class WriteBarrierTransformer(FrameworkGCTransformer):
initializing_stores = {}
Modified: pypy/dist/pypy/translator/test/test_stackcheck.py
==============================================================================
--- pypy/dist/pypy/translator/test/test_stackcheck.py (original)
+++ pypy/dist/pypy/translator/test/test_stackcheck.py Sun Sep 7 12:31:55 2008
@@ -1,22 +1,68 @@
from pypy import conftest
-from pypy.translator.translator import TranslationContext
+from pypy.translator.translator import TranslationContext, graphof
from pypy.translator.backendopt.all import backend_optimizations
from pypy.translator.transform import insert_ll_stackcheck
+from pypy.rpython.memory.gctransform import framework
+from pypy.translator.stackless.transform import StacklessTransformer
-def test_simple():
- class A(object):
- def __init__(self, n):
- self.n = n
-
- def f(a):
- x = A(a.n+1)
- if x.n == 10:
- return
- f(x)
+def _follow_path_naive(block, cur_path, accum):
+ cur_path = (cur_path, block)
+ if not block.exits:
+ ops = []
+ while cur_path:
+ block = cur_path[1]
+ ops.extend(reversed(block.operations))
+ cur_path = cur_path[0]
+ accum.append(list(reversed(ops)))
+ return
+ for link in block.exits:
+ _follow_path_naive(link.target, cur_path, accum)
+
+# explodes on loops!
+def paths_naive(g):
+ accum = []
+ _follow_path_naive(g.startblock, None, accum)
+ return accum
- def g(n):
- f(A(n))
+def direct_target(spaceop):
+ return spaceop.args[0].value._obj.graph.name
+
+def direct_calls(p):
+ names = []
+ for spaceop in p:
+ if spaceop.opname == 'direct_call':
+ names.append(direct_target(spaceop))
+ return names
+
+
+def check(g, funcname, ignore=None):
+ paths = paths_naive(g)
+ relevant = []
+ for p in paths:
+ funcs_called = direct_calls(p)
+ if funcname in funcs_called and ignore not in funcs_called:
+ assert 'stack_check___' in funcs_called
+ assert (funcs_called.index(funcname) >
+ funcs_called.index('stack_check___'))
+ relevant.append(p)
+ return relevant
+
+class A(object):
+ def __init__(self, n):
+ self.n = n
+
+def f(a):
+ x = A(a.n+1)
+ if x.n == 10:
+ return
+ f(x)
+
+def g(n):
+ f(A(n))
+ return 0
+
+def test_simple():
t = TranslationContext()
a = t.buildannotator()
a.build_types(g, [int])
@@ -29,7 +75,98 @@
assert n == 1
if conftest.option.view:
t.view()
-
+ check(graphof(t, f), 'f')
-
-
+def test_gctransformed():
+ t = TranslationContext()
+ a = t.buildannotator()
+ a.build_types(g, [int])
+ a.simplify()
+ t.buildrtyper().specialize()
+ backend_optimizations(t)
+ t.checkgraphs()
+ n = insert_ll_stackcheck(t)
+ t.checkgraphs()
+ assert n == 1
+ exctransf = t.getexceptiontransformer()
+ f_graph = graphof(t, f)
+ exctransf.create_exception_handling(f_graph)
+ if conftest.option.view:
+ f_graph.show()
+ check(f_graph, 'f')
+
+ class GCTransform(framework.FrameworkGCTransformer):
+ from pypy.rpython.memory.gc.generation import GenerationGC as \
+ GCClass
+ GC_PARAMS = {}
+
+ gctransf = GCTransform(t)
+ gctransf.transform_graph(f_graph)
+ if conftest.option.view:
+ f_graph.show()
+ relevant = check(f_graph, 'f')
+ for p in relevant:
+ in_between = False
+ reload = 0
+ for spaceop in p:
+ if spaceop.opname == 'direct_call':
+ target = direct_target(spaceop)
+ if target == 'f':
+ in_between = False
+ elif target == 'stack_check___':
+ in_between = True
+ if in_between and spaceop.opname == 'gc_reload_possibly_moved':
+ reload += 1
+
+ assert reload == 0
+
+def test_stackless():
+ t = TranslationContext()
+ a = t.buildannotator()
+ a.build_types(g, [int])
+ a.simplify()
+ t.buildrtyper().specialize()
+ backend_optimizations(t)
+ t.checkgraphs()
+ n = insert_ll_stackcheck(t)
+ t.checkgraphs()
+ assert n == 1
+
+ t.config.translation.stackless = True
+ stacklesstransf = StacklessTransformer(t, g)
+
+ f_graph = graphof(t, f)
+ stacklesstransf.transform_graph(f_graph)
+ if conftest.option.view:
+ f_graph.show()
+
+ exctransf = t.getexceptiontransformer()
+ exctransf.create_exception_handling(f_graph)
+ if conftest.option.view:
+ f_graph.show()
+ check(f_graph, 'f', 'fetch_retval_void')
+
+ class GCTransform(framework.FrameworkGCTransformer):
+ from pypy.rpython.memory.gc.generation import GenerationGC as \
+ GCClass
+ GC_PARAMS = {}
+
+ gctransf = GCTransform(t)
+ gctransf.transform_graph(f_graph)
+ if conftest.option.view:
+ f_graph.show()
+ relevant = check(f_graph, 'f', 'fetch_retval_void')
+ for p in relevant:
+ in_between = False
+ reload = 0
+ for spaceop in p:
+ if spaceop.opname == 'direct_call':
+ target = direct_target(spaceop)
+ if target == 'f':
+ in_between = False
+ elif target == 'stack_check___':
+ in_between = True
+ if in_between and spaceop.opname == 'gc_reload_possibly_moved':
+ reload += 1
+
+ assert reload == 1
More information about the Pypy-commit
mailing list