[pypy-svn] r51799 - in pypy/branch/unified-rtti/pypy/rpython: lltypesystem lltypesystem/test memory memory/gc

arigo at codespeak.net arigo at codespeak.net
Fri Feb 22 16:19:03 CET 2008


Author: arigo
Date: Fri Feb 22 16:19:03 2008
New Revision: 51799

Modified:
   pypy/branch/unified-rtti/pypy/rpython/lltypesystem/llarena.py
   pypy/branch/unified-rtti/pypy/rpython/lltypesystem/llmemory.py
   pypy/branch/unified-rtti/pypy/rpython/lltypesystem/lltype.py
   pypy/branch/unified-rtti/pypy/rpython/lltypesystem/test/test_llarena.py
   pypy/branch/unified-rtti/pypy/rpython/memory/gc/generation.py
   pypy/branch/unified-rtti/pypy/rpython/memory/gc/semispace.py
   pypy/branch/unified-rtti/pypy/rpython/memory/gcheader.py
   pypy/branch/unified-rtti/pypy/rpython/memory/support.py
Log:
Merge r51675 from the trunk.


Modified: pypy/branch/unified-rtti/pypy/rpython/lltypesystem/llarena.py
==============================================================================
--- pypy/branch/unified-rtti/pypy/rpython/lltypesystem/llarena.py	(original)
+++ pypy/branch/unified-rtti/pypy/rpython/lltypesystem/llarena.py	Fri Feb 22 16:19:03 2008
@@ -1,4 +1,4 @@
-import array
+import array, weakref
 from pypy.rpython.lltypesystem import lltype, llmemory
 
 # An "arena" is a large area of memory which can hold a number of
@@ -15,6 +15,7 @@
 
 class Arena(object):
     object_arena_location = {}     # {container: (arena, offset)}
+    old_object_arena_location = weakref.WeakKeyDictionary()
 
     def __init__(self, nbytes, zero):
         self.nbytes = nbytes
@@ -29,7 +30,7 @@
         if size is None:
             stop = self.nbytes
         else:
-            stop = start + size
+            stop = start + llmemory.raw_malloc_usage(size)
         assert 0 <= start <= stop <= self.nbytes
         for offset, ptr in self.objectptrs.items():
             size = self.objectsizes[offset]
@@ -79,9 +80,7 @@
         addr2 = size._raw_malloc([], zero=zero)
         pattern = 'X' + 'x'*(bytes-1)
         self.usagemap[offset:offset+bytes] = array.array('c', pattern)
-        self.objectptrs[offset] = addr2.ptr
-        self.objectsizes[offset] = bytes
-        Arena.object_arena_location[addr2.ptr._obj] = self, offset
+        self.setobject(addr2, offset, bytes)
         # common case: 'size' starts with a GCHeaderOffset.  In this case
         # we can also remember that the real object starts after the header.
         while isinstance(size, RoundedUpForAllocation):
@@ -91,12 +90,17 @@
             objaddr = addr2 + size.offsets[0]
             hdrbytes = llmemory.raw_malloc_usage(size.offsets[0])
             objoffset = offset + hdrbytes
-            assert objoffset not in self.objectptrs
-            self.objectptrs[objoffset] = objaddr.ptr
-            self.objectsizes[objoffset] = bytes - hdrbytes
-            Arena.object_arena_location[objaddr.ptr._obj] = self, objoffset
+            self.setobject(objaddr, objoffset, bytes - hdrbytes)
         return addr2
 
+    def setobject(self, objaddr, offset, bytes):
+        assert offset not in self.objectptrs
+        self.objectptrs[offset] = objaddr.ptr
+        self.objectsizes[offset] = bytes
+        container = objaddr.ptr._obj
+        Arena.object_arena_location[container] = self, offset
+        Arena.old_object_arena_location[container] = self, offset
+
 class fakearenaaddress(llmemory.fakeaddress):
 
     def __init__(self, arena, offset):
@@ -148,6 +152,7 @@
         return True
 
     def compare_with_fakeaddr(self, other):
+        other = other._fixup()
         if not other:
             return None, None
         obj = other.ptr._obj
@@ -205,6 +210,30 @@
         return self.arena._getid() + self.offset
 
 
