[pypy-commit] pypy cpyext-gc-support: Start implementing rawrefcount in the GC

arigo noreply at buildbot.pypy.org
Fri Oct 16 11:51:07 EDT 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cpyext-gc-support
Changeset: r80285:62d0fd30e60a
Date: 2015-10-16 17:51 +0200
http://bitbucket.org/pypy/pypy/changeset/62d0fd30e60a/

Log:	Start implementing rawrefcount in the GC

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
@@ -1080,35 +1080,19 @@
                   "odd-valued (i.e. tagged) pointer unexpected here")
         return self.nursery <= addr < self.nursery + self.nursery_size
 
-    def appears_to_be_young(self, addr):
-        # "is a valid addr to a young object?"
-        # but it's ok to occasionally return True accidentally.
-        # Maybe the best implementation would be a bloom filter
-        # of some kind instead of the dictionary lookup that is
-        # sometimes done below.  But the expected common answer
-        # is "Yes" because addr points to the nursery, so it may
-        # not be useful to optimize the other case too much.
-        #
-        # First, if 'addr' appears to be a pointer to some place within
-        # the nursery, return True
-        if not self.translated_to_c:
-            # When non-translated, filter out tagged pointers explicitly.
-            # When translated, it may occasionally give a wrong answer
-            # of True if 'addr' is a tagged pointer with just the wrong value.
-            if not self.is_valid_gc_object(addr):
-                return False
-
+    def is_young_object(self, addr):
+        # Check if the object at 'addr' is young.
+        if not self.is_valid_gc_object(addr):
+            return False     # filter out tagged pointers explicitly.
         if self.nursery <= addr < self.nursery_top:
             return True      # addr is in the nursery
-        #
         # Else, it may be in the set 'young_rawmalloced_objects'
         return (bool(self.young_rawmalloced_objects) and
                 self.young_rawmalloced_objects.contains(addr))
