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

arigo at codespeak.net arigo at codespeak.net
Fri May 19 13:28:12 CEST 2006


Author: arigo
Date: Fri May 19 13:28:09 2006
New Revision: 27466

Modified:
   pypy/dist/pypy/rpython/lltypesystem/lloperation.py
   pypy/dist/pypy/rpython/lltypesystem/test/test_lloperation.py
   pypy/dist/pypy/rpython/memory/gc.py
   pypy/dist/pypy/rpython/memory/gctransform.py
   pypy/dist/pypy/rpython/memory/test/test_transformed_gc.py
Log:
Started generic cloning based on GC support.  This should provide
exactly what is needed by the simple thread cloning approach
discussed on pypy-dev.  Doing it at the GC level also looks like
a good plan.


Modified: pypy/dist/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/lloperation.py	Fri May 19 13:28:09 2006
@@ -317,6 +317,10 @@
     'gc_protect':           LLOp(),
     'gc_unprotect':         LLOp(),    
     'gc_reload_possibly_moved': LLOp(),
+    # experimental operations in support of thread cloning, only
+    # implemented by the Mark&Sweep GC
+    'gc_x_swap_list':       LLOp(),
+    'gc_x_clone':           LLOp(canraise=(MemoryError,)),
 
     # __________ stackless operation(s) __________
 

Modified: pypy/dist/pypy/rpython/lltypesystem/test/test_lloperation.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/test/test_lloperation.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/test/test_lloperation.py	Fri May 19 13:28:09 2006
@@ -21,6 +21,8 @@
 
 def test_llinterp_complete():
     for opname in LL_OPERATIONS:
+        if opname.startswith('gc_x_'):
+            continue   # ignore experimental stuff
         assert opname in LL_INTERP_OPERATIONS
 
 def test_llop():

Modified: pypy/dist/pypy/rpython/memory/gc.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gc.py	(original)
+++ pypy/dist/pypy/rpython/memory/gc.py	Fri May 19 13:28:09 2006
@@ -12,6 +12,9 @@
 int_size = lltypesimulation.sizeof(lltype.Signed)
 gc_header_two_ints = 2*int_size
 
+X_CLONE = lltype.GcStruct('CloneData', ('gcobjectptr', llmemory.GCREF),
+                                       ('malloced_list', llmemory.Address))
+
 class GCError(Exception):
     pass
 
@@ -281,6 +284,82 @@
         hdr.typeid = typeid << 1
     init_gc_object_immortal = init_gc_object
 
