[pypy-svn] r32607 - in pypy/branch/method-cache/pypy: interpreter interpreter/astcompiler objspace/std tool
arigo at codespeak.net
arigo at codespeak.net
Sat Sep 23 15:05:04 CEST 2006
Author: arigo
Date: Sat Sep 23 15:05:01 2006
New Revision: 32607
Added:
pypy/branch/method-cache/pypy/interpreter/methodcache.py (contents, props changed)
Modified:
pypy/branch/method-cache/pypy/interpreter/astcompiler/pyassem.py
pypy/branch/method-cache/pypy/interpreter/astcompiler/pycodegen.py
pypy/branch/method-cache/pypy/interpreter/pyopcode.py
pypy/branch/method-cache/pypy/objspace/std/objspace.py
pypy/branch/method-cache/pypy/objspace/std/typeobject.py
pypy/branch/method-cache/pypy/objspace/std/typetype.py
pypy/branch/method-cache/pypy/tool/stdlib_opcode.py
Log:
First version. Still to do:
- think about interaction with other objspaces than the std
- marshal the MethodCache constants
- in pyassem, get the value stack depth right
Modified: pypy/branch/method-cache/pypy/interpreter/astcompiler/pyassem.py
==============================================================================
--- pypy/branch/method-cache/pypy/interpreter/astcompiler/pyassem.py (original)
+++ pypy/branch/method-cache/pypy/interpreter/astcompiler/pyassem.py Sat Sep 23 15:05:01 2006
@@ -705,7 +705,6 @@
def _lookupConst(self, w_obj, list_w):
space = self.space
- w_obj_type = space.type(w_obj)
for i in range(len(list_w)):
if self._cmpConsts(w_obj, list_w[i]):
return i
@@ -776,7 +775,18 @@
arg = inst.name
index = self._cmp.index(arg)
return InstrInt(inst.op, index)
-
+
+ def _convert_CALL_METHOD(self, inst):
+ assert isinstance(inst, InstrObj)
+ w_cache = inst.obj
+ index = self._lookupConst(w_cache, self.consts)
+ return InstrInt(inst.op, index)
+
+ _convert_CALL_METHOD_KW = _convert_CALL_METHOD
+ _convert_CALL_METHOD_VAR = _convert_CALL_METHOD
+ _convert_CALL_METHOD_VAR_KW = _convert_CALL_METHOD
+ _convert_CALL_METHOD_FAST = _convert_CALL_METHOD
+
# similarly for other opcodes...
@@ -980,6 +990,7 @@
# XXX 1. need to keep track of stack depth on jumps
# XXX 2. at least partly as a result, this code is broken
# XXX 3. Don't need a class here!
+ # XXX CALL_METHOD_* NOT TRACKED YET, we get a bad but safe upper bound
def findDepth(self, insts, debug=0):
depth = 0
Modified: pypy/branch/method-cache/pypy/interpreter/astcompiler/pycodegen.py
==============================================================================
--- pypy/branch/method-cache/pypy/interpreter/astcompiler/pycodegen.py (original)
+++ pypy/branch/method-cache/pypy/interpreter/astcompiler/pycodegen.py Sat Sep 23 15:05:01 2006
@@ -13,6 +13,7 @@
CO_NEWLOCALS, CO_NESTED, CO_GENERATOR, CO_GENERATOR_ALLOWED, \
CO_FUTURE_DIVISION, CO_FUTURE_WITH_STATEMENT
from pypy.interpreter.pyparser.error import SyntaxError
+from pypy.interpreter.methodcache import MethodCache
# drop VERSION dependency since it the ast transformer for 2.4 doesn't work with 2.3 anyway
VERSION = 2
@@ -24,6 +25,13 @@
"CALL_FUNCTION_VAR",
"CALL_FUNCTION_VAR_KW",
]
+callmeth_opcode_info = [
+ # (Have *args, Have **args) : opcode
+ "CALL_METHOD",
+ "CALL_METHOD_KW",
+ "CALL_METHOD_VAR",
+ "CALL_METHOD_VAR_KW",
+]
LOOP = 1
EXCEPT = 2
@@ -1032,7 +1040,12 @@
pos = 0
kw = 0
self.set_lineno(node)
- node.node.accept( self )
+ is_method = isinstance(node.node, ast.Getattr)
+ if is_method:
+ # special case for calls that "look like" method calls
+ node.node.expr.accept( self )
+ else:
+ node.node.accept( self )
for arg in node.args:
arg.accept( self )
if isinstance(arg, ast.Keyword):
@@ -1045,8 +1058,17 @@
node.dstar_args.accept( self )
have_star = node.star_args is not None
have_dstar = node.dstar_args is not None
- opcode = callfunc_opcode_info[ have_star*2 + have_dstar]
- self.emitop_int(opcode, kw << 8 | pos)
+ argkwcount = kw << 8 | pos
+ if is_method:
+ if not have_star and not have_dstar and not kw:
+ opcode = 'CALL_METHOD_FAST'
+ else:
+ opcode = callmeth_opcode_info[ have_star*2 + have_dstar]
+ cache = MethodCache(node.node.attrname, argkwcount)
+ self.emitop_obj(opcode, self.space.wrap(cache))
+ else:
+ opcode = callfunc_opcode_info[ have_star*2 + have_dstar]
+ self.emitop_int(opcode, argkwcount)
def visitPrint(self, node):
self.set_lineno(node)
Added: pypy/branch/method-cache/pypy/interpreter/methodcache.py
==============================================================================
--- (empty file)
+++ pypy/branch/method-cache/pypy/interpreter/methodcache.py Sat Sep 23 15:05:01 2006
@@ -0,0 +1,70 @@
+"""
+"""
+
+from pypy.interpreter import baseobjspace, typedef
+from pypy.interpreter.function import Function
+
+
+class MethodCache(baseobjspace.Wrappable):
+
+ cached_tag = None
+ w_cached_function = None
+
+ def __init__(self, attrname, argkwcount):
+ self.attrname = attrname
+ self.argkwcount = argkwcount
+
+ def load_cache(self, space, w_type, version_tag):
+ w_class, w_value = space.lookup_in_type_where(w_type,
+ '__getattribute__')
+ if w_class is not None and space.is_w(w_class, space.w_object):
+ # no special __getattribute__, so we can perform the
+ # lookup of the attrname in the usual way
+ w_class, w_value = space.lookup_in_type_where(w_type,
+ self.attrname)
+ if type(w_value) is Function:
+ # cache the Function object
+ self.cached_tag = version_tag
+ self.w_cached_function = w_value
+ return True
+ # not the common case - no caching
+ self.cached_tag = None
+ self.w_cached_function = None
+ return False
+
+ def call_args(self, space, w_obj, args):
+ w_type = space.type(w_obj)
+ version_tag = space.gettypeversion(w_type)
+ if version_tag is self.cached_tag or self.load_cache(space, w_type,
+ version_tag):
+ w_value = w_obj.getdictvalue_w(space, self.attrname)
+ if w_value is None:
+ # no method shadowing (this is the common situation that we
+ # are trying to make as fast as possible)
+ args = args.prepend(w_obj)
+ return space.call_args(self.w_cached_function, args)
+ else:
+ # fallback
+ w_value = space.getattr(w_obj, space.wrap(self.attrname))
+ return space.call_args(w_value, args)
+
+ def call_valuestack(self, space, valuestack):
+ nargs = self.argkwcount
+ w_obj = valuestack.top(nargs)
+ w_type = space.type(w_obj)
+ version_tag = space.gettypeversion(w_type)
+ if version_tag is self.cached_tag or self.load_cache(space, w_type,
+ version_tag):
+ w_value = w_obj.getdictvalue_w(space, self.attrname)
+ if w_value is None:
+ # no method shadowing (this is the common situation that we
+ # are trying to make as fast as possible)
+ return self.w_cached_function.funccall_valuestack(nargs + 1,
+ valuestack)
+ else:
+ # fallback
+ w_value = space.getattr(w_obj, space.wrap(self.attrname))
+ return space.call_valuestack(w_value, nargs, valuestack)
+
+
+MethodCache.typedef = typedef.TypeDef("method_cache")
Modified: pypy/branch/method-cache/pypy/interpreter/pyopcode.py
==============================================================================
--- pypy/branch/method-cache/pypy/interpreter/pyopcode.py (original)
+++ pypy/branch/method-cache/pypy/interpreter/pyopcode.py Sat Sep 23 15:05:01 2006
@@ -684,9 +684,9 @@
def CALL_FUNCTION(f, oparg):
# XXX start of hack for performance
- if (oparg >> 8) & 0xff == 0:
+ if oparg <= 0xff:
# Only positional arguments
- nargs = oparg & 0xff
+ nargs = oparg
w_function = f.valuestack.top(nargs)
try:
w_result = f.space.call_valuestack(w_function, nargs, f.valuestack)
@@ -712,6 +712,57 @@
w_varargs = f.valuestack.pop()
f.call_function(oparg, w_varargs, w_varkw)
+ def CALL_METHOD(f, constindex, w_star=None, w_starstar=None):
+ from pypy.interpreter.methodcache import MethodCache
+ w_methodcache = f.getconstant_w(constindex)
+ methodcache = f.space.interp_w(MethodCache, w_methodcache)
+ oparg = methodcache.argkwcount
+ n_arguments = oparg & 0xff
+ n_keywords = (oparg>>8) & 0xff
+ keywords = None
+ if n_keywords:
+ keywords = {}
+ for i in range(n_keywords):
+ w_value = f.valuestack.pop()
+ w_key = f.valuestack.pop()
+ key = f.space.str_w(w_key)
+ keywords[key] = w_value
+ arguments = [None] * n_arguments
+ for i in range(n_arguments - 1, -1, -1):
+ arguments[i] = f.valuestack.pop()
+ args = Arguments(f.space, arguments, keywords, w_star, w_starstar)
+ w_self = f.valuestack.pop()
+ w_result = methodcache.call_args(f.space, w_self, args)
+ #rstack.resume_point("call_function", f, returns=w_result) XXX
+ f.valuestack.push(w_result)
+
+ def CALL_METHOD_VAR(f, oparg):
+ w_varargs = f.valuestack.pop()
+ f.CALL_METHOD(oparg, w_varargs, None)
+
+ def CALL_METHOD_KW(f, oparg):
+ w_varkw = f.valuestack.pop()
+ f.CALL_METHOD(oparg, None, w_varkw)
+
+ def CALL_METHOD_VAR_KW(f, oparg):
+ w_varkw = f.valuestack.pop()
+ w_varargs = f.valuestack.pop()
+ f.CALL_METHOD(oparg, w_varargs, w_varkw)
+
+ def CALL_METHOD_FAST(f, constindex):
+ # Only positional arguments
+ from pypy.interpreter.methodcache import MethodCache
+ w_methodcache = f.getconstant_w(constindex)
+ methodcache = f.space.interp_w(MethodCache, w_methodcache)
+ nargs = methodcache.argkwcount
+ try:
+ w_result = methodcache.call_valuestack(f.space, f.valuestack)
+ #rstack.resume_point("CALL_FUNCTION", f, nargs, returns=w_result)
+ # XXX
+ finally:
+ f.valuestack.drop(nargs + 1)
+ f.valuestack.push(w_result)
+
def MAKE_FUNCTION(f, numdefaults):
w_codeobj = f.valuestack.pop()
codeobj = f.space.interp_w(PyCode, w_codeobj)
@@ -735,6 +786,7 @@
def LIST_APPEND(f):
w = f.valuestack.pop()
v = f.valuestack.pop()
+ # XXX could use a MethodCache here too, or even something more direct
f.space.call_method(v, 'append', w)
def SET_LINENO(f, lineno):
Modified: pypy/branch/method-cache/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/branch/method-cache/pypy/objspace/std/objspace.py (original)
+++ pypy/branch/method-cache/pypy/objspace/std/objspace.py Sat Sep 23 15:05:01 2006
@@ -259,6 +259,10 @@
# unique-for-this-space W_TypeObject instance
return self.fromcache(stdtypedef.TypeCache).getorbuild(typedef)
+ def gettypeversion(self, w_type):
+ assert isinstance(w_type, W_TypeObject)
+ return w_type.version_tag
+
def wrap(self, x):
"Wraps the Python value 'x' into one of the wrapper classes."
# You might notice that this function is rather conspicuously
Modified: pypy/branch/method-cache/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/branch/method-cache/pypy/objspace/std/typeobject.py (original)
+++ pypy/branch/method-cache/pypy/objspace/std/typeobject.py Sat Sep 23 15:05:01 2006
@@ -38,6 +38,11 @@
return "_%s%s" % (klass, name)
+
+class VersionTag(object):
+ pass
+
+
class W_TypeObject(W_Object):
from pypy.objspace.std.typetype import type_typedef as typedef
@@ -207,6 +212,7 @@
w_self.mro_w = space.unpackiterable(w_mro)
return
w_self.mro_w = w_self.compute_mro()
+ w_self.mutated()
def ready(w_self):
for w_base in w_self.bases_w:
@@ -214,6 +220,11 @@
continue
w_base.add_subclass(w_self)
+ def mutated(w_self):
+ """Relevant changes are only the ones that change the result
+ of a lookup(), i.e. changes to dict_w or mro_w."""
+ w_self.version_tag = VersionTag()
+
# compute the most parent class with the same layout as us
def get_layout(w_self):
w_bestbase = w_self.w_bestbase
@@ -407,6 +418,7 @@
space.set(w_descr, w_type, w_value)
return
w_type.dict_w[name] = w_value
+ w_type.mutated()
def delattr__Type_ANY(space, w_type, w_name):
if w_type.lazyloaders:
@@ -419,9 +431,10 @@
return
try:
del w_type.dict_w[name]
- return
except KeyError:
raise OperationError(space.w_AttributeError, w_name)
+ else:
+ w_type.mutated()
# ____________________________________________________________
Modified: pypy/branch/method-cache/pypy/objspace/std/typetype.py
==============================================================================
--- pypy/branch/method-cache/pypy/objspace/std/typetype.py (original)
+++ pypy/branch/method-cache/pypy/objspace/std/typetype.py Sat Sep 23 15:05:01 2006
@@ -139,6 +139,7 @@
space.wrap("can't set %s.__module__" %
w_type.name))
w_type.dict_w['__module__'] = w_value
+ w_type.mutated()
def descr___subclasses__(space, w_type):
"""Return the list of immediate subclasses."""
Modified: pypy/branch/method-cache/pypy/tool/stdlib_opcode.py
==============================================================================
--- pypy/branch/method-cache/pypy/tool/stdlib_opcode.py (original)
+++ pypy/branch/method-cache/pypy/tool/stdlib_opcode.py Sat Sep 23 15:05:01 2006
@@ -5,5 +5,18 @@
opcode_path = py.path.local(__file__).dirpath().dirpath().dirpath('lib-python/modified-2.4.1/opcode.py')
execfile(str(opcode_path), globals())
+ # __________ extra opcodes __________
+
+ def def_op(name, op):
+ opname[op] = name
+ opmap[name] = op
+
+ def_op('CALL_METHOD', 192)
+ def_op('CALL_METHOD_VAR', 193)
+ def_op('CALL_METHOD_KW', 194)
+ def_op('CALL_METHOD_VAR_KW', 195)
+ def_op('CALL_METHOD_FAST', 196)
+
+
load_opcode()
del load_opcode
More information about the Pypy-commit
mailing list