Author: Armin Rigo <ar...@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 -@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(), _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit