[pypy-svn] r20169 - in pypy/branch/somepbc-refactoring/pypy: annotation doc/discussion

pedronis at codespeak.net pedronis at codespeak.net
Tue Nov 22 18:13:56 CET 2005


Author: pedronis
Date: Tue Nov 22 18:13:55 2005
New Revision: 20169

Modified:
   pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py
   pypy/branch/somepbc-refactoring/pypy/annotation/description.py
   pypy/branch/somepbc-refactoring/pypy/doc/discussion/somepbc-refactoring-plan.txt
Log:
(arigo, mwh, pedronis)

introduced logic to compute a "call table" describing exactly which graphs need
to be invoked by which FuncDesc at which call point.  Intended to drive the
RTyper for normalisation and the choice of runtime representation for
functions.



Modified: pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py	Tue Nov 22 18:13:55 2005
@@ -189,11 +189,39 @@
         del self.position_key
 
     def compute_at_fixpoint(self):
-        for cls in self.needs_hash_support.keys():
-            for cls2 in self.needs_hash_support:
-                if issubclass(cls, cls2) and cls is not cls2:
-                    del self.needs_hash_support[cls]
-                    break
+        # getbookkeeper() needs to work during this function, so provide
+        # one with a dummy position
+        self.enter(None)
+        try:
+            def call_sites():
+                newblocks = self.annotator.added_blocks
+                if newblocks is None:
+                    newblocks = self.annotator.annotated  # all of them
+                for block in newblocks:
+                    for op in block.operations:
+                        if op.opname in ('simple_call', 'call_args'):
+                            yield op
+
+            for call_op in call_sites():
+                self.consider_call_site(call_op)
+
+            for cls in self.needs_hash_support.keys():
+                for cls2 in self.needs_hash_support:
+                    if issubclass(cls, cls2) and cls is not cls2:
+                        del self.needs_hash_support[cls]
+                        break
+        finally:
+            self.leave()
+
+    def consider_call_site(self, call_op):
+        binding = self.annotator.binding
+        s_callable = binding(call_op.args[0])
+        if isinstance(s_callable, SomePBC):
+            descs = s_callable.descriptions.keys()
+            family = descs[0].getcallfamily()
+            args_s = [binding(arg) for arg in call_op.args[1:]]
+            args = self.build_args(call_op.opname, args_s)
+            s_callable.getKind().consider_call_site(self, family, descs, args)
 
     def getuniqueclassdef(self, cls):
         """Get the ClassDef associated with the given user cls.

Modified: pypy/branch/somepbc-refactoring/pypy/annotation/description.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/description.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/description.py	Tue Nov 22 18:13:55 2005
@@ -14,11 +14,44 @@
         self.descs = { desc: True }
         self.patterns = {}    # set of "call shapes" in the sense of
                               # pypy.interpreter.argument.Argument
+        self.calltables = {}  # see calltable_add_row()
 
     def update(self, other):
         self.descs.update(other.descs)
         self.patterns.update(other.patterns)
 
+    def calltable_add_row(self, callshape, row):
+        # this code builds and updates a table of which graph to call
+        # at which call site.  Each call site gets a row of graphs,
+        # sharable with other call sites.  Each column is a FunctionDesc.
+        # There is one such table per "call shape".
+        table = self.calltables.setdefault(callshape, [])
+        for i, existing_row in enumerate(table):
+            # which row(s) can the new row be merged with?  The
+            # condition is to have at least a common graph, and no
+            # incompatible graphs elsewhere.
+            for desc, graph in row.items():
+                if existing_row.get(desc) is graph:
+                    # common graph.  Do we find incompatible graphs?
+                    merged = row.copy()
+                    merged.update(existing_row)
+                    for merged_desc, merged_graph in merged.items():
+                        if (merged_desc in row and
+                            row[merged_desc] is not merged_graph):
+                            msg = ("incompatible specializations in a call"
+                                   " to %r")
+                            raise Exception(msg % (merged_desc,))
+                    # done.  Start over again, because this expanded row
+                    # could now be merged with other rows...
+                    del table[i]
+                    self.calltable_add_row(callshape, merged)
+                    return
+            #else: no common graph
+        else:
+            # add this as a new row.
+            table.append(row)
+
+
 class AttrFamily:
     """A family of Desc objects that have common 'getattr' sites.
     The attr families are conceptually a partition of FrozenDesc and ClassDesc
@@ -83,6 +116,10 @@
     def bind_under(self, classdef, name):
         return self
 
+    def consider_call_site(bookkeeper, family, descs, args):
+        print "unimplemented consider_call_site for %r" % (descs,) # XXX
+    consider_call_site = staticmethod(consider_call_site)
+
 
 class FunctionDesc(Desc):
     knowntype = types.FunctionType
@@ -152,6 +189,18 @@
         # XXX static methods
         return self.bookkeeper.getmethoddesc(self, classdef, name)
 
+    def consider_call_site(bookkeeper, family, descs, args):
+        from pypy.annotation.model import s_ImpossibleValue
+        # see comments in CallFamily
+        row = {}
+        for desc in descs:
+            def enlist(graph, ignore):
+                row[desc] = graph
+                return s_ImpossibleValue   # meaningless
+            desc.pycall(enlist, args, s_ImpossibleValue)
+        family.calltable_add_row(args.rawshape(), row)
+    consider_call_site = staticmethod(consider_call_site)
+
 
 class ClassDesc(Desc):
     knowntype = type
@@ -346,7 +395,6 @@
         self.bookkeeper.warning("rebinding an already bound %r" % (self,))
         return self.funcdesc.bind_under(classdef, name)
 
-
 def new_or_old_class(c):
     if hasattr(c, '__class__'):
         return c.__class__

Modified: pypy/branch/somepbc-refactoring/pypy/doc/discussion/somepbc-refactoring-plan.txt
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/doc/discussion/somepbc-refactoring-plan.txt	(original)
+++ pypy/branch/somepbc-refactoring/pypy/doc/discussion/somepbc-refactoring-plan.txt	Tue Nov 22 18:13:55 2005
@@ -112,17 +112,43 @@
 RTyping PBCs of functions
 =========================
 
-We have two plans for this; let me start with the one I prefer :-)
-
-The FuncDesc.specialize() method takes an args_s and return a corresponding
-graph.  Currently, the caller of specialize() parses the actual arguments
-provided by the simple_call or call_args operation, so that args_s is a flat
-parsed list.  The returned graph must have the same number and order of input
-variables.
-
-A proposed change: have specialize() return a graph whose input vars directly
-match the arguments of the underlying space operation.  If there is some
-non-trivial shuffling or ???
-
-
-
+The FuncDesc.specialize() method takes an args_s and return a
+corresponding graph.  The caller of specialize() parses the actual
+arguments provided by the simple_call or call_args operation, so that
+args_s is a flat parsed list.  The returned graph must have the same
+number and order of input variables.
+
+For each call family, we compute a table like this (after annotation
+finished)::
+
+          call_shape   FuncDesc1   FuncDesc2   FuncDesc3   ...
+  ----------------------------------------------------------
+   call0    shape1       graph1
+   call1    shape1       graph1      graph2
+   call2    shape1                   graph3     graph4            
+   call3    shape2                   graph5     graph6
+
+
+We then need to merge some of the lines if they look similar enough,
+e.g. call0 and call1.  Precisely, we can merge two lines if they only
+differ in having more or less holes.  In theory, the same graph could
+appear in two lines that are still not mergeable because of other
+graphs.  For sanity of implementation, we should check that at the end
+each graph only appears once in the table (unless there is only one
+*column*, in which case all problems can be dealt with at call sites).
+
+(Note that before this refactoring, the code was essentially requiring
+that the table ended up with either one single row or one single
+column.)
+
+The table is computed when the annotation is complete, in
+compute_at_fixpoint(), which calls the FuncDesc's consider_call_site()
+for each call site.  The latter merges lines as soon as possible.  The
+table is attached to the call family, grouped by call shape.
+
+During RTyping, compute_at_fixpoint() is called after each new ll
+helper is annotated.  Normally, this should not modify existing tables
+too much, but in some situations it will.  So the rule is that
+consider_call_site() should not add new (unmerged) rows to the table
+after the table is considered "finished" (again, unless there is only
+one column, in which case we should not discover new columns).



More information about the Pypy-commit mailing list