[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