+def _getfakearenaaddress(addr):
+    """Logic to handle test_replace_object_with_stub()."""
+    if isinstance(addr, fakearenaaddress):
+        return addr
+    else:
+        assert isinstance(addr, llmemory.fakeaddress)
+        assert addr, "NULL address"
+        # it must be possible to use the address of an already-freed
+        # arena object
+        obj = addr.ptr._getobj(check=False)
+        return _oldobj_to_address(obj)
+
+def _oldobj_to_address(obj):
+    obj = obj._normalizedcontainer(check=False)
+    try:
+        arena, offset = Arena.old_object_arena_location[obj]
+    except KeyError:
+        if obj._was_freed():
+            msg = "taking address of %r, but it was freed"
+        else:
+            msg = "taking address of %r, but it is not in an arena"
+        raise RuntimeError(msg % (obj,))
+    return arena.getaddr(offset)
+
 class RoundedUpForAllocation(llmemory.AddressOffset):
     """A size that is rounded up in order to preserve alignment of objects
     following it.  For arenas containing heterogenous objects.
@@ -247,7 +276,7 @@
     """Free all objects in the arena, which can then be reused.
     The arena is filled with zeroes if 'zero' is True.  This can also
     be used on a subrange of the arena."""
-    assert isinstance(arena_addr, fakearenaaddress)
+    arena_addr = _getfakearenaaddress(arena_addr)
     arena_addr.arena.reset(zero, arena_addr.offset, size)
 
 def arena_reserve(addr, size, check_alignment=True):
@@ -256,7 +285,7 @@
     overlap.  The size must be symbolic; in non-translated version
     this is used to know what type of lltype object to allocate."""
     from pypy.rpython.memory.lltypelayout import memory_alignment
-    assert isinstance(addr, fakearenaaddress)
+    addr = _getfakearenaaddress(addr)
     if check_alignment and (addr.offset & (memory_alignment-1)) != 0:
         raise ArenaError("object at offset %d would not be correctly aligned"
                          % (addr.offset,))

Modified: pypy/branch/unified-rtti/pypy/rpython/lltypesystem/llmemory.py
==============================================================================
--- pypy/branch/unified-rtti/pypy/rpython/lltypesystem/llmemory.py	(original)
+++ pypy/branch/unified-rtti/pypy/rpython/lltypesystem/llmemory.py	Fri Feb 22 16:19:03 2008
@@ -372,7 +372,9 @@
     # NOTE: the 'ptr' in the addresses must be normalized.
     # Use cast_ptr_to_adr() instead of directly fakeaddress() if unsure.
     def __init__(self, ptr):
-        self.ptr = ptr or None   # null ptr => None
+        if ptr is not None and ptr._obj0 is None:
+            ptr = None   # null ptr => None
+        self.ptr = ptr
 
     def __repr__(self):
         if self.ptr is None:
@@ -407,8 +409,8 @@
 
     def __eq__(self, other):
         if isinstance(other, fakeaddress):
-            obj1 = self.ptr
-            obj2 = other.ptr
+            obj1 = self._fixup().ptr
+            obj2 = other._fixup().ptr
             if obj1 is not None: obj1 = obj1._obj
             if obj2 is not None: obj2 = obj2._obj
             return obj1 == obj2
@@ -450,8 +452,9 @@
         return self.ptr
 
     def _cast_to_ptr(self, EXPECTED_TYPE):
-        if self:
-            return cast_any_ptr(EXPECTED_TYPE, self.ptr)
+        addr = self._fixup()
+        if addr:
+            return cast_any_ptr(EXPECTED_TYPE, addr.ptr)
         else:
             return lltype.nullptr(EXPECTED_TYPE.TO)
 
@@ -461,6 +464,14 @@
         else:
             return 0
 
+    def _fixup(self):
+        if self.ptr is not None and self.ptr._was_freed():
+            # hack to support llarena.test_replace_object_with_stub()
+            from pypy.rpython.lltypesystem import llarena
+            return llarena._getfakearenaaddress(self)
+        else:
+            return self
+
 # ____________________________________________________________
 
 class fakeaddresswithflags(object):

Modified: pypy/branch/unified-rtti/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/branch/unified-rtti/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/branch/unified-rtti/pypy/rpython/lltypesystem/lltype.py	Fri Feb 22 16:19:03 2008
@@ -956,7 +956,7 @@
         self._set_solid(solid)
         self._set_obj0(obj0)
         
-    def _getobj(self):
+    def _getobj(self, check=True):
         obj = self._obj0
         if obj is not None:
             if self._weak:
@@ -965,12 +965,17 @@
                     raise RuntimeError("accessing already garbage collected %r"
                                    % (self._T,))
             if isinstance(obj, _container):
-                obj._check()
+                if check:
+                    obj._check()
             elif isinstance(obj, str) and obj.startswith("delayed!"):
                 raise DelayedPointer
         return obj
     _obj = property(_getobj)
 
+    def _was_freed(self):
+        return (self._obj0 is not None and
+                self._getobj(check=False)._was_freed())
+
     def __getattr__(self, field_name): # ! can only return basic or ptr !
         if isinstance(self._T, Struct):
             if field_name in self._T._flds:
@@ -1171,6 +1176,10 @@
         from pypy.rpython.lltypesystem import llmemory
         if isinstance(self._T, FuncType):
             return llmemory.fakeaddress(self)
+        elif self._was_freed():
+            # hack to support llarena.test_replace_object_with_stub()
+            from pypy.rpython.lltypesystem import llarena
+            return llarena._oldobj_to_address(self._getobj(check=False))
         elif isinstance(self._obj, _subarray):
             return llmemory.fakeaddress(self)
 ##            # return an address built as an offset in the whole array
@@ -1185,8 +1194,8 @@
 
     def _as_ptr(self):
         return self
-    def _as_obj(self):
-        return self._obj
+    def _as_obj(self, check=True):
+        return self._getobj(check=check)
 
     def _expose(self, offset, val):
         """XXX A nice docstring here"""
@@ -1251,13 +1260,13 @@
 
 class _container(object):
     __slots__ = ()
-    def _parentstructure(self):
+    def _parentstructure(self, check=True):
         return None
     def _check(self):
         pass
     def _as_ptr(self):
         return _ptr(Ptr(self._TYPE), self, True)
-    def _as_obj(self):
+    def _as_obj(self, check=True):
         return self
     def _normalizedcontainer(self):
         return self
@@ -1288,7 +1297,16 @@
         self._storage = None
 
     def _was_freed(self):
-        return self._storage is None
+        if self._storage is None:
+            return True
+        if self._wrparent is None:
+            return False
+        parent = self._wrparent()
+        if parent is None:
+            raise RuntimeError("accessing sub%s %r,\n"
+                               "but already garbage collected parent %r"
+                               % (self._kind, self, self._parent_type))
+        return parent._was_freed()
 
     def _setparentstructure(self, parent, parentindex):
         self._wrparent = weakref.ref(parent)
@@ -1300,14 +1318,15 @@
             # keep strong reference to parent, we share the same allocation
             self._keepparent = parent 
 
-    def _parentstructure(self):
+    def _parentstructure(self, check=True):
         if self._wrparent is not None:
             parent = self._wrparent()
             if parent is None:
                 raise RuntimeError("accessing sub%s %r,\n"
                                    "but already garbage collected parent %r"
                                    % (self._kind, self, self._parent_type))
-            parent._check()
+            if check:
+                parent._check()
             return parent
         return None
 
@@ -1316,14 +1335,15 @@
             raise RuntimeError("accessing freed %r" % self._TYPE)
         self._parentstructure()
 
-    def _normalizedcontainer(self):
+    def _normalizedcontainer(self, check=True):
         # if we are the first inlined substructure of a structure,
         # return the whole (larger) structure instead
         container = self
         while True:
-            parent, index = parentlink(container)
+            parent = container._parentstructure(check=check)
             if parent is None:
                 break
+            index = container._parent_index
             T = typeOf(parent)
             if (not isinstance(T, Struct) or T._first_struct()[0] != index
                 or isinstance(T, FixedSizeArray)):
@@ -1518,7 +1538,7 @@
     def __repr__(self):
         
         return '<_subarray at %r in %r>' % (self._parent_index,
-                                            self._parentstructure())
+                                            self._parentstructure(check=False))
 
     def getlength(self):
         assert isinstance(self._TYPE, FixedSizeArray)

