Source

pypy / pypy / rpython / memory / gcwrapper.py

Full commit
Maciej Fijalkows… f53ec1d 
Armin Rigo 711cafe 
Carl Friedrich B… 9383561 
Armin Rigo 9602d0b 
Armin Rigo 43493ce 

Boris Feigin cca62c8 
Alex Gaynor d2c5a48 
Armin Rigo 43493ce 
Boris Feigin cca62c8 
Armin Rigo a5c6077 
Armin Rigo 7eb3656 

Amaury Forgeot d… 9e6a7b5 



Armin Rigo 8194b46 
Maciej Fijalkows… b5a2e0b 
Boris Feigin cca62c8 
Armin Rigo 43493ce 
Carl Friedrich B… 38c151d 
Amaury Forgeot d… d0c8792 

Boris Feigin cca62c8 
Armin Rigo 43493ce 
Amaury Forgeot d… 7a14535 



Armin Rigo 43493ce 
Armin Rigo 9602d0b 
Boris Feigin cca62c8 
Armin Rigo 43493ce 



Boris Feigin cca62c8 
Maciej Fijalkows… b5a2e0b 
Carl Friedrich B… b3e343c 
Maciej Fijalkows… b5a2e0b 
Boris Feigin cca62c8 
Armin Rigo 43493ce 



Amaury Forgeot d… d640533 

Armin Rigo 43493ce 


Armin Rigo 2238c0a 
Armin Rigo e4db7e6 
Armin Rigo 2238c0a 

Armin Rigo 43493ce 
Amaury Forgeot d… d640533 

Boris Feigin cca62c8 
Maciej Fijalkows… b5a2e0b 









Justin Peel d78305b 
Armin Rigo 0ec2ab6 
Justin Peel c7d1e2b 
Justin Peel d78305b 
Armin Rigo 7273161 




Maciej Fijalkows… b5a2e0b 
Amaury Forgeot d… d640533 
Armin Rigo 1bb0348 
Amaury Forgeot d… d640533 

Armin Rigo 1bb0348 
Armin Rigo 43493ce 
Armin Rigo 711cafe 



Boris Feigin cca62c8 
Armin Rigo 43493ce 
Armin Rigo 711cafe 


Amaury Forgeot d… d0c8792 
Boris Feigin cca62c8 
Amaury Forgeot d… d0c8792 

Carl Friedrich B… ff636a5 

Amaury Forgeot d… d0c8792 







Amaury Forgeot d… 9e6a7b5 
Amaury Forgeot d… d0c8792 






Amaury Forgeot d… 9e6a7b5 
Amaury Forgeot d… d0c8792 
Armin Rigo 4b1ea8d 
Carl Friedrich B… 5caf9f5 
Samuele Pedroni 672178c 

Carl Friedrich B… 5caf9f5 
Maciej Fijalkows… b5a2e0b 

Armin Rigo bb5985e 
Armin Rigo 2a380b9 



Carl Friedrich B… c9f3f7c 


Armin Rigo 2a380b9 
Carl Friedrich B… c9f3f7c 





Armin Rigo a84d45c 



Armin Rigo 324a826 

Armin Rigo 0d89ca2 


Armin Rigo 324a826 


Armin Rigo 0d89ca2 

Carl Friedrich B… 7ced987 
Armin Rigo 6a60ef6 













Armin Rigo 8194b46 
Boris Feigin cca62c8 
Armin Rigo 8194b46 
Armin Rigo 4a76bc1 

Armin Rigo 8194b46 









Carl Friedrich B… 450e269 
Armin Rigo 8194b46 


Carl Friedrich B… 450e269 
Armin Rigo 8194b46 


Carl Friedrich B… 450e269 
Armin Rigo 8194b46 
Armin Rigo 4a76bc1 
Maciej Fijalkows… b5a2e0b 



Boris Feigin cca62c8 
Armin Rigo 43493ce 
Boris Feigin cca62c8 
Amaury Forgeot d… 7a14535 
Carl Friedrich B… 9383561 
Amaury Forgeot d… 7a14535 
Carl Friedrich B… 9383561 
Armin Rigo 43493ce 
Alex Gaynor d2c5a48 
Carl Friedrich B… 9383561 





Maciej Fijalkows… f53ec1d 
Carl Friedrich B… 9383561 
Maciej Fijalkows… f53ec1d 
Maciej Fijalkows… 9d13b20 
Armin Rigo 17460c4 

Carl Friedrich B… 9383561 
Armin Rigo a318f20 




Armin Rigo 17460c4 
Maciej Fijalkows… f53ec1d 
Armin Rigo 17460c4 

Alex Gaynor d2c5a48 
Armin Rigo 17460c4 




Boris Feigin cca62c8 

Armin Rigo 43493ce 


















Armin Rigo 1051e00 
Carl Friedrich B… 450e269 

Armin Rigo 1051e00 
Armin Rigo 43493ce 










Armin Rigo a5c6077 
Armin Rigo 43493ce 
Armin Rigo a5c6077 
from pypy.translator.backendopt.finalizer import FinalizerAnalyzer
from pypy.rpython.lltypesystem import lltype, llmemory, llheap
from pypy.rpython import llinterp
from pypy.rpython.annlowlevel import llhelper
from pypy.rpython.memory import gctypelayout
from pypy.objspace.flow.model import Constant


class GCManagedHeap(object):

    def __init__(self, llinterp, flowgraphs, gc_class, GC_PARAMS={}):
        translator = llinterp.typer.annotator.translator
        config = translator.config.translation
        self.gc = gc_class(config,
                           chunk_size      = 10,
                           translated_to_c = False,
                           **GC_PARAMS)
        self.gc.set_root_walker(LLInterpRootWalker(self))
        self.gc.DEBUG = True
        self.llinterp = llinterp
        self.prepare_graphs(flowgraphs)
        self.gc.setup()
        self.has_write_barrier_from_array = hasattr(self.gc,
                                                    'write_barrier_from_array')

    def prepare_graphs(self, flowgraphs):
        lltype2vtable = self.llinterp.typer.lltype2vtable
        layoutbuilder = DirectRunLayoutBuilder(self.gc.__class__,
                                               lltype2vtable,
                                               self.llinterp)
        self.get_type_id = layoutbuilder.get_type_id
        layoutbuilder.initialize_gc_query_function(self.gc)

        constants = collect_constants(flowgraphs)
        for obj in constants:
            TYPE = lltype.typeOf(obj)
            layoutbuilder.consider_constant(TYPE, obj, self.gc)

        self.constantroots = layoutbuilder.addresses_of_static_ptrs
        self.constantrootsnongc = layoutbuilder.addresses_of_static_ptrs_in_nongc
        self._all_prebuilt_gc = layoutbuilder.all_prebuilt_gc

    # ____________________________________________________________
    #
    # Interface for the llinterp
    #
    def malloc(self, TYPE, n=None, flavor='gc', zero=False,
               track_allocation=True):
        if flavor == 'gc':
            typeid = self.get_type_id(TYPE)
            addr = self.gc.malloc(typeid, n, zero=zero)
            result = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(TYPE))
            if not self.gc.malloc_zero_filled:
                gctypelayout.zero_gc_pointers(result)
            return result
        else:
            return lltype.malloc(TYPE, n, flavor=flavor, zero=zero,
                                 track_allocation=track_allocation)

    def malloc_nonmovable(self, TYPE, n=None, zero=False):
        typeid = self.get_type_id(TYPE)
        if not self.gc.can_malloc_nonmovable():
            return lltype.nullptr(TYPE)
        addr = self.gc.malloc_nonmovable(typeid, n, zero=zero)
        result = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(TYPE))
        if not self.gc.malloc_zero_filled:
            gctypelayout.zero_gc_pointers(result)
        return result

    def add_memory_pressure(self, size):
        if hasattr(self.gc, 'raw_malloc_memory_pressure'):
            self.gc.raw_malloc_memory_pressure(size)

    def shrink_array(self, p, smallersize):
        if hasattr(self.gc, 'shrink_array'):
            addr = llmemory.cast_ptr_to_adr(p)
            return self.gc.shrink_array(addr, smallersize)
        return False

    def free(self, TYPE, flavor='gc', track_allocation=True):
        assert flavor != 'gc'
        return lltype.free(TYPE, flavor=flavor,
                           track_allocation=track_allocation)

    def setfield(self, obj, fieldname, fieldvalue):
        STRUCT = lltype.typeOf(obj).TO
        addr = llmemory.cast_ptr_to_adr(obj)
        addr += llmemory.offsetof(STRUCT, fieldname)
        self.setinterior(obj, addr, getattr(STRUCT, fieldname), fieldvalue)

    def setarrayitem(self, array, index, newitem):
        ARRAY = lltype.typeOf(array).TO
        addr = llmemory.cast_ptr_to_adr(array)
        addr += llmemory.itemoffsetof(ARRAY, index)
        self.setinterior(array, addr, ARRAY.OF, newitem, (index,))

    def setinterior(self, toplevelcontainer, inneraddr, INNERTYPE, newvalue,
                    offsets=()):
        if (lltype.typeOf(toplevelcontainer).TO._gckind == 'gc' and
            isinstance(INNERTYPE, lltype.Ptr) and INNERTYPE.TO._gckind == 'gc'):
            #
            wb = True
            if self.has_write_barrier_from_array:
                for index in offsets:
                    if type(index) is not str:
                        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
                        break
            #
            if wb:
                self.gc.write_barrier(
                    llmemory.cast_ptr_to_adr(newvalue),
                    llmemory.cast_ptr_to_adr(toplevelcontainer))
        llheap.setinterior(toplevelcontainer, inneraddr, INNERTYPE, newvalue)

    def collect(self, *gen):
        self.gc.collect(*gen)

    def can_move(self, addr):
        return self.gc.can_move(addr)

    def weakref_create_getlazy(self, objgetter):
        # we have to be lazy in reading the llinterp variable containing
        # the 'obj' pointer, because the gc.malloc() call below could
        # move it around
        type_id = self.get_type_id(gctypelayout.WEAKREF)
        addr = self.gc.malloc(type_id, None, zero=False)
        result = llmemory.cast_adr_to_ptr(addr, gctypelayout.WEAKREFPTR)
        result.weakptr = llmemory.cast_ptr_to_adr(objgetter())
        return llmemory.cast_ptr_to_weakrefptr(result)
    
    def weakref_deref(self, PTRTYPE, obj):
        addr = gctypelayout.ll_weakref_deref(obj)
        return llmemory.cast_adr_to_ptr(addr, PTRTYPE)

    def gc_id(self, ptr):
        ptr = lltype.cast_opaque_ptr(llmemory.GCREF, ptr)
        return self.gc.id(ptr)

    def writebarrier_before_copy(self, source, dest,
                                 source_start, dest_start, length):
        if self.gc.needs_write_barrier:
            source_addr = llmemory.cast_ptr_to_adr(source)
            dest_addr   = llmemory.cast_ptr_to_adr(dest)
            return self.gc.writebarrier_before_copy(source_addr, dest_addr,
                                                    source_start, dest_start,
                                                    length)
        else:
            return True

    def gcflag_extra(self, subopnum, *args):
        if subopnum == 1:      # has_gcflag_extra
            assert len(args) == 0
            return self.gc.gcflag_extra != 0
        assert len(args) == 1
        addr = llmemory.cast_ptr_to_adr(args[0])
        hdr = self.gc.header(addr)
        if subopnum == 3:      # toggle_gcflag_extra
            if hdr.tid & self.gc.gcflag_extra:
                hdr.tid &= ~self.gc.gcflag_extra
            else:
                hdr.tid |= self.gc.gcflag_extra
        return (hdr.tid & self.gc.gcflag_extra) != 0

