[pypy-commit] pypy gc-forkfriendly: Hack out GC flags to possibly be behind a pointer in incminimark.

devin.jeanpierre pypy.commits at gmail.com
Mon May 2 13:42:54 EDT 2016


Author: Devin Jeanpierre <jeanpierreda at gmail.com>
Branch: gc-forkfriendly
Changeset: r84133:d7d1d6464379
Date: 2016-05-02 10:41 -0700
http://bitbucket.org/pypy/pypy/changeset/d7d1d6464379/

Log:	Hack out GC flags to possibly be behind a pointer in incminimark.

	TODO: allocate flags out of the struct, measure cost/benefit (i.e.
	benchmark)

	Sorry about the sledgehammer refactoring. I'm not familiar with the
	GC code yet.

diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -1039,7 +1039,7 @@
             # object: a weakref, or one with any kind of finalizer.
             return False
         #
-        self.header(obj).tid |= GCFLAG_PINNED
+        self.add_flags(obj, GCFLAG_PINNED)
         self.pinned_objects_in_nursery += 1
         return True
 
@@ -1048,11 +1048,11 @@
         ll_assert(self._is_pinned(obj),
             "unpin: object is already not pinned")
         #
-        self.header(obj).tid &= ~GCFLAG_PINNED
+        self.remove_flags(obj, GCFLAG_PINNED)
         self.pinned_objects_in_nursery -= 1
 
     def _is_pinned(self, obj):
-        return (self.header(obj).tid & GCFLAG_PINNED) != 0
+        return (self.get_flags(obj) & GCFLAG_PINNED) != 0
 
     def shrink_array(self, obj, smallerlength):
         #
@@ -1065,7 +1065,7 @@
         # the already-allocated shadow.
         if not self.is_in_nursery(obj):
             return False
-        if self.header(obj).tid & GCFLAG_HAS_SHADOW:
+        if self.get_flags(obj) & GCFLAG_HAS_SHADOW:
             return False
         #
         size_gc_header = self.gcheaderbuilder.size_gc_header
@@ -1083,7 +1083,7 @@
     # Simple helpers
 
     def get_type_id(self, obj):
-        tid = self.header(obj).tid
+        tid = self.get_flags(obj)
         return llop.extract_ushort(llgroup.HALFWORD, tid)
 
     def combine(self, typeid16, flags):
