[pypy-svn] r22135 - in pypy/dist/pypy: jit jit/test rpython

arigo at codespeak.net arigo at codespeak.net
Sat Jan 14 13:37:14 CET 2006


Author: arigo
Date: Sat Jan 14 13:37:09 2006
New Revision: 22135

Added:
   pypy/dist/pypy/jit/test/test_vlist.py   (contents, props changed)
   pypy/dist/pypy/jit/vlist.py   (contents, props changed)
Modified:
   pypy/dist/pypy/jit/llabstractinterp.py
   pypy/dist/pypy/jit/llcontainer.py
   pypy/dist/pypy/jit/llvalue.py
   pypy/dist/pypy/jit/test/test_llabstractinterp.py
   pypy/dist/pypy/rpython/rlist.py
Log:
Starting LLAbstractInterp support for RPython list objects, virtualized
directly instead of as structs and arrays with multiple low-level
representations (e.g. in the presence of over-allocation).

Experimental and incomplete!


Modified: pypy/dist/pypy/jit/llabstractinterp.py
==============================================================================
--- pypy/dist/pypy/jit/llabstractinterp.py	(original)
+++ pypy/dist/pypy/jit/llabstractinterp.py	Sat Jan 14 13:37:09 2006
@@ -5,8 +5,10 @@
 from pypy.rpython.lltypesystem import lltype
 from pypy.translator.simplify import eliminate_empty_blocks, join_blocks
 from pypy.jit.llvalue import LLAbstractValue, const, newvar, dupvar
+from pypy.jit.llvalue import ll_no_return_value
 from pypy.jit.llvalue import FlattenMemo, MatchMemo, FreezeMemo, UnfreezeMemo
 from pypy.jit.llcontainer import LLAbstractContainer, virtualcontainervalue
+from pypy.jit.llcontainer import hasllcontent
 
 # ____________________________________________________________
 
@@ -138,16 +140,16 @@
 
 # ____________________________________________________________
 
-ll_no_return_value = LLAbstractValue(const(None, lltype.Void))
-
 
 class Policy(object):
     def __init__(self, inlining=False, const_propagate=False,
-                       concrete_propagate=True, concrete_args=True):
+                       concrete_propagate=True, concrete_args=True,
+                       oopspec=False):
         self.inlining = inlining
         self.const_propagate = const_propagate
         self.concrete_propagate = concrete_propagate
         self.concrete_args = concrete_args
+        self.oopspec = oopspec
 
 # hint-driven policy
 best_policy = Policy(inlining=True, const_propagate=True, concrete_args=False)
@@ -689,6 +691,13 @@
         fnobj = v_func.value._obj
         if not hasattr(fnobj, 'graph'):
             return None
+
+        if self.interp.policy.oopspec and hasattr(fnobj._callable, 'oopspec'):
+            a_result = self.handle_highlevel_operation(op, fnobj._callable,
+                                                       *args_a)
+            if a_result is not None:
+                return a_result
+
         if getattr(fnobj._callable, 'suggested_primitive', False):
             return None
 
@@ -745,7 +754,7 @@
         return a_real_result
 
     def op_getfield(self, op, a_ptr, a_attrname):
-        if a_ptr.content is not None:
+        if hasllcontent(a_ptr):
             c_attrname = a_attrname.maybe_get_constant()
             assert c_attrname is not None
             return a_ptr.content.getfield(c_attrname.value)
@@ -756,7 +765,7 @@
         return self.residualize(op, [a_ptr, a_attrname], constant_op)
 
     def op_getsubstruct(self, op, a_ptr, a_attrname):
-        if a_ptr.content is not None:
+        if hasllcontent(a_ptr):
             c_attrname = a_attrname.maybe_get_constant()
             assert c_attrname is not None
             # the difference with op_getfield() is only that the following
@@ -765,12 +774,12 @@
         return self.residualize(op, [a_ptr, a_attrname], getattr)
 
     def op_getarraysize(self, op, a_ptr):
-        if a_ptr.content is not None:
+        if hasllcontent(a_ptr):
             return LLAbstractValue(const(a_ptr.content.length))
         return self.residualize(op, [a_ptr], len)
 
     def op_getarrayitem(self, op, a_ptr, a_index):
-        if a_ptr.content is not None:
+        if hasllcontent(a_ptr):
             c_index = a_index.maybe_get_constant()
             if c_index is not None:
                 return a_ptr.content.getfield(c_index.value)
@@ -794,7 +803,7 @@
         return self.residualize(op, [a_T, a_size])
 
     def op_setfield(self, op, a_ptr, a_attrname, a_value):
-        if a_ptr.content is not None:
+        if hasllcontent(a_ptr):
             c_attrname = a_attrname.maybe_get_constant()
             assert c_attrname is not None
             a_ptr.content.setfield(c_attrname.value, a_value)
@@ -802,7 +811,7 @@
         return self.residualize(op, [a_ptr, a_attrname, a_value])
 
     def op_setarrayitem(self, op, a_ptr, a_index, a_value):
-        if a_ptr.content is not None:
+        if hasllcontent(a_ptr):
             c_index = a_index.maybe_get_constant()
             if c_index is not None:
                 a_ptr.content.setfield(c_index.value, a_value)
@@ -810,7 +819,7 @@
         return self.residualize(op, [a_ptr, a_index, a_value])
 
     def op_cast_pointer(self, op, a_ptr):
-        if a_ptr.content is not None:
+        if hasllcontent(a_ptr):
             down_or_up = lltype.castable(op.result.concretetype,
                                          a_ptr.content.getconcretetype())
             # the following works because if a structure is virtual, then
@@ -838,6 +847,50 @@
                 self.residual_operations.append(op)
         return ll_no_return_value
 
+    # High-level operation dispatcher
+    def handle_highlevel_operation(self, op, ll_func, *args_a):
+        # parse the oopspec and fill in the arguments
+        operation_name, args = ll_func.oopspec.split('(', 1)
+        assert args.endswith(')')
+        args = args[:-1] + ','     # trailing comma to force tuple syntax
+        argnames = ll_func.func_code.co_varnames[:len(args_a)]
+        d = dict(zip(argnames, args_a))
+        argtuple = eval(args, d)
+        args_a = []
+        for a in argtuple:
+            if not isinstance(a, LLAbstractValue):
+                a = LLAbstractValue(const(a))
+            args_a.append(a)
+        # end of rather XXX'edly hackish parsing
+
+        if operation_name == 'newlist':
+            from pypy.jit.vlist import oop_newlist
+            handler = oop_newlist
+        else:
+            # dispatch on the 'self' argument if it is virtual
+            a_self = args_a[0]
+            args_a = args_a[1:]
+            if not isinstance(a_self, LLAbstractContainer):
+                return None
+            type_name, operation_name = operation_name.split('.')
+            if a_self.type_name != type_name:
+                return None
+            try:
+                handler = getattr(a_self, 'oop_' + operation_name)
+            except AttributeError:
+                print 'MISSING HANDLER: oop_%s' % (operation_name,)
+                return None
+        try:
+            a_result = handler(op, *args_a)
+        except NotImplementedError:
+            return None
+        if a_result is None:
+            a_result = ll_no_return_value
+        assert op.result.concretetype == a_result.getconcretetype(), (
+            "type mismatch: %s\nreturned %s\nexpected %s" % (
+            handler, a_result.getconcretetype(), op.result.concretetype))
+        return a_result
+
 
 class InsertNextLink(Exception):
     def __init__(self, link):

Modified: pypy/dist/pypy/jit/llcontainer.py
==============================================================================
--- pypy/dist/pypy/jit/llcontainer.py	(original)
+++ pypy/dist/pypy/jit/llcontainer.py	Sat Jan 14 13:37:09 2006
@@ -230,3 +230,6 @@
         # primitive initialized to zero
         a = LLAbstractValue(const(T._defl()))
     return a
+
+def hasllcontent(a_ptr):
+    return isinstance(a_ptr.content, LLVirtualContainer)

Modified: pypy/dist/pypy/jit/llvalue.py
==============================================================================
--- pypy/dist/pypy/jit/llvalue.py	(original)
+++ pypy/dist/pypy/jit/llvalue.py	Sat Jan 14 13:37:09 2006
@@ -24,7 +24,7 @@
     def __repr__(self):
         if self.runtimevar is None:
             if self.content is None:
-                return '<invalid!>'
+                return '<dummy>'
             else:
                 return '<virtual %s>' % (self.content,)
         else:
@@ -47,7 +47,7 @@
                 # don't use 'memo' here: for now, shared LLAbstractValues
                 # need to become distinct LLFrozenRuntimeValues.
                 return LLFrozenRuntimeValue(self)
-        else:
+        elif self.content is not None:
             # virtual container: preserve sharing
             if self in memo.seen:
                 return memo.seen[self]    # already seen
@@ -56,15 +56,21 @@
                 memo.seen[self] = result
                 result.fz_content = self.content.freeze(memo)
                 return result
+        else:
+            return frozen_dummy_value   # dummy
 
     def getconcretetype(self):
         if self.runtimevar is not None:
             return self.runtimevar.concretetype
-        else:
+        elif self.content is not None:
             return lltype.Ptr(self.content.T)
+        else:
+            raise ValueError("ll_dummy_value.getconcretetype()")
 
     def forcevarorconst(self, builder):
         if self.runtimevar is None:
+            if self.content is None:
+                raise ValueError("ll_dummy_value.forcevarorconst()")
             self.runtimevar = self.content.build_runtime_container(builder)
             self.content = None
         return self.runtimevar
@@ -83,10 +89,12 @@
             if not self.concrete:   # skip concrete values, they don't need
                                     # to be present in the residual graph at all
                 memo.result.append(self)
-        else:
+        elif self.content is not None:
             if self not in memo.seen:
                 memo.seen[self] = True
                 self.content.flatten(memo)
+        else:
+            pass    # dummy
 
 # ____________________________________________________________
 
@@ -99,6 +107,18 @@
     # relevant in the finished block.
 
 
+class LLFrozenDummyValue(LLFrozenValue):
+
+    def flatten(self, memo):
+        pass
+
+    def unfreeze(self, memo):
+        return ll_dummy_value
+
+    def match(self, a_value, memo):
+        return True    # a dummy matches anything
+
+
 class LLFrozenConcreteValue(LLFrozenValue):
 
     def __init__(self, a_source):
@@ -236,3 +256,7 @@
     v1 = Variable(v)
     v1.concretetype = v.concretetype
     return v1
+
+ll_no_return_value = LLAbstractValue(const(None, lltype.Void))
+ll_dummy_value = LLAbstractValue()
+frozen_dummy_value = LLFrozenDummyValue()

Modified: pypy/dist/pypy/jit/test/test_llabstractinterp.py
==============================================================================
--- pypy/dist/pypy/jit/test/test_llabstractinterp.py	(original)
+++ pypy/dist/pypy/jit/test/test_llabstractinterp.py	Sat Jan 14 13:37:09 2006
@@ -54,13 +54,16 @@
     result1 = llinterp.eval_graph(graph1, argvalues)
     result2 = llinterp.eval_graph(graph2, argvalues2)
     assert result1 == result2
-    # return a summary of the instructions left in graph2
+    return graph2, summary(interp)
+
+def summary(interp):
+    # return a summary of the instructions left in all the residual graphs
     insns = {}
     for copygraph in interp.itercopygraphs():
         for block in copygraph.iterblocks():
             for op in block.operations:
                 insns[op.opname] = insns.get(op.opname, 0) + 1
-    return graph2, insns
+    return insns
 
 P_INLINE = Policy(inlining=True)
 P_CONST_INLINE = Policy(inlining=True, const_propagate=True)

Added: pypy/dist/pypy/jit/test/test_vlist.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/jit/test/test_vlist.py	Sat Jan 14 13:37:09 2006
@@ -0,0 +1,45 @@
+import py
+from pypy.translator.translator import TranslationContext
+from pypy.jit.llabstractinterp import LLAbstractInterp, Policy
+from pypy.jit.test.test_llabstractinterp import summary
+from pypy.rpython.llinterp import LLInterpreter
+from pypy.rpython.rstr import string_repr
+from pypy.rpython.objectmodel import hint
+
+policy = Policy(inlining=True, const_propagate=True, concrete_args=False,
+                oopspec=True)
+
+def run(fn, argvalues):
+    t = TranslationContext()
+    t.buildannotator().build_types(fn, [type(x) for x in argvalues])
+    rtyper = t.buildrtyper()
+    rtyper.specialize()
+    graph1 = t.graphs[0]
+
+    interp = LLAbstractInterp(policy)
+    hints = {}
+    llvalues = []
+    for i, value in enumerate(argvalues):
+        if isinstance(value, str):
+            value = string_repr.convert_const(value)
+        llvalues.append(value)
+        hints[i] = value
+    graph2 = interp.eval(graph1, hints)
+    #graph2.show()
+
+    llinterp = LLInterpreter(rtyper)
+    result1 = llinterp.eval_graph(graph1, llvalues)
+    result2 = llinterp.eval_graph(graph2, [])
+
+    assert result1 == result2
+
+    return graph2, summary(interp)
+
+
+def test_newlist():
+    py.test.skip("in-progress")
+    def fn(n):
+        lst = [5] * n
+        return len(lst)
+    graph2, insns = run(fn, [12])
+    assert insns == {}

