[pypy-svn] r13129 - in pypy/dist/pypy: annotation rpython rpython/test tool translator

arigo at codespeak.net arigo at codespeak.net
Mon Jun 6 23:33:21 CEST 2005


Author: arigo
Date: Mon Jun  6 23:33:14 2005
New Revision: 13129

Added:
   pypy/dist/pypy/rpython/annlowlevel.py   (contents, props changed)
   pypy/dist/pypy/rpython/normalizecalls.py   (contents, props changed)
   pypy/dist/pypy/rpython/test/test_normalizecalls.py   (contents, props changed)
Modified:
   pypy/dist/pypy/annotation/bookkeeper.py
   pypy/dist/pypy/annotation/policy.py
   pypy/dist/pypy/rpython/rclass.py
   pypy/dist/pypy/rpython/rpbc.py
   pypy/dist/pypy/rpython/rtyper.py
   pypy/dist/pypy/rpython/test/test_rpbc.py
   pypy/dist/pypy/tool/sourcetools.py
   pypy/dist/pypy/tool/unionfind.py
   pypy/dist/pypy/translator/annrpython.py
Log:
- normalizecalls:
     Make sure that all functions called in a group have exactly
     the same signature, by hacking their flow graphs if needed.
     Messy but quite needed.

- started a LowLevelAnnotatorPolicy.

- bookkeeper.compute_at_fixpoint() can now be called several times on
  the same annotator.

- UnionFind mapping interface; has_varargs() and has_varkeywords() in
  sourcetools.

- couple of bug fixes.

(this is a catch-all intermediate check-in; the test we intended to
pass -- virtual method call -- still doesn't pass)


Modified: pypy/dist/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/dist/pypy/annotation/bookkeeper.py	(original)
+++ pypy/dist/pypy/annotation/bookkeeper.py	Mon Jun  6 23:33:14 2005
@@ -85,14 +85,17 @@
         del self.position_key
 
     def compute_at_fixpoint(self):
-        self.pbc_maximal_call_families = UnionFind(PBCCallFamily)
-        self.pbc_callables = {}
+        if self.pbc_maximal_call_families is None:
+            self.pbc_maximal_call_families = UnionFind(PBCCallFamily)
+        if self.pbc_callables is None:
+            self.pbc_callables = {}
 
         for (fn, block, i), shape in self.pbc_call_sites.iteritems():
             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)
+        self.pbc_call_sites = {}
 
     def getclassdef(self, cls):
         """Get the ClassDef associated with the given user cls."""

Modified: pypy/dist/pypy/annotation/policy.py
==============================================================================
--- pypy/dist/pypy/annotation/policy.py	(original)
+++ pypy/dist/pypy/annotation/policy.py	Mon Jun  6 23:33:14 2005
@@ -1,7 +1,8 @@
 # base annotation policy for overrides and specialization
 from pypy.annotation.specialize import memo, ctr_location, default_specialize as default
+from pypy.translator.annrpython import BasicAnnotatorPolicy
 
-class AnnotatorPolicy:
+class AnnotatorPolicy(BasicAnnotatorPolicy):
     """
     Possibly subclass and pass an instance to the annotator to control special casing during annotation
     """

Added: pypy/dist/pypy/rpython/annlowlevel.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/annlowlevel.py	Mon Jun  6 23:33:14 2005
@@ -0,0 +1,20 @@
+"""
+The code needed to flow and annotate low-level helpers -- the ll_*() functions
+"""
+
+##from pypy.translator.annrpython import BasicAnnotatorPolicy
+
+
+##class LowLevelAnnotatorPolicy(BasicAnnotatorPolicy):
+
+##    def compute_at_fixpoint(self, annotator):
+##        pass
+
+
+def annotate_lowlevel_helper(annotator, ll_function, args_s):
+##    saved = annotator.policy
+##    annotator.policy = LowLevelAnnotatorPolicy()
+##    try:
+    annotator.build_types(ll_function, args_s)
+##    finally:
+##        annotator.policy = saved

