Commits

Armin Rigo committed dab0958

Simplify the write barrier by only passing one argument, which is
the object we write into --- and not the new value written. See
comments in minimark. This allows means that assume_young_pointers
is removed because it's now the same as write_barrier.

Comments (0)

Files changed (17)

rpython/jit/backend/arm/opassembler.py

         # Write code equivalent to write_barrier() in the GC: it checks
         # a flag in the object at arglocs[0], and if set, it calls a
         # helper piece of assembler.  The latter saves registers as needed
-        # and call the function jit_remember_young_pointer() from the GC.
+        # and call the function remember_young_pointer() from the GC.
         if we_are_translated():
             cls = self.cpu.gc_ll_descr.has_write_barrier_class()
             assert cls is not None and isinstance(descr, cls)

rpython/jit/backend/llsupport/gc.py

         """ Allocate a new frame, overwritten by tests
         """
         frame = jitframe.JITFRAME.allocate(frame_info)
-        llop.gc_assume_young_pointers(lltype.Void, frame)
+        llop.gc_writebarrier(lltype.Void, frame)
         return frame
 
 class JitFrameDescrs:
 
     def _check_valid_gc(self):
         # we need the hybrid or minimark GC for rgc._make_sure_does_not_move()
-        # to work.  Additionally, 'hybrid' is missing some stuff like
-        # jit_remember_young_pointer() for now.
+        # to work.  'hybrid' could work but isn't tested with the JIT.
         if self.gcdescr.config.translation.gc not in ('minimark',):
             raise NotImplementedError("--gc=%s not implemented with the JIT" %
                                       (self.gcdescr.config.translation.gc,))

rpython/jit/backend/llsupport/llmodel.py

                 new_frame.jf_savedata = frame.jf_savedata
                 new_frame.jf_guard_exc = frame.jf_guard_exc
                 # all other fields are empty
-                llop.gc_assume_young_pointers(lltype.Void, new_frame)
+                llop.gc_writebarrier(lltype.Void, new_frame)
                 return lltype.cast_opaque_ptr(llmemory.GCREF, new_frame)
             except Exception, e:
                 print "Unhandled exception", e, "in realloc_frame"

rpython/jit/backend/x86/assembler.py

         # Write code equivalent to write_barrier() in the GC: it checks
         # a flag in the object at arglocs[0], and if set, it calls a
         # helper piece of assembler.  The latter saves registers as needed
-        # and call the function jit_remember_young_pointer() from the GC.
+        # and call the function remember_young_pointer() from the GC.
         if we_are_translated():
             cls = self.cpu.gc_ll_descr.has_write_barrier_class()
             assert cls is not None and isinstance(descr, cls)

rpython/memory/gc/base.py

     def set_root_walker(self, root_walker):
         self.root_walker = root_walker
 
-    def write_barrier(self, newvalue, addr_struct):
+    def write_barrier(self, addr_struct):
         pass
 
     def size_gc_header(self, typeid=0):

rpython/memory/gc/generation.py

         addr = pointer.address[0]
         newaddr = self.copy(addr)
         pointer.address[0] = newaddr
-        self.write_into_last_generation_obj(obj, newaddr)
+        self.write_into_last_generation_obj(obj)
 
     # ____________________________________________________________
     # Implementation of nursery-only collections
     #  "if addr_struct.int0 & JIT_WB_IF_FLAG: remember_young_pointer()")
     JIT_WB_IF_FLAG = GCFLAG_NO_YOUNG_PTRS
 
-    def write_barrier(self, newvalue, addr_struct):
+    def write_barrier(self, addr_struct):
         if self.header(addr_struct).tid & GCFLAG_NO_YOUNG_PTRS:
-            self.remember_young_pointer(addr_struct, newvalue)
+            self.remember_young_pointer(addr_struct)
 
     def _setup_wb(self):
         DEBUG = self.DEBUG
         # For x86, there is also an extra requirement: when the JIT calls
         # remember_young_pointer(), it assumes that it will not touch the SSE
         # registers, so it does not save and restore them (that's a *hack*!).
-        def remember_young_pointer(addr_struct, addr):
+        def remember_young_pointer(addr_struct):
             #llop.debug_print(lltype.Void, "\tremember_young_pointer",
-            #                 addr_struct, "<-", addr)
+            #                 addr_struct)
             if DEBUG:
                 ll_assert(not self.is_in_nursery(addr_struct),
                           "nursery object with GCFLAG_NO_YOUNG_PTRS")
             #
             # What is important in this function is that it *must*
             # clear the flag GCFLAG_NO_YOUNG_PTRS from 'addr_struct'
-            # if 'addr' is in the nursery.  It is ok if, accidentally,
-            # it also clears the flag in some more rare cases, like
-            # 'addr' being a tagged pointer whose value happens to be
-            # a large integer that fools is_in_nursery().
-            if self.appears_to_be_in_nursery(addr):
-                self.old_objects_pointing_to_young.append(addr_struct)
-                self.header(addr_struct).tid &= ~GCFLAG_NO_YOUNG_PTRS
-            self.write_into_last_generation_obj(addr_struct, addr)
+            # if the newly written value is in the nursery.  It is ok
+            # if it also clears the flag in some more cases --- it is
+            # a win to not actually pass the 'newvalue' pointer here.
+            self.old_objects_pointing_to_young.append(addr_struct)
+            self.header(addr_struct).tid &= ~GCFLAG_NO_YOUNG_PTRS
+            self.write_into_last_generation_obj(addr_struct)
         remember_young_pointer._dont_inline_ = True
         self.remember_young_pointer = remember_young_pointer
 
-    def write_into_last_generation_obj(self, addr_struct, addr):
+    def write_into_last_generation_obj(self, addr_struct):
         objhdr = self.header(addr_struct)
         if objhdr.tid & GCFLAG_NO_HEAP_PTRS:
-            if (self.is_valid_gc_object(addr) and
-                    not self.is_last_generation(addr)):
-                objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
-                self.last_generation_root_objects.append(addr_struct)
-    write_into_last_generation_obj._always_inline_ = True
-
-    def assume_young_pointers(self, addr_struct):
-        objhdr = self.header(addr_struct)
-        if objhdr.tid & GCFLAG_NO_YOUNG_PTRS:
-            self.old_objects_pointing_to_young.append(addr_struct)
-            objhdr.tid &= ~GCFLAG_NO_YOUNG_PTRS
-        if objhdr.tid & GCFLAG_NO_HEAP_PTRS:
             objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
             self.last_generation_root_objects.append(addr_struct)
+    write_into_last_generation_obj._always_inline_ = True
 
     def writebarrier_before_copy(self, source_addr, dest_addr,
                                  source_start, dest_start, length):

rpython/memory/gc/minimark.py

     def JIT_minimal_size_in_nursery(cls):
         return cls.minimal_size_in_nursery
 
-    def write_barrier(self, newvalue, addr_struct):
+    def write_barrier(self, addr_struct):
         if self.header(addr_struct).tid & GCFLAG_TRACK_YOUNG_PTRS:
-            self.remember_young_pointer(addr_struct, newvalue)
+            self.remember_young_pointer(addr_struct)
 
-    def write_barrier_from_array(self, newvalue, addr_array, index):
+    def write_barrier_from_array(self, addr_array, index):
         if self.header(addr_array).tid & GCFLAG_TRACK_YOUNG_PTRS:
             if self.card_page_indices > 0:     # <- constant-folded
                 self.remember_young_pointer_from_array2(addr_array, index)
             else:
-                self.remember_young_pointer(addr_array, newvalue)
+                self.remember_young_pointer(addr_array)
 
     def _init_writebarrier_logic(self):
         DEBUG = self.DEBUG
         # instead of keeping it as a regular method is to
         # make the code in write_barrier() marginally smaller
         # (which is important because it is inlined *everywhere*).
-        def remember_young_pointer(addr_struct, newvalue):
+        def remember_young_pointer(addr_struct):
             # 'addr_struct' is the address of the object in which we write.
-            # 'newvalue' is the address that we are going to write in there.
             # We know that 'addr_struct' has GCFLAG_TRACK_YOUNG_PTRS so far.
             #
             if DEBUG:   # note: PYPY_GC_DEBUG=1 does not enable this
                           self.header(addr_struct).tid & GCFLAG_HAS_CARDS != 0,
                       "young object with GCFLAG_TRACK_YOUNG_PTRS and no cards")
             #
-            # If it seems that what we are writing is a pointer to a young obj
-            # (as checked with appears_to_be_young()), then we need
-            # to remove the flag GCFLAG_TRACK_YOUNG_PTRS and add the object
-            # to the list 'old_objects_pointing_to_young'.  We know that
-            # 'addr_struct' cannot be in the nursery, because nursery objects
-            # never have the flag GCFLAG_TRACK_YOUNG_PTRS to start with.
+            # We need to remove the flag GCFLAG_TRACK_YOUNG_PTRS and add
+            # the object to the list 'old_objects_pointing_to_young'.
+            # We know that 'addr_struct' cannot be in the nursery,
+            # because nursery objects never have the flag
+            # GCFLAG_TRACK_YOUNG_PTRS to start with.  Note that in
+            # theory we don't need to do that if the pointer that we're
+            # writing into the object isn't pointing to a young object.
+            # However, it isn't really a win, because then sometimes
+            # we're going to call this function a lot of times for the
+            # same object; moreover we'd need to pass the 'newvalue' as
+            # an argument here.  The JIT has always called a
+            # 'newvalue'-less version, too.
+            self.old_objects_pointing_to_young.append(addr_struct)
             objhdr = self.header(addr_struct)
-            if self.appears_to_be_young(newvalue):
-                self.old_objects_pointing_to_young.append(addr_struct)
-                objhdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
+            objhdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
             #
             # Second part: if 'addr_struct' is actually a prebuilt GC
             # object and it's the first time we see a write to it, we
-            # add it to the list 'prebuilt_root_objects'.  Note that we
-            # do it even in the (rare?) case of 'addr' being NULL or another
-            # prebuilt object, to simplify code.
+            # add it to the list 'prebuilt_root_objects'.
             if objhdr.tid & GCFLAG_NO_HEAP_PTRS:
                 objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
                 self.prebuilt_root_objects.append(addr_struct)
         remember_young_pointer._dont_inline_ = True
         self.remember_young_pointer = remember_young_pointer
         #
-        def jit_remember_young_pointer(addr_struct):
-            # minimal version of the above, with just one argument,
-            # called by the JIT when GCFLAG_TRACK_YOUNG_PTRS is set
-            self.old_objects_pointing_to_young.append(addr_struct)
-            objhdr = self.header(addr_struct)
-            objhdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
-            if objhdr.tid & GCFLAG_NO_HEAP_PTRS:
-                objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
-                self.prebuilt_root_objects.append(addr_struct)
-        self.jit_remember_young_pointer = jit_remember_young_pointer
-        #
         if self.card_page_indices > 0:
             self._init_writebarrier_with_card_marker()
 
             # called by the JIT when GCFLAG_TRACK_YOUNG_PTRS is set
             # but GCFLAG_CARDS_SET is cleared.  This tries to set
             # GCFLAG_CARDS_SET if possible; otherwise, it falls back
-            # to jit_remember_young_pointer().
+            # to remember_young_pointer().
             objhdr = self.header(addr_array)
             if objhdr.tid & GCFLAG_HAS_CARDS:
                 self.old_objects_with_cards_set.append(addr_array)
                 objhdr.tid |= GCFLAG_CARDS_SET
             else:
-                self.jit_remember_young_pointer(addr_array)
+                self.remember_young_pointer(addr_array)
 
         self.jit_remember_young_pointer_from_array = (
             jit_remember_young_pointer_from_array)
         return llarena.getfakearenaaddress(addr_byte) + (~byteindex)
 
 
