[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