Added: pypy/dist/pypy/rpython/normalizecalls.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/normalizecalls.py	Mon Jun  6 23:33:14 2005
@@ -0,0 +1,100 @@
+from pypy.objspace.flow.model import Variable, Constant, Block, Link
+from pypy.objspace.flow.model import SpaceOperation, checkgraph
+from pypy.annotation.model import *
+from pypy.tool.sourcetools import has_varargs
+from pypy.rpython.rmodel import TyperError
+
+
+def normalize_function_signatures(annotator):
+    # make sure that all functions called in a group have exactly
+    # the same signature, by hacking their flow graphs if needed
+    callables = annotator.getpbccallables()
+    call_families = annotator.getpbccallfamilies()
+    # for methods, we create or complete a corresponding function-only
+    # family with call patterns that have the extra 'self' argument
+    for family in call_families.infos():
+        prevkey = None
+        for classdef, func in family.objects:
+            if classdef is not None:
+                # add (None, func) to the func_family
+                if prevkey is None:
+                    prevkey = (None, func)
+                else:
+                    call_families.union((None, func), prevkey)
+        if prevkey is not None:
+            # copy the patterns from family to func_family with the
+            # extra self argument
+            _, _, func_family = call_families.find(prevkey)
+            for pattern in family.patterns:
+                argcount = pattern[0]
+                pattern = (argcount+1,) + pattern[1:]
+                func_family.patterns[pattern] = True
+    # find the most general signature of each family
+    for family in call_families.infos():
+        functions = [func for classdef, func in family.objects
+                          if classdef is None]  # ignore methods now
+        if len(functions) > 1:  # otherwise, nothing to do
+            if len(family.patterns) > 1:
+                raise TyperError("don't support multiple call patterns "
+                                 "to multiple functions for now %r" % (
+                    functions))
+            pattern, = family.patterns
+            shape_cnt, shape_keys, shape_star, shape_stst = pattern
+            assert not shape_keys, "XXX not implemented"
+            assert not shape_star, "XXX not implemented"
+            assert not shape_stst, "XXX not implemented"
+            # for the first 'shape_cnt' arguments we need to generalize to
+            # a common type
+            generalizedargs = []
+            graph_bindings = {}
+            default_values = {}
+            for func in functions:
+                assert not has_varargs(func), "XXX not implemented"
+                graph = annotator.translator.getflowgraph(func)
+                graph_bindings[graph] = [annotator.binding(v)
+                                         for v in graph.getargs()]
+            for i in range(shape_cnt):
+                args_s = []
+                for bindings in graph_bindings.values():
+                    args_s.append(bindings[i])
+                s_value = unionof(*args_s)
+                generalizedargs.append(s_value)
+            for func in functions:
+                graph = annotator.translator.getflowgraph(func)
+                bindings = graph_bindings[graph]
+                if generalizedargs != bindings: #NB. bindings can also be longer
+                    oldblock = graph.startblock
+                    vlist = []
+                    for i in range(len(generalizedargs)):
+                        v = Variable(graph.getargs()[i])
+                        annotator.setbinding(v, generalizedargs[i])
+                        vlist.append(v)
+                    newblock = Block(vlist)
+                    # add the defaults as constants
+                    defaults = func.func_defaults or ()
+                    for i in range(len(generalizedargs), len(bindings)):
+                        try:
+                            default = defaults[i-len(bindings)]
+                        except IndexError:
+                            raise TyperError("call pattern has %d arguments, "
+                                             "but %r takes at least %d "
+                                             "arguments" % (
+                                len(generalizedargs), func,
+                                len(bindings) - len(defaults)))
+                        vlist.append(Constant(default))
+                    newblock.closeblock(Link(vlist, oldblock))
+                    oldblock.isstartblock = False
+                    newblock.isstartblock = True
+                    graph.startblock = newblock
+                    # finished
+                    checkgraph(graph)
+                    annotator.annotated[newblock] = annotator.annotated[oldblock]
+                # XXX convert the return value too
+
+
+def perform_normalizations(annotator):
+    annotator.frozen += 1
+    try:
+        normalize_function_signatures(annotator)
+    finally:
+        annotator.frozen -= 1

Modified: pypy/dist/pypy/rpython/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/rclass.py	(original)
+++ pypy/dist/pypy/rpython/rclass.py	Mon Jun  6 23:33:14 2005
@@ -122,7 +122,13 @@
         self.initialized = True
 
     def prepare_method(self, name, s_value, allmethods):
-        # special-casing for methods
+        # special-casing for methods:
+        #  - a class (read-only) attribute that would contain a PBC
+        #    with {func: classdef...} is probably meant to be used as a
+        #    method, but in corner cases it could be a constant object
+        #    of type MethodType that just sits here in the class.  But
+        #    as MethodType has a custom __get__ too and we don't support
+        #    it, it's a very bad idea anyway.
         if isinstance(s_value, annmodel.SomePBC):
             debound = {}
             count = 0

