[pypy-svn] r4322 - in pypy/trunk/src/pypy: annotation translator translator/test

arigo at codespeak.net arigo at codespeak.net
Sat May 8 00:59:52 CEST 2004


Author: arigo
Date: Sat May  8 00:59:51 2004
New Revision: 4322

Modified:
   pypy/trunk/src/pypy/annotation/binaryop.py
   pypy/trunk/src/pypy/annotation/factory.py
   pypy/trunk/src/pypy/annotation/model.py
   pypy/trunk/src/pypy/annotation/unaryop.py
   pypy/trunk/src/pypy/translator/annrpython.py
   pypy/trunk/src/pypy/translator/test/snippet.py
   pypy/trunk/src/pypy/translator/test/test_annrpython.py
   pypy/trunk/src/pypy/translator/translator.py
Log:
Python functions can call other Python functions.
Now RPythonAnnotator is independant on the Translator class; the caching of
flow graphs is done by the Bookkeeper.  It also makes using RPythonAnnotator
easier: the tests in translator/test/test_annrpython.py are simplified.

Untestedly, it should also support finite unions of functions, i.e. code like

   fn = dispatcher_list[index]
   fn(args)



Modified: pypy/trunk/src/pypy/annotation/binaryop.py
==============================================================================
--- pypy/trunk/src/pypy/annotation/binaryop.py	(original)
+++ pypy/trunk/src/pypy/annotation/binaryop.py	Sat May  8 00:59:51 2004
@@ -6,7 +6,7 @@
 from pypy.annotation.model import SomeObject, SomeInteger, SomeBool
 from pypy.annotation.model import SomeString, SomeList
 from pypy.annotation.model import SomeTuple, SomeImpossibleValue
-from pypy.annotation.model import SomeInstance
+from pypy.annotation.model import SomeInstance, SomeFunction
 from pypy.annotation.model import set, setunion, missing_operation
 from pypy.annotation.factory import BlockedInference
 
@@ -26,6 +26,9 @@
     def union((obj1, obj2)):
         return SomeObject()
 
+    def inplace_add((obj1, obj2)):
+        return pair(obj1, obj2).add()   # default
+
 
 class __extend__(pairtype(SomeInteger, SomeInteger)):
 
@@ -128,6 +131,12 @@
         return SomeInstance(basedef)
 
 
+class __extend__(pairtype(SomeFunction, SomeFunction)):
+
+    def union((fun1, fun2)):
+        return SomeFunction(setunion(fun1.funcs, fun2.funcs))
+
+
 class __extend__(pairtype(SomeImpossibleValue, SomeObject)):
     def union((imp1, obj2)):
         return obj2

Modified: pypy/trunk/src/pypy/annotation/factory.py
==============================================================================
--- pypy/trunk/src/pypy/annotation/factory.py	(original)
+++ pypy/trunk/src/pypy/annotation/factory.py	Sat May  8 00:59:51 2004
@@ -20,7 +20,10 @@
     def __init__(self, factories = ()):
         # factories that need to be invalidated
         self.invalidatefactories = factories
-        self.position_key = getattr(getbookkeeper(), 'position_key', None)
+        try:
+            self.break_at = getbookkeeper().position_key
+        except AttributeError:
+            self.break_at = None
 
 
 class Bookkeeper:
@@ -31,9 +34,11 @@
 
     Currently used for factories and user-defined classes."""
 
-    def __init__(self):
+    def __init__(self, annotator):
+        self.annotator = annotator
         self.creationpoints = {} # map positions-in-blocks to Factories
         self.userclasses = {}    # map classes to ClassDefs
+        self.flowgraphs = {}     # map functions to flow graphs
 
     def enter(self, position_key):
         """Start of an operation.
@@ -80,6 +85,14 @@
             self.userclasses[cls] = ClassDef(cls, self)
             return self.userclasses[cls]
 
+    def getflowgraph(self, func):
+        """Get the flow graph associated with the given Python func."""
+        try:
+            return self.flowgraphs[func]
+        except KeyError:
+            self.flowgraphs[func] = self.annotator.buildflowgraph(func)
+            return self.flowgraphs[func]
+
 
 def getbookkeeper():
     """Get the current Bookkeeper.
@@ -101,10 +114,20 @@
         self.s_item = pair(self.s_item, s_new_item).union()
 
 
+class FuncCallFactory:
+
+    def pycall(self, func, arglist):
+        bookkeeper = getbookkeeper()
+        graph = bookkeeper.getflowgraph(func)
+        graph.funccallfactories[self] = True
+        bookkeeper.annotator.generalizeinputargs(graph, arglist)
+        return bookkeeper.annotator.getoutputvalue(graph)
+
+
 class InstanceFactory:
 
-    def __init__(self, classdef):
-        self.classdef = classdef
+    def __init__(self, cls):
+        self.classdef = getbookkeeper().getclassdef(cls)
         self.classdef.instancefactories[self] = True
 
     def create(self):

Modified: pypy/trunk/src/pypy/annotation/model.py
==============================================================================
--- pypy/trunk/src/pypy/annotation/model.py	(original)
+++ pypy/trunk/src/pypy/annotation/model.py	Sat May  8 00:59:51 2004
@@ -28,7 +28,7 @@
 #
 
 
-from types import ClassType, BuiltinFunctionType
+from types import ClassType, BuiltinFunctionType, FunctionType
 from pypy.annotation.pairtype import pair, extendabletype
 
 
@@ -100,6 +100,12 @@
     def __init__(self, analyser):
         self.analyser = analyser
 
+class SomeFunction(SomeObject):
+    "Stands for a Python function (or some function out of a list)."
+    knowntype = FunctionType
+    def __init__(self, funcs):
+        self.funcs = funcs   # set of functions that this one may be
+
 class SomeImpossibleValue(SomeObject):
     """The empty set.  Instances are placeholders for objects that
     will never show up at run-time, e.g. elements of an empty list."""
@@ -119,6 +125,8 @@
         result = SomeBuiltin(BUILTIN_FUNCTIONS[x])
     elif isinstance(x, (type, ClassType)) and x.__module__ != '__builtin__':
         result = SomeClass(x)
+    elif isinstance(x, FunctionType):
+        result = SomeFunction({x: True})
     else:
         result = SomeObject()
     result.const = x

Modified: pypy/trunk/src/pypy/annotation/unaryop.py
==============================================================================
--- pypy/trunk/src/pypy/annotation/unaryop.py	(original)
+++ pypy/trunk/src/pypy/annotation/unaryop.py	Sat May  8 00:59:51 2004
@@ -7,10 +7,11 @@
 from pypy.annotation.model import SomeString, SomeList
 from pypy.annotation.model import SomeTuple, SomeImpossibleValue
 from pypy.annotation.model import SomeInstance, SomeBuiltin, SomeClass
+from pypy.annotation.model import SomeFunction
 from pypy.annotation.model import immutablevalue, decode_simple_call
 from pypy.annotation.model import set, setunion, missing_operation
-from pypy.annotation.factory import BlockedInference
-from pypy.annotation.factory import InstanceFactory, getbookkeeper
+from pypy.annotation.factory import BlockedInference, getbookkeeper
+from pypy.annotation.factory import InstanceFactory, FuncCallFactory
 
 
 UNARY_OPERATIONS = set(['len', 'is_true', 'getattr', 'setattr', 'call'])
@@ -52,7 +53,7 @@
 
     def currentdef(ins):
         if ins.revision != ins.classdef.revision:
-            print ins.revision, ins.classdef.revision
+            #print ins.revision, ins.classdef.revision
             raise BlockedInference()
         return ins.classdef
 
@@ -103,7 +104,18 @@
 
     def call(cls, args, kwds):
         # XXX flow into __init__
-        bookkeeper = getbookkeeper()
-        classdef = bookkeeper.getclassdef(cls.cls)
-        factory = bookkeeper.getfactory(InstanceFactory, classdef)
+        factory = getbookkeeper().getfactory(InstanceFactory, cls.cls)
         return factory.create()
+
+
+class __extend__(SomeFunction):
+
+    def call(fun, args, kwds):
+        arglist = decode_simple_call(args, kwds)
+        assert arglist is not None
+        factory = getbookkeeper().getfactory(FuncCallFactory)
+        s_result = SomeImpossibleValue()
+        for func in fun.funcs:
+            s_next_result = factory.pycall(func, arglist)
+            s_result = pair(s_result, s_next_result).union()
+        return s_result

Modified: pypy/trunk/src/pypy/translator/annrpython.py
==============================================================================
--- pypy/trunk/src/pypy/translator/annrpython.py	(original)
+++ pypy/trunk/src/pypy/translator/annrpython.py	Sat May  8 00:59:51 2004
@@ -5,8 +5,9 @@
 from pypy.annotation.model import pair
 from pypy.annotation.factory import ListFactory, InstanceFactory
 from pypy.annotation.factory import BlockedInference, Bookkeeper
+from pypy.objspace.flow import FlowObjSpace
 from pypy.objspace.flow.model import Variable, Constant, UndefinedConstant
-from pypy.objspace.flow.model import SpaceOperation
+from pypy.objspace.flow.model import SpaceOperation, FunctionGraph
 
 
 class AnnotatorError(Exception):
@@ -17,28 +18,37 @@
     """Block annotator for RPython.
     See description in doc/transation/annotation.txt."""
 
-    def __init__(self, translator=None):
+    def __init__(self):
         self.pendingblocks = []  # list of (block, list-of-SomeValues-args)
         self.bindings = {}       # map Variables to SomeValues
         self.annotated = {}      # set of blocks already seen
-        self.bookkeeper = Bookkeeper()
-        self.translator = translator
+        self.notify = {}         # {block: {factory-to-invalidate-when-done}}
+        self.bookkeeper = Bookkeeper(self)
 
     #___ convenience high-level interface __________________
 
-    def build_types(self, flowgraph, input_arg_types):
+    def build_types(self, func, input_arg_types):
         """Recursively build annotations about the specific entry point."""
+        if not isinstance(func, FunctionGraph):
+            flowgraph = self.bookkeeper.getflowgraph(func)
+        else:
+            flowgraph = func
         # make input arguments and set their type
         input_arg_types = list(input_arg_types)
         nbarg = len(flowgraph.getargs())
         while len(input_arg_types) < nbarg:
             input_arg_types.append(object)
-        inputcells = [annmodel.valueoftype(t) for t in input_arg_types]
+        inputcells = []
+        for t in input_arg_types:
+            if not isinstance(t, annmodel.SomeObject):
+                t = annmodel.valueoftype(t)
+            inputcells.append(t)
         
         # register the entry point
         self.addpendingblock(flowgraph.startblock, inputcells)
         # recursively proceed until no more pending block is left
         self.complete()
+        return self.binding(flowgraph.getreturnvar())
 
     def gettype(self, variable):
         """Return the known type of a control flow graph variable,
@@ -99,6 +109,24 @@
             raise TypeError, 'Variable or Constant expected, got %r' % (arg,)
 
 
+    #___ interface for annotator.factory _______
+
+    def buildflowgraph(self, func):
+        space = FlowObjSpace()
+        graph = space.build_flow(func)
+        self.notify[graph.returnblock] = graph.funccallfactories = {}
+        return graph
+
+    def generalizeinputargs(self, flowgraph, inputcells):
+        block = flowgraph.startblock
+        assert len(inputcells) == len(block.inputargs)
+        self.addpendingblock(block, inputcells)
+
+    def getoutputvalue(self, flowgraph):
+        v = flowgraph.getreturnvar()
+        return self.bindings.get(v, annmodel.SomeImpossibleValue())
+
+
     #___ simplification (should be moved elsewhere?) _______
 
     # it should be!
@@ -158,7 +186,7 @@
                 self.flowin(block)
             except BlockedInference, e:
                 #print '_'*60
-                #print 'Blocked at %r:' % (e.position_key,)
+                #print 'Blocked at %r:' % (e.break_at,)
                 #import traceback, sys
                 #traceback.print_tb(sys.exc_info()[2])
                 self.annotated[block] = False   # failed, hopefully temporarily
@@ -197,6 +225,13 @@
         for link in block.exits:
             cells = [self.binding(a) for a in link.args]
             self.addpendingblock(link.target, cells)
+        if block in self.notify:
+            # invalidate some factories when this block is done
+            factories = self.notify[block].keys()
+            self.notify[block].clear()  # don't del: the dict can be re-populated
+            for factory in factories:
+                oldblock, oldindex = factory.position_key
+                self.reflowpendingblock(oldblock)
 
 
     #___ creating the annotations based on operations ______

Modified: pypy/trunk/src/pypy/translator/test/snippet.py
==============================================================================
--- pypy/trunk/src/pypy/translator/test/snippet.py	(original)
+++ pypy/trunk/src/pypy/translator/test/snippet.py	Sat May  8 00:59:51 2004
@@ -267,6 +267,12 @@
     else:
         return n * factorial(n-1)
 
+def factorial2(n):   # analysed in a different order
+    if n > 1:
+        return n * factorial(n-1)
+    else:
+        return 1
+
 def append_five(lst):
     lst += [5]
 

Modified: pypy/trunk/src/pypy/translator/test/test_annrpython.py
==============================================================================
--- pypy/trunk/src/pypy/translator/test/test_annrpython.py	(original)
+++ pypy/trunk/src/pypy/translator/test/test_annrpython.py	Sat May  8 00:59:51 2004
@@ -109,13 +109,11 @@
         a.build_types(fun, [int])
         self.assertEquals(a.gettype(fun.getreturnvar()), int)
 
-    #def test_simplify_calls(self):
-    #    fun = self.make_fun(f_calls_g)
-    #    a = RPythonAnnotator()
-    #    a.build_types(fun, [int])
-    #    a.simplify_calls()
-    #    #self.reallyshow(fun)
-    # XXX write test_transform.py
+    def test_f_calls_g(self):
+        a = RPythonAnnotator()
+        s = a.build_types(f_calls_g, [int])
+        # result should be an integer
+        self.assertEquals(s.knowntype, int)
 
     def test_lists(self):
         fun = self.make_fun(snippet.poor_man_rev_range)
@@ -127,70 +125,59 @@
         self.assertEquals(end_cell.s_item.knowntype, int)
 
     def test_factorial(self):
-        translator = Translator(snippet.factorial)
-        graph = translator.getflowgraph()
-        a = RPythonAnnotator(translator)
-        a.build_types(graph, [int])
+        a = RPythonAnnotator()
+        s = a.build_types(snippet.factorial, [int])
         # result should be an integer
-        self.assertEquals(a.gettype(graph.getreturnvar()), int)
+        self.assertEquals(s.knowntype, int)
+
+    def test_factorial2(self):
+        a = RPythonAnnotator()
+        s = a.build_types(snippet.factorial2, [int])
+        # result should be an integer
+        self.assertEquals(s.knowntype, int)
 
     def test_build_instance(self):
-        translator = Translator(snippet.build_instance)
-        graph = translator.getflowgraph()
-        a = RPythonAnnotator(translator)
-        a.build_types(graph, [])
+        a = RPythonAnnotator()
+        s = a.build_types(snippet.build_instance, [])
         # result should be a snippet.C instance
-        self.assertEquals(a.gettype(graph.getreturnvar()), snippet.C)
+        self.assertEquals(s.knowntype, snippet.C)
 
     def test_set_attr(self):
-        translator = Translator(snippet.set_attr)
-        graph = translator.getflowgraph()
-        a = RPythonAnnotator(translator)
-        a.build_types(graph, [])
+        a = RPythonAnnotator()
+        s = a.build_types(snippet.set_attr, [])
         # result should be an integer
-        self.assertEquals(a.gettype(graph.getreturnvar()), int)
+        self.assertEquals(s.knowntype, int)
 
     def test_merge_setattr(self):
-        translator = Translator(snippet.merge_setattr)
-        graph = translator.getflowgraph()
-        a = RPythonAnnotator(translator)
-        a.build_types(graph, [int])
+        a = RPythonAnnotator()
+        s = a.build_types(snippet.merge_setattr, [int])
         # result should be an integer
-        self.assertEquals(a.gettype(graph.getreturnvar()), int)
+        self.assertEquals(s.knowntype, int)
 
     def test_inheritance1(self):
-        translator = Translator(snippet.inheritance1)
-        graph = translator.getflowgraph()
-        a = RPythonAnnotator(translator)
-        a.build_types(graph, [])
+        a = RPythonAnnotator()
+        s = a.build_types(snippet.inheritance1, [])
         # result should be exactly:
-        self.assertEquals(a.binding(graph.getreturnvar()),
-                          annmodel.SomeTuple([
-                              annmodel.SomeTuple([]),
-                              annmodel.SomeInteger()
-                              ]))
+        self.assertEquals(s, annmodel.SomeTuple([
+                                annmodel.SomeTuple([]),
+                                annmodel.SomeInteger()
+                                ]))
 
     def test_inheritance2(self):
-        translator = Translator(snippet.inheritance2)
-        graph = translator.getflowgraph()
-        a = RPythonAnnotator(translator)
-        a.build_types(graph, [])
+        a = RPythonAnnotator()
+        s = a.build_types(snippet.inheritance2, [])
         # result should be exactly:
-        self.assertEquals(a.binding(graph.getreturnvar()),
-                          annmodel.SomeTuple([
-                              annmodel.SomeInteger(),
-                              annmodel.SomeObject()
-                              ]))
+        self.assertEquals(s, annmodel.SomeTuple([
+                                annmodel.SomeInteger(),
+                                annmodel.SomeObject()
+                                ]))
 
     def test_poor_man_range(self):
-        translator = Translator(snippet.poor_man_range)
-        graph = translator.getflowgraph()
-        a = RPythonAnnotator(translator)
-        a.build_types(graph, [int])
+        a = RPythonAnnotator()
+        s = a.build_types(snippet.poor_man_range, [int])
         # result should be a list of integers
-        self.assertEquals(a.gettype(graph.getreturnvar()), list)
-        end_cell = a.binding(graph.getreturnvar())
-        self.assertEquals(end_cell.s_item.knowntype, int)
+        self.assertEquals(s.knowntype, list)
+        self.assertEquals(s.s_item.knowntype, int)
 
 
 def g(n):
@@ -198,8 +185,11 @@
 
 def f_calls_g(n):
     total = 0
-    for i in g(n):
+    lst = g(n)
+    i = 0
+    while i < len(lst):
         total += i
+        i += 1
     return total
 
 

Modified: pypy/trunk/src/pypy/translator/translator.py
==============================================================================
--- pypy/trunk/src/pypy/translator/translator.py	(original)
+++ pypy/trunk/src/pypy/translator/translator.py	Sat May  8 00:59:51 2004
@@ -104,7 +104,7 @@
         """
         func = func or self.entrypoint
         if self.annotator is None:
-            self.annotator = RPythonAnnotator(self)
+            self.annotator = RPythonAnnotator()
         graph = self.getflowgraph(func)
         self.annotator.build_types(graph, input_args_types)
         return self.annotator
@@ -141,7 +141,7 @@
         if input_arg_types is None:
             ann = self.annotator
         else:
-            ann = RPythonAnnotator(self)
+            ann = RPythonAnnotator()
         if func is None:
             codes = [self.generatecode1(gencls, input_arg_types,
                                         self.entrypoint, ann)]
@@ -192,16 +192,16 @@
         from dis import dis
         dis(func or self.entrypoint)
 
-    def consider_call(self, ann, func, args):
-        graph = self.getflowgraph(func)
-        ann.addpendingblock(graph.startblock, args)
-        result_var = graph.getreturnvar()
-        try:
-            return ann.binding(result_var)
-        except KeyError:
-            # typical case for the 1st call, because addpendingblock() did
-            # not actually start the analysis of the called function yet.
-            return impossiblevalue
+##    def consider_call(self, ann, func, args):
+##        graph = self.getflowgraph(func)
+##        ann.addpendingblock(graph.startblock, args)
+##        result_var = graph.getreturnvar()
+##        try:
+##            return ann.binding(result_var)
+##        except KeyError:
+##            # typical case for the 1st call, because addpendingblock() did
+##            # not actually start the analysis of the called function yet.
+##            return impossiblevalue
 
 
 if __name__ == '__main__':


More information about the Pypy-commit mailing list