Modified: pypy/branch/unified-rtti/pypy/rpython/lltypesystem/test/test_llarena.py
==============================================================================
--- pypy/branch/unified-rtti/pypy/rpython/lltypesystem/test/test_llarena.py	(original)
+++ pypy/branch/unified-rtti/pypy/rpython/lltypesystem/test/test_llarena.py	Fri Feb 22 16:19:03 2008
@@ -165,6 +165,8 @@
         plist.append(llmemory.cast_adr_to_ptr(b, SPTR))
     # clear blist[1] and blist[2] but not blist[0] nor blist[3]
     arena_reset(blist[1], llmemory.raw_malloc_usage(precomputed_size)*2, False)
+    py.test.raises(RuntimeError, "plist[1].x")     # marked as freed
+    py.test.raises(RuntimeError, "plist[2].x")     # marked as freed
     # re-reserve object at index 1 and 2
     blist[1] = reserve(1)
     blist[2] = reserve(2)
@@ -173,6 +175,10 @@
     assert plist[3].x == 103
     py.test.raises(RuntimeError, "plist[1].x")     # marked as freed
     py.test.raises(RuntimeError, "plist[2].x")     # marked as freed
+    # but we can still cast the old ptrs to addresses, which compare equal
+    # to the new ones we gotq
+    assert llmemory.cast_ptr_to_adr(plist[1]) == blist[1]
+    assert llmemory.cast_ptr_to_adr(plist[2]) == blist[2]
     # check via addresses
     assert (blist[0] + llmemory.offsetof(SX, 'x')).signed[0] == 100
     assert (blist[3] + llmemory.offsetof(SX, 'x')).signed[0] == 103
@@ -204,6 +210,45 @@
     assert llmemory.cast_adr_to_int(a) == llmemory.cast_adr_to_int(a1)
     assert llmemory.cast_adr_to_int(a+1) == llmemory.cast_adr_to_int(a1) + 1
 
+def test_replace_object_with_stub():
+    from pypy.rpython.memory.gcheader import GCHeaderBuilder
+    HDR = lltype.Struct('HDR', ('x', lltype.Signed))
+    S = lltype.GcStruct('S', ('y', lltype.Signed), ('z', lltype.Signed))
+    STUB = lltype.GcStruct('STUB', ('t', lltype.Char))
+    gcheaderbuilder = GCHeaderBuilder(HDR)
+    size_gc_header = gcheaderbuilder.size_gc_header
+
+    a = arena_malloc(50, True)
+    hdraddr = a + 12
+    arena_reserve(hdraddr, size_gc_header + llmemory.sizeof(S))
+    hdr = llmemory.cast_adr_to_ptr(hdraddr, lltype.Ptr(HDR))
+    hdr.x = 42
+    obj = llmemory.cast_adr_to_ptr(hdraddr + size_gc_header, lltype.Ptr(S))
+    obj.y = -5
+    obj.z = -6
+
+    hdraddr = llmemory.cast_ptr_to_adr(obj) - size_gc_header
+    arena_reset(hdraddr, size_gc_header + llmemory.sizeof(S), False)
+    arena_reserve(hdraddr, size_gc_header + llmemory.sizeof(STUB))
+
+    # check that it possible to reach the newly reserved HDR+STUB
+    # via the header of the old 'obj' pointer, both via the existing
+    # 'hdraddr':
+    hdr = llmemory.cast_adr_to_ptr(hdraddr, lltype.Ptr(HDR))
+    hdr.x = 46
+    stub = llmemory.cast_adr_to_ptr(hdraddr + size_gc_header, lltype.Ptr(STUB))
+    stub.t = '!'
+
+    # and via a (now-invalid) pointer to the old 'obj': (this is needed
+    # because during a garbage collection there are still pointers to
+    # the old 'obj' around to be fixed)
+    hdraddr = llmemory.cast_ptr_to_adr(obj) - size_gc_header
+    hdr = llmemory.cast_adr_to_ptr(hdraddr, lltype.Ptr(HDR))
+    assert hdr.x == 46
+    stub = llmemory.cast_adr_to_ptr(hdraddr + size_gc_header,
+                                    lltype.Ptr(STUB))
+    assert stub.t == '!'
+
 
 def test_llinterpreted():
     from pypy.rpython.test.test_llinterp import interpret