Modified: pypy/dist/pypy/rpython/rpbc.py
==============================================================================
--- pypy/dist/pypy/rpython/rpbc.py	(original)
+++ pypy/dist/pypy/rpython/rpbc.py	Mon Jun  6 23:33:14 2005
@@ -198,7 +198,7 @@
             vfunc = hop.inputconst(typeOf(f), f)
         else:
             vinst = vlist[0]
-            vcls = self.getfield(vinst, '__class__', hop.llops)
+            vcls = self.r_instance.getfield(vinst, '__class__', hop.llops)
             vfunc = r_class.getclsfield(vcls, self.methodname, hop.llops)
         vlist.insert(0, vfunc)
         return hop.genop('direct_call', vlist, resulttype = rresult)

Modified: pypy/dist/pypy/rpython/rtyper.py
==============================================================================
--- pypy/dist/pypy/rpython/rtyper.py	(original)
+++ pypy/dist/pypy/rpython/rtyper.py	Mon Jun  6 23:33:14 2005
@@ -9,6 +9,8 @@
 from pypy.tool.sourcetools import func_with_new_name, valid_identifier
 from pypy.translator.unsimplify import insert_empty_block
 from pypy.rpython.rmodel import Repr, inputconst, TyperError
+from pypy.rpython.normalizecalls import perform_normalizations
+from pypy.rpython.annlowlevel import annotate_lowlevel_helper
 
 
 debug = False
@@ -71,6 +73,9 @@
 
     def specialize(self):
         """Main entry point: specialize all annotated blocks of the program."""
+        # first make sure that all functions called in a group have exactly
+        # the same signature, by hacking their flow graphs if needed
+        perform_normalizations(self.annotator)
         # new blocks can be created as a result of specialize_block(), so
         # we need to be careful about the loop here.
         already_seen = {}
@@ -340,6 +345,10 @@
             if v is NotImplemented:
                 raise TyperError("don't know how to convert from %r to %r" %
                                  (r_from, r_to))
+            if v.concretetype != r_to.lowleveltype:
+                raise TyperError("bug in convertion from %r to %r: "
+                                 "returned a %r" % (r_from, r_to,
+                                                    v.concretetype))
         return v
 
     def genop(self, opname, args_v, resulttype=None):
@@ -383,8 +392,7 @@
             spec_function = func_with_new_name(ll_function, name)
             # flow and annotate (the copy of) the low-level function
             self.rtyper.call_all_setups()  # compute ForwardReferences now
-            spec_graph = rtyper.annotator.translator.getflowgraph(spec_function)
-            rtyper.annotator.build_types(spec_function, args_s)
+            annotate_lowlevel_helper(rtyper.annotator, spec_function, args_s)
             # cache the result
             self.rtyper.specialized_ll_functions[spec_key] = spec_function
 

Added: pypy/dist/pypy/rpython/test/test_normalizecalls.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/test/test_normalizecalls.py	Mon Jun  6 23:33:14 2005
@@ -0,0 +1,38 @@
+from pypy.annotation import model as annmodel
+from pypy.translator.translator import Translator
+from pypy.rpython.rtyper import RPythonTyper
+
+
+def rtype(fn, argtypes=[]):
+    t = Translator(fn)
+    t.annotate(argtypes)
+    typer = RPythonTyper(t.annotator)
+    typer.specialize()
+    #t.view()
+    t.checkgraphs()
+    return t
+
+# ____________________________________________________________
+
+def test_normalize_f2_as_taking_string_argument():
+    def f1(l1):
+        pass
+    def f2(l2):
+        pass
+    def g(n):
+        if n > 0:
+            f1("123")
+            f = f1
+        else:
+            f2("b")
+            f = f2
+        f("a")
+
+    translator = rtype(g, [int])
+    f1graph = translator.getflowgraph(f1)
+    f2graph = translator.getflowgraph(f2)
+    s_l1 = translator.annotator.binding(f1graph.getargs()[0])
+    s_l2 = translator.annotator.binding(f2graph.getargs()[0])
+    assert s_l1.__class__ == annmodel.SomeString   # and not SomeChar
+    assert s_l2.__class__ == annmodel.SomeString   # and not SomeChar
+    #translator.view()

Modified: pypy/dist/pypy/rpython/test/test_rpbc.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rpbc.py	(original)
+++ pypy/dist/pypy/rpython/test/test_rpbc.py	Mon Jun  6 23:33:14 2005
@@ -8,7 +8,6 @@
     t.annotate(argtypes)
     typer = RPythonTyper(t.annotator)
     typer.specialize()
