Author: Armin Rigo <ar...@tunes.org> Branch: custom-trace Changeset: r45868:775391fe3185 Date: 2011-07-21 21:13 +0200 http://bitbucket.org/pypy/pypy/changeset/775391fe3185/
Log: Step 1 (not translated so far) to add custom tracers. diff --git a/pypy/rpython/lltypesystem/lltype.py b/pypy/rpython/lltypesystem/lltype.py --- a/pypy/rpython/lltypesystem/lltype.py +++ b/pypy/rpython/lltypesystem/lltype.py @@ -362,7 +362,8 @@ about=self)._obj Struct._install_extras(self, **kwds) - def _attach_runtime_type_info_funcptr(self, funcptr, destrptr): + def _attach_runtime_type_info_funcptr(self, funcptr, destrptr, + customtraceptr): if self._runtime_type_info is None: raise TypeError("attachRuntimeTypeInfo: %r must have been built " "with the rtti=True argument" % (self,)) @@ -376,7 +377,7 @@ raise TypeError("expected a runtime type info function " "implementation, got: %s" % funcptr) self._runtime_type_info.query_funcptr = funcptr - if destrptr is not None : + if destrptr is not None: T = typeOf(destrptr) if (not isinstance(T, Ptr) or not isinstance(T.TO, FuncType) or @@ -386,6 +387,18 @@ raise TypeError("expected a destructor function " "implementation, got: %s" % destrptr) self._runtime_type_info.destructor_funcptr = destrptr + if customtraceptr is not None: + from pypy.rpython.lltypesystem import llmemory + T = typeOf(customtraceptr) + if (not isinstance(T, Ptr) or + not isinstance(T.TO, FuncType) or + len(T.TO.ARGS) != 2 or + T.TO.RESULT != llmemory.Address or + T.TO.ARGS[0] != llmemory.Address or + T.TO.ARGS[1] != llmemory.Address): + raise TypeError("expected a custom trace function " + "implementation, got: %s" % customtraceptr) + self._runtime_type_info.custom_trace_funcptr = customtraceptr class GcStruct(RttiStruct): _gckind = 'gc' @@ -2039,10 +2052,12 @@ raise ValueError("only odd integers can be cast back to ptr") return _ptr(PTRTYPE, oddint, solid=True) -def attachRuntimeTypeInfo(GCSTRUCT, funcptr=None, destrptr=None): +def attachRuntimeTypeInfo(GCSTRUCT, funcptr=None, destrptr=None, + customtraceptr=None): if not isinstance(GCSTRUCT, RttiStruct): raise TypeError, "expected a RttiStruct: %s" % GCSTRUCT - GCSTRUCT._attach_runtime_type_info_funcptr(funcptr, destrptr) + GCSTRUCT._attach_runtime_type_info_funcptr(funcptr, destrptr, + customtraceptr) return _ptr(Ptr(RuntimeTypeInfo), GCSTRUCT._runtime_type_info) def getRuntimeTypeInfo(GCSTRUCT): diff --git a/pypy/rpython/memory/gc/base.py b/pypy/rpython/memory/gc/base.py --- a/pypy/rpython/memory/gc/base.py +++ b/pypy/rpython/memory/gc/base.py @@ -69,7 +69,10 @@ varsize_offsets_to_gcpointers_in_var_part, weakpointer_offset, member_index, - is_rpython_class): + is_rpython_class, + has_custom_trace, + get_custom_trace, + fast_path_tracing): self.getfinalizer = getfinalizer self.is_varsize = is_varsize self.has_gcptr_in_varsize = has_gcptr_in_varsize @@ -83,6 +86,9 @@ self.weakpointer_offset = weakpointer_offset self.member_index = member_index self.is_rpython_class = is_rpython_class + self.has_custom_trace = has_custom_trace + self.get_custom_trace = get_custom_trace + self.fast_path_tracing = fast_path_tracing def get_member_index(self, type_id): return self.member_index(type_id) @@ -181,8 +187,14 @@ Typically, 'callback' is a bound method and 'arg' can be None. """ typeid = self.get_type_id(obj) + # + # First, look if we need more than the simple fixed-size tracing + if not self.fast_path_tracing(typeid): + # + # Yes. Two cases: either we are just a GcArray(gcptr), for + # which we have a special case for performance, or we call + # the slow path version. if self.is_gcarrayofgcptr(typeid): - # a performance shortcut for GcArray(gcptr) length = (obj + llmemory.gcarrayofptr_lengthoffset).signed[0] item = obj + llmemory.gcarrayofptr_itemsoffset while length > 0: @@ -191,6 +203,9 @@ item += llmemory.gcarrayofptr_singleitemoffset length -= 1 return + self._trace_slow_path(obj, callback, arg) + # + # Do the tracing on the fixed-size part of the object. offsets = self.offsets_to_gc_pointers(typeid) i = 0 while i < len(offsets): @@ -198,6 +213,10 @@ if self.points_to_valid_gc_object(item): callback(item, arg) i += 1 + trace._annspecialcase_ = 'specialize:arg(2)' + + def _trace_slow_path(self, obj, callback, arg): + typeid = self.get_type_id(obj) if self.has_gcptr_in_varsize(typeid): item = obj + self.varsize_offset_to_variable_part(typeid) length = (obj + self.varsize_offset_to_length(typeid)).signed[0] @@ -212,7 +231,16 @@ j += 1 item += itemlength length -= 1 - trace._annspecialcase_ = 'specialize:arg(2)' + if self.has_custom_trace(typeid): + generator = self.get_custom_trace(typeid) + item = llmemory.NULL + while True: + item = generator(obj, item) + if not item: + break + if self.points_to_valid_gc_object(item): + callback(item, arg) + _trace_slow_path._annspecialcase_ = 'specialize:arg(2)' def trace_partial(self, obj, start, stop, callback, arg): """Like trace(), but only walk the array part, for indices in @@ -317,7 +345,7 @@ break obj = self.run_finalizers.popleft() finalizer = self.getfinalizer(self.get_type_id(obj)) - finalizer(obj) + finalizer(obj, llmemory.NULL) finally: self.finalizer_lock_count -= 1 diff --git a/pypy/rpython/memory/gc/markcompact.py b/pypy/rpython/memory/gc/markcompact.py --- a/pypy/rpython/memory/gc/markcompact.py +++ b/pypy/rpython/memory/gc/markcompact.py @@ -88,6 +88,9 @@ def __init__(self, config, space_size=4096, min_next_collect_after=128, **kwds): + import py + py.test.skip("the 'markcompact' gc needs fixing for custom tracers") + # MovingGCBase.__init__(self, config, **kwds) self.space_size = space_size self.min_next_collect_after = min_next_collect_after diff --git a/pypy/rpython/memory/gc/marksweep.py b/pypy/rpython/memory/gc/marksweep.py --- a/pypy/rpython/memory/gc/marksweep.py +++ b/pypy/rpython/memory/gc/marksweep.py @@ -450,7 +450,7 @@ hdr.next = self.malloced_objects self.malloced_objects = hdr #llop.debug_view(lltype.Void, self.malloced_objects, self.malloced_objects_with_finalizer, size_gc_header) - finalizer(obj) + finalizer(obj, llmemory.NULL) if not self.collect_in_progress: # another collection was caused? debug_print("outer collect interrupted " "by recursive collect") diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py --- a/pypy/rpython/memory/gctransform/framework.py +++ b/pypy/rpython/memory/gctransform/framework.py @@ -1247,15 +1247,15 @@ assert not type_contains_pyobjs(TYPE), "not implemented" if destrptr: typename = TYPE.__name__ - def ll_finalizer(addr): + def ll_finalizer(addr, ignored): v = llmemory.cast_adr_to_ptr(addr, DESTR_ARG) ll_call_destructor(destrptr, v, typename) + return llmemory.NULL fptr = self.transformer.annotate_finalizer(ll_finalizer, - [llmemory.Address], - lltype.Void) + [llmemory.Address, llmemory.Address], llmemory.Address) + return fptr else: - fptr = lltype.nullptr(gctypelayout.GCData.FINALIZERTYPE.TO) - return fptr + return None def gen_zero_gc_pointers(TYPE, v, llops, previous_steps=None): diff --git a/pypy/rpython/memory/gctypelayout.py b/pypy/rpython/memory/gctypelayout.py --- a/pypy/rpython/memory/gctypelayout.py +++ b/pypy/rpython/memory/gctypelayout.py @@ -17,13 +17,21 @@ _alloc_flavor_ = 'raw' OFFSETS_TO_GC_PTR = lltype.Array(lltype.Signed) - ADDRESS_VOID_FUNC = lltype.FuncType([llmemory.Address], lltype.Void) - FINALIZERTYPE = lltype.Ptr(ADDRESS_VOID_FUNC) + + # When used as a finalizer, the following functions only take one + # address and ignore the second, and return NULL. When used as a + # custom tracer (CT), it enumerates the addresses that contain GCREFs. + # It is called with the object as first argument, and the previous + # returned address (or NULL the first time) as the second argument. + FINALIZER_OR_CT_FUNC = lltype.FuncType([llmemory.Address, + llmemory.Address], + llmemory.Address) + FINALIZER_OR_CT = lltype.Ptr(FINALIZER_OR_CT_FUNC) # structure describing the layout of a typeid TYPE_INFO = lltype.Struct("type_info", ("infobits", lltype.Signed), # combination of the T_xxx consts - ("finalizer", FINALIZERTYPE), + ("finalizer_or_customtrace", FINALIZER_OR_CT), ("fixedsize", lltype.Signed), ("ofstoptrs", lltype.Ptr(OFFSETS_TO_GC_PTR)), hints={'immutable': True}, @@ -71,7 +79,11 @@ return (infobits & T_IS_GCARRAY_OF_GCPTR) != 0 def q_finalizer(self, typeid): - return self.get(typeid).finalizer + typeinfo = self.get(typeid) + if typeinfo.infobits & T_HAS_FINALIZER: + return typeinfo.finalizer_or_customtrace + else: + return lltype.nullptr(GCData.FINALIZER_OR_CT_FUNC) def q_offsets_to_gc_pointers(self, typeid): return self.get(typeid).ofstoptrs @@ -105,6 +117,25 @@ infobits = self.get(typeid).infobits return infobits & T_IS_RPYTHON_INSTANCE != 0 + def q_has_custom_trace(self, typeid): + infobits = self.get(typeid).infobits + return infobits & T_HAS_CUSTOM_TRACE != 0 + + def q_get_custom_trace(self, typeid): + ll_assert(self.q_has_custom_trace(typeid), + "T_HAS_CUSTOM_TRACE missing") + typeinfo = self.get(typeid) + return typeinfo.finalizer_or_customtrace + + def q_fast_path_tracing(self, typeid): + # return True if none of the flags T_HAS_GCPTR_IN_VARSIZE, + # T_IS_GCARRAY_OF_GCPTR or T_HAS_CUSTOM_TRACE is set + T_ANY_SLOW_FLAG = (T_HAS_GCPTR_IN_VARSIZE | + T_IS_GCARRAY_OF_GCPTR | + T_HAS_CUSTOM_TRACE) + infobits = self.get(typeid).infobits + return infobits & T_ANY_SLOW_FLAG == 0 + def set_query_functions(self, gc): gc.set_query_functions( self.q_is_varsize, @@ -119,18 +150,23 @@ self.q_varsize_offsets_to_gcpointers_in_var_part, self.q_weakpointer_offset, self.q_member_index, - self.q_is_rpython_class) + self.q_is_rpython_class, + self.q_has_custom_trace, + self.q_get_custom_trace, + self.q_fast_path_tracing) # the lowest 16bits are used to store group member index T_MEMBER_INDEX = 0xffff -T_IS_VARSIZE = 0x10000 -T_HAS_GCPTR_IN_VARSIZE = 0x20000 -T_IS_GCARRAY_OF_GCPTR = 0x40000 -T_IS_WEAKREF = 0x80000 +T_IS_VARSIZE = 0x010000 +T_HAS_GCPTR_IN_VARSIZE = 0x020000 +T_IS_GCARRAY_OF_GCPTR = 0x040000 +T_IS_WEAKREF = 0x080000 T_IS_RPYTHON_INSTANCE = 0x100000 # the type is a subclass of OBJECT +T_HAS_FINALIZER = 0x200000 +T_HAS_CUSTOM_TRACE = 0x400000 T_KEY_MASK = intmask(0xFF000000) -T_KEY_VALUE = intmask(0x7A000000) # bug detection only +T_KEY_VALUE = intmask(0x5A000000) # bug detection only def _check_valid_type_info(p): ll_assert(p.infobits & T_KEY_MASK == T_KEY_VALUE, "invalid type_id") @@ -151,7 +187,18 @@ offsets = offsets_to_gc_pointers(TYPE) infobits = index info.ofstoptrs = builder.offsets2table(offsets, TYPE) - info.finalizer = builder.make_finalizer_funcptr_for_type(TYPE) + # + kind_and_fptr = builder.finalizer_funcptr_for_type(TYPE) + if kind_and_fptr is not None: + kind, fptr = kind_and_fptr + info.finalizer_or_customtrace = fptr + if kind == "finalizer": + infobits |= T_HAS_FINALIZER + elif kind == "custom_trace": + infobits |= T_HAS_CUSTOM_TRACE + else: + assert 0, kind + # if not TYPE._is_varsize(): info.fixedsize = llarena.round_up_for_allocation( llmemory.sizeof(TYPE), builder.GCClass.object_minimal_size) @@ -216,7 +263,7 @@ # for debugging, the following list collects all the prebuilt # GcStructs and GcArrays self.all_prebuilt_gc = [] - self.finalizer_funcptrs = {} + self._finalizer_funcptrs = {} self.offsettable_cache = {} def make_type_info_group(self): @@ -318,15 +365,28 @@ return self.type_info_group def finalizer_funcptr_for_type(self, TYPE): - if TYPE in self.finalizer_funcptrs: - return self.finalizer_funcptrs[TYPE] - fptr = self.make_finalizer_funcptr_for_type(TYPE) - self.finalizer_funcptrs[TYPE] = fptr - return fptr + if TYPE in self._finalizer_funcptrs: + return self._finalizer_funcptrs[TYPE] + fptr1 = self.make_finalizer_funcptr_for_type(TYPE) + fptr2 = self.make_custom_trace_funcptr_for_type(TYPE) + assert not (fptr1 and fptr2), ( + "type %r needs both a finalizer and a custom tracer" % (TYPE,)) + if fptr1: + kind_and_fptr = "finalizer", fptr1 + elif fptr2: + kind_and_fptr = "custom_trace", fptr2 + else: + kind_and_fptr = None + self._finalizer_funcptrs[TYPE] = kind_and_fptr + return kind_and_fptr def make_finalizer_funcptr_for_type(self, TYPE): # must be overridden for proper finalizer support - return lltype.nullptr(GCData.ADDRESS_VOID_FUNC) + return None + + def make_custom_trace_funcptr_for_type(self, TYPE): + # must be overridden for proper custom tracer support + return None def initialize_gc_query_function(self, gc): return GCData(self.type_info_group).set_query_functions(gc) diff --git a/pypy/rpython/memory/gcwrapper.py b/pypy/rpython/memory/gcwrapper.py --- a/pypy/rpython/memory/gcwrapper.py +++ b/pypy/rpython/memory/gcwrapper.py @@ -196,17 +196,28 @@ DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0] destrgraph = destrptr._obj.graph else: - return lltype.nullptr(gctypelayout.GCData.FINALIZERTYPE.TO) + return None assert not type_contains_pyobjs(TYPE), "not implemented" - def ll_finalizer(addr): + def ll_finalizer(addr, dummy): + assert dummy == llmemory.NULL try: v = llmemory.cast_adr_to_ptr(addr, DESTR_ARG) self.llinterp.eval_graph(destrgraph, [v], recursive=True) except llinterp.LLException: raise RuntimeError( "a finalizer raised an exception, shouldn't happen") - return llhelper(gctypelayout.GCData.FINALIZERTYPE, ll_finalizer) + return llmemory.NULL + return llhelper(gctypelayout.GCData.FINALIZER_OR_CT, ll_finalizer) + + def make_custom_trace_funcptr_for_type(self, TYPE): + from pypy.rpython.memory.gctransform.support import get_rtti, \ + type_contains_pyobjs + rtti = get_rtti(TYPE) + if rtti is not None and hasattr(rtti._obj, 'custom_trace_funcptr'): + return rtti._obj.custom_trace_funcptr + else: + return None def collect_constants(graphs): diff --git a/pypy/rpython/memory/test/test_gc.py b/pypy/rpython/memory/test/test_gc.py --- a/pypy/rpython/memory/test/test_gc.py +++ b/pypy/rpython/memory/test/test_gc.py @@ -237,6 +237,46 @@ res = self.interpret(f, [5]) assert 160 <= res <= 165 + def test_custom_trace(self): + from pypy.rpython.annlowlevel import llhelper + from pypy.rpython.lltypesystem import llmemory + from pypy.rpython.lltypesystem.llarena import ArenaError + # + S = lltype.GcStruct('S', ('x', llmemory.Address), + ('y', llmemory.Address), rtti=True) + T = lltype.GcStruct('T', ('z', lltype.Signed)) + offset_of_x = llmemory.offsetof(S, 'x') + def customtrace(obj, prev): + if not prev: + return obj + offset_of_x + else: + return llmemory.NULL + CUSTOMTRACEFUNC = lltype.FuncType([llmemory.Address, llmemory.Address], + llmemory.Address) + customtraceptr = llhelper(lltype.Ptr(CUSTOMTRACEFUNC), customtrace) + lltype.attachRuntimeTypeInfo(S, customtraceptr=customtraceptr) + # + for attrname in ['x', 'y']: + def setup(): + s1 = lltype.malloc(S) + tx = lltype.malloc(T) + tx.z = 42 + ty = lltype.malloc(T) + s1.x = llmemory.cast_ptr_to_adr(tx) + s1.y = llmemory.cast_ptr_to_adr(ty) + return s1 + def f(): + s1 = setup() + llop.gc__collect(lltype.Void) + return llmemory.cast_adr_to_ptr(getattr(s1, attrname), + lltype.Ptr(T)) + if attrname == 'x': + res = self.interpret(f, []) + assert res.z == 42 + else: + py.test.raises((RuntimeError, ArenaError), + self.interpret, f, []) + def test_weakref(self): import weakref, gc class A(object): _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit