[pypy-svn] r39482 - in pypy/dist: lib-python/modified-2.4.1 pypy/config pypy/interpreter pypy/interpreter/astcompiler pypy/interpreter/test
arigo at codespeak.net
arigo at codespeak.net
Tue Feb 27 09:20:54 CET 2007
Author: arigo
Date: Tue Feb 27 09:20:52 2007
New Revision: 39482
Added:
pypy/dist/pypy/interpreter/callmethod.py (contents, props changed)
pypy/dist/pypy/interpreter/test/test_callmethod.py (contents, props changed)
Modified:
pypy/dist/lib-python/modified-2.4.1/opcode.py
pypy/dist/pypy/config/pypyoption.py
pypy/dist/pypy/interpreter/astcompiler/pyassem.py
pypy/dist/pypy/interpreter/astcompiler/pycodegen.py
pypy/dist/pypy/interpreter/baseobjspace.py
Log:
Two bytecodes to try to speed up method calls.
See callmethod.py for a description.
The goal is to avoid creating the bound method object in the common
case. Only works for keyword-less, *arg-less, **arg-less calls so far
but it would be easy to extend.
Modified: pypy/dist/lib-python/modified-2.4.1/opcode.py
==============================================================================
--- pypy/dist/lib-python/modified-2.4.1/opcode.py (original)
+++ pypy/dist/lib-python/modified-2.4.1/opcode.py Tue Feb 27 09:20:52 2007
@@ -189,5 +189,7 @@
# pypy modification, experimental bytecode
def_op('CALL_LIKELY_BUILTIN', 144) # #args + (#kwargs << 8)
+def_op('LOOKUP_METHOD', 145) # Index in name list
+def_op('CALL_METHOD', 146) # #args not including 'self'
del def_op, name_op, jrel_op, jabs_op
Modified: pypy/dist/pypy/config/pypyoption.py
==============================================================================
--- pypy/dist/pypy/config/pypyoption.py (original)
+++ pypy/dist/pypy/config/pypyoption.py Tue Feb 27 09:20:52 2007
@@ -58,7 +58,10 @@
BoolOption("CALL_LIKELY_BUILTIN", "emit a special bytecode for likely calls to builtin functions",
default=False,
requires=[("objspace.usepycfiles", False),
- ("objspace.std.withmultidict", True)])
+ ("objspace.std.withmultidict", True)]),
+ BoolOption("CALL_METHOD", "emit a special bytecode for expr.name()",
+ default=False,
+ requires=[("objspace.usepycfiles", False)]),
]),
BoolOption("nofaking", "disallow faking in the object space",
Modified: pypy/dist/pypy/interpreter/astcompiler/pyassem.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/pyassem.py (original)
+++ pypy/dist/pypy/interpreter/astcompiler/pyassem.py Tue Feb 27 09:20:52 2007
@@ -745,6 +745,7 @@
_convert_LOAD_GLOBAL = _convert_NAME
_convert_STORE_GLOBAL = _convert_NAME
_convert_DELETE_GLOBAL = _convert_NAME
+ _convert_LOOKUP_METHOD = _convert_NAME
def _convert_DEREF(self, inst):
assert isinstance(inst, InstrName)
@@ -938,6 +939,8 @@
return depth_CALL_FUNCTION(argc)-1
def depth_CALL_FUNCTION_VAR_KW(argc):
return depth_CALL_FUNCTION(argc)-2
+def depth_CALL_METHOD(argc):
+ return -argc-1
def depth_MAKE_FUNCTION(argc):
return -argc
def depth_MAKE_CLOSURE(argc):
@@ -1039,6 +1042,7 @@
'SETUP_FINALLY': 3,
'FOR_ITER': 1,
'WITH_CLEANUP': 3,
+ 'LOOKUP_METHOD': 1,
}
# use pattern match
patterns = [
Modified: pypy/dist/pypy/interpreter/astcompiler/pycodegen.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/pycodegen.py (original)
+++ pypy/dist/pypy/interpreter/astcompiler/pycodegen.py Tue Feb 27 09:20:52 2007
@@ -1008,11 +1008,13 @@
self.emit('EXEC_STMT')
def visitCallFunc(self, node):
+ self.set_lineno(node)
if self.emit_builtin_call(node):
return
+ if self.emit_method_call(node):
+ return
pos = 0
kw = 0
- self.set_lineno(node)
node.node.accept( self )
for arg in node.args:
arg.accept( self )
@@ -1029,18 +1031,20 @@
opcode = callfunc_opcode_info[ have_star*2 + have_dstar]
self.emitop_int(opcode, kw << 8 | pos)
- def emit_builtin_call(self, node):
- if not self.space.config.objspace.opcodes.CALL_LIKELY_BUILTIN:
- return False
+ def check_simple_call_args(self, node):
if node.star_args is not None or node.dstar_args is not None:
return False
- pos = 0
# check for kw args
for arg in node.args:
if isinstance(arg, ast.Keyword):
return False
- else:
- pos = pos + 1
+ return True
+
+ def emit_builtin_call(self, node):
+ if not self.space.config.objspace.opcodes.CALL_LIKELY_BUILTIN:
+ return False
+ if not self.check_simple_call_args(node):
+ return False
func = node.node
if not isinstance(func, ast.Name):
return False
@@ -1054,10 +1058,25 @@
and index != -1):
for arg in node.args:
arg.accept(self)
- self.emitop_int("CALL_LIKELY_BUILTIN", index << 8 | pos)
+ self.emitop_int("CALL_LIKELY_BUILTIN", index << 8 | len(node.args))
return True
return False
+ def emit_method_call(self, node):
+ if not self.space.config.objspace.opcodes.CALL_METHOD:
+ return False
+ meth = node.node
+ if not isinstance(meth, ast.Getattr):
+ return False
+ if not self.check_simple_call_args(node):
+ return False
+ meth.expr.accept(self)
+ self.emitop('LOOKUP_METHOD', self.mangle(meth.attrname))
+ for arg in node.args:
+ arg.accept(self)
+ self.emitop_int('CALL_METHOD', len(node.args))
+ return True
+
def visitPrint(self, node):
self.set_lineno(node)
if node.dest:
Modified: pypy/dist/pypy/interpreter/baseobjspace.py
==============================================================================
--- pypy/dist/pypy/interpreter/baseobjspace.py (original)
+++ pypy/dist/pypy/interpreter/baseobjspace.py Tue Feb 27 09:20:52 2007
@@ -179,6 +179,8 @@
# import extra modules for side-effects, possibly based on config
import pypy.interpreter.nestedscope # register *_DEREF bytecodes
+ if self.config.objspace.opcodes.CALL_METHOD:
+ import pypy.interpreter.callmethod # register *_METHOD bytecodes
self.interned_strings = {}
self.pending_actions = []
Added: pypy/dist/pypy/interpreter/callmethod.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/interpreter/callmethod.py Tue Feb 27 09:20:52 2007
@@ -0,0 +1,63 @@
+"""
+Two bytecodes to speed up method calls. Here is how a method call looks
+like: (on the left, without the new bytecodes; on the right, with them)
+
+ <push self> <push self>
+ LOAD_ATTR name LOOKUP_METHOD name
+ <push arg 0> <push arg 0>
+ ... ...
+ <push arg n-1> <push arg n-1>
+ CALL_FUNCTION n CALL_METHOD n
+"""
+
+from pypy.interpreter import pyframe, function
+
+
+class __extend__(pyframe.PyFrame):
+
+ def LOOKUP_METHOD(f, nameindex, *ignored):
+ # stack before after
+ # -------------- --fast-method----fallback-case------------
+ #
+ # w_object None
+ # w_object => w_function w_boundmethod_or_whatever
+ # (more stuff) (more stuff) (more stuff)
+ #
+ # NB. this assumes a space based on pypy.objspace.descroperation.
+ space = f.space
+ w_obj = f.popvalue()
+ w_name = f.getname_w(nameindex)
+ w_typ = space.type(w_obj)
+ w_src, w_dummy = space.lookup_in_type_where(w_typ, '__getattribute__')
+ w_value = None
+ if space.is_w(w_src, space.w_object):
+ name = space.str_w(w_name)
+ w_descr = space.lookup(w_obj, name)
+ descr = space.interpclass_w(w_descr)
+ if descr is None:
+ # this handles directly the common case
+ # module.function(args..)
+ w_value = w_obj.getdictvalue(space, w_name)
+ elif type(descr) is function.Function:
+ w_value = w_obj.getdictvalue_attr_is_in_class(space, w_name)
+ if w_value is None:
+ # fast method path: a function object in the class,
+ # nothing in the instance
+ f.pushvalue(w_descr)
+ f.pushvalue(w_obj)
+ return
+ if w_value is None:
+ w_value = space.getattr(w_obj, w_name)
+ f.pushvalue(w_value)
+ f.pushvalue(None)
+
+ def CALL_METHOD(f, nargs, *ignored):
+ # 'nargs' is the argument count excluding the implicit 'self'
+ w_self = f.peekvalue(nargs)
+ w_callable = f.peekvalue(nargs + 1)
+ try:
+ n = nargs + (w_self is not None)
+ w_result = f.space.call_valuestack(w_callable, n, f)
+ finally:
+ f.dropvalues(nargs + 2)
+ f.pushvalue(w_result)
Added: pypy/dist/pypy/interpreter/test/test_callmethod.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/interpreter/test/test_callmethod.py Tue Feb 27 09:20:52 2007
@@ -0,0 +1,105 @@
+from pypy.conftest import gettestobjspace
+
+
+class AppTestCallMethod:
+ # The exec hacking is needed to have the code snippets compiled
+ # by our own compiler, not CPython's
+
+ def setup_class(cls):
+ cls.space = gettestobjspace(**{"objspace.opcodes.CALL_METHOD": True})
+
+ def test_call_method(self):
+ exec """if 1:
+ class C(object):
+ def m(*args, **kwds):
+ return args, kwds
+ sm = staticmethod(m)
+ cm = classmethod(m)
+
+ c = C()
+ assert c.m() == ((c,), {})
+ assert c.sm() == ((), {})
+ assert c.cm() == ((C,), {})
+ assert c.m(5) == ((c, 5), {})
+ assert c.sm(6) == ((6,), {})
+ assert c.cm(7) == ((C, 7), {})
+ assert c.m(5, x=3) == ((c, 5), {'x': 3})
+ assert c.m(*range(5)) == ((c, 0, 1, 2, 3, 4), {})
+ assert c.m(**{'u': 4}) == ((c,), {'u': 4})
+ """
+
+ def test_call_attribute(self):
+ exec """if 1:
+ class C(object):
+ def m(*args, **kwds):
+ return args, kwds
+ def myfunc(*args):
+ return args
+
+ c = C()
+ c.m = c.m2 = myfunc
+ assert c.m() == ()
+ assert c.m(5, 2) == (5, 2)
+ assert c.m2(4) == (4,)
+ assert c.m2("h", "e", "l", "l", "o") == tuple("hello")
+ """
+
+ def test_call_module(self):
+ exec """if 1:
+ import sys
+ try:
+ sys.exit(5)
+ except SystemExit, e:
+ assert e.args == (5,)
+ else:
+ raise Exception, "did not raise?"
+ """
+
+ def test_custom_getattr(self):
+ exec """if 1:
+ class C(object):
+ def __getattr__(self, name):
+ if name == 'bla':
+ return myfunc
+ raise AttributeError
+ def myfunc(*args):
+ return args
+
+ c = C()
+ assert c.bla(1, 2, 3) == (1, 2, 3)
+ """ in {}
+
+ def test_custom_getattribute(self):
+ exec """if 1:
+ class C(object):
+ def __getattribute__(self, name):
+ if name == 'bla':
+ return myfunc
+ raise AttributeError
+ def bla(self):
+ Boom
+ def myfunc(*args):
+ return args
+
+ c = C()
+ assert c.bla(1, 2, 3) == (1, 2, 3)
+ """ in {}
+
+ def test_builtin(self):
+ exec """if 1:
+ class C(object):
+ foobar = len
+ c = C()
+ assert c.foobar("hello") == 5
+ """
+
+ def test_attributeerror(self):
+ exec """if 1:
+ assert 5 .__add__(6) == 11
+ try:
+ 6 .foobar(7)
+ except AttributeError:
+ pass
+ else:
+ raise Exception("did not raise?")
+ """
More information about the Pypy-commit
mailing list