Modified: pypy/branch/unified-rtti/pypy/rpython/memory/gc/generation.py
==============================================================================
--- pypy/branch/unified-rtti/pypy/rpython/memory/gc/generation.py	(original)
+++ pypy/branch/unified-rtti/pypy/rpython/memory/gc/generation.py	Fri Feb 22 16:19:03 2008
@@ -22,8 +22,8 @@
 class GenerationGC(SemiSpaceGC):
     """A basic generational GC: it's a SemiSpaceGC with an additional
     nursery for young objects.  A write barrier is used to ensure that
-    old objects that contain pointers to young objects are in a linked
-    list, chained to each other via their 'forw' header field.
+    old objects that contain pointers to young objects are recorded in
+    a list.
     """
     inline_simple_malloc = True
     inline_simple_malloc_varsize = True
@@ -44,19 +44,16 @@
         self.initial_nursery_size = nursery_size
         self.auto_nursery_size = auto_nursery_size
         self.min_nursery_size = min_nursery_size
-
-    def setup(self):
-        SemiSpaceGC.setup(self)
-        self.reset_nursery()
-        self.old_objects_pointing_to_young = NULL
-        # ^^^ the head of a linked list inside the old objects space; it
+        self.old_objects_pointing_to_young = self.AddressStack()
+        # ^^^ a list of addresses inside the old objects space; it
         # may contain static prebuilt objects as well.  More precisely,
         # it lists exactly the old and static objects whose
-        # GCFLAG_NO_YOUNG_PTRS bit is not set.  The 'forw' header field
-        # of such objects is abused for this linked list; it needs to be
-        # reset to its correct value when GCFLAG_NO_YOUNG_PTRS is set
-        # again at the start of a collection.
+        # GCFLAG_NO_YOUNG_PTRS bit is not set.
         self.young_objects_with_weakrefs = self.AddressStack()
+        self.reset_nursery()
+
+    def setup(self):
+        SemiSpaceGC.setup(self)
         self.set_nursery_size(self.initial_nursery_size)
         # the GC is fully setup now.  The rest can make use of it.
         if self.auto_nursery_size:
@@ -226,14 +223,11 @@
         # the next usage.
 
     def reset_young_gcflags(self):
-        obj = self.old_objects_pointing_to_young
-        while obj:
+        oldlist = self.old_objects_pointing_to_young
+        while oldlist.non_empty():
+            obj = oldlist.pop()
             hdr = self.header(obj)
             hdr.tid |= GCFLAG_NO_YOUNG_PTRS
-            nextobj = hdr.forw
-            self.init_forwarding(obj)
-            obj = nextobj
-        self.old_objects_pointing_to_young = NULL
 
     def weakrefs_grow_older(self):
         while self.young_objects_with_weakrefs.non_empty():
@@ -278,19 +272,15 @@
 
     def collect_oldrefs_to_nursery(self):
         # Follow the old_objects_pointing_to_young list and move the
-        # young objects they point to out of the nursery.  The 'forw'
-        # fields are reset to their correct value along the way.
+        # young objects they point to out of the nursery.
         count = 0
-        obj = self.old_objects_pointing_to_young
-        while obj:
+        oldlist = self.old_objects_pointing_to_young
+        while oldlist.non_empty():
             count += 1
-            nextobj = self.header(obj).forw
-            self.init_forwarding(obj)
+            obj = oldlist.pop()
             self.trace_and_drag_out_of_nursery(obj)
-            obj = nextobj
         if DEBUG_PRINT:
             llop.debug_print(lltype.Void, "collect_oldrefs_to_nursery", count)
-        self.old_objects_pointing_to_young = NULL
 
     def collect_roots_in_nursery(self):
         # we don't need to trace prebuilt GcStructs during a minor collect:
@@ -362,8 +352,7 @@
                      "nursery object with GCFLAG_NO_YOUNG_PTRS")
         oldhdr = self.header(addr_struct)
         if self.is_in_nursery(addr):
-            oldhdr.forw = self.old_objects_pointing_to_young
-            self.old_objects_pointing_to_young = addr_struct
+            self.old_objects_pointing_to_young.append(addr_struct)
             oldhdr.tid &= ~GCFLAG_NO_YOUNG_PTRS
         if oldhdr.tid & GCFLAG_NO_HEAP_PTRS:
             self.move_to_static_roots(addr_struct)

