[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