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

hpk at codespeak.net hpk at codespeak.net
Fri Jun 24 21:13:39 CEST 2005


Author: hpk
Date: Fri Jun 24 21:13:35 2005
New Revision: 13830

Modified:
   pypy/dist/pypy/annotation/bookkeeper.py
   pypy/dist/pypy/annotation/specialize.py
   pypy/dist/pypy/rpython/normalizecalls.py
   pypy/dist/pypy/rpython/rpbc.py
   pypy/dist/pypy/rpython/rtyper.py
   pypy/dist/pypy/rpython/test/test_llinterp.py
   pypy/dist/pypy/rpython/test/test_rpbc.py
   pypy/dist/pypy/translator/transform.py
Log:
(arigo, hpk with special consulting by pedronis)

- support for specialcase:memo functions (like the often 
  used pypy.tool.cache.Cache.getorbuild function). 
  Currently, only one PBC argument is allowed for such
  functions. Somewhat unfortunately, the support is
  spread across a number of files but it works and
  doesn't really involve a large number of lines. 



Modified: pypy/dist/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/dist/pypy/annotation/bookkeeper.py	(original)
+++ pypy/dist/pypy/annotation/bookkeeper.py	Fri Jun 24 21:13:35 2005
@@ -24,6 +24,7 @@
         self.objects = { obj: True }
         self.read_locations = {}
         self.attrs = {}
+        self.values = {}   # used in the typer 
 
     def update(self, other):
         self.objects.update(other.objects)
@@ -193,6 +194,8 @@
         
         self.pbc_call_sites = {}
 
+        self.memo_tables = []
+
         self.stats = Stats(self)
 
         # import ordering hack

Modified: pypy/dist/pypy/annotation/specialize.py
==============================================================================
--- pypy/dist/pypy/annotation/specialize.py	(original)
+++ pypy/dist/pypy/annotation/specialize.py	Fri Jun 24 21:13:35 2005
@@ -126,6 +126,18 @@
 # ____________________________________________________________________________
 # specializations
 
+class MemoTable:
+    def __init__(self, bookkeeper, func, s_result, arglist_s):
+        self.table = {}
+        self.arglist_s = arglist_s
+        self.s_result = s_result 
+        for arglist in possible_arguments(arglist_s):
+            result = func(*arglist)
+            self.table[arglist] = result
+        bookkeeper.memo_tables.append(self)
+    def _freeze_(self):
+        return True
+
 def memo(bookkeeper, mod, spaceop, func, args, mono):
     """NOT_RPYTHON"""
     assert mono, "not-static call to memoized %s" % func
@@ -140,28 +152,32 @@
         possible_results.append(bookkeeper.immutablevalue(result))
     return unionof(*possible_results), args
 
-def possible_arguments(args):
+def possible_values_of(s):
     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]
+        return [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()
+        return s.prebuiltinstances.keys()
     elif isinstance(s, SomeBool):
-        possible_values = [False, True]
+        return [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:
+        raise ValueError, "memo call with a non-constant arg %r" % (s,)
+
+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.
+    return cartesian_product([possible_values_of(s) for s in args])
+
+def cartesian_product(lstlst):
+    if not lstlst:
+        yield ()
+        return
+    for tuple_tail in cartesian_product(lstlst[1:]):
+        for value in lstlst[0]:
             yield (value,) + tuple_tail
 
 #def argtypes(bookkeeper, spaceop, func, args, mono):

Modified: pypy/dist/pypy/rpython/normalizecalls.py
==============================================================================
--- pypy/dist/pypy/rpython/normalizecalls.py	(original)
+++ pypy/dist/pypy/rpython/normalizecalls.py	Fri Jun 24 21:13:35 2005
@@ -165,9 +165,43 @@
                 annotator.setbinding(graph.getreturnvar(), generalizedresult)
 
 
+def specialize_pbcs_by_memotables(annotator):
+    memo_tables = annotator.bookkeeper.memo_tables
+    access_sets = annotator.getpbcaccesssets()
+    for memo_table in memo_tables: 
+        arglist_s = memo_table.arglist_s 
+        assert len(arglist_s) == 1, "XXX implement >1 arguments" 
+        arg1 = arglist_s[0]
+        assert isinstance(arg1, annmodel.SomePBC)
+
+        if None in arg1.prebuiltinstances:
+            raise TyperError("unsupported: memo call with an argument that can be None")
+        pbcs = arg1.prebuiltinstances.keys()
+        _, _, access_set = access_sets.find(pbcs[0])
+
+        # enforce a structure where we can uniformly access 
+        # our memofield later 
+        for pbc in pbcs[1:]:
+            _, _, access_set = access_sets.union(pbcs[0], pbc)
+        
+        # we can have multiple memo_tables per PBC 
+        i = 0
+        while 1: 
+            fieldname = "memofield_%d" % i
+            if fieldname in access_set.attrs: 
+                i += 1
+                continue
+            memo_table.fieldname = fieldname
+            break
+        access_set.attrs[fieldname] = memo_table.s_result
+        for pbc in pbcs: 
+            value = memo_table.table[(pbc,)] 
+            access_set.values[(pbc, fieldname)] = value 
+
 def perform_normalizations(annotator):
     annotator.frozen += 1
     try:
         normalize_function_signatures(annotator)
+        specialize_pbcs_by_memotables(annotator) 
     finally:
         annotator.frozen -= 1

Modified: pypy/dist/pypy/rpython/rpbc.py
==============================================================================
--- pypy/dist/pypy/rpython/rpbc.py	(original)
+++ pypy/dist/pypy/rpython/rpbc.py	Fri Jun 24 21:13:35 2005
@@ -5,7 +5,7 @@
 from pypy.objspace.flow.model import Constant
 from pypy.rpython.lltype import typeOf, Void, ForwardReference, Struct, Bool
 from pypy.rpython.lltype import Ptr, malloc, nullptr
-from pypy.rpython.rmodel import Repr, TyperError
+from pypy.rpython.rmodel import Repr, TyperError, inputconst
 from pypy.rpython import rclass
 from pypy.rpython.rtyper import HighLevelOp
 
@@ -166,7 +166,10 @@
             result = malloc(self.pbc_type, immortal=True)
             self.pbc_cache[pbc] = result
             for attr, (mangled_name, r_value) in self.llfieldmap.items():
-                thisattrvalue = getattr(pbc, attr)
+                try: 
+                    thisattrvalue = self.access_set.values[(pbc, attr)] 
+                except KeyError: 
+                    thisattrvalue = getattr(pbc, attr)
                 llvalue = r_value.convert_const(thisattrvalue)
                 setattr(result, mangled_name, llvalue)
             return result
@@ -174,10 +177,13 @@
     def rtype_getattr(self, hop):
         attr = hop.args_s[1].const
         vpbc, vattr = hop.inputargs(self, Void)
+        return self.getfield(vpbc, attr, hop.llops)
+
+    def getfield(self, vpbc, attr, llops):
         mangled_name, r_value = self.llfieldmap[attr]
-        cmangledname = hop.inputconst(Void, mangled_name)
-        return hop.genop('getfield', [vpbc, cmangledname],
-                         resulttype = r_value)
+        cmangledname = inputconst(Void, mangled_name)
+        return llops.genop('getfield', [vpbc, cmangledname],
+                           resulttype = r_value)
 
 
 # ____________________________________________________________
@@ -424,3 +430,15 @@
             # now hop2 looks like simple_call(initfunc, instance, args...)
             hop2.dispatch()
         return v_instance
+
+# ____________________________________________________________
+
+def rtype_call_memo(hop): 
+    memo_table = hop.args_v[0].value
+    fieldname = memo_table.fieldname 
+    assert hop.nb_args == 2, "XXX"  
+
+    r_pbc = hop.args_r[1]
+    assert isinstance(r_pbc, MultipleFrozenPBCRepr)
+    v_table, v_pbc = hop.inputargs(Void, r_pbc)
+    return r_pbc.getfield(v_pbc, fieldname, hop.llops)

Modified: pypy/dist/pypy/rpython/rtyper.py
==============================================================================
--- pypy/dist/pypy/rpython/rtyper.py	(original)
+++ pypy/dist/pypy/rpython/rtyper.py	Fri Jun 24 21:13:35 2005
@@ -352,6 +352,9 @@
     def translate_op_newslice(self, hop):
         return rslice.rtype_newslice(hop)
 
+    def translate_op_call_memo(self, hop):
+        return rpbc.rtype_call_memo(hop)
+
     def missing_operation(self, hop):
         raise TyperError("unimplemented operation: '%s'" % hop.spaceop.opname)
 

Modified: pypy/dist/pypy/rpython/test/test_llinterp.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_llinterp.py	(original)
+++ pypy/dist/pypy/rpython/test/test_llinterp.py	Fri Jun 24 21:13:35 2005
@@ -31,6 +31,7 @@
     t = Translator(func)
     t.annotate(argtypes)
     if viewbefore:
+        t.annotator.simplify()
         t.view()
     global typer # we need it for find_exception
     typer = RPythonTyper(t.annotator)
@@ -40,8 +41,8 @@
     return t, typer
 
 def interpret(func, values, view=False, viewbefore=False):
-    t, typer = gengraph(func, [lltype_to_annotation(typeOf(x)) for x in values],
-                        viewbefore)
+    t, typer = gengraph(func, [lltype_to_annotation(typeOf(x)) 
+                  for x in values], viewbefore)
     if view:
         t.view()
     interp = LLInterpreter(t.flowgraphs, typer)
@@ -49,8 +50,8 @@
     return res
 
 def make_interpreter(func, example_values, view=False, viewbefore=False):
-    t, typer = gengraph(func, [lltype_to_annotation(typeOf(x)) for x in example_values],
-                        viewbefore)
+    t, typer = gengraph(func, [lltype_to_annotation(typeOf(x)) 
+                            for x in example_values], viewbefore)
     if view:
         t.view()
     interp = LLInterpreter(t.flowgraphs, typer)

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	Fri Jun 24 21:13:35 2005
@@ -1,7 +1,7 @@
 from pypy.translator.translator import Translator
 from pypy.rpython.lltype import *
 from pypy.rpython.rtyper import RPythonTyper
-from pypy.rpython.test.test_llinterp import interpret
+from pypy.rpython.test.test_llinterp import interpret, make_interpreter
 
 
 def test_easy_call():
@@ -143,3 +143,31 @@
     assert res == 1+10+3
     res = interpret(f3, [])
     assert res == 1+10+100
+
+def test_call_memoized():
+    fr1 = Freezing()
+    fr1.x = 0
+    fr2 = Freezing()
+    fr2.x = 1
+    def getorbuild(key):
+        a = 1
+        if key is fr1:
+            result = eval("a+2")
+        else:
+            result = eval("a+6")
+        return result
+    getorbuild._annspecialcase_ = "specialize:memo"
+
+    def f1(i):
+        if i > 0:
+            fr = fr1
+        else:
+            fr = fr2
+        # XXX this should work without fr.x
+        return getorbuild(fr) + fr.x
+
+    ev_f1 = make_interpreter(f1, [0])#, viewbefore=True)
+    res = ev_f1(0)
+    assert res == 8
+    res = ev_f1(1)
+    assert res == 3

Modified: pypy/dist/pypy/translator/transform.py
==============================================================================
--- pypy/dist/pypy/translator/transform.py	(original)
+++ pypy/dist/pypy/translator/transform.py	Fri Jun 24 21:13:35 2005
@@ -13,6 +13,7 @@
 from pypy.objspace.flow.model import last_exception
 from pypy.translator.annrpython import CannotSimplify
 from pypy.annotation import model as annmodel
+from pypy.annotation.specialize import MemoTable
 
 def fully_annotated_blocks(self):
     """Ignore blocked blocks."""
@@ -152,10 +153,20 @@
                             if not specialcase:
                                 op.args[0] = Constant(specialized_callb.prebuiltinstances.keys()[0])
                             else:
-                                if op.opname == 'simple_call':
-                                    op.opname = intern('simple_specialcase')
+                                if op.opname != 'simple_call':
+                                    assert 0, "not supported: call_args to a specialized function"
+                                callable = callb.prebuiltinstances.keys()[0]
+                                tag = getattr(callable, '_annspecialcase_', None)
+                                if tag == 'specialize:memo':
+                                    arglist_s = [self.binding(v) for v in op.args[1:]]
+                                    memo_table = MemoTable(self.bookkeeper, 
+                                                           callable, 
+                                                           self.binding(op.result), 
+                                                           arglist_s)
+                                    op.opname = intern('call_memo')
+                                    op.args[0] = Constant(memo_table)
                                 else:
-                                    op.opname = intern('specialcase_args')
+                                    op.opname = intern('call_specialcase')
 
 default_extra_passes = [
     transform_specialization,



More information about the Pypy-commit mailing list