# ____________________________________________________________

class LLInterpRootWalker:
    _alloc_flavor_ = 'raw'

    def __init__(self, gcheap):
        self.gcheap = gcheap

    def walk_roots(self, collect_stack_root,
                   collect_static_in_prebuilt_nongc,
                   collect_static_in_prebuilt_gc):
        gcheap = self.gcheap
        gc = gcheap.gc
        if collect_static_in_prebuilt_gc:
            for addrofaddr in gcheap.constantroots:
                if self.gcheap.gc.points_to_valid_gc_object(addrofaddr):
                    collect_static_in_prebuilt_gc(gc, addrofaddr)
        if collect_static_in_prebuilt_nongc:
            for addrofaddr in gcheap.constantrootsnongc:
                if self.gcheap.gc.points_to_valid_gc_object(addrofaddr):
                    collect_static_in_prebuilt_nongc(gc, addrofaddr)
        if collect_stack_root:
            for addrofaddr in gcheap.llinterp.find_roots():
                if self.gcheap.gc.points_to_valid_gc_object(addrofaddr):
                    collect_stack_root(gc, addrofaddr)

    def _walk_prebuilt_gc(self, collect):    # debugging only!  not RPython
        for obj in self.gcheap._all_prebuilt_gc:
            collect(llmemory.cast_ptr_to_adr(obj._as_ptr()))


class DirectRunLayoutBuilder(gctypelayout.TypeLayoutBuilder):

    def __init__(self, GCClass, lltype2vtable, llinterp):
        self.llinterp = llinterp
        super(DirectRunLayoutBuilder, self).__init__(GCClass, lltype2vtable)

    def make_finalizer_funcptr_for_type(self, TYPE):
        from pypy.rpython.memory.gctransform.support import get_rtti
        rtti = get_rtti(TYPE)
        if rtti is not None and hasattr(rtti._obj, 'destructor_funcptr'):
            destrptr = rtti._obj.destructor_funcptr
            DESTR_ARG = lltype.typeOf(destrptr).TO.ARGS[0]
            destrgraph = destrptr._obj.graph
        else:
            return None, False

        t = self.llinterp.typer.annotator.translator
        light = not FinalizerAnalyzer(t).analyze_light_finalizer(destrgraph)
        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 llmemory.NULL
        return llhelper(gctypelayout.GCData.FINALIZER_OR_CT, ll_finalizer), light

    def make_custom_trace_funcptr_for_type(self, TYPE):
        from pypy.rpython.memory.gctransform.support import get_rtti
        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):
    constants = {}
    def collect_args(args):
        for arg in args:
            if (isinstance(arg, Constant) and
                arg.concretetype is not lltype.Void):
                reccollect(constants, arg.value)
    for graph in graphs:
        for block in graph.iterblocks():
            collect_args(block.inputargs)
            for op in block.operations:
                collect_args(op.args)
        for link in graph.iterlinks():
            collect_args(link.args)
            if hasattr(link, "llexitcase"):
                reccollect(constants, link.llexitcase)
    return constants

def reccollect(constants, llvalue):
    if (isinstance(llvalue, lltype._abstract_ptr)
        and llvalue._obj is not None and llvalue._obj not in constants
        and not isinstance(llvalue._obj, int)):
        TYPE = llvalue._T
        constants[llvalue._obj] = True
        if isinstance(TYPE, lltype.Struct):
            for name in TYPE._names:
                reccollect(constants, getattr(llvalue, name))
        elif isinstance(TYPE, lltype.Array):
            for llitem in llvalue:
                reccollect(constants, llitem)
        parent, parentindex = lltype.parentlink(llvalue._obj)
        if parent is not None:
            reccollect(constants, parent._as_ptr())

def prepare_graphs_and_create_gc(llinterp, GCClass, GC_PARAMS={}):
    flowgraphs = llinterp.typer.annotator.translator.graphs[:]
    llinterp.heap = GCManagedHeap(llinterp, flowgraphs, GCClass, GC_PARAMS)