[pypy-svn] r13088 - in pypy/dist/pypy: annotation interpreter module/sys objspace/std tool translator translator/goal translator/test

pedronis at codespeak.net pedronis at codespeak.net
Mon Jun 6 05:09:00 CEST 2005


Author: pedronis
Date: Mon Jun  6 05:08:52 2005
New Revision: 13088

Added:
   pypy/dist/pypy/annotation/policy.py   (contents, props changed)
   pypy/dist/pypy/annotation/specialize.py   (contents, props changed)
Modified:
   pypy/dist/pypy/annotation/bookkeeper.py
   pypy/dist/pypy/annotation/unaryop.py
   pypy/dist/pypy/interpreter/compiler.py
   pypy/dist/pypy/interpreter/error.py
   pypy/dist/pypy/interpreter/miscutils.py
   pypy/dist/pypy/interpreter/pyframe.py
   pypy/dist/pypy/interpreter/typedef.py
   pypy/dist/pypy/module/sys/state.py
   pypy/dist/pypy/objspace/std/fake.py
   pypy/dist/pypy/objspace/std/objspace.py
   pypy/dist/pypy/tool/cache.py
   pypy/dist/pypy/translator/ann_override.py
   pypy/dist/pypy/translator/annrpython.py
   pypy/dist/pypy/translator/goal/translate_pypy.py
   pypy/dist/pypy/translator/test/snippet.py
   pypy/dist/pypy/translator/test/test_annrpython.py
   pypy/dist/pypy/translator/translator.py
Log:
Revised specialization, see annotation/specialize.py and changes in bookkeeper;

now annotator can receive a policy object to specify both overrides and specializations through the methods

specialize and override

a base class version is defined in annotation/policy.py, it will use methods of the form specialize__xxx or override__xxx
chosen by attaching to the callable in user-code the attribute _annspecialcase_ with values:

"specialize:xxx"
"override:xxx"

respectively.

ann_override now defines such a policy class which is used by translate_pypy,

(Notice: if a policy is not passed to the annotator an even more basic policy without any overrides nor specializations is used.)

- 'override' methods take the policy and inputcells

- 'specialize'  methods take the policy and a bookkeeper, the involved spaceop, the involved callable func, 
an Arguments object with SomeObjects and a mono flag whether just one statically known callable is involved;

they should normally return a _tuple_ key to identify the specialized version (which if absent will be created), or None meaning no 
specialization

the policy can define a default_specialize that is always invoked, the version in the base class does the specializion for *arg 
functions

There's no general 'location' specialization anymore, for classes there's "specialize:ctr_location" which is implemented
in specialize.py and exposed by the base policy class.

Now phases successive to annotation can query whether a call space op involves specialized callables:

bookkeeper.query_spaceop_callable(spaceop) can be called with a call spaceop that has the called obj annotated as some PBC
it will return possibly a new PBC with the specialized version effectively considered by the annotator for the callable
(it also returns a flag which is normally false unless, in the only case (for now), the callable involved was marked 
"specialize:memo")

the call site families and tables now refer to specialized versions of callables, not the original ones.





Modified: pypy/dist/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/dist/pypy/annotation/bookkeeper.py	(original)
+++ pypy/dist/pypy/annotation/bookkeeper.py	Mon Jun  6 05:08:52 2005
@@ -11,14 +11,12 @@
 from pypy.annotation.classdef import ClassDef, isclassdef
 from pypy.annotation.listdef import ListDef, MOST_GENERAL_LISTDEF
 from pypy.annotation.dictdef import DictDef, MOST_GENERAL_DICTDEF
-from pypy.tool.sourcetools import func_with_new_name, valid_identifier
-from pypy.interpreter.pycode import CO_VARARGS
 from pypy.interpreter.pycode import cpython_code_signature
-from pypy.interpreter.argument import ArgErr
+from pypy.interpreter.argument import Arguments, ArgErr
 from pypy.rpython.rarithmetic import r_uint
 from pypy.tool.unionfind import UnionFind
 
-import inspect, new
+from pypy.annotation.specialize import decide_callable
 
 class PBCAccessSet:
     def __init__(self, obj):
@@ -59,9 +57,8 @@
         self.listdefs = {}       # map position_keys to ListDefs
         self.dictdefs = {}       # map position_keys to DictDefs
         
-        # mapping position -> most general result, for call sites calling
-        # argtypes specialized functions
-        self.argtypes_spec_callsite_results = {}
+        # mapping position -> key, prev_result for specializations
+        self.spec_callsite_keys_results = {}
 
         self.pbc_maximal_access_sets = UnionFind(PBCAccessSet)
         # can be precisely computed only at fix-point, see
@@ -92,9 +89,10 @@
         self.pbc_callables = {}
 
         for (fn, block, i), shape in self.pbc_call_sites.iteritems():
-            assert block.operations[i].opname in ('call_args', 'simple_call')
-            pbc = self.annotator.binding(block.operations[i].args[0], extquery=True)
-            self.consider_pbc_call(pbc, shape, position=(fn, block, i))
+            spaceop = block.operations[i]
+            assert spaceop.opname in ('call_args', 'simple_call')
+            pbc = self.annotator.binding(spaceop.args[0], extquery=True)
+            self.consider_pbc_call(pbc, shape, spaceop)
 
     def getclassdef(self, cls):
         """Get the ClassDef associated with the given user cls."""
@@ -312,9 +310,18 @@
                 
         return unionof(*actuals)        
 
-    def consider_pbc_call(self, pbc, shape, position=None): # computation done at fix-point
+    def consider_pbc_call(self, pbc, shape, spaceop=None, implicit_init=None): # computation done at fix-point
         if not isinstance(pbc, SomePBC):
             return
+        
+        if implicit_init:
+            implicit_init = pbc, implicit_init
+            shape = (shape[0]+1,) + shape[1:]
+        else:
+            implicit_init = None
+
+        pbc, dontcarememo = self.query_spaceop_callable(spaceop,
+                                                        implicit_init=implicit_init) 
 
         nonnullcallables = []
         for func, classdef in pbc.prebuiltinstances.items():
@@ -327,10 +334,9 @@
             if isinstance(func, (type, ClassType)) and \
                     func.__module__ != '__builtin__':
                 assert classdef is None
-                dontcare, s_init = self.get_s_init(func, position=position)
+                init_classdef, s_init = self.get_s_init(func)
                 if s_init is not None:
-                    init_shape = (shape[0]+1,) + shape[1:]
-                    self.consider_pbc_call(s_init, init_shape) 
+                    self.consider_pbc_call(s_init, shape, spaceop, implicit_init=init_classdef) 
 
             callable = (classdef, func)
             if hasattr(func, 'im_func') and func.im_self is None:
@@ -376,21 +382,52 @@
         return unionof(*results) 
 
     # decide_callable(position, func, args, mono) -> callb, key
-    # query_spaceop_callable(spaceop) -> pbc
+    # query_spaceop_callable(spaceop) -> pbc, memo
     # get_s_init(decided_cls) -> classdef, s_undecided_init
 
-    def get_s_init(self, cls, position=None, mono=True):
-        specialize = getattr(cls, "_specialize_", False)
-        if specialize:
-            if specialize == "location":
-                assert mono, "not-static construction of specialized class %s" % cls
-                cls = self.specialize_by_key(cls, position, 
-                                             name="%s__At_%s" % (cls.__name__, 
-                                                                 position_name(position)))
+    def query_spaceop_callable(self, spaceop, implicit_init=None): # -> s_pbc, memo
+        self.enter(None)
+        try:
+            if implicit_init is None:
+                assert spaceop.opname in ("simple_call", "call_args")
+                obj = spaceop.args[0]
+                s_obj = self.annotator.binding(obj, extquery=True)
+                init_classdef = None
             else:
-                raise Exception, \
-                      "unsupported specialization type '%s'"%(specialize,)
+                s_obj, init_classdef = implicit_init
+
+            assert isinstance(s_obj, SomePBC)
+            if len(s_obj.prebuiltinstances) > 1: # no specialization expected
+                return s_obj, False
+
+            argsvars = spaceop.args[1:]
+            args_s = [self.annotator.binding(v) for v in argsvars]
+            args = self.build_args(spaceop.opname, args_s)
+            if init_classdef:
+                args = args.prepend(SomeInstance(init_classdef))
+
+            func, classdef = s_obj.prebuiltinstances.items()[0]
+            func, key = decide_callable(self, spaceop, func, args, mono=True)
+
+            if key is None:
+                return s_obj, False
+
+            if func is None: # specialisation computes annotation direclty
+                return s_obj, True
+
+            return SomePBC({func: classdef}), False
+        finally:
+            self.leave()
+
+    def build_args(self, op, args_s):
+        space = RPythonCallsSpace()
+        if op == "simple_call":
+            return Arguments(space, args_s)
+        elif op == "call_args":
+            return Arguments.fromshape(space, args_s[0].const, # shape
+                                       args_s[1:])
 
+    def get_s_init(self, cls):
         classdef = self.getclassdef(cls)
         init = getattr(cls, '__init__', None)
         if init is not None and init != object.__init__:
@@ -409,9 +446,20 @@
     def pycall(self, func, args, mono):
         if func is None:   # consider None as a NULL function pointer
             return SomeImpossibleValue()
+
+        # decide and pick if necessary a specialized version
+        base_func = func
+        func, key = decide_callable(self, self.position_key, func, args, mono, unpacked=True)
+        
+        if func is None:
+            assert isinstance(key, SomeObject)
+            return key
+
+        func, args = func # method unpacking done by decide_callable
+            
         if isinstance(func, (type, ClassType)) and \
             func.__module__ != '__builtin__':
-            classdef, s_init = self.get_s_init(func, position=self.position_key, mono=mono)
+            classdef, s_init = self.get_s_init(func)
             s_instance = SomeInstance(classdef)
             # flow into __init__() if the class has got one
             if s_init is not None:
@@ -423,60 +471,7 @@
                     raise Exception, "no __init__ found in %r" % (cls,)
             return s_instance
 
-        if hasattr(func, 'im_func'):
-            if func.im_self is not None:
-                s_self = self.immutablevalue(func.im_self)
-                args = args.prepend(s_self)
-            # for debugging only, but useful to keep anyway:
-            try:
-                func.im_func.class_ = func.im_class
-            except AttributeError:
-                # probably a builtin function, we don't care to preserve
-                # class information then
-                pass
-            func = func.im_func
         assert isinstance(func, FunctionType), "[%s] expected function, got %r" % (self.whereami(), func)
-        # do we need to specialize this function in several versions?
-        specialize = getattr(func, '_specialize_', False)
-
-        if specialize:
-            assert mono, "not-static call to specialized %s" % func
-            base_func = func
-            if specialize == 'argtypes':
-                key = short_type_name(args)
-                func = self.specialize_by_key(func, key,
-                                              func.__name__+'__'+key)
-            elif specialize == "location":
-                # fully specialize: create one version per call position
-                func = self.specialize_by_key(func, self.position_key,
-                                              name="%s__At_%s" % (func.__name__, 
-                                                                  position_name(self.position_key)))
-            elif specialize == "memo":
-                # call the function now, and collect possible results
-                arglist_s, kwds_s = args.unpack()
-                assert not kwds_s, ("no ** args in call to function "
-                                    "marked specialize='concrete'")
-                possible_results = []
-                for arglist in possible_arguments(arglist_s):
-                    result = func(*arglist)
-                    possible_results.append(self.immutablevalue(result))
-                return unionof(*possible_results)
-            else:
-                raise Exception, "unsupported specialization type '%s'"%(specialize,)
-
-        elif func.func_code.co_flags & CO_VARARGS:
-            # calls to *arg functions: create one version per number of args
-            assert mono, "not-static call to *arg function %s" % func
-            assert not args.has_keywords(), (
-                "keyword forbidden in calls to *arg functions")
-            nbargs = len(args.arguments_w)
-            if args.w_stararg is not None:
-                s_len = args.w_stararg.len()
-                assert s_len.is_constant(), "calls require known number of args"
-                nbargs += s_len.const
-            func = self.specialize_by_key(func, nbargs,
-                                          name='%s__%d' % (func.func_name,
-                                                           nbargs))
 
         # parse the arguments according to the function we are calling
         signature = cpython_code_signature(func.func_code)
@@ -492,19 +487,21 @@
 
         r = self.annotator.recursivecall(func, self.position_key, inputcells)
 
-        # in the case of argtypes specialisation we may have been calling a
-        # different function for the site which could also be just partially analysed,
-        # we need to force unifying all past and present results for the site
-        # in order to guarantee the more general results invariant.
-        if specialize == 'argtypes':
-            key = (base_func, self.position_key)
-            prev_r = self.argtypes_spec_callsite_results.get(key)
-            if prev_r is not None:
-                r = unionof(prev_r, r)
-            self.argtypes_spec_callsite_results[key] = r
+        # if we got different specializations keys for a same site, mix previous results for stability
+        if key is not None:
+            occurence = (base_func, self.position_key)
+            try:
+                prev_key, prev_r = self.spec_callsite_keys_results[occurence]
+            except KeyError:
+                self.spec_callsite_keys_results[occurence] = key, r
+            else:
+                if prev_key != key:
+                    r = unionof(r, prev_r)
+                    prev_key = None
+                self.spec_callsite_keys_results[occurence] = prev_key, r
+
         return r
         
-        
 
     def whereami(self):
         return self.annotator.whereami(self.position_key)
@@ -516,48 +513,39 @@
             pos = '?'
         ansi_print("*** WARNING: [%s] %s" % (pos, msg), esc="31") # RED
 
-    def specialize_by_key(self, thing, key, name=None):
-        key = thing, key
-        try:
-            thing = self.cachespecializations[key]
-        except KeyError:
-            if isinstance(thing, FunctionType):
-                # XXX XXX XXX HAAAAAAAAAAAACK
-                # xxx we need a way to let know subsequent phases (the
-                # generator) about the specialized function.
-                # The caller flowgraph, as it is, doesn't know.
-                # This line just avoids that the flowgraph of the original
-                # function (which is what will be considered and compiled for
-                # now) will be computed during generation itself.
-                self.annotator.translator.getflowgraph(thing)
-                #
-                thing = func_with_new_name(thing, name or thing.func_name)
-            elif isinstance(thing, (type, ClassType)):
-                superclasses = iter(inspect.getmro(thing))
-                superclasses.next() # skip thing itself
-                for cls in superclasses:
-                    assert not hasattr(cls, "_specialize_"), "for now specialization only for leaf classes"
-                
-                newdict = {}
-                for attrname,val in thing.__dict__.iteritems():
-                    if attrname == '_specialize_': # don't copy the marker
-                        continue
-                    if isinstance(val, FunctionType):
-                        fname = val.func_name
-                        if name:
-                            fname = "%s_for_%s" % (fname, name)
-                        newval = func_with_new_name(val, fname)
-                    # xxx more special cases
-                    else: 
-                        newval  = val
-                    newdict[attrname] = newval
 
-                thing = type(thing)(name or thing.__name__, (thing,), newdict)
-            else:
-                raise Exception, "specializing %r?? why??"%thing
-            self.cachespecializations[key] = thing
-        return thing
+def ishashable(x):
+    try:
+        hash(x)
+    except TypeError:
+        return False
+    else:
+        return True
+
+# for parsing call arguments
+class RPythonCallsSpace:
+    """Pseudo Object Space providing almost no real operation.
+    For the Arguments class: if it really needs other operations, it means
+    that the call pattern is too complex for R-Python.
+    """
+    def newtuple(self, items_s):
+        return SomeTuple(items_s)
+
+    def newdict(self, stuff):
+        raise CallPatternTooComplex, "'**' argument"
+
+    def unpackiterable(self, s_obj, expected_length=None):
+        if isinstance(s_obj, SomeTuple):
+            if (expected_length is not None and
+                expected_length != len(s_obj.items)):
+                raise ValueError
+            return s_obj.items
+        raise CallPatternTooComplex, "'*' argument must be SomeTuple"
 
+class CallPatternTooComplex(Exception):
+    pass
+
+# get current bookkeeper
 
 def getbookkeeper():
     """Get the current Bookkeeper.
@@ -566,50 +554,3 @@
         return TLS.bookkeeper
     except AttributeError:
         return None
-
-def ishashable(x):
-    try:
-        hash(x)
-    except TypeError:
-        return False
-    else:
-        return True
-
-def short_type_name(args):
-    l = []
-    shape, args_w = args.flatten()
-    for x in args_w:
-        if isinstance(x, SomeInstance) and hasattr(x, 'knowntype'):
-            name = "SI_" + x.knowntype.__name__
-        else:
-            name = x.__class__.__name__
-        l.append(name)
-    return "__".join(l)
-
-def position_name((fn, block, i)):
-    mod = valid_identifier(getattr(fn, '__module__', 'SYNTH'))
-    name = valid_identifier(getattr(fn, '__name__', 'UNKNOWN'))
-    return "%s_%s_Giving_%s" % (mod, name, block.operations[i].result)
-
-def possible_arguments(args):
-    # enumerate all tuples (x1,..xn) of concrete values that are contained
-    # in a tuple args=(s1,..sn) of SomeXxx.  Requires that each s be either
-    # a constant or SomePBC.
-    if not args:
-        yield ()
-        return
-    s = args[0]
-    if s.is_constant():
-        possible_values = [s.const]
-    elif isinstance(s, SomePBC):
-        for value in s.prebuiltinstances.values():
-            assert value is True, ("concrete call with a method bound "
-                                   "on a non-constant instance")
-        possible_values = s.prebuiltinstances.keys()
-    elif isinstance(s, SomeBool):
-        possible_values = [False, True]
-    else:
-        raise AssertionError, "concrete call with a non-constant arg %r" % (s,)
-    for tuple_tail in possible_arguments(args[1:]):
-        for value in possible_values:
-            yield (value,) + tuple_tail

Added: pypy/dist/pypy/annotation/policy.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/annotation/policy.py	Mon Jun  6 05:08:52 2005
@@ -0,0 +1,46 @@
+# base annotation policy for overrides and specialization
+from pypy.annotation.specialize import memo, ctr_location, default_specialize as default
+
+class AnnotatorPolicy:
+    """
+    Possibly subclass and pass an instance to the annotator to control special casing during annotation
+    """
+
+    def getspecialcase(pol, kind, obj):
+        if hasattr(obj, '_annspecialcase_'):
+            sc = obj._annspecialcase_.split(':')
+            assert len(sc) ==2, "_annspecialcase_ should have the form kind:tag"
+            if sc[0] == kind:
+                return sc[1]
+            assert sc[0] in ('override', 'specialize'), "_annspecialcase_ kinds are only 'override', 'specialize'"
+        return None
+
+    def override(pol, func, inputcells):
+        tag = pol.getspecialcase('override', func)
+        if tag is None:
+            return None
+        try:
+            override = getattr(pol, 'override__%s' % tag)
+        except AttributeError:
+            raise AttributeError, "%s override tag found in user program but not defined in annotation policy %s" % (tag, pol) 
+
+        return override(*inputcells)
+
+    def specialize(pol, bookkeeper, spaceop, func, args, mono):
+        tag = pol.getspecialcase('specialize', func)
+        if tag is None:
+            return pol.default_specialize(bookkeeper, spaceop, func, args, mono)
+        
+        try:
+            specialize = getattr(pol, 'specialize__%s' % tag)
+        except AttributeError:
+            raise AttributeError, "%s specialize tag found in user program but not defined in annotation policy %s" % (tag, pol) 
+
+        return specialize(bookkeeper, spaceop, func, args, mono)
+        
+        
+    # common specializations
+
+    default_specialize = staticmethod(default)
+    specialize__memo = staticmethod(memo)
+    specialize__ctr_location = staticmethod(ctr_location)

Added: pypy/dist/pypy/annotation/specialize.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/annotation/specialize.py	Mon Jun  6 05:08:52 2005
@@ -0,0 +1,199 @@
+# specialization support
+import types, inspect, new
+
+from pypy.tool.sourcetools import valid_identifier, func_with_new_name
+
+def decide_callable(bookkeeper, position, func, args, mono=True, unpacked=False):
+    from pypy.objspace.flow.model import SpaceOperation
+    from pypy.annotation.model import SomeObject
+
+    key = None
+    ismeth, im_class, im_self, func, args = unpack_method(bookkeeper, func, args)
+
+
+    if position is None or isinstance(position, SpaceOperation):
+        spaceop = position
+    else:
+        fn, block, i = position
+        spaceop = block.operations[i]
+
+    key = bookkeeper.annotator.policy.specialize(bookkeeper, spaceop, func, args, mono)
+    if key is not None:
+        assert mono, "not-static call to specialized %s" % func
+        if isinstance(key, SomeObject): 
+            # direct computation
+            return None, key
+        if isinstance(key, tuple): 
+            # cache specialization
+            try:
+                func = bookkeeper.cachespecializations[key]
+            except KeyError:
+                postfix = key
+                if postfix[0] is func:
+                    postfix = postfix[1:]
+                func = bookkeeper.cachespecializations[key] = clone(func, postfix)
+        elif isinstance(key, str): 
+            # specialization explicit in operation annotation
+            postfix = key
+            func = clone(func, postfix)
+        else: 
+            # specialization already retrieved
+            func = key
+    
+    if unpacked:
+        func = func, args
+    else:
+        func = repack_method(ismeth, im_class, im_self, func)
+
+    return func, key
+
+def default_specialize(bookkeeper, spaceop, func, args, mono):
+    from pypy.interpreter.pycode import CO_VARARGS
+    if isinstance(func, types.FunctionType) and func.func_code.co_flags & CO_VARARGS:
+        # calls to *arg functions: create one version per number of args
+        assert mono, "not-static call to *arg function %s" % func
+        assert not args.has_keywords(), (
+            "keyword forbidden in calls to *arg functions")
+        nbargs = len(args.arguments_w)
+        if args.w_stararg is not None:
+            s_len = args.w_stararg.len()
+            assert s_len.is_constant(), "calls require known number of args"
+            nbargs += s_len.const
+        return (func, nbargs)
+    return None # no specialization
+
+# helpers
+
+def unpack_method(bookkeeper, func, args):
+    if not hasattr(func, 'im_func'):
+        return False, None, None, func, args
+    if func.im_self is not None:
+        s_self = bookkeeper.immutablevalue(func.im_self)
+        args = args.prepend(s_self)        
+    # for debugging only, but useful to keep anyway:
+    try:
+        func.im_func.class_ = func.im_class
+    except AttributeError:
+        # probably a builtin function, we don't care to preserve
+        # class information then
+        pass
+    return True, func.im_class, func.im_self, func.im_func, args
+
+def repack_method(ismeth, im_class, im_self, func):
+    if not ismeth:
+        return func
+    return new.instancemethod(func, im_self, im_class)
+
+
+def clone(callb, postfix):
+    if not isinstance(postfix, str):
+        postfix = '_'.join([getattr(comp, '__name__', str(comp)) for comp in postfix])
+    
+    name = valid_identifier(callb.__name__ + "__" + postfix)
+
+    if isinstance(callb, types.FunctionType):
+        newcallb = func_with_new_name(callb, name)
+    elif isinstance(callb, (type, types.ClassType)):
+        superclasses = iter(inspect.getmro(callb))
+        superclasses.next() # skip callb itself
+        for cls in superclasses:
+            assert not hasattr(cls, "_annspecialcase_"), "for now specialization only for leaf classes"
+                
+        newdict = {}
+        for attrname,val in callb.__dict__.iteritems():
+            if attrname == '_annspecialcase_': # don't copy the marker
+                continue
+            if isinstance(val, types.FunctionType):
+                fname = val.func_name
+                fname = "%s_for_%s" % (fname, name)
+                newval = func_with_new_name(val, fname)
+                # xxx more special cases
+            else: 
+                newval  = val
+            newdict[attrname] = newval
+
+        newcallb = type(callb)(name or callb.__name__, (callb,), newdict)
+    else:
+        raise Exception, "specializing %r?? why??" % callb
+
+    return newcallb
+
+# ____________________________________________________________________________
+# specializations
+
+def memo(bookkeeper, spaceop, func, args, mono):
+    """NOT_RPYTHON"""
+    from pypy.annotation.model import unionof
+    # call the function now, and collect possible results
+    arglist_s, kwds_s = args.unpack()
+    assert not kwds_s, ("no ** args in call to function "
+                                    "marked specialize='concrete'")
+    possible_results = []
+    for arglist in possible_arguments(arglist_s):
+        result = func(*arglist)
+        possible_results.append(bookkeeper.immutablevalue(result))
+    return unionof(*possible_results)
+
+def possible_arguments(args):
+    from pypy.annotation.model import SomeBool, SomePBC
+    # enumerate all tuples (x1,..xn) of concrete values that are contained
+    # in a tuple args=(s1,..sn) of SomeXxx.  Requires that each s be either
+    # a constant or SomePBC.
+    if not args:
+        yield ()
+        return
+    s = args[0]
+    if s.is_constant():
+        possible_values = [s.const]
+    elif isinstance(s, SomePBC):
+        for value in s.prebuiltinstances.values():
+            assert value is True, ("concrete call with a method bound "
+                                   "on a non-constant instance")
+        possible_values = s.prebuiltinstances.keys()
+    elif isinstance(s, SomeBool):
+        possible_values = [False, True]
+    else:
+        raise AssertionError, "concrete call with a non-constant arg %r" % (s,)
+    for tuple_tail in possible_arguments(args[1:]):
+        for value in possible_values:
+            yield (value,) + tuple_tail
+
+#def argtypes(bookkeeper, spaceop, func, args, mono):
+#    """NOT_RPYTHON"""
+#    from pypy.annotation.model import SomeInstance
+#    l = []
+#    shape, args_w = args.flatten()
+#    for x in args_w:
+#        if isinstance(x, SomeInstance) and hasattr(x, 'knowntype'):
+#            name = "SI_" + x.knowntype.__name__
+#        else:
+#            name = x.__class__.__name__
+#        l.append(name)
+#    return func, "__".join(l)
+
+def ctr_location(bookkeeper, spaceop, orig_cls, args, mono):
+    """NOT_RPYTHON"""
+    from pypy.annotation.model import SomeInstance
+    v = spaceop.result
+    s_ins = bookkeeper.annotator.binding(v, extquery=True)
+    if s_ins is None:
+        return "Giving_"+v.name
+    else:
+        assert isinstance(s_ins, SomeInstance)
+        cls = s_ins.classdef.cls
+        assert issubclass(cls, orig_cls)
+        return cls
+
+def argvalue(i):
+    def specialize_argvalue(bookkeeper, spaceop, func, args, mono):
+        """NOT_RPYTHON"""
+        ignore, args_w = args.flatten()
+        return func, args_w[i].const
+    return specialize_argvalue
+
+def argtype(i):
+    def specialize_argtype(bookkeeper, spaceop, func, args, mono):
+        """NOT_RPYTHON"""
+        ignore, args_w = args.flatten()
+        return func, args_w[i].knowntype
+    return specialize_argtype

Modified: pypy/dist/pypy/annotation/unaryop.py
==============================================================================
--- pypy/dist/pypy/annotation/unaryop.py	(original)
+++ pypy/dist/pypy/annotation/unaryop.py	Mon Jun  6 05:08:52 2005
@@ -12,7 +12,7 @@
 from pypy.annotation.model import SomeInstance, SomeBuiltin, SomeFloat
 from pypy.annotation.model import SomeIterator, SomePBC, new_or_old_class
 from pypy.annotation.model import unionof, set, setunion, missing_operation
-from pypy.annotation.bookkeeper import getbookkeeper
+from pypy.annotation.bookkeeper import getbookkeeper, RPythonCallsSpace
 from pypy.annotation.classdef import isclassdef
 from pypy.annotation import builtin
 
@@ -131,12 +131,10 @@
         return obj   # default unbound __get__ implementation
 
     def simple_call(obj, *args_s):
-        space = RPythonCallsSpace()
-        return obj.call(Arguments(space, args_s))
+        return obj.call(getbookkeeper().build_args("simple_call", args_s))
 
-    def call_args(obj, s_shape, *args_s):
-        space = RPythonCallsSpace()
-        return obj.call(Arguments.fromshape(space, s_shape.const, args_s))
+    def call_args(obj, *args_s):
+        return obj.call(getbookkeeper().build_args("call_args", args_s))
 
     def call(obj, args, implicit_init=False):
         #raise Exception, "cannot follow call_args%r" % ((obj, args),)
@@ -428,28 +426,6 @@
         return immutablevalue(outcome)
             
             
-class RPythonCallsSpace:
-    """Pseudo Object Space providing almost no real operation.
-    For the Arguments class: if it really needs other operations, it means
-    that the call pattern is too complex for R-Python.
-    """
-    def newtuple(self, items_s):
-        return SomeTuple(items_s)
-
-    def newdict(self, stuff):
-        raise CallPatternTooComplex, "'**' argument"
-
-    def unpackiterable(self, s_obj, expected_length=None):
-        if isinstance(s_obj, SomeTuple):
-            if (expected_length is not None and
-                expected_length != len(s_obj.items)):
-                raise ValueError
-            return s_obj.items
-        raise CallPatternTooComplex, "'*' argument must be SomeTuple"
-
-class CallPatternTooComplex(Exception):
-    pass
-
 # annotation of low-level types
 from pypy.annotation.model import SomePtr, ll_to_annotation, annotation_to_lltype
 class __extend__(SomePtr):

Modified: pypy/dist/pypy/interpreter/compiler.py
==============================================================================
--- pypy/dist/pypy/interpreter/compiler.py	(original)
+++ pypy/dist/pypy/interpreter/compiler.py	Mon Jun  6 05:08:52 2005
@@ -112,6 +112,7 @@
             raise OperationError(space.w_TypeError,space.wrap(str(e)))
         from pypy.interpreter.pycode import PyCode
         return space.wrap(PyCode(space)._from_code(c))
+    compile._annspecialcase_ = "override:cpy_compile"
 
     def getcodeflags(self, code):
         from pypy.interpreter.pycode import PyCode

Modified: pypy/dist/pypy/interpreter/error.py
==============================================================================
--- pypy/dist/pypy/interpreter/error.py	(original)
+++ pypy/dist/pypy/interpreter/error.py	Mon Jun  6 05:08:52 2005
@@ -70,6 +70,7 @@
         application."""
         if RECORD_INTERPLEVEL_TRACEBACK:
             self.debug_excs.append(sys.exc_info())
+    record_interpreter_traceback._annspecialcase_ = "override:ignore"
 
     def print_application_traceback(self, space, file=None):
         "NOT_RPYTHON: Dump a standard application-level traceback."

Modified: pypy/dist/pypy/interpreter/miscutils.py
==============================================================================
--- pypy/dist/pypy/interpreter/miscutils.py	(original)
+++ pypy/dist/pypy/interpreter/miscutils.py	Mon Jun  6 05:08:52 2005
@@ -8,7 +8,7 @@
 class Stack:
     """Utility class implementing a stack."""
 
-    _specialize_ = 'location' # polymorphic
+    _annspecialcase_ = "specialize:ctr_location" # polymorphic
 
     def __init__(self):
         self.items = []

Modified: pypy/dist/pypy/interpreter/pyframe.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyframe.py	(original)
+++ pypy/dist/pypy/interpreter/pyframe.py	Mon Jun  6 05:08:52 2005
@@ -19,6 +19,7 @@
    """NOT_RPYTHON"""
    import sys
    return sys.exc_info()[2]   
+cpython_tb._annspecialcase_ = "override:ignore"
 
 class PyFrame(eval.Frame):
     """Represents a frame for a regular Python function

Modified: pypy/dist/pypy/interpreter/typedef.py
==============================================================================
--- pypy/dist/pypy/interpreter/typedef.py	(original)
+++ pypy/dist/pypy/interpreter/typedef.py	Mon Jun  6 05:08:52 2005
@@ -33,7 +33,7 @@
         subcls = _buildusercls(cls, hasdict, wants_slots)
         subclass_cache[key] = subcls
         return subcls
-get_unique_interplevel_subclass._specialize_ = "memo"
+get_unique_interplevel_subclass._annspecialcase_ = "specialize:memo"
 
 def _buildusercls(cls, hasdict, wants_slots):
     "NOT_RPYTHON: initialization-time only"
@@ -109,6 +109,7 @@
         return object.__new__(cls)
     else:
         return new.instance(cls)
+instantiate._annspecialcase_ = "override:instantiate"
 
 def make_descr_typecheck_wrapper(func, extraargs=(), cls=None):
     if func is None:

Modified: pypy/dist/pypy/module/sys/state.py
==============================================================================
--- pypy/dist/pypy/module/sys/state.py	(original)
+++ pypy/dist/pypy/module/sys/state.py	Mon Jun  6 05:08:52 2005
@@ -80,6 +80,7 @@
     """NOT_RPYTHON"""
     from pypy.tool.udir import udir
     return space.wrap(str(udir))
+pypy_getudir._annspecialcase_ = "override:ignore"
 
 def getdefaultencoding(space): 
     return space.wrap(sys.getdefaultencoding())

Modified: pypy/dist/pypy/objspace/std/fake.py
==============================================================================
--- pypy/dist/pypy/objspace/std/fake.py	(original)
+++ pypy/dist/pypy/objspace/std/fake.py	Mon Jun  6 05:08:52 2005
@@ -17,7 +17,7 @@
         return space.gettypeobject(ft.typedef)
     ft = fake_type(type(x))
     return ft(space, x)
-
+fake_object._annspecialcase_ = "override:fake_object"
 
 import sys
 
@@ -43,6 +43,7 @@
         w_exc = space.wrap(exc)
         w_value = space.wrap(value)
     raise OperationError, OperationError(w_exc, w_value), tb
+wrap_exception._annspecialcase_ = "override:ignore"
 
 def fake_type(cpy_type):
     assert type(cpy_type) is type

Modified: pypy/dist/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/std/objspace.py	(original)
+++ pypy/dist/pypy/objspace/std/objspace.py	Mon Jun  6 05:08:52 2005
@@ -238,7 +238,7 @@
         from fake import fake_object
         return fake_object(self, x)
 
-    wrap._specialize_ = "argtypes"
+    wrap._annspecialcase_ = "specialize:argtype1"
 
     def wrap_exception_cls(self, x):
         """NOT_RPYTHON"""
@@ -246,6 +246,7 @@
             w_result = getattr(self, 'w_' + x.__name__)            
             return w_result
         return None
+    wrap_exception_cls._annspecialcase_ = "override:wrap_exception_cls"
         
     def unwrap(self, w_obj):
         if isinstance(w_obj, BaseWrappable):
@@ -324,8 +325,7 @@
             instance.user_setup(self, w_subtype, w_subtype.nslots)
         assert isinstance(instance, cls)
         return instance
-    allocate_instance._specialize_ = "location"
-            
+    allocate_instance._annspecialcase_ = "specialize:arg1"
 
     def unpacktuple(self, w_tuple, expected_length=-1):
         assert isinstance(w_tuple, W_TupleObject)

Modified: pypy/dist/pypy/tool/cache.py
==============================================================================
--- pypy/dist/pypy/tool/cache.py	(original)
+++ pypy/dist/pypy/tool/cache.py	Mon Jun  6 05:08:52 2005
@@ -37,7 +37,7 @@
             result = self._build(key)
             self.content[key] = result
             return result
-    getorbuild._specialize_ = "memo"
+    getorbuild._annspecialcase_ = "specialize:memo"
 
     def _freeze_(self):
         # needs to be SomePBC, but otherwise we can't really freeze the

Modified: pypy/dist/pypy/translator/ann_override.py
==============================================================================
--- pypy/dist/pypy/translator/ann_override.py	(original)
+++ pypy/dist/pypy/translator/ann_override.py	Mon Jun  6 05:08:52 2005
@@ -1,55 +1,39 @@
 # overrides for annotation specific to PyPy codebase
+from pypy.annotation.policy import AnnotatorPolicy
 from pypy.annotation.bookkeeper import getbookkeeper
 from pypy.annotation import model as annmodel
+from pypy.annotation import specialize
 
-from pypy.interpreter import error
-from pypy.interpreter import pyframe
-from pypy.objspace.std import fake
-from pypy.module.sys import state as sys_state
-import pypy.interpreter.typedef as itypedef
-import pypy.interpreter.pycode as pycode
-import pypy.interpreter.compiler as icompiler
-from pypy.objspace.std.objspace import StdObjSpace
-
-def ignore(*args):
-    bk = getbookkeeper()
-    return bk.immutablevalue(None)
-
-def instantiate(clspbc):
-    assert isinstance(clspbc, annmodel.SomePBC)
-    clsdef = None
-    for cls, v in clspbc.prebuiltinstances.items():
-        if not clsdef:
-            clsdef = getbookkeeper().getclassdef(cls)
-        else:
-            clsdef = clsdef.commonbase(getbookkeeper().getclassdef(cls))
-    return annmodel.SomeInstance(clsdef)
-
-def wrap_exception_cls(space, x):
-    import pypy.objspace.std.typeobject as typeobject
-    clsdef = getbookkeeper().getclassdef(typeobject.W_TypeObject)
-    return annmodel.SomeInstance(clsdef, can_be_None=True)
-
-def fake_object(space, x):
-    clsdef = getbookkeeper().getclassdef(itypedef.W_Root)
-    return annmodel.SomeInstance(clsdef)    
-
-def cpy_compile(self, source, filename, mode, flags):
-    clsdef = getbookkeeper().getclassdef(pycode.PyCode)
-    return annmodel.SomeInstance(clsdef)    
-
-pypy_overrides = {}
-
-def install(tgt, override):
-    if hasattr(tgt, 'im_func'):
-        tgt = tgt.im_func
-    pypy_overrides[tgt] = override
-
-install(pyframe.cpython_tb, ignore)
-install(error.OperationError.record_interpreter_traceback, ignore)
-install(sys_state.pypy_getudir, ignore)
-install(fake.wrap_exception, ignore)
-install(fake.fake_object, fake_object)
-install(itypedef.instantiate, instantiate)
-install(StdObjSpace.wrap_exception_cls, wrap_exception_cls)
-install(icompiler.CPythonCompiler.compile, cpy_compile)
+class PyPyAnnotatorPolicy(AnnotatorPolicy):
+
+    def override__ignore(pol, *args):
+        bk = getbookkeeper()
+        return bk.immutablevalue(None)
+
+    def override__instantiate(pol, clspbc):
+        assert isinstance(clspbc, annmodel.SomePBC)
+        clsdef = None
+        for cls, v in clspbc.prebuiltinstances.items():
+            if not clsdef:
+                clsdef = getbookkeeper().getclassdef(cls)
+            else:
+                clsdef = clsdef.commonbase(getbookkeeper().getclassdef(cls))
+        return annmodel.SomeInstance(clsdef)
+
+    def override__wrap_exception_cls(pol, space, x):
+        import pypy.objspace.std.typeobject as typeobject
+        clsdef = getbookkeeper().getclassdef(typeobject.W_TypeObject)
+        return annmodel.SomeInstance(clsdef, can_be_None=True)
+
+    def override__fake_object(pol, space, x):
+        from pypy.interpreter import typedef
+        clsdef = getbookkeeper().getclassdef(typedef.W_Root)
+        return annmodel.SomeInstance(clsdef)    
+
+    def override__cpy_compile(pol, self, source, filename, mode, flags):
+        from pypy.interpreter import pycode
+        clsdef = getbookkeeper().getclassdef(pycode.PyCode)
+        return annmodel.SomeInstance(clsdef)    
+
+    specialize__arg1 = staticmethod(specialize.argvalue(1))
+    specialize__argtype1 = staticmethod(specialize.argtype(1))

Modified: pypy/dist/pypy/translator/annrpython.py
==============================================================================
--- pypy/dist/pypy/translator/annrpython.py	(original)
+++ pypy/dist/pypy/translator/annrpython.py	Mon Jun  6 05:08:52 2005
@@ -12,12 +12,18 @@
 class AnnotatorError(Exception):
     pass
 
+class NullAnnotatorPolicy:
+    def override(pol, func, inputcells):
+        return None
+    
+    def specialize(pol, bookkeeper, spaceop, func, args, mono):
+        return None
 
 class RPythonAnnotator:
     """Block annotator for RPython.
     See description in doc/translation/annotation.txt."""
 
-    def __init__(self, translator=None, overrides={}):
+    def __init__(self, translator=None, policy = None):
         self.translator = translator
         self.pendingblocks = {}  # map {block: function}
         self.bindings = {}       # map Variables to SomeValues
@@ -42,21 +48,20 @@
         # --- end of debugging information ---
         self.bookkeeper = Bookkeeper(self)
         # user-supplied annotation logic for functions we don't want to flow into
-        self.overrides = overrides
+        if policy is None:
+            self.policy = NullAnnotatorPolicy()
+        else:
+            self.policy = policy
 
     def __getstate__(self):
         attrs = """translator pendingblocks bindings annotated links_followed
-        notify bookkeeper overrides""".split()
+        notify bookkeeper policy""".split()
         ret = self.__dict__.copy()
         for key, value in ret.items():
             if key not in attrs:
                 assert type(value) is dict, ("please update %s.__getstate__" %
                                              self.__class__.__name__)
                 ret[key] = {}
-        # special case: clean up the overrides which would trigger bad imports
-        overrides = ret['overrides'] = {}
-        for key in self.overrides:
-            overrides[key] = None
         return ret
 
     def _register_returnvar(self, flowgraph, func):
@@ -232,9 +237,9 @@
     #___ interface for annotator.bookkeeper _______
 
     def recursivecall(self, func, position_key, inputcells):
-        override = self.overrides.get(func, None)
-        if override is not None:
-            return override(*inputcells)
+        overriden = self.policy.override(func, inputcells)
+        if overriden is not None:
+            return overriden
         parent_fn, parent_block, parent_index = position_key
         graph = self.getflowgraph(func, parent_fn, position_key)
         # self.notify[graph.returnblock] is a dictionary of call

Modified: pypy/dist/pypy/translator/goal/translate_pypy.py
==============================================================================
--- pypy/dist/pypy/translator/goal/translate_pypy.py	(original)
+++ pypy/dist/pypy/translator/goal/translate_pypy.py	Mon Jun  6 05:08:52 2005
@@ -59,7 +59,7 @@
 import threading, pdb
 
 from pypy.translator.translator import Translator