Modified: pypy/branch/unified-rtti/pypy/rpython/memory/gc/semispace.py
==============================================================================
--- pypy/branch/unified-rtti/pypy/rpython/memory/gc/semispace.py	(original)
+++ pypy/branch/unified-rtti/pypy/rpython/memory/gc/semispace.py	Fri Feb 22 16:19:03 2008
@@ -15,8 +15,9 @@
 
 TYPEID_MASK = 0xffff
 first_gcflag = 1 << 16
-GCFLAG_IMMORTAL = first_gcflag
-GCFLAG_FINALIZATION_ORDERING = first_gcflag << 1
+GCFLAG_FORWARDED = first_gcflag
+GCFLAG_IMMORTAL = first_gcflag << 1
+GCFLAG_FINALIZATION_ORDERING = first_gcflag << 2
 
 DEBUG_PRINT = False
 memoryError = MemoryError()
@@ -26,12 +27,14 @@
     inline_simple_malloc = True
     inline_simple_malloc_varsize = True
     needs_zero_gc_pointers = False
-    first_unused_gcflag = first_gcflag << 2
+    first_unused_gcflag = first_gcflag << 3
     total_collection_time = 0.0
     total_collection_count = 0
 
-    HDR = lltype.Struct('header', ('forw', llmemory.Address),
-                                  ('tid', lltype.Signed))
+    HDR = lltype.Struct('header', ('tid', lltype.Signed))
+    FORWARDSTUB = lltype.GcStruct('forwarding_stub',
+                                  ('forw', llmemory.Address))
+    FORWARDSTUBPTR = lltype.Ptr(FORWARDSTUB)
 
     def __init__(self, chunk_size=DEFAULT_CHUNK_SIZE, space_size=4096,
                  max_space_size=sys.maxint//2+1):
@@ -41,6 +44,8 @@
         self.gcheaderbuilder = GCHeaderBuilder(self.HDR)
         self.AddressStack = get_address_stack(chunk_size)
         self.AddressDeque = get_address_deque(chunk_size)
+        self.finalizer_lock_count = 0
+        self.red_zone = 0
 
     def setup(self):
         if DEBUG_PRINT:
@@ -55,8 +60,6 @@
         self.objects_with_finalizers = self.AddressDeque()
         self.run_finalizers = self.AddressDeque()
         self.objects_with_weakrefs = self.AddressStack()
-        self.finalizer_lock_count = 0
-        self.red_zone = 0
 
     def disable_finalizers(self):
         self.finalizer_lock_count += 1
@@ -289,15 +292,13 @@
         root.address[0] = self.copy(root.address[0])
 
     def copy(self, obj):
-        # Objects not living the GC heap have all been initialized by
-        # setting their 'forw' address so that it points to themselves.
-        # The logic below will thus simply return 'obj' if 'obj' is prebuilt.
         if self.is_forwarded(obj):
             #llop.debug_print(lltype.Void, obj, "already copied to", self.get_forwarding_address(obj))
             return self.get_forwarding_address(obj)
         else:
             newaddr = self.free
-            totalsize = self.size_gc_header() + self.get_size(obj)
+            objsize = self.get_size(obj)
+            totalsize = self.size_gc_header() + objsize
             llarena.arena_reserve(newaddr, totalsize)
             raw_memcopy(obj - self.size_gc_header(), newaddr, totalsize)
             self.free += totalsize
@@ -305,7 +306,7 @@
             #llop.debug_print(lltype.Void, obj, "copied to", newobj,
             #                 "tid", self.header(obj).tid,
             #                 "size", totalsize)
-            self.set_forwarding_address(obj, newobj)
+            self.set_forwarding_address(obj, newobj, objsize)
             return newobj
 
     def trace_and_copy(self, obj):
@@ -316,14 +317,35 @@
             pointer.address[0] = self.copy(pointer.address[0])
 
     def is_forwarded(self, obj):
-        return self.header(obj).forw != NULL
+        return self.header(obj).tid & GCFLAG_FORWARDED != 0
+        # note: all prebuilt objects also have this flag set
 
     def get_forwarding_address(self, obj):
-        return self.header(obj).forw
+        tid = self.header(obj).tid
+        if tid & GCFLAG_IMMORTAL:
+            return obj      # prebuilt objects are "forwarded" to themselves
+        else:
+            stub = llmemory.cast_adr_to_ptr(obj, self.FORWARDSTUBPTR)
+            return stub.forw
 
-    def set_forwarding_address(self, obj, newobj):
-        gc_info = self.header(obj)
-        gc_info.forw = newobj
+    def set_forwarding_address(self, obj, newobj, objsize):
+        # To mark an object as forwarded, we set the GCFLAG_FORWARDED and
+        # overwrite the object with a FORWARDSTUB.  Doing so is a bit
+        # long-winded on llarena, but it all melts down to two memory
+        # writes after translation to C.
+        size_gc_header = self.size_gc_header()
+        stubsize = llmemory.sizeof(self.FORWARDSTUB)
+        tid = self.header(obj).tid
+        ll_assert(tid & GCFLAG_IMMORTAL == 0,  "unexpected GCFLAG_IMMORTAL")
+        ll_assert(tid & GCFLAG_FORWARDED == 0, "unexpected GCFLAG_FORWARDED")
+        # replace the object at 'obj' with a FORWARDSTUB.
+        hdraddr = obj - size_gc_header
+        llarena.arena_reset(hdraddr, size_gc_header + objsize, False)
+        llarena.arena_reserve(hdraddr, size_gc_header + stubsize)
+        hdr = llmemory.cast_adr_to_ptr(hdraddr, lltype.Ptr(self.HDR))
+        hdr.tid = tid | GCFLAG_FORWARDED
+        stub = llmemory.cast_adr_to_ptr(obj, self.FORWARDSTUBPTR)
+        stub.forw = newobj
 
     def get_size(self, obj):
         typeid = self.get_type_id(obj)
@@ -340,26 +362,24 @@
         return llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
 
     def get_type_id(self, addr):
-        return self.header(addr).tid & TYPEID_MASK
+        tid = self.header(addr).tid
+        ll_assert(tid & (GCFLAG_FORWARDED|GCFLAG_IMMORTAL) != GCFLAG_FORWARDED,
+                  "get_type_id on forwarded obj")
+        # Non-prebuilt forwarded objects are overwritten with a FORWARDSTUB.
+        # Although calling get_type_id() on a forwarded object works by itself,
+        # we catch it as an error because it's likely that what is then
+        # done with the typeid is bogus.
+        return tid & TYPEID_MASK
 
     def init_gc_object(self, addr, typeid, flags=0):
         hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
-        #hdr.forw = NULL   -- unneeded, the space is initially filled with zero
         hdr.tid = typeid | flags
 
     def init_gc_object_immortal(self, addr, typeid, flags=0):
-        # immortal objects always have forward to themselves
         hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
-        hdr.tid = typeid | flags | GCFLAG_IMMORTAL
-        self.init_forwarding(addr + self.gcheaderbuilder.size_gc_header)
-
-    def init_forwarding(self, obj):
-        hdr = self.header(obj)
-        if hdr.tid & GCFLAG_IMMORTAL:
-            hdr.forw = obj      # prebuilt objects point to themselves,
-                                # so that a collection does not move them
-        else:
-            hdr.forw = NULL
+        hdr.tid = typeid | flags | GCFLAG_IMMORTAL | GCFLAG_FORWARDED
+        # immortal objects always have GCFLAG_FORWARDED set;
+        # see get_forwarding_address().
 
     def deal_with_objects_with_finalizers(self, scan):
         # walk over list of objects with finalizers
@@ -371,6 +391,7 @@
         new_with_finalizer = self.AddressDeque()
         marked = self.AddressDeque()
         pending = self.AddressStack()
+        self.tmpstack = self.AddressStack()
         while self.objects_with_finalizers.non_empty():
             x = self.objects_with_finalizers.popleft()
             ll_assert(self._finalization_state(x) != 1, 
@@ -385,11 +406,9 @@
                 state = self._finalization_state(y)
                 if state == 0:
                     self._bump_finalization_state_from_0_to_1(y)
+                    self.trace(y, self._append_if_nonnull, pending)
                 elif state == 2:
-                    self._bump_finalization_state_from_2_to_3(y)
-                else:
-                    continue   # don't need to recurse inside y
-                self.trace(y, self._append_if_nonnull, pending)
+                    self._recursively_bump_finalization_state_from_2_to_3(y)
             scan = self._recursively_bump_finalization_state_from_1_to_2(
                        x, scan)
 
@@ -403,16 +422,11 @@
                 # we must also fix the state from 2 to 3 here, otherwise
                 # we leave the GCFLAG_FINALIZATION_ORDERING bit behind
                 # which will confuse the next collection
-                pending.append(x)
-                while pending.non_empty():
-                    y = pending.pop()
-                    state = self._finalization_state(y)
-                    if state == 2:
-                        self._bump_finalization_state_from_2_to_3(y)
-                        self.trace(y, self._append_if_nonnull, pending)
+                self._recursively_bump_finalization_state_from_2_to_3(x)
             else:
                 new_with_finalizer.append(newx)
 
+        self.tmpstack.delete()
         pending.delete()
         marked.delete()
         self.objects_with_finalizers.delete()
@@ -445,12 +459,19 @@
         hdr = self.header(obj)
         hdr.tid |= GCFLAG_FINALIZATION_ORDERING
 
-    def _bump_finalization_state_from_2_to_3(self, obj):
+    def _recursively_bump_finalization_state_from_2_to_3(self, obj):
         ll_assert(self._finalization_state(obj) == 2,
                   "unexpected finalization state != 2")
         newobj = self.get_forwarding_address(obj)
-        hdr = self.header(newobj)
-        hdr.tid &= ~GCFLAG_FINALIZATION_ORDERING
+        pending = self.tmpstack
+        ll_assert(not pending.non_empty(), "tmpstack not empty")
+        pending.append(newobj)
+        while pending.non_empty():
+            y = pending.pop()
+            hdr = self.header(y)
+            if hdr.tid & GCFLAG_FINALIZATION_ORDERING:     # state 2 ?
+                hdr.tid &= ~GCFLAG_FINALIZATION_ORDERING   # change to state 3
+                self.trace(y, self._append_if_nonnull, pending)
 
     def _recursively_bump_finalization_state_from_1_to_2(self, obj, scan):
         # recursively convert objects from state 1 to state 2.

Modified: pypy/branch/unified-rtti/pypy/rpython/memory/gcheader.py
==============================================================================
--- pypy/branch/unified-rtti/pypy/rpython/memory/gcheader.py	(original)
+++ pypy/branch/unified-rtti/pypy/rpython/memory/gcheader.py	Fri Feb 22 16:19:03 2008
@@ -21,17 +21,17 @@
 
     def header_of_object(self, gcptr):
         # XXX hackhackhack
-        gcptr = gcptr._as_obj()
+        gcptr = gcptr._as_obj(check=False)
         if isinstance(gcptr, llmemory._gctransformed_wref):
-            return self.obj2header[gcptr._ptr._as_obj()]
+            return self.obj2header[gcptr._ptr._as_obj(check=False)]
         return self.obj2header[gcptr]
 
     def object_from_header(headerptr):
-        return header2obj[headerptr._as_obj()]
+        return header2obj[headerptr._as_obj(check=False)]
     object_from_header = staticmethod(object_from_header)
 
     def get_header(self, gcptr):
-        return self.obj2header.get(gcptr._as_obj(), None)
+        return self.obj2header.get(gcptr._as_obj(check=False), None)
 
     def attach_header(self, gcptr, headerptr):
         gcobj = gcptr._as_obj()

Modified: pypy/branch/unified-rtti/pypy/rpython/memory/support.py
==============================================================================
--- pypy/branch/unified-rtti/pypy/rpython/memory/support.py	(original)
+++ pypy/branch/unified-rtti/pypy/rpython/memory/support.py	Fri Feb 22 16:19:03 2008
@@ -27,7 +27,10 @@
 
         def get(self):
             if not self.free_list:
-                return lltype.malloc(CHUNK, flavor="raw")
+                # we zero-initialize the chunks to make the translation
+                # backends happy, but we don't need to do it at run-time.
+                zero = not we_are_translated()
+                return lltype.malloc(CHUNK, flavor="raw", zero=zero)
                 
             result = self.free_list
             self.free_list = result.next



More information about the Pypy-commit mailing list