[pypy-svn] r32613 - in pypy/dist/pypy/rpython: . lltypesystem memory memory/test

mwh at codespeak.net mwh at codespeak.net
Sun Sep 24 01:06:36 CEST 2006


Author: mwh
Date: Sun Sep 24 01:06:32 2006
New Revision: 32613

Modified:
   pypy/dist/pypy/rpython/llinterp.py
   pypy/dist/pypy/rpython/lltypesystem/llmemory.py
   pypy/dist/pypy/rpython/memory/gctransform.py
   pypy/dist/pypy/rpython/memory/test/test_gctransform.py
Log:
so this started out with an attempt to make it possible to llinterp the output
of the refcountinggctransformer.
along the way i:
 * implemented raw_memclear for llinterped objects far more thoroughly
 * changed the refcountinggctransformer to only rely on an implementation
   of raw_malloc from the backend
 * found out once again that refcounting builds take ages and produce slow
   binaries.
it would be nice if someone could read op_gc_call_rtti_destructor and check it
for sanity


Modified: pypy/dist/pypy/rpython/llinterp.py
==============================================================================
--- pypy/dist/pypy/rpython/llinterp.py	(original)
+++ pypy/dist/pypy/rpython/llinterp.py	Sun Sep 24 01:06:32 2006
@@ -632,7 +632,9 @@
         gc.collect()
 
     def op_gc_free(self, addr):
-        raise NotImplementedError("gc_free")
+        # what can you do?
+        pass
+        #raise NotImplementedError("gc_free")
 
     def op_gc_fetch_exception(self):
         raise NotImplementedError("gc_fetch_exception")
@@ -641,7 +643,10 @@
         raise NotImplementedError("gc_restore_exception")
 
     def op_gc_call_rtti_destructor(self, rtti, addr):
-        raise NotImplementedError("gc_call_rtti_destructor")
+        if hasattr(rtti._obj, 'destructor_funcptr'):
+            d = rtti._obj.destructor_funcptr
+            ob = addr.get()
+            return self.op_direct_call(d, ob)
 
     def op_gc_deallocate(self, TYPE, addr):
         raise NotImplementedError("gc_deallocate")

Modified: pypy/dist/pypy/rpython/lltypesystem/llmemory.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/llmemory.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/llmemory.py	Sun Sep 24 01:06:32 2006
@@ -24,6 +24,8 @@
     def raw_malloc(self, rest):
         raise NotImplementedError("raw_malloc(%r, %r)" % (self, rest))
 
+    def raw_memclear(self, adr):
+        raise NotImplementedError("raw_memclear(%r, %r)" % (self, adr))
 
 class ItemOffset(AddressOffset):
 
@@ -64,6 +66,25 @@
             array_adr = cast_ptr_to_adr(p)
             return array_adr + ArrayItemsOffset(T)
 
+    def raw_memclear(self, adr):
+        if (isinstance(self.TYPE, lltype.ContainerType) and self.repeat == 1):
+            from pypy.rpython.rctypes.rmodel import reccopy
+            fresh = lltype.malloc(self.TYPE, flavor='raw', zero=True)
+            reccopy(fresh, adr.get())
+        else:
+            assert adr.offset is not None
+            if isinstance(adr.offset, ArrayItemsOffset):
+                array = adr.ob
+            elif isinstance(adr.offset, CompositeOffset):
+                array = (adr + -adr.offset.offsets[-1]).get()
+            if isinstance(self.TYPE, lltype.ContainerType):
+                fresh = lltype.malloc(self.TYPE, flavor='raw', zero=True)
+                for i in range(self.repeat):
+                    reccopy(fresh, array[i])
+            else:
+                for i in range(self.repeat):
+                    array[i] = self.TYPE._defl()
+        
 
 class FieldOffset(AddressOffset):
 
@@ -86,6 +107,22 @@
         assert rest
         return rest[0].raw_malloc(rest[1:], parenttype=parenttype or self.TYPE)
 
+    def raw_memclear(self, adr):
+        if self.fldname != self.TYPE._arrayfld:
+            return AddressOffset.raw_memclear(adr)   # for the error msg
+        structptr = adr.get()
+        for name in self.TYPE._names[:-1]:
+            FIELDTYPE = getattr(self.TYPE, name) 
+            if isinstance(FIELDTYPE, lltype.ContainerType):
+                fresh = lltype.malloc(FIELDTYPE, raw=True, zero=True)
+                from pypy.rpython.rctypes.rmodel import reccopy
+                fresh = lltype.malloc(self.TYPE, flavor='raw', zero=True)
+                reccopy(fresh, getattr(structptr, name)._obj)
+            else:
+                setattr(structptr, name, FIELDTYPE._defl())
+
+    
+
 
 class CompositeOffset(AddressOffset):
 
@@ -125,6 +162,12 @@
     def raw_malloc(self, rest):
         return self.offsets[0].raw_malloc(self.offsets[1:] + rest)
 
+    def raw_memclear(self, adr):
+        for o in self.offsets[:-1]:
+            o.raw_memclear(adr)
+            adr += o
+        o.raw_memclear(adr)
+
 
 class ArrayItemsOffset(AddressOffset):
 
@@ -153,6 +196,10 @@
                           immortal = self.TYPE._gckind == 'raw')
         return cast_ptr_to_adr(p)
 
+    def raw_memclear(self, adr):
+        # should really zero out the length field, but we can't
+        pass
+
 
 class ArrayLengthOffset(AddressOffset):
 
@@ -191,6 +238,9 @@
         headerptr = self.gcheaderbuilder.new_header(gcobjadr.get())
         return cast_ptr_to_adr(headerptr)
 
+    def raw_memclear(self, adr):
+        headerptr = adr.get()
+        sizeof(lltype.typeOf(headerptr).TO).raw_memclear(cast_ptr_to_adr(headerptr))
 
 class GCHeaderAntiOffset(AddressOffset):
     def __init__(self, gcheaderbuilder):
@@ -560,15 +610,10 @@
     return size
 
 def raw_memclear(adr, size):
-    # hack hack hack
-    # stab stab stab
-    assert (adr.offset is None or
-            (isinstance(adr.offset, ArrayItemsOffset)
-             and isinstance(lltype.typeOf(adr.ob).TO, lltype.FixedSizeArray)))
-    TYPE = lltype.typeOf(adr.ob)
-    fresh = lltype.malloc(TYPE.TO, zero=True, flavor='raw')
-    from pypy.rpython.rctypes.rmodel import reccopy
-    reccopy(fresh, adr.ob)
+    if not isinstance(size, AddressOffset):
+        raise NotImplementedError(size)
+    assert lltype.typeOf(adr) == Address
+    size.raw_memclear(adr)
 
 def raw_memcopy(source, dest, size):
     source = source.get()

Modified: pypy/dist/pypy/rpython/memory/gctransform.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform.py	(original)
+++ pypy/dist/pypy/rpython/memory/gctransform.py	Sun Sep 24 01:06:32 2006
@@ -21,7 +21,9 @@
 from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
 from pypy.rpython.extregistry import ExtRegistryEntry
 from pypy.rpython.rtyper import LowLevelOpList
-import sets, os
+from pypy.rpython.rbuiltin import gen_cast
+from pypy.rpython.rarithmetic import ovfcheck
+import sets, os, sys
 
 def var_ispyobj(var):
     if hasattr(var, 'concretetype'):
@@ -425,27 +427,51 @@
         gc_header_offset = self.gcheaderbuilder.size_gc_header
         self.deallocator_graphs_needing_transforming = []
         # create incref graph
+        HDRPTR = lltype.Ptr(self.HDR)
         def ll_incref(adr):
             if adr:
-                gcheader = adr - gc_header_offset
-                gcheader.signed[0] = gcheader.signed[0] + 1
+                gcheader = llmemory.cast_adr_to_ptr(adr - gc_header_offset, HDRPTR)
+                gcheader.refcount = gcheader.refcount + 1
         def ll_decref(adr, dealloc):
             if adr:
-                gcheader = adr - gc_header_offset
-                refcount = gcheader.signed[0] - 1
-                gcheader.signed[0] = refcount
+                gcheader = llmemory.cast_adr_to_ptr(adr - gc_header_offset, HDRPTR)
+                refcount = gcheader.refcount - 1
+                gcheader.refcount = refcount
                 if refcount == 0:
                     dealloc(adr)
         def ll_decref_simple(adr):
             if adr:
-                gcheader = adr - gc_header_offset
-                refcount = gcheader.signed[0] - 1
+                gcheader = llmemory.cast_adr_to_ptr(adr - gc_header_offset, HDRPTR)
+                refcount = gcheader.refcount - 1
                 if refcount == 0:
                     llop.gc_free(lltype.Void, adr)
                 else:
-                    gcheader.signed[0] = refcount
+                    gcheader.refcount = refcount
         def ll_no_pointer_dealloc(adr):
             llop.gc_free(lltype.Void, adr)
+
+        def ll_malloc_fixedsize(size):
+            size = gc_header_offset + size
+            result = lladdress.raw_malloc(size)
+            lladdress.raw_memclear(result, size)
+            result += gc_header_offset
+            return result
+        def ll_malloc_varsize_no_length(length, size, itemsize):
+            try:
+                fixsize = gc_header_offset + size
+                varsize = ovfcheck(itemsize * length)
+                tot_size = ovfcheck(fixsize + varsize)
+            except OverflowError:
+                raise MemoryError
+            result = lladdress.raw_malloc(tot_size)
+            lladdress.raw_memclear(result, tot_size)
+            result += gc_header_offset
+            return result
+        def ll_malloc_varsize(length, size, itemsize, lengthoffset):
+            result = ll_malloc_varsize_no_length(length, size, itemsize)
+            (result + lengthoffset).signed[0] = length
+            return result
+
         if self.translator:
             self.increfptr = self.inittime_helper(
                 ll_incref, [llmemory.Address], lltype.Void)
@@ -456,6 +482,12 @@
                 ll_decref_simple, [llmemory.Address], lltype.Void)
             self.no_pointer_dealloc_ptr = self.inittime_helper(
                 ll_no_pointer_dealloc, [llmemory.Address], lltype.Void)
+            self.malloc_fixedsize_ptr = self.inittime_helper(
+                ll_malloc_fixedsize, [lltype.Signed], llmemory.Address)
+            self.malloc_varsize_no_length_ptr = self.inittime_helper(
+                ll_malloc_varsize_no_length, [lltype.Signed]*3, llmemory.Address)
+            self.malloc_varsize_ptr = self.inittime_helper(
+                ll_malloc_varsize, [lltype.Signed]*4, llmemory.Address)
             self.mixlevelannotator.finish()   # for now
         # cache graphs:
         self.decref_funcptrs = {}
@@ -526,12 +558,63 @@
         result.extend(self.pop_alive(oldval))
         return result
 
