/pypy/jit/backend/llsupport/gc.py
Python | 890 lines | 741 code | 52 blank | 97 comment | 46 complexity | e799a419657065ecebb8377be33aa4fc MD5 | raw file
- import os
- from pypy.rlib import rgc
- from pypy.rlib.objectmodel import we_are_translated, specialize
- from pypy.rlib.rarithmetic import ovfcheck
- from pypy.rpython.lltypesystem import lltype, llmemory, rffi, rclass, rstr
- from pypy.rpython.lltypesystem import llgroup
- from pypy.rpython.lltypesystem.lloperation import llop
- from pypy.rpython.annlowlevel import llhelper
- from pypy.translator.tool.cbuild import ExternalCompilationInfo
- from pypy.jit.codewriter import heaptracker
- from pypy.jit.metainterp.history import ConstPtr, AbstractDescr
- from pypy.jit.metainterp.resoperation import ResOperation, rop
- from pypy.jit.backend.llsupport import symbolic
- from pypy.jit.backend.llsupport.symbolic import WORD
- from pypy.jit.backend.llsupport.descr import SizeDescr, ArrayDescr
- from pypy.jit.backend.llsupport.descr import GcCache, get_field_descr
- from pypy.jit.backend.llsupport.descr import get_array_descr
- from pypy.jit.backend.llsupport.descr import get_call_descr
- from pypy.jit.backend.llsupport.rewrite import GcRewriterAssembler
- from pypy.rpython.memory.gctransform import asmgcroot
- # ____________________________________________________________
- class GcLLDescription(GcCache):
- def __init__(self, gcdescr, translator=None, rtyper=None):
- GcCache.__init__(self, translator is not None, rtyper)
- self.gcdescr = gcdescr
- if translator and translator.config.translation.gcremovetypeptr:
- self.fielddescr_vtable = None
- else:
- self.fielddescr_vtable = get_field_descr(self, rclass.OBJECT,
- 'typeptr')
- self._generated_functions = []
- def _setup_str(self):
- self.str_descr = get_array_descr(self, rstr.STR)
- self.unicode_descr = get_array_descr(self, rstr.UNICODE)
- def generate_function(self, funcname, func, ARGS, RESULT=llmemory.GCREF):
- """Generates a variant of malloc with the given name and the given
- arguments. It should return NULL if out of memory. If it raises
- anything, it must be an optional MemoryError.
- """
- FUNCPTR = lltype.Ptr(lltype.FuncType(ARGS, RESULT))
- descr = get_call_descr(self, ARGS, RESULT)
- setattr(self, funcname, func)
- setattr(self, funcname + '_FUNCPTR', FUNCPTR)
- setattr(self, funcname + '_descr', descr)
- self._generated_functions.append(funcname)
- @specialize.arg(1)
- def get_malloc_fn(self, funcname):
- func = getattr(self, funcname)
- FUNC = getattr(self, funcname + '_FUNCPTR')
- return llhelper(FUNC, func)
- @specialize.arg(1)
- def get_malloc_fn_addr(self, funcname):
- ll_func = self.get_malloc_fn(funcname)
- return heaptracker.adr2int(llmemory.cast_ptr_to_adr(ll_func))
- def _freeze_(self):
- return True
- def initialize(self):
- pass
- def do_write_barrier(self, gcref_struct, gcref_newptr):
- pass
- def can_use_nursery_malloc(self, size):
- return False
- def has_write_barrier_class(self):
- return None
- def freeing_block(self, start, stop):
- pass
- def get_nursery_free_addr(self):
- raise NotImplementedError
- def get_nursery_top_addr(self):
- raise NotImplementedError
- def gc_malloc(self, sizedescr):
- """Blackhole: do a 'bh_new'. Also used for 'bh_new_with_vtable',
- with the vtable pointer set manually afterwards."""
- assert isinstance(sizedescr, SizeDescr)
- return self._bh_malloc(sizedescr)
- def gc_malloc_array(self, arraydescr, num_elem):
- assert isinstance(arraydescr, ArrayDescr)
- return self._bh_malloc_array(arraydescr, num_elem)
- def gc_malloc_str(self, num_elem):
- return self._bh_malloc_array(self.str_descr, num_elem)
- def gc_malloc_unicode(self, num_elem):
- return self._bh_malloc_array(self.unicode_descr, num_elem)
- def _record_constptrs(self, op, gcrefs_output_list):
- for i in range(op.numargs()):
- v = op.getarg(i)
- if isinstance(v, ConstPtr) and bool(v.value):
- p = v.value
- rgc._make_sure_does_not_move(p)
- gcrefs_output_list.append(p)
- def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
- rewriter = GcRewriterAssembler(self, cpu)
- newops = rewriter.rewrite(operations)
- # record all GCREFs, because the GC (or Boehm) cannot see them and
- # keep them alive if they end up as constants in the assembler
- for op in newops:
- self._record_constptrs(op, gcrefs_output_list)
- return newops
- # ____________________________________________________________
- class GcLLDescr_boehm(GcLLDescription):
- kind = 'boehm'
- moving_gc = False
- round_up = False
- gcrootmap = None
- write_barrier_descr = None
- fielddescr_tid = None
- str_type_id = 0
- unicode_type_id = 0
- get_malloc_slowpath_addr = None
- @classmethod
- def configure_boehm_once(cls):
- """ Configure boehm only once, since we don't cache failures
- """
- if hasattr(cls, 'malloc_fn_ptr'):
- return cls.malloc_fn_ptr
- from pypy.rpython.tool import rffi_platform
- compilation_info = rffi_platform.configure_boehm()
- # on some platform GC_init is required before any other
- # GC_* functions, call it here for the benefit of tests
- # XXX move this to tests
- init_fn_ptr = rffi.llexternal("GC_init",
- [], lltype.Void,
- compilation_info=compilation_info,
- sandboxsafe=True,
- _nowrapper=True)
- init_fn_ptr()
- # Versions 6.x of libgc needs to use GC_local_malloc().
- # Versions 7.x of libgc removed this function; GC_malloc() has
- # the same behavior if libgc was compiled with
- # THREAD_LOCAL_ALLOC.
- class CConfig:
- _compilation_info_ = compilation_info
- HAS_LOCAL_MALLOC = rffi_platform.Has("GC_local_malloc")
- config = rffi_platform.configure(CConfig)
- if config['HAS_LOCAL_MALLOC']:
- GC_MALLOC = "GC_local_malloc"
- else:
- GC_MALLOC = "GC_malloc"
- malloc_fn_ptr = rffi.llexternal(GC_MALLOC,
- [lltype.Signed], # size_t, but good enough
- llmemory.GCREF,
- compilation_info=compilation_info,
- sandboxsafe=True,
- _nowrapper=True)
- cls.malloc_fn_ptr = malloc_fn_ptr
- return malloc_fn_ptr
- def __init__(self, gcdescr, translator, rtyper):
- GcLLDescription.__init__(self, gcdescr, translator, rtyper)
- # grab a pointer to the Boehm 'malloc' function
- self.malloc_fn_ptr = self.configure_boehm_once()
- self._setup_str()
- self._make_functions()
- def _make_functions(self):
- def malloc_fixedsize(size):
- return self.malloc_fn_ptr(size)
- self.generate_function('malloc_fixedsize', malloc_fixedsize,
- [lltype.Signed])
- def malloc_array(basesize, num_elem, itemsize, ofs_length):
- try:
- totalsize = ovfcheck(basesize + ovfcheck(itemsize * num_elem))
- except OverflowError:
- return lltype.nullptr(llmemory.GCREF.TO)
- res = self.malloc_fn_ptr(totalsize)
- if res:
- arrayptr = rffi.cast(rffi.CArrayPtr(lltype.Signed), res)
- arrayptr[ofs_length/WORD] = num_elem
- return res
- self.generate_function('malloc_array', malloc_array,
- [lltype.Signed] * 4)
- def _bh_malloc(self, sizedescr):
- return self.malloc_fixedsize(sizedescr.size)
- def _bh_malloc_array(self, arraydescr, num_elem):
- return self.malloc_array(arraydescr.basesize, num_elem,
- arraydescr.itemsize,
- arraydescr.lendescr.offset)
- # ____________________________________________________________
- # All code below is for the hybrid or minimark GC
- class GcRootMap_asmgcc(object):
- """Handles locating the stack roots in the assembler.
- This is the class supporting --gcrootfinder=asmgcc.
- """
- is_shadow_stack = False
- is_64_bit = (WORD == 8)
- LOC_REG = 0
- LOC_ESP_PLUS = 1
- LOC_EBP_PLUS = 2
- LOC_EBP_MINUS = 3
- GCMAP_ARRAY = rffi.CArray(lltype.Signed)
- CALLSHAPE_ARRAY_PTR = rffi.CArrayPtr(rffi.UCHAR)
- def __init__(self, gcdescr=None):
- # '_gcmap' is an array of length '_gcmap_maxlength' of addresses.
- # '_gcmap_curlength' tells how full the array really is.
- # The addresses are actually grouped in pairs:
- # (addr-after-the-CALL-in-assembler, addr-of-the-call-shape).
- # '_gcmap_deadentries' counts pairs marked dead (2nd item is NULL).
- # '_gcmap_sorted' is True only if we know the array is sorted.
- self._gcmap = lltype.nullptr(self.GCMAP_ARRAY)
- self._gcmap_curlength = 0
- self._gcmap_maxlength = 0
- self._gcmap_deadentries = 0
- self._gcmap_sorted = True
- def add_jit2gc_hooks(self, jit2gc):
- jit2gc.update({
- 'gcmapstart': lambda: self.gcmapstart(),
- 'gcmapend': lambda: self.gcmapend(),
- 'gcmarksorted': lambda: self.gcmarksorted(),
- })
- def initialize(self):
- # hack hack hack. Remove these lines and see MissingRTypeAttribute
- # when the rtyper tries to annotate these methods only when GC-ing...
- self.gcmapstart()
- self.gcmapend()
- self.gcmarksorted()
- def gcmapstart(self):
- return rffi.cast(llmemory.Address, self._gcmap)
- def gcmapend(self):
- addr = self.gcmapstart()
- if self._gcmap_curlength:
- addr += rffi.sizeof(lltype.Signed) * self._gcmap_curlength
- if not we_are_translated() and type(addr) is long:
- from pypy.rpython.lltypesystem import ll2ctypes
- addr = ll2ctypes._lladdress(addr) # XXX workaround
- return addr
- def gcmarksorted(self):
- # Called by the GC when it is about to sort [gcmapstart():gcmapend()].
- # Returns the previous sortedness flag -- i.e. returns True if it
- # is already sorted, False if sorting is needed.
- sorted = self._gcmap_sorted
- self._gcmap_sorted = True
- return sorted
- def put(self, retaddr, callshapeaddr):
- """'retaddr' is the address just after the CALL.
- 'callshapeaddr' is the address of the raw 'shape' marker.
- Both addresses are actually integers here."""
- index = self._gcmap_curlength
- if index + 2 > self._gcmap_maxlength:
- index = self._enlarge_gcmap()
- self._gcmap[index] = retaddr
- self._gcmap[index+1] = callshapeaddr
- self._gcmap_curlength = index + 2
- self._gcmap_sorted = False
- @rgc.no_collect
- def _enlarge_gcmap(self):
- oldgcmap = self._gcmap
- if self._gcmap_deadentries * 3 * 2 > self._gcmap_maxlength:
- # More than 1/3rd of the entries are dead. Don't actually
- # enlarge the gcmap table, but just clean up the dead entries.
- newgcmap = oldgcmap
- else:
- # Normal path: enlarge the array.
- newlength = 250 + (self._gcmap_maxlength // 3) * 4
- newgcmap = lltype.malloc(self.GCMAP_ARRAY, newlength, flavor='raw',
- track_allocation=False)
- self._gcmap_maxlength = newlength
- #
- j = 0
- i = 0
- end = self._gcmap_curlength
- while i < end:
- if oldgcmap[i + 1]:
- newgcmap[j] = oldgcmap[i]
- newgcmap[j + 1] = oldgcmap[i + 1]
- j += 2
- i += 2
- self._gcmap_curlength = j
- self._gcmap_deadentries = 0
- if oldgcmap != newgcmap:
- self._gcmap = newgcmap
- if oldgcmap:
- lltype.free(oldgcmap, flavor='raw', track_allocation=False)
- return j
- @rgc.no_collect
- def freeing_block(self, start, stop):
- # if [start:stop] is a raw block of assembler, then look up the
- # corresponding gcroot markers, and mark them as freed now in
- # self._gcmap by setting the 2nd address of every entry to NULL.
- gcmapstart = self.gcmapstart()
- gcmapend = self.gcmapend()
- if gcmapstart == gcmapend:
- return
- if not self.gcmarksorted():
- asmgcroot.sort_gcmap(gcmapstart, gcmapend)
- # A note about gcmarksorted(): the deletion we do here keeps the
- # array sorted. This avoids needing too many sort_gcmap()s.
- # Indeed, freeing_block() is typically called many times in a row,
- # so it will call sort_gcmap() at most the first time.
- startaddr = rffi.cast(llmemory.Address, start)
- stopaddr = rffi.cast(llmemory.Address, stop)
- item = asmgcroot.binary_search(gcmapstart, gcmapend, startaddr)
- # 'item' points to one of the entries. Because the whole array
- # is sorted, we know that it points either to the first entry we
- # want to kill, or to the previous entry.
- if item.address[0] < startaddr:
- item += asmgcroot.arrayitemsize # go forward one entry
- assert item == gcmapend or item.address[0] >= startaddr
- while item != gcmapend and item.address[0] < stopaddr:
- item.address[1] = llmemory.NULL
- self._gcmap_deadentries += 1
- item += asmgcroot.arrayitemsize
- def get_basic_shape(self):
- # XXX: Should this code even really know about stack frame layout of
- # the JIT?
- if self.is_64_bit:
- return [chr(self.LOC_EBP_PLUS | 4), # return addr: at 8(%rbp)
- chr(self.LOC_EBP_MINUS | 4), # saved %rbx: at -8(%rbp)
- chr(self.LOC_EBP_MINUS | 8), # saved %r12: at -16(%rbp)
- chr(self.LOC_EBP_MINUS | 12), # saved %r13: at -24(%rbp)
- chr(self.LOC_EBP_MINUS | 16), # saved %r14: at -32(%rbp)
- chr(self.LOC_EBP_MINUS | 20), # saved %r15: at -40(%rbp)
- chr(self.LOC_EBP_PLUS | 0), # saved %rbp: at (%rbp)
- chr(0)]
- else:
- return [chr(self.LOC_EBP_PLUS | 4), # return addr: at 4(%ebp)
- chr(self.LOC_EBP_MINUS | 4), # saved %ebx: at -4(%ebp)
- chr(self.LOC_EBP_MINUS | 8), # saved %esi: at -8(%ebp)
- chr(self.LOC_EBP_MINUS | 12), # saved %edi: at -12(%ebp)
- chr(self.LOC_EBP_PLUS | 0), # saved %ebp: at (%ebp)
- chr(0)]
- def _encode_num(self, shape, number):
- assert number >= 0
- flag = 0
- while number >= 0x80:
- shape.append(chr((number & 0x7F) | flag))
- flag = 0x80
- number >>= 7
- shape.append(chr(number | flag))
- def add_frame_offset(self, shape, offset):
- if self.is_64_bit:
- assert (offset & 7) == 0
- offset >>= 1
- else:
- assert (offset & 3) == 0
- if offset >= 0:
- num = self.LOC_EBP_PLUS | offset
- else:
- num = self.LOC_EBP_MINUS | (-offset)
- self._encode_num(shape, num)
- def add_callee_save_reg(self, shape, reg_index):
- assert reg_index > 0
- shape.append(chr(self.LOC_REG | (reg_index << 2)))
- def compress_callshape(self, shape, datablockwrapper):
- # Similar to compress_callshape() in trackgcroot.py.
- # Returns an address to raw memory (as an integer).
- length = len(shape)
- rawaddr = datablockwrapper.malloc_aligned(length, 1)
- p = rffi.cast(self.CALLSHAPE_ARRAY_PTR, rawaddr)
- for i in range(length):
- p[length-1-i] = rffi.cast(rffi.UCHAR, shape[i])
- return rawaddr
- class GcRootMap_shadowstack(object):
- """Handles locating the stack roots in the assembler.
- This is the class supporting --gcrootfinder=shadowstack.
- """
- is_shadow_stack = True
- MARKER_FRAME = 8 # this marker now *follows* the frame addr
- # The "shadowstack" is a portable way in which the GC finds the
- # roots that live in the stack. Normally it is just a list of
- # pointers to GC objects. The pointers may be moved around by a GC
- # collection. But with the JIT, an entry can also be MARKER_FRAME,
- # in which case the previous entry points to an assembler stack frame.
- # During a residual CALL from the assembler (which may indirectly
- # call the GC), we use the force_index stored in the assembler
- # stack frame to identify the call: we can go from the force_index
- # to a list of where the GC pointers are in the frame (this is the
- # purpose of the present class).
- #
- # Note that across CALL_MAY_FORCE or CALL_ASSEMBLER, we can also go
- # from the force_index to a ResumeGuardForcedDescr instance, which
- # is used if the virtualizable or the virtualrefs need to be forced
- # (see pypy.jit.backend.model). The force_index number in the stack
- # frame is initially set to a non-negative value x, but it is
- # occasionally turned into (~x) in case of forcing.
- INTARRAYPTR = rffi.CArrayPtr(rffi.INT)
- CALLSHAPES_ARRAY = rffi.CArray(INTARRAYPTR)
- def __init__(self, gcdescr):
- self._callshapes = lltype.nullptr(self.CALLSHAPES_ARRAY)
- self._callshapes_maxlength = 0
- self.force_index_ofs = gcdescr.force_index_ofs
- def add_jit2gc_hooks(self, jit2gc):
- #
- # ---------------
- # This is used to enumerate the shadowstack in the presence
- # of the JIT. It is also used by the stacklet support in
- # rlib/_stacklet_shadowstack. That's why it is written as
- # an iterator that can also be used with a custom_trace.
- #
- class RootIterator:
- _alloc_flavor_ = "raw"
- def setcontext(iself, context):
- iself.context = context
- def nextleft(iself, gc, range_lowest, prev):
- # Return the next valid GC object's address, in right-to-left
- # order from the shadowstack array. This usually means just
- # returning "prev - sizeofaddr", until we reach "range_lowest",
- # except that we are skipping NULLs. If "prev - sizeofaddr"
- # contains a MARKER_FRAME instead, then we go into
- # JIT-frame-lookup mode.
- #
- while True:
- #
- # If we are not iterating right now in a JIT frame
- if iself.frame_addr == 0:
- #
- # Look for the next shadowstack address that
- # contains a valid pointer
- while prev != range_lowest:
- prev -= llmemory.sizeof(llmemory.Address)
- if prev.signed[0] == self.MARKER_FRAME:
- break
- if gc.points_to_valid_gc_object(prev):
- return prev
- else:
- return llmemory.NULL # done
- #
- # It's a JIT frame. Save away 'prev' for later, and
- # go into JIT-frame-exploring mode.
- prev -= llmemory.sizeof(llmemory.Address)
- frame_addr = prev.signed[0]
- iself.saved_prev = prev
- iself.frame_addr = frame_addr
- addr = llmemory.cast_int_to_adr(frame_addr +
- self.force_index_ofs)
- addr = iself.translateptr(iself.context, addr)
- force_index = addr.signed[0]
- if force_index < 0:
- force_index = ~force_index
- # NB: the next line reads a still-alive _callshapes,
- # because we ensure that just before we called this
- # piece of assembler, we put on the (same) stack a
- # pointer to a loop_token that keeps the force_index
- # alive.
- callshape = self._callshapes[force_index]
- else:
- # Continuing to explore this JIT frame
- callshape = iself.callshape
- #
- # 'callshape' points to the next INT of the callshape.
- # If it's zero we are done with the JIT frame.
- while rffi.cast(lltype.Signed, callshape[0]) != 0:
- #
- # Non-zero: it's an offset inside the JIT frame.
- # Read it and increment 'callshape'.
- offset = rffi.cast(lltype.Signed, callshape[0])
- callshape = lltype.direct_ptradd(callshape, 1)
- addr = llmemory.cast_int_to_adr(iself.frame_addr +
- offset)
- addr = iself.translateptr(iself.context, addr)
- if gc.points_to_valid_gc_object(addr):
- #
- # The JIT frame contains a valid GC pointer at
- # this address (as opposed to NULL). Save
- # 'callshape' for the next call, and return the
- # address.
- iself.callshape = callshape
- return addr
- #
- # Restore 'prev' and loop back to the start.
- iself.frame_addr = 0
- prev = iself.saved_prev
- # ---------------
- #
- root_iterator = RootIterator()
- root_iterator.frame_addr = 0
- root_iterator.context = llmemory.NULL
- root_iterator.translateptr = lambda context, addr: addr
- jit2gc.update({
- 'root_iterator': root_iterator,
- })
- def initialize(self):
- pass
- def get_basic_shape(self):
- return []
- def add_frame_offset(self, shape, offset):
- assert offset != 0
- shape.append(offset)
- def add_callee_save_reg(self, shape, register):
- msg = "GC pointer in %s was not spilled" % register
- os.write(2, '[llsupport/gc] %s\n' % msg)
- raise AssertionError(msg)
- def compress_callshape(self, shape, datablockwrapper):
- length = len(shape)
- SZINT = rffi.sizeof(rffi.INT)
- rawaddr = datablockwrapper.malloc_aligned((length + 1) * SZINT, SZINT)
- p = rffi.cast(self.INTARRAYPTR, rawaddr)
- for i in range(length):
- p[i] = rffi.cast(rffi.INT, shape[i])
- p[length] = rffi.cast(rffi.INT, 0)
- return p
- def write_callshape(self, p, force_index):
- if force_index >= self._callshapes_maxlength:
- self._enlarge_callshape_list(force_index + 1)
- self._callshapes[force_index] = p
- def _enlarge_callshape_list(self, minsize):
- newlength = 250 + (self._callshapes_maxlength // 3) * 4
- if newlength < minsize:
- newlength = minsize
- newarray = lltype.malloc(self.CALLSHAPES_ARRAY, newlength,
- flavor='raw', track_allocation=False)
- if self._callshapes:
- i = self._callshapes_maxlength - 1
- while i >= 0:
- newarray[i] = self._callshapes[i]
- i -= 1
- lltype.free(self._callshapes, flavor='raw', track_allocation=False)
- self._callshapes = newarray
- self._callshapes_maxlength = newlength
- def freeing_block(self, start, stop):
- pass # nothing needed here
- def get_root_stack_top_addr(self):
- rst_addr = llop.gc_adr_of_root_stack_top(llmemory.Address)
- return rffi.cast(lltype.Signed, rst_addr)
- class WriteBarrierDescr(AbstractDescr):
- def __init__(self, gc_ll_descr):
- self.llop1 = gc_ll_descr.llop1
- self.WB_FUNCPTR = gc_ll_descr.WB_FUNCPTR
- self.WB_ARRAY_FUNCPTR = gc_ll_descr.WB_ARRAY_FUNCPTR
- self.fielddescr_tid = gc_ll_descr.fielddescr_tid
- #
- GCClass = gc_ll_descr.GCClass
- if GCClass is None: # for tests
- return
- self.jit_wb_if_flag = GCClass.JIT_WB_IF_FLAG
- self.jit_wb_if_flag_byteofs, self.jit_wb_if_flag_singlebyte = (
- self.extract_flag_byte(self.jit_wb_if_flag))
- #
- if hasattr(GCClass, 'JIT_WB_CARDS_SET'):
- self.jit_wb_cards_set = GCClass.JIT_WB_CARDS_SET
- self.jit_wb_card_page_shift = GCClass.JIT_WB_CARD_PAGE_SHIFT
- self.jit_wb_cards_set_byteofs, self.jit_wb_cards_set_singlebyte = (
- self.extract_flag_byte(self.jit_wb_cards_set))
- else:
- self.jit_wb_cards_set = 0
- def extract_flag_byte(self, flag_word):
- # if convenient for the backend, we compute the info about
- # the flag as (byte-offset, single-byte-flag).
- import struct
- value = struct.pack(lltype.SignedFmt, flag_word)
- assert value.count('\x00') == len(value) - 1 # only one byte is != 0
- i = 0
- while value[i] == '\x00': i += 1
- return (i, struct.unpack('b', value[i])[0])
- def get_write_barrier_fn(self, cpu):
- llop1 = self.llop1
- funcptr = llop1.get_write_barrier_failing_case(self.WB_FUNCPTR)
- funcaddr = llmemory.cast_ptr_to_adr(funcptr)
- return cpu.cast_adr_to_int(funcaddr)
- def get_write_barrier_from_array_fn(self, cpu):
- # returns a function with arguments [array, index, newvalue]
- llop1 = self.llop1
- funcptr = llop1.get_write_barrier_from_array_failing_case(
- self.WB_ARRAY_FUNCPTR)
- funcaddr = llmemory.cast_ptr_to_adr(funcptr)
- return cpu.cast_adr_to_int(funcaddr) # this may return 0
- def has_write_barrier_from_array(self, cpu):
- return self.get_write_barrier_from_array_fn(cpu) != 0
- class GcLLDescr_framework(GcLLDescription):
- DEBUG = False # forced to True by x86/test/test_zrpy_gc.py
- kind = 'framework'
- round_up = True
- def __init__(self, gcdescr, translator, rtyper, llop1=llop,
- really_not_translated=False):
- GcLLDescription.__init__(self, gcdescr, translator, rtyper)
- self.translator = translator
- self.llop1 = llop1
- if really_not_translated:
- assert not self.translate_support_code # but half does not work
- self._initialize_for_tests()
- else:
- assert self.translate_support_code,"required with the framework GC"
- self._check_valid_gc()
- self._make_gcrootmap()
- self._make_layoutbuilder()
- self._setup_gcclass()
- self._setup_tid()
- self._setup_write_barrier()
- self._setup_str()
- self._make_functions(really_not_translated)
- def _initialize_for_tests(self):
- self.layoutbuilder = None
- self.fielddescr_tid = AbstractDescr()
- self.max_size_of_young_obj = 1000
- self.GCClass = None
- def _check_valid_gc(self):
- # we need the hybrid or minimark GC for rgc._make_sure_does_not_move()
- # to work
- if self.gcdescr.config.translation.gc not in ('hybrid', 'minimark'):
- raise NotImplementedError("--gc=%s not implemented with the JIT" %
- (gcdescr.config.translation.gc,))
- def _make_gcrootmap(self):
- # to find roots in the assembler, make a GcRootMap
- name = self.gcdescr.config.translation.gcrootfinder
- try:
- cls = globals()['GcRootMap_' + name]
- except KeyError:
- raise NotImplementedError("--gcrootfinder=%s not implemented"
- " with the JIT" % (name,))
- gcrootmap = cls(self.gcdescr)
- self.gcrootmap = gcrootmap
- def _make_layoutbuilder(self):
- # make a TransformerLayoutBuilder and save it on the translator
- # where it can be fished and reused by the FrameworkGCTransformer
- from pypy.rpython.memory.gctransform import framework
- translator = self.translator
- self.layoutbuilder = framework.TransformerLayoutBuilder(translator)
- self.layoutbuilder.delay_encoding()
- translator._jit2gc = {'layoutbuilder': self.layoutbuilder}
- self.gcrootmap.add_jit2gc_hooks(translator._jit2gc)
- def _setup_gcclass(self):
- from pypy.rpython.memory.gcheader import GCHeaderBuilder
- self.GCClass = self.layoutbuilder.GCClass
- self.moving_gc = self.GCClass.moving_gc
- self.HDRPTR = lltype.Ptr(self.GCClass.HDR)
- self.gcheaderbuilder = GCHeaderBuilder(self.HDRPTR.TO)
- self.max_size_of_young_obj = self.GCClass.JIT_max_size_of_young_obj()
- self.minimal_size_in_nursery=self.GCClass.JIT_minimal_size_in_nursery()
- # for the fast path of mallocs, the following must be true, at least
- assert self.GCClass.inline_simple_malloc
- assert self.GCClass.inline_simple_malloc_varsize
- def _setup_tid(self):
- self.fielddescr_tid = get_field_descr(self, self.GCClass.HDR, 'tid')
- def _setup_write_barrier(self):
- self.WB_FUNCPTR = lltype.Ptr(lltype.FuncType(
- [llmemory.Address, llmemory.Address], lltype.Void))
- self.WB_ARRAY_FUNCPTR = lltype.Ptr(lltype.FuncType(
- [llmemory.Address, lltype.Signed, llmemory.Address], lltype.Void))
- self.write_barrier_descr = WriteBarrierDescr(self)
- def _make_functions(self, really_not_translated):
- from pypy.rpython.memory.gctypelayout import check_typeid
- llop1 = self.llop1
- (self.standard_array_basesize, _, self.standard_array_length_ofs) = \
- symbolic.get_array_token(lltype.GcArray(lltype.Signed),
- not really_not_translated)
- def malloc_nursery_slowpath(size):
- """Allocate 'size' null bytes out of the nursery.
- Note that the fast path is typically inlined by the backend."""
- if self.DEBUG:
- self._random_usage_of_xmm_registers()
- type_id = rffi.cast(llgroup.HALFWORD, 0) # missing here
- return llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
- type_id, size,
- False, False, False)
- self.generate_function('malloc_nursery', malloc_nursery_slowpath,
- [lltype.Signed])
- def malloc_array(itemsize, tid, num_elem):
- """Allocate an array with a variable-size num_elem.
- Only works for standard arrays."""
- type_id = llop.extract_ushort(llgroup.HALFWORD, tid)
- check_typeid(type_id)
- return llop1.do_malloc_varsize_clear(
- llmemory.GCREF,
- type_id, num_elem, self.standard_array_basesize, itemsize,
- self.standard_array_length_ofs)
- self.generate_function('malloc_array', malloc_array,
- [lltype.Signed] * 3)
- def malloc_array_nonstandard(basesize, itemsize, lengthofs, tid,
- num_elem):
- """For the rare case of non-standard arrays, i.e. arrays where
- self.standard_array_{basesize,length_ofs} is wrong. It can
- occur e.g. with arrays of floats on Win32."""
- type_id = llop.extract_ushort(llgroup.HALFWORD, tid)
- check_typeid(type_id)
- return llop1.do_malloc_varsize_clear(
- llmemory.GCREF,
- type_id, num_elem, basesize, itemsize, lengthofs)
- self.generate_function('malloc_array_nonstandard',
- malloc_array_nonstandard,
- [lltype.Signed] * 5)
- str_type_id = self.str_descr.tid
- str_basesize = self.str_descr.basesize
- str_itemsize = self.str_descr.itemsize
- str_ofs_length = self.str_descr.lendescr.offset
- unicode_type_id = self.unicode_descr.tid
- unicode_basesize = self.unicode_descr.basesize
- unicode_itemsize = self.unicode_descr.itemsize
- unicode_ofs_length = self.unicode_descr.lendescr.offset
- def malloc_str(length):
- return llop1.do_malloc_varsize_clear(
- llmemory.GCREF,
- str_type_id, length, str_basesize, str_itemsize,
- str_ofs_length)
- self.generate_function('malloc_str', malloc_str,
- [lltype.Signed])
- def malloc_unicode(length):
- return llop1.do_malloc_varsize_clear(
- llmemory.GCREF,
- unicode_type_id, length, unicode_basesize, unicode_itemsize,
- unicode_ofs_length)
- self.generate_function('malloc_unicode', malloc_unicode,
- [lltype.Signed])
- # Never called as far as I can tell, but there for completeness:
- # allocate a fixed-size object, but not in the nursery, because
- # it is too big.
- def malloc_big_fixedsize(size, tid):
- if self.DEBUG:
- self._random_usage_of_xmm_registers()
- type_id = llop.extract_ushort(llgroup.HALFWORD, tid)
- check_typeid(type_id)
- return llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
- type_id, size,
- False, False, False)
- self.generate_function('malloc_big_fixedsize', malloc_big_fixedsize,
- [lltype.Signed] * 2)
- def _bh_malloc(self, sizedescr):
- from pypy.rpython.memory.gctypelayout import check_typeid
- llop1 = self.llop1
- type_id = llop.extract_ushort(llgroup.HALFWORD, sizedescr.tid)
- check_typeid(type_id)
- return llop1.do_malloc_fixedsize_clear(llmemory.GCREF,
- type_id, sizedescr.size,
- False, False, False)
- def _bh_malloc_array(self, arraydescr, num_elem):
- from pypy.rpython.memory.gctypelayout import check_typeid
- llop1 = self.llop1
- type_id = llop.extract_ushort(llgroup.HALFWORD, arraydescr.tid)
- check_typeid(type_id)
- return llop1.do_malloc_varsize_clear(llmemory.GCREF,
- type_id, num_elem,
- arraydescr.basesize,
- arraydescr.itemsize,
- arraydescr.lendescr.offset)
- class ForTestOnly:
- pass
- for_test_only = ForTestOnly()
- for_test_only.x = 1.23
- def _random_usage_of_xmm_registers(self):
- x0 = self.for_test_only.x
- x1 = x0 * 0.1
- x2 = x0 * 0.2
- x3 = x0 * 0.3
- self.for_test_only.x = x0 + x1 + x2 + x3
- def get_nursery_free_addr(self):
- nurs_addr = llop.gc_adr_of_nursery_free(llmemory.Address)
- return rffi.cast(lltype.Signed, nurs_addr)
- def get_nursery_top_addr(self):
- nurs_top_addr = llop.gc_adr_of_nursery_top(llmemory.Address)
- return rffi.cast(lltype.Signed, nurs_top_addr)
- def initialize(self):
- self.gcrootmap.initialize()
- def init_size_descr(self, S, descr):
- if self.layoutbuilder is not None:
- type_id = self.layoutbuilder.get_type_id(S)
- assert not self.layoutbuilder.is_weakref_type(S)
- assert not self.layoutbuilder.has_finalizer(S)
- descr.tid = llop.combine_ushort(lltype.Signed, type_id, 0)
- def init_array_descr(self, A, descr):
- if self.layoutbuilder is not None:
- type_id = self.layoutbuilder.get_type_id(A)
- descr.tid = llop.combine_ushort(lltype.Signed, type_id, 0)
- def _set_tid(self, gcptr, tid):
- hdr_addr = llmemory.cast_ptr_to_adr(gcptr)
- hdr_addr -= self.gcheaderbuilder.size_gc_header
- hdr = llmemory.cast_adr_to_ptr(hdr_addr, self.HDRPTR)
- hdr.tid = tid
- def do_write_barrier(self, gcref_struct, gcref_newptr):
- hdr_addr = llmemory.cast_ptr_to_adr(gcref_struct)
- hdr_addr -= self.gcheaderbuilder.size_gc_header
- hdr = llmemory.cast_adr_to_ptr(hdr_addr, self.HDRPTR)
- if hdr.tid & self.GCClass.JIT_WB_IF_FLAG:
- # get a pointer to the 'remember_young_pointer' function from
- # the GC, and call it immediately
- llop1 = self.llop1
- funcptr = llop1.get_write_barrier_failing_case(self.WB_FUNCPTR)
- funcptr(llmemory.cast_ptr_to_adr(gcref_struct),
- llmemory.cast_ptr_to_adr(gcref_newptr))
- def can_use_nursery_malloc(self, size):
- return size < self.max_size_of_young_obj
- def has_write_barrier_class(self):
- return WriteBarrierDescr
- def freeing_block(self, start, stop):
- self.gcrootmap.freeing_block(start, stop)
- def get_malloc_slowpath_addr(self):
- return self.get_malloc_fn_addr('malloc_nursery')
- # ____________________________________________________________
- def get_ll_description(gcdescr, translator=None, rtyper=None):
- # translator is None if translate_support_code is False.
- if gcdescr is not None:
- name = gcdescr.config.translation.gctransformer
- else:
- name = "boehm"
- try:
- cls = globals()['GcLLDescr_' + name]
- except KeyError:
- raise NotImplementedError("GC transformer %r not supported by "
- "the JIT backend" % (name,))
- return cls(gcdescr, translator, rtyper)