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

arigo at codespeak.net arigo at codespeak.net
Wed Feb 20 10:42:05 CET 2008


Author: arigo
Date: Wed Feb 20 10:42:04 2008
New Revision: 51675

Modified:
   pypy/dist/pypy/rpython/lltypesystem/llarena.py
   pypy/dist/pypy/rpython/lltypesystem/llmemory.py
   pypy/dist/pypy/rpython/lltypesystem/lltype.py
   pypy/dist/pypy/rpython/lltypesystem/test/test_llarena.py
   pypy/dist/pypy/rpython/memory/gc/generation.py
   pypy/dist/pypy/rpython/memory/gc/semispace.py
   pypy/dist/pypy/rpython/memory/gcheader.py
   pypy/dist/pypy/rpython/memory/support.py
Log:
Merge the n-forw-ptr branch, which removes the 'forw' field from
the HDR of all the objects allocated by the SemiSpaceGC.

in llarena, llmemory, lltype: 

    This seems to be the amount of hacking needed to allow an arena to
    replace an object with another, and still have the old address work in
    order to go to the new object's header.  This works only to some extend
    -- many operations on the old address still raise RuntimeError as
    before, in the hope that it will not allow nasty bugs to go unnoticed.

in semispace, generation:

    Remove 'forw', replace it with a flag.
    Move some initialization code to translation-time instead of run-time.
    Can't prebuild the address lists in generation.py, because we call
    delete() on them later on :-)
    
in support:

    Translation fix for prebuilt AddressStack and AddressDeque.



Modified: pypy/dist/pypy/rpython/lltypesystem/llarena.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/llarena.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/llarena.py	Wed Feb 20 10:42:04 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/dist/pypy/rpython/lltypesystem/llmemory.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/llmemory.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/llmemory.py	Wed Feb 20 10:42:04 2008
@@ -339,7 +339,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:
@@ -374,8 +376,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
@@ -412,8 +414,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)
 
@@ -423,6 +426,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 NullAddressError(Exception):

Modified: pypy/dist/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/lltype.py	Wed Feb 20 10:42:04 2008
@@ -968,7 +968,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:
@@ -977,12 +977,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:
@@ -1178,6 +1183,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
@@ -1192,8 +1201,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"""
@@ -1258,13 +1267,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
@@ -1295,7 +1304,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)
@@ -1307,14 +1325,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
 
@@ -1323,14 +1342,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)):
@@ -1525,7 +1545,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/dist/pypy/rpython/lltypesystem/test/test_llarena.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/test/test_llarena.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/test/test_llarena.py	Wed Feb 20 10:42:04 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/dist/pypy/rpython/memory/gc/generation.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gc/generation.py	(original)
+++ pypy/dist/pypy/rpython/memory/gc/generation.py	Wed Feb 20 10:42:04 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/dist/pypy/rpython/memory/gc/semispace.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gc/semispace.py	(original)
+++ pypy/dist/pypy/rpython/memory/gc/semispace.py	Wed Feb 20 10:42:04 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/dist/pypy/rpython/memory/gcheader.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gcheader.py	(original)
+++ pypy/dist/pypy/rpython/memory/gcheader.py	Wed Feb 20 10:42:04 2008
@@ -16,17 +16,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/dist/pypy/rpython/memory/support.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/support.py	(original)
+++ pypy/dist/pypy/rpython/memory/support.py	Wed Feb 20 10:42:04 2008
@@ -26,7 +26,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