-    def assume_young_pointers(self, addr_struct):
-        """Called occasionally by the JIT to mean ``assume that 'addr_struct'
-        may now contain young pointers.''
-        """
-        objhdr = self.header(addr_struct)
-        if objhdr.tid & GCFLAG_TRACK_YOUNG_PTRS:
-            self.old_objects_pointing_to_young.append(addr_struct)
-            objhdr.tid &= ~GCFLAG_TRACK_YOUNG_PTRS
-            #
-            if objhdr.tid & GCFLAG_NO_HEAP_PTRS:
-                objhdr.tid &= ~GCFLAG_NO_HEAP_PTRS
-                self.prebuilt_root_objects.append(addr_struct)
-
     def writebarrier_before_copy(self, source_addr, dest_addr,
                                  source_start, dest_start, length):
         """ This has the same effect as calling writebarrier over

rpython/memory/gc/test/test_direct.py

 
     def write(self, p, fieldname, newvalue):
         if self.gc.needs_write_barrier:
-            newaddr = llmemory.cast_ptr_to_adr(newvalue)
             addr_struct = llmemory.cast_ptr_to_adr(p)
-            self.gc.write_barrier(newaddr, addr_struct)
+            self.gc.write_barrier(addr_struct)
         setattr(p, fieldname, newvalue)
 
     def writearray(self, p, index, newvalue):
         if self.gc.needs_write_barrier:
-            newaddr = llmemory.cast_ptr_to_adr(newvalue)
             addr_struct = llmemory.cast_ptr_to_adr(p)
             if hasattr(self.gc, 'write_barrier_from_array'):
-                self.gc.write_barrier_from_array(newaddr, addr_struct, index)
+                self.gc.write_barrier_from_array(addr_struct, index)
             else:
-                self.gc.write_barrier(newaddr, addr_struct)
+                self.gc.write_barrier(addr_struct)
         p[index] = newvalue
 
     def malloc(self, TYPE, n=None):
         assert calls == ['semispace_collect']
         calls = []
 
-    def test_assume_young_pointers(self):
+    def test_write_barrier_direct(self):
         s0 = lltype.malloc(S, immortal=True)
         self.consider_constant(s0)
         s = self.malloc(S)
         s.x = 1
         s0.next = s
-        self.gc.assume_young_pointers(llmemory.cast_ptr_to_adr(s0))
+        self.gc.write_barrier(llmemory.cast_ptr_to_adr(s0))
 
         self.gc.collect(0)
 
         assert hdr_src.tid & minimark.GCFLAG_HAS_CARDS
         assert hdr_dst.tid & minimark.GCFLAG_HAS_CARDS
         #
-        young_p = self.malloc(S)
-        self.gc.write_barrier_from_array(young_p, addr_src, 0)
+        self.gc.write_barrier_from_array(addr_src, 0)
         index_in_third_page = int(2.5 * self.gc.card_page_indices)
         assert index_in_third_page < largeobj_size
-        self.gc.write_barrier_from_array(young_p, addr_src,
-                                         index_in_third_page)
+        self.gc.write_barrier_from_array(addr_src, index_in_third_page)
         #
         assert hdr_src.tid & minimark.GCFLAG_CARDS_SET
         addr_byte = self.gc.get_card(addr_src, 0)

rpython/memory/gctransform/framework.py

         else:
             self.shrink_array_ptr = None
 
-        if hasattr(GCClass, 'assume_young_pointers'):
-            # xxx should really be a noop for gcs without generations
-            self.assume_young_pointers_ptr = getfn(
-                GCClass.assume_young_pointers.im_func,
-                [s_gc, annmodel.SomeAddress()],
-                annmodel.s_None)
-
         if hasattr(GCClass, 'heap_stats'):
             self.heap_stats_ptr = getfn(GCClass.heap_stats.im_func,
                     [s_gc], annmodel.SomePtr(lltype.Ptr(ARRAY_TYPEID_MAP)),
         if GCClass.needs_write_barrier:
             self.write_barrier_ptr = getfn(GCClass.write_barrier.im_func,
                                            [s_gc,
-                                            annmodel.SomeAddress(),
                                             annmodel.SomeAddress()],
                                            annmodel.s_None,
                                            inline=True)
-            func = getattr(gcdata.gc, 'jit_remember_young_pointer', None)
+            func = getattr(gcdata.gc, 'remember_young_pointer', None)
             if func is not None:
                 # func should not be a bound method, but a real function
                 assert isinstance(func, types.FunctionType)
                 self.write_barrier_from_array_ptr = getfn(func.im_func,
                                            [s_gc,
                                             annmodel.SomeAddress(),
-                                            annmodel.SomeAddress(),
                                             annmodel.SomeInteger()],
                                            annmodel.s_None,
                                            inline=True)
                                   v_addr, v_length],
                   resultvar=op.result)
 
-    def gct_gc_assume_young_pointers(self, hop):
-        if not hasattr(self, 'assume_young_pointers_ptr'):
+    def gct_gc_writebarrier(self, hop):
+        if self.write_barrier_ptr is None:
             return
         op = hop.spaceop
         v_addr = op.args[0]
         if v_addr.concretetype != llmemory.Address:
             v_addr = hop.genop('cast_ptr_to_adr',
                                [v_addr], resulttype=llmemory.Address)
-        hop.genop("direct_call", [self.assume_young_pointers_ptr,
+        hop.genop("direct_call", [self.write_barrier_ptr,
                                   self.c_const_gc, v_addr])
 
     def gct_gc_heap_stats(self, hop):
             and not isinstance(v_newvalue, Constant)
             and v_struct.concretetype.TO._gckind == "gc"
             and hop.spaceop not in self.clean_sets):
-            v_newvalue = hop.genop("cast_ptr_to_adr", [v_newvalue],
-                                   resulttype = llmemory.Address)
             v_structaddr = hop.genop("cast_ptr_to_adr", [v_struct],
                                      resulttype = llmemory.Address)
             if (self.write_barrier_from_array_ptr is not None and
                 assert v_index.concretetype == lltype.Signed
                 hop.genop("direct_call", [self.write_barrier_from_array_ptr,
                                           self.c_const_gc,
-                                          v_newvalue,
                                           v_structaddr,
                                           v_index])
             else:
                 self.write_barrier_calls += 1
                 hop.genop("direct_call", [self.write_barrier_ptr,
                                           self.c_const_gc,
-                                          v_newvalue,
                                           v_structaddr])
         hop.rename('bare_' + opname)
 

rpython/memory/gctransform/shadowstack.py

                   "save_current_state_away: broken shadowstack")
         #shadowstackref.fullstack = True
         #
-        # cannot use llop.gc_assume_young_pointers() here, because
+        # cannot use llop.gc_writebarrier() here, because
         # we are in a minimally-transformed GC helper :-/
         gc = self.gcdata.gc
-        if hasattr(gc.__class__, 'assume_young_pointers'):
+        if hasattr(gc.__class__, 'write_barrier'):
             shadowstackadr = llmemory.cast_ptr_to_adr(shadowstackref)
-            gc.assume_young_pointers(shadowstackadr)
+            gc.write_barrier(shadowstackadr)
         #
         self.gcdata.root_stack_top = llmemory.NULL  # to detect missing restore
 

rpython/memory/gcwrapper.py

                         assert (type(index) is int    # <- fast path
                                 or lltype.typeOf(index) == lltype.Signed)
                         self.gc.write_barrier_from_array(
-                            llmemory.cast_ptr_to_adr(newvalue),
                             llmemory.cast_ptr_to_adr(toplevelcontainer),
                             index)
                         wb = False
             #
             if wb:
                 self.gc.write_barrier(
-                    llmemory.cast_ptr_to_adr(newvalue),
                     llmemory.cast_ptr_to_adr(toplevelcontainer))
         llheap.setinterior(toplevelcontainer, inneraddr, INNERTYPE, newvalue)
 

rpython/memory/test/test_transformed_gc.py

         res = run([100, 100])
         assert res == 200
 
-    def define_assume_young_pointers(cls):
+    def define_write_barrier_direct(cls):
         from rpython.rlib import rgc
         S = lltype.GcForwardReference()
         S.become(lltype.GcStruct('S',
             s = lltype.malloc(S)
             s.x = 42
             llop.bare_setfield(lltype.Void, s0, void('next'), s)
-            llop.gc_assume_young_pointers(lltype.Void,
-                                          llmemory.cast_ptr_to_adr(s0))
+            llop.gc_writebarrier(lltype.Void, llmemory.cast_ptr_to_adr(s0))
             rgc.collect(0)
             return s0.next.x
 
 
         return f, cleanup, None
 
-    def test_assume_young_pointers(self):
-        run = self.runner("assume_young_pointers")
+    def test_write_barrier_direct(self):
+        run = self.runner("write_barrier_direct")
         res = run([])
         assert res == 42
 

rpython/rlib/_stacklet_asmgcc.py

         self.suspstack = NULL_SUSPSTACK
         ll_assert(bool(s.anchor), "s.anchor should not be null")
         s.handle = handle
-        llop.gc_assume_young_pointers(lltype.Void, llmemory.cast_ptr_to_adr(s))
+        llop.gc_writebarrier(lltype.Void, llmemory.cast_ptr_to_adr(s))
         return s
 
     def get_result_suspstack(self, h):

rpython/rtyper/lltypesystem/lloperation.py

     'gc_thread_die'       : LLOp(),
     'gc_thread_before_fork':LLOp(),   # returns an opaque address
     'gc_thread_after_fork': LLOp(),   # arguments: (result_of_fork, opaqueaddr)
-    'gc_assume_young_pointers': LLOp(canrun=True),
+    'gc_writebarrier':      LLOp(canrun=True),
     'gc_writebarrier_before_copy': LLOp(canrun=True),
     'gc_heap_stats'       : LLOp(canmallocgc=True),
 

rpython/rtyper/lltypesystem/opimpl.py

 def op_get_member_index(memberoffset):
     raise NotImplementedError
 
-def op_gc_assume_young_pointers(addr):
+def op_gc_writebarrier(addr):
     pass
 
 def op_shrink_array(array, smallersize):

rpython/translator/c/gc.py

     def OP_GC_THREAD_AFTER_FORK(self, funcgen, op):
         return ''
 
-    def OP_GC_ASSUME_YOUNG_POINTERS(self, funcgen, op):
+    def OP_GC_WRITEBARRIER(self, funcgen, op):
         return ''
 
     def OP_GC_STACK_BOTTOM(self, funcgen, op):
                self.tid_fieldname(tid_field),
                funcgen.expr(c_skipoffset)))
 
-    def OP_GC_ASSUME_YOUNG_POINTERS(self, funcgen, op):
+    def OP_GC_WRITEBARRIER(self, funcgen, op):
         raise Exception("the FramewokGCTransformer should handle this")
 
     def OP_GC_GCFLAG_EXTRA(self, funcgen, op):

rpython/translator/c/test/test_boehm.py

         run = self.getcompiled(f)
         assert run() == 0x62024230
 
-    def test_assume_young_pointers_nop(self):
+    def test_write_barrier_nop(self):
         S = lltype.GcStruct('S', ('x', lltype.Signed))
         s = lltype.malloc(S)
         s.x = 0
         def f():
-            llop.gc_assume_young_pointers(lltype.Void,
-                                          llmemory.cast_ptr_to_adr(s))
+            llop.gc_writebarrier(lltype.Void, llmemory.cast_ptr_to_adr(s))
             return True
         run = self.getcompiled(f)
         assert run() == True