-from pypy.translator.ann_override import pypy_overrides
+from pypy.translator.ann_override import PyPyAnnotatorPolicy
 from pypy.annotation import model as annmodel
 from pypy.tool.cache import Cache
 from pypy.annotation.model import SomeObject
@@ -85,7 +85,7 @@
     if listen_port:
         run_async_server()
     if not options['-no-a']:
-        a = t.annotate(inputtypes, overrides=pypy_overrides)
+        a = t.annotate(inputtypes, policy=PyPyAnnotatorPolicy())
         sanity_check_exceptblocks(t)
         worstblocks_topten(a)
         if not options['-no-s']:

Modified: pypy/dist/pypy/translator/test/snippet.py
==============================================================================
--- pypy/dist/pypy/translator/test/snippet.py	(original)
+++ pypy/dist/pypy/translator/test/snippet.py	Mon Jun  6 05:08:52 2005
@@ -1136,7 +1136,7 @@
 # class specialization
 
 class PolyStk:
-    _specialize_ = 'location'
+    _annspecialcase_ = "specialize:ctr_location"
 
     def __init__(self):
         self.itms = []

Modified: pypy/dist/pypy/translator/test/test_annrpython.py
==============================================================================
--- pypy/dist/pypy/translator/test/test_annrpython.py	(original)
+++ pypy/dist/pypy/translator/test/test_annrpython.py	Mon Jun  6 05:08:52 2005
@@ -5,6 +5,8 @@
 
 from pypy.translator.annrpython import annmodel
 from pypy.translator.translator import Translator