-    appears_to_be_young._always_inline_ = True
 
     def debug_is_old_object(self, addr):
         return (self.is_valid_gc_object(addr)
-                and not self.appears_to_be_young(addr))
+                and not self.is_young_object(addr))
 
     def is_forwarded(self, obj):
         """Returns True if the nursery obj is marked as forwarded.
@@ -2745,3 +2729,41 @@
                 (obj + offset).address[0] = llmemory.NULL
         self.old_objects_with_weakrefs.delete()
         self.old_objects_with_weakrefs = new_with_weakref
+
+
+    # ----------
+    # RawRefCount
+
+    OB_REFCNT    = 0
+    OB_PYPY_LINK = 1
+
+    rrc_enabled = False
+
+    def rawrefcount_init(self):
+        # see pypy/doc/discussion/rawrefcount.rst
+        if not self.rrc_enabled:
+            self.rrc_p_list_young = self.AddressStack()
+            self.rrc_p_list_old   = self.AddressStack()
+            self.rrc_o_list_young = self.AddressStack()
+            self.rrc_o_list_old   = self.AddressStack()
+            self.rrc_dict         = self.AddressDict()
+            self.rrc_enabled = True
+
+    def rawrefcount_create_link_pypy(self, gcobj, pyobject):
+        ll_assert(self.rrc_enabled, "rawrefcount.init not called")
+        obj = llmemory.cast_ptr_to_adr(gcobj)
+        if self.is_young_object(obj):
+            self.rrc_p_list_young.append(obj)
+        else:
+            self.rrc_p_list_old.append(obj)
+        objint = llmemory.cast_adr_to_int(obj, mode="symbolic")
+        pyobject.signed[self.OB_PYPY_LINK] = objint
+        self.rrc_dict.setitem(obj, pyobject)
+
+    def rawrefcount_from_obj(self, gcobj):
+        obj = llmemory.cast_ptr_to_adr(gcobj)
+        return self.rrc_dict.get(obj)
+
+    def rawrefcount_to_obj(self, pyobject):
+        obj = llmemory.cast_int_to_adr(pyobject.signed[self.OB_PYPY_LINK])
+        return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
diff --git a/rpython/memory/gc/test/test_rawrefcount.py b/rpython/memory/gc/test/test_rawrefcount.py
new file mode 100644
--- /dev/null
+++ b/rpython/memory/gc/test/test_rawrefcount.py
@@ -0,0 +1,115 @@
+from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.memory.gc.incminimark import IncrementalMiniMarkGC
+from rpython.memory.gc.test.test_direct import BaseDirectGCTest
+from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY
+from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_DIRECT
+
+PYOBJ_HDR = lltype.Array(lltype.Signed, hints={'nolength': True})
+PYOBJ_HDR_PTR = lltype.Ptr(PYOBJ_HDR)
+
+OB_REFCNT = IncrementalMiniMarkGC.OB_REFCNT
+OB_PYPY_LINK = IncrementalMiniMarkGC.OB_PYPY_LINK
+
+S = lltype.GcForwardReference()
+S.become(lltype.GcStruct('S',
+                         ('x', lltype.Signed),
+                         ('prev', lltype.Ptr(S)),
+                         ('next', lltype.Ptr(S))))
+
+
+class TestRawRefCount(BaseDirectGCTest):
+    GCClass = IncrementalMiniMarkGC
+
+    def _rawrefcount_pair(self, intval, is_direct=False, is_pyobj=False):
+        if is_direct:
+            rc = REFCNT_FROM_PYPY_DIRECT
+        else:
+            rc = REFCNT_FROM_PYPY
+        #
+        p1 = self.malloc(S)
+        p1.x = intval
+        p1ref = lltype.cast_opaque_ptr(llmemory.GCREF, p1)
+        r1 = lltype.malloc(PYOBJ_HDR, 3, flavor='raw')
+        r1[OB_REFCNT] = rc
+        r1[OB_PYPY_LINK] = 0
+        r1addr = llmemory.cast_ptr_to_adr(r1)
+        self.gc.rawrefcount_init()
+        if is_pyobj:
+            assert not is_direct
+            self.gc.rawrefcount_create_link_pyobj(p1ref, r1addr)
+        else:
+            self.gc.rawrefcount_create_link_pypy(p1ref, r1addr)
+        assert r1[OB_REFCNT] == rc
+        assert r1[OB_PYPY_LINK] != 0
+        return p1, p1ref, r1, r1addr
+
+    def test_rawrefcount_objects_basic(self):
+        p1, p1ref, r1, r1addr = self._rawrefcount_pair(42, is_direct=True)
+        p2 = self.malloc(S)
+        p2.x = 84
+        p2ref = lltype.cast_opaque_ptr(llmemory.GCREF, p2)
+        r2 = lltype.malloc(PYOBJ_HDR, 3, flavor='raw')
+        r2[0] = 1
+        r2[1] = 0
+        r2addr = llmemory.cast_ptr_to_adr(r2)
+        # p2 and r2 are not linked
+        assert r1[1] != 0
+        assert r2[1] == 0
+        assert self.gc.rawrefcount_from_obj(p1ref) == r1addr
+        assert self.gc.rawrefcount_from_obj(p2ref) == llmemory.NULL
+        assert self.gc.rawrefcount_to_obj(r1addr) == p1ref
+        assert self.gc.rawrefcount_to_obj(r2addr) == lltype.nullptr(
+            llmemory.GCREF.TO)
+        lltype.free(r1, flavor='raw')
+        lltype.free(r2, flavor='raw')
+
+    def test_rawrefcount_objects_collection_survives_from_raw(self):
+        for do_collect in [self.gc.minor_collection, self.gc.collect] * 2:
+            p1, p1ref, r1, r1addr = self._rawrefcount_pair(42, is_direct=True)
+            assert r1.c_ob_refcnt == REFCNT_FROM_PYPY_DIRECT
+            r1.ob_refcnt += 1
+            do_collect()
+            assert r1.ob_refcnt == REFCNT_FROM_PYPY_OBJECT + 1
+            assert r1.ob_pypy_link != llmemory.NULL
+            p1ref = self.gc.rawrefcount_to_obj(r1addr)
+            assert lltype.cast_opaque_ptr(lltype.Ptr(S), p1ref).x == 42
+            assert self.gc.rawrefcount_from_obj(p1ref) == r1addr
+
+    def test_rawrefcount_objects_collection_survives_from_obj(self):
+        for do_collect in [self.gc.minor_collection, self.gc.collect] * 2:
+            p1, p1ref, r1, r1addr = self._rawrefcount_pair(42)
+            assert r1.ob_refcnt == REFCNT_FROM_PYPY_OBJECT
+            self.stackroots.append(p1)
+            do_collect()
+            assert r1.ob_refcnt == REFCNT_FROM_PYPY_OBJECT
+            assert r1.ob_pypy_link != llmemory.NULL
+            p1ref = self.gc.rawrefcount_to_obj(r1addr)
+            assert lltype.cast_opaque_ptr(lltype.Ptr(S), p1ref).x == 42
+            assert self.gc.rawrefcount_from_obj(p1ref) == r1addr
+
+    def test_rawrefcount_objects_collection_dies(self):
+        p1, p1ref, r1, r1addr = self._rawrefcount_pair(43)
+        seen = []
+        self.gc.rawrefcount_set_callback(seen.append)
+        self.gc.minor_collection()
+        assert r1.ob_refcnt == REFCNT_FROM_PYPY_OBJECT
+        assert r1.ob_pypy_link != llmemory.NULL
+        p1ref = self.gc.rawrefcount_to_obj(r1addr)
+        assert seen == [p1ref]
+        assert lltype.cast_opaque_ptr(lltype.Ptr(S), p1ref).x == 43
+        assert self.gc.rawrefcount_from_obj(p1ref) == r1addr
+        #
+        del seen[:]
+        self.gc.minor_collection()
+        assert seen == []
+        self.gc.collect()
+        assert seen == [p1ref]
+        assert r1.ob_pypy_link == llmemory.cast_ptr_to_adr(p1ref)
+
+    def test_rawrefcount_objects_detach(self):
+        p1, p1ref, r1, r1addr = self._rawrefcount_pair(43)
+        self.gc.rawrefcount_detach(r1addr)
+        assert r1.ob_pypy_link == llmemory.NULL
+        assert self.gc.rawrefcount_from_obj(p1ref) == llmemory.NULL
+        assert self.gc.rawrefcount_to_obj(r1addr) == lltype.nullptr(
+            llmemory.GCREF.TO)
diff --git a/rpython/rlib/rawrefcount.py b/rpython/rlib/rawrefcount.py
--- a/rpython/rlib/rawrefcount.py
+++ b/rpython/rlib/rawrefcount.py
@@ -5,7 +5,6 @@
 from rpython.rtyper.lltypesystem import lltype, llmemory
 from rpython.rlib.objectmodel import we_are_translated, specialize
 from rpython.rtyper.extregistry import ExtRegistryEntry
-from rpython.rtyper import annlowlevel
 from rpython.rlib import rgc
 
 
@@ -13,22 +12,22 @@
 REFCNT_FROM_PYPY_DIRECT = REFCNT_FROM_PYPY + (sys.maxint//2+1)
 
 
-def _reset_state():
-    global _p_list, _o_list, _adr2pypy, _pypy2ob
-    _p_list = []     # not rpython
-    _o_list = []     # not rpython
-    _adr2pypy = [None]  # not rpython
-    _pypy2ob = {}       # not rpython
-_reset_state()
-
 def _build_pypy_link(p):
     res = len(_adr2pypy)
     _adr2pypy.append(p)
     return res
 
 
+def init():
+    "NOT_RPYTHON: set up rawrefcount with the GC"
+    global _p_list, _o_list, _adr2pypy, _pypy2ob
+    _p_list = []
+    _o_list = []
+    _adr2pypy = [None]
+    _pypy2ob = {}
+
 def create_link_pypy(p, ob):
-    "NOT_RPYTHON: a link where the PyPy object contains all the data"
+    "NOT_RPYTHON: a link where the PyPy object contains some or all the data"
     assert p not in _pypy2ob
     assert not ob.c_ob_pypy_link
     ob.c_ob_pypy_link = _build_pypy_link(p)
@@ -51,18 +50,14 @@
     assert lltype.typeOf(ob) == OB_PTR_TYPE
     return ob
 
- at specialize.arg(0)
 def to_obj(Class, ob):
+    "NOT_RPYTHON"
     link = ob.c_ob_pypy_link
-    if we_are_translated():
-        pypy_gcref = lltype.cast_int_to_ptr(llmemory.GCREF, link)
-        return annlowlevel.cast_gcref_to_instance(Class, pypy_gcref)
-    else:
-        if link == 0:
-            return None
-        p = _adr2pypy[link]
-        assert isinstance(p, Class)
-        return p
+    if link == 0:
+        return None
+    p = _adr2pypy[link]
+    assert isinstance(p, Class)
+    return p
 
 def _collect():
     """NOT_RPYTHON: for tests only.  Emulates a GC collection.