-##    -- maybe add this for tests and for consistency --
-##    def consider_constant(self, TYPE, value):
-##        p = value._as_ptr()
-##        if not self.gcheaderbuilder.get_header(p):
-##            hdr = new_header(p)
-##            hdr.refcount = sys.maxint // 2
+    def replace_malloc(self, op, livevars, block):
+        TYPE = op.result.concretetype.TO
+        assert not TYPE._is_varsize()
+        c_size = Constant(llmemory.sizeof(TYPE), lltype.Signed)
+        ops = []
+        v_raw = varoftype(llmemory.Address)
+        return [SpaceOperation("direct_call",
+                               [self.malloc_fixedsize_ptr, c_size],
+                               v_raw),
+                SpaceOperation("cast_adr_to_ptr",
+                               [v_raw],
+                               op.result)]
+
+    def replace_malloc_varsize(self, op, livevars, block):
+        def intconst(c): return Constant(c, lltype.Signed)
+
+        TYPE = op.result.concretetype.TO
+        assert TYPE._is_varsize()
+
+        c_const_size = intconst(llmemory.sizeof(TYPE, 0))
+        if isinstance(TYPE, lltype.Struct):
+            ARRAY = TYPE._flds[TYPE._arrayfld]
+        else:
+            ARRAY = TYPE
+        assert isinstance(ARRAY, lltype.Array)
+        c_item_size = intconst(llmemory.sizeof(ARRAY.OF))
+
+        ops = []
+        v_raw = varoftype(llmemory.Address)
+        if ARRAY._hints.get("nolength", False):
+            ops.append(
+                SpaceOperation("direct_call",
+                               [self.malloc_varsize_no_length_ptr, op.args[-1],
+                                c_const_size, c_item_size],
+                               v_raw))
+        else:
+            if isinstance(TYPE, lltype.Struct):
+                offset_to_length = llmemory.FieldOffset(TYPE, TYPE._arrayfld) + llmemory.ArrayLengthOffset(ARRAY)
+            else:
+                offset_to_length = llmemory.ArrayLengthOffset(ARRAY)
+            ops.append(
+                SpaceOperation("direct_call",
+                               [self.malloc_varsize_ptr, op.args[-1],
+                                c_const_size, c_item_size, intconst(offset_to_length)],
+                               v_raw))
+
+        ops.append(SpaceOperation("cast_adr_to_ptr", [v_raw], op.result))
+        return ops
+
+    def consider_constant(self, TYPE, value):
+        if value is not lltype.top_container(value):
+            return
+        if isinstance(TYPE, (lltype.GcStruct, lltype.GcArray)):
+            p = value._as_ptr()
+            if not self.gcheaderbuilder.get_header(p):
+                hdr = self.gcheaderbuilder.new_header(p)
+                hdr.refcount = sys.maxint // 2
 
     def static_deallocation_funcptr_for_type(self, TYPE):
         if TYPE in self.static_deallocator_funcptrs:
@@ -559,13 +642,13 @@
     exc_instance = llop.gc_fetch_exception(EXC_INSTANCE_TYPE)
     try:
         v = cast_adr_to_ptr(addr, PTR_TYPE)
-        gcheader = addr - gc_header_offset
+        gcheader = cast_adr_to_ptr(addr - gc_header_offset, HDRPTR)
         # refcount is at zero, temporarily bump it to 1:
-        gcheader.signed[0] = 1
+        gcheader.refcount = 1
         destr_v = cast_pointer(DESTR_ARG, v)
         ll_call_destructor(destrptr, destr_v)
-        refcount = gcheader.signed[0] - 1
-        gcheader.signed[0] = refcount
+        refcount = gcheader.refcount - 1
+        gcheader.refcount = refcount
         if refcount == 0:
 %s
             llop.gc_free(lltype.Void, addr)
@@ -591,7 +674,8 @@
              'PTR_TYPE': lltype.Ptr(TYPE),
              'DESTR_ARG': DESTR_ARG,
              'EXC_INSTANCE_TYPE': self.translator.rtyper.exceptiondata.lltype_of_exception_value,
-             'll_call_destructor': ll_call_destructor}
+             'll_call_destructor': ll_call_destructor,
+             'HDRPTR':lltype.Ptr(self.HDR)}
         exec src in d
         this = d['ll_deallocator']
         fptr = self.annotate_helper(this, [llmemory.Address], lltype.Void)
@@ -620,11 +704,11 @@
         gc_header_offset = self.gcheaderbuilder.size_gc_header
         def ll_dealloc(addr):
             # bump refcount to 1
-            gcheader = addr - gc_header_offset
-            gcheader.signed[0] = 1
+            gcheader = llmemory.cast_adr_to_ptr(addr - gc_header_offset, lltype.Ptr(self.HDR))
+            gcheader.refcount = 1
             v = llmemory.cast_adr_to_ptr(addr, QUERY_ARG_TYPE)
             rtti = queryptr(v)
-            gcheader.signed[0] = 0
+            gcheader.refcount = 0
             llop.gc_call_rtti_destructor(lltype.Void, rtti, addr)
         fptr = self.annotate_helper(ll_dealloc, [llmemory.Address], lltype.Void)
         self.dynamic_deallocator_funcptrs[TYPE] = fptr