+    # experimental support for thread cloning
+    def x_swap_list(self, malloced_list):
+        # Swap the current malloced_objects linked list with another one.
+        # All malloc'ed objects are put into the current malloced_objects
+        # list; this is a way to separate objects depending on when they
+        # were allocated.
+        current = llmemory.cast_ptr_to_adr(self.malloced_objects)
+        self.malloced_objects = llmemory.cast_adr_to_ptr(malloced_list,
+                                                         self.HDRPTR)
+        return current
+
+    def x_clone(self, clonedata):
+        # Recursively clone the gcobject and everything it points to,
+        # directly or indirectly -- but stops at objects that are not
+        # in the malloced_list.  The gcobjectptr and the malloced_list
+        # fields of clonedata are adjusted to refer to the result.
+        size_gc_header = self.gcheaderbuilder.size_gc_header
+        oldobjects = self.AddressLinkedList()
+        hdr = llmemory.cast_adr_to_ptr(clonedata.malloced_list, self.HDRPTR)
+        while hdr:
+            next = hdr.next
+            hdr.typeid |= 1    # mark all objects from malloced_list
+            hdr.next = lltype.nullptr(self.HDR)  # abused to point to the copy
+            oldobjects.append(llmemory.cast_ptr_to_adr(hdr))
+            hdr = next
+
+        # a stack of addresses of places that still points to old objects
+        # and that must possibly be fixed to point to a new copy
+        stack = self.AddressLinkedList()
+        stack.append(llmemory.cast_ptr_to_adr(clonedata)
+                     + llmemory.offsetof(X_CLONE, 'gcobjectptr'))
+        newobjectlist = lltype.nullptr(self.HDR)
+        while stack.non_empty():
+            gcptr_addr = stack.pop()
+            oldobj_addr = gcptr_addr.address[0]
+            oldhdr = llmemory.cast_adr_to_ptr(oldobj_addr - size_gc_header,
+                                              self.HDRPTR)
+            typeid = oldhdr.typeid
+            if not (typeid & 1):
+                continue   # ignore objects that were not in the malloced_list
+            newhdr = oldhdr.next      # abused to point to the copy
+            if not newhdr:
+                typeid >>= 1
+                if self.is_varsize(typeid):
+                    raise NotImplementedError
+                else:
+                    size = self.fixed_size(typeid)
+                    newmem = raw_malloc(size_gc_header + size)
+                    newhdr = llmemory.cast_adr_to_ptr(newmem, self.HDRPTR)
+                    newhdr.typeid = typeid << 1
+                    newhdr.next = newobjectlist
+                    newobjectlist = newhdr
+                    newobj_addr = newmem + size_gc_header
+                    raw_memcopy(oldobj_addr, newobj_addr, size)
+                    offsets = self.offsets_to_gc_pointers(typeid)
+                    i = 0
+                    while i < len(offsets):
+                        pointer_addr = newobj_addr + offsets[i]
+                        stack.append(pointer_addr)
+                        i += 1
+                oldhdr.next = newhdr
+            newobj_addr = llmemory.cast_ptr_to_adr(newhdr) + size_gc_header
+            gcptr_addr.address[0] = newobj_addr
+
+        clonedata.malloced_list = llmemory.cast_ptr_to_adr(newobjectlist)
+
+        # re-create the original linked list
+        next = lltype.nullptr(self.HDR)
+        while oldobjects.non_empty():
+            hdr = llmemory.cast_adr_to_ptr(oldobjects.pop(), self.HDRPTR)
+            hdr.typeid &= ~1   # reset the mark bit
+            hdr.next = next
+            next = hdr
+        oldobjects.delete()
+
+
 class SemiSpaceGC(GCBase):
     _alloc_flavor_ = "raw"
 

Modified: pypy/dist/pypy/rpython/memory/gctransform.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform.py	(original)
+++ pypy/dist/pypy/rpython/memory/gctransform.py	Fri May 19 13:28:09 2006
@@ -868,25 +868,34 @@
                                       inline = True)
 
         classdef = bk.getuniqueclassdef(GCClass)
-        s_gcdata = annmodel.SomeInstance(classdef)
+        s_gc = annmodel.SomeInstance(classdef)
         s_gcref = annmodel.SomePtr(llmemory.GCREF)
         self.malloc_fixedsize_ptr = getfn(
             GCClass.malloc_fixedsize.im_func,
-            [s_gcdata, annmodel.SomeInteger(nonneg=True),
+            [s_gc, annmodel.SomeInteger(nonneg=True),
              annmodel.SomeInteger(nonneg=True),
              annmodel.SomeBool()], s_gcref,
             inline = True)
         self.malloc_varsize_ptr = getfn(
             GCClass.malloc_varsize.im_func,
-            [s_gcdata] + [annmodel.SomeInteger(nonneg=True) for i in range(5)]
+            [s_gc] + [annmodel.SomeInteger(nonneg=True) for i in range(5)]
             + [annmodel.SomeBool()], s_gcref)
         self.collect_ptr = getfn(GCClass.collect.im_func,
-            [s_gcdata], annmodel.s_None)
+            [s_gc], annmodel.s_None)
 
         statics_s = (annmodel.SomeInteger(),)*GCClass.STATISTICS_NUMBERS
         self.statistics_ptr = getfn(GCClass.statistics.im_func,
-                                    [s_gcdata], annmodel.SomeTuple(statics_s))
-                                   
+                                    [s_gc], annmodel.SomeTuple(statics_s))
+
+        # experimental gc_x_* operations
+        self.x_swap_list_ptr = getfn(GCClass.x_swap_list.im_func,
+                                     [s_gc, annmodel.SomeAddress()],
+                                     annmodel.SomeAddress())
+        self.x_clone_ptr = getfn(GCClass.x_clone.im_func,
+                                 [s_gc,
+                                  annmodel.SomePtr(lltype.Ptr(gc.X_CLONE))],
+                                 annmodel.s_None)
+
         annhelper.finish()   # at this point, annotate all mix-level helpers
         annhelper.backend_optimize()
 
