[pypy-svn] r20245 - in pypy/branch/somepbc-refactoring/pypy: annotation rpython rpython/test translator/test

arigo at codespeak.net arigo at codespeak.net
Fri Nov 25 16:05:14 CET 2005


Author: arigo
Date: Fri Nov 25 16:05:13 2005
New Revision: 20245

Modified:
   pypy/branch/somepbc-refactoring/pypy/annotation/specialize.py
   pypy/branch/somepbc-refactoring/pypy/rpython/normalizecalls.py
   pypy/branch/somepbc-refactoring/pypy/rpython/test/test_rpbc.py
   pypy/branch/somepbc-refactoring/pypy/translator/test/test_annrpython.py
Log:
(mwh, pedronis, arigo)

* added tests about memoized functions with two arguments
* made the tests pass
* fixed a bug in normalizecalls.py (caused by obscurity in the corresponding
  code in the trunk)



Modified: pypy/branch/somepbc-refactoring/pypy/annotation/specialize.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/specialize.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/specialize.py	Fri Nov 25 16:05:13 2005
@@ -56,24 +56,31 @@
 
 def memo(funcdesc, arglist_s):
     """NOT_RPYTHON"""
-    from pypy.annotation.model import unionof, SomePBC, SomeImpossibleValue
+    from pypy.annotation.model import SomePBC, SomeImpossibleValue
     # call the function now, and collect possible results
+    for s in arglist_s:
+        if not isinstance(s, SomePBC):
+            if isinstance(s, SomeImpossibleValue):
+                return s    # we will probably get more possible args later
+            raise Exception("memo call: argument must be a class or a frozen "
+                            "PBC, got %r" % (s,))
+    if len(arglist_s) == 1:
+        return memo_one_argument(funcdesc, *arglist_s)
+    elif len(arglist_s) == 2:
+        return memo_two_arguments(funcdesc, *arglist_s)
+    else:
+        raise Exception("memo call: only 1 or 2 arguments functions supported"
+                        " at the moment (%r)" % (funcdesc,))
+
+def memo_one_argument(funcdesc, s):
+    from pypy.annotation.model import SomeImpossibleValue
     func = funcdesc.pyobj
     if func is None:
         raise Exception("memo call: no Python function object to call (%r)" %
                         (funcdesc,))
-    if len(arglist_s) != 1:
-        raise Exception("memo call: only one-argument functions supported"
-                        " at the moment (%r)" % (funcdesc,))
-    s = arglist_s[0]
-    if not isinstance(s, SomePBC):
-        if isinstance(s, SomeImpossibleValue):
-            return s    # we will probably get more possible args later
-        raise Exception("memo call: argument must be a class or a frozen PBC,"
-                        " got %r" % (s,))
-    # compute the concrete result and store them directly on the descs,
+    # compute the concrete results and store them directly on the descs,
     # using a strange attribute name
-    attrname = '$memo_%s_%d' % (funcdesc.name, uid(funcdesc))
+    attrname = '$memo%d_%s' % (uid(funcdesc), funcdesc.name)
     for desc in s.descriptions:
         s_result = desc.s_read_attribute(attrname)
         if isinstance(s_result, SomeImpossibleValue):
@@ -82,15 +89,65 @@
                 raise Exception("memo call with a class or PBC that has no "
                                 "corresponding Python object (%r)" % (desc,))
             result = func(desc.pyobj)
-            s_result = funcdesc.bookkeeper.immutablevalue(result)
             desc.create_new_attribute(attrname, result)
     # get or build the graph of the function that reads this strange attr
     def memoized(x):
         return getattr(x, attrname)
     def builder(translator, func):
         return translator.buildflowgraph(memoized)   # instead of 'func'
-    return funcdesc.cachedgraph('memo', alt_name='memo_%s' % funcdesc.name, 
-                                        builder=builder)
+    return funcdesc.cachedgraph('memo1', alt_name='memo_%s' % funcdesc.name, 
+                                         builder=builder)
+
+def memo_two_arguments(funcdesc, s1, s2):
+    from pypy.annotation.model import SomeImpossibleValue
+    from pypy.annotation.description import FrozenDesc
+    func = funcdesc.pyobj
+    if func is None:
+        raise Exception("memo call: no Python function object to call (%r)" %
+                        (funcdesc,))
+    # compute the concrete results and store them directly on the descs,
+    # using a strange attribute name.  The goal is to store in the pbcs of
+    # 's1' under the common 'attrname' a reader function; each reader function
+    # will read a field 'attrname2' from the pbcs of 's2', where 'attrname2'
+    # differs for each pbc of 's1'.
+    attrname = '$memoreader%d_%s' % (uid(funcdesc), funcdesc.name)
+    for desc1 in s1.descriptions:
+        attrname2 = '$memofield%d_%d_%s' % (uid(funcdesc), uid(desc1),
+                                            funcdesc.name)
+        s_reader = desc1.s_read_attribute(attrname)
+        if isinstance(s_reader, SomeImpossibleValue):
+            # first time we see this 'desc1': sanity-check 'desc1' and
+            # create its reader function
+            assert isinstance(desc1, FrozenDesc), (
+                "XXX not implemented: memo call with a class as first arg")
+            if desc1.pyobj is None:
+                raise Exception("memo call with a class or PBC that has no "
+                                "corresponding Python object (%r)" % (desc1,))
+            def reader(y, attrname2=attrname2):
+                return getattr(y, attrname2)
+            desc1.create_new_attribute(attrname, reader)
+        for desc2 in s2.descriptions:
+            s_result = desc2.s_read_attribute(attrname2)
+            if isinstance(s_result, SomeImpossibleValue):
+                # first time we see this 'desc1+desc2' combination
+                if desc2.pyobj is None:
+                    raise Exception("memo call with a class or PBC that has no "
+                                  "corresponding Python object (%r)" % (desc2,))
+                # concrete call, to get the concrete result
+                result = func(desc1.pyobj, desc2.pyobj)
+                #print 'func(%s, %s) -> %s' % (desc1.pyobj, desc2.pyobj, result)
+                #print 'goes into %s.%s'% (desc2,attrname2)
+                #print 'with reader %s.%s'% (desc1,attrname)
+                desc2.create_new_attribute(attrname2, result)
+    # get or build the graph of the function that reads this indirect
+    # settings of attributes
+    def memoized(x, y):
+        reader_fn = getattr(x, attrname)
+        return reader_fn(y)
+    def builder(translator, func):
+        return translator.buildflowgraph(memoized)   # instead of 'func'
+    return funcdesc.cachedgraph('memo2', alt_name='memo_%s' % funcdesc.name, 
+                                         builder=builder)
 
 def argvalue(i):
     def specialize_argvalue(funcdesc, args_s):

Modified: pypy/branch/somepbc-refactoring/pypy/rpython/normalizecalls.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/rpython/normalizecalls.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/rpython/normalizecalls.py	Fri Nov 25 16:05:13 2005
@@ -127,7 +127,7 @@
             assert i not in argorder
             argorder.append(i)
         need_reordering = (argorder != range(call_nbargs))
-        if need_reordering: 
+        if need_reordering or len(graph.getargs()) != call_nbargs:
             oldblock = graph.startblock
             inlist = []
             defaults = graph.defaults or ()

Modified: pypy/branch/somepbc-refactoring/pypy/rpython/test/test_rpbc.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/rpython/test/test_rpbc.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/rpython/test/test_rpbc.py	Fri Nov 25 16:05:13 2005
@@ -259,6 +259,36 @@
     assert res == 7
     res = interpret(f1, [1]) 
     assert res == 3
+    
+def test_call_memoized_function_2():
+    fr1 = Freezing()
+    fr2 = Freezing()
+    br1 = Freezing(); br1.value = 'a'
+    br2 = Freezing(); br2.value = 'b'
+    def getorbuild(key1, key2):
+        a = 1
+        b = 100
+        if key1 is fr1:
+            result = eval("%s+2" % key2.value)
+        else:
+            result = eval("%s+6" % key2.value)
+        return result
+    getorbuild._annspecialcase_ = "specialize:memo"
+
+    def f1(i):
+        if i > 0:
+            fr = fr1
+        else:
+            fr = fr2
+        if i % 2 == 0:
+            br = br1
+        else:
+            br = br2
+        return getorbuild(fr, br)
+
+    for check in [-1, 0, 1, 2]:
+        res = interpret(f1, [check])
+        assert res == f1(check)
 
 def test_call_memoized_cache():
 

Modified: pypy/branch/somepbc-refactoring/pypy/translator/test/test_annrpython.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/translator/test/test_annrpython.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/translator/test/test_annrpython.py	Fri Nov 25 16:05:13 2005
@@ -1758,9 +1758,6 @@
         assert s.const is prebuilt_instance
 
     def test_call_memoized_function(self):
-        class Freezing:
-            def _freeze_(self):
-                return True
         fr1 = Freezing()
         fr2 = Freezing()
         def getorbuild(key):
@@ -1783,6 +1780,36 @@
         s = a.build_types(f1, [int])
         assert s.knowntype == int
 
+    def test_call_memoized_function_2(self):
+        fr1 = Freezing()
+        fr2 = Freezing()
+        br1 = Freezing(); br1.value = 'a'
+        br2 = Freezing(); br2.value = 'b'
+        def getorbuild(key1, key2):
+            a = 1
+            b = 100
+            if key1 is fr1:
+                result = eval("%s+2" % key2.value)
+            else:
+                result = eval("%s+6" % key2.value)
+            return result
+        getorbuild._annspecialcase_ = "specialize:memo"
+
+        def f1(i):
+            if i > 0:
+                fr = fr1
+            else:
+                fr = fr2
+            if i % 2 == 0:
+                br = br1
+            else:
+                br = br2
+            return getorbuild(fr, br)
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(f1, [int])
+        assert s.knowntype == int
+
 def g(n):
     return [0,1,2,n]
 
@@ -1796,3 +1823,7 @@
     return total
 
 constant_unsigned_five = r_uint(5)
+        
+class Freezing:
+    def _freeze_(self):
+        return True



More information about the Pypy-commit mailing list