+from pypy.annotation import policy
+from pypy.annotation import specialize
 from pypy.annotation.listdef import ListDef
 from pypy.annotation.dictdef import DictDef
 from pypy.objspace.flow.model import *
@@ -589,7 +591,7 @@
         assert s == a.bookkeeper.immutablevalue(42)
 
     def test_class_spec(self):
-        a = self.RPythonAnnotator()
+        a = self.RPythonAnnotator(policy=policy.AnnotatorPolicy())
         s = a.build_types(snippet.class_spec, [])
         assert s.items[0].knowntype == int
         assert s.items[1].knowntype == str
@@ -668,6 +670,7 @@
         def record_exc(e):
             """NOT_RPYTHON"""
             excs.append(sys.exc_info)
+        record_exc._annspecialcase_ = "override:record_exc"
         def g():
             pass
         def f():
@@ -675,9 +678,12 @@
                 g()
             except Exception, e:
                 record_exc(e)
-        def ann_record_exc(s_e):
-            return a.bookkeeper.immutablevalue(None)
-        a = self.RPythonAnnotator(overrides={record_exc: ann_record_exc})
+        class MyAnnotatorPolicy(policy.AnnotatorPolicy):
+
+            def override__record_exc(pol, s_e):
+                return a.bookkeeper.immutablevalue(None)
+            
+        a = self.RPythonAnnotator(policy=MyAnnotatorPolicy())
         s = a.build_types(f, [])
         assert s.const is None
 
@@ -925,17 +931,37 @@
             i = inst(cls)
             assert isinstance(i, cls)
             return i
-        alloc._specialize_ = "location"
+        alloc._annspecialcase_ = "specialize:arg0"
 
         def f():
             c1 = alloc(C1)
             c2 = alloc(C2)
             return c1,c2
-        a = self.RPythonAnnotator()
+
+        class MyAnnotatorPolicy(policy.AnnotatorPolicy):
+
+            specialize__arg0 = staticmethod(specialize.argvalue(0))
+
+        a = self.RPythonAnnotator(policy=MyAnnotatorPolicy())
         s = a.build_types(f, [])
         assert s.items[0].knowntype == C1
         assert s.items[1].knowntype == C2
 
+        callb = a.getpbccallables()
+        assert alloc not in callb
+        def visit(block):
+            if isinstance(block, Block):
+                for spaceop in block.operations:
+                    if spaceop.opname == "simple_call" and spaceop.args[0] == Constant(alloc):
+                        spec_alloc, memo = a.bookkeeper.query_spaceop_callable(spaceop)
+                        assert not memo
+                        spec_alloc = spec_alloc.const
+                        assert spec_alloc in callb
+                        assert callb[spec_alloc] == {(None, spec_alloc): True}
+                        assert (a.binding(a.translator.getflowgraph(spec_alloc).getreturnvar()).knowntype 
+                                == spaceop.args[1].value)
+        traverse(visit, a.translator.getflowgraph(f))
+
     def test_assert_list_doesnt_lose_info(self):
         class T(object):
             pass

Modified: pypy/dist/pypy/translator/translator.py
==============================================================================
--- pypy/dist/pypy/translator/translator.py	(original)
+++ pypy/dist/pypy/translator/translator.py	Mon Jun  6 05:08:52 2005
@@ -114,14 +114,14 @@
             graph = self.getflowgraph(func)
             simplify_graph(graph, passes)
             
-    def annotate(self, input_args_types, func=None, overrides={}):
+    def annotate(self, input_args_types, func=None, policy=None):
         """annotate(self, input_arg_types[, func]) -> Annotator
 
         Provides type information of arguments. Returns annotator.
         """
         func = func or self.entrypoint
         if self.annotator is None:
-            self.annotator = RPythonAnnotator(self, overrides=overrides)
+            self.annotator = RPythonAnnotator(self, policy=policy)
         graph = self.getflowgraph(func)
         self.annotator.build_types(graph, input_args_types, func)
         return self.annotator



More information about the Pypy-commit mailing list