@@ -1153,6 +1162,22 @@
                                         block.operations.index(op))
         return ops
 
+    def replace_gc_x_swap_list(self, op, livevars, block):
+        [v_malloced] = op.args
+        newop = SpaceOperation("direct_call",
+                               [self.x_swap_list_ptr, self.c_const_gc,
+                                                      v_malloced],
+                               op.result)
+        return [newop]
+
+    def replace_gc_x_clone(self, op, livevars, block):
+        [v_clonedata] = op.args
+        newop = SpaceOperation("direct_call",
+                               [self.x_clone_ptr, self.c_const_gc,
+                                                  v_clonedata],
+                               op.result)
+        return [newop]
+
     def push_alive_nopyobj(self, var):
         return []
 

Modified: pypy/dist/pypy/rpython/memory/test/test_transformed_gc.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/test/test_transformed_gc.py	(original)
+++ pypy/dist/pypy/rpython/memory/test/test_transformed_gc.py	Fri May 19 13:28:09 2006
@@ -156,11 +156,12 @@
 
 from pypy.translator.c import gc
 from pypy.annotation import model as annmodel
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, llmemory
 from pypy.rpython.memory import gctransform
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rpython.lltypesystem import lltype
 from pypy.rpython.memory.support import INT_SIZE
+from pypy.rpython.memory.gc import X_CLONE
 from pypy import conftest
 
 
@@ -292,6 +293,40 @@
         heap_size = statistics().item0
         assert heap_size < 16000 * INT_SIZE / 4 # xxx
 
+    def test_cloning(self):
+        py.test.skip("in-progress")
+        B = lltype.GcStruct('B', ('x', lltype.Signed))
+        A = lltype.GcStruct('A', ('b', lltype.Ptr(B)))
+        def make(n):
+            b = lltype.malloc(B)
+            b.x = n
+            a = lltype.malloc(A)
+            a.b = b
+            return a
+        def func():
+            a1 = make(111)
+            # start recording mallocs in a new list
+            oldlist = llop.gc_x_swap_list(llmemory.Address, llmemory.NULL)
+            # the following a2 goes into the new list
+            a2 = make(222)
+            # now put the old list back and get the new list
+            newlist = llop.gc_x_swap_list(llmemory.Address, oldlist)
+            a3 = make(333)
+            # clone a2
+            a2ref = lltype.cast_opaque_ptr(llmemory.GCREF, a2)
+            clonedata = lltype.malloc(X_CLONE)
+            clonedata.gcobjectptr = a2ref
+            clonedata.malloced_list = newlist
+            llop.gc_x_clone(lltype.Void, clonedata)
+            a2copyref = clonedata.gcobjectptr
+            a2copy = lltype.cast_opaque_ptr(lltype.Ptr(A), a2copyref)
+            a2copy.b.x = 444
+            return a1.b.x * 1000000 + a2.b.x * 1000 + a3.b.x
+
+        run = self.runner(func)
+        res = run([])
+        assert res == 111222333
+
 
 class TestStacklessMarkSweepGC(TestMarkSweepGC):
 



More information about the Pypy-commit mailing list