@@ -78,7 +73,7 @@
         wr_list.append((ob, weakref.ref(p)))
         return p
 
-    global _p_list, _o_list, _s_list
+    global _p_list, _o_list
     wr_p_list = []
     new_p_list = []
     for ob in _p_list:
@@ -133,5 +128,60 @@
 
 # ____________________________________________________________
 
-## class Entry(ExtRegistryEntry):
-##     _about_ = create_link_from_pypy
+class Entry(ExtRegistryEntry):
+    _about_ = init
+
+    def compute_result_annotation(self):
+        pass
+
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        hop.genop('gc_rawrefcount_init', [])
+
+class Entry(ExtRegistryEntry):
+    _about_ = (create_link_pypy, create_link_pyobj)
+
+    def compute_result_annotation(self, s_p, s_ob):
+        pass
+
+    def specialize_call(self, hop):
+        if self.instance is create_link_pypy:
+            name = 'gc_rawrefcount_create_link_pypy'
+        elif self.instance is create_link_pyobj:
+            name = 'gc_rawrefcount_create_link_pyobj'
+        hop.exception_cannot_occur()
+        hop.genop(name, hop.args_v)
+
+class Entry(ExtRegistryEntry):
+    _about_ = from_obj
+
+    def compute_result_annotation(self, s_OB_PTR_TYPE, s_p):
+        from rpython.annotator import model as annmodel
+        from rpython.rtyper.llannotation import lltype_to_annotation
+        assert (isinstance(s_p, annmodel.SomeInstance) or
+                    annmodel.s_None.contains(s_p))
+        assert s_OB_PTR_TYPE.is_constant()
+        return lltype_to_annotation(s_OB_PTR_TYPE.const)
+
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        [v_p] = hop.inputargs(hop.args_r[1].lowleveltype)
+        return hop.genop('gc_rawrefcount_from_obj', [v_p],
+                         resulttype = hop.r_result.lowleveltype)
+
+class Entry(ExtRegistryEntry):
+    _about_ = to_obj
+
+    def compute_result_annotation(self, s_Class, s_ob):
+        from rpython.annotator import model as annmodel
+        from rpython.rtyper.llannotation import SomePtr
+        assert isinstance(s_ob, SomePtr)
+        assert s_Class.is_constant()
+        classdef = self.bookkeeper.getuniqueclassdef(s_Class.const)
+        return annmodel.SomeInstance(classdef, can_be_None=True)
+
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        v_ob = hop.inputargs(hop.args_r[1].lowleveltype)
+        return hop.genop('gc_rawrefcount_to_obj', [v_ob],
+                         resulttype = hop.r_result.lowleveltype)
diff --git a/rpython/rlib/test/test_rawrefcount.py b/rpython/rlib/test/test_rawrefcount.py
--- a/rpython/rlib/test/test_rawrefcount.py
+++ b/rpython/rlib/test/test_rawrefcount.py
@@ -16,7 +16,7 @@
 class TestRawRefCount:
 
     def setup_method(self, meth):
-        rawrefcount._reset_state()
+        rawrefcount.init()
 
     def test_create_link_pypy(self):
         p = W_Root(42)
diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -502,6 +502,12 @@
     'gc_gcflag_extra'     : LLOp(),
     'gc_add_memory_pressure': LLOp(),
 
+    'gc_rawrefcount_init':              LLOp(),
+    'gc_rawrefcount_create_link_pypy':  LLOp(),
+    'gc_rawrefcount_create_link_pyobj': LLOp(),
+    'gc_rawrefcount_from_obj':          LLOp(sideeffects=False),
+    'gc_rawrefcount_to_obj':            LLOp(sideeffects=False),
+
     # ------- JIT & GC interaction, only for some GCs ----------
 
     'gc_adr_of_nursery_free' : LLOp(),


More information about the pypy-commit mailing list