@@ -1127,7 +1127,7 @@
         that can never be set on a young object -- except if tid == -42.
         """
         assert self.is_in_nursery(obj)
-        tid = self.header(obj).tid
+        tid = self.get_flags(obj)
         result = (tid & GCFLAG_FINALIZATION_ORDERING != 0)
         if result:
             ll_assert(tid == -42, "bogus header for young obj")
@@ -1208,9 +1208,9 @@
         if not self._is_pinned(obj):
             ll_assert(not self.is_in_nursery(obj),
                       "object in nursery after collection")
-            ll_assert(self.header(obj).tid & GCFLAG_VISITED_RMY == 0,
+            ll_assert(self.get_flags(obj) & GCFLAG_VISITED_RMY == 0,
                       "GCFLAG_VISITED_RMY after collection")
-            ll_assert(self.header(obj).tid & GCFLAG_PINNED == 0,
+            ll_assert(self.get_flags(obj) & GCFLAG_PINNED == 0,
                       "GCFLAG_PINNED outside the nursery after collection")
         else:
             ll_assert(self.is_in_nursery(obj),
@@ -1228,7 +1228,7 @@
             ll_assert(False, "unknown gc_state value")
 
     def _debug_check_object_marking(self, obj):
-        if self.header(obj).tid & GCFLAG_VISITED != 0:
+        if self.get_flags(obj) & GCFLAG_VISITED != 0:
             # A black object.  Should NEVER point to a white object.
             self.trace(obj, self._debug_check_not_white, None)
             # During marking, all visited (black) objects should always have
@@ -1238,17 +1238,17 @@
             # object state VISITED & ~WRITE_BARRIER.
             typeid = self.get_type_id(obj)
             if self.has_gcptr(typeid):
-                ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS != 0,
+                ll_assert(self.get_flags(obj) & GCFLAG_TRACK_YOUNG_PTRS != 0,
                           "black object without GCFLAG_TRACK_YOUNG_PTRS")
 
     def _debug_check_not_white(self, root, ignored):
         obj = root.address[0]
-        if self.header(obj).tid & GCFLAG_VISITED != 0:
+        if self.get_flags(obj) & GCFLAG_VISITED != 0:
             pass    # black -> black
         elif (self._debug_objects_to_trace_dict1.contains(obj) or
               self._debug_objects_to_trace_dict2.contains(obj)):
             pass    # black -> gray
-        elif self.header(obj).tid & GCFLAG_NO_HEAP_PTRS != 0:
+        elif self.get_flags(obj) & GCFLAG_NO_HEAP_PTRS != 0:
             pass    # black -> white-but-prebuilt-so-dont-care
         elif self._is_pinned(obj):
             # black -> pinned: the pinned object is a white one as
@@ -1267,23 +1267,23 @@
         # don't have any GC pointer or are pinned objects
         typeid = self.get_type_id(obj)
         if self.has_gcptr(typeid) and not self._is_pinned(obj):
-            ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS != 0,
+            ll_assert(self.get_flags(obj) & GCFLAG_TRACK_YOUNG_PTRS != 0,
                       "missing GCFLAG_TRACK_YOUNG_PTRS")
         # the GCFLAG_FINALIZATION_ORDERING should not be set between coll.
-        ll_assert(self.header(obj).tid & GCFLAG_FINALIZATION_ORDERING == 0,
+        ll_assert(self.get_flags(obj) & GCFLAG_FINALIZATION_ORDERING == 0,
                   "unexpected GCFLAG_FINALIZATION_ORDERING")
         # the GCFLAG_CARDS_SET should not be set between collections
-        ll_assert(self.header(obj).tid & GCFLAG_CARDS_SET == 0,
+        ll_assert(self.get_flags(obj) & GCFLAG_CARDS_SET == 0,
                   "unexpected GCFLAG_CARDS_SET")
         # if the GCFLAG_HAS_CARDS is set, check that all bits are zero now
-        if self.header(obj).tid & GCFLAG_HAS_CARDS:
+        if self.get_flags(obj) & GCFLAG_HAS_CARDS:
             if self.card_page_indices <= 0:
                 ll_assert(False, "GCFLAG_HAS_CARDS but not using card marking")
                 return
             typeid = self.get_type_id(obj)
             ll_assert(self.has_gcptr_in_varsize(typeid),
                       "GCFLAG_HAS_CARDS but not has_gcptr_in_varsize")
-            ll_assert(self.header(obj).tid & GCFLAG_NO_HEAP_PTRS == 0,
+            ll_assert(self.get_flags(obj) & GCFLAG_NO_HEAP_PTRS == 0,
                       "GCFLAG_HAS_CARDS && GCFLAG_NO_HEAP_PTRS")
             offset_to_length = self.varsize_offset_to_length(typeid)
             length = (obj + offset_to_length).signed[0]
@@ -1306,7 +1306,7 @@
         # This check is called before scanning starts.
         # Scanning is done in a single step.
         # the GCFLAG_VISITED should not be set between collections
-        ll_assert(self.header(obj).tid & GCFLAG_VISITED == 0,
+        ll_assert(self.get_flags(obj) & GCFLAG_VISITED == 0,
                   "unexpected GCFLAG_VISITED")
 
         # All other invariants from the sweeping phase should still be
@@ -1345,11 +1345,11 @@
         return cls.minimal_size_in_nursery
 
     def write_barrier(self, addr_struct):
-        if self.header(addr_struct).tid & GCFLAG_TRACK_YOUNG_PTRS:
+        if self.get_flags(addr_struct) & GCFLAG_TRACK_YOUNG_PTRS:
             self.remember_young_pointer(addr_struct)
 
     def write_barrier_from_array(self, addr_array, index):
-        if self.header(addr_array).tid & GCFLAG_TRACK_YOUNG_PTRS:
+        if self.get_flags(addr_array) & GCFLAG_TRACK_YOUNG_PTRS:
             if self.card_page_indices > 0:
                 self.remember_young_pointer_from_array2(addr_array, index)
             else:
@@ -1367,7 +1367,7 @@
             #
             if DEBUG:   # note: PYPY_GC_DEBUG=1 does not enable this
                 ll_assert(self.debug_is_old_object(addr_struct) or
-                          self.header(addr_struct).tid & GCFLAG_HAS_CARDS != 0,
+                          self.get_flags(addr_struct) & GCFLAG_HAS_CARDS != 0,
                       "young object with GCFLAG_TRACK_YOUNG_PTRS and no cards")
             #
             # We need to remove the flag GCFLAG_TRACK_YOUNG_PTRS and add
@@ -1563,7 +1563,7 @@
             # visit shadow to keep it alive
             # XXX seems like it is save to set GCFLAG_VISITED, however
             # should be double checked
-            self.header(shadow).tid |= GCFLAG_VISITED
+            self.set_flags(shadow, GCFLAG_VISITED)
             new_shadow_object_dict.setitem(obj, shadow)
 
     # ----------
@@ -1711,7 +1711,7 @@
             #
             # clean up object's flags
             obj = cur + size_gc_header
-            self.header(obj).tid &= ~GCFLAG_VISITED
+            self.remove_flags(obj, GCFLAG_VISITED)
             #
             # create a new nursery barrier for the pinned object
             nursery_barriers.append(cur)
@@ -1756,8 +1756,8 @@
         debug_stop("gc-minor")
 
     def _reset_flag_old_objects_pointing_to_pinned(self, obj, ignore):
-        assert self.header(obj).tid & GCFLAG_PINNED_OBJECT_PARENT_KNOWN
-        self.header(obj).tid &= ~GCFLAG_PINNED_OBJECT_PARENT_KNOWN
+        assert self.get_flags(obj) & GCFLAG_PINNED_OBJECT_PARENT_KNOWN
+        self.remove_flags(obj, GCFLAG_PINNED_OBJECT_PARENT_KNOWN)
 
     def _visit_old_objects_pointing_to_pinned(self, obj, ignore):
         self.trace(obj, self._trace_drag_out, obj)
@@ -1794,9 +1794,9 @@
             obj = oldlist.pop()
             #
             # Remove the GCFLAG_CARDS_SET flag.
-            ll_assert(self.header(obj).tid & GCFLAG_CARDS_SET != 0,
+            ll_assert(self.get_flags(obj) & GCFLAG_CARDS_SET != 0,
                 "!GCFLAG_CARDS_SET but object in 'old_objects_with_cards_set'")
-            self.header(obj).tid &= ~GCFLAG_CARDS_SET
+            self.remove_flags(obj, GCFLAG_CARDS_SET)
             #
             # Get the number of card marker bytes in the header.
             typeid = self.get_type_id(obj)
@@ -1809,7 +1809,7 @@
             # means that it is in 'old_objects_pointing_to_young' and
             # will be fully traced by collect_oldrefs_to_nursery() just
             # afterwards.
-            if self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS == 0:
+            if self.get_flags(obj) & GCFLAG_TRACK_YOUNG_PTRS == 0:
                 #
                 # In that case, we just have to reset all card bits.
                 while bytes > 0:
@@ -1849,7 +1849,7 @@
                 ll_assert(not self.is_in_nursery(obj),
                           "expected nursery obj in collect_cardrefs_to_nursery")
                 if self.gc_state == STATE_MARKING:
-                    self.header(obj).tid &= ~GCFLAG_VISITED
+                    self.remove_flags(obj, GCFLAG_VISITED)
                     self.more_objects_to_trace.append(obj)
 
 
@@ -1862,13 +1862,13 @@
             #
             # Check that the flags are correct: we must not have
             # GCFLAG_TRACK_YOUNG_PTRS so far.
-            ll_assert(self.header(obj).tid & GCFLAG_TRACK_YOUNG_PTRS == 0,
+            ll_assert(self.get_flags(obj) & GCFLAG_TRACK_YOUNG_PTRS == 0,
                       "old_objects_pointing_to_young contains obj with "
                       "GCFLAG_TRACK_YOUNG_PTRS")
             #
             # Add the flag GCFLAG_TRACK_YOUNG_PTRS.  All live objects should
             # have this flag set after a nursery collection.
-            self.header(obj).tid |= GCFLAG_TRACK_YOUNG_PTRS
+            self.add_flags(obj, GCFLAG_TRACK_YOUNG_PTRS)
             #
             # Trace the 'obj' to replace pointers to nursery with pointers
             # outside the nursery, possibly forcing nursery objects out
@@ -1908,7 +1908,7 @@
         # Additionally, ignore pinned objects.
         #
         obj = root.address[0]
-        if (self.header(obj).tid & (GCFLAG_VISITED | GCFLAG_PINNED)) == 0:
+        if (self.get_flags(obj) & (GCFLAG_VISITED | GCFLAG_PINNED)) == 0:
             self.more_objects_to_trace.append(obj)
 
     def _trace_drag_out(self, root, parent):
@@ -1929,7 +1929,7 @@
             return
         #
         size_gc_header = self.gcheaderbuilder.size_gc_header
-        if self.header(obj).tid & (GCFLAG_HAS_SHADOW | GCFLAG_PINNED) == 0:
+        if self.get_flags(obj) & (GCFLAG_HAS_SHADOW | GCFLAG_PINNED) == 0:
             #
             # Common case: 'obj' was not already forwarded (otherwise
             # tid == -42, containing all flags), and it doesn't have the
@@ -1957,11 +1957,11 @@
             # become dead and be removed just because the first parent of it
             # is dead and collected.
             if parent != llmemory.NULL and \
-                not self.header(parent).tid & GCFLAG_PINNED_OBJECT_PARENT_KNOWN:
+                not self.get_flags(parent) & GCFLAG_PINNED_OBJECT_PARENT_KNOWN:
                 #
                 self.old_objects_pointing_to_pinned.append(parent)
                 self.updated_old_objects_pointing_to_pinned = True
-                self.header(parent).tid |= GCFLAG_PINNED_OBJECT_PARENT_KNOWN
+                self.set_flags(parent, GCFLAG_PINNED_OBJECT_PARENT_KNOWN)
             #
             if hdr.tid & GCFLAG_VISITED:
                 return
@@ -1981,7 +1981,7 @@
             #
             # Remove the flag GCFLAG_HAS_SHADOW, so that it doesn't get
             # copied to the shadow itself.
-            self.header(obj).tid &= ~GCFLAG_HAS_SHADOW
+            self.remove_flags(obj, GCFLAG_HAS_SHADOW)
             #
             totalsize = size_gc_header + self.get_size(obj)
             self.nursery_surviving_size += raw_malloc_usage(totalsize)
@@ -1989,19 +1989,10 @@
         # Copy it.  Note that references to other objects in the
         # nursery are kept unchanged in this step.
         llmemory.raw_memcopy(obj - size_gc_header, newhdr, totalsize)
-        #
-        # Set the old object's tid to -42 (containing all flags) and
-        # replace the old object's content with the target address.
-        # A bit of no-ops to convince llarena that we are changing
-        # the layout, in non-translated versions.
         typeid = self.get_type_id(obj)
-        obj = llarena.getfakearenaaddress(obj)
-        llarena.arena_reset(obj - size_gc_header, totalsize, 0)
-        llarena.arena_reserve(obj - size_gc_header,
-                              size_gc_header + llmemory.sizeof(FORWARDSTUB))
-        self.header(obj).tid = -42
         newobj = newhdr + size_gc_header
-        llmemory.cast_adr_to_ptr(obj, FORWARDSTUBPTR).forw = newobj
+        self.copy_header(obj, newobj)
+        self.make_forwardstub(obj, newobj)
         #
         # Change the original pointer to this object.
         root.address[0] = newobj
@@ -2016,6 +2007,22 @@
 
     _trace_drag_out._always_inline_ = True
 
+    def make_forwardstub(self, obj, forward_to):
+        """Turn obj into a forwarding stub to forward_to."""
+        # Set the old object's tid to -42 (containing all flags) and
+        # replace the old object's content with the target address.
+        # A bit of no-ops to convince llarena that we are changing
+        # the layout, in non-translated versions.
+        size_gc_header = self.gcheaderbuilder.size_gc_header
+        totalsize = size_gc_header + self.get_size(obj)
+        obj = llarena.getfakearenaaddress(obj)
+        llarena.arena_reset(obj - size_gc_header, totalsize, 0)
+        llarena.arena_reserve(obj - size_gc_header,
+                              size_gc_header + llmemory.sizeof(FORWARDSTUB))
+        # self.set_flags(obj, -42)  # Can't work.
+        self.header(obj).tid = -42
+        llmemory.cast_adr_to_ptr(obj, FORWARDSTUBPTR).forw = forward_to
+
     def _visit_young_rawmalloced_object(self, obj):
         # 'obj' points to a young, raw-malloced object.
         # Any young rawmalloced object never seen by the code here
@@ -2102,7 +2109,7 @@
 
     def _add_to_more_objects_to_trace(self, obj, ignored):
         ll_assert(not self.is_in_nursery(obj), "unexpected nursery obj here")
-        self.header(obj).tid &= ~GCFLAG_VISITED
+        self.remove_flags(obj, GCFLAG_VISITED)
         self.more_objects_to_trace.append(obj)
 
     def minor_and_major_collection(self):
@@ -2337,23 +2344,23 @@
         debug_stop("gc-collect-step")
 
     def _sweep_old_objects_pointing_to_pinned(self, obj, new_list):
-        if self.header(obj).tid & GCFLAG_VISITED:
+        if self.get_flags(obj) & GCFLAG_VISITED:
             new_list.append(obj)
 
     def _free_if_unvisited(self, hdr):
         size_gc_header = self.gcheaderbuilder.size_gc_header
         obj = hdr + size_gc_header
-        if self.header(obj).tid & GCFLAG_VISITED:
-            self.header(obj).tid &= ~GCFLAG_VISITED
+        if self.get_flags(obj) & GCFLAG_VISITED:
+            self.remove_flags(obj, GCFLAG_VISITED)
             return False     # survives
         return True      # dies
 
     def _reset_gcflag_visited(self, obj, ignored):
-        self.header(obj).tid &= ~GCFLAG_VISITED
+        self.remove_flags(obj, GCFLAG_VISITED)
 
     def free_rawmalloced_object_if_unvisited(self, obj, check_flag):
-        if self.header(obj).tid & check_flag:
-            self.header(obj).tid &= ~check_flag   # survives
+        if self.get_flags(obj) & check_flag:
+            self.remove_flags(obj, check_flag)   # survives
             self.old_rawmalloced_objects.append(obj)
         else:
             size_gc_header = self.gcheaderbuilder.size_gc_header
@@ -2363,7 +2370,7 @@
             #
             # Must also include the card marker area, if any
             if (self.card_page_indices > 0    # <- this is constant-folded
-                and self.header(obj).tid & GCFLAG_HAS_CARDS):
+                and self.get_flags(obj) & GCFLAG_HAS_CARDS):
                 #
                 # Get the length and compute the number of extra bytes
                 typeid = self.get_type_id(obj)
@@ -2508,13 +2515,13 @@
         # the next major collection, at which point we want
         # it to look valid (but ready to be freed).
         shadow = shadowhdr + size_gc_header
-        self.header(shadow).tid = self.header(obj).tid
+        self.copy_header(obj, shadow)
         typeid = self.get_type_id(obj)
         if self.is_varsize(typeid):
             lenofs = self.varsize_offset_to_length(typeid)
             (shadow + lenofs).signed[0] = (obj + lenofs).signed[0]
         #
-        self.header(obj).tid |= GCFLAG_HAS_SHADOW
+        self.add_flags(obj, GCFLAG_HAS_SHADOW)
         self.nursery_objects_shadows.setitem(obj, shadow)
         return shadow
 
@@ -2524,7 +2531,7 @@
         # nursery.  Find or allocate a "shadow" object, which is
         # where the object will be moved by the next minor
         # collection
-        if self.header(obj).tid & GCFLAG_HAS_SHADOW:
+        if self.get_flags(obj) & GCFLAG_HAS_SHADOW:
             shadow = self.nursery_objects_shadows.get(obj)
             ll_assert(shadow != llmemory.NULL,
                       "GCFLAG_HAS_SHADOW but no shadow found")
@@ -2546,7 +2553,7 @@
             if self.is_in_nursery(obj):
                 obj = self._find_shadow(obj)
             elif is_hash:
-                if self.header(obj).tid & GCFLAG_HAS_SHADOW:
+                if self.get_flags(obj) & GCFLAG_HAS_SHADOW:
                     #
                     # For identityhash(), we need a special case for some
                     # prebuilt objects: their hash must be the same before
@@ -2598,7 +2605,7 @@
         new_objects = self.AddressStack()
         while self.old_objects_with_light_finalizers.non_empty():
             obj = self.old_objects_with_light_finalizers.pop()
-            if self.header(obj).tid & GCFLAG_VISITED:
+            if self.get_flags(obj) & GCFLAG_VISITED:
                 # surviving
                 new_objects.append(obj)
             else:
@@ -2624,7 +2631,7 @@
             x = self.objects_with_finalizers.popleft()
             ll_assert(self._finalization_state(x) != 1,
                       "bad finalization state 1")
-            if self.header(x).tid & GCFLAG_VISITED:
+            if self.get_flags(x) & GCFLAG_VISITED:
                 new_with_finalizer.append(x)
                 continue
             marked.append(x)
@@ -2669,7 +2676,7 @@
     _append_if_nonnull = staticmethod(_append_if_nonnull)
 
     def _finalization_state(self, obj):
-        tid = self.header(obj).tid
+        tid = self.get_flags(obj)
         if tid & GCFLAG_VISITED:
             if tid & GCFLAG_FINALIZATION_ORDERING:
                 return 2
@@ -2748,13 +2755,13 @@
             elif (bool(self.young_rawmalloced_objects) and
                   self.young_rawmalloced_objects.contains(pointing_to)):
                 # young weakref to a young raw-malloced object
-                if self.header(pointing_to).tid & GCFLAG_VISITED_RMY:
+                if self.get_flags(pointing_to) & GCFLAG_VISITED_RMY:
                     pass    # survives, but does not move
                 else:
                     (obj + offset).address[0] = llmemory.NULL
                     continue    # no need to remember this weakref any longer
             #
-            elif self.header(pointing_to).tid & GCFLAG_NO_HEAP_PTRS:
+            elif self.get_flags(pointing_to) & GCFLAG_NO_HEAP_PTRS:
                 # see test_weakref_to_prebuilt: it's not useful to put
                 # weakrefs into 'old_objects_with_weakrefs' if they point
                 # to a prebuilt object (they are immortal).  If moreover
@@ -2774,14 +2781,14 @@
         new_with_weakref = self.AddressStack()
         while self.old_objects_with_weakrefs.non_empty():
             obj = self.old_objects_with_weakrefs.pop()
-            if self.header(obj).tid & GCFLAG_VISITED == 0:
+            if self.get_flags(obj) & GCFLAG_VISITED == 0:
                 continue # weakref itself dies
             offset = self.weakpointer_offset(self.get_type_id(obj))
             pointing_to = (obj + offset).address[0]
-            ll_assert((self.header(pointing_to).tid & GCFLAG_NO_HEAP_PTRS)
+            ll_assert((self.get_flags(pointing_to) & GCFLAG_NO_HEAP_PTRS)
                       == 0, "registered old weakref should not "
                             "point to a NO_HEAP_PTRS obj")
-            tid = self.header(pointing_to).tid
+            tid = self.get_flags(pointing_to)
             if ((tid & (GCFLAG_VISITED | GCFLAG_FINALIZATION_ORDERING)) ==
                         GCFLAG_VISITED):
                 new_with_weakref.append(obj)
@@ -2933,7 +2940,7 @@
         elif (bool(self.young_rawmalloced_objects) and
               self.young_rawmalloced_objects.contains(obj)):
             # young weakref to a young raw-malloced object
-            if self.header(obj).tid & GCFLAG_VISITED_RMY:
+            if self.get_flags(obj) & GCFLAG_VISITED_RMY:
                 surviving = True    # survives, but does not move
             else:
                 surviving = False
@@ -3027,9 +3034,26 @@
         #  * GCFLAG_NO_HEAP_PTRS: immortal object never traced (so far)
         intobj = self._pyobj(pyobject).ob_pypy_link
         obj = llmemory.cast_int_to_adr(intobj)
-        if self.header(obj).tid & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
+        if self.get_flags(obj) & (GCFLAG_VISITED | GCFLAG_NO_HEAP_PTRS):
             surviving_list.append(pyobject)
             if surviving_dict:
                 surviving_dict.insertclean(obj, pyobject)
         else:
             self._rrc_free(pyobject)
+
+    # Methods meant to be overridden by subclasses that store flags elsewhere.
+
+    def copy_header(self, src, dest):
+        self.header(dest).tid = self.header(src).tid
+
+    def get_flags(self, obj):
+        return self.header(obj).tid
+
+    def set_flags(self, obj, flags):
+        self.header(obj).tid=flags
+
+    def add_flags(self, obj, flags):
+        self.header(obj).tid|=flags
+
+    def remove_flags(self, obj, flags):
+        self.header(obj).tid&=~flags
diff --git a/rpython/memory/gc/incminimark_remoteheader.py b/rpython/memory/gc/incminimark_remoteheader.py
new file mode 100644
--- /dev/null
+++ b/rpython/memory/gc/incminimark_remoteheader.py
@@ -0,0 +1,45 @@
+"""Incminimark with GC flags stored in a separate page for fork-friendliness."""
+
+from rpython.memory.gc import incminimark
+from rpython.rtyper.lltypesystem import lltype, llmemory
+
+class IncrementalMiniMarkRemoteHeaderGC(incminimark.IncrementalMiniMarkGC):
+    # The GC header is similar to incminimark, except that the flags can be
+    # placed anywhere, not just in the bits of tid.
+    # TODO: Actually place flags somewhere other than tid.
+    HDR = lltype.Struct('header',
+                        ('tid', lltype.Signed),
+                        ('remote_flags', lltype.Ptr(lltype.FixedSizeArray(lltype.Signed, 1))))
+
+    def init_gc_object(self, addr, typeid16, flags=0):
+        super(IncrementalMiniMarkRemoteHeaderGC, self).init_gc_object(addr, typeid16, flags)
+        hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR))
+        hdr.remote_flags = lltype.direct_fieldptr(hdr, 'tid')
+
+    def make_forwardstub(self, obj, forward_to):
+        assert (self.header(obj).remote_flags
+                == lltype.direct_fieldptr(self.header(obj), 'tid')), \
+            "Nursery objects should not have separately-allocated flags."
+        super(IncrementalMiniMarkRemoteHeaderGC, self).make_forwardstub(obj, forward_to)
+        hdr = self.header(obj)
+        hdr.remote_flags = lltype.direct_fieldptr(hdr, 'tid')
+
+    def copy_header(self, src, dest):
+        dest_hdr = self.header(dest)
+        dest_hdr.tid = self.get_flags(src)
+        dest_hdr.remote_flags = lltype.direct_fieldptr(dest_hdr, 'tid')
+        # TODO: make new remote flag sometimes.
+
+    # Manipulate flags through a pointer.
+
+    def get_flags(self, obj):
+        return self.header(obj).remote_flags[0]
+
+    def set_flags(self, obj, flags):
+        self.header(obj).remote_flags[0] = flags
+
+    def add_flags(self, obj, flags):
+        self.header(obj).remote_flags[0] |= flags
+
+    def remove_flags(self, obj, flags):
+        self.header(obj).remote_flags[0] &= ~flags
diff --git a/rpython/memory/gc/test/test_direct.py b/rpython/memory/gc/test/test_direct.py
--- a/rpython/memory/gc/test/test_direct.py
+++ b/rpython/memory/gc/test/test_direct.py
@@ -668,6 +668,9 @@
         self.gc.debug_gc_step_until(incminimark.STATE_SCANNING)
         assert self.stackroots[1].x == 13
 
+class TestIncrementalMiniMarkRemoteHeaderGCSimple(TestIncrementalMiniMarkGCSimple):
+    from rpython.memory.gc.incminimark_remoteheader import IncrementalMiniMarkRemoteHeaderGC as GCClass
+
 class TestIncrementalMiniMarkGCFull(DirectGCTest):
     from rpython.memory.gc.incminimark import IncrementalMiniMarkGC as GCClass
     def test_malloc_fixedsize_no_cleanup(self):
@@ -744,4 +747,5 @@
                 assert elem.prev == lltype.nullptr(S)
                 assert elem.next == lltype.nullptr(S)
 
-            
+class TestIncrementalMiniMarkRemoteHeaderGCFull(TestIncrementalMiniMarkGCFull):
+    from rpython.memory.gc.incminimark_remoteheader import IncrementalMiniMarkRemoteHeaderGC as GCClass


More information about the pypy-commit mailing list