Modified: pypy/dist/pypy/rpython/memory/test/test_gctransform.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/test/test_gctransform.py	(original)
+++ pypy/dist/pypy/rpython/memory/test/test_gctransform.py	Sun Sep 24 01:06:32 2006
@@ -69,9 +69,9 @@
 
 def rtype_and_transform(func, inputtypes, transformcls, specialize=True, check=True):
     t = rtype(func, inputtypes, specialize)
+    transformer = transformcls(t)
     etrafo = ExceptionTransformer(t)
     etrafo.transform_completely()
-    transformer = transformcls(t)
     graphs_borrowed = {}
     for graph in t.graphs:
         graphs_borrowed[graph] = transformer.transform_graph(graph)
@@ -586,7 +586,8 @@
     t, transformer = rtype_and_transform(
         f, [int], gctransform.RefcountingGCTransformer, check=False)
     fgraph = graphof(t, f)
-    TYPE = fgraph.startblock.operations[0].result.concretetype.TO
+    s_instance = t.annotator.bookkeeper.valueoftype(A)
+    TYPE = t.rtyper.getrepr(s_instance).lowleveltype.TO
     p = transformer.dynamic_deallocation_funcptr_for_type(TYPE)
     t.rtyper.specialize_more_blocks() 
 
@@ -722,3 +723,99 @@
     res = llinterp.eval_graph(entrygraph, [ll_argv])
 
     assert ''.join(res.chars) == "2"
+
+def llinterpreter_for_refcounted_graph(f, args_s):
+    from pypy.rpython.llinterp import LLInterpreter
+    from pypy.translator.c.genc import CStandaloneBuilder
+    from pypy.translator.c import gc
+
+    t = rtype(f, args_s)
+    cbuild = CStandaloneBuilder(t, f, gc.RefcountingGcPolicy)
+    db = cbuild.generate_graphs_for_llinterp()
+    graph = cbuild.getentrypointptr()._obj.graph
+    llinterp = LLInterpreter(t.rtyper)
+    if conftest.option.view:
+        t.view()
+    return llinterp, graph
+    res = llinterp.eval_graph(graph, [0])
+    assert res == f(0)
+    res = llinterp.eval_graph(graph, [1])
+    assert res == f(1)
+    
+
+def test_llinterp_refcounted_graph():
+    from pypy.annotation.model import SomeInteger
+    
+    class C:
+        pass
+    c = C()
+    c.x = 1
+    def g(x):
+        if x:
+            return c
+        else:
+            d = C()
+            d.x = 2
+            return d
+    def f(x):
+        return g(x).x
+
+    llinterp, graph = llinterpreter_for_refcounted_graph(f, [SomeInteger()])
+
+    res = llinterp.eval_graph(graph, [0])
+    assert res == f(0)
+    res = llinterp.eval_graph(graph, [1])
+    assert res == f(1)
+
+def test_llinterp_refcounted_graph_varsize():
+    from pypy.annotation.model import SomeInteger
+    
+    def f(x):
+        r = []
+        for i in range(x):
+            if i % 2:
+                r.append(x)
+        return len(r)
+
+
+    llinterp, graph = llinterpreter_for_refcounted_graph(f, [SomeInteger()])
+
+    res = llinterp.eval_graph(graph, [0])
+    assert res == f(0)
+    res = llinterp.eval_graph(graph, [10])
+    assert res == f(10)
+
+def test_llinterp_refcounted_graph_with_del():
+    from pypy.annotation.model import SomeInteger
+
+    class D:
+        pass
+
+    delcounter = D()
+    delcounter.dels = 0
+    
+    class C:
+        def __del__(self):
+            delcounter.dels += 1
+    c = C()
+    c.x = 1
+    def h(x):
+        if x:
+            return c
+        else:
+            d = C()
+            d.x = 2
+            return d
+    def g(x):
+        return h(x).x
+    def f(x):
+        r = g(x)
+        return r + delcounter.dels
+
+    llinterp, graph = llinterpreter_for_refcounted_graph(f, [SomeInteger()])
+
+    res = llinterp.eval_graph(graph, [1])
+    assert res == 1
+    res = llinterp.eval_graph(graph, [0])
+    assert res == 3
+



More information about the Pypy-commit mailing list