-    #t.view()
     t.checkgraphs()
     return t
 
@@ -38,9 +37,23 @@
     def m(self, x):
         return self.z + x
 
+class MySubclass(MyBase):
+    def m(self, x):
+        return self.z - x
+
 def test_method_call():
     def f(a, b):
         obj = MyBase()
         obj.z = a
         return obj.m(b)
-    rtype(f, [int, int]) #.view()
+    rtype(f, [int, int])
+
+def PROGRESSING_ON_test_virtual_method_call():
+    def f(a, b):
+        if a > 0:
+            obj = MyBase()
+        else:
+            obj = MySubclass()
+        obj.z = a
+        return obj.m(b)
+    rtype(f, [int, int]).view()

Modified: pypy/dist/pypy/tool/sourcetools.py
==============================================================================
--- pypy/dist/pypy/tool/sourcetools.py	(original)
+++ pypy/dist/pypy/tool/sourcetools.py	Mon Jun  6 23:33:14 2005
@@ -235,3 +235,14 @@
     if not stuff or ('0' <= stuff[0] <= '9'):
         stuff = '_' + stuff
     return stuff
+
+CO_VARARGS      = 0x0004
+CO_VARKEYWORDS  = 0x0008
+
+def has_varargs(func):
+    func = getattr(func, 'func_code', func)
+    return (func.co_flags & CO_VARARGS) != 0
+
+def has_varkeywords(func):
+    func = getattr(func, 'func_code', func)
+    return (func.co_flags & CO_VARKEYWORDS) != 0

Modified: pypy/dist/pypy/tool/unionfind.py
==============================================================================
--- pypy/dist/pypy/tool/unionfind.py	(original)
+++ pypy/dist/pypy/tool/unionfind.py	Mon Jun  6 23:33:14 2005
@@ -19,6 +19,18 @@
 
         return access
 
+    def __contains__(self, obj):
+        return obj in self.link_to_parent
+
+    def __iter__(self):
+        return iter(self.link_to_parent)
+
+    def keys(self):
+        return self.link_to_parent.keys()
+
+    def infos(self):
+        return self.root_info.values()
+
     def find(self, obj):  # -> new_root, obj, info
         if obj not in self.link_to_parent:
             info = self.root_info[obj] = self.info_factory(obj)

Modified: pypy/dist/pypy/translator/annrpython.py
==============================================================================
--- pypy/dist/pypy/translator/annrpython.py	(original)
+++ pypy/dist/pypy/translator/annrpython.py	Mon Jun  6 23:33:14 2005
@@ -12,13 +12,17 @@
 class AnnotatorError(Exception):
     pass
 
-class NullAnnotatorPolicy:
+class BasicAnnotatorPolicy:
     def override(pol, func, inputcells):
         return None
     
     def specialize(pol, bookkeeper, spaceop, func, args, mono):
         return None
 
+    def compute_at_fixpoint(pol, annotator):
+        annotator.bookkeeper.compute_at_fixpoint()
+
+
 class RPythonAnnotator:
     """Block annotator for RPython.
     See description in doc/translation/annotation.txt."""
@@ -47,9 +51,10 @@
         self.return_bindings = {} # map return Variables to functions
         # --- end of debugging information ---
         self.bookkeeper = Bookkeeper(self)
+        self.frozen = False
         # user-supplied annotation logic for functions we don't want to flow into
         if policy is None:
-            self.policy = NullAnnotatorPolicy()
+            self.policy = BasicAnnotatorPolicy()
         else:
             self.policy = policy
 
@@ -156,6 +161,7 @@
     def addpendingblock(self, fn, block, cells, called_from=None):
         """Register an entry point into block with the given input cells."""
         assert self.translator is None or fn in self.translator.flowgraphs
+        assert not self.frozen
         for a in cells:
             assert isinstance(a, annmodel.SomeObject)
         if block not in self.annotated:
@@ -187,7 +193,7 @@
                 print "++-" * 20
             raise AnnotatorError('%d blocks are still blocked' %
                                  self.annotated.values().count(False))
-        self.bookkeeper.compute_at_fixpoint()
+        self.policy.compute_at_fixpoint(self)
 
     def binding(self, arg, extquery=False):
         "Gives the SomeValue corresponding to the given Variable or Constant."
@@ -341,6 +347,7 @@
             raise
 
     def reflowpendingblock(self, fn, block):
+        assert not self.frozen
         self.pendingblocks[block] = fn
         assert block in self.annotated
         self.annotated[block] = False  # must re-flow



More information about the Pypy-commit mailing list