Added: pypy/dist/pypy/jit/vlist.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/jit/vlist.py	Sat Jan 14 13:37:09 2006
@@ -0,0 +1,83 @@
+from pypy.rpython.lltypesystem import lltype
+from pypy.jit.llvalue import LLAbstractValue, newvar, const, ll_dummy_value
+from pypy.jit.llcontainer import LLAbstractContainer
+
+
+class LLVirtualList(LLAbstractContainer):
+    type_name = 'list'
+
+    def __init__(self, T, items_a=None):
+        self.T = T
+        if items_a is None:
+            self.items_a = []
+        else:
+            self.items_a = items_a
+
+    def flatten(self, memo):
+        assert self not in memo.seen; memo.seen[self] = True  # debugging only
+        for a_value in self.items_a:
+            a_value.flatten(memo)
+
+    def match(self, live, memo):
+        if self.__class__ is not live.__class__:
+            return False
+        if len(self.items_a) != len(live.items_a):
+            return False
+        for a1, a2 in zip(self.items_a, live.items_a):
+            if not a1.match(a2, memo):
+                return False
+        else:
+            return True
+
+    def freeze(self, memo):
+        items_a = [a.freeze(memo) for a in self.items_a]
+        return LLVirtualList(self.T, items_a)
+
+    def unfreeze(self, memo):
+        items_a = [a.unfreeze(memo) for a in self.items_a]
+        return LLVirtualList(self.T, items_a)
+
+    def build_runtime_container(self, builder):
+        cno = const(len(self.items_a))
+        v_result = builder.gendirectcall(self.T.ll_newlist, cno)
+        cdum = const(rlist.dum_nocheck, lltype.Void)
+        for i, a in enumerate(self.items_a):
+            ci = const(i)
+            v_item = a.forcevarorconst(builder)
+            builder.gendirectcall(rlist.ll_setitem_nonneg,
+                                  cdum, v_result, ci, v_item)
+        return v_result
+
+    # ____________________________________________________________
+    # High-level operations
+
+    def oop_getitem(self, op, a_index):
+        c_index = a_index.maybe_get_constant()
+        if c_index is None:
+            raise NotImplementedError
+        return self.items_a[c_index.value]
+
+    def oop_setitem(self, op, a_index, a_newobj):
+        c_index = a_index.maybe_get_constant()
+        if c_index is None:
+            raise NotImplementedError
+        self.items_a[c_index.value] = a_newobj
+
+    def oop_append(self, op, a_newobj):
+        self.items_a.append(a_newobj)
+
+    def oop_pop(self, op, a_index):
+        c_index = a_index.maybe_get_constant()
+        if c_index is None:
+            raise NotImplementedError
+        return self.items_a.pop(c_index.value)
+
+
+def oop_newlist(op, a_numitems):
+    c_numitems = a_numitems.maybe_get_constant()
+    if c_numitems is None:
+        raise NotImplementedError
+    LIST = op.result.concretetype.TO
+    items_a = [ll_dummy_value] * c_numitems.value
+    virtuallist = LLVirtualList(LIST, items_a)
+    return LLAbstractValue(content=virtuallist)

Modified: pypy/dist/pypy/rpython/rlist.py
==============================================================================
--- pypy/dist/pypy/rpython/rlist.py	(original)
+++ pypy/dist/pypy/rpython/rlist.py	Sat Jan 14 13:37:09 2006
@@ -558,6 +558,7 @@
         items[newlength] = nullptr(ITEM.TO)
     _ll_list_resize_le(l, newlength)
     return res
+ll_pop_zero.oopspec = 'list.pop(l, 0)'
 
 def ll_pop(func, l, index):
     length = l.length
@@ -861,6 +862,7 @@
     l.items = malloc(LIST.items.TO, length)
     return l
 ll_newlist = typeMethod(ll_newlist)
+ll_newlist.oopspec = 'newlist(length)'
 
 def ll_length(l):
     return l.length
@@ -874,6 +876,7 @@
     l = malloc(LIST, length)
     return l
 ll_fixed_newlist = typeMethod(ll_fixed_newlist)
+ll_fixed_newlist.oopspec = 'newlist(length)'
 
 def ll_fixed_length(l):
     return len(l)



More information about the Pypy-commit mailing list